From a0d0b18d9faeff97408d18cc0186ef9234a95434 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 4 Sep 2013 12:24:03 +0200 Subject: ARM: call of_clk_init from default time_init handler Most DT ARM machs require common clock providers initialized before timers. Currently, arch/arm machs use .init_time to call of_clk_init right before clocksource_of_init. This prevents to remove that callback and use the default one instead. This patch adds a call to of_clk_init() to the default .init_time callback for COMMON_CLK enabled machs to allow to remove custom callbacks where applicable. While at it, also reorder includes alphabetically. Signed-off-by: Sebastian Hesselbarth --- This patch is pulled back from upstream: commit 4178bac4f6e955869395b30246687d41183a5edb diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 98aee32..829a96d 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -11,25 +11,26 @@ * This file contains the ARM-specific time handling details: * reading the RTC at bootup, etc... */ +#include +#include +#include #include -#include -#include -#include #include +#include +#include +#include +#include #include +#include #include +#include #include -#include -#include #include -#include -#include -#include -#include -#include #include #include +#include +#include #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) || \ defined(CONFIG_NVRAM) || defined(CONFIG_NVRAM_MODULE) @@ -116,8 +117,12 @@ int __init register_persistent_clock(clock_access_fn read_boot, void __init time_init(void) { - if (machine_desc->init_time) + if (machine_desc->init_time) { machine_desc->init_time(); - else + } else { +#ifdef CONFIG_COMMON_CLK + of_clk_init(NULL); +#endif clocksource_of_init(); + } } -- cgit v0.10.2 From 2f3187620e02641ee4f25d51996d77c1ddb09ec1 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 1 Jul 2014 16:16:17 +0800 Subject: clk: ppc-corenet: Fix Section mismatch warning WARNING: drivers/built-in.o(.data+0x10258): Section mismatch in reference from the variable ppc_corenet_clk_driver to the (unknown reference) .init.rodata:(unknown) The variable ppc_corenet_clk_driver references the (unknown reference) __initconst (unknown) If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console Signed-off-by: Jingchang Lu --- This patch is pulled back from upstream: commit da788acb28386aa896224e784954bb73c99ff26c diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index e958707..6f968f1 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -264,7 +264,7 @@ static const struct of_device_id ppc_clk_ids[] __initconst = { {} }; -static struct platform_driver ppc_corenet_clk_driver = { +static struct platform_driver ppc_corenet_clk_driver __initdata = { .driver = { .name = "ppc_corenet_clock", .owner = THIS_MODULE, -- cgit v0.10.2 From 74118aa768ebac34f0c6123eb152fdcd8681bae0 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 23 Jun 2014 09:50:16 +0800 Subject: clk: ppc-corenet: Add CLK_OF_DECLARE support Signed-off-by: Jingchang Lu diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 279407a..60c0a01 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -88,7 +88,7 @@ config COMMON_CLK_AXI_CLKGEN config CLK_PPC_CORENET bool "Clock driver for PowerPC corenet platforms" - depends on PPC_E500MC && OF + depends on (PPC_E500MC || FSL_SOC) && OF ---help--- This adds the clock driver support for Freescale PowerPC corenet platforms using common clock framework. diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index 6f968f1..358dd4a 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -238,6 +239,7 @@ err_clks: static const struct of_device_id clk_match[] __initconst = { { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "fsl,sys-clock", .data = of_fixed_clk_setup, }, { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, {} @@ -278,3 +280,13 @@ static int __init ppc_corenet_clk_init(void) return platform_driver_register(&ppc_corenet_clk_driver); } subsys_initcall(ppc_corenet_clk_init); + +static void __init ls1021a_clocks_init(struct device_node *np) +{ + base = of_iomap(np, 0); + if (!base) + return; + + of_clk_init(clk_match); +} +CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-clockgen", ls1021a_clocks_init); -- cgit v0.10.2 From 145b1ac70b1b83dfbd9ccd0894f27115e52b1088 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 16 Sep 2013 09:57:01 +0800 Subject: ARM: dts: Add SoC level device tree support for LS1021A Add Freescale LS1021A SoC device tree support Signed-off-by: Nikhil Badola Signed-off-by: Chenhui Zhao Signed-off-by: Suresh Gupta Signed-off-by: Shaveta Leekha Signed-off-by: Adrian Sendroiu Signed-off-by: Ruchika Gupta Signed-off-by: Bhupesh Sharma Signed-off-by: Jaiprakash Singh Signed-off-by: Ruchika Gupta Signed-off-by: Chao Fu Signed-off-by: Xiubo Li Signed-off-by: Zhao Qiang Signed-off-by: Haijun Zhang Signed-off-by: Jingchang Lu --- This patch has been sent to upstream for review: https://patchwork.kernel.org/patch/4464491/ diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi new file mode 100644 index 0000000..91aa312 --- /dev/null +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -0,0 +1,684 @@ +/* + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * + * 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 "skeleton64.dtsi" +#include + +/ { + compatible = "fsl,ls1021a"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &lpuart0; + serial1 = &lpuart1; + serial2 = &lpuart2; + serial3 = &lpuart3; + serial4 = &lpuart4; + serial5 = &lpuart5; + ethernet0 = &enet0; + ethernet1 = &enet1; + ethernet2 = &enet2; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x20000000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@f00 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <0xf00>; + }; + + cpu@f01 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <0xf01>; + }; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = , + , + , + ; + }; + + pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = , + ; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + ranges; + + gic: interrupt-controller@1400000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x0 0x1401000 0x0 0x1000>, + <0x0 0x1402000 0x0 0x1000>, + <0x0 0x1404000 0x0 0x2000>, + <0x0 0x1406000 0x0 0x2000>; + interrupts = ; + + }; + + ifc: ifc@1530000 { + compatible = "fsl,ifc", "simple-bus"; + reg = <0x0 0x1530000 0x0 0x10000>; + interrupts = ; + }; + + dcfg: dcfg@1ee0000 { + compatible = "fsl,ls1021a-dcfg"; + reg = <0x0 0x1ee0000 0x0 0x10000>; + }; + + qspi: quadspi@1550000 { + compatible = "fsl,vf610-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x1550000 0x0 0x10000>; + interrupts = ; + clock-names = "qspi_en", "qspi"; + clocks = <&platform_clk 1>, <&platform_clk 1>; + big-endian; + amba-base = <0x40000000>; + status = "disabled"; + }; + + esdhc: esdhc@1560000 { + compatible = "fsl,ls1021a-esdhc", "fsl,esdhc"; + reg = <0x0 0x1560000 0x0 0x10000>; + interrupts = ; + clock-frequency = <0>; + voltage-ranges = <1800 1800 3300 3300>; + sdhci,auto-cmd12; + big-endian; + bus-width = <4>; + status = "disabled"; + }; + + scfg: scfg@1570000 { + compatible = "fsl,ls1021a-scfg"; + reg = <0x0 0x1570000 0x0 0x10000>; + }; + + crypto: crypto@1700000 { + compatible = "fsl,sec-v5.3", "fsl,sec-v5.0", "fsl,sec-v4.0"; + fsl,sec-era = <4>; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x1700000 0x0 0x100000>; + ranges = <0x0 0x0 0x1700000 0x100000>; + interrupts = ; + + sec_jr0: jr@10000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x10000 0x10000>; + interrupts = ; + }; + + sec_jr1: jr@20000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x20000 0x10000>; + interrupts = ; + }; + + sec_jr2: jr@30000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x30000 0x10000>; + interrupts = ; + }; + + sec_jr3: jr@40000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x40000 0x10000>; + interrupts = ; + }; + + }; + + clockgen: clocking@1ee1000 { + compatible = "fsl,ls1021a-clockgen"; + reg = <0x0 0x1ee1000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + sysclk: sysclk { + compatible = "fsl,sys-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + clock-output-names = "sysclk"; + }; + + cga_pll1: pll1@800 { + compatible = "fsl,core-pll-clock"; + #clock-cells = <1>; + reg = <0x800>; + clocks = <&sysclk>; + clock-output-names = "cga-pll1", "cga-pll1-div2", + "cga-pll1-div3", "cga-pll1-div4"; + }; + + cga_pll2: pll2@820 { + compatible = "fsl,core-pll-clock"; + #clock-cells = <1>; + reg = <0x820>; + clocks = <&sysclk>; + clock-output-names = "cga-pll2", "cga-pll2-div2", + "cga-pll2-div3", "cga-pll2-div4"; + }; + + platform_clk: pll@c00 { + compatible = "fsl,core-pll-clock"; + #clock-cells = <1>; + reg = <0xc00>; + clocks = <&sysclk>; + clock-output-names = "platform-clk", "platform-clk-div2"; + }; + + + cluster1_clk: clk0c0@0 { + compatible = "fsl,core-mux-clock"; + #clock-cells = <1>; + reg = <0x0>; + clock-names = "pll1cga", "pll1cga-div2"; + clocks = <&cga_pll1 0>, <&cga_pll1 2>; + clock-output-names = "cluster1-clk"; + + }; + + }; + + dspi0: dspi@2100000 { + compatible = "fsl,vf610-dspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2100000 0x0 0x10000>; + interrupts = ; + clock-names = "dspi"; + clocks = <&platform_clk 1>; + spi-num-chipselects = <5>; + big-endian; + status = "disabled"; + }; + + dspi1: dspi@2110000 { + compatible = "fsl,vf610-dspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2110000 0x0 0x10000>; + interrupts = ; + clock-names = "dspi"; + clocks = <&platform_clk 1>; + spi-num-chipselects = <5>; + big-endian; + status = "disabled"; + }; + + i2c0: i2c@2180000 { + compatible = "fsl,vf610-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2180000 0x0 0x10000>; + interrupts = ; + clock-names = "i2c"; + clocks = <&platform_clk 1>; + status = "disabled"; + }; + + i2c1: i2c@2190000 { + compatible = "fsl,vf610-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2190000 0x0 0x10000>; + interrupts = ; + clock-names = "i2c"; + clocks = <&platform_clk 1>; + status = "disabled"; + }; + + i2c2: i2c@21a0000 { + compatible = "fsl,vf610-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x21a0000 0x0 0x10000>; + interrupts = ; + clock-names = "i2c"; + clocks = <&platform_clk 1>; + status = "disabled"; + }; + + duart0: serial@21c0500 { + compatible = "fsl,ns16550", "ns16550a"; + reg = <0x0 0x21c0500 0x0 0x100>; + interrupts = ; + clock-frequency = <0>; + fifo-size = <8>; + status = "disabled"; + }; + + duart1: serial@21c0600 { + compatible = "fsl,ns16550", "ns16550a"; + reg = <0x0 0x21c0600 0x0 0x100>; + interrupts = ; + clock-frequency = <0>; + fifo-size = <8>; + status = "disabled"; + }; + + duart2: serial@21d0500 { + compatible = "fsl,ns16550", "ns16550a"; + reg = <0x0 0x21d0500 0x0 0x100>; + interrupts = ; + clock-frequency = <0>; + fifo-size = <8>; + status = "disabled"; + }; + + duart3: serial@21d0600 { + compatible = "fsl,ns16550", "ns16550a"; + reg = <0x0 0x21d0600 0x0 0x100>; + interrupts = ; + clock-frequency = <0>; + fifo-size = <8>; + status = "disabled"; + }; + + lpuart0: serial@2950000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2950000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart1: serial@2960000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2960000 0x0 0x1000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart2: serial@2970000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2970000 0x0 0x1000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart3: serial@2980000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2980000 0x0 0x1000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart4: serial@2990000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2990000 0x0 0x1000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart5: serial@29a0000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x29a0000 0x0 0x1000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + pwm3: ftm@2a00000 { + compatible = "fsl,vf610-ftm-pwm"; + #pwm-cells = <3>; + reg = <0x0 0x2a00000 0x0 0x10000>; + interrupts = ; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&platform_clk 1>, <&platform_clk 1>, + <&platform_clk 1>, <&platform_clk 1>; + big-endian; + status = "disabled"; + }; + + pwm6: ftm@2a30000 { + compatible = "fsl,vf610-ftm-pwm"; + #pwm-cells = <3>; + reg = <0x0 0x2a30000 0x0 0x10000>; + interrupts = ; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&platform_clk 1>, <&platform_clk 1>, + <&platform_clk 1>, <&platform_clk 1>; + big-endian; + status = "disabled"; + }; + + pwm7: ftm@2a40000 { + compatible = "fsl,vf610-ftm-pwm"; + #pwm-cells = <3>; + reg = <0x0 0x2a40000 0x0 0x10000>; + interrupts = ; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&platform_clk 1>, <&platform_clk 1>, + <&platform_clk 1>, <&platform_clk 1>; + big-endian; + status = "disabled"; + }; + + wdog0: wdog@2ad0000 { + compatible = "fsl,imx21-wdt"; + reg = <0x0 0x2ad0000 0x0 0x10000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "wdog"; + big-endian; + }; + + sai2: sai@2b60000 { + compatible = "fsl,vf610-sai"; + reg = <0x0 0x2b60000 0x0 0x10000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "sai"; + dma-names = "tx", "rx"; + dmas = <&edma0 1 45>, + <&edma0 1 44>; + big-endian-regs; + status = "disabled"; + }; + + edma0: edma@2c00000 { + #dma-cells = <2>; + compatible = "fsl,vf610-edma"; + reg = <0x0 0x2c00000 0x0 0x10000>, + <0x0 0x2c10000 0x0 0x10000>, + <0x0 0x2c20000 0x0 0x10000>; + interrupts = , + ; + interrupt-names = "edma-tx", "edma-err"; + dma-channels = <32>; + big-endian; + clock-names = "dmamux0", "dmamux1"; + clocks = <&platform_clk 1>, + <&platform_clk 1>; + }; + + dcu0: dcu@2ce0000 { + compatible = "fsl,vf610-dcu"; + reg = <0x0 0x2ce000 0x0 0x10000>; + interrupts = ; + clocks = <&platform_clk 1>; + clock-names = "dcu"; + big-endian; + status = "disabled"; + }; + + mdio0: mdio@2d24000 { + compatible = "gianfar"; + device_type = "mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2d24000 0x0 0x4000>; + tbi0: tbi-phy@8 { + reg = <0x8>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@2d10000 { + compatible = "fsl,etsec2"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + model = "eTSEC"; + fsl,dma-endian-le; + fsl,num_rx_queues = <0x1>; + fsl,num_tx_queues = <0x1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; + + queue-group@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2d10000 0x0 0x8000>; + fsl,rx-bit-map = <0xff>; + fsl,tx-bit-map = <0xff>; + interrupts = , + , + ; + }; + + }; + + enet1: ethernet@2d50000 { + compatible = "fsl,etsec2"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + model = "eTSEC"; + fsl,dma-endian-le; + fsl,num_rx_queues = <0x1>; + fsl,num_tx_queues = <0x1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; + + queue-group@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2d50000 0x0 0x8000>; + fsl,rx-bit-map = <0xff>; + fsl,tx-bit-map = <0xff>; + interrupts = , + , + ; + }; + + }; + + enet2: ethernet@2d90000 { + compatible = "fsl,etsec2"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + model = "eTSEC"; + fsl,dma-endian-le; + fsl,num_rx_queues = <0x1>; + fsl,num_tx_queues = <0x1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; + + queue-group@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2d90000 0x0 0x8000>; + fsl,rx-bit-map = <0xff>; + fsl,tx-bit-map = <0xff>; + interrupts = , + , + ; + }; + }; + + usb@8600000 { + compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr"; + reg = <0x0 0x8600000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + dr_mode = "host"; + phy_type = "ulpi"; + }; + + usb@3100000 { + compatible = "fsl,fsl-dwc3"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + dwc3 { + compatible = "snps,dwc3"; + reg = <0x0 0x3100000 0x0 0x10000>; + interrupts = ; + dr_mode = "host"; + maximum-speed = "high-speed"; + }; + }; + }; + + dcsr { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + ranges = <0x0 0x0 0x20000000 0x1000000>; + + dcsr-epu@0 { + compatible = "fsl,ls1021a-dcsr-epu"; + reg = <0x0 0x10000>; + }; + + dcsr-gdi@100000 { + compatible = "fsl,ls1021a-dcsr-gdi"; + reg = <0x100000 0x10000>; + }; + + dcsr-dddi@120000 { + compatible = "fsl,ls1021a-dcsr-dddi"; + reg = <0x120000 0x10000>; + }; + + dcsr-dcfg@220000 { + compatible = "fsl,ls1021a-dcsr-dcfg"; + reg = <0x220000 0x1000>; + }; + + dcsr-clock@221000 { + compatible = "fsl,ls1021a-dcsr-clock"; + reg = <0x221000 0x1000>; + }; + + dcsr-rcpm@222000 { + compatible = "fsl,ls1021a-dcsr-rcpm"; + reg = <0x222000 0x1000 0x223000 0x1000>; + }; + + dcsr-ccp@225000 { + compatible = "fsl,ls1021a-dcsr-ccp"; + reg = <0x225000 0x1000>; + }; + + dcsr-fusectrl@226000 { + compatible = "fsl,ls1021a-dcsr-fusectrl"; + reg = <0x226000 0x1000>; + }; + + dcsr-dap@300000 { + compatible = "fsl,ls1021a-dcsr-dap"; + reg = <0x300000 0x10000>; + }; + + dcsr-cstf@350000 { + compatible = "fsl,ls1021a-dcsr-cstf"; + reg = <0x350000 0x1000 0x3a7000 0x1000>; + }; + + dcsr-a7rom@360000 { + compatible = "fsl,ls1021a-dcsr-a7rom"; + reg = <0x360000 0x10000>; + }; + + dcsr-a7cpu@370000 { + compatible = "fsl,ls1021a-dcsr-a7cpu"; + reg = <0x370000 0x8000>; + }; + + dcsr-a7cti@378000 { + compatible = "fsl,ls1021a-dcsr-a7cti"; + reg = <0x378000 0x4000>; + }; + + dcsr-etm@37c000 { + compatible = "fsl,ls1021a-dcsr-etm"; + reg = <0x37c000 0x1000 0x37d000 0x3000>; + }; + + dcsr-hugorom@3a0000 { + compatible = "fsl,ls1021a-dcsr-hugorom"; + reg = <0x3a0000 0x1000>; + }; + + dcsr-etf@3a1000 { + compatible = "fsl,ls1021a-dcsr-etf"; + reg = <0x3a1000 0x1000 0x3a2000 0x1000>; + }; + + dcsr-etr@3a3000 { + compatible = "fsl,ls1021a-dcsr-etr"; + reg = <0x3a3000 0x1000>; + }; + + dcsr-cti@3a4000 { + compatible = "fsl,ls1021a-dcsr-cti"; + reg = <0x3a4000 0x1000 0x3a5000 0x1000 0x3a6000 0x1000>; + }; + + dcsr-atbrepl@3a8000 { + compatible = "fsl,ls1021a-dcsr-atbrepl"; + reg = <0x3a8000 0x1000>; + }; + + dcsr-tsgen-ctrl@3a9000 { + compatible = "fsl,ls1021a-dcsr-tsgen-ctrl"; + reg = <0x3a9000 0x1000>; + }; + + dcsr-tsgen-read@3aa000 { + compatible = "fsl,ls1021a-dcsr-tsgen-read"; + reg = <0x3aa000 0x1000>; + }; + }; +}; -- cgit v0.10.2 From f9fb0405c0c38472c687bf2a16b46112041a8e87 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 16 Sep 2013 09:59:55 +0800 Subject: ARM: dts: Add initial LS1021A QDS board dts support Signed-off-by: Alison Wang Signed-off-by: Chao Fu Signed-off-by: Jason Jin Signed-off-by: Xiubo Li Signed-off-by: Zhao Qiang Signed-off-by: Bhupesh Sharma Signed-off-by: Jaiprakash Singh Signed-off-by: Jingchang Lu --- This patch has been sent to upstream for review: https://patchwork.kernel.org/patch/4464471/ diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 802720e..f1afde5 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -144,7 +144,8 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6q-sbc6x.dtb \ imx6q-wandboard.dtb \ imx6sl-evk.dtb \ - vf610-twr.dtb + vf610-twr.dtb \ + ls1021a-qds.dtb dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \ imx23-olinuxino.dtb \ imx23-stmp378x_devb.dtb \ diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts new file mode 100644 index 0000000..2deed2d --- /dev/null +++ b/arch/arm/boot/dts/ls1021a-qds.dts @@ -0,0 +1,332 @@ +/* + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * + * 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. + */ + +/dts-v1/; +#include "ls1021a.dtsi" + +/ { + model = "LS1021A QDS Board"; + + soc { + leds { + compatible = "pwm-leds"; + led0 { + label = "led0"; + pwms = <&pwm3 0 150000 0>; + max-brightness = <100>; + }; + led1 { + label = "led1"; + pwms = <&pwm3 1 150000 0>; + max-brightness = <100>; + }; + led2 { + label = "led2"; + pwms = <&pwm3 2 150000 0>; + max-brightness = <100>; + }; + led3 { + label = "led3"; + pwms = <&pwm3 3 150000 0>; + max-brightness = <100>; + }; + led4 { + label = "led4"; + pwms = <&pwm3 4 150000 0>; + max-brightness = <100>; + }; + led5 { + label = "led5"; + pwms = <&pwm3 5 150000 0>; + max-brightness = <100>; + }; + led6 { + label = "led6"; + pwms = <&pwm3 6 150000 0>; + max-brightness = <100>; + }; + led7 { + label = "led7"; + pwms = <&pwm3 7 150000 0>; + max-brightness = <100>; + }; + }; + }; +}; + +&dspi0 { + bus-num = <0>; + status = "okay"; + + dspiflash: at45db021d@0 { + compatible = "atmel,at45db021d", "atmel,at45", "atmel,dataflash"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <16000000>; + spi-cpol; + spi-cpha; + reg = <0>; + }; +}; + +&duart0 { + status = "okay"; +}; + +&duart1 { + status = "okay"; +}; + +&enet0 { + tbi-handle = <&tbi0>; + phy-handle = <&physgmii1c>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet1 { + tbi-handle = <&tbi0>; + phy-handle = <&physgmii1d>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet2 { + phy-handle = <&phyrgmii3>; + phy-connection-type = "rgmii"; + status = "okay"; +}; + +&i2c0 { + status = "okay"; + pca9547@77 { + compatible = "philips,pca9547"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0>; + + rtc@68 { + compatible = "dallas,ds3232"; + reg = <0x68>; + interrupts = ; + }; + }; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x2>; + + ina220@40 { + compatible = "ti,ina220"; + reg = <0x40>; + shunt-resistor = <1000>; + }; + + ina220@41 { + compatible = "ti,ina220"; + reg = <0x41>; + shunt-resistor = <1000>; + }; + }; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3>; + + eeprom@56 { + compatible = "at24,24c512"; + reg = <0x56>; + }; + + eeprom@57 { + compatible = "at24,24c512"; + reg = <0x57>; + }; + + adt7461a@4c { + compatible = "adt7461a"; + reg = <0x4c>; + }; + }; + + i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x4>; + }; + }; +}; + +&ifc { + status = "okay"; + #address-cells = <2>; + #size-cells = <1>; + /* NOR, NAND Flashes and FPGA on board */ + ranges = <0x0 0x0 0x0 0x60000000 0x08000000 + 0x2 0x0 0x0 0x7e800000 0x00010000 + 0x3 0x0 0x0 0x7fb00000 0x00000100>; + + nor@0,0 { + compatible = "cfi-flash"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + }; + + nand@2,0 { + compatible = "fsl,ifc-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x0 0x10000>; + + partition@0 { + /* This location must not be altered */ + /* 1MB for u-boot Bootloader Image */ + reg = <0x0 0x00100000>; + label = "NAND U-Boot Image"; + read-only; + }; + + partition@100000 { + /* 1MB for DTB Image */ + reg = <0x00100000 0x00100000>; + label = "NAND DTB Image"; + }; + + partition@200000 { + /* 10MB for Linux Kernel Image */ + reg = <0x00200000 0x00a00000>; + label = "NAND Linux Kernel Image"; + }; + + partition@c00000 { + /* 500MB for Root file System Image */ + reg = <0x00c00000 0x1f400000>; + label = "NAND Compressed RFS Image"; + }; + }; + + fpga: board-control@3,0 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x3 0x0 0x0000100>; + bank-width = <1>; + device-width = <1>; + ranges = <0 3 0 0x100>; + + mdio-mux-emi1 { + compatible = "mdio-mux-mmioreg"; + mdio-parent-bus = <&mdio0>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x54 1>; /* BRDCFG4 */ + mux-mask = <0xe0>; /* EMI1[2:0] */ + + /* Onboard PHYs */ + ls1021amdio0: mdio@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + phyrgmii1: ethernet-phy@1 { + reg = <0x1>; + }; + }; + ls1021amdio1: mdio@20 { + reg = <0x20>; + #address-cells = <1>; + #size-cells = <0>; + phyrgmii2: ethernet-phy@2 { + reg = <0x2>; + }; + }; + ls1021amdio2: mdio@40 { + reg = <0x40>; + #address-cells = <1>; + #size-cells = <0>; + phyrgmii3: ethernet-phy@3 { + reg = <0x3>; + }; + }; + ls1021amdio3: mdio@60 { + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + physgmii1c: ethernet-phy@1c { + reg = <0x1c>; + }; + }; + ls1021amdio4: mdio@80 { + reg = <0x80>; + #address-cells = <1>; + #size-cells = <0>; + physgmii1d: ethernet-phy@1d { + reg = <0x1d>; + }; + }; + + }; + + }; +}; + +&lpuart0 { + status = "okay"; +}; + +&pwm3 { + status = "okay"; +}; + +&pwm7 { + status = "okay"; +}; + +&qspi { + num-cs = <2>; + bus-num = <0>; + fsl,spi-num-chipselects = <2>; + fsl,spi-flash-chipselects = <0>; + status = "okay"; + + qflash0: s25fl128s@0 { + compatible = "spansion,s25fl129p1"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <0>; + + partition@0 { + label = "s25fl128s-0"; + reg = <0x0 0x1000000>; + }; + }; + + qflash1: s25fl128s@1 { + compatible = "spansion,s25fl129p1"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <1>; + + partition@0x0 { + label = "s25fl128s-1"; + reg = <0x0 0x1000000>; + }; + }; +}; -- cgit v0.10.2 From 9d7475890589e7131bad4eb5cbd6f482af674821 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Thu, 26 Jun 2014 16:47:34 +0800 Subject: ARM: dts: Add initial LS1021A TWR board dts support Signed-off-by: Chen Lu Signed-off-by: Chao Fu Signed-off-by: Jingchang Lu --- This patch has been sent to upstream for review: https://patchwork.kernel.org/patch/4464461/ diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index f1afde5..282026b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -145,7 +145,8 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6q-wandboard.dtb \ imx6sl-evk.dtb \ vf610-twr.dtb \ - ls1021a-qds.dtb + ls1021a-qds.dtb \ + ls1021a-twr.dtb dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \ imx23-olinuxino.dtb \ imx23-stmp378x_devb.dtb \ diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts new file mode 100755 index 0000000..7adc70b --- /dev/null +++ b/arch/arm/boot/dts/ls1021a-twr.dts @@ -0,0 +1,178 @@ +/* + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * + * 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. + */ + +/dts-v1/; +#include "ls1021a.dtsi" + +/ { + model = "LS1021A TWR Board"; +}; + +&dspi1 { + bus-num = <0>; + status = "okay"; + + dspiflash: s25fl064k@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25fl064k"; + spi-max-frequency = <16000000>; + spi-cpol; + spi-cpha; + reg = <0>; + }; +}; + +&duart0 { + status = "okay"; +}; + +&duart1 { + status = "okay"; +}; + +&enet0 { + tbi-handle = <&tbi1>; + phy-handle = <&phy2>; + phy-connection-type = "sgmii"; + status = "ok"; +}; + +&enet1 { + tbi-handle = <&tbi1>; + phy-handle = <&phy0>; + phy-connection-type = "sgmii"; + status = "ok"; +}; + +&enet2 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + status = "ok"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&ifc { + status = "okay"; + #address-cells = <2>; + #size-cells = <1>; + /* NOR, and CPLD on board */ + ranges = <0x0 0x0 0x0 0x60000000 0x08000000 + 0x2 0x0 0x0 0x7fb00000 0x00000100>; + + nor@0,0 { + compatible = "cfi-flash"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* 128KB for rcw */ + reg = <0x00000000 0x0020000>; + label = "NOR bank0 RCW Image"; + }; + + partition@20000 { + /* 1MB for DTB */ + reg = <0x00020000 0x00100000>; + label = "NOR DTB Image"; + }; + + partition@120000 { + /* 8 MB for Linux Kernel Image */ + reg = <0x00120000 0x00800000>; + label = "NOR Linux Kernel Image"; + }; + + partition@920000 { + /* 56MB for Ramdisk Root File System */ + reg = <0x00920000 0x03600000>; + label = "NOR Ramdisk Root File System Image"; + }; + + partition@3f80000 { + /* 512KB for bank4 u-boot Image */ + reg = <0x03f80000 0x80000>; + label = "NOR bank4 u-boot Image"; + }; + + partition@4000000 { + /* 128KB for bank4 RCW Image */ + reg = <0x04000000 0x20000>; + label = "NOR bank4 RCW Image"; + }; + + partition@4020000 { + /* 63MB JFFS2 ROOT File System Image */ + reg = <0x04020000 0x3f00000>; + label = "NOR JFFS2 ROOT File System Image"; + }; + + partition@7f80000 { + /* 512KB for bank0 u-boot Image */ + reg = <0x07f80000 0x80000>; + label = "NOR bank0 u-boot Image"; + }; + }; +}; + +&lpuart0 { + status = "okay"; +}; + +&mdio0 { + phy0: ethernet-phy@0 { + reg = <0x0>; + }; + phy1: ethernet-phy@1 { + reg = <0x1>; + }; + phy2: ethernet-phy@2 { + reg = <0x2>; + }; + tbi1: tbi-phy@1f { + reg = <0x1f>; + device_type = "tbi-phy"; + }; +}; + +&pwm6 { + status = "okay"; +}; + +&pwm7 { + status = "okay"; +}; + +&qspi { + num-cs = <2>; + status = "okay"; + + qflash0: s25fl128s@0 { + compatible = "spansion,s25fl128s"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <0>; + + partition@0 { + label = "s25fl128s-0"; + reg = <0x0 0x1000000>; + }; + }; +}; -- cgit v0.10.2 From bb35145e83b3a34bae2901370a0caf32aae2448c Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 15 Jul 2014 17:23:00 +0800 Subject: dt-bindings: arm: add Freescale LS1021A SoC specific devict tree binding Signed-off-by: Jingchang Lu diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt index e935d7d..2e9b283 100644 --- a/Documentation/devicetree/bindings/arm/fsl.txt +++ b/Documentation/devicetree/bindings/arm/fsl.txt @@ -74,3 +74,31 @@ Required root node properties: i.MX6q generic board Required root node properties: - compatible = "fsl,imx6q"; + + +------------------------------------------------ + +Freescale LS1021A platfform device tree bindings +------------------------------------------------ + +Each device tree must specify the following compatible values: + + "fsl,ls1021a" + +SoC-specific device tree bindings +------------------------------------------- + +Each device tree must specify the following SoC-specific compatible values: + + - compatible = "fsl,ls1021a-scfg": + scfg is the supplemental configuration unit, provides SoC specific + configuration and status registers for the chip.there isn't a dedicate + driver for it, device that has configuration and status register located + in this space can operate on it. Such as getting PEX port status. + + - compatible = "fsl,ls1021a-dcfg": + dcfg is the device configuration unit that provides general purpose + configuration and status for the device, there isn't a dedicate driver + for it, device that has configuration and status register located in + this space can operate on it. Such as setting the secondary core start + address and release the secondary core from holdoff and startup. -- cgit v0.10.2 From 2eeeeda42104eec74cd1dc40f4d4da3442994614 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 16 Sep 2013 10:01:28 +0800 Subject: ARM: imx: Add initial support for Freescale LS1021A The LS1021A SoC is a dual-core Cortex-A7 based processor, this add the initial support for it. Signed-off-by: Jingchang Lu --- This patch has been sent to upstream for review: https://patchwork.kernel.org/patch/4464451/ diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 29a8af6..3948af8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -846,6 +846,24 @@ config SOC_VF610 help This enable support for Freescale Vybrid VF610 processor. +config FSL_SOC + bool + +config SOC_LS1021A + bool "Freescale LS1021A support" + select CPU_V7 + select ARM_GIC + select CLKSRC_OF + select HAVE_ARM_ARCH_TIMER + select HAVE_SMP + select FSL_SOC + select MIGHT_HAVE_PCI + select PCI_DOMAINS if PCI + select ZONE_DMA if ARM_LPAE + + help + This enable support for Freescale LS1021A processor. + endif source "arch/arm/mach-imx/devices/Kconfig" diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 5383c58..41b8044 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -114,4 +114,6 @@ obj-$(CONFIG_SOC_IMX53) += mach-imx53.o obj-$(CONFIG_SOC_VF610) += clk-vf610.o mach-vf610.o +obj-$(CONFIG_SOC_LS1021A) += mach-ls1021a.o + obj-y += devices/ diff --git a/arch/arm/mach-imx/mach-ls1021a.c b/arch/arm/mach-imx/mach-ls1021a.c new file mode 100644 index 0000000..2ffc20f --- /dev/null +++ b/arch/arm/mach-imx/mach-ls1021a.c @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * + * 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 +#include + +#include "common.h" + +static void __init ls1021a_init_machine(void) +{ + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *ls1021a_dt_compat[] __initdata = { + "fsl,ls1021a", + NULL, +}; + +DT_MACHINE_START(LS1021A, "Freescale LS1021A") +#ifdef CONFIG_ZONE_DMA + .dma_zone_size = SZ_128M, +#endif + .init_machine = ls1021a_init_machine, + .dt_compat = ls1021a_dt_compat, + .restart = mxc_restart, +MACHINE_END -- cgit v0.10.2 From 379180c60d82f73177a531323ff3ead32956a88c Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Thu, 7 Nov 2013 14:23:27 +0800 Subject: ARM: imx: Add Freescale LS1021A SMP support Freescale LS1021A SoC deploys two cortex-A7 processors, this adds bring-up support for the secondary core. Signed-off-by: Xiubo Li Signed-off-by: Jingchang Lu --- This patch has been sent to upstream for review: https://patchwork.kernel.org/patch/4464481/ diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 4517fd7..7079ace 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -122,6 +122,7 @@ extern void v7_secondary_startup(void); extern void imx_scu_map_io(void); extern void imx_smp_prepare(void); extern void imx_scu_standby_enable(void); +extern void secondary_startup(void); #else static inline void imx_scu_map_io(void) {} static inline void imx_smp_prepare(void) {} @@ -165,5 +166,6 @@ static inline void imx_init_l2cache(void) {} #endif extern struct smp_operations imx_smp_ops; +extern struct smp_operations ls1021a_smp_ops; #endif diff --git a/arch/arm/mach-imx/mach-ls1021a.c b/arch/arm/mach-imx/mach-ls1021a.c index 2ffc20f..d284cdb 100644 --- a/arch/arm/mach-imx/mach-ls1021a.c +++ b/arch/arm/mach-imx/mach-ls1021a.c @@ -27,6 +27,7 @@ DT_MACHINE_START(LS1021A, "Freescale LS1021A") #ifdef CONFIG_ZONE_DMA .dma_zone_size = SZ_128M, #endif + .smp = smp_ops(ls1021a_smp_ops), .init_machine = ls1021a_init_machine, .dt_compat = ls1021a_dt_compat, .restart = mxc_restart, diff --git a/arch/arm/mach-imx/platsmp.c b/arch/arm/mach-imx/platsmp.c index 1f24c1f..fd41b8d 100644 --- a/arch/arm/mach-imx/platsmp.c +++ b/arch/arm/mach-imx/platsmp.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "common.h" #include "hardware.h" @@ -105,3 +107,33 @@ struct smp_operations imx_smp_ops __initdata = { .cpu_kill = imx_cpu_kill, #endif }; + +#define DCFG_CCSR_SCRATCHRW1 0x200 + +static int ls1021a_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + return 0; +} + +static void __init ls1021a_smp_prepare_cpus(unsigned int max_cpus) +{ + struct device_node *np; + void __iomem *dcfg_base; + unsigned long paddr; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcfg"); + dcfg_base = of_iomap(np, 0); + BUG_ON(!dcfg_base); + + paddr = virt_to_phys(secondary_startup); + writel_relaxed(cpu_to_be32(paddr), dcfg_base + DCFG_CCSR_SCRATCHRW1); + + iounmap(dcfg_base); +} + +struct smp_operations ls1021a_smp_ops __initdata = { + .smp_prepare_cpus = ls1021a_smp_prepare_cpus, + .smp_boot_secondary = ls1021a_boot_secondary, +}; -- cgit v0.10.2 From adbe1911fd08e3c307d648d04fba86f659a24716 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 4 Jul 2014 15:01:18 +0800 Subject: dt-binding: fsl-lpuart: use exact SoC revision to document binding use exact SoC revision instead of wildcard describing to make the binding more clearer. Signed-off-by: Jingchang Lu --- This patch has been sent to upstream: https://patchwork.kernel.org/patch/4544291/ diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt index 6fd1dd1..9b8429f 100644 --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt @@ -1,7 +1,11 @@ * Freescale low power universal asynchronous receiver/transmitter (lpuart) Required properties: -- compatible : Should be "fsl,-lpuart" +- compatible : + - "fsl,vf610-lpuart" for lpuart compatible with the one integrated + on Vybrid vf610 SoC with 8-bit register organization + - "fsl,ls1021a-lpuart" for lpuart compatible with the one integrated + on LS1021A SoC with 32-bit big-endian register organization - reg : Address and length of the register set for the device - interrupts : Should contain uart interrupt -- cgit v0.10.2 From ff750817ce335559b10c1b3745880b5a197f4f14 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 5 May 2014 01:52:09 +0800 Subject: tty: serial: fsl_lpuart: add 32-bit register interface support This add the big-endian 32-bit register version LPUART support. Signed-off-by: Jingchang Lu --- This patch has been sent to upstream: https://patchwork.kernel.org/patch/4544291/ diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 8978dc9..183883a 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1,7 +1,7 @@ /* * Freescale lpuart serial port driver * - * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2012-2014 Freescale Semiconductor, Inc. * * 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 @@ -112,6 +112,113 @@ #define UARTSFIFO_TXOF 0x02 #define UARTSFIFO_RXUF 0x01 +/* 32-bit register defination */ +#define UARTBAUD 0x00 +#define UARTSTAT 0x04 +#define UARTCTRL 0x08 +#define UARTDATA 0x0C +#define UARTMATCH 0x10 +#define UARTMODIR 0x14 +#define UARTFIFO 0x18 +#define UARTWATER 0x1c + +#define UARTBAUD_MAEN1 0x80000000 +#define UARTBAUD_MAEN2 0x40000000 +#define UARTBAUD_M10 0x20000000 +#define UARTBAUD_TDMAE 0x00800000 +#define UARTBAUD_RDMAE 0x00200000 +#define UARTBAUD_MATCFG 0x00400000 +#define UARTBAUD_BOTHEDGE 0x00020000 +#define UARTBAUD_RESYNCDIS 0x00010000 +#define UARTBAUD_LBKDIE 0x00008000 +#define UARTBAUD_RXEDGIE 0x00004000 +#define UARTBAUD_SBNS 0x00002000 +#define UARTBAUD_SBR 0x00000000 +#define UARTBAUD_SBR_MASK 0x1fff + +#define UARTSTAT_LBKDIF 0x80000000 +#define UARTSTAT_RXEDGIF 0x40000000 +#define UARTSTAT_MSBF 0x20000000 +#define UARTSTAT_RXINV 0x10000000 +#define UARTSTAT_RWUID 0x08000000 +#define UARTSTAT_BRK13 0x04000000 +#define UARTSTAT_LBKDE 0x02000000 +#define UARTSTAT_RAF 0x01000000 +#define UARTSTAT_TDRE 0x00800000 +#define UARTSTAT_TC 0x00400000 +#define UARTSTAT_RDRF 0x00200000 +#define UARTSTAT_IDLE 0x00100000 +#define UARTSTAT_OR 0x00080000 +#define UARTSTAT_NF 0x00040000 +#define UARTSTAT_FE 0x00020000 +#define UARTSTAT_PE 0x00010000 +#define UARTSTAT_MA1F 0x00008000 +#define UARTSTAT_M21F 0x00004000 + +#define UARTCTRL_R8T9 0x80000000 +#define UARTCTRL_R9T8 0x40000000 +#define UARTCTRL_TXDIR 0x20000000 +#define UARTCTRL_TXINV 0x10000000 +#define UARTCTRL_ORIE 0x08000000 +#define UARTCTRL_NEIE 0x04000000 +#define UARTCTRL_FEIE 0x02000000 +#define UARTCTRL_PEIE 0x01000000 +#define UARTCTRL_TIE 0x00800000 +#define UARTCTRL_TCIE 0x00400000 +#define UARTCTRL_RIE 0x00200000 +#define UARTCTRL_ILIE 0x00100000 +#define UARTCTRL_TE 0x00080000 +#define UARTCTRL_RE 0x00040000 +#define UARTCTRL_RWU 0x00020000 +#define UARTCTRL_SBK 0x00010000 +#define UARTCTRL_MA1IE 0x00008000 +#define UARTCTRL_MA2IE 0x00004000 +#define UARTCTRL_IDLECFG 0x00000100 +#define UARTCTRL_LOOPS 0x00000080 +#define UARTCTRL_DOZEEN 0x00000040 +#define UARTCTRL_RSRC 0x00000020 +#define UARTCTRL_M 0x00000010 +#define UARTCTRL_WAKE 0x00000008 +#define UARTCTRL_ILT 0x00000004 +#define UARTCTRL_PE 0x00000002 +#define UARTCTRL_PT 0x00000001 + +#define UARTDATA_NOISY 0x00008000 +#define UARTDATA_PARITYE 0x00004000 +#define UARTDATA_FRETSC 0x00002000 +#define UARTDATA_RXEMPT 0x00001000 +#define UARTDATA_IDLINE 0x00000800 +#define UARTDATA_MASK 0x3ff + +#define UARTMODIR_IREN 0x00020000 +#define UARTMODIR_TXCTSSRC 0x00000020 +#define UARTMODIR_TXCTSC 0x00000010 +#define UARTMODIR_RXRTSE 0x00000008 +#define UARTMODIR_TXRTSPOL 0x00000004 +#define UARTMODIR_TXRTSE 0x00000002 +#define UARTMODIR_TXCTSE 0x00000001 + +#define UARTFIFO_TXEMPT 0x00800000 +#define UARTFIFO_RXEMPT 0x00400000 +#define UARTFIFO_TXOF 0x00020000 +#define UARTFIFO_RXUF 0x00010000 +#define UARTFIFO_TXFLUSH 0x00008000 +#define UARTFIFO_RXFLUSH 0x00004000 +#define UARTFIFO_TXOFE 0x00000200 +#define UARTFIFO_RXUFE 0x00000100 +#define UARTFIFO_TXFE 0x00000080 +#define UARTFIFO_FIFOSIZE_MASK 0x7 +#define UARTFIFO_TXSIZE_OFF 4 +#define UARTFIFO_RXFE 0x00000008 +#define UARTFIFO_RXSIZE_OFF 0 + +#define UARTWATER_COUNT_MASK 0xff +#define UARTWATER_TXCNT_OFF 8 +#define UARTWATER_RXCNT_OFF 24 +#define UARTWATER_WATER_MASK 0xff +#define UARTWATER_TXWATER_OFF 0 +#define UARTWATER_RXWATER_OFF 16 + #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" #define UART_NR 6 @@ -121,16 +228,30 @@ struct lpuart_port { struct clk *clk; unsigned int txfifo_size; unsigned int rxfifo_size; + bool lpuart32; }; static struct of_device_id lpuart_dt_ids[] = { { .compatible = "fsl,vf610-lpuart", }, + { + .compatible = "fsl,ls1021a-lpuart", + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpuart_dt_ids); +static u32 lpuart32_read(void __iomem *addr) +{ + return ioread32be(addr); +} + +static void lpuart32_write(u32 val, void __iomem *addr) +{ + iowrite32be(val, addr); +} + static void lpuart_stop_tx(struct uart_port *port) { unsigned char temp; @@ -140,6 +261,15 @@ static void lpuart_stop_tx(struct uart_port *port) writeb(temp, port->membase + UARTCR2); } +static void lpuart32_stop_tx(struct uart_port *port) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL); + temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); + lpuart32_write(temp, port->membase + UARTCTRL); +} + static void lpuart_stop_rx(struct uart_port *port) { unsigned char temp; @@ -148,6 +278,14 @@ static void lpuart_stop_rx(struct uart_port *port) writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); } +static void lpuart32_stop_rx(struct uart_port *port) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL); + lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); +} + static void lpuart_enable_ms(struct uart_port *port) { } @@ -170,6 +308,30 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport) lpuart_stop_tx(&sport->port); } +static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long txcnt; + + txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = txcnt >> UARTWATER_TXCNT_OFF; + txcnt &= UARTWATER_COUNT_MASK; + while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) { + lpuart32_write(xmit->buf[xmit->tail], sport->port.membase + UARTDATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = txcnt >> UARTWATER_TXCNT_OFF; + txcnt &= UARTWATER_COUNT_MASK; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + lpuart32_stop_tx(&sport->port); +} + static void lpuart_start_tx(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -182,6 +344,18 @@ static void lpuart_start_tx(struct uart_port *port) lpuart_transmit_buffer(sport); } +static void lpuart32_start_tx(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL); + lpuart32_write(temp | UARTCTRL_TIE, port->membase + UARTCTRL); + + if (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE) + lpuart32_transmit_buffer(sport); +} + static irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -190,16 +364,25 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); if (sport->port.x_char) { - writeb(sport->port.x_char, sport->port.membase + UARTDR); + if (sport->lpuart32) + lpuart32_write(sport->port.x_char, sport->port.membase + UARTDATA); + else + writeb(sport->port.x_char, sport->port.membase + UARTDR); goto out; } if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - lpuart_stop_tx(&sport->port); + if (sport->lpuart32) + lpuart32_stop_tx(&sport->port); + else + lpuart_stop_tx(&sport->port); goto out; } - lpuart_transmit_buffer(sport); + if (sport->lpuart32) + lpuart32_transmit_buffer(sport); + else + lpuart_transmit_buffer(sport); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); @@ -272,6 +455,70 @@ out: return IRQ_HANDLED; } +static irqreturn_t lpuart32_rxint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned int flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + unsigned long rx, sr; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!(lpuart32_read(sport->port.membase + UARTFIFO) & UARTFIFO_RXEMPT)) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + /* + * to clear the FE, OR, NF, FE, PE flags, + * read STAT then read DATA reg + */ + sr = lpuart32_read(sport->port.membase + UARTSTAT); + rx = lpuart32_read(sport->port.membase + UARTDATA); + rx &= 0x3ff; + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (sr & (UARTSTAT_PE | UARTSTAT_OR | UARTSTAT_FE)) { + if (sr & UARTSTAT_PE) + sport->port.icount.parity++; + else if (sr & UARTSTAT_FE) + sport->port.icount.frame++; + + if (sr & UARTSTAT_OR) + sport->port.icount.overrun++; + + if (sr & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + sr &= sport->port.read_status_mask; + + if (sr & UARTSTAT_PE) + flg = TTY_PARITY; + else if (sr & UARTSTAT_FE) + flg = TTY_FRAME; + + if (sr & UARTSTAT_OR) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(port, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + return IRQ_HANDLED; +} + static irqreturn_t lpuart_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -289,6 +536,26 @@ static irqreturn_t lpuart_int(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t lpuart32_int(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned long sts, rxcount; + + sts = lpuart32_read(sport->port.membase + UARTSTAT); + rxcount = lpuart32_read(sport->port.membase + UARTWATER); + rxcount = rxcount >> UARTWATER_RXCNT_OFF; + + if (sts & UARTSTAT_RDRF || rxcount > 0) + lpuart32_rxint(irq, dev_id); + + if ((sts & UARTSTAT_TDRE) && + !(lpuart32_read(sport->port.membase + UARTBAUD) & UARTBAUD_TDMAE)) + lpuart_txint(irq, dev_id); + + lpuart32_write(sts, sport->port.membase + UARTSTAT); + return IRQ_HANDLED; +} + /* return TIOCSER_TEMT when transmitter is not busy */ static unsigned int lpuart_tx_empty(struct uart_port *port) { @@ -296,6 +563,12 @@ static unsigned int lpuart_tx_empty(struct uart_port *port) TIOCSER_TEMT : 0; } +static unsigned int lpuart32_tx_empty(struct uart_port *port) +{ + return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? + TIOCSER_TEMT : 0; +} + static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int temp = 0; @@ -311,6 +584,21 @@ static unsigned int lpuart_get_mctrl(struct uart_port *port) return temp; } +static unsigned int lpuart32_get_mctrl(struct uart_port *port) +{ + unsigned int temp = 0; + unsigned long reg; + + reg = lpuart32_read(port->membase + UARTMODIR); + if (reg & UARTMODIR_TXCTSE) + temp |= TIOCM_CTS; + + if (reg & UARTMODIR_RXRTSE) + temp |= TIOCM_RTS; + + return temp; +} + static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned char temp; @@ -327,6 +615,22 @@ static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) writeb(temp, port->membase + UARTMODEM); } +static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTMODIR) & + ~(UARTMODIR_RXRTSE | UARTMODIR_TXCTSE); + + if (mctrl & TIOCM_RTS) + temp |= UARTMODIR_RXRTSE; + + if (mctrl & TIOCM_CTS) + temp |= UARTMODIR_TXCTSE; + + lpuart32_write(temp, port->membase + UARTMODIR); +} + static void lpuart_break_ctl(struct uart_port *port, int break_state) { unsigned char temp; @@ -339,6 +643,18 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state) writeb(temp, port->membase + UARTCR2); } +static void lpuart32_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL) & ~UARTCTRL_SBK; + + if (break_state != 0) + temp |= UARTCTRL_SBK; + + lpuart32_write(temp, port->membase + UARTCTRL); +} + static void lpuart_setup_watermark(struct lpuart_port *sport) { unsigned char val, cr2; @@ -373,6 +689,31 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(cr2_saved, sport->port.membase + UARTCR2); } +static void lpuart32_setup_watermark(struct lpuart_port *sport) +{ + unsigned long val, ctrl; + unsigned long ctrl_saved; + + ctrl = lpuart32_read(sport->port.membase + UARTCTRL); + ctrl_saved = ctrl; + ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | + UARTCTRL_RIE | UARTCTRL_RE); + lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + + /* enable FIFO mode */ + val = lpuart32_read(sport->port.membase + UARTFIFO); + val |= UARTFIFO_TXFE | UARTFIFO_RXFE; + val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; + lpuart32_write(val, sport->port.membase + UARTFIFO); + + /* set the watermark */ + val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); + lpuart32_write(val, sport->port.membase + UARTWATER); + + /* Restore cr2 */ + lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL); +} + static int lpuart_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -397,6 +738,40 @@ static int lpuart_startup(struct uart_port *port) return 0; } +static int lpuart32_startup(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + int ret; + unsigned long flags; + unsigned long temp; + + /* determine FIFO size */ + temp = lpuart32_read(sport->port.membase + UARTFIFO); + + sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) & + UARTFIFO_FIFOSIZE_MASK) - 1); + + sport->rxfifo_size = 0x1 << (((temp >> UARTFIFO_RXSIZE_OFF) & + UARTFIFO_FIFOSIZE_MASK) - 1); + + ret = devm_request_irq(port->dev, port->irq, lpuart32_int, 0, + DRIVER_NAME, sport); + if (ret) + return ret; + + spin_lock_irqsave(&sport->port.lock, flags); + + lpuart32_setup_watermark(sport); + + temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE); + temp |= UARTCTRL_ILIE; + lpuart32_write(temp, sport->port.membase + UARTCTRL); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; +} + static void lpuart_shutdown(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -416,6 +791,25 @@ static void lpuart_shutdown(struct uart_port *port) devm_free_irq(port->dev, port->irq, sport); } +static void lpuart32_shutdown(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long temp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable Rx/Tx and interrupts */ + temp = lpuart32_read(port->membase + UARTCTRL); + temp &= ~(UARTCTRL_TE | UARTCTRL_RE | + UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); + lpuart32_write(temp, port->membase + UARTCTRL); + + spin_unlock_irqrestore(&port->lock, flags); + + devm_free_irq(port->dev, port->irq, sport); +} + static void lpuart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -542,6 +936,125 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&sport->port.lock, flags); } +static void +lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long flags; + unsigned long ctrl, old_ctrl, bd, modem; + unsigned int baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int sbr; + + ctrl = old_ctrl = lpuart32_read(sport->port.membase + UARTCTRL); + bd = lpuart32_read(sport->port.membase + UARTBAUD); + modem = lpuart32_read(sport->port.membase + UARTMODIR); + /* + * only support CS8 and CS7, and for CS7 must enable PE. + * supported mode: + * - (7,e/o,1) + * - (8,n,1) + * - (8,m/s,1) + * - (8,e/o,1) + */ + while ((termios->c_cflag & CSIZE) != CS8 && + (termios->c_cflag & CSIZE) != CS7) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8 || + (termios->c_cflag & CSIZE) == CS7) + ctrl = old_ctrl & ~UARTCTRL_M; + + if (termios->c_cflag & CMSPAR) { + if ((termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + } + ctrl |= UARTCTRL_M; + } + + if (termios->c_cflag & CRTSCTS) { + modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } else { + termios->c_cflag &= ~CRTSCTS; + modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } + + if (termios->c_cflag & CSTOPB) + termios->c_cflag &= ~CSTOPB; + + /* parity must be enabled when CS7 to match 8-bits format */ + if ((termios->c_cflag & CSIZE) == CS7) + termios->c_cflag |= PARENB; + + if ((termios->c_cflag & PARENB)) { + if (termios->c_cflag & CMSPAR) { + ctrl &= ~UARTCTRL_PE; + ctrl |= UARTCTRL_M; + } else { + ctrl |= UARTCR1_PE; + if ((termios->c_cflag & CSIZE) == CS8) + ctrl |= UARTCTRL_M; + if (termios->c_cflag & PARODD) + ctrl |= UARTCTRL_PT; + else + ctrl &= ~UARTCTRL_PT; + } + } + + /* ask the core to calculate the divisor */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= (UARTSTAT_FE | UARTSTAT_PE); + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + sport->port.read_status_mask |= UARTSTAT_FE; + + /* characters to ignore */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSTAT_PE; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= UARTSTAT_FE; + /* + * if we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSTAT_OR; + } + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* wait transmit engin complete */ + while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + barrier(); + + /* disable transmit and receive */ + lpuart32_write(old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), + sport->port.membase + UARTCTRL); + + sbr = sport->port.uartclk / (16 * baud); + bd &= ~UARTBAUD_SBR_MASK; + bd |= sbr & UARTBAUD_SBR_MASK; + bd |= UARTBAUD_BOTHEDGE; + bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); + lpuart32_write(bd, sport->port.membase + UARTBAUD); + lpuart32_write(modem, sport->port.membase + UARTMODIR); + lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + /* restore control register */ + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + static const char *lpuart_type(struct uart_port *port) { return "FSL_LPUART"; @@ -602,6 +1115,24 @@ static struct uart_ops lpuart_pops = { .verify_port = lpuart_verify_port, }; +static struct uart_ops lpuart32_pops = { + .tx_empty = lpuart32_tx_empty, + .set_mctrl = lpuart32_set_mctrl, + .get_mctrl = lpuart32_get_mctrl, + .stop_tx = lpuart32_stop_tx, + .start_tx = lpuart32_start_tx, + .stop_rx = lpuart32_stop_rx, + .break_ctl = lpuart32_break_ctl, + .startup = lpuart32_startup, + .shutdown = lpuart32_shutdown, + .set_termios = lpuart32_set_termios, + .type = lpuart_type, + .request_port = lpuart_request_port, + .release_port = lpuart_release_port, + .config_port = lpuart_config_port, + .verify_port = lpuart_verify_port, +}; + static struct lpuart_port *lpuart_ports[UART_NR]; #ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE @@ -613,6 +1144,14 @@ static void lpuart_console_putchar(struct uart_port *port, int ch) writeb(ch, port->membase + UARTDR); } +static void lpuart32_console_putchar(struct uart_port *port, int ch) +{ + while (!(lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE)) + barrier(); + + lpuart32_write(ch, port->membase + UARTDATA); +} + static void lpuart_console_write(struct console *co, const char *s, unsigned int count) { @@ -634,6 +1173,27 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) writeb(old_cr2, sport->port.membase + UARTCR2); } +static void +lpuart32_console_write(struct console *co, const char *s, unsigned int count) +{ + struct lpuart_port *sport = lpuart_ports[co->index]; + unsigned long old_cr, cr; + + /* first save CR2 and then disable interrupts */ + cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr |= (UARTCTRL_TE | UARTCTRL_RE); + cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); + lpuart32_write(cr, sport->port.membase + UARTCTRL); + + uart_console_write(&sport->port, s, count, lpuart32_console_putchar); + + /* wait for transmitter finish complete and restore CR2 */ + while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + barrier(); + + lpuart32_write(old_cr, sport->port.membase + UARTCTRL); +} + /* * if the port was already initialised (eg, by a boot loader), * try to determine the current setup. @@ -687,6 +1247,49 @@ lpuart_console_get_options(struct lpuart_port *sport, int *baud, "from %d to %d\n", baud_raw, *baud); } +static void __init +lpuart32_console_get_options(struct lpuart_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned long cr, bd; + unsigned int sbr, uartclk, baud_raw; + + cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr &= UARTCTRL_TE | UARTCTRL_RE; + if (!cr) + return; + + /* ok, the port was enabled */ + + cr = lpuart32_read(sport->port.membase + UARTCTRL); + + *parity = 'n'; + if (cr & UARTCTRL_PE) { + if (cr & UARTCTRL_PT) + *parity = 'o'; + else + *parity = 'e'; + } + + if (cr & UARTCTRL_M) + *bits = 9; + else + *bits = 8; + + bd = lpuart32_read(sport->port.membase + UARTBAUD); + bd &= UARTBAUD_SBR_MASK; + sbr = bd; + uartclk = clk_get_rate(sport->clk); + /* + * baud = mod_clk/(16*(sbr[13]+(brfa)/32) + */ + baud_raw = uartclk / (16 * sbr); + + if (*baud != baud_raw) + printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + "from %d to %d\n", baud_raw, *baud); +} + static int __init lpuart_console_setup(struct console *co, char *options) { struct lpuart_port *sport; @@ -710,9 +1313,15 @@ static int __init lpuart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - lpuart_console_get_options(sport, &baud, &parity, &bits); + if (sport->lpuart32) + lpuart32_console_get_options(sport, &baud, &parity, &bits); + else + lpuart_console_get_options(sport, &baud, &parity, &bits); - lpuart_setup_watermark(sport); + if (sport->lpuart32) + lpuart32_setup_watermark(sport); + else + lpuart_setup_watermark(sport); return uart_set_options(&sport->port, co, baud, parity, bits, flow); } @@ -728,9 +1337,21 @@ static struct console lpuart_console = { .data = &lpuart_reg, }; +static struct console lpuart32_console = { + .name = DEV_NAME, + .write = lpuart32_console_write, + .device = uart_console_device, + .setup = lpuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpuart_reg, +}; + #define LPUART_CONSOLE (&lpuart_console) +#define LPUART32_CONSOLE (&lpuart32_console) #else #define LPUART_CONSOLE NULL +#define LPUART32_CONSOLE NULL #endif static struct uart_driver lpuart_reg = { @@ -760,7 +1381,7 @@ static int lpuart_probe(struct platform_device *pdev) return ret; } sport->port.line = ret; - + sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; @@ -774,7 +1395,10 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.type = PORT_LPUART; sport->port.iotype = UPIO_MEM; sport->port.irq = platform_get_irq(pdev, 0); - sport->port.ops = &lpuart_pops; + if (sport->lpuart32) + sport->port.ops = &lpuart32_pops; + else + sport->port.ops = &lpuart_pops; sport->port.flags = UPF_BOOT_AUTOCONF; sport->clk = devm_clk_get(&pdev->dev, "ipg"); @@ -796,6 +1420,11 @@ static int lpuart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &sport->port); + if (sport->lpuart32) + lpuart_reg.cons = LPUART32_CONSOLE; + else + lpuart_reg.cons = LPUART_CONSOLE; + ret = uart_add_one_port(&lpuart_reg, &sport->port); if (ret) { clk_disable_unprepare(sport->clk); -- cgit v0.10.2 From 71a6c0cd5defd26d33a570e6a590122af9d830df Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 18 Feb 2014 10:17:12 +0800 Subject: dma: Add Freescale eDMA engine driver support Add Freescale enhanced direct memory(eDMA) controller support. This module can be found on Vybrid and LS-1 SoCs. Signed-off-by: Alison Wang Signed-off-by: Jingchang Lu Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- This patch is pulled back from upstream: commit d6be34fbd39b7d577d25cb4edec538e8990ba07c 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@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@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 03d3975..9fb8108 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -342,6 +342,16 @@ config FSL_RAID the capability to offload RAID5/RAID6 operations from CPU. RAID5 is XOR and memcpy. RAID6 is P/Q and memcpy +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 2d8b4f5..3121300 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..9025300 --- /dev/null +++ b/drivers/dma/fsl-edma.c @@ -0,0 +1,975 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) + +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_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->dstn_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = true; + caps->cmd_terminate = true; + + return 0; +} + +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; + fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps; + + 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"); -- cgit v0.10.2 From 47f50b7e820f616ada529bbe10f7219490fad3ac Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 21 Feb 2014 14:50:06 +0800 Subject: dma: fsl-edma: fix static checker warning of NULL dereference The static checker reports following warning: drivers/dma/fsl-edma.c:732 fsl_edma_xlate() error: we previously assumed 'chan' could be null (see line 737) The changes of the loop cursor in the iteration may result in NULL dereference when dma_get_slave_channel failed but loop will continue. So use list_for_each_entry_safe() instead of list_for_each_entry() to against this. Reported-by: Dan Carpenter Signed-off-by: Jingchang Lu Signed-off-by: Vinod Koul --- This patch is pulled back from upstream: commit 178c81e58e91559fd2c6b1cae43c8f573a2ead36 diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 9025300..381e793 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -723,13 +723,13 @@ 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; + struct dma_chan *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) { + list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { if (chan->client_count) continue; if ((chan->chan_id / DMAMUX_NR) == dma_spec->args[0]) { -- cgit v0.10.2 From ce36ab46b4196b21bbf966696408d9fe58e082bd Mon Sep 17 00:00:00 2001 From: Yuan Yao Date: Fri, 4 Apr 2014 12:27:55 +0800 Subject: dma: fix eDMA driver as a subsys_initcall Because of some driver base on DMA, changed the initcall order as subsys_initcall. Signed-off-by: Yuan Yao Signed-off-by: Vinod Koul --- This patch is pulled back from upstream: commit 8edc51c197b8f409bef7b21755254e6f3ce7ed23 diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 381e793..b396a7f 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -968,7 +968,17 @@ static struct platform_driver fsl_edma_driver = { .remove = fsl_edma_remove, }; -module_platform_driver(fsl_edma_driver); +static int __init fsl_edma_init(void) +{ + return platform_driver_register(&fsl_edma_driver); +} +subsys_initcall(fsl_edma_init); + +static void __exit fsl_edma_exit(void) +{ + platform_driver_unregister(&fsl_edma_driver); +} +module_exit(fsl_edma_exit); MODULE_ALIAS("platform:fsl-edma"); MODULE_DESCRIPTION("Freescale eDMA engine driver"); -- cgit v0.10.2 From 06a082ea233a5fa790884ff25a8f0d4c93268193 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 12 May 2014 21:33:43 +0800 Subject: dmaengine: fsl-edma: fix dmamux index calculating error Signed-off-by: Jingchang Lu --- This patch has been sent to upstream: https://patchwork.kernel.org/patch/4457391/ diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index b396a7f..24ab3d3 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -248,11 +248,12 @@ 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]; + void __iomem *muxaddr; 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; + muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; if (enable) edma_writeb(fsl_chan->edma, @@ -724,6 +725,7 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, { struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; struct dma_chan *chan, *_chan; + unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR; if (dma_spec->args_count != 2) return NULL; @@ -732,7 +734,7 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { if (chan->client_count) continue; - if ((chan->chan_id / DMAMUX_NR) == dma_spec->args[0]) { + if ((chan->chan_id / chans_per_mux) == dma_spec->args[0]) { chan = dma_get_slave_channel(chan); if (chan) { chan->device->privatecnt++; -- cgit v0.10.2 From 0fa22197b06b301ca0b6bd6ed8c476efa2915f9e Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Wed, 9 Jul 2014 14:38:27 +0800 Subject: dmaengine: fsl-edma: swap 8-/16-bit registers offset in big-endian mode As the IP design, all 8-bit and 16-bit registers offset adddress should be swapped in big-endian mode opposite to little-endian mode. Signed-off-by: Jingchang Lu diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 24ab3d3..8040f25 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -177,12 +177,15 @@ struct fsl_edma_engine { /* * R/W functions for big- or little-endian registers * the eDMA controller's endian is independent of the CPU core's endian. + * for the big-endian IP module, the offset for 8-bit or 16-bit register + * should also be swapped oposite to that in little-endian IP. */ static u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr) { + /* swap the reg offset for that in big-endian mode*/ if (edma->big_endian) - return ioread16be(addr); + return ioread16be((void __iomem *)(((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2))); else return ioread16(addr); } @@ -197,11 +200,24 @@ static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr) { - iowrite8(val, addr); + /* swap the reg offset for that in big-endian mode*/ + if (edma->big_endian) + iowrite8(val, (void __iomem *)(((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x3))); + else + iowrite8(val, addr); } static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) { + /* swap the reg offset for that in big-endian mode*/ + if (edma->big_endian) + writew(val, (void __iomem *)(((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2))); + else + writew(val, addr); +} + +static void edma_tcd_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) +{ if (edma->big_endian) iowrite16be(val, addr); else @@ -256,11 +272,10 @@ static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; if (enable) - edma_writeb(fsl_chan->edma, - EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot), + writeb(EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot), muxaddr + ch_off); else - edma_writeb(fsl_chan->edma, EDMAMUX_CHCFG_DIS, muxaddr + ch_off); + writeb(EDMAMUX_CHCFG_DIS, muxaddr + ch_off); } static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) @@ -436,18 +451,18 @@ static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan, * 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)); + edma_writew(fsl_chan->edma, 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)); + edma_writew(fsl_chan->edma, attr, addr + EDMA_TCD_ATTR(ch)); + edma_writew(fsl_chan->edma, 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)); + edma_writew(fsl_chan->edma, citer, addr + EDMA_TCD_CITER(ch)); + edma_writew(fsl_chan->edma, biter, addr + EDMA_TCD_BITER(ch)); + edma_writew(fsl_chan->edma, doff, addr + EDMA_TCD_DOFF(ch)); writel(dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch)); - writew(csr, addr + EDMA_TCD_CSR(ch)); + edma_writew(fsl_chan->edma, csr, addr + EDMA_TCD_CSR(ch)); } static void fill_tcd_params(struct fsl_edma_engine *edma, @@ -465,14 +480,14 @@ static void fill_tcd_params(struct fsl_edma_engine *edma, */ 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_tcd_writew(edma, attr, &(tcd->attr)); + edma_tcd_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_tcd_writew(edma, EDMA_TCD_CITER_CITER(citer), &(tcd->citer)); + edma_tcd_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)); + edma_tcd_writew(edma, EDMA_TCD_BITER_BITER(biter), &(tcd->biter)); if (major_int) csr |= EDMA_TCD_CSR_INT_MAJOR; @@ -482,7 +497,7 @@ static void fill_tcd_params(struct fsl_edma_engine *edma, if (enable_sg) csr |= EDMA_TCD_CSR_E_SG; - edma_writew(edma, csr, &(tcd->csr)); + edma_tcd_writew(edma, csr, &(tcd->csr)); } static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, -- cgit v0.10.2 From 12296039029b255945406aec91e99e50cf6dda4e Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Thu, 10 Jul 2014 10:11:29 +0800 Subject: dmaengine: fsl-edma: add dma memcpy support The eDMA channel support the mem2mem copy with the always on slot number 63. Signed-off-by: Jingchang Lu diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 8040f25..9159831 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -638,6 +638,27 @@ static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); } +static struct dma_async_tx_descriptor * +fsl_edma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long tx_flags) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + + fsl_desc = fsl_edma_alloc_desc(fsl_chan, 1); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = false; + + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[0].vtcd, src, dst, + EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT, + 0x4, len, 0, 1, 1, 0x4, 0, + true, true, false); + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, tx_flags); + +} + static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) { struct fsl_edma_hw_tcd *tcd; @@ -920,6 +941,7 @@ static int fsl_edma_probe(struct platform_device *pdev) 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); + dma_cap_set(DMA_MEMCPY, fsl_edma->dma_dev.cap_mask); fsl_edma->dma_dev.dev = &pdev->dev; fsl_edma->dma_dev.device_alloc_chan_resources @@ -932,6 +954,7 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_edma->dma_dev.device_control = fsl_edma_control; fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps; + fsl_edma->dma_dev.device_prep_dma_memcpy = fsl_edma_prep_memcpy; platform_set_drvdata(pdev, fsl_edma); -- cgit v0.10.2 From 93afd19c8b0f570bce7f662e35c97994bb8c6cd6 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 29 Aug 2014 17:34:34 +0800 Subject: arm: configs: add Freescale LS1021A initial defconfig Signed-off-by: Jingchang Lu Change-Id: Ia3ba1dbeb66d4c929cfe19122d221a2af36377f8 Reviewed-on: http://git.am.freescale.net:8181/17838 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/configs/ls1021a_defconfig b/arch/arm/configs/ls1021a_defconfig new file mode 100644 index 0000000..052e4f9 --- /dev/null +++ b/arch/arm/configs/ls1021a_defconfig @@ -0,0 +1,163 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_CMDLINE_PARSER=y +CONFIG_ARCH_MXC=y +CONFIG_SOC_LS1021A=y +CONFIG_ARM_LPAE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_THUMB2_KERNEL=y +CONFIG_HIGHMEM=y +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_CMDLINE="console=ttyS0,115200" +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_BINFMT_MISC=y +# CONFIG_SUSPEND is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_MROUTE=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +CONFIG_CAN=y +# CONFIG_CAN_BCM is not set +# CONFIG_CAN_GW is not set +CONFIG_CAN_FLEXCAN=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_FW_LOADER is not set +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_NAND=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=256000 +CONFIG_SRAM=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_ATA=y +CONFIG_NETDEVICES=y +CONFIG_VITESSE_PHY=y +CONFIG_BROADCOM_PHY=y +CONFIG_REALTEK_PHY=y +CONFIG_NATIONAL_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_INPUT_EVDEV=y +# CONFIG_MOUSE_PS2_TRACKPOINT is not set +CONFIG_SERIO_SERPORT=m +# CONFIG_CONSOLE_TRANSLATIONS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_DMA is not set +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_GPIO_SYSFS=y +CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_INA2XX=y +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS3232=y +CONFIG_DMADEVICES=y +CONFIG_FSL_EDMA=y +CONFIG_MEMORY=y +# CONFIG_RESET_CONTROLLER is not set +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y +CONFIG_FANOTIFY=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=m +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_UTF8=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m -- cgit v0.10.2 From 51fea93065e9b2686b45e9068dc75a55e652e0bd Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 11 Aug 2014 13:56:35 +0800 Subject: arm: dts: ls1021a: change duart compatible to "fsl,16550-FIFO64" this patch change the duart compatible to 64-byte FIFO mode. Signed-off-by: Jingchang Lu Change-Id: I4d306671daed4262a6f354a3507304d82468c41d Reviewed-on: http://git.am.freescale.net:8181/17835 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index 91aa312..e1076fa 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -278,38 +278,38 @@ }; duart0: serial@21c0500 { - compatible = "fsl,ns16550", "ns16550a"; + compatible = "fsl,16550-FIFO64"; reg = <0x0 0x21c0500 0x0 0x100>; interrupts = ; clock-frequency = <0>; - fifo-size = <8>; + fifo-size = <63>; status = "disabled"; }; duart1: serial@21c0600 { - compatible = "fsl,ns16550", "ns16550a"; + compatible = "fsl,16550-FIFO64"; reg = <0x0 0x21c0600 0x0 0x100>; interrupts = ; clock-frequency = <0>; - fifo-size = <8>; + fifo-size = <63>; status = "disabled"; }; duart2: serial@21d0500 { - compatible = "fsl,ns16550", "ns16550a"; + compatible = "fsl,16550-FIFO64"; reg = <0x0 0x21d0500 0x0 0x100>; interrupts = ; clock-frequency = <0>; - fifo-size = <8>; + fifo-size = <63>; status = "disabled"; }; duart3: serial@21d0600 { - compatible = "fsl,ns16550", "ns16550a"; + compatible = "fsl,16550-FIFO64"; reg = <0x0 0x21d0600 0x0 0x100>; interrupts = ; clock-frequency = <0>; - fifo-size = <8>; + fifo-size = <63>; status = "disabled"; }; -- cgit v0.10.2 From a25f458873b059da4b0684a36536b27100d3e830 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 29 Aug 2014 17:00:02 +0800 Subject: arm: ls1021a: remove the FSL_SOC config added by SOC_LS1021A Signed-off-by: Jingchang Lu Change-Id: I2deac22e04a04c2523c7839d5974a41288e5bb2b Reviewed-on: http://git.am.freescale.net:8181/17837 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 3948af8..5f43281 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -846,9 +846,6 @@ config SOC_VF610 help This enable support for Freescale Vybrid VF610 processor. -config FSL_SOC - bool - config SOC_LS1021A bool "Freescale LS1021A support" select CPU_V7 @@ -856,7 +853,6 @@ config SOC_LS1021A select CLKSRC_OF select HAVE_ARM_ARCH_TIMER select HAVE_SMP - select FSL_SOC select MIGHT_HAVE_PCI select PCI_DOMAINS if PCI select ZONE_DMA if ARM_LPAE -- cgit v0.10.2 From 138d7b8f762489543abf8290333b6fa69baff837 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 21 Jul 2014 17:40:21 +0800 Subject: arm: dts: ls1021a-qds: add tbi node and aliases for enet phy This move the tbi node to boards level device tree source. And add aliases for enet phy to make it be found easily by u-boot on dynamically change the enet "phy-handle" and "phy-connection-type" property. Signed-off-by: Jingchang Lu Change-Id: I80748fdbbeab06cb5804128600369317dbececd6 Reviewed-on: http://git.am.freescale.net:8181/17830 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts index 2deed2d..d162b8b 100644 --- a/arch/arm/boot/dts/ls1021a-qds.dts +++ b/arch/arm/boot/dts/ls1021a-qds.dts @@ -13,6 +13,14 @@ / { model = "LS1021A QDS Board"; + aliases { + enet0_rgmii_phy = &rgmii_phy1; + enet1_rgmii_phy = &rgmii_phy2; + enet2_rgmii_phy = &rgmii_phy3; + enet0_sgmii_phy = &sgmii_phy1c; + enet1_sgmii_phy = &sgmii_phy1d; + }; + soc { leds { compatible = "pwm-leds"; @@ -85,21 +93,21 @@ &enet0 { tbi-handle = <&tbi0>; - phy-handle = <&physgmii1c>; + phy-handle = <&sgmii_phy1c>; phy-connection-type = "sgmii"; status = "okay"; }; &enet1 { tbi-handle = <&tbi0>; - phy-handle = <&physgmii1d>; + phy-handle = <&sgmii_phy1d>; phy-connection-type = "sgmii"; status = "okay"; }; &enet2 { - phy-handle = <&phyrgmii3>; - phy-connection-type = "rgmii"; + phy-handle = <&rgmii_phy3>; + phy-connection-type = "rgmii-id"; status = "okay"; }; @@ -243,7 +251,7 @@ reg = <0>; #address-cells = <1>; #size-cells = <0>; - phyrgmii1: ethernet-phy@1 { + rgmii_phy1: ethernet-phy@1 { reg = <0x1>; }; }; @@ -251,7 +259,7 @@ reg = <0x20>; #address-cells = <1>; #size-cells = <0>; - phyrgmii2: ethernet-phy@2 { + rgmii_phy2: ethernet-phy@2 { reg = <0x2>; }; }; @@ -259,7 +267,7 @@ reg = <0x40>; #address-cells = <1>; #size-cells = <0>; - phyrgmii3: ethernet-phy@3 { + rgmii_phy3: ethernet-phy@3 { reg = <0x3>; }; }; @@ -267,7 +275,7 @@ reg = <0x60>; #address-cells = <1>; #size-cells = <0>; - physgmii1c: ethernet-phy@1c { + sgmii_phy1c: ethernet-phy@1c { reg = <0x1c>; }; }; @@ -275,7 +283,7 @@ reg = <0x80>; #address-cells = <1>; #size-cells = <0>; - physgmii1d: ethernet-phy@1d { + sgmii_phy1d: ethernet-phy@1d { reg = <0x1d>; }; }; @@ -289,6 +297,13 @@ status = "okay"; }; +&mdio0 { + tbi0: tbi-phy@8 { + reg = <0x8>; + device_type = "tbi-phy"; + }; +}; + &pwm3 { status = "okay"; }; -- cgit v0.10.2 From c77758fa1763ec4b0f68719d3b270d6ca9f5b23c Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 21 Jul 2014 17:44:10 +0800 Subject: arm: dts: ls1021a-twr: add aliases for enet phy This add aliases for enet phy to make it be found easily in u-boot on dynamically change the enet "phy-handle" and "phy-connection-type" property. Signed-off-by: Jingchang Lu Change-Id: I60e19aa48856c9b9048415d1c8924b626d70332a Reviewed-on: http://git.am.freescale.net:8181/17831 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts index 7adc70b..6d2fd91 100755 --- a/arch/arm/boot/dts/ls1021a-twr.dts +++ b/arch/arm/boot/dts/ls1021a-twr.dts @@ -12,6 +12,12 @@ / { model = "LS1021A TWR Board"; + + aliases { + enet2_rgmii_phy = &rgmii_phy1; + enet0_sgmii_phy = &sgmii_phy2; + enet1_sgmii_phy = &sgmii_phy0; + }; }; &dspi1 { @@ -39,22 +45,22 @@ &enet0 { tbi-handle = <&tbi1>; - phy-handle = <&phy2>; + phy-handle = <&sgmii_phy2>; phy-connection-type = "sgmii"; - status = "ok"; + status = "okay"; }; &enet1 { tbi-handle = <&tbi1>; - phy-handle = <&phy0>; + phy-handle = <&sgmii_phy0>; phy-connection-type = "sgmii"; - status = "ok"; + status = "okay"; }; &enet2 { - phy-handle = <&phy1>; + phy-handle = <&rgmii_phy1>; phy-connection-type = "rgmii-id"; - status = "ok"; + status = "okay"; }; &i2c0 { @@ -136,13 +142,13 @@ }; &mdio0 { - phy0: ethernet-phy@0 { + sgmii_phy0: ethernet-phy@0 { reg = <0x0>; }; - phy1: ethernet-phy@1 { + rgmii_phy1: ethernet-phy@1 { reg = <0x1>; }; - phy2: ethernet-phy@2 { + sgmii_phy2: ethernet-phy@2 { reg = <0x2>; }; tbi1: tbi-phy@1f { -- cgit v0.10.2 From 3771c4fce819a23d5be2b2edfd999465caf7204f Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 21 Jul 2014 17:36:27 +0800 Subject: arm: dts: ls1021a: remove the tbi node from SoC level dts remove the tbi node which will be added to boards level dts. Signed-off-by: Jingchang Lu Change-Id: I1b7893526e63d0207637f2ae0576c9d5f62a6a06 Reviewed-on: http://git.am.freescale.net:8181/17829 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index e1076fa..ff59a6a 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -460,10 +460,6 @@ #address-cells = <1>; #size-cells = <0>; reg = <0x0 0x2d24000 0x0 0x4000>; - tbi0: tbi-phy@8 { - reg = <0x8>; - device_type = "tbi-phy"; - }; }; enet0: ethernet@2d10000 { -- cgit v0.10.2 From a8d64051683c43eee6841f7ef841cef308c9dd46 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 1 Sep 2014 15:29:43 +0800 Subject: arm: dts: ls1021a: change sysclk compatible to "fixed-clock" The sysclk could be well probed by "fixed-clock" compatible, no custom compatible is needed any more. Signed-off-by: Jingchang Lu Change-Id: I17a21e20ced4304e716e5a9ba07ff56b2adb45a7 Reviewed-on: http://git.am.freescale.net:8181/17833 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index ff59a6a..afd4c2c 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -173,7 +173,7 @@ #address-cells = <1>; #size-cells = <0>; sysclk: sysclk { - compatible = "fsl,sys-clock"; + compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <100000000>; clock-output-names = "sysclk"; -- cgit v0.10.2 From 808efed94db71ea91bfc68b75caa176e57f3370d Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 21 Jul 2014 17:49:36 +0800 Subject: clk: ppc-corenet: remove "fsl,sys-clock" compatible added by LS1 The "fixed-clock" could handle the sysclk node properly. Signed-off-by: Jingchang Lu Change-Id: I1b7fdf0be4f19a32d14240566e17bae2152578d6 Reviewed-on: http://git.am.freescale.net:8181/17832 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index 358dd4a..bb8b5e0 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -239,7 +239,6 @@ err_clks: static const struct of_device_id clk_match[] __initconst = { { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "fsl,sys-clock", .data = of_fixed_clk_setup, }, { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, {} -- cgit v0.10.2 From ac69145e065cc0ae66a172a3763feab4b660114a Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Wed, 27 Aug 2014 14:50:00 +0800 Subject: tty: of_serial: add of support for Freescale 64-byte FIFO UART Signed-off-by: Jingchang Lu Change-Id: I667cb31ce4b5e8f5c2a4c5f2b88e677eb991b9b3 Reviewed-on: http://git.am.freescale.net:8181/17834 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/serial/of-serial.txt b/Documentation/devicetree/bindings/serial/of-serial.txt index 1928a3e..8c4fd03 100644 --- a/Documentation/devicetree/bindings/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/serial/of-serial.txt @@ -14,6 +14,7 @@ Required properties: - "altr,16550-FIFO32" - "altr,16550-FIFO64" - "altr,16550-FIFO128" + - "fsl,16550-FIFO64" - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 2caf9c6..b4507be 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -262,6 +262,8 @@ static struct of_device_id of_platform_serial_table[] = { { .compatible = "ibm,qpace-nwp-serial", .data = (void *)PORT_NWPSERIAL, }, #endif + { .compatible = "fsl,16550-FIFO64", + .data = (void *)PORT_16550A_FSL64, }, { .type = "serial", .data = (void *)PORT_UNKNOWN, }, { /* end of list */ }, }; -- cgit v0.10.2 From 36cf34973f840c0868fd24d5b29f8eca1f9c6c54 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 29 Aug 2014 16:57:20 +0800 Subject: clk: ppc-corenet: rename to ppc-qoriq for better represention The IP is shared on PPC and ARM, rename it to qoriq for better represention. Signed-off-by: Jingchang Lu Change-Id: Ib02baa7731d9d1d9955ffde9860deb517d8d7ca8 Reviewed-on: http://git.am.freescale.net:8181/17836 Reviewed-by: Zhengxiong Jin Tested-by: Review Code-CDREVIEW diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 60c0a01..0e29396 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -86,12 +86,12 @@ config COMMON_CLK_AXI_CLKGEN Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx FPGAs. It is commonly used in Analog Devices' reference designs. -config CLK_PPC_CORENET - bool "Clock driver for PowerPC corenet platforms" - depends on (PPC_E500MC || FSL_SOC) && OF +config CLK_QORIQ + bool "Clock driver for PowerPC corenet and ARM-based platforms" + depends on (PPC_E500MC || ARM) && OF ---help--- This adds the clock driver support for Freescale PowerPC corenet - platforms using common clock framework. + and Freescale ARM based platforms using common clock framework. endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7b11106..d653137 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -42,4 +42,4 @@ obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o -obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o +obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c deleted file mode 100644 index bb8b5e0..0000000 --- a/drivers/clk/clk-ppc-corenet.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright 2013 Freescale Semiconductor, 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. - * - * clock driver for Freescale PowerPC corenet SoCs. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -struct cmux_clk { - struct clk_hw hw; - void __iomem *reg; - u32 flags; -}; - -#define PLL_KILL BIT(31) -#define CLKSEL_SHIFT 27 -#define CLKSEL_ADJUST BIT(0) -#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) - -static void __iomem *base; -static unsigned int clocks_per_pll; - -static int cmux_set_parent(struct clk_hw *hw, u8 idx) -{ - struct cmux_clk *clk = to_cmux_clk(hw); - u32 clksel; - - clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll; - if (clk->flags & CLKSEL_ADJUST) - clksel += 8; - clksel = (clksel & 0xf) << CLKSEL_SHIFT; - iowrite32be(clksel, clk->reg); - - return 0; -} - -static u8 cmux_get_parent(struct clk_hw *hw) -{ - struct cmux_clk *clk = to_cmux_clk(hw); - u32 clksel; - - clksel = ioread32be(clk->reg); - clksel = (clksel >> CLKSEL_SHIFT) & 0xf; - if (clk->flags & CLKSEL_ADJUST) - clksel -= 8; - clksel = (clksel >> 2) * clocks_per_pll + clksel % 4; - - return clksel; -} - -const struct clk_ops cmux_ops = { - .get_parent = cmux_get_parent, - .set_parent = cmux_set_parent, -}; - -static void __init core_mux_init(struct device_node *np) -{ - struct clk *clk; - struct clk_init_data init; - struct cmux_clk *cmux_clk; - struct device_node *node; - int rc, count, i; - u32 offset; - const char *clk_name; - const char **parent_names; - - rc = of_property_read_u32(np, "reg", &offset); - if (rc) { - pr_err("%s: could not get reg property\n", np->name); - return; - } - - /* get the input clock source count */ - count = of_property_count_strings(np, "clock-names"); - if (count < 0) { - pr_err("%s: get clock count error\n", np->name); - return; - } - parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL); - if (!parent_names) { - pr_err("%s: could not allocate parent_names\n", __func__); - return; - } - - for (i = 0; i < count; i++) - parent_names[i] = of_clk_get_parent_name(np, i); - - cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL); - if (!cmux_clk) { - pr_err("%s: could not allocate cmux_clk\n", __func__); - goto err_name; - } - cmux_clk->reg = base + offset; - - node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); - if (node && (offset >= 0x80)) - cmux_clk->flags = CLKSEL_ADJUST; - - rc = of_property_read_string_index(np, "clock-output-names", - 0, &clk_name); - if (rc) { - pr_err("%s: read clock names error\n", np->name); - goto err_clk; - } - - init.name = clk_name; - init.ops = &cmux_ops; - init.parent_names = parent_names; - init.num_parents = count; - init.flags = 0; - cmux_clk->hw.init = &init; - - clk = clk_register(NULL, &cmux_clk->hw); - if (IS_ERR(clk)) { - pr_err("%s: could not register clock\n", clk_name); - goto err_clk; - } - - rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); - if (rc) { - pr_err("Could not register clock provider for node:%s\n", - np->name); - goto err_clk; - } - goto err_name; - -err_clk: - kfree(cmux_clk); -err_name: - /* free *_names because they are reallocated when registered */ - kfree(parent_names); -} - -static void __init core_pll_init(struct device_node *np) -{ - u32 offset, mult; - int i, rc, count; - const char *clk_name, *parent_name; - struct clk_onecell_data *onecell_data; - struct clk **subclks; - - rc = of_property_read_u32(np, "reg", &offset); - if (rc) { - pr_err("%s: could not get reg property\n", np->name); - return; - } - - /* get the multiple of PLL */ - mult = ioread32be(base + offset); - - /* check if this PLL is disabled */ - if (mult & PLL_KILL) { - pr_debug("PLL:%s is disabled\n", np->name); - return; - } - mult = (mult >> 1) & 0x3f; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) { - pr_err("PLL: %s must have a parent\n", np->name); - return; - } - - count = of_property_count_strings(np, "clock-output-names"); - if (count < 0 || count > 4) { - pr_err("%s: clock is not supported\n", np->name); - return; - } - - /* output clock number per PLL */ - clocks_per_pll = count; - - subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); - if (!subclks) { - pr_err("%s: could not allocate subclks\n", __func__); - return; - } - - onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); - if (!onecell_data) { - pr_err("%s: could not allocate onecell_data\n", __func__); - goto err_clks; - } - - for (i = 0; i < count; i++) { - rc = of_property_read_string_index(np, "clock-output-names", - i, &clk_name); - if (rc) { - pr_err("%s: could not get clock names\n", np->name); - goto err_cell; - } - - /* - * when count == 4, there are 4 output clocks: - * /1, /2, /3, /4 respectively - * when count < 4, there are at least 2 output clocks: - * /1, /2, (/4, if count == 3) respectively. - */ - if (count == 4) - subclks[i] = clk_register_fixed_factor(NULL, clk_name, - parent_name, 0, mult, 1 + i); - else - - subclks[i] = clk_register_fixed_factor(NULL, clk_name, - parent_name, 0, mult, 1 << i); - - if (IS_ERR(subclks[i])) { - pr_err("%s: could not register clock\n", clk_name); - goto err_cell; - } - } - - onecell_data->clks = subclks; - onecell_data->clk_num = count; - - rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); - if (rc) { - pr_err("Could not register clk provider for node:%s\n", - np->name); - goto err_cell; - } - - return; -err_cell: - kfree(onecell_data); -err_clks: - kfree(subclks); -} - -static const struct of_device_id clk_match[] __initconst = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, - { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, - {} -}; - -static int __init ppc_corenet_clk_probe(struct platform_device *pdev) -{ - struct device_node *np; - - np = pdev->dev.of_node; - base = of_iomap(np, 0); - if (!base) { - dev_err(&pdev->dev, "iomap error\n"); - return -ENOMEM; - } - of_clk_init(clk_match); - - return 0; -} - -static const struct of_device_id ppc_clk_ids[] __initconst = { - { .compatible = "fsl,qoriq-clockgen-1.0", }, - { .compatible = "fsl,qoriq-clockgen-2.0", }, - {} -}; - -static struct platform_driver ppc_corenet_clk_driver __initdata = { - .driver = { - .name = "ppc_corenet_clock", - .owner = THIS_MODULE, - .of_match_table = ppc_clk_ids, - }, - .probe = ppc_corenet_clk_probe, -}; - -static int __init ppc_corenet_clk_init(void) -{ - return platform_driver_register(&ppc_corenet_clk_driver); -} -subsys_initcall(ppc_corenet_clk_init); - -static void __init ls1021a_clocks_init(struct device_node *np) -{ - base = of_iomap(np, 0); - if (!base) - return; - - of_clk_init(clk_match); -} -CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-clockgen", ls1021a_clocks_init); diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c new file mode 100644 index 0000000..6be6930 --- /dev/null +++ b/drivers/clk/clk-qoriq.c @@ -0,0 +1,291 @@ +/* + * Copyright 2013 Freescale Semiconductor, 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. + * + * clock driver for Freescale PowerPC corenet SoCs. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct cmux_clk { + struct clk_hw hw; + void __iomem *reg; + u32 flags; +}; + +#define PLL_KILL BIT(31) +#define CLKSEL_SHIFT 27 +#define CLKSEL_ADJUST BIT(0) +#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) + +static void __iomem *base; +static unsigned int clocks_per_pll; + +static int cmux_set_parent(struct clk_hw *hw, u8 idx) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll; + if (clk->flags & CLKSEL_ADJUST) + clksel += 8; + clksel = (clksel & 0xf) << CLKSEL_SHIFT; + iowrite32be(clksel, clk->reg); + + return 0; +} + +static u8 cmux_get_parent(struct clk_hw *hw) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ioread32be(clk->reg); + clksel = (clksel >> CLKSEL_SHIFT) & 0xf; + if (clk->flags & CLKSEL_ADJUST) + clksel -= 8; + clksel = (clksel >> 2) * clocks_per_pll + clksel % 4; + + return clksel; +} + +const struct clk_ops cmux_ops = { + .get_parent = cmux_get_parent, + .set_parent = cmux_set_parent, +}; + +static void __init core_mux_init(struct device_node *np) +{ + struct clk *clk; + struct clk_init_data init; + struct cmux_clk *cmux_clk; + struct device_node *node; + int rc, count, i; + u32 offset; + const char *clk_name; + const char **parent_names; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the input clock source count */ + count = of_property_count_strings(np, "clock-names"); + if (count < 0) { + pr_err("%s: get clock count error\n", np->name); + return; + } + parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL); + if (!parent_names) { + pr_err("%s: could not allocate parent_names\n", __func__); + return; + } + + for (i = 0; i < count; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL); + if (!cmux_clk) { + pr_err("%s: could not allocate cmux_clk\n", __func__); + goto err_name; + } + cmux_clk->reg = base + offset; + + node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); + if (node && (offset >= 0x80)) + cmux_clk->flags = CLKSEL_ADJUST; + + rc = of_property_read_string_index(np, "clock-output-names", + 0, &clk_name); + if (rc) { + pr_err("%s: read clock names error\n", np->name); + goto err_clk; + } + + init.name = clk_name; + init.ops = &cmux_ops; + init.parent_names = parent_names; + init.num_parents = count; + init.flags = 0; + cmux_clk->hw.init = &init; + + clk = clk_register(NULL, &cmux_clk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clock\n", clk_name); + goto err_clk; + } + + rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (rc) { + pr_err("Could not register clock provider for node:%s\n", + np->name); + goto err_clk; + } + goto err_name; + +err_clk: + kfree(cmux_clk); +err_name: + /* free *_names because they are reallocated when registered */ + kfree(parent_names); +} + +static void __init core_pll_init(struct device_node *np) +{ + u32 offset, mult; + int i, rc, count; + const char *clk_name, *parent_name; + struct clk_onecell_data *onecell_data; + struct clk **subclks; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the multiple of PLL */ + mult = ioread32be(base + offset); + + /* check if this PLL is disabled */ + if (mult & PLL_KILL) { + pr_debug("PLL:%s is disabled\n", np->name); + return; + } + mult = (mult >> 1) & 0x3f; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) { + pr_err("PLL: %s must have a parent\n", np->name); + return; + } + + count = of_property_count_strings(np, "clock-output-names"); + if (count < 0 || count > 4) { + pr_err("%s: clock is not supported\n", np->name); + return; + } + + /* output clock number per PLL */ + clocks_per_pll = count; + + subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); + if (!subclks) { + pr_err("%s: could not allocate subclks\n", __func__); + return; + } + + onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!onecell_data) { + pr_err("%s: could not allocate onecell_data\n", __func__); + goto err_clks; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(np, "clock-output-names", + i, &clk_name); + if (rc) { + pr_err("%s: could not get clock names\n", np->name); + goto err_cell; + } + + /* + * when count == 4, there are 4 output clocks: + * /1, /2, /3, /4 respectively + * when count < 4, there are at least 2 output clocks: + * /1, /2, (/4, if count == 3) respectively. + */ + if (count == 4) + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 + i); + else + + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 << i); + + if (IS_ERR(subclks[i])) { + pr_err("%s: could not register clock\n", clk_name); + goto err_cell; + } + } + + onecell_data->clks = subclks; + onecell_data->clk_num = count; + + rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); + if (rc) { + pr_err("Could not register clk provider for node:%s\n", + np->name); + goto err_cell; + } + + return; +err_cell: + kfree(onecell_data); +err_clks: + kfree(subclks); +} + +static const struct of_device_id clk_match[] __initconst = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, + { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, + {} +}; + +static int __init qoriq_corenet_clk_probe(struct platform_device *pdev) +{ + struct device_node *np; + + np = pdev->dev.of_node; + base = of_iomap(np, 0); + if (!base) { + dev_err(&pdev->dev, "iomap error\n"); + return -ENOMEM; + } + of_clk_init(clk_match); + + return 0; +} + +static const struct of_device_id qoriq_clk_ids[] __initconst = { + { .compatible = "fsl,qoriq-clockgen-1.0", }, + { .compatible = "fsl,qoriq-clockgen-2.0", }, + {} +}; + +static struct platform_driver qoriq_corenet_clk_driver __initdata = { + .driver = { + .name = "qoriq_corenet_clock", + .owner = THIS_MODULE, + .of_match_table = qoriq_clk_ids, + }, + .probe = qoriq_corenet_clk_probe, +}; + +static int __init qoriq_corenet_clk_init(void) +{ + return platform_driver_register(&qoriq_corenet_clk_driver); +} +subsys_initcall(qoriq_corenet_clk_init); + +static void __init ls1021a_clocks_init(struct device_node *np) +{ + base = of_iomap(np, 0); + if (!base) + return; + + of_clk_init(clk_match); +} +CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-clockgen", ls1021a_clocks_init); -- cgit v0.10.2 From b12afed58ca2bee2ee8708171a9bfb28bb6609d5 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Thu, 18 Sep 2014 10:26:51 +0800 Subject: arm: dts: ls1021a: add alias for the clockgen sysclk node This add an alias named sysclk for the sysclk node for fdt fixup procedure locating it uniquely. Signed-off-by: Jingchang Lu Change-Id: I7e6bd6cb4d81fe44c73944be91cab3fe56810094 Reviewed-on: http://git.am.freescale.net:8181/19199 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index afd4c2c..9afe2b4 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -26,6 +26,7 @@ ethernet0 = &enet0; ethernet1 = &enet1; ethernet2 = &enet2; + sysclk = &sysclk; }; memory@80000000 { -- cgit v0.10.2 From e154f0bec3fa8de71cee0e5668188c4b3be8c4cf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 21 Feb 2014 11:51:31 +0300 Subject: crypto: caam - writel() arguments are swapped My guess is that this little endian configuration is never found in real life, but if it were then the writel() arguments are in the wrong order so the driver would crash immediately. Signed-off-by: Dan Carpenter Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit f829e7a32c9434e31e565bc79f5804a7a984c10f) Change-Id: I8aa8e70dca9affa5da01b80e1968381beeb460eb Reviewed-on: http://git.am.freescale.net:8181/17731 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index d50174f..cbde8b9 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -74,10 +74,10 @@ #endif #else #ifdef __LITTLE_ENDIAN -#define wr_reg32(reg, data) __raw_writel(reg, data) +#define wr_reg32(reg, data) __raw_writel(data, reg) #define rd_reg32(reg) __raw_readl(reg) #ifdef CONFIG_64BIT -#define wr_reg64(reg, data) __raw_writeq(reg, data) +#define wr_reg64(reg, data) __raw_writeq(data, reg) #define rd_reg64(reg) __raw_readq(reg) #endif #endif -- cgit v0.10.2 From 9c3cd9d102c4d8c4e8557343c8b306542e898cb2 Mon Sep 17 00:00:00 2001 From: "Victoria Milhoan (b42089)" Date: Mon, 25 Mar 2013 11:31:20 -0700 Subject: crypto: caam - Define setbits32() and clrbits32() for ARM in the Freescale CAAM driver The kernel defines setbits32() and clrbits32() macros only for Power-based architectures. This patch modifies the Freescale CAAM driver to add macros for use on ARM architectures. Signed-off-by: Victoria Milhoan (b42089) Signed-off-by: Ruchika Gupta (cherry picked from commit 54fbc7392ac344cb94c44a2d8e1e0a16c950a5cd) Change-Id: Ia8065d9722bf5b7e60fd269d345b0b8c95ee96da Reviewed-on: http://git.am.freescale.net:8181/17732 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h index f227922..8c79717 100644 --- a/drivers/crypto/caam/compat.h +++ b/drivers/crypto/caam/compat.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index cbde8b9..ce00c6d 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -83,6 +83,12 @@ #endif #endif +#ifdef CONFIG_ARM +/* These are common macros for Power, put here for ARMs */ +#define setbits32(_addr, _v) writel((readl(_addr) | (_v)), (_addr)) +#define clrbits32(_addr, _v) writel((readl(_addr) & ~(_v)), (_addr)) +#endif + #ifndef CONFIG_64BIT static inline void wr_reg64(u64 __iomem *reg, u64 data) { -- cgit v0.10.2 From 49d7747598e26add4ce6e1b775eacce5a9b2115d Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Mon, 23 Jun 2014 15:08:28 +0530 Subject: crypto: caam - Correct definition of registers in memory map Some registers like SECVID, CHAVID, CHA Revision Number, CTPR were defined as 64 bit resgisters. The IP provides a DWT bit(Double word Transpose) to transpose the two words when a double word register is accessed. However setting this bit would also affect the operation of job descriptors as well as other registers which are truly double word in nature. So, for the IP to work correctly on big-endian as well as little-endian SoC's, change is required to access all 32 bit registers as 32 bit quantities. Signed-off-by: Ruchika Gupta Signed-off-by: Herbert Xu (cherry picked from commit eb1139cd437afadc63f58159c111e3f166bddb51) Conflicts: drivers/crypto/caam/ctrl.c Change-Id: I4b9e3c8a438e6ce485a09dd485a1c463ec38953a Reviewed-on: http://git.am.freescale.net:8181/17733 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 17805eb..9157f7b 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -375,17 +375,17 @@ static int caam_rng_init(struct device *dev) { int gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN; struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); - u64 cha_vid; + u64 cha_vid_ls; struct caam_full __iomem *topregs; int ret = 0; topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - cha_vid = rd_reg64(&topregs->ctrl.perfmon.cha_id); + cha_vid_ls = rd_reg32(&topregs->ctrl.perfmon.cha_id_ls); /* * If SEC has RNG version >= 4 and RNG state handle has not been * already instantiated, do RNG instantiation */ - if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4) { + if ((cha_vid_ls & CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT >= 4) { ctrlpriv->rng4_sh_init = rd_reg32(&topregs->ctrl.r4tst[0].rdsta); /* @@ -539,8 +539,9 @@ static int caam_probe(struct platform_device *pdev) } /* Check to see if QI present. If so, enable */ - ctrlpriv->qi_present = !!(rd_reg64(&topregs->ctrl.perfmon.comp_parms) & - CTPR_QI_MASK); + ctrlpriv->qi_present = + !!(rd_reg32(&topregs->ctrl.perfmon.comp_parms_ms) & + CTPR_MS_QI_MASK); if (ctrlpriv->qi_present) { ctrlpriv->qi = (struct caam_queue_if __force *)&topregs->qi; /* This is all that's required to physically enable QI */ @@ -568,8 +569,9 @@ static int caam_probe(struct platform_device *pdev) /* NOTE: RTIC detection ought to go here, around Si time */ - caam_id = rd_reg64(&topregs->ctrl.perfmon.caam_id); ctrlpriv->era = caam_get_era(); + caam_id = (u64)rd_reg32(&topregs->ctrl.perfmon.caam_id_ms) << 32 | + (u64)rd_reg32(&topregs->ctrl.perfmon.caam_id_ls); /* Report "alive" for developer to see */ dev_info(dev, "device ID = 0x%016llx (Era %d)\n", caam_id, diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index ce00c6d..16a3d2e 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -120,45 +120,45 @@ struct jr_outentry { */ /* Number of DECOs */ -#define CHA_NUM_DECONUM_SHIFT 56 -#define CHA_NUM_DECONUM_MASK (0xfull << CHA_NUM_DECONUM_SHIFT) +#define CHA_NUM_MS_DECONUM_SHIFT 24 +#define CHA_NUM_MS_DECONUM_MASK (0xfull << CHA_NUM_MS_DECONUM_SHIFT) /* CHA Version IDs */ -#define CHA_ID_AES_SHIFT 0 -#define CHA_ID_AES_MASK (0xfull << CHA_ID_AES_SHIFT) +#define CHA_ID_LS_AES_SHIFT 0 +#define CHA_ID_LS_AES_MASK (0xfull << CHA_ID_LS_AES_SHIFT) -#define CHA_ID_DES_SHIFT 4 -#define CHA_ID_DES_MASK (0xfull << CHA_ID_DES_SHIFT) +#define CHA_ID_LS_DES_SHIFT 4 +#define CHA_ID_LS_DES_MASK (0xfull << CHA_ID_LS_DES_SHIFT) -#define CHA_ID_ARC4_SHIFT 8 -#define CHA_ID_ARC4_MASK (0xfull << CHA_ID_ARC4_SHIFT) +#define CHA_ID_LS_ARC4_SHIFT 8 +#define CHA_ID_LS_ARC4_MASK (0xfull << CHA_ID_LS_ARC4_SHIFT) -#define CHA_ID_MD_SHIFT 12 -#define CHA_ID_MD_MASK (0xfull << CHA_ID_MD_SHIFT) +#define CHA_ID_LS_MD_SHIFT 12 +#define CHA_ID_LS_MD_MASK (0xfull << CHA_ID_LS_MD_SHIFT) -#define CHA_ID_RNG_SHIFT 16 -#define CHA_ID_RNG_MASK (0xfull << CHA_ID_RNG_SHIFT) +#define CHA_ID_LS_RNG_SHIFT 16 +#define CHA_ID_LS_RNG_MASK (0xfull << CHA_ID_LS_RNG_SHIFT) -#define CHA_ID_SNW8_SHIFT 20 -#define CHA_ID_SNW8_MASK (0xfull << CHA_ID_SNW8_SHIFT) +#define CHA_ID_LS_SNW8_SHIFT 20 +#define CHA_ID_LS_SNW8_MASK (0xfull << CHA_ID_LS_SNW8_SHIFT) -#define CHA_ID_KAS_SHIFT 24 -#define CHA_ID_KAS_MASK (0xfull << CHA_ID_KAS_SHIFT) +#define CHA_ID_LS_KAS_SHIFT 24 +#define CHA_ID_LS_KAS_MASK (0xfull << CHA_ID_LS_KAS_SHIFT) -#define CHA_ID_PK_SHIFT 28 -#define CHA_ID_PK_MASK (0xfull << CHA_ID_PK_SHIFT) +#define CHA_ID_LS_PK_SHIFT 28 +#define CHA_ID_LS_PK_MASK (0xfull << CHA_ID_LS_PK_SHIFT) -#define CHA_ID_CRC_SHIFT 32 -#define CHA_ID_CRC_MASK (0xfull << CHA_ID_CRC_SHIFT) +#define CHA_ID_MS_CRC_SHIFT 0 +#define CHA_ID_MS_CRC_MASK (0xfull << CHA_ID_MS_CRC_SHIFT) -#define CHA_ID_SNW9_SHIFT 36 -#define CHA_ID_SNW9_MASK (0xfull << CHA_ID_SNW9_SHIFT) +#define CHA_ID_MS_SNW9_SHIFT 4 +#define CHA_ID_MS_SNW9_MASK (0xfull << CHA_ID_MS_SNW9_SHIFT) -#define CHA_ID_DECO_SHIFT 56 -#define CHA_ID_DECO_MASK (0xfull << CHA_ID_DECO_SHIFT) +#define CHA_ID_MS_DECO_SHIFT 24 +#define CHA_ID_MS_DECO_MASK (0xfull << CHA_ID_MS_DECO_SHIFT) -#define CHA_ID_JR_SHIFT 60 -#define CHA_ID_JR_MASK (0xfull << CHA_ID_JR_SHIFT) +#define CHA_ID_MS_JR_SHIFT 28 +#define CHA_ID_MS_JR_MASK (0xfull << CHA_ID_MS_JR_SHIFT) struct sec_vid { u16 ip_id; @@ -178,10 +178,12 @@ struct caam_perfmon { u64 rsvd[13]; /* CAAM Hardware Instantiation Parameters fa0-fbf */ - u64 cha_rev; /* CRNR - CHA Revision Number */ -#define CTPR_QI_SHIFT 57 -#define CTPR_QI_MASK (0x1ull << CTPR_QI_SHIFT) - u64 comp_parms; /* CTPR - Compile Parameters Register */ + u32 cha_rev_ms; /* CRNR - CHA Rev No. Most significant half*/ + u32 cha_rev_ls; /* CRNR - CHA Rev No. Least significant half*/ +#define CTPR_MS_QI_SHIFT 25 +#define CTPR_MS_QI_MASK (0x1ull << CTPR_MS_QI_SHIFT) + u32 comp_parms_ms; /* CTPR - Compile Parameters Register */ + u32 comp_parms_ls; /* CTPR - Compile Parameters Register */ u64 rsvd1[2]; /* CAAM Global Status fc0-fdf */ @@ -195,9 +197,12 @@ struct caam_perfmon { /* Component Instantiation Parameters fe0-fff */ u32 rtic_id; /* RVID - RTIC Version ID */ u32 ccb_id; /* CCBVID - CCB Version ID */ - u64 cha_id; /* CHAVID - CHA Version ID */ - u64 cha_num; /* CHANUM - CHA Number */ - u64 caam_id; /* CAAMVID - CAAM Version ID */ + u32 cha_id_ms; /* CHAVID - CHA Version ID Most Significant*/ + u32 cha_id_ls; /* CHAVID - CHA Version ID Least Significant*/ + u32 cha_num_ms; /* CHANUM - CHA Number Most Significant */ + u32 cha_num_ls; /* CHANUM - CHA Number Least Significant*/ + u32 caam_id_ms; /* CAAMVID - CAAM Version ID MS */ + u32 caam_id_ls; /* CAAMVID - CAAM Version ID LS */ }; /* LIODN programming for DMA configuration */ -- cgit v0.10.2 From b76dab79a99f90457acd9093a5e4ca664bae703b Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Mon, 23 Jun 2014 17:42:33 +0530 Subject: crypto: caam - Configuration for platforms with virtualization enabled in CAAM For platforms with virtualization enabled 1. The job ring registers can be written to only is the job ring has been started i.e STARTR bit in JRSTART register is 1 2. For DECO's under direct software control, with virtualization enabled PL, BMT, ICID and SDID values need to be provided. These are provided by selecting a Job ring in start mode whose parameters would be used for the DECO access programming. Signed-off-by: Ruchika Gupta Signed-off-by: Herbert Xu (cherry picked from commit 17157c90a8abf9323ee2a3daac7ad9f696642dda) Conflicts: drivers/crypto/caam/ctrl.c drivers/crypto/caam/intern.h Change-Id: I8adb64fd4ba06f1007ae6838ad4f5b3ecd04bdc9 Reviewed-on: http://git.am.freescale.net:8181/17734 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 9157f7b..5599cf5 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -88,6 +88,14 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, /* Set the bit to request direct access to DECO0 */ topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; + + if (ctrlpriv->virt_en == 1) + setbits32(&topregs->ctrl.deco_rsr, DECORSR_JR0); + + while (!(rd_reg32(&topregs->ctrl.deco_rsr) & DECORSR_VALID) && + --timeout) + cpu_relax(); + setbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); while (!(rd_reg32(&topregs->ctrl.deco_rq) & DECORR_DEN0) && @@ -130,6 +138,9 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, *status = rd_reg32(&topregs->deco.op_status_hi) & DECO_OP_STATUS_HI_ERR_MASK; + if (ctrlpriv->virt_en == 1) + clrbits32(&topregs->ctrl.deco_rsr, DECORSR_JR0); + /* Mark the DECO as free */ clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); @@ -451,6 +462,7 @@ static int caam_probe(struct platform_device *pdev) #ifdef CONFIG_DEBUG_FS struct caam_perfmon *perfmon; #endif + u32 scfgr, comp_params; ctrlpriv = kzalloc(sizeof(struct caam_drv_private), GFP_KERNEL); if (!ctrlpriv) @@ -483,6 +495,33 @@ static int caam_probe(struct platform_device *pdev) setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); + /* + * Read the Compile Time paramters and SCFGR to determine + * if Virtualization is enabled for this platform + */ + comp_params = rd_reg32(&topregs->ctrl.perfmon.comp_parms_ms); + scfgr = rd_reg32(&topregs->ctrl.scfgr); + + ctrlpriv->virt_en = 0; + if (comp_params & CTPR_MS_VIRT_EN_INCL) { + /* VIRT_EN_INCL = 1 & VIRT_EN_POR = 1 or + * VIRT_EN_INCL = 1 & VIRT_EN_POR = 0 & SCFGR_VIRT_EN = 1 + */ + if ((comp_params & CTPR_MS_VIRT_EN_POR) || + (!(comp_params & CTPR_MS_VIRT_EN_POR) && + (scfgr & SCFGR_VIRT_EN))) + ctrlpriv->virt_en = 1; + } else { + /* VIRT_EN_INCL = 0 && VIRT_EN_POR_VALUE = 1 */ + if (comp_params & CTPR_MS_VIRT_EN_POR) + ctrlpriv->virt_en = 1; + } + + if (ctrlpriv->virt_en == 1) + setbits32(&topregs->ctrl.jrstart, JRSTART_JR0_START | + JRSTART_JR1_START | JRSTART_JR2_START | + JRSTART_JR3_START); + if (sizeof(dma_addr_t) == sizeof(u64)) if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) dma_set_mask(dev, DMA_BIT_MASK(40)); diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index ed0351d..9b2afc8 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -94,6 +94,7 @@ struct caam_drv_private { struct list_head pkc_list; int era; /* SEC Era */ + int virt_en; /* Virtualization enabled in CAAM */ #define RNG4_MAX_HANDLES 2 /* RNG4 block */ diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index 16a3d2e..ed3fe70 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -182,6 +182,8 @@ struct caam_perfmon { u32 cha_rev_ls; /* CRNR - CHA Rev No. Least significant half*/ #define CTPR_MS_QI_SHIFT 25 #define CTPR_MS_QI_MASK (0x1ull << CTPR_MS_QI_SHIFT) +#define CTPR_MS_VIRT_EN_INCL 0x00000001 +#define CTPR_MS_VIRT_EN_POR 0x00000002 u32 comp_parms_ms; /* CTPR - Compile Parameters Register */ u32 comp_parms_ls; /* CTPR - Compile Parameters Register */ u64 rsvd1[2]; @@ -315,9 +317,12 @@ struct caam_ctrl { /* Bus Access Configuration Section 010-11f */ /* Read/Writable */ struct masterid jr_mid[4]; /* JRxLIODNR - JobR LIODN setup */ - u32 rsvd3[12]; + u32 rsvd3[11]; + u32 jrstart; /* JRSTART - Job Ring Start Register */ struct masterid rtic_mid[4]; /* RTICxLIODNR - RTIC LIODN setup */ - u32 rsvd4[7]; + u32 rsvd4[5]; + u32 deco_rsr; /* DECORSR - Deco Request Source */ + u32 rsvd11; u32 deco_rq; /* DECORR - DECO Request */ struct partid deco_mid[5]; /* DECOxLIODNR - 1 per DECO */ u32 rsvd5[22]; @@ -358,7 +363,10 @@ struct caam_ctrl { #define MCFGR_DMA_RESET 0x10000000 #define MCFGR_LONG_PTR 0x00010000 /* Use >32-bit desc addressing */ #define SCFGR_RDBENABLE 0x00000400 +#define SCFGR_VIRT_EN 0x00008000 #define DECORR_RQD0ENABLE 0x00000001 /* Enable DECO0 for direct access */ +#define DECORSR_JR0 0x00000001 /* JR to supply TZ, SDID, ICID */ +#define DECORSR_VALID 0x80000000 #define DECORR_DEN0 0x00010000 /* DECO0 available for access*/ /* AXI read cache control */ @@ -376,6 +384,12 @@ struct caam_ctrl { #define MCFGR_AXIPRI 0x00000008 /* Assert AXI priority sideband */ #define MCFGR_BURST_64 0x00000001 /* Max burst size */ +/* JRSTART register offsets */ +#define JRSTART_JR0_START 0x00000001 /* Start Job ring 0 */ +#define JRSTART_JR1_START 0x00000002 /* Start Job ring 1 */ +#define JRSTART_JR2_START 0x00000004 /* Start Job ring 2 */ +#define JRSTART_JR3_START 0x00000008 /* Start Job ring 3 */ + /* * caam_job_ring - direct job ring setup * 1-4 possible per instantiation, base + 1000/2000/3000/4000 -- cgit v0.10.2 From 34d9bfa09275d2d165112633ab2cadedae1eddce Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Mon, 23 Jun 2014 18:49:30 +0530 Subject: crypto: caam - Add definition of rd/wr_reg64 for little endian platform CAAM IP has certain 64 bit registers . 32 bit architectures cannot force atomic-64 operations. This patch adds definition of these atomic-64 operations for little endian platforms. The definitions which existed previously were for big endian platforms. Signed-off-by: Ruchika Gupta Signed-off-by: Herbert Xu (cherry picked from commit ef94b1d834aace7101de77c3a7c2631b9ae9c5f6) Change-Id: Ieb2e1cccb475f380f44735b6b6d633514e9ab3e3 Reviewed-on: http://git.am.freescale.net:8181/17735 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index ed3fe70..2982d85 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -90,6 +90,7 @@ #endif #ifndef CONFIG_64BIT +#ifdef __BIG_ENDIAN static inline void wr_reg64(u64 __iomem *reg, u64 data) { wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32); @@ -101,6 +102,21 @@ static inline u64 rd_reg64(u64 __iomem *reg) return (((u64)rd_reg32((u32 __iomem *)reg)) << 32) | ((u64)rd_reg32((u32 __iomem *)reg + 1)); } +#else +#ifdef __LITTLE_ENDIAN +static inline void wr_reg64(u64 __iomem *reg, u64 data) +{ + wr_reg32((u32 __iomem *)reg + 1, (data & 0xffffffff00000000ull) >> 32); + wr_reg32((u32 __iomem *)reg, data & 0x00000000ffffffffull); +} + +static inline u64 rd_reg64(u64 __iomem *reg) +{ + return (((u64)rd_reg32((u32 __iomem *)reg + 1)) << 32) | + ((u64)rd_reg32((u32 __iomem *)reg)); +} +#endif +#endif #endif /* -- cgit v0.10.2 From 401354d54ace84f76675f93f7539eb3c3f8708ff Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Mon, 23 Jun 2014 19:50:26 +0530 Subject: crypto: caam - Correct the dma mapping for sg table At few places in caamhash and caamalg, after allocating a dmable buffer for sg table , the buffer was being modified. As per definition of DMA_FROM_DEVICE ,afer allocation the memory should be treated as read-only by the driver. This patch shifts the allocation of dmable buffer for sg table after it is populated by the driver, making it read-only as per the DMA API's requirement. Signed-off-by: Ruchika Gupta Signed-off-by: Herbert Xu (cherry picked from commit 1da2be33ad4c30a2b1d5fe3053b5b7f63e6e2baa) Change-Id: I485040b955e27772c20623f037e8a5167404c18d Reviewed-on: http://git.am.freescale.net:8181/17736 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 2a4eb70..7e071b7 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -2243,8 +2243,6 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) + desc_bytes; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); *all_contig_ptr = all_contig; sec4_sg_index = 0; @@ -2279,6 +2277,8 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, sg_to_sec4_sg_last(req->dst, dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); return edesc; } @@ -2444,8 +2444,6 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) + desc_bytes; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); *contig_ptr = contig; sec4_sg_index = 0; @@ -2469,6 +2467,8 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request sg_to_sec4_sg_last(req->dst, dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); return edesc; } diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 5592fec..52787e1 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -818,9 +818,6 @@ static int ahash_update_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, - DMA_TO_DEVICE); ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, DMA_BIDIRECTIONAL); @@ -849,6 +846,10 @@ static int ahash_update_ctx(struct ahash_request *req) init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, + DMA_TO_DEVICE); + append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + to_hash, LDST_SGF); @@ -921,8 +922,6 @@ static int ahash_final_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); edesc->src_nents = 0; ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, @@ -933,6 +932,9 @@ static int ahash_final_ctx(struct ahash_request *req) last_buflen); (edesc->sec4_sg + sec4_sg_bytes - 1)->len |= SEC4_SG_LEN_FIN; + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen, LDST_SGF); @@ -999,8 +1001,6 @@ static int ahash_finup_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, DMA_TO_DEVICE); @@ -1012,6 +1012,9 @@ static int ahash_finup_ctx(struct ahash_request *req) src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg + sec4_sg_src_index, chained); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen + req->nbytes, LDST_SGF); @@ -1066,8 +1069,6 @@ static int ahash_digest(struct ahash_request *req) } edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); edesc->src_nents = src_nents; edesc->chained = chained; @@ -1077,6 +1078,8 @@ static int ahash_digest(struct ahash_request *req) if (src_nents) { sg_to_sec4_sg_last(req->src, src_nents, edesc->sec4_sg, 0); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); src_dma = edesc->sec4_sg_dma; options = LDST_SGF; } else { @@ -1207,9 +1210,6 @@ static int ahash_update_no_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, - DMA_TO_DEVICE); state->buf_dma = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, buf, *buflen); @@ -1226,6 +1226,10 @@ static int ahash_update_no_ctx(struct ahash_request *req) init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, + DMA_TO_DEVICE); + append_seq_in_ptr(desc, edesc->sec4_sg_dma, to_hash, LDST_SGF); map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); @@ -1307,8 +1311,6 @@ static int ahash_finup_no_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, buf, state->buf_dma, buflen, @@ -1317,6 +1319,9 @@ static int ahash_finup_no_ctx(struct ahash_request *req) src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg + 1, chained); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + append_seq_in_ptr(desc, edesc->sec4_sg_dma, buflen + req->nbytes, LDST_SGF); @@ -1390,13 +1395,14 @@ static int ahash_update_first(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, - DMA_TO_DEVICE); if (src_nents) { sg_to_sec4_sg_last(req->src, src_nents, edesc->sec4_sg, 0); + edesc->sec4_sg_dma = dma_map_single(jrdev, + edesc->sec4_sg, + sec4_sg_bytes, + DMA_TO_DEVICE); src_dma = edesc->sec4_sg_dma; options = LDST_SGF; } else { -- cgit v0.10.2 From 12dcee0e3cc22d4e0c6b380963dc9527cd558e7e Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Mon, 19 May 2014 09:51:33 -0700 Subject: crypto: testmgr - Fix DMA-API warning With DMA-API debug enabled testmgr triggers a "DMA-API: device driver maps memory from stack" warning, when tested on a crypto HW accelerator. Signed-off-by: Tadeusz Struk Signed-off-by: Herbert Xu (cherry picked from commit 9bac019dad8098a77cce555d929f678e22111783) Change-Id: I709ed7e293287b840abb25de21cbe4b1732e8402 Reviewed-on: http://git.am.freescale.net:8181/17737 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 117b312..71e61ac 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -422,16 +422,18 @@ static int __test_aead(struct crypto_aead *tfm, int enc, void *input; void *output; void *assoc; - char iv[MAX_IVLEN]; + char *iv; char *xbuf[XBUFSIZE]; char *xoutbuf[XBUFSIZE]; char *axbuf[XBUFSIZE]; + iv = kzalloc(MAX_IVLEN, GFP_KERNEL); + if (!iv) + return ret; if (testmgr_alloc_buf(xbuf)) goto out_noxbuf; if (testmgr_alloc_buf(axbuf)) goto out_noaxbuf; - if (diff_dst && testmgr_alloc_buf(xoutbuf)) goto out_nooutbuf; @@ -775,6 +777,7 @@ out_nooutbuf: out_noaxbuf: testmgr_free_buf(xbuf); out_noxbuf: + kfree(iv); return ret; } -- cgit v0.10.2 From 16e3c3f7d8f617cdb141f2b893efc6974e9bb00c Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Wed, 23 Jul 2014 11:59:38 +0300 Subject: crypto: testmgr - avoid DMA mapping from text, rodata, stack With DMA_API_DEBUG set, following warnings are emitted (tested on CAAM accelerator): DMA-API: device driver maps memory from kernel text or rodata DMA-API: device driver maps memory from stack and the culprits are: -key in __test_aead and __test_hash -result in __test_hash MAX_KEYLEN is changed to accommodate maximum key length from existing test vectors in crypto/testmgr.h (131 bytes) and rounded. Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit 29b77e5dd88e1b920e3e65681f0e7b961ebbdeb8) Change-Id: I14903ff5032e09754c0138dd2d3292cc3f83c28e Reviewed-on: http://git.am.freescale.net:8181/17738 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 71e61ac..2f5902f 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -199,13 +199,20 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, const char *algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm)); unsigned int i, j, k, temp; struct scatterlist sg[8]; - char result[64]; + char *result; + char *key; struct ahash_request *req; struct tcrypt_result tresult; void *hash_buff; char *xbuf[XBUFSIZE]; int ret = -ENOMEM; + result = kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL); + if (!result) + return ret; + key = kmalloc(MAX_KEYLEN, GFP_KERNEL); + if (!key) + goto out_nobuf; if (testmgr_alloc_buf(xbuf)) goto out_nobuf; @@ -230,7 +237,7 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, goto out; j++; - memset(result, 0, 64); + memset(result, 0, MAX_DIGEST_SIZE); hash_buff = xbuf[0]; hash_buff += align_offset; @@ -240,8 +247,14 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, if (template[i].ksize) { crypto_ahash_clear_flags(tfm, ~0); - ret = crypto_ahash_setkey(tfm, template[i].key, - template[i].ksize); + if (template[i].ksize > MAX_KEYLEN) { + pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n", + j, algo, template[i].ksize, MAX_KEYLEN); + ret = -EINVAL; + goto out; + } + memcpy(key, template[i].key, template[i].ksize); + ret = crypto_ahash_setkey(tfm, key, template[i].ksize); if (ret) { printk(KERN_ERR "alg: hash: setkey failed on " "test %d for %s: ret=%d\n", j, algo, @@ -301,7 +314,7 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, if (template[i].np) { j++; - memset(result, 0, 64); + memset(result, 0, MAX_DIGEST_SIZE); temp = 0; sg_init_table(sg, template[i].np); @@ -320,8 +333,16 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, } if (template[i].ksize) { + if (template[i].ksize > MAX_KEYLEN) { + pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n", + j, algo, template[i].ksize, + MAX_KEYLEN); + ret = -EINVAL; + goto out; + } crypto_ahash_clear_flags(tfm, ~0); - ret = crypto_ahash_setkey(tfm, template[i].key, + memcpy(key, template[i].key, template[i].ksize); + ret = crypto_ahash_setkey(tfm, key, template[i].ksize); if (ret) { @@ -373,6 +394,8 @@ out: out_noreq: testmgr_free_buf(xbuf); out_nobuf: + kfree(key); + kfree(result); return ret; } @@ -430,6 +453,9 @@ static int __test_aead(struct crypto_aead *tfm, int enc, iv = kzalloc(MAX_IVLEN, GFP_KERNEL); if (!iv) return ret; + key = kmalloc(MAX_KEYLEN, GFP_KERNEL); + if (!key) + goto out_noxbuf; if (testmgr_alloc_buf(xbuf)) goto out_noxbuf; if (testmgr_alloc_buf(axbuf)) @@ -494,7 +520,14 @@ static int __test_aead(struct crypto_aead *tfm, int enc, crypto_aead_set_flags( tfm, CRYPTO_TFM_REQ_WEAK_KEY); - key = template[i].key; + if (template[i].klen > MAX_KEYLEN) { + pr_err("alg: aead%s: setkey failed on test %d for %s: key size %d > %d\n", + d, j, algo, template[i].klen, + MAX_KEYLEN); + ret = -EINVAL; + goto out; + } + memcpy(key, template[i].key, template[i].klen); ret = crypto_aead_setkey(tfm, key, template[i].klen); @@ -595,7 +628,14 @@ static int __test_aead(struct crypto_aead *tfm, int enc, if (template[i].wk) crypto_aead_set_flags( tfm, CRYPTO_TFM_REQ_WEAK_KEY); - key = template[i].key; + if (template[i].klen > MAX_KEYLEN) { + pr_err("alg: aead%s: setkey failed on test %d for %s: key size %d > %d\n", + d, j, algo, template[i].klen, + MAX_KEYLEN); + ret = -EINVAL; + goto out; + } + memcpy(key, template[i].key, template[i].klen); ret = crypto_aead_setkey(tfm, key, template[i].klen); if (!ret == template[i].fail) { @@ -777,6 +817,7 @@ out_nooutbuf: out_noaxbuf: testmgr_free_buf(xbuf); out_noxbuf: + kfree(key); kfree(iv); return ret; } diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 7bdadcc..4bbf13a 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -32,7 +32,7 @@ #define MAX_DIGEST_SIZE 64 #define MAX_TAP 8 -#define MAX_KEYLEN 56 +#define MAX_KEYLEN 160 #define MAX_IVLEN 32 struct hash_testvec { -- cgit v0.10.2 From f467cb884daaa86f1a18f069f82e3bac7d6b6797 Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Mon, 21 Jul 2014 16:03:21 +0300 Subject: crypto: caam - fix DECO RSR polling RSR (Request Source Register) is not used when virtualization is disabled, thus don't poll for Valid bit. Besides this, if used, timeout has to be reinitialized. Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit 8f1da7b945b65513fb02b75ce25040c67ce32726) Change-Id: I970dc02469553034efe2bcf7431d104b911a6c76 Reviewed-on: http://git.am.freescale.net:8181/17739 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 5599cf5..570fdaa 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -89,12 +89,15 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, /* Set the bit to request direct access to DECO0 */ topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - if (ctrlpriv->virt_en == 1) + if (ctrlpriv->virt_en == 1) { setbits32(&topregs->ctrl.deco_rsr, DECORSR_JR0); - while (!(rd_reg32(&topregs->ctrl.deco_rsr) & DECORSR_VALID) && - --timeout) - cpu_relax(); + while (!(rd_reg32(&topregs->ctrl.deco_rsr) & DECORSR_VALID) && + --timeout) + cpu_relax(); + + timeout = 100000; + } setbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); -- cgit v0.10.2 From 5d440b0f7e98b51050d282bbef3c8ddd75e27120 Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:48 +0300 Subject: crypto: caam - fix typo in dma_mapping_error dma_mapping_error checks for an incorrect DMA address: s/ctx->sh_desc_enc_dma/ctx->sh_desc_dec_dma Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit 71c65f7c90a176877ad1aa87b752217db61148a8) Change-Id: If55f0e154763c9a293adc6fbc44e9eb01e5fcbc5 Reviewed-on: http://git.am.freescale.net:8181/17740 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 7e071b7..94ba267 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -1474,7 +1474,7 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { + if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { dev_err(jrdev, "unable to map shared descriptor\n"); return -ENOMEM; } -- cgit v0.10.2 From da7beb6f56d8dc293f47ca06e79d774649e70817 Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:49 +0300 Subject: crypto: caam - fix "failed to check map error" DMA warnings Use dma_mapping_error for every dma_map_single / dma_map_page. Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit ce572085282128d57324aabf415673dfbfa32d54) Conflicts: drivers/crypto/caam/caamalg.c Change-Id: I1e2466043f87dc74c955ebfae0aad45be7ac8de9 Reviewed-on: http://git.am.freescale.net:8181/17741 Tested-by: Review Code-CDREVIEW Reviewed-by: Horia Ioan Geanta Neag Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 94ba267..c9ca243 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -2206,6 +2206,10 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, * All other - expected input sequence: AAD, IV, text */ iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, iv_dma)) { + dev_err(jrdev, "unable to map IV\n"); + return ERR_PTR(-ENOMEM); + } if (is_gcm) all_contig = (!assoc_nents && iv_dma + ivsize == sg_dma_address(req->assoc) && @@ -2279,6 +2283,10 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, } edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return ERR_PTR(-ENOMEM); + } return edesc; } @@ -2404,8 +2412,13 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request DMA_FROM_DEVICE, dst_chained); } - /* Check if data are contiguous */ iv_dma = dma_map_single(jrdev, greq->giv, ivsize, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, iv_dma)) { + dev_err(jrdev, "unable to map IV\n"); + return ERR_PTR(-ENOMEM); + } + + /* Check if data are contiguous */ if (assoc_nents || sg_dma_address(req->assoc) + req->assoclen != iv_dma || src_nents || iv_dma + ivsize != sg_dma_address(req->src)) contig &= ~GIV_SRC_CONTIG; @@ -2469,6 +2482,10 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request } edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return ERR_PTR(-ENOMEM); + } return edesc; } @@ -2658,11 +2675,16 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request DMA_FROM_DEVICE, dst_chained); } + iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, iv_dma)) { + dev_err(jrdev, "unable to map IV\n"); + return ERR_PTR(-ENOMEM); + } + /* * Check if iv can be contiguous with source and destination. * If so, include it. If not, create scatterlist. */ - iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE); if (!src_nents && iv_dma + ivsize == sg_dma_address(req->src)) iv_contig = true; else @@ -2701,6 +2723,11 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return ERR_PTR(-ENOMEM); + } + edesc->iv_dma = iv_dma; #ifdef DEBUG diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 52787e1..29385a9 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -137,13 +137,20 @@ struct caam_hash_state { /* Common job descriptor seq in/out ptr routines */ /* Map state->caam_ctx, and append seq_out_ptr command that points to it */ -static inline void map_seq_out_ptr_ctx(u32 *desc, struct device *jrdev, - struct caam_hash_state *state, - int ctx_len) +static inline int map_seq_out_ptr_ctx(u32 *desc, struct device *jrdev, + struct caam_hash_state *state, + int ctx_len) { state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, DMA_FROM_DEVICE); + if (dma_mapping_error(jrdev, state->ctx_dma)) { + dev_err(jrdev, "unable to map ctx\n"); + return -ENOMEM; + } + append_seq_out_ptr(desc, state->ctx_dma, ctx_len, 0); + + return 0; } /* Map req->result, and append seq_out_ptr command that points to it */ @@ -201,14 +208,19 @@ try_buf_map_to_sec4_sg(struct device *jrdev, struct sec4_sg_entry *sec4_sg, } /* Map state->caam_ctx, and add it to link table */ -static inline void ctx_map_to_sec4_sg(u32 *desc, struct device *jrdev, - struct caam_hash_state *state, - int ctx_len, - struct sec4_sg_entry *sec4_sg, - u32 flag) +static inline int ctx_map_to_sec4_sg(u32 *desc, struct device *jrdev, + struct caam_hash_state *state, int ctx_len, + struct sec4_sg_entry *sec4_sg, u32 flag) { state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, flag); + if (dma_mapping_error(jrdev, state->ctx_dma)) { + dev_err(jrdev, "unable to map ctx\n"); + return -ENOMEM; + } + dma_to_sec4_sg_one(sec4_sg, state->ctx_dma, ctx_len, 0); + + return 0; } /* Common shared descriptor commands */ @@ -819,8 +831,10 @@ static int ahash_update_ctx(struct ahash_request *req) edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, - edesc->sec4_sg, DMA_BIDIRECTIONAL); + ret = ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, + edesc->sec4_sg, DMA_BIDIRECTIONAL); + if (ret) + return ret; state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, @@ -849,6 +863,10 @@ static int ahash_update_ctx(struct ahash_request *req) edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + to_hash, LDST_SGF); @@ -924,8 +942,10 @@ static int ahash_final_ctx(struct ahash_request *req) DESC_JOB_IO_LEN; edesc->src_nents = 0; - ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, - DMA_TO_DEVICE); + ret = ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, + edesc->sec4_sg, DMA_TO_DEVICE); + if (ret) + return ret; state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, buf, state->buf_dma, buflen, @@ -934,12 +954,20 @@ static int ahash_final_ctx(struct ahash_request *req) edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen, LDST_SGF); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1002,8 +1030,10 @@ static int ahash_finup_ctx(struct ahash_request *req) edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, - DMA_TO_DEVICE); + ret = ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, + edesc->sec4_sg, DMA_TO_DEVICE); + if (ret) + return ret; state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, buf, state->buf_dma, buflen, @@ -1014,12 +1044,20 @@ static int ahash_finup_ctx(struct ahash_request *req) edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen + req->nbytes, LDST_SGF); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1080,6 +1118,10 @@ static int ahash_digest(struct ahash_request *req) sg_to_sec4_sg_last(req->src, src_nents, edesc->sec4_sg, 0); edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } src_dma = edesc->sec4_sg_dma; options = LDST_SGF; } else { @@ -1090,6 +1132,10 @@ static int ahash_digest(struct ahash_request *req) edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1138,11 +1184,19 @@ static int ahash_final_no_ctx(struct ahash_request *req) init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); state->buf_dma = dma_map_single(jrdev, buf, buflen, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, state->buf_dma)) { + dev_err(jrdev, "unable to map src\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, state->buf_dma, buflen, 0); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } edesc->src_nents = 0; #ifdef DEBUG @@ -1229,10 +1283,16 @@ static int ahash_update_no_ctx(struct ahash_request *req) edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, edesc->sec4_sg_dma, to_hash, LDST_SGF); - map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + ret = map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + if (ret) + return ret; #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1321,12 +1381,20 @@ static int ahash_finup_no_ctx(struct ahash_request *req) edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, edesc->sec4_sg_dma, buflen + req->nbytes, LDST_SGF); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1403,6 +1471,10 @@ static int ahash_update_first(struct ahash_request *req) edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } src_dma = edesc->sec4_sg_dma; options = LDST_SGF; } else { @@ -1420,7 +1492,9 @@ static int ahash_update_first(struct ahash_request *req) append_seq_in_ptr(desc, src_dma, to_hash, options); - map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + ret = map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + if (ret) + return ret; #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index 466dfe5..fcafd65 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -188,7 +188,7 @@ static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) max - copied_idx, false); } -static inline void rng_create_sh_desc(struct caam_rng_ctx *ctx) +static inline int rng_create_sh_desc(struct caam_rng_ctx *ctx) { struct device *jrdev = ctx->jrdev; u32 *desc = ctx->sh_desc; @@ -206,13 +206,18 @@ static inline void rng_create_sh_desc(struct caam_rng_ctx *ctx) ctx->sh_desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "rng shdesc@: ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + return 0; } -static inline void rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) +static inline int rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) { struct device *jrdev = ctx->jrdev; struct buf_data *bd = &ctx->bufs[buf_id]; @@ -223,12 +228,17 @@ static inline void rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) HDR_REVERSE); bd->addr = dma_map_single(jrdev, bd->buf, RN_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(jrdev, bd->addr)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } append_seq_out_ptr_intlen(desc, bd->addr, RN_BUF_SIZE, 0); #ifdef DEBUG print_hex_dump(KERN_ERR, "rng job desc@: ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + return 0; } static void caam_cleanup(struct hwrng *rng) @@ -245,24 +255,44 @@ static void caam_cleanup(struct hwrng *rng) rng_unmap_ctx(rng_ctx); } -static void caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) +static int caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) { struct buf_data *bd = &ctx->bufs[buf_id]; + int err; + + err = rng_create_job_desc(ctx, buf_id); + if (err) + return err; - rng_create_job_desc(ctx, buf_id); atomic_set(&bd->empty, BUF_EMPTY); submit_job(ctx, buf_id == ctx->current_buf); wait_for_completion(&bd->filled); + + return 0; } -static void caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev) +static int caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev) { + int err; + ctx->jrdev = jrdev; - rng_create_sh_desc(ctx); + + err = rng_create_sh_desc(ctx); + if (err) + return err; + ctx->current_buf = 0; ctx->cur_buf_idx = 0; - caam_init_buf(ctx, 0); - caam_init_buf(ctx, 1); + + err = caam_init_buf(ctx, 0); + if (err) + return err; + + err = caam_init_buf(ctx, 1); + if (err) + return err; + + return 0; } static struct hwrng caam_rng = { @@ -285,6 +315,7 @@ static int __init caam_rng_init(void) struct platform_device *pdev; struct device *ctrldev; void *priv; + int err; dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); if (!dev_node) { @@ -316,7 +347,9 @@ static int __init caam_rng_init(void) rng_ctx = kmalloc(sizeof(struct caam_rng_ctx), GFP_DMA); if (!rng_ctx) return -ENOMEM; - caam_init_rng(rng_ctx, dev); + err = caam_init_rng(rng_ctx, dev); + if (err) + return err; dev_info(dev, "registering rng-caam\n"); return hwrng_register(&caam_rng); -- cgit v0.10.2 From 20db973f05a8c184cb2074814c27b36e330ba291 Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:50 +0300 Subject: crypto: caam - fix DMA unmapping error in hash_digest_key Key being hashed is unmapped using the digest size instead of initial length: caam_jr ffe301000.jr: DMA-API: device driver frees DMA memory with different size [device address=0x000000002eeedac0] [map size=80 bytes] [unmap size=20 bytes] ------------[ cut here ]------------ WARNING: at lib/dma-debug.c:1090 Modules linked in: caamhash(+) CPU: 0 PID: 1327 Comm: cryptomgr_test Not tainted 3.16.0-rc1 #23 task: eebda5d0 ti: ee26a000 task.ti: ee26a000 NIP: c0288790 LR: c0288790 CTR: c02d7020 REGS: ee26ba30 TRAP: 0700 Not tainted (3.16.0-rc1) MSR: 00021002 CR: 44022082 XER: 00000000 GPR00: c0288790 ee26bae0 eebda5d0 0000009f c1de3478 c1de382c 00000000 00021002 GPR08: 00000007 00000000 01660000 0000012f 82022082 00000000 c07a1900 eeda29c0 GPR16: 00000000 c61deea0 000c49a0 00000260 c07e1e10 c0da1180 00029002 c0d9ef08 GPR24: c07a0000 c07a4acc ee26bb38 ee2765c0 00000014 ee130210 00000000 00000014 NIP [c0288790] check_unmap+0x640/0xab0 LR [c0288790] check_unmap+0x640/0xab0 Call Trace: [ee26bae0] [c0288790] check_unmap+0x640/0xab0 (unreliable) [ee26bb30] [c0288c78] debug_dma_unmap_page+0x78/0x90 [ee26bbb0] [f929c3d4] ahash_setkey+0x374/0x720 [caamhash] [ee26bc30] [c022fec8] __test_hash+0x228/0x6c0 [ee26bde0] [c0230388] test_hash+0x28/0xb0 [ee26be00] [c0230458] alg_test_hash+0x48/0xc0 [ee26be20] [c022fa94] alg_test+0x114/0x2e0 [ee26bea0] [c022cd1c] cryptomgr_test+0x4c/0x60 [ee26beb0] [c00497a4] kthread+0xc4/0xe0 [ee26bf40] [c000f2fc] ret_from_kernel_thread+0x5c/0x64 Instruction dump: 41de03e8 83da0020 3c60c06d 83fa0024 3863f520 813b0020 815b0024 80fa0018 811a001c 93c10008 93e1000c 4830cf6d <0fe00000> 3c60c06d 3863f0f4 4830cf5d ---[ end trace db1fae088c75c26c ]--- Mapped at: [] ahash_setkey+0xfc/0x720 [caamhash] [] __test_hash+0x228/0x6c0 [] test_hash+0x28/0xb0 [] alg_test_hash+0x48/0xc0 [] alg_test+0x114/0x2e0 Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit e11aa9f1351f150ee9f0166bffc5e007c81c1364) Change-Id: I570eaf170295c91e2ce7f7367ed31c9fd5c9369d Reviewed-on: http://git.am.freescale.net:8181/17742 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 29385a9..824af94 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -499,11 +499,11 @@ static int hash_digest_key(struct caam_hash_ctx *ctx, const u8 *key_in, digestsize, 1); #endif } - *keylen = digestsize; - dma_unmap_single(jrdev, src_dma, *keylen, DMA_TO_DEVICE); dma_unmap_single(jrdev, dst_dma, digestsize, DMA_FROM_DEVICE); + *keylen = digestsize; + kfree(desc); return ret; -- cgit v0.10.2 From 58fd6c9472580e52f990139db67391494cc5a284 Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:51 +0300 Subject: crypto: caam - fix DMA direction mismatch in ahash_done_ctx_dst caam_jr ffe301000.jr: DMA-API: device driver frees DMA memory with different direction [device address=0x00000000062ad1ac] [size=28 bytes] [mapped with DMA_FROM_DEVICE] [unmapped with DMA_TO_DEVICE] ------------[ cut here ]------------ WARNING: at lib/dma-debug.c:1131 Modules linked in: caamhash(+) [last unloaded: caamhash] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.16.0-rc1 #23 task: c0789380 ti: effd2000 task.ti: c07d6000 NIP: c02885cc LR: c02885cc CTR: c02d7020 REGS: effd3d50 TRAP: 0700 Tainted: G W (3.16.0-rc1) MSR: 00021002 CR: 44048082 XER: 00000000 GPR00: c02885cc effd3e00 c0789380 000000c6 c1de3478 c1de382c 00000000 00021002 GPR08: 00000007 00000000 01660000 0000012f 84048082 00000000 00000018 c07db080 GPR16: 00000006 00000100 0000002c eee567e0 c07e1e10 c0da1180 00029002 c0d96708 GPR24: c07a0000 c07a4acc effd3e58 ee29b140 0000001c ee130210 00000000 c0d96700 NIP [c02885cc] check_unmap+0x47c/0xab0 LR [c02885cc] check_unmap+0x47c/0xab0 Call Trace: [effd3e00] [c02885cc] check_unmap+0x47c/0xab0 (unreliable) [effd3e50] [c0288c78] debug_dma_unmap_page+0x78/0x90 [effd3ed0] [f9350974] ahash_done_ctx_dst+0xa4/0x200 [caamhash] [effd3f00] [c0429640] caam_jr_dequeue+0x1c0/0x280 [effd3f50] [c002c94c] tasklet_action+0xcc/0x1a0 [effd3f80] [c002cb30] __do_softirq+0x110/0x220 [effd3fe0] [c002cf34] irq_exit+0xa4/0xe0 [effd3ff0] [c000d834] call_do_irq+0x24/0x3c [c07d7d50] [c000489c] do_IRQ+0x8c/0x110 [c07d7d70] [c000f86c] ret_from_except+0x0/0x18 --- Exception: 501 at _raw_spin_unlock_irq+0x30/0x50 LR = _raw_spin_unlock_irq+0x2c/0x50 [c07d7e40] [c0053084] finish_task_switch+0x74/0x130 [c07d7e60] [c058f278] __schedule+0x238/0x620 [c07d7f70] [c058fb50] schedule_preempt_disabled+0x10/0x20 [c07d7f80] [c00686a0] cpu_startup_entry+0x100/0x1b0 [c07d7fb0] [c074793c] start_kernel+0x338/0x34c [c07d7ff0] [c00003d8] set_ivor+0x140/0x17c Instruction dump: 7d495214 7d294214 806a0010 80c90010 811a001c 813a0020 815a0024 90610008 3c60c06d 90c1000c 3863f764 4830d131 <0fe00000> 3c60c06d 3863f0f4 4830d121 ---[ end trace db1fae088c75c270 ]--- Mapped at: [] ahash_update_first+0x5b4/0xba0 [caamhash] [] __test_hash+0x288/0x6c0 [] test_hash+0x28/0xb0 [] alg_test_hash+0x94/0xc0 [] alg_test+0x114/0x2e0 Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit ef62b2310b4c783a70eba78f29695687d8cdc8df) Change-Id: I9059e608d14ac8c67897ed7b8c04851550de6f37 Reviewed-on: http://git.am.freescale.net:8181/17743 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 824af94..5df55c9 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -763,7 +763,7 @@ static void ahash_done_ctx_dst(struct device *jrdev, u32 *desc, u32 err, dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); } - ahash_unmap_ctx(jrdev, edesc, req, ctx->ctx_len, DMA_TO_DEVICE); + ahash_unmap_ctx(jrdev, edesc, req, ctx->ctx_len, DMA_FROM_DEVICE); kfree(edesc); #ifdef DEBUG -- cgit v0.10.2 From ed21aeafa0adfd2f589dbebef515b8f9ac7ce54e Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:52 +0300 Subject: crypto: caam - fix DMA direction mismatch in ahash_done_ctx_src caam_jr ffe301000.jr: DMA-API: device driver frees DMA memory with different direction [device address=0x0000000006271dac] [size=28 bytes] [mapped with DMA_TO_DEVICE] [unmapped with DMA_FROM_DEVICE] ------------[ cut here ]------------ WARNING: at lib/dma-debug.c:1131 Modules linked in: caamhash(+) [last unloaded: caamhash] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.16.0-rc1 #23 task: c0789380 ti: effd2000 task.ti: c07d6000 NIP: c02885cc LR: c02885cc CTR: c02d7020 REGS: effd3d50 TRAP: 0700 Tainted: G W (3.16.0-rc1) MSR: 00021002 CR: 44048082 XER: 00000000 GPR00: c02885cc effd3e00 c0789380 000000c6 c1de3478 c1de382c 00000000 00021002 GPR08: 00000007 00000000 01660000 0000012f 84048082 00000000 00000018 c07db080 GPR16: 00000006 00000100 0000002c c62517a0 c07e1e10 c0da1180 00029002 c0d95f88 GPR24: c07a0000 c07a4acc effd3e58 ee322bc0 0000001c ee130210 00000000 c0d95f80 NIP [c02885cc] check_unmap+0x47c/0xab0 LR [c02885cc] check_unmap+0x47c/0xab0 Call Trace: [effd3e00] [c02885cc] check_unmap+0x47c/0xab0 (unreliable) [effd3e50] [c0288c78] debug_dma_unmap_page+0x78/0x90 [effd3ed0] [f9624d84] ahash_done_ctx_src+0xa4/0x200 [caamhash] [effd3f00] [c0429640] caam_jr_dequeue+0x1c0/0x280 [effd3f50] [c002c94c] tasklet_action+0xcc/0x1a0 [effd3f80] [c002cb30] __do_softirq+0x110/0x220 [effd3fe0] [c002cf34] irq_exit+0xa4/0xe0 [effd3ff0] [c000d834] call_do_irq+0x24/0x3c [c07d7d50] [c000489c] do_IRQ+0x8c/0x110 [c07d7d70] [c000f86c] ret_from_except+0x0/0x18 --- Exception: 501 at _raw_spin_unlock_irq+0x30/0x50 LR = _raw_spin_unlock_irq+0x2c/0x50 [c07d7e40] [c0053084] finish_task_switch+0x74/0x130 [c07d7e60] [c058f278] __schedule+0x238/0x620 [c07d7f70] [c058fb50] schedule_preempt_disabled+0x10/0x20 [c07d7f80] [c00686a0] cpu_startup_entry+0x100/0x1b0 [c07d7fb0] [c074793c] start_kernel+0x338/0x34c [c07d7ff0] [c00003d8] set_ivor+0x140/0x17c Instruction dump: 7d495214 7d294214 806a0010 80c90010 811a001c 813a0020 815a0024 90610008 3c60c06d 90c1000c 3863f764 4830d131 <0fe00000> 3c60c06d 3863f0f4 4830d121 ---[ end trace db1fae088c75c280 ]--- Mapped at: [] ahash_final_ctx+0x14c/0x7b0 [caamhash] [] __test_hash+0x2ac/0x6c0 [] test_hash+0x28/0xb0 [] alg_test_hash+0x94/0xc0 [] alg_test+0x114/0x2e0 Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit bc9e05f9e770b9c6a1bcc0cdee676e74e5a04fd2) Change-Id: I67764d2c3f2c4f7f6d155717a16d474d43a0c410 Reviewed-on: http://git.am.freescale.net:8181/17744 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 5df55c9..145b5ce 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -725,7 +725,7 @@ static void ahash_done_ctx_src(struct device *jrdev, u32 *desc, u32 err, dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); } - ahash_unmap_ctx(jrdev, edesc, req, digestsize, DMA_FROM_DEVICE); + ahash_unmap_ctx(jrdev, edesc, req, digestsize, DMA_TO_DEVICE); kfree(edesc); #ifdef DEBUG -- cgit v0.10.2 From 2a00f4a99d24c899c7121ee2c6fc255a1a1920ce Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:53 +0300 Subject: crypto: caam - fix uninitialized S/G table size in ahash_digest Not initializing edesc->sec4_sg_bytes correctly causes ahash_done callback to free unallocated DMA memory: caam_jr ffe301000.jr: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x300900000000b44d] [size=46158 bytes] WARNING: at lib/dma-debug.c:1080 Modules linked in: caamhash(+) [last unloaded: caamhash] CPU: 0 PID: 1358 Comm: cryptomgr_test Tainted: G W 3.16.0-rc1 #23 task: eed04250 ti: effd2000 task.ti: c6046000 NIP: c02889fc LR: c02889fc CTR: c02d7020 REGS: effd3d50 TRAP: 0700 Tainted: G W (3.16.0-rc1) MSR: 00029002 CR: 44048082 XER: 00000000 GPR00: c02889fc effd3e00 eed04250 00000091 c1de3478 c1de382c 00000000 00029002 GPR08: 00000007 00000000 01660000 00000000 22048082 00000000 00000018 c07db080 GPR16: 00000006 00000100 0000002c ee2497e0 c07e1e10 c0da1180 00029002 c0d912c8 GPR24: 00000014 ee2497c0 effd3e58 00000000 c078ad4c ee130210 30090000 0000b44d NIP [c02889fc] check_unmap+0x8ac/0xab0 LR [c02889fc] check_unmap+0x8ac/0xab0 Call Trace: [effd3e00] [c02889fc] check_unmap+0x8ac/0xab0 (unreliable) [effd3e50] [c0288c78] debug_dma_unmap_page+0x78/0x90 [effd3ed0] [f9404fec] ahash_done+0x11c/0x190 [caamhash] [effd3f00] [c0429640] caam_jr_dequeue+0x1c0/0x280 [effd3f50] [c002c94c] tasklet_action+0xcc/0x1a0 [effd3f80] [c002cb30] __do_softirq+0x110/0x220 [effd3fe0] [c002cf34] irq_exit+0xa4/0xe0 [effd3ff0] [c000d834] call_do_irq+0x24/0x3c [c6047ae0] [c000489c] do_IRQ+0x8c/0x110 [c6047b00] [c000f86c] ret_from_except+0x0/0x18 --- Exception: 501 at _raw_spin_unlock_irq+0x30/0x50 LR = _raw_spin_unlock_irq+0x2c/0x50 [c6047bd0] [c0590158] wait_for_common+0xb8/0x170 [c6047c10] [c059024c] wait_for_completion_interruptible+0x1c/0x40 [c6047c20] [c022fc78] do_one_async_hash_op.isra.2.part.3+0x18/0x40 [c6047c30] [c022ff98] __test_hash+0x2f8/0x6c0 [c6047de0] [c0230388] test_hash+0x28/0xb0 [c6047e00] [c0230458] alg_test_hash+0x48/0xc0 [c6047e20] [c022fa94] alg_test+0x114/0x2e0 [c6047ea0] [c022cd1c] cryptomgr_test+0x4c/0x60 [c6047eb0] [c00497a4] kthread+0xc4/0xe0 [c6047f40] [c000f2fc] ret_from_kernel_thread+0x5c/0x64 Instruction dump: 41de01c8 80a9002c 2f850000 40fe0008 80a90008 80fa0018 3c60c06d 811a001c 3863f4a4 813a0020 815a0024 4830cd01 <0fe00000> 81340048 2f890000 40feff48 Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit 45e9af78b1abb00b7c394a7ce4e72584c3ca0eb8) Change-Id: Id66573defe2b61fb9d33ed05de221e8524418508 Reviewed-on: http://git.am.freescale.net:8181/17745 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 145b5ce..4920485 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1107,6 +1107,7 @@ static int ahash_digest(struct ahash_request *req) } edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; + edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->src_nents = src_nents; edesc->chained = chained; -- cgit v0.10.2 From c3a62369b6b2556216905421c172eb8b83f2592f Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:54 +0300 Subject: crypto: caam - fix uninitialized edesc->dst_dma field dst_dma not being properly initialized causes ahash_done_ctx_dst to try to free unallocated DMA memory: caam_jr ffe301000.jr: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x0000000006513340] [size=28 bytes] WARNING: at lib/dma-debug.c:1080 Modules linked in: caamhash(+) [last unloaded: caamhash] CPU: 0 PID: 1373 Comm: cryptomgr_test Tainted: G W 3.16.0-rc1 #23 task: ee23e350 ti: effd2000 task.ti: ee1f6000 NIP: c02889fc LR: c02889fc CTR: c02d7020 REGS: effd3d50 TRAP: 0700 Tainted: G W (3.16.0-rc1) MSR: 00029002 CR: 44048082 XER: 00000000 GPR00: c02889fc effd3e00 ee23e350 0000008e c1de3478 c1de382c 00000000 00029002 GPR08: 00000007 00000000 01660000 00000000 24048082 00000000 00000018 c07db080 GPR16: 00000006 00000100 0000002c eeb4a7e0 c07e1e10 c0da1180 00029002 c0d9b3c8 GPR24: eeb4a7c0 00000000 effd3e58 00000000 c078ad4c ee130210 00000000 06513340 NIP [c02889fc] check_unmap+0x8ac/0xab0 LR [c02889fc] check_unmap+0x8ac/0xab0 Call Trace: [effd3e00] [c02889fc] check_unmap+0x8ac/0xab0 (unreliable) [effd3e50] [c0288c78] debug_dma_unmap_page+0x78/0x90 [effd3ed0] [f94b89ec] ahash_done_ctx_dst+0x11c/0x200 [caamhash] [effd3f00] [c0429640] caam_jr_dequeue+0x1c0/0x280 [effd3f50] [c002c94c] tasklet_action+0xcc/0x1a0 [effd3f80] [c002cb30] __do_softirq+0x110/0x220 [effd3fe0] [c002cf34] irq_exit+0xa4/0xe0 [effd3ff0] [c000d834] call_do_irq+0x24/0x3c [ee1f7ae0] [c000489c] do_IRQ+0x8c/0x110 [ee1f7b00] [c000f86c] ret_from_except+0x0/0x18 --- Exception: 501 at _raw_spin_unlock_irq+0x30/0x50 LR = _raw_spin_unlock_irq+0x2c/0x50 [ee1f7bd0] [c0590158] wait_for_common+0xb8/0x170 [ee1f7c10] [c059024c] wait_for_completion_interruptible+0x1c/0x40 [ee1f7c20] [c022fc78] do_one_async_hash_op.isra.2.part.3+0x18/0x40 [ee1f7c30] [c022ffb8] __test_hash+0x318/0x6c0 [ee1f7de0] [c0230388] test_hash+0x28/0xb0 [ee1f7e00] [c02304a4] alg_test_hash+0x94/0xc0 [ee1f7e20] [c022fa94] alg_test+0x114/0x2e0 [ee1f7ea0] [c022cd1c] cryptomgr_test+0x4c/0x60 [ee1f7eb0] [c00497a4] kthread+0xc4/0xe0 [ee1f7f40] [c000f2fc] ret_from_kernel_thread+0x5c/0x64 Instruction dump: 41de01c8 80a9002c 2f850000 40fe0008 80a90008 80fa0018 3c60c06d 811a001c 3863f4a4 813a0020 815a0024 4830cd01 <0fe00000> 81340048 2f890000 40feff48 Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit 76b99080ccc964163b4567fac2bb8619f5ed789f) Change-Id: I8eb93e0c4282d45d70b67941cea62441221df845 Reviewed-on: http://git.am.freescale.net:8181/17746 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 4920485..b3ef772 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1265,6 +1265,7 @@ static int ahash_update_no_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; + edesc->dst_dma = 0; state->buf_dma = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, buf, *buflen); @@ -1464,6 +1465,7 @@ static int ahash_update_first(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; + edesc->dst_dma = 0; if (src_nents) { sg_to_sec4_sg_last(req->src, src_nents, -- cgit v0.10.2 From 9bfa675ce5f66793d07b1a39e69fa0ee642915ef Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Fri, 11 Jul 2014 15:34:55 +0300 Subject: crypto: caam - fix uninitialized state->buf_dma field state->buf_dma not being initialized can cause try_buf_map_to_sec4_sg to try to free unallocated DMA memory: caam_jr ffe301000.jr: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x000000002eb15068] [size=0 bytes] WARNING: at lib/dma-debug.c:1080 Modules linked in: caamhash(+) [last unloaded: caamhash] CPU: 0 PID: 1387 Comm: cryptomgr_test Tainted: G W 3.16.0-rc1 #23 task: eed24e90 ti: eebd0000 task.ti: eebd0000 NIP: c02889fc LR: c02889fc CTR: c02d7020 REGS: eebd1a50 TRAP: 0700 Tainted: G W (3.16.0-rc1) MSR: 00029002 CR: 44042082 XER: 00000000 GPR00: c02889fc eebd1b00 eed24e90 0000008d c1de3478 c1de382c 00000000 00029002 GPR08: 00000007 00000000 01660000 00000000 24042082 00000000 c07a1900 eeda2a40 GPR16: 005d62a0 c078ad4c 00000000 eeb15068 c07e1e10 c0da1180 00029002 c0d97408 GPR24: c62497a0 00000014 eebd1b58 00000000 c078ad4c ee130210 00000000 2eb15068 NIP [c02889fc] check_unmap+0x8ac/0xab0 LR [c02889fc] check_unmap+0x8ac/0xab0 Call Trace: [eebd1b00] [c02889fc] check_unmap+0x8ac/0xab0 (unreliable) --- Exception: 0 at (null) LR = (null) [eebd1b50] [c0288c78] debug_dma_unmap_page+0x78/0x90 (unreliable) [eebd1bd0] [f956f738] ahash_final_ctx+0x6d8/0x7b0 [caamhash] [eebd1c30] [c022ff4c] __test_hash+0x2ac/0x6c0 [eebd1de0] [c0230388] test_hash+0x28/0xb0 [eebd1e00] [c02304a4] alg_test_hash+0x94/0xc0 [eebd1e20] [c022fa94] alg_test+0x114/0x2e0 [eebd1ea0] [c022cd1c] cryptomgr_test+0x4c/0x60 [eebd1eb0] [c00497a4] kthread+0xc4/0xe0 [eebd1f40] [c000f2fc] ret_from_kernel_thread+0x5c/0x64 Instruction dump: 41de01c8 80a9002c 2f850000 40fe0008 80a90008 80fa0018 3c60c06d 811a001c 3863f4a4 813a0020 815a0024 4830cd01 <0fe00000> 81340048 2f890000 40feff48 Signed-off-by: Horia Geanta Acked-by: Kim Phillips Signed-off-by: Herbert Xu (cherry picked from commit de0e35ec2b72be30892f28a939c358af1df4fa2c) Change-Id: I7a3df37981b64f28191fee3f91332c371a179fc1 Reviewed-on: http://git.am.freescale.net:8181/17747 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index b3ef772..7728119 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1546,6 +1546,7 @@ static int ahash_init(struct ahash_request *req) state->final = ahash_final_no_ctx; state->current_buf = 0; + state->buf_dma = 0; return 0; } -- cgit v0.10.2 From a2814c6b29c8455e0cece39207025aa1cd270d8c Mon Sep 17 00:00:00 2001 From: Nitesh Narayan Lal Date: Tue, 2 Sep 2014 15:54:14 +0530 Subject: crypto: caam - Dynamic allocation of addresses for various memory blocks in CAAM. CAAM's memory is broken into following address blocks: Block Included Registers 0 General Registers 1-4 Job ring registers 6 RTIC registers 7 QI registers 8 DECO and CCB Size of the above stated blocks varies in various platforms. The block size can be 4K or 64K. The block size can be dynamically determined by reading CTPR register in CAAM. This patch initializes the block addresses dynamically based on the value read from this register. Signed-off-by: Ruchika Gupta Signed-off-by: Nitesh Narayan Lal Change-Id: I0ff5e5fe947134c56014544f335843dcc1595259 Reviewed-on: http://git.am.freescale.net:8181/17748 Tested-by: Review Code-CDREVIEW Reviewed-by: Ruchika Gupta Reviewed-by: Zhengxiong Jin diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 570fdaa..102869b 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -81,38 +81,37 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, u32 *status) { struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev); - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl = ctrlpriv->ctrl; + struct caam_deco __iomem *deco = ctrlpriv->deco; unsigned int timeout = 100000; u32 deco_dbg_reg, flags; int i; - /* Set the bit to request direct access to DECO0 */ - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; if (ctrlpriv->virt_en == 1) { - setbits32(&topregs->ctrl.deco_rsr, DECORSR_JR0); + setbits32(&ctrl->deco_rsr, DECORSR_JR0); - while (!(rd_reg32(&topregs->ctrl.deco_rsr) & DECORSR_VALID) && + while (!(rd_reg32(&ctrl->deco_rsr) & DECORSR_VALID) && --timeout) cpu_relax(); timeout = 100000; } - setbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); + setbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE); - while (!(rd_reg32(&topregs->ctrl.deco_rq) & DECORR_DEN0) && + while (!(rd_reg32(&ctrl->deco_rq) & DECORR_DEN0) && --timeout) cpu_relax(); if (!timeout) { dev_err(ctrldev, "failed to acquire DECO 0\n"); - clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); + clrbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE); return -ENODEV; } for (i = 0; i < desc_len(desc); i++) - wr_reg32(&topregs->deco.descbuf[i], *(desc + i)); + wr_reg32(&deco->descbuf[i], *(desc + i)); flags = DECO_JQCR_WHL; /* @@ -123,11 +122,11 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, flags |= DECO_JQCR_FOUR; /* Instruct the DECO to execute it */ - wr_reg32(&topregs->deco.jr_ctl_hi, flags); + wr_reg32(&deco->jr_ctl_hi, flags); timeout = 10000000; do { - deco_dbg_reg = rd_reg32(&topregs->deco.desc_dbg); + deco_dbg_reg = rd_reg32(&deco->desc_dbg); /* * If an error occured in the descriptor, then * the DECO status field will be set to 0x0D @@ -138,14 +137,14 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, cpu_relax(); } while ((deco_dbg_reg & DESC_DBG_DECO_STAT_VALID) && --timeout); - *status = rd_reg32(&topregs->deco.op_status_hi) & + *status = rd_reg32(&deco->op_status_hi) & DECO_OP_STATUS_HI_ERR_MASK; if (ctrlpriv->virt_en == 1) - clrbits32(&topregs->ctrl.deco_rsr, DECORSR_JR0); + clrbits32(&ctrl->deco_rsr, DECORSR_JR0); /* Mark the DECO as free */ - clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); + clrbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE); if (!timeout) return -EAGAIN; @@ -176,13 +175,13 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask, int gen_sk) { struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev); - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; struct rng4tst __iomem *r4tst; u32 *desc, status, rdsta_val; int ret = 0, sh_idx; - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - r4tst = &topregs->ctrl.r4tst[0]; + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; + r4tst = &ctrl->r4tst[0]; desc = kmalloc(CAAM_CMD_SZ * 7, GFP_KERNEL); if (!desc) @@ -212,12 +211,11 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask, * CAAM eras), then try again. */ rdsta_val = - rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IFMASK; + rd_reg32(&ctrl->r4tst[0].rdsta) & RDSTA_IFMASK; if (status || !(rdsta_val & (1 << sh_idx))) ret = -EAGAIN; if (ret) break; - dev_info(ctrldev, "Instantiated RNG4 SH%d\n", sh_idx); /* Clear the contents before recreating the descriptor */ memset(desc, 0x00, CAAM_CMD_SZ * 7); @@ -285,12 +283,12 @@ static int caam_remove(struct platform_device *pdev) { struct device *ctrldev; struct caam_drv_private *ctrlpriv; - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; int ring, ret = 0; ctrldev = &pdev->dev; ctrlpriv = dev_get_drvdata(ctrldev); - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; /* Remove platform devices for JobRs */ for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) { @@ -312,7 +310,7 @@ static int caam_remove(struct platform_device *pdev) #endif /* Unmap controller region */ - iounmap(&topregs->ctrl); + iounmap(&ctrl); kfree(ctrlpriv->jrpdev); kfree(ctrlpriv); @@ -329,12 +327,12 @@ static int caam_remove(struct platform_device *pdev) static void kick_trng(struct device *dev, int ent_delay) { struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; struct rng4tst __iomem *r4tst; u32 val; - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - r4tst = &topregs->ctrl.r4tst[0]; + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; + r4tst = &ctrl->r4tst[0]; /* put RNG4 into program mode */ setbits32(&r4tst->rtmctl, RTMCTL_PRGM); @@ -390,18 +388,18 @@ static int caam_rng_init(struct device *dev) int gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN; struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); u64 cha_vid_ls; - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; int ret = 0; - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - cha_vid_ls = rd_reg32(&topregs->ctrl.perfmon.cha_id_ls); + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; + cha_vid_ls = rd_reg32(&ctrl->perfmon.cha_id_ls); /* * If SEC has RNG version >= 4 and RNG state handle has not been * already instantiated, do RNG instantiation */ if ((cha_vid_ls & CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT >= 4) { ctrlpriv->rng4_sh_init = - rd_reg32(&topregs->ctrl.r4tst[0].rdsta); + rd_reg32(&ctrl->r4tst[0].rdsta); /* * If the secure keys (TDKEK, JDKEK, TDSK), were already * generated, signal this to the function that is instantiating @@ -412,7 +410,7 @@ static int caam_rng_init(struct device *dev) ctrlpriv->rng4_sh_init &= RDSTA_IFMASK; do { int inst_handles = - rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & + rd_reg32(&ctrl->r4tst[0].rdsta) & RDSTA_IFMASK; /* * If either SH were instantiated by somebody else @@ -447,7 +445,7 @@ static int caam_rng_init(struct device *dev) ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_IFMASK; /* Enable RDB bit so that RNG works faster */ - setbits32(&topregs->ctrl.scfgr, SCFGR_RDBENABLE); + setbits32(&ctrl->scfgr, SCFGR_RDBENABLE); } return 0; } @@ -460,12 +458,13 @@ static int caam_probe(struct platform_device *pdev) struct device *dev; struct device_node *nprop, *np; struct caam_ctrl __iomem *ctrl; - struct caam_full __iomem *topregs; struct caam_drv_private *ctrlpriv; #ifdef CONFIG_DEBUG_FS struct caam_perfmon *perfmon; #endif u32 scfgr, comp_params; + int pg_size; + int BLOCK_OFFSET = 0; ctrlpriv = kzalloc(sizeof(struct caam_drv_private), GFP_KERNEL); if (!ctrlpriv) @@ -483,10 +482,27 @@ static int caam_probe(struct platform_device *pdev) dev_err(dev, "caam: of_iomap() failed\n"); return -ENOMEM; } - ctrlpriv->ctrl = (struct caam_ctrl __force *)ctrl; + /* Finding the page size for using the CTPR_MS register */ + comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ms); + pg_size = (comp_params & CTPR_MS_PG_SZ_MASK) >> CTPR_MS_PG_SZ_SHIFT; - /* topregs used to derive pointers to CAAM sub-blocks only */ - topregs = (struct caam_full __iomem *)ctrl; + /* Allocating the BLOCK_OFFSET based on the supported page size on + * the platform + */ + if (pg_size == 0) + BLOCK_OFFSET = PG_SIZE_4K; + else + BLOCK_OFFSET = PG_SIZE_64K; + + ctrlpriv->ctrl = (struct caam_ctrl __force *)ctrl; + ctrlpriv->assure = (struct caam_assurance __force *) + ((uint8_t *)ctrl + + BLOCK_OFFSET * ASSURE_BLOCK_NUMBER + ); + ctrlpriv->deco = (struct caam_deco __force *) + ((uint8_t *)ctrl + + BLOCK_OFFSET * DECO_BLOCK_NUMBER + ); /* Get the IRQ of the controller (for security violations only) */ ctrlpriv->secvio_irq = of_irq_to_resource(nprop, 0, NULL); @@ -495,15 +511,14 @@ static int caam_probe(struct platform_device *pdev) * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, * long pointers in master configuration register */ - setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | + setbits32(&ctrl->mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); /* * Read the Compile Time paramters and SCFGR to determine * if Virtualization is enabled for this platform */ - comp_params = rd_reg32(&topregs->ctrl.perfmon.comp_parms_ms); - scfgr = rd_reg32(&topregs->ctrl.scfgr); + scfgr = rd_reg32(&ctrl->scfgr); ctrlpriv->virt_en = 0; if (comp_params & CTPR_MS_VIRT_EN_INCL) { @@ -521,7 +536,7 @@ static int caam_probe(struct platform_device *pdev) } if (ctrlpriv->virt_en == 1) - setbits32(&topregs->ctrl.jrstart, JRSTART_JR0_START | + setbits32(&ctrl->jrstart, JRSTART_JR0_START | JRSTART_JR1_START | JRSTART_JR2_START | JRSTART_JR3_START); @@ -550,7 +565,7 @@ static int caam_probe(struct platform_device *pdev) ctrlpriv->jrpdev = kzalloc(sizeof(struct platform_device *) * rspec, GFP_KERNEL); if (ctrlpriv->jrpdev == NULL) { - iounmap(&topregs->ctrl); + iounmap(&ctrl); return -ENOMEM; } @@ -563,6 +578,11 @@ static int caam_probe(struct platform_device *pdev) pr_warn("JR%d Platform device creation error\n", ring); continue; } + ctrlpriv->jr[ring] = (struct caam_job_ring __force *) + ((uint8_t *)ctrl + + (ring + JR_BLOCK_NUMBER) * + BLOCK_OFFSET + ); ctrlpriv->total_jobrs++; ring++; } @@ -582,12 +602,15 @@ static int caam_probe(struct platform_device *pdev) /* Check to see if QI present. If so, enable */ ctrlpriv->qi_present = - !!(rd_reg32(&topregs->ctrl.perfmon.comp_parms_ms) & + !!(rd_reg32(&ctrl->perfmon.comp_parms_ms) & CTPR_MS_QI_MASK); if (ctrlpriv->qi_present) { - ctrlpriv->qi = (struct caam_queue_if __force *)&topregs->qi; + ctrlpriv->qi = (struct caam_queue_if __force *) + ((uint8_t *)ctrl + + BLOCK_OFFSET * QI_BLOCK_NUMBER + ); /* This is all that's required to physically enable QI */ - wr_reg32(&topregs->qi.qi_control_lo, QICTL_DQEN); + wr_reg32(&ctrlpriv->qi->qi_control_lo, QICTL_DQEN); /* If QMAN driver is present, init CAAM-QI backend */ #ifdef CONFIG_FSL_QMAN @@ -612,8 +635,8 @@ static int caam_probe(struct platform_device *pdev) /* NOTE: RTIC detection ought to go here, around Si time */ ctrlpriv->era = caam_get_era(); - caam_id = (u64)rd_reg32(&topregs->ctrl.perfmon.caam_id_ms) << 32 | - (u64)rd_reg32(&topregs->ctrl.perfmon.caam_id_ls); + caam_id = (u64)rd_reg32(&ctrl->perfmon.caam_id_ms) << 32 | + (u64)rd_reg32(&ctrl->perfmon.caam_id_ls); /* Report "alive" for developer to see */ dev_info(dev, "device ID = 0x%016llx (Era %d)\n", caam_id, @@ -705,11 +728,11 @@ static int caam_probe(struct platform_device *pdev) } #ifdef CONFIG_PM -static int caam_stop_qi(struct caam_full __iomem *topregs) +static int caam_stop_qi(struct caam_qi __iomem *qi) { int qi_stopped, loop = 0; - setbits32(&topregs->qi.qi_control_lo, QICTL_STOP); + setbits32(&qi->qi_control_lo, QICTL_STOP); /* * Wait till QI Job's in Holding tank/deco are completed. @@ -717,10 +740,10 @@ static int caam_stop_qi(struct caam_full __iomem *topregs) * reenabled. */ while (loop <= 100000) { - qi_stopped = rd_reg32(&topregs->qi.qi_status) & + qi_stopped = rd_reg32(&qi->qi_status) & QISTA_STOPD; if (qi_stopped) { - wr_reg32(&topregs->qi.qi_control_lo, + wr_reg32(&qi->qi_control_lo, QICTL_STOP); return 0; } @@ -728,7 +751,7 @@ static int caam_stop_qi(struct caam_full __iomem *topregs) } /* Failed to stop QI interface. Reenable QI Interface */ - wr_reg32(&topregs->qi.qi_control_lo, QICTL_DQEN); + wr_reg32(&qi->qi_control_lo, QICTL_DQEN); return -EBUSY; } @@ -736,22 +759,22 @@ static int caam_stop_qi(struct caam_full __iomem *topregs) static int caam_suspend(struct device *dev) { struct caam_drv_private *caam_priv; - struct caam_full __iomem *topregs; struct caam_ctrl __iomem *ctrl; + struct caam_qi __iomem *qi; int ret = 0; caam_priv = dev_get_drvdata(dev); ctrl = caam_priv->ctrl; - topregs = (struct caam_full __iomem *)ctrl; + qi = caam_priv->qi; /* QI Interface graceful stoppping during suspend */ if (caam_priv->qi_present) { int qi_dqen; - qi_dqen = rd_reg32(&topregs->qi.qi_control_lo) & + qi_dqen = rd_reg32(&qi->qi_control_lo) & QICTL_DQEN; if (qi_dqen) - ret = caam_stop_qi(topregs); + ret = caam_stop_qi(qi); } return ret; @@ -761,23 +784,21 @@ static int caam_suspend(struct device *dev) static int caam_resume(struct device *dev) { struct caam_drv_private *caam_priv; - struct caam_full __iomem *topregs; struct caam_ctrl __iomem *ctrl; int ret; caam_priv = dev_get_drvdata(dev); ctrl = caam_priv->ctrl; - topregs = (struct caam_full __iomem *)ctrl; /* * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, * long pointers in master configuration register */ - setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | + setbits32(&ctrl->mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); /* Enable QI interface of SEC */ if (caam_priv->qi_present) - wr_reg32(&topregs->qi.qi_control_lo, QICTL_DQEN); + wr_reg32(&qi->qi_control_lo, QICTL_DQEN); ret = caam_rng_init(dev); diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index 9b2afc8..423886a 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -78,10 +78,11 @@ struct caam_drv_private { struct platform_device *pdev; /* Physical-presence section */ - struct caam_ctrl *ctrl; /* controller region */ - struct caam_deco **deco; /* DECO/CCB views */ - struct caam_assurance *ac; - struct caam_queue_if *qi; /* QI control region */ + struct caam_ctrl __iomem *ctrl; /* controller region */ + struct caam_deco __iomem *deco; /* DECO/CCB views */ + struct caam_assurance __iomem *assure; + struct caam_queue_if __iomem *qi; /* QI control region */ + struct caam_job_ring __iomem *jr[4]; /* JobR's register space */ /* * Detected geometry block. Filled in from device tree if powerpc, diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index 2982d85..79d2bf3 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -200,6 +200,8 @@ struct caam_perfmon { #define CTPR_MS_QI_MASK (0x1ull << CTPR_MS_QI_SHIFT) #define CTPR_MS_VIRT_EN_INCL 0x00000001 #define CTPR_MS_VIRT_EN_POR 0x00000002 +#define CTPR_MS_PG_SZ_MASK 0x10 +#define CTPR_MS_PG_SZ_SHIFT 4 u32 comp_parms_ms; /* CTPR - Compile Parameters Register */ u32 comp_parms_ls; /* CTPR - Compile Parameters Register */ u64 rsvd1[2]; @@ -764,34 +766,11 @@ struct caam_deco { #define DECO_JQCR_WHL 0x20000000 #define DECO_JQCR_FOUR 0x10000000 -/* - * Current top-level view of memory map is: - * - * 0x0000 - 0x0fff - CAAM Top-Level Control - * 0x1000 - 0x1fff - Job Ring 0 - * 0x2000 - 0x2fff - Job Ring 1 - * 0x3000 - 0x3fff - Job Ring 2 - * 0x4000 - 0x4fff - Job Ring 3 - * 0x5000 - 0x5fff - (unused) - * 0x6000 - 0x6fff - Assurance Controller - * 0x7000 - 0x7fff - Queue Interface - * 0x8000 - 0x8fff - DECO-CCB 0 - * 0x9000 - 0x9fff - DECO-CCB 1 - * 0xa000 - 0xafff - DECO-CCB 2 - * 0xb000 - 0xbfff - DECO-CCB 3 - * 0xc000 - 0xcfff - DECO-CCB 4 - * - * caam_full describes the full register view of CAAM if useful, - * although many configurations may choose to implement parts of - * the register map separately, in differing privilege regions - */ -struct caam_full { - struct caam_ctrl __iomem ctrl; - struct caam_job_ring jr[4]; - u64 rsvd[512]; - struct caam_assurance assure; - struct caam_queue_if qi; - struct caam_deco deco; -}; - -#endif /* REGS_H */ +#define JR_BLOCK_NUMBER 1 +#define ASSURE_BLOCK_NUMBER 6 +#define QI_BLOCK_NUMBER 7 +#define DECO_BLOCK_NUMBER 8 +#define PG_SIZE_4K 0x1000 +#define PG_SIZE_64K 0x10000 +#endif +/* REGS_H */ -- cgit v0.10.2 From 1a8b32f3fc34b98f1d892a305829f3ddf0fa71e2 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 5 Sep 2014 16:57:05 +0800 Subject: dmaengine: fsl-edma: add S/G support for big-endian eDMA model The hardware Scatter/Gather requires the to-be auto loaded TCDs struct in memory retains the same endian as the core independent of the model's register endian, the auto load engine will do the swap if need. Signed-off-by: Jingchang Lu Change-Id: I4251239bd06a64c166873f05e5799d95b267ead8 Reviewed-on: http://git.am.freescale.net:8181/19200 Tested-by: Review Code-CDREVIEW Reviewed-by: Huan Wang Reviewed-by: Zhengxiong Jin diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 9159831..1cc536b 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -183,11 +183,14 @@ struct fsl_edma_engine { static u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr) { + u32 dst; /* swap the reg offset for that in big-endian mode*/ - if (edma->big_endian) - return ioread16be((void __iomem *)(((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2))); - else + if (edma->big_endian) { + dst = ((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2); + return ioread16be((void __iomem *)dst); + } else { return ioread16(addr); + } } static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) @@ -200,28 +203,26 @@ static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr) { + u32 dst; /* swap the reg offset for that in big-endian mode*/ - if (edma->big_endian) - iowrite8(val, (void __iomem *)(((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x3))); - else + if (edma->big_endian) { + dst = ((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x3); + iowrite8(val, (void __iomem *)dst); + } else { iowrite8(val, addr); + } } static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) { + u32 dst; /* swap the reg offset for that in big-endian mode*/ - if (edma->big_endian) - writew(val, (void __iomem *)(((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2))); - else - writew(val, addr); -} - -static void edma_tcd_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) -{ - if (edma->big_endian) - iowrite16be(val, addr); - else + if (edma->big_endian) { + dst = ((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2); + iowrite16be(val, (void __iomem *)dst); + } else { iowrite16(val, addr); + } } static void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr) @@ -448,20 +449,25 @@ static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan, 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 + * TCD parameters should be swapped according the eDMA + * engine requirement. */ edma_writew(fsl_chan->edma, 0, addr + EDMA_TCD_CSR(ch)); - writel(src, addr + EDMA_TCD_SADDR(ch)); - writel(dst, addr + EDMA_TCD_DADDR(ch)); + edma_writel(fsl_chan->edma, src, addr + EDMA_TCD_SADDR(ch)); + edma_writel(fsl_chan->edma, dst, addr + EDMA_TCD_DADDR(ch)); + edma_writew(fsl_chan->edma, attr, addr + EDMA_TCD_ATTR(ch)); edma_writew(fsl_chan->edma, soff, addr + EDMA_TCD_SOFF(ch)); - writel(nbytes, addr + EDMA_TCD_NBYTES(ch)); - writel(slast, addr + EDMA_TCD_SLAST(ch)); + + edma_writel(fsl_chan->edma, nbytes, addr + EDMA_TCD_NBYTES(ch)); + edma_writel(fsl_chan->edma, slast, addr + EDMA_TCD_SLAST(ch)); + edma_writew(fsl_chan->edma, citer, addr + EDMA_TCD_CITER(ch)); edma_writew(fsl_chan->edma, biter, addr + EDMA_TCD_BITER(ch)); edma_writew(fsl_chan->edma, doff, addr + EDMA_TCD_DOFF(ch)); - writel(dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch)); + + edma_writel(fsl_chan->edma, dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch)); + edma_writew(fsl_chan->edma, csr, addr + EDMA_TCD_CSR(ch)); } @@ -474,20 +480,27 @@ static void fill_tcd_params(struct fsl_edma_engine *edma, 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 hardware SGs requires the TCDs to be auto loaded + * in the same endian as the core whenver the eDAM engine's + * register endian. So we don't swap the value, waitting + * for fsl_set_tcd_params doing the swap. */ - edma_writel(edma, src, &(tcd->saddr)); - edma_writel(edma, dst, &(tcd->daddr)); - edma_tcd_writew(edma, attr, &(tcd->attr)); - edma_tcd_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_tcd_writew(edma, EDMA_TCD_CITER_CITER(citer), &(tcd->citer)); - edma_tcd_writew(edma, EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff)); - edma_writel(edma, EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga)); - edma_tcd_writew(edma, EDMA_TCD_BITER_BITER(biter), &(tcd->biter)); + writel(src, &(tcd->saddr)); + writel(dst, &(tcd->daddr)); + + writew(attr, &(tcd->attr)); + + writew(EDMA_TCD_SOFF_SOFF(soff), &(tcd->soff)); + + writel(EDMA_TCD_NBYTES_NBYTES(nbytes), &(tcd->nbytes)); + writel(EDMA_TCD_SLAST_SLAST(slast), &(tcd->slast)); + + writew(EDMA_TCD_CITER_CITER(citer), &(tcd->citer)); + writew(EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff)); + + writel(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga)); + + writew(EDMA_TCD_BITER_BITER(biter), &(tcd->biter)); if (major_int) csr |= EDMA_TCD_CSR_INT_MAJOR; @@ -497,7 +510,7 @@ static void fill_tcd_params(struct fsl_edma_engine *edma, if (enable_sg) csr |= EDMA_TCD_CSR_E_SG; - edma_tcd_writew(edma, csr, &(tcd->csr)); + writew(csr, &(tcd->csr)); } static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, -- cgit v0.10.2 From db3bf6bf1f6deff2d62534f42acf103603e39e95 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:35 +0800 Subject: mtd: spi-nor: copy the SPI NOR commands to a new header file This patch adds a new header :spi-nor.h, and copies all the SPI NOR commands and relative macros into this new header. This hearder can be used by the m25p80.c and other spi-nor controller, such as Freescale's Quadspi. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris Change-Id: Ifd2b736d43fd58666e1fa163d39caa816c3b5375 Reviewed-on: http://git.am.freescale.net:8181/15505 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..483fc2a --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,55 @@ +#ifndef __LINUX_MTD_SPI_NOR_H +#define __LINUX_MTD_SPI_NOR_H + +/* Flash opcodes. */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ +#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_RDCR 0x35 /* Read configuration register */ + +/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ +#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + +/* Used for SST flashes only. */ +#define OPCODE_BP 0x02 /* Byte program */ +#define OPCODE_WRDI 0x04 /* Write disable */ +#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ + +/* Used for Macronix and Winbond flashes. */ +#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ +#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ + +/* Used for Spansion flashes only. */ +#define OPCODE_BRWR 0x17 /* Bank register write */ + +/* Status Register bits. */ +#define SR_WIP 1 /* Write in progress */ +#define SR_WEL 2 /* Write enable latch */ +/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 4 /* Block protect 0 */ +#define SR_BP1 8 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_SRWD 0x80 /* SR write protect */ + +#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ + +/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ + +#endif -- cgit v0.10.2 From bdc091067bc1b1183baabe8446519fbae2e55871 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:36 +0800 Subject: mtd: spi-nor: add the basic data structures The spi_nor{} is cloned from the m25p{}. The spi_nor{} can be used by both the m25p80 and spi-nor controller. We also add the spi_nor_xfer_cfg{} which can be used by the two fundamental primitives: read_xfer/write_xfer. 1) the hooks for spi_nor{}: @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. 2) Add a new field sst_write_second for the SST NOR write. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris Change-Id: I9c1e7766886c973c540342ad03f8d90430cc66a3 Reviewed-on: http://git.am.freescale.net:8181/15506 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 483fc2a..3a3c387 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -52,4 +52,114 @@ /* Configuration Register bits. */ #define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ +enum read_mode { + SPI_NOR_NORMAL = 0, + SPI_NOR_FAST, + SPI_NOR_DUAL, + SPI_NOR_QUAD, +}; + +/** + * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer + * @wren: command for "Write Enable", or 0x00 for not required + * @cmd: command for operation + * @cmd_pins: number of pins to send @cmd (1, 2, 4) + * @addr: address for operation + * @addr_pins: number of pins to send @addr (1, 2, 4) + * @addr_width: number of address bytes + * (3,4, or 0 for address not required) + * @mode: mode data + * @mode_pins: number of pins to send @mode (1, 2, 4) + * @mode_cycles: number of mode cycles (0 for mode not required) + * @dummy_cycles: number of dummy cycles (0 for dummy not required) + */ +struct spi_nor_xfer_cfg { + u8 wren; + u8 cmd; + u8 cmd_pins; + u32 addr; + u8 addr_pins; + u8 addr_width; + u8 mode; + u8 mode_pins; + u8 mode_cycles; + u8 dummy_cycles; +}; + +#define SPI_NOR_MAX_CMD_SIZE 8 +enum spi_nor_ops { + SPI_NOR_OPS_READ = 0, + SPI_NOR_OPS_WRITE, + SPI_NOR_OPS_ERASE, + SPI_NOR_OPS_LOCK, + SPI_NOR_OPS_UNLOCK, +}; + +/** + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector + * @read_opcode: the read opcode + * @read_dummy: the dummy needed by the read operation + * @program_opcode: the program opcode + * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation + * @cfg: used by the read_xfer/write_xfer + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the + * read/write/erase/lock/unlock operations + * @unprepare: [OPTIONAL] do some post work after the + * read/write/erase/lock/unlock operations + * @read_xfer: [OPTIONAL] the read fundamental primitive + * @write_xfer: [OPTIONAL] the writefundamental primitive + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register + * @read_id: [REPLACEABLE] read out the ID data, and find + * the proper spi_device_id + * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR + * at the offset @offs + * @priv: the private data + */ +struct spi_nor { + struct mtd_info *mtd; + struct mutex lock; + struct device *dev; + u32 page_size; + u8 addr_width; + u8 erase_opcode; + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; + enum read_mode flash_read; + bool sst_write_second; + struct spi_nor_xfer_cfg cfg; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); + int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len); + int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len); + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable); + const struct spi_device_id *(*read_id)(struct spi_nor *nor); + int (*wait_till_ready)(struct spi_nor *nor); + + int (*read)(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *read_buf); + void (*write)(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, const u_char *write_buf); + int (*erase)(struct spi_nor *nor, loff_t offs); + + void *priv; +}; #endif -- cgit v0.10.2 From 272e90f38f4a8a9512ce6eab4a9e147cc8c2d846 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:37 +0800 Subject: mtd: spi-nor: add the framework for SPI NOR This patch cloned most of the m25p80.c. In theory, it adds a new spi-nor layer. Before this patch, the layer is like: MTD ------------------------ m25p80 ------------------------ spi bus driver ------------------------ SPI NOR chip After this patch, the layer is like: MTD ------------------------ spi-nor ------------------------ m25p80 ------------------------ spi bus driver ------------------------ SPI NOR chip With the spi-nor controller driver(Freescale Quadspi), it looks like: MTD ------------------------ spi-nor ------------------------ fsl-quadspi ------------------------ SPI NOR chip New APIs: spi_nor_scan: used to scan a spi-nor flash. Signed-off-by: Huang Shijie Acked-by: Marek Vasut [Brian: rebased to include additional m25p_ids[] entry] Signed-off-by: Brian Norris Change-Id: I7c22c4c83350eac8c325ccd8292450fde79bb069 Reviewed-on: http://git.am.freescale.net:8181/15507 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5fab4e6e..8adb5af 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -320,6 +320,8 @@ source "drivers/mtd/onenand/Kconfig" source "drivers/mtd/lpddr/Kconfig" +source "drivers/mtd/spi-nor/Kconfig" + source "drivers/mtd/ubi/Kconfig" endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4cfb31e..40fd153 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig new file mode 100644 index 0000000..41591af --- /dev/null +++ b/drivers/mtd/spi-nor/Kconfig @@ -0,0 +1,6 @@ +config MTD_SPI_NOR_BASE + bool "the framework for SPI-NOR support" + depends on MTD + help + This is the framework for the SPI NOR which can be used by the SPI + device drivers and the SPI-NOR device driver. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..7dfe1f9 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 0000000..50b9290 --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,1088 @@ +/* + * Cloned most of the code from the m25p80.c + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Define max times to check status register before we give up. */ +#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ + +#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) + +/* + * Read the status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_sr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, OPCODE_RDSR, &val, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + + return val; +} + +/* + * Read configuration register, returning its value in the + * location. Return the configuration register value. + * Returns negative if error occured. + */ +static int read_cr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, OPCODE_RDCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; + } + + return val; +} + +/* + * Dummy Cycle calculation for different type of read. + * It can be used to support more commands with + * different dummy cycle requirements. + */ +static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) +{ + switch (nor->flash_read) { + case SPI_NOR_FAST: + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: + return 1; + case SPI_NOR_NORMAL: + return 0; + } + return 0; +} + +/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +static inline int write_sr(struct spi_nor *nor, u8 val) +{ + nor->cmd_buf[0] = val; + return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); +} + +/* + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static inline int write_enable(struct spi_nor *nor) +{ + return nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); +} + +/* + * Send write disble instruction to the chip. + */ +static inline int write_disable(struct spi_nor *nor) +{ + return nor->write_reg(nor, OPCODE_WRDI, NULL, 0, 0); +} + +static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/* Enable/disable 4-byte addressing mode. */ +static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) +{ + int status; + bool need_wren = false; + u8 cmd; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; + case CFI_MFR_MACRONIX: + case 0xEF /* winbond */: + if (need_wren) + write_enable(nor); + + cmd = enable ? OPCODE_EN4B : OPCODE_EX4B; + status = nor->write_reg(nor, cmd, NULL, 0, 0); + if (need_wren) + write_disable(nor); + + return status; + default: + /* Spansion style */ + nor->cmd_buf[0] = enable << 7; + return nor->write_reg(nor, OPCODE_BRWR, nor->cmd_buf, 1, 0); + } +} + +static int spi_nor_wait_till_ready(struct spi_nor *nor) +{ + unsigned long deadline; + int sr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) + break; + else if (!(sr & SR_WIP)) + return 0; + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + +/* + * Service routine to read status register until ready, or timeout occurs. + * Returns non-zero if error. + */ +static int wait_till_ready(struct spi_nor *nor) +{ + return nor->wait_till_ready(nor); +} + +/* + * Erase the whole flash memory + * + * Returns 0 if successful, non-zero otherwise. + */ +static int erase_chip(struct spi_nor *nor) +{ + int ret; + + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + write_enable(nor); + + return nor->write_reg(nor, OPCODE_CHIP_ERASE, NULL, 0, 0); +} + +static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + int ret = 0; + + mutex_lock(&nor->lock); + + if (nor->prepare) { + ret = nor->prepare(nor, ops); + if (ret) { + dev_err(nor->dev, "failed in the preparation.\n"); + mutex_unlock(&nor->lock); + return ret; + } + } + return ret; +} + +static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + if (nor->unprepare) + nor->unprepare(nor, ops); + mutex_unlock(&nor->lock); +} + +/* + * Erase an address range on the nor chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 addr, len; + uint32_t rem; + int ret; + + dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, + (long long)instr->len); + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE); + if (ret) + return ret; + + /* whole-chip erase? */ + if (len == mtd->size) { + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ + + /* "sector"-at-a-time erase */ + } else { + while (len) { + if (nor->erase(nor, addr)) { + ret = -EIO; + goto erase_err; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + } + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + instr->state = MTD_ERASE_FAILED; + return ret; +} + +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset < mtd->size - (mtd->size / 2)) + status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 4)) + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + else if (offset < mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset < mtd->size - (mtd->size / 32)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 64)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset+len > mtd->size - (mtd->size / 64)) + status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); + else if (offset+len > mtd->size - (mtd->size / 32)) + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else if (offset+len > mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 4)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset+len > mtd->size - (mtd->size / 2)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); + return ret; +} + +struct flash_info { + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + u16 addr_width; + + u16 flags; +#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ +#define SST_WRITE 0x04 /* use SST byte programming */ +#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ +#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ +#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +}; + +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .jedec_id = (_jedec_id), \ + .ext_id = (_ext_id), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), \ + }) + +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = (_flags), \ + }) + +/* NOTE: double check command sets and memory organization when you add + * more nor chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +const struct spi_device_id spi_nor_ids[] = { + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, + + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, + + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + + { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, + + /* EON -- en25xxx */ + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, + + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + + /* GigaDevice */ + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, + + /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + + /* Micron */ + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, + { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, + { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, + { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, + { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, + { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, + { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, + { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, + { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, + { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, + { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, + { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, + { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, + { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, + { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, + { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, + { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, + { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, + { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, + { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, + + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, + { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, + { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, + + { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, + { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, + { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, + + { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, + { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, + + /* Catalyst / On Semiconductor -- non-JEDEC */ + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { }, +}; + +static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) +{ + int tmp; + u8 id[5]; + u32 jedec; + u16 ext_jedec; + struct flash_info *info; + + tmp = nor->read_reg(nor, OPCODE_RDID, id, 5); + if (tmp < 0) { + dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + ext_jedec = id[3] << 8 | id[4]; + + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { + info = (void *)spi_nor_ids[tmp].driver_data; + if (info->jedec_id == jedec) { + if (info->ext_id == 0 || info->ext_id == ext_jedec) + return &spi_nor_ids[tmp]; + } + } + dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); + return ERR_PTR(-ENODEV); +} + +static const struct spi_device_id *jedec_probe(struct spi_nor *nor) +{ + return nor->read_id(nor); +} + +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); + if (ret) + return ret; + + ret = nor->read(nor, from, len, retlen, buf); + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; +} + +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t actual; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + write_enable(nor); + + nor->sst_write_second = false; + + actual = to % 2; + /* Start write from odd address. */ + if (actual) { + nor->program_opcode = OPCODE_BP; + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + } + to += actual; + + /* Write out most of the data here. */ + for (; actual < len - 1; actual += 2) { + nor->program_opcode = OPCODE_AAI_WP; + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; + nor->sst_write_second = true; + } + nor->sst_write_second = false; + + write_disable(nor); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(nor); + + nor->program_opcode = OPCODE_BP; + nor->write(nor, to, 1, retlen, buf + actual); + + ret = wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); + } +time_out: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; +} + +/* + * Write an address range to the nor chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. + */ +static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 page_offset, page_size, i; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto write_err; + + write_enable(nor); + + page_offset = to & (nor->page_size - 1); + + /* do all the bytes fit onto one page? */ + if (page_offset + len <= nor->page_size) { + nor->write(nor, to, len, retlen, buf); + } else { + /* the size of data remaining on the first page */ + page_size = nor->page_size - page_offset; + nor->write(nor, to, page_size, retlen, buf); + + /* write everything in nor->page_size chunks */ + for (i = page_size; i < len; i += page_size) { + page_size = len - i; + if (page_size > nor->page_size) + page_size = nor->page_size; + + wait_till_ready(nor); + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + +write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return 0; +} + +static int macronix_quad_enable(struct spi_nor *nor) +{ + int ret, val; + + val = read_sr(nor); + write_enable(nor); + + nor->cmd_buf[0] = val | SR_QUAD_EN_MX; + nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + + if (wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct spi_nor *nor, u16 val) +{ + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + + return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 2, 0); +} + +static int spansion_quad_enable(struct spi_nor *nor) +{ + int ret; + int quad_en = CR_QUAD_EN_SPAN << 8; + + write_enable(nor); + + ret = write_sr_cr(nor, quad_en); + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + + /* read back and check it */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Macronix quad-read not enabled\n"); + return -EINVAL; + } + return status; + default: + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Spansion quad-read not enabled\n"); + return -EINVAL; + } + return status; + } +} + +static int spi_nor_check(struct spi_nor *nor) +{ + if (!nor->dev || !nor->read || !nor->write || + !nor->read_reg || !nor->write_reg || !nor->erase) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } + + if (!nor->read_id) + nor->read_id = spi_nor_read_id; + if (!nor->wait_till_ready) + nor->wait_till_ready = spi_nor_wait_till_ready; + + return 0; +} + +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode) +{ + struct flash_info *info; + struct flash_platform_data *data; + struct device *dev = nor->dev; + struct mtd_info *mtd = nor->mtd; + struct device_node *np = dev->of_node; + int ret; + int i; + + ret = spi_nor_check(nor); + if (ret) + return ret; + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. + */ + data = dev_get_platdata(dev); + if (data && data->type) { + const struct spi_device_id *plat_id; + + for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { + plat_id = &spi_nor_ids[i]; + if (strcmp(data->type, plat_id->name)) + continue; + break; + } + + if (i < ARRAY_SIZE(spi_nor_ids) - 1) + id = plat_id; + else + dev_warn(dev, "unrecognized id %s\n", data->type); + } + + info = (void *)id->driver_data; + + if (info->jedec_id) { + const struct spi_device_id *jid; + + jid = jedec_probe(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { + /* + * JEDEC knows better, so overwrite platform ID. We + * can't trust partitions any longer, but we'll let + * mtd apply them anyway, since some partitions may be + * marked read-only, and we don't want to lose that + * information, even if it's not 100% accurate. + */ + dev_warn(dev, "found %s, expected %s\n", + jid->name, id->name); + id = jid; + info = (void *)jid->driver_data; + } + } + + mutex_init(&nor->lock); + + /* + * Atmel, SST and Intel/Numonyx serial nor tend to power + * up with the software protection bits set + */ + + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { + write_enable(nor); + write_sr(nor, 0); + } + + if (data && data->name) + mtd->name = data->name; + else + mtd->name = dev_name(dev); + + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + mtd->size = info->sector_size * info->n_sectors; + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + + /* nor protection support for STmicro chips */ + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + } + + /* sst nor chips use AAI word program */ + if (info->flags & SST_WRITE) + mtd->_write = sst_write; + else + mtd->_write = spi_nor_write; + + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = OPCODE_BE_4K; + mtd->erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + nor->erase_opcode = OPCODE_BE_4K_PMC; + mtd->erasesize = 4096; + } else { + nor->erase_opcode = OPCODE_SE; + mtd->erasesize = info->sector_size; + } + + if (info->flags & SPI_NOR_NO_ERASE) + mtd->flags |= MTD_NO_ERASE; + + mtd->dev.parent = dev; + nor->page_size = info->page_size; + mtd->writebufsize = nor->page_size; + + if (np) { + /* If we were instantiated by DT, use it */ + if (of_property_read_bool(np, "m25p,fast-read")) + nor->flash_read = SPI_NOR_FAST; + else + nor->flash_read = SPI_NOR_NORMAL; + } else { + /* If we weren't instantiated by DT, default to fast-read */ + nor->flash_read = SPI_NOR_FAST; + } + + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; + + /* Quad/Dual-read mode takes precedence over fast/normal */ + if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + ret = set_quad_mode(nor, info->jedec_id); + if (ret) { + dev_err(dev, "quad mode not supported\n"); + return ret; + } + nor->flash_read = SPI_NOR_QUAD; + } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { + nor->flash_read = SPI_NOR_DUAL; + } + + /* Default commands */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: + nor->read_opcode = OPCODE_QUAD_READ; + break; + case SPI_NOR_DUAL: + nor->read_opcode = OPCODE_DUAL_READ; + break; + case SPI_NOR_FAST: + nor->read_opcode = OPCODE_FAST_READ; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = OPCODE_NORM_READ; + break; + default: + dev_err(dev, "No Read opcode defined\n"); + return -EINVAL; + } + + nor->program_opcode = OPCODE_PP; + + if (info->addr_width) + nor->addr_width = info->addr_width; + else if (mtd->size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: + nor->read_opcode = OPCODE_QUAD_READ_4B; + break; + case SPI_NOR_DUAL: + nor->read_opcode = OPCODE_DUAL_READ_4B; + break; + case SPI_NOR_FAST: + nor->read_opcode = OPCODE_FAST_READ_4B; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = OPCODE_NORM_READ_4B; + break; + } + nor->program_opcode = OPCODE_PP_4B; + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = OPCODE_SE_4B; + mtd->erasesize = info->sector_size; + } else + set_4byte(nor, info->jedec_id, 1); + } else { + nor->addr_width = 3; + } + + nor->read_dummy = spi_nor_read_dummy_cycles(nor); + + dev_info(dev, "%s (%lld Kbytes)\n", id->name, + (long long)mtd->size >> 10); + + dev_dbg(dev, + "mtd .name = %s, .size = 0x%llx (%lldMiB), " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20), + mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions); + + if (mtd->numeraseregions) + for (i = 0; i < mtd->numeraseregions; i++) + dev_dbg(dev, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].erasesize / 1024, + mtd->eraseregions[i].numblocks); + return 0; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huang Shijie "); +MODULE_AUTHOR("Mike Lavender"); +MODULE_DESCRIPTION("framework for SPI NOR"); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 3a3c387..16d8409 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -162,4 +162,24 @@ struct spi_nor { void *priv; }; + +/** + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure + * @id: the spi_device_id provided by the driver + * @mode: the read mode supported by the driver + * + * The drivers can use this fuction to scan the SPI NOR. + * In the scanning, it will try to get all the necessary information to + * fill the mtd_info{} and the spi_nor{}. + * + * The board may assigns a spi_device_id with @id which be used to compared with + * the spi_device_id detected by the scanning. + * + * Return: 0 for success, others for failure. + */ +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode); +extern const struct spi_device_id spi_nor_ids[]; + #endif -- cgit v0.10.2 From 3a845c2faf6680769a0dce95fba0ecca2baba2d3 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:40 +0800 Subject: mtd: spi-nor: add a helper to find the spi_device_id Add the spi_nor_match_id() to find the proper spi_device_id with the NOR flash's name in the spi_nor_ids table. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris Change-Id: I471348a87ccde741055745d14cd25c84b480a803 Reviewed-on: http://git.am.freescale.net:8181/15508 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 50b9290..f7c9e63 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1082,6 +1082,18 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, return 0; } +const struct spi_device_id *spi_nor_match_id(char *name) +{ + const struct spi_device_id *id = spi_nor_ids; + + while (id->name[0]) { + if (!strcmp(name, id->name)) + return id; + id++; + } + return NULL; +} + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huang Shijie "); MODULE_AUTHOR("Mike Lavender"); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 16d8409..41dae78 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -182,4 +182,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, enum read_mode mode); extern const struct spi_device_id spi_nor_ids[]; +/** + * spi_nor_match_id() - find the spi_device_id by the name + * @name: the name of the spi_device_id + * + * The drivers use this function to find the spi_device_id + * specified by the @name. + * + * Return: returns the right spi_device_id pointer on success, + * and returns NULL on failure. + */ +const struct spi_device_id *spi_nor_match_id(char *name); + #endif -- cgit v0.10.2 From 68ccc61a7f9549af1acf396967c6bd5a519ea5fd Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:42 +0800 Subject: mtd: spi-nor: Add Freescale QuadSPI driver (0) What is the QuadSPI controller? The QuadSPI(Quad Serial Peripheral Interface) acts as an interface to one single or two external serial flash devices, each with up to 4 bidirectional data lines. (1) The QuadSPI controller is driven by the LUT(Look-up Table) registers. The LUT registers are a look-up-table for sequences of instructions. A valid sequence consists of four LUT registers. (2) The definition of the LUT register shows below: --------------------------------------------------- | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | --------------------------------------------------- There are several types of INSTRx, such as: CMD : the SPI NOR command. ADDR : the address for the SPI NOR command. DUMMY : the dummy cycles needed by the SPI NOR command. .... There are several types of PADx, such as: PAD1 : use a singe I/O line. PAD2 : use two I/O lines. PAD4 : use quad I/O lines. .... (3) Test this driver with the JFFS2 and UBIFS: For jffs2: ------------- #flash_eraseall /dev/mtd0 #mount -t jffs2 /dev/mtdblock0 tmp #bonnie++ -d tmp -u 0 -s 10 -r 5 For ubifs: ------------- #flash_eraseall /dev/mtd0 #ubiattach /dev/ubi_ctrl -m 0 #ubimkvol /dev/ubi0 -N test -m #mount -t ubifs ubi0:test tmp #bonnie++ -d tmp -u 0 -s 10 -r 5 Signed-off-by: Huang Shijie Signed-off-by: Brian Norris Change-Id: Ie32163e49eb62af82bf311e9165363a2f8880841 Reviewed-on: http://git.am.freescale.net:8181/15509 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 41591af..64cfc39 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -4,3 +4,9 @@ config MTD_SPI_NOR_BASE help This is the framework for the SPI NOR which can be used by the SPI device drivers and the SPI-NOR device driver. +config SPI_FSL_QUADSPI + tristate "Freescale Quad SPI controller" + depends on ARCH_MXC && MTD_SPI_NOR_BASE + help + This enables support for the Quad SPI controller in master mode. + We only connect the NOR to this controller now. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 7dfe1f9..51f9d8b 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o +obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c new file mode 100644 index 0000000..6dc08ed --- /dev/null +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -0,0 +1,1009 @@ +/* + * Freescale QuadSPI driver. + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The registers */ +#define QUADSPI_MCR 0x00 +#define QUADSPI_MCR_RESERVED_SHIFT 16 +#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT) +#define QUADSPI_MCR_MDIS_SHIFT 14 +#define QUADSPI_MCR_MDIS_MASK (1 << QUADSPI_MCR_MDIS_SHIFT) +#define QUADSPI_MCR_CLR_TXF_SHIFT 11 +#define QUADSPI_MCR_CLR_TXF_MASK (1 << QUADSPI_MCR_CLR_TXF_SHIFT) +#define QUADSPI_MCR_CLR_RXF_SHIFT 10 +#define QUADSPI_MCR_CLR_RXF_MASK (1 << QUADSPI_MCR_CLR_RXF_SHIFT) +#define QUADSPI_MCR_DDR_EN_SHIFT 7 +#define QUADSPI_MCR_DDR_EN_MASK (1 << QUADSPI_MCR_DDR_EN_SHIFT) +#define QUADSPI_MCR_END_CFG_SHIFT 2 +#define QUADSPI_MCR_END_CFG_MASK (3 << QUADSPI_MCR_END_CFG_SHIFT) +#define QUADSPI_MCR_SWRSTHD_SHIFT 1 +#define QUADSPI_MCR_SWRSTHD_MASK (1 << QUADSPI_MCR_SWRSTHD_SHIFT) +#define QUADSPI_MCR_SWRSTSD_SHIFT 0 +#define QUADSPI_MCR_SWRSTSD_MASK (1 << QUADSPI_MCR_SWRSTSD_SHIFT) + +#define QUADSPI_IPCR 0x08 +#define QUADSPI_IPCR_SEQID_SHIFT 24 +#define QUADSPI_IPCR_SEQID_MASK (0xF << QUADSPI_IPCR_SEQID_SHIFT) + +#define QUADSPI_BUF0CR 0x10 +#define QUADSPI_BUF1CR 0x14 +#define QUADSPI_BUF2CR 0x18 +#define QUADSPI_BUFXCR_INVALID_MSTRID 0xe + +#define QUADSPI_BUF3CR 0x1c +#define QUADSPI_BUF3CR_ALLMST_SHIFT 31 +#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) + +#define QUADSPI_BFGENCR 0x20 +#define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 +#define QUADSPI_BFGENCR_PAR_EN_MASK (1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT)) +#define QUADSPI_BFGENCR_SEQID_SHIFT 12 +#define QUADSPI_BFGENCR_SEQID_MASK (0xF << QUADSPI_BFGENCR_SEQID_SHIFT) + +#define QUADSPI_BUF0IND 0x30 +#define QUADSPI_BUF1IND 0x34 +#define QUADSPI_BUF2IND 0x38 +#define QUADSPI_SFAR 0x100 + +#define QUADSPI_SMPR 0x108 +#define QUADSPI_SMPR_DDRSMP_SHIFT 16 +#define QUADSPI_SMPR_DDRSMP_MASK (7 << QUADSPI_SMPR_DDRSMP_SHIFT) +#define QUADSPI_SMPR_FSDLY_SHIFT 6 +#define QUADSPI_SMPR_FSDLY_MASK (1 << QUADSPI_SMPR_FSDLY_SHIFT) +#define QUADSPI_SMPR_FSPHS_SHIFT 5 +#define QUADSPI_SMPR_FSPHS_MASK (1 << QUADSPI_SMPR_FSPHS_SHIFT) +#define QUADSPI_SMPR_HSENA_SHIFT 0 +#define QUADSPI_SMPR_HSENA_MASK (1 << QUADSPI_SMPR_HSENA_SHIFT) + +#define QUADSPI_RBSR 0x10c +#define QUADSPI_RBSR_RDBFL_SHIFT 8 +#define QUADSPI_RBSR_RDBFL_MASK (0x3F << QUADSPI_RBSR_RDBFL_SHIFT) + +#define QUADSPI_RBCT 0x110 +#define QUADSPI_RBCT_WMRK_MASK 0x1F +#define QUADSPI_RBCT_RXBRD_SHIFT 8 +#define QUADSPI_RBCT_RXBRD_USEIPS (0x1 << QUADSPI_RBCT_RXBRD_SHIFT) + +#define QUADSPI_TBSR 0x150 +#define QUADSPI_TBDR 0x154 +#define QUADSPI_SR 0x15c +#define QUADSPI_SR_IP_ACC_SHIFT 1 +#define QUADSPI_SR_IP_ACC_MASK (0x1 << QUADSPI_SR_IP_ACC_SHIFT) +#define QUADSPI_SR_AHB_ACC_SHIFT 2 +#define QUADSPI_SR_AHB_ACC_MASK (0x1 << QUADSPI_SR_AHB_ACC_SHIFT) + +#define QUADSPI_FR 0x160 +#define QUADSPI_FR_TFF_MASK 0x1 + +#define QUADSPI_SFA1AD 0x180 +#define QUADSPI_SFA2AD 0x184 +#define QUADSPI_SFB1AD 0x188 +#define QUADSPI_SFB2AD 0x18c +#define QUADSPI_RBDR 0x200 + +#define QUADSPI_LUTKEY 0x300 +#define QUADSPI_LUTKEY_VALUE 0x5AF05AF0 + +#define QUADSPI_LCKCR 0x304 +#define QUADSPI_LCKER_LOCK 0x1 +#define QUADSPI_LCKER_UNLOCK 0x2 + +#define QUADSPI_RSER 0x164 +#define QUADSPI_RSER_TFIE (0x1 << 0) + +#define QUADSPI_LUT_BASE 0x310 + +/* + * The definition of the LUT register shows below: + * + * --------------------------------------------------- + * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | + * --------------------------------------------------- + */ +#define OPRND0_SHIFT 0 +#define PAD0_SHIFT 8 +#define INSTR0_SHIFT 10 +#define OPRND1_SHIFT 16 + +/* Instruction set for the LUT register. */ +#define LUT_STOP 0 +#define LUT_CMD 1 +#define LUT_ADDR 2 +#define LUT_DUMMY 3 +#define LUT_MODE 4 +#define LUT_MODE2 5 +#define LUT_MODE4 6 +#define LUT_READ 7 +#define LUT_WRITE 8 +#define LUT_JMP_ON_CS 9 +#define LUT_ADDR_DDR 10 +#define LUT_MODE_DDR 11 +#define LUT_MODE2_DDR 12 +#define LUT_MODE4_DDR 13 +#define LUT_READ_DDR 14 +#define LUT_WRITE_DDR 15 +#define LUT_DATA_LEARN 16 + +/* + * The PAD definitions for LUT register. + * + * The pad stands for the lines number of IO[0:3]. + * For example, the Quad read need four IO lines, so you should + * set LUT_PAD4 which means we use four IO lines. + */ +#define LUT_PAD1 0 +#define LUT_PAD2 1 +#define LUT_PAD4 2 + +/* Oprands for the LUT register. */ +#define ADDR24BIT 0x18 +#define ADDR32BIT 0x20 + +/* Macros for constructing the LUT register. */ +#define LUT0(ins, pad, opr) \ + (((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \ + ((LUT_##ins) << INSTR0_SHIFT)) + +#define LUT1(ins, pad, opr) (LUT0(ins, pad, opr) << OPRND1_SHIFT) + +/* other macros for LUT register. */ +#define QUADSPI_LUT(x) (QUADSPI_LUT_BASE + (x) * 4) +#define QUADSPI_LUT_NUM 64 + +/* SEQID -- we can have 16 seqids at most. */ +#define SEQID_QUAD_READ 0 +#define SEQID_WREN 1 +#define SEQID_WRDI 2 +#define SEQID_RDSR 3 +#define SEQID_SE 4 +#define SEQID_CHIP_ERASE 5 +#define SEQID_PP 6 +#define SEQID_RDID 7 +#define SEQID_WRSR 8 +#define SEQID_RDCR 9 +#define SEQID_EN4B 10 +#define SEQID_BRWR 11 + +enum fsl_qspi_devtype { + FSL_QUADSPI_VYBRID, + FSL_QUADSPI_IMX6SX, +}; + +struct fsl_qspi_devtype_data { + enum fsl_qspi_devtype devtype; + int rxfifo; + int txfifo; +}; + +static struct fsl_qspi_devtype_data vybrid_data = { + .devtype = FSL_QUADSPI_VYBRID, + .rxfifo = 128, + .txfifo = 64 +}; + +static struct fsl_qspi_devtype_data imx6sx_data = { + .devtype = FSL_QUADSPI_IMX6SX, + .rxfifo = 128, + .txfifo = 512 +}; + +#define FSL_QSPI_MAX_CHIP 4 +struct fsl_qspi { + struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; + struct spi_nor nor[FSL_QSPI_MAX_CHIP]; + void __iomem *iobase; + void __iomem *ahb_base; /* Used when read from AHB bus */ + u32 memmap_phy; + struct clk *clk, *clk_en; + struct device *dev; + struct completion c; + struct fsl_qspi_devtype_data *devtype_data; + u32 nor_size; + u32 nor_num; + u32 clk_rate; + unsigned int chip_base_addr; /* We may support two chips. */ +}; + +static inline int is_vybrid_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_VYBRID; +} + +static inline int is_imx6sx_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX; +} + +/* + * An IC bug makes us to re-arrange the 32-bit data. + * The following chips, such as IMX6SLX, have fixed this bug. + */ +static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) +{ + return is_vybrid_qspi(q) ? __swab32(a) : a; +} + +static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) +{ + writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); +} + +static inline void fsl_qspi_lock_lut(struct fsl_qspi *q) +{ + writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); +} + +static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) +{ + struct fsl_qspi *q = dev_id; + u32 reg; + + /* clear interrupt */ + reg = readl(q->iobase + QUADSPI_FR); + writel(reg, q->iobase + QUADSPI_FR); + + if (reg & QUADSPI_FR_TFF_MASK) + complete(&q->c); + + dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x:0x%.8x\n", q->chip_base_addr, reg); + return IRQ_HANDLED; +} + +static void fsl_qspi_init_lut(struct fsl_qspi *q) +{ + void *__iomem base = q->iobase; + int rxfifo = q->devtype_data->rxfifo; + u32 lut_base; + u8 cmd, addrlen, dummy; + int i; + + fsl_qspi_unlock_lut(q); + + /* Clear all the LUT table */ + for (i = 0; i < QUADSPI_LUT_NUM; i++) + writel(0, base + QUADSPI_LUT_BASE + i * 4); + + /* Quad Read */ + lut_base = SEQID_QUAD_READ * 4; + + if (q->nor_size <= SZ_16M) { + cmd = OPCODE_QUAD_READ; + addrlen = ADDR24BIT; + dummy = 8; + } else { + /* use the 4-byte address */ + cmd = OPCODE_QUAD_READ; + addrlen = ADDR32BIT; + dummy = 8; + } + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + + /* Write enable */ + lut_base = SEQID_WREN * 4; + writel(LUT0(CMD, PAD1, OPCODE_WREN), base + QUADSPI_LUT(lut_base)); + + /* Page Program */ + lut_base = SEQID_PP * 4; + + if (q->nor_size <= SZ_16M) { + cmd = OPCODE_PP; + addrlen = ADDR24BIT; + } else { + /* use the 4-byte address */ + cmd = OPCODE_PP; + addrlen = ADDR32BIT; + } + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); + + /* Read Status */ + lut_base = SEQID_RDSR * 4; + writel(LUT0(CMD, PAD1, OPCODE_RDSR) | LUT1(READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Erase a sector */ + lut_base = SEQID_SE * 4; + + if (q->nor_size <= SZ_16M) { + cmd = OPCODE_SE; + addrlen = ADDR24BIT; + } else { + /* use the 4-byte address */ + cmd = OPCODE_SE; + addrlen = ADDR32BIT; + } + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + /* Erase the whole chip */ + lut_base = SEQID_CHIP_ERASE * 4; + writel(LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), + base + QUADSPI_LUT(lut_base)); + + /* READ ID */ + lut_base = SEQID_RDID * 4; + writel(LUT0(CMD, PAD1, OPCODE_RDID) | LUT1(READ, PAD1, 0x8), + base + QUADSPI_LUT(lut_base)); + + /* Write Register */ + lut_base = SEQID_WRSR * 4; + writel(LUT0(CMD, PAD1, OPCODE_WRSR) | LUT1(WRITE, PAD1, 0x2), + base + QUADSPI_LUT(lut_base)); + + /* Read Configuration Register */ + lut_base = SEQID_RDCR * 4; + writel(LUT0(CMD, PAD1, OPCODE_RDCR) | LUT1(READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Write disable */ + lut_base = SEQID_WRDI * 4; + writel(LUT0(CMD, PAD1, OPCODE_WRDI), base + QUADSPI_LUT(lut_base)); + + /* Enter 4 Byte Mode (Micron) */ + lut_base = SEQID_EN4B * 4; + writel(LUT0(CMD, PAD1, OPCODE_EN4B), base + QUADSPI_LUT(lut_base)); + + /* Enter 4 Byte Mode (Spansion) */ + lut_base = SEQID_BRWR * 4; + writel(LUT0(CMD, PAD1, OPCODE_BRWR), base + QUADSPI_LUT(lut_base)); + + fsl_qspi_lock_lut(q); +} + +/* Get the SEQID for the command */ +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) +{ + switch (cmd) { + case OPCODE_QUAD_READ: + return SEQID_QUAD_READ; + case OPCODE_WREN: + return SEQID_WREN; + case OPCODE_WRDI: + return SEQID_WRDI; + case OPCODE_RDSR: + return SEQID_RDSR; + case OPCODE_SE: + return SEQID_SE; + case OPCODE_CHIP_ERASE: + return SEQID_CHIP_ERASE; + case OPCODE_PP: + return SEQID_PP; + case OPCODE_RDID: + return SEQID_RDID; + case OPCODE_WRSR: + return SEQID_WRSR; + case OPCODE_RDCR: + return SEQID_RDCR; + case OPCODE_EN4B: + return SEQID_EN4B; + case OPCODE_BRWR: + return SEQID_BRWR; + default: + dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); + break; + } + return -EINVAL; +} + +static int +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) +{ + void *__iomem base = q->iobase; + int seqid; + u32 reg, reg2; + int err; + + init_completion(&q->c); + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n", + q->chip_base_addr, addr, len, cmd); + + /* save the reg */ + reg = readl(base + QUADSPI_MCR); + + writel(q->memmap_phy + q->chip_base_addr + addr, base + QUADSPI_SFAR); + writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, + base + QUADSPI_RBCT); + writel(reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); + + do { + reg2 = readl(base + QUADSPI_SR); + if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) { + udelay(1); + dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2); + continue; + } + break; + } while (1); + + /* trigger the LUT now */ + seqid = fsl_qspi_get_seqid(q, cmd); + writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR); + + /* Wait for the interrupt. */ + err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000)); + if (!err) { + dev_err(q->dev, + "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", + cmd, addr, readl(base + QUADSPI_FR), + readl(base + QUADSPI_SR)); + err = -ETIMEDOUT; + } else { + err = 0; + } + + /* restore the MCR */ + writel(reg, base + QUADSPI_MCR); + + return err; +} + +/* Read out the data from the QUADSPI_RBDR buffer registers. */ +static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf) +{ + u32 tmp; + int i = 0; + + while (len > 0) { + tmp = readl(q->iobase + QUADSPI_RBDR + i * 4); + tmp = fsl_qspi_endian_xchg(q, tmp); + dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n", + q->chip_base_addr, tmp); + + if (len >= 4) { + *((u32 *)rxbuf) = tmp; + rxbuf += 4; + } else { + memcpy(rxbuf, &tmp, len); + break; + } + + len -= 4; + i++; + } +} + +/* + * If we have changed the content of the flash by writing or erasing, + * we need to invalidate the AHB buffer. If we do not do so, we may read out + * the wrong data. The spec tells us reset the AHB domain and Serial Flash + * domain at the same time. + */ +static inline void fsl_qspi_invalid(struct fsl_qspi *q) +{ + u32 reg; + + reg = readl(q->iobase + QUADSPI_MCR); + reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK; + writel(reg, q->iobase + QUADSPI_MCR); + + /* + * The minimum delay : 1 AHB + 2 SFCK clocks. + * Delay 1 us is enough. + */ + udelay(1); + + reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK); + writel(reg, q->iobase + QUADSPI_MCR); +} + +static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, + u8 opcode, unsigned int to, u32 *txbuf, + unsigned count, size_t *retlen) +{ + int ret, i, j; + u32 tmp; + + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n", + q->chip_base_addr, to, count); + + /* clear the TX FIFO. */ + tmp = readl(q->iobase + QUADSPI_MCR); + writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); + + /* fill the TX data to the FIFO */ + for (j = 0, i = ((count + 3) / 4); j < i; j++) { + tmp = fsl_qspi_endian_xchg(q, *txbuf); + writel(tmp, q->iobase + QUADSPI_TBDR); + txbuf++; + } + + /* Trigger it */ + ret = fsl_qspi_runcmd(q, opcode, to, count); + + if (ret == 0 && retlen) + *retlen += count; + + return ret; +} + +static void fsl_qspi_set_map_addr(struct fsl_qspi *q) +{ + int nor_size = q->nor_size; + void __iomem *base = q->iobase; + + writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); + writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); + writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); + writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); +} + +/* + * There are two different ways to read out the data from the flash: + * the "IP Command Read" and the "AHB Command Read". + * + * The IC guy suggests we use the "AHB Command Read" which is faster + * then the "IP Command Read". (What's more is that there is a bug in + * the "IP Command Read" in the Vybrid.) + * + * After we set up the registers for the "AHB Command Read", we can use + * the memcpy to read the data directly. A "missed" access to the buffer + * causes the controller to clear the buffer, and use the sequence pointed + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. + */ +static void fsl_qspi_init_abh_read(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + int seqid; + + /* AHB configuration for access buffer 0/1/2 .*/ + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); + writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); + + /* We only use the buffer3 */ + writel(0, base + QUADSPI_BUF0IND); + writel(0, base + QUADSPI_BUF1IND); + writel(0, base + QUADSPI_BUF2IND); + + /* Set the default lut sequence for AHB Read. */ + seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode); + writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT, + q->iobase + QUADSPI_BFGENCR); +} + +/* We use this function to do some basic init for spi_nor_scan(). */ +static int fsl_qspi_nor_setup(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + u32 reg; + int ret; + + /* the default frequency, we will change it in the future.*/ + ret = clk_set_rate(q->clk, 66000000); + if (ret) + return ret; + + /* Init the LUT table. */ + fsl_qspi_init_lut(q); + + /* Disable the module */ + writel(QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, + base + QUADSPI_MCR); + + reg = readl(base + QUADSPI_SMPR); + writel(reg & ~(QUADSPI_SMPR_FSDLY_MASK + | QUADSPI_SMPR_FSPHS_MASK + | QUADSPI_SMPR_HSENA_MASK + | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR); + + /* Enable the module */ + writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, + base + QUADSPI_MCR); + + /* enable the interrupt */ + writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); + + return 0; +} + +static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) +{ + unsigned long rate = q->clk_rate; + int ret; + + if (is_imx6sx_qspi(q)) + rate *= 4; + + ret = clk_set_rate(q->clk, rate); + if (ret) + return ret; + + /* Init the LUT table again. */ + fsl_qspi_init_lut(q); + + /* Init for AHB read */ + fsl_qspi_init_abh_read(q); + + return 0; +} + +static struct of_device_id fsl_qspi_dt_ids[] = { + { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, + { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); + +static void fsl_qspi_set_base_addr(struct fsl_qspi *q, struct spi_nor *nor) +{ + q->chip_base_addr = q->nor_size * (nor - q->nor); +} + +static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + int ret; + struct fsl_qspi *q = nor->priv; + + ret = fsl_qspi_runcmd(q, opcode, 0, len); + if (ret) + return ret; + + fsl_qspi_read_data(q, len, buf); + return 0; +} + +static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + if (!buf) { + ret = fsl_qspi_runcmd(q, opcode, 0, 1); + if (ret) + return ret; + + if (opcode == OPCODE_CHIP_ERASE) + fsl_qspi_invalid(q); + + } else if (len > 0) { + ret = fsl_qspi_nor_write(q, nor, opcode, 0, + (u32 *)buf, len, NULL); + } else { + dev_err(q->dev, "invalid cmd %d\n", opcode); + ret = -EINVAL; + } + + return ret; +} + +static void fsl_qspi_write(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, const u_char *buf) +{ + struct fsl_qspi *q = nor->priv; + + fsl_qspi_nor_write(q, nor, nor->program_opcode, to, + (u32 *)buf, len, retlen); + + /* invalid the data in the AHB buffer. */ + fsl_qspi_invalid(q); +} + +static int fsl_qspi_read(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct fsl_qspi *q = nor->priv; + u8 cmd = nor->read_opcode; + int ret; + + dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", + cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); + + /* Wait until the previous command is finished. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + /* Read out the data directly from the AHB buffer.*/ + memcpy(buf, q->ahb_base + q->chip_base_addr + from, len); + + *retlen += len; + return 0; +} + +static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n", + nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs); + + /* Wait until finished previous write command. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + if (ret) + return ret; + + ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); + if (ret) + return ret; + + fsl_qspi_invalid(q); + return 0; +} + +static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + ret = clk_enable(q->clk_en); + if (ret) + return ret; + + ret = clk_enable(q->clk); + if (ret) { + clk_disable(q->clk_en); + return ret; + } + + fsl_qspi_set_base_addr(q, nor); + return 0; +} + +static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct fsl_qspi *q = nor->priv; + + clk_disable(q->clk); + clk_disable(q->clk_en); +} + +static int fsl_qspi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata; + struct device *dev = &pdev->dev; + struct fsl_qspi *q; + struct resource *res; + struct spi_nor *nor; + struct mtd_info *mtd; + int ret, i = 0; + bool has_second_chip = false; + const struct of_device_id *of_id = + of_match_device(fsl_qspi_dt_ids, &pdev->dev); + + q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL); + if (!q) + return -ENOMEM; + + q->nor_num = of_get_child_count(dev->of_node); + if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP) + return -ENODEV; + + /* find the resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); + q->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(q->iobase)) { + ret = PTR_ERR(q->iobase); + goto map_failed; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "QuadSPI-memory"); + q->ahb_base = devm_ioremap_resource(dev, res); + if (IS_ERR(q->ahb_base)) { + ret = PTR_ERR(q->ahb_base); + goto map_failed; + } + q->memmap_phy = res->start; + + /* find the clocks */ + q->clk_en = devm_clk_get(dev, "qspi_en"); + if (IS_ERR(q->clk_en)) { + ret = PTR_ERR(q->clk_en); + goto map_failed; + } + + q->clk = devm_clk_get(dev, "qspi"); + if (IS_ERR(q->clk)) { + ret = PTR_ERR(q->clk); + goto map_failed; + } + + ret = clk_prepare_enable(q->clk_en); + if (ret) { + dev_err(dev, "can not enable the qspi_en clock\n"); + goto map_failed; + } + + ret = clk_prepare_enable(q->clk); + if (ret) { + clk_disable_unprepare(q->clk_en); + dev_err(dev, "can not enable the qspi clock\n"); + goto map_failed; + } + + /* find the irq */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "failed to get the irq\n"); + goto irq_failed; + } + + ret = devm_request_irq(dev, ret, + fsl_qspi_irq_handler, 0, pdev->name, q); + if (ret) { + dev_err(dev, "failed to request irq.\n"); + goto irq_failed; + } + + q->dev = dev; + q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; + platform_set_drvdata(pdev, q); + + ret = fsl_qspi_nor_setup(q); + if (ret) + goto irq_failed; + + if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) + has_second_chip = true; + + /* iterate the subnodes. */ + for_each_available_child_of_node(dev->of_node, np) { + const struct spi_device_id *id; + char modalias[40]; + + /* skip the holes */ + if (!has_second_chip) + i *= 2; + + nor = &q->nor[i]; + mtd = &q->mtd[i]; + + nor->mtd = mtd; + nor->dev = dev; + nor->priv = q; + mtd->priv = nor; + + /* fill the hooks */ + nor->read_reg = fsl_qspi_read_reg; + nor->write_reg = fsl_qspi_write_reg; + nor->read = fsl_qspi_read; + nor->write = fsl_qspi_write; + nor->erase = fsl_qspi_erase; + + nor->prepare = fsl_qspi_prep; + nor->unprepare = fsl_qspi_unprep; + + if (of_modalias_node(np, modalias, sizeof(modalias)) < 0) + goto map_failed; + + id = spi_nor_match_id(modalias); + if (!id) + goto map_failed; + + ret = of_property_read_u32(np, "spi-max-frequency", + &q->clk_rate); + if (ret < 0) + goto map_failed; + + /* set the chip address for READID */ + fsl_qspi_set_base_addr(q, nor); + + ret = spi_nor_scan(nor, id, SPI_NOR_QUAD); + if (ret) + goto map_failed; + + ppdata.of_node = np; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) + goto map_failed; + + /* Set the correct NOR size now. */ + if (q->nor_size == 0) { + q->nor_size = mtd->size; + + /* Map the SPI NOR to accessiable address */ + fsl_qspi_set_map_addr(q); + } + + /* + * The TX FIFO is 64 bytes in the Vybrid, but the Page Program + * may writes 265 bytes per time. The write is working in the + * unit of the TX FIFO, not in the unit of the SPI NOR's page + * size. + * + * So shrink the spi_nor->page_size if it is larger then the + * TX FIFO. + */ + if (nor->page_size > q->devtype_data->txfifo) + nor->page_size = q->devtype_data->txfifo; + + i++; + } + + /* finish the rest init. */ + ret = fsl_qspi_nor_setup_last(q); + if (ret) + goto last_init_failed; + + clk_disable(q->clk); + clk_disable(q->clk_en); + dev_info(dev, "QuadSPI SPI NOR flash driver\n"); + return 0; + +last_init_failed: + for (i = 0; i < q->nor_num; i++) + mtd_device_unregister(&q->mtd[i]); + +irq_failed: + clk_disable_unprepare(q->clk); + clk_disable_unprepare(q->clk_en); +map_failed: + dev_err(dev, "Freescale QuadSPI probe failed\n"); + return ret; +} + +static int fsl_qspi_remove(struct platform_device *pdev) +{ + struct fsl_qspi *q = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < q->nor_num; i++) + mtd_device_unregister(&q->mtd[i]); + + /* disable the hardware */ + writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + writel(0x0, q->iobase + QUADSPI_RSER); + + clk_unprepare(q->clk); + clk_unprepare(q->clk_en); + return 0; +} + +static struct platform_driver fsl_qspi_driver = { + .driver = { + .name = "fsl-quadspi", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .of_match_table = fsl_qspi_dt_ids, + }, + .probe = fsl_qspi_probe, + .remove = fsl_qspi_remove, +}; +module_platform_driver(fsl_qspi_driver); + +MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver"); +MODULE_AUTHOR("Freescale Semiconductor Inc."); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From c008407abeca3e790bbd9ef570ad8872661dcf35 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:22:57 -0700 Subject: mtd: spi-nor: EXPORT symbols which could be used by module drivers Fix errors like this: ERROR: "spi_nor_ids" [drivers/mtd/devices/m25p80.ko] undefined! ERROR: "spi_nor_scan" [drivers/mtd/devices/m25p80.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Signed-off-by: Brian Norris Change-Id: I61a5d463500e2646dbb10dbab543550bf1ef009e Reviewed-on: http://git.am.freescale.net:8181/15510 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index f7c9e63..5cd86eb 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -584,6 +584,7 @@ const struct spi_device_id spi_nor_ids[] = { { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { }, }; +EXPORT_SYMBOL_GPL(spi_nor_ids); static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) { @@ -1081,6 +1082,7 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, mtd->eraseregions[i].numblocks); return 0; } +EXPORT_SYMBOL_GPL(spi_nor_scan); const struct spi_device_id *spi_nor_match_id(char *name) { @@ -1093,6 +1095,7 @@ const struct spi_device_id *spi_nor_match_id(char *name) } return NULL; } +EXPORT_SYMBOL_GPL(spi_nor_match_id); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huang Shijie "); -- cgit v0.10.2 From 6c51db32b32ebb853a97829c83b15ddd3cf2c52d Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 10 Apr 2014 16:27:28 +0800 Subject: mtd: spi-nor: add the copyright information Add the copyright information for spi-nor.c and spi-nor.h. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris Change-Id: Idde8bf06ec19e581234865b700207ee60e953755 Reviewed-on: http://git.am.freescale.net:8181/15511 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5cd86eb..6c64ab9 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1,5 +1,9 @@ /* - * Cloned most of the code from the m25p80.c + * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with + * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c + * + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 41dae78..9428d28 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -1,3 +1,12 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * 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. + */ + #ifndef __LINUX_MTD_SPI_NOR_H #define __LINUX_MTD_SPI_NOR_H -- cgit v0.10.2 From 249d5b71f0a179c99965605ab177b5f476bf5507 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:10:23 -0700 Subject: mtd: spi-nor: drop \t after #define Spacing is a little non-standard here. Fix up tabs vs. spaces. Signed-off-by: Brian Norris Acked-by: Huang Shijie Reviewed-by: Marek Vasut Change-Id: I3b31d8ffd4ce9c2be8a6e8d765f9ca829bc8d3d0 Reviewed-on: http://git.am.freescale.net:8181/15512 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 9428d28..a6e8719 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,55 +11,55 @@ #define __LINUX_MTD_SPI_NOR_H /* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ -#define OPCODE_RDCR 0x35 /* Read configuration register */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ +#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ +#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ /* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ +#define OPCODE_BP 0x02 /* Byte program */ +#define OPCODE_WRDI 0x04 /* Write disable */ +#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ /* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ +#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ +#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ /* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ +#define OPCODE_BRWR 0x17 /* Bank register write */ /* Status Register bits. */ -#define SR_WIP 1 /* Write in progress */ -#define SR_WEL 2 /* Write enable latch */ +#define SR_WIP 1 /* Write in progress */ +#define SR_WEL 2 /* Write enable latch */ /* meaning of other SR_* bits may differ between vendors */ -#define SR_BP0 4 /* Block protect 0 */ -#define SR_BP1 8 /* Block protect 1 */ -#define SR_BP2 0x10 /* Block protect 2 */ -#define SR_SRWD 0x80 /* SR write protect */ +#define SR_BP0 4 /* Block protect 0 */ +#define SR_BP1 8 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_SRWD 0x80 /* SR write protect */ -#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ /* Configuration Register bits. */ -#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ +#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ enum read_mode { SPI_NOR_NORMAL = 0, @@ -95,7 +95,7 @@ struct spi_nor_xfer_cfg { u8 dummy_cycles; }; -#define SPI_NOR_MAX_CMD_SIZE 8 +#define SPI_NOR_MAX_CMD_SIZE 8 enum spi_nor_ops { SPI_NOR_OPS_READ = 0, SPI_NOR_OPS_WRITE, -- cgit v0.10.2 From 81217b649e903255343064e03adf9908db59eac8 Mon Sep 17 00:00:00 2001 From: Nikhil Badola Date: Thu, 7 Aug 2014 17:19:51 +0530 Subject: arm : dts : ls1021a : Remove unwanted code from USB2.0 dts node Remove #address-cells and #size-cells from USB 2.0 node in ls1021a Signed-off-by: Nikhil Badola Change-Id: Ia2ff9aea201ef18b352437bda267571c235db689 Reviewed-on: http://git.am.freescale.net:8181/15675 Reviewed-by: Jingchang Lu Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index 9afe2b4..8c49ad4 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -543,8 +543,6 @@ usb@8600000 { compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr"; reg = <0x0 0x8600000 0x0 0x1000>; - #address-cells = <1>; - #size-cells = <0>; interrupts = ; dr_mode = "host"; phy_type = "ulpi"; -- cgit v0.10.2 From 3e9d7e053a10d91525920b9ce03e1579976f25d4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2013 13:57:23 -0700 Subject: hwmon: Introduce hwmon_device_register_with_groups hwmon_device_register_with_groups() lets callers register a hwmon device together with all sysfs attributes in a single call. When using hwmon_device_register_with_groups(), hwmon attributes are attached to the hwmon device directly and no longer with its parent device. Signed-off-by: Guenter Roeck (cherry picked from commit bab2243ce1897865e31ea6d59b0478391f51812b) Change-Id: I7612ae8cae84fd24122ca8fd91acba1a11784932 Reviewed-on: http://git.am.freescale.net:8181/19631 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 646314f..a982528 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,35 +26,122 @@ #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" -static struct class *hwmon_class; +struct hwmon_device { + const char *name; + struct device dev; +}; +#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *hwmon_dev_attrs[] = { + &dev_attr_name.attr, + NULL +}; + +static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (to_hwmon_device(dev)->name == NULL) + return 0; + + return attr->mode; +} + +static struct attribute_group hwmon_dev_attr_group = { + .attrs = hwmon_dev_attrs, + .is_visible = hwmon_dev_name_is_visible, +}; + +static const struct attribute_group *hwmon_dev_attr_groups[] = { + &hwmon_dev_attr_group, + NULL +}; + +static void hwmon_dev_release(struct device *dev) +{ + kfree(to_hwmon_device(dev)); +} + +static struct class hwmon_class = { + .name = "hwmon", + .owner = THIS_MODULE, + .dev_groups = hwmon_dev_attr_groups, + .dev_release = hwmon_dev_release, +}; static DEFINE_IDA(hwmon_ida); /** - * hwmon_device_register - register w/ hwmon - * @dev: the device to register + * hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create * * hwmon_device_unregister() must be called when the device is no * longer needed. * * Returns the pointer to the new device. */ -struct device *hwmon_device_register(struct device *dev) +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) { - struct device *hwdev; - int id; + struct hwmon_device *hwdev; + int err, id; id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); - hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, - HWMON_ID_FORMAT, id); + hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); + if (hwdev == NULL) { + err = -ENOMEM; + goto ida_remove; + } - if (IS_ERR(hwdev)) - ida_simple_remove(&hwmon_ida, id); + hwdev->name = name; + hwdev->dev.class = &hwmon_class; + hwdev->dev.parent = dev; + hwdev->dev.groups = groups; + hwdev->dev.of_node = dev ? dev->of_node : NULL; + dev_set_drvdata(&hwdev->dev, drvdata); + dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); + err = device_register(&hwdev->dev); + if (err) + goto free; + + return &hwdev->dev; + +free: + kfree(hwdev); +ida_remove: + ida_simple_remove(&hwmon_ida, id); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); - return hwdev; +/** + * hwmon_device_register - register w/ hwmon + * @dev: the device to register + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device *hwmon_device_register(struct device *dev) +{ + return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); } EXPORT_SYMBOL_GPL(hwmon_device_register); @@ -105,19 +193,21 @@ static void __init hwmon_pci_quirks(void) static int __init hwmon_init(void) { + int err; + hwmon_pci_quirks(); - hwmon_class = class_create(THIS_MODULE, "hwmon"); - if (IS_ERR(hwmon_class)) { - pr_err("couldn't create sysfs class\n"); - return PTR_ERR(hwmon_class); + err = class_register(&hwmon_class); + if (err) { + pr_err("couldn't register hwmon sysfs class\n"); + return err; } return 0; } static void __exit hwmon_exit(void) { - class_destroy(hwmon_class); + class_unregister(&hwmon_class); } subsys_initcall(hwmon_init); diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index b2514f7..6d02ff7 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -15,8 +15,13 @@ #define _HWMON_H_ struct device; +struct attribute_group; struct device *hwmon_device_register(struct device *dev); +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups); void hwmon_device_unregister(struct device *dev); -- cgit v0.10.2 From 4e9f0c4a3adf7800f06f3493cc6cd44ac7bef414 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 11 Jul 2013 20:00:12 -0700 Subject: hwmon: Provide managed hwmon registration Drivers using the new hwmon_device_register_with_groups API often have a remove function which consists solely of a call hwmon_device_unregister(). Provide support for devm_hwmon_device_register_with_groups and devm_hwmon_device_unregister to allow this repeated code to be removed and help eliminate error handling code. Signed-off-by: Guenter Roeck (cherry picked from commit 74188cba088192e14cd7fd5433876e8c947bcdd8) Change-Id: I7a2eca21e6a32bacbc55a979b630f10d19508abc Reviewed-on: http://git.am.freescale.net:8181/19632 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a982528..e176a43 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -163,6 +163,69 @@ void hwmon_device_unregister(struct device *dev) } EXPORT_SYMBOL_GPL(hwmon_device_unregister); +static void devm_hwmon_release(struct device *dev, void *res) +{ + struct device *hwdev = *(struct device **)res; + + hwmon_device_unregister(hwdev); +} + +/** + * devm_hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create + * + * Returns the pointer to the new device. The new device is automatically + * unregistered with the parent device. + */ +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) +{ + struct device **ptr, *hwdev; + + if (!dev) + return ERR_PTR(-EINVAL); + + ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); + if (IS_ERR(hwdev)) + goto error; + + *ptr = hwdev; + devres_add(dev, ptr); + return hwdev; + +error: + devres_free(ptr); + return hwdev; +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); + +static int devm_hwmon_match(struct device *dev, void *res, void *data) +{ + struct device **hwdev = res; + + return *hwdev == data; +} + +/** + * devm_hwmon_device_unregister - removes a previously registered hwmon device + * + * @dev: the parent device of the device to unregister + */ +void devm_hwmon_device_unregister(struct device *dev) +{ + WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); + static void __init hwmon_pci_quirks(void) { #if defined CONFIG_X86 && defined CONFIG_PCI diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 6d02ff7..09354f6 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -22,7 +22,12 @@ struct device * hwmon_device_register_with_groups(struct device *dev, const char *name, void *drvdata, const struct attribute_group **groups); +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups); void hwmon_device_unregister(struct device *dev); +void devm_hwmon_device_unregister(struct device *dev); #endif -- cgit v0.10.2 From 4606e1f5e8101e0c99b1fc4cedc41dfe77117420 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 28 Feb 2014 10:37:55 -0800 Subject: hwmon: Do not accept invalid name attributes hwmon name attributes must not include '-', as specified in Documentation/hwmon/sysfs-interface. Also filter out spaces, tabs, wildcards, and newline characters. Tested-by: Jean Delvare Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck (cherry picked from commit 648cd48c9e566f53c5df30d79857e0937ae13b09) Change-Id: Ib723f0a5304212bede9017bf63326aa2a59b6bc5 Reviewed-on: http://git.am.freescale.net:8181/19633 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index e176a43..a26c385 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -22,6 +22,7 @@ #include #include #include +#include #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -99,6 +100,10 @@ hwmon_device_register_with_groups(struct device *dev, const char *name, struct hwmon_device *hwdev; int err, id; + /* Do not accept invalid characters in hwmon name attribute */ + if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) + return ERR_PTR(-EINVAL); + id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); -- cgit v0.10.2 From 23ad92f65643b08c6f473a4083b350ad7cc501c5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 11 Jan 2014 10:28:05 -0800 Subject: hwmon: Driver for Linear Technologies LTC2945 LTC2945 is a system monitor that measures current, voltage, and power. Signed-off-by: Guenter Roeck (cherry picked from commit 6700ce035f830149d48c270d84736debfb67179e) Change-Id: I7d658c4e03f5e9108fbf1e407b3f1bdecda7ef1d Reviewed-on: http://git.am.freescale.net:8181/19634 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/hwmon/ltc2945 b/Documentation/hwmon/ltc2945 new file mode 100644 index 0000000..f8d0f7f --- /dev/null +++ b/Documentation/hwmon/ltc2945 @@ -0,0 +1,84 @@ +Kernel driver ltc2945 +===================== + +Supported chips: + * Linear Technology LTC2945 + Prefix: 'ltc2945' + Addresses scanned: - + Datasheet: + http://cds.linear.com/docs/en/datasheet/2945fa.pdf + +Author: Guenter Roeck + + +Description +----------- + +The LTC2945 is a rail-to-rail system monitor that measures current, voltage, +and power consumption. + + +Usage Notes +----------- + +This driver does not probe for LTC2945 devices, since there is no register +which can be safely used to identify the chip. You will have to instantiate +the devices explicitly. + +Example: the following will load the driver for an LTC2945 at address 0x10 +on I2C bus #1: +$ modprobe ltc2945 +$ echo ltc2945 0x10 > /sys/bus/i2c/devices/i2c-1/new_device + + +Sysfs entries +------------- + +Voltage readings provided by this driver are reported as obtained from the ADC +registers. If a set of voltage divider resistors is installed, calculate the +real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the +value of the divider resistor against the measured voltage and R2 is the value +of the divider resistor against Ground. + +Current reading provided by this driver is reported as obtained from the ADC +Current Sense register. The reported value assumes that a 1 mOhm sense resistor +is installed. If a different sense resistor is installed, calculate the real +current by dividing the reported value by the sense resistor value in mOhm. + +in1_input VIN voltage (mV). Voltage is measured either at + SENSE+ or VDD pin depending on chip configuration. +in1_min Undervoltage threshold +in1_max Overvoltage threshold +in1_lowest Lowest measured voltage +in1_highest Highest measured voltage +in1_reset_history Write 1 to reset in1 history +in1_min_alarm Undervoltage alarm +in1_max_alarm Overvoltage alarm + +in2_input ADIN voltage (mV) +in2_min Undervoltage threshold +in2_max Overvoltage threshold +in2_lowest Lowest measured voltage +in2_highest Highest measured voltage +in2_reset_history Write 1 to reset in2 history +in2_min_alarm Undervoltage alarm +in2_max_alarm Overvoltage alarm + +curr1_input SENSE current (mA) +curr1_min Undercurrent threshold +curr1_max Overcurrent threshold +curr1_lowest Lowest measured current +curr1_highest Highest measured current +curr1_reset_history Write 1 to reset curr1 history +curr1_min_alarm Undercurrent alarm +curr1_max_alarm Overcurrent alarm + +power1_input Power (in uW). Power is calculated based on SENSE+/VDD + voltage or ADIN voltage depending on chip configuration. +power1_min Low lower threshold +power1_max High power threshold +power1_input_lowest Historical minimum power use +power1_input_highest Historical maximum power use +power1_reset_history Write 1 to reset power1 history +power1_min_alarm Low power alarm +power1_max_alarm High power alarm diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b3ab9d4..4201c7e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -774,6 +774,18 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC2945 + tristate "Linear Technology LTC2945" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC2945 + I2C System Monitor. + + This driver can also be built as a module. If so, the module will + be called ltc2945. + config SENSORS_LTC4151 tristate "Linear Technology LTC4151" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index ec7cde0..eacd2ee 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95234) += lm95234.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o +obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c new file mode 100644 index 0000000..c104cc3 --- /dev/null +++ b/drivers/hwmon/ltc2945.c @@ -0,0 +1,519 @@ +/* + * Driver for Linear Technology LTC2945 I2C Power Monitor + * + * Copyright (c) 2014 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* chip registers */ +#define LTC2945_CONTROL 0x00 +#define LTC2945_ALERT 0x01 +#define LTC2945_STATUS 0x02 +#define LTC2945_FAULT 0x03 +#define LTC2945_POWER_H 0x05 +#define LTC2945_MAX_POWER_H 0x08 +#define LTC2945_MIN_POWER_H 0x0b +#define LTC2945_MAX_POWER_THRES_H 0x0e +#define LTC2945_MIN_POWER_THRES_H 0x11 +#define LTC2945_SENSE_H 0x14 +#define LTC2945_MAX_SENSE_H 0x16 +#define LTC2945_MIN_SENSE_H 0x18 +#define LTC2945_MAX_SENSE_THRES_H 0x1a +#define LTC2945_MIN_SENSE_THRES_H 0x1c +#define LTC2945_VIN_H 0x1e +#define LTC2945_MAX_VIN_H 0x20 +#define LTC2945_MIN_VIN_H 0x22 +#define LTC2945_MAX_VIN_THRES_H 0x24 +#define LTC2945_MIN_VIN_THRES_H 0x26 +#define LTC2945_ADIN_H 0x28 +#define LTC2945_MAX_ADIN_H 0x2a +#define LTC2945_MIN_ADIN_H 0x2c +#define LTC2945_MAX_ADIN_THRES_H 0x2e +#define LTC2945_MIN_ADIN_THRES_H 0x30 +#define LTC2945_MIN_ADIN_THRES_L 0x31 + +/* Fault register bits */ + +#define FAULT_ADIN_UV (1 << 0) +#define FAULT_ADIN_OV (1 << 1) +#define FAULT_VIN_UV (1 << 2) +#define FAULT_VIN_OV (1 << 3) +#define FAULT_SENSE_UV (1 << 4) +#define FAULT_SENSE_OV (1 << 5) +#define FAULT_POWER_UV (1 << 6) +#define FAULT_POWER_OV (1 << 7) + +/* Control register bits */ + +#define CONTROL_MULT_SELECT (1 << 0) +#define CONTROL_TEST_MODE (1 << 4) + +static inline bool is_power_reg(u8 reg) +{ + return reg < LTC2945_SENSE_H; +} + +/* Return the value from the given register in uW, mV, or mA */ +static long long ltc2945_reg_to_val(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + u8 buf[3]; + long long val; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, + is_power_reg(reg) ? 3 : 2); + if (ret < 0) + return ret; + + if (is_power_reg(reg)) { + /* power */ + val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + } else { + /* current, voltage */ + val = (buf[0] << 4) + (buf[1] >> 4); + } + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to uW by assuming current is measured with + * an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val *= 625LL; + } else { + /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ + val = (val * 25LL) >> 1; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. Convert to mV. */ + val *= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. Convert to mV. */ + val = val >> 1; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val *= 25; + break; + default: + return -EINVAL; + } + return val; +} + +static int ltc2945_val_to_reg(struct device *dev, u8 reg, + unsigned long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + int ret; + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to register value by assuming current is measured + * with an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power, which in turn + * determines register calculations. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val = DIV_ROUND_CLOSEST(val, 625); + } else { + /* + * 0.5 mV * 25 uV = 0.0125 uV resolution. + * Divide first to avoid overflow; + * accept loss of accuracy. + */ + val = DIV_ROUND_CLOSEST(val, 25) * 2; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. */ + val /= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. */ + val *= 2; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = DIV_ROUND_CLOSEST(val, 25); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc2945_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + long long value; + + value = ltc2945_reg_to_val(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%lld\n", value); +} + +static ssize_t ltc2945_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + unsigned long val; + u8 regbuf[3]; + int num_regs; + int regval; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + /* convert to register value, then clamp and write result */ + regval = ltc2945_val_to_reg(dev, reg, val); + if (is_power_reg(reg)) { + regval = clamp_val(regval, 0, 0xffffff); + regbuf[0] = regval >> 16; + regbuf[1] = (regval >> 8) & 0xff; + regbuf[2] = regval; + num_regs = 3; + } else { + regval = clamp_val(regval, 0, 0xfff) << 4; + regbuf[0] = regval >> 8; + regbuf[1] = regval & 0xff; + num_regs = 2; + } + ret = regmap_bulk_write(regmap, reg, regbuf, num_regs); + return ret < 0 ? ret : count; +} + +static ssize_t ltc2945_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + int num_regs = is_power_reg(reg) ? 3 : 2; + u8 buf_min[3] = { 0xff, 0xff, 0xff }; + u8 buf_max[3] = { 0, 0, 0 }; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val != 1) + return -EINVAL; + + ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, + CONTROL_TEST_MODE); + + /* Reset minimum */ + ret = regmap_bulk_write(regmap, reg, buf_min, num_regs); + if (ret) + return ret; + + switch (reg) { + case LTC2945_MIN_POWER_H: + reg = LTC2945_MAX_POWER_H; + break; + case LTC2945_MIN_SENSE_H: + reg = LTC2945_MAX_SENSE_H; + break; + case LTC2945_MIN_VIN_H: + reg = LTC2945_MAX_VIN_H; + break; + case LTC2945_MIN_ADIN_H: + reg = LTC2945_MAX_ADIN_H; + break; + default: + BUG(); + break; + } + /* Reset maximum */ + ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); + + /* Try resetting test mode even if there was an error */ + regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0); + + return ret ? : count; +} + +static ssize_t ltc2945_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC2945_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Input voltages */ + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_VIN_H); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_VIN_H); +static SENSOR_DEVICE_ATTR(in1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_VIN_H); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_VIN_H); + +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_ADIN_H); + +/* Voltage alarms */ + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_OV); + +/* Currents (via sense resistor) */ + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_SENSE_H); + +/* Current alarms */ + +static SENSOR_DEVICE_ATTR(curr1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_UV); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_OV); + +/* Power */ + +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_POWER_H); +static SENSOR_DEVICE_ATTR(power1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_input_lowest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MIN_POWER_H); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MAX_POWER_H); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_POWER_H); + +/* Power alarms */ + +static SENSOR_DEVICE_ATTR(power1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_UV); +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_OV); + +static struct attribute *ltc2945_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_lowest.dev_attr.attr, + &sensor_dev_attr_in1_highest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_lowest.dev_attr.attr, + &sensor_dev_attr_in2_highest.dev_attr.attr, + &sensor_dev_attr_in2_reset_history.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_min.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_lowest.dev_attr.attr, + &sensor_dev_attr_curr1_highest.dev_attr.attr, + &sensor_dev_attr_curr1_reset_history.dev_attr.attr, + &sensor_dev_attr_curr1_min_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_min.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_input_lowest.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_min_alarm.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc2945); + +static struct regmap_config ltc2945_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC2945_MIN_ADIN_THRES_L, +}; + +static int ltc2945_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC2945_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc2945_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc2945_id[] = { + {"ltc2945", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc2945_id); + +static struct i2c_driver ltc2945_driver = { + .driver = { + .name = "ltc2945", + }, + .probe = ltc2945_probe, + .id_table = ltc2945_id, +}; + +module_i2c_driver(ltc2945_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("LTC2945 driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From bd7702f31675f02e8554ab326eacc3b89ad2058a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 21 Apr 2014 10:38:08 -0700 Subject: hwmon: (ltc2945) Don't crash the kernel unnecessarily An implementation error should not crash the kernel if it is avoidable. Replace BUG() with WARN_ONCE(). Signed-off-by: Guenter Roeck (cherry picked from commit f75d72309192e52ef9a3efb390b1c4f408c142df) Change-Id: Ib1d20bb2f35fd6f5c551f137102dbd12c68aa489 Reviewed-on: http://git.am.freescale.net:8181/19635 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index c104cc3..c9cddf5 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -1,4 +1,4 @@ -/* + /* * Driver for Linear Technology LTC2945 I2C Power Monitor * * Copyright (c) 2014 Guenter Roeck @@ -314,8 +314,8 @@ static ssize_t ltc2945_reset_history(struct device *dev, reg = LTC2945_MAX_ADIN_H; break; default: - BUG(); - break; + WARN_ONCE(1, "Bad register: 0x%x\n", reg); + return -EINVAL; } /* Reset maximum */ ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); -- cgit v0.10.2 From d2a4798b5737926ee839ae6e70a59811601071e0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 6 May 2014 16:51:36 -0700 Subject: hwmon: (ltc2945) Fix 1st comment line Somehow a couple of spaces got added to the first line. Remove them. No code change. Signed-off-by: Guenter Roeck (cherry picked from commit 86b89d73f9f3648c9f3b375d7841bef18a27fd2a) Change-Id: Ic8011315767d9979151c08fc22e483eb01ae1f3b Reviewed-on: http://git.am.freescale.net:8181/19636 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index c9cddf5..3701b32 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -1,4 +1,4 @@ - /* +/* * Driver for Linear Technology LTC2945 I2C Power Monitor * * Copyright (c) 2014 Guenter Roeck -- cgit v0.10.2 From 2837afbc45893c37e0ae28d653209da6937fa952 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Apr 2014 10:24:42 +0800 Subject: regmap: mmio: add regmap_mmio_{regsize, count}_check. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 41b0c2c976a8758a2b7f5b14cbc5d1a7436932cc Change-Id: I8446ffd9118e759cb68784dd0ee59e9382c698ec Reviewed-on: http://git.am.freescale.net:8181/19672 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 98745dd..3eddd76 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -30,6 +30,16 @@ struct regmap_mmio_context { struct clk *clk; }; +static inline void regmap_mmio_regsize_check(size_t reg_size) +{ + BUG_ON(reg_size != 4); +} + +static inline void regmap_mmio_count_check(size_t count) +{ + BUG_ON(count < 4); +} + static int regmap_mmio_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) @@ -38,7 +48,7 @@ static int regmap_mmio_gather_write(void *context, u32 offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (ctx->clk) { ret = clk_enable(ctx->clk); @@ -81,7 +91,7 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { - BUG_ON(count < 4); + regmap_mmio_count_check(count); return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); } @@ -94,7 +104,7 @@ static int regmap_mmio_read(void *context, u32 offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (ctx->clk) { ret = clk_enable(ctx->clk); -- cgit v0.10.2 From aba9bc9b6a99f4bde2ab22f42feff4c1de8aea50 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Apr 2014 10:27:16 +0800 Subject: regmap: mmio: Add support for 1/2/8 bytes wide register address. Since regmap core and mmio have already support for 1/2/8 bytes wide values, so adds support for 1/2/8 bytes wide registers address. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 932580409a9dacbf42215fa737bf06ae2c0aa624 Change-Id: I148882774797f7a53d6123d0b5dd757231025b6e Reviewed-on: http://git.am.freescale.net:8181/19673 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 3eddd76..c76f278 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -26,18 +26,30 @@ struct regmap_mmio_context { void __iomem *regs; + unsigned reg_bytes; unsigned val_bytes; + unsigned pad_bytes; struct clk *clk; }; static inline void regmap_mmio_regsize_check(size_t reg_size) { - BUG_ON(reg_size != 4); + switch (reg_size) { + case 1: + case 2: + case 4: +#ifdef CONFIG_64BIT + case 8: +#endif + break; + default: + BUG(); + } } static inline void regmap_mmio_count_check(size_t count) { - BUG_ON(count < 4); + BUG_ON(count % 2 != 0); } static int regmap_mmio_gather_write(void *context, @@ -91,9 +103,13 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { + struct regmap_mmio_context *ctx = context; + u32 offset = ctx->reg_bytes + ctx->pad_bytes; + regmap_mmio_count_check(count); - return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); + return regmap_mmio_gather_write(context, data, ctx->reg_bytes, + data + offset, count - offset); } static int regmap_mmio_read(void *context, @@ -219,6 +235,8 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + ctx->reg_bytes = config->reg_bits / 8; + ctx->pad_bytes = config->pad_bits / 8; if (clk_id == NULL) return ctx; -- cgit v0.10.2 From d641a9f768d7b1312a6ee85226512daddd7c29a1 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 22 May 2014 14:42:02 +0800 Subject: regmap: mmio: Fix the bug of 'offset' value parsing. 'offset = *(u32 *)reg;' This will be okey for 32/64-bits register device, but for 8/16-bits register ones, the 'offset' value will overflow, for example: The IMX2 Watchdog, whose registers and values are all 16-bits: If the IO base virtual address is ctx->regs = 0x888c0000, and the now doing the 0x00 register accessing: Using 'offset = *(u32 *)reg' the offset value will possiblly be 0x77310000, Using 'offset = *(u16 *)reg' the offset value will be 0x0000. In the regmap_mmio_gather_write(), ctx->regs + 0x7731000 will be 0xffbd0000, but actually it should be ctx->regs + 0x0000 = 0x888c0000. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 88cb32c657ed13dc29561d0f4aa154e0fd25759f Change-Id: Iddbdf33a3831062b250dfdc1e2067712d1153182 Reviewed-on: http://git.am.freescale.net:8181/19674 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index c76f278..b9da278 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -52,12 +52,31 @@ static inline void regmap_mmio_count_check(size_t count) BUG_ON(count % 2 != 0); } +static inline unsigned int +regmap_mmio_get_offset(const void *reg, size_t reg_size) +{ + switch (reg_size) { + case 1: + return *(u8 *)reg; + case 2: + return *(u16 *)reg; + case 4: + return *(u32 *)reg; +#ifdef CONFIG_64BIT + case 8: + return *(u64 *)reg; +#endif + default: + BUG(); + } +} + static int regmap_mmio_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { struct regmap_mmio_context *ctx = context; - u32 offset; + unsigned int offset; int ret; regmap_mmio_regsize_check(reg_size); @@ -68,7 +87,7 @@ static int regmap_mmio_gather_write(void *context, return ret; } - offset = *(u32 *)reg; + offset = regmap_mmio_get_offset(reg, reg_size); while (val_size) { switch (ctx->val_bytes) { @@ -104,7 +123,7 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { struct regmap_mmio_context *ctx = context; - u32 offset = ctx->reg_bytes + ctx->pad_bytes; + unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; regmap_mmio_count_check(count); @@ -117,7 +136,7 @@ static int regmap_mmio_read(void *context, void *val, size_t val_size) { struct regmap_mmio_context *ctx = context; - u32 offset; + unsigned int offset; int ret; regmap_mmio_regsize_check(reg_size); @@ -128,7 +147,7 @@ static int regmap_mmio_read(void *context, return ret; } - offset = *(u32 *)reg; + offset = regmap_mmio_get_offset(reg, reg_size); while (val_size) { switch (ctx->val_bytes) { -- cgit v0.10.2 From 9221a86587283541a8709c3431c1b95cf5c8be4c Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Apr 2014 10:28:05 +0800 Subject: regmap: mmio: Add regmap_mmio_regbits_check. Fix the support for 1/2/8 bytes wide register address checking. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 451485ba6bfbed36220b9e710fca0525f62e771d Change-Id: I3950b3721b3998d3cdb7e6cac69ec0b4a65c3efb Reviewed-on: http://git.am.freescale.net:8181/19675 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index b9da278..1f95f8b 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -47,6 +47,21 @@ static inline void regmap_mmio_regsize_check(size_t reg_size) } } +static int regmap_mmio_regbits_check(size_t reg_bits) +{ + switch (reg_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + return 0; + default: + return -EINVAL; + } +} + static inline void regmap_mmio_count_check(size_t count) { BUG_ON(count % 2 != 0); @@ -210,8 +225,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, int min_stride; int ret; - if (config->reg_bits != 32) - return ERR_PTR(-EINVAL); + ret = regmap_mmio_regbits_check(config->reg_bits); + if (ret) + return ERR_PTR(ret); if (config->pad_bits) return ERR_PTR(-EINVAL); -- cgit v0.10.2 From 253b8198b9d356660520a1bcac52db86059befe5 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 4 Jun 2014 17:24:12 +0800 Subject: gianfar: Fix the section mismatch warnings. Building with CONFIG_DEBUG_SECTION_MISMATCH enabled, the following WARNING is occured: LD drivers/net/built-in.o WARNING: drivers/net/built-in.o(.text+0xcd4c): Section mismatch in reference from the function gfar_probe() to the function .init.text:gfar_init_addr_hash_table() The function gfar_probe() references the function __init gfar_init_addr_hash_table(). This is often because gfar_probe lacks a __init annotation or the annotation of gfar_init_addr_hash_table is wrong. Signed-off-by: Xiubo Li Signed-off-by: David S. Miller --- This patch is pulled back from upstream: commit 898157ed7473683d515532f9f14bfd2f7743ccd2 Change-Id: If9e87dfccedd8d3eb9e0467ad14b7a940b17249f Reviewed-on: http://git.am.freescale.net:8181/19676 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 5584c39..a4d5a88 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1276,7 +1276,7 @@ static void gfar_hw_init(struct gfar_private *priv) gfar_write_isrg(priv); } -static void __init gfar_init_addr_hash_table(struct gfar_private *priv) +static void gfar_init_addr_hash_table(struct gfar_private *priv) { struct gfar __iomem *regs = priv->gfargrp[0].regs; -- cgit v0.10.2 From b57e074914feb2bfd12b40630b62ccdf3356ac0e Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 24 Sep 2014 13:48:47 +0800 Subject: watchdog: imx2_wdt: disable watchdog timer during low power mode We should set watchdog timer to be disabled in low power mode, as there is no service running in background, otherwise, system will reset unexpected. Signed-off-by: Anson Huang Acked-by: Shawn Guo Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- This patch is pulled back from upstream: commit 1a9c5efa576eccadd2836a1e53dcea21f999c180 Change-Id: I17444286a483479bc47c4e2e449881d8bac1c221 Reviewed-on: http://git.am.freescale.net:8181/19714 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 693ac3f..e5bde40 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -2,6 +2,7 @@ * Watchdog driver for IMX2 and later processors * * Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. + * Copyright (C) 2014 Freescale Semiconductor, Inc. * * some parts adapted by similar drivers from Darius Augulis and Vladimir * Zapolskiy, additional improvements by Wim Van Sebroeck. @@ -40,6 +41,7 @@ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ #define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ #define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ +#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */ #define IMX2_WDT_WSR 0x02 /* Service Register */ #define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */ @@ -87,6 +89,8 @@ static inline void imx2_wdt_setup(void) { u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); + /* Suspend timer in low power mode, write once-only */ + val |= IMX2_WDT_WCR_WDZST; /* Strip the old watchdog Time-Out value */ val &= ~IMX2_WDT_WCR_WT; /* Generate reset if WDOG times out */ -- cgit v0.10.2 From 84a90dcd5f9a890e924defd74f7b0fa43f49af21 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Apr 2014 10:29:02 +0800 Subject: watchdog: imx2_wdt: Sort the header files alphabetically Signed-off-by: Xiubo Li Reviewed-by: Guenter Roeck Acked-by: Shawn Guo Signed-off-by: Wim Van Sebroeck --- This patch is pulled back from upstream: commit 30cb042a846353929042d93d13c9f8e1e5227aa7 Change-Id: I1135180b413b9b194a54449aa276b1e64c88248b Reviewed-on: http://git.am.freescale.net:8181/19715 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index e5bde40..4da339d 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -21,19 +21,19 @@ * Halt on suspend: Manual Can be automatic */ +#include +#include #include +#include +#include #include #include #include #include #include -#include -#include -#include -#include -#include #include -#include +#include +#include #define DRIVER_NAME "imx2-wdt" -- cgit v0.10.2 From 8785399fdb1d63813ff68acb2a11fc4927361df3 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Apr 2014 10:29:41 +0800 Subject: watchdog: imx2_wdt: convert to use regmap API. This watchdog driver will be working on IMX2+, Vybrid, LS1, LS2+ platforms, and will be in different endianness mode in those SoCs: SoCs CPU endian mode WDT endian mode ------------------------------------------------ IMX2+ LE LE Vybird LE LE LS1 LE BE LS2 LE LE Other possible SoCs: SoCs CPU endian mode WDT endian mode ------------------------------------------------ Soc1 BE BE Soc2 BE LE And also the watchdog's registers will be 32-bits for some versions, and though it is 16-bits in IMX2+, Vybird and LS+. Using the regmap APIs, could be more easy to support different endianness and also more easy to support 32-bits version... Signed-off-by: Xiubo Li Reviewed-by: Guenter Roeck Acked-by: Shawn Guo Signed-off-by: Wim Van Sebroeck --- This patch is pulled back from upstream: commit a7977003293ed0c13e62d95fc8cd1d20e22b7282 Change-Id: I3dbc53e41d656ba039e6fa5b1a0aacfdb6021446 Reviewed-on: http://git.am.freescale.net:8181/19716 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b27b75d..f194e81 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -359,6 +359,7 @@ config MAX63XX_WATCHDOG config IMX2_WDT tristate "IMX2+ Watchdog" depends on ARCH_MXC + select REGMAP_MMIO help This is the driver for the hardware watchdog on the Freescale IMX2 and later processors. diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 4da339d..f5d33d9 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,7 @@ static struct { struct clk *clk; - void __iomem *base; + struct regmap *regmap; unsigned timeout; unsigned long status; struct timer_list timer; /* Pings the watchdog when closed */ @@ -87,7 +88,9 @@ static const struct watchdog_info imx2_wdt_info = { static inline void imx2_wdt_setup(void) { - u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); + u32 val; + + regmap_read(imx2_wdt.regmap, IMX2_WDT_WCR, &val); /* Suspend timer in low power mode, write once-only */ val |= IMX2_WDT_WCR_WDZST; @@ -100,17 +103,17 @@ static inline void imx2_wdt_setup(void) /* Set the watchdog's Time-Out value */ val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout); - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_write(imx2_wdt.regmap, IMX2_WDT_WCR, val); /* enable the watchdog */ val |= IMX2_WDT_WCR_WDE; - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_write(imx2_wdt.regmap, IMX2_WDT_WCR, val); } static inline void imx2_wdt_ping(void) { - __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR); - __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR); + regmap_write(imx2_wdt.regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1); + regmap_write(imx2_wdt.regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2); } static void imx2_wdt_timer_ping(unsigned long arg) @@ -143,12 +146,8 @@ static void imx2_wdt_stop(void) static void imx2_wdt_set_timeout(int new_timeout) { - u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); - - /* set the new timeout value in the WSR */ - val &= ~IMX2_WDT_WCR_WT; - val |= WDOG_SEC_TO_COUNT(new_timeout); - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_update_bits(imx2_wdt.regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, + WDOG_SEC_TO_COUNT(new_timeout)); } static int imx2_wdt_open(struct inode *inode, struct file *file) @@ -181,7 +180,7 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd, void __user *argp = (void __user *)arg; int __user *p = argp; int new_value; - u16 val; + u32 val; switch (cmd) { case WDIOC_GETSUPPORT: @@ -192,7 +191,7 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd, return put_user(0, p); case WDIOC_GETBOOTSTATUS: - val = __raw_readw(imx2_wdt.base + IMX2_WDT_WRSR); + regmap_read(imx2_wdt.regmap, IMX2_WDT_WRSR, &val); new_value = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; return put_user(new_value, p); @@ -255,15 +254,30 @@ static struct miscdevice imx2_wdt_miscdev = { .fops = &imx2_wdt_fops, }; +static struct regmap_config imx2_wdt_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x8, +}; + static int __init imx2_wdt_probe(struct platform_device *pdev) { - int ret; struct resource *res; + void __iomem *base; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx2_wdt.base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(imx2_wdt.base)) - return PTR_ERR(imx2_wdt.base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + imx2_wdt.regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &imx2_wdt_regmap_config); + if (IS_ERR(imx2_wdt.regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(imx2_wdt.regmap); + } imx2_wdt.clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(imx2_wdt.clk)) { -- cgit v0.10.2 From c749d79c70ab4133baa33092513be5ef8ad0abf6 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 3 Jun 2014 14:38:05 +0800 Subject: watchdog: imx2_wdt: adds big endianness support. This watchdog driver will be working on IMX2+, Vybrid, LS1, LS2+ platforms, and will be in different endianness mode in those SoCs: SoCs WDT endian mode ------------------------------------ IMX2+ LE Vybird LE LS1 BE LS2 LE Signed-off-by: Xiubo Li Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- This patch is pulled back from upstream: commit f728f4bfc495a588abda4661c09595112677be25 Change-Id: I679a13328e7ee02fbc23dad99a3e672c6186fc4c Reviewed-on: http://git.am.freescale.net:8181/19717 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt index 2144af1..e52ba2d 100644 --- a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt @@ -5,10 +5,15 @@ Required properties: - reg : Should contain WDT registers location and length - interrupts : Should contain WDT interrupt +Optional property: +- big-endian: If present the watchdog device's registers are implemented + in big endian mode, otherwise in little mode. + Examples: wdt@73f98000 { compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; reg = <0x73f98000 0x4000>; interrupts = <58>; + big-endian; }; diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index f5d33d9..73b58bb 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -263,10 +264,14 @@ static struct regmap_config imx2_wdt_regmap_config = { static int __init imx2_wdt_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct resource *res; void __iomem *base; int ret; + if (of_property_read_bool(np, "big-endian")) + imx2_wdt_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) -- cgit v0.10.2 From 316b16840b4d7b96aa7b173599bf1669950fc2b2 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 17 Dec 2013 11:24:38 +0800 Subject: ASoC: Add SAI SoC Digital Audio Interface driver. This adds Freescale SAI ASoC Audio support. This implementation is only compatible with device tree definition. Features: o Supports playback/capture o Supports 16/20/24 bit PCM o Supports 8k - 96k sample rates o Supports master and slave mode. Signed-off-by: Alison Wang Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 4355082149429d1f87b6fbfc3ebc6305a5372ce2 Change-Id: Id2c5064a47530ea7cf3e1442267efcbc8bef0f06 Reviewed-on: http://git.am.freescale.net:8181/19738 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index b7ab71f..ac4fe4e 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,3 +1,7 @@ +config SND_SOC_FSL_SAI + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + config SND_SOC_FSL_SSI tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8db705b..aaccbee 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,11 +10,13 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o snd-soc-p1022-rdk-objs := p1022_rdk.o obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o -# Freescale PowerPC SSI/DMA Platform Support +# Freescale SSI/DMA/SAI/SPDIF Support +snd-soc-fsl-sai-objs := fsl_sai.o snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o +obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c new file mode 100644 index 0000000..1cd3e7a --- /dev/null +++ b/sound/soc/fsl/fsl_sai.c @@ -0,0 +1,493 @@ +/* + * Freescale ALSA SoC Digital Audio Interface (SAI) driver. + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_sai.h" + +static inline u32 sai_readl(struct fsl_sai *sai, + const void __iomem *addr) +{ + u32 val; + + val = __raw_readl(addr); + + if (likely(sai->big_endian_regs)) + val = be32_to_cpu(val); + else + val = le32_to_cpu(val); + rmb(); + + return val; +} + +static inline void sai_writel(struct fsl_sai *sai, + u32 val, void __iomem *addr) +{ + wmb(); + if (likely(sai->big_endian_regs)) + val = cpu_to_be32(val); + else + val = cpu_to_le32(val); + + __raw_writel(val, addr); +} + +static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int fsl_dir) +{ + u32 val_cr2, reg_cr2; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (fsl_dir == FSL_FMT_TRANSMITTER) + reg_cr2 = FSL_SAI_TCR2; + else + reg_cr2 = FSL_SAI_RCR2; + + val_cr2 = sai_readl(sai, sai->base + reg_cr2); + switch (clk_id) { + case FSL_SAI_CLK_BUS: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_BUS; + break; + case FSL_SAI_CLK_MAST1: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1; + break; + case FSL_SAI_CLK_MAST2: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2; + break; + case FSL_SAI_CLK_MAST3: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3; + break; + default: + return -EINVAL; + } + sai_writel(sai, val_cr2, sai->base + reg_cr2); + + return 0; +} + +static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + int ret; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); + sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); + sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); + sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's transmitter sysclk: %d\n", + ret); + return ret; + } + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_RECEIVER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's receiver sysclk: %d\n", + ret); + return ret; + } + + clk_disable_unprepare(sai->clk); + + return 0; +} + +static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, + unsigned int fmt, int fsl_dir) +{ + u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (fsl_dir == FSL_FMT_TRANSMITTER) { + reg_cr2 = FSL_SAI_TCR2; + reg_cr3 = FSL_SAI_TCR3; + reg_cr4 = FSL_SAI_TCR4; + } else { + reg_cr2 = FSL_SAI_RCR2; + reg_cr3 = FSL_SAI_RCR3; + reg_cr4 = FSL_SAI_RCR4; + } + + val_cr2 = sai_readl(sai, sai->base + reg_cr2); + val_cr3 = sai_readl(sai, sai->base + reg_cr3); + val_cr4 = sai_readl(sai, sai->base + reg_cr4); + + if (sai->big_endian_data) + val_cr4 |= FSL_SAI_CR4_MF; + else + val_cr4 &= ~FSL_SAI_CR4_MF; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val_cr4 |= FSL_SAI_CR4_FSE; + val_cr4 |= FSL_SAI_CR4_FSP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + val_cr4 |= FSL_SAI_CR4_FSP; + val_cr2 &= ~FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_IB_NF: + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr2 &= ~FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_IF: + val_cr4 |= FSL_SAI_CR4_FSP; + val_cr2 |= FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_NF: + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr2 |= FSL_SAI_CR2_BCP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + default: + return -EINVAL; + } + + val_cr3 |= FSL_SAI_CR3_TRCE; + + if (fsl_dir == FSL_FMT_RECEIVER) + val_cr2 |= FSL_SAI_CR2_SYNC; + + sai_writel(sai, val_cr2, sai->base + reg_cr2); + sai_writel(sai, val_cr3, sai->base + reg_cr3); + sai_writel(sai, val_cr4, sai->base + reg_cr4); + + return 0; +} + +static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + int ret; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's transmitter format: %d\n", + ret); + return ret; + } + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's receiver format: %d\n", + ret); + return ret; + } + + clk_disable_unprepare(sai->clk); + + return 0; +} + +static int fsl_sai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr, word_width; + unsigned int channels = params_channels(params); + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg_cr4 = FSL_SAI_TCR4; + reg_cr5 = FSL_SAI_TCR5; + reg_mr = FSL_SAI_TMR; + } else { + reg_cr4 = FSL_SAI_RCR4; + reg_cr5 = FSL_SAI_RCR5; + reg_mr = FSL_SAI_RMR; + } + + val_cr4 = sai_readl(sai, sai->base + reg_cr4); + val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; + val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; + + val_cr5 = sai_readl(sai, sai->base + reg_cr5); + val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; + val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + word_width = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + word_width = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + word_width = 24; + break; + default: + return -EINVAL; + } + + val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + val_cr5 |= FSL_SAI_CR5_WNW(word_width); + val_cr5 |= FSL_SAI_CR5_W0W(word_width); + + if (sai->big_endian_data) + val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); + else + val_cr5 |= FSL_SAI_CR5_FBT(0); + + val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + if (channels == 2 || channels == 1) + val_mr = ~0UL - ((1 << channels) - 1); + else + return -EINVAL; + + sai_writel(sai, val_cr4, sai->base + reg_cr4); + sai_writel(sai, val_cr5, sai->base + reg_cr5); + sai_writel(sai, val_mr, sai->base + reg_mr); + + return 0; +} + +static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int tcsr, rcsr; + + tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); + rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tcsr |= FSL_SAI_CSR_FRDE; + rcsr &= ~FSL_SAI_CSR_FRDE; + } else { + rcsr |= FSL_SAI_CSR_FRDE; + tcsr &= ~FSL_SAI_CSR_FRDE; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + tcsr |= FSL_SAI_CSR_TERE; + rcsr |= FSL_SAI_CSR_TERE; + sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { + tcsr &= ~FSL_SAI_CSR_TERE; + rcsr &= ~FSL_SAI_CSR_TERE; + } + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_sai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + int ret; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + ret = clk_prepare_enable(sai->clk); + + return ret; +} + +static void fsl_sai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + clk_disable_unprepare(sai->clk); +} + +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt, + .hw_params = fsl_sai_hw_params, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, + .shutdown = fsl_sai_shutdown, +}; + +static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); + + cpu_dai->playback_dma_data = &sai->dma_params_tx; + cpu_dai->capture_dma_data = &sai->dma_params_rx; + + snd_soc_dai_set_drvdata(cpu_dai, sai); + + return 0; +} + +static int fsl_sai_dai_remove(struct snd_soc_dai *cpu_dai) +{ + cpu_dai->playback_dma_data = NULL; + cpu_dai->capture_dma_data = NULL; + + snd_soc_dai_set_drvdata(cpu_dai, NULL); + + return 0; +} + +static struct snd_soc_dai_driver fsl_sai_dai = { + .probe = fsl_sai_dai_probe, + .remove = fsl_sai_dai_remove, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_component = { + .name = "fsl-sai", +}; + +static int fsl_sai_probe(struct platform_device *pdev) +{ + int ret; + struct fsl_sai *sai; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); + + sai->clk = devm_clk_get(&pdev->dev, "sai"); + if (IS_ERR(sai->clk)) { + dev_err(&pdev->dev, "Cannot get SAI's clock\n"); + return PTR_ERR(sai->clk); + } + + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; + sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; + sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; + sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; + + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + + platform_set_drvdata(pdev, sai); + + ret = snd_soc_register_component(&pdev->dev, &fsl_component, + &fsl_sai_dai, 1); + if (ret) + return ret; + + ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + if (ret) + snd_soc_unregister_component(&pdev->dev); + + return ret; +} + +static int fsl_sai_remove(struct platform_device *pdev) +{ + snd_dmaengine_pcm_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct of_device_id fsl_sai_ids[] = { + { .compatible = "fsl,vf610-sai", }, + { /* sentinel */ } +}; + +static struct platform_driver fsl_sai_driver = { + .probe = fsl_sai_probe, + .remove = fsl_sai_remove, + + .driver = { + .name = "fsl-sai", + .owner = THIS_MODULE, + .of_match_table = fsl_sai_ids, + }, +}; +module_platform_driver(fsl_sai_driver); + +MODULE_DESCRIPTION("Freescale Soc SAI Interface"); +MODULE_AUTHOR("Xiubo Li, "); +MODULE_ALIAS("platform:fsl-sai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h new file mode 100644 index 0000000..41bb62e --- /dev/null +++ b/sound/soc/fsl/fsl_sai.h @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, 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. + */ + +#ifndef __FSL_SAI_H +#define __FSL_SAI_H + +#include + +#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +/* SAI Transmit/Recieve Control Register */ +#define FSL_SAI_TCSR 0x00 +#define FSL_SAI_RCSR 0x80 +#define FSL_SAI_CSR_TERE BIT(31) +#define FSL_SAI_CSR_FWF BIT(17) +#define FSL_SAI_CSR_FRIE BIT(8) +#define FSL_SAI_CSR_FRDE BIT(0) + +/* SAI Transmit Data/FIFO/MASK Register */ +#define FSL_SAI_TDR 0x20 +#define FSL_SAI_TFR 0x40 +#define FSL_SAI_TMR 0x60 + +/* SAI Recieve Data/FIFO/MASK Register */ +#define FSL_SAI_RDR 0xa0 +#define FSL_SAI_RFR 0xc0 +#define FSL_SAI_RMR 0xe0 + +/* SAI Transmit and Recieve Configuration 1 Register */ +#define FSL_SAI_TCR1 0x04 +#define FSL_SAI_RCR1 0x84 + +/* SAI Transmit and Recieve Configuration 2 Register */ +#define FSL_SAI_TCR2 0x08 +#define FSL_SAI_RCR2 0x88 +#define FSL_SAI_CR2_SYNC BIT(30) +#define FSL_SAI_CR2_MSEL_MASK (0xff << 26) +#define FSL_SAI_CR2_MSEL_BUS 0 +#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) +#define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) +#define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) +#define FSL_SAI_CR2_BCP BIT(25) +#define FSL_SAI_CR2_BCD_MSTR BIT(24) + +/* SAI Transmit and Recieve Configuration 3 Register */ +#define FSL_SAI_TCR3 0x0c +#define FSL_SAI_RCR3 0x8c +#define FSL_SAI_CR3_TRCE BIT(16) +#define FSL_SAI_CR3_WDFL(x) (x) +#define FSL_SAI_CR3_WDFL_MASK 0x1f + +/* SAI Transmit and Recieve Configuration 4 Register */ +#define FSL_SAI_TCR4 0x10 +#define FSL_SAI_RCR4 0x90 +#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) +#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) +#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) +#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8) +#define FSL_SAI_CR4_MF BIT(4) +#define FSL_SAI_CR4_FSE BIT(3) +#define FSL_SAI_CR4_FSP BIT(1) +#define FSL_SAI_CR4_FSD_MSTR BIT(0) + +/* SAI Transmit and Recieve Configuration 5 Register */ +#define FSL_SAI_TCR5 0x14 +#define FSL_SAI_RCR5 0x94 +#define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24) +#define FSL_SAI_CR5_WNW_MASK (0x1f << 24) +#define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16) +#define FSL_SAI_CR5_W0W_MASK (0x1f << 16) +#define FSL_SAI_CR5_FBT(x) ((x) << 8) +#define FSL_SAI_CR5_FBT_MASK (0x1f << 8) + +/* SAI type */ +#define FSL_SAI_DMA BIT(0) +#define FSL_SAI_USE_AC97 BIT(1) +#define FSL_SAI_NET BIT(2) +#define FSL_SAI_TRA_SYN BIT(3) +#define FSL_SAI_REC_SYN BIT(4) +#define FSL_SAI_USE_I2S_SLAVE BIT(5) + +#define FSL_FMT_TRANSMITTER 0 +#define FSL_FMT_RECEIVER 1 + +/* SAI clock sources */ +#define FSL_SAI_CLK_BUS 0 +#define FSL_SAI_CLK_MAST1 1 +#define FSL_SAI_CLK_MAST2 2 +#define FSL_SAI_CLK_MAST3 3 + +/* SAI data transfer numbers per DMA request */ +#define FSL_SAI_MAXBURST_TX 6 +#define FSL_SAI_MAXBURST_RX 6 + +struct fsl_sai { + struct clk *clk; + + void __iomem *base; + + bool big_endian_regs; + bool big_endian_data; + + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; +}; + +#endif /* __FSL_SAI_H */ -- cgit v0.10.2 From c76d07be6739d1bfe90929ff10ff676cf54e9e7f Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 20 Dec 2013 12:17:38 +0800 Subject: ASoC: fsl-sai: Remove fsl_sai_remove() There is no need of this function and makes the code slightly shorter Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit a6af47ae5399baf4f5a2426b2121c1bcb9da4019 Change-Id: I194d8ebdd181be51985a2440b7da038ba933dc5c Reviewed-on: http://git.am.freescale.net:8181/19739 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 1cd3e7a..5194355 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -385,19 +385,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } -static int fsl_sai_dai_remove(struct snd_soc_dai *cpu_dai) -{ - cpu_dai->playback_dma_data = NULL; - cpu_dai->capture_dma_data = NULL; - - snd_soc_dai_set_drvdata(cpu_dai, NULL); - - return 0; -} - static struct snd_soc_dai_driver fsl_sai_dai = { .probe = fsl_sai_dai_probe, - .remove = fsl_sai_dai_remove, .playback = { .channels_min = 1, .channels_max = 2, -- cgit v0.10.2 From bd4069918cb3df50208967f35b32b75a0aa3679b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:00 +0800 Subject: ASoC: fsl_sai: Keep symmetry for clk_enable() and clk_disable() There are two functions haven't clk_disable_unprepare() if having error. Thus fix them. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 1fb2d9d7465bcbb519c582fa4a3bd04ff4fce2d2 Change-Id: Ia4a2525d2e32949f2092fcd4de86d46ada27b096 Reviewed-on: http://git.am.freescale.net:8181/19740 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 5194355..e19dd95 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -111,7 +111,7 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, dev_err(cpu_dai->dev, "Cannot set SAI's transmitter sysclk: %d\n", ret); - return ret; + goto err_clk; } ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, @@ -120,12 +120,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, dev_err(cpu_dai->dev, "Cannot set SAI's receiver sysclk: %d\n", ret); - return ret; + goto err_clk; } +err_clk: clk_disable_unprepare(sai->clk); - return 0; + return ret; } static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, @@ -222,7 +223,7 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) dev_err(cpu_dai->dev, "Cannot set SAI's transmitter format: %d\n", ret); - return ret; + goto err_clk; } ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); @@ -230,12 +231,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) dev_err(cpu_dai->dev, "Cannot set SAI's receiver format: %d\n", ret); - return ret; + goto err_clk; } +err_clk: clk_disable_unprepare(sai->clk); - return 0; + return ret; } static int fsl_sai_hw_params(struct snd_pcm_substream *substream, -- cgit v0.10.2 From d88f9a0651a94d7a827a98f8bc08d3f04e0804b4 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:01 +0800 Subject: ASoC: fsl_sai: Use snd_pcm_format_width() Use common helper function snd_pcm_format_width() to make code neater. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 1d7003092771bd2feec30e2f3e5a06aa33479e08 Change-Id: I6baf4f7c72a0d9f6aa332745f9251069c9600ab4 Reviewed-on: http://git.am.freescale.net:8181/19741 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index e19dd95..d7b6950 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -244,9 +244,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr, word_width; + u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; unsigned int channels = params_channels(params); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 word_width = snd_pcm_format_width(params_format(params)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { reg_cr4 = FSL_SAI_TCR4; @@ -267,20 +268,6 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - word_width = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - word_width = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - word_width = 24; - break; - default: - return -EINVAL; - } - val_cr4 |= FSL_SAI_CR4_SYWD(word_width); val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); -- cgit v0.10.2 From 56257748698d892b79da80cff9dc8d1191c372f2 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:02 +0800 Subject: ASoC: fsl_sai: Drop useless channels check in hw_params() SAi only supports two data channels on hardware level and the driver also does register the min->1 and max->2, so no need to check channels. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit d22e28cce80a93578787d273bf1fa26a2be2636b Change-Id: Ia3f1e1375c9a69d7f0259af8e801594be7b464e2 Reviewed-on: http://git.am.freescale.net:8181/19742 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index d7b6950..8079f67 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -278,10 +278,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 |= FSL_SAI_CR5_FBT(0); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); - if (channels == 2 || channels == 1) - val_mr = ~0UL - ((1 << channels) - 1); - else - return -EINVAL; + val_mr = ~0UL - ((1 << channels) - 1); sai_writel(sai, val_cr4, sai->base + reg_cr4); sai_writel(sai, val_cr5, sai->base + reg_cr5); -- cgit v0.10.2 From 90f2fb3a8c77f8aeeb369d5213ca6ea1f4c05755 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:03 +0800 Subject: ASoC: fsl_sai: Drop useless ret in startup() We can save this ret to make the code neater. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 15b29dae6604d2d2daf586429ff12f26272a868a Change-Id: Ib5d7742e67c1bf4a1e72bcf887fe8fe07250c900 Reviewed-on: http://git.am.freescale.net:8181/19743 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 8079f67..e8b737b 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -334,12 +334,9 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, static int fsl_sai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { - int ret; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - ret = clk_prepare_enable(sai->clk); - - return ret; + return clk_prepare_enable(sai->clk); } static void fsl_sai_shutdown(struct snd_pcm_substream *substream, -- cgit v0.10.2 From 0b18ebcbb99526652e3f15cce614e35651b94cb8 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:04 +0800 Subject: ASoC: fsl_sai: Make dev_err information neater Since using dev_err() there's no need to mention SAI any more, it will print the full name of the driver -- fsl_sai. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 190af12dad975f2ea7d69d1c5c9d36fec64da767 Change-Id: I2a186ded87bcafa273f99840e2eadb9e9ed63eba Reviewed-on: http://git.am.freescale.net:8181/19744 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index e8b737b..886274e 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -108,18 +108,14 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_TRANSMITTER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's transmitter sysclk: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); goto err_clk; } ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_RECEIVER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's receiver sysclk: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); goto err_clk; } @@ -220,17 +216,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's transmitter format: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); goto err_clk; } ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's receiver format: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); goto err_clk; } -- cgit v0.10.2 From fbadc482cdadf739ff08fde113e8a0baafcfe4b5 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:05 +0800 Subject: ASoC: fsl_sai: Sort local variable in general way Generally we would write code for local variable like: static new_func() { struct xxx *yyy; ... int ret; } But this driver only follows this pattern for some functions, not all. Thus this patch sorts the local variable in the general way. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 4e3a99f5b004b30bc604d82e5498700649148e0d Change-Id: Idcc0aea15b8b1daca140b05c5c9655cea43746b6 Reviewed-on: http://git.am.freescale.net:8181/19745 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 886274e..8a85dc9 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -53,8 +53,8 @@ static inline void sai_writel(struct fsl_sai *sai, static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) { - u32 val_cr2, reg_cr2; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, reg_cr2; if (fsl_dir == FSL_FMT_TRANSMITTER) reg_cr2 = FSL_SAI_TCR2; @@ -90,8 +90,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - int ret; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; if (dir == SND_SOC_CLOCK_IN) return 0; @@ -128,8 +128,8 @@ err_clk: static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) { - u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; if (fsl_dir == FSL_FMT_TRANSMITTER) { reg_cr2 = FSL_SAI_TCR2; @@ -207,8 +207,8 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - int ret; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; ret = clk_prepare_enable(sai->clk); if (ret) @@ -236,9 +236,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; unsigned int channels = params_channels(params); - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 word_width = snd_pcm_format_width(params_format(params)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -383,10 +383,10 @@ static const struct snd_soc_component_driver fsl_component = { static int fsl_sai_probe(struct platform_device *pdev) { - int ret; + struct device_node *np = pdev->dev.of_node; struct fsl_sai *sai; struct resource *res; - struct device_node *np = pdev->dev.of_node; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) -- cgit v0.10.2 From c7985f927b17047a3003493f5eb2883006e5eb2b Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 25 Dec 2013 11:20:14 +0800 Subject: ASoC: fsl_sai: Move the global registers setting to _dai_probe() Because we cannot make sure which one of _dai_fmt() and _dai_sysclk() will be firstly called. So move the RCSR/TCSR and TCR1/RCR1's initialization to _dai_probe(), and this can make sure that before any of {T,R}CR{1~5} register to be set the RCSR/TCSR's RE/TE bit has been cleared for the hareware limitation. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit e6dc12d7198eddba2e3e7a13feab5c7edde7ba1d Change-Id: Ie77345f4a2d84d55443e84d1c4cb2d2a9db766d0 Reviewed-on: http://git.am.freescale.net:8181/19746 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 8a85dc9..a414f72 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -100,11 +100,6 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, if (ret) return ret; - sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); - sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); - sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); - sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); - ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_TRANSMITTER); if (ret) { @@ -351,6 +346,18 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); + int ret; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); + sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); + sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); + sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); + + clk_disable_unprepare(sai->clk); cpu_dai->playback_dma_data = &sai->dma_params_tx; cpu_dai->capture_dma_data = &sai->dma_params_rx; -- cgit v0.10.2 From f6d460ab12bad902e28a88df4e663a5e423ef3c0 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 25 Dec 2013 12:40:04 +0800 Subject: ASoC: fsl_sai: Add disable operation for the corresponding data channel. Enables/Disables the corresponding data channel for tx/rx operation. A channel must be enabled before its FIFO is accessed, and then disable it when tx/rx is stopped or idle. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit e5d0fa9c3ec59a40e0285d96b65b7f62875acd42 Change-Id: I44a321c2f74580bc6387688777434c88f027c17b Reviewed-on: http://git.am.freescale.net:8181/19747 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a414f72..ba4d9c8 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -124,20 +124,17 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; + u32 val_cr2, val_cr4, reg_cr2, reg_cr4; if (fsl_dir == FSL_FMT_TRANSMITTER) { reg_cr2 = FSL_SAI_TCR2; - reg_cr3 = FSL_SAI_TCR3; reg_cr4 = FSL_SAI_TCR4; } else { reg_cr2 = FSL_SAI_RCR2; - reg_cr3 = FSL_SAI_RCR3; reg_cr4 = FSL_SAI_RCR4; } val_cr2 = sai_readl(sai, sai->base + reg_cr2); - val_cr3 = sai_readl(sai, sai->base + reg_cr3); val_cr4 = sai_readl(sai, sai->base + reg_cr4); if (sai->big_endian_data) @@ -188,13 +185,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - val_cr3 |= FSL_SAI_CR3_TRCE; - if (fsl_dir == FSL_FMT_RECEIVER) val_cr2 |= FSL_SAI_CR2_SYNC; sai_writel(sai, val_cr2, sai->base + reg_cr2); - sai_writel(sai, val_cr3, sai->base + reg_cr3); sai_writel(sai, val_cr4, sai->base + reg_cr4); return 0; @@ -278,7 +272,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int tcsr, rcsr; + u32 tcsr, rcsr, val_cr3, reg_cr3; tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); @@ -286,17 +280,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { tcsr |= FSL_SAI_CSR_FRDE; rcsr &= ~FSL_SAI_CSR_FRDE; + reg_cr3 = FSL_SAI_TCR3; } else { rcsr |= FSL_SAI_CSR_FRDE; tcsr &= ~FSL_SAI_CSR_FRDE; + reg_cr3 = FSL_SAI_RCR3; } + val_cr3 = sai_readl(sai, sai->base + reg_cr3); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: tcsr |= FSL_SAI_CSR_TERE; rcsr |= FSL_SAI_CSR_TERE; + val_cr3 |= FSL_SAI_CR3_TRCE; + + sai_writel(sai, val_cr3, sai->base + reg_cr3); sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); break; @@ -308,8 +309,12 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, tcsr &= ~FSL_SAI_CSR_TERE; rcsr &= ~FSL_SAI_CSR_TERE; } + + val_cr3 &= ~FSL_SAI_CR3_TRCE; + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + sai_writel(sai, val_cr3, sai->base + reg_cr3); break; default: return -EINVAL; -- cgit v0.10.2 From 2848b94e74a3da59911d41ee727ef5c2cead96c0 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 31 Dec 2013 15:33:21 +0800 Subject: ASoC: fsl_sai: Fix one bug for hardware limitation. This is maybe one bug or a limitation of the hardware that the {T,R}CR2's Synchronous Mode bits must be set as late as possible, or the SAI device maybe hanged up, and there has not any explaination about this limitation in the SAI Data Sheet. And the {T,R}CR2's Synchronous Mode bits must be set at the same time whether for Tx or Rx stream. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 496a39d9ec238569fac6daceac8f5420c5edc2f1 Change-Id: Ib09d153b20251254277f3efacdc5d5b5d8f8425b Reviewed-on: http://git.am.freescale.net:8181/19748 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index ba4d9c8..ecd0104 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -145,7 +145,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: val_cr4 |= FSL_SAI_CR4_FSE; - val_cr4 |= FSL_SAI_CR4_FSP; break; default: return -EINVAL; @@ -185,9 +184,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - if (fsl_dir == FSL_FMT_RECEIVER) - val_cr2 |= FSL_SAI_CR2_SYNC; - sai_writel(sai, val_cr2, sai->base + reg_cr2); sai_writel(sai, val_cr4, sai->base + reg_cr4); @@ -253,6 +249,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); else @@ -272,7 +269,15 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 tcsr, rcsr, val_cr3, reg_cr3; + u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3; + + val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2); + val_cr2 &= ~FSL_SAI_CR2_SYNC; + sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2); + + val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2); + val_cr2 |= FSL_SAI_CR2_SYNC; + sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2); tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); -- cgit v0.10.2 From cff192e41a55dfbdc7ba246f407c2842b0d839b2 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 31 Dec 2013 15:33:22 +0800 Subject: ASoC: fsl_sai: fix the endianess for SAI fifo data. Revert the SAI's endianess for fifo data to/from DMA engine. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 72aa62bed3ea30635156fad95f123a0b665072bf Change-Id: I7c8ec99b9d8292527ba2fc47ff071146b2225fae Reviewed-on: http://git.am.freescale.net:8181/19749 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index ecd0104..5802fbd 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -138,9 +138,9 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, val_cr4 = sai_readl(sai, sai->base + reg_cr4); if (sai->big_endian_data) - val_cr4 |= FSL_SAI_CR4_MF; - else val_cr4 &= ~FSL_SAI_CR4_MF; + else + val_cr4 |= FSL_SAI_CR4_MF; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -251,9 +251,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) - val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); - else val_cr5 |= FSL_SAI_CR5_FBT(0); + else + val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); val_mr = ~0UL - ((1 << channels) - 1); -- cgit v0.10.2 From b144f256427e0d35910bba38871ed4ec66a19182 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 8 Jan 2014 16:13:05 +0800 Subject: ASoC: fsl-sai: Clean up the code Makes the code slightly shorter. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 633ff8f8a4393b4a13b94eddd2613198c32035e6 Change-Id: Ic59247589f69e0705d49fc4db9e7d269125856d8 Reviewed-on: http://git.am.freescale.net:8181/19750 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 5802fbd..34ee871 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -62,26 +62,25 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, reg_cr2 = FSL_SAI_RCR2; val_cr2 = sai_readl(sai, sai->base + reg_cr2); + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + switch (clk_id) { case FSL_SAI_CLK_BUS: - val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; val_cr2 |= FSL_SAI_CR2_MSEL_BUS; break; case FSL_SAI_CLK_MAST1: - val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1; break; case FSL_SAI_CLK_MAST2: - val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2; break; case FSL_SAI_CLK_MAST3: - val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3; break; default: return -EINVAL; } + sai_writel(sai, val_cr2, sai->base + reg_cr2); return 0; -- cgit v0.10.2 From ea84624189e996189ea741ea1e36d991ed3c30c3 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 24 Mar 2014 18:24:36 +0800 Subject: ASoC: fsl-sai: convert to use regmap API for Freeacale SAI Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 78957fc349bcf29d415a649601581a993ff25e4d Change-Id: I29156c937a15a11c6b46d83fbcaab6e1afe1b767 Reviewed-on: http://git.am.freescale.net:8181/19751 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 34ee871..ad1fa10 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,34 +23,6 @@ #include "fsl_sai.h" -static inline u32 sai_readl(struct fsl_sai *sai, - const void __iomem *addr) -{ - u32 val; - - val = __raw_readl(addr); - - if (likely(sai->big_endian_regs)) - val = be32_to_cpu(val); - else - val = le32_to_cpu(val); - rmb(); - - return val; -} - -static inline void sai_writel(struct fsl_sai *sai, - u32 val, void __iomem *addr) -{ - wmb(); - if (likely(sai->big_endian_regs)) - val = cpu_to_be32(val); - else - val = cpu_to_le32(val); - - __raw_writel(val, addr); -} - static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) { @@ -61,7 +34,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, else reg_cr2 = FSL_SAI_RCR2; - val_cr2 = sai_readl(sai, sai->base + reg_cr2); + regmap_read(sai->regmap, reg_cr2, &val_cr2); + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; switch (clk_id) { @@ -81,7 +55,7 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - sai_writel(sai, val_cr2, sai->base + reg_cr2); + regmap_write(sai->regmap, reg_cr2, val_cr2); return 0; } @@ -89,32 +63,22 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); int ret; if (dir == SND_SOC_CLOCK_IN) return 0; - ret = clk_prepare_enable(sai->clk); - if (ret) - return ret; - ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_TRANSMITTER); if (ret) { dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); - goto err_clk; + return ret; } ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_RECEIVER); - if (ret) { + if (ret) dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); - goto err_clk; - } - -err_clk: - clk_disable_unprepare(sai->clk); return ret; } @@ -133,8 +97,8 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, reg_cr4 = FSL_SAI_RCR4; } - val_cr2 = sai_readl(sai, sai->base + reg_cr2); - val_cr4 = sai_readl(sai, sai->base + reg_cr4); + regmap_read(sai->regmap, reg_cr2, &val_cr2); + regmap_read(sai->regmap, reg_cr4, &val_cr4); if (sai->big_endian_data) val_cr4 &= ~FSL_SAI_CR4_MF; @@ -183,35 +147,25 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - sai_writel(sai, val_cr2, sai->base + reg_cr2); - sai_writel(sai, val_cr4, sai->base + reg_cr4); + regmap_write(sai->regmap, reg_cr2, val_cr2); + regmap_write(sai->regmap, reg_cr4, val_cr4); return 0; } static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); int ret; - ret = clk_prepare_enable(sai->clk); - if (ret) - return ret; - ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); if (ret) { dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); - goto err_clk; + return ret; } ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); - if (ret) { + if (ret) dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); - goto err_clk; - } - -err_clk: - clk_disable_unprepare(sai->clk); return ret; } @@ -235,11 +189,12 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, reg_mr = FSL_SAI_RMR; } - val_cr4 = sai_readl(sai, sai->base + reg_cr4); + regmap_read(sai->regmap, reg_cr4, &val_cr4); + regmap_read(sai->regmap, reg_cr4, &val_cr5); + val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; - val_cr5 = sai_readl(sai, sai->base + reg_cr5); val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; @@ -257,9 +212,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr4 |= FSL_SAI_CR4_FRSZ(channels); val_mr = ~0UL - ((1 << channels) - 1); - sai_writel(sai, val_cr4, sai->base + reg_cr4); - sai_writel(sai, val_cr5, sai->base + reg_cr5); - sai_writel(sai, val_mr, sai->base + reg_mr); + regmap_write(sai->regmap, reg_cr4, val_cr4); + regmap_write(sai->regmap, reg_cr5, val_cr5); + regmap_write(sai->regmap, reg_mr, val_mr); return 0; } @@ -268,44 +223,34 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3; - - val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2); - val_cr2 &= ~FSL_SAI_CR2_SYNC; - sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2); + u32 tcsr, rcsr; - val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2); - val_cr2 |= FSL_SAI_CR2_SYNC; - sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2); + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, + ~FSL_SAI_CR2_SYNC); + regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, + FSL_SAI_CR2_SYNC); - tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); - rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); + regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr); + regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { tcsr |= FSL_SAI_CSR_FRDE; rcsr &= ~FSL_SAI_CSR_FRDE; - reg_cr3 = FSL_SAI_TCR3; } else { rcsr |= FSL_SAI_CSR_FRDE; tcsr &= ~FSL_SAI_CSR_FRDE; - reg_cr3 = FSL_SAI_RCR3; } - val_cr3 = sai_readl(sai, sai->base + reg_cr3); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: tcsr |= FSL_SAI_CSR_TERE; rcsr |= FSL_SAI_CSR_TERE; - val_cr3 |= FSL_SAI_CR3_TRCE; - sai_writel(sai, val_cr3, sai->base + reg_cr3); - sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); - sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); + regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -314,11 +259,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, rcsr &= ~FSL_SAI_CSR_TERE; } - val_cr3 &= ~FSL_SAI_CR3_TRCE; - - sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); - sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); - sai_writel(sai, val_cr3, sai->base + reg_cr3); + regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); + regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); break; default: return -EINVAL; @@ -331,16 +273,32 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; - return clk_prepare_enable(sai->clk); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = FSL_SAI_TCR3; + else + reg = FSL_SAI_RCR3; + + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, + FSL_SAI_CR3_TRCE); + + return 0; } static void fsl_sai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = FSL_SAI_TCR3; + else + reg = FSL_SAI_RCR3; - clk_disable_unprepare(sai->clk); + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, + ~FSL_SAI_CR3_TRCE); } static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { @@ -355,18 +313,13 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); - int ret; - - ret = clk_prepare_enable(sai->clk); - if (ret) - return ret; - sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); - sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); - sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); - sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); - - clk_disable_unprepare(sai->clk); + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, + FSL_SAI_MAXBURST_TX * 2); + regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, + FSL_SAI_MAXBURST_RX - 1); cpu_dai->playback_dma_data = &sai->dma_params_tx; cpu_dai->capture_dma_data = &sai->dma_params_rx; @@ -397,26 +350,109 @@ static const struct snd_soc_component_driver fsl_component = { .name = "fsl-sai", }; +static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_TCR1: + case FSL_SAI_TCR2: + case FSL_SAI_TCR3: + case FSL_SAI_TCR4: + case FSL_SAI_TCR5: + case FSL_SAI_TFR: + case FSL_SAI_TMR: + case FSL_SAI_RCSR: + case FSL_SAI_RCR1: + case FSL_SAI_RCR2: + case FSL_SAI_RCR3: + case FSL_SAI_RCR4: + case FSL_SAI_RCR5: + case FSL_SAI_RDR: + case FSL_SAI_RFR: + case FSL_SAI_RMR: + return true; + default: + return false; + } +} + +static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TFR: + case FSL_SAI_RFR: + case FSL_SAI_TDR: + case FSL_SAI_RDR: + return true; + default: + return false; + } + +} + +static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_TCR1: + case FSL_SAI_TCR2: + case FSL_SAI_TCR3: + case FSL_SAI_TCR4: + case FSL_SAI_TCR5: + case FSL_SAI_TDR: + case FSL_SAI_TMR: + case FSL_SAI_RCSR: + case FSL_SAI_RCR1: + case FSL_SAI_RCR2: + case FSL_SAI_RCR3: + case FSL_SAI_RCR4: + case FSL_SAI_RCR5: + case FSL_SAI_RMR: + return true; + default: + return false; + } +} + +static struct regmap_config fsl_sai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FSL_SAI_RMR, + .readable_reg = fsl_sai_readable_reg, + .volatile_reg = fsl_sai_volatile_reg, + .writeable_reg = fsl_sai_writeable_reg, +}; + static int fsl_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct fsl_sai *sai; struct resource *res; + void __iomem *base; int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + if (sai->big_endian_regs) + fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sai->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(sai->base)) - return PTR_ERR(sai->base); - - sai->clk = devm_clk_get(&pdev->dev, "sai"); - if (IS_ERR(sai->clk)) { - dev_err(&pdev->dev, "Cannot get SAI's clock\n"); - return PTR_ERR(sai->clk); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "sai", base, &fsl_sai_regmap_config); + if (IS_ERR(sai->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(sai->regmap); } sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; @@ -424,9 +460,6 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; - sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); - sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); - platform_set_drvdata(pdev, sai); ret = snd_soc_register_component(&pdev->dev, &fsl_component, diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 41bb62e..1571459 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -15,31 +15,36 @@ SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) +/* SAI Register Map Register */ +#define FSL_SAI_TCSR 0x00 /* SAI Transmit Control */ +#define FSL_SAI_TCR1 0x04 /* SAI Transmit Configuration 1 */ +#define FSL_SAI_TCR2 0x08 /* SAI Transmit Configuration 2 */ +#define FSL_SAI_TCR3 0x0c /* SAI Transmit Configuration 3 */ +#define FSL_SAI_TCR4 0x10 /* SAI Transmit Configuration 4 */ +#define FSL_SAI_TCR5 0x14 /* SAI Transmit Configuration 5 */ +#define FSL_SAI_TDR 0x20 /* SAI Transmit Data */ +#define FSL_SAI_TFR 0x40 /* SAI Transmit FIFO */ +#define FSL_SAI_TMR 0x60 /* SAI Transmit Mask */ +#define FSL_SAI_RCSR 0x80 /* SAI Receive Control */ +#define FSL_SAI_RCR1 0x84 /* SAI Receive Configuration 1 */ +#define FSL_SAI_RCR2 0x88 /* SAI Receive Configuration 2 */ +#define FSL_SAI_RCR3 0x8c /* SAI Receive Configuration 3 */ +#define FSL_SAI_RCR4 0x90 /* SAI Receive Configuration 4 */ +#define FSL_SAI_RCR5 0x94 /* SAI Receive Configuration 5 */ +#define FSL_SAI_RDR 0xa0 /* SAI Receive Data */ +#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */ +#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */ + /* SAI Transmit/Recieve Control Register */ -#define FSL_SAI_TCSR 0x00 -#define FSL_SAI_RCSR 0x80 #define FSL_SAI_CSR_TERE BIT(31) #define FSL_SAI_CSR_FWF BIT(17) #define FSL_SAI_CSR_FRIE BIT(8) #define FSL_SAI_CSR_FRDE BIT(0) -/* SAI Transmit Data/FIFO/MASK Register */ -#define FSL_SAI_TDR 0x20 -#define FSL_SAI_TFR 0x40 -#define FSL_SAI_TMR 0x60 - -/* SAI Recieve Data/FIFO/MASK Register */ -#define FSL_SAI_RDR 0xa0 -#define FSL_SAI_RFR 0xc0 -#define FSL_SAI_RMR 0xe0 - /* SAI Transmit and Recieve Configuration 1 Register */ -#define FSL_SAI_TCR1 0x04 -#define FSL_SAI_RCR1 0x84 +#define FSL_SAI_CR1_RFW_MASK 0x1f /* SAI Transmit and Recieve Configuration 2 Register */ -#define FSL_SAI_TCR2 0x08 -#define FSL_SAI_RCR2 0x88 #define FSL_SAI_CR2_SYNC BIT(30) #define FSL_SAI_CR2_MSEL_MASK (0xff << 26) #define FSL_SAI_CR2_MSEL_BUS 0 @@ -50,15 +55,11 @@ #define FSL_SAI_CR2_BCD_MSTR BIT(24) /* SAI Transmit and Recieve Configuration 3 Register */ -#define FSL_SAI_TCR3 0x0c -#define FSL_SAI_RCR3 0x8c #define FSL_SAI_CR3_TRCE BIT(16) #define FSL_SAI_CR3_WDFL(x) (x) #define FSL_SAI_CR3_WDFL_MASK 0x1f /* SAI Transmit and Recieve Configuration 4 Register */ -#define FSL_SAI_TCR4 0x10 -#define FSL_SAI_RCR4 0x90 #define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) #define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) #define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) @@ -69,8 +70,6 @@ #define FSL_SAI_CR4_FSD_MSTR BIT(0) /* SAI Transmit and Recieve Configuration 5 Register */ -#define FSL_SAI_TCR5 0x14 -#define FSL_SAI_RCR5 0x94 #define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24) #define FSL_SAI_CR5_WNW_MASK (0x1f << 24) #define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16) @@ -100,9 +99,7 @@ #define FSL_SAI_MAXBURST_RX 6 struct fsl_sai { - struct clk *clk; - - void __iomem *base; + struct regmap *regmap; bool big_endian_regs; bool big_endian_data; -- cgit v0.10.2 From f11008d1545f449889d382d7bb1c31d989363794 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 24 Mar 2014 18:25:10 +0800 Subject: ASoC: fsl-sai: fix Freescale SAI DAI format setting. o Fix some bugs of fsl_sai_set_dai_fmt_tr(). o Add SND_SOC_DAIFMT_LEFT_J support. o Add SND_SOC_DAIFMT_CBS_CFM support. o Add SND_SOC_DAIFMT_CBM_CFS support. o And SND_SOC_DAIFMT_RIGHT_J need to be done in the future. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 13cde090030c7d00e991c85b87c12891cc8e4df4 Change-Id: If49b36a048d2c2b3eedef732ef5bae4eed98bb4b Reviewed-on: http://git.am.freescale.net:8181/19752 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index ad1fa10..466597b 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -105,35 +105,47 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, else val_cr4 |= FSL_SAI_CR4_MF; + /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - val_cr4 |= FSL_SAI_CR4_FSE; + /* Data on rising edge of bclk, frame low, 1clk before data */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* Data on rising edge of bclk, frame high, 0clk before data */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); break; + case SND_SOC_DAIFMT_RIGHT_J: + /* To be done */ default: return -EINVAL; } + /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: - val_cr4 |= FSL_SAI_CR4_FSP; - val_cr2 &= ~FSL_SAI_CR2_BCP; + /* Invert both clocks */ + val_cr2 ^= FSL_SAI_CR2_BCP; + val_cr4 ^= FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_IB_NF: - val_cr4 &= ~FSL_SAI_CR4_FSP; - val_cr2 &= ~FSL_SAI_CR2_BCP; + /* Invert bit clock */ + val_cr2 ^= FSL_SAI_CR2_BCP; break; case SND_SOC_DAIFMT_NB_IF: - val_cr4 |= FSL_SAI_CR4_FSP; - val_cr2 |= FSL_SAI_CR2_BCP; + /* Invert frame clock */ + val_cr4 ^= FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_NB_NF: - val_cr4 &= ~FSL_SAI_CR4_FSP; - val_cr2 |= FSL_SAI_CR2_BCP; + /* Nothing to do for both normal cases */ break; default: return -EINVAL; } + /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; @@ -143,6 +155,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; break; + case SND_SOC_DAIFMT_CBS_CFM: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; default: return -EINVAL; } -- cgit v0.10.2 From 114fcf4aca99672e839c4f9a23fcd3c9f018c699 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 24 Mar 2014 18:25:40 +0800 Subject: ASoC: fsl-sai: Add SND_SOC_DAIFMT_DSP_A/B support. o Add SND_SOC_DAIFMT_DSP_A support. o Add SND_SOC_DAIFMT_DSP_B support. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit a3f7dcc9cc0392528bff75b17adfcd74fb8a0ecd Change-Id: I6863de1501bf897ec70f2d927046ccd3d2866010 Reviewed-on: http://git.am.freescale.net:8181/19753 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 466597b..10535bd 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -108,15 +108,44 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - /* Data on rising edge of bclk, frame low, 1clk before data */ + /* + * Frame low, 1clk before data, one word length for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ val_cr2 &= ~FSL_SAI_CR2_BCP; val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_LEFT_J: - /* Data on rising edge of bclk, frame high, 0clk before data */ + /* + * Frame high, one word length for frame sync, + * frame sync asserts with the first bit of the frame. + */ val_cr2 &= ~FSL_SAI_CR2_BCP; val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); break; + case SND_SOC_DAIFMT_DSP_A: + /* + * Frame high, 1clk before data, one bit for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr4 |= FSL_SAI_CR4_FSE; + sai->is_dsp_mode = true; + break; + case SND_SOC_DAIFMT_DSP_B: + /* + * Frame high, one bit for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); + sai->is_dsp_mode = true; + break; case SND_SOC_DAIFMT_RIGHT_J: /* To be done */ default: @@ -219,7 +248,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; - val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + if (!sai->is_dsp_mode) + val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); @@ -245,6 +276,10 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 tcsr, rcsr; + /* + * The transmitter bit clock and frame sync are to be + * used by both the transmitter and receiver. + */ regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, ~FSL_SAI_CR2_SYNC); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, @@ -261,6 +296,10 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, tcsr &= ~FSL_SAI_CSR_FRDE; } + /* + * It is recommended that the transmitter is the last enabled + * and the first disabled. + */ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 1571459..e432260 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -103,6 +103,7 @@ struct fsl_sai { bool big_endian_regs; bool big_endian_data; + bool is_dsp_mode; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; -- cgit v0.10.2 From 6313b89ee179e04f83641703a07f47c202098f2a Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 24 Mar 2014 18:32:28 +0800 Subject: ASoC: fsl-sai: backport to 3.12 Signed-off-by: Xiubo Li Change-Id: I4e1f12afb9aefa0de69bcbab393eb8ec28f56df1 Reviewed-on: http://git.am.freescale.net:8181/19754 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 10535bd..7286824 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -484,6 +484,41 @@ static struct regmap_config fsl_sai_regmap_config = { .writeable_reg = fsl_sai_writeable_reg, }; +static bool fsl_sai_filter(struct dma_chan *chan, void *param) +{ + struct snd_dmaengine_dai_dma_data *dma_data = param; + + chan->private = dma_data->filter_data; + + return true; +} + +static const struct snd_pcm_hardware fsl_sai_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = FSL_SAI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65535, + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static const struct snd_dmaengine_pcm_config fsl_sai_dmaengine_pcm_config = { + .pcm_hardware = &fsl_sai_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = fsl_sai_filter, + .prealloc_buffer_size = FSL_SAI_DMABUF_SIZE, +}; + static int fsl_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -526,7 +561,8 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret) return ret; - ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, + ret = snd_dmaengine_pcm_register(&pdev->dev, + &fsl_sai_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); if (ret) snd_soc_unregister_component(&pdev->dev); @@ -550,7 +586,6 @@ static const struct of_device_id fsl_sai_ids[] = { static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, .remove = fsl_sai_remove, - .driver = { .name = "fsl-sai", .owner = THIS_MODULE, diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index e432260..fc12d96 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -11,6 +11,8 @@ #include +#define FSL_SAI_DMABUF_SIZE (64 * 1024) + #define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) -- cgit v0.10.2 From 915cad947438c87d5f4245880778a07cb14d2fbd Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Mon, 22 Sep 2014 16:58:24 +0800 Subject: serial: of-serial: add PM suspend/resume support This adds PM suspend/resume support for the of-serial driver to provide power management support on devices attatched to it. Signed-off-by: Jingchang Lu This patch has been sent to upstream for review: https://patchwork.kernel.org/patch/4954521/ Change-Id: I06905237f7d7ef51bf5a1c135cd0880d92c4c104 Reviewed-on: http://git.am.freescale.net:8181/19728 Tested-by: Review Code-CDREVIEW Reviewed-by: Chenhui Zhao Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index b4507be..a320fc5 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -240,6 +240,32 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + serial8250_suspend_port(info->line); + if (info->clk) + clk_disable_unprepare(info->clk); + + return 0; +} + +static int of_serial_resume(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + if (info->clk) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); + /* * A few common types, add more as needed. */ @@ -273,6 +299,7 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, -- cgit v0.10.2 From b1717f30e10360d3ceddecad4076d415ef153f6a Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 2 Apr 2014 18:09:07 +0800 Subject: regmap: implement LE formatting/parsing for 16/32-bit values. Allow busses to request little endianness formatting and parsing for 16- and 32-bit values. This will be useful to support regmap-mmio. For the following the scenarios using the regmap-mmio, for example: Index CPU Device Endianess flag for values ---------------------------------------------------------- 1 LE LE REGMAP_ENDIAN_DEFAULT/NATIVE 2 LE BE REGMAP_ENDIAN_BIG 3 BE BE REGMAP_ENDIAN_DEFAULT/NATIVE 4 BE LE REGMAP_ENDIAN_LITTLE For one device driver, which will support all the cases above, needs two boolean properties in DT node like: 'big-endian' for case 2 and 'little-endian' for case 4, and for cases 1 and 3 they all will be absent. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 4aa8c0694c731e03eb660b92a3afe14859142381 Change-Id: I8ac391476c8be6e59fd38493baa1f8afe03fd3fc Reviewed-on: http://git.am.freescale.net:8181/19854 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..022f596 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -201,6 +201,13 @@ static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift) b[0] = cpu_to_be16(val << shift); } +static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift) +{ + __le16 *b = buf; + + b[0] = cpu_to_le16(val << shift); +} + static void regmap_format_16_native(void *buf, unsigned int val, unsigned int shift) { @@ -225,6 +232,13 @@ static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift) b[0] = cpu_to_be32(val << shift); } +static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift) +{ + __le32 *b = buf; + + b[0] = cpu_to_le32(val << shift); +} + static void regmap_format_32_native(void *buf, unsigned int val, unsigned int shift) { @@ -249,6 +263,13 @@ static unsigned int regmap_parse_16_be(const void *buf) return be16_to_cpu(b[0]); } +static unsigned int regmap_parse_16_le(const void *buf) +{ + const __le16 *b = buf; + + return le16_to_cpu(b[0]); +} + static void regmap_parse_16_be_inplace(void *buf) { __be16 *b = buf; @@ -256,6 +277,13 @@ static void regmap_parse_16_be_inplace(void *buf) b[0] = be16_to_cpu(b[0]); } +static void regmap_parse_16_le_inplace(void *buf) +{ + __le16 *b = buf; + + b[0] = le16_to_cpu(b[0]); +} + static unsigned int regmap_parse_16_native(const void *buf) { return *(u16 *)buf; @@ -278,6 +306,13 @@ static unsigned int regmap_parse_32_be(const void *buf) return be32_to_cpu(b[0]); } +static unsigned int regmap_parse_32_le(const void *buf) +{ + const __le32 *b = buf; + + return le32_to_cpu(b[0]); +} + static void regmap_parse_32_be_inplace(void *buf) { __be32 *b = buf; @@ -285,6 +320,13 @@ static void regmap_parse_32_be_inplace(void *buf) b[0] = be32_to_cpu(b[0]); } +static void regmap_parse_32_le_inplace(void *buf) +{ + __le32 *b = buf; + + b[0] = le32_to_cpu(b[0]); +} + static unsigned int regmap_parse_32_native(const void *buf) { return *(u32 *)buf; @@ -593,6 +635,11 @@ struct regmap *regmap_init(struct device *dev, map->format.parse_val = regmap_parse_16_be; map->format.parse_inplace = regmap_parse_16_be_inplace; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_val = regmap_format_16_le; + map->format.parse_val = regmap_parse_16_le; + map->format.parse_inplace = regmap_parse_16_le_inplace; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_16_native; map->format.parse_val = regmap_parse_16_native; @@ -614,6 +661,11 @@ struct regmap *regmap_init(struct device *dev, map->format.parse_val = regmap_parse_32_be; map->format.parse_inplace = regmap_parse_32_be_inplace; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_val = regmap_format_32_le; + map->format.parse_val = regmap_parse_32_le; + map->format.parse_inplace = regmap_parse_32_le_inplace; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_32_native; map->format.parse_val = regmap_parse_32_native; -- cgit v0.10.2 From 26c46fba650d25e0c9fd91c31e955d202d2e0e22 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 26 Sep 2014 11:51:46 +0800 Subject: regmap: add DT endianness binding support. For many drivers which will support rich endianness of Devices need define DT properties by itself with the binding support. The endianness using regmap: Index Device Properties if needs bytes-swap, or just ignore it ------------------------------------------------------------- 1 BE 'big-endian' 2 LE 'little-endian' The properties include all the register values and the buffers. And these properties are very usful for the MMIO devices: Such as: a memory-mapped device, on one SoC is in BE mode, while in another SoC will be in LE mode, and the CPU will always in LE mode. For the first case, we must use cpu_to_be32/be32_to_cpu for 32-bit registers accessing, so the 'big-endian' property is needed. For the second case, we can just ignore the bytes-swap functions like cpu_to_le32/le32_to_cpu, so the 'little-endian' property could be abscent. And vice versa... Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit d647c199510c2c126ac03ecbea51086e10126a40 Change-Id: I24ce6753dd557be212d2ec9f67f0d9513be22617 Reviewed-on: http://git.am.freescale.net:8181/19855 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index fa6bf52..1f603a6 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -96,6 +96,8 @@ static struct regmap_bus regmap_i2c = { .write = regmap_i2c_write, .gather_write = regmap_i2c_gather_write, .read = regmap_i2c_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, }; /** diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 4c506bd..4f4074a 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = { .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, }; /** diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 022f596..5c0169a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -431,6 +432,102 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } +enum regmap_endian_type { + REGMAP_ENDIAN_REG, + REGMAP_ENDIAN_VAL, +}; + +static int of_regmap_get_endian(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config, + enum regmap_endian_type type, + enum regmap_endian *endian) +{ + struct device_node *np = dev->of_node; + + if (!endian || !config) + return -EINVAL; + + /* + * Firstly, try to parse the endianness from driver's config, + * this is to be compatible with the none DT or the old drivers. + * From the driver's config the endianness value maybe: + * REGMAP_ENDIAN_BIG, + * REGMAP_ENDIAN_LITTLE, + * REGMAP_ENDIAN_NATIVE, + * REGMAP_ENDIAN_DEFAULT. + */ + switch (type) { + case REGMAP_ENDIAN_REG: + *endian = config->reg_format_endian; + break; + case REGMAP_ENDIAN_VAL: + *endian = config->val_format_endian; + break; + default: + return -EINVAL; + } + + /* + * If the endianness parsed from driver config is + * REGMAP_ENDIAN_DEFAULT, that means maybe we are using the DT + * node to specify the endianness information. + */ + if (*endian != REGMAP_ENDIAN_DEFAULT) + return 0; + + /* + * Secondly, try to parse the endianness from DT node if the + * driver config does not specify it. + * From the DT node the endianness value maybe: + * REGMAP_ENDIAN_BIG, + * REGMAP_ENDIAN_LITTLE, + * REGMAP_ENDIAN_NATIVE, + */ + switch (type) { + case REGMAP_ENDIAN_VAL: + if (of_property_read_bool(np, "big-endian")) + *endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + *endian = REGMAP_ENDIAN_LITTLE; + else + *endian = REGMAP_ENDIAN_NATIVE; + break; + case REGMAP_ENDIAN_REG: + break; + default: + return -EINVAL; + } + + /* + * If the endianness parsed from DT node is REGMAP_ENDIAN_NATIVE, that + * maybe means the DT does not care the endianness or it should use + * the regmap bus's default endianness, then we should try to check + * whether the regmap bus has specified the default endianness. + */ + if (*endian != REGMAP_ENDIAN_NATIVE) + return 0; + + /* + * Finally, try to parse the endianness from regmap bus config + * if in device's DT node the endianness property is absent. + */ + switch (type) { + case REGMAP_ENDIAN_REG: + if (bus && bus->reg_format_endian_default) + *endian = bus->reg_format_endian_default; + break; + case REGMAP_ENDIAN_VAL: + if (bus && bus->val_format_endian_default) + *endian = bus->val_format_endian_default; + break; + default: + return -EINVAL; + } + + return 0; +} + /** * regmap_init(): Initialise register map * @@ -526,17 +623,15 @@ struct regmap *regmap_init(struct device *dev, map->reg_read = _regmap_bus_read; } - reg_endian = config->reg_format_endian; - if (reg_endian == REGMAP_ENDIAN_DEFAULT) - reg_endian = bus->reg_format_endian_default; - if (reg_endian == REGMAP_ENDIAN_DEFAULT) - reg_endian = REGMAP_ENDIAN_BIG; - - val_endian = config->val_format_endian; - if (val_endian == REGMAP_ENDIAN_DEFAULT) - val_endian = bus->val_format_endian_default; - if (val_endian == REGMAP_ENDIAN_DEFAULT) - val_endian = REGMAP_ENDIAN_BIG; + ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG, + ®_endian); + if (ret) + return ERR_PTR(ret); + + ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL, + &val_endian); + if (ret) + return ERR_PTR(ret); switch (config->reg_bits + map->reg_shift) { case 2: -- cgit v0.10.2 From a5749bf256d6ae6272f46feddbfcaa577505aaf8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 18 Aug 2014 15:09:02 +0200 Subject: regmap: Fix DT endianess parsing logic Commit d647c199510c ("regmap: add DT endianness binding support.") added support to parse the device endianness from the device tree but unfortunately the added logic doesn't have the same semantics than the old code. This leads to a NULL dereference pointer error when these properties are not provided by the Device Tree: Unable to handle kernel NULL pointer dereference at virtual address 00000044 pgd = c0004000 [00000044] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT SMP ARM Modules linked in: CPU: 5 PID: 1 Comm: swapper/0 Not tainted 3.17.0-rc1-next-20140818ccu #671 task: ea412800 ti: ea484000 task.ti: ea484000 PC is at regmap_update_bits+0xc/0x5c The problem is that platforms that rely on the default value now gets different values due two related issues in the current code: a) It only parses the endianness from DT for the regmap registers and not for the regmap values but it checks unconditionally in both cases if the resulting endiannes is REGMAP_ENDIAN_NATIVE. b) REGMAP_ENDIAN_NATIVE is not even a valid DT property according to the regmap DT binding documentation so it shouldn't be set. Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit ba1b53feb8cacbd84bcf0e48925e30ad29e141a6 Change-Id: Ifaf8389127b8c931b7f118c124ab0935b0b98dd2 Reviewed-on: http://git.am.freescale.net:8181/19857 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 5c0169a..d790447 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -482,7 +482,6 @@ static int of_regmap_get_endian(struct device *dev, * From the DT node the endianness value maybe: * REGMAP_ENDIAN_BIG, * REGMAP_ENDIAN_LITTLE, - * REGMAP_ENDIAN_NATIVE, */ switch (type) { case REGMAP_ENDIAN_VAL: @@ -490,8 +489,10 @@ static int of_regmap_get_endian(struct device *dev, *endian = REGMAP_ENDIAN_BIG; else if (of_property_read_bool(np, "little-endian")) *endian = REGMAP_ENDIAN_LITTLE; - else - *endian = REGMAP_ENDIAN_NATIVE; + + if (*endian != REGMAP_ENDIAN_DEFAULT) + return 0; + break; case REGMAP_ENDIAN_REG: break; @@ -500,15 +501,6 @@ static int of_regmap_get_endian(struct device *dev, } /* - * If the endianness parsed from DT node is REGMAP_ENDIAN_NATIVE, that - * maybe means the DT does not care the endianness or it should use - * the regmap bus's default endianness, then we should try to check - * whether the regmap bus has specified the default endianness. - */ - if (*endian != REGMAP_ENDIAN_NATIVE) - return 0; - - /* * Finally, try to parse the endianness from regmap bus config * if in device's DT node the endianness property is absent. */ -- cgit v0.10.2 From 9cc091a16151c080f0f45ec3199c1bc5b9b51d49 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 19 Aug 2014 10:49:07 -0600 Subject: regmap: of_regmap_get_endian() cleanup Commit d647c199510c ("regmap: add DT endianness binding support") had some issues. Commit ba1b53feb8ca ("regmap: Fix DT endianess parsing logic") fixed the main problem. This patch fixes the other. Specifically, restore the overall default of REGMAP_ENDIAN_BIG if none of the config, DT, or the bus specify any endianness. Without this, of_regmap_get_endian() could return REGMAP_ENDIAN_DEFAULT, which the calling code can't handle. Since all busses do specify an endianness in the current code, this makes no difference right now, but I saw no justification in the patch description for removing this final default. Also, clean up the code a bit: * s/of_regmap_get_endian/regmap_get_endian/ since the function isn't DT- specific, even if the reason it was originally added was to add some DT-specific features. * After potentially reading an endianess specification from DT, the code checks whether DT did specify an endianness, and if so, returns it. Move this test outside the whole switch statement so that if the REGMAP_ENDIAN_REG case ever modifies *endian, this check will pick that up. This partially reverts part of commit ba1b53feb8ca ("regmap: Fix DT endianess parsing logic"), while maintaining the bug-fix that commit made to this code. * Make the comments briefer, and only refer to the specific action taken at their location. This makes most of the comments independent of DT, and easier to follow. Cc: Xiubo Li Cc: Javier Martinez Canillas Cc: Thierry Reding Fixes: d647c199510c ("regmap: add DT endianness binding support") Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 45e1a279ce1d2ff9b2b2fedf4cdced10c7ca3ab5 Change-Id: I7c53af02317b6cb52b42ed0dc7b5d8d77eaccc92 Reviewed-on: http://git.am.freescale.net:8181/19858 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d790447..ddd7154 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -437,7 +437,7 @@ enum regmap_endian_type { REGMAP_ENDIAN_VAL, }; -static int of_regmap_get_endian(struct device *dev, +static int regmap_get_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config, enum regmap_endian_type type, @@ -448,15 +448,7 @@ static int of_regmap_get_endian(struct device *dev, if (!endian || !config) return -EINVAL; - /* - * Firstly, try to parse the endianness from driver's config, - * this is to be compatible with the none DT or the old drivers. - * From the driver's config the endianness value maybe: - * REGMAP_ENDIAN_BIG, - * REGMAP_ENDIAN_LITTLE, - * REGMAP_ENDIAN_NATIVE, - * REGMAP_ENDIAN_DEFAULT. - */ + /* Retrieve the endianness specification from the regmap config */ switch (type) { case REGMAP_ENDIAN_REG: *endian = config->reg_format_endian; @@ -468,31 +460,17 @@ static int of_regmap_get_endian(struct device *dev, return -EINVAL; } - /* - * If the endianness parsed from driver config is - * REGMAP_ENDIAN_DEFAULT, that means maybe we are using the DT - * node to specify the endianness information. - */ + /* If the regmap config specified a non-default value, use that */ if (*endian != REGMAP_ENDIAN_DEFAULT) return 0; - /* - * Secondly, try to parse the endianness from DT node if the - * driver config does not specify it. - * From the DT node the endianness value maybe: - * REGMAP_ENDIAN_BIG, - * REGMAP_ENDIAN_LITTLE, - */ + /* Parse the device's DT node for an endianness specification */ switch (type) { case REGMAP_ENDIAN_VAL: if (of_property_read_bool(np, "big-endian")) *endian = REGMAP_ENDIAN_BIG; else if (of_property_read_bool(np, "little-endian")) *endian = REGMAP_ENDIAN_LITTLE; - - if (*endian != REGMAP_ENDIAN_DEFAULT) - return 0; - break; case REGMAP_ENDIAN_REG: break; @@ -500,10 +478,11 @@ static int of_regmap_get_endian(struct device *dev, return -EINVAL; } - /* - * Finally, try to parse the endianness from regmap bus config - * if in device's DT node the endianness property is absent. - */ + /* If the endianness was specified in DT, use that */ + if (*endian != REGMAP_ENDIAN_DEFAULT) + return 0; + + /* Retrieve the endianness specification from the bus config */ switch (type) { case REGMAP_ENDIAN_REG: if (bus && bus->reg_format_endian_default) @@ -517,6 +496,13 @@ static int of_regmap_get_endian(struct device *dev, return -EINVAL; } + /* If the bus specified a non-default value, use that */ + if (*endian != REGMAP_ENDIAN_DEFAULT) + return 0; + + /* Use this if no other value was found */ + *endian = REGMAP_ENDIAN_BIG; + return 0; } @@ -615,13 +601,13 @@ struct regmap *regmap_init(struct device *dev, map->reg_read = _regmap_bus_read; } - ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG, - ®_endian); + ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG, + ®_endian); if (ret) return ERR_PTR(ret); - ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL, - &val_endian); + ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL, + &val_endian); if (ret) return ERR_PTR(ret); -- cgit v0.10.2 From cf08d43a077f6f7e975885610bde40e2479e093b Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 26 Sep 2014 11:59:13 +0800 Subject: Split regmap_get_endian() in two functions Split regmap_get_endian() in two functions, regmap_get_reg_endian() and regmap_get_val_endian(). This allows to: - Get rid of the three switch()es on "type", incl. error handling in three "default" cases, - Get rid of the regmap_endian_type enum, - Get rid of the non-NULL check of "config" (regmap_init() already checks for that), - Get rid of the "endian" output parameters, and just return the regmap_endian enum value, as the functions can no longer fail. This saves 21 lines of code (despite the still-present one-comment-per-line over-documentation), and 30 bytes of code on ARM V7. Signed-off-by: Geert Uytterhoeven Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit cf673fbc6342b1c2310cdfdc4ed99f18f866b8e4 Change-Id: Ifea4f2c83977bb81b50853de097f52bb586fc6ce Reviewed-on: http://git.am.freescale.net:8181/19859 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ddd7154..baf3c41 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -432,78 +432,64 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } -enum regmap_endian_type { - REGMAP_ENDIAN_REG, - REGMAP_ENDIAN_VAL, -}; +static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus, + const struct regmap_config *config) +{ + enum regmap_endian endian; -static int regmap_get_endian(struct device *dev, - const struct regmap_bus *bus, - const struct regmap_config *config, - enum regmap_endian_type type, - enum regmap_endian *endian) + /* Retrieve the endianness specification from the regmap config */ + endian = config->reg_format_endian; + + /* If the regmap config specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* Retrieve the endianness specification from the bus config */ + if (bus && bus->reg_format_endian_default) + endian = bus->reg_format_endian_default; + + /* If the bus specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* Use this if no other value was found */ + return REGMAP_ENDIAN_BIG; +} + +static enum regmap_endian regmap_get_val_endian(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) { struct device_node *np = dev->of_node; - - if (!endian || !config) - return -EINVAL; + enum regmap_endian endian; /* Retrieve the endianness specification from the regmap config */ - switch (type) { - case REGMAP_ENDIAN_REG: - *endian = config->reg_format_endian; - break; - case REGMAP_ENDIAN_VAL: - *endian = config->val_format_endian; - break; - default: - return -EINVAL; - } + endian = config->val_format_endian; /* If the regmap config specified a non-default value, use that */ - if (*endian != REGMAP_ENDIAN_DEFAULT) - return 0; + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; /* Parse the device's DT node for an endianness specification */ - switch (type) { - case REGMAP_ENDIAN_VAL: - if (of_property_read_bool(np, "big-endian")) - *endian = REGMAP_ENDIAN_BIG; - else if (of_property_read_bool(np, "little-endian")) - *endian = REGMAP_ENDIAN_LITTLE; - break; - case REGMAP_ENDIAN_REG: - break; - default: - return -EINVAL; - } + if (of_property_read_bool(np, "big-endian")) + endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + endian = REGMAP_ENDIAN_LITTLE; /* If the endianness was specified in DT, use that */ - if (*endian != REGMAP_ENDIAN_DEFAULT) - return 0; + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; /* Retrieve the endianness specification from the bus config */ - switch (type) { - case REGMAP_ENDIAN_REG: - if (bus && bus->reg_format_endian_default) - *endian = bus->reg_format_endian_default; - break; - case REGMAP_ENDIAN_VAL: - if (bus && bus->val_format_endian_default) - *endian = bus->val_format_endian_default; - break; - default: - return -EINVAL; - } + if (bus && bus->val_format_endian_default) + endian = bus->val_format_endian_default; /* If the bus specified a non-default value, use that */ - if (*endian != REGMAP_ENDIAN_DEFAULT) - return 0; + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; /* Use this if no other value was found */ - *endian = REGMAP_ENDIAN_BIG; - - return 0; + return REGMAP_ENDIAN_BIG; } /** @@ -601,15 +587,8 @@ struct regmap *regmap_init(struct device *dev, map->reg_read = _regmap_bus_read; } - ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG, - ®_endian); - if (ret) - return ERR_PTR(ret); - - ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL, - &val_endian); - if (ret) - return ERR_PTR(ret); + reg_endian = regmap_get_reg_endian(bus, config); + val_endian = regmap_get_val_endian(dev, bus, config); switch (config->reg_bits + map->reg_shift) { case 2: -- cgit v0.10.2 From 2deca15f919a5684296e4640cdd75766d22c4b2a Mon Sep 17 00:00:00 2001 From: Pankaj Dubey Date: Thu, 18 Sep 2014 15:12:20 +0530 Subject: regmap: fix NULL pointer dereference in regmap_get_val_endian Recents commits for getting reg endianness causing NULL pointer dereference if dev is passed NULL in regmap_init_mmio. This patch fixes this issue, and allows to parse reg endianness only if dev and dev->of_node exist. Signed-off-by: Pankaj Dubey Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 6e64b6ccc1e46932768e3bb8974fc2e5589bca7a Change-Id: Id9f795400bbda943dfd7b2fb71a752d4211b7540 Reviewed-on: http://git.am.freescale.net:8181/19860 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index baf3c41..b4116f2 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -460,7 +460,7 @@ static enum regmap_endian regmap_get_val_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config) { - struct device_node *np = dev->of_node; + struct device_node *np; enum regmap_endian endian; /* Retrieve the endianness specification from the regmap config */ @@ -470,15 +470,20 @@ static enum regmap_endian regmap_get_val_endian(struct device *dev, if (endian != REGMAP_ENDIAN_DEFAULT) return endian; - /* Parse the device's DT node for an endianness specification */ - if (of_property_read_bool(np, "big-endian")) - endian = REGMAP_ENDIAN_BIG; - else if (of_property_read_bool(np, "little-endian")) - endian = REGMAP_ENDIAN_LITTLE; + /* If the dev and dev->of_node exist try to get endianness from DT */ + if (dev && dev->of_node) { + np = dev->of_node; - /* If the endianness was specified in DT, use that */ - if (endian != REGMAP_ENDIAN_DEFAULT) - return endian; + /* Parse the device's DT node for an endianness specification */ + if (of_property_read_bool(np, "big-endian")) + endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + endian = REGMAP_ENDIAN_LITTLE; + + /* If the endianness was specified in DT, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + } /* Retrieve the endianness specification from the bus config */ if (bus && bus->val_format_endian_default) -- cgit v0.10.2 From f64f76329bbc962c10a6469819333e24985a4a44 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 23 May 2014 10:12:04 +0200 Subject: clocksource: ftm: Add FlexTimer Module (FTM) Timer devicetree Documentation The FTM binding could be used on Vybrid and LS1+, add a binding document for it. Signed-off-by: Xiubo Li Cc: Shawn Guo Cc: Jingchang Lu Signed-off-by: Daniel Lezcano --- This patch is pulled back from upstream: commit 12e499d0ed1fa09940a573e5a8cce52b556f3c38 Change-Id: I7e3a87401852f29e83768a44b49dc261a4d67b28 Reviewed-on: http://git.am.freescale.net:8181/19861 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt new file mode 100644 index 0000000..aa8c402 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt @@ -0,0 +1,31 @@ +Freescale FlexTimer Module (FTM) Timer + +Required properties: + +- compatible : should be "fsl,ftm-timer" +- reg : Specifies base physical address and size of the register sets for the + clock event device and clock source device. +- interrupts : Should be the clock event device interrupt. +- clocks : The clocks provided by the SoC to drive the timer, must contain an + entry for each entry in clock-names. +- clock-names : Must include the following entries: + o "ftm-evt" + o "ftm-src" + o "ftm-evt-counter-en" + o "ftm-src-counter-en" +- big-endian: One boolean property, the big endian mode will be in use if it is + present, or the little endian mode will be in use for all the device registers. + +Example: +ftm: ftm@400b8000 { + compatible = "fsl,ftm-timer"; + reg = <0x400b8000 0x1000 0x400b9000 0x1000>; + interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm-evt", "ftm-src", + "ftm-evt-counter-en", "ftm-src-counter-en"; + clocks = <&clks VF610_CLK_FTM2>, + <&clks VF610_CLK_FTM3>, + <&clks VF610_CLK_FTM2_EXT_FIX_EN>, + <&clks VF610_CLK_FTM3_EXT_FIX_EN>; + big-endian; +}; -- cgit v0.10.2 From c3a8cca08d67b4e3f10c1426c8d13a8c48c1caec Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 27 Feb 2014 17:39:49 +0800 Subject: pwm: Add Freescale FTM PWM driver support The FTM PWM device can be found on Vybrid VF610 Tower and Layerscape LS-1 SoCs. Signed-off-by: Xiubo Li Signed-off-by: Alison Wang Signed-off-by: Jingchang Lu Reviewed-by: Sascha Hauer Reviewed-by: Yuan Yao Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit b505183b5117ce149c65ae62f8c00e889acafa69 Change-Id: I49cb21cbeeb944adf54958d29101e95204f1d693 Reviewed-on: http://git.am.freescale.net:8181/19862 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 75840b5..ab88168 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -62,6 +62,16 @@ config PWM_BFIN To compile this driver as a module, choose M here: the module will be called pwm-bfin. +config PWM_FSL_FTM + tristate "Freescale FlexTimer Module (FTM) PWM support" + depends on OF + help + Generic FTM PWM framework driver for Freescale VF610 and + Layerscape LS-1 SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + config PWM_IMX tristate "i.MX PWM support" depends on ARCH_MXC diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 77a8c18..f383784 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o +obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c new file mode 100644 index 0000000..420169e --- /dev/null +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -0,0 +1,495 @@ +/* + * Freescale FlexTimer Module (FTM) PWM Driver + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTM_SC 0x00 +#define FTM_SC_CLK_MASK 0x3 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_PS_SHIFT 0 + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 + +#define FTM_CSC_BASE 0x0C +#define FTM_CSC_MSB BIT(5) +#define FTM_CSC_MSA BIT(4) +#define FTM_CSC_ELSB BIT(3) +#define FTM_CSC_ELSA BIT(2) +#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8)) + +#define FTM_CV_BASE 0x10 +#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8)) + +#define FTM_CNTIN 0x4C +#define FTM_STATUS 0x50 + +#define FTM_MODE 0x54 +#define FTM_MODE_FTMEN BIT(0) +#define FTM_MODE_INIT BIT(2) +#define FTM_MODE_PWMSYNC BIT(3) + +#define FTM_SYNC 0x58 +#define FTM_OUTINIT 0x5C +#define FTM_OUTMASK 0x60 +#define FTM_COMBINE 0x64 +#define FTM_DEADTIME 0x68 +#define FTM_EXTTRIG 0x6C +#define FTM_POL 0x70 +#define FTM_FMS 0x74 +#define FTM_FILTER 0x78 +#define FTM_FLTCTRL 0x7C +#define FTM_QDCTRL 0x80 +#define FTM_CONF 0x84 +#define FTM_FLTPOL 0x88 +#define FTM_SYNCONF 0x8C +#define FTM_INVCTRL 0x90 +#define FTM_SWOCTRL 0x94 +#define FTM_PWMLOAD 0x98 + +enum fsl_pwm_clk { + FSL_PWM_CLK_SYS, + FSL_PWM_CLK_FIX, + FSL_PWM_CLK_EXT, + FSL_PWM_CLK_CNTEN, + FSL_PWM_CLK_MAX +}; + +struct fsl_pwm_chip { + struct pwm_chip chip; + + struct mutex lock; + + unsigned int use_count; + unsigned int cnt_select; + unsigned int clk_ps; + + void __iomem *base; + + int period_ns; + + struct clk *clk[FSL_PWM_CLK_MAX]; +}; + +static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct fsl_pwm_chip, chip); +} + +static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + + return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); +} + +static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); +} + +static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc, + enum fsl_pwm_clk index) +{ + unsigned long sys_rate, cnt_rate; + unsigned long long ratio; + + sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]); + if (!sys_rate) + return -EINVAL; + + cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]); + if (!cnt_rate) + return -EINVAL; + + switch (index) { + case FSL_PWM_CLK_SYS: + fpc->clk_ps = 1; + break; + case FSL_PWM_CLK_FIX: + ratio = 2 * cnt_rate - 1; + do_div(ratio, sys_rate); + fpc->clk_ps = ratio; + break; + case FSL_PWM_CLK_EXT: + ratio = 4 * cnt_rate - 1; + do_div(ratio, sys_rate); + fpc->clk_ps = ratio; + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc, + unsigned long period_ns) +{ + unsigned long long c, c0; + + c = clk_get_rate(fpc->clk[fpc->cnt_select]); + c = c * period_ns; + do_div(c, 1000000000UL); + + do { + c0 = c; + do_div(c0, (1 << fpc->clk_ps)); + if (c0 <= 0xFFFF) + return (unsigned long)c0; + } while (++fpc->clk_ps < 8); + + return 0; +} + +static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc, + unsigned long period_ns, + enum fsl_pwm_clk index) +{ + int ret; + + ret = fsl_pwm_calculate_default_ps(fpc, index); + if (ret) { + dev_err(fpc->chip.dev, + "failed to calculate default prescaler: %d\n", + ret); + return 0; + } + + return fsl_pwm_calculate_cycles(fpc, period_ns); +} + +static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, + unsigned long period_ns) +{ + enum fsl_pwm_clk m0, m1; + unsigned long fix_rate, ext_rate, cycles; + + cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, + FSL_PWM_CLK_SYS); + if (cycles) { + fpc->cnt_select = FSL_PWM_CLK_SYS; + return cycles; + } + + fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]); + ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]); + + if (fix_rate > ext_rate) { + m0 = FSL_PWM_CLK_FIX; + m1 = FSL_PWM_CLK_EXT; + } else { + m0 = FSL_PWM_CLK_EXT; + m1 = FSL_PWM_CLK_FIX; + } + + cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0); + if (cycles) { + fpc->cnt_select = m0; + return cycles; + } + + fpc->cnt_select = m1; + + return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1); +} + +static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, + unsigned long period_ns, + unsigned long duty_ns) +{ + unsigned long long val, duty; + + val = readl(fpc->base + FTM_MOD); + duty = duty_ns * (val + 1); + do_div(duty, period_ns); + + return (unsigned long)duty; +} + +static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 val, period, duty; + + mutex_lock(&fpc->lock); + + /* + * The Freescale FTM controller supports only a single period for + * all PWM channels, therefore incompatible changes need to be + * refused. + */ + if (fpc->period_ns && fpc->period_ns != period_ns) { + dev_err(fpc->chip.dev, + "conflicting period requested for PWM %u\n", + pwm->hwpwm); + mutex_unlock(&fpc->lock); + return -EBUSY; + } + + if (!fpc->period_ns && duty_ns) { + period = fsl_pwm_calculate_period(fpc, period_ns); + if (!period) { + dev_err(fpc->chip.dev, "failed to calculate period\n"); + mutex_unlock(&fpc->lock); + return -EINVAL; + } + + val = readl(fpc->base + FTM_SC); + val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT); + val |= fpc->clk_ps; + writel(val, fpc->base + FTM_SC); + writel(period - 1, fpc->base + FTM_MOD); + + fpc->period_ns = period_ns; + } + + mutex_unlock(&fpc->lock); + + duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); + + writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm)); + writel(duty, fpc->base + FTM_CV(pwm->hwpwm)); + + return 0; +} + +static int fsl_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 val; + + val = readl(fpc->base + FTM_POL); + + if (polarity == PWM_POLARITY_INVERSED) + val |= BIT(pwm->hwpwm); + else + val &= ~BIT(pwm->hwpwm); + + writel(val, fpc->base + FTM_POL); + + return 0; +} + +static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) +{ + u32 val; + int ret; + + if (fpc->use_count != 0) + return 0; + + /* select counter clock source */ + val = readl(fpc->base + FTM_SC); + val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); + val |= FTM_SC_CLK(fpc->cnt_select); + writel(val, fpc->base + FTM_SC); + + ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); + if (ret) + return ret; + + ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); + if (ret) { + clk_disable_unprepare(fpc->clk[fpc->cnt_select]); + return ret; + } + + fpc->use_count++; + + return 0; +} + +static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 val; + int ret; + + mutex_lock(&fpc->lock); + val = readl(fpc->base + FTM_OUTMASK); + val &= ~BIT(pwm->hwpwm); + writel(val, fpc->base + FTM_OUTMASK); + + ret = fsl_counter_clock_enable(fpc); + mutex_unlock(&fpc->lock); + + return ret; +} + +static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) +{ + u32 val; + + /* + * already disabled, do nothing + */ + if (fpc->use_count == 0) + return; + + /* there are still users, so can't disable yet */ + if (--fpc->use_count > 0) + return; + + /* no users left, disable PWM counter clock */ + val = readl(fpc->base + FTM_SC); + val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); + writel(val, fpc->base + FTM_SC); + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); + clk_disable_unprepare(fpc->clk[fpc->cnt_select]); +} + +static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 val; + + mutex_lock(&fpc->lock); + val = readl(fpc->base + FTM_OUTMASK); + val |= BIT(pwm->hwpwm); + writel(val, fpc->base + FTM_OUTMASK); + + fsl_counter_clock_disable(fpc); + + val = readl(fpc->base + FTM_OUTMASK); + + if ((val & 0xFF) == 0xFF) + fpc->period_ns = 0; + + mutex_unlock(&fpc->lock); +} + +static const struct pwm_ops fsl_pwm_ops = { + .request = fsl_pwm_request, + .free = fsl_pwm_free, + .config = fsl_pwm_config, + .set_polarity = fsl_pwm_set_polarity, + .enable = fsl_pwm_enable, + .disable = fsl_pwm_disable, + .owner = THIS_MODULE, +}; + +static int fsl_pwm_init(struct fsl_pwm_chip *fpc) +{ + int ret; + + ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); + if (ret) + return ret; + + writel(0x00, fpc->base + FTM_CNTIN); + writel(0x00, fpc->base + FTM_OUTINIT); + writel(0xFF, fpc->base + FTM_OUTMASK); + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); + + return 0; +} + +static int fsl_pwm_probe(struct platform_device *pdev) +{ + struct fsl_pwm_chip *fpc; + struct resource *res; + int ret; + + fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); + if (!fpc) + return -ENOMEM; + + mutex_init(&fpc->lock); + + fpc->chip.dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fpc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fpc->base)) + return PTR_ERR(fpc->base); + + fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { + dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n"); + return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]); + } + + fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX])) + return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]); + + fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT])) + return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]); + + fpc->clk[FSL_PWM_CLK_CNTEN] = + devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN])) + return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]); + + fpc->chip.ops = &fsl_pwm_ops; + fpc->chip.of_xlate = of_pwm_xlate_with_flags; + fpc->chip.of_pwm_n_cells = 3; + fpc->chip.base = -1; + fpc->chip.npwm = 8; + + ret = pwmchip_add(&fpc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, fpc); + + return fsl_pwm_init(fpc); +} + +static int fsl_pwm_remove(struct platform_device *pdev) +{ + struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev); + + return pwmchip_remove(&fpc->chip); +} + +static const struct of_device_id fsl_pwm_dt_ids[] = { + { .compatible = "fsl,vf610-ftm-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids); + +static struct platform_driver fsl_pwm_driver = { + .driver = { + .name = "fsl-ftm-pwm", + .of_match_table = fsl_pwm_dt_ids, + }, + .probe = fsl_pwm_probe, + .remove = fsl_pwm_remove, +}; +module_platform_driver(fsl_pwm_driver); + +MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver"); +MODULE_AUTHOR("Xiubo Li "); +MODULE_ALIAS("platform:fsl-ftm-pwm"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 9973bda18dcbf256ebf5dbb048335e7a3b4d702f Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 27 Feb 2014 17:39:52 +0800 Subject: Documentation: Add device tree bindings for Freescale FTM PWM. This adds the binding documentation for Freescale FlexTimer Module (FTM) PWM driver under Documentation/devicetree/bindings/pwm/. Signed-off-by: Xiubo Li Reviewed-by: Sascha Hauer Reviewed-by: Yuan Yao Acked-by: Kumar Gala Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit 42586315b7b6e682bd4136a1a2bc2b1d50113487 Change-Id: I5bf1419bcfbb4bd84c6e3eb2285fad660835fda3 Reviewed-on: http://git.am.freescale.net:8181/19863 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt new file mode 100644 index 0000000..0bda229 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt @@ -0,0 +1,35 @@ +Freescale FlexTimer Module (FTM) PWM controller + +Required properties: +- compatible: Should be "fsl,vf610-ftm-pwm". +- reg: Physical base address and length of the controller's registers +- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of + the cells format. +- clock-names: Should include the following module clock source entries: + "ftm_sys" (module clock, also can be used as counter clock), + "ftm_ext" (external counter clock), + "ftm_fix" (fixed counter clock), + "ftm_cnt_clk_en" (external and fixed counter clock enable/disable). +- clocks: Must contain a phandle and clock specifier for each entry in + clock-names, please see clock/clock-bindings.txt for details of the property + values. +- pinctrl-names: Must contain a "default" entry. +- pinctrl-NNN: One property must exist for each entry in pinctrl-names. + See pinctrl/pinctrl-bindings.txt for details of the property values. + + +Example: + +pwm0: pwm@40038000 { + compatible = "fsl,vf610-ftm-pwm"; + reg = <0x40038000 0x1000>; + #pwm-cells = <3>; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&clks VF610_CLK_FTM0>, + <&clks VF610_CLK_FTM0_EXT_SEL>, + <&clks VF610_CLK_FTM0_FIX_SEL>, + <&clks VF610_CLK_FTM0_EXT_FIX_EN>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm0_1>; +}; -- cgit v0.10.2 From d0b49db18e9b8be523044f818a1213ce27ccc563 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 22 May 2014 08:05:20 +0800 Subject: pwm: fsl-ftm: set pwm_chip can_sleep flag The implementation of .config(), .enable() and .disable() operations in this driver may sleep, thus set pwm_chip can_sleep flag. Signed-off-by: Axel Lin Acked-by: Xiubo Li Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit 39fd3f99aba3f7683fc9b62e916e4c886a1cb6b0 Change-Id: I06b3dac78d4acc12d96c8d248814eaaf6dc0ca33 Reviewed-on: http://git.am.freescale.net:8181/19864 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 420169e..a18bc8f 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -454,6 +454,7 @@ static int fsl_pwm_probe(struct platform_device *pdev) fpc->chip.of_pwm_n_cells = 3; fpc->chip.base = -1; fpc->chip.npwm = 8; + fpc->chip.can_sleep = true; ret = pwmchip_add(&fpc->chip); if (ret < 0) { -- cgit v0.10.2 From 3a8ba0a31b0bab8f35d9e0a86667bab54b8430b5 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 19 Aug 2014 12:38:01 +0800 Subject: pwm: fsl-ftm: Clean up the code This patch intends to prepare for converting to direct regmap API usage. Signed-off-by: Xiubo Li Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit cd6d92d2aa1556b22cd05acbc5f2cc8e5caafcc4 Change-Id: Iffdb310f7254bebfe65ca81594b25e4cbd546c9a Reviewed-on: http://git.am.freescale.net:8181/19865 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index a18bc8f..96982da 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -21,11 +21,10 @@ #include #define FTM_SC 0x00 -#define FTM_SC_CLK_MASK 0x3 -#define FTM_SC_CLK_SHIFT 3 -#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK_MASK_SHIFT 3 +#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) +#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) #define FTM_SC_PS_MASK 0x7 -#define FTM_SC_PS_SHIFT 0 #define FTM_CNT 0x04 #define FTM_MOD 0x08 @@ -258,7 +257,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } val = readl(fpc->base + FTM_SC); - val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT); + val &= ~FTM_SC_PS_MASK; val |= fpc->clk_ps; writel(val, fpc->base + FTM_SC); writel(period - 1, fpc->base + FTM_MOD); @@ -305,7 +304,7 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) /* select counter clock source */ val = readl(fpc->base + FTM_SC); - val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); + val &= ~FTM_SC_CLK_MASK; val |= FTM_SC_CLK(fpc->cnt_select); writel(val, fpc->base + FTM_SC); @@ -357,7 +356,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) /* no users left, disable PWM counter clock */ val = readl(fpc->base + FTM_SC); - val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); + val &= ~FTM_SC_CLK_MASK; writel(val, fpc->base + FTM_SC); clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); -- cgit v0.10.2 From a6678fc8ee1624936f865ce4ee784f83a4428007 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 19 Aug 2014 12:38:02 +0800 Subject: pwm: fsl-ftm: Convert to direct regmap API usage The regmap core supports different endian modes for devices. This patch convert to direct regmap API usage, preparing to support big endianness for LS1 SoC. Using the regmap framework it will be easy to support devices that only differ in endianness with the same device driver. Signed-off-by: Xiubo Li Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit 42fa98a9c3609c1aff466cb847e421c611cc9157 Change-Id: If7905b70eed8296dffa7ebde0dc03e951fcb7536 Reviewed-on: http://git.am.freescale.net:8181/19866 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 96982da..0f2cc7e 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #define FTM_SC 0x00 @@ -82,7 +83,7 @@ struct fsl_pwm_chip { unsigned int cnt_select; unsigned int clk_ps; - void __iomem *base; + struct regmap *regmap; int period_ns; @@ -218,10 +219,11 @@ static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, unsigned long period_ns, unsigned long duty_ns) { - unsigned long long val, duty; + unsigned long long duty; + u32 val; - val = readl(fpc->base + FTM_MOD); - duty = duty_ns * (val + 1); + regmap_read(fpc->regmap, FTM_MOD, &val); + duty = (unsigned long long)duty_ns * (val + 1); do_div(duty, period_ns); return (unsigned long)duty; @@ -231,7 +233,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); - u32 val, period, duty; + u32 period, duty; mutex_lock(&fpc->lock); @@ -256,11 +258,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return -EINVAL; } - val = readl(fpc->base + FTM_SC); - val &= ~FTM_SC_PS_MASK; - val |= fpc->clk_ps; - writel(val, fpc->base + FTM_SC); - writel(period - 1, fpc->base + FTM_MOD); + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, + fpc->clk_ps); + regmap_write(fpc->regmap, FTM_MOD, period - 1); fpc->period_ns = period_ns; } @@ -269,8 +269,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); - writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm)); - writel(duty, fpc->base + FTM_CV(pwm->hwpwm)); + regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), + FTM_CSC_MSB | FTM_CSC_ELSB); + regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); return 0; } @@ -282,31 +283,28 @@ static int fsl_pwm_set_polarity(struct pwm_chip *chip, struct fsl_pwm_chip *fpc = to_fsl_chip(chip); u32 val; - val = readl(fpc->base + FTM_POL); + regmap_read(fpc->regmap, FTM_POL, &val); if (polarity == PWM_POLARITY_INVERSED) val |= BIT(pwm->hwpwm); else val &= ~BIT(pwm->hwpwm); - writel(val, fpc->base + FTM_POL); + regmap_write(fpc->regmap, FTM_POL, val); return 0; } static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) { - u32 val; int ret; if (fpc->use_count != 0) return 0; /* select counter clock source */ - val = readl(fpc->base + FTM_SC); - val &= ~FTM_SC_CLK_MASK; - val |= FTM_SC_CLK(fpc->cnt_select); - writel(val, fpc->base + FTM_SC); + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, + FTM_SC_CLK(fpc->cnt_select)); ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); if (ret) @@ -326,13 +324,10 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); - u32 val; int ret; mutex_lock(&fpc->lock); - val = readl(fpc->base + FTM_OUTMASK); - val &= ~BIT(pwm->hwpwm); - writel(val, fpc->base + FTM_OUTMASK); + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); ret = fsl_counter_clock_enable(fpc); mutex_unlock(&fpc->lock); @@ -342,8 +337,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) { - u32 val; - /* * already disabled, do nothing */ @@ -355,9 +348,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) return; /* no users left, disable PWM counter clock */ - val = readl(fpc->base + FTM_SC); - val &= ~FTM_SC_CLK_MASK; - writel(val, fpc->base + FTM_SC); + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0); clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); clk_disable_unprepare(fpc->clk[fpc->cnt_select]); @@ -369,14 +360,12 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) u32 val; mutex_lock(&fpc->lock); - val = readl(fpc->base + FTM_OUTMASK); - val |= BIT(pwm->hwpwm); - writel(val, fpc->base + FTM_OUTMASK); + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), + BIT(pwm->hwpwm)); fsl_counter_clock_disable(fpc); - val = readl(fpc->base + FTM_OUTMASK); - + regmap_read(fpc->regmap, FTM_OUTMASK, &val); if ((val & 0xFF) == 0xFF) fpc->period_ns = 0; @@ -401,19 +390,28 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc) if (ret) return ret; - writel(0x00, fpc->base + FTM_CNTIN); - writel(0x00, fpc->base + FTM_OUTINIT); - writel(0xFF, fpc->base + FTM_OUTMASK); + regmap_write(fpc->regmap, FTM_CNTIN, 0x00); + regmap_write(fpc->regmap, FTM_OUTINIT, 0x00); + regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF); clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); return 0; } +static const struct regmap_config fsl_pwm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FTM_PWMLOAD, +}; + static int fsl_pwm_probe(struct platform_device *pdev) { struct fsl_pwm_chip *fpc; struct resource *res; + void __iomem *base; int ret; fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); @@ -425,9 +423,16 @@ static int fsl_pwm_probe(struct platform_device *pdev) fpc->chip.dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fpc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(fpc->base)) - return PTR_ERR(fpc->base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &fsl_pwm_regmap_config); + if (IS_ERR(fpc->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(fpc->regmap); + } fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { -- cgit v0.10.2 From 15e87bc300c4ce9ebc1acc5627078eabd1e037f2 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 19 Aug 2014 12:38:03 +0800 Subject: pwm: fsl-ftm: Document 'big-endian' property The same FTM PWM device can have a different endianness on different SoCs. The device tree provides a property to describing this so that an operating system device driver can handle all variants of the device. Refer to the table below for the endianness of the FTM PWM block as integrated into the existing SoCs: SoC | FTM-PWM endianness --------+------------------- Vybrid | LE LS1 | BE LS2 | LE Signed-off-by: Xiubo Li Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit a535e2e0debc2255fcf60a11d73fbb0534454cc3 Change-Id: Icf9f1efe7a4fd121cb5568f8552c01043110b108 Reviewed-on: http://git.am.freescale.net:8181/19867 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt index 0bda229..3899d6a 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt @@ -1,5 +1,20 @@ Freescale FlexTimer Module (FTM) PWM controller +The same FTM PWM device can have a different endianness on different SoCs. The +device tree provides a property to describing this so that an operating system +device driver can handle all variants of the device. Refer to the table below +for the endianness of the FTM PWM block as integrated into the existing SoCs: + + SoC | FTM-PWM endianness + --------+------------------- + Vybrid | LE + LS1 | BE + LS2 | LE + +Please see ../regmap/regmap.txt for more detail about how to specify endian +modes in device tree. + + Required properties: - compatible: Should be "fsl,vf610-ftm-pwm". - reg: Physical base address and length of the controller's registers @@ -16,7 +31,8 @@ Required properties: - pinctrl-names: Must contain a "default" entry. - pinctrl-NNN: One property must exist for each entry in pinctrl-names. See pinctrl/pinctrl-bindings.txt for details of the property values. - +- big-endian: Boolean property, required if the FTM PWM registers use a big- + endian rather than little-endian layout. Example: @@ -32,4 +48,5 @@ pwm0: pwm@40038000 { <&clks VF610_CLK_FTM0_EXT_FIX_EN>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pwm0_1>; + big-endian; }; -- cgit v0.10.2 From 3c1adef68a2fa5f03ef0b6af293a770782913172 Mon Sep 17 00:00:00 2001 From: Suresh Gupta Date: Fri, 26 Sep 2014 00:02:56 +0530 Subject: arm : dts : ls1021a : Modify USB3.0 dts node Signed-off-by: Suresh Gupta Change-Id: I7aa37e4914623a303eb520c6d8fd6d4f84e9ddb2 Reviewed-on: http://git.am.freescale.net:8181/19815 Tested-by: Review Code-CDREVIEW Reviewed-by: Jingchang Lu Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index 8c49ad4..22cf9b2 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -548,19 +548,11 @@ phy_type = "ulpi"; }; - usb@3100000 { - compatible = "fsl,fsl-dwc3"; - #address-cells = <2>; - #size-cells = <2>; - ranges; - - dwc3 { - compatible = "snps,dwc3"; - reg = <0x0 0x3100000 0x0 0x10000>; - interrupts = ; - dr_mode = "host"; - maximum-speed = "high-speed"; - }; + usb3@3100000 { + compatible = "snps,dwc3"; + reg = <0x0 0x3100000 0x0 0x10000>; + interrupts = ; + dr_mode = "host"; }; }; -- cgit v0.10.2 From 74b7f31fe4fb995171a3132d9cfa44398cbcc8bd Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 26 Sep 2014 13:36:28 +0800 Subject: clocksource: Add Freescale FlexTimer Module (FTM) timer support The Freescale FlexTimer Module time reference is a 16-bit counter that can be used as an unsigned or signed increase counter. CNTIN defines the starting value of the count and MOD defines the final value of the count. The value of CNTIN is loaded into the FTM counter, and the counter increments until the value of MOD is reached, at which point the counter is reloaded with the value of CNTIN. That's also when an overflow interrupt will be generated. Here using the 'evt' prefix or postfix as clock event device and the 'src' as clock source device. Signed-off-by: Xiubo Li Cc: Shawn Guo Cc: Jingchang Lu Signed-off-by: Daniel Lezcano --- This patch is pulled back from upstream: commit 2529c3a330797000d699d70c9a65b8525c6652de Change-Id: I9a224167f82b13b2f868af82f9bdc5dc1ebb5e48 Reviewed-on: http://git.am.freescale.net:8181/19870 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 971d796..f60e242 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -106,6 +106,11 @@ config CLKSRC_SAMSUNG_PWM for all devicetree enabled platforms. This driver will be needed only on systems that do not have the Exynos MCT available. +config FSL_FTM_TIMER + bool + help + Support for Freescale FlexTimer Module (FTM) timer. + config VF_PIT_TIMER bool help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 704d6d3..c3031f8 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o +obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c new file mode 100644 index 0000000..454227d --- /dev/null +++ b/drivers/clocksource/fsl_ftm_timer.c @@ -0,0 +1,367 @@ +/* + * Freescale FlexTimer Module (FTM) timer driver. + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTM_SC 0x00 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_TOIE BIT(6) +#define FTM_SC_TOF BIT(7) + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 +#define FTM_CNTIN 0x4C + +#define FTM_PS_MAX 7 + +struct ftm_clock_device { + void __iomem *clksrc_base; + void __iomem *clkevt_base; + unsigned long periodic_cyc; + unsigned long ps; + bool big_endian; +}; + +static struct ftm_clock_device *priv; + +static inline u32 ftm_readl(void __iomem *addr) +{ + if (priv->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void ftm_writel(u32 val, void __iomem *addr) +{ + if (priv->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline void ftm_counter_enable(void __iomem *base) +{ + u32 val; + + /* select and enable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= priv->ps | FTM_SC_CLK(1); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_counter_disable(void __iomem *base) +{ + u32 val; + + /* disable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_acknowledge(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOF; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_enable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val |= FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_disable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_reset_counter(void __iomem *base) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + ftm_writel(0x00, base + FTM_CNT); +} + +static u64 ftm_read_sched_clock(void) +{ + return ftm_readl(priv->clksrc_base + FTM_CNT); +} + +static int ftm_set_next_event(unsigned long delta, + struct clock_event_device *unused) +{ + /* + * The CNNIN and MOD are all double buffer registers, writing + * to the MOD register latches the value into a buffer. The MOD + * register is updated with the value of its write buffer with + * the following scenario: + * a, the counter source clock is diabled. + */ + ftm_counter_disable(priv->clkevt_base); + + /* Force the value of CNTIN to be loaded into the FTM counter */ + ftm_reset_counter(priv->clkevt_base); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the delta - 1. + */ + ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD); + + ftm_counter_enable(priv->clkevt_base); + + ftm_irq_enable(priv->clkevt_base); + + return 0; +} + +static void ftm_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ftm_set_next_event(priv->periodic_cyc, evt); + break; + case CLOCK_EVT_MODE_ONESHOT: + ftm_counter_disable(priv->clkevt_base); + break; + default: + return; + } +} + +static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + ftm_irq_acknowledge(priv->clkevt_base); + + if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) { + ftm_irq_disable(priv->clkevt_base); + ftm_counter_disable(priv->clkevt_base); + } + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct clock_event_device ftm_clockevent = { + .name = "Freescale ftm timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = ftm_set_mode, + .set_next_event = ftm_set_next_event, + .rating = 300, +}; + +static struct irqaction ftm_timer_irq = { + .name = "Freescale ftm timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = ftm_evt_interrupt, + .dev_id = &ftm_clockevent, +}; + +static int __init ftm_clockevent_init(unsigned long freq, int irq) +{ + int err; + + ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clkevt_base + FTM_MOD); + + ftm_reset_counter(priv->clkevt_base); + + err = setup_irq(irq, &ftm_timer_irq); + if (err) { + pr_err("ftm: setup irq failed: %d\n", err); + return err; + } + + ftm_clockevent.cpumask = cpumask_of(0); + ftm_clockevent.irq = irq; + + clockevents_config_and_register(&ftm_clockevent, + freq / (1 << priv->ps), + 1, 0xffff); + + ftm_counter_enable(priv->clkevt_base); + + return 0; +} + +static int __init ftm_clocksource_init(unsigned long freq) +{ + int err; + + ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clksrc_base + FTM_MOD); + + ftm_reset_counter(priv->clksrc_base); + + sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps)); + err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm", + freq / (1 << priv->ps), 300, 16, + clocksource_mmio_readl_up); + if (err) { + pr_err("ftm: init clock source mmio failed: %d\n", err); + return err; + } + + ftm_counter_enable(priv->clksrc_base); + + return 0; +} + +static int __init __ftm_clk_init(struct device_node *np, char *cnt_name, + char *ftm_name) +{ + struct clk *clk; + int err; + + clk = of_clk_get_by_name(np, cnt_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) { + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + cnt_name, err); + return err; + } + + clk = of_clk_get_by_name(np, ftm_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + ftm_name, err); + + return clk_get_rate(clk); +} + +static unsigned long __init ftm_clk_init(struct device_node *np) +{ + unsigned long freq; + + freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt"); + if (freq <= 0) + return 0; + + freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src"); + if (freq <= 0) + return 0; + + return freq; +} + +static int __init ftm_calc_closest_round_cyc(unsigned long freq) +{ + priv->ps = 0; + + /* The counter register is only using the lower 16 bits, and + * if the 'freq' value is to big here, then the periodic_cyc + * may exceed 0xFFFF. + */ + do { + priv->periodic_cyc = DIV_ROUND_CLOSEST(freq, + HZ * (1 << priv->ps++)); + } while (priv->periodic_cyc > 0xFFFF); + + if (priv->ps > FTM_PS_MAX) { + pr_err("ftm: the prescaler is %lu > %d\n", + priv->ps, FTM_PS_MAX); + return -EINVAL; + } + + return 0; +} + +static void __init ftm_timer_init(struct device_node *np) +{ + unsigned long freq; + int irq; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return; + + priv->clkevt_base = of_iomap(np, 0); + if (!priv->clkevt_base) { + pr_err("ftm: unable to map event timer registers\n"); + goto err; + } + + priv->clksrc_base = of_iomap(np, 1); + if (!priv->clksrc_base) { + pr_err("ftm: unable to map source timer registers\n"); + goto err; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("ftm: unable to get IRQ from DT, %d\n", irq); + goto err; + } + + priv->big_endian = of_property_read_bool(np, "big-endian"); + + freq = ftm_clk_init(np); + if (!freq) + goto err; + + if (ftm_calc_closest_round_cyc(freq)) + goto err; + + if (ftm_clocksource_init(freq)) + goto err; + + if (ftm_clockevent_init(freq, irq)) + goto err; + + return; + +err: + kfree(priv); +} +CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init); -- cgit v0.10.2 From b2b0945488256e114c34e333750b70852cf65e9f Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 15 Jul 2014 12:23:03 +0800 Subject: regmap: Add the DT binding documentation for endianness Device-Tree binding for device endianness Index Device Endianness properties --------------------------------------------------- 1 BE 'big-endian' 2 LE 'little-endian' For one device driver, which will run in different scenarios above on different SoCs using the devicetree, we need one way to simplify this. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- This patch is pulled back from upstream: commit 275876e208e28abf4b96ec89030e482b1331ee75 Change-Id: I28bbebb0b8e191555b7a7b001d9f4d04453a3106 Reviewed-on: http://git.am.freescale.net:8181/19856 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/regmap/regmap.txt b/Documentation/devicetree/bindings/regmap/regmap.txt new file mode 100644 index 0000000..b494f8b --- /dev/null +++ b/Documentation/devicetree/bindings/regmap/regmap.txt @@ -0,0 +1,47 @@ +Device-Tree binding for regmap + +The endianness mode of CPU & Device scenarios: +Index Device Endianness properties +--------------------------------------------------- +1 BE 'big-endian' +2 LE 'little-endian' + +For one device driver, which will run in different scenarios above +on different SoCs using the devicetree, we need one way to simplify +this. + +Required properties: +- {big,little}-endian: these are boolean properties, if absent + meaning that the CPU and the Device are in the same endianness mode, + these properties are for register values and all the buffers only. + +Examples: +Scenario 1 : CPU in LE mode & device in LE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... +}; + +Scenario 2 : CPU in LE mode & device in BE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... + big-endian; +}; + +Scenario 3 : CPU in BE mode & device in BE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... +}; + +Scenario 4 : CPU in BE mode & device in LE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... + little-endian; +}; -- cgit v0.10.2 From aa0033b7d93653a38e583ffe944550bbd59bb1ae Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Wed, 23 Apr 2014 19:46:17 -0400 Subject: soc: Introduce drivers/soc place-holder for SOC specific drivers Based on earlier thread "https://lkml.org/lkml/2013/10/7/662" and discussion at Kernel Summit'2013, it was agreed to create 'driver/soc' for drivers which are quite SOC specific. Further discussion on the subject is in response to the earlier version of the patch is here: http://lwn.net/Articles/588942/ Cc: Greg Kroah-Hartman Cc: Kumar Gala Cc: Paul Walmsley Cc: Olof Johansson Cc: Arnd Bergmann Signed-off-by: Sandeep Nair Signed-off-by: Santosh Shilimkar Signed-off-by: Kumar Gala Change-Id: I53101824ec18c3cb58fa2db61060ae1247705fcd Reviewed-on: http://git.am.freescale.net:8181/19836 Reviewed-by: Emilian Medve Tested-by: Emilian Medve diff --git a/drivers/Kconfig b/drivers/Kconfig index e82d0ab..774d064 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -130,6 +130,8 @@ source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" +source "drivers/soc/Kconfig" + source "drivers/clk/Kconfig" source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 1af40e9..ae44784 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,6 +31,9 @@ obj-y += amba/ # really early. obj-$(CONFIG_DMADEVICES) += dma/ +# SOC specific infrastructure drivers. +obj-y += soc/ + obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_XEN) += xen/ diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig new file mode 100644 index 0000000..339baa8 --- /dev/null +++ b/drivers/soc/Kconfig @@ -0,0 +1,3 @@ +menu "SOC (System On Chip) specific Drivers" + +endmenu -- cgit v0.10.2 From 857ca623b184bed7f35bca812d815ec5437624f2 Mon Sep 17 00:00:00 2001 From: Wang Dongsheng Date: Tue, 19 Aug 2014 11:06:16 +0800 Subject: soc/fsl: add freescale dir for SOC specific drivers Some Freescale device driver need to move to soc, because these drivers are specific drivers. Before the soc/ to be created, the drivers had been there arch/ or drivers/misc/, but now soc/ dir is a better choice. Signed-off-by: Wang Dongsheng Change-Id: Iba92df04682bae45f1a40d20e4a848814d3d895a Reviewed-on: http://git.am.freescale.net:8181/19837 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 339baa8..522e7fc 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,3 +1,16 @@ menu "SOC (System On Chip) specific Drivers" +config FSL_SOC_DRIVERS + bool "Freescale Soc Drivers" + depends on FSL_SOC || ARCH_MXC + default n + help + Say y here to enable Freescale Soc Device Drivers support. + The Soc Drivers provides the device driver that is a specific block + or feature on Freescale platform. + +if FSL_SOC_DRIVERS + source "drivers/soc/fsl/Kconfig" +endif + endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile new file mode 100644 index 0000000..b40b228 --- /dev/null +++ b/drivers/soc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Freescale SOC specific device drivers. +# + +obj-$(CONFIG_FSL_SOC_DRIVERS) += fsl/ diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig new file mode 100644 index 0000000..72df9b3 --- /dev/null +++ b/drivers/soc/fsl/Kconfig @@ -0,0 +1,3 @@ +if ARM +source "drivers/soc/fsl/Kconfig.arm" +endif diff --git a/drivers/soc/fsl/Kconfig.arm b/drivers/soc/fsl/Kconfig.arm new file mode 100644 index 0000000..8c90b82 --- /dev/null +++ b/drivers/soc/fsl/Kconfig.arm @@ -0,0 +1,16 @@ +# +# Freescale ARM SOC Drivers +# + +config LS1_SOC_DRIVERS + bool "LS1021A Soc Drivers" + depends on SOC_LS1021A + default n + help + Say y here to enable Freescale LS1021A Soc Device Drivers support. + The Soc Drivers provides the device driver that is a specific block + or feature on LS1021A platform. + +if LS1_SOC_DRIVERS + source "drivers/soc/fsl/ls1/Kconfig" +endif diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile new file mode 100644 index 0000000..5ca0e1c --- /dev/null +++ b/drivers/soc/fsl/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for ls1 Soc specific device drivers. +# + +obj-$(CONFIG_LS1_SOC_DRIVERS) += ls1/ diff --git a/drivers/soc/fsl/ls1/Kconfig b/drivers/soc/fsl/ls1/Kconfig new file mode 100644 index 0000000..7556f44 --- /dev/null +++ b/drivers/soc/fsl/ls1/Kconfig @@ -0,0 +1,3 @@ +# +# LS-1 Soc drivers +# -- cgit v0.10.2 From 4d7ebd78a55df1a0cb6b04d62422f8c238af677f Mon Sep 17 00:00:00 2001 From: Wang Dongsheng Date: Thu, 25 Sep 2014 14:50:45 +0800 Subject: soc/fsl: add ftm alarm driver for ls1021a platform Only Ftm0 can be used when system going to deep sleep. So this driver to support ftm0 as a wakeup source. Signed-off-by: Wang Dongsheng Change-Id: Ib5fa7f7b72ab1f47fc80d1d816f112168ab84982 Reviewed-on: http://git.am.freescale.net:8181/19839 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/drivers/soc/fsl/ls1/Kconfig b/drivers/soc/fsl/ls1/Kconfig index 7556f44..c9b04c4 100644 --- a/drivers/soc/fsl/ls1/Kconfig +++ b/drivers/soc/fsl/ls1/Kconfig @@ -1,3 +1,11 @@ # # LS-1 Soc drivers # +config FTM_ALARM + bool "FTM alarm driver" + depends on SOC_LS1021A + default n + help + Say y here to enable FTM alarm support. The FTM alarm provides + alarm functions for wakeup system from deep sleep. There is only + one FTM can be used in ALARM(FTM 0). diff --git a/drivers/soc/fsl/ls1/Makefile b/drivers/soc/fsl/ls1/Makefile new file mode 100644 index 0000000..6299aa1 --- /dev/null +++ b/drivers/soc/fsl/ls1/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FTM_ALARM) += ftm_alarm.o diff --git a/drivers/soc/fsl/ls1/ftm_alarm.c b/drivers/soc/fsl/ls1/ftm_alarm.c new file mode 100644 index 0000000..5135964 --- /dev/null +++ b/drivers/soc/fsl/ls1/ftm_alarm.c @@ -0,0 +1,271 @@ +/* + * Freescale FlexTimer Module (FTM) Alarm driver. + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include + +#define FTM_SC 0x00 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_TOIE BIT(6) +#define FTM_SC_TOF BIT(7) + +#define FTM_SC_CLKS_FIXED_FREQ 0x02 + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 +#define FTM_CNTIN 0x4C + +#define FIXED_FREQ_CLK 32000 +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK) +#define MAX_COUNT_VAL 0xffff + +static void __iomem *ftm1_base; +static u32 alarm_freq; +static bool big_endian; + +static inline u32 ftm_readl(void __iomem *addr) +{ + if (big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void ftm_writel(u32 val, void __iomem *addr) +{ + if (big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline void ftm_counter_enable(void __iomem *base) +{ + u32 val; + + /* select and enable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ)); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_counter_disable(void __iomem *base) +{ + u32 val; + + /* disable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_acknowledge(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOF; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_enable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val |= FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_disable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_reset_counter(void __iomem *base) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + ftm_writel(0x00, base + FTM_CNT); +} + +static u32 time_to_cycle(unsigned long time) +{ + u32 cycle; + + cycle = time * alarm_freq; + if (cycle > MAX_COUNT_VAL) { + pr_err("Out of alarm range.\n"); + cycle = 0; + } + + return cycle; +} + +static u32 cycle_to_time(u32 cycle) +{ + return cycle / alarm_freq + 1; +} + +static void ftm_clean_alarm(void) +{ + ftm_counter_disable(ftm1_base); + + ftm_writel(0x00, ftm1_base + FTM_CNTIN); + ftm_writel(~0UL, ftm1_base + FTM_MOD); + + ftm_reset_counter(ftm1_base); +} + +static int ftm_set_alarm(u64 cycle) +{ + ftm_irq_disable(ftm1_base); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the cycle - 1. + */ + ftm_writel(cycle - 1, ftm1_base + FTM_MOD); + + ftm_counter_enable(ftm1_base); + + ftm_irq_enable(ftm1_base); + + return 0; +} + +static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id) +{ + ftm_irq_acknowledge(ftm1_base); + ftm_irq_disable(ftm1_base); + ftm_clean_alarm(); + + return IRQ_HANDLED; +} + +static ssize_t ftm_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 count, val; + count = ftm_readl(ftm1_base + FTM_MOD); + val = ftm_readl(ftm1_base + FTM_CNT); + val = (count & MAX_COUNT_VAL) - val; + val = cycle_to_time(val); + + return sprintf(buf, "%u\n", val); +} + +static ssize_t ftm_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u32 cycle; + unsigned long time; + + if (kstrtoul(buf, 0, &time)) + return -EINVAL; + + ftm_clean_alarm(); + + cycle = time_to_cycle(time); + if (!cycle) + return -EINVAL; + + ftm_set_alarm(cycle); + + return count; +} + +static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644, + ftm_alarm_show, ftm_alarm_store); + +static int ftm_alarm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *r; + int irq; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + ftm1_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(ftm1_base)) + return PTR_ERR(ftm1_base); + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("ftm: unable to get IRQ from DT, %d\n", irq); + return -EINVAL; + } + + big_endian = of_property_read_bool(np, "big-endian"); + + ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt, + IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &ftm_alarm_attributes); + if (ret) { + dev_err(&pdev->dev, "create sysfs fail.\n"); + return ret; + } + + alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV; + + ftm_clean_alarm(); + + return ret; +} + +static const struct of_device_id ftm_alarm_match[] = { + { .compatible = "fsl,ftm-alarm", }, + { }, +}; + +static struct platform_driver ftm_alarm_driver = { + .probe = ftm_alarm_probe, + .driver = { + .name = "ftm-alarm", + .owner = THIS_MODULE, + .of_match_table = ftm_alarm_match, + }, +}; + +static int __init ftm_alarm_init(void) +{ + return platform_driver_register(&ftm_alarm_driver); +} +device_initcall(ftm_alarm_init); -- cgit v0.10.2 From 623760ee756313c85dd5a43a5d639a76384bcede Mon Sep 17 00:00:00 2001 From: Wang Dongsheng Date: Thu, 25 Sep 2014 14:42:37 +0800 Subject: arm: dts: ls1021a: add wakeup device ftm0 node for ls1021a Add ftm0 node, cause of ftm0 can be set as a alarm before system going to deep sleep. Signed-off-by: Wang Dongsheng Change-Id: Ie337ec554f6acd625cd691a0e07ffb96807cfa10 Reviewed-on: http://git.am.freescale.net:8181/19838 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts index d162b8b..37dad85 100644 --- a/arch/arm/boot/dts/ls1021a-qds.dts +++ b/arch/arm/boot/dts/ls1021a-qds.dts @@ -111,6 +111,10 @@ status = "okay"; }; +&ftm0 { + status = "okay"; +}; + &i2c0 { status = "okay"; pca9547@77 { diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index 22cf9b2..ee95c8d 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -368,6 +368,14 @@ status = "disabled"; }; + ftm0: ftm0@29d0000 { + compatible = "fsl,ftm-alarm"; + reg = <0x0 0x29d0000 0x0 0x10000>; + interrupts = ; + big-endian; + status = "disabled"; + }; + pwm3: ftm@2a00000 { compatible = "fsl,vf610-ftm-pwm"; #pwm-cells = <3>; -- cgit v0.10.2 From f587e6e67f53225ff901342963ca9fccfc04b5ed Mon Sep 17 00:00:00 2001 From: Jia Hongtao Date: Wed, 6 Aug 2014 10:47:54 +0800 Subject: Add LTC2945 node to ls1021a-twr.dts Signed-off-by: Jia Hongtao Change-Id: I08008c3dcd2b85b0c54b9f9ee939287f57745517 Reviewed-on: http://git.am.freescale.net:8181/19641 Tested-by: Review Code-CDREVIEW Reviewed-by: Jingchang Lu Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts index 6d2fd91..a52be7b 100755 --- a/arch/arm/boot/dts/ls1021a-twr.dts +++ b/arch/arm/boot/dts/ls1021a-twr.dts @@ -71,6 +71,13 @@ status = "okay"; }; +&i2c2 { + status = "okay"; + monitor: ltc2945@67 { + reg = <0x67>; + }; +}; + &ifc { status = "okay"; #address-cells = <2>; -- cgit v0.10.2 From 9c10bc680c2f621b66f1ed1ccf35b44f75263385 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 26 Sep 2014 16:06:44 +0800 Subject: arm: configs: ls1021a: enable LTC2945, WDT and CLK_QOIRQ Signed-off-by: Jingchang Lu Change-Id: I7cde95ba1ca9acbf6d2d01649b3ecb3d08db02c1 Reviewed-on: http://git.am.freescale.net:8181/19912 Tested-by: Review Code-CDREVIEW Reviewed-by: Hongtao Jia Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/configs/ls1021a_defconfig b/arch/arm/configs/ls1021a_defconfig index 052e4f9..f1976de 100644 --- a/arch/arm/configs/ls1021a_defconfig +++ b/arch/arm/configs/ls1021a_defconfig @@ -114,7 +114,10 @@ CONFIG_SPI=y CONFIG_SPI_BITBANG=y CONFIG_GPIO_SYSFS=y CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_LTC2945=y CONFIG_SENSORS_INA2XX=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y # CONFIG_USB_SUPPORT is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y @@ -123,6 +126,7 @@ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS3232=y CONFIG_DMADEVICES=y CONFIG_FSL_EDMA=y +CONFIG_CLK_QORIQ=y CONFIG_MEMORY=y # CONFIG_RESET_CONTROLLER is not set CONFIG_EXT2_FS=y -- cgit v0.10.2 From bcb5f7f7f44f3df88f9703bc3caed9805175464a Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 24 Mar 2014 13:21:00 +0800 Subject: ASoC: fsl: Add SGTL5000 based audio machine driver. This is the SGTL5000 codec based audio driver supported with both playback and capture dai link implemention. This implementation is only compatible with device tree definition. Signed-off-by: Xiubo Li Signed-off-by: Alison Wang --- This patch has almost been reviewed okay in the mail list. However the maintainer Mark Brown suggested to use simple-card framework instead of individual machine driver. The discussion could be found: URL: http://lkml.iu.edu//hypermail/linux/kernel/1312.2/02078.html Since from Linux Kernel V3.12 to V3.14, it changes to much for ALSA ASoC framework for supporting simple-card, and there will be about more than 100 patches to backport it, and will also be many potiential risks, so here just using individual machine driver instead of simple-card on SDK branch. Change-Id: Ia37f752f8ee6362e726c3a9c602977aa7c0991b2 Reviewed-on: http://git.am.freescale.net:8181/19755 Tested-by: Review Code-CDREVIEW Reviewed-by: Jingchang Lu Reviewed-by: Huan Wang Reviewed-by: Zhengxiong Jin diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index ac4fe4e..f5ad1b2 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -217,3 +217,26 @@ config SND_SOC_IMX_MC13783 select SND_SOC_IMX_PCM_DMA endif # SND_IMX_SOC + +menuconfig SND_VF610_SOC + tristate "SoC Audio for Freescale VF610 CPUs" + select DMA_ENGINE + help + Say Y or M if you want to add support for codecs attached to + the VF610 CPUs. + + This will enable Freeacale SAI and SGTL5000 codec, and an extra + TWR-AUDIO-SGTL sub-board is needed for SGTL5000. + +if SND_VF610_SOC + +config SND_SOC_VF610_SGTL5000 + tristate "SoC Audio support for VF610 boards with SGTL5000" + depends on OF && I2C + select SND_SOC_FSL_SAI + select SND_SOC_SGTL5000 + help + Say Y if you want to add support for SoC audio on an VF610 board with + a SGTL5000 codec and a SAI. + +endif # SND_VF610_SOC diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index aaccbee..96110d1 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -22,6 +22,10 @@ obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o +# VF610 Platform Support +snd-soc-vf610-sgtl5000-objs := vf610-sgtl5000.o +obj-$(CONFIG_SND_SOC_VF610_SGTL5000) += snd-soc-vf610-sgtl5000.o + # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o diff --git a/sound/soc/fsl/vf610-sgtl5000.c b/sound/soc/fsl/vf610-sgtl5000.c new file mode 100644 index 0000000..d53d554 --- /dev/null +++ b/sound/soc/fsl/vf610-sgtl5000.c @@ -0,0 +1,169 @@ +/* + * Freescale ALSA SoC Audio using SGTL5000 as codec. + * + * Copyright 2012-2014 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + */ + +#include +#include +#include +#include +#include + +#include "../codecs/sgtl5000.h" +#include "fsl_sai.h" + +static unsigned int sysclk_rate; + +static int vf610_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct device *dev = rtd->card->dev; + + ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, + sysclk_rate, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "could not set codec driver clock params :%d\n", + ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, FSL_SAI_CLK_BUS, + sysclk_rate, SND_SOC_CLOCK_OUT); + if (ret) { + dev_err(dev, "could not set cpu dai driver clock params :%d\n", + ret); + return ret; + } + + return 0; +} + +static struct snd_soc_dai_link vf610_sgtl5000_dai = { + .name = "HiFi", + .stream_name = "HiFi", + .codec_dai_name = "sgtl5000", + .init = &vf610_sgtl5000_dai_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, +}; + +static const struct snd_soc_dapm_widget vf610_sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Microphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker Ext", NULL), +}; + +static struct snd_soc_card vf610_sgtl5000_card = { + .owner = THIS_MODULE, + .num_links = 1, + .dai_link = &vf610_sgtl5000_dai, + .dapm_widgets = vf610_sgtl5000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(vf610_sgtl5000_dapm_widgets), +}; + +static int vf610_sgtl5000_parse_dt(struct platform_device *pdev) +{ + int ret; + struct device_node *sai_np, *codec_np; + struct clk *codec_clk; + struct i2c_client *codec_dev; + struct device_node *np = pdev->dev.of_node; + + ret = snd_soc_of_parse_card_name(&vf610_sgtl5000_card, + "simple-audio-card,name"); + if (ret) + return ret; + + ret = snd_soc_of_parse_audio_routing(&vf610_sgtl5000_card, + "simple-audio-card,routing"); + if (ret) + return ret; + + sai_np = of_parse_phandle(np, "simple-audio-card,cpu", 0); + if (!sai_np) { + dev_err(&pdev->dev, "parsing \"saif-controller\" error\n"); + return -EINVAL; + } + vf610_sgtl5000_dai.cpu_of_node = sai_np; + vf610_sgtl5000_dai.platform_of_node = sai_np; + + codec_np = of_parse_phandle(np, "simple-audio-card,codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "parsing \"audio-codec\" error\n"); + ret = -EINVAL; + goto sai_np_fail; + } + vf610_sgtl5000_dai.codec_of_node = codec_np; + + codec_dev = of_find_i2c_device_by_node(codec_np); + if (!codec_dev) { + dev_err(&pdev->dev, "failed to find codec platform device\n"); + ret = PTR_ERR(codec_dev); + goto codec_np_fail; + } + + codec_clk = devm_clk_get(&codec_dev->dev, NULL); + if (IS_ERR(codec_clk)) { + dev_err(&pdev->dev, "failed to get codec clock\n"); + ret = PTR_ERR(codec_clk); + goto codec_np_fail; + } + + sysclk_rate = clk_get_rate(codec_clk); + +codec_np_fail: + of_node_put(codec_np); +sai_np_fail: + of_node_put(sai_np); + + return ret; +} + +static int vf610_sgtl5000_probe(struct platform_device *pdev) +{ + int ret; + + vf610_sgtl5000_card.dev = &pdev->dev; + + ret = vf610_sgtl5000_parse_dt(pdev); + if (ret) { + dev_err(&pdev->dev, "parse sgtl5000 device tree failed :%d\n", + ret); + return ret; + } + + ret = snd_soc_register_card(&vf610_sgtl5000_card); + if (ret) { + dev_err(&pdev->dev, "TWR-AUDIO-SGTL board required :%d\n", + ret); + return ret; + } + + return 0; +} + +static const struct of_device_id vf610_sgtl5000_dt_ids[] = { + { .compatible = "fsl,vf610-sgtl5000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_sgtl5000_dt_ids); + +static struct platform_driver vf610_sgtl5000_driver = { + .probe = vf610_sgtl5000_probe, + .driver = { + .name = "vf610-sgtl5000", + .owner = THIS_MODULE, + .of_match_table = vf610_sgtl5000_dt_ids, + }, +}; +module_platform_driver(vf610_sgtl5000_driver); + +MODULE_DESCRIPTION("Freescale SGTL5000 ASoC driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From cde55c2fe38a7162e97db271c53988b7c5e5657d Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Thu, 25 Sep 2014 12:53:49 +0800 Subject: of/irq: Add of_irq_parse_and_map_pci() and related function The patch addes of_irq_parse_and_map_pci() and related function which will be used by Layerscape PCIe driver. of_irq_parse_raw() and of_irq_parse_one() are copied from Linux 3.17 drivers/of/irq.c. The related commit ID include: 7dc2e1134a22dc242175d5321c0c9e97d16eb87b 2361613206e66ce59cc0e08efa8d98ec15b84ed1 624cfca534f9b1ffb1326617b4e973a3d5ecff4a a9ecdc0fdc54aa499604dbd43132988effcac9b4 355e62f5ad12b005c862838156262eb2df2f8dff a7c194b007ec40a130207e9ace9cecf598fc6ac5 0c02c8007ea5554d028f99fd3e29fc201fdeeab3 530210c7814e83564c7ca7bca8192515042c0b63 of_irq_parse_pci() and of_irq_parse_and_map_pci() are copied from Linux 3.17 drivers/of/of_pci_irq.c The related commit ID include: 98d9f30c820d509145757e6ecbc36013aa02f7bc 2361613206e66ce59cc0e08efa8d98ec15b84ed1 16b84e5a505c790538e534ad8dfda9c288691e40 0c02c8007ea5554d028f99fd3e29fc201fdeeab3 530210c7814e83564c7ca7bca8192515042c0b63 irq_create_of_mapping_new() is copied from Linux 3.17 kernel/irq/irqdomain.c irq_create_of_mapping(). The related commit ID include: e6d30ab1e7d1281784672c0fc2ffa385cfb7279e The copyright is owned by the author of the related commit ID You can find out the detailed copyright information based on the commit ID in the upstream repository. Signed-off-by: Minghuan Lian Change-Id: I24bcaaea4c6cbe43229bccaceb80e74f57a9ef93 Reviewed-on: http://git.am.freescale.net:8181/19677 Tested-by: Review Code-CDREVIEW Reviewed-by: Yang Li Reviewed-by: Zhengxiong Jin diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 1752988..526cde6 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -79,6 +79,211 @@ struct device_node *of_irq_find_parent(struct device_node *child) } /** + * of_irq_parse_raw - Low level interrupt tree parsing + * @parent: the device interrupt parent + * @addr: address specifier (start of "reg" property of the device) + * in be32 format + * @out_irq: structure of_irq updated by this function + * + * Returns 0 on success and a negative number on error + * + * This function is a low-level interrupt tree walking function. It + * can be used to do a partial walk with synthetized reg and interrupts + * properties, for example when resolving PCI interrupts when no device + * node exist for the parent. It takes an interrupt specifier structure as + * input, walks the tree looking for any interrupt-map properties, translates + * the specifier for each map, and then returns the translated map. + */ +int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) +{ + struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + __be32 initial_match_array[MAX_PHANDLE_ARGS]; + const __be32 *match_array = initial_match_array; + const __be32 *tmp, *imap, *imask; + const __be32 dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 }; + u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; + int imaplen, match, i; + +#ifdef DEBUG + of_print_phandle_args("of_irq_parse_raw: ", out_irq); +#endif + + ipar = of_node_get(out_irq->np); + + /* First get the #interrupt-cells property of the current cursor + * that tells us how to interpret the passed-in intspec. If there + * is none, we are nice and just walk up the tree + */ + do { + tmp = of_get_property(ipar, "#interrupt-cells", NULL); + if (tmp != NULL) { + intsize = be32_to_cpu(*tmp); + break; + } + tnode = ipar; + ipar = of_irq_find_parent(ipar); + of_node_put(tnode); + } while (ipar); + if (ipar == NULL) { + pr_debug(" -> no parent found !\n"); + goto fail; + } + + pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", + of_node_full_name(ipar), intsize); + + if (out_irq->args_count != intsize) + return -EINVAL; + + /* Look for this #address-cells. We have to implement the old linux + * trick of looking for the parent here as some device-trees rely on it + */ + old = of_node_get(ipar); + do { + tmp = of_get_property(old, "#address-cells", NULL); + tnode = of_get_parent(old); + of_node_put(old); + old = tnode; + } while (old && tmp == NULL); + of_node_put(old); + old = NULL; + addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); + + pr_debug(" -> addrsize=%d\n", addrsize); + + /* Range check so that the temporary buffer doesn't overflow */ + if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)) + goto fail; + + /* Precalculate the match array - this simplifies match loop */ + for (i = 0; i < addrsize; i++) + initial_match_array[i] = addr ? addr[i] : 0; + for (i = 0; i < intsize; i++) + initial_match_array[addrsize + i] = + cpu_to_be32(out_irq->args[i]); + + /* Now start the actual "proper" walk of the interrupt tree */ + while (ipar != NULL) { + /* Now check if cursor is an interrupt-controller and if it is + * then we are done + */ + if (of_get_property(ipar, "interrupt-controller", NULL) != + NULL) { + pr_debug(" -> got it !\n"); + return 0; + } + + /* + * interrupt-map parsing does not work without a reg + * property when #address-cells != 0 + */ + if (addrsize && !addr) { + pr_debug(" -> no reg passed in when needed !\n"); + goto fail; + } + + /* Now look for an interrupt-map */ + imap = of_get_property(ipar, "interrupt-map", &imaplen); + /* No interrupt map, check for an interrupt parent */ + if (imap == NULL) { + pr_debug(" -> no map, getting parent\n"); + newpar = of_irq_find_parent(ipar); + goto skiplevel; + } + imaplen /= sizeof(u32); + + /* Look for a mask */ + imask = of_get_property(ipar, "interrupt-map-mask", NULL); + if (!imask) + imask = dummy_imask; + + /* Parse interrupt-map */ + match = 0; + while (imaplen > (addrsize + intsize + 1) && !match) { + /* Compare specifiers */ + match = 1; + for (i = 0; i < (addrsize + intsize); i++, imaplen--) + match &= !((match_array[i] ^ *imap++) & + imask[i]); + + pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); + + /* Get the interrupt parent */ + if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) + newpar = of_node_get(of_irq_dflt_pic); + else + newpar = of_find_node_by_phandle( + be32_to_cpup(imap)); + imap++; + --imaplen; + + /* Check if not found */ + if (newpar == NULL) { + pr_debug(" -> imap parent not found !\n"); + goto fail; + } + + if (!of_device_is_available(newpar)) + match = 0; + + /* Get #interrupt-cells and #address-cells of new + * parent + */ + tmp = of_get_property(newpar, "#interrupt-cells", NULL); + if (tmp == NULL) { + pr_debug(" -> parent lacks #interrupt-cells!\n"); + goto fail; + } + newintsize = be32_to_cpu(*tmp); + tmp = of_get_property(newpar, "#address-cells", NULL); + newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); + + pr_debug(" -> newintsize=%d, newaddrsize=%d\n", + newintsize, newaddrsize); + + /* Check for malformed properties */ + if (WARN_ON(newaddrsize + newintsize > + MAX_PHANDLE_ARGS)) + goto fail; + if (imaplen < (newaddrsize + newintsize)) + goto fail; + + imap += newaddrsize + newintsize; + imaplen -= newaddrsize + newintsize; + + pr_debug(" -> imaplen=%d\n", imaplen); + } + if (!match) + goto fail; + + /* + * Successfully parsed an interrrupt-map translation; copy new + * interrupt specifier into the out_irq structure + */ + out_irq->np = newpar; + + match_array = imap - newaddrsize - newintsize; + for (i = 0; i < newintsize; i++) + out_irq->args[i] = be32_to_cpup(imap - newintsize + i); + out_irq->args_count = intsize = newintsize; + addrsize = newaddrsize; + +skiplevel: + /* Iterate again with new parent */ + pr_debug(" -> new parent: %s\n", of_node_full_name(newpar)); + of_node_put(ipar); + ipar = newpar; + newpar = NULL; + } + fail: + of_node_put(ipar); + of_node_put(newpar); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(of_irq_parse_raw); + +/** * of_irq_map_raw - Low level interrupt tree parsing * @parent: the device interrupt parent * @intspec: interrupt specifier ("interrupts" property of the device) @@ -272,6 +477,77 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, EXPORT_SYMBOL_GPL(of_irq_map_raw); /** + * of_irq_parse_one - Resolve an interrupt for a device + * @device: the device whose interrupt is to be resolved + * @index: index of the interrupt to resolve + * @out_irq: structure of_irq filled by this function + * + * This function resolves an interrupt for a node by walking the interrupt tree, + * finding which interrupt controller node it is attached to, and returning the + * interrupt specifier that can be used to retrieve a Linux IRQ number. + */ +int of_irq_parse_one(struct device_node *device, int index, + struct of_phandle_args *out_irq) +{ + struct device_node *p; + const __be32 *intspec, *tmp, *addr; + u32 intsize, intlen; + int i, res = -EINVAL; + + pr_debug("of_irq_parse_one: dev=%s, index=%d\n", + of_node_full_name(device), index); + + /* Get the reg property (if any) */ + addr = of_get_property(device, "reg", NULL); + + /* Try the new-style interrupts-extended first */ + res = of_parse_phandle_with_args(device, "interrupts-extended", + "#interrupt-cells", index, out_irq); + if (!res) + return of_irq_parse_raw(addr, out_irq); + + /* Get the interrupts property */ + intspec = of_get_property(device, "interrupts", &intlen); + if (intspec == NULL) + return -EINVAL; + + intlen /= sizeof(*intspec); + + pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); + + /* Look for the interrupt parent. */ + p = of_irq_find_parent(device); + if (p == NULL) + return -EINVAL; + + /* Get size of interrupt specifier */ + tmp = of_get_property(p, "#interrupt-cells", NULL); + if (tmp == NULL) + goto out; + intsize = be32_to_cpu(*tmp); + + pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); + + /* Check index */ + if ((index + 1) * intsize > intlen) + goto out; + + /* Copy intspec into irq structure */ + intspec += index * intsize; + out_irq->np = p; + out_irq->args_count = intsize; + for (i = 0; i < intsize; i++) + out_irq->args[i] = be32_to_cpup(intspec++); + + /* Check if there are any interrupt-map translations to process */ + res = of_irq_parse_raw(addr, out_irq); + out: + of_node_put(p); + return res; +} +EXPORT_SYMBOL_GPL(of_irq_parse_one); + +/** * of_irq_map_one - Resolve an interrupt for a device * @device: the device whose interrupt is to be resolved * @index: index of the interrupt to resolve diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 6770538..3843b46 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -91,3 +91,116 @@ int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq) return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq); } EXPORT_SYMBOL_GPL(of_irq_map_pci); + +/** + * of_irq_parse_pci - Resolve the interrupt for a PCI device + * @pdev: the device whose interrupt is to be resolved + * @out_irq: structure of_irq filled by this function + * + * This function resolves the PCI interrupt for a given PCI device. If a + * device-node exists for a given pci_dev, it will use normal OF tree + * walking. If not, it will implement standard swizzling and walk up the + * PCI tree until an device-node is found, at which point it will finish + * resolving using the OF tree walking. + */ +int of_irq_parse_pci(const struct pci_dev *pdev, + struct of_phandle_args *out_irq) +{ + struct device_node *dn, *ppnode; + struct pci_dev *ppdev; + __be32 laddr[3]; + u8 pin; + int rc; + + /* Check if we have a device node, if yes, fallback to standard + * device tree parsing + */ + dn = pci_device_to_OF_node(pdev); + if (dn) { + rc = of_irq_parse_one(dn, 0, out_irq); + if (!rc) + return rc; + } + + /* Ok, we don't, time to have fun. Let's start by building up an + * interrupt spec. we assume #interrupt-cells is 1, which is standard + * for PCI. If you do different, then don't use that routine. + */ + rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); + if (rc != 0) + return rc; + /* No pin, exit */ + if (pin == 0) + return -ENODEV; + + /* Now we walk up the PCI tree */ + for (;;) { + /* Get the pci_dev of our parent */ + ppdev = pdev->bus->self; + + /* Ouch, it's a host bridge... */ + if (ppdev == NULL) { + ppnode = pci_bus_to_OF_node(pdev->bus); + + /* No node for host bridge ? give up */ + if (ppnode == NULL) + return -EINVAL; + } else { + /* We found a P2P bridge, check if it has a node */ + ppnode = pci_device_to_OF_node(ppdev); + } + + /* Ok, we have found a parent with a device-node, hand over to + * the OF parsing code. + * We build a unit address from the linux device to be used for + * resolution. Note that we use the linux bus number which may + * not match your firmware bus numbering. + * Fortunately, in most cases, interrupt-map-mask doesn't + * include the bus number as part of the matching. + * You should still be careful about that though if you intend + * to rely on this function (you ship a firmware that doesn't + * create device nodes for all PCI devices). + */ + if (ppnode) + break; + + /* We can only get here if we hit a P2P bridge with no node, + * let's do standard swizzling and try again + */ + pin = pci_swizzle_interrupt_pin(pdev, pin); + pdev = ppdev; + } + + out_irq->np = ppnode; + out_irq->args_count = 1; + out_irq->args[0] = pin; + laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); + laddr[1] = laddr[2] = cpu_to_be32(0); + return of_irq_parse_raw(laddr, out_irq); +} +EXPORT_SYMBOL_GPL(of_irq_parse_pci); + +/** + * of_irq_parse_and_map_pci() - Decode a PCI irq from the device tree and map to a virq + * @dev: The pci device needing an irq + * @slot: PCI slot number; passed when used as map_irq callback. Unused + * @pin: PCI irq pin number; passed when used as map_irq callback. Unused + * + * @slot and @pin are unused, but included in the function so that this + * function can be used directly as the map_irq callback to pci_fixup_irqs(). + */ +int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct of_phandle_args oirq; + int ret; + + ret = of_irq_parse_pci(dev, &oirq); + if (ret) { + dev_err(&dev->dev, + "of_irq_parse_pci() failed with rc=%d\n", ret); + return 0; /* Proper return code 0 == NO_IRQ */ + } + + return irq_create_of_mapping_new(&oirq); +} +EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci); diff --git a/include/linux/of.h b/include/linux/of.h index 54c2560..d27359a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -67,7 +67,7 @@ struct device_node { #endif }; -#define MAX_PHANDLE_ARGS 8 +#define MAX_PHANDLE_ARGS 16 struct of_phandle_args { struct device_node *np; int args_count; diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index fcd63ba..9984a3e 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -47,7 +47,10 @@ static inline int of_irq_map_oldworld(struct device_node *device, int index, } #endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ - +extern int of_irq_parse_raw(const __be32 *addr, + struct of_phandle_args *out_irq); +extern int of_irq_parse_one(struct device_node *device, int index, + struct of_phandle_args *out_irq); extern int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, u32 ointsize, const __be32 *addr, struct of_irq *out_irq); @@ -56,6 +59,7 @@ extern int of_irq_map_one(struct device_node *device, int index, extern unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, unsigned int intsize); +extern unsigned int irq_create_of_mapping_new(struct of_phandle_args *irq_data); extern int of_irq_to_resource(struct device_node *dev, int index, struct resource *r); extern int of_irq_count(struct device_node *dev); diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index fd9c408..790db04 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -5,14 +5,50 @@ #include struct pci_dev; +struct of_phandle_args; struct of_irq; int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq); struct device_node; + +#ifdef CONFIG_OF +int of_irq_parse_pci(const struct pci_dev *pdev, + struct of_phandle_args *out_irq); struct device_node *of_pci_find_child_device(struct device_node *parent, unsigned int devfn); int of_pci_get_devfn(struct device_node *np); +int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); int of_pci_parse_bus_range(struct device_node *node, struct resource *res); +#else +static inline int of_irq_parse_pci(const struct pci_dev *pdev, + struct of_phandle_args *out_irq) +{ + return 0; +} + +static inline struct device_node * +of_pci_find_child_device(struct device_node *parent, unsigned int devfn) +{ + return NULL; +} + +static inline int of_pci_get_devfn(struct device_node *np) +{ + return -EINVAL; +} + +static inline int +of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) +{ + return 0; +} + +static inline int +of_pci_parse_bus_range(struct device_node *node, struct resource *res) +{ + return -EINVAL; +} +#endif #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) int of_pci_msi_chip_add(struct msi_chip *chip); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 706724e..18edabe 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -502,6 +502,43 @@ unsigned int irq_create_of_mapping(struct device_node *controller, } EXPORT_SYMBOL_GPL(irq_create_of_mapping); +unsigned int irq_create_of_mapping_new(struct of_phandle_args *irq_data) +{ + struct irq_domain *domain; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + unsigned int virq; + + domain = irq_data->np ? + irq_find_host(irq_data->np) : irq_default_domain; + if (!domain) { + pr_warn("no irq domain found for %s !\n", + of_node_full_name(irq_data->np)); + return 0; + } + + /* If domain has no translation, then we assume interrupt line */ + if (domain->ops->xlate == NULL) + hwirq = irq_data->args[0]; + else { + if (domain->ops->xlate(domain, irq_data->np, irq_data->args, + irq_data->args_count, &hwirq, &type)) + return 0; + } + + /* Create mapping */ + virq = irq_create_mapping(domain, hwirq); + if (!virq) + return virq; + + /* Set type if specified and different than the current one */ + if (type != IRQ_TYPE_NONE && + type != irq_get_trigger_type(virq)) + irq_set_irq_type(virq, type); + return virq; +} +EXPORT_SYMBOL_GPL(irq_create_of_mapping_new); + /** * irq_dispose_mapping() - Unmap an interrupt * @virq: linux irq number of the interrupt to unmap -- cgit v0.10.2 From ec9a8d730186ec870228a9e9edbaa9e8f08dca03 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 6 Sep 2013 15:54:59 +0900 Subject: PCI: exynos: Add support for MSI This patch adds support for Message Signaled Interrupt in the Exynos PCIe driver using Synopsys designware PCIe core IP. Signed-off-by: Siva Reddy Kallam Signed-off-by: Srikanth T Shivanand Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas Cc: Pratyush Anand Cc: Mohit KUMAR The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=f342d940ee0e3a2b5197fd4fbade1cb6bbc960b7 Change-Id: I38a751701499e994463b4bbbefa588bc24129f7b Reviewed-on: http://git.am.freescale.net:8181/19678 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 510994a..e27caf4 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -11,8 +11,11 @@ * published by the Free Software Foundation. */ +#include +#include #include #include +#include #include #include #include @@ -142,6 +145,204 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return ret; } +static struct irq_chip dw_msi_irq_chip = { + .name = "PCI-MSI", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, +}; + +/* MSI int handler */ +void dw_handle_msi_irq(struct pcie_port *pp) +{ + unsigned long val; + int i, pos; + + for (i = 0; i < MAX_MSI_CTRLS; i++) { + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, + (u32 *)&val); + if (val) { + pos = 0; + while ((pos = find_next_bit(&val, 32, pos)) != 32) { + generic_handle_irq(pp->msi_irq_start + + (i * 32) + pos); + pos++; + } + } + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); + } +} + +void dw_pcie_msi_init(struct pcie_port *pp) +{ + pp->msi_data = __get_free_pages(GFP_KERNEL, 0); + + /* program the msi_data */ + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, + virt_to_phys((void *)pp->msi_data)); + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); +} + +static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) +{ + int flag = 1; + + do { + pos = find_next_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos); + /*if you have reached to the end then get out from here.*/ + if (pos == MAX_MSI_IRQS) + return -ENOSPC; + /* + * Check if this position is at correct offset.nvec is always a + * power of two. pos0 must be nvec bit alligned. + */ + if (pos % msgvec) + pos += msgvec - (pos % msgvec); + else + flag = 0; + } while (flag); + + *pos0 = pos; + return 0; +} + +static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) +{ + int res, bit, irq, pos0, pos1, i; + u32 val; + struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); + + if (!pp) { + BUG(); + return -EINVAL; + } + + pos0 = find_first_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS); + if (pos0 % no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) + goto no_valid_irq; + } + if (no_irqs > 1) { + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + /* there must be nvec number of consecutive free bits */ + while ((pos1 - pos0) < no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) + goto no_valid_irq; + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + } + } + + irq = (pp->msi_irq_start + pos0); + + if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1)) + goto no_valid_irq; + + i = 0; + while (i < no_irqs) { + set_bit(pos0 + i, pp->msi_irq_in_use); + irq_alloc_descs((irq + i), (irq + i), 1, 0); + irq_set_msi_desc(irq + i, desc); + /*Enable corresponding interrupt in MSI interrupt controller */ + res = ((pos0 + i) / 32) * 12; + bit = (pos0 + i) % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + i++; + } + + *pos = pos0; + return irq; + +no_valid_irq: + *pos = pos0; + return -ENOSPC; +} + +static void clear_irq(unsigned int irq) +{ + int res, bit, val, pos; + struct irq_desc *desc; + struct msi_desc *msi; + struct pcie_port *pp; + + /* get the port structure */ + desc = irq_to_desc(irq); + msi = irq_desc_get_msi_desc(desc); + pp = sys_to_pcie(msi->dev->bus->sysdata); + if (!pp) { + BUG(); + return; + } + + pos = irq - pp->msi_irq_start; + + irq_free_desc(irq); + + clear_bit(pos, pp->msi_irq_in_use); + + /* Disable corresponding interrupt on MSI interrupt controller */ + res = (pos / 32) * 12; + bit = pos % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + +static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, + struct msi_desc *desc) +{ + int irq, pos, msgvec; + u16 msg_ctr; + struct msi_msg msg; + struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); + + if (!pp) { + BUG(); + return -EINVAL; + } + + pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, + &msg_ctr); + msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; + if (msgvec == 0) + msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; + if (msgvec > 5) + msgvec = 0; + + irq = assign_irq((1 << msgvec), desc, &pos); + if (irq < 0) + return irq; + + msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; + msg_ctr |= msgvec << 4; + pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, + msg_ctr); + desc->msi_attrib.multiple = msgvec; + + msg.address_lo = virt_to_phys((void *)pp->msi_data); + msg.address_hi = 0x0; + msg.data = pos; + write_msi_msg(irq, &msg); + + return 0; +} + +static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +{ + clear_irq(irq); +} + +static struct msi_chip dw_pcie_msi_chip = { + .setup_irq = dw_msi_setup_irq, + .teardown_irq = dw_msi_teardown_irq, +}; + int dw_pcie_link_up(struct pcie_port *pp) { if (pp->ops->link_up) @@ -150,6 +351,20 @@ int dw_pcie_link_up(struct pcie_port *pp) return 0; } +static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops msi_domain_ops = { + .map = dw_pcie_msi_map, +}; + int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; @@ -157,6 +372,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range_parser parser; u32 val; + struct irq_domain *irq_domain; + if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); return -EINVAL; @@ -223,6 +440,18 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } + if (IS_ENABLED(CONFIG_PCI_MSI)) { + irq_domain = irq_domain_add_linear(pp->dev->of_node, + MAX_MSI_IRQS, &msi_domain_ops, + &dw_pcie_msi_chip); + if (!irq_domain) { + dev_err(pp->dev, "irq domain init failed\n"); + return -ENXIO; + } + + pp->msi_irq_start = irq_find_mapping(irq_domain, 0); + } + if (pp->ops->host_init) pp->ops->host_init(pp); @@ -485,10 +714,21 @@ int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return pp->irq; } +static void dw_pcie_add_bus(struct pci_bus *bus) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + struct pcie_port *pp = sys_to_pcie(bus->sysdata); + + dw_pcie_msi_chip.dev = pp->dev; + bus->msi = &dw_pcie_msi_chip; + } +} + static struct hw_pci dw_pci = { .setup = dw_pcie_setup, .scan = dw_pcie_scan_bus, .map_irq = dw_pcie_map_irq, + .add_bus = dw_pcie_add_bus, }; void dw_pcie_setup_rc(struct pcie_port *pp) diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 133820f..faccbbf 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -20,6 +20,14 @@ struct pcie_port_info { phys_addr_t mem_bus_addr; }; +/* + * Maximum number of MSI IRQs can be 256 per controller. But keep + * it 32 as of now. Probably we will never need more than 32. If needed, + * then increment it in multiple of 32. + */ +#define MAX_MSI_IRQS 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) + struct pcie_port { struct device *dev; u8 root_bus_nr; @@ -38,6 +46,10 @@ struct pcie_port { int irq; u32 lanes; struct pcie_host_ops *ops; + int msi_irq; + int msi_irq_start; + unsigned long msi_data; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; struct pcie_host_ops { @@ -57,6 +69,8 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val); int cfg_write(void __iomem *addr, int where, int size, u32 val); int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); +void dw_handle_msi_irq(struct pcie_port *pp); +void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); -- cgit v0.10.2 From 2a733e1e3a978fb583b5fd0b7fae678dddb7b870 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 9 Oct 2013 09:12:21 -0600 Subject: PCI: designware: Add header guards Add header guards to prevent redundant inclusion. Signed-off-by: Seungwon Jeon Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=18edf4512cfa3e3662bdbdfc5f11c2eb20721734 Change-Id: I31f0750a64fdbc04a6f4716bac5e1a9d843cb020 Reviewed-on: http://git.am.freescale.net:8181/19679 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index faccbbf..d87fbae 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -11,6 +11,9 @@ * published by the Free Software Foundation. */ +#ifndef _PCIE_DESIGNWARE_H +#define _PCIE_DESIGNWARE_H + struct pcie_port_info { u32 cfg0_size; u32 cfg1_size; @@ -77,3 +80,5 @@ int dw_pcie_host_init(struct pcie_port *pp); int dw_pcie_setup(int nr, struct pci_sys_data *sys); struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys); int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); + +#endif /* _PCIE_DESIGNWARE_H */ -- cgit v0.10.2 From 252fe9bb34c4db300af6b1da731ac0636a21e3b5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 9 Oct 2013 09:12:37 -0600 Subject: PCI: designware: Make dw_pcie_rd_own_conf(), etc., static The following variables and functions are used only in pcie-designware.c, so make them static: global_io_offset dw_pcie_rd_own_conf() dw_pcie_wr_own_conf() dw_pcie_setup() dw_pcie_scan_bus() dw_pcie_map_irq() Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=73e408508bf6c76d8dc06f044f0e4703a1e27f14 Change-Id: If9e312b452807d927347d1b0044ec0251de01ab4 Reviewed-on: http://git.am.freescale.net:8181/19680 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index e27caf4..762fbb2 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -67,7 +67,7 @@ static struct hw_pci dw_pci; -unsigned long global_io_offset; +static unsigned long global_io_offset; static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) { @@ -118,8 +118,8 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) writel(val, pp->dbi_base + reg); } -int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) +static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) { int ret; @@ -131,8 +131,8 @@ int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, return ret; } -int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) +static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) { int ret; @@ -667,7 +667,7 @@ static struct pci_ops dw_pcie_ops = { .write = dw_pcie_wr_conf, }; -int dw_pcie_setup(int nr, struct pci_sys_data *sys) +static int dw_pcie_setup(int nr, struct pci_sys_data *sys) { struct pcie_port *pp; @@ -690,7 +690,7 @@ int dw_pcie_setup(int nr, struct pci_sys_data *sys) return 1; } -struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) +static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) { struct pci_bus *bus; struct pcie_port *pp = sys_to_pcie(sys); @@ -707,7 +707,7 @@ struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) return bus; } -int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index d87fbae..890c2ce 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -66,19 +66,12 @@ struct pcie_host_ops { void (*host_init)(struct pcie_port *pp); }; -extern unsigned long global_io_offset; - int cfg_read(void __iomem *addr, int where, int size, u32 *val); int cfg_write(void __iomem *addr, int where, int size, u32 val); -int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); -int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); void dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); -int dw_pcie_setup(int nr, struct pci_sys_data *sys); -struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys); -int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); #endif /* _PCIE_DESIGNWARE_H */ -- cgit v0.10.2 From 229db881a4acd7786ba1db876fc6b7dd5e0f4ece Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 9 Oct 2013 21:32:12 +0900 Subject: PCI: designware: Add irq_create_mapping() Without irq_create_mapping(), the correct IRQ number cannot be provided. In this case, it makes problems such as NULL dereference. Thus, irq_create_mapping() should be added for MSI. Suggested-by: Kishon Vijay Abraham I Signed-off-by: Pratyush Anand Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=904d0e7889933fb48d921c998fd1cabb3a9d6635 Change-Id: I79a67a778cc2b1de70aef538786f37c83d31d0de Reviewed-on: http://git.am.freescale.net:8181/19681 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 762fbb2..53c60c7 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -157,7 +157,7 @@ static struct irq_chip dw_msi_irq_chip = { void dw_handle_msi_irq(struct pcie_port *pp) { unsigned long val; - int i, pos; + int i, pos, irq; for (i = 0; i < MAX_MSI_CTRLS; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, @@ -165,8 +165,9 @@ void dw_handle_msi_irq(struct pcie_port *pp) if (val) { pos = 0; while ((pos = find_next_bit(&val, 32, pos)) != 32) { - generic_handle_irq(pp->msi_irq_start - + (i * 32) + pos); + irq = irq_find_mapping(pp->irq_domain, + i * 32 + pos); + generic_handle_irq(irq); pos++; } } @@ -237,9 +238,8 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) } } - irq = (pp->msi_irq_start + pos0); - - if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1)) + irq = irq_find_mapping(pp->irq_domain, pos0); + if (!irq) goto no_valid_irq; i = 0; @@ -270,6 +270,7 @@ static void clear_irq(unsigned int irq) struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; + struct irq_data *data = irq_get_irq_data(irq); /* get the port structure */ desc = irq_to_desc(irq); @@ -280,7 +281,7 @@ static void clear_irq(unsigned int irq) return; } - pos = irq - pp->msi_irq_start; + pos = data->hwirq; irq_free_desc(irq); @@ -371,8 +372,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range range; struct of_pci_range_parser parser; u32 val; - - struct irq_domain *irq_domain; + int i; if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); @@ -441,15 +441,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (IS_ENABLED(CONFIG_PCI_MSI)) { - irq_domain = irq_domain_add_linear(pp->dev->of_node, + pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, MAX_MSI_IRQS, &msi_domain_ops, &dw_pcie_msi_chip); - if (!irq_domain) { + if (!pp->irq_domain) { dev_err(pp->dev, "irq domain init failed\n"); return -ENXIO; } - pp->msi_irq_start = irq_find_mapping(irq_domain, 0); + for (i = 0; i < MAX_MSI_IRQS; i++) + irq_create_mapping(pp->irq_domain, i); } if (pp->ops->host_init) diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 890c2ce..c15379b 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -50,7 +50,7 @@ struct pcie_port { u32 lanes; struct pcie_host_ops *ops; int msi_irq; - int msi_irq_start; + struct irq_domain *irq_domain; unsigned long msi_data; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; -- cgit v0.10.2 From 11a64ff0a6562c97468ab036f10253ed8d11103e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 14 Nov 2013 11:28:18 -0700 Subject: PCI: Fix whitespace, capitalization, and spelling errors Fix whitespace, capitalization, and spelling errors. No functional change. I know "busses" is not an error, but "buses" was more common, so I used it consistently. Signed-off-by: Marta Rybczynska (pci_reset_bridge_secondary_bus()) Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=f7625980f5820edd1a73536e1a03bcbc1f889fec Change-Id: If74f035977847d4890ef2bd341d7db33765c0c58 Reviewed-on: http://git.am.freescale.net:8181/19682 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 53c60c7..3ac16c8 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -197,7 +197,7 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return -ENOSPC; /* * Check if this position is at correct offset.nvec is always a - * power of two. pos0 must be nvec bit alligned. + * power of two. pos0 must be nvec bit aligned. */ if (pos % msgvec) pos += msgvec - (pos % msgvec); -- cgit v0.10.2 From 05cacb123c33bc99bc3b49521b1e2844371e3b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= Date: Fri, 29 Nov 2013 14:35:24 +0100 Subject: PCI: designware: Fix crash in dw_msi_teardown_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 904d0e788993 ("PCI: designware: Add irq_create_mapping()") resulted in pre-allocated irq descs. Problem was that in assign_irq() these descs were explicitly allocated and hence also freed, resulting in a crash. We also need to clear the entire irq range in teardown. With this commit the teardown basically does exactly the opposite of what was done in setup. The crash this fixes looks like: Unable to handle kernel NULL pointer dereference at virtual address 00000020 PC is at dw_msi_teardown_irq+0x40/0x118 LR is at trace_hardirqs_on_caller+0xf4/0x1c0 Backtrace: [<802c401c>] (dw_msi_teardown_irq+0x0/0x118) from [<802c1844>] (arch_teardown_msi_irq+0x3c/0x40) [<802c1808>] (arch_teardown_msi_irq+0x0/0x40) from [<802c1a08>] (default_teardown_msi_irqs+0x68/0x84) [<802c19a0>] (default_teardown_msi_irqs+0x0/0x84) from [<802c1a34>] (arch_teardown_msi_irqs+0x10/0x14) [<802c1a24>] (arch_teardown_msi_irqs+0x0/0x14) from [<802c1ad0>] (free_msi_irqs+0x98/0x144) [<802c1a38>] (free_msi_irqs+0x0/0x144) from [<802c2570>] (pci_disable_msi+0x48/0x60) [<802c2528>] (pci_disable_msi+0x0/0x60) from [<7f0057d4>] (sxdma_irq_free+0x44/0x48 [sxdma]) [bhelgaas: add crash info] Tested-by: Mohit Kumar Signed-off-by: Bjørn Erik Nilsen Signed-off-by: Bjorn Helgaas Acked-by: Marek Vasut Acked-by: Jingoo Han The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=be3f48cb21c1ca4907a0822eea406c8dd4a73ddb with changing code style to avoid checkpatch errors and warnings Change-Id: Iea3c4fa303fa64a0a6f94a53b615725e83433b51 Reviewed-on: http://git.am.freescale.net:8181/19683 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 3ac16c8..3f0214e 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -209,6 +209,25 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return 0; } +static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, + unsigned int nvec, unsigned int pos) +{ + unsigned int i, res, bit, val; + + i = 0; + while (i < nvec) { + irq_set_msi_desc_off(irq_base, i, NULL); + clear_bit(pos + i, pp->msi_irq_in_use); + /* Disable corresponding interrupt on MSI controller */ + res = ((pos + i) / 32) * 12; + bit = (pos + i) % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + ++i; + } +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int res, bit, irq, pos0, pos1, i; @@ -242,11 +261,20 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) if (!irq) goto no_valid_irq; + /* + * irq_create_mapping (called from dw_pcie_host_init) pre-allocates + * descs so there is no need to allocate descs here. We can therefore + * assume that if irq_find_mapping above returns non-zero, then the + * descs are also successfully allocated. + */ + i = 0; while (i < no_irqs) { + if (irq_set_msi_desc_off(irq, i, desc) != 0) { + clear_irq_range(pp, irq, i, pos0); + goto no_valid_irq; + } set_bit(pos0 + i, pp->msi_irq_in_use); - irq_alloc_descs((irq + i), (irq + i), 1, 0); - irq_set_msi_desc(irq + i, desc); /*Enable corresponding interrupt in MSI interrupt controller */ res = ((pos0 + i) / 32) * 12; bit = (pos0 + i) % 32; @@ -266,7 +294,7 @@ no_valid_irq: static void clear_irq(unsigned int irq) { - int res, bit, val, pos; + unsigned int pos, nvec; struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; @@ -281,18 +309,15 @@ static void clear_irq(unsigned int irq) return; } + /* undo what was done in assign_irq */ pos = data->hwirq; + nvec = 1 << msi->msi_attrib.multiple; - irq_free_desc(irq); - - clear_bit(pos, pp->msi_irq_in_use); + clear_irq_range(pp, irq, nvec, pos); - /* Disable corresponding interrupt on MSI interrupt controller */ - res = (pos / 32) * 12; - bit = pos % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + /* all irqs cleared; reset attributes */ + msi->irq = 0; + msi->msi_attrib.multiple = 0; } static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -- cgit v0.10.2 From 6479766c9ebb900ff045e8577ad0493dcb87d27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= Date: Fri, 29 Nov 2013 14:35:25 +0100 Subject: PCI: designware: Remove redundant call to pci_write_config_word() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit write_msi_msg() does exactly the same so there is no need to explicitly call pci_write_config_word() and do the same twice. Tested-by: Mohit Kumar Signed-off-by: Bjørn Erik Nilsen Signed-off-by: Bjorn Helgaas Acked-by: Marek Vasut Acked-by: Jingoo Han The patch comes from https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=64989e7399f09b72689e25fb40f2d0d5e073b13a Change-Id: Iae9f87518a96114d4c64d9c925f252a8aeab9d14 Reviewed-on: http://git.am.freescale.net:8181/19684 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 3f0214e..c1e0bfd 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -345,10 +345,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, if (irq < 0) return irq; - msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; - msg_ctr |= msgvec << 4; - pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, - msg_ctr); + /* + * write_msi_msg() will update PCI_MSI_FLAGS so there is + * no need to explicitly call pci_write_config_word(). + */ desc->msi_attrib.multiple = msgvec; msg.address_lo = virt_to_phys((void *)pp->msi_data); -- cgit v0.10.2 From ce2f9cb83a82976f1bd9560daefdaf7fa7ac5ac2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 9 Dec 2013 15:11:25 -0700 Subject: PCI: designware: Use typical "for" loop idiom It's conventional to use "for" rather than "while" for simple iteration. No functional change. Signed-off-by: Bjorn Helgaas The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=0b8cfb6aa3aabc96177b1e68ef13d2eb5c686606 Change-Id: Idc9f17b671cd3798ea8976f61a4574f556e93141 Reviewed-on: http://git.am.freescale.net:8181/19685 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index c1e0bfd..268e986 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -214,8 +214,7 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, { unsigned int i, res, bit, val; - i = 0; - while (i < nvec) { + for (i = 0; i < nvec; i++) { irq_set_msi_desc_off(irq_base, i, NULL); clear_bit(pos + i, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI controller */ @@ -224,7 +223,6 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); - ++i; } } @@ -268,8 +266,7 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) * descs are also successfully allocated. */ - i = 0; - while (i < no_irqs) { + for (i = 0; i < no_irqs; i++) { if (irq_set_msi_desc_off(irq, i, desc) != 0) { clear_irq_range(pp, irq, i, pos0); goto no_valid_irq; @@ -281,7 +278,6 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); - i++; } *pos = pos0; -- cgit v0.10.2 From 94658a9fd0af2d4cb42ba3e1e8fa72d7541176c3 Mon Sep 17 00:00:00 2001 From: Harro Haan Date: Thu, 12 Dec 2013 19:29:03 +0100 Subject: PCI: designware: Fix missing MSI IRQs The interrupts were cleared after the IRQ handler was called. This means that new interrupts that occur after the handler handled the previous IRQ but before the interrupt is cleared will be missed. Tested-by: Marek Vasut Tested-by: Matthias Mann Signed-off-by: Harro Haan Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han Acked-by: Mohit Kumar Cc: Richard Zhu Cc: Shawn Guo Cc: Pratyush Anand Cc: Tim Harvey Cc: Juergen Beisert Cc: Arnd Bergmann Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Sean Cross The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=ca1658921b63e5771423603367c5bee528acc977 Change-Id: I9f23d8e1d96d1e580d3efa43b755d9a79103217c Reviewed-on: http://git.am.freescale.net:8181/19686 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 268e986..c954de6 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -167,11 +167,13 @@ void dw_handle_msi_irq(struct pcie_port *pp) while ((pos = find_next_bit(&val, 32, pos)) != 32) { irq = irq_find_mapping(pp->irq_domain, i * 32 + pos); + dw_pcie_wr_own_conf(pp, + PCIE_MSI_INTR0_STATUS + i * 12, + 4, 1 << pos); generic_handle_irq(irq); pos++; } } - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); } } -- cgit v0.10.2 From abcbd7528023c40643732178d9b8807b981421a0 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 11 Dec 2013 15:08:32 +0530 Subject: PCI: designware: Add dw_pcie prefix before cfg_read/write The cfg_read/write functions are DesignWare-specific. Add dw_pcie prefix to avoid collision in global name space. Tested-by: Jingoo Han Signed-off-by: Pratyush Anand Signed-off-by: Bjorn Helgaas Reviewed-by: Jagannadha Sutradharudu Teki Acked-by: Jingoo Han The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=a01ef59e131b78b0fa7af235ea958bd17e5e86ca Change-Id: I3562a37f35993bf4428bd092d6de11e60ebc2906 Reviewed-on: http://git.am.freescale.net:8181/19687 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index c954de6..426f6dd 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -74,7 +74,7 @@ static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) return sys->private_data; } -int cfg_read(void __iomem *addr, int where, int size, u32 *val) +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) { *val = readl(addr); @@ -88,7 +88,7 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val) return PCIBIOS_SUCCESSFUL; } -int cfg_write(void __iomem *addr, int where, int size, u32 val) +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) { if (size == 4) writel(val, addr); @@ -126,7 +126,8 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, if (pp->ops->rd_own_conf) ret = pp->ops->rd_own_conf(pp, where, size, val); else - ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); + ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, + size, val); return ret; } @@ -139,8 +140,8 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, if (pp->ops->wr_own_conf) ret = pp->ops->wr_own_conf(pp, where, size, val); else - ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, - val); + ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, + size, val); return ret; } @@ -574,11 +575,13 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, if (bus->parent->number == pp->root_bus_nr) { dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = cfg_read(pp->va_cfg0_base + address, where, size, val); + ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, + val); dw_pcie_prog_viewport_mem_outbound(pp); } else { dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = cfg_read(pp->va_cfg1_base + address, where, size, val); + ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, + val); dw_pcie_prog_viewport_io_outbound(pp); } @@ -597,11 +600,13 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, if (bus->parent->number == pp->root_bus_nr) { dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = cfg_write(pp->va_cfg0_base + address, where, size, val); + ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, + val); dw_pcie_prog_viewport_mem_outbound(pp); } else { dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = cfg_write(pp->va_cfg1_base + address, where, size, val); + ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, + val); dw_pcie_prog_viewport_io_outbound(pp); } diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index c15379b..3063b35 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -66,8 +66,8 @@ struct pcie_host_ops { void (*host_init)(struct pcie_port *pp); }; -int cfg_read(void __iomem *addr, int where, int size, u32 *val); -int cfg_write(void __iomem *addr, int where, int size, u32 val); +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); void dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); -- cgit v0.10.2 From f86d1d21a49a56722462711392ecf380f5737d0b Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 11 Dec 2013 15:08:33 +0530 Subject: PCI: designware: Fix I/O transfers by using CPU (not realio) address pp->io_base, which is the input of the outbound IO address translation unit, should be the CPU address. It was incorrectly programmed to the realio address. We should pass global_io_offset rather than sys->io_offset to pci_ioremap_io(), so we map the new window into the first available spot in the Linux view of the I/O space. We must also pass CPU address instead of realio address to pci_ioremap_io(). This patch fixes above issue. It has been tested with Lecroy PTC in AIC mode and Pericom PI7C9X2G303EL PCIe switch, which does not work otherwise. Tested-by: Mohit Kumar Tested-by: Tim Harvey Signed-off-by: Pratyush Anand Signed-off-by: Bjorn Helgaas Reviewed-by: Marek Vasut Reviewed-by: Jagannadha Sutradharudu Teki Acked-by: Arnd Bergmann Acked-by: Jingoo Han Cc: Richard Zhu The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=fce8591f73c6a30c231f220d1092362aae0b985c Change-Id: I32d0d6f638c4d9f27eea11a2f9df377c6d31ab2d Reviewed-on: http://git.am.freescale.net:8181/19688 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 426f6dd..f173dd0 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -418,6 +418,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) + global_io_offset); pp->config.io_size = resource_size(&pp->io); pp->config.io_bus_addr = range.pci_addr; + pp->io_base = range.cpu_addr; } if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); @@ -443,7 +444,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->cfg0_base = pp->cfg.start; pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; - pp->io_base = pp->io.start; pp->mem_base = pp->mem.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, @@ -613,7 +613,6 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, return ret; } - static int dw_pcie_valid_config(struct pcie_port *pp, struct pci_bus *bus, int dev) { @@ -707,7 +706,7 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) if (global_io_offset < SZ_1M && pp->config.io_size > 0) { sys->io_offset = global_io_offset - pp->config.io_bus_addr; - pci_ioremap_io(sys->io_offset, pp->io.start); + pci_ioremap_io(global_io_offset, pp->io_base); global_io_offset += SZ_64K; pci_add_resource_offset(&sys->resources, &pp->io, sys->io_offset); -- cgit v0.10.2 From 5fd84fda07810d8aa8408ed642064834d8b86930 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Feb 2014 21:40:11 +0000 Subject: pci: pcie-designware: Remove irq_desc abuse There is no reason to care about irq_desc in that context, escpecially as irq_data for that interrupt is retrieved as well. Use the proper accessor for the msi descriptor Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Acked-by: Bjorn Helgaas Acked-by: Jingoo Han Cc: Mohit Kumar Cc: pci Link: http://lkml.kernel.org/r/20140223212736.987803648@linutronix.de The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=f7bfca6db60a6ca0a73126918b2fb6f851065947 Change-Id: Iccd04b791032d0e48d4907f11e2274b3eaa0131d Reviewed-on: http://git.am.freescale.net:8181/19690 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index f173dd0..6eca144 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -294,14 +294,12 @@ no_valid_irq: static void clear_irq(unsigned int irq) { unsigned int pos, nvec; - struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; struct irq_data *data = irq_get_irq_data(irq); /* get the port structure */ - desc = irq_to_desc(irq); - msi = irq_desc_get_msi_desc(desc); + msi = irq_data_get_msi(data); pp = sys_to_pcie(msi->dev->bus->sysdata); if (!pp) { BUG(); -- cgit v0.10.2 From 9a39a423c659246b7b772c5244b72adb88166704 Mon Sep 17 00:00:00 2001 From: Mohit Kumar Date: Mon, 14 Apr 2014 14:22:54 -0600 Subject: PCI: designware: Fix comment for setting number of lanes Corrects comment for setting number of lanes. Signed-off-by: Mohit Kumar Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=66c5c34bf80c28d370eb9bcf30153ea0304a288a Change-Id: I7e20ddc2d977de0fb9eb29d3f4ba6163077aabcf Reviewed-on: http://git.am.freescale.net:8181/19691 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 6eca144..a9a62ce 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -764,7 +764,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) u32 membase; u32 memlimit; - /* set the number of lines as 4 */ + /* set the number of lanes */ dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); val &= ~PORT_LINK_MODE_MASK; switch (pp->lanes) { -- cgit v0.10.2 From 92f1814673480a73d53a868b4420487964235d8a Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 5 Mar 2014 14:25:51 +0100 Subject: PCI: designware: Use new OF interrupt mapping when possible Use new OF interrupt mapping (of_irq_parse_and_map_pci()) when possible. This is the recommended method of doing the IRQ mapping. For old devicetrees we fall back to the previous practice. This makes INTB, INTC, and INTD work on i.MX. Tested-by: Tim Harvey Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Reviewed-by: Marek Vasut Acked-by: Arnd Bergmann Acked-by: Jingoo Han The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=f86b3e392780050e5907f1c0f3cb6c4cc05fd6bb Change-Id: Ic2bb7c8d649a867fe39f03ecff5c589dfbda93e8 Reviewed-on: http://git.am.freescale.net:8181/19692 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index a9a62ce..c4e3732 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -490,7 +491,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) dw_pci.nr_controllers = 1; dw_pci.private_data = (void **)&pp; - pci_common_init(&dw_pci); + pci_common_init_dev(pp->dev, &dw_pci); pci_assign_unassigned_resources(); #ifdef CONFIG_PCI_DOMAINS dw_pci.domain++; @@ -723,7 +724,7 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) if (pp) { pp->root_bus_nr = sys->busnr; - bus = pci_scan_root_bus(NULL, sys->busnr, &dw_pcie_ops, + bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops, sys, &sys->resources); } else { bus = NULL; @@ -736,8 +737,13 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); + int irq; - return pp->irq; + irq = of_irq_parse_and_map_pci(dev, slot, pin); + if (!irq) + irq = pp->irq; + + return irq; } static void dw_pcie_add_bus(struct pci_bus *bus) -- cgit v0.10.2 From 6bc1cdfbe26171cc2cb48f57dfaa91a8fc88b1a8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Apr 2014 14:22:54 -0600 Subject: PCI: designware: Remove unnecessary use of 'conf_lock' spinlock Serialization of configuration accesses is provided by 'pci_lock' in drivers/pci/access.c thus making the driver's 'conf_lock' superfluous. Signed-off-by: Andrew Murray Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han Acked-by: Richard Zhu The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=11c6fbd8d982617996fbc39097a84092eb6e8005 Change-Id: I6e879fc331aafd62d231ac0abb97ac5f0c535f09 Reviewed-on: http://git.am.freescale.net:8181/19693 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index c4e3732..4958460 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -639,7 +639,6 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 *val) { struct pcie_port *pp = sys_to_pcie(bus->sysdata); - unsigned long flags; int ret; if (!pp) { @@ -652,13 +651,11 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, return PCIBIOS_DEVICE_NOT_FOUND; } - spin_lock_irqsave(&pp->conf_lock, flags); if (bus->number != pp->root_bus_nr) ret = dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_rd_own_conf(pp, where, size, val); - spin_unlock_irqrestore(&pp->conf_lock, flags); return ret; } @@ -667,7 +664,6 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { struct pcie_port *pp = sys_to_pcie(bus->sysdata); - unsigned long flags; int ret; if (!pp) { @@ -678,13 +674,11 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - spin_lock_irqsave(&pp->conf_lock, flags); if (bus->number != pp->root_bus_nr) ret = dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_wr_own_conf(pp, where, size, val); - spin_unlock_irqrestore(&pp->conf_lock, flags); return ret; } diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 3063b35..a10747d 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -41,7 +41,6 @@ struct pcie_port { void __iomem *va_cfg1_base; u64 io_base; u64 mem_base; - spinlock_t conf_lock; struct resource cfg; struct resource io; struct resource mem; -- cgit v0.10.2 From bd71b23887496fcb6aa2787551e9abb99db4f517 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 28 Mar 2014 17:52:58 +0100 Subject: PCI: designware: Make MSI ISR shared IRQ aware On i.MX6 the host controller MSI IRQ is shared with PCI legacy INTD. Make sure we don't bail too early from the IRQ handler. The issue is fairly theoretical as it would require a system setup with a PCIe switch where one connected device is using legacy INTD and another one using MSI, but better fix it now. Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han Acked-by: Richard Zhu The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=7f4f16eef5aeba31bdfb7702ced06a42f2777e04 Change-Id: Ief02f9b15fa69316a067154dfcd727148687af9c Reviewed-on: http://git.am.freescale.net:8181/19694 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 4958460..1eaf4df 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -156,15 +156,17 @@ static struct irq_chip dw_msi_irq_chip = { }; /* MSI int handler */ -void dw_handle_msi_irq(struct pcie_port *pp) +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { unsigned long val; int i, pos, irq; + irqreturn_t ret = IRQ_NONE; for (i = 0; i < MAX_MSI_CTRLS; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, (u32 *)&val); if (val) { + ret = IRQ_HANDLED; pos = 0; while ((pos = find_next_bit(&val, 32, pos)) != 32) { irq = irq_find_mapping(pp->irq_domain, @@ -177,6 +179,8 @@ void dw_handle_msi_irq(struct pcie_port *pp) } } } + + return ret; } void dw_pcie_msi_init(struct pcie_port *pp) diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index a10747d..77f592f 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -67,7 +67,7 @@ struct pcie_host_ops { int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); -void dw_handle_msi_irq(struct pcie_port *pp); +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); -- cgit v0.10.2 From c81386b5d64c5a0eef1fe98c50ff18bc511fdf91 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 17 Jul 2014 14:30:40 +0530 Subject: PCI: designware: Look for configuration space in 'reg', not 'ranges' The configuration address space has so far been specified in *ranges*, however it should be specified in *reg* making it a platform MEM resource. Hence used 'platform_get_resource_*' API to get configuration address space in the designware driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas Acked-by: Mohit Kumar Acked-by: Jingoo Han Cc: Jason Gunthorpe Cc: Marek Vasut Cc: Arnd Bergmann The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=4dd964df36d0e548e1806ec2ec275b62d4dc46e8 Change-Id: Ie78a1c43233c789a746801f9e49651a102d45936 Reviewed-on: http://git.am.freescale.net:8181/19695 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1eaf4df..0b7b455 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "pcie-designware.h" @@ -396,11 +397,23 @@ static const struct irq_domain_ops msi_domain_ops = { int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; + struct platform_device *pdev = to_platform_device(pp->dev); struct of_pci_range range; struct of_pci_range_parser parser; + struct resource *cfg_res; u32 val; int i; + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->config.cfg0_size = resource_size(cfg_res)/2; + pp->config.cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->config.cfg0_size; + } else { + dev_err(pp->dev, "missing *config* reg space\n"); + } + if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); return -EINVAL; @@ -433,6 +446,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) of_pci_range_to_resource(&range, np, &pp->cfg); pp->config.cfg0_size = resource_size(&pp->cfg)/2; pp->config.cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_base = pp->cfg.start; + pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; } } @@ -445,8 +460,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } } - pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; pp->mem_base = pp->mem.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, -- cgit v0.10.2 From 72d3741749281130f0edce4271efcd4a1e97f19d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 17 Jul 2014 14:30:41 +0530 Subject: PCI: designware: Program ATU with untranslated address In DRA7, the CPU sees 32-bit addresses, but the PCIe controller can see only 28-bit addresses. So whenever the CPU issues a read/write request, the 4 most significant bits are used by L3 to determine the target controller. For example, the CPU reserves [mem 0x20000000-0x2fffffff] for the PCIe controller but the PCIe controller will see only [0x00000000-0x0fffffff]. For programming the outbound translation window the *base* should be programmed as 0x00000000. Whenever we try to write to, e.g., 0x20000000, it will be translated to whatever we have programmed in the translation window with base as 0x00000000. This is needed when the dt node is modelled something like this: axi { compatible = "simple-bus"; #size-cells = <1>; #address-cells = <1>; ranges = <0x0 0x20000000 0x10000000 // 28-bit bus 0x51000000 0x51000000 0x3000>; pcie@51000000 { reg = <0x1000 0x2000>, <0x51002000 0x14c>, <0x51000000 0x2000>; reg-names = "config", "ti_conf", "rc_dbics"; #address-cells = <3>; #size-cells = <2>; ranges = <0x81000000 0 0 0x03000 0 0x00010000 0x82000000 0 0x20013000 0x13000 0 0xffed000>; }; }; Here the CPU address for configuration space is 0x20013000 and the controller address for configuration space is 0x13000. The controller address should be used while programming the ATU (in order for translation to happen properly in DRA7xx). Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Bjorn Helgaas Reviewed-by: Mohit Kumar Cc: Jason Gunthorpe Cc: Jingoo Han Cc: Marek Vasut Cc: Arnd Bergmann The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=f4c55c5a3f7f68c06cc559ed7af8b2d017cbb0a7 Change-Id: I4868d44de5bfd1eb81a1a54ce5ba62ef1068887a Reviewed-on: http://git.am.freescale.net:8181/19696 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 0b7b455..8aab1d6 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -401,8 +401,15 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range range; struct of_pci_range_parser parser; struct resource *cfg_res; - u32 val; - int i; + u32 val, na, ns; + const __be32 *addrp; + int i, index; + + /* Find the address cell size and the number of cells in order to get + * the untranslated address. + */ + of_property_read_u32(np, "#address-cells", &na); + ns = of_n_size_cells(np); cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { @@ -410,6 +417,12 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.cfg1_size = resource_size(cfg_res)/2; pp->cfg0_base = cfg_res->start; pp->cfg1_base = cfg_res->start + pp->config.cfg0_size; + + /* Find the untranslated configuration space address */ + index = of_property_match_string(np, "reg-names", "config"); + addrp = of_get_address(np, index, false, false); + pp->cfg0_mod_base = of_read_number(addrp, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->config.cfg0_size; } else { dev_err(pp->dev, "missing *config* reg space\n"); } @@ -435,12 +448,20 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.io_size = resource_size(&pp->io); pp->config.io_bus_addr = range.pci_addr; pp->io_base = range.cpu_addr; + + /* Find the untranslated IO space address */ + pp->io_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); pp->mem.name = "MEM"; pp->config.mem_size = resource_size(&pp->mem); pp->config.mem_bus_addr = range.pci_addr; + + /* Find the untranslated MEM space address */ + pp->mem_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == 0) { of_pci_range_to_resource(&range, np, &pp->cfg); @@ -448,6 +469,12 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->config.cfg1_size = resource_size(&pp->cfg)/2; pp->cfg0_base = pp->cfg.start; pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; + + /* Find the untranslated configuration space address */ + pp->cfg0_mod_base = of_read_number(parser.range - + parser.np + na, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + + pp->config.cfg0_size; } } @@ -522,9 +549,9 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) /* Program viewport 0 : OUTBOUND : CFG0 */ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1, + dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->config.cfg0_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -538,9 +565,9 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1, + dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->config.cfg1_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -553,9 +580,9 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1, + dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->config.mem_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr), @@ -569,9 +596,9 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1, + dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->io_mod_base + pp->config.io_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr), diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 77f592f..add6527 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -36,11 +36,15 @@ struct pcie_port { u8 root_bus_nr; void __iomem *dbi_base; u64 cfg0_base; + u64 cfg0_mod_base; void __iomem *va_cfg0_base; u64 cfg1_base; + u64 cfg1_mod_base; void __iomem *va_cfg1_base; u64 io_base; + u64 io_mod_base; u64 mem_base; + u64 mem_mod_base; struct resource cfg; struct resource io; struct resource mem; -- cgit v0.10.2 From 880de9c8a7063b2d2439eab0333c2c1511ba9efa Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 21 Jul 2014 12:58:41 -0400 Subject: PCI: designware: Add config access-related pcie_host_ops for v3.65 hardware DesignWare v3.65 hardware requires application space registers to be configured to access the remote EP config space. To support this, add rd_other_conf() and wr_other_conf() to pcie_host_ops. [bhelgaas: changelog] Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit Kumar Acked-by: Jingoo Han Acked-by: Santosh Shilimkar CC: Russell King CC: Grant Likely CC: Rob Herring CC: Richard Zhu CC: Kishon Vijay Abraham I CC: Marek Vasut CC: Arnd Bergmann CC: Pawel Moll CC: Mark Rutland CC: Ian Campbell CC: Kumar Gala CC: Randy Dunlap The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=a1c0ae9c24627a12c781ebd9947a6442861f6168 Change-Id: I067670dda92d175b15034146c5e6269c74aeb0aa Reviewed-on: http://git.am.freescale.net:8181/19697 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 8aab1d6..0e9838a 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -696,7 +696,11 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, } if (bus->number != pp->root_bus_nr) - ret = dw_pcie_rd_other_conf(pp, bus, devfn, + if (pp->ops->rd_other_conf) + ret = pp->ops->rd_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_rd_own_conf(pp, where, size, val); @@ -719,7 +723,11 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, return PCIBIOS_DEVICE_NOT_FOUND; if (bus->number != pp->root_bus_nr) - ret = dw_pcie_wr_other_conf(pp, bus, devfn, + if (pp->ops->wr_other_conf) + ret = pp->ops->wr_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_wr_own_conf(pp, where, size, val); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index add6527..93062229 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -65,6 +65,10 @@ struct pcie_host_ops { u32 val, void __iomem *dbi_base); int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); int (*link_up)(struct pcie_port *pp); void (*host_init)(struct pcie_port *pp); }; -- cgit v0.10.2 From 12faf852ccf6e5b70ef24b0034daeee66e647293 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 21 Jul 2014 12:58:42 -0400 Subject: PCI: designware: Add MSI-related pcie_host_ops for v3.65 hardware DesignWare v3.65 hardware implements MSI controller registers in application space. This requires updates to the DesignWare core to support controllers based on this older hardware. Add msi_irq_set()/clear() interfaces to allow Set/Clear MSI IRQ enable bit in the application register. Also, v3.65 hardware uses the MSI_IRQ register in application register space to raise MSI IRQ to the RC from EP. Current code uses the standard mechanism as per PCI spec. So add get_msi_data() to get the address of this register so common code can work on both v3.65 and newer hardware. [bhelgaas: changelog] Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit Kumar Acked-by: Jingoo Han Acked-by: Santosh Shilimkar CC: Russell King CC: Grant Likely CC: Rob Herring CC: Richard Zhu CC: Kishon Vijay Abraham I CC: Marek Vasut CC: Arnd Bergmann CC: Pawel Moll CC: Mark Rutland CC: Ian Campbell CC: Kumar Gala CC: Randy Dunlap The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=2f37c5a81cff2c341fa19fdd132ece6aea30a735 Change-Id: I5c235d4cd8727dd1bbac51278a132dd1463b77a3 Reviewed-on: http://git.am.freescale.net:8181/19698 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 0e9838a..52bd3a1 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -218,27 +218,47 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return 0; } +static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, unsigned int nvec, unsigned int pos) { - unsigned int i, res, bit, val; + unsigned int i; for (i = 0; i < nvec; i++) { irq_set_msi_desc_off(irq_base, i, NULL); clear_bit(pos + i, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI controller */ - res = ((pos + i) / 32) * 12; - bit = (pos + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + if (pp->ops->msi_clear_irq) + pp->ops->msi_clear_irq(pp, pos + i); + else + dw_pcie_msi_clear_irq(pp, pos + i); } } +static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { - int res, bit, irq, pos0, pos1, i; - u32 val; + int irq, pos0, pos1, i; struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); if (!pp) { @@ -282,11 +302,10 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) } set_bit(pos0 + i, pp->msi_irq_in_use); /*Enable corresponding interrupt in MSI interrupt controller */ - res = ((pos0 + i) / 32) * 12; - bit = (pos0 + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + if (pp->ops->msi_set_irq) + pp->ops->msi_set_irq(pp, pos0 + i); + else + dw_pcie_msi_set_irq(pp, pos0 + i); } *pos = pos0; @@ -354,7 +373,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, */ desc->msi_attrib.multiple = msgvec; - msg.address_lo = virt_to_phys((void *)pp->msi_data); + if (pp->ops->get_msi_data) + msg.address_lo = pp->ops->get_msi_data(pp); + else + msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; msg.data = pos; write_msi_msg(irq, &msg); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 93062229..daf81f9 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -71,6 +71,9 @@ struct pcie_host_ops { unsigned int devfn, int where, int size, u32 val); int (*link_up)(struct pcie_port *pp); void (*host_init)(struct pcie_port *pp); + void (*msi_set_irq)(struct pcie_port *pp, int irq); + void (*msi_clear_irq)(struct pcie_port *pp, int irq); + u32 (*get_msi_data)(struct pcie_port *pp); }; int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); -- cgit v0.10.2 From a767dda446279d3c55c9871647449c90ba8e445b Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Wed, 23 Jul 2014 14:54:51 -0400 Subject: PCI: designware: Add support for v3.65 hardware The Keystone PCI controller is based on v3.65 DesignWare hardware. This version differs from newer versions of the hardware in functional areas discussed below that make it necessary to change dw_pcie_host_init() to support v3.65 based PCI controller. 1. No support for ATU port. Any ATU-specific resource handling code is to be bypassed for v3.65 h/w. 2. MSI controller uses application space to implement MSI and 32 MSI interrupts are multiplexed over 8 IRQs to the host. Hence the code to process MSI IRQ needs to be different. This patch allows platform driver to provide its own irq_domain_ops ptr to irq_domain_add_linear() through an API callback from the DesignWare core driver. 3. MSI interrupt generation requires EP to write to the RC's application register. So enhance the driver to allow setup of inbound access to MSI IRQ register as a post scan bus API callback. Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit KUMAR Acked-by: Jingoo Han CC: Santosh Shilimkar CC: Russell King CC: Grant Likely CC: Rob Herring CC: Jingoo Han CC: Richard Zhu CC: Kishon Vijay Abraham I CC: Marek Vasut CC: Arnd Bergmann CC: Pawel Moll CC: Mark Rutland CC: Ian Campbell CC: Kumar Gala CC: Randy Dunlap The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=b14a3d1784a9252aa3bbe0bb9d14588be32f18a1 Change-Id: I077e4084dd1148bb40ea006cf06f26619f3bf639 Reviewed-on: http://git.am.freescale.net:8181/19699 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 52bd3a1..76d3d8e 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -425,7 +425,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct resource *cfg_res; u32 val, na, ns; const __be32 *addrp; - int i, index; + int i, index, ret; /* Find the address cell size and the number of cells in order to get * the untranslated address. @@ -511,17 +511,24 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->mem_base = pp->mem.start; - pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, - pp->config.cfg0_size); if (!pp->va_cfg0_base) { - dev_err(pp->dev, "error with ioremap in function\n"); - return -ENOMEM; + pp->cfg0_base = pp->cfg.start; + pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, + pp->config.cfg0_size); + if (!pp->va_cfg0_base) { + dev_err(pp->dev, "error with ioremap in function\n"); + return -ENOMEM; + } } - pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, - pp->config.cfg1_size); + if (!pp->va_cfg1_base) { - dev_err(pp->dev, "error with ioremap\n"); - return -ENOMEM; + pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; + pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, + pp->config.cfg1_size); + if (!pp->va_cfg1_base) { + dev_err(pp->dev, "error with ioremap\n"); + return -ENOMEM; + } } if (of_property_read_u32(np, "num-lanes", &pp->lanes)) { @@ -530,16 +537,22 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, - MAX_MSI_IRQS, &msi_domain_ops, - &dw_pcie_msi_chip); - if (!pp->irq_domain) { - dev_err(pp->dev, "irq domain init failed\n"); - return -ENXIO; - } + if (!pp->ops->msi_host_init) { + pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, + MAX_MSI_IRQS, &msi_domain_ops, + &dw_pcie_msi_chip); + if (!pp->irq_domain) { + dev_err(pp->dev, "irq domain init failed\n"); + return -ENXIO; + } - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); + for (i = 0; i < MAX_MSI_IRQS; i++) + irq_create_mapping(pp->irq_domain, i); + } else { + ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); + if (ret < 0) + return ret; + } } if (pp->ops->host_init) @@ -799,6 +812,9 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) BUG(); } + if (bus && pp->ops->scan_bus) + pp->ops->scan_bus(pp); + return bus; } diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index daf81f9..3e84e0a 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -74,6 +74,8 @@ struct pcie_host_ops { void (*msi_set_irq)(struct pcie_port *pp, int irq); void (*msi_clear_irq)(struct pcie_port *pp, int irq); u32 (*get_msi_data)(struct pcie_port *pp); + void (*scan_bus)(struct pcie_port *pp); + int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip); }; int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); -- cgit v0.10.2 From 1805601447a2f00dcf2cef7ebc6b6fe940b613a6 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 23 Jul 2014 19:52:38 +0200 Subject: PCI: designware: Parse bus-range property from devicetree This allows to explicitly specify the covered bus numbers in the devicetree, which will come in handy once we see a SoC with more than one PCIe host controller instance. Previously the driver relied on the behavior of pci_scan_root_bus() to fill in a range of 0x00-0xff if no valid range was found. We fall back to the same range if no valid DT entry was found to keep backwards compatibility, but now do it explicitly. [bhelgaas: use %pR in error message to avoid duplication] Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit Kumar The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=4f2ebe00597c44f7dc6f88a052a2981ddcf6a0b6 Change-Id: Ib2a849e0d1399b16eaafccfdb2c77e6d4fc916bc Reviewed-on: http://git.am.freescale.net:8181/19700 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 76d3d8e..561fa30 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -500,6 +500,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } } + ret = of_pci_parse_bus_range(np, &pp->busn); + if (ret < 0) { + pp->busn.name = np->name; + pp->busn.start = 0; + pp->busn.end = 0xff; + pp->busn.flags = IORESOURCE_BUS; + dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n", + ret, &pp->busn); + } + if (!pp->dbi_base) { pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start, resource_size(&pp->cfg)); @@ -794,6 +804,7 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr; pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset); + pci_add_resource(&sys->resources, &pp->busn); return 1; } diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 3e84e0a..a476e60 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -48,6 +48,7 @@ struct pcie_port { struct resource cfg; struct resource io; struct resource mem; + struct resource busn; struct pcie_port_info config; int irq; u32 lanes; -- cgit v0.10.2 From 275ca8210c96ffbbdf70df90b43961f56dd8f72a Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 23 Jul 2014 19:52:39 +0200 Subject: PCI: designware: Use pci_create_root_bus() instead of pci_scan_root_bus() Use pci_create_root_bus() similar to other PCI host controller drivers. The main problem with pci_scan_root_bus() is that it not only creates the root bus, but also activates all devices on the bus. This triggers PCI device driver probe routines, which fail because resources haven't been allocated. To work around this we made sure that the host controller driver is probed early and finishes resource allocation before any other device drivers are registered. Switching to pci_create_root_bus() allows us to get rid of this special handling. Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Reviewed-by: Pratyush Anand Acked-by: Mohit Kumar The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=92483df2bad7649caacad60ec7b0f8016e894e11 Change-Id: I7cd41d6390b2586b0f319c5ad4e844454776bf32 Reviewed-on: http://git.am.freescale.net:8181/19701 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 561fa30..6646283 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -814,14 +814,13 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) struct pci_bus *bus; struct pcie_port *pp = sys_to_pcie(sys); - if (pp) { - pp->root_bus_nr = sys->busnr; - bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops, - sys, &sys->resources); - } else { - bus = NULL; - BUG(); - } + pp->root_bus_nr = sys->busnr; + bus = pci_create_root_bus(pp->dev, sys->busnr, + &dw_pcie_ops, sys, &sys->resources); + if (!bus) + return NULL; + + pci_scan_child_bus(bus); if (bus && pp->ops->scan_bus) pp->ops->scan_bus(pp); -- cgit v0.10.2 From 2dbf8e6609737a4085a5b74bbfe205369bf5f9b5 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 23 Jul 2014 19:52:40 +0200 Subject: PCI: designware: Remove pci_assign_unassigned_resources() from dw_pcie_host_init() The pci_common_init_dev() call right before will already handle the device resource allocation, so this call was a no-op. Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Acked-by: Mohit Kumar The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=8ddebc4103e6544bd31f0c97e55491387717a124 Change-Id: I99d25006888b16545605bf87ee33c394c22e2f55 Reviewed-on: http://git.am.freescale.net:8181/19702 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 6646283..538bbf3 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -581,7 +581,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) dw_pci.private_data = (void **)&pp; pci_common_init_dev(pp->dev, &dw_pci); - pci_assign_unassigned_resources(); #ifdef CONFIG_PCI_DOMAINS dw_pci.domain++; #endif -- cgit v0.10.2 From 0d34bc93fde2e24036f93b8f17e8347cef5ae915 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 5 Sep 2014 09:37:55 -0600 Subject: PCI: designware: Check private_data validity in single place The driver had checks for this sprinkled all over. As we call sys_to_pcie() before every instance of this check, we can move the check to this single location to make things clear. Removing the statements after BUG[_ON]() is safe as the kernel is halted at this point anyway. Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han Acked-by: Mohit Kumar The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=84a263f39403ca3b399af77499876e02e634b00b Change-Id: I79866011d82e6b12daaef6464bdf79c5adccc0ca Reviewed-on: http://git.am.freescale.net:8181/19703 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 538bbf3..12c42fc 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -73,6 +73,8 @@ static unsigned long global_io_offset; static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) { + BUG_ON(!sys->private_data); + return sys->private_data; } @@ -261,11 +263,6 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) int irq, pos0, pos1, i; struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); - if (!pp) { - BUG(); - return -EINVAL; - } - pos0 = find_first_zero_bit(pp->msi_irq_in_use, MAX_MSI_IRQS); if (pos0 % no_irqs) { @@ -326,10 +323,6 @@ static void clear_irq(unsigned int irq) /* get the port structure */ msi = irq_data_get_msi(data); pp = sys_to_pcie(msi->dev->bus->sysdata); - if (!pp) { - BUG(); - return; - } /* undo what was done in assign_irq */ pos = data->hwirq; @@ -350,11 +343,6 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, struct msi_msg msg; struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); - if (!pp) { - BUG(); - return -EINVAL; - } - pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, &msg_ctr); msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; @@ -729,11 +717,6 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, struct pcie_port *pp = sys_to_pcie(bus->sysdata); int ret; - if (!pp) { - BUG(); - return -EINVAL; - } - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { *val = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; @@ -758,11 +741,6 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, struct pcie_port *pp = sys_to_pcie(bus->sysdata); int ret; - if (!pp) { - BUG(); - return -EINVAL; - } - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) return PCIBIOS_DEVICE_NOT_FOUND; @@ -790,9 +768,6 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) pp = sys_to_pcie(sys); - if (!pp) - return 0; - if (global_io_offset < SZ_1M && pp->config.io_size > 0) { sys->io_offset = global_io_offset - pp->config.io_bus_addr; pci_ioremap_io(global_io_offset, pp->io_base); -- cgit v0.10.2 From ed10ee2cda3e54f21880bc72b33a621830bec064 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Fri, 5 Sep 2014 17:48:54 -0600 Subject: PCI: designware: Fold struct pcie_port_info into struct pcie_port The struct pcie_port_info doesn't contain any exclusive information compared to other elements of struct pcie_port. So, keeping a separate structure does not seem very logical. Therefore remove this struct and embed its elements directly into struct pcie_port. Signed-off-by: Pratyush Anand Signed-off-by: Bjorn Helgaas Acked-by: Mohit Kumar The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=adf70fc087b1750c3792cd56abc6a45e49bb3a11 Change-Id: I2f67951e1c7fb3f29477a2889b7ae468071dd1f1 Reviewed-on: http://git.am.freescale.net:8181/19704 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 12c42fc..5d720c2 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -423,16 +423,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp) cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { - pp->config.cfg0_size = resource_size(cfg_res)/2; - pp->config.cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_size = resource_size(cfg_res)/2; + pp->cfg1_size = resource_size(cfg_res)/2; pp->cfg0_base = cfg_res->start; - pp->cfg1_base = cfg_res->start + pp->config.cfg0_size; + pp->cfg1_base = cfg_res->start + pp->cfg0_size; /* Find the untranslated configuration space address */ index = of_property_match_string(np, "reg-names", "config"); addrp = of_get_address(np, index, false, false); pp->cfg0_mod_base = of_read_number(addrp, ns); - pp->cfg1_mod_base = pp->cfg0_mod_base + pp->config.cfg0_size; + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; } else { dev_err(pp->dev, "missing *config* reg space\n"); } @@ -455,8 +455,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) IO_SPACE_LIMIT, range.pci_addr + range.size + global_io_offset); - pp->config.io_size = resource_size(&pp->io); - pp->config.io_bus_addr = range.pci_addr; + pp->io_size = resource_size(&pp->io); + pp->io_bus_addr = range.pci_addr; pp->io_base = range.cpu_addr; /* Find the untranslated IO space address */ @@ -466,8 +466,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); pp->mem.name = "MEM"; - pp->config.mem_size = resource_size(&pp->mem); - pp->config.mem_bus_addr = range.pci_addr; + pp->mem_size = resource_size(&pp->mem); + pp->mem_bus_addr = range.pci_addr; /* Find the untranslated MEM space address */ pp->mem_mod_base = of_read_number(parser.range - @@ -475,16 +475,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (restype == 0) { of_pci_range_to_resource(&range, np, &pp->cfg); - pp->config.cfg0_size = resource_size(&pp->cfg)/2; - pp->config.cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_size = resource_size(&pp->cfg)/2; + pp->cfg1_size = resource_size(&pp->cfg)/2; pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; + pp->cfg1_base = pp->cfg.start + pp->cfg0_size; /* Find the untranslated configuration space address */ pp->cfg0_mod_base = of_read_number(parser.range - parser.np + na, ns); pp->cfg1_mod_base = pp->cfg0_mod_base + - pp->config.cfg0_size; + pp->cfg0_size; } } @@ -512,7 +512,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) if (!pp->va_cfg0_base) { pp->cfg0_base = pp->cfg.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, - pp->config.cfg0_size); + pp->cfg0_size); if (!pp->va_cfg0_base) { dev_err(pp->dev, "error with ioremap in function\n"); return -ENOMEM; @@ -520,9 +520,9 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (!pp->va_cfg1_base) { - pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; + pp->cfg1_base = pp->cfg.start + pp->cfg0_size; pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, - pp->config.cfg1_size); + pp->cfg1_size); if (!pp->va_cfg1_base) { dev_err(pp->dev, "error with ioremap\n"); return -ENOMEM; @@ -583,7 +583,7 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->config.cfg0_size - 1, + dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -599,7 +599,7 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->config.cfg1_size - 1, + dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -614,10 +614,10 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->config.mem_size - 1, + dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr), + dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), PCIE_ATU_UPPER_TARGET); dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); } @@ -630,10 +630,10 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_mod_base + pp->config.io_size - 1, + dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr), + dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), PCIE_ATU_UPPER_TARGET); dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); } @@ -768,15 +768,15 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) pp = sys_to_pcie(sys); - if (global_io_offset < SZ_1M && pp->config.io_size > 0) { - sys->io_offset = global_io_offset - pp->config.io_bus_addr; + if (global_io_offset < SZ_1M && pp->io_size > 0) { + sys->io_offset = global_io_offset - pp->io_bus_addr; pci_ioremap_io(global_io_offset, pp->io_base); global_io_offset += SZ_64K; pci_add_resource_offset(&sys->resources, &pp->io, sys->io_offset); } - sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr; + sys->mem_offset = pp->mem.start - pp->mem_bus_addr; pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pp->busn); @@ -833,7 +833,6 @@ static struct hw_pci dw_pci = { void dw_pcie_setup_rc(struct pcie_port *pp) { - struct pcie_port_info *config = &pp->config; u32 val; u32 membase; u32 memlimit; @@ -888,7 +887,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* setup memory base, memory limit */ membase = ((u32)pp->mem_base & 0xfff00000) >> 16; - memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000; + memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000; val = memlimit | membase; dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index a476e60..48f8670 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -14,15 +14,6 @@ #ifndef _PCIE_DESIGNWARE_H #define _PCIE_DESIGNWARE_H -struct pcie_port_info { - u32 cfg0_size; - u32 cfg1_size; - u32 io_size; - u32 mem_size; - phys_addr_t io_bus_addr; - phys_addr_t mem_bus_addr; -}; - /* * Maximum number of MSI IRQs can be 256 per controller. But keep * it 32 as of now. Probably we will never need more than 32. If needed, @@ -38,18 +29,23 @@ struct pcie_port { u64 cfg0_base; u64 cfg0_mod_base; void __iomem *va_cfg0_base; + u32 cfg0_size; u64 cfg1_base; u64 cfg1_mod_base; void __iomem *va_cfg1_base; + u32 cfg1_size; u64 io_base; u64 io_mod_base; + phys_addr_t io_bus_addr; + u32 io_size; u64 mem_base; u64 mem_mod_base; + phys_addr_t mem_bus_addr; + u32 mem_size; struct resource cfg; struct resource io; struct resource mem; struct resource busn; - struct pcie_port_info config; int irq; u32 lanes; struct pcie_host_ops *ops; -- cgit v0.10.2 From a600e1b99486f180401264944c534c151b87e4c8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 22 Sep 2014 14:52:07 -0600 Subject: PCI: designware: Use NULL instead of false of_get_address() expects pointers in the third and fourth parameters. Pass NULL in order to fix the following sparse warnings: drivers/pci/host/pcie-designware.c:433:51: warning: Using plain integer as NULL pointer drivers/pci/host/pcie-designware.c:433:58: warning: Using plain integer as NULL pointer Signed-off-by: Fabio Estevam Signed-off-by: Bjorn Helgaas Acked-by: Lucas Stach Acked-by: Jingoo Han The patch comes from https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=9f0dbe087bff6cfffcf8b0c25c08891d66b987be Change-Id: I5c034c7ceeb951b308fb2dcb82d6a191db94c935 Reviewed-on: http://git.am.freescale.net:8181/19705 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 5d720c2..1c59e4e 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -430,7 +430,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) /* Find the untranslated configuration space address */ index = of_property_match_string(np, "reg-names", "config"); - addrp = of_get_address(np, index, false, false); + addrp = of_get_address(np, index, NULL, NULL); pp->cfg0_mod_base = of_read_number(addrp, ns); pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; } else { -- cgit v0.10.2 From c6e5e81662b199625ab309b37f7cb062ca716c43 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 23 Sep 2014 11:02:42 -0600 Subject: PCI/MSI: Remove "pos" from the struct msi_desc msi_attrib "msi_attrib.pos" is only used for MSI (not MSI-X), and we already cache the MSI capability offset in "dev->msi_cap". Remove "pos" from the struct msi_attrib and use "dev->msi_cap" directly. [bhelgaas: changelog, fix whitespace] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas The patch is part of: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=d591f2eeaf13bc6759069f14b6bf1b1ada116774 Change-Id: Ib04e9b6db0c44b4d8e003fadd25e2f14e071c39c Reviewed-on: http://git.am.freescale.net:8181/19706 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1c59e4e..8a01ea9 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -343,9 +343,8 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, struct msi_msg msg; struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); - pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, - &msg_ctr); - msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; + pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &msg_ctr); + msgvec = (msg_ctr & PCI_MSI_FLAGS_QSIZE) >> 4; if (msgvec == 0) msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; if (msgvec > 5) -- cgit v0.10.2 From b1d0632015abedc155c4e91583ec465048f4dd98 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Tue, 23 Sep 2014 22:28:56 +0800 Subject: PCI: designware: Fix configuration base address when using 'reg' The code has calculated cfg0_base and cfg1_base when parsing 'reg' or 'ranges' property of PCI DTS node, so remove duplicate calculation. When using 'reg', resource cfg is not used, so this code computed an incorrect configuration base. Signed-off-by: Minghuan Lian Signed-off-by: Bjorn Helgaas Acked-by: Mohit KUMAR The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=ec98e9ab6f2475ff57c12d069e78b90548c0f60e Change-Id: Iac12f15871759879a572b77c0f1e44fd50e0bd4f Reviewed-on: http://git.am.freescale.net:8181/19707 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 8a01ea9..4aa8146 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -509,7 +509,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->mem_base = pp->mem.start; if (!pp->va_cfg0_base) { - pp->cfg0_base = pp->cfg.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, pp->cfg0_size); if (!pp->va_cfg0_base) { @@ -519,7 +518,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (!pp->va_cfg1_base) { - pp->cfg1_base = pp->cfg.start + pp->cfg0_size; pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, pp->cfg1_size); if (!pp->va_cfg1_base) { -- cgit v0.10.2 From 12123b0fb0ec880fdd2f1e088d3139a101933593 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Tue, 23 Sep 2014 22:28:57 +0800 Subject: PCI: designware: Fix IO resource end address calculation End address should be equal to start_addr + size - 1. Fix PCI IO resource end address calculation. Signed-off-by: Minghuan Lian Signed-off-by: Bjorn Helgaas Acked-by: Mohit KUMAR The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=0c61ea77cceafd1134225099961c2df0866b500f Change-Id: I1c820af498f99bca3ea235f297182a6620fa7ee1 Reviewed-on: http://git.am.freescale.net:8181/19708 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 4aa8146..5c56eea 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -453,7 +453,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->io.end = min_t(resource_size_t, IO_SPACE_LIMIT, range.pci_addr + range.size - + global_io_offset); + + global_io_offset - 1); pp->io_size = resource_size(&pp->io); pp->io_bus_addr = range.pci_addr; pp->io_base = range.cpu_addr; -- cgit v0.10.2 From 279cc277d1ea921a71c575fc1ed9f7ad51e11a3d Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Tue, 23 Sep 2014 22:28:58 +0800 Subject: PCI: designware: Rename get_msi_data() to get_msi_addr() The struct pcie_host_ops .get_msi_data() method returns the MSI message address. To accurately express its purpose, rename it to .get_msi_addr(). Signed-off-by: Minghuan Lian Signed-off-by: Bjorn Helgaas Acked-by: Mohit KUMAR The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=450e344e421b9f555261a2d97952d9e71d4cb082 Change-Id: I34a975f242addd70a7c7682e9e867b1414137d12 Reviewed-on: http://git.am.freescale.net:8181/19709 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 5c56eea..a90f9b4d 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -360,8 +360,8 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, */ desc->msi_attrib.multiple = msgvec; - if (pp->ops->get_msi_data) - msg.address_lo = pp->ops->get_msi_data(pp); + if (pp->ops->get_msi_addr) + msg.address_lo = pp->ops->get_msi_addr(pp); else msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 48f8670..904e40a 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -70,7 +70,7 @@ struct pcie_host_ops { void (*host_init)(struct pcie_port *pp); void (*msi_set_irq)(struct pcie_port *pp, int irq); void (*msi_clear_irq)(struct pcie_port *pp, int irq); - u32 (*get_msi_data)(struct pcie_port *pp); + u32 (*get_msi_addr)(struct pcie_port *pp); void (*scan_bus)(struct pcie_port *pp); int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip); }; -- cgit v0.10.2 From bca4f700f06f87d8ba41f97f4a4180b2533196f2 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Tue, 23 Sep 2014 22:28:59 +0800 Subject: PCI: designware: Add get_msi_data() to pcie_host_ops Add a struct pcie_host_ops .get_msi_data() method for platforms to return their special MSI message data. Signed-off-by: Minghuan Lian Signed-off-by: Bjorn Helgaas Acked-by: Mohit KUMAR The patch comes from: https://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?h=next&id=24832b4de315ad00e5430a53772750dfcf18514d Change-Id: Iecbb0f94a4f04eb96e6a1c2a8d9c3768a1aa11bf Reviewed-on: http://git.am.freescale.net:8181/19710 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index a90f9b4d..f727445 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -365,7 +365,12 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, else msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; - msg.data = pos; + + if (pp->ops->get_msi_data) + msg.data = pp->ops->get_msi_data(pp, pos); + else + msg.data = pos; + write_msi_msg(irq, &msg); return 0; diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 904e40a..c625675 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -71,6 +71,7 @@ struct pcie_host_ops { void (*msi_set_irq)(struct pcie_port *pp, int irq); void (*msi_clear_irq)(struct pcie_port *pp, int irq); u32 (*get_msi_addr)(struct pcie_port *pp); + u32 (*get_msi_data)(struct pcie_port *pp, int pos); void (*scan_bus)(struct pcie_port *pp); int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip); }; -- cgit v0.10.2 From 39b7c64c722ec47e2799742b14b77df009765080 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 29 Jul 2013 23:57:57 -0700 Subject: mtd: m25p80: remove obsolete FIXME The FIXME and NOTE have already been fixed (we have FAST_READ support). Signed-off-by: Brian Norris Reviewed-by: Sourav Poddar Acked-by: Marek Vasut (cherry picked from commit 1a874e91018ea99d7f012a0824669aa9ed833d6f) Change-Id: I02d75e34033100678d65466628217bd493e82135 Reviewed-on: http://git.am.freescale.net:8181/20034 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index dfa5af13..879d9cd 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -368,10 +368,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_message_init(&m); memset(t, 0, (sizeof t)); - /* NOTE: - * OPCODE_FAST_READ (if available) is faster. - * Should add 1 byte DUMMY_BYTE. - */ t[0].tx_buf = flash->command; t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); spi_message_add_tail(&t[0], &m); @@ -389,11 +385,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, return 1; } - /* FIXME switch to OPCODE_FAST_READ. It's required for higher - * clocks; and at this writing, every chip this driver handles - * supports that opcode. - */ - /* Set up the write data buffer. */ opcode = flash->read_opcode; flash->command[0] = opcode; -- cgit v0.10.2 From a80b39bb6ee4a8a70fc47ec632aa76f9de8e108e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 19 Aug 2013 21:30:22 -0700 Subject: mtd: m25p80: remove M25PXX_USE_FAST_READ Kconfig Remove the compile-time option for FAST_READ, since we have run-time support for detecting it. This refactors the logic for enabling fast-read, such that for DT-enabled devices, we honor the "m25p,fast-read" property but for non-DT devices, we default to using FAST_READ whenever the flash device supports it. Normal READ and FAST_READ differ only in the following: * FAST_READ supports SPI higher clock frequencies [1] * number of dummy cycles; FAST_READ requires 8 dummy cycles (whereas READ requires 0) to allow the flash sufficient setup time, even when running at higher clock speeds Thus, for flash chips which support FAST_READ, there is otherwise no limiting reason why we cannot use the FAST_READ opcode instead of READ. It simply allows the SPI controller to run at higher clock rates. So theoretically, nobody should be needing the compile-time option anyway. [1] I have a Spansion S25FL128S datasheet which says: "The maximum operating clock frequency for the READ command is 50 MHz." And: "The maximum operating clock frequency for FAST READ command is 133 MHz." Signed-off-by: Brian Norris (cherry picked from commit ddba7c5ad797f4b878f4e177ef300c1f9837cd29) Change-Id: I205637becab372f43d3e8e741f20d35aac79a5fe Reviewed-on: http://git.am.freescale.net:8181/20036 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 74ab4b7..0128138 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -95,13 +95,6 @@ config MTD_M25P80 if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction. -config M25PXX_USE_FAST_READ - bool "Use FAST_READ OPCode allowing SPI CLK >= 50MHz" - depends on MTD_M25P80 - default y - help - This option enables FAST_READ access supported by ST M25Pxx. - config MTD_SPEAR_SMI tristate "SPEAR MTD NOR Support through SMI controller" depends on PLAT_SPEAR diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 879d9cd..b670eaa 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1066,13 +1066,14 @@ static int m25p_probe(struct spi_device *spi) flash->page_size = info->page_size; flash->mtd.writebufsize = flash->page_size; - flash->fast_read = false; - if (np && of_property_read_bool(np, "m25p,fast-read")) + if (np) + /* If we were instantiated by DT, use it */ + flash->fast_read = of_property_read_bool(np, "m25p,fast-read"); + else + /* If we weren't instantiated by DT, default to fast-read */ flash->fast_read = true; -#ifdef CONFIG_M25PXX_USE_FAST_READ - flash->fast_read = true; -#endif + /* Some devices cannot do fast-read, no matter what DT tells us */ if (info->flags & M25P_NO_FR) flash->fast_read = false; -- cgit v0.10.2 From 15f6139b9263a13c35009eb2bc69f08bc31d23d7 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 26 Sep 2014 09:07:11 +0800 Subject: arm: ls1021a: remove dma_zone_size defination CONFIG_ZONE_DMA is enough to claim the dma limitation, and no need for limitation smaller than 4GB, so remove the dma_zone_size defination. Signed-off-by: Jingchang Lu --- the upstream include this change in patchwork is: https://patchwork.kernel.org/patch/4946151/ Change-Id: Ia2ed1fd18519e1e2553e2aa8ce1c1729657a6ed3 Reviewed-on: http://git.am.freescale.net:8181/19874 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/mach-imx/mach-ls1021a.c b/arch/arm/mach-imx/mach-ls1021a.c index d284cdb..3895ffc 100644 --- a/arch/arm/mach-imx/mach-ls1021a.c +++ b/arch/arm/mach-imx/mach-ls1021a.c @@ -24,9 +24,6 @@ static const char *ls1021a_dt_compat[] __initdata = { }; DT_MACHINE_START(LS1021A, "Freescale LS1021A") -#ifdef CONFIG_ZONE_DMA - .dma_zone_size = SZ_128M, -#endif .smp = smp_ops(ls1021a_smp_ops), .init_machine = ls1021a_init_machine, .dt_compat = ls1021a_dt_compat, -- cgit v0.10.2 From 455db517cddbd6dfc2608594199a84432470cf92 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Mon, 1 Sep 2014 16:10:13 +0000 Subject: PCI: Layerscape: Add Layerscape PCIe driver Add support for Freescale Layerscape PCIe controller. This driver re-uses the designware core code. Signed-off-by: Minghuan Lian Change-Id: I799aa1cd488a44b4ba9c198694f75d56b2294a03 Reviewed-on: http://git.am.freescale.net:8181/19711 Tested-by: Review Code-CDREVIEW Reviewed-by: Mingkai Hu Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt new file mode 100644 index 0000000..d7b0026 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt @@ -0,0 +1,46 @@ +Freescale Layerscape PCIe controller + +This PCIe host controller is based on the Synopsis Designware PCIe IP +and thus inherits all the common properties defined in designware-pcie.txt. + +Required properties: +- compatible: should contain the platform identifier such as "fsl,ls1021a-pcie" +- reg: base addresses and lengths of the PCIe controller +- interrupts: A list of interrupt outputs of the controller. Must contain an + entry for each entry in the interrupt-names property. +- interrupt-names: Must include the following entries: + "intr": The interrupt that is asserted for controller interrupts + "msi": The interrupt that is asserted when an MSI is received + "pme": The interrupt that is asserted when PME state changes +- fsl,pcie-scfg: Must include two entries. + The first entry must be a link to the SCFG device node + The second entry must be '0' or '1' based on physical PCIe controller index. + used to get SCFG PEXN registers + +Example: + + pcie@3400000 { + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie"; + reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */ + 0x40 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = , /* controller interrupt */ + , /* MSI interrupt */ + ; /* PME interrupt */ + interrupt-names = "intr", "msi", "pme"; + fsl,pcie-scfg = <&scfg 0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + num-lanes = <4>; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* prefetchable memory */ + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 3d95048..d6b60f6 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -19,4 +19,12 @@ config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA +config PCI_LAYERSCAPE + bool "Freescale Layerscape PCIe controller" + depends on OF + select PCIE_DW + select MFD_SYSCON + help + Say Y here if you want PCIe controller support on Layerscape SoCs. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index c9a997b..e293b51 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c new file mode 100644 index 0000000..8f5e65e --- /dev/null +++ b/drivers/pci/host/pci-layerscape.c @@ -0,0 +1,274 @@ +/* + * PCIe host controller driver for Freescale Layerscape SoCs + * + * Copyright (C) 2014 Freescale Semiconductor. + * + * Author: Minghuan Lian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +/* PEX1/2 Misc Ports Status Register */ +#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4) +#define LTSSM_STATE_SHIFT 20 +#define LTSSM_STATE_MASK 0x3f +#define LTSSM_PCIE_L0 0x11 /* L0 state */ + +/* SCFG MSI register */ +#define SCFG_SPIMSICR 0x40 +#define SCFG_SPIMSICLRCR 0x90 + +#define MSI_LS1021A_ADDR 0x1570040 +#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx) + +/* Symbol Timer Register and Filter Mask Register 1 */ +#define PCIE_STRFMR1 0x71c + +struct ls_pcie { + struct list_head node; + struct device *dev; + struct pci_bus *bus; + void __iomem *dbi; + struct regmap *scfg; + struct pcie_port pp; + int index; + int msi_irq; +}; + +#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) + +static int ls_pcie_link_up(struct pcie_port *pp) +{ + u32 state; + struct ls_pcie *pcie = to_ls_pcie(pp); + + regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); + state = bitrev32(state); + state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; + + if (state < LTSSM_PCIE_L0) + return 0; + + return 1; +} + +static u32 ls_pcie_get_msi_addr(struct pcie_port *pp) +{ + return MSI_LS1021A_ADDR; +} + +static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos) +{ + struct ls_pcie *pcie = to_ls_pcie(pp); + + return MSI_LS1021A_DATA(pcie->index); +} + +static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data) +{ + struct pcie_port *pp = data; + struct ls_pcie *pcie = to_ls_pcie(pp); + unsigned int msi_irq; + + /* clear the interrupt */ + regmap_write(pcie->scfg, SCFG_SPIMSICLRCR, + MSI_LS1021A_DATA(pcie->index)); + + msi_irq = irq_find_mapping(pp->irq_domain, 0); + if (!msi_irq) { + /* + * that's weird who triggered this? + * just clear it + */ + dev_err(pcie->dev, "unexpected MSI\n"); + return IRQ_NONE; + } + + generic_handle_irq(msi_irq); + return IRQ_HANDLED; +} + +static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ +} + +static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ +} + +static void ls1021a_pcie_msi_fixup(struct pcie_port *pp) +{ + int i; + + /* + * LS1021A has only one MSI interrupt + * Set all msi interrupts as used except the first one + */ + for (i = 1; i < MAX_MSI_IRQS; i++) + set_bit(i, pp->msi_irq_in_use); +} + +static void ls_pcie_host_init(struct pcie_port *pp) +{ + struct ls_pcie *pcie = to_ls_pcie(pp); + int count = 0; + u32 val; + + dw_pcie_setup_rc(pp); + + while (!ls_pcie_link_up(pp)) { + usleep_range(100, 1000); + count++; + if (count >= 200) { + dev_err(pp->dev, "phy link never came up\n"); + return; + } + } + + if (of_device_is_compatible(pcie->dev->of_node, "fsl,ls1021a-pcie")) { + /* + * LS1021A Workaround for internal TKT228622 + * to fix the INTx hang issue + */ + val = ioread32(pcie->dbi + PCIE_STRFMR1); + val &= 0xffff; + iowrite32(val, pcie->dbi + PCIE_STRFMR1); + + ls1021a_pcie_msi_fixup(pp); + } +} + +static struct pcie_host_ops ls_pcie_host_ops = { + .link_up = ls_pcie_link_up, + .host_init = ls_pcie_host_init, + .msi_set_irq = ls_pcie_msi_set_irq, + .msi_clear_irq = ls_pcie_msi_clear_irq, + .get_msi_addr = ls_pcie_get_msi_addr, + .get_msi_data = ls_pcie_get_msi_data, +}; + +static int ls_add_pcie_port(struct ls_pcie *pcie) +{ + struct pcie_port *pp; + int ret; + + if (!pcie) + return -EINVAL; + + pp = &pcie->pp; + pp->dev = pcie->dev; + pp->dbi_base = pcie->dbi; + pp->msi_irq = pcie->msi_irq; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + ret = devm_request_irq(pp->dev, pp->msi_irq, + ls_pcie_msi_irq_handler, + IRQF_SHARED, "ls-pcie-msi", pp); + if (ret) { + dev_err(pp->dev, "failed to request msi irq\n"); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &ls_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(pp->dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init ls_pcie_probe(struct platform_device *pdev) +{ + struct ls_pcie *pcie; + struct resource *dbi_base; + u32 index[2]; + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = &pdev->dev; + + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!dbi_base) { + dev_err(&pdev->dev, "missing *regs* space\n"); + return -ENODEV; + } + + pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); + if (IS_ERR(pcie->dbi)) + return PTR_ERR(pcie->dbi); + + pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "fsl,pcie-scfg"); + if (IS_ERR(pcie->scfg)) { + dev_err(&pdev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(pcie->scfg); + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "fsl,pcie-scfg", index, 2); + if (ret) + return ret; + pcie->index = index[1]; + + pcie->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pcie->msi_irq < 0) { + dev_err(&pdev->dev, + "failed to get MSI IRQ: %d\n", pcie->msi_irq); + return pcie->msi_irq; + } + + ret = ls_add_pcie_port(pcie); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, pcie); + + return 0; +} + +static const struct of_device_id ls_pcie_of_match[] = { + { .compatible = "fsl,ls1021a-pcie" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ls_pcie_of_match); + +static struct platform_driver ls_pcie_driver = { + .driver = { + .name = "layerscape-pcie", + .owner = THIS_MODULE, + .of_match_table = ls_pcie_of_match, + }, +}; + +module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe); + +MODULE_AUTHOR("Minghuan Lian "); +MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 69929fb032e8c14b398427e0fda53136d0347cdd Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Wed, 3 Sep 2014 18:42:52 +0000 Subject: arm:dts:ls1021a: add PCIe device node Signed-off-by: Minghuan Lian Change-Id: I5deb88a99bd7b5d40251a4935d4d8a556abad7ae Reviewed-on: http://git.am.freescale.net:8181/19712 Tested-by: Review Code-CDREVIEW Reviewed-by: Jingchang Lu Reviewed-by: Mingkai Hu Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index ee95c8d..80747dc 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -121,7 +121,7 @@ }; scfg: scfg@1570000 { - compatible = "fsl,ls1021a-scfg"; + compatible = "fsl,ls1021a-scfg", "syscon"; reg = <0x0 0x1570000 0x0 0x10000>; }; @@ -562,6 +562,56 @@ interrupts = ; dr_mode = "host"; }; + + pcie@3400000 { + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie"; + reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */ + 0x40 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = , /* controller interrupt */ + , /* MSI interrupt */ + ; /* PME interrupt */ + interrupt-names = "intr", "msi", "pme"; + fsl,pcie-scfg = <&scfg 0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + num-lanes = <4>; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>; + }; + + pcie@3500000 { + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie"; + reg = <0x00 0x03500000 0x0 0x00010000 /* controller registers */ + 0x48 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = , + , + ; + interrupt-names = "intr", "msi", "pme"; + fsl,pcie-scfg = <&scfg 1>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + num-lanes = <2>; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x48 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0x82000000 0x0 0x40000000 0x48 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>; + }; }; dcsr { -- cgit v0.10.2 From ba916f0061712f0675d39c2a5ac095f71def6f5d Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Fri, 26 Sep 2014 16:21:48 +0800 Subject: Revert "arm: ls1021a: remove the FSL_SOC config added by SOC_LS1021A" This reverts commit 14bbc976701a2ebc62343d3122e5ff772060a35f. LS1021A shares IPs with sophisticated PowerPC platform, many PowerPC drivers have depends on FSL_SOC defination, so to consistent with this, FSL_SOC is introduced. Signed-off-by: Jingchang Lu Change-Id: Ie5a69b78d317d09f9fee54dde3f1cd4bffdb9588 Reviewed-on: http://git.am.freescale.net:8181/19915 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 5f43281..3948af8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -846,6 +846,9 @@ config SOC_VF610 help This enable support for Freescale Vybrid VF610 processor. +config FSL_SOC + bool + config SOC_LS1021A bool "Freescale LS1021A support" select CPU_V7 @@ -853,6 +856,7 @@ config SOC_LS1021A select CLKSRC_OF select HAVE_ARM_ARCH_TIMER select HAVE_SMP + select FSL_SOC select MIGHT_HAVE_PCI select PCI_DOMAINS if PCI select ZONE_DMA if ARM_LPAE -- cgit v0.10.2 From 73fe3c275ca0fdaa8c1721d8884293c42ff8689a Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Fri, 26 Sep 2014 18:04:35 +0800 Subject: mtd: m25p80: re-align ID entries No change in the table data. Signed-off-by: Brian Norris Reviewed-by: Sourav Poddar (cherry picked from commit 6e5d9bda27000c682a9b38f0466941007e295f82) Change-Id: I295d6300994b0a7f852cc4a2dcd3441bf2f813cb Reviewed-on: http://git.am.freescale.net:8181/20035 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index b670eaa..d6c5c57 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -741,17 +740,19 @@ static const struct spi_device_id m25p_ids[] = { { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, /* EON -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, - { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, - { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, - { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, - { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, 0) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, @@ -781,12 +782,11 @@ static const struct spi_device_id m25p_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, - { "n25q512a", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) }, /* PMC */ - { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, - { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, - { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). @@ -933,11 +933,9 @@ static int m25p_probe(struct spi_device *spi) struct flash_platform_data *data; struct m25p *flash; struct flash_info *info; - unsigned i, ret; + unsigned i; struct mtd_part_parser_data ppdata; struct device_node __maybe_unused *np = spi->dev.of_node; - struct resource res; - struct device_node *mnp = spi->master->dev.of_node; #ifdef CONFIG_MTD_OF_PARTS if (!of_device_is_available(np)) @@ -1015,17 +1013,8 @@ static int m25p_probe(struct spi_device *spi) if (data && data->name) flash->mtd.name = data->name; - else { - ret = of_address_to_resource(mnp, 0, &res); - if (ret) { - dev_err(&spi->dev, "failed to get spi master resource\n"); - return ret; - } - flash->mtd.name = kasprintf(GFP_KERNEL, "spi%x.%d", - (unsigned)res.start, spi->chip_select); - if (!flash->mtd.name) - return -ENOMEM; - } + else + flash->mtd.name = dev_name(&spi->dev); flash->mtd.type = MTD_NORFLASH; flash->mtd.writesize = 1; -- cgit v0.10.2 From 120bcd121c31f4c5b5347bcf5ca7c92ef04a759b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 23 Oct 2013 19:34:46 -0700 Subject: mtd: m25p80: remove 'disabled' device check It seems like the following commit was never necessary commit 5f949137952020214cd167093dd7be448f21c079 Author: Shaohui Xie Date: Fri Oct 14 15:49:00 2011 +0800 mtd: m25p80: don't probe device which has status of 'disabled' because it duplicates the code in of_platform_device_create_pdata() which ensures that 'disabled' nodes are never instantiated. Also, drop the __maybe_unused. Signed-off-by: Brian Norris Reviewed-by: Sourav Poddar Reviewed-by: Grant Likely Cc: Rob Herring Cc: (cherry picked from commit dc525ff4705cee2291b1637a650489aca86ac937) Change-Id: I01e898a2d0cbf26e144e498eea6b0eec53c5cfdf Reviewed-on: http://git.am.freescale.net:8181/20037 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index d6c5c57..a1dc49a 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -935,12 +935,7 @@ static int m25p_probe(struct spi_device *spi) struct flash_info *info; unsigned i; struct mtd_part_parser_data ppdata; - struct device_node __maybe_unused *np = spi->dev.of_node; - -#ifdef CONFIG_MTD_OF_PARTS - if (!of_device_is_available(np)) - return -ENODEV; -#endif + struct device_node *np = spi->dev.of_node; /* Platform data helps sort out which chip type we have, as * well as how this board partitions it. If we don't have -- cgit v0.10.2 From ffaf4eadb5ce9c1a6a03d0793f50e2c4567c478a Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 23 Oct 2013 13:38:09 -0700 Subject: mtd: m25p80: add support for Macronix mx25l3255e A new 32Mbit SPI NOR flash from Macronix. Nothing special. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut (cherry picked from commit 5ff14821a37c92d139181c3fbc939afa993b959f) Change-Id: I677b8981e01bbdc7496b86674ab56749959edbeb Reviewed-on: http://git.am.freescale.net:8181/20038 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index a1dc49a..5897889 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -769,6 +769,7 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, -- cgit v0.10.2 From a821def088c0ca9511e9675e79bd113a695e2e70 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Sun, 27 Oct 2013 15:42:12 -0700 Subject: mtd: m25p80: fixup device removal failure path Device removal should fail if MTD unregistration fails. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut (cherry picked from commit 9650b9bec61d861b6b59d09eb389410b05d196e4) Change-Id: Ia597ef093f4d1492f9d04c4a7bbaa5be32e5d161 Reviewed-on: http://git.am.freescale.net:8181/20039 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 5897889..7eda71d 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1125,9 +1125,7 @@ static int m25p_remove(struct spi_device *spi) struct m25p *flash = spi_get_drvdata(spi); /* Clean up MTD stuff. */ - mtd_device_unregister(&flash->mtd); - - return 0; + return mtd_device_unregister(&flash->mtd); } -- cgit v0.10.2 From 51a3d734e50877ceb4f04317e245e5c7bec2ae40 Mon Sep 17 00:00:00 2001 From: Sourav Poddar Date: Wed, 6 Nov 2013 20:05:34 +0530 Subject: drivers: mtd: m25p80: convert "bool" read check into an enum This is a cleanup prior to adding quad read support. This will facilitate easy addition of more read commands check under an enum rather that defining a separate bool for it. Signed-off-by: Sourav Poddar Signed-off-by: Brian Norris (cherry picked from commit 8552b439aba7f32063755d23f79ca27b4d0a3115) Change-Id: I9c8d509ef64ff555c22bde09bf13f74b65655f4d Reviewed-on: http://git.am.freescale.net:8181/20040 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7eda71d..04f8a24 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -84,6 +84,11 @@ /****************************************************************************/ +enum read_type { + M25P80_NORMAL = 0, + M25P80_FAST, +}; + struct m25p { struct spi_device *spi; struct mutex lock; @@ -94,7 +99,7 @@ struct m25p { u8 read_opcode; u8 program_opcode; u8 *command; - bool fast_read; + enum read_type flash_read; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -350,6 +355,24 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) } /* + * Dummy Cycle calculation for different type of read. + * It can be used to support more commands with + * different dummy cycle requirements. + */ +static inline int m25p80_dummy_cycles_read(struct m25p *flash) +{ + switch (flash->flash_read) { + case M25P80_FAST: + return 1; + case M25P80_NORMAL: + return 0; + default: + dev_err(&flash->spi->dev, "No valid read type supported\n"); + return -1; + } +} + +/* * Read an address range from the flash chip. The address range * may be any size provided it is within the physical boundaries. */ @@ -360,6 +383,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_transfer t[2]; struct spi_message m; uint8_t opcode; + int dummy; pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), __func__, (u32)from, len); @@ -367,8 +391,14 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_message_init(&m); memset(t, 0, (sizeof t)); + dummy = m25p80_dummy_cycles_read(flash); + if (dummy < 0) { + dev_err(&flash->spi->dev, "No valid read command supported\n"); + return -EINVAL; + } + t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); + t[0].len = m25p_cmdsz(flash) + dummy; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; @@ -391,8 +421,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_sync(flash->spi, &m); - *retlen = m.actual_length - m25p_cmdsz(flash) - - (flash->fast_read ? 1 : 0); + *retlen = m.actual_length - m25p_cmdsz(flash) - dummy; mutex_unlock(&flash->lock); @@ -1051,22 +1080,31 @@ static int m25p_probe(struct spi_device *spi) flash->page_size = info->page_size; flash->mtd.writebufsize = flash->page_size; - if (np) + if (np) { /* If we were instantiated by DT, use it */ - flash->fast_read = of_property_read_bool(np, "m25p,fast-read"); - else + if (of_property_read_bool(np, "m25p,fast-read")) + flash->flash_read = M25P80_FAST; + } else { /* If we weren't instantiated by DT, default to fast-read */ - flash->fast_read = true; + flash->flash_read = M25P80_FAST; + } /* Some devices cannot do fast-read, no matter what DT tells us */ if (info->flags & M25P_NO_FR) - flash->fast_read = false; + flash->flash_read = M25P80_NORMAL; /* Default commands */ - if (flash->fast_read) + switch (flash->flash_read) { + case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ; - else + break; + case M25P80_NORMAL: flash->read_opcode = OPCODE_NORM_READ; + break; + default: + dev_err(&flash->spi->dev, "No Read opcode defined\n"); + return -EINVAL; + } flash->program_opcode = OPCODE_PP; @@ -1077,9 +1115,14 @@ static int m25p_probe(struct spi_device *spi) flash->addr_width = 4; if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Dedicated 4-byte command set */ - flash->read_opcode = flash->fast_read ? - OPCODE_FAST_READ_4B : - OPCODE_NORM_READ_4B; + switch (flash->flash_read) { + case M25P80_FAST: + flash->read_opcode = OPCODE_FAST_READ_4B; + break; + case M25P80_NORMAL: + flash->read_opcode = OPCODE_NORM_READ_4B; + break; + } flash->program_opcode = OPCODE_PP_4B; /* No small sector erase for 4-byte command set */ flash->erase_opcode = OPCODE_SE_4B; -- cgit v0.10.2 From c673d9db5397ecd085936b8314030e6660ef2869 Mon Sep 17 00:00:00 2001 From: Sourav Poddar Date: Wed, 6 Nov 2013 20:05:35 +0530 Subject: drivers: mtd: m25p80: add quad read support Some flash also support quad read mode. Adding support for quad read mode in m25p80 for Spansion and Macronix flash. [Tweaked by Brian] With this patch, quad-read support will override fast-read and normal-read, if the SPI controller and flash chip both support it. Signed-off-by: Sourav Poddar Tested-by: Sourav Poddar Reviewed-by: Marek Vasut Signed-off-by: Brian Norris (cherry picked from commit 3487a63955c34ea508bcf4ca5131ddd953876e2d) Change-Id: Ib5ea483806cb7237bc23d4219626a198b09aecce Reviewed-on: http://git.am.freescale.net:8181/20041 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 04f8a24..7dc2c14 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -41,6 +41,7 @@ #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ #define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -48,10 +49,12 @@ #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */ #define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ @@ -76,6 +79,11 @@ #define SR_BP2 0x10 /* Block protect 2 */ #define SR_SRWD 0x80 /* SR write protect */ +#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ + +/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ + /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 6 @@ -87,6 +95,7 @@ enum read_type { M25P80_NORMAL = 0, M25P80_FAST, + M25P80_QUAD, }; struct m25p { @@ -136,6 +145,26 @@ static int read_sr(struct m25p *flash) } /* + * Read configuration register, returning its value in the + * location. Return the configuration register value. + * Returns negative if error occured. + */ +static int read_cr(struct m25p *flash) +{ + u8 code = OPCODE_RDCR; + int ret; + u8 val; + + ret = spi_write_then_read(flash->spi, &code, 1, &val, 1); + if (ret < 0) { + dev_err(&flash->spi->dev, "error %d reading CR\n", ret); + return ret; + } + + return val; +} + +/* * Write status register 1 byte * Returns negative if error occurred. */ @@ -225,6 +254,93 @@ static int wait_till_ready(struct m25p *flash) } /* + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct m25p *flash, u16 val) +{ + flash->command[0] = OPCODE_WRSR; + flash->command[1] = val & 0xff; + flash->command[2] = (val >> 8); + + return spi_write(flash->spi, flash->command, 3); +} + +static int macronix_quad_enable(struct m25p *flash) +{ + int ret, val; + u8 cmd[2]; + cmd[0] = OPCODE_WRSR; + + val = read_sr(flash); + cmd[1] = val | SR_QUAD_EN_MX; + write_enable(flash); + + spi_write(flash->spi, &cmd, 2); + + if (wait_till_ready(flash)) + return 1; + + ret = read_sr(flash); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(&flash->spi->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +static int spansion_quad_enable(struct m25p *flash) +{ + int ret; + int quad_en = CR_QUAD_EN_SPAN << 8; + + write_enable(flash); + + ret = write_sr_cr(flash, quad_en); + if (ret < 0) { + dev_err(&flash->spi->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + + /* read back and check it */ + ret = read_cr(flash); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(&flash->spi->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +static int set_quad_mode(struct m25p *flash, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(flash); + if (status) { + dev_err(&flash->spi->dev, + "Macronix quad-read not enabled\n"); + return -EINVAL; + } + return status; + default: + status = spansion_quad_enable(flash); + if (status) { + dev_err(&flash->spi->dev, + "Spansion quad-read not enabled\n"); + return -EINVAL; + } + return status; + } +} + +/* * Erase the whole flash memory * * Returns 0 if successful, non-zero otherwise. @@ -363,6 +479,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash) { switch (flash->flash_read) { case M25P80_FAST: + case M25P80_QUAD: return 1; case M25P80_NORMAL: return 0; @@ -727,6 +844,7 @@ struct flash_info { #define SST_WRITE 0x04 /* use SST byte programming */ #define M25P_NO_FR 0x08 /* Can't do fastread */ #define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -804,7 +922,7 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) }, /* Micron */ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, @@ -824,7 +942,7 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) }, { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) }, { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, @@ -966,6 +1084,7 @@ static int m25p_probe(struct spi_device *spi) unsigned i; struct mtd_part_parser_data ppdata; struct device_node *np = spi->dev.of_node; + int ret; /* Platform data helps sort out which chip type we have, as * well as how this board partitions it. If we don't have @@ -1093,8 +1212,21 @@ static int m25p_probe(struct spi_device *spi) if (info->flags & M25P_NO_FR) flash->flash_read = M25P80_NORMAL; + /* Quad-read mode takes precedence over fast/normal */ + if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) { + ret = set_quad_mode(flash, info->jedec_id); + if (ret) { + dev_err(&flash->spi->dev, "quad mode not supported\n"); + return ret; + } + flash->flash_read = M25P80_QUAD; + } + /* Default commands */ switch (flash->flash_read) { + case M25P80_QUAD: + flash->read_opcode = OPCODE_QUAD_READ; + break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ; break; @@ -1116,6 +1248,9 @@ static int m25p_probe(struct spi_device *spi) if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Dedicated 4-byte command set */ switch (flash->flash_read) { + case M25P80_QUAD: + flash->read_opcode = OPCODE_QUAD_READ; + break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ_4B; break; -- cgit v0.10.2 From 26330d39522ea3702992a6fa4dba302679a087d7 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 11 Nov 2013 22:55:29 +0200 Subject: mtd: m25p80: add support for m25px16 Add support for Micron m25px16 spi flash chip. Signed-off-by: Igor Grinberg Signed-off-by: Brian Norris (cherry picked from commit 574926c5bc3d787bb0b935b99d8825b3199ba76b) Change-Id: Id66a4ca5be6ff4b6ea9debbb0eeba372ceafbb69 Reviewed-on: http://git.am.freescale.net:8181/20042 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7dc2c14..7b976e8 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -998,6 +998,7 @@ static const struct spi_device_id m25p_ids[] = { { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, + { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, -- cgit v0.10.2 From 0254eeaf58dde672d4bb667859de87ea02c0d703 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 15 Jan 2014 16:48:55 +0100 Subject: mtd: m25p80: Use OPCODE_QUAD_READ_4B for 4-byte addressing commit 3487a63955c34ea508bcf4ca5131ddd953876e2d ("drivers: mtd: m25p80: add quad read support") in -next added both the 3-byte OPCODE_QUAD_READ and the 4-byte OPCODE_QUAD_READ_4B, but incorrectly uses OPCODE_QUAD_READ for both 3-byte and 4-byte addressing. Use OPCODE_QUAD_READ_4B in the 4-byte case to fix this. Signed-off-by: Geert Uytterhoeven Acked-by: Marek Vasut Signed-off-by: Brian Norris (cherry picked from commit 7587f64d546d6a05dab0a7d1ac964e7ac12072f0) Change-Id: I94b049c4e645600e8b692e835f1eb427b1292ab1 Reviewed-on: http://git.am.freescale.net:8181/20043 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7b976e8..19632e3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1250,7 +1250,7 @@ static int m25p_probe(struct spi_device *spi) /* Dedicated 4-byte command set */ switch (flash->flash_read) { case M25P80_QUAD: - flash->read_opcode = OPCODE_QUAD_READ; + flash->read_opcode = OPCODE_QUAD_READ_4B; break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ_4B; -- cgit v0.10.2 From ca745cb7626ae974806ea706ad786b832e4de7fa Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 4 Dec 2013 22:59:40 -0800 Subject: mtd: m25p80: assign default read command In the following commit (in -next): commit 8552b439aba7f32063755d23f79ca27b4d0a3115 drivers: mtd: m25p80: convert "bool" read check into an enum We converted the boolean 'fast_read' property to become an enum 'flash_read', but at the same time, we changed the conditional path so that it doesn't choose a default value in some cases (technically, we choose the correct default simply by virtue of devm_kzalloc(), which zeroes this out to be a NORMAL read operation, but still...). Fix this by setting a default for the 'else' clause. Signed-off-by: Brian Norris Cc: Sourav Poddar Acked-by: Marek Vasut (cherry picked from commit 99ed1a167578f85963a0cdf5fd7b2291eaecc400) Change-Id: Ic0d5d184ad7cf702a6027271a2a5388b37a1a0ca Reviewed-on: http://git.am.freescale.net:8181/20044 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 19632e3..d0f6475 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1204,6 +1204,8 @@ static int m25p_probe(struct spi_device *spi) /* If we were instantiated by DT, use it */ if (of_property_read_bool(np, "m25p,fast-read")) flash->flash_read = M25P80_FAST; + else + flash->flash_read = M25P80_NORMAL; } else { /* If we weren't instantiated by DT, default to fast-read */ flash->flash_read = M25P80_FAST; -- cgit v0.10.2 From 146191da8edbacfadc5cdfb2a68d606e0dec9609 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 21 Jan 2014 13:59:16 +0100 Subject: mtd: m25p80: Enable Quad SPI read transfers for s25fl512s Spansion s25fl512s supports Quad SPI transfers, hence set the M25P80_QUAD_READ flag. Signed-off-by: Geert Uytterhoeven Acked-by: Marek Vasut Signed-off-by: Brian Norris (cherry picked from commit d8d5d10d0f27d1975e71617efff941321a0dc142) Change-Id: I85800faa7ea1eff351d5e13f11e2f61ee7bb4380 Reviewed-on: http://git.am.freescale.net:8181/20045 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index d0f6475..320c6a3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -943,7 +943,7 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) }, { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, -- cgit v0.10.2 From af8b08e6f3de7bd59b9f8b52ebbc4104bc297299 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 21 Jan 2014 13:59:17 +0100 Subject: mtd: m25p80: Set rx_nbits for Quad SPI transfers When using the Quad Read opcode, SPI masters still use Single SPI transfers, as spi_transfer.rx_nbits defaults to SPI_NBITS_SINGLE. Use SPI_NBITS_QUAD to fix this. While an earlier version of commit 3487a63955c34ea508bcf4ca5131ddd953876e2d ("drivers: mtd: m25p80: add quad read support") did this correctly, it was forgotten in the version that got merged. Signed-off-by: Geert Uytterhoeven Acked-by: Marek Vasut Signed-off-by: Brian Norris (cherry picked from commit 464e906737d6eba2fe63e913e0df4306423b4f61) Change-Id: Idff0abef064a56cb91958b475359ae0f663efdc0 Reviewed-on: http://git.am.freescale.net:8181/20046 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 320c6a3..ad19139 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -489,6 +489,16 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash) } } +static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) +{ + switch (flash->flash_read) { + case M25P80_QUAD: + return 4; + default: + return 0; + } +} + /* * Read an address range from the flash chip. The address range * may be any size provided it is within the physical boundaries. @@ -519,6 +529,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; + t[1].rx_nbits = m25p80_rx_nbits(flash); t[1].len = len; spi_message_add_tail(&t[1], &m); -- cgit v0.10.2 From 9f881c5b1ea0a40599bba3d2fe6d251106fe04de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 21 Jan 2014 13:59:18 +0100 Subject: mtd: m25p80: Add dual read support Add support for Dual SPI read transfers, which is supported by some Spansion SPI FLASHes. Signed-off-by: Geert Uytterhoeven Acked-by: Marek Vasut Signed-off-by: Brian Norris (cherry picked from commit dbbafb74239e8296bc20f86366b3f38e13650900) Change-Id: Ic69dfc56d0cd556ee00ab2181d4a88709929d56f Reviewed-on: http://git.am.freescale.net:8181/20047 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index ad19139..73bf661 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -41,7 +41,8 @@ #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes */ +#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ #define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -54,7 +55,8 @@ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */ +#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ #define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ @@ -95,6 +97,7 @@ enum read_type { M25P80_NORMAL = 0, M25P80_FAST, + M25P80_DUAL, M25P80_QUAD, }; @@ -479,6 +482,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash) { switch (flash->flash_read) { case M25P80_FAST: + case M25P80_DUAL: case M25P80_QUAD: return 1; case M25P80_NORMAL: @@ -492,6 +496,8 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash) static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) { switch (flash->flash_read) { + case M25P80_DUAL: + return 2; case M25P80_QUAD: return 4; default: @@ -855,7 +861,8 @@ struct flash_info { #define SST_WRITE 0x04 /* use SST byte programming */ #define M25P_NO_FR 0x08 /* Can't do fastread */ #define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ -#define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read */ +#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */ +#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -1226,7 +1233,7 @@ static int m25p_probe(struct spi_device *spi) if (info->flags & M25P_NO_FR) flash->flash_read = M25P80_NORMAL; - /* Quad-read mode takes precedence over fast/normal */ + /* Quad/Dual-read mode takes precedence over fast/normal */ if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) { ret = set_quad_mode(flash, info->jedec_id); if (ret) { @@ -1234,6 +1241,8 @@ static int m25p_probe(struct spi_device *spi) return ret; } flash->flash_read = M25P80_QUAD; + } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) { + flash->flash_read = M25P80_DUAL; } /* Default commands */ @@ -1241,6 +1250,9 @@ static int m25p_probe(struct spi_device *spi) case M25P80_QUAD: flash->read_opcode = OPCODE_QUAD_READ; break; + case M25P80_DUAL: + flash->read_opcode = OPCODE_DUAL_READ; + break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ; break; @@ -1265,6 +1277,9 @@ static int m25p_probe(struct spi_device *spi) case M25P80_QUAD: flash->read_opcode = OPCODE_QUAD_READ_4B; break; + case M25P80_DUAL: + flash->read_opcode = OPCODE_DUAL_READ_4B; + break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ_4B; break; -- cgit v0.10.2 From 3b1ccb5befc258029a00b924163bc539faa05d98 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 21 Jan 2014 13:59:19 +0100 Subject: mtd: m25p80: Enable Dual SPI read transfers for s25fl256s1 and s25fl512s Spansion s25fl256s1 and s25fl512s support Dual SPI transfers, hence set the M25P80_DUAL_READ flag. Signed-off-by: Geert Uytterhoeven Signed-off-by: Brian Norris (cherry picked from commit f5e00838e83f6fc93f42c7a01b0c612031955b31) Change-Id: I2e154cafc4e97d1663432bd160d2ec3fc3b98636 Reviewed-on: http://git.am.freescale.net:8181/20048 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 73bf661..f0871a2 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -960,8 +960,8 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) }, { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, -- cgit v0.10.2 From 4bbbc2a90ab30fd3d078b18da93a2a28ad19e868 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Fri, 26 Sep 2014 18:12:52 +0800 Subject: mtd: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Cc: David Woodhouse Cc: Brian Norris Cc: linux-mtd@lists.infradead.org Signed-off-by: Paul Gortmaker [Brian: dropped one incorrect hunk] Signed-off-by: Brian Norris (cherry picked from commit 3ea5b037e750274659648b58fb97426566a90373) Change-Id: I9e527be3d3783b665bb659e5e2c977bd4f2b64cc Reviewed-on: http://git.am.freescale.net:8181/20049 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index f0871a2..1e147a8 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -15,7 +15,6 @@ * */ -#include #include #include #include -- cgit v0.10.2 From df1f423971610148f586b809314201d732e3cda7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 21 Jan 2014 15:56:34 +0800 Subject: mtd: m25p80: Use positive logic to check JEDEC ID For slightly better readability. Signed-off-by: Axel Lin Signed-off-by: Brian Norris (cherry picked from commit b2fda1296bb8e213a6bad3937326ae98c4c4773c) Change-Id: Ifadc84eb60ed11bf59c47691cfe58b6a5ecb2274 Reviewed-on: http://git.am.freescale.net:8181/20050 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 1e147a8..c6e6d8e 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1078,9 +1078,8 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi) for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { info = (void *)m25p_ids[tmp].driver_data; if (info->jedec_id == jedec) { - if (info->ext_id != 0 && info->ext_id != ext_jedec) - continue; - return &m25p_ids[tmp]; + if (info->ext_id == 0 || info->ext_id == ext_jedec) + return &m25p_ids[tmp]; } } dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); -- cgit v0.10.2 From 2a3886753d598616dea1438b340cfd878f8b2bbb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Feb 2014 09:51:18 +0100 Subject: mtd: m25p80: add support for the Spansion s25fl008k chip Signed-off-by: Yusuke Goda Signed-off-by: Kuninori Morimoto Signed-off-by: Geert Uytterhoeven Signed-off-by: Brian Norris (cherry picked from commit bec44c45c245b38662f1e61bf0bde95fac1e7fb5) Change-Id: Id9363959c57004a211d216c745c9584da8d2cdd0 Reviewed-on: http://git.am.freescale.net:8181/20051 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index c6e6d8e..882b720 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -971,6 +971,7 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, -- cgit v0.10.2 From 91192f47219b88a20a1def157250d91f1ed87a05 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 29 Jan 2014 13:39:43 -0800 Subject: mtd: m25p80: add Macronix mx66l1g55g 1Gbit SPI flash Signed-off-by: Brian Norris Acked-by: Marek Vasut (cherry picked from commit 6f7db7f3203a0bd48170807adeb53dd401d29110) Change-Id: I84a2d2385c1196c943d9d6558cedded87885e4f8 Reviewed-on: http://git.am.freescale.net:8181/20052 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 882b720..524dab3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -940,6 +940,7 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) }, /* Micron */ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, -- cgit v0.10.2 From b553ebd79d3fa2ec07904a7a35c414a00777ddc0 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 20 Mar 2014 05:00:12 -0700 Subject: mtd: m25p80: use the SPI nor framework Use the new SPI nor framework, and rewrite the m25p80: (0) remove all the NOR comands. (1) change the m25p->command to an array. (2) implement the necessary hooks, such as m25p80_read/m25p80_write. Tested with the m25p32. Signed-off-by: Huang Shijie Acked-by: Marek Vasut [Brian: rebased] Signed-off-by: Brian Norris (cherry picked from commit 03e296f613affcc2671c1e86d8c25ecad867204e) Change-Id: I8ac0159ab3d572cd12bdc1e5b2b6e6c5057b8359 Reviewed-on: http://git.am.freescale.net:8181/20053 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 0128138..004b17b 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP config MTD_M25P80 tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" - depends on SPI_MASTER + depends on SPI_MASTER && MTD_SPI_NOR_BASE help This enables access to most modern SPI flash chips, used for program and data storage. Series supported include Atmel AT26DF, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 524dab3..4af6400 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -19,485 +19,98 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include -#include #include #include +#include -/* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ -#define OPCODE_RDCR 0x35 /* Read configuration register */ - -/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ - -/* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ - -/* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ - -/* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ - -/* Status Register bits. */ -#define SR_WIP 1 /* Write in progress */ -#define SR_WEL 2 /* Write enable latch */ -/* meaning of other SR_* bits may differ between vendors */ -#define SR_BP0 4 /* Block protect 0 */ -#define SR_BP1 8 /* Block protect 1 */ -#define SR_BP2 0x10 /* Block protect 2 */ -#define SR_SRWD 0x80 /* SR write protect */ - -#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ - -/* Configuration Register bits. */ -#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ - -/* Define max times to check status register before we give up. */ -#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 6 - -#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) - -/****************************************************************************/ - -enum read_type { - M25P80_NORMAL = 0, - M25P80_FAST, - M25P80_DUAL, - M25P80_QUAD, -}; - struct m25p { struct spi_device *spi; - struct mutex lock; + struct spi_nor spi_nor; struct mtd_info mtd; - u16 page_size; - u16 addr_width; - u8 erase_opcode; - u8 read_opcode; - u8 program_opcode; - u8 *command; - enum read_type flash_read; + u8 command[MAX_CMD_SIZE]; }; -static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) -{ - return container_of(mtd, struct m25p, mtd); -} - -/****************************************************************************/ - -/* - * Internal helper functions - */ - -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. - */ -static int read_sr(struct m25p *flash) -{ - ssize_t retval; - u8 code = OPCODE_RDSR; - u8 val; - - retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); - - if (retval < 0) { - dev_err(&flash->spi->dev, "error %d reading SR\n", - (int) retval); - return retval; - } - - return val; -} - -/* - * Read configuration register, returning its value in the - * location. Return the configuration register value. - * Returns negative if error occured. - */ -static int read_cr(struct m25p *flash) -{ - u8 code = OPCODE_RDCR; - int ret; - u8 val; - - ret = spi_write_then_read(flash->spi, &code, 1, &val, 1); - if (ret < 0) { - dev_err(&flash->spi->dev, "error %d reading CR\n", ret); - return ret; - } - - return val; -} - -/* - * Write status register 1 byte - * Returns negative if error occurred. - */ -static int write_sr(struct m25p *flash, u8 val) -{ - flash->command[0] = OPCODE_WRSR; - flash->command[1] = val; - - return spi_write(flash->spi, flash->command, 2); -} - -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static inline int write_enable(struct m25p *flash) -{ - u8 code = OPCODE_WREN; - - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Send write disble instruction to the chip. - */ -static inline int write_disable(struct m25p *flash) -{ - u8 code = OPCODE_WRDI; - - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Enable/disable 4-byte addressing mode. - */ -static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) -{ - int status; - bool need_wren = false; - - switch (JEDEC_MFR(jedec_id)) { - case CFI_MFR_ST: /* Micron, actually */ - /* Some Micron need WREN command; all will accept it */ - need_wren = true; - case CFI_MFR_MACRONIX: - case 0xEF /* winbond */: - if (need_wren) - write_enable(flash); - - flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; - status = spi_write(flash->spi, flash->command, 1); - - if (need_wren) - write_disable(flash); - - return status; - default: - /* Spansion style */ - flash->command[0] = OPCODE_BRWR; - flash->command[1] = enable << 7; - return spi_write(flash->spi, flash->command, 2); - } -} - -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. - */ -static int wait_till_ready(struct m25p *flash) -{ - unsigned long deadline; - int sr; - - deadline = jiffies + MAX_READY_WAIT_JIFFIES; - - do { - if ((sr = read_sr(flash)) < 0) - break; - else if (!(sr & SR_WIP)) - return 0; - - cond_resched(); - - } while (!time_after_eq(jiffies, deadline)); - - return 1; -} - -/* - * Write status Register and configuration register with 2 bytes - * The first byte will be written to the status register, while the - * second byte will be written to the configuration register. - * Return negative if error occured. - */ -static int write_sr_cr(struct m25p *flash, u16 val) -{ - flash->command[0] = OPCODE_WRSR; - flash->command[1] = val & 0xff; - flash->command[2] = (val >> 8); - - return spi_write(flash->spi, flash->command, 3); -} - -static int macronix_quad_enable(struct m25p *flash) -{ - int ret, val; - u8 cmd[2]; - cmd[0] = OPCODE_WRSR; - - val = read_sr(flash); - cmd[1] = val | SR_QUAD_EN_MX; - write_enable(flash); - - spi_write(flash->spi, &cmd, 2); - - if (wait_till_ready(flash)) - return 1; - - ret = read_sr(flash); - if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { - dev_err(&flash->spi->dev, "Macronix Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -static int spansion_quad_enable(struct m25p *flash) +static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) { + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; int ret; - int quad_en = CR_QUAD_EN_SPAN << 8; - - write_enable(flash); - ret = write_sr_cr(flash, quad_en); - if (ret < 0) { - dev_err(&flash->spi->dev, - "error while writing configuration register\n"); - return -EINVAL; - } - - /* read back and check it */ - ret = read_cr(flash); - if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { - dev_err(&flash->spi->dev, "Spansion Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -static int set_quad_mode(struct m25p *flash, u32 jedec_id) -{ - int status; - - switch (JEDEC_MFR(jedec_id)) { - case CFI_MFR_MACRONIX: - status = macronix_quad_enable(flash); - if (status) { - dev_err(&flash->spi->dev, - "Macronix quad-read not enabled\n"); - return -EINVAL; - } - return status; - default: - status = spansion_quad_enable(flash); - if (status) { - dev_err(&flash->spi->dev, - "Spansion quad-read not enabled\n"); - return -EINVAL; - } - return status; - } -} - -/* - * Erase the whole flash memory - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_chip(struct m25p *flash) -{ - pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__, - (long long)(flash->mtd.size >> 10)); + ret = spi_write_then_read(spi, &code, 1, val, len); + if (ret < 0) + dev_err(&spi->dev, "error %d reading %x\n", ret, code); - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) - return 1; - - /* Send write enable, then erase commands. */ - write_enable(flash); - - /* Set up command buffer. */ - flash->command[0] = OPCODE_CHIP_ERASE; - - spi_write(flash->spi, flash->command, 1); - - return 0; + return ret; } -static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) { /* opcode is in cmd[0] */ - cmd[1] = addr >> (flash->addr_width * 8 - 8); - cmd[2] = addr >> (flash->addr_width * 8 - 16); - cmd[3] = addr >> (flash->addr_width * 8 - 24); - cmd[4] = addr >> (flash->addr_width * 8 - 32); + cmd[1] = addr >> (nor->addr_width * 8 - 8); + cmd[2] = addr >> (nor->addr_width * 8 - 16); + cmd[3] = addr >> (nor->addr_width * 8 - 24); + cmd[4] = addr >> (nor->addr_width * 8 - 32); } -static int m25p_cmdsz(struct m25p *flash) +static int m25p_cmdsz(struct spi_nor *nor) { - return 1 + flash->addr_width; + return 1 + nor->addr_width; } -/* - * Erase one sector of flash memory at offset ``offset'' which is any - * address within the sector which should be erased. - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_sector(struct m25p *flash, u32 offset) +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int wr_en) { - pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev), - __func__, flash->mtd.erasesize / 1024, offset); - - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) - return 1; + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; - /* Send write enable, then erase commands. */ - write_enable(flash); - - /* Set up command buffer. */ - flash->command[0] = flash->erase_opcode; - m25p_addr2cmd(flash, offset, flash->command); - - spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); + flash->command[0] = opcode; + if (buf) + memcpy(&flash->command[1], buf, len); - return 0; + return spi_write(spi, flash->command, len + 1); } -/****************************************************************************/ - -/* - * MTD implementation - */ - -/* - * Erase an address range on the flash chip. The address range may extend - * one or more erase sectors. Return an error is there is a problem erasing. - */ -static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) +static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - struct m25p *flash = mtd_to_m25p(mtd); - u32 addr,len; - uint32_t rem; - - pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev), - __func__, (long long)instr->addr, - (long long)instr->len); - - div_u64_rem(instr->len, mtd->erasesize, &rem); - if (rem) - return -EINVAL; - - addr = instr->addr; - len = instr->len; - - mutex_lock(&flash->lock); - - /* whole-chip erase? */ - if (len == flash->mtd.size) { - if (erase_chip(flash)) { - instr->state = MTD_ERASE_FAILED; - mutex_unlock(&flash->lock); - return -EIO; - } + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; + struct spi_transfer t[2] = {}; + struct spi_message m; + int cmd_sz = m25p_cmdsz(nor); - /* REVISIT in some cases we could speed up erasing large regions - * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up - * to use "small sector erase", but that's not always optimal. - */ + spi_message_init(&m); - /* "sector"-at-a-time erase */ - } else { - while (len) { - if (erase_sector(flash, addr)) { - instr->state = MTD_ERASE_FAILED; - mutex_unlock(&flash->lock); - return -EIO; - } + if (nor->program_opcode == OPCODE_AAI_WP && nor->sst_write_second) + cmd_sz = 1; - addr += mtd->erasesize; - len -= mtd->erasesize; - } - } + flash->command[0] = nor->program_opcode; + m25p_addr2cmd(nor, to, flash->command); - mutex_unlock(&flash->lock); + t[0].tx_buf = flash->command; + t[0].len = cmd_sz; + spi_message_add_tail(&t[0], &m); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + t[1].tx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); - return 0; -} + spi_sync(spi, &m); -/* - * Dummy Cycle calculation for different type of read. - * It can be used to support more commands with - * different dummy cycle requirements. - */ -static inline int m25p80_dummy_cycles_read(struct m25p *flash) -{ - switch (flash->flash_read) { - case M25P80_FAST: - case M25P80_DUAL: - case M25P80_QUAD: - return 1; - case M25P80_NORMAL: - return 0; - default: - dev_err(&flash->spi->dev, "No valid read type supported\n"); - return -1; - } + *retlen += m.actual_length - cmd_sz; } -static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) +static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) { - switch (flash->flash_read) { - case M25P80_DUAL: + switch (nor->flash_read) { + case SPI_NOR_DUAL: return 2; - case M25P80_QUAD: + case SPI_NOR_QUAD: return 4; default: return 0; @@ -505,590 +118,72 @@ static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) } /* - * Read an address range from the flash chip. The address range + * Read an address range from the nor chip. The address range * may be any size provided it is within the physical boundaries. */ -static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) { - struct m25p *flash = mtd_to_m25p(mtd); + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; struct spi_transfer t[2]; struct spi_message m; - uint8_t opcode; - int dummy; + int dummy = nor->read_dummy; + int ret; - pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)from, len); + /* Wait till previous write/erase is done. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; spi_message_init(&m); memset(t, 0, (sizeof t)); - dummy = m25p80_dummy_cycles_read(flash); - if (dummy < 0) { - dev_err(&flash->spi->dev, "No valid read command supported\n"); - return -EINVAL; - } + flash->command[0] = nor->read_opcode; + m25p_addr2cmd(nor, from, flash->command); t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + dummy; + t[0].len = m25p_cmdsz(nor) + dummy; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; - t[1].rx_nbits = m25p80_rx_nbits(flash); + t[1].rx_nbits = m25p80_rx_nbits(nor); t[1].len = len; spi_message_add_tail(&t[1], &m); - mutex_lock(&flash->lock); - - /* Wait till previous write/erase is done. */ - if (wait_till_ready(flash)) { - /* REVISIT status return?? */ - mutex_unlock(&flash->lock); - return 1; - } - - /* Set up the write data buffer. */ - opcode = flash->read_opcode; - flash->command[0] = opcode; - m25p_addr2cmd(flash, from, flash->command); - - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash) - dummy; - - mutex_unlock(&flash->lock); + spi_sync(spi, &m); + *retlen = m.actual_length - m25p_cmdsz(nor) - dummy; return 0; } -/* - * Write an address range to the flash chip. Data must be written in - * FLASH_PAGESIZE chunks. The address range may be any size provided - * it is within the physical boundaries. - */ -static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int m25p80_erase(struct spi_nor *nor, loff_t offset) { - struct m25p *flash = mtd_to_m25p(mtd); - u32 page_offset, page_size; - struct spi_transfer t[2]; - struct spi_message m; - - pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)to, len); - - spi_message_init(&m); - memset(t, 0, (sizeof t)); - - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - spi_message_add_tail(&t[1], &m); - - mutex_lock(&flash->lock); - - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) { - mutex_unlock(&flash->lock); - return 1; - } - - write_enable(flash); - - /* Set up the opcode in the write buffer. */ - flash->command[0] = flash->program_opcode; - m25p_addr2cmd(flash, to, flash->command); - - page_offset = to & (flash->page_size - 1); - - /* do all the bytes fit onto one page? */ - if (page_offset + len <= flash->page_size) { - t[1].len = len; - - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash); - } else { - u32 i; - - /* the size of data remaining on the first page */ - page_size = flash->page_size - page_offset; - - t[1].len = page_size; - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash); - - /* write everything in flash->page_size chunks */ - for (i = page_size; i < len; i += page_size) { - page_size = len - i; - if (page_size > flash->page_size) - page_size = flash->page_size; - - /* write the next page to flash */ - m25p_addr2cmd(flash, to + i, flash->command); - - t[1].tx_buf = buf + i; - t[1].len = page_size; - - wait_till_ready(flash); - - write_enable(flash); - - spi_sync(flash->spi, &m); - - *retlen += m.actual_length - m25p_cmdsz(flash); - } - } - - mutex_unlock(&flash->lock); - - return 0; -} - -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct m25p *flash = mtd_to_m25p(mtd); - struct spi_transfer t[2]; - struct spi_message m; - size_t actual; - int cmd_sz, ret; - - pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)to, len); - - spi_message_init(&m); - memset(t, 0, (sizeof t)); - - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - spi_message_add_tail(&t[1], &m); + struct m25p *flash = nor->priv; + int ret; - mutex_lock(&flash->lock); + dev_dbg(nor->dev, "%dKiB at 0x%08x\n", + flash->mtd.erasesize / 1024, (u32)offset); /* Wait until finished previous write command. */ - ret = wait_till_ready(flash); + ret = nor->wait_till_ready(nor); if (ret) - goto time_out; - - write_enable(flash); - - actual = to % 2; - /* Start write from odd address. */ - if (actual) { - flash->command[0] = OPCODE_BP; - m25p_addr2cmd(flash, to, flash->command); - - /* write one byte. */ - t[1].len = 1; - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - m25p_cmdsz(flash); - } - to += actual; - - flash->command[0] = OPCODE_AAI_WP; - m25p_addr2cmd(flash, to, flash->command); - - /* Write out most of the data here. */ - cmd_sz = m25p_cmdsz(flash); - for (; actual < len - 1; actual += 2) { - t[0].len = cmd_sz; - /* write two bytes. */ - t[1].len = 2; - t[1].tx_buf = buf + actual; + return ret; - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - cmd_sz; - cmd_sz = 1; - to += 2; - } - write_disable(flash); - ret = wait_till_ready(flash); + /* Send write enable, then erase commands. */ + ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); if (ret) - goto time_out; - - /* Write out trailing byte if it exists. */ - if (actual != len) { - write_enable(flash); - flash->command[0] = OPCODE_BP; - m25p_addr2cmd(flash, to, flash->command); - t[0].len = m25p_cmdsz(flash); - t[1].len = 1; - t[1].tx_buf = buf + actual; - - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - m25p_cmdsz(flash); - write_disable(flash); - } - -time_out: - mutex_unlock(&flash->lock); - return ret; -} - -static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct m25p *flash = mtd_to_m25p(mtd); - uint32_t offset = ofs; - uint8_t status_old, status_new; - int res = 0; - - mutex_lock(&flash->lock); - /* Wait until finished previous command */ - if (wait_till_ready(flash)) { - res = 1; - goto err; - } - - status_old = read_sr(flash); - - if (offset < flash->mtd.size-(flash->mtd.size/2)) - status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/4)) - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - else if (offset < flash->mtd.size-(flash->mtd.size/8)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/16)) - status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; - else if (offset < flash->mtd.size-(flash->mtd.size/32)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/64)) - status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; - else - status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - - /* Only modify protection if it will not unlock other areas */ - if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) > - (status_old&(SR_BP2|SR_BP1|SR_BP0))) { - write_enable(flash); - if (write_sr(flash, status_new) < 0) { - res = 1; - goto err; - } - } - -err: mutex_unlock(&flash->lock); - return res; -} - -static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct m25p *flash = mtd_to_m25p(mtd); - uint32_t offset = ofs; - uint8_t status_old, status_new; - int res = 0; - - mutex_lock(&flash->lock); - /* Wait until finished previous command */ - if (wait_till_ready(flash)) { - res = 1; - goto err; - } - - status_old = read_sr(flash); - - if (offset+len > flash->mtd.size-(flash->mtd.size/64)) - status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0); - else if (offset+len > flash->mtd.size-(flash->mtd.size/32)) - status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - else if (offset+len > flash->mtd.size-(flash->mtd.size/16)) - status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; - else if (offset+len > flash->mtd.size-(flash->mtd.size/8)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset+len > flash->mtd.size-(flash->mtd.size/4)) - status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; - else if (offset+len > flash->mtd.size-(flash->mtd.size/2)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - - /* Only modify protection if it will not lock other areas */ - if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) < - (status_old&(SR_BP2|SR_BP1|SR_BP0))) { - write_enable(flash); - if (write_sr(flash, status_new) < 0) { - res = 1; - goto err; - } - } - -err: mutex_unlock(&flash->lock); - return res; -} - -/****************************************************************************/ - -/* - * SPI device driver setup and teardown - */ - -struct flash_info { - /* JEDEC id zero means "no ID" (most older chips); otherwise it has - * a high byte of zero plus three data bytes: the manufacturer id, - * then a two byte device id. - */ - u32 jedec_id; - u16 ext_id; - - /* The size listed here is what works with OPCODE_SE, which isn't - * necessarily called a "sector" by the vendor. - */ - unsigned sector_size; - u16 n_sectors; - - u16 page_size; - u16 addr_width; - - u16 flags; -#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ -#define M25P_NO_ERASE 0x02 /* No erase command needed */ -#define SST_WRITE 0x04 /* use SST byte programming */ -#define M25P_NO_FR 0x08 /* Can't do fastread */ -#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ -#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */ -#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */ -}; - -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ - .jedec_id = (_jedec_id), \ - .ext_id = (_ext_id), \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), \ - }) - -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = (_page_size), \ - .addr_width = (_addr_width), \ - .flags = (_flags), \ - }) - -/* NOTE: double check command sets and memory organization when you add - * more flash chips. This current list focusses on newer chips, which - * have been converging on command sets which including JEDEC ID. - */ -static const struct spi_device_id m25p_ids[] = { - /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, - - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - - { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - - { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, - - /* EON -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, - { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, - { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, - { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, - - /* ESMT */ - { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, - - /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, - - /* GigaDevice */ - { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, - { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, - - /* Intel/Numonyx -- xxxs33b */ - { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, - { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, - { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, - - /* Macronix */ - { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, - { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, - { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, - { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, - { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, - { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, - { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, - { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, - { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, - { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, - { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) }, - { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) }, - - /* Micron */ - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, - { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, - - /* PMC */ - { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, - { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, - { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, - - /* Spansion -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, - { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, - { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) }, - { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, - { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, - { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, - { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, - { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, - { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, - { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, - { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, - { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, - { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, - { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, - { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - - /* SST -- large erase sizes are "overlays", "sectors" are 4K */ - { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, - { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, - { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, - { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, - { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, - { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, - { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, - { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - - /* ST Microelectronics -- newer production may have feature updates */ - { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, - { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, - { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, - { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, - { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, - { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, - { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, - { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, - { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, - { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, - - { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, - { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, - { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, - { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, - { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, - { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, - { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, - { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, - { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, - - { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, - { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, - { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, - - { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, - { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, - { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, - - { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, - { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, - - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ - { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, - { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, - { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, - { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, - - /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { }, -}; -MODULE_DEVICE_TABLE(spi, m25p_ids); - -static const struct spi_device_id *jedec_probe(struct spi_device *spi) -{ - int tmp; - u8 code = OPCODE_RDID; - u8 id[5]; - u32 jedec; - u16 ext_jedec; - struct flash_info *info; + return ret; - /* JEDEC also defines an optional "extended device information" - * string for after vendor-specific data, after the three bytes - * we use here. Supporting some chips might require using it. - */ - tmp = spi_write_then_read(spi, &code, 1, id, 5); - if (tmp < 0) { - pr_debug("%s: error %d reading JEDEC ID\n", - dev_name(&spi->dev), tmp); - return ERR_PTR(tmp); - } - jedec = id[0]; - jedec = jedec << 8; - jedec |= id[1]; - jedec = jedec << 8; - jedec |= id[2]; + /* Set up command buffer. */ + flash->command[0] = nor->erase_opcode; + m25p_addr2cmd(nor, offset, flash->command); - ext_jedec = id[3] << 8 | id[4]; + spi_write(flash->spi, flash->command, m25p_cmdsz(nor)); - for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { - info = (void *)m25p_ids[tmp].driver_data; - if (info->jedec_id == jedec) { - if (info->ext_id == 0 || info->ext_id == ext_jedec) - return &m25p_ids[tmp]; - } - } - dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); - return ERR_PTR(-ENODEV); + return 0; } - /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -1096,231 +191,43 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi) */ static int m25p_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); - struct flash_platform_data *data; - struct m25p *flash; - struct flash_info *info; - unsigned i; struct mtd_part_parser_data ppdata; - struct device_node *np = spi->dev.of_node; + struct flash_platform_data *data; + struct m25p *flash; + struct spi_nor *nor; + enum read_mode mode = SPI_NOR_NORMAL; int ret; - /* Platform data helps sort out which chip type we have, as - * well as how this board partitions it. If we don't have - * a chip ID, try the JEDEC id commands; they'll work for most - * newer chips, even if we don't recognize the particular chip. - */ - data = dev_get_platdata(&spi->dev); - if (data && data->type) { - const struct spi_device_id *plat_id; - - for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { - plat_id = &m25p_ids[i]; - if (strcmp(data->type, plat_id->name)) - continue; - break; - } - - if (i < ARRAY_SIZE(m25p_ids) - 1) - id = plat_id; - else - dev_warn(&spi->dev, "unrecognized id %s\n", data->type); - } - - info = (void *)id->driver_data; - - if (info->jedec_id) { - const struct spi_device_id *jid; - - jid = jedec_probe(spi); - if (IS_ERR(jid)) { - return PTR_ERR(jid); - } else if (jid != id) { - /* - * JEDEC knows better, so overwrite platform ID. We - * can't trust partitions any longer, but we'll let - * mtd apply them anyway, since some partitions may be - * marked read-only, and we don't want to lose that - * information, even if it's not 100% accurate. - */ - dev_warn(&spi->dev, "found %s, expected %s\n", - jid->name, id->name); - id = jid; - info = (void *)jid->driver_data; - } - } - flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL); - if (!flash->command) - return -ENOMEM; - - flash->spi = spi; - mutex_init(&flash->lock); - spi_set_drvdata(spi, flash); - - /* - * Atmel, SST and Intel/Numonyx serial flash tend to power - * up with the software protection bits set - */ + nor = &flash->spi_nor; - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || - JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || - JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { - write_enable(flash); - write_sr(flash, 0); - } - - if (data && data->name) - flash->mtd.name = data->name; - else - flash->mtd.name = dev_name(&spi->dev); - - flash->mtd.type = MTD_NORFLASH; - flash->mtd.writesize = 1; - flash->mtd.flags = MTD_CAP_NORFLASH; - flash->mtd.size = info->sector_size * info->n_sectors; - flash->mtd._erase = m25p80_erase; - flash->mtd._read = m25p80_read; - - /* flash protection support for STmicro chips */ - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { - flash->mtd._lock = m25p80_lock; - flash->mtd._unlock = m25p80_unlock; - } + /* install the hooks */ + nor->read = m25p80_read; + nor->write = m25p80_write; + nor->erase = m25p80_erase; + nor->write_reg = m25p80_write_reg; + nor->read_reg = m25p80_read_reg; - /* sst flash chips use AAI word program */ - if (info->flags & SST_WRITE) - flash->mtd._write = sst_write; - else - flash->mtd._write = m25p80_write; + nor->dev = &spi->dev; + nor->mtd = &flash->mtd; + nor->priv = flash; - /* prefer "small sector" erase if possible */ - if (info->flags & SECT_4K) { - flash->erase_opcode = OPCODE_BE_4K; - flash->mtd.erasesize = 4096; - } else if (info->flags & SECT_4K_PMC) { - flash->erase_opcode = OPCODE_BE_4K_PMC; - flash->mtd.erasesize = 4096; - } else { - flash->erase_opcode = OPCODE_SE; - flash->mtd.erasesize = info->sector_size; - } + spi_set_drvdata(spi, flash); + flash->mtd.priv = nor; + flash->spi = spi; - if (info->flags & M25P_NO_ERASE) - flash->mtd.flags |= MTD_NO_ERASE; + if (spi->mode & SPI_RX_QUAD) + mode = SPI_NOR_QUAD; + ret = spi_nor_scan(nor, spi_get_device_id(spi), mode); + if (ret) + return ret; + data = dev_get_platdata(&spi->dev); ppdata.of_node = spi->dev.of_node; - flash->mtd.dev.parent = &spi->dev; - flash->page_size = info->page_size; - flash->mtd.writebufsize = flash->page_size; - - if (np) { - /* If we were instantiated by DT, use it */ - if (of_property_read_bool(np, "m25p,fast-read")) - flash->flash_read = M25P80_FAST; - else - flash->flash_read = M25P80_NORMAL; - } else { - /* If we weren't instantiated by DT, default to fast-read */ - flash->flash_read = M25P80_FAST; - } - - /* Some devices cannot do fast-read, no matter what DT tells us */ - if (info->flags & M25P_NO_FR) - flash->flash_read = M25P80_NORMAL; - - /* Quad/Dual-read mode takes precedence over fast/normal */ - if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) { - ret = set_quad_mode(flash, info->jedec_id); - if (ret) { - dev_err(&flash->spi->dev, "quad mode not supported\n"); - return ret; - } - flash->flash_read = M25P80_QUAD; - } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) { - flash->flash_read = M25P80_DUAL; - } - /* Default commands */ - switch (flash->flash_read) { - case M25P80_QUAD: - flash->read_opcode = OPCODE_QUAD_READ; - break; - case M25P80_DUAL: - flash->read_opcode = OPCODE_DUAL_READ; - break; - case M25P80_FAST: - flash->read_opcode = OPCODE_FAST_READ; - break; - case M25P80_NORMAL: - flash->read_opcode = OPCODE_NORM_READ; - break; - default: - dev_err(&flash->spi->dev, "No Read opcode defined\n"); - return -EINVAL; - } - - flash->program_opcode = OPCODE_PP; - - if (info->addr_width) - flash->addr_width = info->addr_width; - else if (flash->mtd.size > 0x1000000) { - /* enable 4-byte addressing if the device exceeds 16MiB */ - flash->addr_width = 4; - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { - /* Dedicated 4-byte command set */ - switch (flash->flash_read) { - case M25P80_QUAD: - flash->read_opcode = OPCODE_QUAD_READ_4B; - break; - case M25P80_DUAL: - flash->read_opcode = OPCODE_DUAL_READ_4B; - break; - case M25P80_FAST: - flash->read_opcode = OPCODE_FAST_READ_4B; - break; - case M25P80_NORMAL: - flash->read_opcode = OPCODE_NORM_READ_4B; - break; - } - flash->program_opcode = OPCODE_PP_4B; - /* No small sector erase for 4-byte command set */ - flash->erase_opcode = OPCODE_SE_4B; - flash->mtd.erasesize = info->sector_size; - } else - set_4byte(flash, info->jedec_id, 1); - } else { - flash->addr_width = 3; - } - - dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, - (long long)flash->mtd.size >> 10); - - pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) " - ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", - flash->mtd.name, - (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), - flash->mtd.erasesize, flash->mtd.erasesize / 1024, - flash->mtd.numeraseregions); - - if (flash->mtd.numeraseregions) - for (i = 0; i < flash->mtd.numeraseregions; i++) - pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, " - ".erasesize = 0x%.8x (%uKiB), " - ".numblocks = %d }\n", - i, (long long)flash->mtd.eraseregions[i].offset, - flash->mtd.eraseregions[i].erasesize, - flash->mtd.eraseregions[i].erasesize / 1024, - flash->mtd.eraseregions[i].numblocks); - - - /* partitions should match sector boundaries; and it may be good to - * use readonly partitions for writeprotected sectors (BP2..BP0). - */ return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, data ? data->parts : NULL, data ? data->nr_parts : 0); @@ -1341,7 +248,7 @@ static struct spi_driver m25p80_driver = { .name = "m25p80", .owner = THIS_MODULE, }, - .id_table = m25p_ids, + .id_table = spi_nor_ids, .probe = m25p_probe, .remove = m25p_remove, -- cgit v0.10.2 From cb7f1fab13a54f1467ca60d222ca00f3ed081845 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:15:31 -0700 Subject: mtd: spi-nor: re-name OPCODE_* to SPINOR_OP_* Qualify these with a better namespace, and prepare them for use in more drivers. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie (cherry picked from commit b02e7f3ef0beb72da8fc64542f0ac977996ec56b) Change-Id: I50fac2cb23653825b2f8e3ac65dd0ecb35eaf78b Reviewed-on: http://git.am.freescale.net:8181/20054 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4af6400..1557d8f 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -86,7 +86,7 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, spi_message_init(&m); - if (nor->program_opcode == OPCODE_AAI_WP && nor->sst_write_second) + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) cmd_sz = 1; flash->command[0] = nor->program_opcode; @@ -171,7 +171,7 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 6dc08ed..2977f02 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -294,12 +294,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) lut_base = SEQID_QUAD_READ * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_QUAD_READ; + cmd = SPINOR_OP_QUAD_READ; addrlen = ADDR24BIT; dummy = 8; } else { /* use the 4-byte address */ - cmd = OPCODE_QUAD_READ; + cmd = SPINOR_OP_QUAD_READ; addrlen = ADDR32BIT; dummy = 8; } @@ -311,17 +311,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Write enable */ lut_base = SEQID_WREN * 4; - writel(LUT0(CMD, PAD1, OPCODE_WREN), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base)); /* Page Program */ lut_base = SEQID_PP * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_PP; + cmd = SPINOR_OP_PP; addrlen = ADDR24BIT; } else { /* use the 4-byte address */ - cmd = OPCODE_PP; + cmd = SPINOR_OP_PP; addrlen = ADDR32BIT; } @@ -331,18 +331,18 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Read Status */ lut_base = SEQID_RDSR * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDSR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Erase a sector */ lut_base = SEQID_SE * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_SE; + cmd = SPINOR_OP_SE; addrlen = ADDR24BIT; } else { /* use the 4-byte address */ - cmd = OPCODE_SE; + cmd = SPINOR_OP_SE; addrlen = ADDR32BIT; } @@ -351,35 +351,35 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Erase the whole chip */ lut_base = SEQID_CHIP_ERASE * 4; - writel(LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), + writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), base + QUADSPI_LUT(lut_base)); /* READ ID */ lut_base = SEQID_RDID * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDID) | LUT1(READ, PAD1, 0x8), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), base + QUADSPI_LUT(lut_base)); /* Write Register */ lut_base = SEQID_WRSR * 4; - writel(LUT0(CMD, PAD1, OPCODE_WRSR) | LUT1(WRITE, PAD1, 0x2), + writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), base + QUADSPI_LUT(lut_base)); /* Read Configuration Register */ lut_base = SEQID_RDCR * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDCR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Write disable */ lut_base = SEQID_WRDI * 4; - writel(LUT0(CMD, PAD1, OPCODE_WRDI), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Micron) */ lut_base = SEQID_EN4B * 4; - writel(LUT0(CMD, PAD1, OPCODE_EN4B), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Spansion) */ lut_base = SEQID_BRWR * 4; - writel(LUT0(CMD, PAD1, OPCODE_BRWR), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + QUADSPI_LUT(lut_base)); fsl_qspi_lock_lut(q); } @@ -388,29 +388,29 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { - case OPCODE_QUAD_READ: + case SPINOR_OP_QUAD_READ: return SEQID_QUAD_READ; - case OPCODE_WREN: + case SPINOR_OP_WREN: return SEQID_WREN; - case OPCODE_WRDI: + case SPINOR_OP_WRDI: return SEQID_WRDI; - case OPCODE_RDSR: + case SPINOR_OP_RDSR: return SEQID_RDSR; - case OPCODE_SE: + case SPINOR_OP_SE: return SEQID_SE; - case OPCODE_CHIP_ERASE: + case SPINOR_OP_CHIP_ERASE: return SEQID_CHIP_ERASE; - case OPCODE_PP: + case SPINOR_OP_PP: return SEQID_PP; - case OPCODE_RDID: + case SPINOR_OP_RDID: return SEQID_RDID; - case OPCODE_WRSR: + case SPINOR_OP_WRSR: return SEQID_WRSR; - case OPCODE_RDCR: + case SPINOR_OP_RDCR: return SEQID_RDCR; - case OPCODE_EN4B: + case SPINOR_OP_EN4B: return SEQID_EN4B; - case OPCODE_BRWR: + case SPINOR_OP_BRWR: return SEQID_BRWR; default: dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); @@ -688,7 +688,7 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, if (ret) return ret; - if (opcode == OPCODE_CHIP_ERASE) + if (opcode == SPINOR_OP_CHIP_ERASE) fsl_qspi_invalid(q); } else if (len > 0) { @@ -750,7 +750,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 6c64ab9..1716f3c 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -38,7 +38,7 @@ static int read_sr(struct spi_nor *nor) int ret; u8 val; - ret = nor->read_reg(nor, OPCODE_RDSR, &val, 1); + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); if (ret < 0) { pr_err("error %d reading SR\n", (int) ret); return ret; @@ -57,7 +57,7 @@ static int read_cr(struct spi_nor *nor) int ret; u8 val; - ret = nor->read_reg(nor, OPCODE_RDCR, &val, 1); + ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1); if (ret < 0) { dev_err(nor->dev, "error %d reading CR\n", ret); return ret; @@ -91,7 +91,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) static inline int write_sr(struct spi_nor *nor, u8 val) { nor->cmd_buf[0] = val; - return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); } /* @@ -100,7 +100,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) */ static inline int write_enable(struct spi_nor *nor) { - return nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); } /* @@ -108,7 +108,7 @@ static inline int write_enable(struct spi_nor *nor) */ static inline int write_disable(struct spi_nor *nor) { - return nor->write_reg(nor, OPCODE_WRDI, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); } static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) @@ -132,7 +132,7 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) if (need_wren) write_enable(nor); - cmd = enable ? OPCODE_EN4B : OPCODE_EX4B; + cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; status = nor->write_reg(nor, cmd, NULL, 0, 0); if (need_wren) write_disable(nor); @@ -141,7 +141,7 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) default: /* Spansion style */ nor->cmd_buf[0] = enable << 7; - return nor->write_reg(nor, OPCODE_BRWR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); } } @@ -193,7 +193,7 @@ static int erase_chip(struct spi_nor *nor) /* Send write enable, then erase commands. */ write_enable(nor); - return nor->write_reg(nor, OPCODE_CHIP_ERASE, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); } static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -253,7 +253,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) } /* REVISIT in some cases we could speed up erasing large regions - * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up * to use "small sector erase", but that's not always optimal. */ @@ -385,7 +385,7 @@ struct flash_info { u32 jedec_id; u16 ext_id; - /* The size listed here is what works with OPCODE_SE, which isn't + /* The size listed here is what works with SPINOR_OP_SE, which isn't * necessarily called a "sector" by the vendor. */ unsigned sector_size; @@ -395,11 +395,11 @@ struct flash_info { u16 addr_width; u16 flags; -#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */ #define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ #define SST_WRITE 0x04 /* use SST byte programming */ #define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ -#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ }; @@ -598,7 +598,7 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) u16 ext_jedec; struct flash_info *info; - tmp = nor->read_reg(nor, OPCODE_RDID, id, 5); + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); if (tmp < 0) { dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); return ERR_PTR(tmp); @@ -670,7 +670,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, actual = to % 2; /* Start write from odd address. */ if (actual) { - nor->program_opcode = OPCODE_BP; + nor->program_opcode = SPINOR_OP_BP; /* write one byte. */ nor->write(nor, to, 1, retlen, buf); @@ -682,7 +682,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, /* Write out most of the data here. */ for (; actual < len - 1; actual += 2) { - nor->program_opcode = OPCODE_AAI_WP; + nor->program_opcode = SPINOR_OP_AAI_WP; /* write two bytes. */ nor->write(nor, to, 2, retlen, buf + actual); @@ -703,7 +703,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, if (actual != len) { write_enable(nor); - nor->program_opcode = OPCODE_BP; + nor->program_opcode = SPINOR_OP_BP; nor->write(nor, to, 1, retlen, buf + actual); ret = wait_till_ready(nor); @@ -777,7 +777,7 @@ static int macronix_quad_enable(struct spi_nor *nor) write_enable(nor); nor->cmd_buf[0] = val | SR_QUAD_EN_MX; - nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); if (wait_till_ready(nor)) return 1; @@ -802,7 +802,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val) nor->cmd_buf[0] = val & 0xff; nor->cmd_buf[1] = (val >> 8); - return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 2, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); } static int spansion_quad_enable(struct spi_nor *nor) @@ -967,13 +967,13 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { - nor->erase_opcode = OPCODE_BE_4K; + nor->erase_opcode = SPINOR_OP_BE_4K; mtd->erasesize = 4096; } else if (info->flags & SECT_4K_PMC) { - nor->erase_opcode = OPCODE_BE_4K_PMC; + nor->erase_opcode = SPINOR_OP_BE_4K_PMC; mtd->erasesize = 4096; } else { - nor->erase_opcode = OPCODE_SE; + nor->erase_opcode = SPINOR_OP_SE; mtd->erasesize = info->sector_size; } @@ -1014,23 +1014,23 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = OPCODE_QUAD_READ; + nor->read_opcode = SPINOR_OP_QUAD_READ; break; case SPI_NOR_DUAL: - nor->read_opcode = OPCODE_DUAL_READ; + nor->read_opcode = SPINOR_OP_DUAL_READ; break; case SPI_NOR_FAST: - nor->read_opcode = OPCODE_FAST_READ; + nor->read_opcode = SPINOR_OP_FAST_READ; break; case SPI_NOR_NORMAL: - nor->read_opcode = OPCODE_NORM_READ; + nor->read_opcode = SPINOR_OP_NORM_READ; break; default: dev_err(dev, "No Read opcode defined\n"); return -EINVAL; } - nor->program_opcode = OPCODE_PP; + nor->program_opcode = SPINOR_OP_PP; if (info->addr_width) nor->addr_width = info->addr_width; @@ -1041,21 +1041,21 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = OPCODE_QUAD_READ_4B; + nor->read_opcode = SPINOR_OP_QUAD_READ_4B; break; case SPI_NOR_DUAL: - nor->read_opcode = OPCODE_DUAL_READ_4B; + nor->read_opcode = SPINOR_OP_DUAL_READ_4B; break; case SPI_NOR_FAST: - nor->read_opcode = OPCODE_FAST_READ_4B; + nor->read_opcode = SPINOR_OP_FAST_READ_4B; break; case SPI_NOR_NORMAL: - nor->read_opcode = OPCODE_NORM_READ_4B; + nor->read_opcode = SPINOR_OP_NORM_READ_4B; break; } - nor->program_opcode = OPCODE_PP_4B; + nor->program_opcode = SPINOR_OP_PP_4B; /* No small sector erase for 4-byte command set */ - nor->erase_opcode = OPCODE_SE_4B; + nor->erase_opcode = SPINOR_OP_SE_4B; mtd->erasesize = info->sector_size; } else set_4byte(nor, info->jedec_id, 1); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index a6e8719..f1fe1a6 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,41 +11,41 @@ #define __LINUX_MTD_SPI_NOR_H /* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ -#define OPCODE_RDCR 0x35 /* Read configuration register */ +#define SPINOR_OP_WREN 0x06 /* Write enable */ +#define SPINOR_OP_RDSR 0x05 /* Read status register */ +#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SPINOR_OP_NORM_READ 0x03 /* Read data bytes (low frequency) */ +#define SPINOR_OP_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define SPINOR_OP_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SPINOR_OP_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ +#define SPINOR_OP_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define SPINOR_OP_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define SPINOR_OP_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ /* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ +#define SPINOR_OP_BP 0x02 /* Byte program */ +#define SPINOR_OP_WRDI 0x04 /* Write disable */ +#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ /* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ +#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ +#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ /* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ +#define SPINOR_OP_BRWR 0x17 /* Bank register write */ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ -- cgit v0.10.2 From 61fd12f5ae6fded8da952cca8b92b613e08687a2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Apr 2014 14:45:31 +0200 Subject: mtd: m25p80: Revive dual read support Commit 03e296f613affcc2671c1e86d8c25ecad867204e ("mtd: m25p80: use the SPI nor framework") accidentally removed support for Dual SPI read transfers. Add it back. Signed-off-by: Geert Uytterhoeven Acked-by: Huang Shijie Signed-off-by: Brian Norris (cherry picked from commit 8848e161b79421e340bb13facc11d89570b77940) Change-Id: Ia4651487c4ca9f76063c7ec2bb8e04644e21b1df Reviewed-on: http://git.am.freescale.net:8181/20055 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 1557d8f..ed7e0a1b 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -221,6 +221,8 @@ static int m25p_probe(struct spi_device *spi) if (spi->mode & SPI_RX_QUAD) mode = SPI_NOR_QUAD; + else if (spi->mode & SPI_RX_DUAL) + mode = SPI_NOR_DUAL; ret = spi_nor_scan(nor, spi_get_device_id(spi), mode); if (ret) return ret; -- cgit v0.10.2 From 78d41fd015b994728762a2c058fcc8fe9dd8e6b0 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 09:54:31 +0800 Subject: mtd: spi-nor: fix the wrong dummy value For the DDR Quad read, the dummy cycles maybe 3 or 6 which is less then 8. The dummy cycles is actually 8 for SPI fast/dual/quad read. This patch makes preparations for the DDR quad read, it fixes the wrong dummy value for both the spi-nor.c and m25p80.c. Signed-off-by: Huang Shijie The upstream status of this patch can be found at: https://patchwork.kernel.org/patch/4074921/ Change-Id: I7ca208d1964812f77f66708c659d826c39baff4d Reviewed-on: http://git.am.freescale.net:8181/20056 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index ed7e0a1b..6205b22 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -128,9 +128,12 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, struct spi_device *spi = flash->spi; struct spi_transfer t[2]; struct spi_message m; - int dummy = nor->read_dummy; + unsigned int dummy = nor->read_dummy; int ret; + /* convert the dummy cycles to the number of bytes */ + dummy /= 8; + /* Wait till previous write/erase is done. */ ret = nor->wait_till_ready(nor); if (ret) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1716f3c..bd84110 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -77,7 +77,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) case SPI_NOR_FAST: case SPI_NOR_DUAL: case SPI_NOR_QUAD: - return 1; + return 8; case SPI_NOR_NORMAL: return 0; } -- cgit v0.10.2 From ffdc806f73a2cbe03f112f090b2f01aed7079301 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:06:13 +0800 Subject: mtd: spi-nor: add a new field for spi_nor{} We need the SPI NOR child node to store some specific features, such as the dummy cycles for the DDR Quad read. But now, we only have the @dev field in the spi_nor{}. The @dev may points to a spi_device{} for m25p80, while it may points to a platform_deivice{} for the SPI NOR controller, such as fsl_quadspi.c. It is not convenient for us to get come information from the SPI NOR flash. This patch adds a new field @np to spi_nor{}, it points to the child node for the SPI NOR flash. Signed-off-by: Huang Shijie The patch was pending at: https://patchwork.kernel.org/patch/4074931/ Change-Id: I0613744ca972ddc7481d82488d3e4c4e74c67652 Reviewed-on: http://git.am.freescale.net:8181/20057 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 6205b22..92a14fb 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -215,6 +215,7 @@ static int m25p_probe(struct spi_device *spi) nor->read_reg = m25p80_read_reg; nor->dev = &spi->dev; + nor->np = spi->dev.of_node; nor->mtd = &flash->mtd; nor->priv = flash; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index f1fe1a6..f3e87bf 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -109,6 +109,8 @@ enum spi_nor_ops { * @mtd: point to a mtd_info structure * @lock: the lock for the read/write/erase/lock/unlock operations * @dev: point to a spi device, or a spi nor controller device. + * @np: If exit, it points to a device_node which stands for the + * SPI NOR flash child node. * @page_size: the page size of the SPI NOR * @addr_width: number of address bytes * @erase_opcode: the opcode for erasing a sector @@ -140,6 +142,7 @@ struct spi_nor { struct mtd_info *mtd; struct mutex lock; struct device *dev; + struct device_node *np; u32 page_size; u8 addr_width; u8 erase_opcode; -- cgit v0.10.2 From cdbd6a3f41ff52225e07d72a0dd980d6c035bacc Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 19:16:49 -0700 Subject: mtd: spi-nor: unify read opcode variants with ST SPI FSM serial_flash_cmds.h defines our opcodes a little differently. Let's borrow its naming, since it's borrowed from the SFDP standard, and it's more extensible. This prepares us for merging serial_flash_cmds.h and spi-nor.h opcode listing. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie (cherry picked from commit 58b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1) Change-Id: Id3eff06b36acaa388d2581af59abc569c6a7f474 Reviewed-on: http://git.am.freescale.net:8181/20058 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 2977f02..b41bbbc 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -294,12 +294,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) lut_base = SEQID_QUAD_READ * 4; if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_QUAD_READ; + cmd = SPINOR_OP_READ_1_1_4; addrlen = ADDR24BIT; dummy = 8; } else { /* use the 4-byte address */ - cmd = SPINOR_OP_QUAD_READ; + cmd = SPINOR_OP_READ_1_1_4; addrlen = ADDR32BIT; dummy = 8; } @@ -388,7 +388,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { - case SPINOR_OP_QUAD_READ: + case SPINOR_OP_READ_1_1_4: return SEQID_QUAD_READ; case SPINOR_OP_WREN: return SEQID_WREN; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index bd84110..02236e8 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1014,16 +1014,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = SPINOR_OP_QUAD_READ; + nor->read_opcode = SPINOR_OP_READ_1_1_4; break; case SPI_NOR_DUAL: - nor->read_opcode = SPINOR_OP_DUAL_READ; + nor->read_opcode = SPINOR_OP_READ_1_1_2; break; case SPI_NOR_FAST: - nor->read_opcode = SPINOR_OP_FAST_READ; + nor->read_opcode = SPINOR_OP_READ_FAST; break; case SPI_NOR_NORMAL: - nor->read_opcode = SPINOR_OP_NORM_READ; + nor->read_opcode = SPINOR_OP_READ; break; default: dev_err(dev, "No Read opcode defined\n"); @@ -1041,16 +1041,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = SPINOR_OP_QUAD_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_1_1_4; break; case SPI_NOR_DUAL: - nor->read_opcode = SPINOR_OP_DUAL_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_1_1_2; break; case SPI_NOR_FAST: - nor->read_opcode = SPINOR_OP_FAST_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_FAST; break; case SPI_NOR_NORMAL: - nor->read_opcode = SPINOR_OP_NORM_READ_4B; + nor->read_opcode = SPINOR_OP_READ4; break; } nor->program_opcode = SPINOR_OP_PP_4B; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index f3e87bf..48fe9fc 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -10,14 +10,22 @@ #ifndef __LINUX_MTD_SPI_NOR_H #define __LINUX_MTD_SPI_NOR_H +/* + * Note on opcode nomenclature: some opcodes have a format like + * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number + * of I/O lines used for the opcode, address, and data (respectively). The + * FUNCTION has an optional suffix of '4', to represent an opcode which + * requires a 4-byte (32-bit) address. + */ + /* Flash opcodes. */ #define SPINOR_OP_WREN 0x06 /* Write enable */ #define SPINOR_OP_RDSR 0x05 /* Read status register */ #define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ -#define SPINOR_OP_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define SPINOR_OP_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define SPINOR_OP_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define SPINOR_OP_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -28,10 +36,10 @@ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define SPINOR_OP_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define SPINOR_OP_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define SPINOR_OP_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define SPINOR_OP_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ -- cgit v0.10.2 From b77452e4557346fe6a2b715f8033a4f2549b7a72 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Mon, 29 Sep 2014 13:58:00 +0800 Subject: mtd: spi-nor: add DDR quad read support This patch adds the DDR quad read support by the following: [1] add SPI_NOR_DDR_QUAD read mode. [2] add DDR Quad read opcodes: SPINOR_OP_READ_1_4_4_D / SPINOR_OP_READ4_1_4_4_D [3] add set_ddr_quad_mode() to initialize for the DDR quad read. Currently it only works for Spansion NOR. [3] about the dummy cycles. We set the dummy with 8 for DDR quad read by default. The m25p80.c can not support the DDR quad read, but the SPI NOR controller can set the dummy value in its child DT node, and the SPI NOR framework can parse it out. Test this patch for Spansion s25fl128s NOR flash. Signed-off-by: Huang Shijie The upstream status of this patch can be found at: https://patchwork.kernel.org/patch/4074961 Change-Id: Id67e247e357bdd8bea99816e31f603898671d968 Reviewed-on: http://git.am.freescale.net:8181/20125 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 02236e8..792354a 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -73,7 +73,20 @@ static int read_cr(struct spi_nor *nor) */ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) { + u32 dummy; + switch (nor->flash_read) { + case SPI_NOR_DDR_QUAD: + /* + * The m25p80.c can not support the DDR quad read. + * We set the dummy cycles to 8 by default. The SPI NOR + * controller driver can set it in its child DT node. + * We parse it out here. + */ + if (nor->np && !of_property_read_u32(nor->np, + "spi-nor,ddr-quad-read-dummy", &dummy)) { + return dummy; + } case SPI_NOR_FAST: case SPI_NOR_DUAL: case SPI_NOR_QUAD: @@ -402,6 +415,7 @@ struct flash_info { #define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +#define SPI_NOR_DDR_QUAD_READ 0x80 /* Flash supports DDR Quad Read */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -829,6 +843,24 @@ static int spansion_quad_enable(struct spi_nor *nor) return 0; } +static int set_ddr_quad_mode(struct spi_nor *nor, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_AMD: /* Spansion, actually */ + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, + "Spansion DDR quad-read not enabled\n"); + return status; + } + return status; + default: + return -EINVAL; + } +} + static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) { int status; @@ -999,8 +1031,15 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, if (info->flags & SPI_NOR_NO_FR) nor->flash_read = SPI_NOR_NORMAL; - /* Quad/Dual-read mode takes precedence over fast/normal */ - if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + /* DDR Quad/Quad/Dual-read mode takes precedence over fast/normal */ + if (mode == SPI_NOR_DDR_QUAD && info->flags & SPI_NOR_DDR_QUAD_READ) { + ret = set_ddr_quad_mode(nor, info->jedec_id); + if (ret) { + dev_err(dev, "DDR quad mode not supported\n"); + return ret; + } + nor->flash_read = SPI_NOR_DDR_QUAD; + } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { ret = set_quad_mode(nor, info->jedec_id); if (ret) { dev_err(dev, "quad mode not supported\n"); @@ -1013,6 +1052,14 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { + case SPI_NOR_DDR_QUAD: + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Spansion */ + nor->read_opcode = SPINOR_OP_READ_1_4_4_D; + } else { + dev_err(dev, "DDR Quad Read is not supported.\n"); + return -EINVAL; + } + break; case SPI_NOR_QUAD: nor->read_opcode = SPINOR_OP_READ_1_1_4; break; @@ -1040,6 +1087,9 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Dedicated 4-byte command set */ switch (nor->flash_read) { + case SPI_NOR_DDR_QUAD: + nor->read_opcode = SPINOR_OP_READ4_1_4_4_D; + break; case SPI_NOR_QUAD: nor->read_opcode = SPINOR_OP_READ4_1_1_4; break; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 48fe9fc..0f02a22 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -12,10 +12,11 @@ /* * Note on opcode nomenclature: some opcodes have a format like - * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number + * SPINOR_OP_FUNCTION{4,}_x_y_z{_D}. The numbers x, y,and z stand for the number * of I/O lines used for the opcode, address, and data (respectively). The * FUNCTION has an optional suffix of '4', to represent an opcode which - * requires a 4-byte (32-bit) address. + * requires a 4-byte (32-bit) address. The suffix of 'D' stands for the + * DDR mode. */ /* Flash opcodes. */ @@ -26,6 +27,7 @@ #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ #define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ #define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ_1_4_4_D 0xed /* Read data bytes (DDR Quad SPI) */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -40,6 +42,7 @@ #define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ #define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ #define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ4_1_4_4_D 0xee /* Read data bytes (DDR Quad SPI) */ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ @@ -74,6 +77,7 @@ enum read_mode { SPI_NOR_FAST, SPI_NOR_DUAL, SPI_NOR_QUAD, + SPI_NOR_DDR_QUAD, }; /** -- cgit v0.10.2 From 586682455f9d6fe7854baf499b4b9c436716863c Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:22:10 +0800 Subject: Documentation: mtd: add a new document for SPI NOR flash We need a DT property to store the dummy cycles for DDR Quad read. This is a common feature for the SPI NOR flash, such as Spansion and Micron chips. Add this file to describe this specific SPI NOR flash features which will be referred by the SPI NOR flash drivers. Signed-off-by: Huang Shijie This patch was under discussion at: https://patchwork.kernel.org/patch/4074941/ Change-Id: Id5eac26d6ee941a0976721a704881bc7012716ad Reviewed-on: http://git.am.freescale.net:8181/20126 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/mtd/spi-nor-flash.txt b/Documentation/devicetree/bindings/mtd/spi-nor-flash.txt new file mode 100644 index 0000000..aba4d54 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/spi-nor-flash.txt @@ -0,0 +1,7 @@ +This file defines some DT properties for specific SPI NOR flash features. +The SPI NOR controller drivers may refer to this file, such as fsl-quadspi.txt + +Optional properties: + - spi-nor,ddr-quad-read-dummy: The dummy cycles used by the DDR Quad read. + Please refer to the chip's datasheet. This + property can be 4 or 6 which is less then 8. -- cgit v0.10.2 From 78e5793dbd53dbbbcea92434d9da002435ddcff8 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:41 +0800 Subject: Documentation: add the binding file for Freescale QuadSPI driver This patch adds the binding file for Freescale QuadSPI driver. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris (cherry picked from commit c7a8a11c6bb78f49895d42294a88002ea544922f) Change-Id: I1af8259f5518ae733eae21f9f21ca13b827adba3 Reviewed-on: http://git.am.freescale.net:8181/20127 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt new file mode 100644 index 0000000..823d134 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -0,0 +1,35 @@ +* Freescale Quad Serial Peripheral Interface(QuadSPI) + +Required properties: + - compatible : Should be "fsl,vf610-qspi" + - reg : the first contains the register location and length, + the second contains the memory mapping address and length + - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" + - interrupts : Should contain the interrupt for the device + - clocks : The clocks needed by the QuadSPI controller + - clock-names : the name of the clocks + +Optional properties: + - fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B. + Each bus can be connected with two NOR flashes. + Most of the time, each bus only has one NOR flash + connected, this is the default case. + But if there are two NOR flashes connected to the + bus, you should enable this property. + (Please check the board's schematic.) + +Example: + +qspi0: quadspi@40044000 { + compatible = "fsl,vf610-qspi"; + reg = <0x40044000 0x1000>, <0x20000000 0x10000000>; + reg-names = "QuadSPI", "QuadSPI-memory"; + interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks VF610_CLK_QSPI0_EN>, + <&clks VF610_CLK_QSPI0>; + clock-names = "qspi_en", "qspi"; + + flash0: s25fl128s@0 { + .... + }; +}; -- cgit v0.10.2 From 7f64109228271ecbac063c99954d55767c4ca8e7 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:27:07 +0800 Subject: Documentation: fsl-quadspi: update the document The patch updates the document by adding more information to describe the DT proporties used by the Freescale Quadspi driver and the childs nodes. For the child node for SPI NOR flash, we add the required property ("spi-max-frequency"), and refer to spi-nor-flash.txt for the optional properties. Signed-off-by: Huang Shijie The upstream status of this patch: http://patchwork.ozlabs.org/patch/343229/ Change-Id: Ib625035f0fd863840eca9dce66699836883bdfe3 Reviewed-on: http://git.am.freescale.net:8181/20128 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt index 823d134..7e1dbaf 100644 --- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -1,5 +1,11 @@ * Freescale Quad Serial Peripheral Interface(QuadSPI) +The QuadSPI controller acts as the SPI master. It is described with a node +for the controller and a set of child nodes for each SPI NOR flash. + +Part I - The DT node for the controller: +------------------------------ + Required properties: - compatible : Should be "fsl,vf610-qspi" - reg : the first contains the register location and length, @@ -18,6 +24,16 @@ Optional properties: bus, you should enable this property. (Please check the board's schematic.) +Part II - The DT nodes for each SPI NOR flash +------------------------------ +Required properties: +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: + Please refer to the Documentation/devicetree/bindings/mtd/spi-nor-flash.txt + If you set the "spi-nor,ddr-quad-read-dummy", it means you enable the DDR + quad read feature for the driver. + Example: qspi0: quadspi@40044000 { -- cgit v0.10.2 From 73954df217c66505833f22272eda288ad107908c Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:29:10 +0800 Subject: mtd: fsl-quadspi: use the information stored in spi-nor{} We can get the read/write/erase opcode from the spi nor framework now. What's more is that we can get the correct dummy cycles. This patch uses the information stored in the spi_nor{} to remove the hardcode in the fsl_qspi_init_lut(). Signed-off-by: Huang Shijie The upstream status of this patch can be found: https://patchwork.kernel.org/patch/4074971/ Change-Id: I32f982872df1729582f4122ac0dede934d749a04 Reviewed-on: http://git.am.freescale.net:8181/20129 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index b41bbbc..24ce06f 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -280,8 +280,10 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) { void *__iomem base = q->iobase; int rxfifo = q->devtype_data->rxfifo; + struct spi_nor *nor = &q->nor[0]; + u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT; u32 lut_base; - u8 cmd, addrlen, dummy; + u8 op, dm; int i; fsl_qspi_unlock_lut(q); @@ -292,40 +294,28 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Quad Read */ lut_base = SEQID_QUAD_READ * 4; - - if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_READ_1_1_4; - addrlen = ADDR24BIT; - dummy = 8; - } else { - /* use the 4-byte address */ - cmd = SPINOR_OP_READ_1_1_4; - addrlen = ADDR32BIT; - dummy = 8; + op = nor->read_opcode; + dm = nor->read_dummy; + if (nor->flash_read == SPI_NOR_QUAD) { + if (op == SPINOR_OP_READ_1_1_4 || op == SPINOR_OP_READ4_1_1_4) { + /* read mode : 1-1-4 */ + writel(LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + writel(LUT0(DUMMY, PAD1, dm) | LUT1(READ, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + } else { + dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); + } } - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), - base + QUADSPI_LUT(lut_base)); - writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo), - base + QUADSPI_LUT(lut_base + 1)); - /* Write enable */ lut_base = SEQID_WREN * 4; writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base)); /* Page Program */ lut_base = SEQID_PP * 4; - - if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_PP; - addrlen = ADDR24BIT; - } else { - /* use the 4-byte address */ - cmd = SPINOR_OP_PP; - addrlen = ADDR32BIT; - } - - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + writel(LUT0(CMD, PAD1, nor->program_opcode) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); @@ -336,17 +326,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Erase a sector */ lut_base = SEQID_SE * 4; - - if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_SE; - addrlen = ADDR24BIT; - } else { - /* use the 4-byte address */ - cmd = SPINOR_OP_SE; - addrlen = ADDR32BIT; - } - - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + writel(LUT0(CMD, PAD1, nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); /* Erase the whole chip */ @@ -396,6 +376,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) return SEQID_WRDI; case SPINOR_OP_RDSR: return SEQID_RDSR; + case SPINOR_OP_BE_4K: case SPINOR_OP_SE: return SEQID_SE; case SPINOR_OP_CHIP_ERASE: -- cgit v0.10.2 From 29875e3645e20e47cc6b0c05443b2ee37a58ff21 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:32:02 +0800 Subject: mtd: fsl-quadspi: add the DDR quad read support for Spansion NOR Add the DDR quad read support for the fsl-quadspi driver. Check the "spi-nor,ddr-quad-read-dummy" DT property, if the DT node is exit, it means we could enable the DDR quad read. (1) Test this patch with imx6sx-sdb board (Spansion s25fl128s) The clock rate is 66MHz. (2) The information of NOR flash: ----------------------------------------------- root@imx6qdlsolo:~# mtdinfo /dev/mtd0 mtd0 Name: 21e4000.qspi Type: nor Eraseblock size: 65536 bytes, 64.0 KiB Amount of eraseblocks: 256 (16777216 bytes, 16.0 MiB) Minimum input/output unit size: 1 byte Sub-page size: 1 byte Character device major/minor: 90:0 Bad blocks are allowed: false Device is writable: true ----------------------------------------------- (3) Test this patch set with UBIFS & bonnie++: ----------------------------------------------- ubiattach /dev/ubi_ctrl -m 0 ubimkvol /dev/ubi0 -N test -m mount -t ubifs ubi0:test tmp bonnie++ -d tmp -u 0 -s 10 -r 5 ----------------------------------------------- (4) Test this patch with mtd_speedtest.ko root@imx6qdlsolo:~# insmod mtd_speedtest.ko dev=0 ================================================= mtd_speedtest: MTD device: 0 mtd_speedtest: not NAND flash, assume page size is 512 bytes. mtd_speedtest: MTD device size 16777216, eraseblock size 65536, page size 512, count of eraseblocks 256, pages per eraseblock 128, OOB size 0 mtd_speedtest: testing eraseblock write speed mtd_speedtest: eraseblock write speed is 665 KiB/s mtd_speedtest: testing eraseblock read speed mtd_speedtest: eraseblock read speed is 49799 KiB/s mtd_speedtest: testing page write speed mtd_speedtest: page write speed is 662 KiB/s mtd_speedtest: testing page read speed mtd_speedtest: page read speed is 24236 KiB/s mtd_speedtest: testing 2 page write speed mtd_speedtest: 2 page write speed is 657 KiB/s mtd_speedtest: testing 2 page read speed mtd_speedtest: 2 page read speed is 32637 KiB/s mtd_speedtest: Testing erase speed mtd_speedtest: erase speed is 518 KiB/s mtd_speedtest: Testing 2x multi-block erase speed mtd_speedtest: 2x multi-block erase speed is 506 KiB/s mtd_speedtest: Testing 4x multi-block erase speed mtd_speedtest: 4x multi-block erase speed is 503 KiB/s mtd_speedtest: Testing 8x multi-block erase speed mtd_speedtest: 8x multi-block erase speed is 501 KiB/s mtd_speedtest: Testing 16x multi-block erase speed mtd_speedtest: 16x multi-block erase speed is 498 KiB/s mtd_speedtest: Testing 32x multi-block erase speed mtd_speedtest: 32x multi-block erase speed is 496 KiB/s mtd_speedtest: Testing 64x multi-block erase speed mtd_speedtest: 64x multi-block erase speed is 495 KiB/s mtd_speedtest: finished ================================================= (5) Conclusion: The DDR quad read could be 49799 KiB/s. Signed-off-by: Huang Shijie The upstream status of this patch can be found at: https://patchwork.kernel.org/patch/4074991/ Change-Id: I80c58bec32659d375c4656402e0c3d3ce3ba2e55 Reviewed-on: http://git.am.freescale.net:8181/20130 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 24ce06f..ddab7e8 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -29,6 +29,9 @@ /* The registers */ #define QUADSPI_MCR 0x00 +#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT 29 +#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK \ + (1 << MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT) #define QUADSPI_MCR_RESERVED_SHIFT 16 #define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT) #define QUADSPI_MCR_MDIS_SHIFT 14 @@ -292,7 +295,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) for (i = 0; i < QUADSPI_LUT_NUM; i++) writel(0, base + QUADSPI_LUT_BASE + i * 4); - /* Quad Read */ + /* Quad Read and DDR Quad Read*/ lut_base = SEQID_QUAD_READ * 4; op = nor->read_opcode; dm = nor->read_dummy; @@ -307,6 +310,24 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) } else { dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); } + } else if (nor->flash_read == SPI_NOR_DDR_QUAD) { + if (op == SPINOR_OP_READ_1_4_4_D || + op == SPINOR_OP_READ4_1_4_4_D) { + /* read mode : 1-4-4, such as Spansion s25fl128s. */ + writel(LUT0(CMD, PAD1, op) + | LUT1(ADDR_DDR, PAD4, addrlen), + base + QUADSPI_LUT(lut_base)); + + writel(LUT0(MODE_DDR, PAD4, 0xff) + | LUT1(DUMMY, PAD1, dm), + base + QUADSPI_LUT(lut_base + 1)); + + writel(LUT0(READ_DDR, PAD4, rxfifo) + | LUT1(JMP_ON_CS, PAD1, 0), + base + QUADSPI_LUT(lut_base + 2)); + } else { + dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); + } } /* Write enable */ @@ -368,6 +389,9 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { + case SPINOR_OP_READ_1_4_4_D: + case SPINOR_OP_READ4_1_4_4_D: + case SPINOR_OP_READ4_1_1_4: case SPINOR_OP_READ_1_1_4: return SEQID_QUAD_READ; case SPINOR_OP_WREN: @@ -558,6 +582,8 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q) static void fsl_qspi_init_abh_read(struct fsl_qspi *q) { void __iomem *base = q->iobase; + struct spi_nor *nor = &q->nor[0]; + u32 reg, reg2; int seqid; /* AHB configuration for access buffer 0/1/2 .*/ @@ -572,9 +598,30 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) writel(0, base + QUADSPI_BUF2IND); /* Set the default lut sequence for AHB Read. */ - seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode); + seqid = fsl_qspi_get_seqid(q, nor->read_opcode); writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT, q->iobase + QUADSPI_BFGENCR); + + /* enable the DDR quad read */ + if (nor->flash_read == SPI_NOR_DDR_QUAD) { + reg = readl(q->iobase + QUADSPI_MCR); + + /* Firstly, disable the module */ + writel(reg | QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + + /* Set the Sampling Register for DDR */ + reg2 = readl(q->iobase + QUADSPI_SMPR); + reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK; + reg2 |= (2 << QUADSPI_SMPR_DDRSMP_SHIFT); + writel(reg2, q->iobase + QUADSPI_SMPR); + + /* Enable the module again (enable the DDR too) */ + reg |= QUADSPI_MCR_DDR_EN_MASK; + if (is_imx6sx_qspi(q)) + reg |= MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK; + + writel(reg, q->iobase + QUADSPI_MCR); + } } /* We use this function to do some basic init for spi_nor_scan(). */ @@ -863,7 +910,9 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* iterate the subnodes. */ for_each_available_child_of_node(dev->of_node, np) { const struct spi_device_id *id; + enum read_mode mode = SPI_NOR_QUAD; char modalias[40]; + u32 dummy = 0; /* skip the holes */ if (!has_second_chip) @@ -874,6 +923,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) nor->mtd = mtd; nor->dev = dev; + nor->np = np; nor->priv = q; mtd->priv = nor; @@ -899,10 +949,16 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (ret < 0) goto map_failed; + /* Can we enable the DDR Quad Read? */ + ret = of_property_read_u32(np, "spi-nor,ddr-quad-read-dummy", + &dummy); + if (!ret && dummy > 0) + mode = SPI_NOR_DDR_QUAD; + /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); - ret = spi_nor_scan(nor, id, SPI_NOR_QUAD); + ret = spi_nor_scan(nor, id, mode); if (ret) goto map_failed; -- cgit v0.10.2 From 58bb64ef5ae667aad702dabd6929b56c9a5c0ed2 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:36:11 +0800 Subject: mtd: spi-nor: add DDR quad read support for Micron This patch adds the DDR(or DTR) quad read support for the Micron SPI NOR flash. Tested with n25q256a. Signed-off-by: Huang Shijie The upstream link of this patch: https://patchwork.kernel.org/patch/4075011/ Change-Id: Ib226886ff8d9e80d6aa5fb72dc86278188b2e3a3 Reviewed-on: http://git.am.freescale.net:8181/20131 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 792354a..67b6fce 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -856,6 +856,9 @@ static int set_ddr_quad_mode(struct spi_nor *nor, u32 jedec_id) return status; } return status; + case CFI_MFR_ST: /* Micron, actually */ + /* DTR quad read works with the Extended SPI protocol. */ + return 0; default: return -EINVAL; } @@ -1055,6 +1058,8 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, case SPI_NOR_DDR_QUAD: if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Spansion */ nor->read_opcode = SPINOR_OP_READ_1_4_4_D; + } else if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { + nor->read_opcode = SPINOR_OP_READ_1_1_4_D; } else { dev_err(dev, "DDR Quad Read is not supported.\n"); return -EINVAL; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 0f02a22..e1bc2c3 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -27,6 +27,7 @@ #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ #define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ #define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ_1_1_4_D 0x6d /* Read data bytes (DDR Quad SPI) */ #define SPINOR_OP_READ_1_4_4_D 0xed /* Read data bytes (DDR Quad SPI) */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ -- cgit v0.10.2 From cb2adaf5cb56c53ed14e1ab872f14e1926049ff9 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 10:37:51 +0800 Subject: mtd: fsl-quadspi: add DDR quad read support for Micron Add DDR quad read opcode and LUT sequence for Micron N25Q256A. The performace : ================================================= mtd_speedtest: MTD device: 1 mtd_speedtest: not NAND flash, assume page size is 512 bytes. mtd_speedtest: MTD device size 33554432, eraseblock size 65536, page size 512, count of eraseblocks 512, pages per eraseblock 128, OOB size 0 mtd_speedtest: testing eraseblock write speed mtd_speedtest: eraseblock write speed is 2426 KiB/s mtd_speedtest: testing eraseblock read speed mtd_speedtest: eraseblock read speed is 32157 KiB/s mtd_speedtest: testing page write speed mtd_speedtest: page write speed is 2362 KiB/s mtd_speedtest: testing page read speed mtd_speedtest: page read speed is 17741 KiB/s mtd_speedtest: testing 2 page write speed mtd_speedtest: 2 page write speed is 2384 KiB/s mtd_speedtest: testing 2 page read speed mtd_speedtest: 2 page read speed is 24058 KiB/s mtd_speedtest: Testing erase speed mtd_speedtest: erase speed is 1927529 KiB/s mtd_speedtest: Testing 2x multi-block erase speed mtd_speedtest: 2x multi-block erase speed is 2184533 KiB/s mtd_speedtest: Testing 4x multi-block erase speed mtd_speedtest: 4x multi-block erase speed is 2184533 KiB/s mtd_speedtest: Testing 8x multi-block erase speed mtd_speedtest: 8x multi-block erase speed is 2340571 KiB/s mtd_speedtest: Testing 16x multi-block erase speed mtd_speedtest: 16x multi-block erase speed is 2340571 KiB/s mtd_speedtest: Testing 32x multi-block erase speed mtd_speedtest: 32x multi-block erase speed is 2340571 KiB/s mtd_speedtest: Testing 64x multi-block erase speed mtd_speedtest: 64x multi-block erase speed is 2340571 KiB/s mtd_speedtest: finished ================================================= Signed-off-by: Huang Shijie The upstream link of this patch: https://patchwork.kernel.org/patch/4075001/ Change-Id: Ice094cec23114af5cda5dd4b24c3b2e60719fd6a Reviewed-on: http://git.am.freescale.net:8181/20132 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index ddab7e8..edef8f1 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -325,6 +325,18 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) writel(LUT0(READ_DDR, PAD4, rxfifo) | LUT1(JMP_ON_CS, PAD1, 0), base + QUADSPI_LUT(lut_base + 2)); + } else if (op == SPINOR_OP_READ_1_1_4_D) { + /* read mode : 1-1-4, such as Micron N25Q256A. */ + writel(LUT0(CMD, PAD1, op) + | LUT1(ADDR_DDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + writel(LUT0(DUMMY, PAD1, dm) + | LUT1(READ_DDR, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + + writel(LUT0(JMP_ON_CS, PAD1, 0), + base + QUADSPI_LUT(lut_base + 2)); } else { dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); } @@ -389,6 +401,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { + case SPINOR_OP_READ_1_1_4_D: case SPINOR_OP_READ_1_4_4_D: case SPINOR_OP_READ4_1_4_4_D: case SPINOR_OP_READ4_1_1_4: -- cgit v0.10.2 From fc66840264ac55ac7d4bc8e324f9a4f34bf2ec44 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Mon, 29 Sep 2014 14:46:32 +0800 Subject: mtd: spi-nor: read 6 bytes for the ID Currently, we read 5 bytes for ID, but s25fl128s has the same ext_id(0x4d01) with s25fl129p1. The s25fl128s can support the DDR Quad read, while s25fl129p1 does not. So we have to distinguish the two NOR flashs. This patch reads out 6 bytes for the ID, and use the 6 bytes ID to search the right flash_info. The detail of the patch is: [1] change the "ext_id" from u16 to u32. We can store two bytes or three bytes with the @ext_id now. [2] search the right flash_info with the 6byte ID and the new @ext_id. We use "matched" variable to track the legacy two bytes @ext_id. If the flash_info's @ext_id is three bytes, we will use the sixth byte of the ID to check it. [3] add the new item to spi_nor_ids for s25fl128s. Signed-off-by: Huang Shijie The upstream link of this patch: http://patchwork.ozlabs.org/patch/353244/ Change-Id: Id27774eefbf9e1a8f80e1dcd8fb0d3f9363923c1 Reviewed-on: http://git.am.freescale.net:8181/20134 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 67b6fce..5df9d0d 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -396,7 +396,7 @@ struct flash_info { * then a two byte device id. */ u32 jedec_id; - u16 ext_id; + u32 ext_id; /* The size listed here is what works with SPINOR_OP_SE, which isn't * necessarily called a "sector" by the vendor. @@ -519,6 +519,8 @@ const struct spi_device_id spi_nor_ids[] = { { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl128s", INFO(0x012018, 0x4d0180, 64 * 1024, 256, + SPI_NOR_QUAD_READ) }, { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, @@ -607,12 +609,13 @@ EXPORT_SYMBOL_GPL(spi_nor_ids); static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) { int tmp; - u8 id[5]; + u8 id[6]; u32 jedec; - u16 ext_jedec; + u32 ext_jedec; struct flash_info *info; + int matched = -1; - tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 6); if (tmp < 0) { dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); return ERR_PTR(tmp); @@ -628,8 +631,26 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { info = (void *)spi_nor_ids[tmp].driver_data; if (info->jedec_id == jedec) { - if (info->ext_id == 0 || info->ext_id == ext_jedec) + if (info->ext_id == 0) return &spi_nor_ids[tmp]; + + /* the legacy two bytes ext_id */ + if ((info->ext_id >> 16) == 0) { + if (info->ext_id == ext_jedec) + matched = tmp; + } else { + /* check the sixth byte now */ + ext_jedec = ext_jedec << 8 | id[5]; + if (info->ext_id == ext_jedec) + return &spi_nor_ids[tmp]; + + /* reset back the ext_jedec */ + ext_jedec >>= 8; + } + } else { + /* shortcut */ + if (matched != -1) + return &spi_nor_ids[matched]; } } dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); -- cgit v0.10.2 From 60df914f43d16d84dedba6909baf84488a578b31 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 5 Sep 2013 15:51:21 +0900 Subject: spi: fsl-dspi: add missing __iomem annotation Added missing __iomem annotation in order to fix the following sparse warnings: drivers/spi/spi-fsl-dspi.c:140:16: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:140:16: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:140:16: got void * drivers/spi/spi-fsl-dspi.c:143:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:143:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:143:9: got void * drivers/spi/spi-fsl-dspi.c:132:18: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:132:18: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:132:18: got void * drivers/spi/spi-fsl-dspi.c:241:17: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:241:17: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:241:17: got void * drivers/spi/spi-fsl-dspi.c:132:18: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:132:18: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:132:18: got void * drivers/spi/spi-fsl-dspi.c:259:29: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:259:29: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:259:29: got void * drivers/spi/spi-fsl-dspi.c:266:29: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:266:29: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:266:29: got void * drivers/spi/spi-fsl-dspi.c:298:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:298:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:298:9: got void * drivers/spi/spi-fsl-dspi.c:299:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:299:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:299:9: got void * drivers/spi/spi-fsl-dspi.c:300:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:300:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:300:9: got void * drivers/spi/spi-fsl-dspi.c:303:17: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:303:17: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:303:17: got void * drivers/spi/spi-fsl-dspi.c:318:21: warning: incorrect type in argument 1 (different address spaces) drivers/spi/spi-fsl-dspi.c:318:21: expected void const volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:318:21: got void * drivers/spi/spi-fsl-dspi.c:327:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:327:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:327:9: got void * drivers/spi/spi-fsl-dspi.c:386:9: warning: incorrect type in argument 2 (different address spaces) drivers/spi/spi-fsl-dspi.c:386:9: expected void volatile [noderef] *addr drivers/spi/spi-fsl-dspi.c:386:9: got void * drivers/spi/spi-fsl-dspi.c:485:20: warning: incorrect type in assignment (different address spaces) drivers/spi/spi-fsl-dspi.c:485:20: expected void *base drivers/spi/spi-fsl-dspi.c:485:20: got void [noderef] * Signed-off-by: Jingoo Han Signed-off-by: Mark Brown (cherry picked from commit 2ef3599d8181afcbd347226faba75991f9e008fc) Change-Id: I51696586aa10ad6f848614146baed2f64dd5ff9a Reviewed-on: http://git.am.freescale.net:8181/20080 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index f132234..b7c632e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -108,7 +108,7 @@ struct fsl_dspi { struct spi_bitbang bitbang; struct platform_device *pdev; - void *base; + void __iomem *base; int irq; struct clk *clk; -- cgit v0.10.2 From 6ac66cdab994ddc32d1a7676620eee8be04ac1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 10 Sep 2013 10:46:33 +0200 Subject: spi: fsl-dspi: several minor improvements and fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - improve dependencies using COMPILE_TEST - fix a typo - drop platform_set_drvdata(pdev, NULL) in error path of probe - make MODULE_LICENSE match the header Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown (cherry picked from commit b444d1dfe296433a93d6b814d924e0ab99ad7e7b) Change-Id: Id41bb981d35f216297d5cc49c071a64c99912c5b Reviewed-on: http://git.am.freescale.net:8181/20081 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9c53cc..c463d36 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -264,6 +264,7 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select SPI_BITBANG + depends on SOC_VF610 || COMPILE_TEST help This enables support for the Freescale DSPI controller in master mode. VF610 platform uses the controller. diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index b7c632e..5faa9b2 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -165,7 +165,7 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, } } - pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\ + pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld\ ,we use the max prescaler value.\n", speed_hz, clkrate); *pbr = ARRAY_SIZE(pbr_tbl) - 1; *br = ARRAY_SIZE(brs) - 1; @@ -519,7 +519,6 @@ out_clk_put: clk_disable_unprepare(dspi->clk); out_master_put: spi_master_put(master); - platform_set_drvdata(pdev, NULL); return ret; } @@ -547,5 +546,5 @@ static struct platform_driver fsl_dspi_driver = { module_platform_driver(fsl_dspi_driver); MODULE_DESCRIPTION("Freescale DSPI Controller Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v0.10.2 From fa8d74c3d960b7aefe058cd74d50a3a124efb969 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 10 Sep 2013 15:43:41 +0800 Subject: spi: bitbang: Let spi_bitbang_start() take a reference to master Many drivers that use bitbang library have a leak on probe error paths. This is because once a spi_master_get() call succeeds, we need an additional spi_master_put() call to free the memory. Fix this issue by moving the code taking a reference to master to spi_bitbang_start(), so spi_bitbang_start() will take a reference to master on success. With this change, the caller is responsible for calling spi_bitbang_stop() to decrement the reference and spi_master_put() as counterpart of spi_alloc_master() to prevent a memory leak. So now we have below patten for drivers using bitbang library: probe: spi_alloc_master -> Init reference count to 1 spi_bitbang_start -> Increment reference count remove: spi_bitbang_stop -> Decrement reference count spi_master_put -> Decrement reference count (reference count reaches 0) Fixup all users accordingly. Signed-off-by: Axel Lin Suggested-by: Uwe Kleine-Koenig Acked-by: Uwe Kleine-Koenig Signed-off-by: Mark Brown (cherry picked from commit 94c69f765f1b4a658d96905ec59928e3e3e07e6a) Change-Id: Ifce35be3f5b3a64cfbf8f42dcafa35134f039899 Reviewed-on: http://git.am.freescale.net:8181/20082 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 9a64c3f..595b62c 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -219,7 +219,7 @@ static int altera_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.chipselect = altera_spi_chipsel; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 05dd692..e4a2001 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -231,7 +231,7 @@ static int ath79_spi_probe(struct platform_device *pdev) master->num_chipselect = pdata->num_chipselect; } - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.chipselect = ath79_spi_chipselect; sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 1d00d9b3..e06400a 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -775,7 +775,7 @@ static int au1550_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8c11355..0056623 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -414,10 +414,16 @@ static int spi_bitbang_unprepare_hardware(struct spi_master *spi) * This routine registers the spi_master, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). + * + * On success, this routine will take a reference to master. The caller is + * responsible for calling spi_bitbang_stop() to decrement the reference and + * spi_master_put() as counterpart of spi_alloc_master() to prevent a memory + * leak. */ int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; + int ret; if (!master || !bitbang->chipselect) return -EINVAL; @@ -449,7 +455,11 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - return spi_register_master(master); + ret = spi_register_master(spi_master_get(master)); + if (ret) + spi_master_put(master); + + return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_start); diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 5ed08e5..b1ecea2 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -225,7 +225,7 @@ static void butterfly_attach(struct parport *p) master->bus_num = 42; master->num_chipselect = 2; - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = butterfly_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 8fbfe24..af2bb73 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -916,7 +916,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto unmap_io; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; if (dspi->bitbang.master == NULL) { ret = -ENODEV; goto irq_free; @@ -925,7 +925,7 @@ static int davinci_spi_probe(struct platform_device *pdev) dspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dspi->clk)) { ret = -ENODEV; - goto put_master; + goto irq_free; } clk_prepare_enable(dspi->clk); @@ -1015,8 +1015,6 @@ free_dma: free_clk: clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); -put_master: - spi_master_put(master); irq_free: free_irq(dspi->irq, dspi); unmap_io: @@ -1024,7 +1022,7 @@ unmap_io: release_region: release_mem_region(dspi->pbase, resource_size(r)); free_master: - kfree(master); + spi_master_put(master); err: return ret; } @@ -1051,11 +1049,11 @@ static int davinci_spi_remove(struct platform_device *pdev) clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); - spi_master_put(master); free_irq(dspi->irq, dspi); iounmap(dspi->base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(dspi->pbase, resource_size(r)); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 7d84418..d428a40 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -347,7 +347,7 @@ static int efm32_spi_probe(struct platform_device *pdev) ddata = spi_master_get_devdata(master); - ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.master = master; ddata->bitbang.chipselect = efm32_spi_chipselect; ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 5faa9b2..2957faf 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -449,7 +449,7 @@ static int dspi_probe(struct platform_device *pdev) dspi = spi_master_get_devdata(master); dspi->pdev = pdev; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; dspi->bitbang.chipselect = dspi_chipselect; dspi->bitbang.setup_transfer = dspi_setup_transfer; dspi->bitbang.txrx_bufs = dspi_txrx_transfer; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 68b69fe..14c01b4 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -467,7 +467,7 @@ static int spi_gpio_probe(struct platform_device *pdev) } #endif - spi_gpio->bitbang.master = spi_master_get(master); + spi_gpio->bitbang.master = master; spi_gpio->bitbang.chipselect = spi_gpio_chipselect; if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) { @@ -486,7 +486,6 @@ static int spi_gpio_probe(struct platform_device *pdev) status = spi_bitbang_start(&spi_gpio->bitbang); if (status < 0) { - spi_master_put(spi_gpio->bitbang.master); gpio_free: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); @@ -510,13 +509,13 @@ static int spi_gpio_remove(struct platform_device *pdev) /* stop() unregisters child devices too */ status = spi_bitbang_stop(&spi_gpio->bitbang); - spi_master_put(spi_gpio->bitbang.master); if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); + spi_master_put(spi_gpio->bitbang.master); return status; } diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 9410695..954d071 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -786,7 +786,7 @@ static int spi_imx_probe(struct platform_device *pdev) master->num_chipselect = num_cs; spi_imx = spi_master_get_devdata(master); - spi_imx->bitbang.master = spi_master_get(master); + spi_imx->bitbang.master = master; for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 0759b5d..41c5765 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -222,7 +222,7 @@ static void spi_lm70llp_attach(struct parport *p) /* * SPI and bitbang hookup. */ - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = lm70_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx; pp->bitbang.flags = SPI_3WIRE; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index ba4e99a..8497df2 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -349,7 +349,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) } hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; @@ -437,7 +437,6 @@ err_iomap: kfree(hw->ioarea); err_pdata: spi_master_put(hw->master); - err_nomem: return err; } diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 333cb1b..91c6685 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -306,7 +306,7 @@ static int tiny_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.setup_transfer = tiny_spi_setup_transfer; diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 0ee53c2..c57740bb7 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -396,7 +396,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) master->dev.of_node = np; platform_set_drvdata(op, master); hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->dev = dev; init_completion(&hw->done); @@ -558,6 +558,7 @@ static int spi_ppc4xx_of_remove(struct platform_device *op) free_irq(hw->irqnum, hw); iounmap(hw->regs); free_gpios(hw); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index ce318d9..c9cfdfa 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -524,7 +524,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); memset(hw, 0, sizeof(struct s3c24xx_spi)); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 8eefeb6..38eb24d 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -133,7 +133,7 @@ static int sh_sci_spi_probe(struct platform_device *dev) sp->info = dev_get_platdata(&dev->dev); /* setup spi bitbang adaptor */ - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.master->bus_num = sp->info->bus_num; sp->bitbang.master->num_chipselect = sp->info->num_chipselect; sp->bitbang.chipselect = sh_sci_spi_chipselect; diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index a1f21b7..592b4af 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -632,7 +632,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) if (ret) goto free_master; - sspi->bitbang.master = spi_master_get(master); + sspi->bitbang.master = master; sspi->bitbang.chipselect = spi_sirfsoc_chipselect; sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer; sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 0bf1b2c..ec3a83f 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -372,7 +372,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; xspi = spi_master_get_devdata(master); - xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.master = master; xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; -- cgit v0.10.2 From 17973c275e01568e09ab7214c25ac5c2020329f5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 12 Oct 2013 15:15:31 +0800 Subject: spi: fsl-dspi: add missing clk_disable_unprepare() in dspi_remove() clock source is prepared and enabled by clk_prepare_enable() in probe function, but no disable or unprepare in remove. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown (cherry picked from commit 05209f457069e595ce0262a9032cbade05398571) Change-Id: Ib45ea4b0fb7f211e5e31352268c4aff4afd4b3e3 Reviewed-on: http://git.am.freescale.net:8181/20083 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 2957faf..49a7346 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -530,6 +530,7 @@ static int dspi_remove(struct platform_device *pdev) /* Disconnect from the SPI framework */ spi_bitbang_stop(&dspi->bitbang); + clk_disable_unprepare(dspi->clk); spi_master_put(dspi->bitbang.master); return 0; -- cgit v0.10.2 From e2ed5c25646a8fb2b7bdd71e9183c473ad8e8dc5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 8 Jan 2014 22:27:54 +0800 Subject: spi: fsl-dspi: Add missing breaks for switch cases Signed-off-by: Axel Lin Signed-off-by: Mark Brown (cherry picked from commit e07725be735e1791cf74e9db06a8bde62e1f517d) Change-Id: Id0205a7e671df191a232d328091f1dc8a963b9f1 Reviewed-on: http://git.am.freescale.net:8181/20084 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 49a7346..bd0c658 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -320,8 +320,10 @@ static void dspi_chipselect(struct spi_device *spi, int value) switch (value) { case BITBANG_CS_ACTIVE: pushr |= SPI_PUSHR_CONT; + break; case BITBANG_CS_INACTIVE: pushr &= ~SPI_PUSHR_CONT; + break; } writel(pushr, dspi->base + SPI_PUSHR); -- cgit v0.10.2 From cabbcd518fd26e3ad3f94f6a9464cfaae4789253 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Sun, 28 Sep 2014 16:34:27 +0800 Subject: spi: Remove duplicate code to set default bits_per_word setting The implementation in spi_setup() already set spi->bits_per_word = 8 when spi->bits_per_word is 0 before calling spi->master->setup. So we don't need to do it again in setup() callback. Signed-off-by: Axel Lin Acked-by: Marek Vasut Acked-by: Barry Song Acked-by: Guenter Roeck Signed-off-by: Mark Brown (cherry picked from commit 23061f1eb844edd349c3a0f5f40e244c9d2abfde) Change-Id: I842867bbb99dd71bfcf30ac90300c6ca23c384b9 Reviewed-on: http://git.am.freescale.net:8181/20085 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index bd0c658..a253920 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -375,9 +375,6 @@ static int dspi_setup(struct spi_device *spi) if (!spi->max_speed_hz) return -EINVAL; - if (!spi->bits_per_word) - spi->bits_per_word = 8; - return dspi_setup_transfer(spi, NULL); } -- cgit v0.10.2 From e251d7f80c22d192cc706b3800e0836705ea8007 Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Wed, 12 Feb 2014 15:29:05 +0800 Subject: spi/fsl-dspi: Convert to use regmap and add big-endian support Freescale DSPI module will have two endianess in different platform, but ARM is little endian. So when DSPI in big endian, core in little endian, readl and writel can not adjust R/W register in this condition. This patch will remove general readl/writel, and import regmap mechanism. Data endian will be transfered in regmap APIs. Documents: dspi add bool "big-endian" in dts node if DSPI module work in big endian. Signed-off-by: Chao Fu Reviewed-by: Xiubo Li Signed-off-by: Mark Brown (cherry picked from commit 1acbdeb92c87fc18eade0815dedc257fe45b88b7) Change-Id: I60e630bb18a2101af3154633bbe5824a52ac45f2 Reviewed-on: http://git.am.freescale.net:8181/20086 Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt index a1fb303..5376de4 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt @@ -10,6 +10,7 @@ Required properties: - pinctrl-names: must contain a "default" entry. - spi-num-chipselects : the number of the chipselect signals. - bus-num : the slave chip chipselect signal number. +- big-endian : if DSPI modudle is big endian, the bool will be set in node. Example: dspi0@4002c000 { @@ -24,6 +25,7 @@ dspi0@4002c000 { bus-num = <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_dspi0_1>; + big-endian; status = "okay"; sflash: at26df081a@0 { diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c463d36..21bf181 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -264,6 +264,7 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select SPI_BITBANG + select REGMAP_MMIO depends on SOC_VF610 || COMPILE_TEST help This enables support for the Freescale DSPI controller in master diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index a253920..286f51d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -108,7 +109,7 @@ struct fsl_dspi { struct spi_bitbang bitbang; struct platform_device *pdev; - void __iomem *base; + struct regmap *regmap; int irq; struct clk *clk; @@ -129,18 +130,11 @@ struct fsl_dspi { static inline int is_double_byte_mode(struct fsl_dspi *dspi) { - return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK) - == SPI_FRAME_BITS(8)) ? 0 : 1; -} + unsigned int val; -static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits) -{ - u32 temp; + regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val); - temp = readl(dspi->base + SPI_CTAR(dspi->cs)); - temp &= ~SPI_FRAME_BITS_MASK; - temp |= SPI_FRAME_BITS(bits); - writel(temp, dspi->base + SPI_CTAR(dspi->cs)); + return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; } static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, @@ -188,7 +182,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) */ if (tx_word && (dspi->len == 1)) { dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - set_bit_mode(dspi, 8); + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); tx_word = 0; } @@ -238,7 +233,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ } - writel(dspi_pushr, dspi->base + SPI_PUSHR); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); + tx_count++; } @@ -253,17 +249,23 @@ static int dspi_transfer_read(struct fsl_dspi *dspi) while ((dspi->rx < dspi->rx_end) && (rx_count < DSPI_FIFO_SIZE)) { if (rx_word) { + unsigned int val; + if ((dspi->rx_end - dspi->rx) == 1) break; - d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) *(u16 *)dspi->rx = d; dspi->rx += 2; } else { - d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + unsigned int val; + + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) *(u8 *)dspi->rx = d; dspi->rx++; @@ -295,13 +297,13 @@ static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) if (!dspi->tx) dspi->dataflags |= TRAN_STATE_TX_VOID; - writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR); - writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs)); - writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER); + regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), dspi->cur_chip->ctar_val); + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); if (t->speed_hz) - writel(dspi->cur_chip->ctar_val, - dspi->base + SPI_CTAR(dspi->cs)); + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); dspi_transfer_write(dspi); @@ -315,7 +317,9 @@ static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) static void dspi_chipselect(struct spi_device *spi, int value) { struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - u32 pushr = readl(dspi->base + SPI_PUSHR); + unsigned int pushr; + + regmap_read(dspi->regmap, SPI_PUSHR, &pushr); switch (value) { case BITBANG_CS_ACTIVE: @@ -326,7 +330,7 @@ static void dspi_chipselect(struct spi_device *spi, int value) break; } - writel(pushr, dspi->base + SPI_PUSHR); + regmap_write(dspi->regmap, SPI_PUSHR, pushr); } static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) @@ -382,13 +386,15 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) { struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - writel(SPI_SR_EOQF, dspi->base + SPI_SR); + regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); dspi_transfer_read(dspi); if (!dspi->len) { if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) - set_bit_mode(dspi, 16); + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); + dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); } else { @@ -434,12 +440,20 @@ static const struct dev_pm_ops dspi_pm = { SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume) }; +static struct regmap_config dspi_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x88, +}; + static int dspi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct spi_master *master; struct fsl_dspi *dspi; struct resource *res; + void __iomem *base; int ret = 0, cs_num, bus_num; master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); @@ -474,12 +488,24 @@ static int dspi_probe(struct platform_device *pdev) master->bus_num = bus_num; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dspi->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dspi->base)) { - ret = PTR_ERR(dspi->base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); goto out_master_put; } + dspi_regmap_config.lock_arg = dspi; + dspi_regmap_config.val_format_endian = + of_property_read_bool(np, "big-endian") + ? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT; + dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, + &dspi_regmap_config); + if (IS_ERR(dspi->regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(dspi->regmap)); + return PTR_ERR(dspi->regmap); + } + dspi->irq = platform_get_irq(pdev, 0); if (dspi->irq < 0) { dev_err(&pdev->dev, "can't get platform irq\n"); -- cgit v0.10.2 From ab10da3c5d4d5ec8e7464d28cfa27d3c02e9805a Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Wed, 12 Feb 2014 15:29:06 +0800 Subject: spi/fsl-dspi: Remove some coding sytle not in standard Remove some coding sytle not in standard in former code. Signed-off-by: Chao Fu Reviewed-by: Xiubo Li Signed-off-by: Mark Brown (cherry picked from commit 88386e858bbb21eb05c15c040dbb3e6ad1cb3568) Change-Id: Ia4d6096ca86bf5e939748017120ddf33dafdb12e Reviewed-on: http://git.am.freescale.net:8181/20087 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 286f51d..b113b89 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -111,9 +111,9 @@ struct fsl_dspi { struct regmap *regmap; int irq; - struct clk *clk; + struct clk *clk; - struct spi_transfer *cur_transfer; + struct spi_transfer *cur_transfer; struct chip_data *cur_chip; size_t len; void *tx; @@ -124,8 +124,8 @@ struct fsl_dspi { u8 cs; u16 void_write_data; - wait_queue_head_t waitq; - u32 waitflags; + wait_queue_head_t waitq; + u32 waitflags; }; static inline int is_double_byte_mode(struct fsl_dspi *dspi) -- cgit v0.10.2 From 18e92b67cb0977efb668713d16c6a18045d6b88e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 26 Feb 2014 10:30:14 +0900 Subject: spi: fsl-dspi: Use SIMPLE_DEV_PM_OPS macro Use SIMPLE_DEV_PM_OPS macro in order to make the code simpler. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown (cherry picked from commit ba811addff3d29d1ea9861dbfc06e8ef80714f94) Change-Id: Ic2086e577d7d11df710c03c85202864b76151882 Reviewed-on: http://git.am.freescale.net:8181/20088 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index b113b89..2249cb7 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -436,9 +436,7 @@ static int dspi_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static const struct dev_pm_ops dspi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume) -}; +static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); static struct regmap_config dspi_regmap_config = { .reg_bits = 32, -- cgit v0.10.2 From 0e2c166707d78690131bedcd946c691d9bbc36c8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 25 Mar 2014 09:19:05 +0800 Subject: spi: fsl-dspi: Fix memory leak The memory allocated for chip is not freed anywhere. Convert to use devm_kzalloc to fix the memory leak. Signed-off-by: Axel Lin Signed-off-by: Mark Brown (cherry picked from commit 0e0cd9ea8961b82947a40471080e7968b634820e) Change-Id: Ia54f0ab47d9e36a375f66ea4c285b2f4ecc09ed4 Reviewed-on: http://git.am.freescale.net:8181/20089 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 2249cb7..d565eee 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -342,7 +342,8 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (chip == NULL) { - chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL); + chip = devm_kzalloc(&spi->dev, sizeof(struct chip_data), + GFP_KERNEL); if (!chip) return -ENOMEM; } @@ -353,7 +354,6 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) fmsz = spi->bits_per_word - 1; } else { pr_err("Invalid wordsize\n"); - kfree(chip); return -ENODEV; } -- cgit v0.10.2 From 46d5d4e9fa3de26aa7916fc03ae8c303f756ea6d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 May 2014 16:45:41 +0900 Subject: spi: fsl-dspi: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown (cherry picked from commit 790d190257b339bba9ff821d7d49f4567146f4ad) Change-Id: I27ecbe600bf91f328e2762cb65dfe76261060489 Reviewed-on: http://git.am.freescale.net:8181/20090 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d565eee..5021ddf 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -406,7 +406,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct of_device_id fsl_dspi_dt_ids[] = { +static const struct of_device_id fsl_dspi_dt_ids[] = { { .compatible = "fsl,vf610-dspi", .data = NULL, }, { /* sentinel */ } }; -- cgit v0.10.2 From 45cdc912b18c3df0abc0e14fc9a56939261b79ed Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 18 Aug 2014 15:48:20 +0800 Subject: spi: fsl-dspi: Convert to use regmap framework's endianness method. Signed-off-by: Xiubo Li Acked-by: Chao Fu Signed-off-by: Mark Brown (cherry picked from commit c99428d035908b9c0b8be452f9b091bc5e090256) Change-Id: I4afafcd1d5fd244ea287f899bca03baa95c61531 Reviewed-on: http://git.am.freescale.net:8181/20091 Tested-by: Review Code-CDREVIEW Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt index 5376de4..cbbe16e 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt @@ -10,7 +10,12 @@ Required properties: - pinctrl-names: must contain a "default" entry. - spi-num-chipselects : the number of the chipselect signals. - bus-num : the slave chip chipselect signal number. -- big-endian : if DSPI modudle is big endian, the bool will be set in node. + +Optional property: +- big-endian: If present the dspi device's registers are implemented + in big endian mode, otherwise in native mode(same with CPU), for more + detail please see: Documentation/devicetree/bindings/regmap/regmap.txt. + Example: dspi0@4002c000 { diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 5021ddf..ebc4d1f 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -493,9 +493,6 @@ static int dspi_probe(struct platform_device *pdev) } dspi_regmap_config.lock_arg = dspi; - dspi_regmap_config.val_format_endian = - of_property_read_bool(np, "big-endian") - ? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT; dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, &dspi_regmap_config); if (IS_ERR(dspi->regmap)) { -- cgit v0.10.2 From ba653c824c76ae1a2bf3902ef9b67e5da42286ea Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 11 Sep 2014 13:38:34 +0800 Subject: drivers/rtc/rtc-ds3232.c: enable ds3232 to work as wakeup source Add suspend/resume and device_init_wakeup to enable ds3232 as wakeup source, /sys/class/rtc/rtcX/wakealarm for set wakeup alarm. Signed-off-by: Wang Dongsheng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- This patch is pulled back from upstream with few backport changes: commit c93a3ae2d213ff75a279fe6e28d8f41ca7f01483 Change-Id: I63ec38f3757becb1a2c37d07e5d03de38ac7e996 Reviewed-on: http://git.am.freescale.net:8181/21139 Reviewed-by: Dongsheng Wang Reviewed-by: Zhengxiong Jin Tested-by: Zhengxiong Jin diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index dfd810f..adaf06c 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -57,6 +57,7 @@ struct ds3232 { * in the remove function. */ struct mutex mutex; + bool suspended; int exiting; }; @@ -345,7 +346,15 @@ static irqreturn_t ds3232_irq(int irq, void *dev_id) struct ds3232 *ds3232 = i2c_get_clientdata(client); disable_irq_nosync(irq); - schedule_work(&ds3232->work); + + /* + * If rtc as a wakeup source, can't schedule the work + * at system resume flow, because at this time the i2c bus + * has not been resumed. + */ + if (!ds3232->suspended) + schedule_work(&ds3232->work); + return IRQ_HANDLED; } @@ -363,22 +372,26 @@ static void ds3232_work(struct work_struct *work) if (stat & DS3232_REG_SR_A1F) { control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); - if (control < 0) - goto out; - /* disable alarm1 interrupt */ - control &= ~(DS3232_REG_CR_A1IE); - i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); - - /* clear the alarm pend flag */ - stat &= ~DS3232_REG_SR_A1F; - i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); - - rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + if (control < 0) { + pr_warn("Read DS3232 Control Register error." + "Disable IRQ%d.\n", client->irq); + } else { + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + i2c_smbus_write_byte_data(client, DS3232_REG_CR, + control); + + /* clear the alarm pend flag */ + stat &= ~DS3232_REG_SR_A1F; + i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + + rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + + if (!ds3232->exiting) + enable_irq(client->irq); + } } -out: - if (!ds3232->exiting) - enable_irq(client->irq); unlock: mutex_unlock(&ds3232->mutex); } @@ -411,23 +424,17 @@ static int ds3232_probe(struct i2c_client *client, if (ret) return ret; - ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, - &ds3232_rtc_ops, THIS_MODULE); - if (IS_ERR(ds3232->rtc)) { - dev_err(&client->dev, "unable to register the class device\n"); - return PTR_ERR(ds3232->rtc); - } - if (client->irq > 0) { ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, IRQF_SHARED, "ds3232", client); if (ret) { dev_err(&client->dev, "unable to request IRQ\n"); - return ret; } + device_init_wakeup(&client->dev, 1); } - - return 0; + ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, + &ds3232_rtc_ops, THIS_MODULE); + return PTR_ERR_OR_ZERO(ds3232->rtc); } static int ds3232_remove(struct i2c_client *client) @@ -446,6 +453,42 @@ static int ds3232_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int ds3232_suspend(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + + if (device_can_wakeup(dev)) { + ds3232->suspended = true; + irq_set_irq_wake(client->irq, 1); + } + + return 0; +} + +static int ds3232_resume(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + + if (ds3232->suspended) { + ds3232->suspended = false; + + /* Clear the hardware alarm pend flag */ + schedule_work(&ds3232->work); + + irq_set_irq_wake(client->irq, 0); + } + + return 0; +} +#endif + +static const struct dev_pm_ops ds3232_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) +}; + static const struct i2c_device_id ds3232_id[] = { { "ds3232", 0 }, { } @@ -456,6 +499,7 @@ static struct i2c_driver ds3232_driver = { .driver = { .name = "rtc-ds3232", .owner = THIS_MODULE, + .pm = &ds3232_pm_ops, }, .probe = ds3232_probe, .remove = ds3232_remove, -- cgit v0.10.2 From 05b9f0b59c8579abb3d04c1c1d0ed7234f2ab1ec Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Thu, 25 Sep 2014 17:52:02 +0800 Subject: spi:fsl-dspi:add dspi tcfq mode transfer support TCFQ is interrupt of Transfer Complete Flag in DSPI module. EOQ is interrupt of End of Queue Flag in DSPI module. For adopting of different platform, either of them is a way of DSPI transfer data. This patch add TCF support for DSPI module in other platform. The software will be changed in two transfer methods as followwing: EOQ TCFQ transfer data: dspi_eoq_write dspi_tcfq_write receive data: dspi_eoq_read dspi_tcfq_read Using which method will decided by paltform soc dtsi file. Remove bitbang: Add tcf funtions, DSPI module need get cs change information in a spi transfer. According cs change, DSPI will give last data the right flag. Bitbang provide cs change behind the last data in a transfer. So DSPI can not deal the last data in every transfer properly, so remove the bitbang in the driver. Merge duplicate code: dspi_data_from_popr dspi_data_to_pushr Remove clk reference in regmap I/O: Set the clk parament is NULL in devm_regmap_init_mmio_clk, it will avoid clk handle in every register read/write, and advance tranferring efficiency. Signed-off-by: Chao Fu The upstream status of this patch can be found at: https://patchwork.kernel.org/patch/4974181 Change-Id: I0ed6c1598c111e74b5e53c248e00dec2398fa900 Reviewed-on: http://git.am.freescale.net:8181/20096 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 21bf181..c17c349 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -263,7 +263,6 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" - select SPI_BITBANG select REGMAP_MMIO depends on SOC_VF610 || COMPILE_TEST help diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ebc4d1f..ec0ad01 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -63,9 +63,11 @@ #define SPI_CTAR0_SLAVE 0x0c #define SPI_SR 0x2c +#define SPI_SR_TCFQF 0x80000000 #define SPI_SR_EOQF 0x10000000 #define SPI_RSER 0x30 +#define SPI_RSER_TCFQE 0x80000000 #define SPI_RSER_EOQFE 0x10000000 #define SPI_PUSHR 0x34 @@ -105,8 +107,14 @@ struct chip_data { u16 void_write_data; }; +enum dspi_trans_mode { + DSPI_EOQ_MODE = 0, + DSPI_TCFQ_MODE, + DSPI_DMA_MODE, /*TODO*/ +}; + struct fsl_dspi { - struct spi_bitbang bitbang; + struct spi_master *master; struct platform_device *pdev; struct regmap *regmap; @@ -114,6 +122,7 @@ struct fsl_dspi { struct clk *clk; struct spi_transfer *cur_transfer; + struct spi_message *cur_msg; struct chip_data *cur_chip; size_t len; void *tx; @@ -123,6 +132,8 @@ struct fsl_dspi { char dataflags; u8 cs; u16 void_write_data; + u32 cs_change; + enum dspi_trans_mode trans_mode; wait_queue_head_t waitq; u32 waitflags; @@ -165,12 +176,68 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, *br = ARRAY_SIZE(brs) - 1; } -static int dspi_transfer_write(struct fsl_dspi *dspi) +static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) +{ + u16 d16; + u8 d8; + + if (tx_word) { + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + d16 = *(u16 *)dspi->tx; + dspi->tx += 2; + } else { + d16 = dspi->void_write_data; + } + + dspi->len -= 2; + + return SPI_PUSHR_TXDATA(d16) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + } else { + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + + d8 = *(u8 *)dspi->tx; + dspi->tx++; + } else { + d8 = (u8)dspi->void_write_data; + } + + dspi->len--; + + return SPI_PUSHR_TXDATA(d8) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + } +} + +static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) +{ + u16 d; + unsigned int val; + + if (rx_word) { + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); + + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u16 *)dspi->rx = d; + dspi->rx += 2; + } else { + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u8 *)dspi->rx = d; + dspi->rx++; + } +} + +static int dspi_eoq_write(struct fsl_dspi *dspi) { int tx_count = 0; int tx_word; - u16 d16; - u8 d8; u32 dspi_pushr = 0; int first = 1; @@ -188,43 +255,13 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) } while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { - if (tx_word) { - if (dspi->len == 1) - break; - - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { - d16 = *(u16 *)dspi->tx; - dspi->tx += 2; - } else { - d16 = dspi->void_write_data; - } - - dspi_pushr = SPI_PUSHR_TXDATA(d16) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | - SPI_PUSHR_CONT; - - dspi->len -= 2; - } else { - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { - - d8 = *(u8 *)dspi->tx; - dspi->tx++; - } else { - d8 = (u8)dspi->void_write_data; - } - - dspi_pushr = SPI_PUSHR_TXDATA(d8) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | - SPI_PUSHR_CONT; - - dspi->len--; - } + dspi_pushr = dspi_data_to_pushr(dspi, tx_word); if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { /* last transfer in the transfer */ dspi_pushr |= SPI_PUSHR_EOQ; + if ((dspi->cs_change) && (!dspi->len)) + dspi_pushr &= ~SPI_PUSHR_CONT; } else if (tx_word && (dspi->len == 1)) dspi_pushr |= SPI_PUSHR_EOQ; @@ -241,99 +278,119 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) return tx_count * (tx_word + 1); } -static int dspi_transfer_read(struct fsl_dspi *dspi) +static int dspi_eoq_read(struct fsl_dspi *dspi) { int rx_count = 0; int rx_word = is_double_byte_mode(dspi); - u16 d; + while ((dspi->rx < dspi->rx_end) && (rx_count < DSPI_FIFO_SIZE)) { - if (rx_word) { - unsigned int val; - - if ((dspi->rx_end - dspi->rx) == 1) - break; - - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); - - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - *(u16 *)dspi->rx = d; - dspi->rx += 2; - - } else { - unsigned int val; - - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - *(u8 *)dspi->rx = d; - dspi->rx++; - } + dspi_data_from_popr(dspi, rx_word); rx_count++; } return rx_count; } -static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) +static int dspi_tcfq_write(struct fsl_dspi *dspi) { - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - dspi->cur_transfer = t; - dspi->cur_chip = spi_get_ctldata(spi); - dspi->cs = spi->chip_select; - dspi->void_write_data = dspi->cur_chip->void_write_data; + int tx_word; + u32 dspi_pushr = 0; - dspi->dataflags = 0; - dspi->tx = (void *)t->tx_buf; - dspi->tx_end = dspi->tx + t->len; - dspi->rx = t->rx_buf; - dspi->rx_end = dspi->rx + t->len; - dspi->len = t->len; + tx_word = is_double_byte_mode(dspi); - if (!dspi->rx) - dspi->dataflags |= TRAN_STATE_RX_VOID; + if (tx_word && (dspi->len == 1)) { + dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); + tx_word = 0; + } - if (!dspi->tx) - dspi->dataflags |= TRAN_STATE_TX_VOID; + dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); - regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), dspi->cur_chip->ctar_val); - regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); + if ((dspi->cs_change) && (!dspi->len)) + dspi_pushr &= ~SPI_PUSHR_CONT; - if (t->speed_hz) - regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), - dspi->cur_chip->ctar_val); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - dspi_transfer_write(dspi); + return tx_word + 1; +} - if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) - dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); - dspi->waitflags = 0; +static void dspi_tcfq_read(struct fsl_dspi *dspi) +{ + int rx_word = is_double_byte_mode(dspi); - return t->len - dspi->len; + dspi_data_from_popr(dspi, rx_word); } -static void dspi_chipselect(struct spi_device *spi, int value) +static int dspi_transfer_one_message(struct spi_master *master, + struct spi_message *message) { - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - unsigned int pushr; + struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + struct spi_transfer *transfer; + int status = 0; + message->actual_length = 0; + + list_for_each_entry(transfer, &message->transfers, transfer_list) { + dspi->cur_transfer = transfer; + dspi->cur_msg = message; + dspi->cur_chip = spi_get_ctldata(spi); + dspi->cs = spi->chip_select; + if (dspi->cur_transfer->transfer_list.next + == &dspi->cur_msg->transfers) + transfer->cs_change = 1; + dspi->cs_change = transfer->cs_change; + dspi->void_write_data = dspi->cur_chip->void_write_data; + + dspi->dataflags = 0; + dspi->tx = (void *)transfer->tx_buf; + dspi->tx_end = dspi->tx + transfer->len; + dspi->rx = transfer->rx_buf; + dspi->rx_end = dspi->rx + transfer->len; + dspi->len = transfer->len; + + if (!dspi->rx) + dspi->dataflags |= TRAN_STATE_RX_VOID; + + if (!dspi->tx) + dspi->dataflags |= TRAN_STATE_TX_VOID; + + regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); + regmap_update_bits(dspi->regmap, SPI_MCR, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); + if (transfer->speed_hz) + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); + + if (dspi->trans_mode == DSPI_EOQ_MODE) { + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); + message->actual_length += dspi_eoq_write(dspi); + } else if (dspi->trans_mode == DSPI_TCFQ_MODE) { + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); + message->actual_length += dspi_tcfq_write(dspi); + } + + if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) + dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); + dspi->waitflags = 0; - regmap_read(dspi->regmap, SPI_PUSHR, &pushr); + if (transfer->delay_usecs) + udelay(transfer->delay_usecs); - switch (value) { - case BITBANG_CS_ACTIVE: - pushr |= SPI_PUSHR_CONT; - break; - case BITBANG_CS_INACTIVE: - pushr &= ~SPI_PUSHR_CONT; - break; } - regmap_write(dspi->regmap, SPI_PUSHR, pushr); + message->status = status; + spi_finalize_current_message(master); + + return status; + } -static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +static int dspi_setup(struct spi_device *spi) { struct chip_data *chip; struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); @@ -343,7 +400,7 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) chip = spi_get_ctldata(spi); if (chip == NULL) { chip = devm_kzalloc(&spi->dev, sizeof(struct chip_data), - GFP_KERNEL); + GFP_KERNEL); if (!chip) return -ENOMEM; } @@ -374,21 +431,28 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) return 0; } -static int dspi_setup(struct spi_device *spi) +static void dspi_cleanup(struct spi_device *spi) { - if (!spi->max_speed_hz) - return -EINVAL; + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); + + dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n", + spi->master->bus_num, spi->chip_select); - return dspi_setup_transfer(spi, NULL); + kfree(chip); } static irqreturn_t dspi_interrupt(int irq, void *dev_id) { struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - - regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); - - dspi_transfer_read(dspi); + struct spi_message *msg = dspi->cur_msg; + + if (dspi->trans_mode == DSPI_EOQ_MODE) { + regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); + dspi_eoq_read(dspi); + } else if (dspi->trans_mode == DSPI_TCFQ_MODE) { + regmap_write(dspi->regmap, SPI_SR, SPI_SR_TCFQF); + dspi_tcfq_read(dspi); + } if (!dspi->len) { if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) @@ -398,9 +462,10 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); } else { - dspi_transfer_write(dspi); - - return IRQ_HANDLED; + if (dspi->trans_mode == DSPI_EOQ_MODE) + msg->actual_length += dspi_eoq_write(dspi); + else if (dspi->trans_mode == DSPI_TCFQ_MODE) + msg->actual_length += dspi_tcfq_write(dspi); } return IRQ_HANDLED; @@ -460,12 +525,13 @@ static int dspi_probe(struct platform_device *pdev) dspi = spi_master_get_devdata(master); dspi->pdev = pdev; - dspi->bitbang.master = master; - dspi->bitbang.chipselect = dspi_chipselect; - dspi->bitbang.setup_transfer = dspi_setup_transfer; - dspi->bitbang.txrx_bufs = dspi_txrx_transfer; - dspi->bitbang.master->setup = dspi_setup; - dspi->bitbang.master->dev.of_node = pdev->dev.of_node; + dspi->master = master; + + master->transfer = NULL; + master->cleanup = dspi_cleanup; + master->setup = dspi_setup; + master->transfer_one_message = dspi_transfer_one_message; + master->dev.of_node = pdev->dev.of_node; master->mode_bits = SPI_CPOL | SPI_CPHA; master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | @@ -485,6 +551,15 @@ static int dspi_probe(struct platform_device *pdev) } master->bus_num = bus_num; + if (of_property_read_bool(np, "eoq-mode")) + dspi->trans_mode = DSPI_EOQ_MODE; + else if (of_property_read_bool(np, "tcfq-mode")) + dspi->trans_mode = DSPI_TCFQ_MODE; + else { + dev_err(&pdev->dev, "can't get dspi transfer mode\n"); + goto out_master_put; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { @@ -493,7 +568,7 @@ static int dspi_probe(struct platform_device *pdev) } dspi_regmap_config.lock_arg = dspi; - dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, + dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, &dspi_regmap_config); if (IS_ERR(dspi->regmap)) { dev_err(&pdev->dev, "failed to init regmap: %ld\n", @@ -526,7 +601,7 @@ static int dspi_probe(struct platform_device *pdev) init_waitqueue_head(&dspi->waitq); platform_set_drvdata(pdev, master); - ret = spi_bitbang_start(&dspi->bitbang); + ret = spi_register_master(master); if (ret != 0) { dev_err(&pdev->dev, "Problem registering DSPI master\n"); goto out_clk_put; @@ -549,9 +624,9 @@ static int dspi_remove(struct platform_device *pdev) struct fsl_dspi *dspi = spi_master_get_devdata(master); /* Disconnect from the SPI framework */ - spi_bitbang_stop(&dspi->bitbang); clk_disable_unprepare(dspi->clk); - spi_master_put(dspi->bitbang.master); + spi_unregister_master(dspi->master); + spi_master_put(dspi->master); return 0; } -- cgit v0.10.2 From 64a142f0e877d172deb70d25c3a6c3709be50bfb Mon Sep 17 00:00:00 2001 From: Chao Fu Date: Tue, 30 Sep 2014 10:36:34 +0800 Subject: spi-nor:fsl-quadspi:Add LS1021 support for fsl_quadspi FSL Quadspi module register bitwise is big-endian, but on ohter paltform is little endian. Add functions for Quadspi register read/write for bitwise: qspi_readl qpsi_writel Add devtype for LS1021: struct fsl_qspi_devtype_data ls1_data Signed-off-by: Chao Fu The upstream status of this patch can be found at: http://patchwork.ozlabs.org/patch/399388/ Change-Id: Ib1a8bc11a52e8d9bb1021c8956a5783d3915de2e Reviewed-on: http://git.am.freescale.net:8181/20296 Tested-by: Review Code-CDREVIEW Reviewed-by: Li Xiubo Reviewed-by: Zhengxiong Jin diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index edef8f1..2dd29c3 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -192,9 +192,11 @@ #define SEQID_EN4B 10 #define SEQID_BRWR 11 + enum fsl_qspi_devtype { FSL_QUADSPI_VYBRID, FSL_QUADSPI_IMX6SX, + FSL_QUADSPI_LS1, }; struct fsl_qspi_devtype_data { @@ -215,6 +217,12 @@ static struct fsl_qspi_devtype_data imx6sx_data = { .txfifo = 512 }; +static struct fsl_qspi_devtype_data ls1_data = { + .devtype = FSL_QUADSPI_LS1, + .rxfifo = 128, + .txfifo = 64 +}; + #define FSL_QSPI_MAX_CHIP 4 struct fsl_qspi { struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; @@ -242,6 +250,23 @@ static inline int is_imx6sx_qspi(struct fsl_qspi *q) return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX; } +static inline int is_ls1_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_LS1; +} + +static inline void qspi_writel(struct fsl_qspi *q, u32 val, void __iomem *addr) +{ + is_ls1_qspi(q) ? __raw_writel(cpu_to_be32(val), addr) : + __raw_writel(cpu_to_le32(val), addr); +} + +static inline u32 qspi_readl(struct fsl_qspi *q, void __iomem *addr) +{ + return is_ls1_qspi(q) ? cpu_to_be32((__force u32) __raw_readl(addr)) : + cpu_to_le32((__force u32) __raw_readl(addr)); +} + /* * An IC bug makes us to re-arrange the 32-bit data. * The following chips, such as IMX6SLX, have fixed this bug. @@ -253,14 +278,14 @@ static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) { - writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); - writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); + qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + qspi_writel(q, QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); } static inline void fsl_qspi_lock_lut(struct fsl_qspi *q) { - writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); - writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); + qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + qspi_writel(q, QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); } static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) @@ -269,8 +294,8 @@ static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) u32 reg; /* clear interrupt */ - reg = readl(q->iobase + QUADSPI_FR); - writel(reg, q->iobase + QUADSPI_FR); + reg = qspi_readl(q, q->iobase + QUADSPI_FR); + qspi_writel(q, reg, q->iobase + QUADSPI_FR); if (reg & QUADSPI_FR_TFF_MASK) complete(&q->c); @@ -281,7 +306,7 @@ static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) static void fsl_qspi_init_lut(struct fsl_qspi *q) { - void *__iomem base = q->iobase; + void __iomem *base = q->iobase; int rxfifo = q->devtype_data->rxfifo; struct spi_nor *nor = &q->nor[0]; u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT; @@ -293,7 +318,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Clear all the LUT table */ for (i = 0; i < QUADSPI_LUT_NUM; i++) - writel(0, base + QUADSPI_LUT_BASE + i * 4); + qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4); /* Quad Read and DDR Quad Read*/ lut_base = SEQID_QUAD_READ * 4; @@ -302,10 +327,13 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) if (nor->flash_read == SPI_NOR_QUAD) { if (op == SPINOR_OP_READ_1_1_4 || op == SPINOR_OP_READ4_1_1_4) { /* read mode : 1-1-4 */ - writel(LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen), + qspi_writel(q, + LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); - writel(LUT0(DUMMY, PAD1, dm) | LUT1(READ, PAD4, rxfifo), + qspi_writel(q, + LUT0(DUMMY, PAD1, dm) + | LUT1(READ, PAD4, rxfifo), base + QUADSPI_LUT(lut_base + 1)); } else { dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); @@ -314,28 +342,28 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) if (op == SPINOR_OP_READ_1_4_4_D || op == SPINOR_OP_READ4_1_4_4_D) { /* read mode : 1-4-4, such as Spansion s25fl128s. */ - writel(LUT0(CMD, PAD1, op) + qspi_writel(q, LUT0(CMD, PAD1, op) | LUT1(ADDR_DDR, PAD4, addrlen), base + QUADSPI_LUT(lut_base)); - writel(LUT0(MODE_DDR, PAD4, 0xff) + qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff) | LUT1(DUMMY, PAD1, dm), base + QUADSPI_LUT(lut_base + 1)); - writel(LUT0(READ_DDR, PAD4, rxfifo) + qspi_writel(q, LUT0(READ_DDR, PAD4, rxfifo) | LUT1(JMP_ON_CS, PAD1, 0), base + QUADSPI_LUT(lut_base + 2)); } else if (op == SPINOR_OP_READ_1_1_4_D) { /* read mode : 1-1-4, such as Micron N25Q256A. */ - writel(LUT0(CMD, PAD1, op) + qspi_writel(q, LUT0(CMD, PAD1, op) | LUT1(ADDR_DDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); - writel(LUT0(DUMMY, PAD1, dm) + qspi_writel(q, LUT0(DUMMY, PAD1, dm) | LUT1(READ_DDR, PAD4, rxfifo), base + QUADSPI_LUT(lut_base + 1)); - writel(LUT0(JMP_ON_CS, PAD1, 0), + qspi_writel(q, LUT0(JMP_ON_CS, PAD1, 0), base + QUADSPI_LUT(lut_base + 2)); } else { dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); @@ -344,55 +372,62 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Write enable */ lut_base = SEQID_WREN * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base)); + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN), + base + QUADSPI_LUT(lut_base)); /* Page Program */ lut_base = SEQID_PP * 4; - writel(LUT0(CMD, PAD1, nor->program_opcode) | LUT1(ADDR, PAD1, addrlen), + qspi_writel(q, LUT0(CMD, PAD1, + nor->program_opcode) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); - writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); + qspi_writel(q, LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); /* Read Status */ lut_base = SEQID_RDSR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), + qspi_writel(q, LUT0(CMD, PAD1, + SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Erase a sector */ lut_base = SEQID_SE * 4; - writel(LUT0(CMD, PAD1, nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen), + qspi_writel(q, LUT0(CMD, PAD1, + nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); /* Erase the whole chip */ lut_base = SEQID_CHIP_ERASE * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), base + QUADSPI_LUT(lut_base)); /* READ ID */ lut_base = SEQID_RDID * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), base + QUADSPI_LUT(lut_base)); /* Write Register */ lut_base = SEQID_WRSR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), base + QUADSPI_LUT(lut_base)); /* Read Configuration Register */ lut_base = SEQID_RDCR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Write disable */ lut_base = SEQID_WRDI * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + QUADSPI_LUT(lut_base)); + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRDI), + base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Micron) */ lut_base = SEQID_EN4B * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + QUADSPI_LUT(lut_base)); + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_EN4B), + base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Spansion) */ lut_base = SEQID_BRWR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + QUADSPI_LUT(lut_base)); + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR), + base + QUADSPI_LUT(lut_base)); fsl_qspi_lock_lut(q); } @@ -440,7 +475,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) static int fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) { - void *__iomem base = q->iobase; + void __iomem *base = q->iobase; int seqid; u32 reg, reg2; int err; @@ -450,15 +485,16 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) q->chip_base_addr, addr, len, cmd); /* save the reg */ - reg = readl(base + QUADSPI_MCR); + reg = qspi_readl(q, base + QUADSPI_MCR); - writel(q->memmap_phy + q->chip_base_addr + addr, base + QUADSPI_SFAR); - writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, + qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr, + base + QUADSPI_SFAR); + qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, base + QUADSPI_RBCT); - writel(reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); + qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); do { - reg2 = readl(base + QUADSPI_SR); + reg2 = qspi_readl(q, base + QUADSPI_SR); if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) { udelay(1); dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2); @@ -469,22 +505,23 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) /* trigger the LUT now */ seqid = fsl_qspi_get_seqid(q, cmd); - writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR); + qspi_writel(q, (seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, + base + QUADSPI_IPCR); /* Wait for the interrupt. */ err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000)); if (!err) { dev_err(q->dev, "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", - cmd, addr, readl(base + QUADSPI_FR), - readl(base + QUADSPI_SR)); + cmd, addr, qspi_readl(q, base + QUADSPI_FR), + qspi_readl(q, base + QUADSPI_SR)); err = -ETIMEDOUT; } else { err = 0; } /* restore the MCR */ - writel(reg, base + QUADSPI_MCR); + qspi_writel(q, reg, base + QUADSPI_MCR); return err; } @@ -496,7 +533,7 @@ static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf) int i = 0; while (len > 0) { - tmp = readl(q->iobase + QUADSPI_RBDR + i * 4); + tmp = qspi_readl(q, q->iobase + QUADSPI_RBDR + i * 4); tmp = fsl_qspi_endian_xchg(q, tmp); dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n", q->chip_base_addr, tmp); @@ -524,9 +561,9 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q) { u32 reg; - reg = readl(q->iobase + QUADSPI_MCR); + reg = qspi_readl(q, q->iobase + QUADSPI_MCR); reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK; - writel(reg, q->iobase + QUADSPI_MCR); + qspi_writel(q, reg, q->iobase + QUADSPI_MCR); /* * The minimum delay : 1 AHB + 2 SFCK clocks. @@ -535,7 +572,7 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q) udelay(1); reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK); - writel(reg, q->iobase + QUADSPI_MCR); + qspi_writel(q, reg, q->iobase + QUADSPI_MCR); } static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, @@ -549,13 +586,13 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, q->chip_base_addr, to, count); /* clear the TX FIFO. */ - tmp = readl(q->iobase + QUADSPI_MCR); - writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); + tmp = qspi_readl(q, q->iobase + QUADSPI_MCR); + qspi_writel(q, tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); /* fill the TX data to the FIFO */ for (j = 0, i = ((count + 3) / 4); j < i; j++) { tmp = fsl_qspi_endian_xchg(q, *txbuf); - writel(tmp, q->iobase + QUADSPI_TBDR); + qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR); txbuf++; } @@ -573,10 +610,10 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q) int nor_size = q->nor_size; void __iomem *base = q->iobase; - writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); - writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); - writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); - writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); + qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); + qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); + qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); + qspi_writel(q, nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); } /* @@ -600,40 +637,41 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) int seqid; /* AHB configuration for access buffer 0/1/2 .*/ - writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); - writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); - writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); - writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); + qspi_writel(q, QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); /* We only use the buffer3 */ - writel(0, base + QUADSPI_BUF0IND); - writel(0, base + QUADSPI_BUF1IND); - writel(0, base + QUADSPI_BUF2IND); + qspi_writel(q, 0, base + QUADSPI_BUF0IND); + qspi_writel(q, 0, base + QUADSPI_BUF1IND); + qspi_writel(q, 0, base + QUADSPI_BUF2IND); /* Set the default lut sequence for AHB Read. */ seqid = fsl_qspi_get_seqid(q, nor->read_opcode); - writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT, + qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT, q->iobase + QUADSPI_BFGENCR); /* enable the DDR quad read */ if (nor->flash_read == SPI_NOR_DDR_QUAD) { - reg = readl(q->iobase + QUADSPI_MCR); + reg = qspi_readl(q, q->iobase + QUADSPI_MCR); /* Firstly, disable the module */ - writel(reg | QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK, q->iobase + + QUADSPI_MCR); /* Set the Sampling Register for DDR */ - reg2 = readl(q->iobase + QUADSPI_SMPR); + reg2 = qspi_readl(q, q->iobase + QUADSPI_SMPR); reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK; reg2 |= (2 << QUADSPI_SMPR_DDRSMP_SHIFT); - writel(reg2, q->iobase + QUADSPI_SMPR); + qspi_writel(q, reg2, q->iobase + QUADSPI_SMPR); /* Enable the module again (enable the DDR too) */ reg |= QUADSPI_MCR_DDR_EN_MASK; if (is_imx6sx_qspi(q)) reg |= MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK; - writel(reg, q->iobase + QUADSPI_MCR); + qspi_writel(q, reg, q->iobase + QUADSPI_MCR); } } @@ -653,21 +691,21 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q) fsl_qspi_init_lut(q); /* Disable the module */ - writel(QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, + qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR); - reg = readl(base + QUADSPI_SMPR); - writel(reg & ~(QUADSPI_SMPR_FSDLY_MASK + reg = qspi_readl(q, base + QUADSPI_SMPR); + qspi_writel(q, reg & ~(QUADSPI_SMPR_FSDLY_MASK | QUADSPI_SMPR_FSPHS_MASK | QUADSPI_SMPR_HSENA_MASK | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR); /* Enable the module */ - writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, + qspi_writel(q, QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, base + QUADSPI_MCR); /* enable the interrupt */ - writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); + qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); return 0; } @@ -696,6 +734,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) static struct of_device_id fsl_qspi_dt_ids[] = { { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, + { .compatible = "fsl,ls1-qspi", .data = (void *)&ls1_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); @@ -1034,8 +1073,8 @@ static int fsl_qspi_remove(struct platform_device *pdev) mtd_device_unregister(&q->mtd[i]); /* disable the hardware */ - writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); - writel(0x0, q->iobase + QUADSPI_RSER); + qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER); clk_unprepare(q->clk); clk_unprepare(q->clk_en); -- cgit v0.10.2 From fb0146b5caaf75a80c80f25ca74ab9a7a72e3cf7 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Wed, 8 Oct 2014 15:18:11 +0800 Subject: qe: move qe from arch/powerpc/sysdev/ to drivers/soc/ ls1 has qe ip block too, so move qe code from platform directory to public directory. Signed-off-by: Zhao Qiang --- patch on upstream can be found with this link: http://patchwork.ozlabs.org/patch/385724/, it is under discussion Change-Id: I39aed531a4792990e3bb8ecc6f4e57f8d9b41bae Reviewed-on: http://git.am.freescale.net:8181/15818 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 95c2bf0..d20dc2b 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1028,8 +1028,6 @@ source "drivers/Kconfig" source "fs/Kconfig" -source "arch/powerpc/sysdev/qe_lib/Kconfig" - source "lib/Kconfig" source "arch/powerpc/Kconfig.debug" diff --git a/arch/powerpc/include/asm/immap_qe.h b/arch/powerpc/include/asm/immap_qe.h deleted file mode 100644 index c76ef30..0000000 --- a/arch/powerpc/include/asm/immap_qe.h +++ /dev/null @@ -1,488 +0,0 @@ -/* - * QUICC Engine (QE) Internal Memory Map. - * The Internal Memory Map for devices with QE on them. This - * is the superset of all QE devices (8360, etc.). - - * Copyright (C) 2006. Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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. - */ -#ifndef _ASM_POWERPC_IMMAP_QE_H -#define _ASM_POWERPC_IMMAP_QE_H -#ifdef __KERNEL__ - -#include -#include - -#define QE_IMMAP_SIZE (1024 * 1024) /* 1MB from 1MB+IMMR */ - -/* QE I-RAM */ -struct qe_iram { - __be32 iadd; /* I-RAM Address Register */ - __be32 idata; /* I-RAM Data Register */ - u8 res0[0x04]; - __be32 iready; /* I-RAM Ready Register */ - u8 res1[0x70]; -} __attribute__ ((packed)); - -/* QE Interrupt Controller */ -struct qe_ic_regs { - __be32 qicr; - __be32 qivec; - __be32 qripnr; - __be32 qipnr; - __be32 qipxcc; - __be32 qipycc; - __be32 qipwcc; - __be32 qipzcc; - __be32 qimr; - __be32 qrimr; - __be32 qicnr; - u8 res0[0x4]; - __be32 qiprta; - __be32 qiprtb; - u8 res1[0x4]; - __be32 qricr; - u8 res2[0x20]; - __be32 qhivec; - u8 res3[0x1C]; -} __attribute__ ((packed)); - -/* Communications Processor */ -struct cp_qe { - __be32 cecr; /* QE command register */ - __be32 ceccr; /* QE controller configuration register */ - __be32 cecdr; /* QE command data register */ - u8 res0[0xA]; - __be16 ceter; /* QE timer event register */ - u8 res1[0x2]; - __be16 cetmr; /* QE timers mask register */ - __be32 cetscr; /* QE time-stamp timer control register */ - __be32 cetsr1; /* QE time-stamp register 1 */ - __be32 cetsr2; /* QE time-stamp register 2 */ - u8 res2[0x8]; - __be32 cevter; /* QE virtual tasks event register */ - __be32 cevtmr; /* QE virtual tasks mask register */ - __be16 cercr; /* QE RAM control register */ - u8 res3[0x2]; - u8 res4[0x24]; - __be16 ceexe1; /* QE external request 1 event register */ - u8 res5[0x2]; - __be16 ceexm1; /* QE external request 1 mask register */ - u8 res6[0x2]; - __be16 ceexe2; /* QE external request 2 event register */ - u8 res7[0x2]; - __be16 ceexm2; /* QE external request 2 mask register */ - u8 res8[0x2]; - __be16 ceexe3; /* QE external request 3 event register */ - u8 res9[0x2]; - __be16 ceexm3; /* QE external request 3 mask register */ - u8 res10[0x2]; - __be16 ceexe4; /* QE external request 4 event register */ - u8 res11[0x2]; - __be16 ceexm4; /* QE external request 4 mask register */ - u8 res12[0x3A]; - __be32 ceurnr; /* QE microcode revision number register */ - u8 res13[0x244]; -} __attribute__ ((packed)); - -/* QE Multiplexer */ -struct qe_mux { - __be32 cmxgcr; /* CMX general clock route register */ - __be32 cmxsi1cr_l; /* CMX SI1 clock route low register */ - __be32 cmxsi1cr_h; /* CMX SI1 clock route high register */ - __be32 cmxsi1syr; /* CMX SI1 SYNC route register */ - __be32 cmxucr[4]; /* CMX UCCx clock route registers */ - __be32 cmxupcr; /* CMX UPC clock route register */ - u8 res0[0x1C]; -} __attribute__ ((packed)); - -/* QE Timers */ -struct qe_timers { - u8 gtcfr1; /* Timer 1 and Timer 2 global config register*/ - u8 res0[0x3]; - u8 gtcfr2; /* Timer 3 and timer 4 global config register*/ - u8 res1[0xB]; - __be16 gtmdr1; /* Timer 1 mode register */ - __be16 gtmdr2; /* Timer 2 mode register */ - __be16 gtrfr1; /* Timer 1 reference register */ - __be16 gtrfr2; /* Timer 2 reference register */ - __be16 gtcpr1; /* Timer 1 capture register */ - __be16 gtcpr2; /* Timer 2 capture register */ - __be16 gtcnr1; /* Timer 1 counter */ - __be16 gtcnr2; /* Timer 2 counter */ - __be16 gtmdr3; /* Timer 3 mode register */ - __be16 gtmdr4; /* Timer 4 mode register */ - __be16 gtrfr3; /* Timer 3 reference register */ - __be16 gtrfr4; /* Timer 4 reference register */ - __be16 gtcpr3; /* Timer 3 capture register */ - __be16 gtcpr4; /* Timer 4 capture register */ - __be16 gtcnr3; /* Timer 3 counter */ - __be16 gtcnr4; /* Timer 4 counter */ - __be16 gtevr1; /* Timer 1 event register */ - __be16 gtevr2; /* Timer 2 event register */ - __be16 gtevr3; /* Timer 3 event register */ - __be16 gtevr4; /* Timer 4 event register */ - __be16 gtps; /* Timer 1 prescale register */ - u8 res2[0x46]; -} __attribute__ ((packed)); - -/* BRG */ -struct qe_brg { - __be32 brgc[16]; /* BRG configuration registers */ - u8 res0[0x40]; -} __attribute__ ((packed)); - -/* SPI */ -struct spi { - u8 res0[0x20]; - __be32 spmode; /* SPI mode register */ - u8 res1[0x2]; - u8 spie; /* SPI event register */ - u8 res2[0x1]; - u8 res3[0x2]; - u8 spim; /* SPI mask register */ - u8 res4[0x1]; - u8 res5[0x1]; - u8 spcom; /* SPI command register */ - u8 res6[0x2]; - __be32 spitd; /* SPI transmit data register (cpu mode) */ - __be32 spird; /* SPI receive data register (cpu mode) */ - u8 res7[0x8]; -} __attribute__ ((packed)); - -/* SI */ -struct si1 { - __be16 sixmr1[4]; /* SI1 TDMx (x = A B C D) mode register */ - u8 siglmr1_h; /* SI1 global mode register high */ - u8 res0[0x1]; - u8 sicmdr1_h; /* SI1 command register high */ - u8 res2[0x1]; - u8 sistr1_h; /* SI1 status register high */ - u8 res3[0x1]; - __be16 sirsr1_h; /* SI1 RAM shadow address register high */ - u8 sitarc1; /* SI1 RAM counter Tx TDMA */ - u8 sitbrc1; /* SI1 RAM counter Tx TDMB */ - u8 sitcrc1; /* SI1 RAM counter Tx TDMC */ - u8 sitdrc1; /* SI1 RAM counter Tx TDMD */ - u8 sirarc1; /* SI1 RAM counter Rx TDMA */ - u8 sirbrc1; /* SI1 RAM counter Rx TDMB */ - u8 sircrc1; /* SI1 RAM counter Rx TDMC */ - u8 sirdrc1; /* SI1 RAM counter Rx TDMD */ - u8 res4[0x8]; - __be16 siemr1; /* SI1 TDME mode register 16 bits */ - __be16 sifmr1; /* SI1 TDMF mode register 16 bits */ - __be16 sigmr1; /* SI1 TDMG mode register 16 bits */ - __be16 sihmr1; /* SI1 TDMH mode register 16 bits */ - u8 siglmg1_l; /* SI1 global mode register low 8 bits */ - u8 res5[0x1]; - u8 sicmdr1_l; /* SI1 command register low 8 bits */ - u8 res6[0x1]; - u8 sistr1_l; /* SI1 status register low 8 bits */ - u8 res7[0x1]; - __be16 sirsr1_l; /* SI1 RAM shadow address register low 16 bits*/ - u8 siterc1; /* SI1 RAM counter Tx TDME 8 bits */ - u8 sitfrc1; /* SI1 RAM counter Tx TDMF 8 bits */ - u8 sitgrc1; /* SI1 RAM counter Tx TDMG 8 bits */ - u8 sithrc1; /* SI1 RAM counter Tx TDMH 8 bits */ - u8 sirerc1; /* SI1 RAM counter Rx TDME 8 bits */ - u8 sirfrc1; /* SI1 RAM counter Rx TDMF 8 bits */ - u8 sirgrc1; /* SI1 RAM counter Rx TDMG 8 bits */ - u8 sirhrc1; /* SI1 RAM counter Rx TDMH 8 bits */ - u8 res8[0x8]; - __be32 siml1; /* SI1 multiframe limit register */ - u8 siedm1; /* SI1 extended diagnostic mode register */ - u8 res9[0xBB]; -} __attribute__ ((packed)); - -/* SI Routing Tables */ -struct sir { - u8 tx[0x400]; - u8 rx[0x400]; - u8 res0[0x800]; -} __attribute__ ((packed)); - -/* USB Controller */ -struct qe_usb_ctlr { - u8 usb_usmod; - u8 usb_usadr; - u8 usb_uscom; - u8 res1[1]; - __be16 usb_usep[4]; - u8 res2[4]; - __be16 usb_usber; - u8 res3[2]; - __be16 usb_usbmr; - u8 res4[1]; - u8 usb_usbs; - __be16 usb_ussft; - u8 res5[2]; - __be16 usb_usfrn; - u8 res6[0x22]; -} __attribute__ ((packed)); - -/* MCC */ -struct qe_mcc { - __be32 mcce; /* MCC event register */ - __be32 mccm; /* MCC mask register */ - __be32 mccf; /* MCC configuration register */ - __be32 merl; /* MCC emergency request level register */ - u8 res0[0xF0]; -} __attribute__ ((packed)); - -/* QE UCC Slow */ -struct ucc_slow { - __be32 gumr_l; /* UCCx general mode register (low) */ - __be32 gumr_h; /* UCCx general mode register (high) */ - __be16 upsmr; /* UCCx protocol-specific mode register */ - u8 res0[0x2]; - __be16 utodr; /* UCCx transmit on demand register */ - __be16 udsr; /* UCCx data synchronization register */ - __be16 ucce; /* UCCx event register */ - u8 res1[0x2]; - __be16 uccm; /* UCCx mask register */ - u8 res2[0x1]; - u8 uccs; /* UCCx status register */ - u8 res3[0x24]; - __be16 utpt; - u8 res4[0x52]; - u8 guemr; /* UCC general extended mode register */ -} __attribute__ ((packed)); - -/* QE UCC Fast */ -struct ucc_fast { - __be32 gumr; /* UCCx general mode register */ - __be32 upsmr; /* UCCx protocol-specific mode register */ - __be16 utodr; /* UCCx transmit on demand register */ - u8 res0[0x2]; - __be16 udsr; /* UCCx data synchronization register */ - u8 res1[0x2]; - __be32 ucce; /* UCCx event register */ - __be32 uccm; /* UCCx mask register */ - u8 uccs; /* UCCx status register */ - u8 res2[0x7]; - __be32 urfb; /* UCC receive FIFO base */ - __be16 urfs; /* UCC receive FIFO size */ - u8 res3[0x2]; - __be16 urfet; /* UCC receive FIFO emergency threshold */ - __be16 urfset; /* UCC receive FIFO special emergency - threshold */ - __be32 utfb; /* UCC transmit FIFO base */ - __be16 utfs; /* UCC transmit FIFO size */ - u8 res4[0x2]; - __be16 utfet; /* UCC transmit FIFO emergency threshold */ - u8 res5[0x2]; - __be16 utftt; /* UCC transmit FIFO transmit threshold */ - u8 res6[0x2]; - __be16 utpt; /* UCC transmit polling timer */ - u8 res7[0x2]; - __be32 urtry; /* UCC retry counter register */ - u8 res8[0x4C]; - u8 guemr; /* UCC general extended mode register */ -} __attribute__ ((packed)); - -struct ucc { - union { - struct ucc_slow slow; - struct ucc_fast fast; - u8 res[0x200]; /* UCC blocks are 512 bytes each */ - }; -} __attribute__ ((packed)); - -/* MultiPHY UTOPIA POS Controllers (UPC) */ -struct upc { - __be32 upgcr; /* UTOPIA/POS general configuration register */ - __be32 uplpa; /* UTOPIA/POS last PHY address */ - __be32 uphec; /* ATM HEC register */ - __be32 upuc; /* UTOPIA/POS UCC configuration */ - __be32 updc1; /* UTOPIA/POS device 1 configuration */ - __be32 updc2; /* UTOPIA/POS device 2 configuration */ - __be32 updc3; /* UTOPIA/POS device 3 configuration */ - __be32 updc4; /* UTOPIA/POS device 4 configuration */ - __be32 upstpa; /* UTOPIA/POS STPA threshold */ - u8 res0[0xC]; - __be32 updrs1_h; /* UTOPIA/POS device 1 rate select */ - __be32 updrs1_l; /* UTOPIA/POS device 1 rate select */ - __be32 updrs2_h; /* UTOPIA/POS device 2 rate select */ - __be32 updrs2_l; /* UTOPIA/POS device 2 rate select */ - __be32 updrs3_h; /* UTOPIA/POS device 3 rate select */ - __be32 updrs3_l; /* UTOPIA/POS device 3 rate select */ - __be32 updrs4_h; /* UTOPIA/POS device 4 rate select */ - __be32 updrs4_l; /* UTOPIA/POS device 4 rate select */ - __be32 updrp1; /* UTOPIA/POS device 1 receive priority low */ - __be32 updrp2; /* UTOPIA/POS device 2 receive priority low */ - __be32 updrp3; /* UTOPIA/POS device 3 receive priority low */ - __be32 updrp4; /* UTOPIA/POS device 4 receive priority low */ - __be32 upde1; /* UTOPIA/POS device 1 event */ - __be32 upde2; /* UTOPIA/POS device 2 event */ - __be32 upde3; /* UTOPIA/POS device 3 event */ - __be32 upde4; /* UTOPIA/POS device 4 event */ - __be16 uprp1; - __be16 uprp2; - __be16 uprp3; - __be16 uprp4; - u8 res1[0x8]; - __be16 uptirr1_0; /* Device 1 transmit internal rate 0 */ - __be16 uptirr1_1; /* Device 1 transmit internal rate 1 */ - __be16 uptirr1_2; /* Device 1 transmit internal rate 2 */ - __be16 uptirr1_3; /* Device 1 transmit internal rate 3 */ - __be16 uptirr2_0; /* Device 2 transmit internal rate 0 */ - __be16 uptirr2_1; /* Device 2 transmit internal rate 1 */ - __be16 uptirr2_2; /* Device 2 transmit internal rate 2 */ - __be16 uptirr2_3; /* Device 2 transmit internal rate 3 */ - __be16 uptirr3_0; /* Device 3 transmit internal rate 0 */ - __be16 uptirr3_1; /* Device 3 transmit internal rate 1 */ - __be16 uptirr3_2; /* Device 3 transmit internal rate 2 */ - __be16 uptirr3_3; /* Device 3 transmit internal rate 3 */ - __be16 uptirr4_0; /* Device 4 transmit internal rate 0 */ - __be16 uptirr4_1; /* Device 4 transmit internal rate 1 */ - __be16 uptirr4_2; /* Device 4 transmit internal rate 2 */ - __be16 uptirr4_3; /* Device 4 transmit internal rate 3 */ - __be32 uper1; /* Device 1 port enable register */ - __be32 uper2; /* Device 2 port enable register */ - __be32 uper3; /* Device 3 port enable register */ - __be32 uper4; /* Device 4 port enable register */ - u8 res2[0x150]; -} __attribute__ ((packed)); - -/* SDMA */ -struct sdma { - __be32 sdsr; /* Serial DMA status register */ - __be32 sdmr; /* Serial DMA mode register */ - __be32 sdtr1; /* SDMA system bus threshold register */ - __be32 sdtr2; /* SDMA secondary bus threshold register */ - __be32 sdhy1; /* SDMA system bus hysteresis register */ - __be32 sdhy2; /* SDMA secondary bus hysteresis register */ - __be32 sdta1; /* SDMA system bus address register */ - __be32 sdta2; /* SDMA secondary bus address register */ - __be32 sdtm1; /* SDMA system bus MSNUM register */ - __be32 sdtm2; /* SDMA secondary bus MSNUM register */ - u8 res0[0x10]; - __be32 sdaqr; /* SDMA address bus qualify register */ - __be32 sdaqmr; /* SDMA address bus qualify mask register */ - u8 res1[0x4]; - __be32 sdebcr; /* SDMA CAM entries base register */ - u8 res2[0x38]; -} __attribute__ ((packed)); - -/* Debug Space */ -struct dbg { - __be32 bpdcr; /* Breakpoint debug command register */ - __be32 bpdsr; /* Breakpoint debug status register */ - __be32 bpdmr; /* Breakpoint debug mask register */ - __be32 bprmrr0; /* Breakpoint request mode risc register 0 */ - __be32 bprmrr1; /* Breakpoint request mode risc register 1 */ - u8 res0[0x8]; - __be32 bprmtr0; /* Breakpoint request mode trb register 0 */ - __be32 bprmtr1; /* Breakpoint request mode trb register 1 */ - u8 res1[0x8]; - __be32 bprmir; /* Breakpoint request mode immediate register */ - __be32 bprmsr; /* Breakpoint request mode serial register */ - __be32 bpemr; /* Breakpoint exit mode register */ - u8 res2[0x48]; -} __attribute__ ((packed)); - -/* - * RISC Special Registers (Trap and Breakpoint). These are described in - * the QE Developer's Handbook. - */ -struct rsp { - __be32 tibcr[16]; /* Trap/instruction breakpoint control regs */ - u8 res0[64]; - __be32 ibcr0; - __be32 ibs0; - __be32 ibcnr0; - u8 res1[4]; - __be32 ibcr1; - __be32 ibs1; - __be32 ibcnr1; - __be32 npcr; - __be32 dbcr; - __be32 dbar; - __be32 dbamr; - __be32 dbsr; - __be32 dbcnr; - u8 res2[12]; - __be32 dbdr_h; - __be32 dbdr_l; - __be32 dbdmr_h; - __be32 dbdmr_l; - __be32 bsr; - __be32 bor; - __be32 bior; - u8 res3[4]; - __be32 iatr[4]; - __be32 eccr; /* Exception control configuration register */ - __be32 eicr; - u8 res4[0x100-0xf8]; -} __attribute__ ((packed)); - -struct qe_immap { - struct qe_iram iram; /* I-RAM */ - struct qe_ic_regs ic; /* Interrupt Controller */ - struct cp_qe cp; /* Communications Processor */ - struct qe_mux qmx; /* QE Multiplexer */ - struct qe_timers qet; /* QE Timers */ - struct spi spi[0x2]; /* spi */ - struct qe_mcc mcc; /* mcc */ - struct qe_brg brg; /* brg */ - struct qe_usb_ctlr usb; /* USB */ - struct si1 si1; /* SI */ - u8 res11[0x800]; - struct sir sir; /* SI Routing Tables */ - struct ucc ucc1; /* ucc1 */ - struct ucc ucc3; /* ucc3 */ - struct ucc ucc5; /* ucc5 */ - struct ucc ucc7; /* ucc7 */ - u8 res12[0x600]; - struct upc upc1; /* MultiPHY UTOPIA POS Ctrlr 1*/ - struct ucc ucc2; /* ucc2 */ - struct ucc ucc4; /* ucc4 */ - struct ucc ucc6; /* ucc6 */ - struct ucc ucc8; /* ucc8 */ - u8 res13[0x600]; - struct upc upc2; /* MultiPHY UTOPIA POS Ctrlr 2*/ - struct sdma sdma; /* SDMA */ - struct dbg dbg; /* 0x104080 - 0x1040FF - Debug Space */ - struct rsp rsp[0x2]; /* 0x104100 - 0x1042FF - RISC Special Registers - (Trap and Breakpoint) */ - u8 res14[0x300]; /* 0x104300 - 0x1045FF */ - u8 res15[0x3A00]; /* 0x104600 - 0x107FFF */ - u8 res16[0x8000]; /* 0x108000 - 0x110000 */ - u8 muram[0xC000]; /* 0x110000 - 0x11C000 - Multi-user RAM */ - u8 res17[0x24000]; /* 0x11C000 - 0x140000 */ - u8 res18[0xC0000]; /* 0x140000 - 0x200000 */ -} __attribute__ ((packed)); - -extern struct qe_immap __iomem *qe_immr; -extern phys_addr_t get_qe_base(void); - -/* - * Returns the offset within the QE address space of the given pointer. - * - * Note that the QE does not support 36-bit physical addresses, so if - * get_qe_base() returns a number above 4GB, the caller will probably fail. - */ -static inline phys_addr_t immrbar_virt_to_phys(void *address) -{ - void *q = (void *)qe_immr; - - /* Is it a MURAM address? */ - if ((address >= q) && (address < (q + QE_IMMAP_SIZE))) - return get_qe_base() + (address - q); - - /* It's an address returned by kmalloc */ - return virt_to_phys(address); -} - -#endif /* __KERNEL__ */ -#endif /* _ASM_POWERPC_IMMAP_QE_H */ diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h deleted file mode 100644 index a7387c2..0000000 --- a/arch/powerpc/include/asm/qe.h +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QUICC Engine (QE) external definitions and structure. - * - * 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. - */ -#ifndef _ASM_POWERPC_QE_H -#define _ASM_POWERPC_QE_H -#ifdef __KERNEL__ - -#include -#include -#include -#include -#include - -#define QE_NUM_OF_SNUM 256 /* There are 256 serial number in QE */ -#define QE_NUM_OF_BRGS 16 -#define QE_NUM_OF_PORTS 1024 - -/* Memory partitions -*/ -#define MEM_PART_SYSTEM 0 -#define MEM_PART_SECONDARY 1 -#define MEM_PART_MURAM 2 - -extern int siram_init_flag; - -/* Clocks and BRGs */ -enum qe_clock { - QE_CLK_NONE = 0, - QE_BRG1, /* Baud Rate Generator 1 */ - QE_BRG2, /* Baud Rate Generator 2 */ - QE_BRG3, /* Baud Rate Generator 3 */ - QE_BRG4, /* Baud Rate Generator 4 */ - QE_BRG5, /* Baud Rate Generator 5 */ - QE_BRG6, /* Baud Rate Generator 6 */ - QE_BRG7, /* Baud Rate Generator 7 */ - QE_BRG8, /* Baud Rate Generator 8 */ - QE_BRG9, /* Baud Rate Generator 9 */ - QE_BRG10, /* Baud Rate Generator 10 */ - QE_BRG11, /* Baud Rate Generator 11 */ - QE_BRG12, /* Baud Rate Generator 12 */ - QE_BRG13, /* Baud Rate Generator 13 */ - QE_BRG14, /* Baud Rate Generator 14 */ - QE_BRG15, /* Baud Rate Generator 15 */ - QE_BRG16, /* Baud Rate Generator 16 */ - QE_CLK1, /* Clock 1 */ - QE_CLK2, /* Clock 2 */ - QE_CLK3, /* Clock 3 */ - QE_CLK4, /* Clock 4 */ - QE_CLK5, /* Clock 5 */ - QE_CLK6, /* Clock 6 */ - QE_CLK7, /* Clock 7 */ - QE_CLK8, /* Clock 8 */ - QE_CLK9, /* Clock 9 */ - QE_CLK10, /* Clock 10 */ - QE_CLK11, /* Clock 11 */ - QE_CLK12, /* Clock 12 */ - QE_CLK13, /* Clock 13 */ - QE_CLK14, /* Clock 14 */ - QE_CLK15, /* Clock 15 */ - QE_CLK16, /* Clock 16 */ - QE_CLK17, /* Clock 17 */ - QE_CLK18, /* Clock 18 */ - QE_CLK19, /* Clock 19 */ - QE_CLK20, /* Clock 20 */ - QE_CLK21, /* Clock 21 */ - QE_CLK22, /* Clock 22 */ - QE_CLK23, /* Clock 23 */ - QE_CLK24, /* Clock 24 */ - QE_RSYNC_PIN, /* RSYNC from pin */ - QE_TSYNC_PIN, /* TSYNC from pin */ - QE_CLK_DUMMY -}; - -static inline bool qe_clock_is_brg(enum qe_clock clk) -{ - return clk >= QE_BRG1 && clk <= QE_BRG16; -} - -extern spinlock_t cmxgcr_lock; - -/* Export QE common operations */ -#ifdef CONFIG_QUICC_ENGINE -extern void qe_reset(void); -#else -static inline void qe_reset(void) {} -#endif - -/* QE PIO */ -#define QE_PIO_PINS 32 - -struct qe_pio_regs { - __be32 cpodr; /* Open drain register */ - __be32 cpdata; /* Data register */ - __be32 cpdir1; /* Direction register */ - __be32 cpdir2; /* Direction register */ - __be32 cppar1; /* Pin assignment register */ - __be32 cppar2; /* Pin assignment register */ -#ifdef CONFIG_PPC_85xx - u8 pad[8]; -#endif -}; - -#define QE_PIO_DIR_IN 2 -#define QE_PIO_DIR_OUT 1 -extern void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, - int dir, int open_drain, int assignment, - int has_irq); -#ifdef CONFIG_QUICC_ENGINE -extern int par_io_init(struct device_node *np); -extern int par_io_of_config(struct device_node *np); -extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, - int assignment, int has_irq); -extern int par_io_data_set(u8 port, u8 pin, u8 val); -#else -static inline int par_io_init(struct device_node *np) { return -ENOSYS; } -static inline int par_io_of_config(struct device_node *np) { return -ENOSYS; } -static inline int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, - int assignment, int has_irq) { return -ENOSYS; } -static inline int par_io_data_set(u8 port, u8 pin, u8 val) { return -ENOSYS; } -#endif /* CONFIG_QUICC_ENGINE */ - -/* - * Pin multiplexing functions. - */ -struct qe_pin; -#ifdef CONFIG_QE_GPIO -extern struct qe_pin *qe_pin_request(struct device_node *np, int index); -extern void qe_pin_free(struct qe_pin *qe_pin); -extern void qe_pin_set_gpio(struct qe_pin *qe_pin); -extern void qe_pin_set_dedicated(struct qe_pin *pin); -#else -static inline struct qe_pin *qe_pin_request(struct device_node *np, int index) -{ - return ERR_PTR(-ENOSYS); -} -static inline void qe_pin_free(struct qe_pin *qe_pin) {} -static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {} -static inline void qe_pin_set_dedicated(struct qe_pin *pin) {} -#endif /* CONFIG_QE_GPIO */ - -#ifdef CONFIG_QUICC_ENGINE -int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input); -#else -static inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, - u32 cmd_input) -{ - return -ENOSYS; -} -#endif /* CONFIG_QUICC_ENGINE */ - -/* QE internal API */ -enum qe_clock qe_clock_source(const char *source); -unsigned int qe_get_brg_clk(void); -int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier); -int qe_get_snum(void); -void qe_put_snum(u8 snum); -unsigned int qe_get_num_of_risc(void); -unsigned int qe_get_num_of_snums(void); - -static inline int qe_alive_during_sleep(void) -{ - /* - * MPC8568E reference manual says: - * - * "...power down sequence waits for all I/O interfaces to become idle. - * In some applications this may happen eventually without actively - * shutting down interfaces, but most likely, software will have to - * take steps to shut down the eTSEC, QUICC Engine Block, and PCI - * interfaces before issuing the command (either the write to the core - * MSR[WE] as described above or writing to POWMGTCSR) to put the - * device into sleep state." - * - * MPC8569E reference manual has a similar paragraph. - */ -#ifdef CONFIG_PPC_85xx - return 0; -#else - return 1; -#endif -} - -/* we actually use cpm_muram implementation, define this for convenience */ -#define qe_muram_init cpm_muram_init -#define qe_muram_alloc cpm_muram_alloc -#define qe_muram_alloc_fixed cpm_muram_alloc_fixed -#define qe_muram_free cpm_muram_free -#define qe_muram_addr cpm_muram_addr -#define qe_muram_offset cpm_muram_offset - -/* Structure that defines QE firmware binary files. - * - * See Documentation/powerpc/qe_firmware.txt for a description of these - * fields. - */ -struct qe_firmware { - struct qe_header { - __be32 length; /* Length of the entire structure, in bytes */ - u8 magic[3]; /* Set to { 'Q', 'E', 'F' } */ - u8 version; /* Version of this layout. First ver is '1' */ - } header; - u8 id[62]; /* Null-terminated identifier string */ - u8 split; /* 0 = shared I-RAM, 1 = split I-RAM */ - u8 count; /* Number of microcode[] structures */ - struct { - __be16 model; /* The SOC model */ - u8 major; /* The SOC revision major */ - u8 minor; /* The SOC revision minor */ - } __attribute__ ((packed)) soc; - u8 padding[4]; /* Reserved, for alignment */ - __be64 extended_modes; /* Extended modes */ - __be32 vtraps[8]; /* Virtual trap addresses */ - u8 reserved[4]; /* Reserved, for future expansion */ - struct qe_microcode { - u8 id[32]; /* Null-terminated identifier */ - __be32 traps[16]; /* Trap addresses, 0 == ignore */ - __be32 eccr; /* The value for the ECCR register */ - __be32 iram_offset; /* Offset into I-RAM for the code */ - __be32 count; /* Number of 32-bit words of the code */ - __be32 code_offset; /* Offset of the actual microcode */ - u8 major; /* The microcode version major */ - u8 minor; /* The microcode version minor */ - u8 revision; /* The microcode version revision */ - u8 padding; /* Reserved, for alignment */ - u8 reserved[4]; /* Reserved, for future expansion */ - } __attribute__ ((packed)) microcode[1]; - /* All microcode binaries should be located here */ - /* CRC32 should be located here, after the microcode binaries */ -} __attribute__ ((packed)); - -struct qe_firmware_info { - char id[64]; /* Firmware name */ - u32 vtraps[8]; /* Virtual trap addresses */ - u64 extended_modes; /* Extended modes */ -}; - -#ifdef CONFIG_QUICC_ENGINE -/* Upload a firmware to the QE */ -int qe_upload_firmware(const struct qe_firmware *firmware); -#else -static inline int qe_upload_firmware(const struct qe_firmware *firmware) -{ - return -ENOSYS; -} -#endif /* CONFIG_QUICC_ENGINE */ - -/* Obtain information on the uploaded firmware */ -struct qe_firmware_info *qe_get_firmware_info(void); - -/* QE USB */ -int qe_usb_clock_set(enum qe_clock clk, int rate); - -/* Buffer descriptors */ -struct qe_bd { - __be16 status; - __be16 length; - __be32 buf; -} __attribute__ ((packed)); - -#define BD_STATUS_MASK 0xffff0000 -#define BD_LENGTH_MASK 0x0000ffff - -/* Alignment */ -#define QE_INTR_TABLE_ALIGN 16 /* ??? */ -#define QE_ALIGNMENT_OF_BD 8 -#define QE_ALIGNMENT_OF_PRAM 64 - -/* RISC allocation */ -#define QE_RISC_ALLOCATION_RISC1 0x1 /* RISC 1 */ -#define QE_RISC_ALLOCATION_RISC2 0x2 /* RISC 2 */ -#define QE_RISC_ALLOCATION_RISC3 0x4 /* RISC 3 */ -#define QE_RISC_ALLOCATION_RISC4 0x8 /* RISC 4 */ -#define QE_RISC_ALLOCATION_RISC1_AND_RISC2 (QE_RISC_ALLOCATION_RISC1 | \ - QE_RISC_ALLOCATION_RISC2) -#define QE_RISC_ALLOCATION_FOUR_RISCS (QE_RISC_ALLOCATION_RISC1 | \ - QE_RISC_ALLOCATION_RISC2 | \ - QE_RISC_ALLOCATION_RISC3 | \ - QE_RISC_ALLOCATION_RISC4) - -/* QE extended filtering Table Lookup Key Size */ -enum qe_fltr_tbl_lookup_key_size { - QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES - = 0x3f, /* LookupKey parsed by the Generate LookupKey - CMD is truncated to 8 bytes */ - QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES - = 0x5f, /* LookupKey parsed by the Generate LookupKey - CMD is truncated to 16 bytes */ -}; - -/* QE FLTR extended filtering Largest External Table Lookup Key Size */ -enum qe_fltr_largest_external_tbl_lookup_key_size { - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE - = 0x0,/* not used */ - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES - = QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES, /* 8 bytes */ - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES - = QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES, /* 16 bytes */ -}; - -/* structure representing QE parameter RAM */ -struct qe_timer_tables { - u16 tm_base; /* QE timer table base adr */ - u16 tm_ptr; /* QE timer table pointer */ - u16 r_tmr; /* QE timer mode register */ - u16 r_tmv; /* QE timer valid register */ - u32 tm_cmd; /* QE timer cmd register */ - u32 tm_cnt; /* QE timer internal cnt */ -} __attribute__ ((packed)); - -#define QE_FLTR_TAD_SIZE 8 - -/* QE extended filtering Termination Action Descriptor (TAD) */ -struct qe_fltr_tad { - u8 serialized[QE_FLTR_TAD_SIZE]; -} __attribute__ ((packed)); - -/* Communication Direction */ -enum comm_dir { - COMM_DIR_NONE = 0, - COMM_DIR_RX = 1, - COMM_DIR_TX = 2, - COMM_DIR_RX_AND_TX = 3 -}; - -/* QE CMXUCR Registers. - * There are two UCCs represented in each of the four CMXUCR registers. - * These values are for the UCC in the LSBs - */ -#define QE_CMXUCR_MII_ENET_MNG 0x00007000 -#define QE_CMXUCR_MII_ENET_MNG_SHIFT 12 -#define QE_CMXUCR_GRANT 0x00008000 -#define QE_CMXUCR_TSA 0x00004000 -#define QE_CMXUCR_BKPT 0x00000100 -#define QE_CMXUCR_TX_CLK_SRC_MASK 0x0000000F - -/* QE CMXGCR Registers. -*/ -#define QE_CMXGCR_MII_ENET_MNG 0x00007000 -#define QE_CMXGCR_MII_ENET_MNG_SHIFT 12 -#define QE_CMXGCR_USBCS 0x0000000f -#define QE_CMXGCR_USBCS_CLK3 0x1 -#define QE_CMXGCR_USBCS_CLK5 0x2 -#define QE_CMXGCR_USBCS_CLK7 0x3 -#define QE_CMXGCR_USBCS_CLK9 0x4 -#define QE_CMXGCR_USBCS_CLK13 0x5 -#define QE_CMXGCR_USBCS_CLK17 0x6 -#define QE_CMXGCR_USBCS_CLK19 0x7 -#define QE_CMXGCR_USBCS_CLK21 0x8 -#define QE_CMXGCR_USBCS_BRG9 0x9 -#define QE_CMXGCR_USBCS_BRG10 0xa - -/* QE CECR Commands. -*/ -#define QE_CR_FLG 0x00010000 -#define QE_RESET 0x80000000 -#define QE_INIT_TX_RX 0x00000000 -#define QE_INIT_RX 0x00000001 -#define QE_INIT_TX 0x00000002 -#define QE_ENTER_HUNT_MODE 0x00000003 -#define QE_STOP_TX 0x00000004 -#define QE_GRACEFUL_STOP_TX 0x00000005 -#define QE_RESTART_TX 0x00000006 -#define QE_CLOSE_RX_BD 0x00000007 -#define QE_SWITCH_COMMAND 0x00000007 -#define QE_SET_GROUP_ADDRESS 0x00000008 -#define QE_START_IDMA 0x00000009 -#define QE_MCC_STOP_RX 0x00000009 -#define QE_ATM_TRANSMIT 0x0000000a -#define QE_HPAC_CLEAR_ALL 0x0000000b -#define QE_GRACEFUL_STOP_RX 0x0000001a -#define QE_RESTART_RX 0x0000001b -#define QE_HPAC_SET_PRIORITY 0x0000010b -#define QE_HPAC_STOP_TX 0x0000020b -#define QE_HPAC_STOP_RX 0x0000030b -#define QE_HPAC_GRACEFUL_STOP_TX 0x0000040b -#define QE_HPAC_GRACEFUL_STOP_RX 0x0000050b -#define QE_HPAC_START_TX 0x0000060b -#define QE_HPAC_START_RX 0x0000070b -#define QE_USB_STOP_TX 0x0000000a -#define QE_USB_RESTART_TX 0x0000000c -#define QE_QMC_STOP_TX 0x0000000c -#define QE_QMC_STOP_RX 0x0000000d -#define QE_SS7_SU_FIL_RESET 0x0000000e -/* jonathbr added from here down for 83xx */ -#define QE_RESET_BCS 0x0000000a -#define QE_MCC_INIT_TX_RX_16 0x00000003 -#define QE_MCC_STOP_TX 0x00000004 -#define QE_MCC_INIT_TX_1 0x00000005 -#define QE_MCC_INIT_RX_1 0x00000006 -#define QE_MCC_RESET 0x00000007 -#define QE_SET_TIMER 0x00000008 -#define QE_RANDOM_NUMBER 0x0000000c -#define QE_ATM_MULTI_THREAD_INIT 0x00000011 -#define QE_ASSIGN_PAGE 0x00000012 -#define QE_ADD_REMOVE_HASH_ENTRY 0x00000013 -#define QE_START_FLOW_CONTROL 0x00000014 -#define QE_STOP_FLOW_CONTROL 0x00000015 -#define QE_ASSIGN_PAGE_TO_DEVICE 0x00000016 - -#define QE_ASSIGN_RISC 0x00000010 -#define QE_CR_MCN_NORMAL_SHIFT 6 -#define QE_CR_MCN_USB_SHIFT 4 -#define QE_CR_MCN_RISC_ASSIGN_SHIFT 8 -#define QE_CR_SNUM_SHIFT 17 - -/* QE CECR Sub Block - sub block of QE command. -*/ -#define QE_CR_SUBBLOCK_INVALID 0x00000000 -#define QE_CR_SUBBLOCK_USB 0x03200000 -#define QE_CR_SUBBLOCK_UCCFAST1 0x02000000 -#define QE_CR_SUBBLOCK_UCCFAST2 0x02200000 -#define QE_CR_SUBBLOCK_UCCFAST3 0x02400000 -#define QE_CR_SUBBLOCK_UCCFAST4 0x02600000 -#define QE_CR_SUBBLOCK_UCCFAST5 0x02800000 -#define QE_CR_SUBBLOCK_UCCFAST6 0x02a00000 -#define QE_CR_SUBBLOCK_UCCFAST7 0x02c00000 -#define QE_CR_SUBBLOCK_UCCFAST8 0x02e00000 -#define QE_CR_SUBBLOCK_UCCSLOW1 0x00000000 -#define QE_CR_SUBBLOCK_UCCSLOW2 0x00200000 -#define QE_CR_SUBBLOCK_UCCSLOW3 0x00400000 -#define QE_CR_SUBBLOCK_UCCSLOW4 0x00600000 -#define QE_CR_SUBBLOCK_UCCSLOW5 0x00800000 -#define QE_CR_SUBBLOCK_UCCSLOW6 0x00a00000 -#define QE_CR_SUBBLOCK_UCCSLOW7 0x00c00000 -#define QE_CR_SUBBLOCK_UCCSLOW8 0x00e00000 -#define QE_CR_SUBBLOCK_MCC1 0x03800000 -#define QE_CR_SUBBLOCK_MCC2 0x03a00000 -#define QE_CR_SUBBLOCK_MCC3 0x03000000 -#define QE_CR_SUBBLOCK_IDMA1 0x02800000 -#define QE_CR_SUBBLOCK_IDMA2 0x02a00000 -#define QE_CR_SUBBLOCK_IDMA3 0x02c00000 -#define QE_CR_SUBBLOCK_IDMA4 0x02e00000 -#define QE_CR_SUBBLOCK_HPAC 0x01e00000 -#define QE_CR_SUBBLOCK_SPI1 0x01400000 -#define QE_CR_SUBBLOCK_SPI2 0x01600000 -#define QE_CR_SUBBLOCK_RAND 0x01c00000 -#define QE_CR_SUBBLOCK_TIMER 0x01e00000 -#define QE_CR_SUBBLOCK_GENERAL 0x03c00000 - -/* QE CECR Protocol - For non-MCC, specifies mode for QE CECR command */ -#define QE_CR_PROTOCOL_UNSPECIFIED 0x00 /* For all other protocols */ -#define QE_CR_PROTOCOL_HDLC_TRANSPARENT 0x00 -#define QE_CR_PROTOCOL_QMC 0x02 -#define QE_CR_PROTOCOL_UART 0x04 -#define QE_CR_PROTOCOL_ATM_POS 0x0A -#define QE_CR_PROTOCOL_ETHERNET 0x0C -#define QE_CR_PROTOCOL_L2_SWITCH 0x0D - -/* BRG configuration register */ -#define QE_BRGC_ENABLE 0x00010000 -#define QE_BRGC_DIVISOR_SHIFT 1 -#define QE_BRGC_DIVISOR_MAX 0xFFF -#define QE_BRGC_DIV16 1 - -/* QE Timers registers */ -#define QE_GTCFR1_PCAS 0x80 -#define QE_GTCFR1_STP2 0x20 -#define QE_GTCFR1_RST2 0x10 -#define QE_GTCFR1_GM2 0x08 -#define QE_GTCFR1_GM1 0x04 -#define QE_GTCFR1_STP1 0x02 -#define QE_GTCFR1_RST1 0x01 - -/* SDMA registers */ -#define QE_SDSR_BER1 0x02000000 -#define QE_SDSR_BER2 0x01000000 - -#define QE_SDMR_GLB_1_MSK 0x80000000 -#define QE_SDMR_ADR_SEL 0x20000000 -#define QE_SDMR_BER1_MSK 0x02000000 -#define QE_SDMR_BER2_MSK 0x01000000 -#define QE_SDMR_EB1_MSK 0x00800000 -#define QE_SDMR_ER1_MSK 0x00080000 -#define QE_SDMR_ER2_MSK 0x00040000 -#define QE_SDMR_CEN_MASK 0x0000E000 -#define QE_SDMR_SBER_1 0x00000200 -#define QE_SDMR_SBER_2 0x00000200 -#define QE_SDMR_EB1_PR_MASK 0x000000C0 -#define QE_SDMR_ER1_PR 0x00000008 - -#define QE_SDMR_CEN_SHIFT 13 -#define QE_SDMR_EB1_PR_SHIFT 6 - -#define QE_SDTM_MSNUM_SHIFT 24 - -#define QE_SDEBCR_BA_MASK 0x01FFFFFF - -/* Communication Processor */ -#define QE_CP_CERCR_MEE 0x8000 /* Multi-user RAM ECC enable */ -#define QE_CP_CERCR_IEE 0x4000 /* Instruction RAM ECC enable */ -#define QE_CP_CERCR_CIR 0x0800 /* Common instruction RAM */ - -/* I-RAM */ -#define QE_IRAM_IADD_AIE 0x80000000 /* Auto Increment Enable */ -#define QE_IRAM_IADD_BADDR 0x00080000 /* Base Address */ -#define QE_IRAM_READY 0x80000000 /* Ready */ - -/* UPC */ -#define UPGCR_PROTOCOL 0x80000000 /* protocol ul2 or pl2 */ -#define UPGCR_TMS 0x40000000 /* Transmit master/slave mode */ -#define UPGCR_RMS 0x20000000 /* Receive master/slave mode */ -#define UPGCR_ADDR 0x10000000 /* Master MPHY Addr multiplexing */ -#define UPGCR_DIAG 0x01000000 /* Diagnostic mode */ - -/* UCC GUEMR register */ -#define UCC_GUEMR_MODE_MASK_RX 0x02 -#define UCC_GUEMR_MODE_FAST_RX 0x02 -#define UCC_GUEMR_MODE_SLOW_RX 0x00 -#define UCC_GUEMR_MODE_MASK_TX 0x01 -#define UCC_GUEMR_MODE_FAST_TX 0x01 -#define UCC_GUEMR_MODE_SLOW_TX 0x00 -#define UCC_GUEMR_MODE_MASK (UCC_GUEMR_MODE_MASK_RX | UCC_GUEMR_MODE_MASK_TX) -#define UCC_GUEMR_SET_RESERVED3 0x10 /* Bit 3 in the guemr is reserved but - must be set 1 */ - -/* structure representing UCC SLOW parameter RAM */ -struct ucc_slow_pram { - __be16 rbase; /* RX BD base address */ - __be16 tbase; /* TX BD base address */ - u8 rbmr; /* RX bus mode register (same as CPM's RFCR) */ - u8 tbmr; /* TX bus mode register (same as CPM's TFCR) */ - __be16 mrblr; /* Rx buffer length */ - __be32 rstate; /* Rx internal state */ - __be32 rptr; /* Rx internal data pointer */ - __be16 rbptr; /* rb BD Pointer */ - __be16 rcount; /* Rx internal byte count */ - __be32 rtemp; /* Rx temp */ - __be32 tstate; /* Tx internal state */ - __be32 tptr; /* Tx internal data pointer */ - __be16 tbptr; /* Tx BD pointer */ - __be16 tcount; /* Tx byte count */ - __be32 ttemp; /* Tx temp */ - __be32 rcrc; /* temp receive CRC */ - __be32 tcrc; /* temp transmit CRC */ -} __attribute__ ((packed)); - -/* General UCC SLOW Mode Register (GUMRH & GUMRL) */ -#define UCC_SLOW_GUMR_H_SAM_QMC 0x00000000 -#define UCC_SLOW_GUMR_H_SAM_SATM 0x00008000 -#define UCC_SLOW_GUMR_H_REVD 0x00002000 -#define UCC_SLOW_GUMR_H_TRX 0x00001000 -#define UCC_SLOW_GUMR_H_TTX 0x00000800 -#define UCC_SLOW_GUMR_H_CDP 0x00000400 -#define UCC_SLOW_GUMR_H_CTSP 0x00000200 -#define UCC_SLOW_GUMR_H_CDS 0x00000100 -#define UCC_SLOW_GUMR_H_CTSS 0x00000080 -#define UCC_SLOW_GUMR_H_TFL 0x00000040 -#define UCC_SLOW_GUMR_H_RFW 0x00000020 -#define UCC_SLOW_GUMR_H_TXSY 0x00000010 -#define UCC_SLOW_GUMR_H_4SYNC 0x00000004 -#define UCC_SLOW_GUMR_H_8SYNC 0x00000008 -#define UCC_SLOW_GUMR_H_16SYNC 0x0000000c -#define UCC_SLOW_GUMR_H_RTSM 0x00000002 -#define UCC_SLOW_GUMR_H_RSYN 0x00000001 - -#define UCC_SLOW_GUMR_L_TCI 0x10000000 -#define UCC_SLOW_GUMR_L_RINV 0x02000000 -#define UCC_SLOW_GUMR_L_TINV 0x01000000 -#define UCC_SLOW_GUMR_L_TEND 0x00040000 -#define UCC_SLOW_GUMR_L_TDCR_MASK 0x00030000 -#define UCC_SLOW_GUMR_L_TDCR_32 0x00030000 -#define UCC_SLOW_GUMR_L_TDCR_16 0x00020000 -#define UCC_SLOW_GUMR_L_TDCR_8 0x00010000 -#define UCC_SLOW_GUMR_L_TDCR_1 0x00000000 -#define UCC_SLOW_GUMR_L_RDCR_MASK 0x0000c000 -#define UCC_SLOW_GUMR_L_RDCR_32 0x0000c000 -#define UCC_SLOW_GUMR_L_RDCR_16 0x00008000 -#define UCC_SLOW_GUMR_L_RDCR_8 0x00004000 -#define UCC_SLOW_GUMR_L_RDCR_1 0x00000000 -#define UCC_SLOW_GUMR_L_RENC_NRZI 0x00000800 -#define UCC_SLOW_GUMR_L_RENC_NRZ 0x00000000 -#define UCC_SLOW_GUMR_L_TENC_NRZI 0x00000100 -#define UCC_SLOW_GUMR_L_TENC_NRZ 0x00000000 -#define UCC_SLOW_GUMR_L_DIAG_MASK 0x000000c0 -#define UCC_SLOW_GUMR_L_DIAG_LE 0x000000c0 -#define UCC_SLOW_GUMR_L_DIAG_ECHO 0x00000080 -#define UCC_SLOW_GUMR_L_DIAG_LOOP 0x00000040 -#define UCC_SLOW_GUMR_L_DIAG_NORM 0x00000000 -#define UCC_SLOW_GUMR_L_ENR 0x00000020 -#define UCC_SLOW_GUMR_L_ENT 0x00000010 -#define UCC_SLOW_GUMR_L_MODE_MASK 0x0000000F -#define UCC_SLOW_GUMR_L_MODE_BISYNC 0x00000008 -#define UCC_SLOW_GUMR_L_MODE_AHDLC 0x00000006 -#define UCC_SLOW_GUMR_L_MODE_UART 0x00000004 -#define UCC_SLOW_GUMR_L_MODE_QMC 0x00000002 - -/* General UCC FAST Mode Register */ -#define UCC_FAST_GUMR_TCI 0x20000000 -#define UCC_FAST_GUMR_TRX 0x10000000 -#define UCC_FAST_GUMR_TTX 0x08000000 -#define UCC_FAST_GUMR_CDP 0x04000000 -#define UCC_FAST_GUMR_CTSP 0x02000000 -#define UCC_FAST_GUMR_CDS 0x01000000 -#define UCC_FAST_GUMR_CTSS 0x00800000 -#define UCC_FAST_GUMR_TXSY 0x00020000 -#define UCC_FAST_GUMR_RSYN 0x00010000 -#define UCC_FAST_GUMR_RTSM 0x00002000 -#define UCC_FAST_GUMR_REVD 0x00000400 -#define UCC_FAST_GUMR_ENR 0x00000020 -#define UCC_FAST_GUMR_ENT 0x00000010 - -/* UART Slow UCC Event Register (UCCE) */ -#define UCC_UART_UCCE_AB 0x0200 -#define UCC_UART_UCCE_IDLE 0x0100 -#define UCC_UART_UCCE_GRA 0x0080 -#define UCC_UART_UCCE_BRKE 0x0040 -#define UCC_UART_UCCE_BRKS 0x0020 -#define UCC_UART_UCCE_CCR 0x0008 -#define UCC_UART_UCCE_BSY 0x0004 -#define UCC_UART_UCCE_TX 0x0002 -#define UCC_UART_UCCE_RX 0x0001 - -/* HDLC Slow UCC Event Register (UCCE) */ -#define UCC_HDLC_UCCE_GLR 0x1000 -#define UCC_HDLC_UCCE_GLT 0x0800 -#define UCC_HDLC_UCCE_IDLE 0x0100 -#define UCC_HDLC_UCCE_BRKE 0x0040 -#define UCC_HDLC_UCCE_BRKS 0x0020 -#define UCC_HDLC_UCCE_TXE 0x0010 -#define UCC_HDLC_UCCE_RXF 0x0008 -#define UCC_HDLC_UCCE_BSY 0x0004 -#define UCC_HDLC_UCCE_TXB 0x0002 -#define UCC_HDLC_UCCE_RXB 0x0001 - -/* BISYNC Slow UCC Event Register (UCCE) */ -#define UCC_BISYNC_UCCE_GRA 0x0080 -#define UCC_BISYNC_UCCE_TXE 0x0010 -#define UCC_BISYNC_UCCE_RCH 0x0008 -#define UCC_BISYNC_UCCE_BSY 0x0004 -#define UCC_BISYNC_UCCE_TXB 0x0002 -#define UCC_BISYNC_UCCE_RXB 0x0001 - -/* Transparent UCC Event Register (UCCE) */ -#define UCC_TRANS_UCCE_GRA 0x0080 -#define UCC_TRANS_UCCE_TXE 0x0010 -#define UCC_TRANS_UCCE_RXF 0x0008 -#define UCC_TRANS_UCCE_BSY 0x0004 -#define UCC_TRANS_UCCE_TXB 0x0002 -#define UCC_TRANS_UCCE_RXB 0x0001 - - -/* Gigabit Ethernet Fast UCC Event Register (UCCE) */ -#define UCC_GETH_UCCE_MPD 0x80000000 -#define UCC_GETH_UCCE_SCAR 0x40000000 -#define UCC_GETH_UCCE_GRA 0x20000000 -#define UCC_GETH_UCCE_CBPR 0x10000000 -#define UCC_GETH_UCCE_BSY 0x08000000 -#define UCC_GETH_UCCE_RXC 0x04000000 -#define UCC_GETH_UCCE_TXC 0x02000000 -#define UCC_GETH_UCCE_TXE 0x01000000 -#define UCC_GETH_UCCE_TXB7 0x00800000 -#define UCC_GETH_UCCE_TXB6 0x00400000 -#define UCC_GETH_UCCE_TXB5 0x00200000 -#define UCC_GETH_UCCE_TXB4 0x00100000 -#define UCC_GETH_UCCE_TXB3 0x00080000 -#define UCC_GETH_UCCE_TXB2 0x00040000 -#define UCC_GETH_UCCE_TXB1 0x00020000 -#define UCC_GETH_UCCE_TXB0 0x00010000 -#define UCC_GETH_UCCE_RXB7 0x00008000 -#define UCC_GETH_UCCE_RXB6 0x00004000 -#define UCC_GETH_UCCE_RXB5 0x00002000 -#define UCC_GETH_UCCE_RXB4 0x00001000 -#define UCC_GETH_UCCE_RXB3 0x00000800 -#define UCC_GETH_UCCE_RXB2 0x00000400 -#define UCC_GETH_UCCE_RXB1 0x00000200 -#define UCC_GETH_UCCE_RXB0 0x00000100 -#define UCC_GETH_UCCE_RXF7 0x00000080 -#define UCC_GETH_UCCE_RXF6 0x00000040 -#define UCC_GETH_UCCE_RXF5 0x00000020 -#define UCC_GETH_UCCE_RXF4 0x00000010 -#define UCC_GETH_UCCE_RXF3 0x00000008 -#define UCC_GETH_UCCE_RXF2 0x00000004 -#define UCC_GETH_UCCE_RXF1 0x00000002 -#define UCC_GETH_UCCE_RXF0 0x00000001 - -/* UCC Protocol Specific Mode Register (UPSMR), when used for UART */ -#define UCC_UART_UPSMR_FLC 0x8000 -#define UCC_UART_UPSMR_SL 0x4000 -#define UCC_UART_UPSMR_CL_MASK 0x3000 -#define UCC_UART_UPSMR_CL_8 0x3000 -#define UCC_UART_UPSMR_CL_7 0x2000 -#define UCC_UART_UPSMR_CL_6 0x1000 -#define UCC_UART_UPSMR_CL_5 0x0000 -#define UCC_UART_UPSMR_UM_MASK 0x0c00 -#define UCC_UART_UPSMR_UM_NORMAL 0x0000 -#define UCC_UART_UPSMR_UM_MAN_MULTI 0x0400 -#define UCC_UART_UPSMR_UM_AUTO_MULTI 0x0c00 -#define UCC_UART_UPSMR_FRZ 0x0200 -#define UCC_UART_UPSMR_RZS 0x0100 -#define UCC_UART_UPSMR_SYN 0x0080 -#define UCC_UART_UPSMR_DRT 0x0040 -#define UCC_UART_UPSMR_PEN 0x0010 -#define UCC_UART_UPSMR_RPM_MASK 0x000c -#define UCC_UART_UPSMR_RPM_ODD 0x0000 -#define UCC_UART_UPSMR_RPM_LOW 0x0004 -#define UCC_UART_UPSMR_RPM_EVEN 0x0008 -#define UCC_UART_UPSMR_RPM_HIGH 0x000C -#define UCC_UART_UPSMR_TPM_MASK 0x0003 -#define UCC_UART_UPSMR_TPM_ODD 0x0000 -#define UCC_UART_UPSMR_TPM_LOW 0x0001 -#define UCC_UART_UPSMR_TPM_EVEN 0x0002 -#define UCC_UART_UPSMR_TPM_HIGH 0x0003 - -/* UCC Protocol Specific Mode Register (UPSMR), when used for Ethernet */ -#define UCC_GETH_UPSMR_FTFE 0x80000000 -#define UCC_GETH_UPSMR_PTPE 0x40000000 -#define UCC_GETH_UPSMR_ECM 0x04000000 -#define UCC_GETH_UPSMR_HSE 0x02000000 -#define UCC_GETH_UPSMR_PRO 0x00400000 -#define UCC_GETH_UPSMR_CAP 0x00200000 -#define UCC_GETH_UPSMR_RSH 0x00100000 -#define UCC_GETH_UPSMR_RPM 0x00080000 -#define UCC_GETH_UPSMR_R10M 0x00040000 -#define UCC_GETH_UPSMR_RLPB 0x00020000 -#define UCC_GETH_UPSMR_TBIM 0x00010000 -#define UCC_GETH_UPSMR_RES1 0x00002000 -#define UCC_GETH_UPSMR_RMM 0x00001000 -#define UCC_GETH_UPSMR_CAM 0x00000400 -#define UCC_GETH_UPSMR_BRO 0x00000200 -#define UCC_GETH_UPSMR_SMM 0x00000080 -#define UCC_GETH_UPSMR_SGMM 0x00000020 - -/* UCC Transmit On Demand Register (UTODR) */ -#define UCC_SLOW_TOD 0x8000 -#define UCC_FAST_TOD 0x8000 - -/* UCC Bus Mode Register masks */ -/* Not to be confused with the Bundle Mode Register */ -#define UCC_BMR_GBL 0x20 -#define UCC_BMR_BO_BE 0x10 -#define UCC_BMR_CETM 0x04 -#define UCC_BMR_DTB 0x02 -#define UCC_BMR_BDB 0x01 - -/* Function code masks */ -#define FC_GBL 0x20 -#define FC_DTB_LCL 0x02 -#define UCC_FAST_FUNCTION_CODE_GBL 0x20 -#define UCC_FAST_FUNCTION_CODE_DTB_LCL 0x02 -#define UCC_FAST_FUNCTION_CODE_BDB_LCL 0x01 - -#endif /* __KERNEL__ */ -#endif /* _ASM_POWERPC_QE_H */ diff --git a/arch/powerpc/include/asm/qe_ic.h b/arch/powerpc/include/asm/qe_ic.h deleted file mode 100644 index 25784cc..0000000 --- a/arch/powerpc/include/asm/qe_ic.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QE IC external definitions and structure. - * - * 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. - */ -#ifndef _ASM_POWERPC_QE_IC_H -#define _ASM_POWERPC_QE_IC_H - -#include - -struct device_node; -struct qe_ic; - -#define NUM_OF_QE_IC_GROUPS 6 - -/* Flags when we init the QE IC */ -#define QE_IC_SPREADMODE_GRP_W 0x00000001 -#define QE_IC_SPREADMODE_GRP_X 0x00000002 -#define QE_IC_SPREADMODE_GRP_Y 0x00000004 -#define QE_IC_SPREADMODE_GRP_Z 0x00000008 -#define QE_IC_SPREADMODE_GRP_RISCA 0x00000010 -#define QE_IC_SPREADMODE_GRP_RISCB 0x00000020 - -#define QE_IC_LOW_SIGNAL 0x00000100 -#define QE_IC_HIGH_SIGNAL 0x00000200 - -#define QE_IC_GRP_W_PRI0_DEST_SIGNAL_HIGH 0x00001000 -#define QE_IC_GRP_W_PRI1_DEST_SIGNAL_HIGH 0x00002000 -#define QE_IC_GRP_X_PRI0_DEST_SIGNAL_HIGH 0x00004000 -#define QE_IC_GRP_X_PRI1_DEST_SIGNAL_HIGH 0x00008000 -#define QE_IC_GRP_Y_PRI0_DEST_SIGNAL_HIGH 0x00010000 -#define QE_IC_GRP_Y_PRI1_DEST_SIGNAL_HIGH 0x00020000 -#define QE_IC_GRP_Z_PRI0_DEST_SIGNAL_HIGH 0x00040000 -#define QE_IC_GRP_Z_PRI1_DEST_SIGNAL_HIGH 0x00080000 -#define QE_IC_GRP_RISCA_PRI0_DEST_SIGNAL_HIGH 0x00100000 -#define QE_IC_GRP_RISCA_PRI1_DEST_SIGNAL_HIGH 0x00200000 -#define QE_IC_GRP_RISCB_PRI0_DEST_SIGNAL_HIGH 0x00400000 -#define QE_IC_GRP_RISCB_PRI1_DEST_SIGNAL_HIGH 0x00800000 -#define QE_IC_GRP_W_DEST_SIGNAL_SHIFT (12) - -/* QE interrupt sources groups */ -enum qe_ic_grp_id { - QE_IC_GRP_W = 0, /* QE interrupt controller group W */ - QE_IC_GRP_X, /* QE interrupt controller group X */ - QE_IC_GRP_Y, /* QE interrupt controller group Y */ - QE_IC_GRP_Z, /* QE interrupt controller group Z */ - QE_IC_GRP_RISCA, /* QE interrupt controller RISC group A */ - QE_IC_GRP_RISCB /* QE interrupt controller RISC group B */ -}; - -#ifdef CONFIG_QUICC_ENGINE -void qe_ic_init(struct device_node *node, unsigned int flags, - void (*low_handler)(unsigned int irq, struct irq_desc *desc), - void (*high_handler)(unsigned int irq, struct irq_desc *desc)); -unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic); -unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic); -#else -static inline void qe_ic_init(struct device_node *node, unsigned int flags, - void (*low_handler)(unsigned int irq, struct irq_desc *desc), - void (*high_handler)(unsigned int irq, struct irq_desc *desc)) -{} -static inline unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic) -{ return 0; } -static inline unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) -{ return 0; } -#endif /* CONFIG_QUICC_ENGINE */ - -void qe_ic_set_highest_priority(unsigned int virq, int high); -int qe_ic_set_priority(unsigned int virq, unsigned int priority); -int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high); - -static inline void qe_ic_cascade_low_ipic(unsigned int irq, - struct irq_desc *desc) -{ - struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); - unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic); - - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); -} - -static inline void qe_ic_cascade_high_ipic(unsigned int irq, - struct irq_desc *desc) -{ - struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); - unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic); - - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); -} - -static inline void qe_ic_cascade_low_mpic(unsigned int irq, - struct irq_desc *desc) -{ - struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); - unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic); - struct irq_chip *chip = irq_desc_get_chip(desc); - - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); - - chip->irq_eoi(&desc->irq_data); -} - -static inline void qe_ic_cascade_high_mpic(unsigned int irq, - struct irq_desc *desc) -{ - struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); - unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic); - struct irq_chip *chip = irq_desc_get_chip(desc); - - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); - - chip->irq_eoi(&desc->irq_data); -} - -static inline void qe_ic_cascade_muxed_mpic(unsigned int irq, - struct irq_desc *desc) -{ - struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); - unsigned int cascade_irq; - struct irq_chip *chip = irq_desc_get_chip(desc); - - cascade_irq = qe_ic_get_high_irq(qe_ic); - if (cascade_irq == NO_IRQ) - cascade_irq = qe_ic_get_low_irq(qe_ic); - - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); - - chip->irq_eoi(&desc->irq_data); -} - -#endif /* _ASM_POWERPC_QE_IC_H */ diff --git a/arch/powerpc/include/asm/ucc.h b/arch/powerpc/include/asm/ucc.h deleted file mode 100644 index 39a0bb5..0000000 --- a/arch/powerpc/include/asm/ucc.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * Internal header file for UCC unit routines. - * - * 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. - */ -#ifndef __UCC_H__ -#define __UCC_H__ - -#include -#include - -#define STATISTICS - -#define UCC_MAX_NUM 8 - -/* Slow or fast type for UCCs. -*/ -enum ucc_speed_type { - UCC_SPEED_TYPE_FAST = UCC_GUEMR_MODE_FAST_RX | UCC_GUEMR_MODE_FAST_TX, - UCC_SPEED_TYPE_SLOW = UCC_GUEMR_MODE_SLOW_RX | UCC_GUEMR_MODE_SLOW_TX -}; - -/* ucc_set_type - * Sets UCC to slow or fast mode. - * - * ucc_num - (In) number of UCC (0-7). - * speed - (In) slow or fast mode for UCC. - */ -int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed); - -int ucc_set_qe_mux_mii_mng(unsigned int ucc_num); - -int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, - enum comm_dir mode); -int ucc_set_tdm_rxtx_clk(unsigned int tdm_num, enum qe_clock clock, - enum comm_dir mode); -int ucc_set_tdm_rxtx_sync(unsigned int tdm_num, enum qe_clock clock, - enum comm_dir mode); - -int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask); - -/* QE MUX clock routing for UCC -*/ -static inline int ucc_set_qe_mux_grant(unsigned int ucc_num, int set) -{ - return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_GRANT); -} - -static inline int ucc_set_qe_mux_tsa(unsigned int ucc_num, int set) -{ - return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_TSA); -} - -static inline int ucc_set_qe_mux_bkpt(unsigned int ucc_num, int set) -{ - return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_BKPT); -} - -#endif /* __UCC_H__ */ diff --git a/arch/powerpc/include/asm/ucc_fast.h b/arch/powerpc/include/asm/ucc_fast.h deleted file mode 100644 index 561b00b..0000000 --- a/arch/powerpc/include/asm/ucc_fast.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Internal header file for UCC FAST unit routines. - * - * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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. - */ -#ifndef __UCC_FAST_H__ -#define __UCC_FAST_H__ - -#include - -#include -#include - -#include - -/* Receive BD's status */ -#define R_E 0x80000000 /* buffer empty */ -#define R_W 0x20000000 /* wrap bit */ -#define R_I 0x10000000 /* interrupt on reception */ -#define R_L 0x08000000 /* last */ -#define R_F 0x04000000 /* first */ -#define R_CM 0x02000000 /* CM */ - -/* transmit BD's status */ -#define T_R 0x80000000 /* ready bit */ -#define T_W 0x20000000 /* wrap bit */ -#define T_I 0x10000000 /* interrupt on completion */ -#define T_L 0x08000000 /* last */ -#define T_TC 0x04000000 /* crc */ -#define T_CM 0x02000000 /* CM */ - -/* Rx Data buffer must be 4 bytes aligned in most cases */ -#define UCC_FAST_RX_ALIGN 4 -#define UCC_FAST_MRBLR_ALIGNMENT 4 -#define UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT 8 - -/* Sizes */ -#define UCC_FAST_URFS_MIN_VAL 0x88 -#define UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR 8 - -/* ucc_fast_channel_protocol_mode - UCC FAST mode */ -enum ucc_fast_channel_protocol_mode { - UCC_FAST_PROTOCOL_MODE_HDLC = 0x00000000, - UCC_FAST_PROTOCOL_MODE_RESERVED01 = 0x00000001, - UCC_FAST_PROTOCOL_MODE_RESERVED_QMC = 0x00000002, - UCC_FAST_PROTOCOL_MODE_RESERVED02 = 0x00000003, - UCC_FAST_PROTOCOL_MODE_RESERVED_UART = 0x00000004, - UCC_FAST_PROTOCOL_MODE_RESERVED03 = 0x00000005, - UCC_FAST_PROTOCOL_MODE_RESERVED_EX_MAC_1 = 0x00000006, - UCC_FAST_PROTOCOL_MODE_RESERVED_EX_MAC_2 = 0x00000007, - UCC_FAST_PROTOCOL_MODE_RESERVED_BISYNC = 0x00000008, - UCC_FAST_PROTOCOL_MODE_RESERVED04 = 0x00000009, - UCC_FAST_PROTOCOL_MODE_ATM = 0x0000000A, - UCC_FAST_PROTOCOL_MODE_RESERVED05 = 0x0000000B, - UCC_FAST_PROTOCOL_MODE_ETHERNET = 0x0000000C, - UCC_FAST_PROTOCOL_MODE_RESERVED06 = 0x0000000D, - UCC_FAST_PROTOCOL_MODE_POS = 0x0000000E, - UCC_FAST_PROTOCOL_MODE_RESERVED07 = 0x0000000F -}; - -/* ucc_fast_transparent_txrx - UCC Fast Transparent TX & RX */ -enum ucc_fast_transparent_txrx { - UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL = 0x00000000, - UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_TRANSPARENT = 0x18000000 -}; - -/* UCC fast diagnostic mode */ -enum ucc_fast_diag_mode { - UCC_FAST_DIAGNOSTIC_NORMAL = 0x0, - UCC_FAST_DIAGNOSTIC_LOCAL_LOOP_BACK = 0x40000000, - UCC_FAST_DIAGNOSTIC_AUTO_ECHO = 0x80000000, - UCC_FAST_DIAGNOSTIC_LOOP_BACK_AND_ECHO = 0xC0000000 -}; - -/* UCC fast Sync length (transparent mode only) */ -enum ucc_fast_sync_len { - UCC_FAST_SYNC_LEN_NOT_USED = 0x0, - UCC_FAST_SYNC_LEN_AUTOMATIC = 0x00004000, - UCC_FAST_SYNC_LEN_8_BIT = 0x00008000, - UCC_FAST_SYNC_LEN_16_BIT = 0x0000C000 -}; - -/* UCC fast RTS mode */ -enum ucc_fast_ready_to_send { - UCC_FAST_SEND_IDLES_BETWEEN_FRAMES = 0x00000000, - UCC_FAST_SEND_FLAGS_BETWEEN_FRAMES = 0x00002000 -}; - -/* UCC fast receiver decoding mode */ -enum ucc_fast_rx_decoding_method { - UCC_FAST_RX_ENCODING_NRZ = 0x00000000, - UCC_FAST_RX_ENCODING_NRZI = 0x00000800, - UCC_FAST_RX_ENCODING_RESERVED0 = 0x00001000, - UCC_FAST_RX_ENCODING_RESERVED1 = 0x00001800 -}; - -/* UCC fast transmitter encoding mode */ -enum ucc_fast_tx_encoding_method { - UCC_FAST_TX_ENCODING_NRZ = 0x00000000, - UCC_FAST_TX_ENCODING_NRZI = 0x00000100, - UCC_FAST_TX_ENCODING_RESERVED0 = 0x00000200, - UCC_FAST_TX_ENCODING_RESERVED1 = 0x00000300 -}; - -/* UCC fast CRC length */ -enum ucc_fast_transparent_tcrc { - UCC_FAST_16_BIT_CRC = 0x00000000, - UCC_FAST_CRC_RESERVED0 = 0x00000040, - UCC_FAST_32_BIT_CRC = 0x00000080, - UCC_FAST_CRC_RESERVED1 = 0x000000C0 -}; - -/* Fast UCC initialization structure */ -struct ucc_fast_info { - int ucc_num; - int tdm_num; - enum qe_clock rx_clock; - enum qe_clock tx_clock; - enum qe_clock rx_sync; - enum qe_clock tx_sync; - resource_size_t regs; - int irq; - u32 uccm_mask; - int bd_mem_part; - int brkpt_support; - int grant_support; - int tsa; - int cdp; - int cds; - int ctsp; - int ctss; - int tci; - int txsy; - int rtsm; - int revd; - int rsyn; - u16 max_rx_buf_length; - u16 urfs; - u16 urfet; - u16 urfset; - u16 utfs; - u16 utfet; - u16 utftt; - u16 ufpt; - enum ucc_fast_channel_protocol_mode mode; - enum ucc_fast_transparent_txrx ttx_trx; - enum ucc_fast_tx_encoding_method tenc; - enum ucc_fast_rx_decoding_method renc; - enum ucc_fast_transparent_tcrc tcrc; - enum ucc_fast_sync_len synl; -}; - -struct ucc_fast_private { - struct ucc_fast_info *uf_info; - struct ucc_fast __iomem *uf_regs; /* a pointer to the UCC regs. */ - u32 __iomem *p_ucce; /* a pointer to the event register in memory. */ - u32 __iomem *p_uccm; /* a pointer to the mask register in memory. */ -#ifdef CONFIG_UGETH_TX_ON_DEMAND - u16 __iomem *p_utodr; /* pointer to the transmit on demand register */ -#endif - int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ - int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ - int stopped_tx; /* Whether channel has been stopped for Tx - (STOP_TX, etc.) */ - int stopped_rx; /* Whether channel has been stopped for Rx */ - u32 ucc_fast_tx_virtual_fifo_base_offset;/* pointer to base of Tx - virtual fifo */ - u32 ucc_fast_rx_virtual_fifo_base_offset;/* pointer to base of Rx - virtual fifo */ -#ifdef STATISTICS - u32 tx_frames; /* Transmitted frames counter. */ - u32 rx_frames; /* Received frames counter (only frames - passed to application). */ - u32 tx_discarded; /* Discarded tx frames counter (frames that - were discarded by the driver due to errors). - */ - u32 rx_discarded; /* Discarded rx frames counter (frames that - were discarded by the driver due to errors). - */ -#endif /* STATISTICS */ - u16 mrblr; /* maximum receive buffer length */ -}; - -/* ucc_fast_init - * Initializes Fast UCC according to user provided parameters. - * - * uf_info - (In) pointer to the fast UCC info structure. - * uccf_ret - (Out) pointer to the fast UCC structure. - */ -int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret); - -/* ucc_fast_free - * Frees all resources for fast UCC. - * - * uccf - (In) pointer to the fast UCC structure. - */ -void ucc_fast_free(struct ucc_fast_private * uccf); - -/* ucc_fast_enable - * Enables a fast UCC port. - * This routine enables Tx and/or Rx through the General UCC Mode Register. - * - * uccf - (In) pointer to the fast UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode); - -/* ucc_fast_disable - * Disables a fast UCC port. - * This routine disables Tx and/or Rx through the General UCC Mode Register. - * - * uccf - (In) pointer to the fast UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode); - -/* ucc_fast_irq - * Handles interrupts on fast UCC. - * Called from the general interrupt routine to handle interrupts on fast UCC. - * - * uccf - (In) pointer to the fast UCC structure. - */ -void ucc_fast_irq(struct ucc_fast_private * uccf); - -/* ucc_fast_transmit_on_demand - * Immediately forces a poll of the transmitter for data to be sent. - * Typically, the hardware performs a periodic poll for data that the - * transmit routine has set up to be transmitted. In cases where - * this polling cycle is not soon enough, this optional routine can - * be invoked to force a poll right away, instead. Proper use for - * each transmission for which this functionality is desired is to - * call the transmit routine and then this routine right after. - * - * uccf - (In) pointer to the fast UCC structure. - */ -void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf); - -u32 ucc_fast_get_qe_cr_subblock(int uccf_num); - -void ucc_fast_dump_regs(struct ucc_fast_private * uccf); - -#endif /* __UCC_FAST_H__ */ diff --git a/arch/powerpc/include/asm/ucc_slow.h b/arch/powerpc/include/asm/ucc_slow.h deleted file mode 100644 index c44131e..0000000 --- a/arch/powerpc/include/asm/ucc_slow.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * Internal header file for UCC SLOW unit routines. - * - * 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. - */ -#ifndef __UCC_SLOW_H__ -#define __UCC_SLOW_H__ - -#include - -#include -#include - -#include - -/* transmit BD's status */ -#define T_R 0x80000000 /* ready bit */ -#define T_PAD 0x40000000 /* add pads to short frames */ -#define T_W 0x20000000 /* wrap bit */ -#define T_I 0x10000000 /* interrupt on completion */ -#define T_L 0x08000000 /* last */ - -#define T_A 0x04000000 /* Address - the data transmitted as address - chars */ -#define T_TC 0x04000000 /* transmit CRC */ -#define T_CM 0x02000000 /* continuous mode */ -#define T_DEF 0x02000000 /* collision on previous attempt to transmit */ -#define T_P 0x01000000 /* Preamble - send Preamble sequence before - data */ -#define T_HB 0x01000000 /* heartbeat */ -#define T_NS 0x00800000 /* No Stop */ -#define T_LC 0x00800000 /* late collision */ -#define T_RL 0x00400000 /* retransmission limit */ -#define T_UN 0x00020000 /* underrun */ -#define T_CT 0x00010000 /* CTS lost */ -#define T_CSL 0x00010000 /* carrier sense lost */ -#define T_RC 0x003c0000 /* retry count */ - -/* Receive BD's status */ -#define R_E 0x80000000 /* buffer empty */ -#define R_W 0x20000000 /* wrap bit */ -#define R_I 0x10000000 /* interrupt on reception */ -#define R_L 0x08000000 /* last */ -#define R_C 0x08000000 /* the last byte in this buffer is a cntl - char */ -#define R_F 0x04000000 /* first */ -#define R_A 0x04000000 /* the first byte in this buffer is address - byte */ -#define R_CM 0x02000000 /* continuous mode */ -#define R_ID 0x01000000 /* buffer close on reception of idles */ -#define R_M 0x01000000 /* Frame received because of promiscuous - mode */ -#define R_AM 0x00800000 /* Address match */ -#define R_DE 0x00800000 /* Address match */ -#define R_LG 0x00200000 /* Break received */ -#define R_BR 0x00200000 /* Frame length violation */ -#define R_NO 0x00100000 /* Rx Non Octet Aligned Packet */ -#define R_FR 0x00100000 /* Framing Error (no stop bit) character - received */ -#define R_PR 0x00080000 /* Parity Error character received */ -#define R_AB 0x00080000 /* Frame Aborted */ -#define R_SH 0x00080000 /* frame is too short */ -#define R_CR 0x00040000 /* CRC Error */ -#define R_OV 0x00020000 /* Overrun */ -#define R_CD 0x00010000 /* CD lost */ -#define R_CL 0x00010000 /* this frame is closed because of a - collision */ - -/* Rx Data buffer must be 4 bytes aligned in most cases.*/ -#define UCC_SLOW_RX_ALIGN 4 -#define UCC_SLOW_MRBLR_ALIGNMENT 4 -#define UCC_SLOW_PRAM_SIZE 0x100 -#define ALIGNMENT_OF_UCC_SLOW_PRAM 64 - -/* UCC Slow Channel Protocol Mode */ -enum ucc_slow_channel_protocol_mode { - UCC_SLOW_CHANNEL_PROTOCOL_MODE_QMC = 0x00000002, - UCC_SLOW_CHANNEL_PROTOCOL_MODE_UART = 0x00000004, - UCC_SLOW_CHANNEL_PROTOCOL_MODE_BISYNC = 0x00000008, -}; - -/* UCC Slow Transparent Transmit CRC (TCRC) */ -enum ucc_slow_transparent_tcrc { - /* 16-bit CCITT CRC (HDLC). (X16 + X12 + X5 + 1) */ - UCC_SLOW_TRANSPARENT_TCRC_CCITT_CRC16 = 0x00000000, - /* CRC16 (BISYNC). (X16 + X15 + X2 + 1) */ - UCC_SLOW_TRANSPARENT_TCRC_CRC16 = 0x00004000, - /* 32-bit CCITT CRC (Ethernet and HDLC) */ - UCC_SLOW_TRANSPARENT_TCRC_CCITT_CRC32 = 0x00008000, -}; - -/* UCC Slow oversampling rate for transmitter (TDCR) */ -enum ucc_slow_tx_oversampling_rate { - /* 1x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_1 = 0x00000000, - /* 8x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_8 = 0x00010000, - /* 16x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_16 = 0x00020000, - /* 32x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_32 = 0x00030000, -}; - -/* UCC Slow Oversampling rate for receiver (RDCR) -*/ -enum ucc_slow_rx_oversampling_rate { - /* 1x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_1 = 0x00000000, - /* 8x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_8 = 0x00004000, - /* 16x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_16 = 0x00008000, - /* 32x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_32 = 0x0000c000, -}; - -/* UCC Slow Transmitter encoding method (TENC) -*/ -enum ucc_slow_tx_encoding_method { - UCC_SLOW_TRANSMITTER_ENCODING_METHOD_TENC_NRZ = 0x00000000, - UCC_SLOW_TRANSMITTER_ENCODING_METHOD_TENC_NRZI = 0x00000100 -}; - -/* UCC Slow Receiver decoding method (RENC) -*/ -enum ucc_slow_rx_decoding_method { - UCC_SLOW_RECEIVER_DECODING_METHOD_RENC_NRZ = 0x00000000, - UCC_SLOW_RECEIVER_DECODING_METHOD_RENC_NRZI = 0x00000800 -}; - -/* UCC Slow Diagnostic mode (DIAG) -*/ -enum ucc_slow_diag_mode { - UCC_SLOW_DIAG_MODE_NORMAL = 0x00000000, - UCC_SLOW_DIAG_MODE_LOOPBACK = 0x00000040, - UCC_SLOW_DIAG_MODE_ECHO = 0x00000080, - UCC_SLOW_DIAG_MODE_LOOPBACK_ECHO = 0x000000c0 -}; - -struct ucc_slow_info { - int ucc_num; - int protocol; /* QE_CR_PROTOCOL_xxx */ - enum qe_clock rx_clock; - enum qe_clock tx_clock; - phys_addr_t regs; - int irq; - u16 uccm_mask; - int data_mem_part; - int init_tx; - int init_rx; - u32 tx_bd_ring_len; - u32 rx_bd_ring_len; - int rx_interrupts; - int brkpt_support; - int grant_support; - int tsa; - int cdp; - int cds; - int ctsp; - int ctss; - int rinv; - int tinv; - int rtsm; - int rfw; - int tci; - int tend; - int tfl; - int txsy; - u16 max_rx_buf_length; - enum ucc_slow_transparent_tcrc tcrc; - enum ucc_slow_channel_protocol_mode mode; - enum ucc_slow_diag_mode diag; - enum ucc_slow_tx_oversampling_rate tdcr; - enum ucc_slow_rx_oversampling_rate rdcr; - enum ucc_slow_tx_encoding_method tenc; - enum ucc_slow_rx_decoding_method renc; -}; - -struct ucc_slow_private { - struct ucc_slow_info *us_info; - struct ucc_slow __iomem *us_regs; /* Ptr to memory map of UCC regs */ - struct ucc_slow_pram *us_pram; /* a pointer to the parameter RAM */ - u32 us_pram_offset; - int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ - int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ - int stopped_tx; /* Whether channel has been stopped for Tx - (STOP_TX, etc.) */ - int stopped_rx; /* Whether channel has been stopped for Rx */ - struct list_head confQ; /* frames passed to chip waiting for tx */ - u32 first_tx_bd_mask; /* mask is used in Tx routine to save status - and length for first BD in a frame */ - u32 tx_base_offset; /* first BD in Tx BD table offset (In MURAM) */ - u32 rx_base_offset; /* first BD in Rx BD table offset (In MURAM) */ - struct qe_bd *confBd; /* next BD for confirm after Tx */ - struct qe_bd *tx_bd; /* next BD for new Tx request */ - struct qe_bd *rx_bd; /* next BD to collect after Rx */ - void *p_rx_frame; /* accumulating receive frame */ - u16 *p_ucce; /* a pointer to the event register in memory. - */ - u16 *p_uccm; /* a pointer to the mask register in memory */ - u16 saved_uccm; /* a saved mask for the RX Interrupt bits */ -#ifdef STATISTICS - u32 tx_frames; /* Transmitted frames counters */ - u32 rx_frames; /* Received frames counters (only frames - passed to application) */ - u32 rx_discarded; /* Discarded frames counters (frames that - were discarded by the driver due to - errors) */ -#endif /* STATISTICS */ -}; - -/* ucc_slow_init - * Initializes Slow UCC according to provided parameters. - * - * us_info - (In) pointer to the slow UCC info structure. - * uccs_ret - (Out) pointer to the slow UCC structure. - */ -int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret); - -/* ucc_slow_free - * Frees all resources for slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_free(struct ucc_slow_private * uccs); - -/* ucc_slow_enable - * Enables a fast UCC port. - * This routine enables Tx and/or Rx through the General UCC Mode Register. - * - * uccs - (In) pointer to the slow UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode); - -/* ucc_slow_disable - * Disables a fast UCC port. - * This routine disables Tx and/or Rx through the General UCC Mode Register. - * - * uccs - (In) pointer to the slow UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode); - -/* ucc_slow_poll_transmitter_now - * Immediately forces a poll of the transmitter for data to be sent. - * Typically, the hardware performs a periodic poll for data that the - * transmit routine has set up to be transmitted. In cases where - * this polling cycle is not soon enough, this optional routine can - * be invoked to force a poll right away, instead. Proper use for - * each transmission for which this functionality is desired is to - * call the transmit routine and then this routine right after. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs); - -/* ucc_slow_graceful_stop_tx - * Smoothly stops transmission on a specified slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs); - -/* ucc_slow_stop_tx - * Stops transmission on a specified slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_stop_tx(struct ucc_slow_private * uccs); - -/* ucc_slow_restart_tx - * Restarts transmitting on a specified slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_restart_tx(struct ucc_slow_private *uccs); - -u32 ucc_slow_get_qe_cr_subblock(int uccs_num); - -#endif /* __UCC_SLOW_H__ */ diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c index bf4c447..584d8cc 100644 --- a/arch/powerpc/platforms/83xx/km83xx.c +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -37,8 +37,8 @@ #include #include #include -#include -#include +#include +#include #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/misc.c b/arch/powerpc/platforms/83xx/misc.c index 125336f..3e2e6d2 100644 --- a/arch/powerpc/platforms/83xx/misc.c +++ b/arch/powerpc/platforms/83xx/misc.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 8d76220..e1186be 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -36,8 +36,8 @@ #include #include #include -#include -#include +#include +#include #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index eff5baa..9f75944 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -25,8 +25,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index 1a26d2f..7c1a22f 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -44,8 +44,8 @@ #include #include #include -#include -#include +#include +#include #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c index b63b42d..5e17d71 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_rdk.c +++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c @@ -20,8 +20,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c index 7731c75..7157baa 100644 --- a/arch/powerpc/platforms/85xx/common.c +++ b/arch/powerpc/platforms/85xx/common.c @@ -7,7 +7,7 @@ */ #include -#include +#include #include #include "mpc85xx.h" diff --git a/arch/powerpc/platforms/85xx/corenet_generic.c b/arch/powerpc/platforms/85xx/corenet_generic.c index e4a1400..adb65a5 100644 --- a/arch/powerpc/platforms/85xx/corenet_generic.c +++ b/arch/powerpc/platforms/85xx/corenet_generic.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index c111fba..7171ab0 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -47,8 +47,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 2746b8b..ccfc8d0 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -25,8 +25,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/arch/powerpc/platforms/85xx/twr_p102x.c b/arch/powerpc/platforms/85xx/twr_p102x.c index 4e119ac..79dc247 100644 --- a/arch/powerpc/platforms/85xx/twr_p102x.c +++ b/arch/powerpc/platforms/85xx/twr_p102x.c @@ -34,8 +34,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index be181b6..d09ae32f 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -278,25 +278,6 @@ config TAU_AVERAGE If in doubt, say N here. -config QUICC_ENGINE - bool "Freescale QUICC Engine (QE) Support" - depends on FSL_SOC && (PPC32 || PPC64) - select PPC_LIB_RHEAP - select CRC32 - help - The QUICC Engine (QE) is a new generation of communications - coprocessors on Freescale embedded CPUs (akin to CPM in older chips). - Selecting this option means that you wish to build a kernel - for a machine with a QE coprocessor. - -config QE_GPIO - bool "QE GPIO support" - depends on QUICC_ENGINE - select ARCH_REQUIRE_GPIOLIB - help - Say Y here if you're going to use hardware that connects to the - QE GPIOs. - config CPM2 bool "Enable support for the CPM2 (Communications Processor Module)" depends on (FSL_SOC_BOOKE && PPC32) || 8260 diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index ca258c5..d6bab98 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -28,7 +28,6 @@ obj-$(CONFIG_FSL_85XX_CACHE_SRAM) += fsl_85xx_l2ctlr.o fsl_85xx_cache_sram.o obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_FSL_RIO) += fsl_rio.o fsl_rmu.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o -obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o \ mv64x60_udbg.o diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig deleted file mode 100644 index 644b7dbf..0000000 --- a/arch/powerpc/sysdev/qe_lib/Kconfig +++ /dev/null @@ -1,27 +0,0 @@ -# -# QE Communication options -# - -config UCC_SLOW - bool - default y if SERIAL_QE - help - This option provides qe_lib support to UCC slow - protocols: UART, BISYNC, QMC - -config UCC_FAST - bool - default y if UCC_GETH || FSL_UCC_TDM || FSL_UCC_HDLC - help - This option provides qe_lib support to UCC fast - protocols: HDLC, Ethernet, ATM, transparent - -config UCC - bool - default y if UCC_FAST || UCC_SLOW - -config QE_USB - bool - default y if USB_FSL_QE - help - QE USB Controller support diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/arch/powerpc/sysdev/qe_lib/Makefile deleted file mode 100644 index f1855c1..0000000 --- a/arch/powerpc/sysdev/qe_lib/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# Makefile for the linux ppc-specific parts of QE -# -obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o - -obj-$(CONFIG_UCC) += ucc.o -obj-$(CONFIG_UCC_SLOW) += ucc_slow.o -obj-$(CONFIG_UCC_FAST) += ucc_fast.o -obj-$(CONFIG_QE_USB) += usb.o -obj-$(CONFIG_QE_GPIO) += gpio.o diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c deleted file mode 100644 index 521e67a..0000000 --- a/arch/powerpc/sysdev/qe_lib/gpio.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * QUICC Engine GPIOs - * - * Copyright (c) MontaVista Software, Inc. 2008. - * - * Author: Anton Vorontsov - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct qe_gpio_chip { - struct of_mm_gpio_chip mm_gc; - spinlock_t lock; - - unsigned long pin_flags[QE_PIO_PINS]; -#define QE_PIN_REQUESTED 0 - - /* shadowed data register to clear/set bits safely */ - u32 cpdata; - - /* saved_regs used to restore dedicated functions */ - struct qe_pio_regs saved_regs; -}; - -static inline struct qe_gpio_chip * -to_qe_gpio_chip(struct of_mm_gpio_chip *mm_gc) -{ - return container_of(mm_gc, struct qe_gpio_chip, mm_gc); -} - -static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) -{ - struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; - - qe_gc->cpdata = in_be32(®s->cpdata); - qe_gc->saved_regs.cpdata = qe_gc->cpdata; - qe_gc->saved_regs.cpdir1 = in_be32(®s->cpdir1); - qe_gc->saved_regs.cpdir2 = in_be32(®s->cpdir2); - qe_gc->saved_regs.cppar1 = in_be32(®s->cppar1); - qe_gc->saved_regs.cppar2 = in_be32(®s->cppar2); - qe_gc->saved_regs.cpodr = in_be32(®s->cpodr); -} - -static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; - u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); - - return in_be32(®s->cpdata) & pin_mask; -} - -static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); - struct qe_pio_regs __iomem *regs = mm_gc->regs; - unsigned long flags; - u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); - - spin_lock_irqsave(&qe_gc->lock, flags); - - if (val) - qe_gc->cpdata |= pin_mask; - else - qe_gc->cpdata &= ~pin_mask; - - out_be32(®s->cpdata, qe_gc->cpdata); - - spin_unlock_irqrestore(&qe_gc->lock, flags); -} - -static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); - unsigned long flags; - - spin_lock_irqsave(&qe_gc->lock, flags); - - __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_IN, 0, 0, 0); - - spin_unlock_irqrestore(&qe_gc->lock, flags); - - return 0; -} - -static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); - unsigned long flags; - - qe_gpio_set(gc, gpio, val); - - spin_lock_irqsave(&qe_gc->lock, flags); - - __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0); - - spin_unlock_irqrestore(&qe_gc->lock, flags); - - return 0; -} - -struct qe_pin { - /* - * The qe_gpio_chip name is unfortunate, we should change that to - * something like qe_pio_controller. Someday. - */ - struct qe_gpio_chip *controller; - int num; -}; - -/** - * qe_pin_request - Request a QE pin - * @np: device node to get a pin from - * @index: index of a pin in the device tree - * Context: non-atomic - * - * This function return qe_pin so that you could use it with the rest of - * the QE Pin Multiplexing API. - */ -struct qe_pin *qe_pin_request(struct device_node *np, int index) -{ - struct qe_pin *qe_pin; - struct gpio_chip *gc; - struct of_mm_gpio_chip *mm_gc; - struct qe_gpio_chip *qe_gc; - int err; - unsigned long flags; - - qe_pin = kzalloc(sizeof(*qe_pin), GFP_KERNEL); - if (!qe_pin) { - pr_debug("%s: can't allocate memory\n", __func__); - return ERR_PTR(-ENOMEM); - } - - err = of_get_gpio(np, index); - if (err < 0) - goto err0; - gc = gpio_to_chip(err); - if (WARN_ON(!gc)) - goto err0; - - if (!of_device_is_compatible(gc->of_node, "fsl,mpc8323-qe-pario-bank")) { - pr_debug("%s: tried to get a non-qe pin\n", __func__); - err = -EINVAL; - goto err0; - } - - mm_gc = to_of_mm_gpio_chip(gc); - qe_gc = to_qe_gpio_chip(mm_gc); - - spin_lock_irqsave(&qe_gc->lock, flags); - - err -= gc->base; - if (test_and_set_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[err]) == 0) { - qe_pin->controller = qe_gc; - qe_pin->num = err; - err = 0; - } else { - err = -EBUSY; - } - - spin_unlock_irqrestore(&qe_gc->lock, flags); - - if (!err) - return qe_pin; -err0: - kfree(qe_pin); - pr_debug("%s failed with status %d\n", __func__, err); - return ERR_PTR(err); -} -EXPORT_SYMBOL(qe_pin_request); - -/** - * qe_pin_free - Free a pin - * @qe_pin: pointer to the qe_pin structure - * Context: any - * - * This function frees the qe_pin structure and makes a pin available - * for further qe_pin_request() calls. - */ -void qe_pin_free(struct qe_pin *qe_pin) -{ - struct qe_gpio_chip *qe_gc = qe_pin->controller; - unsigned long flags; - const int pin = qe_pin->num; - - spin_lock_irqsave(&qe_gc->lock, flags); - test_and_clear_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[pin]); - spin_unlock_irqrestore(&qe_gc->lock, flags); - - kfree(qe_pin); -} -EXPORT_SYMBOL(qe_pin_free); - -/** - * qe_pin_set_dedicated - Revert a pin to a dedicated peripheral function mode - * @qe_pin: pointer to the qe_pin structure - * Context: any - * - * This function resets a pin to a dedicated peripheral function that - * has been set up by the firmware. - */ -void qe_pin_set_dedicated(struct qe_pin *qe_pin) -{ - struct qe_gpio_chip *qe_gc = qe_pin->controller; - struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; - struct qe_pio_regs *sregs = &qe_gc->saved_regs; - int pin = qe_pin->num; - u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1)); - u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2); - bool second_reg = pin > (QE_PIO_PINS / 2) - 1; - unsigned long flags; - - spin_lock_irqsave(&qe_gc->lock, flags); - - if (second_reg) { - clrsetbits_be32(®s->cpdir2, mask2, sregs->cpdir2 & mask2); - clrsetbits_be32(®s->cppar2, mask2, sregs->cppar2 & mask2); - } else { - clrsetbits_be32(®s->cpdir1, mask2, sregs->cpdir1 & mask2); - clrsetbits_be32(®s->cppar1, mask2, sregs->cppar1 & mask2); - } - - if (sregs->cpdata & mask1) - qe_gc->cpdata |= mask1; - else - qe_gc->cpdata &= ~mask1; - - out_be32(®s->cpdata, qe_gc->cpdata); - clrsetbits_be32(®s->cpodr, mask1, sregs->cpodr & mask1); - - spin_unlock_irqrestore(&qe_gc->lock, flags); -} -EXPORT_SYMBOL(qe_pin_set_dedicated); - -/** - * qe_pin_set_gpio - Set a pin to the GPIO mode - * @qe_pin: pointer to the qe_pin structure - * Context: any - * - * This function sets a pin to the GPIO mode. - */ -void qe_pin_set_gpio(struct qe_pin *qe_pin) -{ - struct qe_gpio_chip *qe_gc = qe_pin->controller; - struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; - unsigned long flags; - - spin_lock_irqsave(&qe_gc->lock, flags); - - /* Let's make it input by default, GPIO API is able to change that. */ - __par_io_config_pin(regs, qe_pin->num, QE_PIO_DIR_IN, 0, 0, 0); - - spin_unlock_irqrestore(&qe_gc->lock, flags); -} -EXPORT_SYMBOL(qe_pin_set_gpio); - -static int __init qe_add_gpiochips(void) -{ - struct device_node *np; - - for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") { - int ret; - struct qe_gpio_chip *qe_gc; - struct of_mm_gpio_chip *mm_gc; - struct gpio_chip *gc; - - qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL); - if (!qe_gc) { - ret = -ENOMEM; - goto err; - } - - spin_lock_init(&qe_gc->lock); - - mm_gc = &qe_gc->mm_gc; - gc = &mm_gc->gc; - - mm_gc->save_regs = qe_gpio_save_regs; - gc->ngpio = QE_PIO_PINS; - gc->direction_input = qe_gpio_dir_in; - gc->direction_output = qe_gpio_dir_out; - gc->get = qe_gpio_get; - gc->set = qe_gpio_set; - - ret = of_mm_gpiochip_add(np, mm_gc); - if (ret) - goto err; - continue; -err: - pr_err("%s: registration failed with status %d\n", - np->full_name, ret); - kfree(qe_gc); - /* try others anyway */ - } - return 0; -} -arch_initcall(qe_add_gpiochips); diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c deleted file mode 100644 index ad324ba..0000000 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * Copyright (C) 2006-2010, 2012 Freescale Semiconductor, Inc. - * All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * Based on cpm2_common.c from Dan Malek (dmalek@jlc.net) - * - * Description: - * General Purpose functions for the global management of the - * QUICC Engine (QE). - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void qe_snums_init(void); -static int qe_sdma_init(void); - -int siram_init_flag; - -static DEFINE_SPINLOCK(qe_lock); -DEFINE_SPINLOCK(cmxgcr_lock); -EXPORT_SYMBOL(cmxgcr_lock); - -/* QE snum state */ -enum qe_snum_state { - QE_SNUM_STATE_USED, - QE_SNUM_STATE_FREE -}; - -/* QE snum */ -struct qe_snum { - u8 num; - enum qe_snum_state state; -}; - -/* We allocate this here because it is used almost exclusively for - * the communication processor devices. - */ -struct qe_immap __iomem *qe_immr; -EXPORT_SYMBOL(qe_immr); - -static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ -static unsigned int qe_num_of_snum; - -static phys_addr_t qebase = -1; - -phys_addr_t get_qe_base(void) -{ - struct device_node *qe; - int size; - const u32 *prop; - - if (qebase != -1) - return qebase; - - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return qebase; - } - - prop = of_get_property(qe, "reg", &size); - if (prop && size >= sizeof(*prop)) - qebase = of_translate_address(qe, prop); - of_node_put(qe); - - return qebase; -} - -EXPORT_SYMBOL(get_qe_base); - -void qe_reset(void) -{ - if (qe_immr == NULL) - qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE); - - qe_snums_init(); - - qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, - QE_CR_PROTOCOL_UNSPECIFIED, 0); - - /* Reclaim the MURAM memory for our use. */ - qe_muram_init(); - - if (qe_sdma_init()) - panic("sdma init failed!"); -} - -int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) -{ - unsigned long flags; - u8 mcn_shift = 0, dev_shift = 0; - u32 ret; - - spin_lock_irqsave(&qe_lock, flags); - if (cmd == QE_RESET) { - out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG)); - } else { - if (cmd == QE_ASSIGN_PAGE) { - /* Here device is the SNUM, not sub-block */ - dev_shift = QE_CR_SNUM_SHIFT; - } else if (cmd == QE_ASSIGN_RISC) { - /* Here device is the SNUM, and mcnProtocol is - * e_QeCmdRiscAssignment value */ - dev_shift = QE_CR_SNUM_SHIFT; - mcn_shift = QE_CR_MCN_RISC_ASSIGN_SHIFT; - } else { - if (device == QE_CR_SUBBLOCK_USB) - mcn_shift = QE_CR_MCN_USB_SHIFT; - else - mcn_shift = QE_CR_MCN_NORMAL_SHIFT; - } - - out_be32(&qe_immr->cp.cecdr, cmd_input); - out_be32(&qe_immr->cp.cecr, - (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32) - mcn_protocol << mcn_shift)); - } - - /* wait for the QE_CR_FLG to clear */ - ret = spin_event_timeout((in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) == 0, - 100, 0); - /* On timeout (e.g. failure), the expression will be false (ret == 0), - otherwise it will be true (ret == 1). */ - spin_unlock_irqrestore(&qe_lock, flags); - - return ret == 1; -} -EXPORT_SYMBOL(qe_issue_cmd); - -/* Set a baud rate generator. This needs lots of work. There are - * 16 BRGs, which can be connected to the QE channels or output - * as clocks. The BRGs are in two different block of internal - * memory mapped space. - * The BRG clock is the QE clock divided by 2. - * It was set up long ago during the initial boot phase and is - * is given to us. - * Baud rate clocks are zero-based in the driver code (as that maps - * to port numbers). Documentation uses 1-based numbering. - */ -static unsigned int brg_clk = 0; - -unsigned int qe_get_brg_clk(void) -{ - struct device_node *qe; - int size; - const u32 *prop; - - if (brg_clk) - return brg_clk; - - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return brg_clk; - } - - prop = of_get_property(qe, "brg-frequency", &size); - if (prop && size == sizeof(*prop)) - brg_clk = *prop; - - of_node_put(qe); - - return brg_clk; -} -EXPORT_SYMBOL(qe_get_brg_clk); - -/* Program the BRG to the given sampling rate and multiplier - * - * @brg: the BRG, QE_BRG1 - QE_BRG16 - * @rate: the desired sampling rate - * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or - * GUMR_L[TDCR]. E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01, - * then 'multiplier' should be 8. - */ -int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier) -{ - u32 divisor, tempval; - u32 div16 = 0; - - if ((brg < QE_BRG1) || (brg > QE_BRG16)) - return -EINVAL; - - divisor = qe_get_brg_clk() / (rate * multiplier); - - if (divisor > QE_BRGC_DIVISOR_MAX + 1) { - div16 = QE_BRGC_DIV16; - divisor /= 16; - } - - /* Errata QE_General4, which affects some MPC832x and MPC836x SOCs, says - that the BRG divisor must be even if you're not using divide-by-16 - mode. */ - if (!div16 && (divisor & 1) && (divisor > 3)) - divisor++; - - tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | - QE_BRGC_ENABLE | div16; - - out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval); - - return 0; -} -EXPORT_SYMBOL(qe_setbrg); - -/* Convert a string to a QE clock source enum - * - * This function takes a string, typically from a property in the device - * tree, and returns the corresponding "enum qe_clock" value. -*/ -enum qe_clock qe_clock_source(const char *source) -{ - unsigned int i; - - if (strcasecmp(source, "none") == 0) - return QE_CLK_NONE; - - if (strcasecmp(source, "tsync_pin") == 0) - return QE_TSYNC_PIN; - - if (strcasecmp(source, "rsync_pin") == 0) - return QE_RSYNC_PIN; - - if (strncasecmp(source, "brg", 3) == 0) { - i = simple_strtoul(source + 3, NULL, 10); - if ((i >= 1) && (i <= 16)) - return (QE_BRG1 - 1) + i; - else - return QE_CLK_DUMMY; - } - - if (strncasecmp(source, "clk", 3) == 0) { - i = simple_strtoul(source + 3, NULL, 10); - if ((i >= 1) && (i <= 24)) - return (QE_CLK1 - 1) + i; - else - return QE_CLK_DUMMY; - } - - return QE_CLK_DUMMY; -} -EXPORT_SYMBOL(qe_clock_source); - -/* Initialize SNUMs (thread serial numbers) according to - * QE Module Control chapter, SNUM table - */ -static void qe_snums_init(void) -{ - int i; - static const u8 snum_init_76[] = { - 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, - 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, - 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, - 0xD8, 0xD9, 0xE8, 0xE9, 0x44, 0x45, 0x4C, 0x4D, - 0x54, 0x55, 0x5C, 0x5D, 0x64, 0x65, 0x6C, 0x6D, - 0x74, 0x75, 0x7C, 0x7D, 0x84, 0x85, 0x8C, 0x8D, - 0x94, 0x95, 0x9C, 0x9D, 0xA4, 0xA5, 0xAC, 0xAD, - 0xB4, 0xB5, 0xBC, 0xBD, 0xC4, 0xC5, 0xCC, 0xCD, - 0xD4, 0xD5, 0xDC, 0xDD, 0xE4, 0xE5, 0xEC, 0xED, - 0xF4, 0xF5, 0xFC, 0xFD, - }; - static const u8 snum_init_46[] = { - 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, - 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, - 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, - 0xD8, 0xD9, 0xE8, 0xE9, 0x08, 0x09, 0x18, 0x19, - 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59, - 0x68, 0x69, 0x78, 0x79, 0x80, 0x81, - }; - static const u8 *snum_init; - - qe_num_of_snum = qe_get_num_of_snums(); - - if (qe_num_of_snum == 76) - snum_init = snum_init_76; - else - snum_init = snum_init_46; - - for (i = 0; i < qe_num_of_snum; i++) { - snums[i].num = snum_init[i]; - snums[i].state = QE_SNUM_STATE_FREE; - } -} - -int qe_get_snum(void) -{ - unsigned long flags; - int snum = -EBUSY; - int i; - - spin_lock_irqsave(&qe_lock, flags); - for (i = 0; i < qe_num_of_snum; i++) { - if (snums[i].state == QE_SNUM_STATE_FREE) { - snums[i].state = QE_SNUM_STATE_USED; - snum = snums[i].num; - break; - } - } - spin_unlock_irqrestore(&qe_lock, flags); - - return snum; -} -EXPORT_SYMBOL(qe_get_snum); - -void qe_put_snum(u8 snum) -{ - int i; - - for (i = 0; i < qe_num_of_snum; i++) { - if (snums[i].num == snum) { - snums[i].state = QE_SNUM_STATE_FREE; - break; - } - } -} -EXPORT_SYMBOL(qe_put_snum); - -static int qe_sdma_init(void) -{ - struct sdma __iomem *sdma = &qe_immr->sdma; - static unsigned long sdma_buf_offset = (unsigned long)-ENOMEM; - - if (!sdma) - return -ENODEV; - - /* allocate 2 internal temporary buffers (512 bytes size each) for - * the SDMA */ - if (IS_ERR_VALUE(sdma_buf_offset)) { - sdma_buf_offset = qe_muram_alloc(512 * 2, 4096); - if (IS_ERR_VALUE(sdma_buf_offset)) - return -ENOMEM; - } - - out_be32(&sdma->sdebcr, (u32) sdma_buf_offset & QE_SDEBCR_BA_MASK); - out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK | - (0x1 << QE_SDMR_CEN_SHIFT))); - - return 0; -} - -/* The maximum number of RISCs we support */ -#define MAX_QE_RISC 4 - -/* Firmware information stored here for qe_get_firmware_info() */ -static struct qe_firmware_info qe_firmware_info; - -/* - * Set to 1 if QE firmware has been uploaded, and therefore - * qe_firmware_info contains valid data. - */ -static int qe_firmware_uploaded; - -/* - * Upload a QE microcode - * - * This function is a worker function for qe_upload_firmware(). It does - * the actual uploading of the microcode. - */ -static void qe_upload_microcode(const void *base, - const struct qe_microcode *ucode) -{ - const __be32 *code = base + be32_to_cpu(ucode->code_offset); - unsigned int i; - - if (ucode->major || ucode->minor || ucode->revision) - printk(KERN_INFO "qe-firmware: " - "uploading microcode '%s' version %u.%u.%u\n", - ucode->id, ucode->major, ucode->minor, ucode->revision); - else - printk(KERN_INFO "qe-firmware: " - "uploading microcode '%s'\n", ucode->id); - - /* Use auto-increment */ - out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | - QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); - - for (i = 0; i < be32_to_cpu(ucode->count); i++) - out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); - - /* Set I-RAM Ready Register */ - out_be32(&qe_immr->iram.iready, be32_to_cpu(QE_IRAM_READY)); -} - -/* - * Upload a microcode to the I-RAM at a specific address. - * - * See Documentation/powerpc/qe_firmware.txt for information on QE microcode - * uploading. - * - * Currently, only version 1 is supported, so the 'version' field must be - * set to 1. - * - * The SOC model and revision are not validated, they are only displayed for - * informational purposes. - * - * 'calc_size' is the calculated size, in bytes, of the firmware structure and - * all of the microcode structures, minus the CRC. - * - * 'length' is the size that the structure says it is, including the CRC. - */ -int qe_upload_firmware(const struct qe_firmware *firmware) -{ - unsigned int i; - unsigned int j; - u32 crc; - size_t calc_size = sizeof(struct qe_firmware); - size_t length; - const struct qe_header *hdr; - - if (!firmware) { - printk(KERN_ERR "qe-firmware: invalid pointer\n"); - return -EINVAL; - } - - hdr = &firmware->header; - length = be32_to_cpu(hdr->length); - - /* Check the magic */ - if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || - (hdr->magic[2] != 'F')) { - printk(KERN_ERR "qe-firmware: not a microcode\n"); - return -EPERM; - } - - /* Check the version */ - if (hdr->version != 1) { - printk(KERN_ERR "qe-firmware: unsupported version\n"); - return -EPERM; - } - - /* Validate some of the fields */ - if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) { - printk(KERN_ERR "qe-firmware: invalid data\n"); - return -EINVAL; - } - - /* Validate the length and check if there's a CRC */ - calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); - - for (i = 0; i < firmware->count; i++) - /* - * For situations where the second RISC uses the same microcode - * as the first, the 'code_offset' and 'count' fields will be - * zero, so it's okay to add those. - */ - calc_size += sizeof(__be32) * - be32_to_cpu(firmware->microcode[i].count); - - /* Validate the length */ - if (length != calc_size + sizeof(__be32)) { - printk(KERN_ERR "qe-firmware: invalid length\n"); - return -EPERM; - } - - /* Validate the CRC */ - crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size)); - if (crc != crc32(0, firmware, calc_size)) { - printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n"); - return -EIO; - } - - /* - * If the microcode calls for it, split the I-RAM. - */ - if (!firmware->split) - setbits16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR); - - if (firmware->soc.model) - printk(KERN_INFO - "qe-firmware: firmware '%s' for %u V%u.%u\n", - firmware->id, be16_to_cpu(firmware->soc.model), - firmware->soc.major, firmware->soc.minor); - else - printk(KERN_INFO "qe-firmware: firmware '%s'\n", - firmware->id); - - /* - * The QE only supports one microcode per RISC, so clear out all the - * saved microcode information and put in the new. - */ - memset(&qe_firmware_info, 0, sizeof(qe_firmware_info)); - strcpy(qe_firmware_info.id, firmware->id); - qe_firmware_info.extended_modes = firmware->extended_modes; - memcpy(qe_firmware_info.vtraps, firmware->vtraps, - sizeof(firmware->vtraps)); - - /* Loop through each microcode. */ - for (i = 0; i < firmware->count; i++) { - const struct qe_microcode *ucode = &firmware->microcode[i]; - - /* Upload a microcode if it's present */ - if (ucode->code_offset) - qe_upload_microcode(firmware, ucode); - - /* Program the traps for this processor */ - for (j = 0; j < 16; j++) { - u32 trap = be32_to_cpu(ucode->traps[j]); - - if (trap) - out_be32(&qe_immr->rsp[i].tibcr[j], trap); - } - - /* Enable traps */ - out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); - } - - qe_firmware_uploaded = 1; - - return 0; -} -EXPORT_SYMBOL(qe_upload_firmware); - -/* - * Get info on the currently-loaded firmware - * - * This function also checks the device tree to see if the boot loader has - * uploaded a firmware already. - */ -struct qe_firmware_info *qe_get_firmware_info(void) -{ - static int initialized; - struct property *prop; - struct device_node *qe; - struct device_node *fw = NULL; - const char *sprop; - unsigned int i; - - /* - * If we haven't checked yet, and a driver hasn't uploaded a firmware - * yet, then check the device tree for information. - */ - if (qe_firmware_uploaded) - return &qe_firmware_info; - - if (initialized) - return NULL; - - initialized = 1; - - /* - * Newer device trees have an "fsl,qe" compatible property for the QE - * node, but we still need to support older device trees. - */ - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return NULL; - } - - /* Find the 'firmware' child node */ - for_each_child_of_node(qe, fw) { - if (strcmp(fw->name, "firmware") == 0) - break; - } - - of_node_put(qe); - - /* Did we find the 'firmware' node? */ - if (!fw) - return NULL; - - qe_firmware_uploaded = 1; - - /* Copy the data into qe_firmware_info*/ - sprop = of_get_property(fw, "id", NULL); - if (sprop) - strncpy(qe_firmware_info.id, sprop, - sizeof(qe_firmware_info.id) - 1); - - prop = of_find_property(fw, "extended-modes", NULL); - if (prop && (prop->length == sizeof(u64))) { - const u64 *iprop = prop->value; - - qe_firmware_info.extended_modes = *iprop; - } - - prop = of_find_property(fw, "virtual-traps", NULL); - if (prop && (prop->length == 32)) { - const u32 *iprop = prop->value; - - for (i = 0; i < ARRAY_SIZE(qe_firmware_info.vtraps); i++) - qe_firmware_info.vtraps[i] = iprop[i]; - } - - of_node_put(fw); - - return &qe_firmware_info; -} -EXPORT_SYMBOL(qe_get_firmware_info); - -unsigned int qe_get_num_of_risc(void) -{ - struct device_node *qe; - int size; - unsigned int num_of_risc = 0; - const u32 *prop; - - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - /* Older devices trees did not have an "fsl,qe" - * compatible property, so we need to look for - * the QE node by name. - */ - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return num_of_risc; - } - - prop = of_get_property(qe, "fsl,qe-num-riscs", &size); - if (prop && size == sizeof(*prop)) - num_of_risc = *prop; - - of_node_put(qe); - - return num_of_risc; -} -EXPORT_SYMBOL(qe_get_num_of_risc); - -unsigned int qe_get_num_of_snums(void) -{ - struct device_node *qe; - int size; - unsigned int num_of_snums; - const u32 *prop; - - num_of_snums = 28; /* The default number of snum for threads is 28 */ - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - /* Older devices trees did not have an "fsl,qe" - * compatible property, so we need to look for - * the QE node by name. - */ - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return num_of_snums; - } - - prop = of_get_property(qe, "fsl,qe-num-snums", &size); - if (prop && size == sizeof(*prop)) { - num_of_snums = *prop; - if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) { - /* No QE ever has fewer than 28 SNUMs */ - pr_err("QE: number of snum is invalid\n"); - of_node_put(qe); - return -EINVAL; - } - } - - of_node_put(qe); - - return num_of_snums; -} -EXPORT_SYMBOL(qe_get_num_of_snums); - -#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) -static int qe_resume(struct platform_device *ofdev) -{ - if (!qe_alive_during_sleep()) - qe_reset(); - return 0; -} - -static int qe_probe(struct platform_device *ofdev) -{ - return 0; -} - -static const struct of_device_id qe_ids[] = { - { .compatible = "fsl,qe", }, - { }, -}; - -static struct platform_driver qe_driver = { - .driver = { - .name = "fsl-qe", - .owner = THIS_MODULE, - .of_match_table = qe_ids, - }, - .probe = qe_probe, - .resume = qe_resume, -}; - -static int __init qe_drv_init(void) -{ - return platform_driver_register(&qe_driver); -} -device_initcall(qe_drv_init); -#endif /* defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) */ diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c deleted file mode 100644 index b2b87c3..0000000 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/qe_ic.c - * - * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Li Yang - * Based on code from Shlomi Gridish - * - * QUICC ENGINE Interrupt Controller - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qe_ic.h" - -static DEFINE_RAW_SPINLOCK(qe_ic_lock); - -static struct qe_ic_info qe_ic_info[] = { - [1] = { - .mask = 0x00008000, - .mask_reg = QEIC_CIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPWCC, - }, - [2] = { - .mask = 0x00004000, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPWCC, - }, - [3] = { - .mask = 0x00002000, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPWCC, - }, - [10] = { - .mask = 0x00000040, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPZCC, - }, - [11] = { - .mask = 0x00000020, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPZCC, - }, - [12] = { - .mask = 0x00000010, - .mask_reg = QEIC_CIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPZCC, - }, - [13] = { - .mask = 0x00000008, - .mask_reg = QEIC_CIMR, - .pri_code = 4, - .pri_reg = QEIC_CIPZCC, - }, - [14] = { - .mask = 0x00000004, - .mask_reg = QEIC_CIMR, - .pri_code = 5, - .pri_reg = QEIC_CIPZCC, - }, - [15] = { - .mask = 0x00000002, - .mask_reg = QEIC_CIMR, - .pri_code = 6, - .pri_reg = QEIC_CIPZCC, - }, - [20] = { - .mask = 0x10000000, - .mask_reg = QEIC_CRIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPRTA, - }, - [25] = { - .mask = 0x00800000, - .mask_reg = QEIC_CRIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPRTB, - }, - [26] = { - .mask = 0x00400000, - .mask_reg = QEIC_CRIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPRTB, - }, - [27] = { - .mask = 0x00200000, - .mask_reg = QEIC_CRIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPRTB, - }, - [28] = { - .mask = 0x00100000, - .mask_reg = QEIC_CRIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPRTB, - }, - [32] = { - .mask = 0x80000000, - .mask_reg = QEIC_CIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPXCC, - }, - [33] = { - .mask = 0x40000000, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPXCC, - }, - [34] = { - .mask = 0x20000000, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPXCC, - }, - [35] = { - .mask = 0x10000000, - .mask_reg = QEIC_CIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPXCC, - }, - [36] = { - .mask = 0x08000000, - .mask_reg = QEIC_CIMR, - .pri_code = 4, - .pri_reg = QEIC_CIPXCC, - }, - [40] = { - .mask = 0x00800000, - .mask_reg = QEIC_CIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPYCC, - }, - [41] = { - .mask = 0x00400000, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPYCC, - }, - [42] = { - .mask = 0x00200000, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPYCC, - }, - [43] = { - .mask = 0x00100000, - .mask_reg = QEIC_CIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPYCC, - }, -}; - -static inline u32 qe_ic_read(volatile __be32 __iomem * base, unsigned int reg) -{ - return in_be32(base + (reg >> 2)); -} - -static inline void qe_ic_write(volatile __be32 __iomem * base, unsigned int reg, - u32 value) -{ - out_be32(base + (reg >> 2), value); -} - -static inline struct qe_ic *qe_ic_from_irq(unsigned int virq) -{ - return irq_get_chip_data(virq); -} - -static inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d) -{ - return irq_data_get_irq_chip_data(d); -} - -static void qe_ic_unmask_irq(struct irq_data *d) -{ - struct qe_ic *qe_ic = qe_ic_from_irq_data(d); - unsigned int src = irqd_to_hwirq(d); - unsigned long flags; - u32 temp; - - raw_spin_lock_irqsave(&qe_ic_lock, flags); - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); - qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, - temp | qe_ic_info[src].mask); - - raw_spin_unlock_irqrestore(&qe_ic_lock, flags); -} - -static void qe_ic_mask_irq(struct irq_data *d) -{ - struct qe_ic *qe_ic = qe_ic_from_irq_data(d); - unsigned int src = irqd_to_hwirq(d); - unsigned long flags; - u32 temp; - - raw_spin_lock_irqsave(&qe_ic_lock, flags); - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); - qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, - temp & ~qe_ic_info[src].mask); - - /* Flush the above write before enabling interrupts; otherwise, - * spurious interrupts will sometimes happen. To be 100% sure - * that the write has reached the device before interrupts are - * enabled, the mask register would have to be read back; however, - * this is not required for correctness, only to avoid wasting - * time on a large number of spurious interrupts. In testing, - * a sync reduced the observed spurious interrupts to zero. - */ - mb(); - - raw_spin_unlock_irqrestore(&qe_ic_lock, flags); -} - -static struct irq_chip qe_ic_irq_chip = { - .name = "QEIC", - .irq_unmask = qe_ic_unmask_irq, - .irq_mask = qe_ic_mask_irq, - .irq_mask_ack = qe_ic_mask_irq, -}; - -static int qe_ic_host_match(struct irq_domain *h, struct device_node *node) -{ - /* Exact match, unless qe_ic node is NULL */ - return h->of_node == NULL || h->of_node == node; -} - -static int qe_ic_host_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct qe_ic *qe_ic = h->host_data; - struct irq_chip *chip; - - if (qe_ic_info[hw].mask == 0) { - printk(KERN_ERR "Can't map reserved IRQ\n"); - return -EINVAL; - } - /* Default chip */ - chip = &qe_ic->hc_irq; - - irq_set_chip_data(virq, qe_ic); - irq_set_status_flags(virq, IRQ_LEVEL); - - irq_set_chip_and_handler(virq, chip, handle_level_irq); - - return 0; -} - -static struct irq_domain_ops qe_ic_host_ops = { - .match = qe_ic_host_match, - .map = qe_ic_host_map, - .xlate = irq_domain_xlate_onetwocell, -}; - -/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ -unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic) -{ - int irq; - - BUG_ON(qe_ic == NULL); - - /* get the interrupt source vector. */ - irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26; - - if (irq == 0) - return NO_IRQ; - - return irq_linear_revmap(qe_ic->irqhost, irq); -} - -/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ -unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) -{ - int irq; - - BUG_ON(qe_ic == NULL); - - /* get the interrupt source vector. */ - irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26; - - if (irq == 0) - return NO_IRQ; - - return irq_linear_revmap(qe_ic->irqhost, irq); -} - -void __init qe_ic_init(struct device_node *node, unsigned int flags, - void (*low_handler)(unsigned int irq, struct irq_desc *desc), - void (*high_handler)(unsigned int irq, struct irq_desc *desc)) -{ - struct qe_ic *qe_ic; - struct resource res; - u32 temp = 0, ret, high_active = 0; - - ret = of_address_to_resource(node, 0, &res); - if (ret) - return; - - qe_ic = kzalloc(sizeof(*qe_ic), GFP_KERNEL); - if (qe_ic == NULL) - return; - - qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS, - &qe_ic_host_ops, qe_ic); - if (qe_ic->irqhost == NULL) { - kfree(qe_ic); - return; - } - - qe_ic->regs = ioremap(res.start, resource_size(&res)); - - qe_ic->hc_irq = qe_ic_irq_chip; - - qe_ic->virq_high = irq_of_parse_and_map(node, 0); - qe_ic->virq_low = irq_of_parse_and_map(node, 1); - - if (qe_ic->virq_low == NO_IRQ) { - printk(KERN_ERR "Failed to map QE_IC low IRQ\n"); - kfree(qe_ic); - return; - } - - /* default priority scheme is grouped. If spread mode is */ - /* required, configure cicr accordingly. */ - if (flags & QE_IC_SPREADMODE_GRP_W) - temp |= CICR_GWCC; - if (flags & QE_IC_SPREADMODE_GRP_X) - temp |= CICR_GXCC; - if (flags & QE_IC_SPREADMODE_GRP_Y) - temp |= CICR_GYCC; - if (flags & QE_IC_SPREADMODE_GRP_Z) - temp |= CICR_GZCC; - if (flags & QE_IC_SPREADMODE_GRP_RISCA) - temp |= CICR_GRTA; - if (flags & QE_IC_SPREADMODE_GRP_RISCB) - temp |= CICR_GRTB; - - /* choose destination signal for highest priority interrupt */ - if (flags & QE_IC_HIGH_SIGNAL) { - temp |= (SIGNAL_HIGH << CICR_HPIT_SHIFT); - high_active = 1; - } - - qe_ic_write(qe_ic->regs, QEIC_CICR, temp); - - irq_set_handler_data(qe_ic->virq_low, qe_ic); - irq_set_chained_handler(qe_ic->virq_low, low_handler); - - if (qe_ic->virq_high != NO_IRQ && - qe_ic->virq_high != qe_ic->virq_low) { - irq_set_handler_data(qe_ic->virq_high, qe_ic); - irq_set_chained_handler(qe_ic->virq_high, high_handler); - } -} - -void qe_ic_set_highest_priority(unsigned int virq, int high) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - u32 temp = 0; - - temp = qe_ic_read(qe_ic->regs, QEIC_CICR); - - temp &= ~CICR_HP_MASK; - temp |= src << CICR_HP_SHIFT; - - temp &= ~CICR_HPIT_MASK; - temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << CICR_HPIT_SHIFT; - - qe_ic_write(qe_ic->regs, QEIC_CICR, temp); -} - -/* Set Priority level within its group, from 1 to 8 */ -int qe_ic_set_priority(unsigned int virq, unsigned int priority) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - u32 temp; - - if (priority > 8 || priority == 0) - return -EINVAL; - if (src > 127) - return -EINVAL; - if (qe_ic_info[src].pri_reg == 0) - return -EINVAL; - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].pri_reg); - - if (priority < 4) { - temp &= ~(0x7 << (32 - priority * 3)); - temp |= qe_ic_info[src].pri_code << (32 - priority * 3); - } else { - temp &= ~(0x7 << (24 - priority * 3)); - temp |= qe_ic_info[src].pri_code << (24 - priority * 3); - } - - qe_ic_write(qe_ic->regs, qe_ic_info[src].pri_reg, temp); - - return 0; -} - -/* Set a QE priority to use high irq, only priority 1~2 can use high irq */ -int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - u32 temp, control_reg = QEIC_CICNR, shift = 0; - - if (priority > 2 || priority == 0) - return -EINVAL; - - switch (qe_ic_info[src].pri_reg) { - case QEIC_CIPZCC: - shift = CICNR_ZCC1T_SHIFT; - break; - case QEIC_CIPWCC: - shift = CICNR_WCC1T_SHIFT; - break; - case QEIC_CIPYCC: - shift = CICNR_YCC1T_SHIFT; - break; - case QEIC_CIPXCC: - shift = CICNR_XCC1T_SHIFT; - break; - case QEIC_CIPRTA: - shift = CRICR_RTA1T_SHIFT; - control_reg = QEIC_CRICR; - break; - case QEIC_CIPRTB: - shift = CRICR_RTB1T_SHIFT; - control_reg = QEIC_CRICR; - break; - default: - return -EINVAL; - } - - shift += (2 - priority) * 2; - temp = qe_ic_read(qe_ic->regs, control_reg); - temp &= ~(SIGNAL_MASK << shift); - temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << shift; - qe_ic_write(qe_ic->regs, control_reg, temp); - - return 0; -} - -static struct bus_type qe_ic_subsys = { - .name = "qe_ic", - .dev_name = "qe_ic", -}; - -static struct device device_qe_ic = { - .id = 0, - .bus = &qe_ic_subsys, -}; - -static int __init init_qe_ic_sysfs(void) -{ - int rc; - - printk(KERN_DEBUG "Registering qe_ic with sysfs...\n"); - - rc = subsys_system_register(&qe_ic_subsys, NULL); - if (rc) { - printk(KERN_ERR "Failed registering qe_ic sys class\n"); - return -ENODEV; - } - rc = device_register(&device_qe_ic); - if (rc) { - printk(KERN_ERR "Failed registering qe_ic sys device\n"); - return -ENODEV; - } - return 0; -} - -subsys_initcall(init_qe_ic_sysfs); diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/arch/powerpc/sysdev/qe_lib/qe_ic.h deleted file mode 100644 index efef7ab..0000000 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/qe_ic.h - * - * QUICC ENGINE Interrupt Controller Header - * - * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Li Yang - * Based on code from Shlomi Gridish - * - * 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. - */ -#ifndef _POWERPC_SYSDEV_QE_IC_H -#define _POWERPC_SYSDEV_QE_IC_H - -#include - -#define NR_QE_IC_INTS 64 - -/* QE IC registers offset */ -#define QEIC_CICR 0x00 -#define QEIC_CIVEC 0x04 -#define QEIC_CRIPNR 0x08 -#define QEIC_CIPNR 0x0c -#define QEIC_CIPXCC 0x10 -#define QEIC_CIPYCC 0x14 -#define QEIC_CIPWCC 0x18 -#define QEIC_CIPZCC 0x1c -#define QEIC_CIMR 0x20 -#define QEIC_CRIMR 0x24 -#define QEIC_CICNR 0x28 -#define QEIC_CIPRTA 0x30 -#define QEIC_CIPRTB 0x34 -#define QEIC_CRICR 0x3c -#define QEIC_CHIVEC 0x60 - -/* Interrupt priority registers */ -#define CIPCC_SHIFT_PRI0 29 -#define CIPCC_SHIFT_PRI1 26 -#define CIPCC_SHIFT_PRI2 23 -#define CIPCC_SHIFT_PRI3 20 -#define CIPCC_SHIFT_PRI4 13 -#define CIPCC_SHIFT_PRI5 10 -#define CIPCC_SHIFT_PRI6 7 -#define CIPCC_SHIFT_PRI7 4 - -/* CICR priority modes */ -#define CICR_GWCC 0x00040000 -#define CICR_GXCC 0x00020000 -#define CICR_GYCC 0x00010000 -#define CICR_GZCC 0x00080000 -#define CICR_GRTA 0x00200000 -#define CICR_GRTB 0x00400000 -#define CICR_HPIT_SHIFT 8 -#define CICR_HPIT_MASK 0x00000300 -#define CICR_HP_SHIFT 24 -#define CICR_HP_MASK 0x3f000000 - -/* CICNR */ -#define CICNR_WCC1T_SHIFT 20 -#define CICNR_ZCC1T_SHIFT 28 -#define CICNR_YCC1T_SHIFT 12 -#define CICNR_XCC1T_SHIFT 4 - -/* CRICR */ -#define CRICR_RTA1T_SHIFT 20 -#define CRICR_RTB1T_SHIFT 28 - -/* Signal indicator */ -#define SIGNAL_MASK 3 -#define SIGNAL_HIGH 2 -#define SIGNAL_LOW 0 - -struct qe_ic { - /* Control registers offset */ - volatile u32 __iomem *regs; - - /* The remapper for this QEIC */ - struct irq_domain *irqhost; - - /* The "linux" controller struct */ - struct irq_chip hc_irq; - - /* VIRQ numbers of QE high/low irqs */ - unsigned int virq_high; - unsigned int virq_low; -}; - -/* - * QE interrupt controller internal structure - */ -struct qe_ic_info { - u32 mask; /* location of this source at the QIMR register. */ - u32 mask_reg; /* Mask register offset */ - u8 pri_code; /* for grouped interrupts sources - the interrupt - code as appears at the group priority register */ - u32 pri_reg; /* Group priority register offset */ -}; - -#endif /* _POWERPC_SYSDEV_QE_IC_H */ diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c deleted file mode 100644 index a88807b..0000000 --- a/arch/powerpc/sysdev/qe_lib/qe_io.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/qe_io.c - * - * QE Parallel I/O ports configuration routines - * - * Copyright 2006 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Li Yang - * Based on code from Shlomi Gridish - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#undef DEBUG - -static struct qe_pio_regs __iomem *par_io; -static int num_par_io_ports = 0; - -int par_io_init(struct device_node *np) -{ - struct resource res; - int ret; - const u32 *num_ports; - - /* Map Parallel I/O ports registers */ - ret = of_address_to_resource(np, 0, &res); - if (ret) - return ret; - par_io = ioremap(res.start, resource_size(&res)); - - num_ports = of_get_property(np, "num-ports", NULL); - if (num_ports) - num_par_io_ports = *num_ports; - - return 0; -} - -void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, - int open_drain, int assignment, int has_irq) -{ - u32 pin_mask1bit; - u32 pin_mask2bits; - u32 new_mask2bits; - u32 tmp_val; - - /* calculate pin location for single and 2 bits information */ - pin_mask1bit = (u32) (1 << (QE_PIO_PINS - (pin + 1))); - - /* Set open drain, if required */ - tmp_val = in_be32(&par_io->cpodr); - if (open_drain) - out_be32(&par_io->cpodr, pin_mask1bit | tmp_val); - else - out_be32(&par_io->cpodr, ~pin_mask1bit & tmp_val); - - /* define direction */ - tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? - in_be32(&par_io->cpdir2) : - in_be32(&par_io->cpdir1); - - /* get all bits mask for 2 bit per port */ - pin_mask2bits = (u32) (0x3 << (QE_PIO_PINS - - (pin % (QE_PIO_PINS / 2) + 1) * 2)); - - /* Get the final mask we need for the right definition */ - new_mask2bits = (u32) (dir << (QE_PIO_PINS - - (pin % (QE_PIO_PINS / 2) + 1) * 2)); - - /* clear and set 2 bits mask */ - if (pin > (QE_PIO_PINS / 2) - 1) { - out_be32(&par_io->cpdir2, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cpdir2, new_mask2bits | tmp_val); - } else { - out_be32(&par_io->cpdir1, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cpdir1, new_mask2bits | tmp_val); - } - /* define pin assignment */ - tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? - in_be32(&par_io->cppar2) : - in_be32(&par_io->cppar1); - - new_mask2bits = (u32) (assignment << (QE_PIO_PINS - - (pin % (QE_PIO_PINS / 2) + 1) * 2)); - /* clear and set 2 bits mask */ - if (pin > (QE_PIO_PINS / 2) - 1) { - out_be32(&par_io->cppar2, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cppar2, new_mask2bits | tmp_val); - } else { - out_be32(&par_io->cppar1, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cppar1, new_mask2bits | tmp_val); - } -} -EXPORT_SYMBOL(__par_io_config_pin); - -int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, - int assignment, int has_irq) -{ - if (!par_io || port >= num_par_io_ports) - return -EINVAL; - - __par_io_config_pin(&par_io[port], pin, dir, open_drain, assignment, - has_irq); - return 0; -} -EXPORT_SYMBOL(par_io_config_pin); - -int par_io_data_set(u8 port, u8 pin, u8 val) -{ - u32 pin_mask, tmp_val; - - if (port >= num_par_io_ports) - return -EINVAL; - if (pin >= QE_PIO_PINS) - return -EINVAL; - /* calculate pin location */ - pin_mask = (u32) (1 << (QE_PIO_PINS - 1 - pin)); - - tmp_val = in_be32(&par_io[port].cpdata); - - if (val == 0) /* clear */ - out_be32(&par_io[port].cpdata, ~pin_mask & tmp_val); - else /* set */ - out_be32(&par_io[port].cpdata, pin_mask | tmp_val); - - return 0; -} -EXPORT_SYMBOL(par_io_data_set); - -int par_io_of_config(struct device_node *np) -{ - struct device_node *pio; - const phandle *ph; - int pio_map_len; - const unsigned int *pio_map; - - if (par_io == NULL) { - printk(KERN_ERR "par_io not initialized\n"); - return -1; - } - - ph = of_get_property(np, "pio-handle", NULL); - if (ph == NULL) { - printk(KERN_ERR "pio-handle not available\n"); - return -1; - } - - pio = of_find_node_by_phandle(*ph); - - pio_map = of_get_property(pio, "pio-map", &pio_map_len); - if (pio_map == NULL) { - printk(KERN_ERR "pio-map is not set!\n"); - return -1; - } - pio_map_len /= sizeof(unsigned int); - if ((pio_map_len % 6) != 0) { - printk(KERN_ERR "pio-map format wrong!\n"); - return -1; - } - - while (pio_map_len > 0) { - par_io_config_pin((u8) pio_map[0], (u8) pio_map[1], - (int) pio_map[2], (int) pio_map[3], - (int) pio_map[4], (int) pio_map[5]); - pio_map += 6; - pio_map_len -= 6; - } - of_node_put(pio); - return 0; -} -EXPORT_SYMBOL(par_io_of_config); - -#ifdef DEBUG -static void dump_par_io(void) -{ - unsigned int i; - - printk(KERN_INFO "%s: par_io=%p\n", __func__, par_io); - for (i = 0; i < num_par_io_ports; i++) { - printk(KERN_INFO " cpodr[%u]=%08x\n", i, - in_be32(&par_io[i].cpodr)); - printk(KERN_INFO " cpdata[%u]=%08x\n", i, - in_be32(&par_io[i].cpdata)); - printk(KERN_INFO " cpdir1[%u]=%08x\n", i, - in_be32(&par_io[i].cpdir1)); - printk(KERN_INFO " cpdir2[%u]=%08x\n", i, - in_be32(&par_io[i].cpdir2)); - printk(KERN_INFO " cppar1[%u]=%08x\n", i, - in_be32(&par_io[i].cppar1)); - printk(KERN_INFO " cppar2[%u]=%08x\n", i, - in_be32(&par_io[i].cppar2)); - } - -} -EXPORT_SYMBOL(dump_par_io); -#endif /* DEBUG */ diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c deleted file mode 100644 index 1e27d63..0000000 --- a/arch/powerpc/sysdev/qe_lib/ucc.c +++ /dev/null @@ -1,984 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/ucc.c - * - * QE UCC API Set - UCC specific routines implementations. - * - * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -int ucc_set_qe_mux_mii_mng(unsigned int ucc_num) -{ - unsigned long flags; - - if (ucc_num > UCC_MAX_NUM - 1) - return -EINVAL; - - spin_lock_irqsave(&cmxgcr_lock, flags); - clrsetbits_be32(&qe_immr->qmx.cmxgcr, QE_CMXGCR_MII_ENET_MNG, - ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT); - spin_unlock_irqrestore(&cmxgcr_lock, flags); - - return 0; -} -EXPORT_SYMBOL(ucc_set_qe_mux_mii_mng); - -/* Configure the UCC to either Slow or Fast. - * - * A given UCC can be figured to support either "slow" devices (e.g. UART) - * or "fast" devices (e.g. Ethernet). - * - * 'ucc_num' is the UCC number, from 0 - 7. - * - * This function also sets the UCC_GUEMR_SET_RESERVED3 bit because that bit - * must always be set to 1. - */ -int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed) -{ - u8 __iomem *guemr; - - /* The GUEMR register is at the same location for both slow and fast - devices, so we just use uccX.slow.guemr. */ - switch (ucc_num) { - case 0: guemr = &qe_immr->ucc1.slow.guemr; - break; - case 1: guemr = &qe_immr->ucc2.slow.guemr; - break; - case 2: guemr = &qe_immr->ucc3.slow.guemr; - break; - case 3: guemr = &qe_immr->ucc4.slow.guemr; - break; - case 4: guemr = &qe_immr->ucc5.slow.guemr; - break; - case 5: guemr = &qe_immr->ucc6.slow.guemr; - break; - case 6: guemr = &qe_immr->ucc7.slow.guemr; - break; - case 7: guemr = &qe_immr->ucc8.slow.guemr; - break; - default: - return -EINVAL; - } - - clrsetbits_8(guemr, UCC_GUEMR_MODE_MASK, - UCC_GUEMR_SET_RESERVED3 | speed); - - return 0; -} - -static void get_cmxucr_reg(unsigned int ucc_num, __be32 __iomem **cmxucr, - unsigned int *reg_num, unsigned int *shift) -{ - unsigned int cmx = ((ucc_num & 1) << 1) + (ucc_num > 3); - - *reg_num = cmx + 1; - *cmxucr = &qe_immr->qmx.cmxucr[cmx]; - *shift = 16 - 8 * (ucc_num & 2); -} - -int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask) -{ - __be32 __iomem *cmxucr; - unsigned int reg_num; - unsigned int shift; - - /* check if the UCC number is in range. */ - if (ucc_num > UCC_MAX_NUM - 1) - return -EINVAL; - - get_cmxucr_reg(ucc_num, &cmxucr, ®_num, &shift); - - if (set) - setbits32(cmxucr, mask << shift); - else - clrbits32(cmxucr, mask << shift); - - return 0; -} - -int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, - enum comm_dir mode) -{ - __be32 __iomem *cmxucr; - unsigned int reg_num; - unsigned int shift; - u32 clock_bits = 0; - - /* check if the UCC number is in range. */ - if (ucc_num > UCC_MAX_NUM - 1) - return -EINVAL; - - /* The communications direction must be RX or TX */ - if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) - return -EINVAL; - - get_cmxucr_reg(ucc_num, &cmxucr, ®_num, &shift); - - switch (reg_num) { - case 1: - switch (clock) { - case QE_BRG1: clock_bits = 1; break; - case QE_BRG2: clock_bits = 2; break; - case QE_BRG7: clock_bits = 3; break; - case QE_BRG8: clock_bits = 4; break; - case QE_CLK9: clock_bits = 5; break; - case QE_CLK10: clock_bits = 6; break; - case QE_CLK11: clock_bits = 7; break; - case QE_CLK12: clock_bits = 8; break; - case QE_CLK15: clock_bits = 9; break; - case QE_CLK16: clock_bits = 10; break; - default: break; - } - break; - case 2: - switch (clock) { - case QE_BRG5: clock_bits = 1; break; - case QE_BRG6: clock_bits = 2; break; - case QE_BRG7: clock_bits = 3; break; - case QE_BRG8: clock_bits = 4; break; - case QE_CLK13: clock_bits = 5; break; - case QE_CLK14: clock_bits = 6; break; - case QE_CLK19: clock_bits = 7; break; - case QE_CLK20: clock_bits = 8; break; - case QE_CLK15: clock_bits = 9; break; - case QE_CLK16: clock_bits = 10; break; - default: break; - } - break; - case 3: - switch (clock) { - case QE_BRG9: clock_bits = 1; break; - case QE_BRG10: clock_bits = 2; break; - case QE_BRG15: clock_bits = 3; break; - case QE_BRG16: clock_bits = 4; break; - case QE_CLK3: clock_bits = 5; break; - case QE_CLK4: clock_bits = 6; break; - case QE_CLK17: clock_bits = 7; break; - case QE_CLK18: clock_bits = 8; break; - case QE_CLK7: clock_bits = 9; break; - case QE_CLK8: clock_bits = 10; break; - case QE_CLK16: clock_bits = 11; break; - default: break; - } - break; - case 4: - switch (clock) { - case QE_BRG13: clock_bits = 1; break; - case QE_BRG14: clock_bits = 2; break; - case QE_BRG15: clock_bits = 3; break; - case QE_BRG16: clock_bits = 4; break; - case QE_CLK5: clock_bits = 5; break; - case QE_CLK6: clock_bits = 6; break; - case QE_CLK21: clock_bits = 7; break; - case QE_CLK22: clock_bits = 8; break; - case QE_CLK7: clock_bits = 9; break; - case QE_CLK8: clock_bits = 10; break; - case QE_CLK16: clock_bits = 11; break; - default: break; - } - break; - default: break; - } - - /* Check for invalid combination of clock and UCC number */ - if (!clock_bits) - return -ENOENT; - - if (mode == COMM_DIR_RX) - shift += 4; - - clrsetbits_be32(cmxucr, QE_CMXUCR_TX_CLK_SRC_MASK << shift, - clock_bits << shift); - - return 0; -} - -/* tdm_num: TDM A-H port num is 0-7 */ -int ucc_set_tdm_rxtx_clk(u32 tdm_num, enum qe_clock clock, - enum comm_dir mode) -{ - u32 clock_bits, shift; - struct qe_mux *qe_mux_reg; - __be32 __iomem *cmxs1cr; - - clock_bits = 0; - qe_mux_reg = &qe_immr->qmx; - - if ((tdm_num > 7 || tdm_num < 0)) - return -EINVAL; - - /* The communications direction must be RX or TX */ - if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) - return -EINVAL; - - switch (mode) { - case COMM_DIR_RX: - switch (tdm_num) { - case 0: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK3: - clock_bits = 6; - break; - case QE_CLK8: - clock_bits = 7; - break; - default: - break; - } - shift = 28; - break; - case 1: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK5: - clock_bits = 6; - break; - case QE_CLK10: - clock_bits = 7; - break; - default: - break; - } - shift = 24; - break; - case 2: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK7: - clock_bits = 6; - break; - case QE_CLK12: - clock_bits = 7; - break; - default: - break; - } - shift = 20; - break; - case 3: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK9: - clock_bits = 6; - break; - case QE_CLK14: - clock_bits = 7; - break; - default: - break; - } - shift = 16; - break; - case 4: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK11: - clock_bits = 6; - break; - case QE_CLK16: - clock_bits = 7; - break; - default: - break; - } - shift = 28; - break; - case 5: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK13: - clock_bits = 6; - break; - case QE_CLK18: - clock_bits = 7; - break; - default: - break; - } - shift = 24; - break; - case 6: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK15: - clock_bits = 6; - break; - case QE_CLK20: - clock_bits = 7; - break; - default: - break; - } - shift = 20; - break; - case 7: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK17: - clock_bits = 6; - break; - case QE_CLK22: - clock_bits = 7; - break; - default: - break; - } - shift = 16; - break; - default: - break; - } - break; - case COMM_DIR_TX: - switch (tdm_num) { - case 0: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK4: - clock_bits = 6; - break; - case QE_CLK9: - clock_bits = 7; - break; - default: - break; - } - shift = 12; - break; - case 1: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK6: - clock_bits = 6; - break; - case QE_CLK11: - clock_bits = 7; - break; - default: - break; - } - shift = 8; - break; - case 2: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK8: - clock_bits = 6; - break; - case QE_CLK13: - clock_bits = 7; - break; - default: - break; - } - shift = 4; - break; - case 3: - switch (clock) { - case QE_BRG3: - clock_bits = 1; - break; - case QE_BRG4: - clock_bits = 2; - break; - case QE_CLK1: - clock_bits = 4; - break; - case QE_CLK2: - clock_bits = 5; - break; - case QE_CLK10: - clock_bits = 6; - break; - case QE_CLK15: - clock_bits = 7; - break; - default: - break; - } - shift = 0; - break; - case 4: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK12: - clock_bits = 6; - break; - case QE_CLK17: - clock_bits = 7; - break; - default: - break; - } - shift = 12; - break; - case 5: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK14: - clock_bits = 6; - break; - case QE_CLK19: - clock_bits = 7; - break; - default: - break; - } - shift = 8; - break; - case 6: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK16: - clock_bits = 6; - break; - case QE_CLK21: - clock_bits = 7; - break; - default: - break; - } - shift = 4; - break; - case 7: - switch (clock) { - case QE_BRG12: - clock_bits = 1; - break; - case QE_BRG13: - clock_bits = 2; - break; - case QE_CLK23: - clock_bits = 4; - break; - case QE_CLK24: - clock_bits = 5; - break; - case QE_CLK18: - clock_bits = 6; - break; - case QE_CLK3: - clock_bits = 7; - break; - default: - break; - } - shift = 0; - break; - default: - break; - } - break; - default: - break; - } - - if (!clock_bits) - return -ENOENT; - - cmxs1cr = (tdm_num < 4) ? (&qe_mux_reg->cmxsi1cr_l) : - (&qe_mux_reg->cmxsi1cr_h); - - clrsetbits_be32(cmxs1cr, QE_CMXUCR_TX_CLK_SRC_MASK << shift, - clock_bits << shift); - - return 0; -} - - -int ucc_set_tdm_rxtx_sync(u32 tdm_num, enum qe_clock clock, - enum comm_dir mode) -{ - u32 shift, clock_bits; - struct qe_mux *qe_mux_reg; - int source; - - source = 0; - shift = 0; - qe_mux_reg = &qe_immr->qmx; - - if ((tdm_num > 7 || tdm_num < 0)) - return -EINVAL; - - /* The communications direction must be RX or TX */ - if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) - return -EINVAL; - - switch (mode) { - case COMM_DIR_RX: - switch (tdm_num) { - case 0: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG10: - source = 2; - break; - default: - source = -1; - break; - } - shift = 30; - break; - case 1: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG10: - source = 2; - break; - default: - source = -1; - break; - } - shift = 28; - break; - case 2: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG11: - source = 2; - break; - default: - source = -1; - break; - } - shift = 26; - break; - case 3: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG11: - source = 2; - break; - default: - source = -1; - break; - } - shift = 24; - break; - case 4: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG14: - source = 2; - break; - default: - source = -1; - break; - } - shift = 22; - break; - case 5: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG14: - source = 2; - break; - default: - source = -1; - break; - } - shift = 20; - break; - case 6: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG15: - source = 2; - break; - default: - source = -1; - break; - } - shift = 18; - break; - case 7: - switch (clock) { - case QE_RSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG15: - source = 2; - break; - default: - source = -1; - break; - } - shift = 16; - break; - default: - source = -1; - break; - } - break; - case COMM_DIR_TX: - switch (tdm_num) { - case 0: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG10: - source = 2; - break; - default: - source = -1; - break; - } - shift = 14; - break; - case 1: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG10: - source = 2; - break; - default: - source = -1; - break; - } - shift = 12; - break; - case 2: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG11: - source = 2; - break; - default: - source = -1; - break; - } - shift = 10; - break; - case 3: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG9: - source = 1; - break; - case QE_BRG11: - source = 2; - break; - default: - source = -1; - break; - } - shift = 8; - break; - case 4: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG14: - source = 2; - break; - default: - source = -1; - break; - } - shift = 6; - break; - case 5: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG14: - source = 2; - break; - default: - source = -1; - break; - } - shift = 4; - break; - case 6: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG15: - source = 2; - break; - default: - source = -1; - break; - } - shift = 2; - break; - case 7: - switch (clock) { - case QE_TSYNC_PIN: - source = 0; - break; - case QE_BRG13: - source = 1; - break; - case QE_BRG15: - source = 2; - break; - default: - source = -1; - break; - } - shift = 0; - break; - - default: - source = -1; - break; - } - break; - default: - source = -1; - break; - } - - if (source == -1) - return -ENOENT; - - clock_bits = (u32) source; - - clrsetbits_be32(&qe_mux_reg->cmxsi1syr, - QE_CMXUCR_TX_CLK_SRC_MASK << shift, - clock_bits << shift); - - return 0; -} diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c deleted file mode 100644 index 67191a0..0000000 --- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QE UCC Fast API Set - UCC Fast specific routines implementations. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -void ucc_fast_dump_regs(struct ucc_fast_private * uccf) -{ - printk(KERN_INFO "UCC%u Fast registers:\n", uccf->uf_info->ucc_num); - printk(KERN_INFO "Base address: 0x%p\n", uccf->uf_regs); - - printk(KERN_INFO "gumr : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->gumr, in_be32(&uccf->uf_regs->gumr)); - printk(KERN_INFO "upsmr : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->upsmr, in_be32(&uccf->uf_regs->upsmr)); - printk(KERN_INFO "utodr : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utodr, in_be16(&uccf->uf_regs->utodr)); - printk(KERN_INFO "udsr : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->udsr, in_be16(&uccf->uf_regs->udsr)); - printk(KERN_INFO "ucce : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->ucce, in_be32(&uccf->uf_regs->ucce)); - printk(KERN_INFO "uccm : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->uccm, in_be32(&uccf->uf_regs->uccm)); - printk(KERN_INFO "uccs : addr=0x%p, val=0x%02x\n", - &uccf->uf_regs->uccs, in_8(&uccf->uf_regs->uccs)); - printk(KERN_INFO "urfb : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->urfb, in_be32(&uccf->uf_regs->urfb)); - printk(KERN_INFO "urfs : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->urfs, in_be16(&uccf->uf_regs->urfs)); - printk(KERN_INFO "urfet : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->urfet, in_be16(&uccf->uf_regs->urfet)); - printk(KERN_INFO "urfset: addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->urfset, in_be16(&uccf->uf_regs->urfset)); - printk(KERN_INFO "utfb : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->utfb, in_be32(&uccf->uf_regs->utfb)); - printk(KERN_INFO "utfs : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utfs, in_be16(&uccf->uf_regs->utfs)); - printk(KERN_INFO "utfet : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utfet, in_be16(&uccf->uf_regs->utfet)); - printk(KERN_INFO "utftt : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utftt, in_be16(&uccf->uf_regs->utftt)); - printk(KERN_INFO "utpt : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utpt, in_be16(&uccf->uf_regs->utpt)); - printk(KERN_INFO "urtry : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->urtry, in_be32(&uccf->uf_regs->urtry)); - printk(KERN_INFO "guemr : addr=0x%p, val=0x%02x\n", - &uccf->uf_regs->guemr, in_8(&uccf->uf_regs->guemr)); -} -EXPORT_SYMBOL(ucc_fast_dump_regs); - -u32 ucc_fast_get_qe_cr_subblock(int uccf_num) -{ - switch (uccf_num) { - case 0: return QE_CR_SUBBLOCK_UCCFAST1; - case 1: return QE_CR_SUBBLOCK_UCCFAST2; - case 2: return QE_CR_SUBBLOCK_UCCFAST3; - case 3: return QE_CR_SUBBLOCK_UCCFAST4; - case 4: return QE_CR_SUBBLOCK_UCCFAST5; - case 5: return QE_CR_SUBBLOCK_UCCFAST6; - case 6: return QE_CR_SUBBLOCK_UCCFAST7; - case 7: return QE_CR_SUBBLOCK_UCCFAST8; - default: return QE_CR_SUBBLOCK_INVALID; - } -} -EXPORT_SYMBOL(ucc_fast_get_qe_cr_subblock); - -void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf) -{ - out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); -} -EXPORT_SYMBOL(ucc_fast_transmit_on_demand); - -void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) -{ - struct ucc_fast __iomem *uf_regs; - u32 gumr; - - uf_regs = uccf->uf_regs; - - /* Enable reception and/or transmission on this UCC. */ - gumr = in_be32(&uf_regs->gumr); - if (mode & COMM_DIR_TX) { - gumr |= UCC_FAST_GUMR_ENT; - uccf->enabled_tx = 1; - } - if (mode & COMM_DIR_RX) { - gumr |= UCC_FAST_GUMR_ENR; - uccf->enabled_rx = 1; - } - out_be32(&uf_regs->gumr, gumr); -} -EXPORT_SYMBOL(ucc_fast_enable); - -void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) -{ - struct ucc_fast __iomem *uf_regs; - u32 gumr; - - uf_regs = uccf->uf_regs; - - /* Disable reception and/or transmission on this UCC. */ - gumr = in_be32(&uf_regs->gumr); - if (mode & COMM_DIR_TX) { - gumr &= ~UCC_FAST_GUMR_ENT; - uccf->enabled_tx = 0; - } - if (mode & COMM_DIR_RX) { - gumr &= ~UCC_FAST_GUMR_ENR; - uccf->enabled_rx = 0; - } - out_be32(&uf_regs->gumr, gumr); -} -EXPORT_SYMBOL(ucc_fast_disable); - -int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret) -{ - struct ucc_fast_private *uccf; - struct ucc_fast __iomem *uf_regs; - u32 gumr; - int ret; - - if (!uf_info) - return -EINVAL; - - /* check if the UCC port number is in range. */ - if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) { - printk(KERN_ERR "%s: illegal UCC number\n", __func__); - return -EINVAL; - } - - /* Check that 'max_rx_buf_length' is properly aligned (4). */ - if (uf_info->max_rx_buf_length & (UCC_FAST_MRBLR_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: max_rx_buf_length not aligned\n", - __func__); - return -EINVAL; - } - - /* Validate Virtual Fifo register values */ - if (uf_info->urfs < UCC_FAST_URFS_MIN_VAL) { - printk(KERN_ERR "%s: urfs is too small\n", __func__); - return -EINVAL; - } - - if (uf_info->urfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: urfs is not aligned\n", __func__); - return -EINVAL; - } - - if (uf_info->urfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: urfet is not aligned.\n", __func__); - return -EINVAL; - } - - if (uf_info->urfset & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: urfset is not aligned\n", __func__); - return -EINVAL; - } - - if (uf_info->utfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: utfs is not aligned\n", __func__); - return -EINVAL; - } - - if (uf_info->utfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: utfet is not aligned\n", __func__); - return -EINVAL; - } - - if (uf_info->utftt & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: utftt is not aligned\n", __func__); - return -EINVAL; - } - - uccf = kzalloc(sizeof(struct ucc_fast_private), GFP_KERNEL); - if (!uccf) { - printk(KERN_ERR "%s: Cannot allocate private data\n", - __func__); - return -ENOMEM; - } - - /* Fill fast UCC structure */ - uccf->uf_info = uf_info; - /* Set the PHY base address */ - uccf->uf_regs = ioremap(uf_info->regs, sizeof(struct ucc_fast)); - if (uccf->uf_regs == NULL) { - printk(KERN_ERR "%s: Cannot map UCC registers\n", __func__); - kfree(uccf); - return -ENOMEM; - } - - uccf->enabled_tx = 0; - uccf->enabled_rx = 0; - uccf->stopped_tx = 0; - uccf->stopped_rx = 0; - uf_regs = uccf->uf_regs; - uccf->p_ucce = &uf_regs->ucce; - uccf->p_uccm = &uf_regs->uccm; -#ifdef CONFIG_UGETH_TX_ON_DEMAND - uccf->p_utodr = &uf_regs->utodr; -#endif -#ifdef STATISTICS - uccf->tx_frames = 0; - uccf->rx_frames = 0; - uccf->rx_discarded = 0; -#endif /* STATISTICS */ - - /* Set UCC to fast type */ - ret = ucc_set_type(uf_info->ucc_num, UCC_SPEED_TYPE_FAST); - if (ret) { - printk(KERN_ERR "%s: cannot set UCC type\n", __func__); - ucc_fast_free(uccf); - return ret; - } - - uccf->mrblr = uf_info->max_rx_buf_length; - - /* Set GUMR */ - /* For more details see the hardware spec. */ - gumr = uf_info->ttx_trx; - if (uf_info->tci) - gumr |= UCC_FAST_GUMR_TCI; - if (uf_info->cdp) - gumr |= UCC_FAST_GUMR_CDP; - if (uf_info->ctsp) - gumr |= UCC_FAST_GUMR_CTSP; - if (uf_info->cds) - gumr |= UCC_FAST_GUMR_CDS; - if (uf_info->ctss) - gumr |= UCC_FAST_GUMR_CTSS; - if (uf_info->txsy) - gumr |= UCC_FAST_GUMR_TXSY; - if (uf_info->rsyn) - gumr |= UCC_FAST_GUMR_RSYN; - gumr |= uf_info->synl; - if (uf_info->rtsm) - gumr |= UCC_FAST_GUMR_RTSM; - gumr |= uf_info->renc; - if (uf_info->revd) - gumr |= UCC_FAST_GUMR_REVD; - gumr |= uf_info->tenc; - gumr |= uf_info->tcrc; - gumr |= uf_info->mode; - out_be32(&uf_regs->gumr, gumr); - - /* Allocate memory for Tx Virtual Fifo */ - uccf->ucc_fast_tx_virtual_fifo_base_offset = - qe_muram_alloc(uf_info->utfs, UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); - if (IS_ERR_VALUE(uccf->ucc_fast_tx_virtual_fifo_base_offset)) { - printk(KERN_ERR "%s: cannot allocate MURAM for TX FIFO\n", - __func__); - uccf->ucc_fast_tx_virtual_fifo_base_offset = 0; - ucc_fast_free(uccf); - return -ENOMEM; - } - - /* Allocate memory for Rx Virtual Fifo */ - uccf->ucc_fast_rx_virtual_fifo_base_offset = - qe_muram_alloc(uf_info->urfs + - UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR, - UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); - if (IS_ERR_VALUE(uccf->ucc_fast_rx_virtual_fifo_base_offset)) { - printk(KERN_ERR "%s: cannot allocate MURAM for RX FIFO\n", - __func__); - uccf->ucc_fast_rx_virtual_fifo_base_offset = 0; - ucc_fast_free(uccf); - return -ENOMEM; - } - - /* Set Virtual Fifo registers */ - out_be16(&uf_regs->urfs, uf_info->urfs); - out_be16(&uf_regs->urfet, uf_info->urfet); - out_be16(&uf_regs->urfset, uf_info->urfset); - out_be16(&uf_regs->utfs, uf_info->utfs); - out_be16(&uf_regs->utfet, uf_info->utfet); - out_be16(&uf_regs->utftt, uf_info->utftt); - /* utfb, urfb are offsets from MURAM base */ - out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset); - out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset); - - /* Mux clocking */ - /* Grant Support */ - ucc_set_qe_mux_grant(uf_info->ucc_num, uf_info->grant_support); - /* Breakpoint Support */ - ucc_set_qe_mux_bkpt(uf_info->ucc_num, uf_info->brkpt_support); - /* Set Tsa or NMSI mode. */ - ucc_set_qe_mux_tsa(uf_info->ucc_num, uf_info->tsa); - /* If NMSI (not Tsa), set Tx and Rx clock. */ - if (!uf_info->tsa) { - /* Rx clock routing */ - if ((uf_info->rx_clock != QE_CLK_NONE) && - ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->rx_clock, - COMM_DIR_RX)) { - printk(KERN_ERR "%s: illegal value for RX clock\n", - __func__); - ucc_fast_free(uccf); - return -EINVAL; - } - /* Tx clock routing */ - if ((uf_info->tx_clock != QE_CLK_NONE) && - ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->tx_clock, - COMM_DIR_TX)) { - printk(KERN_ERR "%s: illegal value for TX clock\n", - __func__); - ucc_fast_free(uccf); - return -EINVAL; - } - } else { - /* tdm Rx clock routing */ - if ((uf_info->rx_clock != QE_CLK_NONE) && - ucc_set_tdm_rxtx_clk(uf_info->tdm_num, - uf_info->rx_clock, COMM_DIR_RX)) { - pr_err("%s: illegal value for RX clock", __func__); - ucc_fast_free(uccf); - return -EINVAL; - } - - /* tdm Tx clock routing */ - if ((uf_info->tx_clock != QE_CLK_NONE) && - ucc_set_tdm_rxtx_clk(uf_info->tdm_num, - uf_info->tx_clock, COMM_DIR_TX)) { - pr_err("%s:illegal value for TX clock", __func__); - ucc_fast_free(uccf); - return -EINVAL; - } - - /* tdm Rx sync clock routing */ - if ((uf_info->rx_sync != QE_CLK_NONE) && - ucc_set_tdm_rxtx_sync(uf_info->tdm_num, - uf_info->rx_sync, COMM_DIR_RX)) { - pr_err("%s:illegal value for TX clock", __func__); - ucc_fast_free(uccf); - return -EINVAL; - } - - /* tdm Tx sync clock routing */ - if ((uf_info->tx_sync != QE_CLK_NONE) && - ucc_set_tdm_rxtx_sync(uf_info->tdm_num, - uf_info->tx_sync, COMM_DIR_TX)) { - pr_err("%s:illegal value for TX clock", __func__); - ucc_fast_free(uccf); - return -EINVAL; - } - } - - /* Set interrupt mask register at UCC level. */ - out_be32(&uf_regs->uccm, uf_info->uccm_mask); - - /* First, clear anything pending at UCC level, - * otherwise, old garbage may come through - * as soon as the dam is opened. */ - - /* Writing '1' clears */ - out_be32(&uf_regs->ucce, 0xffffffff); - - *uccf_ret = uccf; - return 0; -} -EXPORT_SYMBOL(ucc_fast_init); - -void ucc_fast_free(struct ucc_fast_private * uccf) -{ - if (!uccf) - return; - - if (uccf->ucc_fast_tx_virtual_fifo_base_offset) - qe_muram_free(uccf->ucc_fast_tx_virtual_fifo_base_offset); - - if (uccf->ucc_fast_rx_virtual_fifo_base_offset) - qe_muram_free(uccf->ucc_fast_rx_virtual_fifo_base_offset); - - if (uccf->uf_regs) - iounmap(uccf->uf_regs); - - kfree(uccf); -} -EXPORT_SYMBOL(ucc_fast_free); diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/arch/powerpc/sysdev/qe_lib/ucc_slow.c deleted file mode 100644 index 1c062f4..0000000 --- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QE UCC Slow API Set - UCC Slow specific routines implementations. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -u32 ucc_slow_get_qe_cr_subblock(int uccs_num) -{ - switch (uccs_num) { - case 0: return QE_CR_SUBBLOCK_UCCSLOW1; - case 1: return QE_CR_SUBBLOCK_UCCSLOW2; - case 2: return QE_CR_SUBBLOCK_UCCSLOW3; - case 3: return QE_CR_SUBBLOCK_UCCSLOW4; - case 4: return QE_CR_SUBBLOCK_UCCSLOW5; - case 5: return QE_CR_SUBBLOCK_UCCSLOW6; - case 6: return QE_CR_SUBBLOCK_UCCSLOW7; - case 7: return QE_CR_SUBBLOCK_UCCSLOW8; - default: return QE_CR_SUBBLOCK_INVALID; - } -} -EXPORT_SYMBOL(ucc_slow_get_qe_cr_subblock); - -void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs) -{ - out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD); -} - -void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs) -{ - struct ucc_slow_info *us_info = uccs->us_info; - u32 id; - - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_GRACEFUL_STOP_TX, id, - QE_CR_PROTOCOL_UNSPECIFIED, 0); -} -EXPORT_SYMBOL(ucc_slow_graceful_stop_tx); - -void ucc_slow_stop_tx(struct ucc_slow_private * uccs) -{ - struct ucc_slow_info *us_info = uccs->us_info; - u32 id; - - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_STOP_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); -} -EXPORT_SYMBOL(ucc_slow_stop_tx); - -void ucc_slow_restart_tx(struct ucc_slow_private * uccs) -{ - struct ucc_slow_info *us_info = uccs->us_info; - u32 id; - - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_RESTART_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); -} -EXPORT_SYMBOL(ucc_slow_restart_tx); - -void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) -{ - struct ucc_slow *us_regs; - u32 gumr_l; - - us_regs = uccs->us_regs; - - /* Enable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); - if (mode & COMM_DIR_TX) { - gumr_l |= UCC_SLOW_GUMR_L_ENT; - uccs->enabled_tx = 1; - } - if (mode & COMM_DIR_RX) { - gumr_l |= UCC_SLOW_GUMR_L_ENR; - uccs->enabled_rx = 1; - } - out_be32(&us_regs->gumr_l, gumr_l); -} -EXPORT_SYMBOL(ucc_slow_enable); - -void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) -{ - struct ucc_slow *us_regs; - u32 gumr_l; - - us_regs = uccs->us_regs; - - /* Disable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); - if (mode & COMM_DIR_TX) { - gumr_l &= ~UCC_SLOW_GUMR_L_ENT; - uccs->enabled_tx = 0; - } - if (mode & COMM_DIR_RX) { - gumr_l &= ~UCC_SLOW_GUMR_L_ENR; - uccs->enabled_rx = 0; - } - out_be32(&us_regs->gumr_l, gumr_l); -} -EXPORT_SYMBOL(ucc_slow_disable); - -/* Initialize the UCC for Slow operations - * - * The caller should initialize the following us_info - */ -int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret) -{ - struct ucc_slow_private *uccs; - u32 i; - struct ucc_slow __iomem *us_regs; - u32 gumr; - struct qe_bd *bd; - u32 id; - u32 command; - int ret = 0; - - if (!us_info) - return -EINVAL; - - /* check if the UCC port number is in range. */ - if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM - 1)) { - printk(KERN_ERR "%s: illegal UCC number\n", __func__); - return -EINVAL; - } - - /* - * Set mrblr - * Check that 'max_rx_buf_length' is properly aligned (4), unless - * rfw is 1, meaning that QE accepts one byte at a time, unlike normal - * case when QE accepts 32 bits at a time. - */ - if ((!us_info->rfw) && - (us_info->max_rx_buf_length & (UCC_SLOW_MRBLR_ALIGNMENT - 1))) { - printk(KERN_ERR "max_rx_buf_length not aligned.\n"); - return -EINVAL; - } - - uccs = kzalloc(sizeof(struct ucc_slow_private), GFP_KERNEL); - if (!uccs) { - printk(KERN_ERR "%s: Cannot allocate private data\n", - __func__); - return -ENOMEM; - } - - /* Fill slow UCC structure */ - uccs->us_info = us_info; - /* Set the PHY base address */ - uccs->us_regs = ioremap(us_info->regs, sizeof(struct ucc_slow)); - if (uccs->us_regs == NULL) { - printk(KERN_ERR "%s: Cannot map UCC registers\n", __func__); - kfree(uccs); - return -ENOMEM; - } - - uccs->saved_uccm = 0; - uccs->p_rx_frame = 0; - us_regs = uccs->us_regs; - uccs->p_ucce = (u16 *) & (us_regs->ucce); - uccs->p_uccm = (u16 *) & (us_regs->uccm); -#ifdef STATISTICS - uccs->rx_frames = 0; - uccs->tx_frames = 0; - uccs->rx_discarded = 0; -#endif /* STATISTICS */ - - /* Get PRAM base */ - uccs->us_pram_offset = - qe_muram_alloc(UCC_SLOW_PRAM_SIZE, ALIGNMENT_OF_UCC_SLOW_PRAM); - if (IS_ERR_VALUE(uccs->us_pram_offset)) { - printk(KERN_ERR "%s: cannot allocate MURAM for PRAM", __func__); - ucc_slow_free(uccs); - return -ENOMEM; - } - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, id, us_info->protocol, - uccs->us_pram_offset); - - uccs->us_pram = qe_muram_addr(uccs->us_pram_offset); - - /* Set UCC to slow type */ - ret = ucc_set_type(us_info->ucc_num, UCC_SPEED_TYPE_SLOW); - if (ret) { - printk(KERN_ERR "%s: cannot set UCC type", __func__); - ucc_slow_free(uccs); - return ret; - } - - out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length); - - INIT_LIST_HEAD(&uccs->confQ); - - /* Allocate BDs. */ - uccs->rx_base_offset = - qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd), - QE_ALIGNMENT_OF_BD); - if (IS_ERR_VALUE(uccs->rx_base_offset)) { - printk(KERN_ERR "%s: cannot allocate %u RX BDs\n", __func__, - us_info->rx_bd_ring_len); - uccs->rx_base_offset = 0; - ucc_slow_free(uccs); - return -ENOMEM; - } - - uccs->tx_base_offset = - qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd), - QE_ALIGNMENT_OF_BD); - if (IS_ERR_VALUE(uccs->tx_base_offset)) { - printk(KERN_ERR "%s: cannot allocate TX BDs", __func__); - uccs->tx_base_offset = 0; - ucc_slow_free(uccs); - return -ENOMEM; - } - - /* Init Tx bds */ - bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs->tx_base_offset); - for (i = 0; i < us_info->tx_bd_ring_len - 1; i++) { - /* clear bd buffer */ - out_be32(&bd->buf, 0); - /* set bd status and length */ - out_be32((u32 *) bd, 0); - bd++; - } - /* for last BD set Wrap bit */ - out_be32(&bd->buf, 0); - out_be32((u32 *) bd, cpu_to_be32(T_W)); - - /* Init Rx bds */ - bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset); - for (i = 0; i < us_info->rx_bd_ring_len - 1; i++) { - /* set bd status and length */ - out_be32((u32*)bd, 0); - /* clear bd buffer */ - out_be32(&bd->buf, 0); - bd++; - } - /* for last BD set Wrap bit */ - out_be32((u32*)bd, cpu_to_be32(R_W)); - out_be32(&bd->buf, 0); - - /* Set GUMR (For more details see the hardware spec.). */ - /* gumr_h */ - gumr = us_info->tcrc; - if (us_info->cdp) - gumr |= UCC_SLOW_GUMR_H_CDP; - if (us_info->ctsp) - gumr |= UCC_SLOW_GUMR_H_CTSP; - if (us_info->cds) - gumr |= UCC_SLOW_GUMR_H_CDS; - if (us_info->ctss) - gumr |= UCC_SLOW_GUMR_H_CTSS; - if (us_info->tfl) - gumr |= UCC_SLOW_GUMR_H_TFL; - if (us_info->rfw) - gumr |= UCC_SLOW_GUMR_H_RFW; - if (us_info->txsy) - gumr |= UCC_SLOW_GUMR_H_TXSY; - if (us_info->rtsm) - gumr |= UCC_SLOW_GUMR_H_RTSM; - out_be32(&us_regs->gumr_h, gumr); - - /* gumr_l */ - gumr = us_info->tdcr | us_info->rdcr | us_info->tenc | us_info->renc | - us_info->diag | us_info->mode; - if (us_info->tci) - gumr |= UCC_SLOW_GUMR_L_TCI; - if (us_info->rinv) - gumr |= UCC_SLOW_GUMR_L_RINV; - if (us_info->tinv) - gumr |= UCC_SLOW_GUMR_L_TINV; - if (us_info->tend) - gumr |= UCC_SLOW_GUMR_L_TEND; - out_be32(&us_regs->gumr_l, gumr); - - /* Function code registers */ - - /* if the data is in cachable memory, the 'global' */ - /* in the function code should be set. */ - uccs->us_pram->tbmr = UCC_BMR_BO_BE; - uccs->us_pram->rbmr = UCC_BMR_BO_BE; - - /* rbase, tbase are offsets from MURAM base */ - out_be16(&uccs->us_pram->rbase, uccs->rx_base_offset); - out_be16(&uccs->us_pram->tbase, uccs->tx_base_offset); - - /* Mux clocking */ - /* Grant Support */ - ucc_set_qe_mux_grant(us_info->ucc_num, us_info->grant_support); - /* Breakpoint Support */ - ucc_set_qe_mux_bkpt(us_info->ucc_num, us_info->brkpt_support); - /* Set Tsa or NMSI mode. */ - ucc_set_qe_mux_tsa(us_info->ucc_num, us_info->tsa); - /* If NMSI (not Tsa), set Tx and Rx clock. */ - if (!us_info->tsa) { - /* Rx clock routing */ - if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->rx_clock, - COMM_DIR_RX)) { - printk(KERN_ERR "%s: illegal value for RX clock\n", - __func__); - ucc_slow_free(uccs); - return -EINVAL; - } - /* Tx clock routing */ - if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->tx_clock, - COMM_DIR_TX)) { - printk(KERN_ERR "%s: illegal value for TX clock\n", - __func__); - ucc_slow_free(uccs); - return -EINVAL; - } - } - - /* Set interrupt mask register at UCC level. */ - out_be16(&us_regs->uccm, us_info->uccm_mask); - - /* First, clear anything pending at UCC level, - * otherwise, old garbage may come through - * as soon as the dam is opened. */ - - /* Writing '1' clears */ - out_be16(&us_regs->ucce, 0xffff); - - /* Issue QE Init command */ - if (us_info->init_tx && us_info->init_rx) - command = QE_INIT_TX_RX; - else if (us_info->init_tx) - command = QE_INIT_TX; - else - command = QE_INIT_RX; /* We know at least one is TRUE */ - - qe_issue_cmd(command, id, us_info->protocol, 0); - - *uccs_ret = uccs; - return 0; -} -EXPORT_SYMBOL(ucc_slow_init); - -void ucc_slow_free(struct ucc_slow_private * uccs) -{ - if (!uccs) - return; - - if (uccs->rx_base_offset) - qe_muram_free(uccs->rx_base_offset); - - if (uccs->tx_base_offset) - qe_muram_free(uccs->tx_base_offset); - - if (uccs->us_pram) - qe_muram_free(uccs->us_pram_offset); - - if (uccs->us_regs) - iounmap(uccs->us_regs); - - kfree(uccs); -} -EXPORT_SYMBOL(ucc_slow_free); - diff --git a/arch/powerpc/sysdev/qe_lib/usb.c b/arch/powerpc/sysdev/qe_lib/usb.c deleted file mode 100644 index 27f23bd..0000000 --- a/arch/powerpc/sysdev/qe_lib/usb.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * QE USB routines - * - * Copyright 2006 Freescale Semiconductor, Inc. - * Shlomi Gridish - * Jerry Huang - * Copyright (c) MontaVista Software, Inc. 2008. - * Anton Vorontsov - * - * 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 -#include -#include -#include -#include -#include - -int qe_usb_clock_set(enum qe_clock clk, int rate) -{ - struct qe_mux __iomem *mux = &qe_immr->qmx; - unsigned long flags; - u32 val; - - switch (clk) { - case QE_CLK3: val = QE_CMXGCR_USBCS_CLK3; break; - case QE_CLK5: val = QE_CMXGCR_USBCS_CLK5; break; - case QE_CLK7: val = QE_CMXGCR_USBCS_CLK7; break; - case QE_CLK9: val = QE_CMXGCR_USBCS_CLK9; break; - case QE_CLK13: val = QE_CMXGCR_USBCS_CLK13; break; - case QE_CLK17: val = QE_CMXGCR_USBCS_CLK17; break; - case QE_CLK19: val = QE_CMXGCR_USBCS_CLK19; break; - case QE_CLK21: val = QE_CMXGCR_USBCS_CLK21; break; - case QE_BRG9: val = QE_CMXGCR_USBCS_BRG9; break; - case QE_BRG10: val = QE_CMXGCR_USBCS_BRG10; break; - default: - pr_err("%s: requested unknown clock %d\n", __func__, clk); - return -EINVAL; - } - - if (qe_clock_is_brg(clk)) - qe_setbrg(clk, rate, 1); - - spin_lock_irqsave(&cmxgcr_lock, flags); - - clrsetbits_be32(&mux->cmxgcr, QE_CMXGCR_USBCS, val); - - spin_unlock_irqrestore(&cmxgcr_lock, flags); - - return 0; -} -EXPORT_SYMBOL(qe_usb_clock_set); diff --git a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c index 427de85..74b0de8 100755 --- a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c +++ b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c @@ -58,7 +58,7 @@ #include #include #include -#include /* For struct qe_firmware */ +#include /* For struct qe_firmware */ #include #include #include /* For file access mask */ diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index c4f6506..dd5d405b 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -29,7 +29,7 @@ #include #include -#include /* for ucc_set_qe_mux_mii_mng() */ +#include /* for ucc_set_qe_mux_mii_mng() */ #include "gianfar.h" diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 5930c39..fbaedd3 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -38,10 +38,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include "ucc_geth.h" diff --git a/drivers/net/ethernet/freescale/ucc_geth.h b/drivers/net/ethernet/freescale/ucc_geth.h index 75f3371..a803635 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.h +++ b/drivers/net/ethernet/freescale/ucc_geth.h @@ -22,11 +22,11 @@ #include #include -#include -#include +#include +#include -#include -#include +#include +#include #define DRV_DESC "QE UCC Gigabit Ethernet Controller" #define DRV_NAME "ucc_geth" diff --git a/drivers/net/wan/fsl_ucc_hdlc.h b/drivers/net/wan/fsl_ucc_hdlc.h index e0c8a4a..93cc20cc 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.h +++ b/drivers/net/wan/fsl_ucc_hdlc.h @@ -14,11 +14,11 @@ #include #include -#include -#include +#include +#include -#include -#include +#include +#include /* SI RAM entries */ #define SIR_LAST 0x0001 diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 522e7fc..bb02cfa 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -13,4 +13,6 @@ if FSL_SOC_DRIVERS source "drivers/soc/fsl/Kconfig" endif +source drivers/soc/qe/Kconfig + endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index b40b228..1212b75 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -3,3 +3,5 @@ # obj-$(CONFIG_FSL_SOC_DRIVERS) += fsl/ + +obj-$(CONFIG_QUICC_ENGINE) += qe/ diff --git a/drivers/soc/qe/Kconfig b/drivers/soc/qe/Kconfig new file mode 100644 index 0000000..5c2b9d8 --- /dev/null +++ b/drivers/soc/qe/Kconfig @@ -0,0 +1,47 @@ +# +# QE Communication options +# + +config QUICC_ENGINE + bool "Freescale QUICC Engine (QE) Support" + depends on FSL_SOC && (PPC32 || PPC64) + select PPC_LIB_RHEAP + select CRC32 + ---help--- + The QUICC Engine (QE) is a new generation of communications + coprocessors on Freescale embedded CPUs (akin to CPM in older chips). + Selecting this option means that you wish to build a kernel + for a machine with a QE coprocessor. + +config QE_GPIO + bool "QE GPIO support" + depends on QUICC_ENGINE + select ARCH_REQUIRE_GPIOLIB + ---help--- + Say Y here if you're going to use hardware that connects to the + QE GPIOs. + + +config UCC_SLOW + bool + default y if SERIAL_QE + help + This option provides qe_lib support to UCC slow + protocols: UART, BISYNC, QMC + +config UCC_FAST + bool + default y if UCC_GETH || FSL_UCC_TDM || FSL_UCC_HDLC + help + This option provides qe_lib support to UCC fast + protocols: HDLC, Ethernet, ATM, transparent + +config UCC + bool + default y if UCC_FAST || UCC_SLOW + +config QE_USB + bool + default y if USB_FSL_QE + help + QE USB Controller support diff --git a/drivers/soc/qe/Makefile b/drivers/soc/qe/Makefile new file mode 100644 index 0000000..f1855c1 --- /dev/null +++ b/drivers/soc/qe/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the linux ppc-specific parts of QE +# +obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o + +obj-$(CONFIG_UCC) += ucc.o +obj-$(CONFIG_UCC_SLOW) += ucc_slow.o +obj-$(CONFIG_UCC_FAST) += ucc_fast.o +obj-$(CONFIG_QE_USB) += usb.o +obj-$(CONFIG_QE_GPIO) += gpio.o diff --git a/drivers/soc/qe/gpio.c b/drivers/soc/qe/gpio.c new file mode 100644 index 0000000..fb891d4 --- /dev/null +++ b/drivers/soc/qe/gpio.c @@ -0,0 +1,318 @@ +/* + * QUICC Engine GPIOs + * + * Copyright (c) MontaVista Software, Inc. 2008. + * + * Author: Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct qe_gpio_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + unsigned long pin_flags[QE_PIO_PINS]; +#define QE_PIN_REQUESTED 0 + + /* shadowed data register to clear/set bits safely */ + u32 cpdata; + + /* saved_regs used to restore dedicated functions */ + struct qe_pio_regs saved_regs; +}; + +static inline struct qe_gpio_chip * +to_qe_gpio_chip(struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct qe_gpio_chip, mm_gc); +} + +static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); + struct qe_pio_regs __iomem *regs = mm_gc->regs; + + qe_gc->cpdata = in_be32(®s->cpdata); + qe_gc->saved_regs.cpdata = qe_gc->cpdata; + qe_gc->saved_regs.cpdir1 = in_be32(®s->cpdir1); + qe_gc->saved_regs.cpdir2 = in_be32(®s->cpdir2); + qe_gc->saved_regs.cppar1 = in_be32(®s->cppar1); + qe_gc->saved_regs.cppar2 = in_be32(®s->cppar2); + qe_gc->saved_regs.cpodr = in_be32(®s->cpodr); +} + +static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct qe_pio_regs __iomem *regs = mm_gc->regs; + u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); + + return in_be32(®s->cpdata) & pin_mask; +} + +static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); + struct qe_pio_regs __iomem *regs = mm_gc->regs; + unsigned long flags; + u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (val) + qe_gc->cpdata |= pin_mask; + else + qe_gc->cpdata &= ~pin_mask; + + out_be32(®s->cpdata, qe_gc->cpdata); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} + +static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_IN, 0, 0, 0); + + spin_unlock_irqrestore(&qe_gc->lock, flags); + + return 0; +} + +static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc); + unsigned long flags; + + qe_gpio_set(gc, gpio, val); + + spin_lock_irqsave(&qe_gc->lock, flags); + + __par_io_config_pin(mm_gc->regs, gpio, QE_PIO_DIR_OUT, 0, 0, 0); + + spin_unlock_irqrestore(&qe_gc->lock, flags); + + return 0; +} + +struct qe_pin { + /* + * The qe_gpio_chip name is unfortunate, we should change that to + * something like qe_pio_controller. Someday. + */ + struct qe_gpio_chip *controller; + int num; +}; + +/** + * qe_pin_request - Request a QE pin + * @np: device node to get a pin from + * @index: index of a pin in the device tree + * Context: non-atomic + * + * This function return qe_pin so that you could use it with the rest of + * the QE Pin Multiplexing API. + */ +struct qe_pin *qe_pin_request(struct device_node *np, int index) +{ + struct qe_pin *qe_pin; + struct gpio_chip *gc; + struct of_mm_gpio_chip *mm_gc; + struct qe_gpio_chip *qe_gc; + int err; + unsigned long flags; + + qe_pin = kzalloc(sizeof(*qe_pin), GFP_KERNEL); + if (!qe_pin) { + pr_debug("%s: can't allocate memory\n", __func__); + return ERR_PTR(-ENOMEM); + } + + err = of_get_gpio(np, index); + if (err < 0) + goto err0; + gc = gpio_to_chip(err); + if (WARN_ON(!gc)) + goto err0; + + if (!of_device_is_compatible(gc->of_node, + "fsl,mpc8323-qe-pario-bank")) { + pr_debug("%s: tried to get a non-qe pin\n", __func__); + err = -EINVAL; + goto err0; + } + + mm_gc = to_of_mm_gpio_chip(gc); + qe_gc = to_qe_gpio_chip(mm_gc); + + spin_lock_irqsave(&qe_gc->lock, flags); + + err -= gc->base; + if (test_and_set_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[err]) == 0) { + qe_pin->controller = qe_gc; + qe_pin->num = err; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irqrestore(&qe_gc->lock, flags); + + if (!err) + return qe_pin; +err0: + kfree(qe_pin); + pr_debug("%s failed with status %d\n", __func__, err); + return ERR_PTR(err); +} +EXPORT_SYMBOL(qe_pin_request); + +/** + * qe_pin_free - Free a pin + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function frees the qe_pin structure and makes a pin available + * for further qe_pin_request() calls. + */ +void qe_pin_free(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + unsigned long flags; + const int pin = qe_pin->num; + + spin_lock_irqsave(&qe_gc->lock, flags); + test_and_clear_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[pin]); + spin_unlock_irqrestore(&qe_gc->lock, flags); + + kfree(qe_pin); +} +EXPORT_SYMBOL(qe_pin_free); + +/** + * qe_pin_set_dedicated - Revert a pin to a dedicated peripheral function mode + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function resets a pin to a dedicated peripheral function that + * has been set up by the firmware. + */ +void qe_pin_set_dedicated(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + struct qe_pio_regs *sregs = &qe_gc->saved_regs; + int pin = qe_pin->num; + u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1)); + u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2); + bool second_reg = pin > (QE_PIO_PINS / 2) - 1; + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (second_reg) { + clrsetbits_be32(®s->cpdir2, mask2, sregs->cpdir2 & mask2); + clrsetbits_be32(®s->cppar2, mask2, sregs->cppar2 & mask2); + } else { + clrsetbits_be32(®s->cpdir1, mask2, sregs->cpdir1 & mask2); + clrsetbits_be32(®s->cppar1, mask2, sregs->cppar1 & mask2); + } + + if (sregs->cpdata & mask1) + qe_gc->cpdata |= mask1; + else + qe_gc->cpdata &= ~mask1; + + out_be32(®s->cpdata, qe_gc->cpdata); + clrsetbits_be32(®s->cpodr, mask1, sregs->cpodr & mask1); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} +EXPORT_SYMBOL(qe_pin_set_dedicated); + +/** + * qe_pin_set_gpio - Set a pin to the GPIO mode + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function sets a pin to the GPIO mode. + */ +void qe_pin_set_gpio(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + /* Let's make it input by default, GPIO API is able to change that. */ + __par_io_config_pin(regs, qe_pin->num, QE_PIO_DIR_IN, 0, 0, 0); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} +EXPORT_SYMBOL(qe_pin_set_gpio); + +static int __init qe_add_gpiochips(void) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") { + int ret; + struct qe_gpio_chip *qe_gc; + struct of_mm_gpio_chip *mm_gc; + struct gpio_chip *gc; + + qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL); + if (!qe_gc) { + ret = -ENOMEM; + goto err; + } + + spin_lock_init(&qe_gc->lock); + + mm_gc = &qe_gc->mm_gc; + gc = &mm_gc->gc; + + mm_gc->save_regs = qe_gpio_save_regs; + gc->ngpio = QE_PIO_PINS; + gc->direction_input = qe_gpio_dir_in; + gc->direction_output = qe_gpio_dir_out; + gc->get = qe_gpio_get; + gc->set = qe_gpio_set; + + ret = of_mm_gpiochip_add(np, mm_gc); + if (ret) + goto err; + continue; +err: + pr_err("%s: registration failed with status %d\n", + np->full_name, ret); + kfree(qe_gc); + /* try others anyway */ + } + return 0; +} +arch_initcall(qe_add_gpiochips); diff --git a/drivers/soc/qe/qe.c b/drivers/soc/qe/qe.c new file mode 100644 index 0000000..25f0e0d --- /dev/null +++ b/drivers/soc/qe/qe.c @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2006-2010, 2012 Freescale Semiconductor, Inc. + * All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * Based on cpm2_common.c from Dan Malek (dmalek@jlc.net) + * + * Description: + * General Purpose functions for the global management of the + * QUICC Engine (QE). + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void qe_snums_init(void); +static int qe_sdma_init(void); + +int siram_init_flag; + +static DEFINE_SPINLOCK(qe_lock); +DEFINE_SPINLOCK(cmxgcr_lock); +EXPORT_SYMBOL(cmxgcr_lock); + +/* QE snum state */ +enum qe_snum_state { + QE_SNUM_STATE_USED, + QE_SNUM_STATE_FREE +}; + +/* QE snum */ +struct qe_snum { + u8 num; + enum qe_snum_state state; +}; + +/* We allocate this here because it is used almost exclusively for + * the communication processor devices. + */ +struct qe_immap __iomem *qe_immr; +EXPORT_SYMBOL(qe_immr); + +/* Dynamically allocated SNUMs */ +static struct qe_snum snums[QE_NUM_OF_SNUM]; +static unsigned int qe_num_of_snum; + +static phys_addr_t qebase = -1; + +phys_addr_t get_qe_base(void) +{ + struct device_node *qe; + int size; + const u32 *prop; + + if (qebase != -1) + return qebase; + + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return qebase; + } + + prop = of_get_property(qe, "reg", &size); + if (prop && size >= sizeof(*prop)) + qebase = of_translate_address(qe, prop); + of_node_put(qe); + + return qebase; +} +EXPORT_SYMBOL(get_qe_base); + +void qe_reset(void) +{ + if (qe_immr == NULL) + qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE); + + qe_snums_init(); + + qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, + QE_CR_PROTOCOL_UNSPECIFIED, 0); + + /* Reclaim the MURAM memory for our use. */ + qe_muram_init(); + + if (qe_sdma_init()) + panic("sdma init failed!"); +} + +int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) +{ + unsigned long flags; + u8 mcn_shift = 0, dev_shift = 0; + u32 ret; + + spin_lock_irqsave(&qe_lock, flags); + if (cmd == QE_RESET) { + out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG)); + } else { + if (cmd == QE_ASSIGN_PAGE) { + /* Here device is the SNUM, not sub-block */ + dev_shift = QE_CR_SNUM_SHIFT; + } else if (cmd == QE_ASSIGN_RISC) { + /* Here device is the SNUM, and mcnProtocol is + * e_QeCmdRiscAssignment value */ + dev_shift = QE_CR_SNUM_SHIFT; + mcn_shift = QE_CR_MCN_RISC_ASSIGN_SHIFT; + } else { + if (device == QE_CR_SUBBLOCK_USB) + mcn_shift = QE_CR_MCN_USB_SHIFT; + else + mcn_shift = QE_CR_MCN_NORMAL_SHIFT; + } + + out_be32(&qe_immr->cp.cecdr, cmd_input); + out_be32(&qe_immr->cp.cecr, + (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32) + mcn_protocol << mcn_shift)); + } + + /* wait for the QE_CR_FLG to clear */ + ret = spin_event_timeout((in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) == 0, + 100, 0); + /* On timeout (e.g. failure), the expression will be false (ret == 0), + otherwise it will be true (ret == 1). */ + spin_unlock_irqrestore(&qe_lock, flags); + + return ret == 1; +} +EXPORT_SYMBOL(qe_issue_cmd); + +/* Set a baud rate generator. This needs lots of work. There are + * 16 BRGs, which can be connected to the QE channels or output + * as clocks. The BRGs are in two different block of internal + * memory mapped space. + * The BRG clock is the QE clock divided by 2. + * It was set up long ago during the initial boot phase and is + * is given to us. + * Baud rate clocks are zero-based in the driver code (as that maps + * to port numbers). Documentation uses 1-based numbering. + */ +static unsigned int brg_clk; + +unsigned int qe_get_brg_clk(void) +{ + struct device_node *qe; + int size; + const u32 *prop; + + if (brg_clk) + return brg_clk; + + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return brg_clk; + } + + prop = of_get_property(qe, "brg-frequency", &size); + if (prop && size == sizeof(*prop)) + brg_clk = *prop; + + of_node_put(qe); + + return brg_clk; +} +EXPORT_SYMBOL(qe_get_brg_clk); + +/* Program the BRG to the given sampling rate and multiplier + * + * @brg: the BRG, QE_BRG1 - QE_BRG16 + * @rate: the desired sampling rate + * @multiplier: corresponds to the value programmed in GUMR_L[RDCR] or + * GUMR_L[TDCR]. E.g., if this BRG is the RX clock, and GUMR_L[RDCR]=01, + * then 'multiplier' should be 8. + */ +int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier) +{ + u32 divisor, tempval; + u32 div16 = 0; + + if ((brg < QE_BRG1) || (brg > QE_BRG16)) + return -EINVAL; + + divisor = qe_get_brg_clk() / (rate * multiplier); + + if (divisor > QE_BRGC_DIVISOR_MAX + 1) { + div16 = QE_BRGC_DIV16; + divisor /= 16; + } + + /* Errata QE_General4, which affects some MPC832x and MPC836x SOCs, says + that the BRG divisor must be even if you're not using divide-by-16 + mode. */ + if (!div16 && (divisor & 1) && (divisor > 3)) + divisor++; + + tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | + QE_BRGC_ENABLE | div16; + + out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval); + + return 0; +} +EXPORT_SYMBOL(qe_setbrg); + +/* Convert a string to a QE clock source enum + * + * This function takes a string, typically from a property in the device + * tree, and returns the corresponding "enum qe_clock" value. +*/ +enum qe_clock qe_clock_source(const char *source) +{ + unsigned int ret; + unsigned long val; + + if (strcasecmp(source, "none") == 0) + return QE_CLK_NONE; + + if (strcasecmp(source, "tsync_pin") == 0) + return QE_TSYNC_PIN; + + if (strcasecmp(source, "rsync_pin") == 0) + return QE_RSYNC_PIN; + + if (strncasecmp(source, "brg", 3) == 0) { + ret = kstrtoul(source + 3, 10, &val); + if (ret) + return ret; + + if ((val >= 1) && (val <= 16)) + return (QE_BRG1 - 1) + val; + else + return QE_CLK_DUMMY; + } + + if (strncasecmp(source, "clk", 3) == 0) { + ret = kstrtoul(source + 3, 10, &val); + if (ret) + return ret; + if ((val >= 1) && (val <= 24)) + return (QE_CLK1 - 1) + val; + else + return QE_CLK_DUMMY; + } + + return QE_CLK_DUMMY; +} +EXPORT_SYMBOL(qe_clock_source); + +/* Initialize SNUMs (thread serial numbers) according to + * QE Module Control chapter, SNUM table + */ +static void qe_snums_init(void) +{ + int i; + static const u8 snum_init_76[] = { + 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, + 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, + 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, + 0xD8, 0xD9, 0xE8, 0xE9, 0x44, 0x45, 0x4C, 0x4D, + 0x54, 0x55, 0x5C, 0x5D, 0x64, 0x65, 0x6C, 0x6D, + 0x74, 0x75, 0x7C, 0x7D, 0x84, 0x85, 0x8C, 0x8D, + 0x94, 0x95, 0x9C, 0x9D, 0xA4, 0xA5, 0xAC, 0xAD, + 0xB4, 0xB5, 0xBC, 0xBD, 0xC4, 0xC5, 0xCC, 0xCD, + 0xD4, 0xD5, 0xDC, 0xDD, 0xE4, 0xE5, 0xEC, 0xED, + 0xF4, 0xF5, 0xFC, 0xFD, + }; + static const u8 snum_init_46[] = { + 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, + 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, + 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, + 0xD8, 0xD9, 0xE8, 0xE9, 0x08, 0x09, 0x18, 0x19, + 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59, + 0x68, 0x69, 0x78, 0x79, 0x80, 0x81, + }; + static const u8 *snum_init; + + qe_num_of_snum = qe_get_num_of_snums(); + + if (qe_num_of_snum == 76) + snum_init = snum_init_76; + else + snum_init = snum_init_46; + + for (i = 0; i < qe_num_of_snum; i++) { + snums[i].num = snum_init[i]; + snums[i].state = QE_SNUM_STATE_FREE; + } +} + +int qe_get_snum(void) +{ + unsigned long flags; + int snum = -EBUSY; + int i; + + spin_lock_irqsave(&qe_lock, flags); + for (i = 0; i < qe_num_of_snum; i++) { + if (snums[i].state == QE_SNUM_STATE_FREE) { + snums[i].state = QE_SNUM_STATE_USED; + snum = snums[i].num; + break; + } + } + spin_unlock_irqrestore(&qe_lock, flags); + + return snum; +} +EXPORT_SYMBOL(qe_get_snum); + +void qe_put_snum(u8 snum) +{ + int i; + + for (i = 0; i < qe_num_of_snum; i++) { + if (snums[i].num == snum) { + snums[i].state = QE_SNUM_STATE_FREE; + break; + } + } +} +EXPORT_SYMBOL(qe_put_snum); + +static int qe_sdma_init(void) +{ + struct sdma __iomem *sdma = &qe_immr->sdma; + static unsigned long sdma_buf_offset = (unsigned long)-ENOMEM; + + if (!sdma) + return -ENODEV; + + /* allocate 2 internal temporary buffers (512 bytes size each) for + * the SDMA */ + if (IS_ERR_VALUE(sdma_buf_offset)) { + sdma_buf_offset = qe_muram_alloc(512 * 2, 4096); + if (IS_ERR_VALUE(sdma_buf_offset)) + return -ENOMEM; + } + + out_be32(&sdma->sdebcr, (u32) sdma_buf_offset & QE_SDEBCR_BA_MASK); + out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK | + (0x1 << QE_SDMR_CEN_SHIFT))); + + return 0; +} + +/* The maximum number of RISCs we support */ +#define MAX_QE_RISC 4 + +/* Firmware information stored here for qe_get_firmware_info() */ +static struct qe_firmware_info qe_firmware_info; + +/* + * Set to 1 if QE firmware has been uploaded, and therefore + * qe_firmware_info contains valid data. + */ +static int qe_firmware_uploaded; + +/* + * Upload a QE microcode + * + * This function is a worker function for qe_upload_firmware(). It does + * the actual uploading of the microcode. + */ +static void qe_upload_microcode(const void *base, + const struct qe_microcode *ucode) +{ + const __be32 *code = base + be32_to_cpu(ucode->code_offset); + unsigned int i; + + if (ucode->major || ucode->minor || ucode->revision) + pr_info("qe-FM: uploading microcode '%s' version %u.%u.%u\n", + ucode->id, ucode->major, ucode->minor, ucode->revision); + else + pr_info("qe-FM: uploading microcode '%s'\n", ucode->id); + + /* Use auto-increment */ + out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | + QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); + + for (i = 0; i < be32_to_cpu(ucode->count); i++) + out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); + + /* Set I-RAM Ready Register */ + out_be32(&qe_immr->iram.iready, be32_to_cpu(QE_IRAM_READY)); +} + +/* + * Upload a microcode to the I-RAM at a specific address. + * + * See Documentation/powerpc/qe_firmware.txt for information on QE microcode + * uploading. + * + * Currently, only version 1 is supported, so the 'version' field must be + * set to 1. + * + * The SOC model and revision are not validated, they are only displayed for + * informational purposes. + * + * 'calc_size' is the calculated size, in bytes, of the firmware structure and + * all of the microcode structures, minus the CRC. + * + * 'length' is the size that the structure says it is, including the CRC. + */ +int qe_upload_firmware(const struct qe_firmware *firmware) +{ + unsigned int i; + unsigned int j; + u32 crc; + size_t calc_size = sizeof(struct qe_firmware); + size_t length; + const struct qe_header *hdr; + + if (!firmware) { + pr_info("qe-firmware: invalid pointer\n"); + return -EINVAL; + } + + hdr = &firmware->header; + length = be32_to_cpu(hdr->length); + + /* Check the magic */ + if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || + (hdr->magic[2] != 'F')) { + pr_info("qe-firmware: not a microcode\n"); + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + pr_info("qe-firmware: unsupported version\n"); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) { + pr_info("qe-firmware: invalid data\n"); + return -EINVAL; + } + + /* Validate the length and check if there's a CRC */ + calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + + for (i = 0; i < firmware->count; i++) + /* + * For situations where the second RISC uses the same microcode + * as the first, the 'code_offset' and 'count' fields will be + * zero, so it's okay to add those. + */ + calc_size += sizeof(__be32) * + be32_to_cpu(firmware->microcode[i].count); + + /* Validate the length */ + if (length != calc_size + sizeof(__be32)) { + pr_info("qe-firmware: invalid length\n"); + return -EPERM; + } + + /* Validate the CRC */ + crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size)); + if (crc != crc32(0, firmware, calc_size)) { + pr_info("qe-firmware: firmware CRC is invalid\n"); + return -EIO; + } + + /* + * If the microcode calls for it, split the I-RAM. + */ + if (!firmware->split) + setbits16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR); + + if (firmware->soc.model) + pr_info("qe-firmware: firmware '%s' for %u V%u.%u\n", + firmware->id, be16_to_cpu(firmware->soc.model), + firmware->soc.major, firmware->soc.minor); + else + pr_info("qe-firmware: firmware '%s'\n", + firmware->id); + + /* + * The QE only supports one microcode per RISC, so clear out all the + * saved microcode information and put in the new. + */ + memset(&qe_firmware_info, 0, sizeof(qe_firmware_info)); + strcpy(qe_firmware_info.id, firmware->id); + qe_firmware_info.extended_modes = firmware->extended_modes; + memcpy(qe_firmware_info.vtraps, firmware->vtraps, + sizeof(firmware->vtraps)); + + /* Loop through each microcode. */ + for (i = 0; i < firmware->count; i++) { + const struct qe_microcode *ucode = &firmware->microcode[i]; + + /* Upload a microcode if it's present */ + if (ucode->code_offset) + qe_upload_microcode(firmware, ucode); + + /* Program the traps for this processor */ + for (j = 0; j < 16; j++) { + u32 trap = be32_to_cpu(ucode->traps[j]); + + if (trap) + out_be32(&qe_immr->rsp[i].tibcr[j], trap); + } + + /* Enable traps */ + out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); + } + + qe_firmware_uploaded = 1; + + return 0; +} +EXPORT_SYMBOL(qe_upload_firmware); + +/* + * Get info on the currently-loaded firmware + * + * This function also checks the device tree to see if the boot loader has + * uploaded a firmware already. + */ +struct qe_firmware_info *qe_get_firmware_info(void) +{ + static int initialized; + struct property *prop; + struct device_node *qe; + struct device_node *fw = NULL; + const char *sprop; + unsigned int i; + + /* + * If we haven't checked yet, and a driver hasn't uploaded a firmware + * yet, then check the device tree for information. + */ + if (qe_firmware_uploaded) + return &qe_firmware_info; + + if (initialized) + return NULL; + + initialized = 1; + + /* + * Newer device trees have an "fsl,qe" compatible property for the QE + * node, but we still need to support older device trees. + */ + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return NULL; + } + + /* Find the 'firmware' child node */ + for_each_child_of_node(qe, fw) { + if (strcmp(fw->name, "firmware") == 0) + break; + } + + of_node_put(qe); + + /* Did we find the 'firmware' node? */ + if (!fw) + return NULL; + + qe_firmware_uploaded = 1; + + /* Copy the data into qe_firmware_info*/ + sprop = of_get_property(fw, "id", NULL); + if (sprop) + strncpy(qe_firmware_info.id, sprop, + sizeof(qe_firmware_info.id) - 1); + + prop = of_find_property(fw, "extended-modes", NULL); + if (prop && (prop->length == sizeof(u64))) { + const u64 *iprop = prop->value; + + qe_firmware_info.extended_modes = *iprop; + } + + prop = of_find_property(fw, "virtual-traps", NULL); + if (prop && (prop->length == 32)) { + const u32 *iprop = prop->value; + + for (i = 0; i < ARRAY_SIZE(qe_firmware_info.vtraps); i++) + qe_firmware_info.vtraps[i] = iprop[i]; + } + + of_node_put(fw); + + return &qe_firmware_info; +} +EXPORT_SYMBOL(qe_get_firmware_info); + +unsigned int qe_get_num_of_risc(void) +{ + struct device_node *qe; + int size; + unsigned int num_of_risc = 0; + const u32 *prop; + + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + /* Older devices trees did not have an "fsl,qe" + * compatible property, so we need to look for + * the QE node by name. + */ + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return num_of_risc; + } + + prop = of_get_property(qe, "fsl,qe-num-riscs", &size); + if (prop && size == sizeof(*prop)) + num_of_risc = *prop; + + of_node_put(qe); + + return num_of_risc; +} +EXPORT_SYMBOL(qe_get_num_of_risc); + +unsigned int qe_get_num_of_snums(void) +{ + struct device_node *qe; + int size; + unsigned int num_of_snums; + const u32 *prop; + + num_of_snums = 28; /* The default number of snum for threads is 28 */ + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!qe) { + /* Older devices trees did not have an "fsl,qe" + * compatible property, so we need to look for + * the QE node by name. + */ + qe = of_find_node_by_type(NULL, "qe"); + if (!qe) + return num_of_snums; + } + + prop = of_get_property(qe, "fsl,qe-num-snums", &size); + if (prop && size == sizeof(*prop)) { + num_of_snums = *prop; + if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) { + /* No QE ever has fewer than 28 SNUMs */ + pr_err("QE: number of snum is invalid\n"); + of_node_put(qe); + return -EINVAL; + } + } + + of_node_put(qe); + + return num_of_snums; +} +EXPORT_SYMBOL(qe_get_num_of_snums); + +#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) +static int qe_resume(struct platform_device *ofdev) +{ + if (!qe_alive_during_sleep()) + qe_reset(); + return 0; +} + +static int qe_probe(struct platform_device *ofdev) +{ + return 0; +} + +static const struct of_device_id qe_ids[] = { + { .compatible = "fsl,qe", }, + { }, +}; + +static struct platform_driver qe_driver = { + .driver = { + .name = "fsl-qe", + .owner = THIS_MODULE, + .of_match_table = qe_ids, + }, + .probe = qe_probe, + .resume = qe_resume, +}; + +static int __init qe_drv_init(void) +{ + return platform_driver_register(&qe_driver); +} +device_initcall(qe_drv_init); +#endif /* defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) */ diff --git a/drivers/soc/qe/qe_ic.c b/drivers/soc/qe/qe_ic.c new file mode 100644 index 0000000..1968f22 --- /dev/null +++ b/drivers/soc/qe/qe_ic.c @@ -0,0 +1,501 @@ +/* + * arch/powerpc/sysdev/qe_lib/qe_ic.c + * + * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Li Yang + * Based on code from Shlomi Gridish + * + * QUICC ENGINE Interrupt Controller + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qe_ic.h" + +static DEFINE_RAW_SPINLOCK(qe_ic_lock); + +static struct qe_ic_info qe_ic_info[] = { + [1] = { + .mask = 0x00008000, + .mask_reg = QEIC_CIMR, + .pri_code = 0, + .pri_reg = QEIC_CIPWCC, + }, + [2] = { + .mask = 0x00004000, + .mask_reg = QEIC_CIMR, + .pri_code = 1, + .pri_reg = QEIC_CIPWCC, + }, + [3] = { + .mask = 0x00002000, + .mask_reg = QEIC_CIMR, + .pri_code = 2, + .pri_reg = QEIC_CIPWCC, + }, + [10] = { + .mask = 0x00000040, + .mask_reg = QEIC_CIMR, + .pri_code = 1, + .pri_reg = QEIC_CIPZCC, + }, + [11] = { + .mask = 0x00000020, + .mask_reg = QEIC_CIMR, + .pri_code = 2, + .pri_reg = QEIC_CIPZCC, + }, + [12] = { + .mask = 0x00000010, + .mask_reg = QEIC_CIMR, + .pri_code = 3, + .pri_reg = QEIC_CIPZCC, + }, + [13] = { + .mask = 0x00000008, + .mask_reg = QEIC_CIMR, + .pri_code = 4, + .pri_reg = QEIC_CIPZCC, + }, + [14] = { + .mask = 0x00000004, + .mask_reg = QEIC_CIMR, + .pri_code = 5, + .pri_reg = QEIC_CIPZCC, + }, + [15] = { + .mask = 0x00000002, + .mask_reg = QEIC_CIMR, + .pri_code = 6, + .pri_reg = QEIC_CIPZCC, + }, + [20] = { + .mask = 0x10000000, + .mask_reg = QEIC_CRIMR, + .pri_code = 3, + .pri_reg = QEIC_CIPRTA, + }, + [25] = { + .mask = 0x00800000, + .mask_reg = QEIC_CRIMR, + .pri_code = 0, + .pri_reg = QEIC_CIPRTB, + }, + [26] = { + .mask = 0x00400000, + .mask_reg = QEIC_CRIMR, + .pri_code = 1, + .pri_reg = QEIC_CIPRTB, + }, + [27] = { + .mask = 0x00200000, + .mask_reg = QEIC_CRIMR, + .pri_code = 2, + .pri_reg = QEIC_CIPRTB, + }, + [28] = { + .mask = 0x00100000, + .mask_reg = QEIC_CRIMR, + .pri_code = 3, + .pri_reg = QEIC_CIPRTB, + }, + [32] = { + .mask = 0x80000000, + .mask_reg = QEIC_CIMR, + .pri_code = 0, + .pri_reg = QEIC_CIPXCC, + }, + [33] = { + .mask = 0x40000000, + .mask_reg = QEIC_CIMR, + .pri_code = 1, + .pri_reg = QEIC_CIPXCC, + }, + [34] = { + .mask = 0x20000000, + .mask_reg = QEIC_CIMR, + .pri_code = 2, + .pri_reg = QEIC_CIPXCC, + }, + [35] = { + .mask = 0x10000000, + .mask_reg = QEIC_CIMR, + .pri_code = 3, + .pri_reg = QEIC_CIPXCC, + }, + [36] = { + .mask = 0x08000000, + .mask_reg = QEIC_CIMR, + .pri_code = 4, + .pri_reg = QEIC_CIPXCC, + }, + [40] = { + .mask = 0x00800000, + .mask_reg = QEIC_CIMR, + .pri_code = 0, + .pri_reg = QEIC_CIPYCC, + }, + [41] = { + .mask = 0x00400000, + .mask_reg = QEIC_CIMR, + .pri_code = 1, + .pri_reg = QEIC_CIPYCC, + }, + [42] = { + .mask = 0x00200000, + .mask_reg = QEIC_CIMR, + .pri_code = 2, + .pri_reg = QEIC_CIPYCC, + }, + [43] = { + .mask = 0x00100000, + .mask_reg = QEIC_CIMR, + .pri_code = 3, + .pri_reg = QEIC_CIPYCC, + }, +}; + +static inline u32 qe_ic_read(__be32 __iomem *base, unsigned int reg) +{ + return in_be32(base + (reg >> 2)); +} + +static inline void qe_ic_write(__be32 __iomem *base, unsigned int reg, + u32 value) +{ + out_be32(base + (reg >> 2), value); +} + +static inline struct qe_ic *qe_ic_from_irq(unsigned int virq) +{ + return irq_get_chip_data(virq); +} + +static inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d) +{ + return irq_data_get_irq_chip_data(d); +} + +static void qe_ic_unmask_irq(struct irq_data *d) +{ + struct qe_ic *qe_ic = qe_ic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); + unsigned long flags; + u32 temp; + + raw_spin_lock_irqsave(&qe_ic_lock, flags); + + temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); + qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, + temp | qe_ic_info[src].mask); + + raw_spin_unlock_irqrestore(&qe_ic_lock, flags); +} + +static void qe_ic_mask_irq(struct irq_data *d) +{ + struct qe_ic *qe_ic = qe_ic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); + unsigned long flags; + u32 temp; + + raw_spin_lock_irqsave(&qe_ic_lock, flags); + + temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); + qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, + temp & ~qe_ic_info[src].mask); + + /* Flush the above write before enabling interrupts; otherwise, + * spurious interrupts will sometimes happen. To be 100% sure + * that the write has reached the device before interrupts are + * enabled, the mask register would have to be read back; however, + * this is not required for correctness, only to avoid wasting + * time on a large number of spurious interrupts. In testing, + * a sync reduced the observed spurious interrupts to zero. + */ + mb(); + + raw_spin_unlock_irqrestore(&qe_ic_lock, flags); +} + +static struct irq_chip qe_ic_irq_chip = { + .name = "QEIC", + .irq_unmask = qe_ic_unmask_irq, + .irq_mask = qe_ic_mask_irq, + .irq_mask_ack = qe_ic_mask_irq, +}; + +static int qe_ic_host_match(struct irq_domain *h, struct device_node *node) +{ + /* Exact match, unless qe_ic node is NULL */ + return h->of_node == NULL || h->of_node == node; +} + +static int qe_ic_host_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct qe_ic *qe_ic = h->host_data; + struct irq_chip *chip; + + if (qe_ic_info[hw].mask == 0) { + pr_err("Can't map reserved IRQ\n"); + return -EINVAL; + } + /* Default chip */ + chip = &qe_ic->hc_irq; + + irq_set_chip_data(virq, qe_ic); + irq_set_status_flags(virq, IRQ_LEVEL); + + irq_set_chip_and_handler(virq, chip, handle_level_irq); + + return 0; +} + +static struct irq_domain_ops qe_ic_host_ops = { + .match = qe_ic_host_match, + .map = qe_ic_host_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ +unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic) +{ + int irq; + + BUG_ON(qe_ic == NULL); + + /* get the interrupt source vector. */ + irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26; + + if (irq == 0) + return NO_IRQ; + + return irq_linear_revmap(qe_ic->irqhost, irq); +} + +/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ +unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) +{ + int irq; + + BUG_ON(qe_ic == NULL); + + /* get the interrupt source vector. */ + irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26; + + if (irq == 0) + return NO_IRQ; + + return irq_linear_revmap(qe_ic->irqhost, irq); +} + +void __init qe_ic_init(struct device_node *node, unsigned int flags, + void (*low_handler)(unsigned int irq, struct irq_desc *desc), + void (*high_handler)(unsigned int irq, struct irq_desc *desc)) +{ + struct qe_ic *qe_ic; + struct resource res; + u32 temp = 0, ret, high_active = 0; + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return; + + qe_ic = kzalloc(sizeof(*qe_ic), GFP_KERNEL); + if (qe_ic == NULL) + return; + + qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS, + &qe_ic_host_ops, qe_ic); + if (qe_ic->irqhost == NULL) { + kfree(qe_ic); + return; + } + + qe_ic->regs = ioremap(res.start, resource_size(&res)); + + qe_ic->hc_irq = qe_ic_irq_chip; + + qe_ic->virq_high = irq_of_parse_and_map(node, 0); + qe_ic->virq_low = irq_of_parse_and_map(node, 1); + + if (qe_ic->virq_low == NO_IRQ) { + pr_err("Failed to map QE_IC low IRQ\n"); + kfree(qe_ic); + return; + } + + /* default priority scheme is grouped. If spread mode is */ + /* required, configure cicr accordingly. */ + if (flags & QE_IC_SPREADMODE_GRP_W) + temp |= CICR_GWCC; + if (flags & QE_IC_SPREADMODE_GRP_X) + temp |= CICR_GXCC; + if (flags & QE_IC_SPREADMODE_GRP_Y) + temp |= CICR_GYCC; + if (flags & QE_IC_SPREADMODE_GRP_Z) + temp |= CICR_GZCC; + if (flags & QE_IC_SPREADMODE_GRP_RISCA) + temp |= CICR_GRTA; + if (flags & QE_IC_SPREADMODE_GRP_RISCB) + temp |= CICR_GRTB; + + /* choose destination signal for highest priority interrupt */ + if (flags & QE_IC_HIGH_SIGNAL) { + temp |= (SIGNAL_HIGH << CICR_HPIT_SHIFT); + high_active = 1; + } + + qe_ic_write(qe_ic->regs, QEIC_CICR, temp); + + irq_set_handler_data(qe_ic->virq_low, qe_ic); + irq_set_chained_handler(qe_ic->virq_low, low_handler); + + if (qe_ic->virq_high != NO_IRQ && + qe_ic->virq_high != qe_ic->virq_low) { + irq_set_handler_data(qe_ic->virq_high, qe_ic); + irq_set_chained_handler(qe_ic->virq_high, high_handler); + } +} + +void qe_ic_set_highest_priority(unsigned int virq, int high) +{ + struct qe_ic *qe_ic = qe_ic_from_irq(virq); + unsigned int src = virq_to_hw(virq); + u32 temp = 0; + + temp = qe_ic_read(qe_ic->regs, QEIC_CICR); + + temp &= ~CICR_HP_MASK; + temp |= src << CICR_HP_SHIFT; + + temp &= ~CICR_HPIT_MASK; + temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << CICR_HPIT_SHIFT; + + qe_ic_write(qe_ic->regs, QEIC_CICR, temp); +} + +/* Set Priority level within its group, from 1 to 8 */ +int qe_ic_set_priority(unsigned int virq, unsigned int priority) +{ + struct qe_ic *qe_ic = qe_ic_from_irq(virq); + unsigned int src = virq_to_hw(virq); + u32 temp; + + if (priority > 8 || priority == 0) + return -EINVAL; + if (src > 127) + return -EINVAL; + if (qe_ic_info[src].pri_reg == 0) + return -EINVAL; + + temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].pri_reg); + + if (priority < 4) { + temp &= ~(0x7 << (32 - priority * 3)); + temp |= qe_ic_info[src].pri_code << (32 - priority * 3); + } else { + temp &= ~(0x7 << (24 - priority * 3)); + temp |= qe_ic_info[src].pri_code << (24 - priority * 3); + } + + qe_ic_write(qe_ic->regs, qe_ic_info[src].pri_reg, temp); + + return 0; +} + +/* Set a QE priority to use high irq, only priority 1~2 can use high irq */ +int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high) +{ + struct qe_ic *qe_ic = qe_ic_from_irq(virq); + unsigned int src = virq_to_hw(virq); + u32 temp, control_reg = QEIC_CICNR, shift = 0; + + if (priority > 2 || priority == 0) + return -EINVAL; + + switch (qe_ic_info[src].pri_reg) { + case QEIC_CIPZCC: + shift = CICNR_ZCC1T_SHIFT; + break; + case QEIC_CIPWCC: + shift = CICNR_WCC1T_SHIFT; + break; + case QEIC_CIPYCC: + shift = CICNR_YCC1T_SHIFT; + break; + case QEIC_CIPXCC: + shift = CICNR_XCC1T_SHIFT; + break; + case QEIC_CIPRTA: + shift = CRICR_RTA1T_SHIFT; + control_reg = QEIC_CRICR; + break; + case QEIC_CIPRTB: + shift = CRICR_RTB1T_SHIFT; + control_reg = QEIC_CRICR; + break; + default: + return -EINVAL; + } + + shift += (2 - priority) * 2; + temp = qe_ic_read(qe_ic->regs, control_reg); + temp &= ~(SIGNAL_MASK << shift); + temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << shift; + qe_ic_write(qe_ic->regs, control_reg, temp); + + return 0; +} + +static struct bus_type qe_ic_subsys = { + .name = "qe_ic", + .dev_name = "qe_ic", +}; + +static struct device device_qe_ic = { + .id = 0, + .bus = &qe_ic_subsys, +}; + +static int __init init_qe_ic_sysfs(void) +{ + int rc; + + pr_debug("Registering qe_ic with sysfs...\n"); + + rc = subsys_system_register(&qe_ic_subsys, NULL); + if (rc) { + pr_err("Failed registering qe_ic sys class\n"); + return -ENODEV; + } + rc = device_register(&device_qe_ic); + if (rc) { + pr_err("Failed registering qe_ic sys device\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_qe_ic_sysfs); diff --git a/drivers/soc/qe/qe_ic.h b/drivers/soc/qe/qe_ic.h new file mode 100644 index 0000000..aab8abd --- /dev/null +++ b/drivers/soc/qe/qe_ic.h @@ -0,0 +1,103 @@ +/* + * arch/powerpc/sysdev/qe_lib/qe_ic.h + * + * QUICC ENGINE Interrupt Controller Header + * + * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Li Yang + * Based on code from Shlomi Gridish + * + * 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. + */ +#ifndef _POWERPC_SYSDEV_QE_IC_H +#define _POWERPC_SYSDEV_QE_IC_H + +#include + +#define NR_QE_IC_INTS 64 + +/* QE IC registers offset */ +#define QEIC_CICR 0x00 +#define QEIC_CIVEC 0x04 +#define QEIC_CRIPNR 0x08 +#define QEIC_CIPNR 0x0c +#define QEIC_CIPXCC 0x10 +#define QEIC_CIPYCC 0x14 +#define QEIC_CIPWCC 0x18 +#define QEIC_CIPZCC 0x1c +#define QEIC_CIMR 0x20 +#define QEIC_CRIMR 0x24 +#define QEIC_CICNR 0x28 +#define QEIC_CIPRTA 0x30 +#define QEIC_CIPRTB 0x34 +#define QEIC_CRICR 0x3c +#define QEIC_CHIVEC 0x60 + +/* Interrupt priority registers */ +#define CIPCC_SHIFT_PRI0 29 +#define CIPCC_SHIFT_PRI1 26 +#define CIPCC_SHIFT_PRI2 23 +#define CIPCC_SHIFT_PRI3 20 +#define CIPCC_SHIFT_PRI4 13 +#define CIPCC_SHIFT_PRI5 10 +#define CIPCC_SHIFT_PRI6 7 +#define CIPCC_SHIFT_PRI7 4 + +/* CICR priority modes */ +#define CICR_GWCC 0x00040000 +#define CICR_GXCC 0x00020000 +#define CICR_GYCC 0x00010000 +#define CICR_GZCC 0x00080000 +#define CICR_GRTA 0x00200000 +#define CICR_GRTB 0x00400000 +#define CICR_HPIT_SHIFT 8 +#define CICR_HPIT_MASK 0x00000300 +#define CICR_HP_SHIFT 24 +#define CICR_HP_MASK 0x3f000000 + +/* CICNR */ +#define CICNR_WCC1T_SHIFT 20 +#define CICNR_ZCC1T_SHIFT 28 +#define CICNR_YCC1T_SHIFT 12 +#define CICNR_XCC1T_SHIFT 4 + +/* CRICR */ +#define CRICR_RTA1T_SHIFT 20 +#define CRICR_RTB1T_SHIFT 28 + +/* Signal indicator */ +#define SIGNAL_MASK 3 +#define SIGNAL_HIGH 2 +#define SIGNAL_LOW 0 + +struct qe_ic { + /* Control registers offset */ + u32 __iomem *regs; + + /* The remapper for this QEIC */ + struct irq_domain *irqhost; + + /* The "linux" controller struct */ + struct irq_chip hc_irq; + + /* VIRQ numbers of QE high/low irqs */ + unsigned int virq_high; + unsigned int virq_low; +}; + +/* + * QE interrupt controller internal structure + */ +struct qe_ic_info { + u32 mask; /* location of this source at the QIMR register. */ + u32 mask_reg; /* Mask register offset */ + u8 pri_code; /* for grouped interrupts sources - the interrupt + code as appears at the group priority register */ + u32 pri_reg; /* Group priority register offset */ +}; + +#endif /* _POWERPC_SYSDEV_QE_IC_H */ diff --git a/drivers/soc/qe/qe_io.c b/drivers/soc/qe/qe_io.c new file mode 100644 index 0000000..939e903 --- /dev/null +++ b/drivers/soc/qe/qe_io.c @@ -0,0 +1,217 @@ +/* + * arch/powerpc/sysdev/qe_lib/qe_io.c + * + * QE Parallel I/O ports configuration routines + * + * Copyright 2006 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Li Yang + * Based on code from Shlomi Gridish + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#undef DEBUG + +static struct qe_pio_regs __iomem *par_io; +static int num_par_io_ports; + +int par_io_init(struct device_node *np) +{ + struct resource res; + int ret; + const u32 *num_ports; + + /* Map Parallel I/O ports registers */ + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + par_io = ioremap(res.start, resource_size(&res)); + + num_ports = of_get_property(np, "num-ports", NULL); + if (num_ports) + num_par_io_ports = *num_ports; + + return 0; +} + +void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, + int open_drain, int assignment, int has_irq) +{ + u32 pin_mask1bit; + u32 pin_mask2bits; + u32 new_mask2bits; + u32 tmp_val; + + /* calculate pin location for single and 2 bits information */ + pin_mask1bit = (u32) (1 << (QE_PIO_PINS - (pin + 1))); + + /* Set open drain, if required */ + tmp_val = in_be32(&par_io->cpodr); + if (open_drain) + out_be32(&par_io->cpodr, pin_mask1bit | tmp_val); + else + out_be32(&par_io->cpodr, ~pin_mask1bit & tmp_val); + + /* define direction */ + tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? + in_be32(&par_io->cpdir2) : + in_be32(&par_io->cpdir1); + + /* get all bits mask for 2 bit per port */ + pin_mask2bits = (u32) (0x3 << (QE_PIO_PINS - + (pin % (QE_PIO_PINS / 2) + 1) * 2)); + + /* Get the final mask we need for the right definition */ + new_mask2bits = (u32) (dir << (QE_PIO_PINS - + (pin % (QE_PIO_PINS / 2) + 1) * 2)); + + /* clear and set 2 bits mask */ + if (pin > (QE_PIO_PINS / 2) - 1) { + out_be32(&par_io->cpdir2, + ~pin_mask2bits & tmp_val); + tmp_val &= ~pin_mask2bits; + out_be32(&par_io->cpdir2, new_mask2bits | tmp_val); + } else { + out_be32(&par_io->cpdir1, + ~pin_mask2bits & tmp_val); + tmp_val &= ~pin_mask2bits; + out_be32(&par_io->cpdir1, new_mask2bits | tmp_val); + } + /* define pin assignment */ + tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? + in_be32(&par_io->cppar2) : + in_be32(&par_io->cppar1); + + new_mask2bits = (u32) (assignment << (QE_PIO_PINS - + (pin % (QE_PIO_PINS / 2) + 1) * 2)); + /* clear and set 2 bits mask */ + if (pin > (QE_PIO_PINS / 2) - 1) { + out_be32(&par_io->cppar2, + ~pin_mask2bits & tmp_val); + tmp_val &= ~pin_mask2bits; + out_be32(&par_io->cppar2, new_mask2bits | tmp_val); + } else { + out_be32(&par_io->cppar1, + ~pin_mask2bits & tmp_val); + tmp_val &= ~pin_mask2bits; + out_be32(&par_io->cppar1, new_mask2bits | tmp_val); + } +} +EXPORT_SYMBOL(__par_io_config_pin); + +int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, + int assignment, int has_irq) +{ + if (!par_io || port >= num_par_io_ports) + return -EINVAL; + + __par_io_config_pin(&par_io[port], pin, dir, open_drain, assignment, + has_irq); + return 0; +} +EXPORT_SYMBOL(par_io_config_pin); + +int par_io_data_set(u8 port, u8 pin, u8 val) +{ + u32 pin_mask, tmp_val; + + if (port >= num_par_io_ports) + return -EINVAL; + if (pin >= QE_PIO_PINS) + return -EINVAL; + /* calculate pin location */ + pin_mask = (u32) (1 << (QE_PIO_PINS - 1 - pin)); + + tmp_val = in_be32(&par_io[port].cpdata); + + if (val == 0) /* clear */ + out_be32(&par_io[port].cpdata, ~pin_mask & tmp_val); + else /* set */ + out_be32(&par_io[port].cpdata, pin_mask | tmp_val); + + return 0; +} +EXPORT_SYMBOL(par_io_data_set); + +int par_io_of_config(struct device_node *np) +{ + struct device_node *pio; + const phandle *ph; + int pio_map_len; + const unsigned int *pio_map; + + if (par_io == NULL) { + pr_info("par_io not initialized\n"); + return -1; + } + + ph = of_get_property(np, "pio-handle", NULL); + if (ph == NULL) { + pr_info("pio-handle not available\n"); + return -1; + } + + pio = of_find_node_by_phandle(*ph); + + pio_map = of_get_property(pio, "pio-map", &pio_map_len); + if (pio_map == NULL) { + pr_info("pio-map is not set!\n"); + return -1; + } + pio_map_len /= sizeof(unsigned int); + if ((pio_map_len % 6) != 0) { + pr_info("pio-map format wrong!\n"); + return -1; + } + + while (pio_map_len > 0) { + par_io_config_pin((u8) pio_map[0], (u8) pio_map[1], + (int) pio_map[2], (int) pio_map[3], + (int) pio_map[4], (int) pio_map[5]); + pio_map += 6; + pio_map_len -= 6; + } + of_node_put(pio); + return 0; +} +EXPORT_SYMBOL(par_io_of_config); + +#ifdef DEBUG +static void dump_par_io(void) +{ + unsigned int i; + + pr_info("%s: par_io=%p\n", __func__, par_io); + for (i = 0; i < num_par_io_ports; i++) { + pr_info(" cpodr[%u]=%08x\n", i, + in_be32(&par_io[i].cpodr)); + pr_info(" cpdata[%u]=%08x\n", i, + in_be32(&par_io[i].cpdata)); + pr_info(" cpdir1[%u]=%08x\n", i, + in_be32(&par_io[i].cpdir1)); + pr_info(" cpdir2[%u]=%08x\n", i, + in_be32(&par_io[i].cpdir2)); + pr_info(" cppar1[%u]=%08x\n", i, + in_be32(&par_io[i].cppar1)); + pr_info(" cppar2[%u]=%08x\n", i, + in_be32(&par_io[i].cppar2)); + } +} +EXPORT_SYMBOL(dump_par_io); +#endif /* DEBUG */ diff --git a/drivers/soc/qe/ucc.c b/drivers/soc/qe/ucc.c new file mode 100644 index 0000000..c333387 --- /dev/null +++ b/drivers/soc/qe/ucc.c @@ -0,0 +1,1081 @@ +/* + * arch/powerpc/sysdev/qe_lib/ucc.c + * + * QE UCC API Set - UCC specific routines implementations. + * + * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int ucc_set_qe_mux_mii_mng(unsigned int ucc_num) +{ + unsigned long flags; + + if (ucc_num > UCC_MAX_NUM - 1) + return -EINVAL; + + spin_lock_irqsave(&cmxgcr_lock, flags); + clrsetbits_be32(&qe_immr->qmx.cmxgcr, QE_CMXGCR_MII_ENET_MNG, + ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT); + spin_unlock_irqrestore(&cmxgcr_lock, flags); + + return 0; +} +EXPORT_SYMBOL(ucc_set_qe_mux_mii_mng); + +/* Configure the UCC to either Slow or Fast. + * + * A given UCC can be figured to support either "slow" devices (e.g. UART) + * or "fast" devices (e.g. Ethernet). + * + * 'ucc_num' is the UCC number, from 0 - 7. + * + * This function also sets the UCC_GUEMR_SET_RESERVED3 bit because that bit + * must always be set to 1. + */ +int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed) +{ + u8 __iomem *guemr; + + /* The GUEMR register is at the same location for both slow and fast + devices, so we just use uccX.slow.guemr. */ + switch (ucc_num) { + case 0: + guemr = &qe_immr->ucc1.slow.guemr; + break; + case 1: + guemr = &qe_immr->ucc2.slow.guemr; + break; + case 2: + guemr = &qe_immr->ucc3.slow.guemr; + break; + case 3: + guemr = &qe_immr->ucc4.slow.guemr; + break; + case 4: + guemr = &qe_immr->ucc5.slow.guemr; + break; + case 5: + guemr = &qe_immr->ucc6.slow.guemr; + break; + case 6: + guemr = &qe_immr->ucc7.slow.guemr; + break; + case 7: + guemr = &qe_immr->ucc8.slow.guemr; + break; + default: + return -EINVAL; + } + + clrsetbits_8(guemr, UCC_GUEMR_MODE_MASK, + UCC_GUEMR_SET_RESERVED3 | speed); + + return 0; +} + +static void get_cmxucr_reg(unsigned int ucc_num, __be32 __iomem **cmxucr, + unsigned int *reg_num, unsigned int *shift) +{ + unsigned int cmx = ((ucc_num & 1) << 1) + (ucc_num > 3); + + *reg_num = cmx + 1; + *cmxucr = &qe_immr->qmx.cmxucr[cmx]; + *shift = 16 - 8 * (ucc_num & 2); +} + +int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask) +{ + __be32 __iomem *cmxucr; + unsigned int reg_num; + unsigned int shift; + + /* check if the UCC number is in range. */ + if (ucc_num > UCC_MAX_NUM - 1) + return -EINVAL; + + get_cmxucr_reg(ucc_num, &cmxucr, ®_num, &shift); + + if (set) + setbits32(cmxucr, mask << shift); + else + clrbits32(cmxucr, mask << shift); + + return 0; +} + +int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, + enum comm_dir mode) +{ + __be32 __iomem *cmxucr; + unsigned int reg_num; + unsigned int shift; + u32 clock_bits = 0; + + /* check if the UCC number is in range. */ + if (ucc_num > UCC_MAX_NUM - 1) + return -EINVAL; + + /* The communications direction must be RX or TX */ + if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) + return -EINVAL; + + get_cmxucr_reg(ucc_num, &cmxucr, ®_num, &shift); + + switch (reg_num) { + case 1: + switch (clock) { + case QE_BRG1: + clock_bits = 1; + break; + case QE_BRG2: + clock_bits = 2; + break; + case QE_BRG7: + clock_bits = 3; + break; + case QE_BRG8: + clock_bits = 4; + break; + case QE_CLK9: + clock_bits = 5; + break; + case QE_CLK10: + clock_bits = 6; + break; + case QE_CLK11: + clock_bits = 7; + break; + case QE_CLK12: + clock_bits = 8; + break; + case QE_CLK15: + clock_bits = 9; + break; + case QE_CLK16: + clock_bits = 10; + break; + default: + break; + } + break; + case 2: + switch (clock) { + case QE_BRG5: + clock_bits = 1; + break; + case QE_BRG6: + clock_bits = 2; + break; + case QE_BRG7: + clock_bits = 3; + break; + case QE_BRG8: + clock_bits = 4; + break; + case QE_CLK13: + clock_bits = 5; + break; + case QE_CLK14: + clock_bits = 6; + break; + case QE_CLK19: + clock_bits = 7; + break; + case QE_CLK20: + clock_bits = 8; + break; + case QE_CLK15: + clock_bits = 9; + break; + case QE_CLK16: + clock_bits = 10; + break; + default: + break; + } + break; + case 3: + switch (clock) { + case QE_BRG9: + clock_bits = 1; + break; + case QE_BRG10: + clock_bits = 2; + break; + case QE_BRG15: + clock_bits = 3; + break; + case QE_BRG16: + clock_bits = 4; + break; + case QE_CLK3: + clock_bits = 5; + break; + case QE_CLK4: + clock_bits = 6; + break; + case QE_CLK17: + clock_bits = 7; + break; + case QE_CLK18: + clock_bits = 8; + break; + case QE_CLK7: + clock_bits = 9; + break; + case QE_CLK8: + clock_bits = 10; + break; + case QE_CLK16: + clock_bits = 11; + break; + default: + break; + } + break; + case 4: + switch (clock) { + case QE_BRG13: + clock_bits = 1; + break; + case QE_BRG14: + clock_bits = 2; + break; + case QE_BRG15: + clock_bits = 3; + break; + case QE_BRG16: + clock_bits = 4; + break; + case QE_CLK5: + clock_bits = 5; + break; + case QE_CLK6: + clock_bits = 6; + break; + case QE_CLK21: + clock_bits = 7; + break; + case QE_CLK22: + clock_bits = 8; + break; + case QE_CLK7: + clock_bits = 9; + break; + case QE_CLK8: + clock_bits = 10; + break; + case QE_CLK16: + clock_bits = 11; + break; + default: + break; + } + break; + default: + break; + } + + /* Check for invalid combination of clock and UCC number */ + if (!clock_bits) + return -ENOENT; + + if (mode == COMM_DIR_RX) + shift += 4; + + clrsetbits_be32(cmxucr, QE_CMXUCR_TX_CLK_SRC_MASK << shift, + clock_bits << shift); + + return 0; +} + +/* tdm_num: TDM A-H port num is 0-7 */ +int ucc_set_tdm_rxtx_clk(u32 tdm_num, enum qe_clock clock, + enum comm_dir mode) +{ + u32 clock_bits, shift; + struct qe_mux *qe_mux_reg; + __be32 __iomem *cmxs1cr; + + clock_bits = 0; + qe_mux_reg = &qe_immr->qmx; + + if ((tdm_num > 7 || tdm_num < 0)) + return -EINVAL; + + /* The communications direction must be RX or TX */ + if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) + return -EINVAL; + + switch (mode) { + case COMM_DIR_RX: + switch (tdm_num) { + case 0: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK3: + clock_bits = 6; + break; + case QE_CLK8: + clock_bits = 7; + break; + default: + break; + } + shift = 28; + break; + case 1: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK5: + clock_bits = 6; + break; + case QE_CLK10: + clock_bits = 7; + break; + default: + break; + } + shift = 24; + break; + case 2: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK7: + clock_bits = 6; + break; + case QE_CLK12: + clock_bits = 7; + break; + default: + break; + } + shift = 20; + break; + case 3: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK9: + clock_bits = 6; + break; + case QE_CLK14: + clock_bits = 7; + break; + default: + break; + } + shift = 16; + break; + case 4: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK11: + clock_bits = 6; + break; + case QE_CLK16: + clock_bits = 7; + break; + default: + break; + } + shift = 28; + break; + case 5: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK13: + clock_bits = 6; + break; + case QE_CLK18: + clock_bits = 7; + break; + default: + break; + } + shift = 24; + break; + case 6: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK15: + clock_bits = 6; + break; + case QE_CLK20: + clock_bits = 7; + break; + default: + break; + } + shift = 20; + break; + case 7: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK17: + clock_bits = 6; + break; + case QE_CLK22: + clock_bits = 7; + break; + default: + break; + } + shift = 16; + break; + default: + break; + } + break; + case COMM_DIR_TX: + switch (tdm_num) { + case 0: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK4: + clock_bits = 6; + break; + case QE_CLK9: + clock_bits = 7; + break; + default: + break; + } + shift = 12; + break; + case 1: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK6: + clock_bits = 6; + break; + case QE_CLK11: + clock_bits = 7; + break; + default: + break; + } + shift = 8; + break; + case 2: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK8: + clock_bits = 6; + break; + case QE_CLK13: + clock_bits = 7; + break; + default: + break; + } + shift = 4; + break; + case 3: + switch (clock) { + case QE_BRG3: + clock_bits = 1; + break; + case QE_BRG4: + clock_bits = 2; + break; + case QE_CLK1: + clock_bits = 4; + break; + case QE_CLK2: + clock_bits = 5; + break; + case QE_CLK10: + clock_bits = 6; + break; + case QE_CLK15: + clock_bits = 7; + break; + default: + break; + } + shift = 0; + break; + case 4: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK12: + clock_bits = 6; + break; + case QE_CLK17: + clock_bits = 7; + break; + default: + break; + } + shift = 12; + break; + case 5: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK14: + clock_bits = 6; + break; + case QE_CLK19: + clock_bits = 7; + break; + default: + break; + } + shift = 8; + break; + case 6: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK16: + clock_bits = 6; + break; + case QE_CLK21: + clock_bits = 7; + break; + default: + break; + } + shift = 4; + break; + case 7: + switch (clock) { + case QE_BRG12: + clock_bits = 1; + break; + case QE_BRG13: + clock_bits = 2; + break; + case QE_CLK23: + clock_bits = 4; + break; + case QE_CLK24: + clock_bits = 5; + break; + case QE_CLK18: + clock_bits = 6; + break; + case QE_CLK3: + clock_bits = 7; + break; + default: + break; + } + shift = 0; + break; + default: + break; + } + break; + default: + break; + } + + if (!clock_bits) + return -ENOENT; + + cmxs1cr = (tdm_num < 4) ? (&qe_mux_reg->cmxsi1cr_l) : + (&qe_mux_reg->cmxsi1cr_h); + + clrsetbits_be32(cmxs1cr, QE_CMXUCR_TX_CLK_SRC_MASK << shift, + clock_bits << shift); + + return 0; +} + + +int ucc_set_tdm_rxtx_sync(u32 tdm_num, enum qe_clock clock, + enum comm_dir mode) +{ + u32 shift, clock_bits; + struct qe_mux *qe_mux_reg; + int source; + + source = 0; + shift = 0; + qe_mux_reg = &qe_immr->qmx; + + if ((tdm_num > 7 || tdm_num < 0)) + return -EINVAL; + + /* The communications direction must be RX or TX */ + if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) + return -EINVAL; + + switch (mode) { + case COMM_DIR_RX: + switch (tdm_num) { + case 0: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG10: + source = 2; + break; + default: + source = -1; + break; + } + shift = 30; + break; + case 1: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG10: + source = 2; + break; + default: + source = -1; + break; + } + shift = 28; + break; + case 2: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG11: + source = 2; + break; + default: + source = -1; + break; + } + shift = 26; + break; + case 3: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG11: + source = 2; + break; + default: + source = -1; + break; + } + shift = 24; + break; + case 4: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG14: + source = 2; + break; + default: + source = -1; + break; + } + shift = 22; + break; + case 5: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG14: + source = 2; + break; + default: + source = -1; + break; + } + shift = 20; + break; + case 6: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG15: + source = 2; + break; + default: + source = -1; + break; + } + shift = 18; + break; + case 7: + switch (clock) { + case QE_RSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG15: + source = 2; + break; + default: + source = -1; + break; + } + shift = 16; + break; + default: + source = -1; + break; + } + break; + case COMM_DIR_TX: + switch (tdm_num) { + case 0: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG10: + source = 2; + break; + default: + source = -1; + break; + } + shift = 14; + break; + case 1: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG10: + source = 2; + break; + default: + source = -1; + break; + } + shift = 12; + break; + case 2: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG11: + source = 2; + break; + default: + source = -1; + break; + } + shift = 10; + break; + case 3: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG9: + source = 1; + break; + case QE_BRG11: + source = 2; + break; + default: + source = -1; + break; + } + shift = 8; + break; + case 4: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG14: + source = 2; + break; + default: + source = -1; + break; + } + shift = 6; + break; + case 5: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG14: + source = 2; + break; + default: + source = -1; + break; + } + shift = 4; + break; + case 6: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG15: + source = 2; + break; + default: + source = -1; + break; + } + shift = 2; + break; + case 7: + switch (clock) { + case QE_TSYNC_PIN: + source = 0; + break; + case QE_BRG13: + source = 1; + break; + case QE_BRG15: + source = 2; + break; + default: + source = -1; + break; + } + shift = 0; + break; + + default: + source = -1; + break; + } + break; + default: + source = -1; + break; + } + + if (source == -1) + return -ENOENT; + + clock_bits = (u32) source; + + clrsetbits_be32(&qe_mux_reg->cmxsi1syr, + QE_CMXUCR_TX_CLK_SRC_MASK << shift, + clock_bits << shift); + + return 0; +} diff --git a/drivers/soc/qe/ucc_fast.c b/drivers/soc/qe/ucc_fast.c new file mode 100644 index 0000000..8088852 --- /dev/null +++ b/drivers/soc/qe/ucc_fast.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * Description: + * QE UCC Fast API Set - UCC Fast specific routines implementations. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +void ucc_fast_dump_regs(struct ucc_fast_private *uccf) +{ + pr_info("UCC%u Fast registers:\n", uccf->uf_info->ucc_num); + pr_info("Base address: 0x%p\n", uccf->uf_regs); + + pr_info("gumr : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->gumr, in_be32(&uccf->uf_regs->gumr)); + pr_info("upsmr : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->upsmr, in_be32(&uccf->uf_regs->upsmr)); + pr_info("utodr : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utodr, in_be16(&uccf->uf_regs->utodr)); + pr_info("udsr : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->udsr, in_be16(&uccf->uf_regs->udsr)); + pr_info("ucce : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->ucce, in_be32(&uccf->uf_regs->ucce)); + pr_info("uccm : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->uccm, in_be32(&uccf->uf_regs->uccm)); + pr_info("uccs : addr=0x%p, val=0x%02x\n", + &uccf->uf_regs->uccs, in_8(&uccf->uf_regs->uccs)); + pr_info("urfb : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->urfb, in_be32(&uccf->uf_regs->urfb)); + pr_info("urfs : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->urfs, in_be16(&uccf->uf_regs->urfs)); + pr_info("urfet : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->urfet, in_be16(&uccf->uf_regs->urfet)); + pr_info("urfset: addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->urfset, in_be16(&uccf->uf_regs->urfset)); + pr_info("utfb : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->utfb, in_be32(&uccf->uf_regs->utfb)); + pr_info("utfs : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utfs, in_be16(&uccf->uf_regs->utfs)); + pr_info("utfet : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utfet, in_be16(&uccf->uf_regs->utfet)); + pr_info("utftt : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utftt, in_be16(&uccf->uf_regs->utftt)); + pr_info("utpt : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utpt, in_be16(&uccf->uf_regs->utpt)); + pr_info("urtry : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->urtry, in_be32(&uccf->uf_regs->urtry)); + pr_info("guemr : addr=0x%p, val=0x%02x\n", + &uccf->uf_regs->guemr, in_8(&uccf->uf_regs->guemr)); +} +EXPORT_SYMBOL(ucc_fast_dump_regs); + +u32 ucc_fast_get_qe_cr_subblock(int uccf_num) +{ + switch (uccf_num) { + case 0: return QE_CR_SUBBLOCK_UCCFAST1; + case 1: return QE_CR_SUBBLOCK_UCCFAST2; + case 2: return QE_CR_SUBBLOCK_UCCFAST3; + case 3: return QE_CR_SUBBLOCK_UCCFAST4; + case 4: return QE_CR_SUBBLOCK_UCCFAST5; + case 5: return QE_CR_SUBBLOCK_UCCFAST6; + case 6: return QE_CR_SUBBLOCK_UCCFAST7; + case 7: return QE_CR_SUBBLOCK_UCCFAST8; + default: return QE_CR_SUBBLOCK_INVALID; + } +} +EXPORT_SYMBOL(ucc_fast_get_qe_cr_subblock); + +void ucc_fast_transmit_on_demand(struct ucc_fast_private *uccf) +{ + out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); +} +EXPORT_SYMBOL(ucc_fast_transmit_on_demand); + +void ucc_fast_enable(struct ucc_fast_private *uccf, enum comm_dir mode) +{ + struct ucc_fast __iomem *uf_regs; + u32 gumr; + + uf_regs = uccf->uf_regs; + + /* Enable reception and/or transmission on this UCC. */ + gumr = in_be32(&uf_regs->gumr); + if (mode & COMM_DIR_TX) { + gumr |= UCC_FAST_GUMR_ENT; + uccf->enabled_tx = 1; + } + if (mode & COMM_DIR_RX) { + gumr |= UCC_FAST_GUMR_ENR; + uccf->enabled_rx = 1; + } + out_be32(&uf_regs->gumr, gumr); +} +EXPORT_SYMBOL(ucc_fast_enable); + +void ucc_fast_disable(struct ucc_fast_private *uccf, enum comm_dir mode) +{ + struct ucc_fast __iomem *uf_regs; + u32 gumr; + + uf_regs = uccf->uf_regs; + + /* Disable reception and/or transmission on this UCC. */ + gumr = in_be32(&uf_regs->gumr); + if (mode & COMM_DIR_TX) { + gumr &= ~UCC_FAST_GUMR_ENT; + uccf->enabled_tx = 0; + } + if (mode & COMM_DIR_RX) { + gumr &= ~UCC_FAST_GUMR_ENR; + uccf->enabled_rx = 0; + } + out_be32(&uf_regs->gumr, gumr); +} +EXPORT_SYMBOL(ucc_fast_disable); + +int ucc_fast_init(struct ucc_fast_info *uf_info, + struct ucc_fast_private **uccf_ret) +{ + struct ucc_fast_private *uccf; + struct ucc_fast __iomem *uf_regs; + u32 gumr; + int ret; + + if (!uf_info) + return -EINVAL; + + /* check if the UCC port number is in range. */ + if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) { + pr_err("%s: illegal UCC number\n", __func__); + return -EINVAL; + } + + /* Check that 'max_rx_buf_length' is properly aligned (4). */ + if (uf_info->max_rx_buf_length & (UCC_FAST_MRBLR_ALIGNMENT - 1)) { + pr_err("%s: max_rx_buf_length not aligned\n", + __func__); + return -EINVAL; + } + + /* Validate Virtual Fifo register values */ + if (uf_info->urfs < UCC_FAST_URFS_MIN_VAL) { + pr_err("%s: urfs is too small\n", __func__); + return -EINVAL; + } + + if (uf_info->urfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { + pr_err("%s: urfs is not aligned\n", __func__); + return -EINVAL; + } + + if (uf_info->urfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { + pr_err("%s: urfet is not aligned.\n", __func__); + return -EINVAL; + } + + if (uf_info->urfset & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { + pr_err("%s: urfset is not aligned\n", __func__); + return -EINVAL; + } + + if (uf_info->utfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { + pr_err("%s: utfs is not aligned\n", __func__); + return -EINVAL; + } + + if (uf_info->utfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { + pr_err("%s: utfet is not aligned\n", __func__); + return -EINVAL; + } + + if (uf_info->utftt & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { + pr_err("%s: utftt is not aligned\n", __func__); + return -EINVAL; + } + + uccf = kzalloc(sizeof(struct ucc_fast_private), GFP_KERNEL); + if (!uccf) { + pr_err("%s: Cannot allocate private data\n", + __func__); + return -ENOMEM; + } + + /* Fill fast UCC structure */ + uccf->uf_info = uf_info; + /* Set the PHY base address */ + uccf->uf_regs = ioremap(uf_info->regs, sizeof(struct ucc_fast)); + if (uccf->uf_regs == NULL) { + pr_err("%s: Cannot map UCC registers\n", __func__); + kfree(uccf); + return -ENOMEM; + } + + uccf->enabled_tx = 0; + uccf->enabled_rx = 0; + uccf->stopped_tx = 0; + uccf->stopped_rx = 0; + uf_regs = uccf->uf_regs; + uccf->p_ucce = &uf_regs->ucce; + uccf->p_uccm = &uf_regs->uccm; +#ifdef CONFIG_UGETH_TX_ON_DEMAND + uccf->p_utodr = &uf_regs->utodr; +#endif +#ifdef STATISTICS + uccf->tx_frames = 0; + uccf->rx_frames = 0; + uccf->rx_discarded = 0; +#endif /* STATISTICS */ + + /* Set UCC to fast type */ + ret = ucc_set_type(uf_info->ucc_num, UCC_SPEED_TYPE_FAST); + if (ret) { + pr_err("%s: cannot set UCC type\n", __func__); + ucc_fast_free(uccf); + return ret; + } + + uccf->mrblr = uf_info->max_rx_buf_length; + + /* Set GUMR */ + /* For more details see the hardware spec. */ + gumr = uf_info->ttx_trx; + if (uf_info->tci) + gumr |= UCC_FAST_GUMR_TCI; + if (uf_info->cdp) + gumr |= UCC_FAST_GUMR_CDP; + if (uf_info->ctsp) + gumr |= UCC_FAST_GUMR_CTSP; + if (uf_info->cds) + gumr |= UCC_FAST_GUMR_CDS; + if (uf_info->ctss) + gumr |= UCC_FAST_GUMR_CTSS; + if (uf_info->txsy) + gumr |= UCC_FAST_GUMR_TXSY; + if (uf_info->rsyn) + gumr |= UCC_FAST_GUMR_RSYN; + gumr |= uf_info->synl; + if (uf_info->rtsm) + gumr |= UCC_FAST_GUMR_RTSM; + gumr |= uf_info->renc; + if (uf_info->revd) + gumr |= UCC_FAST_GUMR_REVD; + gumr |= uf_info->tenc; + gumr |= uf_info->tcrc; + gumr |= uf_info->mode; + out_be32(&uf_regs->gumr, gumr); + + /* Allocate memory for Tx Virtual Fifo */ + uccf->ucc_fast_tx_virtual_fifo_base_offset = + qe_muram_alloc(uf_info->utfs, UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + if (IS_ERR_VALUE(uccf->ucc_fast_tx_virtual_fifo_base_offset)) { + pr_err("%s: cannot allocate MURAM for TX FIFO\n", + __func__); + uccf->ucc_fast_tx_virtual_fifo_base_offset = 0; + ucc_fast_free(uccf); + return -ENOMEM; + } + + /* Allocate memory for Rx Virtual Fifo */ + uccf->ucc_fast_rx_virtual_fifo_base_offset = + qe_muram_alloc(uf_info->urfs + + UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + if (IS_ERR_VALUE(uccf->ucc_fast_rx_virtual_fifo_base_offset)) { + pr_err("%s: cannot allocate MURAM for RX FIFO\n", + __func__); + uccf->ucc_fast_rx_virtual_fifo_base_offset = 0; + ucc_fast_free(uccf); + return -ENOMEM; + } + + /* Set Virtual Fifo registers */ + out_be16(&uf_regs->urfs, uf_info->urfs); + out_be16(&uf_regs->urfet, uf_info->urfet); + out_be16(&uf_regs->urfset, uf_info->urfset); + out_be16(&uf_regs->utfs, uf_info->utfs); + out_be16(&uf_regs->utfet, uf_info->utfet); + out_be16(&uf_regs->utftt, uf_info->utftt); + /* utfb, urfb are offsets from MURAM base */ + out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset); + out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset); + + /* Mux clocking */ + /* Grant Support */ + ucc_set_qe_mux_grant(uf_info->ucc_num, uf_info->grant_support); + /* Breakpoint Support */ + ucc_set_qe_mux_bkpt(uf_info->ucc_num, uf_info->brkpt_support); + /* Set Tsa or NMSI mode. */ + ucc_set_qe_mux_tsa(uf_info->ucc_num, uf_info->tsa); + /* If NMSI (not Tsa), set Tx and Rx clock. */ + if (!uf_info->tsa) { + /* Rx clock routing */ + if ((uf_info->rx_clock != QE_CLK_NONE) && + ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->rx_clock, + COMM_DIR_RX)) { + pr_err("%s: illegal value for RX clock\n", + __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + /* Tx clock routing */ + if ((uf_info->tx_clock != QE_CLK_NONE) && + ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->tx_clock, + COMM_DIR_TX)) { + pr_err("%s: illegal value for TX clock\n", + __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + } else { + /* tdm Rx clock routing */ + if ((uf_info->rx_clock != QE_CLK_NONE) && + ucc_set_tdm_rxtx_clk(uf_info->tdm_num, + uf_info->rx_clock, COMM_DIR_RX)) { + pr_err("%s: illegal value for RX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + + /* tdm Tx clock routing */ + if ((uf_info->tx_clock != QE_CLK_NONE) && + ucc_set_tdm_rxtx_clk(uf_info->tdm_num, + uf_info->tx_clock, COMM_DIR_TX)) { + pr_err("%s:illegal value for TX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + + /* tdm Rx sync clock routing */ + if ((uf_info->rx_sync != QE_CLK_NONE) && + ucc_set_tdm_rxtx_sync(uf_info->tdm_num, + uf_info->rx_sync, COMM_DIR_RX)) { + pr_err("%s:illegal value for TX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + + /* tdm Tx sync clock routing */ + if ((uf_info->tx_sync != QE_CLK_NONE) && + ucc_set_tdm_rxtx_sync(uf_info->tdm_num, + uf_info->tx_sync, COMM_DIR_TX)) { + pr_err("%s:illegal value for TX clock", __func__); + ucc_fast_free(uccf); + return -EINVAL; + } + } + + /* Set interrupt mask register at UCC level. */ + out_be32(&uf_regs->uccm, uf_info->uccm_mask); + + /* First, clear anything pending at UCC level, + * otherwise, old garbage may come through + * as soon as the dam is opened. */ + + /* Writing '1' clears */ + out_be32(&uf_regs->ucce, 0xffffffff); + + *uccf_ret = uccf; + return 0; +} +EXPORT_SYMBOL(ucc_fast_init); + +void ucc_fast_free(struct ucc_fast_private *uccf) +{ + if (!uccf) + return; + + if (uccf->ucc_fast_tx_virtual_fifo_base_offset) + qe_muram_free(uccf->ucc_fast_tx_virtual_fifo_base_offset); + + if (uccf->ucc_fast_rx_virtual_fifo_base_offset) + qe_muram_free(uccf->ucc_fast_rx_virtual_fifo_base_offset); + + if (uccf->uf_regs) + iounmap(uccf->uf_regs); + + kfree(uccf); +} +EXPORT_SYMBOL(ucc_fast_free); diff --git a/drivers/soc/qe/ucc_slow.c b/drivers/soc/qe/ucc_slow.c new file mode 100644 index 0000000..d023f19 --- /dev/null +++ b/drivers/soc/qe/ucc_slow.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * Description: + * QE UCC Slow API Set - UCC Slow specific routines implementations. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +u32 ucc_slow_get_qe_cr_subblock(int uccs_num) +{ + switch (uccs_num) { + case 0: return QE_CR_SUBBLOCK_UCCSLOW1; + case 1: return QE_CR_SUBBLOCK_UCCSLOW2; + case 2: return QE_CR_SUBBLOCK_UCCSLOW3; + case 3: return QE_CR_SUBBLOCK_UCCSLOW4; + case 4: return QE_CR_SUBBLOCK_UCCSLOW5; + case 5: return QE_CR_SUBBLOCK_UCCSLOW6; + case 6: return QE_CR_SUBBLOCK_UCCSLOW7; + case 7: return QE_CR_SUBBLOCK_UCCSLOW8; + default: return QE_CR_SUBBLOCK_INVALID; + } +} +EXPORT_SYMBOL(ucc_slow_get_qe_cr_subblock); + +void ucc_slow_poll_transmitter_now(struct ucc_slow_private *uccs) +{ + out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD); +} + +void ucc_slow_graceful_stop_tx(struct ucc_slow_private *uccs) +{ + struct ucc_slow_info *us_info = uccs->us_info; + u32 id; + + id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); + qe_issue_cmd(QE_GRACEFUL_STOP_TX, id, + QE_CR_PROTOCOL_UNSPECIFIED, 0); +} +EXPORT_SYMBOL(ucc_slow_graceful_stop_tx); + +void ucc_slow_stop_tx(struct ucc_slow_private *uccs) +{ + struct ucc_slow_info *us_info = uccs->us_info; + u32 id; + + id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); + qe_issue_cmd(QE_STOP_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); +} +EXPORT_SYMBOL(ucc_slow_stop_tx); + +void ucc_slow_restart_tx(struct ucc_slow_private *uccs) +{ + struct ucc_slow_info *us_info = uccs->us_info; + u32 id; + + id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); + qe_issue_cmd(QE_RESTART_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); +} +EXPORT_SYMBOL(ucc_slow_restart_tx); + +void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode) +{ + struct ucc_slow *us_regs; + u32 gumr_l; + + us_regs = uccs->us_regs; + + /* Enable reception and/or transmission on this UCC. */ + gumr_l = in_be32(&us_regs->gumr_l); + if (mode & COMM_DIR_TX) { + gumr_l |= UCC_SLOW_GUMR_L_ENT; + uccs->enabled_tx = 1; + } + if (mode & COMM_DIR_RX) { + gumr_l |= UCC_SLOW_GUMR_L_ENR; + uccs->enabled_rx = 1; + } + out_be32(&us_regs->gumr_l, gumr_l); +} +EXPORT_SYMBOL(ucc_slow_enable); + +void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode) +{ + struct ucc_slow *us_regs; + u32 gumr_l; + + us_regs = uccs->us_regs; + + /* Disable reception and/or transmission on this UCC. */ + gumr_l = in_be32(&us_regs->gumr_l); + if (mode & COMM_DIR_TX) { + gumr_l &= ~UCC_SLOW_GUMR_L_ENT; + uccs->enabled_tx = 0; + } + if (mode & COMM_DIR_RX) { + gumr_l &= ~UCC_SLOW_GUMR_L_ENR; + uccs->enabled_rx = 0; + } + out_be32(&us_regs->gumr_l, gumr_l); +} +EXPORT_SYMBOL(ucc_slow_disable); + +/* Initialize the UCC for Slow operations + * + * The caller should initialize the following us_info + */ +int ucc_slow_init(struct ucc_slow_info *us_info, + struct ucc_slow_private **uccs_ret) +{ + struct ucc_slow_private *uccs; + u32 i; + struct ucc_slow __iomem *us_regs; + u32 gumr; + struct qe_bd *bd; + u32 id; + u32 command; + int ret = 0; + + if (!us_info) + return -EINVAL; + + /* check if the UCC port number is in range. */ + if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM - 1)) { + pr_err("%s: illegal UCC number\n", __func__); + return -EINVAL; + } + + /* + * Set mrblr + * Check that 'max_rx_buf_length' is properly aligned (4), unless + * rfw is 1, meaning that QE accepts one byte at a time, unlike normal + * case when QE accepts 32 bits at a time. + */ + if ((!us_info->rfw) && + (us_info->max_rx_buf_length & (UCC_SLOW_MRBLR_ALIGNMENT - 1))) { + pr_err("max_rx_buf_length not aligned.\n"); + return -EINVAL; + } + + uccs = kzalloc(sizeof(struct ucc_slow_private), GFP_KERNEL); + if (!uccs) { + pr_err("%s: Cannot allocate private data\n", + __func__); + return -ENOMEM; + } + + /* Fill slow UCC structure */ + uccs->us_info = us_info; + /* Set the PHY base address */ + uccs->us_regs = ioremap(us_info->regs, sizeof(struct ucc_slow)); + if (uccs->us_regs == NULL) { + pr_err("%s: Cannot map UCC registers\n", __func__); + kfree(uccs); + return -ENOMEM; + } + + uccs->saved_uccm = 0; + uccs->p_rx_frame = 0; + us_regs = uccs->us_regs; + uccs->p_ucce = (u16 *)&(us_regs->ucce); + uccs->p_uccm = (u16 *)&(us_regs->uccm); +#ifdef STATISTICS + uccs->rx_frames = 0; + uccs->tx_frames = 0; + uccs->rx_discarded = 0; +#endif /* STATISTICS */ + + /* Get PRAM base */ + uccs->us_pram_offset = + qe_muram_alloc(UCC_SLOW_PRAM_SIZE, ALIGNMENT_OF_UCC_SLOW_PRAM); + if (IS_ERR_VALUE(uccs->us_pram_offset)) { + pr_err("%s: cannot allocate MURAM for PRAM", __func__); + ucc_slow_free(uccs); + return -ENOMEM; + } + id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, id, us_info->protocol, + uccs->us_pram_offset); + + uccs->us_pram = qe_muram_addr(uccs->us_pram_offset); + + /* Set UCC to slow type */ + ret = ucc_set_type(us_info->ucc_num, UCC_SPEED_TYPE_SLOW); + if (ret) { + pr_err("%s: cannot set UCC type", __func__); + ucc_slow_free(uccs); + return ret; + } + + out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length); + + INIT_LIST_HEAD(&uccs->confQ); + + /* Allocate BDs. */ + uccs->rx_base_offset = + qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd), + QE_ALIGNMENT_OF_BD); + if (IS_ERR_VALUE(uccs->rx_base_offset)) { + pr_err("%s: cannot allocate %u RX BDs\n", __func__, + us_info->rx_bd_ring_len); + uccs->rx_base_offset = 0; + ucc_slow_free(uccs); + return -ENOMEM; + } + + uccs->tx_base_offset = + qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd), + QE_ALIGNMENT_OF_BD); + if (IS_ERR_VALUE(uccs->tx_base_offset)) { + pr_err("%s: cannot allocate TX BDs", __func__); + uccs->tx_base_offset = 0; + ucc_slow_free(uccs); + return -ENOMEM; + } + + /* Init Tx bds */ + bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs->tx_base_offset); + for (i = 0; i < us_info->tx_bd_ring_len - 1; i++) { + /* clear bd buffer */ + out_be32(&bd->buf, 0); + /* set bd status and length */ + out_be32((u32 *) bd, 0); + bd++; + } + /* for last BD set Wrap bit */ + out_be32(&bd->buf, 0); + out_be32((u32 *) bd, cpu_to_be32(T_W)); + + /* Init Rx bds */ + bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset); + for (i = 0; i < us_info->rx_bd_ring_len - 1; i++) { + /* set bd status and length */ + out_be32((u32 *)bd, 0); + /* clear bd buffer */ + out_be32(&bd->buf, 0); + bd++; + } + /* for last BD set Wrap bit */ + out_be32((u32 *)bd, cpu_to_be32(R_W)); + out_be32(&bd->buf, 0); + + /* Set GUMR (For more details see the hardware spec.). */ + /* gumr_h */ + gumr = us_info->tcrc; + if (us_info->cdp) + gumr |= UCC_SLOW_GUMR_H_CDP; + if (us_info->ctsp) + gumr |= UCC_SLOW_GUMR_H_CTSP; + if (us_info->cds) + gumr |= UCC_SLOW_GUMR_H_CDS; + if (us_info->ctss) + gumr |= UCC_SLOW_GUMR_H_CTSS; + if (us_info->tfl) + gumr |= UCC_SLOW_GUMR_H_TFL; + if (us_info->rfw) + gumr |= UCC_SLOW_GUMR_H_RFW; + if (us_info->txsy) + gumr |= UCC_SLOW_GUMR_H_TXSY; + if (us_info->rtsm) + gumr |= UCC_SLOW_GUMR_H_RTSM; + out_be32(&us_regs->gumr_h, gumr); + + /* gumr_l */ + gumr = us_info->tdcr | us_info->rdcr | us_info->tenc | us_info->renc | + us_info->diag | us_info->mode; + if (us_info->tci) + gumr |= UCC_SLOW_GUMR_L_TCI; + if (us_info->rinv) + gumr |= UCC_SLOW_GUMR_L_RINV; + if (us_info->tinv) + gumr |= UCC_SLOW_GUMR_L_TINV; + if (us_info->tend) + gumr |= UCC_SLOW_GUMR_L_TEND; + out_be32(&us_regs->gumr_l, gumr); + + /* Function code registers */ + + /* if the data is in cachable memory, the 'global' */ + /* in the function code should be set. */ + uccs->us_pram->tbmr = UCC_BMR_BO_BE; + uccs->us_pram->rbmr = UCC_BMR_BO_BE; + + /* rbase, tbase are offsets from MURAM base */ + out_be16(&uccs->us_pram->rbase, uccs->rx_base_offset); + out_be16(&uccs->us_pram->tbase, uccs->tx_base_offset); + + /* Mux clocking */ + /* Grant Support */ + ucc_set_qe_mux_grant(us_info->ucc_num, us_info->grant_support); + /* Breakpoint Support */ + ucc_set_qe_mux_bkpt(us_info->ucc_num, us_info->brkpt_support); + /* Set Tsa or NMSI mode. */ + ucc_set_qe_mux_tsa(us_info->ucc_num, us_info->tsa); + /* If NMSI (not Tsa), set Tx and Rx clock. */ + if (!us_info->tsa) { + /* Rx clock routing */ + if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->rx_clock, + COMM_DIR_RX)) { + pr_err("%s: illegal value for RX clock\n", + __func__); + ucc_slow_free(uccs); + return -EINVAL; + } + /* Tx clock routing */ + if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->tx_clock, + COMM_DIR_TX)) { + pr_err("%s: illegal value for TX clock\n", + __func__); + ucc_slow_free(uccs); + return -EINVAL; + } + } + + /* Set interrupt mask register at UCC level. */ + out_be16(&us_regs->uccm, us_info->uccm_mask); + + /* First, clear anything pending at UCC level, + * otherwise, old garbage may come through + * as soon as the dam is opened. */ + + /* Writing '1' clears */ + out_be16(&us_regs->ucce, 0xffff); + + /* Issue QE Init command */ + if (us_info->init_tx && us_info->init_rx) + command = QE_INIT_TX_RX; + else if (us_info->init_tx) + command = QE_INIT_TX; + else + command = QE_INIT_RX; /* We know at least one is TRUE */ + + qe_issue_cmd(command, id, us_info->protocol, 0); + + *uccs_ret = uccs; + return 0; +} +EXPORT_SYMBOL(ucc_slow_init); + +void ucc_slow_free(struct ucc_slow_private *uccs) +{ + if (!uccs) + return; + + if (uccs->rx_base_offset) + qe_muram_free(uccs->rx_base_offset); + + if (uccs->tx_base_offset) + qe_muram_free(uccs->tx_base_offset); + + if (uccs->us_pram) + qe_muram_free(uccs->us_pram_offset); + + if (uccs->us_regs) + iounmap(uccs->us_regs); + + kfree(uccs); +} +EXPORT_SYMBOL(ucc_slow_free); diff --git a/drivers/soc/qe/usb.c b/drivers/soc/qe/usb.c new file mode 100644 index 0000000..58f812a --- /dev/null +++ b/drivers/soc/qe/usb.c @@ -0,0 +1,76 @@ +/* + * QE USB routines + * + * Copyright 2006 Freescale Semiconductor, Inc. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include + +int qe_usb_clock_set(enum qe_clock clk, int rate) +{ + struct qe_mux __iomem *mux = &qe_immr->qmx; + unsigned long flags; + u32 val; + + switch (clk) { + case QE_CLK3: + val = QE_CMXGCR_USBCS_CLK3; + break; + case QE_CLK5: + val = QE_CMXGCR_USBCS_CLK5; + break; + case QE_CLK7: + val = QE_CMXGCR_USBCS_CLK7; + break; + case QE_CLK9: + val = QE_CMXGCR_USBCS_CLK9; + break; + case QE_CLK13: + val = QE_CMXGCR_USBCS_CLK13; + break; + case QE_CLK17: + val = QE_CMXGCR_USBCS_CLK17; + break; + case QE_CLK19: + val = QE_CMXGCR_USBCS_CLK19; + break; + case QE_CLK21: + val = QE_CMXGCR_USBCS_CLK21; + break; + case QE_BRG9: + val = QE_CMXGCR_USBCS_BRG9; + break; + case QE_BRG10: + val = QE_CMXGCR_USBCS_BRG10; + break; + default: + pr_err("%s: requested unknown clock %d\n", __func__, clk); + return -EINVAL; + } + + if (qe_clock_is_brg(clk)) + qe_setbrg(clk, rate, 1); + + spin_lock_irqsave(&cmxgcr_lock, flags); + + clrsetbits_be32(&mux->cmxgcr, QE_CMXGCR_USBCS, val); + + spin_unlock_irqrestore(&cmxgcr_lock, flags); + + return 0; +} +EXPORT_SYMBOL(qe_usb_clock_set); diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index 07971e3..e856d85 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "spi-fsl-lib.h" #include "spi-fsl-cpm.h" diff --git a/drivers/tdm/device/fsl_ucc_tdm.h b/drivers/tdm/device/fsl_ucc_tdm.h index 1a1b161..a1213fa 100644 --- a/drivers/tdm/device/fsl_ucc_tdm.h +++ b/drivers/tdm/device/fsl_ucc_tdm.h @@ -31,11 +31,11 @@ #include #include -#include -#include +#include +#include -#include -#include +#include +#include /* SI RAM entries */ #define SIR_LAST 0x0001 diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 8831748..4718ebe 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index f3bb363..d41d8e2 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 0b4654259..fe5fa06 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include "fhci.h" diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c index 6af2512..31b4402 100644 --- a/drivers/usb/host/fhci-hub.c +++ b/drivers/usb/host/fhci-hub.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "fhci.h" /* virtual root hub specific descriptor */ diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 95ca598..6f1d4ad 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include "fhci.h" diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index 154e6a0..d7c49531 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -27,8 +27,8 @@ #include #include #include -#include -#include +#include +#include #define USB_CLOCK 48000000 diff --git a/include/linux/fsl/immap_qe.h b/include/linux/fsl/immap_qe.h new file mode 100644 index 0000000..bd1ebd4 --- /dev/null +++ b/include/linux/fsl/immap_qe.h @@ -0,0 +1,488 @@ +/* + * QUICC Engine (QE) Internal Memory Map. + * The Internal Memory Map for devices with QE on them. This + * is the superset of all QE devices (8360, etc.). + * Copyright (C) 2006. Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: + * Shlomi Gridish + * Li Yang + * + * 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. + */ +#ifndef _ASM_POWERPC_IMMAP_QE_H +#define _ASM_POWERPC_IMMAP_QE_H +#ifdef __KERNEL__ + +#include +#include + +#define QE_IMMAP_SIZE (1024 * 1024) /* 1MB from 1MB+IMMR */ + +/* QE I-RAM */ +struct qe_iram { + __be32 iadd; /* I-RAM Address Register */ + __be32 idata; /* I-RAM Data Register */ + u8 res0[0x04]; + __be32 iready; /* I-RAM Ready Register */ + u8 res1[0x70]; +} __packed; + +/* QE Interrupt Controller */ +struct qe_ic_regs { + __be32 qicr; + __be32 qivec; + __be32 qripnr; + __be32 qipnr; + __be32 qipxcc; + __be32 qipycc; + __be32 qipwcc; + __be32 qipzcc; + __be32 qimr; + __be32 qrimr; + __be32 qicnr; + u8 res0[0x4]; + __be32 qiprta; + __be32 qiprtb; + u8 res1[0x4]; + __be32 qricr; + u8 res2[0x20]; + __be32 qhivec; + u8 res3[0x1C]; +} __packed; + +/* Communications Processor */ +struct cp_qe { + __be32 cecr; /* QE command register */ + __be32 ceccr; /* QE controller configuration register */ + __be32 cecdr; /* QE command data register */ + u8 res0[0xA]; + __be16 ceter; /* QE timer event register */ + u8 res1[0x2]; + __be16 cetmr; /* QE timers mask register */ + __be32 cetscr; /* QE time-stamp timer control register */ + __be32 cetsr1; /* QE time-stamp register 1 */ + __be32 cetsr2; /* QE time-stamp register 2 */ + u8 res2[0x8]; + __be32 cevter; /* QE virtual tasks event register */ + __be32 cevtmr; /* QE virtual tasks mask register */ + __be16 cercr; /* QE RAM control register */ + u8 res3[0x2]; + u8 res4[0x24]; + __be16 ceexe1; /* QE external request 1 event register */ + u8 res5[0x2]; + __be16 ceexm1; /* QE external request 1 mask register */ + u8 res6[0x2]; + __be16 ceexe2; /* QE external request 2 event register */ + u8 res7[0x2]; + __be16 ceexm2; /* QE external request 2 mask register */ + u8 res8[0x2]; + __be16 ceexe3; /* QE external request 3 event register */ + u8 res9[0x2]; + __be16 ceexm3; /* QE external request 3 mask register */ + u8 res10[0x2]; + __be16 ceexe4; /* QE external request 4 event register */ + u8 res11[0x2]; + __be16 ceexm4; /* QE external request 4 mask register */ + u8 res12[0x3A]; + __be32 ceurnr; /* QE microcode revision number register */ + u8 res13[0x244]; +} __packed; + +/* QE Multiplexer */ +struct qe_mux { + __be32 cmxgcr; /* CMX general clock route register */ + __be32 cmxsi1cr_l; /* CMX SI1 clock route low register */ + __be32 cmxsi1cr_h; /* CMX SI1 clock route high register */ + __be32 cmxsi1syr; /* CMX SI1 SYNC route register */ + __be32 cmxucr[4]; /* CMX UCCx clock route registers */ + __be32 cmxupcr; /* CMX UPC clock route register */ + u8 res0[0x1C]; +} __packed; + +/* QE Timers */ +struct qe_timers { + u8 gtcfr1; /* Timer 1 and Timer 2 global config register*/ + u8 res0[0x3]; + u8 gtcfr2; /* Timer 3 and timer 4 global config register*/ + u8 res1[0xB]; + __be16 gtmdr1; /* Timer 1 mode register */ + __be16 gtmdr2; /* Timer 2 mode register */ + __be16 gtrfr1; /* Timer 1 reference register */ + __be16 gtrfr2; /* Timer 2 reference register */ + __be16 gtcpr1; /* Timer 1 capture register */ + __be16 gtcpr2; /* Timer 2 capture register */ + __be16 gtcnr1; /* Timer 1 counter */ + __be16 gtcnr2; /* Timer 2 counter */ + __be16 gtmdr3; /* Timer 3 mode register */ + __be16 gtmdr4; /* Timer 4 mode register */ + __be16 gtrfr3; /* Timer 3 reference register */ + __be16 gtrfr4; /* Timer 4 reference register */ + __be16 gtcpr3; /* Timer 3 capture register */ + __be16 gtcpr4; /* Timer 4 capture register */ + __be16 gtcnr3; /* Timer 3 counter */ + __be16 gtcnr4; /* Timer 4 counter */ + __be16 gtevr1; /* Timer 1 event register */ + __be16 gtevr2; /* Timer 2 event register */ + __be16 gtevr3; /* Timer 3 event register */ + __be16 gtevr4; /* Timer 4 event register */ + __be16 gtps; /* Timer 1 prescale register */ + u8 res2[0x46]; +} __packed; + +/* BRG */ +struct qe_brg { + __be32 brgc[16]; /* BRG configuration registers */ + u8 res0[0x40]; +} __packed; + +/* SPI */ +struct spi { + u8 res0[0x20]; + __be32 spmode; /* SPI mode register */ + u8 res1[0x2]; + u8 spie; /* SPI event register */ + u8 res2[0x1]; + u8 res3[0x2]; + u8 spim; /* SPI mask register */ + u8 res4[0x1]; + u8 res5[0x1]; + u8 spcom; /* SPI command register */ + u8 res6[0x2]; + __be32 spitd; /* SPI transmit data register (cpu mode) */ + __be32 spird; /* SPI receive data register (cpu mode) */ + u8 res7[0x8]; +} __packed; + +/* SI */ +struct si1 { + __be16 sixmr1[4]; /* SI1 TDMx (x = A B C D) mode register */ + u8 siglmr1_h; /* SI1 global mode register high */ + u8 res0[0x1]; + u8 sicmdr1_h; /* SI1 command register high */ + u8 res2[0x1]; + u8 sistr1_h; /* SI1 status register high */ + u8 res3[0x1]; + __be16 sirsr1_h; /* SI1 RAM shadow address register high */ + u8 sitarc1; /* SI1 RAM counter Tx TDMA */ + u8 sitbrc1; /* SI1 RAM counter Tx TDMB */ + u8 sitcrc1; /* SI1 RAM counter Tx TDMC */ + u8 sitdrc1; /* SI1 RAM counter Tx TDMD */ + u8 sirarc1; /* SI1 RAM counter Rx TDMA */ + u8 sirbrc1; /* SI1 RAM counter Rx TDMB */ + u8 sircrc1; /* SI1 RAM counter Rx TDMC */ + u8 sirdrc1; /* SI1 RAM counter Rx TDMD */ + u8 res4[0x8]; + __be16 siemr1; /* SI1 TDME mode register 16 bits */ + __be16 sifmr1; /* SI1 TDMF mode register 16 bits */ + __be16 sigmr1; /* SI1 TDMG mode register 16 bits */ + __be16 sihmr1; /* SI1 TDMH mode register 16 bits */ + u8 siglmg1_l; /* SI1 global mode register low 8 bits */ + u8 res5[0x1]; + u8 sicmdr1_l; /* SI1 command register low 8 bits */ + u8 res6[0x1]; + u8 sistr1_l; /* SI1 status register low 8 bits */ + u8 res7[0x1]; + __be16 sirsr1_l; /* SI1 RAM shadow address register low 16 bits*/ + u8 siterc1; /* SI1 RAM counter Tx TDME 8 bits */ + u8 sitfrc1; /* SI1 RAM counter Tx TDMF 8 bits */ + u8 sitgrc1; /* SI1 RAM counter Tx TDMG 8 bits */ + u8 sithrc1; /* SI1 RAM counter Tx TDMH 8 bits */ + u8 sirerc1; /* SI1 RAM counter Rx TDME 8 bits */ + u8 sirfrc1; /* SI1 RAM counter Rx TDMF 8 bits */ + u8 sirgrc1; /* SI1 RAM counter Rx TDMG 8 bits */ + u8 sirhrc1; /* SI1 RAM counter Rx TDMH 8 bits */ + u8 res8[0x8]; + __be32 siml1; /* SI1 multiframe limit register */ + u8 siedm1; /* SI1 extended diagnostic mode register */ + u8 res9[0xBB]; +} __packed; + +/* SI Routing Tables */ +struct sir { + u8 tx[0x400]; + u8 rx[0x400]; + u8 res0[0x800]; +} __packed; + +/* USB Controller */ +struct qe_usb_ctlr { + u8 usb_usmod; + u8 usb_usadr; + u8 usb_uscom; + u8 res1[1]; + __be16 usb_usep[4]; + u8 res2[4]; + __be16 usb_usber; + u8 res3[2]; + __be16 usb_usbmr; + u8 res4[1]; + u8 usb_usbs; + __be16 usb_ussft; + u8 res5[2]; + __be16 usb_usfrn; + u8 res6[0x22]; +} __packed; + +/* MCC */ +struct qe_mcc { + __be32 mcce; /* MCC event register */ + __be32 mccm; /* MCC mask register */ + __be32 mccf; /* MCC configuration register */ + __be32 merl; /* MCC emergency request level register */ + u8 res0[0xF0]; +} __packed; + +/* QE UCC Slow */ +struct ucc_slow { + __be32 gumr_l; /* UCCx general mode register (low) */ + __be32 gumr_h; /* UCCx general mode register (high) */ + __be16 upsmr; /* UCCx protocol-specific mode register */ + u8 res0[0x2]; + __be16 utodr; /* UCCx transmit on demand register */ + __be16 udsr; /* UCCx data synchronization register */ + __be16 ucce; /* UCCx event register */ + u8 res1[0x2]; + __be16 uccm; /* UCCx mask register */ + u8 res2[0x1]; + u8 uccs; /* UCCx status register */ + u8 res3[0x24]; + __be16 utpt; + u8 res4[0x52]; + u8 guemr; /* UCC general extended mode register */ +} __packed; + +/* QE UCC Fast */ +struct ucc_fast { + __be32 gumr; /* UCCx general mode register */ + __be32 upsmr; /* UCCx protocol-specific mode register */ + __be16 utodr; /* UCCx transmit on demand register */ + u8 res0[0x2]; + __be16 udsr; /* UCCx data synchronization register */ + u8 res1[0x2]; + __be32 ucce; /* UCCx event register */ + __be32 uccm; /* UCCx mask register */ + u8 uccs; /* UCCx status register */ + u8 res2[0x7]; + __be32 urfb; /* UCC receive FIFO base */ + __be16 urfs; /* UCC receive FIFO size */ + u8 res3[0x2]; + __be16 urfet; /* UCC receive FIFO emergency threshold */ + __be16 urfset; /* UCC receive FIFO special emergency + threshold */ + __be32 utfb; /* UCC transmit FIFO base */ + __be16 utfs; /* UCC transmit FIFO size */ + u8 res4[0x2]; + __be16 utfet; /* UCC transmit FIFO emergency threshold */ + u8 res5[0x2]; + __be16 utftt; /* UCC transmit FIFO transmit threshold */ + u8 res6[0x2]; + __be16 utpt; /* UCC transmit polling timer */ + u8 res7[0x2]; + __be32 urtry; /* UCC retry counter register */ + u8 res8[0x4C]; + u8 guemr; /* UCC general extended mode register */ +} __packed; + +struct ucc { + union { + struct ucc_slow slow; + struct ucc_fast fast; + u8 res[0x200]; /* UCC blocks are 512 bytes each */ + }; +} __packed; + +/* MultiPHY UTOPIA POS Controllers (UPC) */ +struct upc { + __be32 upgcr; /* UTOPIA/POS general configuration register */ + __be32 uplpa; /* UTOPIA/POS last PHY address */ + __be32 uphec; /* ATM HEC register */ + __be32 upuc; /* UTOPIA/POS UCC configuration */ + __be32 updc1; /* UTOPIA/POS device 1 configuration */ + __be32 updc2; /* UTOPIA/POS device 2 configuration */ + __be32 updc3; /* UTOPIA/POS device 3 configuration */ + __be32 updc4; /* UTOPIA/POS device 4 configuration */ + __be32 upstpa; /* UTOPIA/POS STPA threshold */ + u8 res0[0xC]; + __be32 updrs1_h; /* UTOPIA/POS device 1 rate select */ + __be32 updrs1_l; /* UTOPIA/POS device 1 rate select */ + __be32 updrs2_h; /* UTOPIA/POS device 2 rate select */ + __be32 updrs2_l; /* UTOPIA/POS device 2 rate select */ + __be32 updrs3_h; /* UTOPIA/POS device 3 rate select */ + __be32 updrs3_l; /* UTOPIA/POS device 3 rate select */ + __be32 updrs4_h; /* UTOPIA/POS device 4 rate select */ + __be32 updrs4_l; /* UTOPIA/POS device 4 rate select */ + __be32 updrp1; /* UTOPIA/POS device 1 receive priority low */ + __be32 updrp2; /* UTOPIA/POS device 2 receive priority low */ + __be32 updrp3; /* UTOPIA/POS device 3 receive priority low */ + __be32 updrp4; /* UTOPIA/POS device 4 receive priority low */ + __be32 upde1; /* UTOPIA/POS device 1 event */ + __be32 upde2; /* UTOPIA/POS device 2 event */ + __be32 upde3; /* UTOPIA/POS device 3 event */ + __be32 upde4; /* UTOPIA/POS device 4 event */ + __be16 uprp1; + __be16 uprp2; + __be16 uprp3; + __be16 uprp4; + u8 res1[0x8]; + __be16 uptirr1_0; /* Device 1 transmit internal rate 0 */ + __be16 uptirr1_1; /* Device 1 transmit internal rate 1 */ + __be16 uptirr1_2; /* Device 1 transmit internal rate 2 */ + __be16 uptirr1_3; /* Device 1 transmit internal rate 3 */ + __be16 uptirr2_0; /* Device 2 transmit internal rate 0 */ + __be16 uptirr2_1; /* Device 2 transmit internal rate 1 */ + __be16 uptirr2_2; /* Device 2 transmit internal rate 2 */ + __be16 uptirr2_3; /* Device 2 transmit internal rate 3 */ + __be16 uptirr3_0; /* Device 3 transmit internal rate 0 */ + __be16 uptirr3_1; /* Device 3 transmit internal rate 1 */ + __be16 uptirr3_2; /* Device 3 transmit internal rate 2 */ + __be16 uptirr3_3; /* Device 3 transmit internal rate 3 */ + __be16 uptirr4_0; /* Device 4 transmit internal rate 0 */ + __be16 uptirr4_1; /* Device 4 transmit internal rate 1 */ + __be16 uptirr4_2; /* Device 4 transmit internal rate 2 */ + __be16 uptirr4_3; /* Device 4 transmit internal rate 3 */ + __be32 uper1; /* Device 1 port enable register */ + __be32 uper2; /* Device 2 port enable register */ + __be32 uper3; /* Device 3 port enable register */ + __be32 uper4; /* Device 4 port enable register */ + u8 res2[0x150]; +} __packed; + +/* SDMA */ +struct sdma { + __be32 sdsr; /* Serial DMA status register */ + __be32 sdmr; /* Serial DMA mode register */ + __be32 sdtr1; /* SDMA system bus threshold register */ + __be32 sdtr2; /* SDMA secondary bus threshold register */ + __be32 sdhy1; /* SDMA system bus hysteresis register */ + __be32 sdhy2; /* SDMA secondary bus hysteresis register */ + __be32 sdta1; /* SDMA system bus address register */ + __be32 sdta2; /* SDMA secondary bus address register */ + __be32 sdtm1; /* SDMA system bus MSNUM register */ + __be32 sdtm2; /* SDMA secondary bus MSNUM register */ + u8 res0[0x10]; + __be32 sdaqr; /* SDMA address bus qualify register */ + __be32 sdaqmr; /* SDMA address bus qualify mask register */ + u8 res1[0x4]; + __be32 sdebcr; /* SDMA CAM entries base register */ + u8 res2[0x38]; +} __packed; + +/* Debug Space */ +struct dbg { + __be32 bpdcr; /* Breakpoint debug command register */ + __be32 bpdsr; /* Breakpoint debug status register */ + __be32 bpdmr; /* Breakpoint debug mask register */ + __be32 bprmrr0; /* Breakpoint request mode risc register 0 */ + __be32 bprmrr1; /* Breakpoint request mode risc register 1 */ + u8 res0[0x8]; + __be32 bprmtr0; /* Breakpoint request mode trb register 0 */ + __be32 bprmtr1; /* Breakpoint request mode trb register 1 */ + u8 res1[0x8]; + __be32 bprmir; /* Breakpoint request mode immediate register */ + __be32 bprmsr; /* Breakpoint request mode serial register */ + __be32 bpemr; /* Breakpoint exit mode register */ + u8 res2[0x48]; +} __packed; + +/* + * RISC Special Registers (Trap and Breakpoint). These are described in + * the QE Developer's Handbook. + */ +struct rsp { + __be32 tibcr[16]; /* Trap/instruction breakpoint control regs */ + u8 res0[64]; + __be32 ibcr0; + __be32 ibs0; + __be32 ibcnr0; + u8 res1[4]; + __be32 ibcr1; + __be32 ibs1; + __be32 ibcnr1; + __be32 npcr; + __be32 dbcr; + __be32 dbar; + __be32 dbamr; + __be32 dbsr; + __be32 dbcnr; + u8 res2[12]; + __be32 dbdr_h; + __be32 dbdr_l; + __be32 dbdmr_h; + __be32 dbdmr_l; + __be32 bsr; + __be32 bor; + __be32 bior; + u8 res3[4]; + __be32 iatr[4]; + __be32 eccr; /* Exception control configuration register */ + __be32 eicr; + u8 res4[0x100-0xf8]; +} __packed; + +struct qe_immap { + struct qe_iram iram; /* I-RAM */ + struct qe_ic_regs ic; /* Interrupt Controller */ + struct cp_qe cp; /* Communications Processor */ + struct qe_mux qmx; /* QE Multiplexer */ + struct qe_timers qet; /* QE Timers */ + struct spi spi[0x2]; /* spi */ + struct qe_mcc mcc; /* mcc */ + struct qe_brg brg; /* brg */ + struct qe_usb_ctlr usb; /* USB */ + struct si1 si1; /* SI */ + u8 res11[0x800]; + struct sir sir; /* SI Routing Tables */ + struct ucc ucc1; /* ucc1 */ + struct ucc ucc3; /* ucc3 */ + struct ucc ucc5; /* ucc5 */ + struct ucc ucc7; /* ucc7 */ + u8 res12[0x600]; + struct upc upc1; /* MultiPHY UTOPIA POS Ctrlr 1*/ + struct ucc ucc2; /* ucc2 */ + struct ucc ucc4; /* ucc4 */ + struct ucc ucc6; /* ucc6 */ + struct ucc ucc8; /* ucc8 */ + u8 res13[0x600]; + struct upc upc2; /* MultiPHY UTOPIA POS Ctrlr 2*/ + struct sdma sdma; /* SDMA */ + struct dbg dbg; /* 0x104080 - 0x1040FF + Debug Space */ + struct rsp rsp[0x2]; /* 0x104100 - 0x1042FF + RISC Special Registers + (Trap and Breakpoint) */ + u8 res14[0x300]; /* 0x104300 - 0x1045FF */ + u8 res15[0x3A00]; /* 0x104600 - 0x107FFF */ + u8 res16[0x8000]; /* 0x108000 - 0x110000 */ + u8 muram[0xC000]; /* 0x110000 - 0x11C000 + Multi-user RAM */ + u8 res17[0x24000]; /* 0x11C000 - 0x140000 */ + u8 res18[0xC0000]; /* 0x140000 - 0x200000 */ +} __packed; + +extern struct qe_immap __iomem *qe_immr; +extern phys_addr_t get_qe_base(void); + +/* + * Returns the offset within the QE address space of the given pointer. + * + * Note that the QE does not support 36-bit physical addresses, so if + * get_qe_base() returns a number above 4GB, the caller will probably fail. + */ +static inline phys_addr_t immrbar_virt_to_phys(void *address) +{ + void *q = (void *)qe_immr; + + /* Is it a MURAM address? */ + if ((address >= q) && (address < (q + QE_IMMAP_SIZE))) + return get_qe_base() + (address - q); + + /* It's an address returned by kmalloc */ + return virt_to_phys(address); +} + +#endif /* __KERNEL__ */ +#endif /* _ASM_POWERPC_IMMAP_QE_H */ diff --git a/include/linux/fsl/qe.h b/include/linux/fsl/qe.h new file mode 100644 index 0000000..c96ff9e --- /dev/null +++ b/include/linux/fsl/qe.h @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * Description: + * QUICC Engine (QE) external definitions and structure. + * + * 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. + */ +#ifndef _ASM_POWERPC_QE_H +#define _ASM_POWERPC_QE_H +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include + +#define QE_NUM_OF_SNUM 256 /* There are 256 serial number in QE */ +#define QE_NUM_OF_BRGS 16 +#define QE_NUM_OF_PORTS 1024 + +/* Memory partitions +*/ +#define MEM_PART_SYSTEM 0 +#define MEM_PART_SECONDARY 1 +#define MEM_PART_MURAM 2 + +extern int siram_init_flag; + +/* Clocks and BRGs */ +enum qe_clock { + QE_CLK_NONE = 0, + QE_BRG1, /* Baud Rate Generator 1 */ + QE_BRG2, /* Baud Rate Generator 2 */ + QE_BRG3, /* Baud Rate Generator 3 */ + QE_BRG4, /* Baud Rate Generator 4 */ + QE_BRG5, /* Baud Rate Generator 5 */ + QE_BRG6, /* Baud Rate Generator 6 */ + QE_BRG7, /* Baud Rate Generator 7 */ + QE_BRG8, /* Baud Rate Generator 8 */ + QE_BRG9, /* Baud Rate Generator 9 */ + QE_BRG10, /* Baud Rate Generator 10 */ + QE_BRG11, /* Baud Rate Generator 11 */ + QE_BRG12, /* Baud Rate Generator 12 */ + QE_BRG13, /* Baud Rate Generator 13 */ + QE_BRG14, /* Baud Rate Generator 14 */ + QE_BRG15, /* Baud Rate Generator 15 */ + QE_BRG16, /* Baud Rate Generator 16 */ + QE_CLK1, /* Clock 1 */ + QE_CLK2, /* Clock 2 */ + QE_CLK3, /* Clock 3 */ + QE_CLK4, /* Clock 4 */ + QE_CLK5, /* Clock 5 */ + QE_CLK6, /* Clock 6 */ + QE_CLK7, /* Clock 7 */ + QE_CLK8, /* Clock 8 */ + QE_CLK9, /* Clock 9 */ + QE_CLK10, /* Clock 10 */ + QE_CLK11, /* Clock 11 */ + QE_CLK12, /* Clock 12 */ + QE_CLK13, /* Clock 13 */ + QE_CLK14, /* Clock 14 */ + QE_CLK15, /* Clock 15 */ + QE_CLK16, /* Clock 16 */ + QE_CLK17, /* Clock 17 */ + QE_CLK18, /* Clock 18 */ + QE_CLK19, /* Clock 19 */ + QE_CLK20, /* Clock 20 */ + QE_CLK21, /* Clock 21 */ + QE_CLK22, /* Clock 22 */ + QE_CLK23, /* Clock 23 */ + QE_CLK24, /* Clock 24 */ + QE_RSYNC_PIN, /* RSYNC from pin */ + QE_TSYNC_PIN, /* TSYNC from pin */ + QE_CLK_DUMMY +}; + +static inline bool qe_clock_is_brg(enum qe_clock clk) +{ + return clk >= QE_BRG1 && clk <= QE_BRG16; +} + +extern spinlock_t cmxgcr_lock; + +/* Export QE common operations */ +#ifdef CONFIG_QUICC_ENGINE +extern void qe_reset(void); +#else +static inline void qe_reset(void) {} +#endif + +/* QE PIO */ +#define QE_PIO_PINS 32 + +struct qe_pio_regs { + __be32 cpodr; /* Open drain register */ + __be32 cpdata; /* Data register */ + __be32 cpdir1; /* Direction register */ + __be32 cpdir2; /* Direction register */ + __be32 cppar1; /* Pin assignment register */ + __be32 cppar2; /* Pin assignment register */ +#ifdef CONFIG_PPC_85xx + u8 pad[8]; +#endif +}; + +#define QE_PIO_DIR_IN 2 +#define QE_PIO_DIR_OUT 1 +extern void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, + int dir, int open_drain, int assignment, + int has_irq); +#ifdef CONFIG_QUICC_ENGINE +extern int par_io_init(struct device_node *np); +extern int par_io_of_config(struct device_node *np); +extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, + int assignment, int has_irq); +extern int par_io_data_set(u8 port, u8 pin, u8 val); +#else +static inline int par_io_init(struct device_node *np) { return -ENOSYS; } +static inline int par_io_of_config(struct device_node *np) { return -ENOSYS; } +static inline int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, + int assignment, int has_irq) { return -ENOSYS; } +static inline int par_io_data_set(u8 port, u8 pin, u8 val) { return -ENOSYS; } +#endif /* CONFIG_QUICC_ENGINE */ + +/* + * Pin multiplexing functions. + */ +struct qe_pin; +#ifdef CONFIG_QE_GPIO +extern struct qe_pin *qe_pin_request(struct device_node *np, int index); +extern void qe_pin_free(struct qe_pin *qe_pin); +extern void qe_pin_set_gpio(struct qe_pin *qe_pin); +extern void qe_pin_set_dedicated(struct qe_pin *pin); +#else +static inline struct qe_pin *qe_pin_request(struct device_node *np, int index) +{ + return ERR_PTR(-ENOSYS); +} +static inline void qe_pin_free(struct qe_pin *qe_pin) {} +static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {} +static inline void qe_pin_set_dedicated(struct qe_pin *pin) {} +#endif /* CONFIG_QE_GPIO */ + +#ifdef CONFIG_QUICC_ENGINE +int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input); +#else +static inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, + u32 cmd_input) +{ + return -ENOSYS; +} +#endif /* CONFIG_QUICC_ENGINE */ + +/* QE internal API */ +enum qe_clock qe_clock_source(const char *source); +unsigned int qe_get_brg_clk(void); +int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier); +int qe_get_snum(void); +void qe_put_snum(u8 snum); +unsigned int qe_get_num_of_risc(void); +unsigned int qe_get_num_of_snums(void); + +static inline int qe_alive_during_sleep(void) +{ + /* + * MPC8568E reference manual says: + * + * "...power down sequence waits for all I/O interfaces to become idle. + * In some applications this may happen eventually without actively + * shutting down interfaces, but most likely, software will have to + * take steps to shut down the eTSEC, QUICC Engine Block, and PCI + * interfaces before issuing the command (either the write to the core + * MSR[WE] as described above or writing to POWMGTCSR) to put the + * device into sleep state." + * + * MPC8569E reference manual has a similar paragraph. + */ +#ifdef CONFIG_PPC_85xx + return 0; +#else + return 1; +#endif +} + +/* we actually use cpm_muram implementation, define this for convenience */ +#define qe_muram_init cpm_muram_init +#define qe_muram_alloc cpm_muram_alloc +#define qe_muram_alloc_fixed cpm_muram_alloc_fixed +#define qe_muram_free cpm_muram_free +#define qe_muram_addr cpm_muram_addr +#define qe_muram_offset cpm_muram_offset + +/* Structure that defines QE firmware binary files. + * + * See Documentation/powerpc/qe_firmware.txt for a description of these + * fields. + */ +struct qe_firmware { + struct qe_header { + __be32 length; /* Length of the entire structure, in bytes */ + u8 magic[3]; /* Set to { 'Q', 'E', 'F' } */ + u8 version; /* Version of this layout. First ver is '1' */ + } header; + u8 id[62]; /* Null-terminated identifier string */ + u8 split; /* 0 = shared I-RAM, 1 = split I-RAM */ + u8 count; /* Number of microcode[] structures */ + struct { + __be16 model; /* The SOC model */ + u8 major; /* The SOC revision major */ + u8 minor; /* The SOC revision minor */ + } __packed soc; + u8 padding[4]; /* Reserved, for alignment */ + __be64 extended_modes; /* Extended modes */ + __be32 vtraps[8]; /* Virtual trap addresses */ + u8 reserved[4]; /* Reserved, for future expansion */ + struct qe_microcode { + u8 id[32]; /* Null-terminated identifier */ + __be32 traps[16]; /* Trap addresses, 0 == ignore */ + __be32 eccr; /* The value for the ECCR register */ + __be32 iram_offset; /* Offset into I-RAM for the code */ + __be32 count; /* Number of 32-bit words of the code */ + __be32 code_offset; /* Offset of the actual microcode */ + u8 major; /* The microcode version major */ + u8 minor; /* The microcode version minor */ + u8 revision; /* The microcode version revision */ + u8 padding; /* Reserved, for alignment */ + u8 reserved[4]; /* Reserved, for future expansion */ + } __packed microcode[1]; + /* All microcode binaries should be located here */ + /* CRC32 should be located here, after the microcode binaries */ +} __packed; + +struct qe_firmware_info { + char id[64]; /* Firmware name */ + u32 vtraps[8]; /* Virtual trap addresses */ + u64 extended_modes; /* Extended modes */ +}; + +#ifdef CONFIG_QUICC_ENGINE +/* Upload a firmware to the QE */ +int qe_upload_firmware(const struct qe_firmware *firmware); +#else +static inline int qe_upload_firmware(const struct qe_firmware *firmware) +{ + return -ENOSYS; +} +#endif /* CONFIG_QUICC_ENGINE */ + +/* Obtain information on the uploaded firmware */ +struct qe_firmware_info *qe_get_firmware_info(void); + +/* QE USB */ +int qe_usb_clock_set(enum qe_clock clk, int rate); + +/* Buffer descriptors */ +struct qe_bd { + __be16 status; + __be16 length; + __be32 buf; +} __packed; + +#define BD_STATUS_MASK 0xffff0000 +#define BD_LENGTH_MASK 0x0000ffff + +/* Alignment */ +#define QE_INTR_TABLE_ALIGN 16 /* ??? */ +#define QE_ALIGNMENT_OF_BD 8 +#define QE_ALIGNMENT_OF_PRAM 64 + +/* RISC allocation */ +#define QE_RISC_ALLOCATION_RISC1 0x1 /* RISC 1 */ +#define QE_RISC_ALLOCATION_RISC2 0x2 /* RISC 2 */ +#define QE_RISC_ALLOCATION_RISC3 0x4 /* RISC 3 */ +#define QE_RISC_ALLOCATION_RISC4 0x8 /* RISC 4 */ +#define QE_RISC_ALLOCATION_RISC1_AND_RISC2 (QE_RISC_ALLOCATION_RISC1 | \ + QE_RISC_ALLOCATION_RISC2) +#define QE_RISC_ALLOCATION_FOUR_RISCS (QE_RISC_ALLOCATION_RISC1 | \ + QE_RISC_ALLOCATION_RISC2 | \ + QE_RISC_ALLOCATION_RISC3 | \ + QE_RISC_ALLOCATION_RISC4) + +/* QE extended filtering Table Lookup Key Size */ +enum qe_fltr_tbl_lookup_key_size { + QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES + = 0x3f, /* LookupKey parsed by the Generate LookupKey + CMD is truncated to 8 bytes */ + QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES + = 0x5f, /* LookupKey parsed by the Generate LookupKey + CMD is truncated to 16 bytes */ +}; + +/* QE FLTR extended filtering Largest External Table Lookup Key Size */ +enum qe_fltr_largest_external_tbl_lookup_key_size { + QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE + = 0x0,/* not used */ + QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES + = QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES, /* 8 bytes */ + QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES + = QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES, /* 16 bytes */ +}; + +/* structure representing QE parameter RAM */ +struct qe_timer_tables { + u16 tm_base; /* QE timer table base adr */ + u16 tm_ptr; /* QE timer table pointer */ + u16 r_tmr; /* QE timer mode register */ + u16 r_tmv; /* QE timer valid register */ + u32 tm_cmd; /* QE timer cmd register */ + u32 tm_cnt; /* QE timer internal cnt */ +} __packed; + +#define QE_FLTR_TAD_SIZE 8 + +/* QE extended filtering Termination Action Descriptor (TAD) */ +struct qe_fltr_tad { + u8 serialized[QE_FLTR_TAD_SIZE]; +} __packed; + +/* Communication Direction */ +enum comm_dir { + COMM_DIR_NONE = 0, + COMM_DIR_RX = 1, + COMM_DIR_TX = 2, + COMM_DIR_RX_AND_TX = 3 +}; + +/* QE CMXUCR Registers. + * There are two UCCs represented in each of the four CMXUCR registers. + * These values are for the UCC in the LSBs + */ +#define QE_CMXUCR_MII_ENET_MNG 0x00007000 +#define QE_CMXUCR_MII_ENET_MNG_SHIFT 12 +#define QE_CMXUCR_GRANT 0x00008000 +#define QE_CMXUCR_TSA 0x00004000 +#define QE_CMXUCR_BKPT 0x00000100 +#define QE_CMXUCR_TX_CLK_SRC_MASK 0x0000000F + +/* QE CMXGCR Registers. +*/ +#define QE_CMXGCR_MII_ENET_MNG 0x00007000 +#define QE_CMXGCR_MII_ENET_MNG_SHIFT 12 +#define QE_CMXGCR_USBCS 0x0000000f +#define QE_CMXGCR_USBCS_CLK3 0x1 +#define QE_CMXGCR_USBCS_CLK5 0x2 +#define QE_CMXGCR_USBCS_CLK7 0x3 +#define QE_CMXGCR_USBCS_CLK9 0x4 +#define QE_CMXGCR_USBCS_CLK13 0x5 +#define QE_CMXGCR_USBCS_CLK17 0x6 +#define QE_CMXGCR_USBCS_CLK19 0x7 +#define QE_CMXGCR_USBCS_CLK21 0x8 +#define QE_CMXGCR_USBCS_BRG9 0x9 +#define QE_CMXGCR_USBCS_BRG10 0xa + +/* QE CECR Commands. +*/ +#define QE_CR_FLG 0x00010000 +#define QE_RESET 0x80000000 +#define QE_INIT_TX_RX 0x00000000 +#define QE_INIT_RX 0x00000001 +#define QE_INIT_TX 0x00000002 +#define QE_ENTER_HUNT_MODE 0x00000003 +#define QE_STOP_TX 0x00000004 +#define QE_GRACEFUL_STOP_TX 0x00000005 +#define QE_RESTART_TX 0x00000006 +#define QE_CLOSE_RX_BD 0x00000007 +#define QE_SWITCH_COMMAND 0x00000007 +#define QE_SET_GROUP_ADDRESS 0x00000008 +#define QE_START_IDMA 0x00000009 +#define QE_MCC_STOP_RX 0x00000009 +#define QE_ATM_TRANSMIT 0x0000000a +#define QE_HPAC_CLEAR_ALL 0x0000000b +#define QE_GRACEFUL_STOP_RX 0x0000001a +#define QE_RESTART_RX 0x0000001b +#define QE_HPAC_SET_PRIORITY 0x0000010b +#define QE_HPAC_STOP_TX 0x0000020b +#define QE_HPAC_STOP_RX 0x0000030b +#define QE_HPAC_GRACEFUL_STOP_TX 0x0000040b +#define QE_HPAC_GRACEFUL_STOP_RX 0x0000050b +#define QE_HPAC_START_TX 0x0000060b +#define QE_HPAC_START_RX 0x0000070b +#define QE_USB_STOP_TX 0x0000000a +#define QE_USB_RESTART_TX 0x0000000c +#define QE_QMC_STOP_TX 0x0000000c +#define QE_QMC_STOP_RX 0x0000000d +#define QE_SS7_SU_FIL_RESET 0x0000000e +/* jonathbr added from here down for 83xx */ +#define QE_RESET_BCS 0x0000000a +#define QE_MCC_INIT_TX_RX_16 0x00000003 +#define QE_MCC_STOP_TX 0x00000004 +#define QE_MCC_INIT_TX_1 0x00000005 +#define QE_MCC_INIT_RX_1 0x00000006 +#define QE_MCC_RESET 0x00000007 +#define QE_SET_TIMER 0x00000008 +#define QE_RANDOM_NUMBER 0x0000000c +#define QE_ATM_MULTI_THREAD_INIT 0x00000011 +#define QE_ASSIGN_PAGE 0x00000012 +#define QE_ADD_REMOVE_HASH_ENTRY 0x00000013 +#define QE_START_FLOW_CONTROL 0x00000014 +#define QE_STOP_FLOW_CONTROL 0x00000015 +#define QE_ASSIGN_PAGE_TO_DEVICE 0x00000016 + +#define QE_ASSIGN_RISC 0x00000010 +#define QE_CR_MCN_NORMAL_SHIFT 6 +#define QE_CR_MCN_USB_SHIFT 4 +#define QE_CR_MCN_RISC_ASSIGN_SHIFT 8 +#define QE_CR_SNUM_SHIFT 17 + +/* QE CECR Sub Block - sub block of QE command. +*/ +#define QE_CR_SUBBLOCK_INVALID 0x00000000 +#define QE_CR_SUBBLOCK_USB 0x03200000 +#define QE_CR_SUBBLOCK_UCCFAST1 0x02000000 +#define QE_CR_SUBBLOCK_UCCFAST2 0x02200000 +#define QE_CR_SUBBLOCK_UCCFAST3 0x02400000 +#define QE_CR_SUBBLOCK_UCCFAST4 0x02600000 +#define QE_CR_SUBBLOCK_UCCFAST5 0x02800000 +#define QE_CR_SUBBLOCK_UCCFAST6 0x02a00000 +#define QE_CR_SUBBLOCK_UCCFAST7 0x02c00000 +#define QE_CR_SUBBLOCK_UCCFAST8 0x02e00000 +#define QE_CR_SUBBLOCK_UCCSLOW1 0x00000000 +#define QE_CR_SUBBLOCK_UCCSLOW2 0x00200000 +#define QE_CR_SUBBLOCK_UCCSLOW3 0x00400000 +#define QE_CR_SUBBLOCK_UCCSLOW4 0x00600000 +#define QE_CR_SUBBLOCK_UCCSLOW5 0x00800000 +#define QE_CR_SUBBLOCK_UCCSLOW6 0x00a00000 +#define QE_CR_SUBBLOCK_UCCSLOW7 0x00c00000 +#define QE_CR_SUBBLOCK_UCCSLOW8 0x00e00000 +#define QE_CR_SUBBLOCK_MCC1 0x03800000 +#define QE_CR_SUBBLOCK_MCC2 0x03a00000 +#define QE_CR_SUBBLOCK_MCC3 0x03000000 +#define QE_CR_SUBBLOCK_IDMA1 0x02800000 +#define QE_CR_SUBBLOCK_IDMA2 0x02a00000 +#define QE_CR_SUBBLOCK_IDMA3 0x02c00000 +#define QE_CR_SUBBLOCK_IDMA4 0x02e00000 +#define QE_CR_SUBBLOCK_HPAC 0x01e00000 +#define QE_CR_SUBBLOCK_SPI1 0x01400000 +#define QE_CR_SUBBLOCK_SPI2 0x01600000 +#define QE_CR_SUBBLOCK_RAND 0x01c00000 +#define QE_CR_SUBBLOCK_TIMER 0x01e00000 +#define QE_CR_SUBBLOCK_GENERAL 0x03c00000 + +/* QE CECR Protocol - For non-MCC, specifies mode for QE CECR command */ +#define QE_CR_PROTOCOL_UNSPECIFIED 0x00 /* For all other protocols */ +#define QE_CR_PROTOCOL_HDLC_TRANSPARENT 0x00 +#define QE_CR_PROTOCOL_QMC 0x02 +#define QE_CR_PROTOCOL_UART 0x04 +#define QE_CR_PROTOCOL_ATM_POS 0x0A +#define QE_CR_PROTOCOL_ETHERNET 0x0C +#define QE_CR_PROTOCOL_L2_SWITCH 0x0D + +/* BRG configuration register */ +#define QE_BRGC_ENABLE 0x00010000 +#define QE_BRGC_DIVISOR_SHIFT 1 +#define QE_BRGC_DIVISOR_MAX 0xFFF +#define QE_BRGC_DIV16 1 + +/* QE Timers registers */ +#define QE_GTCFR1_PCAS 0x80 +#define QE_GTCFR1_STP2 0x20 +#define QE_GTCFR1_RST2 0x10 +#define QE_GTCFR1_GM2 0x08 +#define QE_GTCFR1_GM1 0x04 +#define QE_GTCFR1_STP1 0x02 +#define QE_GTCFR1_RST1 0x01 + +/* SDMA registers */ +#define QE_SDSR_BER1 0x02000000 +#define QE_SDSR_BER2 0x01000000 + +#define QE_SDMR_GLB_1_MSK 0x80000000 +#define QE_SDMR_ADR_SEL 0x20000000 +#define QE_SDMR_BER1_MSK 0x02000000 +#define QE_SDMR_BER2_MSK 0x01000000 +#define QE_SDMR_EB1_MSK 0x00800000 +#define QE_SDMR_ER1_MSK 0x00080000 +#define QE_SDMR_ER2_MSK 0x00040000 +#define QE_SDMR_CEN_MASK 0x0000E000 +#define QE_SDMR_SBER_1 0x00000200 +#define QE_SDMR_SBER_2 0x00000200 +#define QE_SDMR_EB1_PR_MASK 0x000000C0 +#define QE_SDMR_ER1_PR 0x00000008 + +#define QE_SDMR_CEN_SHIFT 13 +#define QE_SDMR_EB1_PR_SHIFT 6 + +#define QE_SDTM_MSNUM_SHIFT 24 + +#define QE_SDEBCR_BA_MASK 0x01FFFFFF + +/* Communication Processor */ +#define QE_CP_CERCR_MEE 0x8000 /* Multi-user RAM ECC enable */ +#define QE_CP_CERCR_IEE 0x4000 /* Instruction RAM ECC enable */ +#define QE_CP_CERCR_CIR 0x0800 /* Common instruction RAM */ + +/* I-RAM */ +#define QE_IRAM_IADD_AIE 0x80000000 /* Auto Increment Enable */ +#define QE_IRAM_IADD_BADDR 0x00080000 /* Base Address */ +#define QE_IRAM_READY 0x80000000 /* Ready */ + +/* UPC */ +#define UPGCR_PROTOCOL 0x80000000 /* protocol ul2 or pl2 */ +#define UPGCR_TMS 0x40000000 /* Transmit master/slave mode */ +#define UPGCR_RMS 0x20000000 /* Receive master/slave mode */ +#define UPGCR_ADDR 0x10000000 /* Master MPHY Addr multiplexing */ +#define UPGCR_DIAG 0x01000000 /* Diagnostic mode */ + +/* UCC GUEMR register */ +#define UCC_GUEMR_MODE_MASK_RX 0x02 +#define UCC_GUEMR_MODE_FAST_RX 0x02 +#define UCC_GUEMR_MODE_SLOW_RX 0x00 +#define UCC_GUEMR_MODE_MASK_TX 0x01 +#define UCC_GUEMR_MODE_FAST_TX 0x01 +#define UCC_GUEMR_MODE_SLOW_TX 0x00 +#define UCC_GUEMR_MODE_MASK (UCC_GUEMR_MODE_MASK_RX | UCC_GUEMR_MODE_MASK_TX) +#define UCC_GUEMR_SET_RESERVED3 0x10 /* Bit 3 in the guemr is reserved but + must be set 1 */ + +/* structure representing UCC SLOW parameter RAM */ +struct ucc_slow_pram { + __be16 rbase; /* RX BD base address */ + __be16 tbase; /* TX BD base address */ + u8 rbmr; /* RX bus mode register (same as CPM's RFCR) */ + u8 tbmr; /* TX bus mode register (same as CPM's TFCR) */ + __be16 mrblr; /* Rx buffer length */ + __be32 rstate; /* Rx internal state */ + __be32 rptr; /* Rx internal data pointer */ + __be16 rbptr; /* rb BD Pointer */ + __be16 rcount; /* Rx internal byte count */ + __be32 rtemp; /* Rx temp */ + __be32 tstate; /* Tx internal state */ + __be32 tptr; /* Tx internal data pointer */ + __be16 tbptr; /* Tx BD pointer */ + __be16 tcount; /* Tx byte count */ + __be32 ttemp; /* Tx temp */ + __be32 rcrc; /* temp receive CRC */ + __be32 tcrc; /* temp transmit CRC */ +} __packed; + +/* General UCC SLOW Mode Register (GUMRH & GUMRL) */ +#define UCC_SLOW_GUMR_H_SAM_QMC 0x00000000 +#define UCC_SLOW_GUMR_H_SAM_SATM 0x00008000 +#define UCC_SLOW_GUMR_H_REVD 0x00002000 +#define UCC_SLOW_GUMR_H_TRX 0x00001000 +#define UCC_SLOW_GUMR_H_TTX 0x00000800 +#define UCC_SLOW_GUMR_H_CDP 0x00000400 +#define UCC_SLOW_GUMR_H_CTSP 0x00000200 +#define UCC_SLOW_GUMR_H_CDS 0x00000100 +#define UCC_SLOW_GUMR_H_CTSS 0x00000080 +#define UCC_SLOW_GUMR_H_TFL 0x00000040 +#define UCC_SLOW_GUMR_H_RFW 0x00000020 +#define UCC_SLOW_GUMR_H_TXSY 0x00000010 +#define UCC_SLOW_GUMR_H_4SYNC 0x00000004 +#define UCC_SLOW_GUMR_H_8SYNC 0x00000008 +#define UCC_SLOW_GUMR_H_16SYNC 0x0000000c +#define UCC_SLOW_GUMR_H_RTSM 0x00000002 +#define UCC_SLOW_GUMR_H_RSYN 0x00000001 + +#define UCC_SLOW_GUMR_L_TCI 0x10000000 +#define UCC_SLOW_GUMR_L_RINV 0x02000000 +#define UCC_SLOW_GUMR_L_TINV 0x01000000 +#define UCC_SLOW_GUMR_L_TEND 0x00040000 +#define UCC_SLOW_GUMR_L_TDCR_MASK 0x00030000 +#define UCC_SLOW_GUMR_L_TDCR_32 0x00030000 +#define UCC_SLOW_GUMR_L_TDCR_16 0x00020000 +#define UCC_SLOW_GUMR_L_TDCR_8 0x00010000 +#define UCC_SLOW_GUMR_L_TDCR_1 0x00000000 +#define UCC_SLOW_GUMR_L_RDCR_MASK 0x0000c000 +#define UCC_SLOW_GUMR_L_RDCR_32 0x0000c000 +#define UCC_SLOW_GUMR_L_RDCR_16 0x00008000 +#define UCC_SLOW_GUMR_L_RDCR_8 0x00004000 +#define UCC_SLOW_GUMR_L_RDCR_1 0x00000000 +#define UCC_SLOW_GUMR_L_RENC_NRZI 0x00000800 +#define UCC_SLOW_GUMR_L_RENC_NRZ 0x00000000 +#define UCC_SLOW_GUMR_L_TENC_NRZI 0x00000100 +#define UCC_SLOW_GUMR_L_TENC_NRZ 0x00000000 +#define UCC_SLOW_GUMR_L_DIAG_MASK 0x000000c0 +#define UCC_SLOW_GUMR_L_DIAG_LE 0x000000c0 +#define UCC_SLOW_GUMR_L_DIAG_ECHO 0x00000080 +#define UCC_SLOW_GUMR_L_DIAG_LOOP 0x00000040 +#define UCC_SLOW_GUMR_L_DIAG_NORM 0x00000000 +#define UCC_SLOW_GUMR_L_ENR 0x00000020 +#define UCC_SLOW_GUMR_L_ENT 0x00000010 +#define UCC_SLOW_GUMR_L_MODE_MASK 0x0000000F +#define UCC_SLOW_GUMR_L_MODE_BISYNC 0x00000008 +#define UCC_SLOW_GUMR_L_MODE_AHDLC 0x00000006 +#define UCC_SLOW_GUMR_L_MODE_UART 0x00000004 +#define UCC_SLOW_GUMR_L_MODE_QMC 0x00000002 + +/* General UCC FAST Mode Register */ +#define UCC_FAST_GUMR_TCI 0x20000000 +#define UCC_FAST_GUMR_TRX 0x10000000 +#define UCC_FAST_GUMR_TTX 0x08000000 +#define UCC_FAST_GUMR_CDP 0x04000000 +#define UCC_FAST_GUMR_CTSP 0x02000000 +#define UCC_FAST_GUMR_CDS 0x01000000 +#define UCC_FAST_GUMR_CTSS 0x00800000 +#define UCC_FAST_GUMR_TXSY 0x00020000 +#define UCC_FAST_GUMR_RSYN 0x00010000 +#define UCC_FAST_GUMR_RTSM 0x00002000 +#define UCC_FAST_GUMR_REVD 0x00000400 +#define UCC_FAST_GUMR_ENR 0x00000020 +#define UCC_FAST_GUMR_ENT 0x00000010 + +/* UART Slow UCC Event Register (UCCE) */ +#define UCC_UART_UCCE_AB 0x0200 +#define UCC_UART_UCCE_IDLE 0x0100 +#define UCC_UART_UCCE_GRA 0x0080 +#define UCC_UART_UCCE_BRKE 0x0040 +#define UCC_UART_UCCE_BRKS 0x0020 +#define UCC_UART_UCCE_CCR 0x0008 +#define UCC_UART_UCCE_BSY 0x0004 +#define UCC_UART_UCCE_TX 0x0002 +#define UCC_UART_UCCE_RX 0x0001 + +/* HDLC Slow UCC Event Register (UCCE) */ +#define UCC_HDLC_UCCE_GLR 0x1000 +#define UCC_HDLC_UCCE_GLT 0x0800 +#define UCC_HDLC_UCCE_IDLE 0x0100 +#define UCC_HDLC_UCCE_BRKE 0x0040 +#define UCC_HDLC_UCCE_BRKS 0x0020 +#define UCC_HDLC_UCCE_TXE 0x0010 +#define UCC_HDLC_UCCE_RXF 0x0008 +#define UCC_HDLC_UCCE_BSY 0x0004 +#define UCC_HDLC_UCCE_TXB 0x0002 +#define UCC_HDLC_UCCE_RXB 0x0001 + +/* BISYNC Slow UCC Event Register (UCCE) */ +#define UCC_BISYNC_UCCE_GRA 0x0080 +#define UCC_BISYNC_UCCE_TXE 0x0010 +#define UCC_BISYNC_UCCE_RCH 0x0008 +#define UCC_BISYNC_UCCE_BSY 0x0004 +#define UCC_BISYNC_UCCE_TXB 0x0002 +#define UCC_BISYNC_UCCE_RXB 0x0001 + +/* Transparent UCC Event Register (UCCE) */ +#define UCC_TRANS_UCCE_GRA 0x0080 +#define UCC_TRANS_UCCE_TXE 0x0010 +#define UCC_TRANS_UCCE_RXF 0x0008 +#define UCC_TRANS_UCCE_BSY 0x0004 +#define UCC_TRANS_UCCE_TXB 0x0002 +#define UCC_TRANS_UCCE_RXB 0x0001 + + +/* Gigabit Ethernet Fast UCC Event Register (UCCE) */ +#define UCC_GETH_UCCE_MPD 0x80000000 +#define UCC_GETH_UCCE_SCAR 0x40000000 +#define UCC_GETH_UCCE_GRA 0x20000000 +#define UCC_GETH_UCCE_CBPR 0x10000000 +#define UCC_GETH_UCCE_BSY 0x08000000 +#define UCC_GETH_UCCE_RXC 0x04000000 +#define UCC_GETH_UCCE_TXC 0x02000000 +#define UCC_GETH_UCCE_TXE 0x01000000 +#define UCC_GETH_UCCE_TXB7 0x00800000 +#define UCC_GETH_UCCE_TXB6 0x00400000 +#define UCC_GETH_UCCE_TXB5 0x00200000 +#define UCC_GETH_UCCE_TXB4 0x00100000 +#define UCC_GETH_UCCE_TXB3 0x00080000 +#define UCC_GETH_UCCE_TXB2 0x00040000 +#define UCC_GETH_UCCE_TXB1 0x00020000 +#define UCC_GETH_UCCE_TXB0 0x00010000 +#define UCC_GETH_UCCE_RXB7 0x00008000 +#define UCC_GETH_UCCE_RXB6 0x00004000 +#define UCC_GETH_UCCE_RXB5 0x00002000 +#define UCC_GETH_UCCE_RXB4 0x00001000 +#define UCC_GETH_UCCE_RXB3 0x00000800 +#define UCC_GETH_UCCE_RXB2 0x00000400 +#define UCC_GETH_UCCE_RXB1 0x00000200 +#define UCC_GETH_UCCE_RXB0 0x00000100 +#define UCC_GETH_UCCE_RXF7 0x00000080 +#define UCC_GETH_UCCE_RXF6 0x00000040 +#define UCC_GETH_UCCE_RXF5 0x00000020 +#define UCC_GETH_UCCE_RXF4 0x00000010 +#define UCC_GETH_UCCE_RXF3 0x00000008 +#define UCC_GETH_UCCE_RXF2 0x00000004 +#define UCC_GETH_UCCE_RXF1 0x00000002 +#define UCC_GETH_UCCE_RXF0 0x00000001 + +/* UCC Protocol Specific Mode Register (UPSMR), when used for UART */ +#define UCC_UART_UPSMR_FLC 0x8000 +#define UCC_UART_UPSMR_SL 0x4000 +#define UCC_UART_UPSMR_CL_MASK 0x3000 +#define UCC_UART_UPSMR_CL_8 0x3000 +#define UCC_UART_UPSMR_CL_7 0x2000 +#define UCC_UART_UPSMR_CL_6 0x1000 +#define UCC_UART_UPSMR_CL_5 0x0000 +#define UCC_UART_UPSMR_UM_MASK 0x0c00 +#define UCC_UART_UPSMR_UM_NORMAL 0x0000 +#define UCC_UART_UPSMR_UM_MAN_MULTI 0x0400 +#define UCC_UART_UPSMR_UM_AUTO_MULTI 0x0c00 +#define UCC_UART_UPSMR_FRZ 0x0200 +#define UCC_UART_UPSMR_RZS 0x0100 +#define UCC_UART_UPSMR_SYN 0x0080 +#define UCC_UART_UPSMR_DRT 0x0040 +#define UCC_UART_UPSMR_PEN 0x0010 +#define UCC_UART_UPSMR_RPM_MASK 0x000c +#define UCC_UART_UPSMR_RPM_ODD 0x0000 +#define UCC_UART_UPSMR_RPM_LOW 0x0004 +#define UCC_UART_UPSMR_RPM_EVEN 0x0008 +#define UCC_UART_UPSMR_RPM_HIGH 0x000C +#define UCC_UART_UPSMR_TPM_MASK 0x0003 +#define UCC_UART_UPSMR_TPM_ODD 0x0000 +#define UCC_UART_UPSMR_TPM_LOW 0x0001 +#define UCC_UART_UPSMR_TPM_EVEN 0x0002 +#define UCC_UART_UPSMR_TPM_HIGH 0x0003 + +/* UCC Protocol Specific Mode Register (UPSMR), when used for Ethernet */ +#define UCC_GETH_UPSMR_FTFE 0x80000000 +#define UCC_GETH_UPSMR_PTPE 0x40000000 +#define UCC_GETH_UPSMR_ECM 0x04000000 +#define UCC_GETH_UPSMR_HSE 0x02000000 +#define UCC_GETH_UPSMR_PRO 0x00400000 +#define UCC_GETH_UPSMR_CAP 0x00200000 +#define UCC_GETH_UPSMR_RSH 0x00100000 +#define UCC_GETH_UPSMR_RPM 0x00080000 +#define UCC_GETH_UPSMR_R10M 0x00040000 +#define UCC_GETH_UPSMR_RLPB 0x00020000 +#define UCC_GETH_UPSMR_TBIM 0x00010000 +#define UCC_GETH_UPSMR_RES1 0x00002000 +#define UCC_GETH_UPSMR_RMM 0x00001000 +#define UCC_GETH_UPSMR_CAM 0x00000400 +#define UCC_GETH_UPSMR_BRO 0x00000200 +#define UCC_GETH_UPSMR_SMM 0x00000080 +#define UCC_GETH_UPSMR_SGMM 0x00000020 + +/* UCC Transmit On Demand Register (UTODR) */ +#define UCC_SLOW_TOD 0x8000 +#define UCC_FAST_TOD 0x8000 + +/* UCC Bus Mode Register masks */ +/* Not to be confused with the Bundle Mode Register */ +#define UCC_BMR_GBL 0x20 +#define UCC_BMR_BO_BE 0x10 +#define UCC_BMR_CETM 0x04 +#define UCC_BMR_DTB 0x02 +#define UCC_BMR_BDB 0x01 + +/* Function code masks */ +#define FC_GBL 0x20 +#define FC_DTB_LCL 0x02 +#define UCC_FAST_FUNCTION_CODE_GBL 0x20 +#define UCC_FAST_FUNCTION_CODE_DTB_LCL 0x02 +#define UCC_FAST_FUNCTION_CODE_BDB_LCL 0x01 + +#endif /* __KERNEL__ */ +#endif /* _ASM_POWERPC_QE_H */ diff --git a/include/linux/fsl/qe_ic.h b/include/linux/fsl/qe_ic.h new file mode 100644 index 0000000..79f162c --- /dev/null +++ b/include/linux/fsl/qe_ic.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * Description: + * QE IC external definitions and structure. + * + * 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. + */ +#ifndef _ASM_POWERPC_QE_IC_H +#define _ASM_POWERPC_QE_IC_H + +#include + +struct device_node; +struct qe_ic; + +#define NUM_OF_QE_IC_GROUPS 6 + +/* Flags when we init the QE IC */ +#define QE_IC_SPREADMODE_GRP_W 0x00000001 +#define QE_IC_SPREADMODE_GRP_X 0x00000002 +#define QE_IC_SPREADMODE_GRP_Y 0x00000004 +#define QE_IC_SPREADMODE_GRP_Z 0x00000008 +#define QE_IC_SPREADMODE_GRP_RISCA 0x00000010 +#define QE_IC_SPREADMODE_GRP_RISCB 0x00000020 + +#define QE_IC_LOW_SIGNAL 0x00000100 +#define QE_IC_HIGH_SIGNAL 0x00000200 + +#define QE_IC_GRP_W_PRI0_DEST_SIGNAL_HIGH 0x00001000 +#define QE_IC_GRP_W_PRI1_DEST_SIGNAL_HIGH 0x00002000 +#define QE_IC_GRP_X_PRI0_DEST_SIGNAL_HIGH 0x00004000 +#define QE_IC_GRP_X_PRI1_DEST_SIGNAL_HIGH 0x00008000 +#define QE_IC_GRP_Y_PRI0_DEST_SIGNAL_HIGH 0x00010000 +#define QE_IC_GRP_Y_PRI1_DEST_SIGNAL_HIGH 0x00020000 +#define QE_IC_GRP_Z_PRI0_DEST_SIGNAL_HIGH 0x00040000 +#define QE_IC_GRP_Z_PRI1_DEST_SIGNAL_HIGH 0x00080000 +#define QE_IC_GRP_RISCA_PRI0_DEST_SIGNAL_HIGH 0x00100000 +#define QE_IC_GRP_RISCA_PRI1_DEST_SIGNAL_HIGH 0x00200000 +#define QE_IC_GRP_RISCB_PRI0_DEST_SIGNAL_HIGH 0x00400000 +#define QE_IC_GRP_RISCB_PRI1_DEST_SIGNAL_HIGH 0x00800000 +#define QE_IC_GRP_W_DEST_SIGNAL_SHIFT (12) + +/* QE interrupt sources groups */ +enum qe_ic_grp_id { + QE_IC_GRP_W = 0, /* QE interrupt controller group W */ + QE_IC_GRP_X, /* QE interrupt controller group X */ + QE_IC_GRP_Y, /* QE interrupt controller group Y */ + QE_IC_GRP_Z, /* QE interrupt controller group Z */ + QE_IC_GRP_RISCA, /* QE interrupt controller RISC group A */ + QE_IC_GRP_RISCB /* QE interrupt controller RISC group B */ +}; + +#ifdef CONFIG_QUICC_ENGINE +void qe_ic_init(struct device_node *node, unsigned int flags, + void (*low_handler)(unsigned int irq, struct irq_desc *desc), + void (*high_handler)(unsigned int irq, struct irq_desc *desc)); +unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic); +unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic); +#else +static inline void qe_ic_init(struct device_node *node, unsigned int flags, + void (*low_handler)(unsigned int irq, struct irq_desc *desc), + void (*high_handler)(unsigned int irq, struct irq_desc *desc)) +{} +static inline unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic) +{ return 0; } +static inline unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) +{ return 0; } +#endif /* CONFIG_QUICC_ENGINE */ + +void qe_ic_set_highest_priority(unsigned int virq, int high); +int qe_ic_set_priority(unsigned int virq, unsigned int priority); +int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high); + +static inline void qe_ic_cascade_low_ipic(unsigned int irq, + struct irq_desc *desc) +{ + struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); + unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic); + + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); +} + +static inline void qe_ic_cascade_high_ipic(unsigned int irq, + struct irq_desc *desc) +{ + struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); + unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic); + + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); +} + +static inline void qe_ic_cascade_low_mpic(unsigned int irq, + struct irq_desc *desc) +{ + struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); + unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic); + struct irq_chip *chip = irq_desc_get_chip(desc); + + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); + + chip->irq_eoi(&desc->irq_data); +} + +static inline void qe_ic_cascade_high_mpic(unsigned int irq, + struct irq_desc *desc) +{ + struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); + unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic); + struct irq_chip *chip = irq_desc_get_chip(desc); + + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); + + chip->irq_eoi(&desc->irq_data); +} + +static inline void qe_ic_cascade_muxed_mpic(unsigned int irq, + struct irq_desc *desc) +{ + struct qe_ic *qe_ic = irq_desc_get_handler_data(desc); + unsigned int cascade_irq; + struct irq_chip *chip = irq_desc_get_chip(desc); + + cascade_irq = qe_ic_get_high_irq(qe_ic); + if (cascade_irq == NO_IRQ) + cascade_irq = qe_ic_get_low_irq(qe_ic); + + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); + + chip->irq_eoi(&desc->irq_data); +} + +#endif /* _ASM_POWERPC_QE_IC_H */ diff --git a/include/linux/fsl/ucc.h b/include/linux/fsl/ucc.h new file mode 100644 index 0000000..622e2fc --- /dev/null +++ b/include/linux/fsl/ucc.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * Description: + * Internal header file for UCC unit routines. + * + * 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. + */ +#ifndef __UCC_H__ +#define __UCC_H__ + +#include +#include + +#define STATISTICS + +#define UCC_MAX_NUM 8 + +/* Slow or fast type for UCCs. +*/ +enum ucc_speed_type { + UCC_SPEED_TYPE_FAST = UCC_GUEMR_MODE_FAST_RX | UCC_GUEMR_MODE_FAST_TX, + UCC_SPEED_TYPE_SLOW = UCC_GUEMR_MODE_SLOW_RX | UCC_GUEMR_MODE_SLOW_TX +}; + +/* ucc_set_type + * Sets UCC to slow or fast mode. + * + * ucc_num - (In) number of UCC (0-7). + * speed - (In) slow or fast mode for UCC. + */ +int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed); + +int ucc_set_qe_mux_mii_mng(unsigned int ucc_num); + +int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, + enum comm_dir mode); +int ucc_set_tdm_rxtx_clk(unsigned int tdm_num, enum qe_clock clock, + enum comm_dir mode); +int ucc_set_tdm_rxtx_sync(unsigned int tdm_num, enum qe_clock clock, + enum comm_dir mode); + +int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask); + +/* QE MUX clock routing for UCC +*/ +static inline int ucc_set_qe_mux_grant(unsigned int ucc_num, int set) +{ + return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_GRANT); +} + +static inline int ucc_set_qe_mux_tsa(unsigned int ucc_num, int set) +{ + return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_TSA); +} + +static inline int ucc_set_qe_mux_bkpt(unsigned int ucc_num, int set) +{ + return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_BKPT); +} + +#endif /* __UCC_H__ */ diff --git a/include/linux/fsl/ucc_fast.h b/include/linux/fsl/ucc_fast.h new file mode 100644 index 0000000..ec24f28 --- /dev/null +++ b/include/linux/fsl/ucc_fast.h @@ -0,0 +1,251 @@ +/* + * Internal header file for UCC FAST unit routines. + * + * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * 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. + */ +#ifndef __UCC_FAST_H__ +#define __UCC_FAST_H__ + +#include + +#include +#include + +#include + +/* Receive BD's status */ +#define R_E 0x80000000 /* buffer empty */ +#define R_W 0x20000000 /* wrap bit */ +#define R_I 0x10000000 /* interrupt on reception */ +#define R_L 0x08000000 /* last */ +#define R_F 0x04000000 /* first */ +#define R_CM 0x02000000 /* CM */ + +/* transmit BD's status */ +#define T_R 0x80000000 /* ready bit */ +#define T_W 0x20000000 /* wrap bit */ +#define T_I 0x10000000 /* interrupt on completion */ +#define T_L 0x08000000 /* last */ +#define T_TC 0x04000000 /* crc */ +#define T_CM 0x02000000 /* CM */ + +/* Rx Data buffer must be 4 bytes aligned in most cases */ +#define UCC_FAST_RX_ALIGN 4 +#define UCC_FAST_MRBLR_ALIGNMENT 4 +#define UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT 8 + +/* Sizes */ +#define UCC_FAST_URFS_MIN_VAL 0x88 +#define UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR 8 + +/* ucc_fast_channel_protocol_mode - UCC FAST mode */ +enum ucc_fast_channel_protocol_mode { + UCC_FAST_PROTOCOL_MODE_HDLC = 0x00000000, + UCC_FAST_PROTOCOL_MODE_RESERVED01 = 0x00000001, + UCC_FAST_PROTOCOL_MODE_RESERVED_QMC = 0x00000002, + UCC_FAST_PROTOCOL_MODE_RESERVED02 = 0x00000003, + UCC_FAST_PROTOCOL_MODE_RESERVED_UART = 0x00000004, + UCC_FAST_PROTOCOL_MODE_RESERVED03 = 0x00000005, + UCC_FAST_PROTOCOL_MODE_RESERVED_EX_MAC_1 = 0x00000006, + UCC_FAST_PROTOCOL_MODE_RESERVED_EX_MAC_2 = 0x00000007, + UCC_FAST_PROTOCOL_MODE_RESERVED_BISYNC = 0x00000008, + UCC_FAST_PROTOCOL_MODE_RESERVED04 = 0x00000009, + UCC_FAST_PROTOCOL_MODE_ATM = 0x0000000A, + UCC_FAST_PROTOCOL_MODE_RESERVED05 = 0x0000000B, + UCC_FAST_PROTOCOL_MODE_ETHERNET = 0x0000000C, + UCC_FAST_PROTOCOL_MODE_RESERVED06 = 0x0000000D, + UCC_FAST_PROTOCOL_MODE_POS = 0x0000000E, + UCC_FAST_PROTOCOL_MODE_RESERVED07 = 0x0000000F +}; + +/* ucc_fast_transparent_txrx - UCC Fast Transparent TX & RX */ +enum ucc_fast_transparent_txrx { + UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL = 0x00000000, + UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_TRANSPARENT = 0x18000000 +}; + +/* UCC fast diagnostic mode */ +enum ucc_fast_diag_mode { + UCC_FAST_DIAGNOSTIC_NORMAL = 0x0, + UCC_FAST_DIAGNOSTIC_LOCAL_LOOP_BACK = 0x40000000, + UCC_FAST_DIAGNOSTIC_AUTO_ECHO = 0x80000000, + UCC_FAST_DIAGNOSTIC_LOOP_BACK_AND_ECHO = 0xC0000000 +}; + +/* UCC fast Sync length (transparent mode only) */ +enum ucc_fast_sync_len { + UCC_FAST_SYNC_LEN_NOT_USED = 0x0, + UCC_FAST_SYNC_LEN_AUTOMATIC = 0x00004000, + UCC_FAST_SYNC_LEN_8_BIT = 0x00008000, + UCC_FAST_SYNC_LEN_16_BIT = 0x0000C000 +}; + +/* UCC fast RTS mode */ +enum ucc_fast_ready_to_send { + UCC_FAST_SEND_IDLES_BETWEEN_FRAMES = 0x00000000, + UCC_FAST_SEND_FLAGS_BETWEEN_FRAMES = 0x00002000 +}; + +/* UCC fast receiver decoding mode */ +enum ucc_fast_rx_decoding_method { + UCC_FAST_RX_ENCODING_NRZ = 0x00000000, + UCC_FAST_RX_ENCODING_NRZI = 0x00000800, + UCC_FAST_RX_ENCODING_RESERVED0 = 0x00001000, + UCC_FAST_RX_ENCODING_RESERVED1 = 0x00001800 +}; + +/* UCC fast transmitter encoding mode */ +enum ucc_fast_tx_encoding_method { + UCC_FAST_TX_ENCODING_NRZ = 0x00000000, + UCC_FAST_TX_ENCODING_NRZI = 0x00000100, + UCC_FAST_TX_ENCODING_RESERVED0 = 0x00000200, + UCC_FAST_TX_ENCODING_RESERVED1 = 0x00000300 +}; + +/* UCC fast CRC length */ +enum ucc_fast_transparent_tcrc { + UCC_FAST_16_BIT_CRC = 0x00000000, + UCC_FAST_CRC_RESERVED0 = 0x00000040, + UCC_FAST_32_BIT_CRC = 0x00000080, + UCC_FAST_CRC_RESERVED1 = 0x000000C0 +}; + +/* Fast UCC initialization structure */ +struct ucc_fast_info { + int ucc_num; + int tdm_num; + enum qe_clock rx_clock; + enum qe_clock tx_clock; + enum qe_clock rx_sync; + enum qe_clock tx_sync; + resource_size_t regs; + int irq; + u32 uccm_mask; + int bd_mem_part; + int brkpt_support; + int grant_support; + int tsa; + int cdp; + int cds; + int ctsp; + int ctss; + int tci; + int txsy; + int rtsm; + int revd; + int rsyn; + u16 max_rx_buf_length; + u16 urfs; + u16 urfet; + u16 urfset; + u16 utfs; + u16 utfet; + u16 utftt; + u16 ufpt; + enum ucc_fast_channel_protocol_mode mode; + enum ucc_fast_transparent_txrx ttx_trx; + enum ucc_fast_tx_encoding_method tenc; + enum ucc_fast_rx_decoding_method renc; + enum ucc_fast_transparent_tcrc tcrc; + enum ucc_fast_sync_len synl; +}; + +struct ucc_fast_private { + struct ucc_fast_info *uf_info; + struct ucc_fast __iomem *uf_regs; /* a pointer to the UCC regs. */ + u32 __iomem *p_ucce; /* a pointer to the event register in memory. */ + u32 __iomem *p_uccm; /* a pointer to the mask register in memory. */ +#ifdef CONFIG_UGETH_TX_ON_DEMAND + u16 __iomem *p_utodr; /* pointer to the transmit on demand register */ +#endif + int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ + int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ + int stopped_tx; /* Whether channel has been stopped for Tx + (STOP_TX, etc.) */ + int stopped_rx; /* Whether channel has been stopped for Rx */ + u32 ucc_fast_tx_virtual_fifo_base_offset;/* pointer to base of Tx + virtual fifo */ + u32 ucc_fast_rx_virtual_fifo_base_offset;/* pointer to base of Rx + virtual fifo */ +#ifdef STATISTICS + u32 tx_frames; /* Transmitted frames counter. */ + u32 rx_frames; /* Received frames counter (only frames + passed to application). */ + u32 tx_discarded; /* Discarded tx frames counter (frames that + were discarded by the driver due to errors). + */ + u32 rx_discarded; /* Discarded rx frames counter (frames that + were discarded by the driver due to errors). + */ +#endif /* STATISTICS */ + u16 mrblr; /* maximum receive buffer length */ +}; + +/* ucc_fast_init + * Initializes Fast UCC according to user provided parameters. + * + * uf_info - (In) pointer to the fast UCC info structure. + * uccf_ret - (Out) pointer to the fast UCC structure. + */ +int ucc_fast_init(struct ucc_fast_info *uf_info, + struct ucc_fast_private **uccf_ret); + +/* ucc_fast_free + * Frees all resources for fast UCC. + * + * uccf - (In) pointer to the fast UCC structure. + */ +void ucc_fast_free(struct ucc_fast_private *uccf); + +/* ucc_fast_enable + * Enables a fast UCC port. + * This routine enables Tx and/or Rx through the General UCC Mode Register. + * + * uccf - (In) pointer to the fast UCC structure. + * mode - (In) TX, RX, or both. + */ +void ucc_fast_enable(struct ucc_fast_private *uccf, enum comm_dir mode); + +/* ucc_fast_disable + * Disables a fast UCC port. + * This routine disables Tx and/or Rx through the General UCC Mode Register. + * + * uccf - (In) pointer to the fast UCC structure. + * mode - (In) TX, RX, or both. + */ +void ucc_fast_disable(struct ucc_fast_private *uccf, enum comm_dir mode); + +/* ucc_fast_irq + * Handles interrupts on fast UCC. + * Called from the general interrupt routine to handle interrupts on fast UCC. + * + * uccf - (In) pointer to the fast UCC structure. + */ +void ucc_fast_irq(struct ucc_fast_private *uccf); + +/* ucc_fast_transmit_on_demand + * Immediately forces a poll of the transmitter for data to be sent. + * Typically, the hardware performs a periodic poll for data that the + * transmit routine has set up to be transmitted. In cases where + * this polling cycle is not soon enough, this optional routine can + * be invoked to force a poll right away, instead. Proper use for + * each transmission for which this functionality is desired is to + * call the transmit routine and then this routine right after. + * + * uccf - (In) pointer to the fast UCC structure. + */ +void ucc_fast_transmit_on_demand(struct ucc_fast_private *uccf); + +u32 ucc_fast_get_qe_cr_subblock(int uccf_num); + +void ucc_fast_dump_regs(struct ucc_fast_private *uccf); + +#endif /* __UCC_FAST_H__ */ diff --git a/include/linux/fsl/ucc_slow.h b/include/linux/fsl/ucc_slow.h new file mode 100644 index 0000000..56c0318 --- /dev/null +++ b/include/linux/fsl/ucc_slow.h @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. + * + * Authors: Shlomi Gridish + * Li Yang + * + * Description: + * Internal header file for UCC SLOW unit routines. + * + * 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. + */ +#ifndef __UCC_SLOW_H__ +#define __UCC_SLOW_H__ + +#include + +#include +#include + +#include + +/* transmit BD's status */ +#define T_R 0x80000000 /* ready bit */ +#define T_PAD 0x40000000 /* add pads to short frames */ +#define T_W 0x20000000 /* wrap bit */ +#define T_I 0x10000000 /* interrupt on completion */ +#define T_L 0x08000000 /* last */ + +#define T_A 0x04000000 /* Address - the data transmitted as address + chars */ +#define T_TC 0x04000000 /* transmit CRC */ +#define T_CM 0x02000000 /* continuous mode */ +#define T_DEF 0x02000000 /* collision on previous attempt to transmit */ +#define T_P 0x01000000 /* Preamble - send Preamble sequence before + data */ +#define T_HB 0x01000000 /* heartbeat */ +#define T_NS 0x00800000 /* No Stop */ +#define T_LC 0x00800000 /* late collision */ +#define T_RL 0x00400000 /* retransmission limit */ +#define T_UN 0x00020000 /* underrun */ +#define T_CT 0x00010000 /* CTS lost */ +#define T_CSL 0x00010000 /* carrier sense lost */ +#define T_RC 0x003c0000 /* retry count */ + +/* Receive BD's status */ +#define R_E 0x80000000 /* buffer empty */ +#define R_W 0x20000000 /* wrap bit */ +#define R_I 0x10000000 /* interrupt on reception */ +#define R_L 0x08000000 /* last */ +#define R_C 0x08000000 /* the last byte in this buffer is a cntl + char */ +#define R_F 0x04000000 /* first */ +#define R_A 0x04000000 /* the first byte in this buffer is address + byte */ +#define R_CM 0x02000000 /* continuous mode */ +#define R_ID 0x01000000 /* buffer close on reception of idles */ +#define R_M 0x01000000 /* Frame received because of promiscuous + mode */ +#define R_AM 0x00800000 /* Address match */ +#define R_DE 0x00800000 /* Address match */ +#define R_LG 0x00200000 /* Break received */ +#define R_BR 0x00200000 /* Frame length violation */ +#define R_NO 0x00100000 /* Rx Non Octet Aligned Packet */ +#define R_FR 0x00100000 /* Framing Error (no stop bit) character + received */ +#define R_PR 0x00080000 /* Parity Error character received */ +#define R_AB 0x00080000 /* Frame Aborted */ +#define R_SH 0x00080000 /* frame is too short */ +#define R_CR 0x00040000 /* CRC Error */ +#define R_OV 0x00020000 /* Overrun */ +#define R_CD 0x00010000 /* CD lost */ +#define R_CL 0x00010000 /* this frame is closed because of a + collision */ + +/* Rx Data buffer must be 4 bytes aligned in most cases.*/ +#define UCC_SLOW_RX_ALIGN 4 +#define UCC_SLOW_MRBLR_ALIGNMENT 4 +#define UCC_SLOW_PRAM_SIZE 0x100 +#define ALIGNMENT_OF_UCC_SLOW_PRAM 64 + +/* UCC Slow Channel Protocol Mode */ +enum ucc_slow_channel_protocol_mode { + UCC_SLOW_CHANNEL_PROTOCOL_MODE_QMC = 0x00000002, + UCC_SLOW_CHANNEL_PROTOCOL_MODE_UART = 0x00000004, + UCC_SLOW_CHANNEL_PROTOCOL_MODE_BISYNC = 0x00000008, +}; + +/* UCC Slow Transparent Transmit CRC (TCRC) */ +enum ucc_slow_transparent_tcrc { + /* 16-bit CCITT CRC (HDLC). (X16 + X12 + X5 + 1) */ + UCC_SLOW_TRANSPARENT_TCRC_CCITT_CRC16 = 0x00000000, + /* CRC16 (BISYNC). (X16 + X15 + X2 + 1) */ + UCC_SLOW_TRANSPARENT_TCRC_CRC16 = 0x00004000, + /* 32-bit CCITT CRC (Ethernet and HDLC) */ + UCC_SLOW_TRANSPARENT_TCRC_CCITT_CRC32 = 0x00008000, +}; + +/* UCC Slow oversampling rate for transmitter (TDCR) */ +enum ucc_slow_tx_oversampling_rate { + /* 1x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_1 = 0x00000000, + /* 8x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_8 = 0x00010000, + /* 16x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_16 = 0x00020000, + /* 32x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_32 = 0x00030000, +}; + +/* UCC Slow Oversampling rate for receiver (RDCR) +*/ +enum ucc_slow_rx_oversampling_rate { + /* 1x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_1 = 0x00000000, + /* 8x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_8 = 0x00004000, + /* 16x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_16 = 0x00008000, + /* 32x clock mode */ + UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_32 = 0x0000c000, +}; + +/* UCC Slow Transmitter encoding method (TENC) +*/ +enum ucc_slow_tx_encoding_method { + UCC_SLOW_TRANSMITTER_ENCODING_METHOD_TENC_NRZ = 0x00000000, + UCC_SLOW_TRANSMITTER_ENCODING_METHOD_TENC_NRZI = 0x00000100 +}; + +/* UCC Slow Receiver decoding method (RENC) +*/ +enum ucc_slow_rx_decoding_method { + UCC_SLOW_RECEIVER_DECODING_METHOD_RENC_NRZ = 0x00000000, + UCC_SLOW_RECEIVER_DECODING_METHOD_RENC_NRZI = 0x00000800 +}; + +/* UCC Slow Diagnostic mode (DIAG) +*/ +enum ucc_slow_diag_mode { + UCC_SLOW_DIAG_MODE_NORMAL = 0x00000000, + UCC_SLOW_DIAG_MODE_LOOPBACK = 0x00000040, + UCC_SLOW_DIAG_MODE_ECHO = 0x00000080, + UCC_SLOW_DIAG_MODE_LOOPBACK_ECHO = 0x000000c0 +}; + +struct ucc_slow_info { + int ucc_num; + int protocol; /* QE_CR_PROTOCOL_xxx */ + enum qe_clock rx_clock; + enum qe_clock tx_clock; + phys_addr_t regs; + int irq; + u16 uccm_mask; + int data_mem_part; + int init_tx; + int init_rx; + u32 tx_bd_ring_len; + u32 rx_bd_ring_len; + int rx_interrupts; + int brkpt_support; + int grant_support; + int tsa; + int cdp; + int cds; + int ctsp; + int ctss; + int rinv; + int tinv; + int rtsm; + int rfw; + int tci; + int tend; + int tfl; + int txsy; + u16 max_rx_buf_length; + enum ucc_slow_transparent_tcrc tcrc; + enum ucc_slow_channel_protocol_mode mode; + enum ucc_slow_diag_mode diag; + enum ucc_slow_tx_oversampling_rate tdcr; + enum ucc_slow_rx_oversampling_rate rdcr; + enum ucc_slow_tx_encoding_method tenc; + enum ucc_slow_rx_decoding_method renc; +}; + +struct ucc_slow_private { + struct ucc_slow_info *us_info; + struct ucc_slow __iomem *us_regs; /* Ptr to memory map of UCC regs */ + struct ucc_slow_pram *us_pram; /* a pointer to the parameter RAM */ + u32 us_pram_offset; + int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ + int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ + int stopped_tx; /* Whether channel has been stopped for Tx + (STOP_TX, etc.) */ + int stopped_rx; /* Whether channel has been stopped for Rx */ + struct list_head confQ; /* frames passed to chip waiting for tx */ + u32 first_tx_bd_mask; /* mask is used in Tx routine to save status + and length for first BD in a frame */ + u32 tx_base_offset; /* first BD in Tx BD table offset (In MURAM) */ + u32 rx_base_offset; /* first BD in Rx BD table offset (In MURAM) */ + struct qe_bd *confBd; /* next BD for confirm after Tx */ + struct qe_bd *tx_bd; /* next BD for new Tx request */ + struct qe_bd *rx_bd; /* next BD to collect after Rx */ + void *p_rx_frame; /* accumulating receive frame */ + u16 *p_ucce; /* a pointer to the event register in memory. + */ + u16 *p_uccm; /* a pointer to the mask register in memory */ + u16 saved_uccm; /* a saved mask for the RX Interrupt bits */ +#ifdef STATISTICS + u32 tx_frames; /* Transmitted frames counters */ + u32 rx_frames; /* Received frames counters (only frames + passed to application) */ + u32 rx_discarded; /* Discarded frames counters (frames that + were discarded by the driver due to + errors) */ +#endif /* STATISTICS */ +}; + +/* ucc_slow_init + * Initializes Slow UCC according to provided parameters. + * + * us_info - (In) pointer to the slow UCC info structure. + * uccs_ret - (Out) pointer to the slow UCC structure. + */ +int ucc_slow_init(struct ucc_slow_info *us_info, + struct ucc_slow_private **uccs_ret); + +/* ucc_slow_free + * Frees all resources for slow UCC. + * + * uccs - (In) pointer to the slow UCC structure. + */ +void ucc_slow_free(struct ucc_slow_private *uccs); + +/* ucc_slow_enable + * Enables a fast UCC port. + * This routine enables Tx and/or Rx through the General UCC Mode Register. + * + * uccs - (In) pointer to the slow UCC structure. + * mode - (In) TX, RX, or both. + */ +void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode); + +/* ucc_slow_disable + * Disables a fast UCC port. + * This routine disables Tx and/or Rx through the General UCC Mode Register. + * + * uccs - (In) pointer to the slow UCC structure. + * mode - (In) TX, RX, or both. + */ +void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode); + +/* ucc_slow_poll_transmitter_now + * Immediately forces a poll of the transmitter for data to be sent. + * Typically, the hardware performs a periodic poll for data that the + * transmit routine has set up to be transmitted. In cases where + * this polling cycle is not soon enough, this optional routine can + * be invoked to force a poll right away, instead. Proper use for + * each transmission for which this functionality is desired is to + * call the transmit routine and then this routine right after. + * + * uccs - (In) pointer to the slow UCC structure. + */ +void ucc_slow_poll_transmitter_now(struct ucc_slow_private *uccs); + +/* ucc_slow_graceful_stop_tx + * Smoothly stops transmission on a specified slow UCC. + * + * uccs - (In) pointer to the slow UCC structure. + */ +void ucc_slow_graceful_stop_tx(struct ucc_slow_private *uccs); + +/* ucc_slow_stop_tx + * Stops transmission on a specified slow UCC. + * + * uccs - (In) pointer to the slow UCC structure. + */ +void ucc_slow_stop_tx(struct ucc_slow_private *uccs); + +/* ucc_slow_restart_tx + * Restarts transmitting on a specified slow UCC. + * + * uccs - (In) pointer to the slow UCC structure. + */ +void ucc_slow_restart_tx(struct ucc_slow_private *uccs); + +u32 ucc_slow_get_qe_cr_subblock(int uccs_num); + +#endif /* __UCC_SLOW_H__ */ -- cgit v0.10.2 From 2c20b5b7c68a45e70a56df7a9c197c06e5a81290 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Wed, 13 Aug 2014 17:29:18 +0800 Subject: qe_common: add qe common functions to qe_common.c qe need to call some common functions, move them into public directory, add a new file drivers/soc/qe/qe_common.c for them. Signed-off-by: Zhao Qiang --- upstream link: http://patchwork.ozlabs.org/patch/393169/ it is under discussion. Change-Id: Ib2b252f355921291b596d8ddc6bbe17aa53384b2 Reviewed-on: http://git.am.freescale.net:8181/16840 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/drivers/soc/qe/Makefile b/drivers/soc/qe/Makefile index f1855c1..77f6fd9 100644 --- a/drivers/soc/qe/Makefile +++ b/drivers/soc/qe/Makefile @@ -1,7 +1,7 @@ # # Makefile for the linux ppc-specific parts of QE # -obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o +obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o qe_common.o obj-$(CONFIG_UCC) += ucc.o obj-$(CONFIG_UCC_SLOW) += ucc_slow.o diff --git a/drivers/soc/qe/qe_common.c b/drivers/soc/qe/qe_common.c new file mode 100644 index 0000000..c82ddcc --- /dev/null +++ b/drivers/soc/qe/qe_common.c @@ -0,0 +1,185 @@ +/* + * Common QE code + * + * Author: Scott Wood + * + * Copyright 2007-2008,2010 Freescale Semiconductor, Inc. + * + * Some parts derived from commproc.c/cpm2_common.c, which is: + * Copyright (c) 1997 Dan error_act (dmalek@jlc.net) + * Copyright (c) 1999-2001 Dan Malek + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This program 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 +#include +#include +#include + +#include +#include +#include + +static spinlock_t qe_muram_lock; +static rh_block_t qe_boot_muram_rh_block[16]; +static rh_info_t qe_muram_info; +static u8 __iomem *muram_vbase; +static phys_addr_t muram_pbase; + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 + +int qe_muram_init(void) +{ + struct device_node *np; + struct resource r; + u32 zero[OF_MAX_ADDR_CELLS] = {}; + resource_size_t max = 0; + int i = 0; + int ret = 0; + + if (muram_pbase) + return 0; + + spin_lock_init(&qe_muram_lock); + /* initialize the info header */ + rh_init(&qe_muram_info, 1, + sizeof(qe_boot_muram_rh_block) / + sizeof(qe_boot_muram_rh_block[0]), + qe_boot_muram_rh_block); + + np = of_find_compatible_node(NULL, NULL, "fsl,qe-muram-data"); + if (!np) { + /* try legacy bindings */ + np = of_find_node_by_name(NULL, "data-only"); + if (!np) { + pr_err("Cannot find CPM muram data node"); + ret = -ENODEV; + goto out; + } + } + + muram_pbase = of_translate_address(np, zero); + if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) { + pr_err("Cannot translate zero through CPM muram node"); + ret = -ENODEV; + goto out; + } + + while (of_address_to_resource(np, i++, &r) == 0) { + if (r.end > max) + max = r.end; + + rh_attach_region(&qe_muram_info, r.start - muram_pbase, + resource_size(&r)); + } + + muram_vbase = ioremap(muram_pbase, max - muram_pbase + 1); + if (!muram_vbase) { + pr_err("Cannot map CPM muram"); + ret = -ENOMEM; + } + +out: + of_node_put(np); + return ret; +} + +/** + * qe_muram_alloc - allocate the requested size worth of multi-user ram + * @size: number of bytes to allocate + * @align: requested alignment, in bytes + * + * This function returns an offset into the muram area. + * Use qe_dpram_addr() to get the virtual address of the area. + * Use qe_muram_free() to free the allocation. + */ +unsigned long qe_muram_alloc(unsigned long size, unsigned long align) +{ + unsigned long start; + unsigned long flags; + + spin_lock_irqsave(&qe_muram_lock, flags); + qe_muram_info.alignment = align; + start = rh_alloc(&qe_muram_info, size, "commproc"); + memset(qe_muram_addr(start), 0, size); + spin_unlock_irqrestore(&qe_muram_lock, flags); + + return start; +} +EXPORT_SYMBOL(qe_muram_alloc); + +/** + * qe_muram_free - free a chunk of multi-user ram + * @offset: The beginning of the chunk as returned by qe_muram_alloc(). + */ +int qe_muram_free(unsigned long offset) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&qe_muram_lock, flags); + ret = rh_free(&qe_muram_info, offset); + spin_unlock_irqrestore(&qe_muram_lock, flags); + + return ret; +} +EXPORT_SYMBOL(qe_muram_free); + +/** + * qe_muram_alloc_fixed - reserve a specific region of multi-user ram + * @offset: the offset into the muram area to reserve + * @size: the number of bytes to reserve + * + * This function returns "start" on success, -ENOMEM on failure. + * Use qe_dpram_addr() to get the virtual address of the area. + * Use qe_muram_free() to free the allocation. + */ +unsigned long qe_muram_alloc_fixed(unsigned long offset, unsigned long size) +{ + unsigned long start; + unsigned long flags; + + spin_lock_irqsave(&qe_muram_lock, flags); + qe_muram_info.alignment = 1; + start = rh_alloc_fixed(&qe_muram_info, offset, size, "commproc"); + spin_unlock_irqrestore(&qe_muram_lock, flags); + + return start; +} +EXPORT_SYMBOL(qe_muram_alloc_fixed); + +/** + * qe_muram_addr - turn a muram offset into a virtual address + * @offset: muram offset to convert + */ +void __iomem *qe_muram_addr(unsigned long offset) +{ + return muram_vbase + offset; +} +EXPORT_SYMBOL(qe_muram_addr); + +unsigned long qe_muram_offset(void __iomem *addr) +{ + return addr - (void __iomem *)muram_vbase; +} +EXPORT_SYMBOL(qe_muram_offset); + +/** + * qe_muram_dma - turn a muram virtual address into a DMA address + * @offset: virtual address from qe_muram_addr() to convert + */ +dma_addr_t qe_muram_dma(void __iomem *addr) +{ + return muram_pbase + ((u8 __iomem *)addr - muram_vbase); +} +EXPORT_SYMBOL(qe_muram_dma); diff --git a/include/linux/fsl/qe.h b/include/linux/fsl/qe.h index c96ff9e..5a6a647 100644 --- a/include/linux/fsl/qe.h +++ b/include/linux/fsl/qe.h @@ -19,7 +19,8 @@ #include #include #include -#include +#include +#include #include #define QE_NUM_OF_SNUM 256 /* There are 256 serial number in QE */ @@ -190,13 +191,48 @@ static inline int qe_alive_during_sleep(void) #endif } -/* we actually use cpm_muram implementation, define this for convenience */ -#define qe_muram_init cpm_muram_init -#define qe_muram_alloc cpm_muram_alloc -#define qe_muram_alloc_fixed cpm_muram_alloc_fixed -#define qe_muram_free cpm_muram_free -#define qe_muram_addr cpm_muram_addr -#define qe_muram_offset cpm_muram_offset +int qe_muram_init(void); + +#if defined(CONFIG_QUICC_ENGINE) +unsigned long qe_muram_alloc(unsigned long size, unsigned long align); +int qe_muram_free(unsigned long offset); +unsigned long qe_muram_alloc_fixed(unsigned long offset, unsigned long size); +void __iomem *qe_muram_addr(unsigned long offset); +unsigned long qe_muram_offset(void __iomem *addr); +dma_addr_t qe_muram_dma(void __iomem *addr); +#else +static inline unsigned long qe_muram_alloc(unsigned long size, + unsigned long align) +{ + return -ENOSYS; +} + +static inline int qe_muram_free(unsigned long offset) +{ + return -ENOSYS; +} + +static inline unsigned long qe_muram_alloc_fixed(unsigned long offset, + unsigned long size) +{ + return -ENOSYS; +} + +static inline void __iomem *qe_muram_addr(unsigned long offset) +{ + return NULL; +} + +static inline unsigned long qe_muram_offset(void __iomem *addr) +{ + return -ENOSYS; +} + +static inline dma_addr_t qe_muram_dma(void __iomem *addr) +{ + return 0; +} +#endif /* defined(CONFIG_QUICC_ENGINE) */ /* Structure that defines QE firmware binary files. * -- cgit v0.10.2 From 7ba5c48b5faf6fbd16e61263dfd4a380e3902569 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Tue, 30 Sep 2014 10:25:25 +0800 Subject: rheap: move rheap.c from arch/powerpc/lib/ to lib/ qe need to use the rheap, so move it to public directory. Signed-off-by: Zhao Qiang --- upstream link: http://patchwork.ozlabs.org/patch/393170/ it is under discussion. Change-Id: Ied2765d6e0eb3b7ade0fef02cfe226c8a8566c5f Reviewed-on: http://git.am.freescale.net:8181/16841 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index d20dc2b..18b658e 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1046,7 +1046,4 @@ config PPC_CLOCK default n select HAVE_CLK -config PPC_LIB_RHEAP - bool - source "arch/powerpc/kvm/Kconfig" diff --git a/arch/powerpc/include/asm/fsl_85xx_cache_sram.h b/arch/powerpc/include/asm/fsl_85xx_cache_sram.h index 2af2bdc..5071a2c 100644 --- a/arch/powerpc/include/asm/fsl_85xx_cache_sram.h +++ b/arch/powerpc/include/asm/fsl_85xx_cache_sram.h @@ -26,7 +26,7 @@ #ifndef __ASM_POWERPC_FSL_85XX_CACHE_SRAM_H__ #define __ASM_POWERPC_FSL_85XX_CACHE_SRAM_H__ -#include +#include #include /* @@ -37,7 +37,7 @@ struct mpc85xx_cache_sram { phys_addr_t base_phys; void *base_virt; unsigned int size; - rh_info_t *rh; + struct _rh_info *rh; spinlock_t lock; }; diff --git a/arch/powerpc/include/asm/rheap.h b/arch/powerpc/include/asm/rheap.h deleted file mode 100644 index 1723817..0000000 --- a/arch/powerpc/include/asm/rheap.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * include/asm-ppc/rheap.h - * - * Header file for the implementation of a remote heap. - * - * Author: Pantelis Antoniou - * - * 2004 (c) INTRACOM S.A. Greece. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifndef __ASM_PPC_RHEAP_H__ -#define __ASM_PPC_RHEAP_H__ - -#include - -typedef struct _rh_block { - struct list_head list; - unsigned long start; - int size; - const char *owner; -} rh_block_t; - -typedef struct _rh_info { - unsigned int alignment; - int max_blocks; - int empty_slots; - rh_block_t *block; - struct list_head empty_list; - struct list_head free_list; - struct list_head taken_list; - unsigned int flags; -} rh_info_t; - -#define RHIF_STATIC_INFO 0x1 -#define RHIF_STATIC_BLOCK 0x2 - -typedef struct _rh_stats { - unsigned long start; - int size; - const char *owner; -} rh_stats_t; - -#define RHGS_FREE 0 -#define RHGS_TAKEN 1 - -/* Create a remote heap dynamically */ -extern rh_info_t *rh_create(unsigned int alignment); - -/* Destroy a remote heap, created by rh_create() */ -extern void rh_destroy(rh_info_t * info); - -/* Initialize in place a remote info block */ -extern void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks, - rh_block_t * block); - -/* Attach a free region to manage */ -extern int rh_attach_region(rh_info_t * info, unsigned long start, int size); - -/* Detach a free region */ -extern unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size); - -/* Allocate the given size from the remote heap (with alignment) */ -extern unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, - const char *owner); - -/* Allocate the given size from the remote heap */ -extern unsigned long rh_alloc(rh_info_t * info, int size, const char *owner); - -/* Allocate the given size from the given address */ -extern unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, - const char *owner); - -/* Free the allocated area */ -extern int rh_free(rh_info_t * info, unsigned long start); - -/* Get stats for debugging purposes */ -extern int rh_get_stats(rh_info_t * info, int what, int max_stats, - rh_stats_t * stats); - -/* Simple dump of remote heap info */ -extern void rh_dump(rh_info_t * info); - -/* Set owner of taken block */ -extern int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner); - -#endif /* __ASM_PPC_RHEAP_H__ */ diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 4504332..c7b4e2f 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -26,8 +26,6 @@ obj-$(CONFIG_SMP) += locks.o obj-$(CONFIG_ALTIVEC) += vmx-helper.o endif -obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o - obj-y += code-patching.o obj-y += feature-fixups.o obj-$(CONFIG_FTR_FIXUP_SELFTEST) += feature-fixups-test.o diff --git a/arch/powerpc/lib/rheap.c b/arch/powerpc/lib/rheap.c deleted file mode 100644 index a1060a8..0000000 --- a/arch/powerpc/lib/rheap.c +++ /dev/null @@ -1,747 +0,0 @@ -/* - * A Remote Heap. Remote means that we don't touch the memory that the - * heap points to. Normal heap implementations use the memory they manage - * to place their list. We cannot do that because the memory we manage may - * have special properties, for example it is uncachable or of different - * endianess. - * - * Author: Pantelis Antoniou - * - * 2004 (c) INTRACOM S.A. Greece. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * Fixup a list_head, needed when copying lists. If the pointers fall - * between s and e, apply the delta. This assumes that - * sizeof(struct list_head *) == sizeof(unsigned long *). - */ -static inline void fixup(unsigned long s, unsigned long e, int d, - struct list_head *l) -{ - unsigned long *pp; - - pp = (unsigned long *)&l->next; - if (*pp >= s && *pp < e) - *pp += d; - - pp = (unsigned long *)&l->prev; - if (*pp >= s && *pp < e) - *pp += d; -} - -/* Grow the allocated blocks */ -static int grow(rh_info_t * info, int max_blocks) -{ - rh_block_t *block, *blk; - int i, new_blocks; - int delta; - unsigned long blks, blke; - - if (max_blocks <= info->max_blocks) - return -EINVAL; - - new_blocks = max_blocks - info->max_blocks; - - block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_ATOMIC); - if (block == NULL) - return -ENOMEM; - - if (info->max_blocks > 0) { - - /* copy old block area */ - memcpy(block, info->block, - sizeof(rh_block_t) * info->max_blocks); - - delta = (char *)block - (char *)info->block; - - /* and fixup list pointers */ - blks = (unsigned long)info->block; - blke = (unsigned long)(info->block + info->max_blocks); - - for (i = 0, blk = block; i < info->max_blocks; i++, blk++) - fixup(blks, blke, delta, &blk->list); - - fixup(blks, blke, delta, &info->empty_list); - fixup(blks, blke, delta, &info->free_list); - fixup(blks, blke, delta, &info->taken_list); - - /* free the old allocated memory */ - if ((info->flags & RHIF_STATIC_BLOCK) == 0) - kfree(info->block); - } - - info->block = block; - info->empty_slots += new_blocks; - info->max_blocks = max_blocks; - info->flags &= ~RHIF_STATIC_BLOCK; - - /* add all new blocks to the free list */ - blk = block + info->max_blocks - new_blocks; - for (i = 0; i < new_blocks; i++, blk++) - list_add(&blk->list, &info->empty_list); - - return 0; -} - -/* - * Assure at least the required amount of empty slots. If this function - * causes a grow in the block area then all pointers kept to the block - * area are invalid! - */ -static int assure_empty(rh_info_t * info, int slots) -{ - int max_blocks; - - /* This function is not meant to be used to grow uncontrollably */ - if (slots >= 4) - return -EINVAL; - - /* Enough space */ - if (info->empty_slots >= slots) - return 0; - - /* Next 16 sized block */ - max_blocks = ((info->max_blocks + slots) + 15) & ~15; - - return grow(info, max_blocks); -} - -static rh_block_t *get_slot(rh_info_t * info) -{ - rh_block_t *blk; - - /* If no more free slots, and failure to extend. */ - /* XXX: You should have called assure_empty before */ - if (info->empty_slots == 0) { - printk(KERN_ERR "rh: out of slots; crash is imminent.\n"); - return NULL; - } - - /* Get empty slot to use */ - blk = list_entry(info->empty_list.next, rh_block_t, list); - list_del_init(&blk->list); - info->empty_slots--; - - /* Initialize */ - blk->start = 0; - blk->size = 0; - blk->owner = NULL; - - return blk; -} - -static inline void release_slot(rh_info_t * info, rh_block_t * blk) -{ - list_add(&blk->list, &info->empty_list); - info->empty_slots++; -} - -static void attach_free_block(rh_info_t * info, rh_block_t * blkn) -{ - rh_block_t *blk; - rh_block_t *before; - rh_block_t *after; - rh_block_t *next; - int size; - unsigned long s, e, bs, be; - struct list_head *l; - - /* We assume that they are aligned properly */ - size = blkn->size; - s = blkn->start; - e = s + size; - - /* Find the blocks immediately before and after the given one - * (if any) */ - before = NULL; - after = NULL; - next = NULL; - - list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); - - bs = blk->start; - be = bs + blk->size; - - if (next == NULL && s >= bs) - next = blk; - - if (be == s) - before = blk; - - if (e == bs) - after = blk; - - /* If both are not null, break now */ - if (before != NULL && after != NULL) - break; - } - - /* Now check if they are really adjacent */ - if (before && s != (before->start + before->size)) - before = NULL; - - if (after && e != after->start) - after = NULL; - - /* No coalescing; list insert and return */ - if (before == NULL && after == NULL) { - - if (next != NULL) - list_add(&blkn->list, &next->list); - else - list_add(&blkn->list, &info->free_list); - - return; - } - - /* We don't need it anymore */ - release_slot(info, blkn); - - /* Grow the before block */ - if (before != NULL && after == NULL) { - before->size += size; - return; - } - - /* Grow the after block backwards */ - if (before == NULL && after != NULL) { - after->start -= size; - after->size += size; - return; - } - - /* Grow the before block, and release the after block */ - before->size += size + after->size; - list_del(&after->list); - release_slot(info, after); -} - -static void attach_taken_block(rh_info_t * info, rh_block_t * blkn) -{ - rh_block_t *blk; - struct list_head *l; - - /* Find the block immediately before the given one (if any) */ - list_for_each(l, &info->taken_list) { - blk = list_entry(l, rh_block_t, list); - if (blk->start > blkn->start) { - list_add_tail(&blkn->list, &blk->list); - return; - } - } - - list_add_tail(&blkn->list, &info->taken_list); -} - -/* - * Create a remote heap dynamically. Note that no memory for the blocks - * are allocated. It will upon the first allocation - */ -rh_info_t *rh_create(unsigned int alignment) -{ - rh_info_t *info; - - /* Alignment must be a power of two */ - if ((alignment & (alignment - 1)) != 0) - return ERR_PTR(-EINVAL); - - info = kmalloc(sizeof(*info), GFP_ATOMIC); - if (info == NULL) - return ERR_PTR(-ENOMEM); - - info->alignment = alignment; - - /* Initially everything as empty */ - info->block = NULL; - info->max_blocks = 0; - info->empty_slots = 0; - info->flags = 0; - - INIT_LIST_HEAD(&info->empty_list); - INIT_LIST_HEAD(&info->free_list); - INIT_LIST_HEAD(&info->taken_list); - - return info; -} -EXPORT_SYMBOL_GPL(rh_create); - -/* - * Destroy a dynamically created remote heap. Deallocate only if the areas - * are not static - */ -void rh_destroy(rh_info_t * info) -{ - if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL) - kfree(info->block); - - if ((info->flags & RHIF_STATIC_INFO) == 0) - kfree(info); -} -EXPORT_SYMBOL_GPL(rh_destroy); - -/* - * Initialize in place a remote heap info block. This is needed to support - * operation very early in the startup of the kernel, when it is not yet safe - * to call kmalloc. - */ -void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks, - rh_block_t * block) -{ - int i; - rh_block_t *blk; - - /* Alignment must be a power of two */ - if ((alignment & (alignment - 1)) != 0) - return; - - info->alignment = alignment; - - /* Initially everything as empty */ - info->block = block; - info->max_blocks = max_blocks; - info->empty_slots = max_blocks; - info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK; - - INIT_LIST_HEAD(&info->empty_list); - INIT_LIST_HEAD(&info->free_list); - INIT_LIST_HEAD(&info->taken_list); - - /* Add all new blocks to the free list */ - for (i = 0, blk = block; i < max_blocks; i++, blk++) - list_add(&blk->list, &info->empty_list); -} -EXPORT_SYMBOL_GPL(rh_init); - -/* Attach a free memory region, coalesces regions if adjuscent */ -int rh_attach_region(rh_info_t * info, unsigned long start, int size) -{ - rh_block_t *blk; - unsigned long s, e, m; - int r; - - /* The region must be aligned */ - s = start; - e = s + size; - m = info->alignment - 1; - - /* Round start up */ - s = (s + m) & ~m; - - /* Round end down */ - e = e & ~m; - - if (IS_ERR_VALUE(e) || (e < s)) - return -ERANGE; - - /* Take final values */ - start = s; - size = e - s; - - /* Grow the blocks, if needed */ - r = assure_empty(info, 1); - if (r < 0) - return r; - - blk = get_slot(info); - blk->start = start; - blk->size = size; - blk->owner = NULL; - - attach_free_block(info, blk); - - return 0; -} -EXPORT_SYMBOL_GPL(rh_attach_region); - -/* Detatch given address range, splits free block if needed. */ -unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size) -{ - struct list_head *l; - rh_block_t *blk, *newblk; - unsigned long s, e, m, bs, be; - - /* Validate size */ - if (size <= 0) - return (unsigned long) -EINVAL; - - /* The region must be aligned */ - s = start; - e = s + size; - m = info->alignment - 1; - - /* Round start up */ - s = (s + m) & ~m; - - /* Round end down */ - e = e & ~m; - - if (assure_empty(info, 1) < 0) - return (unsigned long) -ENOMEM; - - blk = NULL; - list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); - /* The range must lie entirely inside one free block */ - bs = blk->start; - be = blk->start + blk->size; - if (s >= bs && e <= be) - break; - blk = NULL; - } - - if (blk == NULL) - return (unsigned long) -ENOMEM; - - /* Perfect fit */ - if (bs == s && be == e) { - /* Delete from free list, release slot */ - list_del(&blk->list); - release_slot(info, blk); - return s; - } - - /* blk still in free list, with updated start and/or size */ - if (bs == s || be == e) { - if (bs == s) - blk->start += size; - blk->size -= size; - - } else { - /* The front free fragment */ - blk->size = s - bs; - - /* the back free fragment */ - newblk = get_slot(info); - newblk->start = e; - newblk->size = be - e; - - list_add(&newblk->list, &blk->list); - } - - return s; -} -EXPORT_SYMBOL_GPL(rh_detach_region); - -/* Allocate a block of memory at the specified alignment. The value returned - * is an offset into the buffer initialized by rh_init(), or a negative number - * if there is an error. - */ -unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner) -{ - struct list_head *l; - rh_block_t *blk; - rh_block_t *newblk; - unsigned long start, sp_size; - - /* Validate size, and alignment must be power of two */ - if (size <= 0 || (alignment & (alignment - 1)) != 0) - return (unsigned long) -EINVAL; - - /* Align to configured alignment */ - size = (size + (info->alignment - 1)) & ~(info->alignment - 1); - - if (assure_empty(info, 2) < 0) - return (unsigned long) -ENOMEM; - - blk = NULL; - list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); - if (size <= blk->size) { - start = (blk->start + alignment - 1) & ~(alignment - 1); - if (start + size <= blk->start + blk->size) - break; - } - blk = NULL; - } - - if (blk == NULL) - return (unsigned long) -ENOMEM; - - /* Just fits */ - if (blk->size == size) { - /* Move from free list to taken list */ - list_del(&blk->list); - newblk = blk; - } else { - /* Fragment caused, split if needed */ - /* Create block for fragment in the beginning */ - sp_size = start - blk->start; - if (sp_size) { - rh_block_t *spblk; - - spblk = get_slot(info); - spblk->start = blk->start; - spblk->size = sp_size; - /* add before the blk */ - list_add(&spblk->list, blk->list.prev); - } - newblk = get_slot(info); - newblk->start = start; - newblk->size = size; - - /* blk still in free list, with updated start and size - * for fragment in the end */ - blk->start = start + size; - blk->size -= sp_size + size; - /* No fragment in the end, remove blk */ - if (blk->size == 0) { - list_del(&blk->list); - release_slot(info, blk); - } - } - - newblk->owner = owner; - attach_taken_block(info, newblk); - - return start; -} -EXPORT_SYMBOL_GPL(rh_alloc_align); - -/* Allocate a block of memory at the default alignment. The value returned is - * an offset into the buffer initialized by rh_init(), or a negative number if - * there is an error. - */ -unsigned long rh_alloc(rh_info_t * info, int size, const char *owner) -{ - return rh_alloc_align(info, size, info->alignment, owner); -} -EXPORT_SYMBOL_GPL(rh_alloc); - -/* Allocate a block of memory at the given offset, rounded up to the default - * alignment. The value returned is an offset into the buffer initialized by - * rh_init(), or a negative number if there is an error. - */ -unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner) -{ - struct list_head *l; - rh_block_t *blk, *newblk1, *newblk2; - unsigned long s, e, m, bs = 0, be = 0; - - /* Validate size */ - if (size <= 0) - return (unsigned long) -EINVAL; - - /* The region must be aligned */ - s = start; - e = s + size; - m = info->alignment - 1; - - /* Round start up */ - s = (s + m) & ~m; - - /* Round end down */ - e = e & ~m; - - if (assure_empty(info, 2) < 0) - return (unsigned long) -ENOMEM; - - blk = NULL; - list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); - /* The range must lie entirely inside one free block */ - bs = blk->start; - be = blk->start + blk->size; - if (s >= bs && e <= be) - break; - blk = NULL; - } - - if (blk == NULL) - return (unsigned long) -ENOMEM; - - /* Perfect fit */ - if (bs == s && be == e) { - /* Move from free list to taken list */ - list_del(&blk->list); - blk->owner = owner; - - start = blk->start; - attach_taken_block(info, blk); - - return start; - - } - - /* blk still in free list, with updated start and/or size */ - if (bs == s || be == e) { - if (bs == s) - blk->start += size; - blk->size -= size; - - } else { - /* The front free fragment */ - blk->size = s - bs; - - /* The back free fragment */ - newblk2 = get_slot(info); - newblk2->start = e; - newblk2->size = be - e; - - list_add(&newblk2->list, &blk->list); - } - - newblk1 = get_slot(info); - newblk1->start = s; - newblk1->size = e - s; - newblk1->owner = owner; - - start = newblk1->start; - attach_taken_block(info, newblk1); - - return start; -} -EXPORT_SYMBOL_GPL(rh_alloc_fixed); - -/* Deallocate the memory previously allocated by one of the rh_alloc functions. - * The return value is the size of the deallocated block, or a negative number - * if there is an error. - */ -int rh_free(rh_info_t * info, unsigned long start) -{ - rh_block_t *blk, *blk2; - struct list_head *l; - int size; - - /* Linear search for block */ - blk = NULL; - list_for_each(l, &info->taken_list) { - blk2 = list_entry(l, rh_block_t, list); - if (start < blk2->start) - break; - blk = blk2; - } - - if (blk == NULL || start > (blk->start + blk->size)) - return -EINVAL; - - /* Remove from taken list */ - list_del(&blk->list); - - /* Get size of freed block */ - size = blk->size; - attach_free_block(info, blk); - - return size; -} -EXPORT_SYMBOL_GPL(rh_free); - -int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats) -{ - rh_block_t *blk; - struct list_head *l; - struct list_head *h; - int nr; - - switch (what) { - - case RHGS_FREE: - h = &info->free_list; - break; - - case RHGS_TAKEN: - h = &info->taken_list; - break; - - default: - return -EINVAL; - } - - /* Linear search for block */ - nr = 0; - list_for_each(l, h) { - blk = list_entry(l, rh_block_t, list); - if (stats != NULL && nr < max_stats) { - stats->start = blk->start; - stats->size = blk->size; - stats->owner = blk->owner; - stats++; - } - nr++; - } - - return nr; -} -EXPORT_SYMBOL_GPL(rh_get_stats); - -int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner) -{ - rh_block_t *blk, *blk2; - struct list_head *l; - int size; - - /* Linear search for block */ - blk = NULL; - list_for_each(l, &info->taken_list) { - blk2 = list_entry(l, rh_block_t, list); - if (start < blk2->start) - break; - blk = blk2; - } - - if (blk == NULL || start > (blk->start + blk->size)) - return -EINVAL; - - blk->owner = owner; - size = blk->size; - - return size; -} -EXPORT_SYMBOL_GPL(rh_set_owner); - -void rh_dump(rh_info_t * info) -{ - static rh_stats_t st[32]; /* XXX maximum 32 blocks */ - int maxnr; - int i, nr; - - maxnr = ARRAY_SIZE(st); - - printk(KERN_INFO - "info @0x%p (%d slots empty / %d max)\n", - info, info->empty_slots, info->max_blocks); - - printk(KERN_INFO " Free:\n"); - nr = rh_get_stats(info, RHGS_FREE, maxnr, st); - if (nr > maxnr) - nr = maxnr; - for (i = 0; i < nr; i++) - printk(KERN_INFO - " 0x%lx-0x%lx (%u)\n", - st[i].start, st[i].start + st[i].size, - st[i].size); - printk(KERN_INFO "\n"); - - printk(KERN_INFO " Taken:\n"); - nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st); - if (nr > maxnr) - nr = maxnr; - for (i = 0; i < nr; i++) - printk(KERN_INFO - " 0x%lx-0x%lx (%u) %s\n", - st[i].start, st[i].start + st[i].size, - st[i].size, st[i].owner != NULL ? st[i].owner : ""); - printk(KERN_INFO "\n"); -} -EXPORT_SYMBOL_GPL(rh_dump); - -void rh_dump_blk(rh_info_t * info, rh_block_t * blk) -{ - printk(KERN_INFO - "blk @0x%p: 0x%lx-0x%lx (%u)\n", - blk, blk->start, blk->start + blk->size, blk->size); -} -EXPORT_SYMBOL_GPL(rh_dump_blk); - diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index d6c7506..0ea1aee 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -254,7 +254,7 @@ config PPC4xx_GPIO config PPC4xx_OCM bool "PPC4xx On Chip Memory (OCM) support" depends on 4xx - select PPC_LIB_RHEAP + select LIB_RHEAP help Enable OCM support for PowerPC 4xx platforms with on chip memory, OCM provides the fast place for memory access to improve performance. diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index ae9fdb51..ab4777a 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -19,7 +19,7 @@ if PPC32 config FSL_85XX_CACHE_SRAM bool "Freescale l2cache sram support" - select PPC_LIB_RHEAP + select LIB_RHEAP help When selected, this option enables cache-sram support for memory allocation on P1/P2 QorIQ platforms. diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index d09ae32f..9c38a8d 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -282,7 +282,7 @@ config CPM2 bool "Enable support for the CPM2 (Communications Processor Module)" depends on (FSL_SOC_BOOKE && PPC32) || 8260 select CPM - select PPC_LIB_RHEAP + select LIB_RHEAP select PPC_PCI_CHOICE select ARCH_REQUIRE_GPIOLIB help diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 1211e3b..1d7281d 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -33,7 +33,7 @@ config PPC_8xx bool "Freescale 8xx" select FSL_SOC select 8xx - select PPC_LIB_RHEAP + select LIB_RHEAP config 40x bool "AMCC 40x" diff --git a/arch/powerpc/sysdev/cpm1.c b/arch/powerpc/sysdev/cpm1.c index 5e6ff38..c6f5762 100644 --- a/arch/powerpc/sysdev/cpm1.c +++ b/arch/powerpc/sysdev/cpm1.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/powerpc/sysdev/cpm2.c b/arch/powerpc/sysdev/cpm2.c index 8dc1e24..5a63d35 100644 --- a/arch/powerpc/sysdev/cpm2.c +++ b/arch/powerpc/sysdev/cpm2.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index 4dd5341..0a98dca 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -65,8 +65,8 @@ void __init udbg_init_cpm(void) #endif static spinlock_t cpm_muram_lock; -static rh_block_t cpm_boot_muram_rh_block[16]; -static rh_info_t cpm_muram_info; +static struct _rh_block cpm_boot_muram_rh_block[16]; +static struct _rh_info cpm_muram_info; static u8 __iomem *muram_vbase; static phys_addr_t muram_pbase; diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c index 1b15f93..6ae237a 100644 --- a/arch/powerpc/sysdev/ppc4xx_ocm.c +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,7 +49,7 @@ struct ocm_region { int memtotal; int memfree; - rh_info_t *rh; + struct _rh_info *rh; struct list_head list; }; diff --git a/drivers/dma/bestcomm/Kconfig b/drivers/dma/bestcomm/Kconfig index 29e4270..9bb1bf8 100644 --- a/drivers/dma/bestcomm/Kconfig +++ b/drivers/dma/bestcomm/Kconfig @@ -6,7 +6,7 @@ config PPC_BESTCOMM tristate "Bestcomm DMA engine support" depends on PPC_MPC52xx default n - select PPC_LIB_RHEAP + select LIB_RHEAP help BestComm is the name of the communication coprocessor found on the Freescale MPC5200 family of processor. Its usage is diff --git a/drivers/soc/qe/Kconfig b/drivers/soc/qe/Kconfig index 5c2b9d8..49118e1 100644 --- a/drivers/soc/qe/Kconfig +++ b/drivers/soc/qe/Kconfig @@ -5,7 +5,7 @@ config QUICC_ENGINE bool "Freescale QUICC Engine (QE) Support" depends on FSL_SOC && (PPC32 || PPC64) - select PPC_LIB_RHEAP + select LIB_RHEAP select CRC32 ---help--- The QUICC Engine (QE) is a new generation of communications diff --git a/drivers/soc/qe/qe.c b/drivers/soc/qe/qe.c index 25f0e0d..e0926f5 100644 --- a/drivers/soc/qe/qe.c +++ b/drivers/soc/qe/qe.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include static void qe_snums_init(void); static int qe_sdma_init(void); diff --git a/drivers/soc/qe/qe_common.c b/drivers/soc/qe/qe_common.c index c82ddcc..8e93b88 100644 --- a/drivers/soc/qe/qe_common.c +++ b/drivers/soc/qe/qe_common.c @@ -26,12 +26,12 @@ #include #include -#include +#include #include static spinlock_t qe_muram_lock; -static rh_block_t qe_boot_muram_rh_block[16]; -static rh_info_t qe_muram_info; +static struct _rh_block qe_boot_muram_rh_block[16]; +static struct _rh_info qe_muram_info; static u8 __iomem *muram_vbase; static phys_addr_t muram_pbase; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2a46153..4ce7204 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1972,7 +1972,7 @@ config FB_FSL_DIU select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select PPC_LIB_RHEAP + select LIB_RHEAP ---help--- Framebuffer driver for the Freescale SoC DIU diff --git a/include/linux/fsl/bestcomm/sram.h b/include/linux/fsl/bestcomm/sram.h index b6d6689..8555dab 100644 --- a/include/linux/fsl/bestcomm/sram.h +++ b/include/linux/fsl/bestcomm/sram.h @@ -12,7 +12,7 @@ #ifndef __BESTCOMM_SRAM_H__ #define __BESTCOMM_SRAM_H__ -#include +#include #include #include @@ -25,7 +25,7 @@ struct bcom_sram { phys_addr_t base_phys; void *base_virt; unsigned int size; - rh_info_t *rh; + struct _rh_info *rh; spinlock_t lock; }; diff --git a/include/linux/fsl/rheap.h b/include/linux/fsl/rheap.h new file mode 100644 index 0000000..88149da --- /dev/null +++ b/include/linux/fsl/rheap.h @@ -0,0 +1,93 @@ +/* + * include/asm-ppc/rheap.h + * + * Header file for the implementation of a remote heap. + * + * Author: Pantelis Antoniou + * + * 2004 (c) INTRACOM S.A. Greece. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __ASM_PPC_RHEAP_H__ +#define __ASM_PPC_RHEAP_H__ + +#include + +struct _rh_block { + struct list_head list; + unsigned long start; + int size; + const char *owner; +}; + +struct _rh_info { + unsigned int alignment; + int max_blocks; + int empty_slots; + struct _rh_block *block; + struct list_head empty_list; + struct list_head free_list; + struct list_head taken_list; + unsigned int flags; +}; + +#define RHIF_STATIC_INFO 0x1 +#define RHIF_STATIC_BLOCK 0x2 + +struct _rh_stats { + unsigned long start; + int size; + const char *owner; +}; + +#define RHGS_FREE 0 +#define RHGS_TAKEN 1 + +/* Create a remote heap dynamically */ +extern struct _rh_info *rh_create(unsigned int alignment); + +/* Destroy a remote heap, created by rh_create() */ +extern void rh_destroy(struct _rh_info *info); + +/* Initialize in place a remote info block */ +extern void rh_init(struct _rh_info *info, unsigned int alignment, + int max_blocks, struct _rh_block *block); + +/* Attach a free region to manage */ +extern int rh_attach_region(struct _rh_info *info, unsigned long start, + int size); + +/* Detach a free region */ +extern unsigned long rh_detach_region(struct _rh_info *info, + unsigned long start, int size); + +/* Allocate the given size from the remote heap (with alignment) */ +extern unsigned long rh_alloc_align(struct _rh_info *info, int size, + int alignment, const char *owner); + +/* Allocate the given size from the remote heap */ +extern unsigned long rh_alloc(struct _rh_info *info, int size, + const char *owner); + +/* Allocate the given size from the given address */ +extern unsigned long rh_alloc_fixed(struct _rh_info *info, unsigned long start, + int size, const char *owner); + +/* Free the allocated area */ +extern int rh_free(struct _rh_info *info, unsigned long start); + +/* Get stats for debugging purposes */ +extern int rh_get_stats(struct _rh_info *info, int what, int max_stats, + struct _rh_stats *stats); + +/* Simple dump of remote heap info */ +extern void rh_dump(struct _rh_info *info); + +/* Set owner of taken block */ +extern int rh_set_owner(struct _rh_info *info, unsigned long start, + const char *owner); + +#endif /* __ASM_PPC_RHEAP_H__ */ diff --git a/lib/Kconfig b/lib/Kconfig index 4490089..4ffcc86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -365,6 +365,9 @@ config CPU_RMAP bool depends on SMP +config LIB_RHEAP + bool + config DQL bool diff --git a/lib/Makefile b/lib/Makefile index 6e23a0f..8523e9d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -194,3 +194,5 @@ quiet_cmd_build_OID_registry = GEN $@ clean-files += oid_registry_data.c obj-$(CONFIG_UCS2_STRING) += ucs2_string.o + +obj-$(CONFIG_LIB_RHEAP) += rheap.o diff --git a/lib/rheap.c b/lib/rheap.c new file mode 100644 index 0000000..241cc0a --- /dev/null +++ b/lib/rheap.c @@ -0,0 +1,747 @@ +/* + * A Remote Heap. Remote means that we don't touch the memory that the + * heap points to. Normal heap implementations use the memory they manage + * to place their list. We cannot do that because the memory we manage may + * have special properties, for example it is uncachable or of different + * endianess. + * + * Author: Pantelis Antoniou + * + * 2004 (c) INTRACOM S.A. Greece. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Fixup a list_head, needed when copying lists. If the pointers fall + * between s and e, apply the delta. This assumes that + * sizeof(struct list_head *) == sizeof(unsigned long *). + */ +static inline void fixup(unsigned long s, unsigned long e, int d, + struct list_head *l) +{ + unsigned long *pp; + + pp = (unsigned long *)&l->next; + if (*pp >= s && *pp < e) + *pp += d; + + pp = (unsigned long *)&l->prev; + if (*pp >= s && *pp < e) + *pp += d; +} + +/* Grow the allocated blocks */ +static int grow(struct _rh_info *info, int max_blocks) +{ + struct _rh_block *block, *blk; + int i, new_blocks; + int delta; + unsigned long blks, blke; + + if (max_blocks <= info->max_blocks) + return -EINVAL; + + new_blocks = max_blocks - info->max_blocks; + + block = kmalloc(sizeof(struct _rh_block) * max_blocks, GFP_ATOMIC); + if (block == NULL) + return -ENOMEM; + + if (info->max_blocks > 0) { + + /* copy old block area */ + memcpy(block, info->block, + sizeof(struct _rh_block) * info->max_blocks); + + delta = (char *)block - (char *)info->block; + + /* and fixup list pointers */ + blks = (unsigned long)info->block; + blke = (unsigned long)(info->block + info->max_blocks); + + for (i = 0, blk = block; i < info->max_blocks; i++, blk++) + fixup(blks, blke, delta, &blk->list); + + fixup(blks, blke, delta, &info->empty_list); + fixup(blks, blke, delta, &info->free_list); + fixup(blks, blke, delta, &info->taken_list); + + /* free the old allocated memory */ + if ((info->flags & RHIF_STATIC_BLOCK) == 0) + kfree(info->block); + } + + info->block = block; + info->empty_slots += new_blocks; + info->max_blocks = max_blocks; + info->flags &= ~RHIF_STATIC_BLOCK; + + /* add all new blocks to the free list */ + blk = block + info->max_blocks - new_blocks; + for (i = 0; i < new_blocks; i++, blk++) + list_add(&blk->list, &info->empty_list); + + return 0; +} + +/* + * Assure at least the required amount of empty slots. If this function + * causes a grow in the block area then all pointers kept to the block + * area are invalid! + */ +static int assure_empty(struct _rh_info *info, int slots) +{ + int max_blocks; + + /* This function is not meant to be used to grow uncontrollably */ + if (slots >= 4) + return -EINVAL; + + /* Enough space */ + if (info->empty_slots >= slots) + return 0; + + /* Next 16 sized block */ + max_blocks = ((info->max_blocks + slots) + 15) & ~15; + + return grow(info, max_blocks); +} + +static struct _rh_block *get_slot(struct _rh_info *info) +{ + struct _rh_block *blk; + + /* If no more free slots, and failure to extend. */ + /* XXX: You should have called assure_empty before */ + if (info->empty_slots == 0) { + pr_err("rh: out of slots; crash is imminent.\n"); + return NULL; + } + + /* Get empty slot to use */ + blk = list_entry(info->empty_list.next, struct _rh_block, list); + list_del_init(&blk->list); + info->empty_slots--; + + /* Initialize */ + blk->start = 0; + blk->size = 0; + blk->owner = NULL; + + return blk; +} + +static inline void release_slot(struct _rh_info *info, struct _rh_block *blk) +{ + list_add(&blk->list, &info->empty_list); + info->empty_slots++; +} + +static void attach_free_block(struct _rh_info *info, struct _rh_block *blkn) +{ + struct _rh_block *blk; + struct _rh_block *before; + struct _rh_block *after; + struct _rh_block *next; + int size; + unsigned long s, e, bs, be; + struct list_head *l; + + /* We assume that they are aligned properly */ + size = blkn->size; + s = blkn->start; + e = s + size; + + /* Find the blocks immediately before and after the given one + * (if any) */ + before = NULL; + after = NULL; + next = NULL; + + list_for_each(l, &info->free_list) { + blk = list_entry(l, struct _rh_block, list); + + bs = blk->start; + be = bs + blk->size; + + if (next == NULL && s >= bs) + next = blk; + + if (be == s) + before = blk; + + if (e == bs) + after = blk; + + /* If both are not null, break now */ + if (before != NULL && after != NULL) + break; + } + + /* Now check if they are really adjacent */ + if (before && s != (before->start + before->size)) + before = NULL; + + if (after && e != after->start) + after = NULL; + + /* No coalescing; list insert and return */ + if (before == NULL && after == NULL) { + + if (next != NULL) + list_add(&blkn->list, &next->list); + else + list_add(&blkn->list, &info->free_list); + + return; + } + + /* We don't need it anymore */ + release_slot(info, blkn); + + /* Grow the before block */ + if (before != NULL && after == NULL) { + before->size += size; + return; + } + + /* Grow the after block backwards */ + if (before == NULL && after != NULL) { + after->start -= size; + after->size += size; + return; + } + + /* Grow the before block, and release the after block */ + before->size += size + after->size; + list_del(&after->list); + release_slot(info, after); +} + +static void attach_taken_block(struct _rh_info *info, struct _rh_block *blkn) +{ + struct _rh_block *blk; + struct list_head *l; + + /* Find the block immediately before the given one (if any) */ + list_for_each(l, &info->taken_list) { + blk = list_entry(l, struct _rh_block, list); + if (blk->start > blkn->start) { + list_add_tail(&blkn->list, &blk->list); + return; + } + } + + list_add_tail(&blkn->list, &info->taken_list); +} + +/* + * Create a remote heap dynamically. Note that no memory for the blocks + * are allocated. It will upon the first allocation + */ +struct _rh_info *rh_create(unsigned int alignment) +{ + struct _rh_info *info; + + /* Alignment must be a power of two */ + if ((alignment & (alignment - 1)) != 0) + return ERR_PTR(-EINVAL); + + info = kmalloc(sizeof(*info), GFP_ATOMIC); + if (info == NULL) + return ERR_PTR(-ENOMEM); + + info->alignment = alignment; + + /* Initially everything as empty */ + info->block = NULL; + info->max_blocks = 0; + info->empty_slots = 0; + info->flags = 0; + + INIT_LIST_HEAD(&info->empty_list); + INIT_LIST_HEAD(&info->free_list); + INIT_LIST_HEAD(&info->taken_list); + + return info; +} +EXPORT_SYMBOL_GPL(rh_create); + +/* + * Destroy a dynamically created remote heap. Deallocate only if the areas + * are not static + */ +void rh_destroy(struct _rh_info *info) +{ + if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL) + kfree(info->block); + + if ((info->flags & RHIF_STATIC_INFO) == 0) + kfree(info); +} +EXPORT_SYMBOL_GPL(rh_destroy); + +/* + * Initialize in place a remote heap info block. This is needed to support + * operation very early in the startup of the kernel, when it is not yet safe + * to call kmalloc. + */ +void rh_init(struct _rh_info *info, unsigned int alignment, int max_blocks, + struct _rh_block *block) +{ + int i; + struct _rh_block *blk; + + /* Alignment must be a power of two */ + if ((alignment & (alignment - 1)) != 0) + return; + + info->alignment = alignment; + + /* Initially everything as empty */ + info->block = block; + info->max_blocks = max_blocks; + info->empty_slots = max_blocks; + info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK; + + INIT_LIST_HEAD(&info->empty_list); + INIT_LIST_HEAD(&info->free_list); + INIT_LIST_HEAD(&info->taken_list); + + /* Add all new blocks to the free list */ + for (i = 0, blk = block; i < max_blocks; i++, blk++) + list_add(&blk->list, &info->empty_list); +} +EXPORT_SYMBOL_GPL(rh_init); + +/* Attach a free memory region, coalesces regions if adjuscent */ +int rh_attach_region(struct _rh_info *info, unsigned long start, int size) +{ + struct _rh_block *blk; + unsigned long s, e, m; + int r; + + /* The region must be aligned */ + s = start; + e = s + size; + m = info->alignment - 1; + + /* Round start up */ + s = (s + m) & ~m; + + /* Round end down */ + e = e & ~m; + + if (IS_ERR_VALUE(e) || (e < s)) + return -ERANGE; + + /* Take final values */ + start = s; + size = e - s; + + /* Grow the blocks, if needed */ + r = assure_empty(info, 1); + if (r < 0) + return r; + + blk = get_slot(info); + blk->start = start; + blk->size = size; + blk->owner = NULL; + + attach_free_block(info, blk); + + return 0; +} +EXPORT_SYMBOL_GPL(rh_attach_region); + +/* Detatch given address range, splits free block if needed. */ +unsigned long rh_detach_region(struct _rh_info *info, unsigned long start, + int size) +{ + struct list_head *l; + struct _rh_block *blk, *newblk; + unsigned long s, e, m, bs, be; + + /* Validate size */ + if (size <= 0) + return (unsigned long) -EINVAL; + + /* The region must be aligned */ + s = start; + e = s + size; + m = info->alignment - 1; + + /* Round start up */ + s = (s + m) & ~m; + + /* Round end down */ + e = e & ~m; + + if (assure_empty(info, 1) < 0) + return (unsigned long) -ENOMEM; + + blk = NULL; + list_for_each(l, &info->free_list) { + blk = list_entry(l, struct _rh_block, list); + /* The range must lie entirely inside one free block */ + bs = blk->start; + be = blk->start + blk->size; + if (s >= bs && e <= be) + break; + blk = NULL; + } + + if (blk == NULL) + return (unsigned long) -ENOMEM; + + /* Perfect fit */ + if (bs == s && be == e) { + /* Delete from free list, release slot */ + list_del(&blk->list); + release_slot(info, blk); + return s; + } + + /* blk still in free list, with updated start and/or size */ + if (bs == s || be == e) { + if (bs == s) + blk->start += size; + blk->size -= size; + + } else { + /* The front free fragment */ + blk->size = s - bs; + + /* the back free fragment */ + newblk = get_slot(info); + newblk->start = e; + newblk->size = be - e; + + list_add(&newblk->list, &blk->list); + } + + return s; +} +EXPORT_SYMBOL_GPL(rh_detach_region); + +/* Allocate a block of memory at the specified alignment. The value returned + * is an offset into the buffer initialized by rh_init(), or a negative number + * if there is an error. + */ +unsigned long rh_alloc_align(struct _rh_info *info, int size, + int alignment, const char *owner) +{ + struct list_head *l; + struct _rh_block *blk; + struct _rh_block *newblk; + unsigned long start, sp_size; + + /* Validate size, and alignment must be power of two */ + if (size <= 0 || (alignment & (alignment - 1)) != 0) + return (unsigned long) -EINVAL; + + /* Align to configured alignment */ + size = (size + (info->alignment - 1)) & ~(info->alignment - 1); + + if (assure_empty(info, 2) < 0) + return (unsigned long) -ENOMEM; + + blk = NULL; + list_for_each(l, &info->free_list) { + blk = list_entry(l, struct _rh_block, list); + if (size <= blk->size) { + start = (blk->start + alignment - 1) & ~(alignment - 1); + if (start + size <= blk->start + blk->size) + break; + } + blk = NULL; + } + + if (blk == NULL) + return (unsigned long) -ENOMEM; + + /* Just fits */ + if (blk->size == size) { + /* Move from free list to taken list */ + list_del(&blk->list); + newblk = blk; + } else { + /* Fragment caused, split if needed */ + /* Create block for fragment in the beginning */ + sp_size = start - blk->start; + if (sp_size) { + struct _rh_block *spblk; + + spblk = get_slot(info); + spblk->start = blk->start; + spblk->size = sp_size; + /* add before the blk */ + list_add(&spblk->list, blk->list.prev); + } + newblk = get_slot(info); + newblk->start = start; + newblk->size = size; + + /* blk still in free list, with updated start and size + * for fragment in the end */ + blk->start = start + size; + blk->size -= sp_size + size; + /* No fragment in the end, remove blk */ + if (blk->size == 0) { + list_del(&blk->list); + release_slot(info, blk); + } + } + + newblk->owner = owner; + attach_taken_block(info, newblk); + + return start; +} +EXPORT_SYMBOL_GPL(rh_alloc_align); + +/* Allocate a block of memory at the default alignment. The value returned is + * an offset into the buffer initialized by rh_init(), or a negative number if + * there is an error. + */ +unsigned long rh_alloc(struct _rh_info *info, int size, const char *owner) +{ + return rh_alloc_align(info, size, info->alignment, owner); +} +EXPORT_SYMBOL_GPL(rh_alloc); + +/* Allocate a block of memory at the given offset, rounded up to the default + * alignment. The value returned is an offset into the buffer initialized by + * rh_init(), or a negative number if there is an error. + */ +unsigned long rh_alloc_fixed(struct _rh_info *info, unsigned long start, + int size, const char *owner) +{ + struct list_head *l; + struct _rh_block *blk, *newblk1, *newblk2; + unsigned long s, e, m, bs = 0, be = 0; + + /* Validate size */ + if (size <= 0) + return (unsigned long) -EINVAL; + + /* The region must be aligned */ + s = start; + e = s + size; + m = info->alignment - 1; + + /* Round start up */ + s = (s + m) & ~m; + + /* Round end down */ + e = e & ~m; + + if (assure_empty(info, 2) < 0) + return (unsigned long) -ENOMEM; + + blk = NULL; + list_for_each(l, &info->free_list) { + blk = list_entry(l, struct _rh_block, list); + /* The range must lie entirely inside one free block */ + bs = blk->start; + be = blk->start + blk->size; + if (s >= bs && e <= be) + break; + blk = NULL; + } + + if (blk == NULL) + return (unsigned long) -ENOMEM; + + /* Perfect fit */ + if (bs == s && be == e) { + /* Move from free list to taken list */ + list_del(&blk->list); + blk->owner = owner; + + start = blk->start; + attach_taken_block(info, blk); + + return start; + + } + + /* blk still in free list, with updated start and/or size */ + if (bs == s || be == e) { + if (bs == s) + blk->start += size; + blk->size -= size; + + } else { + /* The front free fragment */ + blk->size = s - bs; + + /* The back free fragment */ + newblk2 = get_slot(info); + newblk2->start = e; + newblk2->size = be - e; + + list_add(&newblk2->list, &blk->list); + } + + newblk1 = get_slot(info); + newblk1->start = s; + newblk1->size = e - s; + newblk1->owner = owner; + + start = newblk1->start; + attach_taken_block(info, newblk1); + + return start; +} +EXPORT_SYMBOL_GPL(rh_alloc_fixed); + +/* Deallocate the memory previously allocated by one of the rh_alloc functions. + * The return value is the size of the deallocated block, or a negative number + * if there is an error. + */ +int rh_free(struct _rh_info *info, unsigned long start) +{ + struct _rh_block *blk, *blk2; + struct list_head *l; + int size; + + /* Linear search for block */ + blk = NULL; + list_for_each(l, &info->taken_list) { + blk2 = list_entry(l, struct _rh_block, list); + if (start < blk2->start) + break; + blk = blk2; + } + + if (blk == NULL || start > (blk->start + blk->size)) + return -EINVAL; + + /* Remove from taken list */ + list_del(&blk->list); + + /* Get size of freed block */ + size = blk->size; + attach_free_block(info, blk); + + return size; +} +EXPORT_SYMBOL_GPL(rh_free); + +int rh_get_stats(struct _rh_info *info, int what, int max_stats, + struct _rh_stats *stats) +{ + struct _rh_block *blk; + struct list_head *l; + struct list_head *h; + int nr; + + switch (what) { + + case RHGS_FREE: + h = &info->free_list; + break; + + case RHGS_TAKEN: + h = &info->taken_list; + break; + + default: + return -EINVAL; + } + + /* Linear search for block */ + nr = 0; + list_for_each(l, h) { + blk = list_entry(l, struct _rh_block, list); + if (stats != NULL && nr < max_stats) { + stats->start = blk->start; + stats->size = blk->size; + stats->owner = blk->owner; + stats++; + } + nr++; + } + + return nr; +} +EXPORT_SYMBOL_GPL(rh_get_stats); + +int rh_set_owner(struct _rh_info *info, unsigned long start, const char *owner) +{ + struct _rh_block *blk, *blk2; + struct list_head *l; + int size; + + /* Linear search for block */ + blk = NULL; + list_for_each(l, &info->taken_list) { + blk2 = list_entry(l, struct _rh_block, list); + if (start < blk2->start) + break; + blk = blk2; + } + + if (blk == NULL || start > (blk->start + blk->size)) + return -EINVAL; + + blk->owner = owner; + size = blk->size; + + return size; +} +EXPORT_SYMBOL_GPL(rh_set_owner); + +void rh_dump(struct _rh_info *info) +{ + static struct _rh_stats st[32]; /* XXX maximum 32 blocks */ + int maxnr; + int i, nr; + + maxnr = ARRAY_SIZE(st); + + pr_info("info @0x%p (%d slots empty / %d max)\n", + info, info->empty_slots, info->max_blocks); + + pr_info(" Free:\n"); + nr = rh_get_stats(info, RHGS_FREE, maxnr, st); + if (nr > maxnr) + nr = maxnr; + for (i = 0; i < nr; i++) + pr_info(" 0x%lx-0x%lx (%u)\n", + st[i].start, st[i].start + st[i].size, + st[i].size); + pr_info("\n"); + + pr_info(" Taken:\n"); + nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st); + if (nr > maxnr) + nr = maxnr; + for (i = 0; i < nr; i++) + pr_info(" 0x%lx-0x%lx (%u) %s\n", + st[i].start, st[i].start + st[i].size, + st[i].size, st[i].owner != NULL ? st[i].owner : ""); + pr_info("\n"); +} +EXPORT_SYMBOL_GPL(rh_dump); + +void rh_dump_blk(struct _rh_info *info, struct _rh_block *blk) +{ + pr_info("blk @0x%p: 0x%lx-0x%lx (%u)\n", + blk, blk->start, blk->start + blk->size, blk->size); +} +EXPORT_SYMBOL_GPL(rh_dump_blk); + -- cgit v0.10.2 From 36396c5901f1067d25d9ada16acbd36edb716759 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Fri, 10 Oct 2014 10:38:48 +0800 Subject: qe-uart: modify qe-uart to adapt both powerpc and arm qe has been supported by arm board ls1021, qe-uart need to be supported by ls1021. modify the code to make qe-uart can work on both powerpc and ls1021. Signed-off-by: Zhao Qiang --- upstream link: http://patchwork.ozlabs.org/patch/398471/ it is under discussion. Change-Id: I07a9a091882cd572330b38e7a6e0632aea9a9042 Reviewed-on: http://git.am.freescale.net:8181/21119 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h index dff714d..a932f99 100644 --- a/arch/arm/include/asm/delay.h +++ b/arch/arm/include/asm/delay.h @@ -57,6 +57,22 @@ extern void __bad_udelay(void); __const_udelay((n) * UDELAY_MULT)) : \ __udelay(n)) +#define spin_event_timeout(condition, timeout, delay) \ +({ \ + typeof(condition) __ret; \ + int i = 0; \ + while (!(__ret = (condition)) && (i++ < timeout)) { \ + if (delay) \ + udelay(delay); \ + else \ + cpu_relax(); \ + udelay(1); \ + } \ + if (!__ret) \ + __ret = (condition); \ + __ret; \ +}) + /* Loop-based definitions for assembly code. */ extern void __loop_delay(unsigned long loops); extern void __loop_udelay(unsigned long usecs); diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index d070741..4bec694 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -206,6 +206,34 @@ extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); #endif #endif +/* access ports */ +#define setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr)) +#define clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr)) + +#define setbits16(_addr, _v) iowrite16be(ioread16be(_addr) | (_v), (_addr)) +#define clrbits16(_addr, _v) iowrite16be(ioread16be(_addr) & ~(_v), (_addr)) + +#define setbits8(_addr, _v) iowrite8(ioread8(_addr) | (_v), (_addr)) +#define clrbits8(_addr, _v) iowrite8(ioread8(_addr) & ~(_v), (_addr)) + +/* Clear and set bits in one shot. These macros can be used to clear and + * set multiple bits in a register using a single read-modify-write. These + * macros can also be used to set a multiple-bit bit pattern using a mask, + * by specifying the mask in the 'clear' parameter and the new bit pattern + * in the 'set' parameter. + */ + +#define clrsetbits_be32(addr, clear, set) \ + iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_le32(addr, clear, set) \ + iowrite32le((ioread32le(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_be16(addr, clear, set) \ + iowrite16be((ioread16be(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_le16(addr, clear, set) \ + iowrite16le((ioread16le(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_8(addr, clear, set) \ + iowrite8((ioread8(addr) & ~(clear)) | (set), (addr)) + /* * IO port access primitives * ------------------------- diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h index 53c15de..4358904 100644 --- a/arch/arm/include/asm/irq.h +++ b/arch/arm/include/asm/irq.h @@ -30,6 +30,8 @@ extern void asm_do_IRQ(unsigned int, struct pt_regs *); void handle_IRQ(unsigned int, struct pt_regs *); void init_IRQ(void); +extern irq_hw_number_t virq_to_hw(unsigned int virq); + #ifdef CONFIG_MULTI_IRQ_HANDLER extern void (*handle_arch_irq)(struct pt_regs *); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 9723d17..afa204a 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -121,6 +121,13 @@ void __init init_IRQ(void) machine_desc->init_irq(); } +irq_hw_number_t virq_to_hw(unsigned int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + return WARN_ON(!irq_data) ? 0 : irq_data->hwirq; +} +EXPORT_SYMBOL_GPL(virq_to_hw); + #ifdef CONFIG_MULTI_IRQ_HANDLER void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { diff --git a/drivers/soc/qe/Kconfig b/drivers/soc/qe/Kconfig index 49118e1..43b984b 100644 --- a/drivers/soc/qe/Kconfig +++ b/drivers/soc/qe/Kconfig @@ -4,7 +4,6 @@ config QUICC_ENGINE bool "Freescale QUICC Engine (QE) Support" - depends on FSL_SOC && (PPC32 || PPC64) select LIB_RHEAP select CRC32 ---help--- diff --git a/drivers/soc/qe/qe.c b/drivers/soc/qe/qe.c index e0926f5..2aaa5b2 100644 --- a/drivers/soc/qe/qe.c +++ b/drivers/soc/qe/qe.c @@ -74,8 +74,8 @@ static phys_addr_t qebase = -1; phys_addr_t get_qe_base(void) { struct device_node *qe; - int size; - const u32 *prop; + int ret; + struct resource res; if (qebase != -1) return qebase; @@ -87,9 +87,9 @@ phys_addr_t get_qe_base(void) return qebase; } - prop = of_get_property(qe, "reg", &size); - if (prop && size >= sizeof(*prop)) - qebase = of_translate_address(qe, prop); + ret = of_address_to_resource(qe, 0, &res); + if (!ret) + qebase = res.start; of_node_put(qe); return qebase; @@ -121,7 +121,7 @@ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) spin_lock_irqsave(&qe_lock, flags); if (cmd == QE_RESET) { - out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG)); + iowrite32be((u32) (cmd | QE_CR_FLG), &qe_immr->cp.cecr); } else { if (cmd == QE_ASSIGN_PAGE) { /* Here device is the SNUM, not sub-block */ @@ -138,15 +138,14 @@ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) mcn_shift = QE_CR_MCN_NORMAL_SHIFT; } - out_be32(&qe_immr->cp.cecdr, cmd_input); - out_be32(&qe_immr->cp.cecr, - (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32) - mcn_protocol << mcn_shift)); + iowrite32be(cmd_input, &qe_immr->cp.cecdr); + iowrite32be((cmd | QE_CR_FLG | ((u32) device << dev_shift) | + (u32)mcn_protocol << mcn_shift), &qe_immr->cp.cecr); } /* wait for the QE_CR_FLG to clear */ - ret = spin_event_timeout((in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) == 0, - 100, 0); + ret = spin_event_timeout((ioread32be(&qe_immr->cp.cecr) + & QE_CR_FLG) == 0, 100, 0); /* On timeout (e.g. failure), the expression will be false (ret == 0), otherwise it will be true (ret == 1). */ spin_unlock_irqrestore(&qe_lock, flags); @@ -170,8 +169,8 @@ static unsigned int brg_clk; unsigned int qe_get_brg_clk(void) { struct device_node *qe; - int size; - const u32 *prop; + u32 val; + int ret; if (brg_clk) return brg_clk; @@ -183,9 +182,9 @@ unsigned int qe_get_brg_clk(void) return brg_clk; } - prop = of_get_property(qe, "brg-frequency", &size); - if (prop && size == sizeof(*prop)) - brg_clk = *prop; + ret = of_property_read_u32_index(qe, "brg-frequency", 0, &val); + if (!ret) + brg_clk = val; of_node_put(qe); @@ -225,7 +224,7 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier) tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE | div16; - out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval); + iowrite32be(tempval, &qe_immr->brg.brgc[brg - QE_BRG1]); return 0; } @@ -365,9 +364,9 @@ static int qe_sdma_init(void) return -ENOMEM; } - out_be32(&sdma->sdebcr, (u32) sdma_buf_offset & QE_SDEBCR_BA_MASK); - out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK | - (0x1 << QE_SDMR_CEN_SHIFT))); + iowrite32be((u32) sdma_buf_offset & QE_SDEBCR_BA_MASK, &sdma->sdebcr); + iowrite32be((QE_SDMR_GLB_1_MSK | (0x1 << QE_SDMR_CEN_SHIFT)), + &sdma->sdmr); return 0; } @@ -403,14 +402,14 @@ static void qe_upload_microcode(const void *base, pr_info("qe-FM: uploading microcode '%s'\n", ucode->id); /* Use auto-increment */ - out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | - QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); + iowrite32be(be32_to_cpu(ucode->iram_offset) | QE_IRAM_IADD_AIE | + QE_IRAM_IADD_BADDR, &qe_immr->iram.iadd); for (i = 0; i < be32_to_cpu(ucode->count); i++) - out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); + iowrite32be(be32_to_cpu(code[i]), &qe_immr->iram.idata); /* Set I-RAM Ready Register */ - out_be32(&qe_immr->iram.iready, be32_to_cpu(QE_IRAM_READY)); + iowrite32be(be32_to_cpu(QE_IRAM_READY), &qe_immr->iram.iready); } /* @@ -528,11 +527,11 @@ int qe_upload_firmware(const struct qe_firmware *firmware) u32 trap = be32_to_cpu(ucode->traps[j]); if (trap) - out_be32(&qe_immr->rsp[i].tibcr[j], trap); + iowrite32be(trap, &qe_immr->rsp[i].tibcr[j]); } /* Enable traps */ - out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); + iowrite32be(be32_to_cpu(ucode->eccr), &qe_immr->rsp[i].eccr); } qe_firmware_uploaded = 1; @@ -651,9 +650,9 @@ EXPORT_SYMBOL(qe_get_num_of_risc); unsigned int qe_get_num_of_snums(void) { struct device_node *qe; - int size; unsigned int num_of_snums; - const u32 *prop; + u32 val; + int ret; num_of_snums = 28; /* The default number of snum for threads is 28 */ qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); @@ -667,9 +666,9 @@ unsigned int qe_get_num_of_snums(void) return num_of_snums; } - prop = of_get_property(qe, "fsl,qe-num-snums", &size); - if (prop && size == sizeof(*prop)) { - num_of_snums = *prop; + ret = of_property_read_u32_index(qe, "fsl,qe-num-snums", 0, &val); + if (!ret) { + num_of_snums = val; if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) { /* No QE ever has fewer than 28 SNUMs */ pr_err("QE: number of snum is invalid\n"); diff --git a/drivers/soc/qe/qe_common.c b/drivers/soc/qe/qe_common.c index 8e93b88..6bc9b18 100644 --- a/drivers/soc/qe/qe_common.c +++ b/drivers/soc/qe/qe_common.c @@ -68,7 +68,7 @@ int qe_muram_init(void) } } - muram_pbase = of_translate_address(np, zero); + muram_pbase = (phys_addr_t)of_translate_address(np, zero); if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) { pr_err("Cannot translate zero through CPM muram node"); ret = -ENODEV; diff --git a/drivers/soc/qe/qe_ic.c b/drivers/soc/qe/qe_ic.c index 1968f22..cc1b8d5 100644 --- a/drivers/soc/qe/qe_ic.c +++ b/drivers/soc/qe/qe_ic.c @@ -16,7 +16,10 @@ #include #include +#include #include +#include +#include #include #include #include @@ -177,13 +180,13 @@ static struct qe_ic_info qe_ic_info[] = { static inline u32 qe_ic_read(__be32 __iomem *base, unsigned int reg) { - return in_be32(base + (reg >> 2)); + return ioread32be(base + (reg >> 2)); } static inline void qe_ic_write(__be32 __iomem *base, unsigned int reg, u32 value) { - out_be32(base + (reg >> 2), value); + iowrite32be(value, base + (reg >> 2)); } static inline struct qe_ic *qe_ic_from_irq(unsigned int virq) diff --git a/drivers/soc/qe/qe_io.c b/drivers/soc/qe/qe_io.c index 939e903..7f40d3c 100644 --- a/drivers/soc/qe/qe_io.c +++ b/drivers/soc/qe/qe_io.c @@ -24,7 +24,6 @@ #include #include #include -#include #undef DEBUG @@ -62,16 +61,16 @@ void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, pin_mask1bit = (u32) (1 << (QE_PIO_PINS - (pin + 1))); /* Set open drain, if required */ - tmp_val = in_be32(&par_io->cpodr); + tmp_val = ioread32be(&par_io->cpodr); if (open_drain) - out_be32(&par_io->cpodr, pin_mask1bit | tmp_val); + iowrite32be(pin_mask1bit | tmp_val, &par_io->cpodr); else - out_be32(&par_io->cpodr, ~pin_mask1bit & tmp_val); + iowrite32be(~pin_mask1bit & tmp_val, &par_io->cpodr); /* define direction */ tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? - in_be32(&par_io->cpdir2) : - in_be32(&par_io->cpdir1); + ioread32be(&par_io->cpdir2) : + ioread32be(&par_io->cpdir1); /* get all bits mask for 2 bit per port */ pin_mask2bits = (u32) (0x3 << (QE_PIO_PINS - @@ -83,34 +82,30 @@ void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, /* clear and set 2 bits mask */ if (pin > (QE_PIO_PINS / 2) - 1) { - out_be32(&par_io->cpdir2, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cpdir2); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cpdir2, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cpdir2); } else { - out_be32(&par_io->cpdir1, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cpdir1); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cpdir1, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cpdir1); } /* define pin assignment */ tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? - in_be32(&par_io->cppar2) : - in_be32(&par_io->cppar1); + ioread32be(&par_io->cppar2) : + ioread32be(&par_io->cppar1); new_mask2bits = (u32) (assignment << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2)); /* clear and set 2 bits mask */ if (pin > (QE_PIO_PINS / 2) - 1) { - out_be32(&par_io->cppar2, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cppar2); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cppar2, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cppar2); } else { - out_be32(&par_io->cppar1, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cppar1); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cppar1, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cppar1); } } EXPORT_SYMBOL(__par_io_config_pin); @@ -138,12 +133,12 @@ int par_io_data_set(u8 port, u8 pin, u8 val) /* calculate pin location */ pin_mask = (u32) (1 << (QE_PIO_PINS - 1 - pin)); - tmp_val = in_be32(&par_io[port].cpdata); + tmp_val = ioread32be(&par_io[port].cpdata); if (val == 0) /* clear */ - out_be32(&par_io[port].cpdata, ~pin_mask & tmp_val); + iowrite32be(~pin_mask & tmp_val, &par_io[port].cpdata); else /* set */ - out_be32(&par_io[port].cpdata, pin_mask | tmp_val); + iowrite32be(pin_mask | tmp_val, &par_io[port].cpdata); return 0; } @@ -200,17 +195,17 @@ static void dump_par_io(void) pr_info("%s: par_io=%p\n", __func__, par_io); for (i = 0; i < num_par_io_ports; i++) { pr_info(" cpodr[%u]=%08x\n", i, - in_be32(&par_io[i].cpodr)); + ioread32be(&par_io[i].cpodr)); pr_info(" cpdata[%u]=%08x\n", i, - in_be32(&par_io[i].cpdata)); + ioread32be(&par_io[i].cpdata)); pr_info(" cpdir1[%u]=%08x\n", i, - in_be32(&par_io[i].cpdir1)); + ioread32be(&par_io[i].cpdir1)); pr_info(" cpdir2[%u]=%08x\n", i, - in_be32(&par_io[i].cpdir2)); + ioread32be(&par_io[i].cpdir2)); pr_info(" cppar1[%u]=%08x\n", i, - in_be32(&par_io[i].cppar1)); + ioread32be(&par_io[i].cppar1)); pr_info(" cppar2[%u]=%08x\n", i, - in_be32(&par_io[i].cppar2)); + ioread32be(&par_io[i].cppar2)); } } EXPORT_SYMBOL(dump_par_io); diff --git a/drivers/soc/qe/ucc_slow.c b/drivers/soc/qe/ucc_slow.c index d023f19..edb1b2e 100644 --- a/drivers/soc/qe/ucc_slow.c +++ b/drivers/soc/qe/ucc_slow.c @@ -46,7 +46,7 @@ EXPORT_SYMBOL(ucc_slow_get_qe_cr_subblock); void ucc_slow_poll_transmitter_now(struct ucc_slow_private *uccs) { - out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD); + iowrite16be(UCC_SLOW_TOD, &uccs->us_regs->utodr); } void ucc_slow_graceful_stop_tx(struct ucc_slow_private *uccs) @@ -88,7 +88,7 @@ void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode) us_regs = uccs->us_regs; /* Enable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); + gumr_l = ioread32be(&us_regs->gumr_l); if (mode & COMM_DIR_TX) { gumr_l |= UCC_SLOW_GUMR_L_ENT; uccs->enabled_tx = 1; @@ -97,7 +97,7 @@ void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode) gumr_l |= UCC_SLOW_GUMR_L_ENR; uccs->enabled_rx = 1; } - out_be32(&us_regs->gumr_l, gumr_l); + iowrite32be(gumr_l, &us_regs->gumr_l); } EXPORT_SYMBOL(ucc_slow_enable); @@ -109,7 +109,7 @@ void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode) us_regs = uccs->us_regs; /* Disable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); + gumr_l = ioread32be(&us_regs->gumr_l); if (mode & COMM_DIR_TX) { gumr_l &= ~UCC_SLOW_GUMR_L_ENT; uccs->enabled_tx = 0; @@ -118,7 +118,7 @@ void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode) gumr_l &= ~UCC_SLOW_GUMR_L_ENR; uccs->enabled_rx = 0; } - out_be32(&us_regs->gumr_l, gumr_l); + iowrite32be(gumr_l, &us_regs->gumr_l); } EXPORT_SYMBOL(ucc_slow_disable); @@ -209,7 +209,7 @@ int ucc_slow_init(struct ucc_slow_info *us_info, return ret; } - out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length); + iowrite16be(us_info->max_rx_buf_length, &uccs->us_pram->mrblr); INIT_LIST_HEAD(&uccs->confQ); @@ -239,27 +239,27 @@ int ucc_slow_init(struct ucc_slow_info *us_info, bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs->tx_base_offset); for (i = 0; i < us_info->tx_bd_ring_len - 1; i++) { /* clear bd buffer */ - out_be32(&bd->buf, 0); + iowrite32be(0, &bd->buf); /* set bd status and length */ - out_be32((u32 *) bd, 0); + iowrite32be(0, (u32 *) bd); bd++; } /* for last BD set Wrap bit */ - out_be32(&bd->buf, 0); - out_be32((u32 *) bd, cpu_to_be32(T_W)); + iowrite32be(0, &bd->buf); + iowrite32be(T_W, (u32 *) bd); /* Init Rx bds */ bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset); for (i = 0; i < us_info->rx_bd_ring_len - 1; i++) { /* set bd status and length */ - out_be32((u32 *)bd, 0); + iowrite32be(0, (u32 *)bd); /* clear bd buffer */ - out_be32(&bd->buf, 0); + iowrite32be(0, &bd->buf); bd++; } /* for last BD set Wrap bit */ - out_be32((u32 *)bd, cpu_to_be32(R_W)); - out_be32(&bd->buf, 0); + iowrite32be(R_W, (u32 *)bd); + iowrite32be(0, &bd->buf); /* Set GUMR (For more details see the hardware spec.). */ /* gumr_h */ @@ -280,7 +280,7 @@ int ucc_slow_init(struct ucc_slow_info *us_info, gumr |= UCC_SLOW_GUMR_H_TXSY; if (us_info->rtsm) gumr |= UCC_SLOW_GUMR_H_RTSM; - out_be32(&us_regs->gumr_h, gumr); + iowrite32be(gumr, &us_regs->gumr_h); /* gumr_l */ gumr = us_info->tdcr | us_info->rdcr | us_info->tenc | us_info->renc | @@ -293,7 +293,7 @@ int ucc_slow_init(struct ucc_slow_info *us_info, gumr |= UCC_SLOW_GUMR_L_TINV; if (us_info->tend) gumr |= UCC_SLOW_GUMR_L_TEND; - out_be32(&us_regs->gumr_l, gumr); + iowrite32be(gumr, &us_regs->gumr_l); /* Function code registers */ @@ -303,8 +303,8 @@ int ucc_slow_init(struct ucc_slow_info *us_info, uccs->us_pram->rbmr = UCC_BMR_BO_BE; /* rbase, tbase are offsets from MURAM base */ - out_be16(&uccs->us_pram->rbase, uccs->rx_base_offset); - out_be16(&uccs->us_pram->tbase, uccs->tx_base_offset); + iowrite16be(uccs->rx_base_offset, &uccs->us_pram->rbase); + iowrite16be(uccs->tx_base_offset, &uccs->us_pram->tbase); /* Mux clocking */ /* Grant Support */ @@ -334,14 +334,14 @@ int ucc_slow_init(struct ucc_slow_info *us_info, } /* Set interrupt mask register at UCC level. */ - out_be16(&us_regs->uccm, us_info->uccm_mask); + iowrite16be(us_info->uccm_mask, &us_regs->uccm); /* First, clear anything pending at UCC level, * otherwise, old garbage may come through * as soon as the dam is opened. */ /* Writing '1' clears */ - out_be16(&us_regs->ucce, 0xffff); + iowrite16be(0xffff, &us_regs->ucce); /* Issue QE Init command */ if (us_info->init_tx && us_info->init_rx) diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 4718ebe..2c9a87c 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -26,13 +26,13 @@ #include #include #include +#include #include #include #include #include -#include /* * The GUMR flag for Soft UART. This would normally be defined in qe.h, @@ -257,11 +257,11 @@ static unsigned int qe_uart_tx_empty(struct uart_port *port) struct qe_bd *bdp = qe_port->tx_bd_base; while (1) { - if (in_be16(&bdp->status) & BD_SC_READY) + if (ioread16be(&bdp->status) & BD_SC_READY) /* This BD is not done, so return "not done" */ return 0; - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) /* * This BD is done and it's the last one, so return * "done" @@ -339,13 +339,13 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - p = qe2cpu_addr(bdp->buf, qe_port); + p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); *p++ = port->x_char; - out_be16(&bdp->length, 1); + iowrite16be(1, &bdp->length); setbits16(&bdp->status, BD_SC_READY); /* Get next BD. */ - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) bdp = qe_port->tx_bd_base; else bdp++; @@ -364,10 +364,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - while (!(in_be16(&bdp->status) & BD_SC_READY) && + while (!(ioread16be(&bdp->status) & BD_SC_READY) && (xmit->tail != xmit->head)) { count = 0; - p = qe2cpu_addr(bdp->buf, qe_port); + p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); while (count < qe_port->tx_fifosize) { *p++ = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -377,11 +377,11 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) break; } - out_be16(&bdp->length, count); + iowrite16be(count, &bdp->length); setbits16(&bdp->status, BD_SC_READY); /* Get next BD. */ - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) bdp = qe_port->tx_bd_base; else bdp++; @@ -414,7 +414,7 @@ static void qe_uart_start_tx(struct uart_port *port) container_of(port, struct uart_qe_port, port); /* If we currently are transmitting, then just return */ - if (in_be16(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX) + if (ioread16be(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX) return; /* Otherwise, pump the port and start transmission */ @@ -479,14 +479,14 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) */ bdp = qe_port->rx_cur; while (1) { - status = in_be16(&bdp->status); + status = ioread16be(&bdp->status); /* If this one is empty, then we assume we've read them all */ if (status & BD_SC_EMPTY) break; /* get number of characters, and check space in RX buffer */ - i = in_be16(&bdp->length); + i = ioread16be(&bdp->length); /* If we don't have enough room in RX buffer for the entire BD, * then we try later, which will be the next RX interrupt. @@ -497,7 +497,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) } /* get pointer */ - cp = qe2cpu_addr(bdp->buf, qe_port); + cp = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); /* loop through the buffer */ while (i-- > 0) { @@ -519,7 +519,7 @@ error_return: /* This BD is ready to be used again. Clear status. get next */ clrsetbits_be16(&bdp->status, BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID, BD_SC_EMPTY); - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) bdp = qe_port->rx_bd_base; else bdp++; @@ -578,8 +578,8 @@ static irqreturn_t qe_uart_int(int irq, void *data) u16 events; /* Clear the interrupts */ - events = in_be16(&uccp->ucce); - out_be16(&uccp->ucce, events); + events = ioread16be(&uccp->ucce); + iowrite16be(events, &uccp->ucce); if (events & UCC_UART_UCCE_BRKE) uart_handle_break(&qe_port->port); @@ -610,17 +610,17 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) bdp = qe_port->rx_bd_base; qe_port->rx_cur = qe_port->rx_bd_base; for (i = 0; i < (qe_port->rx_nrfifos - 1); i++) { - out_be16(&bdp->status, BD_SC_EMPTY | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_EMPTY | BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); bd_virt += qe_port->rx_fifosize; bdp++; } /* */ - out_be16(&bdp->status, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); /* Set the physical address of the host memory * buffers in the buffer descriptors, and the @@ -631,9 +631,9 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) qe_port->tx_cur = qe_port->tx_bd_base; bdp = qe_port->tx_bd_base; for (i = 0; i < (qe_port->tx_nrfifos - 1); i++) { - out_be16(&bdp->status, BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); bd_virt += qe_port->tx_fifosize; bdp++; } @@ -643,9 +643,9 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) setbits16(&qe_port->tx_cur->status, BD_SC_P); #endif - out_be16(&bdp->status, BD_SC_WRAP | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_WRAP | BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); } /* @@ -667,21 +667,21 @@ static void qe_uart_init_ucc(struct uart_qe_port *qe_port) ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX); /* Program the UCC UART parameter RAM */ - out_8(&uccup->common.rbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); - out_8(&uccup->common.tbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); - out_be16(&uccup->common.mrblr, qe_port->rx_fifosize); - out_be16(&uccup->maxidl, 0x10); - out_be16(&uccup->brkcr, 1); - out_be16(&uccup->parec, 0); - out_be16(&uccup->frmec, 0); - out_be16(&uccup->nosec, 0); - out_be16(&uccup->brkec, 0); - out_be16(&uccup->uaddr[0], 0); - out_be16(&uccup->uaddr[1], 0); - out_be16(&uccup->toseq, 0); + iowrite8(UCC_BMR_GBL | UCC_BMR_BO_BE, &uccup->common.rbmr); + iowrite8(UCC_BMR_GBL | UCC_BMR_BO_BE, &uccup->common.tbmr); + iowrite16be(qe_port->rx_fifosize, &uccup->common.mrblr); + iowrite16be(0x10, &uccup->maxidl); + iowrite16be(1, &uccup->brkcr); + iowrite16be(0, &uccup->parec); + iowrite16be(0, &uccup->frmec); + iowrite16be(0, &uccup->nosec); + iowrite16be(0, &uccup->brkec); + iowrite16be(0, &uccup->uaddr[0]); + iowrite16be(0, &uccup->uaddr[1]); + iowrite16be(0, &uccup->toseq); for (i = 0; i < 8; i++) - out_be16(&uccup->cchars[i], 0xC000); - out_be16(&uccup->rccm, 0xc0ff); + iowrite16be(0xC000, &uccup->cchars[i]); + iowrite16be(0xc0ff, &uccup->rccm); /* Configure the GUMR registers for UART */ if (soft_uart) { @@ -715,30 +715,30 @@ static void qe_uart_init_ucc(struct uart_qe_port *qe_port) #endif /* Disable rx interrupts and clear all pending events. */ - out_be16(&uccp->uccm, 0); - out_be16(&uccp->ucce, 0xffff); - out_be16(&uccp->udsr, 0x7e7e); + iowrite16be(0, &uccp->uccm); + iowrite16be(0xffff, &uccp->ucce); + iowrite16be(0x7e7e, &uccp->udsr); /* Initialize UPSMR */ - out_be16(&uccp->upsmr, 0); + iowrite16be(0, &uccp->upsmr); if (soft_uart) { - out_be16(&uccup->supsmr, 0x30); - out_be16(&uccup->res92, 0); - out_be32(&uccup->rx_state, 0); - out_be32(&uccup->rx_cnt, 0); - out_8(&uccup->rx_bitmark, 0); - out_8(&uccup->rx_length, 10); - out_be32(&uccup->dump_ptr, 0x4000); - out_8(&uccup->rx_temp_dlst_qe, 0); - out_be32(&uccup->rx_frame_rem, 0); - out_8(&uccup->rx_frame_rem_size, 0); + iowrite16be(0x30, &uccup->supsmr); + iowrite16be(0, &uccup->res92); + iowrite32be(0, &uccup->rx_state); + iowrite32be(0, &uccup->rx_cnt); + iowrite8(0, &uccup->rx_bitmark); + iowrite8(10, &uccup->rx_length); + iowrite32be(0x4000, &uccup->dump_ptr); + iowrite8(0, &uccup->rx_temp_dlst_qe); + iowrite32be(0, &uccup->rx_frame_rem); + iowrite8(0, &uccup->rx_frame_rem_size); /* Soft-UART requires TX to be 1X */ - out_8(&uccup->tx_mode, - UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1); - out_be16(&uccup->tx_state, 0); - out_8(&uccup->resD4, 0); - out_be16(&uccup->resD5, 0); + iowrite8(UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1, + &uccup->tx_mode); + iowrite16be(0, &uccup->tx_state); + iowrite8(0, &uccup->resD4); + iowrite16be(0, &uccup->resD5); /* Set UART mode. * Enable receive and transmit. @@ -866,9 +866,9 @@ static void qe_uart_set_termios(struct uart_port *port, struct ucc_slow __iomem *uccp = qe_port->uccp; unsigned int baud; unsigned long flags; - u16 upsmr = in_be16(&uccp->upsmr); + u16 upsmr = ioread16be(&uccp->upsmr); struct ucc_uart_pram __iomem *uccup = qe_port->uccup; - u16 supsmr = in_be16(&uccup->supsmr); + u16 supsmr = ioread16be(&uccup->supsmr); u8 char_length = 2; /* 1 + CL + PEN + 1 + SL */ /* Character length programmed into the mode register is the @@ -966,10 +966,10 @@ static void qe_uart_set_termios(struct uart_port *port, /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); - out_be16(&uccp->upsmr, upsmr); + iowrite16be(upsmr, &uccp->upsmr); if (soft_uart) { - out_be16(&uccup->supsmr, supsmr); - out_8(&uccup->rx_length, char_length); + iowrite16be(supsmr, &uccup->supsmr); + iowrite8(char_length, &uccup->rx_length); /* Soft-UART requires a 1X multiplier for TX */ qe_setbrg(qe_port->us_info.rx_clock, baud, 16); @@ -1139,7 +1139,9 @@ static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l) { struct device_node *np; const char *soc_string; +#ifdef CONFIG_PPC_85xx unsigned int svr; +#endif unsigned int soc; /* Find the CPU node */ @@ -1156,10 +1158,12 @@ static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l) if ((sscanf(soc_string, "PowerPC,%u", &soc) != 1) || !soc) return 0; +#ifdef CONFIG_PPC_85xx /* Get the revision from the SVR */ svr = mfspr(SPRN_SVR); *rev_h = (svr >> 4) & 0xf; *rev_l = svr & 0xf; +#endif return soc; } @@ -1202,7 +1206,7 @@ static void uart_firmware_cont(const struct firmware *fw, void *context) static int ucc_uart_probe(struct platform_device *ofdev) { struct device_node *np = ofdev->dev.of_node; - const unsigned int *iprop; /* Integer OF properties */ + u32 val; const char *sprop; /* String OF properties */ struct uart_qe_port *qe_port = NULL; struct resource res; @@ -1285,10 +1289,10 @@ static int ucc_uart_probe(struct platform_device *ofdev) /* Get the UCC number (device ID) */ /* UCCs are numbered 1-7 */ - iprop = of_get_property(np, "cell-index", NULL); - if (!iprop) { - iprop = of_get_property(np, "device-id", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "cell-index", 0, &val); + if (ret) { + ret = of_property_read_u32_index(np, "device-id", 0, &val); + if (ret) { dev_err(&ofdev->dev, "UCC is unspecified in " "device tree\n"); ret = -EINVAL; @@ -1296,12 +1300,12 @@ static int ucc_uart_probe(struct platform_device *ofdev) } } - if ((*iprop < 1) || (*iprop > UCC_MAX_NUM)) { - dev_err(&ofdev->dev, "no support for UCC%u\n", *iprop); + if ((val < 1) || (val > UCC_MAX_NUM)) { + dev_err(&ofdev->dev, "no support for UCC%u\n", val); ret = -ENODEV; goto out_free; } - qe_port->ucc_num = *iprop - 1; + qe_port->ucc_num = val - 1; /* * In the future, we should not require the BRG to be specified in the @@ -1345,13 +1349,13 @@ static int ucc_uart_probe(struct platform_device *ofdev) } /* Get the port number, numbered 0-3 */ - iprop = of_get_property(np, "port-number", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "port-number", 0, &val); + if (ret) { dev_err(&ofdev->dev, "missing port-number in device tree\n"); ret = -EINVAL; goto out_free; } - qe_port->port.line = *iprop; + qe_port->port.line = val; if (qe_port->port.line >= UCC_MAX_UART) { dev_err(&ofdev->dev, "port-number must be 0-%u\n", UCC_MAX_UART - 1); @@ -1381,31 +1385,31 @@ static int ucc_uart_probe(struct platform_device *ofdev) } } - iprop = of_get_property(np, "brg-frequency", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "brg-frequency", 0, &val); + if (ret) { dev_err(&ofdev->dev, "missing brg-frequency in device tree\n"); ret = -EINVAL; goto out_np; } - if (*iprop) - qe_port->port.uartclk = *iprop; + if (val) + qe_port->port.uartclk = val; else { /* * Older versions of U-Boot do not initialize the brg-frequency * property, so in this case we assume the BRG frequency is * half the QE bus frequency. */ - iprop = of_get_property(np, "bus-frequency", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "bus-frequency", 0, &val); + if (ret) { dev_err(&ofdev->dev, "missing QE bus-frequency in device tree\n"); ret = -EINVAL; goto out_np; } - if (*iprop) - qe_port->port.uartclk = *iprop / 2; + if (val) + qe_port->port.uartclk = val / 2; else { dev_err(&ofdev->dev, "invalid QE bus-frequency in device tree\n"); diff --git a/include/linux/fsl/qe.h b/include/linux/fsl/qe.h index 5a6a647..ef4422c 100644 --- a/include/linux/fsl/qe.h +++ b/include/linux/fsl/qe.h @@ -306,6 +306,27 @@ struct qe_bd { #define BD_STATUS_MASK 0xffff0000 #define BD_LENGTH_MASK 0x0000ffff +/* Buffer descriptor control/status used by serial + */ + +#define BD_SC_EMPTY (0x8000) /* Receive is empty */ +#define BD_SC_READY (0x8000) /* Transmit is ready */ +#define BD_SC_WRAP (0x2000) /* Last buffer descriptor */ +#define BD_SC_INTRPT (0x1000) /* Interrupt on change */ +#define BD_SC_LAST (0x0800) /* Last buffer in frame */ +#define BD_SC_TC (0x0400) /* Transmit CRC */ +#define BD_SC_CM (0x0200) /* Continuous mode */ +#define BD_SC_ID (0x0100) /* Rec'd too many idles */ +#define BD_SC_P (0x0100) /* xmt preamble */ +#define BD_SC_BR (0x0020) /* Break received */ +#define BD_SC_FR (0x0010) /* Framing error */ +#define BD_SC_PR (0x0008) /* Parity error */ +#define BD_SC_NAK (0x0004) /* NAK - did not respond */ +#define BD_SC_OV (0x0002) /* Overrun */ +#define BD_SC_UN (0x0002) /* Underrun */ +#define BD_SC_CD (0x0001) /* */ +#define BD_SC_CL (0x0001) /* Collision */ + /* Alignment */ #define QE_INTR_TABLE_ALIGN 16 /* ??? */ #define QE_ALIGNMENT_OF_BD 8 -- cgit v0.10.2 From d02fce78053a6efda2b6cf135606b6a41d413a28 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Wed, 24 Sep 2014 15:37:36 +0800 Subject: qe: run qe_init and qe_ic_init qe and qe_ic need to be initialized before the qe app drivers, using subsys_initcall to run qe_init and qe_ic_init Signed-off-by: Zhao Qiang --- upstream link: http://patchwork.ozlabs.org/patch/398469/ it is under discussion Change-Id: If59edcf3d4ecaaa18cf2a835bcaff842718c187b Reviewed-on: http://git.am.freescale.net:8181/21120 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/drivers/soc/qe/qe.c b/drivers/soc/qe/qe.c index 2aaa5b2..bfea0f8 100644 --- a/drivers/soc/qe/qe.c +++ b/drivers/soc/qe/qe.c @@ -683,6 +683,21 @@ unsigned int qe_get_num_of_snums(void) } EXPORT_SYMBOL(qe_get_num_of_snums); +static int __init qe_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!np) { + pr_err("%s: Could not find Quicc Engine node\n", __func__); + return -ENODEV; + } + qe_reset(); + of_node_put(np); + return 0; +} +subsys_initcall(qe_init); + #if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) static int qe_resume(struct platform_device *ofdev) { diff --git a/drivers/soc/qe/qe_ic.c b/drivers/soc/qe/qe_ic.c index cc1b8d5..11fe98c 100644 --- a/drivers/soc/qe/qe_ic.c +++ b/drivers/soc/qe/qe_ic.c @@ -34,6 +34,7 @@ #include #include "qe_ic.h" +#include "../../irqchip/irqchip.h" static DEFINE_RAW_SPINLOCK(qe_ic_lock); @@ -501,4 +502,18 @@ static int __init init_qe_ic_sysfs(void) return 0; } +static int __init qeic_of_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic"); + if (np) { + qe_ic_init(np, 0, qe_ic_cascade_low_mpic, + qe_ic_cascade_high_mpic); + of_node_put(np); + } + return 0; +} +subsys_initcall(qeic_of_init); + subsys_initcall(init_qe_ic_sysfs); -- cgit v0.10.2 From 65ec881ba13ee1d436dadd18a1051b99d78b6bde Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Thu, 25 Sep 2014 15:30:57 +0800 Subject: ls1021a-twr/qe: add qe node to ls1-twr add qe node to ls1021atwr fdt. Signed-off-by: Zhao Qiang --- upstream link: http://patchwork.ozlabs.org/patch/398470/ it is under discussion. Change-Id: I4f0bc40003265f85bde01a9982ef7f91edd1d08e Reviewed-on: http://git.am.freescale.net:8181/21121 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts index a52be7b..415387f 100755 --- a/arch/arm/boot/dts/ls1021a-twr.dts +++ b/arch/arm/boot/dts/ls1021a-twr.dts @@ -164,6 +164,30 @@ }; }; +&uqe { + tdma: ucc@2000 { + compatible = "fsl,ucc-tdm"; + rx-clock-name = "clk8"; + tx-clock-name = "clk9"; + fsl,rx-sync-clock = "rsync_pin"; + fsl,tx-sync-clock = "tsync_pin"; + fsl,tx-timeslot = <0xfffffffe>; + fsl,rx-timeslot = <0xfffffffe>; + fsl,tdm-framer-type = "e1"; + fsl,tdm-mode = "normal"; + fsl,tdm-id = <0>; + fsl,siram-entry-id = <0>; + }; + + serial: ucc@2200 { + device_type = "serial"; + compatible = "ucc_uart"; + port-number = <1>; + rx-clock-name = "brg2"; + tx-clock-name = "brg2"; + }; +}; + &pwm6 { status = "okay"; }; diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index 80747dc..3f2ab89 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -314,6 +314,70 @@ status = "disabled"; }; + uqe: uqe@2400000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "qe"; + compatible = "fsl,qe", "simple-bus"; + ranges = <0x0 0x0 0x2400000 0x40000>; + reg = <0x0 0x2400000 0x0 0x480>; + brg-frequency = <100000000>; + bus-frequency = <200000000>; + + fsl,qe-num-riscs = <1>; + fsl,qe-num-snums = <28>; + + qeic: qeic@80 { + compatible = "fsl,qe-ic"; + reg = <0x80 0x80>; + #address-cells = <0>; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <0 109 0x04 0 109 0x04>; + }; + + si1: si@700 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,qe-si"; + reg = <0x700 0x80>; + }; + + siram1: siram@1000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,qe-siram"; + reg = <0x1000 0x800>; + }; + + ucc@2000 { + cell-index = <1>; + reg = <0x2000 0x200>; + interrupts = <32>; + interrupt-parent = <&qeic>; + }; + + ucc@2200 { + cell-index = <3>; + reg = <0x2200 0x200>; + interrupts = <34>; + interrupt-parent = <&qeic>; + }; + + muram@10000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,qe-muram", "fsl,cpm-muram"; + ranges = <0x0 0x10000 0x6000>; + + data-only@0 { + compatible = "fsl,qe-muram-data", + "fsl,cpm-muram-data"; + reg = <0x0 0x6000>; + }; + }; + }; + lpuart0: serial@2950000 { compatible = "fsl,ls1021a-lpuart"; reg = <0x0 0x2950000 0x0 0x1000>; -- cgit v0.10.2 From 3338d67019795bd214211932045e17baff22d5f3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 21 Aug 2014 20:50:25 -0300 Subject: pwm: fsl-ftm: Select REGMAP_MMIO Commit 42fa98a9c360 ("pwm: fsl-ftm: Convert to direct regmap API usage") introduced the following error when REGMAP_MMIO=n: drivers/built-in.o: In function `fsl_pwm_probe': >> pwm-fsl-ftm.c:(.text+0xd7d7): undefined reference to `devm_regmap_init_mmio_clk' Select select REGMAP_MMIO in order to fix this error. Reported-by: kbuild test robot Signed-off-by: Fabio Estevam Signed-off-by: Thierry Reding --- This patch is pulled back from upstream: commit 00018a8ae5c552a2464e0df15437511ba4f56495 Change-Id: I07d149b0163d9f30137f8168c9f2b426e19c579e Reviewed-on: http://git.am.freescale.net:8181/21306 Tested-by: Review Code-CDREVIEW Reviewed-by: Dongsheng Wang Reviewed-by: Chao Fu Reviewed-by: Zhengxiong Jin diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ab88168..9c64341 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -65,6 +65,7 @@ config PWM_BFIN config PWM_FSL_FTM tristate "Freescale FlexTimer Module (FTM) PWM support" depends on OF + select REGMAP_MMIO help Generic FTM PWM framework driver for Freescale VF610 and Layerscape LS-1 SoCs. -- cgit v0.10.2 From 53a3bb65a928123d2a001840ecb774408cfc1ce0 Mon Sep 17 00:00:00 2001 From: Haijun Zhang Date: Fri, 9 May 2014 11:04:28 +0800 Subject: mmc:esdhc: add esdhc support on ls1021a-qds Ls1021a-qds has the same ip block as esdhc on powerpc platform. But they have diferent endian mode and different IO entry. So we change the IO entry to generic IO to support working on different architecture with different endian mode. Also add some properties to support esdhc on ls1021a-qds. Signed-off-by: Qiu WeiJie Signed-off-by: Haijun Zhang --- This patch has sent to linux-mmc maillist. URL: https://patchwork.kernel.org/patch/3976141/ Change-Id: I4959b07bf9e38a442316f0f45425018fa7d6f579 Reviewed-on: http://git.am.freescale.net:8181/14824 Tested-by: Review Code-CDREVIEW Reviewed-by: Xiaobo Xie Reviewed-by: Zhengxiong Jin diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0f9f36b..2e747cc 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -32,7 +32,7 @@ static u32 esdhc_readl(struct sdhci_host *host, int reg) { u32 ret; - ret = in_be32(host->ioaddr + reg); + ret = sdhci_32bs_readl(host, reg); /* * The bit of ADMA flag in eSDHC is not compatible with standard * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is @@ -44,7 +44,7 @@ static u32 esdhc_readl(struct sdhci_host *host, int reg) * the verdor version number, oxFE is SDHCI_HOST_VERSION. */ if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { - u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + u32 tmp = sdhci_32bs_readl(host, SDHCI_SLOT_INT_STATUS); tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (tmp > VENDOR_V_22) ret |= SDHCI_CAN_DO_ADMA2; @@ -68,9 +68,9 @@ static u16 esdhc_readw(struct sdhci_host *host, int reg) int shift = (reg & 0x2) * 8; if (unlikely(reg == SDHCI_HOST_VERSION)) - ret = in_be32(host->ioaddr + base) & 0xffff; + ret = sdhci_32bs_readl(host, base) & 0xffff; else - ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; + ret = (sdhci_32bs_readl(host, base) >> shift) & 0xffff; /* T4240-R1.0-R2.0 had a incorrect vendor version and spec version */ if ((reg == SDHCI_HOST_VERSION) && @@ -85,7 +85,10 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) { int base = reg & ~0x3; int shift = (reg & 0x3) * 8; - u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; + u32 ret; + u8 val; + + ret = sdhci_32bs_readl(host, base); /* * "DMA select" locates at offset 0x28 in SD specification, but on @@ -94,16 +97,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) if (reg == SDHCI_HOST_CONTROL) { u32 dma_bits; - dma_bits = in_be32(host->ioaddr + reg); /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; + dma_bits = (ret >> 5) & SDHCI_CTRL_DMA_MASK; /* fixup the result */ ret &= ~SDHCI_CTRL_DMA_MASK; ret |= dma_bits; + val = (ret & 0xff); } - return ret; + val = (ret >> shift) & 0xff; + + return val; } static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) @@ -115,11 +120,28 @@ static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) */ if (reg == SDHCI_INT_ENABLE) val |= SDHCI_INT_BLK_GAP; - sdhci_be32bs_writel(host, val, reg); + sdhci_32bs_writel(host, val, reg); } static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + sdhci_32bs_writel(host, val << 16 | + pltfm_host->xfer_mode_shadow, + SDHCI_TRANSFER_MODE); + return; + } + if (reg == SDHCI_BLOCK_SIZE) { /* * Two last DMA bits are reserved, and first one is used for @@ -128,7 +150,7 @@ static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) */ val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); } - sdhci_be32bs_writew(host, val, reg); + sdhci_clrsetbits(host, 0xffff, val, reg); } static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) @@ -149,10 +171,10 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) /* DMA select is 22,23 bits in Protocol Control Register */ dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; - clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, - dma_bits); + sdhci_clrsetbits(host, SDHCI_CTRL_DMA_MASK << 5, dma_bits, + SDHCI_HOST_CONTROL); val &= ~SDHCI_CTRL_DMA_MASK; - val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; + val |= sdhci_32bs_readl(host, reg) & SDHCI_CTRL_DMA_MASK; } /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ @@ -174,7 +196,7 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) temp &= ~ESDHC_CLOCK_CRDEN; esdhc_writel(host, temp, ESDHC_SYSTEM_CONTROL); - sdhci_be32bs_writeb(host, val, reg); + sdhci_32bs_writeb(host, val, reg); temp |= ESDHC_CLOCK_CRDEN; esdhc_writel(host, temp, ESDHC_SYSTEM_CONTROL); @@ -190,7 +212,7 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) if (SVR_SOC_VER(svr) == SVR_T4240) { u8 vol; - vol = sdhci_be32bs_readb(host, reg); + vol = sdhci_32bs_readb(host, reg); if (host->pwr == SDHCI_POWER_180) vol &= ~ESDHC_VOL_SEL; else @@ -199,7 +221,7 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) return; } - sdhci_be32bs_writeb(host, val, reg); + sdhci_clrsetbits(host, 0xff, val, reg); } /* @@ -216,7 +238,7 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) dma_addr_t dmastart; dma_addr_t dmanow; - tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + tmp = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; applicable = (intmask & SDHCI_INT_DATA_END) && @@ -234,7 +256,7 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + SDHCI_DEFAULT_BOUNDARY_SIZE; host->data->bytes_xfered = dmanow - dmastart; - sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); + esdhc_writel(host, dmanow, SDHCI_DMA_ADDRESS); return; } @@ -316,7 +338,8 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) static int esdhc_of_enable_dma(struct sdhci_host *host) { - setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); + esdhc_writel(host, esdhc_readl(host, ESDHC_DMA_SYSCTL) + | ESDHC_DMA_SNOOP, ESDHC_DMA_SYSCTL); return 0; } @@ -352,12 +375,12 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) static u32 esdhc_proctl; static void esdhc_of_suspend(struct sdhci_host *host) { - esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + esdhc_proctl = esdhc_readl(host, SDHCI_HOST_CONTROL); } static void esdhc_of_resume(struct sdhci_host *host) { - sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); + esdhc_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); } #endif @@ -390,7 +413,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) u32 vvn; svr = mfspr(SPRN_SVR); - vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + vvn = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (vvn == VENDOR_V_22) host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; @@ -437,18 +460,18 @@ static int esdhc_of_get_cd(struct sdhci_host *host) if (host->quirks2 & SDHCI_QUIRK2_FORCE_CMD13_DETECT_CARD) return -ENOSYS; - sysctl = sdhci_be32bs_readl(host, SDHCI_CLOCK_CONTROL); + sysctl = sdhci_32bs_readl(host, SDHCI_CLOCK_CONTROL); /* Enable the controller clock to update the present state */ - sdhci_be32bs_writel(host, sysctl | SDHCI_CLOCK_INT_EN, + sdhci_32bs_writel(host, sysctl | SDHCI_CLOCK_INT_EN, SDHCI_CLOCK_CONTROL); /* Detect the card present or absent */ - present = sdhci_be32bs_readl(host, SDHCI_PRESENT_STATE); + present = sdhci_32bs_readl(host, SDHCI_PRESENT_STATE); present &= (SDHCI_CARD_PRESENT | SDHCI_CARD_CDPL); /* Resave the previous to System control register */ - sdhci_be32bs_writel(host, sysctl, SDHCI_CLOCK_CONTROL); + sdhci_32bs_writel(host, sysctl, SDHCI_CLOCK_CONTROL); return !!present; } @@ -486,8 +509,8 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) break; } - clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, - ESDHC_CTRL_BUSWIDTH_MASK, ctrl); + sdhci_clrsetbits(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl, + SDHCI_HOST_CONTROL); return 0; } @@ -528,19 +551,18 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { .ops = &sdhci_esdhc_ops, }; -static int sdhci_esdhc_probe(struct platform_device *pdev) +static void esdhc_get_property(struct platform_device *pdev) { - struct sdhci_host *host; - struct device_node *np; - int ret; - - host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); - if (IS_ERR(host)) - return PTR_ERR(host); + struct device_node *np = pdev->dev.of_node; + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); sdhci_get_of_property(pdev); - np = pdev->dev.of_node; + /* call to generic mmc_of_parse to support additional capabilities */ + mmc_of_parse(host->mmc); + mmc_of_parse_voltage(np, &host->ocr_mask); + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard @@ -549,10 +571,27 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; } - /* call to generic mmc_of_parse to support additional capabilities */ - mmc_of_parse(host->mmc); - mmc_of_parse_voltage(np, &host->ocr_mask); + if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + + if (!pltfm_host->clock) { + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); + pltfm_host->clock = clk_get_rate(pltfm_host->clk); + clk_prepare_enable(pltfm_host->clk); + } +} + +static int sdhci_esdhc_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + int ret; + + + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + esdhc_get_property(pdev); ret = sdhci_add_host(host); if (ret) sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 57c514a..3605665 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -35,26 +35,26 @@ static void sdhci_hlwd_writel(struct sdhci_host *host, u32 val, int reg) { - sdhci_be32bs_writel(host, val, reg); + sdhci_32bs_writel(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static void sdhci_hlwd_writew(struct sdhci_host *host, u16 val, int reg) { - sdhci_be32bs_writew(host, val, reg); + sdhci_32bs_writew(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg) { - sdhci_be32bs_writeb(host, val, reg); + sdhci_32bs_writeb(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static const struct sdhci_ops sdhci_hlwd_ops = { - .read_l = sdhci_be32bs_readl, - .read_w = sdhci_be32bs_readw, - .read_b = sdhci_be32bs_readb, + .read_l = sdhci_32bs_readl, + .read_w = sdhci_32bs_readw, + .read_b = sdhci_32bs_readb, .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index f0b2030..2512c11 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -135,6 +135,7 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, { struct sdhci_host *host; struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; @@ -160,6 +161,14 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, goto err; } + pltfm_host = sdhci_priv(host); + pltfm_host->endian_mode = BIG_ENDIAN_MODE; + +#ifdef CONFIG_OF + if (of_get_property(np, "little-endian", NULL)) + pltfm_host->endian_mode = LITTLE_ENDIAN_MODE; +#endif /* CONFIG_OF */ + host->hw_name = dev_name(&pdev->dev); if (pdata && pdata->ops) host->ops = pdata->ops; @@ -245,7 +254,7 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_register); int sdhci_pltfm_unregister(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + int dead = (sdhci_readl(host, SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index e15ced79..c31aa94 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -28,6 +28,10 @@ struct sdhci_pltfm_host { /* migrate from sdhci_of_host */ unsigned int clock; u16 xfer_mode_shadow; + enum endian_mode { + LITTLE_ENDIAN_MODE, + BIG_ENDIAN_MODE, + } endian_mode; unsigned long private[0] ____cacheline_aligned; }; @@ -37,33 +41,61 @@ struct sdhci_pltfm_host { * These accessors are designed for big endian hosts doing I/O to * little endian controllers incorporating a 32-bit hardware byte swapper. */ -static inline u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg) +static inline void sdhci_clrsetbits(struct sdhci_host *host, u32 mask, + u32 val, int reg) { - return in_be32(host->ioaddr + reg); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + void __iomem *base = host->ioaddr + (reg & ~0x3); + u32 shift = (reg & 0x3) * 8; + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(((ioread32be(base) & ~(mask << shift)) | + (val << shift)), base); + else + iowrite32(((ioread32(base) & ~(mask << shift)) | + (val << shift)), base); } -static inline u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg) +static inline u32 sdhci_32bs_readl(struct sdhci_host *host, int reg) { - return in_be16(host->ioaddr + (reg ^ 0x2)); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + return ioread32be(host->ioaddr + reg); + else + return ioread32(host->ioaddr + reg); } -static inline u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg) +static inline u16 sdhci_32bs_readw(struct sdhci_host *host, int reg) { - return in_8(host->ioaddr + (reg ^ 0x3)); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + return ioread16be(host->ioaddr + (reg ^ 0x2)); + else + return ioread16(host->ioaddr + (reg ^ 0x2)); } -static inline void sdhci_be32bs_writel(struct sdhci_host *host, - u32 val, int reg) +static inline u8 sdhci_32bs_readb(struct sdhci_host *host, int reg) { - out_be32(host->ioaddr + reg, val); + return ioread8(host->ioaddr + (reg ^ 0x3)); } -static inline void sdhci_be32bs_writew(struct sdhci_host *host, +static inline void sdhci_32bs_writel(struct sdhci_host *host, + u32 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(val, host->ioaddr + reg); + else + iowrite32(val, host->ioaddr + reg); +} + +static inline void sdhci_32bs_writew(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - int base = reg & ~0x3; - int shift = (reg & 0x2) * 8; switch (reg) { case SDHCI_TRANSFER_MODE: @@ -74,20 +106,20 @@ static inline void sdhci_be32bs_writew(struct sdhci_host *host, pltfm_host->xfer_mode_shadow = val; return; case SDHCI_COMMAND: - sdhci_be32bs_writel(host, - val << 16 | pltfm_host->xfer_mode_shadow, - SDHCI_TRANSFER_MODE); + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(val << 16 | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); + else + iowrite32(val << 16 | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); return; } - clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift); + sdhci_clrsetbits(host, 0xffff, val, reg); } -static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg) +static inline void sdhci_32bs_writeb(struct sdhci_host *host, u8 val, int reg) { - int base = reg & ~0x3; - int shift = (reg & 0x3) * 8; - - clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); + sdhci_clrsetbits(host, 0xff, val, reg); } #endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */ -- cgit v0.10.2 From 3579676a1a33380e958fd06e5089bc9397759fe0 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 24 Sep 2014 15:27:31 +0800 Subject: fb: Add DCU framebuffer driver for LS1021A platform The Display Controller Unit (DCU) module is a system master that fetches graphics stored in internal or external memory and displays them on a TFT LCD panel. A wide range of panel sizes is supported and the timing of the interface signals is highly configurable. Graphics are read directly from memory and then blended in real-time, which allows for dynamic content creation with minimal CPU intervention. The features: (1) Full RGB888 output to TFT LCD panel. (2) For the current LCD panel, WQVGA "480x272" is supported. (3) Blending of each pixel using up to 4 source layers dependent on size of panel. (4) Each graphic layer can be placed with one pixel resolution in either axis. (5) Each graphic layer support RGB565 and RGB888 direct colors without alpha channel and BGRA8888 direct colors with an alpha channel. (6) Each graphic layer support alpha blending with 8-bit resolution. Signed-off-by: Alison Wang Signed-off-by: Xiubo Li The maintainer and many other people all have strong opinions to add DCU driver based the DRM framework. The mails URL: http://lists.infradead.org/pipermail/linux-arm-kernel/2013-September/197863.html The first DRM version of DCU will be send out to the community before 30 November 2014. Change-Id: I9feb7c9b975431a1bb3906eb955dcf6ae09654eb Reviewed-on: http://git.am.freescale.net:8181/19647 Tested-by: Review Code-CDREVIEW Reviewed-by: Chao Fu Reviewed-by: Huan Wang Reviewed-by: Zhengxiong Jin diff --git a/Documentation/devicetree/bindings/video/fsl-dcu-fb.txt b/Documentation/devicetree/bindings/video/fsl-dcu-fb.txt new file mode 100644 index 0000000..20fc74c --- /dev/null +++ b/Documentation/devicetree/bindings/video/fsl-dcu-fb.txt @@ -0,0 +1,69 @@ +* Freescale Display Control Unit (DCU) + +=== For dcu node === +Required properties: +- compatible: Should be one of "fsl,vf610-dcu" and "fsl,ls1021a-dcu". +- reg: Address and length of the register set for DCU. +- interrupts: Should contain DCU interrupts. +- clocks: From common clock binding: handle to DCU clock. +- clock-names: From common clock binding: Shall be "dcu". +- display: The phandle to display node. + +Optional properties: +- tcon-controller: The phandle of TCON controller. +- scfg-controller: The phandle of scfg node. + +=== For display sub-node === +Required properties: +- bits-per-pixel: <24> for RGB888. + +Required timing node for dispplay sub-node: +- display-timings: Refer to binding doc display-timing.txt for details. + +=== For TCON node === +Required properties: +- compatible: Should be "fsl,tcon". +- reg: Address and length of the register set for TCON. +- clocks: From common clock binding: handle to TCON clock. +- clock-names: From common clock binding: Shall be "tcon". + +Examples: + +dcu0: dcu@40058000 { + compatible = "fsl,vf610-dcu"; + reg = <0x40058000 0x1200>; + interrupts = <0 30 0x04>; + clocks = <&clks VF610_CLK_DCU0>; + clock-names = "dcu"; + tcon-controller = <&tcon0>; + scfg-controller = <&scfg>; + display = <&display>; + + display: display@0 { + bits-per-pixel = <24>; + + display-timings { + native-mode = <&timing0>; + timing0: nl4827hc19 { + clock-frequency = <10870000>; + hactive = <480>; + vactive = <272>; + hback-porch = <2>; + hfront-porch = <2>; + vback-porch = <1>; + vfront-porch = <1>; + hsync-len = <41>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + }; + }; + }; +}; + +tcon0: tcon@4003d000 { + compatible = "fsl,vf610-tcon"; + reg = <0x4003d000 0x1000>; + clocks = <&clks VF610_CLK_TCON0>; + clock-names = "tcon"; +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4ce7204..2aadb06 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1965,6 +1965,24 @@ config FB_MBX_DEBUG If unsure, say N. +config FB_FSL_DCU + tristate "Freescale DCU framebuffer support" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB_MODE_HELPERS + select VIDEOMODE_HELPERS + ---help--- + Framebuffer driver for the Freescale SoC DCU. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + . + + If unsure, say N. + config FB_FSL_DIU tristate "Freescale DIU framebuffer support" depends on FB && FSL_SOC diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f5ef0ed..2c6bcf6 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -129,6 +129,7 @@ obj-$(CONFIG_FB_IMX) += imxfb.o obj-$(CONFIG_FB_S3C) += s3c-fb.o obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o obj-$(CONFIG_FB_FSL_DIU) += fsl-diu-fb.o +obj-$(CONFIG_FB_FSL_DCU) += fsl-dcu-fb.o obj-$(CONFIG_FB_COBALT) += cobalt_lcdfb.o obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o diff --git a/drivers/video/fsl-dcu-fb.c b/drivers/video/fsl-dcu-fb.c new file mode 100644 index 0000000..2fc9994 --- /dev/null +++ b/drivers/video/fsl-dcu-fb.c @@ -0,0 +1,1194 @@ +/* + * Copyright 2012-2014 Freescale Semiconductor, Inc. + * + * Freescale DCU framebuffer device driver + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include