summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Weigel <Matthew.Weigel@freescale.com>2014-10-28 16:17:08 (GMT)
committerMatthew Weigel <Matthew.Weigel@freescale.com>2014-12-11 18:38:59 (GMT)
commita1a034f31f51cdfe41cf7c2e9d3f8bd4963ae516 (patch)
treec9caa656194ef66fc9f547345f917c7011584bcf
parenta53aca56e29a3c4f2c76ac37740d586504676793 (diff)
parent5ccc4130fc9501cf73bd65a1386535b1e9f16d7c (diff)
downloadlinux-fsl-qoriq-a1a034f31f51cdfe41cf7c2e9d3f8bd4963ae516.tar.xz
Merge branch 'ls1-linux/LS1-SDK' to commit 2ec9fb2.
-rw-r--r--Documentation/devicetree/bindings/arm/fsl.txt28
-rw-r--r--Documentation/devicetree/bindings/clock/ls1021a-clock.txt27
-rw-r--r--Documentation/devicetree/bindings/dma/fsl-edma.txt76
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt (renamed from Documentation/devicetree/bindings/powerpc/fsl/ifc.txt)2
-rw-r--r--Documentation/devicetree/bindings/mtd/fsl-quadspi.txt51
-rw-r--r--Documentation/devicetree/bindings/mtd/spi-nor-flash.txt7
-rw-r--r--Documentation/devicetree/bindings/pci/layerscape-pci.txt46
-rw-r--r--Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt52
-rw-r--r--Documentation/devicetree/bindings/regmap/regmap.txt47
-rw-r--r--Documentation/devicetree/bindings/serial/fsl-lpuart.txt6
-rw-r--r--Documentation/devicetree/bindings/serial/of-serial.txt1
-rw-r--r--Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt7
-rw-r--r--Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt31
-rw-r--r--Documentation/devicetree/bindings/video/fsl-dcu-fb.txt69
-rw-r--r--Documentation/devicetree/bindings/video/fsl-sii902x.txt17
-rw-r--r--Documentation/hwmon/ltc294584
-rw-r--r--arch/arm/boot/dts/Makefile4
-rw-r--r--arch/arm/boot/dts/ls1021a-qds.dts423
-rwxr-xr-xarch/arm/boot/dts/ls1021a-twr.dts299
-rw-r--r--arch/arm/boot/dts/ls1021a.dtsi897
-rw-r--r--arch/arm/configs/ls1021a_defconfig180
-rw-r--r--arch/arm/include/asm/assembler.h2
-rw-r--r--arch/arm/include/asm/cacheflush.h46
-rw-r--r--arch/arm/include/asm/delay.h16
-rw-r--r--arch/arm/include/asm/io.h28
-rw-r--r--arch/arm/include/asm/irq.h2
-rw-r--r--arch/arm/kernel/irq.c7
-rw-r--r--arch/arm/kernel/sleep.S5
-rw-r--r--arch/arm/kernel/time.c29
-rw-r--r--arch/arm/mach-imx/Kconfig19
-rw-r--r--arch/arm/mach-imx/Makefile5
-rw-r--r--arch/arm/mach-imx/clk-ls1021a.c191
-rw-r--r--arch/arm/mach-imx/clk.h8
-rw-r--r--arch/arm/mach-imx/common.h6
-rw-r--r--arch/arm/mach-imx/hotplug.c57
-rw-r--r--arch/arm/mach-imx/mach-ls1021a.c31
-rw-r--r--arch/arm/mach-imx/platsmp.c36
-rw-r--r--arch/arm/mach-imx/pm-ls1.c389
-rw-r--r--arch/arm/mach-imx/sleep-ls1.S132
-rw-r--r--arch/arm/mach-imx/sleep-ls1.h19
-rw-r--r--arch/arm/mach-imx/src.c21
-rw-r--r--arch/arm/mach-vexpress/dcscb.c56
-rw-r--r--arch/arm/mach-vexpress/tc2_pm.c48
-rw-r--r--arch/arm/mm/proc-v7.S17
-rw-r--r--arch/powerpc/Kconfig9
-rw-r--r--arch/powerpc/configs/corenet32_smp_defconfig1
-rw-r--r--arch/powerpc/configs/corenet64_smp_defconfig1
-rw-r--r--arch/powerpc/configs/mpc85xx_defconfig1
-rw-r--r--arch/powerpc/configs/mpc85xx_smp_defconfig1
-rw-r--r--arch/powerpc/include/asm/fsl_85xx_cache_sram.h4
-rw-r--r--arch/powerpc/include/asm/fsl_pm.h12
-rw-r--r--arch/powerpc/lib/Makefile2
-rw-r--r--arch/powerpc/platforms/44x/Kconfig2
-rw-r--r--arch/powerpc/platforms/83xx/km83xx.c4
-rw-r--r--arch/powerpc/platforms/83xx/misc.c2
-rw-r--r--arch/powerpc/platforms/83xx/mpc832x_mds.c4
-rw-r--r--arch/powerpc/platforms/83xx/mpc832x_rdb.c4
-rw-r--r--arch/powerpc/platforms/83xx/mpc836x_mds.c4
-rw-r--r--arch/powerpc/platforms/83xx/mpc836x_rdk.c4
-rw-r--r--arch/powerpc/platforms/85xx/Kconfig2
-rw-r--r--arch/powerpc/platforms/85xx/common.c2
-rw-r--r--arch/powerpc/platforms/85xx/corenet_generic.c2
-rw-r--r--arch/powerpc/platforms/85xx/deepsleep.c13
-rw-r--r--arch/powerpc/platforms/85xx/mpc85xx_mds.c4
-rw-r--r--arch/powerpc/platforms/85xx/mpc85xx_rdb.c4
-rw-r--r--arch/powerpc/platforms/85xx/twr_p102x.c4
-rw-r--r--arch/powerpc/platforms/Kconfig21
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype2
-rw-r--r--arch/powerpc/sysdev/Makefile2
-rw-r--r--arch/powerpc/sysdev/cpm1.c2
-rw-r--r--arch/powerpc/sysdev/cpm2.c2
-rw-r--r--arch/powerpc/sysdev/cpm_common.c6
-rw-r--r--arch/powerpc/sysdev/ppc4xx_ocm.c4
-rw-r--r--arch/powerpc/sysdev/qe_lib/Kconfig27
-rw-r--r--crypto/testmgr.c64
-rw-r--r--crypto/testmgr.h2
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile3
-rw-r--r--drivers/base/regmap/regmap-i2c.c2
-rw-r--r--drivers/base/regmap/regmap-mmio.c83
-rw-r--r--drivers/base/regmap/regmap-spi.c2
-rw-r--r--drivers/base/regmap/regmap.c131
-rw-r--r--drivers/clk/Kconfig8
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/clk-qoriq.c (renamed from drivers/clk/clk-ppc-corenet.c)39
-rw-r--r--drivers/clocksource/Kconfig5
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/fsl_ftm_timer.c367
-rw-r--r--drivers/cpufreq/Kconfig.arm8
-rw-r--r--drivers/cpufreq/Kconfig.powerpc14
-rw-r--r--drivers/cpufreq/Makefile2
-rw-r--r--drivers/cpufreq/qoriq-cpufreq.c (renamed from drivers/cpufreq/ppc-corenet-cpufreq.c)165
-rw-r--r--drivers/crypto/caam/caamalg.c41
-rw-r--r--drivers/crypto/caam/caamhash.c158
-rw-r--r--drivers/crypto/caam/caamrng.c51
-rw-r--r--drivers/crypto/caam/compat.h2
-rw-r--r--drivers/crypto/caam/ctrl.c175
-rw-r--r--drivers/crypto/caam/intern.h10
-rw-r--r--drivers/crypto/caam/pdb.h30
-rw-r--r--drivers/crypto/caam/regs.h156
-rw-r--r--drivers/dma/Kconfig10
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/bestcomm/Kconfig2
-rw-r--r--drivers/dma/fsl-edma.c1079
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c52
-rw-r--r--drivers/hwmon/Kconfig12
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/hwmon.c190
-rw-r--r--drivers/hwmon/ltc2945.c519
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c15
-rw-r--r--drivers/memory/Kconfig8
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/fsl_ifc.c (renamed from arch/powerpc/sysdev/fsl_ifc.c)93
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c134
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c12
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c11
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h74
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/devices/Kconfig9
-rw-r--r--drivers/mtd/devices/m25p80.c1145
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c141
-rw-r--r--drivers/mtd/spi-nor/Kconfig12
-rw-r--r--drivers/mtd/spi-nor/Makefile2
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c1098
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c1183
-rw-r--r--drivers/net/can/Kconfig12
-rw-r--r--drivers/net/can/flexcan.c574
-rw-r--r--drivers/net/ethernet/freescale/Kconfig2
-rwxr-xr-xdrivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c2
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c56
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c265
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h106
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c8
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.h8
-rw-r--r--drivers/net/wan/fsl_ucc_hdlc.h8
-rw-r--r--drivers/of/irq.c276
-rw-r--r--drivers/of/of_pci_irq.c113
-rw-r--r--drivers/pci/host/Kconfig8
-rw-r--r--drivers/pci/host/Makefile1
-rw-r--r--drivers/pci/host/pci-layerscape.c273
-rw-r--r--drivers/pci/host/pcie-designware.c525
-rw-r--r--drivers/pci/host/pcie-designware.h60
-rw-r--r--drivers/platform/fsl/sleep_fsm.c479
-rw-r--r--drivers/platform/fsl/sleep_fsm.h106
-rw-r--r--drivers/pwm/Kconfig11
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c556
-rw-r--r--drivers/rtc/rtc-ds3232.c94
-rw-r--r--drivers/soc/Kconfig18
-rw-r--r--drivers/soc/Makefile7
-rw-r--r--drivers/soc/fsl/Kconfig3
-rw-r--r--drivers/soc/fsl/Kconfig.arm16
-rw-r--r--drivers/soc/fsl/Makefile5
-rw-r--r--drivers/soc/fsl/ls1/Kconfig11
-rw-r--r--drivers/soc/fsl/ls1/Makefile1
-rw-r--r--drivers/soc/fsl/ls1/ftm_alarm.c271
-rw-r--r--drivers/soc/qe/Kconfig46
-rw-r--r--drivers/soc/qe/Makefile (renamed from arch/powerpc/sysdev/qe_lib/Makefile)2
-rw-r--r--drivers/soc/qe/gpio.c (renamed from arch/powerpc/sysdev/qe_lib/gpio.c)5
-rw-r--r--drivers/soc/qe/qe.c (renamed from arch/powerpc/sysdev/qe_lib/qe.c)139
-rw-r--r--drivers/soc/qe/qe_common.c185
-rw-r--r--drivers/soc/qe/qe_ic.c (renamed from arch/powerpc/sysdev/qe_lib/qe_ic.c)40
-rw-r--r--drivers/soc/qe/qe_ic.h (renamed from arch/powerpc/sysdev/qe_lib/qe_ic.h)4
-rw-r--r--drivers/soc/qe/qe_io.c (renamed from arch/powerpc/sysdev/qe_lib/qe_io.c)82
-rw-r--r--drivers/soc/qe/ucc.c (renamed from arch/powerpc/sysdev/qe_lib/ucc.c)219
-rw-r--r--drivers/soc/qe/ucc_fast.c (renamed from arch/powerpc/sysdev/qe_lib/ucc_fast.c)169
-rw-r--r--drivers/soc/qe/ucc_slow.c (renamed from arch/powerpc/sysdev/qe_lib/ucc_slow.c)96
-rw-r--r--drivers/soc/qe/usb.c (renamed from arch/powerpc/sysdev/qe_lib/usb.c)44
-rw-r--r--drivers/spi/Kconfig3
-rw-r--r--drivers/spi/spi-altera.c2
-rw-r--r--drivers/spi/spi-ath79.c2
-rw-r--r--drivers/spi/spi-au1550.c2
-rw-r--r--drivers/spi/spi-bitbang.c12
-rw-r--r--drivers/spi/spi-butterfly.c2
-rw-r--r--drivers/spi/spi-davinci.c10
-rw-r--r--drivers/spi/spi-efm32.c2
-rw-r--r--drivers/spi/spi-fsl-cpm.c2
-rw-r--r--drivers/spi/spi-fsl-dspi.c377
-rw-r--r--drivers/spi/spi-gpio.c5
-rw-r--r--drivers/spi/spi-imx.c2
-rw-r--r--drivers/spi/spi-lm70llp.c2
-rw-r--r--drivers/spi/spi-nuc900.c3
-rw-r--r--drivers/spi/spi-oc-tiny.c2
-rw-r--r--drivers/spi/spi-ppc4xx.c3
-rw-r--r--drivers/spi/spi-s3c24xx.c2
-rw-r--r--drivers/spi/spi-sh-sci.c2
-rw-r--r--drivers/spi/spi-sirf.c2
-rw-r--r--drivers/spi/spi-xilinx.c2
-rw-r--r--drivers/tdm/device/Kconfig4
-rw-r--r--drivers/tdm/device/fsl_ucc_tdm.c221
-rw-r--r--drivers/tdm/device/fsl_ucc_tdm.h8
-rw-r--r--drivers/tdm/test/Kconfig2
-rw-r--r--drivers/tty/serial/fsl_lpuart.c659
-rw-r--r--drivers/tty/serial/of_serial.c31
-rw-r--r--drivers/tty/serial/ucc_uart.c178
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c2
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c18
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h4
-rw-r--r--drivers/usb/host/ehci-fsl.c47
-rw-r--r--drivers/usb/host/fhci-hcd.c2
-rw-r--r--drivers/usb/host/fhci-hub.c2
-rw-r--r--drivers/usb/host/fhci-sched.c2
-rw-r--r--drivers/usb/host/fhci.h4
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c112
-rw-r--r--drivers/video/Kconfig35
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/fsl-dcu-fb.c1258
-rw-r--r--drivers/video/fsl-sii902x.c552
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/imx2_wdt.c393
-rw-r--r--include/dt-bindings/clock/ls1021a-clock.h55
-rw-r--r--include/linux/fsl/bestcomm/sram.h4
-rw-r--r--include/linux/fsl/immap_qe.h (renamed from arch/powerpc/include/asm/immap_qe.h)48
-rw-r--r--include/linux/fsl/qe.h (renamed from arch/powerpc/include/asm/qe.h)109
-rw-r--r--include/linux/fsl/qe_ic.h (renamed from arch/powerpc/include/asm/qe_ic.h)4
-rw-r--r--include/linux/fsl/rheap.h (renamed from arch/powerpc/include/asm/rheap.h)50
-rw-r--r--include/linux/fsl/ucc.h (renamed from arch/powerpc/include/asm/ucc.h)8
-rw-r--r--include/linux/fsl/ucc_fast.h (renamed from arch/powerpc/include/asm/ucc_fast.h)25
-rw-r--r--include/linux/fsl/ucc_slow.h (renamed from arch/powerpc/include/asm/ucc_slow.h)25
-rw-r--r--include/linux/fsl_devices.h1
-rw-r--r--include/linux/fsl_ifc.h (renamed from arch/powerpc/include/asm/fsl_ifc.h)42
-rw-r--r--include/linux/hwmon.h10
-rw-r--r--include/linux/mtd/spi-nor.h222
-rw-r--r--include/linux/of.h2
-rw-r--r--include/linux/of_irq.h6
-rw-r--r--include/linux/of_pci.h36
-rw-r--r--include/sound/pcm.h2
-rw-r--r--kernel/irq/irqdomain.c37
-rw-r--r--lib/Kconfig3
-rw-r--r--lib/Makefile2
-rw-r--r--lib/rheap.c (renamed from arch/powerpc/lib/rheap.c)128
-rw-r--r--sound/core/pcm_lib.c38
-rw-r--r--sound/soc/fsl/Kconfig28
-rw-r--r--sound/soc/fsl/Makefile8
-rw-r--r--sound/soc/fsl/fsl_sai.c609
-rw-r--r--sound/soc/fsl/fsl_sai.h114
-rw-r--r--sound/soc/fsl/vf610-sgtl5000.c169
240 files changed, 18031 insertions, 3652 deletions
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.
diff --git a/Documentation/devicetree/bindings/clock/ls1021a-clock.txt b/Documentation/devicetree/bindings/clock/ls1021a-clock.txt
new file mode 100644
index 0000000..e53d976
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/ls1021a-clock.txt
@@ -0,0 +1,27 @@
+Gating clock bindings for Freescale LS1021A SOC
+
+Required properties:
+- compatible: Should be "fsl,ls1021a-gate"
+- reg: Address and length of the register set
+- #clock-cells: Should be <1>
+
+The clock consumers should specify the desired clock by having one clock
+ID in its "clocks" phandle cell.
+Please see include/dt-bindings/clock/ls1021a-clock.h for the full list of
+LS1021A clock IDs.
+
+Example:
+
+gate: gate@1ee0000 {
+ compatible = "fsl,ls1021a-gate";
+ reg = <0x0 0x1ee0000 0x0 0x10000>;
+ #clock-cells = <1>;
+};
+
+wdog0: wdog@2ad0000 {
+ compatible = "fsl,ls1021a-wdt", "fsl,imx21-wdt";
+ reg = <0x0 0x2ad0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gate LS1021A_CLK_WDOG12_EN>;
+ clock-names = "wdog12_en";
+};
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/Documentation/devicetree/bindings/powerpc/fsl/ifc.txt b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt
index d5e3704..ee6226b 100644
--- a/Documentation/devicetree/bindings/powerpc/fsl/ifc.txt
+++ b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt
@@ -18,6 +18,8 @@ Properties:
interrupt (NAND_EVTER_STAT). If there is only one,
that interrupt reports both types of event.
+- little-endian : If this property is absent, the big-endian mode will
+ be in use as default for registers.
- ranges : Each range corresponds to a single chipselect, and covers
the entire access window as configured.
diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
new file mode 100644
index 0000000..7e1dbaf
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
@@ -0,0 +1,51 @@
+* 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,
+ 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.)
+
+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 {
+ 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 {
+ ....
+ };
+};
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.
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 = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */
+ <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* 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/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
new file mode 100644
index 0000000..3899d6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
@@ -0,0 +1,52 @@
+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
+- #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.
+- big-endian: Boolean property, required if the FTM PWM registers use a big-
+ endian rather than little-endian layout.
+
+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>;
+ big-endian;
+};
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;
+};
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,<soc>-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
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/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
index a1fb303..cbbe16e 100644
--- a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
+++ b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
@@ -10,6 +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.
+
+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 {
@@ -24,6 +30,7 @@ dspi0@4002c000 {
bus-num = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_dspi0_1>;
+ big-endian;
status = "okay";
sflash: at26df081a@0 {
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;
+};
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/Documentation/devicetree/bindings/video/fsl-sii902x.txt b/Documentation/devicetree/bindings/video/fsl-sii902x.txt
new file mode 100644
index 0000000..c513a41
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/fsl-sii902x.txt
@@ -0,0 +1,17 @@
+Device-Tree bindings for framebuffer hdmi driver
+
+Required properties:
+- compatible: Should be "fsl,sii902x".
+- reg: The I2C address of the device.
+- interrupts: Interrupt number to the cpu.
+
+Example:
+
+&i2c1 {
+ status = "okay";
+ hdmi: sii9022a@39 {
+ compatible = "fsl,sii902x";
+ reg = <0x39>;
+ interrupts = <GIC_SPI 167 IRQ_TYPE_EDGE_RISING>;
+ };
+};
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 <linux@roeck-us.net>
+
+
+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/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 802720e..282026b 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -144,7 +144,9 @@ dtb-$(CONFIG_ARCH_MXC) += \
imx6q-sbc6x.dtb \
imx6q-wandboard.dtb \
imx6sl-evk.dtb \
- vf610-twr.dtb
+ vf610-twr.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-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts
new file mode 100644
index 0000000..7df7dcf
--- /dev/null
+++ b/arch/arm/boot/dts/ls1021a-qds.dts
@@ -0,0 +1,423 @@
+/*
+ * 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";
+
+ 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;
+ };
+
+ clocks {
+ sys_mclk: clock {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24576000>;
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
+ sound {
+ compatible = "fsl,vf610-sgtl5000";
+ simple-audio-card,name = "FSL-VF610-TWR-BOARD";
+ simple-audio-card,routing =
+ "MIC_IN", "Microphone Jack",
+ "Microphone Jack", "Mic Bias",
+ "LINE_IN", "Line In Jack",
+ "Headphone Jack", "HP_OUT",
+ "Speaker Ext", "LINE_OUT";
+
+ simple-audio-card,cpu = <&sai2>;
+
+ simple-audio-card,codec = <&codec>;
+ };
+
+ 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 = <3000000>;
+ spi-cpol;
+ spi-cpha;
+ reg = <0>;
+ };
+};
+
+&duart0 {
+ status = "okay";
+};
+
+&duart1 {
+ status = "okay";
+};
+
+&enet0 {
+ tbi-handle = <&tbi0>;
+ phy-handle = <&sgmii_phy1c>;
+ phy-connection-type = "sgmii";
+ status = "okay";
+};
+
+&enet1 {
+ tbi-handle = <&tbi0>;
+ phy-handle = <&sgmii_phy1d>;
+ phy-connection-type = "sgmii";
+ status = "okay";
+};
+
+&enet2 {
+ phy-handle = <&rgmii_phy3>;
+ phy-connection-type = "rgmii-id";
+ status = "okay";
+};
+
+&ftm0 {
+ 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 = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
+
+ 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>;
+
+ codec: sgtl5000@2a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x2a>;
+ VDDA-supply = <&reg_3p3v>;
+ VDDIO-supply = <&reg_3p3v>;
+ clocks = <&sys_mclk 1>;
+ };
+ };
+ };
+};
+
+&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 = "fsl,ls1021aqds-fpga", "fsl,fpga-qixis";
+ #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>;
+ rgmii_phy1: ethernet-phy@1 {
+ reg = <0x1>;
+ };
+ };
+ ls1021amdio1: mdio@20 {
+ reg = <0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ rgmii_phy2: ethernet-phy@2 {
+ reg = <0x2>;
+ };
+ };
+ ls1021amdio2: mdio@40 {
+ reg = <0x40>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ rgmii_phy3: ethernet-phy@3 {
+ reg = <0x3>;
+ };
+ };
+ ls1021amdio3: mdio@60 {
+ reg = <0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ sgmii_phy1c: ethernet-phy@1c {
+ reg = <0x1c>;
+ };
+ };
+ ls1021amdio4: mdio@80 {
+ reg = <0x80>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ sgmii_phy1d: ethernet-phy@1d {
+ reg = <0x1d>;
+ };
+ };
+
+ };
+
+ };
+};
+
+&lpuart0 {
+ status = "okay";
+};
+
+&mdio0 {
+ tbi0: tbi-phy@8 {
+ reg = <0x8>;
+ device_type = "tbi-phy";
+ };
+};
+
+&uqe {
+ tdma: ucc@2000 {
+ compatible = "fsl,ucc-tdm";
+ rx-clock-name = "brg1";
+ tx-clock-name = "brg1";
+ fsl,rx-sync-clock = "none";
+ fsl,tx-sync-clock = "none";
+ fsl,tx-timeslot = <0xfffffffe>;
+ fsl,rx-timeslot = <0xfffffffe>;
+ fsl,tdm-framer-type = "e1";
+ fsl,tdm-mode = "internal-loopback";
+ 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";
+ };
+};
+
+&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,s25fl128s";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ };
+
+ qflash1: s25fl128s@1 {
+ compatible = "spansion,s25fl128s";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <20000000>;
+ reg = <1>;
+ };
+};
+
+&can0 {
+ status = "okay";
+};
+
+&can1 {
+ status = "okay";
+};
+
+&sai2 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts
new file mode 100755
index 0000000..7f6ed3d
--- /dev/null
+++ b/arch/arm/boot/dts/ls1021a-twr.dts
@@ -0,0 +1,299 @@
+/*
+ * 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";
+
+ aliases {
+ enet2_rgmii_phy = &rgmii_phy1;
+ enet0_sgmii_phy = &sgmii_phy2;
+ enet1_sgmii_phy = &sgmii_phy0;
+ };
+
+ clocks {
+ sys_mclk: clock {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24576000>;
+ };
+ };
+
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_3p3v: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
+ sound {
+ compatible = "fsl,vf610-sgtl5000";
+ simple-audio-card,name = "FSL-VF610-TWR-BOARD";
+ simple-audio-card,routing =
+ "MIC_IN", "Microphone Jack",
+ "Microphone Jack", "Mic Bias",
+ "LINE_IN", "Line In Jack",
+ "Headphone Jack", "HP_OUT",
+ "Speaker Ext", "LINE_OUT";
+
+ simple-audio-card,cpu = <&sai1>;
+
+ simple-audio-card,codec = <&codec>;
+ };
+};
+
+&dcu0 {
+ display = <&display>;
+ status = "okay";
+
+ 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 = <2>;
+ vfront-porch = <2>;
+ hsync-len = <41>;
+ vsync-len = <4>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ };
+ };
+ };
+};
+
+&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 = <&sgmii_phy2>;
+ phy-connection-type = "sgmii";
+ status = "okay";
+};
+
+&enet1 {
+ tbi-handle = <&tbi1>;
+ phy-handle = <&sgmii_phy0>;
+ phy-connection-type = "sgmii";
+ status = "okay";
+};
+
+&enet2 {
+ phy-handle = <&rgmii_phy1>;
+ phy-connection-type = "rgmii-id";
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+};
+
+&i2c1 {
+ status = "okay";
+ codec: sgtl5000@a {
+ compatible = "fsl,sgtl5000";
+ reg = <0x0a>;
+ VDDA-supply = <&reg_3p3v>;
+ VDDIO-supply = <&reg_3p3v>;
+ clocks = <&sys_mclk 1>;
+ };
+
+ hdmi: sii9022a@39 {
+ compatible = "fsl,sii902x";
+ reg = <0x39>;
+ interrupts = <GIC_SPI 167 IRQ_TYPE_EDGE_RISING>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+ monitor: ltc2945@67 {
+ reg = <0x67>;
+ };
+};
+
+&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 {
+ sgmii_phy0: ethernet-phy@0 {
+ reg = <0x0>;
+ };
+ rgmii_phy1: ethernet-phy@1 {
+ reg = <0x1>;
+ };
+ sgmii_phy2: ethernet-phy@2 {
+ reg = <0x2>;
+ };
+ tbi1: tbi-phy@1f {
+ reg = <0x1f>;
+ device_type = "tbi-phy";
+ };
+};
+
+&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";
+};
+
+&pwm7 {
+ status = "okay";
+};
+
+&qspi {
+ num-cs = <2>;
+ status = "okay";
+
+ qflash0: n25q128a13@0 {
+ compatible = "micron,n25q128a13";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ };
+
+ qflash1: n25q128a13@1 {
+ compatible = "micron,n25q128a13";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <20000000>;
+ reg = <1>;
+ };
+};
+
+&sai1 {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
new file mode 100644
index 0000000..7092336
--- /dev/null
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -0,0 +1,897 @@
+/*
+ * 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 <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ 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;
+ sysclk = &sysclk;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+
+ };
+
+ 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>;
+ clocks = <&cluster1_clk>;
+ };
+
+ cpu@f01 {
+ compatible = "arm,cortex-a7";
+ device_type = "cpu";
+ reg = <0xf01>;
+ clocks = <&cluster1_clk>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>;
+ };
+
+ pmu {
+ compatible = "arm,cortex-a7-pmu";
+ interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ device_type = "soc";
+ 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 = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
+
+ };
+
+ ifc: ifc@1530000 {
+ compatible = "fsl,ifc", "simple-bus";
+ reg = <0x0 0x1530000 0x0 0x10000>;
+ interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dcfg: dcfg@1ee0000 {
+ compatible = "fsl,ls1021a-dcfg";
+ reg = <0x0 0x1ee0000 0x0 0x10000>;
+ };
+
+ qspi: quadspi@1550000 {
+ compatible = "fsl,ls1-qspi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0x1550000 0x0 0x10000>,
+ <0x0 0x40000000 0x0 0x10000000>;
+ reg-names = "QuadSPI", "QuadSPI-memory";
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+ 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", "syscon";
+ reg = <0x0 0x1570000 0x0 0x10000>;
+ big-endian;
+ };
+
+ 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 = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
+
+ 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 = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ 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 = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ 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 = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ 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 = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ };
+
+ clockgen: clocking@1ee1000 {
+ compatible = "fsl,ls1021a-clockgen";
+ reg = <0x0 0x1ee1000 0x0 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ sysclk: sysclk {
+ compatible = "fixed-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-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 = <0>;
+ reg = <0x0>;
+ clock-names = "pll1cga", "pll1cga-div2", "pll1cga-div4";
+ clocks = <&cga_pll1 0>, <&cga_pll1 1>, <&cga_pll1 2>;
+ clock-output-names = "cluster1-clk";
+ };
+
+ };
+
+ rcpm: rcpm@1ee2000 {
+ compatible = "fsl,ls1021a-rcpm", "fsl,qoriq-rcpm-2.1";
+ reg = <0x0 0x1ee2000 0x0 0x10000>;
+ };
+
+ dspi0: dspi@2100000 {
+ compatible = "fsl,vf610-dspi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0x2100000 0x0 0x10000>;
+ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "dspi";
+ clocks = <&platform_clk 1>;
+ spi-num-chipselects = <5>;
+ big-endian;
+ tcfq-mode;
+ status = "disabled";
+ };
+
+ dspi1: dspi@2110000 {
+ compatible = "fsl,vf610-dspi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0x2110000 0x0 0x10000>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "i2c";
+ clocks = <&platform_clk 1>;
+ status = "disabled";
+ };
+
+ duart0: serial@21c0500 {
+ compatible = "fsl,16550-FIFO64";
+ reg = <0x0 0x21c0500 0x0 0x100>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <0>;
+ fifo-size = <63>;
+ status = "disabled";
+ };
+
+ duart1: serial@21c0600 {
+ compatible = "fsl,16550-FIFO64";
+ reg = <0x0 0x21c0600 0x0 0x100>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <0>;
+ fifo-size = <63>;
+ status = "disabled";
+ };
+
+ duart2: serial@21d0500 {
+ compatible = "fsl,16550-FIFO64";
+ reg = <0x0 0x21d0500 0x0 0x100>;
+ interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <0>;
+ fifo-size = <63>;
+ status = "disabled";
+ };
+
+ duart3: serial@21d0600 {
+ compatible = "fsl,16550-FIFO64";
+ reg = <0x0 0x21d0600 0x0 0x100>;
+ interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+ clock-frequency = <0>;
+ fifo-size = <63>;
+ 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>;
+ interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&sysclk>;
+ clock-names = "ipg";
+ status = "disabled";
+ };
+
+ lpuart1: serial@2960000 {
+ compatible = "fsl,ls1021a-lpuart";
+ reg = <0x0 0x2960000 0x0 0x1000>;
+ interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "ipg";
+ status = "disabled";
+ };
+
+ lpuart2: serial@2970000 {
+ compatible = "fsl,ls1021a-lpuart";
+ reg = <0x0 0x2970000 0x0 0x1000>;
+ interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "ipg";
+ status = "disabled";
+ };
+
+ lpuart3: serial@2980000 {
+ compatible = "fsl,ls1021a-lpuart";
+ reg = <0x0 0x2980000 0x0 0x1000>;
+ interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "ipg";
+ status = "disabled";
+ };
+
+ lpuart4: serial@2990000 {
+ compatible = "fsl,ls1021a-lpuart";
+ reg = <0x0 0x2990000 0x0 0x1000>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "ipg";
+ status = "disabled";
+ };
+
+ lpuart5: serial@29a0000 {
+ compatible = "fsl,ls1021a-lpuart";
+ reg = <0x0 0x29a0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "ipg";
+ status = "disabled";
+ };
+
+ gpio1: gpio@2300000 {
+ compatible = "fsl,ls1021a-gpio";
+ reg = <0x0 0x2300000 0x0 0x10000>;
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio2: gpio@2310000 {
+ compatible = "fsl,ls1021a-gpio";
+ reg = <0x0 0x2310000 0x0 0x10000>;
+ interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio3: gpio@2320000 {
+ compatible = "fsl,ls1021a-gpio";
+ reg = <0x0 0x2320000 0x0 0x10000>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio4: gpio@2330000 {
+ compatible = "fsl,ls1021a-gpio";
+ reg = <0x0 0x2330000 0x0 0x10000>;
+ interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ ftm0: ftm0@29d0000 {
+ compatible = "fsl,ftm-alarm";
+ reg = <0x0 0x29d0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
+ big-endian;
+ status = "disabled";
+ };
+
+ pwm3: ftm@2a00000 {
+ compatible = "fsl,vf610-ftm-pwm";
+ #pwm-cells = <3>;
+ reg = <0x0 0x2a00000 0x0 0x10000>;
+ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "wdog";
+ big-endian;
+ };
+
+ sai1: sai@2b50000 {
+ compatible = "fsl,vf610-sai";
+ reg = <0x0 0x2b50000 0x0 0x10000>;
+ interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-names = "sai";
+ dma-names = "tx", "rx";
+ dmas = <&edma0 1 47>,
+ <&edma0 1 46>;
+ big-endian-regs;
+ status = "disabled";
+ };
+
+ sai2: sai@2b60000 {
+ compatible = "fsl,vf610-sai";
+ reg = <0x0 0x2b60000 0x0 0x10000>;
+ interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>;
+ 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,ls1021a-dcu";
+ reg = <0x0 0x2ce0000 0x0 0x10000>;
+ interrupts = <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 0>;
+ clock-names = "dcu";
+ scfg-controller = <&scfg>;
+ big-endian;
+ status = "disabled";
+ };
+
+ mdio0: mdio@2d24000 {
+ compatible = "gianfar";
+ device_type = "mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0x2d24000 0x0 0x4000>;
+ };
+
+ 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 = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ };
+
+ 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 = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ };
+
+ 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 = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
+
+ can0: can@2a70000 {
+ compatible = "fsl,ls1021a-flexcan";
+ reg = <0x0 0x2a70000 0x0 0x1000>;
+ interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-frequency = <150000000>;
+ clock-names = "per";
+ little-endian;
+ status = "disabled";
+ };
+
+ can1: can@2a80000 {
+ compatible = "fsl,ls1021a-flexcan";
+ reg = <0x0 0x2a80000 0x0 0x1000>;
+ interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-frequency = <150000000>;
+ clock-names = "per";
+ little-endian;
+ status = "disabled";
+ };
+
+ can2: can@2a90000 {
+ compatible = "fsl,ls1021a-flexcan";
+ reg = <0x0 0x2a90000 0x0 0x1000>;
+ interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-frequency = <150000000>;
+ clock-names = "per";
+ little-endian;
+ status = "disabled";
+ };
+
+ can3: can@2aa0000 {
+ compatible = "fsl,ls1021a-flexcan";
+ reg = <0x0 0x2aa0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&platform_clk 1>;
+ clock-frequency = <150000000>;
+ clock-names = "per";
+ little-endian;
+ status = "disabled";
+ };
+
+ usb@8600000 {
+ compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr";
+ reg = <0x0 0x8600000 0x0 0x1000>;
+ interrupts = <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
+ dr_mode = "host";
+ phy_type = "ulpi";
+ };
+
+ usb3@3100000 {
+ compatible = "snps,dwc3";
+ reg = <0x0 0x3100000 0x0 0x10000>;
+ interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
+ 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 = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */
+ <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* 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 = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 182 IRQ_TYPE_LEVEL_HIGH>;
+ 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 {
+ #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>;
+ };
+ };
+};
diff --git a/arch/arm/configs/ls1021a_defconfig b/arch/arm/configs/ls1021a_defconfig
new file mode 100644
index 0000000..58a5241
--- /dev/null
+++ b/arch/arm/configs/ls1021a_defconfig
@@ -0,0 +1,180 @@
+# 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_MTD_NAND_FSL_IFC=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_GIANFAR=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_IRQ_DOMAIN_DEBUG=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_GPIOLIB=y
+CONFIG_OF_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_MPC8XXX=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
+CONFIG_LEDS_TRIGGERS=y
+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
+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
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_MINORS=8
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_ESDHC=y
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index fcc1b5b..b6c3b52 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -263,7 +263,7 @@
* you cannot return to the original mode.
*/
.macro safe_svcmode_maskall reg:req
-#if __LINUX_ARM_ARCH__ >= 6
+#if __LINUX_ARM_ARCH__ >= 6 && !defined(CONFIG_CPU_V7M)
mrs \reg , cpsr
eor \reg, \reg, #HYP_MODE
tst \reg, #MODE_MASK
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index cc8a2ca..3dac4cc 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -436,4 +436,50 @@ static inline void __sync_cache_range_r(volatile void *p, size_t size)
#define sync_cache_w(ptr) __sync_cache_range_w(ptr, sizeof *(ptr))
#define sync_cache_r(ptr) __sync_cache_range_r(ptr, sizeof *(ptr))
+/*
+ * Disabling cache access for one CPU in an ARMv7 SMP system is tricky.
+ * To do so we must:
+ *
+ * - Clear the SCTLR.C bit to prevent further cache allocations
+ * - Flush the desired level of cache
+ * - Clear the ACTLR "SMP" bit to disable local coherency
+ *
+ * ... and so without any intervening memory access in between those steps,
+ * not even to the stack.
+ *
+ * WARNING -- After this has been called:
+ *
+ * - No ldrex/strex (and similar) instructions must be used.
+ * - The CPU is obviously no longer coherent with the other CPUs.
+ * - This is unlikely to work as expected if Linux is running non-secure.
+ *
+ * Note:
+ *
+ * - This is known to apply to several ARMv7 processor implementations,
+ * however some exceptions may exist. Caveat emptor.
+ *
+ * - The clobber list is dictated by the call to v7_flush_dcache_*.
+ * fp is preserved to the stack explicitly prior disabling the cache
+ * since adding it to the clobber list is incompatible with having
+ * CONFIG_FRAME_POINTER=y. ip is saved as well if ever r12-clobbering
+ * trampoline are inserted by the linker and to keep sp 64-bit aligned.
+ */
+#define v7_exit_coherency_flush(level) \
+ asm volatile( \
+ "stmfd sp!, {fp, ip}\n\t" \
+ "mrc p15, 0, r0, c1, c0, 0 @ get SCTLR\n\t" \
+ "bic r0, r0, #"__stringify(CR_C)"\n\t" \
+ "mcr p15, 0, r0, c1, c0, 0 @ set SCTLR\n\t" \
+ "isb\n\t" \
+ "bl v7_flush_dcache_"__stringify(level)"\n\t" \
+ "clrex\n\t" \
+ "mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \
+ "bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \
+ "mcr p15, 0, r0, c1, c0, 1 @ set ACTLR\n\t" \
+ "isb\n\t" \
+ "dsb\n\t" \
+ "ldmfd sp!, {fp, ip}" \
+ : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+ "r9", "r10", "lr", "memory")
+
#endif
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/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S
index db1536b..101a2b4 100644
--- a/arch/arm/kernel/sleep.S
+++ b/arch/arm/kernel/sleep.S
@@ -130,6 +130,10 @@ ENDPROC(cpu_resume_after_mmu)
.data
.align
ENTRY(cpu_resume)
+#ifdef CONFIG_ARM_VIRT_EXT
+ bl __hyp_stub_install_secondary
+#endif
+ safe_svcmode_maskall r1
mov r1, #0
ALT_SMP(mrc p15, 0, r0, c0, c0, 5)
ALT_UP_B(1f)
@@ -147,7 +151,6 @@ ENTRY(cpu_resume)
ldr r0, [r0, #SLEEP_SAVE_SP_PHYS]
ldr r0, [r0, r1, lsl #2]
- setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off
@ load phys pgd, stack, resume fn
ARM( ldmia r0!, {r1, sp, pc} )
THUMB( ldmia r0!, {r1, r2, r3} )
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 <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/errno.h>
#include <linux/export.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/time.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/profile.h>
#include <linux/sched.h>
+#include <linux/sched_clock.h>
#include <linux/smp.h>
+#include <linux/time.h>
#include <linux/timex.h>
-#include <linux/errno.h>
-#include <linux/profile.h>
#include <linux/timer.h>
-#include <linux/clocksource.h>
-#include <linux/irq.h>
-#include <linux/sched_clock.h>
-#include <asm/thread_info.h>
-#include <asm/stacktrace.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
+#include <asm/stacktrace.h>
+#include <asm/thread_info.h>
#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();
+ }
}
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 29a8af6..16b5206 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -846,6 +846,25 @@ 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
+ select FSL_SLEEP_FSM if PM
+
+ 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..c5b7cca 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
ifeq ($(CONFIG_PM),y)
obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
+obj-$(CONFIG_SOC_LS1021A) += pm-ls1.o sleep-ls1.o
endif
# i.MX5 based machines
@@ -114,4 +115,8 @@ 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-$(CONFIG_SOC_LS1021A) += clk-ls1021a.o
+
obj-y += devices/
diff --git a/arch/arm/mach-imx/clk-ls1021a.c b/arch/arm/mach-imx/clk-ls1021a.c
new file mode 100644
index 0000000..bba7c9a
--- /dev/null
+++ b/arch/arm/mach-imx/clk-ls1021a.c
@@ -0,0 +1,191 @@
+/*
+ * 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 <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <dt-bindings/clock/ls1021a-clock.h>
+
+#include "clk.h"
+
+static struct clk *clks[LS1021A_CLK_END];
+static struct clk_onecell_data clk_data;
+
+/* The DCFG registers are in big endian mode on LS1021A SoC */
+static u8 ls1021a_clk_shift_be(u8 shift)
+{
+ u8 m, n;
+
+ m = shift / 8;
+ n = shift % 8;
+
+ return (u8)((3 - m) * 8 + n);
+}
+
+static void __init ls1021a_clocks_init(struct device_node *np)
+{
+ static void __iomem *dcfg_base;
+ int i;
+
+#define DCFG_CCSR_DEVDISR1 (dcfg_base + 0x70)
+#define DCFG_CCSR_DEVDISR2 (dcfg_base + 0x74)
+#define DCFG_CCSR_DEVDISR3 (dcfg_base + 0x78)
+#define DCFG_CCSR_DEVDISR4 (dcfg_base + 0x7c)
+#define DCFG_CCSR_DEVDISR5 (dcfg_base + 0x80)
+#define DCFG_CCSR_RSTRQMR1 (dcfg_base + 0xc0)
+
+ if (dcfg_base) {
+ pr_warn("LS1021A gate clk has already added.\n");
+ return;
+ }
+
+ dcfg_base = of_iomap(np, 0);
+
+ BUG_ON(!dcfg_base);
+
+ clks[LS1021A_CLK_PBL_EN] = ls1021a_clk_gate("pbl_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(0));
+ clks[LS1021A_CLK_ESDHC_EN] = ls1021a_clk_gate("esdhc_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(2));
+ clks[LS1021A_CLK_DMA1_EN] = ls1021a_clk_gate("dma1_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(8));
+ clks[LS1021A_CLK_DMA2_EN] = ls1021a_clk_gate("dma2_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(9));
+ clks[LS1021A_CLK_USB3_PHY_EN] = ls1021a_clk_gate("usb3_phy_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(12));
+ clks[LS1021A_CLK_USB2_EN] = ls1021a_clk_gate("usb2_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(13));
+ clks[LS1021A_CLK_SATA_EN] = ls1021a_clk_gate("sata_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(16));
+ clks[LS1021A_CLK_USB3_EN] = ls1021a_clk_gate("usb3_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(17));
+ clks[LS1021A_CLK_SEC_EN] = ls1021a_clk_gate("sec_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(22));
+ clks[LS1021A_CLK_2D_ACE_EN] = ls1021a_clk_gate("2d_ace_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(30));
+ clks[LS1021A_CLK_QE_EN] = ls1021a_clk_gate("qe_en", "dummy",
+ DCFG_CCSR_DEVDISR1,
+ ls1021a_clk_shift_be(31));
+
+ clks[LS1021A_CLK_ETSEC1_EN] = ls1021a_clk_gate("etsec1_en", "dummy",
+ DCFG_CCSR_DEVDISR2,
+ ls1021a_clk_shift_be(0));
+ clks[LS1021A_CLK_ETSEC2_EN] = ls1021a_clk_gate("etsec2_en", "dummy",
+ DCFG_CCSR_DEVDISR2,
+ ls1021a_clk_shift_be(1));
+ clks[LS1021A_CLK_ETSEC3_EN] = ls1021a_clk_gate("etsec3_en", "dummy",
+ DCFG_CCSR_DEVDISR2,
+ ls1021a_clk_shift_be(2));
+
+ clks[LS1021A_CLK_PEX1_EN] = ls1021a_clk_gate("pex1_en", "dummy",
+ DCFG_CCSR_DEVDISR3,
+ ls1021a_clk_shift_be(0));
+ clks[LS1021A_CLK_PEX2_EN] = ls1021a_clk_gate("pex2_en", "dummy",
+ DCFG_CCSR_DEVDISR3,
+ ls1021a_clk_shift_be(1));
+
+ clks[LS1021A_CLK_DUART1_EN] = ls1021a_clk_gate("duart1_en", "dummy",
+ DCFG_CCSR_DEVDISR4,
+ ls1021a_clk_shift_be(2));
+ clks[LS1021A_CLK_DUART2_EN] = ls1021a_clk_gate("duart2_en", "dummy",
+ DCFG_CCSR_DEVDISR4,
+ ls1021a_clk_shift_be(3));
+ clks[LS1021A_CLK_QSPI_EN] = ls1021a_clk_gate("qspi_en", "dummy",
+ DCFG_CCSR_DEVDISR4,
+ ls1021a_clk_shift_be(4));
+
+ clks[LS1021A_CLK_DDR_EN] = ls1021a_clk_gate("ddr_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(0));
+ clks[LS1021A_CLK_OCRAM1_EN] = ls1021a_clk_gate("ocram1_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(4));
+ clks[LS1021A_CLK_IFC_EN] = ls1021a_clk_gate("ifc_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(8));
+ clks[LS1021A_CLK_GPIO_EN] = ls1021a_clk_gate("gpio_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(9));
+ clks[LS1021A_CLK_DBG_EN] = ls1021a_clk_gate("dbg_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(10));
+ clks[LS1021A_CLK_FLEXCAN1_EN] = ls1021a_clk_gate("flexcan1_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(12));
+ clks[LS1021A_CLK_FLEXCAN234_EN] = ls1021a_clk_gate("flexcan234_en",
+ "dummy", DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(13));
+ /* For flextimer 2/3/4/5/6/7/8 */
+ clks[LS1021A_CLK_FLEXTIMER_EN] = ls1021a_clk_gate("flextimer_en",
+ "dummy", DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(14));
+ clks[LS1021A_CLK_SECMON_EN] = ls1021a_clk_gate("secmon_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(17));
+ clks[LS1021A_CLK_WDOG_EN] = ls1021a_clk_gate("wdog_en", "dummy",
+ DCFG_CCSR_RSTRQMR1,
+ ls1021a_clk_shift_be(22));
+ clks[LS1021A_CLK_WDOG12_EN] = ls1021a_clk_gate("wdog12_en", "wdog_en",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(21));
+ clks[LS1021A_CLK_I2C23_EN] = ls1021a_clk_gate("i2c23_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(22));
+ /* For SAI 1/2/3/4 */
+ clks[LS1021A_CLK_SAI_EN] = ls1021a_clk_gate("sai_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(23));
+ /* For lpuart 2/3/4/5/6 */
+ clks[LS1021A_CLK_LPUART_EN] = ls1021a_clk_gate("lpuart_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(24));
+ clks[LS1021A_CLK_DSPI12_EN] = ls1021a_clk_gate("dspi12_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(25));
+ clks[LS1021A_CLK_ASRC_EN] = ls1021a_clk_gate("asrc_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(26));
+ clks[LS1021A_CLK_SPDIF_EN] = ls1021a_clk_gate("spdif_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(27));
+ clks[LS1021A_CLK_I2C1_EN] = ls1021a_clk_gate("i2c1_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(29));
+ clks[LS1021A_CLK_LPUART1_EN] = ls1021a_clk_gate("lpuart1_en", "dummy",
+ DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(30));
+ clks[LS1021A_CLK_FLEXTIMER1_EN] = ls1021a_clk_gate("flextimer1_en",
+ "dummy", DCFG_CCSR_DEVDISR5,
+ ls1021a_clk_shift_be(31));
+
+ for (i = 0; i < LS1021A_CLK_END; i++) {
+ if (IS_ERR(clks[i])) {
+ pr_err("LS1021A clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ BUG();
+ }
+ }
+
+ /* Add the clocks to provider list */
+ clk_data.clks = clks;
+ clk_data.clk_num = LS1021A_CLK_END;
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-gate", ls1021a_clocks_init);
diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h
index 048c5ad8..2a58d01 100644
--- a/arch/arm/mach-imx/clk.h
+++ b/arch/arm/mach-imx/clk.h
@@ -86,6 +86,14 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent,
shift, 0, &imx_ccm_lock);
}
+static inline struct clk *ls1021a_clk_gate(const char *name, const char *parent,
+ void __iomem *reg, u8 shift)
+{
+ return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT |
+ CLK_IGNORE_UNUSED, reg, shift,
+ CLK_GATE_SET_TO_DISABLE, &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, int num_parents)
{
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 4517fd7..b34ff2e 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -115,6 +115,7 @@ void tzic_handle_irq(struct pt_regs *);
extern void imx_enable_cpu(int cpu, bool enable);
extern void imx_set_cpu_jump(int cpu, void *jump_addr);
extern u32 imx_get_cpu_arg(int cpu);
+extern u32 ls1_get_cpu_arg(int cpu);
extern void imx_set_cpu_arg(int cpu, u32 arg);
extern void v7_cpu_resume(void);
#ifdef CONFIG_SMP
@@ -122,6 +123,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) {}
@@ -144,6 +146,9 @@ extern void imx6q_set_chicken_bit(void);
extern void imx_cpu_die(unsigned int cpu);
extern int imx_cpu_kill(unsigned int cpu);
+extern void ls1021a_cpu_die(unsigned int cpu);
+extern int ls1021a_cpu_kill(unsigned int cpu);
+
#ifdef CONFIG_PM
extern void imx6q_pm_init(void);
extern void imx5_pm_init(void);
@@ -165,5 +170,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/hotplug.c b/arch/arm/mach-imx/hotplug.c
index 3daf1ed..bbbb96e 100644
--- a/arch/arm/mach-imx/hotplug.c
+++ b/arch/arm/mach-imx/hotplug.c
@@ -14,6 +14,9 @@
#include <linux/jiffies.h>
#include <asm/cp15.h>
#include <asm/proc-fns.h>
+#include<asm/smp.h>
+#include<asm/smp_plat.h>
+#include<asm/cacheflush.h>
#include "common.h"
@@ -38,6 +41,22 @@ static inline void cpu_enter_lowpower(void)
: "cc");
}
+static inline void cpu_leave_lowpower(void)
+{
+ unsigned int v;
+
+ asm volatile(
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " orr %0, %0, %1\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " orr %0, %0, %2\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ : "=&r" (v)
+ : "Ir" (CR_C), "Ir" (0x40)
+ : "cc");
+}
+
/*
* platform-specific code to shutdown a CPU
*
@@ -66,3 +85,41 @@ int imx_cpu_kill(unsigned int cpu)
imx_set_cpu_arg(cpu, 0);
return 1;
}
+
+/*
+ * For LS102x platforms, shutdowning a CPU is not supported by hardware.
+ * So we just put the offline CPU into lower-power state here.
+ */
+void __ref ls1021a_cpu_die(unsigned int cpu)
+{
+ v7_exit_coherency_flush(louis);
+
+ /*we are ready to enter lower-power state*/
+ wfi();
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ cpu_leave_lowpower();
+
+ /*
+ * Do not return to the idle loop - jump back to the secondary
+ * cpu initialisation. There's some initialisation which needs
+ * to be repeated to undo the effects of taking the CPU offline.
+ */
+ __asm__("mov sp, %0\n"
+ " mov fp, #0\n"
+ " b secondary_startup"
+ :
+ : "r" (task_stack_page(current) + THREAD_SIZE - 8));
+}
+
+int ls1021a_cpu_kill(unsigned int cpu)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(50);
+
+ while (!ls1_get_cpu_arg(cpu))
+ if (time_after(jiffies, timeout))
+ return 0;
+ return 1;
+}
diff --git a/arch/arm/mach-imx/mach-ls1021a.c b/arch/arm/mach-imx/mach-ls1021a.c
new file mode 100644
index 0000000..3895ffc
--- /dev/null
+++ b/arch/arm/mach-imx/mach-ls1021a.c
@@ -0,0 +1,31 @@
+/*
+ * 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 <linux/of_platform.h>
+#include <asm/mach/arch.h>
+
+#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")
+ .smp = smp_ops(ls1021a_smp_ops),
+ .init_machine = ls1021a_init_machine,
+ .dt_compat = ls1021a_dt_compat,
+ .restart = mxc_restart,
+MACHINE_END
diff --git a/arch/arm/mach-imx/platsmp.c b/arch/arm/mach-imx/platsmp.c
index 1f24c1f..c412178 100644
--- a/arch/arm/mach-imx/platsmp.c
+++ b/arch/arm/mach-imx/platsmp.c
@@ -16,6 +16,8 @@
#include <asm/page.h>
#include <asm/smp_scu.h>
#include <asm/mach/map.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include "common.h"
#include "hardware.h"
@@ -105,3 +107,37 @@ 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,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = ls1021a_cpu_die,
+ .cpu_kill = ls1021a_cpu_kill,
+#endif
+};
diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c
new file mode 100644
index 0000000..bdcd92b
--- /dev/null
+++ b/arch/arm/mach-imx/pm-ls1.c
@@ -0,0 +1,389 @@
+/*
+ * Support deep sleep feature for LS1
+ *
+ * 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 <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/cpu_pm.h>
+#include <asm/suspend.h>
+#include <asm/delay.h>
+#include <asm/cp15.h>
+#include <asm/cacheflush.h>
+#include <asm/idmap.h>
+
+#include "common.h"
+#include "sleep-ls1.h"
+
+#define FSL_SLEEP 0x1
+#define FSL_DEEP_SLEEP 0x2
+
+#define DCSR_EPU_EPSMCR15 0x278
+#define DCSR_EPU_EPECR0 0x300
+#define DCSR_RCPM_CG1CR0 0x31c
+#define DCSR_RCPM_CSTTACR0 0xb00
+
+#define CCSR_SCFG_DPSLPCR 0
+#define CCSR_SCFG_DPSLPCR_VAL 0x1
+#define CCSR_SCFG_PMCINTECR 0x160
+#define CCSR_SCFG_PMCINTLECR 0x164
+#define CCSR_SCFG_PMCINTSR 0x168
+#define CCSR_SCFG_SPARECR2 0x504
+#define CCSR_SCFG_SPARECR3 0x508
+
+#define CCSR_DCFG_CRSTSR 0x400
+#define CCSR_DCFG_CRSTSR_VAL 0x00000008
+
+#define CCSR_RCPM_POWMGTCSR 0x130
+#define CCSR_RCPM_POWMGTCSR_LPM20_REQ 0x00100000
+#define CCSR_RCPM_POWMGTCSR_LPM20_ST 0x00000200
+#define CCSR_RCPM_POWMGTCSR_P_LPM20_ST 0x00000100
+#define CCSR_RCPM_CLPCL10SETR 0x1c4
+#define CCSR_RCPM_CLPCL10SETR_C0 0x1
+#define CCSR_RCPM_IPPDEXPCR0 0x140
+#define CCSR_RCPM_IPPDEXPCR1 0x144
+
+#define QIXIS_CTL_SYS 0x5
+#define QIXIS_CTL_SYS_EVTSW_MASK 0x0c
+#define QIXIS_CTL_SYS_EVTSW_IRQ 0x04
+
+#define QIXIS_PWR_CTL2 0x21
+#define QIXIS_PWR_CTL2_PCTL 0x2
+
+#define OCRAM_BASE 0x10000000
+#define OCRAM_SIZE 0x10000 /* 64K */
+/* use the last page of SRAM */
+#define SRAM_CODE_BASE_PHY (OCRAM_BASE + OCRAM_SIZE - PAGE_SIZE)
+
+struct ls1_pm_baseaddr {
+ void __iomem *epu;
+ void __iomem *dcsr_rcpm1;
+ void __iomem *dcsr_rcpm2;
+ void __iomem *rcpm;
+ void __iomem *scfg;
+ void __iomem *dcfg;
+ void __iomem *fpga;
+ void __iomem *sram;
+};
+
+/* 128 bytes buffer for restoring data broke by DDR training initialization */
+#define DDR_BUF_SIZE 128
+static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64);
+static struct ls1_pm_baseaddr ls1_pm_base;
+/* supported sleep modes by the present platform */
+static unsigned int sleep_modes;
+
+static void ls1_pm_iomap(void)
+{
+ struct device_node *np;
+ void *base;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcsr-epu");
+ base = of_iomap(np, 0);
+ BUG_ON(!base);
+ ls1_pm_base.epu = base;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcsr-rcpm");
+ base = of_iomap(np, 0);
+ BUG_ON(!base);
+ ls1_pm_base.dcsr_rcpm1 = base;
+ base = of_iomap(np, 1);
+ BUG_ON(!base);
+ ls1_pm_base.dcsr_rcpm2 = base;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
+ base = of_iomap(np, 0);
+ BUG_ON(!base);
+ ls1_pm_base.scfg = base;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcfg");
+ base = of_iomap(np, 0);
+ BUG_ON(!base);
+ ls1_pm_base.dcfg = base;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,ls1021aqds-fpga");
+ base = of_iomap(np, 0);
+ BUG_ON(!base);
+ ls1_pm_base.fpga = base;
+
+ base = ioremap(SRAM_CODE_BASE_PHY, PAGE_SIZE);
+ BUG_ON(!base);
+ ls1_pm_base.sram = base;
+}
+
+static void ls1_pm_uniomap(void)
+{
+ iounmap(ls1_pm_base.epu);
+ iounmap(ls1_pm_base.dcsr_rcpm1);
+ iounmap(ls1_pm_base.dcsr_rcpm2);
+ iounmap(ls1_pm_base.scfg);
+ iounmap(ls1_pm_base.dcfg);
+ iounmap(ls1_pm_base.fpga);
+ iounmap(ls1_pm_base.sram);
+}
+
+
+static void ls1_setup_wakeup_source(void)
+{
+ /* enable wakeup interrupt during deep sleep */
+ iowrite32be(0xfcfc0000, ls1_pm_base.scfg + CCSR_SCFG_PMCINTECR);
+ iowrite32be(0, ls1_pm_base.scfg + CCSR_SCFG_PMCINTLECR);
+ /* clear PMC interrupt status */
+ iowrite32be(0xffffffff, ls1_pm_base.scfg + CCSR_SCFG_PMCINTSR);
+}
+
+static void ls1_clear_wakeup_source(void)
+{
+ /* disable wakeup interrupt during deep sleep */
+ iowrite32be(0, ls1_pm_base.scfg + CCSR_SCFG_PMCINTECR);
+ /* clear PMC interrupt status */
+ iowrite32be(0xffffffff, ls1_pm_base.scfg + CCSR_SCFG_PMCINTSR);
+}
+
+/* set IP powerdown exception, make them work during sleep/deep sleep */
+static void ls1_set_powerdown(void)
+{
+ iowrite32be(0x8000c000, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0);
+ iowrite32be(0xf0000000, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1);
+}
+
+static void ls1_save_ddr(void *base)
+{
+ u32 ddr_buff_addr;
+
+ /*
+ * DDR training initialization will break 128 bytes at the beginning
+ * of DDR, therefore, save them so that the bootloader will restore
+ * them. Assume that DDR is mapped to the address space started with
+ * CONFIG_PAGE_OFFSET.
+ */
+ memcpy(ddr_buff, (void *)CONFIG_PAGE_OFFSET, DDR_BUF_SIZE);
+
+ ddr_buff_addr = (u32)__pa(ddr_buff);
+
+ /*
+ * the bootloader will restore the first 128 bytes of DDR from
+ * the location indicated by the register SPARECR3
+ */
+ iowrite32(ddr_buff_addr, base + CCSR_SCFG_SPARECR3);
+}
+
+static void ls1_set_resume_entry(void *base)
+{
+ u32 resume_addr;
+
+ /* the bootloader will finally jump to this address to resume kernel */
+ resume_addr = (u32)(__pa(ls1_deepsleep_resume));
+
+ /* use the register SPARECR2 to save the return entry */
+ iowrite32(resume_addr, base + CCSR_SCFG_SPARECR2);
+}
+
+static void ls1_copy_sram_code(void)
+{
+ memcpy(ls1_pm_base.sram, ls1_start_fsm, ls1_sram_code_size);
+}
+
+static int ls1_start_deepsleep(unsigned long addr)
+{
+ typedef void (*ls1_deepsleep_t)(unsigned long);
+ ls1_deepsleep_t ls1_do_deepsleep_phy;
+
+ /* Switch to the identity mapping */
+ setup_mm_for_reboot();
+ v7_exit_coherency_flush(all);
+
+ ls1_do_deepsleep_phy =
+ (ls1_deepsleep_t)(unsigned long)virt_to_phys(ls1_do_deepsleep);
+ ls1_do_deepsleep_phy(addr);
+
+ /* never get here */
+ BUG();
+
+ return 0;
+}
+
+void ls1_fsm_setup(void)
+{
+ iowrite32be(0x00001001, ls1_pm_base.dcsr_rcpm1 + DCSR_RCPM_CSTTACR0);
+ iowrite32be(0x00000001, ls1_pm_base.dcsr_rcpm1 + DCSR_RCPM_CG1CR0);
+
+ fsl_epu_setup_default(ls1_pm_base.epu);
+
+ /*
+ * pull the MCKE signal(EVT4_B pin) low before enabling
+ * deep sleep signals by FPGA
+ */
+ iowrite32be(0x5, ls1_pm_base.epu + DCSR_EPU_EPECR0);
+
+ iowrite32be(0x76300000, ls1_pm_base.epu + DCSR_EPU_EPSMCR15);
+}
+
+static inline void ls1_clrsetbits_be32(void __iomem *addr, u32 clear, u32 set)
+{
+ u32 tmp;
+
+ tmp = ioread32be(addr);
+ tmp = (tmp & ~clear) | set;
+ iowrite32be(tmp, addr);
+}
+
+static void ls1_enter_deepsleep(void)
+{
+ u32 tmp;
+
+ /* disable QE device to make deep sleep work */
+ iowrite32be(0x00000001, ls1_pm_base.dcfg + 0x70);
+
+ /* save DDR data */
+ ls1_save_ddr(ls1_pm_base.scfg);
+
+ /* save kernel resume entry */
+ ls1_set_resume_entry(ls1_pm_base.scfg);
+
+ /* Request to put cluster 0 in PCL10 state */
+ ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_CLPCL10SETR,
+ CCSR_RCPM_CLPCL10SETR_C0,
+ CCSR_RCPM_CLPCL10SETR_C0);
+
+ /* setup the registers of the EPU FSM for deep sleep */
+ ls1_fsm_setup();
+
+ /* connect the EVENT button to IRQ in FPGA */
+ tmp = ioread8(ls1_pm_base.fpga + QIXIS_CTL_SYS);
+ tmp &= ~QIXIS_CTL_SYS_EVTSW_MASK;
+ tmp |= QIXIS_CTL_SYS_EVTSW_IRQ;
+ iowrite8(tmp, ls1_pm_base.fpga + QIXIS_CTL_SYS);
+
+ /* enable deep sleep signals in FPGA */
+ tmp = ioread8(ls1_pm_base.fpga + QIXIS_PWR_CTL2);
+ iowrite8(tmp | QIXIS_PWR_CTL2_PCTL, ls1_pm_base.fpga + QIXIS_PWR_CTL2);
+
+ /* enable Warm Device Reset */
+ ls1_clrsetbits_be32(ls1_pm_base.scfg + CCSR_SCFG_DPSLPCR,
+ CCSR_SCFG_DPSLPCR_VAL, CCSR_SCFG_DPSLPCR_VAL);
+
+ ls1_clrsetbits_be32(ls1_pm_base.dcfg + CCSR_DCFG_CRSTSR,
+ CCSR_DCFG_CRSTSR_VAL, CCSR_DCFG_CRSTSR_VAL);
+
+ /* copy the last stage code to sram */
+ ls1_copy_sram_code();
+
+ ls1_setup_wakeup_source();
+
+ cpu_suspend(SRAM_CODE_BASE_PHY, ls1_start_deepsleep);
+
+ ls1_clear_wakeup_source();
+
+ /* disable Warm Device Reset */
+ ls1_clrsetbits_be32(ls1_pm_base.scfg + CCSR_SCFG_DPSLPCR,
+ CCSR_SCFG_DPSLPCR_VAL, 0);
+
+ /* disable deep sleep signals in FPGA */
+ tmp = ioread8(ls1_pm_base.fpga + QIXIS_PWR_CTL2);
+ iowrite8(tmp & ~QIXIS_PWR_CTL2_PCTL, ls1_pm_base.fpga + QIXIS_PWR_CTL2);
+
+#define DCFG_CCSR_SCRATCHRW1 0x200
+ /* prepare for booting secondary cores */
+ iowrite32be(virt_to_phys(secondary_startup),
+ ls1_pm_base.dcfg + DCFG_CCSR_SCRATCHRW1);
+}
+
+static int ls1_suspend_enter(suspend_state_t state)
+{
+ int ret = 0;
+
+ ls1_set_powerdown();
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ flush_cache_louis();
+ ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_POWMGTCSR,
+ CCSR_RCPM_POWMGTCSR_LPM20_REQ,
+ CCSR_RCPM_POWMGTCSR_LPM20_REQ);
+
+ cpu_do_idle();
+ break;
+
+ case PM_SUSPEND_MEM:
+ ls1_enter_deepsleep();
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ls1_suspend_valid(suspend_state_t state)
+{
+ if (state == PM_SUSPEND_STANDBY && (sleep_modes & FSL_SLEEP))
+ return 1;
+
+ if (state == PM_SUSPEND_MEM && (sleep_modes & FSL_DEEP_SLEEP))
+ return 1;
+
+ return 0;
+}
+
+static int ls1_suspend_begin(suspend_state_t state)
+{
+ ls1_pm_iomap();
+
+ return 0;
+}
+
+static void ls1_suspend_end(void)
+{
+ ls1_pm_uniomap();
+}
+
+static const struct platform_suspend_ops ls1_suspend_ops = {
+ .valid = ls1_suspend_valid,
+ .enter = ls1_suspend_enter,
+ .begin = ls1_suspend_begin,
+ .end = ls1_suspend_end,
+};
+
+
+static const struct of_device_id rcpm_matches[] = {
+ {
+ .compatible = "fsl,ls1021a-rcpm",
+ .data = (void *)(FSL_SLEEP | FSL_DEEP_SLEEP),
+ },
+ {}
+};
+
+static int __init ls1_pm_init(void)
+{
+ const struct of_device_id *match;
+ struct device_node *np;
+ void *base;
+
+ np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
+ if (!np) {
+ pr_err("%s: can't find the rcpm node.\n", __func__);
+ return -EINVAL;
+ }
+
+ base = of_iomap(np, 0);
+ of_node_put(np);
+ if (!base)
+ return -ENOMEM;
+
+ sleep_modes = (unsigned int)match->data;
+ ls1_pm_base.rcpm = base;
+ suspend_set_ops(&ls1_suspend_ops);
+ return 0;
+}
+arch_initcall(ls1_pm_init);
diff --git a/arch/arm/mach-imx/sleep-ls1.S b/arch/arm/mach-imx/sleep-ls1.S
new file mode 100644
index 0000000..ae0a8af
--- /dev/null
+++ b/arch/arm/mach-imx/sleep-ls1.S
@@ -0,0 +1,132 @@
+/*
+ * Support deep sleep feature for LS1
+ *
+ * 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 <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/cache.h>
+#include <asm/cp15.h>
+
+#define CCSR_DDR_BASE 0x01080000
+#define CCSR_DDR_SDRAM_CFG_2 0x114
+
+#define CCSR_SCFG_BASE 0x01570000
+#define CCSR_SCFG_HRSTCR 0x1a8
+
+#define DCSR_EPU_BASE 0x20000000
+#define DCSR_EPU_EPGCR 0x0
+#define DCSR_EPU_EPECR0 0x300
+#define DCSR_EPU_EPECR15 0x33c
+
+/* for big endian registers */
+.macro ls1_set_bits, addr, value
+ ldr r4, \addr
+ ldr r5, [r4]
+ ldr r6, \value
+ rev r6, r6
+ orr r5, r5, r6
+ str r5, [r4]
+.endm
+
+/* 1000 loops per round */
+.macro ls1_delay, count
+ mov r0, \count
+11: mov r7, #1000
+12: subs r7, r7, #1
+ bne 12b
+ subs r0, r0, #1
+ bne 11b
+.endm
+
+/*
+ * r0: the physical entry address of SRAM code
+ *
+ */
+ .align L1_CACHE_SHIFT
+ .pushsection .idmap.text,"ax"
+ENTRY(ls1_do_deepsleep)
+ /* disable MMU, M bit in SCTLR */
+ mrc p15, 0, r3, c1, c0, 0
+ bic r3, r3, #CR_M
+ mcr p15, 0, r3, c1, c0, 0
+ isb
+
+ /* jump to sram code using physical address */
+ bx r0
+ENDPROC(ls1_do_deepsleep)
+ .popsection
+
+/*
+ * The code will be copied to SRAM.
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(ls1_start_fsm)
+ /* set HRSTCR */
+ ls1_set_bits ls1_ccsr_scfg_hrstcr_addr, ls1_ccsr_scfg_hrstcr_val
+
+ /* Place DDR controller in self refresh mode */
+ ls1_set_bits ls1_ddr_cfg2_addr, ls1_ddr_cfg2_val
+
+ ls1_delay #2000
+
+ /* Set EVT4_B to lock the signal MCKE down */
+ ldr r4, ls1_dcsr_epu_epecr0
+ ldr r5, ls1_dcsr_epu_epecr0_val
+ rev r5, r5
+ str r5, [r4]
+
+ ls1_delay #2000
+
+ /* Enable all EPU Counters */
+ ls1_set_bits ls1_dcsr_epu_epgcr_addr, ls1_dcsr_epu_epgcr_val
+
+ /* Enable SCU15 */
+ ls1_set_bits ls1_dcsr_epu_epecr15, ls1_dcsr_epu_epecr15_val
+
+ /* Enter WFI mode, and EPU FSM will start */
+20: wfi
+ b 20b
+
+ls1_ccsr_scfg_hrstcr_addr:
+ .word CCSR_SCFG_BASE + CCSR_SCFG_HRSTCR
+ls1_ccsr_scfg_hrstcr_val:
+ .word 0x80000000
+
+ls1_ddr_cfg2_addr:
+ .word CCSR_DDR_BASE + CCSR_DDR_SDRAM_CFG_2
+ls1_ddr_cfg2_val:
+ .word (1 << 31)
+
+ls1_dcsr_epu_epgcr_addr:
+ .word DCSR_EPU_BASE + DCSR_EPU_EPGCR
+ls1_dcsr_epu_epgcr_val:
+ .word 0x80000000
+
+ls1_dcsr_epu_epecr0:
+ .word DCSR_EPU_BASE + DCSR_EPU_EPECR0
+ls1_dcsr_epu_epecr0_val:
+ .word 0
+
+ls1_dcsr_epu_epecr15:
+ .word DCSR_EPU_BASE + DCSR_EPU_EPECR15
+ls1_dcsr_epu_epecr15_val:
+ .word 0x90000004
+
+ENTRY(ls1_sram_code_size)
+ .word . - ls1_start_fsm
+
+/* the bootloader will jump to here after wakeup from deep sleep */
+ .align L1_CACHE_SHIFT
+ENTRY(ls1_deepsleep_resume)
+ THUMB( adr r6, BSYM(1f) )
+ THUMB( bx r6 )
+ THUMB( .thumb )
+ THUMB(1: )
+ b cpu_resume
diff --git a/arch/arm/mach-imx/sleep-ls1.h b/arch/arm/mach-imx/sleep-ls1.h
new file mode 100644
index 0000000..5255999
--- /dev/null
+++ b/arch/arm/mach-imx/sleep-ls1.h
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+#ifndef __ARM_SLEEP_LS1_H
+#define __ARM_SLEEP_LS1_H
+
+void ls1_do_deepsleep(unsigned long addr);
+void ls1_start_fsm(void);
+void ls1_deepsleep_resume(void);
+void fsl_epu_setup_default(void __iomem *epu_base);
+
+extern int ls1_sram_code_size;
+
+#endif
diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c
index 10a6b1a..e73cbbe 100644
--- a/arch/arm/mach-imx/src.c
+++ b/arch/arm/mach-imx/src.c
@@ -30,6 +30,8 @@
#define BP_SRC_SCR_CORE1_RST 14
#define BP_SRC_SCR_CORE1_ENABLE 22
+#define CCSR_TWAITSR0 0x04C
+
static void __iomem *src_base;
static DEFINE_SPINLOCK(scr_lock);
@@ -114,6 +116,25 @@ void imx_set_cpu_arg(int cpu, u32 arg)
writel_relaxed(arg, src_base + SRC_GPR1 + cpu * 8 + 4);
}
+u32 ls1_get_cpu_arg(int cpu)
+{
+ struct device_node *np;
+ void __iomem *ls1_rcpm_base;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-rcpm-2.1");
+ if (!np) {
+ pr_err("%s(): Can not find the RCPM node.\n", __func__);
+ return -ENODEV;
+ }
+
+ ls1_rcpm_base = of_iomap(np, 0);
+ of_node_put(np);
+ WARN_ON(!ls1_rcpm_base);
+
+ cpu = cpu_logical_map(cpu);
+ return ioread32be(ls1_rcpm_base + CCSR_TWAITSR0) & (1 << cpu);
+}
+
void imx_src_prepare_restart(void)
{
u32 val;
diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c
index 3a6384c..14d4996 100644
--- a/arch/arm/mach-vexpress/dcscb.c
+++ b/arch/arm/mach-vexpress/dcscb.c
@@ -133,38 +133,8 @@ static void dcscb_power_down(void)
if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
arch_spin_unlock(&dcscb_lock);
- /*
- * Flush all cache levels for this cluster.
- *
- * To do so we do:
- * - Clear the SCTLR.C bit to prevent further cache allocations
- * - Flush the whole cache
- * - Clear the ACTLR "SMP" bit to disable local coherency
- *
- * Let's do it in the safest possible way i.e. with
- * no memory access within the following sequence
- * including to the stack.
- *
- * Note: fp is preserved to the stack explicitly prior doing
- * this since adding it to the clobber list is incompatible
- * with having CONFIG_FRAME_POINTER=y.
- */
- asm volatile(
- "str fp, [sp, #-4]! \n\t"
- "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t"
- "bic r0, r0, #"__stringify(CR_C)" \n\t"
- "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t"
- "isb \n\t"
- "bl v7_flush_dcache_all \n\t"
- "clrex \n\t"
- "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t"
- "bic r0, r0, #(1 << 6) @ disable local coherency \n\t"
- "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t"
- "isb \n\t"
- "dsb \n\t"
- "ldr fp, [sp], #4"
- : : : "r0","r1","r2","r3","r4","r5","r6","r7",
- "r9","r10","lr","memory");
+ /* Flush all cache levels for this cluster. */
+ v7_exit_coherency_flush(all);
/*
* This is a harmless no-op. On platforms with a real
@@ -183,26 +153,8 @@ static void dcscb_power_down(void)
} else {
arch_spin_unlock(&dcscb_lock);
- /*
- * Flush the local CPU cache.
- * Let's do it in the safest possible way as above.
- */
- asm volatile(
- "str fp, [sp, #-4]! \n\t"
- "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t"
- "bic r0, r0, #"__stringify(CR_C)" \n\t"
- "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t"
- "isb \n\t"
- "bl v7_flush_dcache_louis \n\t"
- "clrex \n\t"
- "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t"
- "bic r0, r0, #(1 << 6) @ disable local coherency \n\t"
- "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t"
- "isb \n\t"
- "dsb \n\t"
- "ldr fp, [sp], #4"
- : : : "r0","r1","r2","r3","r4","r5","r6","r7",
- "r9","r10","lr","memory");
+ /* Disable and flush the local CPU cache. */
+ v7_exit_coherency_flush(louis);
}
__mcpm_cpu_down(cpu, cluster);
diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c
index e6eb481..4eb92eb 100644
--- a/arch/arm/mach-vexpress/tc2_pm.c
+++ b/arch/arm/mach-vexpress/tc2_pm.c
@@ -156,32 +156,7 @@ static void tc2_pm_down(u64 residency)
: : "r" (0x400) );
}
- /*
- * We need to disable and flush the whole (L1 and L2) cache.
- * Let's do it in the safest possible way i.e. with
- * no memory access within the following sequence
- * including the stack.
- *
- * Note: fp is preserved to the stack explicitly prior doing
- * this since adding it to the clobber list is incompatible
- * with having CONFIG_FRAME_POINTER=y.
- */
- asm volatile(
- "str fp, [sp, #-4]! \n\t"
- "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t"
- "bic r0, r0, #"__stringify(CR_C)" \n\t"
- "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t"
- "isb \n\t"
- "bl v7_flush_dcache_all \n\t"
- "clrex \n\t"
- "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t"
- "bic r0, r0, #(1 << 6) @ disable local coherency \n\t"
- "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t"
- "isb \n\t"
- "dsb \n\t"
- "ldr fp, [sp], #4"
- : : : "r0","r1","r2","r3","r4","r5","r6","r7",
- "r9","r10","lr","memory");
+ v7_exit_coherency_flush(all);
cci_disable_port_by_cpu(mpidr);
@@ -197,26 +172,7 @@ static void tc2_pm_down(u64 residency)
arch_spin_unlock(&tc2_pm_lock);
- /*
- * We need to disable and flush only the L1 cache.
- * Let's do it in the safest possible way as above.
- */
- asm volatile(
- "str fp, [sp, #-4]! \n\t"
- "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t"
- "bic r0, r0, #"__stringify(CR_C)" \n\t"
- "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t"
- "isb \n\t"
- "bl v7_flush_dcache_louis \n\t"
- "clrex \n\t"
- "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t"
- "bic r0, r0, #(1 << 6) @ disable local coherency \n\t"
- "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t"
- "isb \n\t"
- "dsb \n\t"
- "ldr fp, [sp], #4"
- : : : "r0","r1","r2","r3","r4","r5","r6","r7",
- "r9","r10","lr","memory");
+ v7_exit_coherency_flush(louis);
}
__mcpm_cpu_down(cpu, cluster);
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index ced046d..2267b73 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -92,7 +92,7 @@ ENDPROC(cpu_v7_dcache_clean_area)
/* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */
.globl cpu_v7_suspend_size
-.equ cpu_v7_suspend_size, 4 * 8
+.equ cpu_v7_suspend_size, 4 * 9
#ifdef CONFIG_ARM_CPU_SUSPEND
ENTRY(cpu_v7_do_suspend)
stmfd sp!, {r4 - r10, lr}
@@ -101,13 +101,17 @@ ENTRY(cpu_v7_do_suspend)
stmia r0!, {r4 - r5}
#ifdef CONFIG_MMU
mrc p15, 0, r6, c3, c0, 0 @ Domain ID
+#ifdef CONFIG_ARM_LPAE
+ mrrc p15, 1, r5, r7, c2 @ TTB 1
+#else
mrc p15, 0, r7, c2, c0, 1 @ TTB 1
+#endif
mrc p15, 0, r11, c2, c0, 2 @ TTB control register
#endif
mrc p15, 0, r8, c1, c0, 0 @ Control register
mrc p15, 0, r9, c1, c0, 1 @ Auxiliary control register
mrc p15, 0, r10, c1, c0, 2 @ Co-processor access control
- stmia r0, {r6 - r11}
+ stmia r0, {r5 - r11}
ldmfd sp!, {r4 - r10, pc}
ENDPROC(cpu_v7_do_suspend)
@@ -118,16 +122,19 @@ ENTRY(cpu_v7_do_resume)
ldmia r0!, {r4 - r5}
mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID
mcr p15, 0, r5, c13, c0, 3 @ User r/o thread ID
- ldmia r0, {r6 - r11}
+ ldmia r0, {r5 - r11}
#ifdef CONFIG_MMU
mcr p15, 0, ip, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r6, c3, c0, 0 @ Domain ID
-#ifndef CONFIG_ARM_LPAE
+#ifdef CONFIG_ARM_LPAE
+ mcrr p15, 0, r1, ip, c2 @ TTB 0
+ mcrr p15, 1, r5, r7, c2 @ TTB 1
+#else
ALT_SMP(orr r1, r1, #TTB_FLAGS_SMP)
ALT_UP(orr r1, r1, #TTB_FLAGS_UP)
-#endif
mcr p15, 0, r1, c2, c0, 0 @ TTB 0
mcr p15, 0, r7, c2, c0, 1 @ TTB 1
+#endif
mcr p15, 0, r11, c2, c0, 2 @ TTB control register
ldr r4, =PRRR @ PRRR
ldr r5, =NMRR @ NMRR
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 95c2bf0..cafd166 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -721,10 +721,6 @@ config FSL_LBC
controller. Also contains some common code used by
drivers for specific local bus peripherals.
-config FSL_IFC
- bool
- depends on FSL_SOC
-
config FSL_GTM
bool
depends on PPC_83xx || QUICC_ENGINE || CPM2
@@ -1028,8 +1024,6 @@ source "drivers/Kconfig"
source "fs/Kconfig"
-source "arch/powerpc/sysdev/qe_lib/Kconfig"
-
source "lib/Kconfig"
source "arch/powerpc/Kconfig.debug"
@@ -1048,7 +1042,4 @@ config PPC_CLOCK
default n
select HAVE_CLK
-config PPC_LIB_RHEAP
- bool
-
source "arch/powerpc/kvm/Kconfig"
diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig
index a2afada..3a225a4 100644
--- a/arch/powerpc/configs/corenet32_smp_defconfig
+++ b/arch/powerpc/configs/corenet32_smp_defconfig
@@ -149,6 +149,7 @@ CONFIG_RTC_DRV_DS3232=y
CONFIG_RTC_DRV_CMOS=y
CONFIG_UIO=y
CONFIG_STAGING=y
+CONFIG_MEMORY=y
CONFIG_FSL_PME2=y
CONFIG_FSL_PAMU=y
CONFIG_VIRT_DRIVERS=y
diff --git a/arch/powerpc/configs/corenet64_smp_defconfig b/arch/powerpc/configs/corenet64_smp_defconfig
index 61c01cc..bcca3b3 100644
--- a/arch/powerpc/configs/corenet64_smp_defconfig
+++ b/arch/powerpc/configs/corenet64_smp_defconfig
@@ -172,6 +172,7 @@ CONFIG_FSL_PME2=y
CONFIG_FSL_PAMU=y
CONFIG_VIRT_DRIVERS=y
CONFIG_FSL_HV_MANAGER=y
+CONFIG_MEMORY=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
CONFIG_ISO9660_FS=m
diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig
index 7e4a6dd..c18161c 100644
--- a/arch/powerpc/configs/mpc85xx_defconfig
+++ b/arch/powerpc/configs/mpc85xx_defconfig
@@ -216,6 +216,7 @@ CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_CMOS=y
CONFIG_DMADEVICES=y
CONFIG_FSL_DMA=y
+CONFIG_MEMORY=y
# CONFIG_NET_DMA is not set
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig
index 7eb8788..fe09bef 100644
--- a/arch/powerpc/configs/mpc85xx_smp_defconfig
+++ b/arch/powerpc/configs/mpc85xx_smp_defconfig
@@ -222,6 +222,7 @@ CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_CMOS=y
CONFIG_DMADEVICES=y
CONFIG_FSL_DMA=y
+CONFIG_MEMORY=y
# CONFIG_NET_DMA is not set
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
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 <asm/rheap.h>
+#include <linux/fsl/rheap.h>
#include <linux/spinlock.h>
/*
@@ -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/fsl_pm.h b/arch/powerpc/include/asm/fsl_pm.h
index 8e6a656..9811cb2 100644
--- a/arch/powerpc/include/asm/fsl_pm.h
+++ b/arch/powerpc/include/asm/fsl_pm.h
@@ -37,10 +37,16 @@ struct fsl_pm_ops {
extern const struct fsl_pm_ops *qoriq_pm_ops;
-struct fsm_reg_vals;
+struct fsm_reg_vals {
+ u32 offset;
+ u32 value;
+};
-extern void fsl_dp_fsm_setup(void __iomem *dcsr_base, struct fsm_reg_vals *val);
-extern void fsl_dp_fsm_clean(void __iomem *dcsr_base, struct fsm_reg_vals *val);
+void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val);
+void fsl_epu_setup_default(void __iomem *epu_base);
+void fsl_npc_setup_default(void __iomem *npc_base);
+void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val);
+void fsl_epu_clean_default(void __iomem *epu_base);
extern int fsl_dp_iomap(void);
extern void fsl_dp_iounmap(void);
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/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/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 <asm/udbg.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#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 <asm/io.h>
#include <asm/hw_irq.h>
#include <asm/ipic.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
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 <asm/udbg.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#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 <asm/time.h>
#include <asm/ipic.h>
#include <asm/udbg.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
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 <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
#include <sysdev/simple_gpio.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#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 <asm/time.h>
#include <asm/ipic.h>
#include <asm/udbg.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#include <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
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/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 <linux/of_platform.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
#include <sysdev/cpm2_pic.h>
#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 <asm/udbg.h>
#include <asm/mpic.h>
#include <asm/ehv_pic.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe_ic.h>
#include <linux/of_platform.h>
#include <sysdev/fsl_soc.h>
diff --git a/arch/powerpc/platforms/85xx/deepsleep.c b/arch/powerpc/platforms/85xx/deepsleep.c
index 88dee90..03852dc 100644
--- a/arch/powerpc/platforms/85xx/deepsleep.c
+++ b/arch/powerpc/platforms/85xx/deepsleep.c
@@ -34,6 +34,12 @@
#define CCSR_GPIO1_GPDAT 0x130008
#define CCSR_GPIO1_GPDIR_29 0x4
+#define EPU_BLOCK_OFFSET 0x00000000
+#define NPC_BLOCK_OFFSET 0x00001000
+
+#define CSTTACR0 0xb00
+#define CG1CR0 0x31c
+
/* 128 bytes buffer for restoring data broke by DDR training initialization */
#define DDR_BUF_SIZE 128
static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64);
@@ -179,14 +185,17 @@ int fsl_enter_epu_deepsleep(void)
*/
setbits32(ccsr_base + CPC_CPCHDBCR0, CPC_CPCHDBCR0_SPEC_DIS);
- fsl_dp_fsm_setup(dcsr_base, NULL);
+ fsl_epu_setup_default(dcsr_base + EPU_BLOCK_OFFSET);
+ fsl_npc_setup_default(dcsr_base + NPC_BLOCK_OFFSET);
+ out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001);
+ out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001);
fsl_dp_enter_low(ccsr_base, dcsr_base, pld_base, pld_flag);
/* disable Warm Device Reset request */
clrbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
- fsl_dp_fsm_clean(dcsr_base, NULL);
+ fsl_epu_clean_default(dcsr_base + EPU_BLOCK_OFFSET);
return 0;
}
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 <sysdev/fsl_soc.h>
#include <sysdev/fsl_pci.h>
#include <sysdev/simple_gpio.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#include <asm/mpic.h>
#include <asm/swiotlb.h>
#include <asm/fsl_guts.h>
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 <asm/prom.h>
#include <asm/udbg.h>
#include <asm/mpic.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#include <asm/fsl_guts.h>
#include <sysdev/fsl_soc.h>
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 <asm/prom.h>
#include <asm/udbg.h>
#include <asm/mpic.h>
-#include <asm/qe.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/qe_ic.h>
#include <asm/fsl_guts.h>
#include <sysdev/fsl_soc.h>
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index be181b6..9c38a8d 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -278,30 +278,11 @@ 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
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/Makefile b/arch/powerpc/sysdev/Makefile
index ca258c5..b91e758 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -22,13 +22,11 @@ obj-$(CONFIG_FSL_LBC) += fsl_lbc.o
obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y)
obj-$(CONFIG_FSL_PMC) += fsl_pmc.o
obj-$(CONFIG_FSL_CORENET_RCPM) += fsl_rcpm.o
-obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_FSL_GTM) += fsl_gtm.o
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/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 <asm/cpm1.h>
#include <asm/io.h>
#include <asm/tlbflush.h>
-#include <asm/rheap.h>
+#include <linux/fsl/rheap.h>
#include <asm/prom.h>
#include <asm/cpm.h>
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 <asm/page.h>
#include <asm/pgtable.h>
#include <asm/cpm2.h>
-#include <asm/rheap.h>
+#include <linux/fsl/rheap.h>
#include <asm/fs_pd.h>
#include <sysdev/fsl_soc.h>
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 <asm/udbg.h>
#include <asm/io.h>
-#include <asm/rheap.h>
+#include <linux/fsl/rheap.h>
#include <asm/cpm.h>
#include <mm/mmu_decl.h>
@@ -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 <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
-#include <asm/rheap.h>
+#include <linux/fsl/rheap.h>
#include <asm/ppc4xx_ocm.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
@@ -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/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/crypto/testmgr.c b/crypto/testmgr.c
index 117b312..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;
}
@@ -422,16 +445,21 @@ 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;
+ key = kmalloc(MAX_KEYLEN, GFP_KERNEL);
+ if (!key)
+ goto out_noxbuf;
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;
@@ -492,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);
@@ -593,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) {
@@ -775,6 +817,8 @@ 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 {
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/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-mmio.c b/drivers/base/regmap/regmap-mmio.c
index 98745dd..1f95f8b 100644
--- a/drivers/base/regmap/regmap-mmio.c
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -26,19 +26,75 @@
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)
+{
+ switch (reg_size) {
+ case 1:
+ case 2:
+ case 4:
+#ifdef CONFIG_64BIT
+ case 8:
+#endif
+ break;
+ default:
+ BUG();
+ }
+}
+
+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);
+}
+
+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;
- BUG_ON(reg_size != 4);
+ regmap_mmio_regsize_check(reg_size);
if (ctx->clk) {
ret = clk_enable(ctx->clk);
@@ -46,7 +102,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) {
@@ -81,9 +137,13 @@ 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);
+ struct regmap_mmio_context *ctx = context;
+ unsigned int offset = ctx->reg_bytes + ctx->pad_bytes;
- return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
+ regmap_mmio_count_check(count);
+
+ return regmap_mmio_gather_write(context, data, ctx->reg_bytes,
+ data + offset, count - offset);
}
static int regmap_mmio_read(void *context,
@@ -91,10 +151,10 @@ 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;
- BUG_ON(reg_size != 4);
+ regmap_mmio_regsize_check(reg_size);
if (ctx->clk) {
ret = clk_enable(ctx->clk);
@@ -102,7 +162,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) {
@@ -165,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);
@@ -209,6 +270,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;
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 7d689a1..b4116f2 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -15,6 +15,7 @@
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
@@ -201,6 +202,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 +233,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 +264,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 +278,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 +307,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 +321,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;
@@ -389,6 +432,71 @@ static void regmap_range_exit(struct regmap *map)
kfree(map->selector_work_buf);
}
+static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ 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;
+ enum regmap_endian endian;
+
+ /* Retrieve the endianness specification from the regmap config */
+ endian = config->val_format_endian;
+
+ /* If the regmap config specified a non-default value, use that */
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
+
+ /* If the dev and dev->of_node exist try to get endianness from DT */
+ if (dev && dev->of_node) {
+ np = dev->of_node;
+
+ /* 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)
+ endian = bus->val_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;
+}
+
/**
* regmap_init(): Initialise register map
*
@@ -484,17 +592,8 @@ 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;
+ 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:
@@ -593,6 +692,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 +718,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;
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 279407a..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 && 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-qoriq.c
index e958707..c674bbf 100644
--- a/drivers/clk/clk-ppc-corenet.c
+++ b/drivers/clk/clk-qoriq.c
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/slab.h>
@@ -19,6 +20,7 @@ struct cmux_clk {
struct clk_hw hw;
void __iomem *reg;
u32 flags;
+ unsigned int num_parents;
};
#define PLL_KILL BIT(31)
@@ -27,14 +29,13 @@ struct cmux_clk {
#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;
+ clksel = ((idx / clk->num_parents) << 2) + idx % clk->num_parents;
if (clk->flags & CLKSEL_ADJUST)
clksel += 8;
clksel = (clksel & 0xf) << CLKSEL_SHIFT;
@@ -52,7 +53,7 @@ static u8 cmux_get_parent(struct clk_hw *hw)
clksel = (clksel >> CLKSEL_SHIFT) & 0xf;
if (clk->flags & CLKSEL_ADJUST)
clksel -= 8;
- clksel = (clksel >> 2) * clocks_per_pll + clksel % 4;
+ clksel = (clksel >> 2) * clk->num_parents + clksel % 4;
return clksel;
}
@@ -100,6 +101,7 @@ static void __init core_mux_init(struct device_node *np)
goto err_name;
}
cmux_clk->reg = base + offset;
+ cmux_clk->num_parents = count;
node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen");
if (node && (offset >= 0x80))
@@ -176,9 +178,6 @@ static void __init core_pll_init(struct device_node *np)
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__);
@@ -243,7 +242,7 @@ static const struct of_device_id clk_match[] __initconst = {
{}
};
-static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
+static int __init qoriq_corenet_clk_probe(struct platform_device *pdev)
{
struct device_node *np;
@@ -258,23 +257,33 @@ static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ppc_clk_ids[] __initconst = {
+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 ppc_corenet_clk_driver = {
+static struct platform_driver qoriq_corenet_clk_driver __initdata = {
.driver = {
- .name = "ppc_corenet_clock",
+ .name = "qoriq_corenet_clock",
.owner = THIS_MODULE,
- .of_match_table = ppc_clk_ids,
+ .of_match_table = qoriq_clk_ids,
},
- .probe = ppc_corenet_clk_probe,
+ .probe = qoriq_corenet_clk_probe,
};
-static int __init ppc_corenet_clk_init(void)
+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)
{
- return platform_driver_register(&ppc_corenet_clk_driver);
+ base = of_iomap(np, 0);
+ if (!base)
+ return;
+
+ of_clk_init(clk_match);
}
-subsys_initcall(ppc_corenet_clk_init);
+CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-clockgen", ls1021a_clocks_init);
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 <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+#include <linux/slab.h>
+
+#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);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 0fa204b..553a50c 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -235,3 +235,11 @@ config ARM_TEGRA_CPUFREQ
default y
help
This adds the CPUFreq driver support for TEGRA SOCs.
+
+config QORIQ_CPUFREQ
+ tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
+ depends on OF && COMMON_CLK
+ select CLK_QORIQ
+ help
+ This adds the CPUFreq driver support for Freescale QorIQ SoCs
+ which are capable of changing the CPU's frequency dynamically.
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
index e859a1d..1527a79 100644
--- a/drivers/cpufreq/Kconfig.powerpc
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -35,15 +35,13 @@ config MPC85xx_CPUFREQ
have a JOG feature, which provides a dynamic mechanism
to lower or raise the CPU core clock at runtime.
-config PPC_CORENET_CPUFREQ
- tristate "CPU frequency scaling driver for Freescale E500MC SoCs"
- depends on PPC_E500MC && OF && COMMON_CLK
- select CPU_FREQ_TABLE
- select CLK_PPC_CORENET
+config QORIQ_CPUFREQ
+ tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
+ depends on OF && COMMON_CLK
+ select CLK_QORIQ
help
- This adds the CPUFreq driver support for Freescale e500mc,
- e5500 and e6500 series SoCs which are capable of changing
- the CPU's frequency dynamically.
+ This adds the CPUFreq driver support for Freescale QorIQ SoCs
+ which are capable of changing the CPU's frequency dynamically.
config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks"
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index cc1b57d..708f794 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -84,7 +84,7 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o
ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o
obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o
obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
-obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o
+obj-$(CONFIG_QORIQ_CPUFREQ) += qoriq-cpufreq.o
obj-$(CONFIG_MPC85xx_CPUFREQ) += mpc85xx-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
index 8500627..9d0aa19 100644
--- a/drivers/cpufreq/ppc-corenet-cpufreq.c
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -1,7 +1,7 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
*
- * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs.
+ * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
*
* 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
@@ -13,7 +13,6 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/errno.h>
-#include <sysdev/fsl_soc.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -73,17 +72,7 @@ static const u32 *fmask;
static DEFINE_MUTEX(cpufreq_lock);
static DEFINE_PER_CPU(struct cpu_data *, cpu_data);
-/* cpumask in a cluster */
-static DEFINE_PER_CPU(cpumask_var_t, cpu_mask);
-
-#ifndef CONFIG_SMP
-static inline const struct cpumask *cpu_core_mask(int cpu)
-{
- return cpumask_of(0);
-}
-#endif
-
-static unsigned int corenet_cpufreq_get_speed(unsigned int cpu)
+static unsigned int qoriq_cpufreq_get_speed(unsigned int cpu)
{
struct cpu_data *data = per_cpu(cpu_data, cpu);
@@ -140,7 +129,80 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
}
}
-static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
+#if defined(CONFIG_ARM)
+static int get_cpu_physical_id(int cpu)
+{
+ return topology_core_id(cpu);
+}
+#else
+static int get_cpu_physical_id(int cpu)
+{
+ return get_hard_smp_processor_id(cpu);
+}
+#endif
+
+static u32 get_bus_freq(void)
+{
+ struct device_node *soc;
+ u32 sysfreq;
+
+ soc = of_find_node_by_type(NULL, "soc");
+ if (!soc)
+ return 0;
+
+ if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
+ sysfreq = 0;
+
+ of_node_put(soc);
+
+ return sysfreq;
+}
+
+static struct device_node *cpu_to_clk_node(int cpu)
+{
+ struct device_node *np, *clk_np;
+
+ if (!cpu_present(cpu))
+ return NULL;
+
+ np = of_get_cpu_node(cpu, NULL);
+ if (!np)
+ return NULL;
+
+ clk_np = of_parse_phandle(np, "clocks", 0);
+ if (!clk_np)
+ return NULL;
+
+ of_node_put(np);
+
+ return clk_np;
+}
+
+/* traverse cpu nodes to get cpu mask of sharing clock wire */
+static void set_affected_cpus(struct cpufreq_policy *policy)
+{
+ struct device_node *np, *clk_np;
+ struct cpumask *dstp = policy->cpus;
+ int i;
+
+ np = cpu_to_clk_node(policy->cpu);
+ if (!np)
+ return;
+
+ for_each_present_cpu(i) {
+ clk_np = cpu_to_clk_node(i);
+ if (!clk_np)
+ continue;
+
+ if (clk_np == np)
+ cpumask_set_cpu(i, dstp);
+
+ of_node_put(clk_np);
+ }
+ of_node_put(np);
+}
+
+static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
struct device_node *np;
int i, count, ret;
@@ -181,7 +243,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
}
if (fmask)
- mask = fmask[get_hard_smp_processor_id(cpu)];
+ mask = fmask[get_cpu_physical_id(cpu)];
else
mask = 0x0;
@@ -213,15 +275,15 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
per_cpu(cpu_data, cpu) = data;
/* update ->cpus if we have cluster, no harm if not */
- cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
- for_each_cpu(i, per_cpu(cpu_mask, cpu))
+ set_affected_cpus(policy);
+ for_each_cpu(i, policy->cpus)
per_cpu(cpu_data, i) = data;
/* Minimum transition latency is 12 platform clocks */
u64temp = 12ULL * NSEC_PER_SEC;
- do_div(u64temp, fsl_get_sys_freq());
+ do_div(u64temp, get_bus_freq());
policy->cpuinfo.transition_latency = u64temp + 1;
- policy->cur = corenet_cpufreq_get_speed(policy->cpu);
+ policy->cur = qoriq_cpufreq_get_speed(policy->cpu);
cpufreq_frequency_table_get_attr(table, cpu);
of_node_put(np);
@@ -241,23 +303,19 @@ err_np:
return -ENODEV;
}
-static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
- unsigned int cpu;
cpufreq_frequency_table_put_attr(policy->cpu);
of_node_put(data->parent);
kfree(data->table);
kfree(data);
- for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
- per_cpu(cpu_data, cpu) = NULL;
-
return 0;
}
-static int corenet_cpufreq_verify(struct cpufreq_policy *policy)
+static int qoriq_cpufreq_verify(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *table =
per_cpu(cpu_data, policy->cpu)->table;
@@ -265,7 +323,7 @@ static int corenet_cpufreq_verify(struct cpufreq_policy *policy)
return cpufreq_frequency_table_verify(policy, table);
}
-static int corenet_cpufreq_target(struct cpufreq_policy *policy,
+static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
struct cpufreq_freqs freqs;
@@ -297,20 +355,20 @@ static int corenet_cpufreq_target(struct cpufreq_policy *policy,
return ret;
}
-static struct freq_attr *corenet_cpufreq_attr[] = {
+static struct freq_attr *qoriq_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
-static struct cpufreq_driver ppc_corenet_cpufreq_driver = {
- .name = "ppc_cpufreq",
+static struct cpufreq_driver qoriq_cpufreq_driver = {
+ .name = "qoriq_cpufreq",
.flags = CPUFREQ_CONST_LOOPS,
- .init = corenet_cpufreq_cpu_init,
- .exit = __exit_p(corenet_cpufreq_cpu_exit),
- .verify = corenet_cpufreq_verify,
- .target = corenet_cpufreq_target,
- .get = corenet_cpufreq_get_speed,
- .attr = corenet_cpufreq_attr,
+ .init = qoriq_cpufreq_cpu_init,
+ .exit = __exit_p(qoriq_cpufreq_cpu_exit),
+ .verify = qoriq_cpufreq_verify,
+ .target = qoriq_cpufreq_target,
+ .get = qoriq_cpufreq_get_speed,
+ .attr = qoriq_cpufreq_attr,
};
static const struct of_device_id node_matches[] __initdata = {
@@ -320,63 +378,46 @@ static const struct of_device_id node_matches[] __initdata = {
{ .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
{ .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
{ .compatible = "fsl,qoriq-clockgen-2.0", },
+ { .compatible = "fsl,ls1021a-clockgen", },
{}
};
-static int __init ppc_corenet_cpufreq_init(void)
+static int __init qoriq_cpufreq_init(void)
{
int ret;
struct device_node *np;
const struct of_device_id *match;
const struct soc_data *data;
- unsigned int cpu;
np = of_find_matching_node(NULL, node_matches);
if (!np)
return -ENODEV;
- for_each_possible_cpu(cpu) {
- if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL))
- goto err_mask;
- cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu));
- }
-
match = of_match_node(node_matches, np);
data = match->data;
if (data) {
if (data->flag)
fmask = data->freq_mask;
- min_cpufreq = fsl_get_sys_freq();
+ min_cpufreq = get_bus_freq();
} else {
- min_cpufreq = fsl_get_sys_freq() / 2;
+ min_cpufreq = get_bus_freq() / 2;
}
of_node_put(np);
- ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver);
+ ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
if (!ret)
- pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n");
+ pr_info("Freescale PowerPC qoriq CPU frequency scaling driver\n");
return ret;
-
-err_mask:
- for_each_possible_cpu(cpu)
- free_cpumask_var(per_cpu(cpu_mask, cpu));
-
- return -ENOMEM;
}
-module_init(ppc_corenet_cpufreq_init);
+module_init(qoriq_cpufreq_init);
-static void __exit ppc_corenet_cpufreq_exit(void)
+static void __exit qoriq_cpufreq_exit(void)
{
- unsigned int cpu;
-
- for_each_possible_cpu(cpu)
- free_cpumask_var(per_cpu(cpu_mask, cpu));
-
- cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver);
+ cpufreq_unregister_driver(&qoriq_cpufreq_driver);
}
-module_exit(ppc_corenet_cpufreq_exit);
+module_exit(qoriq_cpufreq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 2a4eb70..c9ca243 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;
}
@@ -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) &&
@@ -2243,8 +2247,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 +2281,12 @@ 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);
+ 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;
@@ -2444,8 +2457,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 +2480,12 @@ 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);
+ 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 5592fec..7728119 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 */
@@ -487,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;
@@ -713,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
@@ -751,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
@@ -818,12 +830,11 @@ 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);
+ 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 +860,14 @@ 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);
+ 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);
@@ -921,23 +940,34 @@ 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,
- 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,
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);
+ 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__)": ",
@@ -999,11 +1029,11 @@ 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);
+ 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,
@@ -1012,11 +1042,22 @@ 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);
+ 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__)": ",
@@ -1066,8 +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_dma = dma_map_single(jrdev, edesc->sec4_sg,
- sec4_sg_bytes, DMA_TO_DEVICE);
+ edesc->sec4_sg_bytes = sec4_sg_bytes;
edesc->src_nents = src_nents;
edesc->chained = chained;
@@ -1077,6 +1117,12 @@ 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);
+ 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 {
@@ -1087,6 +1133,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__)": ",
@@ -1135,11 +1185,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
@@ -1207,9 +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->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
- sec4_sg_bytes,
- DMA_TO_DEVICE);
+ edesc->dst_dma = 0;
state->buf_dma = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg,
buf, *buflen);
@@ -1226,9 +1282,19 @@ 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);
+ 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__)": ",
@@ -1307,8 +1373,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,11 +1381,22 @@ 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);
+ 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__)": ",
@@ -1390,13 +1465,19 @@ 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);
+ edesc->dst_dma = 0;
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);
+ 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 {
@@ -1414,7 +1495,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__)": ",
@@ -1463,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;
}
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);
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 <linux/hash.h>
#include <linux/hw_random.h>
#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/spinlock.h>
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 17805eb..17af050 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -81,27 +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;
- setbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE);
- while (!(rd_reg32(&topregs->ctrl.deco_rq) & DECORR_DEN0) &&
+ if (ctrlpriv->virt_en == 1) {
+ setbits32(&ctrl->deco_rsr, DECORSR_JR0);
+
+ while (!(rd_reg32(&ctrl->deco_rsr) & DECORSR_VALID) &&
+ --timeout)
+ cpu_relax();
+
+ timeout = 100000;
+ }
+
+ setbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE);
+
+ 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;
/*
@@ -112,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
@@ -127,11 +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(&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;
@@ -162,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)
@@ -198,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);
@@ -271,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++) {
@@ -298,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);
@@ -315,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);
@@ -375,19 +387,19 @@ 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;
- struct caam_full __iomem *topregs;
+ u64 cha_vid_ls;
+ struct caam_ctrl __iomem *ctrl;
int ret = 0;
- topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
- cha_vid = rd_reg64(&topregs->ctrl.perfmon.cha_id);
+ 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 & 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);
+ rd_reg32(&ctrl->r4tst[0].rdsta);
/*
* If the secure keys (TDKEK, JDKEK, TDSK), were already
* generated, signal this to the function that is instantiating
@@ -398,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
@@ -433,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;
}
@@ -446,11 +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)
@@ -468,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);
@@ -480,9 +511,35 @@ 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
+ */
+ scfgr = rd_reg32(&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(&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));
@@ -508,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;
}
@@ -521,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++;
}
@@ -539,12 +601,16 @@ 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(&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
@@ -568,8 +634,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(&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,
@@ -661,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_queue_if __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.
@@ -673,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;
}
@@ -684,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;
}
@@ -692,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_queue_if __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;
@@ -717,23 +784,23 @@ 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;
+ struct caam_queue_if __iomem *qi;
int ret;
caam_priv = dev_get_drvdata(dev);
ctrl = caam_priv->ctrl;
- topregs = (struct caam_full __iomem *)ctrl;
+ qi = caam_priv->qi;
/*
* 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 ed0351d..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,
@@ -94,6 +95,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/pdb.h b/drivers/crypto/caam/pdb.h
index 0680b30..faab55b 100644
--- a/drivers/crypto/caam/pdb.h
+++ b/drivers/crypto/caam/pdb.h
@@ -66,9 +66,15 @@ struct ipsec_encap_ctr {
struct ipsec_encap_ccm {
u32 salt; /* lower 24 bits */
+#ifdef CONFIG_CPU_BIG_ENDIAN
u8 b0_flags;
u8 ctr_flags;
u16 ctr_initial;
+#else
+ u16 ctr_initial;
+ u8 ctr_flags;
+ u8 b0_flags;
+#endif
u32 iv[2];
};
@@ -79,10 +85,17 @@ struct ipsec_encap_gcm {
};
struct ipsec_encap_pdb {
+#ifdef CONFIG_CPU_BIG_ENDIAN
u8 hmo_rsvd;
u8 ip_nh;
u8 ip_nh_offset;
u8 options;
+#else
+ u8 options;
+ u8 ip_nh_offset;
+ u8 ip_nh;
+ u8 hmo_rsvd;
+#endif
u32 seq_num_ext_hi;
u32 seq_num;
union {
@@ -92,8 +105,13 @@ struct ipsec_encap_pdb {
struct ipsec_encap_gcm gcm;
};
u32 spi;
+#ifdef CONFIG_CPU_BIG_ENDIAN
u16 rsvd1;
u16 ip_hdr_len;
+#else
+ u16 ip_hdr_len;
+ u16 rsvd1;
+#endif
u32 ip_hdr[0]; /* optional IP Header content */
};
@@ -108,9 +126,15 @@ struct ipsec_decap_ctr {
struct ipsec_decap_ccm {
u32 salt;
+#ifdef CONFIG_CPU_BIG_ENDIAN
u8 iv_flags;
u8 ctr_flags;
u16 ctr_initial;
+#else
+ u16 ctr_initial;
+ u8 ctr_flags;
+ u8 iv_flags;
+#endif
};
struct ipsec_decap_gcm {
@@ -119,9 +143,15 @@ struct ipsec_decap_gcm {
};
struct ipsec_decap_pdb {
+#ifdef CONFIG_CPU_BIG_ENDIAN
u16 hmo_ip_hdr_len;
u8 ip_nh_offset;
u8 options;
+#else
+ u8 options;
+ u8 ip_nh_offset;
+ u16 hmo_ip_hdr_len;
+#endif
union {
struct ipsec_decap_cbc cbc;
struct ipsec_decap_ctr ctr;
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index d50174f..79d2bf3 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -74,16 +74,23 @@
#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
#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
+#ifdef __BIG_ENDIAN
static inline void wr_reg64(u64 __iomem *reg, u64 data)
{
wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32);
@@ -95,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
/*
@@ -114,45 +136,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;
@@ -172,10 +194,16 @@ 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)
+#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];
/* CAAM Global Status fc0-fdf */
@@ -189,9 +217,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 */
@@ -304,9 +335,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];
@@ -347,7 +381,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 */
@@ -365,6 +402,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
@@ -723,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 */
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/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/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
new file mode 100644
index 0000000..eadb155
--- /dev/null
+++ b/drivers/dma/fsl-edma.c
@@ -0,0 +1,1079 @@
+/*
+ * drivers/dma/fsl-edma.c
+ *
+ * Copyright 2013-2014 Freescale Semiconductor, Inc.
+ *
+ * Driver for the Freescale eDMA engine with flexible channel multiplexing
+ * capability for DMA request sources. The eDMA block can be found on some
+ * Vybrid and Layerscape SoCs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+
+#include "virt-dma.h"
+
+#define EDMA_CR 0x00
+#define EDMA_ES 0x04
+#define EDMA_ERQ 0x0C
+#define EDMA_EEI 0x14
+#define EDMA_SERQ 0x1B
+#define EDMA_CERQ 0x1A
+#define EDMA_SEEI 0x19
+#define EDMA_CEEI 0x18
+#define EDMA_CINT 0x1F
+#define EDMA_CERR 0x1E
+#define EDMA_SSRT 0x1D
+#define EDMA_CDNE 0x1C
+#define EDMA_INTR 0x24
+#define EDMA_ERR 0x2C
+
+#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
+#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
+#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
+#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
+#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
+#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
+#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
+#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
+#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
+#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
+#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
+#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
+#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
+
+#define EDMA_CR_EDBG BIT(1)
+#define EDMA_CR_ERCA BIT(2)
+#define EDMA_CR_ERGA BIT(3)
+#define EDMA_CR_HOE BIT(4)
+#define EDMA_CR_HALT BIT(5)
+#define EDMA_CR_CLM BIT(6)
+#define EDMA_CR_EMLM BIT(7)
+#define EDMA_CR_ECX BIT(16)
+#define EDMA_CR_CX BIT(17)
+
+#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
+#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
+#define EDMA_CINT_CINT(x) ((x) & 0x1F)
+#define EDMA_CERR_CERR(x) ((x) & 0x1F)
+
+#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
+#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
+#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
+#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
+#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
+#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
+#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
+#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
+#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
+#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
+#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
+#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
+#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
+#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
+
+#define EDMA_TCD_SOFF_SOFF(x) (x)
+#define EDMA_TCD_NBYTES_NBYTES(x) (x)
+#define EDMA_TCD_SLAST_SLAST(x) (x)
+#define EDMA_TCD_DADDR_DADDR(x) (x)
+#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
+#define EDMA_TCD_DOFF_DOFF(x) (x)
+#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
+#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
+
+#define EDMA_TCD_CSR_START BIT(0)
+#define EDMA_TCD_CSR_INT_MAJOR BIT(1)
+#define EDMA_TCD_CSR_INT_HALF BIT(2)
+#define EDMA_TCD_CSR_D_REQ BIT(3)
+#define EDMA_TCD_CSR_E_SG BIT(4)
+#define EDMA_TCD_CSR_E_LINK BIT(5)
+#define EDMA_TCD_CSR_ACTIVE BIT(6)
+#define EDMA_TCD_CSR_DONE BIT(7)
+
+#define EDMAMUX_CHCFG_DIS 0x0
+#define EDMAMUX_CHCFG_ENBL 0x80
+#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
+
+#define DMAMUX_NR 2
+
+#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;
+ u32 slave_id;
+ 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.
+ * 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)
+{
+ u32 dst;
+ /* swap the reg offset for that in big-endian mode*/
+ 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)
+{
+ 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)
+{
+ u32 dst;
+ /* swap the reg offset for that in big-endian mode*/
+ 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) {
+ 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)
+{
+ 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;
+ 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)
+ writeb(EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot),
+ muxaddr + ch_off);
+ else
+ writeb(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 should be swapped according the eDMA
+ * engine requirement.
+ */
+ edma_writew(fsl_chan->edma, 0, addr + EDMA_TCD_CSR(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));
+
+ 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));
+
+ edma_writel(fsl_chan->edma, dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch));
+
+ edma_writew(fsl_chan->edma, 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 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.
+ */
+ 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;
+
+ if (disable_req)
+ csr |= EDMA_TCD_CSR_D_REQ;
+
+ if (enable_sg)
+ csr |= EDMA_TCD_CSR_E_SG;
+
+ writew(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 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;
+ 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, *_chan;
+ struct fsl_edma_chan *fsl_chan;
+ unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR;
+
+ if (dma_spec->args_count != 2)
+ return NULL;
+
+ mutex_lock(&fsl_edma->fsl_edma_mutex);
+ list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) {
+ if (chan->client_count)
+ continue;
+ if ((chan->chan_id / chans_per_mux) == dma_spec->args[0]) {
+ chan = dma_get_slave_channel(chan);
+ if (chan) {
+ chan->device->privatecnt++;
+ fsl_chan = to_fsl_edma_chan(chan);
+ fsl_chan->slave_id = dma_spec->args[1];
+ fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id,
+ 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->slave_id = 0;
+ 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);
+ 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
+ = 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;
+ fsl_edma->dma_dev.device_prep_dma_memcpy = fsl_edma_prep_memcpy;
+
+ 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;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_edma_suspend(struct device *dev)
+{
+ struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
+ struct fsl_edma_chan *fsl_chan;
+ int i;
+
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ fsl_chan = &fsl_edma->chans[i];
+ fsl_edma_chan_mux(fsl_chan, 0, false);
+ }
+
+ return 0;
+}
+
+static int fsl_edma_resume(struct device *dev)
+{
+ struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
+ struct fsl_edma_chan *fsl_chan;
+ int i;
+
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ fsl_chan = &fsl_edma->chans[i];
+ edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
+ fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true);
+ }
+
+ edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA,
+ fsl_edma->membase + EDMA_CR);
+
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(fsl_edma_pm_ops, fsl_edma_suspend, fsl_edma_resume);
+
+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,
+ .pm = &fsl_edma_pm_ops,
+ },
+ .probe = fsl_edma_probe,
+ .remove = fsl_edma_remove,
+};
+
+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");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b6ed304..80a674d 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -163,10 +163,10 @@ config GPIO_MPC5200
config GPIO_MPC8XXX
bool "MPC512x/MPC8xxx GPIO support"
depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
- FSL_SOC_BOOKE || PPC_86xx
+ FSL_SOC_BOOKE || PPC_86xx || ARCH_LAYERSCAPE
help
Say Y here if you're going to use hardware that connects to the
- MPC512x/831x/834x/837x/8572/8610 GPIOs.
+ MPC512x/831x/834x/837x/8572/8610/LS1021A GPIOs.
config GPIO_MSM_V1
tristate "Qualcomm MSM GPIO v1"
@@ -193,7 +193,7 @@ config GPIO_MVEBU
config GPIO_MXC
def_bool y
- depends on ARCH_MXC
+ depends on ARCH_MXC && !ARCH_LAYERSCAPE
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 2aa3ca2..6333085 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -1,5 +1,5 @@
/*
- * GPIOs on MPC512x/8349/8572/8610 and compatible
+ * GPIOs on MPC512x/8349/8572/8610/LS1020A and compatible
*
* Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
*
@@ -14,9 +14,11 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#define MPC8XXX_GPIO_PINS 32
@@ -56,9 +58,17 @@ static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
- mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT);
+ mpc8xxx_gc->data = ioread32be(mm->regs + GPIO_DAT);
}
+/* Generic set and clear bits accessor ports */
+#define bgpio_setbits32(_addr, _v) \
+ iowrite32be(ioread32be(_addr) | (_v), (_addr))
+#define bgpio_clrbits32(_addr, _v) \
+ iowrite32be(ioread32be(_addr) & ~(_v), (_addr))
+#define bgpio_clrsetbits32(addr, clear, set) \
+ iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr))
+
/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs
* defined as output cannot be determined by reading GPDAT register,
* so we use shadow data register instead. The status of input pins
@@ -71,9 +81,9 @@ static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio)
struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
u32 out_mask, out_shadow;
- out_mask = in_be32(mm->regs + GPIO_DIR);
+ out_mask = ioread32be(mm->regs + GPIO_DIR);
- val = in_be32(mm->regs + GPIO_DAT) & ~out_mask;
+ val = ioread32be(mm->regs + GPIO_DAT) & ~out_mask;
out_shadow = mpc8xxx_gc->data & out_mask;
return (val | out_shadow) & mpc8xxx_gpio2mask(gpio);
@@ -83,7 +93,7 @@ static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
- return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio);
+ return ioread32be(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio);
}
static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
@@ -99,7 +109,7 @@ static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
else
mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio);
- out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+ iowrite32be(mpc8xxx_gc->data, mm->regs + GPIO_DAT);
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
}
@@ -112,7 +122,7 @@ static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
+ bgpio_clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
@@ -129,7 +139,7 @@ static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
+ bgpio_setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio));
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
@@ -163,7 +173,8 @@ static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc)
struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
unsigned int mask;
- mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR);
+ mask = ioread32be(mm->regs + GPIO_IER)
+ & ioread32be(mm->regs + GPIO_IMR);
if (mask)
generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq,
32 - ffs(mask)));
@@ -179,7 +190,8 @@ static void mpc8xxx_irq_unmask(struct irq_data *d)
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ bgpio_setbits32(mm->regs + GPIO_IMR,
+ mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
}
@@ -192,7 +204,8 @@ static void mpc8xxx_irq_mask(struct irq_data *d)
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ bgpio_clrbits32(mm->regs + GPIO_IMR,
+ mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
}
@@ -202,7 +215,7 @@ static void mpc8xxx_irq_ack(struct irq_data *d)
struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d);
struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
- out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
+ iowrite32be(mpc8xxx_gpio2mask(irqd_to_hwirq(d)), mm->regs + GPIO_IER);
}
static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -214,14 +227,14 @@ static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
switch (flow_type) {
case IRQ_TYPE_EDGE_FALLING:
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- setbits32(mm->regs + GPIO_ICR,
+ bgpio_setbits32(mm->regs + GPIO_ICR,
mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_BOTH:
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(mm->regs + GPIO_ICR,
+ bgpio_clrbits32(mm->regs + GPIO_ICR,
mpc8xxx_gpio2mask(irqd_to_hwirq(d)));
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@@ -254,20 +267,20 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type)
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrsetbits_be32(reg, 3 << shift, 2 << shift);
+ bgpio_clrsetbits32(reg, 3 << shift, 2 << shift);
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrsetbits_be32(reg, 3 << shift, 1 << shift);
+ bgpio_clrsetbits32(reg, 3 << shift, 1 << shift);
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
case IRQ_TYPE_EDGE_BOTH:
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
- clrbits32(reg, 3 << shift);
+ bgpio_clrbits32(reg, 3 << shift);
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
break;
@@ -312,6 +325,7 @@ static struct of_device_id mpc8xxx_gpio_ids[] __initdata = {
{ .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, },
{ .compatible = "fsl,pq3-gpio", },
{ .compatible = "fsl,qoriq-gpio", },
+ { .compatible = "fsl,ls1021a-gpio", },
{}
};
@@ -363,8 +377,8 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
mpc8xxx_gc->of_dev_id_data = id->data;
/* ack and mask all irqs */
- out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
- out_be32(mm_gc->regs + GPIO_IMR, 0);
+ iowrite32be(0xffffffff, mm_gc->regs + GPIO_IER);
+ iowrite32be(0, mm_gc->regs + GPIO_IMR);
irq_set_handler_data(hwirq, mpc8xxx_gc);
irq_set_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade);
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/hwmon.c b/drivers/hwmon/hwmon.c
index 646314f..a26c385 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -15,45 +15,138 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
#include <linux/hwmon.h>
#include <linux/gfp.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
+#include <linux/string.h>
#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;
+
+ /* 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);
- 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;
+ 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);
+
+/**
+ * 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);
@@ -75,6 +168,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
@@ -105,19 +261,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/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c
new file mode 100644
index 0000000..3701b32
--- /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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+
+/* 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:
+ WARN_ONCE(1, "Bad register: 0x%x\n", reg);
+ return -EINVAL;
+ }
+ /* 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, &ltc2945_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 <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LTC2945 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index bad5b84..687ecb3 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -41,6 +41,7 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
+#include <linux/pm.h>
#include <linux/i2c/pca954x.h>
@@ -274,9 +275,23 @@ static int pca954x_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int pca954x_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pca954x *data = i2c_get_clientdata(client);
+
+ data->last_chan = 0;
+ return i2c_smbus_write_byte(client, 0);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
+
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "pca954x",
+ .pm = &pca954x_pm,
.owner = THIS_MODULE,
},
.probe = pca954x_probe,
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 29a11db..57721ed 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -50,4 +50,12 @@ config TEGRA30_MC
analysis, especially for IOMMU/SMMU(System Memory Management
Unit) module.
+config FSL_IFC
+ bool "Freescale Integrated Flash Controller"
+ depends on FSL_SOC
+ help
+ This driver is for the Integrated Flash Controller(IFC) module
+ available in Freescale SoCs. This controller allows to handle
+ devices such as NOR, NAND, FPGA and ASIC etc.
+
endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 969d923..d1961c5 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -6,6 +6,7 @@ ifeq ($(CONFIG_DDR),y)
obj-$(CONFIG_OF) += of_memory.o
endif
obj-$(CONFIG_TI_EMIF) += emif.o
+obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
diff --git a/arch/powerpc/sysdev/fsl_ifc.c b/drivers/memory/fsl_ifc.c
index 592e029..93a8cac 100644
--- a/arch/powerpc/sysdev/fsl_ifc.c
+++ b/drivers/memory/fsl_ifc.c
@@ -29,10 +29,14 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/fsl_ifc.h>
#include <asm/prom.h>
-#include <asm/fsl_ifc.h>
struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
EXPORT_SYMBOL(fsl_ifc_ctrl_dev);
@@ -66,7 +70,8 @@ int fsl_ifc_find(phys_addr_t addr_base)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) {
- u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
+ u32 cspr = ifc_in32(
+ &fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
if (cspr & CSPR_V && (cspr & CSPR_BA) ==
convert_ifc_address(addr_base))
return i;
@@ -83,16 +88,16 @@ static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
/*
* Clear all the common status and event registers
*/
- if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
+ if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
/* enable all error and events */
- out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN);
+ ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en);
/* enable all error and event interrupts */
- out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN);
- out_be32(&ifc->cm_erattr0, 0x0);
- out_be32(&ifc->cm_erattr1, 0x0);
+ ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en);
+ ifc_out32(0x0, &ifc->cm_erattr0);
+ ifc_out32(0x0, &ifc->cm_erattr1);
return 0;
}
@@ -131,9 +136,9 @@ static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
spin_lock_irqsave(&nand_irq_lock, flags);
- stat = in_be32(&ifc->ifc_nand.nand_evter_stat);
+ stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
if (stat) {
- out_be32(&ifc->ifc_nand.nand_evter_stat, stat);
+ ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat);
ctrl->nand_stat = stat;
wake_up(&ctrl->nand_wait);
}
@@ -165,36 +170,37 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
irqreturn_t ret = IRQ_NONE;
/* read for chip select error */
- cs_err = in_be32(&ifc->cm_evter_stat);
+ cs_err = ifc_in32(&ifc->cm_evter_stat);
if (cs_err) {
- dev_err(ctrl->dev, "transaction sent to IFC is not mapped to"
- "any memory bank 0x%08X\n", cs_err);
+ dev_err(ctrl->dev, "transaction sent to IFC is not mapped to");
+ dev_err(ctrl->dev, " any memory bank 0x%08X\n", cs_err);
+
/* clear the chip select error */
- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
/* read error attribute registers print the error information */
- status = in_be32(&ifc->cm_erattr0);
- err_addr = in_be32(&ifc->cm_erattr1);
-
- if (status & IFC_CM_ERATTR0_ERTYP_READ)
- dev_err(ctrl->dev, "Read transaction error"
- "CM_ERATTR0 0x%08X\n", status);
- else
- dev_err(ctrl->dev, "Write transaction error"
- "CM_ERATTR0 0x%08X\n", status);
-
+ status = ifc_in32(&ifc->cm_erattr0);
+ err_addr = ifc_in32(&ifc->cm_erattr1);
+
+ if (status & IFC_CM_ERATTR0_ERTYP_READ) {
+ dev_err(ctrl->dev, "Read transaction error");
+ dev_err(ctrl->dev, " CM_ERATTR0 0x%08X\n", status);
+ } else {
+ dev_err(ctrl->dev, "Write transaction error");
+ dev_err(ctrl->dev, " CM_ERATTR0 0x%08X\n", status);
+ }
err_axiid = (status & IFC_CM_ERATTR0_ERAID) >>
IFC_CM_ERATTR0_ERAID_SHIFT;
- dev_err(ctrl->dev, "AXI ID of the error"
- "transaction 0x%08X\n", err_axiid);
+ dev_err(ctrl->dev, "AXI ID of the erro");
+ dev_err(ctrl->dev, " transaction 0x%08X\n", err_axiid);
err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >>
IFC_CM_ERATTR0_ESRCID_SHIFT;
- dev_err(ctrl->dev, "SRC ID of the error"
- "transaction 0x%08X\n", err_srcid);
+ dev_err(ctrl->dev, "SRC ID of the error");
+ dev_err(ctrl->dev, " transaction 0x%08X\n", err_srcid);
- dev_err(ctrl->dev, "Transaction Address corresponding to error"
- "ERADDR 0x%08X\n", err_addr);
+ dev_err(ctrl->dev, "Transaction Address corresponding to error");
+ dev_err(ctrl->dev, " ERADDR 0x%08X\n", err_addr);
ret = IRQ_HANDLED;
}
@@ -235,11 +241,19 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
goto err;
}
+ if (of_property_read_bool(dev->dev.of_node, "little-endian"))
+ fsl_ifc_ctrl_dev->little_endian = true;
+ else
+ fsl_ifc_ctrl_dev->little_endian = false;
+
+ dev_dbg(&dev->dev, "IFC REGISTERS are %s endian\n",
+ fsl_ifc_ctrl_dev->little_endian ? "LITTLE" : "BIG");
/* get the Controller level irq */
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_ifc_ctrl_dev->irq == NO_IRQ) {
- dev_err(&dev->dev, "failed to get irq resource "
- "for IFC\n");
+ dev_err(&dev->dev, "failed to get irq resource ");
+ dev_err(&dev->dev, "for IFC\n");
+
ret = -ENODEV;
goto err;
}
@@ -316,15 +330,18 @@ static int fsl_ifc_resume(struct device *dev)
ctrl->saved_regs = NULL;
}
- ver = in_be32(&ctrl->regs->ifc_rev);
- ncfgr = in_be32(&ifc->ifc_nand.ncfgr);
+ ver = ifc_in32(&ctrl->regs->ifc_rev);
+ ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr);
if (ver >= FSL_IFC_V1_3_0) {
- out_be32(&ifc->ifc_nand.ncfgr, ncfgr | IFC_NAND_SRAM_INIT_EN);
+ ifc_out32(&ifc->ifc_nand.ncfgr,
+ ncfgr | IFC_NAND_SRAM_INIT_EN);
/* wait for SRAM_INIT bit to be clear or timeout */
- status = spin_event_timeout(!(in_be32(&ifc->ifc_nand.ncfgr)
- & IFC_NAND_SRAM_INIT_EN),
- IFC_TIMEOUT_MSECS, 0);
+ status = spin_event_timeout(
+ !(ifc_in32(&ifc->ifc_nand.ncfgr)
+ & IFC_NAND_SRAM_INIT_EN),
+ IFC_TIMEOUT_MSECS, 0);
+
if (!status)
dev_err(ctrl->dev, "Timeout waiting for IFC SRAM INIT");
}
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 7fc5099..414184d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -107,7 +107,7 @@ config MMC_SDHCI_PLTFM
config MMC_SDHCI_OF_ESDHC
tristate "SDHCI OF support for the Freescale eSDHC controller"
depends on MMC_SDHCI_PLTFM
- depends on PPC_OF
+ depends on PPC_OF || ARCH_MXC
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
help
This selects the Freescale eSDHC controller support.
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 0f9f36b..244c42a 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -21,18 +21,23 @@
#include <linux/mmc/host.h>
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
+
+#if defined CONFIG_PPC_OF
#include <asm/mpc85xx.h>
+#endif
#define VENDOR_V_22 0x12
#define VENDOR_V_23 0x13
+#if defined CONFIG_PPC_OF
static u32 svr;
+#endif
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 +49,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,15 +73,17 @@ 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;
+#if defined CONFIG_PPC_OF
/* T4240-R1.0-R2.0 had a incorrect vendor version and spec version */
if ((reg == SDHCI_HOST_VERSION) &&
((SVR_SOC_VER(svr) == SVR_T4240) &&
(SVR_REV(svr) <= 0x20)))
ret = (VENDOR_V_23 << SDHCI_VENDOR_VER_SHIFT) | SDHCI_SPEC_200;
+#endif
return ret;
}
@@ -85,7 +92,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 +104,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 +127,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 +157,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 +178,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 +203,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);
@@ -187,19 +216,21 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
if (!host->pwr || !val)
return;
+#if defined CONFIG_PPC_OF
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
vol |= ESDHC_VOL_SEL;
} else
return;
+#endif
}
- sdhci_be32bs_writeb(host, val, reg);
+ sdhci_clrsetbits(host, 0xff, val, reg);
}
/*
@@ -216,7 +247,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,11 +265,12 @@ 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;
}
+#if defined CONFIG_PPC_OF
/*
* Check for A-004388: eSDHC DMA might not stop if error
* occurs on system transaction
@@ -263,6 +295,7 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
((SVR_SOC_VER(svr) == SVR_P5021) && (SVR_REV(svr) <= 0x21)) ||
((SVR_SOC_VER(svr) == SVR_P5040) && (SVR_REV(svr) <= 0x21))))
return;
+#endif
sdhci_reset(host, SDHCI_RESET_DATA);
@@ -316,7 +349,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 +386,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
@@ -389,8 +423,10 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
{
u32 vvn;
+#if defined CONFIG_PPC_OF
svr = mfspr(SPRN_SVR);
- vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
+#endif
+ 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;
@@ -398,6 +434,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
if (vvn > VENDOR_V_22)
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
+#if defined CONFIG_PPC_OF
/*
* Check for A-005055: A glitch is generated on the card clock
* due to software reset or a clock change
@@ -424,6 +461,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
((SVR_SOC_VER(svr) == SVR_P1014) && (SVR_REV(svr) == 0x10)) ||
((SVR_SOC_VER(svr) == SVR_P1010) && (SVR_REV(svr) == 0x10)))
host->quirks2 |= SDHCI_QUIRK2_DISABLE_CLOCK_BEFORE_RESET;
+#endif
}
/* Return: 1 - the card is present; 0 - card is absent */
@@ -437,18 +475,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 +524,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 +566,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 +586,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 */
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/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 74ab4b7..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,
@@ -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 dfa5af13..92a14fb 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -15,922 +15,178 @@
*
*/
-#include <linux/init.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/math64.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/mod_devicetable.h>
-#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
+#include <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_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 */
-
-/* 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_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 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)
-
-/****************************************************************************/
-
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;
- bool fast_read;
+ u8 command[MAX_CMD_SIZE];
};
-static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
+static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
- 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);
+ struct m25p *flash = nor->priv;
+ struct spi_device *spi = flash->spi;
+ int ret;
- if (retval < 0) {
- dev_err(&flash->spi->dev, "error %d reading SR\n",
- (int) retval);
- return retval;
- }
-
- 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;
+ ret = spi_write_then_read(spi, &code, 1, val, len);
+ if (ret < 0)
+ dev_err(&spi->dev, "error %d reading %x\n", ret, code);
- return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+ return ret;
}
-/*
- * Enable/disable 4-byte addressing mode.
- */
-static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
+static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
{
- 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);
- }
+ /* opcode is in cmd[0] */
+ 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);
}
-/*
- * Service routine to read status register until ready, or timeout occurs.
- * Returns non-zero if error.
- */
-static int wait_till_ready(struct m25p *flash)
+static int m25p_cmdsz(struct spi_nor *nor)
{
- 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;
+ return 1 + nor->addr_width;
}
-/*
- * Erase the whole flash memory
- *
- * Returns 0 if successful, non-zero otherwise.
- */
-static int erase_chip(struct m25p *flash)
+static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
+ int wr_en)
{
- pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__,
- (long long)(flash->mtd.size >> 10));
+ struct m25p *flash = nor->priv;
+ struct spi_device *spi = flash->spi;
- /* 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);
+ flash->command[0] = opcode;
+ if (buf)
+ memcpy(&flash->command[1], buf, len);
- return 0;
+ return spi_write(spi, flash->command, len + 1);
}
-static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
+static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
{
- /* 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);
-}
+ 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);
-static int m25p_cmdsz(struct m25p *flash)
-{
- return 1 + flash->addr_width;
-}
+ spi_message_init(&m);
-/*
- * 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)
-{
- pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev),
- __func__, flash->mtd.erasesize / 1024, offset);
+ if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+ cmd_sz = 1;
- /* Wait until finished previous write command. */
- if (wait_till_ready(flash))
- return 1;
+ flash->command[0] = nor->program_opcode;
+ m25p_addr2cmd(nor, to, flash->command);
- /* Send write enable, then erase commands. */
- write_enable(flash);
+ t[0].tx_buf = flash->command;
+ t[0].len = cmd_sz;
+ spi_message_add_tail(&t[0], &m);
- /* Set up command buffer. */
- flash->command[0] = flash->erase_opcode;
- m25p_addr2cmd(flash, offset, flash->command);
+ t[1].tx_buf = buf;
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
- spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
+ spi_sync(spi, &m);
- return 0;
+ *retlen += m.actual_length - cmd_sz;
}
-/****************************************************************************/
-
-/*
- * 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 inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
{
- 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;
- }
-
- /* 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 (erase_sector(flash, addr)) {
- instr->state = MTD_ERASE_FAILED;
- mutex_unlock(&flash->lock);
- return -EIO;
- }
-
- addr += mtd->erasesize;
- len -= mtd->erasesize;
- }
+ switch (nor->flash_read) {
+ case SPI_NOR_DUAL:
+ return 2;
+ case SPI_NOR_QUAD:
+ return 4;
+ default:
+ return 0;
}
-
- mutex_unlock(&flash->lock);
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
- return 0;
}
/*
- * 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;
-
- pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
- __func__, (u32)from, 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);
-
- t[1].rx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ unsigned int dummy = nor->read_dummy;
+ int ret;
- mutex_lock(&flash->lock);
+ /* convert the dummy cycles to the number of bytes */
+ dummy /= 8;
/* Wait till previous write/erase is done. */
- if (wait_till_ready(flash)) {
- /* REVISIT status return?? */
- mutex_unlock(&flash->lock);
- 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;
- m25p_addr2cmd(flash, from, flash->command);
-
- spi_sync(flash->spi, &m);
-
- *retlen = m.actual_length - m25p_cmdsz(flash) -
- (flash->fast_read ? 1 : 0);
-
- mutex_unlock(&flash->lock);
-
- 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)
-{
- 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);
+ ret = nor->wait_till_ready(nor);
+ if (ret)
+ return ret;
spi_message_init(&m);
memset(t, 0, (sizeof t));
+ flash->command[0] = nor->read_opcode;
+ m25p_addr2cmd(nor, from, flash->command);
+
t[0].tx_buf = flash->command;
- t[0].len = m25p_cmdsz(flash);
+ t[0].len = m25p_cmdsz(nor) + dummy;
spi_message_add_tail(&t[0], &m);
- t[1].tx_buf = buf;
+ t[1].rx_buf = buf;
+ t[1].rx_nbits = m25p80_rx_nbits(nor);
+ t[1].len = len;
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);
+ spi_sync(spi, &m);
+ *retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
return 0;
}
-static int sst_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);
- 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);
+ struct m25p *flash = nor->priv;
+ int ret;
- 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);
+ 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);
+ return ret;
- /* 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;
-
- 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, SPINOR_OP_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 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), \
- })
+ return ret;
-/* 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) },
- { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, 0) },
- { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
-
- /* 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) },
- { "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, 0) },
-
- /* 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) },
- { "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) },
-
- /* 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, 0) },
- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) },
- { "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) },
- { "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) },
-
- { "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;
-
- /* 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)
- continue;
- 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
@@ -938,205 +194,46 @@ 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, ret;
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))
- return -ENODEV;
-#endif
-
- /* 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;
- }
- }
+ struct flash_platform_data *data;
+ struct m25p *flash;
+ struct spi_nor *nor;
+ enum read_mode mode = SPI_NOR_NORMAL;
+ int ret;
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;
+ nor = &flash->spi_nor;
- 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
- */
-
- 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 {
- 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;
- }
-
- 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->np = spi->dev.of_node;
+ 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;
+ 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;
+ 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;
-
- flash->fast_read = false;
- if (np && of_property_read_bool(np, "m25p,fast-read"))
- flash->fast_read = true;
-
-#ifdef CONFIG_M25PXX_USE_FAST_READ
- flash->fast_read = true;
-#endif
- if (info->flags & M25P_NO_FR)
- flash->fast_read = false;
-
- /* Default commands */
- if (flash->fast_read)
- flash->read_opcode = OPCODE_FAST_READ;
- else
- flash->read_opcode = OPCODE_NORM_READ;
-
- 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 */
- flash->read_opcode = flash->fast_read ?
- OPCODE_FAST_READ_4B :
- OPCODE_NORM_READ_4B;
- 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);
@@ -1148,9 +245,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);
}
@@ -1159,7 +254,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,
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 8639a42..d9341d4 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -29,7 +29,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand_ecc.h>
-#include <asm/fsl_ifc.h>
+#include <linux/of_address.h>
+#include <linux/fsl_ifc.h>
#define FSL_IFC_V1_1_0 0x01010000
#define ERR_BYTE 0xFF /* Value returned for read
@@ -239,8 +240,9 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
ifc_nand_ctrl->page = page_addr;
/* Program ROW0/COL0 */
- iowrite32be(page_addr, &ifc->ifc_nand.row0);
- iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0);
+ ifc_out32(page_addr, &ifc->ifc_nand.row0);
+ ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column,
+ &ifc->ifc_nand.col0);
buf_num = page_addr & priv->bufnum_mask;
@@ -302,19 +304,20 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
int i;
/* set the chip select for NAND Transaction */
- iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT,
+ ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT,
&ifc->ifc_nand.nand_csel);
dev_vdbg(priv->dev,
"%s: fir0=%08x fcr0=%08x\n",
__func__,
- ioread32be(&ifc->ifc_nand.nand_fir0),
- ioread32be(&ifc->ifc_nand.nand_fcr0));
+ ifc_in32(&ifc->ifc_nand.nand_fir0),
+ ifc_in32(&ifc->ifc_nand.nand_fcr0));
ctrl->nand_stat = 0;
/* start read/write seq */
- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT,
+ &ifc->ifc_nand.nandseq_strt);
/* wait for command complete flag or timeout */
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
@@ -337,7 +340,8 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
int sector_end = sector + chip->ecc.steps - 1;
for (i = sector / 4; i <= sector_end / 4; i++)
- eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]);
+ eccstat[i] = ifc_in32(
+ &ifc->ifc_nand.nand_eccstat[i]);
for (i = sector; i <= sector_end; i++) {
errors = check_read_ecc(mtd, ctrl, eccstat, i);
@@ -377,31 +381,31 @@ static void fsl_ifc_do_read(struct nand_chip *chip,
/* Program FIR/IFC_NAND_FCR0 for Small/Large page */
if (mtd->writesize > 512) {
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
(IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
(IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
- iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
(NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT),
&ifc->ifc_nand.nand_fcr0);
} else {
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
(IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
if (oob)
- iowrite32be(NAND_CMD_READOOB <<
+ ifc_out32(NAND_CMD_READOOB <<
IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
else
- iowrite32be(NAND_CMD_READ0 <<
+ ifc_out32(NAND_CMD_READ0 <<
IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
}
@@ -423,7 +427,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
switch (command) {
/* READ0 read the entire buffer to use hardware ECC. */
case NAND_CMD_READ0:
- iowrite32be(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, 0, page_addr, 0);
ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
@@ -438,7 +442,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* READOOB reads only the OOB because no ECC is performed. */
case NAND_CMD_READOOB:
- iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(mtd->oobsize - column,
+ &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, column, page_addr, 1);
ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
@@ -454,19 +459,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
if (command == NAND_CMD_PARAM)
timing = IFC_FIR_OP_RBCD;
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
(timing << IFC_NAND_FIR0_OP2_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT,
+ ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
- iowrite32be(column, &ifc->ifc_nand.row3);
+ ifc_out32(column, &ifc->ifc_nand.row3);
/*
* although currently it's 8 bytes for READID, we always read
* the maximum 256 bytes(for PARAM)
*/
- iowrite32be(256, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
ifc_nand_ctrl->read_bytes = 256;
set_addr(mtd, 0, 0, 0);
@@ -481,16 +486,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* ERASE2 uses the block and page address from ERASE1 */
case NAND_CMD_ERASE2:
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
(NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT),
&ifc->ifc_nand.nand_fcr0);
- iowrite32be(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
ifc_nand_ctrl->read_bytes = 0;
fsl_ifc_run_command(mtd);
return;
@@ -507,14 +512,14 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) |
(NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT);
- iowrite32be(
+ ifc_out32(
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
(IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) |
(IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(
+ ifc_out32(
(IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) |
(IFC_FIR_OP_RDSTAT <<
IFC_NAND_FIR1_OP6_SHIFT) |
@@ -528,14 +533,14 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
(NAND_CMD_STATUS <<
IFC_NAND_FCR0_CMD3_SHIFT));
- iowrite32be(
+ ifc_out32(
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) |
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |
(IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(
+ ifc_out32(
(IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) |
(IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) |
(IFC_FIR_OP_RDSTAT <<
@@ -556,7 +561,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
column -= mtd->writesize;
ifc_nand_ctrl->oob = 1;
}
- iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0);
set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob);
return;
}
@@ -564,11 +569,11 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
case NAND_CMD_PAGEPROG: {
if (ifc_nand_ctrl->oob) {
- iowrite32be(ifc_nand_ctrl->index -
+ ifc_out32(ifc_nand_ctrl->index -
ifc_nand_ctrl->column,
&ifc->ifc_nand.nand_fbcr);
} else {
- iowrite32be(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
}
fsl_ifc_run_command(mtd);
@@ -576,12 +581,12 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
}
case NAND_CMD_STATUS:
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
- iowrite32be(1, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, 0, 0, 0);
ifc_nand_ctrl->read_bytes = 1;
@@ -591,13 +596,14 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
* The chip always seems to report that it is
* write-protected, even when it is not.
*/
- setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP);
+ ifc_out8((ifc_in8(ifc_nand_ctrl->addr) | (NAND_STATUS_WP)),
+ ifc_nand_ctrl->addr);
return;
case NAND_CMD_RESET:
- iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT,
+ ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT,
&ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT,
+ ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
fsl_ifc_run_command(mtd);
return;
@@ -654,7 +660,8 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
* next byte.
*/
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes)
- return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]);
+ return ifc_in8(
+ &ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]);
dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
return ERR_BYTE;
@@ -675,7 +682,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
* next byte.
*/
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
- data = in_be16((uint16_t __iomem *)&ifc_nand_ctrl->
+ data = ifc_in16((uint16_t __iomem *)&ifc_nand_ctrl->
addr[ifc_nand_ctrl->index]);
ifc_nand_ctrl->index += 2;
return (uint8_t) data;
@@ -722,18 +729,18 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
u32 nand_fsr;
/* Use READ_STATUS command, but wait for the device to be ready */
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
- iowrite32be(1, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, 0, 0, 0);
ifc_nand_ctrl->read_bytes = 1;
fsl_ifc_run_command(mtd);
- nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr);
+ nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
/*
* The chip always seems to report that it is
@@ -827,34 +834,36 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
uint32_t cs = priv->bank;
/* Save CSOR and CSOR_ext */
- csor = ioread32be(&ifc->csor_cs[cs].csor);
- csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext);
+ csor = ifc_in32(&ifc->csor_cs[cs].csor);
+ csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext);
/* chage PageSize 8K and SpareSize 1K*/
csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
- iowrite32be(csor_8k, &ifc->csor_cs[cs].csor);
- iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext);
+ ifc_out32(csor_8k, &ifc->csor_cs[cs].csor);
+ ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext);
/* READID */
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
+ ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
- iowrite32be(0x0, &ifc->ifc_nand.row3);
+ ifc_out32(0x0, &ifc->ifc_nand.row3);
- iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr);
/* Program ROW0/COL0 */
- iowrite32be(0x0, &ifc->ifc_nand.row0);
- iowrite32be(0x0, &ifc->ifc_nand.col0);
+ ifc_out32(0x0, &ifc->ifc_nand.row0);
+ ifc_out32(0x0, &ifc->ifc_nand.col0);
/* set the chip select for NAND Transaction */
- iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel);
+ ifc_out32(cs << IFC_NAND_CSEL_SHIFT,
+ &ifc->ifc_nand.nand_csel);
/* start read seq */
- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT,
+ &ifc->ifc_nand.nandseq_strt);
/* wait for command complete flag or timeout */
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
@@ -864,8 +873,8 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
/* Restore CSOR and CSOR_ext */
- iowrite32be(csor, &ifc->csor_cs[cs].csor);
- iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext);
+ ifc_out32(csor, &ifc->csor_cs[cs].csor);
+ ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext);
}
static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
@@ -882,7 +891,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* fill in nand_chip structure */
/* set up function call table */
- if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16)
+ if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) &
+ CSPR_PORT_SIZE_16)
chip->read_byte = fsl_ifc_read_byte16;
else
chip->read_byte = fsl_ifc_read_byte;
@@ -896,13 +906,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
- iowrite32be(0x0, &ifc->ifc_nand.ncfgr);
+ ifc_out32(0x0, &ifc->ifc_nand.ncfgr);
/* set up nand options */
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->options = NAND_NO_SUBPAGE_WRITE;
- if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
+ if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr)
+ & CSPR_PORT_SIZE_16) {
chip->read_byte = fsl_ifc_read_byte16;
chip->options |= NAND_BUSWIDTH_16;
} else {
@@ -915,7 +926,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.read_page = fsl_ifc_read_page;
chip->ecc.write_page = fsl_ifc_write_page;
- csor = ioread32be(&ifc->csor_cs[priv->bank].csor);
+ csor = ifc_in32(&ifc->csor_cs[priv->bank].csor);
/* Hardware generates ECC per 512 Bytes */
chip->ecc.size = 512;
@@ -981,7 +992,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.mode = NAND_ECC_SOFT;
}
- ver = ioread32be(&ifc->ifc_rev);
+ ver = ifc_in32(&ifc->ifc_rev);
if (ver == FSL_IFC_V1_1_0)
fsl_ifc_sram_init(priv);
@@ -1006,7 +1017,7 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank,
phys_addr_t addr)
{
- u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr);
+ u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr);
if (!(cspr & CSPR_V))
return 0;
@@ -1093,13 +1104,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
dev_set_drvdata(priv->dev, priv);
- iowrite32be(IFC_NAND_EVTER_EN_OPC_EN |
+ ifc_out32(IFC_NAND_EVTER_EN_OPC_EN |
IFC_NAND_EVTER_EN_FTOER_EN |
IFC_NAND_EVTER_EN_WPER_EN,
&ifc->ifc_nand.nand_evter_en);
/* enable NAND Machine Interrupts */
- iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN |
+ ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN |
IFC_NAND_EVTER_INTR_FTOERIR_EN |
IFC_NAND_EVTER_INTR_WPERIR_EN,
&ifc->ifc_nand.nand_evter_intr_en);
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
new file mode 100644
index 0000000..64cfc39
--- /dev/null
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -0,0 +1,12 @@
+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.
+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
new file mode 100644
index 0000000..51f9d8b
--- /dev/null
+++ b/drivers/mtd/spi-nor/Makefile
@@ -0,0 +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..2dd29c3
--- /dev/null
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -0,0 +1,1098 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/completion.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+
+/* 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
+#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,
+ FSL_QUADSPI_LS1,
+};
+
+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
+};
+
+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];
+ 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;
+}
+
+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.
+ */
+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)
+{
+ 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)
+{
+ 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)
+{
+ struct fsl_qspi *q = dev_id;
+ u32 reg;
+
+ /* clear interrupt */
+ 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);
+
+ 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;
+ struct spi_nor *nor = &q->nor[0];
+ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
+ u32 lut_base;
+ u8 op, dm;
+ int i;
+
+ fsl_qspi_unlock_lut(q);
+
+ /* Clear all the LUT table */
+ for (i = 0; i < QUADSPI_LUT_NUM; i++)
+ qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
+
+ /* Quad Read and DDR Quad Read*/
+ lut_base = SEQID_QUAD_READ * 4;
+ 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 */
+ qspi_writel(q,
+ LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ 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);
+ }
+ } 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. */
+ qspi_writel(q, LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD4, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff)
+ | LUT1(DUMMY, PAD1, dm),
+ base + QUADSPI_LUT(lut_base + 1));
+
+ 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. */
+ qspi_writel(q, LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(DUMMY, PAD1, dm)
+ | LUT1(READ_DDR, PAD4, rxfifo),
+ base + QUADSPI_LUT(lut_base + 1));
+
+ 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);
+ }
+ }
+
+ /* Write enable */
+ lut_base = SEQID_WREN * 4;
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN),
+ base + QUADSPI_LUT(lut_base));
+
+ /* Page Program */
+ lut_base = SEQID_PP * 4;
+ qspi_writel(q, LUT0(CMD, PAD1,
+ nor->program_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+ qspi_writel(q, LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
+
+ /* Read Status */
+ lut_base = SEQID_RDSR * 4;
+ 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;
+ 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;
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE),
+ base + QUADSPI_LUT(lut_base));
+
+ /* READ ID */
+ lut_base = SEQID_RDID * 4;
+ 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;
+ 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;
+ 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;
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRDI),
+ base + QUADSPI_LUT(lut_base));
+
+ /* Enter 4 Byte Mode (Micron) */
+ lut_base = SEQID_EN4B * 4;
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_EN4B),
+ base + QUADSPI_LUT(lut_base));
+
+ /* Enter 4 Byte Mode (Spansion) */
+ lut_base = SEQID_BRWR * 4;
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_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 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:
+ case SPINOR_OP_READ_1_1_4:
+ return SEQID_QUAD_READ;
+ case SPINOR_OP_WREN:
+ return SEQID_WREN;
+ case SPINOR_OP_WRDI:
+ 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:
+ return SEQID_CHIP_ERASE;
+ case SPINOR_OP_PP:
+ return SEQID_PP;
+ case SPINOR_OP_RDID:
+ return SEQID_RDID;
+ case SPINOR_OP_WRSR:
+ return SEQID_WRSR;
+ case SPINOR_OP_RDCR:
+ return SEQID_RDCR;
+ case SPINOR_OP_EN4B:
+ return SEQID_EN4B;
+ case SPINOR_OP_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 = qspi_readl(q, base + QUADSPI_MCR);
+
+ 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);
+ qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR);
+
+ do {
+ 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);
+ continue;
+ }
+ break;
+ } while (1);
+
+ /* trigger the LUT now */
+ seqid = fsl_qspi_get_seqid(q, cmd);
+ 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, qspi_readl(q, base + QUADSPI_FR),
+ qspi_readl(q, base + QUADSPI_SR));
+ err = -ETIMEDOUT;
+ } else {
+ err = 0;
+ }
+
+ /* restore the MCR */
+ qspi_writel(q, 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 = 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);
+
+ 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 = qspi_readl(q, q->iobase + QUADSPI_MCR);
+ reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
+ qspi_writel(q, 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);
+ qspi_writel(q, 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 = 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);
+ qspi_writel(q, 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;
+
+ 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);
+}
+
+/*
+ * 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;
+ struct spi_nor *nor = &q->nor[0];
+ u32 reg, reg2;
+ int seqid;
+
+ /* AHB configuration for access buffer 0/1/2 .*/
+ 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 */
+ 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);
+ 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 = qspi_readl(q, q->iobase + QUADSPI_MCR);
+
+ /* Firstly, disable the module */
+ qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK, q->iobase
+ + QUADSPI_MCR);
+
+ /* Set the Sampling Register for DDR */
+ reg2 = qspi_readl(q, q->iobase + QUADSPI_SMPR);
+ reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK;
+ reg2 |= (2 << QUADSPI_SMPR_DDRSMP_SHIFT);
+ 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;
+
+ qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+ }
+}
+
+/* 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 */
+ qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK,
+ base + QUADSPI_MCR);
+
+ 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 */
+ qspi_writel(q, QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
+ base + QUADSPI_MCR);
+
+ /* enable the interrupt */
+ qspi_writel(q, 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, },
+ { .compatible = "fsl,ls1-qspi", .data = (void *)&ls1_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 == SPINOR_OP_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, SPINOR_OP_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;
+ enum read_mode mode = SPI_NOR_QUAD;
+ char modalias[40];
+ u32 dummy = 0;
+
+ /* skip the holes */
+ if (!has_second_chip)
+ i *= 2;
+
+ nor = &q->nor[i];
+ mtd = &q->mtd[i];
+
+ nor->mtd = mtd;
+ nor->dev = dev;
+ nor->np = np;
+ 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;
+
+ /* 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, mode);
+ 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 */
+ 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);
+ 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");
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
new file mode 100644
index 0000000..5df9d0d
--- /dev/null
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -0,0 +1,1183 @@
+/*
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/math64.h>
+
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/mtd.h>
+#include <linux/of_platform.h>
+#include <linux/spi/flash.h>
+#include <linux/mtd/spi-nor.h>
+
+/* 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, SPINOR_OP_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, SPINOR_OP_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)
+{
+ 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:
+ return 8;
+ 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, SPINOR_OP_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, SPINOR_OP_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, SPINOR_OP_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 ? SPINOR_OP_EN4B : SPINOR_OP_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, SPINOR_OP_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, SPINOR_OP_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 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.
+ */
+
+ /* "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;
+ u32 ext_id;
+
+ /* The size listed here is what works with SPINOR_OP_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 /* 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 /* 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) \
+ ((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) },
+ { "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) },
+ { "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) },
+ { },
+};
+EXPORT_SYMBOL_GPL(spi_nor_ids);
+
+static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
+{
+ int tmp;
+ u8 id[6];
+ u32 jedec;
+ u32 ext_jedec;
+ struct flash_info *info;
+ int matched = -1;
+
+ 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);
+ }
+ 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)
+ 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);
+ 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 = SPINOR_OP_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 = SPINOR_OP_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 = SPINOR_OP_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, SPINOR_OP_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, SPINOR_OP_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_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;
+ case CFI_MFR_ST: /* Micron, actually */
+ /* DTR quad read works with the Extended SPI protocol. */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+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 = SPINOR_OP_BE_4K;
+ mtd->erasesize = 4096;
+ } else if (info->flags & SECT_4K_PMC) {
+ nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+ mtd->erasesize = 4096;
+ } else {
+ nor->erase_opcode = SPINOR_OP_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;
+
+ /* 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");
+ 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_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;
+ }
+ break;
+ case SPI_NOR_QUAD:
+ nor->read_opcode = SPINOR_OP_READ_1_1_4;
+ break;
+ case SPI_NOR_DUAL:
+ nor->read_opcode = SPINOR_OP_READ_1_1_2;
+ break;
+ case SPI_NOR_FAST:
+ nor->read_opcode = SPINOR_OP_READ_FAST;
+ break;
+ case SPI_NOR_NORMAL:
+ nor->read_opcode = SPINOR_OP_READ;
+ break;
+ default:
+ dev_err(dev, "No Read opcode defined\n");
+ return -EINVAL;
+ }
+
+ nor->program_opcode = SPINOR_OP_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_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;
+ case SPI_NOR_DUAL:
+ nor->read_opcode = SPINOR_OP_READ4_1_1_2;
+ break;
+ case SPI_NOR_FAST:
+ nor->read_opcode = SPINOR_OP_READ4_FAST;
+ break;
+ case SPI_NOR_NORMAL:
+ nor->read_opcode = SPINOR_OP_READ4;
+ break;
+ }
+ nor->program_opcode = SPINOR_OP_PP_4B;
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_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;
+}
+EXPORT_SYMBOL_GPL(spi_nor_scan);
+
+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;
+}
+EXPORT_SYMBOL_GPL(spi_nor_match_id);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");
+MODULE_AUTHOR("Mike Lavender");
+MODULE_DESCRIPTION("framework for SPI NOR");
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 3c06947..5115a36 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -108,6 +108,18 @@ config CAN_FLEXCAN
---help---
Say Y here if you want to support for Freescale FlexCAN.
+config CAN_FLEXCAN_RX_FIFO_EN
+ tristate "Support for RX FIFO mode in Freescale FLEXCAN based chips"
+ depends on CAN_FLEXCAN
+ ---help---
+ Say Y here if your Freescale SoC's FlexCAN module has support for RX FIFO MODE.
+
+config CAN_FLEXCAN_ERRATA_ERR005829
+ tristate "ERRATA_ERR005829 applicable to certain variants of Freescale FLEXCAN based chips"
+ depends on CAN_FLEXCAN
+ ---help---
+ Say Y here if your Freescale SoC's FlexCAN module needs s/w workaround for ERRATA ERR005829.
+
config PCH_CAN
tristate "Intel EG20T PCH CAN controller"
depends on PCI
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index e381142..805a923 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -59,10 +59,11 @@
#define FLEXCAN_MCR_WAK_SRC BIT(19)
#define FLEXCAN_MCR_DOZE BIT(18)
#define FLEXCAN_MCR_SRX_DIS BIT(17)
+#define FLEXCAN_MCR_SRX_EN ~(BIT(17))
#define FLEXCAN_MCR_BCC BIT(16)
#define FLEXCAN_MCR_LPRIO_EN BIT(13)
#define FLEXCAN_MCR_AEN BIT(12)
-#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x1f)
+#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
#define FLEXCAN_MCR_IDAM_A (0 << 8)
#define FLEXCAN_MCR_IDAM_B (1 << 8)
#define FLEXCAN_MCR_IDAM_C (2 << 8)
@@ -92,6 +93,27 @@
#define FLEXCAN_CTRL_ERR_ALL \
(FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
+/* FLEXCAN control register 2 (CTRL2) bits */
+#define FLEXCAN_CRL2_ECRWRE BIT(29)
+#define FLEXCAN_CRL2_WRMFRZ BIT(28)
+#define FLEXCAN_CRL2_RFFN(x) (((x) & 0x0f) << 24)
+#define FLEXCAN_CRL2_TASD(x) (((x) & 0x1f) << 19)
+#define FLEXCAN_CRL2_MRP BIT(18)
+#define FLEXCAN_CRL2_RRS BIT(17)
+#define FLEXCAN_CRL2_EACEN BIT(16)
+
+/* FLEXCAN memory error control register (MECR) bits */
+#define FLEXCAN_MECR_ECRWRDIS BIT(31)
+#define FLEXCAN_MECR_HANCEI_MSK BIT(19)
+#define FLEXCAN_MECR_FANCEI_MSK BIT(18)
+#define FLEXCAN_MECR_CEI_MSK BIT(16)
+#define FLEXCAN_MECR_HAERRIE BIT(15)
+#define FLEXCAN_MECR_FAERRIE BIT(14)
+#define FLEXCAN_MECR_EXTERRIE BIT(13)
+#define FLEXCAN_MECR_RERRDIS BIT(9)
+#define FLEXCAN_MECR_ECCDIS BIT(8)
+#define FLEXCAN_MECR_NCEFAFRZ BIT(7)
+
/* FLEXCAN error and status register (ESR) bits */
#define FLEXCAN_ESR_TWRN_INT BIT(17)
#define FLEXCAN_ESR_RWRN_INT BIT(16)
@@ -125,17 +147,39 @@
FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT)
/* FLEXCAN interrupt flag register (IFLAG) bits */
+#ifdef CONFIG_CAN_FLEXCAN_ERRATA_ERR005829
+/* Errata ERR005829 step7: Reserve first valid MB */
+#define FLEXCAN_TX_BUF_RESERVED 8
+#define FLEXCAN_TX_BUF_ID 9
+#else
#define FLEXCAN_TX_BUF_ID 8
+#endif
#define FLEXCAN_IFLAG_BUF(x) BIT(x)
#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7)
#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6)
#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5)
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
#define FLEXCAN_IFLAG_DEFAULT \
(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | \
FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID))
+#else
+#define FLEXCAN_IFLAG_DEFAULT \
+ (FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID))
+#endif
/* FLEXCAN message buffers */
#define FLEXCAN_MB_CNT_CODE(x) (((x) & 0xf) << 24)
+#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
+#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
+#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
+#define FLEXCAN_MB_CODE_RX_OVERRRUN (0x6 << 24)
+#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)
+
+#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)
+#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24)
+#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24)
+#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24)
+
#define FLEXCAN_MB_CNT_SRR BIT(22)
#define FLEXCAN_MB_CNT_IDE BIT(21)
#define FLEXCAN_MB_CNT_RTR BIT(20)
@@ -150,18 +194,20 @@
* FLEXCAN hardware feature flags
*
* Below is some version info we got:
- * SOC Version IP-Version Glitch- [TR]WRN_INT
- * Filter? connected?
- * MX25 FlexCAN2 03.00.00.00 no no
- * MX28 FlexCAN2 03.00.04.00 yes yes
- * MX35 FlexCAN2 03.00.00.00 no no
- * MX53 FlexCAN2 03.00.00.00 yes no
- * MX6s FlexCAN3 10.00.12.00 yes yes
+ * SOC Version IP-Version Glitch- [TR]WRN_INT Memory err
+ * Filter? connected? detection
+ * MX25 FlexCAN2 03.00.00.00 no no no
+ * MX28 FlexCAN2 03.00.04.00 yes yes no
+ * MX35 FlexCAN2 03.00.00.00 no no no
+ * MX53 FlexCAN2 03.00.00.00 yes no no
+ * MX6s FlexCAN3 10.00.12.00 yes yes no
+ * VF610 FlexCAN3 ? no yes yes
*
* Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
*/
#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */
#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */
+#define FLEXCAN_HAS_MECR_FEATURES BIT(3) /* Memory error detection */
/* Structure of the message buffer */
struct flexcan_mb {
@@ -192,8 +238,17 @@ struct flexcan_regs {
u32 crcr; /* 0x44 */
u32 rxfgmask; /* 0x48 */
u32 rxfir; /* 0x4c */
- u32 _reserved3[12];
- struct flexcan_mb cantxfg[64];
+ u32 _reserved3[12]; /* 0x50 */
+ struct flexcan_mb cantxfg[64]; /* 0x80 */
+ u32 _reserved4[408];
+ u32 mecr; /* 0xae0 */
+ u32 erriar; /* 0xae4 */
+ u32 erridpr; /* 0xae8 */
+ u32 errippr; /* 0xaec */
+ u32 rerrar; /* 0xaf0 */
+ u32 rerrdr; /* 0xaf4 */
+ u32 rerrsynr; /* 0xaf8 */
+ u32 errsr; /* 0xafc */
};
struct flexcan_devtype_data {
@@ -214,6 +269,10 @@ struct flexcan_priv {
struct flexcan_platform_data *pdata;
const struct flexcan_devtype_data *devtype_data;
struct regulator *reg_xceiver;
+
+ /* Read and Write APIs */
+ u32 (*read) (void __iomem *addr);
+ void (*write) (u32 val, void __iomem *addr);
};
static struct flexcan_devtype_data fsl_p1010_devtype_data = {
@@ -224,6 +283,14 @@ static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.features = FLEXCAN_HAS_V10_FEATURES,
};
+static struct flexcan_devtype_data fsl_vf610_devtype_data = {
+ .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES,
+};
+
+static struct flexcan_devtype_data fsl_ls1021a_devtype_data = {
+ .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES,
+};
+
static const struct can_bittiming_const flexcan_bittiming_const = {
.name = DRV_NAME,
.tseg1_min = 4,
@@ -237,29 +304,38 @@ static const struct can_bittiming_const flexcan_bittiming_const = {
};
/*
- * Abstract off the read/write for arm versus ppc.
+ * FlexCAN module is essentially modelled as a little-endian IP in most
+ * SoCs, i.e the registers as well as the message buffer areas are
+ * implemented in a little-endian fashion.
+ *
+ * However there are some SoCs (e.g. LS1021A) which implement the FlexCAN
+ * module in a big-endian fashion (i.e the registers as well as the
+ * message buffer areas are implemented in a big-endian way).
+ *
+ * In addition, the FlexCAN module can be found on SoCs having ARM or
+ * PPC cores. So, we need to abstract off the register read/write
+ * functions, ensuring that these cater to all the combinations of module
+ * endianess and underlying CPU endianess.
*/
-#if defined(__BIG_ENDIAN)
-static inline u32 flexcan_read(void __iomem *addr)
+static inline u32 flexcan_read_le(void __iomem *addr)
{
- return in_be32(addr);
+ return ioread32(addr);
}
-static inline void flexcan_write(u32 val, void __iomem *addr)
+static inline void flexcan_write_le(u32 val, void __iomem *addr)
{
- out_be32(addr, val);
+ iowrite32(val, addr);
}
-#else
-static inline u32 flexcan_read(void __iomem *addr)
+
+static inline u32 flexcan_read_be(void __iomem *addr)
{
- return readl(addr);
+ return ioread32be(addr);
}
-static inline void flexcan_write(u32 val, void __iomem *addr)
+static inline void flexcan_write_be(u32 val, void __iomem *addr)
{
- writel(val, addr);
+ iowrite32be(val, addr);
}
-#endif
static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
{
@@ -290,14 +366,14 @@ static int flexcan_chip_enable(struct flexcan_priv *priv)
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
- reg = flexcan_read(&regs->mcr);
+ reg = priv->read(&regs->mcr);
reg &= ~FLEXCAN_MCR_MDIS;
- flexcan_write(reg, &regs->mcr);
+ priv->write(reg, &regs->mcr);
- while (timeout-- && (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
- usleep_range(10, 20);
+ while (timeout-- && (priv->read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
+ udelay(10);
- if (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK)
+ if (priv->read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK)
return -ETIMEDOUT;
return 0;
@@ -309,25 +385,80 @@ static int flexcan_chip_disable(struct flexcan_priv *priv)
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
- reg = flexcan_read(&regs->mcr);
+ reg = priv->read(&regs->mcr);
reg |= FLEXCAN_MCR_MDIS;
- flexcan_write(reg, &regs->mcr);
+ priv->write(reg, &regs->mcr);
- while (timeout-- && !(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
- usleep_range(10, 20);
+ while (timeout-- && !(priv->read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
+ udelay(10);
- if (!(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
+ if (!(priv->read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
return -ETIMEDOUT;
return 0;
}
-static int flexcan_get_berr_counter(const struct net_device *dev,
- struct can_berr_counter *bec)
+static int flexcan_chip_freeze(struct flexcan_priv *priv)
+{
+ struct flexcan_regs __iomem *regs = priv->base;
+ unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate;
+ u32 reg;
+
+ reg = priv->read(&regs->mcr);
+ reg |= FLEXCAN_MCR_HALT;
+ priv->write(reg, &regs->mcr);
+
+ while (timeout-- && !(priv->read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK))
+ udelay(100);
+
+ if (!(priv->read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
+{
+ struct flexcan_regs __iomem *regs = priv->base;
+ unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
+ u32 reg;
+
+ reg = priv->read(&regs->mcr);
+ reg &= ~FLEXCAN_MCR_HALT;
+ reg &= ~FLEXCAN_MCR_FRZ;
+ priv->write(reg, &regs->mcr);
+
+ while (timeout-- && (priv->read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK))
+ udelay(10);
+
+ if (priv->read(&regs->mcr) & FLEXCAN_MCR_FRZ_ACK)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int flexcan_chip_softreset(struct flexcan_priv *priv)
+{
+ struct flexcan_regs __iomem *regs = priv->base;
+ unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
+
+ priv->write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
+ while (timeout-- && (priv->read(&regs->mcr) & FLEXCAN_MCR_SOFTRST))
+ udelay(10);
+
+ if (priv->read(&regs->mcr) & FLEXCAN_MCR_SOFTRST)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+
+static int __flexcan_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
- u32 reg = flexcan_read(&regs->ecr);
+ u32 reg = priv->read(&regs->ecr);
bec->txerr = (reg >> 0) & 0xff;
bec->rxerr = (reg >> 8) & 0xff;
@@ -335,6 +466,29 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
return 0;
}
+static int flexcan_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ const struct flexcan_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = clk_prepare_enable(priv->clk_ipg);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(priv->clk_per);
+ if (err)
+ goto out_disable_ipg;
+
+ err = __flexcan_get_berr_counter(dev, bec);
+
+ clk_disable_unprepare(priv->clk_per);
+ out_disable_ipg:
+ clk_disable_unprepare(priv->clk_ipg);
+
+ return err;
+}
+
static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
const struct flexcan_priv *priv = netdev_priv(dev);
@@ -360,17 +514,30 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (cf->can_dlc > 0) {
u32 data = be32_to_cpup((__be32 *)&cf->data[0]);
- flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
+ priv->write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
}
if (cf->can_dlc > 3) {
u32 data = be32_to_cpup((__be32 *)&cf->data[4]);
- flexcan_write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
+ priv->write(data, &regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
}
can_put_echo_skb(skb, dev, 0);
- flexcan_write(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
- flexcan_write(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+ priv->write(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
+ priv->write(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+#ifdef CONFIG_CAN_FLEXCAN_ERRATA_ERR005829
+ /* Errata ERR005829 step8:
+ * Write twice INACTIVE(0x8) code to first MB.
+ */
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &regs->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &regs->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
+#endif
+
+#ifndef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
+ netdev_info(dev, "XMIT done\n");
+#endif
return NETDEV_TX_OK;
}
@@ -447,7 +614,7 @@ static void do_state(struct net_device *dev,
struct flexcan_priv *priv = netdev_priv(dev);
struct can_berr_counter bec;
- flexcan_get_berr_counter(dev, &bec);
+ __flexcan_get_berr_counter(dev, &bec);
switch (priv->can.state) {
case CAN_STATE_ERROR_ACTIVE:
@@ -493,6 +660,13 @@ static void do_state(struct net_device *dev,
/* process state changes depending on the new state */
switch (new_state) {
+ case CAN_STATE_ERROR_WARNING:
+ netdev_dbg(dev, "Error Warning\n");
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (bec.txerr > bec.rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ break;
case CAN_STATE_ERROR_ACTIVE:
netdev_dbg(dev, "Error Active\n");
cf->can_id |= CAN_ERR_PROT;
@@ -545,6 +719,7 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
return 1;
}
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
static void flexcan_read_fifo(const struct net_device *dev,
struct can_frame *cf)
{
@@ -553,8 +728,35 @@ static void flexcan_read_fifo(const struct net_device *dev,
struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
u32 reg_ctrl, reg_id;
- reg_ctrl = flexcan_read(&mb->can_ctrl);
- reg_id = flexcan_read(&mb->can_id);
+ reg_ctrl = priv->read(&mb->can_ctrl);
+ reg_id = priv->read(&mb->can_id);
+ if (reg_ctrl & FLEXCAN_MB_CNT_IDE)
+ cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ cf->can_id = (reg_id >> 18) & CAN_SFF_MASK;
+
+ if (reg_ctrl & FLEXCAN_MB_CNT_RTR)
+ cf->can_id |= CAN_RTR_FLAG;
+ cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);
+
+ *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0]));
+ *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1]));
+
+ /* mark as read */
+ priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
+ priv->read(&regs->timer);
+}
+#else
+static void flexcan_read_mailbox(const struct net_device *dev,
+ struct can_frame *cf, u32 mailbox)
+{
+ const struct flexcan_priv *priv = netdev_priv(dev);
+ struct flexcan_regs __iomem *regs = priv->base;
+ struct flexcan_mb __iomem *mb = &regs->cantxfg[mailbox];
+ u32 reg_ctrl, reg_id;
+
+ reg_ctrl = priv->read(&mb->can_ctrl);
+ reg_id = priv->read(&mb->can_id);
if (reg_ctrl & FLEXCAN_MB_CNT_IDE)
cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
@@ -564,15 +766,17 @@ static void flexcan_read_fifo(const struct net_device *dev,
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);
- *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0]));
- *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1]));
+ *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0]));
+ *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1]));
/* mark as read */
- flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
- flexcan_read(&regs->timer);
+ priv->write(BIT(mailbox), &regs->iflag1);
+ priv->read(&regs->timer);
}
+#endif
-static int flexcan_read_frame(struct net_device *dev)
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
+static int flexcan_read_frame_fifo_mode(struct net_device *dev)
{
struct net_device_stats *stats = &dev->stats;
struct can_frame *cf;
@@ -585,6 +789,31 @@ static int flexcan_read_frame(struct net_device *dev)
}
flexcan_read_fifo(dev, cf);
+
+ netif_receive_skb(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ can_led_event(dev, CAN_LED_EVENT_RX);
+
+ return 1;
+}
+#else
+static int flexcan_read_frame_legacy_mode(struct net_device *dev, u32 mailbox)
+{
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+
+ skb = alloc_can_skb(dev, &cf);
+ if (unlikely(!skb)) {
+ stats->rx_dropped++;
+ return 0;
+ }
+
+ flexcan_read_mailbox(dev, cf, mailbox);
+
netif_receive_skb(skb);
stats->rx_packets++;
@@ -594,6 +823,7 @@ static int flexcan_read_frame(struct net_device *dev)
return 1;
}
+#endif
static int flexcan_poll(struct napi_struct *napi, int quota)
{
@@ -607,18 +837,33 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
* The error bits are cleared on read,
* use saved value from irq handler.
*/
- reg_esr = flexcan_read(&regs->esr) | priv->reg_esr;
+ reg_esr = priv->read(&regs->esr) | priv->reg_esr;
/* handle state changes */
work_done += flexcan_poll_state(dev, reg_esr);
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
/* handle RX-FIFO */
- reg_iflag1 = flexcan_read(&regs->iflag1);
+ reg_iflag1 = priv->read(&regs->iflag1);
while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE &&
work_done < quota) {
- work_done += flexcan_read_frame(dev);
- reg_iflag1 = flexcan_read(&regs->iflag1);
+ work_done += flexcan_read_frame_fifo_mode(dev);
+ reg_iflag1 = priv->read(&regs->iflag1);
}
+#else
+ unsigned long iflag1;
+ u32 mailbox;
+
+ /* handle RX-Buffers */
+ reg_iflag1 = priv->read(&regs->iflag1);
+ iflag1 = reg_iflag1 & (FLEXCAN_TX_BUF_ID - 1);
+ while ((reg_iflag1 & (FLEXCAN_TX_BUF_ID - 1)) &&
+ work_done < quota) {
+ mailbox = find_first_bit(&iflag1, (FLEXCAN_TX_BUF_ID - 1));
+ work_done += flexcan_read_frame_legacy_mode(dev, mailbox);
+ reg_iflag1 = priv->read(&regs->iflag1);
+ }
+#endif
/* report bus errors */
if (flexcan_has_and_handle_berr(priv, reg_esr) && work_done < quota)
@@ -627,8 +872,8 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
if (work_done < quota) {
napi_complete(napi);
/* enable IRQs */
- flexcan_write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
- flexcan_write(priv->reg_ctrl_default, &regs->ctrl);
+ priv->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+ priv->write(priv->reg_ctrl_default, &regs->ctrl);
}
return work_done;
@@ -642,11 +887,11 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg_iflag1, reg_esr;
- reg_iflag1 = flexcan_read(&regs->iflag1);
- reg_esr = flexcan_read(&regs->esr);
+ reg_iflag1 = priv->read(&regs->iflag1);
+ reg_esr = priv->read(&regs->esr);
/* ACK all bus error and state change IRQ sources */
if (reg_esr & FLEXCAN_ESR_ALL_INT)
- flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, &regs->esr);
+ priv->write(reg_esr & FLEXCAN_ESR_ALL_INT, &regs->esr);
/*
* schedule NAPI in case of:
@@ -654,6 +899,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
* - state change IRQ
* - bus error IRQ and bus error reporting is activated
*/
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
(reg_esr & FLEXCAN_ESR_ERR_STATE) ||
flexcan_has_and_handle_berr(priv, reg_esr)) {
@@ -662,26 +908,45 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
* save them for later use.
*/
priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
- flexcan_write(FLEXCAN_IFLAG_DEFAULT &
+ priv->write(FLEXCAN_IFLAG_DEFAULT &
~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->imask1);
- flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
+ priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
&regs->ctrl);
napi_schedule(&priv->napi);
}
/* FIFO overflow */
if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
- flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1);
+ priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1);
dev->stats.rx_over_errors++;
dev->stats.rx_errors++;
}
+#else
+ if ((reg_iflag1 & (FLEXCAN_TX_BUF_ID - 1)) ||
+ (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
+ flexcan_has_and_handle_berr(priv, reg_esr)) {
+ /*
+ * The error bits are cleared on read,
+ * save them for later use.
+ */
+ priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
+ priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
+ &regs->ctrl);
+ napi_schedule(&priv->napi);
+ }
+#endif
/* transmission complete interrupt */
if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) {
stats->tx_bytes += can_get_echo_skb(dev, 0);
stats->tx_packets++;
can_led_event(dev, CAN_LED_EVENT_TX);
- flexcan_write((1 << FLEXCAN_TX_BUF_ID), &regs->iflag1);
+#ifndef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
+ /* after sending a RTR frame mailbox is in RX mode */
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+#endif
+ priv->write((1 << FLEXCAN_TX_BUF_ID), &regs->iflag1);
netif_wake_queue(dev);
}
@@ -695,7 +960,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
struct flexcan_regs __iomem *regs = priv->base;
u32 reg;
- reg = flexcan_read(&regs->ctrl);
+ reg = priv->read(&regs->ctrl);
reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) |
FLEXCAN_CTRL_RJW(0x3) |
FLEXCAN_CTRL_PSEG1(0x7) |
@@ -719,11 +984,11 @@ static void flexcan_set_bittiming(struct net_device *dev)
reg |= FLEXCAN_CTRL_SMP;
netdev_info(dev, "writing ctrl=0x%08x\n", reg);
- flexcan_write(reg, &regs->ctrl);
+ priv->write(reg, &regs->ctrl);
/* print chip status */
netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__,
- flexcan_read(&regs->mcr), flexcan_read(&regs->ctrl));
+ priv->read(&regs->mcr), priv->read(&regs->ctrl));
}
/*
@@ -737,7 +1002,8 @@ static int flexcan_chip_start(struct net_device *dev)
struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
int err;
- u32 reg_mcr, reg_ctrl;
+ u32 reg_mcr, reg_ctrl, reg_crl2, reg_mecr;
+ int i;
/* enable module */
err = flexcan_chip_enable(priv);
@@ -745,16 +1011,9 @@ static int flexcan_chip_start(struct net_device *dev)
return err;
/* soft reset */
- flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
- udelay(10);
-
- reg_mcr = flexcan_read(&regs->mcr);
- if (reg_mcr & FLEXCAN_MCR_SOFTRST) {
- netdev_err(dev, "Failed to softreset can module (mcr=0x%08x)\n",
- reg_mcr);
- err = -ENODEV;
- goto out;
- }
+ err = flexcan_chip_softreset(priv);
+ if (err)
+ goto out_chip_disable;
flexcan_set_bittiming(dev);
@@ -770,14 +1029,22 @@ static int flexcan_chip_start(struct net_device *dev)
* disable local echo
*
*/
- reg_mcr = flexcan_read(&regs->mcr);
+ reg_mcr = priv->read(&regs->mcr);
reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
+#else
+ reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
+ FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
+ FLEXCAN_MCR_IDAM_A |
+ FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
+ reg_mcr &= FLEXCAN_MCR_SRX_EN;
+#endif
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
- flexcan_write(reg_mcr, &regs->mcr);
+ priv->write(reg_mcr, &regs->mcr);
/*
* CTRL
@@ -791,7 +1058,7 @@ static int flexcan_chip_start(struct net_device *dev)
* enable bus off interrupt
* (== FLEXCAN_CTRL_ERR_STATE)
*/
- reg_ctrl = flexcan_read(&regs->ctrl);
+ reg_ctrl = priv->read(&regs->ctrl);
reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
FLEXCAN_CTRL_ERR_STATE;
@@ -803,45 +1070,105 @@ static int flexcan_chip_start(struct net_device *dev)
if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE ||
priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
reg_ctrl |= FLEXCAN_CTRL_ERR_MSK;
+ else
+ reg_ctrl &= ~FLEXCAN_CTRL_ERR_MSK;
/* save for later use */
priv->reg_ctrl_default = reg_ctrl;
netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
- flexcan_write(reg_ctrl, &regs->ctrl);
+ priv->write(reg_ctrl, &regs->ctrl);
+
+ /* clear and invalidate all mailboxes first */
+#ifndef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
+ for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) {
+ priv->write(0, &regs->cantxfg[i].can_ctrl);
+ priv->write(0, &regs->cantxfg[i].can_id);
+ priv->write(0, &regs->cantxfg[i].data[0]);
+ priv->write(0, &regs->cantxfg[i].data[1]);
+
+ /* put MB into rx queue */
+ priv->write(FLEXCAN_MB_CNT_CODE(0x4),
+ &regs->cantxfg[i].can_ctrl);
+ }
+#else
+ for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->cantxfg); i++) {
+ priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
+ &regs->cantxfg[i].can_ctrl);
+ }
+#endif
- /* Abort any pending TX, mark Mailbox as INACTIVE */
- flexcan_write(FLEXCAN_MB_CNT_CODE(0x4),
+#ifdef CONFIG_CAN_FLEXCAN_ERRATA_ERR005829
+ /* Errata ERR005829: mark first TX mailbox as INACTIVE */
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &regs->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl);
+#endif
+
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
+ /* mark TX mailbox as INACTIVE */
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
&regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);
+#endif
/* acceptance mask/acceptance code (accept everything) */
- flexcan_write(0x0, &regs->rxgmask);
- flexcan_write(0x0, &regs->rx14mask);
- flexcan_write(0x0, &regs->rx15mask);
+ priv->write(0x0, &regs->rxgmask);
+ priv->write(0x0, &regs->rx14mask);
+ priv->write(0x0, &regs->rx15mask);
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
- flexcan_write(0x0, &regs->rxfgmask);
+ priv->write(0x0, &regs->rxfgmask);
+#else
+ priv->write(0x0, &regs->rxfgmask);
+#endif
+
+ /*
+ * On Vybrid, disable memory error detection interrupts
+ * and freeze mode.
+ * This also works around errata e5295 which generates
+ * false positive memory errors and put the device in
+ * freeze mode.
+ */
+ if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) {
+ /*
+ * Follow the protocol as described in "Detection
+ * and Correction of Memory Errors" to write to
+ * MECR register
+ */
+ reg_crl2 = priv->read(&regs->crl2);
+ reg_crl2 |= FLEXCAN_CRL2_ECRWRE;
+ priv->write(reg_crl2, &regs->crl2);
+
+ reg_mecr = priv->read(&regs->mecr);
+ reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
+ priv->write(reg_mecr, &regs->mecr);
+ reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
+ FLEXCAN_MECR_FANCEI_MSK);
+ priv->write(reg_mecr, &regs->mecr);
+ }
err = flexcan_transceiver_enable(priv);
if (err)
- goto out;
+ goto out_chip_disable;
/* synchronize with the can bus */
- reg_mcr = flexcan_read(&regs->mcr);
- reg_mcr &= ~FLEXCAN_MCR_HALT;
- flexcan_write(reg_mcr, &regs->mcr);
+ err = flexcan_chip_unfreeze(priv);
+ if (err)
+ goto out_transceiver_disable;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
/* enable FIFO interrupts */
- flexcan_write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+ priv->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
/* print chip status */
netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
- flexcan_read(&regs->mcr), flexcan_read(&regs->ctrl));
+ priv->read(&regs->mcr), priv->read(&regs->ctrl));
return 0;
- out:
+ out_transceiver_disable:
+ flexcan_transceiver_disable(priv);
+ out_chip_disable:
flexcan_chip_disable(priv);
return err;
}
@@ -856,16 +1183,14 @@ static void flexcan_chip_stop(struct net_device *dev)
{
struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->base;
- u32 reg;
- /* Disable + halt module */
- reg = flexcan_read(&regs->mcr);
- reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT;
- flexcan_write(reg, &regs->mcr);
+ /* freeze + disable module */
+ flexcan_chip_freeze(priv);
+ flexcan_chip_disable(priv);
/* Disable all interrupts */
- flexcan_write(0, &regs->imask1);
- flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
+ priv->write(0, &regs->imask1);
+ priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
&regs->ctrl);
flexcan_transceiver_disable(priv);
@@ -982,31 +1307,40 @@ static int register_flexcandev(struct net_device *dev)
err = flexcan_chip_disable(priv);
if (err)
goto out_disable_per;
- reg = flexcan_read(&regs->ctrl);
+ reg = priv->read(&regs->ctrl);
reg |= FLEXCAN_CTRL_CLK_SRC;
- flexcan_write(reg, &regs->ctrl);
+ priv->write(reg, &regs->ctrl);
err = flexcan_chip_enable(priv);
if (err)
goto out_chip_disable;
/* set freeze, halt and activate FIFO, restrict register access */
- reg = flexcan_read(&regs->mcr);
+ reg = priv->read(&regs->mcr);
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
- flexcan_write(reg, &regs->mcr);
+#else
+ reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV;
+#endif
+ priv->write(reg, &regs->mcr);
/*
* Currently we only support newer versions of this core
* featuring a RX FIFO. Older cores found on some Coldfire
* derivates are not yet supported.
*/
- reg = flexcan_read(&regs->mcr);
+ reg = priv->read(&regs->mcr);
+#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN
if (!(reg & FLEXCAN_MCR_FEN)) {
netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
err = -ENODEV;
goto out_chip_disable;
}
+#else
+ if (!(reg & FLEXCAN_MCR_FEN))
+ netdev_info(dev, "Legacy mode (non-RX FIFO) supported\n");
+#endif
err = register_candev(dev);
@@ -1029,7 +1363,10 @@ static void unregister_flexcandev(struct net_device *dev)
static const struct of_device_id flexcan_of_match[] = {
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
+ { .compatible = "fsl,ls1021a-flexcan",
+ .data = &fsl_ls1021a_devtype_data, },
{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
+ { .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, flexcan_of_match);
@@ -1051,6 +1388,8 @@ static int flexcan_probe(struct platform_device *pdev)
void __iomem *base;
int err, irq;
u32 clock_freq = 0;
+ /* Default case for most ARM based FSL SoC having BE FlexCAN IP */
+ bool core_is_little = true, module_is_little = false;
if (pdev->dev.of_node)
of_property_read_u32(pdev->dev.of_node,
@@ -1083,9 +1422,9 @@ static int flexcan_probe(struct platform_device *pdev)
of_id = of_match_device(flexcan_of_match, &pdev->dev);
if (of_id) {
devtype_data = of_id->data;
- } else if (pdev->id_entry->driver_data) {
+ } else if (platform_get_device_id(pdev)->driver_data) {
devtype_data = (struct flexcan_devtype_data *)
- pdev->id_entry->driver_data;
+ platform_get_device_id(pdev)->driver_data;
} else {
return -ENODEV;
}
@@ -1099,6 +1438,25 @@ static int flexcan_probe(struct platform_device *pdev)
dev->flags |= IFF_ECHO;
priv = netdev_priv(dev);
+
+ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ core_is_little = false;
+
+ if (of_property_read_bool(dev->dev.of_node, "little-endian"))
+ module_is_little = true;
+
+ if ((core_is_little && module_is_little) ||
+ (!core_is_little && !module_is_little)) {
+ priv->read = flexcan_read_le;
+ priv->write = flexcan_write_le;
+ }
+
+ if ((!core_is_little && module_is_little) ||
+ (core_is_little && !module_is_little)) {
+ priv->read = flexcan_read_be;
+ priv->write = flexcan_write_be;
+ }
+
priv->can.clock.freq = clock_freq;
priv->can.bittiming_const = &flexcan_bittiming_const;
priv->can.do_set_mode = flexcan_set_mode;
@@ -1152,8 +1510,7 @@ static int flexcan_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int flexcan_suspend(struct device *device)
+static int __maybe_unused flexcan_suspend(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
@@ -1172,7 +1529,7 @@ static int flexcan_suspend(struct device *device)
return 0;
}
-static int flexcan_resume(struct device *device)
+static int __maybe_unused flexcan_resume(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
@@ -1184,7 +1541,6 @@ static int flexcan_resume(struct device *device)
}
return flexcan_chip_enable(priv);
}
-#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
index bc51248..3e26476 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -88,7 +88,7 @@ config UGETH_TX_ON_DEMAND
config GIANFAR
tristate "Gianfar Ethernet"
depends on FSL_SOC
- select FSL_85XX_CACHE_SRAM
+ select FSL_85XX_CACHE_SRAM if PPC
select FSL_PQ_MDIO
select PHYLIB
select CRC32
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 <linux/of_irq.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
-#include <asm/qe.h> /* For struct qe_firmware */
+#include <linux/fsl/qe.h> /* For struct qe_firmware */
#include <sysdev/fsl_soc.h>
#include <asm/fsl_pm.h>
#include <linux/stat.h> /* 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..d31302a 100644
--- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
@@ -29,7 +29,7 @@
#include <linux/of_device.h>
#include <asm/io.h>
-#include <asm/ucc.h> /* for ucc_set_qe_mux_mii_mng() */
+#include <linux/fsl/ucc.h> /* for ucc_set_qe_mux_mii_mng() */
#include "gianfar.h"
@@ -103,19 +103,22 @@ static int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
{
struct fsl_pq_mdio_priv *priv = bus->priv;
struct fsl_pq_mii __iomem *regs = priv->regs;
- u32 status;
+ unsigned int timeout;
/* Set the PHY address and the register address we want to write */
- out_be32(&regs->miimadd, (mii_id << 8) | regnum);
+ iowrite32be((mii_id << 8) | regnum, &regs->miimadd);
/* Write out the value we want */
- out_be32(&regs->miimcon, value);
+ iowrite32be(value, &regs->miimcon);
/* Wait for the transaction to finish */
- status = spin_event_timeout(!(in_be32(&regs->miimind) & MIIMIND_BUSY),
- MII_TIMEOUT, 0);
+ timeout = MII_TIMEOUT;
+ while ((ioread32be(&regs->miimind) & MIIMIND_BUSY) && timeout) {
+ cpu_relax();
+ timeout--;
+ }
- return status ? 0 : -ETIMEDOUT;
+ return timeout ? 0 : -ETIMEDOUT;
}
/*
@@ -132,25 +135,29 @@ static int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
struct fsl_pq_mdio_priv *priv = bus->priv;
struct fsl_pq_mii __iomem *regs = priv->regs;
- u32 status;
+ unsigned int timeout;
u16 value;
/* Set the PHY address and the register address we want to read */
- out_be32(&regs->miimadd, (mii_id << 8) | regnum);
+ iowrite32be((mii_id << 8) | regnum, &regs->miimadd);
/* Clear miimcom, and then initiate a read */
- out_be32(&regs->miimcom, 0);
- out_be32(&regs->miimcom, MII_READ_COMMAND);
+ iowrite32be(0, &regs->miimcom);
+ iowrite32be(MII_READ_COMMAND, &regs->miimcom);
/* Wait for the transaction to finish, normally less than 100us */
- status = spin_event_timeout(!(in_be32(&regs->miimind) &
- (MIIMIND_NOTVALID | MIIMIND_BUSY)),
- MII_TIMEOUT, 0);
- if (!status)
+ timeout = MII_TIMEOUT;
+ while ((ioread32be(&regs->miimind) &
+ (MIIMIND_NOTVALID | MIIMIND_BUSY)) && timeout) {
+ cpu_relax();
+ timeout--;
+ }
+
+ if (!timeout)
return -ETIMEDOUT;
/* Grab the value of the register from miimstat */
- value = in_be32(&regs->miimstat);
+ value = ioread32be(&regs->miimstat);
dev_dbg(&bus->dev, "read %04x from address %x/%x\n", value, mii_id, regnum);
return value;
@@ -161,23 +168,26 @@ static int fsl_pq_mdio_reset(struct mii_bus *bus)
{
struct fsl_pq_mdio_priv *priv = bus->priv;
struct fsl_pq_mii __iomem *regs = priv->regs;
- u32 status;
+ unsigned int timeout;
mutex_lock(&bus->mdio_lock);
/* Reset the management interface */
- out_be32(&regs->miimcfg, MIIMCFG_RESET);
+ iowrite32be(MIIMCFG_RESET, &regs->miimcfg);
/* Setup the MII Mgmt clock speed */
- out_be32(&regs->miimcfg, MIIMCFG_INIT_VALUE);
+ iowrite32be(MIIMCFG_INIT_VALUE, &regs->miimcfg);
/* Wait until the bus is free */
- status = spin_event_timeout(!(in_be32(&regs->miimind) & MIIMIND_BUSY),
- MII_TIMEOUT, 0);
+ timeout = MII_TIMEOUT;
+ while ((ioread32be(&regs->miimind) & MIIMIND_BUSY) && timeout) {
+ cpu_relax();
+ timeout--;
+ }
mutex_unlock(&bus->mdio_lock);
- if (!status) {
+ if (!timeout) {
dev_err(&bus->dev, "timeout waiting for MII bus\n");
return -EBUSY;
}
@@ -434,7 +444,7 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
tbipa = data->get_tbipa(priv->map);
- out_be32(tbipa, be32_to_cpup(prop));
+ iowrite32be(be32_to_cpup(prop), tbipa);
}
}
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 5584c39..2f67fe8 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -90,8 +90,10 @@
#endif
#include <asm/io.h>
+#ifdef CONFIG_PPC
#include <asm/reg.h>
#include <asm/mpc85xx.h>
+#endif
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <linux/module.h>
@@ -102,6 +104,8 @@
#include <linux/phy_fixed.h>
#include <linux/of.h>
#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include "gianfar.h"
@@ -161,10 +165,12 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num,
const u8 *addr);
static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#ifdef CONFIG_FSL_85XX_CACHE_SRAM
bool gfar_l2sram_en = true;
module_param(gfar_l2sram_en, bool, 0444);
MODULE_PARM_DESC(gfar_l2sram_en,
"Enable allocation to L2 SRAM.");
+#endif
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
@@ -202,7 +208,8 @@ static int gfar_init_bds(struct net_device *ndev)
/* Set the last descriptor in the ring to indicate wrap */
txbdp--;
- txbdp->status |= TXBD_WRAP;
+ txbdp->status = cpu_to_be16(be16_to_cpu(txbdp->status) |
+ TXBD_WRAP);
}
for (i = 0; i < priv->num_rx_queues; i++) {
@@ -216,7 +223,7 @@ static int gfar_init_bds(struct net_device *ndev)
if (skb) {
gfar_init_rxbdp(rx_queue, rxbdp,
- rxbdp->bufPtr);
+ be32_to_cpu(rxbdp->bufPtr));
} else {
skb = gfar_alloc_skb(ndev);
if (!skb) {
@@ -240,7 +247,6 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
{
void *vaddr = NULL;
dma_addr_t addr;
- phys_addr_t paddr;
int i, j, k;
struct gfar_private *priv = netdev_priv(ndev);
struct device *dev = priv->dev;
@@ -256,7 +262,9 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size;
/* Allocate memory for the buffer descriptors */
+#ifdef CONFIG_FSL_85XX_CACHE_SRAM
if (priv->bd_l2sram_en) {
+ phys_addr_t paddr;
vaddr = mpc85xx_cache_sram_alloc(BD_RING_REG_SZ(priv),
&paddr, L1_CACHE_BYTES);
if (vaddr)
@@ -268,6 +276,7 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
priv->bd_l2sram_en = 0;
}
}
+#endif
if (!priv->bd_l2sram_en)
vaddr = dma_alloc_coherent(dev, BD_RING_REG_SZ(priv),
@@ -697,26 +706,27 @@ static int gfar_parse_group(struct device_node *np,
grp->priv = priv;
spin_lock_init(&grp->grplock);
if (priv->mode == MQ_MG_MODE) {
- u32 *rxq_mask, *txq_mask;
- rxq_mask = (u32 *)of_get_property(np, "fsl,rx-bit-map", NULL);
- txq_mask = (u32 *)of_get_property(np, "fsl,tx-bit-map", NULL);
+ u32 rxq_mask, txq_mask;
+ int ret;
+ grp->rx_bit_map = (DEFAULT_MAPPING >> priv->num_grps);
+ grp->tx_bit_map = (DEFAULT_MAPPING >> priv->num_grps);
+
+ ret = of_property_read_u32(np, "fsl,rx-bit-map", &rxq_mask);
+ if (!ret)
+ grp->rx_bit_map = rxq_mask;
+
+ ret = of_property_read_u32(np, "fsl,tx-bit-map", &txq_mask);
+ if (!ret)
+ grp->tx_bit_map = txq_mask;
+
+#ifndef CONFIG_AS_FASTPATH
if (priv->poll_mode == GFAR_SQ_POLLING) {
/* One Q per interrupt group: Q0 to G0, Q1 to G1 */
grp->rx_bit_map = (DEFAULT_MAPPING >> priv->num_grps);
grp->tx_bit_map = (DEFAULT_MAPPING >> priv->num_grps);
- #ifdef CONFIG_AS_FASTPATH
- grp->rx_bit_map = rxq_mask ?
- *rxq_mask : (DEFAULT_MAPPING >> priv->num_grps);
- grp->tx_bit_map = txq_mask ?
- *txq_mask : (DEFAULT_MAPPING >> priv->num_grps);
- #endif
- } else { /* GFAR_MQ_POLLING */
- grp->rx_bit_map = rxq_mask ?
- *rxq_mask : (DEFAULT_MAPPING >> priv->num_grps);
- grp->tx_bit_map = txq_mask ?
- *txq_mask : (DEFAULT_MAPPING >> priv->num_grps);
}
+#endif
} else {
grp->rx_bit_map = 0xFF;
grp->tx_bit_map = 0xFF;
@@ -764,11 +774,10 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
struct gfar_private *priv = NULL;
struct device_node *np = ofdev->dev.of_node;
struct device_node *child = NULL;
- const u32 *stash;
- const u32 *stash_len;
- const u32 *stash_idx;
+ struct property *stash;
+ u32 stash_len = 0;
+ u32 stash_idx = 0;
unsigned int num_tx_qs, num_rx_qs;
- u32 *tx_queues, *rx_queues;
unsigned short mode, poll_mode;
if (!np || !of_device_is_available(np))
@@ -782,10 +791,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
poll_mode = GFAR_SQ_POLLING;
}
- /* parse the num of HW tx and rx queues */
- tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL);
- rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL);
-
if (mode == SQ_SG_MODE) {
num_tx_qs = 1;
num_rx_qs = 1;
@@ -804,8 +809,17 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
num_tx_qs = num_grps; /* one txq per int group */
num_rx_qs = num_grps; /* one rxq per int group */
} else { /* GFAR_MQ_POLLING */
- num_tx_qs = tx_queues ? *tx_queues : 1;
- num_rx_qs = rx_queues ? *rx_queues : 1;
+ u32 tx_queues, rx_queues;
+ int ret;
+
+ /* parse the num of HW tx and rx queues */
+ ret = of_property_read_u32(np, "fsl,num_tx_queues",
+ &tx_queues);
+ num_tx_qs = ret ? 1 : tx_queues;
+
+ ret = of_property_read_u32(np, "fsl,num_rx_queues",
+ &rx_queues);
+ num_rx_qs = ret ? 1 : rx_queues;
}
}
@@ -856,7 +870,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
priv->rx_list.count = 0;
mutex_init(&priv->rx_queue_access);
- model = of_get_property(np, "model", NULL);
+ err = of_property_read_string(np, "model", &model);
for (i = 0; i < MAXGROUPS; i++)
priv->gfargrp[i].regs = NULL;
@@ -874,27 +888,29 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
goto err_grp_init;
}
+#ifdef CONFIG_FSL_85XX_CACHE_SRAM
if (gfar_l2sram_en) {
/* try to alloc the BD rings to L2 SRAM */
priv->bd_l2sram_en = 1;
}
+#endif
- stash = of_get_property(np, "bd-stash", NULL);
+ stash = of_find_property(np, "bd-stash", NULL);
if (stash) {
priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING;
priv->bd_stash_en = 1;
}
- stash_len = of_get_property(np, "rx-stash-len", NULL);
+ err = of_property_read_u32(np, "rx-stash-len", &stash_len);
- if (stash_len)
- priv->rx_stash_size = *stash_len;
+ if (err == 0)
+ priv->rx_stash_size = stash_len;
- stash_idx = of_get_property(np, "rx-stash-idx", NULL);
+ err = of_property_read_u32(np, "rx-stash-idx", &stash_idx);
- if (stash_idx)
- priv->rx_stash_index = *stash_idx;
+ if (err == 0)
+ priv->rx_stash_index = stash_idx;
if (stash_len || stash_idx)
priv->device_flags |= FSL_GIANFAR_DEV_HAS_BUF_STASHING;
@@ -921,24 +937,27 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
FSL_GIANFAR_DEV_HAS_EXTENDED_HASH |
FSL_GIANFAR_DEV_HAS_TIMER;
- ctype = of_get_property(np, "phy-connection-type", NULL);
+ err = of_property_read_string(np, "phy-connection-type", &ctype);
/* We only care about rgmii-id. The rest are autodetected */
- if (ctype && !strcmp(ctype, "rgmii-id"))
+ if (err == 0 && !strcmp(ctype, "rgmii-id"))
priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
else
priv->interface = PHY_INTERFACE_MODE_MII;
- if (of_get_property(np, "fsl,magic-packet", NULL))
+ if (of_find_property(np, "fsl,magic-packet", NULL))
priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
- if (of_get_property(np, "fsl,wake-on-filer", NULL))
+ if (of_find_property(np, "fsl,wake-on-filer", NULL))
priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
/* Find the TBI PHY. If it's not there, we don't support SGMII */
priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
+
+ priv->dma_endian_le = of_property_read_bool(np, "fsl,dma-endian-le");
+
#if defined CONFIG_FSL_GIANFAR_1588
/* Handle IEEE1588 node */
if (!gfar_ptp_init(np, priv))
@@ -1095,6 +1114,7 @@ static void gfar_init_filer_table(struct gfar_private *priv)
}
}
+#ifdef CONFIG_PPC
static void __gfar_detect_errata_83xx(struct gfar_private *priv)
{
unsigned int pvr = mfspr(SPRN_PVR);
@@ -1127,6 +1147,7 @@ static void __gfar_detect_errata_85xx(struct gfar_private *priv)
((SVR_SOC_VER(svr) == SVR_P2010) && (SVR_REV(svr) < 0x20)))
priv->errata |= GFAR_ERRATA_76; /* aka eTSEC 20 */
}
+#endif
static void gfar_detect_errata(struct gfar_private *priv)
{
@@ -1135,10 +1156,12 @@ static void gfar_detect_errata(struct gfar_private *priv)
/* no plans to fix */
priv->errata |= GFAR_ERRATA_A002;
+#ifdef CONFIG_PPC
if (pvr_version_is(PVR_VER_E500V1) || pvr_version_is(PVR_VER_E500V2))
__gfar_detect_errata_85xx(priv);
else /* non-mpc85xx parts, i.e. e300 core based */
__gfar_detect_errata_83xx(priv);
+#endif
if (priv->errata)
dev_info(dev, "enabled errata workarounds, flags: 0x%x\n",
@@ -1276,7 +1299,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;
@@ -1988,26 +2011,32 @@ static void gfar_halt_nodisable(struct gfar_private *priv)
{
struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
+ unsigned int timeout;
+ int stopped;
gfar_ints_disable(priv);
+ if (gfar_is_dma_stopped(priv))
+ return;
+
/* Stop the DMA, and wait for it to stop */
tempval = gfar_read(&regs->dmactrl);
- if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) !=
- (DMACTRL_GRS | DMACTRL_GTS)) {
- int ret;
-
- tempval |= (DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&regs->dmactrl, tempval);
+ tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&regs->dmactrl, tempval);
- do {
- ret = spin_event_timeout(((gfar_read(&regs->ievent) &
- (IEVENT_GRSC | IEVENT_GTSC)) ==
- (IEVENT_GRSC | IEVENT_GTSC)), 1000000, 0);
- if (!ret && !(gfar_read(&regs->ievent) & IEVENT_GRSC))
- ret = __gfar_is_rx_idle(priv);
- } while (!ret);
+retry:
+ timeout = 1000;
+ while (!(stopped = gfar_is_dma_stopped(priv)) && timeout) {
+ cpu_relax();
+ timeout--;
}
+
+ if (!timeout)
+ stopped = gfar_is_dma_stopped(priv);
+
+ if (!stopped && !gfar_is_rx_dma_stopped(priv) &&
+ !__gfar_is_rx_idle(priv))
+ goto retry;
}
/* Halt the receive and transmit queues */
@@ -2065,14 +2094,15 @@ static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue)
if (!tx_queue->tx_skbuff[i])
continue;
- dma_unmap_single(priv->dev, txbdp->bufPtr,
- txbdp->length, DMA_TO_DEVICE);
+ dma_unmap_single(priv->dev, be32_to_cpu(txbdp->bufPtr),
+ be16_to_cpu(txbdp->length), DMA_TO_DEVICE);
txbdp->lstatus = 0;
for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags;
j++) {
txbdp++;
- dma_unmap_page(priv->dev, txbdp->bufPtr,
- txbdp->length, DMA_TO_DEVICE);
+ dma_unmap_page(priv->dev, be32_to_cpu(txbdp->bufPtr),
+ be16_to_cpu(txbdp->length),
+ DMA_TO_DEVICE);
}
txbdp++;
dev_kfree_skb_any(tx_queue->tx_skbuff[i]);
@@ -2092,7 +2122,7 @@ static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue)
for (i = 0; i < rx_queue->rx_ring_size; i++) {
if (rx_queue->rx_skbuff[i]) {
- dma_unmap_single(priv->dev, rxbdp->bufPtr,
+ dma_unmap_single(priv->dev, be32_to_cpu(rxbdp->bufPtr),
priv->rx_buffer_size,
DMA_FROM_DEVICE);
dev_kfree_skb_any(rx_queue->rx_skbuff[i]);
@@ -2131,9 +2161,11 @@ static void free_skb_resources(struct gfar_private *priv)
free_skb_rx_queue(rx_queue);
}
+#ifdef CONFIG_FSL_85XX_CACHE_SRAM
if (priv->bd_l2sram_en)
mpc85xx_cache_sram_free(priv->tx_queue[0]->tx_bd_base);
else
+#endif
dma_free_coherent(priv->dev, BD_RING_REG_SZ(priv),
priv->tx_queue[0]->tx_bd_base,
priv->tx_queue[0]->tx_bd_dma_base);
@@ -2152,6 +2184,8 @@ void gfar_start(struct gfar_private *priv)
/* Initialize DMACTRL to have WWR and WOP */
tempval = gfar_read(&regs->dmactrl);
tempval |= DMACTRL_INIT_SETTINGS;
+ if (priv->dma_endian_le)
+ tempval |= DMACTRL_LE;
gfar_write(&regs->dmactrl, tempval);
/* Make sure we aren't stopped */
@@ -2328,10 +2362,11 @@ static int gfar_enet_open(struct net_device *dev)
return err;
}
+
void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
{
fcb->flags |= TXFCB_VLN;
- fcb->vlctl = vlan_tx_tag_get(skb);
+ fcb->vlctl = cpu_to_be16(vlan_tx_tag_get(skb));
}
/* This is called by the kernel when a frame is ready for transmission.
@@ -2416,7 +2451,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_queue->stats.tx_packets++;
txbdp = txbdp_start = tx_queue->cur_tx;
- lstatus = txbdp->lstatus;
+ lstatus = be32_to_cpu(txbdp->lstatus);
/* Time stamp insertion requires one additional TxBD */
if (unlikely(do_tstamp))
@@ -2424,11 +2459,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_queue->tx_ring_size);
if (nr_frags == 0) {
- if (unlikely(do_tstamp))
- txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_LAST |
- TXBD_INTERRUPT);
- else
+ if (unlikely(do_tstamp)) {
+ u32 lstatus_ts = be32_to_cpu(txbdp_tstamp->lstatus);
+ lstatus_ts |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
+ txbdp_tstamp->lstatus = cpu_to_be32(lstatus_ts);
+ } else {
lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
+ }
} else {
/* Place the fragment addresses and lengths into the TxBDs */
for (i = 0; i < nr_frags; i++) {
@@ -2438,7 +2475,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
frag_len = skb_shinfo(skb)->frags[i].size;
- lstatus = txbdp->lstatus | frag_len |
+ lstatus = be32_to_cpu(txbdp->lstatus) | frag_len |
BD_LFLAG(TXBD_READY);
/* Handle the last BD specially */
@@ -2452,11 +2489,11 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
DMA_TO_DEVICE);
/* set the TxBD length and buffer pointer */
- txbdp->bufPtr = bufaddr;
- txbdp->lstatus = lstatus;
+ txbdp->bufPtr = cpu_to_be32(bufaddr);
+ txbdp->lstatus = cpu_to_be32(lstatus);
}
- lstatus = txbdp_start->lstatus;
+ lstatus = be32_to_cpu(txbdp_start->lstatus);
}
/* Add TxPAL between FCB and frame if required */
@@ -2507,13 +2544,14 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
*/
vlan_ctrl = gfar_read(&regs->dfvlan);
vlan_ctrl &= ~0xFFFF;
- vlan_ctrl |= (fcb->vlctl & 0xFFFF);
+ vlan_ctrl |= (be16_to_cpu(fcb->vlctl) & 0xFFFF);
gfar_write(&regs->dfvlan, vlan_ctrl);
#endif
}
- txbdp_start->bufPtr = dma_map_single(priv->dev, skb->data,
- skb_headlen(skb), DMA_TO_DEVICE);
+ bufaddr = dma_map_single(priv->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ txbdp_start->bufPtr = cpu_to_be32(bufaddr);
/* If time stamping is requested one additional TxBD must be set up. The
* first TxBD points to the FCB and must have a data length of
@@ -2521,26 +2559,25 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
* the full frame length.
*/
if (unlikely(do_tstamp)) {
- txbdp_tstamp->bufPtr = txbdp_start->bufPtr + fcb_len;
- txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_READY) |
- (skb_headlen(skb) - fcb_len);
+ u32 lstatus_ts = be32_to_cpu(txbdp_tstamp->lstatus);
+ bufaddr = be32_to_cpu(txbdp_start->bufPtr);
+
+ bufaddr += fcb_len;
+ lstatus_ts |= BD_LFLAG(TXBD_READY) |
+ (skb_headlen(skb) - fcb_len);
+
+ txbdp_tstamp->bufPtr = cpu_to_be32(bufaddr);
+ txbdp_tstamp->lstatus = cpu_to_be32(lstatus_ts);
lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | GMAC_FCB_LEN;
} else {
lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
}
- /* The powerpc-specific eieio() is used, as wmb() has too strong
- * semantics (it requires synchronization between cacheable and
- * uncacheable mappings, which eieio doesn't provide and which we
- * don't need), thus requiring a more expensive sync instruction. At
- * some point, the set of architecture-independent barrier functions
- * should be expanded to include weaker barriers.
- */
- eieio();
+ gfar_wmb();
- txbdp_start->lstatus = lstatus;
+ txbdp_start->lstatus = cpu_to_be32(lstatus);
- eieio(); /* force lstatus write before tx_skbuff */
+ gfar_wmb(); /* force lstatus write before tx_skbuff */
tx_queue->tx_skbuff[tx_queue->skb_curtx] = skb;
@@ -2721,7 +2758,7 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
lbdp = skip_txbd(bdp, nr_txbds - 1, base, tx_ring_size);
- lstatus = lbdp->lstatus;
+ lstatus = be32_to_cpu(lbdp->lstatus);
/* Only clean completed frames */
if ((lstatus & BD_LFLAG(TXBD_READY)) &&
@@ -2730,11 +2767,12 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) {
next = next_txbd(bdp, base, tx_ring_size);
- buflen = next->length + GMAC_FCB_LEN + GMAC_TXPAL_LEN;
+ buflen = be16_to_cpu(next->length) +
+ GMAC_FCB_LEN + GMAC_TXPAL_LEN;
} else
- buflen = bdp->length;
+ buflen = be16_to_cpu(bdp->length);
- dma_unmap_single(priv->dev, bdp->bufPtr,
+ dma_unmap_single(priv->dev, be32_to_cpu(bdp->bufPtr),
buflen, DMA_TO_DEVICE);
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) {
@@ -2780,17 +2818,18 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
skb_pull(skb, GMAC_FCB_LEN + GMAC_TXPAL_LEN);
skb_tstamp_tx(skb, &shhwtstamps);
#endif
- bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
+ gfar_clear_txbd_status(bdp);
bdp = next;
}
- bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
+ gfar_clear_txbd_status(bdp);
bdp = next_txbd(bdp, base, tx_ring_size);
for (i = 0; i < frags; i++) {
- dma_unmap_page(priv->dev, bdp->bufPtr,
- bdp->length, DMA_TO_DEVICE);
- bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
+ dma_unmap_page(priv->dev, be32_to_cpu(bdp->bufPtr),
+ be16_to_cpu(bdp->length),
+ DMA_TO_DEVICE);
+ gfar_clear_txbd_status(bdp);
bdp = next_txbd(bdp, base, tx_ring_size);
}
@@ -2979,8 +3018,9 @@ static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
* RXFCB_VLN is pseudo randomly set.
*/
if (dev->features & NETIF_F_HW_VLAN_CTAG_RX &&
- fcb->flags & RXFCB_VLN)
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), fcb->vlctl);
+ be16_to_cpu(fcb->flags) & RXFCB_VLN)
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ be16_to_cpu(fcb->vlctl));
/* Send the packet up the stack */
napi_gro_receive(napi, skb);
@@ -3011,7 +3051,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
amount_pull = priv->uses_rxfcb ? GMAC_FCB_LEN : 0;
- while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
+ while (!(be16_to_cpu(bdp->status) & RXBD_EMPTY) && rx_work_limit--) {
struct sk_buff *newskb;
rmb();
@@ -3021,17 +3061,18 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
skb = rx_queue->rx_skbuff[rx_queue->skb_currx];
- dma_unmap_single(priv->dev, bdp->bufPtr,
+ dma_unmap_single(priv->dev, be32_to_cpu(bdp->bufPtr),
priv->rx_buffer_size, DMA_FROM_DEVICE);
- if (unlikely(!(bdp->status & RXBD_ERR) &&
- bdp->length > priv->rx_buffer_size))
- bdp->status = RXBD_LARGE;
+ if (unlikely(!(be16_to_cpu(bdp->status) & RXBD_ERR) &&
+ be16_to_cpu(bdp->length) > priv->rx_buffer_size))
+ bdp->status = cpu_to_be16(RXBD_LARGE);
/* We drop the frame if we failed to allocate a new buffer */
- if (unlikely(!newskb || !(bdp->status & RXBD_LAST) ||
- bdp->status & RXBD_ERR)) {
- count_errors(bdp->status, dev);
+ if (unlikely(!newskb ||
+ !(be16_to_cpu(bdp->status) & RXBD_LAST) ||
+ be16_to_cpu(bdp->status) & RXBD_ERR)) {
+ count_errors(be16_to_cpu(bdp->status), dev);
if (unlikely(!newskb))
newskb = skb;
@@ -3043,7 +3084,8 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
howmany++;
if (likely(skb)) {
- pkt_len = bdp->length - ETH_FCS_LEN;
+ pkt_len = be16_to_cpu(bdp->length) -
+ ETH_FCS_LEN;
/* Remove the FCS from the packet length */
skb_put(skb, pkt_len);
rx_queue->stats.rx_bytes += pkt_len;
@@ -3456,22 +3498,21 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num,
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs;
- int idx;
- char tmpbuf[ETH_ALEN];
u32 tempval;
u32 __iomem *macptr = &regs->macstnaddr1;
macptr += num*2;
- /* Now copy it into the mac registers backwards, cuz
- * little endian is silly
+ /* For a station address of 0x12345678ABCD in transmission
+ * order (BE), MACnADDR1 is set to 0xCDAB7856 and
+ * MACnADDR2 is set to 0x34120000.
*/
- for (idx = 0; idx < ETH_ALEN; idx++)
- tmpbuf[ETH_ALEN - 1 - idx] = addr[idx];
+ tempval = (addr[5] << 24) | (addr[4] << 16) |
+ (addr[3] << 8) | addr[2];
- gfar_write(macptr, *((u32 *) (tmpbuf)));
+ gfar_write(macptr, tempval);
- tempval = *((u32 *) (tmpbuf + 4));
+ tempval = (addr[1] << 24) | (addr[0] << 16);
gfar_write(macptr+1, tempval);
}
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index 5f7a98d..f7c14a5 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -302,9 +302,14 @@ extern const char gfar_driver_version[];
#define RQUEUE_EN_ALL 0x000000FF
/* Init to do tx snooping for buffers and descriptors */
+#ifdef CONFIG_SOC_LS1021A
+#define DMACTRL_INIT_SETTINGS 0x00000003
+#else
#define DMACTRL_INIT_SETTINGS 0x000000c3
+#endif
#define DMACTRL_GRS 0x00000010
#define DMACTRL_GTS 0x00000008
+#define DMACTRL_LE 0x00008000
#define TSTAT_CLEAR_THALT_ALL 0xFF000000
#define TSTAT_CLEAR_THALT 0x80000000
@@ -616,12 +621,12 @@ struct txbd8
{
union {
struct {
- u16 status; /* Status Fields */
- u16 length; /* Buffer length */
+ __be16 status; /* Status Fields */
+ __be16 length; /* Buffer length */
};
- u32 lstatus;
+ __be32 lstatus;
};
- u32 bufPtr; /* Buffer Pointer */
+ __be32 bufPtr; /* Buffer Pointer */
};
struct txfcb {
@@ -629,28 +634,28 @@ struct txfcb {
u8 ptp; /* Flag to enable tx timestamping */
u8 l4os; /* Level 4 Header Offset */
u8 l3os; /* Level 3 Header Offset */
- u16 phcs; /* Pseudo-header Checksum */
- u16 vlctl; /* VLAN control word */
+ __be16 phcs; /* Pseudo-header Checksum */
+ __be16 vlctl; /* VLAN control word */
};
struct rxbd8
{
union {
struct {
- u16 status; /* Status Fields */
- u16 length; /* Buffer Length */
+ __be16 status; /* Status Fields */
+ __be16 length; /* Buffer Length */
};
- u32 lstatus;
+ __be32 lstatus;
};
- u32 bufPtr; /* Buffer Pointer */
+ __be32 bufPtr; /* Buffer Pointer */
};
struct rxfcb {
- u16 flags;
+ __be16 flags;
u8 rq; /* Receive Queue index */
u8 pro; /* Layer 4 Protocol */
u16 reserved;
- u16 vlctl; /* VLAN control word */
+ __be16 vlctl; /* VLAN control word */
};
struct gianfar_skb_cb {
@@ -1327,6 +1332,9 @@ struct gfar_private {
/* L2 SRAM alloc of BDs */
bd_l2sram_en:1;
+ /* little endian dma buffer and descriptor host interface */
+ unsigned int dma_endian_le;
+
/* The total tx and rx ring size for the enabled queues */
unsigned int total_tx_ring_size;
unsigned int total_rx_ring_size;
@@ -1424,6 +1432,44 @@ static inline void gfar_write_isrg(struct gfar_private *priv)
}
}
+static inline int gfar_is_dma_stopped(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+
+ return ((gfar_read(&regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)) ==
+ (IEVENT_GRSC | IEVENT_GTSC));
+}
+
+static inline int gfar_is_rx_dma_stopped(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+
+ return gfar_read(&regs->ievent) & IEVENT_GRSC;
+}
+
+static inline void gfar_wmb(void)
+{
+#if defined(CONFIG_PPC)
+ /* The powerpc-specific eieio() is used, as wmb() has too strong
+ * semantics (it requires synchronization between cacheable and
+ * uncacheable mappings, which eieio() doesn't provide and which we
+ * don't need), thus requiring a more expensive sync instruction. At
+ * some point, the set of architecture-independent barrier functions
+ * should be expanded to include weaker barriers.
+ */
+ eieio();
+#else
+ wmb(); /* order write acesses for BD (or FCB) fields */
+#endif
+}
+
+static inline void gfar_clear_txbd_status(struct txbd8 *bdp)
+{
+ u32 lstatus = be32_to_cpu(bdp->lstatus);
+ lstatus &= BD_LFLAG(TXBD_WRAP);
+ bdp->lstatus = cpu_to_be32(lstatus);
+}
+
irqreturn_t gfar_receive(int irq, void *dev_id);
int startup_gfar(struct net_device *dev);
void stop_gfar(struct net_device *dev);
@@ -1503,30 +1549,31 @@ static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb)
}
static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb,
- int fcb_length)
+ int fcb_length)
{
/* If we're here, it's a IP packet with a TCP or UDP
- * payload. We set it to checksum, using a pseudo-header
- * we provide
- */
+ * payload. We set it to checksum, using a pseudo-header
+ * we provide
+ */
u8 flags = TXFCB_DEFAULT;
/* Tell the controller what the protocol is
- * And provide the already calculated phcs
- */
+ * And provide the already calculated phcs
+ */
if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
flags |= TXFCB_UDP;
- fcb->phcs = udp_hdr(skb)->check;
+ fcb->phcs = (__force __be16)(udp_hdr(skb)->check);
} else
- fcb->phcs = tcp_hdr(skb)->check;
+ fcb->phcs = (__force __be16)(tcp_hdr(skb)->check);
/* l3os is the distance between the start of the
- * frame (skb->data) and the start of the IP hdr.
- * l4os is the distance between the start of the
- * l3 hdr and the l4 hdr
- */
- fcb->l3os = (u16)(skb_network_offset(skb) - fcb_length);
+ * frame (skb->data) and the start of the IP hdr.
+ * l4os is the distance between the start of the
+ * l3 hdr and the l4 hdr
+ */
+ fcb->l3os = (u8)(skb_network_offset(skb) - fcb_length);
fcb->l4os = skb_network_header_len(skb);
+
fcb->flags = flags;
}
@@ -1549,15 +1596,15 @@ static inline void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue,
{
u32 lstatus;
- bdp->bufPtr = buf;
+ bdp->bufPtr = cpu_to_be32(buf);
lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT);
if (bdp == rx_queue->rx_bd_base + rx_queue->rx_ring_size - 1)
lstatus |= BD_LFLAG(RXBD_WRAP);
- eieio();
+ gfar_wmb();
- bdp->lstatus = lstatus;
+ bdp->lstatus = cpu_to_be32(lstatus);
}
static inline void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue,
@@ -1615,7 +1662,8 @@ static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb)
* were verified, then we tell the kernel that no
* checksumming is necessary. Otherwise, it is [FIXME]
*/
- if ((fcb->flags & RXFCB_CSUM_MASK) == (RXFCB_CIP | RXFCB_CTU))
+ if ((be16_to_cpu(fcb->flags) & RXFCB_CSUM_MASK) ==
+ (RXFCB_CIP | RXFCB_CTU))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb_checksum_none_assert(skb);
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 <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
-#include <asm/ucc.h>
-#include <asm/ucc_fast.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/ucc.h>
+#include <linux/fsl/ucc_fast.h>
#include <asm/machdep.h>
#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 <linux/list.h>
#include <linux/if_ether.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
-#include <asm/ucc_fast.h>
+#include <linux/fsl/ucc.h>
+#include <linux/fsl/ucc_fast.h>
#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 <linux/kernel.h>
#include <linux/list.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
-#include <asm/ucc_fast.h>
+#include <linux/fsl/ucc.h>
+#include <linux/fsl/ucc_fast.h>
/* SI RAM entries */
#define SIR_LAST 0x0001
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/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..c5d9ec3
--- /dev/null
+++ b/drivers/pci/host/pci-layerscape.c
@@ -0,0 +1,273 @@
+/*
+ * PCIe host controller driver for Freescale Layerscape SoCs
+ *
+ * Copyright (C) 2014 Freescale Semiconductor.
+ *
+ * Author: Minghuan Lian <Minghuan.Lian@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/bitrev.h>
+
+#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 = (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 <Minghuan.Lian@freescale.com>");
+MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 510994a..f727445 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -11,11 +11,16 @@
* published by the Free Software Foundation.
*/
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/msi.h>
#include <linux/of_address.h>
+#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
+#include <linux/platform_device.h>
#include <linux/types.h>
#include "pcie-designware.h"
@@ -64,14 +69,16 @@
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)
{
+ BUG_ON(!sys->private_data);
+
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);
@@ -85,7 +92,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);
@@ -115,33 +122,270 @@ 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;
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;
}
-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;
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;
+}
+
+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 */
+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,
+ i * 32 + pos);
+ dw_pcie_wr_own_conf(pp,
+ PCIE_MSI_INTR0_STATUS + i * 12,
+ 4, 1 << pos);
+ generic_handle_irq(irq);
+ pos++;
+ }
+ }
+ }
return ret;
}
+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 aligned.
+ */
+ if (pos % msgvec)
+ pos += msgvec - (pos % msgvec);
+ else
+ flag = 0;
+ } while (flag);
+
+ *pos0 = pos;
+ 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;
+
+ 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 */
+ 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 irq, pos0, pos1, i;
+ struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata);
+
+ 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 = irq_find_mapping(pp->irq_domain, pos0);
+ 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.
+ */
+
+ 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;
+ }
+ set_bit(pos0 + i, pp->msi_irq_in_use);
+ /*Enable corresponding interrupt in MSI interrupt controller */
+ 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;
+ return irq;
+
+no_valid_irq:
+ *pos = pos0;
+ return -ENOSPC;
+}
+
+static void clear_irq(unsigned int irq)
+{
+ unsigned int pos, nvec;
+ struct msi_desc *msi;
+ struct pcie_port *pp;
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ /* get the port structure */
+ msi = irq_data_get_msi(data);
+ pp = sys_to_pcie(msi->dev->bus->sysdata);
+
+ /* undo what was done in assign_irq */
+ pos = data->hwirq;
+ nvec = 1 << msi->msi_attrib.multiple;
+
+ clear_irq_range(pp, irq, nvec, pos);
+
+ /* 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,
+ 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);
+
+ 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)
+ msgvec = 0;
+
+ irq = assign_irq((1 << msgvec), desc, &pos);
+ if (irq < 0)
+ return irq;
+
+ /*
+ * 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;
+
+ 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;
+
+ 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;
+}
+
+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,12 +394,52 @@ 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;
+ struct platform_device *pdev = to_platform_device(pp->dev);
struct of_pci_range range;
struct of_pci_range_parser parser;
- u32 val;
+ struct resource *cfg_res;
+ u32 val, na, ns;
+ const __be32 *addrp;
+ int i, index, ret;
+
+ /* 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) {
+ 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->cfg0_size;
+
+ /* Find the untranslated configuration space address */
+ index = of_property_match_string(np, "reg-names", "config");
+ 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 {
+ 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");
@@ -174,23 +458,50 @@ 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);
- pp->config.io_size = resource_size(&pp->io);
- pp->config.io_bus_addr = range.pci_addr;
+ + global_io_offset - 1);
+ 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 */
+ 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;
+ 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 -
+ parser.np + na, ns);
}
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->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->cfg0_size;
}
}
+ 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));
@@ -200,22 +511,24 @@ 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,
- pp->config.cfg0_size);
if (!pp->va_cfg0_base) {
- dev_err(pp->dev, "error with ioremap in function\n");
- return -ENOMEM;
+ pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
+ pp->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->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
+ pp->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)) {
@@ -223,6 +536,25 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
return -EINVAL;
}
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ 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);
+ } else {
+ ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
if (pp->ops->host_init)
pp->ops->host_init(pp);
@@ -238,8 +570,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_assign_unassigned_resources();
+ pci_common_init_dev(pp->dev, &dw_pci);
#ifdef CONFIG_PCI_DOMAINS
dw_pci.domain++;
#endif
@@ -252,9 +583,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->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);
@@ -268,9 +599,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->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);
@@ -283,12 +614,12 @@ 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->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);
}
@@ -299,12 +630,12 @@ 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->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);
}
@@ -321,11 +652,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);
}
@@ -344,18 +677,19 @@ 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);
}
return ret;
}
-
static int dw_pcie_valid_config(struct pcie_port *pp,
struct pci_bus *bus, int dev)
{
@@ -383,26 +717,22 @@ 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) {
- BUG();
- return -EINVAL;
- }
-
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
*val = 0xffffffff;
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,
+ 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);
- spin_unlock_irqrestore(&pp->conf_lock, flags);
return ret;
}
@@ -411,24 +741,20 @@ 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) {
- BUG();
- return -EINVAL;
- }
-
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,
+ 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);
- spin_unlock_irqrestore(&pp->conf_lock, flags);
return ret;
}
@@ -438,67 +764,82 @@ 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;
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(sys->io_offset, pp->io.start);
+ 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);
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);
- if (pp) {
- pp->root_bus_nr = sys->busnr;
- bus = pci_scan_root_bus(NULL, 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);
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);
+ int irq;
+
+ irq = of_irq_parse_and_map_pci(dev, slot, pin);
+ if (!irq)
+ irq = pp->irq;
+
+ return irq;
+}
- 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)
{
- struct pcie_port_info *config = &pp->config;
u32 val;
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) {
@@ -548,7 +889,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 133820f..c625675 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -11,33 +11,48 @@
* published by the Free Software Foundation.
*/
-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;
-};
+#ifndef _PCIE_DESIGNWARE_H
+#define _PCIE_DESIGNWARE_H
+
+/*
+ * 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;
void __iomem *dbi_base;
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;
- spinlock_t conf_lock;
+ u64 mem_mod_base;
+ phys_addr_t mem_bus_addr;
+ u32 mem_size;
struct resource cfg;
struct resource io;
struct resource mem;
- struct pcie_port_info config;
+ struct resource busn;
int irq;
u32 lanes;
struct pcie_host_ops *ops;
+ int msi_irq;
+ struct irq_domain *irq_domain;
+ unsigned long msi_data;
+ DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
};
struct pcie_host_ops {
@@ -47,19 +62,26 @@ 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);
+ 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);
};
-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);
+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);
+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);
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 */
diff --git a/drivers/platform/fsl/sleep_fsm.c b/drivers/platform/fsl/sleep_fsm.c
index 35b033e..987a93a8 100644
--- a/drivers/platform/fsl/sleep_fsm.c
+++ b/drivers/platform/fsl/sleep_fsm.c
@@ -12,342 +12,261 @@
* option) any later version.
*/
+#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/types.h>
-#define FSL_STRIDE_4B 4
-#define FSL_STRIDE_8B 8
-
-/* End flag */
-#define FSM_END_FLAG 0xFFFFFFFFUL
-
-/* EPGCR (Event Processor Global Control Register) */
-#define EPGCR 0x000
-
-/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */
-#define EPEVTCR0 0x050
-#define EPEVTCR9 0x074
-#define EPEVTCR_STRIDE FSL_STRIDE_4B
-
-/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
-#define EPXTRIGCR 0x090
-
-/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */
-#define EPIMCR0 0x100
-#define EPIMCR31 0x17C
-#define EPIMCR_STRIDE FSL_STRIDE_4B
-
-/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */
-#define EPSMCR0 0x200
-#define EPSMCR15 0x278
-#define EPSMCR_STRIDE FSL_STRIDE_8B
-
-/* EPECR0-15 (Event Processor Event Control Registers) */
-#define EPECR0 0x300
-#define EPECR15 0x33C
-#define EPECR_STRIDE FSL_STRIDE_4B
-
-/* EPACR0-15 (Event Processor Action Control Registers) */
-#define EPACR0 0x400
-#define EPACR15 0x43C
-#define EPACR_STRIDE FSL_STRIDE_4B
-
-/* EPCCRi0-15 (Event Processor Counter Control Registers) */
-#define EPCCR0 0x800
-#define EPCCR15 0x83C
-#define EPCCR_STRIDE FSL_STRIDE_4B
-
-/* EPCMPR0-15 (Event Processor Counter Compare Registers) */
-#define EPCMPR0 0x900
-#define EPCMPR15 0x93C
-#define EPCMPR_STRIDE FSL_STRIDE_4B
-
-/* EPCTR0-31 (Event Processor Counter Register) */
-#define EPCTR0 0xA00
-#define EPCTR31 0xA7C
-#define EPCTR_STRIDE FSL_STRIDE_4B
-
-/* NPC triggered Memory-Mapped Access Registers */
-#define NCR 0x000
-#define MCCR1 0x0CC
-#define MCSR1 0x0D0
-#define MMAR1LO 0x0D4
-#define MMAR1HI 0x0D8
-#define MMDR1 0x0DC
-#define MCSR2 0x0E0
-#define MMAR2LO 0x0E4
-#define MMAR2HI 0x0E8
-#define MMDR2 0x0EC
-#define MCSR3 0x0F0
-#define MMAR3LO 0x0F4
-#define MMAR3HI 0x0F8
-#define MMDR3 0x0FC
-
-/* RCPM Core State Action Control Register 0 */
-#define CSTTACR0 0xB00
-
-/* RCPM Core Group 1 Configuration Register 0 */
-#define CG1CR0 0x31C
-
-/* Block offsets */
-#define RCPM_BLOCK_OFFSET 0x00022000
-#define EPU_BLOCK_OFFSET 0x00000000
-#define NPC_BLOCK_OFFSET 0x00001000
-
-struct fsm_reg_vals {
- u32 offset;
- u32 value;
-};
-
+#include "sleep_fsm.h"
/*
* These values are from chip's reference manual. For example,
* the values for T1040 can be found in "8.4.3.8 Programming
* supporting deep sleep mode" of Chapter 8 "Run Control and
* Power Management (RCPM)".
- * The default value can be applied to T104x.
+ * The default value can be applied to T104x, LS1021.
*/
-struct fsm_reg_vals fsm_default_val[] = {
+struct fsm_reg_vals epu_default_val[] = {
/* EPGCR (Event Processor Global Control Register) */
- {EPU_BLOCK_OFFSET + EPGCR, 0},
+ {EPGCR, 0},
/* EPECR (Event Processor Event Control Registers) */
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 2, 0xF0004004},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 3, 0x80000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 4, 0x20000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 5, 0x08000004},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 6, 0x80000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 7, 0x80000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 8, 0x60000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 9, 0x08000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 10, 0x42000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 11, 0x90000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 12, 0x80000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 13, 0x08000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 14, 0x02000084},
- {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 15, 0x00000004},
+ {EPECR0 + EPECR_STRIDE * 0, 0},
+ {EPECR0 + EPECR_STRIDE * 1, 0},
+ {EPECR0 + EPECR_STRIDE * 2, 0xF0004004},
+ {EPECR0 + EPECR_STRIDE * 3, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 4, 0x20000084},
+ {EPECR0 + EPECR_STRIDE * 5, 0x08000004},
+ {EPECR0 + EPECR_STRIDE * 6, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 7, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 8, 0x60000084},
+ {EPECR0 + EPECR_STRIDE * 9, 0x08000084},
+ {EPECR0 + EPECR_STRIDE * 10, 0x42000084},
+ {EPECR0 + EPECR_STRIDE * 11, 0x90000084},
+ {EPECR0 + EPECR_STRIDE * 12, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 13, 0x08000084},
+ {EPECR0 + EPECR_STRIDE * 14, 0x02000084},
+ {EPECR0 + EPECR_STRIDE * 15, 0x00000004},
/*
* EPEVTCR (Event Processor EVT Pin Control Registers)
* SCU8 triger EVT2, and SCU11 triger EVT9
*/
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 3, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 4, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 5, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 6, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 7, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 8, 0},
- {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 0, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 1, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 3, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 4, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 5, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 6, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 7, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 8, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001},
/* EPCMPR (Event Processor Counter Compare Registers) */
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 3, 0},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 6, 0},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 7, 0},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 13, 0},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF},
- {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 0, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 1, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 3, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020},
+ {EPCMPR0 + EPCMPR_STRIDE * 6, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 7, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 13, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF},
/* EPCCR (Event Processor Counter Control Registers) */
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 2, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 3, 0},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 4, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 5, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 6, 0},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 7, 0},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 8, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 9, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 10, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 11, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 12, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 13, 0},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 14, 0x92840000},
- {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 15, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 0, 0},
+ {EPCCR0 + EPCCR_STRIDE * 1, 0},
+ {EPCCR0 + EPCCR_STRIDE * 2, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 3, 0},
+ {EPCCR0 + EPCCR_STRIDE * 4, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 5, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 6, 0},
+ {EPCCR0 + EPCCR_STRIDE * 7, 0},
+ {EPCCR0 + EPCCR_STRIDE * 8, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 9, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 10, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 11, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 12, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 13, 0},
+ {EPCCR0 + EPCCR_STRIDE * 14, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 15, 0x92840000},
/* EPSMCR (Event Processor SCU Mux Control Registers) */
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031},
- {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 0, 0},
+ {EPSMCR0 + EPSMCR_STRIDE * 1, 0},
+ {EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000},
+ {EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000},
+ {EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00},
+ {EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000},
+ {EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000},
+ {EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030},
+ {EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000},
+ {EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100},
+ {EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031},
+ {EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000},
/* EPACR (Event Processor Action Control Registers) */
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 2, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 3, 0x00000080},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 4, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 5, 0x00000040},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 6, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 7, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 8, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 9, 0x0000001C},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 10, 0x00000020},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 11, 0},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 12, 0x00000003},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 13, 0x06000000},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 14, 0x04000000},
- {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 15, 0x02000000},
+ {EPACR0 + EPACR_STRIDE * 0, 0},
+ {EPACR0 + EPACR_STRIDE * 1, 0},
+ {EPACR0 + EPACR_STRIDE * 2, 0},
+ {EPACR0 + EPACR_STRIDE * 3, 0x00000080},
+ {EPACR0 + EPACR_STRIDE * 4, 0},
+ {EPACR0 + EPACR_STRIDE * 5, 0x00000040},
+ {EPACR0 + EPACR_STRIDE * 6, 0},
+ {EPACR0 + EPACR_STRIDE * 7, 0},
+ {EPACR0 + EPACR_STRIDE * 8, 0},
+ {EPACR0 + EPACR_STRIDE * 9, 0x0000001C},
+ {EPACR0 + EPACR_STRIDE * 10, 0x00000020},
+ {EPACR0 + EPACR_STRIDE * 11, 0},
+ {EPACR0 + EPACR_STRIDE * 12, 0x00000003},
+ {EPACR0 + EPACR_STRIDE * 13, 0x06000000},
+ {EPACR0 + EPACR_STRIDE * 14, 0x04000000},
+ {EPACR0 + EPACR_STRIDE * 15, 0x02000000},
/* EPIMCR (Event Processor Input Mux Control Registers) */
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 0, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 1, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 2, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 3, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 6, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 7, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 8, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 9, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 10, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 11, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 13, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 14, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 15, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 17, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 18, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 19, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 21, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 23, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 24, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 25, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 26, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 27, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 29, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 30, 0},
- {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 0, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 1, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 2, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 3, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 6, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 7, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 8, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 9, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 10, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 11, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 13, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 14, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 15, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 17, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 18, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 19, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 21, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 23, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 24, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 25, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 26, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 27, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 29, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 30, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000},
/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
- {EPU_BLOCK_OFFSET + EPXTRIGCR, 0x0000FFDF},
+ {EPXTRIGCR, 0x0000FFDF},
+ /* end */
+ {FSM_END_FLAG, 0},
+};
+
+struct fsm_reg_vals npc_default_val[] = {
/* NPC triggered Memory-Mapped Access Registers */
- {NPC_BLOCK_OFFSET + NCR, 0x80000000},
- {NPC_BLOCK_OFFSET + MCCR1, 0},
- {NPC_BLOCK_OFFSET + MCSR1, 0},
- {NPC_BLOCK_OFFSET + MMAR1LO, 0},
- {NPC_BLOCK_OFFSET + MMAR1HI, 0},
- {NPC_BLOCK_OFFSET + MMDR1, 0},
- {NPC_BLOCK_OFFSET + MCSR2, 0},
- {NPC_BLOCK_OFFSET + MMAR2LO, 0},
- {NPC_BLOCK_OFFSET + MMAR2HI, 0},
- {NPC_BLOCK_OFFSET + MMDR2, 0},
- {NPC_BLOCK_OFFSET + MCSR3, 0x80000000},
- {NPC_BLOCK_OFFSET + MMAR3LO, 0x000E2130},
- {NPC_BLOCK_OFFSET + MMAR3HI, 0x00030000},
- {NPC_BLOCK_OFFSET + MMDR3, 0x00020000},
- /* Configure RCPM for detecting Core0’s PH15 state */
- {RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001},
- {RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001},
+ {NCR, 0x80000000},
+ {MCCR1, 0},
+ {MCSR1, 0},
+ {MMAR1LO, 0},
+ {MMAR1HI, 0},
+ {MMDR1, 0},
+ {MCSR2, 0},
+ {MMAR2LO, 0},
+ {MMAR2HI, 0},
+ {MMDR2, 0},
+ {MCSR3, 0x80000000},
+ {MMAR3LO, 0x000E2130},
+ {MMAR3HI, 0x00030000},
+ {MMDR3, 0x00020000},
/* end */
{FSM_END_FLAG, 0},
};
/**
- * fsl_dp_fsm_clean - Clear EPU's FSM
- * @dcsr_base: the base address of DCSR registers
- * @val: Pointer to values for FSM registers. If NULL,
- * will use the default value.
+ * fsl_fsm_setup - Configure EPU's FSM registers
+ * @base: the base address of registers
+ * @val: Pointer to address-value pairs for FSM registers
*/
-void fsl_dp_fsm_clean(void __iomem *dcsr_base, struct fsm_reg_vals *val)
+void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val)
{
- void *epu_base = dcsr_base + EPU_BLOCK_OFFSET;
- u32 offset;
- struct fsm_reg_vals *data;
+ struct fsm_reg_vals *data = val;
+
+ BUG_ON(!base || !data);
+ while (data->offset != FSM_END_FLAG) {
+ iowrite32be(data->value, base + data->offset);
+ data++;
+ }
+}
+
+void fsl_epu_setup_default(void __iomem *epu_base)
+{
+ fsl_fsm_setup(epu_base, epu_default_val);
+}
+
+void fsl_npc_setup_default(void __iomem *npc_base)
+{
+ fsl_fsm_setup(npc_base, npc_default_val);
+}
- if (val) {
- data = val;
- while (data->offset != FSM_END_FLAG) {
- out_be32(dcsr_base + data->offset, 0);
- data++;
- }
- return;
+/**
+ * fsl_fsm_clean - Clear EPU's FSM registers
+ * @base: the base address of registers
+ * @val: Pointer to address-value pairs for FSM registers
+ */
+void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val)
+{
+ struct fsm_reg_vals *data = val;
+
+ BUG_ON(!base || !data);
+ while (data->offset != FSM_END_FLAG) {
+ iowrite32be(0, base + data->offset);
+ data++;
}
+}
+
+void fsl_epu_clean_default(void __iomem *epu_base)
+{
+ u32 offset;
/* follow the exact sequence to clear the registers */
/* Clear EPACRn */
for (offset = EPACR0; offset <= EPACR15; offset += EPACR_STRIDE)
- out_be32(epu_base + offset, 0);
+ iowrite32be(0, epu_base + offset);
/* Clear EPEVTCRn */
for (offset = EPEVTCR0; offset <= EPEVTCR9; offset += EPEVTCR_STRIDE)
- out_be32(epu_base + offset, 0);
+ iowrite32be(0, epu_base + offset);
/* Clear EPGCR */
- out_be32(epu_base + EPGCR, 0);
+ iowrite32be(0, epu_base + EPGCR);
/* Clear EPSMCRn */
for (offset = EPSMCR0; offset <= EPSMCR15; offset += EPSMCR_STRIDE)
- out_be32(epu_base + offset, 0);
+ iowrite32be(0, epu_base + offset);
/* Clear EPCCRn */
- for (offset = EPCCR0; offset <= EPCCR15; offset += EPCCR_STRIDE)
- out_be32(epu_base + offset, 0);
+ for (offset = EPCCR0; offset <= EPCCR31; offset += EPCCR_STRIDE)
+ iowrite32be(0, epu_base + offset);
/* Clear EPCMPRn */
- for (offset = EPCMPR0; offset <= EPCMPR15; offset += EPCMPR_STRIDE)
- out_be32(epu_base + offset, 0);
+ for (offset = EPCMPR0; offset <= EPCMPR31; offset += EPCMPR_STRIDE)
+ iowrite32be(0, epu_base + offset);
/* Clear EPCTRn */
for (offset = EPCTR0; offset <= EPCTR31; offset += EPCTR_STRIDE)
- out_be32(epu_base + offset, 0);
+ iowrite32be(0, epu_base + offset);
/* Clear EPIMCRn */
for (offset = EPIMCR0; offset <= EPIMCR31; offset += EPIMCR_STRIDE)
- out_be32(epu_base + offset, 0);
+ iowrite32be(0, epu_base + offset);
/* Clear EPXTRIGCRn */
- out_be32(epu_base + EPXTRIGCR, 0);
+ iowrite32be(0, epu_base + EPXTRIGCR);
/* Clear EPECRn */
for (offset = EPECR0; offset <= EPECR15; offset += EPECR_STRIDE)
- out_be32(epu_base + offset, 0);
-}
-
-/**
- * fsl_dp_fsm_setup - Configure EPU's FSM
- * @dcsr_base: the base address of DCSR registers
- * @val: Pointer to values for FSM registers. If NULL,
- * will use the default value.
- */
-void fsl_dp_fsm_setup(void __iomem *dcsr_base, struct fsm_reg_vals *val)
-{
- struct fsm_reg_vals *data;
-
- /* if NULL, use the default values */
- if (val)
- data = val;
- else
- data = fsm_default_val;
-
- /* clear all registers */
- fsl_dp_fsm_clean(dcsr_base, NULL);
-
- while (data->offset != FSM_END_FLAG) {
- if (data->value)
- out_be32(dcsr_base + data->offset, data->value);
- data++;
- }
+ iowrite32be(0, epu_base + offset);
}
diff --git a/drivers/platform/fsl/sleep_fsm.h b/drivers/platform/fsl/sleep_fsm.h
new file mode 100644
index 0000000..8fea8ec
--- /dev/null
+++ b/drivers/platform/fsl/sleep_fsm.h
@@ -0,0 +1,106 @@
+/*
+ * Freescale deep sleep FSM (finite-state machine) configuration
+ *
+ * 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.
+ */
+#ifndef _FSL_SLEEP_FSM_H
+#define _FSL_SLEEP_FSM_H
+
+#define FSL_STRIDE_4B 4
+#define FSL_STRIDE_8B 8
+
+/* End flag */
+#define FSM_END_FLAG 0xFFFFFFFFUL
+
+/* Block offsets */
+#define RCPM_BLOCK_OFFSET 0x00022000
+#define EPU_BLOCK_OFFSET 0x00000000
+#define NPC_BLOCK_OFFSET 0x00001000
+
+/* EPGCR (Event Processor Global Control Register) */
+#define EPGCR 0x000
+
+/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */
+#define EPEVTCR0 0x050
+#define EPEVTCR9 0x074
+#define EPEVTCR_STRIDE FSL_STRIDE_4B
+
+/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
+#define EPXTRIGCR 0x090
+
+/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */
+#define EPIMCR0 0x100
+#define EPIMCR31 0x17C
+#define EPIMCR_STRIDE FSL_STRIDE_4B
+
+/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */
+#define EPSMCR0 0x200
+#define EPSMCR15 0x278
+#define EPSMCR_STRIDE FSL_STRIDE_8B
+
+/* EPECR0-15 (Event Processor Event Control Registers) */
+#define EPECR0 0x300
+#define EPECR15 0x33C
+#define EPECR_STRIDE FSL_STRIDE_4B
+
+/* EPACR0-15 (Event Processor Action Control Registers) */
+#define EPACR0 0x400
+#define EPACR15 0x43C
+#define EPACR_STRIDE FSL_STRIDE_4B
+
+/* EPCCRi0-15 (Event Processor Counter Control Registers) */
+#define EPCCR0 0x800
+#define EPCCR15 0x83C
+#define EPCCR31 0x87C
+#define EPCCR_STRIDE FSL_STRIDE_4B
+
+/* EPCMPR0-15 (Event Processor Counter Compare Registers) */
+#define EPCMPR0 0x900
+#define EPCMPR15 0x93C
+#define EPCMPR31 0x97C
+#define EPCMPR_STRIDE FSL_STRIDE_4B
+
+/* EPCTR0-31 (Event Processor Counter Register) */
+#define EPCTR0 0xA00
+#define EPCTR31 0xA7C
+#define EPCTR_STRIDE FSL_STRIDE_4B
+
+/* NPC triggered Memory-Mapped Access Registers */
+#define NCR 0x000
+#define MCCR1 0x0CC
+#define MCSR1 0x0D0
+#define MMAR1LO 0x0D4
+#define MMAR1HI 0x0D8
+#define MMDR1 0x0DC
+#define MCSR2 0x0E0
+#define MMAR2LO 0x0E4
+#define MMAR2HI 0x0E8
+#define MMDR2 0x0EC
+#define MCSR3 0x0F0
+#define MMAR3LO 0x0F4
+#define MMAR3HI 0x0F8
+#define MMDR3 0x0FC
+
+/* RCPM Core State Action Control Register 0 */
+#define CSTTACR0 0xB00
+
+/* RCPM Core Group 1 Configuration Register 0 */
+#define CG1CR0 0x31C
+
+struct fsm_reg_vals {
+ u32 offset;
+ u32 value;
+};
+
+void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val);
+void fsl_epu_setup_default(void __iomem *epu_base);
+void fsl_npc_setup_default(void __iomem *npc_base);
+void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val);
+void fsl_epu_clean_default(void __iomem *epu_base);
+
+#endif /* _FSL_SLEEP_FSM_H */
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 75840b5..9c64341 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -62,6 +62,17 @@ 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
+ select REGMAP_MMIO
+ 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..666e1be
--- /dev/null
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -0,0 +1,556 @@
+/*
+ * 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 <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define FTM_SC 0x00
+#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_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;
+
+ struct regmap *regmap;
+
+ 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 duty;
+ u32 val;
+
+ regmap_read(fpc->regmap, FTM_MOD, &val);
+ duty = (unsigned long long)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 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;
+ }
+
+ 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;
+ }
+
+ mutex_unlock(&fpc->lock);
+
+ duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns);
+
+ 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;
+}
+
+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;
+
+ regmap_read(fpc->regmap, FTM_POL, &val);
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ val |= BIT(pwm->hwpwm);
+ else
+ val &= ~BIT(pwm->hwpwm);
+
+ regmap_write(fpc->regmap, FTM_POL, val);
+
+ return 0;
+}
+
+static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
+{
+ int ret;
+
+ if (fpc->use_count++ != 0)
+ return 0;
+
+ /* select counter clock source */
+ 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)
+ return ret;
+
+ ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ if (ret) {
+ clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ int ret;
+
+ mutex_lock(&fpc->lock);
+ regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0);
+
+ ret = fsl_counter_clock_enable(fpc);
+ mutex_unlock(&fpc->lock);
+
+ return ret;
+}
+
+static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
+{
+ /*
+ * 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 */
+ 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]);
+}
+
+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);
+ regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm),
+ BIT(pwm->hwpwm));
+
+ fsl_counter_clock_disable(fpc);
+
+ regmap_read(fpc->regmap, FTM_OUTMASK, &val);
+ 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;
+
+ 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 bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FTM_CNT:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config fsl_pwm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = FTM_PWMLOAD,
+ .volatile_reg = fsl_pwm_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+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);
+ if (!fpc)
+ return -ENOMEM;
+
+ mutex_init(&fpc->lock);
+
+ fpc->chip.dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ftm_sys", 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])) {
+ 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;
+ fpc->chip.can_sleep = true;
+
+ 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);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_pwm_suspend(struct device *dev)
+{
+ struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
+ u32 val;
+
+ regcache_cache_only(fpc->regmap, true);
+ regcache_mark_dirty(fpc->regmap);
+
+ /* read from cache */
+ regmap_read(fpc->regmap, FTM_OUTMASK, &val);
+ if ((val & 0xFF) != 0xFF) {
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
+ }
+
+ return 0;
+}
+
+static int fsl_pwm_resume(struct device *dev)
+{
+ struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
+ u32 val;
+
+ /* read from cache */
+ regmap_read(fpc->regmap, FTM_OUTMASK, &val);
+ if ((val & 0xFF) != 0xFF) {
+ clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
+ clk_prepare_enable(fpc->clk[fpc->cnt_select]);
+ clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ }
+
+ /* restore all registers from cache */
+ regcache_cache_only(fpc->regmap, false);
+ regcache_sync(fpc->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops fsl_pwm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_pwm_suspend, fsl_pwm_resume)
+};
+
+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,
+ .pm = &fsl_pwm_pm_ops,
+ },
+ .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 <Li.Xiubo@freescale.com>");
+MODULE_ALIAS("platform:fsl-ftm-pwm");
+MODULE_LICENSE("GPL");
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,
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
new file mode 100644
index 0000000..bb02cfa
--- /dev/null
+++ b/drivers/soc/Kconfig
@@ -0,0 +1,18 @@
+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
+
+source drivers/soc/qe/Kconfig
+
+endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
new file mode 100644
index 0000000..1212b75
--- /dev/null
+++ b/drivers/soc/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Freescale SOC specific device drivers.
+#
+
+obj-$(CONFIG_FSL_SOC_DRIVERS) += fsl/
+
+obj-$(CONFIG_QUICC_ENGINE) += qe/
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..c9b04c4
--- /dev/null
+++ b/drivers/soc/fsl/ls1/Kconfig
@@ -0,0 +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 <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#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);
diff --git a/drivers/soc/qe/Kconfig b/drivers/soc/qe/Kconfig
new file mode 100644
index 0000000..43b984b
--- /dev/null
+++ b/drivers/soc/qe/Kconfig
@@ -0,0 +1,46 @@
+#
+# QE Communication options
+#
+
+config QUICC_ENGINE
+ bool "Freescale QUICC Engine (QE) Support"
+ select 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/arch/powerpc/sysdev/qe_lib/Makefile b/drivers/soc/qe/Makefile
index f1855c1..77f6fd9 100644
--- a/arch/powerpc/sysdev/qe_lib/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/arch/powerpc/sysdev/qe_lib/gpio.c b/drivers/soc/qe/gpio.c
index 521e67a..fb891d4 100644
--- a/arch/powerpc/sysdev/qe_lib/gpio.c
+++ b/drivers/soc/qe/gpio.c
@@ -21,7 +21,7 @@
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/export.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
struct qe_gpio_chip {
struct of_mm_gpio_chip mm_gc;
@@ -158,7 +158,8 @@ struct qe_pin *qe_pin_request(struct device_node *np, int index)
if (WARN_ON(!gc))
goto err0;
- if (!of_device_is_compatible(gc->of_node, "fsl,mpc8323-qe-pario-bank")) {
+ 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;
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/drivers/soc/qe/qe.c
index ad324ba..bfea0f8 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/drivers/soc/qe/qe.c
@@ -2,8 +2,8 @@
* Copyright (C) 2006-2010, 2012 Freescale Semiconductor, Inc.
* All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
* Based on cpm2_common.c from Dan Malek (dmalek@jlc.net)
*
* Description:
@@ -33,10 +33,10 @@
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/pgtable.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
#include <asm/prom.h>
-#include <asm/rheap.h>
+#include <linux/fsl/rheap.h>
static void qe_snums_init(void);
static int qe_sdma_init(void);
@@ -65,7 +65,8 @@ struct qe_snum {
struct qe_immap __iomem *qe_immr;
EXPORT_SYMBOL(qe_immr);
-static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */
+/* 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;
@@ -73,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;
@@ -86,14 +87,13 @@ 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;
}
-
EXPORT_SYMBOL(get_qe_base);
void qe_reset(void)
@@ -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);
@@ -165,13 +164,13 @@ EXPORT_SYMBOL(qe_issue_cmd);
* 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;
+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;
}
@@ -238,7 +237,8 @@ EXPORT_SYMBOL(qe_setbrg);
*/
enum qe_clock qe_clock_source(const char *source)
{
- unsigned int i;
+ unsigned int ret;
+ unsigned long val;
if (strcasecmp(source, "none") == 0)
return QE_CLK_NONE;
@@ -250,17 +250,22 @@ enum qe_clock qe_clock_source(const char *source)
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;
+ 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) {
- i = simple_strtoul(source + 3, NULL, 10);
- if ((i >= 1) && (i <= 24))
- return (QE_CLK1 - 1) + i;
+ 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;
}
@@ -359,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;
}
@@ -391,22 +396,20 @@ static void qe_upload_microcode(const void *base,
unsigned int i;
if (ucode->major || ucode->minor || ucode->revision)
- printk(KERN_INFO "qe-firmware: "
- "uploading microcode '%s' version %u.%u.%u\n",
+ pr_info("qe-FM: 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);
+ 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);
}
/*
@@ -436,7 +439,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware)
const struct qe_header *hdr;
if (!firmware) {
- printk(KERN_ERR "qe-firmware: invalid pointer\n");
+ pr_info("qe-firmware: invalid pointer\n");
return -EINVAL;
}
@@ -446,19 +449,19 @@ int qe_upload_firmware(const struct qe_firmware *firmware)
/* 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");
+ pr_info("qe-firmware: not a microcode\n");
return -EPERM;
}
/* Check the version */
if (hdr->version != 1) {
- printk(KERN_ERR "qe-firmware: unsupported version\n");
+ pr_info("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");
+ pr_info("qe-firmware: invalid data\n");
return -EINVAL;
}
@@ -476,14 +479,14 @@ int qe_upload_firmware(const struct qe_firmware *firmware)
/* Validate the length */
if (length != calc_size + sizeof(__be32)) {
- printk(KERN_ERR "qe-firmware: invalid length\n");
+ 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)) {
- printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n");
+ pr_info("qe-firmware: firmware CRC is invalid\n");
return -EIO;
}
@@ -494,12 +497,11 @@ int qe_upload_firmware(const struct qe_firmware *firmware)
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",
+ 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
- printk(KERN_INFO "qe-firmware: firmware '%s'\n",
+ pr_info("qe-firmware: firmware '%s'\n",
firmware->id);
/*
@@ -525,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;
@@ -648,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");
@@ -664,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");
@@ -681,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_common.c b/drivers/soc/qe/qe_common.c
new file mode 100644
index 0000000..6bc9b18
--- /dev/null
+++ b/drivers/soc/qe/qe_common.c
@@ -0,0 +1,185 @@
+/*
+ * Common QE code
+ *
+ * Author: Scott Wood <scottwood@freescale.com>
+ *
+ * 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 <dan@embeddedalley.com>
+ * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com)
+ * 2006 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <linux/fsl/rheap.h>
+#include <linux/fsl/qe.h>
+
+static spinlock_t qe_muram_lock;
+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;
+
+/* 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 = (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;
+ 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/arch/powerpc/sysdev/qe_lib/qe_ic.c b/drivers/soc/qe/qe_ic.c
index b2b87c3..11fe98c 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_ic.c
+++ b/drivers/soc/qe/qe_ic.c
@@ -16,7 +16,10 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/irqdomain.h>
#include <linux/errno.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/stddef.h>
@@ -26,11 +29,12 @@
#include <linux/bootmem.h>
#include <linux/spinlock.h>
#include <asm/irq.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/prom.h>
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe_ic.h>
#include "qe_ic.h"
+#include "../../irqchip/irqchip.h"
static DEFINE_RAW_SPINLOCK(qe_ic_lock);
@@ -175,15 +179,15 @@ static struct qe_ic_info qe_ic_info[] = {
},
};
-static inline u32 qe_ic_read(volatile __be32 __iomem * base, unsigned int reg)
+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(volatile __be32 __iomem * base, unsigned int reg,
+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)
@@ -258,7 +262,7 @@ static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
struct irq_chip *chip;
if (qe_ic_info[hw].mask == 0) {
- printk(KERN_ERR "Can't map reserved IRQ\n");
+ pr_err("Can't map reserved IRQ\n");
return -EINVAL;
}
/* Default chip */
@@ -341,7 +345,7 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags,
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");
+ pr_err("Failed to map QE_IC low IRQ\n");
kfree(qe_ic);
return;
}
@@ -483,19 +487,33 @@ static int __init init_qe_ic_sysfs(void)
{
int rc;
- printk(KERN_DEBUG "Registering qe_ic with sysfs...\n");
+ pr_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");
+ pr_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");
+ pr_err("Failed registering qe_ic sys device\n");
return -ENODEV;
}
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);
diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/drivers/soc/qe/qe_ic.h
index efef7ab..aab8abd 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_ic.h
+++ b/drivers/soc/qe/qe_ic.h
@@ -16,7 +16,7 @@
#ifndef _POWERPC_SYSDEV_QE_IC_H
#define _POWERPC_SYSDEV_QE_IC_H
-#include <asm/qe_ic.h>
+#include <linux/fsl/qe_ic.h>
#define NR_QE_IC_INTS 64
@@ -76,7 +76,7 @@
struct qe_ic {
/* Control registers offset */
- volatile u32 __iomem *regs;
+ u32 __iomem *regs;
/* The remapper for this QEIC */
struct irq_domain *irqhost;
diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/drivers/soc/qe/qe_io.c
index a88807b..7f40d3c 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/drivers/soc/qe/qe_io.c
@@ -21,15 +21,14 @@
#include <linux/module.h>
#include <linux/ioport.h>
-#include <asm/io.h>
-#include <asm/qe.h>
+#include <linux/io.h>
+#include <linux/fsl/qe.h>
#include <asm/prom.h>
-#include <sysdev/fsl_soc.h>
#undef DEBUG
static struct qe_pio_regs __iomem *par_io;
-static int num_par_io_ports = 0;
+static int num_par_io_ports;
int par_io_init(struct device_node *np)
{
@@ -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;
}
@@ -157,13 +152,13 @@ int par_io_of_config(struct device_node *np)
const unsigned int *pio_map;
if (par_io == NULL) {
- printk(KERN_ERR "par_io not initialized\n");
+ pr_info("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");
+ pr_info("pio-handle not available\n");
return -1;
}
@@ -171,12 +166,12 @@ int par_io_of_config(struct device_node *np)
pio_map = of_get_property(pio, "pio-map", &pio_map_len);
if (pio_map == NULL) {
- printk(KERN_ERR "pio-map is not set!\n");
+ pr_info("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");
+ pr_info("pio-map format wrong!\n");
return -1;
}
@@ -197,22 +192,21 @@ static void dump_par_io(void)
{
unsigned int i;
- printk(KERN_INFO "%s: par_io=%p\n", __func__, par_io);
+ pr_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));
+ pr_info(" cpodr[%u]=%08x\n", i,
+ ioread32be(&par_io[i].cpodr));
+ pr_info(" cpdata[%u]=%08x\n", i,
+ ioread32be(&par_io[i].cpdata));
+ pr_info(" cpdir1[%u]=%08x\n", i,
+ ioread32be(&par_io[i].cpdir1));
+ pr_info(" cpdir2[%u]=%08x\n", i,
+ ioread32be(&par_io[i].cpdir2));
+ pr_info(" cppar1[%u]=%08x\n", i,
+ ioread32be(&par_io[i].cppar1));
+ pr_info(" cppar2[%u]=%08x\n", i,
+ ioread32be(&par_io[i].cppar2));
}
-
}
EXPORT_SYMBOL(dump_par_io);
#endif /* DEBUG */
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/drivers/soc/qe/ucc.c
index 1e27d63..c333387 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/drivers/soc/qe/ucc.c
@@ -5,8 +5,8 @@
*
* Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* 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
@@ -21,10 +21,10 @@
#include <linux/export.h>
#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
-#include <asm/ucc.h>
+#include <linux/io.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/ucc.h>
int ucc_set_qe_mux_mii_mng(unsigned int ucc_num)
{
@@ -59,21 +59,29 @@ int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed)
/* 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;
+ case 0:
+ guemr = &qe_immr->ucc1.slow.guemr;
break;
- case 1: guemr = &qe_immr->ucc2.slow.guemr;
+ case 1:
+ guemr = &qe_immr->ucc2.slow.guemr;
break;
- case 2: guemr = &qe_immr->ucc3.slow.guemr;
+ case 2:
+ guemr = &qe_immr->ucc3.slow.guemr;
break;
- case 3: guemr = &qe_immr->ucc4.slow.guemr;
+ case 3:
+ guemr = &qe_immr->ucc4.slow.guemr;
break;
- case 4: guemr = &qe_immr->ucc5.slow.guemr;
+ case 4:
+ guemr = &qe_immr->ucc5.slow.guemr;
break;
- case 5: guemr = &qe_immr->ucc6.slow.guemr;
+ case 5:
+ guemr = &qe_immr->ucc6.slow.guemr;
break;
- case 6: guemr = &qe_immr->ucc7.slow.guemr;
+ case 6:
+ guemr = &qe_immr->ucc7.slow.guemr;
break;
- case 7: guemr = &qe_immr->ucc8.slow.guemr;
+ case 7:
+ guemr = &qe_immr->ucc8.slow.guemr;
break;
default:
return -EINVAL;
@@ -136,67 +144,156 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
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;
+ 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;
+ 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;
+ 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;
+ 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;
+ default:
+ break;
}
/* Check for invalid combination of clock and UCC number */
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/drivers/soc/qe/ucc_fast.c
index 67191a0..3ed8d22 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/drivers/soc/qe/ucc_fast.c
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* Description:
* QE UCC Fast API Set - UCC Fast specific routines implementations.
@@ -21,54 +21,54 @@
#include <linux/err.h>
#include <linux/export.h>
-#include <asm/io.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/io.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
-#include <asm/ucc_fast.h>
+#include <linux/fsl/ucc.h>
+#include <linux/fsl/ucc_fast.h>
-void ucc_fast_dump_regs(struct ucc_fast_private * uccf)
+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));
+ 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, ioread32be(&uccf->uf_regs->gumr));
+ pr_info("upsmr : addr=0x%p, val=0x%08x\n",
+ &uccf->uf_regs->upsmr, ioread32be(&uccf->uf_regs->upsmr));
+ pr_info("utodr : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->utodr, ioread16be(&uccf->uf_regs->utodr));
+ pr_info("udsr : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->udsr, ioread16be(&uccf->uf_regs->udsr));
+ pr_info("ucce : addr=0x%p, val=0x%08x\n",
+ &uccf->uf_regs->ucce, ioread32be(&uccf->uf_regs->ucce));
+ pr_info("uccm : addr=0x%p, val=0x%08x\n",
+ &uccf->uf_regs->uccm, ioread32be(&uccf->uf_regs->uccm));
+ pr_info("uccs : addr=0x%p, val=0x%02x\n",
+ &uccf->uf_regs->uccs, ioread8(&uccf->uf_regs->uccs));
+ pr_info("urfb : addr=0x%p, val=0x%08x\n",
+ &uccf->uf_regs->urfb, ioread32be(&uccf->uf_regs->urfb));
+ pr_info("urfs : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->urfs, ioread16be(&uccf->uf_regs->urfs));
+ pr_info("urfet : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->urfet, ioread16be(&uccf->uf_regs->urfet));
+ pr_info("urfset: addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->urfset, ioread16be(&uccf->uf_regs->urfset));
+ pr_info("utfb : addr=0x%p, val=0x%08x\n",
+ &uccf->uf_regs->utfb, ioread32be(&uccf->uf_regs->utfb));
+ pr_info("utfs : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->utfs, ioread16be(&uccf->uf_regs->utfs));
+ pr_info("utfet : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->utfet, ioread16be(&uccf->uf_regs->utfet));
+ pr_info("utftt : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->utftt, ioread16be(&uccf->uf_regs->utftt));
+ pr_info("utpt : addr=0x%p, val=0x%04x\n",
+ &uccf->uf_regs->utpt, ioread16be(&uccf->uf_regs->utpt));
+ pr_info("urtry : addr=0x%p, val=0x%08x\n",
+ &uccf->uf_regs->urtry, ioread32be(&uccf->uf_regs->urtry));
+ pr_info("guemr : addr=0x%p, val=0x%02x\n",
+ &uccf->uf_regs->guemr, ioread8(&uccf->uf_regs->guemr));
}
EXPORT_SYMBOL(ucc_fast_dump_regs);
@@ -88,13 +88,13 @@ u32 ucc_fast_get_qe_cr_subblock(int uccf_num)
}
EXPORT_SYMBOL(ucc_fast_get_qe_cr_subblock);
-void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf)
+void ucc_fast_transmit_on_demand(struct ucc_fast_private *uccf)
{
- out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD);
+ iowrite16be(UCC_FAST_TOD, &uccf->uf_regs->utodr);
}
EXPORT_SYMBOL(ucc_fast_transmit_on_demand);
-void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode)
+void ucc_fast_enable(struct ucc_fast_private *uccf, enum comm_dir mode)
{
struct ucc_fast __iomem *uf_regs;
u32 gumr;
@@ -102,7 +102,7 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode)
uf_regs = uccf->uf_regs;
/* Enable reception and/or transmission on this UCC. */
- gumr = in_be32(&uf_regs->gumr);
+ gumr = ioread32be(&uf_regs->gumr);
if (mode & COMM_DIR_TX) {
gumr |= UCC_FAST_GUMR_ENT;
uccf->enabled_tx = 1;
@@ -111,11 +111,11 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode)
gumr |= UCC_FAST_GUMR_ENR;
uccf->enabled_rx = 1;
}
- out_be32(&uf_regs->gumr, gumr);
+ iowrite32be(gumr, &uf_regs->gumr);
}
EXPORT_SYMBOL(ucc_fast_enable);
-void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode)
+void ucc_fast_disable(struct ucc_fast_private *uccf, enum comm_dir mode)
{
struct ucc_fast __iomem *uf_regs;
u32 gumr;
@@ -123,7 +123,7 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode)
uf_regs = uccf->uf_regs;
/* Disable reception and/or transmission on this UCC. */
- gumr = in_be32(&uf_regs->gumr);
+ gumr = ioread32be(&uf_regs->gumr);
if (mode & COMM_DIR_TX) {
gumr &= ~UCC_FAST_GUMR_ENT;
uccf->enabled_tx = 0;
@@ -132,11 +132,12 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode)
gumr &= ~UCC_FAST_GUMR_ENR;
uccf->enabled_rx = 0;
}
- out_be32(&uf_regs->gumr, gumr);
+ iowrite32be(gumr, &uf_regs->gumr);
}
EXPORT_SYMBOL(ucc_fast_disable);
-int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret)
+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;
@@ -148,56 +149,56 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
/* 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__);
+ 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)) {
- printk(KERN_ERR "%s: max_rx_buf_length not aligned\n",
+ 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) {
- printk(KERN_ERR "%s: urfs is too small\n", __func__);
+ pr_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__);
+ pr_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__);
+ pr_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__);
+ pr_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__);
+ pr_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__);
+ pr_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__);
+ pr_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",
+ pr_err("%s: Cannot allocate private data\n",
__func__);
return -ENOMEM;
}
@@ -207,7 +208,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
/* 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__);
+ pr_err("%s: Cannot map UCC registers\n", __func__);
kfree(uccf);
return -ENOMEM;
}
@@ -231,7 +232,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
/* 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__);
+ pr_err("%s: cannot set UCC type\n", __func__);
ucc_fast_free(uccf);
return ret;
}
@@ -264,13 +265,13 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
gumr |= uf_info->tenc;
gumr |= uf_info->tcrc;
gumr |= uf_info->mode;
- out_be32(&uf_regs->gumr, gumr);
+ iowrite32be(gumr, &uf_regs->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",
+ pr_err("%s: cannot allocate MURAM for TX FIFO\n",
__func__);
uccf->ucc_fast_tx_virtual_fifo_base_offset = 0;
ucc_fast_free(uccf);
@@ -283,7 +284,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
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",
+ pr_err("%s: cannot allocate MURAM for RX FIFO\n",
__func__);
uccf->ucc_fast_rx_virtual_fifo_base_offset = 0;
ucc_fast_free(uccf);
@@ -291,15 +292,15 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
}
/* 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);
+ iowrite16be(uf_info->urfs, &uf_regs->urfs);
+ iowrite16be(uf_info->urfet, &uf_regs->urfet);
+ iowrite16be(uf_info->urfset, &uf_regs->urfset);
+ iowrite16be(uf_info->utfs, &uf_regs->utfs);
+ iowrite16be(uf_info->utfet, &uf_regs->utfet);
+ iowrite16be(uf_info->utftt, &uf_regs->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);
+ iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb);
+ iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb);
/* Mux clocking */
/* Grant Support */
@@ -314,7 +315,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
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",
+ pr_err("%s: illegal value for RX clock\n",
__func__);
ucc_fast_free(uccf);
return -EINVAL;
@@ -323,7 +324,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
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",
+ pr_err("%s: illegal value for TX clock\n",
__func__);
ucc_fast_free(uccf);
return -EINVAL;
@@ -367,21 +368,21 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
}
/* Set interrupt mask register at UCC level. */
- out_be32(&uf_regs->uccm, uf_info->uccm_mask);
+ iowrite32be(uf_info->uccm_mask, &uf_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_be32(&uf_regs->ucce, 0xffffffff);
+ iowrite32be(0xffffffff, &uf_regs->ucce);
*uccf_ret = uccf;
return 0;
}
EXPORT_SYMBOL(ucc_fast_init);
-void ucc_fast_free(struct ucc_fast_private * uccf)
+void ucc_fast_free(struct ucc_fast_private *uccf)
{
if (!uccf)
return;
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/drivers/soc/qe/ucc_slow.c
index 1c062f4..edb1b2e 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c
+++ b/drivers/soc/qe/ucc_slow.c
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* Description:
* QE UCC Slow API Set - UCC Slow specific routines implementations.
@@ -21,12 +21,12 @@
#include <linux/err.h>
#include <linux/export.h>
-#include <asm/io.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/io.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
-#include <asm/ucc_slow.h>
+#include <linux/fsl/ucc.h>
+#include <linux/fsl/ucc_slow.h>
u32 ucc_slow_get_qe_cr_subblock(int uccs_num)
{
@@ -44,12 +44,12 @@ u32 ucc_slow_get_qe_cr_subblock(int uccs_num)
}
EXPORT_SYMBOL(ucc_slow_get_qe_cr_subblock);
-void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs)
+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)
+void ucc_slow_graceful_stop_tx(struct ucc_slow_private *uccs)
{
struct ucc_slow_info *us_info = uccs->us_info;
u32 id;
@@ -60,7 +60,7 @@ void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs)
}
EXPORT_SYMBOL(ucc_slow_graceful_stop_tx);
-void ucc_slow_stop_tx(struct ucc_slow_private * uccs)
+void ucc_slow_stop_tx(struct ucc_slow_private *uccs)
{
struct ucc_slow_info *us_info = uccs->us_info;
u32 id;
@@ -70,7 +70,7 @@ void ucc_slow_stop_tx(struct ucc_slow_private * uccs)
}
EXPORT_SYMBOL(ucc_slow_stop_tx);
-void ucc_slow_restart_tx(struct ucc_slow_private * uccs)
+void ucc_slow_restart_tx(struct ucc_slow_private *uccs)
{
struct ucc_slow_info *us_info = uccs->us_info;
u32 id;
@@ -80,7 +80,7 @@ void ucc_slow_restart_tx(struct ucc_slow_private * uccs)
}
EXPORT_SYMBOL(ucc_slow_restart_tx);
-void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode)
+void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode)
{
struct ucc_slow *us_regs;
u32 gumr_l;
@@ -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,11 +97,11 @@ 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);
-void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode)
+void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode)
{
struct ucc_slow *us_regs;
u32 gumr_l;
@@ -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);
@@ -126,7 +126,8 @@ EXPORT_SYMBOL(ucc_slow_disable);
*
* The caller should initialize the following us_info
*/
-int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret)
+int ucc_slow_init(struct ucc_slow_info *us_info,
+ struct ucc_slow_private **uccs_ret)
{
struct ucc_slow_private *uccs;
u32 i;
@@ -142,7 +143,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* 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__);
+ pr_err("%s: illegal UCC number\n", __func__);
return -EINVAL;
}
@@ -154,13 +155,13 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
*/
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");
+ pr_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",
+ pr_err("%s: Cannot allocate private data\n",
__func__);
return -ENOMEM;
}
@@ -170,7 +171,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* 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__);
+ pr_err("%s: Cannot map UCC registers\n", __func__);
kfree(uccs);
return -ENOMEM;
}
@@ -178,8 +179,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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);
+ uccs->p_ucce = (u16 *)&(us_regs->ucce);
+ uccs->p_uccm = (u16 *)&(us_regs->uccm);
#ifdef STATISTICS
uccs->rx_frames = 0;
uccs->tx_frames = 0;
@@ -190,7 +191,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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__);
+ pr_err("%s: cannot allocate MURAM for PRAM", __func__);
ucc_slow_free(uccs);
return -ENOMEM;
}
@@ -203,12 +204,12 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* 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__);
+ 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);
+ iowrite16be(us_info->max_rx_buf_length, &uccs->us_pram->mrblr);
INIT_LIST_HEAD(&uccs->confQ);
@@ -217,7 +218,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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__,
+ 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);
@@ -228,7 +229,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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__);
+ pr_err("%s: cannot allocate TX BDs", __func__);
uccs->tx_base_offset = 0;
ucc_slow_free(uccs);
return -ENOMEM;
@@ -238,27 +239,27 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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 */
@@ -279,7 +280,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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 |
@@ -292,7 +293,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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 */
@@ -302,8 +303,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
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 */
@@ -317,7 +318,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* 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",
+ pr_err("%s: illegal value for RX clock\n",
__func__);
ucc_slow_free(uccs);
return -EINVAL;
@@ -325,7 +326,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
/* 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",
+ pr_err("%s: illegal value for TX clock\n",
__func__);
ucc_slow_free(uccs);
return -EINVAL;
@@ -333,14 +334,14 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
}
/* 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)
@@ -357,7 +358,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc
}
EXPORT_SYMBOL(ucc_slow_init);
-void ucc_slow_free(struct ucc_slow_private * uccs)
+void ucc_slow_free(struct ucc_slow_private *uccs)
{
if (!uccs)
return;
@@ -377,4 +378,3 @@ void ucc_slow_free(struct ucc_slow_private * uccs)
kfree(uccs);
}
EXPORT_SYMBOL(ucc_slow_free);
-
diff --git a/arch/powerpc/sysdev/qe_lib/usb.c b/drivers/soc/qe/usb.c
index 27f23bd..58f812a 100644
--- a/arch/powerpc/sysdev/qe_lib/usb.c
+++ b/drivers/soc/qe/usb.c
@@ -17,8 +17,8 @@
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/io.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
int qe_usb_clock_set(enum qe_clock clk, int rate)
{
@@ -27,16 +27,36 @@ int qe_usb_clock_set(enum qe_clock clk, int rate)
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;
+ 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;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b9c53cc..352f982 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -263,7 +263,8 @@ config SPI_FSL_SPI
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
- select SPI_BITBANG
+ select REGMAP_MMIO
+ depends on SOC_VF610 || SOC_LS1021A || 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-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-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 <linux/fsl_devices.h>
#include <linux/dma-mapping.h>
#include <asm/cpm.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
#include "spi-fsl-lib.h"
#include "spi-fsl-cpm.h"
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index f132234..a6df59e 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -62,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
@@ -104,15 +107,22 @@ 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;
- void *base;
+ struct regmap *regmap;
int irq;
- struct clk *clk;
+ struct clk *clk;
- struct spi_transfer *cur_transfer;
+ struct spi_transfer *cur_transfer;
+ struct spi_message *cur_msg;
struct chip_data *cur_chip;
size_t len;
void *tx;
@@ -122,25 +132,20 @@ 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;
+ wait_queue_head_t waitq;
+ u32 waitflags;
};
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,
@@ -165,18 +170,74 @@ 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;
}
-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,48 +249,19 @@ 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;
}
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;
@@ -238,96 +270,127 @@ 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++;
}
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) {
- if ((dspi->rx_end - dspi->rx) == 1)
- break;
-
- d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
-
- if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
- *(u16 *)dspi->rx = d;
- dspi->rx += 2;
-
- } else {
- d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
- 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);
- 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);
+ if ((dspi->cs_change) && (!dspi->len))
+ dspi_pushr &= ~SPI_PUSHR_CONT;
- if (t->speed_hz)
- writel(dspi->cur_chip->ctar_val,
- dspi->base + SPI_CTAR(dspi->cs));
+ 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);
- u32 pushr = readl(dspi->base + SPI_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;
+
+ if (transfer->delay_usecs)
+ udelay(transfer->delay_usecs);
- switch (value) {
- case BITBANG_CS_ACTIVE:
- pushr |= SPI_PUSHR_CONT;
- case BITBANG_CS_INACTIVE:
- pushr &= ~SPI_PUSHR_CONT;
}
- writel(pushr, dspi->base + SPI_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);
@@ -368,40 +431,47 @@ 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);
- if (!spi->bits_per_word)
- spi->bits_per_word = 8;
+ 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;
-
- writel(SPI_SR_EOQF, dspi->base + SPI_SR);
-
- 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)
- 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 {
- 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;
}
-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 */ }
};
@@ -431,8 +501,13 @@ 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,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x88,
};
static int dspi_probe(struct platform_device *pdev)
@@ -441,6 +516,7 @@ static int dspi_probe(struct platform_device *pdev)
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));
@@ -449,12 +525,13 @@ 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.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) |
@@ -474,13 +551,31 @@ 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);
- 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 = 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",
+ 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");
@@ -506,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;
@@ -519,7 +614,6 @@ out_clk_put:
clk_disable_unprepare(dspi->clk);
out_master_put:
spi_master_put(master);
- platform_set_drvdata(pdev, NULL);
return ret;
}
@@ -530,8 +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);
- spi_master_put(dspi->bitbang.master);
+ clk_disable_unprepare(dspi->clk);
+ spi_unregister_master(dspi->master);
+ spi_master_put(dspi->master);
return 0;
}
@@ -547,5 +642,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);
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;
diff --git a/drivers/tdm/device/Kconfig b/drivers/tdm/device/Kconfig
index af6a710..5490e1d 100644
--- a/drivers/tdm/device/Kconfig
+++ b/drivers/tdm/device/Kconfig
@@ -6,7 +6,7 @@ menu "TDM Device support"
config TDM_FSL
tristate "Driver for Freescale TDM controller"
- depends on FSL_SOC
+ depends on FSL_SOC || ARCH_MXC
---help---
This is a driver for Freescale TDM controller. The controller
is found in various Freescale SOCs viz MPC8315, P1020. The TDM driver
@@ -15,7 +15,7 @@ config TDM_FSL
config FSL_UCC_TDM
tristate "UCC TDM driver for Freescale QE engine"
- depends on FSL_SOC || CONFIG_QE
+ depends on FSL_SOC || QUICC_ENGINE
---help---
This is a driver for Freescale QE UCC working with TDM interface.
diff --git a/drivers/tdm/device/fsl_ucc_tdm.c b/drivers/tdm/device/fsl_ucc_tdm.c
index ad25c0a..a6abe71 100644
--- a/drivers/tdm/device/fsl_ucc_tdm.c
+++ b/drivers/tdm/device/fsl_ucc_tdm.c
@@ -29,6 +29,7 @@
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/init.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/tdm.h>
@@ -37,8 +38,6 @@
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
-#include <sysdev/fsl_soc.h>
-#include <sysdev/fsl_soc.h>
#include <linux/slab.h>
#include "fsl_ucc_tdm.h"
@@ -143,27 +142,27 @@ static void dump_ucc(struct ucc_tdm_private *priv)
ucc_fast_dump_regs(priv->uccf);
dev_info(priv->dev, "Dumping UCC %d Parameter RAM\n",
priv->ut_info->uf_info.ucc_num);
- dev_info(priv->dev, "rbase = 0x%x\n", in_be32(&ucc_pram->rbase));
- dev_info(priv->dev, "rbptr = 0x%x\n", in_be32(&ucc_pram->rbptr));
- dev_info(priv->dev, "mrblr = 0x%x\n", in_be16(&ucc_pram->mrblr));
- dev_info(priv->dev, "rbdlen = 0x%x\n", in_be16(&ucc_pram->rbdlen));
- dev_info(priv->dev, "rbdstat = 0x%x\n", in_be16(&ucc_pram->rbdstat));
- dev_info(priv->dev, "rstate = 0x%x\n", in_be32(&ucc_pram->rstate));
- dev_info(priv->dev, "rdptr = 0x%x\n", in_be32(&ucc_pram->rdptr));
- dev_info(priv->dev, "riptr = 0x%x\n", in_be16(&ucc_pram->riptr));
- dev_info(priv->dev, "tbase = 0x%x\n", in_be32(&ucc_pram->tbase));
- dev_info(priv->dev, "tbptr = 0x%x\n", in_be32(&ucc_pram->tbptr));
- dev_info(priv->dev, "tbdlen = 0x%x\n", in_be16(&ucc_pram->tbdlen));
- dev_info(priv->dev, "tbdstat = 0x%x\n", in_be16(&ucc_pram->tbdstat));
- dev_info(priv->dev, "tstate = 0x%x\n", in_be32(&ucc_pram->tstate));
- dev_info(priv->dev, "tdptr = 0x%x\n", in_be32(&ucc_pram->tdptr));
- dev_info(priv->dev, "tiptr = 0x%x\n", in_be16(&ucc_pram->tiptr));
- dev_info(priv->dev, "rcrc = 0x%x\n", in_be32(&ucc_pram->rcrc));
- dev_info(priv->dev, "tcrc = 0x%x\n", in_be32(&ucc_pram->tcrc));
- dev_info(priv->dev, "c_mask = 0x%x\n", in_be32(&ucc_pram->c_mask));
- dev_info(priv->dev, "c_pers = 0x%x\n", in_be32(&ucc_pram->c_pres));
- dev_info(priv->dev, "disfc = 0x%x\n", in_be16(&ucc_pram->disfc));
- dev_info(priv->dev, "crcec = 0x%x\n", in_be16(&ucc_pram->crcec));
+ dev_info(priv->dev, "rbase = 0x%x\n", ioread32be(&ucc_pram->rbase));
+ dev_info(priv->dev, "rbptr = 0x%x\n", ioread32be(&ucc_pram->rbptr));
+ dev_info(priv->dev, "mrblr = 0x%x\n", ioread16be(&ucc_pram->mrblr));
+ dev_info(priv->dev, "rbdlen = 0x%x\n", ioread16be(&ucc_pram->rbdlen));
+ dev_info(priv->dev, "rbdstat = 0x%x\n", ioread16be(&ucc_pram->rbdstat));
+ dev_info(priv->dev, "rstate = 0x%x\n", ioread32be(&ucc_pram->rstate));
+ dev_info(priv->dev, "rdptr = 0x%x\n", ioread32be(&ucc_pram->rdptr));
+ dev_info(priv->dev, "riptr = 0x%x\n", ioread16be(&ucc_pram->riptr));
+ dev_info(priv->dev, "tbase = 0x%x\n", ioread32be(&ucc_pram->tbase));
+ dev_info(priv->dev, "tbptr = 0x%x\n", ioread32be(&ucc_pram->tbptr));
+ dev_info(priv->dev, "tbdlen = 0x%x\n", ioread16be(&ucc_pram->tbdlen));
+ dev_info(priv->dev, "tbdstat = 0x%x\n", ioread16be(&ucc_pram->tbdstat));
+ dev_info(priv->dev, "tstate = 0x%x\n", ioread32be(&ucc_pram->tstate));
+ dev_info(priv->dev, "tdptr = 0x%x\n", ioread32be(&ucc_pram->tdptr));
+ dev_info(priv->dev, "tiptr = 0x%x\n", ioread16be(&ucc_pram->tiptr));
+ dev_info(priv->dev, "rcrc = 0x%x\n", ioread32be(&ucc_pram->rcrc));
+ dev_info(priv->dev, "tcrc = 0x%x\n", ioread32be(&ucc_pram->tcrc));
+ dev_info(priv->dev, "c_mask = 0x%x\n", ioread32be(&ucc_pram->c_mask));
+ dev_info(priv->dev, "c_pers = 0x%x\n", ioread32be(&ucc_pram->c_pres));
+ dev_info(priv->dev, "disfc = 0x%x\n", ioread16be(&ucc_pram->disfc));
+ dev_info(priv->dev, "crcec = 0x%x\n", ioread16be(&ucc_pram->crcec));
}
static void dump_bds(struct ucc_tdm_private *priv)
@@ -243,18 +242,18 @@ static void init_si(struct ucc_tdm_private *priv)
mask = 0x01 << i;
if (priv->tx_ts_mask & mask)
- out_be16(&siram[siram_entry_id * 32 + i],
- siram_entry_valid);
+ iowrite16be(siram_entry_valid,
+ &siram[siram_entry_id * 32 + i]);
else
- out_be16(&siram[siram_entry_id * 32 + i],
- siram_entry_closed);
+ iowrite16be(siram_entry_closed,
+ &siram[siram_entry_id * 32 + i]);
if (priv->rx_ts_mask & mask)
- out_be16(&siram[siram_entry_id * 32 + 0x200 + i],
- siram_entry_valid);
+ iowrite16be(siram_entry_valid,
+ &siram[siram_entry_id * 32 + 0x200 + i]);
else
- out_be16(&siram[siram_entry_id * 32 + 0x200 + i],
- siram_entry_closed);
+ iowrite16be(siram_entry_closed,
+ &siram[siram_entry_id * 32 + 0x200 + i]);
}
setbits16(&siram[(siram_entry_id * 32) + (priv->num_of_ts - 1)],
@@ -288,16 +287,16 @@ static void init_si(struct ucc_tdm_private *priv)
switch (tdm_port) {
case 0:
- out_be16(&si_regs->sixmr1[0], sixmr);
+ iowrite16be(sixmr, &si_regs->sixmr1[0]);
break;
case 1:
- out_be16(&si_regs->sixmr1[1], sixmr);
+ iowrite16be(sixmr, &si_regs->sixmr1[1]);
break;
case 2:
- out_be16(&si_regs->sixmr1[2], sixmr);
+ iowrite16be(sixmr, &si_regs->sixmr1[2]);
break;
case 3:
- out_be16(&si_regs->sixmr1[3], sixmr);
+ iowrite16be(sixmr, &si_regs->sixmr1[3]);
break;
default:
dev_err(priv->dev, "can not find tdm sixmr reg\n");
@@ -348,7 +347,7 @@ static int utdm_init(struct ucc_tdm_private *priv)
(u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
/* Set UPSMR normal mode */
- out_be32(&priv->uf_regs->upsmr, 0);
+ iowrite32be(0, &priv->uf_regs->upsmr);
priv->tx_bd = dma_alloc_coherent(priv->dev,
NUM_OF_BUF * MAX_RX_BUF_LENGTH,
@@ -408,27 +407,27 @@ static int utdm_init(struct ucc_tdm_private *priv)
}
/* Set RIPTR, TIPTR */
- out_be16(&priv->ucc_pram->riptr, (u16)riptr);
- out_be16(&priv->ucc_pram->tiptr, (u16)tiptr);
+ iowrite16be((u16)riptr, &priv->ucc_pram->riptr);
+ iowrite16be((u16)tiptr, &priv->ucc_pram->tiptr);
/* Set MRBLR */
- out_be16(&priv->ucc_pram->mrblr, (u16)MAX_RX_BUF_LENGTH);
+ iowrite16be((u16)MAX_RX_BUF_LENGTH, &priv->ucc_pram->mrblr);
/* Set RBASE, TBASE */
- out_be32(&priv->ucc_pram->rbase, (u32)priv->dma_rx_bd);
- out_be32(&priv->ucc_pram->tbase, (u32)priv->dma_tx_bd);
+ iowrite32be((u32)priv->dma_rx_bd, &priv->ucc_pram->rbase);
+ iowrite32be((u32)priv->dma_tx_bd, &priv->ucc_pram->tbase);
/* Set RSTATE, TSTATE */
- out_be32(&priv->ucc_pram->rstate, 0x30000000);
- out_be32(&priv->ucc_pram->tstate, 0x30000000);
+ iowrite32be(0x30000000, &priv->ucc_pram->rstate);
+ iowrite32be(0x30000000, &priv->ucc_pram->tstate);
/* Set C_MASK, C_PRES for 16bit CRC */
- out_be32(&priv->ucc_pram->c_mask, 0x0000F0B8);
- out_be32(&priv->ucc_pram->c_pres, 0x0000FFFF);
+ iowrite32be(0x0000F0B8, &priv->ucc_pram->c_mask);
+ iowrite32be(0x0000FFFF, &priv->ucc_pram->c_pres);
- out_be16(&priv->ucc_pram->res0, 0);
+ iowrite16be(0, &priv->ucc_pram->res0);
for (i = 0; i < 4; i++)
- out_be32(&priv->ucc_pram->res4[i], 0x0);
+ iowrite32be(0x0, &priv->ucc_pram->res4[i]);
/* Get BD buffer */
bd_buffer = dma_alloc_coherent(priv->dev,
@@ -454,18 +453,18 @@ static int utdm_init(struct ucc_tdm_private *priv)
else
bd_status = R_E | R_I | R_W | R_CM;
- out_be32((u32 *)(priv->rx_bd + i), bd_status);
- out_be32(&priv->rx_bd[i].buf, priv->dma_rx_addr
- + i * MAX_RX_BUF_LENGTH);
+ iowrite32be(bd_status, (u32 *)(priv->rx_bd + i));
+ iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH,
+ &priv->rx_bd[i].buf);
if (i < (NUM_OF_BUF - 1))
bd_status = T_I;
else
bd_status = T_I | T_W;
- out_be32((u32 *)(priv->tx_bd + i), bd_status);
- out_be32(&priv->tx_bd[i].buf, priv->dma_tx_addr
- + i * MAX_RX_BUF_LENGTH);
+ iowrite32be(bd_status, (u32 *)(priv->tx_bd + i));
+ iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH,
+ &priv->tx_bd[i].buf);
}
priv->phase_rx = 0;
@@ -546,7 +545,7 @@ static int ucc_tdm_write(struct tdm_adapter *adap, u8 *write_buf,
priv->phase_tx = 0;
bd = (priv->tx_bd + priv->phase_tx);
- bd_stat_len = in_be32((u32 __iomem *)bd);
+ bd_stat_len = ioread32be((u32 __iomem *)bd);
tdm_send_buf = priv->tx_buffer +
priv->phase_tx * MAX_RX_BUF_LENGTH;
@@ -557,7 +556,7 @@ static int ucc_tdm_write(struct tdm_adapter *adap, u8 *write_buf,
copy_len = MAX_RX_BUF_LENGTH;
ret = spin_event_timeout(((bd_stat_len =
- in_be32((u32 __iomem *)bd)) & T_R) != T_R ,
+ ioread32be((u32 __iomem *)bd)) & T_R) != T_R ,
1000000, 500);
if (!ret) {
dev_err(priv->dev, "TDM write data error!\n");
@@ -570,11 +569,11 @@ static int ucc_tdm_write(struct tdm_adapter *adap, u8 *write_buf,
bd_stat_len &= ~(T_L | BD_LEN_MASK);
if (i == (buf_num - 1))
- out_be32((u32 __iomem *)(bd),
- bd_stat_len | T_R | T_L | T_I | copy_len);
+ iowrite32be(bd_stat_len | T_R | T_L | T_I | copy_len,
+ (u32 __iomem *)(bd));
else
- out_be32((u32 __iomem *)(bd),
- bd_stat_len | T_R | T_I | copy_len);
+ iowrite32be(bd_stat_len | T_R | T_I | copy_len,
+ (u32 __iomem *)(bd));
priv->phase_tx++;
}
@@ -593,8 +592,8 @@ static irqreturn_t ucc_tdm_irq_handler(int irq, void *dev_id)
ut_info = priv->ut_info;
uccf = priv->uccf;
- ucce = in_be32(uccf->p_ucce);
- uccm = in_be32(uccf->p_uccm);
+ ucce = ioread32be(uccf->p_ucce);
+ uccm = ioread32be(uccf->p_uccm);
if ((ucce >> 16) & UCC_TRANS_UCCE_RXB) {
if (priv->phase_rx == NUM_OF_BUF - 1)
@@ -607,7 +606,7 @@ static irqreturn_t ucc_tdm_irq_handler(int irq, void *dev_id)
}
- out_be32(uccf->p_ucce, ucce);
+ iowrite32be(ucce, uccf->p_ucce);
return IRQ_HANDLED;
@@ -763,18 +762,18 @@ static int ucc_tdm_probe(struct platform_device *pdev)
struct ucc_tdm_info *ut_info;
struct resource res;
int ucc_num;
- const unsigned int *prop;
+ u32 val;
const char *sprop;
struct device_node *np2;
int ret;
- prop = of_get_property(np, "cell-index", NULL);
- if (!prop) {
+ ret = of_property_read_u32_index(np, "cell-index", 0, &val);
+ if (ret) {
dev_err(&pdev->dev, "Invalid ucc property\n");
return -ENODEV;
}
- ucc_num = *prop - 1;
+ ucc_num = val - 1;
if ((ucc_num > 7) || (ucc_num < 0)) {
dev_err(&pdev->dev, ": Invalid UCC num\n");
return -EINVAL;
@@ -859,54 +858,54 @@ static int ucc_tdm_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, utdm_priv);
utdm_priv->dev = &pdev->dev;
- prop = of_get_property(np, "fsl,tx-timeslot", NULL);
- if (!prop) {
+ ret = of_property_read_u32_index(np, "fsl,tx-timeslot", 0, &val);
+ if (ret) {
ret = -EINVAL;
dev_err(&pdev->dev, "Invalid tx-timeslot property\n");
goto err_miss_property;
}
- utdm_priv->tx_ts_mask = *prop;
+ utdm_priv->tx_ts_mask = val;
- prop = of_get_property(np, "fsl,rx-timeslot", NULL);
- if (!prop) {
+ ret = of_property_read_u32_index(np, "fsl,rx-timeslot", 0, &val);
+ if (ret) {
ret = -EINVAL;
dev_err(&pdev->dev, "Invalid rx-timeslot property\n");
goto err_miss_property;
}
- utdm_priv->rx_ts_mask = *prop;
+ utdm_priv->rx_ts_mask = val;
- prop = of_get_property(np, "fsl,tdm-id", NULL);
- if (!prop) {
+ ret = of_property_read_u32_index(np, "fsl,tdm-id", 0, &val);
+ if (ret) {
ret = -EINVAL;
dev_err(&pdev->dev, "No fsl,tdm-id property for this UCC\n");
goto err_miss_property;
}
- utdm_priv->tdm_port = *prop;
+ utdm_priv->tdm_port = val;
ut_info->uf_info.tdm_num = utdm_priv->tdm_port ;
- prop = of_get_property(np, "fsl,tdm-mode", NULL);
- if (!prop) {
+ sprop = of_get_property(np, "fsl,tdm-mode", NULL);
+ if (!sprop) {
ret = -EINVAL;
dev_err(&pdev->dev, "No tdm-mode property for UCC\n");
goto err_miss_property;
}
- utdm_priv->tdm_mode = set_tdm_mode((const char *)prop);
+ utdm_priv->tdm_mode = set_tdm_mode(sprop);
- prop = of_get_property(np, "fsl,tdm-framer-type", NULL);
- if (!prop) {
+ sprop = of_get_property(np, "fsl,tdm-framer-type", NULL);
+ if (!sprop) {
ret = -EINVAL;
dev_err(&pdev->dev, "No tdm-framer-type property for UCC\n");
goto err_miss_property;
}
- utdm_priv->tdm_framer_type = set_tdm_framer((const char *)prop);
+ utdm_priv->tdm_framer_type = set_tdm_framer(sprop);
- prop = of_get_property(np, "fsl,siram-entry-id", NULL);
- if (!prop) {
+ ret = of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val);
+ if (ret) {
ret = -EINVAL;
dev_err(&pdev->dev, "No siram entry id for UCC\n");
goto err_miss_property;
}
- utdm_priv->siram_entry_id = *(const u32 *)prop;
+ utdm_priv->siram_entry_id = val;
np2 = of_find_node_by_name(NULL, "si");
if (!np2) {
@@ -1008,11 +1007,11 @@ static void store_clk_config(struct ucc_tdm_private *utdm_priv)
struct qe_mux *qe_mux_reg = &qe_immr->qmx;
/* store si clk */
- utdm_priv->cmxsi1cr_h = in_be32(&qe_mux_reg->cmxsi1cr_h);
- utdm_priv->cmxsi1cr_l = in_be32(&qe_mux_reg->cmxsi1cr_l);
+ utdm_priv->cmxsi1cr_h = ioread32be(&qe_mux_reg->cmxsi1cr_h);
+ utdm_priv->cmxsi1cr_l = ioread32be(&qe_mux_reg->cmxsi1cr_l);
/* store si sync */
- utdm_priv->cmxsi1syr = in_be32(&qe_mux_reg->cmxsi1syr);
+ utdm_priv->cmxsi1syr = ioread32be(&qe_mux_reg->cmxsi1syr);
/* store ucc clk */
memcpy_fromio(utdm_priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32));
@@ -1024,10 +1023,10 @@ static void resume_clk_config(struct ucc_tdm_private *utdm_priv)
memcpy_toio(qe_mux_reg->cmxucr, utdm_priv->cmxucr, 4 * sizeof(u32));
- out_be32(&qe_mux_reg->cmxsi1cr_h, utdm_priv->cmxsi1cr_h);
- out_be32(&qe_mux_reg->cmxsi1cr_l, utdm_priv->cmxsi1cr_l);
+ iowrite32be(utdm_priv->cmxsi1cr_h, &qe_mux_reg->cmxsi1cr_h);
+ iowrite32be(utdm_priv->cmxsi1cr_l, &qe_mux_reg->cmxsi1cr_l);
- out_be32(&qe_mux_reg->cmxsi1syr, utdm_priv->cmxsi1syr);
+ iowrite32be(utdm_priv->cmxsi1syr, &qe_mux_reg->cmxsi1syr);
}
@@ -1044,8 +1043,8 @@ static int ucc_tdm_suspend(struct device *dev)
uf_regs = utdm_priv->uf_regs;
/* backup gumr guemr*/
- utdm_priv->gumr = in_be32(&uf_regs->gumr);
- utdm_priv->guemr = in_8(&uf_regs->guemr);
+ utdm_priv->gumr = ioread32be(&uf_regs->gumr);
+ utdm_priv->guemr = ioread8(&uf_regs->guemr);
utdm_priv->ucc_pram_bak = kmalloc(sizeof(struct ucc_transparent_param),
GFP_KERNEL);
@@ -1086,25 +1085,25 @@ static int ucc_tdm_resume(struct device *dev)
uccf = utdm_priv->uccf;
/* restore gumr guemr */
- out_8(&uf_regs->guemr, utdm_priv->guemr);
- out_be32(&uf_regs->gumr, utdm_priv->gumr);
+ iowrite8(utdm_priv->guemr, &uf_regs->guemr);
+ iowrite32be(utdm_priv->gumr, &uf_regs->gumr);
/* 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);
+ iowrite16be(uf_info->urfs, &uf_regs->urfs);
+ iowrite16be(uf_info->urfet, &uf_regs->urfet);
+ iowrite16be(uf_info->urfset, &uf_regs->urfset);
+ iowrite16be(uf_info->utfs, &uf_regs->utfs);
+ iowrite16be(uf_info->utfet, &uf_regs->utfet);
+ iowrite16be(uf_info->utftt, &uf_regs->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);
+ iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb);
+ iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb);
/* tdm Rx Tx and sync clock routing */
resume_clk_config(utdm_priv);
- out_be32(&uf_regs->uccm, uf_info->uccm_mask);
- out_be32(&uf_regs->ucce, 0xffffffff);
+ iowrite32be(uf_info->uccm_mask, &uf_regs->uccm);
+ iowrite32be(0xffffffff, &uf_regs->ucce);
ucc_fast_disable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX);
@@ -1117,7 +1116,7 @@ static int ucc_tdm_resume(struct device *dev)
(u8)QE_CR_PROTOCOL_UNSPECIFIED, 0);
/* Set UPSMR normal mode */
- out_be32(&uf_regs->upsmr, 0);
+ iowrite32be(0, &uf_regs->upsmr);
/* init parameter base */
cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num);
@@ -1139,18 +1138,18 @@ static int ucc_tdm_resume(struct device *dev)
else
bd_status = R_E | R_I | R_W | R_CM;
- out_be32((u32 *)(utdm_priv->rx_bd + i), bd_status);
- out_be32(&utdm_priv->rx_bd[i].buf, utdm_priv->dma_rx_addr
- + i * MAX_RX_BUF_LENGTH);
+ iowrite32be(bd_status, (u32 *)(utdm_priv->rx_bd + i));
+ iowrite32be(utdm_priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH,
+ &utdm_priv->rx_bd[i].buf);
if (i < (NUM_OF_BUF - 1))
bd_status = T_I;
else
bd_status = T_I | T_W;
- out_be32((u32 *)(utdm_priv->tx_bd + i), bd_status);
- out_be32(&utdm_priv->tx_bd[i].buf, utdm_priv->dma_tx_addr
- + i * MAX_RX_BUF_LENGTH);
+ iowrite32be(bd_status, (u32 *)(utdm_priv->tx_bd + i));
+ iowrite32be(utdm_priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH,
+ &utdm_priv->tx_bd[i].buf);
}
/* if tdm is busy enable TX and RX */
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 <linux/kernel.h>
#include <linux/list.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
-#include <asm/ucc_fast.h>
+#include <linux/fsl/ucc.h>
+#include <linux/fsl/ucc_fast.h>
/* SI RAM entries */
#define SIR_LAST 0x0001
diff --git a/drivers/tdm/test/Kconfig b/drivers/tdm/test/Kconfig
index 39f9581..243c934 100644
--- a/drivers/tdm/test/Kconfig
+++ b/drivers/tdm/test/Kconfig
@@ -14,7 +14,7 @@ config TDM_TEST
config UCC_TDM_TEST
tristate "UCC TDM test Module"
- depends on FSL_SOC || CONFIG_QE
+ depends on FSL_SOC || QUICC_ENGINE
---help---
This UCC TDM test module is a small test module to test
ucc tdm transfer and receive data via ucc1.
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 8978dc9..1c5d020 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);
@@ -829,6 +1458,20 @@ static int lpuart_suspend(struct device *dev)
static int lpuart_resume(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
+ unsigned long temp;
+
+ if (sport->lpuart32) {
+ lpuart32_setup_watermark(sport);
+ temp = lpuart32_read(sport->port.membase + UARTCTRL);
+ temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE |
+ UARTCTRL_TE | UARTCTRL_ILIE);
+ lpuart32_write(temp, sport->port.membase + UARTCTRL);
+ } else {
+ lpuart_setup_watermark(sport);
+ temp = readb(sport->port.membase + UARTCR2);
+ temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
+ writeb(temp, sport->port.membase + UARTCR2);
+ }
uart_resume_port(&lpuart_reg, &sport->port);
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index 2caf9c6..58be230 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -159,7 +159,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL))
return -EBUSY;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
@@ -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.
*/
@@ -262,6 +288,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 */ },
};
@@ -271,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,
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 8831748..2c9a87c 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -26,13 +26,13 @@
#include <linux/tty_flip.h>
#include <linux/io.h>
#include <linux/of_platform.h>
+#include <linux/of_irq.h>
#include <linux/dma-mapping.h>
#include <linux/fs_uart_pd.h>
-#include <asm/ucc_slow.h>
+#include <linux/fsl/ucc_slow.h>
#include <linux/firmware.h>
-#include <asm/reg.h>
/*
* 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/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 <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
#include <asm/cpm.h>
#include <asm/dma.h>
#include <asm/reg.h>
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index c540dfa..ef4a00f 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -245,10 +245,10 @@ static int dr_controller_setup(struct fsl_udc *udc)
if (udc->pdata->have_sysif_regs) {
if (udc->pdata->controller_ver) {
/* controller version 1.6 or above */
- ctrl = __raw_readl(&usb_sys_regs->control);
+ ctrl = ioread32be(&usb_sys_regs->control);
ctrl &= ~USB_CTRL_UTMI_PHY_EN;
ctrl |= USB_CTRL_USB_EN;
- __raw_writel(ctrl, &usb_sys_regs->control);
+ iowrite32be(ctrl, &usb_sys_regs->control);
}
}
portctrl |= PORTSCX_PTS_ULPI;
@@ -260,10 +260,10 @@ static int dr_controller_setup(struct fsl_udc *udc)
if (udc->pdata->have_sysif_regs) {
if (udc->pdata->controller_ver) {
/* controller version 1.6 or above */
- ctrl = __raw_readl(&usb_sys_regs->control);
+ ctrl = ioread32be(&usb_sys_regs->control);
ctrl |= (USB_CTRL_UTMI_PHY_EN |
USB_CTRL_USB_EN);
- __raw_writel(ctrl, &usb_sys_regs->control);
+ iowrite32be(ctrl, &usb_sys_regs->control);
mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI
PHY CLK to become stable - 10ms*/
}
@@ -329,22 +329,22 @@ static int dr_controller_setup(struct fsl_udc *udc)
/* Config control enable i/o output, cpu endian register */
#ifndef CONFIG_ARCH_MXC
if (udc->pdata->have_sysif_regs) {
- ctrl = __raw_readl(&usb_sys_regs->control);
+ ctrl = ioread32be(&usb_sys_regs->control);
ctrl |= USB_CTRL_IOENB;
- __raw_writel(ctrl, &usb_sys_regs->control);
+ iowrite32be(ctrl, &usb_sys_regs->control);
}
#endif
-#if !defined(CONFIG_NOT_COHERENT_CACHE)
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
/* Turn on cache snooping hardware, since some PowerPC platforms
* wholly rely on hardware to deal with cache coherent. */
if (udc->pdata->have_sysif_regs) {
/* Setup Snooping for all the 4GB space */
tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
- __raw_writel(tmp, &usb_sys_regs->snoop1);
+ iowrite32be(tmp, &usb_sys_regs->snoop1);
tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
- __raw_writel(tmp, &usb_sys_regs->snoop2);
+ iowrite32be(tmp, &usb_sys_regs->snoop2);
}
#endif
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
index c6703bb..e4c4c01 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -17,6 +17,10 @@
#define USB_MAX_CTRL_PAYLOAD 64
#define USB_DR_SYS_OFFSET 0x400
+#ifdef CONFIG_SOC_LS1021A
+#undef CONFIG_ARCH_MXC
+#endif
+
/* USB DR device mode registers (Little Endian) */
struct usb_dr_device {
/* Capability register */
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index c307ab0..e6386c0 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -30,7 +30,10 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
+#include <linux/of_platform.h>
+#ifdef CONFIG_PPC
#include <sysdev/fsl_soc.h>
+#endif
#include "ehci-fsl.h"
@@ -58,7 +61,7 @@ static bool usb_phy_clk_valid(struct usb_hcd *hcd)
void __iomem *non_ehci = hcd->regs;
bool ret = true;
- if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID))
+ if (!(ioread32be(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID))
ret = false;
return ret;
@@ -194,12 +197,17 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6)
setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
+ /* Set USB_EN bit to select ULPI phy for USB controller version 2.5 */
+ if (pdata->controller_ver == FSL_USB_VER_2_5 &&
+ pdata->phy_mode == FSL_USB2_PHY_ULPI)
+ iowrite32be(USB_CTRL_USB_EN, hcd->regs + FSL_SOC_USB_CTRL);
+
/*
* Enable UTMI phy and program PTS field in UTMI mode before asserting
* controller reset for USB Controller version 2.5
*/
if (pdata->has_fsl_erratum_a007792) {
- writel_be(CTRL_UTMI_PHY_EN, hcd->regs + FSL_SOC_USB_CTRL);
+ setbits32(hcd->regs + FSL_SOC_USB_CTRL, CTRL_UTMI_PHY_EN);
writel(PORT_PTS_UTMI, hcd->regs + FSL_SOC_USB_PORTSC1);
}
@@ -378,9 +386,11 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
/* Setup Snooping for all the 4GB space */
/* SNOOP1 starts from 0x0, size 2G */
- out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0 | SNOOP_SIZE_2GB);
+ iowrite32be(0x0 | SNOOP_SIZE_2GB, non_ehci +
+ FSL_SOC_USB_SNOOP1);
/* SNOOP2 starts from 0x80000000, size 2G */
- out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
+ iowrite32be(0x80000000 | SNOOP_SIZE_2GB, non_ehci +
+ FSL_SOC_USB_SNOOP2);
}
/* Deal with USB erratum A-005275 */
@@ -396,14 +406,9 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
return -EINVAL;
if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
- unsigned int chip, rev, svr;
-
- svr = mfspr(SPRN_SVR);
- chip = svr >> 16;
- rev = (svr >> 4) & 0xf;
/* Deal with USB Erratum #14 on MPC834x Rev 1.0 & 1.1 chips */
- if ((rev == 1) && (chip >= 0x8050) && (chip <= 0x8055))
+ if (pdata->has_fsl_erratum_14 == 1)
ehci->has_fsl_port_bug = 1;
if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
@@ -417,13 +422,13 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
if (pdata->have_sysif_regs) {
#ifdef CONFIG_FSL_SOC_BOOKE
- out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
- out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
+ iowrite32be(0x00000008, non_ehci + FSL_SOC_USB_PRICTRL);
+ iowrite32be(0x00000080, non_ehci + FSL_SOC_USB_AGECNTTHRSH);
#else
- out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
- out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+ iowrite32be(0x0000000c, non_ehci + FSL_SOC_USB_PRICTRL);
+ iowrite32be(0x00000040, non_ehci + FSL_SOC_USB_AGECNTTHRSH);
#endif
- out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+ iowrite32be(0x00000040, non_ehci + FSL_SOC_USB_SICTRL);
}
return 0;
@@ -499,7 +504,7 @@ static int ehci_fsl_save_context(struct usb_hcd *hcd)
return -ENOMEM;
_memcpy_fromio(ehci_fsl->saved_regs, ehci->regs,
sizeof(struct ehci_regs));
- ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+ ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
return 0;
}
@@ -514,7 +519,7 @@ static int ehci_fsl_restore_context(struct usb_hcd *hcd)
if (ehci_fsl->saved_regs) {
_memcpy_toio(ehci->regs, ehci_fsl->saved_regs,
sizeof(struct ehci_regs));
- out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl);
+ iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
kfree(ehci_fsl->saved_regs);
ehci_fsl->saved_regs = NULL;
}
@@ -691,7 +696,9 @@ static int ehci_fsl_drv_suspend(struct device *dev)
return 0;
}
#endif
+#ifdef CONFIG_PPC
mpc85xx_pmc_set_wake(dev, true);
+#endif
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
device_may_wakeup(dev));
@@ -701,7 +708,7 @@ static int ehci_fsl_drv_suspend(struct device *dev)
if (!fsl_deep_sleep())
return 0;
- ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+ ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
return 0;
}
@@ -734,7 +741,9 @@ static int ehci_fsl_drv_resume(struct device *dev)
}
#endif
+#ifdef CONFIG_PPC
mpc85xx_pmc_set_wake(dev, false);
+#endif
ehci_prepare_ports_for_controller_resume(ehci);
if (!fsl_deep_sleep())
@@ -743,7 +752,7 @@ static int ehci_fsl_drv_resume(struct device *dev)
usb_root_hub_lost_power(hcd->self.root_hub);
/* Restore USB PHY settings and enable the controller. */
- out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl);
+ iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
ehci_reset(ehci);
ehci_fsl_reinit(ehci);
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 <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
#include <asm/fsl_gtm.h>
#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 <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/gpio.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
#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 <linux/io.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
-#include <asm/qe.h>
+#include <linux/fsl/qe.h>
#include <asm/fsl_gtm.h>
#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 <linux/io.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
-#include <asm/qe.h>
-#include <asm/immap_qe.h>
+#include <linux/fsl/qe.h>
+#include <linux/fsl/immap_qe.h>
#define USB_CLOCK 48000000
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index 8cb82cc..7640426 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -17,7 +17,7 @@
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/module.h>
-#include <asm/mpc85xx.h>
+#include <linux/dma-mapping.h>
struct fsl_usb2_dev_data {
char *dr_mode; /* controller mode */
@@ -95,7 +95,11 @@ static struct platform_device *fsl_usb2_device_register(
pdev->dev.parent = &ofdev->dev;
pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask;
- *pdev->dev.dma_mask = *ofdev->dev.dma_mask;
+
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask;
+ else
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
retval = platform_device_add_data(pdev, pdata, sizeof(*pdata));
if (retval)
@@ -120,92 +124,6 @@ error:
static const struct of_device_id fsl_usb2_mph_dr_of_match[];
-static bool has_erratum_a005275(struct device_node *node)
-{
- unsigned int svr = mfspr(SPRN_SVR);
- bool flag = false;
- /* Deal with USB Erratum USB A-005275
- * Packet corruption in HS mode, default to
- * FS mode for the following
- * P3041 and P2041 rev 1.0 and 1.1
- * P5020 and P5010 rev 1.0 and 2.0
- * P5040 and P1010 rev 1.0
- */
- if ((SVR_SOC_VER(svr) == SVR_P2041) ||
- (SVR_SOC_VER(svr) == SVR_P3041))
- flag = (SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x11);
- else if ((SVR_SOC_VER(svr) == SVR_P5010) ||
- (SVR_SOC_VER(svr) == SVR_P5020))
- flag = (SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20);
- else if ((SVR_SOC_VER(svr) == SVR_P5040) ||
- (SVR_SOC_VER(svr) == SVR_P1010))
- flag = (SVR_REV(svr) == 0x10);
-
- return flag;
-}
-
-static bool has_erratum_a005697(void)
-{
- unsigned int svr = mfspr(SPRN_SVR);
- bool flag = false;
-
- switch (SVR_SOC_VER(svr)) {
- case SVR_P1014:
- case SVR_T1040:
- case SVR_T2080:
- case SVR_T2081:
- if (SVR_REV(svr) == 0x10)
- flag = true;
- break;
- case SVR_9132:
- if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x11))
- flag = true;
- break;
- case SVR_P5040:
- case SVR_P5021:
- if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20) ||
- (SVR_REV(svr) == 0x21))
- flag = true;
- break;
- case SVR_P1010:
- case SVR_T4240:
- case SVR_T4160:
- case SVR_P5020:
- case SVR_P5010:
- if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20))
- flag = true;
- break;
- case SVR_P2040:
- case SVR_P2041:
- case SVR_P3041:
- if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x11) ||
- (SVR_REV(svr) == 0x20))
- flag = true;
- break;
- case SVR_P4080:
- if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20) ||
- (SVR_REV(svr) == 0x30))
- flag = true;
- break;
- case SVR_B4860:
- case SVR_B4420:
- if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20) ||
- (SVR_REV(svr) == 0x21) || (SVR_REV(svr) == 0x22))
- flag = true;
- break;
- }
-
- return flag;
-}
-
-static bool has_erratum_a007792(int controller_ver)
-{
- if (controller_ver == FSL_USB_VER_2_5)
- return true;
- else
- return false;
-}
-
static int usb_get_ver_info(struct device_node *np)
{
int ver = -1;
@@ -299,31 +217,35 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
pdata->phy_mode = determine_usb_phy(prop);
pdata->controller_ver = usb_get_ver_info(np);
- /* Activate workaround for USB erratum-A005275 if
- * fsl,no-erratum-a005275 property not defined for
+ /* Activate workaround for USB erratum-A00XXXX if
+ * fsl,erratum-a00XXXX property is defined for
* affected socs
*/
- if (!of_get_property(np, "fsl,no-erratum-a005275", NULL) &&
- has_erratum_a005275(np))
+ if (of_get_property(np, "fsl,usb_erratum-a005275", NULL))
pdata->has_fsl_erratum_a005275 = 1;
else
pdata->has_fsl_erratum_a005275 = 0;
- if (has_erratum_a005697())
+ if (of_get_property(np, "fsl,usb_erratum-a005697", NULL))
pdata->has_fsl_erratum_a005697 = 1;
else
pdata->has_fsl_erratum_a005697 = 0;
- if (has_erratum_a007792(pdata->controller_ver))
+ if (of_get_property(np, "fsl,usb_erratum-a007792", NULL))
pdata->has_fsl_erratum_a007792 = 1;
else
pdata->has_fsl_erratum_a007792 = 0;
- if (of_get_property(np, "fsl,erratum_a006918", NULL))
+ if (of_get_property(np, "fsl,usb_erratum_a006918", NULL))
pdata->has_fsl_erratum_a006918 = 1;
else
pdata->has_fsl_erratum_a006918 = 0;
+ if (of_get_property(np, "fsl,usb_erratum_14", NULL))
+ pdata->has_fsl_erratum_14 = 1;
+ else
+ pdata->has_fsl_erratum_14 = 0;
+
if (pdata->have_sysif_regs) {
if (pdata->controller_ver < 0) {
dev_warn(&ofdev->dev, "Could not get controller version\n");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 2a46153..76eff63 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1965,6 +1965,39 @@ config FB_MBX_DEBUG
If unsure, say N.
+config FB_FSL_SII902X
+ tristate "Si Image SII9022 DVI/HDMI Interface Chip"
+ depends on FB
+ select FB_MODE_HELPERS
+ ---help---
+ Driver for the SII9022 DVI/HDMI controller.
+
+ 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
+ <file:Documentation/kbuild/modules.txt>.
+
+ 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
+ select REGMAP_MMIO
+ ---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
+ <file:Documentation/kbuild/modules.txt>.
+
+ If unsure, say N.
+
config FB_FSL_DIU
tristate "Freescale DIU framebuffer support"
depends on FB && FSL_SOC
@@ -1972,7 +2005,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/drivers/video/Makefile b/drivers/video/Makefile
index f5ef0ed..3535ed9 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -129,6 +129,8 @@ 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_SII902X) += fsl-sii902x.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..3fc0b79
--- /dev/null
+++ b/drivers/video/fsl-dcu-fb.c
@@ -0,0 +1,1258 @@
+/*
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/uaccess.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+
+#define DRIVER_NAME "fsl-dcu-fb"
+
+#define DCU_DCU_MODE 0x10
+#define DCU_MODE_BLEND_ITER(x) ((x) << 20)
+#define DCU_MODE_RASTER_EN BIT(14)
+#define DCU_MODE_DCU_MODE(x) (x)
+#define DCU_MODE_DCU_MODE_MASK 0x03
+#define DCU_MODE_OFF 0
+#define DCU_MODE_NORMAL 1
+#define DCU_MODE_TEST 2
+#define DCU_MODE_COLORBAR 3
+
+#define DCU_BGND 0x14
+#define DCU_BGND_R(x) ((x) << 16)
+#define DCU_BGND_G(x) ((x) << 8)
+#define DCU_BGND_B(x) (x)
+
+#define DCU_DISP_SIZE 0x18
+#define DCU_DISP_SIZE_DELTA_Y(x) ((x) << 16)
+#define DCU_DISP_SIZE_DELTA_X(x) (x)
+
+#define DCU_HSYN_PARA 0x1c
+#define DCU_HSYN_PARA_BP(x) ((x) << 22)
+#define DCU_HSYN_PARA_PW(x) ((x) << 11)
+#define DCU_HSYN_PARA_FP(x) (x)
+
+#define DCU_VSYN_PARA 0x20
+#define DCU_VSYN_PARA_BP(x) ((x) << 22)
+#define DCU_VSYN_PARA_PW(x) ((x) << 11)
+#define DCU_VSYN_PARA_FP(x) (x)
+
+#define DCU_SYN_POL 0x24
+#define DCU_SYN_POL_INV_PXCK_FALL (0 << 6)
+#define DCU_SYN_POL_NEG_REMAIN (0 << 5)
+#define DCU_SYN_POL_INV_VS_LOW BIT(1)
+#define DCU_SYN_POL_INV_HS_LOW BIT(0)
+
+#define DCU_THRESHOLD 0x28
+#define DCU_THRESHOLD_LS_BF_VS(x) ((x) << 16)
+#define DCU_THRESHOLD_OUT_BUF_HIGH(x) ((x) << 8)
+#define DCU_THRESHOLD_OUT_BUF_LOW(x) (x)
+
+#define DCU_INT_STATUS 0x2C
+#define DCU_INT_STATUS_UNDRUN BIT(1)
+
+#define DCU_INT_MASK 0x30
+#define DCU_INT_MASK_UNDRUN BIT(1)
+#define DCU_INT_MASK_ALL 0xffffffff
+
+#define DCU_DIV_RATIO 0x54
+
+#define DCU_UPDATE_MODE 0xcc
+#define DCU_UPDATE_MODE_MODE BIT(31)
+#define DCU_UPDATE_MODE_READREG BIT(30)
+
+#define DCU_CTRLDESCLN_1(x) (0x200 + (x) * 0x40)
+#define DCU_CTRLDESCLN_1_HEIGHT(x) ((x) << 16)
+#define DCU_CTRLDESCLN_1_WIDTH(x) (x)
+
+#define DCU_CTRLDESCLN_2(x) (0x204 + (x) * 0x40)
+#define DCU_CTRLDESCLN_2_POSY(x) ((x) << 16)
+#define DCU_CTRLDESCLN_2_POSX(x) (x)
+
+#define DCU_CTRLDESCLN_3(x) (0x208 + (x) * 0x40)
+
+#define DCU_CTRLDESCLN_4(x) (0x20c + (x) * 0x40)
+#define DCU_CTRLDESCLN_4_EN BIT(31)
+#define DCU_CTRLDESCLN_4_TILE_EN BIT(30)
+#define DCU_CTRLDESCLN_4_DATA_SEL_CLUT BIT(29)
+#define DCU_CTRLDESCLN_4_SAFETY_EN BIT(28)
+#define DCU_CTRLDESCLN_4_TRANS(x) ((x) << 20)
+#define DCU_CTRLDESCLN_4_BPP(x) ((x) << 16)
+#define DCU_CTRLDESCLN_4_RLE_EN BIT(15)
+#define DCU_CTRLDESCLN_4_LUOFFS(x) ((x) << 4)
+#define DCU_CTRLDESCLN_4_BB_ON BIT(2)
+#define DCU_CTRLDESCLN_4_AB(x) (x)
+
+#define DCU_CTRLDESCLN_5(x) (0x210 + (x) * 0x40)
+#define DCU_CTRLDESCLN_5_CKMAX_R(x) ((x) << 16)
+#define DCU_CTRLDESCLN_5_CKMAX_G(x) ((x) << 8)
+#define DCU_CTRLDESCLN_5_CKMAX_B(x) (x)
+
+#define DCU_CTRLDESCLN_6(x) (0x214 + (x) * 0x40)
+#define DCU_CTRLDESCLN_6_CKMIN_R(x) ((x) << 16)
+#define DCU_CTRLDESCLN_6_CKMIN_G(x) ((x) << 8)
+#define DCU_CTRLDESCLN_6_CKMIN_B(x) (x)
+
+#define DCU_CTRLDESCLN_7(x) (0x218 + (x) * 0x40)
+#define DCU_CTRLDESCLN_7_TILE_VER(x) ((x) << 16)
+#define DCU_CTRLDESCLN_7_TILE_HOR(x) (x)
+
+#define DCU_CTRLDESCLN_8(x) (0x21c + (x) * 0x40)
+#define DCU_CTRLDESCLN_8_FG_FCOLOR(x) (x)
+
+#define DCU_CTRLDESCLN_9(x) (0x220 + (x) * 0x40)
+#define DCU_CTRLDESCLN_9_BG_BCOLOR(x) (x)
+
+#define DCU_CTRLDESCLN_10(x) (0x224 + (x) * 0x40)
+
+#ifdef CONFIG_SOC_VF610
+#define DCU_TOTAL_LAYER_NUM 64
+#define DCU_LAYER_NUM_MAX 6
+#else
+#define DCU_TOTAL_LAYER_NUM 16
+#define DCU_LAYER_NUM_MAX 4
+#endif
+
+#define BPP_16_RGB565 4
+#define BPP_24_RGB888 5
+#define BPP_32_ARGB8888 6
+
+#define TCON_CTRL1 0x00
+#define TCON_BYPASS_ENABLE BIT(29)
+
+#define MFB_SET_ALPHA _IOW('M', 0, __u8)
+#define MFB_GET_ALPHA _IOR('M', 0, __u8)
+#define MFB_SET_LAYER _IOW('M', 4, struct layer_display_offset)
+#define MFB_GET_LAYER _IOR('M', 4, struct layer_display_offset)
+
+struct dcu_fb_data {
+ struct fb_info *fsl_dcu_info[DCU_LAYER_NUM_MAX];
+ struct device *dev;
+ struct regmap *regmap;
+ struct regmap *tcon_regmap;
+ struct regmap *scfg_regmap;
+ unsigned int irq;
+ struct clk *clk;
+ struct clk *tcon_clk;
+};
+
+struct layer_display_offset {
+ int x_layer_d;
+ int y_layer_d;
+};
+
+struct mfb_info {
+ int index;
+ char *id;
+ unsigned long pseudo_palette[16];
+ unsigned char alpha;
+ unsigned char blend;
+ unsigned int count;
+ int x_layer_d; /* layer display x offset to physical screen */
+ int y_layer_d; /* layer display y offset to physical screen */
+ struct dcu_fb_data *parent;
+};
+
+enum mfb_index {
+ LAYER0 = 0,
+ LAYER1,
+ LAYER2,
+ LAYER3,
+ LAYER4,
+ LAYER5,
+};
+
+static struct mfb_info mfb_template[] = {
+ {
+ .index = LAYER0,
+ .id = "Layer0",
+ .alpha = 0xff,
+ .blend = 0,
+ .count = 0,
+ .x_layer_d = 0,
+ .y_layer_d = 0,
+ },
+ {
+ .index = LAYER1,
+ .id = "Layer1",
+ .alpha = 0xff,
+ .blend = 0,
+ .count = 0,
+ .x_layer_d = 50,
+ .y_layer_d = 50,
+ },
+ {
+ .index = LAYER2,
+ .id = "Layer2",
+ .alpha = 0xff,
+ .blend = 0,
+ .count = 0,
+ .x_layer_d = 100,
+ .y_layer_d = 100,
+ },
+ {
+ .index = LAYER3,
+ .id = "Layer3",
+ .alpha = 0xff,
+ .blend = 0,
+ .count = 0,
+ .x_layer_d = 150,
+ .y_layer_d = 150,
+ },
+ {
+ .index = LAYER4,
+ .id = "Layer4",
+ .alpha = 0xff,
+ .blend = 0,
+ .count = 0,
+ .x_layer_d = 200,
+ .y_layer_d = 200,
+ },
+ {
+ .index = LAYER5,
+ .id = "Layer5",
+ .alpha = 0xff,
+ .blend = 0,
+ .count = 0,
+ .x_layer_d = 250,
+ .y_layer_d = 250,
+ },
+};
+
+static void reset_total_layers(struct device_node *np,
+ struct dcu_fb_data *dcufb)
+{
+ int i;
+
+ for (i = 0; i < DCU_TOTAL_LAYER_NUM; i++) {
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_1(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_2(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_4(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_5(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_6(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_7(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_8(i), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_9(i), 0);
+ if (of_device_is_compatible(np, "fsl,ls1021a-dcu"))
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_10(i), 0);
+ }
+
+ regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
+}
+
+static int enable_panel(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ unsigned int bpp;
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_1(mfbi->index),
+ DCU_CTRLDESCLN_1_HEIGHT(var->yres) |
+ DCU_CTRLDESCLN_1_WIDTH(var->xres));
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_2(mfbi->index),
+ DCU_CTRLDESCLN_2_POSY(mfbi->y_layer_d) |
+ DCU_CTRLDESCLN_2_POSX(mfbi->x_layer_d));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(mfbi->index),
+ info->fix.smem_start);
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ bpp = BPP_16_RGB565;
+ break;
+ case 24:
+ bpp = BPP_24_RGB888;
+ break;
+ case 32:
+ bpp = BPP_32_ARGB8888;
+ break;
+ default:
+ dev_err(dcufb->dev, "unsupported color depth: %u\n",
+ var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_4(mfbi->index),
+ DCU_CTRLDESCLN_4_EN |
+ DCU_CTRLDESCLN_4_TRANS(mfbi->alpha) |
+ DCU_CTRLDESCLN_4_BPP(bpp) |
+ DCU_CTRLDESCLN_4_AB(mfbi->blend));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_5(mfbi->index),
+ DCU_CTRLDESCLN_5_CKMAX_R(0xff) |
+ DCU_CTRLDESCLN_5_CKMAX_G(0xff) |
+ DCU_CTRLDESCLN_5_CKMAX_B(0xff));
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_6(mfbi->index),
+ DCU_CTRLDESCLN_6_CKMIN_R(0) |
+ DCU_CTRLDESCLN_6_CKMIN_G(0) |
+ DCU_CTRLDESCLN_6_CKMIN_B(0));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_7(mfbi->index),
+ DCU_CTRLDESCLN_7_TILE_VER(0) |
+ DCU_CTRLDESCLN_7_TILE_HOR(0));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_8(mfbi->index),
+ DCU_CTRLDESCLN_8_FG_FCOLOR(0));
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_9(mfbi->index),
+ DCU_CTRLDESCLN_9_BG_BCOLOR(0));
+
+ regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
+
+ return 0;
+}
+
+static int disable_panel(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_1(mfbi->index),
+ DCU_CTRLDESCLN_1_HEIGHT(0) |
+ DCU_CTRLDESCLN_1_WIDTH(0));
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_2(mfbi->index),
+ DCU_CTRLDESCLN_2_POSY(0) |
+ DCU_CTRLDESCLN_2_POSX(0));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(mfbi->index), 0);
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_4(mfbi->index), 0);
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_5(mfbi->index),
+ DCU_CTRLDESCLN_5_CKMAX_R(0) |
+ DCU_CTRLDESCLN_5_CKMAX_G(0) |
+ DCU_CTRLDESCLN_5_CKMAX_B(0));
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_6(mfbi->index),
+ DCU_CTRLDESCLN_6_CKMIN_R(0) |
+ DCU_CTRLDESCLN_6_CKMIN_G(0) |
+ DCU_CTRLDESCLN_6_CKMIN_B(0));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_7(mfbi->index),
+ DCU_CTRLDESCLN_7_TILE_VER(0) |
+ DCU_CTRLDESCLN_7_TILE_HOR(0));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_8(mfbi->index),
+ DCU_CTRLDESCLN_8_FG_FCOLOR(0));
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_9(mfbi->index),
+ DCU_CTRLDESCLN_9_BG_BCOLOR(0));
+
+ regmap_write(dcufb->regmap, DCU_UPDATE_MODE,
+ DCU_UPDATE_MODE_READREG);
+ return 0;
+}
+
+static void enable_controller(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+
+ regmap_update_bits(dcufb->regmap, DCU_DCU_MODE,
+ DCU_MODE_DCU_MODE_MASK,
+ DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
+}
+
+static void disable_controller(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+
+ regmap_update_bits(dcufb->regmap, DCU_DCU_MODE,
+ DCU_MODE_DCU_MODE_MASK,
+ DCU_MODE_DCU_MODE(DCU_MODE_OFF));
+}
+
+static int fsl_dcu_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (var->xoffset + info->var.xres > info->var.xres_virtual)
+ var->xoffset = info->var.xres_virtual - info->var.xres;
+
+ if (var->yoffset + info->var.yres > info->var.yres_virtual)
+ var->yoffset = info->var.yres_virtual - info->var.yres;
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ default:
+ dev_err(dcufb->dev, "unsupported color depth: %u\n",
+ var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_dcu_calc_div(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ unsigned long long div;
+
+ div = (unsigned long long)(clk_get_rate(dcufb->clk) / 1000);
+ div *= info->var.pixclock;
+ do_div(div, 1000000000);
+
+ return div;
+}
+
+static void update_controller(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ unsigned int div;
+
+ div = fsl_dcu_calc_div(info);
+ regmap_write(dcufb->regmap, DCU_DIV_RATIO, div);
+
+ regmap_write(dcufb->regmap, DCU_DISP_SIZE,
+ DCU_DISP_SIZE_DELTA_Y(var->yres) |
+ DCU_DISP_SIZE_DELTA_X(var->xres / 16));
+
+ /* Horizontal and vertical sync parameters */
+ regmap_write(dcufb->regmap, DCU_HSYN_PARA,
+ DCU_HSYN_PARA_BP(var->left_margin) |
+ DCU_HSYN_PARA_PW(var->hsync_len) |
+ DCU_HSYN_PARA_FP(var->right_margin));
+
+ regmap_write(dcufb->regmap, DCU_VSYN_PARA,
+ DCU_VSYN_PARA_BP(var->upper_margin) |
+ DCU_VSYN_PARA_PW(var->vsync_len) |
+ DCU_VSYN_PARA_FP(var->lower_margin));
+
+ regmap_write(dcufb->regmap, DCU_SYN_POL,
+ DCU_SYN_POL_INV_PXCK_FALL | DCU_SYN_POL_NEG_REMAIN |
+ DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW);
+
+ regmap_write(dcufb->regmap, DCU_BGND, DCU_BGND_R(0) |
+ DCU_BGND_G(0) | DCU_BGND_B(0));
+
+ regmap_write(dcufb->regmap, DCU_DCU_MODE,
+ DCU_MODE_BLEND_ITER(DCU_LAYER_NUM_MAX) |
+ DCU_MODE_RASTER_EN);
+
+ regmap_write(dcufb->regmap, DCU_THRESHOLD,
+ DCU_THRESHOLD_LS_BF_VS(0x3) |
+ DCU_THRESHOLD_OUT_BUF_HIGH(0x78) |
+ DCU_THRESHOLD_OUT_BUF_LOW(0xa));
+
+ regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
+}
+
+static int map_video_memory(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ u32 smem_len = info->fix.line_length * info->var.yres_virtual;
+
+ info->fix.smem_len = smem_len;
+
+ info->screen_base = dma_alloc_writecombine(info->device,
+ info->fix.smem_len, (dma_addr_t *)&info->fix.smem_start,
+ GFP_KERNEL);
+ if (!info->screen_base) {
+ dev_err(dcufb->dev, "unable to allocate fb memory\n");
+ return -ENOMEM;
+ }
+
+ memset(info->screen_base, 0, info->fix.smem_len);
+
+ return 0;
+}
+
+static void unmap_video_memory(struct fb_info *info)
+{
+ if (!info->screen_base)
+ return;
+
+ dma_free_writecombine(info->device, info->fix.smem_len,
+ info->screen_base, info->fix.smem_start);
+
+ info->screen_base = NULL;
+ info->fix.smem_start = 0;
+ info->fix.smem_len = 0;
+}
+
+static int fsl_dcu_set_layer(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ int pixel_offset;
+ unsigned long addr;
+
+ pixel_offset = (var->yoffset * var->xres_virtual) + var->xoffset;
+ addr = info->fix.smem_start +
+ (pixel_offset * (var->bits_per_pixel >> 3));
+
+ regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(mfbi->index), addr);
+ regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
+
+ return 0;
+}
+
+static int fsl_dcu_set_par(struct fb_info *info)
+{
+ unsigned long len;
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ len = info->var.yres_virtual * info->fix.line_length;
+ if (len != info->fix.smem_len) {
+ if (info->fix.smem_start)
+ unmap_video_memory(info);
+
+ if (map_video_memory(info)) {
+ dev_err(dcufb->dev, "unable to allocate fb memory\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* Only layer 0 could update LCD controller */
+ if (mfbi->index == LAYER0) {
+ update_controller(info);
+ enable_controller(info);
+ }
+
+ enable_panel(info);
+ return 0;
+}
+
+static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
+{
+ return ((val << width) + 0x7FFF - val) >> 16;
+}
+
+static int fsl_dcu_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ unsigned int val;
+ int ret = -EINVAL;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+
+ val = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+static int fsl_dcu_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if ((info->var.xoffset == var->xoffset) &&
+ (info->var.yoffset == var->yoffset))
+ return 0;
+
+ if ((var->xoffset + info->var.xres) > info->var.xres_virtual
+ || (var->yoffset + info->var.yres) > info->var.yres_virtual)
+ return -EINVAL;
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ fsl_dcu_set_layer(info);
+
+ return 0;
+}
+
+static int fsl_dcu_blank(int blank_mode, struct fb_info *info)
+{
+ switch (blank_mode) {
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ disable_panel(info);
+ break;
+ case FB_BLANK_POWERDOWN:
+ disable_controller(info);
+ break;
+ case FB_BLANK_UNBLANK:
+ enable_panel(info);
+ break;
+ }
+
+ return 0;
+}
+
+static int fsl_dcu_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ struct layer_display_offset layer_d;
+ void __user *buf = (void __user *)arg;
+ unsigned char alpha;
+
+ switch (cmd) {
+ case MFB_SET_LAYER:
+ if (copy_from_user(&layer_d, buf, sizeof(layer_d)))
+ return -EFAULT;
+ mfbi->x_layer_d = layer_d.x_layer_d;
+ mfbi->y_layer_d = layer_d.y_layer_d;
+ fsl_dcu_set_par(info);
+ break;
+ case MFB_GET_LAYER:
+ layer_d.x_layer_d = mfbi->x_layer_d;
+ layer_d.y_layer_d = mfbi->y_layer_d;
+ if (copy_to_user(buf, &layer_d, sizeof(layer_d)))
+ return -EFAULT;
+ break;
+ case MFB_GET_ALPHA:
+ alpha = mfbi->alpha;
+ if (copy_to_user(buf, &alpha, sizeof(alpha)))
+ return -EFAULT;
+ break;
+ case MFB_SET_ALPHA:
+ if (copy_from_user(&alpha, buf, sizeof(alpha)))
+ return -EFAULT;
+ mfbi->blend = 1;
+ mfbi->alpha = alpha;
+ fsl_dcu_set_par(info);
+ break;
+ default:
+ dev_err(dcufb->dev, "unknown ioctl command (0x%08X)\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int fsl_dcu_open(struct fb_info *info, int user)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ int ret = 0;
+
+ mfbi->index = info->node;
+
+ mfbi->count++;
+ if (mfbi->count == 1) {
+ fsl_dcu_check_var(&info->var, info);
+ ret = fsl_dcu_set_par(info);
+ if (ret < 0)
+ mfbi->count--;
+ else
+ regmap_update_bits(dcufb->regmap, DCU_INT_MASK,
+ DCU_INT_MASK_ALL, DCU_INT_MASK_ALL);
+ }
+
+ return ret;
+}
+
+static int fsl_dcu_release(struct fb_info *info, int user)
+{
+ struct mfb_info *mfbi = info->par;
+ int ret = 0;
+
+ mfbi->count--;
+ if (mfbi->count == 0)
+ ret = disable_panel(info);
+
+ return ret;
+}
+
+static struct fb_ops fsl_dcu_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = fsl_dcu_check_var,
+ .fb_set_par = fsl_dcu_set_par,
+ .fb_setcolreg = fsl_dcu_setcolreg,
+ .fb_blank = fsl_dcu_blank,
+ .fb_pan_display = fsl_dcu_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_ioctl = fsl_dcu_ioctl,
+ .fb_open = fsl_dcu_open,
+ .fb_release = fsl_dcu_release,
+};
+
+static int fsl_dcu_init_fbinfo(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ struct device_node *np = dcufb->dev->of_node;
+ struct device_node *display_np;
+ struct device_node *timings_np;
+ struct display_timings *timings;
+ int i;
+ int ret = 0;
+
+ display_np = of_parse_phandle(np, "display", 0);
+ if (!display_np) {
+ dev_err(dcufb->dev, "failed to find display phandle\n");
+ return -ENOENT;
+ }
+
+ ret = of_property_read_u32(display_np, "bits-per-pixel",
+ &var->bits_per_pixel);
+ if (ret < 0) {
+ dev_err(dcufb->dev, "failed to get property bits-per-pixel\n");
+ goto put_display_node;
+ }
+
+ timings = of_get_display_timings(display_np);
+ if (!timings) {
+ dev_err(dcufb->dev, "failed to get display timings\n");
+ ret = -ENOENT;
+ goto put_display_node;
+ }
+
+ timings_np = of_find_node_by_name(display_np,
+ "display-timings");
+ if (!timings_np) {
+ dev_err(dcufb->dev, "failed to find display-timings node\n");
+ ret = -ENOENT;
+ goto put_display_node;
+ }
+
+ for (i = 0; i < of_get_child_count(timings_np); i++) {
+ struct videomode vm;
+ struct fb_videomode fb_vm;
+
+ ret = videomode_from_timings(timings, &vm, i);
+ if (ret < 0)
+ goto put_timings_node;
+
+ ret = fb_videomode_from_videomode(&vm, &fb_vm);
+ if (ret < 0)
+ goto put_timings_node;
+
+ fb_add_videomode(&fb_vm, &info->modelist);
+ }
+
+ return 0;
+put_timings_node:
+ of_node_put(timings_np);
+put_display_node:
+ of_node_put(display_np);
+ return ret;
+}
+
+static int install_framebuffer(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct dcu_fb_data *dcufb = mfbi->parent;
+ struct fb_modelist *modelist;
+ int ret;
+
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->fbops = &fsl_dcu_ops;
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->pseudo_palette = &mfbi->pseudo_palette;
+
+ fb_alloc_cmap(&info->cmap, 16, 0);
+
+ INIT_LIST_HEAD(&info->modelist);
+
+ ret = fsl_dcu_init_fbinfo(info);
+ if (ret)
+ return ret;
+
+ modelist = list_first_entry(&info->modelist,
+ struct fb_modelist, list);
+ fb_videomode_to_var(&info->var, &modelist->mode);
+
+ fsl_dcu_check_var(&info->var, info);
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(dcufb->dev, "failed to register framebuffer device\n");
+ return ret;
+ }
+
+ pr_info("fb%d: fb device registered successfully.\n", info->node);
+ return 0;
+}
+
+static void uninstall_framebuffer(struct fb_info *info)
+{
+ unregister_framebuffer(info);
+ unmap_video_memory(info);
+
+ if (&info->cmap)
+ fb_dealloc_cmap(&info->cmap);
+}
+
+static irqreturn_t fsl_dcu_irq(int irq, void *dev_id)
+{
+ struct dcu_fb_data *dcufb = dev_id;
+ unsigned int status;
+
+ regmap_read(dcufb->regmap, DCU_INT_STATUS, &status);
+
+ if (status & DCU_INT_STATUS_UNDRUN) {
+ regmap_update_bits(dcufb->regmap, DCU_DCU_MODE,
+ DCU_MODE_DCU_MODE_MASK,
+ DCU_MODE_DCU_MODE(DCU_MODE_OFF));
+ udelay(1);
+ regmap_update_bits(dcufb->regmap, DCU_DCU_MODE,
+ DCU_MODE_DCU_MODE_MASK,
+ DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
+ }
+
+ regmap_write(dcufb->regmap, DCU_INT_STATUS, status);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config fsl_tcon_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = TCON_CTRL1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int bypass_tcon(struct dcu_fb_data *dcufb, struct device_node *np)
+{
+ struct device_node *tcon_np;
+ struct platform_device *pdev;
+ struct resource *res;
+ void __iomem *base;
+
+ tcon_np = of_parse_phandle(np, "tcon-controller", 0);
+ if (!tcon_np)
+ return 0;
+
+ pdev = of_find_device_by_node(tcon_np);
+ if (!pdev)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "could not ioremap resource\n");
+ return PTR_ERR(base);
+ }
+
+ dcufb->tcon_clk = devm_clk_get(&pdev->dev, "tcon");
+ if (IS_ERR(dcufb->tcon_clk))
+ return PTR_ERR(dcufb->tcon_clk);
+ clk_prepare_enable(dcufb->tcon_clk);
+
+ dcufb->tcon_regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "tcon", base, &fsl_tcon_regmap_config);
+ if (IS_ERR(dcufb->tcon_regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(dcufb->tcon_regmap);
+ }
+
+ regmap_write(dcufb->tcon_regmap, TCON_CTRL1, TCON_BYPASS_ENABLE);
+
+ return 0;
+}
+
+static const struct regmap_config fsl_scfg_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = 0x28,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int scfg_config(struct dcu_fb_data *dcufb, struct device_node *np)
+{
+ struct device_node *scfg_np;
+ struct platform_device *pdev;
+ struct resource *res;
+ void __iomem *base;
+
+ scfg_np = of_parse_phandle(np, "scfg-controller", 0);
+ if (!scfg_np)
+ return 0;
+
+ pdev = of_find_device_by_node(scfg_np);
+ if (!pdev)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "could not ioremap resource\n");
+ return PTR_ERR(base);
+ }
+
+ dcufb->scfg_regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ NULL, base, &fsl_scfg_regmap_config);
+ if (IS_ERR(dcufb->scfg_regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(dcufb->scfg_regmap);
+ }
+
+ regmap_write(dcufb->scfg_regmap, 0x028, 0x80000000);
+
+ return 0;
+}
+
+static const struct regmap_config fsl_dcu_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = 0x5e8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int fsl_dcu_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct dcu_fb_data *dcufb;
+ struct mfb_info *mfbi;
+ struct resource *res;
+ void __iomem *base;
+ int ret = 0;
+ int i;
+
+ dcufb = devm_kzalloc(&pdev->dev,
+ sizeof(struct dcu_fb_data), GFP_KERNEL);
+ if (!dcufb)
+ return -ENOMEM;
+
+ dcufb->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, dcufb);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "could not get memory IO resource\n");
+ return -ENODEV;
+ }
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "could not ioremap resource\n");
+ return PTR_ERR(base);
+ }
+
+ dcufb->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ NULL, base, &fsl_dcu_regmap_config);
+ if (IS_ERR(dcufb->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(dcufb->regmap);
+ }
+
+ dcufb->irq = platform_get_irq(pdev, 0);
+ if (!dcufb->irq) {
+ dev_err(&pdev->dev, "could not get irq\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, dcufb->irq, fsl_dcu_irq,
+ 0, DRIVER_NAME, dcufb);
+ if (ret) {
+ dev_err(&pdev->dev, "could not request irq\n");
+ return -EINVAL;
+ }
+
+ /* Put TCON in bypass mode, so the input signals from DCU are passed
+ * through TCON unchanged */
+ ret = bypass_tcon(dcufb, np);
+ if (ret) {
+ dev_err(&pdev->dev, "could not bypass TCON\n");
+ return -EINVAL;
+ }
+
+ ret = scfg_config(dcufb, np);
+ if (ret) {
+ dev_err(&pdev->dev, "could not config scfg\n");
+ return -EINVAL;
+ }
+
+ dcufb->clk = devm_clk_get(&pdev->dev, "dcu");
+ if (IS_ERR(dcufb->clk)) {
+ ret = PTR_ERR(dcufb->clk);
+ dev_err(&pdev->dev, "could not get clock\n");
+ return -EINVAL;
+ }
+ clk_prepare_enable(dcufb->clk);
+
+ pm_runtime_enable(dcufb->dev);
+ pm_runtime_get_sync(dcufb->dev);
+
+ reset_total_layers(np, dcufb);
+
+ for (i = 0; i < ARRAY_SIZE(dcufb->fsl_dcu_info); i++) {
+ dcufb->fsl_dcu_info[i] =
+ framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev);
+ if (!dcufb->fsl_dcu_info[i]) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dcufb->fsl_dcu_info[i]->fix.smem_start = 0;
+
+ mfbi = dcufb->fsl_dcu_info[i]->par;
+ memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
+ mfbi->parent = dcufb;
+
+ ret = install_framebuffer(dcufb->fsl_dcu_info[i]);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "could not register framebuffer %d\n", i);
+ goto err;
+ }
+ }
+
+ goto out;
+err:
+ for (i = 0; i < ARRAY_SIZE(dcufb->fsl_dcu_info); i++) {
+ if (dcufb->fsl_dcu_info[i])
+ framebuffer_release(dcufb->fsl_dcu_info[i]);
+ }
+
+ clk_disable_unprepare(dcufb->clk);
+out:
+ pm_runtime_put_sync(dcufb->dev);
+ pm_runtime_disable(dcufb->dev);
+ return ret;
+}
+
+static int fsl_dcu_remove(struct platform_device *pdev)
+{
+ struct dcu_fb_data *dcufb = dev_get_drvdata(&pdev->dev);
+ int i;
+
+ pm_runtime_get_sync(dcufb->dev);
+
+ disable_controller(dcufb->fsl_dcu_info[0]);
+
+ clk_disable_unprepare(dcufb->tcon_clk);
+ clk_disable_unprepare(dcufb->clk);
+ free_irq(dcufb->irq, dcufb);
+
+ for (i = 0; i < ARRAY_SIZE(dcufb->fsl_dcu_info); i++) {
+ uninstall_framebuffer(dcufb->fsl_dcu_info[i]);
+ framebuffer_release(dcufb->fsl_dcu_info[i]);
+ }
+
+ pm_runtime_put_sync(dcufb->dev);
+ pm_runtime_disable(dcufb->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int fsl_dcu_runtime_suspend(struct device *dev)
+{
+ struct dcu_fb_data *dcufb = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(dcufb->clk);
+ clk_disable_unprepare(dcufb->tcon_clk);
+
+ return 0;
+}
+
+static int fsl_dcu_runtime_resume(struct device *dev)
+{
+ struct dcu_fb_data *dcufb = dev_get_drvdata(dev);
+
+ clk_prepare_enable(dcufb->tcon_clk);
+ clk_prepare_enable(dcufb->clk);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_dcu_suspend(struct device *dev)
+{
+ struct dcu_fb_data *dcufb = dev_get_drvdata(dev);
+
+ regcache_cache_only(dcufb->regmap, true);
+ regcache_mark_dirty(dcufb->regmap);
+ clk_disable_unprepare(dcufb->clk);
+
+ if (dcufb->tcon_regmap) {
+ regcache_cache_only(dcufb->tcon_regmap, true);
+ regcache_mark_dirty(dcufb->tcon_regmap);
+ clk_disable_unprepare(dcufb->tcon_clk);
+ }
+
+ if (dcufb->scfg_regmap) {
+ regcache_cache_only(dcufb->scfg_regmap, true);
+ regcache_mark_dirty(dcufb->scfg_regmap);
+ }
+
+ return 0;
+}
+
+static int fsl_dcu_resume(struct device *dev)
+{
+ struct dcu_fb_data *dcufb = dev_get_drvdata(dev);
+ struct device_node *np = dev->of_node;
+
+ /* Enable clocks and restore all registers */
+ if (dcufb->tcon_regmap) {
+ clk_prepare_enable(dcufb->tcon_clk);
+ regcache_cache_only(dcufb->tcon_regmap, false);
+ regcache_sync(dcufb->tcon_regmap);
+ }
+
+ if (dcufb->scfg_regmap) {
+ regcache_cache_only(dcufb->scfg_regmap, false);
+ regcache_sync(dcufb->scfg_regmap);
+ }
+
+ clk_prepare_enable(dcufb->clk);
+ reset_total_layers(np, dcufb);
+ regcache_cache_only(dcufb->regmap, false);
+ regcache_sync(dcufb->regmap);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_dcu_pm_ops = {
+ SET_RUNTIME_PM_OPS(fsl_dcu_runtime_suspend,
+ fsl_dcu_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_suspend, fsl_dcu_resume)
+};
+
+static struct of_device_id fsl_dcu_dt_ids[] = {
+ { .compatible = "fsl,vf610-dcu", },
+ { .compatible = "fsl,ls1021a-dcu", },
+ {}
+};
+
+static struct platform_driver fsl_dcu_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_dcu_dt_ids,
+ .pm = &fsl_dcu_pm_ops,
+ },
+ .probe = fsl_dcu_probe,
+ .remove = fsl_dcu_remove,
+};
+
+module_platform_driver(fsl_dcu_driver);
+
+MODULE_AUTHOR("Alison Wang");
+MODULE_DESCRIPTION("Freescale DCU framebuffer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/fsl-sii902x.c b/drivers/video/fsl-sii902x.c
new file mode 100644
index 0000000..7b0145f
--- /dev/null
+++ b/drivers/video/fsl-sii902x.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/fsl_devices.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "edid.h"
+
+#define SII902X_INPUT_BUS_FMT 0x08
+#define SII902X_TPI_AVI_INPUT_FMT 0x09
+#define SII902X_TPI_AVI_OUTPUT_FMT 0x0A
+#define SII902X_SYS_CONTROL 0x1A
+#define SII902X_SYS_CTR_DDC_REQ BIT(2)
+#define SII902X_SYS_CTR_DDC_BUS_AVAI (BIT(2) | BIT(1))
+#define SII902X_TPI_FAMILY_DEV_ID 0x1B
+#define SII902X_TPI_DEV_REV_ID 0x1C
+#define SII902X_TPI_REV_LEVEL_ID 0x1D
+#define SII902X_POWER_STATE 0x1E
+#define SII902X_TPI_AUDIO_CFG0 0x24
+#define SII902X_TPI_AUDIO_CFG1 0x25
+#define SII902X_TPI_AUDIO_CFG2 0x26
+#define SII902X_TPI_AUDIO_CFG3 0x27
+#define SII902X_TPI_HDCP_REV 0x30
+#define SII902X_TPI_INT_ENABLE 0x3C
+#define SII902X_TPI_INT_STATUS 0x3D
+#define SII902X_TPI_INT_PLUG_IN BIT(2)
+#define SII902X_GENERAL_PURPOSE_IO0 0xBC
+#define SII902X_GENERAL_PURPOSE_IO1 0xBD
+#define SII902X_GENERAL_PURPOSE_IO2 0xBE
+#define SII902X_TRANS_MODE_DIFF 0xC7
+
+bool g_enable_hdmi;
+
+struct sii902x_data {
+ struct i2c_client *client;
+ struct delayed_work det_work;
+ struct fb_info *fbi;
+ struct regmap *regmap;
+ unsigned int irq;
+ u8 cable_plugin;
+} *sii902x;
+
+static struct i2c_client *sii902x_to_i2c(struct sii902x_data *sii902x)
+{
+ return sii902x->client;
+}
+
+static s32 sii902x_write(const struct i2c_client *client,
+ u8 command, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, command, value);
+}
+
+static s32 sii902x_read(const struct i2c_client *client, u8 command)
+{
+ int val;
+
+ val = i2c_smbus_read_word_data(client, command);
+
+ return val & 0xff;
+}
+
+static ssize_t sii902x_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ strcpy(buf, sii902x->fbi->fix.id);
+ sprintf(buf+strlen(buf), "\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, sii902x_show_name, NULL);
+
+static ssize_t sii902x_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (sii902x->cable_plugin == 0)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, sii902x_show_state, NULL);
+
+static void sii902x_power_up_tx(struct sii902x_data *sii902x)
+{
+ struct i2c_client *client = sii902x_to_i2c(sii902x);
+ int val;
+
+ val = sii902x_read(client, SII902X_POWER_STATE);
+ val &= ~0x3;
+ sii902x_write(client, SII902X_POWER_STATE, val);
+}
+
+static void sii902x_setup(struct fb_info *fbi)
+{
+ u16 data[4];
+ u32 refresh;
+ u8 *tmp;
+ int i;
+
+ /* Power up */
+ sii902x_power_up_tx(sii902x);
+
+ /* set TPI video mode */
+ data[0] = PICOS2KHZ(fbi->var.pixclock) / 10;
+ data[2] = fbi->var.hsync_len + fbi->var.left_margin +
+ fbi->var.xres + fbi->var.right_margin;
+ data[3] = fbi->var.vsync_len + fbi->var.upper_margin +
+ fbi->var.yres + fbi->var.lower_margin;
+ refresh = data[2] * data[3];
+ refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh;
+ data[1] = refresh * 100;
+ tmp = (u8 *)data;
+ for (i = 0; i < 8; i++)
+ sii902x_write(sii902x->client, i, tmp[i]);
+
+ /* input bus/pixel: full pixel wide (24bit), rising edge */
+ sii902x_write(sii902x->client, SII902X_INPUT_BUS_FMT, 0x70);
+ /* Set input format to RGB */
+ sii902x_write(sii902x->client, SII902X_TPI_AVI_INPUT_FMT, 0x00);
+ /* set output format to RGB */
+ sii902x_write(sii902x->client, SII902X_TPI_AVI_OUTPUT_FMT, 0x00);
+ /* audio setup */
+ sii902x_write(sii902x->client, SII902X_TPI_AUDIO_CFG1, 0x00);
+ sii902x_write(sii902x->client, SII902X_TPI_AUDIO_CFG2, 0x40);
+ sii902x_write(sii902x->client, SII902X_TPI_AUDIO_CFG3, 0x00);
+}
+
+static int __sii902x_read_edid(struct i2c_adapter *adp,
+ unsigned char *edid, u8 *buf)
+{
+ unsigned short addr = 0x50;
+ int ret;
+
+ struct i2c_msg msg[2] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf,
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = edid,
+ },
+ };
+
+ if (adp == NULL)
+ return -EINVAL;
+
+ memset(edid, 0, EDID_LENGTH);
+
+ ret = i2c_transfer(adp, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ /* If 0x50 fails, try 0x37. */
+ if (edid[1] == 0x00) {
+ msg[0].addr = msg[1].addr = 0x37;
+ ret = i2c_transfer(adp, msg, 2);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (edid[1] == 0x00)
+ return -ENOENT;
+
+ return 0;
+}
+/* make sure edid has 256 bytes*/
+static int __sii902x_get_edid(struct i2c_adapter *adp,
+ struct fb_info *fbi)
+{
+ u8 *edid;
+ u8 buf[2] = {0, 0};
+ int num, ret;
+
+ edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!edid)
+ return -ENOMEM;
+
+ ret = __sii902x_read_edid(adp, edid, buf);
+ if (ret)
+ return ret;
+
+ /* edid first block parsing */
+ memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
+ fb_edid_to_monspecs(edid, &fbi->monspecs);
+
+ /* need read ext block? Only support one more blk now */
+ num = edid[0x7E];
+ if (num) {
+ buf[0] = 0x80;
+ ret = __sii902x_read_edid(adp, edid, buf);
+ if (ret)
+ return ret;
+
+ fb_edid_add_monspecs(edid, &fbi->monspecs);
+ }
+
+ kfree(edid);
+ return 0;
+}
+static int sii902x_get_edid(struct fb_info *fbi)
+{
+ int old, dat, ret, cnt = 100;
+
+ old = sii902x_read(sii902x->client, SII902X_SYS_CONTROL);
+
+ sii902x_write(sii902x->client, SII902X_SYS_CONTROL,
+ old | SII902X_SYS_CTR_DDC_REQ);
+ do {
+ cnt--;
+ msleep(20);
+ dat = sii902x_read(sii902x->client, SII902X_SYS_CONTROL);
+ } while ((!(dat & 0x2)) && cnt);
+
+ if (!cnt) {
+ ret = -1;
+ goto done;
+ }
+
+ sii902x_write(sii902x->client, SII902X_SYS_CONTROL,
+ old | SII902X_SYS_CTR_DDC_BUS_AVAI);
+
+ /* edid reading */
+ ret = __sii902x_get_edid(sii902x->client->adapter, fbi);
+
+ cnt = 100;
+ do {
+ cnt--;
+ sii902x_write(sii902x->client, SII902X_SYS_CONTROL,
+ old & ~SII902X_SYS_CTR_DDC_BUS_AVAI);
+ msleep(20);
+ dat = sii902x_read(sii902x->client, SII902X_SYS_CONTROL);
+ } while ((dat & 0x6) && cnt);
+
+ if (!cnt)
+ ret = -1;
+
+done:
+ sii902x_write(sii902x->client, SII902X_SYS_CONTROL, old);
+ return ret;
+}
+
+static void det_worker(struct work_struct *work)
+{
+ struct fb_info *fbi = sii902x->fbi;
+ struct fb_monspecs *monspecs = &fbi->monspecs;
+ int val, ret;
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ val = sii902x_read(sii902x->client, SII902X_TPI_INT_STATUS);
+ if (!(val & 0x1) && !g_enable_hdmi)
+ goto err;
+
+ /* cable connection changes */
+ if (val & SII902X_TPI_INT_PLUG_IN || g_enable_hdmi) {
+ sii902x->cable_plugin = 1;
+ sprintf(event_string, "EVENT=plugin");
+
+ ret = sii902x_get_edid(fbi);
+ if (ret < 0) {
+ dev_err(&sii902x->client->dev, "read edid fail\n");
+ goto err;
+ }
+
+ /* make sure fb is powerdown */
+ console_lock();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ console_unlock();
+
+ if (monspecs->modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ fb_destroy_modelist(&fbi->modelist);
+
+ for (i = 0; i < monspecs->modedb_len; i++) {
+ /* We do not support interlaced mode for now */
+ if (!(monspecs->modedb[i].vmode &
+ FB_VMODE_INTERLACED))
+ fb_add_videomode(&monspecs->modedb[i],
+ &fbi->modelist);
+ }
+
+ fb_var_to_videomode(&m, &fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &fbi->modelist);
+
+ fb_videomode_to_var(&fbi->var, mode);
+
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(fbi, &fbi->var);
+ fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ }
+
+ console_lock();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ console_unlock();
+ } else {
+ sii902x->cable_plugin = 0;
+ sprintf(event_string, "EVENT=plugout");
+ console_lock();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ console_unlock();
+ }
+ kobject_uevent_env(&sii902x->client->dev.kobj,
+ KOBJ_CHANGE, envp);
+
+err:
+ sii902x_write(sii902x->client, SII902X_TPI_INT_STATUS, val);
+}
+
+static irqreturn_t sii902x_detect_handler(int irq, void *data)
+{
+ if (g_enable_hdmi)
+ g_enable_hdmi = false;
+
+ if (sii902x->fbi)
+ schedule_delayed_work(&(sii902x->det_work),
+ msecs_to_jiffies(20));
+ return IRQ_HANDLED;
+}
+
+static void sii902x_poweron(void)
+{
+ /* Turn on DVI or HDMI */
+ sii902x_write(sii902x->client, SII902X_SYS_CONTROL, 0x00);
+}
+
+static void sii902x_poweroff(void)
+{
+ /* disable tmds before changing resolution */
+ sii902x_write(sii902x->client, SII902X_SYS_CONTROL, 0x10);
+}
+
+static int sii902x_fb_event(struct notifier_block *nb,
+ unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ if (sii902x->fbi != NULL)
+ break;
+ sii902x->fbi = fbi;
+ if (g_enable_hdmi && sii902x->fbi) {
+ schedule_delayed_work(&(sii902x->det_work),
+ msecs_to_jiffies(20));
+ }
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ sii902x_setup(fbi);
+ break;
+ case FB_EVENT_BLANK:
+ if (*((int *)event->data) == FB_BLANK_UNBLANK)
+ sii902x_poweron();
+ else
+ sii902x_poweroff();
+ break;
+ }
+
+ return 0;
+}
+
+static void sii902x_chip_id(struct sii902x_data *sii902x)
+{
+ struct i2c_client *client = sii902x_to_i2c(sii902x);
+ int val;
+
+ /* read device ID */
+ val = sii902x_read(client, SII902X_TPI_FAMILY_DEV_ID);
+ pr_info("Sii902x: read id = 0x%02X", val);
+ val = sii902x_read(client, SII902X_TPI_DEV_REV_ID);
+ pr_info("-0x%02X", val);
+ val = sii902x_read(client, SII902X_TPI_REV_LEVEL_ID);
+ pr_info("-0x%02X", val);
+ val = sii902x_read(client, SII902X_TPI_HDCP_REV);
+ pr_info("-0x%02X\n", val);
+}
+
+static int sii902x_initialize(struct sii902x_data *sii902x)
+{
+ struct i2c_client *client = sii902x_to_i2c(sii902x);
+ int ret, cnt;
+
+ for (cnt = 0; cnt < 5; cnt++) {
+ /* Set 902x in hardware TPI mode on and jump out of D3 state */
+ ret = sii902x_write(client, SII902X_TRANS_MODE_DIFF, 0x00);
+ if (ret < 0)
+ break;
+ }
+ if (0 != ret)
+ dev_err(&client->dev, "cound not find device\n");
+
+ return ret;
+}
+
+static void sii902x_enable_source(struct sii902x_data *sii902x)
+{
+ struct i2c_client *client = sii902x_to_i2c(sii902x);
+ int val;
+
+ sii902x_write(client, SII902X_GENERAL_PURPOSE_IO0, 0x01);
+ sii902x_write(client, SII902X_GENERAL_PURPOSE_IO1, 0x82);
+ val = sii902x_read(client, SII902X_GENERAL_PURPOSE_IO2);
+ val |= 0x1;
+ sii902x_write(client, SII902X_GENERAL_PURPOSE_IO2, val);
+}
+
+static struct notifier_block nb = {
+ .notifier_call = sii902x_fb_event,
+};
+
+static int sii902x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
+ int ret, err;
+ struct fb_info edid_fbi;
+
+ if (!g_enable_hdmi)
+ return -EPERM;
+
+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -ENODEV;
+ }
+
+ sii902x = devm_kzalloc(&client->dev, sizeof(*sii902x), GFP_KERNEL);
+ if (!sii902x)
+ return -ENOMEM;
+
+ sii902x->client = client;
+ i2c_set_clientdata(client, sii902x);
+
+ err = sii902x_initialize(sii902x);
+ if (err)
+ return err;
+
+ sii902x_chip_id(sii902x);
+ sii902x_power_up_tx(sii902x);
+ sii902x_enable_source(sii902x);
+
+ /* try to read edid */
+ if (sii902x_get_edid(&edid_fbi) < 0)
+ dev_warn(&client->dev, "Can not read edid\n");
+
+ if (client->irq) {
+ ret = devm_request_irq(&client->dev, client->irq,
+ sii902x_detect_handler, 0,
+ "SII902x_det", sii902x);
+ if (ret < 0)
+ dev_warn(&client->dev,
+ "cound not request det irq %d\n",
+ client->irq);
+ else {
+ INIT_DELAYED_WORK(&(sii902x->det_work), det_worker);
+ /*enable cable hot plug irq*/
+ sii902x_write(client, SII902X_TPI_INT_ENABLE, 0x01);
+ }
+ ret = device_create_file(&client->dev, &dev_attr_fb_name);
+ if (ret < 0)
+ dev_warn(&client->dev,
+ "cound not create sys node for fb name\n");
+ ret = device_create_file(&client->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&client->dev,
+ "cound not create sys node for cable state\n");
+ }
+
+ fb_register_client(&nb);
+
+ return 0;
+}
+
+static int sii902x_remove(struct i2c_client *client)
+{
+ fb_unregister_client(&nb);
+ sii902x_poweroff();
+ return 0;
+}
+
+static const struct i2c_device_id sii902x_id[] = {
+ { "sii902x", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, sii902x_id);
+
+static const struct of_device_id sii902x_dt_ids[] = {
+ { .compatible = "fsl,sii902x", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sii902x_dt_ids);
+
+static struct i2c_driver sii902x_i2c_driver = {
+ .driver = {
+ .name = "sii902x",
+ .owner = THIS_MODULE,
+ .of_match_table = sii902x_dt_ids,
+ },
+ .probe = sii902x_probe,
+ .remove = sii902x_remove,
+ .id_table = sii902x_id,
+};
+module_i2c_driver(sii902x_i2c_driver);
+
+static int __init enable_hdmi_setup(char *str)
+{
+ g_enable_hdmi = true;
+
+ return 1;
+}
+__setup("hdmi", enable_hdmi_setup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SII902x DVI/HDMI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b27b75d..d201a49 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -359,6 +359,8 @@ config MAX63XX_WATCHDOG
config IMX2_WDT
tristate "IMX2+ Watchdog"
depends on ARCH_MXC
+ select REGMAP_MMIO
+ select WATCHDOG_CORE
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 693ac3f..729b09f 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. <w.sang@pengutronix.de>
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* some parts adapted by similar drivers from Darius Augulis and Vladimir
* Zapolskiy, additional improvements by Wim Van Sebroeck.
@@ -20,19 +21,18 @@
* Halt on suspend: Manual Can be automatic
*/
+#include <linux/clk.h>
#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
-#include <linux/watchdog.h>
-#include <linux/clk.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
+#include <linux/regmap.h>
#include <linux/timer.h>
-#include <linux/jiffies.h>
+#include <linux/watchdog.h>
#define DRIVER_NAME "imx2-wdt"
@@ -40,6 +40,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 */
@@ -53,19 +54,12 @@
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
-#define IMX2_WDT_STATUS_OPEN 0
-#define IMX2_WDT_STATUS_STARTED 1
-#define IMX2_WDT_EXPECT_CLOSE 2
-
-static struct {
+struct imx2_wdt_device {
struct clk *clk;
- void __iomem *base;
- unsigned timeout;
- unsigned long status;
+ struct regmap *regmap;
struct timer_list timer; /* Pings the watchdog when closed */
-} imx2_wdt;
-
-static struct miscdevice imx2_wdt_miscdev;
+ struct watchdog_device wdog;
+};
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
@@ -83,10 +77,15 @@ static const struct watchdog_info imx2_wdt_info = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
};
-static inline void imx2_wdt_setup(void)
+static inline void imx2_wdt_setup(struct watchdog_device *wdog)
{
- u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+ u32 val;
+ regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
+
+ /* 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 */
@@ -94,234 +93,272 @@ static inline void imx2_wdt_setup(void)
/* Keep Watchdog Disabled */
val &= ~IMX2_WDT_WCR_WDE;
/* Set the watchdog's Time-Out value */
- val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout);
+ val |= WDOG_SEC_TO_COUNT(wdog->timeout);
- __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+ regmap_write(wdev->regmap, IMX2_WDT_WCR, val);
/* enable the watchdog */
val |= IMX2_WDT_WCR_WDE;
- __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
+ regmap_write(wdev->regmap, IMX2_WDT_WCR, val);
}
-static inline void imx2_wdt_ping(void)
+static inline bool imx2_wdt_is_running(struct imx2_wdt_device *wdev)
{
- __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR);
- __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR);
-}
+ u32 val;
-static void imx2_wdt_timer_ping(unsigned long arg)
-{
- /* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */
- imx2_wdt_ping();
- mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2);
+ regmap_read(wdev->regmap, IMX2_WDT_WCR, &val);
+
+ return val & IMX2_WDT_WCR_WDE;
}
-static void imx2_wdt_start(void)
+static int imx2_wdt_ping(struct watchdog_device *wdog)
{
- if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
- /* at our first start we enable clock and do initialisations */
- clk_prepare_enable(imx2_wdt.clk);
-
- imx2_wdt_setup();
- } else /* delete the timer that pings the watchdog after close */
- del_timer_sync(&imx2_wdt.timer);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
- /* Watchdog is enabled - time to reload the timeout value */
- imx2_wdt_ping();
+ regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1);
+ regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2);
+ return 0;
}
-static void imx2_wdt_stop(void)
+static void imx2_wdt_timer_ping(unsigned long arg)
{
- /* we don't need a clk_disable, it cannot be disabled once started.
- * We use a timer to ping the watchdog while /dev/watchdog is closed */
- imx2_wdt_timer_ping(0);
+ struct watchdog_device *wdog = (struct watchdog_device *)arg;
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ /* ping it every wdog->timeout / 2 seconds to prevent reboot */
+ imx2_wdt_ping(wdog);
+ mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
}
-static void imx2_wdt_set_timeout(int new_timeout)
+static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int new_timeout)
{
- u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
- /* 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(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
+ WDOG_SEC_TO_COUNT(new_timeout));
+ return 0;
}
-static int imx2_wdt_open(struct inode *inode, struct file *file)
+static int imx2_wdt_start(struct watchdog_device *wdog)
{
- if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
- return -EBUSY;
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ if (imx2_wdt_is_running(wdev)) {
+ /* delete the timer that pings the watchdog after close */
+ del_timer_sync(&wdev->timer);
+ imx2_wdt_set_timeout(wdog, wdog->timeout);
+ } else
+ imx2_wdt_setup(wdog);
- imx2_wdt_start();
- return nonseekable_open(inode, file);
+ return imx2_wdt_ping(wdog);
}
-static int imx2_wdt_close(struct inode *inode, struct file *file)
+static int imx2_wdt_stop(struct watchdog_device *wdog)
{
- if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout)
- imx2_wdt_stop();
- else {
- dev_crit(imx2_wdt_miscdev.parent,
- "Unexpected close: Expect reboot!\n");
- imx2_wdt_ping();
- }
-
- clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
- clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status);
+ /*
+ * We don't need a clk_disable, it cannot be disabled once started.
+ * We use a timer to ping the watchdog while /dev/watchdog is closed
+ */
+ imx2_wdt_timer_ping((unsigned long)wdog);
return 0;
}
-static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog)
{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_value;
- u16 val;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &imx2_wdt_info,
- sizeof(struct watchdog_info)) ? -EFAULT : 0;
-
- case WDIOC_GETSTATUS:
- return put_user(0, p);
-
- case WDIOC_GETBOOTSTATUS:
- val = __raw_readw(imx2_wdt.base + IMX2_WDT_WRSR);
- new_value = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
- return put_user(new_value, p);
-
- case WDIOC_KEEPALIVE:
- imx2_wdt_ping();
- return 0;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_value, p))
- return -EFAULT;
- if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME))
- return -EINVAL;
- imx2_wdt_set_timeout(new_value);
- imx2_wdt.timeout = new_value;
- imx2_wdt_ping();
-
- /* Fallthrough to return current value */
- case WDIOC_GETTIMEOUT:
- return put_user(imx2_wdt.timeout, p);
-
- default:
- return -ENOTTY;
- }
-}
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
-static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- size_t i;
- char c;
-
- if (len == 0) /* Can we see this even ? */
- return 0;
-
- clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
- /* scan to see whether or not we got the magic character */
- for (i = 0; i != len; i++) {
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
+ if (imx2_wdt_is_running(wdev)) {
+ imx2_wdt_set_timeout(wdog, wdog->timeout);
+ imx2_wdt_timer_ping((unsigned long)wdog);
}
-
- imx2_wdt_ping();
- return len;
}
-static const struct file_operations imx2_wdt_fops = {
+static struct watchdog_ops imx2_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = imx2_wdt_ioctl,
- .open = imx2_wdt_open,
- .release = imx2_wdt_close,
- .write = imx2_wdt_write,
+ .start = imx2_wdt_start,
+ .stop = imx2_wdt_stop,
+ .ping = imx2_wdt_ping,
+ .set_timeout = imx2_wdt_set_timeout,
};
-static struct miscdevice imx2_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .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 device_node *np = pdev->dev.of_node;
+ struct imx2_wdt_device *wdev;
+ struct watchdog_device *wdog;
struct resource *res;
+ void __iomem *base;
+ bool big_endian;
+ int ret;
+ u32 val;
+
+ wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
+ if (!wdev)
+ return -ENOMEM;
+
+ big_endian = of_property_read_bool(np, "big-endian");
+ if (big_endian)
+ imx2_wdt_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
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);
+
+ wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
+ &imx2_wdt_regmap_config);
+ if (IS_ERR(wdev->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(wdev->regmap);
+ }
- imx2_wdt.clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(imx2_wdt.clk)) {
+ wdev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(wdev->clk)) {
dev_err(&pdev->dev, "can't get Watchdog clock\n");
- return PTR_ERR(imx2_wdt.clk);
+ return PTR_ERR(wdev->clk);
}
- imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
- if (imx2_wdt.timeout != timeout)
- dev_warn(&pdev->dev, "Initial timeout out of range! "
- "Clamped from %u to %u\n", timeout, imx2_wdt.timeout);
+ wdog = &wdev->wdog;
+ wdog->info = &imx2_wdt_info;
+ wdog->ops = &imx2_wdt_ops;
+ wdog->min_timeout = 1;
+ wdog->max_timeout = IMX2_WDT_MAX_TIME;
- setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
+ clk_prepare_enable(wdev->clk);
- imx2_wdt_miscdev.parent = &pdev->dev;
- ret = misc_register(&imx2_wdt_miscdev);
- if (ret)
- goto fail;
+ regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val);
+ wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
- dev_info(&pdev->dev,
- "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n",
- imx2_wdt.timeout, nowayout);
- return 0;
+ wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
+ if (wdog->timeout != timeout)
+ dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
+ timeout, wdog->timeout);
+
+ platform_set_drvdata(pdev, wdog);
+ watchdog_set_drvdata(wdog, wdev);
+ watchdog_set_nowayout(wdog, nowayout);
+ watchdog_init_timeout(wdog, timeout, &pdev->dev);
+
+ setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog);
-fail:
- imx2_wdt_miscdev.parent = NULL;
- return ret;
+ imx2_wdt_ping_if_active(wdog);
+
+ ret = watchdog_register_device(wdog);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot register watchdog device\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n",
+ wdog->timeout, nowayout);
+
+ return 0;
}
static int __exit imx2_wdt_remove(struct platform_device *pdev)
{
- misc_deregister(&imx2_wdt_miscdev);
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
- if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
- del_timer_sync(&imx2_wdt.timer);
+ watchdog_unregister_device(wdog);
- dev_crit(imx2_wdt_miscdev.parent,
- "Device removed: Expect reboot!\n");
+ if (imx2_wdt_is_running(wdev)) {
+ del_timer_sync(&wdev->timer);
+ imx2_wdt_ping(wdog);
+ dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
}
-
- imx2_wdt_miscdev.parent = NULL;
return 0;
}
static void imx2_wdt_shutdown(struct platform_device *pdev)
{
- if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
- /* we are running, we need to delete the timer but will give
- * max timeout before reboot will take place */
- del_timer_sync(&imx2_wdt.timer);
- imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME);
- imx2_wdt_ping();
-
- dev_crit(imx2_wdt_miscdev.parent,
- "Device shutdown: Expect reboot!\n");
+ struct watchdog_device *wdog = platform_get_drvdata(pdev);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ if (imx2_wdt_is_running(wdev)) {
+ /*
+ * We are running, we need to delete the timer but will
+ * give max timeout before reboot will take place
+ */
+ del_timer_sync(&wdev->timer);
+ imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
+ imx2_wdt_ping(wdog);
+ dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
+ }
+}
+
+#ifdef CONFIG_PM_SLEEP
+/* Disable watchdog if it is active or non-active but still running */
+static int imx2_wdt_suspend(struct device *dev)
+{
+ struct watchdog_device *wdog = dev_get_drvdata(dev);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ /* The watchdog IP block is running */
+ if (imx2_wdt_is_running(wdev)) {
+ imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
+ imx2_wdt_ping(wdog);
+
+ /* The watchdog is not active */
+ if (!watchdog_active(wdog))
+ del_timer_sync(&wdev->timer);
}
+
+ clk_disable_unprepare(wdev->clk);
+
+ return 0;
}
+/* Enable watchdog and configure it if necessary */
+static int imx2_wdt_resume(struct device *dev)
+{
+ struct watchdog_device *wdog = dev_get_drvdata(dev);
+ struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+ clk_prepare_enable(wdev->clk);
+
+ if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) {
+ /*
+ * If the watchdog is still active and resumes
+ * from deep sleep state, need to restart the
+ * watchdog again.
+ */
+ imx2_wdt_setup(wdog);
+ imx2_wdt_set_timeout(wdog, wdog->timeout);
+ imx2_wdt_ping(wdog);
+ } else if (imx2_wdt_is_running(wdev)) {
+ /* Resuming from non-deep sleep state. */
+ imx2_wdt_set_timeout(wdog, wdog->timeout);
+ imx2_wdt_ping(wdog);
+ /*
+ * But the watchdog is not active, then start
+ * the timer again.
+ */
+ if (!watchdog_active(wdog))
+ mod_timer(&wdev->timer,
+ jiffies + wdog->timeout * HZ / 2);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
+ imx2_wdt_resume);
+
static const struct of_device_id imx2_wdt_dt_ids[] = {
{ .compatible = "fsl,imx21-wdt", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids);
static struct platform_driver imx2_wdt_driver = {
.remove = __exit_p(imx2_wdt_remove),
@@ -329,6 +366,7 @@ static struct platform_driver imx2_wdt_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &imx2_wdt_pm_ops,
.of_match_table = imx2_wdt_dt_ids,
},
};
@@ -338,5 +376,4 @@ module_platform_driver_probe(imx2_wdt_driver, imx2_wdt_probe);
MODULE_AUTHOR("Wolfram Sang");
MODULE_DESCRIPTION("Watchdog driver for IMX2 and later");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/dt-bindings/clock/ls1021a-clock.h b/include/dt-bindings/clock/ls1021a-clock.h
new file mode 100644
index 0000000..09b47d7
--- /dev/null
+++ b/include/dt-bindings/clock/ls1021a-clock.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_LS1021A_H
+#define __DT_BINDINGS_CLOCK_LS1021A_H
+
+#define LS1021A_CLK_DUMMY 0
+#define LS1021A_CLK_PBL_EN 1
+#define LS1021A_CLK_ESDHC_EN 2
+#define LS1021A_CLK_DMA1_EN 3
+#define LS1021A_CLK_DMA2_EN 4
+#define LS1021A_CLK_USB3_PHY_EN 5
+#define LS1021A_CLK_USB2_EN 6
+#define LS1021A_CLK_SATA_EN 7
+#define LS1021A_CLK_USB3_EN 8
+#define LS1021A_CLK_SEC_EN 9
+#define LS1021A_CLK_2D_ACE_EN 10
+#define LS1021A_CLK_QE_EN 11
+#define LS1021A_CLK_ETSEC1_EN 12
+#define LS1021A_CLK_ETSEC2_EN 13
+#define LS1021A_CLK_ETSEC3_EN 14
+#define LS1021A_CLK_PEX1_EN 15
+#define LS1021A_CLK_PEX2_EN 16
+#define LS1021A_CLK_DUART1_EN 17
+#define LS1021A_CLK_DUART2_EN 18
+#define LS1021A_CLK_QSPI_EN 19
+#define LS1021A_CLK_DDR_EN 20
+#define LS1021A_CLK_OCRAM1_EN 21
+#define LS1021A_CLK_IFC_EN 22
+#define LS1021A_CLK_GPIO_EN 23
+#define LS1021A_CLK_DBG_EN 24
+#define LS1021A_CLK_FLEXCAN1_EN 25
+#define LS1021A_CLK_FLEXCAN234_EN 26
+#define LS1021A_CLK_FLEXTIMER_EN 27
+#define LS1021A_CLK_SECMON_EN 28
+#define LS1021A_CLK_WDOG_EN 29
+#define LS1021A_CLK_WDOG12_EN 30
+#define LS1021A_CLK_I2C23_EN 31
+#define LS1021A_CLK_SAI_EN 32
+#define LS1021A_CLK_LPUART_EN 33
+#define LS1021A_CLK_DSPI12_EN 34
+#define LS1021A_CLK_ASRC_EN 35
+#define LS1021A_CLK_SPDIF_EN 36
+#define LS1021A_CLK_I2C1_EN 37
+#define LS1021A_CLK_LPUART1_EN 38
+#define LS1021A_CLK_FLEXTIMER1_EN 39
+#define LS1021A_CLK_END 40
+
+#endif /* __DT_BINDINGS_CLOCK_LS1021A_H */
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 <asm/rheap.h>
+#include <linux/fsl/rheap.h>
#include <asm/mmu.h>
#include <linux/spinlock.h>
@@ -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/arch/powerpc/include/asm/immap_qe.h b/include/linux/fsl/immap_qe.h
index c76ef30..bd1ebd4 100644
--- a/arch/powerpc/include/asm/immap_qe.h
+++ b/include/linux/fsl/immap_qe.h
@@ -2,11 +2,11 @@
* 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 <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors:
+ * Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* 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
@@ -18,7 +18,7 @@
#ifdef __KERNEL__
#include <linux/kernel.h>
-#include <asm/io.h>
+#include <linux/io.h>
#define QE_IMMAP_SIZE (1024 * 1024) /* 1MB from 1MB+IMMR */
@@ -29,7 +29,7 @@ struct qe_iram {
u8 res0[0x04];
__be32 iready; /* I-RAM Ready Register */
u8 res1[0x70];
-} __attribute__ ((packed));
+} __packed;
/* QE Interrupt Controller */
struct qe_ic_regs {
@@ -52,7 +52,7 @@ struct qe_ic_regs {
u8 res2[0x20];
__be32 qhivec;
u8 res3[0x1C];
-} __attribute__ ((packed));
+} __packed;
/* Communications Processor */
struct cp_qe {
@@ -90,7 +90,7 @@ struct cp_qe {
u8 res12[0x3A];
__be32 ceurnr; /* QE microcode revision number register */
u8 res13[0x244];
-} __attribute__ ((packed));
+} __packed;
/* QE Multiplexer */
struct qe_mux {
@@ -101,7 +101,7 @@ struct qe_mux {
__be32 cmxucr[4]; /* CMX UCCx clock route registers */
__be32 cmxupcr; /* CMX UPC clock route register */
u8 res0[0x1C];
-} __attribute__ ((packed));
+} __packed;
/* QE Timers */
struct qe_timers {
@@ -131,13 +131,13 @@ struct qe_timers {
__be16 gtevr4; /* Timer 4 event register */
__be16 gtps; /* Timer 1 prescale register */
u8 res2[0x46];
-} __attribute__ ((packed));
+} __packed;
/* BRG */
struct qe_brg {
__be32 brgc[16]; /* BRG configuration registers */
u8 res0[0x40];
-} __attribute__ ((packed));
+} __packed;
/* SPI */
struct spi {
@@ -155,7 +155,7 @@ struct spi {
__be32 spitd; /* SPI transmit data register (cpu mode) */
__be32 spird; /* SPI receive data register (cpu mode) */
u8 res7[0x8];
-} __attribute__ ((packed));
+} __packed;
/* SI */
struct si1 {
@@ -199,14 +199,14 @@ struct si1 {
__be32 siml1; /* SI1 multiframe limit register */
u8 siedm1; /* SI1 extended diagnostic mode register */
u8 res9[0xBB];
-} __attribute__ ((packed));
+} __packed;
/* SI Routing Tables */
struct sir {
- u8 tx[0x400];
+ u8 tx[0x400];
u8 rx[0x400];
u8 res0[0x800];
-} __attribute__ ((packed));
+} __packed;
/* USB Controller */
struct qe_usb_ctlr {
@@ -225,7 +225,7 @@ struct qe_usb_ctlr {
u8 res5[2];
__be16 usb_usfrn;
u8 res6[0x22];
-} __attribute__ ((packed));
+} __packed;
/* MCC */
struct qe_mcc {
@@ -234,7 +234,7 @@ struct qe_mcc {
__be32 mccf; /* MCC configuration register */
__be32 merl; /* MCC emergency request level register */
u8 res0[0xF0];
-} __attribute__ ((packed));
+} __packed;
/* QE UCC Slow */
struct ucc_slow {
@@ -253,7 +253,7 @@ struct ucc_slow {
__be16 utpt;
u8 res4[0x52];
u8 guemr; /* UCC general extended mode register */
-} __attribute__ ((packed));
+} __packed;
/* QE UCC Fast */
struct ucc_fast {
@@ -285,7 +285,7 @@ struct ucc_fast {
__be32 urtry; /* UCC retry counter register */
u8 res8[0x4C];
u8 guemr; /* UCC general extended mode register */
-} __attribute__ ((packed));
+} __packed;
struct ucc {
union {
@@ -293,7 +293,7 @@ struct ucc {
struct ucc_fast fast;
u8 res[0x200]; /* UCC blocks are 512 bytes each */
};
-} __attribute__ ((packed));
+} __packed;
/* MultiPHY UTOPIA POS Controllers (UPC) */
struct upc {
@@ -349,7 +349,7 @@ struct upc {
__be32 uper3; /* Device 3 port enable register */
__be32 uper4; /* Device 4 port enable register */
u8 res2[0x150];
-} __attribute__ ((packed));
+} __packed;
/* SDMA */
struct sdma {
@@ -369,7 +369,7 @@ struct sdma {
u8 res1[0x4];
__be32 sdebcr; /* SDMA CAM entries base register */
u8 res2[0x38];
-} __attribute__ ((packed));
+} __packed;
/* Debug Space */
struct dbg {
@@ -386,7 +386,7 @@ struct dbg {
__be32 bprmsr; /* Breakpoint request mode serial register */
__be32 bpemr; /* Breakpoint exit mode register */
u8 res2[0x48];
-} __attribute__ ((packed));
+} __packed;
/*
* RISC Special Registers (Trap and Breakpoint). These are described in
@@ -421,7 +421,7 @@ struct rsp {
__be32 eccr; /* Exception control configuration register */
__be32 eicr;
u8 res4[0x100-0xf8];
-} __attribute__ ((packed));
+} __packed;
struct qe_immap {
struct qe_iram iram; /* I-RAM */
@@ -461,7 +461,7 @@ struct qe_immap {
Multi-user RAM */
u8 res17[0x24000]; /* 0x11C000 - 0x140000 */
u8 res18[0xC0000]; /* 0x140000 - 0x200000 */
-} __attribute__ ((packed));
+} __packed;
extern struct qe_immap __iomem *qe_immr;
extern phys_addr_t get_qe_base(void);
diff --git a/arch/powerpc/include/asm/qe.h b/include/linux/fsl/qe.h
index a7387c2..ef4422c 100644
--- a/arch/powerpc/include/asm/qe.h
+++ b/include/linux/fsl/qe.h
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* Description:
* QUICC Engine (QE) external definitions and structure.
@@ -19,8 +19,9 @@
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/err.h>
-#include <asm/cpm.h>
-#include <asm/immap_qe.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/fsl/immap_qe.h>
#define QE_NUM_OF_SNUM 256 /* There are 256 serial number in QE */
#define QE_NUM_OF_BRGS 16
@@ -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.
*
@@ -213,30 +249,30 @@ struct qe_firmware {
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;
+ __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 */
+ u8 id[32]; /* Null-terminated identifier */
__be32 traps[16]; /* Trap addresses, 0 == ignore */
- __be32 eccr; /* The value for the ECCR register */
+ __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 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 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];
+ } __packed microcode[1];
/* All microcode binaries should be located here */
/* CRC32 should be located here, after the microcode binaries */
-} __attribute__ ((packed));
+} __packed;
struct qe_firmware_info {
char id[64]; /* Firmware name */
@@ -265,11 +301,32 @@ struct qe_bd {
__be16 status;
__be16 length;
__be32 buf;
-} __attribute__ ((packed));
+} __packed;
#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
@@ -315,14 +372,14 @@ struct qe_timer_tables {
u16 r_tmv; /* QE timer valid register */
u32 tm_cmd; /* QE timer cmd register */
u32 tm_cnt; /* QE timer internal cnt */
-} __attribute__ ((packed));
+} __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));
+} __packed;
/* Communication Direction */
enum comm_dir {
@@ -542,7 +599,7 @@ struct ucc_slow_pram {
__be32 ttemp; /* Tx temp */
__be32 rcrc; /* temp receive CRC */
__be32 tcrc; /* temp transmit CRC */
-} __attribute__ ((packed));
+} __packed;
/* General UCC SLOW Mode Register (GUMRH & GUMRL) */
#define UCC_SLOW_GUMR_H_SAM_QMC 0x00000000
diff --git a/arch/powerpc/include/asm/qe_ic.h b/include/linux/fsl/qe_ic.h
index 25784cc..79f162c 100644
--- a/arch/powerpc/include/asm/qe_ic.h
+++ b/include/linux/fsl/qe_ic.h
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* Description:
* QE IC external definitions and structure.
diff --git a/arch/powerpc/include/asm/rheap.h b/include/linux/fsl/rheap.h
index 1723817..88149da 100644
--- a/arch/powerpc/include/asm/rheap.h
+++ b/include/linux/fsl/rheap.h
@@ -16,74 +16,78 @@
#include <linux/list.h>
-typedef struct _rh_block {
+struct _rh_block {
struct list_head list;
unsigned long start;
int size;
const char *owner;
-} rh_block_t;
+};
-typedef struct _rh_info {
+struct _rh_info {
unsigned int alignment;
int max_blocks;
int empty_slots;
- rh_block_t *block;
+ struct _rh_block *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 {
+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);
+extern struct _rh_info *rh_create(unsigned int alignment);
/* Destroy a remote heap, created by rh_create() */
-extern void rh_destroy(rh_info_t * info);
+extern void rh_destroy(struct _rh_info *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);
+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(rh_info_t * info, unsigned long start, int size);
+extern int rh_attach_region(struct _rh_info *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);
+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(rh_info_t * info, int size, int alignment,
- const char *owner);
+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(rh_info_t * info, int size, const char *owner);
+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(rh_info_t * info, unsigned long start, int size,
- const char *owner);
+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(rh_info_t * info, unsigned long start);
+extern int rh_free(struct _rh_info *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);
+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(rh_info_t * info);
+extern void rh_dump(struct _rh_info *info);
/* Set owner of taken block */
-extern int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner);
+extern int rh_set_owner(struct _rh_info *info, unsigned long start,
+ const char *owner);
#endif /* __ASM_PPC_RHEAP_H__ */
diff --git a/arch/powerpc/include/asm/ucc.h b/include/linux/fsl/ucc.h
index 39a0bb5..622e2fc 100644
--- a/arch/powerpc/include/asm/ucc.h
+++ b/include/linux/fsl/ucc.h
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* Description:
* Internal header file for UCC unit routines.
@@ -15,8 +15,8 @@
#ifndef __UCC_H__
#define __UCC_H__
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
#define STATISTICS
diff --git a/arch/powerpc/include/asm/ucc_fast.h b/include/linux/fsl/ucc_fast.h
index 561b00b..ec24f28 100644
--- a/arch/powerpc/include/asm/ucc_fast.h
+++ b/include/linux/fsl/ucc_fast.h
@@ -3,8 +3,8 @@
*
* Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* 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
@@ -16,10 +16,10 @@
#include <linux/kernel.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
+#include <linux/fsl/ucc.h>
/* Receive BD's status */
#define R_E 0x80000000 /* buffer empty */
@@ -195,14 +195,15 @@ struct ucc_fast_private {
* 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);
+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);
+void ucc_fast_free(struct ucc_fast_private *uccf);
/* ucc_fast_enable
* Enables a fast UCC port.
@@ -211,7 +212,7 @@ void ucc_fast_free(struct ucc_fast_private * uccf);
* 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);
+void ucc_fast_enable(struct ucc_fast_private *uccf, enum comm_dir mode);
/* ucc_fast_disable
* Disables a fast UCC port.
@@ -220,7 +221,7 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode);
* 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);
+void ucc_fast_disable(struct ucc_fast_private *uccf, enum comm_dir mode);
/* ucc_fast_irq
* Handles interrupts on fast UCC.
@@ -228,7 +229,7 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode);
*
* uccf - (In) pointer to the fast UCC structure.
*/
-void ucc_fast_irq(struct ucc_fast_private * uccf);
+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.
@@ -241,10 +242,10 @@ void ucc_fast_irq(struct ucc_fast_private * uccf);
*
* uccf - (In) pointer to the fast UCC structure.
*/
-void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf);
+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);
+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/include/linux/fsl/ucc_slow.h
index c44131e..56c0318 100644
--- a/arch/powerpc/include/asm/ucc_slow.h
+++ b/include/linux/fsl/ucc_slow.h
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
*
- * Authors: Shlomi Gridish <gridish@freescale.com>
- * Li Yang <leoli@freescale.com>
+ * Authors: Shlomi Gridish <gridish@freescale.com>
+ * Li Yang <leoli@freescale.com>
*
* Description:
* Internal header file for UCC SLOW unit routines.
@@ -17,10 +17,10 @@
#include <linux/kernel.h>
-#include <asm/immap_qe.h>
-#include <asm/qe.h>
+#include <linux/fsl/immap_qe.h>
+#include <linux/fsl/qe.h>
-#include <asm/ucc.h>
+#include <linux/fsl/ucc.h>
/* transmit BD's status */
#define T_R 0x80000000 /* ready bit */
@@ -224,14 +224,15 @@ struct ucc_slow_private {
* 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);
+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);
+void ucc_slow_free(struct ucc_slow_private *uccs);
/* ucc_slow_enable
* Enables a fast UCC port.
@@ -240,7 +241,7 @@ void ucc_slow_free(struct ucc_slow_private * uccs);
* 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);
+void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode);
/* ucc_slow_disable
* Disables a fast UCC port.
@@ -249,7 +250,7 @@ void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode);
* 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);
+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.
@@ -262,21 +263,21 @@ void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode);
*
* uccs - (In) pointer to the slow UCC structure.
*/
-void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs);
+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);
+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);
+void ucc_slow_stop_tx(struct ucc_slow_private *uccs);
/* ucc_slow_restart_tx
* Restarts transmitting on a specified slow UCC.
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 2c4b7d3..8c87c37 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -97,6 +97,7 @@ struct fsl_usb2_platform_data {
unsigned has_fsl_erratum_a006918:1;
unsigned has_fsl_erratum_a005697:1;
unsigned has_fsl_erratum_a007792:1;
+ unsigned has_fsl_erratum_14:1;
/* register save area for suspend/resume */
u32 pm_command;
diff --git a/arch/powerpc/include/asm/fsl_ifc.h b/include/linux/fsl_ifc.h
index e06473b..3833238 100644
--- a/arch/powerpc/include/asm/fsl_ifc.h
+++ b/include/linux/fsl_ifc.h
@@ -832,6 +832,7 @@ struct fsl_ifc_ctrl {
u32 nand_stat;
wait_queue_head_t nand_wait;
+ bool little_endian;
#ifdef CONFIG_PM_SLEEP
/* save regs when system go to deep-sleep */
struct fsl_ifc_regs *saved_regs;
@@ -840,5 +841,46 @@ struct fsl_ifc_ctrl {
extern struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
+static inline u32 ifc_in32(void __iomem *addr)
+{
+ if (fsl_ifc_ctrl_dev->little_endian)
+ return ioread32(addr);
+ else
+ return ioread32be(addr);
+}
+
+static inline u16 ifc_in16(void __iomem *addr)
+{
+ if (fsl_ifc_ctrl_dev->little_endian)
+ return ioread16(addr);
+ else
+ return ioread16be(addr);
+}
+
+static inline u8 ifc_in8(void __iomem *addr)
+{
+ return ioread8(addr);
+}
+
+static inline void ifc_out32(u32 val, void __iomem *addr)
+{
+ if (fsl_ifc_ctrl_dev->little_endian)
+ return iowrite32(val, addr);
+ else
+ return iowrite32be(val, addr);
+}
+
+static inline void ifc_out16(u16 val, void __iomem *addr)
+{
+ if (fsl_ifc_ctrl_dev->little_endian)
+ return iowrite16(val, addr);
+ else
+ return iowrite16be(val, addr);
+}
+
+static inline void ifc_out8(u8 val, void __iomem *addr)
+{
+ return iowrite8(val, addr);
+}
#endif /* __ASM_FSL_IFC_H */
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index b2514f7..09354f6 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -15,9 +15,19 @@
#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);
+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
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
new file mode 100644
index 0000000..e1bc2c3
--- /dev/null
+++ b/include/linux/mtd/spi-nor.h
@@ -0,0 +1,222 @@
+/*
+ * 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
+
+/*
+ * Note on opcode nomenclature: some opcodes have a format like
+ * 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. The suffix of 'D' stands for the
+ * DDR mode.
+ */
+
+/* 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_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_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 */
+#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 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_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) */
+
+/* Used for SST flashes only. */
+#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 SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
+#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
+
+/* Used for Spansion flashes only. */
+#define SPINOR_OP_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 */
+
+enum read_mode {
+ SPI_NOR_NORMAL = 0,
+ SPI_NOR_FAST,
+ SPI_NOR_DUAL,
+ SPI_NOR_QUAD,
+ SPI_NOR_DDR_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.
+ * @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
+ * @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;
+ struct device_node *np;
+ 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;
+};
+
+/**
+ * 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[];
+
+/**
+ * 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
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 <linux/msi.h>
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/include/sound/pcm.h b/include/sound/pcm.h
index 84b10f9..a07b670 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -418,6 +418,8 @@ struct snd_pcm_substream {
#endif
/* misc flags */
unsigned int hw_opened: 1;
+ /* data swapped flags */
+ unsigned int data_swapped;
};
#define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0)
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
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/arch/powerpc/lib/rheap.c b/lib/rheap.c
index a1060a8..241cc0a 100644
--- a/arch/powerpc/lib/rheap.c
+++ b/lib/rheap.c
@@ -20,7 +20,7 @@
#include <linux/err.h>
#include <linux/slab.h>
-#include <asm/rheap.h>
+#include <linux/fsl/rheap.h>
/*
* Fixup a list_head, needed when copying lists. If the pointers fall
@@ -42,9 +42,9 @@ static inline void fixup(unsigned long s, unsigned long e, int d,
}
/* Grow the allocated blocks */
-static int grow(rh_info_t * info, int max_blocks)
+static int grow(struct _rh_info *info, int max_blocks)
{
- rh_block_t *block, *blk;
+ struct _rh_block *block, *blk;
int i, new_blocks;
int delta;
unsigned long blks, blke;
@@ -54,7 +54,7 @@ static int grow(rh_info_t * info, int max_blocks)
new_blocks = max_blocks - info->max_blocks;
- block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_ATOMIC);
+ block = kmalloc(sizeof(struct _rh_block) * max_blocks, GFP_ATOMIC);
if (block == NULL)
return -ENOMEM;
@@ -62,7 +62,7 @@ static int grow(rh_info_t * info, int max_blocks)
/* copy old block area */
memcpy(block, info->block,
- sizeof(rh_block_t) * info->max_blocks);
+ sizeof(struct _rh_block) * info->max_blocks);
delta = (char *)block - (char *)info->block;
@@ -100,7 +100,7 @@ static int grow(rh_info_t * info, int max_blocks)
* 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)
+static int assure_empty(struct _rh_info *info, int slots)
{
int max_blocks;
@@ -118,19 +118,19 @@ static int assure_empty(rh_info_t * info, int slots)
return grow(info, max_blocks);
}
-static rh_block_t *get_slot(rh_info_t * info)
+static struct _rh_block *get_slot(struct _rh_info *info)
{
- rh_block_t *blk;
+ 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) {
- printk(KERN_ERR "rh: out of slots; crash is imminent.\n");
+ pr_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);
+ blk = list_entry(info->empty_list.next, struct _rh_block, list);
list_del_init(&blk->list);
info->empty_slots--;
@@ -142,18 +142,18 @@ static rh_block_t *get_slot(rh_info_t * info)
return blk;
}
-static inline void release_slot(rh_info_t * info, rh_block_t * 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(rh_info_t * info, rh_block_t * blkn)
+static void attach_free_block(struct _rh_info *info, struct _rh_block *blkn)
{
- rh_block_t *blk;
- rh_block_t *before;
- rh_block_t *after;
- rh_block_t *next;
+ 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;
@@ -170,7 +170,7 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
next = NULL;
list_for_each(l, &info->free_list) {
- blk = list_entry(l, rh_block_t, list);
+ blk = list_entry(l, struct _rh_block, list);
bs = blk->start;
be = bs + blk->size;
@@ -229,14 +229,14 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
release_slot(info, after);
}
-static void attach_taken_block(rh_info_t * info, rh_block_t * blkn)
+static void attach_taken_block(struct _rh_info *info, struct _rh_block *blkn)
{
- rh_block_t *blk;
+ 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, rh_block_t, list);
+ blk = list_entry(l, struct _rh_block, list);
if (blk->start > blkn->start) {
list_add_tail(&blkn->list, &blk->list);
return;
@@ -250,9 +250,9 @@ static void attach_taken_block(rh_info_t * info, rh_block_t * blkn)
* 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)
+struct _rh_info *rh_create(unsigned int alignment)
{
- rh_info_t *info;
+ struct _rh_info *info;
/* Alignment must be a power of two */
if ((alignment & (alignment - 1)) != 0)
@@ -282,7 +282,7 @@ 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)
+void rh_destroy(struct _rh_info *info)
{
if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL)
kfree(info->block);
@@ -297,11 +297,11 @@ EXPORT_SYMBOL_GPL(rh_destroy);
* 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)
+void rh_init(struct _rh_info *info, unsigned int alignment, int max_blocks,
+ struct _rh_block *block)
{
int i;
- rh_block_t *blk;
+ struct _rh_block *blk;
/* Alignment must be a power of two */
if ((alignment & (alignment - 1)) != 0)
@@ -326,9 +326,9 @@ void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,
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)
+int rh_attach_region(struct _rh_info *info, unsigned long start, int size)
{
- rh_block_t *blk;
+ struct _rh_block *blk;
unsigned long s, e, m;
int r;
@@ -367,10 +367,11 @@ int rh_attach_region(rh_info_t * info, unsigned long start, int size)
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)
+unsigned long rh_detach_region(struct _rh_info *info, unsigned long start,
+ int size)
{
struct list_head *l;
- rh_block_t *blk, *newblk;
+ struct _rh_block *blk, *newblk;
unsigned long s, e, m, bs, be;
/* Validate size */
@@ -393,7 +394,7 @@ unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size)
blk = NULL;
list_for_each(l, &info->free_list) {
- blk = list_entry(l, rh_block_t, 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;
@@ -439,11 +440,12 @@ EXPORT_SYMBOL_GPL(rh_detach_region);
* 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)
+unsigned long rh_alloc_align(struct _rh_info *info, int size,
+ int alignment, const char *owner)
{
struct list_head *l;
- rh_block_t *blk;
- rh_block_t *newblk;
+ struct _rh_block *blk;
+ struct _rh_block *newblk;
unsigned long start, sp_size;
/* Validate size, and alignment must be power of two */
@@ -458,7 +460,7 @@ unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const ch
blk = NULL;
list_for_each(l, &info->free_list) {
- blk = list_entry(l, rh_block_t, 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)
@@ -480,7 +482,7 @@ unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const ch
/* Create block for fragment in the beginning */
sp_size = start - blk->start;
if (sp_size) {
- rh_block_t *spblk;
+ struct _rh_block *spblk;
spblk = get_slot(info);
spblk->start = blk->start;
@@ -514,7 +516,7 @@ EXPORT_SYMBOL_GPL(rh_alloc_align);
* 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)
+unsigned long rh_alloc(struct _rh_info *info, int size, const char *owner)
{
return rh_alloc_align(info, size, info->alignment, owner);
}
@@ -524,10 +526,11 @@ EXPORT_SYMBOL_GPL(rh_alloc);
* 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)
+unsigned long rh_alloc_fixed(struct _rh_info *info, unsigned long start,
+ int size, const char *owner)
{
struct list_head *l;
- rh_block_t *blk, *newblk1, *newblk2;
+ struct _rh_block *blk, *newblk1, *newblk2;
unsigned long s, e, m, bs = 0, be = 0;
/* Validate size */
@@ -550,7 +553,7 @@ unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, co
blk = NULL;
list_for_each(l, &info->free_list) {
- blk = list_entry(l, rh_block_t, 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;
@@ -609,16 +612,16 @@ EXPORT_SYMBOL_GPL(rh_alloc_fixed);
* 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)
+int rh_free(struct _rh_info *info, unsigned long start)
{
- rh_block_t *blk, *blk2;
+ 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, rh_block_t, list);
+ blk2 = list_entry(l, struct _rh_block, list);
if (start < blk2->start)
break;
blk = blk2;
@@ -638,9 +641,10 @@ int rh_free(rh_info_t * info, unsigned long start)
}
EXPORT_SYMBOL_GPL(rh_free);
-int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
+int rh_get_stats(struct _rh_info *info, int what, int max_stats,
+ struct _rh_stats *stats)
{
- rh_block_t *blk;
+ struct _rh_block *blk;
struct list_head *l;
struct list_head *h;
int nr;
@@ -662,7 +666,7 @@ int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
/* Linear search for block */
nr = 0;
list_for_each(l, h) {
- blk = list_entry(l, rh_block_t, list);
+ blk = list_entry(l, struct _rh_block, list);
if (stats != NULL && nr < max_stats) {
stats->start = blk->start;
stats->size = blk->size;
@@ -676,16 +680,16 @@ int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
}
EXPORT_SYMBOL_GPL(rh_get_stats);
-int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner)
+int rh_set_owner(struct _rh_info *info, unsigned long start, const char *owner)
{
- rh_block_t *blk, *blk2;
+ 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, rh_block_t, list);
+ blk2 = list_entry(l, struct _rh_block, list);
if (start < blk2->start)
break;
blk = blk2;
@@ -701,46 +705,42 @@ int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner)
}
EXPORT_SYMBOL_GPL(rh_set_owner);
-void rh_dump(rh_info_t * info)
+void rh_dump(struct _rh_info *info)
{
- static rh_stats_t st[32]; /* XXX maximum 32 blocks */
+ static struct _rh_stats 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",
+ pr_info("info @0x%p (%d slots empty / %d max)\n",
info, info->empty_slots, info->max_blocks);
- printk(KERN_INFO " Free:\n");
+ pr_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",
+ pr_info(" 0x%lx-0x%lx (%u)\n",
st[i].start, st[i].start + st[i].size,
st[i].size);
- printk(KERN_INFO "\n");
+ pr_info("\n");
- printk(KERN_INFO " Taken:\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++)
- printk(KERN_INFO
- " 0x%lx-0x%lx (%u) %s\n",
+ 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 : "");
- printk(KERN_INFO "\n");
+ pr_info("\n");
}
EXPORT_SYMBOL_GPL(rh_dump);
-void rh_dump_blk(rh_info_t * info, rh_block_t * blk)
+void rh_dump_blk(struct _rh_info *info, struct _rh_block *blk)
{
- printk(KERN_INFO
- "blk @0x%p: 0x%lx-0x%lx (%u)\n",
+ 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);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a210467..f1fe6e6 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1962,6 +1962,9 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
+ int i;
+ char tmp;
+
if (substream->ops->copy) {
if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
return err;
@@ -1969,6 +1972,22 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
+
+ if (substream->data_swapped) {
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ for (i = 0;
+ i < frames_to_bytes(runtime, frames);
+ i = i + 2) {
+ tmp = *(hwbuf + i);
+ *(hwbuf + i) = *(hwbuf + i + 1);
+ *(hwbuf + i + 1) = tmp;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
}
return 0;
}
@@ -2184,11 +2203,30 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
+ int i;
+ char tmp;
+
if (substream->ops->copy) {
if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+ if (substream->data_swapped) {
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ for (i = 0;
+ i < frames_to_bytes(runtime, frames);
+ i = i + 2) {
+ tmp = *(hwbuf + i);
+ *(hwbuf + i) = *(hwbuf + i + 1);
+ *(hwbuf + i + 1) = tmp;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index b7ab71f..5acb70b 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,3 +1,8 @@
+config SND_SOC_FSL_SAI
+ tristate
+ select REGMAP_MMIO
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
config SND_SOC_FSL_SSI
tristate
@@ -213,3 +218,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 8db705b..96110d1 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -10,16 +10,22 @@ 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
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/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
new file mode 100644
index 0000000..ff86daf
--- /dev/null
+++ b/sound/soc/fsl/fsl_sai.c
@@ -0,0 +1,609 @@
+/*
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_sai.h"
+
+static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int fsl_dir)
+{
+ 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;
+ else
+ reg_cr2 = FSL_SAI_RCR2;
+
+ regmap_read(sai->regmap, reg_cr2, &val_cr2);
+
+ val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+
+ switch (clk_id) {
+ case FSL_SAI_CLK_BUS:
+ val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
+ break;
+ case FSL_SAI_CLK_MAST1:
+ val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
+ break;
+ case FSL_SAI_CLK_MAST2:
+ val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
+ break;
+ case FSL_SAI_CLK_MAST3:
+ val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(sai->regmap, reg_cr2, val_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;
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ 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);
+ 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 rx sysclk: %d\n", ret);
+
+ return ret;
+}
+
+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_cr4, reg_cr2, reg_cr4;
+
+ if (fsl_dir == FSL_FMT_TRANSMITTER) {
+ reg_cr2 = FSL_SAI_TCR2;
+ reg_cr4 = FSL_SAI_TCR4;
+ } else {
+ reg_cr2 = FSL_SAI_RCR2;
+ reg_cr4 = FSL_SAI_RCR4;
+ }
+
+ 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;
+ else
+ val_cr4 |= FSL_SAI_CR4_MF;
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /*
+ * 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:
+ /*
+ * 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:
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ val_cr2 ^= FSL_SAI_CR2_BCP;
+ val_cr4 ^= FSL_SAI_CR4_FSP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ val_cr2 ^= FSL_SAI_CR2_BCP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ val_cr4 ^= FSL_SAI_CR4_FSP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* 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;
+ 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;
+ 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;
+ }
+
+ 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)
+{
+ int 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);
+ return ret;
+ }
+
+ ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
+ if (ret)
+ dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
+
+ return ret;
+}
+
+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);
+ u32 word_width = snd_pcm_format_width(params_format(params));
+
+ 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;
+ }
+
+ 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 &= ~FSL_SAI_CR5_WNW_MASK;
+ val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
+ val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+
+ 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);
+
+ val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+ if (sai->big_endian_data)
+ 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);
+
+ 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;
+}
+
+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;
+
+ /*
+ * 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,
+ FSL_SAI_CR2_SYNC);
+
+ 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;
+ } else {
+ rcsr |= FSL_SAI_CSR_FRDE;
+ 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:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ tcsr |= FSL_SAI_CSR_TERE;
+ rcsr |= FSL_SAI_CSR_TERE;
+
+ 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:
+ if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
+ tcsr &= ~FSL_SAI_CSR_TERE;
+ rcsr &= ~FSL_SAI_CSR_TERE;
+ }
+
+ regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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;
+
+ if (sai->big_endian_regs)
+ substream->data_swapped = 1;
+
+ 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;
+
+ 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 = {
+ .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);
+
+ 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;
+
+ snd_soc_dai_set_drvdata(cpu_dai, sai);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver fsl_sai_dai = {
+ .probe = fsl_sai_dai_probe,
+ .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 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 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;
+ 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);
+ 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);
+ }
+
+ if (sai->big_endian_regs) {
+ sai->dma_params_rx.addr = res->start + FSL_SAI_RDR + 2;
+ sai->dma_params_tx.addr = res->start + FSL_SAI_TDR + 2;
+ } else {
+ 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;
+
+ 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,
+ &fsl_sai_dmaengine_pcm_config,
+ 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, <Li.Xiubo@freescale.com>");
+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..fc12d96
--- /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 <sound/dmaengine_pcm.h>
+
+#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)
+
+/* 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_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 and Recieve Configuration 1 Register */
+#define FSL_SAI_CR1_RFW_MASK 0x1f
+
+/* SAI Transmit and Recieve Configuration 2 Register */
+#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_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_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_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 regmap *regmap;
+
+ 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;
+};
+
+#endif /* __FSL_SAI_H */
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 <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#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");