diff --git a/Makefile b/Makefile index 30d3c5a92..b0b968da3 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,11 @@ KVERSION ?= $(KVERSION_SHORT)-amd64 KERNEL_VERSION ?= 4.9.168 KERNEL_SUBVERSION ?= 1+deb9u5 kernel_procure_method ?= build +CONFIGURED_ARCH ?= amd64 LINUX_HEADER_COMMON = linux-headers-$(KVERSION_SHORT)-common_$(KERNEL_VERSION)-$(KERNEL_SUBVERSION)_all.deb -LINUX_HEADER_AMD64 = linux-headers-$(KVERSION)_$(KERNEL_VERSION)-$(KERNEL_SUBVERSION)_amd64.deb -LINUX_IMAGE = linux-image-$(KVERSION)_$(KERNEL_VERSION)-$(KERNEL_SUBVERSION)_amd64.deb +LINUX_HEADER_AMD64 = linux-headers-$(KVERSION)_$(KERNEL_VERSION)-$(KERNEL_SUBVERSION)_$(CONFIGURED_ARCH).deb +LINUX_IMAGE = linux-image-$(KVERSION)_$(KERNEL_VERSION)-$(KERNEL_SUBVERSION)_$(CONFIGURED_ARCH).deb MAIN_TARGET = $(LINUX_HEADER_COMMON) DERIVED_TARGETS = $(LINUX_HEADER_AMD64) $(LINUX_IMAGE) @@ -73,9 +74,13 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : debian/bin/gencontrol.py # generate linux build file for amd64_none_amd64 + fakeroot make -f debian/rules.gen setup_armhf_none_armmp + fakeroot make -f debian/rules.gen setup_arm64_none fakeroot make -f debian/rules.gen setup_amd64_none_amd64 # Applying patches and configuration changes + git add debian/build/build_armhf_none_armmp/.config -f + git add debian/build/build_arm64_none_arm64/.config -f git add debian/build/build_amd64_none_amd64/.config -f git add debian/config.defines.dump -f git add debian/control -f @@ -89,7 +94,11 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : # Building a custom kernel from Debian kernel source DO_DOCS=False fakeroot make -f debian/rules -j $(shell nproc) binary-indep - fakeroot make -f debian/rules.gen -j $(shell nproc) binary-arch_amd64_none +ifeq ($(CONFIGURED_ARCH), armhf) + fakeroot make -f debian/rules.gen -j $(shell nproc) binary-arch_$(CONFIGURED_ARCH)_none_armmp +else + fakeroot make -f debian/rules.gen -j $(shell nproc) binary-arch_$(CONFIGURED_ARCH)_none +endif popd ifneq ($(DEST),) diff --git a/patch/0001-Marvell-support-for-armada7020-on-LK4.9.168.patch b/patch/0001-Marvell-support-for-armada7020-on-LK4.9.168.patch new file mode 100644 index 000000000..ede52b3c6 --- /dev/null +++ b/patch/0001-Marvell-support-for-armada7020-on-LK4.9.168.patch @@ -0,0 +1,117374 @@ +From c08b47b0037266c4854a8ddbc13342fd77574bc0 Mon Sep 17 00:00:00 2001 +From: gilt +Date: Mon, 26 Aug 2019 10:08:44 +0300 +Subject: [PATCH 1/6] Marvell support for armada7020 on LK4.9.168 + +Change-Id: I603a3b832676d1284f0da9c373a6b96b07c733d2 +--- + Documentation/ABI/testing/sysfs-class-net-phydev | 36 + + Documentation/devicetree/bindings/net/ethernet.txt | 5 + + .../devicetree/bindings/net/marvell-pp2.txt | 91 +- + .../devicetree/bindings/rtc/armada-380-rtc.txt | 8 +- + Documentation/devicetree/bindings/usb/usb-xhci.txt | 5 +- + arch/arm64/Kconfig.platforms | 2 + + arch/arm64/boot/dts/marvell/Makefile | 1 + + arch/arm64/boot/dts/marvell/armada-7020-amc.dts | 140 + + arch/arm64/boot/dts/marvell/armada-7040-db.dts | 101 + + arch/arm64/boot/dts/marvell/armada-8020.dtsi | 10 + + arch/arm64/boot/dts/marvell/armada-8040-db.dts | 52 + + arch/arm64/boot/dts/marvell/armada-8040.dtsi | 9 + + arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 25 +- + .../boot/dts/marvell/armada-cp110-master.dtsi | 295 +- + .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 281 +- + drivers/clk/mvebu/ap806-system-controller.c | 21 +- + drivers/irqchip/Kconfig | 6 + + drivers/irqchip/Makefile | 2 + + drivers/irqchip/irq-mvebu-gicp.c | 280 + + drivers/irqchip/irq-mvebu-gicp.h | 11 + + drivers/irqchip/irq-mvebu-icu.c | 289 + + drivers/mmc/core/core.c | 11 +- + drivers/mmc/core/mmc_ops.c | 25 + + drivers/mmc/host/Kconfig | 8 + + drivers/mmc/host/Makefile | 3 + + drivers/mmc/host/dw_mmc.c | 3 +- + drivers/mmc/host/jz4740_mmc.c | 3 +- + drivers/mmc/host/mmci.c | 3 +- + drivers/mmc/host/mtk-sd.c | 3 +- + drivers/mmc/host/omap_hsmmc.c | 3 +- + drivers/mmc/host/rtsx_pci_sdmmc.c | 3 +- + drivers/mmc/host/sdhci-xenon-phy.c | 825 ++ + drivers/mmc/host/sdhci-xenon.c | 551 ++ + drivers/mmc/host/sdhci-xenon.h | 100 + + drivers/mmc/host/sdhci.c | 424 +- + drivers/mmc/host/sdhci.h | 6 + + drivers/mtd/devices/docg3.c | 49 +- + drivers/mtd/devices/docg3.h | 2 - + drivers/mtd/mtdcore.c | 16 + + drivers/mtd/mtdswap.c | 18 +- + drivers/mtd/nand/Kconfig | 573 +- + drivers/mtd/nand/Makefile | 60 +- + drivers/mtd/nand/cavium/Kconfig | 28 + + drivers/mtd/nand/cavium/Makefile | 2 + + drivers/mtd/nand/cavium/bch_common.h | 50 + + drivers/mtd/nand/cavium/bch_pf.c | 322 + + drivers/mtd/nand/cavium/bch_pf.h | 29 + + drivers/mtd/nand/cavium/bch_regs.h | 222 + + drivers/mtd/nand/cavium/bch_vf.c | 316 + + drivers/mtd/nand/cavium/bch_vf.h | 65 + + drivers/mtd/nand/cavium/cavium_nand.c | 2108 ++++++ + drivers/mtd/nand/nand_base.c | 393 +- + drivers/mtd/nand/nand_timings.c | 26 +- + drivers/mtd/nand/nandsim.c | 86 +- + drivers/mtd/nand/pxa3xx_nand.c | 63 +- + drivers/mtd/nand/raw/Kconfig | 580 ++ + drivers/mtd/nand/raw/Makefile | 68 + + drivers/mtd/nand/raw/ams-delta.c | 290 + + drivers/mtd/nand/raw/atmel/Makefile | 4 + + drivers/mtd/nand/raw/atmel/nand-controller.c | 2567 +++++++ + drivers/mtd/nand/raw/atmel/pmecc.c | 1011 +++ + drivers/mtd/nand/raw/atmel/pmecc.h | 73 + + drivers/mtd/nand/raw/au1550nd.c | 517 ++ + drivers/mtd/nand/raw/bcm47xxnflash/Makefile | 4 + + drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h | 26 + + drivers/mtd/nand/raw/bcm47xxnflash/main.c | 81 + + drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c | 456 ++ + drivers/mtd/nand/raw/bf5xx_nand.c | 862 +++ + drivers/mtd/nand/raw/brcmnand/Makefile | 8 + + drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c | 109 + + drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c | 142 + + drivers/mtd/nand/raw/brcmnand/brcmnand.c | 2617 +++++++ + drivers/mtd/nand/raw/brcmnand/brcmnand.h | 74 + + drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c | 44 + + drivers/mtd/nand/raw/brcmnand/iproc_nand.c | 160 + + drivers/mtd/nand/raw/cafe_nand.c | 891 +++ + drivers/mtd/nand/raw/cmx270_nand.c | 246 + + drivers/mtd/nand/raw/cs553x_nand.c | 357 + + drivers/mtd/nand/raw/davinci_nand.c | 879 +++ + drivers/mtd/nand/raw/denali.c | 1451 ++++ + drivers/mtd/nand/raw/denali.h | 339 + + drivers/mtd/nand/raw/denali_dt.c | 167 + + drivers/mtd/nand/raw/denali_pci.c | 130 + + drivers/mtd/nand/raw/diskonchip.c | 1711 +++++ + drivers/mtd/nand/raw/docg4.c | 1421 ++++ + drivers/mtd/nand/raw/fsl_elbc_nand.c | 979 +++ + drivers/mtd/nand/raw/fsl_ifc_nand.c | 1114 +++ + drivers/mtd/nand/raw/fsl_upm.c | 363 + + drivers/mtd/nand/raw/fsmc_nand.c | 1175 +++ + drivers/mtd/nand/raw/gpio.c | 327 + + drivers/mtd/nand/raw/gpmi-nand/Makefile | 3 + + drivers/mtd/nand/raw/gpmi-nand/bch-regs.h | 128 + + drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 1510 ++++ + drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2182 ++++++ + drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 315 + + drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h | 187 + + drivers/mtd/nand/raw/hisi504_nand.c | 896 +++ + drivers/mtd/nand/raw/jz4740_nand.c | 536 ++ + drivers/mtd/nand/raw/jz4780_bch.c | 380 + + drivers/mtd/nand/raw/jz4780_bch.h | 43 + + drivers/mtd/nand/raw/jz4780_nand.c | 417 + + drivers/mtd/nand/raw/lpc32xx_mlc.c | 909 +++ + drivers/mtd/nand/raw/lpc32xx_slc.c | 1032 +++ + drivers/mtd/nand/raw/marvell_nand.c | 3077 ++++++++ + drivers/mtd/nand/raw/mpc5121_nfc.c | 857 +++ + drivers/mtd/nand/raw/mtk_ecc.c | 544 ++ + drivers/mtd/nand/raw/mtk_ecc.h | 50 + + drivers/mtd/nand/raw/mtk_nand.c | 1576 ++++ + drivers/mtd/nand/raw/mxc_nand.c | 1856 +++++ + drivers/mtd/nand/raw/nand_amd.c | 51 + + drivers/mtd/nand/raw/nand_base.c | 6679 ++++++++++++++++ + drivers/mtd/nand/raw/nand_bbt.c | 1452 ++++ + drivers/mtd/nand/raw/nand_bch.c | 234 + + drivers/mtd/nand/raw/nand_ecc.c | 533 ++ + drivers/mtd/nand/raw/nand_hynix.c | 676 ++ + drivers/mtd/nand/raw/nand_ids.c | 221 + + drivers/mtd/nand/raw/nand_macronix.c | 30 + + drivers/mtd/nand/raw/nand_micron.c | 289 + + drivers/mtd/nand/raw/nand_samsung.c | 115 + + drivers/mtd/nand/raw/nand_timings.c | 324 + + drivers/mtd/nand/raw/nand_toshiba.c | 51 + + drivers/mtd/nand/raw/nandsim.c | 2392 ++++++ + drivers/mtd/nand/raw/ndfc.c | 286 + + drivers/mtd/nand/raw/nuc900_nand.c | 306 + + drivers/mtd/nand/raw/omap2.c | 2338 ++++++ + drivers/mtd/nand/raw/omap_elm.c | 578 ++ + drivers/mtd/nand/raw/orion_nand.c | 234 + + drivers/mtd/nand/raw/oxnas_nand.c | 206 + + drivers/mtd/nand/raw/pasemi_nand.c | 232 + + drivers/mtd/nand/raw/plat_nand.c | 144 + + drivers/mtd/nand/raw/pxa3xx_nand.c | 2105 ++++++ + drivers/mtd/nand/raw/qcom_nandc.c | 2827 +++++++ + drivers/mtd/nand/raw/r852.c | 1079 +++ + drivers/mtd/nand/raw/r852.h | 160 + + drivers/mtd/nand/raw/s3c2410.c | 1296 ++++ + drivers/mtd/nand/raw/sh_flctl.c | 1253 +++ + drivers/mtd/nand/raw/sharpsl.c | 235 + + drivers/mtd/nand/raw/sm_common.c | 202 + + drivers/mtd/nand/raw/sm_common.h | 61 + + drivers/mtd/nand/raw/socrates_nand.c | 243 + + drivers/mtd/nand/raw/sunxi_nand.c | 2321 ++++++ + drivers/mtd/nand/raw/tango_nand.c | 688 ++ + drivers/mtd/nand/raw/tmio_nand.c | 513 ++ + drivers/mtd/nand/raw/txx9ndfmc.c | 423 ++ + drivers/mtd/nand/raw/vf610_nfc.c | 845 +++ + drivers/mtd/nand/raw/xway_nand.c | 245 + + drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 1 - + drivers/net/ethernet/freescale/fec_main.c | 1 - + drivers/net/ethernet/marvell/Kconfig | 24 +- + drivers/net/ethernet/marvell/Makefile | 1 + + drivers/net/ethernet/marvell/mv643xx_eth.c | 2 - + drivers/net/ethernet/marvell/mvneta.c | 3 - + drivers/net/ethernet/marvell/mvpp2.c | 4747 +++++++++--- + drivers/net/ethernet/marvell/mvpp2x/Makefile | 5 + + drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.c | 3564 +++++++++ + drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.h | 478 ++ + .../ethernet/marvell/mvpp2x/mv_gop110_hw_type.h | 1989 +++++ + drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h | 1017 +++ + .../net/ethernet/marvell/mvpp2x/mv_pp2x_debug.c | 99 + + .../net/ethernet/marvell/mvpp2x/mv_pp2x_debug.h | 33 + + .../net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c | 1343 ++++ + drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.c | 6772 +++++++++++++++++ + drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.h | 817 ++ + .../net/ethernet/marvell/mvpp2x/mv_pp2x_hw_type.h | 3038 ++++++++ + drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c | 7966 ++++++++++++++++++++ + drivers/net/ethernet/nuvoton/w90p910_ether.c | 1 - + drivers/of/irq.c | 5 +- + drivers/rtc/rtc-armada38x.c | 446 +- + drivers/usb/host/xhci-plat.c | 31 +- + drivers/usb/host/xhci.h | 3 +- + .../dt-bindings/interrupt-controller/mvebu-icu.h | 15 + + include/dt-bindings/phy/phy-comphy-mvebu.h | 140 + + include/linux/mmc/core.h | 1 + + include/linux/mmc/host.h | 3 +- + include/linux/mtd/mtd.h | 28 +- + include/linux/mtd/nand.h | 65 +- + include/linux/mtd/rawnand.h | 1691 +++++ + include/linux/netdevice.h | 4 + + include/linux/phy.h | 21 +- + include/linux/phy/phy.h | 4 +- + include/linux/platform_data/mtd-nand-pxa3xx.h | 44 +- + include/net/tso.h | 2 + + include/uapi/linux/if_ether.h | 2 + + net/core/dev.c | 13 +- + 184 files changed, 109574 insertions(+), 2522 deletions(-) + create mode 100644 Documentation/ABI/testing/sysfs-class-net-phydev + create mode 100644 arch/arm64/boot/dts/marvell/armada-7020-amc.dts + create mode 100644 drivers/irqchip/irq-mvebu-gicp.c + create mode 100644 drivers/irqchip/irq-mvebu-gicp.h + create mode 100644 drivers/irqchip/irq-mvebu-icu.c + create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c + create mode 100644 drivers/mmc/host/sdhci-xenon.c + create mode 100644 drivers/mmc/host/sdhci-xenon.h + create mode 100644 drivers/mtd/nand/cavium/Kconfig + create mode 100644 drivers/mtd/nand/cavium/Makefile + create mode 100644 drivers/mtd/nand/cavium/bch_common.h + create mode 100644 drivers/mtd/nand/cavium/bch_pf.c + create mode 100644 drivers/mtd/nand/cavium/bch_pf.h + create mode 100644 drivers/mtd/nand/cavium/bch_regs.h + create mode 100644 drivers/mtd/nand/cavium/bch_vf.c + create mode 100644 drivers/mtd/nand/cavium/bch_vf.h + create mode 100644 drivers/mtd/nand/cavium/cavium_nand.c + create mode 100644 drivers/mtd/nand/raw/Kconfig + create mode 100644 drivers/mtd/nand/raw/Makefile + create mode 100644 drivers/mtd/nand/raw/ams-delta.c + create mode 100644 drivers/mtd/nand/raw/atmel/Makefile + create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c + create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c + create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h + create mode 100644 drivers/mtd/nand/raw/au1550nd.c + create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/Makefile + create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h + create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/main.c + create mode 100644 drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c + create mode 100644 drivers/mtd/nand/raw/bf5xx_nand.c + create mode 100644 drivers/mtd/nand/raw/brcmnand/Makefile + create mode 100644 drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c + create mode 100644 drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c + create mode 100644 drivers/mtd/nand/raw/brcmnand/brcmnand.c + create mode 100644 drivers/mtd/nand/raw/brcmnand/brcmnand.h + create mode 100644 drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c + create mode 100644 drivers/mtd/nand/raw/brcmnand/iproc_nand.c + create mode 100644 drivers/mtd/nand/raw/cafe_nand.c + create mode 100644 drivers/mtd/nand/raw/cmx270_nand.c + create mode 100644 drivers/mtd/nand/raw/cs553x_nand.c + create mode 100644 drivers/mtd/nand/raw/davinci_nand.c + create mode 100644 drivers/mtd/nand/raw/denali.c + create mode 100644 drivers/mtd/nand/raw/denali.h + create mode 100644 drivers/mtd/nand/raw/denali_dt.c + create mode 100644 drivers/mtd/nand/raw/denali_pci.c + create mode 100644 drivers/mtd/nand/raw/diskonchip.c + create mode 100644 drivers/mtd/nand/raw/docg4.c + create mode 100644 drivers/mtd/nand/raw/fsl_elbc_nand.c + create mode 100644 drivers/mtd/nand/raw/fsl_ifc_nand.c + create mode 100644 drivers/mtd/nand/raw/fsl_upm.c + create mode 100644 drivers/mtd/nand/raw/fsmc_nand.c + create mode 100644 drivers/mtd/nand/raw/gpio.c + create mode 100644 drivers/mtd/nand/raw/gpmi-nand/Makefile + create mode 100644 drivers/mtd/nand/raw/gpmi-nand/bch-regs.h + create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c + create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c + create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h + create mode 100644 drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h + create mode 100644 drivers/mtd/nand/raw/hisi504_nand.c + create mode 100644 drivers/mtd/nand/raw/jz4740_nand.c + create mode 100644 drivers/mtd/nand/raw/jz4780_bch.c + create mode 100644 drivers/mtd/nand/raw/jz4780_bch.h + create mode 100644 drivers/mtd/nand/raw/jz4780_nand.c + create mode 100644 drivers/mtd/nand/raw/lpc32xx_mlc.c + create mode 100644 drivers/mtd/nand/raw/lpc32xx_slc.c + create mode 100644 drivers/mtd/nand/raw/marvell_nand.c + create mode 100644 drivers/mtd/nand/raw/mpc5121_nfc.c + create mode 100644 drivers/mtd/nand/raw/mtk_ecc.c + create mode 100644 drivers/mtd/nand/raw/mtk_ecc.h + create mode 100644 drivers/mtd/nand/raw/mtk_nand.c + create mode 100644 drivers/mtd/nand/raw/mxc_nand.c + create mode 100644 drivers/mtd/nand/raw/nand_amd.c + create mode 100644 drivers/mtd/nand/raw/nand_base.c + create mode 100644 drivers/mtd/nand/raw/nand_bbt.c + create mode 100644 drivers/mtd/nand/raw/nand_bch.c + create mode 100644 drivers/mtd/nand/raw/nand_ecc.c + create mode 100644 drivers/mtd/nand/raw/nand_hynix.c + create mode 100644 drivers/mtd/nand/raw/nand_ids.c + create mode 100644 drivers/mtd/nand/raw/nand_macronix.c + create mode 100644 drivers/mtd/nand/raw/nand_micron.c + create mode 100644 drivers/mtd/nand/raw/nand_samsung.c + create mode 100644 drivers/mtd/nand/raw/nand_timings.c + create mode 100644 drivers/mtd/nand/raw/nand_toshiba.c + create mode 100644 drivers/mtd/nand/raw/nandsim.c + create mode 100644 drivers/mtd/nand/raw/ndfc.c + create mode 100644 drivers/mtd/nand/raw/nuc900_nand.c + create mode 100644 drivers/mtd/nand/raw/omap2.c + create mode 100644 drivers/mtd/nand/raw/omap_elm.c + create mode 100644 drivers/mtd/nand/raw/orion_nand.c + create mode 100644 drivers/mtd/nand/raw/oxnas_nand.c + create mode 100644 drivers/mtd/nand/raw/pasemi_nand.c + create mode 100644 drivers/mtd/nand/raw/plat_nand.c + create mode 100644 drivers/mtd/nand/raw/pxa3xx_nand.c + create mode 100644 drivers/mtd/nand/raw/qcom_nandc.c + create mode 100644 drivers/mtd/nand/raw/r852.c + create mode 100644 drivers/mtd/nand/raw/r852.h + create mode 100644 drivers/mtd/nand/raw/s3c2410.c + create mode 100644 drivers/mtd/nand/raw/sh_flctl.c + create mode 100644 drivers/mtd/nand/raw/sharpsl.c + create mode 100644 drivers/mtd/nand/raw/sm_common.c + create mode 100644 drivers/mtd/nand/raw/sm_common.h + create mode 100644 drivers/mtd/nand/raw/socrates_nand.c + create mode 100644 drivers/mtd/nand/raw/sunxi_nand.c + create mode 100644 drivers/mtd/nand/raw/tango_nand.c + create mode 100644 drivers/mtd/nand/raw/tmio_nand.c + create mode 100644 drivers/mtd/nand/raw/txx9ndfmc.c + create mode 100644 drivers/mtd/nand/raw/vf610_nfc.c + create mode 100644 drivers/mtd/nand/raw/xway_nand.c + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/Makefile + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.c + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.h + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw_type.h + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.c + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.h + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.c + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.h + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw_type.h + create mode 100644 drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c + create mode 100644 include/dt-bindings/interrupt-controller/mvebu-icu.h + create mode 100644 include/dt-bindings/phy/phy-comphy-mvebu.h + create mode 100644 include/linux/mtd/rawnand.h + +diff --git a/Documentation/ABI/testing/sysfs-class-net-phydev b/Documentation/ABI/testing/sysfs-class-net-phydev +new file mode 100644 +index 00000000..6ebabfb +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-class-net-phydev +@@ -0,0 +1,36 @@ ++What: /sys/class/mdio_bus///attached_dev ++Date: May 2017 ++KernelVersion: 4.13 ++Contact: netdev@vger.kernel.org ++Description: ++ Symbolic link to the network device this PHY device is ++ attached to. ++ ++What: /sys/class/mdio_bus///phy_has_fixups ++Date: February 2014 ++KernelVersion: 3.15 ++Contact: netdev@vger.kernel.org ++Description: ++ Boolean value indicating whether the PHY device has ++ any fixups registered against it (phy_register_fixup) ++ ++What: /sys/class/mdio_bus///phy_id ++Date: November 2012 ++KernelVersion: 3.8 ++Contact: netdev@vger.kernel.org ++Description: ++ 32-bit hexadecimal value corresponding to the PHY device's OUI, ++ model and revision number. ++ ++What: /sys/class/mdio_bus///phy_interface ++Date: February 2014 ++KernelVersion: 3.15 ++Contact: netdev@vger.kernel.org ++Description: ++ String value indicating the PHY interface, possible ++ values are:. ++ (not available), mii, gmii, sgmii, tbi, rev-mii, ++ rmii, rgmii, rgmii-id, rgmii-rxid, rgmii-txid, rtbi, smii ++ xgmii, moca, qsgmii, trgmii, 1000base-x, 2500base-x, rxaui, ++ xaui, 10gbase-kr, unknown ++ +diff --git a/Documentation/devicetree/bindings/net/ethernet.txt b/Documentation/devicetree/bindings/net/ethernet.txt +index 05150957..d4abe9a 100644 +--- a/Documentation/devicetree/bindings/net/ethernet.txt ++++ b/Documentation/devicetree/bindings/net/ethernet.txt +@@ -29,6 +29,11 @@ The following properties are common to the Ethernet controllers: + * "smii" + * "xgmii" + * "trgmii" ++ * "2000base-x", ++ * "2500base-x", ++ * "rxaui" ++ * "xaui" ++ * "10gbase-kr" (10GBASE-KR, XFI, SFI) + - phy-connection-type: the same as "phy-mode" property but described in ePAPR; + - phy-handle: phandle, specifies a reference to a node representing a PHY + device; this property is described in ePAPR and so preferred; +diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt +index aa4f423..b7da08e 100644 +--- a/Documentation/devicetree/bindings/net/marvell-pp2.txt ++++ b/Documentation/devicetree/bindings/net/marvell-pp2.txt +@@ -1,17 +1,29 @@ +-* Marvell Armada 375 Ethernet Controller (PPv2) ++* Marvell Armada 375 Ethernet Controller (PPv2.1) ++ Marvell Armada 7K/8K Ethernet Controller (PPv2.2) + + Required properties: + +-- compatible: should be "marvell,armada-375-pp2" ++- compatible: should be one of: ++ "marvell,armada-375-pp2" ++ "marvell,armada-7k-pp2" + - reg: addresses and length of the register sets for the device. +- Must contain the following register sets: ++ For "marvell,armada-375-pp2", must contain the following register ++ sets: + - common controller registers + - LMS registers +- In addition, at least one port register set is required. +-- clocks: a pointer to the reference clocks for this device, consequently: +- - main controller clock +- - GOP clock +-- clock-names: names of used clocks, must be "pp_clk" and "gop_clk". ++ - one register area per Ethernet port ++ For "marvell,armada-7k-pp2", must contain the following register ++ sets: ++ - packet processor registers ++ - networking interfaces registers ++ ++- clocks: pointers to the reference clocks for this device, consequently: ++ - main controller clock (for both armada-375-pp2 and armada-7k-pp2) ++ - GOP clock (for both armada-375-pp2 and armada-7k-pp2) ++ - MG clock (only for armada-7k-pp2) ++ - AXI clock (only for armada-7k-pp2) ++- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk" ++ and "axi_clk" (the 2 latter only for armada-7k-pp2). + + The ethernet ports are represented by subnodes. At least one port is + required. +@@ -19,19 +31,24 @@ required. + Required properties (port): + + - interrupts: interrupt for the port +-- port-id: should be '0' or '1' for ethernet ports, and '2' for the +- loopback port ++- port-id: ID of the port from the MAC point of view ++- gop-port-id: only for marvell,armada-7k-pp2, ID of the port from the ++ GOP (Group Of Ports) point of view. This ID is used to index the ++ per-port registers in the second register area. + - phy-mode: See ethernet.txt file in the same directory + + Optional properties (port): + + - marvell,loopback: port is loopback mode + - phy: a phandle to a phy node defining the PHY address (as the reg +- property, a single integer). Note: if this property isn't present, +- then fixed link is assumed, and the 'fixed-link' property is +- mandatory. ++ property, a single integer). ++- interrupt-names: if more than a single interrupt for rx is given, must ++ be the name associated to the interrupts listed. Valid ++ names are: "tx-cpu0", "tx-cpu1", "tx-cpu2", "tx-cpu3", ++ "rx-shared", "link". ++- marvell,system-controller: a phandle to the system controller. + +-Example: ++Example for marvell,armada-375-pp2: + + ethernet@f0000 { + compatible = "marvell,armada-375-pp2"; +@@ -59,3 +76,49 @@ ethernet@f0000 { + phy-mode = "gmii"; + }; + }; ++ ++Example for marvell,armada-7k-pp2: ++ ++cpm_ethernet: ethernet@0 { ++ compatible = "marvell,armada-7k-pp22"; ++ reg = <0x0 0x100000>, <0x129000 0xb000>; ++ clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, ++ <&cpm_syscon0 1 5>, <&cpm_syscon0 1 18>; ++ clock-names = "pp_clk", "gop_clk", "gp_clk", "axi_clk"; ++ ++ eth0: eth0 { ++ interrupts = , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared"; ++ port-id = <0>; ++ gop-port-id = <0>; ++ }; ++ ++ eth1: eth1 { ++ interrupts = , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared"; ++ port-id = <1>; ++ gop-port-id = <2>; ++ }; ++ ++ eth2: eth2 { ++ interrupts = , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared"; ++ port-id = <2>; ++ gop-port-id = <3>; ++ }; ++}; +diff --git a/Documentation/devicetree/bindings/rtc/armada-380-rtc.txt b/Documentation/devicetree/bindings/rtc/armada-380-rtc.txt +index 2eb9d4e..c3c9a12 100644 +--- a/Documentation/devicetree/bindings/rtc/armada-380-rtc.txt ++++ b/Documentation/devicetree/bindings/rtc/armada-380-rtc.txt +@@ -1,9 +1,11 @@ +-* Real Time Clock of the Armada 38x SoCs ++* Real Time Clock of the Armada 38x/7K/8K SoCs + +-RTC controller for the Armada 38x SoCs ++RTC controller for the Armada 38x, 7K and 8K SoCs + + Required properties: +-- compatible : Should be "marvell,armada-380-rtc" ++- compatible : Should be one of the following: ++ "marvell,armada-380-rtc" for Armada 38x SoC ++ "marvell,armada-8k-rtc" for Aramda 7K/8K SoCs + - reg: a list of base address and size pairs, one for each entry in + reg-names + - reg names: should contain: +diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt +index 7790c81..b982779 100644 +--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt +@@ -24,7 +24,10 @@ Required properties: + - interrupts: one XHCI interrupt should be described here. + + Optional properties: +- - clocks: reference to a clock ++ - clocks: reference to the clocks ++ - clock-names: mandatory if there is a second clock, in this case ++ the name must be "core" for the first clock and "reg" for the ++ second one + - usb3-lpm-capable: determines if platform is USB3 LPM capable + - quirk-broken-port-ped: set if the controller has broken port disable mechanism + +diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms +index 3428a4b..9556259 100644 +--- a/arch/arm64/Kconfig.platforms ++++ b/arch/arm64/Kconfig.platforms +@@ -105,6 +105,8 @@ config ARCH_MVEBU + select ARMADA_AP806_SYSCON + select ARMADA_CP110_SYSCON + select ARMADA_37XX_CLK ++ select MVEBU_GICP ++ select MVEBU_ICU + select MVEBU_ODMI + select MVEBU_PIC + help +diff --git a/arch/arm64/boot/dts/marvell/Makefile b/arch/arm64/boot/dts/marvell/Makefile +index cf39531..55f86c8 100644 +--- a/arch/arm64/boot/dts/marvell/Makefile ++++ b/arch/arm64/boot/dts/marvell/Makefile +@@ -4,6 +4,7 @@ dtb-$(CONFIG_ARCH_BERLIN) += berlin4ct-stb.dtb + + # Mvebu SoC Family + dtb-$(CONFIG_ARCH_MVEBU) += armada-3720-db.dtb ++dtb-$(CONFIG_ARCH_MVEBU) += armada-7020-amc.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-7040-db.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-db.dtb + +diff --git a/arch/arm64/boot/dts/marvell/armada-7020-amc.dts b/arch/arm64/boot/dts/marvell/armada-7020-amc.dts +new file mode 100644 +index 00000000..9fc5f94 +--- /dev/null ++++ b/arch/arm64/boot/dts/marvell/armada-7020-amc.dts +@@ -0,0 +1,140 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright (C) 2019 Marvell Technology Group Ltd. ++ * ++ * Device Tree file for Marvell Armada 7020 AMC board. ++ */ ++ ++#include "armada-7020.dtsi" ++ ++/ { ++ model = "Marvell Armada-7020 AMC board setup"; ++ compatible = "marvell,armada7020-amc", "marvell,armada7020", ++ "marvell,armada-ap806"; ++ ++ memory@0 { ++ device_type = "memory"; ++ reg = <0x0 0x0 0x0 0x40000000>; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ aliases { ++ ethernet0 = &cp0_emac0; ++ ethernet1 = &cp0_emac2; ++ }; ++ ++}; ++ ++&i2c0 { ++ status = "okay"; ++ clock-frequency = <100000>; ++}; ++ ++&spi0 { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&cpm_ethernet { ++ status = "okay"; ++}; ++ ++&cp0_emac0 { ++ status = "okay"; ++ phy-mode = "10gbase-kr"; ++}; ++ ++&cp0_emac2 { ++ status = "okay"; ++ phy = <&phy0>; ++ phy-mode = "rgmii-id"; ++}; ++ ++&cp0_ppv22 { ++ status = "okay"; ++ l4_chksum_jumbo_port = <0>; ++}; ++ ++&cp0_eth0 { ++ status = "okay"; ++}; ++ ++&cp0_eth1 { ++ status = "okay"; ++}; ++ ++&cpm_i2c0 { ++ status = "okay"; ++ clock-frequency = <100000>; ++}; ++ ++&cpm_mdio { ++ status = "okay"; ++ ++ phy0: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&cpm_nand { ++ status = "okay"; ++ ++ nand@0 { ++ reg = <0>; ++ label = "main-storage"; ++ nand-rb = <0>; ++ nand-ecc-mode = "hw"; ++ nand-on-flash-bbt; ++ nand-ecc-strength = <8>; ++ nand-ecc-step-size = <512>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "U-Boot"; ++ reg = <0 0x200000>; ++ }; ++ partition@200000 { ++ label = "Linux"; ++ reg = <0x200000 0xd00000>; ++ }; ++ partition@1000000 { ++ label = "Filesystem"; ++ reg = <0x1000000 0x3f000000>; ++ }; ++ }; ++ }; ++}; ++ ++&cpm_pcie0 { ++ status = "okay"; ++ num-lanes = <4>; ++ num-viewport = <8>; ++ ++ ranges = <0x81000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x00010000 ++ 0x82000000 0x0 0x00000000 0x8 0x00000000 0x2 0x00000000>; ++}; ++ ++&cpm_sata0 { ++ status = "okay"; ++}; ++ ++&cpm_sdhci0 { ++ status = "okay"; ++ bus-width = <4>; ++ no-1-8-v; ++ broken-cd; ++}; ++ ++&cpm_usb3_0 { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/marvell/armada-7040-db.dts b/arch/arm64/boot/dts/marvell/armada-7040-db.dts +index 070b589..7a2fc01 100644 +--- a/arch/arm64/boot/dts/marvell/armada-7040-db.dts ++++ b/arch/arm64/boot/dts/marvell/armada-7040-db.dts +@@ -44,6 +44,7 @@ + * Device Tree file for Marvell Armada 7040 Development board platform + */ + ++#include + #include "armada-7040.dtsi" + + / { +@@ -59,6 +60,40 @@ + device_type = "memory"; + reg = <0x0 0x0 0x0 0x80000000>; + }; ++ ++ aliases { ++ ethernet0 = &cp0_emac0; ++ ethernet1 = &cp0_emac2; ++ ethernet2 = &cp0_emac3; ++ }; ++ ++ cpm_reg_usb3_0_vbus: cpm-usb3-0-vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb3h0-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&expander0 0 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ cpm_reg_usb3_1_vbus: cpm-usb3-1-vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "usb3h1-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&expander0 1 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ cpm_usb3_0_phy: cpm-usb3-0-phy { ++ compatible = "usb-nop-xceiv"; ++ vcc-supply = <&cpm_reg_usb3_0_vbus>; ++ }; ++ ++ cpm_usb3_1_phy: cpm-usb3-1-phy { ++ compatible = "usb-nop-xceiv"; ++ vcc-supply = <&cpm_reg_usb3_1_vbus>; ++ }; + }; + + &i2c0 { +@@ -105,6 +140,14 @@ + &cpm_i2c0 { + status = "okay"; + clock-frequency = <100000>; ++ ++ expander0: pca9555@21 { ++ compatible = "nxp,pca9555"; ++ pinctrl-names = "default"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ reg = <0x21>; ++ }; + }; + + &cpm_spi1 { +@@ -140,9 +183,67 @@ + }; + + &cpm_usb3_0 { ++ usb-phy = <&cpm_usb3_0_phy>; + status = "okay"; + }; + + &cpm_usb3_1 { ++ usb-phy = <&cpm_usb3_1_phy>; ++ status = "okay"; ++}; ++ ++&ap_sdhci0 { ++ status = "okay"; ++ bus-width = <4>; ++ no-1-8-v; ++ non-removable; ++}; ++ ++&cpm_sdhci0 { ++ status = "okay"; ++ bus-width = <4>; ++ no-1-8-v; ++ non-removable; ++}; ++ ++&cpm_mdio { ++ status = "okay"; ++ ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&cp0_emac0 { ++ phy-mode = "10gbase-kr"; /* lane-2 */ ++}; ++&cp0_emac2 { ++ phy-mode = "sgmii"; /* lane-0 */ ++ phy = <&phy0>; ++}; ++&cp0_emac3 { ++ phy-mode = "rgmii-id"; /* rgmii-1 */ ++ phy = <&phy1>; ++}; ++ ++&cp0_ppv22 { ++ status = "okay"; ++ l4_chksum_jumbo_port = <0>; ++}; ++ ++&cp0_eth0 { ++ status = "okay"; ++}; ++&cp0_eth1 { ++ status = "okay"; ++}; ++&cp0_eth2 { ++ status = "okay"; ++}; ++ ++&cpm_crypto { + status = "okay"; + }; +diff --git a/arch/arm64/boot/dts/marvell/armada-8020.dtsi b/arch/arm64/boot/dts/marvell/armada-8020.dtsi +index 048e5cf..7c08f1f 100644 +--- a/arch/arm64/boot/dts/marvell/armada-8020.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-8020.dtsi +@@ -54,3 +54,13 @@ + compatible = "marvell,armada8020", "marvell,armada-ap806-dual", + "marvell,armada-ap806"; + }; ++ ++/* The RTC requires external oscillator. But on Aramda 80x0, the RTC clock ++ * in CP master is not connected (by package) to the oscillator. So ++ * disable it. However, the RTC clock in CP slave is connected to the ++ * oscillator so this one is let enabled. ++ */ ++ ++&cpm_rtc { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/marvell/armada-8040-db.dts b/arch/arm64/boot/dts/marvell/armada-8040-db.dts +index 6e6f182..6af3c39 100644 +--- a/arch/arm64/boot/dts/marvell/armada-8040-db.dts ++++ b/arch/arm64/boot/dts/marvell/armada-8040-db.dts +@@ -124,6 +124,28 @@ + status = "okay"; + }; + ++&cpm_mdio { ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&cpm_ethernet { ++ status = "okay"; ++}; ++ ++&cpm_eth2 { ++ status = "okay"; ++ phy = <&phy1>; ++ phy-mode = "rgmii-id"; ++}; ++ ++&cpm_crypto { ++ status = "okay"; ++}; ++ + /* CON5 on CP1 expansion */ + &cps_pcie2 { + status = "okay"; +@@ -148,3 +170,33 @@ + &cps_usb3_1 { + status = "okay"; + }; ++ ++&cps_mdio { ++ status = "okay"; ++ ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++}; ++ ++&cps_ethernet { ++ status = "okay"; ++}; ++ ++&cps_eth1 { ++ status = "okay"; ++ phy = <&phy0>; ++ phy-mode = "rgmii-id"; ++}; ++ ++&ap_sdhci0 { ++ status = "okay"; ++ bus-width = <4>; ++ non-removable; ++}; ++ ++&cpm_sdhci0 { ++ status = "okay"; ++ bus-width = <8>; ++ non-removable; ++}; +diff --git a/arch/arm64/boot/dts/marvell/armada-8040.dtsi b/arch/arm64/boot/dts/marvell/armada-8040.dtsi +index 9c1b28c..33813a7 100644 +--- a/arch/arm64/boot/dts/marvell/armada-8040.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-8040.dtsi +@@ -54,3 +54,12 @@ + compatible = "marvell,armada8040", "marvell,armada-ap806-quad", + "marvell,armada-ap806"; + }; ++ ++/* The RTC requires external oscillator. But on Aramda 80x0, the RTC clock ++ * in CP master is not connected (by package) to the oscillator. So ++ * disable it. However, the RTC clock in CP slave is connected to the ++ * oscillator so this one is let enabled. ++ */ ++&cpm_rtc { ++ status = "disabled"; ++}; +diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi +index 7b61361..44d8e41 100644 +--- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi +@@ -146,6 +146,13 @@ + marvell,spi-base = <128>, <136>, <144>, <152>; + }; + ++ gicp: gicp@3f0040 { ++ compatible = "marvell,ap806-gicp"; ++ reg = <0x3f0040 0x10>; ++ marvell,spi-ranges = <64 64>, <288 64>; ++ msi-controller; ++ }; ++ + pic: interrupt-controller@3f0100 { + compatible = "marvell,armada-8k-pic"; + reg = <0x3f0100 0x10>; +@@ -159,6 +166,7 @@ + reg = <0x400000 0x1000>, + <0x410000 0x1000>; + msi-parent = <&gic_v2m0>; ++ clocks = <&ap_syscon 3>; + dma-coherent; + }; + +@@ -167,6 +175,7 @@ + reg = <0x420000 0x1000>, + <0x430000 0x1000>; + msi-parent = <&gic_v2m0>; ++ clocks = <&ap_syscon 3>; + dma-coherent; + }; + +@@ -175,6 +184,7 @@ + reg = <0x440000 0x1000>, + <0x450000 0x1000>; + msi-parent = <&gic_v2m0>; ++ clocks = <&ap_syscon 3>; + dma-coherent; + }; + +@@ -183,6 +193,7 @@ + reg = <0x460000 0x1000>, + <0x470000 0x1000>; + msi-parent = <&gic_v2m0>; ++ clocks = <&ap_syscon 3>; + dma-coherent; + }; + +@@ -229,13 +240,25 @@ + + }; + ++ ap_sdhci0: sdhci@6e0000 { ++ compatible = "marvell,armada-ap806-sdhci"; ++ reg = <0x6e0000 0x300>; ++ interrupts = ; ++ clock-names = "core"; ++ clocks = <&ap_syscon 4>; ++ dma-coherent; ++ marvell,xenon-phy-slow-mode; ++ status = "disabled"; ++ }; ++ + ap_syscon: system-controller@6f4000 { + compatible = "marvell,ap806-system-controller", + "syscon"; + #clock-cells = <1>; + clock-output-names = "ap-cpu-cluster-0", + "ap-cpu-cluster-1", +- "ap-fixed", "ap-mss"; ++ "ap-fixed", "ap-mss", ++ "ap-emmc"; + reg = <0x6f4000 0x1000>; + }; + }; +diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi +index 602e2c2..7d87d84 100644 +--- a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi +@@ -44,21 +44,210 @@ + * Device Tree file for Marvell Armada CP110 Master. + */ + ++#define ICU_GRP_NSR 0x0 ++ + / { + cp110-master { + #address-cells = <2>; + #size-cells = <2>; + compatible = "simple-bus"; +- interrupt-parent = <&gic>; ++ interrupt-parent = <&cpm_icu>; + ranges; + + config-space { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; +- interrupt-parent = <&gic>; + ranges = <0x0 0x0 0xf2000000 0x2000000>; + ++ cpm_ethernet: ethernet@0 { ++ compatible = "marvell,armada-7k-pp22"; ++ reg = <0x0 0x100000>, <0x129000 0xb000>; ++ clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, <&cpm_syscon0 1 5>, ++ <&cpm_syscon0 1 6>, <&cpm_syscon0 1 18>; ++ clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; ++ status = "disabled"; ++ dma-coherent; ++ ++ cpm_eth0: eth0 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared", "link"; ++ port-id = <0>; ++ gop-port-id = <0>; ++ status = "disabled"; ++ }; ++ ++ cpm_eth1: eth1 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared", "link"; ++ port-id = <1>; ++ gop-port-id = <2>; ++ status = "disabled"; ++ }; ++ ++ cpm_eth2: eth2 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared", "link"; ++ port-id = <2>; ++ gop-port-id = <3>; ++ status = "disabled"; ++ }; ++ }; ++ ++ cp0_gop: gop { ++ cp0_emac0: mac0 { ++ interrupts = ; /* Link IRQ */ ++ port-id = <0>; /* gop_port_id */ ++ }; ++ cp0_emac2: mac2 { ++ interrupts = ; /* Link IRQ */ ++ port-id = <2>; /* gop_port_id */ ++ }; ++ cp0_emac3: mac3 { ++ interrupts = ; /* Link IRQ */ ++ port-id = <3>; /* gop_port_id */ ++ }; ++ }; ++ ++ cp0_ppv22: ppv22@000000 { ++ compatible = "marvell,mv-pp22"; ++ reg = <0x000000 0x90000>, /* Packet Processor regs */ ++ <0x129000 0x0600>, /* XMIB regs */ ++ <0x12a000 0x200>, /* LED regs */ ++ <0x12a200 0x200>, /* SMI regs */ ++ <0x12a400 0x200>, /* TAI regs */ ++ <0x12a600 0x200>, /* XSMI regs */ ++ <0x12b000 0x1000>, /* MG Internal regs */ ++ <0x130000 0x6000>, /* MSPG regs */ ++ <0x130400 0x200>, /* MSPG - XPCS regs */ ++ <0x130600 0x200>, /* FCA - flow control regs*/ ++ <0x130e00 0x100>, /* MSPG - GMAC regs */ ++ <0x130f00 0x100>, /* MSPG - XLG MAC regs */ ++ <0x441100 0x100>, /* RFU-1 Regs */ ++ <0x220000 0x800>; /* CM3 SRAM */ ++ reg-names = "pp", "xmib", "led", "smi", "tai", "xsmi", "mg", "mspg", "xpcs", ++ "fca", "gmac", "xlg", "rfu1", "cm3"; ++ clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 18>, <&cpm_syscon0 1 9>, ++ <&cpm_syscon0 1 6>, <&cpm_syscon0 1 5>; ++ clock-names = "pp_clk", "gop_core_clk", "gop_clk", "mg_core_clk", "mg_clk"; ++ dma-coherent; ++ cp0_eth0: eth0@010000 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ port-id = <0>; /* pp2_port_id */ ++ emac-data = <&cp0_emac0>; ++ status = "disabled"; ++ }; ++ cp0_eth1: eth1@020000 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ port-id = <1>; /* pp2_port_id */ ++ emac-data = <&cp0_emac2>; ++ status = "disabled"; ++ }; ++ cp0_eth2: eth2@030000 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ port-id = <2>; /* pp2_port_id */ ++ emac-data = <&cp0_emac3>; ++ status = "disabled"; ++ }; ++ }; ++ ++ cpm_comphy: phy@120000 { ++ compatible = "marvell,comphy-cp110"; ++ reg = <0x120000 0x6000>; ++ marvell,system-controller = <&cpm_syscon0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpm_comphy0: phy@0 { ++ reg = <0>; ++ #phy-cells = <1>; ++ }; ++ ++ cpm_comphy1: phy@1 { ++ reg = <1>; ++ #phy-cells = <1>; ++ }; ++ ++ cpm_comphy2: phy@2 { ++ reg = <2>; ++ #phy-cells = <1>; ++ }; ++ ++ cpm_comphy3: phy@3 { ++ reg = <3>; ++ #phy-cells = <1>; ++ }; ++ ++ cpm_comphy4: phy@4 { ++ reg = <4>; ++ #phy-cells = <1>; ++ }; ++ ++ cpm_comphy5: phy@5 { ++ reg = <5>; ++ #phy-cells = <1>; ++ }; ++ }; ++ ++ cpm_mdio: mdio@12a200 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "marvell,orion-mdio"; ++ reg = <0x12a200 0x10>; ++ clocks = <&cpm_syscon0 1 9>, <&cpm_syscon0 1 5>; ++ status = "disabled"; ++ }; ++ ++ cpm_icu: interrupt-controller@1e0000 { ++ compatible = "marvell,cp110-icu"; ++ reg = <0x1e0000 0x10>; ++ #interrupt-cells = <3>; ++ interrupt-controller; ++ msi-parent = <&gicp>; ++ }; ++ + cpm_syscon0: system-controller@440000 { + compatible = "marvell,cp110-system-controller0", + "syscon"; +@@ -74,36 +263,46 @@ + "cpm-gop-dp", "none", "cpm-pcie_x10", + "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", + "cpm-sata", "cpm-sata-usb", "cpm-main", +- "cpm-sd-mmc", "none", "none", ++ "cpm-sd-mmc-gop", "none", "none", + "cpm-slow-io", "cpm-usb3h0", "cpm-usb3h1", + "cpm-usb3dev", "cpm-eip150", "cpm-eip197"; + }; + ++ cpm_rtc: rtc@284000 { ++ compatible = "marvell,armada-8k-rtc"; ++ reg = <0x284000 0x20>, <0x284080 0x24>; ++ reg-names = "rtc", "rtc-soc"; ++ interrupts = ; ++ }; ++ + cpm_sata0: sata@540000 { +- compatible = "marvell,armada-8k-ahci"; ++ compatible = "marvell,armada-8k-ahci", ++ "generic-ahci"; + reg = <0x540000 0x30000>; +- interrupts = ; ++ interrupts = ; + clocks = <&cpm_syscon0 1 15>; + status = "disabled"; + }; + + cpm_usb3_0: usb3@500000 { +- compatible = "marvell,armada-8k-xhci", +- "generic-xhci"; ++ compatible = "generic-xhci"; + reg = <0x500000 0x4000>; + dma-coherent; +- interrupts = ; +- clocks = <&cpm_syscon0 1 22>; ++ clock-names = "core", "reg"; ++ clocks = <&cpm_syscon0 1 22>, ++ <&cpm_syscon0 1 16>; ++ interrupts = ; + status = "disabled"; + }; + + cpm_usb3_1: usb3@510000 { +- compatible = "marvell,armada-8k-xhci", +- "generic-xhci"; ++ compatible = "generic-xhci"; + reg = <0x510000 0x4000>; + dma-coherent; +- interrupts = ; +- clocks = <&cpm_syscon0 1 23>; ++ clock-names = "core", "reg"; ++ clocks = <&cpm_syscon0 1 23>, ++ <&cpm_syscon0 1 16>; ++ interrupts = ; + status = "disabled"; + }; + +@@ -150,8 +349,8 @@ + reg = <0x701000 0x20>; + #address-cells = <1>; + #size-cells = <0>; +- interrupts = ; + clocks = <&cpm_syscon0 1 21>; ++ interrupts = ; + status = "disabled"; + }; + +@@ -160,10 +359,64 @@ + reg = <0x701100 0x20>; + #address-cells = <1>; + #size-cells = <0>; +- interrupts = ; ++ interrupts = ; + clocks = <&cpm_syscon0 1 21>; + status = "disabled"; + }; ++ ++ cpm_nand: nand@720000 { ++ /* ++ * Due to the limiation of the pin available ++ * this controller is only usable on the CPM ++ * for A7K and on the CPS for A8K. ++ */ ++ compatible = "marvell,armada-8k-nand-controller", ++ "marvell,armada370-nand-controller"; ++ reg = <0x720000 0x54>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = ; ++ clock-names = "core", "reg"; ++ clocks = <&cpm_syscon0 1 2>, ++ <&cpm_syscon0 1 17>; ++ marvell,system-controller = <&cpm_syscon0>; ++ status = "disabled"; ++ }; ++ ++ cpm_trng: trng@760000 { ++ compatible = "marvell,armada-8k-rng", "inside-secure,safexcel-eip76"; ++ reg = <0x760000 0x7d>; ++ clocks = <&cpm_syscon0 1 25>; ++ interrupts = ; ++ status = "okay"; ++ }; ++ ++ cpm_sdhci0: sdhci@780000 { ++ compatible = "marvell,armada-cp110-sdhci"; ++ reg = <0x780000 0x300>; ++ interrupts = ; ++ clock-names = "core"; ++ clocks = <&cpm_syscon0 1 4>; ++ dma-coherent; ++ status = "disabled"; ++ }; ++ ++ cpm_crypto: crypto@800000 { ++ compatible = "inside-secure,safexcel-eip197"; ++ reg = <0x800000 0x200000>; ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "mem", "ring0", "ring1", ++ "ring2", "ring3", "eip"; ++ clocks = <&cpm_syscon0 1 26>; ++ dma-mask = <0xff 0xffffffff>; ++ status = "disabled"; ++ }; + }; + + cpm_pcie0: pcie@f2600000 { +@@ -185,8 +438,8 @@ + /* non-prefetchable memory */ + 0x82000000 0 0xf6000000 0 0xf6000000 0 0xf00000>; + interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; +- interrupts = ; ++ interrupt-map = <0 0 0 0 &cpm_icu 0 ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>; ++ interrupts = ; + num-lanes = <1>; + clocks = <&cpm_syscon0 1 13>; + status = "disabled"; +@@ -211,8 +464,8 @@ + /* non-prefetchable memory */ + 0x82000000 0 0xf7000000 0 0xf7000000 0 0xf00000>; + interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>; +- interrupts = ; ++ interrupt-map = <0 0 0 0 &cpm_icu 0 ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>; ++ interrupts = ; + + num-lanes = <1>; + clocks = <&cpm_syscon0 1 11>; +@@ -238,8 +491,8 @@ + /* non-prefetchable memory */ + 0x82000000 0 0xf8000000 0 0xf8000000 0 0xf00000>; + interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; +- interrupts = ; ++ interrupt-map = <0 0 0 0 &cpm_icu 0 ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>; ++ interrupts = ; + + num-lanes = <1>; + clocks = <&cpm_syscon0 1 12>; +diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi +index 6bf9e24..39c6c27 100644 +--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi +@@ -44,21 +44,217 @@ + * Device Tree file for Marvell Armada CP110 Slave. + */ + ++#define ICU_GRP_NSR 0x0 ++ + / { + cp110-slave { + #address-cells = <2>; + #size-cells = <2>; + compatible = "simple-bus"; +- interrupt-parent = <&gic>; ++ interrupt-parent = <&cps_icu>; + ranges; + + config-space { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; +- interrupt-parent = <&gic>; + ranges = <0x0 0x0 0xf4000000 0x2000000>; + ++ cps_rtc: rtc@284000 { ++ compatible = "marvell,armada-8k-rtc"; ++ reg = <0x284000 0x20>, <0x284080 0x24>; ++ reg-names = "rtc", "rtc-soc"; ++ interrupts = ; ++ }; ++ ++ cps_ethernet: ethernet@0 { ++ compatible = "marvell,armada-7k-pp22"; ++ reg = <0x0 0x100000>, <0x129000 0xb000>; ++ clocks = <&cps_syscon0 1 3>, <&cps_syscon0 1 9>, <&cps_syscon0 1 5>, ++ <&cps_syscon0 1 6>, <&cps_syscon0 1 18>; ++ clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; ++ status = "disabled"; ++ dma-coherent; ++ ++ cps_eth0: eth0 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared", "link"; ++ port-id = <0>; ++ gop-port-id = <0>; ++ status = "disabled"; ++ }; ++ ++ cps_eth1: eth1 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared", "link"; ++ port-id = <1>; ++ gop-port-id = <2>; ++ status = "disabled"; ++ }; ++ ++ cps_eth2: eth2 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2", ++ "tx-cpu3", "rx-shared", "link"; ++ port-id = <2>; ++ gop-port-id = <3>; ++ status = "disabled"; ++ }; ++ }; ++ ++ cp1_gop: gop { ++ cp1_emac0: mac0 { ++ interrupts = ; /* Link IRQ */ ++ port-id = <0>; /* gop_port_id */ ++ }; ++ cp1_emac2: mac2 { ++ interrupts = ; /* Link IRQ */ ++ port-id = <2>; /* gop_port_id */ ++ }; ++ cp1_emac3: mac3 { ++ interrupts = ; /* Link IRQ */ ++ port-id = <3>; /* gop_port_id */ ++ }; ++ }; ++ ++ cp1_ppv22: ppv22@000000 { ++ compatible = "marvell,mv-pp22"; ++ reg = <0x000000 0x90000>, /* Packet Processor regs */ ++ <0x129000 0x0600>, /* XMIB regs */ ++ <0x12a000 0x200>, /* LED regs */ ++ <0x12a200 0x200>, /* SMI regs */ ++ <0x12a400 0x200>, /* TAI regs */ ++ <0x12a600 0x200>, /* XSMI regs */ ++ <0x12b000 0x1000>, /* MG Internal regs */ ++ <0x130000 0x6000>, /* MSPG regs */ ++ <0x130400 0x200>, /* MSPG - XPCS regs */ ++ <0x130600 0x200>, /* FCA - flow control regs*/ ++ <0x130e00 0x100>, /* MSPG - GMAC regs */ ++ <0x130f00 0x100>, /* MSPG - XLG MAC regs */ ++ <0x441100 0x100>, /* RFU-1 Regs */ ++ <0x220000 0x800>; /* CM3 SRAM */ ++ reg-names = "pp", "xmib", "led", "smi", "tai", "xsmi", "mg", "mspg", "xpcs", ++ "fca", "gmac", "xlg", "rfu1", "cm3"; ++ clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 18>, <&cpm_syscon0 1 9>, ++ <&cpm_syscon0 1 6>, <&cpm_syscon0 1 5>; ++ clock-names = "pp_clk", "gop_core_clk", "gop_clk", "mg_core_clk", "mg_clk"; ++ dma-coherent; ++ cp1_eth0: eth0@010000 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ port-id = <0>; /* pp2_port_id */ ++ emac-data = <&cp1_emac0>; ++ status = "disabled"; ++ }; ++ cp1_eth1: eth1@020000 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ port-id = <1>; /* pp2_port_id */ ++ emac-data = <&cp1_emac2>; ++ status = "disabled"; ++ }; ++ cp1_eth2: eth2@030000 { ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ port-id = <2>; /* pp2_port_id */ ++ emac-data = <&cp1_emac3>; ++ status = "disabled"; ++ }; ++ }; ++ ++ cps_comphy: phy@120000 { ++ compatible = "marvell,comphy-cp110"; ++ reg = <0x120000 0x6000>; ++ marvell,system-controller = <&cps_syscon0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cps_comphy0: phy@0 { ++ reg = <0>; ++ #phy-cells = <1>; ++ }; ++ ++ cps_comphy1: phy@1 { ++ reg = <1>; ++ #phy-cells = <1>; ++ }; ++ ++ cps_comphy2: phy@2 { ++ reg = <2>; ++ #phy-cells = <1>; ++ }; ++ ++ cps_comphy3: phy@3 { ++ reg = <3>; ++ #phy-cells = <1>; ++ }; ++ ++ cps_comphy4: phy@4 { ++ reg = <4>; ++ #phy-cells = <1>; ++ }; ++ ++ cps_comphy5: phy@5 { ++ reg = <5>; ++ #phy-cells = <1>; ++ }; ++ }; ++ ++ cps_mdio: mdio@12a200 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "marvell,orion-mdio"; ++ reg = <0x12a200 0x10>; ++ clocks = <&cps_syscon0 1 9>, <&cps_syscon0 1 5>; ++ status = "disabled"; ++ }; ++ ++ cps_icu: interrupt-controller@1e0000 { ++ compatible = "marvell,cp110-icu"; ++ reg = <0x1e0000 0x10>; ++ #interrupt-cells = <3>; ++ interrupt-controller; ++ msi-parent = <&gicp>; ++ }; ++ + cps_syscon0: system-controller@440000 { + compatible = "marvell,cp110-system-controller0", + "syscon"; +@@ -74,36 +270,39 @@ + "cps-gop-dp", "none", "cps-pcie_x10", + "cps-pcie_x11", "cps-pcie_x4", "cps-pcie-xor", + "cps-sata", "cps-sata-usb", "cps-main", +- "cps-sd-mmc", "none", "none", ++ "cps-sd-mmc-gop", "none", "none", + "cps-slow-io", "cps-usb3h0", "cps-usb3h1", + "cps-usb3dev", "cps-eip150", "cps-eip197"; + }; + + cps_sata0: sata@540000 { +- compatible = "marvell,armada-8k-ahci"; ++ compatible = "marvell,armada-8k-ahci", ++ "generic-ahci"; + reg = <0x540000 0x30000>; +- interrupts = ; + clocks = <&cps_syscon0 1 15>; ++ interrupts = ; + status = "disabled"; + }; + + cps_usb3_0: usb3@500000 { +- compatible = "marvell,armada-8k-xhci", +- "generic-xhci"; ++ compatible = "generic-xhci"; + reg = <0x500000 0x4000>; + dma-coherent; +- interrupts = ; +- clocks = <&cps_syscon0 1 22>; ++ clock-names = "core", "reg"; ++ clocks = <&cps_syscon0 1 22>, ++ <&cps_syscon0 1 16>; ++ interrupts = ; + status = "disabled"; + }; + + cps_usb3_1: usb3@510000 { +- compatible = "marvell,armada-8k-xhci", +- "generic-xhci"; ++ compatible = "generic-xhci"; + reg = <0x510000 0x4000>; + dma-coherent; +- interrupts = ; +- clocks = <&cps_syscon0 1 23>; ++ clock-names = "core", "reg"; ++ clocks = <&cps_syscon0 1 23>, ++ <&cps_syscon0 1 16>; ++ interrupts = ; + status = "disabled"; + }; + +@@ -150,8 +349,8 @@ + reg = <0x701000 0x20>; + #address-cells = <1>; + #size-cells = <0>; +- interrupts = ; + clocks = <&cps_syscon0 1 21>; ++ interrupts = ; + status = "disabled"; + }; + +@@ -160,10 +359,50 @@ + reg = <0x701100 0x20>; + #address-cells = <1>; + #size-cells = <0>; +- interrupts = ; ++ interrupts = ; + clocks = <&cps_syscon0 1 21>; + status = "disabled"; + }; ++ ++ cps_nand: nand@720000 { ++ /* ++ * Due to the limiation of the pin available ++ * this controller is only usable on the CPM ++ * for A7K and on the CPS for A8K. ++ */ ++ compatible = "marvell,armada370-nand"; ++ reg = <0x720000 0x54>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ interrupts = ; ++ clocks = <&cps_syscon0 1 2>; ++ status = "disabled"; ++ }; ++ ++ cps_trng: trng@760000 { ++ compatible = "marvell,armada-8k-rng", "inside-secure,safexcel-eip76"; ++ reg = <0x760000 0x7d>; ++ clocks = <&cps_syscon0 1 25>; ++ interrupts = ; ++ status = "okay"; ++ }; ++ ++ cps_crypto: crypto@800000 { ++ compatible = "inside-secure,safexcel-eip197"; ++ reg = <0x800000 0x200000>; ++ interrupts = , ++ , ++ , ++ , ++ , ++ ; ++ interrupt-names = "mem", "ring0", "ring1", ++ "ring2", "ring3", "eip"; ++ clocks = <&cps_syscon0 1 26>; ++ dma-mask = <0xff 0xffffffff>; ++ status = "disabled"; ++ }; + }; + + cps_pcie0: pcie@f4600000 { +@@ -185,8 +424,8 @@ + /* non-prefetchable memory */ + 0x82000000 0 0xfa000000 0 0xfa000000 0 0xf00000>; + interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>; +- interrupts = ; ++ interrupt-map = <0 0 0 0 &cps_icu 0 ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>; ++ interrupts = ; + num-lanes = <1>; + clocks = <&cps_syscon0 1 13>; + status = "disabled"; +@@ -211,8 +450,8 @@ + /* non-prefetchable memory */ + 0x82000000 0 0xfb000000 0 0xfb000000 0 0xf00000>; + interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>; +- interrupts = ; ++ interrupt-map = <0 0 0 0 &cps_icu 0 ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>; ++ interrupts = ; + + num-lanes = <1>; + clocks = <&cps_syscon0 1 11>; +@@ -238,8 +477,8 @@ + /* non-prefetchable memory */ + 0x82000000 0 0xfc000000 0 0xfc000000 0 0xf00000>; + interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>; +- interrupts = ; ++ interrupt-map = <0 0 0 0 &cps_icu 0 ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>; ++ interrupts = ; + + num-lanes = <1>; + clocks = <&cps_syscon0 1 12>; +diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c +index 962e0c5..3c99a1a 100644 +--- a/drivers/clk/mvebu/ap806-system-controller.c ++++ b/drivers/clk/mvebu/ap806-system-controller.c +@@ -23,7 +23,7 @@ + #define AP806_SAR_REG 0x400 + #define AP806_SAR_CLKFREQ_MODE_MASK 0x1f + +-#define AP806_CLK_NUM 4 ++#define AP806_CLK_NUM 5 + + static struct clk *ap806_clks[AP806_CLK_NUM]; + +@@ -135,6 +135,23 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev) + goto fail3; + } + ++ /* eMMC Clock is fixed clock divided by 3 */ ++ if (of_property_read_string_index(np, "clock-output-names", ++ 4, &name)) { ++ ap806_clk_data.clk_num--; ++ dev_warn(&pdev->dev, ++ "eMMC clock mising: update the device tree!\n"); ++ } else { ++ ap806_clks[4] = clk_register_fixed_factor(NULL, name, ++ fixedclk_name, ++ 0, 1, 3); ++ if (IS_ERR(ap806_clks[4])) { ++ ret = PTR_ERR(ap806_clks[4]); ++ goto fail4; ++ } ++ } ++ ++ of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data); + if (ret) + goto fail_clk_add; +@@ -142,6 +159,8 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev) + return 0; + + fail_clk_add: ++ clk_unregister_fixed_factor(ap806_clks[4]); ++fail4: + clk_unregister_fixed_factor(ap806_clks[3]); + fail3: + clk_unregister_fixed_rate(ap806_clks[2]); +diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig +index 910cb5e..d8f7c33 100644 +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -256,6 +256,12 @@ config IRQ_MXS + select IRQ_DOMAIN + select STMP_DEVICE + ++config MVEBU_GICP ++ bool ++ ++config MVEBU_ICU ++ bool ++ + config MVEBU_ODMI + bool + select GENERIC_MSI_IRQ_DOMAIN +diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile +index e4dbfc8..051e0ab 100644 +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -68,6 +68,8 @@ obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o + obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o + obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o + obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o ++obj-$(CONFIG_MVEBU_GICP) += irq-mvebu-gicp.o ++obj-$(CONFIG_MVEBU_ICU) += irq-mvebu-icu.o + obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o + obj-$(CONFIG_MVEBU_PIC) += irq-mvebu-pic.o + obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o +diff --git a/drivers/irqchip/irq-mvebu-gicp.c b/drivers/irqchip/irq-mvebu-gicp.c +new file mode 100644 +index 00000000..17a4a7b +--- /dev/null ++++ b/drivers/irqchip/irq-mvebu-gicp.c +@@ -0,0 +1,280 @@ ++/* ++ * Copyright (C) 2017 Marvell ++ * ++ * Thomas Petazzoni ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "irq-mvebu-gicp.h" ++ ++#define GICP_SETSPI_NSR_OFFSET 0x0 ++#define GICP_CLRSPI_NSR_OFFSET 0x8 ++ ++struct mvebu_gicp_spi_range { ++ unsigned int start; ++ unsigned int count; ++}; ++ ++struct mvebu_gicp { ++ struct mvebu_gicp_spi_range *spi_ranges; ++ unsigned int spi_ranges_cnt; ++ unsigned int spi_cnt; ++ unsigned long *spi_bitmap; ++ spinlock_t spi_lock; ++ struct resource *res; ++ struct device *dev; ++}; ++ ++static int gicp_idx_to_spi(struct mvebu_gicp *gicp, int idx) ++{ ++ int i; ++ ++ for (i = 0; i < gicp->spi_ranges_cnt; i++) { ++ struct mvebu_gicp_spi_range *r = &gicp->spi_ranges[i]; ++ ++ if (idx < r->count) ++ return r->start + idx; ++ ++ idx -= r->count; ++ } ++ ++ return -EINVAL; ++} ++ ++int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi, ++ phys_addr_t *clrspi) ++{ ++ struct platform_device *pdev; ++ struct mvebu_gicp *gicp; ++ ++ pdev = of_find_device_by_node(dn); ++ if (!pdev) ++ return -ENODEV; ++ ++ gicp = platform_get_drvdata(pdev); ++ if (!gicp) ++ return -ENODEV; ++ ++ *setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET; ++ *clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET; ++ ++ return 0; ++} ++ ++static void gicp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++{ ++ struct mvebu_gicp *gicp = data->chip_data; ++ phys_addr_t setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET; ++ ++ msg->data = data->hwirq; ++ msg->address_lo = lower_32_bits(setspi); ++ msg->address_hi = upper_32_bits(setspi); ++} ++ ++static struct irq_chip gicp_irq_chip = { ++ .name = "GICP", ++ .irq_mask = irq_chip_mask_parent, ++ .irq_unmask = irq_chip_unmask_parent, ++ .irq_eoi = irq_chip_eoi_parent, ++ .irq_set_affinity = irq_chip_set_affinity_parent, ++ .irq_set_type = irq_chip_set_type_parent, ++ .irq_compose_msi_msg = gicp_compose_msi_msg, ++}; ++ ++static int gicp_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs, void *args) ++{ ++ struct mvebu_gicp *gicp = domain->host_data; ++ struct irq_fwspec fwspec; ++ unsigned int hwirq; ++ int ret; ++ ++ spin_lock(&gicp->spi_lock); ++ hwirq = find_first_zero_bit(gicp->spi_bitmap, gicp->spi_cnt); ++ if (hwirq == gicp->spi_cnt) { ++ spin_unlock(&gicp->spi_lock); ++ return -ENOSPC; ++ } ++ __set_bit(hwirq, gicp->spi_bitmap); ++ spin_unlock(&gicp->spi_lock); ++ ++ fwspec.fwnode = domain->parent->fwnode; ++ fwspec.param_count = 3; ++ fwspec.param[0] = GIC_SPI; ++ fwspec.param[1] = gicp_idx_to_spi(gicp, hwirq) - 32; ++ /* ++ * Assume edge rising for now, it will be properly set when ++ * ->set_type() is called ++ */ ++ fwspec.param[2] = IRQ_TYPE_EDGE_RISING; ++ ++ ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); ++ if (ret) { ++ dev_err(gicp->dev, "Cannot allocate parent IRQ\n"); ++ goto free_hwirq; ++ } ++ ++ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, ++ &gicp_irq_chip, gicp); ++ if (ret) ++ goto free_irqs_parent; ++ ++ return 0; ++ ++free_irqs_parent: ++ irq_domain_free_irqs_parent(domain, virq, nr_irqs); ++free_hwirq: ++ spin_lock(&gicp->spi_lock); ++ __clear_bit(hwirq, gicp->spi_bitmap); ++ spin_unlock(&gicp->spi_lock); ++ return ret; ++} ++ ++static void gicp_irq_domain_free(struct irq_domain *domain, ++ unsigned int virq, unsigned int nr_irqs) ++{ ++ struct mvebu_gicp *gicp = domain->host_data; ++ struct irq_data *d = irq_domain_get_irq_data(domain, virq); ++ ++ if (d->hwirq >= gicp->spi_cnt) { ++ dev_err(gicp->dev, "Invalid hwirq %lu\n", d->hwirq); ++ return; ++ } ++ ++ irq_domain_free_irqs_parent(domain, virq, nr_irqs); ++ ++ spin_lock(&gicp->spi_lock); ++ __clear_bit(d->hwirq, gicp->spi_bitmap); ++ spin_unlock(&gicp->spi_lock); ++} ++ ++static const struct irq_domain_ops gicp_domain_ops = { ++ .alloc = gicp_irq_domain_alloc, ++ .free = gicp_irq_domain_free, ++}; ++ ++static struct irq_chip gicp_msi_irq_chip = { ++ .name = "GICP", ++ .irq_set_type = irq_chip_set_type_parent, ++}; ++ ++static struct msi_domain_ops gicp_msi_ops = { ++}; ++ ++static struct msi_domain_info gicp_msi_domain_info = { ++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), ++ .ops = &gicp_msi_ops, ++ .chip = &gicp_msi_irq_chip, ++}; ++ ++static int mvebu_gicp_probe(struct platform_device *pdev) ++{ ++ struct mvebu_gicp *gicp; ++ struct irq_domain *inner_domain, *plat_domain, *parent_domain; ++ struct device_node *node = pdev->dev.of_node; ++ struct device_node *irq_parent_dn; ++ int ret, i; ++ ++ gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL); ++ if (!gicp) ++ return -ENOMEM; ++ ++ gicp->dev = &pdev->dev; ++ spin_lock_init(&gicp->spi_lock); ++ ++ gicp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!gicp->res) ++ return -ENODEV; ++ ++ ret = of_property_count_u32_elems(node, "marvell,spi-ranges"); ++ if (ret < 0) ++ return ret; ++ ++ gicp->spi_ranges_cnt = ret / 2; ++ ++ gicp->spi_ranges = ++ devm_kzalloc(&pdev->dev, ++ gicp->spi_ranges_cnt * ++ sizeof(struct mvebu_gicp_spi_range), ++ GFP_KERNEL); ++ if (!gicp->spi_ranges) ++ return -ENOMEM; ++ ++ for (i = 0; i < gicp->spi_ranges_cnt; i++) { ++ of_property_read_u32_index(node, "marvell,spi-ranges", ++ i * 2, ++ &gicp->spi_ranges[i].start); ++ ++ of_property_read_u32_index(node, "marvell,spi-ranges", ++ i * 2 + 1, ++ &gicp->spi_ranges[i].count); ++ ++ gicp->spi_cnt += gicp->spi_ranges[i].count; ++ } ++ ++ gicp->spi_bitmap = devm_kzalloc(&pdev->dev, ++ BITS_TO_LONGS(gicp->spi_cnt) * sizeof(long), ++ GFP_KERNEL); ++ if (!gicp->spi_bitmap) ++ return -ENOMEM; ++ ++ irq_parent_dn = of_irq_find_parent(node); ++ if (!irq_parent_dn) { ++ dev_err(&pdev->dev, "failed to find parent IRQ node\n"); ++ return -ENODEV; ++ } ++ ++ parent_domain = irq_find_host(irq_parent_dn); ++ if (!parent_domain) { ++ dev_err(&pdev->dev, "failed to find parent IRQ domain\n"); ++ return -ENODEV; ++ } ++ ++ inner_domain = irq_domain_create_hierarchy(parent_domain, 0, ++ gicp->spi_cnt, ++ of_node_to_fwnode(node), ++ &gicp_domain_ops, gicp); ++ if (!inner_domain) ++ return -ENOMEM; ++ ++ ++ plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node), ++ &gicp_msi_domain_info, ++ inner_domain); ++ if (!plat_domain) { ++ irq_domain_remove(inner_domain); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, gicp); ++ ++ return 0; ++} ++ ++static const struct of_device_id mvebu_gicp_of_match[] = { ++ { .compatible = "marvell,ap806-gicp", }, ++ {}, ++}; ++ ++static struct platform_driver mvebu_gicp_driver = { ++ .probe = mvebu_gicp_probe, ++ .driver = { ++ .name = "mvebu-gicp", ++ .of_match_table = mvebu_gicp_of_match, ++ }, ++}; ++builtin_platform_driver(mvebu_gicp_driver); +diff --git a/drivers/irqchip/irq-mvebu-gicp.h b/drivers/irqchip/irq-mvebu-gicp.h +new file mode 100644 +index 00000000..98535e8 +--- /dev/null ++++ b/drivers/irqchip/irq-mvebu-gicp.h +@@ -0,0 +1,11 @@ ++#ifndef __MVEBU_GICP_H__ ++#define __MVEBU_GICP_H__ ++ ++#include ++ ++struct device_node; ++ ++int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi, ++ phys_addr_t *clrspi); ++ ++#endif /* __MVEBU_GICP_H__ */ +diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c +new file mode 100644 +index 00000000..e18c48d +--- /dev/null ++++ b/drivers/irqchip/irq-mvebu-icu.c +@@ -0,0 +1,289 @@ ++/* ++ * Copyright (C) 2017 Marvell ++ * ++ * Hanna Hawa ++ * Thomas Petazzoni ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "irq-mvebu-gicp.h" ++ ++/* ICU registers */ ++#define ICU_SETSPI_NSR_AL 0x10 ++#define ICU_SETSPI_NSR_AH 0x14 ++#define ICU_CLRSPI_NSR_AL 0x18 ++#define ICU_CLRSPI_NSR_AH 0x1c ++#define ICU_INT_CFG(x) (0x100 + 4 * (x)) ++#define ICU_INT_ENABLE BIT(24) ++#define ICU_IS_EDGE BIT(28) ++#define ICU_GROUP_SHIFT 29 ++ ++/* ICU definitions */ ++#define ICU_MAX_IRQS 207 ++#define ICU_SATA0_ICU_ID 109 ++#define ICU_SATA1_ICU_ID 107 ++ ++struct mvebu_icu { ++ struct irq_chip irq_chip; ++ void __iomem *base; ++ struct irq_domain *domain; ++ struct device *dev; ++}; ++ ++struct mvebu_icu_irq_data { ++ struct mvebu_icu *icu; ++ unsigned int icu_group; ++ unsigned int type; ++}; ++ ++static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) ++{ ++ struct irq_data *d = irq_get_irq_data(desc->irq); ++ struct mvebu_icu_irq_data *icu_irqd = d->chip_data; ++ struct mvebu_icu *icu = icu_irqd->icu; ++ unsigned int icu_int; ++ ++ if (msg->address_lo || msg->address_hi) { ++ /* Configure the ICU with irq number & type */ ++ icu_int = msg->data | ICU_INT_ENABLE; ++ if (icu_irqd->type & IRQ_TYPE_EDGE_RISING) ++ icu_int |= ICU_IS_EDGE; ++ icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT; ++ } else { ++ /* De-configure the ICU */ ++ icu_int = 0; ++ } ++ ++ writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq)); ++ ++ /* ++ * The SATA unit has 2 ports, and a dedicated ICU entry per ++ * port. The ahci sata driver supports only one irq interrupt ++ * per SATA unit. To solve this conflict, we configure the 2 ++ * SATA wired interrupts in the south bridge into 1 GIC ++ * interrupt in the north bridge. Even if only a single port ++ * is enabled, if sata node is enabled, both interrupts are ++ * configured (regardless of which port is actually in use). ++ */ ++ if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) { ++ writel_relaxed(icu_int, ++ icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID)); ++ writel_relaxed(icu_int, ++ icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID)); ++ } ++} ++ ++static int ++mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, ++ unsigned long *hwirq, unsigned int *type) ++{ ++ struct mvebu_icu *icu = d->host_data; ++ unsigned int icu_group; ++ ++ /* Check the count of the parameters in dt */ ++ if (WARN_ON(fwspec->param_count < 3)) { ++ dev_err(icu->dev, "wrong ICU parameter count %d\n", ++ fwspec->param_count); ++ return -EINVAL; ++ } ++ ++ /* Only ICU group type is handled */ ++ icu_group = fwspec->param[0]; ++ if (icu_group != ICU_GRP_NSR && icu_group != ICU_GRP_SR && ++ icu_group != ICU_GRP_SEI && icu_group != ICU_GRP_REI) { ++ dev_err(icu->dev, "wrong ICU group type %x\n", icu_group); ++ return -EINVAL; ++ } ++ ++ *hwirq = fwspec->param[1]; ++ if (*hwirq >= ICU_MAX_IRQS) { ++ dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq); ++ return -EINVAL; ++ } ++ ++ /* Mask the type to prevent wrong DT configuration */ ++ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; ++ ++ return 0; ++} ++ ++static int ++mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs, void *args) ++{ ++ int err; ++ unsigned long hwirq; ++ struct irq_fwspec *fwspec = args; ++ struct mvebu_icu *icu = platform_msi_get_host_data(domain); ++ struct mvebu_icu_irq_data *icu_irqd; ++ ++ icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL); ++ if (!icu_irqd) ++ return -ENOMEM; ++ ++ err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq, ++ &icu_irqd->type); ++ if (err) { ++ dev_err(icu->dev, "failed to translate ICU parameters\n"); ++ goto free_irqd; ++ } ++ ++ icu_irqd->icu_group = fwspec->param[0]; ++ icu_irqd->icu = icu; ++ ++ err = platform_msi_domain_alloc(domain, virq, nr_irqs); ++ if (err) { ++ dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n"); ++ goto free_irqd; ++ } ++ ++ /* Make sure there is no interrupt left pending by the firmware */ ++ err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false); ++ if (err) ++ goto free_msi; ++ ++ err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, ++ &icu->irq_chip, icu_irqd); ++ if (err) { ++ dev_err(icu->dev, "failed to set the data to IRQ domain\n"); ++ goto free_msi; ++ } ++ ++ return 0; ++ ++free_msi: ++ platform_msi_domain_free(domain, virq, nr_irqs); ++free_irqd: ++ kfree(icu_irqd); ++ return err; ++} ++ ++static void ++mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs) ++{ ++ struct irq_data *d = irq_get_irq_data(virq); ++ struct mvebu_icu_irq_data *icu_irqd = d->chip_data; ++ ++ kfree(icu_irqd); ++ ++ platform_msi_domain_free(domain, virq, nr_irqs); ++} ++ ++static const struct irq_domain_ops mvebu_icu_domain_ops = { ++ .translate = mvebu_icu_irq_domain_translate, ++ .alloc = mvebu_icu_irq_domain_alloc, ++ .free = mvebu_icu_irq_domain_free, ++}; ++ ++static int mvebu_icu_probe(struct platform_device *pdev) ++{ ++ struct mvebu_icu *icu; ++ struct device_node *node = pdev->dev.of_node; ++ struct device_node *gicp_dn; ++ struct resource *res; ++ phys_addr_t setspi, clrspi; ++ u32 i, icu_int; ++ int ret; ++ ++ icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu), ++ GFP_KERNEL); ++ if (!icu) ++ return -ENOMEM; ++ ++ icu->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ icu->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(icu->base)) { ++ dev_err(&pdev->dev, "Failed to map icu base address.\n"); ++ return PTR_ERR(icu->base); ++ } ++ ++ icu->irq_chip.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, ++ "ICU.%x", ++ (unsigned int)res->start); ++ if (!icu->irq_chip.name) ++ return -ENOMEM; ++ ++ icu->irq_chip.irq_mask = irq_chip_mask_parent; ++ icu->irq_chip.irq_unmask = irq_chip_unmask_parent; ++ icu->irq_chip.irq_eoi = irq_chip_eoi_parent; ++ icu->irq_chip.irq_set_type = irq_chip_set_type_parent; ++#ifdef CONFIG_SMP ++ icu->irq_chip.irq_set_affinity = irq_chip_set_affinity_parent; ++#endif ++ ++ /* ++ * We're probed after MSI domains have been resolved, so force ++ * resolution here. ++ */ ++ pdev->dev.msi_domain = of_msi_get_domain(&pdev->dev, node, ++ DOMAIN_BUS_PLATFORM_MSI); ++ if (!pdev->dev.msi_domain) ++ return -EPROBE_DEFER; ++ ++ gicp_dn = irq_domain_get_of_node(pdev->dev.msi_domain); ++ if (!gicp_dn) ++ return -ENODEV; ++ ++ ret = mvebu_gicp_get_doorbells(gicp_dn, &setspi, &clrspi); ++ if (ret) ++ return ret; ++ ++ /* Set Clear/Set ICU SPI message address in AP */ ++ writel_relaxed(upper_32_bits(setspi), icu->base + ICU_SETSPI_NSR_AH); ++ writel_relaxed(lower_32_bits(setspi), icu->base + ICU_SETSPI_NSR_AL); ++ writel_relaxed(upper_32_bits(clrspi), icu->base + ICU_CLRSPI_NSR_AH); ++ writel_relaxed(lower_32_bits(clrspi), icu->base + ICU_CLRSPI_NSR_AL); ++ ++ /* ++ * Clean all ICU interrupts with type SPI_NSR, required to ++ * avoid unpredictable SPI assignments done by firmware. ++ */ ++ for (i = 0 ; i < ICU_MAX_IRQS ; i++) { ++ icu_int = readl(icu->base + ICU_INT_CFG(i)); ++ if ((icu_int >> ICU_GROUP_SHIFT) == ICU_GRP_NSR) ++ writel_relaxed(0x0, icu->base + ICU_INT_CFG(i)); ++ } ++ ++ icu->domain = ++ platform_msi_create_device_domain(&pdev->dev, ICU_MAX_IRQS, ++ mvebu_icu_write_msg, ++ &mvebu_icu_domain_ops, icu); ++ if (!icu->domain) { ++ dev_err(&pdev->dev, "Failed to create ICU domain\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id mvebu_icu_of_match[] = { ++ { .compatible = "marvell,cp110-icu", }, ++ {}, ++}; ++ ++static struct platform_driver mvebu_icu_driver = { ++ .probe = mvebu_icu_probe, ++ .driver = { ++ .name = "mvebu-icu", ++ .of_match_table = mvebu_icu_of_match, ++ }, ++}; ++builtin_platform_driver(mvebu_icu_driver); +diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index cff58297..52c6acb 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -611,18 +611,15 @@ bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq) + * mmc_pre_req - Prepare for a new request + * @host: MMC host to prepare command + * @mrq: MMC request to prepare for +- * @is_first_req: true if there is no previous started request +- * that may run in parellel to this call, otherwise false + * + * mmc_pre_req() is called in prior to mmc_start_req() to let + * host prepare for the new request. Preparation of a request may be + * performed while another request is running on the host. + */ +-static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, +- bool is_first_req) ++static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq) + { + if (host->ops->pre_req) +- host->ops->pre_req(host, mrq, is_first_req); ++ host->ops->pre_req(host, mrq); + } + + /** +@@ -666,7 +663,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, + + /* Prepare a new request */ + if (areq) +- mmc_pre_req(host, areq->mrq, !host->areq); ++ mmc_pre_req(host, areq->mrq); + + if (host->areq) { + err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq); +@@ -695,7 +692,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, + + /* prepare the request again */ + if (areq) +- mmc_pre_req(host, areq->mrq, !host->areq); ++ mmc_pre_req(host, areq->mrq); + } + } + +diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c +index ad6e979..236c5d9 100644 +--- a/drivers/mmc/core/mmc_ops.c ++++ b/drivers/mmc/core/mmc_ops.c +@@ -661,6 +661,31 @@ int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error) + } + EXPORT_SYMBOL_GPL(mmc_send_tuning); + ++int mmc_abort_tuning(struct mmc_host *host, u32 opcode) ++{ ++ struct mmc_command cmd = {0}; ++ ++ /* ++ * eMMC specification specifies that CMD12 can be used to stop a tuning ++ * command, but SD specification does not, so do nothing unless it is ++ * eMMC. ++ */ ++ if (opcode != MMC_SEND_TUNING_BLOCK_HS200) ++ return 0; ++ ++ cmd.opcode = MMC_STOP_TRANSMISSION; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; ++ ++ /* ++ * For drivers that override R1 to R1b, set an arbitrary timeout based ++ * on the tuning timeout i.e. 150ms. ++ */ ++ cmd.busy_timeout = 150; ++ ++ return mmc_wait_for_cmd(host, &cmd, 0); ++} ++EXPORT_SYMBOL_GPL(mmc_abort_tuning); ++ + static int + mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, + u8 len) +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 5274f50..55760d1 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -798,3 +798,11 @@ config MMC_SDHCI_BRCMSTB + Broadcom STB SoCs. + + If unsure, say Y. ++ ++config MMC_SDHCI_XENON ++ tristate "Marvell Xenon eMMC/SD/SDIO SDHCI driver" ++ depends on MMC_SDHCI_PLTFM ++ help ++ This selects Marvell Xenon eMMC/SD/SDIO SDHCI. ++ If you have a controller with this interface, say Y or M here. ++ If unsure, say N. +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index e2bdaaf4..4f28545 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -80,3 +80,6 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG + endif ++ ++obj-$(CONFIG_MMC_SDHCI_XENON) += sdhci-xenon-driver.o ++sdhci-xenon-driver-y += sdhci-xenon.o sdhci-xenon-phy.o +diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c +index e10a00d..35a3544 100644 +--- a/drivers/mmc/host/dw_mmc.c ++++ b/drivers/mmc/host/dw_mmc.c +@@ -885,8 +885,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host, + } + + static void dw_mci_pre_req(struct mmc_host *mmc, +- struct mmc_request *mrq, +- bool is_first_req) ++ struct mmc_request *mrq) + { + struct dw_mci_slot *slot = mmc_priv(mmc); + struct mmc_data *data = mrq->data; +diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c +index 1752007..78adb58 100644 +--- a/drivers/mmc/host/jz4740_mmc.c ++++ b/drivers/mmc/host/jz4740_mmc.c +@@ -320,8 +320,7 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, + } + + static void jz4740_mmc_pre_request(struct mmc_host *mmc, +- struct mmc_request *mrq, +- bool is_first_req) ++ struct mmc_request *mrq) + { + struct jz4740_mmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; +diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c +index df990bb..1ddbb64 100644 +--- a/drivers/mmc/host/mmci.c ++++ b/drivers/mmc/host/mmci.c +@@ -684,8 +684,7 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) + next->dma_chan = NULL; + } + +-static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, +- bool is_first_req) ++static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq) + { + struct mmci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; +diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c +index 6f9535e..0c9cf6d 100644 +--- a/drivers/mmc/host/mtk-sd.c ++++ b/drivers/mmc/host/mtk-sd.c +@@ -927,8 +927,7 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) + msdc_start_command(host, mrq, mrq->cmd); + } + +-static void msdc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, +- bool is_first_req) ++static void msdc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) + { + struct msdc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; +diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c +index 009242b..8ac47ce 100644 +--- a/drivers/mmc/host/omap_hsmmc.c ++++ b/drivers/mmc/host/omap_hsmmc.c +@@ -1565,8 +1565,7 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + } + } + +-static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, +- bool is_first_req) ++static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) + { + struct omap_hsmmc_host *host = mmc_priv(mmc); + +diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c +index 3ccaa14..ecb99a8 100644 +--- a/drivers/mmc/host/rtsx_pci_sdmmc.c ++++ b/drivers/mmc/host/rtsx_pci_sdmmc.c +@@ -190,8 +190,7 @@ static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host, + return using_cookie; + } + +-static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, +- bool is_first_req) ++static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) + { + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; +diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c +new file mode 100644 +index 00000000..f7e26b0 +--- /dev/null ++++ b/drivers/mmc/host/sdhci-xenon-phy.c +@@ -0,0 +1,825 @@ ++/* ++ * PHY support for Xenon SDHC ++ * ++ * Copyright (C) 2016 Marvell, All Rights Reserved. ++ * ++ * Author: Hu Ziji ++ * Date: 2016-8-24 ++ * ++ * 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 version 2. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "sdhci-pltfm.h" ++#include "sdhci-xenon.h" ++ ++/* Register base for eMMC PHY 5.0 Version */ ++#define XENON_EMMC_5_0_PHY_REG_BASE 0x0160 ++/* Register base for eMMC PHY 5.1 Version */ ++#define XENON_EMMC_PHY_REG_BASE 0x0170 ++ ++#define XENON_EMMC_PHY_TIMING_ADJUST XENON_EMMC_PHY_REG_BASE ++#define XENON_EMMC_5_0_PHY_TIMING_ADJUST XENON_EMMC_5_0_PHY_REG_BASE ++#define XENON_TIMING_ADJUST_SLOW_MODE BIT(29) ++#define XENON_TIMING_ADJUST_SDIO_MODE BIT(28) ++#define XENON_SAMPL_INV_QSP_PHASE_SELECT BIT(18) ++#define XENON_SAMPL_INV_QSP_PHASE_SELECT_SHIFT 18 ++#define XENON_PHY_INITIALIZAION BIT(31) ++#define XENON_WAIT_CYCLE_BEFORE_USING_MASK 0xF ++#define XENON_WAIT_CYCLE_BEFORE_USING_SHIFT 12 ++#define XENON_FC_SYNC_EN_DURATION_MASK 0xF ++#define XENON_FC_SYNC_EN_DURATION_SHIFT 8 ++#define XENON_FC_SYNC_RST_EN_DURATION_MASK 0xF ++#define XENON_FC_SYNC_RST_EN_DURATION_SHIFT 4 ++#define XENON_FC_SYNC_RST_DURATION_MASK 0xF ++#define XENON_FC_SYNC_RST_DURATION_SHIFT 0 ++ ++#define XENON_EMMC_PHY_FUNC_CONTROL (XENON_EMMC_PHY_REG_BASE + 0x4) ++#define XENON_EMMC_5_0_PHY_FUNC_CONTROL \ ++ (XENON_EMMC_5_0_PHY_REG_BASE + 0x4) ++#define XENON_ASYNC_DDRMODE_MASK BIT(23) ++#define XENON_ASYNC_DDRMODE_SHIFT 23 ++#define XENON_CMD_DDR_MODE BIT(16) ++#define XENON_DQ_DDR_MODE_SHIFT 8 ++#define XENON_DQ_DDR_MODE_MASK 0xFF ++#define XENON_DQ_ASYNC_MODE BIT(4) ++ ++#define XENON_EMMC_PHY_PAD_CONTROL (XENON_EMMC_PHY_REG_BASE + 0x8) ++#define XENON_EMMC_5_0_PHY_PAD_CONTROL \ ++ (XENON_EMMC_5_0_PHY_REG_BASE + 0x8) ++#define XENON_REC_EN_SHIFT 24 ++#define XENON_REC_EN_MASK 0xF ++#define XENON_FC_DQ_RECEN BIT(24) ++#define XENON_FC_CMD_RECEN BIT(25) ++#define XENON_FC_QSP_RECEN BIT(26) ++#define XENON_FC_QSN_RECEN BIT(27) ++#define XENON_OEN_QSN BIT(28) ++#define XENON_AUTO_RECEN_CTRL BIT(30) ++#define XENON_FC_ALL_CMOS_RECEIVER 0xF000 ++ ++#define XENON_EMMC5_FC_QSP_PD BIT(18) ++#define XENON_EMMC5_FC_QSP_PU BIT(22) ++#define XENON_EMMC5_FC_CMD_PD BIT(17) ++#define XENON_EMMC5_FC_CMD_PU BIT(21) ++#define XENON_EMMC5_FC_DQ_PD BIT(16) ++#define XENON_EMMC5_FC_DQ_PU BIT(20) ++ ++#define XENON_EMMC_PHY_PAD_CONTROL1 (XENON_EMMC_PHY_REG_BASE + 0xC) ++#define XENON_EMMC5_1_FC_QSP_PD BIT(9) ++#define XENON_EMMC5_1_FC_QSP_PU BIT(25) ++#define XENON_EMMC5_1_FC_CMD_PD BIT(8) ++#define XENON_EMMC5_1_FC_CMD_PU BIT(24) ++#define XENON_EMMC5_1_FC_DQ_PD 0xFF ++#define XENON_EMMC5_1_FC_DQ_PU (0xFF << 16) ++ ++#define XENON_EMMC_PHY_PAD_CONTROL2 (XENON_EMMC_PHY_REG_BASE + 0x10) ++#define XENON_EMMC_5_0_PHY_PAD_CONTROL2 \ ++ (XENON_EMMC_5_0_PHY_REG_BASE + 0xC) ++#define XENON_ZNR_MASK 0x1F ++#define XENON_ZNR_SHIFT 8 ++#define XENON_ZPR_MASK 0x1F ++/* Preferred ZNR and ZPR value vary between different boards. ++ * The specific ZNR and ZPR value should be defined here ++ * according to board actual timing. ++ */ ++#define XENON_ZNR_DEF_VALUE 0xF ++#define XENON_ZPR_DEF_VALUE 0xF ++ ++#define XENON_EMMC_PHY_DLL_CONTROL (XENON_EMMC_PHY_REG_BASE + 0x14) ++#define XENON_EMMC_5_0_PHY_DLL_CONTROL \ ++ (XENON_EMMC_5_0_PHY_REG_BASE + 0x10) ++#define XENON_DLL_ENABLE BIT(31) ++#define XENON_DLL_UPDATE_STROBE_5_0 BIT(30) ++#define XENON_DLL_REFCLK_SEL BIT(30) ++#define XENON_DLL_UPDATE BIT(23) ++#define XENON_DLL_PHSEL1_SHIFT 24 ++#define XENON_DLL_PHSEL0_SHIFT 16 ++#define XENON_DLL_PHASE_MASK 0x3F ++#define XENON_DLL_PHASE_90_DEGREE 0x1F ++#define XENON_DLL_FAST_LOCK BIT(5) ++#define XENON_DLL_GAIN2X BIT(3) ++#define XENON_DLL_BYPASS_EN BIT(0) ++ ++#define XENON_EMMC_5_0_PHY_LOGIC_TIMING_ADJUST \ ++ (XENON_EMMC_5_0_PHY_REG_BASE + 0x14) ++#define XENON_EMMC_5_0_PHY_LOGIC_TIMING_VALUE 0x5A54 ++#define XENON_EMMC_PHY_LOGIC_TIMING_ADJUST (XENON_EMMC_PHY_REG_BASE + 0x18) ++#define XENON_LOGIC_TIMING_VALUE 0x00AA8977 ++ ++/* ++ * List offset of PHY registers and some special register values ++ * in eMMC PHY 5.0 or eMMC PHY 5.1 ++ */ ++struct xenon_emmc_phy_regs { ++ /* Offset of Timing Adjust register */ ++ u16 timing_adj; ++ /* Offset of Func Control register */ ++ u16 func_ctrl; ++ /* Offset of Pad Control register */ ++ u16 pad_ctrl; ++ /* Offset of Pad Control register 2 */ ++ u16 pad_ctrl2; ++ /* Offset of DLL Control register */ ++ u16 dll_ctrl; ++ /* Offset of Logic Timing Adjust register */ ++ u16 logic_timing_adj; ++ /* DLL Update Enable bit */ ++ u32 dll_update; ++ /* value in Logic Timing Adjustment register */ ++ u32 logic_timing_val; ++}; ++ ++static const char * const phy_types[] = { ++ "emmc 5.0 phy", ++ "emmc 5.1 phy" ++}; ++ ++enum xenon_phy_type_enum { ++ EMMC_5_0_PHY, ++ EMMC_5_1_PHY, ++ NR_PHY_TYPES ++}; ++ ++enum soc_pad_ctrl_type { ++ SOC_PAD_SD, ++ SOC_PAD_FIXED_1_8V, ++}; ++ ++struct soc_pad_ctrl { ++ /* Register address of SoC PHY PAD ctrl */ ++ void __iomem *reg; ++ /* SoC PHY PAD ctrl type */ ++ enum soc_pad_ctrl_type pad_type; ++ /* SoC specific operation to set SoC PHY PAD */ ++ void (*set_soc_pad)(struct sdhci_host *host, ++ unsigned char signal_voltage); ++}; ++ ++static struct xenon_emmc_phy_regs xenon_emmc_5_0_phy_regs = { ++ .timing_adj = XENON_EMMC_5_0_PHY_TIMING_ADJUST, ++ .func_ctrl = XENON_EMMC_5_0_PHY_FUNC_CONTROL, ++ .pad_ctrl = XENON_EMMC_5_0_PHY_PAD_CONTROL, ++ .pad_ctrl2 = XENON_EMMC_5_0_PHY_PAD_CONTROL2, ++ .dll_ctrl = XENON_EMMC_5_0_PHY_DLL_CONTROL, ++ .logic_timing_adj = XENON_EMMC_5_0_PHY_LOGIC_TIMING_ADJUST, ++ .dll_update = XENON_DLL_UPDATE_STROBE_5_0, ++ .logic_timing_val = XENON_EMMC_5_0_PHY_LOGIC_TIMING_VALUE, ++}; ++ ++static struct xenon_emmc_phy_regs xenon_emmc_5_1_phy_regs = { ++ .timing_adj = XENON_EMMC_PHY_TIMING_ADJUST, ++ .func_ctrl = XENON_EMMC_PHY_FUNC_CONTROL, ++ .pad_ctrl = XENON_EMMC_PHY_PAD_CONTROL, ++ .pad_ctrl2 = XENON_EMMC_PHY_PAD_CONTROL2, ++ .dll_ctrl = XENON_EMMC_PHY_DLL_CONTROL, ++ .logic_timing_adj = XENON_EMMC_PHY_LOGIC_TIMING_ADJUST, ++ .dll_update = XENON_DLL_UPDATE, ++ .logic_timing_val = XENON_LOGIC_TIMING_VALUE, ++}; ++ ++/* ++ * eMMC PHY configuration and operations ++ */ ++struct xenon_emmc_phy_params { ++ bool slow_mode; ++ ++ u8 znr; ++ u8 zpr; ++ ++ /* Nr of consecutive Sampling Points of a Valid Sampling Window */ ++ u8 nr_tun_times; ++ /* Divider for calculating Tuning Step */ ++ u8 tun_step_divider; ++ ++ struct soc_pad_ctrl pad_ctrl; ++}; ++ ++static int xenon_alloc_emmc_phy(struct sdhci_host *host) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_params *params; ++ ++ params = devm_kzalloc(mmc_dev(host->mmc), sizeof(*params), GFP_KERNEL); ++ if (!params) ++ return -ENOMEM; ++ ++ priv->phy_params = params; ++ if (priv->phy_type == EMMC_5_0_PHY) ++ priv->emmc_phy_regs = &xenon_emmc_5_0_phy_regs; ++ else ++ priv->emmc_phy_regs = &xenon_emmc_5_1_phy_regs; ++ ++ return 0; ++} ++ ++/* ++ * eMMC 5.0/5.1 PHY init/re-init. ++ * eMMC PHY init should be executed after: ++ * 1. SDCLK frequency changes. ++ * 2. SDCLK is stopped and re-enabled. ++ * 3. config in emmc_phy_regs->timing_adj and emmc_phy_regs->func_ctrl ++ * are changed ++ */ ++static int xenon_emmc_phy_init(struct sdhci_host *host) ++{ ++ u32 reg; ++ u32 wait, clock; ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs; ++ ++ reg = sdhci_readl(host, phy_regs->timing_adj); ++ reg |= XENON_PHY_INITIALIZAION; ++ sdhci_writel(host, reg, phy_regs->timing_adj); ++ ++ /* Add duration of FC_SYNC_RST */ ++ wait = ((reg >> XENON_FC_SYNC_RST_DURATION_SHIFT) & ++ XENON_FC_SYNC_RST_DURATION_MASK); ++ /* Add interval between FC_SYNC_EN and FC_SYNC_RST */ ++ wait += ((reg >> XENON_FC_SYNC_RST_EN_DURATION_SHIFT) & ++ XENON_FC_SYNC_RST_EN_DURATION_MASK); ++ /* Add duration of asserting FC_SYNC_EN */ ++ wait += ((reg >> XENON_FC_SYNC_EN_DURATION_SHIFT) & ++ XENON_FC_SYNC_EN_DURATION_MASK); ++ /* Add duration of waiting for PHY */ ++ wait += ((reg >> XENON_WAIT_CYCLE_BEFORE_USING_SHIFT) & ++ XENON_WAIT_CYCLE_BEFORE_USING_MASK); ++ /* 4 additional bus clock and 4 AXI bus clock are required */ ++ wait += 8; ++ wait <<= 20; ++ ++ clock = host->clock; ++ if (!clock) ++ /* Use the possibly slowest bus frequency value */ ++ clock = XENON_LOWEST_SDCLK_FREQ; ++ /* get the wait time */ ++ wait /= clock; ++ wait++; ++ /* wait for host eMMC PHY init completes */ ++ udelay(wait); ++ ++ reg = sdhci_readl(host, phy_regs->timing_adj); ++ reg &= XENON_PHY_INITIALIZAION; ++ if (reg) { ++ dev_err(mmc_dev(host->mmc), "eMMC PHY init cannot complete after %d us\n", ++ wait); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++#define ARMADA_3700_SOC_PAD_1_8V 0x1 ++#define ARMADA_3700_SOC_PAD_3_3V 0x0 ++ ++static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host, ++ unsigned char signal_voltage) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_params *params = priv->phy_params; ++ ++ if (params->pad_ctrl.pad_type == SOC_PAD_FIXED_1_8V) { ++ writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg); ++ } else if (params->pad_ctrl.pad_type == SOC_PAD_SD) { ++ if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) ++ writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg); ++ else if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) ++ writel(ARMADA_3700_SOC_PAD_3_3V, params->pad_ctrl.reg); ++ } ++} ++ ++/* ++ * Set SoC PHY voltage PAD control register, ++ * according to the operation voltage on PAD. ++ * The detailed operation depends on SoC implementation. ++ */ ++static void xenon_emmc_phy_set_soc_pad(struct sdhci_host *host, ++ unsigned char signal_voltage) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_params *params = priv->phy_params; ++ ++ if (!params->pad_ctrl.reg) ++ return; ++ ++ if (params->pad_ctrl.set_soc_pad) ++ params->pad_ctrl.set_soc_pad(host, signal_voltage); ++} ++ ++/* ++ * Enable eMMC PHY HW DLL ++ * DLL should be enabled and stable before HS200/SDR104 tuning, ++ * and before HS400 data strobe setting. ++ */ ++static int xenon_emmc_phy_enable_dll(struct sdhci_host *host) ++{ ++ u32 reg; ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs; ++ ktime_t timeout; ++ ++ if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR)) ++ return -EINVAL; ++ ++ reg = sdhci_readl(host, phy_regs->dll_ctrl); ++ if (reg & XENON_DLL_ENABLE) ++ return 0; ++ ++ /* Enable DLL */ ++ reg = sdhci_readl(host, phy_regs->dll_ctrl); ++ reg |= (XENON_DLL_ENABLE | XENON_DLL_FAST_LOCK); ++ ++ /* ++ * Set Phase as 90 degree, which is most common value. ++ * Might set another value if necessary. ++ * The granularity is 1 degree. ++ */ ++ reg &= ~((XENON_DLL_PHASE_MASK << XENON_DLL_PHSEL0_SHIFT) | ++ (XENON_DLL_PHASE_MASK << XENON_DLL_PHSEL1_SHIFT)); ++ reg |= ((XENON_DLL_PHASE_90_DEGREE << XENON_DLL_PHSEL0_SHIFT) | ++ (XENON_DLL_PHASE_90_DEGREE << XENON_DLL_PHSEL1_SHIFT)); ++ ++ reg &= ~XENON_DLL_BYPASS_EN; ++ reg |= phy_regs->dll_update; ++ if (priv->phy_type == EMMC_5_1_PHY) ++ reg &= ~XENON_DLL_REFCLK_SEL; ++ sdhci_writel(host, reg, phy_regs->dll_ctrl); ++ ++ /* Wait max 32 ms */ ++ timeout = ktime_add_ms(ktime_get(), 32); ++ while (!(sdhci_readw(host, XENON_SLOT_EXT_PRESENT_STATE) & ++ XENON_DLL_LOCK_STATE)) { ++ if (ktime_after(ktime_get(), timeout)) { ++ dev_err(mmc_dev(host->mmc), "Wait for DLL Lock time-out\n"); ++ return -ETIMEDOUT; ++ } ++ udelay(100); ++ } ++ return 0; ++} ++ ++/* ++ * Config to eMMC PHY to prepare for tuning. ++ * Enable HW DLL and set the TUNING_STEP ++ */ ++static int xenon_emmc_phy_config_tuning(struct sdhci_host *host) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_params *params = priv->phy_params; ++ u32 reg, tuning_step; ++ int ret; ++ ++ if (host->clock <= MMC_HIGH_52_MAX_DTR) ++ return -EINVAL; ++ ++ ret = xenon_emmc_phy_enable_dll(host); ++ if (ret) ++ return ret; ++ ++ /* Achieve TUNING_STEP with HW DLL help */ ++ reg = sdhci_readl(host, XENON_SLOT_DLL_CUR_DLY_VAL); ++ tuning_step = reg / params->tun_step_divider; ++ if (unlikely(tuning_step > XENON_TUNING_STEP_MASK)) { ++ dev_warn(mmc_dev(host->mmc), ++ "HS200 TUNING_STEP %d is larger than MAX value\n", ++ tuning_step); ++ tuning_step = XENON_TUNING_STEP_MASK; ++ } ++ ++ /* Set TUNING_STEP for later tuning */ ++ reg = sdhci_readl(host, XENON_SLOT_OP_STATUS_CTRL); ++ reg &= ~(XENON_TUN_CONSECUTIVE_TIMES_MASK << ++ XENON_TUN_CONSECUTIVE_TIMES_SHIFT); ++ reg |= (params->nr_tun_times << XENON_TUN_CONSECUTIVE_TIMES_SHIFT); ++ reg &= ~(XENON_TUNING_STEP_MASK << XENON_TUNING_STEP_SHIFT); ++ reg |= (tuning_step << XENON_TUNING_STEP_SHIFT); ++ sdhci_writel(host, reg, XENON_SLOT_OP_STATUS_CTRL); ++ ++ return 0; ++} ++ ++static void xenon_emmc_phy_disable_data_strobe(struct sdhci_host *host) ++{ ++ u32 reg; ++ ++ /* Disable SDHC Data Strobe */ ++ reg = sdhci_readl(host, XENON_SLOT_EMMC_CTRL); ++ reg &= ~XENON_ENABLE_DATA_STROBE; ++ sdhci_writel(host, reg, XENON_SLOT_EMMC_CTRL); ++} ++ ++/* Set HS400 Data Strobe */ ++static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u32 reg; ++ ++ if (WARN_ON(host->timing != MMC_TIMING_MMC_HS400)) ++ return; ++ ++ if (host->clock <= MMC_HIGH_52_MAX_DTR) ++ return; ++ ++ dev_dbg(mmc_dev(host->mmc), "starts HS400 strobe delay adjustment\n"); ++ ++ xenon_emmc_phy_enable_dll(host); ++ ++ /* Enable SDHC Data Strobe */ ++ reg = sdhci_readl(host, XENON_SLOT_EMMC_CTRL); ++ reg |= XENON_ENABLE_DATA_STROBE; ++ sdhci_writel(host, reg, XENON_SLOT_EMMC_CTRL); ++ ++ /* Set Data Strobe Pull down */ ++ if (priv->phy_type == EMMC_5_0_PHY) { ++ reg = sdhci_readl(host, XENON_EMMC_5_0_PHY_PAD_CONTROL); ++ reg |= XENON_EMMC5_FC_QSP_PD; ++ reg &= ~XENON_EMMC5_FC_QSP_PU; ++ sdhci_writel(host, reg, XENON_EMMC_5_0_PHY_PAD_CONTROL); ++ } else { ++ reg = sdhci_readl(host, XENON_EMMC_PHY_PAD_CONTROL1); ++ reg |= XENON_EMMC5_1_FC_QSP_PD; ++ reg &= ~XENON_EMMC5_1_FC_QSP_PU; ++ sdhci_writel(host, reg, XENON_EMMC_PHY_PAD_CONTROL1); ++ } ++} ++ ++/* ++ * If eMMC PHY Slow Mode is required in lower speed mode (SDCLK < 55MHz) ++ * in SDR mode, enable Slow Mode to bypass eMMC PHY. ++ * SDIO slower SDR mode also requires Slow Mode. ++ * ++ * If Slow Mode is enabled, return true. ++ * Otherwise, return false. ++ */ ++static bool xenon_emmc_phy_slow_mode(struct sdhci_host *host, ++ unsigned char timing) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_params *params = priv->phy_params; ++ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs; ++ u32 reg; ++ int ret; ++ ++ if (host->clock > MMC_HIGH_52_MAX_DTR) ++ return false; ++ ++ reg = sdhci_readl(host, phy_regs->timing_adj); ++ /* When in slower SDR mode, enable Slow Mode for SDIO ++ * or when Slow Mode flag is set ++ */ ++ switch (timing) { ++ case MMC_TIMING_LEGACY: ++ /* ++ * If Slow Mode is required, enable Slow Mode by default ++ * in early init phase to avoid any potential issue. ++ */ ++ if (params->slow_mode) { ++ reg |= XENON_TIMING_ADJUST_SLOW_MODE; ++ ret = true; ++ } else { ++ reg &= ~XENON_TIMING_ADJUST_SLOW_MODE; ++ ret = false; ++ } ++ break; ++ case MMC_TIMING_UHS_SDR25: ++ case MMC_TIMING_UHS_SDR12: ++ case MMC_TIMING_SD_HS: ++ case MMC_TIMING_MMC_HS: ++ if ((priv->init_card_type == MMC_TYPE_SDIO) || ++ params->slow_mode) { ++ reg |= XENON_TIMING_ADJUST_SLOW_MODE; ++ ret = true; ++ break; ++ } ++ default: ++ reg &= ~XENON_TIMING_ADJUST_SLOW_MODE; ++ ret = false; ++ } ++ ++ sdhci_writel(host, reg, phy_regs->timing_adj); ++ return ret; ++} ++ ++/* ++ * Set-up eMMC 5.0/5.1 PHY. ++ * Specific configuration depends on the current speed mode in use. ++ */ ++static void xenon_emmc_phy_set(struct sdhci_host *host, ++ unsigned char timing) ++{ ++ u32 reg; ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ struct xenon_emmc_phy_params *params = priv->phy_params; ++ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs; ++ ++ dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting starts\n"); ++ ++ /* Setup pad, set bit[28] and bits[26:24] */ ++ reg = sdhci_readl(host, phy_regs->pad_ctrl); ++ reg |= (XENON_FC_DQ_RECEN | XENON_FC_CMD_RECEN | ++ XENON_FC_QSP_RECEN | XENON_OEN_QSN); ++ /* All FC_XX_RECEIVCE should be set as CMOS Type */ ++ reg |= XENON_FC_ALL_CMOS_RECEIVER; ++ sdhci_writel(host, reg, phy_regs->pad_ctrl); ++ ++ /* Set CMD and DQ Pull Up */ ++ if (priv->phy_type == EMMC_5_0_PHY) { ++ reg = sdhci_readl(host, XENON_EMMC_5_0_PHY_PAD_CONTROL); ++ reg |= (XENON_EMMC5_FC_CMD_PU | XENON_EMMC5_FC_DQ_PU); ++ reg &= ~(XENON_EMMC5_FC_CMD_PD | XENON_EMMC5_FC_DQ_PD); ++ sdhci_writel(host, reg, XENON_EMMC_5_0_PHY_PAD_CONTROL); ++ } else { ++ reg = sdhci_readl(host, XENON_EMMC_PHY_PAD_CONTROL1); ++ reg |= (XENON_EMMC5_1_FC_CMD_PU | XENON_EMMC5_1_FC_DQ_PU); ++ reg &= ~(XENON_EMMC5_1_FC_CMD_PD | XENON_EMMC5_1_FC_DQ_PD); ++ sdhci_writel(host, reg, XENON_EMMC_PHY_PAD_CONTROL1); ++ } ++ ++ if (timing == MMC_TIMING_LEGACY) { ++ xenon_emmc_phy_slow_mode(host, timing); ++ goto phy_init; ++ } ++ ++ /* ++ * If SDIO card, set SDIO Mode ++ * Otherwise, clear SDIO Mode ++ */ ++ reg = sdhci_readl(host, phy_regs->timing_adj); ++ if (priv->init_card_type == MMC_TYPE_SDIO) ++ reg |= XENON_TIMING_ADJUST_SDIO_MODE; ++ else ++ reg &= ~XENON_TIMING_ADJUST_SDIO_MODE; ++ sdhci_writel(host, reg, phy_regs->timing_adj); ++ ++ if (xenon_emmc_phy_slow_mode(host, timing)) ++ goto phy_init; ++ ++ /* ++ * Set preferred ZNR and ZPR value ++ * The ZNR and ZPR value vary between different boards. ++ * Define them both in sdhci-xenon-emmc-phy.h. ++ */ ++ reg = sdhci_readl(host, phy_regs->pad_ctrl2); ++ reg &= ~((XENON_ZNR_MASK << XENON_ZNR_SHIFT) | XENON_ZPR_MASK); ++ reg |= ((params->znr << XENON_ZNR_SHIFT) | params->zpr); ++ sdhci_writel(host, reg, phy_regs->pad_ctrl2); ++ ++ /* ++ * When setting EMMC_PHY_FUNC_CONTROL register, ++ * SD clock should be disabled ++ */ ++ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL); ++ reg &= ~SDHCI_CLOCK_CARD_EN; ++ sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); ++ ++ reg = sdhci_readl(host, phy_regs->func_ctrl); ++ switch (timing) { ++ case MMC_TIMING_MMC_HS400: ++ reg |= (XENON_DQ_DDR_MODE_MASK << XENON_DQ_DDR_MODE_SHIFT) | ++ XENON_CMD_DDR_MODE; ++ reg &= ~XENON_DQ_ASYNC_MODE; ++ break; ++ case MMC_TIMING_UHS_DDR50: ++ case MMC_TIMING_MMC_DDR52: ++ reg |= (XENON_DQ_DDR_MODE_MASK << XENON_DQ_DDR_MODE_SHIFT) | ++ XENON_CMD_DDR_MODE | XENON_DQ_ASYNC_MODE; ++ break; ++ default: ++ reg &= ~((XENON_DQ_DDR_MODE_MASK << XENON_DQ_DDR_MODE_SHIFT) | ++ XENON_CMD_DDR_MODE); ++ reg |= XENON_DQ_ASYNC_MODE; ++ } ++ sdhci_writel(host, reg, phy_regs->func_ctrl); ++ ++ /* Enable bus clock */ ++ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL); ++ reg |= SDHCI_CLOCK_CARD_EN; ++ sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); ++ ++ if (timing == MMC_TIMING_MMC_HS400) ++ /* Hardware team recommend a value for HS400 */ ++ sdhci_writel(host, phy_regs->logic_timing_val, ++ phy_regs->logic_timing_adj); ++ else ++ xenon_emmc_phy_disable_data_strobe(host); ++ ++phy_init: ++ xenon_emmc_phy_init(host); ++ ++ dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting completes\n"); ++} ++ ++static int get_dt_pad_ctrl_data(struct sdhci_host *host, ++ struct device_node *np, ++ struct xenon_emmc_phy_params *params) ++{ ++ int ret = 0; ++ const char *name; ++ struct resource iomem; ++ ++ if (of_device_is_compatible(np, "marvell,armada-3700-sdhci")) ++ params->pad_ctrl.set_soc_pad = armada_3700_soc_pad_voltage_set; ++ else ++ return 0; ++ ++ if (of_address_to_resource(np, 1, &iomem)) { ++ dev_err(mmc_dev(host->mmc), "Unable to find SoC PAD ctrl register address for %s\n", ++ np->name); ++ return -EINVAL; ++ } ++ ++ params->pad_ctrl.reg = devm_ioremap_resource(mmc_dev(host->mmc), ++ &iomem); ++ if (IS_ERR(params->pad_ctrl.reg)) ++ return PTR_ERR(params->pad_ctrl.reg); ++ ++ ret = of_property_read_string(np, "marvell,pad-type", &name); ++ if (ret) { ++ dev_err(mmc_dev(host->mmc), "Unable to determine SoC PHY PAD ctrl type\n"); ++ return ret; ++ } ++ if (!strcmp(name, "sd")) { ++ params->pad_ctrl.pad_type = SOC_PAD_SD; ++ } else if (!strcmp(name, "fixed-1-8v")) { ++ params->pad_ctrl.pad_type = SOC_PAD_FIXED_1_8V; ++ } else { ++ dev_err(mmc_dev(host->mmc), "Unsupported SoC PHY PAD ctrl type %s\n", ++ name); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int xenon_emmc_phy_parse_param_dt(struct sdhci_host *host, ++ struct device_node *np, ++ struct xenon_emmc_phy_params *params) ++{ ++ u32 value; ++ ++ params->slow_mode = false; ++ if (of_property_read_bool(np, "marvell,xenon-phy-slow-mode")) ++ params->slow_mode = true; ++ ++ params->znr = XENON_ZNR_DEF_VALUE; ++ if (!of_property_read_u32(np, "marvell,xenon-phy-znr", &value)) ++ params->znr = value & XENON_ZNR_MASK; ++ ++ params->zpr = XENON_ZPR_DEF_VALUE; ++ if (!of_property_read_u32(np, "marvell,xenon-phy-zpr", &value)) ++ params->zpr = value & XENON_ZPR_MASK; ++ ++ params->nr_tun_times = XENON_TUN_CONSECUTIVE_TIMES; ++ if (!of_property_read_u32(np, "marvell,xenon-phy-nr-success-tun", ++ &value)) ++ params->nr_tun_times = value & XENON_TUN_CONSECUTIVE_TIMES_MASK; ++ ++ params->tun_step_divider = XENON_TUNING_STEP_DIVIDER; ++ if (!of_property_read_u32(np, "marvell,xenon-phy-tun-step-divider", ++ &value)) ++ params->tun_step_divider = value & 0xFF; ++ ++ return get_dt_pad_ctrl_data(host, np, params); ++} ++ ++/* Set SoC PHY Voltage PAD */ ++void xenon_soc_pad_ctrl(struct sdhci_host *host, ++ unsigned char signal_voltage) ++{ ++ xenon_emmc_phy_set_soc_pad(host, signal_voltage); ++} ++ ++/* ++ * Setting PHY when card is working in High Speed Mode. ++ * HS400 set data strobe line. ++ * HS200/SDR104 set tuning config to prepare for tuning. ++ */ ++static int xenon_hs_delay_adj(struct sdhci_host *host) ++{ ++ int ret = 0; ++ ++ if (WARN_ON(host->clock <= XENON_DEFAULT_SDCLK_FREQ)) ++ return -EINVAL; ++ ++ switch (host->timing) { ++ case MMC_TIMING_MMC_HS400: ++ xenon_emmc_phy_strobe_delay_adj(host); ++ return 0; ++ case MMC_TIMING_MMC_HS200: ++ case MMC_TIMING_UHS_SDR104: ++ return xenon_emmc_phy_config_tuning(host); ++ case MMC_TIMING_MMC_DDR52: ++ case MMC_TIMING_UHS_DDR50: ++ /* ++ * DDR Mode requires driver to scan Sampling Fixed Delay Line, ++ * to find out a perfect operation sampling point. ++ * It is hard to implement such a scan in host driver ++ * since initiating commands by host driver is not safe. ++ * Thus so far just keep PHY Sampling Fixed Delay in ++ * default value of DDR mode. ++ * ++ * If any timing issue occurs in DDR mode on Marvell products, ++ * please contact maintainer for internal support in Marvell. ++ */ ++ dev_warn_once(mmc_dev(host->mmc), "Timing issue might occur in DDR mode\n"); ++ return 0; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Adjust PHY setting. ++ * PHY setting should be adjusted when SDCLK frequency, Bus Width ++ * or Speed Mode is changed. ++ * Additional config are required when card is working in High Speed mode, ++ * after leaving Legacy Mode. ++ */ ++int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ int ret = 0; ++ ++ if (!host->clock) { ++ priv->clock = 0; ++ return 0; ++ } ++ ++ /* ++ * The timing, frequency or bus width is changed, ++ * better to set eMMC PHY based on current setting ++ * and adjust Xenon SDHC delay. ++ */ ++ if ((host->clock == priv->clock) && ++ (ios->bus_width == priv->bus_width) && ++ (ios->timing == priv->timing)) ++ return 0; ++ ++ xenon_emmc_phy_set(host, ios->timing); ++ ++ /* Update the record */ ++ priv->bus_width = ios->bus_width; ++ ++ priv->timing = ios->timing; ++ priv->clock = host->clock; ++ ++ /* Legacy mode is a special case */ ++ if (ios->timing == MMC_TIMING_LEGACY) ++ return 0; ++ ++ if (host->clock > XENON_DEFAULT_SDCLK_FREQ) ++ ret = xenon_hs_delay_adj(host); ++ return ret; ++} ++ ++static int xenon_add_phy(struct device_node *np, struct sdhci_host *host, ++ const char *phy_name) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ int i, ret; ++ ++ for (i = 0; i < NR_PHY_TYPES; i++) { ++ if (!strcmp(phy_name, phy_types[i])) { ++ priv->phy_type = i; ++ break; ++ } ++ } ++ if (i == NR_PHY_TYPES) { ++ dev_err(mmc_dev(host->mmc), ++ "Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n", ++ phy_name); ++ priv->phy_type = EMMC_5_1_PHY; ++ } ++ ++ ret = xenon_alloc_emmc_phy(host); ++ if (ret) ++ return ret; ++ ++ return xenon_emmc_phy_parse_param_dt(host, np, priv->phy_params); ++} ++ ++int xenon_phy_parse_dt(struct device_node *np, struct sdhci_host *host) ++{ ++ const char *phy_type = NULL; ++ ++ if (!of_property_read_string(np, "marvell,xenon-phy-type", &phy_type)) ++ return xenon_add_phy(np, host, phy_type); ++ ++ return xenon_add_phy(np, host, "emmc 5.1 phy"); ++} +diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c +new file mode 100644 +index 00000000..a6c7891 +--- /dev/null ++++ b/drivers/mmc/host/sdhci-xenon.c +@@ -0,0 +1,551 @@ ++/* ++ * Driver for Marvell Xenon SDHC as a platform device ++ * ++ * Copyright (C) 2016 Marvell, All Rights Reserved. ++ * ++ * Author: Hu Ziji ++ * Date: 2016-8-24 ++ * ++ * 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 version 2. ++ * ++ * Inspired by Jisheng Zhang ++ * Special thanks to Video BG4 project team. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "sdhci-pltfm.h" ++#include "sdhci-xenon.h" ++ ++static int xenon_enable_internal_clk(struct sdhci_host *host) ++{ ++ u32 reg; ++ ktime_t timeout; ++ ++ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL); ++ reg |= SDHCI_CLOCK_INT_EN; ++ sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL); ++ /* Wait max 20 ms */ ++ timeout = ktime_add_ms(ktime_get(), 20); ++ while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) ++ & SDHCI_CLOCK_INT_STABLE)) { ++ if (ktime_after(ktime_get(), timeout)) { ++ dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n"); ++ return -ETIMEDOUT; ++ } ++ usleep_range(900, 1100); ++ } ++ ++ return 0; ++} ++ ++/* Set SDCLK-off-while-idle */ ++static void xenon_set_sdclk_off_idle(struct sdhci_host *host, ++ unsigned char sdhc_id, bool enable) ++{ ++ u32 reg; ++ u32 mask; ++ ++ reg = sdhci_readl(host, XENON_SYS_OP_CTRL); ++ /* Get the bit shift basing on the SDHC index */ ++ mask = (0x1 << (XENON_SDCLK_IDLEOFF_ENABLE_SHIFT + sdhc_id)); ++ if (enable) ++ reg |= mask; ++ else ++ reg &= ~mask; ++ ++ sdhci_writel(host, reg, XENON_SYS_OP_CTRL); ++} ++ ++/* Enable/Disable the Auto Clock Gating function */ ++static void xenon_set_acg(struct sdhci_host *host, bool enable) ++{ ++ u32 reg; ++ ++ reg = sdhci_readl(host, XENON_SYS_OP_CTRL); ++ if (enable) ++ reg &= ~XENON_AUTO_CLKGATE_DISABLE_MASK; ++ else ++ reg |= XENON_AUTO_CLKGATE_DISABLE_MASK; ++ sdhci_writel(host, reg, XENON_SYS_OP_CTRL); ++} ++ ++/* Enable this SDHC */ ++static void xenon_enable_sdhc(struct sdhci_host *host, ++ unsigned char sdhc_id) ++{ ++ u32 reg; ++ ++ reg = sdhci_readl(host, XENON_SYS_OP_CTRL); ++ reg |= (BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); ++ sdhci_writel(host, reg, XENON_SYS_OP_CTRL); ++ ++ host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; ++ /* ++ * Force to clear BUS_TEST to ++ * skip bus_test_pre and bus_test_post ++ */ ++ host->mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST; ++} ++ ++/* Disable this SDHC */ ++static void xenon_disable_sdhc(struct sdhci_host *host, ++ unsigned char sdhc_id) ++{ ++ u32 reg; ++ ++ reg = sdhci_readl(host, XENON_SYS_OP_CTRL); ++ reg &= ~(BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); ++ sdhci_writel(host, reg, XENON_SYS_OP_CTRL); ++} ++ ++/* Enable Parallel Transfer Mode */ ++static void xenon_enable_sdhc_parallel_tran(struct sdhci_host *host, ++ unsigned char sdhc_id) ++{ ++ u32 reg; ++ ++ reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); ++ reg |= BIT(sdhc_id); ++ sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); ++} ++ ++/* Mask command conflict error */ ++static void xenon_mask_cmd_conflict_err(struct sdhci_host *host) ++{ ++ u32 reg; ++ ++ reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); ++ reg |= XENON_MASK_CMD_CONFLICT_ERR; ++ sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); ++} ++ ++static void xenon_retune_setup(struct sdhci_host *host) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u32 reg; ++ ++ /* Disable the Re-Tuning Request functionality */ ++ reg = sdhci_readl(host, XENON_SLOT_RETUNING_REQ_CTRL); ++ reg &= ~XENON_RETUNING_COMPATIBLE; ++ sdhci_writel(host, reg, XENON_SLOT_RETUNING_REQ_CTRL); ++ ++ /* Disable the Re-tuning Interrupt */ ++ reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); ++ reg &= ~SDHCI_INT_RETUNE; ++ sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE); ++ reg = sdhci_readl(host, SDHCI_INT_ENABLE); ++ reg &= ~SDHCI_INT_RETUNE; ++ sdhci_writel(host, reg, SDHCI_INT_ENABLE); ++ ++ /* Force to use Tuning Mode 1 */ ++ host->tuning_mode = SDHCI_TUNING_MODE_1; ++ /* Set re-tuning period */ ++ host->tuning_count = 1 << (priv->tuning_count - 1); ++} ++ ++/* ++ * Operations inside struct sdhci_ops ++ */ ++/* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */ ++static void xenon_reset_exit(struct sdhci_host *host, ++ unsigned char sdhc_id, u8 mask) ++{ ++ /* Only SOFTWARE RESET ALL will clear the register setting */ ++ if (!(mask & SDHCI_RESET_ALL)) ++ return; ++ ++ /* Disable tuning request and auto-retuning again */ ++ xenon_retune_setup(host); ++ ++ xenon_set_acg(host, true); ++ ++ xenon_set_sdclk_off_idle(host, sdhc_id, false); ++ ++ xenon_mask_cmd_conflict_err(host); ++} ++ ++static void xenon_reset(struct sdhci_host *host, u8 mask) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ ++ sdhci_reset(host, mask); ++ xenon_reset_exit(host, priv->sdhc_id, mask); ++} ++ ++/* ++ * Xenon defines different values for HS200 and HS400 ++ * in Host_Control_2 ++ */ ++static void xenon_set_uhs_signaling(struct sdhci_host *host, ++ unsigned int timing) ++{ ++ u16 ctrl_2; ++ ++ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ /* Select Bus Speed Mode for host */ ++ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; ++ if (timing == MMC_TIMING_MMC_HS200) ++ ctrl_2 |= XENON_CTRL_HS200; ++ else if (timing == MMC_TIMING_UHS_SDR104) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR104; ++ else if (timing == MMC_TIMING_UHS_SDR12) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR12; ++ else if (timing == MMC_TIMING_UHS_SDR25) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR25; ++ else if (timing == MMC_TIMING_UHS_SDR50) ++ ctrl_2 |= SDHCI_CTRL_UHS_SDR50; ++ else if ((timing == MMC_TIMING_UHS_DDR50) || ++ (timing == MMC_TIMING_MMC_DDR52)) ++ ctrl_2 |= SDHCI_CTRL_UHS_DDR50; ++ else if (timing == MMC_TIMING_MMC_HS400) ++ ctrl_2 |= XENON_CTRL_HS400; ++ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); ++} ++ ++static void xenon_voltage_switch(struct sdhci_host *host) ++{ ++ /* Wait for 5ms after set 1.8V signal enable bit */ ++ usleep_range(5000, 5500); ++} ++ ++static const struct sdhci_ops sdhci_xenon_ops = { ++ .voltage_switch = xenon_voltage_switch, ++ .set_clock = sdhci_set_clock, ++ .set_bus_width = sdhci_set_bus_width, ++ .reset = xenon_reset, ++ .set_uhs_signaling = xenon_set_uhs_signaling, ++ .get_max_clock = sdhci_pltfm_clk_get_max_clock, ++}; ++ ++static const struct sdhci_pltfm_data sdhci_xenon_pdata = { ++ .ops = &sdhci_xenon_ops, ++ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | ++ SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | ++ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, ++}; ++ ++/* ++ * Xenon Specific Operations in mmc_host_ops ++ */ ++static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u32 reg; ++ ++ /* ++ * HS400/HS200/eMMC HS doesn't have Preset Value register. ++ * However, sdhci_set_ios will read HS400/HS200 Preset register. ++ * Disable Preset Value register for HS400/HS200. ++ * eMMC HS with preset_enabled set will trigger a bug in ++ * get_preset_value(). ++ */ ++ if ((ios->timing == MMC_TIMING_MMC_HS400) || ++ (ios->timing == MMC_TIMING_MMC_HS200) || ++ (ios->timing == MMC_TIMING_MMC_HS)) { ++ host->preset_enabled = false; ++ host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; ++ host->flags &= ~SDHCI_PV_ENABLED; ++ ++ reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; ++ sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); ++ } else { ++ host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN; ++ } ++ ++ sdhci_set_ios(mmc, ios); ++ xenon_phy_adj(host, ios); ++ ++ if (host->clock > XENON_DEFAULT_SDCLK_FREQ) ++ xenon_set_sdclk_off_idle(host, priv->sdhc_id, true); ++} ++ ++static int xenon_start_signal_voltage_switch(struct mmc_host *mmc, ++ struct mmc_ios *ios) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ ++ /* ++ * Before SD/SDIO set signal voltage, SD bus clock should be ++ * disabled. However, sdhci_set_clock will also disable the Internal ++ * clock in mmc_set_signal_voltage(). ++ * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated. ++ * Thus here manually enable internal clock. ++ * ++ * After switch completes, it is unnecessary to disable internal clock, ++ * since keeping internal clock active obeys SD spec. ++ */ ++ xenon_enable_internal_clk(host); ++ ++ xenon_soc_pad_ctrl(host, ios->signal_voltage); ++ ++ /* ++ * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable. ++ * Thus SDHCI_CTRL_VDD_180 bit might not work then. ++ * Skip the standard voltage switch to avoid any issue. ++ */ ++ if (PTR_ERR(mmc->supply.vqmmc) == -ENODEV) ++ return 0; ++ ++ return sdhci_start_signal_voltage_switch(mmc, ios); ++} ++ ++/* ++ * Update card type. ++ * priv->init_card_type will be used in PHY timing adjustment. ++ */ ++static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ ++ /* Update card type*/ ++ priv->init_card_type = card->type; ++} ++ ++static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ ++ if (host->timing == MMC_TIMING_UHS_DDR50) ++ return 0; ++ ++ /* ++ * Currently force Xenon driver back to support mode 1 only, ++ * even though Xenon might claim to support mode 2 or mode 3. ++ * It requires more time to test mode 2/mode 3 on more platforms. ++ */ ++ if (host->tuning_mode != SDHCI_TUNING_MODE_1) ++ xenon_retune_setup(host); ++ ++ return sdhci_execute_tuning(mmc, opcode); ++} ++ ++static void xenon_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u32 reg; ++ u8 sdhc_id = priv->sdhc_id; ++ ++ sdhci_enable_sdio_irq(mmc, enable); ++ ++ if (enable) { ++ /* ++ * Set SDIO Card Inserted indication ++ * to enable detecting SDIO async irq. ++ */ ++ reg = sdhci_readl(host, XENON_SYS_CFG_INFO); ++ reg |= (1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); ++ sdhci_writel(host, reg, XENON_SYS_CFG_INFO); ++ } else { ++ /* Clear SDIO Card Inserted indication */ ++ reg = sdhci_readl(host, XENON_SYS_CFG_INFO); ++ reg &= ~(1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); ++ sdhci_writel(host, reg, XENON_SYS_CFG_INFO); ++ } ++} ++ ++static void xenon_replace_mmc_host_ops(struct sdhci_host *host) ++{ ++ host->mmc_host_ops.set_ios = xenon_set_ios; ++ host->mmc_host_ops.start_signal_voltage_switch = ++ xenon_start_signal_voltage_switch; ++ host->mmc_host_ops.init_card = xenon_init_card; ++ host->mmc_host_ops.execute_tuning = xenon_execute_tuning; ++ host->mmc_host_ops.enable_sdio_irq = xenon_enable_sdio_irq; ++} ++ ++/* ++ * Parse Xenon specific DT properties: ++ * sdhc-id: the index of current SDHC. ++ * Refer to XENON_SYS_CFG_INFO register ++ * tun-count: the interval between re-tuning ++ */ ++static int xenon_probe_dt(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct mmc_host *mmc = host->mmc; ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u32 sdhc_id, nr_sdhc; ++ u32 tuning_count; ++ ++ /* Disable HS200 on Armada AP806 */ ++ if (of_device_is_compatible(np, "marvell,armada-ap806-sdhci")) ++ host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; ++ ++ sdhc_id = 0x0; ++ if (!of_property_read_u32(np, "marvell,xenon-sdhc-id", &sdhc_id)) { ++ nr_sdhc = sdhci_readl(host, XENON_SYS_CFG_INFO); ++ nr_sdhc &= XENON_NR_SUPPORTED_SLOT_MASK; ++ if (unlikely(sdhc_id > nr_sdhc)) { ++ dev_err(mmc_dev(mmc), "SDHC Index %d exceeds Number of SDHCs %d\n", ++ sdhc_id, nr_sdhc); ++ return -EINVAL; ++ } ++ } ++ priv->sdhc_id = sdhc_id; ++ ++ tuning_count = XENON_DEF_TUNING_COUNT; ++ if (!of_property_read_u32(np, "marvell,xenon-tun-count", ++ &tuning_count)) { ++ if (unlikely(tuning_count >= XENON_TMR_RETUN_NO_PRESENT)) { ++ dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n", ++ XENON_DEF_TUNING_COUNT); ++ tuning_count = XENON_DEF_TUNING_COUNT; ++ } ++ } ++ priv->tuning_count = tuning_count; ++ ++ return xenon_phy_parse_dt(np, host); ++} ++ ++static int xenon_sdhc_prepare(struct sdhci_host *host) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u8 sdhc_id = priv->sdhc_id; ++ ++ /* Enable SDHC */ ++ xenon_enable_sdhc(host, sdhc_id); ++ ++ /* Enable ACG */ ++ xenon_set_acg(host, true); ++ ++ /* Enable Parallel Transfer Mode */ ++ xenon_enable_sdhc_parallel_tran(host, sdhc_id); ++ ++ /* Disable SDCLK-Off-While-Idle before card init */ ++ xenon_set_sdclk_off_idle(host, sdhc_id, false); ++ ++ xenon_mask_cmd_conflict_err(host); ++ ++ return 0; ++} ++ ++static void xenon_sdhc_unprepare(struct sdhci_host *host) ++{ ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); ++ u8 sdhc_id = priv->sdhc_id; ++ ++ /* disable SDHC */ ++ xenon_disable_sdhc(host, sdhc_id); ++} ++ ++static int xenon_probe(struct platform_device *pdev) ++{ ++ struct sdhci_pltfm_host *pltfm_host; ++ struct sdhci_host *host; ++ struct xenon_priv *priv; ++ int err; ++ ++ host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata, ++ sizeof(struct xenon_priv)); ++ if (IS_ERR(host)) ++ return PTR_ERR(host); ++ ++ pltfm_host = sdhci_priv(host); ++ priv = sdhci_pltfm_priv(pltfm_host); ++ ++ /* ++ * Link Xenon specific mmc_host_ops function, ++ * to replace standard ones in sdhci_ops. ++ */ ++ xenon_replace_mmc_host_ops(host); ++ ++ pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); ++ if (IS_ERR(pltfm_host->clk)) { ++ err = PTR_ERR(pltfm_host->clk); ++ dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err); ++ goto free_pltfm; ++ } ++ err = clk_prepare_enable(pltfm_host->clk); ++ if (err) ++ goto free_pltfm; ++ ++ err = mmc_of_parse(host->mmc); ++ if (err) ++ goto err_clk; ++ ++ sdhci_get_of_property(pdev); ++ ++ xenon_set_acg(host, false); ++ ++ /* Xenon specific dt parse */ ++ err = xenon_probe_dt(pdev); ++ if (err) ++ goto err_clk; ++ ++ err = xenon_sdhc_prepare(host); ++ if (err) ++ goto err_clk; ++ ++ err = sdhci_add_host(host); ++ if (err) ++ goto remove_sdhc; ++ ++ return 0; ++ ++remove_sdhc: ++ xenon_sdhc_unprepare(host); ++err_clk: ++ clk_disable_unprepare(pltfm_host->clk); ++free_pltfm: ++ sdhci_pltfm_free(pdev); ++ return err; ++} ++ ++static int xenon_remove(struct platform_device *pdev) ++{ ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ ++ sdhci_remove_host(host, 0); ++ ++ xenon_sdhc_unprepare(host); ++ ++ clk_disable_unprepare(pltfm_host->clk); ++ ++ sdhci_pltfm_free(pdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id sdhci_xenon_dt_ids[] = { ++ { .compatible = "marvell,armada-ap806-sdhci",}, ++ { .compatible = "marvell,armada-cp110-sdhci",}, ++ { .compatible = "marvell,armada-3700-sdhci",}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids); ++ ++static struct platform_driver sdhci_xenon_driver = { ++ .driver = { ++ .name = "xenon-sdhci", ++ .of_match_table = sdhci_xenon_dt_ids, ++ .pm = &sdhci_pltfm_pmops, ++ }, ++ .probe = xenon_probe, ++ .remove = xenon_remove, ++}; ++ ++module_platform_driver(sdhci_xenon_driver); ++ ++MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC"); ++MODULE_AUTHOR("Hu Ziji "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h +new file mode 100644 +index 00000000..73debb4 +--- /dev/null ++++ b/drivers/mmc/host/sdhci-xenon.h +@@ -0,0 +1,100 @@ ++/* ++ * Copyright (C) 2016 Marvell, All Rights Reserved. ++ * ++ * Author: Hu Ziji ++ * Date: 2016-8-24 ++ * ++ * 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 version 2. ++ */ ++#ifndef SDHCI_XENON_H_ ++#define SDHCI_XENON_H_ ++ ++/* Register Offset of Xenon SDHC self-defined register */ ++#define XENON_SYS_CFG_INFO 0x0104 ++#define XENON_SLOT_TYPE_SDIO_SHIFT 24 ++#define XENON_NR_SUPPORTED_SLOT_MASK 0x7 ++ ++#define XENON_SYS_OP_CTRL 0x0108 ++#define XENON_AUTO_CLKGATE_DISABLE_MASK BIT(20) ++#define XENON_SDCLK_IDLEOFF_ENABLE_SHIFT 8 ++#define XENON_SLOT_ENABLE_SHIFT 0 ++ ++#define XENON_SYS_EXT_OP_CTRL 0x010C ++#define XENON_MASK_CMD_CONFLICT_ERR BIT(8) ++ ++#define XENON_SLOT_OP_STATUS_CTRL 0x0128 ++#define XENON_TUN_CONSECUTIVE_TIMES_SHIFT 16 ++#define XENON_TUN_CONSECUTIVE_TIMES_MASK 0x7 ++#define XENON_TUN_CONSECUTIVE_TIMES 0x4 ++#define XENON_TUNING_STEP_SHIFT 12 ++#define XENON_TUNING_STEP_MASK 0xF ++#define XENON_TUNING_STEP_DIVIDER BIT(6) ++ ++#define XENON_SLOT_EMMC_CTRL 0x0130 ++#define XENON_ENABLE_DATA_STROBE BIT(24) ++ ++#define XENON_SLOT_RETUNING_REQ_CTRL 0x0144 ++/* retuning compatible */ ++#define XENON_RETUNING_COMPATIBLE 0x1 ++ ++#define XENON_SLOT_EXT_PRESENT_STATE 0x014C ++#define XENON_DLL_LOCK_STATE 0x1 ++ ++#define XENON_SLOT_DLL_CUR_DLY_VAL 0x0150 ++ ++/* Tuning Parameter */ ++#define XENON_TMR_RETUN_NO_PRESENT 0xF ++#define XENON_DEF_TUNING_COUNT 0x9 ++ ++#define XENON_DEFAULT_SDCLK_FREQ 400000 ++#define XENON_LOWEST_SDCLK_FREQ 100000 ++ ++/* Xenon specific Mode Select value */ ++#define XENON_CTRL_HS200 0x5 ++#define XENON_CTRL_HS400 0x6 ++ ++struct xenon_priv { ++ unsigned char tuning_count; ++ /* idx of SDHC */ ++ u8 sdhc_id; ++ ++ /* ++ * eMMC/SD/SDIO require different register settings. ++ * Xenon driver has to recognize card type ++ * before mmc_host->card is not available. ++ * This field records the card type during init. ++ * It is updated in xenon_init_card(). ++ * ++ * It is only valid during initialization after it is updated. ++ * Do not access this variable in normal transfers after ++ * initialization completes. ++ */ ++ unsigned int init_card_type; ++ ++ /* ++ * The bus_width, timing, and clock fields in below ++ * record the current ios setting of Xenon SDHC. ++ * Driver will adjust PHY setting if any change to ++ * ios affects PHY timing. ++ */ ++ unsigned char bus_width; ++ unsigned char timing; ++ unsigned int clock; ++ ++ int phy_type; ++ /* ++ * Contains board-specific PHY parameters ++ * passed from device tree. ++ */ ++ void *phy_params; ++ struct xenon_emmc_phy_regs *emmc_phy_regs; ++}; ++ ++int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios); ++int xenon_phy_parse_dt(struct device_node *np, ++ struct sdhci_host *host); ++void xenon_soc_pad_ctrl(struct sdhci_host *host, ++ unsigned char signal_voltage); ++#endif +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index 6bf58d2..fe77d30 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + +@@ -1343,20 +1344,10 @@ u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock, + } + EXPORT_SYMBOL_GPL(sdhci_calc_clk); + +-void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) ++void sdhci_enable_clk(struct sdhci_host *host, u16 clk) + { +- u16 clk; + unsigned long timeout; + +- host->mmc->actual_clock = 0; +- +- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); +- +- if (clock == 0) +- return; +- +- clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); +- + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + +@@ -1379,6 +1370,22 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + } ++EXPORT_SYMBOL_GPL(sdhci_enable_clk); ++ ++void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) ++{ ++ u16 clk; ++ ++ host->mmc->actual_clock = 0; ++ ++ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ ++ if (clock == 0) ++ return; ++ ++ clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); ++ sdhci_enable_clk(host, clk); ++} + EXPORT_SYMBOL_GPL(sdhci_set_clock); + + static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode, +@@ -1572,12 +1579,15 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) + } + EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); + +-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + { + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + u8 ctrl; + ++ if (ios->power_mode == MMC_POWER_UNDEFINED) ++ return; ++ + spin_lock_irqsave(&host->lock, flags); + + if (host->flags & SDHCI_DEVICE_DEAD) { +@@ -1632,7 +1642,14 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + + if ((ios->timing == MMC_TIMING_SD_HS || +- ios->timing == MMC_TIMING_MMC_HS) ++ ios->timing == MMC_TIMING_MMC_HS || ++ ios->timing == MMC_TIMING_MMC_HS400 || ++ ios->timing == MMC_TIMING_MMC_HS200 || ++ ios->timing == MMC_TIMING_MMC_DDR52 || ++ ios->timing == MMC_TIMING_UHS_SDR50 || ++ ios->timing == MMC_TIMING_UHS_SDR104 || ++ ios->timing == MMC_TIMING_UHS_DDR50 || ++ ios->timing == MMC_TIMING_UHS_SDR25) + && !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) + ctrl |= SDHCI_CTRL_HISPD; + else +@@ -1641,16 +1658,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + if (host->version >= SDHCI_SPEC_300) { + u16 clk, ctrl_2; + +- /* In case of UHS-I modes, set High Speed Enable */ +- if ((ios->timing == MMC_TIMING_MMC_HS400) || +- (ios->timing == MMC_TIMING_MMC_HS200) || +- (ios->timing == MMC_TIMING_MMC_DDR52) || +- (ios->timing == MMC_TIMING_UHS_SDR50) || +- (ios->timing == MMC_TIMING_UHS_SDR104) || +- (ios->timing == MMC_TIMING_UHS_DDR50) || +- (ios->timing == MMC_TIMING_UHS_SDR25)) +- ctrl |= SDHCI_CTRL_HISPD; +- + if (!host->preset_enabled) { + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + /* +@@ -1732,6 +1739,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); + } ++EXPORT_SYMBOL_GPL(sdhci_set_ios); + + static int sdhci_get_cd(struct mmc_host *mmc) + { +@@ -1825,7 +1833,7 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) + } + } + +-static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) ++void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) + { + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; +@@ -1845,9 +1853,10 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) + if (!enable) + pm_runtime_put_noidle(host->mmc->parent); + } ++EXPORT_SYMBOL_GPL(sdhci_enable_sdio_irq); + +-static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, +- struct mmc_ios *ios) ++int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, ++ struct mmc_ios *ios) + { + struct sdhci_host *host = mmc_priv(mmc); + u16 ctrl; +@@ -1939,6 +1948,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, + return 0; + } + } ++EXPORT_SYMBOL_GPL(sdhci_start_signal_voltage_switch); + + static int sdhci_card_busy(struct mmc_host *mmc) + { +@@ -1963,11 +1973,157 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) + return 0; + } + +-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) ++static void sdhci_start_tuning(struct sdhci_host *host) ++{ ++ u16 ctrl; ++ ++ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl |= SDHCI_CTRL_EXEC_TUNING; ++ if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND) ++ ctrl |= SDHCI_CTRL_TUNED_CLK; ++ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); ++ ++ /* ++ * As per the Host Controller spec v3.00, tuning command ++ * generates Buffer Read Ready interrupt, so enable that. ++ * ++ * Note: The spec clearly says that when tuning sequence ++ * is being performed, the controller does not generate ++ * interrupts other than Buffer Read Ready interrupt. But ++ * to make sure we don't hit a controller bug, we _only_ ++ * enable Buffer Read Ready interrupt here. ++ */ ++ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); ++ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); ++} ++ ++static void sdhci_end_tuning(struct sdhci_host *host) ++{ ++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); ++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++} ++ ++static void sdhci_reset_tuning(struct sdhci_host *host) + { +- struct sdhci_host *host = mmc_priv(mmc); + u16 ctrl; +- int tuning_loop_counter = MAX_TUNING_LOOP; ++ ++ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl &= ~SDHCI_CTRL_TUNED_CLK; ++ ctrl &= ~SDHCI_CTRL_EXEC_TUNING; ++ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); ++} ++ ++static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode, ++ unsigned long flags) ++{ ++ sdhci_reset_tuning(host); ++ ++ sdhci_do_reset(host, SDHCI_RESET_CMD); ++ sdhci_do_reset(host, SDHCI_RESET_DATA); ++ ++ sdhci_end_tuning(host); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ mmc_abort_tuning(host->mmc, opcode); ++ spin_lock_irqsave(&host->lock, flags); ++} ++ ++/* ++ * We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI ++ * tuning command does not have a data payload (or rather the hardware does it ++ * automatically) so mmc_send_tuning() will return -EIO. Also the tuning command ++ * interrupt setup is different to other commands and there is no timeout ++ * interrupt so special handling is needed. ++ */ ++static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode, ++ unsigned long flags) ++{ ++ struct mmc_host *mmc = host->mmc; ++ struct mmc_command cmd = {0}; ++ struct mmc_request mrq = {NULL}; ++ ++ cmd.opcode = opcode; ++ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.mrq = &mrq; ++ ++ mrq.cmd = &cmd; ++ /* ++ * In response to CMD19, the card sends 64 bytes of tuning ++ * block to the Host Controller. So we set the block size ++ * to 64 here. ++ */ ++ if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200 && ++ mmc->ios.bus_width == MMC_BUS_WIDTH_8) ++ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), SDHCI_BLOCK_SIZE); ++ else ++ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE); ++ ++ /* ++ * The tuning block is sent by the card to the host controller. ++ * So we set the TRNS_READ bit in the Transfer Mode register. ++ * This also takes care of setting DMA Enable and Multi Block ++ * Select in the same register to 0. ++ */ ++ sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); ++ ++ sdhci_send_command(host, &cmd); ++ ++ host->cmd = NULL; ++ ++ sdhci_del_timer(host, &mrq); ++ ++ host->tuning_done = 0; ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ /* Wait for Buffer Read Ready interrupt */ ++ wait_event_timeout(host->buf_ready_int, (host->tuning_done == 1), ++ msecs_to_jiffies(50)); ++ ++ spin_lock_irqsave(&host->lock, flags); ++} ++ ++static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode, ++ unsigned long flags) ++{ ++ int i; ++ ++ /* ++ * Issue opcode repeatedly till Execute Tuning is set to 0 or the number ++ * of loops reaches 40 times. ++ */ ++ for (i = 0; i < MAX_TUNING_LOOP; i++) { ++ u16 ctrl; ++ ++ sdhci_send_tuning(host, opcode, flags); ++ ++ if (!host->tuning_done) { ++ pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", ++ mmc_hostname(host->mmc)); ++ sdhci_abort_tuning(host, opcode, flags); ++ return; ++ } ++ ++ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { ++ if (ctrl & SDHCI_CTRL_TUNED_CLK) ++ return; /* Success! */ ++ break; ++ } ++ ++ /* eMMC spec does not require a delay between tuning cycles */ ++ if (opcode == MMC_SEND_TUNING_BLOCK) ++ mdelay(1); ++ } ++ ++ pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", ++ mmc_hostname(host->mmc)); ++ sdhci_reset_tuning(host); ++} ++ ++int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); + int err = 0; + unsigned long flags; + unsigned int tuning_count = 0; +@@ -1976,7 +2132,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + spin_lock_irqsave(&host->lock, flags); + + hs400_tuning = host->flags & SDHCI_HS400_TUNING; +- host->flags &= ~SDHCI_HS400_TUNING; + + if (host->tuning_mode == SDHCI_TUNING_MODE_1) + tuning_count = host->tuning_count; +@@ -2019,159 +2174,24 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) + if (host->ops->platform_execute_tuning) { + spin_unlock_irqrestore(&host->lock, flags); + err = host->ops->platform_execute_tuning(host, opcode); +- return err; +- } +- +- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- ctrl |= SDHCI_CTRL_EXEC_TUNING; +- if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND) +- ctrl |= SDHCI_CTRL_TUNED_CLK; +- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); +- +- /* +- * As per the Host Controller spec v3.00, tuning command +- * generates Buffer Read Ready interrupt, so enable that. +- * +- * Note: The spec clearly says that when tuning sequence +- * is being performed, the controller does not generate +- * interrupts other than Buffer Read Ready interrupt. But +- * to make sure we don't hit a controller bug, we _only_ +- * enable Buffer Read Ready interrupt here. +- */ +- sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); +- sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); +- +- /* +- * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number +- * of loops reaches 40 times. +- */ +- do { +- struct mmc_command cmd = {0}; +- struct mmc_request mrq = {NULL}; +- +- cmd.opcode = opcode; +- cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; +- cmd.retries = 0; +- cmd.data = NULL; +- cmd.mrq = &mrq; +- cmd.error = 0; +- +- if (tuning_loop_counter-- == 0) +- break; +- +- mrq.cmd = &cmd; +- +- /* +- * In response to CMD19, the card sends 64 bytes of tuning +- * block to the Host Controller. So we set the block size +- * to 64 here. +- */ +- if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) { +- if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) +- sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), +- SDHCI_BLOCK_SIZE); +- else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) +- sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), +- SDHCI_BLOCK_SIZE); +- } else { +- sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), +- SDHCI_BLOCK_SIZE); +- } +- +- /* +- * The tuning block is sent by the card to the host controller. +- * So we set the TRNS_READ bit in the Transfer Mode register. +- * This also takes care of setting DMA Enable and Multi Block +- * Select in the same register to 0. +- */ +- sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); +- +- sdhci_send_command(host, &cmd); +- +- host->cmd = NULL; +- sdhci_del_timer(host, &mrq); +- +- spin_unlock_irqrestore(&host->lock, flags); +- /* Wait for Buffer Read Ready interrupt */ +- wait_event_timeout(host->buf_ready_int, +- (host->tuning_done == 1), +- msecs_to_jiffies(50)); + spin_lock_irqsave(&host->lock, flags); +- +- if (!host->tuning_done) { +- pr_info(DRIVER_NAME ": Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock\n"); +- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- ctrl &= ~SDHCI_CTRL_TUNED_CLK; +- ctrl &= ~SDHCI_CTRL_EXEC_TUNING; +- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); +- +- sdhci_do_reset(host, SDHCI_RESET_CMD); +- sdhci_do_reset(host, SDHCI_RESET_DATA); +- +- err = -EIO; +- +- if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200) +- goto out; +- +- sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +- sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +- +- spin_unlock_irqrestore(&host->lock, flags); +- +- memset(&cmd, 0, sizeof(cmd)); +- cmd.opcode = MMC_STOP_TRANSMISSION; +- cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; +- cmd.busy_timeout = 50; +- mmc_wait_for_cmd(mmc, &cmd, 0); +- +- spin_lock_irqsave(&host->lock, flags); +- +- goto out; +- } +- +- host->tuning_done = 0; +- +- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); +- +- /* eMMC spec does not require a delay between tuning cycles */ +- if (opcode == MMC_SEND_TUNING_BLOCK) +- mdelay(1); +- } while (ctrl & SDHCI_CTRL_EXEC_TUNING); +- +- /* +- * The Host Driver has exhausted the maximum number of loops allowed, +- * so use fixed sampling frequency. +- */ +- if (tuning_loop_counter < 0) { +- ctrl &= ~SDHCI_CTRL_TUNED_CLK; +- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); +- } +- if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { +- pr_info(DRIVER_NAME ": Tuning procedure failed, falling back to fixed sampling clock\n"); +- err = -EIO; ++ goto out_unlock; + } + +-out: +- if (tuning_count) { +- /* +- * In case tuning fails, host controllers which support +- * re-tuning can try tuning again at a later time, when the +- * re-tuning timer expires. So for these controllers, we +- * return 0. Since there might be other controllers who do not +- * have this capability, we return error for them. +- */ +- err = 0; +- } ++ host->mmc->retune_period = tuning_count; + +- host->mmc->retune_period = err ? 0 : tuning_count; ++ sdhci_start_tuning(host); + +- sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); +- sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ __sdhci_execute_tuning(host, opcode, flags); ++ ++ sdhci_end_tuning(host); + out_unlock: ++ host->flags &= ~SDHCI_HS400_TUNING; + spin_unlock_irqrestore(&host->lock, flags); ++ + return err; + } ++EXPORT_SYMBOL_GPL(sdhci_execute_tuning); + + static int sdhci_select_drive_strength(struct mmc_card *card, + unsigned int max_dtr, int host_drv, +@@ -2229,8 +2249,7 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + data->host_cookie = COOKIE_UNMAPPED; + } + +-static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, +- bool is_first_req) ++static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) + { + struct sdhci_host *host = mmc_priv(mmc); + +@@ -2943,23 +2962,25 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) + + sdhci_init(host, 0); + +- /* Force clock and power re-program */ +- host->pwr = 0; +- host->clock = 0; +- mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); +- mmc->ops->set_ios(mmc, &mmc->ios); ++ if (mmc->ios.power_mode != MMC_POWER_UNDEFINED) { ++ /* Force clock and power re-program */ ++ host->pwr = 0; ++ host->clock = 0; ++ mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); ++ mmc->ops->set_ios(mmc, &mmc->ios); ++ ++ if ((host_flags & SDHCI_PV_ENABLED) && ++ !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { ++ spin_lock_irqsave(&host->lock, flags); ++ sdhci_enable_preset_value(host, true); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } + +- if ((host_flags & SDHCI_PV_ENABLED) && +- !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { +- spin_lock_irqsave(&host->lock, flags); +- sdhci_enable_preset_value(host, true); +- spin_unlock_irqrestore(&host->lock, flags); ++ if ((mmc->caps2 & MMC_CAP2_HS400_ES) && ++ mmc->ops->hs400_enhanced_strobe) ++ mmc->ops->hs400_enhanced_strobe(mmc, &mmc->ios); + } + +- if ((mmc->caps2 & MMC_CAP2_HS400_ES) && +- mmc->ops->hs400_enhanced_strobe) +- mmc->ops->hs400_enhanced_strobe(mmc, &mmc->ios); +- + spin_lock_irqsave(&host->lock, flags); + + host->runtime_suspended = false; +@@ -3042,6 +3063,8 @@ static int sdhci_set_dma_mask(struct sdhci_host *host) + void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1) + { + u16 v; ++ u64 dt_caps_mask = 0; ++ u64 dt_caps = 0; + + if (host->read_caps) + return; +@@ -3056,18 +3079,35 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1) + + sdhci_do_reset(host, SDHCI_RESET_ALL); + ++ of_property_read_u64(mmc_dev(host->mmc)->of_node, ++ "sdhci-caps-mask", &dt_caps_mask); ++ of_property_read_u64(mmc_dev(host->mmc)->of_node, ++ "sdhci-caps", &dt_caps); ++ + v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION); + host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; + + if (host->quirks & SDHCI_QUIRK_MISSING_CAPS) + return; + +- host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES); ++ if (caps) { ++ host->caps = *caps; ++ } else { ++ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); ++ host->caps &= ~lower_32_bits(dt_caps_mask); ++ host->caps |= lower_32_bits(dt_caps); ++ } + + if (host->version < SDHCI_SPEC_300) + return; + +- host->caps1 = caps1 ? *caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1); ++ if (caps1) { ++ host->caps1 = *caps1; ++ } else { ++ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); ++ host->caps1 &= ~upper_32_bits(dt_caps_mask); ++ host->caps1 |= upper_32_bits(dt_caps); ++ } + } + EXPORT_SYMBOL_GPL(__sdhci_read_caps); + +diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h +index 2570455..9879ecc 100644 +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -682,6 +682,7 @@ static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) + u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock, + unsigned int *actual_clock); + void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); ++void sdhci_enable_clk(struct sdhci_host *host, u16 clk); + void sdhci_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd); + void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, +@@ -689,6 +690,11 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, + void sdhci_set_bus_width(struct sdhci_host *host, int width); + void sdhci_reset(struct sdhci_host *host, u8 mask); + void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); ++int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); ++void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); ++int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, ++ struct mmc_ios *ios); ++void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable); + + #ifdef CONFIG_PM + extern int sdhci_suspend_host(struct sdhci_host *host); +diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c +index b833e6c..84b1613 100644 +--- a/drivers/mtd/devices/docg3.c ++++ b/drivers/mtd/devices/docg3.c +@@ -1809,37 +1809,22 @@ static int dbg_protection_show(struct seq_file *s, void *p) + } + DEBUGFS_RO_ATTR(protection, dbg_protection_show); + +-static int __init doc_dbg_register(struct docg3 *docg3) +-{ +- struct dentry *root, *entry; +- +- root = debugfs_create_dir("docg3", NULL); +- if (!root) +- return -ENOMEM; +- +- entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3, +- &flashcontrol_fops); +- if (entry) +- entry = debugfs_create_file("asic_mode", S_IRUSR, root, +- docg3, &asic_mode_fops); +- if (entry) +- entry = debugfs_create_file("device_id", S_IRUSR, root, +- docg3, &device_id_fops); +- if (entry) +- entry = debugfs_create_file("protection", S_IRUSR, root, +- docg3, &protection_fops); +- if (entry) { +- docg3->debugfs_root = root; +- return 0; +- } else { +- debugfs_remove_recursive(root); +- return -ENOMEM; +- } +-} +- +-static void doc_dbg_unregister(struct docg3 *docg3) ++static void __init doc_dbg_register(struct mtd_info *floor) + { +- debugfs_remove_recursive(docg3->debugfs_root); ++ struct dentry *root = floor->dbg.dfs_dir; ++ struct docg3 *docg3 = floor->priv; ++ ++ if (IS_ERR_OR_NULL(root)) ++ return; ++ ++ debugfs_create_file("docg3_flashcontrol", S_IRUSR, root, docg3, ++ &flashcontrol_fops); ++ debugfs_create_file("docg3_asic_mode", S_IRUSR, root, docg3, ++ &asic_mode_fops); ++ debugfs_create_file("docg3_device_id", S_IRUSR, root, docg3, ++ &device_id_fops); ++ debugfs_create_file("docg3_protection", S_IRUSR, root, docg3, ++ &protection_fops); + } + + /** +@@ -2114,6 +2099,8 @@ static int __init docg3_probe(struct platform_device *pdev) + 0); + if (ret) + goto err_probe; ++ ++ doc_dbg_register(cascade->floors[floor]); + } + + ret = doc_register_sysfs(pdev, cascade); +@@ -2121,7 +2108,6 @@ static int __init docg3_probe(struct platform_device *pdev) + goto err_probe; + + platform_set_drvdata(pdev, cascade); +- doc_dbg_register(cascade->floors[0]->priv); + return 0; + + notfound: +@@ -2148,7 +2134,6 @@ static int docg3_release(struct platform_device *pdev) + int floor; + + doc_unregister_sysfs(pdev, cascade); +- doc_dbg_unregister(docg3); + for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) + if (cascade->floors[floor]) + doc_release_device(cascade->floors[floor]); +diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h +index 19fb93f..e999465 100644 +--- a/drivers/mtd/devices/docg3.h ++++ b/drivers/mtd/devices/docg3.h +@@ -299,7 +299,6 @@ struct docg3_cascade { + * @oob_autoecc: if 1, use only bytes 0-7, 15, and fill the others with HW ECC + * if 0, use all the 16 bytes. + * @oob_write_buf: prepared OOB for next page_write +- * @debugfs_root: debugfs root node + */ + struct docg3 { + struct device *dev; +@@ -312,7 +311,6 @@ struct docg3 { + loff_t oob_write_ofs; + int oob_autoecc; + u8 oob_write_buf[DOC_LAYOUT_OOB_SIZE]; +- struct dentry *debugfs_root; + }; + + #define doc_err(fmt, arg...) dev_err(docg3->dev, (fmt), ## arg) +diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c +index d46e4ad..84fa97d 100644 +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -478,6 +479,8 @@ int mtd_pairing_groups(struct mtd_info *mtd) + } + EXPORT_SYMBOL_GPL(mtd_pairing_groups); + ++static struct dentry *dfs_dir_mtd; ++ + /** + * add_mtd_device - register an MTD device + * @mtd: pointer to new MTD device info structure +@@ -555,6 +558,14 @@ int add_mtd_device(struct mtd_info *mtd) + if (error) + goto fail_added; + ++ if (!IS_ERR_OR_NULL(dfs_dir_mtd)) { ++ mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd); ++ if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) { ++ pr_debug("mtd device %s won't show data in debugfs\n", ++ dev_name(&mtd->dev)); ++ } ++ } ++ + device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL, + "mtd%dro", i); + +@@ -597,6 +608,8 @@ int del_mtd_device(struct mtd_info *mtd) + + mutex_lock(&mtd_table_mutex); + ++ debugfs_remove_recursive(mtd->dbg.dfs_dir); ++ + if (idr_find(&mtd_idr, mtd->index) != mtd) { + ret = -ENODEV; + goto out_error; +@@ -1805,6 +1818,8 @@ static int __init init_mtd(void) + if (ret) + goto out_procfs; + ++ dfs_dir_mtd = debugfs_create_dir("mtd", NULL); ++ + return 0; + + out_procfs: +@@ -1819,6 +1834,7 @@ static int __init init_mtd(void) + + static void __exit cleanup_mtd(void) + { ++ debugfs_remove_recursive(dfs_dir_mtd); + cleanup_mtdchar(); + if (proc_mtd) + remove_proc_entry("mtd", NULL); +diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c +index cb06bdd..e7285fe 100644 +--- a/drivers/mtd/mtdswap.c ++++ b/drivers/mtd/mtdswap.c +@@ -138,8 +138,6 @@ struct mtdswap_dev { + + char *page_buf; + char *oob_buf; +- +- struct dentry *debugfs_root; + }; + + struct mtdswap_oobdata { +@@ -1320,26 +1318,19 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d) + struct gendisk *gd = d->mbd_dev->disk; + struct device *dev = disk_to_dev(gd); + +- struct dentry *root; ++ struct dentry *root = d->mtd->dbg.dfs_dir; + struct dentry *dent; + +- root = debugfs_create_dir(gd->disk_name, NULL); +- if (IS_ERR(root)) ++ if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + +- if (!root) { +- dev_err(dev, "failed to initialize debugfs\n"); ++ if (IS_ERR_OR_NULL(root)) + return -1; +- } +- +- d->debugfs_root = root; + +- dent = debugfs_create_file("stats", S_IRUSR, root, d, ++ dent = debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, + &mtdswap_fops); + if (!dent) { + dev_err(d->dev, "debugfs_create_file failed\n"); +- debugfs_remove_recursive(root); +- d->debugfs_root = NULL; + return -1; + } + +@@ -1542,7 +1533,6 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev) + { + struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev); + +- debugfs_remove_recursive(d->debugfs_root); + del_mtd_blktrans_dev(dev); + mtdswap_cleanup(d); + kfree(d); +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index b254090..6d53734 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -1,572 +1 @@ +-config MTD_NAND_ECC +- tristate +- +-config MTD_NAND_ECC_SMC +- bool "NAND ECC Smart Media byte order" +- depends on MTD_NAND_ECC +- default n +- help +- Software ECC according to the Smart Media Specification. +- The original Linux implementation had byte 0 and 1 swapped. +- +- +-menuconfig MTD_NAND +- tristate "NAND Device Support" +- depends on MTD +- select MTD_NAND_IDS +- select MTD_NAND_ECC +- help +- This enables support for accessing all type of NAND flash +- devices. For further information see +- . +- +-if MTD_NAND +- +-config MTD_NAND_BCH +- tristate +- select BCH +- depends on MTD_NAND_ECC_BCH +- default MTD_NAND +- +-config MTD_NAND_ECC_BCH +- bool "Support software BCH ECC" +- default n +- help +- This enables support for software BCH error correction. Binary BCH +- codes are more powerful and cpu intensive than traditional Hamming +- ECC codes. They are used with NAND devices requiring more than 1 bit +- of error correction. +- +-config MTD_SM_COMMON +- tristate +- default n +- +-config MTD_NAND_DENALI +- tristate +- +-config MTD_NAND_DENALI_PCI +- tristate "Support Denali NAND controller on Intel Moorestown" +- select MTD_NAND_DENALI +- depends on HAS_DMA && PCI +- help +- Enable the driver for NAND flash on Intel Moorestown, using the +- Denali NAND controller core. +- +-config MTD_NAND_DENALI_DT +- tristate "Support Denali NAND controller as a DT device" +- select MTD_NAND_DENALI +- depends on HAS_DMA && HAVE_CLK && OF +- help +- Enable the driver for NAND flash on platforms using a Denali NAND +- controller as a DT device. +- +-config MTD_NAND_DENALI_SCRATCH_REG_ADDR +- hex "Denali NAND size scratch register address" +- default "0xFF108018" +- depends on MTD_NAND_DENALI_PCI +- help +- Some platforms place the NAND chip size in a scratch register +- because (some versions of) the driver aren't able to automatically +- determine the size of certain chips. Set the address of the +- scratch register here to enable this feature. On Intel Moorestown +- boards, the scratch register is at 0xFF108018. +- +-config MTD_NAND_GPIO +- tristate "GPIO assisted NAND Flash driver" +- depends on GPIOLIB || COMPILE_TEST +- depends on HAS_IOMEM +- help +- This enables a NAND flash driver where control signals are +- connected to GPIO pins, and commands and data are communicated +- via a memory mapped interface. +- +-config MTD_NAND_AMS_DELTA +- tristate "NAND Flash device on Amstrad E3" +- depends on MACH_AMS_DELTA +- default y +- help +- Support for NAND flash on Amstrad E3 (Delta). +- +-config MTD_NAND_OMAP2 +- tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone" +- depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE) +- help +- Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 +- and Keystone platforms. +- +-config MTD_NAND_OMAP_BCH +- depends on MTD_NAND_OMAP2 +- bool "Support hardware based BCH error correction" +- default n +- select BCH +- help +- This config enables the ELM hardware engine, which can be used to +- locate and correct errors when using BCH ECC scheme. This offloads +- the cpu from doing ECC error searching and correction. However some +- legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine +- so this is optional for them. +- +-config MTD_NAND_OMAP_BCH_BUILD +- def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH +- +-config MTD_NAND_IDS +- tristate +- +-config MTD_NAND_RICOH +- tristate "Ricoh xD card reader" +- default n +- depends on PCI +- select MTD_SM_COMMON +- help +- Enable support for Ricoh R5C852 xD card reader +- You also need to enable ether +- NAND SSFDC (SmartMedia) read only translation layer' or new +- expermental, readwrite +- 'SmartMedia/xD new translation layer' +- +-config MTD_NAND_AU1550 +- tristate "Au1550/1200 NAND support" +- depends on MIPS_ALCHEMY +- help +- This enables the driver for the NAND flash controller on the +- AMD/Alchemy 1550 SOC. +- +-config MTD_NAND_BF5XX +- tristate "Blackfin on-chip NAND Flash Controller driver" +- depends on BF54x || BF52x +- help +- This enables the Blackfin on-chip NAND flash controller +- +- No board specific support is done by this driver, each board +- must advertise a platform_device for the driver to attach. +- +- This driver can also be built as a module. If so, the module +- will be called bf5xx-nand. +- +-config MTD_NAND_BF5XX_HWECC +- bool "BF5XX NAND Hardware ECC" +- default y +- depends on MTD_NAND_BF5XX +- help +- Enable the use of the BF5XX's internal ECC generator when +- using NAND. +- +-config MTD_NAND_BF5XX_BOOTROM_ECC +- bool "Use Blackfin BootROM ECC Layout" +- default n +- depends on MTD_NAND_BF5XX_HWECC +- help +- If you wish to modify NAND pages and allow the Blackfin on-chip +- BootROM to boot from them, say Y here. This is only necessary +- if you are booting U-Boot out of NAND and you wish to update +- U-Boot from Linux' userspace. Otherwise, you should say N here. +- +- If unsure, say N. +- +-config MTD_NAND_S3C2410 +- tristate "NAND Flash support for Samsung S3C SoCs" +- depends on ARCH_S3C24XX || ARCH_S3C64XX +- help +- This enables the NAND flash controller on the S3C24xx and S3C64xx +- SoCs +- +- No board specific support is done by this driver, each board +- must advertise a platform_device for the driver to attach. +- +-config MTD_NAND_S3C2410_DEBUG +- bool "Samsung S3C NAND driver debug" +- depends on MTD_NAND_S3C2410 +- help +- Enable debugging of the S3C NAND driver +- +-config MTD_NAND_S3C2410_HWECC +- bool "Samsung S3C NAND Hardware ECC" +- depends on MTD_NAND_S3C2410 +- help +- Enable the use of the controller's internal ECC generator when +- using NAND. Early versions of the chips have had problems with +- incorrect ECC generation, and if using these, the default of +- software ECC is preferable. +- +-config MTD_NAND_NDFC +- tristate "NDFC NanD Flash Controller" +- depends on 4xx +- select MTD_NAND_ECC_SMC +- help +- NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs +- +-config MTD_NAND_S3C2410_CLKSTOP +- bool "Samsung S3C NAND IDLE clock stop" +- depends on MTD_NAND_S3C2410 +- default n +- help +- Stop the clock to the NAND controller when there is no chip +- selected to save power. This will mean there is a small delay +- when the is NAND chip selected or released, but will save +- approximately 5mA of power when there is nothing happening. +- +-config MTD_NAND_DISKONCHIP +- tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)" +- depends on HAS_IOMEM +- select REED_SOLOMON +- select REED_SOLOMON_DEC16 +- help +- This is a reimplementation of M-Systems DiskOnChip 2000, +- Millennium and Millennium Plus as a standard NAND device driver, +- as opposed to the earlier self-contained MTD device drivers. +- This should enable, among other things, proper JFFS2 operation on +- these devices. +- +-config MTD_NAND_DISKONCHIP_PROBE_ADVANCED +- bool "Advanced detection options for DiskOnChip" +- depends on MTD_NAND_DISKONCHIP +- help +- This option allows you to specify nonstandard address at which to +- probe for a DiskOnChip, or to change the detection options. You +- are unlikely to need any of this unless you are using LinuxBIOS. +- Say 'N'. +- +-config MTD_NAND_DISKONCHIP_PROBE_ADDRESS +- hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED +- depends on MTD_NAND_DISKONCHIP +- default "0" +- ---help--- +- By default, the probe for DiskOnChip devices will look for a +- DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. +- This option allows you to specify a single address at which to probe +- for the device, which is useful if you have other devices in that +- range which get upset when they are probed. +- +- (Note that on PowerPC, the normal probe will only check at +- 0xE4000000.) +- +- Normally, you should leave this set to zero, to allow the probe at +- the normal addresses. +- +-config MTD_NAND_DISKONCHIP_PROBE_HIGH +- bool "Probe high addresses" +- depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED +- help +- By default, the probe for DiskOnChip devices will look for a +- DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. +- This option changes to make it probe between 0xFFFC8000 and +- 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be +- useful to you. Say 'N'. +- +-config MTD_NAND_DISKONCHIP_BBTWRITE +- bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP" +- depends on MTD_NAND_DISKONCHIP +- help +- On DiskOnChip devices shipped with the INFTL filesystem (Millennium +- and 2000 TSOP/Alon), Linux reserves some space at the end of the +- device for the Bad Block Table (BBT). If you have existing INFTL +- data on your device (created by non-Linux tools such as M-Systems' +- DOS drivers), your data might overlap the area Linux wants to use for +- the BBT. If this is a concern for you, leave this option disabled and +- Linux will not write BBT data into this area. +- The downside of leaving this option disabled is that if bad blocks +- are detected by Linux, they will not be recorded in the BBT, which +- could cause future problems. +- Once you enable this option, new filesystems (INFTL or others, created +- in Linux or other operating systems) will not use the reserved area. +- The only reason not to enable this option is to prevent damage to +- preexisting filesystems. +- Even if you leave this disabled, you can enable BBT writes at module +- load time (assuming you build diskonchip as a module) with the module +- parameter "inftl_bbt_write=1". +- +-config MTD_NAND_DOCG4 +- tristate "Support for DiskOnChip G4" +- depends on HAS_IOMEM +- select BCH +- select BITREVERSE +- help +- Support for diskonchip G4 nand flash, found in various smartphones and +- PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba +- Portege G900, Asus P526, and O2 XDA Zinc. +- +- With this driver you will be able to use UBI and create a ubifs on the +- device, so you may wish to consider enabling UBI and UBIFS as well. +- +- These devices ship with the Mys/Sandisk SAFTL formatting, for which +- there is currently no mtd parser, so you may want to use command line +- partitioning to segregate write-protected blocks. On the Treo680, the +- first five erase blocks (256KiB each) are write-protected, followed +- by the block containing the saftl partition table. This is probably +- typical. +- +-config MTD_NAND_SHARPSL +- tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" +- depends on ARCH_PXA +- +-config MTD_NAND_CAFE +- tristate "NAND support for OLPC CAFÉ chip" +- depends on PCI +- select REED_SOLOMON +- select REED_SOLOMON_DEC16 +- help +- Use NAND flash attached to the CAFÉ chip designed for the OLPC +- laptop. +- +-config MTD_NAND_CS553X +- tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" +- depends on X86_32 +- depends on !UML && HAS_IOMEM +- help +- The CS553x companion chips for the AMD Geode processor +- include NAND flash controllers with built-in hardware ECC +- capabilities; enabling this option will allow you to use +- these. The driver will check the MSRs to verify that the +- controller is enabled for NAND, and currently requires that +- the controller be in MMIO mode. +- +- If you say "m", the module will be called cs553x_nand. +- +-config MTD_NAND_ATMEL +- tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32" +- depends on ARCH_AT91 || AVR32 +- help +- Enables support for NAND Flash / Smart Media Card interface +- on Atmel AT91 and AVR32 processors. +- +-config MTD_NAND_PXA3xx +- tristate "NAND support on PXA3xx and Armada 370/XP" +- depends on PXA3xx || ARCH_MMP || PLAT_ORION +- help +- This enables the driver for the NAND flash device found on +- PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). +- +-config MTD_NAND_SLC_LPC32XX +- tristate "NXP LPC32xx SLC Controller" +- depends on ARCH_LPC32XX +- help +- Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell +- chips) NAND controller. This is the default for the PHYTEC 3250 +- reference board which contains a NAND256R3A2CZA6 chip. +- +- Please check the actual NAND chip connected and its support +- by the SLC NAND controller. +- +-config MTD_NAND_MLC_LPC32XX +- tristate "NXP LPC32xx MLC Controller" +- depends on ARCH_LPC32XX +- help +- Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND +- controller. This is the default for the WORK92105 controller +- board. +- +- Please check the actual NAND chip connected and its support +- by the MLC NAND controller. +- +-config MTD_NAND_CM_X270 +- tristate "Support for NAND Flash on CM-X270 modules" +- depends on MACH_ARMCORE +- +-config MTD_NAND_PASEMI +- tristate "NAND support for PA Semi PWRficient" +- depends on PPC_PASEMI +- help +- Enables support for NAND Flash interface on PA Semi PWRficient +- based boards +- +-config MTD_NAND_TMIO +- tristate "NAND Flash device on Toshiba Mobile IO Controller" +- depends on MFD_TMIO +- help +- Support for NAND flash connected to a Toshiba Mobile IO +- Controller in some PDAs, including the Sharp SL6000x. +- +-config MTD_NAND_NANDSIM +- tristate "Support for NAND Flash Simulator" +- help +- The simulator may simulate various NAND flash chips for the +- MTD nand layer. +- +-config MTD_NAND_GPMI_NAND +- tristate "GPMI NAND Flash Controller driver" +- depends on MTD_NAND && MXS_DMA +- help +- Enables NAND Flash support for IMX23, IMX28 or IMX6. +- The GPMI controller is very powerful, with the help of BCH +- module, it can do the hardware ECC. The GPMI supports several +- NAND flashs at the same time. The GPMI may conflicts with other +- block, such as SD card. So pay attention to it when you enable +- the GPMI. +- +-config MTD_NAND_BRCMNAND +- tristate "Broadcom STB NAND controller" +- depends on ARM || ARM64 || MIPS +- help +- Enables the Broadcom NAND controller driver. The controller was +- originally designed for Set-Top Box but is used on various BCM7xxx, +- BCM3xxx, BCM63xxx, iProc/Cygnus and more. +- +-config MTD_NAND_BCM47XXNFLASH +- tristate "Support for NAND flash on BCM4706 BCMA bus" +- depends on BCMA_NFLASH +- help +- BCMA bus can have various flash memories attached, they are +- registered by bcma as platform devices. This enables driver for +- NAND flash memories. For now only BCM4706 is supported. +- +-config MTD_NAND_PLATFORM +- tristate "Support for generic platform NAND driver" +- depends on HAS_IOMEM +- help +- This implements a generic NAND driver for on-SOC platform +- devices. You will need to provide platform-specific functions +- via platform_data. +- +-config MTD_NAND_ORION +- tristate "NAND Flash support for Marvell Orion SoC" +- depends on PLAT_ORION +- help +- This enables the NAND flash controller on Orion machines. +- +- No board specific support is done by this driver, each board +- must advertise a platform_device for the driver to attach. +- +-config MTD_NAND_FSL_ELBC +- tristate "NAND support for Freescale eLBC controllers" +- depends on FSL_SOC +- select FSL_LBC +- help +- Various Freescale chips, including the 8313, include a NAND Flash +- Controller Module with built-in hardware ECC capabilities. +- Enabling this option will enable you to use this to control +- external NAND devices. +- +-config MTD_NAND_FSL_IFC +- tristate "NAND support for Freescale IFC controller" +- depends on FSL_SOC || ARCH_LAYERSCAPE +- select FSL_IFC +- select MEMORY +- help +- Various Freescale chips e.g P1010, include a NAND Flash machine +- with built-in hardware ECC capabilities. +- Enabling this option will enable you to use this to control +- external NAND devices. +- +-config MTD_NAND_FSL_UPM +- tristate "Support for NAND on Freescale UPM" +- depends on PPC_83xx || PPC_85xx +- select FSL_LBC +- help +- Enables support for NAND Flash chips wired onto Freescale PowerPC +- processor localbus with User-Programmable Machine support. +- +-config MTD_NAND_MPC5121_NFC +- tristate "MPC5121 built-in NAND Flash Controller support" +- depends on PPC_MPC512x +- help +- This enables the driver for the NAND flash controller on the +- MPC5121 SoC. +- +-config MTD_NAND_VF610_NFC +- tristate "Support for Freescale NFC for VF610/MPC5125" +- depends on (SOC_VF610 || COMPILE_TEST) +- depends on HAS_IOMEM +- help +- Enables support for NAND Flash Controller on some Freescale +- processors like the VF610, MPC5125, MCF54418 or Kinetis K70. +- The driver supports a maximum 2k page size. With 2k pages and +- 64 bytes or more of OOB, hardware ECC with up to 32-bit error +- correction is supported. Hardware ECC is only enabled through +- device tree. +- +-config MTD_NAND_MXC +- tristate "MXC NAND support" +- depends on ARCH_MXC +- help +- This enables the driver for the NAND flash controller on the +- MXC processors. +- +-config MTD_NAND_SH_FLCTL +- tristate "Support for NAND on Renesas SuperH FLCTL" +- depends on SUPERH || COMPILE_TEST +- depends on HAS_IOMEM +- depends on HAS_DMA +- help +- Several Renesas SuperH CPU has FLCTL. This option enables support +- for NAND Flash using FLCTL. +- +-config MTD_NAND_DAVINCI +- tristate "Support NAND on DaVinci/Keystone SoC" +- depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) +- help +- Enable the driver for NAND flash chips on Texas Instruments +- DaVinci/Keystone processors. +- +-config MTD_NAND_TXX9NDFMC +- tristate "NAND Flash support for TXx9 SoC" +- depends on SOC_TX4938 || SOC_TX4939 +- help +- This enables the NAND flash controller on the TXx9 SoCs. +- +-config MTD_NAND_SOCRATES +- tristate "Support for NAND on Socrates board" +- depends on SOCRATES +- help +- Enables support for NAND Flash chips wired onto Socrates board. +- +-config MTD_NAND_NUC900 +- tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." +- depends on ARCH_W90X900 +- help +- This enables the driver for the NAND Flash on evaluation board based +- on w90p910 / NUC9xx. +- +-config MTD_NAND_JZ4740 +- tristate "Support for JZ4740 SoC NAND controller" +- depends on MACH_JZ4740 +- help +- Enables support for NAND Flash on JZ4740 SoC based boards. +- +-config MTD_NAND_JZ4780 +- tristate "Support for NAND on JZ4780 SoC" +- depends on MACH_JZ4780 && JZ4780_NEMC +- help +- Enables support for NAND Flash connected to the NEMC on JZ4780 SoC +- based boards, using the BCH controller for hardware error correction. +- +-config MTD_NAND_FSMC +- tristate "Support for NAND on ST Micros FSMC" +- depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 +- help +- Enables support for NAND Flash chips on the ST Microelectronics +- Flexible Static Memory Controller (FSMC) +- +-config MTD_NAND_XWAY +- bool "Support for NAND on Lantiq XWAY SoC" +- depends on LANTIQ && SOC_TYPE_XWAY +- help +- Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached +- to the External Bus Unit (EBU). +- +-config MTD_NAND_SUNXI +- tristate "Support for NAND on Allwinner SoCs" +- depends on ARCH_SUNXI +- help +- Enables support for NAND Flash chips on Allwinner SoCs. +- +-config MTD_NAND_HISI504 +- tristate "Support for NAND controller on Hisilicon SoC Hip04" +- depends on HAS_DMA +- help +- Enables support for NAND controller on Hisilicon SoC Hip04. +- +-config MTD_NAND_QCOM +- tristate "Support for NAND on QCOM SoCs" +- depends on ARCH_QCOM +- help +- Enables support for NAND flash chips on SoCs containing the EBI2 NAND +- controller. This controller is found on IPQ806x SoC. +- +-config MTD_NAND_MTK +- tristate "Support for NAND controller on MTK SoCs" +- depends on HAS_DMA +- help +- Enables support for NAND controller on MTK SoCs. +- This controller is found on mt27xx, mt81xx, mt65xx SoCs. +- +-endif # MTD_NAND ++source "drivers/mtd/nand/raw/Kconfig" +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index cafde6f..f168a5b 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -1,62 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 + # + # linux/drivers/nand/Makefile + # + +-obj-$(CONFIG_MTD_NAND) += nand.o +-obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o +-obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o +-obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +-obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o +- +-obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o +-obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o +-obj-$(CONFIG_MTD_NAND_DENALI) += denali.o +-obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o +-obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o +-obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o +-obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o +-obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o +-obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o +-obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +-obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o +-obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o +-obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +-obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o +-obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o +-obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o +-obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o +-obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o +-omap2_nand-objs := omap2.o +-obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o +-obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o +-obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o +-obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o +-obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o +-obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o +-obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o +-obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o +-obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o +-obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o +-obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o +-obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o +-obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o +-obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o +-obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o +-obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o +-obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o +-obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o +-obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o +-obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o +-obj-$(CONFIG_MTD_NAND_RICOH) += r852.o +-obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +-obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o +-obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +-obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o +-obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ +-obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o +-obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o +-obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ +-obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o +-obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o +- +-nand-objs := nand_base.o nand_bbt.o nand_timings.o ++obj-y += raw/ +diff --git a/drivers/mtd/nand/cavium/Kconfig b/drivers/mtd/nand/cavium/Kconfig +new file mode 100644 +index 00000000..d8ee594 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/Kconfig +@@ -0,0 +1,28 @@ ++# ++# Cavium OcteonTX Nand flash controller and BCH/copy engine ++# ++config MTD_NAND_CAVIUM ++ tristate "Support for NAND on Cavium Octeon TX SOCs" ++ depends on PCI && HAS_DMA && (ARM64 || COMPILE_TEST) ++ default MTD_NAND if ARCH_THUNDER ++ default N ++ help ++ Enables support for NAND Flash found some Cavium Octeon TX SOCs. ++ Only one of the possible 8 NAND chips is currently supported. ++ Either software ECC or the hardware BCH engine may be used. ++ ++ To compile this as a module, choose M here. ++ ++config CAVIUM_BCH ++ tristate "Cavium BCH driver" ++ depends on MTD_NAND_CAVIUM || COMPILE_TEST ++ depends on PCI_MSI && 64BIT ++ default MTD_NAND_CAVIUM ++ help ++ Optional support for Cavium BCH block found in OcteonTX ++ series of SoCs. ++ Currently used for hardware assist to MTD_NAND_CAVIUM, ++ potentially also usable as a general dma_engine. ++ ++ If being used with MTD_NAND_CAVIUM=y, choose Y here, ++ otherwise, to compile this as a module, choose M here. +diff --git a/drivers/mtd/nand/cavium/Makefile b/drivers/mtd/nand/cavium/Makefile +new file mode 100644 +index 00000000..0d5633f +--- /dev/null ++++ b/drivers/mtd/nand/cavium/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_MTD_NAND_CAVIUM) += cavium_nand.o ++obj-$(CONFIG_CAVIUM_BCH) += bch_pf.o bch_vf.o +diff --git a/drivers/mtd/nand/cavium/bch_common.h b/drivers/mtd/nand/cavium/bch_common.h +new file mode 100644 +index 00000000..ca97343 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/bch_common.h +@@ -0,0 +1,50 @@ ++#include ++/* ++ * Copyright (C) 2018 Cavium, Inc. ++ * ++ * 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. ++ */ ++ ++#ifndef __BCH_COMMON_H ++#define __BCH_COMMON_H ++ ++#include ++#include ++#include ++ ++#include "bch_regs.h" ++ ++/* Device ID */ ++#define BCH_PCI_PF_DEVICE_ID 0xa043 ++#define BCH_PCI_VF_DEVICE_ID 0xa044 ++ ++#define BCH_81XX_PCI_PF_SUBSYS_ID 0xa243 ++#define BCH_81XX_PCI_VF_SUBSYS_ID 0xa244 ++#define BCH_83XX_PCI_PF_SUBSYS_ID 0xa343 ++#define BCH_83XX_PCI_VF_SUBSYS_ID 0xa344 ++ ++/* flags to indicate the features supported */ ++#define BCH_FLAG_SRIOV_ENABLED BIT(1) ++ ++/* ++ * BCH Registers map for 81xx ++ */ ++ ++/* PF registers */ ++#define BCH_CTL 0x0ull ++#define BCH_ERR_CFG 0x10ull ++#define BCH_BIST_RESULT 0x80ull ++#define BCH_ERR_INT 0x88ull ++#define BCH_ERR_INT_W1S 0x90ull ++#define BCH_ERR_INT_ENA_W1C 0xA0ull ++#define BCH_ERR_INT_ENA_W1S 0xA8ull ++ ++/* VF registers */ ++#define BCH_VQX_CTL(z) 0x0ull ++#define BCH_VQX_CMD_BUF(z) 0x8ull ++#define BCH_VQX_CMD_PTR(z) 0x20ull ++#define BCH_VQX_DOORBELL(z) 0x800ull ++ ++#endif /* __BCH_COMMON_H */ +diff --git a/drivers/mtd/nand/cavium/bch_pf.c b/drivers/mtd/nand/cavium/bch_pf.c +new file mode 100644 +index 00000000..e7138a1 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/bch_pf.c +@@ -0,0 +1,322 @@ ++/* ++ * Copyright (C) 2018 Cavium, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License ++ * as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bch_pf.h" ++ ++#define DRV_NAME "thunder-bch" ++#define DRV_VERSION "1.0" ++ ++static bool no_vf; /* no auto-config of VFs, allow their use in guest kernel */ ++module_param(no_vf, bool, 0444); ++ ++DEFINE_MUTEX(octeontx_bch_devices_lock); ++LIST_HEAD(octeontx_bch_devices); ++ ++static unsigned int num_vfs = BCH_NR_VF; ++ ++static void bch_enable_interrupts(struct bch_device *bch) ++{ ++ writeq(~0ull, bch->reg_base + BCH_ERR_INT_ENA_W1S); ++} ++ ++static int do_bch_init(struct bch_device *bch) ++{ ++ int ret = 0; ++ ++ bch_enable_interrupts(bch); ++ ++ return ret; ++} ++ ++static irqreturn_t bch_intr_handler(int irq, void *bch_irq) ++{ ++ struct bch_device *bch = (struct bch_device *)bch_irq; ++ u64 ack = readq(bch->reg_base + BCH_ERR_INT); ++ ++ writeq(ack, bch->reg_base + BCH_ERR_INT); ++ return IRQ_HANDLED; ++} ++ ++static void bch_reset(struct bch_device *bch) ++{ ++ writeq(1, bch->reg_base + BCH_CTL); ++ mdelay(2); ++} ++ ++static void bch_disable(struct bch_device *bch) ++{ ++ writeq(~0ull, bch->reg_base + BCH_ERR_INT_ENA_W1C); ++ writeq(~0ull, bch->reg_base + BCH_ERR_INT); ++ bch_reset(bch); ++} ++ ++static u32 bch_check_bist_status(struct bch_device *bch) ++{ ++ return readq(bch->reg_base + BCH_BIST_RESULT); ++} ++ ++static int bch_device_init(struct bch_device *bch) ++{ ++ u64 bist; ++ u16 sdevid; ++ int rc; ++ struct device *dev = &bch->pdev->dev; ++ ++ /* Reset the PF when probed first */ ++ bch_reset(bch); ++ ++ pci_read_config_word(bch->pdev, PCI_SUBSYSTEM_ID, &sdevid); ++ ++ /*Check BIST status*/ ++ bist = (u64)bch_check_bist_status(bch); ++ if (bist) { ++ dev_err(dev, "BCH BIST failed with code 0x%llx", bist); ++ return -ENODEV; ++ } ++ ++ /* Get max VQs/VFs supported by the device */ ++ bch->max_vfs = pci_sriov_get_totalvfs(bch->pdev); ++ if (num_vfs > bch->max_vfs) { ++ dev_warn(dev, "Num of VFs to enable %d is greater than max available. Enabling %d VFs.\n", ++ num_vfs, bch->max_vfs); ++ num_vfs = bch->max_vfs; ++ } ++ /* Get number of VQs/VFs to be enabled */ ++ bch->vfs_enabled = num_vfs; ++ ++ /*TODO: Get CLK frequency*/ ++ /*Reset device parameters*/ ++ rc = do_bch_init(bch); ++ ++ return rc; ++} ++ ++static int bch_register_interrupts(struct bch_device *bch) ++{ ++ int ret; ++ struct device *dev = &bch->pdev->dev; ++ u32 num_vec = BCH_MSIX_VECTORS; ++ ++ /* Enable MSI-X */ ++ ret = pci_alloc_irq_vectors(bch->pdev, num_vec, num_vec, PCI_IRQ_MSIX); ++ if (ret < 0) { ++ dev_err(&bch->pdev->dev, "Request for #%d msix vectors failed\n", ++ num_vec); ++ return ret; ++ } ++ ++ /* Register error interrupt handlers */ ++ ret = request_irq(pci_irq_vector(bch->pdev, 0), ++ bch_intr_handler, 0, "BCH", bch); ++ if (ret) ++ goto fail; ++ ++ /* Enable error interrupt */ ++ bch_enable_interrupts(bch); ++ return 0; ++ ++fail: ++ dev_err(dev, "Request irq failed\n"); ++ pci_disable_msix(bch->pdev); ++ return ret; ++} ++ ++static void bch_unregister_interrupts(struct bch_device *bch) ++{ ++ free_irq(pci_irq_vector(bch->pdev, 0), bch); ++ pci_disable_msix(bch->pdev); ++} ++ ++ ++static int bch_sriov_configure(struct pci_dev *pdev, int numvfs) ++{ ++ struct bch_device *bch = pci_get_drvdata(pdev); ++ int tmp, ret = -EBUSY, disable = 0; ++ ++ mutex_lock(&octeontx_bch_devices_lock); ++ if (bch->vfs_in_use) ++ goto exit; ++ ++ ret = 0; ++ tmp = bch->vfs_enabled; ++ if (bch->flags & BCH_FLAG_SRIOV_ENABLED) ++ disable = 1; ++ ++ if (disable) { ++ pci_disable_sriov(pdev); ++ bch->flags &= ~BCH_FLAG_SRIOV_ENABLED; ++ bch->vfs_enabled = 0; ++ } ++ ++ if (numvfs > 0) { ++ bch->vfs_enabled = numvfs; ++ ret = pci_enable_sriov(pdev, numvfs); ++ if (ret == 0) { ++ bch->flags |= BCH_FLAG_SRIOV_ENABLED; ++ ret = numvfs; ++ } else { ++ bch->vfs_enabled = tmp; ++ } ++ } ++ ++ dev_notice(&bch->pdev->dev, "VFs enabled: %d\n", ret); ++exit: ++ mutex_unlock(&octeontx_bch_devices_lock); ++ return ret; ++} ++ ++static void *token = (void *)(-EPROBE_DEFER); ++ ++static int bch_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ struct device *dev = &pdev->dev; ++ struct bch_device *bch; ++ int err; ++ ++ /* unsafe on CN81XX pass 1.0/1.1 */ ++ if (MIDR_IS_CPU_MODEL_RANGE(read_cpuid_id(), ++ MIDR_OCTEON_T81, 0x00, 0x01)) ++ return -ENODEV; ++ ++ bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL); ++ if (!bch) ++ return -ENOMEM; ++ ++ pci_set_drvdata(pdev, bch); ++ bch->pdev = pdev; ++ err = pci_enable_device(pdev); ++ if (err) { ++ dev_err(dev, "Failed to enable PCI device\n"); ++ pci_set_drvdata(pdev, NULL); ++ return err; ++ } ++ ++ err = pci_request_regions(pdev, DRV_NAME); ++ if (err) { ++ dev_err(dev, "PCI request regions failed 0x%x\n", err); ++ goto bch_err_disable_device; ++ } ++ ++ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); ++ if (err) { ++ dev_err(dev, "Unable to get usable DMA configuration\n"); ++ goto bch_err_release_regions; ++ } ++ ++ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); ++ if (err) { ++ dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); ++ goto bch_err_release_regions; ++ } ++ ++ /* MAP PF's configuration registers */ ++ bch->reg_base = pcim_iomap(pdev, 0, 0); ++ if (!bch->reg_base) { ++ dev_err(dev, "Cannot map config register space, aborting\n"); ++ err = -ENOMEM; ++ goto bch_err_release_regions; ++ } ++ ++ bch_device_init(bch); ++ ++ /* Register interrupts */ ++ err = bch_register_interrupts(bch); ++ if (err) ++ goto bch_err_release_regions; ++ ++ INIT_LIST_HEAD(&bch->list); ++ mutex_lock(&octeontx_bch_devices_lock); ++ list_add(&bch->list, &octeontx_bch_devices); ++ token = (void *)pdev; ++ mutex_unlock(&octeontx_bch_devices_lock); ++ ++ if (!no_vf) ++ bch_sriov_configure(pdev, num_vfs); ++ ++ return 0; ++ ++bch_err_release_regions: ++ pci_release_regions(pdev); ++bch_err_disable_device: ++ pci_disable_device(pdev); ++ pci_set_drvdata(pdev, NULL); ++ return err; ++} ++ ++static void bch_remove(struct pci_dev *pdev) ++{ ++ struct bch_device *bch = pci_get_drvdata(pdev); ++ struct bch_device *curr; ++ ++ if (!bch) ++ return; ++ ++ mutex_lock(&octeontx_bch_devices_lock); ++ token = ERR_PTR(-EPROBE_DEFER); ++ bch_disable(bch); ++ list_for_each_entry(curr, &octeontx_bch_devices, list) { ++ if (curr == bch) { ++ list_del(&bch->list); ++ break; ++ } ++ } ++ mutex_unlock(&octeontx_bch_devices_lock); ++ ++ pci_disable_sriov(pdev); ++ bch_unregister_interrupts(bch); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++ pci_set_drvdata(pdev, NULL); ++} ++ ++ ++/* get/put async wrt probe, from cavium_nand or copy client */ ++void *cavm_bch_getp(void) ++{ ++ try_module_get(THIS_MODULE); ++ return token; ++} ++EXPORT_SYMBOL(cavm_bch_getp); ++ ++void cavm_bch_putp(void *token) ++{ ++ if (token) ++ module_put(THIS_MODULE); ++} ++EXPORT_SYMBOL(cavm_bch_putp); ++ ++/* Supported devices */ ++static const struct pci_device_id bch_id_table[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, BCH_PCI_PF_DEVICE_ID) }, ++ { 0, } /* end of table */ ++}; ++ ++static struct pci_driver bch_pci_driver = { ++ .name = DRV_NAME, ++ .id_table = bch_id_table, ++ .probe = bch_probe, ++ .remove = bch_remove, ++ .sriov_configure = bch_sriov_configure ++}; ++ ++module_pci_driver(bch_pci_driver); ++ ++MODULE_AUTHOR("Cavium Inc"); ++MODULE_DESCRIPTION("Cavium Thunder BCH Physical Function Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION(DRV_VERSION); ++MODULE_DEVICE_TABLE(pci, bch_id_table); +diff --git a/drivers/mtd/nand/cavium/bch_pf.h b/drivers/mtd/nand/cavium/bch_pf.h +new file mode 100644 +index 00000000..e8ab403 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/bch_pf.h +@@ -0,0 +1,29 @@ ++/* ++ * Copyright (C) 2018 Cavium, Inc. ++ * ++ * 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. ++ */ ++ ++#ifndef __BCHPF_H ++#define __BCHPF_H ++ ++#include "bch_common.h" ++ ++#define BCH_MSIX_VECTORS 1 ++ ++struct bch_device; ++ ++struct bch_device { ++ struct list_head list; ++ u8 max_vfs; /* Maximum Virtual Functions supported */ ++ u8 vfs_enabled; /* Number of enabled VFs */ ++ u8 vfs_in_use; /* Number of VFs in use */ ++ u32 flags; /* Flags to hold device status bits */ ++ ++ void __iomem *reg_base; /* Register start address */ ++ struct pci_dev *pdev; /* pci device handle */ ++}; ++ ++#endif /* __BCHPF_H */ +diff --git a/drivers/mtd/nand/cavium/bch_regs.h b/drivers/mtd/nand/cavium/bch_regs.h +new file mode 100644 +index 00000000..8641931 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/bch_regs.h +@@ -0,0 +1,222 @@ ++/* ++ * Copyright (C) 2018 Cavium, Inc. ++ * ++ * 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. ++ */ ++ ++#ifndef __BCH_REGS_H ++#define __BCH_REGS_H ++ ++#define BCH_NR_VF 1 ++ ++union bch_cmd { ++ u64 u[4]; ++ struct fields { ++ struct { ++#if defined(__BIG_ENDIAN_BITFIELD) ++ u64 ecc_gen:2; ++ u64 reserved_36_61:26; ++ u64 ecc_level:4; ++ u64 reserved_12_31:20; ++ u64 size:12; ++#else ++ u64 size:12; ++ u64 reserved_12_31:20; ++ u64 ecc_level:4; ++ u64 reserved_36_61:26; ++ u64 ecc_gen:2; ++#endif ++ } cword; ++ struct { ++#if defined(__BIG_ENDIAN_BITFIELD) ++ u64 reserved_58_63:6; ++ u64 fw:1; ++ u64 nc:1; ++ u64 reserved_49_55:7; ++ u64 ptr:49; ++#else ++ u64 ptr:49; ++ u64 reserved_49_55:7; ++ u64 nc:1; ++ u64 fw:1; ++ u64 reserved_58_63:6; ++#endif ++ } oword; ++ struct { ++#if defined(__BIG_ENDIAN_BITFIELD) ++ u64 reserved_57_63:7; ++ u64 nc:1; ++ u64 reserved_49_55:7; ++ u64 ptr:49; ++#else ++ u64 ptr:49; ++ u64 reserved_49_55:7; ++ u64 nc:1; ++ u64 reserved_57_63:7; ++#endif ++ } iword; ++ struct { ++#if defined(__BIG_ENDIAN_BITFIELD) ++ u64 reserved_49_63:15; ++ u64 ptr:49; ++#else ++ u64 ptr:49; ++ u64 reserved_49_63:15; ++#endif ++ } rword; ++ } s; ++}; ++ ++enum ecc_gen { ++ eg_correct, ++ eg_copy, ++ eg_gen, ++ eg_copy3, ++}; ++ ++/** Response from BCH instruction */ ++union bch_resp { ++ uint16_t u16; ++ struct { ++#ifdef __BIG_ENDIAN_BITFIELD ++ uint16_t done:1; /** Block is done */ ++ uint16_t uncorrectable:1;/** too many bits flipped */ ++ uint16_t erased:1; /** Block is erased */ ++ uint16_t zero:6; /** Always zero, ignore */ ++ uint16_t num_errors:7; /** Number of errors in block */ ++#else ++ uint16_t num_errors:7; /** Number of errors in block */ ++ uint16_t zero:6; /** Always zero, ignore */ ++ uint16_t erased:1; /** Block is erased */ ++ uint16_t uncorrectable:1;/** too many bits flipped */ ++ uint16_t done:1; /** Block is done */ ++#endif ++ } s; ++}; ++ ++union bch_vqx_ctl { ++ u64 u; ++ struct { ++#if defined(__BIG_ENDIAN_BITFIELD) ++ u64 reserved_22_63:42; ++ u64 early_term:4; ++ u64 one_cmd:1; ++ u64 erase_disable:1; ++ u64 reserved_6_15:10; ++ u64 max_read:4; ++ u64 cmd_be:1; ++ u64 reserved_0:1; ++#else /* Little Endian */ ++ u64 reserved_0:1; ++ u64 cmd_be:1; ++ u64 max_read:4; ++ u64 reserved_6_15:10; ++ u64 erase_disable:1; ++ u64 one_cmd:1; ++ u64 early_term:4; ++ u64 reserved_22_63:42; ++#endif ++ } s; ++}; ++ ++union bch_vqx_cmd_buf { ++ u64 u; ++ struct { ++#if defined(__BIG_ENDIAN_BITFIELD) ++ u64 reserved_48_63:16; ++ u64 ldwb:1; ++ u64 dfb:1; ++ u64 size:13; ++ u64 reserved_0_32:33; ++#else /* Little Endian */ ++ u64 reserved_0_32:33; ++ u64 size:13; ++ u64 dfb:1; ++ u64 ldwb:1; ++ u64 reserved_48_63:16; ++#endif ++ } s; ++}; ++ ++/* keep queue state indexed, even though just one supported here, ++ * for later generalization to similarly-shaped queues on other Cavium devices ++ */ ++enum {QID_BCH, QID_MAX}; ++struct bch_q { ++ struct device *dev; ++ int index; ++ u16 max_depth; ++ u16 pool_size_m1; ++ u64 *base_vaddr; ++ dma_addr_t base_paddr; ++}; ++extern struct bch_q cavium_bch_q[QID_MAX]; ++ ++/* with one dma-mapped area, virt<->phys conversions by +/- (vaddr-paddr) */ ++static inline dma_addr_t qphys(int qid, void *v) ++{ ++ struct bch_q *q = &cavium_bch_q[qid]; ++ int off = (u8 *)v - (u8 *)q->base_vaddr; ++ ++ return q->base_paddr + off; ++} ++#define cavm_ptr_to_phys(v) qphys(QID_BCH, (v)) ++ ++static inline void *qvirt(int qid, dma_addr_t p) ++{ ++ struct bch_q *q = &cavium_bch_q[qid]; ++ int off = p - q->base_paddr; ++ ++ return q->base_vaddr + off; ++} ++#define cavm_phys_to_ptr(p) qvirt(QID_BCH, (p)) ++ ++/* plenty for interleaved r/w on two planes with 16k page, ecc_size 1k */ ++/* QDEPTH >= 16, as successive chunks must align on 128-byte boundaries */ ++#define QDEPTH 256 /* u64s in a command queue chunk, incl next-pointer */ ++#define NQS 1 /* linked chunks in the chain */ ++ ++int cavm_cmd_queue_initialize(struct device *dev, ++ int queue_id, int max_depth, int fpa_pool, int pool_size); ++int cavm_cmd_queue_shutdown(int queue_id); ++ ++/** ++ * Write an arbitrary number of command words to a command queue. ++ * This is a generic function; the fixed number of command word ++ * functions yield higher performance. ++ * ++ * Could merge with crypto version for FPA use on cn83xx ++ */ ++static inline int cavm_cmd_queue_write(int queue_id, ++ bool use_locking, int cmd_count, const uint64_t *cmds) ++{ ++ int ret = 0; ++ uint64_t *cmd_ptr; ++ struct bch_q *qptr = &cavium_bch_q[queue_id]; ++ ++ if (unlikely((cmd_count < 1) || (cmd_count > 32))) ++ return -EINVAL; ++ if (unlikely(cmds == NULL)) ++ return -EINVAL; ++ ++ cmd_ptr = qptr->base_vaddr; ++ ++ while (cmd_count > 0) { ++ int slot = qptr->index % (QDEPTH * NQS); ++ ++ if (slot % QDEPTH != QDEPTH - 1) { ++ cmd_ptr[slot] = *cmds++; ++ cmd_count--; ++ } ++ ++ qptr->index++; ++ } ++ ++ wmb(); /* flush commands before ringing bell */ ++ ++ return ret; ++} ++ ++#endif /*__BCH_REGS_H*/ +diff --git a/drivers/mtd/nand/cavium/bch_vf.c b/drivers/mtd/nand/cavium/bch_vf.c +new file mode 100644 +index 00000000..e4798b7 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/bch_vf.c +@@ -0,0 +1,316 @@ ++/* ++ * Copyright (C) 2018 Cavium, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License ++ * as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++#include "bch_vf.h" ++#include "bch_pf.h" ++ ++#define DRV_NAME "thunder-bch_vf" ++#define DRV_VERSION "1.0" ++ ++/* ++ * handles passed between client->VF->PF ++ * bch_vf is held by cavium_nand, or a possible dmaengine client ++ * bch_bp is ref to BF driver, whether VF sees it at this security level or not ++ */ ++static void *bch_pf = (void *)(-EPROBE_DEFER); ++#ifdef DEBUG ++static int waits[3]; /*visible wait-loop count*/ ++module_param_array(waits, int, NULL, 0444); ++#define WAIT_COUNT(n) (void)(waits[n]++) ++#else ++static struct bch_vf *bch_vf = (void *)(-EPROBE_DEFER); ++#define WAIT_COUNT(n) (void)0 ++#endif ++ ++/** ++ * Given a data block calculate the ecc data and fill in the response ++ * ++ * @param[in] block 8-byte aligned pointer to data block to calculate ECC ++ * @param block_size Size of block in bytes, must be a multiple of two. ++ * @param bch_level Number of errors that must be corrected. The number of ++ * parity bytes is equal to ((15 * bch_level) + 7) / 8. ++ * Must be 4, 8, 16, 24, 32, 40, 48, 56, 60 or 64. ++ * @param[out] ecc 8-byte aligned pointer to where ecc data should go ++ * @param[in] resp pointer to where responses will be written. ++ * ++ * @return Zero on success, negative on failure. ++ */ ++int cavm_bch_encode(struct bch_vf *vf, dma_addr_t block, uint16_t block_size, ++ uint8_t bch_level, dma_addr_t ecc, dma_addr_t resp) ++{ ++ union bch_cmd cmd; ++ int rc; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.s.cword.ecc_gen = eg_gen; ++ cmd.s.cword.ecc_level = bch_level; ++ cmd.s.cword.size = block_size; ++ ++ cmd.s.oword.ptr = ecc; ++ cmd.s.iword.ptr = block; ++ cmd.s.rword.ptr = resp; ++ rc = cavm_cmd_queue_write(QID_BCH, 1, ++ sizeof(cmd) / sizeof(uint64_t), cmd.u); ++ if (rc) ++ return -1; ++ ++ cavm_bch_write_doorbell(1, vf); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cavm_bch_encode); ++ ++/** ++ * Given a data block and ecc data correct the data block ++ * ++ * @param[in] block_ecc_in 8-byte aligned pointer to data block with ECC ++ * data concatenated to the end to correct ++ * @param block_size Size of block in bytes, must be a multiple of ++ * two. ++ * @param bch_level Number of errors that must be corrected. The ++ * number of parity bytes is equal to ++ * ((15 * bch_level) + 7) / 8. ++ * Must be 4, 8, 16, 24, 32, 40, 48, 56, 60 or 64. ++ * @param[out] block_out 8-byte aligned pointer to corrected data buffer. ++ * This should not be the same as block_ecc_in. ++ * @param[in] resp pointer to where responses will be written. ++ * ++ * @return Zero on success, negative on failure. ++ */ ++ ++int cavm_bch_decode(struct bch_vf *vf, dma_addr_t block_ecc_in, ++ uint16_t block_size, uint8_t bch_level, ++ dma_addr_t block_out, dma_addr_t resp) ++{ ++ union bch_cmd cmd; ++ int rc; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.s.cword.ecc_gen = eg_correct; ++ cmd.s.cword.ecc_level = bch_level; ++ cmd.s.cword.size = block_size; ++ ++ cmd.s.oword.ptr = block_out; ++ cmd.s.iword.ptr = block_ecc_in; ++ cmd.s.rword.ptr = resp; ++ rc = cavm_cmd_queue_write(QID_BCH, 1, ++ sizeof(cmd) / sizeof(uint64_t), cmd.u); ++ if (rc) ++ return -1; ++ ++ cavm_bch_write_doorbell(1, vf); ++ return 0; ++} ++EXPORT_SYMBOL(cavm_bch_decode); ++ ++int cavm_bch_wait(struct bch_vf *vf, union bch_resp *resp, dma_addr_t handle) ++{ ++ int max = 1000; ++ ++ rmb(); /* HW is updating *resp */ ++ WAIT_COUNT(0); ++ while (!resp->s.done && max-- >= 0) { ++ WAIT_COUNT(1); ++ usleep_range(10, 20); ++ rmb(); /* HW is updating *resp */ ++ } ++ if (resp->s.done) ++ return 0; ++ WAIT_COUNT(2); ++ return -ETIMEDOUT; ++} ++EXPORT_SYMBOL(cavm_bch_wait); ++ ++struct bch_q cavium_bch_q[QID_MAX]; ++EXPORT_SYMBOL(cavium_bch_q); ++ ++int cavm_cmd_queue_initialize(struct device *dev, ++ int queue_id, int max_depth, int fpa_pool, int pool_size) ++{ ++ /* some params are for later merge with CPT or cn83xx */ ++ struct bch_q *q = &cavium_bch_q[queue_id]; ++ union bch_cmd *qb; ++ int chunk = max_depth + 1; ++ int i, size; ++ ++ if ((unsigned int)queue_id >= QID_MAX) ++ return -EINVAL; ++ if (max_depth & chunk) /* must be 2^N - 1 */ ++ return -EINVAL; ++ ++ size = NQS * chunk * sizeof(u64); ++ qb = dma_alloc_coherent(dev, size, ++ &q->base_paddr, GFP_KERNEL | GFP_DMA); ++ ++ if (!qb) ++ return -ENOMEM; ++ ++ q->dev = dev; ++ q->index = 0; ++ q->max_depth = max_depth; ++ q->pool_size_m1 = pool_size; ++ q->base_vaddr = (u64 *)qb; ++ ++ for (i = 0; i < NQS; i++) { ++ int inext = (i + 1) * chunk - 1; ++ u64 *ixp = &qb->u[inext]; ++ int j = (i + 1) % NQS; ++ int jnext = j * chunk; ++ dma_addr_t jbase = q->base_paddr + jnext * sizeof(u64); ++ *ixp = jbase; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(cavm_cmd_queue_initialize); ++ ++int cavm_cmd_queue_shutdown(int queue_id) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(cavm_cmd_queue_shutdown); ++ ++static int bchvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ struct device *dev = &pdev->dev; ++ struct bch_vf *vf; ++ union bch_vqx_ctl ctl; ++ union bch_vqx_cmd_buf cbuf; ++ int err; ++ ++ vf = devm_kzalloc(dev, sizeof(*vf), GFP_KERNEL); ++ if (!vf) ++ return -ENOMEM; ++ ++ pci_set_drvdata(pdev, vf); ++ vf->pdev = pdev; ++ err = pci_enable_device(pdev); ++ if (err) { ++ dev_err(dev, "Failed to enable PCI device\n"); ++ pci_set_drvdata(pdev, NULL); ++ return err; ++ } ++ ++ err = pci_request_regions(pdev, DRV_NAME); ++ if (err) { ++ dev_err(dev, "PCI request regions failed 0x%x\n", err); ++ goto bchvf_err_disable_device; ++ } ++ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); ++ if (err) { ++ dev_err(dev, "Unable to get usable DMA configuration\n"); ++ goto release; ++ } ++ ++ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); ++ if (err) { ++ dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); ++ goto release; ++ } ++ ++ /* MAP PF's configuration registers */ ++ vf->reg_base = pcim_iomap(pdev, 0, 0); ++ if (!vf->reg_base) { ++ dev_err(dev, "Cannot map config register space, aborting\n"); ++ err = -ENOMEM; ++ goto release; ++ } ++ ++ err = cavm_cmd_queue_initialize(dev, QID_BCH, QDEPTH - 1, 0, ++ sizeof(union bch_cmd) * QDEPTH); ++ if (err) { ++ dev_err(dev, "cavm_cmd_queue_initialize() failed"); ++ goto release; ++ } ++ ++ ctl.u = readq(vf->reg_base + BCH_VQX_CTL(0)); ++ ++ cbuf.u = 0; ++ cbuf.s.ldwb = 1; ++ cbuf.s.dfb = 1; ++ cbuf.s.size = QDEPTH; ++ writeq(cbuf.u, vf->reg_base + BCH_VQX_CMD_BUF(0)); ++ ++ writeq(ctl.u, vf->reg_base + BCH_VQX_CTL(0)); ++ ++ writeq(cavium_bch_q[QID_BCH].base_paddr, ++ vf->reg_base + BCH_VQX_CMD_PTR(0)); ++ ++ /* publish to _get/_put */ ++ bch_vf = vf; ++ ++ return 0; ++ ++release: ++ pci_release_regions(pdev); ++bchvf_err_disable_device: ++ pci_disable_device(pdev); ++ pci_set_drvdata(pdev, NULL); ++ ++ return err; ++} ++ ++/* get/put async wrt probe, from VF */ ++void *cavm_bch_getv(void) ++{ ++ if (!bch_vf) ++ return NULL; ++ bch_pf = cavm_bch_getp(); ++ if (IS_ERR_OR_NULL(bch_pf)) ++ return bch_pf; ++ try_module_get(THIS_MODULE); ++ return bch_vf; ++} ++EXPORT_SYMBOL(cavm_bch_getv); ++ ++void cavm_bch_putv(void *token) ++{ ++ if (!IS_ERR_OR_NULL(token)) { ++ module_put(THIS_MODULE); ++ cavm_bch_putp(bch_pf); ++ } ++} ++EXPORT_SYMBOL(cavm_bch_putv); ++ ++static void bchvf_remove(struct pci_dev *pdev) ++{ ++ struct bch_vf *vf = pci_get_drvdata(pdev); ++ ++ if (!vf) { ++ dev_err(&pdev->dev, "Invalid BCH-VF device\n"); ++ return; ++ } ++ ++ pci_set_drvdata(pdev, NULL); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++} ++ ++/* Supported devices */ ++static const struct pci_device_id bchvf_id_table[] = { ++ {PCI_VDEVICE(CAVIUM, BCH_PCI_VF_DEVICE_ID), 0}, ++ { 0, } /* end of table */ ++}; ++ ++static struct pci_driver bchvf_pci_driver = { ++ .name = DRV_NAME, ++ .id_table = bchvf_id_table, ++ .probe = bchvf_probe, ++ .remove = bchvf_remove, ++}; ++ ++module_pci_driver(bchvf_pci_driver); ++ ++MODULE_AUTHOR("Cavium Inc"); ++MODULE_DESCRIPTION("Cavium Thunder BCH Virtual Function Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION(DRV_VERSION); ++MODULE_DEVICE_TABLE(pci, bchvf_id_table); +diff --git a/drivers/mtd/nand/cavium/bch_vf.h b/drivers/mtd/nand/cavium/bch_vf.h +new file mode 100644 +index 00000000..4496d931 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/bch_vf.h +@@ -0,0 +1,65 @@ ++/* ++ * Copyright (C) 2018 Cavium, Inc. ++ * ++ * 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. ++ */ ++ ++#ifndef __BCH_VF_H ++#define __BCH_VF_H ++ ++#include "bch_common.h" ++ ++struct bch_vf { ++ u16 flags; /* Flags to hold device status bits */ ++ u8 vfid; /* which VF (0.. _MAX - 1) */ ++ u8 node; /* Operating node: Bits (46:44) in BAR0 address */ ++ u8 priority; /* VF priority ring: 1-High proirity round ++ * robin ring;0-Low priority round robin ring; ++ * not for BCH ++ */ ++ struct pci_dev *pdev; /* pci device handle */ ++ void __iomem *reg_base; /* Register start address */ ++}; ++ ++struct buf_ptr { ++ u8 *vptr; ++ dma_addr_t dma_addr; ++ u16 size; ++}; ++ ++extern void *cavm_bch_getp(void); ++extern void cavm_bch_putp(void *pf); ++extern void *cavm_bch_getv(void); ++extern void cavm_bch_putv(void *vf); ++ ++extern int cavm_bch_encode(struct bch_vf *vf, ++ dma_addr_t block, uint16_t block_size, ++ uint8_t ecc_level, dma_addr_t ecc, ++ dma_addr_t resp); ++extern int cavm_bch_decode(struct bch_vf *vf, ++ dma_addr_t block_ecc_in, uint16_t block_size, ++ uint8_t ecc_level, dma_addr_t block_out, ++ dma_addr_t resp); ++ ++ ++extern int cavm_bch_wait(struct bch_vf *vf, union bch_resp *resp, ++ dma_addr_t bch_rhandle); ++ ++/** ++ * Ring the BCH doorbell telling it that new commands are ++ * available. ++ * ++ * @param num_commands Number of new commands ++ * @param vf virtual function handle ++ */ ++static inline void cavm_bch_write_doorbell(uint64_t num_commands, ++ struct bch_vf *vf) ++{ ++ uint64_t num_words = ++ num_commands * sizeof(union bch_cmd) / sizeof(uint64_t); ++ writeq(num_words, vf->reg_base + BCH_VQX_DOORBELL(0)); ++} ++ ++#endif /* __BCH_VF_H */ +diff --git a/drivers/mtd/nand/cavium/cavium_nand.c b/drivers/mtd/nand/cavium/cavium_nand.c +new file mode 100644 +index 00000000..4a329f0 +--- /dev/null ++++ b/drivers/mtd/nand/cavium/cavium_nand.c +@@ -0,0 +1,2108 @@ ++/* ++ * Cavium cn8xxx NAND flash controller (NDF) driver. ++ * ++ * Copyright (C) 2018 Cavium Inc. ++ * Authors: Jan Glauber ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bch_vf.h" ++ ++/* ++ * The NDF_CMD queue takes commands between 16 - 128 bit. ++ * All commands must be 16 bit aligned and are little endian. ++ * WAIT_STATUS commands must be 64 bit aligned. ++ * Commands are selected by the 4 bit opcode. ++ * ++ * Available Commands: ++ * ++ * 16 Bit: ++ * NOP ++ * WAIT ++ * BUS_ACQ, BUS_REL ++ * CHIP_EN, CHIP_DIS ++ * ++ * 32 Bit: ++ * CLE_CMD ++ * RD_CMD, RD_EDO_CMD ++ * WR_CMD ++ * ++ * 64 Bit: ++ * SET_TM_PAR ++ * ++ * 96 Bit: ++ * ALE_CMD ++ * ++ * 128 Bit: ++ * WAIT_STATUS, WAIT_STATUS_ALE ++ */ ++ ++/* NDF Register offsets */ ++#define NDF_CMD 0x0 ++#define NDF_MISC 0x8 ++#define NDF_ECC_CNT 0x10 ++#define NDF_DRBELL 0x30 ++#define NDF_ST_REG 0x38 /* status */ ++#define NDF_INT 0x40 ++#define NDF_INT_W1S 0x48 ++#define NDF_DMA_CFG 0x50 ++#define NDF_DMA_ADR 0x58 ++#define NDF_INT_ENA_W1C 0x60 ++#define NDF_INT_ENA_W1S 0x68 ++ ++/* NDF command opcodes */ ++#define NDF_OP_NOP 0x0 ++#define NDF_OP_SET_TM_PAR 0x1 ++#define NDF_OP_WAIT 0x2 ++#define NDF_OP_CHIP_EN_DIS 0x3 ++#define NDF_OP_CLE_CMD 0x4 ++#define NDF_OP_ALE_CMD 0x5 ++#define NDF_OP_WR_CMD 0x8 ++#define NDF_OP_RD_CMD 0x9 ++#define NDF_OP_RD_EDO_CMD 0xa ++#define NDF_OP_WAIT_STATUS 0xb /* same opcode for WAIT_STATUS_ALE */ ++#define NDF_OP_BUS_ACQ_REL 0xf ++ ++#define NDF_BUS_ACQUIRE 1 ++#define NDF_BUS_RELEASE 0 ++ ++struct ndf_nop_cmd { ++ u16 opcode : 4; ++ u16 nop : 12; ++}; ++ ++struct ndf_wait_cmd { ++ u16 opcode : 4; ++ u16 r_b : 1; /* wait for one cycle or PBUS_WAIT deassert */ ++ u16 : 3; ++ u16 wlen : 3; /* timing parameter select */ ++ u16 : 5; ++}; ++ ++struct ndf_bus_cmd { ++ u16 opcode : 4; ++ u16 direction : 4; /* 1 = acquire, 0 = release */ ++ u16 : 8; ++}; ++ ++struct ndf_chip_cmd { ++ u16 opcode : 4; ++ u16 chip : 3; /* select chip, 0 = disable */ ++ u16 enable : 1; /* 1 = enable, 0 = disable */ ++ u16 bus_width : 2; /* 10 = 16 bit, 01 = 8 bit */ ++ u16 : 6; ++}; ++ ++struct ndf_cle_cmd { ++ u32 opcode : 4; ++ u32 : 4; ++ u32 cmd_data : 8; /* command sent to the PBUS AD pins */ ++ u32 clen1 : 3; /* time between PBUS CLE and WE asserts */ ++ u32 clen2 : 3; /* time WE remains asserted */ ++ u32 clen3 : 3; /* time between WE deassert and CLE */ ++ u32 : 7; ++}; ++ ++/* RD_EDO_CMD uses the same layout as RD_CMD */ ++struct ndf_rd_cmd { ++ u32 opcode : 4; ++ u32 data : 16; /* data bytes */ ++ u32 rlen1 : 3; ++ u32 rlen2 : 3; ++ u32 rlen3 : 3; ++ u32 rlen4 : 3; ++}; ++ ++struct ndf_wr_cmd { ++ u32 opcode : 4; ++ u32 data : 16; /* data bytes */ ++ u32 : 4; ++ u32 wlen1 : 3; ++ u32 wlen2 : 3; ++ u32 : 3; ++}; ++ ++struct ndf_set_tm_par_cmd { ++ u64 opcode : 4; ++ u64 tim_mult : 4; /* multiplier for the seven paramters */ ++ u64 tm_par1 : 8; /* --> Following are the 7 timing parameters that */ ++ u64 tm_par2 : 8; /* specify the number of coprocessor cycles. */ ++ u64 tm_par3 : 8; /* A value of zero means one cycle. */ ++ u64 tm_par4 : 8; /* All values are scaled by tim_mult */ ++ u64 tm_par5 : 8; /* using tim_par * (2 ^ tim_mult). */ ++ u64 tm_par6 : 8; ++ u64 tm_par7 : 8; ++}; ++ ++struct ndf_ale_cmd { ++ u32 opcode : 4; ++ u32 : 4; ++ u32 adr_byte_num: 4; /* number of address bytes to be sent */ ++ u32 : 4; ++ u32 alen1 : 3; ++ u32 alen2 : 3; ++ u32 alen3 : 3; ++ u32 alen4 : 3; ++ u32 : 4; ++ u8 adr_byt1; ++ u8 adr_byt2; ++ u8 adr_byt3; ++ u8 adr_byt4; ++ u8 adr_byt5; ++ u8 adr_byt6; ++ u8 adr_byt7; ++ u8 adr_byt8; ++}; ++ ++struct ndf_wait_status_cmd { ++ u32 opcode : 4; ++ u32 : 4; ++ u32 data : 8; /* data */ ++ u32 clen1 : 3; ++ u32 clen2 : 3; ++ u32 clen3 : 3; ++ u32 : 8; ++ u32 ale_ind : 8; /* set to 5 to select WAIT_STATUS_ALE command */ ++ u32 adr_byte_num: 4; /* ALE only: number of address bytes to be sent */ ++ u32 : 4; ++ u32 alen1 : 3; /* ALE only */ ++ u32 alen2 : 3; /* ALE only */ ++ u32 alen3 : 3; /* ALE only */ ++ u32 alen4 : 3; /* ALE only */ ++ u32 : 4; ++ u8 adr_byt[4]; /* ALE only */ ++ u32 nine : 4; /* set to 9 */ ++ u32 and_mask : 8; ++ u32 comp_byte : 8; ++ u32 rlen1 : 3; ++ u32 rlen2 : 3; ++ u32 rlen3 : 3; ++ u32 rlen4 : 3; ++}; ++ ++union ndf_cmd { ++ u64 val[2]; ++ union { ++ struct ndf_nop_cmd nop; ++ struct ndf_wait_cmd wait; ++ struct ndf_bus_cmd bus_acq_rel; ++ struct ndf_chip_cmd chip_en_dis; ++ struct ndf_cle_cmd cle_cmd; ++ struct ndf_rd_cmd rd_cmd; ++ struct ndf_wr_cmd wr_cmd; ++ struct ndf_set_tm_par_cmd set_tm_par; ++ struct ndf_ale_cmd ale_cmd; ++ struct ndf_wait_status_cmd wait_status; ++ } u; ++}; ++ ++#define NDF_MISC_MB_DIS BIT_ULL(27) /* Disable multi-bit error hangs */ ++#define NDF_MISC_NBR_HWM GENMASK_ULL(26, 24) /* High watermark for NBR FIFO or load/store operations */ ++#define NDF_MISC_WAIT_CNT GENMASK_ULL(23, 18) /* Wait input filter count */ ++#define NDF_MISC_FR_BYTE GENMASK_ULL(17, 7) /* Unfilled NFD_CMD queue bytes */ ++#define NDF_MISC_RD_DONE BIT_ULL(6) /* Set by HW when it reads the last 8 bytes of NDF_CMD */ ++#define NDF_MISC_RD_VAL BIT_ULL(5) /* Set by HW when it reads. SW read of NDF_CMD clears it */ ++#define NDF_MISC_RD_CMD BIT_ULL(4) /* Let HW read NDF_CMD queue. Cleared on SW NDF_CMD write */ ++#define NDF_MISC_BT_DIS BIT_ULL(2) /* Boot disable */ ++#define NDF_MISC_EX_DIS BIT_ULL(1) /* Stop comand execution after completing command queue */ ++#define NDF_MISC_RST_FF BIT_ULL(0) /* Reset fifo */ ++ ++#define NDF_INT_DMA_DONE BIT_ULL(7) /* DMA request complete */ ++#define NDF_INT_OVFR BIT_ULL(6) /* NDF_CMD write when queue is full */ ++#define NDF_INT_ECC_MULT BIT_ULL(5) /* Multi-bit ECC error detected */ ++#define NDF_INT_ECC_1BIT BIT_ULL(4) /* Single-bit ECC error detected and fixed */ ++#define NDF_INT_SM_BAD BIT_ULL(3) /* State machine is in bad state */ ++#define NDF_INT_WDOG BIT_ULL(2) /* Watchdog timer expired during command execution */ ++#define NDF_INT_FULL BIT_ULL(1) /* NDF_CMD queue is full */ ++#define NDF_INT_EMPTY BIT_ULL(0) /* NDF_CMD queue is empty */ ++ ++#define NDF_DMA_CFG_EN BIT_ULL(63) /* DMA engine enable */ ++#define NDF_DMA_CFG_RW BIT_ULL(62) /* Read or write */ ++#define NDF_DMA_CFG_CLR BIT_ULL(61) /* Terminates DMA and clears enable bit */ ++#define NDF_DMA_CFG_SWAP32 BIT_ULL(59) /* 32-bit swap enable */ ++#define NDF_DMA_CFG_SWAP16 BIT_ULL(58) /* 16-bit swap enable */ ++#define NDF_DMA_CFG_SWAP8 BIT_ULL(57) /* 8-bit swap enable */ ++#define NDF_DMA_CFG_CMD_BE BIT_ULL(56) /* Endian mode */ ++#define NDF_DMA_CFG_SIZE GENMASK_ULL(55, 36) /* Number of 64 bit transfers */ ++ ++#define NDF_ST_REG_EXE_IDLE BIT_ULL(15) /* Command execution status idle */ ++#define NDF_ST_REG_EXE_SM GENMASK_ULL(14, 11) /* Command execution SM states */ ++#define NDF_ST_REG_BT_SM GENMASK_ULL(10, 7) /* DMA and load SM states */ ++#define NDF_ST_REG_RD_FF_BAD BIT_ULL(6) /* Queue read-back SM bad state */ ++#define NDF_ST_REG_RD_FF GENMASK_ULL(5, 4) /* Queue read-back SM states */ ++#define NDF_ST_REG_MAIN_BAD BIT_ULL(3) /* Main SM is in a bad state */ ++#define NDF_ST_REG_MAIN_SM GENMASK_ULL(2, 0) /* Main SM states */ ++ ++#define MAX_NAND_NAME_LEN 64 ++#define NAND_MAX_PAGESIZE 4096 ++#define NAND_MAX_OOBSIZE 256 ++ ++/* NAND chip related information */ ++struct cvm_nand_chip { ++ struct list_head node; ++ struct nand_chip nand; ++ int cs; /* chip select 0..7 */ ++ struct ndf_set_tm_par_cmd timings; /* timing parameters */ ++ int selected_page; ++ bool oob_only; ++ bool iface_set; ++ int iface_mode; ++ int row_bytes; ++ int col_bytes; ++}; ++ ++struct cvm_nand_buf { ++ int dmabuflen; ++ u8 *dmabuf; ++ dma_addr_t dmaaddr; ++ ++ int data_len; /* Number of bytes in the data buffer */ ++ int data_index; /* Current read index */ ++}; ++ ++/* NAND flash controller (NDF) related information */ ++struct cvm_nfc { ++ struct nand_hw_control controller; ++ struct device *dev; ++ void __iomem *base; ++ struct list_head chips; ++ int selected_chip; /* Currently selected NAND chip number */ ++ struct clk *clk; /* System clock */ ++ ++ /* ++ * Status is separate from cvm_nand_buf because ++ * it can be used in parallel and during init. ++ */ ++ u8 *stat; ++ dma_addr_t stat_addr; ++ bool use_status; ++ ++ struct cvm_nand_buf buf; ++ union bch_resp *bch_resp; ++ dma_addr_t bch_rhandle; ++ ++ /* BCH of all-0xff, so erased pages read as error-free */ ++ unsigned char *eccmask; ++}; ++ ++/* settable timings - 0..7 select timing of alen1..4/clen1..3/etc */ ++enum tm_idx { ++ t0, /* fixed at 4<controller.wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Read a single byte from the temporary buffer. Used after READID ++ * to get the NAND information and for STATUS. ++ */ ++static u8 cvm_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ ++ if (tn->use_status) { ++ tn->use_status = false; ++ return *tn->stat; ++ } ++ ++ if (tn->buf.data_index < tn->buf.data_len) ++ return tn->buf.dmabuf[tn->buf.data_index++]; ++ else ++ dev_err(tn->dev, "No data to read\n"); ++ ++ return 0xff; ++} ++ ++/* ++ * Read a number of pending bytes from the temporary buffer. Used ++ * to get page and OOB data. ++ */ ++static void cvm_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ ++ if (len > tn->buf.data_len - tn->buf.data_index) { ++ dev_err(tn->dev, "Not enough data for read of %d bytes\n", len); ++ return; ++ } ++ ++ memcpy(buf, tn->buf.dmabuf + tn->buf.data_index, len); ++ tn->buf.data_index += len; ++} ++ ++static void cvm_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ ++ memcpy(tn->buf.dmabuf + tn->buf.data_len, buf, len); ++ tn->buf.data_len += len; ++} ++ ++/* Overwrite default function to avoid sync abort on chip = -1. */ ++static void cvm_nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ return; ++} ++ ++static inline int timing_to_cycle(u32 psec, unsigned long clock) ++{ ++ unsigned int ns; ++ int ticks; ++ ++ ns = DIV_ROUND_UP(psec, 1000); ++ ns += slew_ns; ++ ++ clock /= 1000000; /* no rounding needed since clock is multiple of 1MHz */ ++ ns *= clock; ++ ++ ticks = DIV_ROUND_UP(ns, 1000); ++ ++ /* actual delay is (tm_parX+1)<tim_mult = 0; ++ sWH = timing_to_cycle(timings->tWH_min, sclk); ++ sCLS = timing_to_cycle(timings->tCLS_min, sclk); ++ sCLH = timing_to_cycle(timings->tCLH_min, sclk); ++ sALS = timing_to_cycle(timings->tALS_min, sclk); ++ sALH = timing_to_cycle(timings->tALH_min, sclk); ++ sRP = timing_to_cycle(timings->tRP_min, sclk); ++ sREH = timing_to_cycle(timings->tREH_min, sclk); ++ sWB = timing_to_cycle(timings->tWB_max, sclk); ++ sWC = timing_to_cycle(timings->tWC_min, sclk); ++ ++ tp->tm_par1 = sWH; ++ tp->tm_par2 = sCLH; ++ tp->tm_par3 = sRP + 1; ++ tp->tm_par4 = sCLS - sWH; ++ tp->tm_par5 = sWC - sWH + 1; ++ tp->tm_par6 = sWB; ++ tp->tm_par7 = 0; ++ tp->tim_mult++; /* overcompensate for bad math */ ++ ++ /* TODO: comment parameter re-use */ ++ ++ pr_debug("%s: tim_par: mult: %d p1: %d p2: %d p3: %d\n", ++ __func__, tp->tim_mult, tp->tm_par1, tp->tm_par2, tp->tm_par3); ++ pr_debug(" p4: %d p5: %d p6: %d p7: %d\n", ++ tp->tm_par4, tp->tm_par5, tp->tm_par6, tp->tm_par7); ++ ++} ++ ++static int set_default_timings(struct cvm_nfc *tn, ++ const struct nand_sdr_timings *timings) ++{ ++ unsigned long sclk = clk_get_rate(tn->clk); ++ ++ set_timings(NULL, &default_timing_parms, timings, sclk); ++ return 0; ++} ++ ++static int cvm_nfc_chip_set_timings(struct cvm_nand_chip *chip, ++ const struct nand_sdr_timings *timings) ++{ ++ struct cvm_nfc *tn = to_cvm_nfc(chip->nand.controller); ++ unsigned long sclk = clk_get_rate(tn->clk); ++ ++ set_timings(chip, &chip->timings, timings, sclk); ++ return 0; ++} ++ ++/* How many bytes are free in the NFD_CMD queue? */ ++static int ndf_cmd_queue_free(struct cvm_nfc *tn) ++{ ++ u64 ndf_misc; ++ ++ ndf_misc = readq(tn->base + NDF_MISC); ++ return FIELD_GET(NDF_MISC_FR_BYTE, ndf_misc); ++} ++ ++/* Submit a command to the NAND command queue. */ ++static int ndf_submit(struct cvm_nfc *tn, union ndf_cmd *cmd) ++{ ++ int opcode = cmd->val[0] & 0xf; ++ ++ switch (opcode) { ++ /* All these commands fit in one 64bit word */ ++ case NDF_OP_NOP: ++ case NDF_OP_SET_TM_PAR: ++ case NDF_OP_WAIT: ++ case NDF_OP_CHIP_EN_DIS: ++ case NDF_OP_CLE_CMD: ++ case NDF_OP_WR_CMD: ++ case NDF_OP_RD_CMD: ++ case NDF_OP_RD_EDO_CMD: ++ case NDF_OP_BUS_ACQ_REL: ++ if (ndf_cmd_queue_free(tn) < 8) ++ goto full; ++ writeq(cmd->val[0], tn->base + NDF_CMD); ++ break; ++ case NDF_OP_ALE_CMD: /* ALE commands take either one or two 64bit words */ ++ if (cmd->u.ale_cmd.adr_byte_num < 5) { ++ if (ndf_cmd_queue_free(tn) < 8) ++ goto full; ++ writeq(cmd->val[0], tn->base + NDF_CMD); ++ } else { ++ if (ndf_cmd_queue_free(tn) < 16) ++ goto full; ++ writeq(cmd->val[0], tn->base + NDF_CMD); ++ writeq(cmd->val[1], tn->base + NDF_CMD); ++ } ++ break; ++ case NDF_OP_WAIT_STATUS: /* Wait status commands take two 64bit words */ ++ if (ndf_cmd_queue_free(tn) < 16) ++ goto full; ++ writeq(cmd->val[0], tn->base + NDF_CMD); ++ writeq(cmd->val[1], tn->base + NDF_CMD); ++ break; ++ default: ++ dev_err(tn->dev, "ndf_submit: unknown command: %u\n", opcode); ++ return -EINVAL; ++ } ++ return 0; ++full: ++ dev_err(tn->dev, "ndf_submit: no space left in command queue\n"); ++ return -ENOMEM; ++} ++ ++/* ++ * Wait for the ready/busy signal. First wait for busy to be valid, ++ * then wait for busy to de-assert. ++ */ ++static int ndf_build_wait_busy(struct cvm_nfc *tn) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.wait.opcode = NDF_OP_WAIT; ++ cmd.u.wait.r_b = 1; ++ cmd.u.wait.wlen = t6; ++ ++ if (ndf_submit(tn, &cmd)) ++ return -ENOMEM; ++ return 0; ++} ++ ++static bool ndf_dma_done(struct cvm_nfc *tn) ++{ ++ u64 dma_cfg, ndf_int; ++ ++ /* Enable bit should be clear after a transfer */ ++ dma_cfg = readq(tn->base + NDF_DMA_CFG); ++ if (!(dma_cfg & NDF_DMA_CFG_EN)) ++ return true; ++ ++ /* Check DMA done bit */ ++ ndf_int = readq(tn->base + NDF_INT); ++ if (!(ndf_int & NDF_INT_DMA_DONE)) ++ return false; ++ ++ return true; ++} ++ ++static int ndf_wait(struct cvm_nfc *tn) ++{ ++ long time_left = HZ; ++ ++ /* enable all IRQ types */ ++ writeq(0xff, tn->base + NDF_INT_ENA_W1S); ++ time_left = wait_event_timeout(tn->controller.wq, ++ ndf_dma_done(tn), time_left); ++ writeq(0xff, tn->base + NDF_INT_ENA_W1C); ++ ++ if (!time_left) { ++ dev_err(tn->dev, "ndf_wait: timeout error\n"); ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++static int ndf_wait_idle(struct cvm_nfc *tn) ++{ ++ u64 val; ++ u64 dval = 0; ++ int rc; ++ int pause = 100; ++ u64 tot_us = USEC_PER_SEC / 10; ++ ++ rc = readq_poll_timeout(tn->base + NDF_ST_REG, ++ val, val & NDF_ST_REG_EXE_IDLE, pause, tot_us); ++ if (!rc) ++ rc = readq_poll_timeout(tn->base + NDF_DMA_CFG, ++ dval, !(dval & NDF_DMA_CFG_EN), pause, tot_us); ++ ++ return rc; ++} ++ ++/* Issue set timing parameters */ ++static int ndf_queue_cmd_timing(struct cvm_nfc *tn, ++ struct ndf_set_tm_par_cmd *timings) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.set_tm_par.opcode = NDF_OP_SET_TM_PAR; ++ cmd.u.set_tm_par.tim_mult = timings->tim_mult; ++ cmd.u.set_tm_par.tm_par1 = timings->tm_par1; ++ cmd.u.set_tm_par.tm_par2 = timings->tm_par2; ++ cmd.u.set_tm_par.tm_par3 = timings->tm_par3; ++ cmd.u.set_tm_par.tm_par4 = timings->tm_par4; ++ cmd.u.set_tm_par.tm_par5 = timings->tm_par5; ++ cmd.u.set_tm_par.tm_par6 = timings->tm_par6; ++ cmd.u.set_tm_par.tm_par7 = timings->tm_par7; ++ return ndf_submit(tn, &cmd); ++} ++ ++/* Issue bus acquire or release */ ++static int ndf_queue_cmd_bus(struct cvm_nfc *tn, int direction) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.bus_acq_rel.opcode = NDF_OP_BUS_ACQ_REL; ++ cmd.u.bus_acq_rel.direction = direction; ++ return ndf_submit(tn, &cmd); ++} ++ ++/* Issue chip select or deselect */ ++static int ndf_queue_cmd_chip(struct cvm_nfc *tn, int enable, int chip, ++ int width) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.chip_en_dis.opcode = NDF_OP_CHIP_EN_DIS; ++ cmd.u.chip_en_dis.chip = chip; ++ cmd.u.chip_en_dis.enable = enable; ++ cmd.u.chip_en_dis.bus_width = width; ++ return ndf_submit(tn, &cmd); ++} ++ ++static int ndf_queue_cmd_wait(struct cvm_nfc *tn, int t_delay) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.wait.opcode = NDF_OP_WAIT; ++ cmd.u.wait.wlen = t_delay; ++ return ndf_submit(tn, &cmd); ++} ++ ++static int ndf_queue_cmd_cle(struct cvm_nfc *tn, int command) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.cle_cmd.opcode = NDF_OP_CLE_CMD; ++ cmd.u.cle_cmd.cmd_data = command; ++ cmd.u.cle_cmd.clen1 = t4; ++ cmd.u.cle_cmd.clen2 = t1; ++ cmd.u.cle_cmd.clen3 = t2; ++ return ndf_submit(tn, &cmd); ++} ++ ++static int ndf_queue_cmd_ale(struct cvm_nfc *tn, int addr_bytes, ++ struct nand_chip *nand, u64 page, ++ u32 col, int page_size) ++{ ++ struct cvm_nand_chip *cvm_nand = (nand) ? to_cvm_nand(nand) : NULL; ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.ale_cmd.opcode = NDF_OP_ALE_CMD; ++ cmd.u.ale_cmd.adr_byte_num = addr_bytes; ++ ++ /* set column bit for OOB area, assume OOB follows page */ ++ if (cvm_nand && cvm_nand->oob_only) ++ col += page_size; ++ ++ /* page is u64 for this generality, even if cmdfunc() passes int */ ++ switch (addr_bytes) { ++ /* 4-8 bytes: page, then 2-byte col */ ++ case 8: ++ cmd.u.ale_cmd.adr_byt8 = (page >> 40) & 0xff; ++ /* fall thru */ ++ case 7: ++ cmd.u.ale_cmd.adr_byt7 = (page >> 32) & 0xff; ++ /* fall thru */ ++ case 6: ++ cmd.u.ale_cmd.adr_byt6 = (page >> 24) & 0xff; ++ /* fall thru */ ++ case 5: ++ cmd.u.ale_cmd.adr_byt5 = (page >> 16) & 0xff; ++ /* fall thru */ ++ case 4: ++ cmd.u.ale_cmd.adr_byt4 = (page >> 8) & 0xff; ++ cmd.u.ale_cmd.adr_byt3 = page & 0xff; ++ cmd.u.ale_cmd.adr_byt2 = (col >> 8) & 0xff; ++ cmd.u.ale_cmd.adr_byt1 = col & 0xff; ++ break; ++ /* 1-3 bytes: just the page address */ ++ case 3: ++ cmd.u.ale_cmd.adr_byt3 = (page >> 16) & 0xff; ++ /* fall thru */ ++ case 2: ++ cmd.u.ale_cmd.adr_byt2 = (page >> 8) & 0xff; ++ /* fall thru */ ++ case 1: ++ cmd.u.ale_cmd.adr_byt1 = page & 0xff; ++ break; ++ default: ++ break; ++ } ++ ++ cmd.u.ale_cmd.alen1 = t3; ++ cmd.u.ale_cmd.alen2 = t1; ++ cmd.u.ale_cmd.alen3 = t5; ++ cmd.u.ale_cmd.alen4 = t2; ++ return ndf_submit(tn, &cmd); ++} ++ ++static int ndf_queue_cmd_write(struct cvm_nfc *tn, int len) ++{ ++ union ndf_cmd cmd; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ cmd.u.wr_cmd.opcode = NDF_OP_WR_CMD; ++ cmd.u.wr_cmd.data = len; ++ cmd.u.wr_cmd.wlen1 = t3; ++ cmd.u.wr_cmd.wlen2 = t1; ++ return ndf_submit(tn, &cmd); ++} ++ ++static int ndf_build_pre_cmd(struct cvm_nfc *tn, int cmd1, ++ int addr_bytes, u64 page, u32 col, int cmd2) ++{ ++ struct nand_chip *nand = tn->controller.active; ++ struct cvm_nand_chip *cvm_nand; ++ struct ndf_set_tm_par_cmd *timings; ++ int width, page_size, rc; ++ ++ /* Also called before chip probing is finished */ ++ if (!nand) { ++ timings = &default_timing_parms; ++ page_size = default_page_size; ++ width = default_width; ++ } else { ++ cvm_nand = to_cvm_nand(nand); ++ timings = &cvm_nand->timings; ++ page_size = nand->mtd.writesize; ++ if (nand->options & NAND_BUSWIDTH_16) ++ width = 2; ++ else ++ width = 1; ++ } ++ ++ rc = ndf_queue_cmd_timing(tn, timings); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_bus(tn, NDF_BUS_ACQUIRE); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_chip(tn, 1, tn->selected_chip, width); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_wait(tn, t1); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_cle(tn, cmd1); ++ if (rc) ++ return rc; ++ ++ if (addr_bytes) { ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_ale(tn, addr_bytes, nand, ++ page, col, page_size); ++ if (rc) ++ return rc; ++ } ++ ++ /* CLE 2 */ ++ if (cmd2) { ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_cle(tn, cmd2); ++ if (rc) ++ return rc; ++ } ++ return 0; ++} ++ ++static int ndf_build_post_cmd(struct cvm_nfc *tn, int hold_time) ++{ ++ int rc; ++ ++ /* Deselect chip */ ++ rc = ndf_queue_cmd_chip(tn, 0, 0, 0); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_wait(tn, t2); ++ if (rc) ++ return rc; ++ ++ /* Release bus */ ++ rc = ndf_queue_cmd_bus(tn, 0); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_wait(tn, hold_time); ++ if (rc) ++ return rc; ++ ++ /* Write 1 to clear all interrupt bits before starting DMA */ ++ writeq(0xff, tn->base + NDF_INT); ++ ++ /* and enable, before doorbell starts actiion */ ++ writeq(0xff, tn->base + NDF_INT_ENA_W1S); ++ ++ /* ++ * Last action is ringing the doorbell with number of bus ++ * acquire-releases cycles (currently 1). ++ */ ++ writeq(1, tn->base + NDF_DRBELL); ++ return 0; ++} ++ ++/* Setup the NAND DMA engine for a transfer. */ ++static void ndf_setup_dma(struct cvm_nfc *tn, int is_write, ++ dma_addr_t bus_addr, int len) ++{ ++ u64 dma_cfg; ++ ++ dma_cfg = FIELD_PREP(NDF_DMA_CFG_RW, is_write) | ++ FIELD_PREP(NDF_DMA_CFG_SIZE, (len >> 3) - 1); ++ dma_cfg |= NDF_DMA_CFG_EN; ++ writeq(bus_addr, tn->base + NDF_DMA_ADR); ++ writeq(dma_cfg, tn->base + NDF_DMA_CFG); ++} ++ ++static int cvm_nand_reset(struct cvm_nfc *tn) ++{ ++ int rc; ++ ++ rc = ndf_build_pre_cmd(tn, NAND_CMD_RESET, 0, 0, 0, 0); ++ if (rc) ++ return rc; ++ ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ rc = ndf_build_post_cmd(tn, t2); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++static int ndf_read(struct cvm_nfc *tn, int cmd1, int addr_bytes, ++ u64 page, u32 col, int cmd2, int len) ++{ ++ dma_addr_t bus_addr = tn->use_status ? tn->stat_addr : tn->buf.dmaaddr; ++ struct nand_chip *nand = tn->controller.active; ++ int timing_mode, bytes, rc; ++ union ndf_cmd cmd; ++ u64 start, end; ++ ++ if (!nand) ++ timing_mode = default_onfi_timing; ++ else ++ timing_mode = nand->onfi_timing_mode_default; ++ ++ /* Build the command and address cycles */ ++ rc = ndf_build_pre_cmd(tn, cmd1, addr_bytes, page, col, cmd2); ++ if (rc) ++ return rc; ++ ++ /* This waits for some time, then waits for busy to be de-asserted. */ ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ memset(&cmd, 0, sizeof(cmd)); ++ ++ if (timing_mode < 4) ++ cmd.u.rd_cmd.opcode = NDF_OP_RD_CMD; ++ else ++ cmd.u.rd_cmd.opcode = NDF_OP_RD_EDO_CMD; ++ ++ cmd.u.rd_cmd.data = len; ++ cmd.u.rd_cmd.rlen1 = t7; ++ cmd.u.rd_cmd.rlen2 = t3; ++ cmd.u.rd_cmd.rlen3 = t1; ++ cmd.u.rd_cmd.rlen4 = t7; ++ rc = ndf_submit(tn, &cmd); ++ if (rc) ++ return rc; ++ ++ start = (u64) bus_addr; ++ ndf_setup_dma(tn, 0, bus_addr, len); ++ ++ rc = ndf_build_post_cmd(tn, t2); ++ if (rc) ++ return rc; ++ ++ /* Wait for the DMA to complete */ ++ rc = ndf_wait(tn); ++ if (rc) ++ return rc; ++ ++ end = readq(tn->base + NDF_DMA_ADR); ++ bytes = end - start; ++ ++ /* Make sure NDF is really done */ ++ rc = ndf_wait_idle(tn); ++ if (rc) { ++ dev_err(tn->dev, "poll idle failed\n"); ++ return rc; ++ } ++ ++ return bytes; ++} ++ ++static int cvm_nand_get_features(struct mtd_info *mtd, ++ struct nand_chip *chip, int feature_addr, ++ u8 *subfeature_para) ++{ ++ struct nand_chip *nand = chip; ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ int len = 8; ++ int rc; ++ ++ memset(tn->buf.dmabuf, 0xff, len); ++ tn->buf.data_index = 0; ++ tn->buf.data_len = 0; ++ rc = ndf_read(tn, NAND_CMD_GET_FEATURES, 1, feature_addr, 0, 0, len); ++ if (rc) ++ return rc; ++ ++ memcpy(subfeature_para, tn->buf.dmabuf, ONFI_SUBFEATURE_PARAM_LEN); ++ ++ return 0; ++} ++ ++static int cvm_nand_set_features(struct mtd_info *mtd, ++ struct nand_chip *chip, int feature_addr, ++ u8 *subfeature_para) ++{ ++ struct nand_chip *nand = chip; ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ const int len = ONFI_SUBFEATURE_PARAM_LEN; ++ int rc; ++ ++ rc = ndf_build_pre_cmd(tn, NAND_CMD_SET_FEATURES, ++ 1, feature_addr, 0, 0); ++ if (rc) ++ return rc; ++ ++ memcpy(tn->buf.dmabuf, subfeature_para, len); ++ memset(tn->buf.dmabuf + len, 0, 8 - len); ++ ++ ndf_setup_dma(tn, 1, tn->buf.dmaaddr, 8); ++ ++ rc = ndf_queue_cmd_write(tn, 8); ++ if (rc) ++ return rc; ++ ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ rc = ndf_build_post_cmd(tn, t2); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++/* ++ * Read a page from NAND. If the buffer has room, the out of band ++ * data will be included. ++ */ ++static int ndf_page_read(struct cvm_nfc *tn, u64 page, int col, int len) ++{ ++ struct nand_chip *nand = tn->controller.active; ++ struct cvm_nand_chip *chip = to_cvm_nand(nand); ++ int addr_bytes = chip->row_bytes + chip->col_bytes; ++ ++ memset(tn->buf.dmabuf, 0xff, len); ++ return ndf_read(tn, NAND_CMD_READ0, addr_bytes, ++ page, col, NAND_CMD_READSTART, len); ++} ++ ++/* Erase a NAND block */ ++static int ndf_block_erase(struct cvm_nfc *tn, u64 page_addr) ++{ ++ struct nand_chip *nand = tn->controller.active; ++ struct cvm_nand_chip *chip = to_cvm_nand(nand); ++ int addr_bytes = chip->row_bytes; ++ int rc; ++ ++ rc = ndf_build_pre_cmd(tn, NAND_CMD_ERASE1, addr_bytes, ++ page_addr, 0, NAND_CMD_ERASE2); ++ if (rc) ++ return rc; ++ ++ /* Wait for R_B to signal erase is complete */ ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ rc = ndf_build_post_cmd(tn, t2); ++ if (rc) ++ return rc; ++ ++ /* Wait until the command queue is idle */ ++ return ndf_wait_idle(tn); ++} ++ ++/* ++ * Write a page (or less) to NAND. ++ */ ++static int ndf_page_write(struct cvm_nfc *tn, int page) ++{ ++ int len, rc; ++ struct nand_chip *nand = tn->controller.active; ++ struct cvm_nand_chip *chip = to_cvm_nand(nand); ++ int addr_bytes = chip->row_bytes + chip->col_bytes; ++ ++ len = tn->buf.data_len - tn->buf.data_index; ++ chip->oob_only = (tn->buf.data_index >= nand->mtd.writesize); ++ WARN_ON_ONCE(len & 0x7); ++ ++ ndf_setup_dma(tn, 1, tn->buf.dmaaddr + tn->buf.data_index, len); ++ rc = ndf_build_pre_cmd(tn, NAND_CMD_SEQIN, addr_bytes, page, 0, 0); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_write(tn, len); ++ if (rc) ++ return rc; ++ ++ rc = ndf_queue_cmd_cle(tn, NAND_CMD_PAGEPROG); ++ if (rc) ++ return rc; ++ ++ /* Wait for R_B to signal program is complete */ ++ rc = ndf_build_wait_busy(tn); ++ if (rc) ++ return rc; ++ ++ rc = ndf_build_post_cmd(tn, t2); ++ if (rc) ++ return rc; ++ ++ /* Wait for the DMA to complete */ ++ rc = ndf_wait(tn); ++ if (rc) ++ return rc; ++ ++ /* Data transfer is done but NDF is not, it is waiting for R/B# */ ++ return ndf_wait_idle(tn); ++} ++ ++static void cvm_nand_cmdfunc(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nand_chip *cvm_nand = to_cvm_nand(nand); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ int rc; ++ ++ tn->selected_chip = cvm_nand->cs; ++ if (tn->selected_chip < 0 || tn->selected_chip >= NAND_MAX_CHIPS) { ++ dev_err(tn->dev, "invalid chip select\n"); ++ return; ++ } ++ ++ tn->use_status = false; ++ ++ switch (command) { ++ case NAND_CMD_READID: ++ tn->buf.data_index = 0; ++ cvm_nand->oob_only = false; ++ rc = ndf_read(tn, command, 1, column, 0, 0, 8); ++ if (rc < 0) ++ dev_err(tn->dev, "READID failed with %d\n", rc); ++ else ++ tn->buf.data_len = rc; ++ break; ++ ++ case NAND_CMD_READOOB: ++ cvm_nand->oob_only = true; ++ tn->buf.data_index = 0; ++ tn->buf.data_len = 0; ++ rc = ndf_page_read(tn, page_addr, column, mtd->oobsize); ++ if (rc < mtd->oobsize) ++ dev_err(tn->dev, "READOOB failed with %d\n", ++ tn->buf.data_len); ++ else ++ tn->buf.data_len = rc; ++ break; ++ ++ case NAND_CMD_READ0: ++ cvm_nand->oob_only = false; ++ tn->buf.data_index = 0; ++ tn->buf.data_len = 0; ++ rc = ndf_page_read(tn, ++ page_addr, column, ++ mtd->writesize + mtd->oobsize); ++ ++ if (rc < mtd->writesize + mtd->oobsize) ++ dev_err(tn->dev, "READ0 failed with %d\n", rc); ++ else ++ tn->buf.data_len = rc; ++ break; ++ ++ case NAND_CMD_STATUS: ++ /* used in oob/not states */ ++ tn->use_status = true; ++ rc = ndf_read(tn, command, 0, 0, 0, 0, 8); ++ if (rc < 0) ++ dev_err(tn->dev, "STATUS failed with %d\n", rc); ++ break; ++ ++ case NAND_CMD_RESET: ++ /* used in oob/not states */ ++ rc = cvm_nand_reset(tn); ++ if (rc < 0) ++ dev_err(tn->dev, "RESET failed with %d\n", rc); ++ break; ++ ++ case NAND_CMD_PARAM: ++ cvm_nand->oob_only = false; ++ tn->buf.data_index = 0; ++ rc = ndf_read(tn, command, 1, 0, 0, 0, ++ min(tn->buf.dmabuflen, 3 * 512)); ++ if (rc < 0) ++ dev_err(tn->dev, "PARAM failed with %d\n", rc); ++ else ++ tn->buf.data_len = rc; ++ break; ++ ++ case NAND_CMD_RNDOUT: ++ tn->buf.data_index = column; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ if (ndf_block_erase(tn, page_addr)) ++ dev_err(tn->dev, "ERASE1 failed\n"); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ /* We do all erase processing in the first command, so ignore ++ * this one. ++ */ ++ break; ++ ++ case NAND_CMD_SEQIN: ++ cvm_nand->oob_only = (column >= mtd->writesize); ++ tn->buf.data_index = column; ++ tn->buf.data_len = column; ++ ++ cvm_nand->selected_page = page_addr; ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ rc = ndf_page_write(tn, cvm_nand->selected_page); ++ if (rc) ++ dev_err(tn->dev, "PAGEPROG failed with %d\n", rc); ++ break; ++ ++ case NAND_CMD_SET_FEATURES: ++ cvm_nand->oob_only = false; ++ /* assume tn->buf.data_len == 4 of data has been set there */ ++ rc = cvm_nand_set_features(mtd, nand, ++ page_addr, tn->buf.dmabuf); ++ if (rc) ++ dev_err(tn->dev, "SET_FEATURES failed with %d\n", rc); ++ break; ++ ++ case NAND_CMD_GET_FEATURES: ++ cvm_nand->oob_only = false; ++ rc = cvm_nand_get_features(mtd, nand, ++ page_addr, tn->buf.dmabuf); ++ if (!rc) { ++ tn->buf.data_index = 0; ++ tn->buf.data_len = 4; ++ } else { ++ dev_err(tn->dev, "GET_FEATURES failed with %d\n", rc); ++ } ++ break; ++ ++ default: ++ WARN_ON_ONCE(1); ++ dev_err(tn->dev, "unhandled nand cmd: %x\n", command); ++ } ++} ++ ++static int cvm_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct cvm_nfc *tn = to_cvm_nfc(chip->controller); ++ int ret; ++ ++ ret = ndf_wait_idle(tn); ++ return (ret < 0) ? -EIO : 0; ++} ++ ++/* check compatibility with ONFI timing mode#N, and optionally apply */ ++static int cvm_nand_setup_data_interface(struct mtd_info *mtd, int chipnr, ++ const struct nand_data_interface *conf) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nand_chip *chip = to_cvm_nand(nand); ++ int rc; ++ static u64 tWC_N[MAX_ONFI_MODE+2]; /* cache a mode signature */ ++ int mode; /* deduced mode number, for reporting and restricting */ ++ ++ nand->select_chip(mtd, chipnr); ++ ++ /* ++ * Cache timing modes for reporting, and reducing needless change. ++ * ++ * Challenge: caller does not pass ONFI mode#, but reporting the mode ++ * and restricting to a maximum, or a list, are useful for diagnosing ++ * new hardware. So use tWC_min, distinct and monotonic across modes, ++ * to discover the requested/accepted mode number ++ */ ++ for (mode = MAX_ONFI_MODE; mode >= 0 && !tWC_N[0]; mode--) { ++ const struct nand_sdr_timings *t; ++ ++ t = onfi_async_timing_mode_to_sdr_timings(mode); ++ if (!t) ++ continue; ++ tWC_N[mode] = t->tWC_min; ++ } ++ ++ if (!conf) { ++ rc = -EINVAL; ++ } else if (nand->data_interface && ++ chip->iface_set && chip->iface_mode == mode) { ++ /* ++ * Cases: ++ * - called from nand_reset, which clears DDR timing ++ * mode back to SDR. BUT if we're already in SDR, ++ * timing mode persists over resets. ++ * While mtd/nand layer only supports SDR, ++ * this is always safe. And this driver only supports SDR. ++ * ++ * - called from post-power-event nand_reset (maybe ++ * NFC+flash power down, or system hibernate. ++ * Address this when CONFIG_PM support added ++ */ ++ rc = 0; ++ } else { ++ rc = cvm_nfc_chip_set_timings(chip, &conf->timings.sdr); ++ if (!rc) { ++ chip->iface_mode = mode; ++ chip->iface_set = true; ++ } ++ } ++ return rc; ++} ++ ++#ifdef DEBUG ++# define DEBUG_INIT 1 ++# define DEBUG_READ 2 ++# define DEBUG_WRITE 4 ++# define DEBUG_ALL 7 ++static int trace = DEBUG_INIT; ++module_param(trace, int, 0644); ++# define DEV_DBG(D, d, f, ...) do { \ ++ if ((D) & trace) \ ++ dev_dbg(d, f, ##__VA_ARGS__); \ ++ } while (0) ++#else ++# define DEV_DBG(D, d, f, ...) (void)0 ++#endif ++ ++#if IS_ENABLED(CONFIG_CAVIUM_BCH) ++ ++/* ++ * Optional BCH driver is not built into cavium_nand, ++ * because the engine can also be used in copy mode as a dma-engine ++ * (not implemented, but a good reson to keep modules separate). ++ * Kconfig defaults BCH=y when NAND=y, and BCH=m when NAND=m, ++ * but there's no means to *enforce* this in Kconfig, ++ * so check at build time. ++ */ ++# if defined(CONFIG_CAVIUM_BCH_MODULE) ++# if !defined(CONFIG_MTD_NAND_CAVIUM_MODULE) ++# error if CONFIG_CAVIUM_BCH=m, CONFIG_MTD_NAND_CAVIUM=m is required ++# endif ++# endif ++ ++static void cavm_bch_reset(void) ++{ ++ cavm_bch_putv(bch_vf); ++ bch_vf = cavm_bch_getv(); ++} ++ ++/* ++ * Given a page, calculate the ECC code ++ * ++ * chip: Pointer to NAND chip data structure ++ * buf: Buffer to calculate ECC on ++ * code: Buffer to hold ECC data ++ * ++ * Return 0 on success or -1 on failure ++ */ ++static int octeon_nand_bch_calculate_ecc_internal(struct mtd_info *mtd, ++ dma_addr_t ihandle, uint8_t *code) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ int rc; ++ int i; ++ static uint8_t *ecc_buffer; ++ static int ecc_size; ++ static dma_addr_t ecc_handle; ++ union bch_resp *r = tn->bch_resp; ++ ++ if (!ecc_buffer || ecc_size < nand->ecc.size) { ++ ecc_size = nand->ecc.size; ++ ecc_buffer = dma_alloc_coherent(tn->dev, ecc_size, ++ &ecc_handle, GFP_KERNEL); ++ } ++ ++ memset(ecc_buffer, 0, nand->ecc.bytes); ++ ++ r->u16 = 0; ++ wmb(); /* flush done=0 before making request */ ++ ++ rc = cavm_bch_encode(bch_vf, ihandle, nand->ecc.size, ++ nand->ecc.strength, ++ ecc_handle, tn->bch_rhandle); ++ ++ if (!rc) { ++ cavm_bch_wait(bch_vf, r, tn->bch_rhandle); ++ } else { ++ ++ dev_err(tn->dev, "octeon_bch_encode failed\n"); ++ return -1; ++ } ++ ++ if (!r->s.done || r->s.uncorrectable) { ++ dev_err(tn->dev, ++ "%s timeout, done:%d uncorr:%d corr:%d erased:%d\n", ++ __func__, r->s.done, r->s.uncorrectable, ++ r->s.num_errors, r->s.erased); ++ cavm_bch_reset(); ++ return -1; ++ } ++ ++ memcpy(code, ecc_buffer, nand->ecc.bytes); ++ ++ for (i = 0; i < nand->ecc.bytes; i++) ++ code[i] ^= tn->eccmask[i]; ++ ++ return tn->bch_resp->s.num_errors; ++} ++ ++/* ++ * Given a page, calculate the ECC code ++ * ++ * mtd: MTD block structure ++ * dat: raw data (unused) ++ * ecc_code: buffer for ECC ++ */ ++static int octeon_nand_bch_calculate(struct mtd_info *mtd, ++ const uint8_t *dat, uint8_t *ecc_code) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ dma_addr_t handle = dma_map_single(tn->dev, (u8 *)dat, ++ nand->ecc.size, DMA_TO_DEVICE); ++ int ret; ++ ++ ret = octeon_nand_bch_calculate_ecc_internal( ++ mtd, handle, (void *)ecc_code); ++ ++ dma_unmap_single(tn->dev, handle, ++ nand->ecc.size, DMA_TO_DEVICE); ++ return ret; ++} ++/* ++ * Detect and correct multi-bit ECC for a page ++ * ++ * mtd: MTD block structure ++ * dat: raw data read from the chip ++ * read_ecc: ECC from the chip (unused) ++ * isnull: unused ++ * ++ * Returns number of bits corrected or -1 if unrecoverable ++ */ ++static int octeon_nand_bch_correct(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *isnull) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ int i = nand->ecc.size + nand->ecc.bytes; ++ static uint8_t *data_buffer; ++ static dma_addr_t ihandle; ++ static int buffer_size; ++ dma_addr_t ohandle; ++ union bch_resp *r = tn->bch_resp; ++ int rc; ++ ++ if (i > buffer_size) { ++ if (buffer_size) ++ dma_free_coherent(tn->dev, buffer_size, ++ data_buffer, ihandle); ++ data_buffer = dma_alloc_coherent(tn->dev, i, ++ &ihandle, GFP_KERNEL); ++ if (!data_buffer) { ++ dev_err(tn->dev, ++ "%s: Could not allocate %d bytes for buffer\n", ++ __func__, i); ++ goto error; ++ } ++ buffer_size = i; ++ } ++ ++ memcpy(data_buffer, dat, nand->ecc.size); ++ memcpy(data_buffer + nand->ecc.size, ++ read_ecc, nand->ecc.bytes); ++ ++ for (i = 0; i < nand->ecc.bytes; i++) ++ data_buffer[nand->ecc.size + i] ^= tn->eccmask[i]; ++ ++ r->u16 = 0; ++ wmb(); /* flush done=0 before making request */ ++ ++ ohandle = dma_map_single(tn->dev, dat, nand->ecc.size, DMA_FROM_DEVICE); ++ rc = cavm_bch_decode(bch_vf, ihandle, nand->ecc.size, ++ nand->ecc.strength, ohandle, tn->bch_rhandle); ++ ++ if (!rc) ++ cavm_bch_wait(bch_vf, r, tn->bch_rhandle); ++ ++ dma_unmap_single(tn->dev, ohandle, nand->ecc.size, DMA_FROM_DEVICE); ++ ++ if (rc) { ++ dev_err(tn->dev, "cavm_bch_decode failed\n"); ++ goto error; ++ } ++ ++ if (!r->s.done) { ++ dev_err(tn->dev, "Error: BCH engine timeout\n"); ++ cavm_bch_reset(); ++ goto error; ++ } ++ ++ if (r->s.erased) { ++ DEV_DBG(DEBUG_ALL, tn->dev, "Info: BCH block is erased\n"); ++ return 0; ++ } ++ ++ if (r->s.uncorrectable) { ++ DEV_DBG(DEBUG_ALL, tn->dev, ++ "Cannot correct NAND block, response: 0x%x\n", ++ r->u16); ++ goto error; ++ } ++ ++ return r->s.num_errors; ++ ++error: ++ DEV_DBG(DEBUG_ALL, tn->dev, "Error performing bch correction\n"); ++ return -1; ++} ++ ++void octeon_nand_bch_hwctl(struct mtd_info *mtd, int mode) ++{ ++ /* Do nothing. */ ++} ++ ++static int octeon_nand_hw_bch_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p; ++ uint8_t *ecc_code = chip->buffers->ecccode; ++ unsigned int max_bitflips = 0; ++ ++ /* chip->read_buf() insists on sequential order, we do OOB first */ ++ memcpy(chip->oob_poi, tn->buf.dmabuf + mtd->writesize, mtd->oobsize); ++ ++ /* Use private buffer as input for ECC correction */ ++ p = tn->buf.dmabuf; ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ ++ DEV_DBG(DEBUG_READ, tn->dev, ++ "Correcting block offset %lx, ecc offset %x\n", ++ p - buf, i); ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ DEV_DBG(DEBUG_ALL, tn->dev, ++ "Cannot correct NAND page %d\n", page); ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ ++ /* Copy corrected data to caller's buffer now */ ++ memcpy(buf, tn->buf.dmabuf, mtd->writesize); ++ ++ return max_bitflips; ++} ++ ++static int octeon_nand_hw_bch_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct cvm_nfc *tn = to_cvm_nfc(chip->controller); ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ const uint8_t *p; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ ++ DEV_DBG(DEBUG_WRITE, tn->dev, "%s(buf?%p, oob%d p%x)\n", ++ __func__, buf, oob_required, page); ++ for (i = 0; i < chip->ecc.total; i++) ++ ecc_calc[i] = 0xFF; ++ ++ /* Copy the page data from caller's buffers to private buffer */ ++ chip->write_buf(mtd, buf, mtd->writesize); ++ /* Use private date as source for ECC calculation */ ++ p = tn->buf.dmabuf; ++ ++ /* Hardware ECC calculation */ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int ret; ++ ++ ret = chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ if (ret < 0) ++ DEV_DBG(DEBUG_WRITE, tn->dev, ++ "calculate(mtd, p?%p, &ecc_calc[%d]?%p) returned %d\n", ++ p, i, &ecc_calc[i], ret); ++ ++ DEV_DBG(DEBUG_WRITE, tn->dev, ++ "block offset %lx, ecc offset %x\n", p - buf, i); ++ } ++ ++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ /* Store resulting OOB into private buffer, will be sent to HW */ ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return 0; ++} ++ ++/** ++ * nand_write_page_raw - [INTERN] raw page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ * ++ * Not for syndrome calculating ECC controllers, which use a special oob layout. ++ */ ++static int octeon_nand_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ chip->write_buf(mtd, buf, mtd->writesize); ++ if (oob_required) ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return 0; ++} ++ ++/** ++ * octeon_nand_write_oob_std - [REPLACEABLE] the most common OOB data write ++ * function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to write ++ */ ++static int octeon_nand_write_oob_std(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ int page) ++{ ++ int status = 0; ++ const uint8_t *buf = chip->oob_poi; ++ int length = mtd->oobsize; ++ ++ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); ++ chip->write_buf(mtd, buf, length); ++ /* Send command to program the OOB data */ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/** ++ * octeon_nand_read_page_raw - [INTERN] read raw page data without ecc ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * Not for syndrome calculating ECC controllers, which use a special oob layout. ++ */ ++static int octeon_nand_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ chip->read_buf(mtd, buf, mtd->writesize); ++ if (oob_required) ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ return 0; ++} ++ ++static int octeon_nand_read_oob_std(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ int page) ++ ++{ ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ return 0; ++} ++ ++static int octeon_nand_calc_bch_ecc_strength(struct nand_chip *nand) ++{ ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ int nsteps = mtd->writesize / ecc->size; ++ int oobchunk = mtd->oobsize / nsteps; ++ ++ /* ecc->strength determines ecc_level and OOB's ecc_bytes. */ ++ const u8 strengths[] = {4, 8, 16, 24, 32, 40, 48, 56, 60, 64}; ++ /* first set the desired ecc_level to match strengths[] */ ++ int index = ARRAY_SIZE(strengths) - 1; ++ int need; ++ ++ while (index > 0 && !(ecc->options & NAND_ECC_MAXIMIZE) && ++ strengths[index - 1] >= ecc->strength) ++ index--; ++ ++ do { ++ need = DIV_ROUND_UP(15 * strengths[index], 8); ++ if (need <= oobchunk - 2) ++ break; ++ } while (index > 0); ++ ++ ecc->strength = strengths[index]; ++ ecc->bytes = need; ++ ++ if (!tn->eccmask) ++ tn->eccmask = devm_kzalloc(tn->dev, ecc->bytes, GFP_KERNEL); ++ if (!tn->eccmask) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/* sample the BCH signature of an erased (all 0xff) page, ++ * to XOR into all page traffic, so erased pages have no ECC errors ++ */ ++static int cvm_bch_save_empty_eccmask(struct nand_chip *nand) ++{ ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ unsigned int eccsize = nand->ecc.size; ++ unsigned int eccbytes = nand->ecc.bytes; ++ uint8_t erased_ecc[eccbytes]; ++ dma_addr_t erased_handle; ++ unsigned char *erased_page = dma_alloc_coherent(tn->dev, eccsize, ++ &erased_handle, GFP_KERNEL); ++ int i; ++ int rc = 0; ++ ++ if (!erased_page) ++ return -ENOMEM; ++ ++ memset(erased_page, 0xff, eccsize); ++ memset(erased_ecc, 0, eccbytes); ++ ++ rc = octeon_nand_bch_calculate_ecc_internal(mtd, ++ erased_handle, erased_ecc); ++ ++ dma_free_coherent(tn->dev, eccsize, erased_page, erased_handle); ++ ++ for (i = 0; i < eccbytes; i++) ++ tn->eccmask[i] = erased_ecc[i] ^ 0xff; ++ ++ return rc; ++} ++#endif /*CONFIG_CAVIUM_BCH*/ ++ ++static void cvm_nfc_chip_sizing(struct nand_chip *nand) ++{ ++ struct cvm_nand_chip *chip = to_cvm_nand(nand); ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ ++ chip->row_bytes = nand->onfi_params.addr_cycles & 0xf; ++ chip->col_bytes = nand->onfi_params.addr_cycles >> 4; ++ ++ /* ++ * HW_BCH using Cavium BCH engine, or SOFT_BCH laid out in ++ * HW_BCH-compatible fashion, depending on devtree advice ++ * and kernel config. ++ * BCH/NFC hardware capable of subpage ops, not implemented. ++ */ ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ++ nand->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ if (ecc->mode != NAND_ECC_NONE) { ++ int nsteps = ecc->steps ?: 1; ++ ++ if (ecc->size && ecc->size != mtd->writesize) ++ nsteps = mtd->writesize / ecc->size; ++ else if (mtd->writesize > def_ecc_size && ++ !(mtd->writesize & (def_ecc_size - 1))) ++ nsteps = mtd->writesize / def_ecc_size; ++ ecc->steps = nsteps; ++ ecc->size = mtd->writesize / nsteps; ++ ecc->bytes = mtd->oobsize / nsteps; ++ ++ /* ++ * no subpage ops, but set subpage-shift to match ecc->steps ++ * so mtd_nandbiterrs tests appropriate boundaries ++ */ ++ if (!mtd->subpage_sft && !(ecc->steps & (ecc->steps - 1))) ++ mtd->subpage_sft = fls(ecc->steps) - 1; ++ ++#if IS_ENABLED(CONFIG_CAVIUM_BCH) ++ if (ecc->mode != NAND_ECC_SOFT && ++ !IS_ERR_OR_NULL(bch_vf) && ++ !octeon_nand_calc_bch_ecc_strength(nand)) { ++ struct cvm_nfc *tn = to_cvm_nfc(nand->controller); ++ struct device *dev = tn->dev; ++ ++ dev_info(dev, "Using hardware BCH engine support\n"); ++ ecc->mode = NAND_ECC_HW_SYNDROME; ++ ecc->algo = NAND_ECC_BCH; ++ ecc->read_page = octeon_nand_hw_bch_read_page; ++ ecc->write_page = octeon_nand_hw_bch_write_page; ++ ecc->read_page_raw = octeon_nand_read_page_raw; ++ ecc->write_page_raw = octeon_nand_write_page_raw; ++ ecc->read_oob = octeon_nand_read_oob_std; ++ ecc->write_oob = octeon_nand_write_oob_std; ++ ++ ecc->calculate = octeon_nand_bch_calculate; ++ ecc->correct = octeon_nand_bch_correct; ++ ecc->hwctl = octeon_nand_bch_hwctl; ++ ++ DEV_DBG(DEBUG_INIT, tn->dev, ++ "NAND chip %d using hw_bch\n", ++ tn->selected_chip); ++ DEV_DBG(DEBUG_INIT, tn->dev, ++ " %d bytes ECC per %d byte block\n", ++ ecc->bytes, ecc->size); ++ DEV_DBG(DEBUG_INIT, tn->dev, ++ " for %d bits of correction per block.", ++ ecc->strength); ++ ++ cvm_bch_save_empty_eccmask(nand); ++ } ++#endif /*CONFIG_CAVIUM_BCH*/ ++ } ++} ++ ++static int cvm_nfc_chip_init(struct cvm_nfc *tn, struct device *dev, ++ struct device_node *np) ++{ ++ struct cvm_nand_chip *chip; ++ struct nand_chip *nand; ++ struct mtd_info *mtd; ++ int ret; ++ ++ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32(np, "reg", &chip->cs); ++ if (ret) { ++ dev_err(dev, "could not retrieve reg property: %d\n", ret); ++ return ret; ++ } ++ ++ if (chip->cs >= NAND_MAX_CHIPS) { ++ dev_err(dev, "invalid reg value: %u (max CS = 7)\n", chip->cs); ++ return -EINVAL; ++ } ++ ++ nand = &chip->nand; ++ nand->controller = &tn->controller; ++ ++ nand_set_flash_node(nand, np); ++ ++ nand->select_chip = cvm_nand_select_chip; ++ nand->cmdfunc = cvm_nand_cmdfunc; ++ nand->waitfunc = cvm_nand_waitfunc; ++ nand->read_byte = cvm_nand_read_byte; ++ nand->read_buf = cvm_nand_read_buf; ++ nand->write_buf = cvm_nand_write_buf; ++ nand->onfi_set_features = cvm_nand_set_features; ++ nand->onfi_get_features = cvm_nand_get_features; ++ nand->setup_data_interface = cvm_nand_setup_data_interface; ++ ++ mtd = nand_to_mtd(nand); ++ mtd->dev.parent = dev; ++ ++ /* TODO: support more then 1 chip */ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; ++ ++ cvm_nfc_chip_sizing(nand); ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) { ++ dev_err(dev, "nand_scan_tail failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(dev, "failed to register mtd device: %d\n", ret); ++ nand_release(mtd); ++ return ret; ++ } ++ ++ list_add_tail(&chip->node, &tn->chips); ++ return 0; ++} ++ ++static int cvm_nfc_chips_init(struct cvm_nfc *tn) ++{ ++ struct device *dev = tn->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *nand_np; ++ int nr_chips = of_get_child_count(np); ++ int ret; ++ ++ if (nr_chips > NAND_MAX_CHIPS) { ++ dev_err(dev, "too many NAND chips: %d\n", nr_chips); ++ return -EINVAL; ++ } ++ ++ if (!nr_chips) { ++ dev_err(dev, "no DT NAND chips found\n"); ++ return -ENODEV; ++ } ++ ++ pr_info("%s: scanning %d chips DTs\n", __func__, nr_chips); ++ ++ for_each_child_of_node(np, nand_np) { ++ ret = cvm_nfc_chip_init(tn, dev, nand_np); ++ if (ret) { ++ of_node_put(nand_np); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++/* Reset NFC and initialize registers. */ ++static int cvm_nfc_init(struct cvm_nfc *tn) ++{ ++ const struct nand_sdr_timings *timings; ++ u64 ndf_misc; ++ int rc; ++ ++ /* Initialize values and reset the fifo */ ++ ndf_misc = readq(tn->base + NDF_MISC); ++ ++ ndf_misc &= ~NDF_MISC_EX_DIS; ++ ndf_misc |= (NDF_MISC_BT_DIS | NDF_MISC_RST_FF); ++ writeq(ndf_misc, tn->base + NDF_MISC); ++ ++ /* Bring the fifo out of reset */ ++ ndf_misc &= ~(NDF_MISC_RST_FF); ++ ++ /* Maximum of co-processor cycles for glitch filtering */ ++ ndf_misc |= FIELD_PREP(NDF_MISC_WAIT_CNT, 0x3f); ++ ++ writeq(ndf_misc, tn->base + NDF_MISC); ++ ++ /* Set timing parameters to onfi mode 0 for probing */ ++ timings = onfi_async_timing_mode_to_sdr_timings(0); ++ if (IS_ERR(timings)) ++ return PTR_ERR(timings); ++ rc = set_default_timings(tn, timings); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++static int cvm_nfc_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct device *dev = &pdev->dev; ++ struct cvm_nfc *tn; ++ int ret; ++ ++#if IS_ENABLED(CONFIG_CAVIUM_BCH) ++ bch_vf = cavm_bch_getv(); ++ if (IS_ERR(bch_vf)) ++ return PTR_ERR(bch_vf); ++#endif ++ ++ tn = devm_kzalloc(dev, sizeof(*tn), GFP_KERNEL); ++ if (!tn) ++ return -ENOMEM; ++ ++ tn->dev = dev; ++ spin_lock_init(&tn->controller.lock); ++ init_waitqueue_head(&tn->controller.wq); ++ INIT_LIST_HEAD(&tn->chips); ++ ++ pci_set_drvdata(pdev, tn); ++ ret = pcim_enable_device(pdev); ++ if (ret) ++ return ret; ++ ret = pci_request_regions(pdev, KBUILD_MODNAME); ++ if (ret) ++ return ret; ++ tn->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); ++ if (!tn->base) { ++ ret = -EINVAL; ++ goto release; ++ } ++ ++ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); ++ if (ret < 0) ++ goto release; ++ ++ tn->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(tn->clk)) { ++ ret = PTR_ERR(tn->clk); ++ goto release; ++ } ++ ++ ret = clk_prepare_enable(tn->clk); ++ if (ret) ++ goto release; ++ ++ if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) ++ dev_err(dev, "64 bit DMA mask not available\n"); ++ ++ tn->buf.dmabuflen = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; ++ tn->buf.dmabuf = dma_alloc_coherent(dev, tn->buf.dmabuflen, ++ &tn->buf.dmaaddr, GFP_KERNEL); ++ if (!tn->buf.dmabuf) { ++ ret = -ENOMEM; ++ goto unclk; ++ } ++ ++ /* one hw-bch response, for one outstanding transaction */ ++ tn->bch_resp = dma_alloc_coherent(dev, sizeof(*tn->bch_resp), ++ &tn->bch_rhandle, GFP_KERNEL); ++ ++ tn->stat = dma_alloc_coherent(dev, 8, &tn->stat_addr, GFP_KERNEL); ++ if (!tn->stat) { ++ ret = -ENOMEM; ++ goto unclk; ++ } ++ ++ ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), ++ cvm_nfc_isr, 0, "nand-flash-controller", tn); ++ if (ret) ++ goto unclk; ++ ++ cvm_nfc_init(tn); ++ ret = cvm_nfc_chips_init(tn); ++ if (ret) { ++ dev_err(dev, "failed to init nand chips\n"); ++ goto unclk; ++ } ++ dev_info(dev, "probed\n"); ++ return 0; ++ ++unclk: ++ clk_disable_unprepare(tn->clk); ++release: ++ pci_release_regions(pdev); ++ pci_set_drvdata(pdev, NULL); ++ return ret; ++} ++ ++static void cvm_nfc_remove(struct pci_dev *pdev) ++{ ++ struct cvm_nfc *tn = pci_get_drvdata(pdev); ++ struct cvm_nand_chip *chip; ++ ++ if (!tn) ++ return; ++ ++ while (!list_empty(&tn->chips)) { ++ chip = list_first_entry(&tn->chips, struct cvm_nand_chip, ++ node); ++ nand_release(&chip->nand.mtd); ++ list_del(&chip->node); ++ } ++ clk_disable_unprepare(tn->clk); ++ pci_release_regions(pdev); ++ ++#if IS_ENABLED(CONFIG_CAVIUM_BCH) ++ if (bch_vf) ++ cavm_bch_putv(bch_vf); ++#endif ++ ++ pci_set_drvdata(pdev, NULL); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int cvm_nfc_suspend(struct pci_dev *pdev, pm_message_t unused) ++{ ++ struct cvm_nfc *tn = pci_get_drvdata(pdev); ++ struct cvm_nand_chip *chip; ++ ++ list_for_each_entry(chip, &tn->chips, node) ++ chip->iface_set = false; ++ clk_disable_unprepare(tn->clk); ++ ++ return 0; ++} ++ ++static int cvm_nfc_resume(struct pci_dev *pdev) ++{ ++ struct cvm_nfc *tn = pci_get_drvdata(pdev); ++ int ret = clk_prepare_enable(tn->clk); ++ ++ if (ret) { ++ dev_err(tn->dev, "failed to enable clk\n"); ++ return ret; ++ } ++ ++ /* can some of this be skipped, or refactored... */ ++ cvm_nfc_init(tn); ++ ret = cvm_nfc_chips_init(tn); ++ if (ret) { ++ dev_err(tn->dev, "failed to resume nand chips\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct pci_device_id cvm_nfc_pci_id_table[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa04f) }, ++ { 0, } ++}; ++ ++MODULE_DEVICE_TABLE(pci, cvm_nfc_pci_id_table); ++ ++static struct pci_driver cvm_nfc_pci_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = cvm_nfc_pci_id_table, ++ .probe = cvm_nfc_probe, ++ .remove = cvm_nfc_remove, ++#ifdef CONFIG_PM_SLEEP ++ .suspend = cvm_nfc_suspend, ++ .resume = cvm_nfc_resume, ++#endif ++}; ++ ++module_pci_driver(cvm_nfc_pci_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jan Glauber "); ++MODULE_DESCRIPTION("Cavium Inc. cvm NAND driver"); +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index 5fb4516..e77c265 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -1207,178 +1207,6 @@ int nand_reset(struct nand_chip *chip, int chipnr) + } + + /** +- * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks +- * @mtd: mtd info +- * @ofs: offset to start unlock from +- * @len: length to unlock +- * @invert: when = 0, unlock the range of blocks within the lower and +- * upper boundary address +- * when = 1, unlock the range of blocks outside the boundaries +- * of the lower and upper boundary address +- * +- * Returs unlock status. +- */ +-static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, +- uint64_t len, int invert) +-{ +- int ret = 0; +- int status, page; +- struct nand_chip *chip = mtd_to_nand(mtd); +- +- /* Submit address of first page to unlock */ +- page = ofs >> chip->page_shift; +- chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); +- +- /* Submit address of last page to unlock */ +- page = (ofs + len) >> chip->page_shift; +- chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, +- (page | invert) & chip->pagemask); +- +- /* Call wait ready function */ +- status = chip->waitfunc(mtd, chip); +- /* See if device thinks it succeeded */ +- if (status & NAND_STATUS_FAIL) { +- pr_debug("%s: error status = 0x%08x\n", +- __func__, status); +- ret = -EIO; +- } +- +- return ret; +-} +- +-/** +- * nand_unlock - [REPLACEABLE] unlocks specified locked blocks +- * @mtd: mtd info +- * @ofs: offset to start unlock from +- * @len: length to unlock +- * +- * Returns unlock status. +- */ +-int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +-{ +- int ret = 0; +- int chipnr; +- struct nand_chip *chip = mtd_to_nand(mtd); +- +- pr_debug("%s: start = 0x%012llx, len = %llu\n", +- __func__, (unsigned long long)ofs, len); +- +- if (check_offs_len(mtd, ofs, len)) +- return -EINVAL; +- +- /* Align to last block address if size addresses end of the device */ +- if (ofs + len == mtd->size) +- len -= mtd->erasesize; +- +- nand_get_device(mtd, FL_UNLOCKING); +- +- /* Shift to get chip number */ +- chipnr = ofs >> chip->chip_shift; +- +- /* +- * Reset the chip. +- * If we want to check the WP through READ STATUS and check the bit 7 +- * we must reset the chip +- * some operation can also clear the bit 7 of status register +- * eg. erase/program a locked block +- */ +- nand_reset(chip, chipnr); +- +- chip->select_chip(mtd, chipnr); +- +- /* Check, if it is write protected */ +- if (nand_check_wp(mtd)) { +- pr_debug("%s: device is write protected!\n", +- __func__); +- ret = -EIO; +- goto out; +- } +- +- ret = __nand_unlock(mtd, ofs, len, 0); +- +-out: +- chip->select_chip(mtd, -1); +- nand_release_device(mtd); +- +- return ret; +-} +-EXPORT_SYMBOL(nand_unlock); +- +-/** +- * nand_lock - [REPLACEABLE] locks all blocks present in the device +- * @mtd: mtd info +- * @ofs: offset to start unlock from +- * @len: length to unlock +- * +- * This feature is not supported in many NAND parts. 'Micron' NAND parts do +- * have this feature, but it allows only to lock all blocks, not for specified +- * range for block. Implementing 'lock' feature by making use of 'unlock', for +- * now. +- * +- * Returns lock status. +- */ +-int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +-{ +- int ret = 0; +- int chipnr, status, page; +- struct nand_chip *chip = mtd_to_nand(mtd); +- +- pr_debug("%s: start = 0x%012llx, len = %llu\n", +- __func__, (unsigned long long)ofs, len); +- +- if (check_offs_len(mtd, ofs, len)) +- return -EINVAL; +- +- nand_get_device(mtd, FL_LOCKING); +- +- /* Shift to get chip number */ +- chipnr = ofs >> chip->chip_shift; +- +- /* +- * Reset the chip. +- * If we want to check the WP through READ STATUS and check the bit 7 +- * we must reset the chip +- * some operation can also clear the bit 7 of status register +- * eg. erase/program a locked block +- */ +- nand_reset(chip, chipnr); +- +- chip->select_chip(mtd, chipnr); +- +- /* Check, if it is write protected */ +- if (nand_check_wp(mtd)) { +- pr_debug("%s: device is write protected!\n", +- __func__); +- status = MTD_ERASE_FAILED; +- ret = -EIO; +- goto out; +- } +- +- /* Submit address of first page to lock */ +- page = ofs >> chip->page_shift; +- chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); +- +- /* Call wait ready function */ +- status = chip->waitfunc(mtd, chip); +- /* See if device thinks it succeeded */ +- if (status & NAND_STATUS_FAIL) { +- pr_debug("%s: error status = 0x%08x\n", +- __func__, status); +- ret = -EIO; +- goto out; +- } +- +- ret = __nand_unlock(mtd, ofs, len, 0x1); +- +-out: +- chip->select_chip(mtd, -1); +- nand_release_device(mtd); +- +- return ret; +-} +-EXPORT_SYMBOL(nand_lock); +- +-/** + * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data + * @buf: buffer to test + * @len: buffer length +@@ -2703,7 +2531,6 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write +- * @cached: cached programming + * @raw: use _raw version of write_page + */ + static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, +@@ -4568,6 +4395,226 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) + } + } + ++/** ++ * nand_check_ecc_caps - check the sanity of preset ECC settings ++ * @chip: nand chip info structure ++ * @caps: ECC caps info structure ++ * @oobavail: OOB size that the ECC engine can use ++ * ++ * When ECC step size and strength are already set, check if they are supported ++ * by the controller and the calculated ECC bytes fit within the chip's OOB. ++ * On success, the calculated ECC bytes is set. ++ */ ++int nand_check_ecc_caps(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_ecc_step_info *stepinfo; ++ int preset_step = chip->ecc.size; ++ int preset_strength = chip->ecc.strength; ++ int nsteps, ecc_bytes; ++ int i, j; ++ ++ if (WARN_ON(oobavail < 0)) ++ return -EINVAL; ++ ++ if (!preset_step || !preset_strength) ++ return -ENODATA; ++ ++ nsteps = mtd->writesize / preset_step; ++ ++ for (i = 0; i < caps->nstepinfos; i++) { ++ stepinfo = &caps->stepinfos[i]; ++ ++ if (stepinfo->stepsize != preset_step) ++ continue; ++ ++ for (j = 0; j < stepinfo->nstrengths; j++) { ++ if (stepinfo->strengths[j] != preset_strength) ++ continue; ++ ++ ecc_bytes = caps->calc_ecc_bytes(preset_step, ++ preset_strength); ++ if (WARN_ON_ONCE(ecc_bytes < 0)) ++ return ecc_bytes; ++ ++ if (ecc_bytes * nsteps > oobavail) { ++ pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", ++ preset_step, preset_strength); ++ return -ENOSPC; ++ } ++ ++ chip->ecc.bytes = ecc_bytes; ++ ++ return 0; ++ } ++ } ++ ++ pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", ++ preset_step, preset_strength); ++ ++ return -ENOTSUPP; ++} ++EXPORT_SYMBOL_GPL(nand_check_ecc_caps); ++ ++/** ++ * nand_match_ecc_req - meet the chip's requirement with least ECC bytes ++ * @chip: nand chip info structure ++ * @caps: ECC engine caps info structure ++ * @oobavail: OOB size that the ECC engine can use ++ * ++ * If a chip's ECC requirement is provided, try to meet it with the least ++ * number of ECC bytes (i.e. with the largest number of OOB-free bytes). ++ * On success, the chosen ECC settings are set. ++ */ ++int nand_match_ecc_req(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_ecc_step_info *stepinfo; ++ int req_step = chip->ecc_step_ds; ++ int req_strength = chip->ecc_strength_ds; ++ int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; ++ int best_step, best_strength, best_ecc_bytes; ++ int best_ecc_bytes_total = INT_MAX; ++ int i, j; ++ ++ if (WARN_ON(oobavail < 0)) ++ return -EINVAL; ++ ++ /* No information provided by the NAND chip */ ++ if (!req_step || !req_strength) ++ return -ENOTSUPP; ++ ++ /* number of correctable bits the chip requires in a page */ ++ req_corr = mtd->writesize / req_step * req_strength; ++ ++ for (i = 0; i < caps->nstepinfos; i++) { ++ stepinfo = &caps->stepinfos[i]; ++ step_size = stepinfo->stepsize; ++ ++ for (j = 0; j < stepinfo->nstrengths; j++) { ++ strength = stepinfo->strengths[j]; ++ ++ /* ++ * If both step size and strength are smaller than the ++ * chip's requirement, it is not easy to compare the ++ * resulted reliability. ++ */ ++ if (step_size < req_step && strength < req_strength) ++ continue; ++ ++ if (mtd->writesize % step_size) ++ continue; ++ ++ nsteps = mtd->writesize / step_size; ++ ++ ecc_bytes = caps->calc_ecc_bytes(step_size, strength); ++ if (WARN_ON_ONCE(ecc_bytes < 0)) ++ continue; ++ ecc_bytes_total = ecc_bytes * nsteps; ++ ++ if (ecc_bytes_total > oobavail || ++ strength * nsteps < req_corr) ++ continue; ++ ++ /* ++ * We assume the best is to meet the chip's requrement ++ * with the least number of ECC bytes. ++ */ ++ if (ecc_bytes_total < best_ecc_bytes_total) { ++ best_ecc_bytes_total = ecc_bytes_total; ++ best_step = step_size; ++ best_strength = strength; ++ best_ecc_bytes = ecc_bytes; ++ } ++ } ++ } ++ ++ if (best_ecc_bytes_total == INT_MAX) ++ return -ENOTSUPP; ++ ++ chip->ecc.size = best_step; ++ chip->ecc.strength = best_strength; ++ chip->ecc.bytes = best_ecc_bytes; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_match_ecc_req); ++ ++/** ++ * nand_maximize_ecc - choose the max ECC strength available ++ * @chip: nand chip info structure ++ * @caps: ECC engine caps info structure ++ * @oobavail: OOB size that the ECC engine can use ++ * ++ * Choose the max ECC strength that is supported on the controller, and can fit ++ * within the chip's OOB. On success, the chosen ECC settings are set. ++ */ ++int nand_maximize_ecc(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_ecc_step_info *stepinfo; ++ int step_size, strength, nsteps, ecc_bytes, corr; ++ int best_corr = 0; ++ int best_step = 0; ++ int best_strength, best_ecc_bytes; ++ int i, j; ++ ++ if (WARN_ON(oobavail < 0)) ++ return -EINVAL; ++ ++ for (i = 0; i < caps->nstepinfos; i++) { ++ stepinfo = &caps->stepinfos[i]; ++ step_size = stepinfo->stepsize; ++ ++ /* If chip->ecc.size is already set, respect it */ ++ if (chip->ecc.size && step_size != chip->ecc.size) ++ continue; ++ ++ for (j = 0; j < stepinfo->nstrengths; j++) { ++ strength = stepinfo->strengths[j]; ++ ++ if (mtd->writesize % step_size) ++ continue; ++ ++ nsteps = mtd->writesize / step_size; ++ ++ ecc_bytes = caps->calc_ecc_bytes(step_size, strength); ++ if (WARN_ON_ONCE(ecc_bytes < 0)) ++ continue; ++ ++ if (ecc_bytes * nsteps > oobavail) ++ continue; ++ ++ corr = strength * nsteps; ++ ++ /* ++ * If the number of correctable bits is the same, ++ * bigger step_size has more reliability. ++ */ ++ if (corr > best_corr || ++ (corr == best_corr && step_size > best_step)) { ++ best_corr = corr; ++ best_step = step_size; ++ best_strength = strength; ++ best_ecc_bytes = ecc_bytes; ++ } ++ } ++ } ++ ++ if (!best_corr) ++ return -ENOTSUPP; ++ ++ chip->ecc.size = best_step; ++ chip->ecc.strength = best_strength; ++ chip->ecc.bytes = best_ecc_bytes; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_maximize_ecc); ++ + /* + * Check if the chip configuration meet the datasheet requirements. + +diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c +index 13a5874..7e36d7d1 100644 +--- a/drivers/mtd/nand/nand_timings.c ++++ b/drivers/mtd/nand/nand_timings.c +@@ -18,6 +18,8 @@ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 20000, + .tALS_min = 50000, +@@ -58,6 +60,8 @@ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 25000, +@@ -98,6 +102,8 @@ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 15000, +@@ -138,6 +144,8 @@ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, +@@ -178,6 +186,8 @@ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, +@@ -218,6 +228,8 @@ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, +@@ -290,10 +302,22 @@ int onfi_init_data_interface(struct nand_chip *chip, + *iface = onfi_sdr_timings[timing_mode]; + + /* +- * TODO: initialize timings that cannot be deduced from timing mode: ++ * Initialize timings that cannot be deduced from timing mode: + * tR, tPROG, tCCS, ... + * These information are part of the ONFI parameter page. + */ ++ if (chip->onfi_version) { ++ struct nand_onfi_params *params = &chip->onfi_params; ++ struct nand_sdr_timings *timings = &iface->timings.sdr; ++ ++ /* microseconds -> picoseconds */ ++ timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); ++ timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); ++ timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); ++ ++ /* nanoseconds -> picoseconds */ ++ timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); ++ } + + return 0; + } +diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c +index 1eb9344..1ddf0b73 100644 +--- a/drivers/mtd/nand/nandsim.c ++++ b/drivers/mtd/nand/nandsim.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -286,11 +287,6 @@ + /* Maximum page cache pages needed to read or write a NAND page to the cache_file */ + #define NS_MAX_HELD_PAGES 16 + +-struct nandsim_debug_info { +- struct dentry *dfs_root; +- struct dentry *dfs_wear_report; +-}; +- + /* + * A union to represent flash memory contents and flash buffer. + */ +@@ -369,8 +365,6 @@ struct nandsim { + void *file_buf; + struct page *held_pages[NS_MAX_HELD_PAGES]; + int held_cnt; +- +- struct nandsim_debug_info dbg; + }; + + /* +@@ -523,44 +517,23 @@ static int nandsim_debugfs_open(struct inode *inode, struct file *file) + */ + static int nandsim_debugfs_create(struct nandsim *dev) + { +- struct nandsim_debug_info *dbg = &dev->dbg; ++ struct dentry *root = nsmtd->dbg.dfs_dir; + struct dentry *dent; +- int err; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + +- dent = debugfs_create_dir("nandsim", NULL); +- if (IS_ERR_OR_NULL(dent)) { +- int err = dent ? -ENODEV : PTR_ERR(dent); ++ if (IS_ERR_OR_NULL(root)) ++ return -1; + +- NS_ERR("cannot create \"nandsim\" debugfs directory, err %d\n", +- err); +- return err; ++ dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, ++ root, dev, &dfs_fops); ++ if (IS_ERR_OR_NULL(dent)) { ++ NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n"); ++ return -1; + } +- dbg->dfs_root = dent; +- +- dent = debugfs_create_file("wear_report", S_IRUSR, +- dbg->dfs_root, dev, &dfs_fops); +- if (IS_ERR_OR_NULL(dent)) +- goto out_remove; +- dbg->dfs_wear_report = dent; + + return 0; +- +-out_remove: +- debugfs_remove_recursive(dbg->dfs_root); +- err = dent ? PTR_ERR(dent) : -ENODEV; +- return err; +-} +- +-/** +- * nandsim_debugfs_remove - destroy all debugfs files +- */ +-static void nandsim_debugfs_remove(struct nandsim *ns) +-{ +- if (IS_ENABLED(CONFIG_DEBUG_FS)) +- debugfs_remove_recursive(ns->dbg.dfs_root); + } + + /* +@@ -906,7 +879,7 @@ static int parse_weakpages(void) + zero_ok = (*w == '0' ? 1 : 0); + page_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !page_no) { +- NS_ERR("invalid weakpagess.\n"); ++ NS_ERR("invalid weakpages.\n"); + return -EINVAL; + } + max_writes = 3; +@@ -1373,31 +1346,18 @@ static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t + return 0; + } + +-static int set_memalloc(void) +-{ +- if (current->flags & PF_MEMALLOC) +- return 0; +- current->flags |= PF_MEMALLOC; +- return 1; +-} +- +-static void clear_memalloc(int memalloc) +-{ +- if (memalloc) +- current->flags &= ~PF_MEMALLOC; +-} +- + static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) + { + ssize_t tx; +- int err, memalloc; ++ int err; ++ unsigned int noreclaim_flag; + + err = get_pages(ns, file, count, pos); + if (err) + return err; +- memalloc = set_memalloc(); ++ noreclaim_flag = memalloc_noreclaim_save(); + tx = kernel_read(file, pos, buf, count); +- clear_memalloc(memalloc); ++ memalloc_noreclaim_restore(noreclaim_flag); + put_pages(ns); + return tx; + } +@@ -1405,14 +1365,15 @@ static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_ + static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) + { + ssize_t tx; +- int err, memalloc; ++ int err; ++ unsigned int noreclaim_flag; + + err = get_pages(ns, file, count, pos); + if (err) + return err; +- memalloc = set_memalloc(); ++ noreclaim_flag = memalloc_noreclaim_save(); + tx = kernel_write(file, buf, count, pos); +- clear_memalloc(memalloc); ++ memalloc_noreclaim_restore(noreclaim_flag); + put_pages(ns); + return tx; + } +@@ -2313,8 +2274,6 @@ static int __init ns_init_module(void) + retval = nand_scan_ident(nsmtd, 1, NULL); + if (retval) { + NS_ERR("cannot scan NAND Simulator device\n"); +- if (retval > 0) +- retval = -ENXIO; + goto error; + } + +@@ -2350,8 +2309,6 @@ static int __init ns_init_module(void) + retval = nand_scan_tail(nsmtd); + if (retval) { + NS_ERR("can't register NAND Simulator\n"); +- if (retval > 0) +- retval = -ENXIO; + goto error; + } + +@@ -2372,9 +2329,6 @@ static int __init ns_init_module(void) + if ((retval = setup_wear_reporting(nsmtd)) != 0) + goto err_exit; + +- if ((retval = nandsim_debugfs_create(nand)) != 0) +- goto err_exit; +- + if ((retval = init_nandsim(nsmtd)) != 0) + goto err_exit; + +@@ -2390,6 +2344,9 @@ static int __init ns_init_module(void) + if (retval != 0) + goto err_exit; + ++ if ((retval = nandsim_debugfs_create(nand)) != 0) ++ goto err_exit; ++ + return 0; + + err_exit: +@@ -2415,7 +2372,6 @@ static void __exit ns_cleanup_module(void) + struct nandsim *ns = nand_get_controller_data(chip); + int i; + +- nandsim_debugfs_remove(ns); + free_nandsim(ns); /* Free nandsim private resources */ + nand_release(nsmtd); /* Unregister driver */ + for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) +diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c +index 3b8911c..97bae65 100644 +--- a/drivers/mtd/nand/pxa3xx_nand.c ++++ b/drivers/mtd/nand/pxa3xx_nand.c +@@ -30,6 +30,8 @@ + #include + #include + #include ++#include ++#include + + #define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) + #define NAND_STOP_DELAY msecs_to_jiffies(40) +@@ -45,6 +47,10 @@ + */ + #define INIT_BUFFER_SIZE 2048 + ++/* System control register and bit to enable NAND on some SoCs */ ++#define GENCONF_SOC_DEVICE_MUX 0x208 ++#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) ++ + /* registers and bit definitions */ + #define NDCR (0x00) /* Control register */ + #define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +@@ -174,6 +180,7 @@ enum { + enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, ++ PXA3XX_NAND_VARIANT_ARMADA_8K, + }; + + struct pxa3xx_nand_host { +@@ -425,6 +432,10 @@ static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section, + .compatible = "marvell,armada370-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, + }, ++ { ++ .compatible = "marvell,armada-8k-nand", ++ .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K, ++ }, + {} + }; + MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); +@@ -825,7 +836,8 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) + info->retcode = ERR_UNCORERR; + if (status & NDSR_CORERR) { + info->retcode = ERR_CORERR; +- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 && ++ if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) && + info->ecc_bch) + info->ecc_err_cnt = NDSR_ERR_CNT(status); + else +@@ -888,7 +900,8 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) + nand_writel(info, NDCB0, info->ndcb2); + + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ +- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) + nand_writel(info, NDCB0, info->ndcb3); + } + +@@ -1672,7 +1685,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) + chip->options |= NAND_BUSWIDTH_16; + + /* Device detection must be done with ECC disabled */ +- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) + nand_writel(info, NDECCCTRL, 0x0); + + if (pdata->flash_bbt) +@@ -1681,8 +1695,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) + chip->ecc.strength = pdata->ecc_strength; + chip->ecc.size = pdata->ecc_step_size; + +- if (nand_scan_ident(mtd, 1, NULL)) +- return -ENODEV; ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; + + if (!pdata->keep_config) { + ret = pxa3xx_nand_init(host); +@@ -1709,7 +1724,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) + * (aka splitted) command handling, + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { +- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) { ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) { + chip->cmdfunc = nand_cmdfunc_extended; + } else { + dev_err(&info->pdev->dev, +@@ -1775,8 +1791,11 @@ static int alloc_nand_resource(struct platform_device *pdev) + int ret, irq, cs; + + pdata = dev_get_platdata(&pdev->dev); +- if (pdata->num_cs <= 0) ++ if (pdata->num_cs <= 0) { ++ dev_err(&pdev->dev, "invalid number of chip selects\n"); + return -ENODEV; ++ } ++ + info = devm_kzalloc(&pdev->dev, + sizeof(*info) + sizeof(*host) * pdata->num_cs, + GFP_KERNEL); +@@ -1814,8 +1833,9 @@ static int alloc_nand_resource(struct platform_device *pdev) + nand_hw_control_init(chip->controller); + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) { +- dev_err(&pdev->dev, "failed to get nand clock\n"); +- return PTR_ERR(info->clk); ++ ret = PTR_ERR(info->clk); ++ dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret); ++ return ret; + } + ret = clk_prepare_enable(info->clk); + if (ret < 0) +@@ -1843,6 +1863,7 @@ static int alloc_nand_resource(struct platform_device *pdev) + info->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(info->mmio_base)) { + ret = PTR_ERR(info->mmio_base); ++ dev_err(&pdev->dev, "failed to map register space: %d\n", ret); + goto fail_disable_clk; + } + info->mmio_phys = r->start; +@@ -1862,7 +1883,7 @@ static int alloc_nand_resource(struct platform_device *pdev) + pxa3xx_nand_irq_thread, IRQF_ONESHOT, + pdev->name, info); + if (ret < 0) { +- dev_err(&pdev->dev, "failed to request IRQ\n"); ++ dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); + goto fail_free_buf; + } + +@@ -1921,6 +1942,24 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev) + if (!of_id) + return 0; + ++ /* ++ * Some SoCs like A7k/A8k need to enable manually the NAND ++ * controller to avoid being bootloader dependent. This is done ++ * through the use of a single bit in the System Functions registers. ++ */ ++ if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) { ++ struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle( ++ pdev->dev.of_node, "marvell,system-controller"); ++ u32 reg; ++ ++ if (IS_ERR(sysctrl_base)) ++ return PTR_ERR(sysctrl_base); ++ ++ regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ®); ++ reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN; ++ regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); ++ } ++ + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; +@@ -1961,10 +2000,8 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) + } + + ret = alloc_nand_resource(pdev); +- if (ret) { +- dev_err(&pdev->dev, "alloc nand resource failed\n"); ++ if (ret) + return ret; +- } + + info = platform_get_drvdata(pdev); + probe_success = 0; +diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig +new file mode 100644 +index 00000000..e6b8c59 +--- /dev/null ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -0,0 +1,580 @@ ++config MTD_NAND_ECC ++ tristate ++ ++config MTD_NAND_ECC_SMC ++ bool "NAND ECC Smart Media byte order" ++ depends on MTD_NAND_ECC ++ default n ++ help ++ Software ECC according to the Smart Media Specification. ++ The original Linux implementation had byte 0 and 1 swapped. ++ ++ ++menuconfig MTD_NAND ++ tristate "NAND Device Support" ++ depends on MTD ++ select MTD_NAND_ECC ++ help ++ This enables support for accessing all type of NAND flash ++ devices. For further information see ++ . ++ ++if MTD_NAND ++ ++config MTD_NAND_BCH ++ tristate ++ select BCH ++ depends on MTD_NAND_ECC_BCH ++ default MTD_NAND ++ ++config MTD_NAND_ECC_BCH ++ bool "Support software BCH ECC" ++ default n ++ help ++ This enables support for software BCH error correction. Binary BCH ++ codes are more powerful and cpu intensive than traditional Hamming ++ ECC codes. They are used with NAND devices requiring more than 1 bit ++ of error correction. ++ ++config MTD_SM_COMMON ++ tristate ++ default n ++ ++config MTD_NAND_DENALI ++ tristate ++ ++config MTD_NAND_DENALI_PCI ++ tristate "Support Denali NAND controller on Intel Moorestown" ++ select MTD_NAND_DENALI ++ depends on HAS_DMA && PCI ++ help ++ Enable the driver for NAND flash on Intel Moorestown, using the ++ Denali NAND controller core. ++ ++config MTD_NAND_DENALI_DT ++ tristate "Support Denali NAND controller as a DT device" ++ select MTD_NAND_DENALI ++ depends on HAS_DMA && HAVE_CLK && OF ++ help ++ Enable the driver for NAND flash on platforms using a Denali NAND ++ controller as a DT device. ++ ++config MTD_NAND_GPIO ++ tristate "GPIO assisted NAND Flash driver" ++ depends on GPIOLIB || COMPILE_TEST ++ depends on HAS_IOMEM ++ help ++ This enables a NAND flash driver where control signals are ++ connected to GPIO pins, and commands and data are communicated ++ via a memory mapped interface. ++ ++config MTD_NAND_AMS_DELTA ++ tristate "NAND Flash device on Amstrad E3" ++ depends on MACH_AMS_DELTA ++ default y ++ help ++ Support for NAND flash on Amstrad E3 (Delta). ++ ++config MTD_NAND_OMAP2 ++ tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone" ++ depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE) ++ help ++ Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 ++ and Keystone platforms. ++ ++config MTD_NAND_OMAP_BCH ++ depends on MTD_NAND_OMAP2 ++ bool "Support hardware based BCH error correction" ++ default n ++ select BCH ++ help ++ This config enables the ELM hardware engine, which can be used to ++ locate and correct errors when using BCH ECC scheme. This offloads ++ the cpu from doing ECC error searching and correction. However some ++ legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine ++ so this is optional for them. ++ ++config MTD_NAND_OMAP_BCH_BUILD ++ def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH ++ ++config MTD_NAND_RICOH ++ tristate "Ricoh xD card reader" ++ default n ++ depends on PCI ++ select MTD_SM_COMMON ++ help ++ Enable support for Ricoh R5C852 xD card reader ++ You also need to enable ether ++ NAND SSFDC (SmartMedia) read only translation layer' or new ++ expermental, readwrite ++ 'SmartMedia/xD new translation layer' ++ ++config MTD_NAND_AU1550 ++ tristate "Au1550/1200 NAND support" ++ depends on MIPS_ALCHEMY ++ help ++ This enables the driver for the NAND flash controller on the ++ AMD/Alchemy 1550 SOC. ++ ++config MTD_NAND_BF5XX ++ tristate "Blackfin on-chip NAND Flash Controller driver" ++ depends on BF54x || BF52x ++ help ++ This enables the Blackfin on-chip NAND flash controller ++ ++ No board specific support is done by this driver, each board ++ must advertise a platform_device for the driver to attach. ++ ++ This driver can also be built as a module. If so, the module ++ will be called bf5xx-nand. ++ ++config MTD_NAND_BF5XX_HWECC ++ bool "BF5XX NAND Hardware ECC" ++ default y ++ depends on MTD_NAND_BF5XX ++ help ++ Enable the use of the BF5XX's internal ECC generator when ++ using NAND. ++ ++config MTD_NAND_BF5XX_BOOTROM_ECC ++ bool "Use Blackfin BootROM ECC Layout" ++ default n ++ depends on MTD_NAND_BF5XX_HWECC ++ help ++ If you wish to modify NAND pages and allow the Blackfin on-chip ++ BootROM to boot from them, say Y here. This is only necessary ++ if you are booting U-Boot out of NAND and you wish to update ++ U-Boot from Linux' userspace. Otherwise, you should say N here. ++ ++ If unsure, say N. ++ ++config MTD_NAND_S3C2410 ++ tristate "NAND Flash support for Samsung S3C SoCs" ++ depends on ARCH_S3C24XX || ARCH_S3C64XX ++ help ++ This enables the NAND flash controller on the S3C24xx and S3C64xx ++ SoCs ++ ++ No board specific support is done by this driver, each board ++ must advertise a platform_device for the driver to attach. ++ ++config MTD_NAND_S3C2410_DEBUG ++ bool "Samsung S3C NAND driver debug" ++ depends on MTD_NAND_S3C2410 ++ help ++ Enable debugging of the S3C NAND driver ++ ++config MTD_NAND_NDFC ++ tristate "NDFC NanD Flash Controller" ++ depends on 4xx ++ select MTD_NAND_ECC_SMC ++ help ++ NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs ++ ++config MTD_NAND_S3C2410_CLKSTOP ++ bool "Samsung S3C NAND IDLE clock stop" ++ depends on MTD_NAND_S3C2410 ++ default n ++ help ++ Stop the clock to the NAND controller when there is no chip ++ selected to save power. This will mean there is a small delay ++ when the is NAND chip selected or released, but will save ++ approximately 5mA of power when there is nothing happening. ++ ++config MTD_NAND_TANGO ++ tristate "NAND Flash support for Tango chips" ++ depends on ARCH_TANGO || COMPILE_TEST ++ depends on HAS_DMA ++ help ++ Enables the NAND Flash controller on Tango chips. ++ ++config MTD_NAND_DISKONCHIP ++ tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)" ++ depends on HAS_IOMEM ++ select REED_SOLOMON ++ select REED_SOLOMON_DEC16 ++ help ++ This is a reimplementation of M-Systems DiskOnChip 2000, ++ Millennium and Millennium Plus as a standard NAND device driver, ++ as opposed to the earlier self-contained MTD device drivers. ++ This should enable, among other things, proper JFFS2 operation on ++ these devices. ++ ++config MTD_NAND_DISKONCHIP_PROBE_ADVANCED ++ bool "Advanced detection options for DiskOnChip" ++ depends on MTD_NAND_DISKONCHIP ++ help ++ This option allows you to specify nonstandard address at which to ++ probe for a DiskOnChip, or to change the detection options. You ++ are unlikely to need any of this unless you are using LinuxBIOS. ++ Say 'N'. ++ ++config MTD_NAND_DISKONCHIP_PROBE_ADDRESS ++ hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED ++ depends on MTD_NAND_DISKONCHIP ++ default "0" ++ ---help--- ++ By default, the probe for DiskOnChip devices will look for a ++ DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. ++ This option allows you to specify a single address at which to probe ++ for the device, which is useful if you have other devices in that ++ range which get upset when they are probed. ++ ++ (Note that on PowerPC, the normal probe will only check at ++ 0xE4000000.) ++ ++ Normally, you should leave this set to zero, to allow the probe at ++ the normal addresses. ++ ++config MTD_NAND_DISKONCHIP_PROBE_HIGH ++ bool "Probe high addresses" ++ depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED ++ help ++ By default, the probe for DiskOnChip devices will look for a ++ DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. ++ This option changes to make it probe between 0xFFFC8000 and ++ 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be ++ useful to you. Say 'N'. ++ ++config MTD_NAND_DISKONCHIP_BBTWRITE ++ bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP" ++ depends on MTD_NAND_DISKONCHIP ++ help ++ On DiskOnChip devices shipped with the INFTL filesystem (Millennium ++ and 2000 TSOP/Alon), Linux reserves some space at the end of the ++ device for the Bad Block Table (BBT). If you have existing INFTL ++ data on your device (created by non-Linux tools such as M-Systems' ++ DOS drivers), your data might overlap the area Linux wants to use for ++ the BBT. If this is a concern for you, leave this option disabled and ++ Linux will not write BBT data into this area. ++ The downside of leaving this option disabled is that if bad blocks ++ are detected by Linux, they will not be recorded in the BBT, which ++ could cause future problems. ++ Once you enable this option, new filesystems (INFTL or others, created ++ in Linux or other operating systems) will not use the reserved area. ++ The only reason not to enable this option is to prevent damage to ++ preexisting filesystems. ++ Even if you leave this disabled, you can enable BBT writes at module ++ load time (assuming you build diskonchip as a module) with the module ++ parameter "inftl_bbt_write=1". ++ ++config MTD_NAND_DOCG4 ++ tristate "Support for DiskOnChip G4" ++ depends on HAS_IOMEM ++ select BCH ++ select BITREVERSE ++ help ++ Support for diskonchip G4 nand flash, found in various smartphones and ++ PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba ++ Portege G900, Asus P526, and O2 XDA Zinc. ++ ++ With this driver you will be able to use UBI and create a ubifs on the ++ device, so you may wish to consider enabling UBI and UBIFS as well. ++ ++ These devices ship with the Mys/Sandisk SAFTL formatting, for which ++ there is currently no mtd parser, so you may want to use command line ++ partitioning to segregate write-protected blocks. On the Treo680, the ++ first five erase blocks (256KiB each) are write-protected, followed ++ by the block containing the saftl partition table. This is probably ++ typical. ++ ++config MTD_NAND_SHARPSL ++ tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" ++ depends on ARCH_PXA ++ ++config MTD_NAND_CAFE ++ tristate "NAND support for OLPC CAFÉ chip" ++ depends on PCI ++ select REED_SOLOMON ++ select REED_SOLOMON_DEC16 ++ help ++ Use NAND flash attached to the CAFÉ chip designed for the OLPC ++ laptop. ++ ++config MTD_NAND_CS553X ++ tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" ++ depends on X86_32 ++ depends on !UML && HAS_IOMEM ++ help ++ The CS553x companion chips for the AMD Geode processor ++ include NAND flash controllers with built-in hardware ECC ++ capabilities; enabling this option will allow you to use ++ these. The driver will check the MSRs to verify that the ++ controller is enabled for NAND, and currently requires that ++ the controller be in MMIO mode. ++ ++ If you say "m", the module will be called cs553x_nand. ++ ++config MTD_NAND_ATMEL ++ tristate "Support for NAND Flash / SmartMedia on AT91" ++ depends on ARCH_AT91 ++ select MFD_ATMEL_SMC ++ help ++ Enables support for NAND Flash / Smart Media Card interface ++ on Atmel AT91 processors. ++ ++config MTD_NAND_PXA3xx ++ tristate "NAND support on PXA3xx and Armada 370/XP" ++ depends on !MTD_NAND_MARVELL ++ depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU ++ help ++ ++ This enables the driver for the NAND flash device found on ++ PXA3xx processors (NFCv1) and also on 32-bit Armada ++ platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada ++ platforms (7K, 8K) (NFCv2). ++ ++config MTD_NAND_MARVELL ++ tristate "NAND controller support on Marvell boards" ++ depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ ++ COMPILE_TEST ++ depends on HAS_IOMEM ++ help ++ This enables the NAND flash controller driver for Marvell boards, ++ including: ++ - PXA3xx processors (NFCv1) ++ - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2) ++ - 64-bit Aramda platforms (7k, 8k) (NFCv2) ++ ++config MTD_NAND_SLC_LPC32XX ++ tristate "NXP LPC32xx SLC Controller" ++ depends on ARCH_LPC32XX ++ help ++ Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell ++ chips) NAND controller. This is the default for the PHYTEC 3250 ++ reference board which contains a NAND256R3A2CZA6 chip. ++ ++ Please check the actual NAND chip connected and its support ++ by the SLC NAND controller. ++ ++config MTD_NAND_MLC_LPC32XX ++ tristate "NXP LPC32xx MLC Controller" ++ depends on ARCH_LPC32XX ++ help ++ Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND ++ controller. This is the default for the WORK92105 controller ++ board. ++ ++ Please check the actual NAND chip connected and its support ++ by the MLC NAND controller. ++ ++config MTD_NAND_CM_X270 ++ tristate "Support for NAND Flash on CM-X270 modules" ++ depends on MACH_ARMCORE ++ ++config MTD_NAND_PASEMI ++ tristate "NAND support for PA Semi PWRficient" ++ depends on PPC_PASEMI ++ help ++ Enables support for NAND Flash interface on PA Semi PWRficient ++ based boards ++ ++config MTD_NAND_TMIO ++ tristate "NAND Flash device on Toshiba Mobile IO Controller" ++ depends on MFD_TMIO ++ help ++ Support for NAND flash connected to a Toshiba Mobile IO ++ Controller in some PDAs, including the Sharp SL6000x. ++ ++config MTD_NAND_NANDSIM ++ tristate "Support for NAND Flash Simulator" ++ help ++ The simulator may simulate various NAND flash chips for the ++ MTD nand layer. ++ ++config MTD_NAND_GPMI_NAND ++ tristate "GPMI NAND Flash Controller driver" ++ depends on MTD_NAND && MXS_DMA ++ help ++ Enables NAND Flash support for IMX23, IMX28 or IMX6. ++ The GPMI controller is very powerful, with the help of BCH ++ module, it can do the hardware ECC. The GPMI supports several ++ NAND flashs at the same time. ++ ++config MTD_NAND_BRCMNAND ++ tristate "Broadcom STB NAND controller" ++ depends on ARM || ARM64 || MIPS ++ help ++ Enables the Broadcom NAND controller driver. The controller was ++ originally designed for Set-Top Box but is used on various BCM7xxx, ++ BCM3xxx, BCM63xxx, iProc/Cygnus and more. ++ ++config MTD_NAND_BCM47XXNFLASH ++ tristate "Support for NAND flash on BCM4706 BCMA bus" ++ depends on BCMA_NFLASH ++ help ++ BCMA bus can have various flash memories attached, they are ++ registered by bcma as platform devices. This enables driver for ++ NAND flash memories. For now only BCM4706 is supported. ++ ++config MTD_NAND_PLATFORM ++ tristate "Support for generic platform NAND driver" ++ depends on HAS_IOMEM ++ help ++ This implements a generic NAND driver for on-SOC platform ++ devices. You will need to provide platform-specific functions ++ via platform_data. ++ ++config MTD_NAND_ORION ++ tristate "NAND Flash support for Marvell Orion SoC" ++ depends on PLAT_ORION ++ help ++ This enables the NAND flash controller on Orion machines. ++ ++ No board specific support is done by this driver, each board ++ must advertise a platform_device for the driver to attach. ++ ++config MTD_NAND_OXNAS ++ tristate "NAND Flash support for Oxford Semiconductor SoC" ++ depends on ARCH_OXNAS || COMPILE_TEST ++ depends on HAS_IOMEM ++ help ++ This enables the NAND flash controller on Oxford Semiconductor SoCs. ++ ++config MTD_NAND_FSL_ELBC ++ tristate "NAND support for Freescale eLBC controllers" ++ depends on FSL_SOC ++ select FSL_LBC ++ help ++ Various Freescale chips, including the 8313, include a NAND Flash ++ Controller Module with built-in hardware ECC capabilities. ++ Enabling this option will enable you to use this to control ++ external NAND devices. ++ ++config MTD_NAND_FSL_IFC ++ tristate "NAND support for Freescale IFC controller" ++ depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A ++ select FSL_IFC ++ select MEMORY ++ help ++ Various Freescale chips e.g P1010, include a NAND Flash machine ++ with built-in hardware ECC capabilities. ++ Enabling this option will enable you to use this to control ++ external NAND devices. ++ ++config MTD_NAND_FSL_UPM ++ tristate "Support for NAND on Freescale UPM" ++ depends on PPC_83xx || PPC_85xx ++ select FSL_LBC ++ help ++ Enables support for NAND Flash chips wired onto Freescale PowerPC ++ processor localbus with User-Programmable Machine support. ++ ++config MTD_NAND_MPC5121_NFC ++ tristate "MPC5121 built-in NAND Flash Controller support" ++ depends on PPC_MPC512x ++ help ++ This enables the driver for the NAND flash controller on the ++ MPC5121 SoC. ++ ++config MTD_NAND_VF610_NFC ++ tristate "Support for Freescale NFC for VF610/MPC5125" ++ depends on (SOC_VF610 || COMPILE_TEST) ++ depends on HAS_IOMEM ++ help ++ Enables support for NAND Flash Controller on some Freescale ++ processors like the VF610, MPC5125, MCF54418 or Kinetis K70. ++ The driver supports a maximum 2k page size. With 2k pages and ++ 64 bytes or more of OOB, hardware ECC with up to 32-bit error ++ correction is supported. Hardware ECC is only enabled through ++ device tree. ++ ++config MTD_NAND_MXC ++ tristate "MXC NAND support" ++ depends on ARCH_MXC ++ help ++ This enables the driver for the NAND flash controller on the ++ MXC processors. ++ ++config MTD_NAND_SH_FLCTL ++ tristate "Support for NAND on Renesas SuperH FLCTL" ++ depends on SUPERH || COMPILE_TEST ++ depends on HAS_IOMEM ++ depends on HAS_DMA ++ help ++ Several Renesas SuperH CPU has FLCTL. This option enables support ++ for NAND Flash using FLCTL. ++ ++config MTD_NAND_DAVINCI ++ tristate "Support NAND on DaVinci/Keystone SoC" ++ depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) ++ help ++ Enable the driver for NAND flash chips on Texas Instruments ++ DaVinci/Keystone processors. ++ ++config MTD_NAND_TXX9NDFMC ++ tristate "NAND Flash support for TXx9 SoC" ++ depends on SOC_TX4938 || SOC_TX4939 ++ help ++ This enables the NAND flash controller on the TXx9 SoCs. ++ ++config MTD_NAND_SOCRATES ++ tristate "Support for NAND on Socrates board" ++ depends on SOCRATES ++ help ++ Enables support for NAND Flash chips wired onto Socrates board. ++ ++config MTD_NAND_NUC900 ++ tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." ++ depends on ARCH_W90X900 ++ help ++ This enables the driver for the NAND Flash on evaluation board based ++ on w90p910 / NUC9xx. ++ ++config MTD_NAND_JZ4740 ++ tristate "Support for JZ4740 SoC NAND controller" ++ depends on MACH_JZ4740 ++ help ++ Enables support for NAND Flash on JZ4740 SoC based boards. ++ ++config MTD_NAND_JZ4780 ++ tristate "Support for NAND on JZ4780 SoC" ++ depends on MACH_JZ4780 && JZ4780_NEMC ++ help ++ Enables support for NAND Flash connected to the NEMC on JZ4780 SoC ++ based boards, using the BCH controller for hardware error correction. ++ ++config MTD_NAND_FSMC ++ tristate "Support for NAND on ST Micros FSMC" ++ depends on OF ++ depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 ++ help ++ Enables support for NAND Flash chips on the ST Microelectronics ++ Flexible Static Memory Controller (FSMC) ++ ++config MTD_NAND_XWAY ++ bool "Support for NAND on Lantiq XWAY SoC" ++ depends on LANTIQ && SOC_TYPE_XWAY ++ help ++ Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached ++ to the External Bus Unit (EBU). ++ ++config MTD_NAND_SUNXI ++ tristate "Support for NAND on Allwinner SoCs" ++ depends on ARCH_SUNXI ++ help ++ Enables support for NAND Flash chips on Allwinner SoCs. ++ ++config MTD_NAND_HISI504 ++ tristate "Support for NAND controller on Hisilicon SoC Hip04" ++ depends on ARCH_HISI || COMPILE_TEST ++ depends on HAS_DMA ++ help ++ Enables support for NAND controller on Hisilicon SoC Hip04. ++ ++config MTD_NAND_QCOM ++ tristate "Support for NAND on QCOM SoCs" ++ depends on ARCH_QCOM ++ help ++ Enables support for NAND flash chips on SoCs containing the EBI2 NAND ++ controller. This controller is found on IPQ806x SoC. ++ ++config MTD_NAND_MTK ++ tristate "Support for NAND controller on MTK SoCs" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ depends on HAS_DMA ++ help ++ Enables support for NAND controller on MTK SoCs. ++ This controller is found on mt27xx, mt81xx, mt65xx SoCs. ++ ++endif # MTD_NAND +diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile +new file mode 100644 +index 00000000..4e09824 +--- /dev/null ++++ b/drivers/mtd/nand/raw/Makefile +@@ -0,0 +1,68 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++obj-$(CONFIG_MTD_NAND) += nand.o ++obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o ++obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o ++obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o ++ ++#obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o ++obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o ++obj-$(CONFIG_MTD_NAND_DENALI) += denali.o ++obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o ++obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o ++obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o ++obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o ++obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o ++obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o ++obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o ++obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o ++obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o ++obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o ++obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o ++#obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o ++obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o ++obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o ++obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/ ++obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o ++omap2_nand-objs := omap2.o ++obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o ++obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o ++obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o ++obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o ++obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o ++obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o ++obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o ++obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o ++obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o ++obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o ++obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o ++obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o ++obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o ++obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o ++obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o ++obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o ++obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o ++obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o ++obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o ++obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o ++obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o ++obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o ++obj-$(CONFIG_MTD_NAND_RICOH) += r852.o ++obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o ++obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o ++obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ ++obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o ++obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ ++obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o ++obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o ++obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ ++obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o ++obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o ++ ++nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o ++nand-objs += nand_amd.o ++nand-objs += nand_hynix.o ++nand-objs += nand_macronix.o ++nand-objs += nand_micron.o ++nand-objs += nand_samsung.o ++nand-objs += nand_toshiba.o +diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c +new file mode 100644 +index 00000000..dcec9cf +--- /dev/null ++++ b/drivers/mtd/nand/raw/ams-delta.c +@@ -0,0 +1,290 @@ ++/* ++ * drivers/mtd/nand/ams-delta.c ++ * ++ * Copyright (C) 2006 Jonathan McDowell ++ * ++ * Derived from drivers/mtd/toto.c ++ * Converted to platform driver by Janusz Krzysztofik ++ * Partially stolen from drivers/mtd/nand/plat_nand.c ++ * ++ * 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. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * Amstrad E3 (Delta). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++ ++/* ++ * MTD structure for E3 (Delta) ++ */ ++static struct mtd_info *ams_delta_mtd = NULL; ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info[] = { ++ { .name = "Kernel", ++ .offset = 0, ++ .size = 3 * SZ_1M + SZ_512K }, ++ { .name = "u-boot", ++ .offset = 3 * SZ_1M + SZ_512K, ++ .size = SZ_256K }, ++ { .name = "u-boot params", ++ .offset = 3 * SZ_1M + SZ_512K + SZ_256K, ++ .size = SZ_256K }, ++ { .name = "Amstrad LDR", ++ .offset = 4 * SZ_1M, ++ .size = SZ_256K }, ++ { .name = "File system", ++ .offset = 4 * SZ_1M + 1 * SZ_256K, ++ .size = 27 * SZ_1M }, ++ { .name = "PBL reserved", ++ .offset = 32 * SZ_1M - 3 * SZ_256K, ++ .size = 3 * SZ_256K }, ++}; ++ ++static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ void __iomem *io_base = (void __iomem *)nand_get_controller_data(this); ++ ++ writew(0, io_base + OMAP_MPUIO_IO_CNTL); ++ writew(byte, this->IO_ADDR_W); ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0); ++ ndelay(40); ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1); ++} ++ ++static u_char ams_delta_read_byte(struct mtd_info *mtd) ++{ ++ u_char res; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ void __iomem *io_base = (void __iomem *)nand_get_controller_data(this); ++ ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0); ++ ndelay(40); ++ writew(~0, io_base + OMAP_MPUIO_IO_CNTL); ++ res = readw(this->IO_ADDR_R); ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1); ++ ++ return res; ++} ++ ++static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf, ++ int len) ++{ ++ int i; ++ ++ for (i=0; i bit 2 ++ * NAND_CLE: bit 1 -> bit 7 ++ * NAND_ALE: bit 2 -> bit 6 ++ */ ++static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE, ++ (ctrl & NAND_NCE) == 0); ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE, ++ (ctrl & NAND_CLE) != 0); ++ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE, ++ (ctrl & NAND_ALE) != 0); ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ ams_delta_write_byte(mtd, cmd); ++} ++ ++static int ams_delta_nand_ready(struct mtd_info *mtd) ++{ ++ return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB); ++} ++ ++static const struct gpio _mandatory_gpio[] = { ++ { ++ .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE, ++ .flags = GPIOF_OUT_INIT_HIGH, ++ .label = "nand_nce", ++ }, ++ { ++ .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE, ++ .flags = GPIOF_OUT_INIT_HIGH, ++ .label = "nand_nre", ++ }, ++ { ++ .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP, ++ .flags = GPIOF_OUT_INIT_HIGH, ++ .label = "nand_nwp", ++ }, ++ { ++ .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE, ++ .flags = GPIOF_OUT_INIT_HIGH, ++ .label = "nand_nwe", ++ }, ++ { ++ .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE, ++ .flags = GPIOF_OUT_INIT_LOW, ++ .label = "nand_ale", ++ }, ++ { ++ .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE, ++ .flags = GPIOF_OUT_INIT_LOW, ++ .label = "nand_cle", ++ }, ++}; ++ ++/* ++ * Main initialization routine ++ */ ++static int ams_delta_init(struct platform_device *pdev) ++{ ++ struct nand_chip *this; ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ void __iomem *io_base; ++ int err = 0; ++ ++ if (!res) ++ return -ENXIO; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ++ if (!this) { ++ printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ ams_delta_mtd = nand_to_mtd(this); ++ ams_delta_mtd->owner = THIS_MODULE; ++ ++ /* ++ * Don't try to request the memory region from here, ++ * it should have been already requested from the ++ * gpio-omap driver and requesting it again would fail. ++ */ ++ ++ io_base = ioremap(res->start, resource_size(res)); ++ if (io_base == NULL) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ err = -EIO; ++ goto out_free; ++ } ++ ++ nand_set_controller_data(this, (void *)io_base); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH; ++ this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT; ++ this->read_byte = ams_delta_read_byte; ++ this->write_buf = ams_delta_write_buf; ++ this->read_buf = ams_delta_read_buf; ++ this->cmd_ctrl = ams_delta_hwcontrol; ++ if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) { ++ this->dev_ready = ams_delta_nand_ready; ++ } else { ++ this->dev_ready = NULL; ++ printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n"); ++ } ++ /* 25 us command delay time */ ++ this->chip_delay = 30; ++ this->ecc.mode = NAND_ECC_SOFT; ++ this->ecc.algo = NAND_ECC_HAMMING; ++ ++ platform_set_drvdata(pdev, io_base); ++ ++ /* Set chip enabled, but */ ++ err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); ++ if (err) ++ goto out_gpio; ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(ams_delta_mtd, 1); ++ if (err) ++ goto out_mtd; ++ ++ /* Register the partitions */ ++ mtd_device_register(ams_delta_mtd, partition_info, ++ ARRAY_SIZE(partition_info)); ++ ++ goto out; ++ ++ out_mtd: ++ gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); ++out_gpio: ++ gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); ++ iounmap(io_base); ++out_free: ++ kfree(this); ++ out: ++ return err; ++} ++ ++/* ++ * Clean up routine ++ */ ++static int ams_delta_cleanup(struct platform_device *pdev) ++{ ++ void __iomem *io_base = platform_get_drvdata(pdev); ++ ++ /* Release resources, unregister device */ ++ nand_release(ams_delta_mtd); ++ ++ gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); ++ gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); ++ iounmap(io_base); ++ ++ /* Free the MTD device structure */ ++ kfree(mtd_to_nand(ams_delta_mtd)); ++ ++ return 0; ++} ++ ++static struct platform_driver ams_delta_nand_driver = { ++ .probe = ams_delta_init, ++ .remove = ams_delta_cleanup, ++ .driver = { ++ .name = "ams-delta-nand", ++ }, ++}; ++ ++module_platform_driver(ams_delta_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jonathan McDowell "); ++MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); +diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile +new file mode 100644 +index 00000000..288db4f +--- /dev/null ++++ b/drivers/mtd/nand/raw/atmel/Makefile +@@ -0,0 +1,4 @@ ++obj-$(CONFIG_MTD_NAND_ATMEL) += atmel-nand-controller.o atmel-pmecc.o ++ ++atmel-nand-controller-objs := nand-controller.o ++atmel-pmecc-objs := pmecc.o +diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c +new file mode 100644 +index 00000000..1c1b305 +--- /dev/null ++++ b/drivers/mtd/nand/raw/atmel/nand-controller.c +@@ -0,0 +1,2567 @@ ++/* ++ * Copyright 2017 ATMEL ++ * Copyright 2017 Free Electrons ++ * ++ * Author: Boris Brezillon ++ * ++ * Derived from the atmel_nand.c driver which contained the following ++ * copyrights: ++ * ++ * Copyright 2003 Rick Bronson ++ * ++ * Derived from drivers/mtd/nand/autcpu12.c ++ * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * Derived from drivers/mtd/spia.c ++ * Copyright 2000 Steven J. Hill (sjhill@cotw.com) ++ * ++ * ++ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 ++ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 ++ * ++ * Derived from Das U-Boot source code ++ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) ++ * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas ++ * ++ * Add Programmable Multibit ECC support for various AT91 SoC ++ * Copyright 2012 ATMEL, Hong Xu ++ * ++ * Add Nand Flash Controller support for SAMA5 SoC ++ * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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. ++ * ++ * A few words about the naming convention in this file. This convention ++ * applies to structure and function names. ++ * ++ * Prefixes: ++ * ++ * - atmel_nand_: all generic structures/functions ++ * - atmel_smc_nand_: all structures/functions specific to the SMC interface ++ * (at91sam9 and avr32 SoCs) ++ * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface ++ * (sama5 SoCs and later) ++ * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block ++ * that is available in the HSMC block ++ * - _nand_: all SoC specific structures/functions ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pmecc.h" ++ ++#define ATMEL_HSMC_NFC_CFG 0x0 ++#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) ++#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) ++#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20)) ++#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16) ++#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13) ++#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12) ++#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9) ++#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8) ++#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0) ++#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1) ++ ++#define ATMEL_HSMC_NFC_CTRL 0x4 ++#define ATMEL_HSMC_NFC_CTRL_EN BIT(0) ++#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1) ++ ++#define ATMEL_HSMC_NFC_SR 0x8 ++#define ATMEL_HSMC_NFC_IER 0xc ++#define ATMEL_HSMC_NFC_IDR 0x10 ++#define ATMEL_HSMC_NFC_IMR 0x14 ++#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1) ++#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4) ++#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5) ++#define ATMEL_HSMC_NFC_SR_BUSY BIT(8) ++#define ATMEL_HSMC_NFC_SR_WR BIT(11) ++#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12) ++#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16) ++#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17) ++#define ATMEL_HSMC_NFC_SR_DTOE BIT(20) ++#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21) ++#define ATMEL_HSMC_NFC_SR_AWB BIT(22) ++#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23) ++#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \ ++ ATMEL_HSMC_NFC_SR_UNDEF | \ ++ ATMEL_HSMC_NFC_SR_AWB | \ ++ ATMEL_HSMC_NFC_SR_NFCASE) ++#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24) ++ ++#define ATMEL_HSMC_NFC_ADDR 0x18 ++#define ATMEL_HSMC_NFC_BANK 0x1c ++ ++#define ATMEL_NFC_MAX_RB_ID 7 ++ ++#define ATMEL_NFC_SRAM_SIZE 0x2400 ++ ++#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2)) ++#define ATMEL_NFC_VCMD2 BIT(18) ++#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19) ++#define ATMEL_NFC_CSID(cs) ((cs) << 22) ++#define ATMEL_NFC_DATAEN BIT(25) ++#define ATMEL_NFC_NFCWR BIT(26) ++ ++#define ATMEL_NFC_MAX_ADDR_CYCLES 5 ++ ++#define ATMEL_NAND_ALE_OFFSET BIT(21) ++#define ATMEL_NAND_CLE_OFFSET BIT(22) ++ ++#define DEFAULT_TIMEOUT_MS 1000 ++#define MIN_DMA_LEN 128 ++ ++static bool atmel_nand_avoid_dma __read_mostly; ++ ++MODULE_PARM_DESC(avoiddma, "Avoid using DMA"); ++module_param_named(avoiddma, atmel_nand_avoid_dma, bool, 0400); ++ ++enum atmel_nand_rb_type { ++ ATMEL_NAND_NO_RB, ++ ATMEL_NAND_NATIVE_RB, ++ ATMEL_NAND_GPIO_RB, ++}; ++ ++struct atmel_nand_rb { ++ enum atmel_nand_rb_type type; ++ union { ++ struct gpio_desc *gpio; ++ int id; ++ }; ++}; ++ ++struct atmel_nand_cs { ++ int id; ++ struct atmel_nand_rb rb; ++ struct gpio_desc *csgpio; ++ struct { ++ void __iomem *virt; ++ dma_addr_t dma; ++ } io; ++ ++ struct atmel_smc_cs_conf smcconf; ++}; ++ ++struct atmel_nand { ++ struct list_head node; ++ struct device *dev; ++ struct nand_chip base; ++ struct atmel_nand_cs *activecs; ++ struct atmel_pmecc_user *pmecc; ++ struct gpio_desc *cdgpio; ++ int numcs; ++ struct atmel_nand_cs cs[]; ++}; ++ ++static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip) ++{ ++ return container_of(chip, struct atmel_nand, base); ++} ++ ++enum atmel_nfc_data_xfer { ++ ATMEL_NFC_NO_DATA, ++ ATMEL_NFC_READ_DATA, ++ ATMEL_NFC_WRITE_DATA, ++}; ++ ++struct atmel_nfc_op { ++ u8 cs; ++ u8 ncmds; ++ u8 cmds[2]; ++ u8 naddrs; ++ u8 addrs[5]; ++ enum atmel_nfc_data_xfer data; ++ u32 wait; ++ u32 errors; ++}; ++ ++struct atmel_nand_controller; ++struct atmel_nand_controller_caps; ++ ++struct atmel_nand_controller_ops { ++ int (*probe)(struct platform_device *pdev, ++ const struct atmel_nand_controller_caps *caps); ++ int (*remove)(struct atmel_nand_controller *nc); ++ void (*nand_init)(struct atmel_nand_controller *nc, ++ struct atmel_nand *nand); ++ int (*ecc_init)(struct atmel_nand *nand); ++ int (*setup_data_interface)(struct atmel_nand *nand, int csline, ++ const struct nand_data_interface *conf); ++}; ++ ++struct atmel_nand_controller_caps { ++ bool has_dma; ++ bool legacy_of_bindings; ++ u32 ale_offs; ++ u32 cle_offs; ++ const struct atmel_nand_controller_ops *ops; ++}; ++ ++struct atmel_nand_controller { ++ struct nand_controller base; ++ const struct atmel_nand_controller_caps *caps; ++ struct device *dev; ++ struct regmap *smc; ++ struct dma_chan *dmac; ++ struct atmel_pmecc *pmecc; ++ struct list_head chips; ++ struct clk *mck; ++}; ++ ++static inline struct atmel_nand_controller * ++to_nand_controller(struct nand_controller *ctl) ++{ ++ return container_of(ctl, struct atmel_nand_controller, base); ++} ++ ++struct atmel_smc_nand_controller { ++ struct atmel_nand_controller base; ++ struct regmap *matrix; ++ unsigned int ebi_csa_offs; ++}; ++ ++static inline struct atmel_smc_nand_controller * ++to_smc_nand_controller(struct nand_controller *ctl) ++{ ++ return container_of(to_nand_controller(ctl), ++ struct atmel_smc_nand_controller, base); ++} ++ ++struct atmel_hsmc_nand_controller { ++ struct atmel_nand_controller base; ++ struct { ++ struct gen_pool *pool; ++ void __iomem *virt; ++ dma_addr_t dma; ++ } sram; ++ const struct atmel_hsmc_reg_layout *hsmc_layout; ++ struct regmap *io; ++ struct atmel_nfc_op op; ++ struct completion complete; ++ int irq; ++ ++ /* Only used when instantiating from legacy DT bindings. */ ++ struct clk *clk; ++}; ++ ++static inline struct atmel_hsmc_nand_controller * ++to_hsmc_nand_controller(struct nand_controller *ctl) ++{ ++ return container_of(to_nand_controller(ctl), ++ struct atmel_hsmc_nand_controller, base); ++} ++ ++static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status) ++{ ++ op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS; ++ op->wait ^= status & op->wait; ++ ++ return !op->wait || op->errors; ++} ++ ++static irqreturn_t atmel_nfc_interrupt(int irq, void *data) ++{ ++ struct atmel_hsmc_nand_controller *nc = data; ++ u32 sr, rcvd; ++ bool done; ++ ++ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr); ++ ++ rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); ++ done = atmel_nfc_op_done(&nc->op, sr); ++ ++ if (rcvd) ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd); ++ ++ if (done) ++ complete(&nc->complete); ++ ++ return rcvd ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll, ++ unsigned int timeout_ms) ++{ ++ int ret; ++ ++ if (!timeout_ms) ++ timeout_ms = DEFAULT_TIMEOUT_MS; ++ ++ if (poll) { ++ u32 status; ++ ++ ret = regmap_read_poll_timeout(nc->base.smc, ++ ATMEL_HSMC_NFC_SR, status, ++ atmel_nfc_op_done(&nc->op, ++ status), ++ 0, timeout_ms * 1000); ++ } else { ++ init_completion(&nc->complete); ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER, ++ nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); ++ ret = wait_for_completion_timeout(&nc->complete, ++ msecs_to_jiffies(timeout_ms)); ++ if (!ret) ++ ret = -ETIMEDOUT; ++ else ++ ret = 0; ++ ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); ++ } ++ ++ if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) { ++ dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n"); ++ ret = -ETIMEDOUT; ++ } ++ ++ if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) { ++ dev_err(nc->base.dev, "Access to an undefined area\n"); ++ ret = -EIO; ++ } ++ ++ if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) { ++ dev_err(nc->base.dev, "Access while busy\n"); ++ ret = -EIO; ++ } ++ ++ if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) { ++ dev_err(nc->base.dev, "Wrong access size\n"); ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static void atmel_nand_dma_transfer_finished(void *data) ++{ ++ struct completion *finished = data; ++ ++ complete(finished); ++} ++ ++static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc, ++ void *buf, dma_addr_t dev_dma, size_t len, ++ enum dma_data_direction dir) ++{ ++ DECLARE_COMPLETION_ONSTACK(finished); ++ dma_addr_t src_dma, dst_dma, buf_dma; ++ struct dma_async_tx_descriptor *tx; ++ dma_cookie_t cookie; ++ ++ buf_dma = dma_map_single(nc->dev, buf, len, dir); ++ if (dma_mapping_error(nc->dev, dev_dma)) { ++ dev_err(nc->dev, ++ "Failed to prepare a buffer for DMA access\n"); ++ goto err; ++ } ++ ++ if (dir == DMA_FROM_DEVICE) { ++ src_dma = dev_dma; ++ dst_dma = buf_dma; ++ } else { ++ src_dma = buf_dma; ++ dst_dma = dev_dma; ++ } ++ ++ tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len, ++ DMA_CTRL_ACK | DMA_PREP_INTERRUPT); ++ if (!tx) { ++ dev_err(nc->dev, "Failed to prepare DMA memcpy\n"); ++ goto err_unmap; ++ } ++ ++ tx->callback = atmel_nand_dma_transfer_finished; ++ tx->callback_param = &finished; ++ ++ cookie = dmaengine_submit(tx); ++ if (dma_submit_error(cookie)) { ++ dev_err(nc->dev, "Failed to do DMA tx_submit\n"); ++ goto err_unmap; ++ } ++ ++ dma_async_issue_pending(nc->dmac); ++ wait_for_completion(&finished); ++ ++ return 0; ++ ++err_unmap: ++ dma_unmap_single(nc->dev, buf_dma, len, dir); ++ ++err: ++ dev_dbg(nc->dev, "Fall back to CPU I/O\n"); ++ ++ return -EIO; ++} ++ ++static u8 atmel_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ ++ return ioread8(nand->activecs->io.virt); ++} ++ ++static u16 atmel_nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ ++ return ioread16(nand->activecs->io.virt); ++} ++ ++static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ iowrite16(byte | (byte << 8), nand->activecs->io.virt); ++ else ++ iowrite8(byte, nand->activecs->io.virt); ++} ++ ++static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_nand_controller *nc; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ /* ++ * If the controller supports DMA, the buffer address is DMA-able and ++ * len is long enough to make DMA transfers profitable, let's trigger ++ * a DMA transfer. If it fails, fallback to PIO mode. ++ */ ++ if (nc->dmac && virt_addr_valid(buf) && ++ len >= MIN_DMA_LEN && ++ !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len, ++ DMA_FROM_DEVICE)) ++ return; ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ ioread16_rep(nand->activecs->io.virt, buf, len / 2); ++ else ++ ioread8_rep(nand->activecs->io.virt, buf, len); ++} ++ ++static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_nand_controller *nc; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ /* ++ * If the controller supports DMA, the buffer address is DMA-able and ++ * len is long enough to make DMA transfers profitable, let's trigger ++ * a DMA transfer. If it fails, fallback to PIO mode. ++ */ ++ if (nc->dmac && virt_addr_valid(buf) && ++ len >= MIN_DMA_LEN && ++ !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma, ++ len, DMA_TO_DEVICE)) ++ return; ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ iowrite16_rep(nand->activecs->io.virt, buf, len / 2); ++ else ++ iowrite8_rep(nand->activecs->io.virt, buf, len); ++} ++ ++static int atmel_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ ++ return gpiod_get_value(nand->activecs->rb.gpio); ++} ++ ++static void atmel_nand_select_chip(struct mtd_info *mtd, int cs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ ++ if (cs < 0 || cs >= nand->numcs) { ++ nand->activecs = NULL; ++ chip->dev_ready = NULL; ++ return; ++ } ++ ++ nand->activecs = &nand->cs[cs]; ++ ++ if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB) ++ chip->dev_ready = atmel_nand_dev_ready; ++} ++ ++static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ u32 status; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status); ++ ++ return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); ++} ++ ++static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ atmel_nand_select_chip(mtd, cs); ++ ++ if (!nand->activecs) { ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, ++ ATMEL_HSMC_NFC_CTRL_DIS); ++ return; ++ } ++ ++ if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB) ++ chip->dev_ready = atmel_hsmc_nand_dev_ready; ++ ++ regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG, ++ ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK | ++ ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK | ++ ATMEL_HSMC_NFC_CFG_RSPARE | ++ ATMEL_HSMC_NFC_CFG_WSPARE, ++ ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | ++ ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | ++ ATMEL_HSMC_NFC_CFG_RSPARE); ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, ++ ATMEL_HSMC_NFC_CTRL_EN); ++} ++ ++static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) ++{ ++ u8 *addrs = nc->op.addrs; ++ unsigned int op = 0; ++ u32 addr, val; ++ int i, ret; ++ ++ nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; ++ ++ for (i = 0; i < nc->op.ncmds; i++) ++ op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); ++ ++ if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); ++ ++ op |= ATMEL_NFC_CSID(nc->op.cs) | ++ ATMEL_NFC_ACYCLE(nc->op.naddrs); ++ ++ if (nc->op.ncmds > 1) ++ op |= ATMEL_NFC_VCMD2; ++ ++ addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | ++ (addrs[3] << 24); ++ ++ if (nc->op.data != ATMEL_NFC_NO_DATA) { ++ op |= ATMEL_NFC_DATAEN; ++ nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; ++ ++ if (nc->op.data == ATMEL_NFC_WRITE_DATA) ++ op |= ATMEL_NFC_NFCWR; ++ } ++ ++ /* Clear all flags. */ ++ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); ++ ++ /* Send the command. */ ++ regmap_write(nc->io, op, addr); ++ ++ ret = atmel_nfc_wait(nc, poll, 0); ++ if (ret) ++ dev_err(nc->base.dev, ++ "Failed to send NAND command (err = %d)!", ++ ret); ++ ++ /* Reset the op state. */ ++ memset(&nc->op, 0, sizeof(nc->op)); ++ ++ return ret; ++} ++ ++static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat, ++ unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ if (ctrl & NAND_ALE) { ++ if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) ++ return; ++ ++ nc->op.addrs[nc->op.naddrs++] = dat; ++ } else if (ctrl & NAND_CLE) { ++ if (nc->op.ncmds > 1) ++ return; ++ ++ nc->op.cmds[nc->op.ncmds++] = dat; ++ } ++ ++ if (dat == NAND_CMD_NONE) { ++ nc->op.cs = nand->activecs->id; ++ atmel_nfc_exec_op(nc, true); ++ } ++} ++ ++static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_nand_controller *nc; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) { ++ if (ctrl & NAND_NCE) ++ gpiod_set_value(nand->activecs->csgpio, 0); ++ else ++ gpiod_set_value(nand->activecs->csgpio, 1); ++ } ++ ++ if (ctrl & NAND_ALE) ++ writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs); ++ else if (ctrl & NAND_CLE) ++ writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs); ++} ++ ++static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf, ++ bool oob_required) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ int ret = -EIO; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ if (nc->base.dmac) ++ ret = atmel_nand_dma_transfer(&nc->base, (void *)buf, ++ nc->sram.dma, mtd->writesize, ++ DMA_TO_DEVICE); ++ ++ /* Falling back to CPU copy. */ ++ if (ret) ++ memcpy_toio(nc->sram.virt, buf, mtd->writesize); ++ ++ if (oob_required) ++ memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, ++ mtd->oobsize); ++} ++ ++static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf, ++ bool oob_required) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ int ret = -EIO; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ if (nc->base.dmac) ++ ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma, ++ mtd->writesize, DMA_FROM_DEVICE); ++ ++ /* Falling back to CPU copy. */ ++ if (ret) ++ memcpy_fromio(buf, nc->sram.virt, mtd->writesize); ++ ++ if (oob_required) ++ memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, ++ mtd->oobsize); ++} ++ ++static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ if (column >= 0) { ++ nc->op.addrs[nc->op.naddrs++] = column; ++ ++ /* ++ * 2 address cycles for the column offset on large page NANDs. ++ */ ++ if (mtd->writesize > 512) ++ nc->op.addrs[nc->op.naddrs++] = column >> 8; ++ } ++ ++ if (page >= 0) { ++ nc->op.addrs[nc->op.naddrs++] = page; ++ nc->op.addrs[nc->op.naddrs++] = page >> 8; ++ ++ if (chip->options & NAND_ROW_ADDR_3) ++ nc->op.addrs[nc->op.naddrs++] = page >> 16; ++ } ++} ++ ++static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw) ++{ ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_nand_controller *nc; ++ int ret; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ if (raw) ++ return 0; ++ ++ ret = atmel_pmecc_enable(nand->pmecc, op); ++ if (ret) ++ dev_err(nc->dev, ++ "Failed to enable ECC engine (err = %d)\n", ret); ++ ++ return ret; ++} ++ ++static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw) ++{ ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ ++ if (!raw) ++ atmel_pmecc_disable(nand->pmecc); ++} ++ ++static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw) ++{ ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand_controller *nc; ++ struct mtd_oob_region oobregion; ++ void *eccbuf; ++ int ret, i; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ if (raw) ++ return 0; ++ ++ ret = atmel_pmecc_wait_rdy(nand->pmecc); ++ if (ret) { ++ dev_err(nc->dev, ++ "Failed to transfer NAND page data (err = %d)\n", ++ ret); ++ return ret; ++ } ++ ++ mtd_ooblayout_ecc(mtd, 0, &oobregion); ++ eccbuf = chip->oob_poi + oobregion.offset; ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ atmel_pmecc_get_generated_eccbytes(nand->pmecc, i, ++ eccbuf); ++ eccbuf += chip->ecc.bytes; ++ } ++ ++ return 0; ++} ++ ++static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, ++ bool raw) ++{ ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand_controller *nc; ++ struct mtd_oob_region oobregion; ++ int ret, i, max_bitflips = 0; ++ void *databuf, *eccbuf; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ if (raw) ++ return 0; ++ ++ ret = atmel_pmecc_wait_rdy(nand->pmecc); ++ if (ret) { ++ dev_err(nc->dev, ++ "Failed to read NAND page data (err = %d)\n", ++ ret); ++ return ret; ++ } ++ ++ mtd_ooblayout_ecc(mtd, 0, &oobregion); ++ eccbuf = chip->oob_poi + oobregion.offset; ++ databuf = buf; ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf, ++ eccbuf); ++ if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc)) ++ ret = nand_check_erased_ecc_chunk(databuf, ++ chip->ecc.size, ++ eccbuf, ++ chip->ecc.bytes, ++ NULL, 0, ++ chip->ecc.strength); ++ ++ if (ret >= 0) ++ max_bitflips = max(ret, max_bitflips); ++ else ++ mtd->ecc_stats.failed++; ++ ++ databuf += chip->ecc.size; ++ eccbuf += chip->ecc.bytes; ++ } ++ ++ return max_bitflips; ++} ++ ++static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, ++ bool oob_required, int page, bool raw) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ int ret; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); ++ if (ret) ++ return ret; ++ ++ atmel_nand_write_buf(mtd, buf, mtd->writesize); ++ ++ ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); ++ if (ret) { ++ atmel_pmecc_disable(nand->pmecc); ++ return ret; ++ } ++ ++ atmel_nand_pmecc_disable(chip, raw); ++ ++ atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, const u8 *buf, ++ int oob_required, int page) ++{ ++ return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false); ++} ++ ++static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, int oob_required, ++ int page) ++{ ++ return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true); ++} ++ ++static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, ++ bool oob_required, int page, bool raw) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); ++ if (ret) ++ return ret; ++ ++ atmel_nand_read_buf(mtd, buf, mtd->writesize); ++ atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ ret = atmel_nand_pmecc_correct_data(chip, buf, raw); ++ ++ atmel_nand_pmecc_disable(chip, raw); ++ ++ return ret; ++} ++ ++static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false); ++} ++ ++static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true); ++} ++ ++static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, ++ const u8 *buf, bool oob_required, ++ int page, bool raw) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ int ret, status; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ atmel_nfc_copy_to_sram(chip, buf, false); ++ ++ nc->op.cmds[0] = NAND_CMD_SEQIN; ++ nc->op.ncmds = 1; ++ atmel_nfc_set_op_addr(chip, page, 0x0); ++ nc->op.cs = nand->activecs->id; ++ nc->op.data = ATMEL_NFC_WRITE_DATA; ++ ++ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); ++ if (ret) ++ return ret; ++ ++ ret = atmel_nfc_exec_op(nc, false); ++ if (ret) { ++ atmel_nand_pmecc_disable(chip, raw); ++ dev_err(nc->base.dev, ++ "Failed to transfer NAND page data (err = %d)\n", ++ ret); ++ return ret; ++ } ++ ++ ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); ++ ++ atmel_nand_pmecc_disable(chip, raw); ++ ++ if (ret) ++ return ret; ++ ++ atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ nc->op.cmds[0] = NAND_CMD_PAGEPROG; ++ nc->op.ncmds = 1; ++ nc->op.cs = nand->activecs->id; ++ ret = atmel_nfc_exec_op(nc, false); ++ if (ret) ++ dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n", ++ ret); ++ ++ status = chip->waitfunc(mtd, chip); ++ if (status & NAND_STATUS_FAIL) ++ return -EIO; ++ ++ return ret; ++} ++ ++static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, int oob_required, ++ int page) ++{ ++ return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, ++ false); ++} ++ ++static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, int page) ++{ ++ return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, ++ true); ++} ++ ++static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, ++ bool oob_required, int page, ++ bool raw) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_hsmc_nand_controller *nc; ++ int ret; ++ ++ nc = to_hsmc_nand_controller(chip->controller); ++ ++ /* ++ * Optimized read page accessors only work when the NAND R/B pin is ++ * connected to a native SoC R/B pin. If that's not the case, fallback ++ * to the non-optimized one. ++ */ ++ if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) { ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, ++ raw); ++ } ++ ++ nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; ++ ++ if (mtd->writesize > 512) ++ nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART; ++ ++ atmel_nfc_set_op_addr(chip, page, 0x0); ++ nc->op.cs = nand->activecs->id; ++ nc->op.data = ATMEL_NFC_READ_DATA; ++ ++ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); ++ if (ret) ++ return ret; ++ ++ ret = atmel_nfc_exec_op(nc, false); ++ if (ret) { ++ atmel_nand_pmecc_disable(chip, raw); ++ dev_err(nc->base.dev, ++ "Failed to load NAND page data (err = %d)\n", ++ ret); ++ return ret; ++ } ++ ++ atmel_nfc_copy_from_sram(chip, buf, true); ++ ++ ret = atmel_nand_pmecc_correct_data(chip, buf, raw); ++ ++ atmel_nand_pmecc_disable(chip, raw); ++ ++ return ret; ++} ++ ++static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, ++ false); ++} ++ ++static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u8 *buf, int oob_required, ++ int page) ++{ ++ return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, ++ true); ++} ++ ++static int atmel_nand_pmecc_init(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_nand_controller *nc; ++ struct atmel_pmecc_user_req req; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ if (!nc->pmecc) { ++ dev_err(nc->dev, "HW ECC not supported\n"); ++ return -ENOTSUPP; ++ } ++ ++ if (nc->caps->legacy_of_bindings) { ++ u32 val; ++ ++ if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap", ++ &val)) ++ chip->ecc.strength = val; ++ ++ if (!of_property_read_u32(nc->dev->of_node, ++ "atmel,pmecc-sector-size", ++ &val)) ++ chip->ecc.size = val; ++ } ++ ++ if (chip->ecc.options & NAND_ECC_MAXIMIZE) ++ req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; ++ else if (chip->ecc.strength) ++ req.ecc.strength = chip->ecc.strength; ++ else if (chip->ecc_strength_ds) ++ req.ecc.strength = chip->ecc_strength_ds; ++ else ++ req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; ++ ++ if (chip->ecc.size) ++ req.ecc.sectorsize = chip->ecc.size; ++ else if (chip->ecc_step_ds) ++ req.ecc.sectorsize = chip->ecc_step_ds; ++ else ++ req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; ++ ++ req.pagesize = mtd->writesize; ++ req.oobsize = mtd->oobsize; ++ ++ if (mtd->writesize <= 512) { ++ req.ecc.bytes = 4; ++ req.ecc.ooboffset = 0; ++ } else { ++ req.ecc.bytes = mtd->oobsize - 2; ++ req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO; ++ } ++ ++ nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req); ++ if (IS_ERR(nand->pmecc)) ++ return PTR_ERR(nand->pmecc); ++ ++ chip->ecc.algo = NAND_ECC_BCH; ++ chip->ecc.size = req.ecc.sectorsize; ++ chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors; ++ chip->ecc.strength = req.ecc.strength; ++ ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ++ ++ return 0; ++} ++ ++static int atmel_nand_ecc_init(struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ struct atmel_nand_controller *nc; ++ int ret; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ switch (chip->ecc.mode) { ++ case NAND_ECC_NONE: ++ case NAND_ECC_SOFT: ++ /* ++ * Nothing to do, the core will initialize everything for us. ++ */ ++ break; ++ ++ case NAND_ECC_HW: ++ ret = atmel_nand_pmecc_init(chip); ++ if (ret) ++ return ret; ++ ++ chip->ecc.read_page = atmel_nand_pmecc_read_page; ++ chip->ecc.write_page = atmel_nand_pmecc_write_page; ++ chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw; ++ chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw; ++ break; ++ ++ default: ++ /* Other modes are not supported. */ ++ dev_err(nc->dev, "Unsupported ECC mode: %d\n", ++ chip->ecc.mode); ++ return -ENOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ int ret; ++ ++ ret = atmel_nand_ecc_init(nand); ++ if (ret) ++ return ret; ++ ++ if (chip->ecc.mode != NAND_ECC_HW) ++ return 0; ++ ++ /* Adjust the ECC operations for the HSMC IP. */ ++ chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page; ++ chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; ++ chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; ++ chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; ++ ++ return 0; ++} ++ ++static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, ++ const struct nand_data_interface *conf, ++ struct atmel_smc_cs_conf *smcconf) ++{ ++ u32 ncycles, totalcycles, timeps, mckperiodps; ++ struct atmel_nand_controller *nc; ++ int ret; ++ ++ nc = to_nand_controller(nand->base.controller); ++ ++ /* DDR interface not supported. */ ++ if (conf->type != NAND_SDR_IFACE) ++ return -ENOTSUPP; ++ ++ /* ++ * tRC < 30ns implies EDO mode. This controller does not support this ++ * mode. ++ */ ++ if (conf->timings.sdr.tRC_min < 30000) ++ return -ENOTSUPP; ++ ++ atmel_smc_cs_conf_init(smcconf); ++ ++ mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck); ++ mckperiodps *= 1000; ++ ++ /* ++ * Set write pulse timing. This one is easy to extract: ++ * ++ * NWE_PULSE = tWP ++ */ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps); ++ totalcycles = ncycles; ++ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* ++ * The write setup timing depends on the operation done on the NAND. ++ * All operations goes through the same data bus, but the operation ++ * type depends on the address we are writing to (ALE/CLE address ++ * lines). ++ * Since we have no way to differentiate the different operations at ++ * the SMC level, we must consider the worst case (the biggest setup ++ * time among all operation types): ++ * ++ * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE ++ */ ++ timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min, ++ conf->timings.sdr.tALS_min); ++ timeps = max(timeps, conf->timings.sdr.tDS_min); ++ ncycles = DIV_ROUND_UP(timeps, mckperiodps); ++ ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0; ++ totalcycles += ncycles; ++ ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* ++ * As for the write setup timing, the write hold timing depends on the ++ * operation done on the NAND: ++ * ++ * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH) ++ */ ++ timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min, ++ conf->timings.sdr.tALH_min); ++ timeps = max3(timeps, conf->timings.sdr.tDH_min, ++ conf->timings.sdr.tWH_min); ++ ncycles = DIV_ROUND_UP(timeps, mckperiodps); ++ totalcycles += ncycles; ++ ++ /* ++ * The write cycle timing is directly matching tWC, but is also ++ * dependent on the other timings on the setup and hold timings we ++ * calculated earlier, which gives: ++ * ++ * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD) ++ */ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps); ++ ncycles = max(totalcycles, ncycles); ++ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* ++ * We don't want the CS line to be toggled between each byte/word ++ * transfer to the NAND. The only way to guarantee that is to have the ++ * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: ++ * ++ * NCS_WR_PULSE = NWE_CYCLE ++ */ ++ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* ++ * As for the write setup timing, the read hold timing depends on the ++ * operation done on the NAND: ++ * ++ * NRD_HOLD = max(tREH, tRHOH) ++ */ ++ timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min); ++ ncycles = DIV_ROUND_UP(timeps, mckperiodps); ++ totalcycles = ncycles; ++ ++ /* ++ * TDF = tRHZ - NRD_HOLD ++ */ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps); ++ ncycles -= totalcycles; ++ ++ /* ++ * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and ++ * we might end up with a config that does not fit in the TDF field. ++ * Just take the max value in this case and hope that the NAND is more ++ * tolerant than advertised. ++ */ ++ if (ncycles > ATMEL_SMC_MODE_TDF_MAX) ++ ncycles = ATMEL_SMC_MODE_TDF_MAX; ++ else if (ncycles < ATMEL_SMC_MODE_TDF_MIN) ++ ncycles = ATMEL_SMC_MODE_TDF_MIN; ++ ++ smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) | ++ ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; ++ ++ /* ++ * Read pulse timing directly matches tRP: ++ * ++ * NRD_PULSE = tRP ++ */ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); ++ totalcycles += ncycles; ++ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* ++ * The write cycle timing is directly matching tWC, but is also ++ * dependent on the setup and hold timings we calculated earlier, ++ * which gives: ++ * ++ * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD) ++ * ++ * NRD_SETUP is always 0. ++ */ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps); ++ ncycles = max(totalcycles, ncycles); ++ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* ++ * We don't want the CS line to be toggled between each byte/word ++ * transfer from the NAND. The only way to guarantee that is to have ++ * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: ++ * ++ * NCS_RD_PULSE = NRD_CYCLE ++ */ ++ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* Txxx timings are directly matching tXXX ones. */ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps); ++ ret = atmel_smc_cs_conf_set_timing(smcconf, ++ ATMEL_HSMC_TIMINGS_TCLR_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps); ++ ret = atmel_smc_cs_conf_set_timing(smcconf, ++ ATMEL_HSMC_TIMINGS_TADL_SHIFT, ++ ncycles); ++ /* ++ * Version 4 of the ONFI spec mandates that tADL be at least 400 ++ * nanoseconds, but, depending on the master clock rate, 400 ns may not ++ * fit in the tADL field of the SMC reg. We need to relax the check and ++ * accept the -ERANGE return code. ++ * ++ * Note that previous versions of the ONFI spec had a lower tADL_min ++ * (100 or 200 ns). It's not clear why this timing constraint got ++ * increased but it seems most NANDs are fine with values lower than ++ * 400ns, so we should be safe. ++ */ ++ if (ret && ret != -ERANGE) ++ return ret; ++ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); ++ ret = atmel_smc_cs_conf_set_timing(smcconf, ++ ATMEL_HSMC_TIMINGS_TAR_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps); ++ ret = atmel_smc_cs_conf_set_timing(smcconf, ++ ATMEL_HSMC_TIMINGS_TRR_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps); ++ ret = atmel_smc_cs_conf_set_timing(smcconf, ++ ATMEL_HSMC_TIMINGS_TWB_SHIFT, ++ ncycles); ++ if (ret) ++ return ret; ++ ++ /* Attach the CS line to the NFC logic. */ ++ smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL; ++ ++ /* Set the appropriate data bus width. */ ++ if (nand->base.options & NAND_BUSWIDTH_16) ++ smcconf->mode |= ATMEL_SMC_MODE_DBW_16; ++ ++ /* Operate in NRD/NWE READ/WRITEMODE. */ ++ smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD | ++ ATMEL_SMC_MODE_WRITEMODE_NWE; ++ ++ return 0; ++} ++ ++static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand, ++ int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct atmel_nand_controller *nc; ++ struct atmel_smc_cs_conf smcconf; ++ struct atmel_nand_cs *cs; ++ int ret; ++ ++ nc = to_nand_controller(nand->base.controller); ++ ++ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); ++ if (ret) ++ return ret; ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ cs = &nand->cs[csline]; ++ cs->smcconf = smcconf; ++ atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf); ++ ++ return 0; ++} ++ ++static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand, ++ int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct atmel_hsmc_nand_controller *nc; ++ struct atmel_smc_cs_conf smcconf; ++ struct atmel_nand_cs *cs; ++ int ret; ++ ++ nc = to_hsmc_nand_controller(nand->base.controller); ++ ++ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); ++ if (ret) ++ return ret; ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ cs = &nand->cs[csline]; ++ cs->smcconf = smcconf; ++ ++ if (cs->rb.type == ATMEL_NAND_NATIVE_RB) ++ cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id); ++ ++ atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id, ++ &cs->smcconf); ++ ++ return 0; ++} ++ ++static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct atmel_nand *nand = to_atmel_nand(chip); ++ struct atmel_nand_controller *nc; ++ ++ nc = to_nand_controller(nand->base.controller); ++ ++ if (csline >= nand->numcs || ++ (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) ++ return -EINVAL; ++ ++ return nc->caps->ops->setup_data_interface(nand, csline, conf); ++} ++ ++static void atmel_nand_init(struct atmel_nand_controller *nc, ++ struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ mtd->dev.parent = nc->dev; ++ nand->base.controller = &nc->base; ++ ++ chip->cmd_ctrl = atmel_nand_cmd_ctrl; ++ chip->read_byte = atmel_nand_read_byte; ++ chip->read_word = atmel_nand_read_word; ++ chip->write_byte = atmel_nand_write_byte; ++ chip->read_buf = atmel_nand_read_buf; ++ chip->write_buf = atmel_nand_write_buf; ++ chip->select_chip = atmel_nand_select_chip; ++ ++ if (nc->mck && nc->caps->ops->setup_data_interface) ++ chip->setup_data_interface = atmel_nand_setup_data_interface; ++ ++ /* Some NANDs require a longer delay than the default one (20us). */ ++ chip->chip_delay = 40; ++ ++ /* ++ * Use a bounce buffer when the buffer passed by the MTD user is not ++ * suitable for DMA. ++ */ ++ if (nc->dmac) ++ chip->options |= NAND_USE_BOUNCE_BUFFER; ++ ++ /* Default to HW ECC if pmecc is available. */ ++ if (nc->pmecc) ++ chip->ecc.mode = NAND_ECC_HW; ++} ++ ++static void atmel_smc_nand_init(struct atmel_nand_controller *nc, ++ struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ struct atmel_smc_nand_controller *smc_nc; ++ int i; ++ ++ atmel_nand_init(nc, nand); ++ ++ smc_nc = to_smc_nand_controller(chip->controller); ++ if (!smc_nc->matrix) ++ return; ++ ++ /* Attach the CS to the NAND Flash logic. */ ++ for (i = 0; i < nand->numcs; i++) ++ regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs, ++ BIT(nand->cs[i].id), BIT(nand->cs[i].id)); ++} ++ ++static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, ++ struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ ++ atmel_nand_init(nc, nand); ++ ++ /* Overload some methods for the HSMC controller. */ ++ chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl; ++ chip->select_chip = atmel_hsmc_nand_select_chip; ++} ++ ++static int atmel_nand_detect(struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand_controller *nc; ++ int ret; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ ret = nand_scan_ident(mtd, nand->numcs, NULL); ++ if (ret) ++ dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int atmel_nand_unregister(struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ ret = mtd_device_unregister(mtd); ++ if (ret) ++ return ret; ++ ++ nand_cleanup(chip); ++ list_del(&nand->node); ++ ++ return 0; ++} ++ ++static int atmel_nand_register(struct atmel_nand *nand) ++{ ++ struct nand_chip *chip = &nand->base; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct atmel_nand_controller *nc; ++ int ret; ++ ++ nc = to_nand_controller(chip->controller); ++ ++ if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { ++ /* ++ * We keep the MTD name unchanged to avoid breaking platforms ++ * where the MTD cmdline parser is used and the bootloader ++ * has not been updated to use the new naming scheme. ++ */ ++ mtd->name = "atmel_nand"; ++ } else if (!mtd->name) { ++ /* ++ * If the new bindings are used and the bootloader has not been ++ * updated to pass a new mtdparts parameter on the cmdline, you ++ * should define the following property in your nand node: ++ * ++ * label = "atmel_nand"; ++ * ++ * This way, mtd->name will be set by the core when ++ * nand_set_flash_node() is called. ++ */ ++ mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, ++ "%s:nand.%d", dev_name(nc->dev), ++ nand->cs[0].id); ++ if (!mtd->name) { ++ dev_err(nc->dev, "Failed to allocate mtd->name\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) { ++ dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); ++ nand_cleanup(chip); ++ return ret; ++ } ++ ++ list_add_tail(&nand->node, &nc->chips); ++ ++ return 0; ++} ++ ++static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, ++ struct device_node *np, ++ int reg_cells) ++{ ++ struct atmel_nand *nand; ++ struct gpio_desc *gpio; ++ int numcs, ret, i; ++ ++ numcs = of_property_count_elems_of_size(np, "reg", ++ reg_cells * sizeof(u32)); ++ if (numcs < 1) { ++ dev_err(nc->dev, "Missing or invalid reg property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ nand = devm_kzalloc(nc->dev, ++ sizeof(*nand) + (numcs * sizeof(*nand->cs)), ++ GFP_KERNEL); ++ if (!nand) { ++ dev_err(nc->dev, "Failed to allocate NAND object\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ nand->numcs = numcs; ++ ++ gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0, ++ &np->fwnode, GPIOD_IN, ++ "nand-det"); ++ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { ++ dev_err(nc->dev, ++ "Failed to get detect gpio (err = %ld)\n", ++ PTR_ERR(gpio)); ++ return ERR_CAST(gpio); ++ } ++ ++ if (!IS_ERR(gpio)) ++ nand->cdgpio = gpio; ++ ++ for (i = 0; i < numcs; i++) { ++ struct resource res; ++ u32 val; ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) { ++ dev_err(nc->dev, "Invalid reg property (err = %d)\n", ++ ret); ++ return ERR_PTR(ret); ++ } ++ ++ ret = of_property_read_u32_index(np, "reg", i * reg_cells, ++ &val); ++ if (ret) { ++ dev_err(nc->dev, "Invalid reg property (err = %d)\n", ++ ret); ++ return ERR_PTR(ret); ++ } ++ ++ nand->cs[i].id = val; ++ ++ nand->cs[i].io.dma = res.start; ++ nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res); ++ if (IS_ERR(nand->cs[i].io.virt)) ++ return ERR_CAST(nand->cs[i].io.virt); ++ ++ if (!of_property_read_u32(np, "atmel,rb", &val)) { ++ if (val > ATMEL_NFC_MAX_RB_ID) ++ return ERR_PTR(-EINVAL); ++ ++ nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB; ++ nand->cs[i].rb.id = val; ++ } else { ++ gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, ++ "rb", i, &np->fwnode, ++ GPIOD_IN, "nand-rb"); ++ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { ++ dev_err(nc->dev, ++ "Failed to get R/B gpio (err = %ld)\n", ++ PTR_ERR(gpio)); ++ return ERR_CAST(gpio); ++ } ++ ++ if (!IS_ERR(gpio)) { ++ nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB; ++ nand->cs[i].rb.gpio = gpio; ++ } ++ } ++ ++ gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs", ++ i, &np->fwnode, ++ GPIOD_OUT_HIGH, ++ "nand-cs"); ++ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { ++ dev_err(nc->dev, ++ "Failed to get CS gpio (err = %ld)\n", ++ PTR_ERR(gpio)); ++ return ERR_CAST(gpio); ++ } ++ ++ if (!IS_ERR(gpio)) ++ nand->cs[i].csgpio = gpio; ++ } ++ ++ nand_set_flash_node(&nand->base, np); ++ ++ return nand; ++} ++ ++static int ++atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, ++ struct atmel_nand *nand) ++{ ++ int ret; ++ ++ /* No card inserted, skip this NAND. */ ++ if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) { ++ dev_info(nc->dev, "No SmartMedia card inserted.\n"); ++ return 0; ++ } ++ ++ nc->caps->ops->nand_init(nc, nand); ++ ++ ret = atmel_nand_detect(nand); ++ if (ret) ++ return ret; ++ ++ ret = nc->caps->ops->ecc_init(nand); ++ if (ret) ++ return ret; ++ ++ return atmel_nand_register(nand); ++} ++ ++static int ++atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc) ++{ ++ struct atmel_nand *nand, *tmp; ++ int ret; ++ ++ list_for_each_entry_safe(nand, tmp, &nc->chips, node) { ++ ret = atmel_nand_unregister(nand); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc) ++{ ++ struct device *dev = nc->dev; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct atmel_nand *nand; ++ struct gpio_desc *gpio; ++ struct resource *res; ++ ++ /* ++ * Legacy bindings only allow connecting a single NAND with a unique CS ++ * line to the controller. ++ */ ++ nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs), ++ GFP_KERNEL); ++ if (!nand) ++ return -ENOMEM; ++ ++ nand->numcs = 1; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nand->cs[0].io.virt = devm_ioremap_resource(dev, res); ++ if (IS_ERR(nand->cs[0].io.virt)) ++ return PTR_ERR(nand->cs[0].io.virt); ++ ++ nand->cs[0].io.dma = res->start; ++ ++ /* ++ * The old driver was hardcoding the CS id to 3 for all sama5 ++ * controllers. Since this id is only meaningful for the sama5 ++ * controller we can safely assign this id to 3 no matter the ++ * controller. ++ * If one wants to connect a NAND to a different CS line, he will ++ * have to use the new bindings. ++ */ ++ nand->cs[0].id = 3; ++ ++ /* R/B GPIO. */ ++ gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN); ++ if (IS_ERR(gpio)) { ++ dev_err(dev, "Failed to get R/B gpio (err = %ld)\n", ++ PTR_ERR(gpio)); ++ return PTR_ERR(gpio); ++ } ++ ++ if (gpio) { ++ nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB; ++ nand->cs[0].rb.gpio = gpio; ++ } ++ ++ /* CS GPIO. */ ++ gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH); ++ if (IS_ERR(gpio)) { ++ dev_err(dev, "Failed to get CS gpio (err = %ld)\n", ++ PTR_ERR(gpio)); ++ return PTR_ERR(gpio); ++ } ++ ++ nand->cs[0].csgpio = gpio; ++ ++ /* Card detect GPIO. */ ++ gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN); ++ if (IS_ERR(gpio)) { ++ dev_err(dev, ++ "Failed to get detect gpio (err = %ld)\n", ++ PTR_ERR(gpio)); ++ return PTR_ERR(gpio); ++ } ++ ++ nand->cdgpio = gpio; ++ ++ nand_set_flash_node(&nand->base, nc->dev->of_node); ++ ++ return atmel_nand_controller_add_nand(nc, nand); ++} ++ ++static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc) ++{ ++ struct device_node *np, *nand_np; ++ struct device *dev = nc->dev; ++ int ret, reg_cells; ++ u32 val; ++ ++ /* We do not retrieve the SMC syscon when parsing old DTs. */ ++ if (nc->caps->legacy_of_bindings) ++ return atmel_nand_controller_legacy_add_nands(nc); ++ ++ np = dev->of_node; ++ ++ ret = of_property_read_u32(np, "#address-cells", &val); ++ if (ret) { ++ dev_err(dev, "missing #address-cells property\n"); ++ return ret; ++ } ++ ++ reg_cells = val; ++ ++ ret = of_property_read_u32(np, "#size-cells", &val); ++ if (ret) { ++ dev_err(dev, "missing #address-cells property\n"); ++ return ret; ++ } ++ ++ reg_cells += val; ++ ++ for_each_child_of_node(np, nand_np) { ++ struct atmel_nand *nand; ++ ++ nand = atmel_nand_create(nc, nand_np, reg_cells); ++ if (IS_ERR(nand)) { ++ ret = PTR_ERR(nand); ++ goto err; ++ } ++ ++ ret = atmel_nand_controller_add_nand(nc, nand); ++ if (ret) ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ atmel_nand_controller_remove_nands(nc); ++ ++ return ret; ++} ++ ++static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc) ++{ ++ if (nc->dmac) ++ dma_release_channel(nc->dmac); ++ ++ clk_put(nc->mck); ++} ++ ++static const struct of_device_id atmel_matrix_of_ids[] = { ++ { ++ .compatible = "atmel,at91sam9260-matrix", ++ .data = (void *)AT91SAM9260_MATRIX_EBICSA, ++ }, ++ { ++ .compatible = "atmel,at91sam9261-matrix", ++ .data = (void *)AT91SAM9261_MATRIX_EBICSA, ++ }, ++ { ++ .compatible = "atmel,at91sam9263-matrix", ++ .data = (void *)AT91SAM9263_MATRIX_EBI0CSA, ++ }, ++ { ++ .compatible = "atmel,at91sam9rl-matrix", ++ .data = (void *)AT91SAM9RL_MATRIX_EBICSA, ++ }, ++ { ++ .compatible = "atmel,at91sam9g45-matrix", ++ .data = (void *)AT91SAM9G45_MATRIX_EBICSA, ++ }, ++ { ++ .compatible = "atmel,at91sam9n12-matrix", ++ .data = (void *)AT91SAM9N12_MATRIX_EBICSA, ++ }, ++ { ++ .compatible = "atmel,at91sam9x5-matrix", ++ .data = (void *)AT91SAM9X5_MATRIX_EBICSA, ++ }, ++ { /* sentinel */ }, ++}; ++ ++static int atmel_nand_controller_init(struct atmel_nand_controller *nc, ++ struct platform_device *pdev, ++ const struct atmel_nand_controller_caps *caps) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ int ret; ++ ++ nand_controller_init(&nc->base); ++ INIT_LIST_HEAD(&nc->chips); ++ nc->dev = dev; ++ nc->caps = caps; ++ ++ platform_set_drvdata(pdev, nc); ++ ++ nc->pmecc = devm_atmel_pmecc_get(dev); ++ if (IS_ERR(nc->pmecc)) { ++ ret = PTR_ERR(nc->pmecc); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "Could not get PMECC object (err = %d)\n", ++ ret); ++ return ret; ++ } ++ ++ if (nc->caps->has_dma && !atmel_nand_avoid_dma) { ++ dma_cap_mask_t mask; ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_MEMCPY, mask); ++ ++ nc->dmac = dma_request_channel(mask, NULL, NULL); ++ if (!nc->dmac) ++ dev_err(nc->dev, "Failed to request DMA channel\n"); ++ } ++ ++ /* We do not retrieve the SMC syscon when parsing old DTs. */ ++ if (nc->caps->legacy_of_bindings) ++ return 0; ++ ++ nc->mck = of_clk_get(dev->parent->of_node, 0); ++ if (IS_ERR(nc->mck)) { ++ dev_err(dev, "Failed to retrieve MCK clk\n"); ++ return PTR_ERR(nc->mck); ++ } ++ ++ np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); ++ if (!np) { ++ dev_err(dev, "Missing or invalid atmel,smc property\n"); ++ return -EINVAL; ++ } ++ ++ nc->smc = syscon_node_to_regmap(np); ++ of_node_put(np); ++ if (IS_ERR(nc->smc)) { ++ ret = PTR_ERR(nc->smc); ++ dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) ++{ ++ struct device *dev = nc->base.dev; ++ const struct of_device_id *match; ++ struct device_node *np; ++ int ret; ++ ++ /* We do not retrieve the matrix syscon when parsing old DTs. */ ++ if (nc->base.caps->legacy_of_bindings) ++ return 0; ++ ++ np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0); ++ if (!np) ++ return 0; ++ ++ match = of_match_node(atmel_matrix_of_ids, np); ++ if (!match) { ++ of_node_put(np); ++ return 0; ++ } ++ ++ nc->matrix = syscon_node_to_regmap(np); ++ of_node_put(np); ++ if (IS_ERR(nc->matrix)) { ++ ret = PTR_ERR(nc->matrix); ++ dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret); ++ return ret; ++ } ++ ++ nc->ebi_csa_offs = (unsigned int)match->data; ++ ++ /* ++ * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 ++ * add 4 to ->ebi_csa_offs. ++ */ ++ if (of_device_is_compatible(dev->parent->of_node, ++ "atmel,at91sam9263-ebi1")) ++ nc->ebi_csa_offs += 4; ++ ++ return 0; ++} ++ ++static int ++atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) ++{ ++ struct regmap_config regmap_conf = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ }; ++ ++ struct device *dev = nc->base.dev; ++ struct device_node *nand_np, *nfc_np; ++ void __iomem *iomem; ++ struct resource res; ++ int ret; ++ ++ nand_np = dev->of_node; ++ nfc_np = of_find_compatible_node(dev->of_node, NULL, ++ "atmel,sama5d3-nfc"); ++ ++ nc->clk = of_clk_get(nfc_np, 0); ++ if (IS_ERR(nc->clk)) { ++ ret = PTR_ERR(nc->clk); ++ dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(nc->clk); ++ if (ret) { ++ dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ nc->irq = of_irq_get(nand_np, 0); ++ if (nc->irq <= 0) { ++ ret = nc->irq ?: -ENXIO; ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "Failed to get IRQ number (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ ret = of_address_to_resource(nfc_np, 0, &res); ++ if (ret) { ++ dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ iomem = devm_ioremap_resource(dev, &res); ++ if (IS_ERR(iomem)) { ++ ret = PTR_ERR(iomem); ++ goto out; ++ } ++ ++ regmap_conf.name = "nfc-io"; ++ regmap_conf.max_register = resource_size(&res) - 4; ++ nc->io = devm_regmap_init_mmio(dev, iomem, ®map_conf); ++ if (IS_ERR(nc->io)) { ++ ret = PTR_ERR(nc->io); ++ dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ ret = of_address_to_resource(nfc_np, 1, &res); ++ if (ret) { ++ dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ iomem = devm_ioremap_resource(dev, &res); ++ if (IS_ERR(iomem)) { ++ ret = PTR_ERR(iomem); ++ goto out; ++ } ++ ++ regmap_conf.name = "smc"; ++ regmap_conf.max_register = resource_size(&res) - 4; ++ nc->base.smc = devm_regmap_init_mmio(dev, iomem, ®map_conf); ++ if (IS_ERR(nc->base.smc)) { ++ ret = PTR_ERR(nc->base.smc); ++ dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ ret = of_address_to_resource(nfc_np, 2, &res); ++ if (ret) { ++ dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n", ++ ret); ++ goto out; ++ } ++ ++ nc->sram.virt = devm_ioremap_resource(dev, &res); ++ if (IS_ERR(nc->sram.virt)) { ++ ret = PTR_ERR(nc->sram.virt); ++ goto out; ++ } ++ ++ nc->sram.dma = res.start; ++ ++out: ++ of_node_put(nfc_np); ++ ++ return ret; ++} ++ ++static int ++atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) ++{ ++ struct device *dev = nc->base.dev; ++ struct device_node *np; ++ int ret; ++ ++ np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); ++ if (!np) { ++ dev_err(dev, "Missing or invalid atmel,smc property\n"); ++ return -EINVAL; ++ } ++ ++ nc->hsmc_layout = atmel_hsmc_get_reg_layout(np); ++ ++ nc->irq = of_irq_get(np, 0); ++ of_node_put(np); ++ if (nc->irq <= 0) { ++ ret = nc->irq ?: -ENXIO; ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "Failed to get IRQ number (err = %d)\n", ++ ret); ++ return ret; ++ } ++ ++ np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); ++ if (!np) { ++ dev_err(dev, "Missing or invalid atmel,nfc-io property\n"); ++ return -EINVAL; ++ } ++ ++ nc->io = syscon_node_to_regmap(np); ++ of_node_put(np); ++ if (IS_ERR(nc->io)) { ++ ret = PTR_ERR(nc->io); ++ dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret); ++ return ret; ++ } ++ ++ nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node, ++ "atmel,nfc-sram", 0); ++ if (!nc->sram.pool) { ++ dev_err(nc->base.dev, "Missing SRAM\n"); ++ return -ENOMEM; ++ } ++ ++ nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool, ++ ATMEL_NFC_SRAM_SIZE, ++ &nc->sram.dma); ++ if (!nc->sram.virt) { ++ dev_err(nc->base.dev, ++ "Could not allocate memory from the NFC SRAM pool\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int ++atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) ++{ ++ struct atmel_hsmc_nand_controller *hsmc_nc; ++ int ret; ++ ++ ret = atmel_nand_controller_remove_nands(nc); ++ if (ret) ++ return ret; ++ ++ hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base); ++ if (hsmc_nc->sram.pool) ++ gen_pool_free(hsmc_nc->sram.pool, ++ (unsigned long)hsmc_nc->sram.virt, ++ ATMEL_NFC_SRAM_SIZE); ++ ++ if (hsmc_nc->clk) { ++ clk_disable_unprepare(hsmc_nc->clk); ++ clk_put(hsmc_nc->clk); ++ } ++ ++ atmel_nand_controller_cleanup(nc); ++ ++ return 0; ++} ++ ++static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev, ++ const struct atmel_nand_controller_caps *caps) ++{ ++ struct device *dev = &pdev->dev; ++ struct atmel_hsmc_nand_controller *nc; ++ int ret; ++ ++ nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); ++ if (!nc) ++ return -ENOMEM; ++ ++ ret = atmel_nand_controller_init(&nc->base, pdev, caps); ++ if (ret) ++ return ret; ++ ++ if (caps->legacy_of_bindings) ++ ret = atmel_hsmc_nand_controller_legacy_init(nc); ++ else ++ ret = atmel_hsmc_nand_controller_init(nc); ++ ++ if (ret) ++ return ret; ++ ++ /* Make sure all irqs are masked before registering our IRQ handler. */ ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); ++ ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt, ++ IRQF_SHARED, "nfc", nc); ++ if (ret) { ++ dev_err(dev, ++ "Could not get register NFC interrupt handler (err = %d)\n", ++ ret); ++ goto err; ++ } ++ ++ /* Initial NFC configuration. */ ++ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG, ++ ATMEL_HSMC_NFC_CFG_DTO_MAX); ++ ++ ret = atmel_nand_controller_add_nands(&nc->base); ++ if (ret) ++ goto err; ++ ++ return 0; ++ ++err: ++ atmel_hsmc_nand_controller_remove(&nc->base); ++ ++ return ret; ++} ++ ++static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { ++ .probe = atmel_hsmc_nand_controller_probe, ++ .remove = atmel_hsmc_nand_controller_remove, ++ .ecc_init = atmel_hsmc_nand_ecc_init, ++ .nand_init = atmel_hsmc_nand_init, ++ .setup_data_interface = atmel_hsmc_nand_setup_data_interface, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { ++ .has_dma = true, ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &atmel_hsmc_nc_ops, ++}; ++ ++/* Only used to parse old bindings. */ ++static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = { ++ .has_dma = true, ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &atmel_hsmc_nc_ops, ++ .legacy_of_bindings = true, ++}; ++ ++static int atmel_smc_nand_controller_probe(struct platform_device *pdev, ++ const struct atmel_nand_controller_caps *caps) ++{ ++ struct device *dev = &pdev->dev; ++ struct atmel_smc_nand_controller *nc; ++ int ret; ++ ++ nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); ++ if (!nc) ++ return -ENOMEM; ++ ++ ret = atmel_nand_controller_init(&nc->base, pdev, caps); ++ if (ret) ++ return ret; ++ ++ ret = atmel_smc_nand_controller_init(nc); ++ if (ret) ++ return ret; ++ ++ return atmel_nand_controller_add_nands(&nc->base); ++} ++ ++static int ++atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc) ++{ ++ int ret; ++ ++ ret = atmel_nand_controller_remove_nands(nc); ++ if (ret) ++ return ret; ++ ++ atmel_nand_controller_cleanup(nc); ++ ++ return 0; ++} ++ ++/* ++ * The SMC reg layout of at91rm9200 is completely different which prevents us ++ * from re-using atmel_smc_nand_setup_data_interface() for the ++ * ->setup_data_interface() hook. ++ * At this point, there's no support for the at91rm9200 SMC IP, so we leave ++ * ->setup_data_interface() unassigned. ++ */ ++static const struct atmel_nand_controller_ops at91rm9200_nc_ops = { ++ .probe = atmel_smc_nand_controller_probe, ++ .remove = atmel_smc_nand_controller_remove, ++ .ecc_init = atmel_nand_ecc_init, ++ .nand_init = atmel_smc_nand_init, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = { ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &at91rm9200_nc_ops, ++}; ++ ++static const struct atmel_nand_controller_ops atmel_smc_nc_ops = { ++ .probe = atmel_smc_nand_controller_probe, ++ .remove = atmel_smc_nand_controller_remove, ++ .ecc_init = atmel_nand_ecc_init, ++ .nand_init = atmel_smc_nand_init, ++ .setup_data_interface = atmel_smc_nand_setup_data_interface, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &atmel_smc_nc_ops, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = { ++ .ale_offs = BIT(22), ++ .cle_offs = BIT(21), ++ .ops = &atmel_smc_nc_ops, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = { ++ .has_dma = true, ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &atmel_smc_nc_ops, ++}; ++ ++/* Only used to parse old bindings. */ ++static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = { ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &atmel_smc_nc_ops, ++ .legacy_of_bindings = true, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = { ++ .ale_offs = BIT(22), ++ .cle_offs = BIT(21), ++ .ops = &atmel_smc_nc_ops, ++ .legacy_of_bindings = true, ++}; ++ ++static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = { ++ .has_dma = true, ++ .ale_offs = BIT(21), ++ .cle_offs = BIT(22), ++ .ops = &atmel_smc_nc_ops, ++ .legacy_of_bindings = true, ++}; ++ ++static const struct of_device_id atmel_nand_controller_of_ids[] = { ++ { ++ .compatible = "atmel,at91rm9200-nand-controller", ++ .data = &atmel_rm9200_nc_caps, ++ }, ++ { ++ .compatible = "atmel,at91sam9260-nand-controller", ++ .data = &atmel_sam9260_nc_caps, ++ }, ++ { ++ .compatible = "atmel,at91sam9261-nand-controller", ++ .data = &atmel_sam9261_nc_caps, ++ }, ++ { ++ .compatible = "atmel,at91sam9g45-nand-controller", ++ .data = &atmel_sam9g45_nc_caps, ++ }, ++ { ++ .compatible = "atmel,sama5d3-nand-controller", ++ .data = &atmel_sama5_nc_caps, ++ }, ++ /* Support for old/deprecated bindings: */ ++ { ++ .compatible = "atmel,at91rm9200-nand", ++ .data = &atmel_rm9200_nand_caps, ++ }, ++ { ++ .compatible = "atmel,sama5d4-nand", ++ .data = &atmel_rm9200_nand_caps, ++ }, ++ { ++ .compatible = "atmel,sama5d2-nand", ++ .data = &atmel_rm9200_nand_caps, ++ }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids); ++ ++static int atmel_nand_controller_probe(struct platform_device *pdev) ++{ ++ const struct atmel_nand_controller_caps *caps; ++ ++ if (pdev->id_entry) ++ caps = (void *)pdev->id_entry->driver_data; ++ else ++ caps = of_device_get_match_data(&pdev->dev); ++ ++ if (!caps) { ++ dev_err(&pdev->dev, "Could not retrieve NFC caps\n"); ++ return -EINVAL; ++ } ++ ++ if (caps->legacy_of_bindings) { ++ u32 ale_offs = 21; ++ ++ /* ++ * If we are parsing legacy DT props and the DT contains a ++ * valid NFC node, forward the request to the sama5 logic. ++ */ ++ if (of_find_compatible_node(pdev->dev.of_node, NULL, ++ "atmel,sama5d3-nfc")) ++ caps = &atmel_sama5_nand_caps; ++ ++ /* ++ * Even if the compatible says we are dealing with an ++ * at91rm9200 controller, the atmel,nand-has-dma specify that ++ * this controller supports DMA, which means we are in fact ++ * dealing with an at91sam9g45+ controller. ++ */ ++ if (!caps->has_dma && ++ of_property_read_bool(pdev->dev.of_node, ++ "atmel,nand-has-dma")) ++ caps = &atmel_sam9g45_nand_caps; ++ ++ /* ++ * All SoCs except the at91sam9261 are assigning ALE to A21 and ++ * CLE to A22. If atmel,nand-addr-offset != 21 this means we're ++ * actually dealing with an at91sam9261 controller. ++ */ ++ of_property_read_u32(pdev->dev.of_node, ++ "atmel,nand-addr-offset", &ale_offs); ++ if (ale_offs != 21) ++ caps = &atmel_sam9261_nand_caps; ++ } ++ ++ return caps->ops->probe(pdev, caps); ++} ++ ++static int atmel_nand_controller_remove(struct platform_device *pdev) ++{ ++ struct atmel_nand_controller *nc = platform_get_drvdata(pdev); ++ ++ return nc->caps->ops->remove(nc); ++} ++ ++static __maybe_unused int atmel_nand_controller_resume(struct device *dev) ++{ ++ struct atmel_nand_controller *nc = dev_get_drvdata(dev); ++ struct atmel_nand *nand; ++ ++ list_for_each_entry(nand, &nc->chips, node) { ++ int i; ++ ++ for (i = 0; i < nand->numcs; i++) ++ nand_reset(&nand->base, i); ++ } ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL, ++ atmel_nand_controller_resume); ++ ++static struct platform_driver atmel_nand_controller_driver = { ++ .driver = { ++ .name = "atmel-nand-controller", ++ .of_match_table = of_match_ptr(atmel_nand_controller_of_ids), ++ .pm = &atmel_nand_controller_pm_ops, ++ }, ++ .probe = atmel_nand_controller_probe, ++ .remove = atmel_nand_controller_remove, ++}; ++module_platform_driver(atmel_nand_controller_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Boris Brezillon "); ++MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs"); ++MODULE_ALIAS("platform:atmel-nand-controller"); +diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c +new file mode 100644 +index 00000000..4124bf9 +--- /dev/null ++++ b/drivers/mtd/nand/raw/atmel/pmecc.c +@@ -0,0 +1,1011 @@ ++/* ++ * Copyright 2017 ATMEL ++ * Copyright 2017 Free Electrons ++ * ++ * Author: Boris Brezillon ++ * ++ * Derived from the atmel_nand.c driver which contained the following ++ * copyrights: ++ * ++ * Copyright 2003 Rick Bronson ++ * ++ * Derived from drivers/mtd/nand/autcpu12.c ++ * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * Derived from drivers/mtd/spia.c ++ * Copyright 2000 Steven J. Hill (sjhill@cotw.com) ++ * ++ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 ++ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 ++ * ++ * Derived from Das U-Boot source code ++ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) ++ * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas ++ * ++ * Add Programmable Multibit ECC support for various AT91 SoC ++ * Copyright 2012 ATMEL, Hong Xu ++ * ++ * Add Nand Flash Controller support for SAMA5 SoC ++ * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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. ++ * ++ * The PMECC is an hardware assisted BCH engine, which means part of the ++ * ECC algorithm is left to the software. The hardware/software repartition ++ * is explained in the "PMECC Controller Functional Description" chapter in ++ * Atmel datasheets, and some of the functions in this file are directly ++ * implementing the algorithms described in the "Software Implementation" ++ * sub-section. ++ * ++ * TODO: it seems that the software BCH implementation in lib/bch.c is already ++ * providing some of the logic we are implementing here. It would be smart ++ * to expose the needed lib/bch.c helpers/functions and re-use them here. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pmecc.h" ++ ++/* Galois field dimension */ ++#define PMECC_GF_DIMENSION_13 13 ++#define PMECC_GF_DIMENSION_14 14 ++ ++/* Primitive Polynomial used by PMECC */ ++#define PMECC_GF_13_PRIMITIVE_POLY 0x201b ++#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 ++ ++#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 ++#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 ++ ++/* Time out value for reading PMECC status register */ ++#define PMECC_MAX_TIMEOUT_MS 100 ++ ++/* PMECC Register Definitions */ ++#define ATMEL_PMECC_CFG 0x0 ++#define PMECC_CFG_BCH_STRENGTH(x) (x) ++#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0) ++#define PMECC_CFG_SECTOR512 (0 << 4) ++#define PMECC_CFG_SECTOR1024 (1 << 4) ++#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8) ++#define PMECC_CFG_READ_OP (0 << 12) ++#define PMECC_CFG_WRITE_OP (1 << 12) ++#define PMECC_CFG_SPARE_ENABLE BIT(16) ++#define PMECC_CFG_AUTO_ENABLE BIT(20) ++ ++#define ATMEL_PMECC_SAREA 0x4 ++#define ATMEL_PMECC_SADDR 0x8 ++#define ATMEL_PMECC_EADDR 0xc ++ ++#define ATMEL_PMECC_CLK 0x10 ++#define PMECC_CLK_133MHZ (2 << 0) ++ ++#define ATMEL_PMECC_CTRL 0x14 ++#define PMECC_CTRL_RST BIT(0) ++#define PMECC_CTRL_DATA BIT(1) ++#define PMECC_CTRL_USER BIT(2) ++#define PMECC_CTRL_ENABLE BIT(4) ++#define PMECC_CTRL_DISABLE BIT(5) ++ ++#define ATMEL_PMECC_SR 0x18 ++#define PMECC_SR_BUSY BIT(0) ++#define PMECC_SR_ENABLE BIT(4) ++ ++#define ATMEL_PMECC_IER 0x1c ++#define ATMEL_PMECC_IDR 0x20 ++#define ATMEL_PMECC_IMR 0x24 ++#define ATMEL_PMECC_ISR 0x28 ++#define PMECC_ERROR_INT BIT(0) ++ ++#define ATMEL_PMECC_ECC(sector, n) \ ++ ((((sector) + 1) * 0x40) + (n)) ++ ++#define ATMEL_PMECC_REM(sector, n) \ ++ ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200) ++ ++/* PMERRLOC Register Definitions */ ++#define ATMEL_PMERRLOC_ELCFG 0x0 ++#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) ++#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) ++#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) ++ ++#define ATMEL_PMERRLOC_ELPRIM 0x4 ++#define ATMEL_PMERRLOC_ELEN 0x8 ++#define ATMEL_PMERRLOC_ELDIS 0xc ++#define PMERRLOC_DISABLE BIT(0) ++ ++#define ATMEL_PMERRLOC_ELSR 0x10 ++#define PMERRLOC_ELSR_BUSY BIT(0) ++ ++#define ATMEL_PMERRLOC_ELIER 0x14 ++#define ATMEL_PMERRLOC_ELIDR 0x18 ++#define ATMEL_PMERRLOC_ELIMR 0x1c ++#define ATMEL_PMERRLOC_ELISR 0x20 ++#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8) ++#define PMERRLOC_CALC_DONE BIT(0) ++ ++#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28) ++ ++#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs)) ++ ++struct atmel_pmecc_gf_tables { ++ u16 *alpha_to; ++ u16 *index_of; ++}; ++ ++struct atmel_pmecc_caps { ++ const int *strengths; ++ int nstrengths; ++ int el_offset; ++ bool correct_erased_chunks; ++}; ++ ++struct atmel_pmecc { ++ struct device *dev; ++ const struct atmel_pmecc_caps *caps; ++ ++ struct { ++ void __iomem *base; ++ void __iomem *errloc; ++ } regs; ++ ++ struct mutex lock; ++}; ++ ++struct atmel_pmecc_user_conf_cache { ++ u32 cfg; ++ u32 sarea; ++ u32 saddr; ++ u32 eaddr; ++}; ++ ++struct atmel_pmecc_user { ++ struct atmel_pmecc_user_conf_cache cache; ++ struct atmel_pmecc *pmecc; ++ const struct atmel_pmecc_gf_tables *gf_tables; ++ int eccbytes; ++ s16 *partial_syn; ++ s16 *si; ++ s16 *lmu; ++ s16 *smu; ++ s32 *mu; ++ s32 *dmu; ++ s32 *delta; ++ u32 isr; ++}; ++ ++static DEFINE_MUTEX(pmecc_gf_tables_lock); ++static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512; ++static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024; ++ ++static inline int deg(unsigned int poly) ++{ ++ /* polynomial degree is the most-significant bit index */ ++ return fls(poly) - 1; ++} ++ ++static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly, ++ struct atmel_pmecc_gf_tables *gf_tables) ++{ ++ unsigned int i, x = 1; ++ const unsigned int k = BIT(deg(poly)); ++ unsigned int nn = BIT(mm) - 1; ++ ++ /* primitive polynomial must be of degree m */ ++ if (k != (1u << mm)) ++ return -EINVAL; ++ ++ for (i = 0; i < nn; i++) { ++ gf_tables->alpha_to[i] = x; ++ gf_tables->index_of[x] = i; ++ if (i && (x == 1)) ++ /* polynomial is not primitive (a^i=1 with 0alpha_to[nn] = 1; ++ gf_tables->index_of[0] = 0; ++ ++ return 0; ++} ++ ++static const struct atmel_pmecc_gf_tables * ++atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req) ++{ ++ struct atmel_pmecc_gf_tables *gf_tables; ++ unsigned int poly, degree, table_size; ++ int ret; ++ ++ if (req->ecc.sectorsize == 512) { ++ degree = PMECC_GF_DIMENSION_13; ++ poly = PMECC_GF_13_PRIMITIVE_POLY; ++ table_size = PMECC_LOOKUP_TABLE_SIZE_512; ++ } else { ++ degree = PMECC_GF_DIMENSION_14; ++ poly = PMECC_GF_14_PRIMITIVE_POLY; ++ table_size = PMECC_LOOKUP_TABLE_SIZE_1024; ++ } ++ ++ gf_tables = kzalloc(sizeof(*gf_tables) + ++ (2 * table_size * sizeof(u16)), ++ GFP_KERNEL); ++ if (!gf_tables) ++ return ERR_PTR(-ENOMEM); ++ ++ gf_tables->alpha_to = (void *)(gf_tables + 1); ++ gf_tables->index_of = gf_tables->alpha_to + table_size; ++ ++ ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables); ++ if (ret) { ++ kfree(gf_tables); ++ return ERR_PTR(ret); ++ } ++ ++ return gf_tables; ++} ++ ++static const struct atmel_pmecc_gf_tables * ++atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req) ++{ ++ const struct atmel_pmecc_gf_tables **gf_tables, *ret; ++ ++ mutex_lock(&pmecc_gf_tables_lock); ++ if (req->ecc.sectorsize == 512) ++ gf_tables = &pmecc_gf_tables_512; ++ else ++ gf_tables = &pmecc_gf_tables_1024; ++ ++ ret = *gf_tables; ++ ++ if (!ret) { ++ ret = atmel_pmecc_create_gf_tables(req); ++ if (!IS_ERR(ret)) ++ *gf_tables = ret; ++ } ++ mutex_unlock(&pmecc_gf_tables_lock); ++ ++ return ret; ++} ++ ++static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc, ++ struct atmel_pmecc_user_req *req) ++{ ++ int i, max_eccbytes, eccbytes = 0, eccstrength = 0; ++ ++ if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0) ++ return -EINVAL; ++ ++ if (req->ecc.ooboffset >= 0 && ++ req->ecc.ooboffset + req->ecc.bytes > req->oobsize) ++ return -EINVAL; ++ ++ if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) { ++ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) ++ return -EINVAL; ++ ++ if (req->pagesize > 512) ++ req->ecc.sectorsize = 1024; ++ else ++ req->ecc.sectorsize = 512; ++ } ++ ++ if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024) ++ return -EINVAL; ++ ++ if (req->pagesize % req->ecc.sectorsize) ++ return -EINVAL; ++ ++ req->ecc.nsectors = req->pagesize / req->ecc.sectorsize; ++ ++ max_eccbytes = req->ecc.bytes; ++ ++ for (i = 0; i < pmecc->caps->nstrengths; i++) { ++ int nbytes, strength = pmecc->caps->strengths[i]; ++ ++ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH && ++ strength < req->ecc.strength) ++ continue; ++ ++ nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize), ++ 8); ++ nbytes *= req->ecc.nsectors; ++ ++ if (nbytes > max_eccbytes) ++ break; ++ ++ eccstrength = strength; ++ eccbytes = nbytes; ++ ++ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) ++ break; ++ } ++ ++ if (!eccstrength) ++ return -EINVAL; ++ ++ req->ecc.bytes = eccbytes; ++ req->ecc.strength = eccstrength; ++ ++ if (req->ecc.ooboffset < 0) ++ req->ecc.ooboffset = req->oobsize - eccbytes; ++ ++ return 0; ++} ++ ++struct atmel_pmecc_user * ++atmel_pmecc_create_user(struct atmel_pmecc *pmecc, ++ struct atmel_pmecc_user_req *req) ++{ ++ struct atmel_pmecc_user *user; ++ const struct atmel_pmecc_gf_tables *gf_tables; ++ int strength, size, ret; ++ ++ ret = atmel_pmecc_prepare_user_req(pmecc, req); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ size = sizeof(*user); ++ size = ALIGN(size, sizeof(u16)); ++ /* Reserve space for partial_syn, si and smu */ ++ size += ((2 * req->ecc.strength) + 1) * sizeof(u16) * ++ (2 + req->ecc.strength + 2); ++ /* Reserve space for lmu. */ ++ size += (req->ecc.strength + 1) * sizeof(u16); ++ /* Reserve space for mu, dmu and delta. */ ++ size = ALIGN(size, sizeof(s32)); ++ size += (req->ecc.strength + 1) * sizeof(s32) * 3; ++ ++ user = kzalloc(size, GFP_KERNEL); ++ if (!user) ++ return ERR_PTR(-ENOMEM); ++ ++ user->pmecc = pmecc; ++ ++ user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16)); ++ user->si = user->partial_syn + ((2 * req->ecc.strength) + 1); ++ user->lmu = user->si + ((2 * req->ecc.strength) + 1); ++ user->smu = user->lmu + (req->ecc.strength + 1); ++ user->mu = (s32 *)PTR_ALIGN(user->smu + ++ (((2 * req->ecc.strength) + 1) * ++ (req->ecc.strength + 2)), ++ sizeof(s32)); ++ user->dmu = user->mu + req->ecc.strength + 1; ++ user->delta = user->dmu + req->ecc.strength + 1; ++ ++ gf_tables = atmel_pmecc_get_gf_tables(req); ++ if (IS_ERR(gf_tables)) { ++ kfree(user); ++ return ERR_CAST(gf_tables); ++ } ++ ++ user->gf_tables = gf_tables; ++ ++ user->eccbytes = req->ecc.bytes / req->ecc.nsectors; ++ ++ for (strength = 0; strength < pmecc->caps->nstrengths; strength++) { ++ if (pmecc->caps->strengths[strength] == req->ecc.strength) ++ break; ++ } ++ ++ user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) | ++ PMECC_CFG_NSECTORS(req->ecc.nsectors); ++ ++ if (req->ecc.sectorsize == 1024) ++ user->cache.cfg |= PMECC_CFG_SECTOR1024; ++ ++ user->cache.sarea = req->oobsize - 1; ++ user->cache.saddr = req->ecc.ooboffset; ++ user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1; ++ ++ return user; ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_create_user); ++ ++void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user) ++{ ++ kfree(user); ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user); ++ ++static int get_strength(struct atmel_pmecc_user *user) ++{ ++ const int *strengths = user->pmecc->caps->strengths; ++ ++ return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK]; ++} ++ ++static int get_sectorsize(struct atmel_pmecc_user *user) ++{ ++ return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512; ++} ++ ++static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector) ++{ ++ int strength = get_strength(user); ++ u32 value; ++ int i; ++ ++ /* Fill odd syndromes */ ++ for (i = 0; i < strength; i++) { ++ value = readl_relaxed(user->pmecc->regs.base + ++ ATMEL_PMECC_REM(sector, i / 2)); ++ if (i & 1) ++ value >>= 16; ++ ++ user->partial_syn[(2 * i) + 1] = value; ++ } ++} ++ ++static void atmel_pmecc_substitute(struct atmel_pmecc_user *user) ++{ ++ int degree = get_sectorsize(user) == 512 ? 13 : 14; ++ int cw_len = BIT(degree) - 1; ++ int strength = get_strength(user); ++ s16 *alpha_to = user->gf_tables->alpha_to; ++ s16 *index_of = user->gf_tables->index_of; ++ s16 *partial_syn = user->partial_syn; ++ s16 *si; ++ int i, j; ++ ++ /* ++ * si[] is a table that holds the current syndrome value, ++ * an element of that table belongs to the field ++ */ ++ si = user->si; ++ ++ memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1)); ++ ++ /* Computation 2t syndromes based on S(x) */ ++ /* Odd syndromes */ ++ for (i = 1; i < 2 * strength; i += 2) { ++ for (j = 0; j < degree; j++) { ++ if (partial_syn[i] & BIT(j)) ++ si[i] = alpha_to[i * j] ^ si[i]; ++ } ++ } ++ /* Even syndrome = (Odd syndrome) ** 2 */ ++ for (i = 2, j = 1; j <= strength; i = ++j << 1) { ++ if (si[j] == 0) { ++ si[i] = 0; ++ } else { ++ s16 tmp; ++ ++ tmp = index_of[si[j]]; ++ tmp = (tmp * 2) % cw_len; ++ si[i] = alpha_to[tmp]; ++ } ++ } ++} ++ ++static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user) ++{ ++ s16 *lmu = user->lmu; ++ s16 *si = user->si; ++ s32 *mu = user->mu; ++ s32 *dmu = user->dmu; ++ s32 *delta = user->delta; ++ int degree = get_sectorsize(user) == 512 ? 13 : 14; ++ int cw_len = BIT(degree) - 1; ++ int strength = get_strength(user); ++ int num = 2 * strength + 1; ++ s16 *index_of = user->gf_tables->index_of; ++ s16 *alpha_to = user->gf_tables->alpha_to; ++ int i, j, k; ++ u32 dmu_0_count, tmp; ++ s16 *smu = user->smu; ++ ++ /* index of largest delta */ ++ int ro; ++ int largest; ++ int diff; ++ ++ dmu_0_count = 0; ++ ++ /* First Row */ ++ ++ /* Mu */ ++ mu[0] = -1; ++ ++ memset(smu, 0, sizeof(s16) * num); ++ smu[0] = 1; ++ ++ /* discrepancy set to 1 */ ++ dmu[0] = 1; ++ /* polynom order set to 0 */ ++ lmu[0] = 0; ++ delta[0] = (mu[0] * 2 - lmu[0]) >> 1; ++ ++ /* Second Row */ ++ ++ /* Mu */ ++ mu[1] = 0; ++ /* Sigma(x) set to 1 */ ++ memset(&smu[num], 0, sizeof(s16) * num); ++ smu[num] = 1; ++ ++ /* discrepancy set to S1 */ ++ dmu[1] = si[1]; ++ ++ /* polynom order set to 0 */ ++ lmu[1] = 0; ++ ++ delta[1] = (mu[1] * 2 - lmu[1]) >> 1; ++ ++ /* Init the Sigma(x) last row */ ++ memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num); ++ ++ for (i = 1; i <= strength; i++) { ++ mu[i + 1] = i << 1; ++ /* Begin Computing Sigma (Mu+1) and L(mu) */ ++ /* check if discrepancy is set to 0 */ ++ if (dmu[i] == 0) { ++ dmu_0_count++; ++ ++ tmp = ((strength - (lmu[i] >> 1) - 1) / 2); ++ if ((strength - (lmu[i] >> 1) - 1) & 0x1) ++ tmp += 2; ++ else ++ tmp += 1; ++ ++ if (dmu_0_count == tmp) { ++ for (j = 0; j <= (lmu[i] >> 1) + 1; j++) ++ smu[(strength + 1) * num + j] = ++ smu[i * num + j]; ++ ++ lmu[strength + 1] = lmu[i]; ++ return; ++ } ++ ++ /* copy polynom */ ++ for (j = 0; j <= lmu[i] >> 1; j++) ++ smu[(i + 1) * num + j] = smu[i * num + j]; ++ ++ /* copy previous polynom order to the next */ ++ lmu[i + 1] = lmu[i]; ++ } else { ++ ro = 0; ++ largest = -1; ++ /* find largest delta with dmu != 0 */ ++ for (j = 0; j < i; j++) { ++ if ((dmu[j]) && (delta[j] > largest)) { ++ largest = delta[j]; ++ ro = j; ++ } ++ } ++ ++ /* compute difference */ ++ diff = (mu[i] - mu[ro]); ++ ++ /* Compute degree of the new smu polynomial */ ++ if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) ++ lmu[i + 1] = lmu[i]; ++ else ++ lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; ++ ++ /* Init smu[i+1] with 0 */ ++ for (k = 0; k < num; k++) ++ smu[(i + 1) * num + k] = 0; ++ ++ /* Compute smu[i+1] */ ++ for (k = 0; k <= lmu[ro] >> 1; k++) { ++ s16 a, b, c; ++ ++ if (!(smu[ro * num + k] && dmu[i])) ++ continue; ++ ++ a = index_of[dmu[i]]; ++ b = index_of[dmu[ro]]; ++ c = index_of[smu[ro * num + k]]; ++ tmp = a + (cw_len - b) + c; ++ a = alpha_to[tmp % cw_len]; ++ smu[(i + 1) * num + (k + diff)] = a; ++ } ++ ++ for (k = 0; k <= lmu[i] >> 1; k++) ++ smu[(i + 1) * num + k] ^= smu[i * num + k]; ++ } ++ ++ /* End Computing Sigma (Mu+1) and L(mu) */ ++ /* In either case compute delta */ ++ delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; ++ ++ /* Do not compute discrepancy for the last iteration */ ++ if (i >= strength) ++ continue; ++ ++ for (k = 0; k <= (lmu[i + 1] >> 1); k++) { ++ tmp = 2 * (i - 1); ++ if (k == 0) { ++ dmu[i + 1] = si[tmp + 3]; ++ } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { ++ s16 a, b, c; ++ ++ a = index_of[smu[(i + 1) * num + k]]; ++ b = si[2 * (i - 1) + 3 - k]; ++ c = index_of[b]; ++ tmp = a + c; ++ tmp %= cw_len; ++ dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1]; ++ } ++ } ++ } ++} ++ ++static int atmel_pmecc_err_location(struct atmel_pmecc_user *user) ++{ ++ int sector_size = get_sectorsize(user); ++ int degree = sector_size == 512 ? 13 : 14; ++ struct atmel_pmecc *pmecc = user->pmecc; ++ int strength = get_strength(user); ++ int ret, roots_nbr, i, err_nbr = 0; ++ int num = (2 * strength) + 1; ++ s16 *smu = user->smu; ++ u32 val; ++ ++ writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS); ++ ++ for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) { ++ writel_relaxed(smu[(strength + 1) * num + i], ++ pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i)); ++ err_nbr++; ++ } ++ ++ val = (err_nbr - 1) << 16; ++ if (sector_size == 1024) ++ val |= 1; ++ ++ writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG); ++ writel((sector_size * 8) + (degree * strength), ++ pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN); ++ ++ ret = readl_relaxed_poll_timeout(pmecc->regs.errloc + ++ ATMEL_PMERRLOC_ELISR, ++ val, val & PMERRLOC_CALC_DONE, 0, ++ PMECC_MAX_TIMEOUT_MS * 1000); ++ if (ret) { ++ dev_err(pmecc->dev, ++ "PMECC: Timeout to calculate error location.\n"); ++ return ret; ++ } ++ ++ roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8; ++ /* Number of roots == degree of smu hence <= cap */ ++ if (roots_nbr == user->lmu[strength + 1] >> 1) ++ return err_nbr - 1; ++ ++ /* ++ * Number of roots does not match the degree of smu ++ * unable to correct error. ++ */ ++ return -EBADMSG; ++} ++ ++int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, ++ void *data, void *ecc) ++{ ++ struct atmel_pmecc *pmecc = user->pmecc; ++ int sectorsize = get_sectorsize(user); ++ int eccbytes = user->eccbytes; ++ int i, nerrors; ++ ++ if (!(user->isr & BIT(sector))) ++ return 0; ++ ++ atmel_pmecc_gen_syndrome(user, sector); ++ atmel_pmecc_substitute(user); ++ atmel_pmecc_get_sigma(user); ++ ++ nerrors = atmel_pmecc_err_location(user); ++ if (nerrors < 0) ++ return nerrors; ++ ++ for (i = 0; i < nerrors; i++) { ++ const char *area; ++ int byte, bit; ++ u32 errpos; ++ u8 *ptr; ++ ++ errpos = readl_relaxed(pmecc->regs.errloc + ++ ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i)); ++ errpos--; ++ ++ byte = errpos / 8; ++ bit = errpos % 8; ++ ++ if (byte < sectorsize) { ++ ptr = data + byte; ++ area = "data"; ++ } else if (byte < sectorsize + eccbytes) { ++ ptr = ecc + byte - sectorsize; ++ area = "ECC"; ++ } else { ++ dev_dbg(pmecc->dev, ++ "Invalid errpos value (%d, max is %d)\n", ++ errpos, (sectorsize + eccbytes) * 8); ++ return -EINVAL; ++ } ++ ++ dev_dbg(pmecc->dev, ++ "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n", ++ area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit))); ++ ++ *ptr ^= BIT(bit); ++ } ++ ++ return nerrors; ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector); ++ ++bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user) ++{ ++ return user->pmecc->caps->correct_erased_chunks; ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks); ++ ++void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, ++ int sector, void *ecc) ++{ ++ struct atmel_pmecc *pmecc = user->pmecc; ++ u8 *ptr = ecc; ++ int i; ++ ++ for (i = 0; i < user->eccbytes; i++) ++ ptr[i] = readb_relaxed(pmecc->regs.base + ++ ATMEL_PMECC_ECC(sector, i)); ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes); ++ ++int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op) ++{ ++ struct atmel_pmecc *pmecc = user->pmecc; ++ u32 cfg; ++ ++ if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) { ++ dev_err(pmecc->dev, "Bad ECC operation!"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&user->pmecc->lock); ++ ++ cfg = user->cache.cfg; ++ if (op == NAND_ECC_WRITE) ++ cfg |= PMECC_CFG_WRITE_OP; ++ else ++ cfg |= PMECC_CFG_AUTO_ENABLE; ++ ++ writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG); ++ writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA); ++ writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR); ++ writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR); ++ ++ writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); ++ writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_enable); ++ ++void atmel_pmecc_disable(struct atmel_pmecc_user *user) ++{ ++ struct atmel_pmecc *pmecc = user->pmecc; ++ ++ writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); ++ writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); ++ mutex_unlock(&user->pmecc->lock); ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_disable); ++ ++int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user) ++{ ++ struct atmel_pmecc *pmecc = user->pmecc; ++ u32 status; ++ int ret; ++ ++ ret = readl_relaxed_poll_timeout(pmecc->regs.base + ++ ATMEL_PMECC_SR, ++ status, !(status & PMECC_SR_BUSY), 0, ++ PMECC_MAX_TIMEOUT_MS * 1000); ++ if (ret) { ++ dev_err(pmecc->dev, ++ "Timeout while waiting for PMECC ready.\n"); ++ return ret; ++ } ++ ++ user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy); ++ ++static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev, ++ const struct atmel_pmecc_caps *caps, ++ int pmecc_res_idx, int errloc_res_idx) ++{ ++ struct device *dev = &pdev->dev; ++ struct atmel_pmecc *pmecc; ++ struct resource *res; ++ ++ pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL); ++ if (!pmecc) ++ return ERR_PTR(-ENOMEM); ++ ++ pmecc->caps = caps; ++ pmecc->dev = dev; ++ mutex_init(&pmecc->lock); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx); ++ pmecc->regs.base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pmecc->regs.base)) ++ return ERR_CAST(pmecc->regs.base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx); ++ pmecc->regs.errloc = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pmecc->regs.errloc)) ++ return ERR_CAST(pmecc->regs.errloc); ++ ++ /* Disable all interrupts before registering the PMECC handler. */ ++ writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); ++ ++ /* Reset the ECC engine */ ++ writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); ++ writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); ++ ++ return pmecc; ++} ++ ++static void devm_atmel_pmecc_put(struct device *dev, void *res) ++{ ++ struct atmel_pmecc **pmecc = res; ++ ++ put_device((*pmecc)->dev); ++} ++ ++static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev, ++ struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct atmel_pmecc *pmecc, **ptr; ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ ++ get_device(&pdev->dev); ++ pmecc = platform_get_drvdata(pdev); ++ ++ *ptr = pmecc; ++ ++ devres_add(userdev, ptr); ++ ++ return pmecc; ++} ++ ++static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 }; ++ ++static struct atmel_pmecc_caps at91sam9g45_caps = { ++ .strengths = atmel_pmecc_strengths, ++ .nstrengths = 5, ++ .el_offset = 0x8c, ++}; ++ ++static struct atmel_pmecc_caps sama5d4_caps = { ++ .strengths = atmel_pmecc_strengths, ++ .nstrengths = 5, ++ .el_offset = 0x8c, ++ .correct_erased_chunks = true, ++}; ++ ++static struct atmel_pmecc_caps sama5d2_caps = { ++ .strengths = atmel_pmecc_strengths, ++ .nstrengths = 6, ++ .el_offset = 0xac, ++ .correct_erased_chunks = true, ++}; ++ ++static const struct of_device_id atmel_pmecc_legacy_match[] = { ++ { .compatible = "atmel,sama5d4-nand", &sama5d4_caps }, ++ { .compatible = "atmel,sama5d2-nand", &sama5d2_caps }, ++ { /* sentinel */ } ++}; ++ ++struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev) ++{ ++ struct atmel_pmecc *pmecc; ++ struct device_node *np; ++ ++ if (!userdev) ++ return ERR_PTR(-EINVAL); ++ ++ if (!userdev->of_node) ++ return NULL; ++ ++ np = of_parse_phandle(userdev->of_node, "ecc-engine", 0); ++ if (np) { ++ pmecc = atmel_pmecc_get_by_node(userdev, np); ++ of_node_put(np); ++ } else { ++ /* ++ * Support old DT bindings: in this case the PMECC iomem ++ * resources are directly defined in the user pdev at position ++ * 1 and 2. Extract all relevant information from there. ++ */ ++ struct platform_device *pdev = to_platform_device(userdev); ++ const struct atmel_pmecc_caps *caps; ++ const struct of_device_id *match; ++ ++ /* No PMECC engine available. */ ++ if (!of_property_read_bool(userdev->of_node, ++ "atmel,has-pmecc")) ++ return NULL; ++ ++ caps = &at91sam9g45_caps; ++ ++ /* Find the caps associated to the NAND dev node. */ ++ match = of_match_node(atmel_pmecc_legacy_match, ++ userdev->of_node); ++ if (match && match->data) ++ caps = match->data; ++ ++ pmecc = atmel_pmecc_create(pdev, caps, 1, 2); ++ } ++ ++ return pmecc; ++} ++EXPORT_SYMBOL(devm_atmel_pmecc_get); ++ ++static const struct of_device_id atmel_pmecc_match[] = { ++ { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps }, ++ { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps }, ++ { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, atmel_pmecc_match); ++ ++static int atmel_pmecc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct atmel_pmecc_caps *caps; ++ struct atmel_pmecc *pmecc; ++ ++ caps = of_device_get_match_data(&pdev->dev); ++ if (!caps) { ++ dev_err(dev, "Invalid caps\n"); ++ return -EINVAL; ++ } ++ ++ pmecc = atmel_pmecc_create(pdev, caps, 0, 1); ++ if (IS_ERR(pmecc)) ++ return PTR_ERR(pmecc); ++ ++ platform_set_drvdata(pdev, pmecc); ++ ++ return 0; ++} ++ ++static struct platform_driver atmel_pmecc_driver = { ++ .driver = { ++ .name = "atmel-pmecc", ++ .of_match_table = of_match_ptr(atmel_pmecc_match), ++ }, ++ .probe = atmel_pmecc_probe, ++}; ++module_platform_driver(atmel_pmecc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Boris Brezillon "); ++MODULE_DESCRIPTION("PMECC engine driver"); ++MODULE_ALIAS("platform:atmel_pmecc"); +diff --git a/drivers/mtd/nand/raw/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h +new file mode 100644 +index 00000000..a8ddbfc +--- /dev/null ++++ b/drivers/mtd/nand/raw/atmel/pmecc.h +@@ -0,0 +1,73 @@ ++/* ++ * © Copyright 2016 ATMEL ++ * © Copyright 2016 Free Electrons ++ * ++ * Author: Boris Brezillon ++ * ++ * Derived from the atmel_nand.c driver which contained the following ++ * copyrights: ++ * ++ * Copyright © 2003 Rick Bronson ++ * ++ * Derived from drivers/mtd/nand/autcpu12.c ++ * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * Derived from drivers/mtd/spia.c ++ * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) ++ * ++ * ++ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 ++ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007 ++ * ++ * Derived from Das U-Boot source code ++ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) ++ * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas ++ * ++ * Add Programmable Multibit ECC support for various AT91 SoC ++ * © Copyright 2012 ATMEL, Hong Xu ++ * ++ * Add Nand Flash Controller support for SAMA5 SoC ++ * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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. ++ * ++ */ ++ ++#ifndef ATMEL_PMECC_H ++#define ATMEL_PMECC_H ++ ++#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0 ++#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0 ++#define ATMEL_PMECC_OOBOFFSET_AUTO -1 ++ ++struct atmel_pmecc_user_req { ++ int pagesize; ++ int oobsize; ++ struct { ++ int strength; ++ int bytes; ++ int sectorsize; ++ int nsectors; ++ int ooboffset; ++ } ecc; ++}; ++ ++struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev); ++ ++struct atmel_pmecc_user * ++atmel_pmecc_create_user(struct atmel_pmecc *pmecc, ++ struct atmel_pmecc_user_req *req); ++void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user); ++ ++int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op); ++void atmel_pmecc_disable(struct atmel_pmecc_user *user); ++int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user); ++int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, ++ void *data, void *ecc); ++bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user); ++void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, ++ int sector, void *ecc); ++ ++#endif /* ATMEL_PMECC_H */ +diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c +new file mode 100644 +index 00000000..8ab827e +--- /dev/null ++++ b/drivers/mtd/nand/raw/au1550nd.c +@@ -0,0 +1,517 @@ ++/* ++ * drivers/mtd/nand/au1550nd.c ++ * ++ * Copyright (C) 2004 Embedded Edge, LLC ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++struct au1550nd_ctx { ++ struct nand_chip chip; ++ ++ int cs; ++ void __iomem *base; ++ void (*write_byte)(struct mtd_info *, u_char); ++}; ++ ++/** ++ * au_read_byte - read one byte from the chip ++ * @mtd: MTD device structure ++ * ++ * read function for 8bit buswidth ++ */ ++static u_char au_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ u_char ret = readb(this->IO_ADDR_R); ++ wmb(); /* drain writebuffer */ ++ return ret; ++} ++ ++/** ++ * au_write_byte - write one byte to the chip ++ * @mtd: MTD device structure ++ * @byte: pointer to data byte to write ++ * ++ * write function for 8it buswidth ++ */ ++static void au_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ writeb(byte, this->IO_ADDR_W); ++ wmb(); /* drain writebuffer */ ++} ++ ++/** ++ * au_read_byte16 - read one byte endianness aware from the chip ++ * @mtd: MTD device structure ++ * ++ * read function for 16bit buswidth with endianness conversion ++ */ ++static u_char au_read_byte16(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); ++ wmb(); /* drain writebuffer */ ++ return ret; ++} ++ ++/** ++ * au_write_byte16 - write one byte endianness aware to the chip ++ * @mtd: MTD device structure ++ * @byte: pointer to data byte to write ++ * ++ * write function for 16bit buswidth with endianness conversion ++ */ ++static void au_write_byte16(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); ++ wmb(); /* drain writebuffer */ ++} ++ ++/** ++ * au_read_word - read one word from the chip ++ * @mtd: MTD device structure ++ * ++ * read function for 16bit buswidth without endianness conversion ++ */ ++static u16 au_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ u16 ret = readw(this->IO_ADDR_R); ++ wmb(); /* drain writebuffer */ ++ return ret; ++} ++ ++/** ++ * au_write_buf - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * write function for 8bit buswidth ++ */ ++static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ for (i = 0; i < len; i++) { ++ writeb(buf[i], this->IO_ADDR_W); ++ wmb(); /* drain writebuffer */ ++ } ++} ++ ++/** ++ * au_read_buf - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * read function for 8bit buswidth ++ */ ++static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ for (i = 0; i < len; i++) { ++ buf[i] = readb(this->IO_ADDR_R); ++ wmb(); /* drain writebuffer */ ++ } ++} ++ ++/** ++ * au_write_buf16 - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * write function for 16bit buswidth ++ */ ++static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i = 0; i < len; i++) { ++ writew(p[i], this->IO_ADDR_W); ++ wmb(); /* drain writebuffer */ ++ } ++ ++} ++ ++/** ++ * au_read_buf16 - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * read function for 16bit buswidth ++ */ ++static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i = 0; i < len; i++) { ++ p[i] = readw(this->IO_ADDR_R); ++ wmb(); /* drain writebuffer */ ++ } ++} ++ ++/* Select the chip by setting nCE to low */ ++#define NAND_CTL_SETNCE 1 ++/* Deselect the chip by setting nCE to high */ ++#define NAND_CTL_CLRNCE 2 ++/* Select the command latch by setting CLE to high */ ++#define NAND_CTL_SETCLE 3 ++/* Deselect the command latch by setting CLE to low */ ++#define NAND_CTL_CLRCLE 4 ++/* Select the address latch by setting ALE to high */ ++#define NAND_CTL_SETALE 5 ++/* Deselect the address latch by setting ALE to low */ ++#define NAND_CTL_CLRALE 6 ++ ++static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, ++ chip); ++ ++ switch (cmd) { ++ ++ case NAND_CTL_SETCLE: ++ this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD; ++ break; ++ ++ case NAND_CTL_CLRCLE: ++ this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; ++ break; ++ ++ case NAND_CTL_SETALE: ++ this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR; ++ break; ++ ++ case NAND_CTL_CLRALE: ++ this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA; ++ /* FIXME: Nobody knows why this is necessary, ++ * but it works only that way */ ++ udelay(1); ++ break; ++ ++ case NAND_CTL_SETNCE: ++ /* assert (force assert) chip enable */ ++ alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL); ++ break; ++ ++ case NAND_CTL_CLRNCE: ++ /* deassert chip enable */ ++ alchemy_wrsmem(0, AU1000_MEM_STNDCTL); ++ break; ++ } ++ ++ this->IO_ADDR_R = this->IO_ADDR_W; ++ ++ wmb(); /* Drain the writebuffer */ ++} ++ ++int au1550_device_ready(struct mtd_info *mtd) ++{ ++ return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0; ++} ++ ++/** ++ * au1550_select_chip - control -CE line ++ * Forbid driving -CE manually permitting the NAND controller to do this. ++ * Keeping -CE asserted during the whole sector reads interferes with the ++ * NOR flash and PCMCIA drivers as it causes contention on the static bus. ++ * We only have to hold -CE low for the NAND read commands since the flash ++ * chip needs it to be asserted during chip not ready time but the NAND ++ * controller keeps it released. ++ * ++ * @mtd: MTD device structure ++ * @chip: chipnumber to select, -1 for deselect ++ */ ++static void au1550_select_chip(struct mtd_info *mtd, int chip) ++{ ++} ++ ++/** ++ * au1550_command - Send command to NAND device ++ * @mtd: MTD device structure ++ * @command: the command to be sent ++ * @column: the column address for this command, -1 if none ++ * @page_addr: the page address for this command, -1 if none ++ */ ++static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, ++ chip); ++ int ce_override = 0, i; ++ unsigned long flags = 0; ++ ++ /* Begin command latch cycle */ ++ au1550_hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->writesize) { ++ /* OOB area */ ++ column -= mtd->writesize; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ ctx->write_byte(mtd, readcmd); ++ } ++ ctx->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ au1550_hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (this->options & NAND_BUSWIDTH_16 && ++ !nand_opcode_8bits(command)) ++ column >>= 1; ++ ctx->write_byte(mtd, column); ++ } ++ if (page_addr != -1) { ++ ctx->write_byte(mtd, (u8)(page_addr & 0xff)); ++ ++ if (command == NAND_CMD_READ0 || ++ command == NAND_CMD_READ1 || ++ command == NAND_CMD_READOOB) { ++ /* ++ * NAND controller will release -CE after ++ * the last address byte is written, so we'll ++ * have to forcibly assert it. No interrupts ++ * are allowed while we do this as we don't ++ * want the NOR flash or PCMCIA drivers to ++ * steal our precious bytes of data... ++ */ ++ ce_override = 1; ++ local_irq_save(flags); ++ au1550_hwcontrol(mtd, NAND_CTL_SETNCE); ++ } ++ ++ ctx->write_byte(mtd, (u8)(page_addr >> 8)); ++ ++ if (this->options & NAND_ROW_ADDR_3) ++ ctx->write_byte(mtd, ++ ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ au1550_hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * Program and erase have their own busy handlers. ++ * Status and sequential in need no delay. ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ break; ++ ++ case NAND_CMD_READ0: ++ case NAND_CMD_READ1: ++ case NAND_CMD_READOOB: ++ /* Check if we're really driving -CE low (just in case) */ ++ if (unlikely(!ce_override)) ++ break; ++ ++ /* Apply a short delay always to ensure that we do wait tWB. */ ++ ndelay(100); ++ /* Wait for a chip to become ready... */ ++ for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i) ++ udelay(1); ++ ++ /* Release -CE and re-enable interrupts. */ ++ au1550_hwcontrol(mtd, NAND_CTL_CLRNCE); ++ local_irq_restore(flags); ++ return; ++ } ++ /* Apply this short delay always to ensure that we do wait tWB. */ ++ ndelay(100); ++ ++ while(!this->dev_ready(mtd)); ++} ++ ++static int find_nand_cs(unsigned long nand_base) ++{ ++ void __iomem *base = ++ (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); ++ unsigned long addr, staddr, start, mask, end; ++ int i; ++ ++ for (i = 0; i < 4; i++) { ++ addr = 0x1000 + (i * 0x10); /* CSx */ ++ staddr = __raw_readl(base + addr + 0x08); /* STADDRx */ ++ /* figure out the decoded range of this CS */ ++ start = (staddr << 4) & 0xfffc0000; ++ mask = (staddr << 18) & 0xfffc0000; ++ end = (start | (start - 1)) & ~(start ^ mask); ++ if ((nand_base >= start) && (nand_base < end)) ++ return i; ++ } ++ ++ return -ENODEV; ++} ++ ++static int au1550nd_probe(struct platform_device *pdev) ++{ ++ struct au1550nd_platdata *pd; ++ struct au1550nd_ctx *ctx; ++ struct nand_chip *this; ++ struct mtd_info *mtd; ++ struct resource *r; ++ int ret, cs; ++ ++ pd = dev_get_platdata(&pdev->dev); ++ if (!pd) { ++ dev_err(&pdev->dev, "missing platform data\n"); ++ return -ENODEV; ++ } ++ ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!r) { ++ dev_err(&pdev->dev, "no NAND memory resource\n"); ++ ret = -ENODEV; ++ goto out1; ++ } ++ if (request_mem_region(r->start, resource_size(r), "au1550-nand")) { ++ dev_err(&pdev->dev, "cannot claim NAND memory area\n"); ++ ret = -ENOMEM; ++ goto out1; ++ } ++ ++ ctx->base = ioremap_nocache(r->start, 0x1000); ++ if (!ctx->base) { ++ dev_err(&pdev->dev, "cannot remap NAND memory area\n"); ++ ret = -ENODEV; ++ goto out2; ++ } ++ ++ this = &ctx->chip; ++ mtd = nand_to_mtd(this); ++ mtd->dev.parent = &pdev->dev; ++ ++ /* figure out which CS# r->start belongs to */ ++ cs = find_nand_cs(r->start); ++ if (cs < 0) { ++ dev_err(&pdev->dev, "cannot detect NAND chipselect\n"); ++ ret = -ENODEV; ++ goto out3; ++ } ++ ctx->cs = cs; ++ ++ this->dev_ready = au1550_device_ready; ++ this->select_chip = au1550_select_chip; ++ this->cmdfunc = au1550_command; ++ ++ /* 30 us command delay time */ ++ this->chip_delay = 30; ++ this->ecc.mode = NAND_ECC_SOFT; ++ this->ecc.algo = NAND_ECC_HAMMING; ++ ++ if (pd->devwidth) ++ this->options |= NAND_BUSWIDTH_16; ++ ++ this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte; ++ ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte; ++ this->read_word = au_read_word; ++ this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf; ++ this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf; ++ ++ ret = nand_scan(mtd, 1); ++ if (ret) { ++ dev_err(&pdev->dev, "NAND scan failed with %d\n", ret); ++ goto out3; ++ } ++ ++ mtd_device_register(mtd, pd->parts, pd->num_parts); ++ ++ platform_set_drvdata(pdev, ctx); ++ ++ return 0; ++ ++out3: ++ iounmap(ctx->base); ++out2: ++ release_mem_region(r->start, resource_size(r)); ++out1: ++ kfree(ctx); ++ return ret; ++} ++ ++static int au1550nd_remove(struct platform_device *pdev) ++{ ++ struct au1550nd_ctx *ctx = platform_get_drvdata(pdev); ++ struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ nand_release(nand_to_mtd(&ctx->chip)); ++ iounmap(ctx->base); ++ release_mem_region(r->start, 0x1000); ++ kfree(ctx); ++ return 0; ++} ++ ++static struct platform_driver au1550nd_driver = { ++ .driver = { ++ .name = "au1550-nand", ++ }, ++ .probe = au1550nd_probe, ++ .remove = au1550nd_remove, ++}; ++ ++module_platform_driver(au1550nd_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Embedded Edge, LLC"); ++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board"); +diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/Makefile b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile +new file mode 100644 +index 00000000..f05b119 +--- /dev/null ++++ b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile +@@ -0,0 +1,4 @@ ++bcm47xxnflash-y += main.o ++bcm47xxnflash-y += ops_bcm4706.o ++ ++obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash.o +diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h +new file mode 100644 +index 00000000..201b9baa +--- /dev/null ++++ b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h +@@ -0,0 +1,26 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __BCM47XXNFLASH_H ++#define __BCM47XXNFLASH_H ++ ++#ifndef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++#endif ++ ++#include ++#include ++ ++struct bcm47xxnflash { ++ struct bcma_drv_cc *cc; ++ ++ struct nand_chip nand_chip; ++ ++ unsigned curr_command; ++ int curr_page_addr; ++ int curr_column; ++ ++ u8 id_data[8]; ++}; ++ ++int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n); ++ ++#endif /* BCM47XXNFLASH */ +diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/main.c b/drivers/mtd/nand/raw/bcm47xxnflash/main.c +new file mode 100644 +index 00000000..fb31429 +--- /dev/null ++++ b/drivers/mtd/nand/raw/bcm47xxnflash/main.c +@@ -0,0 +1,81 @@ ++/* ++ * BCM47XX NAND flash driver ++ * ++ * Copyright (C) 2012 RafaÅ‚ MiÅ‚ecki ++ * ++ * 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 "bcm47xxnflash.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); ++ ++static const char *probes[] = { "bcm47xxpart", NULL }; ++ ++static int bcm47xxnflash_probe(struct platform_device *pdev) ++{ ++ struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); ++ struct bcm47xxnflash *b47n; ++ struct mtd_info *mtd; ++ int err = 0; ++ ++ b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL); ++ if (!b47n) ++ return -ENOMEM; ++ ++ nand_set_controller_data(&b47n->nand_chip, b47n); ++ mtd = nand_to_mtd(&b47n->nand_chip); ++ mtd->dev.parent = &pdev->dev; ++ b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash); ++ ++ if (b47n->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { ++ err = bcm47xxnflash_ops_bcm4706_init(b47n); ++ } else { ++ pr_err("Device not supported\n"); ++ err = -ENOTSUPP; ++ } ++ if (err) { ++ pr_err("Initialization failed: %d\n", err); ++ return err; ++ } ++ ++ platform_set_drvdata(pdev, b47n); ++ ++ err = mtd_device_parse_register(mtd, probes, NULL, NULL, 0); ++ if (err) { ++ pr_err("Failed to register MTD device: %d\n", err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int bcm47xxnflash_remove(struct platform_device *pdev) ++{ ++ struct bcm47xxnflash *nflash = platform_get_drvdata(pdev); ++ ++ nand_release(nand_to_mtd(&nflash->nand_chip)); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm47xxnflash_driver = { ++ .probe = bcm47xxnflash_probe, ++ .remove = bcm47xxnflash_remove, ++ .driver = { ++ .name = "bcma_nflash", ++ }, ++}; ++ ++module_platform_driver(bcm47xxnflash_driver); +diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c +new file mode 100644 +index 00000000..54bac5b +--- /dev/null ++++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c +@@ -0,0 +1,456 @@ ++/* ++ * BCM47XX NAND flash driver ++ * ++ * Copyright (C) 2012 RafaÅ‚ MiÅ‚ecki ++ * ++ * 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 "bcm47xxnflash.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has ++ * shown ~1000 retries as maxiumum. */ ++#define NFLASH_READY_RETRIES 10000 ++ ++#define NFLASH_SECTOR_SIZE 512 ++ ++#define NCTL_CMD0 0x00010000 ++#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */ ++#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */ ++#define NCTL_CMD1W 0x00080000 ++#define NCTL_READ 0x00100000 ++#define NCTL_WRITE 0x00200000 ++#define NCTL_SPECADDR 0x01000000 ++#define NCTL_READY 0x04000000 ++#define NCTL_ERR 0x08000000 ++#define NCTL_CSA 0x40000000 ++#define NCTL_START 0x80000000 ++ ++/************************************************** ++ * Various helpers ++ **************************************************/ ++ ++static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock) ++{ ++ return ((ns * 1000 * clock) / 1000000) + 1; ++} ++ ++static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) ++{ ++ int i = 0; ++ ++ bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code); ++ for (i = 0; i < NFLASH_READY_RETRIES; i++) { ++ if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) { ++ i = 0; ++ break; ++ } ++ } ++ if (i) { ++ pr_err("NFLASH control command not ready!\n"); ++ return -EBUSY; ++ } ++ return 0; ++} ++ ++static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) ++{ ++ int i; ++ ++ for (i = 0; i < NFLASH_READY_RETRIES; i++) { ++ if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) { ++ if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & ++ BCMA_CC_NFLASH_CTL_ERR) { ++ pr_err("Error on polling\n"); ++ return -EBUSY; ++ } else { ++ return 0; ++ } ++ } ++ } ++ ++ pr_err("Polling timeout!\n"); ++ return -EBUSY; ++} ++ ++/************************************************** ++ * R/W ++ **************************************************/ ++ ++static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ ++ u32 ctlcode; ++ u32 *dest = (u32 *)buf; ++ int i; ++ int toread; ++ ++ BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); ++ /* Don't validate column using nand_chip->page_shift, it may be bigger ++ * when accessing OOB */ ++ ++ while (len) { ++ /* We can read maximum of 0x200 bytes at once */ ++ toread = min(len, 0x200); ++ ++ /* Set page and column */ ++ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, ++ b47n->curr_column); ++ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, ++ b47n->curr_page_addr); ++ ++ /* Prepare to read */ ++ ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL | ++ NCTL_CMD0; ++ ctlcode |= NAND_CMD_READSTART << 8; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) ++ return; ++ if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) ++ return; ++ ++ /* Eventually read some data :) */ ++ for (i = 0; i < toread; i += 4, dest++) { ++ ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ; ++ if (i == toread - 4) /* Last read goes without that */ ++ ctlcode &= ~NCTL_CSA; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ++ ctlcode)) ++ return; ++ *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); ++ } ++ ++ b47n->curr_column += toread; ++ len -= toread; ++ } ++} ++ ++static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ struct bcma_drv_cc *cc = b47n->cc; ++ ++ u32 ctlcode; ++ const u32 *data = (u32 *)buf; ++ int i; ++ ++ BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); ++ /* Don't validate column using nand_chip->page_shift, it may be bigger ++ * when accessing OOB */ ++ ++ for (i = 0; i < len; i += 4, data++) { ++ bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data); ++ ++ ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE; ++ if (i == len - 4) /* Last read goes without that */ ++ ctlcode &= ~NCTL_CSA; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) { ++ pr_err("%s ctl_cmd didn't work!\n", __func__); ++ return; ++ } ++ } ++ ++ b47n->curr_column += len; ++} ++ ++/************************************************** ++ * NAND chip ops ++ **************************************************/ ++ ++static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ u32 code = 0; ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (cmd & NAND_CTRL_CLE) ++ code = cmd | NCTL_CMD0; ++ ++ /* nCS is not needed for reset command */ ++ if (cmd != NAND_CMD_RESET) ++ code |= NCTL_CSA; ++ ++ bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code); ++} ++ ++/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */ ++static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd, ++ int chip) ++{ ++ return; ++} ++ ++static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ ++ return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY); ++} ++ ++/* ++ * Default nand_command and nand_command_lp don't match BCM4706 hardware layout. ++ * For example, reading chip id is performed in a non-standard way. ++ * Setting column and page is also handled differently, we use a special ++ * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert ++ * standard commands would be much more complicated. ++ */ ++static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd, ++ unsigned command, int column, ++ int page_addr) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ struct bcma_drv_cc *cc = b47n->cc; ++ u32 ctlcode; ++ int i; ++ ++ if (column != -1) ++ b47n->curr_column = column; ++ if (page_addr != -1) ++ b47n->curr_page_addr = page_addr; ++ ++ switch (command) { ++ case NAND_CMD_RESET: ++ nand_chip->cmd_ctrl(mtd, command, NAND_CTRL_CLE); ++ ++ ndelay(100); ++ nand_wait_ready(mtd); ++ break; ++ case NAND_CMD_READID: ++ ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0; ++ ctlcode |= NAND_CMD_READID; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) { ++ pr_err("READID error\n"); ++ break; ++ } ++ ++ /* ++ * Reading is specific, last one has to go without NCTL_CSA ++ * bit. We don't know how many reads NAND subsystem is going ++ * to perform, so cache everything. ++ */ ++ for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) { ++ ctlcode = NCTL_CSA | NCTL_READ; ++ if (i == ARRAY_SIZE(b47n->id_data) - 1) ++ ctlcode &= ~NCTL_CSA; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ++ ctlcode)) { ++ pr_err("READID error\n"); ++ break; ++ } ++ b47n->id_data[i] = ++ bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA) ++ & 0xFF; ++ } ++ ++ break; ++ case NAND_CMD_STATUS: ++ ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) ++ pr_err("STATUS command error\n"); ++ break; ++ case NAND_CMD_READ0: ++ break; ++ case NAND_CMD_READOOB: ++ if (page_addr != -1) ++ b47n->curr_column += mtd->writesize; ++ break; ++ case NAND_CMD_ERASE1: ++ bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, ++ b47n->curr_page_addr); ++ ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 | ++ NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8); ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) ++ pr_err("ERASE1 failed\n"); ++ break; ++ case NAND_CMD_ERASE2: ++ break; ++ case NAND_CMD_SEQIN: ++ /* Set page and column */ ++ bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR, ++ b47n->curr_column); ++ bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, ++ b47n->curr_page_addr); ++ ++ /* Prepare to write */ ++ ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0; ++ ctlcode |= NAND_CMD_SEQIN; ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) ++ pr_err("SEQIN failed\n"); ++ break; ++ case NAND_CMD_PAGEPROG: ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 | ++ NAND_CMD_PAGEPROG)) ++ pr_err("PAGEPROG failed\n"); ++ if (bcm47xxnflash_ops_bcm4706_poll(cc)) ++ pr_err("PAGEPROG not ready\n"); ++ break; ++ default: ++ pr_err("Command 0x%X unsupported\n", command); ++ break; ++ } ++ b47n->curr_command = command; ++} ++ ++static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ struct bcma_drv_cc *cc = b47n->cc; ++ u32 tmp = 0; ++ ++ switch (b47n->curr_command) { ++ case NAND_CMD_READID: ++ if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) { ++ pr_err("Requested invalid id_data: %d\n", ++ b47n->curr_column); ++ return 0; ++ } ++ return b47n->id_data[b47n->curr_column++]; ++ case NAND_CMD_STATUS: ++ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ)) ++ return 0; ++ return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff; ++ case NAND_CMD_READOOB: ++ bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); ++ return tmp & 0xFF; ++ } ++ ++ pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); ++ return 0; ++} ++ ++static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd, ++ uint8_t *buf, int len) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ ++ switch (b47n->curr_command) { ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ bcm47xxnflash_ops_bcm4706_read(mtd, buf, len); ++ return; ++ } ++ ++ pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); ++} ++ ++static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); ++ ++ switch (b47n->curr_command) { ++ case NAND_CMD_SEQIN: ++ bcm47xxnflash_ops_bcm4706_write(mtd, buf, len); ++ return; ++ } ++ ++ pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command); ++} ++ ++/************************************************** ++ * Init ++ **************************************************/ ++ ++int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) ++{ ++ struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip; ++ int err; ++ u32 freq; ++ u16 clock; ++ u8 w0, w1, w2, w3, w4; ++ ++ unsigned long chipsize; /* MiB */ ++ u8 tbits, col_bits, col_size, row_bits, row_bsize; ++ u32 val; ++ ++ b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; ++ nand_chip->cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; ++ nand_chip->dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; ++ b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; ++ b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; ++ b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; ++ b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; ++ b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp; ++ b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ nand_chip->chip_delay = 50; ++ b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; ++ b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */ ++ ++ /* Enable NAND flash access */ ++ bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, ++ BCMA_CC_4706_FLASHSCFG_NF1); ++ ++ /* Configure wait counters */ ++ if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) { ++ /* 400 MHz */ ++ freq = 400000000 / 4; ++ } else { ++ freq = bcma_chipco_pll_read(b47n->cc, 4); ++ freq = (freq & 0xFFF) >> 3; ++ /* Fixed reference clock 25 MHz and m = 2 */ ++ freq = (freq * 25000000 / 2) / 4; ++ } ++ clock = freq / 1000000; ++ w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock); ++ w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock); ++ w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); ++ w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); ++ w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock); ++ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0, ++ (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0)); ++ ++ /* Scan NAND */ ++ err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1); ++ if (err) { ++ pr_err("Could not scan NAND flash: %d\n", err); ++ goto exit; ++ } ++ ++ /* Configure FLASH */ ++ chipsize = b47n->nand_chip.chipsize >> 20; ++ tbits = ffs(chipsize); /* find first bit set */ ++ if (!tbits || tbits != fls(chipsize)) { ++ pr_err("Invalid flash size: 0x%lX\n", chipsize); ++ err = -ENOTSUPP; ++ goto exit; ++ } ++ tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */ ++ ++ col_bits = b47n->nand_chip.page_shift + 1; ++ col_size = (col_bits + 7) / 8; ++ ++ row_bits = tbits - col_bits + 1; ++ row_bsize = (row_bits + 7) / 8; ++ ++ val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2; ++ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val); ++ ++exit: ++ if (err) ++ bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG, ++ ~BCMA_CC_4706_FLASHSCFG_NF1); ++ return err; ++} +diff --git a/drivers/mtd/nand/raw/bf5xx_nand.c b/drivers/mtd/nand/raw/bf5xx_nand.c +new file mode 100644 +index 00000000..87bbd17 +--- /dev/null ++++ b/drivers/mtd/nand/raw/bf5xx_nand.c +@@ -0,0 +1,862 @@ ++/* linux/drivers/mtd/nand/bf5xx_nand.c ++ * ++ * Copyright 2006-2008 Analog Devices Inc. ++ * http://blackfin.uclinux.org/ ++ * Bryan Wu ++ * ++ * Blackfin BF5xx on-chip NAND flash controller driver ++ * ++ * Derived from drivers/mtd/nand/s3c2410.c ++ * Copyright (c) 2007 Ben Dooks ++ * ++ * Derived from drivers/mtd/nand/cafe.c ++ * Copyright © 2006 Red Hat, Inc. ++ * Copyright © 2006 David Woodhouse ++ * ++ * Changelog: ++ * 12-Jun-2007 Bryan Wu: Initial version ++ * 18-Jul-2007 Bryan Wu: ++ * - ECC_HW and ECC_SW supported ++ * - DMA supported in ECC_HW ++ * - YAFFS tested as rootfs in both ECC_HW and ECC_SW ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "bf5xx-nand" ++#define DRV_VERSION "1.2" ++#define DRV_AUTHOR "Bryan Wu " ++#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" ++ ++/* NFC_STAT Masks */ ++#define NBUSY 0x01 /* Not Busy */ ++#define WB_FULL 0x02 /* Write Buffer Full */ ++#define PG_WR_STAT 0x04 /* Page Write Pending */ ++#define PG_RD_STAT 0x08 /* Page Read Pending */ ++#define WB_EMPTY 0x10 /* Write Buffer Empty */ ++ ++/* NFC_IRQSTAT Masks */ ++#define NBUSYIRQ 0x01 /* Not Busy IRQ */ ++#define WB_OVF 0x02 /* Write Buffer Overflow */ ++#define WB_EDGE 0x04 /* Write Buffer Edge Detect */ ++#define RD_RDY 0x08 /* Read Data Ready */ ++#define WR_DONE 0x10 /* Page Write Done */ ++ ++/* NFC_RST Masks */ ++#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */ ++ ++/* NFC_PGCTL Masks */ ++#define PG_RD_START 0x01 /* Page Read Start */ ++#define PG_WR_START 0x02 /* Page Write Start */ ++ ++#ifdef CONFIG_MTD_NAND_BF5XX_HWECC ++static int hardware_ecc = 1; ++#else ++static int hardware_ecc; ++#endif ++ ++static const unsigned short bfin_nfc_pin_req[] = ++ {P_NAND_CE, ++ P_NAND_RB, ++ P_NAND_D0, ++ P_NAND_D1, ++ P_NAND_D2, ++ P_NAND_D3, ++ P_NAND_D4, ++ P_NAND_D5, ++ P_NAND_D6, ++ P_NAND_D7, ++ P_NAND_WE, ++ P_NAND_RE, ++ P_NAND_CLE, ++ P_NAND_ALE, ++ 0}; ++ ++#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC ++static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 7) ++ return -ERANGE; ++ ++ oobregion->offset = section * 8; ++ oobregion->length = 3; ++ ++ return 0; ++} ++ ++static int bootrom_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 7) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 8) + 3; ++ oobregion->length = 5; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = { ++ .ecc = bootrom_ooblayout_ecc, ++ .free = bootrom_ooblayout_free, ++}; ++#endif ++ ++/* ++ * Data structures for bf5xx nand flash controller driver ++ */ ++ ++/* bf5xx nand info */ ++struct bf5xx_nand_info { ++ /* mtd info */ ++ struct nand_hw_control controller; ++ struct nand_chip chip; ++ ++ /* platform info */ ++ struct bf5xx_nand_platform *platform; ++ ++ /* device info */ ++ struct device *device; ++ ++ /* DMA stuff */ ++ struct completion dma_completion; ++}; ++ ++/* ++ * Conversion functions ++ */ ++static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info, ++ chip); ++} ++ ++static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) ++{ ++ return platform_get_drvdata(pdev); ++} ++ ++static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) ++{ ++ return dev_get_platdata(&pdev->dev); ++} ++ ++/* ++ * struct nand_chip interface function pointers ++ */ ++ ++/* ++ * bf5xx_nand_hwcontrol ++ * ++ * Issue command and address cycles to the chip ++ */ ++static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ while (bfin_read_NFC_STAT() & WB_FULL) ++ cpu_relax(); ++ ++ if (ctrl & NAND_CLE) ++ bfin_write_NFC_CMD(cmd); ++ else if (ctrl & NAND_ALE) ++ bfin_write_NFC_ADDR(cmd); ++ SSYNC(); ++} ++ ++/* ++ * bf5xx_nand_devready() ++ * ++ * returns 0 if the nand is busy, 1 if it is ready ++ */ ++static int bf5xx_nand_devready(struct mtd_info *mtd) ++{ ++ unsigned short val = bfin_read_NFC_STAT(); ++ ++ if ((val & NBUSY) == NBUSY) ++ return 1; ++ else ++ return 0; ++} ++ ++/* ++ * ECC functions ++ * These allow the bf5xx to use the controller's ECC ++ * generator block to ECC the data as it passes through ++ */ ++ ++/* ++ * ECC error correction function ++ */ ++static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); ++ u32 syndrome[5]; ++ u32 calced, stored; ++ int i; ++ unsigned short failing_bit, failing_byte; ++ u_char data; ++ ++ calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); ++ stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); ++ ++ syndrome[0] = (calced ^ stored); ++ ++ /* ++ * syndrome 0: all zero ++ * No error in data ++ * No action ++ */ ++ if (!syndrome[0] || !calced || !stored) ++ return 0; ++ ++ /* ++ * sysdrome 0: only one bit is one ++ * ECC data was incorrect ++ * No action ++ */ ++ if (hweight32(syndrome[0]) == 1) { ++ dev_err(info->device, "ECC data was incorrect!\n"); ++ return -EBADMSG; ++ } ++ ++ syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); ++ syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); ++ syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); ++ syndrome[4] = syndrome[2] ^ syndrome[3]; ++ ++ for (i = 0; i < 5; i++) ++ dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]); ++ ++ dev_info(info->device, ++ "calced[0x%08x], stored[0x%08x]\n", ++ calced, stored); ++ ++ /* ++ * sysdrome 0: exactly 11 bits are one, each parity ++ * and parity' pair is 1 & 0 or 0 & 1. ++ * 1-bit correctable error ++ * Correct the error ++ */ ++ if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { ++ dev_info(info->device, ++ "1-bit correctable error, correct it.\n"); ++ dev_info(info->device, ++ "syndrome[1] 0x%08x\n", syndrome[1]); ++ ++ failing_bit = syndrome[1] & 0x7; ++ failing_byte = syndrome[1] >> 0x3; ++ data = *(dat + failing_byte); ++ data = data ^ (0x1 << failing_bit); ++ *(dat + failing_byte) = data; ++ ++ return 1; ++ } ++ ++ /* ++ * sysdrome 0: random data ++ * More than 1-bit error, non-correctable error ++ * Discard data, mark bad block ++ */ ++ dev_err(info->device, ++ "More than 1-bit error, non-correctable error.\n"); ++ dev_err(info->device, ++ "Please discard data, mark bad block\n"); ++ ++ return -EBADMSG; ++} ++ ++static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret, bitflips = 0; ++ ++ ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); ++ if (ret < 0) ++ return ret; ++ ++ bitflips = ret; ++ ++ /* If ecc size is 512, correct second 256 bytes */ ++ if (chip->ecc.size == 512) { ++ dat += 256; ++ read_ecc += 3; ++ calc_ecc += 3; ++ ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); ++ if (ret < 0) ++ return ret; ++ ++ bitflips += ret; ++ } ++ ++ return bitflips; ++} ++ ++static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ return; ++} ++ ++static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_code) ++{ ++ struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u16 ecc0, ecc1; ++ u32 code[2]; ++ u8 *p; ++ ++ /* first 3 bytes ECC code for 256 page size */ ++ ecc0 = bfin_read_NFC_ECC0(); ++ ecc1 = bfin_read_NFC_ECC1(); ++ ++ code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); ++ ++ dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); ++ ++ p = (u8 *) code; ++ memcpy(ecc_code, p, 3); ++ ++ /* second 3 bytes ECC code for 512 ecc size */ ++ if (chip->ecc.size == 512) { ++ ecc0 = bfin_read_NFC_ECC2(); ++ ecc1 = bfin_read_NFC_ECC3(); ++ code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); ++ ++ /* second 3 bytes in ecc_code for second 256 ++ * bytes of 512 page size ++ */ ++ p = (u8 *) (code + 1); ++ memcpy((ecc_code + 3), p, 3); ++ dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); ++ } ++ ++ return 0; ++} ++ ++/* ++ * PIO mode for buffer writing and reading ++ */ ++static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ unsigned short val; ++ ++ /* ++ * Data reads are requested by first writing to NFC_DATA_RD ++ * and then reading back from NFC_READ. ++ */ ++ for (i = 0; i < len; i++) { ++ while (bfin_read_NFC_STAT() & WB_FULL) ++ cpu_relax(); ++ ++ /* Contents do not matter */ ++ bfin_write_NFC_DATA_RD(0x0000); ++ SSYNC(); ++ ++ while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY) ++ cpu_relax(); ++ ++ buf[i] = bfin_read_NFC_READ(); ++ ++ val = bfin_read_NFC_IRQSTAT(); ++ val |= RD_RDY; ++ bfin_write_NFC_IRQSTAT(val); ++ SSYNC(); ++ } ++} ++ ++static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd) ++{ ++ uint8_t val; ++ ++ bf5xx_nand_read_buf(mtd, &val, 1); ++ ++ return val; ++} ++ ++static void bf5xx_nand_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) { ++ while (bfin_read_NFC_STAT() & WB_FULL) ++ cpu_relax(); ++ ++ bfin_write_NFC_DATA_WR(buf[i]); ++ SSYNC(); ++ } ++} ++ ++static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ /* ++ * Data reads are requested by first writing to NFC_DATA_RD ++ * and then reading back from NFC_READ. ++ */ ++ bfin_write_NFC_DATA_RD(0x5555); ++ ++ SSYNC(); ++ ++ for (i = 0; i < len; i++) ++ p[i] = bfin_read_NFC_READ(); ++} ++ ++static void bf5xx_nand_write_buf16(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ int i; ++ u16 *p = (u16 *) buf; ++ len >>= 1; ++ ++ for (i = 0; i < len; i++) ++ bfin_write_NFC_DATA_WR(p[i]); ++ ++ SSYNC(); ++} ++ ++/* ++ * DMA functions for buffer writing and reading ++ */ ++static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) ++{ ++ struct bf5xx_nand_info *info = dev_id; ++ ++ clear_dma_irqstat(CH_NFC); ++ disable_dma(CH_NFC); ++ complete(&info->dma_completion); ++ ++ return IRQ_HANDLED; ++} ++ ++static void bf5xx_nand_dma_rw(struct mtd_info *mtd, ++ uint8_t *buf, int is_read) ++{ ++ struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ unsigned short val; ++ ++ dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n", ++ mtd, buf, is_read); ++ ++ /* ++ * Before starting a dma transfer, be sure to invalidate/flush ++ * the cache over the address range of your DMA buffer to ++ * prevent cache coherency problems. Otherwise very subtle bugs ++ * can be introduced to your driver. ++ */ ++ if (is_read) ++ invalidate_dcache_range((unsigned int)buf, ++ (unsigned int)(buf + chip->ecc.size)); ++ else ++ flush_dcache_range((unsigned int)buf, ++ (unsigned int)(buf + chip->ecc.size)); ++ ++ /* ++ * This register must be written before each page is ++ * transferred to generate the correct ECC register ++ * values. ++ */ ++ bfin_write_NFC_RST(ECC_RST); ++ SSYNC(); ++ while (bfin_read_NFC_RST() & ECC_RST) ++ cpu_relax(); ++ ++ disable_dma(CH_NFC); ++ clear_dma_irqstat(CH_NFC); ++ ++ /* setup DMA register with Blackfin DMA API */ ++ set_dma_config(CH_NFC, 0x0); ++ set_dma_start_addr(CH_NFC, (unsigned long) buf); ++ ++ /* The DMAs have different size on BF52x and BF54x */ ++#ifdef CONFIG_BF52x ++ set_dma_x_count(CH_NFC, (chip->ecc.size >> 1)); ++ set_dma_x_modify(CH_NFC, 2); ++ val = DI_EN | WDSIZE_16; ++#endif ++ ++#ifdef CONFIG_BF54x ++ set_dma_x_count(CH_NFC, (chip->ecc.size >> 2)); ++ set_dma_x_modify(CH_NFC, 4); ++ val = DI_EN | WDSIZE_32; ++#endif ++ /* setup write or read operation */ ++ if (is_read) ++ val |= WNR; ++ set_dma_config(CH_NFC, val); ++ enable_dma(CH_NFC); ++ ++ /* Start PAGE read/write operation */ ++ if (is_read) ++ bfin_write_NFC_PGCTL(PG_RD_START); ++ else ++ bfin_write_NFC_PGCTL(PG_WR_START); ++ wait_for_completion(&info->dma_completion); ++} ++ ++static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, ++ uint8_t *buf, int len) ++{ ++ struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len); ++ ++ if (len == chip->ecc.size) ++ bf5xx_nand_dma_rw(mtd, buf, 1); ++ else ++ bf5xx_nand_read_buf(mtd, buf, len); ++} ++ ++static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len); ++ ++ if (len == chip->ecc.size) ++ bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0); ++ else ++ bf5xx_nand_write_buf(mtd, buf, len); ++} ++ ++static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ bf5xx_nand_read_buf(mtd, buf, mtd->writesize); ++ bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return 0; ++} ++ ++static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, int oob_required, ++ int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/* ++ * System initialization functions ++ */ ++static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) ++{ ++ int ret; ++ ++ /* Do not use dma */ ++ if (!hardware_ecc) ++ return 0; ++ ++ init_completion(&info->dma_completion); ++ ++ /* Request NFC DMA channel */ ++ ret = request_dma(CH_NFC, "BF5XX NFC driver"); ++ if (ret < 0) { ++ dev_err(info->device, " unable to get DMA channel\n"); ++ return ret; ++ } ++ ++#ifdef CONFIG_BF54x ++ /* Setup DMAC1 channel mux for NFC which shared with SDH */ ++ bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1); ++ SSYNC(); ++#endif ++ ++ set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info); ++ ++ /* Turn off the DMA channel first */ ++ disable_dma(CH_NFC); ++ return 0; ++} ++ ++static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info) ++{ ++ /* Free NFC DMA channel */ ++ if (hardware_ecc) ++ free_dma(CH_NFC); ++} ++ ++/* ++ * BF5XX NFC hardware initialization ++ * - pin mux setup ++ * - clear interrupt status ++ */ ++static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) ++{ ++ int err = 0; ++ unsigned short val; ++ struct bf5xx_nand_platform *plat = info->platform; ++ ++ /* setup NFC_CTL register */ ++ dev_info(info->device, ++ "data_width=%d, wr_dly=%d, rd_dly=%d\n", ++ (plat->data_width ? 16 : 8), ++ plat->wr_dly, plat->rd_dly); ++ ++ val = (1 << NFC_PG_SIZE_OFFSET) | ++ (plat->data_width << NFC_NWIDTH_OFFSET) | ++ (plat->rd_dly << NFC_RDDLY_OFFSET) | ++ (plat->wr_dly << NFC_WRDLY_OFFSET); ++ dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val); ++ ++ bfin_write_NFC_CTL(val); ++ SSYNC(); ++ ++ /* clear interrupt status */ ++ bfin_write_NFC_IRQMASK(0x0); ++ SSYNC(); ++ val = bfin_read_NFC_IRQSTAT(); ++ bfin_write_NFC_IRQSTAT(val); ++ SSYNC(); ++ ++ /* DMA initialization */ ++ if (bf5xx_nand_dma_init(info)) ++ err = -ENXIO; ++ ++ return err; ++} ++ ++/* ++ * Device management interface ++ */ ++static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&info->chip); ++ struct mtd_partition *parts = info->platform->partitions; ++ int nr = info->platform->nr_partitions; ++ ++ return mtd_device_register(mtd, parts, nr); ++} ++ ++static int bf5xx_nand_remove(struct platform_device *pdev) ++{ ++ struct bf5xx_nand_info *info = to_nand_info(pdev); ++ ++ /* first thing we need to do is release all our mtds ++ * and their partitions, then go through freeing the ++ * resources used ++ */ ++ nand_release(nand_to_mtd(&info->chip)); ++ ++ peripheral_free_list(bfin_nfc_pin_req); ++ bf5xx_nand_dma_remove(info); ++ ++ return 0; ++} ++ ++static int bf5xx_nand_scan(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; ++ ++ if (hardware_ecc) { ++ /* ++ * for nand with page size > 512B, think it as several sections with 512B ++ */ ++ if (likely(mtd->writesize >= 512)) { ++ chip->ecc.size = 512; ++ chip->ecc.bytes = 6; ++ chip->ecc.strength = 2; ++ } else { ++ chip->ecc.size = 256; ++ chip->ecc.bytes = 3; ++ chip->ecc.strength = 1; ++ bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET)); ++ SSYNC(); ++ } ++ } ++ ++ return nand_scan_tail(mtd); ++} ++ ++/* ++ * bf5xx_nand_probe ++ * ++ * called by device layer when it finds a device matching ++ * one our driver can handled. This code checks to see if ++ * it can allocate all necessary resources then calls the ++ * nand layer to look for devices ++ */ ++static int bf5xx_nand_probe(struct platform_device *pdev) ++{ ++ struct bf5xx_nand_platform *plat = to_nand_plat(pdev); ++ struct bf5xx_nand_info *info = NULL; ++ struct nand_chip *chip = NULL; ++ struct mtd_info *mtd = NULL; ++ int err = 0; ++ ++ dev_dbg(&pdev->dev, "(%p)\n", pdev); ++ ++ if (!plat) { ++ dev_err(&pdev->dev, "no platform specific information\n"); ++ return -EINVAL; ++ } ++ ++ if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { ++ dev_err(&pdev->dev, "requesting Peripherals failed\n"); ++ return -EFAULT; ++ } ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (info == NULL) { ++ err = -ENOMEM; ++ goto out_err; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ nand_hw_control_init(&info->controller); ++ ++ info->device = &pdev->dev; ++ info->platform = plat; ++ ++ /* initialise chip data struct */ ++ chip = &info->chip; ++ mtd = nand_to_mtd(&info->chip); ++ ++ if (plat->data_width) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN; ++ ++ chip->read_buf = (plat->data_width) ? ++ bf5xx_nand_read_buf16 : bf5xx_nand_read_buf; ++ chip->write_buf = (plat->data_width) ? ++ bf5xx_nand_write_buf16 : bf5xx_nand_write_buf; ++ ++ chip->read_byte = bf5xx_nand_read_byte; ++ ++ chip->cmd_ctrl = bf5xx_nand_hwcontrol; ++ chip->dev_ready = bf5xx_nand_devready; ++ ++ nand_set_controller_data(chip, mtd); ++ chip->controller = &info->controller; ++ ++ chip->IO_ADDR_R = (void __iomem *) NFC_READ; ++ chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR; ++ ++ chip->chip_delay = 0; ++ ++ /* initialise mtd info data struct */ ++ mtd->dev.parent = &pdev->dev; ++ ++ /* initialise the hardware */ ++ err = bf5xx_nand_hw_init(info); ++ if (err) ++ goto out_err; ++ ++ /* setup hardware ECC data struct */ ++ if (hardware_ecc) { ++#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC ++ mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops); ++#endif ++ chip->read_buf = bf5xx_nand_dma_read_buf; ++ chip->write_buf = bf5xx_nand_dma_write_buf; ++ chip->ecc.calculate = bf5xx_nand_calculate_ecc; ++ chip->ecc.correct = bf5xx_nand_correct_data; ++ chip->ecc.mode = NAND_ECC_HW; ++ chip->ecc.hwctl = bf5xx_nand_enable_hwecc; ++ chip->ecc.read_page_raw = bf5xx_nand_read_page_raw; ++ chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; ++ } else { ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ } ++ ++ /* scan hardware nand chip and setup mtd info data struct */ ++ if (bf5xx_nand_scan(mtd)) { ++ err = -ENXIO; ++ goto out_err_nand_scan; ++ } ++ ++#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC ++ chip->badblockpos = 63; ++#endif ++ ++ /* add NAND partition */ ++ bf5xx_nand_add_partition(info); ++ ++ dev_dbg(&pdev->dev, "initialised ok\n"); ++ return 0; ++ ++out_err_nand_scan: ++ bf5xx_nand_dma_remove(info); ++out_err: ++ peripheral_free_list(bfin_nfc_pin_req); ++ ++ return err; ++} ++ ++/* driver device registration */ ++static struct platform_driver bf5xx_nand_driver = { ++ .probe = bf5xx_nand_probe, ++ .remove = bf5xx_nand_remove, ++ .driver = { ++ .name = DRV_NAME, ++ }, ++}; ++ ++module_platform_driver(bf5xx_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR(DRV_AUTHOR); ++MODULE_DESCRIPTION(DRV_DESC); ++MODULE_ALIAS("platform:" DRV_NAME); +diff --git a/drivers/mtd/nand/raw/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile +new file mode 100644 +index 00000000..195b845 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/Makefile +@@ -0,0 +1,8 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# link order matters; don't link the more generic brcmstb_nand.o before the ++# more specific iproc_nand.o, for instance ++obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o ++obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o ++obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o ++obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o ++obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o +diff --git a/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c +new file mode 100644 +index 00000000..59444b3 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright © 2015 Broadcom Corporation ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "brcmnand.h" ++ ++struct bcm63138_nand_soc { ++ struct brcmnand_soc soc; ++ void __iomem *base; ++}; ++ ++#define BCM63138_NAND_INT_STATUS 0x00 ++#define BCM63138_NAND_INT_EN 0x04 ++ ++enum { ++ BCM63138_CTLRDY = BIT(4), ++}; ++ ++static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc) ++{ ++ struct bcm63138_nand_soc *priv = ++ container_of(soc, struct bcm63138_nand_soc, soc); ++ void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS; ++ u32 val = brcmnand_readl(mmio); ++ ++ if (val & BCM63138_CTLRDY) { ++ brcmnand_writel(val & ~BCM63138_CTLRDY, mmio); ++ return true; ++ } ++ ++ return false; ++} ++ ++static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en) ++{ ++ struct bcm63138_nand_soc *priv = ++ container_of(soc, struct bcm63138_nand_soc, soc); ++ void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN; ++ u32 val = brcmnand_readl(mmio); ++ ++ if (en) ++ val |= BCM63138_CTLRDY; ++ else ++ val &= ~BCM63138_CTLRDY; ++ ++ brcmnand_writel(val, mmio); ++} ++ ++static int bcm63138_nand_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct bcm63138_nand_soc *priv; ++ struct brcmnand_soc *soc; ++ struct resource *res; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ soc = &priv->soc; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base"); ++ priv->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ soc->ctlrdy_ack = bcm63138_nand_intc_ack; ++ soc->ctlrdy_set_enabled = bcm63138_nand_intc_set; ++ ++ return brcmnand_probe(pdev, soc); ++} ++ ++static const struct of_device_id bcm63138_nand_of_match[] = { ++ { .compatible = "brcm,nand-bcm63138" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match); ++ ++static struct platform_driver bcm63138_nand_driver = { ++ .probe = bcm63138_nand_probe, ++ .remove = brcmnand_remove, ++ .driver = { ++ .name = "bcm63138_nand", ++ .pm = &brcmnand_pm_ops, ++ .of_match_table = bcm63138_nand_of_match, ++ } ++}; ++module_platform_driver(bcm63138_nand_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Brian Norris"); ++MODULE_DESCRIPTION("NAND driver for BCM63138"); +diff --git a/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c +new file mode 100644 +index 00000000..34c91b0 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c +@@ -0,0 +1,142 @@ ++/* ++ * Copyright 2015 Simon Arlott ++ * ++ * 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. ++ * ++ * 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. ++ * ++ * Derived from bcm63138_nand.c: ++ * Copyright © 2015 Broadcom Corporation ++ * ++ * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h: ++ * Copyright 2000-2010 Broadcom Corporation ++ * ++ * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/flash/nandflash.c: ++ * Copyright 2000-2010 Broadcom Corporation ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "brcmnand.h" ++ ++struct bcm6368_nand_soc { ++ struct brcmnand_soc soc; ++ void __iomem *base; ++}; ++ ++#define BCM6368_NAND_INT 0x00 ++#define BCM6368_NAND_STATUS_SHIFT 0 ++#define BCM6368_NAND_STATUS_MASK (0xfff << BCM6368_NAND_STATUS_SHIFT) ++#define BCM6368_NAND_ENABLE_SHIFT 16 ++#define BCM6368_NAND_ENABLE_MASK (0xffff << BCM6368_NAND_ENABLE_SHIFT) ++#define BCM6368_NAND_BASE_ADDR0 0x04 ++#define BCM6368_NAND_BASE_ADDR1 0x0c ++ ++enum { ++ BCM6368_NP_READ = BIT(0), ++ BCM6368_BLOCK_ERASE = BIT(1), ++ BCM6368_COPY_BACK = BIT(2), ++ BCM6368_PAGE_PGM = BIT(3), ++ BCM6368_CTRL_READY = BIT(4), ++ BCM6368_DEV_RBPIN = BIT(5), ++ BCM6368_ECC_ERR_UNC = BIT(6), ++ BCM6368_ECC_ERR_CORR = BIT(7), ++}; ++ ++static bool bcm6368_nand_intc_ack(struct brcmnand_soc *soc) ++{ ++ struct bcm6368_nand_soc *priv = ++ container_of(soc, struct bcm6368_nand_soc, soc); ++ void __iomem *mmio = priv->base + BCM6368_NAND_INT; ++ u32 val = brcmnand_readl(mmio); ++ ++ if (val & (BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT)) { ++ /* Ack interrupt */ ++ val &= ~BCM6368_NAND_STATUS_MASK; ++ val |= BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT; ++ brcmnand_writel(val, mmio); ++ return true; ++ } ++ ++ return false; ++} ++ ++static void bcm6368_nand_intc_set(struct brcmnand_soc *soc, bool en) ++{ ++ struct bcm6368_nand_soc *priv = ++ container_of(soc, struct bcm6368_nand_soc, soc); ++ void __iomem *mmio = priv->base + BCM6368_NAND_INT; ++ u32 val = brcmnand_readl(mmio); ++ ++ /* Don't ack any interrupts */ ++ val &= ~BCM6368_NAND_STATUS_MASK; ++ ++ if (en) ++ val |= BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT; ++ else ++ val &= ~(BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT); ++ ++ brcmnand_writel(val, mmio); ++} ++ ++static int bcm6368_nand_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct bcm6368_nand_soc *priv; ++ struct brcmnand_soc *soc; ++ struct resource *res; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ soc = &priv->soc; ++ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "nand-int-base"); ++ priv->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ soc->ctlrdy_ack = bcm6368_nand_intc_ack; ++ soc->ctlrdy_set_enabled = bcm6368_nand_intc_set; ++ ++ /* Disable and ack all interrupts */ ++ brcmnand_writel(0, priv->base + BCM6368_NAND_INT); ++ brcmnand_writel(BCM6368_NAND_STATUS_MASK, ++ priv->base + BCM6368_NAND_INT); ++ ++ return brcmnand_probe(pdev, soc); ++} ++ ++static const struct of_device_id bcm6368_nand_of_match[] = { ++ { .compatible = "brcm,nand-bcm6368" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match); ++ ++static struct platform_driver bcm6368_nand_driver = { ++ .probe = bcm6368_nand_probe, ++ .remove = brcmnand_remove, ++ .driver = { ++ .name = "bcm6368_nand", ++ .pm = &brcmnand_pm_ops, ++ .of_match_table = bcm6368_nand_of_match, ++ } ++}; ++module_platform_driver(bcm6368_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Simon Arlott"); ++MODULE_DESCRIPTION("NAND driver for BCM6368"); +diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c +new file mode 100644 +index 00000000..1866ac5 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c +@@ -0,0 +1,2617 @@ ++/* ++ * Copyright © 2010-2015 Broadcom Corporation ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "brcmnand.h" ++ ++/* ++ * This flag controls if WP stays on between erase/write commands to mitigate ++ * flash corruption due to power glitches. Values: ++ * 0: NAND_WP is not used or not available ++ * 1: NAND_WP is set by default, cleared for erase/write operations ++ * 2: NAND_WP is always cleared ++ */ ++static int wp_on = 1; ++module_param(wp_on, int, 0444); ++ ++/*********************************************************************** ++ * Definitions ++ ***********************************************************************/ ++ ++#define DRV_NAME "brcmnand" ++ ++#define CMD_NULL 0x00 ++#define CMD_PAGE_READ 0x01 ++#define CMD_SPARE_AREA_READ 0x02 ++#define CMD_STATUS_READ 0x03 ++#define CMD_PROGRAM_PAGE 0x04 ++#define CMD_PROGRAM_SPARE_AREA 0x05 ++#define CMD_COPY_BACK 0x06 ++#define CMD_DEVICE_ID_READ 0x07 ++#define CMD_BLOCK_ERASE 0x08 ++#define CMD_FLASH_RESET 0x09 ++#define CMD_BLOCKS_LOCK 0x0a ++#define CMD_BLOCKS_LOCK_DOWN 0x0b ++#define CMD_BLOCKS_UNLOCK 0x0c ++#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d ++#define CMD_PARAMETER_READ 0x0e ++#define CMD_PARAMETER_CHANGE_COL 0x0f ++#define CMD_LOW_LEVEL_OP 0x10 ++ ++struct brcm_nand_dma_desc { ++ u32 next_desc; ++ u32 next_desc_ext; ++ u32 cmd_irq; ++ u32 dram_addr; ++ u32 dram_addr_ext; ++ u32 tfr_len; ++ u32 total_len; ++ u32 flash_addr; ++ u32 flash_addr_ext; ++ u32 cs; ++ u32 pad2[5]; ++ u32 status_valid; ++} __packed; ++ ++/* Bitfields for brcm_nand_dma_desc::status_valid */ ++#define FLASH_DMA_ECC_ERROR (1 << 8) ++#define FLASH_DMA_CORR_ERROR (1 << 9) ++ ++/* 512B flash cache in the NAND controller HW */ ++#define FC_SHIFT 9U ++#define FC_BYTES 512U ++#define FC_WORDS (FC_BYTES >> 2) ++ ++#define BRCMNAND_MIN_PAGESIZE 512 ++#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) ++#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) ++ ++#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) ++#define NAND_POLL_STATUS_TIMEOUT_MS 100 ++ ++/* Controller feature flags */ ++enum { ++ BRCMNAND_HAS_1K_SECTORS = BIT(0), ++ BRCMNAND_HAS_PREFETCH = BIT(1), ++ BRCMNAND_HAS_CACHE_MODE = BIT(2), ++ BRCMNAND_HAS_WP = BIT(3), ++}; ++ ++struct brcmnand_controller { ++ struct device *dev; ++ struct nand_controller controller; ++ void __iomem *nand_base; ++ void __iomem *nand_fc; /* flash cache */ ++ void __iomem *flash_dma_base; ++ unsigned int irq; ++ unsigned int dma_irq; ++ int nand_version; ++ ++ /* Some SoCs provide custom interrupt status register(s) */ ++ struct brcmnand_soc *soc; ++ ++ /* Some SoCs have a gateable clock for the controller */ ++ struct clk *clk; ++ ++ int cmd_pending; ++ bool dma_pending; ++ struct completion done; ++ struct completion dma_done; ++ ++ /* List of NAND hosts (one for each chip-select) */ ++ struct list_head host_list; ++ ++ struct brcm_nand_dma_desc *dma_desc; ++ dma_addr_t dma_pa; ++ ++ /* in-memory cache of the FLASH_CACHE, used only for some commands */ ++ u8 flash_cache[FC_BYTES]; ++ ++ /* Controller revision details */ ++ const u16 *reg_offsets; ++ unsigned int reg_spacing; /* between CS1, CS2, ... regs */ ++ const u8 *cs_offsets; /* within each chip-select */ ++ const u8 *cs0_offsets; /* within CS0, if different */ ++ unsigned int max_block_size; ++ const unsigned int *block_sizes; ++ unsigned int max_page_size; ++ const unsigned int *page_sizes; ++ unsigned int max_oob; ++ u32 features; ++ ++ /* for low-power standby/resume only */ ++ u32 nand_cs_nand_select; ++ u32 nand_cs_nand_xor; ++ u32 corr_stat_threshold; ++ u32 flash_dma_mode; ++}; ++ ++struct brcmnand_cfg { ++ u64 device_size; ++ unsigned int block_size; ++ unsigned int page_size; ++ unsigned int spare_area_size; ++ unsigned int device_width; ++ unsigned int col_adr_bytes; ++ unsigned int blk_adr_bytes; ++ unsigned int ful_adr_bytes; ++ unsigned int sector_size_1k; ++ unsigned int ecc_level; ++ /* use for low-power standby/resume only */ ++ u32 acc_control; ++ u32 config; ++ u32 config_ext; ++ u32 timing_1; ++ u32 timing_2; ++}; ++ ++struct brcmnand_host { ++ struct list_head node; ++ ++ struct nand_chip chip; ++ struct platform_device *pdev; ++ int cs; ++ ++ unsigned int last_cmd; ++ unsigned int last_byte; ++ u64 last_addr; ++ struct brcmnand_cfg hwcfg; ++ struct brcmnand_controller *ctrl; ++}; ++ ++enum brcmnand_reg { ++ BRCMNAND_CMD_START = 0, ++ BRCMNAND_CMD_EXT_ADDRESS, ++ BRCMNAND_CMD_ADDRESS, ++ BRCMNAND_INTFC_STATUS, ++ BRCMNAND_CS_SELECT, ++ BRCMNAND_CS_XOR, ++ BRCMNAND_LL_OP, ++ BRCMNAND_CS0_BASE, ++ BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ ++ BRCMNAND_CORR_THRESHOLD, ++ BRCMNAND_CORR_THRESHOLD_EXT, ++ BRCMNAND_UNCORR_COUNT, ++ BRCMNAND_CORR_COUNT, ++ BRCMNAND_CORR_EXT_ADDR, ++ BRCMNAND_CORR_ADDR, ++ BRCMNAND_UNCORR_EXT_ADDR, ++ BRCMNAND_UNCORR_ADDR, ++ BRCMNAND_SEMAPHORE, ++ BRCMNAND_ID, ++ BRCMNAND_ID_EXT, ++ BRCMNAND_LL_RDATA, ++ BRCMNAND_OOB_READ_BASE, ++ BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ ++ BRCMNAND_OOB_WRITE_BASE, ++ BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ ++ BRCMNAND_FC_BASE, ++}; ++ ++/* BRCMNAND v4.0 */ ++static const u16 brcmnand_regs_v40[] = { ++ [BRCMNAND_CMD_START] = 0x04, ++ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, ++ [BRCMNAND_CMD_ADDRESS] = 0x0c, ++ [BRCMNAND_INTFC_STATUS] = 0x6c, ++ [BRCMNAND_CS_SELECT] = 0x14, ++ [BRCMNAND_CS_XOR] = 0x18, ++ [BRCMNAND_LL_OP] = 0x178, ++ [BRCMNAND_CS0_BASE] = 0x40, ++ [BRCMNAND_CS1_BASE] = 0xd0, ++ [BRCMNAND_CORR_THRESHOLD] = 0x84, ++ [BRCMNAND_CORR_THRESHOLD_EXT] = 0, ++ [BRCMNAND_UNCORR_COUNT] = 0, ++ [BRCMNAND_CORR_COUNT] = 0, ++ [BRCMNAND_CORR_EXT_ADDR] = 0x70, ++ [BRCMNAND_CORR_ADDR] = 0x74, ++ [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, ++ [BRCMNAND_UNCORR_ADDR] = 0x7c, ++ [BRCMNAND_SEMAPHORE] = 0x58, ++ [BRCMNAND_ID] = 0x60, ++ [BRCMNAND_ID_EXT] = 0x64, ++ [BRCMNAND_LL_RDATA] = 0x17c, ++ [BRCMNAND_OOB_READ_BASE] = 0x20, ++ [BRCMNAND_OOB_READ_10_BASE] = 0x130, ++ [BRCMNAND_OOB_WRITE_BASE] = 0x30, ++ [BRCMNAND_OOB_WRITE_10_BASE] = 0, ++ [BRCMNAND_FC_BASE] = 0x200, ++}; ++ ++/* BRCMNAND v5.0 */ ++static const u16 brcmnand_regs_v50[] = { ++ [BRCMNAND_CMD_START] = 0x04, ++ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, ++ [BRCMNAND_CMD_ADDRESS] = 0x0c, ++ [BRCMNAND_INTFC_STATUS] = 0x6c, ++ [BRCMNAND_CS_SELECT] = 0x14, ++ [BRCMNAND_CS_XOR] = 0x18, ++ [BRCMNAND_LL_OP] = 0x178, ++ [BRCMNAND_CS0_BASE] = 0x40, ++ [BRCMNAND_CS1_BASE] = 0xd0, ++ [BRCMNAND_CORR_THRESHOLD] = 0x84, ++ [BRCMNAND_CORR_THRESHOLD_EXT] = 0, ++ [BRCMNAND_UNCORR_COUNT] = 0, ++ [BRCMNAND_CORR_COUNT] = 0, ++ [BRCMNAND_CORR_EXT_ADDR] = 0x70, ++ [BRCMNAND_CORR_ADDR] = 0x74, ++ [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, ++ [BRCMNAND_UNCORR_ADDR] = 0x7c, ++ [BRCMNAND_SEMAPHORE] = 0x58, ++ [BRCMNAND_ID] = 0x60, ++ [BRCMNAND_ID_EXT] = 0x64, ++ [BRCMNAND_LL_RDATA] = 0x17c, ++ [BRCMNAND_OOB_READ_BASE] = 0x20, ++ [BRCMNAND_OOB_READ_10_BASE] = 0x130, ++ [BRCMNAND_OOB_WRITE_BASE] = 0x30, ++ [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, ++ [BRCMNAND_FC_BASE] = 0x200, ++}; ++ ++/* BRCMNAND v6.0 - v7.1 */ ++static const u16 brcmnand_regs_v60[] = { ++ [BRCMNAND_CMD_START] = 0x04, ++ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, ++ [BRCMNAND_CMD_ADDRESS] = 0x0c, ++ [BRCMNAND_INTFC_STATUS] = 0x14, ++ [BRCMNAND_CS_SELECT] = 0x18, ++ [BRCMNAND_CS_XOR] = 0x1c, ++ [BRCMNAND_LL_OP] = 0x20, ++ [BRCMNAND_CS0_BASE] = 0x50, ++ [BRCMNAND_CS1_BASE] = 0, ++ [BRCMNAND_CORR_THRESHOLD] = 0xc0, ++ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, ++ [BRCMNAND_UNCORR_COUNT] = 0xfc, ++ [BRCMNAND_CORR_COUNT] = 0x100, ++ [BRCMNAND_CORR_EXT_ADDR] = 0x10c, ++ [BRCMNAND_CORR_ADDR] = 0x110, ++ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, ++ [BRCMNAND_UNCORR_ADDR] = 0x118, ++ [BRCMNAND_SEMAPHORE] = 0x150, ++ [BRCMNAND_ID] = 0x194, ++ [BRCMNAND_ID_EXT] = 0x198, ++ [BRCMNAND_LL_RDATA] = 0x19c, ++ [BRCMNAND_OOB_READ_BASE] = 0x200, ++ [BRCMNAND_OOB_READ_10_BASE] = 0, ++ [BRCMNAND_OOB_WRITE_BASE] = 0x280, ++ [BRCMNAND_OOB_WRITE_10_BASE] = 0, ++ [BRCMNAND_FC_BASE] = 0x400, ++}; ++ ++/* BRCMNAND v7.1 */ ++static const u16 brcmnand_regs_v71[] = { ++ [BRCMNAND_CMD_START] = 0x04, ++ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, ++ [BRCMNAND_CMD_ADDRESS] = 0x0c, ++ [BRCMNAND_INTFC_STATUS] = 0x14, ++ [BRCMNAND_CS_SELECT] = 0x18, ++ [BRCMNAND_CS_XOR] = 0x1c, ++ [BRCMNAND_LL_OP] = 0x20, ++ [BRCMNAND_CS0_BASE] = 0x50, ++ [BRCMNAND_CS1_BASE] = 0, ++ [BRCMNAND_CORR_THRESHOLD] = 0xdc, ++ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, ++ [BRCMNAND_UNCORR_COUNT] = 0xfc, ++ [BRCMNAND_CORR_COUNT] = 0x100, ++ [BRCMNAND_CORR_EXT_ADDR] = 0x10c, ++ [BRCMNAND_CORR_ADDR] = 0x110, ++ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, ++ [BRCMNAND_UNCORR_ADDR] = 0x118, ++ [BRCMNAND_SEMAPHORE] = 0x150, ++ [BRCMNAND_ID] = 0x194, ++ [BRCMNAND_ID_EXT] = 0x198, ++ [BRCMNAND_LL_RDATA] = 0x19c, ++ [BRCMNAND_OOB_READ_BASE] = 0x200, ++ [BRCMNAND_OOB_READ_10_BASE] = 0, ++ [BRCMNAND_OOB_WRITE_BASE] = 0x280, ++ [BRCMNAND_OOB_WRITE_10_BASE] = 0, ++ [BRCMNAND_FC_BASE] = 0x400, ++}; ++ ++/* BRCMNAND v7.2 */ ++static const u16 brcmnand_regs_v72[] = { ++ [BRCMNAND_CMD_START] = 0x04, ++ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, ++ [BRCMNAND_CMD_ADDRESS] = 0x0c, ++ [BRCMNAND_INTFC_STATUS] = 0x14, ++ [BRCMNAND_CS_SELECT] = 0x18, ++ [BRCMNAND_CS_XOR] = 0x1c, ++ [BRCMNAND_LL_OP] = 0x20, ++ [BRCMNAND_CS0_BASE] = 0x50, ++ [BRCMNAND_CS1_BASE] = 0, ++ [BRCMNAND_CORR_THRESHOLD] = 0xdc, ++ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, ++ [BRCMNAND_UNCORR_COUNT] = 0xfc, ++ [BRCMNAND_CORR_COUNT] = 0x100, ++ [BRCMNAND_CORR_EXT_ADDR] = 0x10c, ++ [BRCMNAND_CORR_ADDR] = 0x110, ++ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, ++ [BRCMNAND_UNCORR_ADDR] = 0x118, ++ [BRCMNAND_SEMAPHORE] = 0x150, ++ [BRCMNAND_ID] = 0x194, ++ [BRCMNAND_ID_EXT] = 0x198, ++ [BRCMNAND_LL_RDATA] = 0x19c, ++ [BRCMNAND_OOB_READ_BASE] = 0x200, ++ [BRCMNAND_OOB_READ_10_BASE] = 0, ++ [BRCMNAND_OOB_WRITE_BASE] = 0x400, ++ [BRCMNAND_OOB_WRITE_10_BASE] = 0, ++ [BRCMNAND_FC_BASE] = 0x600, ++}; ++ ++enum brcmnand_cs_reg { ++ BRCMNAND_CS_CFG_EXT = 0, ++ BRCMNAND_CS_CFG, ++ BRCMNAND_CS_ACC_CONTROL, ++ BRCMNAND_CS_TIMING1, ++ BRCMNAND_CS_TIMING2, ++}; ++ ++/* Per chip-select offsets for v7.1 */ ++static const u8 brcmnand_cs_offsets_v71[] = { ++ [BRCMNAND_CS_ACC_CONTROL] = 0x00, ++ [BRCMNAND_CS_CFG_EXT] = 0x04, ++ [BRCMNAND_CS_CFG] = 0x08, ++ [BRCMNAND_CS_TIMING1] = 0x0c, ++ [BRCMNAND_CS_TIMING2] = 0x10, ++}; ++ ++/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ ++static const u8 brcmnand_cs_offsets[] = { ++ [BRCMNAND_CS_ACC_CONTROL] = 0x00, ++ [BRCMNAND_CS_CFG_EXT] = 0x04, ++ [BRCMNAND_CS_CFG] = 0x04, ++ [BRCMNAND_CS_TIMING1] = 0x08, ++ [BRCMNAND_CS_TIMING2] = 0x0c, ++}; ++ ++/* Per chip-select offset for <= v5.0 on CS0 only */ ++static const u8 brcmnand_cs_offsets_cs0[] = { ++ [BRCMNAND_CS_ACC_CONTROL] = 0x00, ++ [BRCMNAND_CS_CFG_EXT] = 0x08, ++ [BRCMNAND_CS_CFG] = 0x08, ++ [BRCMNAND_CS_TIMING1] = 0x10, ++ [BRCMNAND_CS_TIMING2] = 0x14, ++}; ++ ++/* ++ * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had ++ * one config register, but once the bitfields overflowed, newer controllers ++ * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around. ++ */ ++enum { ++ CFG_BLK_ADR_BYTES_SHIFT = 8, ++ CFG_COL_ADR_BYTES_SHIFT = 12, ++ CFG_FUL_ADR_BYTES_SHIFT = 16, ++ CFG_BUS_WIDTH_SHIFT = 23, ++ CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT), ++ CFG_DEVICE_SIZE_SHIFT = 24, ++ ++ /* Only for pre-v7.1 (with no CFG_EXT register) */ ++ CFG_PAGE_SIZE_SHIFT = 20, ++ CFG_BLK_SIZE_SHIFT = 28, ++ ++ /* Only for v7.1+ (with CFG_EXT register) */ ++ CFG_EXT_PAGE_SIZE_SHIFT = 0, ++ CFG_EXT_BLK_SIZE_SHIFT = 4, ++}; ++ ++/* BRCMNAND_INTFC_STATUS */ ++enum { ++ INTFC_FLASH_STATUS = GENMASK(7, 0), ++ ++ INTFC_ERASED = BIT(27), ++ INTFC_OOB_VALID = BIT(28), ++ INTFC_CACHE_VALID = BIT(29), ++ INTFC_FLASH_READY = BIT(30), ++ INTFC_CTLR_READY = BIT(31), ++}; ++ ++static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) ++{ ++ return brcmnand_readl(ctrl->nand_base + offs); ++} ++ ++static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, ++ u32 val) ++{ ++ brcmnand_writel(val, ctrl->nand_base + offs); ++} ++ ++static int brcmnand_revision_init(struct brcmnand_controller *ctrl) ++{ ++ static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; ++ static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; ++ static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; ++ ++ ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; ++ ++ /* Only support v4.0+? */ ++ if (ctrl->nand_version < 0x0400) { ++ dev_err(ctrl->dev, "version %#x not supported\n", ++ ctrl->nand_version); ++ return -ENODEV; ++ } ++ ++ /* Register offsets */ ++ if (ctrl->nand_version >= 0x0702) ++ ctrl->reg_offsets = brcmnand_regs_v72; ++ else if (ctrl->nand_version >= 0x0701) ++ ctrl->reg_offsets = brcmnand_regs_v71; ++ else if (ctrl->nand_version >= 0x0600) ++ ctrl->reg_offsets = brcmnand_regs_v60; ++ else if (ctrl->nand_version >= 0x0500) ++ ctrl->reg_offsets = brcmnand_regs_v50; ++ else if (ctrl->nand_version >= 0x0400) ++ ctrl->reg_offsets = brcmnand_regs_v40; ++ ++ /* Chip-select stride */ ++ if (ctrl->nand_version >= 0x0701) ++ ctrl->reg_spacing = 0x14; ++ else ++ ctrl->reg_spacing = 0x10; ++ ++ /* Per chip-select registers */ ++ if (ctrl->nand_version >= 0x0701) { ++ ctrl->cs_offsets = brcmnand_cs_offsets_v71; ++ } else { ++ ctrl->cs_offsets = brcmnand_cs_offsets; ++ ++ /* v5.0 and earlier has a different CS0 offset layout */ ++ if (ctrl->nand_version <= 0x0500) ++ ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; ++ } ++ ++ /* Page / block sizes */ ++ if (ctrl->nand_version >= 0x0701) { ++ /* >= v7.1 use nice power-of-2 values! */ ++ ctrl->max_page_size = 16 * 1024; ++ ctrl->max_block_size = 2 * 1024 * 1024; ++ } else { ++ ctrl->page_sizes = page_sizes; ++ if (ctrl->nand_version >= 0x0600) ++ ctrl->block_sizes = block_sizes_v6; ++ else ++ ctrl->block_sizes = block_sizes_v4; ++ ++ if (ctrl->nand_version < 0x0400) { ++ ctrl->max_page_size = 4096; ++ ctrl->max_block_size = 512 * 1024; ++ } ++ } ++ ++ /* Maximum spare area sector size (per 512B) */ ++ if (ctrl->nand_version >= 0x0702) ++ ctrl->max_oob = 128; ++ else if (ctrl->nand_version >= 0x0600) ++ ctrl->max_oob = 64; ++ else if (ctrl->nand_version >= 0x0500) ++ ctrl->max_oob = 32; ++ else ++ ctrl->max_oob = 16; ++ ++ /* v6.0 and newer (except v6.1) have prefetch support */ ++ if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) ++ ctrl->features |= BRCMNAND_HAS_PREFETCH; ++ ++ /* ++ * v6.x has cache mode, but it's implemented differently. Ignore it for ++ * now. ++ */ ++ if (ctrl->nand_version >= 0x0700) ++ ctrl->features |= BRCMNAND_HAS_CACHE_MODE; ++ ++ if (ctrl->nand_version >= 0x0500) ++ ctrl->features |= BRCMNAND_HAS_1K_SECTORS; ++ ++ if (ctrl->nand_version >= 0x0700) ++ ctrl->features |= BRCMNAND_HAS_WP; ++ else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) ++ ctrl->features |= BRCMNAND_HAS_WP; ++ ++ return 0; ++} ++ ++static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, ++ enum brcmnand_reg reg) ++{ ++ u16 offs = ctrl->reg_offsets[reg]; ++ ++ if (offs) ++ return nand_readreg(ctrl, offs); ++ else ++ return 0; ++} ++ ++static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, ++ enum brcmnand_reg reg, u32 val) ++{ ++ u16 offs = ctrl->reg_offsets[reg]; ++ ++ if (offs) ++ nand_writereg(ctrl, offs, val); ++} ++ ++static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, ++ enum brcmnand_reg reg, u32 mask, unsigned ++ int shift, u32 val) ++{ ++ u32 tmp = brcmnand_read_reg(ctrl, reg); ++ ++ tmp &= ~mask; ++ tmp |= val << shift; ++ brcmnand_write_reg(ctrl, reg, tmp); ++} ++ ++static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) ++{ ++ return __raw_readl(ctrl->nand_fc + word * 4); ++} ++ ++static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, ++ int word, u32 val) ++{ ++ __raw_writel(val, ctrl->nand_fc + word * 4); ++} ++ ++static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, ++ enum brcmnand_cs_reg reg) ++{ ++ u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; ++ u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; ++ u8 cs_offs; ++ ++ if (cs == 0 && ctrl->cs0_offsets) ++ cs_offs = ctrl->cs0_offsets[reg]; ++ else ++ cs_offs = ctrl->cs_offsets[reg]; ++ ++ if (cs && offs_cs1) ++ return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; ++ ++ return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; ++} ++ ++static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) ++{ ++ if (ctrl->nand_version < 0x0600) ++ return 1; ++ return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); ++} ++ ++static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ unsigned int shift = 0, bits; ++ enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; ++ int cs = host->cs; ++ ++ if (ctrl->nand_version >= 0x0702) ++ bits = 7; ++ else if (ctrl->nand_version >= 0x0600) ++ bits = 6; ++ else if (ctrl->nand_version >= 0x0500) ++ bits = 5; ++ else ++ bits = 4; ++ ++ if (ctrl->nand_version >= 0x0702) { ++ if (cs >= 4) ++ reg = BRCMNAND_CORR_THRESHOLD_EXT; ++ shift = (cs % 4) * bits; ++ } else if (ctrl->nand_version >= 0x0600) { ++ if (cs >= 5) ++ reg = BRCMNAND_CORR_THRESHOLD_EXT; ++ shift = (cs % 5) * bits; ++ } ++ brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); ++} ++ ++static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) ++{ ++ if (ctrl->nand_version < 0x0602) ++ return 24; ++ return 0; ++} ++ ++/*********************************************************************** ++ * NAND ACC CONTROL bitfield ++ * ++ * Some bits have remained constant throughout hardware revision, while ++ * others have shifted around. ++ ***********************************************************************/ ++ ++/* Constant for all versions (where supported) */ ++enum { ++ /* See BRCMNAND_HAS_CACHE_MODE */ ++ ACC_CONTROL_CACHE_MODE = BIT(22), ++ ++ /* See BRCMNAND_HAS_PREFETCH */ ++ ACC_CONTROL_PREFETCH = BIT(23), ++ ++ ACC_CONTROL_PAGE_HIT = BIT(24), ++ ACC_CONTROL_WR_PREEMPT = BIT(25), ++ ACC_CONTROL_PARTIAL_PAGE = BIT(26), ++ ACC_CONTROL_RD_ERASED = BIT(27), ++ ACC_CONTROL_FAST_PGM_RDIN = BIT(28), ++ ACC_CONTROL_WR_ECC = BIT(30), ++ ACC_CONTROL_RD_ECC = BIT(31), ++}; ++ ++static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) ++{ ++ if (ctrl->nand_version >= 0x0702) ++ return GENMASK(7, 0); ++ else if (ctrl->nand_version >= 0x0600) ++ return GENMASK(6, 0); ++ else ++ return GENMASK(5, 0); ++} ++ ++#define NAND_ACC_CONTROL_ECC_SHIFT 16 ++#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 ++ ++static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) ++{ ++ u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; ++ ++ mask <<= NAND_ACC_CONTROL_ECC_SHIFT; ++ ++ /* v7.2 includes additional ECC levels */ ++ if (ctrl->nand_version >= 0x0702) ++ mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; ++ ++ return mask; ++} ++ ++static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); ++ u32 acc_control = nand_readreg(ctrl, offs); ++ u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; ++ ++ if (en) { ++ acc_control |= ecc_flags; /* enable RD/WR ECC */ ++ acc_control |= host->hwcfg.ecc_level ++ << NAND_ACC_CONTROL_ECC_SHIFT; ++ } else { ++ acc_control &= ~ecc_flags; /* disable RD/WR ECC */ ++ acc_control &= ~brcmnand_ecc_level_mask(ctrl); ++ } ++ ++ nand_writereg(ctrl, offs, acc_control); ++} ++ ++static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) ++{ ++ if (ctrl->nand_version >= 0x0702) ++ return 9; ++ else if (ctrl->nand_version >= 0x0600) ++ return 7; ++ else if (ctrl->nand_version >= 0x0500) ++ return 6; ++ else ++ return -1; ++} ++ ++static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ int shift = brcmnand_sector_1k_shift(ctrl); ++ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, ++ BRCMNAND_CS_ACC_CONTROL); ++ ++ if (shift < 0) ++ return 0; ++ ++ return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; ++} ++ ++static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ int shift = brcmnand_sector_1k_shift(ctrl); ++ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, ++ BRCMNAND_CS_ACC_CONTROL); ++ u32 tmp; ++ ++ if (shift < 0) ++ return; ++ ++ tmp = nand_readreg(ctrl, acc_control_offs); ++ tmp &= ~(1 << shift); ++ tmp |= (!!val) << shift; ++ nand_writereg(ctrl, acc_control_offs, tmp); ++} ++ ++/*********************************************************************** ++ * CS_NAND_SELECT ++ ***********************************************************************/ ++ ++enum { ++ CS_SELECT_NAND_WP = BIT(29), ++ CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), ++}; ++ ++static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, ++ u32 mask, u32 expected_val, ++ unsigned long timeout_ms) ++{ ++ unsigned long limit; ++ u32 val; ++ ++ if (!timeout_ms) ++ timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS; ++ ++ limit = jiffies + msecs_to_jiffies(timeout_ms); ++ do { ++ val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); ++ if ((val & mask) == expected_val) ++ return 0; ++ ++ cpu_relax(); ++ } while (time_after(limit, jiffies)); ++ ++ dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n", ++ expected_val, val & mask); ++ ++ return -ETIMEDOUT; ++} ++ ++static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) ++{ ++ u32 val = en ? CS_SELECT_NAND_WP : 0; ++ ++ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); ++} ++ ++/*********************************************************************** ++ * Flash DMA ++ ***********************************************************************/ ++ ++enum flash_dma_reg { ++ FLASH_DMA_REVISION = 0x00, ++ FLASH_DMA_FIRST_DESC = 0x04, ++ FLASH_DMA_FIRST_DESC_EXT = 0x08, ++ FLASH_DMA_CTRL = 0x0c, ++ FLASH_DMA_MODE = 0x10, ++ FLASH_DMA_STATUS = 0x14, ++ FLASH_DMA_INTERRUPT_DESC = 0x18, ++ FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, ++ FLASH_DMA_ERROR_STATUS = 0x20, ++ FLASH_DMA_CURRENT_DESC = 0x24, ++ FLASH_DMA_CURRENT_DESC_EXT = 0x28, ++}; ++ ++static inline bool has_flash_dma(struct brcmnand_controller *ctrl) ++{ ++ return ctrl->flash_dma_base; ++} ++ ++static inline bool flash_dma_buf_ok(const void *buf) ++{ ++ return buf && !is_vmalloc_addr(buf) && ++ likely(IS_ALIGNED((uintptr_t)buf, 4)); ++} ++ ++static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, ++ u32 val) ++{ ++ brcmnand_writel(val, ctrl->flash_dma_base + offs); ++} ++ ++static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) ++{ ++ return brcmnand_readl(ctrl->flash_dma_base + offs); ++} ++ ++/* Low-level operation types: command, address, write, or read */ ++enum brcmnand_llop_type { ++ LL_OP_CMD, ++ LL_OP_ADDR, ++ LL_OP_WR, ++ LL_OP_RD, ++}; ++ ++/*********************************************************************** ++ * Internal support functions ++ ***********************************************************************/ ++ ++static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, ++ struct brcmnand_cfg *cfg) ++{ ++ if (ctrl->nand_version <= 0x0701) ++ return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && ++ cfg->ecc_level == 15; ++ else ++ return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 && ++ cfg->ecc_level == 15) || ++ (cfg->spare_area_size == 28 && cfg->ecc_level == 16)); ++} ++ ++/* ++ * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given ++ * the layout/configuration. ++ * Returns -ERRCODE on failure. ++ */ ++static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_cfg *cfg = &host->hwcfg; ++ int sas = cfg->spare_area_size << cfg->sector_size_1k; ++ int sectors = cfg->page_size / (512 << cfg->sector_size_1k); ++ ++ if (section >= sectors) ++ return -ERANGE; ++ ++ oobregion->offset = (section * sas) + 6; ++ oobregion->length = 3; ++ ++ return 0; ++} ++ ++static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_cfg *cfg = &host->hwcfg; ++ int sas = cfg->spare_area_size << cfg->sector_size_1k; ++ int sectors = cfg->page_size / (512 << cfg->sector_size_1k); ++ ++ if (section >= sectors * 2) ++ return -ERANGE; ++ ++ oobregion->offset = (section / 2) * sas; ++ ++ if (section & 1) { ++ oobregion->offset += 9; ++ oobregion->length = 7; ++ } else { ++ oobregion->length = 6; ++ ++ /* First sector of each page may have BBI */ ++ if (!section) { ++ /* ++ * Small-page NAND use byte 6 for BBI while large-page ++ * NAND use byte 0. ++ */ ++ if (cfg->page_size > 512) ++ oobregion->offset++; ++ oobregion->length--; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { ++ .ecc = brcmnand_hamming_ooblayout_ecc, ++ .free = brcmnand_hamming_ooblayout_free, ++}; ++ ++static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_cfg *cfg = &host->hwcfg; ++ int sas = cfg->spare_area_size << cfg->sector_size_1k; ++ int sectors = cfg->page_size / (512 << cfg->sector_size_1k); ++ ++ if (section >= sectors) ++ return -ERANGE; ++ ++ oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; ++ oobregion->length = chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_cfg *cfg = &host->hwcfg; ++ int sas = cfg->spare_area_size << cfg->sector_size_1k; ++ int sectors = cfg->page_size / (512 << cfg->sector_size_1k); ++ ++ if (section >= sectors) ++ return -ERANGE; ++ ++ if (sas <= chip->ecc.bytes) ++ return 0; ++ ++ oobregion->offset = section * sas; ++ oobregion->length = sas - chip->ecc.bytes; ++ ++ if (!section) { ++ oobregion->offset++; ++ oobregion->length--; ++ } ++ ++ return 0; ++} ++ ++static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_cfg *cfg = &host->hwcfg; ++ int sas = cfg->spare_area_size << cfg->sector_size_1k; ++ ++ if (section > 1 || sas - chip->ecc.bytes < 6 || ++ (section && sas - chip->ecc.bytes == 6)) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->offset = 0; ++ oobregion->length = 5; ++ } else { ++ oobregion->offset = 6; ++ oobregion->length = sas - chip->ecc.bytes - 6; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { ++ .ecc = brcmnand_bch_ooblayout_ecc, ++ .free = brcmnand_bch_ooblayout_free_lp, ++}; ++ ++static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { ++ .ecc = brcmnand_bch_ooblayout_ecc, ++ .free = brcmnand_bch_ooblayout_free_sp, ++}; ++ ++static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) ++{ ++ struct brcmnand_cfg *p = &host->hwcfg; ++ struct mtd_info *mtd = nand_to_mtd(&host->chip); ++ struct nand_ecc_ctrl *ecc = &host->chip.ecc; ++ unsigned int ecc_level = p->ecc_level; ++ int sas = p->spare_area_size << p->sector_size_1k; ++ int sectors = p->page_size / (512 << p->sector_size_1k); ++ ++ if (p->sector_size_1k) ++ ecc_level <<= 1; ++ ++ if (is_hamming_ecc(host->ctrl, p)) { ++ ecc->bytes = 3 * sectors; ++ mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); ++ return 0; ++ } ++ ++ /* ++ * CONTROLLER_VERSION: ++ * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) ++ * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) ++ * But we will just be conservative. ++ */ ++ ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); ++ if (p->page_size == 512) ++ mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); ++ else ++ mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); ++ ++ if (ecc->bytes >= sas) { ++ dev_err(&host->pdev->dev, ++ "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", ++ ecc->bytes, sas); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void brcmnand_wp(struct mtd_info *mtd, int wp) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ ++ if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { ++ static int old_wp = -1; ++ int ret; ++ ++ if (old_wp != wp) { ++ dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); ++ old_wp = wp; ++ } ++ ++ /* ++ * make sure ctrl/flash ready before and after ++ * changing state of #WP pin ++ */ ++ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | ++ NAND_STATUS_READY, ++ NAND_CTRL_RDY | ++ NAND_STATUS_READY, 0); ++ if (ret) ++ return; ++ ++ brcmnand_set_wp(ctrl, wp); ++ nand_status_op(chip, NULL); ++ /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ ++ ret = bcmnand_ctrl_poll_status(ctrl, ++ NAND_CTRL_RDY | ++ NAND_STATUS_READY | ++ NAND_STATUS_WP, ++ NAND_CTRL_RDY | ++ NAND_STATUS_READY | ++ (wp ? 0 : NAND_STATUS_WP), 0); ++ ++ if (ret) ++ dev_err_ratelimited(&host->pdev->dev, ++ "nand #WP expected %s\n", ++ wp ? "on" : "off"); ++ } ++} ++ ++/* Helper functions for reading and writing OOB registers */ ++static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) ++{ ++ u16 offset0, offset10, reg_offs; ++ ++ offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; ++ offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; ++ ++ if (offs >= ctrl->max_oob) ++ return 0x77; ++ ++ if (offs >= 16 && offset10) ++ reg_offs = offset10 + ((offs - 0x10) & ~0x03); ++ else ++ reg_offs = offset0 + (offs & ~0x03); ++ ++ return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); ++} ++ ++static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, ++ u32 data) ++{ ++ u16 offset0, offset10, reg_offs; ++ ++ offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; ++ offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; ++ ++ if (offs >= ctrl->max_oob) ++ return; ++ ++ if (offs >= 16 && offset10) ++ reg_offs = offset10 + ((offs - 0x10) & ~0x03); ++ else ++ reg_offs = offset0 + (offs & ~0x03); ++ ++ nand_writereg(ctrl, reg_offs, data); ++} ++ ++/* ++ * read_oob_from_regs - read data from OOB registers ++ * @ctrl: NAND controller ++ * @i: sub-page sector index ++ * @oob: buffer to read to ++ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) ++ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal ++ */ ++static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, ++ int sas, int sector_1k) ++{ ++ int tbytes = sas << sector_1k; ++ int j; ++ ++ /* Adjust OOB values for 1K sector size */ ++ if (sector_1k && (i & 0x01)) ++ tbytes = max(0, tbytes - (int)ctrl->max_oob); ++ tbytes = min_t(int, tbytes, ctrl->max_oob); ++ ++ for (j = 0; j < tbytes; j++) ++ oob[j] = oob_reg_read(ctrl, j); ++ return tbytes; ++} ++ ++/* ++ * write_oob_to_regs - write data to OOB registers ++ * @i: sub-page sector index ++ * @oob: buffer to write from ++ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) ++ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal ++ */ ++static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, ++ const u8 *oob, int sas, int sector_1k) ++{ ++ int tbytes = sas << sector_1k; ++ int j; ++ ++ /* Adjust OOB values for 1K sector size */ ++ if (sector_1k && (i & 0x01)) ++ tbytes = max(0, tbytes - (int)ctrl->max_oob); ++ tbytes = min_t(int, tbytes, ctrl->max_oob); ++ ++ for (j = 0; j < tbytes; j += 4) ++ oob_reg_write(ctrl, j, ++ (oob[j + 0] << 24) | ++ (oob[j + 1] << 16) | ++ (oob[j + 2] << 8) | ++ (oob[j + 3] << 0)); ++ return tbytes; ++} ++ ++static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) ++{ ++ struct brcmnand_controller *ctrl = data; ++ ++ /* Discard all NAND_CTLRDY interrupts during DMA */ ++ if (ctrl->dma_pending) ++ return IRQ_HANDLED; ++ ++ complete(&ctrl->done); ++ return IRQ_HANDLED; ++} ++ ++/* Handle SoC-specific interrupt hardware */ ++static irqreturn_t brcmnand_irq(int irq, void *data) ++{ ++ struct brcmnand_controller *ctrl = data; ++ ++ if (ctrl->soc->ctlrdy_ack(ctrl->soc)) ++ return brcmnand_ctlrdy_irq(irq, data); ++ ++ return IRQ_NONE; ++} ++ ++static irqreturn_t brcmnand_dma_irq(int irq, void *data) ++{ ++ struct brcmnand_controller *ctrl = data; ++ ++ complete(&ctrl->dma_done); ++ ++ return IRQ_HANDLED; ++} ++ ++static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ int ret; ++ ++ dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, ++ brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); ++ BUG_ON(ctrl->cmd_pending != 0); ++ ctrl->cmd_pending = cmd; ++ ++ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); ++ WARN_ON(ret); ++ ++ mb(); /* flush previous writes */ ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, ++ cmd << brcmnand_cmd_shift(ctrl)); ++} ++ ++/*********************************************************************** ++ * NAND MTD API: read/program/erase ++ ***********************************************************************/ ++ ++static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, ++ unsigned int ctrl) ++{ ++ /* intentionally left blank */ ++} ++ ++static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ unsigned long timeo = msecs_to_jiffies(100); ++ ++ dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); ++ if (ctrl->cmd_pending && ++ wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { ++ u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) ++ >> brcmnand_cmd_shift(ctrl); ++ ++ dev_err_ratelimited(ctrl->dev, ++ "timeout waiting for command %#02x\n", cmd); ++ dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", ++ brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); ++ } ++ ctrl->cmd_pending = 0; ++ return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & ++ INTFC_FLASH_STATUS; ++} ++ ++enum { ++ LLOP_RE = BIT(16), ++ LLOP_WE = BIT(17), ++ LLOP_ALE = BIT(18), ++ LLOP_CLE = BIT(19), ++ LLOP_RETURN_IDLE = BIT(31), ++ ++ LLOP_DATA_MASK = GENMASK(15, 0), ++}; ++ ++static int brcmnand_low_level_op(struct brcmnand_host *host, ++ enum brcmnand_llop_type type, u32 data, ++ bool last_op) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&host->chip); ++ struct nand_chip *chip = &host->chip; ++ struct brcmnand_controller *ctrl = host->ctrl; ++ u32 tmp; ++ ++ tmp = data & LLOP_DATA_MASK; ++ switch (type) { ++ case LL_OP_CMD: ++ tmp |= LLOP_WE | LLOP_CLE; ++ break; ++ case LL_OP_ADDR: ++ /* WE | ALE */ ++ tmp |= LLOP_WE | LLOP_ALE; ++ break; ++ case LL_OP_WR: ++ /* WE */ ++ tmp |= LLOP_WE; ++ break; ++ case LL_OP_RD: ++ /* RE */ ++ tmp |= LLOP_RE; ++ break; ++ } ++ if (last_op) ++ /* RETURN_IDLE */ ++ tmp |= LLOP_RETURN_IDLE; ++ ++ dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); ++ ++ brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); ++ ++ brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); ++ return brcmnand_waitfunc(mtd, chip); ++} ++ ++static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ u64 addr = (u64)page_addr << chip->page_shift; ++ int native_cmd = 0; ++ ++ if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || ++ command == NAND_CMD_RNDOUT) ++ addr = (u64)column; ++ /* Avoid propagating a negative, don't-care address */ ++ else if (page_addr < 0) ++ addr = 0; ++ ++ dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, ++ (unsigned long long)addr); ++ ++ host->last_cmd = command; ++ host->last_byte = 0; ++ host->last_addr = addr; ++ ++ switch (command) { ++ case NAND_CMD_RESET: ++ native_cmd = CMD_FLASH_RESET; ++ break; ++ case NAND_CMD_STATUS: ++ native_cmd = CMD_STATUS_READ; ++ break; ++ case NAND_CMD_READID: ++ native_cmd = CMD_DEVICE_ID_READ; ++ break; ++ case NAND_CMD_READOOB: ++ native_cmd = CMD_SPARE_AREA_READ; ++ break; ++ case NAND_CMD_ERASE1: ++ native_cmd = CMD_BLOCK_ERASE; ++ brcmnand_wp(mtd, 0); ++ break; ++ case NAND_CMD_PARAM: ++ native_cmd = CMD_PARAMETER_READ; ++ break; ++ case NAND_CMD_SET_FEATURES: ++ case NAND_CMD_GET_FEATURES: ++ brcmnand_low_level_op(host, LL_OP_CMD, command, false); ++ brcmnand_low_level_op(host, LL_OP_ADDR, column, false); ++ break; ++ case NAND_CMD_RNDOUT: ++ native_cmd = CMD_PARAMETER_CHANGE_COL; ++ addr &= ~((u64)(FC_BYTES - 1)); ++ /* ++ * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 ++ * NB: hwcfg.sector_size_1k may not be initialized yet ++ */ ++ if (brcmnand_get_sector_size_1k(host)) { ++ host->hwcfg.sector_size_1k = ++ brcmnand_get_sector_size_1k(host); ++ brcmnand_set_sector_size_1k(host, 0); ++ } ++ break; ++ } ++ ++ if (!native_cmd) ++ return; ++ ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, ++ (host->cs << 16) | ((addr >> 32) & 0xffff)); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); ++ ++ brcmnand_send_cmd(host, native_cmd); ++ brcmnand_waitfunc(mtd, chip); ++ ++ if (native_cmd == CMD_PARAMETER_READ || ++ native_cmd == CMD_PARAMETER_CHANGE_COL) { ++ /* Copy flash cache word-wise */ ++ u32 *flash_cache = (u32 *)ctrl->flash_cache; ++ int i; ++ ++ brcmnand_soc_data_bus_prepare(ctrl->soc, true); ++ ++ /* ++ * Must cache the FLASH_CACHE now, since changes in ++ * SECTOR_SIZE_1K may invalidate it ++ */ ++ for (i = 0; i < FC_WORDS; i++) ++ /* ++ * Flash cache is big endian for parameter pages, at ++ * least on STB SoCs ++ */ ++ flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i)); ++ ++ brcmnand_soc_data_bus_unprepare(ctrl->soc, true); ++ ++ /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ ++ if (host->hwcfg.sector_size_1k) ++ brcmnand_set_sector_size_1k(host, ++ host->hwcfg.sector_size_1k); ++ } ++ ++ /* Re-enable protection is necessary only after erase */ ++ if (command == NAND_CMD_ERASE1) ++ brcmnand_wp(mtd, 1); ++} ++ ++static uint8_t brcmnand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ uint8_t ret = 0; ++ int addr, offs; ++ ++ switch (host->last_cmd) { ++ case NAND_CMD_READID: ++ if (host->last_byte < 4) ++ ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> ++ (24 - (host->last_byte << 3)); ++ else if (host->last_byte < 8) ++ ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> ++ (56 - (host->last_byte << 3)); ++ break; ++ ++ case NAND_CMD_READOOB: ++ ret = oob_reg_read(ctrl, host->last_byte); ++ break; ++ ++ case NAND_CMD_STATUS: ++ ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & ++ INTFC_FLASH_STATUS; ++ if (wp_on) /* hide WP status */ ++ ret |= NAND_STATUS_WP; ++ break; ++ ++ case NAND_CMD_PARAM: ++ case NAND_CMD_RNDOUT: ++ addr = host->last_addr + host->last_byte; ++ offs = addr & (FC_BYTES - 1); ++ ++ /* At FC_BYTES boundary, switch to next column */ ++ if (host->last_byte > 0 && offs == 0) ++ nand_change_read_column_op(chip, addr, NULL, 0, false); ++ ++ ret = ctrl->flash_cache[offs]; ++ break; ++ case NAND_CMD_GET_FEATURES: ++ if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { ++ ret = 0; ++ } else { ++ bool last = host->last_byte == ++ ONFI_SUBFEATURE_PARAM_LEN - 1; ++ brcmnand_low_level_op(host, LL_OP_RD, 0, last); ++ ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; ++ } ++ } ++ ++ dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); ++ host->last_byte++; ++ ++ return ret; ++} ++ ++static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++, buf++) ++ *buf = brcmnand_read_byte(mtd); ++} ++ ++static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ ++ switch (host->last_cmd) { ++ case NAND_CMD_SET_FEATURES: ++ for (i = 0; i < len; i++) ++ brcmnand_low_level_op(host, LL_OP_WR, buf[i], ++ (i + 1) == len); ++ break; ++ default: ++ BUG(); ++ break; ++ } ++} ++ ++/** ++ * Construct a FLASH_DMA descriptor as part of a linked list. You must know the ++ * following ahead of time: ++ * - Is this descriptor the beginning or end of a linked list? ++ * - What is the (DMA) address of the next descriptor in the linked list? ++ */ ++static int brcmnand_fill_dma_desc(struct brcmnand_host *host, ++ struct brcm_nand_dma_desc *desc, u64 addr, ++ dma_addr_t buf, u32 len, u8 dma_cmd, ++ bool begin, bool end, ++ dma_addr_t next_desc) ++{ ++ memset(desc, 0, sizeof(*desc)); ++ /* Descriptors are written in native byte order (wordwise) */ ++ desc->next_desc = lower_32_bits(next_desc); ++ desc->next_desc_ext = upper_32_bits(next_desc); ++ desc->cmd_irq = (dma_cmd << 24) | ++ (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ ++ (!!begin) | ((!!end) << 1); /* head, tail */ ++#ifdef CONFIG_CPU_BIG_ENDIAN ++ desc->cmd_irq |= 0x01 << 12; ++#endif ++ desc->dram_addr = lower_32_bits(buf); ++ desc->dram_addr_ext = upper_32_bits(buf); ++ desc->tfr_len = len; ++ desc->total_len = len; ++ desc->flash_addr = lower_32_bits(addr); ++ desc->flash_addr_ext = upper_32_bits(addr); ++ desc->cs = host->cs; ++ desc->status_valid = 0x01; ++ return 0; ++} ++ ++/** ++ * Kick the FLASH_DMA engine, with a given DMA descriptor ++ */ ++static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ unsigned long timeo = msecs_to_jiffies(100); ++ ++ flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); ++ (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); ++ flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); ++ (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); ++ ++ /* Start FLASH_DMA engine */ ++ ctrl->dma_pending = true; ++ mb(); /* flush previous writes */ ++ flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ ++ ++ if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { ++ dev_err(ctrl->dev, ++ "timeout waiting for DMA; status %#x, error status %#x\n", ++ flash_dma_readl(ctrl, FLASH_DMA_STATUS), ++ flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); ++ } ++ ctrl->dma_pending = false; ++ flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ ++} ++ ++static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, ++ u32 len, u8 dma_cmd) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ dma_addr_t buf_pa; ++ int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ ++ buf_pa = dma_map_single(ctrl->dev, buf, len, dir); ++ if (dma_mapping_error(ctrl->dev, buf_pa)) { ++ dev_err(ctrl->dev, "unable to map buffer for DMA\n"); ++ return -ENOMEM; ++ } ++ ++ brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, ++ dma_cmd, true, true, 0); ++ ++ brcmnand_dma_run(host, ctrl->dma_pa); ++ ++ dma_unmap_single(ctrl->dev, buf_pa, len, dir); ++ ++ if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) ++ return -EBADMSG; ++ else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) ++ return -EUCLEAN; ++ ++ return 0; ++} ++ ++/* ++ * Assumes proper CS is already set ++ */ ++static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, ++ u64 addr, unsigned int trans, u32 *buf, ++ u8 *oob, u64 *err_addr) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ int i, j, ret = 0; ++ ++ /* Clear error addresses */ ++ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); ++ brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); ++ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); ++ brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); ++ ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, ++ (host->cs << 16) | ((addr >> 32) & 0xffff)); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); ++ ++ for (i = 0; i < trans; i++, addr += FC_BYTES) { ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, ++ lower_32_bits(addr)); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); ++ /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ ++ brcmnand_send_cmd(host, CMD_PAGE_READ); ++ brcmnand_waitfunc(mtd, chip); ++ ++ if (likely(buf)) { ++ brcmnand_soc_data_bus_prepare(ctrl->soc, false); ++ ++ for (j = 0; j < FC_WORDS; j++, buf++) ++ *buf = brcmnand_read_fc(ctrl, j); ++ ++ brcmnand_soc_data_bus_unprepare(ctrl->soc, false); ++ } ++ ++ if (oob) ++ oob += read_oob_from_regs(ctrl, i, oob, ++ mtd->oobsize / trans, ++ host->hwcfg.sector_size_1k); ++ ++ if (!ret) { ++ *err_addr = brcmnand_read_reg(ctrl, ++ BRCMNAND_UNCORR_ADDR) | ++ ((u64)(brcmnand_read_reg(ctrl, ++ BRCMNAND_UNCORR_EXT_ADDR) ++ & 0xffff) << 32); ++ if (*err_addr) ++ ret = -EBADMSG; ++ } ++ ++ if (!ret) { ++ *err_addr = brcmnand_read_reg(ctrl, ++ BRCMNAND_CORR_ADDR) | ++ ((u64)(brcmnand_read_reg(ctrl, ++ BRCMNAND_CORR_EXT_ADDR) ++ & 0xffff) << 32); ++ if (*err_addr) ++ ret = -EUCLEAN; ++ } ++ } ++ ++ return ret; ++} ++ ++/* ++ * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC ++ * error ++ * ++ * Because the HW ECC signals an ECC error if an erase paged has even a single ++ * bitflip, we must check each ECC error to see if it is actually an erased ++ * page with bitflips, not a truly corrupted page. ++ * ++ * On a real error, return a negative error code (-EBADMSG for ECC error), and ++ * buf will contain raw data. ++ * Otherwise, buf gets filled with 0xffs and return the maximum number of ++ * bitflips-per-ECC-sector to the caller. ++ * ++ */ ++static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, ++ struct nand_chip *chip, void *buf, u64 addr) ++{ ++ int i, sas; ++ void *oob = chip->oob_poi; ++ int bitflips = 0; ++ int page = addr >> chip->page_shift; ++ int ret; ++ ++ if (!buf) { ++ buf = chip->data_buf; ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ } ++ ++ sas = mtd->oobsize / chip->ecc.steps; ++ ++ /* read without ecc for verification */ ++ ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < chip->ecc.steps; i++, oob += sas) { ++ ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, ++ oob, sas, NULL, 0, ++ chip->ecc.strength); ++ if (ret < 0) ++ return ret; ++ ++ bitflips = max(bitflips, ret); ++ } ++ ++ return bitflips; ++} ++ ++static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, ++ u64 addr, unsigned int trans, u32 *buf, u8 *oob) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ u64 err_addr = 0; ++ int err; ++ bool retry = true; ++ ++ dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); ++ ++try_dmaread: ++ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); ++ ++ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { ++ err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, ++ CMD_PAGE_READ); ++ if (err) { ++ if (mtd_is_bitflip_or_eccerr(err)) ++ err_addr = addr; ++ else ++ return -EIO; ++ } ++ } else { ++ if (oob) ++ memset(oob, 0x99, mtd->oobsize); ++ ++ err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, ++ oob, &err_addr); ++ } ++ ++ if (mtd_is_eccerr(err)) { ++ /* ++ * On controller version and 7.0, 7.1 , DMA read after a ++ * prior PIO read that reported uncorrectable error, ++ * the DMA engine captures this error following DMA read ++ * cleared only on subsequent DMA read, so just retry once ++ * to clear a possible false error reported for current DMA ++ * read ++ */ ++ if ((ctrl->nand_version == 0x0700) || ++ (ctrl->nand_version == 0x0701)) { ++ if (retry) { ++ retry = false; ++ goto try_dmaread; ++ } ++ } ++ ++ /* ++ * Controller version 7.2 has hw encoder to detect erased page ++ * bitflips, apply sw verification for older controllers only ++ */ ++ if (ctrl->nand_version < 0x0702) { ++ err = brcmstb_nand_verify_erased_page(mtd, chip, buf, ++ addr); ++ /* erased page bitflips corrected */ ++ if (err >= 0) ++ return err; ++ } ++ ++ dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", ++ (unsigned long long)err_addr); ++ mtd->ecc_stats.failed++; ++ /* NAND layer expects zero on ECC errors */ ++ return 0; ++ } ++ ++ if (mtd_is_bitflip(err)) { ++ unsigned int corrected = brcmnand_count_corrected(ctrl); ++ ++ dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", ++ (unsigned long long)err_addr); ++ mtd->ecc_stats.corrected += corrected; ++ /* Always exceed the software-imposed threshold */ ++ return max(mtd->bitflip_threshold, corrected); ++ } ++ ++ return 0; ++} ++ ++static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ return brcmnand_read(mtd, chip, host->last_addr, ++ mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); ++} ++ ++static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; ++ int ret; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ brcmnand_set_ecc_enabled(host, 0); ++ ret = brcmnand_read(mtd, chip, host->last_addr, ++ mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); ++ brcmnand_set_ecc_enabled(host, 1); ++ return ret; ++} ++ ++static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, ++ mtd->writesize >> FC_SHIFT, ++ NULL, (u8 *)chip->oob_poi); ++} ++ ++static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ ++ brcmnand_set_ecc_enabled(host, 0); ++ brcmnand_read(mtd, chip, (u64)page << chip->page_shift, ++ mtd->writesize >> FC_SHIFT, ++ NULL, (u8 *)chip->oob_poi); ++ brcmnand_set_ecc_enabled(host, 1); ++ return 0; ++} ++ ++static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, ++ u64 addr, const u32 *buf, u8 *oob) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; ++ int status, ret = 0; ++ ++ dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); ++ ++ if (unlikely((unsigned long)buf & 0x03)) { ++ dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); ++ buf = (u32 *)((unsigned long)buf & ~0x03); ++ } ++ ++ brcmnand_wp(mtd, 0); ++ ++ for (i = 0; i < ctrl->max_oob; i += 4) ++ oob_reg_write(ctrl, i, 0xffffffff); ++ ++ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { ++ if (brcmnand_dma_trans(host, addr, (u32 *)buf, ++ mtd->writesize, CMD_PROGRAM_PAGE)) ++ ret = -EIO; ++ goto out; ++ } ++ ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, ++ (host->cs << 16) | ((addr >> 32) & 0xffff)); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); ++ ++ for (i = 0; i < trans; i++, addr += FC_BYTES) { ++ /* full address MUST be set before populating FC */ ++ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, ++ lower_32_bits(addr)); ++ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); ++ ++ if (buf) { ++ brcmnand_soc_data_bus_prepare(ctrl->soc, false); ++ ++ for (j = 0; j < FC_WORDS; j++, buf++) ++ brcmnand_write_fc(ctrl, j, *buf); ++ ++ brcmnand_soc_data_bus_unprepare(ctrl->soc, false); ++ } else if (oob) { ++ for (j = 0; j < FC_WORDS; j++) ++ brcmnand_write_fc(ctrl, j, 0xffffffff); ++ } ++ ++ if (oob) { ++ oob += write_oob_to_regs(ctrl, i, oob, ++ mtd->oobsize / trans, ++ host->hwcfg.sector_size_1k); ++ } ++ ++ /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ ++ brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); ++ status = brcmnand_waitfunc(mtd, chip); ++ ++ if (status & NAND_STATUS_FAIL) { ++ dev_info(ctrl->dev, "program failed at %llx\n", ++ (unsigned long long)addr); ++ ret = -EIO; ++ goto out; ++ } ++ } ++out: ++ brcmnand_wp(mtd, 1); ++ return ret; ++} ++ ++static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ void *oob = oob_required ? chip->oob_poi : NULL; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int brcmnand_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ void *oob = oob_required ? chip->oob_poi : NULL; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ brcmnand_set_ecc_enabled(host, 0); ++ brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); ++ brcmnand_set_ecc_enabled(host, 1); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, ++ NULL, chip->oob_poi); ++} ++ ++static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ int ret; ++ ++ brcmnand_set_ecc_enabled(host, 0); ++ ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, ++ (u8 *)chip->oob_poi); ++ brcmnand_set_ecc_enabled(host, 1); ++ ++ return ret; ++} ++ ++/*********************************************************************** ++ * Per-CS setup (1 NAND device) ++ ***********************************************************************/ ++ ++static int brcmnand_set_cfg(struct brcmnand_host *host, ++ struct brcmnand_cfg *cfg) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ struct nand_chip *chip = &host->chip; ++ u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); ++ u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, ++ BRCMNAND_CS_CFG_EXT); ++ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, ++ BRCMNAND_CS_ACC_CONTROL); ++ u8 block_size = 0, page_size = 0, device_size = 0; ++ u32 tmp; ++ ++ if (ctrl->block_sizes) { ++ int i, found; ++ ++ for (i = 0, found = 0; ctrl->block_sizes[i]; i++) ++ if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { ++ block_size = i; ++ found = 1; ++ } ++ if (!found) { ++ dev_warn(ctrl->dev, "invalid block size %u\n", ++ cfg->block_size); ++ return -EINVAL; ++ } ++ } else { ++ block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); ++ } ++ ++ if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && ++ cfg->block_size > ctrl->max_block_size)) { ++ dev_warn(ctrl->dev, "invalid block size %u\n", ++ cfg->block_size); ++ block_size = 0; ++ } ++ ++ if (ctrl->page_sizes) { ++ int i, found; ++ ++ for (i = 0, found = 0; ctrl->page_sizes[i]; i++) ++ if (ctrl->page_sizes[i] == cfg->page_size) { ++ page_size = i; ++ found = 1; ++ } ++ if (!found) { ++ dev_warn(ctrl->dev, "invalid page size %u\n", ++ cfg->page_size); ++ return -EINVAL; ++ } ++ } else { ++ page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); ++ } ++ ++ if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && ++ cfg->page_size > ctrl->max_page_size)) { ++ dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); ++ return -EINVAL; ++ } ++ ++ if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { ++ dev_warn(ctrl->dev, "invalid device size 0x%llx\n", ++ (unsigned long long)cfg->device_size); ++ return -EINVAL; ++ } ++ device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); ++ ++ tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) | ++ (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) | ++ (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) | ++ (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) | ++ (device_size << CFG_DEVICE_SIZE_SHIFT); ++ if (cfg_offs == cfg_ext_offs) { ++ tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) | ++ (block_size << CFG_BLK_SIZE_SHIFT); ++ nand_writereg(ctrl, cfg_offs, tmp); ++ } else { ++ nand_writereg(ctrl, cfg_offs, tmp); ++ tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) | ++ (block_size << CFG_EXT_BLK_SIZE_SHIFT); ++ nand_writereg(ctrl, cfg_ext_offs, tmp); ++ } ++ ++ tmp = nand_readreg(ctrl, acc_control_offs); ++ tmp &= ~brcmnand_ecc_level_mask(ctrl); ++ tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; ++ tmp &= ~brcmnand_spare_area_mask(ctrl); ++ tmp |= cfg->spare_area_size; ++ nand_writereg(ctrl, acc_control_offs, tmp); ++ ++ brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); ++ ++ /* threshold = ceil(BCH-level * 0.75) */ ++ brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); ++ ++ return 0; ++} ++ ++static void brcmnand_print_cfg(struct brcmnand_host *host, ++ char *buf, struct brcmnand_cfg *cfg) ++{ ++ buf += sprintf(buf, ++ "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", ++ (unsigned long long)cfg->device_size >> 20, ++ cfg->block_size >> 10, ++ cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, ++ cfg->page_size >= 1024 ? "KiB" : "B", ++ cfg->spare_area_size, cfg->device_width); ++ ++ /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ ++ if (is_hamming_ecc(host->ctrl, cfg)) ++ sprintf(buf, ", Hamming ECC"); ++ else if (cfg->sector_size_1k) ++ sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); ++ else ++ sprintf(buf, ", BCH-%u", cfg->ecc_level); ++} ++ ++/* ++ * Minimum number of bytes to address a page. Calculated as: ++ * roundup(log2(size / page-size) / 8) ++ * ++ * NB: the following does not "round up" for non-power-of-2 'size'; but this is ++ * OK because many other things will break if 'size' is irregular... ++ */ ++static inline int get_blk_adr_bytes(u64 size, u32 writesize) ++{ ++ return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; ++} ++ ++static int brcmnand_setup_dev(struct brcmnand_host *host) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&host->chip); ++ struct nand_chip *chip = &host->chip; ++ struct brcmnand_controller *ctrl = host->ctrl; ++ struct brcmnand_cfg *cfg = &host->hwcfg; ++ char msg[128]; ++ u32 offs, tmp, oob_sector; ++ int ret; ++ ++ memset(cfg, 0, sizeof(*cfg)); ++ ++ ret = of_property_read_u32(nand_get_flash_node(chip), ++ "brcm,nand-oob-sector-size", ++ &oob_sector); ++ if (ret) { ++ /* Use detected size */ ++ cfg->spare_area_size = mtd->oobsize / ++ (mtd->writesize >> FC_SHIFT); ++ } else { ++ cfg->spare_area_size = oob_sector; ++ } ++ if (cfg->spare_area_size > ctrl->max_oob) ++ cfg->spare_area_size = ctrl->max_oob; ++ /* ++ * Set oobsize to be consistent with controller's spare_area_size, as ++ * the rest is inaccessible. ++ */ ++ mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); ++ ++ cfg->device_size = mtd->size; ++ cfg->block_size = mtd->erasesize; ++ cfg->page_size = mtd->writesize; ++ cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; ++ cfg->col_adr_bytes = 2; ++ cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); ++ ++ if (chip->ecc.mode != NAND_ECC_HW) { ++ dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n", ++ chip->ecc.mode); ++ return -EINVAL; ++ } ++ ++ if (chip->ecc.algo == NAND_ECC_UNKNOWN) { ++ if (chip->ecc.strength == 1 && chip->ecc.size == 512) ++ /* Default to Hamming for 1-bit ECC, if unspecified */ ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ else ++ /* Otherwise, BCH */ ++ chip->ecc.algo = NAND_ECC_BCH; ++ } ++ ++ if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 || ++ chip->ecc.size != 512)) { ++ dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n", ++ chip->ecc.strength, chip->ecc.size); ++ return -EINVAL; ++ } ++ ++ switch (chip->ecc.size) { ++ case 512: ++ if (chip->ecc.algo == NAND_ECC_HAMMING) ++ cfg->ecc_level = 15; ++ else ++ cfg->ecc_level = chip->ecc.strength; ++ cfg->sector_size_1k = 0; ++ break; ++ case 1024: ++ if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { ++ dev_err(ctrl->dev, "1KB sectors not supported\n"); ++ return -EINVAL; ++ } ++ if (chip->ecc.strength & 0x1) { ++ dev_err(ctrl->dev, ++ "odd ECC not supported with 1KB sectors\n"); ++ return -EINVAL; ++ } ++ ++ cfg->ecc_level = chip->ecc.strength >> 1; ++ cfg->sector_size_1k = 1; ++ break; ++ default: ++ dev_err(ctrl->dev, "unsupported ECC size: %d\n", ++ chip->ecc.size); ++ return -EINVAL; ++ } ++ ++ cfg->ful_adr_bytes = cfg->blk_adr_bytes; ++ if (mtd->writesize > 512) ++ cfg->ful_adr_bytes += cfg->col_adr_bytes; ++ else ++ cfg->ful_adr_bytes += 1; ++ ++ ret = brcmnand_set_cfg(host, cfg); ++ if (ret) ++ return ret; ++ ++ brcmnand_set_ecc_enabled(host, 1); ++ ++ brcmnand_print_cfg(host, msg, cfg); ++ dev_info(ctrl->dev, "detected %s\n", msg); ++ ++ /* Configure ACC_CONTROL */ ++ offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); ++ tmp = nand_readreg(ctrl, offs); ++ tmp &= ~ACC_CONTROL_PARTIAL_PAGE; ++ tmp &= ~ACC_CONTROL_RD_ERASED; ++ ++ /* We need to turn on Read from erased paged protected by ECC */ ++ if (ctrl->nand_version >= 0x0702) ++ tmp |= ACC_CONTROL_RD_ERASED; ++ tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; ++ if (ctrl->features & BRCMNAND_HAS_PREFETCH) ++ tmp &= ~ACC_CONTROL_PREFETCH; ++ ++ nand_writereg(ctrl, offs, tmp); ++ ++ return 0; ++} ++ ++static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ struct platform_device *pdev = host->pdev; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ int ret; ++ u16 cfg_offs; ++ ++ ret = of_property_read_u32(dn, "reg", &host->cs); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get chip-select\n"); ++ return -ENXIO; ++ } ++ ++ mtd = nand_to_mtd(&host->chip); ++ chip = &host->chip; ++ ++ nand_set_flash_node(chip, dn); ++ nand_set_controller_data(chip, host); ++ mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", ++ host->cs); ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = &pdev->dev; ++ ++ chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; ++ chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; ++ ++ chip->cmd_ctrl = brcmnand_cmd_ctrl; ++ chip->cmdfunc = brcmnand_cmdfunc; ++ chip->waitfunc = brcmnand_waitfunc; ++ chip->read_byte = brcmnand_read_byte; ++ chip->read_buf = brcmnand_read_buf; ++ chip->write_buf = brcmnand_write_buf; ++ ++ chip->ecc.mode = NAND_ECC_HW; ++ chip->ecc.read_page = brcmnand_read_page; ++ chip->ecc.write_page = brcmnand_write_page; ++ chip->ecc.read_page_raw = brcmnand_read_page_raw; ++ chip->ecc.write_page_raw = brcmnand_write_page_raw; ++ chip->ecc.write_oob_raw = brcmnand_write_oob_raw; ++ chip->ecc.read_oob_raw = brcmnand_read_oob_raw; ++ chip->ecc.read_oob = brcmnand_read_oob; ++ chip->ecc.write_oob = brcmnand_write_oob; ++ ++ chip->controller = &ctrl->controller; ++ ++ /* ++ * The bootloader might have configured 16bit mode but ++ * NAND READID command only works in 8bit mode. We force ++ * 8bit mode here to ensure that NAND READID commands works. ++ */ ++ cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); ++ nand_writereg(ctrl, cfg_offs, ++ nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; ++ ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ /* ++ * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA ++ * to/from, and have nand_base pass us a bounce buffer instead, as ++ * needed. ++ */ ++ chip->options |= NAND_USE_BOUNCE_BUFFER; ++ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) ++ chip->bbt_options |= NAND_BBT_NO_OOB; ++ ++ if (brcmnand_setup_dev(host)) ++ return -ENXIO; ++ ++ chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; ++ /* only use our internal HW threshold */ ++ mtd->bitflip_threshold = 1; ++ ++ ret = brcmstb_choose_ecc_layout(host); ++ if (ret) ++ return ret; ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ return ret; ++ ++ return mtd_device_register(mtd, NULL, 0); ++} ++ ++static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, ++ int restore) ++{ ++ struct brcmnand_controller *ctrl = host->ctrl; ++ u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); ++ u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, ++ BRCMNAND_CS_CFG_EXT); ++ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, ++ BRCMNAND_CS_ACC_CONTROL); ++ u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); ++ u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); ++ ++ if (restore) { ++ nand_writereg(ctrl, cfg_offs, host->hwcfg.config); ++ if (cfg_offs != cfg_ext_offs) ++ nand_writereg(ctrl, cfg_ext_offs, ++ host->hwcfg.config_ext); ++ nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); ++ nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); ++ nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); ++ } else { ++ host->hwcfg.config = nand_readreg(ctrl, cfg_offs); ++ if (cfg_offs != cfg_ext_offs) ++ host->hwcfg.config_ext = ++ nand_readreg(ctrl, cfg_ext_offs); ++ host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); ++ host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); ++ host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); ++ } ++} ++ ++static int brcmnand_suspend(struct device *dev) ++{ ++ struct brcmnand_controller *ctrl = dev_get_drvdata(dev); ++ struct brcmnand_host *host; ++ ++ list_for_each_entry(host, &ctrl->host_list, node) ++ brcmnand_save_restore_cs_config(host, 0); ++ ++ ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); ++ ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); ++ ctrl->corr_stat_threshold = ++ brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); ++ ++ if (has_flash_dma(ctrl)) ++ ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); ++ ++ return 0; ++} ++ ++static int brcmnand_resume(struct device *dev) ++{ ++ struct brcmnand_controller *ctrl = dev_get_drvdata(dev); ++ struct brcmnand_host *host; ++ ++ if (has_flash_dma(ctrl)) { ++ flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); ++ flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); ++ } ++ ++ brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); ++ brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); ++ brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, ++ ctrl->corr_stat_threshold); ++ if (ctrl->soc) { ++ /* Clear/re-enable interrupt */ ++ ctrl->soc->ctlrdy_ack(ctrl->soc); ++ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); ++ } ++ ++ list_for_each_entry(host, &ctrl->host_list, node) { ++ struct nand_chip *chip = &host->chip; ++ ++ brcmnand_save_restore_cs_config(host, 1); ++ ++ /* Reset the chip, required by some chips after power-up */ ++ nand_reset_op(chip); ++ } ++ ++ return 0; ++} ++ ++const struct dev_pm_ops brcmnand_pm_ops = { ++ .suspend = brcmnand_suspend, ++ .resume = brcmnand_resume, ++}; ++EXPORT_SYMBOL_GPL(brcmnand_pm_ops); ++ ++static const struct of_device_id brcmnand_of_match[] = { ++ { .compatible = "brcm,brcmnand-v4.0" }, ++ { .compatible = "brcm,brcmnand-v5.0" }, ++ { .compatible = "brcm,brcmnand-v6.0" }, ++ { .compatible = "brcm,brcmnand-v6.1" }, ++ { .compatible = "brcm,brcmnand-v6.2" }, ++ { .compatible = "brcm,brcmnand-v7.0" }, ++ { .compatible = "brcm,brcmnand-v7.1" }, ++ { .compatible = "brcm,brcmnand-v7.2" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, brcmnand_of_match); ++ ++/*********************************************************************** ++ * Platform driver setup (per controller) ++ ***********************************************************************/ ++ ++int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *dn = dev->of_node, *child; ++ struct brcmnand_controller *ctrl; ++ struct resource *res; ++ int ret; ++ ++ /* We only support device-tree instantiation */ ++ if (!dn) ++ return -ENODEV; ++ ++ if (!of_match_node(brcmnand_of_match, dn)) ++ return -ENODEV; ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, ctrl); ++ ctrl->dev = dev; ++ ++ init_completion(&ctrl->done); ++ init_completion(&ctrl->dma_done); ++ nand_controller_init(&ctrl->controller); ++ INIT_LIST_HEAD(&ctrl->host_list); ++ ++ /* NAND register range */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ctrl->nand_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ctrl->nand_base)) ++ return PTR_ERR(ctrl->nand_base); ++ ++ /* Enable clock before using NAND registers */ ++ ctrl->clk = devm_clk_get(dev, "nand"); ++ if (!IS_ERR(ctrl->clk)) { ++ ret = clk_prepare_enable(ctrl->clk); ++ if (ret) ++ return ret; ++ } else { ++ ret = PTR_ERR(ctrl->clk); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ ++ ctrl->clk = NULL; ++ } ++ ++ /* Initialize NAND revision */ ++ ret = brcmnand_revision_init(ctrl); ++ if (ret) ++ goto err; ++ ++ /* ++ * Most chips have this cache at a fixed offset within 'nand' block. ++ * Some must specify this region separately. ++ */ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); ++ if (res) { ++ ctrl->nand_fc = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ctrl->nand_fc)) { ++ ret = PTR_ERR(ctrl->nand_fc); ++ goto err; ++ } ++ } else { ++ ctrl->nand_fc = ctrl->nand_base + ++ ctrl->reg_offsets[BRCMNAND_FC_BASE]; ++ } ++ ++ /* FLASH_DMA */ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); ++ if (res) { ++ ctrl->flash_dma_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ctrl->flash_dma_base)) { ++ ret = PTR_ERR(ctrl->flash_dma_base); ++ goto err; ++ } ++ ++ flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ ++ flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); ++ ++ /* Allocate descriptor(s) */ ++ ctrl->dma_desc = dmam_alloc_coherent(dev, ++ sizeof(*ctrl->dma_desc), ++ &ctrl->dma_pa, GFP_KERNEL); ++ if (!ctrl->dma_desc) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ctrl->dma_irq = platform_get_irq(pdev, 1); ++ if ((int)ctrl->dma_irq < 0) { ++ dev_err(dev, "missing FLASH_DMA IRQ\n"); ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ ret = devm_request_irq(dev, ctrl->dma_irq, ++ brcmnand_dma_irq, 0, DRV_NAME, ++ ctrl); ++ if (ret < 0) { ++ dev_err(dev, "can't allocate IRQ %d: error %d\n", ++ ctrl->dma_irq, ret); ++ goto err; ++ } ++ ++ dev_info(dev, "enabling FLASH_DMA\n"); ++ } ++ ++ /* Disable automatic device ID config, direct addressing */ ++ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, ++ CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); ++ /* Disable XOR addressing */ ++ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); ++ ++ if (ctrl->features & BRCMNAND_HAS_WP) { ++ /* Permanently disable write protection */ ++ if (wp_on == 2) ++ brcmnand_set_wp(ctrl, false); ++ } else { ++ wp_on = 0; ++ } ++ ++ /* IRQ */ ++ ctrl->irq = platform_get_irq(pdev, 0); ++ if ((int)ctrl->irq < 0) { ++ dev_err(dev, "no IRQ defined\n"); ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ /* ++ * Some SoCs integrate this controller (e.g., its interrupt bits) in ++ * interesting ways ++ */ ++ if (soc) { ++ ctrl->soc = soc; ++ ++ ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, ++ DRV_NAME, ctrl); ++ ++ /* Enable interrupt */ ++ ctrl->soc->ctlrdy_ack(ctrl->soc); ++ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); ++ } else { ++ /* Use standard interrupt infrastructure */ ++ ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, ++ DRV_NAME, ctrl); ++ } ++ if (ret < 0) { ++ dev_err(dev, "can't allocate IRQ %d: error %d\n", ++ ctrl->irq, ret); ++ goto err; ++ } ++ ++ for_each_available_child_of_node(dn, child) { ++ if (of_device_is_compatible(child, "brcm,nandcs")) { ++ struct brcmnand_host *host; ++ ++ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); ++ if (!host) { ++ of_node_put(child); ++ ret = -ENOMEM; ++ goto err; ++ } ++ host->pdev = pdev; ++ host->ctrl = ctrl; ++ ++ ret = brcmnand_init_cs(host, child); ++ if (ret) { ++ devm_kfree(dev, host); ++ continue; /* Try all chip-selects */ ++ } ++ ++ list_add_tail(&host->node, &ctrl->host_list); ++ } ++ } ++ ++ /* No chip-selects could initialize properly */ ++ if (list_empty(&ctrl->host_list)) { ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(ctrl->clk); ++ return ret; ++ ++} ++EXPORT_SYMBOL_GPL(brcmnand_probe); ++ ++int brcmnand_remove(struct platform_device *pdev) ++{ ++ struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); ++ struct brcmnand_host *host; ++ ++ list_for_each_entry(host, &ctrl->host_list, node) ++ nand_release(nand_to_mtd(&host->chip)); ++ ++ clk_disable_unprepare(ctrl->clk); ++ ++ dev_set_drvdata(&pdev->dev, NULL); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(brcmnand_remove); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Kevin Cernekee"); ++MODULE_AUTHOR("Brian Norris"); ++MODULE_DESCRIPTION("NAND driver for Broadcom chips"); ++MODULE_ALIAS("platform:brcmnand"); +diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h +new file mode 100644 +index 00000000..5c44cd4 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h +@@ -0,0 +1,74 @@ ++/* ++ * Copyright © 2015 Broadcom Corporation ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __BRCMNAND_H__ ++#define __BRCMNAND_H__ ++ ++#include ++#include ++ ++struct platform_device; ++struct dev_pm_ops; ++ ++struct brcmnand_soc { ++ bool (*ctlrdy_ack)(struct brcmnand_soc *soc); ++ void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); ++ void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, ++ bool is_param); ++}; ++ ++static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc, ++ bool is_param) ++{ ++ if (soc && soc->prepare_data_bus) ++ soc->prepare_data_bus(soc, true, is_param); ++} ++ ++static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc, ++ bool is_param) ++{ ++ if (soc && soc->prepare_data_bus) ++ soc->prepare_data_bus(soc, false, is_param); ++} ++ ++static inline u32 brcmnand_readl(void __iomem *addr) ++{ ++ /* ++ * MIPS endianness is configured by boot strap, which also reverses all ++ * bus endianness (i.e., big-endian CPU + big endian bus ==> native ++ * endian I/O). ++ * ++ * Other architectures (e.g., ARM) either do not support big endian, or ++ * else leave I/O in little endian mode. ++ */ ++ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) ++ return __raw_readl(addr); ++ else ++ return readl_relaxed(addr); ++} ++ ++static inline void brcmnand_writel(u32 val, void __iomem *addr) ++{ ++ /* See brcmnand_readl() comments */ ++ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) ++ __raw_writel(val, addr); ++ else ++ writel_relaxed(val, addr); ++} ++ ++int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); ++int brcmnand_remove(struct platform_device *pdev); ++ ++extern const struct dev_pm_ops brcmnand_pm_ops; ++ ++#endif /* __BRCMNAND_H__ */ +diff --git a/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c +new file mode 100644 +index 00000000..5c271077 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c +@@ -0,0 +1,44 @@ ++/* ++ * Copyright © 2015 Broadcom Corporation ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++ ++#include "brcmnand.h" ++ ++static const struct of_device_id brcmstb_nand_of_match[] = { ++ { .compatible = "brcm,brcmnand" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match); ++ ++static int brcmstb_nand_probe(struct platform_device *pdev) ++{ ++ return brcmnand_probe(pdev, NULL); ++} ++ ++static struct platform_driver brcmstb_nand_driver = { ++ .probe = brcmstb_nand_probe, ++ .remove = brcmnand_remove, ++ .driver = { ++ .name = "brcmstb_nand", ++ .pm = &brcmnand_pm_ops, ++ .of_match_table = brcmstb_nand_of_match, ++ } ++}; ++module_platform_driver(brcmstb_nand_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Brian Norris"); ++MODULE_DESCRIPTION("NAND driver for Broadcom STB chips"); +diff --git a/drivers/mtd/nand/raw/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c +new file mode 100644 +index 00000000..4c6ae11 +--- /dev/null ++++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c +@@ -0,0 +1,160 @@ ++/* ++ * Copyright © 2015 Broadcom Corporation ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "brcmnand.h" ++ ++struct iproc_nand_soc { ++ struct brcmnand_soc soc; ++ ++ void __iomem *idm_base; ++ void __iomem *ext_base; ++ spinlock_t idm_lock; ++}; ++ ++#define IPROC_NAND_CTLR_READY_OFFSET 0x10 ++#define IPROC_NAND_CTLR_READY BIT(0) ++ ++#define IPROC_NAND_IO_CTRL_OFFSET 0x00 ++#define IPROC_NAND_APB_LE_MODE BIT(24) ++#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6) ++ ++static bool iproc_nand_intc_ack(struct brcmnand_soc *soc) ++{ ++ struct iproc_nand_soc *priv = ++ container_of(soc, struct iproc_nand_soc, soc); ++ void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET; ++ u32 val = brcmnand_readl(mmio); ++ ++ if (val & IPROC_NAND_CTLR_READY) { ++ brcmnand_writel(IPROC_NAND_CTLR_READY, mmio); ++ return true; ++ } ++ ++ return false; ++} ++ ++static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) ++{ ++ struct iproc_nand_soc *priv = ++ container_of(soc, struct iproc_nand_soc, soc); ++ void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; ++ u32 val; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->idm_lock, flags); ++ ++ val = brcmnand_readl(mmio); ++ ++ if (en) ++ val |= IPROC_NAND_INT_CTRL_READ_ENABLE; ++ else ++ val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE; ++ ++ brcmnand_writel(val, mmio); ++ ++ spin_unlock_irqrestore(&priv->idm_lock, flags); ++} ++ ++static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare, ++ bool is_param) ++{ ++ struct iproc_nand_soc *priv = ++ container_of(soc, struct iproc_nand_soc, soc); ++ void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; ++ u32 val; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->idm_lock, flags); ++ ++ val = brcmnand_readl(mmio); ++ ++ /* ++ * In the case of BE or when dealing with NAND data, alway configure ++ * the APB bus to LE mode before accessing the FIFO and back to BE mode ++ * after the access is done ++ */ ++ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) { ++ if (prepare) ++ val |= IPROC_NAND_APB_LE_MODE; ++ else ++ val &= ~IPROC_NAND_APB_LE_MODE; ++ } else { /* when in LE accessing the parameter page, keep APB in BE */ ++ val &= ~IPROC_NAND_APB_LE_MODE; ++ } ++ ++ brcmnand_writel(val, mmio); ++ ++ spin_unlock_irqrestore(&priv->idm_lock, flags); ++} ++ ++static int iproc_nand_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct iproc_nand_soc *priv; ++ struct brcmnand_soc *soc; ++ struct resource *res; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ soc = &priv->soc; ++ ++ spin_lock_init(&priv->idm_lock); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm"); ++ priv->idm_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->idm_base)) ++ return PTR_ERR(priv->idm_base); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext"); ++ priv->ext_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->ext_base)) ++ return PTR_ERR(priv->ext_base); ++ ++ soc->ctlrdy_ack = iproc_nand_intc_ack; ++ soc->ctlrdy_set_enabled = iproc_nand_intc_set; ++ soc->prepare_data_bus = iproc_nand_apb_access; ++ ++ return brcmnand_probe(pdev, soc); ++} ++ ++static const struct of_device_id iproc_nand_of_match[] = { ++ { .compatible = "brcm,nand-iproc" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, iproc_nand_of_match); ++ ++static struct platform_driver iproc_nand_driver = { ++ .probe = iproc_nand_probe, ++ .remove = brcmnand_remove, ++ .driver = { ++ .name = "iproc_nand", ++ .pm = &brcmnand_pm_ops, ++ .of_match_table = iproc_nand_of_match, ++ } ++}; ++module_platform_driver(iproc_nand_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Brian Norris"); ++MODULE_AUTHOR("Ray Jui"); ++MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs"); +diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c +new file mode 100644 +index 00000000..de36762 +--- /dev/null ++++ b/drivers/mtd/nand/raw/cafe_nand.c +@@ -0,0 +1,891 @@ ++/* ++ * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 ++ * ++ * The data sheet for this device can be found at: ++ * http://wiki.laptop.org/go/Datasheets ++ * ++ * Copyright © 2006 Red Hat, Inc. ++ * Copyright © 2006 David Woodhouse ++ */ ++ ++#define DEBUG ++ ++#include ++#undef DEBUG ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CAFE_NAND_CTRL1 0x00 ++#define CAFE_NAND_CTRL2 0x04 ++#define CAFE_NAND_CTRL3 0x08 ++#define CAFE_NAND_STATUS 0x0c ++#define CAFE_NAND_IRQ 0x10 ++#define CAFE_NAND_IRQ_MASK 0x14 ++#define CAFE_NAND_DATA_LEN 0x18 ++#define CAFE_NAND_ADDR1 0x1c ++#define CAFE_NAND_ADDR2 0x20 ++#define CAFE_NAND_TIMING1 0x24 ++#define CAFE_NAND_TIMING2 0x28 ++#define CAFE_NAND_TIMING3 0x2c ++#define CAFE_NAND_NONMEM 0x30 ++#define CAFE_NAND_ECC_RESULT 0x3C ++#define CAFE_NAND_DMA_CTRL 0x40 ++#define CAFE_NAND_DMA_ADDR0 0x44 ++#define CAFE_NAND_DMA_ADDR1 0x48 ++#define CAFE_NAND_ECC_SYN01 0x50 ++#define CAFE_NAND_ECC_SYN23 0x54 ++#define CAFE_NAND_ECC_SYN45 0x58 ++#define CAFE_NAND_ECC_SYN67 0x5c ++#define CAFE_NAND_READ_DATA 0x1000 ++#define CAFE_NAND_WRITE_DATA 0x2000 ++ ++#define CAFE_GLOBAL_CTRL 0x3004 ++#define CAFE_GLOBAL_IRQ 0x3008 ++#define CAFE_GLOBAL_IRQ_MASK 0x300c ++#define CAFE_NAND_RESET 0x3034 ++ ++/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */ ++#define CTRL1_CHIPSELECT (1<<19) ++ ++struct cafe_priv { ++ struct nand_chip nand; ++ struct pci_dev *pdev; ++ void __iomem *mmio; ++ struct rs_control *rs; ++ uint32_t ctl1; ++ uint32_t ctl2; ++ int datalen; ++ int nr_data; ++ int data_pos; ++ int page_addr; ++ dma_addr_t dmaaddr; ++ unsigned char *dmabuf; ++}; ++ ++static int usedma = 1; ++module_param(usedma, int, 0644); ++ ++static int skipbbt = 0; ++module_param(skipbbt, int, 0644); ++ ++static int debug = 0; ++module_param(debug, int, 0644); ++ ++static int regdebug = 0; ++module_param(regdebug, int, 0644); ++ ++static int checkecc = 1; ++module_param(checkecc, int, 0644); ++ ++static unsigned int numtimings; ++static int timing[3]; ++module_param_array(timing, int, &numtimings, 0644); ++ ++static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; ++ ++/* Hrm. Why isn't this already conditional on something in the struct device? */ ++#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) ++ ++/* Make it easier to switch to PIO if we need to */ ++#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr) ++#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr) ++ ++static int cafe_device_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000); ++ uint32_t irqs = cafe_readl(cafe, NAND_IRQ); ++ ++ cafe_writel(cafe, irqs, NAND_IRQ); ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", ++ result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ), ++ cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK)); ++ ++ return result; ++} ++ ++ ++static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ ++ if (usedma) ++ memcpy(cafe->dmabuf + cafe->datalen, buf, len); ++ else ++ memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); ++ ++ cafe->datalen += len; ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", ++ len, cafe->datalen); ++} ++ ++static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ ++ if (usedma) ++ memcpy(buf, cafe->dmabuf + cafe->datalen, len); ++ else ++ memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", ++ len, cafe->datalen); ++ cafe->datalen += len; ++} ++ ++static uint8_t cafe_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ uint8_t d; ++ ++ cafe_read_buf(mtd, &d, 1); ++ cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); ++ ++ return d; ++} ++ ++static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ int adrbytes = 0; ++ uint32_t ctl1; ++ uint32_t doneint = 0x80000000; ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", ++ command, column, page_addr); ++ ++ if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { ++ /* Second half of a command we already calculated */ ++ cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2); ++ ctl1 = cafe->ctl1; ++ cafe->ctl2 &= ~(1<<30); ++ cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", ++ cafe->ctl1, cafe->nr_data); ++ goto do_command; ++ } ++ /* Reset ECC engine */ ++ cafe_writel(cafe, 0, NAND_CTRL2); ++ ++ /* Emulate NAND_CMD_READOOB on large-page chips */ ++ if (mtd->writesize > 512 && ++ command == NAND_CMD_READOOB) { ++ column += mtd->writesize; ++ command = NAND_CMD_READ0; ++ } ++ ++ /* FIXME: Do we need to send read command before sending data ++ for small-page chips, to position the buffer correctly? */ ++ ++ if (column != -1) { ++ cafe_writel(cafe, column, NAND_ADDR1); ++ adrbytes = 2; ++ if (page_addr != -1) ++ goto write_adr2; ++ } else if (page_addr != -1) { ++ cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1); ++ page_addr >>= 16; ++ write_adr2: ++ cafe_writel(cafe, page_addr, NAND_ADDR2); ++ adrbytes += 2; ++ if (mtd->size > mtd->writesize << 16) ++ adrbytes++; ++ } ++ ++ cafe->data_pos = cafe->datalen = 0; ++ ++ /* Set command valid bit, mask in the chip select bit */ ++ ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT); ++ ++ /* Set RD or WR bits as appropriate */ ++ if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { ++ ctl1 |= (1<<26); /* rd */ ++ /* Always 5 bytes, for now */ ++ cafe->datalen = 4; ++ /* And one address cycle -- even for STATUS, since the controller doesn't work without */ ++ adrbytes = 1; ++ } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || ++ command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { ++ ctl1 |= 1<<26; /* rd */ ++ /* For now, assume just read to end of page */ ++ cafe->datalen = mtd->writesize + mtd->oobsize - column; ++ } else if (command == NAND_CMD_SEQIN) ++ ctl1 |= 1<<25; /* wr */ ++ ++ /* Set number of address bytes */ ++ if (adrbytes) ++ ctl1 |= ((adrbytes-1)|8) << 27; ++ ++ if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { ++ /* Ignore the first command of a pair; the hardware ++ deals with them both at once, later */ ++ cafe->ctl1 = ctl1; ++ cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", ++ cafe->ctl1, cafe->datalen); ++ return; ++ } ++ /* RNDOUT and READ0 commands need a following byte */ ++ if (command == NAND_CMD_RNDOUT) ++ cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2); ++ else if (command == NAND_CMD_READ0 && mtd->writesize > 512) ++ cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2); ++ ++ do_command: ++ cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", ++ cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2)); ++ ++ /* NB: The datasheet lies -- we really should be subtracting 1 here */ ++ cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); ++ cafe_writel(cafe, 0x90000000, NAND_IRQ); ++ if (usedma && (ctl1 & (3<<25))) { ++ uint32_t dmactl = 0xc0000000 + cafe->datalen; ++ /* If WR or RD bits set, set up DMA */ ++ if (ctl1 & (1<<26)) { ++ /* It's a read */ ++ dmactl |= (1<<29); ++ /* ... so it's done when the DMA is done, not just ++ the command. */ ++ doneint = 0x10000000; ++ } ++ cafe_writel(cafe, dmactl, NAND_DMA_CTRL); ++ } ++ cafe->datalen = 0; ++ ++ if (unlikely(regdebug)) { ++ int i; ++ printk("About to write command %08x to register 0\n", ctl1); ++ for (i=4; i< 0x5c; i+=4) ++ printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); ++ } ++ ++ cafe_writel(cafe, ctl1, NAND_CTRL1); ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay(100); ++ ++ if (1) { ++ int c; ++ uint32_t irqs; ++ ++ for (c = 500000; c != 0; c--) { ++ irqs = cafe_readl(cafe, NAND_IRQ); ++ if (irqs & doneint) ++ break; ++ udelay(1); ++ if (!(c % 100000)) ++ cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); ++ cpu_relax(); ++ } ++ cafe_writel(cafe, doneint, NAND_IRQ); ++ cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", ++ command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ)); ++ } ++ ++ WARN_ON(cafe->ctl2 & (1<<30)); ++ ++ switch (command) { ++ ++ case NAND_CMD_CACHEDPROG: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_RNDIN: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_RNDOUT: ++ cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); ++ return; ++ } ++ nand_wait_ready(mtd); ++ cafe_writel(cafe, cafe->ctl2, NAND_CTRL2); ++} ++ ++static void cafe_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); ++ ++ /* Mask the appropriate bit into the stored value of ctl1 ++ which will be used by cafe_nand_cmdfunc() */ ++ if (chipnr) ++ cafe->ctl1 |= CTRL1_CHIPSELECT; ++ else ++ cafe->ctl1 &= ~CTRL1_CHIPSELECT; ++} ++ ++static irqreturn_t cafe_nand_interrupt(int irq, void *id) ++{ ++ struct mtd_info *mtd = id; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ uint32_t irqs = cafe_readl(cafe, NAND_IRQ); ++ cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ); ++ if (!irqs) ++ return IRQ_NONE; ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ)); ++ return IRQ_HANDLED; ++} ++ ++static void cafe_nand_bug(struct mtd_info *mtd) ++{ ++ BUG(); ++} ++ ++static int cafe_nand_write_oob(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, ++ mtd->oobsize); ++} ++ ++/* Don't use -- use nand_read_oob_std for now */ ++static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); ++} ++/** ++ * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller expects OOB data read to chip->oob_poi ++ * ++ * The hw generator calculates the error syndrome automatically. Therefore ++ * we need a special oob layout and handling. ++ */ ++static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ unsigned int max_bitflips = 0; ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", ++ cafe_readl(cafe, NAND_ECC_RESULT), ++ cafe_readl(cafe, NAND_ECC_SYN01)); ++ ++ nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { ++ unsigned short syn[8], pat[4]; ++ int pos[4]; ++ u8 *oob = chip->oob_poi; ++ int i, n; ++ ++ for (i=0; i<8; i+=2) { ++ uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); ++ syn[i] = cafe->rs->index_of[tmp & 0xfff]; ++ syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; ++ } ++ ++ n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, ++ pat); ++ ++ for (i = 0; i < n; i++) { ++ int p = pos[i]; ++ ++ /* The 12-bit symbols are mapped to bytes here */ ++ ++ if (p > 1374) { ++ /* out of range */ ++ n = -1374; ++ } else if (p == 0) { ++ /* high four bits do not correspond to data */ ++ if (pat[i] > 0xff) ++ n = -2048; ++ else ++ buf[0] ^= pat[i]; ++ } else if (p == 1365) { ++ buf[2047] ^= pat[i] >> 4; ++ oob[0] ^= pat[i] << 4; ++ } else if (p > 1365) { ++ if ((p & 1) == 1) { ++ oob[3*p/2 - 2048] ^= pat[i] >> 4; ++ oob[3*p/2 - 2047] ^= pat[i] << 4; ++ } else { ++ oob[3*p/2 - 2049] ^= pat[i] >> 8; ++ oob[3*p/2 - 2048] ^= pat[i]; ++ } ++ } else if ((p & 1) == 1) { ++ buf[3*p/2] ^= pat[i] >> 4; ++ buf[3*p/2 + 1] ^= pat[i] << 4; ++ } else { ++ buf[3*p/2 - 1] ^= pat[i] >> 8; ++ buf[3*p/2] ^= pat[i]; ++ } ++ } ++ ++ if (n < 0) { ++ dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", ++ cafe_readl(cafe, NAND_ADDR2) * 2048); ++ for (i = 0; i < 0x5c; i += 4) ++ printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); ++ mtd->ecc_stats.failed++; ++ } else { ++ dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); ++ mtd->ecc_stats.corrected += n; ++ max_bitflips = max_t(unsigned int, max_bitflips, n); ++ } ++ } ++ ++ return max_bitflips; ++} ++ ++static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 0; ++ oobregion->length = chip->ecc.total; ++ ++ return 0; ++} ++ ++static int cafe_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = chip->ecc.total; ++ oobregion->length = mtd->oobsize - chip->ecc.total; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops cafe_ooblayout_ops = { ++ .ecc = cafe_ooblayout_ecc, ++ .free = cafe_ooblayout_free, ++}; ++ ++/* Ick. The BBT code really ought to be able to work this bit out ++ for itself from the above, at least for the 2KiB case */ ++static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' }; ++static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' }; ++ ++static uint8_t cafe_bbt_pattern_512[] = { 0xBB }; ++static uint8_t cafe_mirror_pattern_512[] = { 0xBC }; ++ ++ ++static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 14, ++ .len = 4, ++ .veroffs = 18, ++ .maxblocks = 4, ++ .pattern = cafe_bbt_pattern_2048 ++}; ++ ++static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 14, ++ .len = 4, ++ .veroffs = 18, ++ .maxblocks = 4, ++ .pattern = cafe_mirror_pattern_2048 ++}; ++ ++static struct nand_bbt_descr cafe_bbt_main_descr_512 = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 14, ++ .len = 1, ++ .veroffs = 15, ++ .maxblocks = 4, ++ .pattern = cafe_bbt_pattern_512 ++}; ++ ++static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 14, ++ .len = 1, ++ .veroffs = 15, ++ .maxblocks = 4, ++ .pattern = cafe_mirror_pattern_512 ++}; ++ ++ ++static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ /* Set up ECC autogeneration */ ++ cafe->ctl2 |= (1<<30); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs) ++{ ++ return 0; ++} ++ ++/* F_2[X]/(X**6+X+1) */ ++static unsigned short gf64_mul(u8 a, u8 b) ++{ ++ u8 c; ++ unsigned int i; ++ ++ c = 0; ++ for (i = 0; i < 6; i++) { ++ if (a & 1) ++ c ^= b; ++ a >>= 1; ++ b <<= 1; ++ if ((b & 0x40) != 0) ++ b ^= 0x43; ++ } ++ ++ return c; ++} ++ ++/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ ++static u16 gf4096_mul(u16 a, u16 b) ++{ ++ u8 ah, al, bh, bl, ch, cl; ++ ++ ah = a >> 6; ++ al = a & 0x3f; ++ bh = b >> 6; ++ bl = b & 0x3f; ++ ++ ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl); ++ cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl); ++ ++ return (ch << 6) ^ cl; ++} ++ ++static int cafe_mul(int x) ++{ ++ if (x == 0) ++ return 1; ++ return gf4096_mul(x, 0xe01); ++} ++ ++static int cafe_nand_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct mtd_info *mtd; ++ struct cafe_priv *cafe; ++ uint32_t ctrl; ++ int err = 0; ++ int old_dma; ++ struct nand_buffers *nbuf; ++ ++ /* Very old versions shared the same PCI ident for all three ++ functions on the chip. Verify the class too... */ ++ if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) ++ return -ENODEV; ++ ++ err = pci_enable_device(pdev); ++ if (err) ++ return err; ++ ++ pci_set_master(pdev); ++ ++ cafe = kzalloc(sizeof(*cafe), GFP_KERNEL); ++ if (!cafe) ++ return -ENOMEM; ++ ++ mtd = nand_to_mtd(&cafe->nand); ++ mtd->dev.parent = &pdev->dev; ++ nand_set_controller_data(&cafe->nand, cafe); ++ ++ cafe->pdev = pdev; ++ cafe->mmio = pci_iomap(pdev, 0, 0); ++ if (!cafe->mmio) { ++ dev_warn(&pdev->dev, "failed to iomap\n"); ++ err = -ENOMEM; ++ goto out_free_mtd; ++ } ++ ++ cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); ++ if (!cafe->rs) { ++ err = -ENOMEM; ++ goto out_ior; ++ } ++ ++ cafe->nand.cmdfunc = cafe_nand_cmdfunc; ++ cafe->nand.dev_ready = cafe_device_ready; ++ cafe->nand.read_byte = cafe_read_byte; ++ cafe->nand.read_buf = cafe_read_buf; ++ cafe->nand.write_buf = cafe_write_buf; ++ cafe->nand.select_chip = cafe_select_chip; ++ cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp; ++ cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ cafe->nand.chip_delay = 0; ++ ++ /* Enable the following for a flash based bad block table */ ++ cafe->nand.bbt_options = NAND_BBT_USE_FLASH; ++ cafe->nand.options = NAND_OWN_BUFFERS; ++ ++ if (skipbbt) { ++ cafe->nand.options |= NAND_SKIP_BBTSCAN; ++ cafe->nand.block_bad = cafe_nand_block_bad; ++ } ++ ++ if (numtimings && numtimings != 3) { ++ dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); ++ } ++ ++ if (numtimings == 3) { ++ cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", ++ timing[0], timing[1], timing[2]); ++ } else { ++ timing[0] = cafe_readl(cafe, NAND_TIMING1); ++ timing[1] = cafe_readl(cafe, NAND_TIMING2); ++ timing[2] = cafe_readl(cafe, NAND_TIMING3); ++ ++ if (timing[0] | timing[1] | timing[2]) { ++ cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", ++ timing[0], timing[1], timing[2]); ++ } else { ++ dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); ++ timing[0] = timing[1] = timing[2] = 0xffffffff; ++ } ++ } ++ ++ /* Start off by resetting the NAND controller completely */ ++ cafe_writel(cafe, 1, NAND_RESET); ++ cafe_writel(cafe, 0, NAND_RESET); ++ ++ cafe_writel(cafe, timing[0], NAND_TIMING1); ++ cafe_writel(cafe, timing[1], NAND_TIMING2); ++ cafe_writel(cafe, timing[2], NAND_TIMING3); ++ ++ cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); ++ err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, ++ "CAFE NAND", mtd); ++ if (err) { ++ dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); ++ goto out_ior; ++ } ++ ++ /* Disable master reset, enable NAND clock */ ++ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ++ ctrl &= 0xffffeff0; ++ ctrl |= 0x00007000; ++ cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); ++ cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); ++ cafe_writel(cafe, 0, NAND_DMA_CTRL); ++ ++ cafe_writel(cafe, 0x7006, GLOBAL_CTRL); ++ cafe_writel(cafe, 0x700a, GLOBAL_CTRL); ++ ++ /* Enable NAND IRQ in global IRQ mask register */ ++ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); ++ cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", ++ cafe_readl(cafe, GLOBAL_CTRL), ++ cafe_readl(cafe, GLOBAL_IRQ_MASK)); ++ ++ /* Do not use the DMA for the nand_scan_ident() */ ++ old_dma = usedma; ++ usedma = 0; ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan_ident(mtd, 2, NULL); ++ if (err) ++ goto out_irq; ++ ++ cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, ++ 2112 + sizeof(struct nand_buffers) + ++ mtd->writesize + mtd->oobsize, ++ &cafe->dmaaddr, GFP_KERNEL); ++ if (!cafe->dmabuf) { ++ err = -ENOMEM; ++ goto out_irq; ++ } ++ cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112; ++ ++ /* Set up DMA address */ ++ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); ++ if (sizeof(cafe->dmaaddr) > 4) ++ /* Shift in two parts to shut the compiler up */ ++ cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); ++ else ++ cafe_writel(cafe, 0, NAND_DMA_ADDR1); ++ ++ cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", ++ cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); ++ ++ /* this driver does not need the @ecccalc and @ecccode */ ++ nbuf->ecccalc = NULL; ++ nbuf->ecccode = NULL; ++ nbuf->databuf = (uint8_t *)(nbuf + 1); ++ ++ /* Restore the DMA flag */ ++ usedma = old_dma; ++ ++ cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ ++ if (mtd->writesize == 2048) ++ cafe->ctl2 |= 1<<29; /* 2KiB page size */ ++ ++ /* Set up ECC according to the type of chip we found */ ++ mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); ++ if (mtd->writesize == 2048) { ++ cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; ++ cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; ++ } else if (mtd->writesize == 512) { ++ cafe->nand.bbt_td = &cafe_bbt_main_descr_512; ++ cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; ++ } else { ++ printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", ++ mtd->writesize); ++ goto out_free_dma; ++ } ++ cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; ++ cafe->nand.ecc.size = mtd->writesize; ++ cafe->nand.ecc.bytes = 14; ++ cafe->nand.ecc.strength = 4; ++ cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; ++ cafe->nand.ecc.calculate = (void *)cafe_nand_bug; ++ cafe->nand.ecc.correct = (void *)cafe_nand_bug; ++ cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; ++ cafe->nand.ecc.write_oob = cafe_nand_write_oob; ++ cafe->nand.ecc.read_page = cafe_nand_read_page; ++ cafe->nand.ecc.read_oob = cafe_nand_read_oob; ++ ++ err = nand_scan_tail(mtd); ++ if (err) ++ goto out_free_dma; ++ ++ pci_set_drvdata(pdev, mtd); ++ ++ mtd->name = "cafe_nand"; ++ mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); ++ ++ goto out; ++ ++ out_free_dma: ++ dma_free_coherent(&cafe->pdev->dev, ++ 2112 + sizeof(struct nand_buffers) + ++ mtd->writesize + mtd->oobsize, ++ cafe->dmabuf, cafe->dmaaddr); ++ out_irq: ++ /* Disable NAND IRQ in global IRQ mask register */ ++ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); ++ free_irq(pdev->irq, mtd); ++ out_ior: ++ pci_iounmap(pdev, cafe->mmio); ++ out_free_mtd: ++ kfree(cafe); ++ out: ++ return err; ++} ++ ++static void cafe_nand_remove(struct pci_dev *pdev) ++{ ++ struct mtd_info *mtd = pci_get_drvdata(pdev); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ ++ /* Disable NAND IRQ in global IRQ mask register */ ++ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); ++ free_irq(pdev->irq, mtd); ++ nand_release(mtd); ++ free_rs(cafe->rs); ++ pci_iounmap(pdev, cafe->mmio); ++ dma_free_coherent(&cafe->pdev->dev, ++ 2112 + sizeof(struct nand_buffers) + ++ mtd->writesize + mtd->oobsize, ++ cafe->dmabuf, cafe->dmaaddr); ++ kfree(cafe); ++} ++ ++static const struct pci_device_id cafe_nand_tbl[] = { ++ { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, ++ PCI_ANY_ID, PCI_ANY_ID }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); ++ ++static int cafe_nand_resume(struct pci_dev *pdev) ++{ ++ uint32_t ctrl; ++ struct mtd_info *mtd = pci_get_drvdata(pdev); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct cafe_priv *cafe = nand_get_controller_data(chip); ++ ++ /* Start off by resetting the NAND controller completely */ ++ cafe_writel(cafe, 1, NAND_RESET); ++ cafe_writel(cafe, 0, NAND_RESET); ++ cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); ++ ++ /* Restore timing configuration */ ++ cafe_writel(cafe, timing[0], NAND_TIMING1); ++ cafe_writel(cafe, timing[1], NAND_TIMING2); ++ cafe_writel(cafe, timing[2], NAND_TIMING3); ++ ++ /* Disable master reset, enable NAND clock */ ++ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ++ ctrl &= 0xffffeff0; ++ ctrl |= 0x00007000; ++ cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); ++ cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); ++ cafe_writel(cafe, 0, NAND_DMA_CTRL); ++ cafe_writel(cafe, 0x7006, GLOBAL_CTRL); ++ cafe_writel(cafe, 0x700a, GLOBAL_CTRL); ++ ++ /* Set up DMA address */ ++ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); ++ if (sizeof(cafe->dmaaddr) > 4) ++ /* Shift in two parts to shut the compiler up */ ++ cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); ++ else ++ cafe_writel(cafe, 0, NAND_DMA_ADDR1); ++ ++ /* Enable NAND IRQ in global IRQ mask register */ ++ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); ++ return 0; ++} ++ ++static struct pci_driver cafe_nand_pci_driver = { ++ .name = "CAFÉ NAND", ++ .id_table = cafe_nand_tbl, ++ .probe = cafe_nand_probe, ++ .remove = cafe_nand_remove, ++ .resume = cafe_nand_resume, ++}; ++ ++module_pci_driver(cafe_nand_pci_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); +diff --git a/drivers/mtd/nand/raw/cmx270_nand.c b/drivers/mtd/nand/raw/cmx270_nand.c +new file mode 100644 +index 00000000..1fc435f +--- /dev/null ++++ b/drivers/mtd/nand/raw/cmx270_nand.c +@@ -0,0 +1,246 @@ ++/* ++ * linux/drivers/mtd/nand/cmx270-nand.c ++ * ++ * Copyright (C) 2006 Compulab, Ltd. ++ * Mike Rapoport ++ * ++ * Derived from drivers/mtd/nand/h1910.c ++ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) ++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * ++ * 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. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * CM-X270 board. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#define GPIO_NAND_CS (11) ++#define GPIO_NAND_RB (89) ++ ++/* MTD structure for CM-X270 board */ ++static struct mtd_info *cmx270_nand_mtd; ++ ++/* remaped IO address of the device */ ++static void __iomem *cmx270_nand_io; ++ ++/* ++ * Define static partitions for flash device ++ */ ++static struct mtd_partition partition_info[] = { ++ [0] = { ++ .name = "cmx270-0", ++ .offset = 0, ++ .size = MTDPART_SIZ_FULL ++ } ++}; ++#define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) ++ ++static u_char cmx270_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ return (readl(this->IO_ADDR_R) >> 16); ++} ++ ++static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ for (i=0; iIO_ADDR_R) >> 16; ++} ++ ++static inline void nand_cs_on(void) ++{ ++ gpio_set_value(GPIO_NAND_CS, 0); ++} ++ ++static void nand_cs_off(void) ++{ ++ dsb(); ++ ++ gpio_set_value(GPIO_NAND_CS, 1); ++} ++ ++/* ++ * hardware specific access to control-lines ++ */ ++static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, ++ unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; ++ ++ dsb(); ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if ( ctrl & NAND_ALE ) ++ nandaddr |= (1 << 3); ++ else ++ nandaddr &= ~(1 << 3); ++ if ( ctrl & NAND_CLE ) ++ nandaddr |= (1 << 2); ++ else ++ nandaddr &= ~(1 << 2); ++ if ( ctrl & NAND_NCE ) ++ nand_cs_on(); ++ else ++ nand_cs_off(); ++ } ++ ++ dsb(); ++ this->IO_ADDR_W = (void __iomem*)nandaddr; ++ if (dat != NAND_CMD_NONE) ++ writel((dat << 16), this->IO_ADDR_W); ++ ++ dsb(); ++} ++ ++/* ++ * read device ready pin ++ */ ++static int cmx270_device_ready(struct mtd_info *mtd) ++{ ++ dsb(); ++ ++ return (gpio_get_value(GPIO_NAND_RB)); ++} ++ ++/* ++ * Main initialization routine ++ */ ++static int __init cmx270_init(void) ++{ ++ struct nand_chip *this; ++ int ret; ++ ++ if (!(machine_is_armcore() && cpu_is_pxa27x())) ++ return -ENODEV; ++ ++ ret = gpio_request(GPIO_NAND_CS, "NAND CS"); ++ if (ret) { ++ pr_warn("CM-X270: failed to request NAND CS gpio\n"); ++ return ret; ++ } ++ ++ gpio_direction_output(GPIO_NAND_CS, 1); ++ ++ ret = gpio_request(GPIO_NAND_RB, "NAND R/B"); ++ if (ret) { ++ pr_warn("CM-X270: failed to request NAND R/B gpio\n"); ++ goto err_gpio_request; ++ } ++ ++ gpio_direction_input(GPIO_NAND_RB); ++ ++ /* Allocate memory for MTD device structure and private data */ ++ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ++ if (!this) { ++ ret = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); ++ if (!cmx270_nand_io) { ++ pr_debug("Unable to ioremap NAND device\n"); ++ ret = -EINVAL; ++ goto err_ioremap; ++ } ++ ++ cmx270_nand_mtd = nand_to_mtd(this); ++ ++ /* Link the private data with the MTD structure */ ++ cmx270_nand_mtd->owner = THIS_MODULE; ++ ++ /* insert callbacks */ ++ this->IO_ADDR_R = cmx270_nand_io; ++ this->IO_ADDR_W = cmx270_nand_io; ++ this->cmd_ctrl = cmx270_hwcontrol; ++ this->dev_ready = cmx270_device_ready; ++ ++ /* 15 us command delay time */ ++ this->chip_delay = 20; ++ this->ecc.mode = NAND_ECC_SOFT; ++ this->ecc.algo = NAND_ECC_HAMMING; ++ ++ /* read/write functions */ ++ this->read_byte = cmx270_read_byte; ++ this->read_buf = cmx270_read_buf; ++ this->write_buf = cmx270_write_buf; ++ ++ /* Scan to find existence of the device */ ++ ret = nand_scan(cmx270_nand_mtd, 1); ++ if (ret) { ++ pr_notice("No NAND device\n"); ++ goto err_scan; ++ } ++ ++ /* Register the partitions */ ++ ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, ++ partition_info, NUM_PARTITIONS); ++ if (ret) ++ goto err_scan; ++ ++ /* Return happy */ ++ return 0; ++ ++err_scan: ++ iounmap(cmx270_nand_io); ++err_ioremap: ++ kfree(this); ++err_kzalloc: ++ gpio_free(GPIO_NAND_RB); ++err_gpio_request: ++ gpio_free(GPIO_NAND_CS); ++ ++ return ret; ++ ++} ++module_init(cmx270_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit cmx270_cleanup(void) ++{ ++ /* Release resources, unregister device */ ++ nand_release(cmx270_nand_mtd); ++ ++ gpio_free(GPIO_NAND_RB); ++ gpio_free(GPIO_NAND_CS); ++ ++ iounmap(cmx270_nand_io); ++ ++ kfree(mtd_to_nand(cmx270_nand_mtd)); ++} ++module_exit(cmx270_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Mike Rapoport "); ++MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module"); +diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c +new file mode 100644 +index 00000000..d488775 +--- /dev/null ++++ b/drivers/mtd/nand/raw/cs553x_nand.c +@@ -0,0 +1,357 @@ ++/* ++ * drivers/mtd/nand/cs553x_nand.c ++ * ++ * (C) 2005, 2006 Red Hat Inc. ++ * ++ * Author: David Woodhouse ++ * Tom Sylla ++ * ++ * 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. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash controller found on ++ * the AMD CS5535/CS5536 companion chipsets for the Geode processor. ++ * mtd-id for command line partitioning is cs553x_nand_cs[0-3] ++ * where 0-3 reflects the chip select for NAND. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define NR_CS553X_CONTROLLERS 4 ++ ++#define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */ ++#define CAP_CS5535 0x2df000ULL ++#define CAP_CS5536 0x5df500ULL ++ ++/* NAND Timing MSRs */ ++#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */ ++#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */ ++#define MSR_NANDF_RSVD 0x5140001d /* Reserved */ ++ ++/* NAND BAR MSRs */ ++#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */ ++#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */ ++#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */ ++#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */ ++ /* Each made up of... */ ++#define FLSH_LBAR_EN (1ULL<<32) ++#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */ ++#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */ ++ /* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */ ++ /* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */ ++ ++/* Pin function selection MSR (IDE vs. flash on the IDE pins) */ ++#define MSR_DIVIL_BALL_OPTS 0x51400015 ++#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */ ++ ++/* Registers within the NAND flash controller BAR -- memory mapped */ ++#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */ ++#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */ ++#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */ ++#define MM_NAND_STS 0x810 ++#define MM_NAND_ECC_LSB 0x811 ++#define MM_NAND_ECC_MSB 0x812 ++#define MM_NAND_ECC_COL 0x813 ++#define MM_NAND_LAC 0x814 ++#define MM_NAND_ECC_CTL 0x815 ++ ++/* Registers within the NAND flash controller BAR -- I/O mapped */ ++#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */ ++#define IO_NAND_CTL 0x04 ++#define IO_NAND_IO 0x05 ++#define IO_NAND_STS 0x06 ++#define IO_NAND_ECC_CTL 0x08 ++#define IO_NAND_ECC_LSB 0x09 ++#define IO_NAND_ECC_MSB 0x0a ++#define IO_NAND_ECC_COL 0x0b ++#define IO_NAND_LAC 0x0c ++ ++#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */ ++#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */ ++#define CS_NAND_CTL_ALE (1<<2) ++#define CS_NAND_CTL_CLE (1<<1) ++#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */ ++ ++#define CS_NAND_STS_FLASH_RDY (1<<3) ++#define CS_NAND_CTLR_BUSY (1<<2) ++#define CS_NAND_CMD_COMP (1<<1) ++#define CS_NAND_DIST_ST (1<<0) ++ ++#define CS_NAND_ECC_PARITY (1<<2) ++#define CS_NAND_ECC_CLRECC (1<<1) ++#define CS_NAND_ECC_ENECC (1<<0) ++ ++static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ while (unlikely(len > 0x800)) { ++ memcpy_fromio(buf, this->IO_ADDR_R, 0x800); ++ buf += 0x800; ++ len -= 0x800; ++ } ++ memcpy_fromio(buf, this->IO_ADDR_R, len); ++} ++ ++static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ while (unlikely(len > 0x800)) { ++ memcpy_toio(this->IO_ADDR_R, buf, 0x800); ++ buf += 0x800; ++ len -= 0x800; ++ } ++ memcpy_toio(this->IO_ADDR_R, buf, len); ++} ++ ++static unsigned char cs553x_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ return readb(this->IO_ADDR_R); ++} ++ ++static void cs553x_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int i = 100000; ++ ++ while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) { ++ udelay(1); ++ i--; ++ } ++ writeb(byte, this->IO_ADDR_W + 0x801); ++} ++ ++static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ void __iomem *mmio_base = this->IO_ADDR_R; ++ if (ctrl & NAND_CTRL_CHANGE) { ++ unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01; ++ writeb(ctl, mmio_base + MM_NAND_CTL); ++ } ++ if (cmd != NAND_CMD_NONE) ++ cs553x_write_byte(mtd, cmd); ++} ++ ++static int cs553x_device_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ void __iomem *mmio_base = this->IO_ADDR_R; ++ unsigned char foo = readb(mmio_base + MM_NAND_STS); ++ ++ return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY); ++} ++ ++static void cs_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ void __iomem *mmio_base = this->IO_ADDR_R; ++ ++ writeb(0x07, mmio_base + MM_NAND_ECC_CTL); ++} ++ ++static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) ++{ ++ uint32_t ecc; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ void __iomem *mmio_base = this->IO_ADDR_R; ++ ++ ecc = readl(mmio_base + MM_NAND_STS); ++ ++ ecc_code[1] = ecc >> 8; ++ ecc_code[0] = ecc >> 16; ++ ecc_code[2] = ecc >> 24; ++ return 0; ++} ++ ++static struct mtd_info *cs553x_mtd[4]; ++ ++static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) ++{ ++ int err = 0; ++ struct nand_chip *this; ++ struct mtd_info *new_mtd; ++ ++ printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr); ++ ++ if (!mmio) { ++ printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n"); ++ return -ENXIO; ++ } ++ ++ /* Allocate memory for MTD device structure and private data */ ++ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ++ if (!this) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ new_mtd = nand_to_mtd(this); ++ ++ /* Link the private data with the MTD structure */ ++ new_mtd->owner = THIS_MODULE; ++ ++ /* map physical address */ ++ this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096); ++ if (!this->IO_ADDR_R) { ++ printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr); ++ err = -EIO; ++ goto out_mtd; ++ } ++ ++ this->cmd_ctrl = cs553x_hwcontrol; ++ this->dev_ready = cs553x_device_ready; ++ this->read_byte = cs553x_read_byte; ++ this->read_buf = cs553x_read_buf; ++ this->write_buf = cs553x_write_buf; ++ ++ this->chip_delay = 0; ++ ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 256; ++ this->ecc.bytes = 3; ++ this->ecc.hwctl = cs_enable_hwecc; ++ this->ecc.calculate = cs_calculate_ecc; ++ this->ecc.correct = nand_correct_data; ++ this->ecc.strength = 1; ++ ++ /* Enable the following for a flash based bad block table */ ++ this->bbt_options = NAND_BBT_USE_FLASH; ++ ++ new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs); ++ if (!new_mtd->name) { ++ err = -ENOMEM; ++ goto out_ior; ++ } ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(new_mtd, 1); ++ if (err) ++ goto out_free; ++ ++ cs553x_mtd[cs] = new_mtd; ++ goto out; ++ ++out_free: ++ kfree(new_mtd->name); ++out_ior: ++ iounmap(this->IO_ADDR_R); ++out_mtd: ++ kfree(this); ++out: ++ return err; ++} ++ ++static int is_geode(void) ++{ ++ /* These are the CPUs which will have a CS553[56] companion chip */ ++ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && ++ boot_cpu_data.x86 == 5 && ++ boot_cpu_data.x86_model == 10) ++ return 1; /* Geode LX */ ++ ++ if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC || ++ boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) && ++ boot_cpu_data.x86 == 5 && ++ boot_cpu_data.x86_model == 5) ++ return 1; /* Geode GX (née GX2) */ ++ ++ return 0; ++} ++ ++static int __init cs553x_init(void) ++{ ++ int err = -ENXIO; ++ int i; ++ uint64_t val; ++ ++ /* If the CPU isn't a Geode GX or LX, abort */ ++ if (!is_geode()) ++ return -ENXIO; ++ ++ /* If it doesn't have the CS553[56], abort */ ++ rdmsrl(MSR_DIVIL_GLD_CAP, val); ++ val &= ~0xFFULL; ++ if (val != CAP_CS5535 && val != CAP_CS5536) ++ return -ENXIO; ++ ++ /* If it doesn't have the NAND controller enabled, abort */ ++ rdmsrl(MSR_DIVIL_BALL_OPTS, val); ++ if (val & PIN_OPT_IDE) { ++ printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n"); ++ return -ENXIO; ++ } ++ ++ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { ++ rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val); ++ ++ if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND)) ++ err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF); ++ } ++ ++ /* Register all devices together here. This means we can easily hack it to ++ do mtdconcat etc. if we want to. */ ++ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { ++ if (cs553x_mtd[i]) { ++ /* If any devices registered, return success. Else the last error. */ ++ mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, ++ NULL, 0); ++ err = 0; ++ } ++ } ++ ++ return err; ++} ++ ++module_init(cs553x_init); ++ ++static void __exit cs553x_cleanup(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { ++ struct mtd_info *mtd = cs553x_mtd[i]; ++ struct nand_chip *this; ++ void __iomem *mmio_base; ++ ++ if (!mtd) ++ continue; ++ ++ this = mtd_to_nand(mtd); ++ mmio_base = this->IO_ADDR_R; ++ ++ /* Release resources, unregister device */ ++ nand_release(mtd); ++ kfree(mtd->name); ++ cs553x_mtd[i] = NULL; ++ ++ /* unmap physical address */ ++ iounmap(mmio_base); ++ ++ /* Free the MTD device structure */ ++ kfree(this); ++ } ++} ++ ++module_exit(cs553x_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip"); +diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c +new file mode 100644 +index 00000000..ccc8c43 +--- /dev/null ++++ b/drivers/mtd/nand/raw/davinci_nand.c +@@ -0,0 +1,879 @@ ++/* ++ * davinci_nand.c - NAND Flash Driver for DaVinci family chips ++ * ++ * Copyright © 2006 Texas Instruments. ++ * ++ * Port to 2.6.23 Copyright © 2008 by: ++ * Sander Huijsen ++ * Troy Kisky ++ * Dirk Behme ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * This is a device driver for the NAND flash controller found on the ++ * various DaVinci family chips. It handles up to four SoC chipselects, ++ * and some flavors of secondary chipselect (e.g. based on A12) as used ++ * with multichip packages. ++ * ++ * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC ++ * available on chips like the DM355 and OMAP-L137 and needed with the ++ * more error-prone MLC NAND chips. ++ * ++ * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY ++ * outputs in a "wire-AND" configuration, with no per-chip signals. ++ */ ++struct davinci_nand_info { ++ struct nand_chip chip; ++ ++ struct device *dev; ++ struct clk *clk; ++ ++ bool is_readmode; ++ ++ void __iomem *base; ++ void __iomem *vaddr; ++ ++ uint32_t ioaddr; ++ uint32_t current_cs; ++ ++ uint32_t mask_chipsel; ++ uint32_t mask_ale; ++ uint32_t mask_cle; ++ ++ uint32_t core_chipsel; ++ ++ struct davinci_aemif_timing *timing; ++}; ++ ++static DEFINE_SPINLOCK(davinci_nand_lock); ++static bool ecc4_busy; ++ ++static inline struct davinci_nand_info *to_davinci_nand(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct davinci_nand_info, chip); ++} ++ ++static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info, ++ int offset) ++{ ++ return __raw_readl(info->base + offset); ++} ++ ++static inline void davinci_nand_writel(struct davinci_nand_info *info, ++ int offset, unsigned long value) ++{ ++ __raw_writel(value, info->base + offset); ++} ++ ++/*----------------------------------------------------------------------*/ ++ ++/* ++ * Access to hardware control lines: ALE, CLE, secondary chipselect. ++ */ ++ ++static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ uint32_t addr = info->current_cs; ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ ++ /* Did the control lines change? */ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE) ++ addr |= info->mask_cle; ++ else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE) ++ addr |= info->mask_ale; ++ ++ nand->IO_ADDR_W = (void __iomem __force *)addr; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ iowrite8(cmd, nand->IO_ADDR_W); ++} ++ ++static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ uint32_t addr = info->ioaddr; ++ ++ /* maybe kick in a second chipselect */ ++ if (chip > 0) ++ addr |= info->mask_chipsel; ++ info->current_cs = addr; ++ ++ info->chip.IO_ADDR_W = (void __iomem __force *)addr; ++ info->chip.IO_ADDR_R = info->chip.IO_ADDR_W; ++} ++ ++/*----------------------------------------------------------------------*/ ++ ++/* ++ * 1-bit hardware ECC ... context maintained for each core chipselect ++ */ ++ ++static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd) ++{ ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ ++ return davinci_nand_readl(info, NANDF1ECC_OFFSET ++ + 4 * info->core_chipsel); ++} ++ ++static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode) ++{ ++ struct davinci_nand_info *info; ++ uint32_t nandcfr; ++ unsigned long flags; ++ ++ info = to_davinci_nand(mtd); ++ ++ /* Reset ECC hardware */ ++ nand_davinci_readecc_1bit(mtd); ++ ++ spin_lock_irqsave(&davinci_nand_lock, flags); ++ ++ /* Restart ECC hardware */ ++ nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET); ++ nandcfr |= BIT(8 + info->core_chipsel); ++ davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr); ++ ++ spin_unlock_irqrestore(&davinci_nand_lock, flags); ++} ++ ++/* ++ * Read hardware ECC value and pack into three bytes ++ */ ++static int nand_davinci_calculate_1bit(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_code) ++{ ++ unsigned int ecc_val = nand_davinci_readecc_1bit(mtd); ++ unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4); ++ ++ /* invert so that erased block ecc is correct */ ++ ecc24 = ~ecc24; ++ ecc_code[0] = (u_char)(ecc24); ++ ecc_code[1] = (u_char)(ecc24 >> 8); ++ ecc_code[2] = (u_char)(ecc24 >> 16); ++ ++ return 0; ++} ++ ++static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) | ++ (read_ecc[2] << 16); ++ uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) | ++ (calc_ecc[2] << 16); ++ uint32_t diff = eccCalc ^ eccNand; ++ ++ if (diff) { ++ if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { ++ /* Correctable error */ ++ if ((diff >> (12 + 3)) < chip->ecc.size) { ++ dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7); ++ return 1; ++ } else { ++ return -EBADMSG; ++ } ++ } else if (!(diff & (diff - 1))) { ++ /* Single bit ECC error in the ECC itself, ++ * nothing to fix */ ++ return 1; ++ } else { ++ /* Uncorrectable error */ ++ return -EBADMSG; ++ } ++ ++ } ++ return 0; ++} ++ ++/*----------------------------------------------------------------------*/ ++ ++/* ++ * 4-bit hardware ECC ... context maintained over entire AEMIF ++ * ++ * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME ++ * since that forces use of a problematic "infix OOB" layout. ++ * Among other things, it trashes manufacturer bad block markers. ++ * Also, and specific to this hardware, it ECC-protects the "prepad" ++ * in the OOB ... while having ECC protection for parts of OOB would ++ * seem useful, the current MTD stack sometimes wants to update the ++ * OOB without recomputing ECC. ++ */ ++ ++static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) ++{ ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ unsigned long flags; ++ u32 val; ++ ++ /* Reset ECC hardware */ ++ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); ++ ++ spin_lock_irqsave(&davinci_nand_lock, flags); ++ ++ /* Start 4-bit ECC calculation for read/write */ ++ val = davinci_nand_readl(info, NANDFCR_OFFSET); ++ val &= ~(0x03 << 4); ++ val |= (info->core_chipsel << 4) | BIT(12); ++ davinci_nand_writel(info, NANDFCR_OFFSET, val); ++ ++ info->is_readmode = (mode == NAND_ECC_READ); ++ ++ spin_unlock_irqrestore(&davinci_nand_lock, flags); ++} ++ ++/* Read raw ECC code after writing to NAND. */ ++static void ++nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) ++{ ++ const u32 mask = 0x03ff03ff; ++ ++ code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; ++ code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; ++ code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; ++ code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; ++} ++ ++/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */ ++static int nand_davinci_calculate_4bit(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_code) ++{ ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ u32 raw_ecc[4], *p; ++ unsigned i; ++ ++ /* After a read, terminate ECC calculation by a dummy read ++ * of some 4-bit ECC register. ECC covers everything that ++ * was read; correct() just uses the hardware state, so ++ * ecc_code is not needed. ++ */ ++ if (info->is_readmode) { ++ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); ++ return 0; ++ } ++ ++ /* Pack eight raw 10-bit ecc values into ten bytes, making ++ * two passes which each convert four values (in upper and ++ * lower halves of two 32-bit words) into five bytes. The ++ * ROM boot loader uses this same packing scheme. ++ */ ++ nand_davinci_readecc_4bit(info, raw_ecc); ++ for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { ++ *ecc_code++ = p[0] & 0xff; ++ *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); ++ *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); ++ *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); ++ *ecc_code++ = (p[1] >> 18) & 0xff; ++ } ++ ++ return 0; ++} ++ ++/* Correct up to 4 bits in data we just read, using state left in the ++ * hardware plus the ecc_code computed when it was first written. ++ */ ++static int nand_davinci_correct_4bit(struct mtd_info *mtd, ++ u_char *data, u_char *ecc_code, u_char *null) ++{ ++ int i; ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ unsigned short ecc10[8]; ++ unsigned short *ecc16; ++ u32 syndrome[4]; ++ u32 ecc_state; ++ unsigned num_errors, corrected; ++ unsigned long timeo; ++ ++ /* Unpack ten bytes into eight 10 bit values. We know we're ++ * little-endian, and use type punning for less shifting/masking. ++ */ ++ if (WARN_ON(0x01 & (unsigned) ecc_code)) ++ return -EINVAL; ++ ecc16 = (unsigned short *)ecc_code; ++ ++ ecc10[0] = (ecc16[0] >> 0) & 0x3ff; ++ ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); ++ ecc10[2] = (ecc16[1] >> 4) & 0x3ff; ++ ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); ++ ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); ++ ecc10[5] = (ecc16[3] >> 2) & 0x3ff; ++ ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); ++ ecc10[7] = (ecc16[4] >> 6) & 0x3ff; ++ ++ /* Tell ECC controller about the expected ECC codes. */ ++ for (i = 7; i >= 0; i--) ++ davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); ++ ++ /* Allow time for syndrome calculation ... then read it. ++ * A syndrome of all zeroes 0 means no detected errors. ++ */ ++ davinci_nand_readl(info, NANDFSR_OFFSET); ++ nand_davinci_readecc_4bit(info, syndrome); ++ if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) ++ return 0; ++ ++ /* ++ * Clear any previous address calculation by doing a dummy read of an ++ * error address register. ++ */ ++ davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET); ++ ++ /* Start address calculation, and wait for it to complete. ++ * We _could_ start reading more data while this is working, ++ * to speed up the overall page read. ++ */ ++ davinci_nand_writel(info, NANDFCR_OFFSET, ++ davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); ++ ++ /* ++ * ECC_STATE field reads 0x3 (Error correction complete) immediately ++ * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately ++ * begin trying to poll for the state, you may fall right out of your ++ * loop without any of the correction calculations having taken place. ++ * The recommendation from the hardware team is to initially delay as ++ * long as ECC_STATE reads less than 4. After that, ECC HW has entered ++ * correction state. ++ */ ++ timeo = jiffies + usecs_to_jiffies(100); ++ do { ++ ecc_state = (davinci_nand_readl(info, ++ NANDFSR_OFFSET) >> 8) & 0x0f; ++ cpu_relax(); ++ } while ((ecc_state < 4) && time_before(jiffies, timeo)); ++ ++ for (;;) { ++ u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); ++ ++ switch ((fsr >> 8) & 0x0f) { ++ case 0: /* no error, should not happen */ ++ davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); ++ return 0; ++ case 1: /* five or more errors detected */ ++ davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); ++ return -EBADMSG; ++ case 2: /* error addresses computed */ ++ case 3: ++ num_errors = 1 + ((fsr >> 16) & 0x03); ++ goto correct; ++ default: /* still working on it */ ++ cpu_relax(); ++ continue; ++ } ++ } ++ ++correct: ++ /* correct each error */ ++ for (i = 0, corrected = 0; i < num_errors; i++) { ++ int error_address, error_value; ++ ++ if (i > 1) { ++ error_address = davinci_nand_readl(info, ++ NAND_ERR_ADD2_OFFSET); ++ error_value = davinci_nand_readl(info, ++ NAND_ERR_ERRVAL2_OFFSET); ++ } else { ++ error_address = davinci_nand_readl(info, ++ NAND_ERR_ADD1_OFFSET); ++ error_value = davinci_nand_readl(info, ++ NAND_ERR_ERRVAL1_OFFSET); ++ } ++ ++ if (i & 1) { ++ error_address >>= 16; ++ error_value >>= 16; ++ } ++ error_address &= 0x3ff; ++ error_address = (512 + 7) - error_address; ++ ++ if (error_address < 512) { ++ data[error_address] ^= error_value; ++ corrected++; ++ } ++ } ++ ++ return corrected; ++} ++ ++/*----------------------------------------------------------------------*/ ++ ++/* ++ * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's ++ * how these chips are normally wired. This translates to both 8 and 16 ++ * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). ++ * ++ * For now we assume that configuration, or any other one which ignores ++ * the two LSBs for NAND access ... so we can issue 32-bit reads/writes ++ * and have that transparently morphed into multiple NAND operations. ++ */ ++static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) ++ ioread32_rep(chip->IO_ADDR_R, buf, len >> 2); ++ else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) ++ ioread16_rep(chip->IO_ADDR_R, buf, len >> 1); ++ else ++ ioread8_rep(chip->IO_ADDR_R, buf, len); ++} ++ ++static void nand_davinci_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) ++ iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2); ++ else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) ++ iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1); ++ else ++ iowrite8_rep(chip->IO_ADDR_R, buf, len); ++} ++ ++/* ++ * Check hardware register for wait status. Returns 1 if device is ready, ++ * 0 if it is still busy. ++ */ ++static int nand_davinci_dev_ready(struct mtd_info *mtd) ++{ ++ struct davinci_nand_info *info = to_davinci_nand(mtd); ++ ++ return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0); ++} ++ ++/*----------------------------------------------------------------------*/ ++ ++/* An ECC layout for using 4-bit ECC with small-page flash, storing ++ * ten ECC bytes plus the manufacturer's bad block marker byte, and ++ * and not overlapping the default BBT markers. ++ */ ++static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 2) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->offset = 0; ++ oobregion->length = 5; ++ } else if (section == 1) { ++ oobregion->offset = 6; ++ oobregion->length = 2; ++ } else { ++ oobregion->offset = 13; ++ oobregion->length = 3; ++ } ++ ++ return 0; ++} ++ ++static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 1) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->offset = 8; ++ oobregion->length = 5; ++ } else { ++ oobregion->offset = 16; ++ oobregion->length = mtd->oobsize - 16; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = { ++ .ecc = hwecc4_ooblayout_small_ecc, ++ .free = hwecc4_ooblayout_small_free, ++}; ++ ++#if defined(CONFIG_OF) ++static const struct of_device_id davinci_nand_of_match[] = { ++ {.compatible = "ti,davinci-nand", }, ++ {.compatible = "ti,keystone-nand", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, davinci_nand_of_match); ++ ++static struct davinci_nand_pdata ++ *nand_davinci_get_pdata(struct platform_device *pdev) ++{ ++ if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { ++ struct davinci_nand_pdata *pdata; ++ const char *mode; ++ u32 prop; ++ ++ pdata = devm_kzalloc(&pdev->dev, ++ sizeof(struct davinci_nand_pdata), ++ GFP_KERNEL); ++ pdev->dev.platform_data = pdata; ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "ti,davinci-chipselect", &prop)) ++ pdev->id = prop; ++ else ++ return ERR_PTR(-EINVAL); ++ ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "ti,davinci-mask-ale", &prop)) ++ pdata->mask_ale = prop; ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "ti,davinci-mask-cle", &prop)) ++ pdata->mask_cle = prop; ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "ti,davinci-mask-chipsel", &prop)) ++ pdata->mask_chipsel = prop; ++ if (!of_property_read_string(pdev->dev.of_node, ++ "ti,davinci-ecc-mode", &mode)) { ++ if (!strncmp("none", mode, 4)) ++ pdata->ecc_mode = NAND_ECC_NONE; ++ if (!strncmp("soft", mode, 4)) ++ pdata->ecc_mode = NAND_ECC_SOFT; ++ if (!strncmp("hw", mode, 2)) ++ pdata->ecc_mode = NAND_ECC_HW; ++ } ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "ti,davinci-ecc-bits", &prop)) ++ pdata->ecc_bits = prop; ++ ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "ti,davinci-nand-buswidth", &prop) && prop == 16) ++ pdata->options |= NAND_BUSWIDTH_16; ++ ++ if (of_property_read_bool(pdev->dev.of_node, ++ "ti,davinci-nand-use-bbt")) ++ pdata->bbt_options = NAND_BBT_USE_FLASH; ++ ++ /* ++ * Since kernel v4.8, this driver has been fixed to enable ++ * use of 4-bit hardware ECC with subpages and verified on ++ * TI's keystone EVMs (K2L, K2HK and K2E). ++ * However, in the interest of not breaking systems using ++ * existing UBI partitions, sub-page writes are not being ++ * (re)enabled. If you want to use subpage writes on Keystone ++ * platforms (i.e. do not have any existing UBI partitions), ++ * then use "ti,davinci-nand" as the compatible in your ++ * device-tree file. ++ */ ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "ti,keystone-nand")) { ++ pdata->options |= NAND_NO_SUBPAGE_WRITE; ++ } ++ } ++ ++ return dev_get_platdata(&pdev->dev); ++} ++#else ++static struct davinci_nand_pdata ++ *nand_davinci_get_pdata(struct platform_device *pdev) ++{ ++ return dev_get_platdata(&pdev->dev); ++} ++#endif ++ ++static int nand_davinci_probe(struct platform_device *pdev) ++{ ++ struct davinci_nand_pdata *pdata; ++ struct davinci_nand_info *info; ++ struct resource *res1; ++ struct resource *res2; ++ void __iomem *vaddr; ++ void __iomem *base; ++ int ret; ++ uint32_t val; ++ struct mtd_info *mtd; ++ ++ pdata = nand_davinci_get_pdata(pdev); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ ++ /* insist on board-specific configuration */ ++ if (!pdata) ++ return -ENODEV; ++ ++ /* which external chipselect will we be managing? */ ++ if (pdev->id < 0 || pdev->id > 3) ++ return -ENODEV; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, info); ++ ++ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!res1 || !res2) { ++ dev_err(&pdev->dev, "resource missing\n"); ++ return -EINVAL; ++ } ++ ++ vaddr = devm_ioremap_resource(&pdev->dev, res1); ++ if (IS_ERR(vaddr)) ++ return PTR_ERR(vaddr); ++ ++ /* ++ * This registers range is used to setup NAND settings. In case with ++ * TI AEMIF driver, the same memory address range is requested already ++ * by AEMIF, so we cannot request it twice, just ioremap. ++ * The AEMIF and NAND drivers not use the same registers in this range. ++ */ ++ base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2)); ++ if (!base) { ++ dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2); ++ return -EADDRNOTAVAIL; ++ } ++ ++ info->dev = &pdev->dev; ++ info->base = base; ++ info->vaddr = vaddr; ++ ++ mtd = nand_to_mtd(&info->chip); ++ mtd->dev.parent = &pdev->dev; ++ nand_set_flash_node(&info->chip, pdev->dev.of_node); ++ ++ info->chip.IO_ADDR_R = vaddr; ++ info->chip.IO_ADDR_W = vaddr; ++ info->chip.chip_delay = 0; ++ info->chip.select_chip = nand_davinci_select_chip; ++ ++ /* options such as NAND_BBT_USE_FLASH */ ++ info->chip.bbt_options = pdata->bbt_options; ++ /* options such as 16-bit widths */ ++ info->chip.options = pdata->options; ++ info->chip.bbt_td = pdata->bbt_td; ++ info->chip.bbt_md = pdata->bbt_md; ++ info->timing = pdata->timing; ++ ++ info->ioaddr = (uint32_t __force) vaddr; ++ ++ info->current_cs = info->ioaddr; ++ info->core_chipsel = pdev->id; ++ info->mask_chipsel = pdata->mask_chipsel; ++ ++ /* use nandboot-capable ALE/CLE masks by default */ ++ info->mask_ale = pdata->mask_ale ? : MASK_ALE; ++ info->mask_cle = pdata->mask_cle ? : MASK_CLE; ++ ++ /* Set address of hardware control function */ ++ info->chip.cmd_ctrl = nand_davinci_hwcontrol; ++ info->chip.dev_ready = nand_davinci_dev_ready; ++ ++ /* Speed up buffer I/O */ ++ info->chip.read_buf = nand_davinci_read_buf; ++ info->chip.write_buf = nand_davinci_write_buf; ++ ++ /* Use board-specific ECC config */ ++ info->chip.ecc.mode = pdata->ecc_mode; ++ ++ ret = -EINVAL; ++ ++ info->clk = devm_clk_get(&pdev->dev, "aemif"); ++ if (IS_ERR(info->clk)) { ++ ret = PTR_ERR(info->clk); ++ dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(info->clk); ++ if (ret < 0) { ++ dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", ++ ret); ++ goto err_clk_enable; ++ } ++ ++ spin_lock_irq(&davinci_nand_lock); ++ ++ /* put CSxNAND into NAND mode */ ++ val = davinci_nand_readl(info, NANDFCR_OFFSET); ++ val |= BIT(info->core_chipsel); ++ davinci_nand_writel(info, NANDFCR_OFFSET, val); ++ ++ spin_unlock_irq(&davinci_nand_lock); ++ ++ /* Scan to find existence of the device(s) */ ++ ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); ++ if (ret < 0) { ++ dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); ++ goto err; ++ } ++ ++ switch (info->chip.ecc.mode) { ++ case NAND_ECC_NONE: ++ pdata->ecc_bits = 0; ++ break; ++ case NAND_ECC_SOFT: ++ pdata->ecc_bits = 0; ++ /* ++ * This driver expects Hamming based ECC when ecc_mode is set ++ * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to ++ * avoid adding an extra ->ecc_algo field to ++ * davinci_nand_pdata. ++ */ ++ info->chip.ecc.algo = NAND_ECC_HAMMING; ++ break; ++ case NAND_ECC_HW: ++ if (pdata->ecc_bits == 4) { ++ /* No sanity checks: CPUs must support this, ++ * and the chips may not use NAND_BUSWIDTH_16. ++ */ ++ ++ /* No sharing 4-bit hardware between chipselects yet */ ++ spin_lock_irq(&davinci_nand_lock); ++ if (ecc4_busy) ++ ret = -EBUSY; ++ else ++ ecc4_busy = true; ++ spin_unlock_irq(&davinci_nand_lock); ++ ++ if (ret == -EBUSY) ++ return ret; ++ ++ info->chip.ecc.calculate = nand_davinci_calculate_4bit; ++ info->chip.ecc.correct = nand_davinci_correct_4bit; ++ info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; ++ info->chip.ecc.bytes = 10; ++ info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; ++ info->chip.ecc.algo = NAND_ECC_BCH; ++ } else { ++ /* 1bit ecc hamming */ ++ info->chip.ecc.calculate = nand_davinci_calculate_1bit; ++ info->chip.ecc.correct = nand_davinci_correct_1bit; ++ info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; ++ info->chip.ecc.bytes = 3; ++ info->chip.ecc.algo = NAND_ECC_HAMMING; ++ } ++ info->chip.ecc.size = 512; ++ info->chip.ecc.strength = pdata->ecc_bits; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Update ECC layout if needed ... for 1-bit HW ECC, the default ++ * is OK, but it allocates 6 bytes when only 3 are needed (for ++ * each 512 bytes). For the 4-bit HW ECC, that default is not ++ * usable: 10 bytes are needed, not 6. ++ */ ++ if (pdata->ecc_bits == 4) { ++ int chunks = mtd->writesize / 512; ++ ++ if (!chunks || mtd->oobsize < 16) { ++ dev_dbg(&pdev->dev, "too small\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ /* For small page chips, preserve the manufacturer's ++ * badblock marking data ... and make sure a flash BBT ++ * table marker fits in the free bytes. ++ */ ++ if (chunks == 1) { ++ mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); ++ } else if (chunks == 4 || chunks == 8) { ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ++ info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; ++ } else { ++ ret = -EIO; ++ goto err; ++ } ++ } ++ ++ ret = nand_scan_tail(mtd); ++ if (ret < 0) ++ goto err; ++ ++ if (pdata->parts) ++ ret = mtd_device_parse_register(mtd, NULL, NULL, ++ pdata->parts, pdata->nr_parts); ++ else ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret < 0) ++ goto err; ++ ++ val = davinci_nand_readl(info, NRCSR_OFFSET); ++ dev_info(&pdev->dev, "controller rev. %d.%d\n", ++ (val >> 8) & 0xff, val & 0xff); ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(info->clk); ++ ++err_clk_enable: ++ spin_lock_irq(&davinci_nand_lock); ++ if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) ++ ecc4_busy = false; ++ spin_unlock_irq(&davinci_nand_lock); ++ return ret; ++} ++ ++static int nand_davinci_remove(struct platform_device *pdev) ++{ ++ struct davinci_nand_info *info = platform_get_drvdata(pdev); ++ ++ spin_lock_irq(&davinci_nand_lock); ++ if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) ++ ecc4_busy = false; ++ spin_unlock_irq(&davinci_nand_lock); ++ ++ nand_release(nand_to_mtd(&info->chip)); ++ ++ clk_disable_unprepare(info->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver nand_davinci_driver = { ++ .probe = nand_davinci_probe, ++ .remove = nand_davinci_remove, ++ .driver = { ++ .name = "davinci_nand", ++ .of_match_table = of_match_ptr(davinci_nand_of_match), ++ }, ++}; ++MODULE_ALIAS("platform:davinci_nand"); ++ ++module_platform_driver(nand_davinci_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_DESCRIPTION("Davinci NAND flash driver"); ++ +diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c +new file mode 100644 +index 00000000..200fb34 +--- /dev/null ++++ b/drivers/mtd/nand/raw/denali.c +@@ -0,0 +1,1451 @@ ++/* ++ * NAND Flash Controller Device Driver ++ * Copyright © 2009-2010, Intel Corporation and its suppliers. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "denali.h" ++ ++MODULE_LICENSE("GPL"); ++ ++#define DENALI_NAND_NAME "denali-nand" ++ ++/* Host Data/Command Interface */ ++#define DENALI_HOST_ADDR 0x00 ++#define DENALI_HOST_DATA 0x10 ++ ++#define DENALI_MAP00 (0 << 26) /* direct access to buffer */ ++#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ ++#define DENALI_MAP10 (2 << 26) /* high-level control plane */ ++#define DENALI_MAP11 (3 << 26) /* direct controller access */ ++ ++/* MAP11 access cycle type */ ++#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */ ++#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ ++#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ ++ ++/* MAP10 commands */ ++#define DENALI_ERASE 0x01 ++ ++#define DENALI_BANK(denali) ((denali)->active_bank << 24) ++ ++#define DENALI_INVALID_BANK -1 ++#define DENALI_NR_BANKS 4 ++ ++/* ++ * The bus interface clock, clk_x, is phase aligned with the core clock. The ++ * clk_x is an integral multiple N of the core clk. The value N is configured ++ * at IP delivery time, and its available value is 4, 5, or 6. We need to align ++ * to the largest value to make it work with any possible configuration. ++ */ ++#define DENALI_CLK_X_MULT 6 ++ ++/* ++ * this macro allows us to convert from an MTD structure to our own ++ * device context (denali) structure. ++ */ ++static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); ++} ++ ++static void denali_host_write(struct denali_nand_info *denali, ++ uint32_t addr, uint32_t data) ++{ ++ iowrite32(addr, denali->host + DENALI_HOST_ADDR); ++ iowrite32(data, denali->host + DENALI_HOST_DATA); ++} ++ ++/* ++ * Use the configuration feature register to determine the maximum number of ++ * banks that the hardware supports. ++ */ ++static void detect_max_banks(struct denali_nand_info *denali) ++{ ++ uint32_t features = ioread32(denali->reg + FEATURES); ++ ++ denali->max_banks = 1 << (features & FEATURES__N_BANKS); ++ ++ /* the encoding changed from rev 5.0 to 5.1 */ ++ if (denali->revision < 0x0501) ++ denali->max_banks <<= 1; ++} ++ ++static void denali_enable_irq(struct denali_nand_info *denali) ++{ ++ int i; ++ ++ for (i = 0; i < DENALI_NR_BANKS; i++) ++ iowrite32(U32_MAX, denali->reg + INTR_EN(i)); ++ iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE); ++} ++ ++static void denali_disable_irq(struct denali_nand_info *denali) ++{ ++ int i; ++ ++ for (i = 0; i < DENALI_NR_BANKS; i++) ++ iowrite32(0, denali->reg + INTR_EN(i)); ++ iowrite32(0, denali->reg + GLOBAL_INT_ENABLE); ++} ++ ++static void denali_clear_irq(struct denali_nand_info *denali, ++ int bank, uint32_t irq_status) ++{ ++ /* write one to clear bits */ ++ iowrite32(irq_status, denali->reg + INTR_STATUS(bank)); ++} ++ ++static void denali_clear_irq_all(struct denali_nand_info *denali) ++{ ++ int i; ++ ++ for (i = 0; i < DENALI_NR_BANKS; i++) ++ denali_clear_irq(denali, i, U32_MAX); ++} ++ ++static irqreturn_t denali_isr(int irq, void *dev_id) ++{ ++ struct denali_nand_info *denali = dev_id; ++ irqreturn_t ret = IRQ_NONE; ++ uint32_t irq_status; ++ int i; ++ ++ spin_lock(&denali->irq_lock); ++ ++ for (i = 0; i < DENALI_NR_BANKS; i++) { ++ irq_status = ioread32(denali->reg + INTR_STATUS(i)); ++ if (irq_status) ++ ret = IRQ_HANDLED; ++ ++ denali_clear_irq(denali, i, irq_status); ++ ++ if (i != denali->active_bank) ++ continue; ++ ++ denali->irq_status |= irq_status; ++ ++ if (denali->irq_status & denali->irq_mask) ++ complete(&denali->complete); ++ } ++ ++ spin_unlock(&denali->irq_lock); ++ ++ return ret; ++} ++ ++static void denali_reset_irq(struct denali_nand_info *denali) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&denali->irq_lock, flags); ++ denali->irq_status = 0; ++ denali->irq_mask = 0; ++ spin_unlock_irqrestore(&denali->irq_lock, flags); ++} ++ ++static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, ++ uint32_t irq_mask) ++{ ++ unsigned long time_left, flags; ++ uint32_t irq_status; ++ ++ spin_lock_irqsave(&denali->irq_lock, flags); ++ ++ irq_status = denali->irq_status; ++ ++ if (irq_mask & irq_status) { ++ /* return immediately if the IRQ has already happened. */ ++ spin_unlock_irqrestore(&denali->irq_lock, flags); ++ return irq_status; ++ } ++ ++ denali->irq_mask = irq_mask; ++ reinit_completion(&denali->complete); ++ spin_unlock_irqrestore(&denali->irq_lock, flags); ++ ++ time_left = wait_for_completion_timeout(&denali->complete, ++ msecs_to_jiffies(1000)); ++ if (!time_left) { ++ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", ++ denali->irq_mask); ++ return 0; ++ } ++ ++ return denali->irq_status; ++} ++ ++static uint32_t denali_check_irq(struct denali_nand_info *denali) ++{ ++ unsigned long flags; ++ uint32_t irq_status; ++ ++ spin_lock_irqsave(&denali->irq_lock, flags); ++ irq_status = denali->irq_status; ++ spin_unlock_irqrestore(&denali->irq_lock, flags); ++ ++ return irq_status; ++} ++ ++/* ++ * This helper function setups the registers for ECC and whether or not ++ * the spare area will be transferred. ++ */ ++static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, ++ bool transfer_spare) ++{ ++ int ecc_en_flag, transfer_spare_flag; ++ ++ /* set ECC, transfer spare bits if needed */ ++ ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0; ++ transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0; ++ ++ /* Enable spare area/ECC per user's request. */ ++ iowrite32(ecc_en_flag, denali->reg + ECC_ENABLE); ++ iowrite32(transfer_spare_flag, denali->reg + TRANSFER_SPARE_REG); ++} ++ ++static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ int i; ++ ++ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), ++ denali->host + DENALI_HOST_ADDR); ++ ++ for (i = 0; i < len; i++) ++ buf[i] = ioread32(denali->host + DENALI_HOST_DATA); ++} ++ ++static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ int i; ++ ++ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), ++ denali->host + DENALI_HOST_ADDR); ++ ++ for (i = 0; i < len; i++) ++ iowrite32(buf[i], denali->host + DENALI_HOST_DATA); ++} ++ ++static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ uint16_t *buf16 = (uint16_t *)buf; ++ int i; ++ ++ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), ++ denali->host + DENALI_HOST_ADDR); ++ ++ for (i = 0; i < len / 2; i++) ++ buf16[i] = ioread32(denali->host + DENALI_HOST_DATA); ++} ++ ++static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ const uint16_t *buf16 = (const uint16_t *)buf; ++ int i; ++ ++ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali), ++ denali->host + DENALI_HOST_ADDR); ++ ++ for (i = 0; i < len / 2; i++) ++ iowrite32(buf16[i], denali->host + DENALI_HOST_DATA); ++} ++ ++static uint8_t denali_read_byte(struct mtd_info *mtd) ++{ ++ uint8_t byte; ++ ++ denali_read_buf(mtd, &byte, 1); ++ ++ return byte; ++} ++ ++static void denali_write_byte(struct mtd_info *mtd, uint8_t byte) ++{ ++ denali_write_buf(mtd, &byte, 1); ++} ++ ++static uint16_t denali_read_word(struct mtd_info *mtd) ++{ ++ uint16_t word; ++ ++ denali_read_buf16(mtd, (uint8_t *)&word, 2); ++ ++ return word; ++} ++ ++static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ uint32_t type; ++ ++ if (ctrl & NAND_CLE) ++ type = DENALI_MAP11_CMD; ++ else if (ctrl & NAND_ALE) ++ type = DENALI_MAP11_ADDR; ++ else ++ return; ++ ++ /* ++ * Some commands are followed by chip->dev_ready or chip->waitfunc. ++ * irq_status must be cleared here to catch the R/B# interrupt later. ++ */ ++ if (ctrl & NAND_CTRL_CHANGE) ++ denali_reset_irq(denali); ++ ++ denali_host_write(denali, DENALI_BANK(denali) | type, dat); ++} ++ ++static int denali_dev_ready(struct mtd_info *mtd) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ ++ return !!(denali_check_irq(denali) & INTR__INT_ACT); ++} ++ ++static int denali_check_erased_page(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ unsigned long uncor_ecc_flags, ++ unsigned int max_bitflips) ++{ ++ uint8_t *ecc_code = chip->ecc.code_buf; ++ int ecc_steps = chip->ecc.steps; ++ int ecc_size = chip->ecc.size; ++ int ecc_bytes = chip->ecc.bytes; ++ int i, ret, stat; ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < ecc_steps; i++) { ++ if (!(uncor_ecc_flags & BIT(i))) ++ continue; ++ ++ stat = nand_check_erased_ecc_chunk(buf, ecc_size, ++ ecc_code, ecc_bytes, ++ NULL, 0, ++ chip->ecc.strength); ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ ++ buf += ecc_size; ++ ecc_code += ecc_bytes; ++ } ++ ++ return max_bitflips; ++} ++ ++static int denali_hw_ecc_fixup(struct mtd_info *mtd, ++ struct denali_nand_info *denali, ++ unsigned long *uncor_ecc_flags) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int bank = denali->active_bank; ++ uint32_t ecc_cor; ++ unsigned int max_bitflips; ++ ++ ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank)); ++ ecc_cor >>= ECC_COR_INFO__SHIFT(bank); ++ ++ if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) { ++ /* ++ * This flag is set when uncorrectable error occurs at least in ++ * one ECC sector. We can not know "how many sectors", or ++ * "which sector(s)". We need erase-page check for all sectors. ++ */ ++ *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0); ++ return 0; ++ } ++ ++ max_bitflips = ecc_cor & ECC_COR_INFO__MAX_ERRORS; ++ ++ /* ++ * The register holds the maximum of per-sector corrected bitflips. ++ * This is suitable for the return value of the ->read_page() callback. ++ * Unfortunately, we can not know the total number of corrected bits in ++ * the page. Increase the stats by max_bitflips. (compromised solution) ++ */ ++ mtd->ecc_stats.corrected += max_bitflips; ++ ++ return max_bitflips; ++} ++ ++#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) ++#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) ++#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) ++#define ECC_ERROR_UNCORRECTABLE(x) ((x) & ERR_CORRECTION_INFO__ERROR_TYPE) ++#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8) ++#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) ++ ++static int denali_sw_ecc_fixup(struct mtd_info *mtd, ++ struct denali_nand_info *denali, ++ unsigned long *uncor_ecc_flags, uint8_t *buf) ++{ ++ unsigned int ecc_size = denali->nand.ecc.size; ++ unsigned int bitflips = 0; ++ unsigned int max_bitflips = 0; ++ uint32_t err_addr, err_cor_info; ++ unsigned int err_byte, err_sector, err_device; ++ uint8_t err_cor_value; ++ unsigned int prev_sector = 0; ++ uint32_t irq_status; ++ ++ denali_reset_irq(denali); ++ ++ do { ++ err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); ++ err_sector = ECC_SECTOR(err_addr); ++ err_byte = ECC_BYTE(err_addr); ++ ++ err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); ++ err_cor_value = ECC_CORRECTION_VALUE(err_cor_info); ++ err_device = ECC_ERR_DEVICE(err_cor_info); ++ ++ /* reset the bitflip counter when crossing ECC sector */ ++ if (err_sector != prev_sector) ++ bitflips = 0; ++ ++ if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) { ++ /* ++ * Check later if this is a real ECC error, or ++ * an erased sector. ++ */ ++ *uncor_ecc_flags |= BIT(err_sector); ++ } else if (err_byte < ecc_size) { ++ /* ++ * If err_byte is larger than ecc_size, means error ++ * happened in OOB, so we ignore it. It's no need for ++ * us to correct it err_device is represented the NAND ++ * error bits are happened in if there are more than ++ * one NAND connected. ++ */ ++ int offset; ++ unsigned int flips_in_byte; ++ ++ offset = (err_sector * ecc_size + err_byte) * ++ denali->devs_per_cs + err_device; ++ ++ /* correct the ECC error */ ++ flips_in_byte = hweight8(buf[offset] ^ err_cor_value); ++ buf[offset] ^= err_cor_value; ++ mtd->ecc_stats.corrected += flips_in_byte; ++ bitflips += flips_in_byte; ++ ++ max_bitflips = max(max_bitflips, bitflips); ++ } ++ ++ prev_sector = err_sector; ++ } while (!ECC_LAST_ERR(err_cor_info)); ++ ++ /* ++ * Once handle all ecc errors, controller will trigger a ++ * ECC_TRANSACTION_DONE interrupt, so here just wait for ++ * a while for this interrupt ++ */ ++ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); ++ if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) ++ return -EIO; ++ ++ return max_bitflips; ++} ++ ++/* programs the controller to either enable/disable DMA transfers */ ++static void denali_enable_dma(struct denali_nand_info *denali, bool en) ++{ ++ iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->reg + DMA_ENABLE); ++ ioread32(denali->reg + DMA_ENABLE); ++} ++ ++static void denali_setup_dma64(struct denali_nand_info *denali, ++ dma_addr_t dma_addr, int page, int write) ++{ ++ uint32_t mode; ++ const int page_count = 1; ++ ++ mode = DENALI_MAP10 | DENALI_BANK(denali) | page; ++ ++ /* DMA is a three step process */ ++ ++ /* ++ * 1. setup transfer type, interrupt when complete, ++ * burst len = 64 bytes, the number of pages ++ */ ++ denali_host_write(denali, mode, ++ 0x01002000 | (64 << 16) | (write << 8) | page_count); ++ ++ /* 2. set memory low address */ ++ denali_host_write(denali, mode, dma_addr); ++ ++ /* 3. set memory high address */ ++ denali_host_write(denali, mode, (uint64_t)dma_addr >> 32); ++} ++ ++static void denali_setup_dma32(struct denali_nand_info *denali, ++ dma_addr_t dma_addr, int page, int write) ++{ ++ uint32_t mode; ++ const int page_count = 1; ++ ++ mode = DENALI_MAP10 | DENALI_BANK(denali); ++ ++ /* DMA is a four step process */ ++ ++ /* 1. setup transfer type and # of pages */ ++ denali_host_write(denali, mode | page, ++ 0x2000 | (write << 8) | page_count); ++ ++ /* 2. set memory high address bits 23:8 */ ++ denali_host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); ++ ++ /* 3. set memory low address bits 23:8 */ ++ denali_host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); ++ ++ /* 4. interrupt when complete, burst len = 64 bytes */ ++ denali_host_write(denali, mode | 0x14000, 0x2400); ++} ++ ++static void denali_setup_dma(struct denali_nand_info *denali, ++ dma_addr_t dma_addr, int page, int write) ++{ ++ if (denali->caps & DENALI_CAP_DMA_64BIT) ++ denali_setup_dma64(denali, dma_addr, page, write); ++ else ++ denali_setup_dma32(denali, dma_addr, page, write); ++} ++ ++static int denali_pio_read(struct denali_nand_info *denali, void *buf, ++ size_t size, int page, int raw) ++{ ++ uint32_t addr = DENALI_BANK(denali) | page; ++ uint32_t *buf32 = (uint32_t *)buf; ++ uint32_t irq_status, ecc_err_mask; ++ int i; ++ ++ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) ++ ecc_err_mask = INTR__ECC_UNCOR_ERR; ++ else ++ ecc_err_mask = INTR__ECC_ERR; ++ ++ denali_reset_irq(denali); ++ ++ iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR); ++ for (i = 0; i < size / 4; i++) ++ *buf32++ = ioread32(denali->host + DENALI_HOST_DATA); ++ ++ irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); ++ if (!(irq_status & INTR__PAGE_XFER_INC)) ++ return -EIO; ++ ++ if (irq_status & INTR__ERASED_PAGE) ++ memset(buf, 0xff, size); ++ ++ return irq_status & ecc_err_mask ? -EBADMSG : 0; ++} ++ ++static int denali_pio_write(struct denali_nand_info *denali, ++ const void *buf, size_t size, int page, int raw) ++{ ++ uint32_t addr = DENALI_BANK(denali) | page; ++ const uint32_t *buf32 = (uint32_t *)buf; ++ uint32_t irq_status; ++ int i; ++ ++ denali_reset_irq(denali); ++ ++ iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR); ++ for (i = 0; i < size / 4; i++) ++ iowrite32(*buf32++, denali->host + DENALI_HOST_DATA); ++ ++ irq_status = denali_wait_for_irq(denali, ++ INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL); ++ if (!(irq_status & INTR__PROGRAM_COMP)) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int denali_pio_xfer(struct denali_nand_info *denali, void *buf, ++ size_t size, int page, int raw, int write) ++{ ++ if (write) ++ return denali_pio_write(denali, buf, size, page, raw); ++ else ++ return denali_pio_read(denali, buf, size, page, raw); ++} ++ ++static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, ++ size_t size, int page, int raw, int write) ++{ ++ dma_addr_t dma_addr; ++ uint32_t irq_mask, irq_status, ecc_err_mask; ++ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; ++ int ret = 0; ++ ++ dma_addr = dma_map_single(denali->dev, buf, size, dir); ++ if (dma_mapping_error(denali->dev, dma_addr)) { ++ dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); ++ return denali_pio_xfer(denali, buf, size, page, raw, write); ++ } ++ ++ if (write) { ++ /* ++ * INTR__PROGRAM_COMP is never asserted for the DMA transfer. ++ * We can use INTR__DMA_CMD_COMP instead. This flag is asserted ++ * when the page program is completed. ++ */ ++ irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL; ++ ecc_err_mask = 0; ++ } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) { ++ irq_mask = INTR__DMA_CMD_COMP; ++ ecc_err_mask = INTR__ECC_UNCOR_ERR; ++ } else { ++ irq_mask = INTR__DMA_CMD_COMP; ++ ecc_err_mask = INTR__ECC_ERR; ++ } ++ ++ denali_enable_dma(denali, true); ++ ++ denali_reset_irq(denali); ++ denali_setup_dma(denali, dma_addr, page, write); ++ ++ /* wait for operation to complete */ ++ irq_status = denali_wait_for_irq(denali, irq_mask); ++ if (!(irq_status & INTR__DMA_CMD_COMP)) ++ ret = -EIO; ++ else if (irq_status & ecc_err_mask) ++ ret = -EBADMSG; ++ ++ denali_enable_dma(denali, false); ++ dma_unmap_single(denali->dev, dma_addr, size, dir); ++ ++ if (irq_status & INTR__ERASED_PAGE) ++ memset(buf, 0xff, size); ++ ++ return ret; ++} ++ ++static int denali_data_xfer(struct denali_nand_info *denali, void *buf, ++ size_t size, int page, int raw, int write) ++{ ++ setup_ecc_for_xfer(denali, !raw, raw); ++ ++ if (denali->dma_avail) ++ return denali_dma_xfer(denali, buf, size, page, raw, write); ++ else ++ return denali_pio_xfer(denali, buf, size, page, raw, write); ++} ++ ++static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, ++ int page, int write) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ int writesize = mtd->writesize; ++ int oobsize = mtd->oobsize; ++ uint8_t *bufpoi = chip->oob_poi; ++ int ecc_steps = chip->ecc.steps; ++ int ecc_size = chip->ecc.size; ++ int ecc_bytes = chip->ecc.bytes; ++ int oob_skip = denali->oob_skip_bytes; ++ size_t size = writesize + oobsize; ++ int i, pos, len; ++ ++ /* BBM at the beginning of the OOB area */ ++ if (write) ++ nand_prog_page_begin_op(chip, page, writesize, bufpoi, ++ oob_skip); ++ else ++ nand_read_page_op(chip, page, writesize, bufpoi, oob_skip); ++ bufpoi += oob_skip; ++ ++ /* OOB ECC */ ++ for (i = 0; i < ecc_steps; i++) { ++ pos = ecc_size + i * (ecc_size + ecc_bytes); ++ len = ecc_bytes; ++ ++ if (pos >= writesize) ++ pos += oob_skip; ++ else if (pos + len > writesize) ++ len = writesize - pos; ++ ++ if (write) ++ nand_change_write_column_op(chip, pos, bufpoi, len, ++ false); ++ else ++ nand_change_read_column_op(chip, pos, bufpoi, len, ++ false); ++ bufpoi += len; ++ if (len < ecc_bytes) { ++ len = ecc_bytes - len; ++ if (write) ++ nand_change_write_column_op(chip, writesize + ++ oob_skip, bufpoi, ++ len, false); ++ else ++ nand_change_read_column_op(chip, writesize + ++ oob_skip, bufpoi, ++ len, false); ++ bufpoi += len; ++ } ++ } ++ ++ /* OOB free */ ++ len = oobsize - (bufpoi - chip->oob_poi); ++ if (write) ++ nand_change_write_column_op(chip, size - len, bufpoi, len, ++ false); ++ else ++ nand_change_read_column_op(chip, size - len, bufpoi, len, ++ false); ++} ++ ++static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ int writesize = mtd->writesize; ++ int oobsize = mtd->oobsize; ++ int ecc_steps = chip->ecc.steps; ++ int ecc_size = chip->ecc.size; ++ int ecc_bytes = chip->ecc.bytes; ++ void *dma_buf = denali->buf; ++ int oob_skip = denali->oob_skip_bytes; ++ size_t size = writesize + oobsize; ++ int ret, i, pos, len; ++ ++ ret = denali_data_xfer(denali, dma_buf, size, page, 1, 0); ++ if (ret) ++ return ret; ++ ++ /* Arrange the buffer for syndrome payload/ecc layout */ ++ if (buf) { ++ for (i = 0; i < ecc_steps; i++) { ++ pos = i * (ecc_size + ecc_bytes); ++ len = ecc_size; ++ ++ if (pos >= writesize) ++ pos += oob_skip; ++ else if (pos + len > writesize) ++ len = writesize - pos; ++ ++ memcpy(buf, dma_buf + pos, len); ++ buf += len; ++ if (len < ecc_size) { ++ len = ecc_size - len; ++ memcpy(buf, dma_buf + writesize + oob_skip, ++ len); ++ buf += len; ++ } ++ } ++ } ++ ++ if (oob_required) { ++ uint8_t *oob = chip->oob_poi; ++ ++ /* BBM at the beginning of the OOB area */ ++ memcpy(oob, dma_buf + writesize, oob_skip); ++ oob += oob_skip; ++ ++ /* OOB ECC */ ++ for (i = 0; i < ecc_steps; i++) { ++ pos = ecc_size + i * (ecc_size + ecc_bytes); ++ len = ecc_bytes; ++ ++ if (pos >= writesize) ++ pos += oob_skip; ++ else if (pos + len > writesize) ++ len = writesize - pos; ++ ++ memcpy(oob, dma_buf + pos, len); ++ oob += len; ++ if (len < ecc_bytes) { ++ len = ecc_bytes - len; ++ memcpy(oob, dma_buf + writesize + oob_skip, ++ len); ++ oob += len; ++ } ++ } ++ ++ /* OOB free */ ++ len = oobsize - (oob - chip->oob_poi); ++ memcpy(oob, dma_buf + size - len, len); ++ } ++ ++ return 0; ++} ++ ++static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ denali_oob_xfer(mtd, chip, page, 0); ++ ++ return 0; ++} ++ ++static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ ++ denali_reset_irq(denali); ++ ++ denali_oob_xfer(mtd, chip, page, 1); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ unsigned long uncor_ecc_flags = 0; ++ int stat = 0; ++ int ret; ++ ++ ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0); ++ if (ret && ret != -EBADMSG) ++ return ret; ++ ++ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) ++ stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags); ++ else if (ret == -EBADMSG) ++ stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf); ++ ++ if (stat < 0) ++ return stat; ++ ++ if (uncor_ecc_flags) { ++ ret = denali_read_oob(mtd, chip, page); ++ if (ret) ++ return ret; ++ ++ stat = denali_check_erased_page(mtd, chip, buf, ++ uncor_ecc_flags, stat); ++ } ++ ++ return stat; ++} ++ ++static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ int writesize = mtd->writesize; ++ int oobsize = mtd->oobsize; ++ int ecc_steps = chip->ecc.steps; ++ int ecc_size = chip->ecc.size; ++ int ecc_bytes = chip->ecc.bytes; ++ void *dma_buf = denali->buf; ++ int oob_skip = denali->oob_skip_bytes; ++ size_t size = writesize + oobsize; ++ int i, pos, len; ++ ++ /* ++ * Fill the buffer with 0xff first except the full page transfer. ++ * This simplifies the logic. ++ */ ++ if (!buf || !oob_required) ++ memset(dma_buf, 0xff, size); ++ ++ /* Arrange the buffer for syndrome payload/ecc layout */ ++ if (buf) { ++ for (i = 0; i < ecc_steps; i++) { ++ pos = i * (ecc_size + ecc_bytes); ++ len = ecc_size; ++ ++ if (pos >= writesize) ++ pos += oob_skip; ++ else if (pos + len > writesize) ++ len = writesize - pos; ++ ++ memcpy(dma_buf + pos, buf, len); ++ buf += len; ++ if (len < ecc_size) { ++ len = ecc_size - len; ++ memcpy(dma_buf + writesize + oob_skip, buf, ++ len); ++ buf += len; ++ } ++ } ++ } ++ ++ if (oob_required) { ++ const uint8_t *oob = chip->oob_poi; ++ ++ /* BBM at the beginning of the OOB area */ ++ memcpy(dma_buf + writesize, oob, oob_skip); ++ oob += oob_skip; ++ ++ /* OOB ECC */ ++ for (i = 0; i < ecc_steps; i++) { ++ pos = ecc_size + i * (ecc_size + ecc_bytes); ++ len = ecc_bytes; ++ ++ if (pos >= writesize) ++ pos += oob_skip; ++ else if (pos + len > writesize) ++ len = writesize - pos; ++ ++ memcpy(dma_buf + pos, oob, len); ++ oob += len; ++ if (len < ecc_bytes) { ++ len = ecc_bytes - len; ++ memcpy(dma_buf + writesize + oob_skip, oob, ++ len); ++ oob += len; ++ } ++ } ++ ++ /* OOB free */ ++ len = oobsize - (oob - chip->oob_poi); ++ memcpy(dma_buf + size - len, oob, len); ++ } ++ ++ return denali_data_xfer(denali, dma_buf, size, page, 1, 1); ++} ++ ++static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ ++ return denali_data_xfer(denali, (void *)buf, mtd->writesize, ++ page, 0, 1); ++} ++ ++static void denali_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ ++ denali->active_bank = chip; ++} ++ ++static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ uint32_t irq_status; ++ ++ /* R/B# pin transitioned from low to high? */ ++ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT); ++ ++ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; ++} ++ ++static int denali_erase(struct mtd_info *mtd, int page) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ uint32_t irq_status; ++ ++ denali_reset_irq(denali); ++ ++ denali_host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, ++ DENALI_ERASE); ++ ++ /* wait for erase to complete or failure to occur */ ++ irq_status = denali_wait_for_irq(denali, ++ INTR__ERASE_COMP | INTR__ERASE_FAIL); ++ ++ return irq_status & INTR__ERASE_COMP ? 0 : -EIO; ++} ++ ++static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, ++ const struct nand_data_interface *conf) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ const struct nand_sdr_timings *timings; ++ unsigned long t_clk; ++ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; ++ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; ++ int addr_2_data_mask; ++ uint32_t tmp; ++ ++ timings = nand_get_sdr_timings(conf); ++ if (IS_ERR(timings)) ++ return PTR_ERR(timings); ++ ++ /* clk_x period in picoseconds */ ++ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); ++ if (!t_clk) ++ return -EINVAL; ++ ++ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ /* tREA -> ACC_CLKS */ ++ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk); ++ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); ++ ++ tmp = ioread32(denali->reg + ACC_CLKS); ++ tmp &= ~ACC_CLKS__VALUE; ++ tmp |= acc_clks; ++ iowrite32(tmp, denali->reg + ACC_CLKS); ++ ++ /* tRWH -> RE_2_WE */ ++ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk); ++ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); ++ ++ tmp = ioread32(denali->reg + RE_2_WE); ++ tmp &= ~RE_2_WE__VALUE; ++ tmp |= re_2_we; ++ iowrite32(tmp, denali->reg + RE_2_WE); ++ ++ /* tRHZ -> RE_2_RE */ ++ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk); ++ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); ++ ++ tmp = ioread32(denali->reg + RE_2_RE); ++ tmp &= ~RE_2_RE__VALUE; ++ tmp |= re_2_re; ++ iowrite32(tmp, denali->reg + RE_2_RE); ++ ++ /* tWHR -> WE_2_RE */ ++ we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk); ++ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); ++ ++ tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); ++ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; ++ tmp |= we_2_re; ++ iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE); ++ ++ /* tADL -> ADDR_2_DATA */ ++ ++ /* for older versions, ADDR_2_DATA is only 6 bit wide */ ++ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; ++ if (denali->revision < 0x0501) ++ addr_2_data_mask >>= 1; ++ ++ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk); ++ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); ++ ++ tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); ++ tmp &= ~addr_2_data_mask; ++ tmp |= addr_2_data; ++ iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA); ++ ++ /* tREH, tWH -> RDWR_EN_HI_CNT */ ++ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), ++ t_clk); ++ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); ++ ++ tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); ++ tmp &= ~RDWR_EN_HI_CNT__VALUE; ++ tmp |= rdwr_en_hi; ++ iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); ++ ++ /* tRP, tWP -> RDWR_EN_LO_CNT */ ++ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), ++ t_clk); ++ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), ++ t_clk); ++ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); ++ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); ++ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); ++ ++ tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); ++ tmp &= ~RDWR_EN_LO_CNT__VALUE; ++ tmp |= rdwr_en_lo; ++ iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); ++ ++ /* tCS, tCEA -> CS_SETUP_CNT */ ++ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, ++ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, ++ 0); ++ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); ++ ++ tmp = ioread32(denali->reg + CS_SETUP_CNT); ++ tmp &= ~CS_SETUP_CNT__VALUE; ++ tmp |= cs_setup; ++ iowrite32(tmp, denali->reg + CS_SETUP_CNT); ++ ++ return 0; ++} ++ ++static void denali_reset_banks(struct denali_nand_info *denali) ++{ ++ u32 irq_status; ++ int i; ++ ++ for (i = 0; i < denali->max_banks; i++) { ++ denali->active_bank = i; ++ ++ denali_reset_irq(denali); ++ ++ iowrite32(DEVICE_RESET__BANK(i), ++ denali->reg + DEVICE_RESET); ++ ++ irq_status = denali_wait_for_irq(denali, ++ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT); ++ if (!(irq_status & INTR__INT_ACT)) ++ break; ++ } ++ ++ dev_dbg(denali->dev, "%d chips connected\n", i); ++ denali->max_banks = i; ++} ++ ++static void denali_hw_init(struct denali_nand_info *denali) ++{ ++ /* ++ * The REVISION register may not be reliable. Platforms are allowed to ++ * override it. ++ */ ++ if (!denali->revision) ++ denali->revision = swab16(ioread32(denali->reg + REVISION)); ++ ++ /* ++ * tell driver how many bit controller will skip before ++ * writing ECC code in OOB, this register may be already ++ * set by firmware. So we read this value out. ++ * if this value is 0, just let it be. ++ */ ++ denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES); ++ detect_max_banks(denali); ++ iowrite32(0x0F, denali->reg + RB_PIN_ENABLED); ++ iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); ++ ++ iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); ++ ++ /* Should set value for these registers when init */ ++ iowrite32(0, denali->reg + TWO_ROW_ADDR_CYCLES); ++ iowrite32(1, denali->reg + ECC_ENABLE); ++} ++ ++int denali_calc_ecc_bytes(int step_size, int strength) ++{ ++ /* BCH code. Denali requires ecc.bytes to be multiple of 2 */ ++ return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2; ++} ++EXPORT_SYMBOL(denali_calc_ecc_bytes); ++ ++static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, ++ struct denali_nand_info *denali) ++{ ++ int oobavail = mtd->oobsize - denali->oob_skip_bytes; ++ int ret; ++ ++ /* ++ * If .size and .strength are already set (usually by DT), ++ * check if they are supported by this controller. ++ */ ++ if (chip->ecc.size && chip->ecc.strength) ++ return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); ++ ++ /* ++ * We want .size and .strength closest to the chip's requirement ++ * unless NAND_ECC_MAXIMIZE is requested. ++ */ ++ if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { ++ ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); ++ if (!ret) ++ return 0; ++ } ++ ++ /* Max ECC strength is the last thing we can do */ ++ return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); ++} ++ ++static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = denali->oob_skip_bytes; ++ oobregion->length = chip->ecc.total; ++ ++ return 0; ++} ++ ++static int denali_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct denali_nand_info *denali = mtd_to_denali(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = chip->ecc.total + denali->oob_skip_bytes; ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops denali_ooblayout_ops = { ++ .ecc = denali_ooblayout_ecc, ++ .free = denali_ooblayout_free, ++}; ++ ++/* initialize driver data structures */ ++static void denali_drv_init(struct denali_nand_info *denali) ++{ ++ /* ++ * the completion object will be used to notify ++ * the callee that the interrupt is done ++ */ ++ init_completion(&denali->complete); ++ ++ /* ++ * the spinlock will be used to synchronize the ISR with any ++ * element that might be access shared data (interrupt status) ++ */ ++ spin_lock_init(&denali->irq_lock); ++} ++ ++static int denali_multidev_fixup(struct denali_nand_info *denali) ++{ ++ struct nand_chip *chip = &denali->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ /* ++ * Support for multi device: ++ * When the IP configuration is x16 capable and two x8 chips are ++ * connected in parallel, DEVICES_CONNECTED should be set to 2. ++ * In this case, the core framework knows nothing about this fact, ++ * so we should tell it the _logical_ pagesize and anything necessary. ++ */ ++ denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED); ++ ++ /* ++ * On some SoCs, DEVICES_CONNECTED is not auto-detected. ++ * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case. ++ */ ++ if (denali->devs_per_cs == 0) { ++ denali->devs_per_cs = 1; ++ iowrite32(1, denali->reg + DEVICES_CONNECTED); ++ } ++ ++ if (denali->devs_per_cs == 1) ++ return 0; ++ ++ if (denali->devs_per_cs != 2) { ++ dev_err(denali->dev, "unsupported number of devices %d\n", ++ denali->devs_per_cs); ++ return -EINVAL; ++ } ++ ++ /* 2 chips in parallel */ ++ mtd->size <<= 1; ++ mtd->erasesize <<= 1; ++ mtd->writesize <<= 1; ++ mtd->oobsize <<= 1; ++ chip->chipsize <<= 1; ++ chip->page_shift += 1; ++ chip->phys_erase_shift += 1; ++ chip->bbt_erase_shift += 1; ++ chip->chip_shift += 1; ++ chip->pagemask <<= 1; ++ chip->ecc.size <<= 1; ++ chip->ecc.bytes <<= 1; ++ chip->ecc.strength <<= 1; ++ denali->oob_skip_bytes <<= 1; ++ ++ return 0; ++} ++ ++int denali_init(struct denali_nand_info *denali) ++{ ++ struct nand_chip *chip = &denali->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ mtd->dev.parent = denali->dev; ++ denali_hw_init(denali); ++ denali_drv_init(denali); ++ ++ denali_clear_irq_all(denali); ++ ++ /* Request IRQ after all the hardware initialization is finished */ ++ ret = devm_request_irq(denali->dev, denali->irq, denali_isr, ++ IRQF_SHARED, DENALI_NAND_NAME, denali); ++ if (ret) { ++ dev_err(denali->dev, "Unable to request IRQ\n"); ++ return ret; ++ } ++ ++ denali_enable_irq(denali); ++ denali_reset_banks(denali); ++ ++ denali->active_bank = DENALI_INVALID_BANK; ++ ++ nand_set_flash_node(chip, denali->dev->of_node); ++ /* Fallback to the default name if DT did not give "label" property */ ++ if (!mtd->name) ++ mtd->name = "denali-nand"; ++ ++ /* register the driver with the NAND core subsystem */ ++ chip->select_chip = denali_select_chip; ++ chip->read_byte = denali_read_byte; ++ chip->write_byte = denali_write_byte; ++ chip->read_word = denali_read_word; ++ chip->cmd_ctrl = denali_cmd_ctrl; ++ chip->dev_ready = denali_dev_ready; ++ chip->waitfunc = denali_waitfunc; ++ ++ /* clk rate info is needed for setup_data_interface */ ++ if (denali->clk_x_rate) ++ chip->setup_data_interface = denali_setup_data_interface; ++ ++ /* ++ * scan for NAND devices attached to the controller ++ * this is the first stage in a two step process to register ++ * with the nand subsystem ++ */ ++ ret = nand_scan_ident(mtd, denali->max_banks, NULL); ++ if (ret) ++ goto disable_irq; ++ ++ if (ioread32(denali->reg + FEATURES) & FEATURES__DMA) ++ denali->dma_avail = 1; ++ ++ if (denali->dma_avail) { ++ int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32; ++ ++ ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit)); ++ if (ret) { ++ dev_info(denali->dev, ++ "Failed to set DMA mask. Disabling DMA.\n"); ++ denali->dma_avail = 0; ++ } ++ } ++ ++ if (denali->dma_avail) { ++ chip->options |= NAND_USE_BOUNCE_BUFFER; ++ chip->buf_align = 16; ++ } ++ ++ /* ++ * second stage of the NAND scan ++ * this stage requires information regarding ECC and ++ * bad block management. ++ */ ++ ++ chip->bbt_options |= NAND_BBT_USE_FLASH; ++ chip->bbt_options |= NAND_BBT_NO_OOB; ++ ++ chip->ecc.mode = NAND_ECC_HW_SYNDROME; ++ ++ /* no subpage writes on denali */ ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ ret = denali_ecc_setup(mtd, chip, denali); ++ if (ret) { ++ dev_err(denali->dev, "Failed to setup ECC settings.\n"); ++ goto disable_irq; ++ } ++ ++ dev_dbg(denali->dev, ++ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", ++ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); ++ ++ iowrite32(MAKE_ECC_CORRECTION(chip->ecc.strength, 1), ++ denali->reg + ECC_CORRECTION); ++ iowrite32(mtd->erasesize / mtd->writesize, ++ denali->reg + PAGES_PER_BLOCK); ++ iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, ++ denali->reg + DEVICE_WIDTH); ++ iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); ++ iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); ++ ++ iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE); ++ iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE); ++ /* chip->ecc.steps is set by nand_scan_tail(); not available here */ ++ iowrite32(mtd->writesize / chip->ecc.size, ++ denali->reg + CFG_NUM_DATA_BLOCKS); ++ ++ mtd_set_ooblayout(mtd, &denali_ooblayout_ops); ++ ++ if (chip->options & NAND_BUSWIDTH_16) { ++ chip->read_buf = denali_read_buf16; ++ chip->write_buf = denali_write_buf16; ++ } else { ++ chip->read_buf = denali_read_buf; ++ chip->write_buf = denali_write_buf; ++ } ++ chip->ecc.read_page = denali_read_page; ++ chip->ecc.read_page_raw = denali_read_page_raw; ++ chip->ecc.write_page = denali_write_page; ++ chip->ecc.write_page_raw = denali_write_page_raw; ++ chip->ecc.read_oob = denali_read_oob; ++ chip->ecc.write_oob = denali_write_oob; ++ chip->erase = denali_erase; ++ ++ ret = denali_multidev_fixup(denali); ++ if (ret) ++ goto disable_irq; ++ ++ /* ++ * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not ++ * use devm_kmalloc() because the memory allocated by devm_ does not ++ * guarantee DMA-safe alignment. ++ */ ++ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); ++ if (!denali->buf) { ++ ret = -ENOMEM; ++ goto disable_irq; ++ } ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ goto free_buf; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(denali->dev, "Failed to register MTD: %d\n", ret); ++ goto free_buf; ++ } ++ return 0; ++ ++free_buf: ++ kfree(denali->buf); ++disable_irq: ++ denali_disable_irq(denali); ++ ++ return ret; ++} ++EXPORT_SYMBOL(denali_init); ++ ++/* driver exit point */ ++void denali_remove(struct denali_nand_info *denali) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&denali->nand); ++ ++ nand_release(mtd); ++ kfree(denali->buf); ++ denali_disable_irq(denali); ++} ++EXPORT_SYMBOL(denali_remove); +diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h +new file mode 100644 +index 00000000..9239e67 +--- /dev/null ++++ b/drivers/mtd/nand/raw/denali.h +@@ -0,0 +1,339 @@ ++/* ++ * NAND Flash Controller Device Driver ++ * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#ifndef __DENALI_H__ ++#define __DENALI_H__ ++ ++#include ++#include ++ ++#define DEVICE_RESET 0x0 ++#define DEVICE_RESET__BANK(bank) BIT(bank) ++ ++#define TRANSFER_SPARE_REG 0x10 ++#define TRANSFER_SPARE_REG__FLAG BIT(0) ++ ++#define LOAD_WAIT_CNT 0x20 ++#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0) ++ ++#define PROGRAM_WAIT_CNT 0x30 ++#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0) ++ ++#define ERASE_WAIT_CNT 0x40 ++#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0) ++ ++#define INT_MON_CYCCNT 0x50 ++#define INT_MON_CYCCNT__VALUE GENMASK(15, 0) ++ ++#define RB_PIN_ENABLED 0x60 ++#define RB_PIN_ENABLED__BANK(bank) BIT(bank) ++ ++#define MULTIPLANE_OPERATION 0x70 ++#define MULTIPLANE_OPERATION__FLAG BIT(0) ++ ++#define MULTIPLANE_READ_ENABLE 0x80 ++#define MULTIPLANE_READ_ENABLE__FLAG BIT(0) ++ ++#define COPYBACK_DISABLE 0x90 ++#define COPYBACK_DISABLE__FLAG BIT(0) ++ ++#define CACHE_WRITE_ENABLE 0xa0 ++#define CACHE_WRITE_ENABLE__FLAG BIT(0) ++ ++#define CACHE_READ_ENABLE 0xb0 ++#define CACHE_READ_ENABLE__FLAG BIT(0) ++ ++#define PREFETCH_MODE 0xc0 ++#define PREFETCH_MODE__PREFETCH_EN BIT(0) ++#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4) ++ ++#define CHIP_ENABLE_DONT_CARE 0xd0 ++#define CHIP_EN_DONT_CARE__FLAG BIT(0) ++ ++#define ECC_ENABLE 0xe0 ++#define ECC_ENABLE__FLAG BIT(0) ++ ++#define GLOBAL_INT_ENABLE 0xf0 ++#define GLOBAL_INT_EN_FLAG BIT(0) ++ ++#define TWHR2_AND_WE_2_RE 0x100 ++#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0) ++#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8) ++ ++#define TCWAW_AND_ADDR_2_DATA 0x110 ++/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */ ++#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0) ++#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8) ++ ++#define RE_2_WE 0x120 ++#define RE_2_WE__VALUE GENMASK(5, 0) ++ ++#define ACC_CLKS 0x130 ++#define ACC_CLKS__VALUE GENMASK(3, 0) ++ ++#define NUMBER_OF_PLANES 0x140 ++#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0) ++ ++#define PAGES_PER_BLOCK 0x150 ++#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0) ++ ++#define DEVICE_WIDTH 0x160 ++#define DEVICE_WIDTH__VALUE GENMASK(1, 0) ++ ++#define DEVICE_MAIN_AREA_SIZE 0x170 ++#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0) ++ ++#define DEVICE_SPARE_AREA_SIZE 0x180 ++#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0) ++ ++#define TWO_ROW_ADDR_CYCLES 0x190 ++#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0) ++ ++#define MULTIPLANE_ADDR_RESTRICT 0x1a0 ++#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0) ++ ++#define ECC_CORRECTION 0x1b0 ++#define ECC_CORRECTION__VALUE GENMASK(4, 0) ++#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16) ++#define MAKE_ECC_CORRECTION(val, thresh) \ ++ (((val) & (ECC_CORRECTION__VALUE)) | \ ++ (((thresh) << 16) & (ECC_CORRECTION__ERASE_THRESHOLD))) ++ ++#define READ_MODE 0x1c0 ++#define READ_MODE__VALUE GENMASK(3, 0) ++ ++#define WRITE_MODE 0x1d0 ++#define WRITE_MODE__VALUE GENMASK(3, 0) ++ ++#define COPYBACK_MODE 0x1e0 ++#define COPYBACK_MODE__VALUE GENMASK(3, 0) ++ ++#define RDWR_EN_LO_CNT 0x1f0 ++#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0) ++ ++#define RDWR_EN_HI_CNT 0x200 ++#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0) ++ ++#define MAX_RD_DELAY 0x210 ++#define MAX_RD_DELAY__VALUE GENMASK(3, 0) ++ ++#define CS_SETUP_CNT 0x220 ++#define CS_SETUP_CNT__VALUE GENMASK(4, 0) ++#define CS_SETUP_CNT__TWB GENMASK(17, 12) ++ ++#define SPARE_AREA_SKIP_BYTES 0x230 ++#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0) ++ ++#define SPARE_AREA_MARKER 0x240 ++#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0) ++ ++#define DEVICES_CONNECTED 0x250 ++#define DEVICES_CONNECTED__VALUE GENMASK(2, 0) ++ ++#define DIE_MASK 0x260 ++#define DIE_MASK__VALUE GENMASK(7, 0) ++ ++#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 ++#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0) ++ ++#define WRITE_PROTECT 0x280 ++#define WRITE_PROTECT__FLAG BIT(0) ++ ++#define RE_2_RE 0x290 ++#define RE_2_RE__VALUE GENMASK(5, 0) ++ ++#define MANUFACTURER_ID 0x300 ++#define MANUFACTURER_ID__VALUE GENMASK(7, 0) ++ ++#define DEVICE_ID 0x310 ++#define DEVICE_ID__VALUE GENMASK(7, 0) ++ ++#define DEVICE_PARAM_0 0x320 ++#define DEVICE_PARAM_0__VALUE GENMASK(7, 0) ++ ++#define DEVICE_PARAM_1 0x330 ++#define DEVICE_PARAM_1__VALUE GENMASK(7, 0) ++ ++#define DEVICE_PARAM_2 0x340 ++#define DEVICE_PARAM_2__VALUE GENMASK(7, 0) ++ ++#define LOGICAL_PAGE_DATA_SIZE 0x350 ++#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0) ++ ++#define LOGICAL_PAGE_SPARE_SIZE 0x360 ++#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0) ++ ++#define REVISION 0x370 ++#define REVISION__VALUE GENMASK(15, 0) ++ ++#define ONFI_DEVICE_FEATURES 0x380 ++#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0) ++ ++#define ONFI_OPTIONAL_COMMANDS 0x390 ++#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0) ++ ++#define ONFI_TIMING_MODE 0x3a0 ++#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0) ++ ++#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 ++#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0) ++ ++#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 ++#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0) ++#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8) ++ ++#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 ++#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0) ++ ++#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 ++#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0) ++ ++#define FEATURES 0x3f0 ++#define FEATURES__N_BANKS GENMASK(1, 0) ++#define FEATURES__ECC_MAX_ERR GENMASK(5, 2) ++#define FEATURES__DMA BIT(6) ++#define FEATURES__CMD_DMA BIT(7) ++#define FEATURES__PARTITION BIT(8) ++#define FEATURES__XDMA_SIDEBAND BIT(9) ++#define FEATURES__GPREG BIT(10) ++#define FEATURES__INDEX_ADDR BIT(11) ++ ++#define TRANSFER_MODE 0x400 ++#define TRANSFER_MODE__VALUE GENMASK(1, 0) ++ ++#define INTR_STATUS(bank) (0x410 + (bank) * 0x50) ++#define INTR_EN(bank) (0x420 + (bank) * 0x50) ++/* bit[1:0] is used differently depending on IP version */ ++#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */ ++#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */ ++#define INTR__ECC_ERR BIT(1) /* old IP */ ++#define INTR__DMA_CMD_COMP BIT(2) ++#define INTR__TIME_OUT BIT(3) ++#define INTR__PROGRAM_FAIL BIT(4) ++#define INTR__ERASE_FAIL BIT(5) ++#define INTR__LOAD_COMP BIT(6) ++#define INTR__PROGRAM_COMP BIT(7) ++#define INTR__ERASE_COMP BIT(8) ++#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9) ++#define INTR__LOCKED_BLK BIT(10) ++#define INTR__UNSUP_CMD BIT(11) ++#define INTR__INT_ACT BIT(12) ++#define INTR__RST_COMP BIT(13) ++#define INTR__PIPE_CMD_ERR BIT(14) ++#define INTR__PAGE_XFER_INC BIT(15) ++#define INTR__ERASED_PAGE BIT(16) ++ ++#define PAGE_CNT(bank) (0x430 + (bank) * 0x50) ++#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50) ++#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50) ++ ++#define ECC_THRESHOLD 0x600 ++#define ECC_THRESHOLD__VALUE GENMASK(9, 0) ++ ++#define ECC_ERROR_BLOCK_ADDRESS 0x610 ++#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0) ++ ++#define ECC_ERROR_PAGE_ADDRESS 0x620 ++#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0) ++#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12) ++ ++#define ECC_ERROR_ADDRESS 0x630 ++#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0) ++#define ECC_ERROR_ADDRESS__SECTOR_NR GENMASK(15, 12) ++ ++#define ERR_CORRECTION_INFO 0x640 ++#define ERR_CORRECTION_INFO__BYTEMASK GENMASK(7, 0) ++#define ERR_CORRECTION_INFO__DEVICE_NR GENMASK(11, 8) ++#define ERR_CORRECTION_INFO__ERROR_TYPE BIT(14) ++#define ERR_CORRECTION_INFO__LAST_ERR_INFO BIT(15) ++ ++#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10) ++#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8) ++#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0) ++#define ECC_COR_INFO__UNCOR_ERR BIT(7) ++ ++#define CFG_DATA_BLOCK_SIZE 0x6b0 ++ ++#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0 ++ ++#define CFG_NUM_DATA_BLOCKS 0x6d0 ++ ++#define CFG_META_DATA_SIZE 0x6e0 ++ ++#define DMA_ENABLE 0x700 ++#define DMA_ENABLE__FLAG BIT(0) ++ ++#define IGNORE_ECC_DONE 0x710 ++#define IGNORE_ECC_DONE__FLAG BIT(0) ++ ++#define DMA_INTR 0x720 ++#define DMA_INTR_EN 0x730 ++#define DMA_INTR__TARGET_ERROR BIT(0) ++#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1) ++#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2) ++#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3) ++#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4) ++#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5) ++ ++#define TARGET_ERR_ADDR_LO 0x740 ++#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0) ++ ++#define TARGET_ERR_ADDR_HI 0x750 ++#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0) ++ ++#define CHNL_ACTIVE 0x760 ++#define CHNL_ACTIVE__CHANNEL0 BIT(0) ++#define CHNL_ACTIVE__CHANNEL1 BIT(1) ++#define CHNL_ACTIVE__CHANNEL2 BIT(2) ++#define CHNL_ACTIVE__CHANNEL3 BIT(3) ++ ++struct denali_nand_info { ++ struct nand_chip nand; ++ unsigned long clk_x_rate; /* bus interface clock rate */ ++ int active_bank; /* currently selected bank */ ++ struct device *dev; ++ void __iomem *reg; /* Register Interface */ ++ void __iomem *host; /* Host Data/Command Interface */ ++ ++ /* elements used by ISR */ ++ struct completion complete; ++ spinlock_t irq_lock; ++ uint32_t irq_mask; ++ uint32_t irq_status; ++ int irq; ++ ++ void *buf; ++ dma_addr_t dma_addr; ++ int dma_avail; ++ int devs_per_cs; /* devices connected in parallel */ ++ int oob_skip_bytes; ++ int max_banks; ++ unsigned int revision; ++ unsigned int caps; ++ const struct nand_ecc_caps *ecc_caps; ++}; ++ ++#define DENALI_CAP_HW_ECC_FIXUP BIT(0) ++#define DENALI_CAP_DMA_64BIT BIT(1) ++ ++int denali_calc_ecc_bytes(int step_size, int strength); ++extern int denali_init(struct denali_nand_info *denali); ++extern void denali_remove(struct denali_nand_info *denali); ++ ++#endif /* __DENALI_H__ */ +diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c +new file mode 100644 +index 00000000..3f4f4ae +--- /dev/null ++++ b/drivers/mtd/nand/raw/denali_dt.c +@@ -0,0 +1,167 @@ ++/* ++ * NAND Flash Controller Device Driver for DT ++ * ++ * Copyright © 2011, Picochip. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "denali.h" ++ ++struct denali_dt { ++ struct denali_nand_info denali; ++ struct clk *clk; ++}; ++ ++struct denali_dt_data { ++ unsigned int revision; ++ unsigned int caps; ++ const struct nand_ecc_caps *ecc_caps; ++}; ++ ++NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes, ++ 512, 8, 15); ++static const struct denali_dt_data denali_socfpga_data = { ++ .caps = DENALI_CAP_HW_ECC_FIXUP, ++ .ecc_caps = &denali_socfpga_ecc_caps, ++}; ++ ++NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes, ++ 1024, 8, 16, 24); ++static const struct denali_dt_data denali_uniphier_v5a_data = { ++ .caps = DENALI_CAP_HW_ECC_FIXUP | ++ DENALI_CAP_DMA_64BIT, ++ .ecc_caps = &denali_uniphier_v5a_ecc_caps, ++}; ++ ++NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes, ++ 1024, 8, 16); ++static const struct denali_dt_data denali_uniphier_v5b_data = { ++ .revision = 0x0501, ++ .caps = DENALI_CAP_HW_ECC_FIXUP | ++ DENALI_CAP_DMA_64BIT, ++ .ecc_caps = &denali_uniphier_v5b_ecc_caps, ++}; ++ ++static const struct of_device_id denali_nand_dt_ids[] = { ++ { ++ .compatible = "altr,socfpga-denali-nand", ++ .data = &denali_socfpga_data, ++ }, ++ { ++ .compatible = "socionext,uniphier-denali-nand-v5a", ++ .data = &denali_uniphier_v5a_data, ++ }, ++ { ++ .compatible = "socionext,uniphier-denali-nand-v5b", ++ .data = &denali_uniphier_v5b_data, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); ++ ++static int denali_dt_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct denali_dt *dt; ++ const struct denali_dt_data *data; ++ struct denali_nand_info *denali; ++ int ret; ++ ++ dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL); ++ if (!dt) ++ return -ENOMEM; ++ denali = &dt->denali; ++ ++ data = of_device_get_match_data(&pdev->dev); ++ if (data) { ++ denali->revision = data->revision; ++ denali->caps = data->caps; ++ denali->ecc_caps = data->ecc_caps; ++ } ++ ++ denali->dev = &pdev->dev; ++ denali->irq = platform_get_irq(pdev, 0); ++ if (denali->irq < 0) { ++ dev_err(&pdev->dev, "no irq defined\n"); ++ return denali->irq; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg"); ++ denali->reg = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(denali->reg)) ++ return PTR_ERR(denali->reg); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); ++ denali->host = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(denali->host)) ++ return PTR_ERR(denali->host); ++ ++ dt->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(dt->clk)) { ++ dev_err(&pdev->dev, "no clk available\n"); ++ return PTR_ERR(dt->clk); ++ } ++ ret = clk_prepare_enable(dt->clk); ++ if (ret) ++ return ret; ++ ++ /* ++ * Hardcode the clock rate for the backward compatibility. ++ * This works for both SOCFPGA and UniPhier. ++ */ ++ denali->clk_x_rate = 200000000; ++ ++ ret = denali_init(denali); ++ if (ret) ++ goto out_disable_clk; ++ ++ platform_set_drvdata(pdev, dt); ++ return 0; ++ ++out_disable_clk: ++ clk_disable_unprepare(dt->clk); ++ ++ return ret; ++} ++ ++static int denali_dt_remove(struct platform_device *pdev) ++{ ++ struct denali_dt *dt = platform_get_drvdata(pdev); ++ ++ denali_remove(&dt->denali); ++ clk_disable_unprepare(dt->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver denali_dt_driver = { ++ .probe = denali_dt_probe, ++ .remove = denali_dt_remove, ++ .driver = { ++ .name = "denali-nand-dt", ++ .of_match_table = denali_nand_dt_ids, ++ }, ++}; ++ ++module_platform_driver(denali_dt_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jamie Iles"); ++MODULE_DESCRIPTION("DT driver for Denali NAND controller"); +diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c +new file mode 100644 +index 00000000..7ad0db6 +--- /dev/null ++++ b/drivers/mtd/nand/raw/denali_pci.c +@@ -0,0 +1,130 @@ ++/* ++ * NAND Flash Controller Device Driver ++ * Copyright © 2009-2010, Intel Corporation and its suppliers. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++#include ++#include ++#include ++ ++#include "denali.h" ++ ++#define DENALI_NAND_NAME "denali-nand-pci" ++ ++#define INTEL_CE4100 1 ++#define INTEL_MRST 2 ++ ++/* List of platforms this NAND controller has be integrated into */ ++static const struct pci_device_id denali_pci_ids[] = { ++ { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, ++ { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, ++ { /* end: all zeroes */ } ++}; ++MODULE_DEVICE_TABLE(pci, denali_pci_ids); ++ ++NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15); ++ ++static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ int ret; ++ resource_size_t csr_base, mem_base; ++ unsigned long csr_len, mem_len; ++ struct denali_nand_info *denali; ++ ++ denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL); ++ if (!denali) ++ return -ENOMEM; ++ ++ ret = pcim_enable_device(dev); ++ if (ret) { ++ dev_err(&dev->dev, "Spectra: pci_enable_device failed.\n"); ++ return ret; ++ } ++ ++ if (id->driver_data == INTEL_CE4100) { ++ mem_base = pci_resource_start(dev, 0); ++ mem_len = pci_resource_len(dev, 1); ++ csr_base = pci_resource_start(dev, 1); ++ csr_len = pci_resource_len(dev, 1); ++ } else { ++ csr_base = pci_resource_start(dev, 0); ++ csr_len = pci_resource_len(dev, 0); ++ mem_base = pci_resource_start(dev, 1); ++ mem_len = pci_resource_len(dev, 1); ++ if (!mem_len) { ++ mem_base = csr_base + csr_len; ++ mem_len = csr_len; ++ } ++ } ++ ++ pci_set_master(dev); ++ denali->dev = &dev->dev; ++ denali->irq = dev->irq; ++ denali->ecc_caps = &denali_pci_ecc_caps; ++ denali->nand.ecc.options |= NAND_ECC_MAXIMIZE; ++ denali->clk_x_rate = 200000000; /* 200 MHz */ ++ ++ ret = pci_request_regions(dev, DENALI_NAND_NAME); ++ if (ret) { ++ dev_err(&dev->dev, "Spectra: Unable to request memory regions\n"); ++ return ret; ++ } ++ ++ denali->reg = ioremap_nocache(csr_base, csr_len); ++ if (!denali->reg) { ++ dev_err(&dev->dev, "Spectra: Unable to remap memory region\n"); ++ return -ENOMEM; ++ } ++ ++ denali->host = ioremap_nocache(mem_base, mem_len); ++ if (!denali->host) { ++ dev_err(&dev->dev, "Spectra: ioremap_nocache failed!"); ++ ret = -ENOMEM; ++ goto failed_remap_reg; ++ } ++ ++ ret = denali_init(denali); ++ if (ret) ++ goto failed_remap_mem; ++ ++ pci_set_drvdata(dev, denali); ++ ++ return 0; ++ ++failed_remap_mem: ++ iounmap(denali->host); ++failed_remap_reg: ++ iounmap(denali->reg); ++ return ret; ++} ++ ++/* driver exit point */ ++static void denali_pci_remove(struct pci_dev *dev) ++{ ++ struct denali_nand_info *denali = pci_get_drvdata(dev); ++ ++ denali_remove(denali); ++ iounmap(denali->reg); ++ iounmap(denali->host); ++} ++ ++static struct pci_driver denali_pci_driver = { ++ .name = DENALI_NAND_NAME, ++ .id_table = denali_pci_ids, ++ .probe = denali_pci_probe, ++ .remove = denali_pci_remove, ++}; ++ ++module_pci_driver(denali_pci_driver); ++ ++MODULE_DESCRIPTION("PCI driver for Denali NAND controller"); ++MODULE_AUTHOR("Intel Corporation and its suppliers"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c +new file mode 100644 +index 00000000..6bc93ea +--- /dev/null ++++ b/drivers/mtd/nand/raw/diskonchip.c +@@ -0,0 +1,1711 @@ ++/* ++ * drivers/mtd/nand/diskonchip.c ++ * ++ * (C) 2003 Red Hat, Inc. ++ * (C) 2004 Dan Brown ++ * (C) 2004 Kalev Lember ++ * ++ * Author: David Woodhouse ++ * Additional Diskonchip 2000 and Millennium support by Dan Brown ++ * Diskonchip Millennium Plus support by Kalev Lember ++ * ++ * Error correction code lifted from the old docecc code ++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) ++ * Copyright (C) 2000 Netgem S.A. ++ * converted to the generic Reed-Solomon library by Thomas Gleixner ++ * ++ * Interface to generic NAND code for M-Systems DiskOnChip devices ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Where to look for the devices? */ ++#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS ++#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 ++#endif ++ ++static unsigned long doc_locations[] __initdata = { ++#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) ++#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH ++ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, ++ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, ++ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, ++ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, ++ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, ++#else ++ 0xc8000, 0xca000, 0xcc000, 0xce000, ++ 0xd0000, 0xd2000, 0xd4000, 0xd6000, ++ 0xd8000, 0xda000, 0xdc000, 0xde000, ++ 0xe0000, 0xe2000, 0xe4000, 0xe6000, ++ 0xe8000, 0xea000, 0xec000, 0xee000, ++#endif ++#endif ++ 0xffffffff }; ++ ++static struct mtd_info *doclist = NULL; ++ ++struct doc_priv { ++ void __iomem *virtadr; ++ unsigned long physadr; ++ u_char ChipID; ++ u_char CDSNControl; ++ int chips_per_floor; /* The number of chips detected on each floor */ ++ int curfloor; ++ int curchip; ++ int mh0_page; ++ int mh1_page; ++ struct mtd_info *nextdoc; ++ ++ /* Handle the last stage of initialization (BBT scan, partitioning) */ ++ int (*late_init)(struct mtd_info *mtd); ++}; ++ ++/* This is the ecc value computed by the HW ecc generator upon writing an empty ++ page, one with all 0xff for data. */ ++static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; ++ ++#define INFTL_BBT_RESERVED_BLOCKS 4 ++ ++#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) ++#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) ++#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int bitmask); ++static void doc200x_select_chip(struct mtd_info *mtd, int chip); ++ ++static int debug = 0; ++module_param(debug, int, 0); ++ ++static int try_dword = 1; ++module_param(try_dword, int, 0); ++ ++static int no_ecc_failures = 0; ++module_param(no_ecc_failures, int, 0); ++ ++static int no_autopart = 0; ++module_param(no_autopart, int, 0); ++ ++static int show_firmware_partition = 0; ++module_param(show_firmware_partition, int, 0); ++ ++#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE ++static int inftl_bbt_write = 1; ++#else ++static int inftl_bbt_write = 0; ++#endif ++module_param(inftl_bbt_write, int, 0); ++ ++static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; ++module_param(doc_config_location, ulong, 0); ++MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); ++ ++/* Sector size for HW ECC */ ++#define SECTOR_SIZE 512 ++/* The sector bytes are packed into NB_DATA 10 bit words */ ++#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) ++/* Number of roots */ ++#define NROOTS 4 ++/* First consective root */ ++#define FCR 510 ++/* Number of symbols */ ++#define NN 1023 ++ ++/* the Reed Solomon control structure */ ++static struct rs_control *rs_decoder; ++ ++/* ++ * The HW decoder in the DoC ASIC's provides us a error syndrome, ++ * which we must convert to a standard syndrome usable by the generic ++ * Reed-Solomon library code. ++ * ++ * Fabrice Bellard figured this out in the old docecc code. I added ++ * some comments, improved a minor bit and converted it to make use ++ * of the generic Reed-Solomon library. tglx ++ */ ++static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) ++{ ++ int i, j, nerr, errpos[8]; ++ uint8_t parity; ++ uint16_t ds[4], s[5], tmp, errval[8], syn[4]; ++ ++ memset(syn, 0, sizeof(syn)); ++ /* Convert the ecc bytes into words */ ++ ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); ++ ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); ++ ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); ++ ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); ++ parity = ecc[1]; ++ ++ /* Initialize the syndrome buffer */ ++ for (i = 0; i < NROOTS; i++) ++ s[i] = ds[0]; ++ /* ++ * Evaluate ++ * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] ++ * where x = alpha^(FCR + i) ++ */ ++ for (j = 1; j < NROOTS; j++) { ++ if (ds[j] == 0) ++ continue; ++ tmp = rs->index_of[ds[j]]; ++ for (i = 0; i < NROOTS; i++) ++ s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; ++ } ++ ++ /* Calc syn[i] = s[i] / alpha^(v + i) */ ++ for (i = 0; i < NROOTS; i++) { ++ if (s[i]) ++ syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); ++ } ++ /* Call the decoder library */ ++ nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); ++ ++ /* Incorrectable errors ? */ ++ if (nerr < 0) ++ return nerr; ++ ++ /* ++ * Correct the errors. The bitpositions are a bit of magic, ++ * but they are given by the design of the de/encoder circuit ++ * in the DoC ASIC's. ++ */ ++ for (i = 0; i < nerr; i++) { ++ int index, bitpos, pos = 1015 - errpos[i]; ++ uint8_t val; ++ if (pos >= NB_DATA && pos < 1019) ++ continue; ++ if (pos < NB_DATA) { ++ /* extract bit position (MSB first) */ ++ pos = 10 * (NB_DATA - 1 - pos) - 6; ++ /* now correct the following 10 bits. At most two bytes ++ can be modified since pos is even */ ++ index = (pos >> 3) ^ 1; ++ bitpos = pos & 7; ++ if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { ++ val = (uint8_t) (errval[i] >> (2 + bitpos)); ++ parity ^= val; ++ if (index < SECTOR_SIZE) ++ data[index] ^= val; ++ } ++ index = ((pos >> 3) + 1) ^ 1; ++ bitpos = (bitpos + 10) & 7; ++ if (bitpos == 0) ++ bitpos = 8; ++ if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { ++ val = (uint8_t) (errval[i] << (8 - bitpos)); ++ parity ^= val; ++ if (index < SECTOR_SIZE) ++ data[index] ^= val; ++ } ++ } ++ } ++ /* If the parity is wrong, no rescue possible */ ++ return parity ? -EBADMSG : nerr; ++} ++ ++static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) ++{ ++ volatile char dummy; ++ int i; ++ ++ for (i = 0; i < cycles; i++) { ++ if (DoC_is_Millennium(doc)) ++ dummy = ReadDOC(doc->virtadr, NOP); ++ else if (DoC_is_MillenniumPlus(doc)) ++ dummy = ReadDOC(doc->virtadr, Mplus_NOP); ++ else ++ dummy = ReadDOC(doc->virtadr, DOCStatus); ++ } ++ ++} ++ ++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) ++ ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(struct doc_priv *doc) ++{ ++ void __iomem *docptr = doc->virtadr; ++ unsigned long timeo = jiffies + (HZ * 10); ++ ++ if (debug) ++ printk("_DoC_WaitReady...\n"); ++ /* Out-of-line routine to wait for chip response */ ++ if (DoC_is_MillenniumPlus(doc)) { ++ while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { ++ if (time_after(jiffies, timeo)) { ++ printk("_DoC_WaitReady timed out.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ cond_resched(); ++ } ++ } else { ++ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if (time_after(jiffies, timeo)) { ++ printk("_DoC_WaitReady timed out.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ cond_resched(); ++ } ++ } ++ ++ return 0; ++} ++ ++static inline int DoC_WaitReady(struct doc_priv *doc) ++{ ++ void __iomem *docptr = doc->virtadr; ++ int ret = 0; ++ ++ if (DoC_is_MillenniumPlus(doc)) { ++ DoC_Delay(doc, 4); ++ ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(doc); ++ } else { ++ DoC_Delay(doc, 4); ++ ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(doc); ++ DoC_Delay(doc, 2); ++ } ++ ++ if (debug) ++ printk("DoC_WaitReady OK\n"); ++ return ret; ++} ++ ++static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ if (debug) ++ printk("write_byte %02x\n", datum); ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, 2k_CDSN_IO); ++} ++ ++static u_char doc2000_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ u_char ret; ++ ++ ReadDOC(docptr, CDSNSlowIO); ++ DoC_Delay(doc, 2); ++ ret = ReadDOC(docptr, 2k_CDSN_IO); ++ if (debug) ++ printk("read_byte returns %02x\n", ret); ++ return ret; ++} ++ ++static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ if (debug) ++ printk("writebuf of %d bytes: ", len); ++ for (i = 0; i < len; i++) { ++ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ if (debug) ++ printk("\n"); ++} ++ ++static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug) ++ printk("readbuf of %d bytes: ", len); ++ ++ for (i = 0; i < len; i++) { ++ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++} ++ ++static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug) ++ printk("readbuf_dword of %d bytes: ", len); ++ ++ if (unlikely((((unsigned long)buf) | len) & 3)) { ++ for (i = 0; i < len; i++) { ++ *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++ } else { ++ for (i = 0; i < len; i += 4) { ++ *(uint32_t *) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); ++ } ++ } ++} ++ ++static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ uint16_t ret; ++ ++ doc200x_select_chip(mtd, nr); ++ doc200x_hwcontrol(mtd, NAND_CMD_READID, ++ NAND_CTRL_CLE | NAND_CTRL_CHANGE); ++ doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); ++ doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ ++ /* We can't use dev_ready here, but at least we wait for the ++ * command to complete ++ */ ++ udelay(50); ++ ++ ret = this->read_byte(mtd) << 8; ++ ret |= this->read_byte(mtd); ++ ++ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { ++ /* First chip probe. See if we get same results by 32-bit access */ ++ union { ++ uint32_t dword; ++ uint8_t byte[4]; ++ } ident; ++ void __iomem *docptr = doc->virtadr; ++ ++ doc200x_hwcontrol(mtd, NAND_CMD_READID, ++ NAND_CTRL_CLE | NAND_CTRL_CHANGE); ++ doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); ++ doc200x_hwcontrol(mtd, NAND_CMD_NONE, ++ NAND_NCE | NAND_CTRL_CHANGE); ++ ++ udelay(50); ++ ++ ident.dword = readl(docptr + DoC_2k_CDSN_IO); ++ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { ++ printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); ++ this->read_buf = &doc2000_readbuf_dword; ++ } ++ } ++ ++ return ret; ++} ++ ++static void __init doc2000_count_chips(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ uint16_t mfrid; ++ int i; ++ ++ /* Max 4 chips per floor on DiskOnChip 2000 */ ++ doc->chips_per_floor = 4; ++ ++ /* Find out what the first chip is */ ++ mfrid = doc200x_ident_chip(mtd, 0); ++ ++ /* Find how many chips in each floor. */ ++ for (i = 1; i < 4; i++) { ++ if (doc200x_ident_chip(mtd, i) != mfrid) ++ break; ++ } ++ doc->chips_per_floor = i; ++ printk(KERN_DEBUG "Detected %d chips per floor.\n", i); ++} ++ ++static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct doc_priv *doc = nand_get_controller_data(this); ++ ++ int status; ++ ++ DoC_WaitReady(doc); ++ nand_status_op(this, NULL); ++ DoC_WaitReady(doc); ++ status = (int)this->read_byte(mtd); ++ ++ return status; ++} ++ ++static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, Mil_CDSN_IO); ++ WriteDOC(datum, docptr, WritePipeTerm); ++} ++ ++static u_char doc2001_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ //ReadDOC(docptr, CDSNSlowIO); ++ /* 11.4.5 -- delay twice to allow extended length cycle */ ++ DoC_Delay(doc, 2); ++ ReadDOC(docptr, ReadPipeInit); ++ //return ReadDOC(docptr, Mil_CDSN_IO); ++ return ReadDOC(docptr, LastDataRead); ++} ++ ++static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ for (i = 0; i < len; i++) ++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); ++ /* Terminate write pipeline */ ++ WriteDOC(0x00, docptr, WritePipeTerm); ++} ++ ++static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i = 0; i < len - 1; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); ++ ++ /* Terminate read pipeline */ ++ buf[i] = ReadDOC(docptr, LastDataRead); ++} ++ ++static u_char doc2001plus_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ u_char ret; ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ret = ReadDOC(docptr, Mplus_LastDataRead); ++ if (debug) ++ printk("read_byte returns %02x\n", ret); ++ return ret; ++} ++ ++static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug) ++ printk("writebuf of %d bytes: ", len); ++ for (i = 0; i < len; i++) { ++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ if (debug) ++ printk("\n"); ++} ++ ++static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ if (debug) ++ printk("readbuf of %d bytes: ", len); ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ for (i = 0; i < len - 2; i++) { ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ ++ /* Terminate read pipeline */ ++ buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead); ++ if (debug && i < 16) ++ printk("%02x ", buf[len - 2]); ++ buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); ++ if (debug && i < 16) ++ printk("%02x ", buf[len - 1]); ++ if (debug) ++ printk("\n"); ++} ++ ++static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int floor = 0; ++ ++ if (debug) ++ printk("select chip (%d)\n", chip); ++ ++ if (chip == -1) { ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ return; ++ } ++ ++ floor = chip / doc->chips_per_floor; ++ chip -= (floor * doc->chips_per_floor); ++ ++ /* Assert ChipEnable and deassert WriteProtect */ ++ WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); ++ nand_reset_op(this); ++ ++ doc->curchip = chip; ++ doc->curfloor = floor; ++} ++ ++static void doc200x_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int floor = 0; ++ ++ if (debug) ++ printk("select chip (%d)\n", chip); ++ ++ if (chip == -1) ++ return; ++ ++ floor = chip / doc->chips_per_floor; ++ chip -= (floor * doc->chips_per_floor); ++ ++ /* 11.4.4 -- deassert CE before changing chip */ ++ doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); ++ ++ WriteDOC(floor, docptr, FloorSelect); ++ WriteDOC(chip, docptr, CDSNDeviceSelect); ++ ++ doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ ++ doc->curchip = chip; ++ doc->curfloor = floor; ++} ++ ++#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ doc->CDSNControl &= ~CDSN_CTRL_MSK; ++ doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; ++ if (debug) ++ printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); ++ WriteDOC(doc->CDSNControl, docptr, CDSNControl); ++ /* 11.4.3 -- 4 NOPs after CSDNControl write */ ++ DoC_Delay(doc, 4); ++ } ++ if (cmd != NAND_CMD_NONE) { ++ if (DoC_is_2000(doc)) ++ doc2000_write_byte(mtd, cmd); ++ else ++ doc2001_write_byte(mtd, cmd); ++ } ++} ++ ++static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ /* ++ * Must terminate write pipeline before sending any commands ++ * to the device. ++ */ ++ if (command == NAND_CMD_PAGEPROG) { ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ } ++ ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->writesize) { ++ /* OOB area */ ++ column -= mtd->writesize; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ WriteDOC(readcmd, docptr, Mplus_FlashCmd); ++ } ++ WriteDOC(command, docptr, Mplus_FlashCmd); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ ++ if (column != -1 || page_addr != -1) { ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (this->options & NAND_BUSWIDTH_16 && ++ !nand_opcode_8bits(command)) ++ column >>= 1; ++ WriteDOC(column, docptr, Mplus_FlashAddress); ++ } ++ if (page_addr != -1) { ++ WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); ++ WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); ++ if (this->options & NAND_ROW_ADDR_3) { ++ WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); ++ printk("high density\n"); ++ } ++ } ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ /* deassert ALE */ ++ if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || ++ command == NAND_CMD_READOOB || command == NAND_CMD_READID) ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ udelay(this->chip_delay); ++ WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0, docptr, Mplus_WritePipeTerm); ++ while (!(this->read_byte(mtd) & 0x40)) ; ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay(this->chip_delay); ++ return; ++ } ++ } ++ ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay(100); ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)) ; ++} ++ ++static int doc200x_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ if (DoC_is_MillenniumPlus(doc)) { ++ /* 11.4.2 -- must NOP four times before checking FR/B# */ ++ DoC_Delay(doc, 4); ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { ++ if (debug) ++ printk("not ready\n"); ++ return 0; ++ } ++ if (debug) ++ printk("was ready\n"); ++ return 1; ++ } else { ++ /* 11.4.2 -- must NOP four times before checking FR/B# */ ++ DoC_Delay(doc, 4); ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if (debug) ++ printk("not ready\n"); ++ return 0; ++ } ++ /* 11.4.2 -- Must NOP twice if it's ready */ ++ DoC_Delay(doc, 2); ++ if (debug) ++ printk("was ready\n"); ++ return 1; ++ } ++} ++ ++static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs) ++{ ++ /* This is our last resort if we couldn't find or create a BBT. Just ++ pretend all blocks are good. */ ++ return 0; ++} ++ ++static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ /* Prime the ECC engine */ ++ switch (mode) { ++ case NAND_ECC_READ: ++ WriteDOC(DOC_ECC_RESET, docptr, ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, ECCConf); ++ break; ++ case NAND_ECC_WRITE: ++ WriteDOC(DOC_ECC_RESET, docptr, ECCConf); ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); ++ break; ++ } ++} ++ ++static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ ++ /* Prime the ECC engine */ ++ switch (mode) { ++ case NAND_ECC_READ: ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); ++ break; ++ case NAND_ECC_WRITE: ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); ++ break; ++ } ++} ++ ++/* This code is only called on write */ ++static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ int emptymatch = 1; ++ ++ /* flush the pipeline */ ++ if (DoC_is_2000(doc)) { ++ WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); ++ WriteDOC(0, docptr, 2k_CDSN_IO); ++ WriteDOC(0, docptr, 2k_CDSN_IO); ++ WriteDOC(0, docptr, 2k_CDSN_IO); ++ WriteDOC(doc->CDSNControl, docptr, CDSNControl); ++ } else if (DoC_is_MillenniumPlus(doc)) { ++ WriteDOC(0, docptr, Mplus_NOP); ++ WriteDOC(0, docptr, Mplus_NOP); ++ WriteDOC(0, docptr, Mplus_NOP); ++ } else { ++ WriteDOC(0, docptr, NOP); ++ WriteDOC(0, docptr, NOP); ++ WriteDOC(0, docptr, NOP); ++ } ++ ++ for (i = 0; i < 6; i++) { ++ if (DoC_is_MillenniumPlus(doc)) ++ ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); ++ else ++ ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); ++ if (ecc_code[i] != empty_write_ecc[i]) ++ emptymatch = 0; ++ } ++ if (DoC_is_MillenniumPlus(doc)) ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ else ++ WriteDOC(DOC_ECC_DIS, docptr, ECCConf); ++#if 0 ++ /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ ++ if (emptymatch) { ++ /* Note: this somewhat expensive test should not be triggered ++ often. It could be optimized away by examining the data in ++ the writebuf routine, and remembering the result. */ ++ for (i = 0; i < 512; i++) { ++ if (dat[i] == 0xff) ++ continue; ++ emptymatch = 0; ++ break; ++ } ++ } ++ /* If emptymatch still =1, we do have an all-0xff data buffer. ++ Return all-0xff ecc value instead of the computed one, so ++ it'll look just like a freshly-erased page. */ ++ if (emptymatch) ++ memset(ecc_code, 0xff, 6); ++#endif ++ return 0; ++} ++ ++static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *isnull) ++{ ++ int i, ret = 0; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ void __iomem *docptr = doc->virtadr; ++ uint8_t calc_ecc[6]; ++ volatile u_char dummy; ++ ++ /* flush the pipeline */ ++ if (DoC_is_2000(doc)) { ++ dummy = ReadDOC(docptr, 2k_ECCStatus); ++ dummy = ReadDOC(docptr, 2k_ECCStatus); ++ dummy = ReadDOC(docptr, 2k_ECCStatus); ++ } else if (DoC_is_MillenniumPlus(doc)) { ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ } else { ++ dummy = ReadDOC(docptr, ECCConf); ++ dummy = ReadDOC(docptr, ECCConf); ++ dummy = ReadDOC(docptr, ECCConf); ++ } ++ ++ /* Error occurred ? */ ++ if (dummy & 0x80) { ++ for (i = 0; i < 6; i++) { ++ if (DoC_is_MillenniumPlus(doc)) ++ calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); ++ else ++ calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); ++ } ++ ++ ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); ++ if (ret > 0) ++ printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); ++ } ++ if (DoC_is_MillenniumPlus(doc)) ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ else ++ WriteDOC(DOC_ECC_DIS, docptr, ECCConf); ++ if (no_ecc_failures && mtd_is_eccerr(ret)) { ++ printk(KERN_ERR "suppressing ECC failure\n"); ++ ret = 0; ++ } ++ return ret; ++} ++ ++//u_char mydatabuf[528]; ++ ++static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 0; ++ oobregion->length = 6; ++ ++ return 0; ++} ++ ++static int doc200x_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 1) ++ return -ERANGE; ++ ++ /* ++ * The strange out-of-order free bytes definition is a (possibly ++ * unneeded) attempt to retain compatibility. It used to read: ++ * .oobfree = { {8, 8} } ++ * Since that leaves two bytes unusable, it was changed. But the ++ * following scheme might affect existing jffs2 installs by moving the ++ * cleanmarker: ++ * .oobfree = { {6, 10} } ++ * jffs2 seems to handle the above gracefully, but the current scheme ++ * seems safer. The only problem with it is that any code retrieving ++ * free bytes position must be able to handle out-of-order segments. ++ */ ++ if (!section) { ++ oobregion->offset = 8; ++ oobregion->length = 8; ++ } else { ++ oobregion->offset = 6; ++ oobregion->length = 2; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = { ++ .ecc = doc200x_ooblayout_ecc, ++ .free = doc200x_ooblayout_free, ++}; ++ ++/* Find the (I)NFTL Media Header, and optionally also the mirror media header. ++ On successful return, buf will contain a copy of the media header for ++ further processing. id is the string to scan for, and will presumably be ++ either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media ++ header. The page #s of the found media headers are placed in mh0_page and ++ mh1_page in the DOC private structure. */ ++static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ unsigned offs; ++ int ret; ++ size_t retlen; ++ ++ for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { ++ ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); ++ if (retlen != mtd->writesize) ++ continue; ++ if (ret) { ++ printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); ++ } ++ if (memcmp(buf, id, 6)) ++ continue; ++ printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); ++ if (doc->mh0_page == -1) { ++ doc->mh0_page = offs >> this->page_shift; ++ if (!findmirror) ++ return 1; ++ continue; ++ } ++ doc->mh1_page = offs >> this->page_shift; ++ return 2; ++ } ++ if (doc->mh0_page == -1) { ++ printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); ++ return 0; ++ } ++ /* Only one mediaheader was found. We want buf to contain a ++ mediaheader on return, so we'll have to re-read the one we found. */ ++ offs = doc->mh0_page << this->page_shift; ++ ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); ++ if (retlen != mtd->writesize) { ++ /* Insanity. Give up. */ ++ printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); ++ return 0; ++ } ++ return 1; ++} ++ ++static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ int ret = 0; ++ u_char *buf; ++ struct NFTLMediaHeader *mh; ++ const unsigned psize = 1 << this->page_shift; ++ int numparts = 0; ++ unsigned blocks, maxblocks; ++ int offs, numheaders; ++ ++ buf = kmalloc(mtd->writesize, GFP_KERNEL); ++ if (!buf) { ++ return 0; ++ } ++ if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) ++ goto out; ++ mh = (struct NFTLMediaHeader *)buf; ++ ++ le16_to_cpus(&mh->NumEraseUnits); ++ le16_to_cpus(&mh->FirstPhysicalEUN); ++ le32_to_cpus(&mh->FormattedSize); ++ ++ printk(KERN_INFO " DataOrgID = %s\n" ++ " NumEraseUnits = %d\n" ++ " FirstPhysicalEUN = %d\n" ++ " FormattedSize = %d\n" ++ " UnitSizeFactor = %d\n", ++ mh->DataOrgID, mh->NumEraseUnits, ++ mh->FirstPhysicalEUN, mh->FormattedSize, ++ mh->UnitSizeFactor); ++ ++ blocks = mtd->size >> this->phys_erase_shift; ++ maxblocks = min(32768U, mtd->erasesize - psize); ++ ++ if (mh->UnitSizeFactor == 0x00) { ++ /* Auto-determine UnitSizeFactor. The constraints are: ++ - There can be at most 32768 virtual blocks. ++ - There can be at most (virtual block size - page size) ++ virtual blocks (because MediaHeader+BBT must fit in 1). ++ */ ++ mh->UnitSizeFactor = 0xff; ++ while (blocks > maxblocks) { ++ blocks >>= 1; ++ maxblocks = min(32768U, (maxblocks << 1) + psize); ++ mh->UnitSizeFactor--; ++ } ++ printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); ++ } ++ ++ /* NOTE: The lines below modify internal variables of the NAND and MTD ++ layers; variables with have already been configured by nand_scan. ++ Unfortunately, we didn't know before this point what these values ++ should be. Thus, this code is somewhat dependent on the exact ++ implementation of the NAND layer. */ ++ if (mh->UnitSizeFactor != 0xff) { ++ this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); ++ mtd->erasesize <<= (0xff - mh->UnitSizeFactor); ++ printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); ++ blocks = mtd->size >> this->bbt_erase_shift; ++ maxblocks = min(32768U, mtd->erasesize - psize); ++ } ++ ++ if (blocks > maxblocks) { ++ printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); ++ goto out; ++ } ++ ++ /* Skip past the media headers. */ ++ offs = max(doc->mh0_page, doc->mh1_page); ++ offs <<= this->page_shift; ++ offs += mtd->erasesize; ++ ++ if (show_firmware_partition == 1) { ++ parts[0].name = " DiskOnChip Firmware / Media Header partition"; ++ parts[0].offset = 0; ++ parts[0].size = offs; ++ numparts = 1; ++ } ++ ++ parts[numparts].name = " DiskOnChip BDTL partition"; ++ parts[numparts].offset = offs; ++ parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; ++ ++ offs += parts[numparts].size; ++ numparts++; ++ ++ if (offs < mtd->size) { ++ parts[numparts].name = " DiskOnChip Remainder partition"; ++ parts[numparts].offset = offs; ++ parts[numparts].size = mtd->size - offs; ++ numparts++; ++ } ++ ++ ret = numparts; ++ out: ++ kfree(buf); ++ return ret; ++} ++ ++/* This is a stripped-down copy of the code in inftlmount.c */ ++static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ int ret = 0; ++ u_char *buf; ++ struct INFTLMediaHeader *mh; ++ struct INFTLPartition *ip; ++ int numparts = 0; ++ int blocks; ++ int vshift, lastvunit = 0; ++ int i; ++ int end = mtd->size; ++ ++ if (inftl_bbt_write) ++ end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); ++ ++ buf = kmalloc(mtd->writesize, GFP_KERNEL); ++ if (!buf) { ++ return 0; ++ } ++ ++ if (!find_media_headers(mtd, buf, "BNAND", 0)) ++ goto out; ++ doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); ++ mh = (struct INFTLMediaHeader *)buf; ++ ++ le32_to_cpus(&mh->NoOfBootImageBlocks); ++ le32_to_cpus(&mh->NoOfBinaryPartitions); ++ le32_to_cpus(&mh->NoOfBDTLPartitions); ++ le32_to_cpus(&mh->BlockMultiplierBits); ++ le32_to_cpus(&mh->FormatFlags); ++ le32_to_cpus(&mh->PercentUsed); ++ ++ printk(KERN_INFO " bootRecordID = %s\n" ++ " NoOfBootImageBlocks = %d\n" ++ " NoOfBinaryPartitions = %d\n" ++ " NoOfBDTLPartitions = %d\n" ++ " BlockMultiplerBits = %d\n" ++ " FormatFlgs = %d\n" ++ " OsakVersion = %d.%d.%d.%d\n" ++ " PercentUsed = %d\n", ++ mh->bootRecordID, mh->NoOfBootImageBlocks, ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->BlockMultiplierBits, mh->FormatFlags, ++ ((unsigned char *) &mh->OsakVersion)[0] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[1] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[3] & 0xf, ++ mh->PercentUsed); ++ ++ vshift = this->phys_erase_shift + mh->BlockMultiplierBits; ++ ++ blocks = mtd->size >> vshift; ++ if (blocks > 32768) { ++ printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); ++ goto out; ++ } ++ ++ blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); ++ if (inftl_bbt_write && (blocks > mtd->erasesize)) { ++ printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); ++ goto out; ++ } ++ ++ /* Scan the partitions */ ++ for (i = 0; (i < 4); i++) { ++ ip = &(mh->Partitions[i]); ++ le32_to_cpus(&ip->virtualUnits); ++ le32_to_cpus(&ip->firstUnit); ++ le32_to_cpus(&ip->lastUnit); ++ le32_to_cpus(&ip->flags); ++ le32_to_cpus(&ip->spareUnits); ++ le32_to_cpus(&ip->Reserved0); ++ ++ printk(KERN_INFO " PARTITION[%d] ->\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n" ++ " flags = 0x%x\n" ++ " spareUnits = %d\n", ++ i, ip->virtualUnits, ip->firstUnit, ++ ip->lastUnit, ip->flags, ++ ip->spareUnits); ++ ++ if ((show_firmware_partition == 1) && ++ (i == 0) && (ip->firstUnit > 0)) { ++ parts[0].name = " DiskOnChip IPL / Media Header partition"; ++ parts[0].offset = 0; ++ parts[0].size = mtd->erasesize * ip->firstUnit; ++ numparts = 1; ++ } ++ ++ if (ip->flags & INFTL_BINARY) ++ parts[numparts].name = " DiskOnChip BDK partition"; ++ else ++ parts[numparts].name = " DiskOnChip BDTL partition"; ++ parts[numparts].offset = ip->firstUnit << vshift; ++ parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; ++ numparts++; ++ if (ip->lastUnit > lastvunit) ++ lastvunit = ip->lastUnit; ++ if (ip->flags & INFTL_LAST) ++ break; ++ } ++ lastvunit++; ++ if ((lastvunit << vshift) < end) { ++ parts[numparts].name = " DiskOnChip Remainder partition"; ++ parts[numparts].offset = lastvunit << vshift; ++ parts[numparts].size = end - parts[numparts].offset; ++ numparts++; ++ } ++ ret = numparts; ++ out: ++ kfree(buf); ++ return ret; ++} ++ ++static int __init nftl_scan_bbt(struct mtd_info *mtd) ++{ ++ int ret, numparts; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ struct mtd_partition parts[2]; ++ ++ memset((char *)parts, 0, sizeof(parts)); ++ /* On NFTL, we have to find the media headers before we can read the ++ BBTs, since they're stored in the media header eraseblocks. */ ++ numparts = nftl_partscan(mtd, parts); ++ if (!numparts) ++ return -EIO; ++ this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | ++ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | ++ NAND_BBT_VERSION; ++ this->bbt_td->veroffs = 7; ++ this->bbt_td->pages[0] = doc->mh0_page + 1; ++ if (doc->mh1_page != -1) { ++ this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | ++ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | ++ NAND_BBT_VERSION; ++ this->bbt_md->veroffs = 7; ++ this->bbt_md->pages[0] = doc->mh1_page + 1; ++ } else { ++ this->bbt_md = NULL; ++ } ++ ++ ret = this->scan_bbt(mtd); ++ if (ret) ++ return ret; ++ ++ return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); ++} ++ ++static int __init inftl_scan_bbt(struct mtd_info *mtd) ++{ ++ int ret, numparts; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ struct mtd_partition parts[5]; ++ ++ if (this->numchips > doc->chips_per_floor) { ++ printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); ++ return -EIO; ++ } ++ ++ if (DoC_is_MillenniumPlus(doc)) { ++ this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; ++ if (inftl_bbt_write) ++ this->bbt_td->options |= NAND_BBT_WRITE; ++ this->bbt_td->pages[0] = 2; ++ this->bbt_md = NULL; ++ } else { ++ this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; ++ if (inftl_bbt_write) ++ this->bbt_td->options |= NAND_BBT_WRITE; ++ this->bbt_td->offs = 8; ++ this->bbt_td->len = 8; ++ this->bbt_td->veroffs = 7; ++ this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; ++ this->bbt_td->reserved_block_code = 0x01; ++ this->bbt_td->pattern = "MSYS_BBT"; ++ ++ this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; ++ if (inftl_bbt_write) ++ this->bbt_md->options |= NAND_BBT_WRITE; ++ this->bbt_md->offs = 8; ++ this->bbt_md->len = 8; ++ this->bbt_md->veroffs = 7; ++ this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; ++ this->bbt_md->reserved_block_code = 0x01; ++ this->bbt_md->pattern = "TBB_SYSM"; ++ } ++ ++ ret = this->scan_bbt(mtd); ++ if (ret) ++ return ret; ++ ++ memset((char *)parts, 0, sizeof(parts)); ++ numparts = inftl_partscan(mtd, parts); ++ /* At least for now, require the INFTL Media Header. We could probably ++ do without it for non-INFTL use, since all it gives us is ++ autopartitioning, but I want to give it more thought. */ ++ if (!numparts) ++ return -EIO; ++ return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); ++} ++ ++static inline int __init doc2000_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ ++ this->read_byte = doc2000_read_byte; ++ this->write_buf = doc2000_writebuf; ++ this->read_buf = doc2000_readbuf; ++ doc->late_init = nftl_scan_bbt; ++ ++ doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; ++ doc2000_count_chips(mtd); ++ mtd->name = "DiskOnChip 2000 (NFTL Model)"; ++ return (4 * doc->chips_per_floor); ++} ++ ++static inline int __init doc2001_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ ++ this->read_byte = doc2001_read_byte; ++ this->write_buf = doc2001_writebuf; ++ this->read_buf = doc2001_readbuf; ++ ++ ReadDOC(doc->virtadr, ChipID); ++ ReadDOC(doc->virtadr, ChipID); ++ ReadDOC(doc->virtadr, ChipID); ++ if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { ++ /* It's not a Millennium; it's one of the newer ++ DiskOnChip 2000 units with a similar ASIC. ++ Treat it like a Millennium, except that it ++ can have multiple chips. */ ++ doc2000_count_chips(mtd); ++ mtd->name = "DiskOnChip 2000 (INFTL Model)"; ++ doc->late_init = inftl_scan_bbt; ++ return (4 * doc->chips_per_floor); ++ } else { ++ /* Bog-standard Millennium */ ++ doc->chips_per_floor = 1; ++ mtd->name = "DiskOnChip Millennium"; ++ doc->late_init = nftl_scan_bbt; ++ return 1; ++ } ++} ++ ++static inline int __init doc2001plus_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct doc_priv *doc = nand_get_controller_data(this); ++ ++ this->read_byte = doc2001plus_read_byte; ++ this->write_buf = doc2001plus_writebuf; ++ this->read_buf = doc2001plus_readbuf; ++ doc->late_init = inftl_scan_bbt; ++ this->cmd_ctrl = NULL; ++ this->select_chip = doc2001plus_select_chip; ++ this->cmdfunc = doc2001plus_command; ++ this->ecc.hwctl = doc2001plus_enable_hwecc; ++ ++ doc->chips_per_floor = 1; ++ mtd->name = "DiskOnChip Millennium Plus"; ++ ++ return 1; ++} ++ ++static int __init doc_probe(unsigned long physadr) ++{ ++ unsigned char ChipID; ++ struct mtd_info *mtd; ++ struct nand_chip *nand; ++ struct doc_priv *doc; ++ void __iomem *virtadr; ++ unsigned char save_control; ++ unsigned char tmp, tmpb, tmpc; ++ int reg, len, numchips; ++ int ret = 0; ++ ++ if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) ++ return -EBUSY; ++ virtadr = ioremap(physadr, DOC_IOREMAP_LEN); ++ if (!virtadr) { ++ printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); ++ ret = -EIO; ++ goto error_ioremap; ++ } ++ ++ /* It's not possible to cleanly detect the DiskOnChip - the ++ * bootup procedure will put the device into reset mode, and ++ * it's not possible to talk to it without actually writing ++ * to the DOCControl register. So we store the current contents ++ * of the DOCControl register's location, in case we later decide ++ * that it's not a DiskOnChip, and want to put it back how we ++ * found it. ++ */ ++ save_control = ReadDOC(virtadr, DOCControl); ++ ++ /* Reset the DiskOnChip ASIC */ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); ++ ++ /* Enable the DiskOnChip ASIC */ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); ++ ++ ChipID = ReadDOC(virtadr, ChipID); ++ ++ switch (ChipID) { ++ case DOC_ChipID_Doc2k: ++ reg = DoC_2k_ECCStatus; ++ break; ++ case DOC_ChipID_DocMil: ++ reg = DoC_ECCConf; ++ break; ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ case 0: ++ /* Possible Millennium Plus, need to do more checks */ ++ /* Possibly release from power down mode */ ++ for (tmp = 0; (tmp < 4); tmp++) ++ ReadDOC(virtadr, Mplus_Power); ++ ++ /* Reset the Millennium Plus ASIC */ ++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; ++ WriteDOC(tmp, virtadr, Mplus_DOCControl); ++ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); ++ ++ mdelay(1); ++ /* Enable the Millennium Plus ASIC */ ++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; ++ WriteDOC(tmp, virtadr, Mplus_DOCControl); ++ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); ++ mdelay(1); ++ ++ ChipID = ReadDOC(virtadr, ChipID); ++ ++ switch (ChipID) { ++ case DOC_ChipID_DocMilPlus16: ++ reg = DoC_Mplus_Toggle; ++ break; ++ case DOC_ChipID_DocMilPlus32: ++ printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); ++ default: ++ ret = -ENODEV; ++ goto notfound; ++ } ++ break; ++ ++ default: ++ ret = -ENODEV; ++ goto notfound; ++ } ++ /* Check the TOGGLE bit in the ECC register */ ++ tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; ++ tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; ++ if ((tmp == tmpb) || (tmp != tmpc)) { ++ printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); ++ ret = -ENODEV; ++ goto notfound; ++ } ++ ++ for (mtd = doclist; mtd; mtd = doc->nextdoc) { ++ unsigned char oldval; ++ unsigned char newval; ++ nand = mtd_to_nand(mtd); ++ doc = nand_get_controller_data(nand); ++ /* Use the alias resolution register to determine if this is ++ in fact the same DOC aliased to a new address. If writes ++ to one chip's alias resolution register change the value on ++ the other chip, they're the same chip. */ ++ if (ChipID == DOC_ChipID_DocMilPlus16) { ++ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); ++ newval = ReadDOC(virtadr, Mplus_AliasResolution); ++ } else { ++ oldval = ReadDOC(doc->virtadr, AliasResolution); ++ newval = ReadDOC(virtadr, AliasResolution); ++ } ++ if (oldval != newval) ++ continue; ++ if (ChipID == DOC_ChipID_DocMilPlus16) { ++ WriteDOC(~newval, virtadr, Mplus_AliasResolution); ++ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); ++ WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it ++ } else { ++ WriteDOC(~newval, virtadr, AliasResolution); ++ oldval = ReadDOC(doc->virtadr, AliasResolution); ++ WriteDOC(newval, virtadr, AliasResolution); // restore it ++ } ++ newval = ~newval; ++ if (oldval == newval) { ++ printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); ++ goto notfound; ++ } ++ } ++ ++ printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); ++ ++ len = sizeof(struct nand_chip) + sizeof(struct doc_priv) + ++ (2 * sizeof(struct nand_bbt_descr)); ++ nand = kzalloc(len, GFP_KERNEL); ++ if (!nand) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ mtd = nand_to_mtd(nand); ++ doc = (struct doc_priv *) (nand + 1); ++ nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); ++ nand->bbt_md = nand->bbt_td + 1; ++ ++ mtd->owner = THIS_MODULE; ++ mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); ++ ++ nand_set_controller_data(nand, doc); ++ nand->select_chip = doc200x_select_chip; ++ nand->cmd_ctrl = doc200x_hwcontrol; ++ nand->dev_ready = doc200x_dev_ready; ++ nand->waitfunc = doc200x_wait; ++ nand->block_bad = doc200x_block_bad; ++ nand->ecc.hwctl = doc200x_enable_hwecc; ++ nand->ecc.calculate = doc200x_calculate_ecc; ++ nand->ecc.correct = doc200x_correct_data; ++ ++ nand->ecc.mode = NAND_ECC_HW_SYNDROME; ++ nand->ecc.size = 512; ++ nand->ecc.bytes = 6; ++ nand->ecc.strength = 2; ++ nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; ++ nand->bbt_options = NAND_BBT_USE_FLASH; ++ /* Skip the automatic BBT scan so we can run it manually */ ++ nand->options |= NAND_SKIP_BBTSCAN; ++ ++ doc->physadr = physadr; ++ doc->virtadr = virtadr; ++ doc->ChipID = ChipID; ++ doc->curfloor = -1; ++ doc->curchip = -1; ++ doc->mh0_page = -1; ++ doc->mh1_page = -1; ++ doc->nextdoc = doclist; ++ ++ if (ChipID == DOC_ChipID_Doc2k) ++ numchips = doc2000_init(mtd); ++ else if (ChipID == DOC_ChipID_DocMilPlus16) ++ numchips = doc2001plus_init(mtd); ++ else ++ numchips = doc2001_init(mtd); ++ ++ if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) { ++ /* DBB note: i believe nand_release is necessary here, as ++ buffers may have been allocated in nand_base. Check with ++ Thomas. FIX ME! */ ++ /* nand_release will call mtd_device_unregister, but we ++ haven't yet added it. This is handled without incident by ++ mtd_device_unregister, as far as I can tell. */ ++ nand_release(mtd); ++ kfree(nand); ++ goto fail; ++ } ++ ++ /* Success! */ ++ doclist = mtd; ++ return 0; ++ ++ notfound: ++ /* Put back the contents of the DOCControl register, in case it's not ++ actually a DiskOnChip. */ ++ WriteDOC(save_control, virtadr, DOCControl); ++ fail: ++ iounmap(virtadr); ++ ++error_ioremap: ++ release_mem_region(physadr, DOC_IOREMAP_LEN); ++ ++ return ret; ++} ++ ++static void release_nanddoc(void) ++{ ++ struct mtd_info *mtd, *nextmtd; ++ struct nand_chip *nand; ++ struct doc_priv *doc; ++ ++ for (mtd = doclist; mtd; mtd = nextmtd) { ++ nand = mtd_to_nand(mtd); ++ doc = nand_get_controller_data(nand); ++ ++ nextmtd = doc->nextdoc; ++ nand_release(mtd); ++ iounmap(doc->virtadr); ++ release_mem_region(doc->physadr, DOC_IOREMAP_LEN); ++ kfree(nand); ++ } ++} ++ ++static int __init init_nanddoc(void) ++{ ++ int i, ret = 0; ++ ++ /* We could create the decoder on demand, if memory is a concern. ++ * This way we have it handy, if an error happens ++ * ++ * Symbolsize is 10 (bits) ++ * Primitve polynomial is x^10+x^3+1 ++ * first consecutive root is 510 ++ * primitve element to generate roots = 1 ++ * generator polinomial degree = 4 ++ */ ++ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); ++ if (!rs_decoder) { ++ printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); ++ return -ENOMEM; ++ } ++ ++ if (doc_config_location) { ++ printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); ++ ret = doc_probe(doc_config_location); ++ if (ret < 0) ++ goto outerr; ++ } else { ++ for (i = 0; (doc_locations[i] != 0xffffffff); i++) { ++ doc_probe(doc_locations[i]); ++ } ++ } ++ /* No banner message any more. Print a message if no DiskOnChip ++ found, so the user knows we at least tried. */ ++ if (!doclist) { ++ printk(KERN_INFO "No valid DiskOnChip devices found\n"); ++ ret = -ENODEV; ++ goto outerr; ++ } ++ return 0; ++ outerr: ++ free_rs(rs_decoder); ++ return ret; ++} ++ ++static void __exit cleanup_nanddoc(void) ++{ ++ /* Cleanup the nand/DoC resources */ ++ release_nanddoc(); ++ ++ /* Free the reed solomon resources */ ++ if (rs_decoder) { ++ free_rs(rs_decoder); ++ } ++} ++ ++module_init(init_nanddoc); ++module_exit(cleanup_nanddoc); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver"); +diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c +new file mode 100644 +index 00000000..b62f41b +--- /dev/null ++++ b/drivers/mtd/nand/raw/docg4.c +@@ -0,0 +1,1421 @@ ++/* ++ * Copyright © 2012 Mike Dunn ++ * ++ * mtd nand driver for M-Systems DiskOnChip G4 ++ * ++ * 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. ++ * ++ * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus ++ * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. ++ * Should work on these as well. Let me know! ++ * ++ * TODO: ++ * ++ * Mechanism for management of password-protected areas ++ * ++ * Hamming ecc when reading oob only ++ * ++ * According to the M-Sys documentation, this device is also available in a ++ * "dual-die" configuration having a 256MB capacity, but no mechanism for ++ * detecting this variant is documented. Currently this driver assumes 128MB ++ * capacity. ++ * ++ * Support for multiple cascaded devices ("floors"). Not sure which gadgets ++ * contain multiple G4s in a cascaded configuration, if any. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * In "reliable mode" consecutive 2k pages are used in parallel (in some ++ * fashion) to store the same data. The data can be read back from the ++ * even-numbered pages in the normal manner; odd-numbered pages will appear to ++ * contain junk. Systems that boot from the docg4 typically write the secondary ++ * program loader (SPL) code in this mode. The SPL is loaded by the initial ++ * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped ++ * to the reset vector address). This module parameter enables you to use this ++ * driver to write the SPL. When in this mode, no more than 2k of data can be ++ * written at a time, because the addresses do not increment in the normal ++ * manner, and the starting offset must be within an even-numbered 2k region; ++ * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800, ++ * 0x1a00, ... Reliable mode is a special case and should not be used unless ++ * you know what you're doing. ++ */ ++static bool reliable_mode; ++module_param(reliable_mode, bool, 0); ++MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode"); ++ ++/* ++ * You'll want to ignore badblocks if you're reading a partition that contains ++ * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since ++ * it does not use mtd nand's method for marking bad blocks (using oob area). ++ * This will also skip the check of the "page written" flag. ++ */ ++static bool ignore_badblocks; ++module_param(ignore_badblocks, bool, 0); ++MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed"); ++ ++struct docg4_priv { ++ struct mtd_info *mtd; ++ struct device *dev; ++ void __iomem *virtadr; ++ int status; ++ struct { ++ unsigned int command; ++ int column; ++ int page; ++ } last_command; ++ uint8_t oob_buf[16]; ++ uint8_t ecc_buf[7]; ++ int oob_page; ++ struct bch_control *bch; ++}; ++ ++/* ++ * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are ++ * shared with other diskonchip devices (P3, G3 at least). ++ * ++ * Functions with names prefixed with docg4_ are mtd / nand interface functions ++ * (though they may also be called internally). All others are internal. ++ */ ++ ++#define DOC_IOSPACE_DATA 0x0800 ++ ++/* register offsets */ ++#define DOC_CHIPID 0x1000 ++#define DOC_DEVICESELECT 0x100a ++#define DOC_ASICMODE 0x100c ++#define DOC_DATAEND 0x101e ++#define DOC_NOP 0x103e ++ ++#define DOC_FLASHSEQUENCE 0x1032 ++#define DOC_FLASHCOMMAND 0x1034 ++#define DOC_FLASHADDRESS 0x1036 ++#define DOC_FLASHCONTROL 0x1038 ++#define DOC_ECCCONF0 0x1040 ++#define DOC_ECCCONF1 0x1042 ++#define DOC_HAMMINGPARITY 0x1046 ++#define DOC_BCH_SYNDROM(idx) (0x1048 + idx) ++ ++#define DOC_ASICMODECONFIRM 0x1072 ++#define DOC_CHIPID_INV 0x1074 ++#define DOC_POWERMODE 0x107c ++ ++#define DOCG4_MYSTERY_REG 0x1050 ++ ++/* apparently used only to write oob bytes 6 and 7 */ ++#define DOCG4_OOB_6_7 0x1052 ++ ++/* DOC_FLASHSEQUENCE register commands */ ++#define DOC_SEQ_RESET 0x00 ++#define DOCG4_SEQ_PAGE_READ 0x03 ++#define DOCG4_SEQ_FLUSH 0x29 ++#define DOCG4_SEQ_PAGEWRITE 0x16 ++#define DOCG4_SEQ_PAGEPROG 0x1e ++#define DOCG4_SEQ_BLOCKERASE 0x24 ++#define DOCG4_SEQ_SETMODE 0x45 ++ ++/* DOC_FLASHCOMMAND register commands */ ++#define DOCG4_CMD_PAGE_READ 0x00 ++#define DOC_CMD_ERASECYCLE2 0xd0 ++#define DOCG4_CMD_FLUSH 0x70 ++#define DOCG4_CMD_READ2 0x30 ++#define DOC_CMD_PROG_BLOCK_ADDR 0x60 ++#define DOCG4_CMD_PAGEWRITE 0x80 ++#define DOC_CMD_PROG_CYCLE2 0x10 ++#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */ ++#define DOC_CMD_RELIABLE_MODE 0x22 ++#define DOC_CMD_RESET 0xff ++ ++/* DOC_POWERMODE register bits */ ++#define DOC_POWERDOWN_READY 0x80 ++ ++/* DOC_FLASHCONTROL register bits */ ++#define DOC_CTRL_CE 0x10 ++#define DOC_CTRL_UNKNOWN 0x40 ++#define DOC_CTRL_FLASHREADY 0x01 ++ ++/* DOC_ECCCONF0 register bits */ ++#define DOC_ECCCONF0_READ_MODE 0x8000 ++#define DOC_ECCCONF0_UNKNOWN 0x2000 ++#define DOC_ECCCONF0_ECC_ENABLE 0x1000 ++#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff ++ ++/* DOC_ECCCONF1 register bits */ ++#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 ++#define DOC_ECCCONF1_ECC_ENABLE 0x07 ++#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 ++ ++/* DOC_ASICMODE register bits */ ++#define DOC_ASICMODE_RESET 0x00 ++#define DOC_ASICMODE_NORMAL 0x01 ++#define DOC_ASICMODE_POWERDOWN 0x02 ++#define DOC_ASICMODE_MDWREN 0x04 ++#define DOC_ASICMODE_BDETCT_RESET 0x08 ++#define DOC_ASICMODE_RSTIN_RESET 0x10 ++#define DOC_ASICMODE_RAM_WE 0x20 ++ ++/* good status values read after read/write/erase operations */ ++#define DOCG4_PROGSTATUS_GOOD 0x51 ++#define DOCG4_PROGSTATUS_GOOD_2 0xe0 ++ ++/* ++ * On read operations (page and oob-only), the first byte read from I/O reg is a ++ * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read ++ * after reset only) or 0x51, so bit 1 is presumed to be an error indicator. ++ */ ++#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */ ++ ++/* anatomy of the device */ ++#define DOCG4_CHIP_SIZE 0x8000000 ++#define DOCG4_PAGE_SIZE 0x200 ++#define DOCG4_PAGES_PER_BLOCK 0x200 ++#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE) ++#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE) ++#define DOCG4_OOB_SIZE 0x10 ++#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */ ++#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */ ++#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */ ++ ++/* all but the last byte is included in ecc calculation */ ++#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1) ++ ++#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */ ++ ++/* expected values from the ID registers */ ++#define DOCG4_IDREG1_VALUE 0x0400 ++#define DOCG4_IDREG2_VALUE 0xfbff ++ ++/* primitive polynomial used to build the Galois field used by hw ecc gen */ ++#define DOCG4_PRIMITIVE_POLY 0x4443 ++ ++#define DOCG4_M 14 /* Galois field is of order 2^14 */ ++#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ ++ ++#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ ++#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */ ++ ++/* ++ * Bytes 0, 1 are used as badblock marker. ++ * Bytes 2 - 6 are available to the user. ++ * Byte 7 is hamming ecc for first 7 oob bytes only. ++ * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. ++ * Byte 15 (the last) is used by the driver as a "page written" flag. ++ */ ++static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 7; ++ oobregion->length = 9; ++ ++ return 0; ++} ++ ++static int docg4_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 2; ++ oobregion->length = 5; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops docg4_ooblayout_ops = { ++ .ecc = docg4_ooblayout_ecc, ++ .free = docg4_ooblayout_free, ++}; ++ ++/* ++ * The device has a nop register which M-Sys claims is for the purpose of ++ * inserting precise delays. But beware; at least some operations fail if the ++ * nop writes are replaced with a generic delay! ++ */ ++static inline void write_nop(void __iomem *docptr) ++{ ++ writew(0, docptr + DOC_NOP); ++} ++ ++static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ uint16_t *p = (uint16_t *) buf; ++ len >>= 1; ++ ++ for (i = 0; i < len; i++) ++ p[i] = readw(nand->IO_ADDR_R); ++} ++ ++static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ uint16_t *p = (uint16_t *) buf; ++ len >>= 1; ++ ++ for (i = 0; i < len; i++) ++ writew(p[i], nand->IO_ADDR_W); ++} ++ ++static int poll_status(struct docg4_priv *doc) ++{ ++ /* ++ * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL ++ * register. Operations known to take a long time (e.g., block erase) ++ * should sleep for a while before calling this. ++ */ ++ ++ uint16_t flash_status; ++ unsigned long timeo; ++ void __iomem *docptr = doc->virtadr; ++ ++ dev_dbg(doc->dev, "%s...\n", __func__); ++ ++ /* hardware quirk requires reading twice initially */ ++ flash_status = readw(docptr + DOC_FLASHCONTROL); ++ ++ timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */ ++ do { ++ cpu_relax(); ++ flash_status = readb(docptr + DOC_FLASHCONTROL); ++ } while (!(flash_status & DOC_CTRL_FLASHREADY) && ++ time_before(jiffies, timeo)); ++ ++ if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) { ++ dev_err(doc->dev, "%s: timed out!\n", __func__); ++ return NAND_STATUS_FAIL; ++ } ++ ++ return 0; ++} ++ ++ ++static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) ++{ ++ ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ int status = NAND_STATUS_WP; /* inverse logic?? */ ++ dev_dbg(doc->dev, "%s...\n", __func__); ++ ++ /* report any previously unreported error */ ++ if (doc->status) { ++ status |= doc->status; ++ doc->status = 0; ++ return status; ++ } ++ ++ status |= poll_status(doc); ++ return status; ++} ++ ++static void docg4_select_chip(struct mtd_info *mtd, int chip) ++{ ++ /* ++ * Select among multiple cascaded chips ("floors"). Multiple floors are ++ * not yet supported, so the only valid non-negative value is 0. ++ */ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ ++ dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip); ++ ++ if (chip < 0) ++ return; /* deselected */ ++ ++ if (chip > 0) ++ dev_warn(doc->dev, "multiple floors currently unsupported\n"); ++ ++ writew(0, docptr + DOC_DEVICESELECT); ++} ++ ++static void reset(struct mtd_info *mtd) ++{ ++ /* full device reset */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ ++ writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, ++ docptr + DOC_ASICMODE); ++ writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), ++ docptr + DOC_ASICMODECONFIRM); ++ write_nop(docptr); ++ ++ writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, ++ docptr + DOC_ASICMODE); ++ writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), ++ docptr + DOC_ASICMODECONFIRM); ++ ++ writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); ++ ++ poll_status(doc); ++} ++ ++static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) ++{ ++ /* read the 7 hw-generated ecc bytes */ ++ ++ int i; ++ for (i = 0; i < 7; i++) { /* hw quirk; read twice */ ++ ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); ++ ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); ++ } ++} ++ ++static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) ++{ ++ /* ++ * Called after a page read when hardware reports bitflips. ++ * Up to four bitflips can be corrected. ++ */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ int i, numerrs, errpos[4]; ++ const uint8_t blank_read_hwecc[8] = { ++ 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; ++ ++ read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ ++ ++ /* check if read error is due to a blank page */ ++ if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) ++ return 0; /* yes */ ++ ++ /* skip additional check of "written flag" if ignore_badblocks */ ++ if (ignore_badblocks == false) { ++ ++ /* ++ * If the hw ecc bytes are not those of a blank page, there's ++ * still a chance that the page is blank, but was read with ++ * errors. Check the "written flag" in last oob byte, which ++ * is set to zero when a page is written. If more than half ++ * the bits are set, assume a blank page. Unfortunately, the ++ * bit flips(s) are not reported in stats. ++ */ ++ ++ if (nand->oob_poi[15]) { ++ int bit, numsetbits = 0; ++ unsigned long written_flag = nand->oob_poi[15]; ++ for_each_set_bit(bit, &written_flag, 8) ++ numsetbits++; ++ if (numsetbits > 4) { /* assume blank */ ++ dev_warn(doc->dev, ++ "error(s) in blank page " ++ "at offset %08x\n", ++ page * DOCG4_PAGE_SIZE); ++ return 0; ++ } ++ } ++ } ++ ++ /* ++ * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch ++ * algorithm is used to decode this. However the hw operates on page ++ * data in a bit order that is the reverse of that of the bch alg, ++ * requiring that the bits be reversed on the result. Thanks to Ivan ++ * Djelic for his analysis! ++ */ ++ for (i = 0; i < 7; i++) ++ doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); ++ ++ numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, ++ doc->ecc_buf, NULL, errpos); ++ ++ if (numerrs == -EBADMSG) { ++ dev_warn(doc->dev, "uncorrectable errors at offset %08x\n", ++ page * DOCG4_PAGE_SIZE); ++ return -EBADMSG; ++ } ++ ++ BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ ++ ++ /* undo last step in BCH alg (modulo mirroring not needed) */ ++ for (i = 0; i < numerrs; i++) ++ errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); ++ ++ /* fix the errors */ ++ for (i = 0; i < numerrs; i++) { ++ ++ /* ignore if error within oob ecc bytes */ ++ if (errpos[i] > DOCG4_USERDATA_LEN * 8) ++ continue; ++ ++ /* if error within oob area preceeding ecc bytes... */ ++ if (errpos[i] > DOCG4_PAGE_SIZE * 8) ++ change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, ++ (unsigned long *)nand->oob_poi); ++ ++ else /* error in page data */ ++ change_bit(errpos[i], (unsigned long *)buf); ++ } ++ ++ dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n", ++ numerrs, page * DOCG4_PAGE_SIZE); ++ ++ return numerrs; ++} ++ ++static uint8_t docg4_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ ++ dev_dbg(doc->dev, "%s\n", __func__); ++ ++ if (doc->last_command.command == NAND_CMD_STATUS) { ++ int status; ++ ++ /* ++ * Previous nand command was status request, so nand ++ * infrastructure code expects to read the status here. If an ++ * error occurred in a previous operation, report it. ++ */ ++ doc->last_command.command = 0; ++ ++ if (doc->status) { ++ status = doc->status; ++ doc->status = 0; ++ } ++ ++ /* why is NAND_STATUS_WP inverse logic?? */ ++ else ++ status = NAND_STATUS_WP | NAND_STATUS_READY; ++ ++ return status; ++ } ++ ++ dev_warn(doc->dev, "unexpected call to read_byte()\n"); ++ ++ return 0; ++} ++ ++static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr) ++{ ++ /* write the four address bytes packed in docg4_addr to the device */ ++ ++ void __iomem *docptr = doc->virtadr; ++ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); ++ docg4_addr >>= 8; ++ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); ++ docg4_addr >>= 8; ++ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); ++ docg4_addr >>= 8; ++ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); ++} ++ ++static int read_progstatus(struct docg4_priv *doc) ++{ ++ /* ++ * This apparently checks the status of programming. Done after an ++ * erasure, and after page data is written. On error, the status is ++ * saved, to be later retrieved by the nand infrastructure code. ++ */ ++ void __iomem *docptr = doc->virtadr; ++ ++ /* status is read from the I/O reg */ ++ uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); ++ uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); ++ uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); ++ ++ dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n", ++ __func__, status1, status2, status3); ++ ++ if (status1 != DOCG4_PROGSTATUS_GOOD ++ || status2 != DOCG4_PROGSTATUS_GOOD_2 ++ || status3 != DOCG4_PROGSTATUS_GOOD_2) { ++ doc->status = NAND_STATUS_FAIL; ++ dev_warn(doc->dev, "read_progstatus failed: " ++ "%02x, %02x, %02x\n", status1, status2, status3); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int pageprog(struct mtd_info *mtd) ++{ ++ /* ++ * Final step in writing a page. Writes the contents of its ++ * internal buffer out to the flash array, or some such. ++ */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ int retval = 0; ++ ++ dev_dbg(doc->dev, "docg4: %s\n", __func__); ++ ++ writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); ++ writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ /* Just busy-wait; usleep_range() slows things down noticeably. */ ++ poll_status(doc); ++ ++ writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); ++ writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); ++ writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ retval = read_progstatus(doc); ++ writew(0, docptr + DOC_DATAEND); ++ write_nop(docptr); ++ poll_status(doc); ++ write_nop(docptr); ++ ++ return retval; ++} ++ ++static void sequence_reset(struct mtd_info *mtd) ++{ ++ /* common starting sequence for all operations */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ ++ writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); ++ writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); ++ writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ write_nop(docptr); ++ poll_status(doc); ++ write_nop(docptr); ++} ++ ++static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) ++{ ++ /* first step in reading a page */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ ++ dev_dbg(doc->dev, ++ "docg4: %s: g4 page %08x\n", __func__, docg4_addr); ++ ++ sequence_reset(mtd); ++ ++ writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); ++ writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ ++ write_addr(doc, docg4_addr); ++ ++ write_nop(docptr); ++ writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ poll_status(doc); ++} ++ ++static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) ++{ ++ /* first step in writing a page */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ ++ dev_dbg(doc->dev, ++ "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); ++ sequence_reset(mtd); ++ ++ if (unlikely(reliable_mode)) { ++ writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE); ++ writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND); ++ writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ } ++ ++ writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); ++ writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ write_addr(doc, docg4_addr); ++ write_nop(docptr); ++ write_nop(docptr); ++ poll_status(doc); ++} ++ ++static uint32_t mtd_to_docg4_address(int page, int column) ++{ ++ /* ++ * Convert mtd address to format used by the device, 32 bit packed. ++ * ++ * Some notes on G4 addressing... The M-Sys documentation on this device ++ * claims that pages are 2K in length, and indeed, the format of the ++ * address used by the device reflects that. But within each page are ++ * four 512 byte "sub-pages", each with its own oob data that is ++ * read/written immediately after the 512 bytes of page data. This oob ++ * data contains the ecc bytes for the preceeding 512 bytes. ++ * ++ * Rather than tell the mtd nand infrastructure that page size is 2k, ++ * with four sub-pages each, we engage in a little subterfuge and tell ++ * the infrastructure code that pages are 512 bytes in size. This is ++ * done because during the course of reverse-engineering the device, I ++ * never observed an instance where an entire 2K "page" was read or ++ * written as a unit. Each "sub-page" is always addressed individually, ++ * its data read/written, and ecc handled before the next "sub-page" is ++ * addressed. ++ * ++ * This requires us to convert addresses passed by the mtd nand ++ * infrastructure code to those used by the device. ++ * ++ * The address that is written to the device consists of four bytes: the ++ * first two are the 2k page number, and the second is the index into ++ * the page. The index is in terms of 16-bit half-words and includes ++ * the preceeding oob data, so e.g., the index into the second ++ * "sub-page" is 0x108, and the full device address of the start of mtd ++ * page 0x201 is 0x00800108. ++ */ ++ int g4_page = page / 4; /* device's 2K page */ ++ int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ ++ return (g4_page << 16) | g4_index; /* pack */ ++} ++ ++static void docg4_command(struct mtd_info *mtd, unsigned command, int column, ++ int page_addr) ++{ ++ /* handle standard nand commands */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); ++ ++ dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n", ++ __func__, command, page_addr, column); ++ ++ /* ++ * Save the command and its arguments. This enables emulation of ++ * standard flash devices, and also some optimizations. ++ */ ++ doc->last_command.command = command; ++ doc->last_command.column = column; ++ doc->last_command.page = page_addr; ++ ++ switch (command) { ++ ++ case NAND_CMD_RESET: ++ reset(mtd); ++ break; ++ ++ case NAND_CMD_READ0: ++ read_page_prologue(mtd, g4_addr); ++ break; ++ ++ case NAND_CMD_STATUS: ++ /* next call to read_byte() will expect a status */ ++ break; ++ ++ case NAND_CMD_SEQIN: ++ if (unlikely(reliable_mode)) { ++ uint16_t g4_page = g4_addr >> 16; ++ ++ /* writes to odd-numbered 2k pages are invalid */ ++ if (g4_page & 0x01) ++ dev_warn(doc->dev, ++ "invalid reliable mode address\n"); ++ } ++ ++ write_page_prologue(mtd, g4_addr); ++ ++ /* hack for deferred write of oob bytes */ ++ if (doc->oob_page == page_addr) ++ memcpy(nand->oob_poi, doc->oob_buf, 16); ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ pageprog(mtd); ++ break; ++ ++ /* we don't expect these, based on review of nand_base.c */ ++ case NAND_CMD_READOOB: ++ case NAND_CMD_READID: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ dev_warn(doc->dev, "docg4_command: " ++ "unexpected nand command 0x%x\n", command); ++ break; ++ ++ } ++} ++ ++static int read_page(struct mtd_info *mtd, struct nand_chip *nand, ++ uint8_t *buf, int page, bool use_ecc) ++{ ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ uint16_t status, edc_err, *buf16; ++ int bits_corrected = 0; ++ ++ dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); ++ ++ nand_read_page_op(nand, page, 0, NULL, 0); ++ ++ writew(DOC_ECCCONF0_READ_MODE | ++ DOC_ECCCONF0_ECC_ENABLE | ++ DOC_ECCCONF0_UNKNOWN | ++ DOCG4_BCH_SIZE, ++ docptr + DOC_ECCCONF0); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ /* the 1st byte from the I/O reg is a status; the rest is page data */ ++ status = readw(docptr + DOC_IOSPACE_DATA); ++ if (status & DOCG4_READ_ERROR) { ++ dev_err(doc->dev, ++ "docg4_read_page: bad status: 0x%02x\n", status); ++ writew(0, docptr + DOC_DATAEND); ++ return -EIO; ++ } ++ ++ dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); ++ ++ docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ ++ ++ /* this device always reads oob after page data */ ++ /* first 14 oob bytes read from I/O reg */ ++ docg4_read_buf(mtd, nand->oob_poi, 14); ++ ++ /* last 2 read from another reg */ ++ buf16 = (uint16_t *)(nand->oob_poi + 14); ++ *buf16 = readw(docptr + DOCG4_MYSTERY_REG); ++ ++ write_nop(docptr); ++ ++ if (likely(use_ecc == true)) { ++ ++ /* read the register that tells us if bitflip(s) detected */ ++ edc_err = readw(docptr + DOC_ECCCONF1); ++ edc_err = readw(docptr + DOC_ECCCONF1); ++ dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err); ++ ++ /* If bitflips are reported, attempt to correct with ecc */ ++ if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { ++ bits_corrected = correct_data(mtd, buf, page); ++ if (bits_corrected == -EBADMSG) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += bits_corrected; ++ } ++ } ++ ++ writew(0, docptr + DOC_DATAEND); ++ if (bits_corrected == -EBADMSG) /* uncorrectable errors */ ++ return 0; ++ return bits_corrected; ++} ++ ++ ++static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, ++ uint8_t *buf, int oob_required, int page) ++{ ++ return read_page(mtd, nand, buf, page, false); ++} ++ ++static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, ++ uint8_t *buf, int oob_required, int page) ++{ ++ return read_page(mtd, nand, buf, page, true); ++} ++ ++static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, ++ int page) ++{ ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ uint16_t status; ++ ++ dev_dbg(doc->dev, "%s: page %x\n", __func__, page); ++ ++ nand_read_page_op(nand, page, nand->ecc.size, NULL, 0); ++ ++ writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ /* the 1st byte from the I/O reg is a status; the rest is oob data */ ++ status = readw(docptr + DOC_IOSPACE_DATA); ++ if (status & DOCG4_READ_ERROR) { ++ dev_warn(doc->dev, ++ "docg4_read_oob failed: status = 0x%02x\n", status); ++ return -EIO; ++ } ++ ++ dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); ++ ++ docg4_read_buf(mtd, nand->oob_poi, 16); ++ ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ writew(0, docptr + DOC_DATAEND); ++ write_nop(docptr); ++ ++ return 0; ++} ++ ++static int docg4_erase_block(struct mtd_info *mtd, int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ uint16_t g4_page; ++ int status; ++ ++ dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); ++ ++ sequence_reset(mtd); ++ ++ writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); ++ writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ ++ /* only 2 bytes of address are written to specify erase block */ ++ g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ ++ writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); ++ g4_page >>= 8; ++ writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); ++ write_nop(docptr); ++ ++ /* start the erasure */ ++ writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ usleep_range(500, 1000); /* erasure is long; take a snooze */ ++ poll_status(doc); ++ writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); ++ writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); ++ writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ read_progstatus(doc); ++ ++ writew(0, docptr + DOC_DATAEND); ++ write_nop(docptr); ++ poll_status(doc); ++ write_nop(docptr); ++ ++ status = nand->waitfunc(mtd, nand); ++ if (status < 0) ++ return status; ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++static int write_page(struct mtd_info *mtd, struct nand_chip *nand, ++ const uint8_t *buf, int page, bool use_ecc) ++{ ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ uint8_t ecc_buf[8]; ++ ++ dev_dbg(doc->dev, "%s...\n", __func__); ++ ++ nand_prog_page_begin_op(nand, page, 0, NULL, 0); ++ ++ writew(DOC_ECCCONF0_ECC_ENABLE | ++ DOC_ECCCONF0_UNKNOWN | ++ DOCG4_BCH_SIZE, ++ docptr + DOC_ECCCONF0); ++ write_nop(docptr); ++ ++ /* write the page data */ ++ docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); ++ ++ /* oob bytes 0 through 5 are written to I/O reg */ ++ docg4_write_buf16(mtd, nand->oob_poi, 6); ++ ++ /* oob byte 6 written to a separate reg */ ++ writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); ++ ++ write_nop(docptr); ++ write_nop(docptr); ++ ++ /* write hw-generated ecc bytes to oob */ ++ if (likely(use_ecc == true)) { ++ /* oob byte 7 is hamming code */ ++ uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); ++ hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ ++ writew(hamming, docptr + DOCG4_OOB_6_7); ++ write_nop(docptr); ++ ++ /* read the 7 bch bytes from ecc regs */ ++ read_hw_ecc(docptr, ecc_buf); ++ ecc_buf[7] = 0; /* clear the "page written" flag */ ++ } ++ ++ /* write user-supplied bytes to oob */ ++ else { ++ writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); ++ write_nop(docptr); ++ memcpy(ecc_buf, &nand->oob_poi[8], 8); ++ } ++ ++ docg4_write_buf16(mtd, ecc_buf, 8); ++ write_nop(docptr); ++ write_nop(docptr); ++ writew(0, docptr + DOC_DATAEND); ++ write_nop(docptr); ++ ++ return nand_prog_page_end_op(nand); ++} ++ ++static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ return write_page(mtd, nand, buf, page, false); ++} ++ ++static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ return write_page(mtd, nand, buf, page, true); ++} ++ ++static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, ++ int page) ++{ ++ /* ++ * Writing oob-only is not really supported, because MLC nand must write ++ * oob bytes at the same time as page data. Nonetheless, we save the ++ * oob buffer contents here, and then write it along with the page data ++ * if the same page is subsequently written. This allows user space ++ * utilities that write the oob data prior to the page data to work ++ * (e.g., nandwrite). The disdvantage is that, if the intention was to ++ * write oob only, the operation is quietly ignored. Also, oob can get ++ * corrupted if two concurrent processes are running nandwrite. ++ */ ++ ++ /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ doc->oob_page = page; ++ memcpy(doc->oob_buf, nand->oob_poi, 16); ++ return 0; ++} ++ ++static int __init read_factory_bbt(struct mtd_info *mtd) ++{ ++ /* ++ * The device contains a read-only factory bad block table. Read it and ++ * update the memory-based bbt accordingly. ++ */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); ++ uint8_t *buf; ++ int i, block; ++ __u32 eccfailed_stats = mtd->ecc_stats.failed; ++ ++ buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ read_page_prologue(mtd, g4_addr); ++ docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); ++ ++ /* ++ * If no memory-based bbt was created, exit. This will happen if module ++ * parameter ignore_badblocks is set. Then why even call this function? ++ * For an unknown reason, block erase always fails if it's the first ++ * operation after device power-up. The above read ensures it never is. ++ * Ugly, I know. ++ */ ++ if (nand->bbt == NULL) /* no memory-based bbt */ ++ goto exit; ++ ++ if (mtd->ecc_stats.failed > eccfailed_stats) { ++ /* ++ * Whoops, an ecc failure ocurred reading the factory bbt. ++ * It is stored redundantly, so we get another chance. ++ */ ++ eccfailed_stats = mtd->ecc_stats.failed; ++ docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE); ++ if (mtd->ecc_stats.failed > eccfailed_stats) { ++ dev_warn(doc->dev, ++ "The factory bbt could not be read!\n"); ++ goto exit; ++ } ++ } ++ ++ /* ++ * Parse factory bbt and update memory-based bbt. Factory bbt format is ++ * simple: one bit per block, block numbers increase left to right (msb ++ * to lsb). Bit clear means bad block. ++ */ ++ for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { ++ int bitnum; ++ unsigned long bits = ~buf[i]; ++ for_each_set_bit(bitnum, &bits, 8) { ++ int badblock = block + 7 - bitnum; ++ nand->bbt[badblock / 4] |= ++ 0x03 << ((badblock % 4) * 2); ++ mtd->ecc_stats.badblocks++; ++ dev_notice(doc->dev, "factory-marked bad block: %d\n", ++ badblock); ++ } ++ } ++ exit: ++ kfree(buf); ++ return 0; ++} ++ ++static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ /* ++ * Mark a block as bad. Bad blocks are marked in the oob area of the ++ * first page of the block. The default scan_bbt() in the nand ++ * infrastructure code works fine for building the memory-based bbt ++ * during initialization, as does the nand infrastructure function that ++ * checks if a block is bad by reading the bbt. This function replaces ++ * the nand default because writes to oob-only are not supported. ++ */ ++ ++ int ret, i; ++ uint8_t *buf; ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ struct nand_bbt_descr *bbtd = nand->badblock_pattern; ++ int page = (int)(ofs >> nand->page_shift); ++ uint32_t g4_addr = mtd_to_docg4_address(page, 0); ++ ++ dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs); ++ ++ if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) ++ dev_warn(doc->dev, "%s: ofs %llx not start of block!\n", ++ __func__, ofs); ++ ++ /* allocate blank buffer for page data */ ++ buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ /* write bit-wise negation of pattern to oob buffer */ ++ memset(nand->oob_poi, 0xff, mtd->oobsize); ++ for (i = 0; i < bbtd->len; i++) ++ nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; ++ ++ /* write first page of block */ ++ write_page_prologue(mtd, g4_addr); ++ docg4_write_page(mtd, nand, buf, 1, page); ++ ret = pageprog(mtd); ++ ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ /* only called when module_param ignore_badblocks is set */ ++ return 0; ++} ++ ++static int docg4_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ /* ++ * Put the device into "deep power-down" mode. Note that CE# must be ++ * deasserted for this to take effect. The xscale, e.g., can be ++ * configured to float this signal when the processor enters power-down, ++ * and a suitable pull-up ensures its deassertion. ++ */ ++ ++ int i; ++ uint8_t pwr_down; ++ struct docg4_priv *doc = platform_get_drvdata(pdev); ++ void __iomem *docptr = doc->virtadr; ++ ++ dev_dbg(doc->dev, "%s...\n", __func__); ++ ++ /* poll the register that tells us we're ready to go to sleep */ ++ for (i = 0; i < 10; i++) { ++ pwr_down = readb(docptr + DOC_POWERMODE); ++ if (pwr_down & DOC_POWERDOWN_READY) ++ break; ++ usleep_range(1000, 4000); ++ } ++ ++ if (pwr_down & DOC_POWERDOWN_READY) { ++ dev_err(doc->dev, "suspend failed; " ++ "timeout polling DOC_POWERDOWN_READY\n"); ++ return -EIO; ++ } ++ ++ writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN, ++ docptr + DOC_ASICMODE); ++ writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN), ++ docptr + DOC_ASICMODECONFIRM); ++ ++ write_nop(docptr); ++ ++ return 0; ++} ++ ++static int docg4_resume(struct platform_device *pdev) ++{ ++ ++ /* ++ * Exit power-down. Twelve consecutive reads of the address below ++ * accomplishes this, assuming CE# has been asserted. ++ */ ++ ++ struct docg4_priv *doc = platform_get_drvdata(pdev); ++ void __iomem *docptr = doc->virtadr; ++ int i; ++ ++ dev_dbg(doc->dev, "%s...\n", __func__); ++ ++ for (i = 0; i < 12; i++) ++ readb(docptr + 0x1fff); ++ ++ return 0; ++} ++ ++static void __init init_mtd_structs(struct mtd_info *mtd) ++{ ++ /* initialize mtd and nand data structures */ ++ ++ /* ++ * Note that some of the following initializations are not usually ++ * required within a nand driver because they are performed by the nand ++ * infrastructure code as part of nand_scan(). In this case they need ++ * to be initialized here because we skip call to nand_scan_ident() (the ++ * first half of nand_scan()). The call to nand_scan_ident() is skipped ++ * because for this device the chip id is not read in the manner of a ++ * standard nand device. Unfortunately, nand_scan_ident() does other ++ * things as well, such as call nand_set_defaults(). ++ */ ++ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ ++ mtd->size = DOCG4_CHIP_SIZE; ++ mtd->name = "Msys_Diskonchip_G4"; ++ mtd->writesize = DOCG4_PAGE_SIZE; ++ mtd->erasesize = DOCG4_BLOCK_SIZE; ++ mtd->oobsize = DOCG4_OOB_SIZE; ++ mtd_set_ooblayout(mtd, &docg4_ooblayout_ops); ++ nand->chipsize = DOCG4_CHIP_SIZE; ++ nand->chip_shift = DOCG4_CHIP_SHIFT; ++ nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; ++ nand->chip_delay = 20; ++ nand->page_shift = DOCG4_PAGE_SHIFT; ++ nand->pagemask = 0x3ffff; ++ nand->badblockpos = NAND_LARGE_BADBLOCK_POS; ++ nand->badblockbits = 8; ++ nand->ecc.mode = NAND_ECC_HW_SYNDROME; ++ nand->ecc.size = DOCG4_PAGE_SIZE; ++ nand->ecc.prepad = 8; ++ nand->ecc.bytes = 8; ++ nand->ecc.strength = DOCG4_T; ++ nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; ++ nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; ++ nand->controller = &nand->dummy_controller; ++ nand_controller_init(nand->controller); ++ ++ /* methods */ ++ nand->cmdfunc = docg4_command; ++ nand->waitfunc = docg4_wait; ++ nand->select_chip = docg4_select_chip; ++ nand->read_byte = docg4_read_byte; ++ nand->block_markbad = docg4_block_markbad; ++ nand->read_buf = docg4_read_buf; ++ nand->write_buf = docg4_write_buf16; ++ nand->erase = docg4_erase_block; ++ nand->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ nand->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ nand->ecc.read_page = docg4_read_page; ++ nand->ecc.write_page = docg4_write_page; ++ nand->ecc.read_page_raw = docg4_read_page_raw; ++ nand->ecc.write_page_raw = docg4_write_page_raw; ++ nand->ecc.read_oob = docg4_read_oob; ++ nand->ecc.write_oob = docg4_write_oob; ++ ++ /* ++ * The way the nand infrastructure code is written, a memory-based bbt ++ * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, ++ * nand->block_bad() is used. So when ignoring bad blocks, we skip the ++ * scan and define a dummy block_bad() which always returns 0. ++ */ ++ if (ignore_badblocks) { ++ nand->options |= NAND_SKIP_BBTSCAN; ++ nand->block_bad = docg4_block_neverbad; ++ } ++ ++} ++ ++static int __init read_id_reg(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct docg4_priv *doc = nand_get_controller_data(nand); ++ void __iomem *docptr = doc->virtadr; ++ uint16_t id1, id2; ++ ++ /* check for presence of g4 chip by reading id registers */ ++ id1 = readw(docptr + DOC_CHIPID); ++ id1 = readw(docptr + DOCG4_MYSTERY_REG); ++ id2 = readw(docptr + DOC_CHIPID_INV); ++ id2 = readw(docptr + DOCG4_MYSTERY_REG); ++ ++ if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) { ++ dev_info(doc->dev, ++ "NAND device: 128MiB Diskonchip G4 detected\n"); ++ return 0; ++ } ++ ++ return -ENODEV; ++} ++ ++static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; ++ ++static int __init probe_docg4(struct platform_device *pdev) ++{ ++ struct mtd_info *mtd; ++ struct nand_chip *nand; ++ void __iomem *virtadr; ++ struct docg4_priv *doc; ++ int len, retval; ++ struct resource *r; ++ struct device *dev = &pdev->dev; ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (r == NULL) { ++ dev_err(dev, "no io memory resource defined!\n"); ++ return -ENODEV; ++ } ++ ++ virtadr = ioremap(r->start, resource_size(r)); ++ if (!virtadr) { ++ dev_err(dev, "Diskonchip ioremap failed: %pR\n", r); ++ return -EIO; ++ } ++ ++ len = sizeof(struct nand_chip) + sizeof(struct docg4_priv); ++ nand = kzalloc(len, GFP_KERNEL); ++ if (nand == NULL) { ++ retval = -ENOMEM; ++ goto fail_unmap; ++ } ++ ++ mtd = nand_to_mtd(nand); ++ doc = (struct docg4_priv *) (nand + 1); ++ nand_set_controller_data(nand, doc); ++ mtd->dev.parent = &pdev->dev; ++ doc->virtadr = virtadr; ++ doc->dev = dev; ++ ++ init_mtd_structs(mtd); ++ ++ /* initialize kernel bch algorithm */ ++ doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); ++ if (doc->bch == NULL) { ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ platform_set_drvdata(pdev, doc); ++ ++ reset(mtd); ++ retval = read_id_reg(mtd); ++ if (retval == -ENODEV) { ++ dev_warn(dev, "No diskonchip G4 device found.\n"); ++ goto fail; ++ } ++ ++ retval = nand_scan_tail(mtd); ++ if (retval) ++ goto fail; ++ ++ retval = read_factory_bbt(mtd); ++ if (retval) ++ goto fail; ++ ++ retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); ++ if (retval) ++ goto fail; ++ ++ doc->mtd = mtd; ++ return 0; ++ ++fail: ++ nand_release(mtd); /* deletes partitions and mtd devices */ ++ free_bch(doc->bch); ++ kfree(nand); ++ ++fail_unmap: ++ iounmap(virtadr); ++ ++ return retval; ++} ++ ++static int __exit cleanup_docg4(struct platform_device *pdev) ++{ ++ struct docg4_priv *doc = platform_get_drvdata(pdev); ++ nand_release(doc->mtd); ++ free_bch(doc->bch); ++ kfree(mtd_to_nand(doc->mtd)); ++ iounmap(doc->virtadr); ++ return 0; ++} ++ ++static struct platform_driver docg4_driver = { ++ .driver = { ++ .name = "docg4", ++ }, ++ .suspend = docg4_suspend, ++ .resume = docg4_resume, ++ .remove = __exit_p(cleanup_docg4), ++}; ++ ++module_platform_driver_probe(docg4_driver, probe_docg4); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Mike Dunn"); ++MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver"); +diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c +new file mode 100644 +index 00000000..494e76e +--- /dev/null ++++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c +@@ -0,0 +1,979 @@ ++/* Freescale Enhanced Local Bus Controller NAND driver ++ * ++ * Copyright © 2006-2007, 2010 Freescale Semiconductor ++ * ++ * Authors: Nick Spence , ++ * Scott Wood ++ * Jack Lan ++ * Roy Zang ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define MAX_BANKS 8 ++#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ ++#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ ++ ++/* mtd information per set */ ++ ++struct fsl_elbc_mtd { ++ struct nand_chip chip; ++ struct fsl_lbc_ctrl *ctrl; ++ ++ struct device *dev; ++ int bank; /* Chip select bank number */ ++ u8 __iomem *vbase; /* Chip select base virtual address */ ++ int page_size; /* NAND page size (0=512, 1=2048) */ ++ unsigned int fmr; /* FCM Flash Mode Register value */ ++}; ++ ++/* Freescale eLBC FCM controller information */ ++ ++struct fsl_elbc_fcm_ctrl { ++ struct nand_controller controller; ++ struct fsl_elbc_mtd *chips[MAX_BANKS]; ++ ++ u8 __iomem *addr; /* Address of assigned FCM buffer */ ++ unsigned int page; /* Last page written to / read from */ ++ unsigned int read_bytes; /* Number of bytes read during command */ ++ unsigned int column; /* Saved column from SEQIN */ ++ unsigned int index; /* Pointer to next byte to 'read' */ ++ unsigned int status; /* status read from LTESR after last op */ ++ unsigned int mdr; /* UPM/FCM Data Register value */ ++ unsigned int use_mdr; /* Non zero if the MDR is to be set */ ++ unsigned int oob; /* Non zero if operating on OOB data */ ++ unsigned int counter; /* counter for the initializations */ ++ unsigned int max_bitflips; /* Saved during READ0 cmd */ ++}; ++ ++/* These map to the positions used by the FCM hardware ECC generator */ ++ ++static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (16 * section) + 6; ++ if (priv->fmr & FMR_ECCM) ++ oobregion->offset += 2; ++ ++ oobregion->length = chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ ++ if (section > chip->ecc.steps) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->offset = 0; ++ if (mtd->writesize > 512) ++ oobregion->offset++; ++ oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5; ++ } else { ++ oobregion->offset = (16 * section) - ++ ((priv->fmr & FMR_ECCM) ? 5 : 7); ++ if (section < chip->ecc.steps) ++ oobregion->length = 13; ++ else ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = { ++ .ecc = fsl_elbc_ooblayout_ecc, ++ .free = fsl_elbc_ooblayout_free, ++}; ++ ++/* ++ * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, ++ * interfere with ECC positions, that's why we implement our own descriptors. ++ * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. ++ */ ++static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; ++static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | ++ NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 11, ++ .len = 4, ++ .veroffs = 15, ++ .maxblocks = 4, ++ .pattern = bbt_pattern, ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | ++ NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 11, ++ .len = 4, ++ .veroffs = 15, ++ .maxblocks = 4, ++ .pattern = mirror_pattern, ++}; ++ ++/*=================================*/ ++ ++/* ++ * Set up the FCM hardware block and page address fields, and the fcm ++ * structure addr field to point to the correct FCM buffer in memory ++ */ ++static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; ++ int buf_num; ++ ++ elbc_fcm_ctrl->page = page_addr; ++ ++ if (priv->page_size) { ++ /* ++ * large page size chip : FPAR[PI] save the lowest 6 bits, ++ * FBAR[BLK] save the other bits. ++ */ ++ out_be32(&lbc->fbar, page_addr >> 6); ++ out_be32(&lbc->fpar, ++ ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | ++ (oob ? FPAR_LP_MS : 0) | column); ++ buf_num = (page_addr & 1) << 2; ++ } else { ++ /* ++ * small page size chip : FPAR[PI] save the lowest 5 bits, ++ * FBAR[BLK] save the other bits. ++ */ ++ out_be32(&lbc->fbar, page_addr >> 5); ++ out_be32(&lbc->fpar, ++ ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | ++ (oob ? FPAR_SP_MS : 0) | column); ++ buf_num = page_addr & 7; ++ } ++ ++ elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024; ++ elbc_fcm_ctrl->index = column; ++ ++ /* for OOB data point to the second half of the buffer */ ++ if (oob) ++ elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512; ++ ++ dev_vdbg(priv->dev, "set_addr: bank=%d, " ++ "elbc_fcm_ctrl->addr=0x%p (0x%p), " ++ "index %x, pes %d ps %d\n", ++ buf_num, elbc_fcm_ctrl->addr, priv->vbase, ++ elbc_fcm_ctrl->index, ++ chip->phys_erase_shift, chip->page_shift); ++} ++ ++/* ++ * execute FCM command and wait for it to complete ++ */ ++static int fsl_elbc_run_command(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; ++ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; ++ ++ /* Setup the FMR[OP] to execute without write protection */ ++ out_be32(&lbc->fmr, priv->fmr | 3); ++ if (elbc_fcm_ctrl->use_mdr) ++ out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr); ++ ++ dev_vdbg(priv->dev, ++ "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", ++ in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); ++ dev_vdbg(priv->dev, ++ "fsl_elbc_run_command: fbar=%08x fpar=%08x " ++ "fbcr=%08x bank=%d\n", ++ in_be32(&lbc->fbar), in_be32(&lbc->fpar), ++ in_be32(&lbc->fbcr), priv->bank); ++ ++ ctrl->irq_status = 0; ++ /* execute special operation */ ++ out_be32(&lbc->lsor, priv->bank); ++ ++ /* wait for FCM complete flag or timeout */ ++ wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, ++ FCM_TIMEOUT_MSECS * HZ/1000); ++ elbc_fcm_ctrl->status = ctrl->irq_status; ++ /* store mdr value in case it was needed */ ++ if (elbc_fcm_ctrl->use_mdr) ++ elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr); ++ ++ elbc_fcm_ctrl->use_mdr = 0; ++ ++ if (elbc_fcm_ctrl->status != LTESR_CC) { ++ dev_info(priv->dev, ++ "command failed: fir %x fcr %x status %x mdr %x\n", ++ in_be32(&lbc->fir), in_be32(&lbc->fcr), ++ elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr); ++ return -EIO; ++ } ++ ++ if (chip->ecc.mode != NAND_ECC_HW) ++ return 0; ++ ++ elbc_fcm_ctrl->max_bitflips = 0; ++ ++ if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { ++ uint32_t lteccr = in_be32(&lbc->lteccr); ++ /* ++ * if command was a full page read and the ELBC ++ * has the LTECCR register, then bits 12-15 (ppc order) of ++ * LTECCR indicates which 512 byte sub-pages had fixed errors. ++ * bits 28-31 are uncorrectable errors, marked elsewhere. ++ * for small page nand only 1 bit is used. ++ * if the ELBC doesn't have the lteccr register it reads 0 ++ * FIXME: 4 bits can be corrected on NANDs with 2k pages, so ++ * count the number of sub-pages with bitflips and update ++ * ecc_stats.corrected accordingly. ++ */ ++ if (lteccr & 0x000F000F) ++ out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ ++ if (lteccr & 0x000F0000) { ++ mtd->ecc_stats.corrected++; ++ elbc_fcm_ctrl->max_bitflips = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static void fsl_elbc_do_read(struct nand_chip *chip, int oob) ++{ ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; ++ ++ if (priv->page_size) { ++ out_be32(&lbc->fir, ++ (FIR_OP_CM0 << FIR_OP0_SHIFT) | ++ (FIR_OP_CA << FIR_OP1_SHIFT) | ++ (FIR_OP_PA << FIR_OP2_SHIFT) | ++ (FIR_OP_CM1 << FIR_OP3_SHIFT) | ++ (FIR_OP_RBW << FIR_OP4_SHIFT)); ++ ++ out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | ++ (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); ++ } else { ++ out_be32(&lbc->fir, ++ (FIR_OP_CM0 << FIR_OP0_SHIFT) | ++ (FIR_OP_CA << FIR_OP1_SHIFT) | ++ (FIR_OP_PA << FIR_OP2_SHIFT) | ++ (FIR_OP_RBW << FIR_OP3_SHIFT)); ++ ++ if (oob) ++ out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT); ++ else ++ out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); ++ } ++} ++ ++/* cmdfunc send commands to the FCM */ ++static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; ++ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; ++ ++ elbc_fcm_ctrl->use_mdr = 0; ++ ++ /* clear the read buffer */ ++ elbc_fcm_ctrl->read_bytes = 0; ++ if (command != NAND_CMD_PAGEPROG) ++ elbc_fcm_ctrl->index = 0; ++ ++ switch (command) { ++ /* READ0 and READ1 read the entire buffer to use hardware ECC. */ ++ case NAND_CMD_READ1: ++ column += 256; ++ ++ /* fall-through */ ++ case NAND_CMD_READ0: ++ dev_dbg(priv->dev, ++ "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" ++ " 0x%x, column: 0x%x.\n", page_addr, column); ++ ++ ++ out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ ++ set_addr(mtd, 0, page_addr, 0); ++ ++ elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; ++ elbc_fcm_ctrl->index += column; ++ ++ fsl_elbc_do_read(chip, 0); ++ fsl_elbc_run_command(mtd); ++ return; ++ ++ /* READOOB reads only the OOB because no ECC is performed. */ ++ case NAND_CMD_READOOB: ++ dev_vdbg(priv->dev, ++ "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" ++ " 0x%x, column: 0x%x.\n", page_addr, column); ++ ++ out_be32(&lbc->fbcr, mtd->oobsize - column); ++ set_addr(mtd, column, page_addr, 1); ++ ++ elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; ++ ++ fsl_elbc_do_read(chip, 1); ++ fsl_elbc_run_command(mtd); ++ return; ++ ++ case NAND_CMD_READID: ++ case NAND_CMD_PARAM: ++ dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command); ++ ++ out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | ++ (FIR_OP_UA << FIR_OP1_SHIFT) | ++ (FIR_OP_RBW << FIR_OP2_SHIFT)); ++ out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); ++ /* ++ * although currently it's 8 bytes for READID, we always read ++ * the maximum 256 bytes(for PARAM) ++ */ ++ out_be32(&lbc->fbcr, 256); ++ elbc_fcm_ctrl->read_bytes = 256; ++ elbc_fcm_ctrl->use_mdr = 1; ++ elbc_fcm_ctrl->mdr = column; ++ set_addr(mtd, 0, 0, 0); ++ fsl_elbc_run_command(mtd); ++ return; ++ ++ /* ERASE1 stores the block and page address */ ++ case NAND_CMD_ERASE1: ++ dev_vdbg(priv->dev, ++ "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " ++ "page_addr: 0x%x.\n", page_addr); ++ set_addr(mtd, 0, page_addr, 0); ++ return; ++ ++ /* ERASE2 uses the block and page address from ERASE1 */ ++ case NAND_CMD_ERASE2: ++ dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); ++ ++ out_be32(&lbc->fir, ++ (FIR_OP_CM0 << FIR_OP0_SHIFT) | ++ (FIR_OP_PA << FIR_OP1_SHIFT) | ++ (FIR_OP_CM2 << FIR_OP2_SHIFT) | ++ (FIR_OP_CW1 << FIR_OP3_SHIFT) | ++ (FIR_OP_RS << FIR_OP4_SHIFT)); ++ ++ out_be32(&lbc->fcr, ++ (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | ++ (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | ++ (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); ++ ++ out_be32(&lbc->fbcr, 0); ++ elbc_fcm_ctrl->read_bytes = 0; ++ elbc_fcm_ctrl->use_mdr = 1; ++ ++ fsl_elbc_run_command(mtd); ++ return; ++ ++ /* SEQIN sets up the addr buffer and all registers except the length */ ++ case NAND_CMD_SEQIN: { ++ __be32 fcr; ++ dev_vdbg(priv->dev, ++ "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " ++ "page_addr: 0x%x, column: 0x%x.\n", ++ page_addr, column); ++ ++ elbc_fcm_ctrl->column = column; ++ elbc_fcm_ctrl->use_mdr = 1; ++ ++ if (column >= mtd->writesize) { ++ /* OOB area */ ++ column -= mtd->writesize; ++ elbc_fcm_ctrl->oob = 1; ++ } else { ++ WARN_ON(column != 0); ++ elbc_fcm_ctrl->oob = 0; ++ } ++ ++ fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | ++ (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | ++ (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); ++ ++ if (priv->page_size) { ++ out_be32(&lbc->fir, ++ (FIR_OP_CM2 << FIR_OP0_SHIFT) | ++ (FIR_OP_CA << FIR_OP1_SHIFT) | ++ (FIR_OP_PA << FIR_OP2_SHIFT) | ++ (FIR_OP_WB << FIR_OP3_SHIFT) | ++ (FIR_OP_CM3 << FIR_OP4_SHIFT) | ++ (FIR_OP_CW1 << FIR_OP5_SHIFT) | ++ (FIR_OP_RS << FIR_OP6_SHIFT)); ++ } else { ++ out_be32(&lbc->fir, ++ (FIR_OP_CM0 << FIR_OP0_SHIFT) | ++ (FIR_OP_CM2 << FIR_OP1_SHIFT) | ++ (FIR_OP_CA << FIR_OP2_SHIFT) | ++ (FIR_OP_PA << FIR_OP3_SHIFT) | ++ (FIR_OP_WB << FIR_OP4_SHIFT) | ++ (FIR_OP_CM3 << FIR_OP5_SHIFT) | ++ (FIR_OP_CW1 << FIR_OP6_SHIFT) | ++ (FIR_OP_RS << FIR_OP7_SHIFT)); ++ ++ if (elbc_fcm_ctrl->oob) ++ /* OOB area --> READOOB */ ++ fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; ++ else ++ /* First 256 bytes --> READ0 */ ++ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; ++ } ++ ++ out_be32(&lbc->fcr, fcr); ++ set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob); ++ return; ++ } ++ ++ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ ++ case NAND_CMD_PAGEPROG: { ++ dev_vdbg(priv->dev, ++ "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " ++ "writing %d bytes.\n", elbc_fcm_ctrl->index); ++ ++ /* if the write did not start at 0 or is not a full page ++ * then set the exact length, otherwise use a full page ++ * write so the HW generates the ECC. ++ */ ++ if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 || ++ elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) ++ out_be32(&lbc->fbcr, ++ elbc_fcm_ctrl->index - elbc_fcm_ctrl->column); ++ else ++ out_be32(&lbc->fbcr, 0); ++ ++ fsl_elbc_run_command(mtd); ++ return; ++ } ++ ++ /* CMD_STATUS must read the status byte while CEB is active */ ++ /* Note - it does not wait for the ready line */ ++ case NAND_CMD_STATUS: ++ out_be32(&lbc->fir, ++ (FIR_OP_CM0 << FIR_OP0_SHIFT) | ++ (FIR_OP_RBW << FIR_OP1_SHIFT)); ++ out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); ++ out_be32(&lbc->fbcr, 1); ++ set_addr(mtd, 0, 0, 0); ++ elbc_fcm_ctrl->read_bytes = 1; ++ ++ fsl_elbc_run_command(mtd); ++ ++ /* The chip always seems to report that it is ++ * write-protected, even when it is not. ++ */ ++ setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP); ++ return; ++ ++ /* RESET without waiting for the ready line */ ++ case NAND_CMD_RESET: ++ dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); ++ out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); ++ out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); ++ fsl_elbc_run_command(mtd); ++ return; ++ ++ default: ++ dev_err(priv->dev, ++ "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", ++ command); ++ } ++} ++ ++static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ /* The hardware does not seem to support multiple ++ * chips per bank. ++ */ ++} ++ ++/* ++ * Write buf to the FCM Controller Data Buffer ++ */ ++static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; ++ unsigned int bufsize = mtd->writesize + mtd->oobsize; ++ ++ if (len <= 0) { ++ dev_err(priv->dev, "write_buf of %d bytes", len); ++ elbc_fcm_ctrl->status = 0; ++ return; ++ } ++ ++ if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) { ++ dev_err(priv->dev, ++ "write_buf beyond end of buffer " ++ "(%d requested, %u available)\n", ++ len, bufsize - elbc_fcm_ctrl->index); ++ len = bufsize - elbc_fcm_ctrl->index; ++ } ++ ++ memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len); ++ /* ++ * This is workaround for the weird elbc hangs during nand write, ++ * Scott Wood says: "...perhaps difference in how long it takes a ++ * write to make it through the localbus compared to a write to IMMR ++ * is causing problems, and sync isn't helping for some reason." ++ * Reading back the last byte helps though. ++ */ ++ in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1); ++ ++ elbc_fcm_ctrl->index += len; ++} ++ ++/* ++ * read a byte from either the FCM hardware buffer if it has any data left ++ * otherwise issue a command to read a single byte. ++ */ ++static u8 fsl_elbc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; ++ ++ /* If there are still bytes in the FCM, then use the next byte. */ ++ if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes) ++ return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]); ++ ++ dev_err(priv->dev, "read_byte beyond end of buffer\n"); ++ return ERR_BYTE; ++} ++ ++/* ++ * Read from the FCM Controller Data Buffer ++ */ ++static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; ++ int avail; ++ ++ if (len < 0) ++ return; ++ ++ avail = min((unsigned int)len, ++ elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); ++ memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail); ++ elbc_fcm_ctrl->index += avail; ++ ++ if (len > avail) ++ dev_err(priv->dev, ++ "read_buf beyond end of buffer " ++ "(%d requested, %d available)\n", ++ len, avail); ++} ++ ++/* This function is called after Program and Erase Operations to ++ * check for success or failure. ++ */ ++static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; ++ ++ if (elbc_fcm_ctrl->status != LTESR_CC) ++ return NAND_STATUS_FAIL; ++ ++ /* The chip always seems to report that it is ++ * write-protected, even when it is not. ++ */ ++ return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP; ++} ++ ++static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; ++ unsigned int al; ++ ++ /* calculate FMR Address Length field */ ++ al = 0; ++ if (chip->pagemask & 0xffff0000) ++ al++; ++ if (chip->pagemask & 0xff000000) ++ al++; ++ ++ priv->fmr |= al << FMR_AL_SHIFT; ++ ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n", ++ chip->numchips); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n", ++ chip->chipsize); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n", ++ chip->pagemask); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n", ++ chip->chip_delay); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n", ++ chip->badblockpos); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n", ++ chip->chip_shift); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n", ++ chip->page_shift); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", ++ chip->phys_erase_shift); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", ++ chip->ecc.mode); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", ++ chip->ecc.steps); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", ++ chip->ecc.bytes); ++ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", ++ chip->ecc.total); ++ dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n", ++ mtd->ooblayout); ++ dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); ++ dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); ++ dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", ++ mtd->erasesize); ++ dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n", ++ mtd->writesize); ++ dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n", ++ mtd->oobsize); ++ ++ /* adjust Option Register and ECC to match Flash page size */ ++ if (mtd->writesize == 512) { ++ priv->page_size = 0; ++ clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); ++ } else if (mtd->writesize == 2048) { ++ priv->page_size = 1; ++ setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); ++ } else { ++ dev_err(priv->dev, ++ "fsl_elbc_init: page size %d is not supported\n", ++ mtd->writesize); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; ++ ++ nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ if (oob_required) ++ fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) ++ mtd->ecc_stats.failed++; ++ ++ return elbc_fcm_ctrl->max_bitflips; ++} ++ ++/* ECC will be calculated automatically, and errors will be detected in ++ * waitfunc. ++ */ ++static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/* ECC will be calculated automatically, and errors will be detected in ++ * waitfunc. ++ */ ++static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, ++ uint32_t offset, uint32_t data_len, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ fsl_elbc_write_buf(mtd, buf, mtd->writesize); ++ fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ return nand_prog_page_end_op(chip); ++} ++ ++static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) ++{ ++ struct fsl_lbc_ctrl *ctrl = priv->ctrl; ++ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; ++ struct nand_chip *chip = &priv->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); ++ ++ /* Fill in fsl_elbc_mtd structure */ ++ mtd->dev.parent = priv->dev; ++ nand_set_flash_node(chip, priv->dev->of_node); ++ ++ /* set timeout to maximum */ ++ priv->fmr = 15 << FMR_CWTO_SHIFT; ++ if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS) ++ priv->fmr |= FMR_ECCM; ++ ++ /* fill in nand_chip structure */ ++ /* set up function call table */ ++ chip->read_byte = fsl_elbc_read_byte; ++ chip->write_buf = fsl_elbc_write_buf; ++ chip->read_buf = fsl_elbc_read_buf; ++ chip->select_chip = fsl_elbc_select_chip; ++ chip->cmdfunc = fsl_elbc_cmdfunc; ++ chip->waitfunc = fsl_elbc_wait; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ chip->bbt_td = &bbt_main_descr; ++ chip->bbt_md = &bbt_mirror_descr; ++ ++ /* set up nand options */ ++ chip->bbt_options = NAND_BBT_USE_FLASH; ++ ++ chip->controller = &elbc_fcm_ctrl->controller; ++ nand_set_controller_data(chip, priv); ++ ++ chip->ecc.read_page = fsl_elbc_read_page; ++ chip->ecc.write_page = fsl_elbc_write_page; ++ chip->ecc.write_subpage = fsl_elbc_write_subpage; ++ ++ /* If CS Base Register selects full hardware ECC then use it */ ++ if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == ++ BR_DECC_CHK_GEN) { ++ chip->ecc.mode = NAND_ECC_HW; ++ mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops); ++ chip->ecc.size = 512; ++ chip->ecc.bytes = 3; ++ chip->ecc.strength = 1; ++ } else { ++ /* otherwise fall back to default software ECC */ ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ } ++ ++ return 0; ++} ++ ++static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) ++{ ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; ++ struct mtd_info *mtd = nand_to_mtd(&priv->chip); ++ ++ nand_release(mtd); ++ ++ kfree(mtd->name); ++ ++ if (priv->vbase) ++ iounmap(priv->vbase); ++ ++ elbc_fcm_ctrl->chips[priv->bank] = NULL; ++ kfree(priv); ++ return 0; ++} ++ ++static DEFINE_MUTEX(fsl_elbc_nand_mutex); ++ ++static int fsl_elbc_nand_probe(struct platform_device *pdev) ++{ ++ struct fsl_lbc_regs __iomem *lbc; ++ struct fsl_elbc_mtd *priv; ++ struct resource res; ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; ++ static const char *part_probe_types[] ++ = { "cmdlinepart", "RedBoot", "ofpart", NULL }; ++ int ret; ++ int bank; ++ struct device *dev; ++ struct device_node *node = pdev->dev.of_node; ++ struct mtd_info *mtd; ++ ++ if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) ++ return -ENODEV; ++ lbc = fsl_lbc_ctrl_dev->regs; ++ dev = fsl_lbc_ctrl_dev->dev; ++ ++ /* get, allocate and map the memory resource */ ++ ret = of_address_to_resource(node, 0, &res); ++ if (ret) { ++ dev_err(dev, "failed to get resource\n"); ++ return ret; ++ } ++ ++ /* find which chip select it is connected to */ ++ for (bank = 0; bank < MAX_BANKS; bank++) ++ if ((in_be32(&lbc->bank[bank].br) & BR_V) && ++ (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && ++ (in_be32(&lbc->bank[bank].br) & ++ in_be32(&lbc->bank[bank].or) & BR_BA) ++ == fsl_lbc_addr(res.start)) ++ break; ++ ++ if (bank >= MAX_BANKS) { ++ dev_err(dev, "address did not match any chip selects\n"); ++ return -ENODEV; ++ } ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_lock(&fsl_elbc_nand_mutex); ++ if (!fsl_lbc_ctrl_dev->nand) { ++ elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL); ++ if (!elbc_fcm_ctrl) { ++ mutex_unlock(&fsl_elbc_nand_mutex); ++ ret = -ENOMEM; ++ goto err; ++ } ++ elbc_fcm_ctrl->counter++; ++ ++ nand_controller_init(&elbc_fcm_ctrl->controller); ++ fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; ++ } else { ++ elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; ++ } ++ mutex_unlock(&fsl_elbc_nand_mutex); ++ ++ elbc_fcm_ctrl->chips[bank] = priv; ++ priv->bank = bank; ++ priv->ctrl = fsl_lbc_ctrl_dev; ++ priv->dev = &pdev->dev; ++ dev_set_drvdata(priv->dev, priv); ++ ++ priv->vbase = ioremap(res.start, resource_size(&res)); ++ if (!priv->vbase) { ++ dev_err(dev, "failed to map chip region\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ mtd = nand_to_mtd(&priv->chip); ++ mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); ++ if (!nand_to_mtd(&priv->chip)->name) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = fsl_elbc_chip_init(priv); ++ if (ret) ++ goto err; ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ goto err; ++ ++ ret = fsl_elbc_chip_init_tail(mtd); ++ if (ret) ++ goto err; ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ goto err; ++ ++ /* First look for RedBoot table or partitions on the command ++ * line, these take precedence over device tree information */ ++ mtd_device_parse_register(mtd, part_probe_types, NULL, ++ NULL, 0); ++ ++ printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", ++ (unsigned long long)res.start, priv->bank); ++ return 0; ++ ++err: ++ fsl_elbc_chip_remove(priv); ++ return ret; ++} ++ ++static int fsl_elbc_nand_remove(struct platform_device *pdev) ++{ ++ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; ++ struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); ++ ++ fsl_elbc_chip_remove(priv); ++ ++ mutex_lock(&fsl_elbc_nand_mutex); ++ elbc_fcm_ctrl->counter--; ++ if (!elbc_fcm_ctrl->counter) { ++ fsl_lbc_ctrl_dev->nand = NULL; ++ kfree(elbc_fcm_ctrl); ++ } ++ mutex_unlock(&fsl_elbc_nand_mutex); ++ ++ return 0; ++ ++} ++ ++static const struct of_device_id fsl_elbc_nand_match[] = { ++ { .compatible = "fsl,elbc-fcm-nand", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match); ++ ++static struct platform_driver fsl_elbc_nand_driver = { ++ .driver = { ++ .name = "fsl,elbc-fcm-nand", ++ .of_match_table = fsl_elbc_nand_match, ++ }, ++ .probe = fsl_elbc_nand_probe, ++ .remove = fsl_elbc_nand_remove, ++}; ++ ++module_platform_driver(fsl_elbc_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Freescale"); ++MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver"); +diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c +new file mode 100644 +index 00000000..a4cd66a +--- /dev/null ++++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c +@@ -0,0 +1,1114 @@ ++/* ++ * Freescale Integrated Flash Controller NAND driver ++ * ++ * Copyright 2011-2012 Freescale Semiconductor, Inc ++ * ++ * Author: Dipen Dudhat ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ERR_BYTE 0xFF /* Value returned for read ++ bytes when read failed */ ++#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait ++ for IFC NAND Machine */ ++ ++struct fsl_ifc_ctrl; ++ ++/* mtd information per set */ ++struct fsl_ifc_mtd { ++ struct nand_chip chip; ++ struct fsl_ifc_ctrl *ctrl; ++ ++ struct device *dev; ++ int bank; /* Chip select bank number */ ++ unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ ++ u8 __iomem *vbase; /* Chip select base virtual address */ ++}; ++ ++/* overview of the fsl ifc controller */ ++struct fsl_ifc_nand_ctrl { ++ struct nand_controller controller; ++ struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; ++ ++ void __iomem *addr; /* Address of assigned IFC buffer */ ++ unsigned int page; /* Last page written to / read from */ ++ unsigned int read_bytes;/* Number of bytes read during command */ ++ unsigned int column; /* Saved column from SEQIN */ ++ unsigned int index; /* Pointer to next byte to 'read' */ ++ unsigned int oob; /* Non zero if operating on OOB data */ ++ unsigned int eccread; /* Non zero for a full-page ECC read */ ++ unsigned int counter; /* counter for the initializations */ ++ unsigned int max_bitflips; /* Saved during READ0 cmd */ ++}; ++ ++static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; ++ ++/* ++ * Generic flash bbt descriptors ++ */ ++static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; ++static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | ++ NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 2, /* 0 on 8-bit small page */ ++ .len = 4, ++ .veroffs = 6, ++ .maxblocks = 4, ++ .pattern = bbt_pattern, ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | ++ NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 2, /* 0 on 8-bit small page */ ++ .len = 4, ++ .veroffs = 6, ++ .maxblocks = 4, ++ .pattern = mirror_pattern, ++}; ++ ++static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 8; ++ oobregion->length = chip->ecc.total; ++ ++ return 0; ++} ++ ++static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section > 1) ++ return -ERANGE; ++ ++ if (mtd->writesize == 512 && ++ !(chip->options & NAND_BUSWIDTH_16)) { ++ if (!section) { ++ oobregion->offset = 0; ++ oobregion->length = 5; ++ } else { ++ oobregion->offset = 6; ++ oobregion->length = 2; ++ } ++ ++ return 0; ++ } ++ ++ if (!section) { ++ oobregion->offset = 2; ++ oobregion->length = 6; ++ } else { ++ oobregion->offset = chip->ecc.total + 8; ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = { ++ .ecc = fsl_ifc_ooblayout_ecc, ++ .free = fsl_ifc_ooblayout_free, ++}; ++ ++/* ++ * Set up the IFC hardware block and page address fields, and the ifc nand ++ * structure addr field to point to the correct IFC buffer in memory ++ */ ++static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; ++ int buf_num; ++ ++ ifc_nand_ctrl->page = page_addr; ++ /* Program ROW0/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; ++ ++ ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); ++ ifc_nand_ctrl->index = column; ++ ++ /* for OOB data point to the second half of the buffer */ ++ if (oob) ++ ifc_nand_ctrl->index += mtd->writesize; ++} ++ ++/* returns nonzero if entire page is blank */ ++static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, ++ u32 eccstat, unsigned int bufnum) ++{ ++ return (eccstat >> ((3 - bufnum % 4) * 8)) & 15; ++} ++ ++/* ++ * execute IFC NAND command and wait for it to complete ++ */ ++static void fsl_ifc_run_command(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; ++ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; ++ u32 eccstat; ++ int i; ++ ++ /* set the chip select for NAND Transaction */ ++ ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, ++ &ifc->ifc_nand.nand_csel); ++ ++ dev_vdbg(priv->dev, ++ "%s: fir0=%08x fcr0=%08x\n", ++ __func__, ++ ifc_in32(&ifc->ifc_nand.nand_fir0), ++ ifc_in32(&ifc->ifc_nand.nand_fcr0)); ++ ++ ctrl->nand_stat = 0; ++ ++ /* start read/write seq */ ++ 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, ++ msecs_to_jiffies(IFC_TIMEOUT_MSECS)); ++ ++ /* ctrl->nand_stat will be updated from IRQ context */ ++ if (!ctrl->nand_stat) ++ dev_err(priv->dev, "Controller is not responding\n"); ++ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) ++ dev_err(priv->dev, "NAND Flash Timeout Error\n"); ++ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) ++ dev_err(priv->dev, "NAND Flash Write Protect Error\n"); ++ ++ nctrl->max_bitflips = 0; ++ ++ if (nctrl->eccread) { ++ int errors; ++ int bufnum = nctrl->page & priv->bufnum_mask; ++ int sector_start = bufnum * chip->ecc.steps; ++ int sector_end = sector_start + chip->ecc.steps - 1; ++ __be32 *eccstat_regs; ++ ++ eccstat_regs = ifc->ifc_nand.nand_eccstat; ++ eccstat = ifc_in32(&eccstat_regs[sector_start / 4]); ++ ++ for (i = sector_start; i <= sector_end; i++) { ++ if (i != sector_start && !(i % 4)) ++ eccstat = ifc_in32(&eccstat_regs[i / 4]); ++ ++ errors = check_read_ecc(mtd, ctrl, eccstat, i); ++ ++ if (errors == 15) { ++ /* ++ * Uncorrectable error. ++ * We'll check for blank pages later. ++ * ++ * We disable ECCER reporting due to... ++ * erratum IFC-A002770 -- so report it now if we ++ * see an uncorrectable error in ECCSTAT. ++ */ ++ ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER; ++ continue; ++ } ++ ++ mtd->ecc_stats.corrected += errors; ++ nctrl->max_bitflips = max_t(unsigned int, ++ nctrl->max_bitflips, ++ errors); ++ } ++ ++ nctrl->eccread = 0; ++ } ++} ++ ++static void fsl_ifc_do_read(struct nand_chip *chip, ++ int oob, ++ struct mtd_info *mtd) ++{ ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; ++ ++ /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ ++ if (mtd->writesize > 512) { ++ 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); ++ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); ++ ++ ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | ++ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), ++ &ifc->ifc_nand.nand_fcr0); ++ } else { ++ 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); ++ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); ++ ++ if (oob) ++ ifc_out32(NAND_CMD_READOOB << ++ IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc->ifc_nand.nand_fcr0); ++ else ++ ifc_out32(NAND_CMD_READ0 << ++ IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc->ifc_nand.nand_fcr0); ++ } ++} ++ ++/* cmdfunc send commands to the IFC NAND Machine */ ++static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) { ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; ++ ++ /* clear the read buffer */ ++ ifc_nand_ctrl->read_bytes = 0; ++ if (command != NAND_CMD_PAGEPROG) ++ ifc_nand_ctrl->index = 0; ++ ++ switch (command) { ++ /* READ0 read the entire buffer to use hardware ECC. */ ++ case NAND_CMD_READ0: ++ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); ++ set_addr(mtd, 0, page_addr, 0); ++ ++ ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; ++ ifc_nand_ctrl->index += column; ++ ++ if (chip->ecc.mode == NAND_ECC_HW) ++ ifc_nand_ctrl->eccread = 1; ++ ++ fsl_ifc_do_read(chip, 0, mtd); ++ fsl_ifc_run_command(mtd); ++ return; ++ ++ /* READOOB reads only the OOB because no ECC is performed. */ ++ case NAND_CMD_READOOB: ++ 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; ++ ++ fsl_ifc_do_read(chip, 1, mtd); ++ fsl_ifc_run_command(mtd); ++ ++ return; ++ ++ case NAND_CMD_READID: ++ case NAND_CMD_PARAM: { ++ /* ++ * For READID, read 8 bytes that are currently used. ++ * For PARAM, read all 3 copies of 256-bytes pages. ++ */ ++ int len = 8; ++ int timing = IFC_FIR_OP_RB; ++ if (command == NAND_CMD_PARAM) { ++ timing = IFC_FIR_OP_RBCD; ++ len = 256 * 3; ++ } ++ ++ 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); ++ ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc->ifc_nand.nand_fcr0); ++ ifc_out32(column, &ifc->ifc_nand.row3); ++ ++ ifc_out32(len, &ifc->ifc_nand.nand_fbcr); ++ ifc_nand_ctrl->read_bytes = len; ++ ++ set_addr(mtd, 0, 0, 0); ++ fsl_ifc_run_command(mtd); ++ return; ++ } ++ ++ /* ERASE1 stores the block and page address */ ++ case NAND_CMD_ERASE1: ++ set_addr(mtd, 0, page_addr, 0); ++ return; ++ ++ /* ERASE2 uses the block and page address from ERASE1 */ ++ case NAND_CMD_ERASE2: ++ 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); ++ ++ ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | ++ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), ++ &ifc->ifc_nand.nand_fcr0); ++ ++ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); ++ ifc_nand_ctrl->read_bytes = 0; ++ fsl_ifc_run_command(mtd); ++ return; ++ ++ /* SEQIN sets up the addr buffer and all registers except the length */ ++ case NAND_CMD_SEQIN: { ++ u32 nand_fcr0; ++ ifc_nand_ctrl->column = column; ++ ifc_nand_ctrl->oob = 0; ++ ++ if (mtd->writesize > 512) { ++ nand_fcr0 = ++ (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | ++ (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | ++ (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_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_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | ++ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), ++ &ifc->ifc_nand.nand_fir0); ++ ifc_out32( ++ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | ++ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | ++ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), ++ &ifc->ifc_nand.nand_fir1); ++ } else { ++ nand_fcr0 = ((NAND_CMD_PAGEPROG << ++ IFC_NAND_FCR0_CMD1_SHIFT) | ++ (NAND_CMD_SEQIN << ++ IFC_NAND_FCR0_CMD2_SHIFT) | ++ (NAND_CMD_STATUS << ++ IFC_NAND_FCR0_CMD3_SHIFT)); ++ ++ 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); ++ ifc_out32( ++ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | ++ (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | ++ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | ++ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), ++ &ifc->ifc_nand.nand_fir1); ++ ++ if (column >= mtd->writesize) ++ nand_fcr0 |= ++ NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; ++ else ++ nand_fcr0 |= ++ NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; ++ } ++ ++ if (column >= mtd->writesize) { ++ /* OOB area --> READOOB */ ++ column -= mtd->writesize; ++ ifc_nand_ctrl->oob = 1; ++ } ++ ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); ++ set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); ++ return; ++ } ++ ++ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ ++ case NAND_CMD_PAGEPROG: { ++ if (ifc_nand_ctrl->oob) { ++ ifc_out32(ifc_nand_ctrl->index - ++ ifc_nand_ctrl->column, ++ &ifc->ifc_nand.nand_fbcr); ++ } else { ++ ifc_out32(0, &ifc->ifc_nand.nand_fbcr); ++ } ++ ++ fsl_ifc_run_command(mtd); ++ return; ++ } ++ ++ case NAND_CMD_STATUS: { ++ void __iomem *addr; ++ ++ 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); ++ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc->ifc_nand.nand_fcr0); ++ 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); ++ ++ /* ++ * The chip always seems to report that it is ++ * write-protected, even when it is not. ++ */ ++ addr = ifc_nand_ctrl->addr; ++ if (chip->options & NAND_BUSWIDTH_16) ++ ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); ++ else ++ ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); ++ return; ++ } ++ ++ case NAND_CMD_RESET: ++ ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, ++ &ifc->ifc_nand.nand_fir0); ++ ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc->ifc_nand.nand_fcr0); ++ fsl_ifc_run_command(mtd); ++ return; ++ ++ default: ++ dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", ++ __func__, command); ++ } ++} ++ ++static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ /* The hardware does not seem to support multiple ++ * chips per bank. ++ */ ++} ++ ++/* ++ * Write buf to the IFC NAND Controller Data Buffer ++ */ ++static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ unsigned int bufsize = mtd->writesize + mtd->oobsize; ++ ++ if (len <= 0) { ++ dev_err(priv->dev, "%s: len %d bytes", __func__, len); ++ return; ++ } ++ ++ if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { ++ dev_err(priv->dev, ++ "%s: beyond end of buffer (%d requested, %u available)\n", ++ __func__, len, bufsize - ifc_nand_ctrl->index); ++ len = bufsize - ifc_nand_ctrl->index; ++ } ++ ++ memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len); ++ ifc_nand_ctrl->index += len; ++} ++ ++/* ++ * Read a byte from either the IFC hardware buffer ++ * read function for 8-bit buswidth ++ */ ++static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ unsigned int offset; ++ ++ /* ++ * If there are still bytes in the IFC buffer, then use the ++ * next byte. ++ */ ++ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { ++ offset = ifc_nand_ctrl->index++; ++ return ifc_in8(ifc_nand_ctrl->addr + offset); ++ } ++ ++ dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); ++ return ERR_BYTE; ++} ++ ++/* ++ * Read two bytes from the IFC hardware buffer ++ * read function for 16-bit buswith ++ */ ++static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ uint16_t data; ++ ++ /* ++ * If there are still bytes in the IFC buffer, then use the ++ * next byte. ++ */ ++ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { ++ data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); ++ ifc_nand_ctrl->index += 2; ++ return (uint8_t) data; ++ } ++ ++ dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); ++ return ERR_BYTE; ++} ++ ++/* ++ * Read from the IFC Controller Data Buffer ++ */ ++static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ int avail; ++ ++ if (len < 0) { ++ dev_err(priv->dev, "%s: len %d bytes", __func__, len); ++ return; ++ } ++ ++ avail = min((unsigned int)len, ++ ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); ++ memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail); ++ ifc_nand_ctrl->index += avail; ++ ++ if (len > avail) ++ dev_err(priv->dev, ++ "%s: beyond end of buffer (%d requested, %d available)\n", ++ __func__, len, avail); ++} ++ ++/* ++ * This function is called after Program and Erase Operations to ++ * check for success or failure. ++ */ ++static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; ++ u32 nand_fsr; ++ int status; ++ ++ /* Use READ_STATUS command, but wait for the device to be ready */ ++ 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); ++ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc->ifc_nand.nand_fcr0); ++ 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 = ifc_in32(&ifc->ifc_nand.nand_fsr); ++ status = nand_fsr >> 24; ++ /* ++ * The chip always seems to report that it is ++ * write-protected, even when it is not. ++ */ ++ return status | NAND_STATUS_WP; ++} ++ ++/* ++ * The controller does not check for bitflips in erased pages, ++ * therefore software must check instead. ++ */ ++static int check_erased_page(struct nand_chip *chip, u8 *buf) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 *ecc = chip->oob_poi; ++ const int ecc_size = chip->ecc.bytes; ++ const int pkt_size = chip->ecc.size; ++ int i, res, bitflips = 0; ++ struct mtd_oob_region oobregion = { }; ++ ++ mtd_ooblayout_ecc(mtd, 0, &oobregion); ++ ecc += oobregion.offset; ++ ++ for (i = 0; i < chip->ecc.steps; ++i) { ++ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, ++ NULL, 0, ++ chip->ecc.strength); ++ if (res < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += res; ++ ++ bitflips = max(res, bitflips); ++ buf += pkt_size; ++ ecc += ecc_size; ++ } ++ ++ return bitflips; ++} ++ ++static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; ++ ++ nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ if (oob_required) ++ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) { ++ if (!oob_required) ++ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return check_erased_page(chip, buf); ++ } ++ ++ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) ++ mtd->ecc_stats.failed++; ++ ++ return nctrl->max_bitflips; ++} ++ ++/* ECC will be calculated automatically, and errors will be detected in ++ * waitfunc. ++ */ ++static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); ++ ++ dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, ++ chip->numchips); ++ dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, ++ chip->chipsize); ++ dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, ++ chip->pagemask); ++ dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__, ++ chip->chip_delay); ++ dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, ++ chip->badblockpos); ++ dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, ++ chip->chip_shift); ++ dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, ++ chip->page_shift); ++ dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, ++ chip->phys_erase_shift); ++ dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, ++ chip->ecc.mode); ++ dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, ++ chip->ecc.steps); ++ dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, ++ chip->ecc.bytes); ++ dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, ++ chip->ecc.total); ++ dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__, ++ mtd->ooblayout); ++ dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); ++ dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); ++ dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, ++ mtd->erasesize); ++ dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, ++ mtd->writesize); ++ dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, ++ mtd->oobsize); ++ ++ return 0; ++} ++ ++static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) ++{ ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; ++ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; ++ uint32_t csor = 0, csor_8k = 0, csor_ext = 0; ++ uint32_t cs = priv->bank; ++ ++ /* Save CSOR and CSOR_ext */ ++ csor = ifc_in32(&ifc_global->csor_cs[cs].csor); ++ csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); ++ ++ /* chage PageSize 8K and SpareSize 1K*/ ++ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; ++ ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); ++ ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); ++ ++ /* READID */ ++ 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_runtime->ifc_nand.nand_fir0); ++ ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, ++ &ifc_runtime->ifc_nand.nand_fcr0); ++ ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); ++ ++ ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); ++ ++ /* Program ROW0/COL0 */ ++ ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); ++ ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); ++ ++ /* set the chip select for NAND Transaction */ ++ ifc_out32(cs << IFC_NAND_CSEL_SHIFT, ++ &ifc_runtime->ifc_nand.nand_csel); ++ ++ /* start read seq */ ++ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, ++ &ifc_runtime->ifc_nand.nandseq_strt); ++ ++ /* wait for command complete flag or timeout */ ++ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, ++ msecs_to_jiffies(IFC_TIMEOUT_MSECS)); ++ ++ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) ++ printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); ++ ++ /* Restore CSOR and CSOR_ext */ ++ ifc_out32(csor, &ifc_global->csor_cs[cs].csor); ++ ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); ++} ++ ++static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) ++{ ++ struct fsl_ifc_ctrl *ctrl = priv->ctrl; ++ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; ++ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; ++ struct nand_chip *chip = &priv->chip; ++ struct mtd_info *mtd = nand_to_mtd(&priv->chip); ++ u32 csor; ++ ++ /* Fill in fsl_ifc_mtd structure */ ++ mtd->dev.parent = priv->dev; ++ nand_set_flash_node(chip, priv->dev->of_node); ++ ++ /* fill in nand_chip structure */ ++ /* set up function call table */ ++ if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) ++ & CSPR_PORT_SIZE_16) ++ chip->read_byte = fsl_ifc_read_byte16; ++ else ++ chip->read_byte = fsl_ifc_read_byte; ++ ++ chip->write_buf = fsl_ifc_write_buf; ++ chip->read_buf = fsl_ifc_read_buf; ++ chip->select_chip = fsl_ifc_select_chip; ++ chip->cmdfunc = fsl_ifc_cmdfunc; ++ chip->waitfunc = fsl_ifc_wait; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ chip->bbt_td = &bbt_main_descr; ++ chip->bbt_md = &bbt_mirror_descr; ++ ++ ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); ++ ++ /* set up nand options */ ++ chip->bbt_options = NAND_BBT_USE_FLASH; ++ chip->options = NAND_NO_SUBPAGE_WRITE; ++ ++ if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) ++ & CSPR_PORT_SIZE_16) { ++ chip->read_byte = fsl_ifc_read_byte16; ++ chip->options |= NAND_BUSWIDTH_16; ++ } else { ++ chip->read_byte = fsl_ifc_read_byte; ++ } ++ ++ chip->controller = &ifc_nand_ctrl->controller; ++ nand_set_controller_data(chip, priv); ++ ++ chip->ecc.read_page = fsl_ifc_read_page; ++ chip->ecc.write_page = fsl_ifc_write_page; ++ ++ csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); ++ ++ switch (csor & CSOR_NAND_PGS_MASK) { ++ case CSOR_NAND_PGS_512: ++ if (!(chip->options & NAND_BUSWIDTH_16)) { ++ /* Avoid conflict with bad block marker */ ++ bbt_main_descr.offs = 0; ++ bbt_mirror_descr.offs = 0; ++ } ++ ++ priv->bufnum_mask = 15; ++ break; ++ ++ case CSOR_NAND_PGS_2K: ++ priv->bufnum_mask = 3; ++ break; ++ ++ case CSOR_NAND_PGS_4K: ++ priv->bufnum_mask = 1; ++ break; ++ ++ case CSOR_NAND_PGS_8K: ++ priv->bufnum_mask = 0; ++ break; ++ ++ default: ++ dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); ++ return -ENODEV; ++ } ++ ++ /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ ++ if (csor & CSOR_NAND_ECC_DEC_EN) { ++ chip->ecc.mode = NAND_ECC_HW; ++ mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); ++ ++ /* Hardware generates ECC per 512 Bytes */ ++ chip->ecc.size = 512; ++ if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { ++ chip->ecc.bytes = 8; ++ chip->ecc.strength = 4; ++ } else { ++ chip->ecc.bytes = 16; ++ chip->ecc.strength = 8; ++ } ++ } else { ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ } ++ ++ if (ctrl->version >= FSL_IFC_VERSION_1_1_0) ++ fsl_ifc_sram_init(priv); ++ ++ /* ++ * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older ++ * versions which had 8KB. Hence bufnum mask needs to be updated. ++ */ ++ if (ctrl->version >= FSL_IFC_VERSION_2_0_0) ++ priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; ++ ++ return 0; ++} ++ ++static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&priv->chip); ++ ++ nand_release(mtd); ++ ++ kfree(mtd->name); ++ ++ if (priv->vbase) ++ iounmap(priv->vbase); ++ ++ ifc_nand_ctrl->chips[priv->bank] = NULL; ++ ++ return 0; ++} ++ ++static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, ++ phys_addr_t addr) ++{ ++ u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); ++ ++ if (!(cspr & CSPR_V)) ++ return 0; ++ if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) ++ return 0; ++ ++ return (cspr & CSPR_BA) == convert_ifc_address(addr); ++} ++ ++static DEFINE_MUTEX(fsl_ifc_nand_mutex); ++ ++static int fsl_ifc_nand_probe(struct platform_device *dev) ++{ ++ struct fsl_ifc_runtime __iomem *ifc; ++ struct fsl_ifc_mtd *priv; ++ struct resource res; ++ static const char *part_probe_types[] ++ = { "cmdlinepart", "RedBoot", "ofpart", NULL }; ++ int ret; ++ int bank; ++ struct device_node *node = dev->dev.of_node; ++ struct mtd_info *mtd; ++ ++ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) ++ return -ENODEV; ++ ifc = fsl_ifc_ctrl_dev->rregs; ++ ++ /* get, allocate and map the memory resource */ ++ ret = of_address_to_resource(node, 0, &res); ++ if (ret) { ++ dev_err(&dev->dev, "%s: failed to get resource\n", __func__); ++ return ret; ++ } ++ ++ /* find which chip select it is connected to */ ++ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { ++ if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) ++ break; ++ } ++ ++ if (bank >= fsl_ifc_ctrl_dev->banks) { ++ dev_err(&dev->dev, "%s: address did not match any chip selects\n", ++ __func__); ++ return -ENODEV; ++ } ++ ++ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_lock(&fsl_ifc_nand_mutex); ++ if (!fsl_ifc_ctrl_dev->nand) { ++ ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); ++ if (!ifc_nand_ctrl) { ++ mutex_unlock(&fsl_ifc_nand_mutex); ++ return -ENOMEM; ++ } ++ ++ ifc_nand_ctrl->read_bytes = 0; ++ ifc_nand_ctrl->index = 0; ++ ifc_nand_ctrl->addr = NULL; ++ fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; ++ ++ nand_controller_init(&ifc_nand_ctrl->controller); ++ } else { ++ ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; ++ } ++ mutex_unlock(&fsl_ifc_nand_mutex); ++ ++ ifc_nand_ctrl->chips[bank] = priv; ++ priv->bank = bank; ++ priv->ctrl = fsl_ifc_ctrl_dev; ++ priv->dev = &dev->dev; ++ ++ priv->vbase = ioremap(res.start, resource_size(&res)); ++ if (!priv->vbase) { ++ dev_err(priv->dev, "%s: failed to map chip region\n", __func__); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ dev_set_drvdata(priv->dev, priv); ++ ++ 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 */ ++ 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); ++ ++ mtd = nand_to_mtd(&priv->chip); ++ mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); ++ if (!mtd->name) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = fsl_ifc_chip_init(priv); ++ if (ret) ++ goto err; ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ goto err; ++ ++ ret = fsl_ifc_chip_init_tail(mtd); ++ if (ret) ++ goto err; ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ goto err; ++ ++ /* First look for RedBoot table or partitions on the command ++ * line, these take precedence over device tree information */ ++ mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); ++ ++ dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", ++ (unsigned long long)res.start, priv->bank); ++ return 0; ++ ++err: ++ fsl_ifc_chip_remove(priv); ++ return ret; ++} ++ ++static int fsl_ifc_nand_remove(struct platform_device *dev) ++{ ++ struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); ++ ++ fsl_ifc_chip_remove(priv); ++ ++ mutex_lock(&fsl_ifc_nand_mutex); ++ ifc_nand_ctrl->counter--; ++ if (!ifc_nand_ctrl->counter) { ++ fsl_ifc_ctrl_dev->nand = NULL; ++ kfree(ifc_nand_ctrl); ++ } ++ mutex_unlock(&fsl_ifc_nand_mutex); ++ ++ return 0; ++} ++ ++static const struct of_device_id fsl_ifc_nand_match[] = { ++ { ++ .compatible = "fsl,ifc-nand", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); ++ ++static struct platform_driver fsl_ifc_nand_driver = { ++ .driver = { ++ .name = "fsl,ifc-nand", ++ .of_match_table = fsl_ifc_nand_match, ++ }, ++ .probe = fsl_ifc_nand_probe, ++ .remove = fsl_ifc_nand_remove, ++}; ++ ++module_platform_driver(fsl_ifc_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Freescale"); ++MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); +diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c +new file mode 100644 +index 00000000..a88e2cf +--- /dev/null ++++ b/drivers/mtd/nand/raw/fsl_upm.c +@@ -0,0 +1,363 @@ ++/* ++ * Freescale UPM NAND driver. ++ * ++ * Copyright © 2007-2008 MontaVista Software, Inc. ++ * ++ * Author: Anton Vorontsov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FSL_UPM_WAIT_RUN_PATTERN 0x1 ++#define FSL_UPM_WAIT_WRITE_BYTE 0x2 ++#define FSL_UPM_WAIT_WRITE_BUFFER 0x4 ++ ++struct fsl_upm_nand { ++ struct device *dev; ++ struct nand_chip chip; ++ int last_ctrl; ++ struct mtd_partition *parts; ++ struct fsl_upm upm; ++ uint8_t upm_addr_offset; ++ uint8_t upm_cmd_offset; ++ void __iomem *io_base; ++ int rnb_gpio[NAND_MAX_CHIPS]; ++ uint32_t mchip_offsets[NAND_MAX_CHIPS]; ++ uint32_t mchip_count; ++ uint32_t mchip_number; ++ int chip_delay; ++ uint32_t wait_flags; ++}; ++ ++static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) ++{ ++ return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand, ++ chip); ++} ++ ++static int fun_chip_ready(struct mtd_info *mtd) ++{ ++ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); ++ ++ if (gpio_get_value(fun->rnb_gpio[fun->mchip_number])) ++ return 1; ++ ++ dev_vdbg(fun->dev, "busy\n"); ++ return 0; ++} ++ ++static void fun_wait_rnb(struct fsl_upm_nand *fun) ++{ ++ if (fun->rnb_gpio[fun->mchip_number] >= 0) { ++ struct mtd_info *mtd = nand_to_mtd(&fun->chip); ++ int cnt = 1000000; ++ ++ while (--cnt && !fun_chip_ready(mtd)) ++ cpu_relax(); ++ if (!cnt) ++ dev_err(fun->dev, "tired waiting for RNB\n"); ++ } else { ++ ndelay(100); ++ } ++} ++ ++static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); ++ u32 mar; ++ ++ if (!(ctrl & fun->last_ctrl)) { ++ fsl_upm_end_pattern(&fun->upm); ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); ++ } ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if (ctrl & NAND_ALE) ++ fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); ++ else if (ctrl & NAND_CLE) ++ fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); ++ } ++ ++ mar = (cmd << (32 - fun->upm.width)) | ++ fun->mchip_offsets[fun->mchip_number]; ++ fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar); ++ ++ if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) ++ fun_wait_rnb(fun); ++} ++ ++static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); ++ ++ if (mchip_nr == -1) { ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); ++ } else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) { ++ fun->mchip_number = mchip_nr; ++ chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; ++ chip->IO_ADDR_W = chip->IO_ADDR_R; ++ } else { ++ BUG(); ++ } ++} ++ ++static uint8_t fun_read_byte(struct mtd_info *mtd) ++{ ++ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); ++ ++ return in_8(fun->chip.IO_ADDR_R); ++} ++ ++static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); ++ int i; ++ ++ for (i = 0; i < len; i++) ++ buf[i] = in_8(fun->chip.IO_ADDR_R); ++} ++ ++static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); ++ int i; ++ ++ for (i = 0; i < len; i++) { ++ out_8(fun->chip.IO_ADDR_W, buf[i]); ++ if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) ++ fun_wait_rnb(fun); ++ } ++ if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) ++ fun_wait_rnb(fun); ++} ++ ++static int fun_chip_init(struct fsl_upm_nand *fun, ++ const struct device_node *upm_np, ++ const struct resource *io_res) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&fun->chip); ++ int ret; ++ struct device_node *flash_np; ++ ++ fun->chip.IO_ADDR_R = fun->io_base; ++ fun->chip.IO_ADDR_W = fun->io_base; ++ fun->chip.cmd_ctrl = fun_cmd_ctrl; ++ fun->chip.chip_delay = fun->chip_delay; ++ fun->chip.read_byte = fun_read_byte; ++ fun->chip.read_buf = fun_read_buf; ++ fun->chip.write_buf = fun_write_buf; ++ fun->chip.ecc.mode = NAND_ECC_SOFT; ++ fun->chip.ecc.algo = NAND_ECC_HAMMING; ++ if (fun->mchip_count > 1) ++ fun->chip.select_chip = fun_select_chip; ++ ++ if (fun->rnb_gpio[0] >= 0) ++ fun->chip.dev_ready = fun_chip_ready; ++ ++ mtd->dev.parent = fun->dev; ++ ++ flash_np = of_get_next_child(upm_np, NULL); ++ if (!flash_np) ++ return -ENODEV; ++ ++ nand_set_flash_node(&fun->chip, flash_np); ++ mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start, ++ flash_np->name); ++ if (!mtd->name) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = nand_scan(mtd, fun->mchip_count); ++ if (ret) ++ goto err; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++err: ++ of_node_put(flash_np); ++ if (ret) ++ kfree(mtd->name); ++ return ret; ++} ++ ++static int fun_probe(struct platform_device *ofdev) ++{ ++ struct fsl_upm_nand *fun; ++ struct resource io_res; ++ const __be32 *prop; ++ int rnb_gpio; ++ int ret; ++ int size; ++ int i; ++ ++ fun = kzalloc(sizeof(*fun), GFP_KERNEL); ++ if (!fun) ++ return -ENOMEM; ++ ++ ret = of_address_to_resource(ofdev->dev.of_node, 0, &io_res); ++ if (ret) { ++ dev_err(&ofdev->dev, "can't get IO base\n"); ++ goto err1; ++ } ++ ++ ret = fsl_upm_find(io_res.start, &fun->upm); ++ if (ret) { ++ dev_err(&ofdev->dev, "can't find UPM\n"); ++ goto err1; ++ } ++ ++ prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset", ++ &size); ++ if (!prop || size != sizeof(uint32_t)) { ++ dev_err(&ofdev->dev, "can't get UPM address offset\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ fun->upm_addr_offset = *prop; ++ ++ prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size); ++ if (!prop || size != sizeof(uint32_t)) { ++ dev_err(&ofdev->dev, "can't get UPM command offset\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ fun->upm_cmd_offset = *prop; ++ ++ prop = of_get_property(ofdev->dev.of_node, ++ "fsl,upm-addr-line-cs-offsets", &size); ++ if (prop && (size / sizeof(uint32_t)) > 0) { ++ fun->mchip_count = size / sizeof(uint32_t); ++ if (fun->mchip_count >= NAND_MAX_CHIPS) { ++ dev_err(&ofdev->dev, "too much multiple chips\n"); ++ goto err1; ++ } ++ for (i = 0; i < fun->mchip_count; i++) ++ fun->mchip_offsets[i] = be32_to_cpu(prop[i]); ++ } else { ++ fun->mchip_count = 1; ++ } ++ ++ for (i = 0; i < fun->mchip_count; i++) { ++ fun->rnb_gpio[i] = -1; ++ rnb_gpio = of_get_gpio(ofdev->dev.of_node, i); ++ if (rnb_gpio >= 0) { ++ ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev)); ++ if (ret) { ++ dev_err(&ofdev->dev, ++ "can't request RNB gpio #%d\n", i); ++ goto err2; ++ } ++ gpio_direction_input(rnb_gpio); ++ fun->rnb_gpio[i] = rnb_gpio; ++ } else if (rnb_gpio == -EINVAL) { ++ dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); ++ goto err2; ++ } ++ } ++ ++ prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL); ++ if (prop) ++ fun->chip_delay = be32_to_cpup(prop); ++ else ++ fun->chip_delay = 50; ++ ++ prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size); ++ if (prop && size == sizeof(uint32_t)) ++ fun->wait_flags = be32_to_cpup(prop); ++ else ++ fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN | ++ FSL_UPM_WAIT_WRITE_BYTE; ++ ++ fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, ++ resource_size(&io_res)); ++ if (!fun->io_base) { ++ ret = -ENOMEM; ++ goto err2; ++ } ++ ++ fun->dev = &ofdev->dev; ++ fun->last_ctrl = NAND_CLE; ++ ++ ret = fun_chip_init(fun, ofdev->dev.of_node, &io_res); ++ if (ret) ++ goto err2; ++ ++ dev_set_drvdata(&ofdev->dev, fun); ++ ++ return 0; ++err2: ++ for (i = 0; i < fun->mchip_count; i++) { ++ if (fun->rnb_gpio[i] < 0) ++ break; ++ gpio_free(fun->rnb_gpio[i]); ++ } ++err1: ++ kfree(fun); ++ ++ return ret; ++} ++ ++static int fun_remove(struct platform_device *ofdev) ++{ ++ struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); ++ struct mtd_info *mtd = nand_to_mtd(&fun->chip); ++ int i; ++ ++ nand_release(mtd); ++ kfree(mtd->name); ++ ++ for (i = 0; i < fun->mchip_count; i++) { ++ if (fun->rnb_gpio[i] < 0) ++ break; ++ gpio_free(fun->rnb_gpio[i]); ++ } ++ ++ kfree(fun); ++ ++ return 0; ++} ++ ++static const struct of_device_id of_fun_match[] = { ++ { .compatible = "fsl,upm-nand" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, of_fun_match); ++ ++static struct platform_driver of_fun_driver = { ++ .driver = { ++ .name = "fsl,upm-nand", ++ .of_match_table = of_fun_match, ++ }, ++ .probe = fun_probe, ++ .remove = fun_remove, ++}; ++ ++module_platform_driver(of_fun_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Anton Vorontsov "); ++MODULE_DESCRIPTION("Driver for NAND chips working through Freescale " ++ "LocalBus User-Programmable Machine"); +diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c +new file mode 100644 +index 00000000..f49ed46 +--- /dev/null ++++ b/drivers/mtd/nand/raw/fsmc_nand.c +@@ -0,0 +1,1175 @@ ++/* ++ * drivers/mtd/nand/fsmc_nand.c ++ * ++ * ST Microelectronics ++ * Flexible Static Memory Controller (FSMC) ++ * Driver for NAND portions ++ * ++ * Copyright © 2010 ST Microelectronics ++ * Vipin Kumar ++ * Ashish Priyadarshi ++ * ++ * Based on drivers/mtd/nand/nomadik_nand.c ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* fsmc controller registers for NOR flash */ ++#define CTRL 0x0 ++ /* ctrl register definitions */ ++ #define BANK_ENABLE (1 << 0) ++ #define MUXED (1 << 1) ++ #define NOR_DEV (2 << 2) ++ #define WIDTH_8 (0 << 4) ++ #define WIDTH_16 (1 << 4) ++ #define RSTPWRDWN (1 << 6) ++ #define WPROT (1 << 7) ++ #define WRT_ENABLE (1 << 12) ++ #define WAIT_ENB (1 << 13) ++ ++#define CTRL_TIM 0x4 ++ /* ctrl_tim register definitions */ ++ ++#define FSMC_NOR_BANK_SZ 0x8 ++#define FSMC_NOR_REG_SIZE 0x40 ++ ++#define FSMC_NOR_REG(base, bank, reg) (base + \ ++ FSMC_NOR_BANK_SZ * (bank) + \ ++ reg) ++ ++/* fsmc controller registers for NAND flash */ ++#define PC 0x00 ++ /* pc register definitions */ ++ #define FSMC_RESET (1 << 0) ++ #define FSMC_WAITON (1 << 1) ++ #define FSMC_ENABLE (1 << 2) ++ #define FSMC_DEVTYPE_NAND (1 << 3) ++ #define FSMC_DEVWID_8 (0 << 4) ++ #define FSMC_DEVWID_16 (1 << 4) ++ #define FSMC_ECCEN (1 << 6) ++ #define FSMC_ECCPLEN_512 (0 << 7) ++ #define FSMC_ECCPLEN_256 (1 << 7) ++ #define FSMC_TCLR_1 (1) ++ #define FSMC_TCLR_SHIFT (9) ++ #define FSMC_TCLR_MASK (0xF) ++ #define FSMC_TAR_1 (1) ++ #define FSMC_TAR_SHIFT (13) ++ #define FSMC_TAR_MASK (0xF) ++#define STS 0x04 ++ /* sts register definitions */ ++ #define FSMC_CODE_RDY (1 << 15) ++#define COMM 0x08 ++ /* comm register definitions */ ++ #define FSMC_TSET_0 0 ++ #define FSMC_TSET_SHIFT 0 ++ #define FSMC_TSET_MASK 0xFF ++ #define FSMC_TWAIT_6 6 ++ #define FSMC_TWAIT_SHIFT 8 ++ #define FSMC_TWAIT_MASK 0xFF ++ #define FSMC_THOLD_4 4 ++ #define FSMC_THOLD_SHIFT 16 ++ #define FSMC_THOLD_MASK 0xFF ++ #define FSMC_THIZ_1 1 ++ #define FSMC_THIZ_SHIFT 24 ++ #define FSMC_THIZ_MASK 0xFF ++#define ATTRIB 0x0C ++#define IOATA 0x10 ++#define ECC1 0x14 ++#define ECC2 0x18 ++#define ECC3 0x1C ++#define FSMC_NAND_BANK_SZ 0x20 ++ ++#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \ ++ (FSMC_NAND_BANK_SZ * (bank)) + \ ++ reg) ++ ++#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) ++ ++struct fsmc_nand_timings { ++ uint8_t tclr; ++ uint8_t tar; ++ uint8_t thiz; ++ uint8_t thold; ++ uint8_t twait; ++ uint8_t tset; ++}; ++ ++enum access_mode { ++ USE_DMA_ACCESS = 1, ++ USE_WORD_ACCESS, ++}; ++ ++/** ++ * struct fsmc_nand_data - structure for FSMC NAND device state ++ * ++ * @pid: Part ID on the AMBA PrimeCell format ++ * @mtd: MTD info for a NAND flash. ++ * @nand: Chip related info for a NAND flash. ++ * @partitions: Partition info for a NAND Flash. ++ * @nr_partitions: Total number of partition of a NAND flash. ++ * ++ * @bank: Bank number for probed device. ++ * @clk: Clock structure for FSMC. ++ * ++ * @read_dma_chan: DMA channel for read access ++ * @write_dma_chan: DMA channel for write access to NAND ++ * @dma_access_complete: Completion structure ++ * ++ * @data_pa: NAND Physical port for Data. ++ * @data_va: NAND port for Data. ++ * @cmd_va: NAND port for Command. ++ * @addr_va: NAND port for Address. ++ * @regs_va: FSMC regs base address. ++ */ ++struct fsmc_nand_data { ++ u32 pid; ++ struct nand_chip nand; ++ ++ unsigned int bank; ++ struct device *dev; ++ enum access_mode mode; ++ struct clk *clk; ++ ++ /* DMA related objects */ ++ struct dma_chan *read_dma_chan; ++ struct dma_chan *write_dma_chan; ++ struct completion dma_access_complete; ++ ++ struct fsmc_nand_timings *dev_timings; ++ ++ dma_addr_t data_pa; ++ void __iomem *data_va; ++ void __iomem *cmd_va; ++ void __iomem *addr_va; ++ void __iomem *regs_va; ++}; ++ ++static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 2; ++ oobregion->length = 3; ++ ++ return 0; ++} ++ ++static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 8; ++ ++ if (section < chip->ecc.steps - 1) ++ oobregion->length = 8; ++ else ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = { ++ .ecc = fsmc_ecc1_ooblayout_ecc, ++ .free = fsmc_ecc1_ooblayout_free, ++}; ++ ++/* ++ * ECC placement definitions in oobfree type format. ++ * There are 13 bytes of ecc for every 512 byte block and it has to be read ++ * consecutively and immediately after the 512 byte data block for hardware to ++ * generate the error bit offsets in 512 byte data. ++ */ ++static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->length = chip->ecc.bytes; ++ ++ if (!section && mtd->writesize <= 512) ++ oobregion->offset = 0; ++ else ++ oobregion->offset = (section * 16) + 2; ++ ++ return 0; ++} ++ ++static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 15; ++ ++ if (section < chip->ecc.steps - 1) ++ oobregion->length = 3; ++ else ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { ++ .ecc = fsmc_ecc4_ooblayout_ecc, ++ .free = fsmc_ecc4_ooblayout_free, ++}; ++ ++static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand); ++} ++ ++/* ++ * fsmc_cmd_ctrl - For facilitaing Hardware access ++ * This routine allows hardware specific access to control-lines(ALE,CLE) ++ */ ++static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ void __iomem *regs = host->regs_va; ++ unsigned int bank = host->bank; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u32 pc; ++ ++ if (ctrl & NAND_CLE) { ++ this->IO_ADDR_R = host->cmd_va; ++ this->IO_ADDR_W = host->cmd_va; ++ } else if (ctrl & NAND_ALE) { ++ this->IO_ADDR_R = host->addr_va; ++ this->IO_ADDR_W = host->addr_va; ++ } else { ++ this->IO_ADDR_R = host->data_va; ++ this->IO_ADDR_W = host->data_va; ++ } ++ ++ pc = readl(FSMC_NAND_REG(regs, bank, PC)); ++ if (ctrl & NAND_NCE) ++ pc |= FSMC_ENABLE; ++ else ++ pc &= ~FSMC_ENABLE; ++ writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC)); ++ } ++ ++ mb(); ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb_relaxed(cmd, this->IO_ADDR_W); ++} ++ ++/* ++ * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine ++ * ++ * This routine initializes timing parameters related to NAND memory access in ++ * FSMC registers ++ */ ++static void fsmc_nand_setup(struct fsmc_nand_data *host, ++ struct fsmc_nand_timings *tims) ++{ ++ uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; ++ uint32_t tclr, tar, thiz, thold, twait, tset; ++ unsigned int bank = host->bank; ++ void __iomem *regs = host->regs_va; ++ ++ tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; ++ tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; ++ thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; ++ thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; ++ twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; ++ tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; ++ ++ if (host->nand.options & NAND_BUSWIDTH_16) ++ writel_relaxed(value | FSMC_DEVWID_16, ++ FSMC_NAND_REG(regs, bank, PC)); ++ else ++ writel_relaxed(value | FSMC_DEVWID_8, ++ FSMC_NAND_REG(regs, bank, PC)); ++ ++ writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, ++ FSMC_NAND_REG(regs, bank, PC)); ++ writel_relaxed(thiz | thold | twait | tset, ++ FSMC_NAND_REG(regs, bank, COMM)); ++ writel_relaxed(thiz | thold | twait | tset, ++ FSMC_NAND_REG(regs, bank, ATTRIB)); ++} ++ ++static int fsmc_calc_timings(struct fsmc_nand_data *host, ++ const struct nand_sdr_timings *sdrt, ++ struct fsmc_nand_timings *tims) ++{ ++ unsigned long hclk = clk_get_rate(host->clk); ++ unsigned long hclkn = NSEC_PER_SEC / hclk; ++ uint32_t thiz, thold, twait, tset; ++ ++ if (sdrt->tRC_min < 30000) ++ return -EOPNOTSUPP; ++ ++ tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1; ++ if (tims->tar > FSMC_TAR_MASK) ++ tims->tar = FSMC_TAR_MASK; ++ tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1; ++ if (tims->tclr > FSMC_TCLR_MASK) ++ tims->tclr = FSMC_TCLR_MASK; ++ ++ thiz = sdrt->tCS_min - sdrt->tWP_min; ++ tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn); ++ ++ thold = sdrt->tDH_min; ++ if (thold < sdrt->tCH_min) ++ thold = sdrt->tCH_min; ++ if (thold < sdrt->tCLH_min) ++ thold = sdrt->tCLH_min; ++ if (thold < sdrt->tWH_min) ++ thold = sdrt->tWH_min; ++ if (thold < sdrt->tALH_min) ++ thold = sdrt->tALH_min; ++ if (thold < sdrt->tREH_min) ++ thold = sdrt->tREH_min; ++ tims->thold = DIV_ROUND_UP(thold / 1000, hclkn); ++ if (tims->thold == 0) ++ tims->thold = 1; ++ else if (tims->thold > FSMC_THOLD_MASK) ++ tims->thold = FSMC_THOLD_MASK; ++ ++ twait = max(sdrt->tRP_min, sdrt->tWP_min); ++ tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1; ++ if (tims->twait == 0) ++ tims->twait = 1; ++ else if (tims->twait > FSMC_TWAIT_MASK) ++ tims->twait = FSMC_TWAIT_MASK; ++ ++ tset = max(sdrt->tCS_min - sdrt->tWP_min, ++ sdrt->tCEA_max - sdrt->tREA_max); ++ tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1; ++ if (tims->tset == 0) ++ tims->tset = 1; ++ else if (tims->tset > FSMC_TSET_MASK) ++ tims->tset = FSMC_TSET_MASK; ++ ++ return 0; ++} ++ ++static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct fsmc_nand_data *host = nand_get_controller_data(nand); ++ struct fsmc_nand_timings tims; ++ const struct nand_sdr_timings *sdrt; ++ int ret; ++ ++ sdrt = nand_get_sdr_timings(conf); ++ if (IS_ERR(sdrt)) ++ return PTR_ERR(sdrt); ++ ++ ret = fsmc_calc_timings(host, sdrt, &tims); ++ if (ret) ++ return ret; ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ fsmc_nand_setup(host, &tims); ++ ++ return 0; ++} ++ ++/* ++ * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers ++ */ ++static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ void __iomem *regs = host->regs_va; ++ uint32_t bank = host->bank; ++ ++ writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, ++ FSMC_NAND_REG(regs, bank, PC)); ++ writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, ++ FSMC_NAND_REG(regs, bank, PC)); ++ writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, ++ FSMC_NAND_REG(regs, bank, PC)); ++} ++ ++/* ++ * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by ++ * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to ++ * max of 8-bits) ++ */ ++static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, ++ uint8_t *ecc) ++{ ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ void __iomem *regs = host->regs_va; ++ uint32_t bank = host->bank; ++ uint32_t ecc_tmp; ++ unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; ++ ++ do { ++ if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) ++ break; ++ else ++ cond_resched(); ++ } while (!time_after_eq(jiffies, deadline)); ++ ++ if (time_after_eq(jiffies, deadline)) { ++ dev_err(host->dev, "calculate ecc timed out\n"); ++ return -ETIMEDOUT; ++ } ++ ++ ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); ++ ecc[0] = (uint8_t) (ecc_tmp >> 0); ++ ecc[1] = (uint8_t) (ecc_tmp >> 8); ++ ecc[2] = (uint8_t) (ecc_tmp >> 16); ++ ecc[3] = (uint8_t) (ecc_tmp >> 24); ++ ++ ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); ++ ecc[4] = (uint8_t) (ecc_tmp >> 0); ++ ecc[5] = (uint8_t) (ecc_tmp >> 8); ++ ecc[6] = (uint8_t) (ecc_tmp >> 16); ++ ecc[7] = (uint8_t) (ecc_tmp >> 24); ++ ++ ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); ++ ecc[8] = (uint8_t) (ecc_tmp >> 0); ++ ecc[9] = (uint8_t) (ecc_tmp >> 8); ++ ecc[10] = (uint8_t) (ecc_tmp >> 16); ++ ecc[11] = (uint8_t) (ecc_tmp >> 24); ++ ++ ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); ++ ecc[12] = (uint8_t) (ecc_tmp >> 16); ++ ++ return 0; ++} ++ ++/* ++ * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by ++ * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to ++ * max of 1-bit) ++ */ ++static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, ++ uint8_t *ecc) ++{ ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ void __iomem *regs = host->regs_va; ++ uint32_t bank = host->bank; ++ uint32_t ecc_tmp; ++ ++ ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); ++ ecc[0] = (uint8_t) (ecc_tmp >> 0); ++ ecc[1] = (uint8_t) (ecc_tmp >> 8); ++ ecc[2] = (uint8_t) (ecc_tmp >> 16); ++ ++ return 0; ++} ++ ++/* Count the number of 0's in buff upto a max of max_bits */ ++static int count_written_bits(uint8_t *buff, int size, int max_bits) ++{ ++ int k, written_bits = 0; ++ ++ for (k = 0; k < size; k++) { ++ written_bits += hweight8(~buff[k]); ++ if (written_bits > max_bits) ++ break; ++ } ++ ++ return written_bits; ++} ++ ++static void dma_complete(void *param) ++{ ++ struct fsmc_nand_data *host = param; ++ ++ complete(&host->dma_access_complete); ++} ++ ++static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, ++ enum dma_data_direction direction) ++{ ++ struct dma_chan *chan; ++ struct dma_device *dma_dev; ++ struct dma_async_tx_descriptor *tx; ++ dma_addr_t dma_dst, dma_src, dma_addr; ++ dma_cookie_t cookie; ++ unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; ++ int ret; ++ unsigned long time_left; ++ ++ if (direction == DMA_TO_DEVICE) ++ chan = host->write_dma_chan; ++ else if (direction == DMA_FROM_DEVICE) ++ chan = host->read_dma_chan; ++ else ++ return -EINVAL; ++ ++ dma_dev = chan->device; ++ dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); ++ ++ if (direction == DMA_TO_DEVICE) { ++ dma_src = dma_addr; ++ dma_dst = host->data_pa; ++ } else { ++ dma_src = host->data_pa; ++ dma_dst = dma_addr; ++ } ++ ++ tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, ++ len, flags); ++ if (!tx) { ++ dev_err(host->dev, "device_prep_dma_memcpy error\n"); ++ ret = -EIO; ++ goto unmap_dma; ++ } ++ ++ tx->callback = dma_complete; ++ tx->callback_param = host; ++ cookie = tx->tx_submit(tx); ++ ++ ret = dma_submit_error(cookie); ++ if (ret) { ++ dev_err(host->dev, "dma_submit_error %d\n", cookie); ++ goto unmap_dma; ++ } ++ ++ dma_async_issue_pending(chan); ++ ++ time_left = ++ wait_for_completion_timeout(&host->dma_access_complete, ++ msecs_to_jiffies(3000)); ++ if (time_left == 0) { ++ dmaengine_terminate_all(chan); ++ dev_err(host->dev, "wait_for_completion_timeout\n"); ++ ret = -ETIMEDOUT; ++ goto unmap_dma; ++ } ++ ++ ret = 0; ++ ++unmap_dma: ++ dma_unmap_single(dma_dev->dev, dma_addr, len, direction); ++ ++ return ret; ++} ++ ++/* ++ * fsmc_write_buf - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && ++ IS_ALIGNED(len, sizeof(uint32_t))) { ++ uint32_t *p = (uint32_t *)buf; ++ len = len >> 2; ++ for (i = 0; i < len; i++) ++ writel_relaxed(p[i], chip->IO_ADDR_W); ++ } else { ++ for (i = 0; i < len; i++) ++ writeb_relaxed(buf[i], chip->IO_ADDR_W); ++ } ++} ++ ++/* ++ * fsmc_read_buf - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && ++ IS_ALIGNED(len, sizeof(uint32_t))) { ++ uint32_t *p = (uint32_t *)buf; ++ len = len >> 2; ++ for (i = 0; i < len; i++) ++ p[i] = readl_relaxed(chip->IO_ADDR_R); ++ } else { ++ for (i = 0; i < len; i++) ++ buf[i] = readb_relaxed(chip->IO_ADDR_R); ++ } ++} ++ ++/* ++ * fsmc_read_buf_dma - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ ++ dma_xfer(host, buf, len, DMA_FROM_DEVICE); ++} ++ ++/* ++ * fsmc_write_buf_dma - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ ++ dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); ++} ++ ++/* ++ * fsmc_read_page_hwecc ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller expects OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * This routine is needed for fsmc version 8 as reading from NAND chip has to be ++ * performed in a strict sequence as follows: ++ * data(512 byte) -> ecc(13 byte) ++ * After this read, fsmc hardware generates and reports error data bits(up to a ++ * max of 8 bits) ++ */ ++static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int i, j, s, stat, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ uint8_t *ecc_code = chip->ecc.code_buf; ++ int off, len, group = 0; ++ /* ++ * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we ++ * end up reading 14 bytes (7 words) from oob. The local array is ++ * to maintain word alignment ++ */ ++ uint16_t ecc_oob[7]; ++ uint8_t *oob = (uint8_t *)&ecc_oob[0]; ++ unsigned int max_bitflips = 0; ++ ++ for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { ++ nand_read_page_op(chip, page, s * eccsize, NULL, 0); ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ ++ for (j = 0; j < eccbytes;) { ++ struct mtd_oob_region oobregion; ++ int ret; ++ ++ ret = mtd_ooblayout_ecc(mtd, group++, &oobregion); ++ if (ret) ++ return ret; ++ ++ off = oobregion.offset; ++ len = oobregion.length; ++ ++ /* ++ * length is intentionally kept a higher multiple of 2 ++ * to read at least 13 bytes even in case of 16 bit NAND ++ * devices ++ */ ++ if (chip->options & NAND_BUSWIDTH_16) ++ len = roundup(len, 2); ++ ++ nand_read_oob_op(chip, page, off, oob + j, len); ++ j += len; ++ } ++ ++ memcpy(&ecc_code[i], oob, chip->ecc.bytes); ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ ++ return max_bitflips; ++} ++ ++/* ++ * fsmc_bch8_correct_data ++ * @mtd: mtd info structure ++ * @dat: buffer of read data ++ * @read_ecc: ecc read from device spare area ++ * @calc_ecc: ecc calculated from read data ++ * ++ * calc_ecc is a 104 bit information containing maximum of 8 error ++ * offset informations of 13 bits each in 512 bytes of read data. ++ */ ++static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, ++ uint8_t *read_ecc, uint8_t *calc_ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fsmc_nand_data *host = mtd_to_fsmc(mtd); ++ void __iomem *regs = host->regs_va; ++ unsigned int bank = host->bank; ++ uint32_t err_idx[8]; ++ uint32_t num_err, i; ++ uint32_t ecc1, ecc2, ecc3, ecc4; ++ ++ num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; ++ ++ /* no bit flipping */ ++ if (likely(num_err == 0)) ++ return 0; ++ ++ /* too many errors */ ++ if (unlikely(num_err > 8)) { ++ /* ++ * This is a temporary erase check. A newly erased page read ++ * would result in an ecc error because the oob data is also ++ * erased to FF and the calculated ecc for an FF data is not ++ * FF..FF. ++ * This is a workaround to skip performing correction in case ++ * data is FF..FF ++ * ++ * Logic: ++ * For every page, each bit written as 0 is counted until these ++ * number of bits are greater than 8 (the maximum correction ++ * capability of FSMC for each 512 + 13 bytes) ++ */ ++ ++ int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); ++ int bits_data = count_written_bits(dat, chip->ecc.size, 8); ++ ++ if ((bits_ecc + bits_data) <= 8) { ++ if (bits_data) ++ memset(dat, 0xff, chip->ecc.size); ++ return bits_data; ++ } ++ ++ return -EBADMSG; ++ } ++ ++ /* ++ * ------------------- calc_ecc[] bit wise -----------|--13 bits--| ++ * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--| ++ * ++ * calc_ecc is a 104 bit information containing maximum of 8 error ++ * offset informations of 13 bits each. calc_ecc is copied into a ++ * uint64_t array and error offset indexes are populated in err_idx ++ * array ++ */ ++ ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1)); ++ ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2)); ++ ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3)); ++ ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); ++ ++ err_idx[0] = (ecc1 >> 0) & 0x1FFF; ++ err_idx[1] = (ecc1 >> 13) & 0x1FFF; ++ err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); ++ err_idx[3] = (ecc2 >> 7) & 0x1FFF; ++ err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); ++ err_idx[5] = (ecc3 >> 1) & 0x1FFF; ++ err_idx[6] = (ecc3 >> 14) & 0x1FFF; ++ err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); ++ ++ i = 0; ++ while (num_err--) { ++ change_bit(0, (unsigned long *)&err_idx[i]); ++ change_bit(1, (unsigned long *)&err_idx[i]); ++ ++ if (err_idx[i] < chip->ecc.size * 8) { ++ change_bit(err_idx[i], (unsigned long *)dat); ++ i++; ++ } ++ } ++ return i; ++} ++ ++static bool filter(struct dma_chan *chan, void *slave) ++{ ++ chan->private = slave; ++ return true; ++} ++ ++static int fsmc_nand_probe_config_dt(struct platform_device *pdev, ++ struct fsmc_nand_data *host, ++ struct nand_chip *nand) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ u32 val; ++ int ret; ++ ++ nand->options = 0; ++ ++ if (!of_property_read_u32(np, "bank-width", &val)) { ++ if (val == 2) { ++ nand->options |= NAND_BUSWIDTH_16; ++ } else if (val != 1) { ++ dev_err(&pdev->dev, "invalid bank-width %u\n", val); ++ return -EINVAL; ++ } ++ } ++ ++ if (of_get_property(np, "nand-skip-bbtscan", NULL)) ++ nand->options |= NAND_SKIP_BBTSCAN; ++ ++ host->dev_timings = devm_kzalloc(&pdev->dev, ++ sizeof(*host->dev_timings), GFP_KERNEL); ++ if (!host->dev_timings) ++ return -ENOMEM; ++ ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings, ++ sizeof(*host->dev_timings)); ++ if (ret) ++ host->dev_timings = NULL; ++ ++ /* Set default NAND bank to 0 */ ++ host->bank = 0; ++ if (!of_property_read_u32(np, "bank", &val)) { ++ if (val > 3) { ++ dev_err(&pdev->dev, "invalid bank %u\n", val); ++ return -EINVAL; ++ } ++ host->bank = val; ++ } ++ return 0; ++} ++ ++/* ++ * fsmc_nand_probe - Probe function ++ * @pdev: platform device structure ++ */ ++static int __init fsmc_nand_probe(struct platform_device *pdev) ++{ ++ struct fsmc_nand_data *host; ++ struct mtd_info *mtd; ++ struct nand_chip *nand; ++ struct resource *res; ++ dma_cap_mask_t mask; ++ int ret = 0; ++ u32 pid; ++ int i; ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ nand = &host->nand; ++ ++ ret = fsmc_nand_probe_config_dt(pdev, host, nand); ++ if (ret) ++ return ret; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); ++ host->data_va = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(host->data_va)) ++ return PTR_ERR(host->data_va); ++ ++ host->data_pa = (dma_addr_t)res->start; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); ++ host->addr_va = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(host->addr_va)) ++ return PTR_ERR(host->addr_va); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); ++ host->cmd_va = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(host->cmd_va)) ++ return PTR_ERR(host->cmd_va); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); ++ host->regs_va = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(host->regs_va)) ++ return PTR_ERR(host->regs_va); ++ ++ host->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(host->clk)) { ++ dev_err(&pdev->dev, "failed to fetch block clock\n"); ++ return PTR_ERR(host->clk); ++ } ++ ++ ret = clk_prepare_enable(host->clk); ++ if (ret) ++ return ret; ++ ++ /* ++ * This device ID is actually a common AMBA ID as used on the ++ * AMBA PrimeCell bus. However it is not a PrimeCell. ++ */ ++ for (pid = 0, i = 0; i < 4; i++) ++ pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8); ++ host->pid = pid; ++ dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, " ++ "revision %02x, config %02x\n", ++ AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid), ++ AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid)); ++ ++ host->dev = &pdev->dev; ++ ++ if (host->mode == USE_DMA_ACCESS) ++ init_completion(&host->dma_access_complete); ++ ++ /* Link all private pointers */ ++ mtd = nand_to_mtd(&host->nand); ++ nand_set_controller_data(nand, host); ++ nand_set_flash_node(nand, pdev->dev.of_node); ++ ++ mtd->dev.parent = &pdev->dev; ++ nand->IO_ADDR_R = host->data_va; ++ nand->IO_ADDR_W = host->data_va; ++ nand->cmd_ctrl = fsmc_cmd_ctrl; ++ nand->chip_delay = 30; ++ ++ /* ++ * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() ++ * can overwrite this value if the DT provides a different value. ++ */ ++ nand->ecc.mode = NAND_ECC_HW; ++ nand->ecc.hwctl = fsmc_enable_hwecc; ++ nand->ecc.size = 512; ++ nand->badblockbits = 7; ++ ++ switch (host->mode) { ++ case USE_DMA_ACCESS: ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_MEMCPY, mask); ++ host->read_dma_chan = dma_request_channel(mask, filter, NULL); ++ if (!host->read_dma_chan) { ++ dev_err(&pdev->dev, "Unable to get read dma channel\n"); ++ goto err_req_read_chnl; ++ } ++ host->write_dma_chan = dma_request_channel(mask, filter, NULL); ++ if (!host->write_dma_chan) { ++ dev_err(&pdev->dev, "Unable to get write dma channel\n"); ++ goto err_req_write_chnl; ++ } ++ nand->read_buf = fsmc_read_buf_dma; ++ nand->write_buf = fsmc_write_buf_dma; ++ break; ++ ++ default: ++ case USE_WORD_ACCESS: ++ nand->read_buf = fsmc_read_buf; ++ nand->write_buf = fsmc_write_buf; ++ break; ++ } ++ ++ if (host->dev_timings) ++ fsmc_nand_setup(host, host->dev_timings); ++ else ++ nand->setup_data_interface = fsmc_setup_data_interface; ++ ++ if (AMBA_REV_BITS(host->pid) >= 8) { ++ nand->ecc.read_page = fsmc_read_page_hwecc; ++ nand->ecc.calculate = fsmc_read_hwecc_ecc4; ++ nand->ecc.correct = fsmc_bch8_correct_data; ++ nand->ecc.bytes = 13; ++ nand->ecc.strength = 8; ++ } ++ ++ /* ++ * Scan to find existence of the device ++ */ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) { ++ dev_err(&pdev->dev, "No NAND Device found!\n"); ++ goto err_scan_ident; ++ } ++ ++ if (AMBA_REV_BITS(host->pid) >= 8) { ++ switch (mtd->oobsize) { ++ case 16: ++ case 64: ++ case 128: ++ case 224: ++ case 256: ++ break; ++ default: ++ dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", ++ mtd->oobsize); ++ ret = -EINVAL; ++ goto err_probe; ++ } ++ ++ mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); ++ } else { ++ switch (nand->ecc.mode) { ++ case NAND_ECC_HW: ++ dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n"); ++ nand->ecc.calculate = fsmc_read_hwecc_ecc1; ++ nand->ecc.correct = nand_correct_data; ++ nand->ecc.bytes = 3; ++ nand->ecc.strength = 1; ++ break; ++ ++ case NAND_ECC_SOFT: ++ if (nand->ecc.algo == NAND_ECC_BCH) { ++ dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); ++ break; ++ } ++ ++ case NAND_ECC_ON_DIE: ++ break; ++ ++ default: ++ dev_err(&pdev->dev, "Unsupported ECC mode!\n"); ++ goto err_probe; ++ } ++ ++ /* ++ * Don't set layout for BCH4 SW ECC. This will be ++ * generated later in nand_bch_init() later. ++ */ ++ if (nand->ecc.mode == NAND_ECC_HW) { ++ switch (mtd->oobsize) { ++ case 16: ++ case 64: ++ case 128: ++ mtd_set_ooblayout(mtd, ++ &fsmc_ecc1_ooblayout_ops); ++ break; ++ default: ++ dev_warn(&pdev->dev, ++ "No oob scheme defined for oobsize %d\n", ++ mtd->oobsize); ++ ret = -EINVAL; ++ goto err_probe; ++ } ++ } ++ } ++ ++ /* Second stage of scan to fill MTD data-structures */ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ goto err_probe; ++ ++ mtd->name = "nand"; ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) ++ goto err_probe; ++ ++ platform_set_drvdata(pdev, host); ++ dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); ++ return 0; ++ ++err_probe: ++err_scan_ident: ++ if (host->mode == USE_DMA_ACCESS) ++ dma_release_channel(host->write_dma_chan); ++err_req_write_chnl: ++ if (host->mode == USE_DMA_ACCESS) ++ dma_release_channel(host->read_dma_chan); ++err_req_read_chnl: ++ clk_disable_unprepare(host->clk); ++ return ret; ++} ++ ++/* ++ * Clean up routine ++ */ ++static int fsmc_nand_remove(struct platform_device *pdev) ++{ ++ struct fsmc_nand_data *host = platform_get_drvdata(pdev); ++ ++ if (host) { ++ nand_release(nand_to_mtd(&host->nand)); ++ ++ if (host->mode == USE_DMA_ACCESS) { ++ dma_release_channel(host->write_dma_chan); ++ dma_release_channel(host->read_dma_chan); ++ } ++ clk_disable_unprepare(host->clk); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int fsmc_nand_suspend(struct device *dev) ++{ ++ struct fsmc_nand_data *host = dev_get_drvdata(dev); ++ if (host) ++ clk_disable_unprepare(host->clk); ++ return 0; ++} ++ ++static int fsmc_nand_resume(struct device *dev) ++{ ++ struct fsmc_nand_data *host = dev_get_drvdata(dev); ++ if (host) { ++ clk_prepare_enable(host->clk); ++ if (host->dev_timings) ++ fsmc_nand_setup(host, host->dev_timings); ++ } ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); ++ ++static const struct of_device_id fsmc_nand_id_table[] = { ++ { .compatible = "st,spear600-fsmc-nand" }, ++ { .compatible = "stericsson,fsmc-nand" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, fsmc_nand_id_table); ++ ++static struct platform_driver fsmc_nand_driver = { ++ .remove = fsmc_nand_remove, ++ .driver = { ++ .name = "fsmc-nand", ++ .of_match_table = fsmc_nand_id_table, ++ .pm = &fsmc_nand_pm_ops, ++ }, ++}; ++ ++module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Vipin Kumar , Ashish Priyadarshi"); ++MODULE_DESCRIPTION("NAND driver for SPEAr Platforms"); +diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c +new file mode 100644 +index 00000000..fd36489 +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpio.c +@@ -0,0 +1,327 @@ ++/* ++ * drivers/mtd/nand/gpio.c ++ * ++ * Updated, and converted to generic GPIO based driver by Russell King. ++ * ++ * Written by Ben Dooks ++ * Based on 2.4 version by Mark Whittaker ++ * ++ * © 2004 Simtec Electronics ++ * ++ * Device driver for NAND flash that uses a memory mapped interface to ++ * read/write the NAND commands and data, and GPIO pins for control signals ++ * (the DT binding refers to this as "GPIO assisted NAND flash") ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct gpiomtd { ++ void __iomem *io_sync; ++ struct nand_chip nand_chip; ++ struct gpio_nand_platdata plat; ++}; ++ ++static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip); ++} ++ ++ ++#ifdef CONFIG_ARM ++/* gpio_nand_dosync() ++ * ++ * Make sure the GPIO state changes occur in-order with writes to NAND ++ * memory region. ++ * Needed on PXA due to bus-reordering within the SoC itself (see section on ++ * I/O ordering in PXA manual (section 2.3, p35) ++ */ ++static void gpio_nand_dosync(struct gpiomtd *gpiomtd) ++{ ++ unsigned long tmp; ++ ++ if (gpiomtd->io_sync) { ++ /* ++ * Linux memory barriers don't cater for what's required here. ++ * What's required is what's here - a read from a separate ++ * region with a dependency on that read. ++ */ ++ tmp = readl(gpiomtd->io_sync); ++ asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); ++ } ++} ++#else ++static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} ++#endif ++ ++static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); ++ ++ gpio_nand_dosync(gpiomtd); ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if (gpio_is_valid(gpiomtd->plat.gpio_nce)) ++ gpio_set_value(gpiomtd->plat.gpio_nce, ++ !(ctrl & NAND_NCE)); ++ gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE)); ++ gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE)); ++ gpio_nand_dosync(gpiomtd); ++ } ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W); ++ gpio_nand_dosync(gpiomtd); ++} ++ ++static int gpio_nand_devready(struct mtd_info *mtd) ++{ ++ struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); ++ ++ return gpio_get_value(gpiomtd->plat.gpio_rdy); ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id gpio_nand_id_table[] = { ++ { .compatible = "gpio-control-nand" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, gpio_nand_id_table); ++ ++static int gpio_nand_get_config_of(const struct device *dev, ++ struct gpio_nand_platdata *plat) ++{ ++ u32 val; ++ ++ if (!dev->of_node) ++ return -ENODEV; ++ ++ if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { ++ if (val == 2) { ++ plat->options |= NAND_BUSWIDTH_16; ++ } else if (val != 1) { ++ dev_err(dev, "invalid bank-width %u\n", val); ++ return -EINVAL; ++ } ++ } ++ ++ plat->gpio_rdy = of_get_gpio(dev->of_node, 0); ++ plat->gpio_nce = of_get_gpio(dev->of_node, 1); ++ plat->gpio_ale = of_get_gpio(dev->of_node, 2); ++ plat->gpio_cle = of_get_gpio(dev->of_node, 3); ++ plat->gpio_nwp = of_get_gpio(dev->of_node, 4); ++ ++ if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) ++ plat->chip_delay = val; ++ ++ return 0; ++} ++ ++static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) ++{ ++ struct resource *r; ++ u64 addr; ++ ++ if (of_property_read_u64(pdev->dev.of_node, ++ "gpio-control-nand,io-sync-reg", &addr)) ++ return NULL; ++ ++ r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); ++ if (!r) ++ return NULL; ++ ++ r->start = addr; ++ r->end = r->start + 0x3; ++ r->flags = IORESOURCE_MEM; ++ ++ return r; ++} ++#else /* CONFIG_OF */ ++static inline int gpio_nand_get_config_of(const struct device *dev, ++ struct gpio_nand_platdata *plat) ++{ ++ return -ENOSYS; ++} ++ ++static inline struct resource * ++gpio_nand_get_io_sync_of(struct platform_device *pdev) ++{ ++ return NULL; ++} ++#endif /* CONFIG_OF */ ++ ++static inline int gpio_nand_get_config(const struct device *dev, ++ struct gpio_nand_platdata *plat) ++{ ++ int ret = gpio_nand_get_config_of(dev, plat); ++ ++ if (!ret) ++ return ret; ++ ++ if (dev_get_platdata(dev)) { ++ memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static inline struct resource * ++gpio_nand_get_io_sync(struct platform_device *pdev) ++{ ++ struct resource *r = gpio_nand_get_io_sync_of(pdev); ++ ++ if (r) ++ return r; ++ ++ return platform_get_resource(pdev, IORESOURCE_MEM, 1); ++} ++ ++static int gpio_nand_remove(struct platform_device *pdev) ++{ ++ struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); ++ ++ nand_release(nand_to_mtd(&gpiomtd->nand_chip)); ++ ++ if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) ++ gpio_set_value(gpiomtd->plat.gpio_nwp, 0); ++ if (gpio_is_valid(gpiomtd->plat.gpio_nce)) ++ gpio_set_value(gpiomtd->plat.gpio_nce, 1); ++ ++ return 0; ++} ++ ++static int gpio_nand_probe(struct platform_device *pdev) ++{ ++ struct gpiomtd *gpiomtd; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct resource *res; ++ int ret = 0; ++ ++ if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) ++ return -EINVAL; ++ ++ gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); ++ if (!gpiomtd) ++ return -ENOMEM; ++ ++ chip = &gpiomtd->nand_chip; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(chip->IO_ADDR_R)) ++ return PTR_ERR(chip->IO_ADDR_R); ++ ++ res = gpio_nand_get_io_sync(pdev); ++ if (res) { ++ gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(gpiomtd->io_sync)) ++ return PTR_ERR(gpiomtd->io_sync); ++ } ++ ++ ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); ++ if (ret) ++ return ret; ++ ++ if (gpio_is_valid(gpiomtd->plat.gpio_nce)) { ++ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, ++ "NAND NCE"); ++ if (ret) ++ return ret; ++ gpio_direction_output(gpiomtd->plat.gpio_nce, 1); ++ } ++ ++ if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { ++ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, ++ "NAND NWP"); ++ if (ret) ++ return ret; ++ } ++ ++ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); ++ if (ret) ++ return ret; ++ gpio_direction_output(gpiomtd->plat.gpio_ale, 0); ++ ++ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); ++ if (ret) ++ return ret; ++ gpio_direction_output(gpiomtd->plat.gpio_cle, 0); ++ ++ if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { ++ ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, ++ "NAND RDY"); ++ if (ret) ++ return ret; ++ gpio_direction_input(gpiomtd->plat.gpio_rdy); ++ chip->dev_ready = gpio_nand_devready; ++ } ++ ++ nand_set_flash_node(chip, pdev->dev.of_node); ++ chip->IO_ADDR_W = chip->IO_ADDR_R; ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ chip->options = gpiomtd->plat.options; ++ chip->chip_delay = gpiomtd->plat.chip_delay; ++ chip->cmd_ctrl = gpio_nand_cmd_ctrl; ++ ++ mtd = nand_to_mtd(chip); ++ mtd->dev.parent = &pdev->dev; ++ ++ platform_set_drvdata(pdev, gpiomtd); ++ ++ if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) ++ gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); ++ ++ ret = nand_scan(mtd, 1); ++ if (ret) ++ goto err_wp; ++ ++ if (gpiomtd->plat.adjust_parts) ++ gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size); ++ ++ ret = mtd_device_register(mtd, gpiomtd->plat.parts, ++ gpiomtd->plat.num_parts); ++ if (!ret) ++ return 0; ++ ++err_wp: ++ if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) ++ gpio_set_value(gpiomtd->plat.gpio_nwp, 0); ++ ++ return ret; ++} ++ ++static struct platform_driver gpio_nand_driver = { ++ .probe = gpio_nand_probe, ++ .remove = gpio_nand_remove, ++ .driver = { ++ .name = "gpio-nand", ++ .of_match_table = of_match_ptr(gpio_nand_id_table), ++ }, ++}; ++ ++module_platform_driver(gpio_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ben Dooks "); ++MODULE_DESCRIPTION("GPIO NAND Driver"); +diff --git a/drivers/mtd/nand/raw/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile +new file mode 100644 +index 00000000..3a46248 +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o ++gpmi_nand-objs += gpmi-nand.o ++gpmi_nand-objs += gpmi-lib.o +diff --git a/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h +new file mode 100644 +index 00000000..05bb91f +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h +@@ -0,0 +1,128 @@ ++/* ++ * Freescale GPMI NAND Flash Driver ++ * ++ * Copyright 2008-2011 Freescale Semiconductor, Inc. ++ * Copyright 2008 Embedded Alley Solutions, 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. ++ * ++ * 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. ++ */ ++#ifndef __GPMI_NAND_BCH_REGS_H ++#define __GPMI_NAND_BCH_REGS_H ++ ++#define HW_BCH_CTRL 0x00000000 ++#define HW_BCH_CTRL_SET 0x00000004 ++#define HW_BCH_CTRL_CLR 0x00000008 ++#define HW_BCH_CTRL_TOG 0x0000000c ++ ++#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) ++#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) ++ ++#define HW_BCH_STATUS0 0x00000010 ++#define HW_BCH_MODE 0x00000020 ++#define HW_BCH_ENCODEPTR 0x00000030 ++#define HW_BCH_DATAPTR 0x00000040 ++#define HW_BCH_METAPTR 0x00000050 ++#define HW_BCH_LAYOUTSELECT 0x00000070 ++ ++#define HW_BCH_FLASH0LAYOUT0 0x00000080 ++ ++#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 ++#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) ++#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ ++ (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) ++ ++#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 ++#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) ++#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ ++ (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ ++ & BM_BCH_FLASH0LAYOUT0_META_SIZE) ++ ++#define BP_BCH_FLASH0LAYOUT0_ECC0 12 ++#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) ++#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 ++#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) ++#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ ++ (GPMI_IS_MX6(x) \ ++ ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ ++ & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ ++ : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ ++ & BM_BCH_FLASH0LAYOUT0_ECC0) \ ++ ) ++ ++#define MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14 10 ++#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \ ++ (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) ++#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \ ++ ((GPMI_IS_MX6(x) && ((v) == 14)) \ ++ ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \ ++ & MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \ ++ : 0 \ ++ ) ++ ++#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 ++#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ ++ (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) ++#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ ++ (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) ++#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ ++ (GPMI_IS_MX6(x) \ ++ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ ++ : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ ++ ) ++ ++#define HW_BCH_FLASH0LAYOUT1 0x00000090 ++ ++#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 ++#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ ++ (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) ++#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ ++ (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ ++ & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) ++ ++#define BP_BCH_FLASH0LAYOUT1_ECCN 12 ++#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) ++#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 ++#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) ++#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ ++ (GPMI_IS_MX6(x) \ ++ ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ ++ & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ ++ : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ ++ & BM_BCH_FLASH0LAYOUT1_ECCN) \ ++ ) ++ ++#define MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14 10 ++#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \ ++ (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) ++#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \ ++ ((GPMI_IS_MX6(x) && ((v) == 14)) \ ++ ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \ ++ & MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \ ++ : 0 \ ++ ) ++ ++#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 ++#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ ++ (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) ++#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ ++ (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) ++#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ ++ (GPMI_IS_MX6(x) \ ++ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ ++ : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ ++ ) ++ ++#define HW_BCH_VERSION 0x00000160 ++#endif +diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +new file mode 100644 +index 00000000..9778724 +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +@@ -0,0 +1,1510 @@ ++/* ++ * Freescale GPMI NAND Flash Driver ++ * ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. ++ * Copyright (C) 2008 Embedded Alley Solutions, 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. ++ * ++ * 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 ++#include ++#include ++ ++#include "gpmi-nand.h" ++#include "gpmi-regs.h" ++#include "bch-regs.h" ++ ++static struct timing_threshold timing_default_threshold = { ++ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> ++ BP_GPMI_TIMING0_DATA_SETUP), ++ .internal_data_setup_in_ns = 0, ++ .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> ++ BP_GPMI_CTRL1_RDN_DELAY), ++ .max_dll_clock_period_in_ns = 32, ++ .max_dll_delay_in_ns = 16, ++}; ++ ++#define MXS_SET_ADDR 0x4 ++#define MXS_CLR_ADDR 0x8 ++/* ++ * Clear the bit and poll it cleared. This is usually called with ++ * a reset address and mask being either SFTRST(bit 31) or CLKGATE ++ * (bit 30). ++ */ ++static int clear_poll_bit(void __iomem *addr, u32 mask) ++{ ++ int timeout = 0x400; ++ ++ /* clear the bit */ ++ writel(mask, addr + MXS_CLR_ADDR); ++ ++ /* ++ * SFTRST needs 3 GPMI clocks to settle, the reference manual ++ * recommends to wait 1us. ++ */ ++ udelay(1); ++ ++ /* poll the bit becoming clear */ ++ while ((readl(addr) & mask) && --timeout) ++ /* nothing */; ++ ++ return !timeout; ++} ++ ++#define MODULE_CLKGATE (1 << 30) ++#define MODULE_SFTRST (1 << 31) ++/* ++ * The current mxs_reset_block() will do two things: ++ * [1] enable the module. ++ * [2] reset the module. ++ * ++ * In most of the cases, it's ok. ++ * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). ++ * If you try to soft reset the BCH block, it becomes unusable until ++ * the next hard reset. This case occurs in the NAND boot mode. When the board ++ * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. ++ * So If the driver tries to reset the BCH again, the BCH will not work anymore. ++ * You will see a DMA timeout in this case. The bug has been fixed ++ * in the following chips, such as MX28. ++ * ++ * To avoid this bug, just add a new parameter `just_enable` for ++ * the mxs_reset_block(), and rewrite it here. ++ */ ++static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) ++{ ++ int ret; ++ int timeout = 0x400; ++ ++ /* clear and poll SFTRST */ ++ ret = clear_poll_bit(reset_addr, MODULE_SFTRST); ++ if (unlikely(ret)) ++ goto error; ++ ++ /* clear CLKGATE */ ++ writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); ++ ++ if (!just_enable) { ++ /* set SFTRST to reset the block */ ++ writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); ++ udelay(1); ++ ++ /* poll CLKGATE becoming set */ ++ while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) ++ /* nothing */; ++ if (unlikely(!timeout)) ++ goto error; ++ } ++ ++ /* clear and poll SFTRST */ ++ ret = clear_poll_bit(reset_addr, MODULE_SFTRST); ++ if (unlikely(ret)) ++ goto error; ++ ++ /* clear and poll CLKGATE */ ++ ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); ++ if (unlikely(ret)) ++ goto error; ++ ++ return 0; ++ ++error: ++ pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); ++ return -ETIMEDOUT; ++} ++ ++static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) ++{ ++ struct clk *clk; ++ int ret; ++ int i; ++ ++ for (i = 0; i < GPMI_CLK_MAX; i++) { ++ clk = this->resources.clock[i]; ++ if (!clk) ++ break; ++ ++ if (v) { ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ goto err_clk; ++ } else { ++ clk_disable_unprepare(clk); ++ } ++ } ++ return 0; ++ ++err_clk: ++ for (; i > 0; i--) ++ clk_disable_unprepare(this->resources.clock[i - 1]); ++ return ret; ++} ++ ++#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) ++#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) ++ ++int gpmi_init(struct gpmi_nand_data *this) ++{ ++ struct resources *r = &this->resources; ++ int ret; ++ ++ ret = gpmi_enable_clk(this); ++ if (ret) ++ return ret; ++ ret = gpmi_reset_block(r->gpmi_regs, false); ++ if (ret) ++ goto err_out; ++ ++ /* ++ * Reset BCH here, too. We got failures otherwise :( ++ * See later BCH reset for explanation of MX23 handling ++ */ ++ ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); ++ if (ret) ++ goto err_out; ++ ++ ++ /* Choose NAND mode. */ ++ writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); ++ ++ /* Set the IRQ polarity. */ ++ writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, ++ r->gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ /* Disable Write-Protection. */ ++ writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ /* Select BCH ECC. */ ++ writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ /* ++ * Decouple the chip select from dma channel. We use dma0 for all ++ * the chips. ++ */ ++ writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ gpmi_disable_clk(this); ++ return 0; ++err_out: ++ gpmi_disable_clk(this); ++ return ret; ++} ++ ++/* This function is very useful. It is called only when the bug occur. */ ++void gpmi_dump_info(struct gpmi_nand_data *this) ++{ ++ struct resources *r = &this->resources; ++ struct bch_geometry *geo = &this->bch_geometry; ++ u32 reg; ++ int i; ++ ++ dev_err(this->dev, "Show GPMI registers :\n"); ++ for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { ++ reg = readl(r->gpmi_regs + i * 0x10); ++ dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); ++ } ++ ++ /* start to print out the BCH info */ ++ dev_err(this->dev, "Show BCH registers :\n"); ++ for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { ++ reg = readl(r->bch_regs + i * 0x10); ++ dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); ++ } ++ dev_err(this->dev, "BCH Geometry :\n" ++ "GF length : %u\n" ++ "ECC Strength : %u\n" ++ "Page Size in Bytes : %u\n" ++ "Metadata Size in Bytes : %u\n" ++ "ECC Chunk Size in Bytes: %u\n" ++ "ECC Chunk Count : %u\n" ++ "Payload Size in Bytes : %u\n" ++ "Auxiliary Size in Bytes: %u\n" ++ "Auxiliary Status Offset: %u\n" ++ "Block Mark Byte Offset : %u\n" ++ "Block Mark Bit Offset : %u\n", ++ geo->gf_len, ++ geo->ecc_strength, ++ geo->page_size, ++ geo->metadata_size, ++ geo->ecc_chunk_size, ++ geo->ecc_chunk_count, ++ geo->payload_size, ++ geo->auxiliary_size, ++ geo->auxiliary_status_offset, ++ geo->block_mark_byte_offset, ++ geo->block_mark_bit_offset); ++} ++ ++/* Configures the geometry for BCH. */ ++int bch_set_geometry(struct gpmi_nand_data *this) ++{ ++ struct resources *r = &this->resources; ++ struct bch_geometry *bch_geo = &this->bch_geometry; ++ unsigned int block_count; ++ unsigned int block_size; ++ unsigned int metadata_size; ++ unsigned int ecc_strength; ++ unsigned int page_size; ++ unsigned int gf_len; ++ int ret; ++ ++ if (common_nfc_set_geometry(this)) ++ return !0; ++ ++ block_count = bch_geo->ecc_chunk_count - 1; ++ block_size = bch_geo->ecc_chunk_size; ++ metadata_size = bch_geo->metadata_size; ++ ecc_strength = bch_geo->ecc_strength >> 1; ++ page_size = bch_geo->page_size; ++ gf_len = bch_geo->gf_len; ++ ++ ret = gpmi_enable_clk(this); ++ if (ret) ++ return ret; ++ ++ /* ++ * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this ++ * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. ++ * On the other hand, the MX28 needs the reset, because one case has been ++ * seen where the BCH produced ECC errors constantly after 10000 ++ * consecutive reboots. The latter case has not been seen on the MX23 ++ * yet, still we don't know if it could happen there as well. ++ */ ++ ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); ++ if (ret) ++ goto err_out; ++ ++ /* Configure layout 0. */ ++ writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) ++ | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) ++ | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) ++ | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) ++ | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), ++ r->bch_regs + HW_BCH_FLASH0LAYOUT0); ++ ++ writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) ++ | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) ++ | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) ++ | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), ++ r->bch_regs + HW_BCH_FLASH0LAYOUT1); ++ ++ /* Set *all* chip selects to use layout 0. */ ++ writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); ++ ++ /* Enable interrupts. */ ++ writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, ++ r->bch_regs + HW_BCH_CTRL_SET); ++ ++ gpmi_disable_clk(this); ++ return 0; ++err_out: ++ gpmi_disable_clk(this); ++ return ret; ++} ++ ++/* Converts time in nanoseconds to cycles. */ ++static unsigned int ns_to_cycles(unsigned int time, ++ unsigned int period, unsigned int min) ++{ ++ unsigned int k; ++ ++ k = (time + period - 1) / period; ++ return max(k, min); ++} ++ ++#define DEF_MIN_PROP_DELAY 5 ++#define DEF_MAX_PROP_DELAY 9 ++/* Apply timing to current hardware conditions. */ ++static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, ++ struct gpmi_nfc_hardware_timing *hw) ++{ ++ struct timing_threshold *nfc = &timing_default_threshold; ++ struct resources *r = &this->resources; ++ struct nand_chip *nand = &this->nand; ++ struct nand_timing target = this->timing; ++ bool improved_timing_is_available; ++ unsigned long clock_frequency_in_hz; ++ unsigned int clock_period_in_ns; ++ bool dll_use_half_periods; ++ unsigned int dll_delay_shift; ++ unsigned int max_sample_delay_in_ns; ++ unsigned int address_setup_in_cycles; ++ unsigned int data_setup_in_ns; ++ unsigned int data_setup_in_cycles; ++ unsigned int data_hold_in_cycles; ++ int ideal_sample_delay_in_ns; ++ unsigned int sample_delay_factor; ++ int tEYE; ++ unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; ++ unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; ++ ++ /* ++ * If there are multiple chips, we need to relax the timings to allow ++ * for signal distortion due to higher capacitance. ++ */ ++ if (nand->numchips > 2) { ++ target.data_setup_in_ns += 10; ++ target.data_hold_in_ns += 10; ++ target.address_setup_in_ns += 10; ++ } else if (nand->numchips > 1) { ++ target.data_setup_in_ns += 5; ++ target.data_hold_in_ns += 5; ++ target.address_setup_in_ns += 5; ++ } ++ ++ /* Check if improved timing information is available. */ ++ improved_timing_is_available = ++ (target.tREA_in_ns >= 0) && ++ (target.tRLOH_in_ns >= 0) && ++ (target.tRHOH_in_ns >= 0); ++ ++ /* Inspect the clock. */ ++ nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]); ++ clock_frequency_in_hz = nfc->clock_frequency_in_hz; ++ clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz; ++ ++ /* ++ * The NFC quantizes setup and hold parameters in terms of clock cycles. ++ * Here, we quantize the setup and hold timing parameters to the ++ * next-highest clock period to make sure we apply at least the ++ * specified times. ++ * ++ * For data setup and data hold, the hardware interprets a value of zero ++ * as the largest possible delay. This is not what's intended by a zero ++ * in the input parameter, so we impose a minimum of one cycle. ++ */ ++ data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns, ++ clock_period_in_ns, 1); ++ data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns, ++ clock_period_in_ns, 1); ++ address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns, ++ clock_period_in_ns, 0); ++ ++ /* ++ * The clock's period affects the sample delay in a number of ways: ++ * ++ * (1) The NFC HAL tells us the maximum clock period the sample delay ++ * DLL can tolerate. If the clock period is greater than half that ++ * maximum, we must configure the DLL to be driven by half periods. ++ * ++ * (2) We need to convert from an ideal sample delay, in ns, to a ++ * "sample delay factor," which the NFC uses. This factor depends on ++ * whether we're driving the DLL with full or half periods. ++ * Paraphrasing the reference manual: ++ * ++ * AD = SDF x 0.125 x RP ++ * ++ * where: ++ * ++ * AD is the applied delay, in ns. ++ * SDF is the sample delay factor, which is dimensionless. ++ * RP is the reference period, in ns, which is a full clock period ++ * if the DLL is being driven by full periods, or half that if ++ * the DLL is being driven by half periods. ++ * ++ * Let's re-arrange this in a way that's more useful to us: ++ * ++ * 8 ++ * SDF = AD x ---- ++ * RP ++ * ++ * The reference period is either the clock period or half that, so this ++ * is: ++ * ++ * 8 AD x DDF ++ * SDF = AD x ----- = -------- ++ * f x P P ++ * ++ * where: ++ * ++ * f is 1 or 1/2, depending on how we're driving the DLL. ++ * P is the clock period. ++ * DDF is the DLL Delay Factor, a dimensionless value that ++ * incorporates all the constants in the conversion. ++ * ++ * DDF will be either 8 or 16, both of which are powers of two. We can ++ * reduce the cost of this conversion by using bit shifts instead of ++ * multiplication or division. Thus: ++ * ++ * AD << DDS ++ * SDF = --------- ++ * P ++ * ++ * or ++ * ++ * AD = (SDF >> DDS) x P ++ * ++ * where: ++ * ++ * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF. ++ */ ++ if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) { ++ dll_use_half_periods = true; ++ dll_delay_shift = 3 + 1; ++ } else { ++ dll_use_half_periods = false; ++ dll_delay_shift = 3; ++ } ++ ++ /* ++ * Compute the maximum sample delay the NFC allows, under current ++ * conditions. If the clock is running too slowly, no sample delay is ++ * possible. ++ */ ++ if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns) ++ max_sample_delay_in_ns = 0; ++ else { ++ /* ++ * Compute the delay implied by the largest sample delay factor ++ * the NFC allows. ++ */ ++ max_sample_delay_in_ns = ++ (nfc->max_sample_delay_factor * clock_period_in_ns) >> ++ dll_delay_shift; ++ ++ /* ++ * Check if the implied sample delay larger than the NFC ++ * actually allows. ++ */ ++ if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns) ++ max_sample_delay_in_ns = nfc->max_dll_delay_in_ns; ++ } ++ ++ /* ++ * Check if improved timing information is available. If not, we have to ++ * use a less-sophisticated algorithm. ++ */ ++ if (!improved_timing_is_available) { ++ /* ++ * Fold the read setup time required by the NFC into the ideal ++ * sample delay. ++ */ ++ ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns + ++ nfc->internal_data_setup_in_ns; ++ ++ /* ++ * The ideal sample delay may be greater than the maximum ++ * allowed by the NFC. If so, we can trade off sample delay time ++ * for more data setup time. ++ * ++ * In each iteration of the following loop, we add a cycle to ++ * the data setup time and subtract a corresponding amount from ++ * the sample delay until we've satisified the constraints or ++ * can't do any better. ++ */ ++ while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && ++ (data_setup_in_cycles < nfc->max_data_setup_cycles)) { ++ ++ data_setup_in_cycles++; ++ ideal_sample_delay_in_ns -= clock_period_in_ns; ++ ++ if (ideal_sample_delay_in_ns < 0) ++ ideal_sample_delay_in_ns = 0; ++ ++ } ++ ++ /* ++ * Compute the sample delay factor that corresponds most closely ++ * to the ideal sample delay. If the result is too large for the ++ * NFC, use the maximum value. ++ * ++ * Notice that we use the ns_to_cycles function to compute the ++ * sample delay factor. We do this because the form of the ++ * computation is the same as that for calculating cycles. ++ */ ++ sample_delay_factor = ++ ns_to_cycles( ++ ideal_sample_delay_in_ns << dll_delay_shift, ++ clock_period_in_ns, 0); ++ ++ if (sample_delay_factor > nfc->max_sample_delay_factor) ++ sample_delay_factor = nfc->max_sample_delay_factor; ++ ++ /* Skip to the part where we return our results. */ ++ goto return_results; ++ } ++ ++ /* ++ * If control arrives here, we have more detailed timing information, ++ * so we can use a better algorithm. ++ */ ++ ++ /* ++ * Fold the read setup time required by the NFC into the maximum ++ * propagation delay. ++ */ ++ max_prop_delay_in_ns += nfc->internal_data_setup_in_ns; ++ ++ /* ++ * Earlier, we computed the number of clock cycles required to satisfy ++ * the data setup time. Now, we need to know the actual nanoseconds. ++ */ ++ data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles; ++ ++ /* ++ * Compute tEYE, the width of the data eye when reading from the NAND ++ * Flash. The eye width is fundamentally determined by the data setup ++ * time, perturbed by propagation delays and some characteristics of the ++ * NAND Flash device. ++ * ++ * start of the eye = max_prop_delay + tREA ++ * end of the eye = min_prop_delay + tRHOH + data_setup ++ */ ++ tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns + ++ (int)data_setup_in_ns; ++ ++ tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns; ++ ++ /* ++ * The eye must be open. If it's not, we can try to open it by ++ * increasing its main forcer, the data setup time. ++ * ++ * In each iteration of the following loop, we increase the data setup ++ * time by a single clock cycle. We do this until either the eye is ++ * open or we run into NFC limits. ++ */ ++ while ((tEYE <= 0) && ++ (data_setup_in_cycles < nfc->max_data_setup_cycles)) { ++ /* Give a cycle to data setup. */ ++ data_setup_in_cycles++; ++ /* Synchronize the data setup time with the cycles. */ ++ data_setup_in_ns += clock_period_in_ns; ++ /* Adjust tEYE accordingly. */ ++ tEYE += clock_period_in_ns; ++ } ++ ++ /* ++ * When control arrives here, the eye is open. The ideal time to sample ++ * the data is in the center of the eye: ++ * ++ * end of the eye + start of the eye ++ * --------------------------------- - data_setup ++ * 2 ++ * ++ * After some algebra, this simplifies to the code immediately below. ++ */ ++ ideal_sample_delay_in_ns = ++ ((int)max_prop_delay_in_ns + ++ (int)target.tREA_in_ns + ++ (int)min_prop_delay_in_ns + ++ (int)target.tRHOH_in_ns - ++ (int)data_setup_in_ns) >> 1; ++ ++ /* ++ * The following figure illustrates some aspects of a NAND Flash read: ++ * ++ * ++ * __ _____________________________________ ++ * RDN \_________________/ ++ * ++ * <---- tEYE -----> ++ * /-----------------\ ++ * Read Data ----------------------------< >--------- ++ * \-----------------/ ++ * ^ ^ ^ ^ ++ * | | | | ++ * |<--Data Setup -->|<--Delay Time -->| | ++ * | | | | ++ * | | | ++ * | |<-- Quantized Delay Time -->| ++ * | | | ++ * ++ * ++ * We have some issues we must now address: ++ * ++ * (1) The *ideal* sample delay time must not be negative. If it is, we ++ * jam it to zero. ++ * ++ * (2) The *ideal* sample delay time must not be greater than that ++ * allowed by the NFC. If it is, we can increase the data setup ++ * time, which will reduce the delay between the end of the data ++ * setup and the center of the eye. It will also make the eye ++ * larger, which might help with the next issue... ++ * ++ * (3) The *quantized* sample delay time must not fall either before the ++ * eye opens or after it closes (the latter is the problem ++ * illustrated in the above figure). ++ */ ++ ++ /* Jam a negative ideal sample delay to zero. */ ++ if (ideal_sample_delay_in_ns < 0) ++ ideal_sample_delay_in_ns = 0; ++ ++ /* ++ * Extend the data setup as needed to reduce the ideal sample delay ++ * below the maximum permitted by the NFC. ++ */ ++ while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) && ++ (data_setup_in_cycles < nfc->max_data_setup_cycles)) { ++ ++ /* Give a cycle to data setup. */ ++ data_setup_in_cycles++; ++ /* Synchronize the data setup time with the cycles. */ ++ data_setup_in_ns += clock_period_in_ns; ++ /* Adjust tEYE accordingly. */ ++ tEYE += clock_period_in_ns; ++ ++ /* ++ * Decrease the ideal sample delay by one half cycle, to keep it ++ * in the middle of the eye. ++ */ ++ ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); ++ ++ /* Jam a negative ideal sample delay to zero. */ ++ if (ideal_sample_delay_in_ns < 0) ++ ideal_sample_delay_in_ns = 0; ++ } ++ ++ /* ++ * Compute the sample delay factor that corresponds to the ideal sample ++ * delay. If the result is too large, then use the maximum allowed ++ * value. ++ * ++ * Notice that we use the ns_to_cycles function to compute the sample ++ * delay factor. We do this because the form of the computation is the ++ * same as that for calculating cycles. ++ */ ++ sample_delay_factor = ++ ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift, ++ clock_period_in_ns, 0); ++ ++ if (sample_delay_factor > nfc->max_sample_delay_factor) ++ sample_delay_factor = nfc->max_sample_delay_factor; ++ ++ /* ++ * These macros conveniently encapsulate a computation we'll use to ++ * continuously evaluate whether or not the data sample delay is inside ++ * the eye. ++ */ ++ #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns) ++ ++ #define QUANTIZED_DELAY \ ++ ((int) ((sample_delay_factor * clock_period_in_ns) >> \ ++ dll_delay_shift)) ++ ++ #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY)) ++ ++ #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1)) ++ ++ /* ++ * While the quantized sample time falls outside the eye, reduce the ++ * sample delay or extend the data setup to move the sampling point back ++ * toward the eye. Do not allow the number of data setup cycles to ++ * exceed the maximum allowed by the NFC. ++ */ ++ while (SAMPLE_IS_NOT_WITHIN_THE_EYE && ++ (data_setup_in_cycles < nfc->max_data_setup_cycles)) { ++ /* ++ * If control arrives here, the quantized sample delay falls ++ * outside the eye. Check if it's before the eye opens, or after ++ * the eye closes. ++ */ ++ if (QUANTIZED_DELAY > IDEAL_DELAY) { ++ /* ++ * If control arrives here, the quantized sample delay ++ * falls after the eye closes. Decrease the quantized ++ * delay time and then go back to re-evaluate. ++ */ ++ if (sample_delay_factor != 0) ++ sample_delay_factor--; ++ continue; ++ } ++ ++ /* ++ * If control arrives here, the quantized sample delay falls ++ * before the eye opens. Shift the sample point by increasing ++ * data setup time. This will also make the eye larger. ++ */ ++ ++ /* Give a cycle to data setup. */ ++ data_setup_in_cycles++; ++ /* Synchronize the data setup time with the cycles. */ ++ data_setup_in_ns += clock_period_in_ns; ++ /* Adjust tEYE accordingly. */ ++ tEYE += clock_period_in_ns; ++ ++ /* ++ * Decrease the ideal sample delay by one half cycle, to keep it ++ * in the middle of the eye. ++ */ ++ ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1); ++ ++ /* ...and one less period for the delay time. */ ++ ideal_sample_delay_in_ns -= clock_period_in_ns; ++ ++ /* Jam a negative ideal sample delay to zero. */ ++ if (ideal_sample_delay_in_ns < 0) ++ ideal_sample_delay_in_ns = 0; ++ ++ /* ++ * We have a new ideal sample delay, so re-compute the quantized ++ * delay. ++ */ ++ sample_delay_factor = ++ ns_to_cycles( ++ ideal_sample_delay_in_ns << dll_delay_shift, ++ clock_period_in_ns, 0); ++ ++ if (sample_delay_factor > nfc->max_sample_delay_factor) ++ sample_delay_factor = nfc->max_sample_delay_factor; ++ } ++ ++ /* Control arrives here when we're ready to return our results. */ ++return_results: ++ hw->data_setup_in_cycles = data_setup_in_cycles; ++ hw->data_hold_in_cycles = data_hold_in_cycles; ++ hw->address_setup_in_cycles = address_setup_in_cycles; ++ hw->use_half_periods = dll_use_half_periods; ++ hw->sample_delay_factor = sample_delay_factor; ++ hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT; ++ hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; ++ ++ /* Return success. */ ++ return 0; ++} ++ ++/* ++ * <1> Firstly, we should know what's the GPMI-clock means. ++ * The GPMI-clock is the internal clock in the gpmi nand controller. ++ * If you set 100MHz to gpmi nand controller, the GPMI-clock's period ++ * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. ++ * ++ * <2> Secondly, we should know what's the frequency on the nand chip pins. ++ * The frequency on the nand chip pins is derived from the GPMI-clock. ++ * We can get it from the following equation: ++ * ++ * F = G / (DS + DH) ++ * ++ * F : the frequency on the nand chip pins. ++ * G : the GPMI clock, such as 100MHz. ++ * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP ++ * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD ++ * ++ * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, ++ * the nand EDO(extended Data Out) timing could be applied. ++ * The GPMI implements a feedback read strobe to sample the read data. ++ * The feedback read strobe can be delayed to support the nand EDO timing ++ * where the read strobe may deasserts before the read data is valid, and ++ * read data is valid for some time after read strobe. ++ * ++ * The following figure illustrates some aspects of a NAND Flash read: ++ * ++ * |<---tREA---->| ++ * | | ++ * | | | ++ * |<--tRP-->| | ++ * | | | ++ * __ ___|__________________________________ ++ * RDN \________/ | ++ * | ++ * /---------\ ++ * Read Data --------------< >--------- ++ * \---------/ ++ * | | ++ * |<-D->| ++ * FeedbackRDN ________ ____________ ++ * \___________/ ++ * ++ * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. ++ * ++ * ++ * <4> Now, we begin to describe how to compute the right RDN_DELAY. ++ * ++ * 4.1) From the aspect of the nand chip pins: ++ * Delay = (tREA + C - tRP) {1} ++ * ++ * tREA : the maximum read access time. From the ONFI nand standards, ++ * we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4. ++ * Please check it in : www.onfi.org ++ * C : a constant for adjust the delay. default is 4. ++ * tRP : the read pulse width. ++ * Specified by the HW_GPMI_TIMING0:DATA_SETUP: ++ * tRP = (GPMI-clock-period) * DATA_SETUP ++ * ++ * 4.2) From the aspect of the GPMI nand controller: ++ * Delay = RDN_DELAY * 0.125 * RP {2} ++ * ++ * RP : the DLL reference period. ++ * if (GPMI-clock-period > DLL_THRETHOLD) ++ * RP = GPMI-clock-period / 2; ++ * else ++ * RP = GPMI-clock-period; ++ * ++ * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period ++ * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD ++ * is 16ns, but in mx6q, we use 12ns. ++ * ++ * 4.3) since {1} equals {2}, we get: ++ * ++ * (tREA + 4 - tRP) * 8 ++ * RDN_DELAY = --------------------- {3} ++ * RP ++ * ++ * 4.4) We only support the fastest asynchronous mode of ONFI nand. ++ * For some ONFI nand, the mode 4 is the fastest mode; ++ * while for some ONFI nand, the mode 5 is the fastest mode. ++ * So we only support the mode 4 and mode 5. It is no need to ++ * support other modes. ++ */ ++static void gpmi_compute_edo_timing(struct gpmi_nand_data *this, ++ struct gpmi_nfc_hardware_timing *hw) ++{ ++ struct resources *r = &this->resources; ++ unsigned long rate = clk_get_rate(r->clock[0]); ++ int mode = this->timing_mode; ++ int dll_threshold = this->devdata->max_chain_delay; ++ unsigned long delay; ++ unsigned long clk_period; ++ int t_rea; ++ int c = 4; ++ int t_rp; ++ int rp; ++ ++ /* ++ * [1] for GPMI_HW_GPMI_TIMING0: ++ * The async mode requires 40MHz for mode 4, 50MHz for mode 5. ++ * The GPMI can support 100MHz at most. So if we want to ++ * get the 40MHz or 50MHz, we have to set DS=1, DH=1. ++ * Set the ADDRESS_SETUP to 0 in mode 4. ++ */ ++ hw->data_setup_in_cycles = 1; ++ hw->data_hold_in_cycles = 1; ++ hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0); ++ ++ /* [2] for GPMI_HW_GPMI_TIMING1 */ ++ hw->device_busy_timeout = 0x9000; ++ ++ /* [3] for GPMI_HW_GPMI_CTRL1 */ ++ hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; ++ ++ /* ++ * Enlarge 10 times for the numerator and denominator in {3}. ++ * This make us to get more accurate result. ++ */ ++ clk_period = NSEC_PER_SEC / (rate / 10); ++ dll_threshold *= 10; ++ t_rea = ((mode == 5) ? 16 : 20) * 10; ++ c *= 10; ++ ++ t_rp = clk_period * 1; /* DATA_SETUP is 1 */ ++ ++ if (clk_period > dll_threshold) { ++ hw->use_half_periods = 1; ++ rp = clk_period / 2; ++ } else { ++ hw->use_half_periods = 0; ++ rp = clk_period; ++ } ++ ++ /* ++ * Multiply the numerator with 10, we could do a round off: ++ * 7.8 round up to 8; 7.4 round down to 7. ++ */ ++ delay = (((t_rea + c - t_rp) * 8) * 10) / rp; ++ delay = (delay + 5) / 10; ++ ++ hw->sample_delay_factor = delay; ++} ++ ++static int enable_edo_mode(struct gpmi_nand_data *this, int mode) ++{ ++ struct resources *r = &this->resources; ++ struct nand_chip *nand = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ uint8_t *feature; ++ unsigned long rate; ++ int ret; ++ ++ feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL); ++ if (!feature) ++ return -ENOMEM; ++ ++ nand->select_chip(mtd, 0); ++ ++ /* [1] send SET FEATURE command to NAND */ ++ feature[0] = mode; ++ ret = nand->onfi_set_features(mtd, nand, ++ ONFI_FEATURE_ADDR_TIMING_MODE, feature); ++ if (ret) ++ goto err_out; ++ ++ /* [2] send GET FEATURE command to double-check the timing mode */ ++ memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); ++ ret = nand->onfi_get_features(mtd, nand, ++ ONFI_FEATURE_ADDR_TIMING_MODE, feature); ++ if (ret || feature[0] != mode) ++ goto err_out; ++ ++ nand->select_chip(mtd, -1); ++ ++ /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */ ++ rate = (mode == 5) ? 100000000 : 80000000; ++ clk_set_rate(r->clock[0], rate); ++ ++ /* Let the gpmi_begin() re-compute the timing again. */ ++ this->flags &= ~GPMI_TIMING_INIT_OK; ++ ++ this->flags |= GPMI_ASYNC_EDO_ENABLED; ++ this->timing_mode = mode; ++ kfree(feature); ++ dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode); ++ return 0; ++ ++err_out: ++ nand->select_chip(mtd, -1); ++ kfree(feature); ++ dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode); ++ return -EINVAL; ++} ++ ++int gpmi_extra_init(struct gpmi_nand_data *this) ++{ ++ struct nand_chip *chip = &this->nand; ++ ++ /* Enable the asynchronous EDO feature. */ ++ if (GPMI_IS_MX6(this) && chip->onfi_version) { ++ int mode = onfi_get_async_timing_mode(chip); ++ ++ /* We only support the timing mode 4 and mode 5. */ ++ if (mode & ONFI_TIMING_MODE_5) ++ mode = 5; ++ else if (mode & ONFI_TIMING_MODE_4) ++ mode = 4; ++ else ++ return 0; ++ ++ return enable_edo_mode(this, mode); ++ } ++ return 0; ++} ++ ++/* Begin the I/O */ ++void gpmi_begin(struct gpmi_nand_data *this) ++{ ++ struct resources *r = &this->resources; ++ void __iomem *gpmi_regs = r->gpmi_regs; ++ unsigned int clock_period_in_ns; ++ uint32_t reg; ++ unsigned int dll_wait_time_in_us; ++ struct gpmi_nfc_hardware_timing hw; ++ int ret; ++ ++ /* Enable the clock. */ ++ ret = gpmi_enable_clk(this); ++ if (ret) { ++ dev_err(this->dev, "We failed in enable the clk\n"); ++ goto err_out; ++ } ++ ++ /* Only initialize the timing once */ ++ if (this->flags & GPMI_TIMING_INIT_OK) ++ return; ++ this->flags |= GPMI_TIMING_INIT_OK; ++ ++ if (this->flags & GPMI_ASYNC_EDO_ENABLED) ++ gpmi_compute_edo_timing(this, &hw); ++ else ++ gpmi_nfc_compute_hardware_timing(this, &hw); ++ ++ /* [1] Set HW_GPMI_TIMING0 */ ++ reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | ++ BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | ++ BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles); ++ ++ writel(reg, gpmi_regs + HW_GPMI_TIMING0); ++ ++ /* [2] Set HW_GPMI_TIMING1 */ ++ writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout), ++ gpmi_regs + HW_GPMI_TIMING1); ++ ++ /* [3] The following code is to set the HW_GPMI_CTRL1. */ ++ ++ /* Set the WRN_DLY_SEL */ ++ writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR); ++ writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel), ++ gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */ ++ writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); ++ ++ /* Clear out the DLL control fields. */ ++ reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD; ++ writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR); ++ ++ /* If no sample delay is called for, return immediately. */ ++ if (!hw.sample_delay_factor) ++ return; ++ ++ /* Set RDN_DELAY or HALF_PERIOD. */ ++ reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0) ++ | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor); ++ ++ writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ /* At last, we enable the DLL. */ ++ writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); ++ ++ /* ++ * After we enable the GPMI DLL, we have to wait 64 clock cycles before ++ * we can use the GPMI. Calculate the amount of time we need to wait, ++ * in microseconds. ++ */ ++ clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]); ++ dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; ++ ++ if (!dll_wait_time_in_us) ++ dll_wait_time_in_us = 1; ++ ++ /* Wait for the DLL to settle. */ ++ udelay(dll_wait_time_in_us); ++ ++err_out: ++ return; ++} ++ ++void gpmi_end(struct gpmi_nand_data *this) ++{ ++ gpmi_disable_clk(this); ++} ++ ++/* Clears a BCH interrupt. */ ++void gpmi_clear_bch(struct gpmi_nand_data *this) ++{ ++ struct resources *r = &this->resources; ++ writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); ++} ++ ++/* Returns the Ready/Busy status of the given chip. */ ++int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) ++{ ++ struct resources *r = &this->resources; ++ uint32_t mask = 0; ++ uint32_t reg = 0; ++ ++ if (GPMI_IS_MX23(this)) { ++ mask = MX23_BM_GPMI_DEBUG_READY0 << chip; ++ reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); ++ } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) { ++ /* ++ * In the imx6, all the ready/busy pins are bound ++ * together. So we only need to check chip 0. ++ */ ++ if (GPMI_IS_MX6(this)) ++ chip = 0; ++ ++ /* MX28 shares the same R/B register as MX6Q. */ ++ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); ++ reg = readl(r->gpmi_regs + HW_GPMI_STAT); ++ } else ++ dev_err(this->dev, "unknown arch.\n"); ++ return reg & mask; ++} ++ ++static inline void set_dma_type(struct gpmi_nand_data *this, ++ enum dma_ops_type type) ++{ ++ this->last_dma_type = this->dma_type; ++ this->dma_type = type; ++} ++ ++int gpmi_send_command(struct gpmi_nand_data *this) ++{ ++ struct dma_chan *channel = get_dma_chan(this); ++ struct dma_async_tx_descriptor *desc; ++ struct scatterlist *sgl; ++ int chip = this->current_chip; ++ u32 pio[3]; ++ ++ /* [1] send out the PIO words */ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) ++ | BM_GPMI_CTRL0_ADDRESS_INCREMENT ++ | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); ++ pio[1] = pio[2] = 0; ++ desc = dmaengine_prep_slave_sg(channel, ++ (struct scatterlist *)pio, ++ ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ ++ sgl = &this->cmd_sgl; ++ ++ sg_init_one(sgl, this->cmd_buffer, this->command_length); ++ dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); ++ desc = dmaengine_prep_slave_sg(channel, ++ sgl, 1, DMA_MEM_TO_DEV, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [3] submit the DMA */ ++ set_dma_type(this, DMA_FOR_COMMAND); ++ return start_dma_without_bch_irq(this, desc); ++} ++ ++int gpmi_send_data(struct gpmi_nand_data *this) ++{ ++ struct dma_async_tx_descriptor *desc; ++ struct dma_chan *channel = get_dma_chan(this); ++ int chip = this->current_chip; ++ uint32_t command_mode; ++ uint32_t address; ++ u32 pio[2]; ++ ++ /* [1] PIO */ ++ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; ++ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; ++ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(address) ++ | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); ++ pio[1] = 0; ++ desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ++ ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [2] send DMA request */ ++ prepare_data_dma(this, DMA_TO_DEVICE); ++ desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, ++ 1, DMA_MEM_TO_DEV, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [3] submit the DMA */ ++ set_dma_type(this, DMA_FOR_WRITE_DATA); ++ return start_dma_without_bch_irq(this, desc); ++} ++ ++int gpmi_read_data(struct gpmi_nand_data *this) ++{ ++ struct dma_async_tx_descriptor *desc; ++ struct dma_chan *channel = get_dma_chan(this); ++ int chip = this->current_chip; ++ u32 pio[2]; ++ ++ /* [1] : send PIO */ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) ++ | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); ++ pio[1] = 0; ++ desc = dmaengine_prep_slave_sg(channel, ++ (struct scatterlist *)pio, ++ ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [2] : send DMA request */ ++ prepare_data_dma(this, DMA_FROM_DEVICE); ++ desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, ++ 1, DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [3] : submit the DMA */ ++ set_dma_type(this, DMA_FOR_READ_DATA); ++ return start_dma_without_bch_irq(this, desc); ++} ++ ++int gpmi_send_page(struct gpmi_nand_data *this, ++ dma_addr_t payload, dma_addr_t auxiliary) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ uint32_t command_mode; ++ uint32_t address; ++ uint32_t ecc_command; ++ uint32_t buffer_mask; ++ struct dma_async_tx_descriptor *desc; ++ struct dma_chan *channel = get_dma_chan(this); ++ int chip = this->current_chip; ++ u32 pio[6]; ++ ++ /* A DMA descriptor that does an ECC page read. */ ++ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; ++ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; ++ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; ++ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | ++ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; ++ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(address) ++ | BF_GPMI_CTRL0_XFER_COUNT(0); ++ pio[1] = 0; ++ pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC ++ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) ++ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); ++ pio[3] = geo->page_size; ++ pio[4] = payload; ++ pio[5] = auxiliary; ++ ++ desc = dmaengine_prep_slave_sg(channel, ++ (struct scatterlist *)pio, ++ ARRAY_SIZE(pio), DMA_TRANS_NONE, ++ DMA_CTRL_ACK); ++ if (!desc) ++ return -EINVAL; ++ ++ set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); ++ return start_dma_with_bch_irq(this, desc); ++} ++ ++int gpmi_read_page(struct gpmi_nand_data *this, ++ dma_addr_t payload, dma_addr_t auxiliary) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ uint32_t command_mode; ++ uint32_t address; ++ uint32_t ecc_command; ++ uint32_t buffer_mask; ++ struct dma_async_tx_descriptor *desc; ++ struct dma_chan *channel = get_dma_chan(this); ++ int chip = this->current_chip; ++ u32 pio[6]; ++ ++ /* [1] Wait for the chip to report ready. */ ++ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; ++ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; ++ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(address) ++ | BF_GPMI_CTRL0_XFER_COUNT(0); ++ pio[1] = 0; ++ desc = dmaengine_prep_slave_sg(channel, ++ (struct scatterlist *)pio, 2, ++ DMA_TRANS_NONE, 0); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [2] Enable the BCH block and read. */ ++ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; ++ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; ++ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; ++ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE ++ | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; ++ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(address) ++ | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); ++ ++ pio[1] = 0; ++ pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC ++ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) ++ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); ++ pio[3] = geo->page_size; ++ pio[4] = payload; ++ pio[5] = auxiliary; ++ desc = dmaengine_prep_slave_sg(channel, ++ (struct scatterlist *)pio, ++ ARRAY_SIZE(pio), DMA_TRANS_NONE, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [3] Disable the BCH block */ ++ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; ++ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; ++ ++ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) ++ | BM_GPMI_CTRL0_WORD_LENGTH ++ | BF_GPMI_CTRL0_CS(chip, this) ++ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) ++ | BF_GPMI_CTRL0_ADDRESS(address) ++ | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); ++ pio[1] = 0; ++ pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ ++ desc = dmaengine_prep_slave_sg(channel, ++ (struct scatterlist *)pio, 3, ++ DMA_TRANS_NONE, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ if (!desc) ++ return -EINVAL; ++ ++ /* [4] submit the DMA */ ++ set_dma_type(this, DMA_FOR_READ_ECC_PAGE); ++ return start_dma_with_bch_irq(this, desc); ++} ++ ++/** ++ * gpmi_copy_bits - copy bits from one memory region to another ++ * @dst: destination buffer ++ * @dst_bit_off: bit offset we're starting to write at ++ * @src: source buffer ++ * @src_bit_off: bit offset we're starting to read from ++ * @nbits: number of bits to copy ++ * ++ * This functions copies bits from one memory region to another, and is used by ++ * the GPMI driver to copy ECC sections which are not guaranteed to be byte ++ * aligned. ++ * ++ * src and dst should not overlap. ++ * ++ */ ++void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, ++ const u8 *src, size_t src_bit_off, ++ size_t nbits) ++{ ++ size_t i; ++ size_t nbytes; ++ u32 src_buffer = 0; ++ size_t bits_in_src_buffer = 0; ++ ++ if (!nbits) ++ return; ++ ++ /* ++ * Move src and dst pointers to the closest byte pointer and store bit ++ * offsets within a byte. ++ */ ++ src += src_bit_off / 8; ++ src_bit_off %= 8; ++ ++ dst += dst_bit_off / 8; ++ dst_bit_off %= 8; ++ ++ /* ++ * Initialize the src_buffer value with bits available in the first ++ * byte of data so that we end up with a byte aligned src pointer. ++ */ ++ if (src_bit_off) { ++ src_buffer = src[0] >> src_bit_off; ++ if (nbits >= (8 - src_bit_off)) { ++ bits_in_src_buffer += 8 - src_bit_off; ++ } else { ++ src_buffer &= GENMASK(nbits - 1, 0); ++ bits_in_src_buffer += nbits; ++ } ++ nbits -= bits_in_src_buffer; ++ src++; ++ } ++ ++ /* Calculate the number of bytes that can be copied from src to dst. */ ++ nbytes = nbits / 8; ++ ++ /* Try to align dst to a byte boundary. */ ++ if (dst_bit_off) { ++ if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) { ++ src_buffer |= src[0] << bits_in_src_buffer; ++ bits_in_src_buffer += 8; ++ src++; ++ nbytes--; ++ } ++ ++ if (bits_in_src_buffer >= (8 - dst_bit_off)) { ++ dst[0] &= GENMASK(dst_bit_off - 1, 0); ++ dst[0] |= src_buffer << dst_bit_off; ++ src_buffer >>= (8 - dst_bit_off); ++ bits_in_src_buffer -= (8 - dst_bit_off); ++ dst_bit_off = 0; ++ dst++; ++ if (bits_in_src_buffer > 7) { ++ bits_in_src_buffer -= 8; ++ dst[0] = src_buffer; ++ dst++; ++ src_buffer >>= 8; ++ } ++ } ++ } ++ ++ if (!bits_in_src_buffer && !dst_bit_off) { ++ /* ++ * Both src and dst pointers are byte aligned, thus we can ++ * just use the optimized memcpy function. ++ */ ++ if (nbytes) ++ memcpy(dst, src, nbytes); ++ } else { ++ /* ++ * src buffer is not byte aligned, hence we have to copy each ++ * src byte to the src_buffer variable before extracting a byte ++ * to store in dst. ++ */ ++ for (i = 0; i < nbytes; i++) { ++ src_buffer |= src[i] << bits_in_src_buffer; ++ dst[i] = src_buffer; ++ src_buffer >>= 8; ++ } ++ } ++ /* Update dst and src pointers */ ++ dst += nbytes; ++ src += nbytes; ++ ++ /* ++ * nbits is the number of remaining bits. It should not exceed 8 as ++ * we've already copied as much bytes as possible. ++ */ ++ nbits %= 8; ++ ++ /* ++ * If there's no more bits to copy to the destination and src buffer ++ * was already byte aligned, then we're done. ++ */ ++ if (!nbits && !bits_in_src_buffer) ++ return; ++ ++ /* Copy the remaining bits to src_buffer */ ++ if (nbits) ++ src_buffer |= (*src & GENMASK(nbits - 1, 0)) << ++ bits_in_src_buffer; ++ bits_in_src_buffer += nbits; ++ ++ /* ++ * In case there were not enough bits to get a byte aligned dst buffer ++ * prepare the src_buffer variable to match the dst organization (shift ++ * src_buffer by dst_bit_off and retrieve the least significant bits ++ * from dst). ++ */ ++ if (dst_bit_off) ++ src_buffer = (src_buffer << dst_bit_off) | ++ (*dst & GENMASK(dst_bit_off - 1, 0)); ++ bits_in_src_buffer += dst_bit_off; ++ ++ /* ++ * Keep most significant bits from dst if we end up with an unaligned ++ * number of bits. ++ */ ++ nbytes = bits_in_src_buffer / 8; ++ if (bits_in_src_buffer % 8) { ++ src_buffer |= (dst[nbytes] & ++ GENMASK(7, bits_in_src_buffer % 8)) << ++ (nbytes * 8); ++ nbytes++; ++ } ++ ++ /* Copy the remaining bytes to dst */ ++ for (i = 0; i < nbytes; i++) { ++ dst[i] = src_buffer; ++ src_buffer >>= 8; ++ } ++} +diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +new file mode 100644 +index 00000000..dfeb266 +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +@@ -0,0 +1,2182 @@ ++/* ++ * Freescale GPMI NAND Flash Driver ++ * ++ * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. ++ * Copyright (C) 2008 Embedded Alley Solutions, 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. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "gpmi-nand.h" ++#include "bch-regs.h" ++ ++/* Resource names for the GPMI NAND driver. */ ++#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" ++#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" ++#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" ++ ++/* add our owner bbt descriptor */ ++static uint8_t scan_ff_pattern[] = { 0xff }; ++static struct nand_bbt_descr gpmi_bbt_descr = { ++ .options = 0, ++ .offs = 0, ++ .len = 1, ++ .pattern = scan_ff_pattern ++}; ++ ++/* ++ * We may change the layout if we can get the ECC info from the datasheet, ++ * else we will use all the (page + OOB). ++ */ ++static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ struct bch_geometry *geo = &this->bch_geometry; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 0; ++ oobregion->length = geo->page_size - mtd->writesize; ++ ++ return 0; ++} ++ ++static int gpmi_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ struct bch_geometry *geo = &this->bch_geometry; ++ ++ if (section) ++ return -ERANGE; ++ ++ /* The available oob size we have. */ ++ if (geo->page_size < mtd->writesize + mtd->oobsize) { ++ oobregion->offset = geo->page_size - mtd->writesize; ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ } ++ ++ return 0; ++} ++ ++static const char * const gpmi_clks_for_mx2x[] = { ++ "gpmi_io", ++}; ++ ++static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { ++ .ecc = gpmi_ooblayout_ecc, ++ .free = gpmi_ooblayout_free, ++}; ++ ++static const struct gpmi_devdata gpmi_devdata_imx23 = { ++ .type = IS_MX23, ++ .bch_max_ecc_strength = 20, ++ .max_chain_delay = 16, ++ .clks = gpmi_clks_for_mx2x, ++ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), ++}; ++ ++static const struct gpmi_devdata gpmi_devdata_imx28 = { ++ .type = IS_MX28, ++ .bch_max_ecc_strength = 20, ++ .max_chain_delay = 16, ++ .clks = gpmi_clks_for_mx2x, ++ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), ++}; ++ ++static const char * const gpmi_clks_for_mx6[] = { ++ "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", ++}; ++ ++static const struct gpmi_devdata gpmi_devdata_imx6q = { ++ .type = IS_MX6Q, ++ .bch_max_ecc_strength = 40, ++ .max_chain_delay = 12, ++ .clks = gpmi_clks_for_mx6, ++ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), ++}; ++ ++static const struct gpmi_devdata gpmi_devdata_imx6sx = { ++ .type = IS_MX6SX, ++ .bch_max_ecc_strength = 62, ++ .max_chain_delay = 12, ++ .clks = gpmi_clks_for_mx6, ++ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), ++}; ++ ++static const char * const gpmi_clks_for_mx7d[] = { ++ "gpmi_io", "gpmi_bch_apb", ++}; ++ ++static const struct gpmi_devdata gpmi_devdata_imx7d = { ++ .type = IS_MX7D, ++ .bch_max_ecc_strength = 62, ++ .max_chain_delay = 12, ++ .clks = gpmi_clks_for_mx7d, ++ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), ++}; ++ ++static irqreturn_t bch_irq(int irq, void *cookie) ++{ ++ struct gpmi_nand_data *this = cookie; ++ ++ gpmi_clear_bch(this); ++ complete(&this->bch_done); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Calculate the ECC strength by hand: ++ * E : The ECC strength. ++ * G : the length of Galois Field. ++ * N : The chunk count of per page. ++ * O : the oobsize of the NAND chip. ++ * M : the metasize of per page. ++ * ++ * The formula is : ++ * E * G * N ++ * ------------ <= (O - M) ++ * 8 ++ * ++ * So, we get E by: ++ * (O - M) * 8 ++ * E <= ------------- ++ * G * N ++ */ ++static inline int get_ecc_strength(struct gpmi_nand_data *this) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ struct mtd_info *mtd = nand_to_mtd(&this->nand); ++ int ecc_strength; ++ ++ ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) ++ / (geo->gf_len * geo->ecc_chunk_count); ++ ++ /* We need the minor even number. */ ++ return round_down(ecc_strength, 2); ++} ++ ++static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ ++ /* Do the sanity check. */ ++ if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) { ++ /* The mx23/mx28 only support the GF13. */ ++ if (geo->gf_len == 14) ++ return false; ++ } ++ return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; ++} ++ ++/* ++ * If we can get the ECC information from the nand chip, we do not ++ * need to calculate them ourselves. ++ * ++ * We may have available oob space in this case. ++ */ ++static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ struct nand_chip *chip = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int block_mark_bit_offset; ++ ++ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) ++ return -EINVAL; ++ ++ switch (chip->ecc_step_ds) { ++ case SZ_512: ++ geo->gf_len = 13; ++ break; ++ case SZ_1K: ++ geo->gf_len = 14; ++ break; ++ default: ++ dev_err(this->dev, ++ "unsupported nand chip. ecc bits : %d, ecc size : %d\n", ++ chip->ecc_strength_ds, chip->ecc_step_ds); ++ return -EINVAL; ++ } ++ geo->ecc_chunk_size = chip->ecc_step_ds; ++ geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); ++ if (!gpmi_check_ecc(this)) ++ return -EINVAL; ++ ++ /* Keep the C >= O */ ++ if (geo->ecc_chunk_size < mtd->oobsize) { ++ dev_err(this->dev, ++ "unsupported nand chip. ecc size: %d, oob size : %d\n", ++ chip->ecc_step_ds, mtd->oobsize); ++ return -EINVAL; ++ } ++ ++ /* The default value, see comment in the legacy_set_geometry(). */ ++ geo->metadata_size = 10; ++ ++ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; ++ ++ /* ++ * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: ++ * ++ * | P | ++ * |<----------------------------------------------------->| ++ * | | ++ * | (Block Mark) | ++ * | P' | | | | ++ * |<-------------------------------------------->| D | | O' | ++ * | |<---->| |<--->| ++ * V V V V V ++ * +---+----------+-+----------+-+----------+-+----------+-+-----+ ++ * | M | data |E| data |E| data |E| data |E| | ++ * +---+----------+-+----------+-+----------+-+----------+-+-----+ ++ * ^ ^ ++ * | O | ++ * |<------------>| ++ * | | ++ * ++ * P : the page size for BCH module. ++ * E : The ECC strength. ++ * G : the length of Galois Field. ++ * N : The chunk count of per page. ++ * M : the metasize of per page. ++ * C : the ecc chunk size, aka the "data" above. ++ * P': the nand chip's page size. ++ * O : the nand chip's oob size. ++ * O': the free oob. ++ * ++ * The formula for P is : ++ * ++ * E * G * N ++ * P = ------------ + P' + M ++ * 8 ++ * ++ * The position of block mark moves forward in the ECC-based view ++ * of page, and the delta is: ++ * ++ * E * G * (N - 1) ++ * D = (---------------- + M) ++ * 8 ++ * ++ * Please see the comment in legacy_set_geometry(). ++ * With the condition C >= O , we still can get same result. ++ * So the bit position of the physical block mark within the ECC-based ++ * view of the page is : ++ * (P' - D) * 8 ++ */ ++ geo->page_size = mtd->writesize + geo->metadata_size + ++ (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; ++ ++ geo->payload_size = mtd->writesize; ++ ++ geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); ++ geo->auxiliary_size = ALIGN(geo->metadata_size, 4) ++ + ALIGN(geo->ecc_chunk_count, 4); ++ ++ if (!this->swap_block_mark) ++ return 0; ++ ++ /* For bit swap. */ ++ block_mark_bit_offset = mtd->writesize * 8 - ++ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) ++ + geo->metadata_size * 8); ++ ++ geo->block_mark_byte_offset = block_mark_bit_offset / 8; ++ geo->block_mark_bit_offset = block_mark_bit_offset % 8; ++ return 0; ++} ++ ++static int legacy_set_geometry(struct gpmi_nand_data *this) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ struct mtd_info *mtd = nand_to_mtd(&this->nand); ++ unsigned int metadata_size; ++ unsigned int status_size; ++ unsigned int block_mark_bit_offset; ++ ++ /* ++ * The size of the metadata can be changed, though we set it to 10 ++ * bytes now. But it can't be too large, because we have to save ++ * enough space for BCH. ++ */ ++ geo->metadata_size = 10; ++ ++ /* The default for the length of Galois Field. */ ++ geo->gf_len = 13; ++ ++ /* The default for chunk size. */ ++ geo->ecc_chunk_size = 512; ++ while (geo->ecc_chunk_size < mtd->oobsize) { ++ geo->ecc_chunk_size *= 2; /* keep C >= O */ ++ geo->gf_len = 14; ++ } ++ ++ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; ++ ++ /* We use the same ECC strength for all chunks. */ ++ geo->ecc_strength = get_ecc_strength(this); ++ if (!gpmi_check_ecc(this)) { ++ dev_err(this->dev, ++ "ecc strength: %d cannot be supported by the controller (%d)\n" ++ "try to use minimum ecc strength that NAND chip required\n", ++ geo->ecc_strength, ++ this->devdata->bch_max_ecc_strength); ++ return -EINVAL; ++ } ++ ++ geo->page_size = mtd->writesize + geo->metadata_size + ++ (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; ++ geo->payload_size = mtd->writesize; ++ ++ /* ++ * The auxiliary buffer contains the metadata and the ECC status. The ++ * metadata is padded to the nearest 32-bit boundary. The ECC status ++ * contains one byte for every ECC chunk, and is also padded to the ++ * nearest 32-bit boundary. ++ */ ++ metadata_size = ALIGN(geo->metadata_size, 4); ++ status_size = ALIGN(geo->ecc_chunk_count, 4); ++ ++ geo->auxiliary_size = metadata_size + status_size; ++ geo->auxiliary_status_offset = metadata_size; ++ ++ if (!this->swap_block_mark) ++ return 0; ++ ++ /* ++ * We need to compute the byte and bit offsets of ++ * the physical block mark within the ECC-based view of the page. ++ * ++ * NAND chip with 2K page shows below: ++ * (Block Mark) ++ * | | ++ * | D | ++ * |<---->| ++ * V V ++ * +---+----------+-+----------+-+----------+-+----------+-+ ++ * | M | data |E| data |E| data |E| data |E| ++ * +---+----------+-+----------+-+----------+-+----------+-+ ++ * ++ * The position of block mark moves forward in the ECC-based view ++ * of page, and the delta is: ++ * ++ * E * G * (N - 1) ++ * D = (---------------- + M) ++ * 8 ++ * ++ * With the formula to compute the ECC strength, and the condition ++ * : C >= O (C is the ecc chunk size) ++ * ++ * It's easy to deduce to the following result: ++ * ++ * E * G (O - M) C - M C - M ++ * ----------- <= ------- <= -------- < --------- ++ * 8 N N (N - 1) ++ * ++ * So, we get: ++ * ++ * E * G * (N - 1) ++ * D = (---------------- + M) < C ++ * 8 ++ * ++ * The above inequality means the position of block mark ++ * within the ECC-based view of the page is still in the data chunk, ++ * and it's NOT in the ECC bits of the chunk. ++ * ++ * Use the following to compute the bit position of the ++ * physical block mark within the ECC-based view of the page: ++ * (page_size - D) * 8 ++ * ++ * --Huang Shijie ++ */ ++ block_mark_bit_offset = mtd->writesize * 8 - ++ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) ++ + geo->metadata_size * 8); ++ ++ geo->block_mark_byte_offset = block_mark_bit_offset / 8; ++ geo->block_mark_bit_offset = block_mark_bit_offset % 8; ++ return 0; ++} ++ ++int common_nfc_set_geometry(struct gpmi_nand_data *this) ++{ ++ if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) ++ || legacy_set_geometry(this)) ++ return set_geometry_by_ecc_info(this); ++ ++ return 0; ++} ++ ++struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) ++{ ++ /* We use the DMA channel 0 to access all the nand chips. */ ++ return this->dma_chans[0]; ++} ++ ++/* Can we use the upper's buffer directly for DMA? */ ++void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) ++{ ++ struct scatterlist *sgl = &this->data_sgl; ++ int ret; ++ ++ /* first try to map the upper buffer directly */ ++ if (virt_addr_valid(this->upper_buf) && ++ !object_is_on_stack(this->upper_buf)) { ++ sg_init_one(sgl, this->upper_buf, this->upper_len); ++ ret = dma_map_sg(this->dev, sgl, 1, dr); ++ if (ret == 0) ++ goto map_fail; ++ ++ this->direct_dma_map_ok = true; ++ return; ++ } ++ ++map_fail: ++ /* We have to use our own DMA buffer. */ ++ sg_init_one(sgl, this->data_buffer_dma, this->upper_len); ++ ++ if (dr == DMA_TO_DEVICE) ++ memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); ++ ++ dma_map_sg(this->dev, sgl, 1, dr); ++ ++ this->direct_dma_map_ok = false; ++} ++ ++/* This will be called after the DMA operation is finished. */ ++static void dma_irq_callback(void *param) ++{ ++ struct gpmi_nand_data *this = param; ++ struct completion *dma_c = &this->dma_done; ++ ++ switch (this->dma_type) { ++ case DMA_FOR_COMMAND: ++ dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); ++ break; ++ ++ case DMA_FOR_READ_DATA: ++ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); ++ if (this->direct_dma_map_ok == false) ++ memcpy(this->upper_buf, this->data_buffer_dma, ++ this->upper_len); ++ break; ++ ++ case DMA_FOR_WRITE_DATA: ++ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); ++ break; ++ ++ case DMA_FOR_READ_ECC_PAGE: ++ case DMA_FOR_WRITE_ECC_PAGE: ++ /* We have to wait the BCH interrupt to finish. */ ++ break; ++ ++ default: ++ dev_err(this->dev, "in wrong DMA operation.\n"); ++ } ++ ++ complete(dma_c); ++} ++ ++int start_dma_without_bch_irq(struct gpmi_nand_data *this, ++ struct dma_async_tx_descriptor *desc) ++{ ++ struct completion *dma_c = &this->dma_done; ++ unsigned long timeout; ++ ++ init_completion(dma_c); ++ ++ desc->callback = dma_irq_callback; ++ desc->callback_param = this; ++ dmaengine_submit(desc); ++ dma_async_issue_pending(get_dma_chan(this)); ++ ++ /* Wait for the interrupt from the DMA block. */ ++ timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); ++ if (!timeout) { ++ dev_err(this->dev, "DMA timeout, last DMA :%d\n", ++ this->last_dma_type); ++ gpmi_dump_info(this); ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++/* ++ * This function is used in BCH reading or BCH writing pages. ++ * It will wait for the BCH interrupt as long as ONE second. ++ * Actually, we must wait for two interrupts : ++ * [1] firstly the DMA interrupt and ++ * [2] secondly the BCH interrupt. ++ */ ++int start_dma_with_bch_irq(struct gpmi_nand_data *this, ++ struct dma_async_tx_descriptor *desc) ++{ ++ struct completion *bch_c = &this->bch_done; ++ unsigned long timeout; ++ ++ /* Prepare to receive an interrupt from the BCH block. */ ++ init_completion(bch_c); ++ ++ /* start the DMA */ ++ start_dma_without_bch_irq(this, desc); ++ ++ /* Wait for the interrupt from the BCH block. */ ++ timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); ++ if (!timeout) { ++ dev_err(this->dev, "BCH timeout, last DMA :%d\n", ++ this->last_dma_type); ++ gpmi_dump_info(this); ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++static int acquire_register_block(struct gpmi_nand_data *this, ++ const char *res_name) ++{ ++ struct platform_device *pdev = this->pdev; ++ struct resources *res = &this->resources; ++ struct resource *r; ++ void __iomem *p; ++ ++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); ++ p = devm_ioremap_resource(&pdev->dev, r); ++ if (IS_ERR(p)) ++ return PTR_ERR(p); ++ ++ if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)) ++ res->gpmi_regs = p; ++ else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME)) ++ res->bch_regs = p; ++ else ++ dev_err(this->dev, "unknown resource name : %s\n", res_name); ++ ++ return 0; ++} ++ ++static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) ++{ ++ struct platform_device *pdev = this->pdev; ++ const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME; ++ struct resource *r; ++ int err; ++ ++ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); ++ if (!r) { ++ dev_err(this->dev, "Can't get resource for %s\n", res_name); ++ return -ENODEV; ++ } ++ ++ err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this); ++ if (err) ++ dev_err(this->dev, "error requesting BCH IRQ\n"); ++ ++ return err; ++} ++ ++static void release_dma_channels(struct gpmi_nand_data *this) ++{ ++ unsigned int i; ++ for (i = 0; i < DMA_CHANS; i++) ++ if (this->dma_chans[i]) { ++ dma_release_channel(this->dma_chans[i]); ++ this->dma_chans[i] = NULL; ++ } ++} ++ ++static int acquire_dma_channels(struct gpmi_nand_data *this) ++{ ++ struct platform_device *pdev = this->pdev; ++ struct dma_chan *dma_chan; ++ ++ /* request dma channel */ ++ dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx"); ++ if (!dma_chan) { ++ dev_err(this->dev, "Failed to request DMA channel.\n"); ++ goto acquire_err; ++ } ++ ++ this->dma_chans[0] = dma_chan; ++ return 0; ++ ++acquire_err: ++ release_dma_channels(this); ++ return -EINVAL; ++} ++ ++static int gpmi_get_clks(struct gpmi_nand_data *this) ++{ ++ struct resources *r = &this->resources; ++ struct clk *clk; ++ int err, i; ++ ++ for (i = 0; i < this->devdata->clks_count; i++) { ++ clk = devm_clk_get(this->dev, this->devdata->clks[i]); ++ if (IS_ERR(clk)) { ++ err = PTR_ERR(clk); ++ goto err_clock; ++ } ++ ++ r->clock[i] = clk; ++ } ++ ++ if (GPMI_IS_MX6(this)) ++ /* ++ * Set the default value for the gpmi clock. ++ * ++ * If you want to use the ONFI nand which is in the ++ * Synchronous Mode, you should change the clock as you need. ++ */ ++ clk_set_rate(r->clock[0], 22000000); ++ ++ return 0; ++ ++err_clock: ++ dev_dbg(this->dev, "failed in finding the clocks.\n"); ++ return err; ++} ++ ++static int acquire_resources(struct gpmi_nand_data *this) ++{ ++ int ret; ++ ++ ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); ++ if (ret) ++ goto exit_regs; ++ ++ ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME); ++ if (ret) ++ goto exit_regs; ++ ++ ret = acquire_bch_irq(this, bch_irq); ++ if (ret) ++ goto exit_regs; ++ ++ ret = acquire_dma_channels(this); ++ if (ret) ++ goto exit_regs; ++ ++ ret = gpmi_get_clks(this); ++ if (ret) ++ goto exit_clock; ++ return 0; ++ ++exit_clock: ++ release_dma_channels(this); ++exit_regs: ++ return ret; ++} ++ ++static void release_resources(struct gpmi_nand_data *this) ++{ ++ release_dma_channels(this); ++} ++ ++static int init_hardware(struct gpmi_nand_data *this) ++{ ++ int ret; ++ ++ /* ++ * This structure contains the "safe" GPMI timing that should succeed ++ * with any NAND Flash device ++ * (although, with less-than-optimal performance). ++ */ ++ struct nand_timing safe_timing = { ++ .data_setup_in_ns = 80, ++ .data_hold_in_ns = 60, ++ .address_setup_in_ns = 25, ++ .gpmi_sample_delay_in_ns = 6, ++ .tREA_in_ns = -1, ++ .tRLOH_in_ns = -1, ++ .tRHOH_in_ns = -1, ++ }; ++ ++ /* Initialize the hardwares. */ ++ ret = gpmi_init(this); ++ if (ret) ++ return ret; ++ ++ this->timing = safe_timing; ++ return 0; ++} ++ ++static int read_page_prepare(struct gpmi_nand_data *this, ++ void *destination, unsigned length, ++ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, ++ void **use_virt, dma_addr_t *use_phys) ++{ ++ struct device *dev = this->dev; ++ ++ if (virt_addr_valid(destination)) { ++ dma_addr_t dest_phys; ++ ++ dest_phys = dma_map_single(dev, destination, ++ length, DMA_FROM_DEVICE); ++ if (dma_mapping_error(dev, dest_phys)) { ++ if (alt_size < length) { ++ dev_err(dev, "Alternate buffer is too small\n"); ++ return -ENOMEM; ++ } ++ goto map_failed; ++ } ++ *use_virt = destination; ++ *use_phys = dest_phys; ++ this->direct_dma_map_ok = true; ++ return 0; ++ } ++ ++map_failed: ++ *use_virt = alt_virt; ++ *use_phys = alt_phys; ++ this->direct_dma_map_ok = false; ++ return 0; ++} ++ ++static inline void read_page_end(struct gpmi_nand_data *this, ++ void *destination, unsigned length, ++ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, ++ void *used_virt, dma_addr_t used_phys) ++{ ++ if (this->direct_dma_map_ok) ++ dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); ++} ++ ++static inline void read_page_swap_end(struct gpmi_nand_data *this, ++ void *destination, unsigned length, ++ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, ++ void *used_virt, dma_addr_t used_phys) ++{ ++ if (!this->direct_dma_map_ok) ++ memcpy(destination, alt_virt, length); ++} ++ ++static int send_page_prepare(struct gpmi_nand_data *this, ++ const void *source, unsigned length, ++ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, ++ const void **use_virt, dma_addr_t *use_phys) ++{ ++ struct device *dev = this->dev; ++ ++ if (virt_addr_valid(source)) { ++ dma_addr_t source_phys; ++ ++ source_phys = dma_map_single(dev, (void *)source, length, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(dev, source_phys)) { ++ if (alt_size < length) { ++ dev_err(dev, "Alternate buffer is too small\n"); ++ return -ENOMEM; ++ } ++ goto map_failed; ++ } ++ *use_virt = source; ++ *use_phys = source_phys; ++ return 0; ++ } ++map_failed: ++ /* ++ * Copy the content of the source buffer into the alternate ++ * buffer and set up the return values accordingly. ++ */ ++ memcpy(alt_virt, source, length); ++ ++ *use_virt = alt_virt; ++ *use_phys = alt_phys; ++ return 0; ++} ++ ++static void send_page_end(struct gpmi_nand_data *this, ++ const void *source, unsigned length, ++ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, ++ const void *used_virt, dma_addr_t used_phys) ++{ ++ struct device *dev = this->dev; ++ if (used_virt == source) ++ dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE); ++} ++ ++static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) ++{ ++ struct device *dev = this->dev; ++ ++ if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt)) ++ dma_free_coherent(dev, this->page_buffer_size, ++ this->page_buffer_virt, ++ this->page_buffer_phys); ++ kfree(this->cmd_buffer); ++ kfree(this->data_buffer_dma); ++ kfree(this->raw_buffer); ++ ++ this->cmd_buffer = NULL; ++ this->data_buffer_dma = NULL; ++ this->raw_buffer = NULL; ++ this->page_buffer_virt = NULL; ++ this->page_buffer_size = 0; ++} ++ ++/* Allocate the DMA buffers */ ++static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) ++{ ++ struct bch_geometry *geo = &this->bch_geometry; ++ struct device *dev = this->dev; ++ struct mtd_info *mtd = nand_to_mtd(&this->nand); ++ ++ /* [1] Allocate a command buffer. PAGE_SIZE is enough. */ ++ this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL); ++ if (this->cmd_buffer == NULL) ++ goto error_alloc; ++ ++ /* ++ * [2] Allocate a read/write data buffer. ++ * The gpmi_alloc_dma_buffer can be called twice. ++ * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer ++ * is called before the nand_scan_ident; and we allocate a buffer ++ * of the real NAND page size when the gpmi_alloc_dma_buffer is ++ * called after the nand_scan_ident. ++ */ ++ this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, ++ GFP_DMA | GFP_KERNEL); ++ if (this->data_buffer_dma == NULL) ++ goto error_alloc; ++ ++ /* ++ * [3] Allocate the page buffer. ++ * ++ * Both the payload buffer and the auxiliary buffer must appear on ++ * 32-bit boundaries. We presume the size of the payload buffer is a ++ * power of two and is much larger than four, which guarantees the ++ * auxiliary buffer will appear on a 32-bit boundary. ++ */ ++ this->page_buffer_size = geo->payload_size + geo->auxiliary_size; ++ this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size, ++ &this->page_buffer_phys, GFP_DMA); ++ if (!this->page_buffer_virt) ++ goto error_alloc; ++ ++ this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); ++ if (!this->raw_buffer) ++ goto error_alloc; ++ ++ /* Slice up the page buffer. */ ++ this->payload_virt = this->page_buffer_virt; ++ this->payload_phys = this->page_buffer_phys; ++ this->auxiliary_virt = this->payload_virt + geo->payload_size; ++ this->auxiliary_phys = this->payload_phys + geo->payload_size; ++ return 0; ++ ++error_alloc: ++ gpmi_free_dma_buffer(this); ++ return -ENOMEM; ++} ++ ++static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ int ret; ++ ++ /* ++ * Every operation begins with a command byte and a series of zero or ++ * more address bytes. These are distinguished by either the Address ++ * Latch Enable (ALE) or Command Latch Enable (CLE) signals being ++ * asserted. When MTD is ready to execute the command, it will deassert ++ * both latch enables. ++ * ++ * Rather than run a separate DMA operation for every single byte, we ++ * queue them up and run a single DMA operation for the entire series ++ * of command and data bytes. NAND_CMD_NONE means the END of the queue. ++ */ ++ if ((ctrl & (NAND_ALE | NAND_CLE))) { ++ if (data != NAND_CMD_NONE) ++ this->cmd_buffer[this->command_length++] = data; ++ return; ++ } ++ ++ if (!this->command_length) ++ return; ++ ++ ret = gpmi_send_command(this); ++ if (ret) ++ dev_err(this->dev, "Chip: %u, Error %d\n", ++ this->current_chip, ret); ++ ++ this->command_length = 0; ++} ++ ++static int gpmi_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ ++ return gpmi_is_ready(this, this->current_chip); ++} ++ ++static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ ++ if ((this->current_chip < 0) && (chipnr >= 0)) ++ gpmi_begin(this); ++ else if ((this->current_chip >= 0) && (chipnr < 0)) ++ gpmi_end(this); ++ ++ this->current_chip = chipnr; ++} ++ ++static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ ++ dev_dbg(this->dev, "len is %d\n", len); ++ this->upper_buf = buf; ++ this->upper_len = len; ++ ++ gpmi_read_data(this); ++} ++ ++static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ ++ dev_dbg(this->dev, "len is %d\n", len); ++ this->upper_buf = (uint8_t *)buf; ++ this->upper_len = len; ++ ++ gpmi_send_data(this); ++} ++ ++static uint8_t gpmi_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ uint8_t *buf = this->data_buffer_dma; ++ ++ gpmi_read_buf(mtd, buf, 1); ++ return buf[0]; ++} ++ ++/* ++ * Handles block mark swapping. ++ * It can be called in swapping the block mark, or swapping it back, ++ * because the the operations are the same. ++ */ ++static void block_mark_swapping(struct gpmi_nand_data *this, ++ void *payload, void *auxiliary) ++{ ++ struct bch_geometry *nfc_geo = &this->bch_geometry; ++ unsigned char *p; ++ unsigned char *a; ++ unsigned int bit; ++ unsigned char mask; ++ unsigned char from_data; ++ unsigned char from_oob; ++ ++ if (!this->swap_block_mark) ++ return; ++ ++ /* ++ * If control arrives here, we're swapping. Make some convenience ++ * variables. ++ */ ++ bit = nfc_geo->block_mark_bit_offset; ++ p = payload + nfc_geo->block_mark_byte_offset; ++ a = auxiliary; ++ ++ /* ++ * Get the byte from the data area that overlays the block mark. Since ++ * the ECC engine applies its own view to the bits in the page, the ++ * physical block mark won't (in general) appear on a byte boundary in ++ * the data. ++ */ ++ from_data = (p[0] >> bit) | (p[1] << (8 - bit)); ++ ++ /* Get the byte from the OOB. */ ++ from_oob = a[0]; ++ ++ /* Swap them. */ ++ a[0] = from_data; ++ ++ mask = (0x1 << bit) - 1; ++ p[0] = (p[0] & mask) | (from_oob << bit); ++ ++ mask = ~0 << bit; ++ p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); ++} ++ ++static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ struct bch_geometry *nfc_geo = &this->bch_geometry; ++ void *payload_virt; ++ dma_addr_t payload_phys; ++ void *auxiliary_virt; ++ dma_addr_t auxiliary_phys; ++ unsigned int i; ++ unsigned char *status; ++ unsigned int max_bitflips = 0; ++ int ret; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ dev_dbg(this->dev, "page number is : %d\n", page); ++ ret = read_page_prepare(this, buf, nfc_geo->payload_size, ++ this->payload_virt, this->payload_phys, ++ nfc_geo->payload_size, ++ &payload_virt, &payload_phys); ++ if (ret) { ++ dev_err(this->dev, "Inadequate DMA buffer\n"); ++ ret = -ENOMEM; ++ return ret; ++ } ++ auxiliary_virt = this->auxiliary_virt; ++ auxiliary_phys = this->auxiliary_phys; ++ ++ /* go! */ ++ ret = gpmi_read_page(this, payload_phys, auxiliary_phys); ++ read_page_end(this, buf, nfc_geo->payload_size, ++ this->payload_virt, this->payload_phys, ++ nfc_geo->payload_size, ++ payload_virt, payload_phys); ++ if (ret) { ++ dev_err(this->dev, "Error in ECC-based read: %d\n", ret); ++ return ret; ++ } ++ ++ /* Loop over status bytes, accumulating ECC status. */ ++ status = auxiliary_virt + nfc_geo->auxiliary_status_offset; ++ ++ read_page_swap_end(this, buf, nfc_geo->payload_size, ++ this->payload_virt, this->payload_phys, ++ nfc_geo->payload_size, ++ payload_virt, payload_phys); ++ ++ for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { ++ if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) ++ continue; ++ ++ if (*status == STATUS_UNCORRECTABLE) { ++ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; ++ u8 *eccbuf = this->raw_buffer; ++ int offset, bitoffset; ++ int eccbytes; ++ int flips; ++ ++ /* Read ECC bytes into our internal raw_buffer */ ++ offset = nfc_geo->metadata_size * 8; ++ offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); ++ offset -= eccbits; ++ bitoffset = offset % 8; ++ eccbytes = DIV_ROUND_UP(offset + eccbits, 8); ++ offset /= 8; ++ eccbytes -= offset; ++ nand_change_read_column_op(chip, offset, eccbuf, ++ eccbytes, false); ++ ++ /* ++ * ECC data are not byte aligned and we may have ++ * in-band data in the first and last byte of ++ * eccbuf. Set non-eccbits to one so that ++ * nand_check_erased_ecc_chunk() does not count them ++ * as bitflips. ++ */ ++ if (bitoffset) ++ eccbuf[0] |= GENMASK(bitoffset - 1, 0); ++ ++ bitoffset = (bitoffset + eccbits) % 8; ++ if (bitoffset) ++ eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset); ++ ++ /* ++ * The ECC hardware has an uncorrectable ECC status ++ * code in case we have bitflips in an erased page. As ++ * nothing was written into this subpage the ECC is ++ * obviously wrong and we can not trust it. We assume ++ * at this point that we are reading an erased page and ++ * try to correct the bitflips in buffer up to ++ * ecc_strength bitflips. If this is a page with random ++ * data, we exceed this number of bitflips and have a ++ * ECC failure. Otherwise we use the corrected buffer. ++ */ ++ if (i == 0) { ++ /* The first block includes metadata */ ++ flips = nand_check_erased_ecc_chunk( ++ buf + i * nfc_geo->ecc_chunk_size, ++ nfc_geo->ecc_chunk_size, ++ eccbuf, eccbytes, ++ auxiliary_virt, ++ nfc_geo->metadata_size, ++ nfc_geo->ecc_strength); ++ } else { ++ flips = nand_check_erased_ecc_chunk( ++ buf + i * nfc_geo->ecc_chunk_size, ++ nfc_geo->ecc_chunk_size, ++ eccbuf, eccbytes, ++ NULL, 0, ++ nfc_geo->ecc_strength); ++ } ++ ++ if (flips > 0) { ++ max_bitflips = max_t(unsigned int, max_bitflips, ++ flips); ++ mtd->ecc_stats.corrected += flips; ++ continue; ++ } ++ ++ mtd->ecc_stats.failed++; ++ continue; ++ } ++ ++ mtd->ecc_stats.corrected += *status; ++ max_bitflips = max_t(unsigned int, max_bitflips, *status); ++ } ++ ++ /* handle the block mark swapping */ ++ block_mark_swapping(this, buf, auxiliary_virt); ++ ++ if (oob_required) { ++ /* ++ * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() ++ * for details about our policy for delivering the OOB. ++ * ++ * We fill the caller's buffer with set bits, and then copy the ++ * block mark to th caller's buffer. Note that, if block mark ++ * swapping was necessary, it has already been done, so we can ++ * rely on the first byte of the auxiliary buffer to contain ++ * the block mark. ++ */ ++ memset(chip->oob_poi, ~0, mtd->oobsize); ++ chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; ++ } ++ ++ return max_bitflips; ++} ++ ++/* Fake a virtual small page for the subpage read */ ++static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, ++ uint32_t offs, uint32_t len, uint8_t *buf, int page) ++{ ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ void __iomem *bch_regs = this->resources.bch_regs; ++ struct bch_geometry old_geo = this->bch_geometry; ++ struct bch_geometry *geo = &this->bch_geometry; ++ int size = chip->ecc.size; /* ECC chunk size */ ++ int meta, n, page_size; ++ u32 r1_old, r2_old, r1_new, r2_new; ++ unsigned int max_bitflips; ++ int first, last, marker_pos; ++ int ecc_parity_size; ++ int col = 0; ++ int old_swap_block_mark = this->swap_block_mark; ++ ++ /* The size of ECC parity */ ++ ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; ++ ++ /* Align it with the chunk size */ ++ first = offs / size; ++ last = (offs + len - 1) / size; ++ ++ if (this->swap_block_mark) { ++ /* ++ * Find the chunk which contains the Block Marker. ++ * If this chunk is in the range of [first, last], ++ * we have to read out the whole page. ++ * Why? since we had swapped the data at the position of Block ++ * Marker to the metadata which is bound with the chunk 0. ++ */ ++ marker_pos = geo->block_mark_byte_offset / size; ++ if (last >= marker_pos && first <= marker_pos) { ++ dev_dbg(this->dev, ++ "page:%d, first:%d, last:%d, marker at:%d\n", ++ page, first, last, marker_pos); ++ return gpmi_ecc_read_page(mtd, chip, buf, 0, page); ++ } ++ } ++ ++ meta = geo->metadata_size; ++ if (first) { ++ col = meta + (size + ecc_parity_size) * first; ++ meta = 0; ++ buf = buf + first * size; ++ } ++ ++ nand_read_page_op(chip, page, col, NULL, 0); ++ ++ /* Save the old environment */ ++ r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); ++ r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); ++ ++ /* change the BCH registers and bch_geometry{} */ ++ n = last - first + 1; ++ page_size = meta + (size + ecc_parity_size) * n; ++ ++ r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | ++ BM_BCH_FLASH0LAYOUT0_META_SIZE); ++ r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) ++ | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); ++ writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); ++ ++ r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; ++ r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); ++ writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); ++ ++ geo->ecc_chunk_count = n; ++ geo->payload_size = n * size; ++ geo->page_size = page_size; ++ geo->auxiliary_status_offset = ALIGN(meta, 4); ++ ++ dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", ++ page, offs, len, col, first, n, page_size); ++ ++ /* Read the subpage now */ ++ this->swap_block_mark = false; ++ max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page); ++ ++ /* Restore */ ++ writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); ++ writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); ++ this->bch_geometry = old_geo; ++ this->swap_block_mark = old_swap_block_mark; ++ ++ return max_bitflips; ++} ++ ++static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ struct bch_geometry *nfc_geo = &this->bch_geometry; ++ const void *payload_virt; ++ dma_addr_t payload_phys; ++ const void *auxiliary_virt; ++ dma_addr_t auxiliary_phys; ++ int ret; ++ ++ dev_dbg(this->dev, "ecc write page.\n"); ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ if (this->swap_block_mark) { ++ /* ++ * If control arrives here, we're doing block mark swapping. ++ * Since we can't modify the caller's buffers, we must copy them ++ * into our own. ++ */ ++ memcpy(this->payload_virt, buf, mtd->writesize); ++ payload_virt = this->payload_virt; ++ payload_phys = this->payload_phys; ++ ++ memcpy(this->auxiliary_virt, chip->oob_poi, ++ nfc_geo->auxiliary_size); ++ auxiliary_virt = this->auxiliary_virt; ++ auxiliary_phys = this->auxiliary_phys; ++ ++ /* Handle block mark swapping. */ ++ block_mark_swapping(this, ++ (void *)payload_virt, (void *)auxiliary_virt); ++ } else { ++ /* ++ * If control arrives here, we're not doing block mark swapping, ++ * so we can to try and use the caller's buffers. ++ */ ++ ret = send_page_prepare(this, ++ buf, mtd->writesize, ++ this->payload_virt, this->payload_phys, ++ nfc_geo->payload_size, ++ &payload_virt, &payload_phys); ++ if (ret) { ++ dev_err(this->dev, "Inadequate payload DMA buffer\n"); ++ return 0; ++ } ++ ++ ret = send_page_prepare(this, ++ chip->oob_poi, mtd->oobsize, ++ this->auxiliary_virt, this->auxiliary_phys, ++ nfc_geo->auxiliary_size, ++ &auxiliary_virt, &auxiliary_phys); ++ if (ret) { ++ dev_err(this->dev, "Inadequate auxiliary DMA buffer\n"); ++ goto exit_auxiliary; ++ } ++ } ++ ++ /* Ask the NFC. */ ++ ret = gpmi_send_page(this, payload_phys, auxiliary_phys); ++ if (ret) ++ dev_err(this->dev, "Error in ECC-based write: %d\n", ret); ++ ++ if (!this->swap_block_mark) { ++ send_page_end(this, chip->oob_poi, mtd->oobsize, ++ this->auxiliary_virt, this->auxiliary_phys, ++ nfc_geo->auxiliary_size, ++ auxiliary_virt, auxiliary_phys); ++exit_auxiliary: ++ send_page_end(this, buf, mtd->writesize, ++ this->payload_virt, this->payload_phys, ++ nfc_geo->payload_size, ++ payload_virt, payload_phys); ++ } ++ ++ if (ret) ++ return ret; ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/* ++ * There are several places in this driver where we have to handle the OOB and ++ * block marks. This is the function where things are the most complicated, so ++ * this is where we try to explain it all. All the other places refer back to ++ * here. ++ * ++ * These are the rules, in order of decreasing importance: ++ * ++ * 1) Nothing the caller does can be allowed to imperil the block mark. ++ * ++ * 2) In read operations, the first byte of the OOB we return must reflect the ++ * true state of the block mark, no matter where that block mark appears in ++ * the physical page. ++ * ++ * 3) ECC-based read operations return an OOB full of set bits (since we never ++ * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads ++ * return). ++ * ++ * 4) "Raw" read operations return a direct view of the physical bytes in the ++ * page, using the conventional definition of which bytes are data and which ++ * are OOB. This gives the caller a way to see the actual, physical bytes ++ * in the page, without the distortions applied by our ECC engine. ++ * ++ * ++ * What we do for this specific read operation depends on two questions: ++ * ++ * 1) Are we doing a "raw" read, or an ECC-based read? ++ * ++ * 2) Are we using block mark swapping or transcription? ++ * ++ * There are four cases, illustrated by the following Karnaugh map: ++ * ++ * | Raw | ECC-based | ++ * -------------+-------------------------+-------------------------+ ++ * | Read the conventional | | ++ * | OOB at the end of the | | ++ * Swapping | page and return it. It | | ++ * | contains exactly what | | ++ * | we want. | Read the block mark and | ++ * -------------+-------------------------+ return it in a buffer | ++ * | Read the conventional | full of set bits. | ++ * | OOB at the end of the | | ++ * | page and also the block | | ++ * Transcribing | mark in the metadata. | | ++ * | Copy the block mark | | ++ * | into the first byte of | | ++ * | the OOB. | | ++ * -------------+-------------------------+-------------------------+ ++ * ++ * Note that we break rule #4 in the Transcribing/Raw case because we're not ++ * giving an accurate view of the actual, physical bytes in the page (we're ++ * overwriting the block mark). That's OK because it's more important to follow ++ * rule #2. ++ * ++ * It turns out that knowing whether we want an "ECC-based" or "raw" read is not ++ * easy. When reading a page, for example, the NAND Flash MTD code calls our ++ * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an ++ * ECC-based or raw view of the page is implicit in which function it calls ++ * (there is a similar pair of ECC-based/raw functions for writing). ++ */ ++static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ ++ dev_dbg(this->dev, "page number is %d\n", page); ++ /* clear the OOB buffer */ ++ memset(chip->oob_poi, ~0, mtd->oobsize); ++ ++ /* Read out the conventional OOB. */ ++ nand_read_page_op(chip, page, mtd->writesize, NULL, 0); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ /* ++ * Now, we want to make sure the block mark is correct. In the ++ * non-transcribing case (!GPMI_IS_MX23()), we already have it. ++ * Otherwise, we need to explicitly read it. ++ */ ++ if (GPMI_IS_MX23(this)) { ++ /* Read the block mark into the first byte of the OOB buffer. */ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ chip->oob_poi[0] = chip->read_byte(mtd); ++ } ++ ++ return 0; ++} ++ ++static int ++gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) ++{ ++ struct mtd_oob_region of = { }; ++ ++ /* Do we have available oob area? */ ++ mtd_ooblayout_free(mtd, 0, &of); ++ if (!of.length) ++ return -EPERM; ++ ++ if (!nand_is_slc(chip)) ++ return -EPERM; ++ ++ return nand_prog_page_op(chip, page, mtd->writesize + of.offset, ++ chip->oob_poi + of.offset, of.length); ++} ++ ++/* ++ * This function reads a NAND page without involving the ECC engine (no HW ++ * ECC correction). ++ * The tricky part in the GPMI/BCH controller is that it stores ECC bits ++ * inline (interleaved with payload DATA), and do not align data chunk on ++ * byte boundaries. ++ * We thus need to take care moving the payload data and ECC bits stored in the ++ * page into the provided buffers, which is why we're using gpmi_copy_bits. ++ * ++ * See set_geometry_by_ecc_info inline comments to have a full description ++ * of the layout used by the GPMI controller. ++ */ ++static int gpmi_ecc_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ struct bch_geometry *nfc_geo = &this->bch_geometry; ++ int eccsize = nfc_geo->ecc_chunk_size; ++ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; ++ u8 *tmp_buf = this->raw_buffer; ++ size_t src_bit_off; ++ size_t oob_bit_off; ++ size_t oob_byte_off; ++ uint8_t *oob = chip->oob_poi; ++ int step; ++ ++ nand_read_page_op(chip, page, 0, tmp_buf, ++ mtd->writesize + mtd->oobsize); ++ ++ /* ++ * If required, swap the bad block marker and the data stored in the ++ * metadata section, so that we don't wrongly consider a block as bad. ++ * ++ * See the layout description for a detailed explanation on why this ++ * is needed. ++ */ ++ if (this->swap_block_mark) { ++ u8 swap = tmp_buf[0]; ++ ++ tmp_buf[0] = tmp_buf[mtd->writesize]; ++ tmp_buf[mtd->writesize] = swap; ++ } ++ ++ /* ++ * Copy the metadata section into the oob buffer (this section is ++ * guaranteed to be aligned on a byte boundary). ++ */ ++ if (oob_required) ++ memcpy(oob, tmp_buf, nfc_geo->metadata_size); ++ ++ oob_bit_off = nfc_geo->metadata_size * 8; ++ src_bit_off = oob_bit_off; ++ ++ /* Extract interleaved payload data and ECC bits */ ++ for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { ++ if (buf) ++ gpmi_copy_bits(buf, step * eccsize * 8, ++ tmp_buf, src_bit_off, ++ eccsize * 8); ++ src_bit_off += eccsize * 8; ++ ++ /* Align last ECC block to align a byte boundary */ ++ if (step == nfc_geo->ecc_chunk_count - 1 && ++ (oob_bit_off + eccbits) % 8) ++ eccbits += 8 - ((oob_bit_off + eccbits) % 8); ++ ++ if (oob_required) ++ gpmi_copy_bits(oob, oob_bit_off, ++ tmp_buf, src_bit_off, ++ eccbits); ++ ++ src_bit_off += eccbits; ++ oob_bit_off += eccbits; ++ } ++ ++ if (oob_required) { ++ oob_byte_off = oob_bit_off / 8; ++ ++ if (oob_byte_off < mtd->oobsize) ++ memcpy(oob + oob_byte_off, ++ tmp_buf + mtd->writesize + oob_byte_off, ++ mtd->oobsize - oob_byte_off); ++ } ++ ++ return 0; ++} ++ ++/* ++ * This function writes a NAND page without involving the ECC engine (no HW ++ * ECC generation). ++ * The tricky part in the GPMI/BCH controller is that it stores ECC bits ++ * inline (interleaved with payload DATA), and do not align data chunk on ++ * byte boundaries. ++ * We thus need to take care moving the OOB area at the right place in the ++ * final page, which is why we're using gpmi_copy_bits. ++ * ++ * See set_geometry_by_ecc_info inline comments to have a full description ++ * of the layout used by the GPMI controller. ++ */ ++static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ struct bch_geometry *nfc_geo = &this->bch_geometry; ++ int eccsize = nfc_geo->ecc_chunk_size; ++ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; ++ u8 *tmp_buf = this->raw_buffer; ++ uint8_t *oob = chip->oob_poi; ++ size_t dst_bit_off; ++ size_t oob_bit_off; ++ size_t oob_byte_off; ++ int step; ++ ++ /* ++ * Initialize all bits to 1 in case we don't have a buffer for the ++ * payload or oob data in order to leave unspecified bits of data ++ * to their initial state. ++ */ ++ if (!buf || !oob_required) ++ memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize); ++ ++ /* ++ * First copy the metadata section (stored in oob buffer) at the ++ * beginning of the page, as imposed by the GPMI layout. ++ */ ++ memcpy(tmp_buf, oob, nfc_geo->metadata_size); ++ oob_bit_off = nfc_geo->metadata_size * 8; ++ dst_bit_off = oob_bit_off; ++ ++ /* Interleave payload data and ECC bits */ ++ for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { ++ if (buf) ++ gpmi_copy_bits(tmp_buf, dst_bit_off, ++ buf, step * eccsize * 8, eccsize * 8); ++ dst_bit_off += eccsize * 8; ++ ++ /* Align last ECC block to align a byte boundary */ ++ if (step == nfc_geo->ecc_chunk_count - 1 && ++ (oob_bit_off + eccbits) % 8) ++ eccbits += 8 - ((oob_bit_off + eccbits) % 8); ++ ++ if (oob_required) ++ gpmi_copy_bits(tmp_buf, dst_bit_off, ++ oob, oob_bit_off, eccbits); ++ ++ dst_bit_off += eccbits; ++ oob_bit_off += eccbits; ++ } ++ ++ oob_byte_off = oob_bit_off / 8; ++ ++ if (oob_required && oob_byte_off < mtd->oobsize) ++ memcpy(tmp_buf + mtd->writesize + oob_byte_off, ++ oob + oob_byte_off, mtd->oobsize - oob_byte_off); ++ ++ /* ++ * If required, swap the bad block marker and the first byte of the ++ * metadata section, so that we don't modify the bad block marker. ++ * ++ * See the layout description for a detailed explanation on why this ++ * is needed. ++ */ ++ if (this->swap_block_mark) { ++ u8 swap = tmp_buf[0]; ++ ++ tmp_buf[0] = tmp_buf[mtd->writesize]; ++ tmp_buf[mtd->writesize] = swap; ++ } ++ ++ return nand_prog_page_op(chip, page, 0, tmp_buf, ++ mtd->writesize + mtd->oobsize); ++} ++ ++static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page); ++} ++ ++static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page); ++} ++ ++static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct gpmi_nand_data *this = nand_get_controller_data(chip); ++ int ret = 0; ++ uint8_t *block_mark; ++ int column, page, chipnr; ++ ++ chipnr = (int)(ofs >> chip->chip_shift); ++ chip->select_chip(mtd, chipnr); ++ ++ column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; ++ ++ /* Write the block mark. */ ++ block_mark = this->data_buffer_dma; ++ block_mark[0] = 0; /* bad block marker */ ++ ++ /* Shift to get page */ ++ page = (int)(ofs >> chip->page_shift); ++ ++ ret = nand_prog_page_op(chip, page, column, block_mark, 1); ++ ++ chip->select_chip(mtd, -1); ++ ++ return ret; ++} ++ ++static int nand_boot_set_geometry(struct gpmi_nand_data *this) ++{ ++ struct boot_rom_geometry *geometry = &this->rom_geometry; ++ ++ /* ++ * Set the boot block stride size. ++ * ++ * In principle, we should be reading this from the OTP bits, since ++ * that's where the ROM is going to get it. In fact, we don't have any ++ * way to read the OTP bits, so we go with the default and hope for the ++ * best. ++ */ ++ geometry->stride_size_in_pages = 64; ++ ++ /* ++ * Set the search area stride exponent. ++ * ++ * In principle, we should be reading this from the OTP bits, since ++ * that's where the ROM is going to get it. In fact, we don't have any ++ * way to read the OTP bits, so we go with the default and hope for the ++ * best. ++ */ ++ geometry->search_area_stride_exponent = 2; ++ return 0; ++} ++ ++static const char *fingerprint = "STMP"; ++static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) ++{ ++ struct boot_rom_geometry *rom_geo = &this->rom_geometry; ++ struct device *dev = this->dev; ++ struct nand_chip *chip = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int search_area_size_in_strides; ++ unsigned int stride; ++ unsigned int page; ++ uint8_t *buffer = chip->data_buf; ++ int saved_chip_number; ++ int found_an_ncb_fingerprint = false; ++ ++ /* Compute the number of strides in a search area. */ ++ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; ++ ++ saved_chip_number = this->current_chip; ++ chip->select_chip(mtd, 0); ++ ++ /* ++ * Loop through the first search area, looking for the NCB fingerprint. ++ */ ++ dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); ++ ++ for (stride = 0; stride < search_area_size_in_strides; stride++) { ++ /* Compute the page addresses. */ ++ page = stride * rom_geo->stride_size_in_pages; ++ ++ dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); ++ ++ /* ++ * Read the NCB fingerprint. The fingerprint is four bytes long ++ * and starts in the 12th byte of the page. ++ */ ++ nand_read_page_op(chip, page, 12, NULL, 0); ++ chip->read_buf(mtd, buffer, strlen(fingerprint)); ++ ++ /* Look for the fingerprint. */ ++ if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { ++ found_an_ncb_fingerprint = true; ++ break; ++ } ++ ++ } ++ ++ chip->select_chip(mtd, saved_chip_number); ++ ++ if (found_an_ncb_fingerprint) ++ dev_dbg(dev, "\tFound a fingerprint\n"); ++ else ++ dev_dbg(dev, "\tNo fingerprint found\n"); ++ return found_an_ncb_fingerprint; ++} ++ ++/* Writes a transcription stamp. */ ++static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) ++{ ++ struct device *dev = this->dev; ++ struct boot_rom_geometry *rom_geo = &this->rom_geometry; ++ struct nand_chip *chip = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int block_size_in_pages; ++ unsigned int search_area_size_in_strides; ++ unsigned int search_area_size_in_pages; ++ unsigned int search_area_size_in_blocks; ++ unsigned int block; ++ unsigned int stride; ++ unsigned int page; ++ uint8_t *buffer = chip->data_buf; ++ int saved_chip_number; ++ int status; ++ ++ /* Compute the search area geometry. */ ++ block_size_in_pages = mtd->erasesize / mtd->writesize; ++ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; ++ search_area_size_in_pages = search_area_size_in_strides * ++ rom_geo->stride_size_in_pages; ++ search_area_size_in_blocks = ++ (search_area_size_in_pages + (block_size_in_pages - 1)) / ++ block_size_in_pages; ++ ++ dev_dbg(dev, "Search Area Geometry :\n"); ++ dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks); ++ dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); ++ dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); ++ ++ /* Select chip 0. */ ++ saved_chip_number = this->current_chip; ++ chip->select_chip(mtd, 0); ++ ++ /* Loop over blocks in the first search area, erasing them. */ ++ dev_dbg(dev, "Erasing the search area...\n"); ++ ++ for (block = 0; block < search_area_size_in_blocks; block++) { ++ /* Erase this block. */ ++ dev_dbg(dev, "\tErasing block 0x%x\n", block); ++ status = nand_erase_op(chip, block); ++ if (status) ++ dev_err(dev, "[%s] Erase failed.\n", __func__); ++ } ++ ++ /* Write the NCB fingerprint into the page buffer. */ ++ memset(buffer, ~0, mtd->writesize); ++ memcpy(buffer + 12, fingerprint, strlen(fingerprint)); ++ ++ /* Loop through the first search area, writing NCB fingerprints. */ ++ dev_dbg(dev, "Writing NCB fingerprints...\n"); ++ for (stride = 0; stride < search_area_size_in_strides; stride++) { ++ /* Compute the page addresses. */ ++ page = stride * rom_geo->stride_size_in_pages; ++ ++ /* Write the first page of the current stride. */ ++ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); ++ ++ status = chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); ++ if (status) ++ dev_err(dev, "[%s] Write failed.\n", __func__); ++ } ++ ++ /* Deselect chip 0. */ ++ chip->select_chip(mtd, saved_chip_number); ++ return 0; ++} ++ ++static int mx23_boot_init(struct gpmi_nand_data *this) ++{ ++ struct device *dev = this->dev; ++ struct nand_chip *chip = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int block_count; ++ unsigned int block; ++ int chipnr; ++ int page; ++ loff_t byte; ++ uint8_t block_mark; ++ int ret = 0; ++ ++ /* ++ * If control arrives here, we can't use block mark swapping, which ++ * means we're forced to use transcription. First, scan for the ++ * transcription stamp. If we find it, then we don't have to do ++ * anything -- the block marks are already transcribed. ++ */ ++ if (mx23_check_transcription_stamp(this)) ++ return 0; ++ ++ /* ++ * If control arrives here, we couldn't find a transcription stamp, so ++ * so we presume the block marks are in the conventional location. ++ */ ++ dev_dbg(dev, "Transcribing bad block marks...\n"); ++ ++ /* Compute the number of blocks in the entire medium. */ ++ block_count = chip->chipsize >> chip->phys_erase_shift; ++ ++ /* ++ * Loop over all the blocks in the medium, transcribing block marks as ++ * we go. ++ */ ++ for (block = 0; block < block_count; block++) { ++ /* ++ * Compute the chip, page and byte addresses for this block's ++ * conventional mark. ++ */ ++ chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); ++ page = block << (chip->phys_erase_shift - chip->page_shift); ++ byte = block << chip->phys_erase_shift; ++ ++ /* Send the command to read the conventional block mark. */ ++ chip->select_chip(mtd, chipnr); ++ nand_read_page_op(chip, page, mtd->writesize, NULL, 0); ++ block_mark = chip->read_byte(mtd); ++ chip->select_chip(mtd, -1); ++ ++ /* ++ * Check if the block is marked bad. If so, we need to mark it ++ * again, but this time the result will be a mark in the ++ * location where we transcribe block marks. ++ */ ++ if (block_mark != 0xff) { ++ dev_dbg(dev, "Transcribing mark in block %u\n", block); ++ ret = chip->block_markbad(mtd, byte); ++ if (ret) ++ dev_err(dev, ++ "Failed to mark block bad with ret %d\n", ++ ret); ++ } ++ } ++ ++ /* Write the stamp that indicates we've transcribed the block marks. */ ++ mx23_write_transcription_stamp(this); ++ return 0; ++} ++ ++static int nand_boot_init(struct gpmi_nand_data *this) ++{ ++ nand_boot_set_geometry(this); ++ ++ /* This is ROM arch-specific initilization before the BBT scanning. */ ++ if (GPMI_IS_MX23(this)) ++ return mx23_boot_init(this); ++ return 0; ++} ++ ++static int gpmi_set_geometry(struct gpmi_nand_data *this) ++{ ++ int ret; ++ ++ /* Free the temporary DMA memory for reading ID. */ ++ gpmi_free_dma_buffer(this); ++ ++ /* Set up the NFC geometry which is used by BCH. */ ++ ret = bch_set_geometry(this); ++ if (ret) { ++ dev_err(this->dev, "Error setting BCH geometry : %d\n", ret); ++ return ret; ++ } ++ ++ /* Alloc the new DMA buffers according to the pagesize and oobsize */ ++ return gpmi_alloc_dma_buffer(this); ++} ++ ++static int gpmi_init_last(struct gpmi_nand_data *this) ++{ ++ struct nand_chip *chip = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ struct bch_geometry *bch_geo = &this->bch_geometry; ++ int ret; ++ ++ /* Set up the medium geometry */ ++ ret = gpmi_set_geometry(this); ++ if (ret) ++ return ret; ++ ++ /* Init the nand_ecc_ctrl{} */ ++ ecc->read_page = gpmi_ecc_read_page; ++ ecc->write_page = gpmi_ecc_write_page; ++ ecc->read_oob = gpmi_ecc_read_oob; ++ ecc->write_oob = gpmi_ecc_write_oob; ++ ecc->read_page_raw = gpmi_ecc_read_page_raw; ++ ecc->write_page_raw = gpmi_ecc_write_page_raw; ++ ecc->read_oob_raw = gpmi_ecc_read_oob_raw; ++ ecc->write_oob_raw = gpmi_ecc_write_oob_raw; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = bch_geo->ecc_chunk_size; ++ ecc->strength = bch_geo->ecc_strength; ++ mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); ++ ++ /* ++ * We only enable the subpage read when: ++ * (1) the chip is imx6, and ++ * (2) the size of the ECC parity is byte aligned. ++ */ ++ if (GPMI_IS_MX6(this) && ++ ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { ++ ecc->read_subpage = gpmi_ecc_read_subpage; ++ chip->options |= NAND_SUBPAGE_READ; ++ } ++ ++ /* ++ * Can we enable the extra features? such as EDO or Sync mode. ++ * ++ * We do not check the return value now. That's means if we fail in ++ * enable the extra features, we still can run in the normal way. ++ */ ++ gpmi_extra_init(this); ++ ++ return 0; ++} ++ ++static int gpmi_nand_init(struct gpmi_nand_data *this) ++{ ++ struct nand_chip *chip = &this->nand; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ /* init current chip */ ++ this->current_chip = -1; ++ ++ /* init the MTD data structures */ ++ mtd->name = "gpmi-nand"; ++ mtd->dev.parent = this->dev; ++ ++ /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ ++ nand_set_controller_data(chip, this); ++ nand_set_flash_node(chip, this->pdev->dev.of_node); ++ chip->select_chip = gpmi_select_chip; ++ chip->cmd_ctrl = gpmi_cmd_ctrl; ++ chip->dev_ready = gpmi_dev_ready; ++ chip->read_byte = gpmi_read_byte; ++ chip->read_buf = gpmi_read_buf; ++ chip->write_buf = gpmi_write_buf; ++ chip->badblock_pattern = &gpmi_bbt_descr; ++ chip->block_markbad = gpmi_block_markbad; ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ ++ this->swap_block_mark = !GPMI_IS_MX23(this); ++ ++ /* ++ * Allocate a temporary DMA buffer for reading ID in the ++ * nand_scan_ident(). ++ */ ++ this->bch_geometry.payload_size = 1024; ++ this->bch_geometry.auxiliary_size = 128; ++ ret = gpmi_alloc_dma_buffer(this); ++ if (ret) ++ goto err_out; ++ ++ ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL); ++ if (ret) ++ goto err_out; ++ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) { ++ chip->bbt_options |= NAND_BBT_NO_OOB; ++ ++ if (of_property_read_bool(this->dev->of_node, ++ "fsl,no-blockmark-swap")) ++ this->swap_block_mark = false; ++ } ++ dev_dbg(this->dev, "Blockmark swapping %sabled\n", ++ this->swap_block_mark ? "en" : "dis"); ++ ++ ret = gpmi_init_last(this); ++ if (ret) ++ goto err_out; ++ ++ chip->options |= NAND_SKIP_BBTSCAN; ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ goto err_out; ++ ++ ret = nand_boot_init(this); ++ if (ret) ++ goto err_nand_cleanup; ++ ret = chip->scan_bbt(mtd); ++ if (ret) ++ goto err_nand_cleanup; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) ++ goto err_nand_cleanup; ++ return 0; ++ ++err_nand_cleanup: ++ nand_cleanup(chip); ++err_out: ++ gpmi_free_dma_buffer(this); ++ return ret; ++} ++ ++static const struct of_device_id gpmi_nand_id_table[] = { ++ { ++ .compatible = "fsl,imx23-gpmi-nand", ++ .data = &gpmi_devdata_imx23, ++ }, { ++ .compatible = "fsl,imx28-gpmi-nand", ++ .data = &gpmi_devdata_imx28, ++ }, { ++ .compatible = "fsl,imx6q-gpmi-nand", ++ .data = &gpmi_devdata_imx6q, ++ }, { ++ .compatible = "fsl,imx6sx-gpmi-nand", ++ .data = &gpmi_devdata_imx6sx, ++ }, { ++ .compatible = "fsl,imx7d-gpmi-nand", ++ .data = &gpmi_devdata_imx7d, ++ }, {} ++}; ++MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); ++ ++static int gpmi_nand_probe(struct platform_device *pdev) ++{ ++ struct gpmi_nand_data *this; ++ const struct of_device_id *of_id; ++ int ret; ++ ++ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); ++ if (!this) ++ return -ENOMEM; ++ ++ of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); ++ if (of_id) { ++ this->devdata = of_id->data; ++ } else { ++ dev_err(&pdev->dev, "Failed to find the right device id.\n"); ++ return -ENODEV; ++ } ++ ++ platform_set_drvdata(pdev, this); ++ this->pdev = pdev; ++ this->dev = &pdev->dev; ++ ++ ret = acquire_resources(this); ++ if (ret) ++ goto exit_acquire_resources; ++ ++ ret = init_hardware(this); ++ if (ret) ++ goto exit_nfc_init; ++ ++ ret = gpmi_nand_init(this); ++ if (ret) ++ goto exit_nfc_init; ++ ++ dev_info(this->dev, "driver registered.\n"); ++ ++ return 0; ++ ++exit_nfc_init: ++ release_resources(this); ++exit_acquire_resources: ++ ++ return ret; ++} ++ ++static int gpmi_nand_remove(struct platform_device *pdev) ++{ ++ struct gpmi_nand_data *this = platform_get_drvdata(pdev); ++ ++ nand_release(nand_to_mtd(&this->nand)); ++ gpmi_free_dma_buffer(this); ++ release_resources(this); ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int gpmi_pm_suspend(struct device *dev) ++{ ++ struct gpmi_nand_data *this = dev_get_drvdata(dev); ++ ++ release_dma_channels(this); ++ return 0; ++} ++ ++static int gpmi_pm_resume(struct device *dev) ++{ ++ struct gpmi_nand_data *this = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = acquire_dma_channels(this); ++ if (ret < 0) ++ return ret; ++ ++ /* re-init the GPMI registers */ ++ this->flags &= ~GPMI_TIMING_INIT_OK; ++ ret = gpmi_init(this); ++ if (ret) { ++ dev_err(this->dev, "Error setting GPMI : %d\n", ret); ++ return ret; ++ } ++ ++ /* re-init the BCH registers */ ++ ret = bch_set_geometry(this); ++ if (ret) { ++ dev_err(this->dev, "Error setting BCH : %d\n", ret); ++ return ret; ++ } ++ ++ /* re-init others */ ++ gpmi_extra_init(this); ++ ++ return 0; ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops gpmi_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) ++}; ++ ++static struct platform_driver gpmi_nand_driver = { ++ .driver = { ++ .name = "gpmi-nand", ++ .pm = &gpmi_pm_ops, ++ .of_match_table = gpmi_nand_id_table, ++ }, ++ .probe = gpmi_nand_probe, ++ .remove = gpmi_nand_remove, ++}; ++module_platform_driver(gpmi_nand_driver); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +new file mode 100644 +index 00000000..a45e4ce +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +@@ -0,0 +1,315 @@ ++/* ++ * Freescale GPMI NAND Flash Driver ++ * ++ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. ++ * Copyright (C) 2008 Embedded Alley Solutions, 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H ++#define __DRIVERS_MTD_NAND_GPMI_NAND_H ++ ++#include ++#include ++#include ++#include ++ ++#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */ ++struct resources { ++ void __iomem *gpmi_regs; ++ void __iomem *bch_regs; ++ unsigned int dma_low_channel; ++ unsigned int dma_high_channel; ++ struct clk *clock[GPMI_CLK_MAX]; ++}; ++ ++/** ++ * struct bch_geometry - BCH geometry description. ++ * @gf_len: The length of Galois Field. (e.g., 13 or 14) ++ * @ecc_strength: A number that describes the strength of the ECC ++ * algorithm. ++ * @page_size: The size, in bytes, of a physical page, including ++ * both data and OOB. ++ * @metadata_size: The size, in bytes, of the metadata. ++ * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note ++ * the first chunk in the page includes both data and ++ * metadata, so it's a bit larger than this value. ++ * @ecc_chunk_count: The number of ECC chunks in the page, ++ * @payload_size: The size, in bytes, of the payload buffer. ++ * @auxiliary_size: The size, in bytes, of the auxiliary buffer. ++ * @auxiliary_status_offset: The offset into the auxiliary buffer at which ++ * the ECC status appears. ++ * @block_mark_byte_offset: The byte offset in the ECC-based page view at ++ * which the underlying physical block mark appears. ++ * @block_mark_bit_offset: The bit offset into the ECC-based page view at ++ * which the underlying physical block mark appears. ++ */ ++struct bch_geometry { ++ unsigned int gf_len; ++ unsigned int ecc_strength; ++ unsigned int page_size; ++ unsigned int metadata_size; ++ unsigned int ecc_chunk_size; ++ unsigned int ecc_chunk_count; ++ unsigned int payload_size; ++ unsigned int auxiliary_size; ++ unsigned int auxiliary_status_offset; ++ unsigned int block_mark_byte_offset; ++ unsigned int block_mark_bit_offset; ++}; ++ ++/** ++ * struct boot_rom_geometry - Boot ROM geometry description. ++ * @stride_size_in_pages: The size of a boot block stride, in pages. ++ * @search_area_stride_exponent: The logarithm to base 2 of the size of a ++ * search area in boot block strides. ++ */ ++struct boot_rom_geometry { ++ unsigned int stride_size_in_pages; ++ unsigned int search_area_stride_exponent; ++}; ++ ++/* DMA operations types */ ++enum dma_ops_type { ++ DMA_FOR_COMMAND = 1, ++ DMA_FOR_READ_DATA, ++ DMA_FOR_WRITE_DATA, ++ DMA_FOR_READ_ECC_PAGE, ++ DMA_FOR_WRITE_ECC_PAGE ++}; ++ ++/** ++ * struct nand_timing - Fundamental timing attributes for NAND. ++ * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the ++ * maximum of tDS and tWP. A negative value ++ * indicates this characteristic isn't known. ++ * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the ++ * maximum of tDH, tWH and tREH. A negative value ++ * indicates this characteristic isn't known. ++ * @address_setup_in_ns: The address setup time, in nanoseconds. Usually ++ * the maximum of tCLS, tCS and tALS. A negative ++ * value indicates this characteristic isn't known. ++ * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value ++ * indicates this characteristic isn't known. ++ * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A ++ * negative value indicates this characteristic isn't ++ * known. ++ * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A ++ * negative value indicates this characteristic isn't ++ * known. ++ * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A ++ * negative value indicates this characteristic isn't ++ * known. ++ */ ++struct nand_timing { ++ int8_t data_setup_in_ns; ++ int8_t data_hold_in_ns; ++ int8_t address_setup_in_ns; ++ int8_t gpmi_sample_delay_in_ns; ++ int8_t tREA_in_ns; ++ int8_t tRLOH_in_ns; ++ int8_t tRHOH_in_ns; ++}; ++ ++enum gpmi_type { ++ IS_MX23, ++ IS_MX28, ++ IS_MX6Q, ++ IS_MX6SX, ++ IS_MX7D, ++}; ++ ++struct gpmi_devdata { ++ enum gpmi_type type; ++ int bch_max_ecc_strength; ++ int max_chain_delay; /* See the async EDO mode */ ++ const char * const *clks; ++ const int clks_count; ++}; ++ ++struct gpmi_nand_data { ++ /* flags */ ++#define GPMI_ASYNC_EDO_ENABLED (1 << 0) ++#define GPMI_TIMING_INIT_OK (1 << 1) ++ int flags; ++ const struct gpmi_devdata *devdata; ++ ++ /* System Interface */ ++ struct device *dev; ++ struct platform_device *pdev; ++ ++ /* Resources */ ++ struct resources resources; ++ ++ /* Flash Hardware */ ++ struct nand_timing timing; ++ int timing_mode; ++ ++ /* BCH */ ++ struct bch_geometry bch_geometry; ++ struct completion bch_done; ++ ++ /* NAND Boot issue */ ++ bool swap_block_mark; ++ struct boot_rom_geometry rom_geometry; ++ ++ /* MTD / NAND */ ++ struct nand_chip nand; ++ ++ /* General-use Variables */ ++ int current_chip; ++ unsigned int command_length; ++ ++ /* passed from upper layer */ ++ uint8_t *upper_buf; ++ int upper_len; ++ ++ /* for DMA operations */ ++ bool direct_dma_map_ok; ++ ++ struct scatterlist cmd_sgl; ++ char *cmd_buffer; ++ ++ struct scatterlist data_sgl; ++ char *data_buffer_dma; ++ ++ void *page_buffer_virt; ++ dma_addr_t page_buffer_phys; ++ unsigned int page_buffer_size; ++ ++ void *payload_virt; ++ dma_addr_t payload_phys; ++ ++ void *auxiliary_virt; ++ dma_addr_t auxiliary_phys; ++ ++ void *raw_buffer; ++ ++ /* DMA channels */ ++#define DMA_CHANS 8 ++ struct dma_chan *dma_chans[DMA_CHANS]; ++ enum dma_ops_type last_dma_type; ++ enum dma_ops_type dma_type; ++ struct completion dma_done; ++ ++ /* private */ ++ void *private; ++}; ++ ++/** ++ * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. ++ * @data_setup_in_cycles: The data setup time, in cycles. ++ * @data_hold_in_cycles: The data hold time, in cycles. ++ * @address_setup_in_cycles: The address setup time, in cycles. ++ * @device_busy_timeout: The timeout waiting for NAND Ready/Busy, ++ * this value is the number of cycles multiplied ++ * by 4096. ++ * @use_half_periods: Indicates the clock is running slowly, so the ++ * NFC DLL should use half-periods. ++ * @sample_delay_factor: The sample delay factor. ++ * @wrn_dly_sel: The delay on the GPMI write strobe. ++ */ ++struct gpmi_nfc_hardware_timing { ++ /* for HW_GPMI_TIMING0 */ ++ uint8_t data_setup_in_cycles; ++ uint8_t data_hold_in_cycles; ++ uint8_t address_setup_in_cycles; ++ ++ /* for HW_GPMI_TIMING1 */ ++ uint16_t device_busy_timeout; ++#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/ ++ ++ /* for HW_GPMI_CTRL1 */ ++ bool use_half_periods; ++ uint8_t sample_delay_factor; ++ uint8_t wrn_dly_sel; ++}; ++ ++/** ++ * struct timing_threshold - Timing threshold ++ * @max_data_setup_cycles: The maximum number of data setup cycles that ++ * can be expressed in the hardware. ++ * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires ++ * for data read internal setup. In the Reference ++ * Manual, see the chapter "High-Speed NAND ++ * Timing" for more details. ++ * @max_sample_delay_factor: The maximum sample delay factor that can be ++ * expressed in the hardware. ++ * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the ++ * sample delay DLL hardware can possibly work ++ * with (the DLL is unusable with longer periods). ++ * If the full-cycle period is greater than HALF ++ * this value, the DLL must be configured to use ++ * half-periods. ++ * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the ++ * DLL can implement. ++ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current ++ * I/O transaction. If no I/O transaction is in ++ * progress, this is the clock frequency during ++ * the most recent I/O transaction. ++ */ ++struct timing_threshold { ++ const unsigned int max_chip_count; ++ const unsigned int max_data_setup_cycles; ++ const unsigned int internal_data_setup_in_ns; ++ const unsigned int max_sample_delay_factor; ++ const unsigned int max_dll_clock_period_in_ns; ++ const unsigned int max_dll_delay_in_ns; ++ unsigned long clock_frequency_in_hz; ++ ++}; ++ ++/* Common Services */ ++extern int common_nfc_set_geometry(struct gpmi_nand_data *); ++extern struct dma_chan *get_dma_chan(struct gpmi_nand_data *); ++extern void prepare_data_dma(struct gpmi_nand_data *, ++ enum dma_data_direction dr); ++extern int start_dma_without_bch_irq(struct gpmi_nand_data *, ++ struct dma_async_tx_descriptor *); ++extern int start_dma_with_bch_irq(struct gpmi_nand_data *, ++ struct dma_async_tx_descriptor *); ++ ++/* GPMI-NAND helper function library */ ++extern int gpmi_init(struct gpmi_nand_data *); ++extern int gpmi_extra_init(struct gpmi_nand_data *); ++extern void gpmi_clear_bch(struct gpmi_nand_data *); ++extern void gpmi_dump_info(struct gpmi_nand_data *); ++extern int bch_set_geometry(struct gpmi_nand_data *); ++extern int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); ++extern int gpmi_send_command(struct gpmi_nand_data *); ++extern void gpmi_begin(struct gpmi_nand_data *); ++extern void gpmi_end(struct gpmi_nand_data *); ++extern int gpmi_read_data(struct gpmi_nand_data *); ++extern int gpmi_send_data(struct gpmi_nand_data *); ++extern int gpmi_send_page(struct gpmi_nand_data *, ++ dma_addr_t payload, dma_addr_t auxiliary); ++extern int gpmi_read_page(struct gpmi_nand_data *, ++ dma_addr_t payload, dma_addr_t auxiliary); ++ ++void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, ++ const u8 *src, size_t src_bit_off, ++ size_t nbits); ++ ++/* BCH : Status Block Completion Codes */ ++#define STATUS_GOOD 0x00 ++#define STATUS_ERASED 0xff ++#define STATUS_UNCORRECTABLE 0xfe ++ ++/* Use the devdata to distinguish different Archs. */ ++#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23) ++#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28) ++#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q) ++#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX) ++#define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D) ++ ++#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \ ++ GPMI_IS_MX7D(x)) ++#endif +diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h +new file mode 100644 +index 00000000..82114cd +--- /dev/null ++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h +@@ -0,0 +1,187 @@ ++/* ++ * Freescale GPMI NAND Flash Driver ++ * ++ * Copyright 2008-2011 Freescale Semiconductor, Inc. ++ * Copyright 2008 Embedded Alley Solutions, 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. ++ * ++ * 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. ++ */ ++#ifndef __GPMI_NAND_GPMI_REGS_H ++#define __GPMI_NAND_GPMI_REGS_H ++ ++#define HW_GPMI_CTRL0 0x00000000 ++#define HW_GPMI_CTRL0_SET 0x00000004 ++#define HW_GPMI_CTRL0_CLR 0x00000008 ++#define HW_GPMI_CTRL0_TOG 0x0000000c ++ ++#define BP_GPMI_CTRL0_COMMAND_MODE 24 ++#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) ++#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ ++ (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) ++#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 ++#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 ++#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 ++#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 ++ ++#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) ++#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 ++#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 ++ ++/* ++ * Difference in LOCK_CS between imx23 and imx28 : ++ * This bit may impact the _POWER_ consumption. So some chips ++ * do not set it. ++ */ ++#define MX23_BP_GPMI_CTRL0_LOCK_CS 22 ++#define MX28_BP_GPMI_CTRL0_LOCK_CS 27 ++#define LOCK_CS_ENABLE 0x1 ++#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0 ++ ++/* Difference in CS between imx23 and imx28 */ ++#define BP_GPMI_CTRL0_CS 20 ++#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) ++#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) ++#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \ ++ (GPMI_IS_MX23((x)) \ ++ ? MX23_BM_GPMI_CTRL0_CS \ ++ : MX28_BM_GPMI_CTRL0_CS)) ++ ++#define BP_GPMI_CTRL0_ADDRESS 17 ++#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) ++#define BF_GPMI_CTRL0_ADDRESS(v) \ ++ (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) ++#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 ++#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 ++#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 ++ ++#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) ++#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 ++#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 ++ ++#define BP_GPMI_CTRL0_XFER_COUNT 0 ++#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) ++#define BF_GPMI_CTRL0_XFER_COUNT(v) \ ++ (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) ++ ++#define HW_GPMI_COMPARE 0x00000010 ++ ++#define HW_GPMI_ECCCTRL 0x00000020 ++#define HW_GPMI_ECCCTRL_SET 0x00000024 ++#define HW_GPMI_ECCCTRL_CLR 0x00000028 ++#define HW_GPMI_ECCCTRL_TOG 0x0000002c ++ ++#define BP_GPMI_ECCCTRL_ECC_CMD 13 ++#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) ++#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ ++ (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) ++#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 ++#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 ++ ++#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) ++#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 ++#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 ++ ++#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 ++#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) ++#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ ++ (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) ++#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 ++#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF ++ ++#define HW_GPMI_ECCCOUNT 0x00000030 ++#define HW_GPMI_PAYLOAD 0x00000040 ++#define HW_GPMI_AUXILIARY 0x00000050 ++#define HW_GPMI_CTRL1 0x00000060 ++#define HW_GPMI_CTRL1_SET 0x00000064 ++#define HW_GPMI_CTRL1_CLR 0x00000068 ++#define HW_GPMI_CTRL1_TOG 0x0000006c ++ ++#define BP_GPMI_CTRL1_DECOUPLE_CS 24 ++#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS) ++ ++#define BP_GPMI_CTRL1_WRN_DLY_SEL 22 ++#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL) ++#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ ++ (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL) ++#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0 ++#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1 ++#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 ++#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 ++ ++#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) ++ ++#define BP_GPMI_CTRL1_DLL_ENABLE 17 ++#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) ++ ++#define BP_GPMI_CTRL1_HALF_PERIOD 16 ++#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) ++ ++#define BP_GPMI_CTRL1_RDN_DELAY 12 ++#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) ++#define BF_GPMI_CTRL1_RDN_DELAY(v) \ ++ (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) ++ ++#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) ++#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 ++#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 ++ ++#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) ++#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 ++#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 ++ ++#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) ++#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 ++#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 ++ ++#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) ++ ++#define HW_GPMI_TIMING0 0x00000070 ++ ++#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 ++#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP) ++#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ ++ (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) ++ ++#define BP_GPMI_TIMING0_DATA_HOLD 8 ++#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) ++#define BF_GPMI_TIMING0_DATA_HOLD(v) \ ++ (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) ++ ++#define BP_GPMI_TIMING0_DATA_SETUP 0 ++#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) ++#define BF_GPMI_TIMING0_DATA_SETUP(v) \ ++ (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) ++ ++#define HW_GPMI_TIMING1 0x00000080 ++#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16 ++#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT) ++#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \ ++ (((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT) ++ ++#define HW_GPMI_TIMING2 0x00000090 ++#define HW_GPMI_DATA 0x000000a0 ++ ++/* MX28 uses this to detect READY. */ ++#define HW_GPMI_STAT 0x000000b0 ++#define MX28_BP_GPMI_STAT_READY_BUSY 24 ++#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY) ++#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ ++ (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) ++ ++/* MX23 uses this to detect READY. */ ++#define HW_GPMI_DEBUG 0x000000c0 ++#define MX23_BP_GPMI_DEBUG_READY0 28 ++#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) ++#endif +diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c +new file mode 100644 +index 00000000..cb86279 +--- /dev/null ++++ b/drivers/mtd/nand/raw/hisi504_nand.c +@@ -0,0 +1,896 @@ ++/* ++ * Hisilicon NAND Flash controller driver ++ * ++ * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd. ++ * http://www.hisilicon.com ++ * ++ * Author: Zhou Wang ++ * The initial developer of the original code is Zhiyong Cai ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HINFC504_MAX_CHIP (4) ++#define HINFC504_W_LATCH (5) ++#define HINFC504_R_LATCH (7) ++#define HINFC504_RW_LATCH (3) ++ ++#define HINFC504_NFC_TIMEOUT (2 * HZ) ++#define HINFC504_NFC_PM_TIMEOUT (1 * HZ) ++#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ) ++#define HINFC504_CHIP_DELAY (25) ++ ++#define HINFC504_REG_BASE_ADDRESS_LEN (0x100) ++#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128) ++ ++#define HINFC504_ADDR_CYCLE_MASK 0x4 ++ ++#define HINFC504_CON 0x00 ++#define HINFC504_CON_OP_MODE_NORMAL BIT(0) ++#define HINFC504_CON_PAGEISZE_SHIFT (1) ++#define HINFC504_CON_PAGESIZE_MASK (0x07) ++#define HINFC504_CON_BUS_WIDTH BIT(4) ++#define HINFC504_CON_READY_BUSY_SEL BIT(8) ++#define HINFC504_CON_ECCTYPE_SHIFT (9) ++#define HINFC504_CON_ECCTYPE_MASK (0x07) ++ ++#define HINFC504_PWIDTH 0x04 ++#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ ++ ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) ++ ++#define HINFC504_CMD 0x0C ++#define HINFC504_ADDRL 0x10 ++#define HINFC504_ADDRH 0x14 ++#define HINFC504_DATA_NUM 0x18 ++ ++#define HINFC504_OP 0x1C ++#define HINFC504_OP_READ_DATA_EN BIT(1) ++#define HINFC504_OP_WAIT_READY_EN BIT(2) ++#define HINFC504_OP_CMD2_EN BIT(3) ++#define HINFC504_OP_WRITE_DATA_EN BIT(4) ++#define HINFC504_OP_ADDR_EN BIT(5) ++#define HINFC504_OP_CMD1_EN BIT(6) ++#define HINFC504_OP_NF_CS_SHIFT (7) ++#define HINFC504_OP_NF_CS_MASK (3) ++#define HINFC504_OP_ADDR_CYCLE_SHIFT (9) ++#define HINFC504_OP_ADDR_CYCLE_MASK (7) ++ ++#define HINFC504_STATUS 0x20 ++#define HINFC504_READY BIT(0) ++ ++#define HINFC504_INTEN 0x24 ++#define HINFC504_INTEN_DMA BIT(9) ++#define HINFC504_INTEN_UE BIT(6) ++#define HINFC504_INTEN_CE BIT(5) ++ ++#define HINFC504_INTS 0x28 ++#define HINFC504_INTS_DMA BIT(9) ++#define HINFC504_INTS_UE BIT(6) ++#define HINFC504_INTS_CE BIT(5) ++ ++#define HINFC504_INTCLR 0x2C ++#define HINFC504_INTCLR_DMA BIT(9) ++#define HINFC504_INTCLR_UE BIT(6) ++#define HINFC504_INTCLR_CE BIT(5) ++ ++#define HINFC504_ECC_STATUS 0x5C ++#define HINFC504_ECC_16_BIT_SHIFT 12 ++ ++#define HINFC504_DMA_CTRL 0x60 ++#define HINFC504_DMA_CTRL_DMA_START BIT(0) ++#define HINFC504_DMA_CTRL_WE BIT(1) ++#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2) ++#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3) ++#define HINFC504_DMA_CTRL_BURST4_EN BIT(4) ++#define HINFC504_DMA_CTRL_BURST8_EN BIT(5) ++#define HINFC504_DMA_CTRL_BURST16_EN BIT(6) ++#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7) ++#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1) ++#define HINFC504_DMA_CTRL_CS_SHIFT (8) ++#define HINFC504_DMA_CTRL_CS_MASK (0x03) ++ ++#define HINFC504_DMA_ADDR_DATA 0x64 ++#define HINFC504_DMA_ADDR_OOB 0x68 ++ ++#define HINFC504_DMA_LEN 0x6C ++#define HINFC504_DMA_LEN_OOB_SHIFT (16) ++#define HINFC504_DMA_LEN_OOB_MASK (0xFFF) ++ ++#define HINFC504_DMA_PARA 0x70 ++#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0) ++#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1) ++#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2) ++#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3) ++#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4) ++#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5) ++ ++#define HINFC_VERSION 0x74 ++#define HINFC504_LOG_READ_ADDR 0x7C ++#define HINFC504_LOG_READ_LEN 0x80 ++ ++#define HINFC504_NANDINFO_LEN 0x10 ++ ++struct hinfc_host { ++ struct nand_chip chip; ++ struct device *dev; ++ void __iomem *iobase; ++ void __iomem *mmio; ++ struct completion cmd_complete; ++ unsigned int offset; ++ unsigned int command; ++ int chipselect; ++ unsigned int addr_cycle; ++ u32 addr_value[2]; ++ u32 cache_addr_value[2]; ++ char *buffer; ++ dma_addr_t dma_buffer; ++ dma_addr_t dma_oob; ++ int version; ++ unsigned int irq_status; /* interrupt status */ ++}; ++ ++static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg) ++{ ++ return readl(host->iobase + reg); ++} ++ ++static inline void hinfc_write(struct hinfc_host *host, unsigned int value, ++ unsigned int reg) ++{ ++ writel(value, host->iobase + reg); ++} ++ ++static void wait_controller_finished(struct hinfc_host *host) ++{ ++ unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT; ++ int val; ++ ++ while (time_before(jiffies, timeout)) { ++ val = hinfc_read(host, HINFC504_STATUS); ++ if (host->command == NAND_CMD_ERASE2) { ++ /* nfc is ready */ ++ while (!(val & HINFC504_READY)) { ++ usleep_range(500, 1000); ++ val = hinfc_read(host, HINFC504_STATUS); ++ } ++ return; ++ } ++ ++ if (val & HINFC504_READY) ++ return; ++ } ++ ++ /* wait cmd timeout */ ++ dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n"); ++} ++ ++static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned long val; ++ int ret; ++ ++ hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); ++ hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); ++ ++ if (chip->ecc.mode == NAND_ECC_NONE) { ++ hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) ++ << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); ++ ++ hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN ++ | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA); ++ } else { ++ if (host->command == NAND_CMD_READOOB) ++ hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN ++ | HINFC504_DMA_PARA_OOB_EDC_EN ++ | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); ++ else ++ hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN ++ | HINFC504_DMA_PARA_OOB_RW_EN ++ | HINFC504_DMA_PARA_DATA_EDC_EN ++ | HINFC504_DMA_PARA_OOB_EDC_EN ++ | HINFC504_DMA_PARA_DATA_ECC_EN ++ | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); ++ ++ } ++ ++ val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN ++ | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN ++ | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN ++ | ((host->addr_cycle == 4 ? 1 : 0) ++ << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT) ++ | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK) ++ << HINFC504_DMA_CTRL_CS_SHIFT)); ++ ++ if (todev) ++ val |= HINFC504_DMA_CTRL_WE; ++ ++ init_completion(&host->cmd_complete); ++ ++ hinfc_write(host, val, HINFC504_DMA_CTRL); ++ ret = wait_for_completion_timeout(&host->cmd_complete, ++ HINFC504_NFC_DMA_TIMEOUT); ++ ++ if (!ret) { ++ dev_err(host->dev, "DMA operation(irq) timeout!\n"); ++ /* sanity check */ ++ val = hinfc_read(host, HINFC504_DMA_CTRL); ++ if (!(val & HINFC504_DMA_CTRL_DMA_START)) ++ dev_err(host->dev, "DMA is already done but without irq ACK!\n"); ++ else ++ dev_err(host->dev, "DMA is really timeout!\n"); ++ } ++} ++ ++static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host) ++{ ++ host->addr_value[0] &= 0xffff0000; ++ ++ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); ++ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); ++ hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN, ++ HINFC504_CMD); ++ ++ hisi_nfc_dma_transfer(host, 1); ++ ++ return 0; ++} ++ ++static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&host->chip); ++ ++ if ((host->addr_value[0] == host->cache_addr_value[0]) && ++ (host->addr_value[1] == host->cache_addr_value[1])) ++ return 0; ++ ++ host->addr_value[0] &= 0xffff0000; ++ ++ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); ++ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); ++ hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0, ++ HINFC504_CMD); ++ ++ hinfc_write(host, 0, HINFC504_LOG_READ_ADDR); ++ hinfc_write(host, mtd->writesize + mtd->oobsize, ++ HINFC504_LOG_READ_LEN); ++ ++ hisi_nfc_dma_transfer(host, 0); ++ ++ host->cache_addr_value[0] = host->addr_value[0]; ++ host->cache_addr_value[1] = host->addr_value[1]; ++ ++ return 0; ++} ++ ++static int hisi_nfc_send_cmd_erase(struct hinfc_host *host) ++{ ++ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); ++ hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1, ++ HINFC504_CMD); ++ ++ hinfc_write(host, HINFC504_OP_WAIT_READY_EN ++ | HINFC504_OP_CMD2_EN ++ | HINFC504_OP_CMD1_EN ++ | HINFC504_OP_ADDR_EN ++ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) ++ << HINFC504_OP_NF_CS_SHIFT) ++ | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK) ++ << HINFC504_OP_ADDR_CYCLE_SHIFT), ++ HINFC504_OP); ++ ++ wait_controller_finished(host); ++ ++ return 0; ++} ++ ++static int hisi_nfc_send_cmd_readid(struct hinfc_host *host) ++{ ++ hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); ++ hinfc_write(host, NAND_CMD_READID, HINFC504_CMD); ++ hinfc_write(host, 0, HINFC504_ADDRL); ++ ++ hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN ++ | HINFC504_OP_READ_DATA_EN ++ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) ++ << HINFC504_OP_NF_CS_SHIFT) ++ | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP); ++ ++ wait_controller_finished(host); ++ ++ return 0; ++} ++ ++static int hisi_nfc_send_cmd_status(struct hinfc_host *host) ++{ ++ hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); ++ hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD); ++ hinfc_write(host, HINFC504_OP_CMD1_EN ++ | HINFC504_OP_READ_DATA_EN ++ | ((host->chipselect & HINFC504_OP_NF_CS_MASK) ++ << HINFC504_OP_NF_CS_SHIFT), ++ HINFC504_OP); ++ ++ wait_controller_finished(host); ++ ++ return 0; ++} ++ ++static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect) ++{ ++ hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD); ++ ++ hinfc_write(host, HINFC504_OP_CMD1_EN ++ | ((chipselect & HINFC504_OP_NF_CS_MASK) ++ << HINFC504_OP_NF_CS_SHIFT) ++ | HINFC504_OP_WAIT_READY_EN, ++ HINFC504_OP); ++ ++ wait_controller_finished(host); ++ ++ return 0; ++} ++ ++static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ ++ if (chipselect < 0) ++ return; ++ ++ host->chipselect = chipselect; ++} ++ ++static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ ++ if (host->command == NAND_CMD_STATUS) ++ return *(uint8_t *)(host->mmio); ++ ++ host->offset++; ++ ++ if (host->command == NAND_CMD_READID) ++ return *(uint8_t *)(host->mmio + host->offset - 1); ++ ++ return *(uint8_t *)(host->buffer + host->offset - 1); ++} ++ ++static u16 hisi_nfc_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ ++ host->offset += 2; ++ return *(u16 *)(host->buffer + host->offset - 2); ++} ++ ++static void ++hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ ++ memcpy(host->buffer + host->offset, buf, len); ++ host->offset += len; ++} ++ ++static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ ++ memcpy(buf, host->buffer + host->offset, len); ++ host->offset += len; ++} ++ ++static void set_addr(struct mtd_info *mtd, int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ unsigned int command = host->command; ++ ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (chip->options & NAND_BUSWIDTH_16 && ++ !nand_opcode_8bits(command)) ++ column >>= 1; ++ ++ host->addr_value[0] = column & 0xffff; ++ host->addr_cycle = 2; ++ } ++ if (page_addr != -1) { ++ host->addr_value[0] |= (page_addr & 0xffff) ++ << (host->addr_cycle * 8); ++ host->addr_cycle += 2; ++ if (chip->options & NAND_ROW_ADDR_3) { ++ host->addr_cycle += 1; ++ if (host->command == NAND_CMD_ERASE1) ++ host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16; ++ else ++ host->addr_value[1] |= ((page_addr >> 16) & 0xff); ++ } ++ } ++} ++ ++static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, ++ int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ int is_cache_invalid = 1; ++ unsigned int flag = 0; ++ ++ host->command = command; ++ ++ switch (command) { ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ if (command == NAND_CMD_READ0) ++ host->offset = column; ++ else ++ host->offset = column + mtd->writesize; ++ ++ is_cache_invalid = 0; ++ set_addr(mtd, column, page_addr); ++ hisi_nfc_send_cmd_readstart(host); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ host->offset = column; ++ set_addr(mtd, column, page_addr); ++ break; ++ ++ case NAND_CMD_ERASE1: ++ set_addr(mtd, column, page_addr); ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ hisi_nfc_send_cmd_pageprog(host); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ hisi_nfc_send_cmd_erase(host); ++ break; ++ ++ case NAND_CMD_READID: ++ host->offset = column; ++ memset(host->mmio, 0, 0x10); ++ hisi_nfc_send_cmd_readid(host); ++ break; ++ ++ case NAND_CMD_STATUS: ++ flag = hinfc_read(host, HINFC504_CON); ++ if (chip->ecc.mode == NAND_ECC_HW) ++ hinfc_write(host, ++ flag & ~(HINFC504_CON_ECCTYPE_MASK << ++ HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); ++ ++ host->offset = 0; ++ memset(host->mmio, 0, 0x10); ++ hisi_nfc_send_cmd_status(host); ++ hinfc_write(host, flag, HINFC504_CON); ++ break; ++ ++ case NAND_CMD_RESET: ++ hisi_nfc_send_cmd_reset(host, host->chipselect); ++ break; ++ ++ default: ++ dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n", ++ command, column, page_addr); ++ } ++ ++ if (is_cache_invalid) { ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ } ++} ++ ++static irqreturn_t hinfc_irq_handle(int irq, void *devid) ++{ ++ struct hinfc_host *host = devid; ++ unsigned int flag; ++ ++ flag = hinfc_read(host, HINFC504_INTS); ++ /* store interrupts state */ ++ host->irq_status |= flag; ++ ++ if (flag & HINFC504_INTS_DMA) { ++ hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR); ++ complete(&host->cmd_complete); ++ } else if (flag & HINFC504_INTS_CE) { ++ hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR); ++ } else if (flag & HINFC504_INTS_UE) { ++ hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int hisi_nand_read_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, int oob_required, int page) ++{ ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc; ++ int stat_1, stat_2; ++ ++ nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ /* errors which can not be corrected by ECC */ ++ if (host->irq_status & HINFC504_INTS_UE) { ++ mtd->ecc_stats.failed++; ++ } else if (host->irq_status & HINFC504_INTS_CE) { ++ /* TODO: need add other ECC modes! */ ++ switch (chip->ecc.strength) { ++ case 16: ++ status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >> ++ HINFC504_ECC_16_BIT_SHIFT & 0x0fff; ++ stat_2 = status_ecc & 0x3f; ++ stat_1 = status_ecc >> 6 & 0x3f; ++ stat = stat_1 + stat_2; ++ stat_max = max_t(int, stat_1, stat_2); ++ } ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(int, max_bitflips, stat_max); ++ } ++ host->irq_status = 0; ++ ++ return max_bitflips; ++} ++ ++static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct hinfc_host *host = nand_get_controller_data(chip); ++ ++ nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); ++ ++ if (host->irq_status & HINFC504_INTS_UE) { ++ host->irq_status = 0; ++ return -EBADMSG; ++ } ++ ++ host->irq_status = 0; ++ return 0; ++} ++ ++static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, int oob_required, ++ int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ if (oob_required) ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static void hisi_nfc_host_init(struct hinfc_host *host) ++{ ++ struct nand_chip *chip = &host->chip; ++ unsigned int flag = 0; ++ ++ host->version = hinfc_read(host, HINFC_VERSION); ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ host->chipselect = 0; ++ ++ /* default page size: 2K, ecc_none. need modify */ ++ flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL ++ | ((0x001 & HINFC504_CON_PAGESIZE_MASK) ++ << HINFC504_CON_PAGEISZE_SHIFT) ++ | ((0x0 & HINFC504_CON_ECCTYPE_MASK) ++ << HINFC504_CON_ECCTYPE_SHIFT) ++ | ((chip->options & NAND_BUSWIDTH_16) ? ++ HINFC504_CON_BUS_WIDTH : 0); ++ hinfc_write(host, flag, HINFC504_CON); ++ ++ memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN); ++ ++ hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, ++ HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); ++ ++ /* enable DMA irq */ ++ hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN); ++} ++ ++static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ /* FIXME: add ECC bytes position */ ++ return -ENOTSUPP; ++} ++ ++static int hisi_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 2; ++ oobregion->length = 6; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops hisi_ooblayout_ops = { ++ .ecc = hisi_ooblayout_ecc, ++ .free = hisi_ooblayout_free, ++}; ++ ++static int hisi_nfc_ecc_probe(struct hinfc_host *host) ++{ ++ unsigned int flag; ++ int size, strength, ecc_bits; ++ struct device *dev = host->dev; ++ struct nand_chip *chip = &host->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ size = chip->ecc.size; ++ strength = chip->ecc.strength; ++ if (size != 1024) { ++ dev_err(dev, "error ecc size: %d\n", size); ++ return -EINVAL; ++ } ++ ++ if ((size == 1024) && ((strength != 8) && (strength != 16) && ++ (strength != 24) && (strength != 40))) { ++ dev_err(dev, "ecc size and strength do not match\n"); ++ return -EINVAL; ++ } ++ ++ chip->ecc.size = size; ++ chip->ecc.strength = strength; ++ ++ chip->ecc.read_page = hisi_nand_read_page_hwecc; ++ chip->ecc.read_oob = hisi_nand_read_oob; ++ chip->ecc.write_page = hisi_nand_write_page_hwecc; ++ ++ switch (chip->ecc.strength) { ++ case 16: ++ ecc_bits = 6; ++ if (mtd->writesize == 2048) ++ mtd_set_ooblayout(mtd, &hisi_ooblayout_ops); ++ ++ /* TODO: add more page size support */ ++ break; ++ ++ /* TODO: add more ecc strength support */ ++ default: ++ dev_err(dev, "not support strength: %d\n", chip->ecc.strength); ++ return -EINVAL; ++ } ++ ++ flag = hinfc_read(host, HINFC504_CON); ++ /* add ecc type configure */ ++ flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK) ++ << HINFC504_CON_ECCTYPE_SHIFT); ++ hinfc_write(host, flag, HINFC504_CON); ++ ++ /* enable ecc irq */ ++ flag = hinfc_read(host, HINFC504_INTEN) & 0xfff; ++ hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE, ++ HINFC504_INTEN); ++ ++ return 0; ++} ++ ++static int hisi_nfc_probe(struct platform_device *pdev) ++{ ++ int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP; ++ struct device *dev = &pdev->dev; ++ struct hinfc_host *host; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct resource *res; ++ struct device_node *np = dev->of_node; ++ ++ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ host->dev = dev; ++ ++ platform_set_drvdata(pdev, host); ++ chip = &host->chip; ++ mtd = nand_to_mtd(chip); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "no IRQ resource defined\n"); ++ ret = -ENXIO; ++ goto err_res; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ host->iobase = devm_ioremap_resource(dev, res); ++ if (IS_ERR(host->iobase)) { ++ ret = PTR_ERR(host->iobase); ++ goto err_res; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ host->mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR(host->mmio)) { ++ ret = PTR_ERR(host->mmio); ++ dev_err(dev, "devm_ioremap_resource[1] fail\n"); ++ goto err_res; ++ } ++ ++ mtd->name = "hisi_nand"; ++ mtd->dev.parent = &pdev->dev; ++ ++ nand_set_controller_data(chip, host); ++ nand_set_flash_node(chip, np); ++ chip->cmdfunc = hisi_nfc_cmdfunc; ++ chip->select_chip = hisi_nfc_select_chip; ++ chip->read_byte = hisi_nfc_read_byte; ++ chip->read_word = hisi_nfc_read_word; ++ chip->write_buf = hisi_nfc_write_buf; ++ chip->read_buf = hisi_nfc_read_buf; ++ chip->chip_delay = HINFC504_CHIP_DELAY; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ hisi_nfc_host_init(host); ++ ++ ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); ++ if (ret) { ++ dev_err(dev, "failed to request IRQ\n"); ++ goto err_res; ++ } ++ ++ ret = nand_scan_ident(mtd, max_chips, NULL); ++ if (ret) ++ goto err_res; ++ ++ host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, ++ &host->dma_buffer, GFP_KERNEL); ++ if (!host->buffer) { ++ ret = -ENOMEM; ++ goto err_res; ++ } ++ ++ host->dma_oob = host->dma_buffer + mtd->writesize; ++ memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); ++ ++ flag = hinfc_read(host, HINFC504_CON); ++ flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); ++ switch (mtd->writesize) { ++ case 2048: ++ flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break; ++ /* ++ * TODO: add more pagesize support, ++ * default pagesize has been set in hisi_nfc_host_init ++ */ ++ default: ++ dev_err(dev, "NON-2KB page size nand flash\n"); ++ ret = -EINVAL; ++ goto err_res; ++ } ++ hinfc_write(host, flag, HINFC504_CON); ++ ++ if (chip->ecc.mode == NAND_ECC_HW) ++ hisi_nfc_ecc_probe(host); ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) { ++ dev_err(dev, "nand_scan_tail failed: %d\n", ret); ++ goto err_res; ++ } ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(dev, "Err MTD partition=%d\n", ret); ++ goto err_mtd; ++ } ++ ++ return 0; ++ ++err_mtd: ++ nand_release(mtd); ++err_res: ++ return ret; ++} ++ ++static int hisi_nfc_remove(struct platform_device *pdev) ++{ ++ struct hinfc_host *host = platform_get_drvdata(pdev); ++ struct mtd_info *mtd = nand_to_mtd(&host->chip); ++ ++ nand_release(mtd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int hisi_nfc_suspend(struct device *dev) ++{ ++ struct hinfc_host *host = dev_get_drvdata(dev); ++ unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT; ++ ++ while (time_before(jiffies, timeout)) { ++ if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) && ++ (hinfc_read(host, HINFC504_DMA_CTRL) & ++ HINFC504_DMA_CTRL_DMA_START)) { ++ cond_resched(); ++ return 0; ++ } ++ } ++ ++ dev_err(host->dev, "nand controller suspend timeout.\n"); ++ ++ return -EAGAIN; ++} ++ ++static int hisi_nfc_resume(struct device *dev) ++{ ++ int cs; ++ struct hinfc_host *host = dev_get_drvdata(dev); ++ struct nand_chip *chip = &host->chip; ++ ++ for (cs = 0; cs < chip->numchips; cs++) ++ hisi_nfc_send_cmd_reset(host, cs); ++ hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, ++ HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); ++ ++ return 0; ++} ++#endif ++static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume); ++ ++static const struct of_device_id nfc_id_table[] = { ++ { .compatible = "hisilicon,504-nfc" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, nfc_id_table); ++ ++static struct platform_driver hisi_nfc_driver = { ++ .driver = { ++ .name = "hisi_nand", ++ .of_match_table = nfc_id_table, ++ .pm = &hisi_nfc_pm_ops, ++ }, ++ .probe = hisi_nfc_probe, ++ .remove = hisi_nfc_remove, ++}; ++ ++module_platform_driver(hisi_nfc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Zhou Wang"); ++MODULE_AUTHOR("Zhiyong Cai"); ++MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver"); +diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c +new file mode 100644 +index 00000000..613b00a +--- /dev/null ++++ b/drivers/mtd/nand/raw/jz4740_nand.c +@@ -0,0 +1,536 @@ ++/* ++ * Copyright (C) 2009-2010, Lars-Peter Clausen ++ * JZ4740 SoC NAND controller 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. ++ * ++ * 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., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define JZ_REG_NAND_CTRL 0x50 ++#define JZ_REG_NAND_ECC_CTRL 0x100 ++#define JZ_REG_NAND_DATA 0x104 ++#define JZ_REG_NAND_PAR0 0x108 ++#define JZ_REG_NAND_PAR1 0x10C ++#define JZ_REG_NAND_PAR2 0x110 ++#define JZ_REG_NAND_IRQ_STAT 0x114 ++#define JZ_REG_NAND_IRQ_CTRL 0x118 ++#define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) ++ ++#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) ++#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) ++#define JZ_NAND_ECC_CTRL_RS BIT(2) ++#define JZ_NAND_ECC_CTRL_RESET BIT(1) ++#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) ++ ++#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) ++#define JZ_NAND_STATUS_PAD_FINISH BIT(4) ++#define JZ_NAND_STATUS_DEC_FINISH BIT(3) ++#define JZ_NAND_STATUS_ENC_FINISH BIT(2) ++#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) ++#define JZ_NAND_STATUS_ERROR BIT(0) ++ ++#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) ++#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) ++#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa ++ ++#define JZ_NAND_MEM_CMD_OFFSET 0x08000 ++#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 ++ ++struct jz_nand { ++ struct nand_chip chip; ++ void __iomem *base; ++ struct resource *mem; ++ ++ unsigned char banks[JZ_NAND_NUM_BANKS]; ++ void __iomem *bank_base[JZ_NAND_NUM_BANKS]; ++ struct resource *bank_mem[JZ_NAND_NUM_BANKS]; ++ ++ int selected_bank; ++ ++ struct gpio_desc *busy_gpio; ++ bool is_reading; ++}; ++ ++static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct jz_nand, chip); ++} ++ ++static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ uint32_t ctrl; ++ int banknr; ++ ++ ctrl = readl(nand->base + JZ_REG_NAND_CTRL); ++ ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; ++ ++ if (chipnr == -1) { ++ banknr = -1; ++ } else { ++ banknr = nand->banks[chipnr] - 1; ++ chip->IO_ADDR_R = nand->bank_base[banknr]; ++ chip->IO_ADDR_W = nand->bank_base[banknr]; ++ } ++ writel(ctrl, nand->base + JZ_REG_NAND_CTRL); ++ ++ nand->selected_bank = banknr; ++} ++ ++static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ uint32_t reg; ++ void __iomem *bank_base = nand->bank_base[nand->selected_bank]; ++ ++ BUG_ON(nand->selected_bank < 0); ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); ++ if (ctrl & NAND_ALE) ++ bank_base += JZ_NAND_MEM_ADDR_OFFSET; ++ else if (ctrl & NAND_CLE) ++ bank_base += JZ_NAND_MEM_CMD_OFFSET; ++ chip->IO_ADDR_W = bank_base; ++ ++ reg = readl(nand->base + JZ_REG_NAND_CTRL); ++ if (ctrl & NAND_NCE) ++ reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); ++ else ++ reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); ++ writel(reg, nand->base + JZ_REG_NAND_CTRL); ++ } ++ if (dat != NAND_CMD_NONE) ++ writeb(dat, chip->IO_ADDR_W); ++} ++ ++static int jz_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ return gpiod_get_value_cansleep(nand->busy_gpio); ++} ++ ++static void jz_nand_hwctl(struct mtd_info *mtd, int mode) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ uint32_t reg; ++ ++ writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); ++ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); ++ ++ reg |= JZ_NAND_ECC_CTRL_RESET; ++ reg |= JZ_NAND_ECC_CTRL_ENABLE; ++ reg |= JZ_NAND_ECC_CTRL_RS; ++ ++ switch (mode) { ++ case NAND_ECC_READ: ++ reg &= ~JZ_NAND_ECC_CTRL_ENCODING; ++ nand->is_reading = true; ++ break; ++ case NAND_ECC_WRITE: ++ reg |= JZ_NAND_ECC_CTRL_ENCODING; ++ nand->is_reading = false; ++ break; ++ default: ++ break; ++ } ++ ++ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); ++} ++ ++static int jz_nand_calculate_ecc_rs(struct mtd_info *mtd, const uint8_t *dat, ++ uint8_t *ecc_code) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ uint32_t reg, status; ++ int i; ++ unsigned int timeout = 1000; ++ static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, ++ 0x8b, 0xff, 0xb7, 0x6f}; ++ ++ if (nand->is_reading) ++ return 0; ++ ++ do { ++ status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); ++ } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); ++ ++ if (timeout == 0) ++ return -1; ++ ++ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); ++ reg &= ~JZ_NAND_ECC_CTRL_ENABLE; ++ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); ++ ++ for (i = 0; i < 9; ++i) ++ ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); ++ ++ /* If the written data is completly 0xff, we also want to write 0xff as ++ * ecc, otherwise we will get in trouble when doing subpage writes. */ ++ if (memcmp(ecc_code, empty_block_ecc, 9) == 0) ++ memset(ecc_code, 0xff, 9); ++ ++ return 0; ++} ++ ++static void jz_nand_correct_data(uint8_t *dat, int index, int mask) ++{ ++ int offset = index & 0x7; ++ uint16_t data; ++ ++ index += (index >> 3); ++ ++ data = dat[index]; ++ data |= dat[index+1] << 8; ++ ++ mask ^= (data >> offset) & 0x1ff; ++ data &= ~(0x1ff << offset); ++ data |= (mask << offset); ++ ++ dat[index] = data & 0xff; ++ dat[index+1] = (data >> 8) & 0xff; ++} ++ ++static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, ++ uint8_t *read_ecc, uint8_t *calc_ecc) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ int i, error_count, index; ++ uint32_t reg, status, error; ++ unsigned int timeout = 1000; ++ ++ for (i = 0; i < 9; ++i) ++ writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); ++ ++ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); ++ reg |= JZ_NAND_ECC_CTRL_PAR_READY; ++ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); ++ ++ do { ++ status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); ++ } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); ++ ++ if (timeout == 0) ++ return -ETIMEDOUT; ++ ++ reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); ++ reg &= ~JZ_NAND_ECC_CTRL_ENABLE; ++ writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); ++ ++ if (status & JZ_NAND_STATUS_ERROR) { ++ if (status & JZ_NAND_STATUS_UNCOR_ERROR) ++ return -EBADMSG; ++ ++ error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; ++ ++ for (i = 0; i < error_count; ++i) { ++ error = readl(nand->base + JZ_REG_NAND_ERR(i)); ++ index = ((error >> 16) & 0x1ff) - 1; ++ if (index >= 0 && index < 512) ++ jz_nand_correct_data(dat, index, error & 0x1ff); ++ } ++ ++ return error_count; ++ } ++ ++ return 0; ++} ++ ++static int jz_nand_ioremap_resource(struct platform_device *pdev, ++ const char *name, struct resource **res, void *__iomem *base) ++{ ++ int ret; ++ ++ *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); ++ if (!*res) { ++ dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); ++ ret = -ENXIO; ++ goto err; ++ } ++ ++ *res = request_mem_region((*res)->start, resource_size(*res), ++ pdev->name); ++ if (!*res) { ++ dev_err(&pdev->dev, "Failed to request %s memory region\n", name); ++ ret = -EBUSY; ++ goto err; ++ } ++ ++ *base = ioremap((*res)->start, resource_size(*res)); ++ if (!*base) { ++ dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); ++ ret = -EBUSY; ++ goto err_release_mem; ++ } ++ ++ return 0; ++ ++err_release_mem: ++ release_mem_region((*res)->start, resource_size(*res)); ++err: ++ *res = NULL; ++ *base = NULL; ++ return ret; ++} ++ ++static inline void jz_nand_iounmap_resource(struct resource *res, ++ void __iomem *base) ++{ ++ iounmap(base); ++ release_mem_region(res->start, resource_size(res)); ++} ++ ++static int jz_nand_detect_bank(struct platform_device *pdev, ++ struct jz_nand *nand, unsigned char bank, ++ size_t chipnr, uint8_t *nand_maf_id, ++ uint8_t *nand_dev_id) ++{ ++ int ret; ++ char res_name[6]; ++ uint32_t ctrl; ++ struct nand_chip *chip = &nand->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 id[2]; ++ ++ /* Request I/O resource. */ ++ sprintf(res_name, "bank%d", bank); ++ ret = jz_nand_ioremap_resource(pdev, res_name, ++ &nand->bank_mem[bank - 1], ++ &nand->bank_base[bank - 1]); ++ if (ret) ++ return ret; ++ ++ /* Enable chip in bank. */ ++ ctrl = readl(nand->base + JZ_REG_NAND_CTRL); ++ ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); ++ writel(ctrl, nand->base + JZ_REG_NAND_CTRL); ++ ++ if (chipnr == 0) { ++ /* Detect first chip. */ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ goto notfound_id; ++ ++ /* Retrieve the IDs from the first chip. */ ++ chip->select_chip(mtd, 0); ++ nand_reset_op(chip); ++ nand_readid_op(chip, 0, id, sizeof(id)); ++ *nand_maf_id = id[0]; ++ *nand_dev_id = id[1]; ++ } else { ++ /* Detect additional chip. */ ++ chip->select_chip(mtd, chipnr); ++ nand_reset_op(chip); ++ nand_readid_op(chip, 0, id, sizeof(id)); ++ if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { ++ ret = -ENODEV; ++ goto notfound_id; ++ } ++ ++ /* Update size of the MTD. */ ++ chip->numchips++; ++ mtd->size += chip->chipsize; ++ } ++ ++ dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); ++ return 0; ++ ++notfound_id: ++ dev_info(&pdev->dev, "No chip found on bank %i\n", bank); ++ ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); ++ writel(ctrl, nand->base + JZ_REG_NAND_CTRL); ++ jz_nand_iounmap_resource(nand->bank_mem[bank - 1], ++ nand->bank_base[bank - 1]); ++ return ret; ++} ++ ++static int jz_nand_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct jz_nand *nand; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ size_t chipnr, bank_idx; ++ uint8_t nand_maf_id = 0, nand_dev_id = 0; ++ ++ nand = kzalloc(sizeof(*nand), GFP_KERNEL); ++ if (!nand) ++ return -ENOMEM; ++ ++ ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); ++ if (ret) ++ goto err_free; ++ ++ nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN); ++ if (IS_ERR(nand->busy_gpio)) { ++ ret = PTR_ERR(nand->busy_gpio); ++ dev_err(&pdev->dev, "Failed to request busy gpio %d\n", ++ ret); ++ goto err_iounmap_mmio; ++ } ++ ++ chip = &nand->chip; ++ mtd = nand_to_mtd(chip); ++ mtd->dev.parent = &pdev->dev; ++ mtd->name = "jz4740-nand"; ++ ++ chip->ecc.hwctl = jz_nand_hwctl; ++ chip->ecc.calculate = jz_nand_calculate_ecc_rs; ++ chip->ecc.correct = jz_nand_correct_ecc_rs; ++ chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; ++ chip->ecc.size = 512; ++ chip->ecc.bytes = 9; ++ chip->ecc.strength = 4; ++ chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; ++ ++ chip->chip_delay = 50; ++ chip->cmd_ctrl = jz_nand_cmd_ctrl; ++ chip->select_chip = jz_nand_select_chip; ++ ++ if (nand->busy_gpio) ++ chip->dev_ready = jz_nand_dev_ready; ++ ++ platform_set_drvdata(pdev, nand); ++ ++ /* We are going to autodetect NAND chips in the banks specified in the ++ * platform data. Although nand_scan_ident() can detect multiple chips, ++ * it requires those chips to be numbered consecuitively, which is not ++ * always the case for external memory banks. And a fixed chip-to-bank ++ * mapping is not practical either, since for example Dingoo units ++ * produced at different times have NAND chips in different banks. ++ */ ++ chipnr = 0; ++ for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { ++ unsigned char bank; ++ ++ /* If there is no platform data, look for NAND in bank 1, ++ * which is the most likely bank since it is the only one ++ * that can be booted from. ++ */ ++ bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; ++ if (bank == 0) ++ break; ++ if (bank > JZ_NAND_NUM_BANKS) { ++ dev_warn(&pdev->dev, ++ "Skipping non-existing bank: %d\n", bank); ++ continue; ++ } ++ /* The detection routine will directly or indirectly call ++ * jz_nand_select_chip(), so nand->banks has to contain the ++ * bank we're checking. ++ */ ++ nand->banks[chipnr] = bank; ++ if (jz_nand_detect_bank(pdev, nand, bank, chipnr, ++ &nand_maf_id, &nand_dev_id) == 0) ++ chipnr++; ++ else ++ nand->banks[chipnr] = 0; ++ } ++ if (chipnr == 0) { ++ dev_err(&pdev->dev, "No NAND chips found\n"); ++ goto err_iounmap_mmio; ++ } ++ ++ if (pdata && pdata->ident_callback) { ++ pdata->ident_callback(pdev, mtd, &pdata->partitions, ++ &pdata->num_partitions); ++ } ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to scan NAND\n"); ++ goto err_unclaim_banks; ++ } ++ ++ ret = mtd_device_parse_register(mtd, NULL, NULL, ++ pdata ? pdata->partitions : NULL, ++ pdata ? pdata->num_partitions : 0); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to add mtd device\n"); ++ goto err_nand_release; ++ } ++ ++ dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); ++ ++ return 0; ++ ++err_nand_release: ++ nand_release(mtd); ++err_unclaim_banks: ++ while (chipnr--) { ++ unsigned char bank = nand->banks[chipnr]; ++ jz_nand_iounmap_resource(nand->bank_mem[bank - 1], ++ nand->bank_base[bank - 1]); ++ } ++ writel(0, nand->base + JZ_REG_NAND_CTRL); ++err_iounmap_mmio: ++ jz_nand_iounmap_resource(nand->mem, nand->base); ++err_free: ++ kfree(nand); ++ return ret; ++} ++ ++static int jz_nand_remove(struct platform_device *pdev) ++{ ++ struct jz_nand *nand = platform_get_drvdata(pdev); ++ size_t i; ++ ++ nand_release(nand_to_mtd(&nand->chip)); ++ ++ /* Deassert and disable all chips */ ++ writel(0, nand->base + JZ_REG_NAND_CTRL); ++ ++ for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { ++ unsigned char bank = nand->banks[i]; ++ if (bank != 0) { ++ jz_nand_iounmap_resource(nand->bank_mem[bank - 1], ++ nand->bank_base[bank - 1]); ++ } ++ } ++ ++ jz_nand_iounmap_resource(nand->mem, nand->base); ++ ++ kfree(nand); ++ ++ return 0; ++} ++ ++static struct platform_driver jz_nand_driver = { ++ .probe = jz_nand_probe, ++ .remove = jz_nand_remove, ++ .driver = { ++ .name = "jz4740-nand", ++ }, ++}; ++ ++module_platform_driver(jz_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lars-Peter Clausen "); ++MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); ++MODULE_ALIAS("platform:jz4740-nand"); +diff --git a/drivers/mtd/nand/raw/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c +new file mode 100644 +index 00000000..731c605 +--- /dev/null ++++ b/drivers/mtd/nand/raw/jz4780_bch.c +@@ -0,0 +1,380 @@ ++/* ++ * JZ4780 BCH controller ++ * ++ * Copyright (c) 2015 Imagination Technologies ++ * Author: Alex Smith ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz4780_bch.h" ++ ++#define BCH_BHCR 0x0 ++#define BCH_BHCCR 0x8 ++#define BCH_BHCNT 0xc ++#define BCH_BHDR 0x10 ++#define BCH_BHPAR0 0x14 ++#define BCH_BHERR0 0x84 ++#define BCH_BHINT 0x184 ++#define BCH_BHINTES 0x188 ++#define BCH_BHINTEC 0x18c ++#define BCH_BHINTE 0x190 ++ ++#define BCH_BHCR_BSEL_SHIFT 4 ++#define BCH_BHCR_BSEL_MASK (0x7f << BCH_BHCR_BSEL_SHIFT) ++#define BCH_BHCR_ENCE BIT(2) ++#define BCH_BHCR_INIT BIT(1) ++#define BCH_BHCR_BCHE BIT(0) ++ ++#define BCH_BHCNT_PARITYSIZE_SHIFT 16 ++#define BCH_BHCNT_PARITYSIZE_MASK (0x7f << BCH_BHCNT_PARITYSIZE_SHIFT) ++#define BCH_BHCNT_BLOCKSIZE_SHIFT 0 ++#define BCH_BHCNT_BLOCKSIZE_MASK (0x7ff << BCH_BHCNT_BLOCKSIZE_SHIFT) ++ ++#define BCH_BHERR_MASK_SHIFT 16 ++#define BCH_BHERR_MASK_MASK (0xffff << BCH_BHERR_MASK_SHIFT) ++#define BCH_BHERR_INDEX_SHIFT 0 ++#define BCH_BHERR_INDEX_MASK (0x7ff << BCH_BHERR_INDEX_SHIFT) ++ ++#define BCH_BHINT_ERRC_SHIFT 24 ++#define BCH_BHINT_ERRC_MASK (0x7f << BCH_BHINT_ERRC_SHIFT) ++#define BCH_BHINT_TERRC_SHIFT 16 ++#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT) ++#define BCH_BHINT_DECF BIT(3) ++#define BCH_BHINT_ENCF BIT(2) ++#define BCH_BHINT_UNCOR BIT(1) ++#define BCH_BHINT_ERR BIT(0) ++ ++#define BCH_CLK_RATE (200 * 1000 * 1000) ++ ++/* Timeout for BCH calculation/correction. */ ++#define BCH_TIMEOUT_US 100000 ++ ++struct jz4780_bch { ++ struct device *dev; ++ void __iomem *base; ++ struct clk *clk; ++ struct mutex lock; ++}; ++ ++static void jz4780_bch_init(struct jz4780_bch *bch, ++ struct jz4780_bch_params *params, bool encode) ++{ ++ u32 reg; ++ ++ /* Clear interrupt status. */ ++ writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); ++ ++ /* Set up BCH count register. */ ++ reg = params->size << BCH_BHCNT_BLOCKSIZE_SHIFT; ++ reg |= params->bytes << BCH_BHCNT_PARITYSIZE_SHIFT; ++ writel(reg, bch->base + BCH_BHCNT); ++ ++ /* Initialise and enable BCH. */ ++ reg = BCH_BHCR_BCHE | BCH_BHCR_INIT; ++ reg |= params->strength << BCH_BHCR_BSEL_SHIFT; ++ if (encode) ++ reg |= BCH_BHCR_ENCE; ++ writel(reg, bch->base + BCH_BHCR); ++} ++ ++static void jz4780_bch_disable(struct jz4780_bch *bch) ++{ ++ writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); ++ writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR); ++} ++ ++static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf, ++ size_t size) ++{ ++ size_t size32 = size / sizeof(u32); ++ size_t size8 = size % sizeof(u32); ++ const u32 *src32; ++ const u8 *src8; ++ ++ src32 = (const u32 *)buf; ++ while (size32--) ++ writel(*src32++, bch->base + BCH_BHDR); ++ ++ src8 = (const u8 *)src32; ++ while (size8--) ++ writeb(*src8++, bch->base + BCH_BHDR); ++} ++ ++static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf, ++ size_t size) ++{ ++ size_t size32 = size / sizeof(u32); ++ size_t size8 = size % sizeof(u32); ++ u32 *dest32; ++ u8 *dest8; ++ u32 val, offset = 0; ++ ++ dest32 = (u32 *)buf; ++ while (size32--) { ++ *dest32++ = readl(bch->base + BCH_BHPAR0 + offset); ++ offset += sizeof(u32); ++ } ++ ++ dest8 = (u8 *)dest32; ++ val = readl(bch->base + BCH_BHPAR0 + offset); ++ switch (size8) { ++ case 3: ++ dest8[2] = (val >> 16) & 0xff; ++ case 2: ++ dest8[1] = (val >> 8) & 0xff; ++ case 1: ++ dest8[0] = val & 0xff; ++ break; ++ } ++} ++ ++static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq, ++ u32 *status) ++{ ++ u32 reg; ++ int ret; ++ ++ /* ++ * While we could use interrupts here and sleep until the operation ++ * completes, the controller works fairly quickly (usually a few ++ * microseconds) and so the overhead of sleeping until we get an ++ * interrupt quite noticeably decreases performance. ++ */ ++ ret = readl_poll_timeout(bch->base + BCH_BHINT, reg, ++ (reg & irq) == irq, 0, BCH_TIMEOUT_US); ++ if (ret) ++ return false; ++ ++ if (status) ++ *status = reg; ++ ++ writel(reg, bch->base + BCH_BHINT); ++ return true; ++} ++ ++/** ++ * jz4780_bch_calculate() - calculate ECC for a data buffer ++ * @bch: BCH device. ++ * @params: BCH parameters. ++ * @buf: input buffer with raw data. ++ * @ecc_code: output buffer with ECC. ++ * ++ * Return: 0 on success, -ETIMEDOUT if timed out while waiting for BCH ++ * controller. ++ */ ++int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *params, ++ const u8 *buf, u8 *ecc_code) ++{ ++ int ret = 0; ++ ++ mutex_lock(&bch->lock); ++ jz4780_bch_init(bch, params, true); ++ jz4780_bch_write_data(bch, buf, params->size); ++ ++ if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) { ++ jz4780_bch_read_parity(bch, ecc_code, params->bytes); ++ } else { ++ dev_err(bch->dev, "timed out while calculating ECC\n"); ++ ret = -ETIMEDOUT; ++ } ++ ++ jz4780_bch_disable(bch); ++ mutex_unlock(&bch->lock); ++ return ret; ++} ++EXPORT_SYMBOL(jz4780_bch_calculate); ++ ++/** ++ * jz4780_bch_correct() - detect and correct bit errors ++ * @bch: BCH device. ++ * @params: BCH parameters. ++ * @buf: raw data read from the chip. ++ * @ecc_code: ECC read from the chip. ++ * ++ * Given the raw data and the ECC read from the NAND device, detects and ++ * corrects errors in the data. ++ * ++ * Return: the number of bit errors corrected, -EBADMSG if there are too many ++ * errors to correct or -ETIMEDOUT if we timed out waiting for the controller. ++ */ ++int jz4780_bch_correct(struct jz4780_bch *bch, struct jz4780_bch_params *params, ++ u8 *buf, u8 *ecc_code) ++{ ++ u32 reg, mask, index; ++ int i, ret, count; ++ ++ mutex_lock(&bch->lock); ++ ++ jz4780_bch_init(bch, params, false); ++ jz4780_bch_write_data(bch, buf, params->size); ++ jz4780_bch_write_data(bch, ecc_code, params->bytes); ++ ++ if (!jz4780_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) { ++ dev_err(bch->dev, "timed out while correcting data\n"); ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ if (reg & BCH_BHINT_UNCOR) { ++ dev_warn(bch->dev, "uncorrectable ECC error\n"); ++ ret = -EBADMSG; ++ goto out; ++ } ++ ++ /* Correct any detected errors. */ ++ if (reg & BCH_BHINT_ERR) { ++ count = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT; ++ ret = (reg & BCH_BHINT_TERRC_MASK) >> BCH_BHINT_TERRC_SHIFT; ++ ++ for (i = 0; i < count; i++) { ++ reg = readl(bch->base + BCH_BHERR0 + (i * 4)); ++ mask = (reg & BCH_BHERR_MASK_MASK) >> ++ BCH_BHERR_MASK_SHIFT; ++ index = (reg & BCH_BHERR_INDEX_MASK) >> ++ BCH_BHERR_INDEX_SHIFT; ++ buf[(index * 2) + 0] ^= mask; ++ buf[(index * 2) + 1] ^= mask >> 8; ++ } ++ } else { ++ ret = 0; ++ } ++ ++out: ++ jz4780_bch_disable(bch); ++ mutex_unlock(&bch->lock); ++ return ret; ++} ++EXPORT_SYMBOL(jz4780_bch_correct); ++ ++/** ++ * jz4780_bch_get() - get the BCH controller device ++ * @np: BCH device tree node. ++ * ++ * Gets the BCH controller device from the specified device tree node. The ++ * device must be released with jz4780_bch_release() when it is no longer being ++ * used. ++ * ++ * Return: a pointer to jz4780_bch, errors are encoded into the pointer. ++ * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. ++ */ ++static struct jz4780_bch *jz4780_bch_get(struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct jz4780_bch *bch; ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ get_device(&pdev->dev); ++ ++ bch = platform_get_drvdata(pdev); ++ clk_prepare_enable(bch->clk); ++ ++ return bch; ++} ++ ++/** ++ * of_jz4780_bch_get() - get the BCH controller from a DT node ++ * @of_node: the node that contains a bch-controller property. ++ * ++ * Get the bch-controller property from the given device tree ++ * node and pass it to jz4780_bch_get to do the work. ++ * ++ * Return: a pointer to jz4780_bch, errors are encoded into the pointer. ++ * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. ++ */ ++struct jz4780_bch *of_jz4780_bch_get(struct device_node *of_node) ++{ ++ struct jz4780_bch *bch = NULL; ++ struct device_node *np; ++ ++ np = of_parse_phandle(of_node, "ingenic,bch-controller", 0); ++ ++ if (np) { ++ bch = jz4780_bch_get(np); ++ of_node_put(np); ++ } ++ return bch; ++} ++EXPORT_SYMBOL(of_jz4780_bch_get); ++ ++/** ++ * jz4780_bch_release() - release the BCH controller device ++ * @bch: BCH device. ++ */ ++void jz4780_bch_release(struct jz4780_bch *bch) ++{ ++ clk_disable_unprepare(bch->clk); ++ put_device(bch->dev); ++} ++EXPORT_SYMBOL(jz4780_bch_release); ++ ++static int jz4780_bch_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct jz4780_bch *bch; ++ struct resource *res; ++ ++ bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL); ++ if (!bch) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ bch->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(bch->base)) ++ return PTR_ERR(bch->base); ++ ++ jz4780_bch_disable(bch); ++ ++ bch->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(bch->clk)) { ++ dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(bch->clk)); ++ return PTR_ERR(bch->clk); ++ } ++ ++ clk_set_rate(bch->clk, BCH_CLK_RATE); ++ ++ mutex_init(&bch->lock); ++ ++ bch->dev = dev; ++ platform_set_drvdata(pdev, bch); ++ ++ return 0; ++} ++ ++static const struct of_device_id jz4780_bch_dt_match[] = { ++ { .compatible = "ingenic,jz4780-bch" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match); ++ ++static struct platform_driver jz4780_bch_driver = { ++ .probe = jz4780_bch_probe, ++ .driver = { ++ .name = "jz4780-bch", ++ .of_match_table = of_match_ptr(jz4780_bch_dt_match), ++ }, ++}; ++module_platform_driver(jz4780_bch_driver); ++ ++MODULE_AUTHOR("Alex Smith "); ++MODULE_AUTHOR("Harvey Hunt "); ++MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mtd/nand/raw/jz4780_bch.h b/drivers/mtd/nand/raw/jz4780_bch.h +new file mode 100644 +index 00000000..bf471808 +--- /dev/null ++++ b/drivers/mtd/nand/raw/jz4780_bch.h +@@ -0,0 +1,43 @@ ++/* ++ * JZ4780 BCH controller ++ * ++ * Copyright (c) 2015 Imagination Technologies ++ * Author: Alex Smith ++ * ++ * 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 __DRIVERS_MTD_NAND_JZ4780_BCH_H__ ++#define __DRIVERS_MTD_NAND_JZ4780_BCH_H__ ++ ++#include ++ ++struct device; ++struct device_node; ++struct jz4780_bch; ++ ++/** ++ * struct jz4780_bch_params - BCH parameters ++ * @size: data bytes per ECC step. ++ * @bytes: ECC bytes per step. ++ * @strength: number of correctable bits per ECC step. ++ */ ++struct jz4780_bch_params { ++ int size; ++ int bytes; ++ int strength; ++}; ++ ++int jz4780_bch_calculate(struct jz4780_bch *bch, ++ struct jz4780_bch_params *params, ++ const u8 *buf, u8 *ecc_code); ++int jz4780_bch_correct(struct jz4780_bch *bch, ++ struct jz4780_bch_params *params, u8 *buf, ++ u8 *ecc_code); ++ ++void jz4780_bch_release(struct jz4780_bch *bch); ++struct jz4780_bch *of_jz4780_bch_get(struct device_node *np); ++ ++#endif /* __DRIVERS_MTD_NAND_JZ4780_BCH_H__ */ +diff --git a/drivers/mtd/nand/raw/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c +new file mode 100644 +index 00000000..49841da +--- /dev/null ++++ b/drivers/mtd/nand/raw/jz4780_nand.c +@@ -0,0 +1,417 @@ ++/* ++ * JZ4780 NAND driver ++ * ++ * Copyright (c) 2015 Imagination Technologies ++ * Author: Alex Smith ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "jz4780_bch.h" ++ ++#define DRV_NAME "jz4780-nand" ++ ++#define OFFSET_DATA 0x00000000 ++#define OFFSET_CMD 0x00400000 ++#define OFFSET_ADDR 0x00800000 ++ ++/* Command delay when there is no R/B pin. */ ++#define RB_DELAY_US 100 ++ ++struct jz4780_nand_cs { ++ unsigned int bank; ++ void __iomem *base; ++}; ++ ++struct jz4780_nand_controller { ++ struct device *dev; ++ struct jz4780_bch *bch; ++ struct nand_controller controller; ++ unsigned int num_banks; ++ struct list_head chips; ++ int selected; ++ struct jz4780_nand_cs cs[]; ++}; ++ ++struct jz4780_nand_chip { ++ struct nand_chip chip; ++ struct list_head chip_list; ++ ++ struct gpio_desc *busy_gpio; ++ struct gpio_desc *wp_gpio; ++ unsigned int reading: 1; ++}; ++ ++static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip); ++} ++ ++static inline struct jz4780_nand_controller ++*to_jz4780_nand_controller(struct nand_controller *ctrl) ++{ ++ return container_of(ctrl, struct jz4780_nand_controller, controller); ++} ++ ++static void jz4780_nand_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); ++ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); ++ struct jz4780_nand_cs *cs; ++ ++ /* Ensure the currently selected chip is deasserted. */ ++ if (chipnr == -1 && nfc->selected >= 0) { ++ cs = &nfc->cs[nfc->selected]; ++ jz4780_nemc_assert(nfc->dev, cs->bank, false); ++ } ++ ++ nfc->selected = chipnr; ++} ++ ++static void jz4780_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); ++ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); ++ struct jz4780_nand_cs *cs; ++ ++ if (WARN_ON(nfc->selected < 0)) ++ return; ++ ++ cs = &nfc->cs[nfc->selected]; ++ ++ jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE); ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_ALE) ++ writeb(cmd, cs->base + OFFSET_ADDR); ++ else if (ctrl & NAND_CLE) ++ writeb(cmd, cs->base + OFFSET_CMD); ++} ++ ++static int jz4780_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); ++ ++ return !gpiod_get_value_cansleep(nand->busy_gpio); ++} ++ ++static void jz4780_nand_ecc_hwctl(struct mtd_info *mtd, int mode) ++{ ++ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); ++ ++ nand->reading = (mode == NAND_ECC_READ); ++} ++ ++static int jz4780_nand_ecc_calculate(struct mtd_info *mtd, const u8 *dat, ++ u8 *ecc_code) ++{ ++ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); ++ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); ++ struct jz4780_bch_params params; ++ ++ /* ++ * Don't need to generate the ECC when reading, BCH does it for us as ++ * part of decoding/correction. ++ */ ++ if (nand->reading) ++ return 0; ++ ++ params.size = nand->chip.ecc.size; ++ params.bytes = nand->chip.ecc.bytes; ++ params.strength = nand->chip.ecc.strength; ++ ++ return jz4780_bch_calculate(nfc->bch, ¶ms, dat, ecc_code); ++} ++ ++static int jz4780_nand_ecc_correct(struct mtd_info *mtd, u8 *dat, ++ u8 *read_ecc, u8 *calc_ecc) ++{ ++ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd); ++ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller); ++ struct jz4780_bch_params params; ++ ++ params.size = nand->chip.ecc.size; ++ params.bytes = nand->chip.ecc.bytes; ++ params.strength = nand->chip.ecc.strength; ++ ++ return jz4780_bch_correct(nfc->bch, ¶ms, dat, read_ecc); ++} ++ ++static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *dev) ++{ ++ struct nand_chip *chip = &nand->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller); ++ int eccbytes; ++ ++ chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) * ++ (chip->ecc.strength / 8); ++ ++ switch (chip->ecc.mode) { ++ case NAND_ECC_HW: ++ if (!nfc->bch) { ++ dev_err(dev, "HW BCH selected, but BCH controller not found\n"); ++ return -ENODEV; ++ } ++ ++ chip->ecc.hwctl = jz4780_nand_ecc_hwctl; ++ chip->ecc.calculate = jz4780_nand_ecc_calculate; ++ chip->ecc.correct = jz4780_nand_ecc_correct; ++ /* fall through */ ++ case NAND_ECC_SOFT: ++ dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n", ++ (nfc->bch) ? "hardware BCH" : "software ECC", ++ chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); ++ break; ++ case NAND_ECC_NONE: ++ dev_info(dev, "not using ECC\n"); ++ break; ++ default: ++ dev_err(dev, "ECC mode %d not supported\n", chip->ecc.mode); ++ return -EINVAL; ++ } ++ ++ /* The NAND core will generate the ECC layout for SW ECC */ ++ if (chip->ecc.mode != NAND_ECC_HW) ++ return 0; ++ ++ /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ ++ eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; ++ ++ if (eccbytes > mtd->oobsize - 2) { ++ dev_err(dev, ++ "invalid ECC config: required %d ECC bytes, but only %d are available", ++ eccbytes, mtd->oobsize - 2); ++ return -EINVAL; ++ } ++ ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ++ ++ return 0; ++} ++ ++static int jz4780_nand_init_chip(struct platform_device *pdev, ++ struct jz4780_nand_controller *nfc, ++ struct device_node *np, ++ unsigned int chipnr) ++{ ++ struct device *dev = &pdev->dev; ++ struct jz4780_nand_chip *nand; ++ struct jz4780_nand_cs *cs; ++ struct resource *res; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ const __be32 *reg; ++ int ret = 0; ++ ++ cs = &nfc->cs[chipnr]; ++ ++ reg = of_get_property(np, "reg", NULL); ++ if (!reg) ++ return -EINVAL; ++ ++ cs->bank = be32_to_cpu(*reg); ++ ++ jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); ++ cs->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(cs->base)) ++ return PTR_ERR(cs->base); ++ ++ nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); ++ if (!nand) ++ return -ENOMEM; ++ ++ nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN); ++ ++ if (IS_ERR(nand->busy_gpio)) { ++ ret = PTR_ERR(nand->busy_gpio); ++ dev_err(dev, "failed to request busy GPIO: %d\n", ret); ++ return ret; ++ } else if (nand->busy_gpio) { ++ nand->chip.dev_ready = jz4780_nand_dev_ready; ++ } ++ ++ nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); ++ ++ if (IS_ERR(nand->wp_gpio)) { ++ ret = PTR_ERR(nand->wp_gpio); ++ dev_err(dev, "failed to request WP GPIO: %d\n", ret); ++ return ret; ++ } ++ ++ chip = &nand->chip; ++ mtd = nand_to_mtd(chip); ++ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), ++ cs->bank); ++ if (!mtd->name) ++ return -ENOMEM; ++ mtd->dev.parent = dev; ++ ++ chip->IO_ADDR_R = cs->base + OFFSET_DATA; ++ chip->IO_ADDR_W = cs->base + OFFSET_DATA; ++ chip->chip_delay = RB_DELAY_US; ++ chip->options = NAND_NO_SUBPAGE_WRITE; ++ chip->select_chip = jz4780_nand_select_chip; ++ chip->cmd_ctrl = jz4780_nand_cmd_ctrl; ++ chip->ecc.mode = NAND_ECC_HW; ++ chip->controller = &nfc->controller; ++ nand_set_flash_node(chip, np); ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; ++ ++ ret = jz4780_nand_init_ecc(nand, dev); ++ if (ret) ++ return ret; ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ return ret; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ nand_release(mtd); ++ return ret; ++ } ++ ++ list_add_tail(&nand->chip_list, &nfc->chips); ++ ++ return 0; ++} ++ ++static void jz4780_nand_cleanup_chips(struct jz4780_nand_controller *nfc) ++{ ++ struct jz4780_nand_chip *chip; ++ ++ while (!list_empty(&nfc->chips)) { ++ chip = list_first_entry(&nfc->chips, struct jz4780_nand_chip, chip_list); ++ nand_release(nand_to_mtd(&chip->chip)); ++ list_del(&chip->chip_list); ++ } ++} ++ ++static int jz4780_nand_init_chips(struct jz4780_nand_controller *nfc, ++ struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np; ++ int i = 0; ++ int ret; ++ int num_chips = of_get_child_count(dev->of_node); ++ ++ if (num_chips > nfc->num_banks) { ++ dev_err(dev, "found %d chips but only %d banks\n", num_chips, nfc->num_banks); ++ return -EINVAL; ++ } ++ ++ for_each_child_of_node(dev->of_node, np) { ++ ret = jz4780_nand_init_chip(pdev, nfc, np, i); ++ if (ret) { ++ jz4780_nand_cleanup_chips(nfc); ++ return ret; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++static int jz4780_nand_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ unsigned int num_banks; ++ struct jz4780_nand_controller *nfc; ++ int ret; ++ ++ num_banks = jz4780_nemc_num_banks(dev); ++ if (num_banks == 0) { ++ dev_err(dev, "no banks found\n"); ++ return -ENODEV; ++ } ++ ++ nfc = devm_kzalloc(dev, sizeof(*nfc) + (sizeof(nfc->cs[0]) * num_banks), GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ /* ++ * Check for BCH HW before we call nand_scan_ident, to prevent us from ++ * having to call it again if the BCH driver returns -EPROBE_DEFER. ++ */ ++ nfc->bch = of_jz4780_bch_get(dev->of_node); ++ if (IS_ERR(nfc->bch)) ++ return PTR_ERR(nfc->bch); ++ ++ nfc->dev = dev; ++ nfc->num_banks = num_banks; ++ ++ nand_controller_init(&nfc->controller); ++ INIT_LIST_HEAD(&nfc->chips); ++ ++ ret = jz4780_nand_init_chips(nfc, pdev); ++ if (ret) { ++ if (nfc->bch) ++ jz4780_bch_release(nfc->bch); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, nfc); ++ return 0; ++} ++ ++static int jz4780_nand_remove(struct platform_device *pdev) ++{ ++ struct jz4780_nand_controller *nfc = platform_get_drvdata(pdev); ++ ++ if (nfc->bch) ++ jz4780_bch_release(nfc->bch); ++ ++ jz4780_nand_cleanup_chips(nfc); ++ ++ return 0; ++} ++ ++static const struct of_device_id jz4780_nand_dt_match[] = { ++ { .compatible = "ingenic,jz4780-nand" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, jz4780_nand_dt_match); ++ ++static struct platform_driver jz4780_nand_driver = { ++ .probe = jz4780_nand_probe, ++ .remove = jz4780_nand_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = of_match_ptr(jz4780_nand_dt_match), ++ }, ++}; ++module_platform_driver(jz4780_nand_driver); ++ ++MODULE_AUTHOR("Alex Smith "); ++MODULE_AUTHOR("Harvey Hunt "); ++MODULE_DESCRIPTION("Ingenic JZ4780 NAND driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c +new file mode 100644 +index 00000000..e357948 +--- /dev/null ++++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c +@@ -0,0 +1,909 @@ ++/* ++ * Driver for NAND MLC Controller in LPC32xx ++ * ++ * Author: Roland Stigge ++ * ++ * Copyright © 2011 WORK Microwave GmbH ++ * Copyright © 2011, 2012 Roland Stigge ++ * ++ * 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. ++ * ++ * ++ * NAND Flash Controller Operation: ++ * - Read: Auto Decode ++ * - Write: Auto Encode ++ * - Tested Page Sizes: 2048, 4096 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "lpc32xx_mlc" ++ ++/********************************************************************** ++* MLC NAND controller register offsets ++**********************************************************************/ ++ ++#define MLC_BUFF(x) (x + 0x00000) ++#define MLC_DATA(x) (x + 0x08000) ++#define MLC_CMD(x) (x + 0x10000) ++#define MLC_ADDR(x) (x + 0x10004) ++#define MLC_ECC_ENC_REG(x) (x + 0x10008) ++#define MLC_ECC_DEC_REG(x) (x + 0x1000C) ++#define MLC_ECC_AUTO_ENC_REG(x) (x + 0x10010) ++#define MLC_ECC_AUTO_DEC_REG(x) (x + 0x10014) ++#define MLC_RPR(x) (x + 0x10018) ++#define MLC_WPR(x) (x + 0x1001C) ++#define MLC_RUBP(x) (x + 0x10020) ++#define MLC_ROBP(x) (x + 0x10024) ++#define MLC_SW_WP_ADD_LOW(x) (x + 0x10028) ++#define MLC_SW_WP_ADD_HIG(x) (x + 0x1002C) ++#define MLC_ICR(x) (x + 0x10030) ++#define MLC_TIME_REG(x) (x + 0x10034) ++#define MLC_IRQ_MR(x) (x + 0x10038) ++#define MLC_IRQ_SR(x) (x + 0x1003C) ++#define MLC_LOCK_PR(x) (x + 0x10044) ++#define MLC_ISR(x) (x + 0x10048) ++#define MLC_CEH(x) (x + 0x1004C) ++ ++/********************************************************************** ++* MLC_CMD bit definitions ++**********************************************************************/ ++#define MLCCMD_RESET 0xFF ++ ++/********************************************************************** ++* MLC_ICR bit definitions ++**********************************************************************/ ++#define MLCICR_WPROT (1 << 3) ++#define MLCICR_LARGEBLOCK (1 << 2) ++#define MLCICR_LONGADDR (1 << 1) ++#define MLCICR_16BIT (1 << 0) /* unsupported by LPC32x0! */ ++ ++/********************************************************************** ++* MLC_TIME_REG bit definitions ++**********************************************************************/ ++#define MLCTIMEREG_TCEA_DELAY(n) (((n) & 0x03) << 24) ++#define MLCTIMEREG_BUSY_DELAY(n) (((n) & 0x1F) << 19) ++#define MLCTIMEREG_NAND_TA(n) (((n) & 0x07) << 16) ++#define MLCTIMEREG_RD_HIGH(n) (((n) & 0x0F) << 12) ++#define MLCTIMEREG_RD_LOW(n) (((n) & 0x0F) << 8) ++#define MLCTIMEREG_WR_HIGH(n) (((n) & 0x0F) << 4) ++#define MLCTIMEREG_WR_LOW(n) (((n) & 0x0F) << 0) ++ ++/********************************************************************** ++* MLC_IRQ_MR and MLC_IRQ_SR bit definitions ++**********************************************************************/ ++#define MLCIRQ_NAND_READY (1 << 5) ++#define MLCIRQ_CONTROLLER_READY (1 << 4) ++#define MLCIRQ_DECODE_FAILURE (1 << 3) ++#define MLCIRQ_DECODE_ERROR (1 << 2) ++#define MLCIRQ_ECC_READY (1 << 1) ++#define MLCIRQ_WRPROT_FAULT (1 << 0) ++ ++/********************************************************************** ++* MLC_LOCK_PR bit definitions ++**********************************************************************/ ++#define MLCLOCKPR_MAGIC 0xA25E ++ ++/********************************************************************** ++* MLC_ISR bit definitions ++**********************************************************************/ ++#define MLCISR_DECODER_FAILURE (1 << 6) ++#define MLCISR_ERRORS ((1 << 4) | (1 << 5)) ++#define MLCISR_ERRORS_DETECTED (1 << 3) ++#define MLCISR_ECC_READY (1 << 2) ++#define MLCISR_CONTROLLER_READY (1 << 1) ++#define MLCISR_NAND_READY (1 << 0) ++ ++/********************************************************************** ++* MLC_CEH bit definitions ++**********************************************************************/ ++#define MLCCEH_NORMAL (1 << 0) ++ ++struct lpc32xx_nand_cfg_mlc { ++ uint32_t tcea_delay; ++ uint32_t busy_delay; ++ uint32_t nand_ta; ++ uint32_t rd_high; ++ uint32_t rd_low; ++ uint32_t wr_high; ++ uint32_t wr_low; ++ int wp_gpio; ++ struct mtd_partition *parts; ++ unsigned num_parts; ++}; ++ ++static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ ++ if (section >= nand_chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes; ++ oobregion->length = nand_chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ ++ if (section >= nand_chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = 16 * section; ++ oobregion->length = 16 - nand_chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { ++ .ecc = lpc32xx_ooblayout_ecc, ++ .free = lpc32xx_ooblayout_free, ++}; ++ ++static struct nand_bbt_descr lpc32xx_nand_bbt = { ++ .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | ++ NAND_BBT_WRITE, ++ .pages = { 524224, 0, 0, 0, 0, 0, 0, 0 }, ++}; ++ ++static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { ++ .options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB | ++ NAND_BBT_WRITE, ++ .pages = { 524160, 0, 0, 0, 0, 0, 0, 0 }, ++}; ++ ++struct lpc32xx_nand_host { ++ struct nand_chip nand_chip; ++ struct lpc32xx_mlc_platform_data *pdata; ++ struct clk *clk; ++ void __iomem *io_base; ++ int irq; ++ struct lpc32xx_nand_cfg_mlc *ncfg; ++ struct completion comp_nand; ++ struct completion comp_controller; ++ uint32_t llptr; ++ /* ++ * Physical addresses of ECC buffer, DMA data buffers, OOB data buffer ++ */ ++ dma_addr_t oob_buf_phy; ++ /* ++ * Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer ++ */ ++ uint8_t *oob_buf; ++ /* Physical address of DMA base address */ ++ dma_addr_t io_base_phy; ++ ++ struct completion comp_dma; ++ struct dma_chan *dma_chan; ++ struct dma_slave_config dma_slave_config; ++ struct scatterlist sgl; ++ uint8_t *dma_buf; ++ uint8_t *dummy_buf; ++ int mlcsubpages; /* number of 512bytes-subpages */ ++}; ++ ++/* ++ * Activate/Deactivate DMA Operation: ++ * ++ * Using the PL080 DMA Controller for transferring the 512 byte subpages ++ * instead of doing readl() / writel() in a loop slows it down significantly. ++ * Measurements via getnstimeofday() upon 512 byte subpage reads reveal: ++ * ++ * - readl() of 128 x 32 bits in a loop: ~20us ++ * - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us ++ * - DMA read of 512 bytes (32 bit, no bursts): ~100us ++ * ++ * This applies to the transfer itself. In the DMA case: only the ++ * wait_for_completion() (DMA setup _not_ included). ++ * ++ * Note that the 512 bytes subpage transfer is done directly from/to a ++ * FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a ++ * 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND ++ * controller transferring data between its internal buffer to/from the NAND ++ * chip.) ++ * ++ * Therefore, using the PL080 DMA is disabled by default, for now. ++ * ++ */ ++static int use_dma; ++ ++static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) ++{ ++ uint32_t clkrate, tmp; ++ ++ /* Reset MLC controller */ ++ writel(MLCCMD_RESET, MLC_CMD(host->io_base)); ++ udelay(1000); ++ ++ /* Get base clock for MLC block */ ++ clkrate = clk_get_rate(host->clk); ++ if (clkrate == 0) ++ clkrate = 104000000; ++ ++ /* Unlock MLC_ICR ++ * (among others, will be locked again automatically) */ ++ writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); ++ ++ /* Configure MLC Controller: Large Block, 5 Byte Address */ ++ tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR; ++ writel(tmp, MLC_ICR(host->io_base)); ++ ++ /* Unlock MLC_TIME_REG ++ * (among others, will be locked again automatically) */ ++ writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base)); ++ ++ /* Compute clock setup values, see LPC and NAND manual */ ++ tmp = 0; ++ tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1); ++ tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1); ++ tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1); ++ tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1); ++ tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low); ++ tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1); ++ tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low); ++ writel(tmp, MLC_TIME_REG(host->io_base)); ++ ++ /* Enable IRQ for CONTROLLER_READY and NAND_READY */ ++ writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY, ++ MLC_IRQ_MR(host->io_base)); ++ ++ /* Normal nCE operation: nCE controlled by controller */ ++ writel(MLCCEH_NORMAL, MLC_CEH(host->io_base)); ++} ++ ++/* ++ * Hardware specific access to control lines ++ */ ++static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ if (cmd != NAND_CMD_NONE) { ++ if (ctrl & NAND_CLE) ++ writel(cmd, MLC_CMD(host->io_base)); ++ else ++ writel(cmd, MLC_ADDR(host->io_base)); ++ } ++} ++ ++/* ++ * Read Device Ready (NAND device _and_ controller ready) ++ */ ++static int lpc32xx_nand_device_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ if ((readb(MLC_ISR(host->io_base)) & ++ (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) == ++ (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) ++ return 1; ++ ++ return 0; ++} ++ ++static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host) ++{ ++ uint8_t sr; ++ ++ /* Clear interrupt flag by reading status */ ++ sr = readb(MLC_IRQ_SR(host->io_base)); ++ if (sr & MLCIRQ_NAND_READY) ++ complete(&host->comp_nand); ++ if (sr & MLCIRQ_CONTROLLER_READY) ++ complete(&host->comp_controller); ++ ++ return IRQ_HANDLED; ++} ++ ++static int lpc32xx_waitfunc_nand(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY) ++ goto exit; ++ ++ wait_for_completion(&host->comp_nand); ++ ++ while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) { ++ /* Seems to be delayed sometimes by controller */ ++ dev_dbg(&mtd->dev, "Warning: NAND not ready.\n"); ++ cpu_relax(); ++ } ++ ++exit: ++ return NAND_STATUS_READY; ++} ++ ++static int lpc32xx_waitfunc_controller(struct mtd_info *mtd, ++ struct nand_chip *chip) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY) ++ goto exit; ++ ++ wait_for_completion(&host->comp_controller); ++ ++ while (!(readb(MLC_ISR(host->io_base)) & ++ MLCISR_CONTROLLER_READY)) { ++ dev_dbg(&mtd->dev, "Warning: Controller not ready.\n"); ++ cpu_relax(); ++ } ++ ++exit: ++ return NAND_STATUS_READY; ++} ++ ++static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ lpc32xx_waitfunc_nand(mtd, chip); ++ lpc32xx_waitfunc_controller(mtd, chip); ++ ++ return NAND_STATUS_READY; ++} ++ ++/* ++ * Enable NAND write protect ++ */ ++static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) ++{ ++ if (gpio_is_valid(host->ncfg->wp_gpio)) ++ gpio_set_value(host->ncfg->wp_gpio, 0); ++} ++ ++/* ++ * Disable NAND write protect ++ */ ++static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) ++{ ++ if (gpio_is_valid(host->ncfg->wp_gpio)) ++ gpio_set_value(host->ncfg->wp_gpio, 1); ++} ++ ++static void lpc32xx_dma_complete_func(void *completion) ++{ ++ complete(completion); ++} ++ ++static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, ++ enum dma_transfer_direction dir) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ struct dma_async_tx_descriptor *desc; ++ int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; ++ int res; ++ ++ sg_init_one(&host->sgl, mem, len); ++ ++ res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, ++ DMA_BIDIRECTIONAL); ++ if (res != 1) { ++ dev_err(mtd->dev.parent, "Failed to map sg list\n"); ++ return -ENXIO; ++ } ++ desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, ++ flags); ++ if (!desc) { ++ dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); ++ goto out1; ++ } ++ ++ init_completion(&host->comp_dma); ++ desc->callback = lpc32xx_dma_complete_func; ++ desc->callback_param = &host->comp_dma; ++ ++ dmaengine_submit(desc); ++ dma_async_issue_pending(host->dma_chan); ++ ++ wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); ++ ++ dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, ++ DMA_BIDIRECTIONAL); ++ return 0; ++out1: ++ dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, ++ DMA_BIDIRECTIONAL); ++ return -ENXIO; ++} ++ ++static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ int i, j; ++ uint8_t *oobbuf = chip->oob_poi; ++ uint32_t mlc_isr; ++ int res; ++ uint8_t *dma_buf; ++ bool dma_mapped; ++ ++ if ((void *)buf <= high_memory) { ++ dma_buf = buf; ++ dma_mapped = true; ++ } else { ++ dma_buf = host->dma_buf; ++ dma_mapped = false; ++ } ++ ++ /* Writing Command and Address */ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ /* For all sub-pages */ ++ for (i = 0; i < host->mlcsubpages; i++) { ++ /* Start Auto Decode Command */ ++ writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base)); ++ ++ /* Wait for Controller Ready */ ++ lpc32xx_waitfunc_controller(mtd, chip); ++ ++ /* Check ECC Error status */ ++ mlc_isr = readl(MLC_ISR(host->io_base)); ++ if (mlc_isr & MLCISR_DECODER_FAILURE) { ++ mtd->ecc_stats.failed++; ++ dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__); ++ } else if (mlc_isr & MLCISR_ERRORS_DETECTED) { ++ mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1; ++ } ++ ++ /* Read 512 + 16 Bytes */ ++ if (use_dma) { ++ res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, ++ DMA_DEV_TO_MEM); ++ if (res) ++ return res; ++ } else { ++ for (j = 0; j < (512 >> 2); j++) { ++ *((uint32_t *)(buf)) = ++ readl(MLC_BUFF(host->io_base)); ++ buf += 4; ++ } ++ } ++ for (j = 0; j < (16 >> 2); j++) { ++ *((uint32_t *)(oobbuf)) = ++ readl(MLC_BUFF(host->io_base)); ++ oobbuf += 4; ++ } ++ } ++ ++ if (use_dma && !dma_mapped) ++ memcpy(buf, dma_buf, mtd->writesize); ++ ++ return 0; ++} ++ ++static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ const uint8_t *oobbuf = chip->oob_poi; ++ uint8_t *dma_buf = (uint8_t *)buf; ++ int res; ++ int i, j; ++ ++ if (use_dma && (void *)buf >= high_memory) { ++ dma_buf = host->dma_buf; ++ memcpy(dma_buf, buf, mtd->writesize); ++ } ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ for (i = 0; i < host->mlcsubpages; i++) { ++ /* Start Encode */ ++ writeb(0x00, MLC_ECC_ENC_REG(host->io_base)); ++ ++ /* Write 512 + 6 Bytes to Buffer */ ++ if (use_dma) { ++ res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512, ++ DMA_MEM_TO_DEV); ++ if (res) ++ return res; ++ } else { ++ for (j = 0; j < (512 >> 2); j++) { ++ writel(*((uint32_t *)(buf)), ++ MLC_BUFF(host->io_base)); ++ buf += 4; ++ } ++ } ++ writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base)); ++ oobbuf += 4; ++ writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base)); ++ oobbuf += 12; ++ ++ /* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */ ++ writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base)); ++ ++ /* Wait for Controller Ready */ ++ lpc32xx_waitfunc_controller(mtd, chip); ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ /* Read whole page - necessary with MLC controller! */ ++ lpc32xx_read_page(mtd, chip, host->dummy_buf, 1, page); ++ ++ return 0; ++} ++ ++static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ /* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */ ++ return 0; ++} ++ ++/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */ ++static void lpc32xx_ecc_enable(struct mtd_info *mtd, int mode) ++{ ++ /* Always enabled! */ ++} ++ ++static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); ++ dma_cap_mask_t mask; ++ ++ if (!host->pdata || !host->pdata->dma_filter) { ++ dev_err(mtd->dev.parent, "no DMA platform data\n"); ++ return -ENOENT; ++ } ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, ++ "nand-mlc"); ++ if (!host->dma_chan) { ++ dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); ++ return -EBUSY; ++ } ++ ++ /* ++ * Set direction to a sensible value even if the dmaengine driver ++ * should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x ++ * driver criticizes it as "alien transfer direction". ++ */ ++ host->dma_slave_config.direction = DMA_DEV_TO_MEM; ++ host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ host->dma_slave_config.src_maxburst = 128; ++ host->dma_slave_config.dst_maxburst = 128; ++ /* DMA controller does flow control: */ ++ host->dma_slave_config.device_fc = false; ++ host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy); ++ host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy); ++ if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { ++ dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); ++ goto out1; ++ } ++ ++ return 0; ++out1: ++ dma_release_channel(host->dma_chan); ++ return -ENXIO; ++} ++ ++static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev) ++{ ++ struct lpc32xx_nand_cfg_mlc *ncfg; ++ struct device_node *np = dev->of_node; ++ ++ ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); ++ if (!ncfg) ++ return NULL; ++ ++ of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay); ++ of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay); ++ of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta); ++ of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high); ++ of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low); ++ of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high); ++ of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low); ++ ++ if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta || ++ !ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high || ++ !ncfg->wr_low) { ++ dev_err(dev, "chip parameters not specified correctly\n"); ++ return NULL; ++ } ++ ++ ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); ++ ++ return ncfg; ++} ++ ++/* ++ * Probe for NAND controller ++ */ ++static int lpc32xx_nand_probe(struct platform_device *pdev) ++{ ++ struct lpc32xx_nand_host *host; ++ struct mtd_info *mtd; ++ struct nand_chip *nand_chip; ++ struct resource *rc; ++ int res; ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ host->io_base = devm_ioremap_resource(&pdev->dev, rc); ++ if (IS_ERR(host->io_base)) ++ return PTR_ERR(host->io_base); ++ ++ host->io_base_phy = rc->start; ++ ++ nand_chip = &host->nand_chip; ++ mtd = nand_to_mtd(nand_chip); ++ if (pdev->dev.of_node) ++ host->ncfg = lpc32xx_parse_dt(&pdev->dev); ++ if (!host->ncfg) { ++ dev_err(&pdev->dev, ++ "Missing or bad NAND config from device tree\n"); ++ return -ENOENT; ++ } ++ if (host->ncfg->wp_gpio == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ if (gpio_is_valid(host->ncfg->wp_gpio) && ++ gpio_request(host->ncfg->wp_gpio, "NAND WP")) { ++ dev_err(&pdev->dev, "GPIO not available\n"); ++ return -EBUSY; ++ } ++ lpc32xx_wp_disable(host); ++ ++ host->pdata = dev_get_platdata(&pdev->dev); ++ ++ /* link the private data structures */ ++ nand_set_controller_data(nand_chip, host); ++ nand_set_flash_node(nand_chip, pdev->dev.of_node); ++ mtd->dev.parent = &pdev->dev; ++ ++ /* Get NAND clock */ ++ host->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(host->clk)) { ++ dev_err(&pdev->dev, "Clock initialization failure\n"); ++ res = -ENOENT; ++ goto err_exit1; ++ } ++ res = clk_prepare_enable(host->clk); ++ if (res) ++ goto err_put_clk; ++ ++ nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; ++ nand_chip->dev_ready = lpc32xx_nand_device_ready; ++ nand_chip->chip_delay = 25; /* us */ ++ nand_chip->IO_ADDR_R = MLC_DATA(host->io_base); ++ nand_chip->IO_ADDR_W = MLC_DATA(host->io_base); ++ ++ /* Init NAND controller */ ++ lpc32xx_nand_setup(host); ++ ++ platform_set_drvdata(pdev, host); ++ ++ /* Initialize function pointers */ ++ nand_chip->ecc.hwctl = lpc32xx_ecc_enable; ++ nand_chip->ecc.read_page_raw = lpc32xx_read_page; ++ nand_chip->ecc.read_page = lpc32xx_read_page; ++ nand_chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel; ++ nand_chip->ecc.write_page = lpc32xx_write_page_lowlevel; ++ nand_chip->ecc.write_oob = lpc32xx_write_oob; ++ nand_chip->ecc.read_oob = lpc32xx_read_oob; ++ nand_chip->ecc.strength = 4; ++ nand_chip->ecc.bytes = 10; ++ nand_chip->waitfunc = lpc32xx_waitfunc; ++ ++ nand_chip->options = NAND_NO_SUBPAGE_WRITE; ++ nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; ++ nand_chip->bbt_td = &lpc32xx_nand_bbt; ++ nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror; ++ ++ if (use_dma) { ++ res = lpc32xx_dma_setup(host); ++ if (res) { ++ res = -EIO; ++ goto err_exit2; ++ } ++ } ++ ++ /* ++ * Scan to find existance of the device and ++ * Get the type of NAND device SMALL block or LARGE block ++ */ ++ res = nand_scan_ident(mtd, 1, NULL); ++ if (res) ++ goto err_exit3; ++ ++ host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); ++ if (!host->dma_buf) { ++ res = -ENOMEM; ++ goto err_exit3; ++ } ++ ++ host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); ++ if (!host->dummy_buf) { ++ res = -ENOMEM; ++ goto err_exit3; ++ } ++ ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); ++ host->mlcsubpages = mtd->writesize / 512; ++ ++ /* initially clear interrupt status */ ++ readb(MLC_IRQ_SR(host->io_base)); ++ ++ init_completion(&host->comp_nand); ++ init_completion(&host->comp_controller); ++ ++ host->irq = platform_get_irq(pdev, 0); ++ if (host->irq < 0) { ++ dev_err(&pdev->dev, "failed to get platform irq\n"); ++ res = -EINVAL; ++ goto err_exit3; ++ } ++ ++ if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq, ++ IRQF_TRIGGER_HIGH, DRV_NAME, host)) { ++ dev_err(&pdev->dev, "Error requesting NAND IRQ\n"); ++ res = -ENXIO; ++ goto err_exit3; ++ } ++ ++ /* ++ * Fills out all the uninitialized function pointers with the defaults ++ * And scans for a bad block table if appropriate. ++ */ ++ res = nand_scan_tail(mtd); ++ if (res) ++ goto err_exit4; ++ ++ mtd->name = DRV_NAME; ++ ++ res = mtd_device_register(mtd, host->ncfg->parts, ++ host->ncfg->num_parts); ++ if (!res) ++ return res; ++ ++ nand_release(mtd); ++ ++err_exit4: ++ free_irq(host->irq, host); ++err_exit3: ++ if (use_dma) ++ dma_release_channel(host->dma_chan); ++err_exit2: ++ clk_disable_unprepare(host->clk); ++err_put_clk: ++ clk_put(host->clk); ++err_exit1: ++ lpc32xx_wp_enable(host); ++ gpio_free(host->ncfg->wp_gpio); ++ ++ return res; ++} ++ ++/* ++ * Remove NAND device ++ */ ++static int lpc32xx_nand_remove(struct platform_device *pdev) ++{ ++ struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); ++ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); ++ ++ nand_release(mtd); ++ free_irq(host->irq, host); ++ if (use_dma) ++ dma_release_channel(host->dma_chan); ++ ++ clk_disable_unprepare(host->clk); ++ clk_put(host->clk); ++ ++ lpc32xx_wp_enable(host); ++ gpio_free(host->ncfg->wp_gpio); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int lpc32xx_nand_resume(struct platform_device *pdev) ++{ ++ struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); ++ int ret; ++ ++ /* Re-enable NAND clock */ ++ ret = clk_prepare_enable(host->clk); ++ if (ret) ++ return ret; ++ ++ /* Fresh init of NAND controller */ ++ lpc32xx_nand_setup(host); ++ ++ /* Disable write protect */ ++ lpc32xx_wp_disable(host); ++ ++ return 0; ++} ++ ++static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) ++{ ++ struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); ++ ++ /* Enable write protect for safety */ ++ lpc32xx_wp_enable(host); ++ ++ /* Disable clock */ ++ clk_disable_unprepare(host->clk); ++ return 0; ++} ++ ++#else ++#define lpc32xx_nand_resume NULL ++#define lpc32xx_nand_suspend NULL ++#endif ++ ++static const struct of_device_id lpc32xx_nand_match[] = { ++ { .compatible = "nxp,lpc3220-mlc" }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); ++ ++static struct platform_driver lpc32xx_nand_driver = { ++ .probe = lpc32xx_nand_probe, ++ .remove = lpc32xx_nand_remove, ++ .resume = lpc32xx_nand_resume, ++ .suspend = lpc32xx_nand_suspend, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = lpc32xx_nand_match, ++ }, ++}; ++ ++module_platform_driver(lpc32xx_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Roland Stigge "); ++MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller"); +diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c +new file mode 100644 +index 00000000..5f7cc6d +--- /dev/null ++++ b/drivers/mtd/nand/raw/lpc32xx_slc.c +@@ -0,0 +1,1032 @@ ++/* ++ * NXP LPC32XX NAND SLC driver ++ * ++ * Authors: ++ * Kevin Wells ++ * Roland Stigge ++ * ++ * Copyright © 2011 NXP Semiconductors ++ * Copyright © 2012 Roland Stigge ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define LPC32XX_MODNAME "lpc32xx-nand" ++ ++/********************************************************************** ++* SLC NAND controller register offsets ++**********************************************************************/ ++ ++#define SLC_DATA(x) (x + 0x000) ++#define SLC_ADDR(x) (x + 0x004) ++#define SLC_CMD(x) (x + 0x008) ++#define SLC_STOP(x) (x + 0x00C) ++#define SLC_CTRL(x) (x + 0x010) ++#define SLC_CFG(x) (x + 0x014) ++#define SLC_STAT(x) (x + 0x018) ++#define SLC_INT_STAT(x) (x + 0x01C) ++#define SLC_IEN(x) (x + 0x020) ++#define SLC_ISR(x) (x + 0x024) ++#define SLC_ICR(x) (x + 0x028) ++#define SLC_TAC(x) (x + 0x02C) ++#define SLC_TC(x) (x + 0x030) ++#define SLC_ECC(x) (x + 0x034) ++#define SLC_DMA_DATA(x) (x + 0x038) ++ ++/********************************************************************** ++* slc_ctrl register definitions ++**********************************************************************/ ++#define SLCCTRL_SW_RESET (1 << 2) /* Reset the NAND controller bit */ ++#define SLCCTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ ++#define SLCCTRL_DMA_START (1 << 0) /* Start DMA channel bit */ ++ ++/********************************************************************** ++* slc_cfg register definitions ++**********************************************************************/ ++#define SLCCFG_CE_LOW (1 << 5) /* Force CE low bit */ ++#define SLCCFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ ++#define SLCCFG_ECC_EN (1 << 3) /* ECC enable bit */ ++#define SLCCFG_DMA_BURST (1 << 2) /* DMA burst bit */ ++#define SLCCFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ ++#define SLCCFG_WIDTH (1 << 0) /* External device width, 0=8bit */ ++ ++/********************************************************************** ++* slc_stat register definitions ++**********************************************************************/ ++#define SLCSTAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ ++#define SLCSTAT_SLC_FIFO (1 << 1) /* SLC FIFO has data bit */ ++#define SLCSTAT_NAND_READY (1 << 0) /* NAND device is ready bit */ ++ ++/********************************************************************** ++* slc_int_stat, slc_ien, slc_isr, and slc_icr register definitions ++**********************************************************************/ ++#define SLCSTAT_INT_TC (1 << 1) /* Transfer count bit */ ++#define SLCSTAT_INT_RDY_EN (1 << 0) /* Ready interrupt bit */ ++ ++/********************************************************************** ++* slc_tac register definitions ++**********************************************************************/ ++/* Computation of clock cycles on basis of controller and device clock rates */ ++#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, DIV_ROUND_UP(c, n) - 1, 0xF) << s) ++ ++/* Clock setting for RDY write sample wait time in 2*n clocks */ ++#define SLCTAC_WDR(n) (((n) & 0xF) << 28) ++/* Write pulse width in clock cycles, 1 to 16 clocks */ ++#define SLCTAC_WWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 24)) ++/* Write hold time of control and data signals, 1 to 16 clocks */ ++#define SLCTAC_WHOLD(c, n) (SLCTAC_CLOCKS(c, n, 20)) ++/* Write setup time of control and data signals, 1 to 16 clocks */ ++#define SLCTAC_WSETUP(c, n) (SLCTAC_CLOCKS(c, n, 16)) ++/* Clock setting for RDY read sample wait time in 2*n clocks */ ++#define SLCTAC_RDR(n) (((n) & 0xF) << 12) ++/* Read pulse width in clock cycles, 1 to 16 clocks */ ++#define SLCTAC_RWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 8)) ++/* Read hold time of control and data signals, 1 to 16 clocks */ ++#define SLCTAC_RHOLD(c, n) (SLCTAC_CLOCKS(c, n, 4)) ++/* Read setup time of control and data signals, 1 to 16 clocks */ ++#define SLCTAC_RSETUP(c, n) (SLCTAC_CLOCKS(c, n, 0)) ++ ++/********************************************************************** ++* slc_ecc register definitions ++**********************************************************************/ ++/* ECC line party fetch macro */ ++#define SLCECC_TO_LINEPAR(n) (((n) >> 6) & 0x7FFF) ++#define SLCECC_TO_COLPAR(n) ((n) & 0x3F) ++ ++/* ++ * DMA requires storage space for the DMA local buffer and the hardware ECC ++ * storage area. The DMA local buffer is only used if DMA mapping fails ++ * during runtime. ++ */ ++#define LPC32XX_DMA_DATA_SIZE 4096 ++#define LPC32XX_ECC_SAVE_SIZE ((4096 / 256) * 4) ++ ++/* Number of bytes used for ECC stored in NAND per 256 bytes */ ++#define LPC32XX_SLC_DEV_ECC_BYTES 3 ++ ++/* ++ * If the NAND base clock frequency can't be fetched, this frequency will be ++ * used instead as the base. This rate is used to setup the timing registers ++ * used for NAND accesses. ++ */ ++#define LPC32XX_DEF_BUS_RATE 133250000 ++ ++/* Milliseconds for DMA FIFO timeout (unlikely anyway) */ ++#define LPC32XX_DMA_TIMEOUT 100 ++ ++/* ++ * NAND ECC Layout for small page NAND devices ++ * Note: For large and huge page devices, the default layouts are used ++ */ ++static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = 6; ++ oobregion->offset = 10; ++ ++ return 0; ++} ++ ++static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 1) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->offset = 0; ++ oobregion->length = 4; ++ } else { ++ oobregion->offset = 6; ++ oobregion->length = 4; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { ++ .ecc = lpc32xx_ooblayout_ecc, ++ .free = lpc32xx_ooblayout_free, ++}; ++ ++static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; ++static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++/* ++ * Small page FLASH BBT descriptors, marker at offset 0, version at offset 6 ++ * Note: Large page devices used the default layout ++ */ ++static struct nand_bbt_descr bbt_smallpage_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 0, ++ .len = 4, ++ .veroffs = 6, ++ .maxblocks = 4, ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_smallpage_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 0, ++ .len = 4, ++ .veroffs = 6, ++ .maxblocks = 4, ++ .pattern = mirror_pattern ++}; ++ ++/* ++ * NAND platform configuration structure ++ */ ++struct lpc32xx_nand_cfg_slc { ++ uint32_t wdr_clks; ++ uint32_t wwidth; ++ uint32_t whold; ++ uint32_t wsetup; ++ uint32_t rdr_clks; ++ uint32_t rwidth; ++ uint32_t rhold; ++ uint32_t rsetup; ++ int wp_gpio; ++ struct mtd_partition *parts; ++ unsigned num_parts; ++}; ++ ++struct lpc32xx_nand_host { ++ struct nand_chip nand_chip; ++ struct lpc32xx_slc_platform_data *pdata; ++ struct clk *clk; ++ void __iomem *io_base; ++ struct lpc32xx_nand_cfg_slc *ncfg; ++ ++ struct completion comp; ++ struct dma_chan *dma_chan; ++ uint32_t dma_buf_len; ++ struct dma_slave_config dma_slave_config; ++ struct scatterlist sgl; ++ ++ /* ++ * DMA and CPU addresses of ECC work area and data buffer ++ */ ++ uint32_t *ecc_buf; ++ uint8_t *data_buf; ++ dma_addr_t io_base_dma; ++}; ++ ++static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) ++{ ++ uint32_t clkrate, tmp; ++ ++ /* Reset SLC controller */ ++ writel(SLCCTRL_SW_RESET, SLC_CTRL(host->io_base)); ++ udelay(1000); ++ ++ /* Basic setup */ ++ writel(0, SLC_CFG(host->io_base)); ++ writel(0, SLC_IEN(host->io_base)); ++ writel((SLCSTAT_INT_TC | SLCSTAT_INT_RDY_EN), ++ SLC_ICR(host->io_base)); ++ ++ /* Get base clock for SLC block */ ++ clkrate = clk_get_rate(host->clk); ++ if (clkrate == 0) ++ clkrate = LPC32XX_DEF_BUS_RATE; ++ ++ /* Compute clock setup values */ ++ tmp = SLCTAC_WDR(host->ncfg->wdr_clks) | ++ SLCTAC_WWIDTH(clkrate, host->ncfg->wwidth) | ++ SLCTAC_WHOLD(clkrate, host->ncfg->whold) | ++ SLCTAC_WSETUP(clkrate, host->ncfg->wsetup) | ++ SLCTAC_RDR(host->ncfg->rdr_clks) | ++ SLCTAC_RWIDTH(clkrate, host->ncfg->rwidth) | ++ SLCTAC_RHOLD(clkrate, host->ncfg->rhold) | ++ SLCTAC_RSETUP(clkrate, host->ncfg->rsetup); ++ writel(tmp, SLC_TAC(host->io_base)); ++} ++ ++/* ++ * Hardware specific access to control lines ++ */ ++static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ uint32_t tmp; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ /* Does CE state need to be changed? */ ++ tmp = readl(SLC_CFG(host->io_base)); ++ if (ctrl & NAND_NCE) ++ tmp |= SLCCFG_CE_LOW; ++ else ++ tmp &= ~SLCCFG_CE_LOW; ++ writel(tmp, SLC_CFG(host->io_base)); ++ ++ if (cmd != NAND_CMD_NONE) { ++ if (ctrl & NAND_CLE) ++ writel(cmd, SLC_CMD(host->io_base)); ++ else ++ writel(cmd, SLC_ADDR(host->io_base)); ++ } ++} ++ ++/* ++ * Read the Device Ready pin ++ */ ++static int lpc32xx_nand_device_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ int rdy = 0; ++ ++ if ((readl(SLC_STAT(host->io_base)) & SLCSTAT_NAND_READY) != 0) ++ rdy = 1; ++ ++ return rdy; ++} ++ ++/* ++ * Enable NAND write protect ++ */ ++static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) ++{ ++ if (gpio_is_valid(host->ncfg->wp_gpio)) ++ gpio_set_value(host->ncfg->wp_gpio, 0); ++} ++ ++/* ++ * Disable NAND write protect ++ */ ++static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) ++{ ++ if (gpio_is_valid(host->ncfg->wp_gpio)) ++ gpio_set_value(host->ncfg->wp_gpio, 1); ++} ++ ++/* ++ * Prepares SLC for transfers with H/W ECC enabled ++ */ ++static void lpc32xx_nand_ecc_enable(struct mtd_info *mtd, int mode) ++{ ++ /* Hardware ECC is enabled automatically in hardware as needed */ ++} ++ ++/* ++ * Calculates the ECC for the data ++ */ ++static int lpc32xx_nand_ecc_calculate(struct mtd_info *mtd, ++ const unsigned char *buf, ++ unsigned char *code) ++{ ++ /* ++ * ECC is calculated automatically in hardware during syndrome read ++ * and write operations, so it doesn't need to be calculated here. ++ */ ++ return 0; ++} ++ ++/* ++ * Read a single byte from NAND device ++ */ ++static uint8_t lpc32xx_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ return (uint8_t)readl(SLC_DATA(host->io_base)); ++} ++ ++/* ++ * Simple device read without ECC ++ */ ++static void lpc32xx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ /* Direct device read with no ECC */ ++ while (len-- > 0) ++ *buf++ = (uint8_t)readl(SLC_DATA(host->io_base)); ++} ++ ++/* ++ * Simple device write without ECC ++ */ ++static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ ++ /* Direct device write with no ECC */ ++ while (len-- > 0) ++ writel((uint32_t)*buf++, SLC_DATA(host->io_base)); ++} ++ ++/* ++ * Read the OOB data from the device without ECC using FIFO method ++ */ ++static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); ++} ++ ++/* ++ * Write the OOB data to the device without ECC using FIFO method ++ */ ++static int lpc32xx_nand_write_oob_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, ++ mtd->oobsize); ++} ++ ++/* ++ * Fills in the ECC fields in the OOB buffer with the hardware generated ECC ++ */ ++static void lpc32xx_slc_ecc_copy(uint8_t *spare, const uint32_t *ecc, int count) ++{ ++ int i; ++ ++ for (i = 0; i < (count * 3); i += 3) { ++ uint32_t ce = ecc[i / 3]; ++ ce = ~(ce << 2) & 0xFFFFFF; ++ spare[i + 2] = (uint8_t)(ce & 0xFF); ++ ce >>= 8; ++ spare[i + 1] = (uint8_t)(ce & 0xFF); ++ ce >>= 8; ++ spare[i] = (uint8_t)(ce & 0xFF); ++ } ++} ++ ++static void lpc32xx_dma_complete_func(void *completion) ++{ ++ complete(completion); ++} ++ ++static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, ++ void *mem, int len, enum dma_transfer_direction dir) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ struct dma_async_tx_descriptor *desc; ++ int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; ++ int res; ++ ++ host->dma_slave_config.direction = dir; ++ host->dma_slave_config.src_addr = dma; ++ host->dma_slave_config.dst_addr = dma; ++ host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ host->dma_slave_config.src_maxburst = 4; ++ host->dma_slave_config.dst_maxburst = 4; ++ /* DMA controller does flow control: */ ++ host->dma_slave_config.device_fc = false; ++ if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { ++ dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); ++ return -ENXIO; ++ } ++ ++ sg_init_one(&host->sgl, mem, len); ++ ++ res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, ++ DMA_BIDIRECTIONAL); ++ if (res != 1) { ++ dev_err(mtd->dev.parent, "Failed to map sg list\n"); ++ return -ENXIO; ++ } ++ desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, ++ flags); ++ if (!desc) { ++ dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); ++ goto out1; ++ } ++ ++ init_completion(&host->comp); ++ desc->callback = lpc32xx_dma_complete_func; ++ desc->callback_param = &host->comp; ++ ++ dmaengine_submit(desc); ++ dma_async_issue_pending(host->dma_chan); ++ ++ wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); ++ ++ dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, ++ DMA_BIDIRECTIONAL); ++ ++ return 0; ++out1: ++ dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, ++ DMA_BIDIRECTIONAL); ++ return -ENXIO; ++} ++ ++/* ++ * DMA read/write transfers with ECC support ++ */ ++static int lpc32xx_xfer(struct mtd_info *mtd, uint8_t *buf, int eccsubpages, ++ int read) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ int i, status = 0; ++ unsigned long timeout; ++ int res; ++ enum dma_transfer_direction dir = ++ read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; ++ uint8_t *dma_buf; ++ bool dma_mapped; ++ ++ if ((void *)buf <= high_memory) { ++ dma_buf = buf; ++ dma_mapped = true; ++ } else { ++ dma_buf = host->data_buf; ++ dma_mapped = false; ++ if (!read) ++ memcpy(host->data_buf, buf, mtd->writesize); ++ } ++ ++ if (read) { ++ writel(readl(SLC_CFG(host->io_base)) | ++ SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | ++ SLCCFG_DMA_BURST, SLC_CFG(host->io_base)); ++ } else { ++ writel((readl(SLC_CFG(host->io_base)) | ++ SLCCFG_ECC_EN | SLCCFG_DMA_ECC | SLCCFG_DMA_BURST) & ++ ~SLCCFG_DMA_DIR, ++ SLC_CFG(host->io_base)); ++ } ++ ++ /* Clear initial ECC */ ++ writel(SLCCTRL_ECC_CLEAR, SLC_CTRL(host->io_base)); ++ ++ /* Transfer size is data area only */ ++ writel(mtd->writesize, SLC_TC(host->io_base)); ++ ++ /* Start transfer in the NAND controller */ ++ writel(readl(SLC_CTRL(host->io_base)) | SLCCTRL_DMA_START, ++ SLC_CTRL(host->io_base)); ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ /* Data */ ++ res = lpc32xx_xmit_dma(mtd, SLC_DMA_DATA(host->io_base_dma), ++ dma_buf + i * chip->ecc.size, ++ mtd->writesize / chip->ecc.steps, dir); ++ if (res) ++ return res; ++ ++ /* Always _read_ ECC */ ++ if (i == chip->ecc.steps - 1) ++ break; ++ if (!read) /* ECC availability delayed on write */ ++ udelay(10); ++ res = lpc32xx_xmit_dma(mtd, SLC_ECC(host->io_base_dma), ++ &host->ecc_buf[i], 4, DMA_DEV_TO_MEM); ++ if (res) ++ return res; ++ } ++ ++ /* ++ * According to NXP, the DMA can be finished here, but the NAND ++ * controller may still have buffered data. After porting to using the ++ * dmaengine DMA driver (amba-pl080), the condition (DMA_FIFO empty) ++ * appears to be always true, according to tests. Keeping the check for ++ * safety reasons for now. ++ */ ++ if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) { ++ dev_warn(mtd->dev.parent, "FIFO not empty!\n"); ++ timeout = jiffies + msecs_to_jiffies(LPC32XX_DMA_TIMEOUT); ++ while ((readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) && ++ time_before(jiffies, timeout)) ++ cpu_relax(); ++ if (!time_before(jiffies, timeout)) { ++ dev_err(mtd->dev.parent, "FIFO held data too long\n"); ++ status = -EIO; ++ } ++ } ++ ++ /* Read last calculated ECC value */ ++ if (!read) ++ udelay(10); ++ host->ecc_buf[chip->ecc.steps - 1] = ++ readl(SLC_ECC(host->io_base)); ++ ++ /* Flush DMA */ ++ dmaengine_terminate_all(host->dma_chan); ++ ++ if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO || ++ readl(SLC_TC(host->io_base))) { ++ /* Something is left in the FIFO, something is wrong */ ++ dev_err(mtd->dev.parent, "DMA FIFO failure\n"); ++ status = -EIO; ++ } ++ ++ /* Stop DMA & HW ECC */ ++ writel(readl(SLC_CTRL(host->io_base)) & ~SLCCTRL_DMA_START, ++ SLC_CTRL(host->io_base)); ++ writel(readl(SLC_CFG(host->io_base)) & ++ ~(SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | ++ SLCCFG_DMA_BURST), SLC_CFG(host->io_base)); ++ ++ if (!dma_mapped && read) ++ memcpy(buf, host->data_buf, mtd->writesize); ++ ++ return status; ++} ++ ++/* ++ * Read the data and OOB data from the device, use ECC correction with the ++ * data, disable ECC for the OOB data ++ */ ++static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ struct mtd_oob_region oobregion = { }; ++ int stat, i, status, error; ++ uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; ++ ++ /* Issue read command */ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ /* Read data and oob, calculate ECC */ ++ status = lpc32xx_xfer(mtd, buf, chip->ecc.steps, 1); ++ ++ /* Get OOB data */ ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ /* Convert to stored ECC format */ ++ lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps); ++ ++ /* Pointer to ECC data retrieved from NAND spare area */ ++ error = mtd_ooblayout_ecc(mtd, 0, &oobregion); ++ if (error) ++ return error; ++ ++ oobecc = chip->oob_poi + oobregion.offset; ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ stat = chip->ecc.correct(mtd, buf, oobecc, ++ &tmpecc[i * chip->ecc.bytes]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ ++ buf += chip->ecc.size; ++ oobecc += chip->ecc.bytes; ++ } ++ ++ return status; ++} ++ ++/* ++ * Read the data and OOB data from the device, no ECC correction with the ++ * data or OOB data ++ */ ++static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ uint8_t *buf, int oob_required, ++ int page) ++{ ++ /* Issue read command */ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ /* Raw reads can just use the FIFO interface */ ++ chip->read_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return 0; ++} ++ ++/* ++ * Write the data and OOB data to the device, use ECC with the data, ++ * disable ECC for the OOB data ++ */ ++static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct lpc32xx_nand_host *host = nand_get_controller_data(chip); ++ struct mtd_oob_region oobregion = { }; ++ uint8_t *pb; ++ int error; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ /* Write data, calculate ECC on outbound data */ ++ error = lpc32xx_xfer(mtd, (uint8_t *)buf, chip->ecc.steps, 0); ++ if (error) ++ return error; ++ ++ /* ++ * The calculated ECC needs some manual work done to it before ++ * committing it to NAND. Process the calculated ECC and place ++ * the resultant values directly into the OOB buffer. */ ++ error = mtd_ooblayout_ecc(mtd, 0, &oobregion); ++ if (error) ++ return error; ++ ++ pb = chip->oob_poi + oobregion.offset; ++ lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps); ++ ++ /* Write ECC data to device */ ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/* ++ * Write the data and OOB data to the device, no ECC correction with the ++ * data or OOB data ++ */ ++static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, ++ int oob_required, int page) ++{ ++ /* Raw writes can just use the FIFO interface */ ++ nand_prog_page_begin_op(chip, page, 0, buf, ++ chip->ecc.size * chip->ecc.steps); ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) ++{ ++ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); ++ dma_cap_mask_t mask; ++ ++ if (!host->pdata || !host->pdata->dma_filter) { ++ dev_err(mtd->dev.parent, "no DMA platform data\n"); ++ return -ENOENT; ++ } ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, ++ "nand-slc"); ++ if (!host->dma_chan) { ++ dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) ++{ ++ struct lpc32xx_nand_cfg_slc *ncfg; ++ struct device_node *np = dev->of_node; ++ ++ ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); ++ if (!ncfg) ++ return NULL; ++ ++ of_property_read_u32(np, "nxp,wdr-clks", &ncfg->wdr_clks); ++ of_property_read_u32(np, "nxp,wwidth", &ncfg->wwidth); ++ of_property_read_u32(np, "nxp,whold", &ncfg->whold); ++ of_property_read_u32(np, "nxp,wsetup", &ncfg->wsetup); ++ of_property_read_u32(np, "nxp,rdr-clks", &ncfg->rdr_clks); ++ of_property_read_u32(np, "nxp,rwidth", &ncfg->rwidth); ++ of_property_read_u32(np, "nxp,rhold", &ncfg->rhold); ++ of_property_read_u32(np, "nxp,rsetup", &ncfg->rsetup); ++ ++ if (!ncfg->wdr_clks || !ncfg->wwidth || !ncfg->whold || ++ !ncfg->wsetup || !ncfg->rdr_clks || !ncfg->rwidth || ++ !ncfg->rhold || !ncfg->rsetup) { ++ dev_err(dev, "chip parameters not specified correctly\n"); ++ return NULL; ++ } ++ ++ ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); ++ ++ return ncfg; ++} ++ ++/* ++ * Probe for NAND controller ++ */ ++static int lpc32xx_nand_probe(struct platform_device *pdev) ++{ ++ struct lpc32xx_nand_host *host; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ struct resource *rc; ++ int res; ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ host->io_base = devm_ioremap_resource(&pdev->dev, rc); ++ if (IS_ERR(host->io_base)) ++ return PTR_ERR(host->io_base); ++ ++ host->io_base_dma = rc->start; ++ if (pdev->dev.of_node) ++ host->ncfg = lpc32xx_parse_dt(&pdev->dev); ++ if (!host->ncfg) { ++ dev_err(&pdev->dev, ++ "Missing or bad NAND config from device tree\n"); ++ return -ENOENT; ++ } ++ if (host->ncfg->wp_gpio == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev, ++ host->ncfg->wp_gpio, "NAND WP")) { ++ dev_err(&pdev->dev, "GPIO not available\n"); ++ return -EBUSY; ++ } ++ lpc32xx_wp_disable(host); ++ ++ host->pdata = dev_get_platdata(&pdev->dev); ++ ++ chip = &host->nand_chip; ++ mtd = nand_to_mtd(chip); ++ nand_set_controller_data(chip, host); ++ nand_set_flash_node(chip, pdev->dev.of_node); ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = &pdev->dev; ++ ++ /* Get NAND clock */ ++ host->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(host->clk)) { ++ dev_err(&pdev->dev, "Clock failure\n"); ++ res = -ENOENT; ++ goto err_exit1; ++ } ++ res = clk_prepare_enable(host->clk); ++ if (res) ++ goto err_exit1; ++ ++ /* Set NAND IO addresses and command/ready functions */ ++ chip->IO_ADDR_R = SLC_DATA(host->io_base); ++ chip->IO_ADDR_W = SLC_DATA(host->io_base); ++ chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; ++ chip->dev_ready = lpc32xx_nand_device_ready; ++ chip->chip_delay = 20; /* 20us command delay time */ ++ ++ /* Init NAND controller */ ++ lpc32xx_nand_setup(host); ++ ++ platform_set_drvdata(pdev, host); ++ ++ /* NAND callbacks for LPC32xx SLC hardware */ ++ chip->ecc.mode = NAND_ECC_HW_SYNDROME; ++ chip->read_byte = lpc32xx_nand_read_byte; ++ chip->read_buf = lpc32xx_nand_read_buf; ++ chip->write_buf = lpc32xx_nand_write_buf; ++ chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; ++ chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; ++ chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; ++ chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; ++ chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; ++ chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; ++ chip->ecc.calculate = lpc32xx_nand_ecc_calculate; ++ chip->ecc.correct = nand_correct_data; ++ chip->ecc.strength = 1; ++ chip->ecc.hwctl = lpc32xx_nand_ecc_enable; ++ ++ /* ++ * Allocate a large enough buffer for a single huge page plus ++ * extra space for the spare area and ECC storage area ++ */ ++ host->dma_buf_len = LPC32XX_DMA_DATA_SIZE + LPC32XX_ECC_SAVE_SIZE; ++ host->data_buf = devm_kzalloc(&pdev->dev, host->dma_buf_len, ++ GFP_KERNEL); ++ if (host->data_buf == NULL) { ++ res = -ENOMEM; ++ goto err_exit2; ++ } ++ ++ res = lpc32xx_nand_dma_setup(host); ++ if (res) { ++ res = -EIO; ++ goto err_exit2; ++ } ++ ++ /* Find NAND device */ ++ res = nand_scan_ident(mtd, 1, NULL); ++ if (res) ++ goto err_exit3; ++ ++ /* OOB and ECC CPU and DMA work areas */ ++ host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); ++ ++ /* ++ * Small page FLASH has a unique OOB layout, but large and huge ++ * page FLASH use the standard layout. Small page FLASH uses a ++ * custom BBT marker layout. ++ */ ++ if (mtd->writesize <= 512) ++ mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); ++ ++ /* These sizes remain the same regardless of page size */ ++ chip->ecc.size = 256; ++ chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; ++ chip->ecc.prepad = chip->ecc.postpad = 0; ++ ++ /* ++ * Use a custom BBT marker setup for small page FLASH that ++ * won't interfere with the ECC layout. Large and huge page ++ * FLASH use the standard layout. ++ */ ++ if ((chip->bbt_options & NAND_BBT_USE_FLASH) && ++ mtd->writesize <= 512) { ++ chip->bbt_td = &bbt_smallpage_main_descr; ++ chip->bbt_md = &bbt_smallpage_mirror_descr; ++ } ++ ++ /* ++ * Fills out all the uninitialized function pointers with the defaults ++ */ ++ res = nand_scan_tail(mtd); ++ if (res) ++ goto err_exit3; ++ ++ mtd->name = "nxp_lpc3220_slc"; ++ res = mtd_device_register(mtd, host->ncfg->parts, ++ host->ncfg->num_parts); ++ if (!res) ++ return res; ++ ++ nand_release(mtd); ++ ++err_exit3: ++ dma_release_channel(host->dma_chan); ++err_exit2: ++ clk_disable_unprepare(host->clk); ++err_exit1: ++ lpc32xx_wp_enable(host); ++ ++ return res; ++} ++ ++/* ++ * Remove NAND device. ++ */ ++static int lpc32xx_nand_remove(struct platform_device *pdev) ++{ ++ uint32_t tmp; ++ struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); ++ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); ++ ++ nand_release(mtd); ++ dma_release_channel(host->dma_chan); ++ ++ /* Force CE high */ ++ tmp = readl(SLC_CTRL(host->io_base)); ++ tmp &= ~SLCCFG_CE_LOW; ++ writel(tmp, SLC_CTRL(host->io_base)); ++ ++ clk_disable_unprepare(host->clk); ++ lpc32xx_wp_enable(host); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int lpc32xx_nand_resume(struct platform_device *pdev) ++{ ++ struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); ++ int ret; ++ ++ /* Re-enable NAND clock */ ++ ret = clk_prepare_enable(host->clk); ++ if (ret) ++ return ret; ++ ++ /* Fresh init of NAND controller */ ++ lpc32xx_nand_setup(host); ++ ++ /* Disable write protect */ ++ lpc32xx_wp_disable(host); ++ ++ return 0; ++} ++ ++static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) ++{ ++ uint32_t tmp; ++ struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); ++ ++ /* Force CE high */ ++ tmp = readl(SLC_CTRL(host->io_base)); ++ tmp &= ~SLCCFG_CE_LOW; ++ writel(tmp, SLC_CTRL(host->io_base)); ++ ++ /* Enable write protect for safety */ ++ lpc32xx_wp_enable(host); ++ ++ /* Disable clock */ ++ clk_disable_unprepare(host->clk); ++ ++ return 0; ++} ++ ++#else ++#define lpc32xx_nand_resume NULL ++#define lpc32xx_nand_suspend NULL ++#endif ++ ++static const struct of_device_id lpc32xx_nand_match[] = { ++ { .compatible = "nxp,lpc3220-slc" }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); ++ ++static struct platform_driver lpc32xx_nand_driver = { ++ .probe = lpc32xx_nand_probe, ++ .remove = lpc32xx_nand_remove, ++ .resume = lpc32xx_nand_resume, ++ .suspend = lpc32xx_nand_suspend, ++ .driver = { ++ .name = LPC32XX_MODNAME, ++ .of_match_table = lpc32xx_nand_match, ++ }, ++}; ++ ++module_platform_driver(lpc32xx_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Kevin Wells "); ++MODULE_AUTHOR("Roland Stigge "); ++MODULE_DESCRIPTION("NAND driver for the NXP LPC32XX SLC controller"); +diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c +new file mode 100644 +index 00000000..f059693 +--- /dev/null ++++ b/drivers/mtd/nand/raw/marvell_nand.c +@@ -0,0 +1,3077 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Marvell NAND flash controller driver ++ * ++ * Copyright (C) 2017 Marvell ++ * Author: Miquel RAYNAL ++ * ++ * ++ * This NAND controller driver handles two versions of the hardware, ++ * one is called NFCv1 and is available on PXA SoCs and the other is ++ * called NFCv2 and is available on Armada SoCs. ++ * ++ * The main visible difference is that NFCv1 only has Hamming ECC ++ * capabilities, while NFCv2 also embeds a BCH ECC engine. Also, DMA ++ * is not used with NFCv2. ++ * ++ * The ECC layouts are depicted in details in Marvell AN-379, but here ++ * is a brief description. ++ * ++ * When using Hamming, the data is split in 512B chunks (either 1, 2 ++ * or 4) and each chunk will have its own ECC "digest" of 6B at the ++ * beginning of the OOB area and eventually the remaining free OOB ++ * bytes (also called "spare" bytes in the driver). This engine ++ * corrects up to 1 bit per chunk and detects reliably an error if ++ * there are at most 2 bitflips. Here is the page layout used by the ++ * controller when Hamming is chosen: ++ * ++ * +-------------------------------------------------------------+ ++ * | Data 1 | ... | Data N | ECC 1 | ... | ECCN | Free OOB bytes | ++ * +-------------------------------------------------------------+ ++ * ++ * When using the BCH engine, there are N identical (data + free OOB + ++ * ECC) sections and potentially an extra one to deal with ++ * configurations where the chosen (data + free OOB + ECC) sizes do ++ * not align with the page (data + OOB) size. ECC bytes are always ++ * 30B per ECC chunk. Here is the page layout used by the controller ++ * when BCH is chosen: ++ * ++ * +----------------------------------------- ++ * | Data 1 | Free OOB bytes 1 | ECC 1 | ... ++ * +----------------------------------------- ++ * ++ * ------------------------------------------- ++ * ... | Data N | Free OOB bytes N | ECC N | ++ * ------------------------------------------- ++ * ++ * --------------------------------------------+ ++ * Last Data | Last Free OOB bytes | Last ECC | ++ * --------------------------------------------+ ++ * ++ * In both cases, the layout seen by the user is always: all data ++ * first, then all free OOB bytes and finally all ECC bytes. With BCH, ++ * ECC bytes are 30B long and are padded with 0xFF to align on 32 ++ * bytes. ++ * ++ * The controller has certain limitations that are handled by the ++ * driver: ++ * - It can only read 2k at a time. To overcome this limitation, the ++ * driver issues data cycles on the bus, without issuing new ++ * CMD + ADDR cycles. The Marvell term is "naked" operations. ++ * - The ECC strength in BCH mode cannot be tuned. It is fixed 16 ++ * bits. What can be tuned is the ECC block size as long as it ++ * stays between 512B and 2kiB. It's usually chosen based on the ++ * chip ECC requirements. For instance, using 2kiB ECC chunks ++ * provides 4b/512B correctability. ++ * - The controller will always treat data bytes, free OOB bytes ++ * and ECC bytes in that order, no matter what the real layout is ++ * (which is usually all data then all OOB bytes). The ++ * marvell_nfc_layouts array below contains the currently ++ * supported layouts. ++ * - Because of these weird layouts, the Bad Block Markers can be ++ * located in data section. In this case, the NAND_BBT_NO_OOB_BBM ++ * option must be set to prevent scanning/writing bad block ++ * markers. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* Data FIFO granularity, FIFO reads/writes must be a multiple of this length */ ++#define FIFO_DEPTH 8 ++#define FIFO_REP(x) (x / sizeof(u32)) ++#define BCH_SEQ_READS (32 / FIFO_DEPTH) ++/* NFC does not support transfers of larger chunks at a time */ ++#define MAX_CHUNK_SIZE 2112 ++/* NFCv1 cannot read more that 7 bytes of ID */ ++#define NFCV1_READID_LEN 7 ++/* Polling is done at a pace of POLL_PERIOD us until POLL_TIMEOUT is reached */ ++#define POLL_PERIOD 0 ++#define POLL_TIMEOUT 100000 ++/* Interrupt maximum wait period in ms */ ++#define IRQ_TIMEOUT 1000 ++/* Latency in clock cycles between SoC pins and NFC logic */ ++#define MIN_RD_DEL_CNT 3 ++/* Maximum number of contiguous address cycles */ ++#define MAX_ADDRESS_CYC_NFCV1 5 ++#define MAX_ADDRESS_CYC_NFCV2 7 ++/* System control registers/bits to enable the NAND controller on some SoCs */ ++#define GENCONF_SOC_DEVICE_MUX 0x208 ++#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) ++#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20) ++#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21) ++#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25) ++#define GENCONF_CLK_GATING_CTRL 0x220 ++#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2) ++#define GENCONF_ND_CLK_CTRL 0x700 ++#define GENCONF_ND_CLK_CTRL_EN BIT(0) ++ ++/* NAND controller data flash control register */ ++#define NDCR 0x00 ++#define NDCR_ALL_INT GENMASK(11, 0) ++#define NDCR_CS1_CMDDM BIT(7) ++#define NDCR_CS0_CMDDM BIT(8) ++#define NDCR_RDYM BIT(11) ++#define NDCR_ND_ARB_EN BIT(12) ++#define NDCR_RA_START BIT(15) ++#define NDCR_RD_ID_CNT(x) (min_t(unsigned int, x, 0x7) << 16) ++#define NDCR_PAGE_SZ(x) (x >= 2048 ? BIT(24) : 0) ++#define NDCR_DWIDTH_M BIT(26) ++#define NDCR_DWIDTH_C BIT(27) ++#define NDCR_ND_RUN BIT(28) ++#define NDCR_DMA_EN BIT(29) ++#define NDCR_ECC_EN BIT(30) ++#define NDCR_SPARE_EN BIT(31) ++#define NDCR_GENERIC_FIELDS_MASK (~(NDCR_RA_START | NDCR_PAGE_SZ(2048) | \ ++ NDCR_DWIDTH_M | NDCR_DWIDTH_C)) ++ ++/* NAND interface timing parameter 0 register */ ++#define NDTR0 0x04 ++#define NDTR0_TRP(x) ((min_t(unsigned int, x, 0xF) & 0x7) << 0) ++#define NDTR0_TRH(x) (min_t(unsigned int, x, 0x7) << 3) ++#define NDTR0_ETRP(x) ((min_t(unsigned int, x, 0xF) & 0x8) << 3) ++#define NDTR0_SEL_NRE_EDGE BIT(7) ++#define NDTR0_TWP(x) (min_t(unsigned int, x, 0x7) << 8) ++#define NDTR0_TWH(x) (min_t(unsigned int, x, 0x7) << 11) ++#define NDTR0_TCS(x) (min_t(unsigned int, x, 0x7) << 16) ++#define NDTR0_TCH(x) (min_t(unsigned int, x, 0x7) << 19) ++#define NDTR0_RD_CNT_DEL(x) (min_t(unsigned int, x, 0xF) << 22) ++#define NDTR0_SELCNTR BIT(26) ++#define NDTR0_TADL(x) (min_t(unsigned int, x, 0x1F) << 27) ++ ++/* NAND interface timing parameter 1 register */ ++#define NDTR1 0x0C ++#define NDTR1_TAR(x) (min_t(unsigned int, x, 0xF) << 0) ++#define NDTR1_TWHR(x) (min_t(unsigned int, x, 0xF) << 4) ++#define NDTR1_TRHW(x) (min_t(unsigned int, x / 16, 0x3) << 8) ++#define NDTR1_PRESCALE BIT(14) ++#define NDTR1_WAIT_MODE BIT(15) ++#define NDTR1_TR(x) (min_t(unsigned int, x, 0xFFFF) << 16) ++ ++/* NAND controller status register */ ++#define NDSR 0x14 ++#define NDSR_WRCMDREQ BIT(0) ++#define NDSR_RDDREQ BIT(1) ++#define NDSR_WRDREQ BIT(2) ++#define NDSR_CORERR BIT(3) ++#define NDSR_UNCERR BIT(4) ++#define NDSR_CMDD(cs) BIT(8 - cs) ++#define NDSR_RDY(rb) BIT(11 + rb) ++#define NDSR_ERRCNT(x) ((x >> 16) & 0x1F) ++ ++/* NAND ECC control register */ ++#define NDECCCTRL 0x28 ++#define NDECCCTRL_BCH_EN BIT(0) ++ ++/* NAND controller data buffer register */ ++#define NDDB 0x40 ++ ++/* NAND controller command buffer 0 register */ ++#define NDCB0 0x48 ++#define NDCB0_CMD1(x) ((x & 0xFF) << 0) ++#define NDCB0_CMD2(x) ((x & 0xFF) << 8) ++#define NDCB0_ADDR_CYC(x) ((x & 0x7) << 16) ++#define NDCB0_ADDR_GET_NUM_CYC(x) (((x) >> 16) & 0x7) ++#define NDCB0_DBC BIT(19) ++#define NDCB0_CMD_TYPE(x) ((x & 0x7) << 21) ++#define NDCB0_CSEL BIT(24) ++#define NDCB0_RDY_BYP BIT(27) ++#define NDCB0_LEN_OVRD BIT(28) ++#define NDCB0_CMD_XTYPE(x) ((x & 0x7) << 29) ++ ++/* NAND controller command buffer 1 register */ ++#define NDCB1 0x4C ++#define NDCB1_COLS(x) ((x & 0xFFFF) << 0) ++#define NDCB1_ADDRS_PAGE(x) (x << 16) ++ ++/* NAND controller command buffer 2 register */ ++#define NDCB2 0x50 ++#define NDCB2_ADDR5_PAGE(x) (((x >> 16) & 0xFF) << 0) ++#define NDCB2_ADDR5_CYC(x) ((x & 0xFF) << 0) ++ ++/* NAND controller command buffer 3 register */ ++#define NDCB3 0x54 ++#define NDCB3_ADDR6_CYC(x) ((x & 0xFF) << 16) ++#define NDCB3_ADDR7_CYC(x) ((x & 0xFF) << 24) ++ ++/* NAND controller command buffer 0 register 'type' and 'xtype' fields */ ++#define TYPE_READ 0 ++#define TYPE_WRITE 1 ++#define TYPE_ERASE 2 ++#define TYPE_READ_ID 3 ++#define TYPE_STATUS 4 ++#define TYPE_RESET 5 ++#define TYPE_NAKED_CMD 6 ++#define TYPE_NAKED_ADDR 7 ++#define TYPE_MASK 7 ++#define XTYPE_MONOLITHIC_RW 0 ++#define XTYPE_LAST_NAKED_RW 1 ++#define XTYPE_FINAL_COMMAND 3 ++#define XTYPE_READ 4 ++#define XTYPE_WRITE_DISPATCH 4 ++#define XTYPE_NAKED_RW 5 ++#define XTYPE_COMMAND_DISPATCH 6 ++#define XTYPE_MASK 7 ++ ++/** ++ * Marvell ECC engine works differently than the others, in order to limit the ++ * size of the IP, hardware engineers chose to set a fixed strength at 16 bits ++ * per subpage, and depending on a the desired strength needed by the NAND chip, ++ * a particular layout mixing data/spare/ecc is defined, with a possible last ++ * chunk smaller that the others. ++ * ++ * @writesize: Full page size on which the layout applies ++ * @chunk: Desired ECC chunk size on which the layout applies ++ * @strength: Desired ECC strength (per chunk size bytes) on which the ++ * layout applies ++ * @nchunks: Total number of chunks ++ * @full_chunk_cnt: Number of full-sized chunks, which is the number of ++ * repetitions of the pattern: ++ * (data_bytes + spare_bytes + ecc_bytes). ++ * @data_bytes: Number of data bytes per chunk ++ * @spare_bytes: Number of spare bytes per chunk ++ * @ecc_bytes: Number of ecc bytes per chunk ++ * @last_data_bytes: Number of data bytes in the last chunk ++ * @last_spare_bytes: Number of spare bytes in the last chunk ++ * @last_ecc_bytes: Number of ecc bytes in the last chunk ++ */ ++struct marvell_hw_ecc_layout { ++ /* Constraints */ ++ int writesize; ++ int chunk; ++ int strength; ++ /* Corresponding layout */ ++ int nchunks; ++ int full_chunk_cnt; ++ int data_bytes; ++ int spare_bytes; ++ int ecc_bytes; ++ int last_data_bytes; ++ int last_spare_bytes; ++ int last_ecc_bytes; ++}; ++ ++#define MARVELL_LAYOUT(ws, dc, ds, nc, fcc, db, sb, eb, ldb, lsb, leb) \ ++ { \ ++ .writesize = ws, \ ++ .chunk = dc, \ ++ .strength = ds, \ ++ .nchunks = nc, \ ++ .full_chunk_cnt = fcc, \ ++ .data_bytes = db, \ ++ .spare_bytes = sb, \ ++ .ecc_bytes = eb, \ ++ .last_data_bytes = ldb, \ ++ .last_spare_bytes = lsb, \ ++ .last_ecc_bytes = leb, \ ++ } ++ ++/* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */ ++static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = { ++ MARVELL_LAYOUT( 512, 512, 1, 1, 1, 512, 8, 8, 0, 0, 0), ++ MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0), ++ MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0), ++ MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30), ++ MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30, 1024, 64, 30), ++ MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0), ++ MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30), ++ MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0), ++ MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30), ++}; ++ ++/** ++ * The Nand Flash Controller has up to 4 CE and 2 RB pins. The CE selection ++ * is made by a field in NDCB0 register, and in another field in NDCB2 register. ++ * The datasheet describes the logic with an error: ADDR5 field is once ++ * declared at the beginning of NDCB2, and another time at its end. Because the ++ * ADDR5 field of NDCB2 may be used by other bytes, it would be more logical ++ * to use the last bit of this field instead of the first ones. ++ * ++ * @cs: Wanted CE lane. ++ * @ndcb0_csel: Value of the NDCB0 register with or without the flag ++ * selecting the wanted CE lane. This is set once when ++ * the Device Tree is probed. ++ * @rb: Ready/Busy pin for the flash chip ++ */ ++struct marvell_nand_chip_sel { ++ unsigned int cs; ++ u32 ndcb0_csel; ++ unsigned int rb; ++}; ++ ++/** ++ * NAND chip structure: stores NAND chip device related information ++ * ++ * @chip: Base NAND chip structure ++ * @node: Used to store NAND chips into a list ++ * @layout NAND layout when using hardware ECC ++ * @ndcr: Controller register value for this NAND chip ++ * @ndtr0: Timing registers 0 value for this NAND chip ++ * @ndtr1: Timing registers 1 value for this NAND chip ++ * @selected_die: Current active CS ++ * @nsels: Number of CS lines required by the NAND chip ++ * @sels: Array of CS lines descriptions ++ */ ++struct marvell_nand_chip { ++ struct nand_chip chip; ++ struct list_head node; ++ const struct marvell_hw_ecc_layout *layout; ++ u32 ndcr; ++ u32 ndtr0; ++ u32 ndtr1; ++ int addr_cyc; ++ int selected_die; ++ unsigned int nsels; ++ struct marvell_nand_chip_sel sels[0]; ++}; ++ ++static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip) ++{ ++ return container_of(chip, struct marvell_nand_chip, chip); ++} ++ ++static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip ++ *nand) ++{ ++ return &nand->sels[nand->selected_die]; ++} ++ ++/** ++ * NAND controller capabilities for distinction between compatible strings ++ * ++ * @max_cs_nb: Number of Chip Select lines available ++ * @max_rb_nb: Number of Ready/Busy lines available ++ * @need_system_controller: Indicates if the SoC needs to have access to the ++ * system controller (ie. to enable the NAND controller) ++ * @legacy_of_bindings: Indicates if DT parsing must be done using the old ++ * fashion way ++ * @is_nfcv2: NFCv2 has numerous enhancements compared to NFCv1, ie. ++ * BCH error detection and correction algorithm, ++ * NDCB3 register has been added ++ * @use_dma: Use dma for data transfers ++ */ ++struct marvell_nfc_caps { ++ unsigned int max_cs_nb; ++ unsigned int max_rb_nb; ++ bool need_system_controller; ++ bool legacy_of_bindings; ++ bool is_nfcv2; ++ bool use_dma; ++}; ++ ++/** ++ * NAND controller structure: stores Marvell NAND controller information ++ * ++ * @controller: Base controller structure ++ * @dev: Parent device (used to print error messages) ++ * @regs: NAND controller registers ++ * @core_clk: Core clock ++ * @reg_clk: Regiters clock ++ * @complete: Completion object to wait for NAND controller events ++ * @assigned_cs: Bitmask describing already assigned CS lines ++ * @chips: List containing all the NAND chips attached to ++ * this NAND controller ++ * @caps: NAND controller capabilities for each compatible string ++ * @dma_chan: DMA channel (NFCv1 only) ++ * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only) ++ */ ++struct marvell_nfc { ++ struct nand_controller controller; ++ struct device *dev; ++ void __iomem *regs; ++ struct clk *core_clk; ++ struct clk *reg_clk; ++ struct completion complete; ++ unsigned long assigned_cs; ++ struct list_head chips; ++ struct nand_chip *selected_chip; ++ const struct marvell_nfc_caps *caps; ++ ++ /* DMA (NFCv1 only) */ ++ bool use_dma; ++ struct dma_chan *dma_chan; ++ u8 *dma_buf; ++}; ++ ++static inline struct marvell_nfc *to_marvell_nfc(struct nand_controller *ctrl) ++{ ++ return container_of(ctrl, struct marvell_nfc, controller); ++} ++ ++/** ++ * NAND controller timings expressed in NAND Controller clock cycles ++ * ++ * @tRP: ND_nRE pulse width ++ * @tRH: ND_nRE high duration ++ * @tWP: ND_nWE pulse time ++ * @tWH: ND_nWE high duration ++ * @tCS: Enable signal setup time ++ * @tCH: Enable signal hold time ++ * @tADL: Address to write data delay ++ * @tAR: ND_ALE low to ND_nRE low delay ++ * @tWHR: ND_nWE high to ND_nRE low for status read ++ * @tRHW: ND_nRE high duration, read to write delay ++ * @tR: ND_nWE high to ND_nRE low for read ++ */ ++struct marvell_nfc_timings { ++ /* NDTR0 fields */ ++ unsigned int tRP; ++ unsigned int tRH; ++ unsigned int tWP; ++ unsigned int tWH; ++ unsigned int tCS; ++ unsigned int tCH; ++ unsigned int tADL; ++ /* NDTR1 fields */ ++ unsigned int tAR; ++ unsigned int tWHR; ++ unsigned int tRHW; ++ unsigned int tR; ++}; ++ ++/** ++ * Derives a duration in numbers of clock cycles. ++ * ++ * @ps: Duration in pico-seconds ++ * @period_ns: Clock period in nano-seconds ++ * ++ * Convert the duration in nano-seconds, then divide by the period and ++ * return the number of clock periods. ++ */ ++#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns)) ++#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \ ++ period_ns)) ++ ++/** ++ * NAND driver structure filled during the parsing of the ->exec_op() subop ++ * subset of instructions. ++ * ++ * @ndcb: Array of values written to NDCBx registers ++ * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle ++ * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin ++ * @rdy_delay_ns: Optional delay after waiting for the RB pin ++ * @data_delay_ns: Optional delay after the data xfer ++ * @data_instr_idx: Index of the data instruction in the subop ++ * @data_instr: Pointer to the data instruction in the subop ++ */ ++struct marvell_nfc_op { ++ u32 ndcb[4]; ++ unsigned int cle_ale_delay_ns; ++ unsigned int rdy_timeout_ms; ++ unsigned int rdy_delay_ns; ++ unsigned int data_delay_ns; ++ unsigned int data_instr_idx; ++ const struct nand_op_instr *data_instr; ++}; ++ ++/* ++ * Internal helper to conditionnally apply a delay (from the above structure, ++ * most of the time). ++ */ ++static void cond_delay(unsigned int ns) ++{ ++ if (!ns) ++ return; ++ ++ if (ns < 10000) ++ ndelay(ns); ++ else ++ udelay(DIV_ROUND_UP(ns, 1000)); ++} ++ ++/* ++ * The controller has many flags that could generate interrupts, most of them ++ * are disabled and polling is used. For the very slow signals, using interrupts ++ * may relax the CPU charge. ++ */ ++static void marvell_nfc_disable_int(struct marvell_nfc *nfc, u32 int_mask) ++{ ++ u32 reg; ++ ++ /* Writing 1 disables the interrupt */ ++ reg = readl_relaxed(nfc->regs + NDCR); ++ writel_relaxed(reg | int_mask, nfc->regs + NDCR); ++} ++ ++static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask) ++{ ++ u32 reg; ++ ++ /* Writing 0 enables the interrupt */ ++ reg = readl_relaxed(nfc->regs + NDCR); ++ writel_relaxed(reg & ~int_mask, nfc->regs + NDCR); ++} ++ ++static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask) ++{ ++ writel_relaxed(int_mask, nfc->regs + NDSR); ++} ++ ++static void marvell_nfc_force_byte_access(struct nand_chip *chip, ++ bool force_8bit) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 ndcr; ++ ++ /* ++ * Callers of this function do not verify if the NAND is using a 16-bit ++ * an 8-bit bus for normal operations, so we need to take care of that ++ * here by leaving the configuration unchanged if the NAND does not have ++ * the NAND_BUSWIDTH_16 flag set. ++ */ ++ if (!(chip->options & NAND_BUSWIDTH_16)) ++ return; ++ ++ ndcr = readl_relaxed(nfc->regs + NDCR); ++ ++ if (force_8bit) ++ ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C); ++ else ++ ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; ++ ++ writel_relaxed(ndcr, nfc->regs + NDCR); ++} ++ ++static int marvell_nfc_wait_ndrun(struct nand_chip *chip) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 val; ++ int ret; ++ ++ /* ++ * The command is being processed, wait for the ND_RUN bit to be ++ * cleared by the NFC. If not, we must clear it by hand. ++ */ ++ ret = readl_relaxed_poll_timeout(nfc->regs + NDCR, val, ++ (val & NDCR_ND_RUN) == 0, ++ POLL_PERIOD, POLL_TIMEOUT); ++ if (ret) { ++ dev_err(nfc->dev, "Timeout on NAND controller run mode\n"); ++ writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, ++ nfc->regs + NDCR); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Any time a command has to be sent to the controller, the following sequence ++ * has to be followed: ++ * - call marvell_nfc_prepare_cmd() ++ * -> activate the ND_RUN bit that will kind of 'start a job' ++ * -> wait the signal indicating the NFC is waiting for a command ++ * - send the command (cmd and address cycles) ++ * - enventually send or receive the data ++ * - call marvell_nfc_end_cmd() with the corresponding flag ++ * -> wait the flag to be triggered or cancel the job with a timeout ++ * ++ * The following helpers are here to factorize the code a bit so that ++ * specialized functions responsible for executing the actual NAND ++ * operations do not have to replicate the same code blocks. ++ */ ++static int marvell_nfc_prepare_cmd(struct nand_chip *chip) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 ndcr, val; ++ int ret; ++ ++ /* Poll ND_RUN and clear NDSR before issuing any command */ ++ ret = marvell_nfc_wait_ndrun(chip); ++ if (ret) { ++ dev_err(nfc->dev, "Last operation did not succeed\n"); ++ return ret; ++ } ++ ++ ndcr = readl_relaxed(nfc->regs + NDCR); ++ writel_relaxed(readl(nfc->regs + NDSR), nfc->regs + NDSR); ++ ++ /* Assert ND_RUN bit and wait the NFC to be ready */ ++ writel_relaxed(ndcr | NDCR_ND_RUN, nfc->regs + NDCR); ++ ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, ++ val & NDSR_WRCMDREQ, ++ POLL_PERIOD, POLL_TIMEOUT); ++ if (ret) { ++ dev_err(nfc->dev, "Timeout on WRCMDRE\n"); ++ return -ETIMEDOUT; ++ } ++ ++ /* Command may be written, clear WRCMDREQ status bit */ ++ writel_relaxed(NDSR_WRCMDREQ, nfc->regs + NDSR); ++ ++ return 0; ++} ++ ++static void marvell_nfc_send_cmd(struct nand_chip *chip, ++ struct marvell_nfc_op *nfc_op) ++{ ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ ++ dev_dbg(nfc->dev, "\nNDCR: 0x%08x\n" ++ "NDCB0: 0x%08x\nNDCB1: 0x%08x\nNDCB2: 0x%08x\nNDCB3: 0x%08x\n", ++ (u32)readl_relaxed(nfc->regs + NDCR), nfc_op->ndcb[0], ++ nfc_op->ndcb[1], nfc_op->ndcb[2], nfc_op->ndcb[3]); ++ ++ writel_relaxed(to_nand_sel(marvell_nand)->ndcb0_csel | nfc_op->ndcb[0], ++ nfc->regs + NDCB0); ++ writel_relaxed(nfc_op->ndcb[1], nfc->regs + NDCB0); ++ writel(nfc_op->ndcb[2], nfc->regs + NDCB0); ++ ++ /* ++ * Write NDCB0 four times only if LEN_OVRD is set or if ADDR6 or ADDR7 ++ * fields are used (only available on NFCv2). ++ */ ++ if (nfc_op->ndcb[0] & NDCB0_LEN_OVRD || ++ NDCB0_ADDR_GET_NUM_CYC(nfc_op->ndcb[0]) >= 6) { ++ if (!WARN_ON_ONCE(!nfc->caps->is_nfcv2)) ++ writel(nfc_op->ndcb[3], nfc->regs + NDCB0); ++ } ++} ++ ++static int marvell_nfc_end_cmd(struct nand_chip *chip, int flag, ++ const char *label) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 val; ++ int ret; ++ ++ ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val, ++ val & flag, ++ POLL_PERIOD, POLL_TIMEOUT); ++ ++ if (ret) { ++ dev_err(nfc->dev, "Timeout on %s (NDSR: 0x%08x)\n", ++ label, val); ++ if (nfc->dma_chan) ++ dmaengine_terminate_all(nfc->dma_chan); ++ return ret; ++ } ++ ++ /* ++ * DMA function uses this helper to poll on CMDD bits without wanting ++ * them to be cleared. ++ */ ++ if (nfc->use_dma && (readl_relaxed(nfc->regs + NDCR) & NDCR_DMA_EN)) ++ return 0; ++ ++ writel_relaxed(flag, nfc->regs + NDSR); ++ ++ return 0; ++} ++ ++static int marvell_nfc_wait_cmdd(struct nand_chip *chip) ++{ ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ int cs_flag = NDSR_CMDD(to_nand_sel(marvell_nand)->ndcb0_csel); ++ ++ return marvell_nfc_end_cmd(chip, cs_flag, "CMDD"); ++} ++ ++static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ int ret; ++ ++ /* Timeout is expressed in ms */ ++ if (!timeout_ms) ++ timeout_ms = IRQ_TIMEOUT; ++ ++ init_completion(&nfc->complete); ++ ++ marvell_nfc_enable_int(nfc, NDCR_RDYM); ++ ret = wait_for_completion_timeout(&nfc->complete, ++ msecs_to_jiffies(timeout_ms)); ++ marvell_nfc_disable_int(nfc, NDCR_RDYM); ++ marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1)); ++ if (!ret) { ++ dev_err(nfc->dev, "Timeout waiting for RB signal\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 ndcr_generic; ++ ++ if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die) ++ return; ++ ++ if (die_nr < 0 || die_nr >= marvell_nand->nsels) { ++ nfc->selected_chip = NULL; ++ marvell_nand->selected_die = -1; ++ return; ++ } ++ ++ writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0); ++ writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1); ++ ++ /* ++ * Reset the NDCR register to a clean state for this particular chip, ++ * also clear ND_RUN bit. ++ */ ++ ndcr_generic = readl_relaxed(nfc->regs + NDCR) & ++ NDCR_GENERIC_FIELDS_MASK & ~NDCR_ND_RUN; ++ writel_relaxed(ndcr_generic | marvell_nand->ndcr, nfc->regs + NDCR); ++ ++ /* Also reset the interrupt status register */ ++ marvell_nfc_clear_int(nfc, NDCR_ALL_INT); ++ ++ nfc->selected_chip = chip; ++ marvell_nand->selected_die = die_nr; ++} ++ ++static irqreturn_t marvell_nfc_isr(int irq, void *dev_id) ++{ ++ struct marvell_nfc *nfc = dev_id; ++ u32 st = readl_relaxed(nfc->regs + NDSR); ++ u32 ien = (~readl_relaxed(nfc->regs + NDCR)) & NDCR_ALL_INT; ++ ++ /* ++ * RDY interrupt mask is one bit in NDCR while there are two status ++ * bit in NDSR (RDY[cs0/cs2] and RDY[cs1/cs3]). ++ */ ++ if (st & NDSR_RDY(1)) ++ st |= NDSR_RDY(0); ++ ++ if (!(st & ien)) ++ return IRQ_NONE; ++ ++ marvell_nfc_disable_int(nfc, st & NDCR_ALL_INT); ++ ++ if (st & (NDSR_RDY(0) | NDSR_RDY(1))) ++ complete(&nfc->complete); ++ ++ return IRQ_HANDLED; ++} ++ ++/* HW ECC related functions */ ++static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 ndcr = readl_relaxed(nfc->regs + NDCR); ++ ++ if (!(ndcr & NDCR_ECC_EN)) { ++ writel_relaxed(ndcr | NDCR_ECC_EN, nfc->regs + NDCR); ++ ++ /* ++ * When enabling BCH, set threshold to 0 to always know the ++ * number of corrected bitflips. ++ */ ++ if (chip->ecc.algo == NAND_ECC_BCH) ++ writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL); ++ } ++} ++ ++static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ u32 ndcr = readl_relaxed(nfc->regs + NDCR); ++ ++ if (ndcr & NDCR_ECC_EN) { ++ writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR); ++ if (chip->ecc.algo == NAND_ECC_BCH) ++ writel_relaxed(0, nfc->regs + NDECCCTRL); ++ } ++} ++ ++/* DMA related helpers */ ++static void marvell_nfc_enable_dma(struct marvell_nfc *nfc) ++{ ++ u32 reg; ++ ++ reg = readl_relaxed(nfc->regs + NDCR); ++ writel_relaxed(reg | NDCR_DMA_EN, nfc->regs + NDCR); ++} ++ ++static void marvell_nfc_disable_dma(struct marvell_nfc *nfc) ++{ ++ u32 reg; ++ ++ reg = readl_relaxed(nfc->regs + NDCR); ++ writel_relaxed(reg & ~NDCR_DMA_EN, nfc->regs + NDCR); ++} ++ ++/* Read/write PIO/DMA accessors */ ++static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, ++ enum dma_data_direction direction, ++ unsigned int len) ++{ ++ unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE); ++ struct dma_async_tx_descriptor *tx; ++ struct scatterlist sg; ++ dma_cookie_t cookie; ++ int ret; ++ ++ marvell_nfc_enable_dma(nfc); ++ /* Prepare the DMA transfer */ ++ sg_init_one(&sg, nfc->dma_buf, dma_len); ++ dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction); ++ tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1, ++ direction == DMA_FROM_DEVICE ? ++ DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, ++ DMA_PREP_INTERRUPT); ++ if (!tx) { ++ dev_err(nfc->dev, "Could not prepare DMA S/G list\n"); ++ return -ENXIO; ++ } ++ ++ /* Do the task and wait for it to finish */ ++ cookie = dmaengine_submit(tx); ++ ret = dma_submit_error(cookie); ++ if (ret) ++ return -EIO; ++ ++ dma_async_issue_pending(nfc->dma_chan); ++ ret = marvell_nfc_wait_cmdd(nfc->selected_chip); ++ dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction); ++ marvell_nfc_disable_dma(nfc); ++ if (ret) { ++ dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n", ++ dmaengine_tx_status(nfc->dma_chan, cookie, NULL)); ++ dmaengine_terminate_all(nfc->dma_chan); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int marvell_nfc_xfer_data_in_pio(struct marvell_nfc *nfc, u8 *in, ++ unsigned int len) ++{ ++ unsigned int last_len = len % FIFO_DEPTH; ++ unsigned int last_full_offset = round_down(len, FIFO_DEPTH); ++ int i; ++ ++ for (i = 0; i < last_full_offset; i += FIFO_DEPTH) ++ ioread32_rep(nfc->regs + NDDB, in + i, FIFO_REP(FIFO_DEPTH)); ++ ++ if (last_len) { ++ u8 tmp_buf[FIFO_DEPTH]; ++ ++ ioread32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH)); ++ memcpy(in + last_full_offset, tmp_buf, last_len); ++ } ++ ++ return 0; ++} ++ ++static int marvell_nfc_xfer_data_out_pio(struct marvell_nfc *nfc, const u8 *out, ++ unsigned int len) ++{ ++ unsigned int last_len = len % FIFO_DEPTH; ++ unsigned int last_full_offset = round_down(len, FIFO_DEPTH); ++ int i; ++ ++ for (i = 0; i < last_full_offset; i += FIFO_DEPTH) ++ iowrite32_rep(nfc->regs + NDDB, out + i, FIFO_REP(FIFO_DEPTH)); ++ ++ if (last_len) { ++ u8 tmp_buf[FIFO_DEPTH]; ++ ++ memcpy(tmp_buf, out + last_full_offset, last_len); ++ iowrite32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH)); ++ } ++ ++ return 0; ++} ++ ++static void marvell_nfc_check_empty_chunk(struct nand_chip *chip, ++ u8 *data, int data_len, ++ u8 *spare, int spare_len, ++ u8 *ecc, int ecc_len, ++ unsigned int *max_bitflips) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int bf; ++ ++ /* ++ * Blank pages (all 0xFF) that have not been written may be recognized ++ * as bad if bitflips occur, so whenever an uncorrectable error occurs, ++ * check if the entire page (with ECC bytes) is actually blank or not. ++ */ ++ if (!data) ++ data_len = 0; ++ if (!spare) ++ spare_len = 0; ++ if (!ecc) ++ ecc_len = 0; ++ ++ bf = nand_check_erased_ecc_chunk(data, data_len, ecc, ecc_len, ++ spare, spare_len, chip->ecc.strength); ++ if (bf < 0) { ++ mtd->ecc_stats.failed++; ++ return; ++ } ++ ++ /* Update the stats and max_bitflips */ ++ mtd->ecc_stats.corrected += bf; ++ *max_bitflips = max_t(unsigned int, *max_bitflips, bf); ++} ++ ++/* ++ * Check a chunk is correct or not according to hardware ECC engine. ++ * mtd->ecc_stats.corrected is updated, as well as max_bitflips, however ++ * mtd->ecc_stats.failure is not, the function will instead return a non-zero ++ * value indicating that a check on the emptyness of the subpage must be ++ * performed before declaring the subpage corrupted. ++ */ ++static int marvell_nfc_hw_ecc_correct(struct nand_chip *chip, ++ unsigned int *max_bitflips) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ int bf = 0; ++ u32 ndsr; ++ ++ ndsr = readl_relaxed(nfc->regs + NDSR); ++ ++ /* Check uncorrectable error flag */ ++ if (ndsr & NDSR_UNCERR) { ++ writel_relaxed(ndsr, nfc->regs + NDSR); ++ ++ /* ++ * Do not increment ->ecc_stats.failed now, instead, return a ++ * non-zero value to indicate that this chunk was apparently ++ * bad, and it should be check to see if it empty or not. If ++ * the chunk (with ECC bytes) is not declared empty, the calling ++ * function must increment the failure count. ++ */ ++ return -EBADMSG; ++ } ++ ++ /* Check correctable error flag */ ++ if (ndsr & NDSR_CORERR) { ++ writel_relaxed(ndsr, nfc->regs + NDSR); ++ ++ if (chip->ecc.algo == NAND_ECC_BCH) ++ bf = NDSR_ERRCNT(ndsr); ++ else ++ bf = 1; ++ } ++ ++ /* Update the stats and max_bitflips */ ++ mtd->ecc_stats.corrected += bf; ++ *max_bitflips = max_t(unsigned int, *max_bitflips, bf); ++ ++ return 0; ++} ++ ++/* Hamming read helpers */ ++static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip, ++ u8 *data_buf, u8 *oob_buf, ++ bool raw, int page) ++{ ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ struct marvell_nfc_op nfc_op = { ++ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) | ++ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | ++ NDCB0_DBC | ++ NDCB0_CMD1(NAND_CMD_READ0) | ++ NDCB0_CMD2(NAND_CMD_READSTART), ++ .ndcb[1] = NDCB1_ADDRS_PAGE(page), ++ .ndcb[2] = NDCB2_ADDR5_PAGE(page), ++ }; ++ unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0); ++ int ret; ++ ++ /* NFCv2 needs more information about the operation being executed */ ++ if (nfc->caps->is_nfcv2) ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, ++ "RDDREQ while draining FIFO (data/oob)"); ++ if (ret) ++ return ret; ++ ++ /* ++ * Read the page then the OOB area. Unlike what is shown in current ++ * documentation, spare bytes are protected by the ECC engine, and must ++ * be at the beginning of the OOB area or running this driver on legacy ++ * systems will prevent the discovery of the BBM/BBT. ++ */ ++ if (nfc->use_dma) { ++ marvell_nfc_xfer_data_dma(nfc, DMA_FROM_DEVICE, ++ lt->data_bytes + oob_bytes); ++ memcpy(data_buf, nfc->dma_buf, lt->data_bytes); ++ memcpy(oob_buf, nfc->dma_buf + lt->data_bytes, oob_bytes); ++ } else { ++ marvell_nfc_xfer_data_in_pio(nfc, data_buf, lt->data_bytes); ++ marvell_nfc_xfer_data_in_pio(nfc, oob_buf, oob_bytes); ++ } ++ ++ ret = marvell_nfc_wait_cmdd(chip); ++ ++ return ret; ++} ++ ++static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, ++ true, page); ++} ++ ++static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u8 *buf, int oob_required, ++ int page) ++{ ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ unsigned int full_sz = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; ++ int max_bitflips = 0, ret; ++ u8 *raw_buf; ++ ++ marvell_nfc_enable_hw_ecc(chip); ++ marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false, ++ page); ++ ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips); ++ marvell_nfc_disable_hw_ecc(chip); ++ ++ if (!ret) ++ return max_bitflips; ++ ++ /* ++ * When ECC failures are detected, check if the full page has been ++ * written or not. Ignore the failure if it is actually empty. ++ */ ++ raw_buf = kmalloc(full_sz, GFP_KERNEL); ++ if (!raw_buf) ++ return -ENOMEM; ++ ++ marvell_nfc_hw_ecc_hmg_do_read_page(chip, raw_buf, raw_buf + ++ lt->data_bytes, true, page); ++ marvell_nfc_check_empty_chunk(chip, raw_buf, full_sz, NULL, 0, NULL, 0, ++ &max_bitflips); ++ kfree(raw_buf); ++ ++ return max_bitflips; ++} ++ ++/* ++ * Spare area in Hamming layouts is not protected by the ECC engine (even if ++ * it appears before the ECC bytes when reading), the ->read_oob_raw() function ++ * also stands for ->read_oob(). ++ */ ++static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ ++ return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf, ++ chip->oob_poi, true, page); ++} ++ ++/* Hamming write helpers */ ++static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip, ++ const u8 *data_buf, ++ const u8 *oob_buf, bool raw, ++ int page) ++{ ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ struct marvell_nfc_op nfc_op = { ++ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | ++ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | ++ NDCB0_CMD1(NAND_CMD_SEQIN) | ++ NDCB0_CMD2(NAND_CMD_PAGEPROG) | ++ NDCB0_DBC, ++ .ndcb[1] = NDCB1_ADDRS_PAGE(page), ++ .ndcb[2] = NDCB2_ADDR5_PAGE(page), ++ }; ++ unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0); ++ int ret; ++ ++ /* NFCv2 needs more information about the operation being executed */ ++ if (nfc->caps->is_nfcv2) ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ, ++ "WRDREQ while loading FIFO (data)"); ++ if (ret) ++ return ret; ++ ++ /* Write the page then the OOB area */ ++ if (nfc->use_dma) { ++ memcpy(nfc->dma_buf, data_buf, lt->data_bytes); ++ memcpy(nfc->dma_buf + lt->data_bytes, oob_buf, oob_bytes); ++ marvell_nfc_xfer_data_dma(nfc, DMA_TO_DEVICE, lt->data_bytes + ++ lt->ecc_bytes + lt->spare_bytes); ++ } else { ++ marvell_nfc_xfer_data_out_pio(nfc, data_buf, lt->data_bytes); ++ marvell_nfc_xfer_data_out_pio(nfc, oob_buf, oob_bytes); ++ } ++ ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ ret = marvell_nfc_wait_op(chip, ++ PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max)); ++ return ret; ++} ++ ++static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, int page) ++{ ++ return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, ++ true, page); ++} ++ ++static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, int page) ++{ ++ int ret; ++ ++ marvell_nfc_enable_hw_ecc(chip); ++ ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi, ++ false, page); ++ marvell_nfc_disable_hw_ecc(chip); ++ ++ return ret; ++} ++ ++/* ++ * Spare area in Hamming layouts is not protected by the ECC engine (even if ++ * it appears before the ECC bytes when reading), the ->write_oob_raw() function ++ * also stands for ->write_oob(). ++ */ ++static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ int page) ++{ ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ ++ memset(chip->data_buf, 0xFF, mtd->writesize); ++ ++ return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf, ++ chip->oob_poi, true, page); ++} ++ ++/* BCH read helpers */ ++static int marvell_nfc_hw_ecc_bch_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ u8 *oob = chip->oob_poi; ++ int chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; ++ int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) + ++ lt->last_spare_bytes; ++ int data_len = lt->data_bytes; ++ int spare_len = lt->spare_bytes; ++ int ecc_len = lt->ecc_bytes; ++ int chunk; ++ ++ if (oob_required) ++ memset(chip->oob_poi, 0xFF, mtd->oobsize); ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ for (chunk = 0; chunk < lt->nchunks; chunk++) { ++ /* Update last chunk length */ ++ if (chunk >= lt->full_chunk_cnt) { ++ data_len = lt->last_data_bytes; ++ spare_len = lt->last_spare_bytes; ++ ecc_len = lt->last_ecc_bytes; ++ } ++ ++ /* Read data bytes*/ ++ nand_change_read_column_op(chip, chunk * chunk_size, ++ buf + (lt->data_bytes * chunk), ++ data_len, false); ++ ++ /* Read spare bytes */ ++ nand_read_data_op(chip, oob + (lt->spare_bytes * chunk), ++ spare_len, false); ++ ++ /* Read ECC bytes */ ++ nand_read_data_op(chip, oob + ecc_offset + ++ (ALIGN(lt->ecc_bytes, 32) * chunk), ++ ecc_len, false); ++ } ++ ++ return 0; ++} ++ ++static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk, ++ u8 *data, unsigned int data_len, ++ u8 *spare, unsigned int spare_len, ++ int page) ++{ ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ int i, ret; ++ struct marvell_nfc_op nfc_op = { ++ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) | ++ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | ++ NDCB0_LEN_OVRD, ++ .ndcb[1] = NDCB1_ADDRS_PAGE(page), ++ .ndcb[2] = NDCB2_ADDR5_PAGE(page), ++ .ndcb[3] = data_len + spare_len, ++ }; ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return; ++ ++ if (chunk == 0) ++ nfc_op.ndcb[0] |= NDCB0_DBC | ++ NDCB0_CMD1(NAND_CMD_READ0) | ++ NDCB0_CMD2(NAND_CMD_READSTART); ++ ++ /* ++ * Trigger the monolithic read on the first chunk, then naked read on ++ * intermediate chunks and finally a last naked read on the last chunk. ++ */ ++ if (chunk == 0) ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW); ++ else if (chunk < lt->nchunks - 1) ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW); ++ else ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ++ /* ++ * According to the datasheet, when reading from NDDB ++ * with BCH enabled, after each 32 bytes reads, we ++ * have to make sure that the NDSR.RDDREQ bit is set. ++ * ++ * Drain the FIFO, 8 32-bit reads at a time, and skip ++ * the polling on the last read. ++ * ++ * Length is a multiple of 32 bytes, hence it is a multiple of 8 too. ++ */ ++ for (i = 0; i < data_len; i += FIFO_DEPTH * BCH_SEQ_READS) { ++ marvell_nfc_end_cmd(chip, NDSR_RDDREQ, ++ "RDDREQ while draining FIFO (data)"); ++ marvell_nfc_xfer_data_in_pio(nfc, data, ++ FIFO_DEPTH * BCH_SEQ_READS); ++ data += FIFO_DEPTH * BCH_SEQ_READS; ++ } ++ ++ for (i = 0; i < spare_len; i += FIFO_DEPTH * BCH_SEQ_READS) { ++ marvell_nfc_end_cmd(chip, NDSR_RDDREQ, ++ "RDDREQ while draining FIFO (OOB)"); ++ marvell_nfc_xfer_data_in_pio(nfc, spare, ++ FIFO_DEPTH * BCH_SEQ_READS); ++ spare += FIFO_DEPTH * BCH_SEQ_READS; ++ } ++} ++ ++static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u8 *buf, int oob_required, ++ int page) ++{ ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ int data_len = lt->data_bytes, spare_len = lt->spare_bytes; ++ u8 *data = buf, *spare = chip->oob_poi; ++ int max_bitflips = 0; ++ u32 failure_mask = 0; ++ int chunk, ret; ++ ++ /* ++ * With BCH, OOB is not fully used (and thus not read entirely), not ++ * expected bytes could show up at the end of the OOB buffer if not ++ * explicitly erased. ++ */ ++ if (oob_required) ++ memset(chip->oob_poi, 0xFF, mtd->oobsize); ++ ++ marvell_nfc_enable_hw_ecc(chip); ++ ++ for (chunk = 0; chunk < lt->nchunks; chunk++) { ++ /* Update length for the last chunk */ ++ if (chunk >= lt->full_chunk_cnt) { ++ data_len = lt->last_data_bytes; ++ spare_len = lt->last_spare_bytes; ++ } ++ ++ /* Read the chunk and detect number of bitflips */ ++ marvell_nfc_hw_ecc_bch_read_chunk(chip, chunk, data, data_len, ++ spare, spare_len, page); ++ ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips); ++ if (ret) ++ failure_mask |= BIT(chunk); ++ ++ data += data_len; ++ spare += spare_len; ++ } ++ ++ marvell_nfc_disable_hw_ecc(chip); ++ ++ if (!failure_mask) ++ return max_bitflips; ++ ++ /* ++ * Please note that dumping the ECC bytes during a normal read with OOB ++ * area would add a significant overhead as ECC bytes are "consumed" by ++ * the controller in normal mode and must be re-read in raw mode. To ++ * avoid dropping the performances, we prefer not to include them. The ++ * user should re-read the page in raw mode if ECC bytes are required. ++ */ ++ ++ /* ++ * In case there is any subpage read error reported by ->correct(), we ++ * usually re-read only ECC bytes in raw mode and check if the whole ++ * page is empty. In this case, it is normal that the ECC check failed ++ * and we just ignore the error. ++ * ++ * However, it has been empirically observed that for some layouts (e.g ++ * 2k page, 8b strength per 512B chunk), the controller tries to correct ++ * bits and may create itself bitflips in the erased area. To overcome ++ * this strange behavior, the whole page is re-read in raw mode, not ++ * only the ECC bytes. ++ */ ++ for (chunk = 0; chunk < lt->nchunks; chunk++) { ++ int data_off_in_page, spare_off_in_page, ecc_off_in_page; ++ int data_off, spare_off, ecc_off; ++ int data_len, spare_len, ecc_len; ++ ++ /* No failure reported for this chunk, move to the next one */ ++ if (!(failure_mask & BIT(chunk))) ++ continue; ++ ++ data_off_in_page = chunk * (lt->data_bytes + lt->spare_bytes + ++ lt->ecc_bytes); ++ spare_off_in_page = data_off_in_page + ++ (chunk < lt->full_chunk_cnt ? lt->data_bytes : ++ lt->last_data_bytes); ++ ecc_off_in_page = spare_off_in_page + ++ (chunk < lt->full_chunk_cnt ? lt->spare_bytes : ++ lt->last_spare_bytes); ++ ++ data_off = chunk * lt->data_bytes; ++ spare_off = chunk * lt->spare_bytes; ++ ecc_off = (lt->full_chunk_cnt * lt->spare_bytes) + ++ lt->last_spare_bytes + ++ (chunk * (lt->ecc_bytes + 2)); ++ ++ data_len = chunk < lt->full_chunk_cnt ? lt->data_bytes : ++ lt->last_data_bytes; ++ spare_len = chunk < lt->full_chunk_cnt ? lt->spare_bytes : ++ lt->last_spare_bytes; ++ ecc_len = chunk < lt->full_chunk_cnt ? lt->ecc_bytes : ++ lt->last_ecc_bytes; ++ ++ /* ++ * Only re-read the ECC bytes, unless we are using the 2k/8b ++ * layout which is buggy in the sense that the ECC engine will ++ * try to correct data bytes anyway, creating bitflips. In this ++ * case, re-read the entire page. ++ */ ++ if (lt->writesize == 2048 && lt->strength == 8) { ++ nand_change_read_column_op(chip, data_off_in_page, ++ buf + data_off, data_len, ++ false); ++ nand_change_read_column_op(chip, spare_off_in_page, ++ chip->oob_poi + spare_off, spare_len, ++ false); ++ } ++ ++ nand_change_read_column_op(chip, ecc_off_in_page, ++ chip->oob_poi + ecc_off, ecc_len, ++ false); ++ ++ /* Check the entire chunk (data + spare + ecc) for emptyness */ ++ marvell_nfc_check_empty_chunk(chip, buf + data_off, data_len, ++ chip->oob_poi + spare_off, spare_len, ++ chip->oob_poi + ecc_off, ecc_len, ++ &max_bitflips); ++ } ++ ++ return max_bitflips; ++} ++ ++static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ ++ return chip->ecc.read_page_raw(mtd, chip, chip->data_buf, true, page); ++} ++ ++static int marvell_nfc_hw_ecc_bch_read_oob(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ ++ return chip->ecc.read_page(mtd, chip, chip->data_buf, true, page); ++} ++ ++/* BCH write helpers */ ++static int marvell_nfc_hw_ecc_bch_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, int page) ++{ ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ int full_chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes; ++ int data_len = lt->data_bytes; ++ int spare_len = lt->spare_bytes; ++ int ecc_len = lt->ecc_bytes; ++ int spare_offset = 0; ++ int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) + ++ lt->last_spare_bytes; ++ int chunk; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ for (chunk = 0; chunk < lt->nchunks; chunk++) { ++ if (chunk >= lt->full_chunk_cnt) { ++ data_len = lt->last_data_bytes; ++ spare_len = lt->last_spare_bytes; ++ ecc_len = lt->last_ecc_bytes; ++ } ++ ++ /* Point to the column of the next chunk */ ++ nand_change_write_column_op(chip, chunk * full_chunk_size, ++ NULL, 0, false); ++ ++ /* Write the data */ ++ nand_write_data_op(chip, buf + (chunk * lt->data_bytes), ++ data_len, false); ++ ++ if (!oob_required) ++ continue; ++ ++ /* Write the spare bytes */ ++ if (spare_len) ++ nand_write_data_op(chip, chip->oob_poi + spare_offset, ++ spare_len, false); ++ ++ /* Write the ECC bytes */ ++ if (ecc_len) ++ nand_write_data_op(chip, chip->oob_poi + ecc_offset, ++ ecc_len, false); ++ ++ spare_offset += spare_len; ++ ecc_offset += ALIGN(ecc_len, 32); ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int ++marvell_nfc_hw_ecc_bch_write_chunk(struct nand_chip *chip, int chunk, ++ const u8 *data, unsigned int data_len, ++ const u8 *spare, unsigned int spare_len, ++ int page) ++{ ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ u32 xtype; ++ int ret; ++ struct marvell_nfc_op nfc_op = { ++ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | NDCB0_LEN_OVRD, ++ .ndcb[3] = data_len + spare_len, ++ }; ++ ++ /* ++ * First operation dispatches the CMD_SEQIN command, issue the address ++ * cycles and asks for the first chunk of data. ++ * All operations in the middle (if any) will issue a naked write and ++ * also ask for data. ++ * Last operation (if any) asks for the last chunk of data through a ++ * last naked write. ++ */ ++ if (chunk == 0) { ++ if (lt->nchunks == 1) ++ xtype = XTYPE_MONOLITHIC_RW; ++ else ++ xtype = XTYPE_WRITE_DISPATCH; ++ ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(xtype) | ++ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) | ++ NDCB0_CMD1(NAND_CMD_SEQIN); ++ nfc_op.ndcb[1] |= NDCB1_ADDRS_PAGE(page); ++ nfc_op.ndcb[2] |= NDCB2_ADDR5_PAGE(page); ++ } else if (chunk < lt->nchunks - 1) { ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW); ++ } else { ++ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); ++ } ++ ++ /* Always dispatch the PAGEPROG command on the last chunk */ ++ if (chunk == lt->nchunks - 1) ++ nfc_op.ndcb[0] |= NDCB0_CMD2(NAND_CMD_PAGEPROG) | NDCB0_DBC; ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ, ++ "WRDREQ while loading FIFO (data)"); ++ if (ret) ++ return ret; ++ ++ /* Transfer the contents */ ++ iowrite32_rep(nfc->regs + NDDB, data, FIFO_REP(data_len)); ++ iowrite32_rep(nfc->regs + NDDB, spare, FIFO_REP(spare_len)); ++ ++ return 0; ++} ++ ++static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, int page) ++{ ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ const u8 *data = buf; ++ const u8 *spare = chip->oob_poi; ++ int data_len = lt->data_bytes; ++ int spare_len = lt->spare_bytes; ++ int chunk, ret; ++ ++ /* Spare data will be written anyway, so clear it to avoid garbage */ ++ if (!oob_required) ++ memset(chip->oob_poi, 0xFF, mtd->oobsize); ++ ++ marvell_nfc_enable_hw_ecc(chip); ++ ++ for (chunk = 0; chunk < lt->nchunks; chunk++) { ++ if (chunk >= lt->full_chunk_cnt) { ++ data_len = lt->last_data_bytes; ++ spare_len = lt->last_spare_bytes; ++ } ++ ++ marvell_nfc_hw_ecc_bch_write_chunk(chip, chunk, data, data_len, ++ spare, spare_len, page); ++ data += data_len; ++ spare += spare_len; ++ ++ /* ++ * Waiting only for CMDD or PAGED is not enough, ECC are ++ * partially written. No flag is set once the operation is ++ * really finished but the ND_RUN bit is cleared, so wait for it ++ * before stepping into the next command. ++ */ ++ marvell_nfc_wait_ndrun(chip); ++ } ++ ++ ret = marvell_nfc_wait_op(chip, ++ PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max)); ++ ++ marvell_nfc_disable_hw_ecc(chip); ++ ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ int page) ++{ ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ ++ memset(chip->data_buf, 0xFF, mtd->writesize); ++ ++ return chip->ecc.write_page_raw(mtd, chip, chip->data_buf, true, page); ++} ++ ++static int marvell_nfc_hw_ecc_bch_write_oob(struct mtd_info *mtd, ++ struct nand_chip *chip, int page) ++{ ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ ++ memset(chip->data_buf, 0xFF, mtd->writesize); ++ ++ return chip->ecc.write_page(mtd, chip, chip->data_buf, true, page); ++} ++ ++/* NAND framework ->exec_op() hooks and related helpers */ ++static void marvell_nfc_parse_instructions(struct nand_chip *chip, ++ const struct nand_subop *subop, ++ struct marvell_nfc_op *nfc_op) ++{ ++ const struct nand_op_instr *instr = NULL; ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ bool first_cmd = true; ++ unsigned int op_id; ++ int i; ++ ++ /* Reset the input structure as most of its fields will be OR'ed */ ++ memset(nfc_op, 0, sizeof(struct marvell_nfc_op)); ++ ++ for (op_id = 0; op_id < subop->ninstrs; op_id++) { ++ unsigned int offset, naddrs; ++ const u8 *addrs; ++ int len; ++ ++ instr = &subop->instrs[op_id]; ++ ++ switch (instr->type) { ++ case NAND_OP_CMD_INSTR: ++ if (first_cmd) ++ nfc_op->ndcb[0] |= ++ NDCB0_CMD1(instr->ctx.cmd.opcode); ++ else ++ nfc_op->ndcb[0] |= ++ NDCB0_CMD2(instr->ctx.cmd.opcode) | ++ NDCB0_DBC; ++ ++ nfc_op->cle_ale_delay_ns = instr->delay_ns; ++ first_cmd = false; ++ break; ++ ++ case NAND_OP_ADDR_INSTR: ++ offset = nand_subop_get_addr_start_off(subop, op_id); ++ naddrs = nand_subop_get_num_addr_cyc(subop, op_id); ++ addrs = &instr->ctx.addr.addrs[offset]; ++ ++ nfc_op->ndcb[0] |= NDCB0_ADDR_CYC(naddrs); ++ ++ for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) ++ nfc_op->ndcb[1] |= addrs[i] << (8 * i); ++ ++ if (naddrs >= 5) ++ nfc_op->ndcb[2] |= NDCB2_ADDR5_CYC(addrs[4]); ++ if (naddrs >= 6) ++ nfc_op->ndcb[3] |= NDCB3_ADDR6_CYC(addrs[5]); ++ if (naddrs == 7) ++ nfc_op->ndcb[3] |= NDCB3_ADDR7_CYC(addrs[6]); ++ ++ nfc_op->cle_ale_delay_ns = instr->delay_ns; ++ break; ++ ++ case NAND_OP_DATA_IN_INSTR: ++ nfc_op->data_instr = instr; ++ nfc_op->data_instr_idx = op_id; ++ nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ); ++ if (nfc->caps->is_nfcv2) { ++ nfc_op->ndcb[0] |= ++ NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) | ++ NDCB0_LEN_OVRD; ++ len = nand_subop_get_data_len(subop, op_id); ++ nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH); ++ } ++ nfc_op->data_delay_ns = instr->delay_ns; ++ break; ++ ++ case NAND_OP_DATA_OUT_INSTR: ++ nfc_op->data_instr = instr; ++ nfc_op->data_instr_idx = op_id; ++ nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE); ++ if (nfc->caps->is_nfcv2) { ++ nfc_op->ndcb[0] |= ++ NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) | ++ NDCB0_LEN_OVRD; ++ len = nand_subop_get_data_len(subop, op_id); ++ nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH); ++ } ++ nfc_op->data_delay_ns = instr->delay_ns; ++ break; ++ ++ case NAND_OP_WAITRDY_INSTR: ++ nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms; ++ nfc_op->rdy_delay_ns = instr->delay_ns; ++ break; ++ } ++ } ++} ++ ++static int marvell_nfc_xfer_data_pio(struct nand_chip *chip, ++ const struct nand_subop *subop, ++ struct marvell_nfc_op *nfc_op) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ const struct nand_op_instr *instr = nfc_op->data_instr; ++ unsigned int op_id = nfc_op->data_instr_idx; ++ unsigned int len = nand_subop_get_data_len(subop, op_id); ++ unsigned int offset = nand_subop_get_data_start_off(subop, op_id); ++ bool reading = (instr->type == NAND_OP_DATA_IN_INSTR); ++ int ret; ++ ++ if (instr->ctx.data.force_8bit) ++ marvell_nfc_force_byte_access(chip, true); ++ ++ if (reading) { ++ u8 *in = instr->ctx.data.buf.in + offset; ++ ++ ret = marvell_nfc_xfer_data_in_pio(nfc, in, len); ++ } else { ++ const u8 *out = instr->ctx.data.buf.out + offset; ++ ++ ret = marvell_nfc_xfer_data_out_pio(nfc, out, len); ++ } ++ ++ if (instr->ctx.data.force_8bit) ++ marvell_nfc_force_byte_access(chip, false); ++ ++ return ret; ++} ++ ++static int marvell_nfc_monolithic_access_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ bool reading; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ reading = (nfc_op.data_instr->type == NAND_OP_DATA_IN_INSTR); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ, ++ "RDDREQ/WRDREQ while draining raw data"); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.cle_ale_delay_ns); ++ ++ if (reading) { ++ if (nfc_op.rdy_timeout_ms) { ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ if (ret) ++ return ret; ++ } ++ ++ cond_delay(nfc_op.rdy_delay_ns); ++ } ++ ++ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.data_delay_ns); ++ ++ if (!reading) { ++ if (nfc_op.rdy_timeout_ms) { ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ if (ret) ++ return ret; ++ } ++ ++ cond_delay(nfc_op.rdy_delay_ns); ++ } ++ ++ /* ++ * NDCR ND_RUN bit should be cleared automatically at the end of each ++ * operation but experience shows that the behavior is buggy when it ++ * comes to writes (with LEN_OVRD). Clear it by hand in this case. ++ */ ++ if (!reading) { ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ ++ writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, ++ nfc->regs + NDCR); ++ } ++ ++ return 0; ++} ++ ++static int marvell_nfc_naked_access_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ ++ /* ++ * Naked access are different in that they need to be flagged as naked ++ * by the controller. Reset the controller registers fields that inform ++ * on the type and refill them according to the ongoing operation. ++ */ ++ nfc_op.ndcb[0] &= ~(NDCB0_CMD_TYPE(TYPE_MASK) | ++ NDCB0_CMD_XTYPE(XTYPE_MASK)); ++ switch (subop->instrs[0].type) { ++ case NAND_OP_CMD_INSTR: ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_CMD); ++ break; ++ case NAND_OP_ADDR_INSTR: ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_ADDR); ++ break; ++ case NAND_OP_DATA_IN_INSTR: ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ) | ++ NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); ++ break; ++ case NAND_OP_DATA_OUT_INSTR: ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE) | ++ NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW); ++ break; ++ default: ++ /* This should never happen */ ++ break; ++ } ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ++ if (!nfc_op.data_instr) { ++ ret = marvell_nfc_wait_cmdd(chip); ++ cond_delay(nfc_op.cle_ale_delay_ns); ++ return ret; ++ } ++ ++ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ, ++ "RDDREQ/WRDREQ while draining raw data"); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ /* ++ * NDCR ND_RUN bit should be cleared automatically at the end of each ++ * operation but experience shows that the behavior is buggy when it ++ * comes to writes (with LEN_OVRD). Clear it by hand in this case. ++ */ ++ if (subop->instrs[0].type == NAND_OP_DATA_OUT_INSTR) { ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ ++ writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN, ++ nfc->regs + NDCR); ++ } ++ ++ return 0; ++} ++ ++static int marvell_nfc_naked_waitrdy_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ cond_delay(nfc_op.rdy_delay_ns); ++ ++ return ret; ++} ++ ++static int marvell_nfc_read_id_type_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ); ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ_ID); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, ++ "RDDREQ while reading ID"); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.cle_ale_delay_ns); ++ ++ if (nfc_op.rdy_timeout_ms) { ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ if (ret) ++ return ret; ++ } ++ ++ cond_delay(nfc_op.rdy_delay_ns); ++ ++ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.data_delay_ns); ++ ++ return 0; ++} ++ ++static int marvell_nfc_read_status_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ); ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_STATUS); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ, ++ "RDDREQ while reading status"); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.cle_ale_delay_ns); ++ ++ if (nfc_op.rdy_timeout_ms) { ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ if (ret) ++ return ret; ++ } ++ ++ cond_delay(nfc_op.rdy_delay_ns); ++ ++ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op); ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.data_delay_ns); ++ ++ return 0; ++} ++ ++static int marvell_nfc_reset_cmd_type_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_RESET); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.cle_ale_delay_ns); ++ ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.rdy_delay_ns); ++ ++ return 0; ++} ++ ++static int marvell_nfc_erase_cmd_type_exec(struct nand_chip *chip, ++ const struct nand_subop *subop) ++{ ++ struct marvell_nfc_op nfc_op; ++ int ret; ++ ++ marvell_nfc_parse_instructions(chip, subop, &nfc_op); ++ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_ERASE); ++ ++ ret = marvell_nfc_prepare_cmd(chip); ++ if (ret) ++ return ret; ++ ++ marvell_nfc_send_cmd(chip, &nfc_op); ++ ret = marvell_nfc_wait_cmdd(chip); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.cle_ale_delay_ns); ++ ++ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms); ++ if (ret) ++ return ret; ++ ++ cond_delay(nfc_op.rdy_delay_ns); ++ ++ return 0; ++} ++ ++static const struct nand_op_parser marvell_nfcv2_op_parser = NAND_OP_PARSER( ++ /* Monolithic reads/writes */ ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_monolithic_access_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC_NFCV2), ++ NAND_OP_PARSER_PAT_CMD_ELEM(true), ++ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), ++ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_monolithic_access_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2), ++ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE), ++ NAND_OP_PARSER_PAT_CMD_ELEM(true), ++ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), ++ /* Naked commands */ ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_naked_access_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_naked_access_exec, ++ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_naked_access_exec, ++ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_naked_access_exec, ++ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_naked_waitrdy_exec, ++ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), ++ ); ++ ++static const struct nand_op_parser marvell_nfcv1_op_parser = NAND_OP_PARSER( ++ /* Naked commands not supported, use a function for each pattern */ ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_read_id_type_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1), ++ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_erase_cmd_type_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1), ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_read_status_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_reset_cmd_type_exec, ++ NAND_OP_PARSER_PAT_CMD_ELEM(false), ++ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), ++ NAND_OP_PARSER_PATTERN( ++ marvell_nfc_naked_waitrdy_exec, ++ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), ++ ); ++ ++static int marvell_nfc_exec_op(struct nand_chip *chip, ++ const struct nand_operation *op, ++ bool check_only) ++{ ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ ++ if (nfc->caps->is_nfcv2) ++ return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser, ++ op, check_only); ++ else ++ return nand_op_parser_exec_op(chip, &marvell_nfcv1_op_parser, ++ op, check_only); ++} ++ ++/* ++ * Layouts were broken in old pxa3xx_nand driver, these are supposed to be ++ * usable. ++ */ ++static int marvell_nand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = (lt->full_chunk_cnt * lt->ecc_bytes) + ++ lt->last_ecc_bytes; ++ oobregion->offset = mtd->oobsize - oobregion->length; ++ ++ return 0; ++} ++ ++static int marvell_nand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout; ++ ++ if (section) ++ return -ERANGE; ++ ++ /* ++ * Bootrom looks in bytes 0 & 5 for bad blocks for the ++ * 4KB page / 4bit BCH combination. ++ */ ++ if (mtd->writesize == SZ_4K && lt->data_bytes == SZ_2K) ++ oobregion->offset = 6; ++ else ++ oobregion->offset = 2; ++ ++ oobregion->length = (lt->full_chunk_cnt * lt->spare_bytes) + ++ lt->last_spare_bytes - oobregion->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops marvell_nand_ooblayout_ops = { ++ .ecc = marvell_nand_ooblayout_ecc, ++ .free = marvell_nand_ooblayout_free, ++}; ++ ++static int marvell_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, ++ struct nand_ecc_ctrl *ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ const struct marvell_hw_ecc_layout *l; ++ int i; ++ ++ if (!nfc->caps->is_nfcv2 && ++ (mtd->writesize + mtd->oobsize > MAX_CHUNK_SIZE)) { ++ dev_err(nfc->dev, ++ "NFCv1: writesize (%d) cannot be bigger than a chunk (%d)\n", ++ mtd->writesize, MAX_CHUNK_SIZE - mtd->oobsize); ++ return -ENOTSUPP; ++ } ++ ++ to_marvell_nand(chip)->layout = NULL; ++ for (i = 0; i < ARRAY_SIZE(marvell_nfc_layouts); i++) { ++ l = &marvell_nfc_layouts[i]; ++ if (mtd->writesize == l->writesize && ++ ecc->size == l->chunk && ecc->strength == l->strength) { ++ to_marvell_nand(chip)->layout = l; ++ break; ++ } ++ } ++ ++ if (!to_marvell_nand(chip)->layout || ++ (!nfc->caps->is_nfcv2 && ecc->strength > 1)) { ++ dev_err(nfc->dev, ++ "ECC strength %d at page size %d is not supported\n", ++ ecc->strength, mtd->writesize); ++ return -ENOTSUPP; ++ } ++ ++ /* Special care for the layout 2k/8-bit/512B */ ++ if (l->writesize == 2048 && l->strength == 8) { ++ if (mtd->oobsize < 128) { ++ dev_err(nfc->dev, "Requested layout needs at least 128 OOB bytes\n"); ++ return -ENOTSUPP; ++ } else { ++ chip->bbt_options |= NAND_BBT_NO_OOB_BBM; ++ } ++ } ++ ++ mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops); ++ ecc->steps = l->nchunks; ++ ecc->size = l->data_bytes; ++ ++ if (ecc->strength == 1) { ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw; ++ ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page; ++ ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw; ++ ecc->read_oob = ecc->read_oob_raw; ++ ecc->write_page_raw = marvell_nfc_hw_ecc_hmg_write_page_raw; ++ ecc->write_page = marvell_nfc_hw_ecc_hmg_write_page; ++ ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw; ++ ecc->write_oob = ecc->write_oob_raw; ++ } else { ++ chip->ecc.algo = NAND_ECC_BCH; ++ ecc->strength = 16; ++ ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw; ++ ecc->read_page = marvell_nfc_hw_ecc_bch_read_page; ++ ecc->read_oob_raw = marvell_nfc_hw_ecc_bch_read_oob_raw; ++ ecc->read_oob = marvell_nfc_hw_ecc_bch_read_oob; ++ ecc->write_page_raw = marvell_nfc_hw_ecc_bch_write_page_raw; ++ ecc->write_page = marvell_nfc_hw_ecc_bch_write_page; ++ ecc->write_oob_raw = marvell_nfc_hw_ecc_bch_write_oob_raw; ++ ecc->write_oob = marvell_nfc_hw_ecc_bch_write_oob; ++ } ++ ++ return 0; ++} ++ ++static int marvell_nand_ecc_init(struct mtd_info *mtd, ++ struct nand_ecc_ctrl *ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ int ret; ++ ++ if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) { ++ if (chip->ecc_step_ds && chip->ecc_strength_ds) { ++ ecc->size = chip->ecc_step_ds; ++ ecc->strength = chip->ecc_strength_ds; ++ } else { ++ dev_info(nfc->dev, ++ "No minimum ECC strength, using 1b/512B\n"); ++ ecc->size = 512; ++ ecc->strength = 1; ++ } ++ } ++ ++ switch (ecc->mode) { ++ case NAND_ECC_HW: ++ ret = marvell_nand_hw_ecc_ctrl_init(mtd, ecc); ++ if (ret) ++ return ret; ++ break; ++ case NAND_ECC_NONE: ++ case NAND_ECC_SOFT: ++ case NAND_ECC_ON_DIE: ++ if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 && ++ mtd->writesize != SZ_2K) { ++ dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n", ++ mtd->writesize); ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; ++static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | ++ NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 8, ++ .len = 6, ++ .veroffs = 14, ++ .maxblocks = 8, /* Last 8 blocks in each chip */ ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | ++ NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 8, ++ .len = 6, ++ .veroffs = 14, ++ .maxblocks = 8, /* Last 8 blocks in each chip */ ++ .pattern = bbt_mirror_pattern ++}; ++ ++static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, ++ const struct nand_data_interface ++ *conf) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); ++ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); ++ unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2; ++ const struct nand_sdr_timings *sdr; ++ struct marvell_nfc_timings nfc_tmg; ++ int read_delay; ++ ++ sdr = nand_get_sdr_timings(conf); ++ if (IS_ERR(sdr)) ++ return PTR_ERR(sdr); ++ ++ /* ++ * SDR timings are given in pico-seconds while NFC timings must be ++ * expressed in NAND controller clock cycles, which is half of the ++ * frequency of the accessible ECC clock retrieved by clk_get_rate(). ++ * This is not written anywhere in the datasheet but was observed ++ * with an oscilloscope. ++ * ++ * NFC datasheet gives equations from which thoses calculations ++ * are derived, they tend to be slightly more restrictives than the ++ * given core timings and may improve the overall speed. ++ */ ++ nfc_tmg.tRP = TO_CYCLES(DIV_ROUND_UP(sdr->tRC_min, 2), period_ns) - 1; ++ nfc_tmg.tRH = nfc_tmg.tRP; ++ nfc_tmg.tWP = TO_CYCLES(DIV_ROUND_UP(sdr->tWC_min, 2), period_ns) - 1; ++ nfc_tmg.tWH = nfc_tmg.tWP; ++ nfc_tmg.tCS = TO_CYCLES(sdr->tCS_min, period_ns); ++ nfc_tmg.tCH = TO_CYCLES(sdr->tCH_min, period_ns) - 1; ++ nfc_tmg.tADL = TO_CYCLES(sdr->tADL_min, period_ns); ++ /* ++ * Read delay is the time of propagation from SoC pins to NFC internal ++ * logic. With non-EDO timings, this is MIN_RD_DEL_CNT clock cycles. In ++ * EDO mode, an additional delay of tRH must be taken into account so ++ * the data is sampled on the falling edge instead of the rising edge. ++ */ ++ read_delay = sdr->tRC_min >= 30000 ? ++ MIN_RD_DEL_CNT : MIN_RD_DEL_CNT + nfc_tmg.tRH; ++ ++ nfc_tmg.tAR = TO_CYCLES(sdr->tAR_min, period_ns); ++ /* ++ * tWHR and tRHW are supposed to be read to write delays (and vice ++ * versa) but in some cases, ie. when doing a change column, they must ++ * be greater than that to be sure tCCS delay is respected. ++ */ ++ nfc_tmg.tWHR = TO_CYCLES(max_t(int, sdr->tWHR_min, sdr->tCCS_min), ++ period_ns) - 2, ++ nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min), ++ period_ns); ++ ++ /* ++ * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays. ++ * NFCv1: No WAIT_MODE, tR must be maximal. ++ */ ++ if (nfc->caps->is_nfcv2) { ++ nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns); ++ } else { ++ nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max, ++ period_ns); ++ if (nfc_tmg.tR + 3 > nfc_tmg.tCH) ++ nfc_tmg.tR = nfc_tmg.tCH - 3; ++ else ++ nfc_tmg.tR = 0; ++ } ++ ++ if (chipnr < 0) ++ return 0; ++ ++ marvell_nand->ndtr0 = ++ NDTR0_TRP(nfc_tmg.tRP) | ++ NDTR0_TRH(nfc_tmg.tRH) | ++ NDTR0_ETRP(nfc_tmg.tRP) | ++ NDTR0_TWP(nfc_tmg.tWP) | ++ NDTR0_TWH(nfc_tmg.tWH) | ++ NDTR0_TCS(nfc_tmg.tCS) | ++ NDTR0_TCH(nfc_tmg.tCH); ++ ++ marvell_nand->ndtr1 = ++ NDTR1_TAR(nfc_tmg.tAR) | ++ NDTR1_TWHR(nfc_tmg.tWHR) | ++ NDTR1_TR(nfc_tmg.tR); ++ ++ if (nfc->caps->is_nfcv2) { ++ marvell_nand->ndtr0 |= ++ NDTR0_RD_CNT_DEL(read_delay) | ++ NDTR0_SELCNTR | ++ NDTR0_TADL(nfc_tmg.tADL); ++ ++ marvell_nand->ndtr1 |= ++ NDTR1_TRHW(nfc_tmg.tRHW) | ++ NDTR1_WAIT_MODE; ++ } ++ ++ return 0; ++} ++ ++static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, ++ struct device_node *np) ++{ ++ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(dev); ++ struct marvell_nand_chip *marvell_nand; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ int nsels, ret, i; ++ u32 cs, rb; ++ ++ /* ++ * The legacy "num-cs" property indicates the number of CS on the only ++ * chip connected to the controller (legacy bindings does not support ++ * more than one chip). The CS and RB pins are always the #0. ++ * ++ * When not using legacy bindings, a couple of "reg" and "nand-rb" ++ * properties must be filled. For each chip, expressed as a subnode, ++ * "reg" points to the CS lines and "nand-rb" to the RB line. ++ */ ++ if (pdata || nfc->caps->legacy_of_bindings) { ++ nsels = 1; ++ } else { ++ nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); ++ if (nsels <= 0) { ++ dev_err(dev, "missing/invalid reg property\n"); ++ return -EINVAL; ++ } ++ } ++ ++ /* Alloc the nand chip structure */ ++ marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) + ++ (nsels * ++ sizeof(struct marvell_nand_chip_sel)), ++ GFP_KERNEL); ++ if (!marvell_nand) { ++ dev_err(dev, "could not allocate chip structure\n"); ++ return -ENOMEM; ++ } ++ ++ marvell_nand->nsels = nsels; ++ marvell_nand->selected_die = -1; ++ ++ for (i = 0; i < nsels; i++) { ++ if (pdata || nfc->caps->legacy_of_bindings) { ++ /* ++ * Legacy bindings use the CS lines in natural ++ * order (0, 1, ...) ++ */ ++ cs = i; ++ } else { ++ /* Retrieve CS id */ ++ ret = of_property_read_u32_index(np, "reg", i, &cs); ++ if (ret) { ++ dev_err(dev, "could not retrieve reg property: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ if (cs >= nfc->caps->max_cs_nb) { ++ dev_err(dev, "invalid reg value: %u (max CS = %d)\n", ++ cs, nfc->caps->max_cs_nb); ++ return -EINVAL; ++ } ++ ++ if (test_and_set_bit(cs, &nfc->assigned_cs)) { ++ dev_err(dev, "CS %d already assigned\n", cs); ++ return -EINVAL; ++ } ++ ++ /* ++ * The cs variable represents the chip select id, which must be ++ * converted in bit fields for NDCB0 and NDCB2 to select the ++ * right chip. Unfortunately, due to a lack of information on ++ * the subject and incoherent documentation, the user should not ++ * use CS1 and CS3 at all as asserting them is not supported in ++ * a reliable way (due to multiplexing inside ADDR5 field). ++ */ ++ marvell_nand->sels[i].cs = cs; ++ switch (cs) { ++ case 0: ++ case 2: ++ marvell_nand->sels[i].ndcb0_csel = 0; ++ break; ++ case 1: ++ case 3: ++ marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Retrieve RB id */ ++ if (pdata || nfc->caps->legacy_of_bindings) { ++ /* Legacy bindings always use RB #0 */ ++ rb = 0; ++ } else { ++ ret = of_property_read_u32_index(np, "nand-rb", i, ++ &rb); ++ if (ret) { ++ dev_err(dev, ++ "could not retrieve RB property: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ if (rb >= nfc->caps->max_rb_nb) { ++ dev_err(dev, "invalid reg value: %u (max RB = %d)\n", ++ rb, nfc->caps->max_rb_nb); ++ return -EINVAL; ++ } ++ ++ marvell_nand->sels[i].rb = rb; ++ } ++ ++ chip = &marvell_nand->chip; ++ chip->controller = &nfc->controller; ++ nand_set_flash_node(chip, np); ++ ++ chip->exec_op = marvell_nfc_exec_op; ++ chip->select_chip = marvell_nfc_select_chip; ++ if (!of_property_read_bool(np, "marvell,nand-keep-config")) ++ chip->setup_data_interface = marvell_nfc_setup_data_interface; ++ ++ mtd = nand_to_mtd(chip); ++ mtd->dev.parent = dev; ++ ++ /* ++ * Default to HW ECC engine mode. If the nand-ecc-mode property is given ++ * in the DT node, this entry will be overwritten in nand_scan_ident(). ++ */ ++ chip->ecc.mode = NAND_ECC_HW; ++ ++ /* ++ * Save a reference value for timing registers before ++ * ->setup_data_interface() is called. ++ */ ++ marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0); ++ marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1); ++ ++ chip->options |= NAND_BUSWIDTH_AUTO; ++ ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL); ++ if (ret) { ++ dev_err(dev, "could not identify the nand chip\n"); ++ return ret; ++ } ++ ++ if (pdata && pdata->flash_bbt) ++ chip->bbt_options |= NAND_BBT_USE_FLASH; ++ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) { ++ /* ++ * We'll use a bad block table stored in-flash and don't ++ * allow writing the bad block marker to the flash. ++ */ ++ chip->bbt_options |= NAND_BBT_NO_OOB_BBM; ++ chip->bbt_td = &bbt_main_descr; ++ chip->bbt_md = &bbt_mirror_descr; ++ } ++ ++ /* Save the chip-specific fields of NDCR */ ++ marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize); ++ if (chip->options & NAND_BUSWIDTH_16) ++ marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; ++ ++ /* ++ * On small page NANDs, only one cycle is needed to pass the ++ * column address. ++ */ ++ if (mtd->writesize <= 512) { ++ marvell_nand->addr_cyc = 1; ++ } else { ++ marvell_nand->addr_cyc = 2; ++ marvell_nand->ndcr |= NDCR_RA_START; ++ } ++ ++ /* ++ * Now add the number of cycles needed to pass the row ++ * address. ++ * ++ * Addressing a chip using CS 2 or 3 should also need the third row ++ * cycle but due to inconsistance in the documentation and lack of ++ * hardware to test this situation, this case is not supported. ++ */ ++ if (chip->options & NAND_ROW_ADDR_3) ++ marvell_nand->addr_cyc += 3; ++ else ++ marvell_nand->addr_cyc += 2; ++ ++ if (pdata) { ++ chip->ecc.size = pdata->ecc_step_size; ++ chip->ecc.strength = pdata->ecc_strength; ++ } ++ ++ ret = marvell_nand_ecc_init(mtd, &chip->ecc); ++ if (ret) { ++ dev_err(dev, "ECC init failed: %d\n", ret); ++ return ret; ++ } ++ ++ if (chip->ecc.mode == NAND_ECC_HW) { ++ /* ++ * Subpage write not available with hardware ECC, prohibit also ++ * subpage read as in userspace subpage access would still be ++ * allowed and subpage write, if used, would lead to numerous ++ * uncorrectable ECC errors. ++ */ ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ } ++ ++ if (pdata || nfc->caps->legacy_of_bindings) { ++ /* ++ * We keep the MTD name unchanged to avoid breaking platforms ++ * where the MTD cmdline parser is used and the bootloader ++ * has not been updated to use the new naming scheme. ++ */ ++ mtd->name = "pxa3xx_nand-0"; ++ } else if (!mtd->name) { ++ /* ++ * If the new bindings are used and the bootloader has not been ++ * updated to pass a new mtdparts parameter on the cmdline, you ++ * should define the following property in your NAND node, ie: ++ * ++ * label = "main-storage"; ++ * ++ * This way, mtd->name will be set by the core when ++ * nand_set_flash_node() is called. ++ */ ++ mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, ++ "%s:nand.%d", dev_name(nfc->dev), ++ marvell_nand->sels[0].cs); ++ if (!mtd->name) { ++ dev_err(nfc->dev, "Failed to allocate mtd->name\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) { ++ dev_err(dev, "nand_scan_tail failed: %d\n", ret); ++ return ret; ++ } ++ ++ if (pdata) ++ /* Legacy bindings support only one chip */ ++ ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts); ++ else ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(dev, "failed to register mtd device: %d\n", ret); ++ nand_release(mtd); ++ return ret; ++ } ++ ++ list_add_tail(&marvell_nand->node, &nfc->chips); ++ ++ return 0; ++} ++ ++static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc) ++{ ++ struct device_node *np = dev->of_node; ++ struct device_node *nand_np; ++ int max_cs = nfc->caps->max_cs_nb; ++ int nchips; ++ int ret; ++ ++ if (!np) ++ nchips = 1; ++ else ++ nchips = of_get_child_count(np); ++ ++ if (nchips > max_cs) { ++ dev_err(dev, "too many NAND chips: %d (max = %d CS)\n", nchips, ++ max_cs); ++ return -EINVAL; ++ } ++ ++ /* ++ * Legacy bindings do not use child nodes to exhibit NAND chip ++ * properties and layout. Instead, NAND properties are mixed with the ++ * controller ones, and partitions are defined as direct subnodes of the ++ * NAND controller node. ++ */ ++ if (nfc->caps->legacy_of_bindings) { ++ ret = marvell_nand_chip_init(dev, nfc, np); ++ return ret; ++ } ++ ++ for_each_child_of_node(np, nand_np) { ++ ret = marvell_nand_chip_init(dev, nfc, nand_np); ++ if (ret) { ++ of_node_put(nand_np); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc) ++{ ++ struct marvell_nand_chip *entry, *temp; ++ ++ list_for_each_entry_safe(entry, temp, &nfc->chips, node) { ++ nand_release(nand_to_mtd(&entry->chip)); ++ list_del(&entry->node); ++ } ++} ++ ++static int marvell_nfc_init_dma(struct marvell_nfc *nfc) ++{ ++ struct platform_device *pdev = container_of(nfc->dev, ++ struct platform_device, ++ dev); ++ struct dma_slave_config config = {}; ++ struct resource *r; ++ int ret; ++ ++ if (!IS_ENABLED(CONFIG_PXA_DMA)) { ++ dev_warn(nfc->dev, ++ "DMA not enabled in configuration\n"); ++ return -ENOTSUPP; ++ } ++ ++ ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32)); ++ if (ret) ++ return ret; ++ ++ nfc->dma_chan = dma_request_slave_channel(nfc->dev, "data"); ++ if (!nfc->dma_chan) { ++ dev_err(nfc->dev, ++ "Unable to request data DMA channel\n"); ++ return -ENODEV; ++ } ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!r) ++ return -ENXIO; ++ ++ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.src_addr = r->start + NDDB; ++ config.dst_addr = r->start + NDDB; ++ config.src_maxburst = 32; ++ config.dst_maxburst = 32; ++ ret = dmaengine_slave_config(nfc->dma_chan, &config); ++ if (ret < 0) { ++ dev_err(nfc->dev, "Failed to configure DMA channel\n"); ++ return ret; ++ } ++ ++ /* ++ * DMA must act on length multiple of 32 and this length may be ++ * bigger than the destination buffer. Use this buffer instead ++ * for DMA transfers and then copy the desired amount of data to ++ * the provided buffer. ++ */ ++ nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); ++ if (!nfc->dma_buf) ++ return -ENOMEM; ++ ++ nfc->use_dma = true; ++ ++ return 0; ++} ++ ++static void marvell_nfc_reset(struct marvell_nfc *nfc) ++{ ++ /* ++ * ECC operations and interruptions are only enabled when specifically ++ * needed. ECC shall not be activated in the early stages (fails probe). ++ * Arbiter flag, even if marked as "reserved", must be set (empirical). ++ * SPARE_EN bit must always be set or ECC bytes will not be at the same ++ * offset in the read page and this will fail the protection. ++ */ ++ writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN | ++ NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR); ++ writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR); ++ writel_relaxed(0, nfc->regs + NDECCCTRL); ++} ++ ++static int marvell_nfc_init(struct marvell_nfc *nfc) ++{ ++ struct device_node *np = nfc->dev->of_node; ++ ++ /* ++ * Some SoCs like A7k/A8k need to enable manually the NAND ++ * controller, gated clocks and reset bits to avoid being bootloader ++ * dependent. This is done through the use of the System Functions ++ * registers. ++ */ ++ if (nfc->caps->need_system_controller) { ++ struct regmap *sysctrl_base = ++ syscon_regmap_lookup_by_phandle(np, ++ "marvell,system-controller"); ++ ++ if (IS_ERR(sysctrl_base)) ++ return PTR_ERR(sysctrl_base); ++ ++ regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ++ GENCONF_SOC_DEVICE_MUX_NFC_EN | ++ GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST | ++ GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST | ++ GENCONF_SOC_DEVICE_MUX_NFC_INT_EN); ++ ++ regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL, ++ GENCONF_CLK_GATING_CTRL_ND_GATE, ++ GENCONF_CLK_GATING_CTRL_ND_GATE); ++ ++ regmap_update_bits(sysctrl_base, GENCONF_ND_CLK_CTRL, ++ GENCONF_ND_CLK_CTRL_EN, ++ GENCONF_ND_CLK_CTRL_EN); ++ } ++ ++ /* Configure the DMA if appropriate */ ++ if (!nfc->caps->is_nfcv2) ++ marvell_nfc_init_dma(nfc); ++ ++ marvell_nfc_reset(nfc); ++ ++ return 0; ++} ++ ++static int marvell_nfc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *r; ++ struct marvell_nfc *nfc; ++ int ret; ++ int irq; ++ ++ nfc = devm_kzalloc(&pdev->dev, sizeof(struct marvell_nfc), ++ GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ nfc->dev = dev; ++ nand_controller_init(&nfc->controller); ++ INIT_LIST_HEAD(&nfc->chips); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nfc->regs = devm_ioremap_resource(dev, r); ++ if (IS_ERR(nfc->regs)) ++ return PTR_ERR(nfc->regs); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "failed to retrieve irq\n"); ++ return irq; ++ } ++ ++ nfc->core_clk = devm_clk_get(&pdev->dev, "core"); ++ ++ /* Managed the legacy case (when the first clock was not named) */ ++ if (nfc->core_clk == ERR_PTR(-ENOENT)) ++ nfc->core_clk = devm_clk_get(&pdev->dev, NULL); ++ ++ if (IS_ERR(nfc->core_clk)) ++ return PTR_ERR(nfc->core_clk); ++ ++ ret = clk_prepare_enable(nfc->core_clk); ++ if (ret) ++ return ret; ++ ++ nfc->reg_clk = devm_clk_get(&pdev->dev, "reg"); ++ if (IS_ERR(nfc->reg_clk)) { ++ if (PTR_ERR(nfc->reg_clk) != -ENOENT) { ++ ret = PTR_ERR(nfc->reg_clk); ++ goto unprepare_core_clk; ++ } ++ ++ nfc->reg_clk = NULL; ++ } ++ ++ ret = clk_prepare_enable(nfc->reg_clk); ++ if (ret) ++ goto unprepare_core_clk; ++ ++ marvell_nfc_disable_int(nfc, NDCR_ALL_INT); ++ marvell_nfc_clear_int(nfc, NDCR_ALL_INT); ++ ret = devm_request_irq(dev, irq, marvell_nfc_isr, ++ 0, "marvell-nfc", nfc); ++ if (ret) ++ goto unprepare_reg_clk; ++ ++ /* Get NAND controller capabilities */ ++ if (pdev->id_entry) ++ nfc->caps = (void *)pdev->id_entry->driver_data; ++ else ++ nfc->caps = of_device_get_match_data(&pdev->dev); ++ ++ if (!nfc->caps) { ++ dev_err(dev, "Could not retrieve NFC caps\n"); ++ ret = -EINVAL; ++ goto unprepare_reg_clk; ++ } ++ ++ /* Init the controller and then probe the chips */ ++ ret = marvell_nfc_init(nfc); ++ if (ret) ++ goto unprepare_reg_clk; ++ ++ platform_set_drvdata(pdev, nfc); ++ ++ ret = marvell_nand_chips_init(dev, nfc); ++ if (ret) ++ goto unprepare_reg_clk; ++ ++ return 0; ++ ++unprepare_reg_clk: ++ clk_disable_unprepare(nfc->reg_clk); ++unprepare_core_clk: ++ clk_disable_unprepare(nfc->core_clk); ++ ++ return ret; ++} ++ ++static int marvell_nfc_remove(struct platform_device *pdev) ++{ ++ struct marvell_nfc *nfc = platform_get_drvdata(pdev); ++ ++ marvell_nand_chips_cleanup(nfc); ++ ++ if (nfc->use_dma) { ++ dmaengine_terminate_all(nfc->dma_chan); ++ dma_release_channel(nfc->dma_chan); ++ } ++ ++ clk_disable_unprepare(nfc->reg_clk); ++ clk_disable_unprepare(nfc->core_clk); ++ ++ return 0; ++} ++ ++static int __maybe_unused marvell_nfc_suspend(struct device *dev) ++{ ++ struct marvell_nfc *nfc = dev_get_drvdata(dev); ++ struct marvell_nand_chip *chip; ++ ++ list_for_each_entry(chip, &nfc->chips, node) ++ marvell_nfc_wait_ndrun(&chip->chip); ++ ++ clk_disable_unprepare(nfc->reg_clk); ++ clk_disable_unprepare(nfc->core_clk); ++ ++ return 0; ++} ++ ++static int __maybe_unused marvell_nfc_resume(struct device *dev) ++{ ++ struct marvell_nfc *nfc = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(nfc->core_clk); ++ if (ret < 0) ++ return ret; ++ ++ ret = clk_prepare_enable(nfc->reg_clk); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * Reset nfc->selected_chip so the next command will cause the timing ++ * registers to be restored in marvell_nfc_select_chip(). ++ */ ++ nfc->selected_chip = NULL; ++ ++ /* Reset registers that have lost their contents */ ++ marvell_nfc_reset(nfc); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops marvell_nfc_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(marvell_nfc_suspend, marvell_nfc_resume) ++}; ++ ++static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = { ++ .max_cs_nb = 4, ++ .max_rb_nb = 2, ++ .need_system_controller = true, ++ .is_nfcv2 = true, ++}; ++ ++static const struct marvell_nfc_caps marvell_armada370_nfc_caps = { ++ .max_cs_nb = 4, ++ .max_rb_nb = 2, ++ .is_nfcv2 = true, ++}; ++ ++static const struct marvell_nfc_caps marvell_pxa3xx_nfc_caps = { ++ .max_cs_nb = 2, ++ .max_rb_nb = 1, ++ .use_dma = true, ++}; ++ ++static const struct marvell_nfc_caps marvell_armada_8k_nfc_legacy_caps = { ++ .max_cs_nb = 4, ++ .max_rb_nb = 2, ++ .need_system_controller = true, ++ .legacy_of_bindings = true, ++ .is_nfcv2 = true, ++}; ++ ++static const struct marvell_nfc_caps marvell_armada370_nfc_legacy_caps = { ++ .max_cs_nb = 4, ++ .max_rb_nb = 2, ++ .legacy_of_bindings = true, ++ .is_nfcv2 = true, ++}; ++ ++static const struct marvell_nfc_caps marvell_pxa3xx_nfc_legacy_caps = { ++ .max_cs_nb = 2, ++ .max_rb_nb = 1, ++ .legacy_of_bindings = true, ++ .use_dma = true, ++}; ++ ++static const struct platform_device_id marvell_nfc_platform_ids[] = { ++ { ++ .name = "pxa3xx-nand", ++ .driver_data = (kernel_ulong_t)&marvell_pxa3xx_nfc_legacy_caps, ++ }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(platform, marvell_nfc_platform_ids); ++ ++static const struct of_device_id marvell_nfc_of_ids[] = { ++ { ++ .compatible = "marvell,armada-8k-nand-controller", ++ .data = &marvell_armada_8k_nfc_caps, ++ }, ++ { ++ .compatible = "marvell,armada370-nand-controller", ++ .data = &marvell_armada370_nfc_caps, ++ }, ++ { ++ .compatible = "marvell,pxa3xx-nand-controller", ++ .data = &marvell_pxa3xx_nfc_caps, ++ }, ++ /* Support for old/deprecated bindings: */ ++ { ++ .compatible = "marvell,armada-8k-nand", ++ .data = &marvell_armada_8k_nfc_legacy_caps, ++ }, ++ { ++ .compatible = "marvell,armada370-nand", ++ .data = &marvell_armada370_nfc_legacy_caps, ++ }, ++ { ++ .compatible = "marvell,pxa3xx-nand", ++ .data = &marvell_pxa3xx_nfc_legacy_caps, ++ }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, marvell_nfc_of_ids); ++ ++static struct platform_driver marvell_nfc_driver = { ++ .driver = { ++ .name = "marvell-nfc", ++ .of_match_table = marvell_nfc_of_ids, ++ .pm = &marvell_nfc_pm_ops, ++ }, ++ .id_table = marvell_nfc_platform_ids, ++ .probe = marvell_nfc_probe, ++ .remove = marvell_nfc_remove, ++}; ++module_platform_driver(marvell_nfc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Marvell NAND controller driver"); +diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c +new file mode 100644 +index 00000000..b6b97cc9 +--- /dev/null ++++ b/drivers/mtd/nand/raw/mpc5121_nfc.c +@@ -0,0 +1,857 @@ ++/* ++ * Copyright 2004-2008 Freescale Semiconductor, Inc. ++ * Copyright 2009 Semihalf. ++ * ++ * Approved as OSADL project by a majority of OSADL members and funded ++ * by OSADL membership fees in 2009; for details see www.osadl.org. ++ * ++ * Based on original driver from Freescale Semiconductor ++ * written by John Rigby on basis ++ * of drivers/mtd/nand/mxc_nand.c. Reworked and extended ++ * Piotr Ziecik . ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Addresses for NFC MAIN RAM BUFFER areas */ ++#define NFC_MAIN_AREA(n) ((n) * 0x200) ++ ++/* Addresses for NFC SPARE BUFFER areas */ ++#define NFC_SPARE_BUFFERS 8 ++#define NFC_SPARE_LEN 0x40 ++#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) ++ ++/* MPC5121 NFC registers */ ++#define NFC_BUF_ADDR 0x1E04 ++#define NFC_FLASH_ADDR 0x1E06 ++#define NFC_FLASH_CMD 0x1E08 ++#define NFC_CONFIG 0x1E0A ++#define NFC_ECC_STATUS1 0x1E0C ++#define NFC_ECC_STATUS2 0x1E0E ++#define NFC_SPAS 0x1E10 ++#define NFC_WRPROT 0x1E12 ++#define NFC_NF_WRPRST 0x1E18 ++#define NFC_CONFIG1 0x1E1A ++#define NFC_CONFIG2 0x1E1C ++#define NFC_UNLOCKSTART_BLK0 0x1E20 ++#define NFC_UNLOCKEND_BLK0 0x1E22 ++#define NFC_UNLOCKSTART_BLK1 0x1E24 ++#define NFC_UNLOCKEND_BLK1 0x1E26 ++#define NFC_UNLOCKSTART_BLK2 0x1E28 ++#define NFC_UNLOCKEND_BLK2 0x1E2A ++#define NFC_UNLOCKSTART_BLK3 0x1E2C ++#define NFC_UNLOCKEND_BLK3 0x1E2E ++ ++/* Bit Definitions: NFC_BUF_ADDR */ ++#define NFC_RBA_MASK (7 << 0) ++#define NFC_ACTIVE_CS_SHIFT 5 ++#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) ++ ++/* Bit Definitions: NFC_CONFIG */ ++#define NFC_BLS_UNLOCKED (1 << 1) ++ ++/* Bit Definitions: NFC_CONFIG1 */ ++#define NFC_ECC_4BIT (1 << 0) ++#define NFC_FULL_PAGE_DMA (1 << 1) ++#define NFC_SPARE_ONLY (1 << 2) ++#define NFC_ECC_ENABLE (1 << 3) ++#define NFC_INT_MASK (1 << 4) ++#define NFC_BIG_ENDIAN (1 << 5) ++#define NFC_RESET (1 << 6) ++#define NFC_CE (1 << 7) ++#define NFC_ONE_CYCLE (1 << 8) ++#define NFC_PPB_32 (0 << 9) ++#define NFC_PPB_64 (1 << 9) ++#define NFC_PPB_128 (2 << 9) ++#define NFC_PPB_256 (3 << 9) ++#define NFC_PPB_MASK (3 << 9) ++#define NFC_FULL_PAGE_INT (1 << 11) ++ ++/* Bit Definitions: NFC_CONFIG2 */ ++#define NFC_COMMAND (1 << 0) ++#define NFC_ADDRESS (1 << 1) ++#define NFC_INPUT (1 << 2) ++#define NFC_OUTPUT (1 << 3) ++#define NFC_ID (1 << 4) ++#define NFC_STATUS (1 << 5) ++#define NFC_CMD_FAIL (1 << 15) ++#define NFC_INT (1 << 15) ++ ++/* Bit Definitions: NFC_WRPROT */ ++#define NFC_WPC_LOCK_TIGHT (1 << 0) ++#define NFC_WPC_LOCK (1 << 1) ++#define NFC_WPC_UNLOCK (1 << 2) ++ ++#define DRV_NAME "mpc5121_nfc" ++ ++/* Timeouts */ ++#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ ++#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ ++ ++struct mpc5121_nfc_prv { ++ struct nand_chip chip; ++ int irq; ++ void __iomem *regs; ++ struct clk *clk; ++ wait_queue_head_t irq_waitq; ++ uint column; ++ int spareonly; ++ void __iomem *csreg; ++ struct device *dev; ++}; ++ ++static void mpc5121_nfc_done(struct mtd_info *mtd); ++ ++/* Read NFC register */ ++static inline u16 nfc_read(struct mtd_info *mtd, uint reg) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ ++ return in_be16(prv->regs + reg); ++} ++ ++/* Write NFC register */ ++static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ ++ out_be16(prv->regs + reg, val); ++} ++ ++/* Set bits in NFC register */ ++static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) ++{ ++ nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); ++} ++ ++/* Clear bits in NFC register */ ++static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) ++{ ++ nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); ++} ++ ++/* Invoke address cycle */ ++static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) ++{ ++ nfc_write(mtd, NFC_FLASH_ADDR, addr); ++ nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); ++ mpc5121_nfc_done(mtd); ++} ++ ++/* Invoke command cycle */ ++static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) ++{ ++ nfc_write(mtd, NFC_FLASH_CMD, cmd); ++ nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); ++ mpc5121_nfc_done(mtd); ++} ++ ++/* Send data from NFC buffers to NAND flash */ ++static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) ++{ ++ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); ++ nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); ++ mpc5121_nfc_done(mtd); ++} ++ ++/* Receive data from NAND flash */ ++static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) ++{ ++ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); ++ nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); ++ mpc5121_nfc_done(mtd); ++} ++ ++/* Receive ID from NAND flash */ ++static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) ++{ ++ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); ++ nfc_write(mtd, NFC_CONFIG2, NFC_ID); ++ mpc5121_nfc_done(mtd); ++} ++ ++/* Receive status from NAND flash */ ++static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) ++{ ++ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); ++ nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); ++ mpc5121_nfc_done(mtd); ++} ++ ++/* NFC interrupt handler */ ++static irqreturn_t mpc5121_nfc_irq(int irq, void *data) ++{ ++ struct mtd_info *mtd = data; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ ++ nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK); ++ wake_up(&prv->irq_waitq); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Wait for operation complete */ ++static void mpc5121_nfc_done(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ int rv; ++ ++ if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) { ++ nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK); ++ rv = wait_event_timeout(prv->irq_waitq, ++ (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT); ++ ++ if (!rv) ++ dev_warn(prv->dev, ++ "Timeout while waiting for interrupt.\n"); ++ } ++ ++ nfc_clear(mtd, NFC_CONFIG2, NFC_INT); ++} ++ ++/* Do address cycle(s) */ ++static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u32 pagemask = chip->pagemask; ++ ++ if (column != -1) { ++ mpc5121_nfc_send_addr(mtd, column); ++ if (mtd->writesize > 512) ++ mpc5121_nfc_send_addr(mtd, column >> 8); ++ } ++ ++ if (page != -1) { ++ do { ++ mpc5121_nfc_send_addr(mtd, page & 0xFF); ++ page >>= 8; ++ pagemask >>= 8; ++ } while (pagemask); ++ } ++} ++ ++/* Control chip select signals */ ++static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ if (chip < 0) { ++ nfc_clear(mtd, NFC_CONFIG1, NFC_CE); ++ return; ++ } ++ ++ nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); ++ nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & ++ NFC_ACTIVE_CS_MASK); ++ nfc_set(mtd, NFC_CONFIG1, NFC_CE); ++} ++ ++/* Init external chip select logic on ADS5121 board */ ++static int ads5121_chipselect_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ struct device_node *dn; ++ ++ dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld"); ++ if (dn) { ++ prv->csreg = of_iomap(dn, 0); ++ of_node_put(dn); ++ if (!prv->csreg) ++ return -ENOMEM; ++ ++ /* CPLD Register 9 controls NAND /CE Lines */ ++ prv->csreg += 9; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/* Control chips select signal on ADS5121 board */ ++static void ads5121_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand); ++ u8 v; ++ ++ v = in_8(prv->csreg); ++ v |= 0x0F; ++ ++ if (chip >= 0) { ++ mpc5121_nfc_select_chip(mtd, 0); ++ v &= ~(1 << chip); ++ } else ++ mpc5121_nfc_select_chip(mtd, -1); ++ ++ out_8(prv->csreg, v); ++} ++ ++/* Read NAND Ready/Busy signal */ ++static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) ++{ ++ /* ++ * NFC handles ready/busy signal internally. Therefore, this function ++ * always returns status as ready. ++ */ ++ return 1; ++} ++ ++/* Write command to NAND flash */ ++static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, ++ int column, int page) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ ++ prv->column = (column >= 0) ? column : 0; ++ prv->spareonly = 0; ++ ++ switch (command) { ++ case NAND_CMD_PAGEPROG: ++ mpc5121_nfc_send_prog_page(mtd); ++ break; ++ /* ++ * NFC does not support sub-page reads and writes, ++ * so emulate them using full page transfers. ++ */ ++ case NAND_CMD_READ0: ++ column = 0; ++ break; ++ ++ case NAND_CMD_READ1: ++ prv->column += 256; ++ command = NAND_CMD_READ0; ++ column = 0; ++ break; ++ ++ case NAND_CMD_READOOB: ++ prv->spareonly = 1; ++ command = NAND_CMD_READ0; ++ column = 0; ++ break; ++ ++ case NAND_CMD_SEQIN: ++ mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); ++ column = 0; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_READID: ++ case NAND_CMD_STATUS: ++ break; ++ ++ default: ++ return; ++ } ++ ++ mpc5121_nfc_send_cmd(mtd, command); ++ mpc5121_nfc_addr_cycle(mtd, column, page); ++ ++ switch (command) { ++ case NAND_CMD_READ0: ++ if (mtd->writesize > 512) ++ mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); ++ mpc5121_nfc_send_read_page(mtd); ++ break; ++ ++ case NAND_CMD_READID: ++ mpc5121_nfc_send_read_id(mtd); ++ break; ++ ++ case NAND_CMD_STATUS: ++ mpc5121_nfc_send_read_status(mtd); ++ if (chip->options & NAND_BUSWIDTH_16) ++ prv->column = 1; ++ else ++ prv->column = 0; ++ break; ++ } ++} ++ ++/* Copy data from/to NFC spare buffers. */ ++static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, ++ u8 *buffer, uint size, int wr) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand); ++ uint o, s, sbsize, blksize; ++ ++ /* ++ * NAND spare area is available through NFC spare buffers. ++ * The NFC divides spare area into (page_size / 512) chunks. ++ * Each chunk is placed into separate spare memory area, using ++ * first (spare_size / num_of_chunks) bytes of the buffer. ++ * ++ * For NAND device in which the spare area is not divided fully ++ * by the number of chunks, number of used bytes in each spare ++ * buffer is rounded down to the nearest even number of bytes, ++ * and all remaining bytes are added to the last used spare area. ++ * ++ * For more information read section 26.6.10 of MPC5121e ++ * Microcontroller Reference Manual, Rev. 3. ++ */ ++ ++ /* Calculate number of valid bytes in each spare buffer */ ++ sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; ++ ++ while (size) { ++ /* Calculate spare buffer number */ ++ s = offset / sbsize; ++ if (s > NFC_SPARE_BUFFERS - 1) ++ s = NFC_SPARE_BUFFERS - 1; ++ ++ /* ++ * Calculate offset to requested data block in selected spare ++ * buffer and its size. ++ */ ++ o = offset - (s * sbsize); ++ blksize = min(sbsize - o, size); ++ ++ if (wr) ++ memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, ++ buffer, blksize); ++ else ++ memcpy_fromio(buffer, ++ prv->regs + NFC_SPARE_AREA(s) + o, blksize); ++ ++ buffer += blksize; ++ offset += blksize; ++ size -= blksize; ++ }; ++} ++ ++/* Copy data from/to NFC main and spare buffers */ ++static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, ++ int wr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ uint c = prv->column; ++ uint l; ++ ++ /* Handle spare area access */ ++ if (prv->spareonly || c >= mtd->writesize) { ++ /* Calculate offset from beginning of spare area */ ++ if (c >= mtd->writesize) ++ c -= mtd->writesize; ++ ++ prv->column += len; ++ mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); ++ return; ++ } ++ ++ /* ++ * Handle main area access - limit copy length to prevent ++ * crossing main/spare boundary. ++ */ ++ l = min((uint)len, mtd->writesize - c); ++ prv->column += l; ++ ++ if (wr) ++ memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); ++ else ++ memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); ++ ++ /* Handle crossing main/spare boundary */ ++ if (l != len) { ++ buf += l; ++ len -= l; ++ mpc5121_nfc_buf_copy(mtd, buf, len, wr); ++ } ++} ++ ++/* Read data from NFC buffers */ ++static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ mpc5121_nfc_buf_copy(mtd, buf, len, 0); ++} ++ ++/* Write data to NFC buffers */ ++static void mpc5121_nfc_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); ++} ++ ++/* Read byte from NFC buffers */ ++static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) ++{ ++ u8 tmp; ++ ++ mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); ++ ++ return tmp; ++} ++ ++/* Read word from NFC buffers */ ++static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) ++{ ++ u16 tmp; ++ ++ mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); ++ ++ return tmp; ++} ++ ++/* ++ * Read NFC configuration from Reset Config Word ++ * ++ * NFC is configured during reset in basis of information stored ++ * in Reset Config Word. There is no other way to set NAND block ++ * size, spare size and bus width. ++ */ ++static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ struct mpc512x_reset_module *rm; ++ struct device_node *rmnode; ++ uint rcw_pagesize = 0; ++ uint rcw_sparesize = 0; ++ uint rcw_width; ++ uint rcwh; ++ uint romloc, ps; ++ int ret = 0; ++ ++ rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); ++ if (!rmnode) { ++ dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' " ++ "node in device tree!\n"); ++ return -ENODEV; ++ } ++ ++ rm = of_iomap(rmnode, 0); ++ if (!rm) { ++ dev_err(prv->dev, "Error mapping reset module node!\n"); ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ rcwh = in_be32(&rm->rcwhr); ++ ++ /* Bit 6: NFC bus width */ ++ rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; ++ ++ /* Bit 7: NFC Page/Spare size */ ++ ps = (rcwh >> 7) & 0x1; ++ ++ /* Bits [22:21]: ROM Location */ ++ romloc = (rcwh >> 21) & 0x3; ++ ++ /* Decode RCW bits */ ++ switch ((ps << 2) | romloc) { ++ case 0x00: ++ case 0x01: ++ rcw_pagesize = 512; ++ rcw_sparesize = 16; ++ break; ++ case 0x02: ++ case 0x03: ++ rcw_pagesize = 4096; ++ rcw_sparesize = 128; ++ break; ++ case 0x04: ++ case 0x05: ++ rcw_pagesize = 2048; ++ rcw_sparesize = 64; ++ break; ++ case 0x06: ++ case 0x07: ++ rcw_pagesize = 4096; ++ rcw_sparesize = 218; ++ break; ++ } ++ ++ mtd->writesize = rcw_pagesize; ++ mtd->oobsize = rcw_sparesize; ++ if (rcw_width == 2) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ dev_notice(prv->dev, "Configured for " ++ "%u-bit NAND, page size %u " ++ "with %u spare.\n", ++ rcw_width * 8, rcw_pagesize, ++ rcw_sparesize); ++ iounmap(rm); ++out: ++ of_node_put(rmnode); ++ return ret; ++} ++ ++/* Free driver resources */ ++static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); ++ ++ if (prv->clk) ++ clk_disable_unprepare(prv->clk); ++ ++ if (prv->csreg) ++ iounmap(prv->csreg); ++} ++ ++static int mpc5121_nfc_probe(struct platform_device *op) ++{ ++ struct device_node *dn = op->dev.of_node; ++ struct clk *clk; ++ struct device *dev = &op->dev; ++ struct mpc5121_nfc_prv *prv; ++ struct resource res; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ unsigned long regs_paddr, regs_size; ++ const __be32 *chips_no; ++ int resettime = 0; ++ int retval = 0; ++ int rev, len; ++ ++ /* ++ * Check SoC revision. This driver supports only NFC ++ * in MPC5121 revision 2 and MPC5123 revision 3. ++ */ ++ rev = (mfspr(SPRN_SVR) >> 4) & 0xF; ++ if ((rev != 2) && (rev != 3)) { ++ dev_err(dev, "SoC revision %u is not supported!\n", rev); ++ return -ENXIO; ++ } ++ ++ prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); ++ if (!prv) ++ return -ENOMEM; ++ ++ chip = &prv->chip; ++ mtd = nand_to_mtd(chip); ++ ++ mtd->dev.parent = dev; ++ nand_set_controller_data(chip, prv); ++ nand_set_flash_node(chip, dn); ++ prv->dev = dev; ++ ++ /* Read NFC configuration from Reset Config Word */ ++ retval = mpc5121_nfc_read_hw_config(mtd); ++ if (retval) { ++ dev_err(dev, "Unable to read NFC config!\n"); ++ return retval; ++ } ++ ++ prv->irq = irq_of_parse_and_map(dn, 0); ++ if (prv->irq == NO_IRQ) { ++ dev_err(dev, "Error mapping IRQ!\n"); ++ return -EINVAL; ++ } ++ ++ retval = of_address_to_resource(dn, 0, &res); ++ if (retval) { ++ dev_err(dev, "Error parsing memory region!\n"); ++ return retval; ++ } ++ ++ chips_no = of_get_property(dn, "chips", &len); ++ if (!chips_no || len != sizeof(*chips_no)) { ++ dev_err(dev, "Invalid/missing 'chips' property!\n"); ++ return -EINVAL; ++ } ++ ++ regs_paddr = res.start; ++ regs_size = resource_size(&res); ++ ++ if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { ++ dev_err(dev, "Error requesting memory region!\n"); ++ return -EBUSY; ++ } ++ ++ prv->regs = devm_ioremap(dev, regs_paddr, regs_size); ++ if (!prv->regs) { ++ dev_err(dev, "Error mapping memory region!\n"); ++ return -ENOMEM; ++ } ++ ++ mtd->name = "MPC5121 NAND"; ++ chip->dev_ready = mpc5121_nfc_dev_ready; ++ chip->cmdfunc = mpc5121_nfc_command; ++ chip->read_byte = mpc5121_nfc_read_byte; ++ chip->read_word = mpc5121_nfc_read_word; ++ chip->read_buf = mpc5121_nfc_read_buf; ++ chip->write_buf = mpc5121_nfc_write_buf; ++ chip->select_chip = mpc5121_nfc_select_chip; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ chip->bbt_options = NAND_BBT_USE_FLASH; ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ ++ /* Support external chip-select logic on ADS5121 board */ ++ if (of_machine_is_compatible("fsl,mpc5121ads")) { ++ retval = ads5121_chipselect_init(mtd); ++ if (retval) { ++ dev_err(dev, "Chipselect init error!\n"); ++ return retval; ++ } ++ ++ chip->select_chip = ads5121_select_chip; ++ } ++ ++ /* Enable NFC clock */ ++ clk = devm_clk_get(dev, "ipg"); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "Unable to acquire NFC clock!\n"); ++ retval = PTR_ERR(clk); ++ goto error; ++ } ++ retval = clk_prepare_enable(clk); ++ if (retval) { ++ dev_err(dev, "Unable to enable NFC clock!\n"); ++ goto error; ++ } ++ prv->clk = clk; ++ ++ /* Reset NAND Flash controller */ ++ nfc_set(mtd, NFC_CONFIG1, NFC_RESET); ++ while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { ++ if (resettime++ >= NFC_RESET_TIMEOUT) { ++ dev_err(dev, "Timeout while resetting NFC!\n"); ++ retval = -EINVAL; ++ goto error; ++ } ++ ++ udelay(1); ++ } ++ ++ /* Enable write to NFC memory */ ++ nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); ++ ++ /* Enable write to all NAND pages */ ++ nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); ++ nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); ++ nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); ++ ++ /* ++ * Setup NFC: ++ * - Big Endian transfers, ++ * - Interrupt after full page read/write. ++ */ ++ nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | ++ NFC_FULL_PAGE_INT); ++ ++ /* Set spare area size */ ++ nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); ++ ++ init_waitqueue_head(&prv->irq_waitq); ++ retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME, ++ mtd); ++ if (retval) { ++ dev_err(dev, "Error requesting IRQ!\n"); ++ goto error; ++ } ++ ++ /* Detect NAND chips */ ++ retval = nand_scan(mtd, be32_to_cpup(chips_no)); ++ if (retval) { ++ dev_err(dev, "NAND Flash not found !\n"); ++ goto error; ++ } ++ ++ /* Set erase block size */ ++ switch (mtd->erasesize / mtd->writesize) { ++ case 32: ++ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); ++ break; ++ ++ case 64: ++ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); ++ break; ++ ++ case 128: ++ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); ++ break; ++ ++ case 256: ++ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); ++ break; ++ ++ default: ++ dev_err(dev, "Unsupported NAND flash!\n"); ++ retval = -ENXIO; ++ goto error; ++ } ++ ++ dev_set_drvdata(dev, mtd); ++ ++ /* Register device in MTD */ ++ retval = mtd_device_register(mtd, NULL, 0); ++ if (retval) { ++ dev_err(dev, "Error adding MTD device!\n"); ++ goto error; ++ } ++ ++ return 0; ++error: ++ mpc5121_nfc_free(dev, mtd); ++ return retval; ++} ++ ++static int mpc5121_nfc_remove(struct platform_device *op) ++{ ++ struct device *dev = &op->dev; ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ ++ nand_release(mtd); ++ mpc5121_nfc_free(dev, mtd); ++ ++ return 0; ++} ++ ++static const struct of_device_id mpc5121_nfc_match[] = { ++ { .compatible = "fsl,mpc5121-nfc", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mpc5121_nfc_match); ++ ++static struct platform_driver mpc5121_nfc_driver = { ++ .probe = mpc5121_nfc_probe, ++ .remove = mpc5121_nfc_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = mpc5121_nfc_match, ++ }, ++}; ++ ++module_platform_driver(mpc5121_nfc_driver); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c +new file mode 100644 +index 00000000..c51d214 +--- /dev/null ++++ b/drivers/mtd/nand/raw/mtk_ecc.c +@@ -0,0 +1,544 @@ ++/* ++ * MTK ECC controller driver. ++ * Copyright (C) 2016 MediaTek Inc. ++ * Authors: Xiaolei Li ++ * Jorge Ramirez-Ortiz ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mtk_ecc.h" ++ ++#define ECC_IDLE_MASK BIT(0) ++#define ECC_IRQ_EN BIT(0) ++#define ECC_PG_IRQ_SEL BIT(1) ++#define ECC_OP_ENABLE (1) ++#define ECC_OP_DISABLE (0) ++ ++#define ECC_ENCCON (0x00) ++#define ECC_ENCCNFG (0x04) ++#define ECC_MODE_SHIFT (5) ++#define ECC_MS_SHIFT (16) ++#define ECC_ENCDIADDR (0x08) ++#define ECC_ENCIDLE (0x0C) ++#define ECC_ENCIRQ_EN (0x80) ++#define ECC_ENCIRQ_STA (0x84) ++#define ECC_DECCON (0x100) ++#define ECC_DECCNFG (0x104) ++#define DEC_EMPTY_EN BIT(31) ++#define DEC_CNFG_CORRECT (0x3 << 12) ++#define ECC_DECIDLE (0x10C) ++#define ECC_DECENUM0 (0x114) ++#define ECC_DECDONE (0x124) ++#define ECC_DECIRQ_EN (0x200) ++#define ECC_DECIRQ_STA (0x204) ++ ++#define ECC_TIMEOUT (500000) ++ ++#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) ++#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) ++#define ECC_IRQ_REG(op) ((op) == ECC_ENCODE ? \ ++ ECC_ENCIRQ_EN : ECC_DECIRQ_EN) ++ ++struct mtk_ecc_caps { ++ u32 err_mask; ++ const u8 *ecc_strength; ++ u8 num_ecc_strength; ++ u32 encode_parity_reg0; ++ int pg_irq_sel; ++}; ++ ++struct mtk_ecc { ++ struct device *dev; ++ const struct mtk_ecc_caps *caps; ++ void __iomem *regs; ++ struct clk *clk; ++ ++ struct completion done; ++ struct mutex lock; ++ u32 sectors; ++ ++ u8 *eccdata; ++}; ++ ++/* ecc strength that each IP supports */ ++static const u8 ecc_strength_mt2701[] = { ++ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, ++ 40, 44, 48, 52, 56, 60 ++}; ++ ++static const u8 ecc_strength_mt2712[] = { ++ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, ++ 40, 44, 48, 52, 56, 60, 68, 72, 80 ++}; ++ ++static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, ++ enum mtk_ecc_operation op) ++{ ++ struct device *dev = ecc->dev; ++ u32 val; ++ int ret; ++ ++ ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val, ++ val & ECC_IDLE_MASK, ++ 10, ECC_TIMEOUT); ++ if (ret) ++ dev_warn(dev, "%s NOT idle\n", ++ op == ECC_ENCODE ? "encoder" : "decoder"); ++} ++ ++static irqreturn_t mtk_ecc_irq(int irq, void *id) ++{ ++ struct mtk_ecc *ecc = id; ++ enum mtk_ecc_operation op; ++ u32 dec, enc; ++ ++ dec = readw(ecc->regs + ECC_DECIRQ_STA) & ECC_IRQ_EN; ++ if (dec) { ++ op = ECC_DECODE; ++ dec = readw(ecc->regs + ECC_DECDONE); ++ if (dec & ecc->sectors) { ++ /* ++ * Clear decode IRQ status once again to ensure that ++ * there will be no extra IRQ. ++ */ ++ readw(ecc->regs + ECC_DECIRQ_STA); ++ ecc->sectors = 0; ++ complete(&ecc->done); ++ } else { ++ return IRQ_HANDLED; ++ } ++ } else { ++ enc = readl(ecc->regs + ECC_ENCIRQ_STA) & ECC_IRQ_EN; ++ if (enc) { ++ op = ECC_ENCODE; ++ complete(&ecc->done); ++ } else { ++ return IRQ_NONE; ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config) ++{ ++ u32 ecc_bit, dec_sz, enc_sz; ++ u32 reg, i; ++ ++ for (i = 0; i < ecc->caps->num_ecc_strength; i++) { ++ if (ecc->caps->ecc_strength[i] == config->strength) ++ break; ++ } ++ ++ if (i == ecc->caps->num_ecc_strength) { ++ dev_err(ecc->dev, "invalid ecc strength %d\n", ++ config->strength); ++ return -EINVAL; ++ } ++ ++ ecc_bit = i; ++ ++ if (config->op == ECC_ENCODE) { ++ /* configure ECC encoder (in bits) */ ++ enc_sz = config->len << 3; ++ ++ reg = ecc_bit | (config->mode << ECC_MODE_SHIFT); ++ reg |= (enc_sz << ECC_MS_SHIFT); ++ writel(reg, ecc->regs + ECC_ENCCNFG); ++ ++ if (config->mode != ECC_NFI_MODE) ++ writel(lower_32_bits(config->addr), ++ ecc->regs + ECC_ENCDIADDR); ++ ++ } else { ++ /* configure ECC decoder (in bits) */ ++ dec_sz = (config->len << 3) + ++ config->strength * ECC_PARITY_BITS; ++ ++ reg = ecc_bit | (config->mode << ECC_MODE_SHIFT); ++ reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT; ++ reg |= DEC_EMPTY_EN; ++ writel(reg, ecc->regs + ECC_DECCNFG); ++ ++ if (config->sectors) ++ ecc->sectors = 1 << (config->sectors - 1); ++ } ++ ++ return 0; ++} ++ ++void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, ++ int sectors) ++{ ++ u32 offset, i, err; ++ u32 bitflips = 0; ++ ++ stats->corrected = 0; ++ stats->failed = 0; ++ ++ for (i = 0; i < sectors; i++) { ++ offset = (i >> 2) << 2; ++ err = readl(ecc->regs + ECC_DECENUM0 + offset); ++ err = err >> ((i % 4) * 8); ++ err &= ecc->caps->err_mask; ++ if (err == ecc->caps->err_mask) { ++ /* uncorrectable errors */ ++ stats->failed++; ++ continue; ++ } ++ ++ stats->corrected += err; ++ bitflips = max_t(u32, bitflips, err); ++ } ++ ++ stats->bitflips = bitflips; ++} ++EXPORT_SYMBOL(mtk_ecc_get_stats); ++ ++void mtk_ecc_release(struct mtk_ecc *ecc) ++{ ++ clk_disable_unprepare(ecc->clk); ++ put_device(ecc->dev); ++} ++EXPORT_SYMBOL(mtk_ecc_release); ++ ++static void mtk_ecc_hw_init(struct mtk_ecc *ecc) ++{ ++ mtk_ecc_wait_idle(ecc, ECC_ENCODE); ++ writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON); ++ ++ mtk_ecc_wait_idle(ecc, ECC_DECODE); ++ writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON); ++} ++ ++static struct mtk_ecc *mtk_ecc_get(struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_ecc *ecc; ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ get_device(&pdev->dev); ++ ecc = platform_get_drvdata(pdev); ++ clk_prepare_enable(ecc->clk); ++ mtk_ecc_hw_init(ecc); ++ ++ return ecc; ++} ++ ++struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) ++{ ++ struct mtk_ecc *ecc = NULL; ++ struct device_node *np; ++ ++ np = of_parse_phandle(of_node, "ecc-engine", 0); ++ if (np) { ++ ecc = mtk_ecc_get(np); ++ of_node_put(np); ++ } ++ ++ return ecc; ++} ++EXPORT_SYMBOL(of_mtk_ecc_get); ++ ++int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) ++{ ++ enum mtk_ecc_operation op = config->op; ++ u16 reg_val; ++ int ret; ++ ++ ret = mutex_lock_interruptible(&ecc->lock); ++ if (ret) { ++ dev_err(ecc->dev, "interrupted when attempting to lock\n"); ++ return ret; ++ } ++ ++ mtk_ecc_wait_idle(ecc, op); ++ ++ ret = mtk_ecc_config(ecc, config); ++ if (ret) { ++ mutex_unlock(&ecc->lock); ++ return ret; ++ } ++ ++ if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) { ++ init_completion(&ecc->done); ++ reg_val = ECC_IRQ_EN; ++ /* ++ * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it ++ * means this chip can only generate one ecc irq during page ++ * read / write. If is 0, generate one ecc irq each ecc step. ++ */ ++ if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE) ++ reg_val |= ECC_PG_IRQ_SEL; ++ writew(reg_val, ecc->regs + ECC_IRQ_REG(op)); ++ } ++ ++ writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mtk_ecc_enable); ++ ++void mtk_ecc_disable(struct mtk_ecc *ecc) ++{ ++ enum mtk_ecc_operation op = ECC_ENCODE; ++ ++ /* find out the running operation */ ++ if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE) ++ op = ECC_DECODE; ++ ++ /* disable it */ ++ mtk_ecc_wait_idle(ecc, op); ++ if (op == ECC_DECODE) ++ /* ++ * Clear decode IRQ status in case there is a timeout to wait ++ * decode IRQ. ++ */ ++ readw(ecc->regs + ECC_DECIRQ_STA); ++ writew(0, ecc->regs + ECC_IRQ_REG(op)); ++ writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op)); ++ ++ mutex_unlock(&ecc->lock); ++} ++EXPORT_SYMBOL(mtk_ecc_disable); ++ ++int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op) ++{ ++ int ret; ++ ++ ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500)); ++ if (!ret) { ++ dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n", ++ (op == ECC_ENCODE) ? "encoder" : "decoder"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(mtk_ecc_wait_done); ++ ++int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, ++ u8 *data, u32 bytes) ++{ ++ dma_addr_t addr; ++ u32 len; ++ int ret; ++ ++ addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE); ++ ret = dma_mapping_error(ecc->dev, addr); ++ if (ret) { ++ dev_err(ecc->dev, "dma mapping error\n"); ++ return -EINVAL; ++ } ++ ++ config->op = ECC_ENCODE; ++ config->addr = addr; ++ ret = mtk_ecc_enable(ecc, config); ++ if (ret) { ++ dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); ++ return ret; ++ } ++ ++ ret = mtk_ecc_wait_done(ecc, ECC_ENCODE); ++ if (ret) ++ goto timeout; ++ ++ mtk_ecc_wait_idle(ecc, ECC_ENCODE); ++ ++ /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ ++ len = (config->strength * ECC_PARITY_BITS + 7) >> 3; ++ ++ /* write the parity bytes generated by the ECC back to temp buffer */ ++ __ioread32_copy(ecc->eccdata, ++ ecc->regs + ecc->caps->encode_parity_reg0, ++ round_up(len, 4)); ++ ++ /* copy into possibly unaligned OOB region with actual length */ ++ memcpy(data + bytes, ecc->eccdata, len); ++timeout: ++ ++ dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); ++ mtk_ecc_disable(ecc); ++ ++ return ret; ++} ++EXPORT_SYMBOL(mtk_ecc_encode); ++ ++void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p) ++{ ++ const u8 *ecc_strength = ecc->caps->ecc_strength; ++ int i; ++ ++ for (i = 0; i < ecc->caps->num_ecc_strength; i++) { ++ if (*p <= ecc_strength[i]) { ++ if (!i) ++ *p = ecc_strength[i]; ++ else if (*p != ecc_strength[i]) ++ *p = ecc_strength[i - 1]; ++ return; ++ } ++ } ++ ++ *p = ecc_strength[ecc->caps->num_ecc_strength - 1]; ++} ++EXPORT_SYMBOL(mtk_ecc_adjust_strength); ++ ++static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { ++ .err_mask = 0x3f, ++ .ecc_strength = ecc_strength_mt2701, ++ .num_ecc_strength = 20, ++ .encode_parity_reg0 = 0x10, ++ .pg_irq_sel = 0, ++}; ++ ++static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { ++ .err_mask = 0x7f, ++ .ecc_strength = ecc_strength_mt2712, ++ .num_ecc_strength = 23, ++ .encode_parity_reg0 = 0x300, ++ .pg_irq_sel = 1, ++}; ++ ++static const struct of_device_id mtk_ecc_dt_match[] = { ++ { ++ .compatible = "mediatek,mt2701-ecc", ++ .data = &mtk_ecc_caps_mt2701, ++ }, { ++ .compatible = "mediatek,mt2712-ecc", ++ .data = &mtk_ecc_caps_mt2712, ++ }, ++ {}, ++}; ++ ++static int mtk_ecc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_ecc *ecc; ++ struct resource *res; ++ const struct of_device_id *of_ecc_id = NULL; ++ u32 max_eccdata_size; ++ int irq, ret; ++ ++ ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); ++ if (!ecc) ++ return -ENOMEM; ++ ++ of_ecc_id = of_match_device(mtk_ecc_dt_match, &pdev->dev); ++ if (!of_ecc_id) ++ return -ENODEV; ++ ++ ecc->caps = of_ecc_id->data; ++ ++ max_eccdata_size = ecc->caps->num_ecc_strength - 1; ++ max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; ++ max_eccdata_size = (max_eccdata_size * ECC_PARITY_BITS + 7) >> 3; ++ max_eccdata_size = round_up(max_eccdata_size, 4); ++ ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL); ++ if (!ecc->eccdata) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ecc->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ecc->regs)) { ++ dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs)); ++ return PTR_ERR(ecc->regs); ++ } ++ ++ ecc->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(ecc->clk)) { ++ dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk)); ++ return PTR_ERR(ecc->clk); ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "failed to get irq: %d\n", irq); ++ return irq; ++ } ++ ++ ret = dma_set_mask(dev, DMA_BIT_MASK(32)); ++ if (ret) { ++ dev_err(dev, "failed to set DMA mask\n"); ++ return ret; ++ } ++ ++ ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc); ++ if (ret) { ++ dev_err(dev, "failed to request irq\n"); ++ return -EINVAL; ++ } ++ ++ ecc->dev = dev; ++ mutex_init(&ecc->lock); ++ platform_set_drvdata(pdev, ecc); ++ dev_info(dev, "probed\n"); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int mtk_ecc_suspend(struct device *dev) ++{ ++ struct mtk_ecc *ecc = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(ecc->clk); ++ ++ return 0; ++} ++ ++static int mtk_ecc_resume(struct device *dev) ++{ ++ struct mtk_ecc *ecc = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(ecc->clk); ++ if (ret) { ++ dev_err(dev, "failed to enable clk\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume); ++#endif ++ ++MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match); ++ ++static struct platform_driver mtk_ecc_driver = { ++ .probe = mtk_ecc_probe, ++ .driver = { ++ .name = "mtk-ecc", ++ .of_match_table = of_match_ptr(mtk_ecc_dt_match), ++#ifdef CONFIG_PM_SLEEP ++ .pm = &mtk_ecc_pm_ops, ++#endif ++ }, ++}; ++ ++module_platform_driver(mtk_ecc_driver); ++ ++MODULE_AUTHOR("Xiaolei Li "); ++MODULE_DESCRIPTION("MTK Nand ECC Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h +new file mode 100644 +index 00000000..d245c14 +--- /dev/null ++++ b/drivers/mtd/nand/raw/mtk_ecc.h +@@ -0,0 +1,50 @@ ++/* ++ * MTK SDG1 ECC controller ++ * ++ * Copyright (c) 2016 Mediatek ++ * Authors: Xiaolei Li ++ * Jorge Ramirez-Ortiz ++ * 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 __DRIVERS_MTD_NAND_MTK_ECC_H__ ++#define __DRIVERS_MTD_NAND_MTK_ECC_H__ ++ ++#include ++ ++#define ECC_PARITY_BITS (14) ++ ++enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1}; ++enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE}; ++ ++struct device_node; ++struct mtk_ecc; ++ ++struct mtk_ecc_stats { ++ u32 corrected; ++ u32 bitflips; ++ u32 failed; ++}; ++ ++struct mtk_ecc_config { ++ enum mtk_ecc_operation op; ++ enum mtk_ecc_mode mode; ++ dma_addr_t addr; ++ u32 strength; ++ u32 sectors; ++ u32 len; ++}; ++ ++int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32); ++void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int); ++int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation); ++int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *); ++void mtk_ecc_disable(struct mtk_ecc *); ++void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p); ++ ++struct mtk_ecc *of_mtk_ecc_get(struct device_node *); ++void mtk_ecc_release(struct mtk_ecc *); ++ ++#endif +diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c +new file mode 100644 +index 00000000..812f95b +--- /dev/null ++++ b/drivers/mtd/nand/raw/mtk_nand.c +@@ -0,0 +1,1576 @@ ++/* ++ * MTK NAND Flash controller driver. ++ * Copyright (C) 2016 MediaTek Inc. ++ * Authors: Xiaolei Li ++ * Jorge Ramirez-Ortiz ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mtk_ecc.h" ++ ++/* NAND controller register definition */ ++#define NFI_CNFG (0x00) ++#define CNFG_AHB BIT(0) ++#define CNFG_READ_EN BIT(1) ++#define CNFG_DMA_BURST_EN BIT(2) ++#define CNFG_BYTE_RW BIT(6) ++#define CNFG_HW_ECC_EN BIT(8) ++#define CNFG_AUTO_FMT_EN BIT(9) ++#define CNFG_OP_CUST (6 << 12) ++#define NFI_PAGEFMT (0x04) ++#define PAGEFMT_FDM_ECC_SHIFT (12) ++#define PAGEFMT_FDM_SHIFT (8) ++#define PAGEFMT_SEC_SEL_512 BIT(2) ++#define PAGEFMT_512_2K (0) ++#define PAGEFMT_2K_4K (1) ++#define PAGEFMT_4K_8K (2) ++#define PAGEFMT_8K_16K (3) ++/* NFI control */ ++#define NFI_CON (0x08) ++#define CON_FIFO_FLUSH BIT(0) ++#define CON_NFI_RST BIT(1) ++#define CON_BRD BIT(8) /* burst read */ ++#define CON_BWR BIT(9) /* burst write */ ++#define CON_SEC_SHIFT (12) ++/* Timming control register */ ++#define NFI_ACCCON (0x0C) ++#define NFI_INTR_EN (0x10) ++#define INTR_AHB_DONE_EN BIT(6) ++#define NFI_INTR_STA (0x14) ++#define NFI_CMD (0x20) ++#define NFI_ADDRNOB (0x30) ++#define NFI_COLADDR (0x34) ++#define NFI_ROWADDR (0x38) ++#define NFI_STRDATA (0x40) ++#define STAR_EN (1) ++#define STAR_DE (0) ++#define NFI_CNRNB (0x44) ++#define NFI_DATAW (0x50) ++#define NFI_DATAR (0x54) ++#define NFI_PIO_DIRDY (0x58) ++#define PIO_DI_RDY (0x01) ++#define NFI_STA (0x60) ++#define STA_CMD BIT(0) ++#define STA_ADDR BIT(1) ++#define STA_BUSY BIT(8) ++#define STA_EMP_PAGE BIT(12) ++#define NFI_FSM_CUSTDATA (0xe << 16) ++#define NFI_FSM_MASK (0xf << 16) ++#define NFI_ADDRCNTR (0x70) ++#define CNTR_MASK GENMASK(16, 12) ++#define ADDRCNTR_SEC_SHIFT (12) ++#define ADDRCNTR_SEC(val) \ ++ (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT) ++#define NFI_STRADDR (0x80) ++#define NFI_BYTELEN (0x84) ++#define NFI_CSEL (0x90) ++#define NFI_FDML(x) (0xA0 + (x) * sizeof(u32) * 2) ++#define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2) ++#define NFI_FDM_MAX_SIZE (8) ++#define NFI_FDM_MIN_SIZE (1) ++#define NFI_MASTER_STA (0x224) ++#define MASTER_STA_MASK (0x0FFF) ++#define NFI_EMPTY_THRESH (0x23C) ++ ++#define MTK_NAME "mtk-nand" ++#define KB(x) ((x) * 1024UL) ++#define MB(x) (KB(x) * 1024UL) ++ ++#define MTK_TIMEOUT (500000) ++#define MTK_RESET_TIMEOUT (1000000) ++#define MTK_MAX_SECTOR (16) ++#define MTK_NAND_MAX_NSELS (2) ++#define MTK_NFC_MIN_SPARE (16) ++#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ ++ ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ ++ (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) ++ ++struct mtk_nfc_caps { ++ const u8 *spare_size; ++ u8 num_spare_size; ++ u8 pageformat_spare_shift; ++ u8 nfi_clk_div; ++}; ++ ++struct mtk_nfc_bad_mark_ctl { ++ void (*bm_swap)(struct mtd_info *, u8 *buf, int raw); ++ u32 sec; ++ u32 pos; ++}; ++ ++/* ++ * FDM: region used to store free OOB data ++ */ ++struct mtk_nfc_fdm { ++ u32 reg_size; ++ u32 ecc_size; ++}; ++ ++struct mtk_nfc_nand_chip { ++ struct list_head node; ++ struct nand_chip nand; ++ ++ struct mtk_nfc_bad_mark_ctl bad_mark; ++ struct mtk_nfc_fdm fdm; ++ u32 spare_per_sector; ++ ++ int nsels; ++ u8 sels[0]; ++ /* nothing after this field */ ++}; ++ ++struct mtk_nfc_clk { ++ struct clk *nfi_clk; ++ struct clk *pad_clk; ++}; ++ ++struct mtk_nfc { ++ struct nand_controller controller; ++ struct mtk_ecc_config ecc_cfg; ++ struct mtk_nfc_clk clk; ++ struct mtk_ecc *ecc; ++ ++ struct device *dev; ++ const struct mtk_nfc_caps *caps; ++ void __iomem *regs; ++ ++ struct completion done; ++ struct list_head chips; ++ ++ u8 *buffer; ++}; ++ ++/* ++ * supported spare size of each IP. ++ * order should be the same with the spare size bitfiled defination of ++ * register NFI_PAGEFMT. ++ */ ++static const u8 spare_size_mt2701[] = { ++ 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 63, 64 ++}; ++ ++static const u8 spare_size_mt2712[] = { ++ 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67, ++ 74 ++}; ++ ++static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand) ++{ ++ return container_of(nand, struct mtk_nfc_nand_chip, nand); ++} ++ ++static inline u8 *data_ptr(struct nand_chip *chip, const u8 *p, int i) ++{ ++ return (u8 *)p + i * chip->ecc.size; ++} ++ ++static inline u8 *oob_ptr(struct nand_chip *chip, int i) ++{ ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ u8 *poi; ++ ++ /* map the sector's FDM data to free oob: ++ * the beginning of the oob area stores the FDM data of bad mark sectors ++ */ ++ ++ if (i < mtk_nand->bad_mark.sec) ++ poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size; ++ else if (i == mtk_nand->bad_mark.sec) ++ poi = chip->oob_poi; ++ else ++ poi = chip->oob_poi + i * mtk_nand->fdm.reg_size; ++ ++ return poi; ++} ++ ++static inline int mtk_data_len(struct nand_chip *chip) ++{ ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ ++ return chip->ecc.size + mtk_nand->spare_per_sector; ++} ++ ++static inline u8 *mtk_data_ptr(struct nand_chip *chip, int i) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ ++ return nfc->buffer + i * mtk_data_len(chip); ++} ++ ++static inline u8 *mtk_oob_ptr(struct nand_chip *chip, int i) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ ++ return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size; ++} ++ ++static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg) ++{ ++ writel(val, nfc->regs + reg); ++} ++ ++static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg) ++{ ++ writew(val, nfc->regs + reg); ++} ++ ++static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg) ++{ ++ writeb(val, nfc->regs + reg); ++} ++ ++static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg) ++{ ++ return readl_relaxed(nfc->regs + reg); ++} ++ ++static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg) ++{ ++ return readw_relaxed(nfc->regs + reg); ++} ++ ++static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg) ++{ ++ return readb_relaxed(nfc->regs + reg); ++} ++ ++static void mtk_nfc_hw_reset(struct mtk_nfc *nfc) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ /* reset all registers and force the NFI master to terminate */ ++ nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); ++ ++ /* wait for the master to finish the last transaction */ ++ ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val, ++ !(val & MASTER_STA_MASK), 50, ++ MTK_RESET_TIMEOUT); ++ if (ret) ++ dev_warn(dev, "master active in reset [0x%x] = 0x%x\n", ++ NFI_MASTER_STA, val); ++ ++ /* ensure any status register affected by the NFI master is reset */ ++ nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); ++ nfi_writew(nfc, STAR_DE, NFI_STRDATA); ++} ++ ++static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ nfi_writel(nfc, command, NFI_CMD); ++ ++ ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, ++ !(val & STA_CMD), 10, MTK_TIMEOUT); ++ if (ret) { ++ dev_warn(dev, "nfi core timed out entering command mode\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ nfi_writel(nfc, addr, NFI_COLADDR); ++ nfi_writel(nfc, 0, NFI_ROWADDR); ++ nfi_writew(nfc, 1, NFI_ADDRNOB); ++ ++ ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, ++ !(val & STA_ADDR), 10, MTK_TIMEOUT); ++ if (ret) { ++ dev_warn(dev, "nfi core timed out entering address mode\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ u32 fmt, spare, i; ++ ++ if (!mtd->writesize) ++ return 0; ++ ++ spare = mtk_nand->spare_per_sector; ++ ++ switch (mtd->writesize) { ++ case 512: ++ fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512; ++ break; ++ case KB(2): ++ if (chip->ecc.size == 512) ++ fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512; ++ else ++ fmt = PAGEFMT_512_2K; ++ break; ++ case KB(4): ++ if (chip->ecc.size == 512) ++ fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512; ++ else ++ fmt = PAGEFMT_2K_4K; ++ break; ++ case KB(8): ++ if (chip->ecc.size == 512) ++ fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512; ++ else ++ fmt = PAGEFMT_4K_8K; ++ break; ++ case KB(16): ++ fmt = PAGEFMT_8K_16K; ++ break; ++ default: ++ dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize); ++ return -EINVAL; ++ } ++ ++ /* ++ * the hardware will double the value for this eccsize, so we need to ++ * halve it ++ */ ++ if (chip->ecc.size == 1024) ++ spare >>= 1; ++ ++ for (i = 0; i < nfc->caps->num_spare_size; i++) { ++ if (nfc->caps->spare_size[i] == spare) ++ break; ++ } ++ ++ if (i == nfc->caps->num_spare_size) { ++ dev_err(nfc->dev, "invalid spare size %d\n", spare); ++ return -EINVAL; ++ } ++ ++ fmt |= i << nfc->caps->pageformat_spare_shift; ++ ++ fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT; ++ fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT; ++ nfi_writel(nfc, fmt, NFI_PAGEFMT); ++ ++ nfc->ecc_cfg.strength = chip->ecc.strength; ++ nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size; ++ ++ return 0; ++} ++ ++static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct mtk_nfc *nfc = nand_get_controller_data(nand); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand); ++ ++ if (chip < 0) ++ return; ++ ++ mtk_nfc_hw_runtime_config(mtd); ++ ++ nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL); ++} ++ ++static int mtk_nfc_dev_ready(struct mtd_info *mtd) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); ++ ++ if (nfi_readl(nfc, NFI_STA) & STA_BUSY) ++ return 0; ++ ++ return 1; ++} ++ ++static void mtk_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); ++ ++ if (ctrl & NAND_ALE) { ++ mtk_nfc_send_address(nfc, dat); ++ } else if (ctrl & NAND_CLE) { ++ mtk_nfc_hw_reset(nfc); ++ ++ nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG); ++ mtk_nfc_send_command(nfc, dat); ++ } ++} ++ ++static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc) ++{ ++ int rc; ++ u8 val; ++ ++ rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val, ++ val & PIO_DI_RDY, 10, MTK_TIMEOUT); ++ if (rc < 0) ++ dev_err(nfc->dev, "data not ready\n"); ++} ++ ++static inline u8 mtk_nfc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ u32 reg; ++ ++ /* after each byte read, the NFI_STA reg is reset by the hardware */ ++ reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; ++ if (reg != NFI_FSM_CUSTDATA) { ++ reg = nfi_readw(nfc, NFI_CNFG); ++ reg |= CNFG_BYTE_RW | CNFG_READ_EN; ++ nfi_writew(nfc, reg, NFI_CNFG); ++ ++ /* ++ * set to max sector to allow the HW to continue reading over ++ * unaligned accesses ++ */ ++ reg = (MTK_MAX_SECTOR << CON_SEC_SHIFT) | CON_BRD; ++ nfi_writel(nfc, reg, NFI_CON); ++ ++ /* trigger to fetch data */ ++ nfi_writew(nfc, STAR_EN, NFI_STRDATA); ++ } ++ ++ mtk_nfc_wait_ioready(nfc); ++ ++ return nfi_readb(nfc, NFI_DATAR); ++} ++ ++static void mtk_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ buf[i] = mtk_nfc_read_byte(mtd); ++} ++ ++static void mtk_nfc_write_byte(struct mtd_info *mtd, u8 byte) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); ++ u32 reg; ++ ++ reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; ++ ++ if (reg != NFI_FSM_CUSTDATA) { ++ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW; ++ nfi_writew(nfc, reg, NFI_CNFG); ++ ++ reg = MTK_MAX_SECTOR << CON_SEC_SHIFT | CON_BWR; ++ nfi_writel(nfc, reg, NFI_CON); ++ ++ nfi_writew(nfc, STAR_EN, NFI_STRDATA); ++ } ++ ++ mtk_nfc_wait_ioready(nfc); ++ nfi_writeb(nfc, byte, NFI_DATAW); ++} ++ ++static void mtk_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ mtk_nfc_write_byte(mtd, buf[i]); ++} ++ ++static int mtk_nfc_setup_data_interface(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); ++ const struct nand_sdr_timings *timings; ++ u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; ++ ++ timings = nand_get_sdr_timings(conf); ++ if (IS_ERR(timings)) ++ return -ENOTSUPP; ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ rate = clk_get_rate(nfc->clk.nfi_clk); ++ /* There is a frequency divider in some IPs */ ++ rate /= nfc->caps->nfi_clk_div; ++ ++ /* turn clock rate into KHZ */ ++ rate /= 1000; ++ ++ tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000; ++ tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000); ++ tpoecs &= 0xf; ++ ++ tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000; ++ tprecs = DIV_ROUND_UP(tprecs * rate, 1000000); ++ tprecs &= 0x3f; ++ ++ /* sdr interface has no tCR which means CE# low to RE# low */ ++ tc2r = 0; ++ ++ tw2r = timings->tWHR_min / 1000; ++ tw2r = DIV_ROUND_UP(tw2r * rate, 1000000); ++ tw2r = DIV_ROUND_UP(tw2r - 1, 2); ++ tw2r &= 0xf; ++ ++ twh = max(timings->tREH_min, timings->tWH_min) / 1000; ++ twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; ++ twh &= 0xf; ++ ++ twst = timings->tWP_min / 1000; ++ twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; ++ twst &= 0xf; ++ ++ trlt = max(timings->tREA_max, timings->tRP_min) / 1000; ++ trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; ++ trlt &= 0xf; ++ ++ /* ++ * ACCON: access timing control register ++ * ------------------------------------- ++ * 31:28: tpoecs, minimum required time for CS post pulling down after ++ * accessing the device ++ * 27:22: tprecs, minimum required time for CS pre pulling down before ++ * accessing the device ++ * 21:16: tc2r, minimum required time from NCEB low to NREB low ++ * 15:12: tw2r, minimum required time from NWEB high to NREB low. ++ * 11:08: twh, write enable hold time ++ * 07:04: twst, write wait states ++ * 03:00: trlt, read wait states ++ */ ++ trlt = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); ++ nfi_writel(nfc, trlt, NFI_ACCCON); ++ ++ return 0; ++} ++ ++static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ int size = chip->ecc.size + mtk_nand->fdm.reg_size; ++ ++ nfc->ecc_cfg.mode = ECC_DMA_MODE; ++ nfc->ecc_cfg.op = ECC_ENCODE; ++ ++ return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size); ++} ++ ++static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, u8 *b, int c) ++{ ++ /* nop */ ++} ++ ++static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, u8 *buf, int raw) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip); ++ u32 bad_pos = nand->bad_mark.pos; ++ ++ if (raw) ++ bad_pos += nand->bad_mark.sec * mtk_data_len(chip); ++ else ++ bad_pos += nand->bad_mark.sec * chip->ecc.size; ++ ++ swap(chip->oob_poi[0], buf[bad_pos]); ++} ++ ++static int mtk_nfc_format_subpage(struct mtd_info *mtd, u32 offset, ++ u32 len, const u8 *buf) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; ++ u32 start, end; ++ int i, ret; ++ ++ start = offset / chip->ecc.size; ++ end = DIV_ROUND_UP(offset + len, chip->ecc.size); ++ ++ memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); ++ for (i = 0; i < chip->ecc.steps; i++) { ++ memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), ++ chip->ecc.size); ++ ++ if (start > i || i >= end) ++ continue; ++ ++ if (i == mtk_nand->bad_mark.sec) ++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); ++ ++ memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); ++ ++ /* program the CRC back to the OOB */ ++ ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i)); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void mtk_nfc_format_page(struct mtd_info *mtd, const u8 *buf) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; ++ u32 i; ++ ++ memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); ++ for (i = 0; i < chip->ecc.steps; i++) { ++ if (buf) ++ memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), ++ chip->ecc.size); ++ ++ if (i == mtk_nand->bad_mark.sec) ++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); ++ ++ memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); ++ } ++} ++ ++static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start, ++ u32 sectors) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; ++ u32 vall, valm; ++ u8 *oobptr; ++ int i, j; ++ ++ for (i = 0; i < sectors; i++) { ++ oobptr = oob_ptr(chip, start + i); ++ vall = nfi_readl(nfc, NFI_FDML(i)); ++ valm = nfi_readl(nfc, NFI_FDMM(i)); ++ ++ for (j = 0; j < fdm->reg_size; j++) ++ oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); ++ } ++} ++ ++static inline void mtk_nfc_write_fdm(struct nand_chip *chip) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; ++ u32 vall, valm; ++ u8 *oobptr; ++ int i, j; ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ oobptr = oob_ptr(chip, i); ++ vall = 0; ++ valm = 0; ++ for (j = 0; j < 8; j++) { ++ if (j < 4) ++ vall |= (j < fdm->reg_size ? oobptr[j] : 0xff) ++ << (j * 8); ++ else ++ valm |= (j < fdm->reg_size ? oobptr[j] : 0xff) ++ << ((j - 4) * 8); ++ } ++ nfi_writel(nfc, vall, NFI_FDML(i)); ++ nfi_writel(nfc, valm, NFI_FDMM(i)); ++ } ++} ++ ++static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const u8 *buf, int page, int len) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct device *dev = nfc->dev; ++ dma_addr_t addr; ++ u32 reg; ++ int ret; ++ ++ addr = dma_map_single(dev, (void *)buf, len, DMA_TO_DEVICE); ++ ret = dma_mapping_error(nfc->dev, addr); ++ if (ret) { ++ dev_err(nfc->dev, "dma mapping error\n"); ++ return -EINVAL; ++ } ++ ++ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN; ++ nfi_writew(nfc, reg, NFI_CNFG); ++ ++ nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON); ++ nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); ++ nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); ++ ++ init_completion(&nfc->done); ++ ++ reg = nfi_readl(nfc, NFI_CON) | CON_BWR; ++ nfi_writel(nfc, reg, NFI_CON); ++ nfi_writew(nfc, STAR_EN, NFI_STRDATA); ++ ++ ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); ++ if (!ret) { ++ dev_err(dev, "program ahb done timeout\n"); ++ nfi_writew(nfc, 0, NFI_INTR_EN); ++ ret = -ETIMEDOUT; ++ goto timeout; ++ } ++ ++ ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg, ++ ADDRCNTR_SEC(reg) >= chip->ecc.steps, ++ 10, MTK_TIMEOUT); ++ if (ret) ++ dev_err(dev, "hwecc write timeout\n"); ++ ++timeout: ++ ++ dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE); ++ nfi_writel(nfc, 0, NFI_CON); ++ ++ return ret; ++} ++ ++static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const u8 *buf, int page, int raw) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ size_t len; ++ const u8 *bufpoi; ++ u32 reg; ++ int ret; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ if (!raw) { ++ /* OOB => FDM: from register, ECC: from HW */ ++ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN; ++ nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG); ++ ++ nfc->ecc_cfg.op = ECC_ENCODE; ++ nfc->ecc_cfg.mode = ECC_NFI_MODE; ++ ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); ++ if (ret) { ++ /* clear NFI config */ ++ reg = nfi_readw(nfc, NFI_CNFG); ++ reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); ++ nfi_writew(nfc, reg, NFI_CNFG); ++ ++ return ret; ++ } ++ ++ memcpy(nfc->buffer, buf, mtd->writesize); ++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw); ++ bufpoi = nfc->buffer; ++ ++ /* write OOB into the FDM registers (OOB area in MTK NAND) */ ++ mtk_nfc_write_fdm(chip); ++ } else { ++ bufpoi = buf; ++ } ++ ++ len = mtd->writesize + (raw ? mtd->oobsize : 0); ++ ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len); ++ ++ if (!raw) ++ mtk_ecc_disable(nfc->ecc); ++ ++ if (ret) ++ return ret; ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, const u8 *buf, ++ int oob_on, int page) ++{ ++ return mtk_nfc_write_page(mtd, chip, buf, page, 0); ++} ++ ++static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ const u8 *buf, int oob_on, int pg) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ ++ mtk_nfc_format_page(mtd, buf); ++ return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1); ++} ++ ++static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, u32 offset, ++ u32 data_len, const u8 *buf, ++ int oob_on, int page) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ int ret; ++ ++ ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf); ++ if (ret < 0) ++ return ret; ++ ++ /* use the data in the private buffer (now with FDM and CRC) */ ++ return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1); ++} ++ ++static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page); ++} ++ ++static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_ecc_stats stats; ++ int rc, i; ++ ++ rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE; ++ if (rc) { ++ memset(buf, 0xff, sectors * chip->ecc.size); ++ for (i = 0; i < sectors; i++) ++ memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size); ++ return 0; ++ } ++ ++ mtk_ecc_get_stats(nfc->ecc, &stats, sectors); ++ mtd->ecc_stats.corrected += stats.corrected; ++ mtd->ecc_stats.failed += stats.failed; ++ ++ return stats.bitflips; ++} ++ ++static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, ++ u32 data_offs, u32 readlen, ++ u8 *bufpoi, int page, int raw) ++{ ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ u32 spare = mtk_nand->spare_per_sector; ++ u32 column, sectors, start, end, reg; ++ dma_addr_t addr; ++ int bitflips; ++ size_t len; ++ u8 *buf; ++ int rc; ++ ++ start = data_offs / chip->ecc.size; ++ end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); ++ ++ sectors = end - start; ++ column = start * (chip->ecc.size + spare); ++ ++ len = sectors * chip->ecc.size + (raw ? sectors * spare : 0); ++ buf = bufpoi + start * chip->ecc.size; ++ ++ nand_read_page_op(chip, page, column, NULL, 0); ++ ++ addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE); ++ rc = dma_mapping_error(nfc->dev, addr); ++ if (rc) { ++ dev_err(nfc->dev, "dma mapping error\n"); ++ ++ return -EINVAL; ++ } ++ ++ reg = nfi_readw(nfc, NFI_CNFG); ++ reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB; ++ if (!raw) { ++ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN; ++ nfi_writew(nfc, reg, NFI_CNFG); ++ ++ nfc->ecc_cfg.mode = ECC_NFI_MODE; ++ nfc->ecc_cfg.sectors = sectors; ++ nfc->ecc_cfg.op = ECC_DECODE; ++ rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); ++ if (rc) { ++ dev_err(nfc->dev, "ecc enable\n"); ++ /* clear NFI_CNFG */ ++ reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN | ++ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); ++ nfi_writew(nfc, reg, NFI_CNFG); ++ dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); ++ ++ return rc; ++ } ++ } else { ++ nfi_writew(nfc, reg, NFI_CNFG); ++ } ++ ++ nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON); ++ nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); ++ nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); ++ ++ init_completion(&nfc->done); ++ reg = nfi_readl(nfc, NFI_CON) | CON_BRD; ++ nfi_writel(nfc, reg, NFI_CON); ++ nfi_writew(nfc, STAR_EN, NFI_STRDATA); ++ ++ rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); ++ if (!rc) ++ dev_warn(nfc->dev, "read ahb/dma done timeout\n"); ++ ++ rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg, ++ ADDRCNTR_SEC(reg) >= sectors, 10, ++ MTK_TIMEOUT); ++ if (rc < 0) { ++ dev_err(nfc->dev, "subpage done timeout\n"); ++ bitflips = -EIO; ++ } else { ++ bitflips = 0; ++ if (!raw) { ++ rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); ++ bitflips = rc < 0 ? -ETIMEDOUT : ++ mtk_nfc_update_ecc_stats(mtd, buf, sectors); ++ mtk_nfc_read_fdm(chip, start, sectors); ++ } ++ } ++ ++ dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); ++ ++ if (raw) ++ goto done; ++ ++ mtk_ecc_disable(nfc->ecc); ++ ++ if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec) ++ mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw); ++done: ++ nfi_writel(nfc, 0, NFI_CON); ++ ++ return bitflips; ++} ++ ++static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, u32 off, ++ u32 len, u8 *p, int pg) ++{ ++ return mtk_nfc_read_subpage(mtd, chip, off, len, p, pg, 0); ++} ++ ++static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *p, ++ int oob_on, int pg) ++{ ++ return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0); ++} ++ ++static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ u8 *buf, int oob_on, int page) ++{ ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc *nfc = nand_get_controller_data(chip); ++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; ++ int i, ret; ++ ++ memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); ++ ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer, ++ page, 1); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size); ++ ++ if (i == mtk_nand->bad_mark.sec) ++ mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); ++ ++ if (buf) ++ memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i), ++ chip->ecc.size); ++ } ++ ++ return ret; ++} ++ ++static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page); ++} ++ ++static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc) ++{ ++ /* ++ * CNRNB: nand ready/busy register ++ * ------------------------------- ++ * 7:4: timeout register for polling the NAND busy/ready signal ++ * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles. ++ */ ++ nfi_writew(nfc, 0xf1, NFI_CNRNB); ++ nfi_writel(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT); ++ ++ mtk_nfc_hw_reset(nfc); ++ ++ nfi_readl(nfc, NFI_INTR_STA); ++ nfi_writel(nfc, 0, NFI_INTR_EN); ++} ++ ++static irqreturn_t mtk_nfc_irq(int irq, void *id) ++{ ++ struct mtk_nfc *nfc = id; ++ u16 sta, ien; ++ ++ sta = nfi_readw(nfc, NFI_INTR_STA); ++ ien = nfi_readw(nfc, NFI_INTR_EN); ++ ++ if (!(sta & ien)) ++ return IRQ_NONE; ++ ++ nfi_writew(nfc, ~sta & ien, NFI_INTR_EN); ++ complete(&nfc->done); ++ ++ return IRQ_HANDLED; ++} ++ ++static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk) ++{ ++ int ret; ++ ++ ret = clk_prepare_enable(clk->nfi_clk); ++ if (ret) { ++ dev_err(dev, "failed to enable nfi clk\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(clk->pad_clk); ++ if (ret) { ++ dev_err(dev, "failed to enable pad clk\n"); ++ clk_disable_unprepare(clk->nfi_clk); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk) ++{ ++ clk_disable_unprepare(clk->nfi_clk); ++ clk_disable_unprepare(clk->pad_clk); ++} ++ ++static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oob_region) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; ++ u32 eccsteps; ++ ++ eccsteps = mtd->writesize / chip->ecc.size; ++ ++ if (section >= eccsteps) ++ return -ERANGE; ++ ++ oob_region->length = fdm->reg_size - fdm->ecc_size; ++ oob_region->offset = section * fdm->reg_size + fdm->ecc_size; ++ ++ return 0; ++} ++ ++static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oob_region) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); ++ u32 eccsteps; ++ ++ if (section) ++ return -ERANGE; ++ ++ eccsteps = mtd->writesize / chip->ecc.size; ++ oob_region->offset = mtk_nand->fdm.reg_size * eccsteps; ++ oob_region->length = mtd->oobsize - oob_region->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = { ++ .free = mtk_nfc_ooblayout_free, ++ .ecc = mtk_nfc_ooblayout_ecc, ++}; ++ ++static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand); ++ u32 ecc_bytes; ++ ++ ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8); ++ ++ fdm->reg_size = chip->spare_per_sector - ecc_bytes; ++ if (fdm->reg_size > NFI_FDM_MAX_SIZE) ++ fdm->reg_size = NFI_FDM_MAX_SIZE; ++ ++ /* bad block mark storage */ ++ fdm->ecc_size = 1; ++} ++ ++static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl, ++ struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ ++ if (mtd->writesize == 512) { ++ bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap; ++ } else { ++ bm_ctl->bm_swap = mtk_nfc_bad_mark_swap; ++ bm_ctl->sec = mtd->writesize / mtk_data_len(nand); ++ bm_ctl->pos = mtd->writesize % mtk_data_len(nand); ++ } ++} ++ ++static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct mtk_nfc *nfc = nand_get_controller_data(nand); ++ const u8 *spare = nfc->caps->spare_size; ++ u32 eccsteps, i, closest_spare = 0; ++ ++ eccsteps = mtd->writesize / nand->ecc.size; ++ *sps = mtd->oobsize / eccsteps; ++ ++ if (nand->ecc.size == 1024) ++ *sps >>= 1; ++ ++ if (*sps < MTK_NFC_MIN_SPARE) ++ return -EINVAL; ++ ++ for (i = 0; i < nfc->caps->num_spare_size; i++) { ++ if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) { ++ closest_spare = i; ++ if (*sps == spare[i]) ++ break; ++ } ++ } ++ ++ *sps = spare[closest_spare]; ++ ++ if (nand->ecc.size == 1024) ++ *sps <<= 1; ++ ++ return 0; ++} ++ ++static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct mtk_nfc *nfc = nand_get_controller_data(nand); ++ u32 spare; ++ int free, ret; ++ ++ /* support only ecc hw mode */ ++ if (nand->ecc.mode != NAND_ECC_HW) { ++ dev_err(dev, "ecc.mode not supported\n"); ++ return -EINVAL; ++ } ++ ++ /* if optional dt settings not present */ ++ if (!nand->ecc.size || !nand->ecc.strength) { ++ /* use datasheet requirements */ ++ nand->ecc.strength = nand->ecc_strength_ds; ++ nand->ecc.size = nand->ecc_step_ds; ++ ++ /* ++ * align eccstrength and eccsize ++ * this controller only supports 512 and 1024 sizes ++ */ ++ if (nand->ecc.size < 1024) { ++ if (mtd->writesize > 512) { ++ nand->ecc.size = 1024; ++ nand->ecc.strength <<= 1; ++ } else { ++ nand->ecc.size = 512; ++ } ++ } else { ++ nand->ecc.size = 1024; ++ } ++ ++ ret = mtk_nfc_set_spare_per_sector(&spare, mtd); ++ if (ret) ++ return ret; ++ ++ /* calculate oob bytes except ecc parity data */ ++ free = ((nand->ecc.strength * ECC_PARITY_BITS) + 7) >> 3; ++ free = spare - free; ++ ++ /* ++ * enhance ecc strength if oob left is bigger than max FDM size ++ * or reduce ecc strength if oob size is not enough for ecc ++ * parity data. ++ */ ++ if (free > NFI_FDM_MAX_SIZE) { ++ spare -= NFI_FDM_MAX_SIZE; ++ nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS; ++ } else if (free < 0) { ++ spare -= NFI_FDM_MIN_SIZE; ++ nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS; ++ } ++ } ++ ++ mtk_ecc_adjust_strength(nfc->ecc, &nand->ecc.strength); ++ ++ dev_info(dev, "eccsize %d eccstrength %d\n", ++ nand->ecc.size, nand->ecc.strength); ++ ++ return 0; ++} ++ ++static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, ++ struct device_node *np) ++{ ++ struct mtk_nfc_nand_chip *chip; ++ struct nand_chip *nand; ++ struct mtd_info *mtd; ++ int nsels, len; ++ u32 tmp; ++ int ret; ++ int i; ++ ++ if (!of_get_property(np, "reg", &nsels)) ++ return -ENODEV; ++ ++ nsels /= sizeof(u32); ++ if (!nsels || nsels > MTK_NAND_MAX_NSELS) { ++ dev_err(dev, "invalid reg property size %d\n", nsels); ++ return -EINVAL; ++ } ++ ++ chip = devm_kzalloc(dev, sizeof(*chip) + nsels * sizeof(u8), ++ GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ chip->nsels = nsels; ++ for (i = 0; i < nsels; i++) { ++ ret = of_property_read_u32_index(np, "reg", i, &tmp); ++ if (ret) { ++ dev_err(dev, "reg property failure : %d\n", ret); ++ return ret; ++ } ++ chip->sels[i] = tmp; ++ } ++ ++ nand = &chip->nand; ++ nand->controller = &nfc->controller; ++ ++ nand_set_flash_node(nand, np); ++ nand_set_controller_data(nand, nfc); ++ ++ nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ; ++ nand->dev_ready = mtk_nfc_dev_ready; ++ nand->select_chip = mtk_nfc_select_chip; ++ nand->write_byte = mtk_nfc_write_byte; ++ nand->write_buf = mtk_nfc_write_buf; ++ nand->read_byte = mtk_nfc_read_byte; ++ nand->read_buf = mtk_nfc_read_buf; ++ nand->cmd_ctrl = mtk_nfc_cmd_ctrl; ++ nand->setup_data_interface = mtk_nfc_setup_data_interface; ++ ++ /* set default mode in case dt entry is missing */ ++ nand->ecc.mode = NAND_ECC_HW; ++ ++ nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc; ++ nand->ecc.write_page_raw = mtk_nfc_write_page_raw; ++ nand->ecc.write_page = mtk_nfc_write_page_hwecc; ++ nand->ecc.write_oob_raw = mtk_nfc_write_oob_std; ++ nand->ecc.write_oob = mtk_nfc_write_oob_std; ++ ++ nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc; ++ nand->ecc.read_page_raw = mtk_nfc_read_page_raw; ++ nand->ecc.read_page = mtk_nfc_read_page_hwecc; ++ nand->ecc.read_oob_raw = mtk_nfc_read_oob_std; ++ nand->ecc.read_oob = mtk_nfc_read_oob_std; ++ ++ mtd = nand_to_mtd(nand); ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = dev; ++ mtd->name = MTK_NAME; ++ mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops); ++ ++ mtk_nfc_hw_init(nfc); ++ ++ ret = nand_scan_ident(mtd, nsels, NULL); ++ if (ret) ++ return ret; ++ ++ /* store bbt magic in page, cause OOB is not protected */ ++ if (nand->bbt_options & NAND_BBT_USE_FLASH) ++ nand->bbt_options |= NAND_BBT_NO_OOB; ++ ++ ret = mtk_nfc_ecc_init(dev, mtd); ++ if (ret) ++ return -EINVAL; ++ ++ if (nand->options & NAND_BUSWIDTH_16) { ++ dev_err(dev, "16bits buswidth not supported"); ++ return -EINVAL; ++ } ++ ++ ret = mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd); ++ if (ret) ++ return ret; ++ ++ mtk_nfc_set_fdm(&chip->fdm, mtd); ++ mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd); ++ ++ len = mtd->writesize + mtd->oobsize; ++ nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL); ++ if (!nfc->buffer) ++ return -ENOMEM; ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ return ret; ++ ++ ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); ++ if (ret) { ++ dev_err(dev, "mtd parse partition error\n"); ++ nand_release(mtd); ++ return ret; ++ } ++ ++ list_add_tail(&chip->node, &nfc->chips); ++ ++ return 0; ++} ++ ++static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) ++{ ++ struct device_node *np = dev->of_node; ++ struct device_node *nand_np; ++ int ret; ++ ++ for_each_child_of_node(np, nand_np) { ++ ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np); ++ if (ret) { ++ of_node_put(nand_np); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct mtk_nfc_caps mtk_nfc_caps_mt2701 = { ++ .spare_size = spare_size_mt2701, ++ .num_spare_size = 16, ++ .pageformat_spare_shift = 4, ++ .nfi_clk_div = 1, ++}; ++ ++static const struct mtk_nfc_caps mtk_nfc_caps_mt2712 = { ++ .spare_size = spare_size_mt2712, ++ .num_spare_size = 19, ++ .pageformat_spare_shift = 16, ++ .nfi_clk_div = 2, ++}; ++ ++static const struct of_device_id mtk_nfc_id_table[] = { ++ { ++ .compatible = "mediatek,mt2701-nfc", ++ .data = &mtk_nfc_caps_mt2701, ++ }, { ++ .compatible = "mediatek,mt2712-nfc", ++ .data = &mtk_nfc_caps_mt2712, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, mtk_nfc_id_table); ++ ++static int mtk_nfc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct mtk_nfc *nfc; ++ struct resource *res; ++ const struct of_device_id *of_nfc_id = NULL; ++ int ret, irq; ++ ++ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ spin_lock_init(&nfc->controller.lock); ++ init_waitqueue_head(&nfc->controller.wq); ++ INIT_LIST_HEAD(&nfc->chips); ++ ++ /* probe defer if not ready */ ++ nfc->ecc = of_mtk_ecc_get(np); ++ if (IS_ERR(nfc->ecc)) ++ return PTR_ERR(nfc->ecc); ++ else if (!nfc->ecc) ++ return -ENODEV; ++ ++ nfc->dev = dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nfc->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(nfc->regs)) { ++ ret = PTR_ERR(nfc->regs); ++ goto release_ecc; ++ } ++ ++ nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk"); ++ if (IS_ERR(nfc->clk.nfi_clk)) { ++ dev_err(dev, "no clk\n"); ++ ret = PTR_ERR(nfc->clk.nfi_clk); ++ goto release_ecc; ++ } ++ ++ nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk"); ++ if (IS_ERR(nfc->clk.pad_clk)) { ++ dev_err(dev, "no pad clk\n"); ++ ret = PTR_ERR(nfc->clk.pad_clk); ++ goto release_ecc; ++ } ++ ++ ret = mtk_nfc_enable_clk(dev, &nfc->clk); ++ if (ret) ++ goto release_ecc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "no nfi irq resource\n"); ++ ret = -EINVAL; ++ goto clk_disable; ++ } ++ ++ ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc); ++ if (ret) { ++ dev_err(dev, "failed to request nfi irq\n"); ++ goto clk_disable; ++ } ++ ++ ret = dma_set_mask(dev, DMA_BIT_MASK(32)); ++ if (ret) { ++ dev_err(dev, "failed to set dma mask\n"); ++ goto clk_disable; ++ } ++ ++ of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev); ++ if (!of_nfc_id) { ++ ret = -ENODEV; ++ goto clk_disable; ++ } ++ ++ nfc->caps = of_nfc_id->data; ++ ++ platform_set_drvdata(pdev, nfc); ++ ++ ret = mtk_nfc_nand_chips_init(dev, nfc); ++ if (ret) { ++ dev_err(dev, "failed to init nand chips\n"); ++ goto clk_disable; ++ } ++ ++ return 0; ++ ++clk_disable: ++ mtk_nfc_disable_clk(&nfc->clk); ++ ++release_ecc: ++ mtk_ecc_release(nfc->ecc); ++ ++ return ret; ++} ++ ++static int mtk_nfc_remove(struct platform_device *pdev) ++{ ++ struct mtk_nfc *nfc = platform_get_drvdata(pdev); ++ struct mtk_nfc_nand_chip *chip; ++ ++ while (!list_empty(&nfc->chips)) { ++ chip = list_first_entry(&nfc->chips, struct mtk_nfc_nand_chip, ++ node); ++ nand_release(nand_to_mtd(&chip->nand)); ++ list_del(&chip->node); ++ } ++ ++ mtk_ecc_release(nfc->ecc); ++ mtk_nfc_disable_clk(&nfc->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int mtk_nfc_suspend(struct device *dev) ++{ ++ struct mtk_nfc *nfc = dev_get_drvdata(dev); ++ ++ mtk_nfc_disable_clk(&nfc->clk); ++ ++ return 0; ++} ++ ++static int mtk_nfc_resume(struct device *dev) ++{ ++ struct mtk_nfc *nfc = dev_get_drvdata(dev); ++ struct mtk_nfc_nand_chip *chip; ++ struct nand_chip *nand; ++ struct mtd_info *mtd; ++ int ret; ++ u32 i; ++ ++ udelay(200); ++ ++ ret = mtk_nfc_enable_clk(dev, &nfc->clk); ++ if (ret) ++ return ret; ++ ++ /* reset NAND chip if VCC was powered off */ ++ list_for_each_entry(chip, &nfc->chips, node) { ++ nand = &chip->nand; ++ mtd = nand_to_mtd(nand); ++ for (i = 0; i < chip->nsels; i++) { ++ nand->select_chip(mtd, i); ++ nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ } ++ } ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume); ++#endif ++ ++static struct platform_driver mtk_nfc_driver = { ++ .probe = mtk_nfc_probe, ++ .remove = mtk_nfc_remove, ++ .driver = { ++ .name = MTK_NAME, ++ .of_match_table = mtk_nfc_id_table, ++#ifdef CONFIG_PM_SLEEP ++ .pm = &mtk_nfc_pm_ops, ++#endif ++ }, ++}; ++ ++module_platform_driver(mtk_nfc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Xiaolei Li "); ++MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); +diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c +new file mode 100644 +index 00000000..9e49873 +--- /dev/null ++++ b/drivers/mtd/nand/raw/mxc_nand.c +@@ -0,0 +1,1856 @@ ++/* ++ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Copyright 2008 Sascha Hauer, kernel@pengutronix.de ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define DRIVER_NAME "mxc_nand" ++ ++/* Addresses for NFC registers */ ++#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) ++#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) ++#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) ++#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) ++#define NFC_V1_V2_CONFIG (host->regs + 0x0a) ++#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) ++#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) ++#define NFC_V21_RSLTSPARE_AREA (host->regs + 0x10) ++#define NFC_V1_V2_WRPROT (host->regs + 0x12) ++#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) ++#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) ++#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) ++#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) ++#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) ++#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) ++#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) ++#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) ++#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) ++#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) ++#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) ++#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) ++#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) ++ ++#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) ++#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) ++#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) ++#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) ++#define NFC_V1_V2_CONFIG1_BIG (1 << 5) ++#define NFC_V1_V2_CONFIG1_RST (1 << 6) ++#define NFC_V1_V2_CONFIG1_CE (1 << 7) ++#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) ++#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9) ++#define NFC_V2_CONFIG1_FP_INT (1 << 11) ++ ++#define NFC_V1_V2_CONFIG2_INT (1 << 15) ++ ++/* ++ * Operation modes for the NFC. Valid for v1, v2 and v3 ++ * type controllers. ++ */ ++#define NFC_CMD (1 << 0) ++#define NFC_ADDR (1 << 1) ++#define NFC_INPUT (1 << 2) ++#define NFC_OUTPUT (1 << 3) ++#define NFC_ID (1 << 4) ++#define NFC_STATUS (1 << 5) ++ ++#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) ++#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) ++ ++#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) ++#define NFC_V3_CONFIG1_SP_EN (1 << 0) ++#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) ++ ++#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) ++ ++#define NFC_V3_LAUNCH (host->regs_axi + 0x40) ++ ++#define NFC_V3_WRPROT (host->regs_ip + 0x0) ++#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) ++#define NFC_V3_WRPROT_LOCK (1 << 1) ++#define NFC_V3_WRPROT_UNLOCK (1 << 2) ++#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) ++ ++#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) ++ ++#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) ++#define NFC_V3_CONFIG2_PS_512 (0 << 0) ++#define NFC_V3_CONFIG2_PS_2048 (1 << 0) ++#define NFC_V3_CONFIG2_PS_4096 (2 << 0) ++#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) ++#define NFC_V3_CONFIG2_ECC_EN (1 << 3) ++#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) ++#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) ++#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) ++#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift) ++#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) ++#define NFC_V3_CONFIG2_INT_MSK (1 << 15) ++#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) ++#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) ++ ++#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) ++#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) ++#define NFC_V3_CONFIG3_FW8 (1 << 3) ++#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) ++#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) ++#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) ++#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) ++ ++#define NFC_V3_IPC (host->regs_ip + 0x2C) ++#define NFC_V3_IPC_CREQ (1 << 0) ++#define NFC_V3_IPC_INT (1 << 31) ++ ++#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) ++ ++struct mxc_nand_host; ++ ++struct mxc_nand_devtype_data { ++ void (*preset)(struct mtd_info *); ++ void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); ++ void (*send_addr)(struct mxc_nand_host *, uint16_t, int); ++ void (*send_page)(struct mtd_info *, unsigned int); ++ void (*send_read_id)(struct mxc_nand_host *); ++ uint16_t (*get_dev_status)(struct mxc_nand_host *); ++ int (*check_int)(struct mxc_nand_host *); ++ void (*irq_control)(struct mxc_nand_host *, int); ++ u32 (*get_ecc_status)(struct mxc_nand_host *); ++ const struct mtd_ooblayout_ops *ooblayout; ++ void (*select_chip)(struct mtd_info *mtd, int chip); ++ int (*correct_data)(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc); ++ int (*setup_data_interface)(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf); ++ ++ /* ++ * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked ++ * (CONFIG1:INT_MSK is set). To handle this the driver uses ++ * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK ++ */ ++ int irqpending_quirk; ++ int needs_ip; ++ ++ size_t regs_offset; ++ size_t spare0_offset; ++ size_t axi_offset; ++ ++ int spare_len; ++ int eccbytes; ++ int eccsize; ++ int ppb_shift; ++}; ++ ++struct mxc_nand_host { ++ struct nand_chip nand; ++ struct device *dev; ++ ++ void __iomem *spare0; ++ void __iomem *main_area0; ++ ++ void __iomem *base; ++ void __iomem *regs; ++ void __iomem *regs_axi; ++ void __iomem *regs_ip; ++ int status_request; ++ struct clk *clk; ++ int clk_act; ++ int irq; ++ int eccsize; ++ int used_oobsize; ++ int active_cs; ++ ++ struct completion op_completion; ++ ++ uint8_t *data_buf; ++ unsigned int buf_start; ++ ++ const struct mxc_nand_devtype_data *devtype_data; ++ struct mxc_nand_platform_data pdata; ++}; ++ ++static const char * const part_probes[] = { ++ "cmdlinepart", "RedBoot", "ofpart", NULL }; ++ ++static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) ++{ ++ int i; ++ u32 *t = trg; ++ const __iomem u32 *s = src; ++ ++ for (i = 0; i < (size >> 2); i++) ++ *t++ = __raw_readl(s++); ++} ++ ++static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size) ++{ ++ int i; ++ u16 *t = trg; ++ const __iomem u16 *s = src; ++ ++ /* We assume that src (IO) is always 32bit aligned */ ++ if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) { ++ memcpy32_fromio(trg, src, size); ++ return; ++ } ++ ++ for (i = 0; i < (size >> 1); i++) ++ *t++ = __raw_readw(s++); ++} ++ ++static inline void memcpy32_toio(void __iomem *trg, const void *src, int size) ++{ ++ /* __iowrite32_copy use 32bit size values so divide by 4 */ ++ __iowrite32_copy(trg, src, size / 4); ++} ++ ++static void memcpy16_toio(void __iomem *trg, const void *src, int size) ++{ ++ int i; ++ __iomem u16 *t = trg; ++ const u16 *s = src; ++ ++ /* We assume that trg (IO) is always 32bit aligned */ ++ if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) { ++ memcpy32_toio(trg, src, size); ++ return; ++ } ++ ++ for (i = 0; i < (size >> 1); i++) ++ __raw_writew(*s++, t++); ++} ++ ++static int check_int_v3(struct mxc_nand_host *host) ++{ ++ uint32_t tmp; ++ ++ tmp = readl(NFC_V3_IPC); ++ if (!(tmp & NFC_V3_IPC_INT)) ++ return 0; ++ ++ tmp &= ~NFC_V3_IPC_INT; ++ writel(tmp, NFC_V3_IPC); ++ ++ return 1; ++} ++ ++static int check_int_v1_v2(struct mxc_nand_host *host) ++{ ++ uint32_t tmp; ++ ++ tmp = readw(NFC_V1_V2_CONFIG2); ++ if (!(tmp & NFC_V1_V2_CONFIG2_INT)) ++ return 0; ++ ++ if (!host->devtype_data->irqpending_quirk) ++ writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); ++ ++ return 1; ++} ++ ++static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) ++{ ++ uint16_t tmp; ++ ++ tmp = readw(NFC_V1_V2_CONFIG1); ++ ++ if (activate) ++ tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; ++ else ++ tmp |= NFC_V1_V2_CONFIG1_INT_MSK; ++ ++ writew(tmp, NFC_V1_V2_CONFIG1); ++} ++ ++static void irq_control_v3(struct mxc_nand_host *host, int activate) ++{ ++ uint32_t tmp; ++ ++ tmp = readl(NFC_V3_CONFIG2); ++ ++ if (activate) ++ tmp &= ~NFC_V3_CONFIG2_INT_MSK; ++ else ++ tmp |= NFC_V3_CONFIG2_INT_MSK; ++ ++ writel(tmp, NFC_V3_CONFIG2); ++} ++ ++static void irq_control(struct mxc_nand_host *host, int activate) ++{ ++ if (host->devtype_data->irqpending_quirk) { ++ if (activate) ++ enable_irq(host->irq); ++ else ++ disable_irq_nosync(host->irq); ++ } else { ++ host->devtype_data->irq_control(host, activate); ++ } ++} ++ ++static u32 get_ecc_status_v1(struct mxc_nand_host *host) ++{ ++ return readw(NFC_V1_V2_ECC_STATUS_RESULT); ++} ++ ++static u32 get_ecc_status_v2(struct mxc_nand_host *host) ++{ ++ return readl(NFC_V1_V2_ECC_STATUS_RESULT); ++} ++ ++static u32 get_ecc_status_v3(struct mxc_nand_host *host) ++{ ++ return readl(NFC_V3_ECC_STATUS_RESULT); ++} ++ ++static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) ++{ ++ struct mxc_nand_host *host = dev_id; ++ ++ if (!host->devtype_data->check_int(host)) ++ return IRQ_NONE; ++ ++ irq_control(host, 0); ++ ++ complete(&host->op_completion); ++ ++ return IRQ_HANDLED; ++} ++ ++/* This function polls the NANDFC to wait for the basic operation to ++ * complete by checking the INT bit of config2 register. ++ */ ++static int wait_op_done(struct mxc_nand_host *host, int useirq) ++{ ++ int ret = 0; ++ ++ /* ++ * If operation is already complete, don't bother to setup an irq or a ++ * loop. ++ */ ++ if (host->devtype_data->check_int(host)) ++ return 0; ++ ++ if (useirq) { ++ unsigned long timeout; ++ ++ reinit_completion(&host->op_completion); ++ ++ irq_control(host, 1); ++ ++ timeout = wait_for_completion_timeout(&host->op_completion, HZ); ++ if (!timeout && !host->devtype_data->check_int(host)) { ++ dev_dbg(host->dev, "timeout waiting for irq\n"); ++ ret = -ETIMEDOUT; ++ } ++ } else { ++ int max_retries = 8000; ++ int done; ++ ++ do { ++ udelay(1); ++ ++ done = host->devtype_data->check_int(host); ++ if (done) ++ break; ++ ++ } while (--max_retries); ++ ++ if (!done) { ++ dev_dbg(host->dev, "timeout polling for completion\n"); ++ ret = -ETIMEDOUT; ++ } ++ } ++ ++ WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq); ++ ++ return ret; ++} ++ ++static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) ++{ ++ /* fill command */ ++ writel(cmd, NFC_V3_FLASH_CMD); ++ ++ /* send out command */ ++ writel(NFC_CMD, NFC_V3_LAUNCH); ++ ++ /* Wait for operation to complete */ ++ wait_op_done(host, useirq); ++} ++ ++/* This function issues the specified command to the NAND device and ++ * waits for completion. */ ++static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) ++{ ++ pr_debug("send_cmd(host, 0x%x, %d)\n", cmd, useirq); ++ ++ writew(cmd, NFC_V1_V2_FLASH_CMD); ++ writew(NFC_CMD, NFC_V1_V2_CONFIG2); ++ ++ if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { ++ int max_retries = 100; ++ /* Reset completion is indicated by NFC_CONFIG2 */ ++ /* being set to 0 */ ++ while (max_retries-- > 0) { ++ if (readw(NFC_V1_V2_CONFIG2) == 0) { ++ break; ++ } ++ udelay(1); ++ } ++ if (max_retries < 0) ++ pr_debug("%s: RESET failed\n", __func__); ++ } else { ++ /* Wait for operation to complete */ ++ wait_op_done(host, useirq); ++ } ++} ++ ++static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) ++{ ++ /* fill address */ ++ writel(addr, NFC_V3_FLASH_ADDR0); ++ ++ /* send out address */ ++ writel(NFC_ADDR, NFC_V3_LAUNCH); ++ ++ wait_op_done(host, 0); ++} ++ ++/* This function sends an address (or partial address) to the ++ * NAND device. The address is used to select the source/destination for ++ * a NAND command. */ ++static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) ++{ ++ pr_debug("send_addr(host, 0x%x %d)\n", addr, islast); ++ ++ writew(addr, NFC_V1_V2_FLASH_ADDR); ++ writew(NFC_ADDR, NFC_V1_V2_CONFIG2); ++ ++ /* Wait for operation to complete */ ++ wait_op_done(host, islast); ++} ++ ++static void send_page_v3(struct mtd_info *mtd, unsigned int ops) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ uint32_t tmp; ++ ++ tmp = readl(NFC_V3_CONFIG1); ++ tmp &= ~(7 << 4); ++ writel(tmp, NFC_V3_CONFIG1); ++ ++ /* transfer data from NFC ram to nand */ ++ writel(ops, NFC_V3_LAUNCH); ++ ++ wait_op_done(host, false); ++} ++ ++static void send_page_v2(struct mtd_info *mtd, unsigned int ops) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ /* NANDFC buffer 0 is used for page read/write */ ++ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); ++ ++ writew(ops, NFC_V1_V2_CONFIG2); ++ ++ /* Wait for operation to complete */ ++ wait_op_done(host, true); ++} ++ ++static void send_page_v1(struct mtd_info *mtd, unsigned int ops) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ int bufs, i; ++ ++ if (mtd->writesize > 512) ++ bufs = 4; ++ else ++ bufs = 1; ++ ++ for (i = 0; i < bufs; i++) { ++ ++ /* NANDFC buffer 0 is used for page read/write */ ++ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); ++ ++ writew(ops, NFC_V1_V2_CONFIG2); ++ ++ /* Wait for operation to complete */ ++ wait_op_done(host, true); ++ } ++} ++ ++static void send_read_id_v3(struct mxc_nand_host *host) ++{ ++ /* Read ID into main buffer */ ++ writel(NFC_ID, NFC_V3_LAUNCH); ++ ++ wait_op_done(host, true); ++ ++ memcpy32_fromio(host->data_buf, host->main_area0, 16); ++} ++ ++/* Request the NANDFC to perform a read of the NAND device ID. */ ++static void send_read_id_v1_v2(struct mxc_nand_host *host) ++{ ++ /* NANDFC buffer 0 is used for device ID output */ ++ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); ++ ++ writew(NFC_ID, NFC_V1_V2_CONFIG2); ++ ++ /* Wait for operation to complete */ ++ wait_op_done(host, true); ++ ++ memcpy32_fromio(host->data_buf, host->main_area0, 16); ++} ++ ++static uint16_t get_dev_status_v3(struct mxc_nand_host *host) ++{ ++ writew(NFC_STATUS, NFC_V3_LAUNCH); ++ wait_op_done(host, true); ++ ++ return readl(NFC_V3_CONFIG1) >> 16; ++} ++ ++/* This function requests the NANDFC to perform a read of the ++ * NAND device status and returns the current status. */ ++static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) ++{ ++ void __iomem *main_buf = host->main_area0; ++ uint32_t store; ++ uint16_t ret; ++ ++ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); ++ ++ /* ++ * The device status is stored in main_area0. To ++ * prevent corruption of the buffer save the value ++ * and restore it afterwards. ++ */ ++ store = readl(main_buf); ++ ++ writew(NFC_STATUS, NFC_V1_V2_CONFIG2); ++ wait_op_done(host, true); ++ ++ ret = readw(main_buf); ++ ++ writel(store, main_buf); ++ ++ return ret; ++} ++ ++/* This functions is used by upper layer to checks if device is ready */ ++static int mxc_nand_dev_ready(struct mtd_info *mtd) ++{ ++ /* ++ * NFC handles R/B internally. Therefore, this function ++ * always returns status as ready. ++ */ ++ return 1; ++} ++ ++static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ /* ++ * If HW ECC is enabled, we turn it on during init. There is ++ * no need to enable again here. ++ */ ++} ++ ++static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ /* ++ * 1-Bit errors are automatically corrected in HW. No need for ++ * additional correction. 2-Bit errors cannot be corrected by ++ * HW ECC, so we need to return failure ++ */ ++ uint16_t ecc_status = get_ecc_status_v1(host); ++ ++ if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { ++ pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); ++ return -EBADMSG; ++ } ++ ++ return 0; ++} ++ ++static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ u32 ecc_stat, err; ++ int no_subpages = 1; ++ int ret = 0; ++ u8 ecc_bit_mask, err_limit; ++ ++ ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; ++ err_limit = (host->eccsize == 4) ? 0x4 : 0x8; ++ ++ no_subpages = mtd->writesize >> 9; ++ ++ ecc_stat = host->devtype_data->get_ecc_status(host); ++ ++ do { ++ err = ecc_stat & ecc_bit_mask; ++ if (err > err_limit) { ++ printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); ++ return -EBADMSG; ++ } else { ++ ret += err; ++ } ++ ecc_stat >>= 4; ++ } while (--no_subpages); ++ ++ pr_debug("%d Symbol Correctable RS-ECC Error\n", ret); ++ ++ return ret; ++} ++ ++static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ return 0; ++} ++ ++static u_char mxc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ uint8_t ret; ++ ++ /* Check for status request */ ++ if (host->status_request) ++ return host->devtype_data->get_dev_status(host) & 0xFF; ++ ++ if (nand_chip->options & NAND_BUSWIDTH_16) { ++ /* only take the lower byte of each word */ ++ ret = *(uint16_t *)(host->data_buf + host->buf_start); ++ ++ host->buf_start += 2; ++ } else { ++ ret = *(uint8_t *)(host->data_buf + host->buf_start); ++ host->buf_start++; ++ } ++ ++ pr_debug("%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start); ++ return ret; ++} ++ ++static uint16_t mxc_nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ uint16_t ret; ++ ++ ret = *(uint16_t *)(host->data_buf + host->buf_start); ++ host->buf_start += 2; ++ ++ return ret; ++} ++ ++/* Write data of length len to buffer buf. The data to be ++ * written on NAND Flash is first copied to RAMbuffer. After the Data Input ++ * Operation by the NFC, the data is written to NAND Flash */ ++static void mxc_nand_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ u16 col = host->buf_start; ++ int n = mtd->oobsize + mtd->writesize - col; ++ ++ n = min(n, len); ++ ++ memcpy(host->data_buf + col, buf, n); ++ ++ host->buf_start += n; ++} ++ ++/* Read the data buffer from the NAND Flash. To read the data from NAND ++ * Flash first the data output cycle is initiated by the NFC, which copies ++ * the data to RAMbuffer. This data of length len is then copied to buffer buf. ++ */ ++static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ u16 col = host->buf_start; ++ int n = mtd->oobsize + mtd->writesize - col; ++ ++ n = min(n, len); ++ ++ memcpy(buf, host->data_buf + col, n); ++ ++ host->buf_start += n; ++} ++ ++/* This function is used by upper layer for select and ++ * deselect of the NAND chip */ ++static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ if (chip == -1) { ++ /* Disable the NFC clock */ ++ if (host->clk_act) { ++ clk_disable_unprepare(host->clk); ++ host->clk_act = 0; ++ } ++ return; ++ } ++ ++ if (!host->clk_act) { ++ /* Enable the NFC clock */ ++ clk_prepare_enable(host->clk); ++ host->clk_act = 1; ++ } ++} ++ ++static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ if (chip == -1) { ++ /* Disable the NFC clock */ ++ if (host->clk_act) { ++ clk_disable_unprepare(host->clk); ++ host->clk_act = 0; ++ } ++ return; ++ } ++ ++ if (!host->clk_act) { ++ /* Enable the NFC clock */ ++ clk_prepare_enable(host->clk); ++ host->clk_act = 1; ++ } ++ ++ host->active_cs = chip; ++ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); ++} ++ ++/* ++ * The controller splits a page into data chunks of 512 bytes + partial oob. ++ * There are writesize / 512 such chunks, the size of the partial oob parts is ++ * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then ++ * contains additionally the byte lost by rounding (if any). ++ * This function handles the needed shuffling between host->data_buf (which ++ * holds a page in natural order, i.e. writesize bytes data + oobsize bytes ++ * spare) and the NFC buffer. ++ */ ++static void copy_spare(struct mtd_info *mtd, bool bfrom) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(this); ++ u16 i, oob_chunk_size; ++ u16 num_chunks = mtd->writesize / 512; ++ ++ u8 *d = host->data_buf + mtd->writesize; ++ u8 __iomem *s = host->spare0; ++ u16 sparebuf_size = host->devtype_data->spare_len; ++ ++ /* size of oob chunk for all but possibly the last one */ ++ oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; ++ ++ if (bfrom) { ++ for (i = 0; i < num_chunks - 1; i++) ++ memcpy16_fromio(d + i * oob_chunk_size, ++ s + i * sparebuf_size, ++ oob_chunk_size); ++ ++ /* the last chunk */ ++ memcpy16_fromio(d + i * oob_chunk_size, ++ s + i * sparebuf_size, ++ host->used_oobsize - i * oob_chunk_size); ++ } else { ++ for (i = 0; i < num_chunks - 1; i++) ++ memcpy16_toio(&s[i * sparebuf_size], ++ &d[i * oob_chunk_size], ++ oob_chunk_size); ++ ++ /* the last chunk */ ++ memcpy16_toio(&s[i * sparebuf_size], ++ &d[i * oob_chunk_size], ++ host->used_oobsize - i * oob_chunk_size); ++ } ++} ++ ++/* ++ * MXC NANDFC can only perform full page+spare or spare-only read/write. When ++ * the upper layers perform a read/write buf operation, the saved column address ++ * is used to index into the full page. So usually this function is called with ++ * column == 0 (unless no column cycle is needed indicated by column == -1) ++ */ ++static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ /* Write out column address, if necessary */ ++ if (column != -1) { ++ host->devtype_data->send_addr(host, column & 0xff, ++ page_addr == -1); ++ if (mtd->writesize > 512) ++ /* another col addr cycle for 2k page */ ++ host->devtype_data->send_addr(host, ++ (column >> 8) & 0xff, ++ false); ++ } ++ ++ /* Write out page address, if necessary */ ++ if (page_addr != -1) { ++ /* paddr_0 - p_addr_7 */ ++ host->devtype_data->send_addr(host, (page_addr & 0xff), false); ++ ++ if (mtd->writesize > 512) { ++ if (mtd->size >= 0x10000000) { ++ /* paddr_8 - paddr_15 */ ++ host->devtype_data->send_addr(host, ++ (page_addr >> 8) & 0xff, ++ false); ++ host->devtype_data->send_addr(host, ++ (page_addr >> 16) & 0xff, ++ true); ++ } else ++ /* paddr_8 - paddr_15 */ ++ host->devtype_data->send_addr(host, ++ (page_addr >> 8) & 0xff, true); ++ } else { ++ if (nand_chip->options & NAND_ROW_ADDR_3) { ++ /* paddr_8 - paddr_15 */ ++ host->devtype_data->send_addr(host, ++ (page_addr >> 8) & 0xff, ++ false); ++ host->devtype_data->send_addr(host, ++ (page_addr >> 16) & 0xff, ++ true); ++ } else ++ /* paddr_8 - paddr_15 */ ++ host->devtype_data->send_addr(host, ++ (page_addr >> 8) & 0xff, true); ++ } ++ } ++} ++ ++#define MXC_V1_ECCBYTES 5 ++ ++static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ ++ if (section >= nand_chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 6; ++ oobregion->length = MXC_V1_ECCBYTES; ++ ++ return 0; ++} ++ ++static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ ++ if (section > nand_chip->ecc.steps) ++ return -ERANGE; ++ ++ if (!section) { ++ if (mtd->writesize <= 512) { ++ oobregion->offset = 0; ++ oobregion->length = 5; ++ } else { ++ oobregion->offset = 2; ++ oobregion->length = 4; ++ } ++ } else { ++ oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6; ++ if (section < nand_chip->ecc.steps) ++ oobregion->length = (section * 16) + 6 - ++ oobregion->offset; ++ else ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = { ++ .ecc = mxc_v1_ooblayout_ecc, ++ .free = mxc_v1_ooblayout_free, ++}; ++ ++static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; ++ ++ if (section >= nand_chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (section * stepsize) + 7; ++ oobregion->length = nand_chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; ++ ++ if (section >= nand_chip->ecc.steps) ++ return -ERANGE; ++ ++ if (!section) { ++ if (mtd->writesize <= 512) { ++ oobregion->offset = 0; ++ oobregion->length = 5; ++ } else { ++ oobregion->offset = 2; ++ oobregion->length = 4; ++ } ++ } else { ++ oobregion->offset = section * stepsize; ++ oobregion->length = 7; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = { ++ .ecc = mxc_v2_ooblayout_ecc, ++ .free = mxc_v2_ooblayout_free, ++}; ++ ++/* ++ * v2 and v3 type controllers can do 4bit or 8bit ecc depending ++ * on how much oob the nand chip has. For 8bit ecc we need at least ++ * 26 bytes of oob data per 512 byte block. ++ */ ++static int get_eccsize(struct mtd_info *mtd) ++{ ++ int oobbytes_per_512 = 0; ++ ++ oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; ++ ++ if (oobbytes_per_512 < 26) ++ return 4; ++ else ++ return 8; ++} ++ ++static void preset_v1(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ uint16_t config1 = 0; ++ ++ if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize) ++ config1 |= NFC_V1_V2_CONFIG1_ECC_EN; ++ ++ if (!host->devtype_data->irqpending_quirk) ++ config1 |= NFC_V1_V2_CONFIG1_INT_MSK; ++ ++ host->eccsize = 1; ++ ++ writew(config1, NFC_V1_V2_CONFIG1); ++ /* preset operation */ ++ ++ /* Unlock the internal RAM Buffer */ ++ writew(0x2, NFC_V1_V2_CONFIG); ++ ++ /* Blocks to be unlocked */ ++ writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); ++ writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); ++ ++ /* Unlock Block Command for given address range */ ++ writew(0x4, NFC_V1_V2_WRPROT); ++} ++ ++static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ int tRC_min_ns, tRC_ps, ret; ++ unsigned long rate, rate_round; ++ const struct nand_sdr_timings *timings; ++ u16 config1; ++ ++ timings = nand_get_sdr_timings(conf); ++ if (IS_ERR(timings)) ++ return -ENOTSUPP; ++ ++ config1 = readw(NFC_V1_V2_CONFIG1); ++ ++ tRC_min_ns = timings->tRC_min / 1000; ++ rate = 1000000000 / tRC_min_ns; ++ ++ /* ++ * For tRC < 30ns we have to use EDO mode. In this case the controller ++ * does one access per clock cycle. Otherwise the controller does one ++ * access in two clock cycles, thus we have to double the rate to the ++ * controller. ++ */ ++ if (tRC_min_ns < 30) { ++ rate_round = clk_round_rate(host->clk, rate); ++ config1 |= NFC_V2_CONFIG1_ONE_CYCLE; ++ tRC_ps = 1000000000 / (rate_round / 1000); ++ } else { ++ rate *= 2; ++ rate_round = clk_round_rate(host->clk, rate); ++ config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE; ++ tRC_ps = 1000000000 / (rate_round / 1000 / 2); ++ } ++ ++ /* ++ * The timing values compared against are from the i.MX25 Automotive ++ * datasheet, Table 50. NFC Timing Parameters ++ */ ++ if (timings->tCLS_min > tRC_ps - 1000 || ++ timings->tCLH_min > tRC_ps - 2000 || ++ timings->tCS_min > tRC_ps - 1000 || ++ timings->tCH_min > tRC_ps - 2000 || ++ timings->tWP_min > tRC_ps - 1500 || ++ timings->tALS_min > tRC_ps || ++ timings->tALH_min > tRC_ps - 3000 || ++ timings->tDS_min > tRC_ps || ++ timings->tDH_min > tRC_ps - 5000 || ++ timings->tWC_min > 2 * tRC_ps || ++ timings->tWH_min > tRC_ps - 2500 || ++ timings->tRR_min > 6 * tRC_ps || ++ timings->tRP_min > 3 * tRC_ps / 2 || ++ timings->tRC_min > 2 * tRC_ps || ++ timings->tREH_min > (tRC_ps / 2) - 2500) { ++ dev_dbg(host->dev, "Timing out of bounds\n"); ++ return -EINVAL; ++ } ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ ret = clk_set_rate(host->clk, rate); ++ if (ret) ++ return ret; ++ ++ writew(config1, NFC_V1_V2_CONFIG1); ++ ++ dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round, ++ config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" : ++ "normal"); ++ ++ return 0; ++} ++ ++static void preset_v2(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ uint16_t config1 = 0; ++ ++ config1 |= NFC_V2_CONFIG1_FP_INT; ++ ++ if (!host->devtype_data->irqpending_quirk) ++ config1 |= NFC_V1_V2_CONFIG1_INT_MSK; ++ ++ if (mtd->writesize) { ++ uint16_t pages_per_block = mtd->erasesize / mtd->writesize; ++ ++ if (nand_chip->ecc.mode == NAND_ECC_HW) ++ config1 |= NFC_V1_V2_CONFIG1_ECC_EN; ++ ++ host->eccsize = get_eccsize(mtd); ++ if (host->eccsize == 4) ++ config1 |= NFC_V2_CONFIG1_ECC_MODE_4; ++ ++ config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); ++ } else { ++ host->eccsize = 1; ++ } ++ ++ writew(config1, NFC_V1_V2_CONFIG1); ++ /* preset operation */ ++ ++ /* spare area size in 16-bit half-words */ ++ writew(mtd->oobsize / 2, NFC_V21_RSLTSPARE_AREA); ++ ++ /* Unlock the internal RAM Buffer */ ++ writew(0x2, NFC_V1_V2_CONFIG); ++ ++ /* Blocks to be unlocked */ ++ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); ++ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); ++ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); ++ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); ++ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); ++ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); ++ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); ++ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); ++ ++ /* Unlock Block Command for given address range */ ++ writew(0x4, NFC_V1_V2_WRPROT); ++} ++ ++static void preset_v3(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(chip); ++ uint32_t config2, config3; ++ int i, addr_phases; ++ ++ writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); ++ writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); ++ ++ /* Unlock the internal RAM Buffer */ ++ writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, ++ NFC_V3_WRPROT); ++ ++ /* Blocks to be unlocked */ ++ for (i = 0; i < NAND_MAX_CHIPS; i++) ++ writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); ++ ++ writel(0, NFC_V3_IPC); ++ ++ config2 = NFC_V3_CONFIG2_ONE_CYCLE | ++ NFC_V3_CONFIG2_2CMD_PHASES | ++ NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | ++ NFC_V3_CONFIG2_ST_CMD(0x70) | ++ NFC_V3_CONFIG2_INT_MSK | ++ NFC_V3_CONFIG2_NUM_ADDR_PHASE0; ++ ++ addr_phases = fls(chip->pagemask) >> 3; ++ ++ if (mtd->writesize == 2048) { ++ config2 |= NFC_V3_CONFIG2_PS_2048; ++ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); ++ } else if (mtd->writesize == 4096) { ++ config2 |= NFC_V3_CONFIG2_PS_4096; ++ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); ++ } else { ++ config2 |= NFC_V3_CONFIG2_PS_512; ++ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); ++ } ++ ++ if (mtd->writesize) { ++ if (chip->ecc.mode == NAND_ECC_HW) ++ config2 |= NFC_V3_CONFIG2_ECC_EN; ++ ++ config2 |= NFC_V3_CONFIG2_PPB( ++ ffs(mtd->erasesize / mtd->writesize) - 6, ++ host->devtype_data->ppb_shift); ++ host->eccsize = get_eccsize(mtd); ++ if (host->eccsize == 8) ++ config2 |= NFC_V3_CONFIG2_ECC_MODE_8; ++ } ++ ++ writel(config2, NFC_V3_CONFIG2); ++ ++ config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | ++ NFC_V3_CONFIG3_NO_SDMA | ++ NFC_V3_CONFIG3_RBB_MODE | ++ NFC_V3_CONFIG3_SBB(6) | /* Reset default */ ++ NFC_V3_CONFIG3_ADD_OP(0); ++ ++ if (!(chip->options & NAND_BUSWIDTH_16)) ++ config3 |= NFC_V3_CONFIG3_FW8; ++ ++ writel(config3, NFC_V3_CONFIG3); ++ ++ writel(0, NFC_V3_DELAY_LINE); ++} ++ ++/* Used by the upper layer to write command to NAND Flash for ++ * different operations to be carried out on NAND Flash */ ++static void mxc_nand_command(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", ++ command, column, page_addr); ++ ++ /* Reset command state information */ ++ host->status_request = false; ++ ++ /* Command pre-processing step */ ++ switch (command) { ++ case NAND_CMD_RESET: ++ host->devtype_data->preset(mtd); ++ host->devtype_data->send_cmd(host, command, false); ++ break; ++ ++ case NAND_CMD_STATUS: ++ host->buf_start = 0; ++ host->status_request = true; ++ ++ host->devtype_data->send_cmd(host, command, true); ++ WARN_ONCE(column != -1 || page_addr != -1, ++ "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", ++ command, column, page_addr); ++ mxc_do_addr_cycle(mtd, column, page_addr); ++ break; ++ ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ if (command == NAND_CMD_READ0) ++ host->buf_start = column; ++ else ++ host->buf_start = column + mtd->writesize; ++ ++ command = NAND_CMD_READ0; /* only READ0 is valid */ ++ ++ host->devtype_data->send_cmd(host, command, false); ++ WARN_ONCE(column < 0, ++ "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", ++ command, column, page_addr); ++ mxc_do_addr_cycle(mtd, 0, page_addr); ++ ++ if (mtd->writesize > 512) ++ host->devtype_data->send_cmd(host, ++ NAND_CMD_READSTART, true); ++ ++ host->devtype_data->send_page(mtd, NFC_OUTPUT); ++ ++ memcpy32_fromio(host->data_buf, host->main_area0, ++ mtd->writesize); ++ copy_spare(mtd, true); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ if (column >= mtd->writesize) ++ /* call ourself to read a page */ ++ mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); ++ ++ host->buf_start = column; ++ ++ host->devtype_data->send_cmd(host, command, false); ++ WARN_ONCE(column < -1, ++ "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", ++ command, column, page_addr); ++ mxc_do_addr_cycle(mtd, 0, page_addr); ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); ++ copy_spare(mtd, false); ++ host->devtype_data->send_page(mtd, NFC_INPUT); ++ host->devtype_data->send_cmd(host, command, true); ++ WARN_ONCE(column != -1 || page_addr != -1, ++ "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", ++ command, column, page_addr); ++ mxc_do_addr_cycle(mtd, column, page_addr); ++ break; ++ ++ case NAND_CMD_READID: ++ host->devtype_data->send_cmd(host, command, true); ++ mxc_do_addr_cycle(mtd, column, page_addr); ++ host->devtype_data->send_read_id(host); ++ host->buf_start = 0; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ host->devtype_data->send_cmd(host, command, false); ++ WARN_ONCE(column != -1, ++ "Unexpected column value (cmd=%u, col=%d)\n", ++ command, column); ++ mxc_do_addr_cycle(mtd, column, page_addr); ++ ++ break; ++ case NAND_CMD_PARAM: ++ host->devtype_data->send_cmd(host, command, false); ++ mxc_do_addr_cycle(mtd, column, page_addr); ++ host->devtype_data->send_page(mtd, NFC_OUTPUT); ++ memcpy32_fromio(host->data_buf, host->main_area0, 512); ++ host->buf_start = 0; ++ break; ++ default: ++ WARN_ONCE(1, "Unimplemented command (cmd=%u)\n", ++ command); ++ break; ++ } ++} ++ ++static int mxc_nand_onfi_set_features(struct mtd_info *mtd, ++ struct nand_chip *chip, int addr, ++ u8 *subfeature_param) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ int i; ++ ++ if (!chip->onfi_version || ++ !(le16_to_cpu(chip->onfi_params.opt_cmd) ++ & ONFI_OPT_CMD_SET_GET_FEATURES)) ++ return -EINVAL; ++ ++ host->buf_start = 0; ++ ++ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) ++ chip->write_byte(mtd, subfeature_param[i]); ++ ++ memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); ++ host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false); ++ mxc_do_addr_cycle(mtd, addr, -1); ++ host->devtype_data->send_page(mtd, NFC_INPUT); ++ ++ return 0; ++} ++ ++static int mxc_nand_onfi_get_features(struct mtd_info *mtd, ++ struct nand_chip *chip, int addr, ++ u8 *subfeature_param) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct mxc_nand_host *host = nand_get_controller_data(nand_chip); ++ int i; ++ ++ if (!chip->onfi_version || ++ !(le16_to_cpu(chip->onfi_params.opt_cmd) ++ & ONFI_OPT_CMD_SET_GET_FEATURES)) ++ return -EINVAL; ++ ++ host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false); ++ mxc_do_addr_cycle(mtd, addr, -1); ++ host->devtype_data->send_page(mtd, NFC_OUTPUT); ++ memcpy32_fromio(host->data_buf, host->main_area0, 512); ++ host->buf_start = 0; ++ ++ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) ++ *subfeature_param++ = chip->read_byte(mtd); ++ ++ return 0; ++} ++ ++/* ++ * The generic flash bbt decriptors overlap with our ecc ++ * hardware, so define some i.MX specific ones. ++ */ ++static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; ++static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 0, ++ .len = 4, ++ .veroffs = 4, ++ .maxblocks = 4, ++ .pattern = bbt_pattern, ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 0, ++ .len = 4, ++ .veroffs = 4, ++ .maxblocks = 4, ++ .pattern = mirror_pattern, ++}; ++ ++/* v1 + irqpending_quirk: i.MX21 */ ++static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { ++ .preset = preset_v1, ++ .send_cmd = send_cmd_v1_v2, ++ .send_addr = send_addr_v1_v2, ++ .send_page = send_page_v1, ++ .send_read_id = send_read_id_v1_v2, ++ .get_dev_status = get_dev_status_v1_v2, ++ .check_int = check_int_v1_v2, ++ .irq_control = irq_control_v1_v2, ++ .get_ecc_status = get_ecc_status_v1, ++ .ooblayout = &mxc_v1_ooblayout_ops, ++ .select_chip = mxc_nand_select_chip_v1_v3, ++ .correct_data = mxc_nand_correct_data_v1, ++ .irqpending_quirk = 1, ++ .needs_ip = 0, ++ .regs_offset = 0xe00, ++ .spare0_offset = 0x800, ++ .spare_len = 16, ++ .eccbytes = 3, ++ .eccsize = 1, ++}; ++ ++/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ ++static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { ++ .preset = preset_v1, ++ .send_cmd = send_cmd_v1_v2, ++ .send_addr = send_addr_v1_v2, ++ .send_page = send_page_v1, ++ .send_read_id = send_read_id_v1_v2, ++ .get_dev_status = get_dev_status_v1_v2, ++ .check_int = check_int_v1_v2, ++ .irq_control = irq_control_v1_v2, ++ .get_ecc_status = get_ecc_status_v1, ++ .ooblayout = &mxc_v1_ooblayout_ops, ++ .select_chip = mxc_nand_select_chip_v1_v3, ++ .correct_data = mxc_nand_correct_data_v1, ++ .irqpending_quirk = 0, ++ .needs_ip = 0, ++ .regs_offset = 0xe00, ++ .spare0_offset = 0x800, ++ .axi_offset = 0, ++ .spare_len = 16, ++ .eccbytes = 3, ++ .eccsize = 1, ++}; ++ ++/* v21: i.MX25, i.MX35 */ ++static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { ++ .preset = preset_v2, ++ .send_cmd = send_cmd_v1_v2, ++ .send_addr = send_addr_v1_v2, ++ .send_page = send_page_v2, ++ .send_read_id = send_read_id_v1_v2, ++ .get_dev_status = get_dev_status_v1_v2, ++ .check_int = check_int_v1_v2, ++ .irq_control = irq_control_v1_v2, ++ .get_ecc_status = get_ecc_status_v2, ++ .ooblayout = &mxc_v2_ooblayout_ops, ++ .select_chip = mxc_nand_select_chip_v2, ++ .correct_data = mxc_nand_correct_data_v2_v3, ++ .setup_data_interface = mxc_nand_v2_setup_data_interface, ++ .irqpending_quirk = 0, ++ .needs_ip = 0, ++ .regs_offset = 0x1e00, ++ .spare0_offset = 0x1000, ++ .axi_offset = 0, ++ .spare_len = 64, ++ .eccbytes = 9, ++ .eccsize = 0, ++}; ++ ++/* v3.2a: i.MX51 */ ++static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { ++ .preset = preset_v3, ++ .send_cmd = send_cmd_v3, ++ .send_addr = send_addr_v3, ++ .send_page = send_page_v3, ++ .send_read_id = send_read_id_v3, ++ .get_dev_status = get_dev_status_v3, ++ .check_int = check_int_v3, ++ .irq_control = irq_control_v3, ++ .get_ecc_status = get_ecc_status_v3, ++ .ooblayout = &mxc_v2_ooblayout_ops, ++ .select_chip = mxc_nand_select_chip_v1_v3, ++ .correct_data = mxc_nand_correct_data_v2_v3, ++ .irqpending_quirk = 0, ++ .needs_ip = 1, ++ .regs_offset = 0, ++ .spare0_offset = 0x1000, ++ .axi_offset = 0x1e00, ++ .spare_len = 64, ++ .eccbytes = 0, ++ .eccsize = 0, ++ .ppb_shift = 7, ++}; ++ ++/* v3.2b: i.MX53 */ ++static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { ++ .preset = preset_v3, ++ .send_cmd = send_cmd_v3, ++ .send_addr = send_addr_v3, ++ .send_page = send_page_v3, ++ .send_read_id = send_read_id_v3, ++ .get_dev_status = get_dev_status_v3, ++ .check_int = check_int_v3, ++ .irq_control = irq_control_v3, ++ .get_ecc_status = get_ecc_status_v3, ++ .ooblayout = &mxc_v2_ooblayout_ops, ++ .select_chip = mxc_nand_select_chip_v1_v3, ++ .correct_data = mxc_nand_correct_data_v2_v3, ++ .irqpending_quirk = 0, ++ .needs_ip = 1, ++ .regs_offset = 0, ++ .spare0_offset = 0x1000, ++ .axi_offset = 0x1e00, ++ .spare_len = 64, ++ .eccbytes = 0, ++ .eccsize = 0, ++ .ppb_shift = 8, ++}; ++ ++static inline int is_imx21_nfc(struct mxc_nand_host *host) ++{ ++ return host->devtype_data == &imx21_nand_devtype_data; ++} ++ ++static inline int is_imx27_nfc(struct mxc_nand_host *host) ++{ ++ return host->devtype_data == &imx27_nand_devtype_data; ++} ++ ++static inline int is_imx25_nfc(struct mxc_nand_host *host) ++{ ++ return host->devtype_data == &imx25_nand_devtype_data; ++} ++ ++static inline int is_imx51_nfc(struct mxc_nand_host *host) ++{ ++ return host->devtype_data == &imx51_nand_devtype_data; ++} ++ ++static inline int is_imx53_nfc(struct mxc_nand_host *host) ++{ ++ return host->devtype_data == &imx53_nand_devtype_data; ++} ++ ++static const struct platform_device_id mxcnd_devtype[] = { ++ { ++ .name = "imx21-nand", ++ .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data, ++ }, { ++ .name = "imx27-nand", ++ .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data, ++ }, { ++ .name = "imx25-nand", ++ .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data, ++ }, { ++ .name = "imx51-nand", ++ .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data, ++ }, { ++ .name = "imx53-nand", ++ .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data, ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(platform, mxcnd_devtype); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id mxcnd_dt_ids[] = { ++ { ++ .compatible = "fsl,imx21-nand", ++ .data = &imx21_nand_devtype_data, ++ }, { ++ .compatible = "fsl,imx27-nand", ++ .data = &imx27_nand_devtype_data, ++ }, { ++ .compatible = "fsl,imx25-nand", ++ .data = &imx25_nand_devtype_data, ++ }, { ++ .compatible = "fsl,imx51-nand", ++ .data = &imx51_nand_devtype_data, ++ }, { ++ .compatible = "fsl,imx53-nand", ++ .data = &imx53_nand_devtype_data, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); ++ ++static int __init mxcnd_probe_dt(struct mxc_nand_host *host) ++{ ++ struct device_node *np = host->dev->of_node; ++ const struct of_device_id *of_id = ++ of_match_device(mxcnd_dt_ids, host->dev); ++ ++ if (!np) ++ return 1; ++ ++ host->devtype_data = of_id->data; ++ ++ return 0; ++} ++#else ++static int __init mxcnd_probe_dt(struct mxc_nand_host *host) ++{ ++ return 1; ++} ++#endif ++ ++static int mxcnd_probe(struct platform_device *pdev) ++{ ++ struct nand_chip *this; ++ struct mtd_info *mtd; ++ struct mxc_nand_host *host; ++ struct resource *res; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host), ++ GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ /* allocate a temporary buffer for the nand_scan_ident() */ ++ host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL); ++ if (!host->data_buf) ++ return -ENOMEM; ++ ++ host->dev = &pdev->dev; ++ /* structures must be linked */ ++ this = &host->nand; ++ mtd = nand_to_mtd(this); ++ mtd->dev.parent = &pdev->dev; ++ mtd->name = DRIVER_NAME; ++ ++ /* 50 us command delay time */ ++ this->chip_delay = 5; ++ ++ nand_set_controller_data(this, host); ++ nand_set_flash_node(this, pdev->dev.of_node), ++ this->dev_ready = mxc_nand_dev_ready; ++ this->cmdfunc = mxc_nand_command; ++ this->read_byte = mxc_nand_read_byte; ++ this->read_word = mxc_nand_read_word; ++ this->write_buf = mxc_nand_write_buf; ++ this->read_buf = mxc_nand_read_buf; ++ this->onfi_set_features = mxc_nand_onfi_set_features; ++ this->onfi_get_features = mxc_nand_onfi_get_features; ++ ++ host->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(host->clk)) ++ return PTR_ERR(host->clk); ++ ++ err = mxcnd_probe_dt(host); ++ if (err > 0) { ++ struct mxc_nand_platform_data *pdata = ++ dev_get_platdata(&pdev->dev); ++ if (pdata) { ++ host->pdata = *pdata; ++ host->devtype_data = (struct mxc_nand_devtype_data *) ++ pdev->id_entry->driver_data; ++ } else { ++ err = -ENODEV; ++ } ++ } ++ if (err < 0) ++ return err; ++ ++ this->setup_data_interface = host->devtype_data->setup_data_interface; ++ ++ if (host->devtype_data->needs_ip) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ host->regs_ip = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(host->regs_ip)) ++ return PTR_ERR(host->regs_ip); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ } else { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ } ++ ++ host->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(host->base)) ++ return PTR_ERR(host->base); ++ ++ host->main_area0 = host->base; ++ ++ if (host->devtype_data->regs_offset) ++ host->regs = host->base + host->devtype_data->regs_offset; ++ host->spare0 = host->base + host->devtype_data->spare0_offset; ++ if (host->devtype_data->axi_offset) ++ host->regs_axi = host->base + host->devtype_data->axi_offset; ++ ++ this->ecc.bytes = host->devtype_data->eccbytes; ++ host->eccsize = host->devtype_data->eccsize; ++ ++ this->select_chip = host->devtype_data->select_chip; ++ this->ecc.size = 512; ++ mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); ++ ++ if (host->pdata.hw_ecc) { ++ this->ecc.mode = NAND_ECC_HW; ++ } else { ++ this->ecc.mode = NAND_ECC_SOFT; ++ this->ecc.algo = NAND_ECC_HAMMING; ++ } ++ ++ /* NAND bus width determines access functions used by upper layer */ ++ if (host->pdata.width == 2) ++ this->options |= NAND_BUSWIDTH_16; ++ ++ /* update flash based bbt */ ++ if (host->pdata.flash_bbt) ++ this->bbt_options |= NAND_BBT_USE_FLASH; ++ ++ init_completion(&host->op_completion); ++ ++ host->irq = platform_get_irq(pdev, 0); ++ if (host->irq < 0) ++ return host->irq; ++ ++ /* ++ * Use host->devtype_data->irq_control() here instead of irq_control() ++ * because we must not disable_irq_nosync without having requested the ++ * irq. ++ */ ++ host->devtype_data->irq_control(host, 0); ++ ++ err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq, ++ 0, DRIVER_NAME, host); ++ if (err) ++ return err; ++ ++ err = clk_prepare_enable(host->clk); ++ if (err) ++ return err; ++ host->clk_act = 1; ++ ++ /* ++ * Now that we "own" the interrupt make sure the interrupt mask bit is ++ * cleared on i.MX21. Otherwise we can't read the interrupt status bit ++ * on this machine. ++ */ ++ if (host->devtype_data->irqpending_quirk) { ++ disable_irq_nosync(host->irq); ++ host->devtype_data->irq_control(host, 1); ++ } ++ ++ /* first scan to find the device and get the page size */ ++ err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL); ++ if (err) ++ goto escan; ++ ++ switch (this->ecc.mode) { ++ case NAND_ECC_HW: ++ this->ecc.calculate = mxc_nand_calculate_ecc; ++ this->ecc.hwctl = mxc_nand_enable_hwecc; ++ this->ecc.correct = host->devtype_data->correct_data; ++ break; ++ ++ case NAND_ECC_SOFT: ++ break; ++ ++ default: ++ err = -EINVAL; ++ goto escan; ++ } ++ ++ if (this->bbt_options & NAND_BBT_USE_FLASH) { ++ this->bbt_td = &bbt_main_descr; ++ this->bbt_md = &bbt_mirror_descr; ++ } ++ ++ /* allocate the right size buffer now */ ++ devm_kfree(&pdev->dev, (void *)host->data_buf); ++ host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize, ++ GFP_KERNEL); ++ if (!host->data_buf) { ++ err = -ENOMEM; ++ goto escan; ++ } ++ ++ /* Call preset again, with correct writesize this time */ ++ host->devtype_data->preset(mtd); ++ ++ if (!this->ecc.bytes) { ++ if (host->eccsize == 8) ++ this->ecc.bytes = 18; ++ else if (host->eccsize == 4) ++ this->ecc.bytes = 9; ++ } ++ ++ /* ++ * Experimentation shows that i.MX NFC can only handle up to 218 oob ++ * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() ++ * into copying invalid data to/from the spare IO buffer, as this ++ * might cause ECC data corruption when doing sub-page write to a ++ * partially written page. ++ */ ++ host->used_oobsize = min(mtd->oobsize, 218U); ++ ++ if (this->ecc.mode == NAND_ECC_HW) { ++ if (is_imx21_nfc(host) || is_imx27_nfc(host)) ++ this->ecc.strength = 1; ++ else ++ this->ecc.strength = (host->eccsize == 4) ? 4 : 8; ++ } ++ ++ /* second phase scan */ ++ err = nand_scan_tail(mtd); ++ if (err) ++ goto escan; ++ ++ /* Register the partitions */ ++ mtd_device_parse_register(mtd, part_probes, ++ NULL, ++ host->pdata.parts, ++ host->pdata.nr_parts); ++ ++ platform_set_drvdata(pdev, host); ++ ++ return 0; ++ ++escan: ++ if (host->clk_act) ++ clk_disable_unprepare(host->clk); ++ ++ return err; ++} ++ ++static int mxcnd_remove(struct platform_device *pdev) ++{ ++ struct mxc_nand_host *host = platform_get_drvdata(pdev); ++ ++ nand_release(nand_to_mtd(&host->nand)); ++ if (host->clk_act) ++ clk_disable_unprepare(host->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver mxcnd_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = of_match_ptr(mxcnd_dt_ids), ++ }, ++ .id_table = mxcnd_devtype, ++ .probe = mxcnd_probe, ++ .remove = mxcnd_remove, ++}; ++module_platform_driver(mxcnd_driver); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("MXC NAND MTD driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/nand/raw/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c +new file mode 100644 +index 00000000..22f060f +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_amd.c +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (C) 2017 Free Electrons ++ * Copyright (C) 2017 NextThing Co ++ * ++ * Author: Boris Brezillon ++ * ++ * 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 ++ ++static void amd_nand_decode_id(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ nand_decode_ext_id(chip); ++ ++ /* ++ * Check for Spansion/AMD ID + repeating 5th, 6th byte since ++ * some Spansion chips have erasesize that conflicts with size ++ * listed in nand_ids table. ++ * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) ++ */ ++ if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 && ++ chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 && ++ mtd->writesize == 512) { ++ mtd->erasesize = 128 * 1024; ++ mtd->erasesize <<= ((chip->id.data[3] & 0x03) << 1); ++ } ++} ++ ++static int amd_nand_init(struct nand_chip *chip) ++{ ++ if (nand_is_slc(chip)) ++ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; ++ ++ return 0; ++} ++ ++const struct nand_manufacturer_ops amd_nand_manuf_ops = { ++ .detect = amd_nand_decode_id, ++ .init = amd_nand_init, ++}; +diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c +new file mode 100644 +index 00000000..1d9c15d +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_base.c +@@ -0,0 +1,6679 @@ ++/* ++ * Overview: ++ * This is the generic MTD driver for NAND flash devices. It should be ++ * capable of working with almost all NAND chips currently available. ++ * ++ * Additional technical information is available on ++ * http://www.linux-mtd.infradead.org/doc/nand.html ++ * ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) ++ * 2002-2006 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * Credits: ++ * David Woodhouse for adding multichip support ++ * ++ * Aleph One Ltd. and Toby Churchill Ltd. for supporting the ++ * rework for 2K page size chips ++ * ++ * TODO: ++ * Enable cached programming for 2k page size chips ++ * Check, if mtd->ecctype should be set to MTD_ECC_HW ++ * if we have HW ECC support. ++ * BBT table is not serialized, has to be fixed ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int nand_get_device(struct mtd_info *mtd, int new_state); ++ ++static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops); ++ ++/* Define default oob placement schemes for large and small page devices */ ++static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section > 1) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->offset = 0; ++ if (mtd->oobsize == 16) ++ oobregion->length = 4; ++ else ++ oobregion->length = 3; ++ } else { ++ if (mtd->oobsize == 8) ++ return -ERANGE; ++ ++ oobregion->offset = 6; ++ oobregion->length = ecc->total - 4; ++ } ++ ++ return 0; ++} ++ ++static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 1) ++ return -ERANGE; ++ ++ if (mtd->oobsize == 16) { ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = 8; ++ oobregion->offset = 8; ++ } else { ++ oobregion->length = 2; ++ if (!section) ++ oobregion->offset = 3; ++ else ++ oobregion->offset = 6; ++ } ++ ++ return 0; ++} ++ ++const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { ++ .ecc = nand_ooblayout_ecc_sp, ++ .free = nand_ooblayout_free_sp, ++}; ++EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops); ++ ++static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section || !ecc->total) ++ return -ERANGE; ++ ++ oobregion->length = ecc->total; ++ oobregion->offset = mtd->oobsize - oobregion->length; ++ ++ return 0; ++} ++ ++static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = mtd->oobsize - ecc->total - 2; ++ oobregion->offset = 2; ++ ++ return 0; ++} ++ ++const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { ++ .ecc = nand_ooblayout_ecc_lp, ++ .free = nand_ooblayout_free_lp, ++}; ++EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops); ++ ++/* ++ * Support the old "large page" layout used for 1-bit Hamming ECC where ECC ++ * are placed at a fixed offset. ++ */ ++static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ switch (mtd->oobsize) { ++ case 64: ++ oobregion->offset = 40; ++ break; ++ case 128: ++ oobregion->offset = 80; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ oobregion->length = ecc->total; ++ if (oobregion->offset + oobregion->length > mtd->oobsize) ++ return -ERANGE; ++ ++ return 0; ++} ++ ++static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ecc_offset = 0; ++ ++ if (section < 0 || section > 1) ++ return -ERANGE; ++ ++ switch (mtd->oobsize) { ++ case 64: ++ ecc_offset = 40; ++ break; ++ case 128: ++ ecc_offset = 80; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (section == 0) { ++ oobregion->offset = 2; ++ oobregion->length = ecc_offset - 2; ++ } else { ++ oobregion->offset = ecc_offset + ecc->total; ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = { ++ .ecc = nand_ooblayout_ecc_lp_hamming, ++ .free = nand_ooblayout_free_lp_hamming, ++}; ++ ++static int check_offs_len(struct mtd_info *mtd, ++ loff_t ofs, uint64_t len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret = 0; ++ ++ /* Start address must align on block boundary */ ++ if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { ++ pr_debug("%s: unaligned address\n", __func__); ++ ret = -EINVAL; ++ } ++ ++ /* Length must align on block boundary */ ++ if (len & ((1ULL << chip->phys_erase_shift) - 1)) { ++ pr_debug("%s: length not block aligned\n", __func__); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/** ++ * nand_release_device - [GENERIC] release chip ++ * @mtd: MTD device structure ++ * ++ * Release chip lock and wake up anyone waiting on the device. ++ */ ++static void nand_release_device(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ /* Release the controller and the chip */ ++ spin_lock(&chip->controller->lock); ++ chip->controller->active = NULL; ++ chip->state = FL_READY; ++ wake_up(&chip->controller->wq); ++ spin_unlock(&chip->controller->lock); ++} ++ ++/** ++ * nand_read_byte - [DEFAULT] read one byte from the chip ++ * @mtd: MTD device structure ++ * ++ * Default read function for 8bit buswidth ++ */ ++static uint8_t nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ return readb(chip->IO_ADDR_R); ++} ++ ++/** ++ * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip ++ * @mtd: MTD device structure ++ * ++ * Default read function for 16bit buswidth with endianness conversion. ++ * ++ */ ++static uint8_t nand_read_byte16(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); ++} ++ ++/** ++ * nand_read_word - [DEFAULT] read one word from the chip ++ * @mtd: MTD device structure ++ * ++ * Default read function for 16bit buswidth without endianness conversion. ++ */ ++static u16 nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ return readw(chip->IO_ADDR_R); ++} ++ ++/** ++ * nand_select_chip - [DEFAULT] control CE line ++ * @mtd: MTD device structure ++ * @chipnr: chipnumber to select, -1 for deselect ++ * ++ * Default select function for 1 chip devices. ++ */ ++static void nand_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ switch (chipnr) { ++ case -1: ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); ++ break; ++ case 0: ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++/** ++ * nand_write_byte - [DEFAULT] write single byte to chip ++ * @mtd: MTD device structure ++ * @byte: value to write ++ * ++ * Default function to write a byte to I/O[7:0] ++ */ ++static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ chip->write_buf(mtd, &byte, 1); ++} ++ ++/** ++ * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 ++ * @mtd: MTD device structure ++ * @byte: value to write ++ * ++ * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. ++ */ ++static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ uint16_t word = byte; ++ ++ /* ++ * It's not entirely clear what should happen to I/O[15:8] when writing ++ * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: ++ * ++ * When the host supports a 16-bit bus width, only data is ++ * transferred at the 16-bit width. All address and command line ++ * transfers shall use only the lower 8-bits of the data bus. During ++ * command transfers, the host may place any value on the upper ++ * 8-bits of the data bus. During address transfers, the host shall ++ * set the upper 8-bits of the data bus to 00h. ++ * ++ * One user of the write_byte callback is nand_onfi_set_features. The ++ * four parameters are specified to be written to I/O[7:0], but this is ++ * neither an address nor a command transfer. Let's assume a 0 on the ++ * upper I/O lines is OK. ++ */ ++ chip->write_buf(mtd, (uint8_t *)&word, 2); ++} ++ ++/** ++ * nand_write_buf - [DEFAULT] write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * Default write function for 8bit buswidth. ++ */ ++static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ iowrite8_rep(chip->IO_ADDR_W, buf, len); ++} ++ ++/** ++ * nand_read_buf - [DEFAULT] read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * Default read function for 8bit buswidth. ++ */ ++static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ ioread8_rep(chip->IO_ADDR_R, buf, len); ++} ++ ++/** ++ * nand_write_buf16 - [DEFAULT] write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ * ++ * Default write function for 16bit buswidth. ++ */ ++static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u16 *p = (u16 *) buf; ++ ++ iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); ++} ++ ++/** ++ * nand_read_buf16 - [DEFAULT] read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ * ++ * Default read function for 16bit buswidth. ++ */ ++static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u16 *p = (u16 *) buf; ++ ++ ioread16_rep(chip->IO_ADDR_R, p, len >> 1); ++} ++ ++/** ++ * nand_block_bad - [DEFAULT] Read bad block marker from the chip ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * ++ * Check, if the block is bad. ++ */ ++static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) ++{ ++ int page, page_end, res; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u8 bad; ++ ++ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ++ ofs += mtd->erasesize - mtd->writesize; ++ ++ page = (int)(ofs >> chip->page_shift) & chip->pagemask; ++ page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1); ++ ++ for (; page < page_end; page++) { ++ res = chip->ecc.read_oob(mtd, chip, page); ++ if (res < 0) ++ return res; ++ ++ bad = chip->oob_poi[chip->badblockpos]; ++ ++ if (likely(chip->badblockbits == 8)) ++ res = bad != 0xFF; ++ else ++ res = hweight8(bad) < chip->badblockbits; ++ if (res) ++ return res; ++ } ++ ++ return 0; ++} ++ ++/** ++ * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * ++ * This is the default implementation, which can be overridden by a hardware ++ * specific driver. It provides the details for writing a bad block marker to a ++ * block. ++ */ ++static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtd_oob_ops ops; ++ uint8_t buf[2] = { 0, 0 }; ++ int ret = 0, res, i = 0; ++ ++ memset(&ops, 0, sizeof(ops)); ++ ops.oobbuf = buf; ++ ops.ooboffs = chip->badblockpos; ++ if (chip->options & NAND_BUSWIDTH_16) { ++ ops.ooboffs &= ~0x01; ++ ops.len = ops.ooblen = 2; ++ } else { ++ ops.len = ops.ooblen = 1; ++ } ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ++ /* Write to first/last page(s) if necessary */ ++ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ++ ofs += mtd->erasesize - mtd->writesize; ++ do { ++ res = nand_do_write_oob(mtd, ofs, &ops); ++ if (!ret) ++ ret = res; ++ ++ i++; ++ ofs += mtd->writesize; ++ } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); ++ ++ return ret; ++} ++ ++/** ++ * nand_block_markbad_lowlevel - mark a block bad ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * ++ * This function performs the generic NAND bad block marking steps (i.e., bad ++ * block table(s) and/or marker(s)). We only allow the hardware driver to ++ * specify how to write bad block markers to OOB (chip->block_markbad). ++ * ++ * We try operations in the following order: ++ * ++ * (1) erase the affected block, to allow OOB marker to be written cleanly ++ * (2) write bad block marker to OOB area of affected block (unless flag ++ * NAND_BBT_NO_OOB_BBM is present) ++ * (3) update the BBT ++ * ++ * Note that we retain the first error encountered in (2) or (3), finish the ++ * procedures, and dump the error in the end. ++*/ ++static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int res, ret = 0; ++ ++ if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { ++ struct erase_info einfo; ++ ++ /* Attempt erase before marking OOB */ ++ memset(&einfo, 0, sizeof(einfo)); ++ einfo.mtd = mtd; ++ einfo.addr = ofs; ++ einfo.len = 1ULL << chip->phys_erase_shift; ++ nand_erase_nand(mtd, &einfo, 0); ++ ++ /* Write bad block marker to OOB */ ++ nand_get_device(mtd, FL_WRITING); ++ ret = chip->block_markbad(mtd, ofs); ++ nand_release_device(mtd); ++ } ++ ++ /* Mark block bad in BBT */ ++ if (chip->bbt) { ++ res = nand_markbad_bbt(mtd, ofs); ++ if (!ret) ++ ret = res; ++ } ++ ++ if (!ret) ++ mtd->ecc_stats.badblocks++; ++ ++ return ret; ++} ++ ++/** ++ * nand_check_wp - [GENERIC] check if the chip is write protected ++ * @mtd: MTD device structure ++ * ++ * Check, if the device is write protected. The function expects, that the ++ * device is already selected. ++ */ ++static int nand_check_wp(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u8 status; ++ int ret; ++ ++ /* Broken xD cards report WP despite being writable */ ++ if (chip->options & NAND_BROKEN_XD) ++ return 0; ++ ++ /* Check the WP bit */ ++ ret = nand_status_op(chip, &status); ++ if (ret) ++ return ret; ++ ++ return status & NAND_STATUS_WP ? 0 : 1; ++} ++ ++/** ++ * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * ++ * Check if the block is marked as reserved. ++ */ ++static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (!chip->bbt) ++ return 0; ++ /* Return info from the table */ ++ return nand_isreserved_bbt(mtd, ofs); ++} ++ ++/** ++ * nand_block_checkbad - [GENERIC] Check if a block is marked bad ++ * @mtd: MTD device structure ++ * @ofs: offset from device start ++ * @allowbbt: 1, if its allowed to access the bbt area ++ * ++ * Check, if the block is bad. Either by reading the bad block table or ++ * calling of the scan function. ++ */ ++static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (!chip->bbt) ++ return chip->block_bad(mtd, ofs); ++ ++ /* Return info from the table */ ++ return nand_isbad_bbt(mtd, ofs, allowbbt); ++} ++ ++/** ++ * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. ++ * @mtd: MTD device structure ++ * @timeo: Timeout ++ * ++ * Helper function for nand_wait_ready used when needing to wait in interrupt ++ * context. ++ */ ++static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int i; ++ ++ /* Wait for the device to get ready */ ++ for (i = 0; i < timeo; i++) { ++ if (chip->dev_ready(mtd)) ++ break; ++ touch_softlockup_watchdog(); ++ mdelay(1); ++ } ++} ++ ++/** ++ * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. ++ * @mtd: MTD device structure ++ * ++ * Wait for the ready pin after a command, and warn if a timeout occurs. ++ */ ++void nand_wait_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ unsigned long timeo = 400; ++ ++ if (in_interrupt() || oops_in_progress) ++ return panic_nand_wait_ready(mtd, timeo); ++ ++ /* Wait until command is processed or timeout occurs */ ++ timeo = jiffies + msecs_to_jiffies(timeo); ++ do { ++ if (chip->dev_ready(mtd)) ++ return; ++ cond_resched(); ++ } while (time_before(jiffies, timeo)); ++ ++ if (!chip->dev_ready(mtd)) ++ pr_warn_ratelimited("timeout while waiting for chip to become ready\n"); ++} ++EXPORT_SYMBOL_GPL(nand_wait_ready); ++ ++/** ++ * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands. ++ * @mtd: MTD device structure ++ * @timeo: Timeout in ms ++ * ++ * Wait for status ready (i.e. command done) or timeout. ++ */ ++static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) ++{ ++ register struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ timeo = jiffies + msecs_to_jiffies(timeo); ++ do { ++ u8 status; ++ ++ ret = nand_read_data_op(chip, &status, sizeof(status), true); ++ if (ret) ++ return; ++ ++ if (status & NAND_STATUS_READY) ++ break; ++ touch_softlockup_watchdog(); ++ } while (time_before(jiffies, timeo)); ++}; ++ ++/** ++ * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1 ++ * @chip: NAND chip structure ++ * @timeout_ms: Timeout in ms ++ * ++ * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1. ++ * If that does not happen whitin the specified timeout, -ETIMEDOUT is ++ * returned. ++ * ++ * This helper is intended to be used when the controller does not have access ++ * to the NAND R/B pin. ++ * ++ * Be aware that calling this helper from an ->exec_op() implementation means ++ * ->exec_op() must be re-entrant. ++ * ++ * Return 0 if the NAND chip is ready, a negative error otherwise. ++ */ ++int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) ++{ ++ u8 status = 0; ++ int ret; ++ ++ if (!chip->exec_op) ++ return -ENOTSUPP; ++ ++ ret = nand_status_op(chip, NULL); ++ if (ret) ++ return ret; ++ ++ timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); ++ do { ++ ret = nand_read_data_op(chip, &status, sizeof(status), true); ++ if (ret) ++ break; ++ ++ if (status & NAND_STATUS_READY) ++ break; ++ ++ /* ++ * Typical lowest execution time for a tR on most NANDs is 10us, ++ * use this as polling delay before doing something smarter (ie. ++ * deriving a delay from the timeout value, timeout_ms/ratio). ++ */ ++ udelay(10); ++ } while (time_before(jiffies, timeout_ms)); ++ ++ /* ++ * We have to exit READ_STATUS mode in order to read real data on the ++ * bus in case the WAITRDY instruction is preceding a DATA_IN ++ * instruction. ++ */ ++ nand_exit_status_op(chip); ++ ++ if (ret) ++ return ret; ++ ++ return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT; ++}; ++EXPORT_SYMBOL_GPL(nand_soft_waitrdy); ++ ++/** ++ * nand_command - [DEFAULT] Send command to NAND device ++ * @mtd: MTD device structure ++ * @command: the command to be sent ++ * @column: the column address for this command, -1 if none ++ * @page_addr: the page address for this command, -1 if none ++ * ++ * Send command to NAND device. This function is used for small page devices ++ * (512 Bytes per page). ++ */ ++static void nand_command(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ register struct nand_chip *chip = mtd_to_nand(mtd); ++ int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; ++ ++ /* Write out the command to the device */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->writesize) { ++ /* OOB area */ ++ column -= mtd->writesize; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ chip->cmd_ctrl(mtd, readcmd, ctrl); ++ ctrl &= ~NAND_CTRL_CHANGE; ++ } ++ if (command != NAND_CMD_NONE) ++ chip->cmd_ctrl(mtd, command, ctrl); ++ ++ /* Address cycle, when necessary */ ++ ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (chip->options & NAND_BUSWIDTH_16 && ++ !nand_opcode_8bits(command)) ++ column >>= 1; ++ chip->cmd_ctrl(mtd, column, ctrl); ++ ctrl &= ~NAND_CTRL_CHANGE; ++ } ++ if (page_addr != -1) { ++ chip->cmd_ctrl(mtd, page_addr, ctrl); ++ ctrl &= ~NAND_CTRL_CHANGE; ++ chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); ++ if (chip->options & NAND_ROW_ADDR_3) ++ chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); ++ } ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ ++ /* ++ * Program and erase have their own busy handlers status and sequential ++ * in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_NONE: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_READID: ++ case NAND_CMD_SET_FEATURES: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (chip->dev_ready) ++ break; ++ udelay(chip->chip_delay); ++ chip->cmd_ctrl(mtd, NAND_CMD_STATUS, ++ NAND_CTRL_CLE | NAND_CTRL_CHANGE); ++ chip->cmd_ctrl(mtd, ++ NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ ++ nand_wait_status_ready(mtd, 250); ++ return; ++ ++ /* This applies to read commands */ ++ case NAND_CMD_READ0: ++ /* ++ * READ0 is sometimes used to exit GET STATUS mode. When this ++ * is the case no address cycles are requested, and we can use ++ * this information to detect that we should not wait for the ++ * device to be ready. ++ */ ++ if (column == -1 && page_addr == -1) ++ return; ++ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!chip->dev_ready) { ++ udelay(chip->chip_delay); ++ return; ++ } ++ } ++ /* ++ * Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. ++ */ ++ ndelay(100); ++ ++ nand_wait_ready(mtd); ++} ++ ++static void nand_ccs_delay(struct nand_chip *chip) ++{ ++ /* ++ * The controller already takes care of waiting for tCCS when the RNDIN ++ * or RNDOUT command is sent, return directly. ++ */ ++ if (!(chip->options & NAND_WAIT_TCCS)) ++ return; ++ ++ /* ++ * Wait tCCS_min if it is correctly defined, otherwise wait 500ns ++ * (which should be safe for all NANDs). ++ */ ++ if (chip->setup_data_interface) ++ ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); ++ else ++ ndelay(500); ++} ++ ++/** ++ * nand_command_lp - [DEFAULT] Send command to NAND large page device ++ * @mtd: MTD device structure ++ * @command: the command to be sent ++ * @column: the column address for this command, -1 if none ++ * @page_addr: the page address for this command, -1 if none ++ * ++ * Send command to NAND device. This is the version for the new large page ++ * devices. We don't have the separate regions as we have in the small page ++ * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. ++ */ ++static void nand_command_lp(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ register struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ /* Emulate NAND_CMD_READOOB */ ++ if (command == NAND_CMD_READOOB) { ++ column += mtd->writesize; ++ command = NAND_CMD_READ0; ++ } ++ ++ /* Command latch cycle */ ++ if (command != NAND_CMD_NONE) ++ chip->cmd_ctrl(mtd, command, ++ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); ++ ++ if (column != -1 || page_addr != -1) { ++ int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; ++ ++ /* Serially input address */ ++ if (column != -1) { ++ /* Adjust columns for 16 bit buswidth */ ++ if (chip->options & NAND_BUSWIDTH_16 && ++ !nand_opcode_8bits(command)) ++ column >>= 1; ++ chip->cmd_ctrl(mtd, column, ctrl); ++ ctrl &= ~NAND_CTRL_CHANGE; ++ ++ /* Only output a single addr cycle for 8bits opcodes. */ ++ if (!nand_opcode_8bits(command)) ++ chip->cmd_ctrl(mtd, column >> 8, ctrl); ++ } ++ if (page_addr != -1) { ++ chip->cmd_ctrl(mtd, page_addr, ctrl); ++ chip->cmd_ctrl(mtd, page_addr >> 8, ++ NAND_NCE | NAND_ALE); ++ if (chip->options & NAND_ROW_ADDR_3) ++ chip->cmd_ctrl(mtd, page_addr >> 16, ++ NAND_NCE | NAND_ALE); ++ } ++ } ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ ++ /* ++ * Program and erase have their own busy handlers status, sequential ++ * in and status need no delay. ++ */ ++ switch (command) { ++ ++ case NAND_CMD_NONE: ++ case NAND_CMD_CACHEDPROG: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_READID: ++ case NAND_CMD_SET_FEATURES: ++ return; ++ ++ case NAND_CMD_RNDIN: ++ nand_ccs_delay(chip); ++ return; ++ ++ case NAND_CMD_RESET: ++ if (chip->dev_ready) ++ break; ++ udelay(chip->chip_delay); ++ chip->cmd_ctrl(mtd, NAND_CMD_STATUS, ++ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, ++ NAND_NCE | NAND_CTRL_CHANGE); ++ /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ ++ nand_wait_status_ready(mtd, 250); ++ return; ++ ++ case NAND_CMD_RNDOUT: ++ /* No ready / busy check necessary */ ++ chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, ++ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, ++ NAND_NCE | NAND_CTRL_CHANGE); ++ ++ nand_ccs_delay(chip); ++ return; ++ ++ case NAND_CMD_READ0: ++ /* ++ * READ0 is sometimes used to exit GET STATUS mode. When this ++ * is the case no address cycles are requested, and we can use ++ * this information to detect that READSTART should not be ++ * issued. ++ */ ++ if (column == -1 && page_addr == -1) ++ return; ++ ++ chip->cmd_ctrl(mtd, NAND_CMD_READSTART, ++ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, ++ NAND_NCE | NAND_CTRL_CHANGE); ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay. ++ */ ++ if (!chip->dev_ready) { ++ udelay(chip->chip_delay); ++ return; ++ } ++ } ++ ++ /* ++ * Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. ++ */ ++ ndelay(100); ++ ++ nand_wait_ready(mtd); ++} ++ ++/** ++ * panic_nand_get_device - [GENERIC] Get chip for selected access ++ * @chip: the nand chip descriptor ++ * @mtd: MTD device structure ++ * @new_state: the state which is requested ++ * ++ * Used when in panic, no locks are taken. ++ */ ++static void panic_nand_get_device(struct nand_chip *chip, ++ struct mtd_info *mtd, int new_state) ++{ ++ /* Hardware controller shared among independent devices */ ++ chip->controller->active = chip; ++ chip->state = new_state; ++} ++ ++/** ++ * nand_get_device - [GENERIC] Get chip for selected access ++ * @mtd: MTD device structure ++ * @new_state: the state which is requested ++ * ++ * Get the device and lock it for exclusive access ++ */ ++static int ++nand_get_device(struct mtd_info *mtd, int new_state) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ spinlock_t *lock = &chip->controller->lock; ++ wait_queue_head_t *wq = &chip->controller->wq; ++ DECLARE_WAITQUEUE(wait, current); ++retry: ++ spin_lock(lock); ++ ++ /* Hardware controller shared among independent devices */ ++ if (!chip->controller->active) ++ chip->controller->active = chip; ++ ++ if (chip->controller->active == chip && chip->state == FL_READY) { ++ chip->state = new_state; ++ spin_unlock(lock); ++ return 0; ++ } ++ if (new_state == FL_PM_SUSPENDED) { ++ if (chip->controller->active->state == FL_PM_SUSPENDED) { ++ chip->state = FL_PM_SUSPENDED; ++ spin_unlock(lock); ++ return 0; ++ } ++ } ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(wq, &wait); ++ spin_unlock(lock); ++ schedule(); ++ remove_wait_queue(wq, &wait); ++ goto retry; ++} ++ ++/** ++ * panic_nand_wait - [GENERIC] wait until the command is done ++ * @mtd: MTD device structure ++ * @chip: NAND chip structure ++ * @timeo: timeout ++ * ++ * Wait for command done. This is a helper function for nand_wait used when ++ * we are in interrupt context. May happen when in panic and trying to write ++ * an oops through mtdoops. ++ */ ++static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, ++ unsigned long timeo) ++{ ++ int i; ++ for (i = 0; i < timeo; i++) { ++ if (chip->dev_ready) { ++ if (chip->dev_ready(mtd)) ++ break; ++ } else { ++ int ret; ++ u8 status; ++ ++ ret = nand_read_data_op(chip, &status, sizeof(status), ++ true); ++ if (ret) ++ return; ++ ++ if (status & NAND_STATUS_READY) ++ break; ++ } ++ mdelay(1); ++ } ++} ++ ++/** ++ * nand_wait - [DEFAULT] wait until the command is done ++ * @mtd: MTD device structure ++ * @chip: NAND chip structure ++ * ++ * Wait for command done. This applies to erase and program only. ++ */ ++static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ ++ unsigned long timeo = 400; ++ u8 status; ++ int ret; ++ ++ /* ++ * Apply this short delay always to ensure that we do wait tWB in any ++ * case on any machine. ++ */ ++ ndelay(100); ++ ++ ret = nand_status_op(chip, NULL); ++ if (ret) ++ return ret; ++ ++ if (in_interrupt() || oops_in_progress) ++ panic_nand_wait(mtd, chip, timeo); ++ else { ++ timeo = jiffies + msecs_to_jiffies(timeo); ++ do { ++ if (chip->dev_ready) { ++ if (chip->dev_ready(mtd)) ++ break; ++ } else { ++ ret = nand_read_data_op(chip, &status, ++ sizeof(status), true); ++ if (ret) ++ return ret; ++ ++ if (status & NAND_STATUS_READY) ++ break; ++ } ++ cond_resched(); ++ } while (time_before(jiffies, timeo)); ++ } ++ ++ ret = nand_read_data_op(chip, &status, sizeof(status), true); ++ if (ret) ++ return ret; ++ ++ /* This can happen if in case of timeout or buggy dev_ready */ ++ WARN_ON(!(status & NAND_STATUS_READY)); ++ return status; ++} ++ ++/** ++ * nand_reset_data_interface - Reset data interface and timings ++ * @chip: The NAND chip ++ * @chipnr: Internal die id ++ * ++ * Reset the Data interface and timings to ONFI mode 0. ++ * ++ * Returns 0 for success or negative error code otherwise. ++ */ ++static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ if (!chip->setup_data_interface) ++ return 0; ++ ++ /* ++ * The ONFI specification says: ++ * " ++ * To transition from NV-DDR or NV-DDR2 to the SDR data ++ * interface, the host shall use the Reset (FFh) command ++ * using SDR timing mode 0. A device in any timing mode is ++ * required to recognize Reset (FFh) command issued in SDR ++ * timing mode 0. ++ * " ++ * ++ * Configure the data interface in SDR mode and set the ++ * timings to timing mode 0. ++ */ ++ ++ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); ++ ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); ++ if (ret) ++ pr_err("Failed to configure data interface to SDR timing mode 0\n"); ++ ++ return ret; ++} ++ ++/** ++ * nand_setup_data_interface - Setup the best data interface and timings ++ * @chip: The NAND chip ++ * @chipnr: Internal die id ++ * ++ * Find and configure the best data interface and NAND timings supported by ++ * the chip and the driver. ++ * First tries to retrieve supported timing modes from ONFI information, ++ * and if the NAND chip does not support ONFI, relies on the ++ * ->onfi_timing_mode_default specified in the nand_ids table. ++ * ++ * Returns 0 for success or negative error code otherwise. ++ */ ++static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ if (!chip->setup_data_interface) ++ return 0; ++ ++ /* ++ * Ensure the timing mode has been changed on the chip side ++ * before changing timings on the controller side. ++ */ ++ if (chip->onfi_version && ++ (le16_to_cpu(chip->onfi_params.opt_cmd) & ++ ONFI_OPT_CMD_SET_GET_FEATURES)) { ++ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { ++ chip->onfi_timing_mode_default, ++ }; ++ ++ ret = chip->onfi_set_features(mtd, chip, ++ ONFI_FEATURE_ADDR_TIMING_MODE, ++ tmode_param); ++ if (ret) ++ goto err; ++ } ++ ++ ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); ++err: ++ return ret; ++} ++ ++/** ++ * nand_init_data_interface - find the best data interface and timings ++ * @chip: The NAND chip ++ * ++ * Find the best data interface and NAND timings supported by the chip ++ * and the driver. ++ * First tries to retrieve supported timing modes from ONFI information, ++ * and if the NAND chip does not support ONFI, relies on the ++ * ->onfi_timing_mode_default specified in the nand_ids table. After this ++ * function nand_chip->data_interface is initialized with the best timing mode ++ * available. ++ * ++ * Returns 0 for success or negative error code otherwise. ++ */ ++static int nand_init_data_interface(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int modes, mode, ret; ++ ++ if (!chip->setup_data_interface) ++ return 0; ++ ++ /* ++ * First try to identify the best timings from ONFI parameters and ++ * if the NAND does not support ONFI, fallback to the default ONFI ++ * timing mode. ++ */ ++ modes = onfi_get_async_timing_mode(chip); ++ if (modes == ONFI_TIMING_MODE_UNKNOWN) { ++ if (!chip->onfi_timing_mode_default) ++ return 0; ++ ++ modes = GENMASK(chip->onfi_timing_mode_default, 0); ++ } ++ ++ ++ for (mode = fls(modes) - 1; mode >= 0; mode--) { ++ ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode); ++ if (ret) ++ continue; ++ ++ /* Pass -1 to only */ ++ ret = chip->setup_data_interface(mtd, ++ NAND_DATA_IFACE_CHECK_ONLY, ++ &chip->data_interface); ++ if (!ret) { ++ chip->onfi_timing_mode_default = mode; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * nand_fill_column_cycles - fill the column cycles of an address ++ * @chip: The NAND chip ++ * @addrs: Array of address cycles to fill ++ * @offset_in_page: The offset in the page ++ * ++ * Fills the first or the first two bytes of the @addrs field depending ++ * on the NAND bus width and the page size. ++ * ++ * Returns the number of cycles needed to encode the column, or a negative ++ * error code in case one of the arguments is invalid. ++ */ ++static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs, ++ unsigned int offset_in_page) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ /* Make sure the offset is less than the actual page size. */ ++ if (offset_in_page > mtd->writesize + mtd->oobsize) ++ return -EINVAL; ++ ++ /* ++ * On small page NANDs, there's a dedicated command to access the OOB ++ * area, and the column address is relative to the start of the OOB ++ * area, not the start of the page. Asjust the address accordingly. ++ */ ++ if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize) ++ offset_in_page -= mtd->writesize; ++ ++ /* ++ * The offset in page is expressed in bytes, if the NAND bus is 16-bit ++ * wide, then it must be divided by 2. ++ */ ++ if (chip->options & NAND_BUSWIDTH_16) { ++ if (WARN_ON(offset_in_page % 2)) ++ return -EINVAL; ++ ++ offset_in_page /= 2; ++ } ++ ++ addrs[0] = offset_in_page; ++ ++ /* ++ * Small page NANDs use 1 cycle for the columns, while large page NANDs ++ * need 2 ++ */ ++ if (mtd->writesize <= 512) ++ return 1; ++ ++ addrs[1] = offset_in_page >> 8; ++ ++ return 2; ++} ++ ++static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, void *buf, ++ unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ u8 addrs[4]; ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_READ0, 0), ++ NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), ++ PSEC_TO_NSEC(sdr->tRR_min)), ++ NAND_OP_DATA_IN(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ int ret; ++ ++ /* Drop the DATA_IN instruction if len is set to 0. */ ++ if (!len) ++ op.ninstrs--; ++ ++ if (offset_in_page >= mtd->writesize) ++ instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; ++ else if (offset_in_page >= 256 && ++ !(chip->options & NAND_BUSWIDTH_16)) ++ instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; ++ ++ ret = nand_fill_column_cycles(chip, addrs, offset_in_page); ++ if (ret < 0) ++ return ret; ++ ++ addrs[1] = page; ++ addrs[2] = page >> 8; ++ ++ if (chip->options & NAND_ROW_ADDR_3) { ++ addrs[3] = page >> 16; ++ instrs[1].ctx.addr.naddrs++; ++ } ++ ++ return nand_exec_op(chip, &op); ++} ++ ++static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, void *buf, ++ unsigned int len) ++{ ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ u8 addrs[5]; ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_READ0, 0), ++ NAND_OP_ADDR(4, addrs, 0), ++ NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), ++ PSEC_TO_NSEC(sdr->tRR_min)), ++ NAND_OP_DATA_IN(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ int ret; ++ ++ /* Drop the DATA_IN instruction if len is set to 0. */ ++ if (!len) ++ op.ninstrs--; ++ ++ ret = nand_fill_column_cycles(chip, addrs, offset_in_page); ++ if (ret < 0) ++ return ret; ++ ++ addrs[2] = page; ++ addrs[3] = page >> 8; ++ ++ if (chip->options & NAND_ROW_ADDR_3) { ++ addrs[4] = page >> 16; ++ instrs[1].ctx.addr.naddrs++; ++ } ++ ++ return nand_exec_op(chip, &op); ++} ++ ++/** ++ * nand_read_page_op - Do a READ PAGE operation ++ * @chip: The NAND chip ++ * @page: page to read ++ * @offset_in_page: offset within the page ++ * @buf: buffer used to store the data ++ * @len: length of the buffer ++ * ++ * This function issues a READ PAGE operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_read_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, void *buf, unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (offset_in_page + len > mtd->writesize + mtd->oobsize) ++ return -EINVAL; ++ ++ if (chip->exec_op) { ++ if (mtd->writesize > 512) ++ return nand_lp_exec_read_page_op(chip, page, ++ offset_in_page, buf, ++ len); ++ ++ return nand_sp_exec_read_page_op(chip, page, offset_in_page, ++ buf, len); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page); ++ if (len) ++ chip->read_buf(mtd, buf, len); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_read_page_op); ++ ++/** ++ * nand_read_param_page_op - Do a READ PARAMETER PAGE operation ++ * @chip: The NAND chip ++ * @page: parameter page to read ++ * @buf: buffer used to store the data ++ * @len: length of the buffer ++ * ++ * This function issues a READ PARAMETER PAGE operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, ++ unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int i; ++ u8 *p = buf; ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_PARAM, 0), ++ NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), ++ PSEC_TO_NSEC(sdr->tRR_min)), ++ NAND_OP_8BIT_DATA_IN(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ /* Drop the DATA_IN instruction if len is set to 0. */ ++ if (!len) ++ op.ninstrs--; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1); ++ for (i = 0; i < len; i++) ++ p[i] = chip->read_byte(mtd); ++ ++ return 0; ++} ++ ++/** ++ * nand_change_read_column_op - Do a CHANGE READ COLUMN operation ++ * @chip: The NAND chip ++ * @offset_in_page: offset within the page ++ * @buf: buffer used to store the data ++ * @len: length of the buffer ++ * @force_8bit: force 8-bit bus access ++ * ++ * This function issues a CHANGE READ COLUMN operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_change_read_column_op(struct nand_chip *chip, ++ unsigned int offset_in_page, void *buf, ++ unsigned int len, bool force_8bit) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (offset_in_page + len > mtd->writesize + mtd->oobsize) ++ return -EINVAL; ++ ++ /* Small page NANDs do not support column change. */ ++ if (mtd->writesize <= 512) ++ return -ENOTSUPP; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ u8 addrs[2] = {}; ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_RNDOUT, 0), ++ NAND_OP_ADDR(2, addrs, 0), ++ NAND_OP_CMD(NAND_CMD_RNDOUTSTART, ++ PSEC_TO_NSEC(sdr->tCCS_min)), ++ NAND_OP_DATA_IN(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ int ret; ++ ++ ret = nand_fill_column_cycles(chip, addrs, offset_in_page); ++ if (ret < 0) ++ return ret; ++ ++ /* Drop the DATA_IN instruction if len is set to 0. */ ++ if (!len) ++ op.ninstrs--; ++ ++ instrs[3].ctx.data.force_8bit = force_8bit; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1); ++ if (len) ++ chip->read_buf(mtd, buf, len); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_change_read_column_op); ++ ++/** ++ * nand_read_oob_op - Do a READ OOB operation ++ * @chip: The NAND chip ++ * @page: page to read ++ * @offset_in_oob: offset within the OOB area ++ * @buf: buffer used to store the data ++ * @len: length of the buffer ++ * ++ * This function issues a READ OOB operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_read_oob_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_oob, void *buf, unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (offset_in_oob + len > mtd->oobsize) ++ return -EINVAL; ++ ++ if (chip->exec_op) ++ return nand_read_page_op(chip, page, ++ mtd->writesize + offset_in_oob, ++ buf, len); ++ ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page); ++ if (len) ++ chip->read_buf(mtd, buf, len); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_read_oob_op); ++ ++static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, const void *buf, ++ unsigned int len, bool prog) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ u8 addrs[5] = {}; ++ struct nand_op_instr instrs[] = { ++ /* ++ * The first instruction will be dropped if we're dealing ++ * with a large page NAND and adjusted if we're dealing ++ * with a small page NAND and the page offset is > 255. ++ */ ++ NAND_OP_CMD(NAND_CMD_READ0, 0), ++ NAND_OP_CMD(NAND_CMD_SEQIN, 0), ++ NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)), ++ NAND_OP_DATA_OUT(len, buf, 0), ++ NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); ++ int ret; ++ u8 status; ++ ++ if (naddrs < 0) ++ return naddrs; ++ ++ addrs[naddrs++] = page; ++ addrs[naddrs++] = page >> 8; ++ if (chip->options & NAND_ROW_ADDR_3) ++ addrs[naddrs++] = page >> 16; ++ ++ instrs[2].ctx.addr.naddrs = naddrs; ++ ++ /* Drop the last two instructions if we're not programming the page. */ ++ if (!prog) { ++ op.ninstrs -= 2; ++ /* Also drop the DATA_OUT instruction if empty. */ ++ if (!len) ++ op.ninstrs--; ++ } ++ ++ if (mtd->writesize <= 512) { ++ /* ++ * Small pages need some more tweaking: we have to adjust the ++ * first instruction depending on the page offset we're trying ++ * to access. ++ */ ++ if (offset_in_page >= mtd->writesize) ++ instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; ++ else if (offset_in_page >= 256 && ++ !(chip->options & NAND_BUSWIDTH_16)) ++ instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; ++ } else { ++ /* ++ * Drop the first command if we're dealing with a large page ++ * NAND. ++ */ ++ op.instrs++; ++ op.ninstrs--; ++ } ++ ++ ret = nand_exec_op(chip, &op); ++ if (!prog || ret) ++ return ret; ++ ++ ret = nand_status_op(chip, &status); ++ if (ret) ++ return ret; ++ ++ return status; ++} ++ ++/** ++ * nand_prog_page_begin_op - starts a PROG PAGE operation ++ * @chip: The NAND chip ++ * @page: page to write ++ * @offset_in_page: offset within the page ++ * @buf: buffer containing the data to write to the page ++ * @len: length of the buffer ++ * ++ * This function issues the first half of a PROG PAGE operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, const void *buf, ++ unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (offset_in_page + len > mtd->writesize + mtd->oobsize) ++ return -EINVAL; ++ ++ if (chip->exec_op) ++ return nand_exec_prog_page_op(chip, page, offset_in_page, buf, ++ len, false); ++ ++ chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); ++ ++ if (buf) ++ chip->write_buf(mtd, buf, len); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_prog_page_begin_op); ++ ++/** ++ * nand_prog_page_end_op - ends a PROG PAGE operation ++ * @chip: The NAND chip ++ * ++ * This function issues the second half of a PROG PAGE operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_prog_page_end_op(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ u8 status; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_PAGEPROG, ++ PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ ret = nand_exec_op(chip, &op); ++ if (ret) ++ return ret; ++ ++ ret = nand_status_op(chip, &status); ++ if (ret) ++ return ret; ++ } else { ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ ret = chip->waitfunc(mtd, chip); ++ if (ret < 0) ++ return ret; ++ ++ status = ret; ++ } ++ ++ if (status & NAND_STATUS_FAIL) ++ return -EIO; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_prog_page_end_op); ++ ++/** ++ * nand_prog_page_op - Do a full PROG PAGE operation ++ * @chip: The NAND chip ++ * @page: page to write ++ * @offset_in_page: offset within the page ++ * @buf: buffer containing the data to write to the page ++ * @len: length of the buffer ++ * ++ * This function issues a full PROG PAGE operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_prog_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, const void *buf, ++ unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int status; ++ ++ if (!len || !buf) ++ return -EINVAL; ++ ++ if (offset_in_page + len > mtd->writesize + mtd->oobsize) ++ return -EINVAL; ++ ++ if (chip->exec_op) { ++ status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, ++ len, true); ++ } else { ++ chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); ++ chip->write_buf(mtd, buf, len); ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ status = chip->waitfunc(mtd, chip); ++ } ++ ++ if (status & NAND_STATUS_FAIL) ++ return -EIO; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_prog_page_op); ++ ++/** ++ * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation ++ * @chip: The NAND chip ++ * @offset_in_page: offset within the page ++ * @buf: buffer containing the data to send to the NAND ++ * @len: length of the buffer ++ * @force_8bit: force 8-bit bus access ++ * ++ * This function issues a CHANGE WRITE COLUMN operation. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_change_write_column_op(struct nand_chip *chip, ++ unsigned int offset_in_page, ++ const void *buf, unsigned int len, ++ bool force_8bit) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (offset_in_page + len > mtd->writesize + mtd->oobsize) ++ return -EINVAL; ++ ++ /* Small page NANDs do not support column change. */ ++ if (mtd->writesize <= 512) ++ return -ENOTSUPP; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ u8 addrs[2]; ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_RNDIN, 0), ++ NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), ++ NAND_OP_DATA_OUT(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ int ret; ++ ++ ret = nand_fill_column_cycles(chip, addrs, offset_in_page); ++ if (ret < 0) ++ return ret; ++ ++ instrs[2].ctx.data.force_8bit = force_8bit; ++ ++ /* Drop the DATA_OUT instruction if len is set to 0. */ ++ if (!len) ++ op.ninstrs--; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1); ++ if (len) ++ chip->write_buf(mtd, buf, len); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_change_write_column_op); ++ ++/** ++ * nand_readid_op - Do a READID operation ++ * @chip: The NAND chip ++ * @addr: address cycle to pass after the READID command ++ * @buf: buffer used to store the ID ++ * @len: length of the buffer ++ * ++ * This function sends a READID command and reads back the ID returned by the ++ * NAND. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, ++ unsigned int len) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int i; ++ u8 *id = buf; ++ ++ if (len && !buf) ++ return -EINVAL; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_READID, 0), ++ NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), ++ NAND_OP_8BIT_DATA_IN(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ /* Drop the DATA_IN instruction if len is set to 0. */ ++ if (!len) ++ op.ninstrs--; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1); ++ ++ for (i = 0; i < len; i++) ++ id[i] = chip->read_byte(mtd); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_readid_op); ++ ++/** ++ * nand_status_op - Do a STATUS operation ++ * @chip: The NAND chip ++ * @status: out variable to store the NAND status ++ * ++ * This function sends a STATUS command and reads back the status returned by ++ * the NAND. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_status_op(struct nand_chip *chip, u8 *status) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_STATUS, ++ PSEC_TO_NSEC(sdr->tADL_min)), ++ NAND_OP_8BIT_DATA_IN(1, status, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ if (!status) ++ op.ninstrs--; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); ++ if (status) ++ *status = chip->read_byte(mtd); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_status_op); ++ ++/** ++ * nand_exit_status_op - Exit a STATUS operation ++ * @chip: The NAND chip ++ * ++ * This function sends a READ0 command to cancel the effect of the STATUS ++ * command to avoid reading only the status until a new read command is sent. ++ * ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_exit_status_op(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (chip->exec_op) { ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_READ0, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_exit_status_op); ++ ++/** ++ * nand_erase_op - Do an erase operation ++ * @chip: The NAND chip ++ * @eraseblock: block to erase ++ * ++ * This function sends an ERASE command and waits for the NAND to be ready ++ * before returning. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ unsigned int page = eraseblock << ++ (chip->phys_erase_shift - chip->page_shift); ++ int ret; ++ u8 status; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ u8 addrs[3] = { page, page >> 8, page >> 16 }; ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_ERASE1, 0), ++ NAND_OP_ADDR(2, addrs, 0), ++ NAND_OP_CMD(NAND_CMD_ERASE2, ++ PSEC_TO_MSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ if (chip->options & NAND_ROW_ADDR_3) ++ instrs[1].ctx.addr.naddrs++; ++ ++ ret = nand_exec_op(chip, &op); ++ if (ret) ++ return ret; ++ ++ ret = nand_status_op(chip, &status); ++ if (ret) ++ return ret; ++ } else { ++ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); ++ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); ++ ++ ret = chip->waitfunc(mtd, chip); ++ if (ret < 0) ++ return ret; ++ ++ status = ret; ++ } ++ ++ if (status & NAND_STATUS_FAIL) ++ return -EIO; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_erase_op); ++ ++/** ++ * nand_set_features_op - Do a SET FEATURES operation ++ * @chip: The NAND chip ++ * @feature: feature id ++ * @data: 4 bytes of data ++ * ++ * This function sends a SET FEATURES command and waits for the NAND to be ++ * ready before returning. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++static int nand_set_features_op(struct nand_chip *chip, u8 feature, ++ const void *data) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const u8 *params = data; ++ int i, ret; ++ u8 status; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0), ++ NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)), ++ NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data, ++ PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ ret = nand_exec_op(chip, &op); ++ if (ret) ++ return ret; ++ ++ ret = nand_status_op(chip, &status); ++ if (ret) ++ return ret; ++ } else { ++ chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); ++ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) ++ chip->write_byte(mtd, params[i]); ++ ++ ret = chip->waitfunc(mtd, chip); ++ if (ret < 0) ++ return ret; ++ ++ status = ret; ++ } ++ ++ if (status & NAND_STATUS_FAIL) ++ return -EIO; ++ ++ return 0; ++} ++ ++/** ++ * nand_get_features_op - Do a GET FEATURES operation ++ * @chip: The NAND chip ++ * @feature: feature id ++ * @data: 4 bytes of data ++ * ++ * This function sends a GET FEATURES command and waits for the NAND to be ++ * ready before returning. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++static int nand_get_features_op(struct nand_chip *chip, u8 feature, ++ void *data) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 *params = data; ++ int i; ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0), ++ NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), ++ PSEC_TO_NSEC(sdr->tRR_min)), ++ NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, ++ data, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1); ++ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) ++ params[i] = chip->read_byte(mtd); ++ ++ return 0; ++} ++ ++/** ++ * nand_reset_op - Do a reset operation ++ * @chip: The NAND chip ++ * ++ * This function sends a RESET command and waits for the NAND to be ready ++ * before returning. ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_reset_op(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (chip->exec_op) { ++ const struct nand_sdr_timings *sdr = ++ nand_get_sdr_timings(&chip->data_interface); ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), ++ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_reset_op); ++ ++/** ++ * nand_read_data_op - Read data from the NAND ++ * @chip: The NAND chip ++ * @buf: buffer used to store the data ++ * @len: length of the buffer ++ * @force_8bit: force 8-bit bus access ++ * ++ * This function does a raw data read on the bus. Usually used after launching ++ * another NAND operation like nand_read_page_op(). ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, ++ bool force_8bit) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (!len || !buf) ++ return -EINVAL; ++ ++ if (chip->exec_op) { ++ struct nand_op_instr instrs[] = { ++ NAND_OP_DATA_IN(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ instrs[0].ctx.data.force_8bit = force_8bit; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ if (force_8bit) { ++ u8 *p = buf; ++ unsigned int i; ++ ++ for (i = 0; i < len; i++) ++ p[i] = chip->read_byte(mtd); ++ } else { ++ chip->read_buf(mtd, buf, len); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_read_data_op); ++ ++/** ++ * nand_write_data_op - Write data from the NAND ++ * @chip: The NAND chip ++ * @buf: buffer containing the data to send on the bus ++ * @len: length of the buffer ++ * @force_8bit: force 8-bit bus access ++ * ++ * This function does a raw data write on the bus. Usually used after launching ++ * another NAND operation like nand_write_page_begin_op(). ++ * This function does not select/unselect the CS line. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_write_data_op(struct nand_chip *chip, const void *buf, ++ unsigned int len, bool force_8bit) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (!len || !buf) ++ return -EINVAL; ++ ++ if (chip->exec_op) { ++ struct nand_op_instr instrs[] = { ++ NAND_OP_DATA_OUT(len, buf, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ instrs[0].ctx.data.force_8bit = force_8bit; ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ if (force_8bit) { ++ const u8 *p = buf; ++ unsigned int i; ++ ++ for (i = 0; i < len; i++) ++ chip->write_byte(mtd, p[i]); ++ } else { ++ chip->write_buf(mtd, buf, len); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_write_data_op); ++ ++/** ++ * struct nand_op_parser_ctx - Context used by the parser ++ * @instrs: array of all the instructions that must be addressed ++ * @ninstrs: length of the @instrs array ++ * @subop: Sub-operation to be passed to the NAND controller ++ * ++ * This structure is used by the core to split NAND operations into ++ * sub-operations that can be handled by the NAND controller. ++ */ ++struct nand_op_parser_ctx { ++ const struct nand_op_instr *instrs; ++ unsigned int ninstrs; ++ struct nand_subop subop; ++}; ++ ++/** ++ * nand_op_parser_must_split_instr - Checks if an instruction must be split ++ * @pat: the parser pattern element that matches @instr ++ * @instr: pointer to the instruction to check ++ * @start_offset: this is an in/out parameter. If @instr has already been ++ * split, then @start_offset is the offset from which to start ++ * (either an address cycle or an offset in the data buffer). ++ * Conversely, if the function returns true (ie. instr must be ++ * split), this parameter is updated to point to the first ++ * data/address cycle that has not been taken care of. ++ * ++ * Some NAND controllers are limited and cannot send X address cycles with a ++ * unique operation, or cannot read/write more than Y bytes at the same time. ++ * In this case, split the instruction that does not fit in a single ++ * controller-operation into two or more chunks. ++ * ++ * Returns true if the instruction must be split, false otherwise. ++ * The @start_offset parameter is also updated to the offset at which the next ++ * bundle of instruction must start (if an address or a data instruction). ++ */ ++static bool ++nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat, ++ const struct nand_op_instr *instr, ++ unsigned int *start_offset) ++{ ++ switch (pat->type) { ++ case NAND_OP_ADDR_INSTR: ++ if (!pat->addr.maxcycles) ++ break; ++ ++ if (instr->ctx.addr.naddrs - *start_offset > ++ pat->addr.maxcycles) { ++ *start_offset += pat->addr.maxcycles; ++ return true; ++ } ++ break; ++ ++ case NAND_OP_DATA_IN_INSTR: ++ case NAND_OP_DATA_OUT_INSTR: ++ if (!pat->data.maxlen) ++ break; ++ ++ if (instr->ctx.data.len - *start_offset > pat->data.maxlen) { ++ *start_offset += pat->data.maxlen; ++ return true; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ return false; ++} ++ ++/** ++ * nand_op_parser_match_pat - Checks if a pattern matches the instructions ++ * remaining in the parser context ++ * @pat: the pattern to test ++ * @ctx: the parser context structure to match with the pattern @pat ++ * ++ * Check if @pat matches the set or a sub-set of instructions remaining in @ctx. ++ * Returns true if this is the case, false ortherwise. When true is returned, ++ * @ctx->subop is updated with the set of instructions to be passed to the ++ * controller driver. ++ */ ++static bool ++nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat, ++ struct nand_op_parser_ctx *ctx) ++{ ++ unsigned int instr_offset = ctx->subop.first_instr_start_off; ++ const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs; ++ const struct nand_op_instr *instr = ctx->subop.instrs; ++ unsigned int i, ninstrs; ++ ++ for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) { ++ /* ++ * The pattern instruction does not match the operation ++ * instruction. If the instruction is marked optional in the ++ * pattern definition, we skip the pattern element and continue ++ * to the next one. If the element is mandatory, there's no ++ * match and we can return false directly. ++ */ ++ if (instr->type != pat->elems[i].type) { ++ if (!pat->elems[i].optional) ++ return false; ++ ++ continue; ++ } ++ ++ /* ++ * Now check the pattern element constraints. If the pattern is ++ * not able to handle the whole instruction in a single step, ++ * we have to split it. ++ * The last_instr_end_off value comes back updated to point to ++ * the position where we have to split the instruction (the ++ * start of the next subop chunk). ++ */ ++ if (nand_op_parser_must_split_instr(&pat->elems[i], instr, ++ &instr_offset)) { ++ ninstrs++; ++ i++; ++ break; ++ } ++ ++ instr++; ++ ninstrs++; ++ instr_offset = 0; ++ } ++ ++ /* ++ * This can happen if all instructions of a pattern are optional. ++ * Still, if there's not at least one instruction handled by this ++ * pattern, this is not a match, and we should try the next one (if ++ * any). ++ */ ++ if (!ninstrs) ++ return false; ++ ++ /* ++ * We had a match on the pattern head, but the pattern may be longer ++ * than the instructions we're asked to execute. We need to make sure ++ * there's no mandatory elements in the pattern tail. ++ */ ++ for (; i < pat->nelems; i++) { ++ if (!pat->elems[i].optional) ++ return false; ++ } ++ ++ /* ++ * We have a match: update the subop structure accordingly and return ++ * true. ++ */ ++ ctx->subop.ninstrs = ninstrs; ++ ctx->subop.last_instr_end_off = instr_offset; ++ ++ return true; ++} ++ ++#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) ++static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) ++{ ++ const struct nand_op_instr *instr; ++ char *prefix = " "; ++ unsigned int i; ++ ++ pr_debug("executing subop:\n"); ++ ++ for (i = 0; i < ctx->ninstrs; i++) { ++ instr = &ctx->instrs[i]; ++ ++ if (instr == &ctx->subop.instrs[0]) ++ prefix = " ->"; ++ ++ switch (instr->type) { ++ case NAND_OP_CMD_INSTR: ++ pr_debug("%sCMD [0x%02x]\n", prefix, ++ instr->ctx.cmd.opcode); ++ break; ++ case NAND_OP_ADDR_INSTR: ++ pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, ++ instr->ctx.addr.naddrs, ++ instr->ctx.addr.naddrs < 64 ? ++ instr->ctx.addr.naddrs : 64, ++ instr->ctx.addr.addrs); ++ break; ++ case NAND_OP_DATA_IN_INSTR: ++ pr_debug("%sDATA_IN [%d B%s]\n", prefix, ++ instr->ctx.data.len, ++ instr->ctx.data.force_8bit ? ++ ", force 8-bit" : ""); ++ break; ++ case NAND_OP_DATA_OUT_INSTR: ++ pr_debug("%sDATA_OUT [%d B%s]\n", prefix, ++ instr->ctx.data.len, ++ instr->ctx.data.force_8bit ? ++ ", force 8-bit" : ""); ++ break; ++ case NAND_OP_WAITRDY_INSTR: ++ pr_debug("%sWAITRDY [max %d ms]\n", prefix, ++ instr->ctx.waitrdy.timeout_ms); ++ break; ++ } ++ ++ if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) ++ prefix = " "; ++ } ++} ++#else ++static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) ++{ ++ /* NOP */ ++} ++#endif ++ ++/** ++ * nand_op_parser_exec_op - exec_op parser ++ * @chip: the NAND chip ++ * @parser: patterns description provided by the controller driver ++ * @op: the NAND operation to address ++ * @check_only: when true, the function only checks if @op can be handled but ++ * does not execute the operation ++ * ++ * Helper function designed to ease integration of NAND controller drivers that ++ * only support a limited set of instruction sequences. The supported sequences ++ * are described in @parser, and the framework takes care of splitting @op into ++ * multiple sub-operations (if required) and pass them back to the ->exec() ++ * callback of the matching pattern if @check_only is set to false. ++ * ++ * NAND controller drivers should call this function from their own ->exec_op() ++ * implementation. ++ * ++ * Returns 0 on success, a negative error code otherwise. A failure can be ++ * caused by an unsupported operation (none of the supported patterns is able ++ * to handle the requested operation), or an error returned by one of the ++ * matching pattern->exec() hook. ++ */ ++int nand_op_parser_exec_op(struct nand_chip *chip, ++ const struct nand_op_parser *parser, ++ const struct nand_operation *op, bool check_only) ++{ ++ struct nand_op_parser_ctx ctx = { ++ .subop.instrs = op->instrs, ++ .instrs = op->instrs, ++ .ninstrs = op->ninstrs, ++ }; ++ unsigned int i; ++ ++ while (ctx.subop.instrs < op->instrs + op->ninstrs) { ++ int ret; ++ ++ for (i = 0; i < parser->npatterns; i++) { ++ const struct nand_op_parser_pattern *pattern; ++ ++ pattern = &parser->patterns[i]; ++ if (!nand_op_parser_match_pat(pattern, &ctx)) ++ continue; ++ ++ nand_op_parser_trace(&ctx); ++ ++ if (check_only) ++ break; ++ ++ ret = pattern->exec(chip, &ctx.subop); ++ if (ret) ++ return ret; ++ ++ break; ++ } ++ ++ if (i == parser->npatterns) { ++ pr_debug("->exec_op() parser: pattern not found!\n"); ++ return -ENOTSUPP; ++ } ++ ++ /* ++ * Update the context structure by pointing to the start of the ++ * next subop. ++ */ ++ ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs; ++ if (ctx.subop.last_instr_end_off) ++ ctx.subop.instrs -= 1; ++ ++ ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_op_parser_exec_op); ++ ++static bool nand_instr_is_data(const struct nand_op_instr *instr) ++{ ++ return instr && (instr->type == NAND_OP_DATA_IN_INSTR || ++ instr->type == NAND_OP_DATA_OUT_INSTR); ++} ++ ++static bool nand_subop_instr_is_valid(const struct nand_subop *subop, ++ unsigned int instr_idx) ++{ ++ return subop && instr_idx < subop->ninstrs; ++} ++ ++static unsigned int nand_subop_get_start_off(const struct nand_subop *subop, ++ unsigned int instr_idx) ++{ ++ if (instr_idx) ++ return 0; ++ ++ return subop->first_instr_start_off; ++} ++ ++/** ++ * nand_subop_get_addr_start_off - Get the start offset in an address array ++ * @subop: The entire sub-operation ++ * @instr_idx: Index of the instruction inside the sub-operation ++ * ++ * During driver development, one could be tempted to directly use the ++ * ->addr.addrs field of address instructions. This is wrong as address ++ * instructions might be split. ++ * ++ * Given an address instruction, returns the offset of the first cycle to issue. ++ */ ++unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop, ++ unsigned int instr_idx) ++{ ++ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || ++ subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)) ++ return 0; ++ ++ return nand_subop_get_start_off(subop, instr_idx); ++} ++EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off); ++ ++/** ++ * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert ++ * @subop: The entire sub-operation ++ * @instr_idx: Index of the instruction inside the sub-operation ++ * ++ * During driver development, one could be tempted to directly use the ++ * ->addr->naddrs field of a data instruction. This is wrong as instructions ++ * might be split. ++ * ++ * Given an address instruction, returns the number of address cycle to issue. ++ */ ++unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, ++ unsigned int instr_idx) ++{ ++ int start_off, end_off; ++ ++ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || ++ subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)) ++ return 0; ++ ++ start_off = nand_subop_get_addr_start_off(subop, instr_idx); ++ ++ if (instr_idx == subop->ninstrs - 1 && ++ subop->last_instr_end_off) ++ end_off = subop->last_instr_end_off; ++ else ++ end_off = subop->instrs[instr_idx].ctx.addr.naddrs; ++ ++ return end_off - start_off; ++} ++EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc); ++ ++/** ++ * nand_subop_get_data_start_off - Get the start offset in a data array ++ * @subop: The entire sub-operation ++ * @instr_idx: Index of the instruction inside the sub-operation ++ * ++ * During driver development, one could be tempted to directly use the ++ * ->data->buf.{in,out} field of data instructions. This is wrong as data ++ * instructions might be split. ++ * ++ * Given a data instruction, returns the offset to start from. ++ */ ++unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop, ++ unsigned int instr_idx) ++{ ++ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || ++ !nand_instr_is_data(&subop->instrs[instr_idx]))) ++ return 0; ++ ++ return nand_subop_get_start_off(subop, instr_idx); ++} ++EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off); ++ ++/** ++ * nand_subop_get_data_len - Get the number of bytes to retrieve ++ * @subop: The entire sub-operation ++ * @instr_idx: Index of the instruction inside the sub-operation ++ * ++ * During driver development, one could be tempted to directly use the ++ * ->data->len field of a data instruction. This is wrong as data instructions ++ * might be split. ++ * ++ * Returns the length of the chunk of data to send/receive. ++ */ ++unsigned int nand_subop_get_data_len(const struct nand_subop *subop, ++ unsigned int instr_idx) ++{ ++ int start_off = 0, end_off; ++ ++ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || ++ !nand_instr_is_data(&subop->instrs[instr_idx]))) ++ return 0; ++ ++ start_off = nand_subop_get_data_start_off(subop, instr_idx); ++ ++ if (instr_idx == subop->ninstrs - 1 && ++ subop->last_instr_end_off) ++ end_off = subop->last_instr_end_off; ++ else ++ end_off = subop->instrs[instr_idx].ctx.data.len; ++ ++ return end_off - start_off; ++} ++EXPORT_SYMBOL_GPL(nand_subop_get_data_len); ++ ++/** ++ * nand_reset - Reset and initialize a NAND device ++ * @chip: The NAND chip ++ * @chipnr: Internal die id ++ * ++ * Save the timings data structure, then apply SDR timings mode 0 (see ++ * nand_reset_data_interface for details), do the reset operation, and ++ * apply back the previous timings. ++ * ++ * Returns 0 on success, a negative error code otherwise. ++ */ ++int nand_reset(struct nand_chip *chip, int chipnr) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct nand_data_interface saved_data_intf = chip->data_interface; ++ int ret; ++ ++ ret = nand_reset_data_interface(chip, chipnr); ++ if (ret) ++ return ret; ++ ++ /* ++ * The CS line has to be released before we can apply the new NAND ++ * interface settings, hence this weird ->select_chip() dance. ++ */ ++ chip->select_chip(mtd, chipnr); ++ ret = nand_reset_op(chip); ++ chip->select_chip(mtd, -1); ++ if (ret) ++ return ret; ++ ++ chip->select_chip(mtd, chipnr); ++ chip->data_interface = saved_data_intf; ++ ret = nand_setup_data_interface(chip, chipnr); ++ chip->select_chip(mtd, -1); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_reset); ++ ++/** ++ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data ++ * @buf: buffer to test ++ * @len: buffer length ++ * @bitflips_threshold: maximum number of bitflips ++ * ++ * Check if a buffer contains only 0xff, which means the underlying region ++ * has been erased and is ready to be programmed. ++ * The bitflips_threshold specify the maximum number of bitflips before ++ * considering the region is not erased. ++ * Note: The logic of this function has been extracted from the memweight ++ * implementation, except that nand_check_erased_buf function exit before ++ * testing the whole buffer if the number of bitflips exceed the ++ * bitflips_threshold value. ++ * ++ * Returns a positive number of bitflips less than or equal to ++ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the ++ * threshold. ++ */ ++static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) ++{ ++ const unsigned char *bitmap = buf; ++ int bitflips = 0; ++ int weight; ++ ++ for (; len && ((uintptr_t)bitmap) % sizeof(long); ++ len--, bitmap++) { ++ weight = hweight8(*bitmap); ++ bitflips += BITS_PER_BYTE - weight; ++ if (unlikely(bitflips > bitflips_threshold)) ++ return -EBADMSG; ++ } ++ ++ for (; len >= sizeof(long); ++ len -= sizeof(long), bitmap += sizeof(long)) { ++ unsigned long d = *((unsigned long *)bitmap); ++ if (d == ~0UL) ++ continue; ++ weight = hweight_long(d); ++ bitflips += BITS_PER_LONG - weight; ++ if (unlikely(bitflips > bitflips_threshold)) ++ return -EBADMSG; ++ } ++ ++ for (; len > 0; len--, bitmap++) { ++ weight = hweight8(*bitmap); ++ bitflips += BITS_PER_BYTE - weight; ++ if (unlikely(bitflips > bitflips_threshold)) ++ return -EBADMSG; ++ } ++ ++ return bitflips; ++} ++ ++/** ++ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only ++ * 0xff data ++ * @data: data buffer to test ++ * @datalen: data length ++ * @ecc: ECC buffer ++ * @ecclen: ECC length ++ * @extraoob: extra OOB buffer ++ * @extraooblen: extra OOB length ++ * @bitflips_threshold: maximum number of bitflips ++ * ++ * Check if a data buffer and its associated ECC and OOB data contains only ++ * 0xff pattern, which means the underlying region has been erased and is ++ * ready to be programmed. ++ * The bitflips_threshold specify the maximum number of bitflips before ++ * considering the region as not erased. ++ * ++ * Note: ++ * 1/ ECC algorithms are working on pre-defined block sizes which are usually ++ * different from the NAND page size. When fixing bitflips, ECC engines will ++ * report the number of errors per chunk, and the NAND core infrastructure ++ * expect you to return the maximum number of bitflips for the whole page. ++ * This is why you should always use this function on a single chunk and ++ * not on the whole page. After checking each chunk you should update your ++ * max_bitflips value accordingly. ++ * 2/ When checking for bitflips in erased pages you should not only check ++ * the payload data but also their associated ECC data, because a user might ++ * have programmed almost all bits to 1 but a few. In this case, we ++ * shouldn't consider the chunk as erased, and checking ECC bytes prevent ++ * this case. ++ * 3/ The extraoob argument is optional, and should be used if some of your OOB ++ * data are protected by the ECC engine. ++ * It could also be used if you support subpages and want to attach some ++ * extra OOB data to an ECC chunk. ++ * ++ * Returns a positive number of bitflips less than or equal to ++ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the ++ * threshold. In case of success, the passed buffers are filled with 0xff. ++ */ ++int nand_check_erased_ecc_chunk(void *data, int datalen, ++ void *ecc, int ecclen, ++ void *extraoob, int extraooblen, ++ int bitflips_threshold) ++{ ++ int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; ++ ++ data_bitflips = nand_check_erased_buf(data, datalen, ++ bitflips_threshold); ++ if (data_bitflips < 0) ++ return data_bitflips; ++ ++ bitflips_threshold -= data_bitflips; ++ ++ ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); ++ if (ecc_bitflips < 0) ++ return ecc_bitflips; ++ ++ bitflips_threshold -= ecc_bitflips; ++ ++ extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, ++ bitflips_threshold); ++ if (extraoob_bitflips < 0) ++ return extraoob_bitflips; ++ ++ if (data_bitflips) ++ memset(data, 0xff, datalen); ++ ++ if (ecc_bitflips) ++ memset(ecc, 0xff, ecclen); ++ ++ if (extraoob_bitflips) ++ memset(extraoob, 0xff, extraooblen); ++ ++ return data_bitflips + ecc_bitflips + extraoob_bitflips; ++} ++EXPORT_SYMBOL(nand_check_erased_ecc_chunk); ++ ++/** ++ * nand_read_page_raw - [INTERN] read raw page data without ecc ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * Not for syndrome calculating ECC controllers, which use a special oob layout. ++ */ ++int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int ret; ++ ++ ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ if (ret) ++ return ret; ++ ++ if (oob_required) { ++ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, ++ false); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_read_page_raw); ++ ++/** ++ * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * We need a special oob layout and handling even when OOB isn't used. ++ */ ++static int nand_read_page_raw_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ int oob_required, int page) ++{ ++ int eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ uint8_t *oob = chip->oob_poi; ++ int steps, size, ret; ++ ++ ret = nand_read_page_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (steps = chip->ecc.steps; steps > 0; steps--) { ++ ret = nand_read_data_op(chip, buf, eccsize, false); ++ if (ret) ++ return ret; ++ ++ buf += eccsize; ++ ++ if (chip->ecc.prepad) { ++ ret = nand_read_data_op(chip, oob, chip->ecc.prepad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.prepad; ++ } ++ ++ ret = nand_read_data_op(chip, oob, eccbytes, false); ++ if (ret) ++ return ret; ++ ++ oob += eccbytes; ++ ++ if (chip->ecc.postpad) { ++ ret = nand_read_data_op(chip, oob, chip->ecc.postpad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.postpad; ++ } ++ } ++ ++ size = mtd->oobsize - (oob - chip->oob_poi); ++ if (size) { ++ ret = nand_read_data_op(chip, oob, size, false); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ */ ++static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ uint8_t *ecc_code = chip->ecc.code_buf; ++ unsigned int max_bitflips = 0; ++ ++ chip->ecc.read_page_raw(mtd, chip, buf, 1, page); ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ eccsteps = chip->ecc.steps; ++ p = buf; ++ ++ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ return max_bitflips; ++} ++ ++/** ++ * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @data_offs: offset of requested data within the page ++ * @readlen: data length ++ * @bufpoi: buffer to store read data ++ * @page: page number to read ++ */ ++static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, ++ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, ++ int page) ++{ ++ int start_step, end_step, num_steps, ret; ++ uint8_t *p; ++ int data_col_addr, i, gaps = 0; ++ int datafrag_len, eccfrag_len, aligned_len, aligned_pos; ++ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; ++ int index, section = 0; ++ unsigned int max_bitflips = 0; ++ struct mtd_oob_region oobregion = { }; ++ ++ /* Column address within the page aligned to ECC size (256bytes) */ ++ start_step = data_offs / chip->ecc.size; ++ end_step = (data_offs + readlen - 1) / chip->ecc.size; ++ num_steps = end_step - start_step + 1; ++ index = start_step * chip->ecc.bytes; ++ ++ /* Data size aligned to ECC ecc.size */ ++ datafrag_len = num_steps * chip->ecc.size; ++ eccfrag_len = num_steps * chip->ecc.bytes; ++ ++ data_col_addr = start_step * chip->ecc.size; ++ /* If we read not a page aligned data */ ++ p = bufpoi + data_col_addr; ++ ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len); ++ if (ret) ++ return ret; ++ ++ /* Calculate ECC */ ++ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) ++ chip->ecc.calculate(mtd, p, &chip->ecc.calc_buf[i]); ++ ++ /* ++ * The performance is faster if we position offsets according to ++ * ecc.pos. Let's make sure that there are no gaps in ECC positions. ++ */ ++ ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion); ++ if (ret) ++ return ret; ++ ++ if (oobregion.length < eccfrag_len) ++ gaps = 1; ++ ++ if (gaps) { ++ ret = nand_change_read_column_op(chip, mtd->writesize, ++ chip->oob_poi, mtd->oobsize, ++ false); ++ if (ret) ++ return ret; ++ } else { ++ /* ++ * Send the command to read the particular ECC bytes take care ++ * about buswidth alignment in read_buf. ++ */ ++ aligned_pos = oobregion.offset & ~(busw - 1); ++ aligned_len = eccfrag_len; ++ if (oobregion.offset & (busw - 1)) ++ aligned_len++; ++ if ((oobregion.offset + (num_steps * chip->ecc.bytes)) & ++ (busw - 1)) ++ aligned_len++; ++ ++ ret = nand_change_read_column_op(chip, ++ mtd->writesize + aligned_pos, ++ &chip->oob_poi[aligned_pos], ++ aligned_len, false); ++ if (ret) ++ return ret; ++ } ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf, ++ chip->oob_poi, index, eccfrag_len); ++ if (ret) ++ return ret; ++ ++ p = bufpoi + data_col_addr; ++ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { ++ int stat; ++ ++ stat = chip->ecc.correct(mtd, p, &chip->ecc.code_buf[i], ++ &chip->ecc.calc_buf[i]); ++ if (stat == -EBADMSG && ++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { ++ /* check for empty pages with bitflips */ ++ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, ++ &chip->ecc.code_buf[i], ++ chip->ecc.bytes, ++ NULL, 0, ++ chip->ecc.strength); ++ } ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ return max_bitflips; ++} ++ ++/** ++ * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * Not for syndrome calculating ECC controllers which need a special oob layout. ++ */ ++static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ uint8_t *ecc_code = chip->ecc.code_buf; ++ unsigned int max_bitflips = 0; ++ ++ ret = nand_read_page_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ ++ ret = nand_read_data_op(chip, p, eccsize, false); ++ if (ret) ++ return ret; ++ ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ } ++ ++ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false); ++ if (ret) ++ return ret; ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ eccsteps = chip->ecc.steps; ++ p = buf; ++ ++ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat == -EBADMSG && ++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { ++ /* check for empty pages with bitflips */ ++ stat = nand_check_erased_ecc_chunk(p, eccsize, ++ &ecc_code[i], eccbytes, ++ NULL, 0, ++ chip->ecc.strength); ++ } ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ return max_bitflips; ++} ++ ++/** ++ * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * Hardware ECC for large page chips, require OOB to be read first. For this ++ * ECC mode, the write_page method is re-used from ECC_HW. These methods ++ * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with ++ * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from ++ * the data area, by overwriting the NAND manufacturer bad block markings. ++ */ ++static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, int oob_required, int page) ++{ ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_code = chip->ecc.code_buf; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ unsigned int max_bitflips = 0; ++ ++ /* Read the OOB area first */ ++ ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); ++ if (ret) ++ return ret; ++ ++ ret = nand_read_page_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ ++ ret = nand_read_data_op(chip, p, eccsize, false); ++ if (ret) ++ return ret; ++ ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); ++ if (stat == -EBADMSG && ++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { ++ /* check for empty pages with bitflips */ ++ stat = nand_check_erased_ecc_chunk(p, eccsize, ++ &ecc_code[i], eccbytes, ++ NULL, 0, ++ chip->ecc.strength); ++ } ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ return max_bitflips; ++} ++ ++/** ++ * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * The hw generator calculates the error syndrome automatically. Therefore we ++ * need a special oob layout and handling. ++ */ ++static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int ret, i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; ++ uint8_t *p = buf; ++ uint8_t *oob = chip->oob_poi; ++ unsigned int max_bitflips = 0; ++ ++ ret = nand_read_page_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ ++ ret = nand_read_data_op(chip, p, eccsize, false); ++ if (ret) ++ return ret; ++ ++ if (chip->ecc.prepad) { ++ ret = nand_read_data_op(chip, oob, chip->ecc.prepad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.prepad; ++ } ++ ++ chip->ecc.hwctl(mtd, NAND_ECC_READSYN); ++ ++ ret = nand_read_data_op(chip, oob, eccbytes, false); ++ if (ret) ++ return ret; ++ ++ stat = chip->ecc.correct(mtd, p, oob, NULL); ++ ++ oob += eccbytes; ++ ++ if (chip->ecc.postpad) { ++ ret = nand_read_data_op(chip, oob, chip->ecc.postpad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.postpad; ++ } ++ ++ if (stat == -EBADMSG && ++ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { ++ /* check for empty pages with bitflips */ ++ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, ++ oob - eccpadbytes, ++ eccpadbytes, ++ NULL, 0, ++ chip->ecc.strength); ++ } ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ ++ /* Calculate remaining oob bytes */ ++ i = mtd->oobsize - (oob - chip->oob_poi); ++ if (i) { ++ ret = nand_read_data_op(chip, oob, i, false); ++ if (ret) ++ return ret; ++ } ++ ++ return max_bitflips; ++} ++ ++/** ++ * nand_transfer_oob - [INTERN] Transfer oob to client buffer ++ * @mtd: mtd info structure ++ * @oob: oob destination address ++ * @ops: oob ops structure ++ * @len: size of oob to transfer ++ */ ++static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, ++ struct mtd_oob_ops *ops, size_t len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ switch (ops->mode) { ++ ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_RAW: ++ memcpy(oob, chip->oob_poi + ops->ooboffs, len); ++ return oob + len; ++ ++ case MTD_OPS_AUTO_OOB: ++ ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi, ++ ops->ooboffs, len); ++ BUG_ON(ret); ++ return oob + len; ++ ++ default: ++ BUG(); ++ } ++ return NULL; ++} ++ ++/** ++ * nand_setup_read_retry - [INTERN] Set the READ RETRY mode ++ * @mtd: MTD device structure ++ * @retry_mode: the retry mode to use ++ * ++ * Some vendors supply a special command to shift the Vt threshold, to be used ++ * when there are too many bitflips in a page (i.e., ECC error). After setting ++ * a new threshold, the host should retry reading the page. ++ */ ++static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ pr_debug("setting READ RETRY mode %d\n", retry_mode); ++ ++ if (retry_mode >= chip->read_retries) ++ return -EINVAL; ++ ++ if (!chip->setup_read_retry) ++ return -EOPNOTSUPP; ++ ++ return chip->setup_read_retry(mtd, retry_mode); ++} ++ ++/** ++ * nand_do_read_ops - [INTERN] Read data with ECC ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @ops: oob ops structure ++ * ++ * Internal function. Called with chip held. ++ */ ++static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ int chipnr, page, realpage, col, bytes, aligned, oob_required; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret = 0; ++ uint32_t readlen = ops->len; ++ uint32_t oobreadlen = ops->ooblen; ++ uint32_t max_oobsize = mtd_oobavail(mtd, ops); ++ ++ uint8_t *bufpoi, *oob, *buf; ++ int use_bufpoi; ++ unsigned int max_bitflips = 0; ++ int retry_mode = 0; ++ bool ecc_fail = false; ++ ++ chipnr = (int)(from >> chip->chip_shift); ++ chip->select_chip(mtd, chipnr); ++ ++ realpage = (int)(from >> chip->page_shift); ++ page = realpage & chip->pagemask; ++ ++ col = (int)(from & (mtd->writesize - 1)); ++ ++ buf = ops->datbuf; ++ oob = ops->oobbuf; ++ oob_required = oob ? 1 : 0; ++ ++ while (1) { ++ unsigned int ecc_failures = mtd->ecc_stats.failed; ++ ++ bytes = min(mtd->writesize - col, readlen); ++ aligned = (bytes == mtd->writesize); ++ ++ if (!aligned) ++ use_bufpoi = 1; ++ else if (chip->options & NAND_USE_BOUNCE_BUFFER) ++ use_bufpoi = !virt_addr_valid(buf) || ++ !IS_ALIGNED((unsigned long)buf, ++ chip->buf_align); ++ else ++ use_bufpoi = 0; ++ ++ /* Is the current page in the buffer? */ ++ if (realpage != chip->pagebuf || oob) { ++ bufpoi = use_bufpoi ? chip->data_buf : buf; ++ ++ if (use_bufpoi && aligned) ++ pr_debug("%s: using read bounce buffer for buf@%p\n", ++ __func__, buf); ++ ++read_retry: ++ /* ++ * Now read the page into the buffer. Absent an error, ++ * the read methods return max bitflips per ecc step. ++ */ ++ if (unlikely(ops->mode == MTD_OPS_RAW)) ++ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, ++ oob_required, ++ page); ++ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && ++ !oob) ++ ret = chip->ecc.read_subpage(mtd, chip, ++ col, bytes, bufpoi, ++ page); ++ else ++ ret = chip->ecc.read_page(mtd, chip, bufpoi, ++ oob_required, page); ++ if (ret < 0) { ++ if (use_bufpoi) ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ break; ++ } ++ ++ /* Transfer not aligned data */ ++ if (use_bufpoi) { ++ if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && ++ !(mtd->ecc_stats.failed - ecc_failures) && ++ (ops->mode != MTD_OPS_RAW)) { ++ chip->pagebuf = realpage; ++ chip->pagebuf_bitflips = ret; ++ } else { ++ /* Invalidate page cache */ ++ chip->pagebuf = -1; ++ } ++ memcpy(buf, chip->data_buf + col, bytes); ++ } ++ ++ if (unlikely(oob)) { ++ int toread = min(oobreadlen, max_oobsize); ++ ++ if (toread) { ++ oob = nand_transfer_oob(mtd, ++ oob, ops, toread); ++ oobreadlen -= toread; ++ } ++ } ++ ++ if (chip->options & NAND_NEED_READRDY) { ++ /* Apply delay or wait for ready/busy pin */ ++ if (!chip->dev_ready) ++ udelay(chip->chip_delay); ++ else ++ nand_wait_ready(mtd); ++ } ++ ++ if (mtd->ecc_stats.failed - ecc_failures) { ++ if (retry_mode + 1 < chip->read_retries) { ++ retry_mode++; ++ ret = nand_setup_read_retry(mtd, ++ retry_mode); ++ if (ret < 0) ++ break; ++ ++ /* Reset failures; retry */ ++ mtd->ecc_stats.failed = ecc_failures; ++ goto read_retry; ++ } else { ++ /* No more retry modes; real failure */ ++ ecc_fail = true; ++ } ++ } ++ ++ buf += bytes; ++ max_bitflips = max_t(unsigned int, max_bitflips, ret); ++ } else { ++ memcpy(buf, chip->data_buf + col, bytes); ++ buf += bytes; ++ max_bitflips = max_t(unsigned int, max_bitflips, ++ chip->pagebuf_bitflips); ++ } ++ ++ readlen -= bytes; ++ ++ /* Reset to retry mode 0 */ ++ if (retry_mode) { ++ ret = nand_setup_read_retry(mtd, 0); ++ if (ret < 0) ++ break; ++ retry_mode = 0; ++ } ++ ++ if (!readlen) ++ break; ++ ++ /* For subsequent reads align to page boundary */ ++ col = 0; ++ /* Increment page address */ ++ realpage++; ++ ++ page = realpage & chip->pagemask; ++ /* Check, if we cross a chip boundary */ ++ if (!page) { ++ chipnr++; ++ chip->select_chip(mtd, -1); ++ chip->select_chip(mtd, chipnr); ++ } ++ } ++ chip->select_chip(mtd, -1); ++ ++ ops->retlen = ops->len - (size_t) readlen; ++ if (oob) ++ ops->oobretlen = ops->ooblen - oobreadlen; ++ ++ if (ret < 0) ++ return ret; ++ ++ if (ecc_fail) ++ return -EBADMSG; ++ ++ return max_bitflips; ++} ++ ++/** ++ * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @retlen: pointer to variable to store the number of read bytes ++ * @buf: the databuffer to put data ++ * ++ * Get hold of the chip and call nand_do_read. ++ */ ++static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, uint8_t *buf) ++{ ++ struct mtd_oob_ops ops; ++ int ret; ++ ++ nand_get_device(mtd, FL_READING); ++ memset(&ops, 0, sizeof(ops)); ++ ops.len = len; ++ ops.datbuf = buf; ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ret = nand_do_read_ops(mtd, from, &ops); ++ *retlen = ops.retlen; ++ nand_release_device(mtd); ++ return ret; ++} ++ ++/** ++ * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to read ++ */ ++int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) ++{ ++ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); ++} ++EXPORT_SYMBOL(nand_read_oob_std); ++ ++/** ++ * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC ++ * with syndromes ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to read ++ */ ++int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ int length = mtd->oobsize; ++ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; ++ int eccsize = chip->ecc.size; ++ uint8_t *bufpoi = chip->oob_poi; ++ int i, toread, sndrnd = 0, pos, ret; ++ ++ ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < chip->ecc.steps; i++) { ++ if (sndrnd) { ++ int ret; ++ ++ pos = eccsize + i * (eccsize + chunk); ++ if (mtd->writesize > 512) ++ ret = nand_change_read_column_op(chip, pos, ++ NULL, 0, ++ false); ++ else ++ ret = nand_read_page_op(chip, page, pos, NULL, ++ 0); ++ ++ if (ret) ++ return ret; ++ } else ++ sndrnd = 1; ++ toread = min_t(int, length, chunk); ++ ++ ret = nand_read_data_op(chip, bufpoi, toread, false); ++ if (ret) ++ return ret; ++ ++ bufpoi += toread; ++ length -= toread; ++ } ++ if (length > 0) { ++ ret = nand_read_data_op(chip, bufpoi, length, false); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_read_oob_syndrome); ++ ++/** ++ * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to write ++ */ ++int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) ++{ ++ return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, ++ mtd->oobsize); ++} ++EXPORT_SYMBOL(nand_write_oob_std); ++ ++/** ++ * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC ++ * with syndrome - only for large page flash ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @page: page number to write ++ */ ++int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; ++ int eccsize = chip->ecc.size, length = mtd->oobsize; ++ int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps; ++ const uint8_t *bufpoi = chip->oob_poi; ++ ++ /* ++ * data-ecc-data-ecc ... ecc-oob ++ * or ++ * data-pad-ecc-pad-data-pad .... ecc-pad-oob ++ */ ++ if (!chip->ecc.prepad && !chip->ecc.postpad) { ++ pos = steps * (eccsize + chunk); ++ steps = 0; ++ } else ++ pos = eccsize; ++ ++ ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < steps; i++) { ++ if (sndcmd) { ++ if (mtd->writesize <= 512) { ++ uint32_t fill = 0xFFFFFFFF; ++ ++ len = eccsize; ++ while (len > 0) { ++ int num = min_t(int, len, 4); ++ ++ ret = nand_write_data_op(chip, &fill, ++ num, false); ++ if (ret) ++ return ret; ++ ++ len -= num; ++ } ++ } else { ++ pos = eccsize + i * (eccsize + chunk); ++ ret = nand_change_write_column_op(chip, pos, ++ NULL, 0, ++ false); ++ if (ret) ++ return ret; ++ } ++ } else ++ sndcmd = 1; ++ len = min_t(int, length, chunk); ++ ++ ret = nand_write_data_op(chip, bufpoi, len, false); ++ if (ret) ++ return ret; ++ ++ bufpoi += len; ++ length -= len; ++ } ++ if (length > 0) { ++ ret = nand_write_data_op(chip, bufpoi, length, false); ++ if (ret) ++ return ret; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++EXPORT_SYMBOL(nand_write_oob_syndrome); ++ ++/** ++ * nand_do_read_oob - [INTERN] NAND read out-of-band ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @ops: oob operations description structure ++ * ++ * NAND read out-of-band data from the spare area. ++ */ ++static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ unsigned int max_bitflips = 0; ++ int page, realpage, chipnr; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct mtd_ecc_stats stats; ++ int readlen = ops->ooblen; ++ int len; ++ uint8_t *buf = ops->oobbuf; ++ int ret = 0; ++ ++ pr_debug("%s: from = 0x%08Lx, len = %i\n", ++ __func__, (unsigned long long)from, readlen); ++ ++ stats = mtd->ecc_stats; ++ ++ len = mtd_oobavail(mtd, ops); ++ ++ if (unlikely(ops->ooboffs >= len)) { ++ pr_debug("%s: attempt to start read outside oob\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ /* Do not allow reads past end of device */ ++ if (unlikely(from >= mtd->size || ++ ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - ++ (from >> chip->page_shift)) * len)) { ++ pr_debug("%s: attempt to read beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ chipnr = (int)(from >> chip->chip_shift); ++ chip->select_chip(mtd, chipnr); ++ ++ /* Shift to get page */ ++ realpage = (int)(from >> chip->page_shift); ++ page = realpage & chip->pagemask; ++ ++ while (1) { ++ if (ops->mode == MTD_OPS_RAW) ++ ret = chip->ecc.read_oob_raw(mtd, chip, page); ++ else ++ ret = chip->ecc.read_oob(mtd, chip, page); ++ ++ if (ret < 0) ++ break; ++ ++ len = min(len, readlen); ++ buf = nand_transfer_oob(mtd, buf, ops, len); ++ ++ if (chip->options & NAND_NEED_READRDY) { ++ /* Apply delay or wait for ready/busy pin */ ++ if (!chip->dev_ready) ++ udelay(chip->chip_delay); ++ else ++ nand_wait_ready(mtd); ++ } ++ ++ max_bitflips = max_t(unsigned int, max_bitflips, ret); ++ ++ readlen -= len; ++ if (!readlen) ++ break; ++ ++ /* Increment page address */ ++ realpage++; ++ ++ page = realpage & chip->pagemask; ++ /* Check, if we cross a chip boundary */ ++ if (!page) { ++ chipnr++; ++ chip->select_chip(mtd, -1); ++ chip->select_chip(mtd, chipnr); ++ } ++ } ++ chip->select_chip(mtd, -1); ++ ++ ops->oobretlen = ops->ooblen - readlen; ++ ++ if (ret < 0) ++ return ret; ++ ++ if (mtd->ecc_stats.failed - stats.failed) ++ return -EBADMSG; ++ ++ return max_bitflips; ++} ++ ++/** ++ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band ++ * @mtd: MTD device structure ++ * @from: offset to read from ++ * @ops: oob operation description structure ++ * ++ * NAND read data and/or out-of-band data. ++ */ ++static int nand_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ int ret; ++ ++ ops->retlen = 0; ++ ++ /* Do not allow reads past end of device */ ++ if (ops->datbuf && (from + ops->len) > mtd->size) { ++ pr_debug("%s: attempt to read beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->mode != MTD_OPS_PLACE_OOB && ++ ops->mode != MTD_OPS_AUTO_OOB && ++ ops->mode != MTD_OPS_RAW) ++ return -ENOTSUPP; ++ ++ nand_get_device(mtd, FL_READING); ++ ++ if (!ops->datbuf) ++ ret = nand_do_read_oob(mtd, from, ops); ++ else ++ ret = nand_do_read_ops(mtd, from, ops); ++ ++ nand_release_device(mtd); ++ return ret; ++} ++ ++ ++/** ++ * nand_write_page_raw - [INTERN] raw page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ * ++ * Not for syndrome calculating ECC controllers, which use a special oob layout. ++ */ ++int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ int ret; ++ ++ ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ if (ret) ++ return ret; ++ ++ if (oob_required) { ++ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, ++ false); ++ if (ret) ++ return ret; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++EXPORT_SYMBOL(nand_write_page_raw); ++ ++/** ++ * nand_write_page_raw_syndrome - [INTERN] raw page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ * ++ * We need a special oob layout and handling even when ECC isn't checked. ++ */ ++static int nand_write_page_raw_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ int eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ uint8_t *oob = chip->oob_poi; ++ int steps, size, ret; ++ ++ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (steps = chip->ecc.steps; steps > 0; steps--) { ++ ret = nand_write_data_op(chip, buf, eccsize, false); ++ if (ret) ++ return ret; ++ ++ buf += eccsize; ++ ++ if (chip->ecc.prepad) { ++ ret = nand_write_data_op(chip, oob, chip->ecc.prepad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.prepad; ++ } ++ ++ ret = nand_write_data_op(chip, oob, eccbytes, false); ++ if (ret) ++ return ret; ++ ++ oob += eccbytes; ++ ++ if (chip->ecc.postpad) { ++ ret = nand_write_data_op(chip, oob, chip->ecc.postpad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.postpad; ++ } ++ } ++ ++ size = mtd->oobsize - (oob - chip->oob_poi); ++ if (size) { ++ ret = nand_write_data_op(chip, oob, size, false); ++ if (ret) ++ return ret; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++/** ++ * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ */ ++static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ const uint8_t *p = buf; ++ ++ /* Software ECC calculation */ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ ++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); ++} ++ ++/** ++ * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ */ ++static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ int i, eccsize = chip->ecc.size, ret; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ const uint8_t *p = buf; ++ ++ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ ++ ret = nand_write_data_op(chip, p, eccsize, false); ++ if (ret) ++ return ret; ++ ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ } ++ ++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); ++ if (ret) ++ return ret; ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++ ++/** ++ * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @offset: column address of subpage within the page ++ * @data_len: data length ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ */ ++static int nand_write_subpage_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, uint32_t offset, ++ uint32_t data_len, const uint8_t *buf, ++ int oob_required, int page) ++{ ++ uint8_t *oob_buf = chip->oob_poi; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ int ecc_size = chip->ecc.size; ++ int ecc_bytes = chip->ecc.bytes; ++ int ecc_steps = chip->ecc.steps; ++ uint32_t start_step = offset / ecc_size; ++ uint32_t end_step = (offset + data_len - 1) / ecc_size; ++ int oob_bytes = mtd->oobsize / ecc_steps; ++ int step, ret; ++ ++ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (step = 0; step < ecc_steps; step++) { ++ /* configure controller for WRITE access */ ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ ++ /* write data (untouched subpages already masked by 0xFF) */ ++ ret = nand_write_data_op(chip, buf, ecc_size, false); ++ if (ret) ++ return ret; ++ ++ /* mask ECC of un-touched subpages by padding 0xFF */ ++ if ((step < start_step) || (step > end_step)) ++ memset(ecc_calc, 0xff, ecc_bytes); ++ else ++ chip->ecc.calculate(mtd, buf, ecc_calc); ++ ++ /* mask OOB of un-touched subpages by padding 0xFF */ ++ /* if oob_required, preserve OOB metadata of written subpage */ ++ if (!oob_required || (step < start_step) || (step > end_step)) ++ memset(oob_buf, 0xff, oob_bytes); ++ ++ buf += ecc_size; ++ ecc_calc += ecc_bytes; ++ oob_buf += oob_bytes; ++ } ++ ++ /* copy calculated ECC for whole page to chip->buffer->oob */ ++ /* this include masked-value(0xFF) for unwritten subpages */ ++ ecc_calc = chip->ecc.calc_buf; ++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ /* write OOB buffer to NAND device */ ++ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); ++ if (ret) ++ return ret; ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++ ++/** ++ * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ * ++ * The hw generator calculates the error syndrome automatically. Therefore we ++ * need a special oob layout and handling. ++ */ ++static int nand_write_page_syndrome(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ const uint8_t *p = buf; ++ uint8_t *oob = chip->oob_poi; ++ int ret; ++ ++ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ ++ ret = nand_write_data_op(chip, p, eccsize, false); ++ if (ret) ++ return ret; ++ ++ if (chip->ecc.prepad) { ++ ret = nand_write_data_op(chip, oob, chip->ecc.prepad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.prepad; ++ } ++ ++ chip->ecc.calculate(mtd, p, oob); ++ ++ ret = nand_write_data_op(chip, oob, eccbytes, false); ++ if (ret) ++ return ret; ++ ++ oob += eccbytes; ++ ++ if (chip->ecc.postpad) { ++ ret = nand_write_data_op(chip, oob, chip->ecc.postpad, ++ false); ++ if (ret) ++ return ret; ++ ++ oob += chip->ecc.postpad; ++ } ++ } ++ ++ /* Calculate remaining oob bytes */ ++ i = mtd->oobsize - (oob - chip->oob_poi); ++ if (i) { ++ ret = nand_write_data_op(chip, oob, i, false); ++ if (ret) ++ return ret; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/** ++ * nand_write_page - write one page ++ * @mtd: MTD device structure ++ * @chip: NAND chip descriptor ++ * @offset: address offset within the page ++ * @data_len: length of actual data to be written ++ * @buf: the data to write ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ * @raw: use _raw version of write_page ++ */ ++static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint32_t offset, int data_len, const uint8_t *buf, ++ int oob_required, int page, int raw) ++{ ++ int status, subpage; ++ ++ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && ++ chip->ecc.write_subpage) ++ subpage = offset || (data_len < mtd->writesize); ++ else ++ subpage = 0; ++ ++ if (unlikely(raw)) ++ status = chip->ecc.write_page_raw(mtd, chip, buf, ++ oob_required, page); ++ else if (subpage) ++ status = chip->ecc.write_subpage(mtd, chip, offset, data_len, ++ buf, oob_required, page); ++ else ++ status = chip->ecc.write_page(mtd, chip, buf, oob_required, ++ page); ++ ++ if (status < 0) ++ return status; ++ ++ return 0; ++} ++ ++/** ++ * nand_fill_oob - [INTERN] Transfer client buffer to oob ++ * @mtd: MTD device structure ++ * @oob: oob data buffer ++ * @len: oob data write length ++ * @ops: oob ops structure ++ */ ++static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, ++ struct mtd_oob_ops *ops) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ /* ++ * Initialise to all 0xFF, to avoid the possibility of left over OOB ++ * data from a previous OOB read. ++ */ ++ memset(chip->oob_poi, 0xff, mtd->oobsize); ++ ++ switch (ops->mode) { ++ ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_RAW: ++ memcpy(chip->oob_poi + ops->ooboffs, oob, len); ++ return oob + len; ++ ++ case MTD_OPS_AUTO_OOB: ++ ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi, ++ ops->ooboffs, len); ++ BUG_ON(ret); ++ return oob + len; ++ ++ default: ++ BUG(); ++ } ++ return NULL; ++} ++ ++#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) ++ ++/** ++ * nand_do_write_ops - [INTERN] NAND write with ECC ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @ops: oob operations description structure ++ * ++ * NAND write with ECC. ++ */ ++static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ int chipnr, realpage, page, column; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ uint32_t writelen = ops->len; ++ ++ uint32_t oobwritelen = ops->ooblen; ++ uint32_t oobmaxlen = mtd_oobavail(mtd, ops); ++ ++ uint8_t *oob = ops->oobbuf; ++ uint8_t *buf = ops->datbuf; ++ int ret; ++ int oob_required = oob ? 1 : 0; ++ ++ ops->retlen = 0; ++ if (!writelen) ++ return 0; ++ ++ /* Reject writes, which are not page aligned */ ++ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { ++ pr_notice("%s: attempt to write non page aligned data\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ column = to & (mtd->writesize - 1); ++ ++ chipnr = (int)(to >> chip->chip_shift); ++ chip->select_chip(mtd, chipnr); ++ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) { ++ ret = -EIO; ++ goto err_out; ++ } ++ ++ realpage = (int)(to >> chip->page_shift); ++ page = realpage & chip->pagemask; ++ ++ /* Invalidate the page cache, when we write to the cached page */ ++ if (to <= ((loff_t)chip->pagebuf << chip->page_shift) && ++ ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len)) ++ chip->pagebuf = -1; ++ ++ /* Don't allow multipage oob writes with offset */ ++ if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ while (1) { ++ int bytes = mtd->writesize; ++ uint8_t *wbuf = buf; ++ int use_bufpoi; ++ int part_pagewr = (column || writelen < mtd->writesize); ++ ++ if (part_pagewr) ++ use_bufpoi = 1; ++ else if (chip->options & NAND_USE_BOUNCE_BUFFER) ++ use_bufpoi = !virt_addr_valid(buf) || ++ !IS_ALIGNED((unsigned long)buf, ++ chip->buf_align); ++ else ++ use_bufpoi = 0; ++ ++ /* Partial page write?, or need to use bounce buffer */ ++ if (use_bufpoi) { ++ pr_debug("%s: using write bounce buffer for buf@%p\n", ++ __func__, buf); ++ if (part_pagewr) ++ bytes = min_t(int, bytes - column, writelen); ++ chip->pagebuf = -1; ++ memset(chip->data_buf, 0xff, mtd->writesize); ++ memcpy(&chip->data_buf[column], buf, bytes); ++ wbuf = chip->data_buf; ++ } ++ ++ if (unlikely(oob)) { ++ size_t len = min(oobwritelen, oobmaxlen); ++ oob = nand_fill_oob(mtd, oob, len, ops); ++ oobwritelen -= len; ++ } else { ++ /* We still need to erase leftover OOB data */ ++ memset(chip->oob_poi, 0xff, mtd->oobsize); ++ } ++ ++ ret = nand_write_page(mtd, chip, column, bytes, wbuf, ++ oob_required, page, ++ (ops->mode == MTD_OPS_RAW)); ++ if (ret) ++ break; ++ ++ writelen -= bytes; ++ if (!writelen) ++ break; ++ ++ column = 0; ++ buf += bytes; ++ realpage++; ++ ++ page = realpage & chip->pagemask; ++ /* Check, if we cross a chip boundary */ ++ if (!page) { ++ chipnr++; ++ chip->select_chip(mtd, -1); ++ chip->select_chip(mtd, chipnr); ++ } ++ } ++ ++ ops->retlen = ops->len - writelen; ++ if (unlikely(oob)) ++ ops->oobretlen = ops->ooblen; ++ ++err_out: ++ chip->select_chip(mtd, -1); ++ return ret; ++} ++ ++/** ++ * panic_nand_write - [MTD Interface] NAND write with ECC ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @retlen: pointer to variable to store the number of written bytes ++ * @buf: the data to write ++ * ++ * NAND write with ECC. Used when performing writes in interrupt context, this ++ * may for example be called by mtdoops when writing an oops while in panic. ++ */ ++static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const uint8_t *buf) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int chipnr = (int)(to >> chip->chip_shift); ++ struct mtd_oob_ops ops; ++ int ret; ++ ++ /* Grab the device */ ++ panic_nand_get_device(chip, mtd, FL_WRITING); ++ ++ chip->select_chip(mtd, chipnr); ++ ++ /* Wait for the device to get ready */ ++ panic_nand_wait(mtd, chip, 400); ++ ++ memset(&ops, 0, sizeof(ops)); ++ ops.len = len; ++ ops.datbuf = (uint8_t *)buf; ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ++ ret = nand_do_write_ops(mtd, to, &ops); ++ ++ *retlen = ops.retlen; ++ return ret; ++} ++ ++/** ++ * nand_write - [MTD Interface] NAND write with ECC ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @retlen: pointer to variable to store the number of written bytes ++ * @buf: the data to write ++ * ++ * NAND write with ECC. ++ */ ++static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const uint8_t *buf) ++{ ++ struct mtd_oob_ops ops; ++ int ret; ++ ++ nand_get_device(mtd, FL_WRITING); ++ memset(&ops, 0, sizeof(ops)); ++ ops.len = len; ++ ops.datbuf = (uint8_t *)buf; ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ret = nand_do_write_ops(mtd, to, &ops); ++ *retlen = ops.retlen; ++ nand_release_device(mtd); ++ return ret; ++} ++ ++/** ++ * nand_do_write_oob - [MTD Interface] NAND write out-of-band ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @ops: oob operation description structure ++ * ++ * NAND write out-of-band. ++ */ ++static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ int chipnr, page, status, len; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ pr_debug("%s: to = 0x%08x, len = %i\n", ++ __func__, (unsigned int)to, (int)ops->ooblen); ++ ++ len = mtd_oobavail(mtd, ops); ++ ++ /* Do not allow write past end of page */ ++ if ((ops->ooboffs + ops->ooblen) > len) { ++ pr_debug("%s: attempt to write past end of page\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(ops->ooboffs >= len)) { ++ pr_debug("%s: attempt to start write outside oob\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ /* Do not allow write past end of device */ ++ if (unlikely(to >= mtd->size || ++ ops->ooboffs + ops->ooblen > ++ ((mtd->size >> chip->page_shift) - ++ (to >> chip->page_shift)) * len)) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ chipnr = (int)(to >> chip->chip_shift); ++ ++ /* ++ * Reset the chip. Some chips (like the Toshiba TC5832DC found in one ++ * of my DiskOnChip 2000 test units) will clear the whole data page too ++ * if we don't do this. I have no clue why, but I seem to have 'fixed' ++ * it in the doc2000 driver in August 1999. dwmw2. ++ */ ++ nand_reset(chip, chipnr); ++ ++ chip->select_chip(mtd, chipnr); ++ ++ /* Shift to get page */ ++ page = (int)(to >> chip->page_shift); ++ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) { ++ chip->select_chip(mtd, -1); ++ return -EROFS; ++ } ++ ++ /* Invalidate the page cache, if we write to the cached page */ ++ if (page == chip->pagebuf) ++ chip->pagebuf = -1; ++ ++ nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); ++ ++ if (ops->mode == MTD_OPS_RAW) ++ status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); ++ else ++ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); ++ ++ chip->select_chip(mtd, -1); ++ ++ if (status) ++ return status; ++ ++ ops->oobretlen = ops->ooblen; ++ ++ return 0; ++} ++ ++/** ++ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band ++ * @mtd: MTD device structure ++ * @to: offset to write to ++ * @ops: oob operation description structure ++ */ ++static int nand_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ int ret = -ENOTSUPP; ++ ++ ops->retlen = 0; ++ ++ /* Do not allow writes past end of device */ ++ if (ops->datbuf && (to + ops->len) > mtd->size) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ nand_get_device(mtd, FL_WRITING); ++ ++ switch (ops->mode) { ++ case MTD_OPS_PLACE_OOB: ++ case MTD_OPS_AUTO_OOB: ++ case MTD_OPS_RAW: ++ break; ++ ++ default: ++ goto out; ++ } ++ ++ if (!ops->datbuf) ++ ret = nand_do_write_oob(mtd, to, ops); ++ else ++ ret = nand_do_write_ops(mtd, to, ops); ++ ++out: ++ nand_release_device(mtd); ++ return ret; ++} ++ ++/** ++ * single_erase - [GENERIC] NAND standard block erase command function ++ * @mtd: MTD device structure ++ * @page: the page address of the block which will be erased ++ * ++ * Standard erase command for NAND chips. Returns NAND status. ++ */ ++static int single_erase(struct mtd_info *mtd, int page) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ unsigned int eraseblock; ++ ++ /* Send commands to erase a block */ ++ eraseblock = page >> (chip->phys_erase_shift - chip->page_shift); ++ ++ return nand_erase_op(chip, eraseblock); ++} ++ ++/** ++ * nand_erase - [MTD Interface] erase block(s) ++ * @mtd: MTD device structure ++ * @instr: erase instruction ++ * ++ * Erase one ore more blocks. ++ */ ++static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ return nand_erase_nand(mtd, instr, 0); ++} ++ ++/** ++ * nand_erase_nand - [INTERN] erase block(s) ++ * @mtd: MTD device structure ++ * @instr: erase instruction ++ * @allowbbt: allow erasing the bbt area ++ * ++ * Erase one ore more blocks. ++ */ ++int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, ++ int allowbbt) ++{ ++ int page, status, pages_per_block, ret, chipnr; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ loff_t len; ++ ++ pr_debug("%s: start = 0x%012llx, len = %llu\n", ++ __func__, (unsigned long long)instr->addr, ++ (unsigned long long)instr->len); ++ ++ if (check_offs_len(mtd, instr->addr, instr->len)) ++ return -EINVAL; ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device(mtd, FL_ERASING); ++ ++ /* Shift to get first page */ ++ page = (int)(instr->addr >> chip->page_shift); ++ chipnr = (int)(instr->addr >> chip->chip_shift); ++ ++ /* Calculate pages in each block */ ++ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); ++ ++ /* Select the NAND device */ ++ chip->select_chip(mtd, chipnr); ++ ++ /* Check, if it is write protected */ ++ if (nand_check_wp(mtd)) { ++ pr_debug("%s: device is write protected!\n", ++ __func__); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_exit; ++ } ++ ++ /* Loop through the pages */ ++ len = instr->len; ++ ++ instr->state = MTD_ERASING; ++ ++ while (len) { ++ /* Check if we have a bad block, we do not erase bad blocks! */ ++ if (nand_block_checkbad(mtd, ((loff_t) page) << ++ chip->page_shift, allowbbt)) { ++ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", ++ __func__, page); ++ instr->state = MTD_ERASE_FAILED; ++ goto erase_exit; ++ } ++ ++ /* ++ * Invalidate the page cache, if we erase the block which ++ * contains the current cached page. ++ */ ++ if (page <= chip->pagebuf && chip->pagebuf < ++ (page + pages_per_block)) ++ chip->pagebuf = -1; ++ ++ status = chip->erase(mtd, page & chip->pagemask); ++ ++ /* See if block erase succeeded */ ++ if (status) { ++ pr_debug("%s: failed erase, page 0x%08x\n", ++ __func__, page); ++ instr->state = MTD_ERASE_FAILED; ++ instr->fail_addr = ++ ((loff_t)page << chip->page_shift); ++ goto erase_exit; ++ } ++ ++ /* Increment page address and decrement length */ ++ len -= (1ULL << chip->phys_erase_shift); ++ page += pages_per_block; ++ ++ /* Check, if we cross a chip boundary */ ++ if (len && !(page & chip->pagemask)) { ++ chipnr++; ++ chip->select_chip(mtd, -1); ++ chip->select_chip(mtd, chipnr); ++ } ++ } ++ instr->state = MTD_ERASE_DONE; ++ ++erase_exit: ++ ++ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; ++ ++ /* Deselect and wake up anyone waiting on the device */ ++ chip->select_chip(mtd, -1); ++ nand_release_device(mtd); ++ ++ /* Do call back function */ ++ if (!ret) ++ mtd_erase_callback(instr); ++ ++ /* Return more or less happy */ ++ return ret; ++} ++ ++/** ++ * nand_sync - [MTD Interface] sync ++ * @mtd: MTD device structure ++ * ++ * Sync is actually a wait for chip ready function. ++ */ ++static void nand_sync(struct mtd_info *mtd) ++{ ++ pr_debug("%s: called\n", __func__); ++ ++ /* Grab the lock and see if the device is available */ ++ nand_get_device(mtd, FL_SYNCING); ++ /* Release it and go back */ ++ nand_release_device(mtd); ++} ++ ++/** ++ * nand_block_isbad - [MTD Interface] Check if block at offset is bad ++ * @mtd: MTD device structure ++ * @offs: offset relative to mtd start ++ */ ++static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int chipnr = (int)(offs >> chip->chip_shift); ++ int ret; ++ ++ /* Select the NAND device */ ++ nand_get_device(mtd, FL_READING); ++ chip->select_chip(mtd, chipnr); ++ ++ ret = nand_block_checkbad(mtd, offs, 0); ++ ++ chip->select_chip(mtd, -1); ++ nand_release_device(mtd); ++ ++ return ret; ++} ++ ++/** ++ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad ++ * @mtd: MTD device structure ++ * @ofs: offset relative to mtd start ++ */ ++static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ int ret; ++ ++ ret = nand_block_isbad(mtd, ofs); ++ if (ret) { ++ /* If it was bad already, return success and do nothing */ ++ if (ret > 0) ++ return 0; ++ return ret; ++ } ++ ++ return nand_block_markbad_lowlevel(mtd, ofs); ++} ++ ++/** ++ * nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd ++ * @mtd: MTD device structure ++ * @ofs: offset relative to mtd start ++ * @len: length of mtd ++ */ ++static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u32 part_start_block; ++ u32 part_end_block; ++ u32 part_start_die; ++ u32 part_end_die; ++ ++ /* ++ * max_bb_per_die and blocks_per_die used to determine ++ * the maximum bad block count. ++ */ ++ if (!chip->max_bb_per_die || !chip->blocks_per_die) ++ return -ENOTSUPP; ++ ++ /* Get the start and end of the partition in erase blocks. */ ++ part_start_block = mtd_div_by_eb(ofs, mtd); ++ part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1; ++ ++ /* Get the start and end LUNs of the partition. */ ++ part_start_die = part_start_block / chip->blocks_per_die; ++ part_end_die = part_end_block / chip->blocks_per_die; ++ ++ /* ++ * Look up the bad blocks per unit and multiply by the number of units ++ * that the partition spans. ++ */ ++ return chip->max_bb_per_die * (part_end_die - part_start_die + 1); ++} ++ ++/** ++ * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand ++ * @mtd: MTD device structure ++ * @chip: nand chip info structure ++ * @addr: feature address. ++ * @subfeature_param: the subfeature parameters, a four bytes array. ++ */ ++static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, ++ int addr, uint8_t *subfeature_param) ++{ ++ if (!chip->onfi_version || ++ !(le16_to_cpu(chip->onfi_params.opt_cmd) ++ & ONFI_OPT_CMD_SET_GET_FEATURES)) ++ return -EINVAL; ++ ++ return nand_set_features_op(chip, addr, subfeature_param); ++} ++ ++/** ++ * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand ++ * @mtd: MTD device structure ++ * @chip: nand chip info structure ++ * @addr: feature address. ++ * @subfeature_param: the subfeature parameters, a four bytes array. ++ */ ++static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, ++ int addr, uint8_t *subfeature_param) ++{ ++ if (!chip->onfi_version || ++ !(le16_to_cpu(chip->onfi_params.opt_cmd) ++ & ONFI_OPT_CMD_SET_GET_FEATURES)) ++ return -EINVAL; ++ ++ return nand_get_features_op(chip, addr, subfeature_param); ++} ++ ++/** ++ * nand_onfi_get_set_features_notsupp - set/get features stub returning ++ * -ENOTSUPP ++ * @mtd: MTD device structure ++ * @chip: nand chip info structure ++ * @addr: feature address. ++ * @subfeature_param: the subfeature parameters, a four bytes array. ++ * ++ * Should be used by NAND controller drivers that do not support the SET/GET ++ * FEATURES operations. ++ */ ++int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, ++ struct nand_chip *chip, int addr, ++ u8 *subfeature_param) ++{ ++ return -ENOTSUPP; ++} ++EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp); ++ ++/** ++ * nand_suspend - [MTD Interface] Suspend the NAND flash ++ * @mtd: MTD device structure ++ */ ++static int nand_suspend(struct mtd_info *mtd) ++{ ++ return nand_get_device(mtd, FL_PM_SUSPENDED); ++} ++ ++/** ++ * nand_resume - [MTD Interface] Resume the NAND flash ++ * @mtd: MTD device structure ++ */ ++static void nand_resume(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (chip->state == FL_PM_SUSPENDED) ++ nand_release_device(mtd); ++ else ++ pr_err("%s called for a chip which is not in suspended state\n", ++ __func__); ++} ++ ++/** ++ * nand_shutdown - [MTD Interface] Finish the current NAND operation and ++ * prevent further operations ++ * @mtd: MTD device structure ++ */ ++static void nand_shutdown(struct mtd_info *mtd) ++{ ++ nand_get_device(mtd, FL_PM_SUSPENDED); ++} ++ ++/* Set default functions */ ++static void nand_set_defaults(struct nand_chip *chip) ++{ ++ unsigned int busw = chip->options & NAND_BUSWIDTH_16; ++ ++ /* check for proper chip_delay setup, set 20us if not */ ++ if (!chip->chip_delay) ++ chip->chip_delay = 20; ++ ++ /* check, if a user supplied command function given */ ++ if (!chip->cmdfunc && !chip->exec_op) ++ chip->cmdfunc = nand_command; ++ ++ /* check, if a user supplied wait function given */ ++ if (chip->waitfunc == NULL) ++ chip->waitfunc = nand_wait; ++ ++ if (!chip->select_chip) ++ chip->select_chip = nand_select_chip; ++ ++ /* set for ONFI nand */ ++ if (!chip->onfi_set_features) ++ chip->onfi_set_features = nand_onfi_set_features; ++ if (!chip->onfi_get_features) ++ chip->onfi_get_features = nand_onfi_get_features; ++ ++ /* If called twice, pointers that depend on busw may need to be reset */ ++ if (!chip->read_byte || chip->read_byte == nand_read_byte) ++ chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; ++ if (!chip->read_word) ++ chip->read_word = nand_read_word; ++ if (!chip->block_bad) ++ chip->block_bad = nand_block_bad; ++ if (!chip->block_markbad) ++ chip->block_markbad = nand_default_block_markbad; ++ if (!chip->write_buf || chip->write_buf == nand_write_buf) ++ chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; ++ if (!chip->write_byte || chip->write_byte == nand_write_byte) ++ chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; ++ if (!chip->read_buf || chip->read_buf == nand_read_buf) ++ chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; ++ if (!chip->scan_bbt) ++ chip->scan_bbt = nand_default_bbt; ++ ++ if (!chip->controller) { ++ chip->controller = &chip->dummy_controller; ++ nand_controller_init(chip->controller); ++ } ++ ++ if (!chip->buf_align) ++ chip->buf_align = 1; ++} ++ ++/* Sanitize ONFI strings so we can safely print them */ ++static void sanitize_string(uint8_t *s, size_t len) ++{ ++ ssize_t i; ++ ++ /* Null terminate */ ++ s[len - 1] = 0; ++ ++ /* Remove non printable chars */ ++ for (i = 0; i < len - 1; i++) { ++ if (s[i] < ' ' || s[i] > 127) ++ s[i] = '?'; ++ } ++ ++ /* Remove trailing spaces */ ++ strim(s); ++} ++ ++static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) ++{ ++ int i; ++ while (len--) { ++ crc ^= *p++ << 8; ++ for (i = 0; i < 8; i++) ++ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); ++ } ++ ++ return crc; ++} ++ ++/* Parse the Extended Parameter Page. */ ++static int nand_flash_detect_ext_param_page(struct nand_chip *chip, ++ struct nand_onfi_params *p) ++{ ++ struct onfi_ext_param_page *ep; ++ struct onfi_ext_section *s; ++ struct onfi_ext_ecc_info *ecc; ++ uint8_t *cursor; ++ int ret; ++ int len; ++ int i; ++ ++ len = le16_to_cpu(p->ext_param_page_length) * 16; ++ ep = kmalloc(len, GFP_KERNEL); ++ if (!ep) ++ return -ENOMEM; ++ ++ /* Send our own NAND_CMD_PARAM. */ ++ ret = nand_read_param_page_op(chip, 0, NULL, 0); ++ if (ret) ++ goto ext_out; ++ ++ /* Use the Change Read Column command to skip the ONFI param pages. */ ++ ret = nand_change_read_column_op(chip, ++ sizeof(*p) * p->num_of_param_pages, ++ ep, len, true); ++ if (ret) ++ goto ext_out; ++ ++ ret = -EINVAL; ++ if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) ++ != le16_to_cpu(ep->crc))) { ++ pr_debug("fail in the CRC.\n"); ++ goto ext_out; ++ } ++ ++ /* ++ * Check the signature. ++ * Do not strictly follow the ONFI spec, maybe changed in future. ++ */ ++ if (strncmp(ep->sig, "EPPS", 4)) { ++ pr_debug("The signature is invalid.\n"); ++ goto ext_out; ++ } ++ ++ /* find the ECC section. */ ++ cursor = (uint8_t *)(ep + 1); ++ for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { ++ s = ep->sections + i; ++ if (s->type == ONFI_SECTION_TYPE_2) ++ break; ++ cursor += s->length * 16; ++ } ++ if (i == ONFI_EXT_SECTION_MAX) { ++ pr_debug("We can not find the ECC section.\n"); ++ goto ext_out; ++ } ++ ++ /* get the info we want. */ ++ ecc = (struct onfi_ext_ecc_info *)cursor; ++ ++ if (!ecc->codeword_size) { ++ pr_debug("Invalid codeword size\n"); ++ goto ext_out; ++ } ++ ++ chip->ecc_strength_ds = ecc->ecc_bits; ++ chip->ecc_step_ds = 1 << ecc->codeword_size; ++ ret = 0; ++ ++ext_out: ++ kfree(ep); ++ return ret; ++} ++ ++/* ++ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. ++ */ ++static int nand_flash_detect_onfi(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct nand_onfi_params *p = &chip->onfi_params; ++ char id[4]; ++ int i, ret, val; ++ ++ /* Try ONFI for unknown chip or LP */ ++ ret = nand_readid_op(chip, 0x20, id, sizeof(id)); ++ if (ret || strncmp(id, "ONFI", 4)) ++ return 0; ++ ++ ret = nand_read_param_page_op(chip, 0, NULL, 0); ++ if (ret) ++ return 0; ++ ++ for (i = 0; i < 3; i++) { ++ ret = nand_read_data_op(chip, p, sizeof(*p), true); ++ if (ret) ++ return 0; ++ ++ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == ++ le16_to_cpu(p->crc)) { ++ break; ++ } ++ } ++ ++ if (i == 3) { ++ pr_err("Could not find valid ONFI parameter page; aborting\n"); ++ return 0; ++ } ++ ++ /* Check version */ ++ val = le16_to_cpu(p->revision); ++ if (val & (1 << 5)) ++ chip->onfi_version = 23; ++ else if (val & (1 << 4)) ++ chip->onfi_version = 22; ++ else if (val & (1 << 3)) ++ chip->onfi_version = 21; ++ else if (val & (1 << 2)) ++ chip->onfi_version = 20; ++ else if (val & (1 << 1)) ++ chip->onfi_version = 10; ++ ++ if (!chip->onfi_version) { ++ pr_info("unsupported ONFI version: %d\n", val); ++ return 0; ++ } ++ ++ sanitize_string(p->manufacturer, sizeof(p->manufacturer)); ++ sanitize_string(p->model, sizeof(p->model)); ++ if (!mtd->name) ++ mtd->name = p->model; ++ ++ mtd->writesize = le32_to_cpu(p->byte_per_page); ++ ++ /* ++ * pages_per_block and blocks_per_lun may not be a power-of-2 size ++ * (don't ask me who thought of this...). MTD assumes that these ++ * dimensions will be power-of-2, so just truncate the remaining area. ++ */ ++ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); ++ mtd->erasesize *= mtd->writesize; ++ ++ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); ++ ++ /* See erasesize comment */ ++ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); ++ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; ++ chip->bits_per_cell = p->bits_per_cell; ++ ++ chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun); ++ chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun); ++ ++ if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ if (p->ecc_bits != 0xff) { ++ chip->ecc_strength_ds = p->ecc_bits; ++ chip->ecc_step_ds = 512; ++ } else if (chip->onfi_version >= 21 && ++ (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { ++ ++ /* ++ * The nand_flash_detect_ext_param_page() uses the ++ * Change Read Column command which maybe not supported ++ * by the chip->cmdfunc. So try to update the chip->cmdfunc ++ * now. We do not replace user supplied command function. ++ */ ++ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) ++ chip->cmdfunc = nand_command_lp; ++ ++ /* The Extended Parameter Page is supported since ONFI 2.1. */ ++ if (nand_flash_detect_ext_param_page(chip, p)) ++ pr_warn("Failed to detect ONFI extended param page\n"); ++ } else { ++ pr_warn("Could not retrieve ONFI ECC requirements\n"); ++ } ++ ++ return 1; ++} ++ ++/* ++ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. ++ */ ++static int nand_flash_detect_jedec(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct nand_jedec_params *p = &chip->jedec_params; ++ struct jedec_ecc_info *ecc; ++ char id[5]; ++ int i, val, ret; ++ ++ /* Try JEDEC for unknown chip or LP */ ++ ret = nand_readid_op(chip, 0x40, id, sizeof(id)); ++ if (ret || strncmp(id, "JEDEC", sizeof(id))) ++ return 0; ++ ++ ret = nand_read_param_page_op(chip, 0x40, NULL, 0); ++ if (ret) ++ return 0; ++ ++ for (i = 0; i < 3; i++) { ++ ret = nand_read_data_op(chip, p, sizeof(*p), true); ++ if (ret) ++ return 0; ++ ++ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == ++ le16_to_cpu(p->crc)) ++ break; ++ } ++ ++ if (i == 3) { ++ pr_err("Could not find valid JEDEC parameter page; aborting\n"); ++ return 0; ++ } ++ ++ /* Check version */ ++ val = le16_to_cpu(p->revision); ++ if (val & (1 << 2)) ++ chip->jedec_version = 10; ++ else if (val & (1 << 1)) ++ chip->jedec_version = 1; /* vendor specific version */ ++ ++ if (!chip->jedec_version) { ++ pr_info("unsupported JEDEC version: %d\n", val); ++ return 0; ++ } ++ ++ sanitize_string(p->manufacturer, sizeof(p->manufacturer)); ++ sanitize_string(p->model, sizeof(p->model)); ++ if (!mtd->name) ++ mtd->name = p->model; ++ ++ mtd->writesize = le32_to_cpu(p->byte_per_page); ++ ++ /* Please reference to the comment for nand_flash_detect_onfi. */ ++ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); ++ mtd->erasesize *= mtd->writesize; ++ ++ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); ++ ++ /* Please reference to the comment for nand_flash_detect_onfi. */ ++ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); ++ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; ++ chip->bits_per_cell = p->bits_per_cell; ++ ++ if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ /* ECC info */ ++ ecc = &p->ecc_info[0]; ++ ++ if (ecc->codeword_size >= 9) { ++ chip->ecc_strength_ds = ecc->ecc_bits; ++ chip->ecc_step_ds = 1 << ecc->codeword_size; ++ } else { ++ pr_warn("Invalid codeword size\n"); ++ } ++ ++ return 1; ++} ++ ++/* ++ * nand_id_has_period - Check if an ID string has a given wraparound period ++ * @id_data: the ID string ++ * @arrlen: the length of the @id_data array ++ * @period: the period of repitition ++ * ++ * Check if an ID string is repeated within a given sequence of bytes at ++ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a ++ * period of 3). This is a helper function for nand_id_len(). Returns non-zero ++ * if the repetition has a period of @period; otherwise, returns zero. ++ */ ++static int nand_id_has_period(u8 *id_data, int arrlen, int period) ++{ ++ int i, j; ++ for (i = 0; i < period; i++) ++ for (j = i + period; j < arrlen; j += period) ++ if (id_data[i] != id_data[j]) ++ return 0; ++ return 1; ++} ++ ++/* ++ * nand_id_len - Get the length of an ID string returned by CMD_READID ++ * @id_data: the ID string ++ * @arrlen: the length of the @id_data array ++ ++ * Returns the length of the ID string, according to known wraparound/trailing ++ * zero patterns. If no pattern exists, returns the length of the array. ++ */ ++static int nand_id_len(u8 *id_data, int arrlen) ++{ ++ int last_nonzero, period; ++ ++ /* Find last non-zero byte */ ++ for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) ++ if (id_data[last_nonzero]) ++ break; ++ ++ /* All zeros */ ++ if (last_nonzero < 0) ++ return 0; ++ ++ /* Calculate wraparound period */ ++ for (period = 1; period < arrlen; period++) ++ if (nand_id_has_period(id_data, arrlen, period)) ++ break; ++ ++ /* There's a repeated pattern */ ++ if (period < arrlen) ++ return period; ++ ++ /* There are trailing zeros */ ++ if (last_nonzero < arrlen - 1) ++ return last_nonzero + 1; ++ ++ /* No pattern detected */ ++ return arrlen; ++} ++ ++/* Extract the bits of per cell from the 3rd byte of the extended ID */ ++static int nand_get_bits_per_cell(u8 cellinfo) ++{ ++ int bits; ++ ++ bits = cellinfo & NAND_CI_CELLTYPE_MSK; ++ bits >>= NAND_CI_CELLTYPE_SHIFT; ++ return bits + 1; ++} ++ ++/* ++ * Many new NAND share similar device ID codes, which represent the size of the ++ * chip. The rest of the parameters must be decoded according to generic or ++ * manufacturer-specific "extended ID" decoding patterns. ++ */ ++void nand_decode_ext_id(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int extid; ++ u8 *id_data = chip->id.data; ++ /* The 3rd id byte holds MLC / multichip data */ ++ chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); ++ /* The 4th id byte is the important one */ ++ extid = id_data[3]; ++ ++ /* Calc pagesize */ ++ mtd->writesize = 1024 << (extid & 0x03); ++ extid >>= 2; ++ /* Calc oobsize */ ++ mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); ++ extid >>= 2; ++ /* Calc blocksize. Blocksize is multiples of 64KiB */ ++ mtd->erasesize = (64 * 1024) << (extid & 0x03); ++ extid >>= 2; ++ /* Get buswidth information */ ++ if (extid & 0x1) ++ chip->options |= NAND_BUSWIDTH_16; ++} ++EXPORT_SYMBOL_GPL(nand_decode_ext_id); ++ ++/* ++ * Old devices have chip data hardcoded in the device ID table. nand_decode_id ++ * decodes a matching ID table entry and assigns the MTD size parameters for ++ * the chip. ++ */ ++static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ mtd->erasesize = type->erasesize; ++ mtd->writesize = type->pagesize; ++ mtd->oobsize = mtd->writesize / 32; ++ ++ /* All legacy ID NAND are small-page, SLC */ ++ chip->bits_per_cell = 1; ++} ++ ++/* ++ * Set the bad block marker/indicator (BBM/BBI) patterns according to some ++ * heuristic patterns using various detected parameters (e.g., manufacturer, ++ * page size, cell-type information). ++ */ ++static void nand_decode_bbm_options(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ /* Set the bad block position */ ++ if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) ++ chip->badblockpos = NAND_LARGE_BADBLOCK_POS; ++ else ++ chip->badblockpos = NAND_SMALL_BADBLOCK_POS; ++} ++ ++static inline bool is_full_id_nand(struct nand_flash_dev *type) ++{ ++ return type->id_len; ++} ++ ++static bool find_full_id_nand(struct nand_chip *chip, ++ struct nand_flash_dev *type) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 *id_data = chip->id.data; ++ ++ if (!strncmp(type->id, id_data, type->id_len)) { ++ mtd->writesize = type->pagesize; ++ mtd->erasesize = type->erasesize; ++ mtd->oobsize = type->oobsize; ++ ++ chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); ++ chip->chipsize = (uint64_t)type->chipsize << 20; ++ chip->options |= type->options; ++ chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); ++ chip->ecc_step_ds = NAND_ECC_STEP(type); ++ chip->onfi_timing_mode_default = ++ type->onfi_timing_mode_default; ++ ++ if (!mtd->name) ++ mtd->name = type->name; ++ ++ return true; ++ } ++ return false; ++} ++ ++/* ++ * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC ++ * compliant and does not have a full-id or legacy-id entry in the nand_ids ++ * table. ++ */ ++static void nand_manufacturer_detect(struct nand_chip *chip) ++{ ++ /* ++ * Try manufacturer detection if available and use ++ * nand_decode_ext_id() otherwise. ++ */ ++ if (chip->manufacturer.desc && chip->manufacturer.desc->ops && ++ chip->manufacturer.desc->ops->detect) { ++ /* The 3rd id byte holds MLC / multichip data */ ++ chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]); ++ chip->manufacturer.desc->ops->detect(chip); ++ } else { ++ nand_decode_ext_id(chip); ++ } ++} ++ ++/* ++ * Manufacturer initialization. This function is called for all NANDs including ++ * ONFI and JEDEC compliant ones. ++ * Manufacturer drivers should put all their specific initialization code in ++ * their ->init() hook. ++ */ ++static int nand_manufacturer_init(struct nand_chip *chip) ++{ ++ if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops || ++ !chip->manufacturer.desc->ops->init) ++ return 0; ++ ++ return chip->manufacturer.desc->ops->init(chip); ++} ++ ++/* ++ * Manufacturer cleanup. This function is called for all NANDs including ++ * ONFI and JEDEC compliant ones. ++ * Manufacturer drivers should put all their specific cleanup code in their ++ * ->cleanup() hook. ++ */ ++static void nand_manufacturer_cleanup(struct nand_chip *chip) ++{ ++ /* Release manufacturer private data */ ++ if (chip->manufacturer.desc && chip->manufacturer.desc->ops && ++ chip->manufacturer.desc->ops->cleanup) ++ chip->manufacturer.desc->ops->cleanup(chip); ++} ++ ++/* ++ * Get the flash and manufacturer id and lookup if the type is supported. ++ */ ++static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) ++{ ++ const struct nand_manufacturer *manufacturer; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int busw, ret; ++ u8 *id_data = chip->id.data; ++ u8 maf_id, dev_id; ++ ++ /* ++ * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) ++ * after power-up. ++ */ ++ ret = nand_reset(chip, 0); ++ if (ret) ++ return ret; ++ ++ /* Select the device */ ++ chip->select_chip(mtd, 0); ++ ++ /* Send the command for reading device ID */ ++ ret = nand_readid_op(chip, 0, id_data, 2); ++ if (ret) ++ return ret; ++ ++ /* Read manufacturer and device IDs */ ++ maf_id = id_data[0]; ++ dev_id = id_data[1]; ++ ++ /* ++ * Try again to make sure, as some systems the bus-hold or other ++ * interface concerns can cause random data which looks like a ++ * possibly credible NAND flash to appear. If the two results do ++ * not match, ignore the device completely. ++ */ ++ ++ /* Read entire ID string */ ++ ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data)); ++ if (ret) ++ return ret; ++ ++ if (id_data[0] != maf_id || id_data[1] != dev_id) { ++ pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", ++ maf_id, dev_id, id_data[0], id_data[1]); ++ return -ENODEV; ++ } ++ ++ chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data)); ++ ++ /* Try to identify manufacturer */ ++ manufacturer = nand_get_manufacturer(maf_id); ++ chip->manufacturer.desc = manufacturer; ++ ++ if (!type) ++ type = nand_flash_ids; ++ ++ /* ++ * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic ++ * override it. ++ * This is required to make sure initial NAND bus width set by the ++ * NAND controller driver is coherent with the real NAND bus width ++ * (extracted by auto-detection code). ++ */ ++ busw = chip->options & NAND_BUSWIDTH_16; ++ ++ /* ++ * The flag is only set (never cleared), reset it to its default value ++ * before starting auto-detection. ++ */ ++ chip->options &= ~NAND_BUSWIDTH_16; ++ ++ for (; type->name != NULL; type++) { ++ if (is_full_id_nand(type)) { ++ if (find_full_id_nand(chip, type)) ++ goto ident_done; ++ } else if (dev_id == type->dev_id) { ++ break; ++ } ++ } ++ ++ chip->onfi_version = 0; ++ if (!type->name || !type->pagesize) { ++ /* Check if the chip is ONFI compliant */ ++ if (nand_flash_detect_onfi(chip)) ++ goto ident_done; ++ ++ /* Check if the chip is JEDEC compliant */ ++ if (nand_flash_detect_jedec(chip)) ++ goto ident_done; ++ } ++ ++ if (!type->name) ++ return -ENODEV; ++ ++ if (!mtd->name) ++ mtd->name = type->name; ++ ++ chip->chipsize = (uint64_t)type->chipsize << 20; ++ ++ if (!type->pagesize) ++ nand_manufacturer_detect(chip); ++ else ++ nand_decode_id(chip, type); ++ ++ /* Get chip options */ ++ chip->options |= type->options; ++ ++ident_done: ++ ++ if (chip->options & NAND_BUSWIDTH_AUTO) { ++ WARN_ON(busw & NAND_BUSWIDTH_16); ++ nand_set_defaults(chip); ++ } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { ++ /* ++ * Check, if buswidth is correct. Hardware drivers should set ++ * chip correct! ++ */ ++ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", ++ maf_id, dev_id); ++ pr_info("%s %s\n", nand_manufacturer_name(manufacturer), ++ mtd->name); ++ pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8, ++ (chip->options & NAND_BUSWIDTH_16) ? 16 : 8); ++ return -EINVAL; ++ } ++ ++ nand_decode_bbm_options(chip); ++ ++ /* Calculate the address shift from the page size */ ++ chip->page_shift = ffs(mtd->writesize) - 1; ++ /* Convert chipsize to number of pages per chip -1 */ ++ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; ++ ++ chip->bbt_erase_shift = chip->phys_erase_shift = ++ ffs(mtd->erasesize) - 1; ++ if (chip->chipsize & 0xffffffff) ++ chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; ++ else { ++ chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); ++ chip->chip_shift += 32 - 1; ++ } ++ ++ if (chip->chip_shift - chip->page_shift > 16) ++ chip->options |= NAND_ROW_ADDR_3; ++ ++ chip->badblockbits = 8; ++ chip->erase = single_erase; ++ ++ /* Do not replace user supplied command function! */ ++ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) ++ chip->cmdfunc = nand_command_lp; ++ ++ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", ++ maf_id, dev_id); ++ ++ if (chip->onfi_version) ++ pr_info("%s %s\n", nand_manufacturer_name(manufacturer), ++ chip->onfi_params.model); ++ else if (chip->jedec_version) ++ pr_info("%s %s\n", nand_manufacturer_name(manufacturer), ++ chip->jedec_params.model); ++ else ++ pr_info("%s %s\n", nand_manufacturer_name(manufacturer), ++ type->name); ++ ++ pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", ++ (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", ++ mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); ++ return 0; ++} ++ ++static const char * const nand_ecc_modes[] = { ++ [NAND_ECC_NONE] = "none", ++ [NAND_ECC_SOFT] = "soft", ++ [NAND_ECC_HW] = "hw", ++ [NAND_ECC_HW_SYNDROME] = "hw_syndrome", ++ [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", ++ [NAND_ECC_ON_DIE] = "on-die", ++}; ++ ++static int of_get_nand_ecc_mode(struct device_node *np) ++{ ++ const char *pm; ++ int err, i; ++ ++ err = of_property_read_string(np, "nand-ecc-mode", &pm); ++ if (err < 0) ++ return err; ++ ++ for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) ++ if (!strcasecmp(pm, nand_ecc_modes[i])) ++ return i; ++ ++ /* ++ * For backward compatibility we support few obsoleted values that don't ++ * have their mappings into nand_ecc_modes_t anymore (they were merged ++ * with other enums). ++ */ ++ if (!strcasecmp(pm, "soft_bch")) ++ return NAND_ECC_SOFT; ++ ++ return -ENODEV; ++} ++ ++static const char * const nand_ecc_algos[] = { ++ [NAND_ECC_HAMMING] = "hamming", ++ [NAND_ECC_BCH] = "bch", ++}; ++ ++static int of_get_nand_ecc_algo(struct device_node *np) ++{ ++ const char *pm; ++ int err, i; ++ ++ err = of_property_read_string(np, "nand-ecc-algo", &pm); ++ if (!err) { ++ for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++) ++ if (!strcasecmp(pm, nand_ecc_algos[i])) ++ return i; ++ return -ENODEV; ++ } ++ ++ /* ++ * For backward compatibility we also read "nand-ecc-mode" checking ++ * for some obsoleted values that were specifying ECC algorithm. ++ */ ++ err = of_property_read_string(np, "nand-ecc-mode", &pm); ++ if (err < 0) ++ return err; ++ ++ if (!strcasecmp(pm, "soft")) ++ return NAND_ECC_HAMMING; ++ else if (!strcasecmp(pm, "soft_bch")) ++ return NAND_ECC_BCH; ++ ++ return -ENODEV; ++} ++ ++static int of_get_nand_ecc_step_size(struct device_node *np) ++{ ++ int ret; ++ u32 val; ++ ++ ret = of_property_read_u32(np, "nand-ecc-step-size", &val); ++ return ret ? ret : val; ++} ++ ++static int of_get_nand_ecc_strength(struct device_node *np) ++{ ++ int ret; ++ u32 val; ++ ++ ret = of_property_read_u32(np, "nand-ecc-strength", &val); ++ return ret ? ret : val; ++} ++ ++static int of_get_nand_bus_width(struct device_node *np) ++{ ++ u32 val; ++ ++ if (of_property_read_u32(np, "nand-bus-width", &val)) ++ return 8; ++ ++ switch (val) { ++ case 8: ++ case 16: ++ return val; ++ default: ++ return -EIO; ++ } ++} ++ ++static bool of_get_nand_on_flash_bbt(struct device_node *np) ++{ ++ return of_property_read_bool(np, "nand-on-flash-bbt"); ++} ++ ++static int nand_dt_init(struct nand_chip *chip) ++{ ++ struct device_node *dn = nand_get_flash_node(chip); ++ int ecc_mode, ecc_algo, ecc_strength, ecc_step; ++ ++ if (!dn) ++ return 0; ++ ++ if (of_get_nand_bus_width(dn) == 16) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ if (of_get_nand_on_flash_bbt(dn)) ++ chip->bbt_options |= NAND_BBT_USE_FLASH; ++ ++ ecc_mode = of_get_nand_ecc_mode(dn); ++ ecc_algo = of_get_nand_ecc_algo(dn); ++ ecc_strength = of_get_nand_ecc_strength(dn); ++ ecc_step = of_get_nand_ecc_step_size(dn); ++ ++ if (ecc_mode >= 0) ++ chip->ecc.mode = ecc_mode; ++ ++ if (ecc_algo >= 0) ++ chip->ecc.algo = ecc_algo; ++ ++ if (ecc_strength >= 0) ++ chip->ecc.strength = ecc_strength; ++ ++ if (ecc_step > 0) ++ chip->ecc.size = ecc_step; ++ ++ if (of_property_read_bool(dn, "nand-ecc-maximize")) ++ chip->ecc.options |= NAND_ECC_MAXIMIZE; ++ ++ return 0; ++} ++ ++/** ++ * nand_scan_ident - [NAND Interface] Scan for the NAND device ++ * @mtd: MTD device structure ++ * @maxchips: number of chips to scan for ++ * @table: alternative NAND ID table ++ * ++ * This is the first phase of the normal nand_scan() function. It reads the ++ * flash ID and sets up MTD fields accordingly. ++ * ++ */ ++int nand_scan_ident(struct mtd_info *mtd, int maxchips, ++ struct nand_flash_dev *table) ++{ ++ int i, nand_maf_id, nand_dev_id; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ /* Enforce the right timings for reset/detection */ ++ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); ++ ++ ret = nand_dt_init(chip); ++ if (ret) ++ return ret; ++ ++ if (!mtd->name && mtd->dev.parent) ++ mtd->name = dev_name(mtd->dev.parent); ++ ++ /* ++ * ->cmdfunc() is legacy and will only be used if ->exec_op() is not ++ * populated. ++ */ ++ if (!chip->exec_op) { ++ /* ++ * Default functions assigned for ->cmdfunc() and ++ * ->select_chip() both expect ->cmd_ctrl() to be populated. ++ */ ++ if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { ++ pr_err("->cmd_ctrl() should be provided\n"); ++ return -EINVAL; ++ } ++ } ++ ++ /* Set the default functions */ ++ nand_set_defaults(chip); ++ ++ /* Read the flash type */ ++ ret = nand_detect(chip, table); ++ if (ret) { ++ if (!(chip->options & NAND_SCAN_SILENT_NODEV)) ++ pr_warn("No NAND device found\n"); ++ chip->select_chip(mtd, -1); ++ return ret; ++ } ++ ++ nand_maf_id = chip->id.data[0]; ++ nand_dev_id = chip->id.data[1]; ++ ++ chip->select_chip(mtd, -1); ++ ++ /* Check for a chip array */ ++ for (i = 1; i < maxchips; i++) { ++ u8 id[2]; ++ ++ /* See comment in nand_get_flash_type for reset */ ++ nand_reset(chip, i); ++ ++ chip->select_chip(mtd, i); ++ /* Send the command for reading device ID */ ++ nand_readid_op(chip, 0, id, sizeof(id)); ++ /* Read manufacturer and device IDs */ ++ if (nand_maf_id != id[0] || nand_dev_id != id[1]) { ++ chip->select_chip(mtd, -1); ++ break; ++ } ++ chip->select_chip(mtd, -1); ++ } ++ if (i > 1) ++ pr_info("%d chips detected\n", i); ++ ++ /* Store the number of chips and calc total size for mtd */ ++ chip->numchips = i; ++ mtd->size = i * chip->chipsize; ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_scan_ident); ++ ++static int nand_set_ecc_soft_ops(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (WARN_ON(ecc->mode != NAND_ECC_SOFT)) ++ return -EINVAL; ++ ++ switch (ecc->algo) { ++ case NAND_ECC_HAMMING: ++ ecc->calculate = nand_calculate_ecc; ++ ecc->correct = nand_correct_data; ++ ecc->read_page = nand_read_page_swecc; ++ ecc->read_subpage = nand_read_subpage; ++ ecc->write_page = nand_write_page_swecc; ++ ecc->read_page_raw = nand_read_page_raw; ++ ecc->write_page_raw = nand_write_page_raw; ++ ecc->read_oob = nand_read_oob_std; ++ ecc->write_oob = nand_write_oob_std; ++ if (!ecc->size) ++ ecc->size = 256; ++ ecc->bytes = 3; ++ ecc->strength = 1; ++ return 0; ++ case NAND_ECC_BCH: ++ if (!mtd_nand_has_bch()) { ++ WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); ++ return -EINVAL; ++ } ++ ecc->calculate = nand_bch_calculate_ecc; ++ ecc->correct = nand_bch_correct_data; ++ ecc->read_page = nand_read_page_swecc; ++ ecc->read_subpage = nand_read_subpage; ++ ecc->write_page = nand_write_page_swecc; ++ ecc->read_page_raw = nand_read_page_raw; ++ ecc->write_page_raw = nand_write_page_raw; ++ ecc->read_oob = nand_read_oob_std; ++ ecc->write_oob = nand_write_oob_std; ++ ++ /* ++ * Board driver should supply ecc.size and ecc.strength ++ * values to select how many bits are correctable. ++ * Otherwise, default to 4 bits for large page devices. ++ */ ++ if (!ecc->size && (mtd->oobsize >= 64)) { ++ ecc->size = 512; ++ ecc->strength = 4; ++ } ++ ++ /* ++ * if no ecc placement scheme was provided pickup the default ++ * large page one. ++ */ ++ if (!mtd->ooblayout) { ++ /* handle large page devices only */ ++ if (mtd->oobsize < 64) { ++ WARN(1, "OOB layout is required when using software BCH on small pages\n"); ++ return -EINVAL; ++ } ++ ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ++ ++ } ++ ++ /* ++ * We can only maximize ECC config when the default layout is ++ * used, otherwise we don't know how many bytes can really be ++ * used. ++ */ ++ if (mtd->ooblayout == &nand_ooblayout_lp_ops && ++ ecc->options & NAND_ECC_MAXIMIZE) { ++ int steps, bytes; ++ ++ /* Always prefer 1k blocks over 512bytes ones */ ++ ecc->size = 1024; ++ steps = mtd->writesize / ecc->size; ++ ++ /* Reserve 2 bytes for the BBM */ ++ bytes = (mtd->oobsize - 2) / steps; ++ ecc->strength = bytes * 8 / fls(8 * ecc->size); ++ } ++ ++ /* See nand_bch_init() for details. */ ++ ecc->bytes = 0; ++ ecc->priv = nand_bch_init(mtd); ++ if (!ecc->priv) { ++ WARN(1, "BCH ECC initialization failed!\n"); ++ return -EINVAL; ++ } ++ return 0; ++ default: ++ WARN(1, "Unsupported ECC algorithm!\n"); ++ return -EINVAL; ++ } ++} ++ ++/** ++ * nand_check_ecc_caps - check the sanity of preset ECC settings ++ * @chip: nand chip info structure ++ * @caps: ECC caps info structure ++ * @oobavail: OOB size that the ECC engine can use ++ * ++ * When ECC step size and strength are already set, check if they are supported ++ * by the controller and the calculated ECC bytes fit within the chip's OOB. ++ * On success, the calculated ECC bytes is set. ++ */ ++int nand_check_ecc_caps(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_ecc_step_info *stepinfo; ++ int preset_step = chip->ecc.size; ++ int preset_strength = chip->ecc.strength; ++ int nsteps, ecc_bytes; ++ int i, j; ++ ++ if (WARN_ON(oobavail < 0)) ++ return -EINVAL; ++ ++ if (!preset_step || !preset_strength) ++ return -ENODATA; ++ ++ nsteps = mtd->writesize / preset_step; ++ ++ for (i = 0; i < caps->nstepinfos; i++) { ++ stepinfo = &caps->stepinfos[i]; ++ ++ if (stepinfo->stepsize != preset_step) ++ continue; ++ ++ for (j = 0; j < stepinfo->nstrengths; j++) { ++ if (stepinfo->strengths[j] != preset_strength) ++ continue; ++ ++ ecc_bytes = caps->calc_ecc_bytes(preset_step, ++ preset_strength); ++ if (WARN_ON_ONCE(ecc_bytes < 0)) ++ return ecc_bytes; ++ ++ if (ecc_bytes * nsteps > oobavail) { ++ pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", ++ preset_step, preset_strength); ++ return -ENOSPC; ++ } ++ ++ chip->ecc.bytes = ecc_bytes; ++ ++ return 0; ++ } ++ } ++ ++ pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", ++ preset_step, preset_strength); ++ ++ return -ENOTSUPP; ++} ++EXPORT_SYMBOL_GPL(nand_check_ecc_caps); ++ ++/** ++ * nand_match_ecc_req - meet the chip's requirement with least ECC bytes ++ * @chip: nand chip info structure ++ * @caps: ECC engine caps info structure ++ * @oobavail: OOB size that the ECC engine can use ++ * ++ * If a chip's ECC requirement is provided, try to meet it with the least ++ * number of ECC bytes (i.e. with the largest number of OOB-free bytes). ++ * On success, the chosen ECC settings are set. ++ */ ++int nand_match_ecc_req(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_ecc_step_info *stepinfo; ++ int req_step = chip->ecc_step_ds; ++ int req_strength = chip->ecc_strength_ds; ++ int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; ++ int best_step, best_strength, best_ecc_bytes; ++ int best_ecc_bytes_total = INT_MAX; ++ int i, j; ++ ++ if (WARN_ON(oobavail < 0)) ++ return -EINVAL; ++ ++ /* No information provided by the NAND chip */ ++ if (!req_step || !req_strength) ++ return -ENOTSUPP; ++ ++ /* number of correctable bits the chip requires in a page */ ++ req_corr = mtd->writesize / req_step * req_strength; ++ ++ for (i = 0; i < caps->nstepinfos; i++) { ++ stepinfo = &caps->stepinfos[i]; ++ step_size = stepinfo->stepsize; ++ ++ for (j = 0; j < stepinfo->nstrengths; j++) { ++ strength = stepinfo->strengths[j]; ++ ++ /* ++ * If both step size and strength are smaller than the ++ * chip's requirement, it is not easy to compare the ++ * resulted reliability. ++ */ ++ if (step_size < req_step && strength < req_strength) ++ continue; ++ ++ if (mtd->writesize % step_size) ++ continue; ++ ++ nsteps = mtd->writesize / step_size; ++ ++ ecc_bytes = caps->calc_ecc_bytes(step_size, strength); ++ if (WARN_ON_ONCE(ecc_bytes < 0)) ++ continue; ++ ecc_bytes_total = ecc_bytes * nsteps; ++ ++ if (ecc_bytes_total > oobavail || ++ strength * nsteps < req_corr) ++ continue; ++ ++ /* ++ * We assume the best is to meet the chip's requrement ++ * with the least number of ECC bytes. ++ */ ++ if (ecc_bytes_total < best_ecc_bytes_total) { ++ best_ecc_bytes_total = ecc_bytes_total; ++ best_step = step_size; ++ best_strength = strength; ++ best_ecc_bytes = ecc_bytes; ++ } ++ } ++ } ++ ++ if (best_ecc_bytes_total == INT_MAX) ++ return -ENOTSUPP; ++ ++ chip->ecc.size = best_step; ++ chip->ecc.strength = best_strength; ++ chip->ecc.bytes = best_ecc_bytes; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_match_ecc_req); ++ ++/** ++ * nand_maximize_ecc - choose the max ECC strength available ++ * @chip: nand chip info structure ++ * @caps: ECC engine caps info structure ++ * @oobavail: OOB size that the ECC engine can use ++ * ++ * Choose the max ECC strength that is supported on the controller, and can fit ++ * within the chip's OOB. On success, the chosen ECC settings are set. ++ */ ++int nand_maximize_ecc(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const struct nand_ecc_step_info *stepinfo; ++ int step_size, strength, nsteps, ecc_bytes, corr; ++ int best_corr = 0; ++ int best_step = 0; ++ int best_strength, best_ecc_bytes; ++ int i, j; ++ ++ if (WARN_ON(oobavail < 0)) ++ return -EINVAL; ++ ++ for (i = 0; i < caps->nstepinfos; i++) { ++ stepinfo = &caps->stepinfos[i]; ++ step_size = stepinfo->stepsize; ++ ++ /* If chip->ecc.size is already set, respect it */ ++ if (chip->ecc.size && step_size != chip->ecc.size) ++ continue; ++ ++ for (j = 0; j < stepinfo->nstrengths; j++) { ++ strength = stepinfo->strengths[j]; ++ ++ if (mtd->writesize % step_size) ++ continue; ++ ++ nsteps = mtd->writesize / step_size; ++ ++ ecc_bytes = caps->calc_ecc_bytes(step_size, strength); ++ if (WARN_ON_ONCE(ecc_bytes < 0)) ++ continue; ++ ++ if (ecc_bytes * nsteps > oobavail) ++ continue; ++ ++ corr = strength * nsteps; ++ ++ /* ++ * If the number of correctable bits is the same, ++ * bigger step_size has more reliability. ++ */ ++ if (corr > best_corr || ++ (corr == best_corr && step_size > best_step)) { ++ best_corr = corr; ++ best_step = step_size; ++ best_strength = strength; ++ best_ecc_bytes = ecc_bytes; ++ } ++ } ++ } ++ ++ if (!best_corr) ++ return -ENOTSUPP; ++ ++ chip->ecc.size = best_step; ++ chip->ecc.strength = best_strength; ++ chip->ecc.bytes = best_ecc_bytes; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nand_maximize_ecc); ++ ++/* ++ * Check if the chip configuration meet the datasheet requirements. ++ ++ * If our configuration corrects A bits per B bytes and the minimum ++ * required correction level is X bits per Y bytes, then we must ensure ++ * both of the following are true: ++ * ++ * (1) A / B >= X / Y ++ * (2) A >= X ++ * ++ * Requirement (1) ensures we can correct for the required bitflip density. ++ * Requirement (2) ensures we can correct even when all bitflips are clumped ++ * in the same sector. ++ */ ++static bool nand_ecc_strength_good(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int corr, ds_corr; ++ ++ if (ecc->size == 0 || chip->ecc_step_ds == 0) ++ /* Not enough information */ ++ return true; ++ ++ /* ++ * We get the number of corrected bits per page to compare ++ * the correction density. ++ */ ++ corr = (mtd->writesize * ecc->strength) / ecc->size; ++ ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds; ++ ++ return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; ++} ++ ++/** ++ * nand_scan_tail - [NAND Interface] Scan for the NAND device ++ * @mtd: MTD device structure ++ * ++ * This is the second phase of the normal nand_scan() function. It fills out ++ * all the uninitialized function pointers with the defaults and scans for a ++ * bad block table if appropriate. ++ */ ++int nand_scan_tail(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ret, i; ++ ++ /* New bad blocks should be marked in OOB, flash-based BBT, or both */ ++ if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && ++ !(chip->bbt_options & NAND_BBT_USE_FLASH))) { ++ return -EINVAL; ++ } ++ ++ chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); ++ if (!chip->data_buf) ++ return -ENOMEM; ++ ++ /* ++ * FIXME: some NAND manufacturer drivers expect the first die to be ++ * selected when manufacturer->init() is called. They should be fixed ++ * to explictly select the relevant die when interacting with the NAND ++ * chip. ++ */ ++ chip->select_chip(mtd, 0); ++ ret = nand_manufacturer_init(chip); ++ chip->select_chip(mtd, -1); ++ if (ret) ++ goto err_free_buf; ++ ++ /* Set the internal oob buffer location, just after the page data */ ++ chip->oob_poi = chip->data_buf + mtd->writesize; ++ ++ /* ++ * If no default placement scheme is given, select an appropriate one. ++ */ ++ if (!mtd->ooblayout && ++ !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) { ++ switch (mtd->oobsize) { ++ case 8: ++ case 16: ++ mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); ++ break; ++ case 64: ++ case 128: ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops); ++ break; ++ default: ++ /* ++ * Expose the whole OOB area to users if ECC_NONE ++ * is passed. We could do that for all kind of ++ * ->oobsize, but we must keep the old large/small ++ * page with ECC layout when ->oobsize <= 128 for ++ * compatibility reasons. ++ */ ++ if (ecc->mode == NAND_ECC_NONE) { ++ mtd_set_ooblayout(mtd, ++ &nand_ooblayout_lp_ops); ++ break; ++ } ++ ++ WARN(1, "No oob scheme defined for oobsize %d\n", ++ mtd->oobsize); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ } ++ ++ /* ++ * Check ECC mode, default to software if 3byte/512byte hardware ECC is ++ * selected and we have 256 byte pagesize fallback to software ECC ++ */ ++ ++ switch (ecc->mode) { ++ case NAND_ECC_HW_OOB_FIRST: ++ /* Similar to NAND_ECC_HW, but a separate read_page handle */ ++ if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { ++ WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ if (!ecc->read_page) ++ ecc->read_page = nand_read_page_hwecc_oob_first; ++ ++ case NAND_ECC_HW: ++ /* Use standard hwecc read page function? */ ++ if (!ecc->read_page) ++ ecc->read_page = nand_read_page_hwecc; ++ if (!ecc->write_page) ++ ecc->write_page = nand_write_page_hwecc; ++ if (!ecc->read_page_raw) ++ ecc->read_page_raw = nand_read_page_raw; ++ if (!ecc->write_page_raw) ++ ecc->write_page_raw = nand_write_page_raw; ++ if (!ecc->read_oob) ++ ecc->read_oob = nand_read_oob_std; ++ if (!ecc->write_oob) ++ ecc->write_oob = nand_write_oob_std; ++ if (!ecc->read_subpage) ++ ecc->read_subpage = nand_read_subpage; ++ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) ++ ecc->write_subpage = nand_write_subpage_hwecc; ++ ++ case NAND_ECC_HW_SYNDROME: ++ if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && ++ (!ecc->read_page || ++ ecc->read_page == nand_read_page_hwecc || ++ !ecc->write_page || ++ ecc->write_page == nand_write_page_hwecc)) { ++ WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ /* Use standard syndrome read/write page function? */ ++ if (!ecc->read_page) ++ ecc->read_page = nand_read_page_syndrome; ++ if (!ecc->write_page) ++ ecc->write_page = nand_write_page_syndrome; ++ if (!ecc->read_page_raw) ++ ecc->read_page_raw = nand_read_page_raw_syndrome; ++ if (!ecc->write_page_raw) ++ ecc->write_page_raw = nand_write_page_raw_syndrome; ++ if (!ecc->read_oob) ++ ecc->read_oob = nand_read_oob_syndrome; ++ if (!ecc->write_oob) ++ ecc->write_oob = nand_write_oob_syndrome; ++ ++ if (mtd->writesize >= ecc->size) { ++ if (!ecc->strength) { ++ WARN(1, "Driver must set ecc.strength when using hardware ECC\n"); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ break; ++ } ++ pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", ++ ecc->size, mtd->writesize); ++ ecc->mode = NAND_ECC_SOFT; ++ ecc->algo = NAND_ECC_HAMMING; ++ ++ case NAND_ECC_SOFT: ++ ret = nand_set_ecc_soft_ops(mtd); ++ if (ret) { ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ break; ++ ++ case NAND_ECC_ON_DIE: ++ if (!ecc->read_page || !ecc->write_page) { ++ WARN(1, "No ECC functions supplied; on-die ECC not possible\n"); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ if (!ecc->read_oob) ++ ecc->read_oob = nand_read_oob_std; ++ if (!ecc->write_oob) ++ ecc->write_oob = nand_write_oob_std; ++ break; ++ ++ case NAND_ECC_NONE: ++ pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); ++ ecc->read_page = nand_read_page_raw; ++ ecc->write_page = nand_write_page_raw; ++ ecc->read_oob = nand_read_oob_std; ++ ecc->read_page_raw = nand_read_page_raw; ++ ecc->write_page_raw = nand_write_page_raw; ++ ecc->write_oob = nand_write_oob_std; ++ ecc->size = mtd->writesize; ++ ecc->bytes = 0; ++ ecc->strength = 0; ++ break; ++ ++ default: ++ WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ ++ if (ecc->correct || ecc->calculate) { ++ ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL); ++ ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL); ++ if (!ecc->calc_buf || !ecc->code_buf) { ++ ret = -ENOMEM; ++ goto err_nand_manuf_cleanup; ++ } ++ } ++ ++ /* For many systems, the standard OOB write also works for raw */ ++ if (!ecc->read_oob_raw) ++ ecc->read_oob_raw = ecc->read_oob; ++ if (!ecc->write_oob_raw) ++ ecc->write_oob_raw = ecc->write_oob; ++ ++ /* propagate ecc info to mtd_info */ ++ mtd->ecc_strength = ecc->strength; ++ mtd->ecc_step_size = ecc->size; ++ ++ /* ++ * Set the number of read / write steps for one page depending on ECC ++ * mode. ++ */ ++ ecc->steps = mtd->writesize / ecc->size; ++ if (ecc->steps * ecc->size != mtd->writesize) { ++ WARN(1, "Invalid ECC parameters\n"); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ ecc->total = ecc->steps * ecc->bytes; ++ if (ecc->total > mtd->oobsize) { ++ WARN(1, "Total number of ECC bytes exceeded oobsize\n"); ++ ret = -EINVAL; ++ goto err_nand_manuf_cleanup; ++ } ++ ++ /* ++ * The number of bytes available for a client to place data into ++ * the out of band area. ++ */ ++ ret = mtd_ooblayout_count_freebytes(mtd); ++ if (ret < 0) ++ ret = 0; ++ ++ mtd->oobavail = ret; ++ ++ /* ECC sanity check: warn if it's too weak */ ++ if (!nand_ecc_strength_good(mtd)) ++ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", ++ mtd->name); ++ ++ /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ ++ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { ++ switch (ecc->steps) { ++ case 2: ++ mtd->subpage_sft = 1; ++ break; ++ case 4: ++ case 8: ++ case 16: ++ mtd->subpage_sft = 2; ++ break; ++ } ++ } ++ chip->subpagesize = mtd->writesize >> mtd->subpage_sft; ++ ++ /* Initialize state */ ++ chip->state = FL_READY; ++ ++ /* Invalidate the pagebuffer reference */ ++ chip->pagebuf = -1; ++ ++ /* Large page NAND with SOFT_ECC should support subpage reads */ ++ switch (ecc->mode) { ++ case NAND_ECC_SOFT: ++ if (chip->page_shift > 9) ++ chip->options |= NAND_SUBPAGE_READ; ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* Fill in remaining MTD driver data */ ++ mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; ++ mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : ++ MTD_CAP_NANDFLASH; ++ mtd->_erase = nand_erase; ++ mtd->_point = NULL; ++ mtd->_unpoint = NULL; ++ mtd->_read = nand_read; ++ mtd->_write = nand_write; ++ mtd->_panic_write = panic_nand_write; ++ mtd->_read_oob = nand_read_oob; ++ mtd->_write_oob = nand_write_oob; ++ mtd->_sync = nand_sync; ++ mtd->_lock = NULL; ++ mtd->_unlock = NULL; ++ mtd->_suspend = nand_suspend; ++ mtd->_resume = nand_resume; ++ mtd->_reboot = nand_shutdown; ++ mtd->_block_isreserved = nand_block_isreserved; ++ mtd->_block_isbad = nand_block_isbad; ++ mtd->_block_markbad = nand_block_markbad; ++ mtd->_max_bad_blocks = nand_max_bad_blocks; ++ mtd->writebufsize = mtd->writesize; ++ ++ /* ++ * Initialize bitflip_threshold to its default prior scan_bbt() call. ++ * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be ++ * properly set. ++ */ ++ if (!mtd->bitflip_threshold) ++ mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); ++ ++ /* Initialize the ->data_interface field. */ ++ ret = nand_init_data_interface(chip); ++ if (ret) ++ goto err_nand_manuf_cleanup; ++ ++ /* Enter fastest possible mode on all dies. */ ++ for (i = 0; i < chip->numchips; i++) { ++ chip->select_chip(mtd, i); ++ ret = nand_setup_data_interface(chip, i); ++ chip->select_chip(mtd, -1); ++ ++ if (ret) ++ goto err_nand_manuf_cleanup; ++ } ++ ++ /* Check, if we should skip the bad block table scan */ ++ if (chip->options & NAND_SKIP_BBTSCAN) ++ return 0; ++ ++ /* Build bad block table */ ++ ret = chip->scan_bbt(mtd); ++ if (ret) ++ goto err_nand_manuf_cleanup; ++ ++ return 0; ++ ++ ++err_nand_manuf_cleanup: ++ nand_manufacturer_cleanup(chip); ++ ++err_free_buf: ++ kfree(chip->data_buf); ++ kfree(ecc->code_buf); ++ kfree(ecc->calc_buf); ++ ++ return ret; ++} ++EXPORT_SYMBOL(nand_scan_tail); ++ ++/* ++ * is_module_text_address() isn't exported, and it's mostly a pointless ++ * test if this is a module _anyway_ -- they'd have to try _really_ hard ++ * to call us from in-kernel code if the core NAND support is modular. ++ */ ++#ifdef MODULE ++#define caller_is_module() (1) ++#else ++#define caller_is_module() \ ++ is_module_text_address((unsigned long)__builtin_return_address(0)) ++#endif ++ ++/** ++ * nand_scan - [NAND Interface] Scan for the NAND device ++ * @mtd: MTD device structure ++ * @maxchips: number of chips to scan for ++ * ++ * This fills out all the uninitialized function pointers with the defaults. ++ * The flash ID is read and the mtd/chip structures are filled with the ++ * appropriate values. ++ */ ++int nand_scan(struct mtd_info *mtd, int maxchips) ++{ ++ int ret; ++ ++ ret = nand_scan_ident(mtd, maxchips, NULL); ++ if (!ret) ++ ret = nand_scan_tail(mtd); ++ return ret; ++} ++EXPORT_SYMBOL(nand_scan); ++ ++/** ++ * nand_cleanup - [NAND Interface] Free resources held by the NAND device ++ * @chip: NAND chip object ++ */ ++void nand_cleanup(struct nand_chip *chip) ++{ ++ if (chip->ecc.mode == NAND_ECC_SOFT && ++ chip->ecc.algo == NAND_ECC_BCH) ++ nand_bch_free((struct nand_bch_control *)chip->ecc.priv); ++ ++ /* Free bad block table memory */ ++ kfree(chip->bbt); ++ kfree(chip->data_buf); ++ kfree(chip->ecc.code_buf); ++ kfree(chip->ecc.calc_buf); ++ ++ /* Free bad block descriptor memory */ ++ if (chip->badblock_pattern && chip->badblock_pattern->options ++ & NAND_BBT_DYNAMICSTRUCT) ++ kfree(chip->badblock_pattern); ++ ++ /* Free manufacturer priv data. */ ++ nand_manufacturer_cleanup(chip); ++} ++EXPORT_SYMBOL_GPL(nand_cleanup); ++ ++/** ++ * nand_release - [NAND Interface] Unregister the MTD device and free resources ++ * held by the NAND device ++ * @mtd: MTD device structure ++ */ ++void nand_release(struct mtd_info *mtd) ++{ ++ mtd_device_unregister(mtd); ++ nand_cleanup(mtd_to_nand(mtd)); ++} ++EXPORT_SYMBOL_GPL(nand_release); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Steven J. Hill "); ++MODULE_AUTHOR("Thomas Gleixner "); ++MODULE_DESCRIPTION("Generic NAND flash driver code"); +diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c +new file mode 100644 +index 00000000..3609285 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_bbt.c +@@ -0,0 +1,1452 @@ ++/* ++ * Overview: ++ * Bad block table support for the NAND driver ++ * ++ * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * 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. ++ * ++ * Description: ++ * ++ * When nand_scan_bbt is called, then it tries to find the bad block table ++ * depending on the options in the BBT descriptor(s). If no flash based BBT ++ * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory ++ * marked good / bad blocks. This information is used to create a memory BBT. ++ * Once a new bad block is discovered then the "factory" information is updated ++ * on the device. ++ * If a flash based BBT is specified then the function first tries to find the ++ * BBT on flash. If a BBT is found then the contents are read and the memory ++ * based BBT is created. If a mirrored BBT is selected then the mirror is ++ * searched too and the versions are compared. If the mirror has a greater ++ * version number, then the mirror BBT is used to build the memory based BBT. ++ * If the tables are not versioned, then we "or" the bad block information. ++ * If one of the BBTs is out of date or does not exist it is (re)created. ++ * If no BBT exists at all then the device is scanned for factory marked ++ * good / bad blocks and the bad block tables are created. ++ * ++ * For manufacturer created BBTs like the one found on M-SYS DOC devices ++ * the BBT is searched and read but never created ++ * ++ * The auto generated bad block table is located in the last good blocks ++ * of the device. The table is mirrored, so it can be updated eventually. ++ * The table is marked in the OOB area with an ident pattern and a version ++ * number which indicates which of both tables is more up to date. If the NAND ++ * controller needs the complete OOB area for the ECC information then the ++ * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of ++ * course): it moves the ident pattern and the version byte into the data area ++ * and the OOB area will remain untouched. ++ * ++ * The table uses 2 bits per block ++ * 11b: block is good ++ * 00b: block is factory marked bad ++ * 01b, 10b: block is marked bad due to wear ++ * ++ * The memory bad block table uses the following scheme: ++ * 00b: block is good ++ * 01b: block is marked bad due to wear ++ * 10b: block is reserved (to protect the bbt area) ++ * 11b: block is factory marked bad ++ * ++ * Multichip devices like DOC store the bad block info per floor. ++ * ++ * Following assumptions are made: ++ * - bbts start at a page boundary, if autolocated on a block boundary ++ * - the space necessary for a bbt in FLASH does not exceed a block boundary ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BBT_BLOCK_GOOD 0x00 ++#define BBT_BLOCK_WORN 0x01 ++#define BBT_BLOCK_RESERVED 0x02 ++#define BBT_BLOCK_FACTORY_BAD 0x03 ++ ++#define BBT_ENTRY_MASK 0x03 ++#define BBT_ENTRY_SHIFT 2 ++ ++static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); ++ ++static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) ++{ ++ uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; ++ entry >>= (block & BBT_ENTRY_MASK) * 2; ++ return entry & BBT_ENTRY_MASK; ++} ++ ++static inline void bbt_mark_entry(struct nand_chip *chip, int block, ++ uint8_t mark) ++{ ++ uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); ++ chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; ++} ++ ++static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) ++{ ++ if (memcmp(buf, td->pattern, td->len)) ++ return -1; ++ return 0; ++} ++ ++/** ++ * check_pattern - [GENERIC] check if a pattern is in the buffer ++ * @buf: the buffer to search ++ * @len: the length of buffer to search ++ * @paglen: the pagelength ++ * @td: search pattern descriptor ++ * ++ * Check for a pattern at the given place. Used to search bad block tables and ++ * good / bad block identifiers. ++ */ ++static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) ++{ ++ if (td->options & NAND_BBT_NO_OOB) ++ return check_pattern_no_oob(buf, td); ++ ++ /* Compare the pattern */ ++ if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) ++ return -1; ++ ++ return 0; ++} ++ ++/** ++ * check_short_pattern - [GENERIC] check if a pattern is in the buffer ++ * @buf: the buffer to search ++ * @td: search pattern descriptor ++ * ++ * Check for a pattern at the given place. Used to search bad block tables and ++ * good / bad block identifiers. Same as check_pattern, but no optional empty ++ * check. ++ */ ++static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) ++{ ++ /* Compare the pattern */ ++ if (memcmp(buf + td->offs, td->pattern, td->len)) ++ return -1; ++ return 0; ++} ++ ++/** ++ * add_marker_len - compute the length of the marker in data area ++ * @td: BBT descriptor used for computation ++ * ++ * The length will be 0 if the marker is located in OOB area. ++ */ ++static u32 add_marker_len(struct nand_bbt_descr *td) ++{ ++ u32 len; ++ ++ if (!(td->options & NAND_BBT_NO_OOB)) ++ return 0; ++ ++ len = td->len; ++ if (td->options & NAND_BBT_VERSION) ++ len++; ++ return len; ++} ++ ++/** ++ * read_bbt - [GENERIC] Read the bad block table starting from page ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @page: the starting page ++ * @num: the number of bbt descriptors to read ++ * @td: the bbt describtion table ++ * @offs: block number offset in the table ++ * ++ * Read the bad block table starting from page. ++ */ ++static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, ++ struct nand_bbt_descr *td, int offs) ++{ ++ int res, ret = 0, i, j, act = 0; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ size_t retlen, len, totlen; ++ loff_t from; ++ int bits = td->options & NAND_BBT_NRBITS_MSK; ++ uint8_t msk = (uint8_t)((1 << bits) - 1); ++ u32 marker_len; ++ int reserved_block_code = td->reserved_block_code; ++ ++ totlen = (num * bits) >> 3; ++ marker_len = add_marker_len(td); ++ from = ((loff_t)page) << this->page_shift; ++ ++ while (totlen) { ++ len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); ++ if (marker_len) { ++ /* ++ * In case the BBT marker is not in the OOB area it ++ * will be just in the first page. ++ */ ++ len -= marker_len; ++ from += marker_len; ++ marker_len = 0; ++ } ++ res = mtd_read(mtd, from, len, &retlen, buf); ++ if (res < 0) { ++ if (mtd_is_eccerr(res)) { ++ pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", ++ from & ~mtd->writesize); ++ return res; ++ } else if (mtd_is_bitflip(res)) { ++ pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", ++ from & ~mtd->writesize); ++ ret = res; ++ } else { ++ pr_info("nand_bbt: error reading BBT\n"); ++ return res; ++ } ++ } ++ ++ /* Analyse data */ ++ for (i = 0; i < len; i++) { ++ uint8_t dat = buf[i]; ++ for (j = 0; j < 8; j += bits, act++) { ++ uint8_t tmp = (dat >> j) & msk; ++ if (tmp == msk) ++ continue; ++ if (reserved_block_code && (tmp == reserved_block_code)) { ++ pr_info("nand_read_bbt: reserved block at 0x%012llx\n", ++ (loff_t)(offs + act) << ++ this->bbt_erase_shift); ++ bbt_mark_entry(this, offs + act, ++ BBT_BLOCK_RESERVED); ++ mtd->ecc_stats.bbtblocks++; ++ continue; ++ } ++ /* ++ * Leave it for now, if it's matured we can ++ * move this message to pr_debug. ++ */ ++ pr_info("nand_read_bbt: bad block at 0x%012llx\n", ++ (loff_t)(offs + act) << ++ this->bbt_erase_shift); ++ /* Factory marked bad or worn out? */ ++ if (tmp == 0) ++ bbt_mark_entry(this, offs + act, ++ BBT_BLOCK_FACTORY_BAD); ++ else ++ bbt_mark_entry(this, offs + act, ++ BBT_BLOCK_WORN); ++ mtd->ecc_stats.badblocks++; ++ } ++ } ++ totlen -= len; ++ from += len; ++ } ++ return ret; ++} ++ ++/** ++ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @chip: read the table for a specific chip, -1 read all chips; applies only if ++ * NAND_BBT_PERCHIP option is set ++ * ++ * Read the bad block table for all chips starting at a given page. We assume ++ * that the bbt bits are in consecutive order. ++ */ ++static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int res = 0, i; ++ ++ if (td->options & NAND_BBT_PERCHIP) { ++ int offs = 0; ++ for (i = 0; i < this->numchips; i++) { ++ if (chip == -1 || chip == i) ++ res = read_bbt(mtd, buf, td->pages[i], ++ this->chipsize >> this->bbt_erase_shift, ++ td, offs); ++ if (res) ++ return res; ++ offs += this->chipsize >> this->bbt_erase_shift; ++ } ++ } else { ++ res = read_bbt(mtd, buf, td->pages[0], ++ mtd->size >> this->bbt_erase_shift, td, 0); ++ if (res) ++ return res; ++ } ++ return 0; ++} ++ ++/* BBT marker is in the first page, no OOB */ ++static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, ++ struct nand_bbt_descr *td) ++{ ++ size_t retlen; ++ size_t len; ++ ++ len = td->len; ++ if (td->options & NAND_BBT_VERSION) ++ len++; ++ ++ return mtd_read(mtd, offs, len, &retlen, buf); ++} ++ ++/** ++ * scan_read_oob - [GENERIC] Scan data+OOB region to buffer ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @offs: offset at which to scan ++ * @len: length of data region to read ++ * ++ * Scan read data from data+OOB. May traverse multiple pages, interleaving ++ * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" ++ * ECC condition (error or bitflip). May quit on the first (non-ECC) error. ++ */ ++static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, ++ size_t len) ++{ ++ struct mtd_oob_ops ops; ++ int res, ret = 0; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.ooboffs = 0; ++ ops.ooblen = mtd->oobsize; ++ ++ while (len > 0) { ++ ops.datbuf = buf; ++ ops.len = min(len, (size_t)mtd->writesize); ++ ops.oobbuf = buf + ops.len; ++ ++ res = mtd_read_oob(mtd, offs, &ops); ++ if (res) { ++ if (!mtd_is_bitflip_or_eccerr(res)) ++ return res; ++ else if (mtd_is_eccerr(res) || !ret) ++ ret = res; ++ } ++ ++ buf += mtd->oobsize + mtd->writesize; ++ len -= mtd->writesize; ++ offs += mtd->writesize; ++ } ++ return ret; ++} ++ ++static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, ++ size_t len, struct nand_bbt_descr *td) ++{ ++ if (td->options & NAND_BBT_NO_OOB) ++ return scan_read_data(mtd, buf, offs, td); ++ else ++ return scan_read_oob(mtd, buf, offs, len); ++} ++ ++/* Scan write data with oob to flash */ ++static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, ++ uint8_t *buf, uint8_t *oob) ++{ ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.ooboffs = 0; ++ ops.ooblen = mtd->oobsize; ++ ops.datbuf = buf; ++ ops.oobbuf = oob; ++ ops.len = len; ++ ++ return mtd_write_oob(mtd, offs, &ops); ++} ++ ++static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) ++{ ++ u32 ver_offs = td->veroffs; ++ ++ if (!(td->options & NAND_BBT_NO_OOB)) ++ ver_offs += mtd->writesize; ++ return ver_offs; ++} ++ ++/** ++ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @md: descriptor for the bad block table mirror ++ * ++ * Read the bad block table(s) for all chips starting at a given page. We ++ * assume that the bbt bits are in consecutive order. ++ */ ++static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, ++ struct nand_bbt_descr *td, struct nand_bbt_descr *md) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ /* Read the primary version, if available */ ++ if (td->options & NAND_BBT_VERSION) { ++ scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, ++ mtd->writesize, td); ++ td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; ++ pr_info("Bad block table at page %d, version 0x%02X\n", ++ td->pages[0], td->version[0]); ++ } ++ ++ /* Read the mirror version, if available */ ++ if (md && (md->options & NAND_BBT_VERSION)) { ++ scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, ++ mtd->writesize, md); ++ md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; ++ pr_info("Bad block table at page %d, version 0x%02X\n", ++ md->pages[0], md->version[0]); ++ } ++} ++ ++/* Scan a given block partially */ ++static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, ++ loff_t offs, uint8_t *buf, int numpages) ++{ ++ struct mtd_oob_ops ops; ++ int j, ret; ++ ++ ops.ooblen = mtd->oobsize; ++ ops.oobbuf = buf; ++ ops.ooboffs = 0; ++ ops.datbuf = NULL; ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ++ for (j = 0; j < numpages; j++) { ++ /* ++ * Read the full oob until read_oob is fixed to handle single ++ * byte reads for 16 bit buswidth. ++ */ ++ ret = mtd_read_oob(mtd, offs, &ops); ++ /* Ignore ECC errors when checking for BBM */ ++ if (ret && !mtd_is_bitflip_or_eccerr(ret)) ++ return ret; ++ ++ if (check_short_pattern(buf, bd)) ++ return 1; ++ ++ offs += mtd->writesize; ++ } ++ return 0; ++} ++ ++/** ++ * create_bbt - [GENERIC] Create a bad block table by scanning the device ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @bd: descriptor for the good/bad block search pattern ++ * @chip: create the table for a specific chip, -1 read all chips; applies only ++ * if NAND_BBT_PERCHIP option is set ++ * ++ * Create a bad block table by scanning the device for the given good/bad block ++ * identify pattern. ++ */ ++static int create_bbt(struct mtd_info *mtd, uint8_t *buf, ++ struct nand_bbt_descr *bd, int chip) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int i, numblocks, numpages; ++ int startblock; ++ loff_t from; ++ ++ pr_info("Scanning device for bad blocks\n"); ++ ++ if (bd->options & NAND_BBT_SCAN2NDPAGE) ++ numpages = 2; ++ else ++ numpages = 1; ++ ++ if (chip == -1) { ++ numblocks = mtd->size >> this->bbt_erase_shift; ++ startblock = 0; ++ from = 0; ++ } else { ++ if (chip >= this->numchips) { ++ pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", ++ chip + 1, this->numchips); ++ return -EINVAL; ++ } ++ numblocks = this->chipsize >> this->bbt_erase_shift; ++ startblock = chip * numblocks; ++ numblocks += startblock; ++ from = (loff_t)startblock << this->bbt_erase_shift; ++ } ++ ++ if (this->bbt_options & NAND_BBT_SCANLASTPAGE) ++ from += mtd->erasesize - (mtd->writesize * numpages); ++ ++ for (i = startblock; i < numblocks; i++) { ++ int ret; ++ ++ BUG_ON(bd->options & NAND_BBT_NO_OOB); ++ ++ ret = scan_block_fast(mtd, bd, from, buf, numpages); ++ if (ret < 0) ++ return ret; ++ ++ if (ret) { ++ bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); ++ pr_warn("Bad eraseblock %d at 0x%012llx\n", ++ i, (unsigned long long)from); ++ mtd->ecc_stats.badblocks++; ++ } ++ ++ from += (1 << this->bbt_erase_shift); ++ } ++ return 0; ++} ++ ++/** ++ * search_bbt - [GENERIC] scan the device for a specific bad block table ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * ++ * Read the bad block table by searching for a given ident pattern. Search is ++ * preformed either from the beginning up or from the end of the device ++ * downwards. The search starts always at the start of a block. If the option ++ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains ++ * the bad block information of this chip. This is necessary to provide support ++ * for certain DOC devices. ++ * ++ * The bbt ident pattern resides in the oob area of the first page in a block. ++ */ ++static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int i, chips; ++ int startblock, block, dir; ++ int scanlen = mtd->writesize + mtd->oobsize; ++ int bbtblocks; ++ int blocktopage = this->bbt_erase_shift - this->page_shift; ++ ++ /* Search direction top -> down? */ ++ if (td->options & NAND_BBT_LASTBLOCK) { ++ startblock = (mtd->size >> this->bbt_erase_shift) - 1; ++ dir = -1; ++ } else { ++ startblock = 0; ++ dir = 1; ++ } ++ ++ /* Do we have a bbt per chip? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ chips = this->numchips; ++ bbtblocks = this->chipsize >> this->bbt_erase_shift; ++ startblock &= bbtblocks - 1; ++ } else { ++ chips = 1; ++ bbtblocks = mtd->size >> this->bbt_erase_shift; ++ } ++ ++ for (i = 0; i < chips; i++) { ++ /* Reset version information */ ++ td->version[i] = 0; ++ td->pages[i] = -1; ++ /* Scan the maximum number of blocks */ ++ for (block = 0; block < td->maxblocks; block++) { ++ ++ int actblock = startblock + dir * block; ++ loff_t offs = (loff_t)actblock << this->bbt_erase_shift; ++ ++ /* Read first page */ ++ scan_read(mtd, buf, offs, mtd->writesize, td); ++ if (!check_pattern(buf, scanlen, mtd->writesize, td)) { ++ td->pages[i] = actblock << blocktopage; ++ if (td->options & NAND_BBT_VERSION) { ++ offs = bbt_get_ver_offs(mtd, td); ++ td->version[i] = buf[offs]; ++ } ++ break; ++ } ++ } ++ startblock += this->chipsize >> this->bbt_erase_shift; ++ } ++ /* Check, if we found a bbt for each requested chip */ ++ for (i = 0; i < chips; i++) { ++ if (td->pages[i] == -1) ++ pr_warn("Bad block table not found for chip %d\n", i); ++ else ++ pr_info("Bad block table found at page %d, version 0x%02X\n", ++ td->pages[i], td->version[i]); ++ } ++ return 0; ++} ++ ++/** ++ * search_read_bbts - [GENERIC] scan the device for bad block table(s) ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @md: descriptor for the bad block table mirror ++ * ++ * Search and read the bad block table(s). ++ */ ++static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, ++ struct nand_bbt_descr *td, ++ struct nand_bbt_descr *md) ++{ ++ /* Search the primary table */ ++ search_bbt(mtd, buf, td); ++ ++ /* Search the mirror table */ ++ if (md) ++ search_bbt(mtd, buf, md); ++} ++ ++/** ++ * get_bbt_block - Get the first valid eraseblock suitable to store a BBT ++ * @this: the NAND device ++ * @td: the BBT description ++ * @md: the mirror BBT descriptor ++ * @chip: the CHIP selector ++ * ++ * This functions returns a positive block number pointing a valid eraseblock ++ * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if ++ * all blocks are already used of marked bad. If td->pages[chip] was already ++ * pointing to a valid block we re-use it, otherwise we search for the next ++ * valid one. ++ */ ++static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, ++ struct nand_bbt_descr *md, int chip) ++{ ++ int startblock, dir, page, numblocks, i; ++ ++ /* ++ * There was already a version of the table, reuse the page. This ++ * applies for absolute placement too, as we have the page number in ++ * td->pages. ++ */ ++ if (td->pages[chip] != -1) ++ return td->pages[chip] >> ++ (this->bbt_erase_shift - this->page_shift); ++ ++ numblocks = (int)(this->chipsize >> this->bbt_erase_shift); ++ if (!(td->options & NAND_BBT_PERCHIP)) ++ numblocks *= this->numchips; ++ ++ /* ++ * Automatic placement of the bad block table. Search direction ++ * top -> down? ++ */ ++ if (td->options & NAND_BBT_LASTBLOCK) { ++ startblock = numblocks * (chip + 1) - 1; ++ dir = -1; ++ } else { ++ startblock = chip * numblocks; ++ dir = 1; ++ } ++ ++ for (i = 0; i < td->maxblocks; i++) { ++ int block = startblock + dir * i; ++ ++ /* Check, if the block is bad */ ++ switch (bbt_get_entry(this, block)) { ++ case BBT_BLOCK_WORN: ++ case BBT_BLOCK_FACTORY_BAD: ++ continue; ++ } ++ ++ page = block << (this->bbt_erase_shift - this->page_shift); ++ ++ /* Check, if the block is used by the mirror table */ ++ if (!md || md->pages[chip] != page) ++ return block; ++ } ++ ++ return -ENOSPC; ++} ++ ++/** ++ * mark_bbt_block_bad - Mark one of the block reserved for BBT bad ++ * @this: the NAND device ++ * @td: the BBT description ++ * @chip: the CHIP selector ++ * @block: the BBT block to mark ++ * ++ * Blocks reserved for BBT can become bad. This functions is an helper to mark ++ * such blocks as bad. It takes care of updating the in-memory BBT, marking the ++ * block as bad using a bad block marker and invalidating the associated ++ * td->pages[] entry. ++ */ ++static void mark_bbt_block_bad(struct nand_chip *this, ++ struct nand_bbt_descr *td, ++ int chip, int block) ++{ ++ struct mtd_info *mtd = nand_to_mtd(this); ++ loff_t to; ++ int res; ++ ++ bbt_mark_entry(this, block, BBT_BLOCK_WORN); ++ ++ to = (loff_t)block << this->bbt_erase_shift; ++ res = this->block_markbad(mtd, to); ++ if (res) ++ pr_warn("nand_bbt: error %d while marking block %d bad\n", ++ res, block); ++ ++ td->pages[chip] = -1; ++} ++ ++/** ++ * write_bbt - [GENERIC] (Re)write the bad block table ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @td: descriptor for the bad block table ++ * @md: descriptor for the bad block table mirror ++ * @chipsel: selector for a specific chip, -1 for all ++ * ++ * (Re)write the bad block table. ++ */ ++static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ++ struct nand_bbt_descr *td, struct nand_bbt_descr *md, ++ int chipsel) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct erase_info einfo; ++ int i, res, chip = 0; ++ int bits, page, offs, numblocks, sft, sftmsk; ++ int nrchips, pageoffs, ooboffs; ++ uint8_t msk[4]; ++ uint8_t rcode = td->reserved_block_code; ++ size_t retlen, len = 0; ++ loff_t to; ++ struct mtd_oob_ops ops; ++ ++ ops.ooblen = mtd->oobsize; ++ ops.ooboffs = 0; ++ ops.datbuf = NULL; ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ++ if (!rcode) ++ rcode = 0xff; ++ /* Write bad block table per chip rather than per device? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ numblocks = (int)(this->chipsize >> this->bbt_erase_shift); ++ /* Full device write or specific chip? */ ++ if (chipsel == -1) { ++ nrchips = this->numchips; ++ } else { ++ nrchips = chipsel + 1; ++ chip = chipsel; ++ } ++ } else { ++ numblocks = (int)(mtd->size >> this->bbt_erase_shift); ++ nrchips = 1; ++ } ++ ++ /* Loop through the chips */ ++ while (chip < nrchips) { ++ int block; ++ ++ block = get_bbt_block(this, td, md, chip); ++ if (block < 0) { ++ pr_err("No space left to write bad block table\n"); ++ res = block; ++ goto outerr; ++ } ++ ++ /* ++ * get_bbt_block() returns a block number, shift the value to ++ * get a page number. ++ */ ++ page = block << (this->bbt_erase_shift - this->page_shift); ++ ++ /* Set up shift count and masks for the flash table */ ++ bits = td->options & NAND_BBT_NRBITS_MSK; ++ msk[2] = ~rcode; ++ switch (bits) { ++ case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; ++ msk[3] = 0x01; ++ break; ++ case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; ++ msk[3] = 0x03; ++ break; ++ case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; ++ msk[3] = 0x0f; ++ break; ++ case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; ++ msk[3] = 0xff; ++ break; ++ default: return -EINVAL; ++ } ++ ++ to = ((loff_t)page) << this->page_shift; ++ ++ /* Must we save the block contents? */ ++ if (td->options & NAND_BBT_SAVECONTENT) { ++ /* Make it block aligned */ ++ to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); ++ len = 1 << this->bbt_erase_shift; ++ res = mtd_read(mtd, to, len, &retlen, buf); ++ if (res < 0) { ++ if (retlen != len) { ++ pr_info("nand_bbt: error reading block for writing the bad block table\n"); ++ return res; ++ } ++ pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); ++ } ++ /* Read oob data */ ++ ops.ooblen = (len >> this->page_shift) * mtd->oobsize; ++ ops.oobbuf = &buf[len]; ++ res = mtd_read_oob(mtd, to + mtd->writesize, &ops); ++ if (res < 0 || ops.oobretlen != ops.ooblen) ++ goto outerr; ++ ++ /* Calc the byte offset in the buffer */ ++ pageoffs = page - (int)(to >> this->page_shift); ++ offs = pageoffs << this->page_shift; ++ /* Preset the bbt area with 0xff */ ++ memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); ++ ooboffs = len + (pageoffs * mtd->oobsize); ++ ++ } else if (td->options & NAND_BBT_NO_OOB) { ++ ooboffs = 0; ++ offs = td->len; ++ /* The version byte */ ++ if (td->options & NAND_BBT_VERSION) ++ offs++; ++ /* Calc length */ ++ len = (size_t)(numblocks >> sft); ++ len += offs; ++ /* Make it page aligned! */ ++ len = ALIGN(len, mtd->writesize); ++ /* Preset the buffer with 0xff */ ++ memset(buf, 0xff, len); ++ /* Pattern is located at the begin of first page */ ++ memcpy(buf, td->pattern, td->len); ++ } else { ++ /* Calc length */ ++ len = (size_t)(numblocks >> sft); ++ /* Make it page aligned! */ ++ len = ALIGN(len, mtd->writesize); ++ /* Preset the buffer with 0xff */ ++ memset(buf, 0xff, len + ++ (len >> this->page_shift)* mtd->oobsize); ++ offs = 0; ++ ooboffs = len; ++ /* Pattern is located in oob area of first page */ ++ memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); ++ } ++ ++ if (td->options & NAND_BBT_VERSION) ++ buf[ooboffs + td->veroffs] = td->version[chip]; ++ ++ /* Walk through the memory table */ ++ for (i = 0; i < numblocks; i++) { ++ uint8_t dat; ++ int sftcnt = (i << (3 - sft)) & sftmsk; ++ dat = bbt_get_entry(this, chip * numblocks + i); ++ /* Do not store the reserved bbt blocks! */ ++ buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); ++ } ++ ++ memset(&einfo, 0, sizeof(einfo)); ++ einfo.mtd = mtd; ++ einfo.addr = to; ++ einfo.len = 1 << this->bbt_erase_shift; ++ res = nand_erase_nand(mtd, &einfo, 1); ++ if (res < 0) { ++ pr_warn("nand_bbt: error while erasing BBT block %d\n", ++ res); ++ mark_bbt_block_bad(this, td, chip, block); ++ continue; ++ } ++ ++ res = scan_write_bbt(mtd, to, len, buf, ++ td->options & NAND_BBT_NO_OOB ? NULL : ++ &buf[len]); ++ if (res < 0) { ++ pr_warn("nand_bbt: error while writing BBT block %d\n", ++ res); ++ mark_bbt_block_bad(this, td, chip, block); ++ continue; ++ } ++ ++ pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", ++ (unsigned long long)to, td->version[chip]); ++ ++ /* Mark it as used */ ++ td->pages[chip++] = page; ++ } ++ return 0; ++ ++ outerr: ++ pr_warn("nand_bbt: error while writing bad block table %d\n", res); ++ return res; ++} ++ ++/** ++ * nand_memory_bbt - [GENERIC] create a memory based bad block table ++ * @mtd: MTD device structure ++ * @bd: descriptor for the good/bad block search pattern ++ * ++ * The function creates a memory based bbt by scanning the device for ++ * manufacturer / software marked good / bad blocks. ++ */ ++static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ ++ return create_bbt(mtd, this->data_buf, bd, -1); ++} ++ ++/** ++ * check_create - [GENERIC] create and write bbt(s) if necessary ++ * @mtd: MTD device structure ++ * @buf: temporary buffer ++ * @bd: descriptor for the good/bad block search pattern ++ * ++ * The function checks the results of the previous call to read_bbt and creates ++ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found ++ * for the chip/device. Update is necessary if one of the tables is missing or ++ * the version nr. of one table is less than the other. ++ */ ++static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) ++{ ++ int i, chips, writeops, create, chipsel, res, res2; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct nand_bbt_descr *td = this->bbt_td; ++ struct nand_bbt_descr *md = this->bbt_md; ++ struct nand_bbt_descr *rd, *rd2; ++ ++ /* Do we have a bbt per chip? */ ++ if (td->options & NAND_BBT_PERCHIP) ++ chips = this->numchips; ++ else ++ chips = 1; ++ ++ for (i = 0; i < chips; i++) { ++ writeops = 0; ++ create = 0; ++ rd = NULL; ++ rd2 = NULL; ++ res = res2 = 0; ++ /* Per chip or per device? */ ++ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; ++ /* Mirrored table available? */ ++ if (md) { ++ if (td->pages[i] == -1 && md->pages[i] == -1) { ++ create = 1; ++ writeops = 0x03; ++ } else if (td->pages[i] == -1) { ++ rd = md; ++ writeops = 0x01; ++ } else if (md->pages[i] == -1) { ++ rd = td; ++ writeops = 0x02; ++ } else if (td->version[i] == md->version[i]) { ++ rd = td; ++ if (!(td->options & NAND_BBT_VERSION)) ++ rd2 = md; ++ } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { ++ rd = td; ++ writeops = 0x02; ++ } else { ++ rd = md; ++ writeops = 0x01; ++ } ++ } else { ++ if (td->pages[i] == -1) { ++ create = 1; ++ writeops = 0x01; ++ } else { ++ rd = td; ++ } ++ } ++ ++ if (create) { ++ /* Create the bad block table by scanning the device? */ ++ if (!(td->options & NAND_BBT_CREATE)) ++ continue; ++ ++ /* Create the table in memory by scanning the chip(s) */ ++ if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) ++ create_bbt(mtd, buf, bd, chipsel); ++ ++ td->version[i] = 1; ++ if (md) ++ md->version[i] = 1; ++ } ++ ++ /* Read back first? */ ++ if (rd) { ++ res = read_abs_bbt(mtd, buf, rd, chipsel); ++ if (mtd_is_eccerr(res)) { ++ /* Mark table as invalid */ ++ rd->pages[i] = -1; ++ rd->version[i] = 0; ++ i--; ++ continue; ++ } ++ } ++ /* If they weren't versioned, read both */ ++ if (rd2) { ++ res2 = read_abs_bbt(mtd, buf, rd2, chipsel); ++ if (mtd_is_eccerr(res2)) { ++ /* Mark table as invalid */ ++ rd2->pages[i] = -1; ++ rd2->version[i] = 0; ++ i--; ++ continue; ++ } ++ } ++ ++ /* Scrub the flash table(s)? */ ++ if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) ++ writeops = 0x03; ++ ++ /* Update version numbers before writing */ ++ if (md) { ++ td->version[i] = max(td->version[i], md->version[i]); ++ md->version[i] = td->version[i]; ++ } ++ ++ /* Write the bad block table to the device? */ ++ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { ++ res = write_bbt(mtd, buf, td, md, chipsel); ++ if (res < 0) ++ return res; ++ } ++ ++ /* Write the mirror bad block table to the device? */ ++ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { ++ res = write_bbt(mtd, buf, md, td, chipsel); ++ if (res < 0) ++ return res; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * mark_bbt_regions - [GENERIC] mark the bad block table regions ++ * @mtd: MTD device structure ++ * @td: bad block table descriptor ++ * ++ * The bad block table regions are marked as "bad" to prevent accidental ++ * erasures / writes. The regions are identified by the mark 0x02. ++ */ ++static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int i, j, chips, block, nrblocks, update; ++ uint8_t oldval; ++ ++ /* Do we have a bbt per chip? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ chips = this->numchips; ++ nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); ++ } else { ++ chips = 1; ++ nrblocks = (int)(mtd->size >> this->bbt_erase_shift); ++ } ++ ++ for (i = 0; i < chips; i++) { ++ if ((td->options & NAND_BBT_ABSPAGE) || ++ !(td->options & NAND_BBT_WRITE)) { ++ if (td->pages[i] == -1) ++ continue; ++ block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); ++ oldval = bbt_get_entry(this, block); ++ bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); ++ if ((oldval != BBT_BLOCK_RESERVED) && ++ td->reserved_block_code) ++ nand_update_bbt(mtd, (loff_t)block << ++ this->bbt_erase_shift); ++ continue; ++ } ++ update = 0; ++ if (td->options & NAND_BBT_LASTBLOCK) ++ block = ((i + 1) * nrblocks) - td->maxblocks; ++ else ++ block = i * nrblocks; ++ for (j = 0; j < td->maxblocks; j++) { ++ oldval = bbt_get_entry(this, block); ++ bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); ++ if (oldval != BBT_BLOCK_RESERVED) ++ update = 1; ++ block++; ++ } ++ /* ++ * If we want reserved blocks to be recorded to flash, and some ++ * new ones have been marked, then we need to update the stored ++ * bbts. This should only happen once. ++ */ ++ if (update && td->reserved_block_code) ++ nand_update_bbt(mtd, (loff_t)(block - 1) << ++ this->bbt_erase_shift); ++ } ++} ++ ++/** ++ * verify_bbt_descr - verify the bad block description ++ * @mtd: MTD device structure ++ * @bd: the table to verify ++ * ++ * This functions performs a few sanity checks on the bad block description ++ * table. ++ */ ++static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ u32 pattern_len; ++ u32 bits; ++ u32 table_size; ++ ++ if (!bd) ++ return; ++ ++ pattern_len = bd->len; ++ bits = bd->options & NAND_BBT_NRBITS_MSK; ++ ++ BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && ++ !(this->bbt_options & NAND_BBT_USE_FLASH)); ++ BUG_ON(!bits); ++ ++ if (bd->options & NAND_BBT_VERSION) ++ pattern_len++; ++ ++ if (bd->options & NAND_BBT_NO_OOB) { ++ BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); ++ BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); ++ BUG_ON(bd->offs); ++ if (bd->options & NAND_BBT_VERSION) ++ BUG_ON(bd->veroffs != bd->len); ++ BUG_ON(bd->options & NAND_BBT_SAVECONTENT); ++ } ++ ++ if (bd->options & NAND_BBT_PERCHIP) ++ table_size = this->chipsize >> this->bbt_erase_shift; ++ else ++ table_size = mtd->size >> this->bbt_erase_shift; ++ table_size >>= 3; ++ table_size *= bits; ++ if (bd->options & NAND_BBT_NO_OOB) ++ table_size += pattern_len; ++ BUG_ON(table_size > (1 << this->bbt_erase_shift)); ++} ++ ++/** ++ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) ++ * @mtd: MTD device structure ++ * @bd: descriptor for the good/bad block search pattern ++ * ++ * The function checks, if a bad block table(s) is/are already available. If ++ * not it scans the device for manufacturer marked good / bad blocks and writes ++ * the bad block table(s) to the selected place. ++ * ++ * The bad block table memory is allocated here. It must be freed by calling ++ * the nand_free_bbt function. ++ */ ++static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int len, res; ++ uint8_t *buf; ++ struct nand_bbt_descr *td = this->bbt_td; ++ struct nand_bbt_descr *md = this->bbt_md; ++ ++ len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; ++ /* ++ * Allocate memory (2bit per block) and clear the memory bad block ++ * table. ++ */ ++ this->bbt = kzalloc(len, GFP_KERNEL); ++ if (!this->bbt) ++ return -ENOMEM; ++ ++ /* ++ * If no primary table decriptor is given, scan the device to build a ++ * memory based bad block table. ++ */ ++ if (!td) { ++ if ((res = nand_memory_bbt(mtd, bd))) { ++ pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); ++ goto err; ++ } ++ return 0; ++ } ++ verify_bbt_descr(mtd, td); ++ verify_bbt_descr(mtd, md); ++ ++ /* Allocate a temporary buffer for one eraseblock incl. oob */ ++ len = (1 << this->bbt_erase_shift); ++ len += (len >> this->page_shift) * mtd->oobsize; ++ buf = vmalloc(len); ++ if (!buf) { ++ res = -ENOMEM; ++ goto err; ++ } ++ ++ /* Is the bbt at a given page? */ ++ if (td->options & NAND_BBT_ABSPAGE) { ++ read_abs_bbts(mtd, buf, td, md); ++ } else { ++ /* Search the bad block table using a pattern in oob */ ++ search_read_bbts(mtd, buf, td, md); ++ } ++ ++ res = check_create(mtd, buf, bd); ++ if (res) ++ goto err; ++ ++ /* Prevent the bbt regions from erasing / writing */ ++ mark_bbt_region(mtd, td); ++ if (md) ++ mark_bbt_region(mtd, md); ++ ++ vfree(buf); ++ return 0; ++ ++err: ++ kfree(this->bbt); ++ this->bbt = NULL; ++ return res; ++} ++ ++/** ++ * nand_update_bbt - update bad block table(s) ++ * @mtd: MTD device structure ++ * @offs: the offset of the newly marked block ++ * ++ * The function updates the bad block table(s). ++ */ ++static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int len, res = 0; ++ int chip, chipsel; ++ uint8_t *buf; ++ struct nand_bbt_descr *td = this->bbt_td; ++ struct nand_bbt_descr *md = this->bbt_md; ++ ++ if (!this->bbt || !td) ++ return -EINVAL; ++ ++ /* Allocate a temporary buffer for one eraseblock incl. oob */ ++ len = (1 << this->bbt_erase_shift); ++ len += (len >> this->page_shift) * mtd->oobsize; ++ buf = kmalloc(len, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ /* Do we have a bbt per chip? */ ++ if (td->options & NAND_BBT_PERCHIP) { ++ chip = (int)(offs >> this->chip_shift); ++ chipsel = chip; ++ } else { ++ chip = 0; ++ chipsel = -1; ++ } ++ ++ td->version[chip]++; ++ if (md) ++ md->version[chip]++; ++ ++ /* Write the bad block table to the device? */ ++ if (td->options & NAND_BBT_WRITE) { ++ res = write_bbt(mtd, buf, td, md, chipsel); ++ if (res < 0) ++ goto out; ++ } ++ /* Write the mirror bad block table to the device? */ ++ if (md && (md->options & NAND_BBT_WRITE)) { ++ res = write_bbt(mtd, buf, md, td, chipsel); ++ } ++ ++ out: ++ kfree(buf); ++ return res; ++} ++ ++/* ++ * Define some generic bad / good block scan pattern which are used ++ * while scanning a device for factory marked good / bad blocks. ++ */ ++static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; ++ ++/* Generic flash bbt descriptors */ ++static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; ++static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 8, ++ .len = 4, ++ .veroffs = 12, ++ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, ++ .offs = 8, ++ .len = 4, ++ .veroffs = 12, ++ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, ++ .pattern = mirror_pattern ++}; ++ ++static struct nand_bbt_descr bbt_main_no_oob_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP ++ | NAND_BBT_NO_OOB, ++ .len = 4, ++ .veroffs = 4, ++ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_mirror_no_oob_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP ++ | NAND_BBT_NO_OOB, ++ .len = 4, ++ .veroffs = 4, ++ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, ++ .pattern = mirror_pattern ++}; ++ ++#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) ++/** ++ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure ++ * @this: NAND chip to create descriptor for ++ * ++ * This function allocates and initializes a nand_bbt_descr for BBM detection ++ * based on the properties of @this. The new descriptor is stored in ++ * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when ++ * passed to this function. ++ */ ++static int nand_create_badblock_pattern(struct nand_chip *this) ++{ ++ struct nand_bbt_descr *bd; ++ if (this->badblock_pattern) { ++ pr_warn("Bad block pattern already allocated; not replacing\n"); ++ return -EINVAL; ++ } ++ bd = kzalloc(sizeof(*bd), GFP_KERNEL); ++ if (!bd) ++ return -ENOMEM; ++ bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; ++ bd->offs = this->badblockpos; ++ bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; ++ bd->pattern = scan_ff_pattern; ++ bd->options |= NAND_BBT_DYNAMICSTRUCT; ++ this->badblock_pattern = bd; ++ return 0; ++} ++ ++/** ++ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device ++ * @mtd: MTD device structure ++ * ++ * This function selects the default bad block table support for the device and ++ * calls the nand_scan_bbt function. ++ */ ++int nand_default_bbt(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int ret; ++ ++ /* Is a flash based bad block table requested? */ ++ if (this->bbt_options & NAND_BBT_USE_FLASH) { ++ /* Use the default pattern descriptors */ ++ if (!this->bbt_td) { ++ if (this->bbt_options & NAND_BBT_NO_OOB) { ++ this->bbt_td = &bbt_main_no_oob_descr; ++ this->bbt_md = &bbt_mirror_no_oob_descr; ++ } else { ++ this->bbt_td = &bbt_main_descr; ++ this->bbt_md = &bbt_mirror_descr; ++ } ++ } ++ } else { ++ this->bbt_td = NULL; ++ this->bbt_md = NULL; ++ } ++ ++ if (!this->badblock_pattern) { ++ ret = nand_create_badblock_pattern(this); ++ if (ret) ++ return ret; ++ } ++ ++ return nand_scan_bbt(mtd, this->badblock_pattern); ++} ++ ++/** ++ * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved ++ * @mtd: MTD device structure ++ * @offs: offset in the device ++ */ ++int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int block; ++ ++ block = (int)(offs >> this->bbt_erase_shift); ++ return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; ++} ++ ++/** ++ * nand_isbad_bbt - [NAND Interface] Check if a block is bad ++ * @mtd: MTD device structure ++ * @offs: offset in the device ++ * @allowbbt: allow access to bad block table region ++ */ ++int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int block, res; ++ ++ block = (int)(offs >> this->bbt_erase_shift); ++ res = bbt_get_entry(this, block); ++ ++ pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", ++ (unsigned int)offs, block, res); ++ ++ switch (res) { ++ case BBT_BLOCK_GOOD: ++ return 0; ++ case BBT_BLOCK_WORN: ++ return 1; ++ case BBT_BLOCK_RESERVED: ++ return allowbbt ? 0 : 1; ++ } ++ return 1; ++} ++ ++/** ++ * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT ++ * @mtd: MTD device structure ++ * @offs: offset of the bad block ++ */ ++int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ int block, ret = 0; ++ ++ block = (int)(offs >> this->bbt_erase_shift); ++ ++ /* Mark bad block in memory */ ++ bbt_mark_entry(this, block, BBT_BLOCK_WORN); ++ ++ /* Update flash-based bad block table */ ++ if (this->bbt_options & NAND_BBT_USE_FLASH) ++ ret = nand_update_bbt(mtd, offs); ++ ++ return ret; ++} +diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c +new file mode 100644 +index 00000000..505441c +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_bch.c +@@ -0,0 +1,234 @@ ++/* ++ * This file provides ECC correction for more than 1 bit per block of data, ++ * using binary BCH codes. It relies on the generic BCH library lib/bch.c. ++ * ++ * Copyright © 2011 Ivan Djelic ++ * ++ * This file 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 or (at your option) any ++ * later version. ++ * ++ * This file 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 file; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * struct nand_bch_control - private NAND BCH control structure ++ * @bch: BCH control structure ++ * @errloc: error location array ++ * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid ++ */ ++struct nand_bch_control { ++ struct bch_control *bch; ++ unsigned int *errloc; ++ unsigned char *eccmask; ++}; ++ ++/** ++ * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block ++ * @mtd: MTD block structure ++ * @buf: input buffer with raw data ++ * @code: output buffer with ECC ++ */ ++int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, ++ unsigned char *code) ++{ ++ const struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_bch_control *nbc = chip->ecc.priv; ++ unsigned int i; ++ ++ memset(code, 0, chip->ecc.bytes); ++ encode_bch(nbc->bch, buf, chip->ecc.size, code); ++ ++ /* apply mask so that an erased page is a valid codeword */ ++ for (i = 0; i < chip->ecc.bytes; i++) ++ code[i] ^= nbc->eccmask[i]; ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_bch_calculate_ecc); ++ ++/** ++ * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) ++ * @mtd: MTD block structure ++ * @buf: raw data read from the chip ++ * @read_ecc: ECC from the chip ++ * @calc_ecc: the ECC calculated from raw data ++ * ++ * Detect and correct bit errors for a data byte block ++ */ ++int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, ++ unsigned char *read_ecc, unsigned char *calc_ecc) ++{ ++ const struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_bch_control *nbc = chip->ecc.priv; ++ unsigned int *errloc = nbc->errloc; ++ int i, count; ++ ++ count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, ++ NULL, errloc); ++ if (count > 0) { ++ for (i = 0; i < count; i++) { ++ if (errloc[i] < (chip->ecc.size*8)) ++ /* error is located in data, correct it */ ++ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); ++ /* else error in ecc, no action needed */ ++ ++ pr_debug("%s: corrected bitflip %u\n", __func__, ++ errloc[i]); ++ } ++ } else if (count < 0) { ++ printk(KERN_ERR "ecc unrecoverable error\n"); ++ count = -EBADMSG; ++ } ++ return count; ++} ++EXPORT_SYMBOL(nand_bch_correct_data); ++ ++/** ++ * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction ++ * @mtd: MTD block structure ++ * ++ * Returns: ++ * a pointer to a new NAND BCH control structure, or NULL upon failure ++ * ++ * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes ++ * are used to compute BCH parameters m (Galois field order) and t (error ++ * correction capability). @eccbytes should be equal to the number of bytes ++ * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. ++ * ++ * Example: to configure 4 bit correction per 512 bytes, you should pass ++ * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) ++ * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) ++ */ ++struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ unsigned int m, t, eccsteps, i; ++ struct nand_bch_control *nbc = NULL; ++ unsigned char *erased_page; ++ unsigned int eccsize = nand->ecc.size; ++ unsigned int eccbytes = nand->ecc.bytes; ++ unsigned int eccstrength = nand->ecc.strength; ++ ++ if (!eccbytes && eccstrength) { ++ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); ++ nand->ecc.bytes = eccbytes; ++ } ++ ++ if (!eccsize || !eccbytes) { ++ printk(KERN_WARNING "ecc parameters not supplied\n"); ++ goto fail; ++ } ++ ++ m = fls(1+8*eccsize); ++ t = (eccbytes*8)/m; ++ ++ nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); ++ if (!nbc) ++ goto fail; ++ ++ nbc->bch = init_bch(m, t, 0); ++ if (!nbc->bch) ++ goto fail; ++ ++ /* verify that eccbytes has the expected value */ ++ if (nbc->bch->ecc_bytes != eccbytes) { ++ printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", ++ eccbytes, nbc->bch->ecc_bytes); ++ goto fail; ++ } ++ ++ eccsteps = mtd->writesize/eccsize; ++ ++ /* Check that we have an oob layout description. */ ++ if (!mtd->ooblayout) { ++ pr_warn("missing oob scheme"); ++ goto fail; ++ } ++ ++ /* sanity checks */ ++ if (8*(eccsize+eccbytes) >= (1 << m)) { ++ printk(KERN_WARNING "eccsize %u is too large\n", eccsize); ++ goto fail; ++ } ++ ++ /* ++ * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), ++ * which is called by mtd_ooblayout_count_eccbytes(). ++ * Make sure they are properly initialized before calling ++ * mtd_ooblayout_count_eccbytes(). ++ * FIXME: we should probably rework the sequencing in nand_scan_tail() ++ * to avoid setting those fields twice. ++ */ ++ nand->ecc.steps = eccsteps; ++ nand->ecc.total = eccsteps * eccbytes; ++ if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { ++ printk(KERN_WARNING "invalid ecc layout\n"); ++ goto fail; ++ } ++ ++ nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); ++ nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); ++ if (!nbc->eccmask || !nbc->errloc) ++ goto fail; ++ /* ++ * compute and store the inverted ecc of an erased ecc block ++ */ ++ erased_page = kmalloc(eccsize, GFP_KERNEL); ++ if (!erased_page) ++ goto fail; ++ ++ memset(erased_page, 0xff, eccsize); ++ memset(nbc->eccmask, 0, eccbytes); ++ encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); ++ kfree(erased_page); ++ ++ for (i = 0; i < eccbytes; i++) ++ nbc->eccmask[i] ^= 0xff; ++ ++ if (!eccstrength) ++ nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); ++ ++ return nbc; ++fail: ++ nand_bch_free(nbc); ++ return NULL; ++} ++EXPORT_SYMBOL(nand_bch_init); ++ ++/** ++ * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources ++ * @nbc: NAND BCH control structure ++ */ ++void nand_bch_free(struct nand_bch_control *nbc) ++{ ++ if (nbc) { ++ free_bch(nbc->bch); ++ kfree(nbc->errloc); ++ kfree(nbc->eccmask); ++ kfree(nbc); ++ } ++} ++EXPORT_SYMBOL(nand_bch_free); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ivan Djelic "); ++MODULE_DESCRIPTION("NAND software BCH ECC support"); +diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c +new file mode 100644 +index 00000000..7613a03 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_ecc.c +@@ -0,0 +1,533 @@ ++/* ++ * This file contains an ECC algorithm that detects and corrects 1 bit ++ * errors in a 256 byte block of data. ++ * ++ * drivers/mtd/nand/nand_ecc.c ++ * ++ * Copyright © 2008 Koninklijke Philips Electronics NV. ++ * Author: Frans Meulenbroeks ++ * ++ * Completely replaces the previous ECC implementation which was written by: ++ * Steven J. Hill (sjhill@realitydiluted.com) ++ * Thomas Gleixner (tglx@linutronix.de) ++ * ++ * Information on how this algorithm works and how it was developed ++ * can be found in Documentation/mtd/nand_ecc.txt ++ * ++ * This file 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 or (at your option) any ++ * later version. ++ * ++ * This file 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 file; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ */ ++ ++/* ++ * The STANDALONE macro is useful when running the code outside the kernel ++ * e.g. when running the code in a testbed or a benchmark program. ++ * When STANDALONE is used, the module related macros are commented out ++ * as well as the linux include files. ++ * Instead a private definition of mtd_info is given to satisfy the compiler ++ * (the code does not use mtd_info, so the code does not care) ++ */ ++#ifndef STANDALONE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#else ++#include ++struct mtd_info; ++#define EXPORT_SYMBOL(x) /* x */ ++ ++#define MODULE_LICENSE(x) /* x */ ++#define MODULE_AUTHOR(x) /* x */ ++#define MODULE_DESCRIPTION(x) /* x */ ++ ++#define pr_err printf ++#endif ++ ++/* ++ * invparity is a 256 byte table that contains the odd parity ++ * for each byte. So if the number of bits in a byte is even, ++ * the array element is 1, and when the number of bits is odd ++ * the array eleemnt is 0. ++ */ ++static const char invparity[256] = { ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, ++ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 ++}; ++ ++/* ++ * bitsperbyte contains the number of bits per byte ++ * this is only used for testing and repairing parity ++ * (a precalculated value slightly improves performance) ++ */ ++static const char bitsperbyte[256] = { ++ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, ++}; ++ ++/* ++ * addressbits is a lookup table to filter out the bits from the xor-ed ++ * ECC data that identify the faulty location. ++ * this is only used for repairing parity ++ * see the comments in nand_correct_data for more details ++ */ ++static const char addressbits[256] = { ++ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, ++ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, ++ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, ++ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, ++ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, ++ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, ++ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, ++ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, ++ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, ++ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, ++ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, ++ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, ++ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, ++ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, ++ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, ++ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, ++ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, ++ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, ++ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, ++ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, ++ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, ++ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, ++ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, ++ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, ++ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, ++ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, ++ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, ++ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, ++ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, ++ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, ++ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, ++ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f ++}; ++ ++/** ++ * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte ++ * block ++ * @buf: input buffer with raw data ++ * @eccsize: data bytes per ECC step (256 or 512) ++ * @code: output buffer with ECC ++ */ ++void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, ++ unsigned char *code) ++{ ++ int i; ++ const uint32_t *bp = (uint32_t *)buf; ++ /* 256 or 512 bytes/ecc */ ++ const uint32_t eccsize_mult = eccsize >> 8; ++ uint32_t cur; /* current value in buffer */ ++ /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ ++ uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; ++ uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; ++ uint32_t uninitialized_var(rp17); /* to make compiler happy */ ++ uint32_t par; /* the cumulative parity for all data */ ++ uint32_t tmppar; /* the cumulative parity for this iteration; ++ for rp12, rp14 and rp16 at the end of the ++ loop */ ++ ++ par = 0; ++ rp4 = 0; ++ rp6 = 0; ++ rp8 = 0; ++ rp10 = 0; ++ rp12 = 0; ++ rp14 = 0; ++ rp16 = 0; ++ ++ /* ++ * The loop is unrolled a number of times; ++ * This avoids if statements to decide on which rp value to update ++ * Also we process the data by longwords. ++ * Note: passing unaligned data might give a performance penalty. ++ * It is assumed that the buffers are aligned. ++ * tmppar is the cumulative sum of this iteration. ++ * needed for calculating rp12, rp14, rp16 and par ++ * also used as a performance improvement for rp6, rp8 and rp10 ++ */ ++ for (i = 0; i < eccsize_mult << 2; i++) { ++ cur = *bp++; ++ tmppar = cur; ++ rp4 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp6 ^= tmppar; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp8 ^= tmppar; ++ ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ rp6 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp6 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp10 ^= tmppar; ++ ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ rp6 ^= cur; ++ rp8 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp6 ^= cur; ++ rp8 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ rp8 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp8 ^= cur; ++ ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ rp6 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp6 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ rp4 ^= cur; ++ cur = *bp++; ++ tmppar ^= cur; ++ ++ par ^= tmppar; ++ if ((i & 0x1) == 0) ++ rp12 ^= tmppar; ++ if ((i & 0x2) == 0) ++ rp14 ^= tmppar; ++ if (eccsize_mult == 2 && (i & 0x4) == 0) ++ rp16 ^= tmppar; ++ } ++ ++ /* ++ * handle the fact that we use longword operations ++ * we'll bring rp4..rp14..rp16 back to single byte entities by ++ * shifting and xoring first fold the upper and lower 16 bits, ++ * then the upper and lower 8 bits. ++ */ ++ rp4 ^= (rp4 >> 16); ++ rp4 ^= (rp4 >> 8); ++ rp4 &= 0xff; ++ rp6 ^= (rp6 >> 16); ++ rp6 ^= (rp6 >> 8); ++ rp6 &= 0xff; ++ rp8 ^= (rp8 >> 16); ++ rp8 ^= (rp8 >> 8); ++ rp8 &= 0xff; ++ rp10 ^= (rp10 >> 16); ++ rp10 ^= (rp10 >> 8); ++ rp10 &= 0xff; ++ rp12 ^= (rp12 >> 16); ++ rp12 ^= (rp12 >> 8); ++ rp12 &= 0xff; ++ rp14 ^= (rp14 >> 16); ++ rp14 ^= (rp14 >> 8); ++ rp14 &= 0xff; ++ if (eccsize_mult == 2) { ++ rp16 ^= (rp16 >> 16); ++ rp16 ^= (rp16 >> 8); ++ rp16 &= 0xff; ++ } ++ ++ /* ++ * we also need to calculate the row parity for rp0..rp3 ++ * This is present in par, because par is now ++ * rp3 rp3 rp2 rp2 in little endian and ++ * rp2 rp2 rp3 rp3 in big endian ++ * as well as ++ * rp1 rp0 rp1 rp0 in little endian and ++ * rp0 rp1 rp0 rp1 in big endian ++ * First calculate rp2 and rp3 ++ */ ++#ifdef __BIG_ENDIAN ++ rp2 = (par >> 16); ++ rp2 ^= (rp2 >> 8); ++ rp2 &= 0xff; ++ rp3 = par & 0xffff; ++ rp3 ^= (rp3 >> 8); ++ rp3 &= 0xff; ++#else ++ rp3 = (par >> 16); ++ rp3 ^= (rp3 >> 8); ++ rp3 &= 0xff; ++ rp2 = par & 0xffff; ++ rp2 ^= (rp2 >> 8); ++ rp2 &= 0xff; ++#endif ++ ++ /* reduce par to 16 bits then calculate rp1 and rp0 */ ++ par ^= (par >> 16); ++#ifdef __BIG_ENDIAN ++ rp0 = (par >> 8) & 0xff; ++ rp1 = (par & 0xff); ++#else ++ rp1 = (par >> 8) & 0xff; ++ rp0 = (par & 0xff); ++#endif ++ ++ /* finally reduce par to 8 bits */ ++ par ^= (par >> 8); ++ par &= 0xff; ++ ++ /* ++ * and calculate rp5..rp15..rp17 ++ * note that par = rp4 ^ rp5 and due to the commutative property ++ * of the ^ operator we can say: ++ * rp5 = (par ^ rp4); ++ * The & 0xff seems superfluous, but benchmarking learned that ++ * leaving it out gives slightly worse results. No idea why, probably ++ * it has to do with the way the pipeline in pentium is organized. ++ */ ++ rp5 = (par ^ rp4) & 0xff; ++ rp7 = (par ^ rp6) & 0xff; ++ rp9 = (par ^ rp8) & 0xff; ++ rp11 = (par ^ rp10) & 0xff; ++ rp13 = (par ^ rp12) & 0xff; ++ rp15 = (par ^ rp14) & 0xff; ++ if (eccsize_mult == 2) ++ rp17 = (par ^ rp16) & 0xff; ++ ++ /* ++ * Finally calculate the ECC bits. ++ * Again here it might seem that there are performance optimisations ++ * possible, but benchmarks showed that on the system this is developed ++ * the code below is the fastest ++ */ ++#ifdef CONFIG_MTD_NAND_ECC_SMC ++ code[0] = ++ (invparity[rp7] << 7) | ++ (invparity[rp6] << 6) | ++ (invparity[rp5] << 5) | ++ (invparity[rp4] << 4) | ++ (invparity[rp3] << 3) | ++ (invparity[rp2] << 2) | ++ (invparity[rp1] << 1) | ++ (invparity[rp0]); ++ code[1] = ++ (invparity[rp15] << 7) | ++ (invparity[rp14] << 6) | ++ (invparity[rp13] << 5) | ++ (invparity[rp12] << 4) | ++ (invparity[rp11] << 3) | ++ (invparity[rp10] << 2) | ++ (invparity[rp9] << 1) | ++ (invparity[rp8]); ++#else ++ code[1] = ++ (invparity[rp7] << 7) | ++ (invparity[rp6] << 6) | ++ (invparity[rp5] << 5) | ++ (invparity[rp4] << 4) | ++ (invparity[rp3] << 3) | ++ (invparity[rp2] << 2) | ++ (invparity[rp1] << 1) | ++ (invparity[rp0]); ++ code[0] = ++ (invparity[rp15] << 7) | ++ (invparity[rp14] << 6) | ++ (invparity[rp13] << 5) | ++ (invparity[rp12] << 4) | ++ (invparity[rp11] << 3) | ++ (invparity[rp10] << 2) | ++ (invparity[rp9] << 1) | ++ (invparity[rp8]); ++#endif ++ if (eccsize_mult == 1) ++ code[2] = ++ (invparity[par & 0xf0] << 7) | ++ (invparity[par & 0x0f] << 6) | ++ (invparity[par & 0xcc] << 5) | ++ (invparity[par & 0x33] << 4) | ++ (invparity[par & 0xaa] << 3) | ++ (invparity[par & 0x55] << 2) | ++ 3; ++ else ++ code[2] = ++ (invparity[par & 0xf0] << 7) | ++ (invparity[par & 0x0f] << 6) | ++ (invparity[par & 0xcc] << 5) | ++ (invparity[par & 0x33] << 4) | ++ (invparity[par & 0xaa] << 3) | ++ (invparity[par & 0x55] << 2) | ++ (invparity[rp17] << 1) | ++ (invparity[rp16] << 0); ++} ++EXPORT_SYMBOL(__nand_calculate_ecc); ++ ++/** ++ * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte ++ * block ++ * @mtd: MTD block structure ++ * @buf: input buffer with raw data ++ * @code: output buffer with ECC ++ */ ++int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, ++ unsigned char *code) ++{ ++ __nand_calculate_ecc(buf, ++ mtd_to_nand(mtd)->ecc.size, code); ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_calculate_ecc); ++ ++/** ++ * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) ++ * @buf: raw data read from the chip ++ * @read_ecc: ECC from the chip ++ * @calc_ecc: the ECC calculated from raw data ++ * @eccsize: data bytes per ECC step (256 or 512) ++ * ++ * Detect and correct a 1 bit error for eccsize byte block ++ */ ++int __nand_correct_data(unsigned char *buf, ++ unsigned char *read_ecc, unsigned char *calc_ecc, ++ unsigned int eccsize) ++{ ++ unsigned char b0, b1, b2, bit_addr; ++ unsigned int byte_addr; ++ /* 256 or 512 bytes/ecc */ ++ const uint32_t eccsize_mult = eccsize >> 8; ++ ++ /* ++ * b0 to b2 indicate which bit is faulty (if any) ++ * we might need the xor result more than once, ++ * so keep them in a local var ++ */ ++#ifdef CONFIG_MTD_NAND_ECC_SMC ++ b0 = read_ecc[0] ^ calc_ecc[0]; ++ b1 = read_ecc[1] ^ calc_ecc[1]; ++#else ++ b0 = read_ecc[1] ^ calc_ecc[1]; ++ b1 = read_ecc[0] ^ calc_ecc[0]; ++#endif ++ b2 = read_ecc[2] ^ calc_ecc[2]; ++ ++ /* check if there are any bitfaults */ ++ ++ /* repeated if statements are slightly more efficient than switch ... */ ++ /* ordered in order of likelihood */ ++ ++ if ((b0 | b1 | b2) == 0) ++ return 0; /* no error */ ++ ++ if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && ++ (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && ++ ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || ++ (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { ++ /* single bit error */ ++ /* ++ * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty ++ * byte, cp 5/3/1 indicate the faulty bit. ++ * A lookup table (called addressbits) is used to filter ++ * the bits from the byte they are in. ++ * A marginal optimisation is possible by having three ++ * different lookup tables. ++ * One as we have now (for b0), one for b2 ++ * (that would avoid the >> 1), and one for b1 (with all values ++ * << 4). However it was felt that introducing two more tables ++ * hardly justify the gain. ++ * ++ * The b2 shift is there to get rid of the lowest two bits. ++ * We could also do addressbits[b2] >> 1 but for the ++ * performance it does not make any difference ++ */ ++ if (eccsize_mult == 1) ++ byte_addr = (addressbits[b1] << 4) + addressbits[b0]; ++ else ++ byte_addr = (addressbits[b2 & 0x3] << 8) + ++ (addressbits[b1] << 4) + addressbits[b0]; ++ bit_addr = addressbits[b2 >> 2]; ++ /* flip the bit */ ++ buf[byte_addr] ^= (1 << bit_addr); ++ return 1; ++ ++ } ++ /* count nr of bits; use table lookup, faster than calculating it */ ++ if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) ++ return 1; /* error in ECC data; no action needed */ ++ ++ pr_err("%s: uncorrectable ECC error\n", __func__); ++ return -EBADMSG; ++} ++EXPORT_SYMBOL(__nand_correct_data); ++ ++/** ++ * nand_correct_data - [NAND Interface] Detect and correct bit error(s) ++ * @mtd: MTD block structure ++ * @buf: raw data read from the chip ++ * @read_ecc: ECC from the chip ++ * @calc_ecc: the ECC calculated from raw data ++ * ++ * Detect and correct a 1 bit error for 256/512 byte block ++ */ ++int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, ++ unsigned char *read_ecc, unsigned char *calc_ecc) ++{ ++ return __nand_correct_data(buf, read_ecc, calc_ecc, ++ mtd_to_nand(mtd)->ecc.size); ++} ++EXPORT_SYMBOL(nand_correct_data); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Frans Meulenbroeks "); ++MODULE_DESCRIPTION("Generic NAND ECC support"); +diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c +new file mode 100644 +index 00000000..d542908a +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_hynix.c +@@ -0,0 +1,676 @@ ++/* ++ * Copyright (C) 2017 Free Electrons ++ * Copyright (C) 2017 NextThing Co ++ * ++ * Author: Boris Brezillon ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++ ++#define NAND_HYNIX_CMD_SET_PARAMS 0x36 ++#define NAND_HYNIX_CMD_APPLY_PARAMS 0x16 ++ ++#define NAND_HYNIX_1XNM_RR_REPEAT 8 ++ ++/** ++ * struct hynix_read_retry - read-retry data ++ * @nregs: number of register to set when applying a new read-retry mode ++ * @regs: register offsets (NAND chip dependent) ++ * @values: array of values to set in registers. The array size is equal to ++ * (nregs * nmodes) ++ */ ++struct hynix_read_retry { ++ int nregs; ++ const u8 *regs; ++ u8 values[0]; ++}; ++ ++/** ++ * struct hynix_nand - private Hynix NAND struct ++ * @nand_technology: manufacturing process expressed in picometer ++ * @read_retry: read-retry information ++ */ ++struct hynix_nand { ++ const struct hynix_read_retry *read_retry; ++}; ++ ++/** ++ * struct hynix_read_retry_otp - structure describing how the read-retry OTP ++ * area ++ * @nregs: number of hynix private registers to set before reading the reading ++ * the OTP area ++ * @regs: registers that should be configured ++ * @values: values that should be set in regs ++ * @page: the address to pass to the READ_PAGE command. Depends on the NAND ++ * chip ++ * @size: size of the read-retry OTP section ++ */ ++struct hynix_read_retry_otp { ++ int nregs; ++ const u8 *regs; ++ const u8 *values; ++ int page; ++ int size; ++}; ++ ++static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) ++{ ++ u8 jedecid[5] = { }; ++ int ret; ++ ++ ret = nand_readid_op(chip, 0x40, jedecid, sizeof(jedecid)); ++ if (ret) ++ return false; ++ ++ return !strncmp("JEDEC", jedecid, sizeof(jedecid)); ++} ++ ++static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (chip->exec_op) { ++ struct nand_op_instr instrs[] = { ++ NAND_OP_CMD(cmd, 0), ++ }; ++ struct nand_operation op = NAND_OPERATION(instrs); ++ ++ return nand_exec_op(chip, &op); ++ } ++ ++ chip->cmdfunc(mtd, cmd, -1, -1); ++ ++ return 0; ++} ++ ++static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u16 column = ((u16)addr << 8) | addr; ++ ++ chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); ++ chip->write_byte(mtd, val); ++ ++ return 0; ++} ++ ++static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct hynix_nand *hynix = nand_get_manufacturer_data(chip); ++ const u8 *values; ++ int i, ret; ++ ++ values = hynix->read_retry->values + ++ (retry_mode * hynix->read_retry->nregs); ++ ++ /* Enter 'Set Hynix Parameters' mode */ ++ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); ++ if (ret) ++ return ret; ++ ++ /* ++ * Configure the NAND in the requested read-retry mode. ++ * This is done by setting pre-defined values in internal NAND ++ * registers. ++ * ++ * The set of registers is NAND specific, and the values are either ++ * predefined or extracted from an OTP area on the NAND (values are ++ * probably tweaked at production in this case). ++ */ ++ for (i = 0; i < hynix->read_retry->nregs; i++) { ++ ret = hynix_nand_reg_write_op(chip, hynix->read_retry->regs[i], ++ values[i]); ++ if (ret) ++ return ret; ++ } ++ ++ /* Apply the new settings. */ ++ return hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); ++} ++ ++/** ++ * hynix_get_majority - get the value that is occurring the most in a given ++ * set of values ++ * @in: the array of values to test ++ * @repeat: the size of the in array ++ * @out: pointer used to store the output value ++ * ++ * This function implements the 'majority check' logic that is supposed to ++ * overcome the unreliability of MLC NANDs when reading the OTP area storing ++ * the read-retry parameters. ++ * ++ * It's based on a pretty simple assumption: if we repeat the same value ++ * several times and then take the one that is occurring the most, we should ++ * find the correct value. ++ * Let's hope this dummy algorithm prevents us from losing the read-retry ++ * parameters. ++ */ ++static int hynix_get_majority(const u8 *in, int repeat, u8 *out) ++{ ++ int i, j, half = repeat / 2; ++ ++ /* ++ * We only test the first half of the in array because we must ensure ++ * that the value is at least occurring repeat / 2 times. ++ * ++ * This loop is suboptimal since we may count the occurrences of the ++ * same value several time, but we are doing that on small sets, which ++ * makes it acceptable. ++ */ ++ for (i = 0; i < half; i++) { ++ int cnt = 0; ++ u8 val = in[i]; ++ ++ /* Count all values that are matching the one at index i. */ ++ for (j = i + 1; j < repeat; j++) { ++ if (in[j] == val) ++ cnt++; ++ } ++ ++ /* We found a value occurring more than repeat / 2. */ ++ if (cnt > half) { ++ *out = val; ++ return 0; ++ } ++ } ++ ++ return -EIO; ++} ++ ++static int hynix_read_rr_otp(struct nand_chip *chip, ++ const struct hynix_read_retry_otp *info, ++ void *buf) ++{ ++ int i, ret; ++ ++ ret = nand_reset_op(chip); ++ if (ret) ++ return ret; ++ ++ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < info->nregs; i++) { ++ ret = hynix_nand_reg_write_op(chip, info->regs[i], ++ info->values[i]); ++ if (ret) ++ return ret; ++ } ++ ++ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); ++ if (ret) ++ return ret; ++ ++ /* Sequence to enter OTP mode? */ ++ ret = hynix_nand_cmd_op(chip, 0x17); ++ if (ret) ++ return ret; ++ ++ ret = hynix_nand_cmd_op(chip, 0x4); ++ if (ret) ++ return ret; ++ ++ ret = hynix_nand_cmd_op(chip, 0x19); ++ if (ret) ++ return ret; ++ ++ /* Now read the page */ ++ ret = nand_read_page_op(chip, info->page, 0, buf, info->size); ++ if (ret) ++ return ret; ++ ++ /* Put everything back to normal */ ++ ret = nand_reset_op(chip); ++ if (ret) ++ return ret; ++ ++ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); ++ if (ret) ++ return ret; ++ ++ ret = hynix_nand_reg_write_op(chip, 0x38, 0); ++ if (ret) ++ return ret; ++ ++ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); ++ if (ret) ++ return ret; ++ ++ return nand_read_page_op(chip, 0, 0, NULL, 0); ++} ++ ++#define NAND_HYNIX_1XNM_RR_COUNT_OFFS 0 ++#define NAND_HYNIX_1XNM_RR_REG_COUNT_OFFS 8 ++#define NAND_HYNIX_1XNM_RR_SET_OFFS(x, setsize, inv) \ ++ (16 + ((((x) * 2) + ((inv) ? 1 : 0)) * (setsize))) ++ ++static int hynix_mlc_1xnm_rr_value(const u8 *buf, int nmodes, int nregs, ++ int mode, int reg, bool inv, u8 *val) ++{ ++ u8 tmp[NAND_HYNIX_1XNM_RR_REPEAT]; ++ int val_offs = (mode * nregs) + reg; ++ int set_size = nmodes * nregs; ++ int i, ret; ++ ++ for (i = 0; i < NAND_HYNIX_1XNM_RR_REPEAT; i++) { ++ int set_offs = NAND_HYNIX_1XNM_RR_SET_OFFS(i, set_size, inv); ++ ++ tmp[i] = buf[val_offs + set_offs]; ++ } ++ ++ ret = hynix_get_majority(tmp, NAND_HYNIX_1XNM_RR_REPEAT, val); ++ if (ret) ++ return ret; ++ ++ if (inv) ++ *val = ~*val; ++ ++ return 0; ++} ++ ++static u8 hynix_1xnm_mlc_read_retry_regs[] = { ++ 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf ++}; ++ ++static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip, ++ const struct hynix_read_retry_otp *info) ++{ ++ struct hynix_nand *hynix = nand_get_manufacturer_data(chip); ++ struct hynix_read_retry *rr = NULL; ++ int ret, i, j; ++ u8 nregs, nmodes; ++ u8 *buf; ++ ++ buf = kmalloc(info->size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ ret = hynix_read_rr_otp(chip, info, buf); ++ if (ret) ++ goto out; ++ ++ ret = hynix_get_majority(buf, NAND_HYNIX_1XNM_RR_REPEAT, ++ &nmodes); ++ if (ret) ++ goto out; ++ ++ ret = hynix_get_majority(buf + NAND_HYNIX_1XNM_RR_REPEAT, ++ NAND_HYNIX_1XNM_RR_REPEAT, ++ &nregs); ++ if (ret) ++ goto out; ++ ++ rr = kzalloc(sizeof(*rr) + (nregs * nmodes), GFP_KERNEL); ++ if (!rr) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ for (i = 0; i < nmodes; i++) { ++ for (j = 0; j < nregs; j++) { ++ u8 *val = rr->values + (i * nregs); ++ ++ ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j, ++ false, val); ++ if (!ret) ++ continue; ++ ++ ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j, ++ true, val); ++ if (ret) ++ goto out; ++ } ++ } ++ ++ rr->nregs = nregs; ++ rr->regs = hynix_1xnm_mlc_read_retry_regs; ++ hynix->read_retry = rr; ++ chip->setup_read_retry = hynix_nand_setup_read_retry; ++ chip->read_retries = nmodes; ++ ++out: ++ kfree(buf); ++ ++ if (ret) ++ kfree(rr); ++ ++ return ret; ++} ++ ++static const u8 hynix_mlc_1xnm_rr_otp_regs[] = { 0x38 }; ++static const u8 hynix_mlc_1xnm_rr_otp_values[] = { 0x52 }; ++ ++static const struct hynix_read_retry_otp hynix_mlc_1xnm_rr_otps[] = { ++ { ++ .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs), ++ .regs = hynix_mlc_1xnm_rr_otp_regs, ++ .values = hynix_mlc_1xnm_rr_otp_values, ++ .page = 0x21f, ++ .size = 784 ++ }, ++ { ++ .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs), ++ .regs = hynix_mlc_1xnm_rr_otp_regs, ++ .values = hynix_mlc_1xnm_rr_otp_values, ++ .page = 0x200, ++ .size = 528, ++ }, ++}; ++ ++static int hynix_nand_rr_init(struct nand_chip *chip) ++{ ++ int i, ret = 0; ++ bool valid_jedecid; ++ ++ valid_jedecid = hynix_nand_has_valid_jedecid(chip); ++ ++ /* ++ * We only support read-retry for 1xnm NANDs, and those NANDs all ++ * expose a valid JEDEC ID. ++ */ ++ if (valid_jedecid) { ++ u8 nand_tech = chip->id.data[5] >> 4; ++ ++ /* 1xnm technology */ ++ if (nand_tech == 4) { ++ for (i = 0; i < ARRAY_SIZE(hynix_mlc_1xnm_rr_otps); ++ i++) { ++ /* ++ * FIXME: Hynix recommend to copy the ++ * read-retry OTP area into a normal page. ++ */ ++ ret = hynix_mlc_1xnm_rr_init(chip, ++ hynix_mlc_1xnm_rr_otps); ++ if (!ret) ++ break; ++ } ++ } ++ } ++ ++ if (ret) ++ pr_warn("failed to initialize read-retry infrastructure"); ++ ++ return 0; ++} ++ ++static void hynix_nand_extract_oobsize(struct nand_chip *chip, ++ bool valid_jedecid) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 oobsize; ++ ++ oobsize = ((chip->id.data[3] >> 2) & 0x3) | ++ ((chip->id.data[3] >> 4) & 0x4); ++ ++ if (valid_jedecid) { ++ switch (oobsize) { ++ case 0: ++ mtd->oobsize = 2048; ++ break; ++ case 1: ++ mtd->oobsize = 1664; ++ break; ++ case 2: ++ mtd->oobsize = 1024; ++ break; ++ case 3: ++ mtd->oobsize = 640; ++ break; ++ default: ++ /* ++ * We should never reach this case, but if that ++ * happens, this probably means Hynix decided to use ++ * a different extended ID format, and we should find ++ * a way to support it. ++ */ ++ WARN(1, "Invalid OOB size"); ++ break; ++ } ++ } else { ++ switch (oobsize) { ++ case 0: ++ mtd->oobsize = 128; ++ break; ++ case 1: ++ mtd->oobsize = 224; ++ break; ++ case 2: ++ mtd->oobsize = 448; ++ break; ++ case 3: ++ mtd->oobsize = 64; ++ break; ++ case 4: ++ mtd->oobsize = 32; ++ break; ++ case 5: ++ mtd->oobsize = 16; ++ break; ++ case 6: ++ mtd->oobsize = 640; ++ break; ++ default: ++ /* ++ * We should never reach this case, but if that ++ * happens, this probably means Hynix decided to use ++ * a different extended ID format, and we should find ++ * a way to support it. ++ */ ++ WARN(1, "Invalid OOB size"); ++ break; ++ } ++ } ++} ++ ++static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, ++ bool valid_jedecid) ++{ ++ u8 ecc_level = (chip->id.data[4] >> 4) & 0x7; ++ ++ if (valid_jedecid) { ++ /* Reference: H27UCG8T2E datasheet */ ++ chip->ecc_step_ds = 1024; ++ ++ switch (ecc_level) { ++ case 0: ++ chip->ecc_step_ds = 0; ++ chip->ecc_strength_ds = 0; ++ break; ++ case 1: ++ chip->ecc_strength_ds = 4; ++ break; ++ case 2: ++ chip->ecc_strength_ds = 24; ++ break; ++ case 3: ++ chip->ecc_strength_ds = 32; ++ break; ++ case 4: ++ chip->ecc_strength_ds = 40; ++ break; ++ case 5: ++ chip->ecc_strength_ds = 50; ++ break; ++ case 6: ++ chip->ecc_strength_ds = 60; ++ break; ++ default: ++ /* ++ * We should never reach this case, but if that ++ * happens, this probably means Hynix decided to use ++ * a different extended ID format, and we should find ++ * a way to support it. ++ */ ++ WARN(1, "Invalid ECC requirements"); ++ } ++ } else { ++ /* ++ * The ECC requirements field meaning depends on the ++ * NAND technology. ++ */ ++ u8 nand_tech = chip->id.data[5] & 0x7; ++ ++ if (nand_tech < 3) { ++ /* > 26nm, reference: H27UBG8T2A datasheet */ ++ if (ecc_level < 5) { ++ chip->ecc_step_ds = 512; ++ chip->ecc_strength_ds = 1 << ecc_level; ++ } else if (ecc_level < 7) { ++ if (ecc_level == 5) ++ chip->ecc_step_ds = 2048; ++ else ++ chip->ecc_step_ds = 1024; ++ chip->ecc_strength_ds = 24; ++ } else { ++ /* ++ * We should never reach this case, but if that ++ * happens, this probably means Hynix decided ++ * to use a different extended ID format, and ++ * we should find a way to support it. ++ */ ++ WARN(1, "Invalid ECC requirements"); ++ } ++ } else { ++ /* <= 26nm, reference: H27UBG8T2B datasheet */ ++ if (!ecc_level) { ++ chip->ecc_step_ds = 0; ++ chip->ecc_strength_ds = 0; ++ } else if (ecc_level < 5) { ++ chip->ecc_step_ds = 512; ++ chip->ecc_strength_ds = 1 << (ecc_level - 1); ++ } else { ++ chip->ecc_step_ds = 1024; ++ chip->ecc_strength_ds = 24 + ++ (8 * (ecc_level - 5)); ++ } ++ } ++ } ++} ++ ++static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip, ++ bool valid_jedecid) ++{ ++ u8 nand_tech; ++ ++ /* We need scrambling on all TLC NANDs*/ ++ if (chip->bits_per_cell > 2) ++ chip->options |= NAND_NEED_SCRAMBLING; ++ ++ /* And on MLC NANDs with sub-3xnm process */ ++ if (valid_jedecid) { ++ nand_tech = chip->id.data[5] >> 4; ++ ++ /* < 3xnm */ ++ if (nand_tech > 0) ++ chip->options |= NAND_NEED_SCRAMBLING; ++ } else { ++ nand_tech = chip->id.data[5] & 0x7; ++ ++ /* < 32nm */ ++ if (nand_tech > 2) ++ chip->options |= NAND_NEED_SCRAMBLING; ++ } ++} ++ ++static void hynix_nand_decode_id(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ bool valid_jedecid; ++ u8 tmp; ++ ++ /* ++ * Exclude all SLC NANDs from this advanced detection scheme. ++ * According to the ranges defined in several datasheets, it might ++ * appear that even SLC NANDs could fall in this extended ID scheme. ++ * If that the case rework the test to let SLC NANDs go through the ++ * detection process. ++ */ ++ if (chip->id.len < 6 || nand_is_slc(chip)) { ++ nand_decode_ext_id(chip); ++ return; ++ } ++ ++ /* Extract pagesize */ ++ mtd->writesize = 2048 << (chip->id.data[3] & 0x03); ++ ++ tmp = (chip->id.data[3] >> 4) & 0x3; ++ /* ++ * When bit7 is set that means we start counting at 1MiB, otherwise ++ * we start counting at 128KiB and shift this value the content of ++ * ID[3][4:5]. ++ * The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in ++ * this case the erasesize is set to 768KiB. ++ */ ++ if (chip->id.data[3] & 0x80) ++ mtd->erasesize = SZ_1M << tmp; ++ else if (tmp == 3) ++ mtd->erasesize = SZ_512K + SZ_256K; ++ else ++ mtd->erasesize = SZ_128K << tmp; ++ ++ /* ++ * Modern Toggle DDR NANDs have a valid JEDECID even though they are ++ * not exposing a valid JEDEC parameter table. ++ * These NANDs use a different NAND ID scheme. ++ */ ++ valid_jedecid = hynix_nand_has_valid_jedecid(chip); ++ ++ hynix_nand_extract_oobsize(chip, valid_jedecid); ++ hynix_nand_extract_ecc_requirements(chip, valid_jedecid); ++ hynix_nand_extract_scrambling_requirements(chip, valid_jedecid); ++} ++ ++static void hynix_nand_cleanup(struct nand_chip *chip) ++{ ++ struct hynix_nand *hynix = nand_get_manufacturer_data(chip); ++ ++ if (!hynix) ++ return; ++ ++ kfree(hynix->read_retry); ++ kfree(hynix); ++ nand_set_manufacturer_data(chip, NULL); ++} ++ ++static int hynix_nand_init(struct nand_chip *chip) ++{ ++ struct hynix_nand *hynix; ++ int ret; ++ ++ if (!nand_is_slc(chip)) ++ chip->bbt_options |= NAND_BBT_SCANLASTPAGE; ++ else ++ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; ++ ++ hynix = kzalloc(sizeof(*hynix), GFP_KERNEL); ++ if (!hynix) ++ return -ENOMEM; ++ ++ nand_set_manufacturer_data(chip, hynix); ++ ++ ret = hynix_nand_rr_init(chip); ++ if (ret) ++ hynix_nand_cleanup(chip); ++ ++ return ret; ++} ++ ++const struct nand_manufacturer_ops hynix_nand_manuf_ops = { ++ .detect = hynix_nand_decode_id, ++ .init = hynix_nand_init, ++ .cleanup = hynix_nand_cleanup, ++}; +diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c +new file mode 100644 +index 00000000..e9e20cc +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_ids.c +@@ -0,0 +1,221 @@ ++/* ++ * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include ++#include ++ ++#define LP_OPTIONS 0 ++#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) ++ ++#define SP_OPTIONS NAND_NEED_READRDY ++#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) ++ ++/* ++ * The chip ID list: ++ * name, device ID, page size, chip size in MiB, eraseblock size, options ++ * ++ * If page size and eraseblock size are 0, the sizes are taken from the ++ * extended chip ID. ++ */ ++struct nand_flash_dev nand_flash_ids[] = { ++ /* ++ * Some incompatible NAND chips share device ID's and so must be ++ * listed by full ID. We list them first so that we can easily identify ++ * the most specific match. ++ */ ++ {"TC58NVG0S3E 1G 3.3V 8-bit", ++ { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, ++ SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), ++ 2 }, ++ {"TC58NVG2S0F 4G 3.3V 8-bit", ++ { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, ++ SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, ++ {"TC58NVG2S0H 4G 3.3V 8-bit", ++ { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, ++ SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, ++ {"TC58NVG3S0F 8G 3.3V 8-bit", ++ { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, ++ SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, ++ {"TC58NVG5D2 32G 3.3V 8-bit", ++ { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, ++ SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, ++ {"TC58NVG6D2 64G 3.3V 8-bit", ++ { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, ++ SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, ++ {"SDTNRGAMA 64G 3.3V 8-bit", ++ { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, ++ SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, ++ {"H27UCG8T2ATR-BC 64G 3.3V 8-bit", ++ { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, ++ SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, ++ NAND_ECC_INFO(40, SZ_1K), 4 }, ++ ++ LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), ++ ++ LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), ++ LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), ++ ++ LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), ++ LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), ++ ++ LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), ++ LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), ++ ++ LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), ++ LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), ++ LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), ++ LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), ++ LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), ++ ++ LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), ++ ++ /* ++ * These are the new chips with large page size. Their page size and ++ * eraseblock size are determined from the extended ID bytes. ++ */ ++ ++ /* 512 Megabit */ ++ EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), ++ ++ /* 1 Gigabit */ ++ EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), ++ ++ /* 2 Gigabit */ ++ EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), ++ ++ /* 4 Gigabit */ ++ EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), ++ ++ /* 8 Gigabit */ ++ EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), ++ ++ /* 16 Gigabit */ ++ EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), ++ ++ /* 32 Gigabit */ ++ EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), ++ ++ /* 64 Gigabit */ ++ EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), ++ ++ /* 128 Gigabit */ ++ EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), ++ ++ /* 256 Gigabit */ ++ EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), ++ ++ /* 512 Gigabit */ ++ EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), ++ EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), ++ EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), ++ ++ /* Micron MT29F1G01AAADD, via staging/mt29f_spinand */ ++ { ++ .name = "NAND 128MiB 3,3V 1-bit", ++ .mfr_id = NAND_MFR_MICRON, ++ .dev_id = 0x12, ++ .pagesize = 2048, ++ .chipsize = 128, ++ .erasesize = SZ_128K, ++ .options = NAND_BBT_SCAN2NDPAGE, ++ .id_len = 2, ++ .oobsize = 64, ++ .ecc = NAND_ECC_INFO(4, 512), ++ }, ++ ++ {NULL} ++}; ++ ++/* Manufacturer IDs */ ++static const struct nand_manufacturer nand_manufacturers[] = { ++ {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops}, ++ {NAND_MFR_ESMT, "ESMT"}, ++ {NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops}, ++ {NAND_MFR_FUJITSU, "Fujitsu"}, ++ {NAND_MFR_NATIONAL, "National"}, ++ {NAND_MFR_RENESAS, "Renesas"}, ++ {NAND_MFR_STMICRO, "ST Micro"}, ++ {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops}, ++ {NAND_MFR_MICRON, "Micron", µn_nand_manuf_ops}, ++ {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops}, ++ {NAND_MFR_MACRONIX, "Macronix", ¯onix_nand_manuf_ops}, ++ {NAND_MFR_EON, "Eon"}, ++ {NAND_MFR_SANDISK, "SanDisk"}, ++ {NAND_MFR_INTEL, "Intel"}, ++ {NAND_MFR_ATO, "ATO"}, ++ {NAND_MFR_WINBOND, "Winbond"}, ++}; ++ ++/** ++ * nand_get_manufacturer - Get manufacturer information from the manufacturer ++ * ID ++ * @id: manufacturer ID ++ * ++ * Returns a pointer a nand_manufacturer object if the manufacturer is defined ++ * in the NAND manufacturers database, NULL otherwise. ++ */ ++const struct nand_manufacturer *nand_get_manufacturer(u8 id) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++) ++ if (nand_manufacturers[i].id == id) ++ return &nand_manufacturers[i]; ++ ++ return NULL; ++} +diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c +new file mode 100644 +index 00000000..d290ff2 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_macronix.c +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (C) 2017 Free Electrons ++ * Copyright (C) 2017 NextThing Co ++ * ++ * Author: Boris Brezillon ++ * ++ * 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 ++ ++static int macronix_nand_init(struct nand_chip *chip) ++{ ++ if (nand_is_slc(chip)) ++ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; ++ ++ return 0; ++} ++ ++const struct nand_manufacturer_ops macronix_nand_manuf_ops = { ++ .init = macronix_nand_init, ++}; +diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c +new file mode 100644 +index 00000000..02e109a +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_micron.c +@@ -0,0 +1,289 @@ ++/* ++ * Copyright (C) 2017 Free Electrons ++ * Copyright (C) 2017 NextThing Co ++ * ++ * Author: Boris Brezillon ++ * ++ * 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 ++ ++/* ++ * Special Micron status bit that indicates when the block has been ++ * corrected by on-die ECC and should be rewritten ++ */ ++#define NAND_STATUS_WRITE_RECOMMENDED BIT(3) ++ ++struct nand_onfi_vendor_micron { ++ u8 two_plane_read; ++ u8 read_cache; ++ u8 read_unique_id; ++ u8 dq_imped; ++ u8 dq_imped_num_settings; ++ u8 dq_imped_feat_addr; ++ u8 rb_pulldown_strength; ++ u8 rb_pulldown_strength_feat_addr; ++ u8 rb_pulldown_strength_num_settings; ++ u8 otp_mode; ++ u8 otp_page_start; ++ u8 otp_data_prot_addr; ++ u8 otp_num_pages; ++ u8 otp_feat_addr; ++ u8 read_retry_options; ++ u8 reserved[72]; ++ u8 param_revision; ++} __packed; ++ ++static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; ++ ++ return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, ++ feature); ++} ++ ++/* ++ * Configure chip properties from Micron vendor-specific ONFI table ++ */ ++static int micron_nand_onfi_init(struct nand_chip *chip) ++{ ++ struct nand_onfi_params *p = &chip->onfi_params; ++ struct nand_onfi_vendor_micron *micron = (void *)p->vendor; ++ ++ if (!chip->onfi_version) ++ return 0; ++ ++ if (le16_to_cpu(p->vendor_revision) < 1) ++ return 0; ++ ++ chip->read_retries = micron->read_retry_options; ++ chip->setup_read_retry = micron_nand_setup_read_retry; ++ ++ return 0; ++} ++ ++static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section >= 4) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 8; ++ oobregion->length = 8; ++ ++ return 0; ++} ++ ++static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section >= 4) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 2; ++ oobregion->length = 6; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = { ++ .ecc = micron_nand_on_die_ooblayout_ecc, ++ .free = micron_nand_on_die_ooblayout_free, ++}; ++ ++static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) ++{ ++ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; ++ ++ if (enable) ++ feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; ++ ++ return chip->onfi_set_features(nand_to_mtd(chip), chip, ++ ONFI_FEATURE_ON_DIE_ECC, feature); ++} ++ ++static int ++micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, ++ int page) ++{ ++ u8 status; ++ int ret, max_bitflips = 0; ++ ++ ret = micron_nand_on_die_ecc_setup(chip, true); ++ if (ret) ++ return ret; ++ ++ ret = nand_read_page_op(chip, page, 0, NULL, 0); ++ if (ret) ++ goto out; ++ ++ ret = nand_status_op(chip, &status); ++ if (ret) ++ goto out; ++ ++ ret = nand_exit_status_op(chip); ++ if (ret) ++ goto out; ++ ++ if (status & NAND_STATUS_FAIL) ++ mtd->ecc_stats.failed++; ++ ++ /* ++ * The internal ECC doesn't tell us the number of bitflips ++ * that have been corrected, but tells us if it recommends to ++ * rewrite the block. If it's the case, then we pretend we had ++ * a number of bitflips equal to the ECC strength, which will ++ * hint the NAND core to rewrite the block. ++ */ ++ else if (status & NAND_STATUS_WRITE_RECOMMENDED) ++ max_bitflips = chip->ecc.strength; ++ ++ ret = nand_read_data_op(chip, buf, mtd->writesize, false); ++ if (!ret && oob_required) ++ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, ++ false); ++ ++out: ++ micron_nand_on_die_ecc_setup(chip, false); ++ ++ return ret ? ret : max_bitflips; ++} ++ ++static int ++micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ int ret; ++ ++ ret = micron_nand_on_die_ecc_setup(chip, true); ++ if (ret) ++ return ret; ++ ++ ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); ++ micron_nand_on_die_ecc_setup(chip, false); ++ ++ return ret; ++} ++ ++enum { ++ /* The NAND flash doesn't support on-die ECC */ ++ MICRON_ON_DIE_UNSUPPORTED, ++ ++ /* ++ * The NAND flash supports on-die ECC and it can be ++ * enabled/disabled by a set features command. ++ */ ++ MICRON_ON_DIE_SUPPORTED, ++ ++ /* ++ * The NAND flash supports on-die ECC, and it cannot be ++ * disabled. ++ */ ++ MICRON_ON_DIE_MANDATORY, ++}; ++ ++/* ++ * Try to detect if the NAND support on-die ECC. To do this, we enable ++ * the feature, and read back if it has been enabled as expected. We ++ * also check if it can be disabled, because some Micron NANDs do not ++ * allow disabling the on-die ECC and we don't support such NANDs for ++ * now. ++ * ++ * This function also has the side effect of disabling on-die ECC if ++ * it had been left enabled by the firmware/bootloader. ++ */ ++static int micron_supports_on_die_ecc(struct nand_chip *chip) ++{ ++ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; ++ int ret; ++ ++ if (chip->onfi_version == 0) ++ return MICRON_ON_DIE_UNSUPPORTED; ++ ++ if (chip->bits_per_cell != 1) ++ return MICRON_ON_DIE_UNSUPPORTED; ++ ++ ret = micron_nand_on_die_ecc_setup(chip, true); ++ if (ret) ++ return MICRON_ON_DIE_UNSUPPORTED; ++ ++ chip->onfi_get_features(nand_to_mtd(chip), chip, ++ ONFI_FEATURE_ON_DIE_ECC, feature); ++ if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) ++ return MICRON_ON_DIE_UNSUPPORTED; ++ ++ ret = micron_nand_on_die_ecc_setup(chip, false); ++ if (ret) ++ return MICRON_ON_DIE_UNSUPPORTED; ++ ++ chip->onfi_get_features(nand_to_mtd(chip), chip, ++ ONFI_FEATURE_ON_DIE_ECC, feature); ++ if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) ++ return MICRON_ON_DIE_MANDATORY; ++ ++ /* ++ * Some Micron NANDs have an on-die ECC of 4/512, some other ++ * 8/512. We only support the former. ++ */ ++ if (chip->onfi_params.ecc_bits != 4) ++ return MICRON_ON_DIE_UNSUPPORTED; ++ ++ return MICRON_ON_DIE_SUPPORTED; ++} ++ ++static int micron_nand_init(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ondie; ++ int ret; ++ ++ ret = micron_nand_onfi_init(chip); ++ if (ret) ++ return ret; ++ ++ if (mtd->writesize == 2048) ++ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; ++ ++ ondie = micron_supports_on_die_ecc(chip); ++ ++ if (ondie == MICRON_ON_DIE_MANDATORY) { ++ pr_err("On-die ECC forcefully enabled, not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (chip->ecc.mode == NAND_ECC_ON_DIE) { ++ if (ondie == MICRON_ON_DIE_UNSUPPORTED) { ++ pr_err("On-die ECC selected but not supported\n"); ++ return -EINVAL; ++ } ++ ++ chip->ecc.bytes = 8; ++ chip->ecc.size = 512; ++ chip->ecc.strength = 4; ++ chip->ecc.algo = NAND_ECC_BCH; ++ chip->ecc.read_page = micron_nand_read_page_on_die_ecc; ++ chip->ecc.write_page = micron_nand_write_page_on_die_ecc; ++ chip->ecc.read_page_raw = nand_read_page_raw; ++ chip->ecc.write_page_raw = nand_write_page_raw; ++ ++ mtd_set_ooblayout(mtd, µn_nand_on_die_ooblayout_ops); ++ } ++ ++ return 0; ++} ++ ++const struct nand_manufacturer_ops micron_nand_manuf_ops = { ++ .init = micron_nand_init, ++}; +diff --git a/drivers/mtd/nand/raw/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c +new file mode 100644 +index 00000000..d348f01 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_samsung.c +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (C) 2017 Free Electrons ++ * Copyright (C) 2017 NextThing Co ++ * ++ * Author: Boris Brezillon ++ * ++ * 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 ++ ++static void samsung_nand_decode_id(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ /* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */ ++ if (chip->id.len == 6 && !nand_is_slc(chip) && ++ chip->id.data[5] != 0x00) { ++ u8 extid = chip->id.data[3]; ++ ++ /* Get pagesize */ ++ mtd->writesize = 2048 << (extid & 0x03); ++ ++ extid >>= 2; ++ ++ /* Get oobsize */ ++ switch (((extid >> 2) & 0x4) | (extid & 0x3)) { ++ case 1: ++ mtd->oobsize = 128; ++ break; ++ case 2: ++ mtd->oobsize = 218; ++ break; ++ case 3: ++ mtd->oobsize = 400; ++ break; ++ case 4: ++ mtd->oobsize = 436; ++ break; ++ case 5: ++ mtd->oobsize = 512; ++ break; ++ case 6: ++ mtd->oobsize = 640; ++ break; ++ default: ++ /* ++ * We should never reach this case, but if that ++ * happens, this probably means Samsung decided to use ++ * a different extended ID format, and we should find ++ * a way to support it. ++ */ ++ WARN(1, "Invalid OOB size value"); ++ break; ++ } ++ ++ /* Get blocksize */ ++ extid >>= 2; ++ mtd->erasesize = (128 * 1024) << ++ (((extid >> 1) & 0x04) | (extid & 0x03)); ++ ++ /* Extract ECC requirements from 5th id byte*/ ++ extid = (chip->id.data[4] >> 4) & 0x07; ++ if (extid < 5) { ++ chip->ecc_step_ds = 512; ++ chip->ecc_strength_ds = 1 << extid; ++ } else { ++ chip->ecc_step_ds = 1024; ++ switch (extid) { ++ case 5: ++ chip->ecc_strength_ds = 24; ++ break; ++ case 6: ++ chip->ecc_strength_ds = 40; ++ break; ++ case 7: ++ chip->ecc_strength_ds = 60; ++ break; ++ default: ++ WARN(1, "Could not decode ECC info"); ++ chip->ecc_step_ds = 0; ++ } ++ } ++ } else { ++ nand_decode_ext_id(chip); ++ } ++} ++ ++static int samsung_nand_init(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ if (mtd->writesize > 512) ++ chip->options |= NAND_SAMSUNG_LP_OPTIONS; ++ ++ if (!nand_is_slc(chip)) ++ chip->bbt_options |= NAND_BBT_SCANLASTPAGE; ++ else ++ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; ++ ++ return 0; ++} ++ ++const struct nand_manufacturer_ops samsung_nand_manuf_ops = { ++ .detect = samsung_nand_decode_id, ++ .init = samsung_nand_init, ++}; +diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c +new file mode 100644 +index 00000000..9400d03 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_timings.c +@@ -0,0 +1,324 @@ ++/* ++ * Copyright (C) 2014 Free Electrons ++ * ++ * Author: Boris BREZILLON ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++static const struct nand_data_interface onfi_sdr_timings[] = { ++ /* Mode 0 */ ++ { ++ .type = NAND_SDR_IFACE, ++ .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, ++ .tADL_min = 400000, ++ .tALH_min = 20000, ++ .tALS_min = 50000, ++ .tAR_min = 25000, ++ .tCEA_max = 100000, ++ .tCEH_min = 20000, ++ .tCH_min = 20000, ++ .tCHZ_max = 100000, ++ .tCLH_min = 20000, ++ .tCLR_min = 20000, ++ .tCLS_min = 50000, ++ .tCOH_min = 0, ++ .tCS_min = 70000, ++ .tDH_min = 20000, ++ .tDS_min = 40000, ++ .tFEAT_max = 1000000, ++ .tIR_min = 10000, ++ .tITC_max = 1000000, ++ .tRC_min = 100000, ++ .tREA_max = 40000, ++ .tREH_min = 30000, ++ .tRHOH_min = 0, ++ .tRHW_min = 200000, ++ .tRHZ_max = 200000, ++ .tRLOH_min = 0, ++ .tRP_min = 50000, ++ .tRR_min = 40000, ++ .tRST_max = 250000000000ULL, ++ .tWB_max = 200000, ++ .tWC_min = 100000, ++ .tWH_min = 30000, ++ .tWHR_min = 120000, ++ .tWP_min = 50000, ++ .tWW_min = 100000, ++ }, ++ }, ++ /* Mode 1 */ ++ { ++ .type = NAND_SDR_IFACE, ++ .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, ++ .tADL_min = 400000, ++ .tALH_min = 10000, ++ .tALS_min = 25000, ++ .tAR_min = 10000, ++ .tCEA_max = 45000, ++ .tCEH_min = 20000, ++ .tCH_min = 10000, ++ .tCHZ_max = 50000, ++ .tCLH_min = 10000, ++ .tCLR_min = 10000, ++ .tCLS_min = 25000, ++ .tCOH_min = 15000, ++ .tCS_min = 35000, ++ .tDH_min = 10000, ++ .tDS_min = 20000, ++ .tFEAT_max = 1000000, ++ .tIR_min = 0, ++ .tITC_max = 1000000, ++ .tRC_min = 50000, ++ .tREA_max = 30000, ++ .tREH_min = 15000, ++ .tRHOH_min = 15000, ++ .tRHW_min = 100000, ++ .tRHZ_max = 100000, ++ .tRLOH_min = 0, ++ .tRP_min = 25000, ++ .tRR_min = 20000, ++ .tRST_max = 500000000, ++ .tWB_max = 100000, ++ .tWC_min = 45000, ++ .tWH_min = 15000, ++ .tWHR_min = 80000, ++ .tWP_min = 25000, ++ .tWW_min = 100000, ++ }, ++ }, ++ /* Mode 2 */ ++ { ++ .type = NAND_SDR_IFACE, ++ .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, ++ .tADL_min = 400000, ++ .tALH_min = 10000, ++ .tALS_min = 15000, ++ .tAR_min = 10000, ++ .tCEA_max = 30000, ++ .tCEH_min = 20000, ++ .tCH_min = 10000, ++ .tCHZ_max = 50000, ++ .tCLH_min = 10000, ++ .tCLR_min = 10000, ++ .tCLS_min = 15000, ++ .tCOH_min = 15000, ++ .tCS_min = 25000, ++ .tDH_min = 5000, ++ .tDS_min = 15000, ++ .tFEAT_max = 1000000, ++ .tIR_min = 0, ++ .tITC_max = 1000000, ++ .tRC_min = 35000, ++ .tREA_max = 25000, ++ .tREH_min = 15000, ++ .tRHOH_min = 15000, ++ .tRHW_min = 100000, ++ .tRHZ_max = 100000, ++ .tRLOH_min = 0, ++ .tRR_min = 20000, ++ .tRST_max = 500000000, ++ .tWB_max = 100000, ++ .tRP_min = 17000, ++ .tWC_min = 35000, ++ .tWH_min = 15000, ++ .tWHR_min = 80000, ++ .tWP_min = 17000, ++ .tWW_min = 100000, ++ }, ++ }, ++ /* Mode 3 */ ++ { ++ .type = NAND_SDR_IFACE, ++ .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, ++ .tADL_min = 400000, ++ .tALH_min = 5000, ++ .tALS_min = 10000, ++ .tAR_min = 10000, ++ .tCEA_max = 25000, ++ .tCEH_min = 20000, ++ .tCH_min = 5000, ++ .tCHZ_max = 50000, ++ .tCLH_min = 5000, ++ .tCLR_min = 10000, ++ .tCLS_min = 10000, ++ .tCOH_min = 15000, ++ .tCS_min = 25000, ++ .tDH_min = 5000, ++ .tDS_min = 10000, ++ .tFEAT_max = 1000000, ++ .tIR_min = 0, ++ .tITC_max = 1000000, ++ .tRC_min = 30000, ++ .tREA_max = 20000, ++ .tREH_min = 10000, ++ .tRHOH_min = 15000, ++ .tRHW_min = 100000, ++ .tRHZ_max = 100000, ++ .tRLOH_min = 0, ++ .tRP_min = 15000, ++ .tRR_min = 20000, ++ .tRST_max = 500000000, ++ .tWB_max = 100000, ++ .tWC_min = 30000, ++ .tWH_min = 10000, ++ .tWHR_min = 80000, ++ .tWP_min = 15000, ++ .tWW_min = 100000, ++ }, ++ }, ++ /* Mode 4 */ ++ { ++ .type = NAND_SDR_IFACE, ++ .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, ++ .tADL_min = 400000, ++ .tALH_min = 5000, ++ .tALS_min = 10000, ++ .tAR_min = 10000, ++ .tCEA_max = 25000, ++ .tCEH_min = 20000, ++ .tCH_min = 5000, ++ .tCHZ_max = 30000, ++ .tCLH_min = 5000, ++ .tCLR_min = 10000, ++ .tCLS_min = 10000, ++ .tCOH_min = 15000, ++ .tCS_min = 20000, ++ .tDH_min = 5000, ++ .tDS_min = 10000, ++ .tFEAT_max = 1000000, ++ .tIR_min = 0, ++ .tITC_max = 1000000, ++ .tRC_min = 25000, ++ .tREA_max = 20000, ++ .tREH_min = 10000, ++ .tRHOH_min = 15000, ++ .tRHW_min = 100000, ++ .tRHZ_max = 100000, ++ .tRLOH_min = 5000, ++ .tRP_min = 12000, ++ .tRR_min = 20000, ++ .tRST_max = 500000000, ++ .tWB_max = 100000, ++ .tWC_min = 25000, ++ .tWH_min = 10000, ++ .tWHR_min = 80000, ++ .tWP_min = 12000, ++ .tWW_min = 100000, ++ }, ++ }, ++ /* Mode 5 */ ++ { ++ .type = NAND_SDR_IFACE, ++ .timings.sdr = { ++ .tCCS_min = 500000, ++ .tR_max = 200000000, ++ .tADL_min = 400000, ++ .tALH_min = 5000, ++ .tALS_min = 10000, ++ .tAR_min = 10000, ++ .tCEA_max = 25000, ++ .tCEH_min = 20000, ++ .tCH_min = 5000, ++ .tCHZ_max = 30000, ++ .tCLH_min = 5000, ++ .tCLR_min = 10000, ++ .tCLS_min = 10000, ++ .tCOH_min = 15000, ++ .tCS_min = 15000, ++ .tDH_min = 5000, ++ .tDS_min = 7000, ++ .tFEAT_max = 1000000, ++ .tIR_min = 0, ++ .tITC_max = 1000000, ++ .tRC_min = 20000, ++ .tREA_max = 16000, ++ .tREH_min = 7000, ++ .tRHOH_min = 15000, ++ .tRHW_min = 100000, ++ .tRHZ_max = 100000, ++ .tRLOH_min = 5000, ++ .tRP_min = 10000, ++ .tRR_min = 20000, ++ .tRST_max = 500000000, ++ .tWB_max = 100000, ++ .tWC_min = 20000, ++ .tWH_min = 7000, ++ .tWHR_min = 80000, ++ .tWP_min = 10000, ++ .tWW_min = 100000, ++ }, ++ }, ++}; ++ ++/** ++ * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND ++ * timings according to the given ONFI timing mode ++ * @mode: ONFI timing mode ++ */ ++const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) ++{ ++ if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) ++ return ERR_PTR(-EINVAL); ++ ++ return &onfi_sdr_timings[mode].timings.sdr; ++} ++EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); ++ ++/** ++ * onfi_fill_data_interface - [NAND Interface] Initialize a data interface from ++ * given ONFI mode ++ * @mode: The ONFI timing mode ++ */ ++int onfi_fill_data_interface(struct nand_chip *chip, ++ enum nand_data_interface_type type, ++ int timing_mode) ++{ ++ struct nand_data_interface *iface = &chip->data_interface; ++ ++ if (type != NAND_SDR_IFACE) ++ return -EINVAL; ++ ++ if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings)) ++ return -EINVAL; ++ ++ *iface = onfi_sdr_timings[timing_mode]; ++ ++ /* ++ * Initialize timings that cannot be deduced from timing mode: ++ * tR, tPROG, tCCS, ... ++ * These information are part of the ONFI parameter page. ++ */ ++ if (chip->onfi_version) { ++ struct nand_onfi_params *params = &chip->onfi_params; ++ struct nand_sdr_timings *timings = &iface->timings.sdr; ++ ++ /* microseconds -> picoseconds */ ++ timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); ++ timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); ++ timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); ++ ++ /* nanoseconds -> picoseconds */ ++ timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(onfi_fill_data_interface); +diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c +new file mode 100644 +index 00000000..57df857 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nand_toshiba.c +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (C) 2017 Free Electrons ++ * Copyright (C) 2017 NextThing Co ++ * ++ * Author: Boris Brezillon ++ * ++ * 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 ++ ++static void toshiba_nand_decode_id(struct nand_chip *chip) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ nand_decode_ext_id(chip); ++ ++ /* ++ * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per ++ * 512B page. For Toshiba SLC, we decode the 5th/6th byte as ++ * follows: ++ * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, ++ * 110b -> 24nm ++ * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC ++ */ ++ if (chip->id.len >= 6 && nand_is_slc(chip) && ++ (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && ++ !(chip->id.data[4] & 0x80) /* !BENAND */) ++ mtd->oobsize = 32 * mtd->writesize >> 9; ++} ++ ++static int toshiba_nand_init(struct nand_chip *chip) ++{ ++ if (nand_is_slc(chip)) ++ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; ++ ++ return 0; ++} ++ ++const struct nand_manufacturer_ops toshiba_nand_manuf_ops = { ++ .detect = toshiba_nand_decode_id, ++ .init = toshiba_nand_init, ++}; +diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c +new file mode 100644 +index 00000000..44322a3 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nandsim.c +@@ -0,0 +1,2392 @@ ++/* ++ * NAND flash simulator. ++ * ++ * Author: Artem B. Bityuckiy , ++ * ++ * Copyright (C) 2004 Nokia Corporation ++ * ++ * Note: NS means "NAND Simulator". ++ * Note: Input means input TO flash chip, output means output FROM chip. ++ * ++ * 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, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Default simulator parameters values */ ++#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ ++ !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ ++ !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ ++ !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) ++#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 ++#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 ++#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ ++#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ ++#endif ++ ++#ifndef CONFIG_NANDSIM_ACCESS_DELAY ++#define CONFIG_NANDSIM_ACCESS_DELAY 25 ++#endif ++#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY ++#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 ++#endif ++#ifndef CONFIG_NANDSIM_ERASE_DELAY ++#define CONFIG_NANDSIM_ERASE_DELAY 2 ++#endif ++#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE ++#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 ++#endif ++#ifndef CONFIG_NANDSIM_INPUT_CYCLE ++#define CONFIG_NANDSIM_INPUT_CYCLE 50 ++#endif ++#ifndef CONFIG_NANDSIM_BUS_WIDTH ++#define CONFIG_NANDSIM_BUS_WIDTH 8 ++#endif ++#ifndef CONFIG_NANDSIM_DO_DELAYS ++#define CONFIG_NANDSIM_DO_DELAYS 0 ++#endif ++#ifndef CONFIG_NANDSIM_LOG ++#define CONFIG_NANDSIM_LOG 0 ++#endif ++#ifndef CONFIG_NANDSIM_DBG ++#define CONFIG_NANDSIM_DBG 0 ++#endif ++#ifndef CONFIG_NANDSIM_MAX_PARTS ++#define CONFIG_NANDSIM_MAX_PARTS 32 ++#endif ++ ++static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; ++static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; ++static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; ++static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; ++static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; ++static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; ++static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; ++static uint log = CONFIG_NANDSIM_LOG; ++static uint dbg = CONFIG_NANDSIM_DBG; ++static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS]; ++static unsigned int parts_num; ++static char *badblocks = NULL; ++static char *weakblocks = NULL; ++static char *weakpages = NULL; ++static unsigned int bitflips = 0; ++static char *gravepages = NULL; ++static unsigned int overridesize = 0; ++static char *cache_file = NULL; ++static unsigned int bbt; ++static unsigned int bch; ++static u_char id_bytes[8] = { ++ [0] = CONFIG_NANDSIM_FIRST_ID_BYTE, ++ [1] = CONFIG_NANDSIM_SECOND_ID_BYTE, ++ [2] = CONFIG_NANDSIM_THIRD_ID_BYTE, ++ [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE, ++ [4 ... 7] = 0xFF, ++}; ++ ++module_param_array(id_bytes, byte, NULL, 0400); ++module_param_named(first_id_byte, id_bytes[0], byte, 0400); ++module_param_named(second_id_byte, id_bytes[1], byte, 0400); ++module_param_named(third_id_byte, id_bytes[2], byte, 0400); ++module_param_named(fourth_id_byte, id_bytes[3], byte, 0400); ++module_param(access_delay, uint, 0400); ++module_param(programm_delay, uint, 0400); ++module_param(erase_delay, uint, 0400); ++module_param(output_cycle, uint, 0400); ++module_param(input_cycle, uint, 0400); ++module_param(bus_width, uint, 0400); ++module_param(do_delays, uint, 0400); ++module_param(log, uint, 0400); ++module_param(dbg, uint, 0400); ++module_param_array(parts, ulong, &parts_num, 0400); ++module_param(badblocks, charp, 0400); ++module_param(weakblocks, charp, 0400); ++module_param(weakpages, charp, 0400); ++module_param(bitflips, uint, 0400); ++module_param(gravepages, charp, 0400); ++module_param(overridesize, uint, 0400); ++module_param(cache_file, charp, 0400); ++module_param(bbt, uint, 0400); ++module_param(bch, uint, 0400); ++ ++MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read ID' command"); ++MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)"); ++MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)"); ++MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command (obsolete)"); ++MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)"); ++MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); ++MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); ++MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); ++MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)"); ++MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)"); ++MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); ++MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); ++MODULE_PARM_DESC(log, "Perform logging if not zero"); ++MODULE_PARM_DESC(dbg, "Output debug information if not zero"); ++MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas"); ++/* Page and erase block positions for the following parameters are independent of any partitions */ ++MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas"); ++MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]" ++ " separated by commas e.g. 113:2 means eb 113" ++ " can be erased only twice before failing"); ++MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]" ++ " separated by commas e.g. 1401:2 means page 1401" ++ " can be written only twice before failing"); ++MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)"); ++MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" ++ " separated by commas e.g. 1401:2 means page 1401" ++ " can be read only twice before failing"); ++MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " ++ "The size is specified in erase blocks and as the exponent of a power of two" ++ " e.g. 5 means a size of 32 erase blocks"); ++MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); ++MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area"); ++MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " ++ "be correctable in 512-byte blocks"); ++ ++/* The largest possible page size */ ++#define NS_LARGEST_PAGE_SIZE 4096 ++ ++/* The prefix for simulator output */ ++#define NS_OUTPUT_PREFIX "[nandsim]" ++ ++/* Simulator's output macros (logging, debugging, warning, error) */ ++#define NS_LOG(args...) \ ++ do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) ++#define NS_DBG(args...) \ ++ do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) ++#define NS_WARN(args...) \ ++ do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) ++#define NS_ERR(args...) \ ++ do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) ++#define NS_INFO(args...) \ ++ do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) ++ ++/* Busy-wait delay macros (microseconds, milliseconds) */ ++#define NS_UDELAY(us) \ ++ do { if (do_delays) udelay(us); } while(0) ++#define NS_MDELAY(us) \ ++ do { if (do_delays) mdelay(us); } while(0) ++ ++/* Is the nandsim structure initialized ? */ ++#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) ++ ++/* Good operation completion status */ ++#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) ++ ++/* Operation failed completion status */ ++#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) ++ ++/* Calculate the page offset in flash RAM image by (row, column) address */ ++#define NS_RAW_OFFSET(ns) \ ++ (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column) ++ ++/* Calculate the OOB offset in flash RAM image by (row, column) address */ ++#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) ++ ++/* After a command is input, the simulator goes to one of the following states */ ++#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ ++#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ ++#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ ++#define STATE_CMD_PAGEPROG 0x00000004 /* start page program */ ++#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ ++#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ ++#define STATE_CMD_STATUS 0x00000007 /* read status */ ++#define STATE_CMD_SEQIN 0x00000009 /* sequential data input */ ++#define STATE_CMD_READID 0x0000000A /* read ID */ ++#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ ++#define STATE_CMD_RESET 0x0000000C /* reset */ ++#define STATE_CMD_RNDOUT 0x0000000D /* random output command */ ++#define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */ ++#define STATE_CMD_MASK 0x0000000F /* command states mask */ ++ ++/* After an address is input, the simulator goes to one of these states */ ++#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ ++#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ ++#define STATE_ADDR_COLUMN 0x00000030 /* column address was accepted */ ++#define STATE_ADDR_ZERO 0x00000040 /* one byte zero address was accepted */ ++#define STATE_ADDR_MASK 0x00000070 /* address states mask */ ++ ++/* During data input/output the simulator is in these states */ ++#define STATE_DATAIN 0x00000100 /* waiting for data input */ ++#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ ++ ++#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ ++#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ ++#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ ++#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ ++ ++/* Previous operation is done, ready to accept new requests */ ++#define STATE_READY 0x00000000 ++ ++/* This state is used to mark that the next state isn't known yet */ ++#define STATE_UNKNOWN 0x10000000 ++ ++/* Simulator's actions bit masks */ ++#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ ++#define ACTION_PRGPAGE 0x00200000 /* program the internal buffer to flash */ ++#define ACTION_SECERASE 0x00300000 /* erase sector */ ++#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ ++#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ ++#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ ++#define ACTION_MASK 0x00700000 /* action mask */ ++ ++#define NS_OPER_NUM 13 /* Number of operations supported by the simulator */ ++#define NS_OPER_STATES 6 /* Maximum number of states in operation */ ++ ++#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ ++#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ ++#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ ++#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ ++#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ ++#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ ++#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */ ++ ++/* Remove action bits from state */ ++#define NS_STATE(x) ((x) & ~ACTION_MASK) ++ ++/* ++ * Maximum previous states which need to be saved. Currently saving is ++ * only needed for page program operation with preceded read command ++ * (which is only valid for 512-byte pages). ++ */ ++#define NS_MAX_PREVSTATES 1 ++ ++/* Maximum page cache pages needed to read or write a NAND page to the cache_file */ ++#define NS_MAX_HELD_PAGES 16 ++ ++/* ++ * A union to represent flash memory contents and flash buffer. ++ */ ++union ns_mem { ++ u_char *byte; /* for byte access */ ++ uint16_t *word; /* for 16-bit word access */ ++}; ++ ++/* ++ * The structure which describes all the internal simulator data. ++ */ ++struct nandsim { ++ struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS]; ++ unsigned int nbparts; ++ ++ uint busw; /* flash chip bus width (8 or 16) */ ++ u_char ids[8]; /* chip's ID bytes */ ++ uint32_t options; /* chip's characteristic bits */ ++ uint32_t state; /* current chip state */ ++ uint32_t nxstate; /* next expected state */ ++ ++ uint32_t *op; /* current operation, NULL operations isn't known yet */ ++ uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ ++ uint16_t npstates; /* number of previous states saved */ ++ uint16_t stateidx; /* current state index */ ++ ++ /* The simulated NAND flash pages array */ ++ union ns_mem *pages; ++ ++ /* Slab allocator for nand pages */ ++ struct kmem_cache *nand_pages_slab; ++ ++ /* Internal buffer of page + OOB size bytes */ ++ union ns_mem buf; ++ ++ /* NAND flash "geometry" */ ++ struct { ++ uint64_t totsz; /* total flash size, bytes */ ++ uint32_t secsz; /* flash sector (erase block) size, bytes */ ++ uint pgsz; /* NAND flash page size, bytes */ ++ uint oobsz; /* page OOB area size, bytes */ ++ uint64_t totszoob; /* total flash size including OOB, bytes */ ++ uint pgszoob; /* page size including OOB , bytes*/ ++ uint secszoob; /* sector size including OOB, bytes */ ++ uint pgnum; /* total number of pages */ ++ uint pgsec; /* number of pages per sector */ ++ uint secshift; /* bits number in sector size */ ++ uint pgshift; /* bits number in page size */ ++ uint pgaddrbytes; /* bytes per page address */ ++ uint secaddrbytes; /* bytes per sector address */ ++ uint idbytes; /* the number ID bytes that this chip outputs */ ++ } geom; ++ ++ /* NAND flash internal registers */ ++ struct { ++ unsigned command; /* the command register */ ++ u_char status; /* the status register */ ++ uint row; /* the page number */ ++ uint column; /* the offset within page */ ++ uint count; /* internal counter */ ++ uint num; /* number of bytes which must be processed */ ++ uint off; /* fixed page offset */ ++ } regs; ++ ++ /* NAND flash lines state */ ++ struct { ++ int ce; /* chip Enable */ ++ int cle; /* command Latch Enable */ ++ int ale; /* address Latch Enable */ ++ int wp; /* write Protect */ ++ } lines; ++ ++ /* Fields needed when using a cache file */ ++ struct file *cfile; /* Open file */ ++ unsigned long *pages_written; /* Which pages have been written */ ++ void *file_buf; ++ struct page *held_pages[NS_MAX_HELD_PAGES]; ++ int held_cnt; ++}; ++ ++/* ++ * Operations array. To perform any operation the simulator must pass ++ * through the correspondent states chain. ++ */ ++static struct nandsim_operations { ++ uint32_t reqopts; /* options which are required to perform the operation */ ++ uint32_t states[NS_OPER_STATES]; /* operation's states */ ++} ops[NS_OPER_NUM] = { ++ /* Read page + OOB from the beginning */ ++ {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Read page + OOB from the second half */ ++ {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Read OOB */ ++ {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Program page starting from the beginning */ ++ {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, ++ STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Program page starting from the beginning */ ++ {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, ++ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Program page starting from the second half */ ++ {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, ++ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Program OOB */ ++ {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, ++ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, ++ /* Erase sector */ ++ {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, ++ /* Read status */ ++ {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, ++ /* Read ID */ ++ {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, ++ /* Large page devices read page */ ++ {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++ /* Large page devices random page read */ ++ {OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY, ++ STATE_DATAOUT, STATE_READY}}, ++}; ++ ++struct weak_block { ++ struct list_head list; ++ unsigned int erase_block_no; ++ unsigned int max_erases; ++ unsigned int erases_done; ++}; ++ ++static LIST_HEAD(weak_blocks); ++ ++struct weak_page { ++ struct list_head list; ++ unsigned int page_no; ++ unsigned int max_writes; ++ unsigned int writes_done; ++}; ++ ++static LIST_HEAD(weak_pages); ++ ++struct grave_page { ++ struct list_head list; ++ unsigned int page_no; ++ unsigned int max_reads; ++ unsigned int reads_done; ++}; ++ ++static LIST_HEAD(grave_pages); ++ ++static unsigned long *erase_block_wear = NULL; ++static unsigned int wear_eb_count = 0; ++static unsigned long total_wear = 0; ++ ++/* MTD structure for NAND controller */ ++static struct mtd_info *nsmtd; ++ ++static int nandsim_debugfs_show(struct seq_file *m, void *private) ++{ ++ unsigned long wmin = -1, wmax = 0, avg; ++ unsigned long deciles[10], decile_max[10], tot = 0; ++ unsigned int i; ++ ++ /* Calc wear stats */ ++ for (i = 0; i < wear_eb_count; ++i) { ++ unsigned long wear = erase_block_wear[i]; ++ if (wear < wmin) ++ wmin = wear; ++ if (wear > wmax) ++ wmax = wear; ++ tot += wear; ++ } ++ ++ for (i = 0; i < 9; ++i) { ++ deciles[i] = 0; ++ decile_max[i] = (wmax * (i + 1) + 5) / 10; ++ } ++ deciles[9] = 0; ++ decile_max[9] = wmax; ++ for (i = 0; i < wear_eb_count; ++i) { ++ int d; ++ unsigned long wear = erase_block_wear[i]; ++ for (d = 0; d < 10; ++d) ++ if (wear <= decile_max[d]) { ++ deciles[d] += 1; ++ break; ++ } ++ } ++ avg = tot / wear_eb_count; ++ ++ /* Output wear report */ ++ seq_printf(m, "Total numbers of erases: %lu\n", tot); ++ seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count); ++ seq_printf(m, "Average number of erases: %lu\n", avg); ++ seq_printf(m, "Maximum number of erases: %lu\n", wmax); ++ seq_printf(m, "Minimum number of erases: %lu\n", wmin); ++ for (i = 0; i < 10; ++i) { ++ unsigned long from = (i ? decile_max[i - 1] + 1 : 0); ++ if (from > decile_max[i]) ++ continue; ++ seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n", ++ from, ++ decile_max[i], ++ deciles[i]); ++ } ++ ++ return 0; ++} ++ ++static int nandsim_debugfs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, nandsim_debugfs_show, inode->i_private); ++} ++ ++static const struct file_operations dfs_fops = { ++ .open = nandsim_debugfs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/** ++ * nandsim_debugfs_create - initialize debugfs ++ * @dev: nandsim device description object ++ * ++ * This function creates all debugfs files for UBI device @ubi. Returns zero in ++ * case of success and a negative error code in case of failure. ++ */ ++static int nandsim_debugfs_create(struct nandsim *dev) ++{ ++ struct dentry *root = nsmtd->dbg.dfs_dir; ++ struct dentry *dent; ++ ++ /* ++ * Just skip debugfs initialization when the debugfs directory is ++ * missing. ++ */ ++ if (IS_ERR_OR_NULL(root)) { ++ if (IS_ENABLED(CONFIG_DEBUG_FS) && ++ !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) ++ NS_WARN("CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n"); ++ return 0; ++ } ++ ++ dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, ++ root, dev, &dfs_fops); ++ if (IS_ERR_OR_NULL(dent)) { ++ NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Allocate array of page pointers, create slab allocation for an array ++ * and initialize the array by NULL pointers. ++ * ++ * RETURNS: 0 if success, -ENOMEM if memory alloc fails. ++ */ ++static int __init alloc_device(struct nandsim *ns) ++{ ++ struct file *cfile; ++ int i, err; ++ ++ if (cache_file) { ++ cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); ++ if (IS_ERR(cfile)) ++ return PTR_ERR(cfile); ++ if (!(cfile->f_mode & FMODE_CAN_READ)) { ++ NS_ERR("alloc_device: cache file not readable\n"); ++ err = -EINVAL; ++ goto err_close; ++ } ++ if (!(cfile->f_mode & FMODE_CAN_WRITE)) { ++ NS_ERR("alloc_device: cache file not writeable\n"); ++ err = -EINVAL; ++ goto err_close; ++ } ++ ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) * ++ sizeof(unsigned long)); ++ if (!ns->pages_written) { ++ NS_ERR("alloc_device: unable to allocate pages written array\n"); ++ err = -ENOMEM; ++ goto err_close; ++ } ++ ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL); ++ if (!ns->file_buf) { ++ NS_ERR("alloc_device: unable to allocate file buf\n"); ++ err = -ENOMEM; ++ goto err_free; ++ } ++ ns->cfile = cfile; ++ return 0; ++ } ++ ++ ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); ++ if (!ns->pages) { ++ NS_ERR("alloc_device: unable to allocate page array\n"); ++ return -ENOMEM; ++ } ++ for (i = 0; i < ns->geom.pgnum; i++) { ++ ns->pages[i].byte = NULL; ++ } ++ ns->nand_pages_slab = kmem_cache_create("nandsim", ++ ns->geom.pgszoob, 0, 0, NULL); ++ if (!ns->nand_pages_slab) { ++ NS_ERR("cache_create: unable to create kmem_cache\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++ ++err_free: ++ vfree(ns->pages_written); ++err_close: ++ filp_close(cfile, NULL); ++ return err; ++} ++ ++/* ++ * Free any allocated pages, and free the array of page pointers. ++ */ ++static void free_device(struct nandsim *ns) ++{ ++ int i; ++ ++ if (ns->cfile) { ++ kfree(ns->file_buf); ++ vfree(ns->pages_written); ++ filp_close(ns->cfile, NULL); ++ return; ++ } ++ ++ if (ns->pages) { ++ for (i = 0; i < ns->geom.pgnum; i++) { ++ if (ns->pages[i].byte) ++ kmem_cache_free(ns->nand_pages_slab, ++ ns->pages[i].byte); ++ } ++ kmem_cache_destroy(ns->nand_pages_slab); ++ vfree(ns->pages); ++ } ++} ++ ++static char __init *get_partition_name(int i) ++{ ++ return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); ++} ++ ++/* ++ * Initialize the nandsim structure. ++ * ++ * RETURNS: 0 if success, -ERRNO if failure. ++ */ ++static int __init init_nandsim(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ int i, ret = 0; ++ uint64_t remains; ++ uint64_t next_offset; ++ ++ if (NS_IS_INITIALIZED(ns)) { ++ NS_ERR("init_nandsim: nandsim is already initialized\n"); ++ return -EIO; ++ } ++ ++ /* Force mtd to not do delays */ ++ chip->chip_delay = 0; ++ ++ /* Initialize the NAND flash parameters */ ++ ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; ++ ns->geom.totsz = mtd->size; ++ ns->geom.pgsz = mtd->writesize; ++ ns->geom.oobsz = mtd->oobsize; ++ ns->geom.secsz = mtd->erasesize; ++ ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; ++ ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz); ++ ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ++ ns->geom.secshift = ffs(ns->geom.secsz) - 1; ++ ns->geom.pgshift = chip->page_shift; ++ ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; ++ ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; ++ ns->options = 0; ++ ++ if (ns->geom.pgsz == 512) { ++ ns->options |= OPT_PAGE512; ++ if (ns->busw == 8) ++ ns->options |= OPT_PAGE512_8BIT; ++ } else if (ns->geom.pgsz == 2048) { ++ ns->options |= OPT_PAGE2048; ++ } else if (ns->geom.pgsz == 4096) { ++ ns->options |= OPT_PAGE4096; ++ } else { ++ NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); ++ return -EIO; ++ } ++ ++ if (ns->options & OPT_SMALLPAGE) { ++ if (ns->geom.totsz <= (32 << 20)) { ++ ns->geom.pgaddrbytes = 3; ++ ns->geom.secaddrbytes = 2; ++ } else { ++ ns->geom.pgaddrbytes = 4; ++ ns->geom.secaddrbytes = 3; ++ } ++ } else { ++ if (ns->geom.totsz <= (128 << 20)) { ++ ns->geom.pgaddrbytes = 4; ++ ns->geom.secaddrbytes = 2; ++ } else { ++ ns->geom.pgaddrbytes = 5; ++ ns->geom.secaddrbytes = 3; ++ } ++ } ++ ++ /* Fill the partition_info structure */ ++ if (parts_num > ARRAY_SIZE(ns->partitions)) { ++ NS_ERR("too many partitions.\n"); ++ return -EINVAL; ++ } ++ remains = ns->geom.totsz; ++ next_offset = 0; ++ for (i = 0; i < parts_num; ++i) { ++ uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz; ++ ++ if (!part_sz || part_sz > remains) { ++ NS_ERR("bad partition size.\n"); ++ return -EINVAL; ++ } ++ ns->partitions[i].name = get_partition_name(i); ++ if (!ns->partitions[i].name) { ++ NS_ERR("unable to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ ns->partitions[i].offset = next_offset; ++ ns->partitions[i].size = part_sz; ++ next_offset += ns->partitions[i].size; ++ remains -= ns->partitions[i].size; ++ } ++ ns->nbparts = parts_num; ++ if (remains) { ++ if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { ++ NS_ERR("too many partitions.\n"); ++ return -EINVAL; ++ } ++ ns->partitions[i].name = get_partition_name(i); ++ if (!ns->partitions[i].name) { ++ NS_ERR("unable to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ ns->partitions[i].offset = next_offset; ++ ns->partitions[i].size = remains; ++ ns->nbparts += 1; ++ } ++ ++ if (ns->busw == 16) ++ NS_WARN("16-bit flashes support wasn't tested\n"); ++ ++ printk("flash size: %llu MiB\n", ++ (unsigned long long)ns->geom.totsz >> 20); ++ printk("page size: %u bytes\n", ns->geom.pgsz); ++ printk("OOB area size: %u bytes\n", ns->geom.oobsz); ++ printk("sector size: %u KiB\n", ns->geom.secsz >> 10); ++ printk("pages number: %u\n", ns->geom.pgnum); ++ printk("pages per sector: %u\n", ns->geom.pgsec); ++ printk("bus width: %u\n", ns->busw); ++ printk("bits in sector size: %u\n", ns->geom.secshift); ++ printk("bits in page size: %u\n", ns->geom.pgshift); ++ printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1); ++ printk("flash size with OOB: %llu KiB\n", ++ (unsigned long long)ns->geom.totszoob >> 10); ++ printk("page address bytes: %u\n", ns->geom.pgaddrbytes); ++ printk("sector address bytes: %u\n", ns->geom.secaddrbytes); ++ printk("options: %#x\n", ns->options); ++ ++ if ((ret = alloc_device(ns)) != 0) ++ return ret; ++ ++ /* Allocate / initialize the internal buffer */ ++ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); ++ if (!ns->buf.byte) { ++ NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", ++ ns->geom.pgszoob); ++ return -ENOMEM; ++ } ++ memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); ++ ++ return 0; ++} ++ ++/* ++ * Free the nandsim structure. ++ */ ++static void free_nandsim(struct nandsim *ns) ++{ ++ kfree(ns->buf.byte); ++ free_device(ns); ++ ++ return; ++} ++ ++static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd) ++{ ++ char *w; ++ int zero_ok; ++ unsigned int erase_block_no; ++ loff_t offset; ++ ++ if (!badblocks) ++ return 0; ++ w = badblocks; ++ do { ++ zero_ok = (*w == '0' ? 1 : 0); ++ erase_block_no = simple_strtoul(w, &w, 0); ++ if (!zero_ok && !erase_block_no) { ++ NS_ERR("invalid badblocks.\n"); ++ return -EINVAL; ++ } ++ offset = (loff_t)erase_block_no * ns->geom.secsz; ++ if (mtd_block_markbad(mtd, offset)) { ++ NS_ERR("invalid badblocks.\n"); ++ return -EINVAL; ++ } ++ if (*w == ',') ++ w += 1; ++ } while (*w); ++ return 0; ++} ++ ++static int parse_weakblocks(void) ++{ ++ char *w; ++ int zero_ok; ++ unsigned int erase_block_no; ++ unsigned int max_erases; ++ struct weak_block *wb; ++ ++ if (!weakblocks) ++ return 0; ++ w = weakblocks; ++ do { ++ zero_ok = (*w == '0' ? 1 : 0); ++ erase_block_no = simple_strtoul(w, &w, 0); ++ if (!zero_ok && !erase_block_no) { ++ NS_ERR("invalid weakblocks.\n"); ++ return -EINVAL; ++ } ++ max_erases = 3; ++ if (*w == ':') { ++ w += 1; ++ max_erases = simple_strtoul(w, &w, 0); ++ } ++ if (*w == ',') ++ w += 1; ++ wb = kzalloc(sizeof(*wb), GFP_KERNEL); ++ if (!wb) { ++ NS_ERR("unable to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ wb->erase_block_no = erase_block_no; ++ wb->max_erases = max_erases; ++ list_add(&wb->list, &weak_blocks); ++ } while (*w); ++ return 0; ++} ++ ++static int erase_error(unsigned int erase_block_no) ++{ ++ struct weak_block *wb; ++ ++ list_for_each_entry(wb, &weak_blocks, list) ++ if (wb->erase_block_no == erase_block_no) { ++ if (wb->erases_done >= wb->max_erases) ++ return 1; ++ wb->erases_done += 1; ++ return 0; ++ } ++ return 0; ++} ++ ++static int parse_weakpages(void) ++{ ++ char *w; ++ int zero_ok; ++ unsigned int page_no; ++ unsigned int max_writes; ++ struct weak_page *wp; ++ ++ if (!weakpages) ++ return 0; ++ w = weakpages; ++ do { ++ zero_ok = (*w == '0' ? 1 : 0); ++ page_no = simple_strtoul(w, &w, 0); ++ if (!zero_ok && !page_no) { ++ NS_ERR("invalid weakpages.\n"); ++ return -EINVAL; ++ } ++ max_writes = 3; ++ if (*w == ':') { ++ w += 1; ++ max_writes = simple_strtoul(w, &w, 0); ++ } ++ if (*w == ',') ++ w += 1; ++ wp = kzalloc(sizeof(*wp), GFP_KERNEL); ++ if (!wp) { ++ NS_ERR("unable to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ wp->page_no = page_no; ++ wp->max_writes = max_writes; ++ list_add(&wp->list, &weak_pages); ++ } while (*w); ++ return 0; ++} ++ ++static int write_error(unsigned int page_no) ++{ ++ struct weak_page *wp; ++ ++ list_for_each_entry(wp, &weak_pages, list) ++ if (wp->page_no == page_no) { ++ if (wp->writes_done >= wp->max_writes) ++ return 1; ++ wp->writes_done += 1; ++ return 0; ++ } ++ return 0; ++} ++ ++static int parse_gravepages(void) ++{ ++ char *g; ++ int zero_ok; ++ unsigned int page_no; ++ unsigned int max_reads; ++ struct grave_page *gp; ++ ++ if (!gravepages) ++ return 0; ++ g = gravepages; ++ do { ++ zero_ok = (*g == '0' ? 1 : 0); ++ page_no = simple_strtoul(g, &g, 0); ++ if (!zero_ok && !page_no) { ++ NS_ERR("invalid gravepagess.\n"); ++ return -EINVAL; ++ } ++ max_reads = 3; ++ if (*g == ':') { ++ g += 1; ++ max_reads = simple_strtoul(g, &g, 0); ++ } ++ if (*g == ',') ++ g += 1; ++ gp = kzalloc(sizeof(*gp), GFP_KERNEL); ++ if (!gp) { ++ NS_ERR("unable to allocate memory.\n"); ++ return -ENOMEM; ++ } ++ gp->page_no = page_no; ++ gp->max_reads = max_reads; ++ list_add(&gp->list, &grave_pages); ++ } while (*g); ++ return 0; ++} ++ ++static int read_error(unsigned int page_no) ++{ ++ struct grave_page *gp; ++ ++ list_for_each_entry(gp, &grave_pages, list) ++ if (gp->page_no == page_no) { ++ if (gp->reads_done >= gp->max_reads) ++ return 1; ++ gp->reads_done += 1; ++ return 0; ++ } ++ return 0; ++} ++ ++static void free_lists(void) ++{ ++ struct list_head *pos, *n; ++ list_for_each_safe(pos, n, &weak_blocks) { ++ list_del(pos); ++ kfree(list_entry(pos, struct weak_block, list)); ++ } ++ list_for_each_safe(pos, n, &weak_pages) { ++ list_del(pos); ++ kfree(list_entry(pos, struct weak_page, list)); ++ } ++ list_for_each_safe(pos, n, &grave_pages) { ++ list_del(pos); ++ kfree(list_entry(pos, struct grave_page, list)); ++ } ++ kfree(erase_block_wear); ++} ++ ++static int setup_wear_reporting(struct mtd_info *mtd) ++{ ++ size_t mem; ++ ++ wear_eb_count = div_u64(mtd->size, mtd->erasesize); ++ mem = wear_eb_count * sizeof(unsigned long); ++ if (mem / sizeof(unsigned long) != wear_eb_count) { ++ NS_ERR("Too many erase blocks for wear reporting\n"); ++ return -ENOMEM; ++ } ++ erase_block_wear = kzalloc(mem, GFP_KERNEL); ++ if (!erase_block_wear) { ++ NS_ERR("Too many erase blocks for wear reporting\n"); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static void update_wear(unsigned int erase_block_no) ++{ ++ if (!erase_block_wear) ++ return; ++ total_wear += 1; ++ /* ++ * TODO: Notify this through a debugfs entry, ++ * instead of showing an error message. ++ */ ++ if (total_wear == 0) ++ NS_ERR("Erase counter total overflow\n"); ++ erase_block_wear[erase_block_no] += 1; ++ if (erase_block_wear[erase_block_no] == 0) ++ NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); ++} ++ ++/* ++ * Returns the string representation of 'state' state. ++ */ ++static char *get_state_name(uint32_t state) ++{ ++ switch (NS_STATE(state)) { ++ case STATE_CMD_READ0: ++ return "STATE_CMD_READ0"; ++ case STATE_CMD_READ1: ++ return "STATE_CMD_READ1"; ++ case STATE_CMD_PAGEPROG: ++ return "STATE_CMD_PAGEPROG"; ++ case STATE_CMD_READOOB: ++ return "STATE_CMD_READOOB"; ++ case STATE_CMD_READSTART: ++ return "STATE_CMD_READSTART"; ++ case STATE_CMD_ERASE1: ++ return "STATE_CMD_ERASE1"; ++ case STATE_CMD_STATUS: ++ return "STATE_CMD_STATUS"; ++ case STATE_CMD_SEQIN: ++ return "STATE_CMD_SEQIN"; ++ case STATE_CMD_READID: ++ return "STATE_CMD_READID"; ++ case STATE_CMD_ERASE2: ++ return "STATE_CMD_ERASE2"; ++ case STATE_CMD_RESET: ++ return "STATE_CMD_RESET"; ++ case STATE_CMD_RNDOUT: ++ return "STATE_CMD_RNDOUT"; ++ case STATE_CMD_RNDOUTSTART: ++ return "STATE_CMD_RNDOUTSTART"; ++ case STATE_ADDR_PAGE: ++ return "STATE_ADDR_PAGE"; ++ case STATE_ADDR_SEC: ++ return "STATE_ADDR_SEC"; ++ case STATE_ADDR_ZERO: ++ return "STATE_ADDR_ZERO"; ++ case STATE_ADDR_COLUMN: ++ return "STATE_ADDR_COLUMN"; ++ case STATE_DATAIN: ++ return "STATE_DATAIN"; ++ case STATE_DATAOUT: ++ return "STATE_DATAOUT"; ++ case STATE_DATAOUT_ID: ++ return "STATE_DATAOUT_ID"; ++ case STATE_DATAOUT_STATUS: ++ return "STATE_DATAOUT_STATUS"; ++ case STATE_READY: ++ return "STATE_READY"; ++ case STATE_UNKNOWN: ++ return "STATE_UNKNOWN"; ++ } ++ ++ NS_ERR("get_state_name: unknown state, BUG\n"); ++ return NULL; ++} ++ ++/* ++ * Check if command is valid. ++ * ++ * RETURNS: 1 if wrong command, 0 if right. ++ */ ++static int check_command(int cmd) ++{ ++ switch (cmd) { ++ ++ case NAND_CMD_READ0: ++ case NAND_CMD_READ1: ++ case NAND_CMD_READSTART: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_READOOB: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_READID: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_RESET: ++ case NAND_CMD_RNDOUT: ++ case NAND_CMD_RNDOUTSTART: ++ return 0; ++ ++ default: ++ return 1; ++ } ++} ++ ++/* ++ * Returns state after command is accepted by command number. ++ */ ++static uint32_t get_state_by_command(unsigned command) ++{ ++ switch (command) { ++ case NAND_CMD_READ0: ++ return STATE_CMD_READ0; ++ case NAND_CMD_READ1: ++ return STATE_CMD_READ1; ++ case NAND_CMD_PAGEPROG: ++ return STATE_CMD_PAGEPROG; ++ case NAND_CMD_READSTART: ++ return STATE_CMD_READSTART; ++ case NAND_CMD_READOOB: ++ return STATE_CMD_READOOB; ++ case NAND_CMD_ERASE1: ++ return STATE_CMD_ERASE1; ++ case NAND_CMD_STATUS: ++ return STATE_CMD_STATUS; ++ case NAND_CMD_SEQIN: ++ return STATE_CMD_SEQIN; ++ case NAND_CMD_READID: ++ return STATE_CMD_READID; ++ case NAND_CMD_ERASE2: ++ return STATE_CMD_ERASE2; ++ case NAND_CMD_RESET: ++ return STATE_CMD_RESET; ++ case NAND_CMD_RNDOUT: ++ return STATE_CMD_RNDOUT; ++ case NAND_CMD_RNDOUTSTART: ++ return STATE_CMD_RNDOUTSTART; ++ } ++ ++ NS_ERR("get_state_by_command: unknown command, BUG\n"); ++ return 0; ++} ++ ++/* ++ * Move an address byte to the correspondent internal register. ++ */ ++static inline void accept_addr_byte(struct nandsim *ns, u_char bt) ++{ ++ uint byte = (uint)bt; ++ ++ if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) ++ ns->regs.column |= (byte << 8 * ns->regs.count); ++ else { ++ ns->regs.row |= (byte << 8 * (ns->regs.count - ++ ns->geom.pgaddrbytes + ++ ns->geom.secaddrbytes)); ++ } ++ ++ return; ++} ++ ++/* ++ * Switch to STATE_READY state. ++ */ ++static inline void switch_to_ready_state(struct nandsim *ns, u_char status) ++{ ++ NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); ++ ++ ns->state = STATE_READY; ++ ns->nxstate = STATE_UNKNOWN; ++ ns->op = NULL; ++ ns->npstates = 0; ++ ns->stateidx = 0; ++ ns->regs.num = 0; ++ ns->regs.count = 0; ++ ns->regs.off = 0; ++ ns->regs.row = 0; ++ ns->regs.column = 0; ++ ns->regs.status = status; ++} ++ ++/* ++ * If the operation isn't known yet, try to find it in the global array ++ * of supported operations. ++ * ++ * Operation can be unknown because of the following. ++ * 1. New command was accepted and this is the first call to find the ++ * correspondent states chain. In this case ns->npstates = 0; ++ * 2. There are several operations which begin with the same command(s) ++ * (for example program from the second half and read from the ++ * second half operations both begin with the READ1 command). In this ++ * case the ns->pstates[] array contains previous states. ++ * ++ * Thus, the function tries to find operation containing the following ++ * states (if the 'flag' parameter is 0): ++ * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state ++ * ++ * If (one and only one) matching operation is found, it is accepted ( ++ * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is ++ * zeroed). ++ * ++ * If there are several matches, the current state is pushed to the ++ * ns->pstates. ++ * ++ * The operation can be unknown only while commands are input to the chip. ++ * As soon as address command is accepted, the operation must be known. ++ * In such situation the function is called with 'flag' != 0, and the ++ * operation is searched using the following pattern: ++ * ns->pstates[0], ... ns->pstates[ns->npstates],
++ * ++ * It is supposed that this pattern must either match one operation or ++ * none. There can't be ambiguity in that case. ++ * ++ * If no matches found, the function does the following: ++ * 1. if there are saved states present, try to ignore them and search ++ * again only using the last command. If nothing was found, switch ++ * to the STATE_READY state. ++ * 2. if there are no saved states, switch to the STATE_READY state. ++ * ++ * RETURNS: -2 - no matched operations found. ++ * -1 - several matches. ++ * 0 - operation is found. ++ */ ++static int find_operation(struct nandsim *ns, uint32_t flag) ++{ ++ int opsfound = 0; ++ int i, j, idx = 0; ++ ++ for (i = 0; i < NS_OPER_NUM; i++) { ++ ++ int found = 1; ++ ++ if (!(ns->options & ops[i].reqopts)) ++ /* Ignore operations we can't perform */ ++ continue; ++ ++ if (flag) { ++ if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) ++ continue; ++ } else { ++ if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) ++ continue; ++ } ++ ++ for (j = 0; j < ns->npstates; j++) ++ if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) ++ && (ns->options & ops[idx].reqopts)) { ++ found = 0; ++ break; ++ } ++ ++ if (found) { ++ idx = i; ++ opsfound += 1; ++ } ++ } ++ ++ if (opsfound == 1) { ++ /* Exact match */ ++ ns->op = &ops[idx].states[0]; ++ if (flag) { ++ /* ++ * In this case the find_operation function was ++ * called when address has just began input. But it isn't ++ * yet fully input and the current state must ++ * not be one of STATE_ADDR_*, but the STATE_ADDR_* ++ * state must be the next state (ns->nxstate). ++ */ ++ ns->stateidx = ns->npstates - 1; ++ } else { ++ ns->stateidx = ns->npstates; ++ } ++ ns->npstates = 0; ++ ns->state = ns->op[ns->stateidx]; ++ ns->nxstate = ns->op[ns->stateidx + 1]; ++ NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", ++ idx, get_state_name(ns->state), get_state_name(ns->nxstate)); ++ return 0; ++ } ++ ++ if (opsfound == 0) { ++ /* Nothing was found. Try to ignore previous commands (if any) and search again */ ++ if (ns->npstates != 0) { ++ NS_DBG("find_operation: no operation found, try again with state %s\n", ++ get_state_name(ns->state)); ++ ns->npstates = 0; ++ return find_operation(ns, 0); ++ ++ } ++ NS_DBG("find_operation: no operations found\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return -2; ++ } ++ ++ if (flag) { ++ /* This shouldn't happen */ ++ NS_DBG("find_operation: BUG, operation must be known if address is input\n"); ++ return -2; ++ } ++ ++ NS_DBG("find_operation: there is still ambiguity\n"); ++ ++ ns->pstates[ns->npstates++] = ns->state; ++ ++ return -1; ++} ++ ++static void put_pages(struct nandsim *ns) ++{ ++ int i; ++ ++ for (i = 0; i < ns->held_cnt; i++) ++ put_page(ns->held_pages[i]); ++} ++ ++/* Get page cache pages in advance to provide NOFS memory allocation */ ++static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos) ++{ ++ pgoff_t index, start_index, end_index; ++ struct page *page; ++ struct address_space *mapping = file->f_mapping; ++ ++ start_index = pos >> PAGE_SHIFT; ++ end_index = (pos + count - 1) >> PAGE_SHIFT; ++ if (end_index - start_index + 1 > NS_MAX_HELD_PAGES) ++ return -EINVAL; ++ ns->held_cnt = 0; ++ for (index = start_index; index <= end_index; index++) { ++ page = find_get_page(mapping, index); ++ if (page == NULL) { ++ page = find_or_create_page(mapping, index, GFP_NOFS); ++ if (page == NULL) { ++ write_inode_now(mapping->host, 1); ++ page = find_or_create_page(mapping, index, GFP_NOFS); ++ } ++ if (page == NULL) { ++ put_pages(ns); ++ return -ENOMEM; ++ } ++ unlock_page(page); ++ } ++ ns->held_pages[ns->held_cnt++] = page; ++ } ++ return 0; ++} ++ ++static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) ++{ ++ ssize_t tx; ++ int err; ++ unsigned int noreclaim_flag; ++ ++ err = get_pages(ns, file, count, pos); ++ if (err) ++ return err; ++ noreclaim_flag = memalloc_noreclaim_save(); ++ tx = kernel_read(file, buf, count, &pos); ++ memalloc_noreclaim_restore(noreclaim_flag); ++ put_pages(ns); ++ return tx; ++} ++ ++static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos) ++{ ++ ssize_t tx; ++ int err; ++ unsigned int noreclaim_flag; ++ ++ err = get_pages(ns, file, count, pos); ++ if (err) ++ return err; ++ noreclaim_flag = memalloc_noreclaim_save(); ++ tx = kernel_write(file, buf, count, &pos); ++ memalloc_noreclaim_restore(noreclaim_flag); ++ put_pages(ns); ++ return tx; ++} ++ ++/* ++ * Returns a pointer to the current page. ++ */ ++static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) ++{ ++ return &(ns->pages[ns->regs.row]); ++} ++ ++/* ++ * Retuns a pointer to the current byte, within the current page. ++ */ ++static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) ++{ ++ return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; ++} ++ ++static int do_read_error(struct nandsim *ns, int num) ++{ ++ unsigned int page_no = ns->regs.row; ++ ++ if (read_error(page_no)) { ++ prandom_bytes(ns->buf.byte, num); ++ NS_WARN("simulating read error in page %u\n", page_no); ++ return 1; ++ } ++ return 0; ++} ++ ++static void do_bit_flips(struct nandsim *ns, int num) ++{ ++ if (bitflips && prandom_u32() < (1 << 22)) { ++ int flips = 1; ++ if (bitflips > 1) ++ flips = (prandom_u32() % (int) bitflips) + 1; ++ while (flips--) { ++ int pos = prandom_u32() % (num * 8); ++ ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); ++ NS_WARN("read_page: flipping bit %d in page %d " ++ "reading from %d ecc: corrected=%u failed=%u\n", ++ pos, ns->regs.row, ns->regs.column + ns->regs.off, ++ nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); ++ } ++ } ++} ++ ++/* ++ * Fill the NAND buffer with data read from the specified page. ++ */ ++static void read_page(struct nandsim *ns, int num) ++{ ++ union ns_mem *mypage; ++ ++ if (ns->cfile) { ++ if (!test_bit(ns->regs.row, ns->pages_written)) { ++ NS_DBG("read_page: page %d not written\n", ns->regs.row); ++ memset(ns->buf.byte, 0xFF, num); ++ } else { ++ loff_t pos; ++ ssize_t tx; ++ ++ NS_DBG("read_page: page %d written, reading from %d\n", ++ ns->regs.row, ns->regs.column + ns->regs.off); ++ if (do_read_error(ns, num)) ++ return; ++ pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; ++ tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos); ++ if (tx != num) { ++ NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); ++ return; ++ } ++ do_bit_flips(ns, num); ++ } ++ return; ++ } ++ ++ mypage = NS_GET_PAGE(ns); ++ if (mypage->byte == NULL) { ++ NS_DBG("read_page: page %d not allocated\n", ns->regs.row); ++ memset(ns->buf.byte, 0xFF, num); ++ } else { ++ NS_DBG("read_page: page %d allocated, reading from %d\n", ++ ns->regs.row, ns->regs.column + ns->regs.off); ++ if (do_read_error(ns, num)) ++ return; ++ memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); ++ do_bit_flips(ns, num); ++ } ++} ++ ++/* ++ * Erase all pages in the specified sector. ++ */ ++static void erase_sector(struct nandsim *ns) ++{ ++ union ns_mem *mypage; ++ int i; ++ ++ if (ns->cfile) { ++ for (i = 0; i < ns->geom.pgsec; i++) ++ if (__test_and_clear_bit(ns->regs.row + i, ++ ns->pages_written)) { ++ NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); ++ } ++ return; ++ } ++ ++ mypage = NS_GET_PAGE(ns); ++ for (i = 0; i < ns->geom.pgsec; i++) { ++ if (mypage->byte != NULL) { ++ NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); ++ kmem_cache_free(ns->nand_pages_slab, mypage->byte); ++ mypage->byte = NULL; ++ } ++ mypage++; ++ } ++} ++ ++/* ++ * Program the specified page with the contents from the NAND buffer. ++ */ ++static int prog_page(struct nandsim *ns, int num) ++{ ++ int i; ++ union ns_mem *mypage; ++ u_char *pg_off; ++ ++ if (ns->cfile) { ++ loff_t off; ++ ssize_t tx; ++ int all; ++ ++ NS_DBG("prog_page: writing page %d\n", ns->regs.row); ++ pg_off = ns->file_buf + ns->regs.column + ns->regs.off; ++ off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; ++ if (!test_bit(ns->regs.row, ns->pages_written)) { ++ all = 1; ++ memset(ns->file_buf, 0xff, ns->geom.pgszoob); ++ } else { ++ all = 0; ++ tx = read_file(ns, ns->cfile, pg_off, num, off); ++ if (tx != num) { ++ NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); ++ return -1; ++ } ++ } ++ for (i = 0; i < num; i++) ++ pg_off[i] &= ns->buf.byte[i]; ++ if (all) { ++ loff_t pos = (loff_t)ns->regs.row * ns->geom.pgszoob; ++ tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos); ++ if (tx != ns->geom.pgszoob) { ++ NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); ++ return -1; ++ } ++ __set_bit(ns->regs.row, ns->pages_written); ++ } else { ++ tx = write_file(ns, ns->cfile, pg_off, num, off); ++ if (tx != num) { ++ NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); ++ return -1; ++ } ++ } ++ return 0; ++ } ++ ++ mypage = NS_GET_PAGE(ns); ++ if (mypage->byte == NULL) { ++ NS_DBG("prog_page: allocating page %d\n", ns->regs.row); ++ /* ++ * We allocate memory with GFP_NOFS because a flash FS may ++ * utilize this. If it is holding an FS lock, then gets here, ++ * then kernel memory alloc runs writeback which goes to the FS ++ * again and deadlocks. This was seen in practice. ++ */ ++ mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS); ++ if (mypage->byte == NULL) { ++ NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); ++ return -1; ++ } ++ memset(mypage->byte, 0xFF, ns->geom.pgszoob); ++ } ++ ++ pg_off = NS_PAGE_BYTE_OFF(ns); ++ for (i = 0; i < num; i++) ++ pg_off[i] &= ns->buf.byte[i]; ++ ++ return 0; ++} ++ ++/* ++ * If state has any action bit, perform this action. ++ * ++ * RETURNS: 0 if success, -1 if error. ++ */ ++static int do_state_action(struct nandsim *ns, uint32_t action) ++{ ++ int num; ++ int busdiv = ns->busw == 8 ? 1 : 2; ++ unsigned int erase_block_no, page_no; ++ ++ action &= ACTION_MASK; ++ ++ /* Check that page address input is correct */ ++ if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { ++ NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); ++ return -1; ++ } ++ ++ switch (action) { ++ ++ case ACTION_CPY: ++ /* ++ * Copy page data to the internal buffer. ++ */ ++ ++ /* Column shouldn't be very large */ ++ if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { ++ NS_ERR("do_state_action: column number is too large\n"); ++ break; ++ } ++ num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; ++ read_page(ns, num); ++ ++ NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", ++ num, NS_RAW_OFFSET(ns) + ns->regs.off); ++ ++ if (ns->regs.off == 0) ++ NS_LOG("read page %d\n", ns->regs.row); ++ else if (ns->regs.off < ns->geom.pgsz) ++ NS_LOG("read page %d (second half)\n", ns->regs.row); ++ else ++ NS_LOG("read OOB of page %d\n", ns->regs.row); ++ ++ NS_UDELAY(access_delay); ++ NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); ++ ++ break; ++ ++ case ACTION_SECERASE: ++ /* ++ * Erase sector. ++ */ ++ ++ if (ns->lines.wp) { ++ NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); ++ return -1; ++ } ++ ++ if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec ++ || (ns->regs.row & ~(ns->geom.secsz - 1))) { ++ NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); ++ return -1; ++ } ++ ++ ns->regs.row = (ns->regs.row << ++ 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; ++ ns->regs.column = 0; ++ ++ erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift); ++ ++ NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", ++ ns->regs.row, NS_RAW_OFFSET(ns)); ++ NS_LOG("erase sector %u\n", erase_block_no); ++ ++ erase_sector(ns); ++ ++ NS_MDELAY(erase_delay); ++ ++ if (erase_block_wear) ++ update_wear(erase_block_no); ++ ++ if (erase_error(erase_block_no)) { ++ NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); ++ return -1; ++ } ++ ++ break; ++ ++ case ACTION_PRGPAGE: ++ /* ++ * Program page - move internal buffer data to the page. ++ */ ++ ++ if (ns->lines.wp) { ++ NS_WARN("do_state_action: device is write-protected, programm\n"); ++ return -1; ++ } ++ ++ num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; ++ if (num != ns->regs.count) { ++ NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", ++ ns->regs.count, num); ++ return -1; ++ } ++ ++ if (prog_page(ns, num) == -1) ++ return -1; ++ ++ page_no = ns->regs.row; ++ ++ NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", ++ num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); ++ NS_LOG("programm page %d\n", ns->regs.row); ++ ++ NS_UDELAY(programm_delay); ++ NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); ++ ++ if (write_error(page_no)) { ++ NS_WARN("simulating write failure in page %u\n", page_no); ++ return -1; ++ } ++ ++ break; ++ ++ case ACTION_ZEROOFF: ++ NS_DBG("do_state_action: set internal offset to 0\n"); ++ ns->regs.off = 0; ++ break; ++ ++ case ACTION_HALFOFF: ++ if (!(ns->options & OPT_PAGE512_8BIT)) { ++ NS_ERR("do_state_action: BUG! can't skip half of page for non-512" ++ "byte page size 8x chips\n"); ++ return -1; ++ } ++ NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); ++ ns->regs.off = ns->geom.pgsz/2; ++ break; ++ ++ case ACTION_OOBOFF: ++ NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); ++ ns->regs.off = ns->geom.pgsz; ++ break; ++ ++ default: ++ NS_DBG("do_state_action: BUG! unknown action\n"); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Switch simulator's state. ++ */ ++static void switch_state(struct nandsim *ns) ++{ ++ if (ns->op) { ++ /* ++ * The current operation have already been identified. ++ * Just follow the states chain. ++ */ ++ ++ ns->stateidx += 1; ++ ns->state = ns->nxstate; ++ ns->nxstate = ns->op[ns->stateidx + 1]; ++ ++ NS_DBG("switch_state: operation is known, switch to the next state, " ++ "state: %s, nxstate: %s\n", ++ get_state_name(ns->state), get_state_name(ns->nxstate)); ++ ++ /* See, whether we need to do some action */ ++ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ } else { ++ /* ++ * We don't yet know which operation we perform. ++ * Try to identify it. ++ */ ++ ++ /* ++ * The only event causing the switch_state function to ++ * be called with yet unknown operation is new command. ++ */ ++ ns->state = get_state_by_command(ns->regs.command); ++ ++ NS_DBG("switch_state: operation is unknown, try to find it\n"); ++ ++ if (find_operation(ns, 0) != 0) ++ return; ++ ++ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ } ++ ++ /* For 16x devices column means the page offset in words */ ++ if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { ++ NS_DBG("switch_state: double the column number for 16x device\n"); ++ ns->regs.column <<= 1; ++ } ++ ++ if (NS_STATE(ns->nxstate) == STATE_READY) { ++ /* ++ * The current state is the last. Return to STATE_READY ++ */ ++ ++ u_char status = NS_STATUS_OK(ns); ++ ++ /* In case of data states, see if all bytes were input/output */ ++ if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) ++ && ns->regs.count != ns->regs.num) { ++ NS_WARN("switch_state: not all bytes were processed, %d left\n", ++ ns->regs.num - ns->regs.count); ++ status = NS_STATUS_FAILED(ns); ++ } ++ ++ NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); ++ ++ switch_to_ready_state(ns, status); ++ ++ return; ++ } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { ++ /* ++ * If the next state is data input/output, switch to it now ++ */ ++ ++ ns->state = ns->nxstate; ++ ns->nxstate = ns->op[++ns->stateidx + 1]; ++ ns->regs.num = ns->regs.count = 0; ++ ++ NS_DBG("switch_state: the next state is data I/O, switch, " ++ "state: %s, nxstate: %s\n", ++ get_state_name(ns->state), get_state_name(ns->nxstate)); ++ ++ /* ++ * Set the internal register to the count of bytes which ++ * are expected to be input or output ++ */ ++ switch (NS_STATE(ns->state)) { ++ case STATE_DATAIN: ++ case STATE_DATAOUT: ++ ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; ++ break; ++ ++ case STATE_DATAOUT_ID: ++ ns->regs.num = ns->geom.idbytes; ++ break; ++ ++ case STATE_DATAOUT_STATUS: ++ ns->regs.count = ns->regs.num = 0; ++ break; ++ ++ default: ++ NS_ERR("switch_state: BUG! unknown data state\n"); ++ } ++ ++ } else if (ns->nxstate & STATE_ADDR_MASK) { ++ /* ++ * If the next state is address input, set the internal ++ * register to the number of expected address bytes ++ */ ++ ++ ns->regs.count = 0; ++ ++ switch (NS_STATE(ns->nxstate)) { ++ case STATE_ADDR_PAGE: ++ ns->regs.num = ns->geom.pgaddrbytes; ++ ++ break; ++ case STATE_ADDR_SEC: ++ ns->regs.num = ns->geom.secaddrbytes; ++ break; ++ ++ case STATE_ADDR_ZERO: ++ ns->regs.num = 1; ++ break; ++ ++ case STATE_ADDR_COLUMN: ++ /* Column address is always 2 bytes */ ++ ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes; ++ break; ++ ++ default: ++ NS_ERR("switch_state: BUG! unknown address state\n"); ++ } ++ } else { ++ /* ++ * Just reset internal counters. ++ */ ++ ++ ns->regs.num = 0; ++ ns->regs.count = 0; ++ } ++} ++ ++static u_char ns_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ u_char outb = 0x00; ++ ++ /* Sanity and correctness checks */ ++ if (!ns->lines.ce) { ++ NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); ++ return outb; ++ } ++ if (ns->lines.ale || ns->lines.cle) { ++ NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); ++ return outb; ++ } ++ if (!(ns->state & STATE_DATAOUT_MASK)) { ++ NS_WARN("read_byte: unexpected data output cycle, state is %s " ++ "return %#x\n", get_state_name(ns->state), (uint)outb); ++ return outb; ++ } ++ ++ /* Status register may be read as many times as it is wanted */ ++ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { ++ NS_DBG("read_byte: return %#x status\n", ns->regs.status); ++ return ns->regs.status; ++ } ++ ++ /* Check if there is any data in the internal buffer which may be read */ ++ if (ns->regs.count == ns->regs.num) { ++ NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); ++ return outb; ++ } ++ ++ switch (NS_STATE(ns->state)) { ++ case STATE_DATAOUT: ++ if (ns->busw == 8) { ++ outb = ns->buf.byte[ns->regs.count]; ++ ns->regs.count += 1; ++ } else { ++ outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); ++ ns->regs.count += 2; ++ } ++ break; ++ case STATE_DATAOUT_ID: ++ NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); ++ outb = ns->ids[ns->regs.count]; ++ ns->regs.count += 1; ++ break; ++ default: ++ BUG(); ++ } ++ ++ if (ns->regs.count == ns->regs.num) { ++ NS_DBG("read_byte: all bytes were read\n"); ++ ++ if (NS_STATE(ns->nxstate) == STATE_READY) ++ switch_state(ns); ++ } ++ ++ return outb; ++} ++ ++static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ ++ /* Sanity and correctness checks */ ++ if (!ns->lines.ce) { ++ NS_ERR("write_byte: chip is disabled, ignore write\n"); ++ return; ++ } ++ if (ns->lines.ale && ns->lines.cle) { ++ NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); ++ return; ++ } ++ ++ if (ns->lines.cle == 1) { ++ /* ++ * The byte written is a command. ++ */ ++ ++ if (byte == NAND_CMD_RESET) { ++ NS_LOG("reset chip\n"); ++ switch_to_ready_state(ns, NS_STATUS_OK(ns)); ++ return; ++ } ++ ++ /* Check that the command byte is correct */ ++ if (check_command(byte)) { ++ NS_ERR("write_byte: unknown command %#x\n", (uint)byte); ++ return; ++ } ++ ++ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS ++ || NS_STATE(ns->state) == STATE_DATAOUT) { ++ int row = ns->regs.row; ++ ++ switch_state(ns); ++ if (byte == NAND_CMD_RNDOUT) ++ ns->regs.row = row; ++ } ++ ++ /* Check if chip is expecting command */ ++ if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { ++ /* Do not warn if only 2 id bytes are read */ ++ if (!(ns->regs.command == NAND_CMD_READID && ++ NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) { ++ /* ++ * We are in situation when something else (not command) ++ * was expected but command was input. In this case ignore ++ * previous command(s)/state(s) and accept the last one. ++ */ ++ NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " ++ "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); ++ } ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ } ++ ++ NS_DBG("command byte corresponding to %s state accepted\n", ++ get_state_name(get_state_by_command(byte))); ++ ns->regs.command = byte; ++ switch_state(ns); ++ ++ } else if (ns->lines.ale == 1) { ++ /* ++ * The byte written is an address. ++ */ ++ ++ if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { ++ ++ NS_DBG("write_byte: operation isn't known yet, identify it\n"); ++ ++ if (find_operation(ns, 1) < 0) ++ return; ++ ++ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ ns->regs.count = 0; ++ switch (NS_STATE(ns->nxstate)) { ++ case STATE_ADDR_PAGE: ++ ns->regs.num = ns->geom.pgaddrbytes; ++ break; ++ case STATE_ADDR_SEC: ++ ns->regs.num = ns->geom.secaddrbytes; ++ break; ++ case STATE_ADDR_ZERO: ++ ns->regs.num = 1; ++ break; ++ default: ++ BUG(); ++ } ++ } ++ ++ /* Check that chip is expecting address */ ++ if (!(ns->nxstate & STATE_ADDR_MASK)) { ++ NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " ++ "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ /* Check if this is expected byte */ ++ if (ns->regs.count == ns->regs.num) { ++ NS_ERR("write_byte: no more address bytes expected\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ accept_addr_byte(ns, byte); ++ ++ ns->regs.count += 1; ++ ++ NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", ++ (uint)byte, ns->regs.count, ns->regs.num); ++ ++ if (ns->regs.count == ns->regs.num) { ++ NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); ++ switch_state(ns); ++ } ++ ++ } else { ++ /* ++ * The byte written is an input data. ++ */ ++ ++ /* Check that chip is expecting data input */ ++ if (!(ns->state & STATE_DATAIN_MASK)) { ++ NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " ++ "switch to %s\n", (uint)byte, ++ get_state_name(ns->state), get_state_name(STATE_READY)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ /* Check if this is expected byte */ ++ if (ns->regs.count == ns->regs.num) { ++ NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", ++ ns->regs.num); ++ return; ++ } ++ ++ if (ns->busw == 8) { ++ ns->buf.byte[ns->regs.count] = byte; ++ ns->regs.count += 1; ++ } else { ++ ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); ++ ns->regs.count += 2; ++ } ++ } ++ ++ return; ++} ++ ++static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ ++ ns->lines.cle = bitmask & NAND_CLE ? 1 : 0; ++ ns->lines.ale = bitmask & NAND_ALE ? 1 : 0; ++ ns->lines.ce = bitmask & NAND_NCE ? 1 : 0; ++ ++ if (cmd != NAND_CMD_NONE) ++ ns_nand_write_byte(mtd, cmd); ++} ++ ++static int ns_device_ready(struct mtd_info *mtd) ++{ ++ NS_DBG("device_ready\n"); ++ return 1; ++} ++ ++static uint16_t ns_nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ NS_DBG("read_word\n"); ++ ++ return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); ++} ++ ++static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ ++ /* Check that chip is expecting data input */ ++ if (!(ns->state & STATE_DATAIN_MASK)) { ++ NS_ERR("write_buf: data input isn't expected, state is %s, " ++ "switch to STATE_READY\n", get_state_name(ns->state)); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ /* Check if these are expected bytes */ ++ if (ns->regs.count + len > ns->regs.num) { ++ NS_ERR("write_buf: too many input bytes\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ memcpy(ns->buf.byte + ns->regs.count, buf, len); ++ ns->regs.count += len; ++ ++ if (ns->regs.count == ns->regs.num) { ++ NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); ++ } ++} ++ ++static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ ++ /* Sanity and correctness checks */ ++ if (!ns->lines.ce) { ++ NS_ERR("read_buf: chip is disabled\n"); ++ return; ++ } ++ if (ns->lines.ale || ns->lines.cle) { ++ NS_ERR("read_buf: ALE or CLE pin is high\n"); ++ return; ++ } ++ if (!(ns->state & STATE_DATAOUT_MASK)) { ++ NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", ++ get_state_name(ns->state)); ++ return; ++ } ++ ++ if (NS_STATE(ns->state) != STATE_DATAOUT) { ++ int i; ++ ++ for (i = 0; i < len; i++) ++ buf[i] = mtd_to_nand(mtd)->read_byte(mtd); ++ ++ return; ++ } ++ ++ /* Check if these are expected bytes */ ++ if (ns->regs.count + len > ns->regs.num) { ++ NS_ERR("read_buf: too many bytes to read\n"); ++ switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); ++ return; ++ } ++ ++ memcpy(buf, ns->buf.byte + ns->regs.count, len); ++ ns->regs.count += len; ++ ++ if (ns->regs.count == ns->regs.num) { ++ if (NS_STATE(ns->nxstate) == STATE_READY) ++ switch_state(ns); ++ } ++ ++ return; ++} ++ ++/* ++ * Module initialization function ++ */ ++static int __init ns_init_module(void) ++{ ++ struct nand_chip *chip; ++ struct nandsim *nand; ++ int retval = -ENOMEM, i; ++ ++ if (bus_width != 8 && bus_width != 16) { ++ NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); ++ return -EINVAL; ++ } ++ ++ /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ ++ chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim), ++ GFP_KERNEL); ++ if (!chip) { ++ NS_ERR("unable to allocate core structures.\n"); ++ return -ENOMEM; ++ } ++ nsmtd = nand_to_mtd(chip); ++ nand = (struct nandsim *)(chip + 1); ++ nand_set_controller_data(chip, (void *)nand); ++ ++ /* ++ * Register simulator's callbacks. ++ */ ++ chip->cmd_ctrl = ns_hwcontrol; ++ chip->read_byte = ns_nand_read_byte; ++ chip->dev_ready = ns_device_ready; ++ chip->write_buf = ns_nand_write_buf; ++ chip->read_buf = ns_nand_read_buf; ++ chip->read_word = ns_nand_read_word; ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ ++ /* and 'badblocks' parameters to work */ ++ chip->options |= NAND_SKIP_BBTSCAN; ++ ++ switch (bbt) { ++ case 2: ++ chip->bbt_options |= NAND_BBT_NO_OOB; ++ case 1: ++ chip->bbt_options |= NAND_BBT_USE_FLASH; ++ case 0: ++ break; ++ default: ++ NS_ERR("bbt has to be 0..2\n"); ++ retval = -EINVAL; ++ goto error; ++ } ++ /* ++ * Perform minimum nandsim structure initialization to handle ++ * the initial ID read command correctly ++ */ ++ if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF) ++ nand->geom.idbytes = 8; ++ else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF) ++ nand->geom.idbytes = 6; ++ else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF) ++ nand->geom.idbytes = 4; ++ else ++ nand->geom.idbytes = 2; ++ nand->regs.status = NS_STATUS_OK(nand); ++ nand->nxstate = STATE_UNKNOWN; ++ nand->options |= OPT_PAGE512; /* temporary value */ ++ memcpy(nand->ids, id_bytes, sizeof(nand->ids)); ++ if (bus_width == 16) { ++ nand->busw = 16; ++ chip->options |= NAND_BUSWIDTH_16; ++ } ++ ++ nsmtd->owner = THIS_MODULE; ++ ++ if ((retval = parse_weakblocks()) != 0) ++ goto error; ++ ++ if ((retval = parse_weakpages()) != 0) ++ goto error; ++ ++ if ((retval = parse_gravepages()) != 0) ++ goto error; ++ ++ retval = nand_scan_ident(nsmtd, 1, NULL); ++ if (retval) { ++ NS_ERR("cannot scan NAND Simulator device\n"); ++ goto error; ++ } ++ ++ if (bch) { ++ unsigned int eccsteps, eccbytes; ++ if (!mtd_nand_has_bch()) { ++ NS_ERR("BCH ECC support is disabled\n"); ++ retval = -EINVAL; ++ goto error; ++ } ++ /* use 512-byte ecc blocks */ ++ eccsteps = nsmtd->writesize/512; ++ eccbytes = (bch*13+7)/8; ++ /* do not bother supporting small page devices */ ++ if ((nsmtd->oobsize < 64) || !eccsteps) { ++ NS_ERR("bch not available on small page devices\n"); ++ retval = -EINVAL; ++ goto error; ++ } ++ if ((eccbytes*eccsteps+2) > nsmtd->oobsize) { ++ NS_ERR("invalid bch value %u\n", bch); ++ retval = -EINVAL; ++ goto error; ++ } ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_BCH; ++ chip->ecc.size = 512; ++ chip->ecc.strength = bch; ++ chip->ecc.bytes = eccbytes; ++ NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); ++ } ++ ++ retval = nand_scan_tail(nsmtd); ++ if (retval) { ++ NS_ERR("can't register NAND Simulator\n"); ++ goto error; ++ } ++ ++ if (overridesize) { ++ uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; ++ if (new_size >> overridesize != nsmtd->erasesize) { ++ NS_ERR("overridesize is too big\n"); ++ retval = -EINVAL; ++ goto err_exit; ++ } ++ /* N.B. This relies on nand_scan not doing anything with the size before we change it */ ++ nsmtd->size = new_size; ++ chip->chipsize = new_size; ++ chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1; ++ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; ++ } ++ ++ if ((retval = setup_wear_reporting(nsmtd)) != 0) ++ goto err_exit; ++ ++ if ((retval = init_nandsim(nsmtd)) != 0) ++ goto err_exit; ++ ++ if ((retval = chip->scan_bbt(nsmtd)) != 0) ++ goto err_exit; ++ ++ if ((retval = parse_badblocks(nand, nsmtd)) != 0) ++ goto err_exit; ++ ++ /* Register NAND partitions */ ++ retval = mtd_device_register(nsmtd, &nand->partitions[0], ++ nand->nbparts); ++ if (retval != 0) ++ goto err_exit; ++ ++ if ((retval = nandsim_debugfs_create(nand)) != 0) ++ goto err_exit; ++ ++ return 0; ++ ++err_exit: ++ free_nandsim(nand); ++ nand_release(nsmtd); ++ for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) ++ kfree(nand->partitions[i].name); ++error: ++ kfree(chip); ++ free_lists(); ++ ++ return retval; ++} ++ ++module_init(ns_init_module); ++ ++/* ++ * Module clean-up function ++ */ ++static void __exit ns_cleanup_module(void) ++{ ++ struct nand_chip *chip = mtd_to_nand(nsmtd); ++ struct nandsim *ns = nand_get_controller_data(chip); ++ int i; ++ ++ free_nandsim(ns); /* Free nandsim private resources */ ++ nand_release(nsmtd); /* Unregister driver */ ++ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) ++ kfree(ns->partitions[i].name); ++ kfree(mtd_to_nand(nsmtd)); /* Free other structures */ ++ free_lists(); ++} ++ ++module_exit(ns_cleanup_module); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Artem B. Bityuckiy"); ++MODULE_DESCRIPTION ("The NAND flash simulator"); +diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c +new file mode 100644 +index 00000000..540fa1a +--- /dev/null ++++ b/drivers/mtd/nand/raw/ndfc.c +@@ -0,0 +1,286 @@ ++/* ++ * Overview: ++ * Platform independent driver for NDFC (NanD Flash Controller) ++ * integrated into EP440 cores ++ * ++ * Ported to an OF platform driver by Sean MacLennan ++ * ++ * The NDFC supports multiple chips, but this driver only supports a ++ * single chip since I do not have access to any boards with ++ * multiple chips. ++ * ++ * Author: Thomas Gleixner ++ * ++ * Copyright 2006 IBM ++ * Copyright 2008 PIKA Technologies ++ * Sean MacLennan ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define NDFC_MAX_CS 4 ++ ++struct ndfc_controller { ++ struct platform_device *ofdev; ++ void __iomem *ndfcbase; ++ struct nand_chip chip; ++ int chip_select; ++ struct nand_controller ndfc_control; ++}; ++ ++static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS]; ++ ++static void ndfc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ uint32_t ccr; ++ struct nand_chip *nchip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(nchip); ++ ++ ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); ++ if (chip >= 0) { ++ ccr &= ~NDFC_CCR_BS_MASK; ++ ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); ++ } else ++ ccr |= NDFC_CCR_RESET_CE; ++ out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); ++} ++ ++static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(chip); ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD); ++ else ++ writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE); ++} ++ ++static int ndfc_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(chip); ++ ++ return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; ++} ++ ++static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ uint32_t ccr; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(chip); ++ ++ ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); ++ ccr |= NDFC_CCR_RESET_ECC; ++ out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); ++ wmb(); ++} ++ ++static int ndfc_calculate_ecc(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_code) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(chip); ++ uint32_t ecc; ++ uint8_t *p = (uint8_t *)&ecc; ++ ++ wmb(); ++ ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); ++ /* The NDFC uses Smart Media (SMC) bytes order */ ++ ecc_code[0] = p[1]; ++ ecc_code[1] = p[2]; ++ ecc_code[2] = p[3]; ++ ++ return 0; ++} ++ ++/* ++ * Speedups for buffer read/write/verify ++ * ++ * NDFC allows 32bit read/write of data. So we can speed up the buffer ++ * functions. No further checking, as nand_base will always read/write ++ * page aligned. ++ */ ++static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(chip); ++ uint32_t *p = (uint32_t *) buf; ++ ++ for(;len > 0; len -= 4) ++ *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); ++} ++ ++static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct ndfc_controller *ndfc = nand_get_controller_data(chip); ++ uint32_t *p = (uint32_t *) buf; ++ ++ for(;len > 0; len -= 4) ++ out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); ++} ++ ++/* ++ * Initialize chip structure ++ */ ++static int ndfc_chip_init(struct ndfc_controller *ndfc, ++ struct device_node *node) ++{ ++ struct device_node *flash_np; ++ struct nand_chip *chip = &ndfc->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; ++ chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; ++ chip->cmd_ctrl = ndfc_hwcontrol; ++ chip->dev_ready = ndfc_ready; ++ chip->select_chip = ndfc_select_chip; ++ chip->chip_delay = 50; ++ chip->controller = &ndfc->ndfc_control; ++ chip->read_buf = ndfc_read_buf; ++ chip->write_buf = ndfc_write_buf; ++ chip->ecc.correct = nand_correct_data; ++ chip->ecc.hwctl = ndfc_enable_hwecc; ++ chip->ecc.calculate = ndfc_calculate_ecc; ++ chip->ecc.mode = NAND_ECC_HW; ++ chip->ecc.size = 256; ++ chip->ecc.bytes = 3; ++ chip->ecc.strength = 1; ++ nand_set_controller_data(chip, ndfc); ++ ++ mtd->dev.parent = &ndfc->ofdev->dev; ++ ++ flash_np = of_get_next_child(node, NULL); ++ if (!flash_np) ++ return -ENODEV; ++ nand_set_flash_node(chip, flash_np); ++ ++ mtd->name = kasprintf(GFP_KERNEL, "%s.%s", dev_name(&ndfc->ofdev->dev), ++ flash_np->name); ++ if (!mtd->name) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = nand_scan(mtd, 1); ++ if (ret) ++ goto err; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ ++err: ++ of_node_put(flash_np); ++ if (ret) ++ kfree(mtd->name); ++ return ret; ++} ++ ++static int ndfc_probe(struct platform_device *ofdev) ++{ ++ struct ndfc_controller *ndfc; ++ const __be32 *reg; ++ u32 ccr; ++ u32 cs; ++ int err, len; ++ ++ /* Read the reg property to get the chip select */ ++ reg = of_get_property(ofdev->dev.of_node, "reg", &len); ++ if (reg == NULL || len != 12) { ++ dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); ++ return -ENOENT; ++ } ++ ++ cs = be32_to_cpu(reg[0]); ++ if (cs >= NDFC_MAX_CS) { ++ dev_err(&ofdev->dev, "invalid CS number (%d)\n", cs); ++ return -EINVAL; ++ } ++ ++ ndfc = &ndfc_ctrl[cs]; ++ ndfc->chip_select = cs; ++ ++ nand_controller_init(&ndfc->ndfc_control); ++ ndfc->ofdev = ofdev; ++ dev_set_drvdata(&ofdev->dev, ndfc); ++ ++ ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); ++ if (!ndfc->ndfcbase) { ++ dev_err(&ofdev->dev, "failed to get memory\n"); ++ return -EIO; ++ } ++ ++ ccr = NDFC_CCR_BS(ndfc->chip_select); ++ ++ /* It is ok if ccr does not exist - just default to 0 */ ++ reg = of_get_property(ofdev->dev.of_node, "ccr", NULL); ++ if (reg) ++ ccr |= be32_to_cpup(reg); ++ ++ out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); ++ ++ /* Set the bank settings if given */ ++ reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL); ++ if (reg) { ++ int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); ++ out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg)); ++ } ++ ++ err = ndfc_chip_init(ndfc, ofdev->dev.of_node); ++ if (err) { ++ iounmap(ndfc->ndfcbase); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int ndfc_remove(struct platform_device *ofdev) ++{ ++ struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); ++ struct mtd_info *mtd = nand_to_mtd(&ndfc->chip); ++ ++ nand_release(mtd); ++ kfree(mtd->name); ++ ++ return 0; ++} ++ ++static const struct of_device_id ndfc_match[] = { ++ { .compatible = "ibm,ndfc", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, ndfc_match); ++ ++static struct platform_driver ndfc_driver = { ++ .driver = { ++ .name = "ndfc", ++ .of_match_table = ndfc_match, ++ }, ++ .probe = ndfc_probe, ++ .remove = ndfc_remove, ++}; ++ ++module_platform_driver(ndfc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Thomas Gleixner "); ++MODULE_DESCRIPTION("OF Platform driver for NDFC"); +diff --git a/drivers/mtd/nand/raw/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c +new file mode 100644 +index 00000000..af5b32c9 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nuc900_nand.c +@@ -0,0 +1,306 @@ ++/* ++ * Copyright © 2009 Nuvoton technology corporation. ++ * ++ * Wan ZongShun ++ * ++ * 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;version 2 of the License. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define REG_FMICSR 0x00 ++#define REG_SMCSR 0xa0 ++#define REG_SMISR 0xac ++#define REG_SMCMD 0xb0 ++#define REG_SMADDR 0xb4 ++#define REG_SMDATA 0xb8 ++ ++#define RESET_FMI 0x01 ++#define NAND_EN 0x08 ++#define READYBUSY (0x01 << 18) ++ ++#define SWRST 0x01 ++#define PSIZE (0x01 << 3) ++#define DMARWEN (0x03 << 1) ++#define BUSWID (0x01 << 4) ++#define ECC4EN (0x01 << 5) ++#define WP (0x01 << 24) ++#define NANDCS (0x01 << 25) ++#define ENDADDR (0x01 << 31) ++ ++#define read_data_reg(dev) \ ++ __raw_readl((dev)->reg + REG_SMDATA) ++ ++#define write_data_reg(dev, val) \ ++ __raw_writel((val), (dev)->reg + REG_SMDATA) ++ ++#define write_cmd_reg(dev, val) \ ++ __raw_writel((val), (dev)->reg + REG_SMCMD) ++ ++#define write_addr_reg(dev, val) \ ++ __raw_writel((val), (dev)->reg + REG_SMADDR) ++ ++struct nuc900_nand { ++ struct nand_chip chip; ++ void __iomem *reg; ++ struct clk *clk; ++ spinlock_t lock; ++}; ++ ++static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip); ++} ++ ++static const struct mtd_partition partitions[] = { ++ { ++ .name = "NAND FS 0", ++ .offset = 0, ++ .size = 8 * 1024 * 1024 ++ }, ++ { ++ .name = "NAND FS 1", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL ++ } ++}; ++ ++static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) ++{ ++ unsigned char ret; ++ struct nuc900_nand *nand = mtd_to_nuc900(mtd); ++ ++ ret = (unsigned char)read_data_reg(nand); ++ ++ return ret; ++} ++ ++static void nuc900_nand_read_buf(struct mtd_info *mtd, ++ unsigned char *buf, int len) ++{ ++ int i; ++ struct nuc900_nand *nand = mtd_to_nuc900(mtd); ++ ++ for (i = 0; i < len; i++) ++ buf[i] = (unsigned char)read_data_reg(nand); ++} ++ ++static void nuc900_nand_write_buf(struct mtd_info *mtd, ++ const unsigned char *buf, int len) ++{ ++ int i; ++ struct nuc900_nand *nand = mtd_to_nuc900(mtd); ++ ++ for (i = 0; i < len; i++) ++ write_data_reg(nand, buf[i]); ++} ++ ++static int nuc900_check_rb(struct nuc900_nand *nand) ++{ ++ unsigned int val; ++ spin_lock(&nand->lock); ++ val = __raw_readl(nand->reg + REG_SMISR); ++ val &= READYBUSY; ++ spin_unlock(&nand->lock); ++ ++ return val; ++} ++ ++static int nuc900_nand_devready(struct mtd_info *mtd) ++{ ++ struct nuc900_nand *nand = mtd_to_nuc900(mtd); ++ int ready; ++ ++ ready = (nuc900_check_rb(nand)) ? 1 : 0; ++ return ready; ++} ++ ++static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ register struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nuc900_nand *nand = mtd_to_nuc900(mtd); ++ ++ if (command == NAND_CMD_READOOB) { ++ column += mtd->writesize; ++ command = NAND_CMD_READ0; ++ } ++ ++ write_cmd_reg(nand, command & 0xff); ++ ++ if (column != -1 || page_addr != -1) { ++ ++ if (column != -1) { ++ if (chip->options & NAND_BUSWIDTH_16 && ++ !nand_opcode_8bits(command)) ++ column >>= 1; ++ write_addr_reg(nand, column); ++ write_addr_reg(nand, column >> 8 | ENDADDR); ++ } ++ if (page_addr != -1) { ++ write_addr_reg(nand, page_addr); ++ ++ if (chip->options & NAND_ROW_ADDR_3) { ++ write_addr_reg(nand, page_addr >> 8); ++ write_addr_reg(nand, page_addr >> 16 | ENDADDR); ++ } else { ++ write_addr_reg(nand, page_addr >> 8 | ENDADDR); ++ } ++ } ++ } ++ ++ switch (command) { ++ case NAND_CMD_CACHEDPROG: ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_RNDIN: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (chip->dev_ready) ++ break; ++ udelay(chip->chip_delay); ++ ++ write_cmd_reg(nand, NAND_CMD_STATUS); ++ write_cmd_reg(nand, command); ++ ++ while (!nuc900_check_rb(nand)) ++ ; ++ ++ return; ++ ++ case NAND_CMD_RNDOUT: ++ write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); ++ return; ++ ++ case NAND_CMD_READ0: ++ ++ write_cmd_reg(nand, NAND_CMD_READSTART); ++ default: ++ ++ if (!chip->dev_ready) { ++ udelay(chip->chip_delay); ++ return; ++ } ++ } ++ ++ /* Apply this short delay always to ensure that we do wait tWB in ++ * any case on any machine. */ ++ ndelay(100); ++ ++ while (!chip->dev_ready(mtd)) ++ ; ++} ++ ++ ++static void nuc900_nand_enable(struct nuc900_nand *nand) ++{ ++ unsigned int val; ++ spin_lock(&nand->lock); ++ __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); ++ ++ val = __raw_readl(nand->reg + REG_FMICSR); ++ ++ if (!(val & NAND_EN)) ++ __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); ++ ++ val = __raw_readl(nand->reg + REG_SMCSR); ++ ++ val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); ++ val |= WP; ++ ++ __raw_writel(val, nand->reg + REG_SMCSR); ++ ++ spin_unlock(&nand->lock); ++} ++ ++static int nuc900_nand_probe(struct platform_device *pdev) ++{ ++ struct nuc900_nand *nuc900_nand; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct resource *res; ++ ++ nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), ++ GFP_KERNEL); ++ if (!nuc900_nand) ++ return -ENOMEM; ++ chip = &(nuc900_nand->chip); ++ mtd = nand_to_mtd(chip); ++ ++ mtd->dev.parent = &pdev->dev; ++ spin_lock_init(&nuc900_nand->lock); ++ ++ nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(nuc900_nand->clk)) ++ return -ENOENT; ++ clk_enable(nuc900_nand->clk); ++ ++ chip->cmdfunc = nuc900_nand_command_lp; ++ chip->dev_ready = nuc900_nand_devready; ++ chip->read_byte = nuc900_nand_read_byte; ++ chip->write_buf = nuc900_nand_write_buf; ++ chip->read_buf = nuc900_nand_read_buf; ++ chip->chip_delay = 50; ++ chip->options = 0; ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(nuc900_nand->reg)) ++ return PTR_ERR(nuc900_nand->reg); ++ ++ nuc900_nand_enable(nuc900_nand); ++ ++ if (nand_scan(mtd, 1)) ++ return -ENXIO; ++ ++ mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions)); ++ ++ platform_set_drvdata(pdev, nuc900_nand); ++ ++ return 0; ++} ++ ++static int nuc900_nand_remove(struct platform_device *pdev) ++{ ++ struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); ++ ++ nand_release(nand_to_mtd(&nuc900_nand->chip)); ++ clk_disable(nuc900_nand->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver nuc900_nand_driver = { ++ .probe = nuc900_nand_probe, ++ .remove = nuc900_nand_remove, ++ .driver = { ++ .name = "nuc900-fmi", ++ }, ++}; ++ ++module_platform_driver(nuc900_nand_driver); ++ ++MODULE_AUTHOR("Wan ZongShun "); ++MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:nuc900-fmi"); +diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c +new file mode 100644 +index 00000000..1dc83bb +--- /dev/null ++++ b/drivers/mtd/nand/raw/omap2.c +@@ -0,0 +1,2338 @@ ++/* ++ * Copyright © 2004 Texas Instruments, Jian Zhang ++ * Copyright © 2004 Micron Technology Inc. ++ * Copyright © 2004 David Brownell ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#define DRIVER_NAME "omap2-nand" ++#define OMAP_NAND_TIMEOUT_MS 5000 ++ ++#define NAND_Ecc_P1e (1 << 0) ++#define NAND_Ecc_P2e (1 << 1) ++#define NAND_Ecc_P4e (1 << 2) ++#define NAND_Ecc_P8e (1 << 3) ++#define NAND_Ecc_P16e (1 << 4) ++#define NAND_Ecc_P32e (1 << 5) ++#define NAND_Ecc_P64e (1 << 6) ++#define NAND_Ecc_P128e (1 << 7) ++#define NAND_Ecc_P256e (1 << 8) ++#define NAND_Ecc_P512e (1 << 9) ++#define NAND_Ecc_P1024e (1 << 10) ++#define NAND_Ecc_P2048e (1 << 11) ++ ++#define NAND_Ecc_P1o (1 << 16) ++#define NAND_Ecc_P2o (1 << 17) ++#define NAND_Ecc_P4o (1 << 18) ++#define NAND_Ecc_P8o (1 << 19) ++#define NAND_Ecc_P16o (1 << 20) ++#define NAND_Ecc_P32o (1 << 21) ++#define NAND_Ecc_P64o (1 << 22) ++#define NAND_Ecc_P128o (1 << 23) ++#define NAND_Ecc_P256o (1 << 24) ++#define NAND_Ecc_P512o (1 << 25) ++#define NAND_Ecc_P1024o (1 << 26) ++#define NAND_Ecc_P2048o (1 << 27) ++ ++#define TF(value) (value ? 1 : 0) ++ ++#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) ++#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) ++#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) ++#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) ++#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) ++#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) ++#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) ++#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) ++ ++#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) ++#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) ++#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) ++#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) ++#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) ++#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) ++#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) ++#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) ++ ++#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) ++#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) ++#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) ++#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) ++#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) ++#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) ++#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) ++#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) ++ ++#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) ++#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) ++#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) ++#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) ++#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) ++#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) ++#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) ++#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) ++ ++#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) ++#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) ++ ++#define PREFETCH_CONFIG1_CS_SHIFT 24 ++#define ECC_CONFIG_CS_SHIFT 1 ++#define CS_MASK 0x7 ++#define ENABLE_PREFETCH (0x1 << 7) ++#define DMA_MPU_MODE_SHIFT 2 ++#define ECCSIZE0_SHIFT 12 ++#define ECCSIZE1_SHIFT 22 ++#define ECC1RESULTSIZE 0x1 ++#define ECCCLEAR 0x100 ++#define ECC1 0x1 ++#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 ++#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) ++#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) ++#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) ++#define STATUS_BUFF_EMPTY 0x00000001 ++ ++#define SECTOR_BYTES 512 ++/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ ++#define BCH4_BIT_PAD 4 ++ ++/* GPMC ecc engine settings for read */ ++#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ ++#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ ++#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ ++#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ ++#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ ++ ++/* GPMC ecc engine settings for write */ ++#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ ++#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ ++#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ ++ ++#define BADBLOCK_MARKER_LENGTH 2 ++ ++static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, ++ 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, ++ 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, ++ 0x07, 0x0e}; ++static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, ++ 0xac, 0x6b, 0xff, 0x99, 0x7b}; ++static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; ++ ++/* Shared among all NAND instances to synchronize access to the ECC Engine */ ++static struct nand_controller omap_gpmc_controller = { ++ .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), ++ .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), ++}; ++ ++struct omap_nand_info { ++ struct nand_chip nand; ++ struct platform_device *pdev; ++ ++ int gpmc_cs; ++ bool dev_ready; ++ enum nand_io xfer_type; ++ int devsize; ++ enum omap_ecc ecc_opt; ++ struct device_node *elm_of_node; ++ ++ unsigned long phys_base; ++ struct completion comp; ++ struct dma_chan *dma; ++ int gpmc_irq_fifo; ++ int gpmc_irq_count; ++ enum { ++ OMAP_NAND_IO_READ = 0, /* read */ ++ OMAP_NAND_IO_WRITE, /* write */ ++ } iomode; ++ u_char *buf; ++ int buf_len; ++ /* Interface to GPMC */ ++ struct gpmc_nand_regs reg; ++ struct gpmc_nand_ops *ops; ++ bool flash_bbt; ++ /* fields specific for BCHx_HW ECC scheme */ ++ struct device *elm_dev; ++ /* NAND ready gpio */ ++ struct gpio_desc *ready_gpiod; ++}; ++ ++static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); ++} ++ ++/** ++ * omap_prefetch_enable - configures and starts prefetch transfer ++ * @cs: cs (chip select) number ++ * @fifo_th: fifo threshold to be used for read/ write ++ * @dma_mode: dma mode enable (1) or disable (0) ++ * @u32_count: number of bytes to be transferred ++ * @is_write: prefetch read(0) or write post(1) mode ++ */ ++static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, ++ unsigned int u32_count, int is_write, struct omap_nand_info *info) ++{ ++ u32 val; ++ ++ if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) ++ return -1; ++ ++ if (readl(info->reg.gpmc_prefetch_control)) ++ return -EBUSY; ++ ++ /* Set the amount of bytes to be prefetched */ ++ writel(u32_count, info->reg.gpmc_prefetch_config2); ++ ++ /* Set dma/mpu mode, the prefetch read / post write and ++ * enable the engine. Set which cs is has requested for. ++ */ ++ val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | ++ PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | ++ (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1)); ++ writel(val, info->reg.gpmc_prefetch_config1); ++ ++ /* Start the prefetch engine */ ++ writel(0x1, info->reg.gpmc_prefetch_control); ++ ++ return 0; ++} ++ ++/** ++ * omap_prefetch_reset - disables and stops the prefetch engine ++ */ ++static int omap_prefetch_reset(int cs, struct omap_nand_info *info) ++{ ++ u32 config1; ++ ++ /* check if the same module/cs is trying to reset */ ++ config1 = readl(info->reg.gpmc_prefetch_config1); ++ if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) ++ return -EINVAL; ++ ++ /* Stop the PFPW engine */ ++ writel(0x0, info->reg.gpmc_prefetch_control); ++ ++ /* Reset/disable the PFPW engine */ ++ writel(0x0, info->reg.gpmc_prefetch_config1); ++ ++ return 0; ++} ++ ++/** ++ * omap_hwcontrol - hardware specific access to control-lines ++ * @mtd: MTD device structure ++ * @cmd: command to device ++ * @ctrl: ++ * NAND_NCE: bit 0 -> don't care ++ * NAND_CLE: bit 1 -> Command Latch ++ * NAND_ALE: bit 2 -> Address Latch ++ * ++ * NOTE: boards may use different bits for these!! ++ */ ++static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ ++ if (cmd != NAND_CMD_NONE) { ++ if (ctrl & NAND_CLE) ++ writeb(cmd, info->reg.gpmc_nand_command); ++ ++ else if (ctrl & NAND_ALE) ++ writeb(cmd, info->reg.gpmc_nand_address); ++ ++ else /* NAND_NCE */ ++ writeb(cmd, info->reg.gpmc_nand_data); ++ } ++} ++ ++/** ++ * omap_read_buf8 - read data from NAND controller into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ ++ ioread8_rep(nand->IO_ADDR_R, buf, len); ++} ++ ++/** ++ * omap_write_buf8 - write buffer to NAND controller ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ u_char *p = (u_char *)buf; ++ bool status; ++ ++ while (len--) { ++ iowrite8(*p++, info->nand.IO_ADDR_W); ++ /* wait until buffer is available for write */ ++ do { ++ status = info->ops->nand_writebuffer_empty(); ++ } while (!status); ++ } ++} ++ ++/** ++ * omap_read_buf16 - read data from NAND controller into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ ++ ioread16_rep(nand->IO_ADDR_R, buf, len / 2); ++} ++ ++/** ++ * omap_write_buf16 - write buffer to NAND controller ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ u16 *p = (u16 *) buf; ++ bool status; ++ /* FIXME try bursts of writesw() or DMA ... */ ++ len >>= 1; ++ ++ while (len--) { ++ iowrite16(*p++, info->nand.IO_ADDR_W); ++ /* wait until buffer is available for write */ ++ do { ++ status = info->ops->nand_writebuffer_empty(); ++ } while (!status); ++ } ++} ++ ++/** ++ * omap_read_buf_pref - read data from NAND controller into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ uint32_t r_count = 0; ++ int ret = 0; ++ u32 *p = (u32 *)buf; ++ ++ /* take care of subpage reads */ ++ if (len % 4) { ++ if (info->nand.options & NAND_BUSWIDTH_16) ++ omap_read_buf16(mtd, buf, len % 4); ++ else ++ omap_read_buf8(mtd, buf, len % 4); ++ p = (u32 *) (buf + len % 4); ++ len -= len % 4; ++ } ++ ++ /* configure and start prefetch transfer */ ++ ret = omap_prefetch_enable(info->gpmc_cs, ++ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); ++ if (ret) { ++ /* PFPW engine is busy, use cpu copy method */ ++ if (info->nand.options & NAND_BUSWIDTH_16) ++ omap_read_buf16(mtd, (u_char *)p, len); ++ else ++ omap_read_buf8(mtd, (u_char *)p, len); ++ } else { ++ do { ++ r_count = readl(info->reg.gpmc_prefetch_status); ++ r_count = PREFETCH_STATUS_FIFO_CNT(r_count); ++ r_count = r_count >> 2; ++ ioread32_rep(info->nand.IO_ADDR_R, p, r_count); ++ p += r_count; ++ len -= r_count << 2; ++ } while (len); ++ /* disable and stop the PFPW engine */ ++ omap_prefetch_reset(info->gpmc_cs, info); ++ } ++} ++ ++/** ++ * omap_write_buf_pref - write buffer to NAND controller ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void omap_write_buf_pref(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ uint32_t w_count = 0; ++ int i = 0, ret = 0; ++ u16 *p = (u16 *)buf; ++ unsigned long tim, limit; ++ u32 val; ++ ++ /* take care of subpage writes */ ++ if (len % 2 != 0) { ++ writeb(*buf, info->nand.IO_ADDR_W); ++ p = (u16 *)(buf + 1); ++ len--; ++ } ++ ++ /* configure and start prefetch transfer */ ++ ret = omap_prefetch_enable(info->gpmc_cs, ++ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); ++ if (ret) { ++ /* PFPW engine is busy, use cpu copy method */ ++ if (info->nand.options & NAND_BUSWIDTH_16) ++ omap_write_buf16(mtd, (u_char *)p, len); ++ else ++ omap_write_buf8(mtd, (u_char *)p, len); ++ } else { ++ while (len) { ++ w_count = readl(info->reg.gpmc_prefetch_status); ++ w_count = PREFETCH_STATUS_FIFO_CNT(w_count); ++ w_count = w_count >> 1; ++ for (i = 0; (i < w_count) && len; i++, len -= 2) ++ iowrite16(*p++, info->nand.IO_ADDR_W); ++ } ++ /* wait for data to flushed-out before reset the prefetch */ ++ tim = 0; ++ limit = (loops_per_jiffy * ++ msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); ++ do { ++ cpu_relax(); ++ val = readl(info->reg.gpmc_prefetch_status); ++ val = PREFETCH_STATUS_COUNT(val); ++ } while (val && (tim++ < limit)); ++ ++ /* disable and stop the PFPW engine */ ++ omap_prefetch_reset(info->gpmc_cs, info); ++ } ++} ++ ++/* ++ * omap_nand_dma_callback: callback on the completion of dma transfer ++ * @data: pointer to completion data structure ++ */ ++static void omap_nand_dma_callback(void *data) ++{ ++ complete((struct completion *) data); ++} ++ ++/* ++ * omap_nand_dma_transfer: configure and start dma transfer ++ * @mtd: MTD device structure ++ * @addr: virtual address in RAM of source/destination ++ * @len: number of data bytes to be transferred ++ * @is_write: flag for read/write operation ++ */ ++static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, ++ unsigned int len, int is_write) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ struct dma_async_tx_descriptor *tx; ++ enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : ++ DMA_FROM_DEVICE; ++ struct scatterlist sg; ++ unsigned long tim, limit; ++ unsigned n; ++ int ret; ++ u32 val; ++ ++ if (!virt_addr_valid(addr)) ++ goto out_copy; ++ ++ sg_init_one(&sg, addr, len); ++ n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); ++ if (n == 0) { ++ dev_err(&info->pdev->dev, ++ "Couldn't DMA map a %d byte buffer\n", len); ++ goto out_copy; ++ } ++ ++ tx = dmaengine_prep_slave_sg(info->dma, &sg, n, ++ is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ if (!tx) ++ goto out_copy_unmap; ++ ++ tx->callback = omap_nand_dma_callback; ++ tx->callback_param = &info->comp; ++ dmaengine_submit(tx); ++ ++ init_completion(&info->comp); ++ ++ /* setup and start DMA using dma_addr */ ++ dma_async_issue_pending(info->dma); ++ ++ /* configure and start prefetch transfer */ ++ ret = omap_prefetch_enable(info->gpmc_cs, ++ PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); ++ if (ret) ++ /* PFPW engine is busy, use cpu copy method */ ++ goto out_copy_unmap; ++ ++ wait_for_completion(&info->comp); ++ tim = 0; ++ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); ++ ++ do { ++ cpu_relax(); ++ val = readl(info->reg.gpmc_prefetch_status); ++ val = PREFETCH_STATUS_COUNT(val); ++ } while (val && (tim++ < limit)); ++ ++ /* disable and stop the PFPW engine */ ++ omap_prefetch_reset(info->gpmc_cs, info); ++ ++ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); ++ return 0; ++ ++out_copy_unmap: ++ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); ++out_copy: ++ if (info->nand.options & NAND_BUSWIDTH_16) ++ is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) ++ : omap_write_buf16(mtd, (u_char *) addr, len); ++ else ++ is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) ++ : omap_write_buf8(mtd, (u_char *) addr, len); ++ return 0; ++} ++ ++/** ++ * omap_read_buf_dma_pref - read data from NAND controller into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ if (len <= mtd->oobsize) ++ omap_read_buf_pref(mtd, buf, len); ++ else ++ /* start transfer in DMA mode */ ++ omap_nand_dma_transfer(mtd, buf, len, 0x0); ++} ++ ++/** ++ * omap_write_buf_dma_pref - write buffer to NAND controller ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void omap_write_buf_dma_pref(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ if (len <= mtd->oobsize) ++ omap_write_buf_pref(mtd, buf, len); ++ else ++ /* start transfer in DMA mode */ ++ omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); ++} ++ ++/* ++ * omap_nand_irq - GPMC irq handler ++ * @this_irq: gpmc irq number ++ * @dev: omap_nand_info structure pointer is passed here ++ */ ++static irqreturn_t omap_nand_irq(int this_irq, void *dev) ++{ ++ struct omap_nand_info *info = (struct omap_nand_info *) dev; ++ u32 bytes; ++ ++ bytes = readl(info->reg.gpmc_prefetch_status); ++ bytes = PREFETCH_STATUS_FIFO_CNT(bytes); ++ bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ ++ if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ ++ if (this_irq == info->gpmc_irq_count) ++ goto done; ++ ++ if (info->buf_len && (info->buf_len < bytes)) ++ bytes = info->buf_len; ++ else if (!info->buf_len) ++ bytes = 0; ++ iowrite32_rep(info->nand.IO_ADDR_W, ++ (u32 *)info->buf, bytes >> 2); ++ info->buf = info->buf + bytes; ++ info->buf_len -= bytes; ++ ++ } else { ++ ioread32_rep(info->nand.IO_ADDR_R, ++ (u32 *)info->buf, bytes >> 2); ++ info->buf = info->buf + bytes; ++ ++ if (this_irq == info->gpmc_irq_count) ++ goto done; ++ } ++ ++ return IRQ_HANDLED; ++ ++done: ++ complete(&info->comp); ++ ++ disable_irq_nosync(info->gpmc_irq_fifo); ++ disable_irq_nosync(info->gpmc_irq_count); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * omap_read_buf_irq_pref - read data from NAND controller into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ int ret = 0; ++ ++ if (len <= mtd->oobsize) { ++ omap_read_buf_pref(mtd, buf, len); ++ return; ++ } ++ ++ info->iomode = OMAP_NAND_IO_READ; ++ info->buf = buf; ++ init_completion(&info->comp); ++ ++ /* configure and start prefetch transfer */ ++ ret = omap_prefetch_enable(info->gpmc_cs, ++ PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); ++ if (ret) ++ /* PFPW engine is busy, use cpu copy method */ ++ goto out_copy; ++ ++ info->buf_len = len; ++ ++ enable_irq(info->gpmc_irq_count); ++ enable_irq(info->gpmc_irq_fifo); ++ ++ /* waiting for read to complete */ ++ wait_for_completion(&info->comp); ++ ++ /* disable and stop the PFPW engine */ ++ omap_prefetch_reset(info->gpmc_cs, info); ++ return; ++ ++out_copy: ++ if (info->nand.options & NAND_BUSWIDTH_16) ++ omap_read_buf16(mtd, buf, len); ++ else ++ omap_read_buf8(mtd, buf, len); ++} ++ ++/* ++ * omap_write_buf_irq_pref - write buffer to NAND controller ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void omap_write_buf_irq_pref(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ int ret = 0; ++ unsigned long tim, limit; ++ u32 val; ++ ++ if (len <= mtd->oobsize) { ++ omap_write_buf_pref(mtd, buf, len); ++ return; ++ } ++ ++ info->iomode = OMAP_NAND_IO_WRITE; ++ info->buf = (u_char *) buf; ++ init_completion(&info->comp); ++ ++ /* configure and start prefetch transfer : size=24 */ ++ ret = omap_prefetch_enable(info->gpmc_cs, ++ (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); ++ if (ret) ++ /* PFPW engine is busy, use cpu copy method */ ++ goto out_copy; ++ ++ info->buf_len = len; ++ ++ enable_irq(info->gpmc_irq_count); ++ enable_irq(info->gpmc_irq_fifo); ++ ++ /* waiting for write to complete */ ++ wait_for_completion(&info->comp); ++ ++ /* wait for data to flushed-out before reset the prefetch */ ++ tim = 0; ++ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); ++ do { ++ val = readl(info->reg.gpmc_prefetch_status); ++ val = PREFETCH_STATUS_COUNT(val); ++ cpu_relax(); ++ } while (val && (tim++ < limit)); ++ ++ /* disable and stop the PFPW engine */ ++ omap_prefetch_reset(info->gpmc_cs, info); ++ return; ++ ++out_copy: ++ if (info->nand.options & NAND_BUSWIDTH_16) ++ omap_write_buf16(mtd, buf, len); ++ else ++ omap_write_buf8(mtd, buf, len); ++} ++ ++/** ++ * gen_true_ecc - This function will generate true ECC value ++ * @ecc_buf: buffer to store ecc code ++ * ++ * This generated true ECC value can be used when correcting ++ * data read from NAND flash memory core ++ */ ++static void gen_true_ecc(u8 *ecc_buf) ++{ ++ u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | ++ ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); ++ ++ ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | ++ P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); ++ ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | ++ P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); ++ ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | ++ P1e(tmp) | P2048o(tmp) | P2048e(tmp)); ++} ++ ++/** ++ * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data ++ * @ecc_data1: ecc code from nand spare area ++ * @ecc_data2: ecc code from hardware register obtained from hardware ecc ++ * @page_data: page data ++ * ++ * This function compares two ECC's and indicates if there is an error. ++ * If the error can be corrected it will be corrected to the buffer. ++ * If there is no error, %0 is returned. If there is an error but it ++ * was corrected, %1 is returned. Otherwise, %-1 is returned. ++ */ ++static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ ++ u8 *ecc_data2, /* read from register */ ++ u8 *page_data) ++{ ++ uint i; ++ u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; ++ u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; ++ u8 ecc_bit[24]; ++ u8 ecc_sum = 0; ++ u8 find_bit = 0; ++ uint find_byte = 0; ++ int isEccFF; ++ ++ isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); ++ ++ gen_true_ecc(ecc_data1); ++ gen_true_ecc(ecc_data2); ++ ++ for (i = 0; i <= 2; i++) { ++ *(ecc_data1 + i) = ~(*(ecc_data1 + i)); ++ *(ecc_data2 + i) = ~(*(ecc_data2 + i)); ++ } ++ ++ for (i = 0; i < 8; i++) { ++ tmp0_bit[i] = *ecc_data1 % 2; ++ *ecc_data1 = *ecc_data1 / 2; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ tmp1_bit[i] = *(ecc_data1 + 1) % 2; ++ *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ tmp2_bit[i] = *(ecc_data1 + 2) % 2; ++ *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ comp0_bit[i] = *ecc_data2 % 2; ++ *ecc_data2 = *ecc_data2 / 2; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ comp1_bit[i] = *(ecc_data2 + 1) % 2; ++ *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ comp2_bit[i] = *(ecc_data2 + 2) % 2; ++ *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; ++ } ++ ++ for (i = 0; i < 6; i++) ++ ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; ++ ++ for (i = 0; i < 8; i++) ++ ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; ++ ++ for (i = 0; i < 8; i++) ++ ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; ++ ++ ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; ++ ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; ++ ++ for (i = 0; i < 24; i++) ++ ecc_sum += ecc_bit[i]; ++ ++ switch (ecc_sum) { ++ case 0: ++ /* Not reached because this function is not called if ++ * ECC values are equal ++ */ ++ return 0; ++ ++ case 1: ++ /* Uncorrectable error */ ++ pr_debug("ECC UNCORRECTED_ERROR 1\n"); ++ return -EBADMSG; ++ ++ case 11: ++ /* UN-Correctable error */ ++ pr_debug("ECC UNCORRECTED_ERROR B\n"); ++ return -EBADMSG; ++ ++ case 12: ++ /* Correctable error */ ++ find_byte = (ecc_bit[23] << 8) + ++ (ecc_bit[21] << 7) + ++ (ecc_bit[19] << 6) + ++ (ecc_bit[17] << 5) + ++ (ecc_bit[15] << 4) + ++ (ecc_bit[13] << 3) + ++ (ecc_bit[11] << 2) + ++ (ecc_bit[9] << 1) + ++ ecc_bit[7]; ++ ++ find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; ++ ++ pr_debug("Correcting single bit ECC error at offset: " ++ "%d, bit: %d\n", find_byte, find_bit); ++ ++ page_data[find_byte] ^= (1 << find_bit); ++ ++ return 1; ++ default: ++ if (isEccFF) { ++ if (ecc_data2[0] == 0 && ++ ecc_data2[1] == 0 && ++ ecc_data2[2] == 0) ++ return 0; ++ } ++ pr_debug("UNCORRECTED_ERROR default\n"); ++ return -EBADMSG; ++ } ++} ++ ++/** ++ * omap_correct_data - Compares the ECC read with HW generated ECC ++ * @mtd: MTD device structure ++ * @dat: page data ++ * @read_ecc: ecc read from nand flash ++ * @calc_ecc: ecc read from HW ECC registers ++ * ++ * Compares the ecc read from nand spare area with ECC registers values ++ * and if ECC's mismatched, it will call 'omap_compare_ecc' for error ++ * detection and correction. If there are no errors, %0 is returned. If ++ * there were errors and all of the errors were corrected, the number of ++ * corrected errors is returned. If uncorrectable errors exist, %-1 is ++ * returned. ++ */ ++static int omap_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ int blockCnt = 0, i = 0, ret = 0; ++ int stat = 0; ++ ++ /* Ex NAND_ECC_HW12_2048 */ ++ if ((info->nand.ecc.mode == NAND_ECC_HW) && ++ (info->nand.ecc.size == 2048)) ++ blockCnt = 4; ++ else ++ blockCnt = 1; ++ ++ for (i = 0; i < blockCnt; i++) { ++ if (memcmp(read_ecc, calc_ecc, 3) != 0) { ++ ret = omap_compare_ecc(read_ecc, calc_ecc, dat); ++ if (ret < 0) ++ return ret; ++ /* keep track of the number of corrected errors */ ++ stat += ret; ++ } ++ read_ecc += 3; ++ calc_ecc += 3; ++ dat += 512; ++ } ++ return stat; ++} ++ ++/** ++ * omap_calcuate_ecc - Generate non-inverted ECC bytes. ++ * @mtd: MTD device structure ++ * @dat: The pointer to data on which ecc is computed ++ * @ecc_code: The ecc_code buffer ++ * ++ * Using noninverted ECC can be considered ugly since writing a blank ++ * page ie. padding will clear the ECC bytes. This is no problem as long ++ * nobody is trying to write data on the seemingly unused page. Reading ++ * an erased page will produce an ECC mismatch between generated and read ++ * ECC bytes that has to be dealt with separately. ++ */ ++static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ u32 val; ++ ++ val = readl(info->reg.gpmc_ecc_config); ++ if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) ++ return -EINVAL; ++ ++ /* read ecc result */ ++ val = readl(info->reg.gpmc_ecc1_result); ++ *ecc_code++ = val; /* P128e, ..., P1e */ ++ *ecc_code++ = val >> 16; /* P128o, ..., P1o */ ++ /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ ++ *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); ++ ++ return 0; ++} ++ ++/** ++ * omap_enable_hwecc - This function enables the hardware ecc functionality ++ * @mtd: MTD device structure ++ * @mode: Read/Write mode ++ */ ++static void omap_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; ++ u32 val; ++ ++ /* clear ecc and enable bits */ ++ val = ECCCLEAR | ECC1; ++ writel(val, info->reg.gpmc_ecc_control); ++ ++ /* program ecc and result sizes */ ++ val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) | ++ ECC1RESULTSIZE); ++ writel(val, info->reg.gpmc_ecc_size_config); ++ ++ switch (mode) { ++ case NAND_ECC_READ: ++ case NAND_ECC_WRITE: ++ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); ++ break; ++ case NAND_ECC_READSYN: ++ writel(ECCCLEAR, info->reg.gpmc_ecc_control); ++ break; ++ default: ++ dev_info(&info->pdev->dev, ++ "error: unrecognized Mode[%d]!\n", mode); ++ break; ++ } ++ ++ /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ ++ val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); ++ writel(val, info->reg.gpmc_ecc_config); ++} ++ ++/** ++ * omap_wait - wait until the command is done ++ * @mtd: MTD device structure ++ * @chip: NAND Chip structure ++ * ++ * Wait function is called during Program and erase operations and ++ * the way it is called from MTD layer, we should wait till the NAND ++ * chip is ready after the programming/erase operation has completed. ++ * ++ * Erase can take up to 400ms and program up to 20ms according to ++ * general NAND and SmartMedia specs ++ */ ++static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ unsigned long timeo = jiffies; ++ int status, state = this->state; ++ ++ if (state == FL_ERASING) ++ timeo += msecs_to_jiffies(400); ++ else ++ timeo += msecs_to_jiffies(20); ++ ++ writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); ++ while (time_before(jiffies, timeo)) { ++ status = readb(info->reg.gpmc_nand_data); ++ if (status & NAND_STATUS_READY) ++ break; ++ cond_resched(); ++ } ++ ++ status = readb(info->reg.gpmc_nand_data); ++ return status; ++} ++ ++/** ++ * omap_dev_ready - checks the NAND Ready GPIO line ++ * @mtd: MTD device structure ++ * ++ * Returns true if ready and false if busy. ++ */ ++static int omap_dev_ready(struct mtd_info *mtd) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ ++ return gpiod_get_value(info->ready_gpiod); ++} ++ ++/** ++ * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation ++ * @mtd: MTD device structure ++ * @mode: Read/Write mode ++ * ++ * When using BCH with SW correction (i.e. no ELM), sector size is set ++ * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode ++ * for both reading and writing with: ++ * eccsize0 = 0 (no additional protected byte in spare area) ++ * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) ++ */ ++static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) ++{ ++ unsigned int bch_type; ++ unsigned int dev_width, nsectors; ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ enum omap_ecc ecc_opt = info->ecc_opt; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u32 val, wr_mode; ++ unsigned int ecc_size1, ecc_size0; ++ ++ /* GPMC configurations for calculating ECC */ ++ switch (ecc_opt) { ++ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: ++ bch_type = 0; ++ nsectors = 1; ++ wr_mode = BCH_WRAPMODE_6; ++ ecc_size0 = BCH_ECC_SIZE0; ++ ecc_size1 = BCH_ECC_SIZE1; ++ break; ++ case OMAP_ECC_BCH4_CODE_HW: ++ bch_type = 0; ++ nsectors = chip->ecc.steps; ++ if (mode == NAND_ECC_READ) { ++ wr_mode = BCH_WRAPMODE_1; ++ ecc_size0 = BCH4R_ECC_SIZE0; ++ ecc_size1 = BCH4R_ECC_SIZE1; ++ } else { ++ wr_mode = BCH_WRAPMODE_6; ++ ecc_size0 = BCH_ECC_SIZE0; ++ ecc_size1 = BCH_ECC_SIZE1; ++ } ++ break; ++ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: ++ bch_type = 1; ++ nsectors = 1; ++ wr_mode = BCH_WRAPMODE_6; ++ ecc_size0 = BCH_ECC_SIZE0; ++ ecc_size1 = BCH_ECC_SIZE1; ++ break; ++ case OMAP_ECC_BCH8_CODE_HW: ++ bch_type = 1; ++ nsectors = chip->ecc.steps; ++ if (mode == NAND_ECC_READ) { ++ wr_mode = BCH_WRAPMODE_1; ++ ecc_size0 = BCH8R_ECC_SIZE0; ++ ecc_size1 = BCH8R_ECC_SIZE1; ++ } else { ++ wr_mode = BCH_WRAPMODE_6; ++ ecc_size0 = BCH_ECC_SIZE0; ++ ecc_size1 = BCH_ECC_SIZE1; ++ } ++ break; ++ case OMAP_ECC_BCH16_CODE_HW: ++ bch_type = 0x2; ++ nsectors = chip->ecc.steps; ++ if (mode == NAND_ECC_READ) { ++ wr_mode = 0x01; ++ ecc_size0 = 52; /* ECC bits in nibbles per sector */ ++ ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ ++ } else { ++ wr_mode = 0x01; ++ ecc_size0 = 0; /* extra bits in nibbles per sector */ ++ ecc_size1 = 52; /* OOB bits in nibbles per sector */ ++ } ++ break; ++ default: ++ return; ++ } ++ ++ writel(ECC1, info->reg.gpmc_ecc_control); ++ ++ /* Configure ecc size for BCH */ ++ val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); ++ writel(val, info->reg.gpmc_ecc_size_config); ++ ++ dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; ++ ++ /* BCH configuration */ ++ val = ((1 << 16) | /* enable BCH */ ++ (bch_type << 12) | /* BCH4/BCH8/BCH16 */ ++ (wr_mode << 8) | /* wrap mode */ ++ (dev_width << 7) | /* bus width */ ++ (((nsectors-1) & 0x7) << 4) | /* number of sectors */ ++ (info->gpmc_cs << 1) | /* ECC CS */ ++ (0x1)); /* enable ECC */ ++ ++ writel(val, info->reg.gpmc_ecc_config); ++ ++ /* Clear ecc and enable bits */ ++ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); ++} ++ ++static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; ++static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, ++ 0x97, 0x79, 0xe5, 0x24, 0xb5}; ++ ++/** ++ * _omap_calculate_ecc_bch - Generate ECC bytes for one sector ++ * @mtd: MTD device structure ++ * @dat: The pointer to data on which ecc is computed ++ * @ecc_code: The ecc_code buffer ++ * @i: The sector number (for a multi sector page) ++ * ++ * Support calculating of BCH4/8/16 ECC vectors for one sector ++ * within a page. Sector number is in @i. ++ */ ++static int _omap_calculate_ecc_bch(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_calc, int i) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ int eccbytes = info->nand.ecc.bytes; ++ struct gpmc_nand_regs *gpmc_regs = &info->reg; ++ u8 *ecc_code; ++ unsigned long bch_val1, bch_val2, bch_val3, bch_val4; ++ u32 val; ++ int j; ++ ++ ecc_code = ecc_calc; ++ switch (info->ecc_opt) { ++ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: ++ case OMAP_ECC_BCH8_CODE_HW: ++ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); ++ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); ++ bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); ++ bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); ++ *ecc_code++ = (bch_val4 & 0xFF); ++ *ecc_code++ = ((bch_val3 >> 24) & 0xFF); ++ *ecc_code++ = ((bch_val3 >> 16) & 0xFF); ++ *ecc_code++ = ((bch_val3 >> 8) & 0xFF); ++ *ecc_code++ = (bch_val3 & 0xFF); ++ *ecc_code++ = ((bch_val2 >> 24) & 0xFF); ++ *ecc_code++ = ((bch_val2 >> 16) & 0xFF); ++ *ecc_code++ = ((bch_val2 >> 8) & 0xFF); ++ *ecc_code++ = (bch_val2 & 0xFF); ++ *ecc_code++ = ((bch_val1 >> 24) & 0xFF); ++ *ecc_code++ = ((bch_val1 >> 16) & 0xFF); ++ *ecc_code++ = ((bch_val1 >> 8) & 0xFF); ++ *ecc_code++ = (bch_val1 & 0xFF); ++ break; ++ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: ++ case OMAP_ECC_BCH4_CODE_HW: ++ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); ++ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); ++ *ecc_code++ = ((bch_val2 >> 12) & 0xFF); ++ *ecc_code++ = ((bch_val2 >> 4) & 0xFF); ++ *ecc_code++ = ((bch_val2 & 0xF) << 4) | ++ ((bch_val1 >> 28) & 0xF); ++ *ecc_code++ = ((bch_val1 >> 20) & 0xFF); ++ *ecc_code++ = ((bch_val1 >> 12) & 0xFF); ++ *ecc_code++ = ((bch_val1 >> 4) & 0xFF); ++ *ecc_code++ = ((bch_val1 & 0xF) << 4); ++ break; ++ case OMAP_ECC_BCH16_CODE_HW: ++ val = readl(gpmc_regs->gpmc_bch_result6[i]); ++ ecc_code[0] = ((val >> 8) & 0xFF); ++ ecc_code[1] = ((val >> 0) & 0xFF); ++ val = readl(gpmc_regs->gpmc_bch_result5[i]); ++ ecc_code[2] = ((val >> 24) & 0xFF); ++ ecc_code[3] = ((val >> 16) & 0xFF); ++ ecc_code[4] = ((val >> 8) & 0xFF); ++ ecc_code[5] = ((val >> 0) & 0xFF); ++ val = readl(gpmc_regs->gpmc_bch_result4[i]); ++ ecc_code[6] = ((val >> 24) & 0xFF); ++ ecc_code[7] = ((val >> 16) & 0xFF); ++ ecc_code[8] = ((val >> 8) & 0xFF); ++ ecc_code[9] = ((val >> 0) & 0xFF); ++ val = readl(gpmc_regs->gpmc_bch_result3[i]); ++ ecc_code[10] = ((val >> 24) & 0xFF); ++ ecc_code[11] = ((val >> 16) & 0xFF); ++ ecc_code[12] = ((val >> 8) & 0xFF); ++ ecc_code[13] = ((val >> 0) & 0xFF); ++ val = readl(gpmc_regs->gpmc_bch_result2[i]); ++ ecc_code[14] = ((val >> 24) & 0xFF); ++ ecc_code[15] = ((val >> 16) & 0xFF); ++ ecc_code[16] = ((val >> 8) & 0xFF); ++ ecc_code[17] = ((val >> 0) & 0xFF); ++ val = readl(gpmc_regs->gpmc_bch_result1[i]); ++ ecc_code[18] = ((val >> 24) & 0xFF); ++ ecc_code[19] = ((val >> 16) & 0xFF); ++ ecc_code[20] = ((val >> 8) & 0xFF); ++ ecc_code[21] = ((val >> 0) & 0xFF); ++ val = readl(gpmc_regs->gpmc_bch_result0[i]); ++ ecc_code[22] = ((val >> 24) & 0xFF); ++ ecc_code[23] = ((val >> 16) & 0xFF); ++ ecc_code[24] = ((val >> 8) & 0xFF); ++ ecc_code[25] = ((val >> 0) & 0xFF); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ECC scheme specific syndrome customizations */ ++ switch (info->ecc_opt) { ++ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: ++ /* Add constant polynomial to remainder, so that ++ * ECC of blank pages results in 0x0 on reading back ++ */ ++ for (j = 0; j < eccbytes; j++) ++ ecc_calc[j] ^= bch4_polynomial[j]; ++ break; ++ case OMAP_ECC_BCH4_CODE_HW: ++ /* Set 8th ECC byte as 0x0 for ROM compatibility */ ++ ecc_calc[eccbytes - 1] = 0x0; ++ break; ++ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: ++ /* Add constant polynomial to remainder, so that ++ * ECC of blank pages results in 0x0 on reading back ++ */ ++ for (j = 0; j < eccbytes; j++) ++ ecc_calc[j] ^= bch8_polynomial[j]; ++ break; ++ case OMAP_ECC_BCH8_CODE_HW: ++ /* Set 14th ECC byte as 0x0 for ROM compatibility */ ++ ecc_calc[eccbytes - 1] = 0x0; ++ break; ++ case OMAP_ECC_BCH16_CODE_HW: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction ++ * @mtd: MTD device structure ++ * @dat: The pointer to data on which ecc is computed ++ * @ecc_code: The ecc_code buffer ++ * ++ * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used ++ * when SW based correction is required as ECC is required for one sector ++ * at a time. ++ */ ++static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_calc) ++{ ++ return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0); ++} ++ ++/** ++ * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors ++ * @mtd: MTD device structure ++ * @dat: The pointer to data on which ecc is computed ++ * @ecc_code: The ecc_code buffer ++ * ++ * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. ++ */ ++static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd, ++ const u_char *dat, u_char *ecc_calc) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ int eccbytes = info->nand.ecc.bytes; ++ unsigned long nsectors; ++ int i, ret; ++ ++ nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; ++ for (i = 0; i < nsectors; i++) { ++ ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); ++ if (ret) ++ return ret; ++ ++ ecc_calc += eccbytes; ++ } ++ ++ return 0; ++} ++ ++/** ++ * erased_sector_bitflips - count bit flips ++ * @data: data sector buffer ++ * @oob: oob buffer ++ * @info: omap_nand_info ++ * ++ * Check the bit flips in erased page falls below correctable level. ++ * If falls below, report the page as erased with correctable bit ++ * flip, else report as uncorrectable page. ++ */ ++static int erased_sector_bitflips(u_char *data, u_char *oob, ++ struct omap_nand_info *info) ++{ ++ int flip_bits = 0, i; ++ ++ for (i = 0; i < info->nand.ecc.size; i++) { ++ flip_bits += hweight8(~data[i]); ++ if (flip_bits > info->nand.ecc.strength) ++ return 0; ++ } ++ ++ for (i = 0; i < info->nand.ecc.bytes - 1; i++) { ++ flip_bits += hweight8(~oob[i]); ++ if (flip_bits > info->nand.ecc.strength) ++ return 0; ++ } ++ ++ /* ++ * Bit flips falls in correctable level. ++ * Fill data area with 0xFF ++ */ ++ if (flip_bits) { ++ memset(data, 0xFF, info->nand.ecc.size); ++ memset(oob, 0xFF, info->nand.ecc.bytes); ++ } ++ ++ return flip_bits; ++} ++ ++/** ++ * omap_elm_correct_data - corrects page data area in case error reported ++ * @mtd: MTD device structure ++ * @data: page data ++ * @read_ecc: ecc read from nand flash ++ * @calc_ecc: ecc read from HW ECC registers ++ * ++ * Calculated ecc vector reported as zero in case of non-error pages. ++ * In case of non-zero ecc vector, first filter out erased-pages, and ++ * then process data via ELM to detect bit-flips. ++ */ ++static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ struct nand_ecc_ctrl *ecc = &info->nand.ecc; ++ int eccsteps = info->nand.ecc.steps; ++ int i , j, stat = 0; ++ int eccflag, actual_eccbytes; ++ struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; ++ u_char *ecc_vec = calc_ecc; ++ u_char *spare_ecc = read_ecc; ++ u_char *erased_ecc_vec; ++ u_char *buf; ++ int bitflip_count; ++ bool is_error_reported = false; ++ u32 bit_pos, byte_pos, error_max, pos; ++ int err; ++ ++ switch (info->ecc_opt) { ++ case OMAP_ECC_BCH4_CODE_HW: ++ /* omit 7th ECC byte reserved for ROM code compatibility */ ++ actual_eccbytes = ecc->bytes - 1; ++ erased_ecc_vec = bch4_vector; ++ break; ++ case OMAP_ECC_BCH8_CODE_HW: ++ /* omit 14th ECC byte reserved for ROM code compatibility */ ++ actual_eccbytes = ecc->bytes - 1; ++ erased_ecc_vec = bch8_vector; ++ break; ++ case OMAP_ECC_BCH16_CODE_HW: ++ actual_eccbytes = ecc->bytes; ++ erased_ecc_vec = bch16_vector; ++ break; ++ default: ++ dev_err(&info->pdev->dev, "invalid driver configuration\n"); ++ return -EINVAL; ++ } ++ ++ /* Initialize elm error vector to zero */ ++ memset(err_vec, 0, sizeof(err_vec)); ++ ++ for (i = 0; i < eccsteps ; i++) { ++ eccflag = 0; /* initialize eccflag */ ++ ++ /* ++ * Check any error reported, ++ * In case of error, non zero ecc reported. ++ */ ++ for (j = 0; j < actual_eccbytes; j++) { ++ if (calc_ecc[j] != 0) { ++ eccflag = 1; /* non zero ecc, error present */ ++ break; ++ } ++ } ++ ++ if (eccflag == 1) { ++ if (memcmp(calc_ecc, erased_ecc_vec, ++ actual_eccbytes) == 0) { ++ /* ++ * calc_ecc[] matches pattern for ECC(all 0xff) ++ * so this is definitely an erased-page ++ */ ++ } else { ++ buf = &data[info->nand.ecc.size * i]; ++ /* ++ * count number of 0-bits in read_buf. ++ * This check can be removed once a similar ++ * check is introduced in generic NAND driver ++ */ ++ bitflip_count = erased_sector_bitflips( ++ buf, read_ecc, info); ++ if (bitflip_count) { ++ /* ++ * number of 0-bits within ECC limits ++ * So this may be an erased-page ++ */ ++ stat += bitflip_count; ++ } else { ++ /* ++ * Too many 0-bits. It may be a ++ * - programmed-page, OR ++ * - erased-page with many bit-flips ++ * So this page requires check by ELM ++ */ ++ err_vec[i].error_reported = true; ++ is_error_reported = true; ++ } ++ } ++ } ++ ++ /* Update the ecc vector */ ++ calc_ecc += ecc->bytes; ++ read_ecc += ecc->bytes; ++ } ++ ++ /* Check if any error reported */ ++ if (!is_error_reported) ++ return stat; ++ ++ /* Decode BCH error using ELM module */ ++ elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); ++ ++ err = 0; ++ for (i = 0; i < eccsteps; i++) { ++ if (err_vec[i].error_uncorrectable) { ++ dev_err(&info->pdev->dev, ++ "uncorrectable bit-flips found\n"); ++ err = -EBADMSG; ++ } else if (err_vec[i].error_reported) { ++ for (j = 0; j < err_vec[i].error_count; j++) { ++ switch (info->ecc_opt) { ++ case OMAP_ECC_BCH4_CODE_HW: ++ /* Add 4 bits to take care of padding */ ++ pos = err_vec[i].error_loc[j] + ++ BCH4_BIT_PAD; ++ break; ++ case OMAP_ECC_BCH8_CODE_HW: ++ case OMAP_ECC_BCH16_CODE_HW: ++ pos = err_vec[i].error_loc[j]; ++ break; ++ default: ++ return -EINVAL; ++ } ++ error_max = (ecc->size + actual_eccbytes) * 8; ++ /* Calculate bit position of error */ ++ bit_pos = pos % 8; ++ ++ /* Calculate byte position of error */ ++ byte_pos = (error_max - pos - 1) / 8; ++ ++ if (pos < error_max) { ++ if (byte_pos < 512) { ++ pr_debug("bitflip@dat[%d]=%x\n", ++ byte_pos, data[byte_pos]); ++ data[byte_pos] ^= 1 << bit_pos; ++ } else { ++ pr_debug("bitflip@oob[%d]=%x\n", ++ (byte_pos - 512), ++ spare_ecc[byte_pos - 512]); ++ spare_ecc[byte_pos - 512] ^= ++ 1 << bit_pos; ++ } ++ } else { ++ dev_err(&info->pdev->dev, ++ "invalid bit-flip @ %d:%d\n", ++ byte_pos, bit_pos); ++ err = -EBADMSG; ++ } ++ } ++ } ++ ++ /* Update number of correctable errors */ ++ stat += err_vec[i].error_count; ++ ++ /* Update page data with sector size */ ++ data += ecc->size; ++ spare_ecc += ecc->bytes; ++ } ++ ++ return (err) ? err : stat; ++} ++ ++/** ++ * omap_write_page_bch - BCH ecc based write page function for entire page ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page ++ * ++ * Custom write page method evolved to support multi sector writing in one shot ++ */ ++static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ int ret; ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ /* Enable GPMC ecc engine */ ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ ++ /* Write data */ ++ chip->write_buf(mtd, buf, mtd->writesize); ++ ++ /* Update ecc vector from GPMC result registers */ ++ omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); ++ ++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ /* Write ecc vector to OOB area */ ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/** ++ * omap_write_subpage_bch - BCH hardware ECC based subpage write ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @offset: column address of subpage within the page ++ * @data_len: data length ++ * @buf: data buffer ++ * @oob_required: must write chip->oob_poi to OOB ++ * @page: page number to write ++ * ++ * OMAP optimized subpage write method. ++ */ ++static int omap_write_subpage_bch(struct mtd_info *mtd, ++ struct nand_chip *chip, u32 offset, ++ u32 data_len, const u8 *buf, ++ int oob_required, int page) ++{ ++ u8 *ecc_calc = chip->ecc.calc_buf; ++ int ecc_size = chip->ecc.size; ++ int ecc_bytes = chip->ecc.bytes; ++ int ecc_steps = chip->ecc.steps; ++ u32 start_step = offset / ecc_size; ++ u32 end_step = (offset + data_len - 1) / ecc_size; ++ int step, ret = 0; ++ ++ /* ++ * Write entire page at one go as it would be optimal ++ * as ECC is calculated by hardware. ++ * ECC is calculated for all subpages but we choose ++ * only what we want. ++ */ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ /* Enable GPMC ECC engine */ ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ ++ /* Write data */ ++ chip->write_buf(mtd, buf, mtd->writesize); ++ ++ for (step = 0; step < ecc_steps; step++) { ++ /* mask ECC of un-touched subpages by padding 0xFF */ ++ if (step < start_step || step > end_step) ++ memset(ecc_calc, 0xff, ecc_bytes); ++ else ++ ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step); ++ ++ if (ret) ++ return ret; ++ ++ buf += ecc_size; ++ ecc_calc += ecc_bytes; ++ } ++ ++ /* copy calculated ECC for whole page to chip->buffer->oob */ ++ /* this include masked-value(0xFF) for unwritten subpages */ ++ ecc_calc = chip->ecc.calc_buf; ++ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ /* write OOB buffer to NAND device */ ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/** ++ * omap_read_page_bch - BCH ecc based page read function for entire page ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * @oob_required: caller requires OOB data read to chip->oob_poi ++ * @page: page number to read ++ * ++ * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module ++ * used for error correction. ++ * Custom method evolved to support ELM error correction & multi sector ++ * reading. On reading page data area is read along with OOB data with ++ * ecc engine enabled. ecc vector updated after read of OOB data. ++ * For non error pages ecc vector reported as zero. ++ */ ++static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ uint8_t *ecc_calc = chip->ecc.calc_buf; ++ uint8_t *ecc_code = chip->ecc.code_buf; ++ int stat, ret; ++ unsigned int max_bitflips = 0; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ /* Enable GPMC ecc engine */ ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ ++ /* Read data */ ++ chip->read_buf(mtd, buf, mtd->writesize); ++ ++ /* Read oob bytes */ ++ nand_change_read_column_op(chip, ++ mtd->writesize + BADBLOCK_MARKER_LENGTH, ++ chip->oob_poi + BADBLOCK_MARKER_LENGTH, ++ chip->ecc.total, false); ++ ++ /* Calculate ecc bytes */ ++ omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ ++ return max_bitflips; ++} ++ ++/** ++ * is_elm_present - checks for presence of ELM module by scanning DT nodes ++ * @omap_nand_info: NAND device structure containing platform data ++ */ ++static bool is_elm_present(struct omap_nand_info *info, ++ struct device_node *elm_node) ++{ ++ struct platform_device *pdev; ++ ++ /* check whether elm-id is passed via DT */ ++ if (!elm_node) { ++ dev_err(&info->pdev->dev, "ELM devicetree node not found\n"); ++ return false; ++ } ++ pdev = of_find_device_by_node(elm_node); ++ /* check whether ELM device is registered */ ++ if (!pdev) { ++ dev_err(&info->pdev->dev, "ELM device not found\n"); ++ return false; ++ } ++ /* ELM module available, now configure it */ ++ info->elm_dev = &pdev->dev; ++ return true; ++} ++ ++static bool omap2_nand_ecc_check(struct omap_nand_info *info, ++ struct omap_nand_platform_data *pdata) ++{ ++ bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm; ++ ++ switch (info->ecc_opt) { ++ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: ++ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: ++ ecc_needs_omap_bch = false; ++ ecc_needs_bch = true; ++ ecc_needs_elm = false; ++ break; ++ case OMAP_ECC_BCH4_CODE_HW: ++ case OMAP_ECC_BCH8_CODE_HW: ++ case OMAP_ECC_BCH16_CODE_HW: ++ ecc_needs_omap_bch = true; ++ ecc_needs_bch = false; ++ ecc_needs_elm = true; ++ break; ++ default: ++ ecc_needs_omap_bch = false; ++ ecc_needs_bch = false; ++ ecc_needs_elm = false; ++ break; ++ } ++ ++ if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) { ++ dev_err(&info->pdev->dev, ++ "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); ++ return false; ++ } ++ if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) { ++ dev_err(&info->pdev->dev, ++ "CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); ++ return false; ++ } ++ if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) { ++ dev_err(&info->pdev->dev, "ELM not available\n"); ++ return false; ++ } ++ ++ return true; ++} ++ ++static const char * const nand_xfer_types[] = { ++ [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", ++ [NAND_OMAP_POLLED] = "polled", ++ [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", ++ [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", ++}; ++ ++static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) ++{ ++ struct device_node *child = dev->of_node; ++ int i; ++ const char *s; ++ u32 cs; ++ ++ if (of_property_read_u32(child, "reg", &cs) < 0) { ++ dev_err(dev, "reg not found in DT\n"); ++ return -EINVAL; ++ } ++ ++ info->gpmc_cs = cs; ++ ++ /* detect availability of ELM module. Won't be present pre-OMAP4 */ ++ info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); ++ if (!info->elm_of_node) { ++ info->elm_of_node = of_parse_phandle(child, "elm_id", 0); ++ if (!info->elm_of_node) ++ dev_dbg(dev, "ti,elm-id not in DT\n"); ++ } ++ ++ /* select ecc-scheme for NAND */ ++ if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { ++ dev_err(dev, "ti,nand-ecc-opt not found\n"); ++ return -EINVAL; ++ } ++ ++ if (!strcmp(s, "sw")) { ++ info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; ++ } else if (!strcmp(s, "ham1") || ++ !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { ++ info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; ++ } else if (!strcmp(s, "bch4")) { ++ if (info->elm_of_node) ++ info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; ++ else ++ info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; ++ } else if (!strcmp(s, "bch8")) { ++ if (info->elm_of_node) ++ info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; ++ else ++ info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; ++ } else if (!strcmp(s, "bch16")) { ++ info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; ++ } else { ++ dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); ++ return -EINVAL; ++ } ++ ++ /* select data transfer mode */ ++ if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { ++ for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { ++ if (!strcasecmp(s, nand_xfer_types[i])) { ++ info->xfer_type = i; ++ return 0; ++ } ++ } ++ ++ dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int omap_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ struct nand_chip *chip = &info->nand; ++ int off = BADBLOCK_MARKER_LENGTH; ++ ++ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && ++ !(chip->options & NAND_BUSWIDTH_16)) ++ off = 1; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = off; ++ oobregion->length = chip->ecc.total; ++ ++ return 0; ++} ++ ++static int omap_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ struct nand_chip *chip = &info->nand; ++ int off = BADBLOCK_MARKER_LENGTH; ++ ++ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && ++ !(chip->options & NAND_BUSWIDTH_16)) ++ off = 1; ++ ++ if (section) ++ return -ERANGE; ++ ++ off += chip->ecc.total; ++ if (off >= mtd->oobsize) ++ return -ERANGE; ++ ++ oobregion->offset = off; ++ oobregion->length = mtd->oobsize - off; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops omap_ooblayout_ops = { ++ .ecc = omap_ooblayout_ecc, ++ .free = omap_ooblayout_free, ++}; ++ ++static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int off = BADBLOCK_MARKER_LENGTH; ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ /* ++ * When SW correction is employed, one OMAP specific marker byte is ++ * reserved after each ECC step. ++ */ ++ oobregion->offset = off + (section * (chip->ecc.bytes + 1)); ++ oobregion->length = chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int off = BADBLOCK_MARKER_LENGTH; ++ ++ if (section) ++ return -ERANGE; ++ ++ /* ++ * When SW correction is employed, one OMAP specific marker byte is ++ * reserved after each ECC step. ++ */ ++ off += ((chip->ecc.bytes + 1) * chip->ecc.steps); ++ if (off >= mtd->oobsize) ++ return -ERANGE; ++ ++ oobregion->offset = off; ++ oobregion->length = mtd->oobsize - off; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { ++ .ecc = omap_sw_ooblayout_ecc, ++ .free = omap_sw_ooblayout_free, ++}; ++ ++static int omap_nand_probe(struct platform_device *pdev) ++{ ++ struct omap_nand_info *info; ++ struct omap_nand_platform_data *pdata = NULL; ++ struct mtd_info *mtd; ++ struct nand_chip *nand_chip; ++ int err; ++ dma_cap_mask_t mask; ++ struct resource *res; ++ struct device *dev = &pdev->dev; ++ int min_oobbytes = BADBLOCK_MARKER_LENGTH; ++ int oobbytes_per_step; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), ++ GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ info->pdev = pdev; ++ ++ if (dev->of_node) { ++ if (omap_get_dt_info(dev, info)) ++ return -EINVAL; ++ } else { ++ pdata = dev_get_platdata(&pdev->dev); ++ if (!pdata) { ++ dev_err(&pdev->dev, "platform data missing\n"); ++ return -EINVAL; ++ } ++ ++ info->gpmc_cs = pdata->cs; ++ info->reg = pdata->reg; ++ info->ecc_opt = pdata->ecc_opt; ++ if (pdata->dev_ready) ++ dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n"); ++ ++ info->xfer_type = pdata->xfer_type; ++ info->devsize = pdata->devsize; ++ info->elm_of_node = pdata->elm_of_node; ++ info->flash_bbt = pdata->flash_bbt; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); ++ if (!info->ops) { ++ dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); ++ return -ENODEV; ++ } ++ ++ nand_chip = &info->nand; ++ mtd = nand_to_mtd(nand_chip); ++ mtd->dev.parent = &pdev->dev; ++ nand_chip->ecc.priv = NULL; ++ nand_set_flash_node(nand_chip, dev->of_node); ++ ++ if (!mtd->name) { ++ mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, ++ "omap2-nand.%d", info->gpmc_cs); ++ if (!mtd->name) { ++ dev_err(&pdev->dev, "Failed to set MTD name\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(nand_chip->IO_ADDR_R)) ++ return PTR_ERR(nand_chip->IO_ADDR_R); ++ ++ info->phys_base = res->start; ++ ++ nand_chip->controller = &omap_gpmc_controller; ++ ++ nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; ++ nand_chip->cmd_ctrl = omap_hwcontrol; ++ ++ info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", ++ GPIOD_IN); ++ if (IS_ERR(info->ready_gpiod)) { ++ dev_err(dev, "failed to get ready gpio\n"); ++ return PTR_ERR(info->ready_gpiod); ++ } ++ ++ /* ++ * If RDY/BSY line is connected to OMAP then use the omap ready ++ * function and the generic nand_wait function which reads the status ++ * register after monitoring the RDY/BSY line. Otherwise use a standard ++ * chip delay which is slightly more than tR (AC Timing) of the NAND ++ * device and read status register until you get a failure or success ++ */ ++ if (info->ready_gpiod) { ++ nand_chip->dev_ready = omap_dev_ready; ++ nand_chip->chip_delay = 0; ++ } else { ++ nand_chip->waitfunc = omap_wait; ++ nand_chip->chip_delay = 50; ++ } ++ ++ if (info->flash_bbt) ++ nand_chip->bbt_options |= NAND_BBT_USE_FLASH; ++ ++ /* scan NAND device connected to chip controller */ ++ nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; ++ err = nand_scan_ident(mtd, 1, NULL); ++ if (err) { ++ dev_err(&info->pdev->dev, ++ "scan failed, may be bus-width mismatch\n"); ++ goto return_error; ++ } ++ ++ if (nand_chip->bbt_options & NAND_BBT_USE_FLASH) ++ nand_chip->bbt_options |= NAND_BBT_NO_OOB; ++ else ++ nand_chip->options |= NAND_SKIP_BBTSCAN; ++ ++ /* re-populate low-level callbacks based on xfer modes */ ++ switch (info->xfer_type) { ++ case NAND_OMAP_PREFETCH_POLLED: ++ nand_chip->read_buf = omap_read_buf_pref; ++ nand_chip->write_buf = omap_write_buf_pref; ++ break; ++ ++ case NAND_OMAP_POLLED: ++ /* Use nand_base defaults for {read,write}_buf */ ++ break; ++ ++ case NAND_OMAP_PREFETCH_DMA: ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ info->dma = dma_request_chan(pdev->dev.parent, "rxtx"); ++ ++ if (IS_ERR(info->dma)) { ++ dev_err(&pdev->dev, "DMA engine request failed\n"); ++ err = PTR_ERR(info->dma); ++ goto return_error; ++ } else { ++ struct dma_slave_config cfg; ++ ++ memset(&cfg, 0, sizeof(cfg)); ++ cfg.src_addr = info->phys_base; ++ cfg.dst_addr = info->phys_base; ++ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.src_maxburst = 16; ++ cfg.dst_maxburst = 16; ++ err = dmaengine_slave_config(info->dma, &cfg); ++ if (err) { ++ dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", ++ err); ++ goto return_error; ++ } ++ nand_chip->read_buf = omap_read_buf_dma_pref; ++ nand_chip->write_buf = omap_write_buf_dma_pref; ++ } ++ break; ++ ++ case NAND_OMAP_PREFETCH_IRQ: ++ info->gpmc_irq_fifo = platform_get_irq(pdev, 0); ++ if (info->gpmc_irq_fifo <= 0) { ++ dev_err(&pdev->dev, "error getting fifo irq\n"); ++ err = -ENODEV; ++ goto return_error; ++ } ++ err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, ++ omap_nand_irq, IRQF_SHARED, ++ "gpmc-nand-fifo", info); ++ if (err) { ++ dev_err(&pdev->dev, "requesting irq(%d) error:%d", ++ info->gpmc_irq_fifo, err); ++ info->gpmc_irq_fifo = 0; ++ goto return_error; ++ } ++ ++ info->gpmc_irq_count = platform_get_irq(pdev, 1); ++ if (info->gpmc_irq_count <= 0) { ++ dev_err(&pdev->dev, "error getting count irq\n"); ++ err = -ENODEV; ++ goto return_error; ++ } ++ err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, ++ omap_nand_irq, IRQF_SHARED, ++ "gpmc-nand-count", info); ++ if (err) { ++ dev_err(&pdev->dev, "requesting irq(%d) error:%d", ++ info->gpmc_irq_count, err); ++ info->gpmc_irq_count = 0; ++ goto return_error; ++ } ++ ++ nand_chip->read_buf = omap_read_buf_irq_pref; ++ nand_chip->write_buf = omap_write_buf_irq_pref; ++ ++ break; ++ ++ default: ++ dev_err(&pdev->dev, ++ "xfer_type(%d) not supported!\n", info->xfer_type); ++ err = -EINVAL; ++ goto return_error; ++ } ++ ++ if (!omap2_nand_ecc_check(info, pdata)) { ++ err = -EINVAL; ++ goto return_error; ++ } ++ ++ /* ++ * Bail out earlier to let NAND_ECC_SOFT code create its own ++ * ooblayout instead of using ours. ++ */ ++ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { ++ nand_chip->ecc.mode = NAND_ECC_SOFT; ++ nand_chip->ecc.algo = NAND_ECC_HAMMING; ++ goto scan_tail; ++ } ++ ++ /* populate MTD interface based on ECC scheme */ ++ switch (info->ecc_opt) { ++ case OMAP_ECC_HAM1_CODE_HW: ++ pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.bytes = 3; ++ nand_chip->ecc.size = 512; ++ nand_chip->ecc.strength = 1; ++ nand_chip->ecc.calculate = omap_calculate_ecc; ++ nand_chip->ecc.hwctl = omap_enable_hwecc; ++ nand_chip->ecc.correct = omap_correct_data; ++ mtd_set_ooblayout(mtd, &omap_ooblayout_ops); ++ oobbytes_per_step = nand_chip->ecc.bytes; ++ ++ if (!(nand_chip->options & NAND_BUSWIDTH_16)) ++ min_oobbytes = 1; ++ ++ break; ++ ++ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: ++ pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ nand_chip->ecc.bytes = 7; ++ nand_chip->ecc.strength = 4; ++ nand_chip->ecc.hwctl = omap_enable_hwecc_bch; ++ nand_chip->ecc.correct = nand_bch_correct_data; ++ nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; ++ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); ++ /* Reserve one byte for the OMAP marker */ ++ oobbytes_per_step = nand_chip->ecc.bytes + 1; ++ /* software bch library is used for locating errors */ ++ nand_chip->ecc.priv = nand_bch_init(mtd); ++ if (!nand_chip->ecc.priv) { ++ dev_err(&info->pdev->dev, "unable to use BCH library\n"); ++ err = -EINVAL; ++ goto return_error; ++ } ++ break; ++ ++ case OMAP_ECC_BCH4_CODE_HW: ++ pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ /* 14th bit is kept reserved for ROM-code compatibility */ ++ nand_chip->ecc.bytes = 7 + 1; ++ nand_chip->ecc.strength = 4; ++ nand_chip->ecc.hwctl = omap_enable_hwecc_bch; ++ nand_chip->ecc.correct = omap_elm_correct_data; ++ nand_chip->ecc.read_page = omap_read_page_bch; ++ nand_chip->ecc.write_page = omap_write_page_bch; ++ nand_chip->ecc.write_subpage = omap_write_subpage_bch; ++ mtd_set_ooblayout(mtd, &omap_ooblayout_ops); ++ oobbytes_per_step = nand_chip->ecc.bytes; ++ ++ err = elm_config(info->elm_dev, BCH4_ECC, ++ mtd->writesize / nand_chip->ecc.size, ++ nand_chip->ecc.size, nand_chip->ecc.bytes); ++ if (err < 0) ++ goto return_error; ++ break; ++ ++ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: ++ pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ nand_chip->ecc.bytes = 13; ++ nand_chip->ecc.strength = 8; ++ nand_chip->ecc.hwctl = omap_enable_hwecc_bch; ++ nand_chip->ecc.correct = nand_bch_correct_data; ++ nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; ++ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); ++ /* Reserve one byte for the OMAP marker */ ++ oobbytes_per_step = nand_chip->ecc.bytes + 1; ++ /* software bch library is used for locating errors */ ++ nand_chip->ecc.priv = nand_bch_init(mtd); ++ if (!nand_chip->ecc.priv) { ++ dev_err(&info->pdev->dev, "unable to use BCH library\n"); ++ err = -EINVAL; ++ goto return_error; ++ } ++ break; ++ ++ case OMAP_ECC_BCH8_CODE_HW: ++ pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ /* 14th bit is kept reserved for ROM-code compatibility */ ++ nand_chip->ecc.bytes = 13 + 1; ++ nand_chip->ecc.strength = 8; ++ nand_chip->ecc.hwctl = omap_enable_hwecc_bch; ++ nand_chip->ecc.correct = omap_elm_correct_data; ++ nand_chip->ecc.read_page = omap_read_page_bch; ++ nand_chip->ecc.write_page = omap_write_page_bch; ++ nand_chip->ecc.write_subpage = omap_write_subpage_bch; ++ mtd_set_ooblayout(mtd, &omap_ooblayout_ops); ++ oobbytes_per_step = nand_chip->ecc.bytes; ++ ++ err = elm_config(info->elm_dev, BCH8_ECC, ++ mtd->writesize / nand_chip->ecc.size, ++ nand_chip->ecc.size, nand_chip->ecc.bytes); ++ if (err < 0) ++ goto return_error; ++ ++ break; ++ ++ case OMAP_ECC_BCH16_CODE_HW: ++ pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ nand_chip->ecc.bytes = 26; ++ nand_chip->ecc.strength = 16; ++ nand_chip->ecc.hwctl = omap_enable_hwecc_bch; ++ nand_chip->ecc.correct = omap_elm_correct_data; ++ nand_chip->ecc.read_page = omap_read_page_bch; ++ nand_chip->ecc.write_page = omap_write_page_bch; ++ nand_chip->ecc.write_subpage = omap_write_subpage_bch; ++ mtd_set_ooblayout(mtd, &omap_ooblayout_ops); ++ oobbytes_per_step = nand_chip->ecc.bytes; ++ ++ err = elm_config(info->elm_dev, BCH16_ECC, ++ mtd->writesize / nand_chip->ecc.size, ++ nand_chip->ecc.size, nand_chip->ecc.bytes); ++ if (err < 0) ++ goto return_error; ++ ++ break; ++ default: ++ dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); ++ err = -EINVAL; ++ goto return_error; ++ } ++ ++ /* check if NAND device's OOB is enough to store ECC signatures */ ++ min_oobbytes += (oobbytes_per_step * ++ (mtd->writesize / nand_chip->ecc.size)); ++ if (mtd->oobsize < min_oobbytes) { ++ dev_err(&info->pdev->dev, ++ "not enough OOB bytes required = %d, available=%d\n", ++ min_oobbytes, mtd->oobsize); ++ err = -EINVAL; ++ goto return_error; ++ } ++ ++scan_tail: ++ /* second phase scan */ ++ err = nand_scan_tail(mtd); ++ if (err) ++ goto return_error; ++ ++ if (dev->of_node) ++ mtd_device_register(mtd, NULL, 0); ++ else ++ mtd_device_register(mtd, pdata->parts, pdata->nr_parts); ++ ++ platform_set_drvdata(pdev, mtd); ++ ++ return 0; ++ ++return_error: ++ if (!IS_ERR_OR_NULL(info->dma)) ++ dma_release_channel(info->dma); ++ if (nand_chip->ecc.priv) { ++ nand_bch_free(nand_chip->ecc.priv); ++ nand_chip->ecc.priv = NULL; ++ } ++ return err; ++} ++ ++static int omap_nand_remove(struct platform_device *pdev) ++{ ++ struct mtd_info *mtd = platform_get_drvdata(pdev); ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct omap_nand_info *info = mtd_to_omap(mtd); ++ if (nand_chip->ecc.priv) { ++ nand_bch_free(nand_chip->ecc.priv); ++ nand_chip->ecc.priv = NULL; ++ } ++ if (info->dma) ++ dma_release_channel(info->dma); ++ nand_release(mtd); ++ return 0; ++} ++ ++static const struct of_device_id omap_nand_ids[] = { ++ { .compatible = "ti,omap2-nand", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, omap_nand_ids); ++ ++static struct platform_driver omap_nand_driver = { ++ .probe = omap_nand_probe, ++ .remove = omap_nand_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = of_match_ptr(omap_nand_ids), ++ }, ++}; ++ ++module_platform_driver(omap_nand_driver); ++ ++MODULE_ALIAS("platform:" DRIVER_NAME); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); +diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c +new file mode 100644 +index 00000000..a3f32f9 +--- /dev/null ++++ b/drivers/mtd/nand/raw/omap_elm.c +@@ -0,0 +1,578 @@ ++/* ++ * Error Location Module ++ * ++ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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 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. ++ * ++ */ ++ ++#define DRIVER_NAME "omap-elm" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ELM_SYSCONFIG 0x010 ++#define ELM_IRQSTATUS 0x018 ++#define ELM_IRQENABLE 0x01c ++#define ELM_LOCATION_CONFIG 0x020 ++#define ELM_PAGE_CTRL 0x080 ++#define ELM_SYNDROME_FRAGMENT_0 0x400 ++#define ELM_SYNDROME_FRAGMENT_1 0x404 ++#define ELM_SYNDROME_FRAGMENT_2 0x408 ++#define ELM_SYNDROME_FRAGMENT_3 0x40c ++#define ELM_SYNDROME_FRAGMENT_4 0x410 ++#define ELM_SYNDROME_FRAGMENT_5 0x414 ++#define ELM_SYNDROME_FRAGMENT_6 0x418 ++#define ELM_LOCATION_STATUS 0x800 ++#define ELM_ERROR_LOCATION_0 0x880 ++ ++/* ELM Interrupt Status Register */ ++#define INTR_STATUS_PAGE_VALID BIT(8) ++ ++/* ELM Interrupt Enable Register */ ++#define INTR_EN_PAGE_MASK BIT(8) ++ ++/* ELM Location Configuration Register */ ++#define ECC_BCH_LEVEL_MASK 0x3 ++ ++/* ELM syndrome */ ++#define ELM_SYNDROME_VALID BIT(16) ++ ++/* ELM_LOCATION_STATUS Register */ ++#define ECC_CORRECTABLE_MASK BIT(8) ++#define ECC_NB_ERRORS_MASK 0x1f ++ ++/* ELM_ERROR_LOCATION_0-15 Registers */ ++#define ECC_ERROR_LOCATION_MASK 0x1fff ++ ++#define ELM_ECC_SIZE 0x7ff ++ ++#define SYNDROME_FRAGMENT_REG_SIZE 0x40 ++#define ERROR_LOCATION_SIZE 0x100 ++ ++struct elm_registers { ++ u32 elm_irqenable; ++ u32 elm_sysconfig; ++ u32 elm_location_config; ++ u32 elm_page_ctrl; ++ u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; ++ u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; ++ u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; ++ u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; ++ u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; ++ u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; ++ u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; ++}; ++ ++struct elm_info { ++ struct device *dev; ++ void __iomem *elm_base; ++ struct completion elm_completion; ++ struct list_head list; ++ enum bch_ecc bch_type; ++ struct elm_registers elm_regs; ++ int ecc_steps; ++ int ecc_syndrome_size; ++}; ++ ++static LIST_HEAD(elm_devices); ++ ++static void elm_write_reg(struct elm_info *info, int offset, u32 val) ++{ ++ writel(val, info->elm_base + offset); ++} ++ ++static u32 elm_read_reg(struct elm_info *info, int offset) ++{ ++ return readl(info->elm_base + offset); ++} ++ ++/** ++ * elm_config - Configure ELM module ++ * @dev: ELM device ++ * @bch_type: Type of BCH ecc ++ */ ++int elm_config(struct device *dev, enum bch_ecc bch_type, ++ int ecc_steps, int ecc_step_size, int ecc_syndrome_size) ++{ ++ u32 reg_val; ++ struct elm_info *info = dev_get_drvdata(dev); ++ ++ if (!info) { ++ dev_err(dev, "Unable to configure elm - device not probed?\n"); ++ return -EPROBE_DEFER; ++ } ++ /* ELM cannot detect ECC errors for chunks > 1KB */ ++ if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { ++ dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); ++ return -EINVAL; ++ } ++ /* ELM support 8 error syndrome process */ ++ if (ecc_steps > ERROR_VECTOR_MAX) { ++ dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); ++ return -EINVAL; ++ } ++ ++ reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); ++ elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); ++ info->bch_type = bch_type; ++ info->ecc_steps = ecc_steps; ++ info->ecc_syndrome_size = ecc_syndrome_size; ++ ++ return 0; ++} ++EXPORT_SYMBOL(elm_config); ++ ++/** ++ * elm_configure_page_mode - Enable/Disable page mode ++ * @info: elm info ++ * @index: index number of syndrome fragment vector ++ * @enable: enable/disable flag for page mode ++ * ++ * Enable page mode for syndrome fragment index ++ */ ++static void elm_configure_page_mode(struct elm_info *info, int index, ++ bool enable) ++{ ++ u32 reg_val; ++ ++ reg_val = elm_read_reg(info, ELM_PAGE_CTRL); ++ if (enable) ++ reg_val |= BIT(index); /* enable page mode */ ++ else ++ reg_val &= ~BIT(index); /* disable page mode */ ++ ++ elm_write_reg(info, ELM_PAGE_CTRL, reg_val); ++} ++ ++/** ++ * elm_load_syndrome - Load ELM syndrome reg ++ * @info: elm info ++ * @err_vec: elm error vectors ++ * @ecc: buffer with calculated ecc ++ * ++ * Load syndrome fragment registers with calculated ecc in reverse order. ++ */ ++static void elm_load_syndrome(struct elm_info *info, ++ struct elm_errorvec *err_vec, u8 *ecc) ++{ ++ int i, offset; ++ u32 val; ++ ++ for (i = 0; i < info->ecc_steps; i++) { ++ ++ /* Check error reported */ ++ if (err_vec[i].error_reported) { ++ elm_configure_page_mode(info, i, true); ++ offset = ELM_SYNDROME_FRAGMENT_0 + ++ SYNDROME_FRAGMENT_REG_SIZE * i; ++ switch (info->bch_type) { ++ case BCH8_ECC: ++ /* syndrome fragment 0 = ecc[9-12B] */ ++ val = cpu_to_be32(*(u32 *) &ecc[9]); ++ elm_write_reg(info, offset, val); ++ ++ /* syndrome fragment 1 = ecc[5-8B] */ ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[5]); ++ elm_write_reg(info, offset, val); ++ ++ /* syndrome fragment 2 = ecc[1-4B] */ ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[1]); ++ elm_write_reg(info, offset, val); ++ ++ /* syndrome fragment 3 = ecc[0B] */ ++ offset += 4; ++ val = ecc[0]; ++ elm_write_reg(info, offset, val); ++ break; ++ case BCH4_ECC: ++ /* syndrome fragment 0 = ecc[20-52b] bits */ ++ val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | ++ ((ecc[2] & 0xf) << 28); ++ elm_write_reg(info, offset, val); ++ ++ /* syndrome fragment 1 = ecc[0-20b] bits */ ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; ++ elm_write_reg(info, offset, val); ++ break; ++ case BCH16_ECC: ++ val = cpu_to_be32(*(u32 *) &ecc[22]); ++ elm_write_reg(info, offset, val); ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[18]); ++ elm_write_reg(info, offset, val); ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[14]); ++ elm_write_reg(info, offset, val); ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[10]); ++ elm_write_reg(info, offset, val); ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[6]); ++ elm_write_reg(info, offset, val); ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[2]); ++ elm_write_reg(info, offset, val); ++ offset += 4; ++ val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; ++ elm_write_reg(info, offset, val); ++ break; ++ default: ++ pr_err("invalid config bch_type\n"); ++ } ++ } ++ ++ /* Update ecc pointer with ecc byte size */ ++ ecc += info->ecc_syndrome_size; ++ } ++} ++ ++/** ++ * elm_start_processing - start elm syndrome processing ++ * @info: elm info ++ * @err_vec: elm error vectors ++ * ++ * Set syndrome valid bit for syndrome fragment registers for which ++ * elm syndrome fragment registers are loaded. This enables elm module ++ * to start processing syndrome vectors. ++ */ ++static void elm_start_processing(struct elm_info *info, ++ struct elm_errorvec *err_vec) ++{ ++ int i, offset; ++ u32 reg_val; ++ ++ /* ++ * Set syndrome vector valid, so that ELM module ++ * will process it for vectors error is reported ++ */ ++ for (i = 0; i < info->ecc_steps; i++) { ++ if (err_vec[i].error_reported) { ++ offset = ELM_SYNDROME_FRAGMENT_6 + ++ SYNDROME_FRAGMENT_REG_SIZE * i; ++ reg_val = elm_read_reg(info, offset); ++ reg_val |= ELM_SYNDROME_VALID; ++ elm_write_reg(info, offset, reg_val); ++ } ++ } ++} ++ ++/** ++ * elm_error_correction - locate correctable error position ++ * @info: elm info ++ * @err_vec: elm error vectors ++ * ++ * On completion of processing by elm module, error location status ++ * register updated with correctable/uncorrectable error information. ++ * In case of correctable errors, number of errors located from ++ * elm location status register & read the positions from ++ * elm error location register. ++ */ ++static void elm_error_correction(struct elm_info *info, ++ struct elm_errorvec *err_vec) ++{ ++ int i, j, errors = 0; ++ int offset; ++ u32 reg_val; ++ ++ for (i = 0; i < info->ecc_steps; i++) { ++ ++ /* Check error reported */ ++ if (err_vec[i].error_reported) { ++ offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; ++ reg_val = elm_read_reg(info, offset); ++ ++ /* Check correctable error or not */ ++ if (reg_val & ECC_CORRECTABLE_MASK) { ++ offset = ELM_ERROR_LOCATION_0 + ++ ERROR_LOCATION_SIZE * i; ++ ++ /* Read count of correctable errors */ ++ err_vec[i].error_count = reg_val & ++ ECC_NB_ERRORS_MASK; ++ ++ /* Update the error locations in error vector */ ++ for (j = 0; j < err_vec[i].error_count; j++) { ++ ++ reg_val = elm_read_reg(info, offset); ++ err_vec[i].error_loc[j] = reg_val & ++ ECC_ERROR_LOCATION_MASK; ++ ++ /* Update error location register */ ++ offset += 4; ++ } ++ ++ errors += err_vec[i].error_count; ++ } else { ++ err_vec[i].error_uncorrectable = true; ++ } ++ ++ /* Clearing interrupts for processed error vectors */ ++ elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); ++ ++ /* Disable page mode */ ++ elm_configure_page_mode(info, i, false); ++ } ++ } ++} ++ ++/** ++ * elm_decode_bch_error_page - Locate error position ++ * @dev: device pointer ++ * @ecc_calc: calculated ECC bytes from GPMC ++ * @err_vec: elm error vectors ++ * ++ * Called with one or more error reported vectors & vectors with ++ * error reported is updated in err_vec[].error_reported ++ */ ++void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, ++ struct elm_errorvec *err_vec) ++{ ++ struct elm_info *info = dev_get_drvdata(dev); ++ u32 reg_val; ++ ++ /* Enable page mode interrupt */ ++ reg_val = elm_read_reg(info, ELM_IRQSTATUS); ++ elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); ++ elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); ++ ++ /* Load valid ecc byte to syndrome fragment register */ ++ elm_load_syndrome(info, err_vec, ecc_calc); ++ ++ /* Enable syndrome processing for which syndrome fragment is updated */ ++ elm_start_processing(info, err_vec); ++ ++ /* Wait for ELM module to finish locating error correction */ ++ wait_for_completion(&info->elm_completion); ++ ++ /* Disable page mode interrupt */ ++ reg_val = elm_read_reg(info, ELM_IRQENABLE); ++ elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); ++ elm_error_correction(info, err_vec); ++} ++EXPORT_SYMBOL(elm_decode_bch_error_page); ++ ++static irqreturn_t elm_isr(int this_irq, void *dev_id) ++{ ++ u32 reg_val; ++ struct elm_info *info = dev_id; ++ ++ reg_val = elm_read_reg(info, ELM_IRQSTATUS); ++ ++ /* All error vectors processed */ ++ if (reg_val & INTR_STATUS_PAGE_VALID) { ++ elm_write_reg(info, ELM_IRQSTATUS, ++ reg_val & INTR_STATUS_PAGE_VALID); ++ complete(&info->elm_completion); ++ return IRQ_HANDLED; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static int elm_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct resource *res, *irq; ++ struct elm_info *info; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ info->dev = &pdev->dev; ++ ++ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "no irq resource defined\n"); ++ return -ENODEV; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ info->elm_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(info->elm_base)) ++ return PTR_ERR(info->elm_base); ++ ++ ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, ++ pdev->name, info); ++ if (ret) { ++ dev_err(&pdev->dev, "failure requesting %pr\n", irq); ++ return ret; ++ } ++ ++ pm_runtime_enable(&pdev->dev); ++ if (pm_runtime_get_sync(&pdev->dev) < 0) { ++ ret = -EINVAL; ++ pm_runtime_disable(&pdev->dev); ++ dev_err(&pdev->dev, "can't enable clock\n"); ++ return ret; ++ } ++ ++ init_completion(&info->elm_completion); ++ INIT_LIST_HEAD(&info->list); ++ list_add(&info->list, &elm_devices); ++ platform_set_drvdata(pdev, info); ++ return ret; ++} ++ ++static int elm_remove(struct platform_device *pdev) ++{ ++ pm_runtime_put_sync(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/** ++ * elm_context_save ++ * saves ELM configurations to preserve them across Hardware powered-down ++ */ ++static int elm_context_save(struct elm_info *info) ++{ ++ struct elm_registers *regs = &info->elm_regs; ++ enum bch_ecc bch_type = info->bch_type; ++ u32 offset = 0, i; ++ ++ regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); ++ regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); ++ regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); ++ regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); ++ for (i = 0; i < ERROR_VECTOR_MAX; i++) { ++ offset = i * SYNDROME_FRAGMENT_REG_SIZE; ++ switch (bch_type) { ++ case BCH16_ECC: ++ regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_6 + offset); ++ regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_5 + offset); ++ regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_4 + offset); ++ case BCH8_ECC: ++ regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_3 + offset); ++ regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_2 + offset); ++ case BCH4_ECC: ++ regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_1 + offset); ++ regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_0 + offset); ++ break; ++ default: ++ return -EINVAL; ++ } ++ /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs ++ * to be saved for all BCH schemes*/ ++ regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, ++ ELM_SYNDROME_FRAGMENT_6 + offset); ++ } ++ return 0; ++} ++ ++/** ++ * elm_context_restore ++ * writes configurations saved duing power-down back into ELM registers ++ */ ++static int elm_context_restore(struct elm_info *info) ++{ ++ struct elm_registers *regs = &info->elm_regs; ++ enum bch_ecc bch_type = info->bch_type; ++ u32 offset = 0, i; ++ ++ elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); ++ elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); ++ elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); ++ elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); ++ for (i = 0; i < ERROR_VECTOR_MAX; i++) { ++ offset = i * SYNDROME_FRAGMENT_REG_SIZE; ++ switch (bch_type) { ++ case BCH16_ECC: ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, ++ regs->elm_syndrome_fragment_6[i]); ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, ++ regs->elm_syndrome_fragment_5[i]); ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, ++ regs->elm_syndrome_fragment_4[i]); ++ case BCH8_ECC: ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, ++ regs->elm_syndrome_fragment_3[i]); ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, ++ regs->elm_syndrome_fragment_2[i]); ++ case BCH4_ECC: ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, ++ regs->elm_syndrome_fragment_1[i]); ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, ++ regs->elm_syndrome_fragment_0[i]); ++ break; ++ default: ++ return -EINVAL; ++ } ++ /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ ++ elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, ++ regs->elm_syndrome_fragment_6[i] & ++ ELM_SYNDROME_VALID); ++ } ++ return 0; ++} ++ ++static int elm_suspend(struct device *dev) ++{ ++ struct elm_info *info = dev_get_drvdata(dev); ++ elm_context_save(info); ++ pm_runtime_put_sync(dev); ++ return 0; ++} ++ ++static int elm_resume(struct device *dev) ++{ ++ struct elm_info *info = dev_get_drvdata(dev); ++ pm_runtime_get_sync(dev); ++ elm_context_restore(info); ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id elm_of_match[] = { ++ { .compatible = "ti,am3352-elm" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, elm_of_match); ++#endif ++ ++static struct platform_driver elm_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = of_match_ptr(elm_of_match), ++ .pm = &elm_pm_ops, ++ }, ++ .probe = elm_probe, ++ .remove = elm_remove, ++}; ++ ++module_platform_driver(elm_driver); ++ ++MODULE_DESCRIPTION("ELM driver for BCH error correction"); ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_ALIAS("platform:" DRIVER_NAME); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c +new file mode 100644 +index 00000000..5a5aa1f +--- /dev/null ++++ b/drivers/mtd/nand/raw/orion_nand.c +@@ -0,0 +1,234 @@ ++/* ++ * drivers/mtd/nand/orion_nand.c ++ * ++ * NAND support for Marvell Orion SoC platforms ++ * ++ * Tzachi Perelstein ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct orion_nand_info { ++ struct nand_chip chip; ++ struct clk *clk; ++}; ++ ++static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *nc = mtd_to_nand(mtd); ++ struct orion_nand_data *board = nand_get_controller_data(nc); ++ u32 offs; ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ offs = (1 << board->cle); ++ else if (ctrl & NAND_ALE) ++ offs = (1 << board->ale); ++ else ++ return; ++ ++ if (nc->options & NAND_BUSWIDTH_16) ++ offs <<= 1; ++ ++ writeb(cmd, nc->IO_ADDR_W + offs); ++} ++ ++static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ void __iomem *io_base = chip->IO_ADDR_R; ++#if __LINUX_ARM_ARCH__ >= 5 ++ uint64_t *buf64; ++#endif ++ int i = 0; ++ ++ while (len && (unsigned long)buf & 7) { ++ *buf++ = readb(io_base); ++ len--; ++ } ++#if __LINUX_ARM_ARCH__ >= 5 ++ buf64 = (uint64_t *)buf; ++ while (i < len/8) { ++ /* ++ * Since GCC has no proper constraint (PR 43518) ++ * force x variable to r2/r3 registers as ldrd instruction ++ * requires first register to be even. ++ */ ++ register uint64_t x asm ("r2"); ++ ++ asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base)); ++ buf64[i++] = x; ++ } ++ i *= 8; ++#else ++ readsl(io_base, buf, len/4); ++ i = len / 4 * 4; ++#endif ++ while (i < len) ++ buf[i++] = readb(io_base); ++} ++ ++static int __init orion_nand_probe(struct platform_device *pdev) ++{ ++ struct orion_nand_info *info; ++ struct mtd_info *mtd; ++ struct nand_chip *nc; ++ struct orion_nand_data *board; ++ struct resource *res; ++ void __iomem *io_base; ++ int ret = 0; ++ u32 val = 0; ++ ++ info = devm_kzalloc(&pdev->dev, ++ sizeof(struct orion_nand_info), ++ GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ nc = &info->chip; ++ mtd = nand_to_mtd(nc); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ io_base = devm_ioremap_resource(&pdev->dev, res); ++ ++ if (IS_ERR(io_base)) ++ return PTR_ERR(io_base); ++ ++ if (pdev->dev.of_node) { ++ board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data), ++ GFP_KERNEL); ++ if (!board) ++ return -ENOMEM; ++ if (!of_property_read_u32(pdev->dev.of_node, "cle", &val)) ++ board->cle = (u8)val; ++ else ++ board->cle = 0; ++ if (!of_property_read_u32(pdev->dev.of_node, "ale", &val)) ++ board->ale = (u8)val; ++ else ++ board->ale = 1; ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "bank-width", &val)) ++ board->width = (u8)val * 8; ++ else ++ board->width = 8; ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "chip-delay", &val)) ++ board->chip_delay = (u8)val; ++ } else { ++ board = dev_get_platdata(&pdev->dev); ++ } ++ ++ mtd->dev.parent = &pdev->dev; ++ ++ nand_set_controller_data(nc, board); ++ nand_set_flash_node(nc, pdev->dev.of_node); ++ nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; ++ nc->cmd_ctrl = orion_nand_cmd_ctrl; ++ nc->read_buf = orion_nand_read_buf; ++ nc->ecc.mode = NAND_ECC_SOFT; ++ nc->ecc.algo = NAND_ECC_HAMMING; ++ ++ if (board->chip_delay) ++ nc->chip_delay = board->chip_delay; ++ ++ WARN(board->width > 16, ++ "%d bit bus width out of range", ++ board->width); ++ ++ if (board->width == 16) ++ nc->options |= NAND_BUSWIDTH_16; ++ ++ if (board->dev_ready) ++ nc->dev_ready = board->dev_ready; ++ ++ platform_set_drvdata(pdev, info); ++ ++ /* Not all platforms can gate the clock, so it is not ++ an error if the clock does not exists. */ ++ info->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(info->clk)) { ++ ret = PTR_ERR(info->clk); ++ if (ret == -ENOENT) { ++ info->clk = NULL; ++ } else { ++ dev_err(&pdev->dev, "failed to get clock!\n"); ++ return ret; ++ } ++ } ++ ++ ret = clk_prepare_enable(info->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to prepare clock!\n"); ++ return ret; ++ } ++ ++ ret = nand_scan(mtd, 1); ++ if (ret) ++ goto no_dev; ++ ++ mtd->name = "orion_nand"; ++ ret = mtd_device_register(mtd, board->parts, board->nr_parts); ++ if (ret) { ++ nand_release(mtd); ++ goto no_dev; ++ } ++ ++ return 0; ++ ++no_dev: ++ clk_disable_unprepare(info->clk); ++ return ret; ++} ++ ++static int orion_nand_remove(struct platform_device *pdev) ++{ ++ struct orion_nand_info *info = platform_get_drvdata(pdev); ++ struct nand_chip *chip = &info->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ nand_release(mtd); ++ ++ clk_disable_unprepare(info->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id orion_nand_of_match_table[] = { ++ { .compatible = "marvell,orion-nand", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, orion_nand_of_match_table); ++#endif ++ ++static struct platform_driver orion_nand_driver = { ++ .remove = orion_nand_remove, ++ .driver = { ++ .name = "orion_nand", ++ .of_match_table = of_match_ptr(orion_nand_of_match_table), ++ }, ++}; ++ ++module_platform_driver_probe(orion_nand_driver, orion_nand_probe); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Tzachi Perelstein"); ++MODULE_DESCRIPTION("NAND glue for Orion platforms"); ++MODULE_ALIAS("platform:orion_nand"); +diff --git a/drivers/mtd/nand/raw/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c +new file mode 100644 +index 00000000..01b00bb +--- /dev/null ++++ b/drivers/mtd/nand/raw/oxnas_nand.c +@@ -0,0 +1,206 @@ ++/* ++ * Oxford Semiconductor OXNAS NAND driver ++ ++ * Copyright (C) 2016 Neil Armstrong ++ * Heavily based on plat_nand.c : ++ * Author: Vitaly Wool ++ * Copyright (C) 2013 Ma Haijun ++ * Copyright (C) 2012 John Crispin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Nand commands */ ++#define OXNAS_NAND_CMD_ALE BIT(18) ++#define OXNAS_NAND_CMD_CLE BIT(19) ++ ++#define OXNAS_NAND_MAX_CHIPS 1 ++ ++struct oxnas_nand_ctrl { ++ struct nand_controller base; ++ void __iomem *io_base; ++ struct clk *clk; ++ struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; ++}; ++ ++static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); ++ ++ return readb(oxnas->io_base); ++} ++ ++static void oxnas_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); ++ ++ ioread8_rep(oxnas->io_base, buf, len); ++} ++ ++static void oxnas_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); ++ ++ iowrite8_rep(oxnas->io_base, buf, len); ++} ++ ++/* Single CS command control */ ++static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); ++ ++ if (ctrl & NAND_CLE) ++ writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE); ++ else if (ctrl & NAND_ALE) ++ writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE); ++} ++ ++/* ++ * Probe for the NAND device. ++ */ ++static int oxnas_nand_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *nand_np; ++ struct oxnas_nand_ctrl *oxnas; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct resource *res; ++ int nchips = 0; ++ int count = 0; ++ int err = 0; ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ oxnas = devm_kzalloc(&pdev->dev, sizeof(*oxnas), ++ GFP_KERNEL); ++ if (!oxnas) ++ return -ENOMEM; ++ ++ nand_controller_init(&oxnas->base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(oxnas->io_base)) ++ return PTR_ERR(oxnas->io_base); ++ ++ oxnas->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(oxnas->clk)) ++ oxnas->clk = NULL; ++ ++ /* Only a single chip node is supported */ ++ count = of_get_child_count(np); ++ if (count > 1) ++ return -EINVAL; ++ ++ err = clk_prepare_enable(oxnas->clk); ++ if (err) ++ return err; ++ ++ device_reset_optional(&pdev->dev); ++ ++ for_each_child_of_node(np, nand_np) { ++ chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), ++ GFP_KERNEL); ++ if (!chip) { ++ err = -ENOMEM; ++ goto err_clk_unprepare; ++ } ++ ++ chip->controller = &oxnas->base; ++ ++ nand_set_flash_node(chip, nand_np); ++ nand_set_controller_data(chip, oxnas); ++ ++ mtd = nand_to_mtd(chip); ++ mtd->dev.parent = &pdev->dev; ++ mtd->priv = chip; ++ ++ chip->cmd_ctrl = oxnas_nand_cmd_ctrl; ++ chip->read_buf = oxnas_nand_read_buf; ++ chip->read_byte = oxnas_nand_read_byte; ++ chip->write_buf = oxnas_nand_write_buf; ++ chip->chip_delay = 30; ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(mtd, 1); ++ if (err) ++ goto err_clk_unprepare; ++ ++ err = mtd_device_register(mtd, NULL, 0); ++ if (err) { ++ nand_release(mtd); ++ goto err_clk_unprepare; ++ } ++ ++ oxnas->chips[nchips] = chip; ++ ++nchips; ++ } ++ ++ /* Exit if no chips found */ ++ if (!nchips) { ++ err = -ENODEV; ++ goto err_clk_unprepare; ++ } ++ ++ platform_set_drvdata(pdev, oxnas); ++ ++ return 0; ++ ++err_clk_unprepare: ++ clk_disable_unprepare(oxnas->clk); ++ return err; ++} ++ ++static int oxnas_nand_remove(struct platform_device *pdev) ++{ ++ struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev); ++ ++ if (oxnas->chips[0]) ++ nand_release(nand_to_mtd(oxnas->chips[0])); ++ ++ clk_disable_unprepare(oxnas->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id oxnas_nand_match[] = { ++ { .compatible = "oxsemi,ox820-nand" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, oxnas_nand_match); ++ ++static struct platform_driver oxnas_nand_driver = { ++ .probe = oxnas_nand_probe, ++ .remove = oxnas_nand_remove, ++ .driver = { ++ .name = "oxnas_nand", ++ .of_match_table = oxnas_nand_match, ++ }, ++}; ++ ++module_platform_driver(oxnas_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Neil Armstrong "); ++MODULE_DESCRIPTION("Oxnas NAND driver"); ++MODULE_ALIAS("platform:oxnas_nand"); +diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c +new file mode 100644 +index 00000000..a47a7e4 +--- /dev/null ++++ b/drivers/mtd/nand/raw/pasemi_nand.c +@@ -0,0 +1,232 @@ ++/* ++ * Copyright (C) 2006-2007 PA Semi, Inc ++ * ++ * Author: Egor Martovetsky ++ * Maintained by: Olof Johansson ++ * ++ * Driver for the PWRficient onchip NAND flash interface ++ * ++ * 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. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#undef DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define LBICTRL_LPCCTL_NR 0x00004000 ++#define CLE_PIN_CTL 15 ++#define ALE_PIN_CTL 14 ++ ++static unsigned int lpcctl; ++static struct mtd_info *pasemi_nand_mtd; ++static const char driver_name[] = "pasemi-nand"; ++ ++static void pasemi_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ while (len > 0x800) { ++ memcpy_fromio(buf, chip->IO_ADDR_R, 0x800); ++ buf += 0x800; ++ len -= 0x800; ++ } ++ memcpy_fromio(buf, chip->IO_ADDR_R, len); ++} ++ ++static void pasemi_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ while (len > 0x800) { ++ memcpy_toio(chip->IO_ADDR_R, buf, 0x800); ++ buf += 0x800; ++ len -= 0x800; ++ } ++ memcpy_toio(chip->IO_ADDR_R, buf, len); ++} ++ ++static void pasemi_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ out_8(chip->IO_ADDR_W + (1 << CLE_PIN_CTL), cmd); ++ else ++ out_8(chip->IO_ADDR_W + (1 << ALE_PIN_CTL), cmd); ++ ++ /* Push out posted writes */ ++ eieio(); ++ inl(lpcctl); ++} ++ ++int pasemi_device_ready(struct mtd_info *mtd) ++{ ++ return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); ++} ++ ++static int pasemi_nand_probe(struct platform_device *ofdev) ++{ ++ struct device *dev = &ofdev->dev; ++ struct pci_dev *pdev; ++ struct device_node *np = dev->of_node; ++ struct resource res; ++ struct nand_chip *chip; ++ int err = 0; ++ ++ err = of_address_to_resource(np, 0, &res); ++ ++ if (err) ++ return -EINVAL; ++ ++ /* We only support one device at the moment */ ++ if (pasemi_nand_mtd) ++ return -ENODEV; ++ ++ dev_dbg(dev, "pasemi_nand at %pR\n", &res); ++ ++ /* Allocate memory for MTD device structure and private data */ ++ chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ++ if (!chip) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ pasemi_nand_mtd = nand_to_mtd(chip); ++ ++ /* Link the private data with the MTD structure */ ++ pasemi_nand_mtd->dev.parent = dev; ++ ++ chip->IO_ADDR_R = of_iomap(np, 0); ++ chip->IO_ADDR_W = chip->IO_ADDR_R; ++ ++ if (!chip->IO_ADDR_R) { ++ err = -EIO; ++ goto out_mtd; ++ } ++ ++ pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa008, NULL); ++ if (!pdev) { ++ err = -ENODEV; ++ goto out_ior; ++ } ++ ++ lpcctl = pci_resource_start(pdev, 0); ++ pci_dev_put(pdev); ++ ++ if (!request_region(lpcctl, 4, driver_name)) { ++ err = -EBUSY; ++ goto out_ior; ++ } ++ ++ chip->cmd_ctrl = pasemi_hwcontrol; ++ chip->dev_ready = pasemi_device_ready; ++ chip->read_buf = pasemi_read_buf; ++ chip->write_buf = pasemi_write_buf; ++ chip->chip_delay = 0; ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ ++ /* Enable the following for a flash based bad block table */ ++ chip->bbt_options = NAND_BBT_USE_FLASH; ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(pasemi_nand_mtd, 1); ++ if (err) ++ goto out_lpc; ++ ++ if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { ++ dev_err(dev, "Unable to register MTD device\n"); ++ err = -ENODEV; ++ goto out_lpc; ++ } ++ ++ dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res, ++ lpcctl); ++ ++ return 0; ++ ++ out_lpc: ++ release_region(lpcctl, 4); ++ out_ior: ++ iounmap(chip->IO_ADDR_R); ++ out_mtd: ++ kfree(chip); ++ out: ++ return err; ++} ++ ++static int pasemi_nand_remove(struct platform_device *ofdev) ++{ ++ struct nand_chip *chip; ++ ++ if (!pasemi_nand_mtd) ++ return 0; ++ ++ chip = mtd_to_nand(pasemi_nand_mtd); ++ ++ /* Release resources, unregister device */ ++ nand_release(pasemi_nand_mtd); ++ ++ release_region(lpcctl, 4); ++ ++ iounmap(chip->IO_ADDR_R); ++ ++ /* Free the MTD device structure */ ++ kfree(chip); ++ ++ pasemi_nand_mtd = NULL; ++ ++ return 0; ++} ++ ++static const struct of_device_id pasemi_nand_match[] = ++{ ++ { ++ .compatible = "pasemi,localbus-nand", ++ }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, pasemi_nand_match); ++ ++static struct platform_driver pasemi_nand_driver = ++{ ++ .driver = { ++ .name = driver_name, ++ .of_match_table = pasemi_nand_match, ++ }, ++ .probe = pasemi_nand_probe, ++ .remove = pasemi_nand_remove, ++}; ++ ++module_platform_driver(pasemi_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Egor Martovetsky "); ++MODULE_DESCRIPTION("NAND flash interface driver for PA Semi PWRficient"); +diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c +new file mode 100644 +index 00000000..925a132 +--- /dev/null ++++ b/drivers/mtd/nand/raw/plat_nand.c +@@ -0,0 +1,144 @@ ++/* ++ * Generic NAND driver ++ * ++ * Author: Vitaly Wool ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct plat_nand_data { ++ struct nand_chip chip; ++ void __iomem *io_base; ++}; ++ ++/* ++ * Probe for the NAND device. ++ */ ++static int plat_nand_probe(struct platform_device *pdev) ++{ ++ struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); ++ struct plat_nand_data *data; ++ struct mtd_info *mtd; ++ struct resource *res; ++ const char **part_types; ++ int err = 0; ++ ++ if (!pdata) { ++ dev_err(&pdev->dev, "platform_nand_data is missing\n"); ++ return -EINVAL; ++ } ++ ++ if (pdata->chip.nr_chips < 1) { ++ dev_err(&pdev->dev, "invalid number of chips specified\n"); ++ return -EINVAL; ++ } ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), ++ GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ data->io_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(data->io_base)) ++ return PTR_ERR(data->io_base); ++ ++ nand_set_flash_node(&data->chip, pdev->dev.of_node); ++ mtd = nand_to_mtd(&data->chip); ++ mtd->dev.parent = &pdev->dev; ++ ++ data->chip.IO_ADDR_R = data->io_base; ++ data->chip.IO_ADDR_W = data->io_base; ++ data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; ++ data->chip.dev_ready = pdata->ctrl.dev_ready; ++ data->chip.select_chip = pdata->ctrl.select_chip; ++ data->chip.write_buf = pdata->ctrl.write_buf; ++ data->chip.read_buf = pdata->ctrl.read_buf; ++ data->chip.read_byte = pdata->ctrl.read_byte; ++ data->chip.chip_delay = pdata->chip.chip_delay; ++ data->chip.options |= pdata->chip.options; ++ data->chip.bbt_options |= pdata->chip.bbt_options; ++ ++ data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; ++ data->chip.ecc.mode = NAND_ECC_SOFT; ++ data->chip.ecc.algo = NAND_ECC_HAMMING; ++ ++ platform_set_drvdata(pdev, data); ++ ++ /* Handle any platform specific setup */ ++ if (pdata->ctrl.probe) { ++ err = pdata->ctrl.probe(pdev); ++ if (err) ++ goto out; ++ } ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(mtd, pdata->chip.nr_chips); ++ if (err) ++ goto out; ++ ++ part_types = pdata->chip.part_probe_types; ++ ++ err = mtd_device_parse_register(mtd, part_types, NULL, ++ pdata->chip.partitions, ++ pdata->chip.nr_partitions); ++ ++ if (!err) ++ return err; ++ ++ nand_release(mtd); ++out: ++ if (pdata->ctrl.remove) ++ pdata->ctrl.remove(pdev); ++ return err; ++} ++ ++/* ++ * Remove a NAND device. ++ */ ++static int plat_nand_remove(struct platform_device *pdev) ++{ ++ struct plat_nand_data *data = platform_get_drvdata(pdev); ++ struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); ++ ++ nand_release(nand_to_mtd(&data->chip)); ++ if (pdata->ctrl.remove) ++ pdata->ctrl.remove(pdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id plat_nand_match[] = { ++ { .compatible = "gen_nand" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, plat_nand_match); ++ ++static struct platform_driver plat_nand_driver = { ++ .probe = plat_nand_probe, ++ .remove = plat_nand_remove, ++ .driver = { ++ .name = "gen_nand", ++ .of_match_table = plat_nand_match, ++ }, ++}; ++ ++module_platform_driver(plat_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Vitaly Wool"); ++MODULE_DESCRIPTION("Simple generic NAND driver"); ++MODULE_ALIAS("platform:gen_nand"); +diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c +new file mode 100644 +index 00000000..d1979c7 +--- /dev/null ++++ b/drivers/mtd/nand/raw/pxa3xx_nand.c +@@ -0,0 +1,2105 @@ ++/* ++ * drivers/mtd/nand/pxa3xx_nand.c ++ * ++ * Copyright © 2005 Intel Corporation ++ * Copyright © 2006 Marvell International Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * See Documentation/mtd/nand/pxa3xx-nand.txt for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) ++#define NAND_STOP_DELAY msecs_to_jiffies(40) ++#define PAGE_CHUNK_SIZE (2048) ++ ++/* ++ * Define a buffer size for the initial command that detects the flash device: ++ * STATUS, READID and PARAM. ++ * ONFI param page is 256 bytes, and there are three redundant copies ++ * to be read. JEDEC param page is 512 bytes, and there are also three ++ * redundant copies to be read. ++ * Hence this buffer should be at least 512 x 3. Let's pick 2048. ++ */ ++#define INIT_BUFFER_SIZE 2048 ++ ++/* System control register and bit to enable NAND on some SoCs */ ++#define GENCONF_SOC_DEVICE_MUX 0x208 ++#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) ++ ++/* registers and bit definitions */ ++#define NDCR (0x00) /* Control register */ ++#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ ++#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ ++#define NDSR (0x14) /* Status Register */ ++#define NDPCR (0x18) /* Page Count Register */ ++#define NDBDR0 (0x1C) /* Bad Block Register 0 */ ++#define NDBDR1 (0x20) /* Bad Block Register 1 */ ++#define NDECCCTRL (0x28) /* ECC control */ ++#define NDDB (0x40) /* Data Buffer */ ++#define NDCB0 (0x48) /* Command Buffer0 */ ++#define NDCB1 (0x4C) /* Command Buffer1 */ ++#define NDCB2 (0x50) /* Command Buffer2 */ ++ ++#define NDCR_SPARE_EN (0x1 << 31) ++#define NDCR_ECC_EN (0x1 << 30) ++#define NDCR_DMA_EN (0x1 << 29) ++#define NDCR_ND_RUN (0x1 << 28) ++#define NDCR_DWIDTH_C (0x1 << 27) ++#define NDCR_DWIDTH_M (0x1 << 26) ++#define NDCR_PAGE_SZ (0x1 << 24) ++#define NDCR_NCSX (0x1 << 23) ++#define NDCR_ND_MODE (0x3 << 21) ++#define NDCR_NAND_MODE (0x0) ++#define NDCR_CLR_PG_CNT (0x1 << 20) ++#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) ++#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19) ++#define NDCR_RD_ID_CNT_MASK (0x7 << 16) ++#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) ++ ++#define NDCR_RA_START (0x1 << 15) ++#define NDCR_PG_PER_BLK (0x1 << 14) ++#define NDCR_ND_ARB_EN (0x1 << 12) ++#define NDCR_INT_MASK (0xFFF) ++ ++#define NDSR_MASK (0xfff) ++#define NDSR_ERR_CNT_OFF (16) ++#define NDSR_ERR_CNT_MASK (0x1f) ++#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) ++#define NDSR_RDY (0x1 << 12) ++#define NDSR_FLASH_RDY (0x1 << 11) ++#define NDSR_CS0_PAGED (0x1 << 10) ++#define NDSR_CS1_PAGED (0x1 << 9) ++#define NDSR_CS0_CMDD (0x1 << 8) ++#define NDSR_CS1_CMDD (0x1 << 7) ++#define NDSR_CS0_BBD (0x1 << 6) ++#define NDSR_CS1_BBD (0x1 << 5) ++#define NDSR_UNCORERR (0x1 << 4) ++#define NDSR_CORERR (0x1 << 3) ++#define NDSR_WRDREQ (0x1 << 2) ++#define NDSR_RDDREQ (0x1 << 1) ++#define NDSR_WRCMDREQ (0x1) ++ ++#define NDCB0_LEN_OVRD (0x1 << 28) ++#define NDCB0_ST_ROW_EN (0x1 << 26) ++#define NDCB0_AUTO_RS (0x1 << 25) ++#define NDCB0_CSEL (0x1 << 24) ++#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) ++#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) ++#define NDCB0_CMD_TYPE_MASK (0x7 << 21) ++#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) ++#define NDCB0_NC (0x1 << 20) ++#define NDCB0_DBC (0x1 << 19) ++#define NDCB0_ADDR_CYC_MASK (0x7 << 16) ++#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) ++#define NDCB0_CMD2_MASK (0xff << 8) ++#define NDCB0_CMD1_MASK (0xff) ++#define NDCB0_ADDR_CYC_SHIFT (16) ++ ++#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ ++#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ ++#define EXT_CMD_TYPE_READ 4 /* Read */ ++#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ ++#define EXT_CMD_TYPE_FINAL 3 /* Final command */ ++#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ ++#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ ++ ++/* ++ * This should be large enough to read 'ONFI' and 'JEDEC'. ++ * Let's use 7 bytes, which is the maximum ID count supported ++ * by the controller (see NDCR_RD_ID_CNT_MASK). ++ */ ++#define READ_ID_BYTES 7 ++ ++/* macros for registers read/write */ ++#define nand_writel(info, off, val) \ ++ do { \ ++ dev_vdbg(&info->pdev->dev, \ ++ "%s():%d nand_writel(0x%x, 0x%04x)\n", \ ++ __func__, __LINE__, (val), (off)); \ ++ writel_relaxed((val), (info)->mmio_base + (off)); \ ++ } while (0) ++ ++#define nand_readl(info, off) \ ++ ({ \ ++ unsigned int _v; \ ++ _v = readl_relaxed((info)->mmio_base + (off)); \ ++ dev_vdbg(&info->pdev->dev, \ ++ "%s():%d nand_readl(0x%04x) = 0x%x\n", \ ++ __func__, __LINE__, (off), _v); \ ++ _v; \ ++ }) ++ ++/* error code and state */ ++enum { ++ ERR_NONE = 0, ++ ERR_DMABUSERR = -1, ++ ERR_SENDCMD = -2, ++ ERR_UNCORERR = -3, ++ ERR_BBERR = -4, ++ ERR_CORERR = -5, ++}; ++ ++enum { ++ STATE_IDLE = 0, ++ STATE_PREPARED, ++ STATE_CMD_HANDLE, ++ STATE_DMA_READING, ++ STATE_DMA_WRITING, ++ STATE_DMA_DONE, ++ STATE_PIO_READING, ++ STATE_PIO_WRITING, ++ STATE_CMD_DONE, ++ STATE_READY, ++}; ++ ++enum pxa3xx_nand_variant { ++ PXA3XX_NAND_VARIANT_PXA, ++ PXA3XX_NAND_VARIANT_ARMADA370, ++ PXA3XX_NAND_VARIANT_ARMADA_8K, ++}; ++ ++struct pxa3xx_nand_host { ++ struct nand_chip chip; ++ void *info_data; ++ ++ /* page size of attached chip */ ++ int use_ecc; ++ int cs; ++ ++ /* calculated from pxa3xx_nand_flash data */ ++ unsigned int col_addr_cycles; ++ unsigned int row_addr_cycles; ++}; ++ ++struct pxa3xx_nand_info { ++ struct nand_hw_control controller; ++ struct platform_device *pdev; ++ ++ struct clk *clk; ++ void __iomem *mmio_base; ++ unsigned long mmio_phys; ++ struct completion cmd_complete, dev_ready; ++ ++ unsigned int buf_start; ++ unsigned int buf_count; ++ unsigned int buf_size; ++ unsigned int data_buff_pos; ++ unsigned int oob_buff_pos; ++ ++ /* DMA information */ ++ struct scatterlist sg; ++ enum dma_data_direction dma_dir; ++ struct dma_chan *dma_chan; ++ dma_cookie_t dma_cookie; ++ int drcmr_dat; ++ ++ unsigned char *data_buff; ++ unsigned char *oob_buff; ++ dma_addr_t data_buff_phys; ++ int data_dma_ch; ++ ++ struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; ++ unsigned int state; ++ ++ /* ++ * This driver supports NFCv1 (as found in PXA SoC) ++ * and NFCv2 (as found in Armada 370/XP SoC). ++ */ ++ enum pxa3xx_nand_variant variant; ++ ++ int cs; ++ int use_ecc; /* use HW ECC ? */ ++ int ecc_bch; /* using BCH ECC? */ ++ int use_dma; /* use DMA ? */ ++ int use_spare; /* use spare ? */ ++ int need_wait; ++ ++ /* Amount of real data per full chunk */ ++ unsigned int chunk_size; ++ ++ /* Amount of spare data per full chunk */ ++ unsigned int spare_size; ++ ++ /* Number of full chunks (i.e chunk_size + spare_size) */ ++ unsigned int nfullchunks; ++ ++ /* ++ * Total number of chunks. If equal to nfullchunks, then there ++ * are only full chunks. Otherwise, there is one last chunk of ++ * size (last_chunk_size + last_spare_size) ++ */ ++ unsigned int ntotalchunks; ++ ++ /* Amount of real data in the last chunk */ ++ unsigned int last_chunk_size; ++ ++ /* Amount of spare data in the last chunk */ ++ unsigned int last_spare_size; ++ ++ unsigned int ecc_size; ++ unsigned int ecc_err_cnt; ++ unsigned int max_bitflips; ++ int retcode; ++ ++ /* ++ * Variables only valid during command ++ * execution. step_chunk_size and step_spare_size is the ++ * amount of real data and spare data in the current ++ * chunk. cur_chunk is the current chunk being ++ * read/programmed. ++ */ ++ unsigned int step_chunk_size; ++ unsigned int step_spare_size; ++ unsigned int cur_chunk; ++ ++ /* cached register value */ ++ uint32_t reg_ndcr; ++ uint32_t ndtr0cs0; ++ uint32_t ndtr1cs0; ++ ++ /* generated NDCBx register values */ ++ uint32_t ndcb0; ++ uint32_t ndcb1; ++ uint32_t ndcb2; ++ uint32_t ndcb3; ++}; ++ ++static bool use_dma = 1; ++module_param(use_dma, bool, 0444); ++MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); ++ ++struct pxa3xx_nand_timing { ++ unsigned int tCH; /* Enable signal hold time */ ++ unsigned int tCS; /* Enable signal setup time */ ++ unsigned int tWH; /* ND_nWE high duration */ ++ unsigned int tWP; /* ND_nWE pulse time */ ++ unsigned int tRH; /* ND_nRE high duration */ ++ unsigned int tRP; /* ND_nRE pulse width */ ++ unsigned int tR; /* ND_nWE high to ND_nRE low for read */ ++ unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ ++ unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ ++}; ++ ++struct pxa3xx_nand_flash { ++ uint32_t chip_id; ++ unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ ++ unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ ++ struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ ++}; ++ ++static struct pxa3xx_nand_timing timing[] = { ++ { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, ++ { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, ++ { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, ++ { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, ++}; ++ ++static struct pxa3xx_nand_flash builtin_flash_types[] = { ++ { 0x46ec, 16, 16, &timing[1] }, ++ { 0xdaec, 8, 8, &timing[1] }, ++ { 0xd7ec, 8, 8, &timing[1] }, ++ { 0xa12c, 8, 8, &timing[2] }, ++ { 0xb12c, 16, 16, &timing[2] }, ++ { 0xdc2c, 8, 8, &timing[2] }, ++ { 0xcc2c, 16, 16, &timing[2] }, ++ { 0xba20, 16, 16, &timing[3] }, ++}; ++ ++static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ int nchunks = mtd->writesize / info->chunk_size; ++ ++ if (section >= nchunks) ++ return -ERANGE; ++ ++ oobregion->offset = ((info->ecc_size + info->spare_size) * section) + ++ info->spare_size; ++ oobregion->length = info->ecc_size; ++ ++ return 0; ++} ++ ++static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ int nchunks = mtd->writesize / info->chunk_size; ++ ++ if (section >= nchunks) ++ return -ERANGE; ++ ++ if (!info->spare_size) ++ return 0; ++ ++ oobregion->offset = section * (info->ecc_size + info->spare_size); ++ oobregion->length = info->spare_size; ++ if (!section) { ++ /* ++ * Bootrom looks in bytes 0 & 5 for bad blocks for the ++ * 4KB page / 4bit BCH combination. ++ */ ++ if (mtd->writesize == 4096 && info->chunk_size == 2048) { ++ oobregion->offset += 6; ++ oobregion->length -= 6; ++ } else { ++ oobregion->offset += 2; ++ oobregion->length -= 2; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = { ++ .ecc = pxa3xx_ooblayout_ecc, ++ .free = pxa3xx_ooblayout_free, ++}; ++ ++static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; ++static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; ++ ++static struct nand_bbt_descr bbt_main_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 8, ++ .len = 6, ++ .veroffs = 14, ++ .maxblocks = 8, /* Last 8 blocks in each chip */ ++ .pattern = bbt_pattern ++}; ++ ++static struct nand_bbt_descr bbt_mirror_descr = { ++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE ++ | NAND_BBT_2BIT | NAND_BBT_VERSION, ++ .offs = 8, ++ .len = 6, ++ .veroffs = 14, ++ .maxblocks = 8, /* Last 8 blocks in each chip */ ++ .pattern = bbt_mirror_pattern ++}; ++ ++#define NDTR0_tCH(c) (min((c), 7) << 19) ++#define NDTR0_tCS(c) (min((c), 7) << 16) ++#define NDTR0_tWH(c) (min((c), 7) << 11) ++#define NDTR0_tWP(c) (min((c), 7) << 8) ++#define NDTR0_tRH(c) (min((c), 7) << 3) ++#define NDTR0_tRP(c) (min((c), 7) << 0) ++ ++#define NDTR1_tR(c) (min((c), 65535) << 16) ++#define NDTR1_tWHR(c) (min((c), 15) << 4) ++#define NDTR1_tAR(c) (min((c), 15) << 0) ++ ++/* convert nano-seconds to nand flash controller clock cycles */ ++#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) ++ ++static const struct of_device_id pxa3xx_nand_dt_ids[] = { ++ { ++ .compatible = "marvell,pxa3xx-nand", ++ .data = (void *)PXA3XX_NAND_VARIANT_PXA, ++ }, ++ { ++ .compatible = "marvell,armada370-nand", ++ .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, ++ }, ++ { ++ .compatible = "marvell,armada-8k-nand", ++ .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); ++ ++static enum pxa3xx_nand_variant ++pxa3xx_nand_get_variant(struct platform_device *pdev) ++{ ++ const struct of_device_id *of_id = ++ of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); ++ if (!of_id) ++ return PXA3XX_NAND_VARIANT_PXA; ++ return (enum pxa3xx_nand_variant)of_id->data; ++} ++ ++static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, ++ const struct pxa3xx_nand_timing *t) ++{ ++ struct pxa3xx_nand_info *info = host->info_data; ++ unsigned long nand_clk = clk_get_rate(info->clk); ++ uint32_t ndtr0, ndtr1; ++ ++ ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | ++ NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | ++ NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | ++ NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | ++ NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | ++ NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); ++ ++ ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | ++ NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | ++ NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); ++ ++ info->ndtr0cs0 = ndtr0; ++ info->ndtr1cs0 = ndtr1; ++ nand_writel(info, NDTR0CS0, ndtr0); ++ nand_writel(info, NDTR1CS0, ndtr1); ++} ++ ++static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, ++ const struct nand_sdr_timings *t) ++{ ++ struct pxa3xx_nand_info *info = host->info_data; ++ struct nand_chip *chip = &host->chip; ++ unsigned long nand_clk = clk_get_rate(info->clk); ++ uint32_t ndtr0, ndtr1; ++ ++ u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); ++ u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); ++ u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); ++ u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); ++ u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); ++ u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); ++ u32 tR = chip->chip_delay * 1000; ++ u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); ++ u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); ++ ++ /* fallback to a default value if tR = 0 */ ++ if (!tR) ++ tR = 20000; ++ ++ ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | ++ NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | ++ NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | ++ NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | ++ NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | ++ NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); ++ ++ ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | ++ NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | ++ NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); ++ ++ info->ndtr0cs0 = ndtr0; ++ info->ndtr1cs0 = ndtr1; ++ nand_writel(info, NDTR0CS0, ndtr0); ++ nand_writel(info, NDTR1CS0, ndtr1); ++} ++ ++static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host, ++ unsigned int *flash_width, ++ unsigned int *dfc_width) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct pxa3xx_nand_info *info = host->info_data; ++ const struct pxa3xx_nand_flash *f = NULL; ++ int i, id, ntypes; ++ u8 idbuf[2]; ++ ++ ntypes = ARRAY_SIZE(builtin_flash_types); ++ ++ nand_readid_op(chip, 0, idbuf, sizeof(idbuf)); ++ id = idbuf[0] | (idbuf[1] << 8); ++ ++ for (i = 0; i < ntypes; i++) { ++ f = &builtin_flash_types[i]; ++ ++ if (f->chip_id == id) ++ break; ++ } ++ ++ if (i == ntypes) { ++ dev_err(&info->pdev->dev, "Error: timings not found\n"); ++ return -EINVAL; ++ } ++ ++ pxa3xx_nand_set_timing(host, f->timing); ++ ++ *flash_width = f->flash_width; ++ *dfc_width = f->dfc_width; ++ ++ return 0; ++} ++ ++static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host, ++ int mode) ++{ ++ const struct nand_sdr_timings *timings; ++ ++ mode = fls(mode) - 1; ++ if (mode < 0) ++ mode = 0; ++ ++ timings = onfi_async_timing_mode_to_sdr_timings(mode); ++ if (IS_ERR(timings)) ++ return PTR_ERR(timings); ++ ++ pxa3xx_nand_set_sdr_timing(host, timings); ++ ++ return 0; ++} ++ ++static int pxa3xx_nand_init(struct pxa3xx_nand_host *host) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct pxa3xx_nand_info *info = host->info_data; ++ unsigned int flash_width = 0, dfc_width = 0; ++ int mode, err; ++ ++ mode = onfi_get_async_timing_mode(chip); ++ if (mode == ONFI_TIMING_MODE_UNKNOWN) { ++ err = pxa3xx_nand_init_timings_compat(host, &flash_width, ++ &dfc_width); ++ if (err) ++ return err; ++ ++ if (flash_width == 16) { ++ info->reg_ndcr |= NDCR_DWIDTH_M; ++ chip->options |= NAND_BUSWIDTH_16; ++ } ++ ++ info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0; ++ } else { ++ err = pxa3xx_nand_init_timings_onfi(host, mode); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * NOTE: it is a must to set ND_RUN firstly, then write ++ * command buffer, otherwise, it does not work. ++ * We enable all the interrupt at the same time, and ++ * let pxa3xx_nand_irq to handle all logic. ++ */ ++static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) ++{ ++ uint32_t ndcr; ++ ++ ndcr = info->reg_ndcr; ++ ++ if (info->use_ecc) { ++ ndcr |= NDCR_ECC_EN; ++ if (info->ecc_bch) ++ nand_writel(info, NDECCCTRL, 0x1); ++ } else { ++ ndcr &= ~NDCR_ECC_EN; ++ if (info->ecc_bch) ++ nand_writel(info, NDECCCTRL, 0x0); ++ } ++ ++ if (info->use_dma) ++ ndcr |= NDCR_DMA_EN; ++ else ++ ndcr &= ~NDCR_DMA_EN; ++ ++ if (info->use_spare) ++ ndcr |= NDCR_SPARE_EN; ++ else ++ ndcr &= ~NDCR_SPARE_EN; ++ ++ ndcr |= NDCR_ND_RUN; ++ ++ /* clear status bits and run */ ++ nand_writel(info, NDSR, NDSR_MASK); ++ nand_writel(info, NDCR, 0); ++ nand_writel(info, NDCR, ndcr); ++} ++ ++static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) ++{ ++ uint32_t ndcr; ++ int timeout = NAND_STOP_DELAY; ++ ++ /* wait RUN bit in NDCR become 0 */ ++ ndcr = nand_readl(info, NDCR); ++ while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) { ++ ndcr = nand_readl(info, NDCR); ++ udelay(1); ++ } ++ ++ if (timeout <= 0) { ++ ndcr &= ~NDCR_ND_RUN; ++ nand_writel(info, NDCR, ndcr); ++ } ++ if (info->dma_chan) ++ dmaengine_terminate_all(info->dma_chan); ++ ++ /* clear status bits */ ++ nand_writel(info, NDSR, NDSR_MASK); ++} ++ ++static void __maybe_unused ++enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) ++{ ++ uint32_t ndcr; ++ ++ ndcr = nand_readl(info, NDCR); ++ nand_writel(info, NDCR, ndcr & ~int_mask); ++} ++ ++static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) ++{ ++ uint32_t ndcr; ++ ++ ndcr = nand_readl(info, NDCR); ++ nand_writel(info, NDCR, ndcr | int_mask); ++} ++ ++static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) ++{ ++ if (info->ecc_bch) { ++ u32 val; ++ int ret; ++ ++ /* ++ * According to the datasheet, when reading from NDDB ++ * with BCH enabled, after each 32 bytes reads, we ++ * have to make sure that the NDSR.RDDREQ bit is set. ++ * ++ * Drain the FIFO 8 32 bits reads at a time, and skip ++ * the polling on the last read. ++ */ ++ while (len > 8) { ++ ioread32_rep(info->mmio_base + NDDB, data, 8); ++ ++ ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val, ++ val & NDSR_RDDREQ, 1000, 5000); ++ if (ret) { ++ dev_err(&info->pdev->dev, ++ "Timeout on RDDREQ while draining the FIFO\n"); ++ return; ++ } ++ ++ data += 32; ++ len -= 8; ++ } ++ } ++ ++ ioread32_rep(info->mmio_base + NDDB, data, len); ++} ++ ++static void handle_data_pio(struct pxa3xx_nand_info *info) ++{ ++ switch (info->state) { ++ case STATE_PIO_WRITING: ++ if (info->step_chunk_size) ++ writesl(info->mmio_base + NDDB, ++ info->data_buff + info->data_buff_pos, ++ DIV_ROUND_UP(info->step_chunk_size, 4)); ++ ++ if (info->step_spare_size) ++ writesl(info->mmio_base + NDDB, ++ info->oob_buff + info->oob_buff_pos, ++ DIV_ROUND_UP(info->step_spare_size, 4)); ++ break; ++ case STATE_PIO_READING: ++ if (info->step_chunk_size) ++ drain_fifo(info, ++ info->data_buff + info->data_buff_pos, ++ DIV_ROUND_UP(info->step_chunk_size, 4)); ++ ++ if (info->step_spare_size) ++ drain_fifo(info, ++ info->oob_buff + info->oob_buff_pos, ++ DIV_ROUND_UP(info->step_spare_size, 4)); ++ break; ++ default: ++ dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, ++ info->state); ++ BUG(); ++ } ++ ++ /* Update buffer pointers for multi-page read/write */ ++ info->data_buff_pos += info->step_chunk_size; ++ info->oob_buff_pos += info->step_spare_size; ++} ++ ++static void pxa3xx_nand_data_dma_irq(void *data) ++{ ++ struct pxa3xx_nand_info *info = data; ++ struct dma_tx_state state; ++ enum dma_status status; ++ ++ status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state); ++ if (likely(status == DMA_COMPLETE)) { ++ info->state = STATE_DMA_DONE; ++ } else { ++ dev_err(&info->pdev->dev, "DMA error on data channel\n"); ++ info->retcode = ERR_DMABUSERR; ++ } ++ dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); ++ ++ nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); ++ enable_int(info, NDCR_INT_MASK); ++} ++ ++static void start_data_dma(struct pxa3xx_nand_info *info) ++{ ++ enum dma_transfer_direction direction; ++ struct dma_async_tx_descriptor *tx; ++ ++ switch (info->state) { ++ case STATE_DMA_WRITING: ++ info->dma_dir = DMA_TO_DEVICE; ++ direction = DMA_MEM_TO_DEV; ++ break; ++ case STATE_DMA_READING: ++ info->dma_dir = DMA_FROM_DEVICE; ++ direction = DMA_DEV_TO_MEM; ++ break; ++ default: ++ dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, ++ info->state); ++ BUG(); ++ } ++ info->sg.length = info->chunk_size; ++ if (info->use_spare) ++ info->sg.length += info->spare_size + info->ecc_size; ++ dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); ++ ++ tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction, ++ DMA_PREP_INTERRUPT); ++ if (!tx) { ++ dev_err(&info->pdev->dev, "prep_slave_sg() failed\n"); ++ return; ++ } ++ tx->callback = pxa3xx_nand_data_dma_irq; ++ tx->callback_param = info; ++ info->dma_cookie = dmaengine_submit(tx); ++ dma_async_issue_pending(info->dma_chan); ++ dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n", ++ __func__, direction, info->dma_cookie, info->sg.length); ++} ++ ++static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data) ++{ ++ struct pxa3xx_nand_info *info = data; ++ ++ handle_data_pio(info); ++ ++ info->state = STATE_CMD_DONE; ++ nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) ++{ ++ struct pxa3xx_nand_info *info = devid; ++ unsigned int status, is_completed = 0, is_ready = 0; ++ unsigned int ready, cmd_done; ++ irqreturn_t ret = IRQ_HANDLED; ++ ++ if (info->cs == 0) { ++ ready = NDSR_FLASH_RDY; ++ cmd_done = NDSR_CS0_CMDD; ++ } else { ++ ready = NDSR_RDY; ++ cmd_done = NDSR_CS1_CMDD; ++ } ++ ++ status = nand_readl(info, NDSR); ++ ++ if (status & NDSR_UNCORERR) ++ info->retcode = ERR_UNCORERR; ++ if (status & NDSR_CORERR) { ++ info->retcode = ERR_CORERR; ++ if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) && ++ info->ecc_bch) ++ info->ecc_err_cnt = NDSR_ERR_CNT(status); ++ else ++ info->ecc_err_cnt = 1; ++ ++ /* ++ * Each chunk composing a page is corrected independently, ++ * and we need to store maximum number of corrected bitflips ++ * to return it to the MTD layer in ecc.read_page(). ++ */ ++ info->max_bitflips = max_t(unsigned int, ++ info->max_bitflips, ++ info->ecc_err_cnt); ++ } ++ if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { ++ /* whether use dma to transfer data */ ++ if (info->use_dma) { ++ disable_int(info, NDCR_INT_MASK); ++ info->state = (status & NDSR_RDDREQ) ? ++ STATE_DMA_READING : STATE_DMA_WRITING; ++ start_data_dma(info); ++ goto NORMAL_IRQ_EXIT; ++ } else { ++ info->state = (status & NDSR_RDDREQ) ? ++ STATE_PIO_READING : STATE_PIO_WRITING; ++ ret = IRQ_WAKE_THREAD; ++ goto NORMAL_IRQ_EXIT; ++ } ++ } ++ if (status & cmd_done) { ++ info->state = STATE_CMD_DONE; ++ is_completed = 1; ++ } ++ if (status & ready) { ++ info->state = STATE_READY; ++ is_ready = 1; ++ } ++ ++ /* ++ * Clear all status bit before issuing the next command, which ++ * can and will alter the status bits and will deserve a new ++ * interrupt on its own. This lets the controller exit the IRQ ++ */ ++ nand_writel(info, NDSR, status); ++ ++ if (status & NDSR_WRCMDREQ) { ++ status &= ~NDSR_WRCMDREQ; ++ info->state = STATE_CMD_HANDLE; ++ ++ /* ++ * Command buffer registers NDCB{0-2} (and optionally NDCB3) ++ * must be loaded by writing directly either 12 or 16 ++ * bytes directly to NDCB0, four bytes at a time. ++ * ++ * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored ++ * but each NDCBx register can be read. ++ */ ++ nand_writel(info, NDCB0, info->ndcb0); ++ nand_writel(info, NDCB0, info->ndcb1); ++ nand_writel(info, NDCB0, info->ndcb2); ++ ++ /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) ++ nand_writel(info, NDCB0, info->ndcb3); ++ } ++ ++ if (is_completed) ++ complete(&info->cmd_complete); ++ if (is_ready) ++ complete(&info->dev_ready); ++NORMAL_IRQ_EXIT: ++ return ret; ++} ++ ++static inline int is_buf_blank(uint8_t *buf, size_t len) ++{ ++ for (; len > 0; len--) ++ if (*buf++ != 0xff) ++ return 0; ++ return 1; ++} ++ ++static void set_command_address(struct pxa3xx_nand_info *info, ++ unsigned int page_size, uint16_t column, int page_addr) ++{ ++ /* small page addr setting */ ++ if (page_size < PAGE_CHUNK_SIZE) { ++ info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) ++ | (column & 0xFF); ++ ++ info->ndcb2 = 0; ++ } else { ++ info->ndcb1 = ((page_addr & 0xFFFF) << 16) ++ | (column & 0xFFFF); ++ ++ if (page_addr & 0xFF0000) ++ info->ndcb2 = (page_addr & 0xFF0000) >> 16; ++ else ++ info->ndcb2 = 0; ++ } ++} ++ ++static void prepare_start_command(struct pxa3xx_nand_info *info, int command) ++{ ++ struct pxa3xx_nand_host *host = info->host[info->cs]; ++ struct mtd_info *mtd = nand_to_mtd(&host->chip); ++ ++ /* reset data and oob column point to handle data */ ++ info->buf_start = 0; ++ info->buf_count = 0; ++ info->data_buff_pos = 0; ++ info->oob_buff_pos = 0; ++ info->step_chunk_size = 0; ++ info->step_spare_size = 0; ++ info->cur_chunk = 0; ++ info->use_ecc = 0; ++ info->use_spare = 1; ++ info->retcode = ERR_NONE; ++ info->ecc_err_cnt = 0; ++ info->ndcb3 = 0; ++ info->need_wait = 0; ++ ++ switch (command) { ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ case NAND_CMD_PAGEPROG: ++ info->use_ecc = 1; ++ break; ++ case NAND_CMD_PARAM: ++ info->use_spare = 0; ++ break; ++ default: ++ info->ndcb1 = 0; ++ info->ndcb2 = 0; ++ break; ++ } ++ ++ /* ++ * If we are about to issue a read command, or about to set ++ * the write address, then clean the data buffer. ++ */ ++ if (command == NAND_CMD_READ0 || ++ command == NAND_CMD_READOOB || ++ command == NAND_CMD_SEQIN) { ++ ++ info->buf_count = mtd->writesize + mtd->oobsize; ++ memset(info->data_buff, 0xFF, info->buf_count); ++ } ++ ++} ++ ++static int prepare_set_command(struct pxa3xx_nand_info *info, int command, ++ int ext_cmd_type, uint16_t column, int page_addr) ++{ ++ int addr_cycle, exec_cmd; ++ struct pxa3xx_nand_host *host; ++ struct mtd_info *mtd; ++ ++ host = info->host[info->cs]; ++ mtd = nand_to_mtd(&host->chip); ++ addr_cycle = 0; ++ exec_cmd = 1; ++ ++ if (info->cs != 0) ++ info->ndcb0 = NDCB0_CSEL; ++ else ++ info->ndcb0 = 0; ++ ++ if (command == NAND_CMD_SEQIN) ++ exec_cmd = 0; ++ ++ addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles ++ + host->col_addr_cycles); ++ ++ switch (command) { ++ case NAND_CMD_READOOB: ++ case NAND_CMD_READ0: ++ info->buf_start = column; ++ info->ndcb0 |= NDCB0_CMD_TYPE(0) ++ | addr_cycle ++ | NAND_CMD_READ0; ++ ++ if (command == NAND_CMD_READOOB) ++ info->buf_start += mtd->writesize; ++ ++ if (info->cur_chunk < info->nfullchunks) { ++ info->step_chunk_size = info->chunk_size; ++ info->step_spare_size = info->spare_size; ++ } else { ++ info->step_chunk_size = info->last_chunk_size; ++ info->step_spare_size = info->last_spare_size; ++ } ++ ++ /* ++ * Multiple page read needs an 'extended command type' field, ++ * which is either naked-read or last-read according to the ++ * state. ++ */ ++ if (mtd->writesize == PAGE_CHUNK_SIZE) { ++ info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); ++ } else if (mtd->writesize > PAGE_CHUNK_SIZE) { ++ info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) ++ | NDCB0_LEN_OVRD ++ | NDCB0_EXT_CMD_TYPE(ext_cmd_type); ++ info->ndcb3 = info->step_chunk_size + ++ info->step_spare_size; ++ } ++ ++ set_command_address(info, mtd->writesize, column, page_addr); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ ++ info->buf_start = column; ++ set_command_address(info, mtd->writesize, 0, page_addr); ++ ++ /* ++ * Multiple page programming needs to execute the initial ++ * SEQIN command that sets the page address. ++ */ ++ if (mtd->writesize > PAGE_CHUNK_SIZE) { ++ info->ndcb0 |= NDCB0_CMD_TYPE(0x1) ++ | NDCB0_EXT_CMD_TYPE(ext_cmd_type) ++ | addr_cycle ++ | command; ++ exec_cmd = 1; ++ } ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ if (is_buf_blank(info->data_buff, ++ (mtd->writesize + mtd->oobsize))) { ++ exec_cmd = 0; ++ break; ++ } ++ ++ if (info->cur_chunk < info->nfullchunks) { ++ info->step_chunk_size = info->chunk_size; ++ info->step_spare_size = info->spare_size; ++ } else { ++ info->step_chunk_size = info->last_chunk_size; ++ info->step_spare_size = info->last_spare_size; ++ } ++ ++ /* Second command setting for large pages */ ++ if (mtd->writesize > PAGE_CHUNK_SIZE) { ++ /* ++ * Multiple page write uses the 'extended command' ++ * field. This can be used to issue a command dispatch ++ * or a naked-write depending on the current stage. ++ */ ++ info->ndcb0 |= NDCB0_CMD_TYPE(0x1) ++ | NDCB0_LEN_OVRD ++ | NDCB0_EXT_CMD_TYPE(ext_cmd_type); ++ info->ndcb3 = info->step_chunk_size + ++ info->step_spare_size; ++ ++ /* ++ * This is the command dispatch that completes a chunked ++ * page program operation. ++ */ ++ if (info->cur_chunk == info->ntotalchunks) { ++ info->ndcb0 = NDCB0_CMD_TYPE(0x1) ++ | NDCB0_EXT_CMD_TYPE(ext_cmd_type) ++ | command; ++ info->ndcb1 = 0; ++ info->ndcb2 = 0; ++ info->ndcb3 = 0; ++ } ++ } else { ++ info->ndcb0 |= NDCB0_CMD_TYPE(0x1) ++ | NDCB0_AUTO_RS ++ | NDCB0_ST_ROW_EN ++ | NDCB0_DBC ++ | (NAND_CMD_PAGEPROG << 8) ++ | NAND_CMD_SEQIN ++ | addr_cycle; ++ } ++ break; ++ ++ case NAND_CMD_PARAM: ++ info->buf_count = INIT_BUFFER_SIZE; ++ info->ndcb0 |= NDCB0_CMD_TYPE(0) ++ | NDCB0_ADDR_CYC(1) ++ | NDCB0_LEN_OVRD ++ | command; ++ info->ndcb1 = (column & 0xFF); ++ info->ndcb3 = INIT_BUFFER_SIZE; ++ info->step_chunk_size = INIT_BUFFER_SIZE; ++ break; ++ ++ case NAND_CMD_READID: ++ info->buf_count = READ_ID_BYTES; ++ info->ndcb0 |= NDCB0_CMD_TYPE(3) ++ | NDCB0_ADDR_CYC(1) ++ | command; ++ info->ndcb1 = (column & 0xFF); ++ ++ info->step_chunk_size = 8; ++ break; ++ case NAND_CMD_STATUS: ++ info->buf_count = 1; ++ info->ndcb0 |= NDCB0_CMD_TYPE(4) ++ | NDCB0_ADDR_CYC(1) ++ | command; ++ ++ info->step_chunk_size = 8; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ info->ndcb0 |= NDCB0_CMD_TYPE(2) ++ | NDCB0_AUTO_RS ++ | NDCB0_ADDR_CYC(3) ++ | NDCB0_DBC ++ | (NAND_CMD_ERASE2 << 8) ++ | NAND_CMD_ERASE1; ++ info->ndcb1 = page_addr; ++ info->ndcb2 = 0; ++ ++ break; ++ case NAND_CMD_RESET: ++ info->ndcb0 |= NDCB0_CMD_TYPE(5) ++ | command; ++ ++ break; ++ ++ case NAND_CMD_ERASE2: ++ exec_cmd = 0; ++ break; ++ ++ default: ++ exec_cmd = 0; ++ dev_err(&info->pdev->dev, "non-supported command %x\n", ++ command); ++ break; ++ } ++ ++ return exec_cmd; ++} ++ ++static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ int exec_cmd; ++ ++ /* ++ * if this is a x16 device ,then convert the input ++ * "byte" address into a "word" address appropriate ++ * for indexing a word-oriented device ++ */ ++ if (info->reg_ndcr & NDCR_DWIDTH_M) ++ column /= 2; ++ ++ /* ++ * There may be different NAND chip hooked to ++ * different chip select, so check whether ++ * chip select has been changed, if yes, reset the timing ++ */ ++ if (info->cs != host->cs) { ++ info->cs = host->cs; ++ nand_writel(info, NDTR0CS0, info->ndtr0cs0); ++ nand_writel(info, NDTR1CS0, info->ndtr1cs0); ++ } ++ ++ prepare_start_command(info, command); ++ ++ info->state = STATE_PREPARED; ++ exec_cmd = prepare_set_command(info, command, 0, column, page_addr); ++ ++ if (exec_cmd) { ++ init_completion(&info->cmd_complete); ++ init_completion(&info->dev_ready); ++ info->need_wait = 1; ++ pxa3xx_nand_start(info); ++ ++ if (!wait_for_completion_timeout(&info->cmd_complete, ++ CHIP_DELAY_TIMEOUT)) { ++ dev_err(&info->pdev->dev, "Wait time out!!!\n"); ++ /* Stop State Machine for next command cycle */ ++ pxa3xx_nand_stop(info); ++ } ++ } ++ info->state = STATE_IDLE; ++} ++ ++static void nand_cmdfunc_extended(struct mtd_info *mtd, ++ const unsigned command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ int exec_cmd, ext_cmd_type; ++ ++ /* ++ * if this is a x16 device then convert the input ++ * "byte" address into a "word" address appropriate ++ * for indexing a word-oriented device ++ */ ++ if (info->reg_ndcr & NDCR_DWIDTH_M) ++ column /= 2; ++ ++ /* ++ * There may be different NAND chip hooked to ++ * different chip select, so check whether ++ * chip select has been changed, if yes, reset the timing ++ */ ++ if (info->cs != host->cs) { ++ info->cs = host->cs; ++ nand_writel(info, NDTR0CS0, info->ndtr0cs0); ++ nand_writel(info, NDTR1CS0, info->ndtr1cs0); ++ } ++ ++ /* Select the extended command for the first command */ ++ switch (command) { ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ ext_cmd_type = EXT_CMD_TYPE_MONO; ++ break; ++ case NAND_CMD_SEQIN: ++ ext_cmd_type = EXT_CMD_TYPE_DISPATCH; ++ break; ++ case NAND_CMD_PAGEPROG: ++ ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; ++ break; ++ default: ++ ext_cmd_type = 0; ++ break; ++ } ++ ++ prepare_start_command(info, command); ++ ++ /* ++ * Prepare the "is ready" completion before starting a command ++ * transaction sequence. If the command is not executed the ++ * completion will be completed, see below. ++ * ++ * We can do that inside the loop because the command variable ++ * is invariant and thus so is the exec_cmd. ++ */ ++ info->need_wait = 1; ++ init_completion(&info->dev_ready); ++ do { ++ info->state = STATE_PREPARED; ++ ++ exec_cmd = prepare_set_command(info, command, ext_cmd_type, ++ column, page_addr); ++ if (!exec_cmd) { ++ info->need_wait = 0; ++ complete(&info->dev_ready); ++ break; ++ } ++ ++ init_completion(&info->cmd_complete); ++ pxa3xx_nand_start(info); ++ ++ if (!wait_for_completion_timeout(&info->cmd_complete, ++ CHIP_DELAY_TIMEOUT)) { ++ dev_err(&info->pdev->dev, "Wait time out!!!\n"); ++ /* Stop State Machine for next command cycle */ ++ pxa3xx_nand_stop(info); ++ break; ++ } ++ ++ /* Only a few commands need several steps */ ++ if (command != NAND_CMD_PAGEPROG && ++ command != NAND_CMD_READ0 && ++ command != NAND_CMD_READOOB) ++ break; ++ ++ info->cur_chunk++; ++ ++ /* Check if the sequence is complete */ ++ if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG) ++ break; ++ ++ /* ++ * After a splitted program command sequence has issued ++ * the command dispatch, the command sequence is complete. ++ */ ++ if (info->cur_chunk == (info->ntotalchunks + 1) && ++ command == NAND_CMD_PAGEPROG && ++ ext_cmd_type == EXT_CMD_TYPE_DISPATCH) ++ break; ++ ++ if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { ++ /* Last read: issue a 'last naked read' */ ++ if (info->cur_chunk == info->ntotalchunks - 1) ++ ext_cmd_type = EXT_CMD_TYPE_LAST_RW; ++ else ++ ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; ++ ++ /* ++ * If a splitted program command has no more data to transfer, ++ * the command dispatch must be issued to complete. ++ */ ++ } else if (command == NAND_CMD_PAGEPROG && ++ info->cur_chunk == info->ntotalchunks) { ++ ext_cmd_type = EXT_CMD_TYPE_DISPATCH; ++ } ++ } while (1); ++ ++ info->state = STATE_IDLE; ++} ++ ++static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, int oob_required, ++ int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ ++ nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ if (info->retcode == ERR_CORERR && info->use_ecc) { ++ mtd->ecc_stats.corrected += info->ecc_err_cnt; ++ ++ } else if (info->retcode == ERR_UNCORERR) { ++ /* ++ * for blank page (all 0xff), HW will calculate its ECC as ++ * 0, which is different from the ECC information within ++ * OOB, ignore such uncorrectable errors ++ */ ++ if (is_buf_blank(buf, mtd->writesize)) ++ info->retcode = ERR_NONE; ++ else ++ mtd->ecc_stats.failed++; ++ } ++ ++ return info->max_bitflips; ++} ++ ++static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ char retval = 0xFF; ++ ++ if (info->buf_start < info->buf_count) ++ /* Has just send a new command? */ ++ retval = info->data_buff[info->buf_start++]; ++ ++ return retval; ++} ++ ++static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ u16 retval = 0xFFFF; ++ ++ if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { ++ retval = *((u16 *)(info->data_buff+info->buf_start)); ++ info->buf_start += 2; ++ } ++ return retval; ++} ++ ++static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ int real_len = min_t(size_t, len, info->buf_count - info->buf_start); ++ ++ memcpy(buf, info->data_buff + info->buf_start, real_len); ++ info->buf_start += real_len; ++} ++ ++static void pxa3xx_nand_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ int real_len = min_t(size_t, len, info->buf_count - info->buf_start); ++ ++ memcpy(info->data_buff + info->buf_start, buf, real_len); ++ info->buf_start += real_len; ++} ++ ++static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ return; ++} ++ ++static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ ++ if (info->need_wait) { ++ info->need_wait = 0; ++ if (!wait_for_completion_timeout(&info->dev_ready, ++ CHIP_DELAY_TIMEOUT)) { ++ dev_err(&info->pdev->dev, "Ready time out!!!\n"); ++ return NAND_STATUS_FAIL; ++ } ++ } ++ ++ /* pxa3xx_nand_send_command has waited for command complete */ ++ if (this->state == FL_WRITING || this->state == FL_ERASING) { ++ if (info->retcode == ERR_NONE) ++ return 0; ++ else ++ return NAND_STATUS_FAIL; ++ } ++ ++ return NAND_STATUS_READY; ++} ++ ++static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) ++{ ++ struct pxa3xx_nand_host *host = info->host[info->cs]; ++ struct platform_device *pdev = info->pdev; ++ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ const struct nand_sdr_timings *timings; ++ ++ /* Configure default flash values */ ++ info->chunk_size = PAGE_CHUNK_SIZE; ++ info->reg_ndcr = 0x0; /* enable all interrupts */ ++ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; ++ info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); ++ info->reg_ndcr |= NDCR_SPARE_EN; ++ ++ /* use the common timing to make a try */ ++ timings = onfi_async_timing_mode_to_sdr_timings(0); ++ if (IS_ERR(timings)) ++ return PTR_ERR(timings); ++ ++ pxa3xx_nand_set_sdr_timing(host, timings); ++ return 0; ++} ++ ++static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) ++{ ++ struct pxa3xx_nand_host *host = info->host[info->cs]; ++ struct nand_chip *chip = &host->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; ++ info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; ++ info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; ++} ++ ++static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) ++{ ++ struct platform_device *pdev = info->pdev; ++ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ uint32_t ndcr = nand_readl(info, NDCR); ++ ++ /* Set an initial chunk size */ ++ info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; ++ info->reg_ndcr = ndcr & ++ ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); ++ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; ++ info->ndtr0cs0 = nand_readl(info, NDTR0CS0); ++ info->ndtr1cs0 = nand_readl(info, NDTR1CS0); ++} ++ ++static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) ++{ ++ struct platform_device *pdev = info->pdev; ++ struct dma_slave_config config; ++ dma_cap_mask_t mask; ++ struct pxad_param param; ++ int ret; ++ ++ info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); ++ if (info->data_buff == NULL) ++ return -ENOMEM; ++ if (use_dma == 0) ++ return 0; ++ ++ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ++ if (ret) ++ return ret; ++ ++ sg_init_one(&info->sg, info->data_buff, info->buf_size); ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ param.prio = PXAD_PRIO_LOWEST; ++ param.drcmr = info->drcmr_dat; ++ info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn, ++ ¶m, &pdev->dev, ++ "data"); ++ if (!info->dma_chan) { ++ dev_err(&pdev->dev, "unable to request data dma channel\n"); ++ return -ENODEV; ++ } ++ ++ memset(&config, 0, sizeof(config)); ++ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.src_addr = info->mmio_phys + NDDB; ++ config.dst_addr = info->mmio_phys + NDDB; ++ config.src_maxburst = 32; ++ config.dst_maxburst = 32; ++ ret = dmaengine_slave_config(info->dma_chan, &config); ++ if (ret < 0) { ++ dev_err(&info->pdev->dev, ++ "dma channel configuration failed: %d\n", ++ ret); ++ return ret; ++ } ++ ++ /* ++ * Now that DMA buffers are allocated we turn on ++ * DMA proper for I/O operations. ++ */ ++ info->use_dma = 1; ++ return 0; ++} ++ ++static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) ++{ ++ if (info->use_dma) { ++ dmaengine_terminate_all(info->dma_chan); ++ dma_release_channel(info->dma_chan); ++ } ++ kfree(info->data_buff); ++} ++ ++static int pxa_ecc_init(struct pxa3xx_nand_info *info, ++ struct mtd_info *mtd, ++ int strength, int ecc_stepsize, int page_size) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { ++ info->nfullchunks = 1; ++ info->ntotalchunks = 1; ++ info->chunk_size = 2048; ++ info->spare_size = 40; ++ info->ecc_size = 24; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = 512; ++ ecc->strength = 1; ++ ++ } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { ++ info->nfullchunks = 1; ++ info->ntotalchunks = 1; ++ info->chunk_size = 512; ++ info->spare_size = 8; ++ info->ecc_size = 8; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = 512; ++ ecc->strength = 1; ++ ++ /* ++ * Required ECC: 4-bit correction per 512 bytes ++ * Select: 16-bit correction per 2048 bytes ++ */ ++ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { ++ info->ecc_bch = 1; ++ info->nfullchunks = 1; ++ info->ntotalchunks = 1; ++ info->chunk_size = 2048; ++ info->spare_size = 32; ++ info->ecc_size = 32; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = info->chunk_size; ++ mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ++ ecc->strength = 16; ++ ++ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { ++ info->ecc_bch = 1; ++ info->nfullchunks = 2; ++ info->ntotalchunks = 2; ++ info->chunk_size = 2048; ++ info->spare_size = 32; ++ info->ecc_size = 32; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = info->chunk_size; ++ mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ++ ecc->strength = 16; ++ ++ /* ++ * Required ECC: 8-bit correction per 512 bytes ++ * Select: 16-bit correction per 1024 bytes ++ */ ++ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { ++ info->ecc_bch = 1; ++ info->nfullchunks = 4; ++ info->ntotalchunks = 5; ++ info->chunk_size = 1024; ++ info->spare_size = 0; ++ info->last_chunk_size = 0; ++ info->last_spare_size = 64; ++ info->ecc_size = 32; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = info->chunk_size; ++ mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ++ ecc->strength = 16; ++ } else { ++ dev_err(&info->pdev->dev, ++ "ECC strength %d at page size %d is not supported\n", ++ strength, page_size); ++ return -ENODEV; ++ } ++ ++ dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n", ++ ecc->strength, ecc->size); ++ return 0; ++} ++ ++static int pxa3xx_nand_scan(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct pxa3xx_nand_host *host = nand_get_controller_data(chip); ++ struct pxa3xx_nand_info *info = host->info_data; ++ struct platform_device *pdev = info->pdev; ++ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ int ret; ++ uint16_t ecc_strength, ecc_step; ++ ++ if (pdata->keep_config) { ++ pxa3xx_nand_detect_config(info); ++ } else { ++ ret = pxa3xx_nand_config_ident(info); ++ if (ret) ++ return ret; ++ } ++ ++ if (info->reg_ndcr & NDCR_DWIDTH_M) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ /* Device detection must be done with ECC disabled */ ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) ++ nand_writel(info, NDECCCTRL, 0x0); ++ ++ if (pdata->flash_bbt) ++ chip->bbt_options |= NAND_BBT_USE_FLASH; ++ ++ chip->ecc.strength = pdata->ecc_strength; ++ chip->ecc.size = pdata->ecc_step_size; ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; ++ ++ if (!pdata->keep_config) { ++ ret = pxa3xx_nand_init(host); ++ if (ret) { ++ dev_err(&info->pdev->dev, "Failed to init nand: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) { ++ /* ++ * We'll use a bad block table stored in-flash and don't ++ * allow writing the bad block marker to the flash. ++ */ ++ chip->bbt_options |= NAND_BBT_NO_OOB_BBM; ++ chip->bbt_td = &bbt_main_descr; ++ chip->bbt_md = &bbt_mirror_descr; ++ } ++ ++ /* ++ * If the page size is bigger than the FIFO size, let's check ++ * we are given the right variant and then switch to the extended ++ * (aka splitted) command handling, ++ */ ++ if (mtd->writesize > PAGE_CHUNK_SIZE) { ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || ++ info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) { ++ chip->cmdfunc = nand_cmdfunc_extended; ++ } else { ++ dev_err(&info->pdev->dev, ++ "unsupported page size on this variant\n"); ++ return -ENODEV; ++ } ++ } ++ ++ ecc_strength = chip->ecc.strength; ++ ecc_step = chip->ecc.size; ++ if (!ecc_strength || !ecc_step) { ++ ecc_strength = chip->ecc_strength_ds; ++ ecc_step = chip->ecc_step_ds; ++ } ++ ++ /* Set default ECC strength requirements on non-ONFI devices */ ++ if (ecc_strength < 1 && ecc_step < 1) { ++ ecc_strength = 1; ++ ecc_step = 512; ++ } ++ ++ ret = pxa_ecc_init(info, mtd, ecc_strength, ++ ecc_step, mtd->writesize); ++ if (ret) ++ return ret; ++ ++ /* calculate addressing information */ ++ if (mtd->writesize >= 2048) ++ host->col_addr_cycles = 2; ++ else ++ host->col_addr_cycles = 1; ++ ++ /* release the initial buffer */ ++ kfree(info->data_buff); ++ ++ /* allocate the real data + oob buffer */ ++ info->buf_size = mtd->writesize + mtd->oobsize; ++ ret = pxa3xx_nand_init_buff(info); ++ if (ret) ++ return ret; ++ info->oob_buff = info->data_buff + mtd->writesize; ++ ++ if ((mtd->size >> chip->page_shift) > 65536) ++ host->row_addr_cycles = 3; ++ else ++ host->row_addr_cycles = 2; ++ ++ if (!pdata->keep_config) ++ pxa3xx_nand_config_tail(info); ++ ++ return nand_scan_tail(mtd); ++} ++ ++static int alloc_nand_resource(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct pxa3xx_nand_platform_data *pdata; ++ struct pxa3xx_nand_info *info; ++ struct pxa3xx_nand_host *host; ++ struct nand_chip *chip = NULL; ++ struct mtd_info *mtd; ++ struct resource *r; ++ int ret, irq, cs; ++ ++ pdata = dev_get_platdata(&pdev->dev); ++ if (pdata->num_cs <= 0) { ++ dev_err(&pdev->dev, "invalid number of chip selects\n"); ++ return -ENODEV; ++ } ++ ++ info = devm_kzalloc(&pdev->dev, ++ sizeof(*info) + sizeof(*host) * pdata->num_cs, ++ GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ info->pdev = pdev; ++ info->variant = pxa3xx_nand_get_variant(pdev); ++ for (cs = 0; cs < pdata->num_cs; cs++) { ++ host = (void *)&info[1] + sizeof(*host) * cs; ++ chip = &host->chip; ++ nand_set_controller_data(chip, host); ++ mtd = nand_to_mtd(chip); ++ info->host[cs] = host; ++ host->cs = cs; ++ host->info_data = info; ++ mtd->dev.parent = &pdev->dev; ++ /* FIXME: all chips use the same device tree partitions */ ++ nand_set_flash_node(chip, np); ++ ++ nand_set_controller_data(chip, host); ++ chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; ++ chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; ++ chip->controller = &info->controller; ++ chip->waitfunc = pxa3xx_nand_waitfunc; ++ chip->select_chip = pxa3xx_nand_select_chip; ++ chip->read_word = pxa3xx_nand_read_word; ++ chip->read_byte = pxa3xx_nand_read_byte; ++ chip->read_buf = pxa3xx_nand_read_buf; ++ chip->write_buf = pxa3xx_nand_write_buf; ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ chip->cmdfunc = nand_cmdfunc; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ } ++ ++ nand_hw_control_init(chip->controller); ++ info->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(info->clk)) { ++ ret = PTR_ERR(info->clk); ++ dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret); ++ return ret; ++ } ++ ret = clk_prepare_enable(info->clk); ++ if (ret < 0) ++ return ret; ++ ++ if (!np && use_dma) { ++ r = platform_get_resource(pdev, IORESOURCE_DMA, 0); ++ if (r == NULL) { ++ dev_err(&pdev->dev, ++ "no resource defined for data DMA\n"); ++ ret = -ENXIO; ++ goto fail_disable_clk; ++ } ++ info->drcmr_dat = r->start; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "no IRQ resource defined\n"); ++ ret = -ENXIO; ++ goto fail_disable_clk; ++ } ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ info->mmio_base = devm_ioremap_resource(&pdev->dev, r); ++ if (IS_ERR(info->mmio_base)) { ++ ret = PTR_ERR(info->mmio_base); ++ dev_err(&pdev->dev, "failed to map register space: %d\n", ret); ++ goto fail_disable_clk; ++ } ++ info->mmio_phys = r->start; ++ ++ /* Allocate a buffer to allow flash detection */ ++ info->buf_size = INIT_BUFFER_SIZE; ++ info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); ++ if (info->data_buff == NULL) { ++ ret = -ENOMEM; ++ goto fail_disable_clk; ++ } ++ ++ /* initialize all interrupts to be disabled */ ++ disable_int(info, NDSR_MASK); ++ ++ ret = request_threaded_irq(irq, pxa3xx_nand_irq, ++ pxa3xx_nand_irq_thread, IRQF_ONESHOT, ++ pdev->name, info); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); ++ goto fail_free_buf; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ return 0; ++ ++fail_free_buf: ++ free_irq(irq, info); ++ kfree(info->data_buff); ++fail_disable_clk: ++ clk_disable_unprepare(info->clk); ++ return ret; ++} ++ ++static int pxa3xx_nand_remove(struct platform_device *pdev) ++{ ++ struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); ++ struct pxa3xx_nand_platform_data *pdata; ++ int irq, cs; ++ ++ if (!info) ++ return 0; ++ ++ pdata = dev_get_platdata(&pdev->dev); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq >= 0) ++ free_irq(irq, info); ++ pxa3xx_nand_free_buff(info); ++ ++ /* ++ * In the pxa3xx case, the DFI bus is shared between the SMC and NFC. ++ * In order to prevent a lockup of the system bus, the DFI bus ++ * arbitration is granted to SMC upon driver removal. This is done by ++ * setting the x_ARB_CNTL bit, which also prevents the NAND to have ++ * access to the bus anymore. ++ */ ++ nand_writel(info, NDCR, ++ (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) | ++ NFCV1_NDCR_ARB_CNTL); ++ clk_disable_unprepare(info->clk); ++ ++ for (cs = 0; cs < pdata->num_cs; cs++) ++ nand_release(nand_to_mtd(&info->host[cs]->chip)); ++ return 0; ++} ++ ++static int pxa3xx_nand_probe_dt(struct platform_device *pdev) ++{ ++ struct pxa3xx_nand_platform_data *pdata; ++ struct device_node *np = pdev->dev.of_node; ++ const struct of_device_id *of_id = ++ of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); ++ ++ if (!of_id) ++ return 0; ++ ++ /* ++ * Some SoCs like A7k/A8k need to enable manually the NAND ++ * controller to avoid being bootloader dependent. This is done ++ * through the use of a single bit in the System Functions registers. ++ */ ++ if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) { ++ struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle( ++ pdev->dev.of_node, "marvell,system-controller"); ++ u32 reg; ++ ++ if (IS_ERR(sysctrl_base)) ++ return PTR_ERR(sysctrl_base); ++ ++ regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ®); ++ reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN; ++ regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); ++ } ++ ++ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return -ENOMEM; ++ ++ if (of_get_property(np, "marvell,nand-enable-arbiter", NULL)) ++ pdata->enable_arbiter = 1; ++ if (of_get_property(np, "marvell,nand-keep-config", NULL)) ++ pdata->keep_config = 1; ++ of_property_read_u32(np, "num-cs", &pdata->num_cs); ++ ++ pdev->dev.platform_data = pdata; ++ ++ return 0; ++} ++ ++static int pxa3xx_nand_probe(struct platform_device *pdev) ++{ ++ struct pxa3xx_nand_platform_data *pdata; ++ struct pxa3xx_nand_info *info; ++ int ret, cs, probe_success, dma_available; ++ ++ dma_available = IS_ENABLED(CONFIG_ARM) && ++ (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP)); ++ if (use_dma && !dma_available) { ++ use_dma = 0; ++ dev_warn(&pdev->dev, ++ "This platform can't do DMA on this device\n"); ++ } ++ ++ ret = pxa3xx_nand_probe_dt(pdev); ++ if (ret) ++ return ret; ++ ++ pdata = dev_get_platdata(&pdev->dev); ++ if (!pdata) { ++ dev_err(&pdev->dev, "no platform data defined\n"); ++ return -ENODEV; ++ } ++ ++ ret = alloc_nand_resource(pdev); ++ if (ret) ++ return ret; ++ ++ info = platform_get_drvdata(pdev); ++ probe_success = 0; ++ for (cs = 0; cs < pdata->num_cs; cs++) { ++ struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip); ++ ++ /* ++ * The mtd name matches the one used in 'mtdparts' kernel ++ * parameter. This name cannot be changed or otherwise ++ * user's mtd partitions configuration would get broken. ++ */ ++ mtd->name = "pxa3xx_nand-0"; ++ info->cs = cs; ++ ret = pxa3xx_nand_scan(mtd); ++ if (ret) { ++ dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", ++ cs); ++ continue; ++ } ++ ++ ret = mtd_device_register(mtd, pdata->parts[cs], ++ pdata->nr_parts[cs]); ++ if (!ret) ++ probe_success = 1; ++ } ++ ++ if (!probe_success) { ++ pxa3xx_nand_remove(pdev); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int pxa3xx_nand_suspend(struct device *dev) ++{ ++ struct pxa3xx_nand_info *info = dev_get_drvdata(dev); ++ ++ if (info->state) { ++ dev_err(dev, "driver busy, state = %d\n", info->state); ++ return -EAGAIN; ++ } ++ ++ clk_disable(info->clk); ++ return 0; ++} ++ ++static int pxa3xx_nand_resume(struct device *dev) ++{ ++ struct pxa3xx_nand_info *info = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_enable(info->clk); ++ if (ret < 0) ++ return ret; ++ ++ /* We don't want to handle interrupt without calling mtd routine */ ++ disable_int(info, NDCR_INT_MASK); ++ ++ /* ++ * Directly set the chip select to a invalid value, ++ * then the driver would reset the timing according ++ * to current chip select at the beginning of cmdfunc ++ */ ++ info->cs = 0xff; ++ ++ /* ++ * As the spec says, the NDSR would be updated to 0x1800 when ++ * doing the nand_clk disable/enable. ++ * To prevent it damaging state machine of the driver, clear ++ * all status before resume ++ */ ++ nand_writel(info, NDSR, NDSR_MASK); ++ ++ return 0; ++} ++#else ++#define pxa3xx_nand_suspend NULL ++#define pxa3xx_nand_resume NULL ++#endif ++ ++static const struct dev_pm_ops pxa3xx_nand_pm_ops = { ++ .suspend = pxa3xx_nand_suspend, ++ .resume = pxa3xx_nand_resume, ++}; ++ ++static struct platform_driver pxa3xx_nand_driver = { ++ .driver = { ++ .name = "pxa3xx-nand", ++ .of_match_table = pxa3xx_nand_dt_ids, ++ .pm = &pxa3xx_nand_pm_ops, ++ }, ++ .probe = pxa3xx_nand_probe, ++ .remove = pxa3xx_nand_remove, ++}; ++ ++module_platform_driver(pxa3xx_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("PXA3xx NAND controller driver"); +diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c +new file mode 100644 +index 00000000..3ea1e6b +--- /dev/null ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -0,0 +1,2827 @@ ++/* ++ * Copyright (c) 2016, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* NANDc reg offsets */ ++#define NAND_FLASH_CMD 0x00 ++#define NAND_ADDR0 0x04 ++#define NAND_ADDR1 0x08 ++#define NAND_FLASH_CHIP_SELECT 0x0c ++#define NAND_EXEC_CMD 0x10 ++#define NAND_FLASH_STATUS 0x14 ++#define NAND_BUFFER_STATUS 0x18 ++#define NAND_DEV0_CFG0 0x20 ++#define NAND_DEV0_CFG1 0x24 ++#define NAND_DEV0_ECC_CFG 0x28 ++#define NAND_DEV1_ECC_CFG 0x2c ++#define NAND_DEV1_CFG0 0x30 ++#define NAND_DEV1_CFG1 0x34 ++#define NAND_READ_ID 0x40 ++#define NAND_READ_STATUS 0x44 ++#define NAND_DEV_CMD0 0xa0 ++#define NAND_DEV_CMD1 0xa4 ++#define NAND_DEV_CMD2 0xa8 ++#define NAND_DEV_CMD_VLD 0xac ++#define SFLASHC_BURST_CFG 0xe0 ++#define NAND_ERASED_CW_DETECT_CFG 0xe8 ++#define NAND_ERASED_CW_DETECT_STATUS 0xec ++#define NAND_EBI2_ECC_BUF_CFG 0xf0 ++#define FLASH_BUF_ACC 0x100 ++ ++#define NAND_CTRL 0xf00 ++#define NAND_VERSION 0xf08 ++#define NAND_READ_LOCATION_0 0xf20 ++#define NAND_READ_LOCATION_1 0xf24 ++#define NAND_READ_LOCATION_2 0xf28 ++#define NAND_READ_LOCATION_3 0xf2c ++ ++/* dummy register offsets, used by write_reg_dma */ ++#define NAND_DEV_CMD1_RESTORE 0xdead ++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef ++ ++/* NAND_FLASH_CMD bits */ ++#define PAGE_ACC BIT(4) ++#define LAST_PAGE BIT(5) ++ ++/* NAND_FLASH_CHIP_SELECT bits */ ++#define NAND_DEV_SEL 0 ++#define DM_EN BIT(2) ++ ++/* NAND_FLASH_STATUS bits */ ++#define FS_OP_ERR BIT(4) ++#define FS_READY_BSY_N BIT(5) ++#define FS_MPU_ERR BIT(8) ++#define FS_DEVICE_STS_ERR BIT(16) ++#define FS_DEVICE_WP BIT(23) ++ ++/* NAND_BUFFER_STATUS bits */ ++#define BS_UNCORRECTABLE_BIT BIT(8) ++#define BS_CORRECTABLE_ERR_MSK 0x1f ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DISABLE_STATUS_AFTER_WRITE 4 ++#define CW_PER_PAGE 6 ++#define UD_SIZE_BYTES 9 ++#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define SPARE_SIZE_BYTES 23 ++#define NUM_ADDR_CYCLES 27 ++#define STATUS_BFR_READ 30 ++#define SET_RD_MODE_AFTER_STATUS 31 ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DEV0_CFG1_ECC_DISABLE 0 ++#define WIDE_FLASH 1 ++#define NAND_RECOVERY_CYCLES 2 ++#define CS_ACTIVE_BSY 5 ++#define BAD_BLOCK_BYTE_NUM 6 ++#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define WR_RD_BSY_GAP 17 ++#define ENABLE_BCH_ECC 27 ++ ++/* NAND_DEV0_ECC_CFG bits */ ++#define ECC_CFG_ECC_DISABLE 0 ++#define ECC_SW_RESET 1 ++#define ECC_MODE 4 ++#define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_NUM_DATA_BYTES 16 ++#define ECC_FORCE_CLK_OPEN 30 ++ ++/* NAND_DEV_CMD1 bits */ ++#define READ_ADDR 0 ++ ++/* NAND_DEV_CMD_VLD bits */ ++#define READ_START_VLD BIT(0) ++#define READ_STOP_VLD BIT(1) ++#define WRITE_START_VLD BIT(2) ++#define ERASE_START_VLD BIT(3) ++#define SEQ_READ_START_VLD BIT(4) ++ ++/* NAND_EBI2_ECC_BUF_CFG bits */ ++#define NUM_STEPS 0 ++ ++/* NAND_ERASED_CW_DETECT_CFG bits */ ++#define ERASED_CW_ECC_MASK 1 ++#define AUTO_DETECT_RES 0 ++#define MASK_ECC (1 << ERASED_CW_ECC_MASK) ++#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) ++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) ++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) ++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) ++ ++/* NAND_ERASED_CW_DETECT_STATUS bits */ ++#define PAGE_ALL_ERASED BIT(7) ++#define CODEWORD_ALL_ERASED BIT(6) ++#define PAGE_ERASED BIT(5) ++#define CODEWORD_ERASED BIT(4) ++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) ++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) ++ ++/* NAND_READ_LOCATION_n bits */ ++#define READ_LOCATION_OFFSET 0 ++#define READ_LOCATION_SIZE 16 ++#define READ_LOCATION_LAST 31 ++ ++/* Version Mask */ ++#define NAND_VERSION_MAJOR_MASK 0xf0000000 ++#define NAND_VERSION_MAJOR_SHIFT 28 ++#define NAND_VERSION_MINOR_MASK 0x0fff0000 ++#define NAND_VERSION_MINOR_SHIFT 16 ++ ++/* NAND OP_CMDs */ ++#define PAGE_READ 0x2 ++#define PAGE_READ_WITH_ECC 0x3 ++#define PAGE_READ_WITH_ECC_SPARE 0x4 ++#define PROGRAM_PAGE 0x6 ++#define PAGE_PROGRAM_WITH_ECC 0x7 ++#define PROGRAM_PAGE_SPARE 0x9 ++#define BLOCK_ERASE 0xa ++#define FETCH_ID 0xb ++#define RESET_DEVICE 0xd ++ ++/* Default Value for NAND_DEV_CMD_VLD */ ++#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ ++ ERASE_START_VLD | SEQ_READ_START_VLD) ++ ++/* NAND_CTRL bits */ ++#define BAM_MODE_EN BIT(0) ++ ++/* ++ * the NAND controller performs reads/writes with ECC in 516 byte chunks. ++ * the driver calls the chunks 'step' or 'codeword' interchangeably ++ */ ++#define NANDC_STEP_SIZE 512 ++ ++/* ++ * the largest page size we support is 8K, this will have 16 steps/codewords ++ * of 512 bytes each ++ */ ++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) ++ ++/* we read at most 3 registers per codeword scan */ ++#define MAX_REG_RD (3 * MAX_NUM_STEPS) ++ ++/* ECC modes supported by the controller */ ++#define ECC_NONE BIT(0) ++#define ECC_RS_4BIT BIT(1) ++#define ECC_BCH_4BIT BIT(2) ++#define ECC_BCH_8BIT BIT(3) ++ ++#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \ ++nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ ++ ((offset) << READ_LOCATION_OFFSET) | \ ++ ((size) << READ_LOCATION_SIZE) | \ ++ ((is_last) << READ_LOCATION_LAST)) ++ ++/* ++ * Returns the actual register address for all NAND_DEV_ registers ++ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) ++ */ ++#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) ++ ++#define QPIC_PER_CW_CMD_SGL 32 ++#define QPIC_PER_CW_DATA_SGL 8 ++ ++/* ++ * Flags used in DMA descriptor preparation helper functions ++ * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) ++ */ ++/* Don't set the EOT in current tx BAM sgl */ ++#define NAND_BAM_NO_EOT BIT(0) ++/* Set the NWD flag in current BAM sgl */ ++#define NAND_BAM_NWD BIT(1) ++/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ ++#define NAND_BAM_NEXT_SGL BIT(2) ++/* ++ * Erased codeword status is being used two times in single transfer so this ++ * flag will determine the current value of erased codeword status register ++ */ ++#define NAND_ERASED_CW_SET BIT(4) ++ ++/* ++ * This data type corresponds to the BAM transaction which will be used for all ++ * NAND transfers. ++ * @cmd_sgl - sgl for NAND BAM command pipe ++ * @data_sgl - sgl for NAND BAM consumer/producer pipe ++ * @cmd_sgl_pos - current index in command sgl. ++ * @cmd_sgl_start - start index in command sgl. ++ * @tx_sgl_pos - current index in data sgl for tx. ++ * @tx_sgl_start - start index in data sgl for tx. ++ * @rx_sgl_pos - current index in data sgl for rx. ++ * @rx_sgl_start - start index in data sgl for rx. ++ */ ++struct bam_transaction { ++ struct scatterlist *cmd_sgl; ++ struct scatterlist *data_sgl; ++ u32 cmd_sgl_pos; ++ u32 cmd_sgl_start; ++ u32 tx_sgl_pos; ++ u32 tx_sgl_start; ++ u32 rx_sgl_pos; ++ u32 rx_sgl_start; ++}; ++ ++/* ++ * This data type corresponds to the nand dma descriptor ++ * @list - list for desc_info ++ * @dir - DMA transfer direction ++ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by ++ * ADM ++ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM ++ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM ++ * @dma_desc - low level DMA engine descriptor ++ */ ++struct desc_info { ++ struct list_head node; ++ ++ enum dma_data_direction dir; ++ union { ++ struct scatterlist adm_sgl; ++ struct { ++ struct scatterlist *bam_sgl; ++ int sgl_cnt; ++ }; ++ }; ++ struct dma_async_tx_descriptor *dma_desc; ++}; ++ ++/* ++ * holds the current register values that we want to write. acts as a contiguous ++ * chunk of memory which we use to write the controller registers through DMA. ++ */ ++struct nandc_regs { ++ __le32 cmd; ++ __le32 addr0; ++ __le32 addr1; ++ __le32 chip_sel; ++ __le32 exec; ++ ++ __le32 cfg0; ++ __le32 cfg1; ++ __le32 ecc_bch_cfg; ++ ++ __le32 clrflashstatus; ++ __le32 clrreadstatus; ++ ++ __le32 cmd1; ++ __le32 vld; ++ ++ __le32 orig_cmd1; ++ __le32 orig_vld; ++ ++ __le32 ecc_buf_cfg; ++ __le32 read_location0; ++ __le32 read_location1; ++ __le32 read_location2; ++ __le32 read_location3; ++ ++ __le32 erased_cw_detect_cfg_clr; ++ __le32 erased_cw_detect_cfg_set; ++}; ++ ++/* ++ * NAND controller data struct ++ * ++ * @controller: base controller structure ++ * @host_list: list containing all the chips attached to the ++ * controller ++ * @dev: parent device ++ * @base: MMIO base ++ * @base_dma: physical base address of controller registers ++ * @core_clk: controller clock ++ * @aon_clk: another controller clock ++ * ++ * @chan: dma channel ++ * @cmd_crci: ADM DMA CRCI for command flow control ++ * @data_crci: ADM DMA CRCI for data flow control ++ * @desc_list: DMA descriptor list (list of desc_infos) ++ * ++ * @data_buffer: our local DMA buffer for page read/writes, ++ * used when we can't use the buffer provided ++ * by upper layers directly ++ * @buf_size/count/start: markers for chip->read_buf/write_buf functions ++ * @reg_read_buf: local buffer for reading back registers via DMA ++ * @reg_read_dma: contains dma address for register read buffer ++ * @reg_read_pos: marker for data read in reg_read_buf ++ * ++ * @regs: a contiguous chunk of memory for DMA register ++ * writes. contains the register values to be ++ * written to controller ++ * @cmd1/vld: some fixed controller register values ++ * @props: properties of current NAND controller, ++ * initialized via DT match data ++ * @max_cwperpage: maximum QPIC codewords required. calculated ++ * from all connected NAND devices pagesize ++ */ ++struct qcom_nand_controller { ++ struct nand_controller controller; ++ struct list_head host_list; ++ ++ struct device *dev; ++ ++ void __iomem *base; ++ dma_addr_t base_dma; ++ ++ struct clk *core_clk; ++ struct clk *aon_clk; ++ ++ union { ++ /* will be used only by QPIC for BAM DMA */ ++ struct { ++ struct dma_chan *tx_chan; ++ struct dma_chan *rx_chan; ++ struct dma_chan *cmd_chan; ++ }; ++ ++ /* will be used only by EBI2 for ADM DMA */ ++ struct { ++ struct dma_chan *chan; ++ unsigned int cmd_crci; ++ unsigned int data_crci; ++ }; ++ }; ++ ++ struct list_head desc_list; ++ struct bam_transaction *bam_txn; ++ ++ u8 *data_buffer; ++ int buf_size; ++ int buf_count; ++ int buf_start; ++ unsigned int max_cwperpage; ++ ++ __le32 *reg_read_buf; ++ dma_addr_t reg_read_dma; ++ int reg_read_pos; ++ ++ struct nandc_regs *regs; ++ ++ u32 cmd1, vld; ++ const struct qcom_nandc_props *props; ++}; ++ ++/* ++ * NAND chip structure ++ * ++ * @chip: base NAND chip structure ++ * @node: list node to add itself to host_list in ++ * qcom_nand_controller ++ * ++ * @cs: chip select value for this chip ++ * @cw_size: the number of bytes in a single step/codeword ++ * of a page, consisting of all data, ecc, spare ++ * and reserved bytes ++ * @cw_data: the number of bytes within a codeword protected ++ * by ECC ++ * @use_ecc: request the controller to use ECC for the ++ * upcoming read/write ++ * @bch_enabled: flag to tell whether BCH ECC mode is used ++ * @ecc_bytes_hw: ECC bytes used by controller hardware for this ++ * chip ++ * @status: value to be returned if NAND_CMD_STATUS command ++ * is executed ++ * @last_command: keeps track of last command on this chip. used ++ * for reading correct status ++ * ++ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for ++ * ecc/non-ecc mode for the current nand flash ++ * device ++ */ ++struct qcom_nand_host { ++ struct nand_chip chip; ++ struct list_head node; ++ ++ int cs; ++ int cw_size; ++ int cw_data; ++ bool use_ecc; ++ bool bch_enabled; ++ int ecc_bytes_hw; ++ int spare_bytes; ++ int bbm_size; ++ u8 status; ++ int last_command; ++ ++ u32 cfg0, cfg1; ++ u32 cfg0_raw, cfg1_raw; ++ u32 ecc_buf_cfg; ++ u32 ecc_bch_cfg; ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++}; ++ ++/* ++ * This data type corresponds to the NAND controller properties which varies ++ * among different NAND controllers. ++ * @ecc_modes - ecc mode for NAND ++ * @is_bam - whether NAND controller is using BAM ++ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset ++ */ ++struct qcom_nandc_props { ++ u32 ecc_modes; ++ bool is_bam; ++ u32 dev_cmd_reg_start; ++}; ++ ++/* Frees the BAM transaction memory */ ++static void free_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ devm_kfree(nandc->dev, bam_txn); ++} ++ ++/* Allocates and Initializes the BAM transaction */ ++static struct bam_transaction * ++alloc_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn; ++ size_t bam_txn_size; ++ unsigned int num_cw = nandc->max_cwperpage; ++ void *bam_txn_buf; ++ ++ bam_txn_size = ++ sizeof(*bam_txn) + num_cw * ++ ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + ++ (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); ++ ++ bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); ++ if (!bam_txn_buf) ++ return NULL; ++ ++ bam_txn = bam_txn_buf; ++ bam_txn_buf += sizeof(*bam_txn); ++ ++ bam_txn->cmd_sgl = bam_txn_buf; ++ bam_txn_buf += ++ sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; ++ ++ bam_txn->data_sgl = bam_txn_buf; ++ ++ return bam_txn; ++} ++ ++/* Clears the BAM transaction indexes */ ++static void clear_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (!nandc->props->is_bam) ++ return; ++ ++ bam_txn->cmd_sgl_pos = 0; ++ bam_txn->cmd_sgl_start = 0; ++ bam_txn->tx_sgl_pos = 0; ++ bam_txn->tx_sgl_start = 0; ++ bam_txn->rx_sgl_pos = 0; ++ bam_txn->rx_sgl_start = 0; ++ ++ sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * ++ QPIC_PER_CW_CMD_SGL); ++ sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * ++ QPIC_PER_CW_DATA_SGL); ++} ++ ++static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) ++{ ++ return container_of(chip, struct qcom_nand_host, chip); ++} ++ ++static inline struct qcom_nand_controller * ++get_qcom_nand_controller(struct nand_chip *chip) ++{ ++ return container_of(chip->controller, struct qcom_nand_controller, ++ controller); ++} ++ ++static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) ++{ ++ return ioread32(nandc->base + offset); ++} ++ ++static inline void nandc_write(struct qcom_nand_controller *nandc, int offset, ++ u32 val) ++{ ++ iowrite32(val, nandc->base + offset); ++} ++ ++static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, ++ bool is_cpu) ++{ ++ if (!nandc->props->is_bam) ++ return; ++ ++ if (is_cpu) ++ dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ else ++ dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++} ++ ++static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) ++{ ++ switch (offset) { ++ case NAND_FLASH_CMD: ++ return ®s->cmd; ++ case NAND_ADDR0: ++ return ®s->addr0; ++ case NAND_ADDR1: ++ return ®s->addr1; ++ case NAND_FLASH_CHIP_SELECT: ++ return ®s->chip_sel; ++ case NAND_EXEC_CMD: ++ return ®s->exec; ++ case NAND_FLASH_STATUS: ++ return ®s->clrflashstatus; ++ case NAND_DEV0_CFG0: ++ return ®s->cfg0; ++ case NAND_DEV0_CFG1: ++ return ®s->cfg1; ++ case NAND_DEV0_ECC_CFG: ++ return ®s->ecc_bch_cfg; ++ case NAND_READ_STATUS: ++ return ®s->clrreadstatus; ++ case NAND_DEV_CMD1: ++ return ®s->cmd1; ++ case NAND_DEV_CMD1_RESTORE: ++ return ®s->orig_cmd1; ++ case NAND_DEV_CMD_VLD: ++ return ®s->vld; ++ case NAND_DEV_CMD_VLD_RESTORE: ++ return ®s->orig_vld; ++ case NAND_EBI2_ECC_BUF_CFG: ++ return ®s->ecc_buf_cfg; ++ case NAND_READ_LOCATION_0: ++ return ®s->read_location0; ++ case NAND_READ_LOCATION_1: ++ return ®s->read_location1; ++ case NAND_READ_LOCATION_2: ++ return ®s->read_location2; ++ case NAND_READ_LOCATION_3: ++ return ®s->read_location3; ++ default: ++ return NULL; ++ } ++} ++ ++static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset, ++ u32 val) ++{ ++ struct nandc_regs *regs = nandc->regs; ++ __le32 *reg; ++ ++ reg = offset_to_nandc_reg(regs, offset); ++ ++ if (reg) ++ *reg = cpu_to_le32(val); ++} ++ ++/* helper to configure address register values */ ++static void set_address(struct qcom_nand_host *host, u16 column, int page) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ ++ nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column); ++ nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff); ++} ++ ++/* ++ * update_rw_regs: set up read/write register values, these will be ++ * written to the NAND controller registers via DMA ++ * ++ * @num_cw: number of steps for the read/write operation ++ * @read: read or write operation ++ */ ++static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ u32 cmd, cfg0, cfg1, ecc_bch_cfg; ++ ++ if (read) { ++ if (host->use_ecc) ++ cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; ++ else ++ cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; ++ } else { ++ cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; ++ } ++ ++ if (host->use_ecc) { ++ cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ ++ cfg1 = host->cfg1; ++ ecc_bch_cfg = host->ecc_bch_cfg; ++ } else { ++ cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ ++ cfg1 = host->cfg1_raw; ++ ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ } ++ ++ nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); ++ nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0); ++ nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1); ++ nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg); ++ nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); ++ nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); ++ nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); ++ nandc_set_reg(nandc, NAND_EXEC_CMD, 1); ++ ++ if (read) ++ nandc_set_read_loc(nandc, 0, 0, host->use_ecc ? ++ host->cw_data : host->cw_size, 1); ++} ++ ++/* ++ * Maps the scatter gather list for DMA transfer and forms the DMA descriptor ++ * for BAM. This descriptor will be added in the NAND DMA descriptor queue ++ * which will be submitted to DMA engine. ++ */ ++static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, ++ unsigned long flags) ++{ ++ struct desc_info *desc; ++ struct scatterlist *sgl; ++ unsigned int sgl_cnt; ++ int ret; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ enum dma_transfer_direction dir_eng; ++ struct dma_async_tx_descriptor *dma_desc; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ if (chan == nandc->cmd_chan) { ++ sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; ++ sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; ++ bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } else if (chan == nandc->tx_chan) { ++ sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; ++ sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; ++ bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } else { ++ sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; ++ sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; ++ bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; ++ dir_eng = DMA_DEV_TO_MEM; ++ desc->dir = DMA_FROM_DEVICE; ++ } ++ ++ sg_mark_end(sgl + sgl_cnt - 1); ++ ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); ++ if (ret == 0) { ++ dev_err(nandc->dev, "failure in mapping desc\n"); ++ kfree(desc); ++ return -ENOMEM; ++ } ++ ++ desc->sgl_cnt = sgl_cnt; ++ desc->bam_sgl = sgl; ++ ++ dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, ++ flags); ++ ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failure in prep desc\n"); ++ dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); ++ kfree(desc); ++ return -EINVAL; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ list_add_tail(&desc->node, &nandc->desc_list); ++ ++ return 0; ++} ++ ++/* ++ * Prepares the data descriptor for BAM DMA which will be used for NAND ++ * data reads and writes. ++ */ ++static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, ++ int size, unsigned int flags) ++{ ++ int ret; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (read) { ++ sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], ++ vaddr, size); ++ bam_txn->rx_sgl_pos++; ++ } else { ++ sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], ++ vaddr, size); ++ bam_txn->tx_sgl_pos++; ++ ++ /* ++ * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag ++ * is not set, form the DMA descriptor ++ */ ++ if (!(flags & NAND_BAM_NO_EOT)) { ++ ret = prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, ++ bool flow_control) ++{ ++ struct desc_info *desc; ++ struct dma_async_tx_descriptor *dma_desc; ++ struct scatterlist *sgl; ++ struct dma_slave_config slave_conf; ++ enum dma_transfer_direction dir_eng; ++ int ret; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ sgl = &desc->adm_sgl; ++ ++ sg_init_one(sgl, vaddr, size); ++ ++ if (read) { ++ dir_eng = DMA_DEV_TO_MEM; ++ desc->dir = DMA_FROM_DEVICE; ++ } else { ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } ++ ++ ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); ++ if (ret == 0) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ memset(&slave_conf, 0x00, sizeof(slave_conf)); ++ ++ slave_conf.device_fc = flow_control; ++ if (read) { ++ slave_conf.src_maxburst = 16; ++ slave_conf.src_addr = nandc->base_dma + reg_off; ++ slave_conf.slave_id = nandc->data_crci; ++ } else { ++ slave_conf.dst_maxburst = 16; ++ slave_conf.dst_addr = nandc->base_dma + reg_off; ++ slave_conf.slave_id = nandc->cmd_crci; ++ } ++ ++ ret = dmaengine_slave_config(nandc->chan, &slave_conf); ++ if (ret) { ++ dev_err(nandc->dev, "failed to configure dma channel\n"); ++ goto err; ++ } ++ ++ dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failed to prepare desc\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ list_add_tail(&desc->node, &nandc->desc_list); ++ ++ return 0; ++err: ++ kfree(desc); ++ ++ return ret; ++} ++ ++/* ++ * read_reg_dma: prepares a descriptor to read a given number of ++ * contiguous registers to the reg_read_buf pointer ++ * ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to read ++ * @flags: flags to control DMA descriptor preparation ++ */ ++static int read_reg_dma(struct qcom_nand_controller *nandc, int first, ++ int num_regs, unsigned int flags) ++{ ++ bool flow_control = false; ++ void *vaddr; ++ int size; ++ ++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) ++ flow_control = true; ++ ++ if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) ++ first = dev_cmd_reg_addr(nandc, first); ++ ++ size = num_regs * sizeof(u32); ++ vaddr = nandc->reg_read_buf + nandc->reg_read_pos; ++ nandc->reg_read_pos += num_regs; ++ ++ return prep_adm_dma_desc(nandc, true, first, vaddr, size, flow_control); ++} ++ ++/* ++ * write_reg_dma: prepares a descriptor to write a given number of ++ * contiguous registers ++ * ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to write ++ * @flags: flags to control DMA descriptor preparation ++ */ ++static int write_reg_dma(struct qcom_nand_controller *nandc, int first, ++ int num_regs, unsigned int flags) ++{ ++ bool flow_control = false; ++ struct nandc_regs *regs = nandc->regs; ++ void *vaddr; ++ int size; ++ ++ vaddr = offset_to_nandc_reg(regs, first); ++ ++ if (first == NAND_FLASH_CMD) ++ flow_control = true; ++ ++ if (first == NAND_ERASED_CW_DETECT_CFG) { ++ if (flags & NAND_ERASED_CW_SET) ++ vaddr = ®s->erased_cw_detect_cfg_set; ++ else ++ vaddr = ®s->erased_cw_detect_cfg_clr; ++ } ++ ++ if (first == NAND_EXEC_CMD) ++ flags |= NAND_BAM_NWD; ++ ++ if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) ++ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); ++ ++ if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) ++ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); ++ ++ size = num_regs * sizeof(u32); ++ ++ return prep_adm_dma_desc(nandc, false, first, vaddr, size, ++ flow_control); ++} ++ ++/* ++ * read_data_dma: prepares a DMA descriptor to transfer data from the ++ * controller's internal buffer to the buffer 'vaddr' ++ * ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ */ ++static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) ++{ ++ if (nandc->props->is_bam) ++ return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); ++ ++ return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); ++} ++ ++/* ++ * write_data_dma: prepares a DMA descriptor to transfer data from ++ * 'vaddr' to the controller's internal buffer ++ * ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to read from ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ */ ++static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) ++{ ++ if (nandc->props->is_bam) ++ return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); ++ ++ return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); ++} ++ ++/* ++ * Helper to prepare DMA descriptors for configuring registers ++ * before reading a NAND page. ++ */ ++static void config_nand_page_read(struct qcom_nand_controller *nandc) ++{ ++ write_reg_dma(nandc, NAND_ADDR0, 2, 0); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); ++ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++} ++ ++/* ++ * Helper to prepare DMA descriptors for configuring registers ++ * before reading each codeword in NAND page. ++ */ ++static void config_nand_cw_read(struct qcom_nand_controller *nandc) ++{ ++ if (nandc->props->is_bam) ++ write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, ++ NAND_BAM_NEXT_SGL); ++ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); ++ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ NAND_BAM_NEXT_SGL); ++} ++ ++/* ++ * Helper to prepare dma descriptors to configure registers needed for reading a ++ * single codeword in page ++ */ ++static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) ++{ ++ config_nand_page_read(nandc); ++ config_nand_cw_read(nandc); ++} ++ ++/* ++ * Helper to prepare DMA descriptors used to configure registers needed for ++ * before writing a NAND page. ++ */ ++static void config_nand_page_write(struct qcom_nand_controller *nandc) ++{ ++ write_reg_dma(nandc, NAND_ADDR0, 2, 0); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); ++ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, ++ NAND_BAM_NEXT_SGL); ++} ++ ++/* ++ * Helper to prepare DMA descriptors for configuring registers ++ * before writing each codeword in NAND page. ++ */ ++static void config_nand_cw_write(struct qcom_nand_controller *nandc) ++{ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ ++ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); ++ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); ++} ++ ++/* ++ * the following functions are used within chip->cmdfunc() to perform different ++ * NAND_CMD_* commands ++ */ ++ ++/* sets up descriptors for NAND_CMD_PARAM */ ++static int nandc_param(struct qcom_nand_host *host) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ /* ++ * NAND_CMD_PARAM is called before we know much about the FLASH chip ++ * in use. we configure the controller to perform a raw read of 512 ++ * bytes to read onfi params ++ */ ++ nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE); ++ nandc_set_reg(nandc, NAND_ADDR0, 0); ++ nandc_set_reg(nandc, NAND_ADDR1, 0); ++ nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE ++ | 512 << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES); ++ nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | 0 << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE); ++ nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); ++ ++ /* configure CMD1 and VLD for ONFI param probing */ ++ nandc_set_reg(nandc, NAND_DEV_CMD_VLD, ++ (nandc->vld & ~READ_START_VLD)); ++ nandc_set_reg(nandc, NAND_DEV_CMD1, ++ (nandc->cmd1 & ~(0xFF << READ_ADDR)) ++ | NAND_CMD_PARAM << READ_ADDR); ++ ++ nandc_set_reg(nandc, NAND_EXEC_CMD, 1); ++ ++ nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); ++ nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); ++ nandc_set_read_loc(nandc, 0, 0, 512, 1); ++ ++ write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); ++ write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); ++ ++ nandc->buf_count = 512; ++ memset(nandc->data_buffer, 0xff, nandc->buf_count); ++ ++ config_nand_single_cw_page_read(nandc); ++ ++ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, ++ nandc->buf_count, 0); ++ ++ /* restore CMD1 and VLD regs */ ++ write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); ++ write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_ERASE1 */ ++static int erase_block(struct qcom_nand_host *host, int page_addr) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ nandc_set_reg(nandc, NAND_FLASH_CMD, ++ BLOCK_ERASE | PAGE_ACC | LAST_PAGE); ++ nandc_set_reg(nandc, NAND_ADDR0, page_addr); ++ nandc_set_reg(nandc, NAND_ADDR1, 0); ++ nandc_set_reg(nandc, NAND_DEV0_CFG0, ++ host->cfg0_raw & ~(7 << CW_PER_PAGE)); ++ nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw); ++ nandc_set_reg(nandc, NAND_EXEC_CMD, 1); ++ nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); ++ nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); ++ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ ++ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); ++ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_READID */ ++static int read_id(struct qcom_nand_host *host, int column) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ if (column == -1) ++ return 0; ++ ++ nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); ++ nandc_set_reg(nandc, NAND_ADDR0, column); ++ nandc_set_reg(nandc, NAND_ADDR1, 0); ++ nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, ++ nandc->props->is_bam ? 0 : DM_EN); ++ nandc_set_reg(nandc, NAND_EXEC_CMD, 1); ++ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); ++ ++ return 0; ++} ++ ++/* sets up descriptors for NAND_CMD_RESET */ ++static int reset(struct qcom_nand_host *host) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); ++ nandc_set_reg(nandc, NAND_EXEC_CMD, 1); ++ ++ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ ++ return 0; ++} ++ ++/* helpers to submit/free our list of dma descriptors */ ++static int submit_descs(struct qcom_nand_controller *nandc) ++{ ++ struct desc_info *desc; ++ dma_cookie_t cookie = 0; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ int r; ++ ++ if (nandc->props->is_bam) { ++ if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { ++ r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); ++ if (r) ++ return r; ++ } ++ ++ if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { ++ r = prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); ++ if (r) ++ return r; ++ } ++ ++ if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { ++ r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0); ++ if (r) ++ return r; ++ } ++ } ++ ++ list_for_each_entry(desc, &nandc->desc_list, node) ++ cookie = dmaengine_submit(desc->dma_desc); ++ ++ if (nandc->props->is_bam) { ++ dma_async_issue_pending(nandc->tx_chan); ++ dma_async_issue_pending(nandc->rx_chan); ++ ++ if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) ++ return -ETIMEDOUT; ++ } else { ++ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static void free_descs(struct qcom_nand_controller *nandc) ++{ ++ struct desc_info *desc, *n; ++ ++ list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { ++ list_del(&desc->node); ++ ++ if (nandc->props->is_bam) ++ dma_unmap_sg(nandc->dev, desc->bam_sgl, ++ desc->sgl_cnt, desc->dir); ++ else ++ dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, ++ desc->dir); ++ ++ kfree(desc); ++ } ++} ++ ++/* reset the register read buffer for next NAND operation */ ++static void clear_read_regs(struct qcom_nand_controller *nandc) ++{ ++ nandc->reg_read_pos = 0; ++ nandc_read_buffer_sync(nandc, false); ++} ++ ++static void pre_command(struct qcom_nand_host *host, int command) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ nandc->buf_count = 0; ++ nandc->buf_start = 0; ++ host->use_ecc = false; ++ host->last_command = command; ++ ++ clear_read_regs(nandc); ++ ++ if (command == NAND_CMD_RESET || command == NAND_CMD_READID || ++ command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) ++ clear_bam_transaction(nandc); ++} ++ ++/* ++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our ++ * privately maintained status byte, this status byte can be read after ++ * NAND_CMD_STATUS is called ++ */ ++static void parse_erase_write_errors(struct qcom_nand_host *host, int command) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int num_cw; ++ int i; ++ ++ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; ++ nandc_read_buffer_sync(nandc, true); ++ ++ for (i = 0; i < num_cw; i++) { ++ u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); ++ ++ if (flash_status & FS_MPU_ERR) ++ host->status &= ~NAND_STATUS_WP; ++ ++ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && ++ (flash_status & ++ FS_DEVICE_STS_ERR))) ++ host->status |= NAND_STATUS_FAIL; ++ } ++} ++ ++static void post_command(struct qcom_nand_host *host, int command) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ switch (command) { ++ case NAND_CMD_READID: ++ nandc_read_buffer_sync(nandc, true); ++ memcpy(nandc->data_buffer, nandc->reg_read_buf, ++ nandc->buf_count); ++ break; ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_ERASE1: ++ parse_erase_write_errors(host, command); ++ break; ++ default: ++ break; ++ } ++} ++ ++/* ++ * Implements chip->cmdfunc. It's only used for a limited set of commands. ++ * The rest of the commands wouldn't be called by upper layers. For example, ++ * NAND_CMD_READOOB would never be called because we have our own versions ++ * of read_oob ops for nand_ecc_ctrl. ++ */ ++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ bool wait = false; ++ int ret = 0; ++ ++ pre_command(host, command); ++ ++ switch (command) { ++ case NAND_CMD_RESET: ++ ret = reset(host); ++ wait = true; ++ break; ++ ++ case NAND_CMD_READID: ++ nandc->buf_count = 4; ++ ret = read_id(host, column); ++ wait = true; ++ break; ++ ++ case NAND_CMD_PARAM: ++ ret = nandc_param(host); ++ wait = true; ++ break; ++ ++ case NAND_CMD_ERASE1: ++ ret = erase_block(host, page_addr); ++ wait = true; ++ break; ++ ++ case NAND_CMD_READ0: ++ /* we read the entire page for now */ ++ WARN_ON(column != 0); ++ ++ host->use_ecc = true; ++ set_address(host, 0, page_addr); ++ update_rw_regs(host, ecc->steps, true); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ WARN_ON(column != 0); ++ set_address(host, 0, page_addr); ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_NONE: ++ default: ++ break; ++ } ++ ++ if (ret) { ++ dev_err(nandc->dev, "failure executing command %d\n", ++ command); ++ free_descs(nandc); ++ return; ++ } ++ ++ if (wait) { ++ ret = submit_descs(nandc); ++ if (ret) ++ dev_err(nandc->dev, ++ "failure submitting descs for command %d\n", ++ command); ++ } ++ ++ free_descs(nandc); ++ ++ post_command(host, command); ++} ++ ++/* ++ * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read ++ * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. ++ * ++ * when using RS ECC, the HW reports the same erros when reading an erased CW, ++ * but it notifies that it is an erased CW by placing special characters at ++ * certain offsets in the buffer. ++ * ++ * verify if the page is erased or not, and fix up the page for RS ECC by ++ * replacing the special characters with 0xff. ++ */ ++static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len) ++{ ++ u8 empty1, empty2; ++ ++ /* ++ * an erased page flags an error in NAND_FLASH_STATUS, check if the page ++ * is erased by looking for 0x54s at offsets 3 and 175 from the ++ * beginning of each codeword ++ */ ++ ++ empty1 = data_buf[3]; ++ empty2 = data_buf[175]; ++ ++ /* ++ * if the erased codework markers, if they exist override them with ++ * 0xffs ++ */ ++ if ((empty1 == 0x54 && empty2 == 0xff) || ++ (empty1 == 0xff && empty2 == 0x54)) { ++ data_buf[3] = 0xff; ++ data_buf[175] = 0xff; ++ } ++ ++ /* ++ * check if the entire chunk contains 0xffs or not. if it doesn't, then ++ * restore the original values at the special offsets ++ */ ++ if (memchr_inv(data_buf, 0xff, data_len)) { ++ data_buf[3] = empty1; ++ data_buf[175] = empty2; ++ ++ return false; ++ } ++ ++ return true; ++} ++ ++struct read_stats { ++ __le32 flash; ++ __le32 buffer; ++ __le32 erased_cw; ++}; ++ ++/* ++ * reads back status registers set by the controller to notify page read ++ * errors. this is equivalent to what 'ecc->correct()' would do. ++ */ ++static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, ++ u8 *oob_buf) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ unsigned int max_bitflips = 0; ++ struct read_stats *buf; ++ int i; ++ ++ buf = (struct read_stats *)nandc->reg_read_buf; ++ nandc_read_buffer_sync(nandc, true); ++ ++ for (i = 0; i < ecc->steps; i++, buf++) { ++ u32 flash, buffer, erased_cw; ++ int data_len, oob_len; ++ ++ if (i == (ecc->steps - 1)) { ++ data_len = ecc->size - ((ecc->steps - 1) << 2); ++ oob_len = ecc->steps << 2; ++ } else { ++ data_len = host->cw_data; ++ oob_len = 0; ++ } ++ ++ flash = le32_to_cpu(buf->flash); ++ buffer = le32_to_cpu(buf->buffer); ++ erased_cw = le32_to_cpu(buf->erased_cw); ++ ++ if (flash & (FS_OP_ERR | FS_MPU_ERR)) { ++ bool erased; ++ ++ /* ignore erased codeword errors */ ++ if (host->bch_enabled) { ++ erased = (erased_cw & ERASED_CW) == ERASED_CW ? ++ true : false; ++ } else { ++ erased = erased_chunk_check_and_fixup(data_buf, ++ data_len); ++ } ++ ++ if (erased) { ++ data_buf += data_len; ++ if (oob_buf) ++ oob_buf += oob_len + ecc->bytes; ++ continue; ++ } ++ ++ if (buffer & BS_UNCORRECTABLE_BIT) { ++ int ret, ecclen, extraooblen; ++ void *eccbuf; ++ ++ eccbuf = oob_buf ? oob_buf + oob_len : NULL; ++ ecclen = oob_buf ? host->ecc_bytes_hw : 0; ++ extraooblen = oob_buf ? oob_len : 0; ++ ++ /* ++ * make sure it isn't an erased page reported ++ * as not-erased by HW because of a few bitflips ++ */ ++ ret = nand_check_erased_ecc_chunk(data_buf, ++ data_len, eccbuf, ecclen, oob_buf, ++ extraooblen, ecc->strength); ++ if (ret < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += ret; ++ max_bitflips = ++ max_t(unsigned int, max_bitflips, ret); ++ } ++ } ++ } else { ++ unsigned int stat; ++ ++ stat = buffer & BS_CORRECTABLE_ERR_MSK; ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max(max_bitflips, stat); ++ } ++ ++ data_buf += data_len; ++ if (oob_buf) ++ oob_buf += oob_len + ecc->bytes; ++ } ++ ++ return max_bitflips; ++} ++ ++/* ++ * helper to perform the actual page read operation, used by ecc->read_page(), ++ * ecc->read_oob() ++ */ ++static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, ++ u8 *oob_buf) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int i, ret; ++ ++ config_nand_page_read(nandc); ++ ++ /* queue cmd descs for each codeword */ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size, oob_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + ++ host->spare_bytes; ++ } else { ++ data_size = host->cw_data; ++ oob_size = host->ecc_bytes_hw + host->spare_bytes; ++ } ++ ++ if (nandc->props->is_bam) { ++ if (data_buf && oob_buf) { ++ nandc_set_read_loc(nandc, 0, 0, data_size, 0); ++ nandc_set_read_loc(nandc, 1, data_size, ++ oob_size, 1); ++ } else if (data_buf) { ++ nandc_set_read_loc(nandc, 0, 0, data_size, 1); ++ } else { ++ nandc_set_read_loc(nandc, 0, data_size, ++ oob_size, 1); ++ } ++ } ++ ++ config_nand_cw_read(nandc); ++ ++ if (data_buf) ++ read_data_dma(nandc, FLASH_BUF_ACC, data_buf, ++ data_size, 0); ++ ++ /* ++ * when ecc is enabled, the controller doesn't read the real ++ * or dummy bad block markers in each chunk. To maintain a ++ * consistent layout across RAW and ECC reads, we just ++ * leave the real/dummy BBM offsets empty (i.e, filled with ++ * 0xffs) ++ */ ++ if (oob_buf) { ++ int j; ++ ++ for (j = 0; j < host->bbm_size; j++) ++ *oob_buf++ = 0xff; ++ ++ read_data_dma(nandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = submit_descs(nandc); ++ if (ret) ++ dev_err(nandc->dev, "failure to read page/oob\n"); ++ ++ free_descs(nandc); ++ ++ return ret; ++} ++ ++/* ++ * a helper that copies the last step/codeword of a page (containing free oob) ++ * into our local buffer ++ */ ++static int copy_last_cw(struct qcom_nand_host *host, int page) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int size; ++ int ret; ++ ++ clear_read_regs(nandc); ++ ++ size = host->use_ecc ? host->cw_data : host->cw_size; ++ ++ /* prepare a clean read buffer */ ++ memset(nandc->data_buffer, 0xff, size); ++ ++ set_address(host, host->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(host, 1, true); ++ ++ config_nand_single_cw_page_read(nandc); ++ ++ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); ++ ++ ret = submit_descs(nandc); ++ if (ret) ++ dev_err(nandc->dev, "failed to copy last codeword\n"); ++ ++ free_descs(nandc); ++ ++ return ret; ++} ++ ++/* implements ecc->read_page() */ ++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ u8 *data_buf, *oob_buf = NULL; ++ int ret; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ data_buf = buf; ++ oob_buf = oob_required ? chip->oob_poi : NULL; ++ ++ clear_bam_transaction(nandc); ++ ret = read_page_ecc(host, data_buf, oob_buf); ++ if (ret) { ++ dev_err(nandc->dev, "failure to read page\n"); ++ return ret; ++ } ++ ++ return parse_read_errors(host, data_buf, oob_buf); ++} ++ ++/* implements ecc->read_page_raw() */ ++static int qcom_nandc_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ u8 *data_buf, *oob_buf; ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int i, ret; ++ int read_loc; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ data_buf = buf; ++ oob_buf = chip->oob_poi; ++ ++ host->use_ecc = false; ++ ++ clear_bam_transaction(nandc); ++ update_rw_regs(host, ecc->steps, true); ++ config_nand_page_read(nandc); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size1, data_size2, oob_size1, oob_size2; ++ int reg_off = FLASH_BUF_ACC; ++ ++ data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); ++ oob_size1 = host->bbm_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size2 = ecc->size - data_size1 - ++ ((ecc->steps - 1) << 2); ++ oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + ++ host->spare_bytes; ++ } else { ++ data_size2 = host->cw_data - data_size1; ++ oob_size2 = host->ecc_bytes_hw + host->spare_bytes; ++ } ++ ++ if (nandc->props->is_bam) { ++ read_loc = 0; ++ nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); ++ read_loc += data_size1; ++ ++ nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); ++ read_loc += oob_size1; ++ ++ nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); ++ read_loc += data_size2; ++ ++ nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); ++ } ++ ++ config_nand_cw_read(nandc); ++ ++ read_data_dma(nandc, reg_off, data_buf, data_size1, 0); ++ reg_off += data_size1; ++ data_buf += data_size1; ++ ++ read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); ++ reg_off += oob_size1; ++ oob_buf += oob_size1; ++ ++ read_data_dma(nandc, reg_off, data_buf, data_size2, 0); ++ reg_off += data_size2; ++ data_buf += data_size2; ++ ++ read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); ++ oob_buf += oob_size2; ++ } ++ ++ ret = submit_descs(nandc); ++ if (ret) ++ dev_err(nandc->dev, "failure to read raw page\n"); ++ ++ free_descs(nandc); ++ ++ return 0; ++} ++ ++/* implements ecc->read_oob() */ ++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ret; ++ ++ clear_read_regs(nandc); ++ clear_bam_transaction(nandc); ++ ++ host->use_ecc = true; ++ set_address(host, 0, page); ++ update_rw_regs(host, ecc->steps, true); ++ ++ ret = read_page_ecc(host, NULL, chip->oob_poi); ++ if (ret) ++ dev_err(nandc->dev, "failure to read oob\n"); ++ ++ return ret; ++} ++ ++/* implements ecc->write_page() */ ++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ u8 *data_buf, *oob_buf; ++ int i, ret; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ clear_read_regs(nandc); ++ clear_bam_transaction(nandc); ++ ++ data_buf = (u8 *)buf; ++ oob_buf = chip->oob_poi; ++ ++ host->use_ecc = true; ++ update_rw_regs(host, ecc->steps, false); ++ config_nand_page_write(nandc); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size, oob_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + ++ host->spare_bytes; ++ } else { ++ data_size = host->cw_data; ++ oob_size = ecc->bytes; ++ } ++ ++ ++ write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, ++ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); ++ ++ /* ++ * when ECC is enabled, we don't really need to write anything ++ * to oob for the first n - 1 codewords since these oob regions ++ * just contain ECC bytes that's written by the controller ++ * itself. For the last codeword, we skip the bbm positions and ++ * write to the free oob area. ++ */ ++ if (i == (ecc->steps - 1)) { ++ oob_buf += host->bbm_size; ++ ++ write_data_dma(nandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ ++ config_nand_cw_write(nandc); ++ ++ data_buf += data_size; ++ oob_buf += oob_size; ++ } ++ ++ ret = submit_descs(nandc); ++ if (ret) ++ dev_err(nandc->dev, "failure to write page\n"); ++ ++ free_descs(nandc); ++ ++ if (!ret) ++ ret = nand_prog_page_end_op(chip); ++ ++ return ret; ++} ++ ++/* implements ecc->write_page_raw() */ ++static int qcom_nandc_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, const uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ u8 *data_buf, *oob_buf; ++ int i, ret; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ clear_read_regs(nandc); ++ clear_bam_transaction(nandc); ++ ++ data_buf = (u8 *)buf; ++ oob_buf = chip->oob_poi; ++ ++ host->use_ecc = false; ++ update_rw_regs(host, ecc->steps, false); ++ config_nand_page_write(nandc); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_size1, data_size2, oob_size1, oob_size2; ++ int reg_off = FLASH_BUF_ACC; ++ ++ data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); ++ oob_size1 = host->bbm_size; ++ ++ if (i == (ecc->steps - 1)) { ++ data_size2 = ecc->size - data_size1 - ++ ((ecc->steps - 1) << 2); ++ oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + ++ host->spare_bytes; ++ } else { ++ data_size2 = host->cw_data - data_size1; ++ oob_size2 = host->ecc_bytes_hw + host->spare_bytes; ++ } ++ ++ write_data_dma(nandc, reg_off, data_buf, data_size1, ++ NAND_BAM_NO_EOT); ++ reg_off += data_size1; ++ data_buf += data_size1; ++ ++ write_data_dma(nandc, reg_off, oob_buf, oob_size1, ++ NAND_BAM_NO_EOT); ++ reg_off += oob_size1; ++ oob_buf += oob_size1; ++ ++ write_data_dma(nandc, reg_off, data_buf, data_size2, ++ NAND_BAM_NO_EOT); ++ reg_off += data_size2; ++ data_buf += data_size2; ++ ++ write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); ++ oob_buf += oob_size2; ++ ++ config_nand_cw_write(nandc); ++ } ++ ++ ret = submit_descs(nandc); ++ if (ret) ++ dev_err(nandc->dev, "failure to write raw page\n"); ++ ++ free_descs(nandc); ++ ++ if (!ret) ++ ret = nand_prog_page_end_op(chip); ++ ++ return ret; ++} ++ ++/* ++ * implements ecc->write_oob() ++ * ++ * the NAND controller cannot write only data or only oob within a codeword, ++ * since ecc is calculated for the combined codeword. we first copy the ++ * entire contents for the last codeword(data + oob), replace the old oob ++ * with the new one in chip->oob_poi, and then write the entire codeword. ++ * this read-copy-write operation results in a slight performance loss. ++ */ ++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ u8 *oob = chip->oob_poi; ++ int data_size, oob_size; ++ int ret; ++ ++ host->use_ecc = true; ++ ++ clear_bam_transaction(nandc); ++ ret = copy_last_cw(host, page); ++ if (ret) ++ return ret; ++ ++ clear_read_regs(nandc); ++ clear_bam_transaction(nandc); ++ ++ /* calculate the data and oob size for the last codeword/step */ ++ data_size = ecc->size - ((ecc->steps - 1) << 2); ++ oob_size = mtd->oobavail; ++ ++ /* override new oob content to last codeword */ ++ mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, ++ 0, mtd->oobavail); ++ ++ set_address(host, host->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(host, 1, false); ++ ++ config_nand_page_write(nandc); ++ write_data_dma(nandc, FLASH_BUF_ACC, ++ nandc->data_buffer, data_size + oob_size, 0); ++ config_nand_cw_write(nandc); ++ ++ ret = submit_descs(nandc); ++ ++ free_descs(nandc); ++ ++ if (ret) { ++ dev_err(nandc->dev, "failure to write oob\n"); ++ return -EIO; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int page, ret, bbpos, bad = 0; ++ u32 flash_status; ++ ++ page = (int)(ofs >> chip->page_shift) & chip->pagemask; ++ ++ /* ++ * configure registers for a raw sub page read, the address is set to ++ * the beginning of the last codeword, we don't care about reading ecc ++ * portion of oob. we just want the first few bytes from this codeword ++ * that contains the BBM ++ */ ++ host->use_ecc = false; ++ ++ clear_bam_transaction(nandc); ++ ret = copy_last_cw(host, page); ++ if (ret) ++ goto err; ++ ++ flash_status = le32_to_cpu(nandc->reg_read_buf[0]); ++ ++ if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) { ++ dev_warn(nandc->dev, "error when trying to read BBM\n"); ++ goto err; ++ } ++ ++ bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); ++ ++ bad = nandc->data_buffer[bbpos] != 0xff; ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); ++err: ++ return bad; ++} ++ ++static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int page, ret; ++ ++ clear_read_regs(nandc); ++ clear_bam_transaction(nandc); ++ ++ /* ++ * to mark the BBM as bad, we flash the entire last codeword with 0s. ++ * we don't care about the rest of the content in the codeword since ++ * we aren't going to use this block again ++ */ ++ memset(nandc->data_buffer, 0x00, host->cw_size); ++ ++ page = (int)(ofs >> chip->page_shift) & chip->pagemask; ++ ++ /* prepare write */ ++ host->use_ecc = false; ++ set_address(host, host->cw_size * (ecc->steps - 1), page); ++ update_rw_regs(host, 1, false); ++ ++ config_nand_page_write(nandc); ++ write_data_dma(nandc, FLASH_BUF_ACC, ++ nandc->data_buffer, host->cw_size, 0); ++ config_nand_cw_write(nandc); ++ ++ ret = submit_descs(nandc); ++ ++ free_descs(nandc); ++ ++ if (ret) { ++ dev_err(nandc->dev, "failure to update BBM\n"); ++ return -EIO; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++/* ++ * the three functions below implement chip->read_byte(), chip->read_buf() ++ * and chip->write_buf() respectively. these aren't used for ++ * reading/writing page data, they are used for smaller data like reading ++ * id, status etc ++ */ ++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ u8 *buf = nandc->data_buffer; ++ u8 ret = 0x0; ++ ++ if (host->last_command == NAND_CMD_STATUS) { ++ ret = host->status; ++ ++ host->status = NAND_STATUS_READY | NAND_STATUS_WP; ++ ++ return ret; ++ } ++ ++ if (nandc->buf_start < nandc->buf_count) ++ ret = buf[nandc->buf_start++]; ++ ++ return ret; ++} ++ ++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); ++ ++ memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len); ++ nandc->buf_start += real_len; ++} ++ ++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); ++ ++ memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len); ++ ++ nandc->buf_start += real_len; ++} ++ ++/* we support only one external chip for now */ ++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ ++ if (chipnr <= 0) ++ return; ++ ++ dev_warn(nandc->dev, "invalid chip select\n"); ++} ++ ++/* ++ * NAND controller page layout info ++ * ++ * Layout with ECC enabled: ++ * ++ * |----------------------| |---------------------------------| ++ * | xx.......yy| | *********xx.......yy| ++ * | DATA xx..ECC..yy| | DATA **SPARE**xx..ECC..yy| ++ * | (516) xx.......yy| | (516-n*4) **(n*4)**xx.......yy| ++ * | xx.......yy| | *********xx.......yy| ++ * |----------------------| |---------------------------------| ++ * codeword 1,2..n-1 codeword n ++ * <---(528/532 Bytes)--> <-------(528/532 Bytes)---------> ++ * ++ * n = Number of codewords in the page ++ * . = ECC bytes ++ * * = Spare/free bytes ++ * x = Unused byte(s) ++ * y = Reserved byte(s) ++ * ++ * 2K page: n = 4, spare = 16 bytes ++ * 4K page: n = 8, spare = 32 bytes ++ * 8K page: n = 16, spare = 64 bytes ++ * ++ * the qcom nand controller operates at a sub page/codeword level. each ++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. ++ * the number of ECC bytes vary based on the ECC strength and the bus width. ++ * ++ * the first n - 1 codewords contains 516 bytes of user data, the remaining ++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains ++ * both user data and spare(oobavail) bytes that sum up to 516 bytes. ++ * ++ * When we access a page with ECC enabled, the reserved bytes(s) are not ++ * accessible at all. When reading, we fill up these unreadable positions ++ * with 0xffs. When writing, the controller skips writing the inaccessible ++ * bytes. ++ * ++ * Layout with ECC disabled: ++ * ++ * |------------------------------| |---------------------------------------| ++ * | yy xx.......| | bb *********xx.......| ++ * | DATA1 yy DATA2 xx..ECC..| | DATA1 bb DATA2 **SPARE**xx..ECC..| ++ * | (size1) yy (size2) xx.......| | (size1) bb (size2) **(n*4)**xx.......| ++ * | yy xx.......| | bb *********xx.......| ++ * |------------------------------| |---------------------------------------| ++ * codeword 1,2..n-1 codeword n ++ * <-------(528/532 Bytes)------> <-----------(528/532 Bytes)-----------> ++ * ++ * n = Number of codewords in the page ++ * . = ECC bytes ++ * * = Spare/free bytes ++ * x = Unused byte(s) ++ * y = Dummy Bad Bock byte(s) ++ * b = Real Bad Block byte(s) ++ * size1/size2 = function of codeword size and 'n' ++ * ++ * when the ECC block is disabled, one reserved byte (or two for 16 bit bus ++ * width) is now accessible. For the first n - 1 codewords, these are dummy Bad ++ * Block Markers. In the last codeword, this position contains the real BBM ++ * ++ * In order to have a consistent layout between RAW and ECC modes, we assume ++ * the following OOB layout arrangement: ++ * ++ * |-----------| |--------------------| ++ * |yyxx.......| |bb*********xx.......| ++ * |yyxx..ECC..| |bb*FREEOOB*xx..ECC..| ++ * |yyxx.......| |bb*********xx.......| ++ * |yyxx.......| |bb*********xx.......| ++ * |-----------| |--------------------| ++ * first n - 1 nth OOB region ++ * OOB regions ++ * ++ * n = Number of codewords in the page ++ * . = ECC bytes ++ * * = FREE OOB bytes ++ * y = Dummy bad block byte(s) (inaccessible when ECC enabled) ++ * x = Unused byte(s) ++ * b = Real bad block byte(s) (inaccessible when ECC enabled) ++ * ++ * This layout is read as is when ECC is disabled. When ECC is enabled, the ++ * inaccessible Bad Block byte(s) are ignored when we write to a page/oob, ++ * and assumed as 0xffs when we read a page/oob. The ECC, unused and ++ * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is ++ * the sum of the three). ++ */ ++static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section > 1) ++ return -ERANGE; ++ ++ if (!section) { ++ oobregion->length = (ecc->bytes * (ecc->steps - 1)) + ++ host->bbm_size; ++ oobregion->offset = 0; ++ } else { ++ oobregion->length = host->ecc_bytes_hw + host->spare_bytes; ++ oobregion->offset = mtd->oobsize - oobregion->length; ++ } ++ ++ return 0; ++} ++ ++static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct qcom_nand_host *host = to_qcom_nand_host(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = ecc->steps * 4; ++ oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = { ++ .ecc = qcom_nand_ooblayout_ecc, ++ .free = qcom_nand_ooblayout_free, ++}; ++ ++static int qcom_nand_host_setup(struct qcom_nand_host *host) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ int cwperpage, bad_block_byte; ++ bool wide_bus; ++ int ecc_mode = 1; ++ ++ /* ++ * the controller requires each step consists of 512 bytes of data. ++ * bail out if DT has populated a wrong step size. ++ */ ++ if (ecc->size != NANDC_STEP_SIZE) { ++ dev_err(nandc->dev, "invalid ecc size\n"); ++ return -EINVAL; ++ } ++ ++ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; ++ ++ if (ecc->strength >= 8) { ++ /* 8 bit ECC defaults to BCH ECC on all platforms */ ++ host->bch_enabled = true; ++ ecc_mode = 1; ++ ++ if (wide_bus) { ++ host->ecc_bytes_hw = 14; ++ host->spare_bytes = 0; ++ host->bbm_size = 2; ++ } else { ++ host->ecc_bytes_hw = 13; ++ host->spare_bytes = 2; ++ host->bbm_size = 1; ++ } ++ } else { ++ /* ++ * if the controller supports BCH for 4 bit ECC, the controller ++ * uses lesser bytes for ECC. If RS is used, the ECC bytes is ++ * always 10 bytes ++ */ ++ if (nandc->props->ecc_modes & ECC_BCH_4BIT) { ++ /* BCH */ ++ host->bch_enabled = true; ++ ecc_mode = 0; ++ ++ if (wide_bus) { ++ host->ecc_bytes_hw = 8; ++ host->spare_bytes = 2; ++ host->bbm_size = 2; ++ } else { ++ host->ecc_bytes_hw = 7; ++ host->spare_bytes = 4; ++ host->bbm_size = 1; ++ } ++ } else { ++ /* RS */ ++ host->ecc_bytes_hw = 10; ++ ++ if (wide_bus) { ++ host->spare_bytes = 0; ++ host->bbm_size = 2; ++ } else { ++ host->spare_bytes = 1; ++ host->bbm_size = 1; ++ } ++ } ++ } ++ ++ /* ++ * we consider ecc->bytes as the sum of all the non-data content in a ++ * step. It gives us a clean representation of the oob area (even if ++ * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit ++ * ECC and 12 bytes for 4 bit ECC ++ */ ++ ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size; ++ ++ ecc->read_page = qcom_nandc_read_page; ++ ecc->read_page_raw = qcom_nandc_read_page_raw; ++ ecc->read_oob = qcom_nandc_read_oob; ++ ecc->write_page = qcom_nandc_write_page; ++ ecc->write_page_raw = qcom_nandc_write_page_raw; ++ ecc->write_oob = qcom_nandc_write_oob; ++ ++ ecc->mode = NAND_ECC_HW; ++ ++ mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); ++ ++ cwperpage = mtd->writesize / ecc->size; ++ nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, ++ cwperpage); ++ ++ /* ++ * DATA_UD_BYTES varies based on whether the read/write command protects ++ * spare data with ECC too. We protect spare data by default, so we set ++ * it to main + spare data, which are 512 and 4 bytes respectively. ++ */ ++ host->cw_data = 516; ++ ++ /* ++ * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes ++ * for 8 bit ECC ++ */ ++ host->cw_size = host->cw_data + ecc->bytes; ++ ++ if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { ++ dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); ++ return -EINVAL; ++ } ++ ++ bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; ++ ++ host->cfg0 = (cwperpage - 1) << CW_PER_PAGE ++ | host->cw_data << UD_SIZE_BYTES ++ | 0 << DISABLE_STATUS_AFTER_WRITE ++ | 5 << NUM_ADDR_CYCLES ++ | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS ++ | 0 << STATUS_BFR_READ ++ | 1 << SET_RD_MODE_AFTER_STATUS ++ | host->spare_bytes << SPARE_SIZE_BYTES; ++ ++ host->cfg1 = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | bad_block_byte << BAD_BLOCK_BYTE_NUM ++ | 0 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | wide_bus << WIDE_FLASH ++ | host->bch_enabled << ENABLE_BCH_ECC; ++ ++ host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE ++ | host->cw_size << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES; ++ ++ host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | wide_bus << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE; ++ ++ host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE ++ | 0 << ECC_SW_RESET ++ | host->cw_data << ECC_NUM_DATA_BYTES ++ | 1 << ECC_FORCE_CLK_OPEN ++ | ecc_mode << ECC_MODE ++ | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; ++ ++ host->ecc_buf_cfg = 0x203 << NUM_STEPS; ++ ++ host->clrflashstatus = FS_READY_BSY_N; ++ host->clrreadstatus = 0xc0; ++ nandc->regs->erased_cw_detect_cfg_clr = ++ cpu_to_le32(CLR_ERASED_PAGE_DET); ++ nandc->regs->erased_cw_detect_cfg_set = ++ cpu_to_le32(SET_ERASED_PAGE_DET); ++ ++ dev_dbg(nandc->dev, ++ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", ++ host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg, ++ host->cw_size, host->cw_data, ecc->strength, ecc->bytes, ++ cwperpage); ++ ++ return 0; ++} ++ ++static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) ++{ ++ int ret; ++ ++ ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); ++ if (ret) { ++ dev_err(nandc->dev, "failed to set DMA mask\n"); ++ return ret; ++ } ++ ++ /* ++ * we use the internal buffer for reading ONFI params, reading small ++ * data like ID and status, and preforming read-copy-write operations ++ * when writing to a codeword partially. 532 is the maximum possible ++ * size of a codeword for our nand controller ++ */ ++ nandc->buf_size = 532; ++ ++ nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, ++ GFP_KERNEL); ++ if (!nandc->data_buffer) ++ return -ENOMEM; ++ ++ nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), ++ GFP_KERNEL); ++ if (!nandc->regs) ++ return -ENOMEM; ++ ++ nandc->reg_read_buf = devm_kzalloc(nandc->dev, ++ MAX_REG_RD * sizeof(*nandc->reg_read_buf), ++ GFP_KERNEL); ++ if (!nandc->reg_read_buf) ++ return -ENOMEM; ++ ++ if (nandc->props->is_bam) { ++ nandc->reg_read_dma = ++ dma_map_single(nandc->dev, nandc->reg_read_buf, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { ++ dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); ++ return -EIO; ++ } ++ ++ nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); ++ if (!nandc->tx_chan) { ++ dev_err(nandc->dev, "failed to request tx channel\n"); ++ return -ENODEV; ++ } ++ ++ nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); ++ if (!nandc->rx_chan) { ++ dev_err(nandc->dev, "failed to request rx channel\n"); ++ return -ENODEV; ++ } ++ ++ nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); ++ if (!nandc->cmd_chan) { ++ dev_err(nandc->dev, "failed to request cmd channel\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * Initially allocate BAM transaction to read ONFI param page. ++ * After detecting all the devices, this BAM transaction will ++ * be freed and the next BAM tranasction will be allocated with ++ * maximum codeword size ++ */ ++ nandc->max_cwperpage = 1; ++ nandc->bam_txn = alloc_bam_transaction(nandc); ++ if (!nandc->bam_txn) { ++ dev_err(nandc->dev, ++ "failed to allocate bam transaction\n"); ++ return -ENOMEM; ++ } ++ } else { ++ nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); ++ if (!nandc->chan) { ++ dev_err(nandc->dev, ++ "failed to request slave channel\n"); ++ return -ENODEV; ++ } ++ } ++ ++ INIT_LIST_HEAD(&nandc->desc_list); ++ INIT_LIST_HEAD(&nandc->host_list); ++ ++ nand_controller_init(&nandc->controller); ++ ++ return 0; ++} ++ ++static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) ++{ ++ if (nandc->props->is_bam) { ++ if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) ++ dma_unmap_single(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ ++ if (nandc->tx_chan) ++ dma_release_channel(nandc->tx_chan); ++ ++ if (nandc->rx_chan) ++ dma_release_channel(nandc->rx_chan); ++ ++ if (nandc->cmd_chan) ++ dma_release_channel(nandc->cmd_chan); ++ } else { ++ if (nandc->chan) ++ dma_release_channel(nandc->chan); ++ } ++} ++ ++/* one time setup of a few nand controller registers */ ++static int qcom_nandc_setup(struct qcom_nand_controller *nandc) ++{ ++ u32 nand_ctrl; ++ ++ /* kill onenand */ ++ nandc_write(nandc, SFLASHC_BURST_CFG, 0); ++ nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), ++ NAND_DEV_CMD_VLD_VAL); ++ ++ /* enable ADM or BAM DMA */ ++ if (nandc->props->is_bam) { ++ nand_ctrl = nandc_read(nandc, NAND_CTRL); ++ nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); ++ } else { ++ nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); ++ } ++ ++ /* save the original values of these registers */ ++ nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); ++ nandc->vld = NAND_DEV_CMD_VLD_VAL; ++ ++ return 0; ++} ++ ++static int qcom_nand_host_init(struct qcom_nand_controller *nandc, ++ struct qcom_nand_host *host, ++ struct device_node *dn) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct device *dev = nandc->dev; ++ int ret; ++ ++ ret = of_property_read_u32(dn, "reg", &host->cs); ++ if (ret) { ++ dev_err(dev, "can't get chip-select\n"); ++ return -ENXIO; ++ } ++ ++ nand_set_flash_node(chip, dn); ++ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs); ++ if (!mtd->name) ++ return -ENOMEM; ++ ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = dev; ++ ++ chip->cmdfunc = qcom_nandc_command; ++ chip->select_chip = qcom_nandc_select_chip; ++ chip->read_byte = qcom_nandc_read_byte; ++ chip->read_buf = qcom_nandc_read_buf; ++ chip->write_buf = qcom_nandc_write_buf; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ /* ++ * the bad block marker is readable only when we read the last codeword ++ * of a page with ECC disabled. currently, the nand_base and nand_bbt ++ * helpers don't allow us to read BB from a nand chip with ECC ++ * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad ++ * and block_markbad helpers until we permanently switch to using ++ * MTD_OPS_RAW for all drivers (with the help of badblockbits) ++ */ ++ chip->block_bad = qcom_nandc_block_bad; ++ chip->block_markbad = qcom_nandc_block_markbad; ++ ++ chip->controller = &nandc->controller; ++ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER | ++ NAND_SKIP_BBTSCAN; ++ ++ /* set up initial status value */ ++ host->status = NAND_STATUS_READY | NAND_STATUS_WP; ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ return ret; ++ ++ ret = qcom_nand_host_setup(host); ++ ++ return ret; ++} ++ ++static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc, ++ struct qcom_nand_host *host, ++ struct device_node *dn) ++{ ++ struct nand_chip *chip = &host->chip; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) ++ return ret; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) ++ nand_cleanup(mtd_to_nand(mtd)); ++ ++ return ret; ++} ++ ++static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) ++{ ++ struct device *dev = nandc->dev; ++ struct device_node *dn = dev->of_node, *child; ++ struct qcom_nand_host *host, *tmp; ++ int ret; ++ ++ for_each_available_child_of_node(dn, child) { ++ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); ++ if (!host) { ++ of_node_put(child); ++ return -ENOMEM; ++ } ++ ++ ret = qcom_nand_host_init(nandc, host, child); ++ if (ret) { ++ devm_kfree(dev, host); ++ continue; ++ } ++ ++ list_add_tail(&host->node, &nandc->host_list); ++ } ++ ++ if (list_empty(&nandc->host_list)) ++ return -ENODEV; ++ ++ if (nandc->props->is_bam) { ++ free_bam_transaction(nandc); ++ nandc->bam_txn = alloc_bam_transaction(nandc); ++ if (!nandc->bam_txn) { ++ dev_err(nandc->dev, ++ "failed to allocate bam transaction\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ list_for_each_entry_safe(host, tmp, &nandc->host_list, node) { ++ ret = qcom_nand_mtd_register(nandc, host, child); ++ if (ret) { ++ list_del(&host->node); ++ devm_kfree(dev, host); ++ } ++ } ++ ++ if (list_empty(&nandc->host_list)) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++/* parse custom DT properties here */ ++static int qcom_nandc_parse_dt(struct platform_device *pdev) ++{ ++ struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); ++ struct device_node *np = nandc->dev->of_node; ++ int ret; ++ ++ if (!nandc->props->is_bam) { ++ ret = of_property_read_u32(np, "qcom,cmd-crci", ++ &nandc->cmd_crci); ++ if (ret) { ++ dev_err(nandc->dev, "command CRCI unspecified\n"); ++ return ret; ++ } ++ ++ ret = of_property_read_u32(np, "qcom,data-crci", ++ &nandc->data_crci); ++ if (ret) { ++ dev_err(nandc->dev, "data CRCI unspecified\n"); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int qcom_nandc_probe(struct platform_device *pdev) ++{ ++ struct qcom_nand_controller *nandc; ++ const void *dev_data; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ int ret; ++ ++ nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); ++ if (!nandc) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, nandc); ++ nandc->dev = dev; ++ ++ dev_data = of_device_get_match_data(dev); ++ if (!dev_data) { ++ dev_err(&pdev->dev, "failed to get device data\n"); ++ return -ENODEV; ++ } ++ ++ nandc->props = dev_data; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nandc->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(nandc->base)) ++ return PTR_ERR(nandc->base); ++ ++ nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start); ++ ++ nandc->core_clk = devm_clk_get(dev, "core"); ++ if (IS_ERR(nandc->core_clk)) ++ return PTR_ERR(nandc->core_clk); ++ ++ nandc->aon_clk = devm_clk_get(dev, "aon"); ++ if (IS_ERR(nandc->aon_clk)) ++ return PTR_ERR(nandc->aon_clk); ++ ++ ret = qcom_nandc_parse_dt(pdev); ++ if (ret) ++ return ret; ++ ++ ret = qcom_nandc_alloc(nandc); ++ if (ret) ++ goto err_core_clk; ++ ++ ret = clk_prepare_enable(nandc->core_clk); ++ if (ret) ++ goto err_core_clk; ++ ++ ret = clk_prepare_enable(nandc->aon_clk); ++ if (ret) ++ goto err_aon_clk; ++ ++ ret = qcom_nandc_setup(nandc); ++ if (ret) ++ goto err_setup; ++ ++ ret = qcom_probe_nand_devices(nandc); ++ if (ret) ++ goto err_setup; ++ ++ return 0; ++ ++err_setup: ++ clk_disable_unprepare(nandc->aon_clk); ++err_aon_clk: ++ clk_disable_unprepare(nandc->core_clk); ++err_core_clk: ++ qcom_nandc_unalloc(nandc); ++ ++ return ret; ++} ++ ++static int qcom_nandc_remove(struct platform_device *pdev) ++{ ++ struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); ++ struct qcom_nand_host *host; ++ ++ list_for_each_entry(host, &nandc->host_list, node) ++ nand_release(nand_to_mtd(&host->chip)); ++ ++ qcom_nandc_unalloc(nandc); ++ ++ clk_disable_unprepare(nandc->aon_clk); ++ clk_disable_unprepare(nandc->core_clk); ++ ++ return 0; ++} ++ ++static const struct qcom_nandc_props ipq806x_nandc_props = { ++ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), ++ .is_bam = false, ++ .dev_cmd_reg_start = 0x0, ++}; ++ ++static const struct qcom_nandc_props ipq4019_nandc_props = { ++ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), ++ .is_bam = true, ++ .dev_cmd_reg_start = 0x0, ++}; ++ ++static const struct qcom_nandc_props ipq8074_nandc_props = { ++ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), ++ .is_bam = true, ++ .dev_cmd_reg_start = 0x7000, ++}; ++ ++/* ++ * data will hold a struct pointer containing more differences once we support ++ * more controller variants ++ */ ++static const struct of_device_id qcom_nandc_of_match[] = { ++ { ++ .compatible = "qcom,ipq806x-nand", ++ .data = &ipq806x_nandc_props, ++ }, ++ { ++ .compatible = "qcom,ipq4019-nand", ++ .data = &ipq4019_nandc_props, ++ }, ++ { ++ .compatible = "qcom,ipq8074-nand", ++ .data = &ipq8074_nandc_props, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); ++ ++static struct platform_driver qcom_nandc_driver = { ++ .driver = { ++ .name = "qcom-nandc", ++ .of_match_table = qcom_nandc_of_match, ++ }, ++ .probe = qcom_nandc_probe, ++ .remove = qcom_nandc_remove, ++}; ++module_platform_driver(qcom_nandc_driver); ++ ++MODULE_AUTHOR("Archit Taneja "); ++MODULE_DESCRIPTION("Qualcomm NAND Controller driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c +new file mode 100644 +index 00000000..595635b +--- /dev/null ++++ b/drivers/mtd/nand/raw/r852.c +@@ -0,0 +1,1079 @@ ++/* ++ * Copyright © 2009 - Maxim Levitsky ++ * driver for Ricoh xD readers ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sm_common.h" ++#include "r852.h" ++ ++ ++static bool r852_enable_dma = 1; ++module_param(r852_enable_dma, bool, S_IRUGO); ++MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); ++ ++static int debug; ++module_param(debug, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Debug level (0-2)"); ++ ++/* read register */ ++static inline uint8_t r852_read_reg(struct r852_device *dev, int address) ++{ ++ uint8_t reg = readb(dev->mmio + address); ++ return reg; ++} ++ ++/* write register */ ++static inline void r852_write_reg(struct r852_device *dev, ++ int address, uint8_t value) ++{ ++ writeb(value, dev->mmio + address); ++ mmiowb(); ++} ++ ++ ++/* read dword sized register */ ++static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address) ++{ ++ uint32_t reg = le32_to_cpu(readl(dev->mmio + address)); ++ return reg; ++} ++ ++/* write dword sized register */ ++static inline void r852_write_reg_dword(struct r852_device *dev, ++ int address, uint32_t value) ++{ ++ writel(cpu_to_le32(value), dev->mmio + address); ++ mmiowb(); ++} ++ ++/* returns pointer to our private structure */ ++static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ return nand_get_controller_data(chip); ++} ++ ++ ++/* check if controller supports dma */ ++static void r852_dma_test(struct r852_device *dev) ++{ ++ dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) & ++ (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2); ++ ++ if (!dev->dma_usable) ++ message("Non dma capable device detected, dma disabled"); ++ ++ if (!r852_enable_dma) { ++ message("disabling dma on user request"); ++ dev->dma_usable = 0; ++ } ++} ++ ++/* ++ * Enable dma. Enables ether first or second stage of the DMA, ++ * Expects dev->dma_dir and dev->dma_state be set ++ */ ++static void r852_dma_enable(struct r852_device *dev) ++{ ++ uint8_t dma_reg, dma_irq_reg; ++ ++ /* Set up dma settings */ ++ dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS); ++ dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY); ++ ++ if (dev->dma_dir) ++ dma_reg |= R852_DMA_READ; ++ ++ if (dev->dma_state == DMA_INTERNAL) { ++ dma_reg |= R852_DMA_INTERNAL; ++ /* Precaution to make sure HW doesn't write */ ++ /* to random kernel memory */ ++ r852_write_reg_dword(dev, R852_DMA_ADDR, ++ cpu_to_le32(dev->phys_bounce_buffer)); ++ } else { ++ dma_reg |= R852_DMA_MEMORY; ++ r852_write_reg_dword(dev, R852_DMA_ADDR, ++ cpu_to_le32(dev->phys_dma_addr)); ++ } ++ ++ /* Precaution: make sure write reached the device */ ++ r852_read_reg_dword(dev, R852_DMA_ADDR); ++ ++ r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); ++ ++ /* Set dma irq */ ++ dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); ++ r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, ++ dma_irq_reg | ++ R852_DMA_IRQ_INTERNAL | ++ R852_DMA_IRQ_ERROR | ++ R852_DMA_IRQ_MEMORY); ++} ++ ++/* ++ * Disable dma, called from the interrupt handler, which specifies ++ * success of the operation via 'error' argument ++ */ ++static void r852_dma_done(struct r852_device *dev, int error) ++{ ++ WARN_ON(dev->dma_stage == 0); ++ ++ r852_write_reg_dword(dev, R852_DMA_IRQ_STA, ++ r852_read_reg_dword(dev, R852_DMA_IRQ_STA)); ++ ++ r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); ++ r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); ++ ++ /* Precaution to make sure HW doesn't write to random kernel memory */ ++ r852_write_reg_dword(dev, R852_DMA_ADDR, ++ cpu_to_le32(dev->phys_bounce_buffer)); ++ r852_read_reg_dword(dev, R852_DMA_ADDR); ++ ++ dev->dma_error = error; ++ dev->dma_stage = 0; ++ ++ if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) ++ pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, ++ dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); ++} ++ ++/* ++ * Wait, till dma is done, which includes both phases of it ++ */ ++static int r852_dma_wait(struct r852_device *dev) ++{ ++ long timeout = wait_for_completion_timeout(&dev->dma_done, ++ msecs_to_jiffies(1000)); ++ if (!timeout) { ++ dbg("timeout waiting for DMA interrupt"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Read/Write one page using dma. Only pages can be read (512 bytes) ++*/ ++static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) ++{ ++ int bounce = 0; ++ unsigned long flags; ++ int error; ++ ++ dev->dma_error = 0; ++ ++ /* Set dma direction */ ++ dev->dma_dir = do_read; ++ dev->dma_stage = 1; ++ reinit_completion(&dev->dma_done); ++ ++ dbg_verbose("doing dma %s ", do_read ? "read" : "write"); ++ ++ /* Set initial dma state: for reading first fill on board buffer, ++ from device, for writes first fill the buffer from memory*/ ++ dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; ++ ++ /* if incoming buffer is not page aligned, we should do bounce */ ++ if ((unsigned long)buf & (R852_DMA_LEN-1)) ++ bounce = 1; ++ ++ if (!bounce) { ++ dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, ++ R852_DMA_LEN, ++ (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); ++ ++ if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) ++ bounce = 1; ++ } ++ ++ if (bounce) { ++ dbg_verbose("dma: using bounce buffer"); ++ dev->phys_dma_addr = dev->phys_bounce_buffer; ++ if (!do_read) ++ memcpy(dev->bounce_buffer, buf, R852_DMA_LEN); ++ } ++ ++ /* Enable DMA */ ++ spin_lock_irqsave(&dev->irqlock, flags); ++ r852_dma_enable(dev); ++ spin_unlock_irqrestore(&dev->irqlock, flags); ++ ++ /* Wait till complete */ ++ error = r852_dma_wait(dev); ++ ++ if (error) { ++ r852_dma_done(dev, error); ++ return; ++ } ++ ++ if (do_read && bounce) ++ memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN); ++} ++ ++/* ++ * Program data lines of the nand chip to send data to it ++ */ ++static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ uint32_t reg; ++ ++ /* Don't allow any access to hardware if we suspect card removal */ ++ if (dev->card_unstable) ++ return; ++ ++ /* Special case for whole sector read */ ++ if (len == R852_DMA_LEN && dev->dma_usable) { ++ r852_do_dma(dev, (uint8_t *)buf, 0); ++ return; ++ } ++ ++ /* write DWORD chinks - faster */ ++ while (len >= 4) { ++ reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; ++ r852_write_reg_dword(dev, R852_DATALINE, reg); ++ buf += 4; ++ len -= 4; ++ ++ } ++ ++ /* write rest */ ++ while (len > 0) { ++ r852_write_reg(dev, R852_DATALINE, *buf++); ++ len--; ++ } ++} ++ ++/* ++ * Read data lines of the nand chip to retrieve data ++ */ ++static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ uint32_t reg; ++ ++ if (dev->card_unstable) { ++ /* since we can't signal error here, at least, return ++ predictable buffer */ ++ memset(buf, 0, len); ++ return; ++ } ++ ++ /* special case for whole sector read */ ++ if (len == R852_DMA_LEN && dev->dma_usable) { ++ r852_do_dma(dev, buf, 1); ++ return; ++ } ++ ++ /* read in dword sized chunks */ ++ while (len >= 4) { ++ ++ reg = r852_read_reg_dword(dev, R852_DATALINE); ++ *buf++ = reg & 0xFF; ++ *buf++ = (reg >> 8) & 0xFF; ++ *buf++ = (reg >> 16) & 0xFF; ++ *buf++ = (reg >> 24) & 0xFF; ++ len -= 4; ++ } ++ ++ /* read the reset by bytes */ ++ while (len--) ++ *buf++ = r852_read_reg(dev, R852_DATALINE); ++} ++ ++/* ++ * Read one byte from nand chip ++ */ ++static uint8_t r852_read_byte(struct mtd_info *mtd) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ ++ /* Same problem as in r852_read_buf.... */ ++ if (dev->card_unstable) ++ return 0; ++ ++ return r852_read_reg(dev, R852_DATALINE); ++} ++ ++/* ++ * Control several chip lines & send commands ++ */ ++static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ ++ if (dev->card_unstable) ++ return; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ ++ dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND | ++ R852_CTL_ON | R852_CTL_CARDENABLE); ++ ++ if (ctrl & NAND_ALE) ++ dev->ctlreg |= R852_CTL_DATA; ++ ++ if (ctrl & NAND_CLE) ++ dev->ctlreg |= R852_CTL_COMMAND; ++ ++ if (ctrl & NAND_NCE) ++ dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON); ++ else ++ dev->ctlreg &= ~R852_CTL_WRITE; ++ ++ /* when write is stareted, enable write access */ ++ if (dat == NAND_CMD_ERASE1) ++ dev->ctlreg |= R852_CTL_WRITE; ++ ++ r852_write_reg(dev, R852_CTL, dev->ctlreg); ++ } ++ ++ /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need ++ to set write mode */ ++ if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) { ++ dev->ctlreg |= R852_CTL_WRITE; ++ r852_write_reg(dev, R852_CTL, dev->ctlreg); ++ } ++ ++ if (dat != NAND_CMD_NONE) ++ r852_write_reg(dev, R852_DATALINE, dat); ++} ++ ++/* ++ * Wait till card is ready. ++ * based on nand_wait, but returns errors on DMA error ++ */ ++static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct r852_device *dev = nand_get_controller_data(chip); ++ ++ unsigned long timeout; ++ u8 status; ++ ++ timeout = jiffies + (chip->state == FL_ERASING ? ++ msecs_to_jiffies(400) : msecs_to_jiffies(20)); ++ ++ while (time_before(jiffies, timeout)) ++ if (chip->dev_ready(mtd)) ++ break; ++ ++ nand_status_op(chip, &status); ++ ++ /* Unfortunelly, no way to send detailed error status... */ ++ if (dev->dma_error) { ++ status |= NAND_STATUS_FAIL; ++ dev->dma_error = 0; ++ } ++ return status; ++} ++ ++/* ++ * Check if card is ready ++ */ ++ ++static int r852_ready(struct mtd_info *mtd) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); ++} ++ ++ ++/* ++ * Set ECC engine mode ++*/ ++ ++static void r852_ecc_hwctl(struct mtd_info *mtd, int mode) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ ++ if (dev->card_unstable) ++ return; ++ ++ switch (mode) { ++ case NAND_ECC_READ: ++ case NAND_ECC_WRITE: ++ /* enable ecc generation/check*/ ++ dev->ctlreg |= R852_CTL_ECC_ENABLE; ++ ++ /* flush ecc buffer */ ++ r852_write_reg(dev, R852_CTL, ++ dev->ctlreg | R852_CTL_ECC_ACCESS); ++ ++ r852_read_reg_dword(dev, R852_DATALINE); ++ r852_write_reg(dev, R852_CTL, dev->ctlreg); ++ return; ++ ++ case NAND_ECC_READSYN: ++ /* disable ecc generation */ ++ dev->ctlreg &= ~R852_CTL_ECC_ENABLE; ++ r852_write_reg(dev, R852_CTL, dev->ctlreg); ++ } ++} ++ ++/* ++ * Calculate ECC, only used for writes ++ */ ++ ++static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, ++ uint8_t *ecc_code) ++{ ++ struct r852_device *dev = r852_get_dev(mtd); ++ struct sm_oob *oob = (struct sm_oob *)ecc_code; ++ uint32_t ecc1, ecc2; ++ ++ if (dev->card_unstable) ++ return 0; ++ ++ dev->ctlreg &= ~R852_CTL_ECC_ENABLE; ++ r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); ++ ++ ecc1 = r852_read_reg_dword(dev, R852_DATALINE); ++ ecc2 = r852_read_reg_dword(dev, R852_DATALINE); ++ ++ oob->ecc1[0] = (ecc1) & 0xFF; ++ oob->ecc1[1] = (ecc1 >> 8) & 0xFF; ++ oob->ecc1[2] = (ecc1 >> 16) & 0xFF; ++ ++ oob->ecc2[0] = (ecc2) & 0xFF; ++ oob->ecc2[1] = (ecc2 >> 8) & 0xFF; ++ oob->ecc2[2] = (ecc2 >> 16) & 0xFF; ++ ++ r852_write_reg(dev, R852_CTL, dev->ctlreg); ++ return 0; ++} ++ ++/* ++ * Correct the data using ECC, hw did almost everything for us ++ */ ++ ++static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, ++ uint8_t *read_ecc, uint8_t *calc_ecc) ++{ ++ uint32_t ecc_reg; ++ uint8_t ecc_status, err_byte; ++ int i, error = 0; ++ ++ struct r852_device *dev = r852_get_dev(mtd); ++ ++ if (dev->card_unstable) ++ return 0; ++ ++ if (dev->dma_error) { ++ dev->dma_error = 0; ++ return -EIO; ++ } ++ ++ r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); ++ ecc_reg = r852_read_reg_dword(dev, R852_DATALINE); ++ r852_write_reg(dev, R852_CTL, dev->ctlreg); ++ ++ for (i = 0 ; i <= 1 ; i++) { ++ ++ ecc_status = (ecc_reg >> 8) & 0xFF; ++ ++ /* ecc uncorrectable error */ ++ if (ecc_status & R852_ECC_FAIL) { ++ dbg("ecc: unrecoverable error, in half %d", i); ++ error = -EBADMSG; ++ goto exit; ++ } ++ ++ /* correctable error */ ++ if (ecc_status & R852_ECC_CORRECTABLE) { ++ ++ err_byte = ecc_reg & 0xFF; ++ dbg("ecc: recoverable error, " ++ "in half %d, byte %d, bit %d", i, ++ err_byte, ecc_status & R852_ECC_ERR_BIT_MSK); ++ ++ dat[err_byte] ^= ++ 1 << (ecc_status & R852_ECC_ERR_BIT_MSK); ++ error++; ++ } ++ ++ dat += 256; ++ ecc_reg >>= 16; ++ } ++exit: ++ return error; ++} ++ ++/* ++ * This is copy of nand_read_oob_std ++ * nand_read_oob_syndrome assumes we can send column address - we can't ++ */ ++static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); ++} ++ ++/* ++ * Start the nand engine ++ */ ++ ++static void r852_engine_enable(struct r852_device *dev) ++{ ++ if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { ++ r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); ++ r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); ++ } else { ++ r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); ++ r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); ++ } ++ msleep(300); ++ r852_write_reg(dev, R852_CTL, 0); ++} ++ ++ ++/* ++ * Stop the nand engine ++ */ ++ ++static void r852_engine_disable(struct r852_device *dev) ++{ ++ r852_write_reg_dword(dev, R852_HW, 0); ++ r852_write_reg(dev, R852_CTL, R852_CTL_RESET); ++} ++ ++/* ++ * Test if card is present ++ */ ++ ++static void r852_card_update_present(struct r852_device *dev) ++{ ++ unsigned long flags; ++ uint8_t reg; ++ ++ spin_lock_irqsave(&dev->irqlock, flags); ++ reg = r852_read_reg(dev, R852_CARD_STA); ++ dev->card_detected = !!(reg & R852_CARD_STA_PRESENT); ++ spin_unlock_irqrestore(&dev->irqlock, flags); ++} ++ ++/* ++ * Update card detection IRQ state according to current card state ++ * which is read in r852_card_update_present ++ */ ++static void r852_update_card_detect(struct r852_device *dev) ++{ ++ int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); ++ dev->card_unstable = 0; ++ ++ card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); ++ card_detect_reg |= R852_CARD_IRQ_GENABLE; ++ ++ card_detect_reg |= dev->card_detected ? ++ R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT; ++ ++ r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); ++} ++ ++static ssize_t r852_media_type_show(struct device *sys_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); ++ struct r852_device *dev = r852_get_dev(mtd); ++ char *data = dev->sm ? "smartmedia" : "xd"; ++ ++ strcpy(buf, data); ++ return strlen(data); ++} ++ ++static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); ++ ++ ++/* Detect properties of card in slot */ ++static void r852_update_media_status(struct r852_device *dev) ++{ ++ uint8_t reg; ++ unsigned long flags; ++ int readonly; ++ ++ spin_lock_irqsave(&dev->irqlock, flags); ++ if (!dev->card_detected) { ++ message("card removed"); ++ spin_unlock_irqrestore(&dev->irqlock, flags); ++ return ; ++ } ++ ++ readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO; ++ reg = r852_read_reg(dev, R852_DMA_CAP); ++ dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT); ++ ++ message("detected %s %s card in slot", ++ dev->sm ? "SmartMedia" : "xD", ++ readonly ? "readonly" : "writeable"); ++ ++ dev->readonly = readonly; ++ spin_unlock_irqrestore(&dev->irqlock, flags); ++} ++ ++/* ++ * Register the nand device ++ * Called when the card is detected ++ */ ++static int r852_register_nand_device(struct r852_device *dev) ++{ ++ struct mtd_info *mtd = nand_to_mtd(dev->chip); ++ ++ WARN_ON(dev->card_registred); ++ ++ mtd->dev.parent = &dev->pci_dev->dev; ++ ++ if (dev->readonly) ++ dev->chip->options |= NAND_ROM; ++ ++ r852_engine_enable(dev); ++ ++ if (sm_register_device(mtd, dev->sm)) ++ goto error1; ++ ++ if (device_create_file(&mtd->dev, &dev_attr_media_type)) { ++ message("can't create media type sysfs attribute"); ++ goto error3; ++ } ++ ++ dev->card_registred = 1; ++ return 0; ++error3: ++ nand_release(mtd); ++error1: ++ /* Force card redetect */ ++ dev->card_detected = 0; ++ return -1; ++} ++ ++/* ++ * Unregister the card ++ */ ++ ++static void r852_unregister_nand_device(struct r852_device *dev) ++{ ++ struct mtd_info *mtd = nand_to_mtd(dev->chip); ++ ++ if (!dev->card_registred) ++ return; ++ ++ device_remove_file(&mtd->dev, &dev_attr_media_type); ++ nand_release(mtd); ++ r852_engine_disable(dev); ++ dev->card_registred = 0; ++} ++ ++/* Card state updater */ ++static void r852_card_detect_work(struct work_struct *work) ++{ ++ struct r852_device *dev = ++ container_of(work, struct r852_device, card_detect_work.work); ++ ++ r852_card_update_present(dev); ++ r852_update_card_detect(dev); ++ dev->card_unstable = 0; ++ ++ /* False alarm */ ++ if (dev->card_detected == dev->card_registred) ++ goto exit; ++ ++ /* Read media properties */ ++ r852_update_media_status(dev); ++ ++ /* Register the card */ ++ if (dev->card_detected) ++ r852_register_nand_device(dev); ++ else ++ r852_unregister_nand_device(dev); ++exit: ++ r852_update_card_detect(dev); ++} ++ ++/* Ack + disable IRQ generation */ ++static void r852_disable_irqs(struct r852_device *dev) ++{ ++ uint8_t reg; ++ reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); ++ r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK); ++ ++ reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); ++ r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, ++ reg & ~R852_DMA_IRQ_MASK); ++ ++ r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK); ++ r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK); ++} ++ ++/* Interrupt handler */ ++static irqreturn_t r852_irq(int irq, void *data) ++{ ++ struct r852_device *dev = (struct r852_device *)data; ++ ++ uint8_t card_status, dma_status; ++ unsigned long flags; ++ irqreturn_t ret = IRQ_NONE; ++ ++ spin_lock_irqsave(&dev->irqlock, flags); ++ ++ /* handle card detection interrupts first */ ++ card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); ++ r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); ++ ++ if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) { ++ ++ ret = IRQ_HANDLED; ++ dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); ++ ++ /* we shouldn't receive any interrupts if we wait for card ++ to settle */ ++ WARN_ON(dev->card_unstable); ++ ++ /* disable irqs while card is unstable */ ++ /* this will timeout DMA if active, but better that garbage */ ++ r852_disable_irqs(dev); ++ ++ if (dev->card_unstable) ++ goto out; ++ ++ /* let, card state to settle a bit, and then do the work */ ++ dev->card_unstable = 1; ++ queue_delayed_work(dev->card_workqueue, ++ &dev->card_detect_work, msecs_to_jiffies(100)); ++ goto out; ++ } ++ ++ ++ /* Handle dma interrupts */ ++ dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA); ++ r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status); ++ ++ if (dma_status & R852_DMA_IRQ_MASK) { ++ ++ ret = IRQ_HANDLED; ++ ++ if (dma_status & R852_DMA_IRQ_ERROR) { ++ dbg("received dma error IRQ"); ++ r852_dma_done(dev, -EIO); ++ complete(&dev->dma_done); ++ goto out; ++ } ++ ++ /* received DMA interrupt out of nowhere? */ ++ WARN_ON_ONCE(dev->dma_stage == 0); ++ ++ if (dev->dma_stage == 0) ++ goto out; ++ ++ /* done device access */ ++ if (dev->dma_state == DMA_INTERNAL && ++ (dma_status & R852_DMA_IRQ_INTERNAL)) { ++ ++ dev->dma_state = DMA_MEMORY; ++ dev->dma_stage++; ++ } ++ ++ /* done memory DMA */ ++ if (dev->dma_state == DMA_MEMORY && ++ (dma_status & R852_DMA_IRQ_MEMORY)) { ++ dev->dma_state = DMA_INTERNAL; ++ dev->dma_stage++; ++ } ++ ++ /* Enable 2nd half of dma dance */ ++ if (dev->dma_stage == 2) ++ r852_dma_enable(dev); ++ ++ /* Operation done */ ++ if (dev->dma_stage == 3) { ++ r852_dma_done(dev, 0); ++ complete(&dev->dma_done); ++ } ++ goto out; ++ } ++ ++ /* Handle unknown interrupts */ ++ if (dma_status) ++ dbg("bad dma IRQ status = %x", dma_status); ++ ++ if (card_status & ~R852_CARD_STA_CD) ++ dbg("strange card status = %x", card_status); ++ ++out: ++ spin_unlock_irqrestore(&dev->irqlock, flags); ++ return ret; ++} ++ ++static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) ++{ ++ int error; ++ struct nand_chip *chip; ++ struct r852_device *dev; ++ ++ /* pci initialization */ ++ error = pci_enable_device(pci_dev); ++ ++ if (error) ++ goto error1; ++ ++ pci_set_master(pci_dev); ++ ++ error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); ++ if (error) ++ goto error2; ++ ++ error = pci_request_regions(pci_dev, DRV_NAME); ++ ++ if (error) ++ goto error3; ++ ++ error = -ENOMEM; ++ ++ /* init nand chip, but register it only on card insert */ ++ chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ++ ++ if (!chip) ++ goto error4; ++ ++ /* commands */ ++ chip->cmd_ctrl = r852_cmdctl; ++ chip->waitfunc = r852_wait; ++ chip->dev_ready = r852_ready; ++ ++ /* I/O */ ++ chip->read_byte = r852_read_byte; ++ chip->read_buf = r852_read_buf; ++ chip->write_buf = r852_write_buf; ++ ++ /* ecc */ ++ chip->ecc.mode = NAND_ECC_HW_SYNDROME; ++ chip->ecc.size = R852_DMA_LEN; ++ chip->ecc.bytes = SM_OOB_SIZE; ++ chip->ecc.strength = 2; ++ chip->ecc.hwctl = r852_ecc_hwctl; ++ chip->ecc.calculate = r852_ecc_calculate; ++ chip->ecc.correct = r852_ecc_correct; ++ ++ /* TODO: hack */ ++ chip->ecc.read_oob = r852_read_oob; ++ ++ /* init our device structure */ ++ dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); ++ ++ if (!dev) ++ goto error5; ++ ++ nand_set_controller_data(chip, dev); ++ dev->chip = chip; ++ dev->pci_dev = pci_dev; ++ pci_set_drvdata(pci_dev, dev); ++ ++ dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, ++ &dev->phys_bounce_buffer); ++ ++ if (!dev->bounce_buffer) ++ goto error6; ++ ++ ++ error = -ENODEV; ++ dev->mmio = pci_ioremap_bar(pci_dev, 0); ++ ++ if (!dev->mmio) ++ goto error7; ++ ++ error = -ENOMEM; ++ dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); ++ ++ if (!dev->tmp_buffer) ++ goto error8; ++ ++ init_completion(&dev->dma_done); ++ ++ dev->card_workqueue = create_freezable_workqueue(DRV_NAME); ++ ++ if (!dev->card_workqueue) ++ goto error9; ++ ++ INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work); ++ ++ /* shutdown everything - precation */ ++ r852_engine_disable(dev); ++ r852_disable_irqs(dev); ++ ++ r852_dma_test(dev); ++ ++ dev->irq = pci_dev->irq; ++ spin_lock_init(&dev->irqlock); ++ ++ dev->card_detected = 0; ++ r852_card_update_present(dev); ++ ++ /*register irq handler*/ ++ error = -ENODEV; ++ if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED, ++ DRV_NAME, dev)) ++ goto error10; ++ ++ /* kick initial present test */ ++ queue_delayed_work(dev->card_workqueue, ++ &dev->card_detect_work, 0); ++ ++ ++ printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n"); ++ return 0; ++ ++error10: ++ destroy_workqueue(dev->card_workqueue); ++error9: ++ kfree(dev->tmp_buffer); ++error8: ++ pci_iounmap(pci_dev, dev->mmio); ++error7: ++ pci_free_consistent(pci_dev, R852_DMA_LEN, ++ dev->bounce_buffer, dev->phys_bounce_buffer); ++error6: ++ kfree(dev); ++error5: ++ kfree(chip); ++error4: ++ pci_release_regions(pci_dev); ++error3: ++error2: ++ pci_disable_device(pci_dev); ++error1: ++ return error; ++} ++ ++static void r852_remove(struct pci_dev *pci_dev) ++{ ++ struct r852_device *dev = pci_get_drvdata(pci_dev); ++ ++ /* Stop detect workqueue - ++ we are going to unregister the device anyway*/ ++ cancel_delayed_work_sync(&dev->card_detect_work); ++ destroy_workqueue(dev->card_workqueue); ++ ++ /* Unregister the device, this might make more IO */ ++ r852_unregister_nand_device(dev); ++ ++ /* Stop interrupts */ ++ r852_disable_irqs(dev); ++ free_irq(dev->irq, dev); ++ ++ /* Cleanup */ ++ kfree(dev->tmp_buffer); ++ pci_iounmap(pci_dev, dev->mmio); ++ pci_free_consistent(pci_dev, R852_DMA_LEN, ++ dev->bounce_buffer, dev->phys_bounce_buffer); ++ ++ kfree(dev->chip); ++ kfree(dev); ++ ++ /* Shutdown the PCI device */ ++ pci_release_regions(pci_dev); ++ pci_disable_device(pci_dev); ++} ++ ++static void r852_shutdown(struct pci_dev *pci_dev) ++{ ++ struct r852_device *dev = pci_get_drvdata(pci_dev); ++ ++ cancel_delayed_work_sync(&dev->card_detect_work); ++ r852_disable_irqs(dev); ++ synchronize_irq(dev->irq); ++ pci_disable_device(pci_dev); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int r852_suspend(struct device *device) ++{ ++ struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); ++ ++ if (dev->ctlreg & R852_CTL_CARDENABLE) ++ return -EBUSY; ++ ++ /* First make sure the detect work is gone */ ++ cancel_delayed_work_sync(&dev->card_detect_work); ++ ++ /* Turn off the interrupts and stop the device */ ++ r852_disable_irqs(dev); ++ r852_engine_disable(dev); ++ ++ /* If card was pulled off just during the suspend, which is very ++ unlikely, we will remove it on resume, it too late now ++ anyway... */ ++ dev->card_unstable = 0; ++ return 0; ++} ++ ++static int r852_resume(struct device *device) ++{ ++ struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); ++ struct mtd_info *mtd = nand_to_mtd(dev->chip); ++ ++ r852_disable_irqs(dev); ++ r852_card_update_present(dev); ++ r852_engine_disable(dev); ++ ++ ++ /* If card status changed, just do the work */ ++ if (dev->card_detected != dev->card_registred) { ++ dbg("card was %s during low power state", ++ dev->card_detected ? "added" : "removed"); ++ ++ queue_delayed_work(dev->card_workqueue, ++ &dev->card_detect_work, msecs_to_jiffies(1000)); ++ return 0; ++ } ++ ++ /* Otherwise, initialize the card */ ++ if (dev->card_registred) { ++ r852_engine_enable(dev); ++ dev->chip->select_chip(mtd, 0); ++ nand_reset_op(dev->chip); ++ dev->chip->select_chip(mtd, -1); ++ } ++ ++ /* Program card detection IRQ */ ++ r852_update_card_detect(dev); ++ return 0; ++} ++#endif ++ ++static const struct pci_device_id r852_pci_id_tbl[] = { ++ ++ { PCI_VDEVICE(RICOH, 0x0852), }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); ++ ++static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); ++ ++static struct pci_driver r852_pci_driver = { ++ .name = DRV_NAME, ++ .id_table = r852_pci_id_tbl, ++ .probe = r852_probe, ++ .remove = r852_remove, ++ .shutdown = r852_shutdown, ++ .driver.pm = &r852_pm_ops, ++}; ++ ++module_pci_driver(r852_pci_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Maxim Levitsky "); ++MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver"); +diff --git a/drivers/mtd/nand/raw/r852.h b/drivers/mtd/nand/raw/r852.h +new file mode 100644 +index 00000000..8713c57 +--- /dev/null ++++ b/drivers/mtd/nand/raw/r852.h +@@ -0,0 +1,160 @@ ++/* ++ * Copyright © 2009 - Maxim Levitsky ++ * driver for Ricoh xD readers ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* nand interface + ecc ++ byte write/read does one cycle on nand data lines. ++ dword write/read does 4 cycles ++ if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads ++ results of ecc correction, if DMA read was done before. ++ If write was done two dword reads read generated ecc checksums ++*/ ++#define R852_DATALINE 0x00 ++ ++/* control register */ ++#define R852_CTL 0x04 ++#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/ ++#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/ ++#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */ ++ /* but has to be set on start...*/ ++#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/ ++#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/ ++#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */ ++#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/ ++#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */ ++ ++/* card detection status */ ++#define R852_CARD_STA 0x05 ++ ++#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */ ++#define R852_CARD_STA_RO 0x02 /* card is readonly */ ++#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */ ++#define R852_CARD_STA_ABSENT 0x08 /* card is absent */ ++#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */ ++ ++/* card detection irq status & enable*/ ++#define R852_CARD_IRQ_STA 0x06 /* IRQ status */ ++#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */ ++ ++#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/ ++#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */ ++#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */ ++#define R852_CARD_IRQ_UNK1 0x10 /* unknown */ ++#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */ ++#define R852_CARD_IRQ_MASK 0x1D ++ ++ ++ ++/* hardware enable */ ++#define R852_HW 0x08 ++#define R852_HW_ENABLED 0x01 /* hw enabled */ ++#define R852_HW_UNKNOWN 0x80 ++ ++ ++/* dma capabilities */ ++#define R852_DMA_CAP 0x09 ++#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */ ++ /* hw is smartmedia */ ++#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */ ++#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */ ++ ++ ++/* physical DMA address - 32 bit value*/ ++#define R852_DMA_ADDR 0x0C ++ ++ ++/* dma settings */ ++#define R852_DMA_SETTINGS 0x10 ++#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */ ++#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */ ++#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */ ++ ++/* dma IRQ status */ ++#define R852_DMA_IRQ_STA 0x14 ++ ++/* dma IRQ enable */ ++#define R852_DMA_IRQ_ENABLE 0x18 ++ ++#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */ ++#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */ ++#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */ ++#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */ ++ ++ ++/* ECC syndrome format - read from reg #0 will return two copies of these for ++ each half of the page. ++ first byte is error byte location, and second, bit location + flags */ ++#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */ ++#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */ ++#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */ ++#define R852_ECC_FAIL 0x40 /* non correctable error detected */ ++ ++#define R852_DMA_LEN 512 ++ ++#define DMA_INTERNAL 0 ++#define DMA_MEMORY 1 ++ ++struct r852_device { ++ void __iomem *mmio; /* mmio */ ++ struct nand_chip *chip; /* nand chip backpointer */ ++ struct pci_dev *pci_dev; /* pci backpointer */ ++ ++ /* dma area */ ++ dma_addr_t phys_dma_addr; /* bus address of buffer*/ ++ struct completion dma_done; /* data transfer done */ ++ ++ dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */ ++ uint8_t *bounce_buffer; /* virtual address of bounce buffer */ ++ ++ int dma_dir; /* 1 = read, 0 = write */ ++ int dma_stage; /* 0 - idle, 1 - first step, ++ 2 - second step */ ++ ++ int dma_state; /* 0 = internal, 1 = memory */ ++ int dma_error; /* dma errors */ ++ int dma_usable; /* is it possible to use dma */ ++ ++ /* card status area */ ++ struct delayed_work card_detect_work; ++ struct workqueue_struct *card_workqueue; ++ int card_registred; /* card registered with mtd */ ++ int card_detected; /* card detected in slot */ ++ int card_unstable; /* whenever the card is inserted, ++ is not known yet */ ++ int readonly; /* card is readonly */ ++ int sm; /* Is card smartmedia */ ++ ++ /* interrupt handling */ ++ spinlock_t irqlock; /* IRQ protecting lock */ ++ int irq; /* irq num */ ++ /* misc */ ++ void *tmp_buffer; /* temporary buffer */ ++ uint8_t ctlreg; /* cached contents of control reg */ ++}; ++ ++#define DRV_NAME "r852" ++ ++ ++#define dbg(format, ...) \ ++ if (debug) \ ++ printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) ++ ++#define dbg_verbose(format, ...) \ ++ if (debug > 1) \ ++ printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) ++ ++ ++#define message(format, ...) \ ++ printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) +diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c +new file mode 100644 +index 00000000..0e341c7 +--- /dev/null ++++ b/drivers/mtd/nand/raw/s3c2410.c +@@ -0,0 +1,1296 @@ ++/* linux/drivers/mtd/nand/s3c2410.c ++ * ++ * Copyright © 2004-2008 Simtec Electronics ++ * http://armlinux.simtec.co.uk/ ++ * Ben Dooks ++ * ++ * Samsung S3C2410/S3C2440/S3C2412 NAND 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. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++*/ ++ ++#define pr_fmt(fmt) "nand-s3c2410: " fmt ++ ++#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG ++#define DEBUG ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define S3C2410_NFREG(x) (x) ++ ++#define S3C2410_NFCONF S3C2410_NFREG(0x00) ++#define S3C2410_NFCMD S3C2410_NFREG(0x04) ++#define S3C2410_NFADDR S3C2410_NFREG(0x08) ++#define S3C2410_NFDATA S3C2410_NFREG(0x0C) ++#define S3C2410_NFSTAT S3C2410_NFREG(0x10) ++#define S3C2410_NFECC S3C2410_NFREG(0x14) ++#define S3C2440_NFCONT S3C2410_NFREG(0x04) ++#define S3C2440_NFCMD S3C2410_NFREG(0x08) ++#define S3C2440_NFADDR S3C2410_NFREG(0x0C) ++#define S3C2440_NFDATA S3C2410_NFREG(0x10) ++#define S3C2440_NFSTAT S3C2410_NFREG(0x20) ++#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) ++#define S3C2412_NFSTAT S3C2410_NFREG(0x28) ++#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) ++#define S3C2410_NFCONF_EN (1<<15) ++#define S3C2410_NFCONF_INITECC (1<<12) ++#define S3C2410_NFCONF_nFCE (1<<11) ++#define S3C2410_NFCONF_TACLS(x) ((x)<<8) ++#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) ++#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) ++#define S3C2410_NFSTAT_BUSY (1<<0) ++#define S3C2440_NFCONF_TACLS(x) ((x)<<12) ++#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) ++#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) ++#define S3C2440_NFCONT_INITECC (1<<4) ++#define S3C2440_NFCONT_nFCE (1<<1) ++#define S3C2440_NFCONT_ENABLE (1<<0) ++#define S3C2440_NFSTAT_READY (1<<0) ++#define S3C2412_NFCONF_NANDBOOT (1<<31) ++#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) ++#define S3C2412_NFCONT_nFCE0 (1<<1) ++#define S3C2412_NFSTAT_READY (1<<0) ++ ++/* new oob placement block for use with hardware ecc generation ++ */ ++static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 0; ++ oobregion->length = 3; ++ ++ return 0; ++} ++ ++static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 8; ++ oobregion->length = 8; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = { ++ .ecc = s3c2410_ooblayout_ecc, ++ .free = s3c2410_ooblayout_free, ++}; ++ ++/* controller and mtd information */ ++ ++struct s3c2410_nand_info; ++ ++/** ++ * struct s3c2410_nand_mtd - driver MTD structure ++ * @mtd: The MTD instance to pass to the MTD layer. ++ * @chip: The NAND chip information. ++ * @set: The platform information supplied for this set of NAND chips. ++ * @info: Link back to the hardware information. ++ * @scan_res: The result from calling nand_scan_ident(). ++*/ ++struct s3c2410_nand_mtd { ++ struct nand_chip chip; ++ struct s3c2410_nand_set *set; ++ struct s3c2410_nand_info *info; ++ int scan_res; ++}; ++ ++enum s3c_cpu_type { ++ TYPE_S3C2410, ++ TYPE_S3C2412, ++ TYPE_S3C2440, ++}; ++ ++enum s3c_nand_clk_state { ++ CLOCK_DISABLE = 0, ++ CLOCK_ENABLE, ++ CLOCK_SUSPEND, ++}; ++ ++/* overview of the s3c2410 nand state */ ++ ++/** ++ * struct s3c2410_nand_info - NAND controller state. ++ * @mtds: An array of MTD instances on this controoler. ++ * @platform: The platform data for this board. ++ * @device: The platform device we bound to. ++ * @clk: The clock resource for this controller. ++ * @regs: The area mapped for the hardware registers. ++ * @sel_reg: Pointer to the register controlling the NAND selection. ++ * @sel_bit: The bit in @sel_reg to select the NAND chip. ++ * @mtd_count: The number of MTDs created from this controller. ++ * @save_sel: The contents of @sel_reg to be saved over suspend. ++ * @clk_rate: The clock rate from @clk. ++ * @clk_state: The current clock state. ++ * @cpu_type: The exact type of this controller. ++ */ ++struct s3c2410_nand_info { ++ /* mtd info */ ++ struct nand_controller controller; ++ struct s3c2410_nand_mtd *mtds; ++ struct s3c2410_platform_nand *platform; ++ ++ /* device info */ ++ struct device *device; ++ struct clk *clk; ++ void __iomem *regs; ++ void __iomem *sel_reg; ++ int sel_bit; ++ int mtd_count; ++ unsigned long save_sel; ++ unsigned long clk_rate; ++ enum s3c_nand_clk_state clk_state; ++ ++ enum s3c_cpu_type cpu_type; ++ ++#ifdef CONFIG_ARM_S3C24XX_CPUFREQ ++ struct notifier_block freq_transition; ++#endif ++}; ++ ++struct s3c24XX_nand_devtype_data { ++ enum s3c_cpu_type type; ++}; ++ ++static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = { ++ .type = TYPE_S3C2410, ++}; ++ ++static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = { ++ .type = TYPE_S3C2412, ++}; ++ ++static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = { ++ .type = TYPE_S3C2440, ++}; ++ ++/* conversion functions */ ++ ++static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd, ++ chip); ++} ++ ++static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) ++{ ++ return s3c2410_nand_mtd_toours(mtd)->info; ++} ++ ++static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) ++{ ++ return platform_get_drvdata(dev); ++} ++ ++static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) ++{ ++ return dev_get_platdata(&dev->dev); ++} ++ ++static inline int allow_clk_suspend(struct s3c2410_nand_info *info) ++{ ++#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP ++ return 1; ++#else ++ return 0; ++#endif ++} ++ ++/** ++ * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. ++ * @info: The controller instance. ++ * @new_state: State to which clock should be set. ++ */ ++static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, ++ enum s3c_nand_clk_state new_state) ++{ ++ if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) ++ return; ++ ++ if (info->clk_state == CLOCK_ENABLE) { ++ if (new_state != CLOCK_ENABLE) ++ clk_disable_unprepare(info->clk); ++ } else { ++ if (new_state == CLOCK_ENABLE) ++ clk_prepare_enable(info->clk); ++ } ++ ++ info->clk_state = new_state; ++} ++ ++/* timing calculations */ ++ ++#define NS_IN_KHZ 1000000 ++ ++/** ++ * s3c_nand_calc_rate - calculate timing data. ++ * @wanted: The cycle time in nanoseconds. ++ * @clk: The clock rate in kHz. ++ * @max: The maximum divider value. ++ * ++ * Calculate the timing value from the given parameters. ++ */ ++static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) ++{ ++ int result; ++ ++ result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); ++ ++ pr_debug("result %d from %ld, %d\n", result, clk, wanted); ++ ++ if (result > max) { ++ pr_err("%d ns is too big for current clock rate %ld\n", ++ wanted, clk); ++ return -1; ++ } ++ ++ if (result < 1) ++ result = 1; ++ ++ return result; ++} ++ ++#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk)) ++ ++/* controller setup */ ++ ++/** ++ * s3c2410_nand_setrate - setup controller timing information. ++ * @info: The controller instance. ++ * ++ * Given the information supplied by the platform, calculate and set ++ * the necessary timing registers in the hardware to generate the ++ * necessary timing cycles to the hardware. ++ */ ++static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) ++{ ++ struct s3c2410_platform_nand *plat = info->platform; ++ int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; ++ int tacls, twrph0, twrph1; ++ unsigned long clkrate = clk_get_rate(info->clk); ++ unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); ++ unsigned long flags; ++ ++ /* calculate the timing information for the controller */ ++ ++ info->clk_rate = clkrate; ++ clkrate /= 1000; /* turn clock into kHz for ease of use */ ++ ++ if (plat != NULL) { ++ tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); ++ twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); ++ twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); ++ } else { ++ /* default timings */ ++ tacls = tacls_max; ++ twrph0 = 8; ++ twrph1 = 8; ++ } ++ ++ if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { ++ dev_err(info->device, "cannot get suitable timings\n"); ++ return -EINVAL; ++ } ++ ++ dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", ++ tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), ++ twrph1, to_ns(twrph1, clkrate)); ++ ++ switch (info->cpu_type) { ++ case TYPE_S3C2410: ++ mask = (S3C2410_NFCONF_TACLS(3) | ++ S3C2410_NFCONF_TWRPH0(7) | ++ S3C2410_NFCONF_TWRPH1(7)); ++ set = S3C2410_NFCONF_EN; ++ set |= S3C2410_NFCONF_TACLS(tacls - 1); ++ set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); ++ set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); ++ break; ++ ++ case TYPE_S3C2440: ++ case TYPE_S3C2412: ++ mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | ++ S3C2440_NFCONF_TWRPH0(7) | ++ S3C2440_NFCONF_TWRPH1(7)); ++ ++ set = S3C2440_NFCONF_TACLS(tacls - 1); ++ set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); ++ set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ local_irq_save(flags); ++ ++ cfg = readl(info->regs + S3C2410_NFCONF); ++ cfg &= ~mask; ++ cfg |= set; ++ writel(cfg, info->regs + S3C2410_NFCONF); ++ ++ local_irq_restore(flags); ++ ++ dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); ++ ++ return 0; ++} ++ ++/** ++ * s3c2410_nand_inithw - basic hardware initialisation ++ * @info: The hardware state. ++ * ++ * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() ++ * to setup the hardware access speeds and set the controller to be enabled. ++*/ ++static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) ++{ ++ int ret; ++ ++ ret = s3c2410_nand_setrate(info); ++ if (ret < 0) ++ return ret; ++ ++ switch (info->cpu_type) { ++ case TYPE_S3C2410: ++ default: ++ break; ++ ++ case TYPE_S3C2440: ++ case TYPE_S3C2412: ++ /* enable the controller and de-assert nFCE */ ++ ++ writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); ++ } ++ ++ return 0; ++} ++ ++/** ++ * s3c2410_nand_select_chip - select the given nand chip ++ * @mtd: The MTD instance for this chip. ++ * @chip: The chip number. ++ * ++ * This is called by the MTD layer to either select a given chip for the ++ * @mtd instance, or to indicate that the access has finished and the ++ * chip can be de-selected. ++ * ++ * The routine ensures that the nFCE line is correctly setup, and any ++ * platform specific selection code is called to route nFCE to the specific ++ * chip. ++ */ ++static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct s3c2410_nand_info *info; ++ struct s3c2410_nand_mtd *nmtd; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ unsigned long cur; ++ ++ nmtd = nand_get_controller_data(this); ++ info = nmtd->info; ++ ++ if (chip != -1) ++ s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); ++ ++ cur = readl(info->sel_reg); ++ ++ if (chip == -1) { ++ cur |= info->sel_bit; ++ } else { ++ if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { ++ dev_err(info->device, "invalid chip %d\n", chip); ++ return; ++ } ++ ++ if (info->platform != NULL) { ++ if (info->platform->select_chip != NULL) ++ (info->platform->select_chip) (nmtd->set, chip); ++ } ++ ++ cur &= ~info->sel_bit; ++ } ++ ++ writel(cur, info->sel_reg); ++ ++ if (chip == -1) ++ s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); ++} ++ ++/* s3c2410_nand_hwcontrol ++ * ++ * Issue command and address cycles to the chip ++*/ ++ ++static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ writeb(cmd, info->regs + S3C2410_NFCMD); ++ else ++ writeb(cmd, info->regs + S3C2410_NFADDR); ++} ++ ++/* command and control functions */ ++ ++static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ writeb(cmd, info->regs + S3C2440_NFCMD); ++ else ++ writeb(cmd, info->regs + S3C2440_NFADDR); ++} ++ ++/* s3c2410_nand_devready() ++ * ++ * returns 0 if the nand is busy, 1 if it is ready ++*/ ++ ++static int s3c2410_nand_devready(struct mtd_info *mtd) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; ++} ++ ++static int s3c2440_nand_devready(struct mtd_info *mtd) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; ++} ++ ++static int s3c2412_nand_devready(struct mtd_info *mtd) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY; ++} ++ ++/* ECC handling functions */ ++ ++static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned int diff0, diff1, diff2; ++ unsigned int bit, byte; ++ ++ pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); ++ ++ diff0 = read_ecc[0] ^ calc_ecc[0]; ++ diff1 = read_ecc[1] ^ calc_ecc[1]; ++ diff2 = read_ecc[2] ^ calc_ecc[2]; ++ ++ pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n", ++ __func__, 3, read_ecc, 3, calc_ecc, ++ diff0, diff1, diff2); ++ ++ if (diff0 == 0 && diff1 == 0 && diff2 == 0) ++ return 0; /* ECC is ok */ ++ ++ /* sometimes people do not think about using the ECC, so check ++ * to see if we have an 0xff,0xff,0xff read ECC and then ignore ++ * the error, on the assumption that this is an un-eccd page. ++ */ ++ if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff ++ && info->platform->ignore_unset_ecc) ++ return 0; ++ ++ /* Can we correct this ECC (ie, one row and column change). ++ * Note, this is similar to the 256 error code on smartmedia */ ++ ++ if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && ++ ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && ++ ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { ++ /* calculate the bit position of the error */ ++ ++ bit = ((diff2 >> 3) & 1) | ++ ((diff2 >> 4) & 2) | ++ ((diff2 >> 5) & 4); ++ ++ /* calculate the byte position of the error */ ++ ++ byte = ((diff2 << 7) & 0x100) | ++ ((diff1 << 0) & 0x80) | ++ ((diff1 << 1) & 0x40) | ++ ((diff1 << 2) & 0x20) | ++ ((diff1 << 3) & 0x10) | ++ ((diff0 >> 4) & 0x08) | ++ ((diff0 >> 3) & 0x04) | ++ ((diff0 >> 2) & 0x02) | ++ ((diff0 >> 1) & 0x01); ++ ++ dev_dbg(info->device, "correcting error bit %d, byte %d\n", ++ bit, byte); ++ ++ dat[byte] ^= (1 << bit); ++ return 1; ++ } ++ ++ /* if there is only one bit difference in the ECC, then ++ * one of only a row or column parity has changed, which ++ * means the error is most probably in the ECC itself */ ++ ++ diff0 |= (diff1 << 8); ++ diff0 |= (diff2 << 16); ++ ++ /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */ ++ if ((diff0 & (diff0 - 1)) == 0) ++ return 1; ++ ++ return -1; ++} ++ ++/* ECC functions ++ * ++ * These allow the s3c2410 and s3c2440 to use the controller's ECC ++ * generator block to ECC the data as it passes through] ++*/ ++ ++static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long ctrl; ++ ++ ctrl = readl(info->regs + S3C2410_NFCONF); ++ ctrl |= S3C2410_NFCONF_INITECC; ++ writel(ctrl, info->regs + S3C2410_NFCONF); ++} ++ ++static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long ctrl; ++ ++ ctrl = readl(info->regs + S3C2440_NFCONT); ++ writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, ++ info->regs + S3C2440_NFCONT); ++} ++ ++static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long ctrl; ++ ++ ctrl = readl(info->regs + S3C2440_NFCONT); ++ writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); ++} ++ ++static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); ++ ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ++ ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); ++ ++ pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); ++ ++ return 0; ++} ++ ++static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); ++ ++ ecc_code[0] = ecc; ++ ecc_code[1] = ecc >> 8; ++ ecc_code[2] = ecc >> 16; ++ ++ pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code); ++ ++ return 0; ++} ++ ++static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); ++ ++ ecc_code[0] = ecc; ++ ecc_code[1] = ecc >> 8; ++ ecc_code[2] = ecc >> 16; ++ ++ pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); ++ ++ return 0; ++} ++ ++/* over-ride the standard functions for a little more speed. We can ++ * use read/write block to move the data buffers to/from the controller ++*/ ++ ++static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ readsb(this->IO_ADDR_R, buf, len); ++} ++ ++static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); ++ ++ /* cleanup if we've got less than a word to do */ ++ if (len & 3) { ++ buf += len & ~3; ++ ++ for (; len & 3; len--) ++ *buf++ = readb(info->regs + S3C2440_NFDATA); ++ } ++} ++ ++static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, ++ int len) ++{ ++ struct nand_chip *this = mtd_to_nand(mtd); ++ writesb(this->IO_ADDR_W, buf, len); ++} ++ ++static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, ++ int len) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ ++ writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); ++ ++ /* cleanup any fractional write */ ++ if (len & 3) { ++ buf += len & ~3; ++ ++ for (; len & 3; len--, buf++) ++ writeb(*buf, info->regs + S3C2440_NFDATA); ++ } ++} ++ ++/* cpufreq driver support */ ++ ++#ifdef CONFIG_ARM_S3C24XX_CPUFREQ ++ ++static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct s3c2410_nand_info *info; ++ unsigned long newclk; ++ ++ info = container_of(nb, struct s3c2410_nand_info, freq_transition); ++ newclk = clk_get_rate(info->clk); ++ ++ if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || ++ (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { ++ s3c2410_nand_setrate(info); ++ } ++ ++ return 0; ++} ++ ++static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) ++{ ++ info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; ++ ++ return cpufreq_register_notifier(&info->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++static inline void ++s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) ++{ ++ cpufreq_unregister_notifier(&info->freq_transition, ++ CPUFREQ_TRANSITION_NOTIFIER); ++} ++ ++#else ++static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) ++{ ++ return 0; ++} ++ ++static inline void ++s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) ++{ ++} ++#endif ++ ++/* device management functions */ ++ ++static int s3c24xx_nand_remove(struct platform_device *pdev) ++{ ++ struct s3c2410_nand_info *info = to_nand_info(pdev); ++ ++ if (info == NULL) ++ return 0; ++ ++ s3c2410_nand_cpufreq_deregister(info); ++ ++ /* Release all our mtds and their partitions, then go through ++ * freeing the resources used ++ */ ++ ++ if (info->mtds != NULL) { ++ struct s3c2410_nand_mtd *ptr = info->mtds; ++ int mtdno; ++ ++ for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { ++ pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); ++ nand_release(nand_to_mtd(&ptr->chip)); ++ } ++ } ++ ++ /* free the common resources */ ++ ++ if (!IS_ERR(info->clk)) ++ s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); ++ ++ return 0; ++} ++ ++static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, ++ struct s3c2410_nand_mtd *mtd, ++ struct s3c2410_nand_set *set) ++{ ++ if (set) { ++ struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip); ++ ++ mtdinfo->name = set->name; ++ ++ return mtd_device_parse_register(mtdinfo, NULL, NULL, ++ set->partitions, set->nr_partitions); ++ } ++ ++ return -ENODEV; ++} ++ ++static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ++ struct s3c2410_platform_nand *pdata = info->platform; ++ const struct nand_sdr_timings *timings; ++ int tacls; ++ ++ timings = nand_get_sdr_timings(conf); ++ if (IS_ERR(timings)) ++ return -ENOTSUPP; ++ ++ tacls = timings->tCLS_min - timings->tWP_min; ++ if (tacls < 0) ++ tacls = 0; ++ ++ pdata->tacls = DIV_ROUND_UP(tacls, 1000); ++ pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000); ++ pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000); ++ ++ return s3c2410_nand_setrate(info); ++} ++ ++/** ++ * s3c2410_nand_init_chip - initialise a single instance of an chip ++ * @info: The base NAND controller the chip is on. ++ * @nmtd: The new controller MTD instance to fill in. ++ * @set: The information passed from the board specific platform data. ++ * ++ * Initialise the given @nmtd from the information in @info and @set. This ++ * readies the structure for use with the MTD layer functions by ensuring ++ * all pointers are setup and the necessary control routines selected. ++ */ ++static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, ++ struct s3c2410_nand_mtd *nmtd, ++ struct s3c2410_nand_set *set) ++{ ++ struct device_node *np = info->device->of_node; ++ struct nand_chip *chip = &nmtd->chip; ++ void __iomem *regs = info->regs; ++ ++ nand_set_flash_node(chip, set->of_node); ++ ++ chip->write_buf = s3c2410_nand_write_buf; ++ chip->read_buf = s3c2410_nand_read_buf; ++ chip->select_chip = s3c2410_nand_select_chip; ++ chip->chip_delay = 50; ++ nand_set_controller_data(chip, nmtd); ++ chip->options = set->options; ++ chip->controller = &info->controller; ++ ++ /* ++ * let's keep behavior unchanged for legacy boards booting via pdata and ++ * auto-detect timings only when booting with a device tree. ++ */ ++ if (np) ++ chip->setup_data_interface = s3c2410_nand_setup_data_interface; ++ ++ switch (info->cpu_type) { ++ case TYPE_S3C2410: ++ chip->IO_ADDR_W = regs + S3C2410_NFDATA; ++ info->sel_reg = regs + S3C2410_NFCONF; ++ info->sel_bit = S3C2410_NFCONF_nFCE; ++ chip->cmd_ctrl = s3c2410_nand_hwcontrol; ++ chip->dev_ready = s3c2410_nand_devready; ++ break; ++ ++ case TYPE_S3C2440: ++ chip->IO_ADDR_W = regs + S3C2440_NFDATA; ++ info->sel_reg = regs + S3C2440_NFCONT; ++ info->sel_bit = S3C2440_NFCONT_nFCE; ++ chip->cmd_ctrl = s3c2440_nand_hwcontrol; ++ chip->dev_ready = s3c2440_nand_devready; ++ chip->read_buf = s3c2440_nand_read_buf; ++ chip->write_buf = s3c2440_nand_write_buf; ++ break; ++ ++ case TYPE_S3C2412: ++ chip->IO_ADDR_W = regs + S3C2440_NFDATA; ++ info->sel_reg = regs + S3C2440_NFCONT; ++ info->sel_bit = S3C2412_NFCONT_nFCE0; ++ chip->cmd_ctrl = s3c2440_nand_hwcontrol; ++ chip->dev_ready = s3c2412_nand_devready; ++ ++ if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) ++ dev_info(info->device, "System booted from NAND\n"); ++ ++ break; ++ } ++ ++ chip->IO_ADDR_R = chip->IO_ADDR_W; ++ ++ nmtd->info = info; ++ nmtd->set = set; ++ ++ chip->ecc.mode = info->platform->ecc_mode; ++ ++ /* ++ * If you use u-boot BBT creation code, specifying this flag will ++ * let the kernel fish out the BBT from the NAND. ++ */ ++ if (set->flash_bbt) ++ chip->bbt_options |= NAND_BBT_USE_FLASH; ++} ++ ++/** ++ * s3c2410_nand_update_chip - post probe update ++ * @info: The controller instance. ++ * @nmtd: The driver version of the MTD instance. ++ * ++ * This routine is called after the chip probe has successfully completed ++ * and the relevant per-chip information updated. This call ensure that ++ * we update the internal state accordingly. ++ * ++ * The internal state is currently limited to the ECC state information. ++*/ ++static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, ++ struct s3c2410_nand_mtd *nmtd) ++{ ++ struct nand_chip *chip = &nmtd->chip; ++ ++ switch (chip->ecc.mode) { ++ ++ case NAND_ECC_NONE: ++ dev_info(info->device, "ECC disabled\n"); ++ break; ++ ++ case NAND_ECC_SOFT: ++ /* ++ * This driver expects Hamming based ECC when ecc_mode is set ++ * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to ++ * avoid adding an extra ecc_algo field to ++ * s3c2410_platform_nand. ++ */ ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ dev_info(info->device, "soft ECC\n"); ++ break; ++ ++ case NAND_ECC_HW: ++ chip->ecc.calculate = s3c2410_nand_calculate_ecc; ++ chip->ecc.correct = s3c2410_nand_correct_data; ++ chip->ecc.strength = 1; ++ ++ switch (info->cpu_type) { ++ case TYPE_S3C2410: ++ chip->ecc.hwctl = s3c2410_nand_enable_hwecc; ++ chip->ecc.calculate = s3c2410_nand_calculate_ecc; ++ break; ++ ++ case TYPE_S3C2412: ++ chip->ecc.hwctl = s3c2412_nand_enable_hwecc; ++ chip->ecc.calculate = s3c2412_nand_calculate_ecc; ++ break; ++ ++ case TYPE_S3C2440: ++ chip->ecc.hwctl = s3c2440_nand_enable_hwecc; ++ chip->ecc.calculate = s3c2440_nand_calculate_ecc; ++ break; ++ } ++ ++ dev_dbg(info->device, "chip %p => page shift %d\n", ++ chip, chip->page_shift); ++ ++ /* change the behaviour depending on whether we are using ++ * the large or small page nand device */ ++ if (chip->page_shift > 10) { ++ chip->ecc.size = 256; ++ chip->ecc.bytes = 3; ++ } else { ++ chip->ecc.size = 512; ++ chip->ecc.bytes = 3; ++ mtd_set_ooblayout(nand_to_mtd(chip), ++ &s3c2410_ooblayout_ops); ++ } ++ ++ dev_info(info->device, "hardware ECC\n"); ++ break; ++ ++ default: ++ dev_err(info->device, "invalid ECC mode!\n"); ++ return -EINVAL; ++ } ++ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) ++ chip->options |= NAND_SKIP_BBTSCAN; ++ ++ return 0; ++} ++ ++static const struct of_device_id s3c24xx_nand_dt_ids[] = { ++ { ++ .compatible = "samsung,s3c2410-nand", ++ .data = &s3c2410_nand_devtype_data, ++ }, { ++ /* also compatible with s3c6400 */ ++ .compatible = "samsung,s3c2412-nand", ++ .data = &s3c2412_nand_devtype_data, ++ }, { ++ .compatible = "samsung,s3c2440-nand", ++ .data = &s3c2440_nand_devtype_data, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids); ++ ++static int s3c24xx_nand_probe_dt(struct platform_device *pdev) ++{ ++ const struct s3c24XX_nand_devtype_data *devtype_data; ++ struct s3c2410_platform_nand *pdata; ++ struct s3c2410_nand_info *info = platform_get_drvdata(pdev); ++ struct device_node *np = pdev->dev.of_node, *child; ++ struct s3c2410_nand_set *sets; ++ ++ devtype_data = of_device_get_match_data(&pdev->dev); ++ if (!devtype_data) ++ return -ENODEV; ++ ++ info->cpu_type = devtype_data->type; ++ ++ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return -ENOMEM; ++ ++ pdev->dev.platform_data = pdata; ++ ++ pdata->nr_sets = of_get_child_count(np); ++ if (!pdata->nr_sets) ++ return 0; ++ ++ sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, ++ GFP_KERNEL); ++ if (!sets) ++ return -ENOMEM; ++ ++ pdata->sets = sets; ++ ++ for_each_available_child_of_node(np, child) { ++ sets->name = (char *)child->name; ++ sets->of_node = child; ++ sets->nr_chips = 1; ++ ++ of_node_get(child); ++ ++ sets++; ++ } ++ ++ return 0; ++} ++ ++static int s3c24xx_nand_probe_pdata(struct platform_device *pdev) ++{ ++ struct s3c2410_nand_info *info = platform_get_drvdata(pdev); ++ ++ info->cpu_type = platform_get_device_id(pdev)->driver_data; ++ ++ return 0; ++} ++ ++/* s3c24xx_nand_probe ++ * ++ * called by device layer when it finds a device matching ++ * one our driver can handled. This code checks to see if ++ * it can allocate all necessary resources then calls the ++ * nand layer to look for devices ++*/ ++static int s3c24xx_nand_probe(struct platform_device *pdev) ++{ ++ struct s3c2410_platform_nand *plat; ++ struct s3c2410_nand_info *info; ++ struct s3c2410_nand_mtd *nmtd; ++ struct s3c2410_nand_set *sets; ++ struct resource *res; ++ int err = 0; ++ int size; ++ int nr_sets; ++ int setno; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (info == NULL) { ++ err = -ENOMEM; ++ goto exit_error; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ nand_controller_init(&info->controller); ++ ++ /* get the clock source and enable it */ ++ ++ info->clk = devm_clk_get(&pdev->dev, "nand"); ++ if (IS_ERR(info->clk)) { ++ dev_err(&pdev->dev, "failed to get clock\n"); ++ err = -ENOENT; ++ goto exit_error; ++ } ++ ++ s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); ++ ++ if (pdev->dev.of_node) ++ err = s3c24xx_nand_probe_dt(pdev); ++ else ++ err = s3c24xx_nand_probe_pdata(pdev); ++ ++ if (err) ++ goto exit_error; ++ ++ plat = to_nand_plat(pdev); ++ ++ /* allocate and map the resource */ ++ ++ /* currently we assume we have the one resource */ ++ res = pdev->resource; ++ size = resource_size(res); ++ ++ info->device = &pdev->dev; ++ info->platform = plat; ++ ++ info->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(info->regs)) { ++ err = PTR_ERR(info->regs); ++ goto exit_error; ++ } ++ ++ dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); ++ ++ sets = (plat != NULL) ? plat->sets : NULL; ++ nr_sets = (plat != NULL) ? plat->nr_sets : 1; ++ ++ info->mtd_count = nr_sets; ++ ++ /* allocate our information */ ++ ++ size = nr_sets * sizeof(*info->mtds); ++ info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); ++ if (info->mtds == NULL) { ++ err = -ENOMEM; ++ goto exit_error; ++ } ++ ++ /* initialise all possible chips */ ++ ++ nmtd = info->mtds; ++ ++ for (setno = 0; setno < nr_sets; setno++, nmtd++) { ++ struct mtd_info *mtd = nand_to_mtd(&nmtd->chip); ++ ++ pr_debug("initialising set %d (%p, info %p)\n", ++ setno, nmtd, info); ++ ++ mtd->dev.parent = &pdev->dev; ++ s3c2410_nand_init_chip(info, nmtd, sets); ++ ++ nmtd->scan_res = nand_scan_ident(mtd, ++ (sets) ? sets->nr_chips : 1, ++ NULL); ++ ++ if (nmtd->scan_res == 0) { ++ err = s3c2410_nand_update_chip(info, nmtd); ++ if (err < 0) ++ goto exit_error; ++ nand_scan_tail(mtd); ++ s3c2410_nand_add_partition(info, nmtd, sets); ++ } ++ ++ if (sets != NULL) ++ sets++; ++ } ++ ++ /* initialise the hardware */ ++ err = s3c2410_nand_inithw(info); ++ if (err != 0) ++ goto exit_error; ++ ++ err = s3c2410_nand_cpufreq_register(info); ++ if (err < 0) { ++ dev_err(&pdev->dev, "failed to init cpufreq support\n"); ++ goto exit_error; ++ } ++ ++ if (allow_clk_suspend(info)) { ++ dev_info(&pdev->dev, "clock idle support enabled\n"); ++ s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); ++ } ++ ++ return 0; ++ ++ exit_error: ++ s3c24xx_nand_remove(pdev); ++ ++ if (err == 0) ++ err = -EINVAL; ++ return err; ++} ++ ++/* PM Support */ ++#ifdef CONFIG_PM ++ ++static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) ++{ ++ struct s3c2410_nand_info *info = platform_get_drvdata(dev); ++ ++ if (info) { ++ info->save_sel = readl(info->sel_reg); ++ ++ /* For the moment, we must ensure nFCE is high during ++ * the time we are suspended. This really should be ++ * handled by suspending the MTDs we are using, but ++ * that is currently not the case. */ ++ ++ writel(info->save_sel | info->sel_bit, info->sel_reg); ++ ++ s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); ++ } ++ ++ return 0; ++} ++ ++static int s3c24xx_nand_resume(struct platform_device *dev) ++{ ++ struct s3c2410_nand_info *info = platform_get_drvdata(dev); ++ unsigned long sel; ++ ++ if (info) { ++ s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); ++ s3c2410_nand_inithw(info); ++ ++ /* Restore the state of the nFCE line. */ ++ ++ sel = readl(info->sel_reg); ++ sel &= ~info->sel_bit; ++ sel |= info->save_sel & info->sel_bit; ++ writel(sel, info->sel_reg); ++ ++ s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); ++ } ++ ++ return 0; ++} ++ ++#else ++#define s3c24xx_nand_suspend NULL ++#define s3c24xx_nand_resume NULL ++#endif ++ ++/* driver device registration */ ++ ++static const struct platform_device_id s3c24xx_driver_ids[] = { ++ { ++ .name = "s3c2410-nand", ++ .driver_data = TYPE_S3C2410, ++ }, { ++ .name = "s3c2440-nand", ++ .driver_data = TYPE_S3C2440, ++ }, { ++ .name = "s3c2412-nand", ++ .driver_data = TYPE_S3C2412, ++ }, { ++ .name = "s3c6400-nand", ++ .driver_data = TYPE_S3C2412, /* compatible with 2412 */ ++ }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); ++ ++static struct platform_driver s3c24xx_nand_driver = { ++ .probe = s3c24xx_nand_probe, ++ .remove = s3c24xx_nand_remove, ++ .suspend = s3c24xx_nand_suspend, ++ .resume = s3c24xx_nand_resume, ++ .id_table = s3c24xx_driver_ids, ++ .driver = { ++ .name = "s3c24xx-nand", ++ .of_match_table = s3c24xx_nand_dt_ids, ++ }, ++}; ++ ++module_platform_driver(s3c24xx_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ben Dooks "); ++MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); +diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c +new file mode 100644 +index 00000000..6076dba +--- /dev/null ++++ b/drivers/mtd/nand/raw/sh_flctl.c +@@ -0,0 +1,1253 @@ ++/* ++ * SuperH FLCTL nand controller ++ * ++ * Copyright (c) 2008 Renesas Solutions Corp. ++ * Copyright (c) 2008 Atom Create Engineering Co., Ltd. ++ * ++ * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor ++ * ++ * 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; version 2 of the License. ++ * ++ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 0; ++ oobregion->length = chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 12; ++ oobregion->length = 4; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { ++ .ecc = flctl_4secc_ooblayout_sp_ecc, ++ .free = flctl_4secc_ooblayout_sp_free, ++}; ++ ++static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 6; ++ oobregion->length = chip->ecc.bytes; ++ ++ return 0; ++} ++ ++static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (section >= chip->ecc.steps) ++ return -ERANGE; ++ ++ oobregion->offset = section * 16; ++ oobregion->length = 6; ++ ++ if (!section) { ++ oobregion->offset += 2; ++ oobregion->length -= 2; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { ++ .ecc = flctl_4secc_ooblayout_lp_ecc, ++ .free = flctl_4secc_ooblayout_lp_free, ++}; ++ ++static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; ++ ++static struct nand_bbt_descr flctl_4secc_smallpage = { ++ .options = NAND_BBT_SCAN2NDPAGE, ++ .offs = 11, ++ .len = 1, ++ .pattern = scan_ff_pattern, ++}; ++ ++static struct nand_bbt_descr flctl_4secc_largepage = { ++ .options = NAND_BBT_SCAN2NDPAGE, ++ .offs = 0, ++ .len = 2, ++ .pattern = scan_ff_pattern, ++}; ++ ++static void empty_fifo(struct sh_flctl *flctl) ++{ ++ writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); ++ writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); ++} ++ ++static void start_translation(struct sh_flctl *flctl) ++{ ++ writeb(TRSTRT, FLTRCR(flctl)); ++} ++ ++static void timeout_error(struct sh_flctl *flctl, const char *str) ++{ ++ dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); ++} ++ ++static void wait_completion(struct sh_flctl *flctl) ++{ ++ uint32_t timeout = LOOP_TIMEOUT_MAX; ++ ++ while (timeout--) { ++ if (readb(FLTRCR(flctl)) & TREND) { ++ writeb(0x0, FLTRCR(flctl)); ++ return; ++ } ++ udelay(1); ++ } ++ ++ timeout_error(flctl, __func__); ++ writeb(0x0, FLTRCR(flctl)); ++} ++ ++static void flctl_dma_complete(void *param) ++{ ++ struct sh_flctl *flctl = param; ++ ++ complete(&flctl->dma_complete); ++} ++ ++static void flctl_release_dma(struct sh_flctl *flctl) ++{ ++ if (flctl->chan_fifo0_rx) { ++ dma_release_channel(flctl->chan_fifo0_rx); ++ flctl->chan_fifo0_rx = NULL; ++ } ++ if (flctl->chan_fifo0_tx) { ++ dma_release_channel(flctl->chan_fifo0_tx); ++ flctl->chan_fifo0_tx = NULL; ++ } ++} ++ ++static void flctl_setup_dma(struct sh_flctl *flctl) ++{ ++ dma_cap_mask_t mask; ++ struct dma_slave_config cfg; ++ struct platform_device *pdev = flctl->pdev; ++ struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); ++ int ret; ++ ++ if (!pdata) ++ return; ++ ++ if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0) ++ return; ++ ++ /* We can only either use DMA for both Tx and Rx or not use it at all */ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ ++ flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter, ++ (void *)(uintptr_t)pdata->slave_id_fifo0_tx); ++ dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__, ++ flctl->chan_fifo0_tx); ++ ++ if (!flctl->chan_fifo0_tx) ++ return; ++ ++ memset(&cfg, 0, sizeof(cfg)); ++ cfg.direction = DMA_MEM_TO_DEV; ++ cfg.dst_addr = flctl->fifo; ++ cfg.src_addr = 0; ++ ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg); ++ if (ret < 0) ++ goto err; ++ ++ flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter, ++ (void *)(uintptr_t)pdata->slave_id_fifo0_rx); ++ dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__, ++ flctl->chan_fifo0_rx); ++ ++ if (!flctl->chan_fifo0_rx) ++ goto err; ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.dst_addr = 0; ++ cfg.src_addr = flctl->fifo; ++ ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg); ++ if (ret < 0) ++ goto err; ++ ++ init_completion(&flctl->dma_complete); ++ ++ return; ++ ++err: ++ flctl_release_dma(flctl); ++} ++ ++static void set_addr(struct mtd_info *mtd, int column, int page_addr) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ uint32_t addr = 0; ++ ++ if (column == -1) { ++ addr = page_addr; /* ERASE1 */ ++ } else if (page_addr != -1) { ++ /* SEQIN, READ0, etc.. */ ++ if (flctl->chip.options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ if (flctl->page_size) { ++ addr = column & 0x0FFF; ++ addr |= (page_addr & 0xff) << 16; ++ addr |= ((page_addr >> 8) & 0xff) << 24; ++ /* big than 128MB */ ++ if (flctl->rw_ADRCNT == ADRCNT2_E) { ++ uint32_t addr2; ++ addr2 = (page_addr >> 16) & 0xff; ++ writel(addr2, FLADR2(flctl)); ++ } ++ } else { ++ addr = column; ++ addr |= (page_addr & 0xff) << 8; ++ addr |= ((page_addr >> 8) & 0xff) << 16; ++ addr |= ((page_addr >> 16) & 0xff) << 24; ++ } ++ } ++ writel(addr, FLADR(flctl)); ++} ++ ++static void wait_rfifo_ready(struct sh_flctl *flctl) ++{ ++ uint32_t timeout = LOOP_TIMEOUT_MAX; ++ ++ while (timeout--) { ++ uint32_t val; ++ /* check FIFO */ ++ val = readl(FLDTCNTR(flctl)) >> 16; ++ if (val & 0xFF) ++ return; ++ udelay(1); ++ } ++ timeout_error(flctl, __func__); ++} ++ ++static void wait_wfifo_ready(struct sh_flctl *flctl) ++{ ++ uint32_t len, timeout = LOOP_TIMEOUT_MAX; ++ ++ while (timeout--) { ++ /* check FIFO */ ++ len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; ++ if (len >= 4) ++ return; ++ udelay(1); ++ } ++ timeout_error(flctl, __func__); ++} ++ ++static enum flctl_ecc_res_t wait_recfifo_ready ++ (struct sh_flctl *flctl, int sector_number) ++{ ++ uint32_t timeout = LOOP_TIMEOUT_MAX; ++ void __iomem *ecc_reg[4]; ++ int i; ++ int state = FL_SUCCESS; ++ uint32_t data, size; ++ ++ /* ++ * First this loops checks in FLDTCNTR if we are ready to read out the ++ * oob data. This is the case if either all went fine without errors or ++ * if the bottom part of the loop corrected the errors or marked them as ++ * uncorrectable and the controller is given time to push the data into ++ * the FIFO. ++ */ ++ while (timeout--) { ++ /* check if all is ok and we can read out the OOB */ ++ size = readl(FLDTCNTR(flctl)) >> 24; ++ if ((size & 0xFF) == 4) ++ return state; ++ ++ /* check if a correction code has been calculated */ ++ if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) { ++ /* ++ * either we wait for the fifo to be filled or a ++ * correction pattern is being generated ++ */ ++ udelay(1); ++ continue; ++ } ++ ++ /* check for an uncorrectable error */ ++ if (readl(FL4ECCCR(flctl)) & _4ECCFA) { ++ /* check if we face a non-empty page */ ++ for (i = 0; i < 512; i++) { ++ if (flctl->done_buff[i] != 0xff) { ++ state = FL_ERROR; /* can't correct */ ++ break; ++ } ++ } ++ ++ if (state == FL_SUCCESS) ++ dev_dbg(&flctl->pdev->dev, ++ "reading empty sector %d, ecc error ignored\n", ++ sector_number); ++ ++ writel(0, FL4ECCCR(flctl)); ++ continue; ++ } ++ ++ /* start error correction */ ++ ecc_reg[0] = FL4ECCRESULT0(flctl); ++ ecc_reg[1] = FL4ECCRESULT1(flctl); ++ ecc_reg[2] = FL4ECCRESULT2(flctl); ++ ecc_reg[3] = FL4ECCRESULT3(flctl); ++ ++ for (i = 0; i < 3; i++) { ++ uint8_t org; ++ unsigned int index; ++ ++ data = readl(ecc_reg[i]); ++ ++ if (flctl->page_size) ++ index = (512 * sector_number) + ++ (data >> 16); ++ else ++ index = data >> 16; ++ ++ org = flctl->done_buff[index]; ++ flctl->done_buff[index] = org ^ (data & 0xFF); ++ } ++ state = FL_REPAIRABLE; ++ writel(0, FL4ECCCR(flctl)); ++ } ++ ++ timeout_error(flctl, __func__); ++ return FL_TIMEOUT; /* timeout */ ++} ++ ++static void wait_wecfifo_ready(struct sh_flctl *flctl) ++{ ++ uint32_t timeout = LOOP_TIMEOUT_MAX; ++ uint32_t len; ++ ++ while (timeout--) { ++ /* check FLECFIFO */ ++ len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; ++ if (len >= 4) ++ return; ++ udelay(1); ++ } ++ timeout_error(flctl, __func__); ++} ++ ++static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, ++ int len, enum dma_data_direction dir) ++{ ++ struct dma_async_tx_descriptor *desc = NULL; ++ struct dma_chan *chan; ++ enum dma_transfer_direction tr_dir; ++ dma_addr_t dma_addr; ++ dma_cookie_t cookie; ++ uint32_t reg; ++ int ret; ++ ++ if (dir == DMA_FROM_DEVICE) { ++ chan = flctl->chan_fifo0_rx; ++ tr_dir = DMA_DEV_TO_MEM; ++ } else { ++ chan = flctl->chan_fifo0_tx; ++ tr_dir = DMA_MEM_TO_DEV; ++ } ++ ++ dma_addr = dma_map_single(chan->device->dev, buf, len, dir); ++ ++ if (!dma_mapping_error(chan->device->dev, dma_addr)) ++ desc = dmaengine_prep_slave_single(chan, dma_addr, len, ++ tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ ++ if (desc) { ++ reg = readl(FLINTDMACR(flctl)); ++ reg |= DREQ0EN; ++ writel(reg, FLINTDMACR(flctl)); ++ ++ desc->callback = flctl_dma_complete; ++ desc->callback_param = flctl; ++ cookie = dmaengine_submit(desc); ++ if (dma_submit_error(cookie)) { ++ ret = dma_submit_error(cookie); ++ dev_warn(&flctl->pdev->dev, ++ "DMA submit failed, falling back to PIO\n"); ++ goto out; ++ } ++ ++ dma_async_issue_pending(chan); ++ } else { ++ /* DMA failed, fall back to PIO */ ++ flctl_release_dma(flctl); ++ dev_warn(&flctl->pdev->dev, ++ "DMA failed, falling back to PIO\n"); ++ ret = -EIO; ++ goto out; ++ } ++ ++ ret = ++ wait_for_completion_timeout(&flctl->dma_complete, ++ msecs_to_jiffies(3000)); ++ ++ if (ret <= 0) { ++ dmaengine_terminate_all(chan); ++ dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n"); ++ } ++ ++out: ++ reg = readl(FLINTDMACR(flctl)); ++ reg &= ~DREQ0EN; ++ writel(reg, FLINTDMACR(flctl)); ++ ++ dma_unmap_single(chan->device->dev, dma_addr, len, dir); ++ ++ /* ret > 0 is success */ ++ return ret; ++} ++ ++static void read_datareg(struct sh_flctl *flctl, int offset) ++{ ++ unsigned long data; ++ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; ++ ++ wait_completion(flctl); ++ ++ data = readl(FLDATAR(flctl)); ++ *buf = le32_to_cpu(data); ++} ++ ++static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) ++{ ++ int i, len_4align; ++ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; ++ ++ len_4align = (rlen + 3) / 4; ++ ++ /* initiate DMA transfer */ ++ if (flctl->chan_fifo0_rx && rlen >= 32 && ++ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0) ++ goto convert; /* DMA success */ ++ ++ /* do polling transfer */ ++ for (i = 0; i < len_4align; i++) { ++ wait_rfifo_ready(flctl); ++ buf[i] = readl(FLDTFIFO(flctl)); ++ } ++ ++convert: ++ for (i = 0; i < len_4align; i++) ++ buf[i] = be32_to_cpu(buf[i]); ++} ++ ++static enum flctl_ecc_res_t read_ecfiforeg ++ (struct sh_flctl *flctl, uint8_t *buff, int sector) ++{ ++ int i; ++ enum flctl_ecc_res_t res; ++ unsigned long *ecc_buf = (unsigned long *)buff; ++ ++ res = wait_recfifo_ready(flctl , sector); ++ ++ if (res != FL_ERROR) { ++ for (i = 0; i < 4; i++) { ++ ecc_buf[i] = readl(FLECFIFO(flctl)); ++ ecc_buf[i] = be32_to_cpu(ecc_buf[i]); ++ } ++ } ++ ++ return res; ++} ++ ++static void write_fiforeg(struct sh_flctl *flctl, int rlen, ++ unsigned int offset) ++{ ++ int i, len_4align; ++ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; ++ ++ len_4align = (rlen + 3) / 4; ++ for (i = 0; i < len_4align; i++) { ++ wait_wfifo_ready(flctl); ++ writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); ++ } ++} ++ ++static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, ++ unsigned int offset) ++{ ++ int i, len_4align; ++ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; ++ ++ len_4align = (rlen + 3) / 4; ++ ++ for (i = 0; i < len_4align; i++) ++ buf[i] = cpu_to_be32(buf[i]); ++ ++ /* initiate DMA transfer */ ++ if (flctl->chan_fifo0_tx && rlen >= 32 && ++ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0) ++ return; /* DMA success */ ++ ++ /* do polling transfer */ ++ for (i = 0; i < len_4align; i++) { ++ wait_wecfifo_ready(flctl); ++ writel(buf[i], FLECFIFO(flctl)); ++ } ++} ++ ++static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; ++ uint32_t flcmdcr_val, addr_len_bytes = 0; ++ ++ /* Set SNAND bit if page size is 2048byte */ ++ if (flctl->page_size) ++ flcmncr_val |= SNAND_E; ++ else ++ flcmncr_val &= ~SNAND_E; ++ ++ /* default FLCMDCR val */ ++ flcmdcr_val = DOCMD1_E | DOADR_E; ++ ++ /* Set for FLCMDCR */ ++ switch (cmd) { ++ case NAND_CMD_ERASE1: ++ addr_len_bytes = flctl->erase_ADRCNT; ++ flcmdcr_val |= DOCMD2_E; ++ break; ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ case NAND_CMD_RNDOUT: ++ addr_len_bytes = flctl->rw_ADRCNT; ++ flcmdcr_val |= CDSRC_E; ++ if (flctl->chip.options & NAND_BUSWIDTH_16) ++ flcmncr_val |= SEL_16BIT; ++ break; ++ case NAND_CMD_SEQIN: ++ /* This case is that cmd is READ0 or READ1 or READ00 */ ++ flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ ++ break; ++ case NAND_CMD_PAGEPROG: ++ addr_len_bytes = flctl->rw_ADRCNT; ++ flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; ++ if (flctl->chip.options & NAND_BUSWIDTH_16) ++ flcmncr_val |= SEL_16BIT; ++ break; ++ case NAND_CMD_READID: ++ flcmncr_val &= ~SNAND_E; ++ flcmdcr_val |= CDSRC_E; ++ addr_len_bytes = ADRCNT_1; ++ break; ++ case NAND_CMD_STATUS: ++ case NAND_CMD_RESET: ++ flcmncr_val &= ~SNAND_E; ++ flcmdcr_val &= ~(DOADR_E | DOSR_E); ++ break; ++ default: ++ break; ++ } ++ ++ /* Set address bytes parameter */ ++ flcmdcr_val |= addr_len_bytes; ++ ++ /* Now actually write */ ++ writel(flcmncr_val, FLCMNCR(flctl)); ++ writel(flcmdcr_val, FLCMDCR(flctl)); ++ writel(flcmcdr_val, FLCMCDR(flctl)); ++} ++ ++static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ nand_read_page_op(chip, page, 0, buf, mtd->writesize); ++ if (oob_required) ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ return 0; ++} ++ ++static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ return nand_prog_page_end_op(chip); ++} ++ ++static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ int sector, page_sectors; ++ enum flctl_ecc_res_t ecc_result; ++ ++ page_sectors = flctl->page_size ? 4 : 1; ++ ++ set_cmd_regs(mtd, NAND_CMD_READ0, ++ (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); ++ ++ writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, ++ FLCMNCR(flctl)); ++ writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); ++ writel(page_addr << 2, FLADR(flctl)); ++ ++ empty_fifo(flctl); ++ start_translation(flctl); ++ ++ for (sector = 0; sector < page_sectors; sector++) { ++ read_fiforeg(flctl, 512, 512 * sector); ++ ++ ecc_result = read_ecfiforeg(flctl, ++ &flctl->done_buff[mtd->writesize + 16 * sector], ++ sector); ++ ++ switch (ecc_result) { ++ case FL_REPAIRABLE: ++ dev_info(&flctl->pdev->dev, ++ "applied ecc on page 0x%x", page_addr); ++ mtd->ecc_stats.corrected++; ++ break; ++ case FL_ERROR: ++ dev_warn(&flctl->pdev->dev, ++ "page 0x%x contains corrupted data\n", ++ page_addr); ++ mtd->ecc_stats.failed++; ++ break; ++ default: ++ ; ++ } ++ } ++ ++ wait_completion(flctl); ++ ++ writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), ++ FLCMNCR(flctl)); ++} ++ ++static void execmd_read_oob(struct mtd_info *mtd, int page_addr) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ int page_sectors = flctl->page_size ? 4 : 1; ++ int i; ++ ++ set_cmd_regs(mtd, NAND_CMD_READ0, ++ (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); ++ ++ empty_fifo(flctl); ++ ++ for (i = 0; i < page_sectors; i++) { ++ set_addr(mtd, (512 + 16) * i + 512 , page_addr); ++ writel(16, FLDTCNTR(flctl)); ++ ++ start_translation(flctl); ++ read_fiforeg(flctl, 16, 16 * i); ++ wait_completion(flctl); ++ } ++} ++ ++static void execmd_write_page_sector(struct mtd_info *mtd) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ int page_addr = flctl->seqin_page_addr; ++ int sector, page_sectors; ++ ++ page_sectors = flctl->page_size ? 4 : 1; ++ ++ set_cmd_regs(mtd, NAND_CMD_PAGEPROG, ++ (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); ++ ++ empty_fifo(flctl); ++ writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); ++ writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); ++ writel(page_addr << 2, FLADR(flctl)); ++ start_translation(flctl); ++ ++ for (sector = 0; sector < page_sectors; sector++) { ++ write_fiforeg(flctl, 512, 512 * sector); ++ write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector); ++ } ++ ++ wait_completion(flctl); ++ writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); ++} ++ ++static void execmd_write_oob(struct mtd_info *mtd) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ int page_addr = flctl->seqin_page_addr; ++ int sector, page_sectors; ++ ++ page_sectors = flctl->page_size ? 4 : 1; ++ ++ set_cmd_regs(mtd, NAND_CMD_PAGEPROG, ++ (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); ++ ++ for (sector = 0; sector < page_sectors; sector++) { ++ empty_fifo(flctl); ++ set_addr(mtd, sector * 528 + 512, page_addr); ++ writel(16, FLDTCNTR(flctl)); /* set read size */ ++ ++ start_translation(flctl); ++ write_fiforeg(flctl, 16, 16 * sector); ++ wait_completion(flctl); ++ } ++} ++ ++static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ uint32_t read_cmd = 0; ++ ++ pm_runtime_get_sync(&flctl->pdev->dev); ++ ++ flctl->read_bytes = 0; ++ if (command != NAND_CMD_PAGEPROG) ++ flctl->index = 0; ++ ++ switch (command) { ++ case NAND_CMD_READ1: ++ case NAND_CMD_READ0: ++ if (flctl->hwecc) { ++ /* read page with hwecc */ ++ execmd_read_page_sector(mtd, page_addr); ++ break; ++ } ++ if (flctl->page_size) ++ set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) ++ | command); ++ else ++ set_cmd_regs(mtd, command, command); ++ ++ set_addr(mtd, 0, page_addr); ++ ++ flctl->read_bytes = mtd->writesize + mtd->oobsize; ++ if (flctl->chip.options & NAND_BUSWIDTH_16) ++ column >>= 1; ++ flctl->index += column; ++ goto read_normal_exit; ++ ++ case NAND_CMD_READOOB: ++ if (flctl->hwecc) { ++ /* read page with hwecc */ ++ execmd_read_oob(mtd, page_addr); ++ break; ++ } ++ ++ if (flctl->page_size) { ++ set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) ++ | NAND_CMD_READ0); ++ set_addr(mtd, mtd->writesize, page_addr); ++ } else { ++ set_cmd_regs(mtd, command, command); ++ set_addr(mtd, 0, page_addr); ++ } ++ flctl->read_bytes = mtd->oobsize; ++ goto read_normal_exit; ++ ++ case NAND_CMD_RNDOUT: ++ if (flctl->hwecc) ++ break; ++ ++ if (flctl->page_size) ++ set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) ++ | command); ++ else ++ set_cmd_regs(mtd, command, command); ++ ++ set_addr(mtd, column, 0); ++ ++ flctl->read_bytes = mtd->writesize + mtd->oobsize - column; ++ goto read_normal_exit; ++ ++ case NAND_CMD_READID: ++ set_cmd_regs(mtd, command, command); ++ ++ /* READID is always performed using an 8-bit bus */ ++ if (flctl->chip.options & NAND_BUSWIDTH_16) ++ column <<= 1; ++ set_addr(mtd, column, 0); ++ ++ flctl->read_bytes = 8; ++ writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ ++ empty_fifo(flctl); ++ start_translation(flctl); ++ read_fiforeg(flctl, flctl->read_bytes, 0); ++ wait_completion(flctl); ++ break; ++ ++ case NAND_CMD_ERASE1: ++ flctl->erase1_page_addr = page_addr; ++ break; ++ ++ case NAND_CMD_ERASE2: ++ set_cmd_regs(mtd, NAND_CMD_ERASE1, ++ (command << 8) | NAND_CMD_ERASE1); ++ set_addr(mtd, -1, flctl->erase1_page_addr); ++ start_translation(flctl); ++ wait_completion(flctl); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ if (!flctl->page_size) { ++ /* output read command */ ++ if (column >= mtd->writesize) { ++ column -= mtd->writesize; ++ read_cmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ read_cmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ read_cmd = NAND_CMD_READ1; ++ } ++ } ++ flctl->seqin_column = column; ++ flctl->seqin_page_addr = page_addr; ++ flctl->seqin_read_cmd = read_cmd; ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ empty_fifo(flctl); ++ if (!flctl->page_size) { ++ set_cmd_regs(mtd, NAND_CMD_SEQIN, ++ flctl->seqin_read_cmd); ++ set_addr(mtd, -1, -1); ++ writel(0, FLDTCNTR(flctl)); /* set 0 size */ ++ start_translation(flctl); ++ wait_completion(flctl); ++ } ++ if (flctl->hwecc) { ++ /* write page with hwecc */ ++ if (flctl->seqin_column == mtd->writesize) ++ execmd_write_oob(mtd); ++ else if (!flctl->seqin_column) ++ execmd_write_page_sector(mtd); ++ else ++ printk(KERN_ERR "Invalid address !?\n"); ++ break; ++ } ++ set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); ++ set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); ++ writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ ++ start_translation(flctl); ++ write_fiforeg(flctl, flctl->index, 0); ++ wait_completion(flctl); ++ break; ++ ++ case NAND_CMD_STATUS: ++ set_cmd_regs(mtd, command, command); ++ set_addr(mtd, -1, -1); ++ ++ flctl->read_bytes = 1; ++ writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ ++ start_translation(flctl); ++ read_datareg(flctl, 0); /* read and end */ ++ break; ++ ++ case NAND_CMD_RESET: ++ set_cmd_regs(mtd, command, command); ++ set_addr(mtd, -1, -1); ++ ++ writel(0, FLDTCNTR(flctl)); /* set 0 size */ ++ start_translation(flctl); ++ wait_completion(flctl); ++ break; ++ ++ default: ++ break; ++ } ++ goto runtime_exit; ++ ++read_normal_exit: ++ writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ ++ empty_fifo(flctl); ++ start_translation(flctl); ++ read_fiforeg(flctl, flctl->read_bytes, 0); ++ wait_completion(flctl); ++runtime_exit: ++ pm_runtime_put_sync(&flctl->pdev->dev); ++ return; ++} ++ ++static void flctl_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ int ret; ++ ++ switch (chipnr) { ++ case -1: ++ flctl->flcmncr_base &= ~CE0_ENABLE; ++ ++ pm_runtime_get_sync(&flctl->pdev->dev); ++ writel(flctl->flcmncr_base, FLCMNCR(flctl)); ++ ++ if (flctl->qos_request) { ++ dev_pm_qos_remove_request(&flctl->pm_qos); ++ flctl->qos_request = 0; ++ } ++ ++ pm_runtime_put_sync(&flctl->pdev->dev); ++ break; ++ case 0: ++ flctl->flcmncr_base |= CE0_ENABLE; ++ ++ if (!flctl->qos_request) { ++ ret = dev_pm_qos_add_request(&flctl->pdev->dev, ++ &flctl->pm_qos, ++ DEV_PM_QOS_RESUME_LATENCY, ++ 100); ++ if (ret < 0) ++ dev_err(&flctl->pdev->dev, ++ "PM QoS request failed: %d\n", ret); ++ flctl->qos_request = 1; ++ } ++ ++ if (flctl->holden) { ++ pm_runtime_get_sync(&flctl->pdev->dev); ++ writel(HOLDEN, FLHOLDCR(flctl)); ++ pm_runtime_put_sync(&flctl->pdev->dev); ++ } ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ ++ memcpy(&flctl->done_buff[flctl->index], buf, len); ++ flctl->index += len; ++} ++ ++static uint8_t flctl_read_byte(struct mtd_info *mtd) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ uint8_t data; ++ ++ data = flctl->done_buff[flctl->index]; ++ flctl->index++; ++ return data; ++} ++ ++static uint16_t flctl_read_word(struct mtd_info *mtd) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index]; ++ ++ flctl->index += 2; ++ return *buf; ++} ++ ++static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ ++ memcpy(buf, &flctl->done_buff[flctl->index], len); ++ flctl->index += len; ++} ++ ++static int flctl_chip_init_tail(struct mtd_info *mtd) ++{ ++ struct sh_flctl *flctl = mtd_to_flctl(mtd); ++ struct nand_chip *chip = &flctl->chip; ++ ++ if (mtd->writesize == 512) { ++ flctl->page_size = 0; ++ if (chip->chipsize > (32 << 20)) { ++ /* big than 32MB */ ++ flctl->rw_ADRCNT = ADRCNT_4; ++ flctl->erase_ADRCNT = ADRCNT_3; ++ } else if (chip->chipsize > (2 << 16)) { ++ /* big than 128KB */ ++ flctl->rw_ADRCNT = ADRCNT_3; ++ flctl->erase_ADRCNT = ADRCNT_2; ++ } else { ++ flctl->rw_ADRCNT = ADRCNT_2; ++ flctl->erase_ADRCNT = ADRCNT_1; ++ } ++ } else { ++ flctl->page_size = 1; ++ if (chip->chipsize > (128 << 20)) { ++ /* big than 128MB */ ++ flctl->rw_ADRCNT = ADRCNT2_E; ++ flctl->erase_ADRCNT = ADRCNT_3; ++ } else if (chip->chipsize > (8 << 16)) { ++ /* big than 512KB */ ++ flctl->rw_ADRCNT = ADRCNT_4; ++ flctl->erase_ADRCNT = ADRCNT_2; ++ } else { ++ flctl->rw_ADRCNT = ADRCNT_3; ++ flctl->erase_ADRCNT = ADRCNT_1; ++ } ++ } ++ ++ if (flctl->hwecc) { ++ if (mtd->writesize == 512) { ++ mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); ++ chip->badblock_pattern = &flctl_4secc_smallpage; ++ } else { ++ mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); ++ chip->badblock_pattern = &flctl_4secc_largepage; ++ } ++ ++ chip->ecc.size = 512; ++ chip->ecc.bytes = 10; ++ chip->ecc.strength = 4; ++ chip->ecc.read_page = flctl_read_page_hwecc; ++ chip->ecc.write_page = flctl_write_page_hwecc; ++ chip->ecc.mode = NAND_ECC_HW; ++ ++ /* 4 symbols ECC enabled */ ++ flctl->flcmncr_base |= _4ECCEN; ++ } else { ++ chip->ecc.mode = NAND_ECC_SOFT; ++ chip->ecc.algo = NAND_ECC_HAMMING; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t flctl_handle_flste(int irq, void *dev_id) ++{ ++ struct sh_flctl *flctl = dev_id; ++ ++ dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); ++ writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); ++ ++ return IRQ_HANDLED; ++} ++ ++struct flctl_soc_config { ++ unsigned long flcmncr_val; ++ unsigned has_hwecc:1; ++ unsigned use_holden:1; ++}; ++ ++static struct flctl_soc_config flctl_sh7372_config = { ++ .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, ++ .has_hwecc = 1, ++ .use_holden = 1, ++}; ++ ++static const struct of_device_id of_flctl_match[] = { ++ { .compatible = "renesas,shmobile-flctl-sh7372", ++ .data = &flctl_sh7372_config }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, of_flctl_match); ++ ++static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) ++{ ++ const struct of_device_id *match; ++ struct flctl_soc_config *config; ++ struct sh_flctl_platform_data *pdata; ++ ++ match = of_match_device(of_flctl_match, dev); ++ if (match) ++ config = (struct flctl_soc_config *)match->data; ++ else { ++ dev_err(dev, "%s: no OF configuration attached\n", __func__); ++ return NULL; ++ } ++ ++ pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), ++ GFP_KERNEL); ++ if (!pdata) ++ return NULL; ++ ++ /* set SoC specific options */ ++ pdata->flcmncr_val = config->flcmncr_val; ++ pdata->has_hwecc = config->has_hwecc; ++ pdata->use_holden = config->use_holden; ++ ++ return pdata; ++} ++ ++static int flctl_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct sh_flctl *flctl; ++ struct mtd_info *flctl_mtd; ++ struct nand_chip *nand; ++ struct sh_flctl_platform_data *pdata; ++ int ret; ++ int irq; ++ ++ flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL); ++ if (!flctl) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ flctl->reg = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(flctl->reg)) ++ return PTR_ERR(flctl->reg); ++ flctl->fifo = res->start + 0x24; /* FLDTFIFO */ ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq); ++ return irq; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, ++ "flste", flctl); ++ if (ret) { ++ dev_err(&pdev->dev, "request interrupt failed.\n"); ++ return ret; ++ } ++ ++ if (pdev->dev.of_node) ++ pdata = flctl_parse_dt(&pdev->dev); ++ else ++ pdata = dev_get_platdata(&pdev->dev); ++ ++ if (!pdata) { ++ dev_err(&pdev->dev, "no setup data defined\n"); ++ return -EINVAL; ++ } ++ ++ platform_set_drvdata(pdev, flctl); ++ nand = &flctl->chip; ++ flctl_mtd = nand_to_mtd(nand); ++ nand_set_flash_node(nand, pdev->dev.of_node); ++ flctl_mtd->dev.parent = &pdev->dev; ++ flctl->pdev = pdev; ++ flctl->hwecc = pdata->has_hwecc; ++ flctl->holden = pdata->use_holden; ++ flctl->flcmncr_base = pdata->flcmncr_val; ++ flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; ++ ++ /* Set address of hardware control function */ ++ /* 20 us command delay time */ ++ nand->chip_delay = 20; ++ ++ nand->read_byte = flctl_read_byte; ++ nand->read_word = flctl_read_word; ++ nand->write_buf = flctl_write_buf; ++ nand->read_buf = flctl_read_buf; ++ nand->select_chip = flctl_select_chip; ++ nand->cmdfunc = flctl_cmdfunc; ++ nand->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ nand->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ if (pdata->flcmncr_val & SEL_16BIT) ++ nand->options |= NAND_BUSWIDTH_16; ++ ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_resume(&pdev->dev); ++ ++ flctl_setup_dma(flctl); ++ ++ ret = nand_scan_ident(flctl_mtd, 1, NULL); ++ if (ret) ++ goto err_chip; ++ ++ if (nand->options & NAND_BUSWIDTH_16) { ++ /* ++ * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). ++ * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign ++ * flctl->flcmncr_base to pdata->flcmncr_val. ++ */ ++ pdata->flcmncr_val |= SEL_16BIT; ++ flctl->flcmncr_base = pdata->flcmncr_val; ++ } ++ ++ ret = flctl_chip_init_tail(flctl_mtd); ++ if (ret) ++ goto err_chip; ++ ++ ret = nand_scan_tail(flctl_mtd); ++ if (ret) ++ goto err_chip; ++ ++ ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); ++ ++ return 0; ++ ++err_chip: ++ flctl_release_dma(flctl); ++ pm_runtime_disable(&pdev->dev); ++ return ret; ++} ++ ++static int flctl_remove(struct platform_device *pdev) ++{ ++ struct sh_flctl *flctl = platform_get_drvdata(pdev); ++ ++ flctl_release_dma(flctl); ++ nand_release(nand_to_mtd(&flctl->chip)); ++ pm_runtime_disable(&pdev->dev); ++ ++ return 0; ++} ++ ++static struct platform_driver flctl_driver = { ++ .remove = flctl_remove, ++ .driver = { ++ .name = "sh_flctl", ++ .of_match_table = of_match_ptr(of_flctl_match), ++ }, ++}; ++ ++module_platform_driver_probe(flctl_driver, flctl_probe); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Yoshihiro Shimoda"); ++MODULE_DESCRIPTION("SuperH FLCTL driver"); ++MODULE_ALIAS("platform:sh_flctl"); +diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c +new file mode 100644 +index 00000000..f59c455 +--- /dev/null ++++ b/drivers/mtd/nand/raw/sharpsl.c +@@ -0,0 +1,235 @@ ++/* ++ * drivers/mtd/nand/sharpsl.c ++ * ++ * Copyright (C) 2004 Richard Purdie ++ * Copyright (C) 2008 Dmitry Baryshkov ++ * ++ * Based on Sharp's NAND driver sharp_sl.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++struct sharpsl_nand { ++ struct nand_chip chip; ++ ++ void __iomem *io; ++}; ++ ++static inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip); ++} ++ ++/* register offset */ ++#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ ++#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ ++#define ECCCP 0x08 /* column parity 5 - 0 bit */ ++#define ECCCNTR 0x0C /* ECC byte counter */ ++#define ECCCLRR 0x10 /* cleare ECC */ ++#define FLASHIO 0x14 /* Flash I/O */ ++#define FLASHCTL 0x18 /* Flash Control */ ++ ++/* Flash control bit */ ++#define FLRYBY (1 << 5) ++#define FLCE1 (1 << 4) ++#define FLWP (1 << 3) ++#define FLALE (1 << 2) ++#define FLCLE (1 << 1) ++#define FLCE0 (1 << 0) ++ ++/* ++ * hardware specific access to control-lines ++ * ctrl: ++ * NAND_CNE: bit 0 -> ! bit 0 & 4 ++ * NAND_CLE: bit 1 -> bit 1 ++ * NAND_ALE: bit 2 -> bit 2 ++ * ++ */ ++static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ unsigned char bits = ctrl & 0x07; ++ ++ bits |= (ctrl & 0x01) << 4; ++ ++ bits ^= 0x11; ++ ++ writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int sharpsl_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); ++ return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); ++} ++ ++static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); ++ writeb(0, sharpsl->io + ECCCLRR); ++} ++ ++static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) ++{ ++ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); ++ ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); ++ ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); ++ ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; ++ return readb(sharpsl->io + ECCCNTR) != 0; ++} ++ ++/* ++ * Main initialization routine ++ */ ++static int sharpsl_nand_probe(struct platform_device *pdev) ++{ ++ struct nand_chip *this; ++ struct mtd_info *mtd; ++ struct resource *r; ++ int err = 0; ++ struct sharpsl_nand *sharpsl; ++ struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); ++ ++ if (!data) { ++ dev_err(&pdev->dev, "no platform data!\n"); ++ return -EINVAL; ++ } ++ ++ /* Allocate memory for MTD device structure and private data */ ++ sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); ++ if (!sharpsl) ++ return -ENOMEM; ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!r) { ++ dev_err(&pdev->dev, "no io memory resource defined!\n"); ++ err = -ENODEV; ++ goto err_get_res; ++ } ++ ++ /* map physical address */ ++ sharpsl->io = ioremap(r->start, resource_size(r)); ++ if (!sharpsl->io) { ++ dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n"); ++ err = -EIO; ++ goto err_ioremap; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *)(&sharpsl->chip); ++ ++ /* Link the private data with the MTD structure */ ++ mtd = nand_to_mtd(this); ++ mtd->dev.parent = &pdev->dev; ++ mtd_set_ooblayout(mtd, data->ecc_layout); ++ ++ platform_set_drvdata(pdev, sharpsl); ++ ++ /* ++ * PXA initialize ++ */ ++ writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = sharpsl->io + FLASHIO; ++ this->IO_ADDR_W = sharpsl->io + FLASHIO; ++ /* Set address of hardware control function */ ++ this->cmd_ctrl = sharpsl_nand_hwcontrol; ++ this->dev_ready = sharpsl_nand_dev_ready; ++ /* 15 us command delay time */ ++ this->chip_delay = 15; ++ /* set eccmode using hardware ECC */ ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 256; ++ this->ecc.bytes = 3; ++ this->ecc.strength = 1; ++ this->badblock_pattern = data->badblock_pattern; ++ this->ecc.hwctl = sharpsl_nand_enable_hwecc; ++ this->ecc.calculate = sharpsl_nand_calculate_ecc; ++ this->ecc.correct = nand_correct_data; ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(mtd, 1); ++ if (err) ++ goto err_scan; ++ ++ /* Register the partitions */ ++ mtd->name = "sharpsl-nand"; ++ ++ err = mtd_device_parse_register(mtd, data->part_parsers, NULL, ++ data->partitions, data->nr_partitions); ++ if (err) ++ goto err_add; ++ ++ /* Return happy */ ++ return 0; ++ ++err_add: ++ nand_release(mtd); ++ ++err_scan: ++ iounmap(sharpsl->io); ++err_ioremap: ++err_get_res: ++ kfree(sharpsl); ++ return err; ++} ++ ++/* ++ * Clean up routine ++ */ ++static int sharpsl_nand_remove(struct platform_device *pdev) ++{ ++ struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); ++ ++ /* Release resources, unregister device */ ++ nand_release(nand_to_mtd(&sharpsl->chip)); ++ ++ iounmap(sharpsl->io); ++ ++ /* Free the MTD device structure */ ++ kfree(sharpsl); ++ ++ return 0; ++} ++ ++static struct platform_driver sharpsl_nand_driver = { ++ .driver = { ++ .name = "sharpsl-nand", ++ }, ++ .probe = sharpsl_nand_probe, ++ .remove = sharpsl_nand_remove, ++}; ++ ++module_platform_driver(sharpsl_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Richard Purdie "); ++MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); +diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c +new file mode 100644 +index 00000000..c378705 +--- /dev/null ++++ b/drivers/mtd/nand/raw/sm_common.c +@@ -0,0 +1,202 @@ ++/* ++ * Copyright © 2009 - Maxim Levitsky ++ * Common routines & support for xD format ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include "sm_common.h" ++ ++static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section > 1) ++ return -ERANGE; ++ ++ oobregion->length = 3; ++ oobregion->offset = ((section + 1) * 8) - 3; ++ ++ return 0; ++} ++ ++static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ switch (section) { ++ case 0: ++ /* reserved */ ++ oobregion->offset = 0; ++ oobregion->length = 4; ++ break; ++ case 1: ++ /* LBA1 */ ++ oobregion->offset = 6; ++ oobregion->length = 2; ++ break; ++ case 2: ++ /* LBA2 */ ++ oobregion->offset = 11; ++ oobregion->length = 2; ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops oob_sm_ops = { ++ .ecc = oob_sm_ooblayout_ecc, ++ .free = oob_sm_ooblayout_free, ++}; ++ ++/* NOTE: This layout is is not compatabable with SmartMedia, */ ++/* because the 256 byte devices have page depenent oob layout */ ++/* However it does preserve the bad block markers */ ++/* If you use smftl, it will bypass this and work correctly */ ++/* If you not, then you break SmartMedia compliance anyway */ ++ ++static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = 3; ++ oobregion->offset = 0; ++ ++ return 0; ++} ++ ++static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ switch (section) { ++ case 0: ++ /* reserved */ ++ oobregion->offset = 3; ++ oobregion->length = 2; ++ break; ++ case 1: ++ /* LBA1 */ ++ oobregion->offset = 6; ++ oobregion->length = 2; ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops oob_sm_small_ops = { ++ .ecc = oob_sm_small_ooblayout_ecc, ++ .free = oob_sm_small_ooblayout_free, ++}; ++ ++static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) ++{ ++ struct mtd_oob_ops ops; ++ struct sm_oob oob; ++ int ret; ++ ++ memset(&oob, -1, SM_OOB_SIZE); ++ oob.block_status = 0x0F; ++ ++ /* As long as this function is called on erase block boundaries ++ it will work correctly for 256 byte nand */ ++ ops.mode = MTD_OPS_PLACE_OOB; ++ ops.ooboffs = 0; ++ ops.ooblen = mtd->oobsize; ++ ops.oobbuf = (void *)&oob; ++ ops.datbuf = NULL; ++ ++ ++ ret = mtd_write_oob(mtd, ofs, &ops); ++ if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { ++ printk(KERN_NOTICE ++ "sm_common: can't mark sector at %i as bad\n", ++ (int)ofs); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static struct nand_flash_dev nand_smartmedia_flash_ids[] = { ++ LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0), ++ LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0), ++ LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0), ++ LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0), ++ LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0), ++ LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0), ++ LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0), ++ LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0), ++ LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM), ++ LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0), ++ LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM), ++ {NULL} ++}; ++ ++static struct nand_flash_dev nand_xd_flash_ids[] = { ++ LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0), ++ LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0), ++ LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0), ++ LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0), ++ LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD), ++ LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD), ++ LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD), ++ LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD), ++ {NULL} ++}; ++ ++int sm_register_device(struct mtd_info *mtd, int smartmedia) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ chip->options |= NAND_SKIP_BBTSCAN; ++ ++ /* Scan for card properties */ ++ ret = nand_scan_ident(mtd, 1, smartmedia ? ++ nand_smartmedia_flash_ids : nand_xd_flash_ids); ++ ++ if (ret) ++ return ret; ++ ++ /* Bad block marker position */ ++ chip->badblockpos = 0x05; ++ chip->badblockbits = 7; ++ chip->block_markbad = sm_block_markbad; ++ ++ /* ECC layout */ ++ if (mtd->writesize == SM_SECTOR_SIZE) ++ mtd_set_ooblayout(mtd, &oob_sm_ops); ++ else if (mtd->writesize == SM_SMALL_PAGE) ++ mtd_set_ooblayout(mtd, &oob_sm_small_ops); ++ else ++ return -ENODEV; ++ ++ ret = nand_scan_tail(mtd); ++ ++ if (ret) ++ return ret; ++ ++ return mtd_device_register(mtd, NULL, 0); ++} ++EXPORT_SYMBOL_GPL(sm_register_device); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Maxim Levitsky "); ++MODULE_DESCRIPTION("Common SmartMedia/xD functions"); +diff --git a/drivers/mtd/nand/raw/sm_common.h b/drivers/mtd/nand/raw/sm_common.h +new file mode 100644 +index 00000000..d3e028e +--- /dev/null ++++ b/drivers/mtd/nand/raw/sm_common.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright © 2009 - Maxim Levitsky ++ * Common routines & support for SmartMedia/xD format ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++ ++/* Full oob structure as written on the flash */ ++struct sm_oob { ++ uint32_t reserved; ++ uint8_t data_status; ++ uint8_t block_status; ++ uint8_t lba_copy1[2]; ++ uint8_t ecc2[3]; ++ uint8_t lba_copy2[2]; ++ uint8_t ecc1[3]; ++} __packed; ++ ++ ++/* one sector is always 512 bytes, but it can consist of two nand pages */ ++#define SM_SECTOR_SIZE 512 ++ ++/* oob area is also 16 bytes, but might be from two pages */ ++#define SM_OOB_SIZE 16 ++ ++/* This is maximum zone size, and all devices that have more that one zone ++ have this size */ ++#define SM_MAX_ZONE_SIZE 1024 ++ ++/* support for small page nand */ ++#define SM_SMALL_PAGE 256 ++#define SM_SMALL_OOB_SIZE 8 ++ ++ ++extern int sm_register_device(struct mtd_info *mtd, int smartmedia); ++ ++ ++static inline int sm_sector_valid(struct sm_oob *oob) ++{ ++ return hweight16(oob->data_status) >= 5; ++} ++ ++static inline int sm_block_valid(struct sm_oob *oob) ++{ ++ return hweight16(oob->block_status) >= 7; ++} ++ ++static inline int sm_block_erased(struct sm_oob *oob) ++{ ++ static const uint32_t erased_pattern[4] = { ++ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; ++ ++ /* First test for erased block */ ++ if (!memcmp(oob, erased_pattern, sizeof(*oob))) ++ return 1; ++ return 0; ++} +diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c +new file mode 100644 +index 00000000..575997d +--- /dev/null ++++ b/drivers/mtd/nand/raw/socrates_nand.c +@@ -0,0 +1,243 @@ ++/* ++ * drivers/mtd/nand/socrates_nand.c ++ * ++ * Copyright © 2008 Ilya Yanok, Emcraft Systems ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FPGA_NAND_CMD_MASK (0x7 << 28) ++#define FPGA_NAND_CMD_COMMAND (0x0 << 28) ++#define FPGA_NAND_CMD_ADDR (0x1 << 28) ++#define FPGA_NAND_CMD_READ (0x2 << 28) ++#define FPGA_NAND_CMD_WRITE (0x3 << 28) ++#define FPGA_NAND_BUSY (0x1 << 15) ++#define FPGA_NAND_ENABLE (0x1 << 31) ++#define FPGA_NAND_DATA_SHIFT 16 ++ ++struct socrates_nand_host { ++ struct nand_chip nand_chip; ++ void __iomem *io_base; ++ struct device *dev; ++}; ++ ++/** ++ * socrates_nand_write_buf - write buffer to chip ++ * @mtd: MTD device structure ++ * @buf: data buffer ++ * @len: number of bytes to write ++ */ ++static void socrates_nand_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct socrates_nand_host *host = nand_get_controller_data(this); ++ ++ for (i = 0; i < len; i++) { ++ out_be32(host->io_base, FPGA_NAND_ENABLE | ++ FPGA_NAND_CMD_WRITE | ++ (buf[i] << FPGA_NAND_DATA_SHIFT)); ++ } ++} ++ ++/** ++ * socrates_nand_read_buf - read chip data into buffer ++ * @mtd: MTD device structure ++ * @buf: buffer to store date ++ * @len: number of bytes to read ++ */ ++static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd_to_nand(mtd); ++ struct socrates_nand_host *host = nand_get_controller_data(this); ++ uint32_t val; ++ ++ val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; ++ ++ out_be32(host->io_base, val); ++ for (i = 0; i < len; i++) { ++ buf[i] = (in_be32(host->io_base) >> ++ FPGA_NAND_DATA_SHIFT) & 0xff; ++ } ++} ++ ++/** ++ * socrates_nand_read_byte - read one byte from the chip ++ * @mtd: MTD device structure ++ */ ++static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) ++{ ++ uint8_t byte; ++ socrates_nand_read_buf(mtd, &byte, sizeof(byte)); ++ return byte; ++} ++ ++/** ++ * socrates_nand_read_word - read one word from the chip ++ * @mtd: MTD device structure ++ */ ++static uint16_t socrates_nand_read_word(struct mtd_info *mtd) ++{ ++ uint16_t word; ++ socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word)); ++ return word; ++} ++ ++/* ++ * Hardware specific access to control-lines ++ */ ++static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct socrates_nand_host *host = nand_get_controller_data(nand_chip); ++ uint32_t val; ++ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ val = FPGA_NAND_CMD_COMMAND; ++ else ++ val = FPGA_NAND_CMD_ADDR; ++ ++ if (ctrl & NAND_NCE) ++ val |= FPGA_NAND_ENABLE; ++ ++ val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; ++ ++ out_be32(host->io_base, val); ++} ++ ++/* ++ * Read the Device Ready pin. ++ */ ++static int socrates_nand_device_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand_chip = mtd_to_nand(mtd); ++ struct socrates_nand_host *host = nand_get_controller_data(nand_chip); ++ ++ if (in_be32(host->io_base) & FPGA_NAND_BUSY) ++ return 0; /* busy */ ++ return 1; ++} ++ ++/* ++ * Probe for the NAND device. ++ */ ++static int socrates_nand_probe(struct platform_device *ofdev) ++{ ++ struct socrates_nand_host *host; ++ struct mtd_info *mtd; ++ struct nand_chip *nand_chip; ++ int res; ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ host->io_base = of_iomap(ofdev->dev.of_node, 0); ++ if (host->io_base == NULL) { ++ dev_err(&ofdev->dev, "ioremap failed\n"); ++ return -EIO; ++ } ++ ++ nand_chip = &host->nand_chip; ++ mtd = nand_to_mtd(nand_chip); ++ host->dev = &ofdev->dev; ++ ++ /* link the private data structures */ ++ nand_set_controller_data(nand_chip, host); ++ nand_set_flash_node(nand_chip, ofdev->dev.of_node); ++ mtd->name = "socrates_nand"; ++ mtd->dev.parent = &ofdev->dev; ++ ++ /*should never be accessed directly */ ++ nand_chip->IO_ADDR_R = (void *)0xdeadbeef; ++ nand_chip->IO_ADDR_W = (void *)0xdeadbeef; ++ ++ nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; ++ nand_chip->read_byte = socrates_nand_read_byte; ++ nand_chip->read_word = socrates_nand_read_word; ++ nand_chip->write_buf = socrates_nand_write_buf; ++ nand_chip->read_buf = socrates_nand_read_buf; ++ nand_chip->dev_ready = socrates_nand_device_ready; ++ ++ nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ ++ nand_chip->ecc.algo = NAND_ECC_HAMMING; ++ ++ /* TODO: I have no idea what real delay is. */ ++ nand_chip->chip_delay = 20; /* 20us command delay time */ ++ ++ dev_set_drvdata(&ofdev->dev, host); ++ ++ res = nand_scan(mtd, 1); ++ if (res) ++ goto out; ++ ++ res = mtd_device_register(mtd, NULL, 0); ++ if (!res) ++ return res; ++ ++ nand_release(mtd); ++ ++out: ++ iounmap(host->io_base); ++ return res; ++} ++ ++/* ++ * Remove a NAND device. ++ */ ++static int socrates_nand_remove(struct platform_device *ofdev) ++{ ++ struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); ++ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); ++ ++ nand_release(mtd); ++ ++ iounmap(host->io_base); ++ ++ return 0; ++} ++ ++static const struct of_device_id socrates_nand_match[] = ++{ ++ { ++ .compatible = "abb,socrates-nand", ++ }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, socrates_nand_match); ++ ++static struct platform_driver socrates_nand_driver = { ++ .driver = { ++ .name = "socrates_nand", ++ .of_match_table = socrates_nand_match, ++ }, ++ .probe = socrates_nand_probe, ++ .remove = socrates_nand_remove, ++}; ++ ++module_platform_driver(socrates_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ilya Yanok"); ++MODULE_DESCRIPTION("NAND driver for Socrates board"); +diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c +new file mode 100644 +index 00000000..46bcd77 +--- /dev/null ++++ b/drivers/mtd/nand/raw/sunxi_nand.c +@@ -0,0 +1,2321 @@ ++/* ++ * Copyright (C) 2013 Boris BREZILLON ++ * ++ * Derived from: ++ * https://github.com/yuq/sunxi-nfc-mtd ++ * Copyright (C) 2013 Qiang Yu ++ * ++ * https://github.com/hno/Allwinner-Info ++ * Copyright (C) 2013 Henrik Nordström ++ * ++ * Copyright (C) 2013 Dmitriy B. ++ * Copyright (C) 2013 Sergey Lapin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define NFC_REG_CTL 0x0000 ++#define NFC_REG_ST 0x0004 ++#define NFC_REG_INT 0x0008 ++#define NFC_REG_TIMING_CTL 0x000C ++#define NFC_REG_TIMING_CFG 0x0010 ++#define NFC_REG_ADDR_LOW 0x0014 ++#define NFC_REG_ADDR_HIGH 0x0018 ++#define NFC_REG_SECTOR_NUM 0x001C ++#define NFC_REG_CNT 0x0020 ++#define NFC_REG_CMD 0x0024 ++#define NFC_REG_RCMD_SET 0x0028 ++#define NFC_REG_WCMD_SET 0x002C ++#define NFC_REG_IO_DATA 0x0030 ++#define NFC_REG_ECC_CTL 0x0034 ++#define NFC_REG_ECC_ST 0x0038 ++#define NFC_REG_DEBUG 0x003C ++#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) ++#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) ++#define NFC_REG_SPARE_AREA 0x00A0 ++#define NFC_REG_PAT_ID 0x00A4 ++#define NFC_RAM0_BASE 0x0400 ++#define NFC_RAM1_BASE 0x0800 ++ ++/* define bit use in NFC_CTL */ ++#define NFC_EN BIT(0) ++#define NFC_RESET BIT(1) ++#define NFC_BUS_WIDTH_MSK BIT(2) ++#define NFC_BUS_WIDTH_8 (0 << 2) ++#define NFC_BUS_WIDTH_16 (1 << 2) ++#define NFC_RB_SEL_MSK BIT(3) ++#define NFC_RB_SEL(x) ((x) << 3) ++#define NFC_CE_SEL_MSK GENMASK(26, 24) ++#define NFC_CE_SEL(x) ((x) << 24) ++#define NFC_CE_CTL BIT(6) ++#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) ++#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) ++#define NFC_SAM BIT(12) ++#define NFC_RAM_METHOD BIT(14) ++#define NFC_DEBUG_CTL BIT(31) ++ ++/* define bit use in NFC_ST */ ++#define NFC_RB_B2R BIT(0) ++#define NFC_CMD_INT_FLAG BIT(1) ++#define NFC_DMA_INT_FLAG BIT(2) ++#define NFC_CMD_FIFO_STATUS BIT(3) ++#define NFC_STA BIT(4) ++#define NFC_NATCH_INT_FLAG BIT(5) ++#define NFC_RB_STATE(x) BIT(x + 8) ++ ++/* define bit use in NFC_INT */ ++#define NFC_B2R_INT_ENABLE BIT(0) ++#define NFC_CMD_INT_ENABLE BIT(1) ++#define NFC_DMA_INT_ENABLE BIT(2) ++#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ ++ NFC_CMD_INT_ENABLE | \ ++ NFC_DMA_INT_ENABLE) ++ ++/* define bit use in NFC_TIMING_CTL */ ++#define NFC_TIMING_CTL_EDO BIT(8) ++ ++/* define NFC_TIMING_CFG register layout */ ++#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ ++ (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ ++ (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ ++ (((tCAD) & 0x7) << 8)) ++ ++/* define bit use in NFC_CMD */ ++#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) ++#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) ++#define NFC_CMD(x) (x) ++#define NFC_ADR_NUM_MSK GENMASK(18, 16) ++#define NFC_ADR_NUM(x) (((x) - 1) << 16) ++#define NFC_SEND_ADR BIT(19) ++#define NFC_ACCESS_DIR BIT(20) ++#define NFC_DATA_TRANS BIT(21) ++#define NFC_SEND_CMD1 BIT(22) ++#define NFC_WAIT_FLAG BIT(23) ++#define NFC_SEND_CMD2 BIT(24) ++#define NFC_SEQ BIT(25) ++#define NFC_DATA_SWAP_METHOD BIT(26) ++#define NFC_ROW_AUTO_INC BIT(27) ++#define NFC_SEND_CMD3 BIT(28) ++#define NFC_SEND_CMD4 BIT(29) ++#define NFC_CMD_TYPE_MSK GENMASK(31, 30) ++#define NFC_NORMAL_OP (0 << 30) ++#define NFC_ECC_OP (1 << 30) ++#define NFC_PAGE_OP (2 << 30) ++ ++/* define bit use in NFC_RCMD_SET */ ++#define NFC_READ_CMD_MSK GENMASK(7, 0) ++#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) ++#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) ++ ++/* define bit use in NFC_WCMD_SET */ ++#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) ++#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) ++#define NFC_READ_CMD0_MSK GENMASK(23, 16) ++#define NFC_READ_CMD1_MSK GENMASK(31, 24) ++ ++/* define bit use in NFC_ECC_CTL */ ++#define NFC_ECC_EN BIT(0) ++#define NFC_ECC_PIPELINE BIT(3) ++#define NFC_ECC_EXCEPTION BIT(4) ++#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) ++#define NFC_ECC_BLOCK_512 BIT(5) ++#define NFC_RANDOM_EN BIT(9) ++#define NFC_RANDOM_DIRECTION BIT(10) ++#define NFC_ECC_MODE_MSK GENMASK(15, 12) ++#define NFC_ECC_MODE(x) ((x) << 12) ++#define NFC_RANDOM_SEED_MSK GENMASK(30, 16) ++#define NFC_RANDOM_SEED(x) ((x) << 16) ++ ++/* define bit use in NFC_ECC_ST */ ++#define NFC_ECC_ERR(x) BIT(x) ++#define NFC_ECC_ERR_MSK GENMASK(15, 0) ++#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) ++#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) ++ ++#define NFC_DEFAULT_TIMEOUT_MS 1000 ++ ++#define NFC_SRAM_SIZE 1024 ++ ++#define NFC_MAX_CS 7 ++ ++/* ++ * Ready/Busy detection type: describes the Ready/Busy detection modes ++ * ++ * @RB_NONE: no external detection available, rely on STATUS command ++ * and software timeouts ++ * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy ++ * pin of the NAND flash chip must be connected to one of the ++ * native NAND R/B pins (those which can be muxed to the NAND ++ * Controller) ++ * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy ++ * pin of the NAND flash chip must be connected to a GPIO capable ++ * pin. ++ */ ++enum sunxi_nand_rb_type { ++ RB_NONE, ++ RB_NATIVE, ++ RB_GPIO, ++}; ++ ++/* ++ * Ready/Busy structure: stores information related to Ready/Busy detection ++ * ++ * @type: the Ready/Busy detection mode ++ * @info: information related to the R/B detection mode. Either a gpio ++ * id or a native R/B id (those supported by the NAND controller). ++ */ ++struct sunxi_nand_rb { ++ enum sunxi_nand_rb_type type; ++ union { ++ int gpio; ++ int nativeid; ++ } info; ++}; ++ ++/* ++ * Chip Select structure: stores information related to NAND Chip Select ++ * ++ * @cs: the NAND CS id used to communicate with a NAND Chip ++ * @rb: the Ready/Busy description ++ */ ++struct sunxi_nand_chip_sel { ++ u8 cs; ++ struct sunxi_nand_rb rb; ++}; ++ ++/* ++ * sunxi HW ECC infos: stores information related to HW ECC support ++ * ++ * @mode: the sunxi ECC mode field deduced from ECC requirements ++ */ ++struct sunxi_nand_hw_ecc { ++ int mode; ++}; ++ ++/* ++ * NAND chip structure: stores NAND chip device related information ++ * ++ * @node: used to store NAND chips into a list ++ * @nand: base NAND chip structure ++ * @mtd: base MTD structure ++ * @clk_rate: clk_rate required for this NAND chip ++ * @timing_cfg TIMING_CFG register value for this NAND chip ++ * @selected: current active CS ++ * @nsels: number of CS lines required by the NAND chip ++ * @sels: array of CS lines descriptions ++ */ ++struct sunxi_nand_chip { ++ struct list_head node; ++ struct nand_chip nand; ++ unsigned long clk_rate; ++ u32 timing_cfg; ++ u32 timing_ctl; ++ int selected; ++ int addr_cycles; ++ u32 addr[2]; ++ int cmd_cycles; ++ u8 cmd[2]; ++ int nsels; ++ struct sunxi_nand_chip_sel sels[0]; ++}; ++ ++static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) ++{ ++ return container_of(nand, struct sunxi_nand_chip, nand); ++} ++ ++/* ++ * NAND Controller structure: stores sunxi NAND controller information ++ * ++ * @controller: base controller structure ++ * @dev: parent device (used to print error messages) ++ * @regs: NAND controller registers ++ * @ahb_clk: NAND Controller AHB clock ++ * @mod_clk: NAND Controller mod clock ++ * @assigned_cs: bitmask describing already assigned CS lines ++ * @clk_rate: NAND controller current clock rate ++ * @chips: a list containing all the NAND chips attached to ++ * this NAND controller ++ * @complete: a completion object used to wait for NAND ++ * controller events ++ */ ++struct sunxi_nfc { ++ struct nand_controller controller; ++ struct device *dev; ++ void __iomem *regs; ++ struct clk *ahb_clk; ++ struct clk *mod_clk; ++ struct reset_control *reset; ++ unsigned long assigned_cs; ++ unsigned long clk_rate; ++ struct list_head chips; ++ struct completion complete; ++ struct dma_chan *dmac; ++}; ++ ++static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl) ++{ ++ return container_of(ctrl, struct sunxi_nfc, controller); ++} ++ ++static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) ++{ ++ struct sunxi_nfc *nfc = dev_id; ++ u32 st = readl(nfc->regs + NFC_REG_ST); ++ u32 ien = readl(nfc->regs + NFC_REG_INT); ++ ++ if (!(ien & st)) ++ return IRQ_NONE; ++ ++ if ((ien & st) == ien) ++ complete(&nfc->complete); ++ ++ writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); ++ writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, ++ bool use_polling, unsigned int timeout_ms) ++{ ++ int ret; ++ ++ if (events & ~NFC_INT_MASK) ++ return -EINVAL; ++ ++ if (!timeout_ms) ++ timeout_ms = NFC_DEFAULT_TIMEOUT_MS; ++ ++ if (!use_polling) { ++ init_completion(&nfc->complete); ++ ++ writel(events, nfc->regs + NFC_REG_INT); ++ ++ ret = wait_for_completion_timeout(&nfc->complete, ++ msecs_to_jiffies(timeout_ms)); ++ if (!ret) ++ ret = -ETIMEDOUT; ++ else ++ ret = 0; ++ ++ writel(0, nfc->regs + NFC_REG_INT); ++ } else { ++ u32 status; ++ ++ ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, ++ (status & events) == events, 1, ++ timeout_ms * 1000); ++ } ++ ++ writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); ++ ++ if (ret) ++ dev_err(nfc->dev, "wait interrupt timedout\n"); ++ ++ return ret; ++} ++ ++static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) ++{ ++ u32 status; ++ int ret; ++ ++ ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, ++ !(status & NFC_CMD_FIFO_STATUS), 1, ++ NFC_DEFAULT_TIMEOUT_MS * 1000); ++ if (ret) ++ dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); ++ ++ return ret; ++} ++ ++static int sunxi_nfc_rst(struct sunxi_nfc *nfc) ++{ ++ u32 ctl; ++ int ret; ++ ++ writel(0, nfc->regs + NFC_REG_ECC_CTL); ++ writel(NFC_RESET, nfc->regs + NFC_REG_CTL); ++ ++ ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, ++ !(ctl & NFC_RESET), 1, ++ NFC_DEFAULT_TIMEOUT_MS * 1000); ++ if (ret) ++ dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); ++ ++ return ret; ++} ++ ++static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf, ++ int chunksize, int nchunks, ++ enum dma_data_direction ddir, ++ struct scatterlist *sg) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct dma_async_tx_descriptor *dmad; ++ enum dma_transfer_direction tdir; ++ dma_cookie_t dmat; ++ int ret; ++ ++ if (ddir == DMA_FROM_DEVICE) ++ tdir = DMA_DEV_TO_MEM; ++ else ++ tdir = DMA_MEM_TO_DEV; ++ ++ sg_init_one(sg, buf, nchunks * chunksize); ++ ret = dma_map_sg(nfc->dev, sg, 1, ddir); ++ if (!ret) ++ return -ENOMEM; ++ ++ dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); ++ if (!dmad) { ++ ret = -EINVAL; ++ goto err_unmap_buf; ++ } ++ ++ writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, ++ nfc->regs + NFC_REG_CTL); ++ writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); ++ writel(chunksize, nfc->regs + NFC_REG_CNT); ++ dmat = dmaengine_submit(dmad); ++ ++ ret = dma_submit_error(dmat); ++ if (ret) ++ goto err_clr_dma_flag; ++ ++ return 0; ++ ++err_clr_dma_flag: ++ writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, ++ nfc->regs + NFC_REG_CTL); ++ ++err_unmap_buf: ++ dma_unmap_sg(nfc->dev, sg, 1, ddir); ++ return ret; ++} ++ ++static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd, ++ enum dma_data_direction ddir, ++ struct scatterlist *sg) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ ++ dma_unmap_sg(nfc->dev, sg, 1, ddir); ++ writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, ++ nfc->regs + NFC_REG_CTL); ++} ++ ++static int sunxi_nfc_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ struct sunxi_nand_rb *rb; ++ int ret; ++ ++ if (sunxi_nand->selected < 0) ++ return 0; ++ ++ rb = &sunxi_nand->sels[sunxi_nand->selected].rb; ++ ++ switch (rb->type) { ++ case RB_NATIVE: ++ ret = !!(readl(nfc->regs + NFC_REG_ST) & ++ NFC_RB_STATE(rb->info.nativeid)); ++ break; ++ case RB_GPIO: ++ ret = gpio_get_value(rb->info.gpio); ++ break; ++ case RB_NONE: ++ default: ++ ret = 0; ++ dev_err(nfc->dev, "cannot check R/B NAND status!\n"); ++ break; ++ } ++ ++ return ret; ++} ++ ++static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ struct sunxi_nand_chip_sel *sel; ++ u32 ctl; ++ ++ if (chip > 0 && chip >= sunxi_nand->nsels) ++ return; ++ ++ if (chip == sunxi_nand->selected) ++ return; ++ ++ ctl = readl(nfc->regs + NFC_REG_CTL) & ++ ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); ++ ++ if (chip >= 0) { ++ sel = &sunxi_nand->sels[chip]; ++ ++ ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | ++ NFC_PAGE_SHIFT(nand->page_shift); ++ if (sel->rb.type == RB_NONE) { ++ nand->dev_ready = NULL; ++ } else { ++ nand->dev_ready = sunxi_nfc_dev_ready; ++ if (sel->rb.type == RB_NATIVE) ++ ctl |= NFC_RB_SEL(sel->rb.info.nativeid); ++ } ++ ++ writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); ++ ++ if (nfc->clk_rate != sunxi_nand->clk_rate) { ++ clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); ++ nfc->clk_rate = sunxi_nand->clk_rate; ++ } ++ } ++ ++ writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); ++ writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); ++ writel(ctl, nfc->regs + NFC_REG_CTL); ++ ++ sunxi_nand->selected = chip; ++} ++ ++static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ int ret; ++ int cnt; ++ int offs = 0; ++ u32 tmp; ++ ++ while (len > offs) { ++ bool poll = false; ++ ++ cnt = min(len - offs, NFC_SRAM_SIZE); ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ break; ++ ++ writel(cnt, nfc->regs + NFC_REG_CNT); ++ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; ++ writel(tmp, nfc->regs + NFC_REG_CMD); ++ ++ /* Arbitrary limit for polling mode */ ++ if (cnt < 64) ++ poll = true; ++ ++ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); ++ if (ret) ++ break; ++ ++ if (buf) ++ memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, ++ cnt); ++ offs += cnt; ++ } ++} ++ ++static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ int ret; ++ int cnt; ++ int offs = 0; ++ u32 tmp; ++ ++ while (len > offs) { ++ bool poll = false; ++ ++ cnt = min(len - offs, NFC_SRAM_SIZE); ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ break; ++ ++ writel(cnt, nfc->regs + NFC_REG_CNT); ++ memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); ++ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | ++ NFC_ACCESS_DIR; ++ writel(tmp, nfc->regs + NFC_REG_CMD); ++ ++ /* Arbitrary limit for polling mode */ ++ if (cnt < 64) ++ poll = true; ++ ++ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); ++ if (ret) ++ break; ++ ++ offs += cnt; ++ } ++} ++ ++static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) ++{ ++ uint8_t ret; ++ ++ sunxi_nfc_read_buf(mtd, &ret, 1); ++ ++ return ret; ++} ++ ++static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, ++ unsigned int ctrl) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ int ret; ++ ++ if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && ++ !(ctrl & (NAND_CLE | NAND_ALE))) { ++ u32 cmd = 0; ++ ++ if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles) ++ return; ++ ++ if (sunxi_nand->cmd_cycles--) ++ cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0]; ++ ++ if (sunxi_nand->cmd_cycles--) { ++ cmd |= NFC_SEND_CMD2; ++ writel(sunxi_nand->cmd[1], ++ nfc->regs + NFC_REG_RCMD_SET); ++ } ++ ++ sunxi_nand->cmd_cycles = 0; ++ ++ if (sunxi_nand->addr_cycles) { ++ cmd |= NFC_SEND_ADR | ++ NFC_ADR_NUM(sunxi_nand->addr_cycles); ++ writel(sunxi_nand->addr[0], ++ nfc->regs + NFC_REG_ADDR_LOW); ++ } ++ ++ if (sunxi_nand->addr_cycles > 4) ++ writel(sunxi_nand->addr[1], ++ nfc->regs + NFC_REG_ADDR_HIGH); ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ return; ++ ++ writel(cmd, nfc->regs + NFC_REG_CMD); ++ sunxi_nand->addr[0] = 0; ++ sunxi_nand->addr[1] = 0; ++ sunxi_nand->addr_cycles = 0; ++ sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); ++ } ++ ++ if (ctrl & NAND_CLE) { ++ sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat; ++ } else if (ctrl & NAND_ALE) { ++ sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |= ++ dat << ((sunxi_nand->addr_cycles % 4) * 8); ++ sunxi_nand->addr_cycles++; ++ } ++} ++ ++/* These seed values have been extracted from Allwinner's BSP */ ++static const u16 sunxi_nfc_randomizer_page_seeds[] = { ++ 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, ++ 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, ++ 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, ++ 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, ++ 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, ++ 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, ++ 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, ++ 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, ++ 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, ++ 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, ++ 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, ++ 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, ++ 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, ++ 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, ++ 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, ++ 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, ++}; ++ ++/* ++ * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds ++ * have been generated using ++ * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what ++ * the randomizer engine does internally before de/scrambling OOB data. ++ * ++ * Those tables are statically defined to avoid calculating randomizer state ++ * at runtime. ++ */ ++static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { ++ 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, ++ 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, ++ 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, ++ 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, ++ 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, ++ 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, ++ 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, ++ 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, ++ 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, ++ 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, ++ 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, ++ 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, ++ 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, ++ 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, ++ 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, ++ 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, ++}; ++ ++static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { ++ 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, ++ 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, ++ 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, ++ 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, ++ 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, ++ 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, ++ 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, ++ 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, ++ 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, ++ 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, ++ 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, ++ 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, ++ 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, ++ 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, ++ 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, ++ 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, ++}; ++ ++static u16 sunxi_nfc_randomizer_step(u16 state, int count) ++{ ++ state &= 0x7fff; ++ ++ /* ++ * This loop is just a simple implementation of a Fibonacci LFSR using ++ * the x16 + x15 + 1 polynomial. ++ */ ++ while (count--) ++ state = ((state >> 1) | ++ (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; ++ ++ return state; ++} ++ ++static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) ++{ ++ const u16 *seeds = sunxi_nfc_randomizer_page_seeds; ++ int mod = mtd_div_by_ws(mtd->erasesize, mtd); ++ ++ if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) ++ mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); ++ ++ if (ecc) { ++ if (mtd->ecc_step_size == 512) ++ seeds = sunxi_nfc_randomizer_ecc512_seeds; ++ else ++ seeds = sunxi_nfc_randomizer_ecc1024_seeds; ++ } ++ ++ return seeds[page % mod]; ++} ++ ++static void sunxi_nfc_randomizer_config(struct mtd_info *mtd, ++ int page, bool ecc) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); ++ u16 state; ++ ++ if (!(nand->options & NAND_NEED_SCRAMBLING)) ++ return; ++ ++ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); ++ state = sunxi_nfc_randomizer_state(mtd, page, ecc); ++ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; ++ writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); ++} ++ ++static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ ++ if (!(nand->options & NAND_NEED_SCRAMBLING)) ++ return; ++ ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++} ++ ++static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ ++ if (!(nand->options & NAND_NEED_SCRAMBLING)) ++ return; ++ ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++} ++ ++static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm) ++{ ++ u16 state = sunxi_nfc_randomizer_state(mtd, page, true); ++ ++ bbm[0] ^= state; ++ bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); ++} ++ ++static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len, ++ bool ecc, int page) ++{ ++ sunxi_nfc_randomizer_config(mtd, page, ecc); ++ sunxi_nfc_randomizer_enable(mtd); ++ sunxi_nfc_write_buf(mtd, buf, len); ++ sunxi_nfc_randomizer_disable(mtd); ++} ++ ++static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf, ++ int len, bool ecc, int page) ++{ ++ sunxi_nfc_randomizer_config(mtd, page, ecc); ++ sunxi_nfc_randomizer_enable(mtd); ++ sunxi_nfc_read_buf(mtd, buf, len); ++ sunxi_nfc_randomizer_disable(mtd); ++} ++ ++static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct sunxi_nand_hw_ecc *data = nand->ecc.priv; ++ u32 ecc_ctl; ++ ++ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); ++ ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | ++ NFC_ECC_BLOCK_SIZE_MSK); ++ ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | ++ NFC_ECC_PIPELINE; ++ ++ if (nand->ecc.size == 512) ++ ecc_ctl |= NFC_ECC_BLOCK_512; ++ ++ writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); ++} ++ ++static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++} ++ ++static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) ++{ ++ buf[0] = user_data; ++ buf[1] = user_data >> 8; ++ buf[2] = user_data >> 16; ++ buf[3] = user_data >> 24; ++} ++ ++static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) ++{ ++ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); ++} ++ ++static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob, ++ int step, bool bbm, int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ ++ sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), ++ oob); ++ ++ /* De-randomize the Bad Block Marker. */ ++ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) ++ sunxi_nfc_randomize_bbm(mtd, page, oob); ++} ++ ++static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd, ++ const u8 *oob, int step, ++ bool bbm, int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ u8 user_data[4]; ++ ++ /* Randomize the Bad Block Marker. */ ++ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { ++ memcpy(user_data, oob, sizeof(user_data)); ++ sunxi_nfc_randomize_bbm(mtd, page, user_data); ++ oob = user_data; ++ } ++ ++ writel(sunxi_nfc_buf_to_user_data(oob), ++ nfc->regs + NFC_REG_USER_DATA(step)); ++} ++ ++static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd, ++ unsigned int *max_bitflips, int ret) ++{ ++ if (ret < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += ret; ++ *max_bitflips = max_t(unsigned int, *max_bitflips, ret); ++ } ++} ++ ++static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, ++ int step, u32 status, bool *erased) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ u32 tmp; ++ ++ *erased = false; ++ ++ if (status & NFC_ECC_ERR(step)) ++ return -EBADMSG; ++ ++ if (status & NFC_ECC_PAT_FOUND(step)) { ++ u8 pattern; ++ ++ if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { ++ pattern = 0x0; ++ } else { ++ pattern = 0xff; ++ *erased = true; ++ } ++ ++ if (data) ++ memset(data, pattern, ecc->size); ++ ++ if (oob) ++ memset(oob, pattern, ecc->bytes + 4); ++ ++ return 0; ++ } ++ ++ tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); ++ ++ return NFC_ECC_ERR_CNT(step, tmp); ++} ++ ++static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, ++ u8 *data, int data_off, ++ u8 *oob, int oob_off, ++ int *cur_off, ++ unsigned int *max_bitflips, ++ bool bbm, bool oob_required, int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ int raw_mode = 0; ++ bool erased; ++ int ret; ++ ++ if (*cur_off != data_off) ++ nand_change_read_column_op(nand, data_off, NULL, 0, false); ++ ++ sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); ++ ++ if (data_off + ecc->size != oob_off) ++ nand_change_read_column_op(nand, oob_off, NULL, 0, false); ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ return ret; ++ ++ sunxi_nfc_randomizer_enable(mtd); ++ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, ++ nfc->regs + NFC_REG_CMD); ++ ++ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); ++ sunxi_nfc_randomizer_disable(mtd); ++ if (ret) ++ return ret; ++ ++ *cur_off = oob_off + ecc->bytes + 4; ++ ++ ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0, ++ readl(nfc->regs + NFC_REG_ECC_ST), ++ &erased); ++ if (erased) ++ return 1; ++ ++ if (ret < 0) { ++ /* ++ * Re-read the data with the randomizer disabled to identify ++ * bitflips in erased pages. ++ */ ++ if (nand->options & NAND_NEED_SCRAMBLING) ++ nand_change_read_column_op(nand, data_off, data, ++ ecc->size, false); ++ else ++ memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ++ ecc->size); ++ ++ nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, ++ false); ++ ++ ret = nand_check_erased_ecc_chunk(data, ecc->size, ++ oob, ecc->bytes + 4, ++ NULL, 0, ecc->strength); ++ if (ret >= 0) ++ raw_mode = 1; ++ } else { ++ memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); ++ ++ if (oob_required) { ++ nand_change_read_column_op(nand, oob_off, NULL, 0, ++ false); ++ sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, ++ true, page); ++ ++ sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, ++ bbm, page); ++ } ++ } ++ ++ sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret); ++ ++ return raw_mode; ++} ++ ++static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, ++ u8 *oob, int *cur_off, ++ bool randomize, int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ int offset = ((ecc->bytes + 4) * ecc->steps); ++ int len = mtd->oobsize - offset; ++ ++ if (len <= 0) ++ return; ++ ++ if (!cur_off || *cur_off != offset) ++ nand_change_read_column_op(nand, mtd->writesize, NULL, 0, ++ false); ++ ++ if (!randomize) ++ sunxi_nfc_read_buf(mtd, oob + offset, len); ++ else ++ sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, ++ false, page); ++ ++ if (cur_off) ++ *cur_off = mtd->oobsize + mtd->writesize; ++} ++ ++static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, ++ int oob_required, int page, ++ int nchunks) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ bool randomized = nand->options & NAND_NEED_SCRAMBLING; ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ unsigned int max_bitflips = 0; ++ int ret, i, raw_mode = 0; ++ struct scatterlist sg; ++ u32 status; ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ return ret; ++ ++ ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks, ++ DMA_FROM_DEVICE, &sg); ++ if (ret) ++ return ret; ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ sunxi_nfc_randomizer_config(mtd, page, false); ++ sunxi_nfc_randomizer_enable(mtd); ++ ++ writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | ++ NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); ++ ++ dma_async_issue_pending(nfc->dmac); ++ ++ writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, ++ nfc->regs + NFC_REG_CMD); ++ ++ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); ++ if (ret) ++ dmaengine_terminate_all(nfc->dmac); ++ ++ sunxi_nfc_randomizer_disable(mtd); ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg); ++ ++ if (ret) ++ return ret; ++ ++ status = readl(nfc->regs + NFC_REG_ECC_ST); ++ ++ for (i = 0; i < nchunks; i++) { ++ int data_off = i * ecc->size; ++ int oob_off = i * (ecc->bytes + 4); ++ u8 *data = buf + data_off; ++ u8 *oob = nand->oob_poi + oob_off; ++ bool erased; ++ ++ ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL, ++ oob_required ? oob : NULL, ++ i, status, &erased); ++ ++ /* ECC errors are handled in the second loop. */ ++ if (ret < 0) ++ continue; ++ ++ if (oob_required && !erased) { ++ /* TODO: use DMA to retrieve OOB */ ++ nand_change_read_column_op(nand, ++ mtd->writesize + oob_off, ++ oob, ecc->bytes + 4, false); ++ ++ sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i, ++ !i, page); ++ } ++ ++ if (erased) ++ raw_mode = 1; ++ ++ sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); ++ } ++ ++ if (status & NFC_ECC_ERR_MSK) { ++ for (i = 0; i < nchunks; i++) { ++ int data_off = i * ecc->size; ++ int oob_off = i * (ecc->bytes + 4); ++ u8 *data = buf + data_off; ++ u8 *oob = nand->oob_poi + oob_off; ++ ++ if (!(status & NFC_ECC_ERR(i))) ++ continue; ++ ++ /* ++ * Re-read the data with the randomizer disabled to ++ * identify bitflips in erased pages. ++ * TODO: use DMA to read page in raw mode ++ */ ++ if (randomized) ++ nand_change_read_column_op(nand, data_off, ++ data, ecc->size, ++ false); ++ ++ /* TODO: use DMA to retrieve OOB */ ++ nand_change_read_column_op(nand, ++ mtd->writesize + oob_off, ++ oob, ecc->bytes + 4, false); ++ ++ ret = nand_check_erased_ecc_chunk(data, ecc->size, ++ oob, ecc->bytes + 4, ++ NULL, 0, ++ ecc->strength); ++ if (ret >= 0) ++ raw_mode = 1; ++ ++ sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); ++ } ++ } ++ ++ if (oob_required) ++ sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi, ++ NULL, !raw_mode, ++ page); ++ ++ return max_bitflips; ++} ++ ++static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, ++ const u8 *data, int data_off, ++ const u8 *oob, int oob_off, ++ int *cur_off, bool bbm, ++ int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ int ret; ++ ++ if (data_off != *cur_off) ++ nand_change_write_column_op(nand, data_off, NULL, 0, false); ++ ++ sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); ++ ++ if (data_off + ecc->size != oob_off) ++ nand_change_write_column_op(nand, oob_off, NULL, 0, false); ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ return ret; ++ ++ sunxi_nfc_randomizer_enable(mtd); ++ sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page); ++ ++ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | ++ NFC_ACCESS_DIR | NFC_ECC_OP, ++ nfc->regs + NFC_REG_CMD); ++ ++ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); ++ sunxi_nfc_randomizer_disable(mtd); ++ if (ret) ++ return ret; ++ ++ *cur_off = oob_off + ecc->bytes + 4; ++ ++ return 0; ++} ++ ++static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, ++ u8 *oob, int *cur_off, ++ int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ int offset = ((ecc->bytes + 4) * ecc->steps); ++ int len = mtd->oobsize - offset; ++ ++ if (len <= 0) ++ return; ++ ++ if (!cur_off || *cur_off != offset) ++ nand_change_write_column_op(nand, offset + mtd->writesize, ++ NULL, 0, false); ++ ++ sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); ++ ++ if (cur_off) ++ *cur_off = mtd->oobsize + mtd->writesize; ++} ++ ++static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ unsigned int max_bitflips = 0; ++ int ret, i, cur_off = 0; ++ bool raw_mode = false; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_off = i * ecc->size; ++ int oob_off = i * (ecc->bytes + 4); ++ u8 *data = buf + data_off; ++ u8 *oob = chip->oob_poi + oob_off; ++ ++ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, ++ oob_off + mtd->writesize, ++ &cur_off, &max_bitflips, ++ !i, oob_required, page); ++ if (ret < 0) ++ return ret; ++ else if (ret) ++ raw_mode = true; ++ } ++ ++ if (oob_required) ++ sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, ++ !raw_mode, page); ++ ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ return max_bitflips; ++} ++ ++static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ int ret; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page, ++ chip->ecc.steps); ++ if (ret >= 0) ++ return ret; ++ ++ /* Fallback to PIO mode */ ++ return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page); ++} ++ ++static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u32 data_offs, u32 readlen, ++ u8 *bufpoi, int page) ++{ ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ret, i, cur_off = 0; ++ unsigned int max_bitflips = 0; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ ++ for (i = data_offs / ecc->size; ++ i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { ++ int data_off = i * ecc->size; ++ int oob_off = i * (ecc->bytes + 4); ++ u8 *data = bufpoi + data_off; ++ u8 *oob = chip->oob_poi + oob_off; ++ ++ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, ++ oob, ++ oob_off + mtd->writesize, ++ &cur_off, &max_bitflips, !i, ++ false, page); ++ if (ret < 0) ++ return ret; ++ } ++ ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ return max_bitflips; ++} ++ ++static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u32 data_offs, u32 readlen, ++ u8 *buf, int page) ++{ ++ int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); ++ int ret; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks); ++ if (ret >= 0) ++ return ret; ++ ++ /* Fallback to PIO mode */ ++ return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen, ++ buf, page); ++} ++ ++static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ret, i, cur_off = 0; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_off = i * ecc->size; ++ int oob_off = i * (ecc->bytes + 4); ++ const u8 *data = buf + data_off; ++ const u8 *oob = chip->oob_poi + oob_off; ++ ++ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, ++ oob_off + mtd->writesize, ++ &cur_off, !i, page); ++ if (ret) ++ return ret; ++ } ++ ++ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) ++ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, ++ &cur_off, page); ++ ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u32 data_offs, u32 data_len, ++ const u8 *buf, int oob_required, ++ int page) ++{ ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ret, i, cur_off = 0; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ ++ for (i = data_offs / ecc->size; ++ i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { ++ int data_off = i * ecc->size; ++ int oob_off = i * (ecc->bytes + 4); ++ const u8 *data = buf + data_off; ++ const u8 *oob = chip->oob_poi + oob_off; ++ ++ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, ++ oob_off + mtd->writesize, ++ &cur_off, !i, page); ++ if (ret) ++ return ret; ++ } ++ ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, ++ int page) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ struct scatterlist sg; ++ int ret, i; ++ ++ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ++ if (ret) ++ return ret; ++ ++ ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps, ++ DMA_TO_DEVICE, &sg); ++ if (ret) ++ goto pio_fallback; ++ ++ for (i = 0; i < ecc->steps; i++) { ++ const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); ++ ++ sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page); ++ } ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ sunxi_nfc_randomizer_config(mtd, page, false); ++ sunxi_nfc_randomizer_enable(mtd); ++ ++ writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, ++ nfc->regs + NFC_REG_RCMD_SET); ++ ++ dma_async_issue_pending(nfc->dmac); ++ ++ writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | ++ NFC_DATA_TRANS | NFC_ACCESS_DIR, ++ nfc->regs + NFC_REG_CMD); ++ ++ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); ++ if (ret) ++ dmaengine_terminate_all(nfc->dmac); ++ ++ sunxi_nfc_randomizer_disable(mtd); ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg); ++ ++ if (ret) ++ return ret; ++ ++ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) ++ /* TODO: use DMA to transfer extra OOB bytes ? */ ++ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, ++ NULL, page); ++ ++ return nand_prog_page_end_op(chip); ++ ++pio_fallback: ++ return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page); ++} ++ ++static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ unsigned int max_bitflips = 0; ++ int ret, i, cur_off = 0; ++ bool raw_mode = false; ++ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_off = i * (ecc->size + ecc->bytes + 4); ++ int oob_off = data_off + ecc->size; ++ u8 *data = buf + (i * ecc->size); ++ u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); ++ ++ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, ++ oob_off, &cur_off, ++ &max_bitflips, !i, ++ oob_required, ++ page); ++ if (ret < 0) ++ return ret; ++ else if (ret) ++ raw_mode = true; ++ } ++ ++ if (oob_required) ++ sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, ++ !raw_mode, page); ++ ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ return max_bitflips; ++} ++ ++static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ int ret, i, cur_off = 0; ++ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ ++ sunxi_nfc_hw_ecc_enable(mtd); ++ ++ for (i = 0; i < ecc->steps; i++) { ++ int data_off = i * (ecc->size + ecc->bytes + 4); ++ int oob_off = data_off + ecc->size; ++ const u8 *data = buf + (i * ecc->size); ++ const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); ++ ++ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, ++ oob, oob_off, &cur_off, ++ false, page); ++ if (ret) ++ return ret; ++ } ++ ++ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) ++ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, ++ &cur_off, page); ++ ++ sunxi_nfc_hw_ecc_disable(mtd); ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ int page) ++{ ++ chip->pagebuf = -1; ++ ++ return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page); ++} ++ ++static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ int page) ++{ ++ int ret; ++ ++ chip->pagebuf = -1; ++ ++ memset(chip->data_buf, 0xff, mtd->writesize); ++ ret = chip->ecc.write_page(mtd, chip, chip->data_buf, 1, page); ++ if (ret) ++ return ret; ++ ++ /* Send command to program the OOB data */ ++ return nand_prog_page_end_op(chip); ++} ++ ++static const s32 tWB_lut[] = {6, 12, 16, 20}; ++static const s32 tRHW_lut[] = {4, 8, 12, 20}; ++ ++static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, ++ u32 clk_period) ++{ ++ u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); ++ int i; ++ ++ for (i = 0; i < lut_size; i++) { ++ if (clk_cycles <= lut[i]) ++ return i; ++ } ++ ++ /* Doesn't fit */ ++ return -EINVAL; ++} ++ ++#define sunxi_nand_lookup_timing(l, p, c) \ ++ _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) ++ ++static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *chip = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); ++ const struct nand_sdr_timings *timings; ++ u32 min_clk_period = 0; ++ s32 tWB, tADL, tWHR, tRHW, tCAD; ++ long real_clk_rate; ++ ++ timings = nand_get_sdr_timings(conf); ++ if (IS_ERR(timings)) ++ return -ENOTSUPP; ++ ++ /* T1 <=> tCLS */ ++ if (timings->tCLS_min > min_clk_period) ++ min_clk_period = timings->tCLS_min; ++ ++ /* T2 <=> tCLH */ ++ if (timings->tCLH_min > min_clk_period) ++ min_clk_period = timings->tCLH_min; ++ ++ /* T3 <=> tCS */ ++ if (timings->tCS_min > min_clk_period) ++ min_clk_period = timings->tCS_min; ++ ++ /* T4 <=> tCH */ ++ if (timings->tCH_min > min_clk_period) ++ min_clk_period = timings->tCH_min; ++ ++ /* T5 <=> tWP */ ++ if (timings->tWP_min > min_clk_period) ++ min_clk_period = timings->tWP_min; ++ ++ /* T6 <=> tWH */ ++ if (timings->tWH_min > min_clk_period) ++ min_clk_period = timings->tWH_min; ++ ++ /* T7 <=> tALS */ ++ if (timings->tALS_min > min_clk_period) ++ min_clk_period = timings->tALS_min; ++ ++ /* T8 <=> tDS */ ++ if (timings->tDS_min > min_clk_period) ++ min_clk_period = timings->tDS_min; ++ ++ /* T9 <=> tDH */ ++ if (timings->tDH_min > min_clk_period) ++ min_clk_period = timings->tDH_min; ++ ++ /* T10 <=> tRR */ ++ if (timings->tRR_min > (min_clk_period * 3)) ++ min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); ++ ++ /* T11 <=> tALH */ ++ if (timings->tALH_min > min_clk_period) ++ min_clk_period = timings->tALH_min; ++ ++ /* T12 <=> tRP */ ++ if (timings->tRP_min > min_clk_period) ++ min_clk_period = timings->tRP_min; ++ ++ /* T13 <=> tREH */ ++ if (timings->tREH_min > min_clk_period) ++ min_clk_period = timings->tREH_min; ++ ++ /* T14 <=> tRC */ ++ if (timings->tRC_min > (min_clk_period * 2)) ++ min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); ++ ++ /* T15 <=> tWC */ ++ if (timings->tWC_min > (min_clk_period * 2)) ++ min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); ++ ++ /* T16 - T19 + tCAD */ ++ if (timings->tWB_max > (min_clk_period * 20)) ++ min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); ++ ++ if (timings->tADL_min > (min_clk_period * 32)) ++ min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); ++ ++ if (timings->tWHR_min > (min_clk_period * 32)) ++ min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); ++ ++ if (timings->tRHW_min > (min_clk_period * 20)) ++ min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); ++ ++ tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, ++ min_clk_period); ++ if (tWB < 0) { ++ dev_err(nfc->dev, "unsupported tWB\n"); ++ return tWB; ++ } ++ ++ tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; ++ if (tADL > 3) { ++ dev_err(nfc->dev, "unsupported tADL\n"); ++ return -EINVAL; ++ } ++ ++ tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; ++ if (tWHR > 3) { ++ dev_err(nfc->dev, "unsupported tWHR\n"); ++ return -EINVAL; ++ } ++ ++ tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, ++ min_clk_period); ++ if (tRHW < 0) { ++ dev_err(nfc->dev, "unsupported tRHW\n"); ++ return tRHW; ++ } ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ /* ++ * TODO: according to ONFI specs this value only applies for DDR NAND, ++ * but Allwinner seems to set this to 0x7. Mimic them for now. ++ */ ++ tCAD = 0x7; ++ ++ /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ ++ chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); ++ ++ /* Convert min_clk_period from picoseconds to nanoseconds */ ++ min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); ++ ++ /* ++ * Unlike what is stated in Allwinner datasheet, the clk_rate should ++ * be set to (1 / min_clk_period), and not (2 / min_clk_period). ++ * This new formula was verified with a scope and validated by ++ * Allwinner engineers. ++ */ ++ chip->clk_rate = NSEC_PER_SEC / min_clk_period; ++ real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate); ++ if (real_clk_rate <= 0) { ++ dev_err(nfc->dev, "Unable to round clk %lu\n", chip->clk_rate); ++ return -EINVAL; ++ } ++ ++ /* ++ * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data ++ * output cycle timings shall be used if the host drives tRC less than ++ * 30 ns. ++ */ ++ min_clk_period = NSEC_PER_SEC / real_clk_rate; ++ chip->timing_ctl = ((min_clk_period * 2) < 30) ? ++ NFC_TIMING_CTL_EDO : 0; ++ ++ return 0; ++} ++ ++static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ ++ if (section >= ecc->steps) ++ return -ERANGE; ++ ++ oobregion->offset = section * (ecc->bytes + 4) + 4; ++ oobregion->length = ecc->bytes; ++ ++ return 0; ++} ++ ++static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &nand->ecc; ++ ++ if (section > ecc->steps) ++ return -ERANGE; ++ ++ /* ++ * The first 2 bytes are used for BB markers, hence we ++ * only have 2 bytes available in the first user data ++ * section. ++ */ ++ if (!section && ecc->mode == NAND_ECC_HW) { ++ oobregion->offset = 2; ++ oobregion->length = 2; ++ ++ return 0; ++ } ++ ++ oobregion->offset = section * (ecc->bytes + 4); ++ ++ if (section < ecc->steps) ++ oobregion->length = 4; ++ else ++ oobregion->offset = mtd->oobsize - oobregion->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { ++ .ecc = sunxi_nand_ooblayout_ecc, ++ .free = sunxi_nand_ooblayout_free, ++}; ++ ++static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, ++ struct nand_ecc_ctrl *ecc, ++ struct device_node *np) ++{ ++ static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ struct sunxi_nand_hw_ecc *data; ++ int nsectors; ++ int ret; ++ int i; ++ ++ if (ecc->options & NAND_ECC_MAXIMIZE) { ++ int bytes; ++ ++ ecc->size = 1024; ++ nsectors = mtd->writesize / ecc->size; ++ ++ /* Reserve 2 bytes for the BBM */ ++ bytes = (mtd->oobsize - 2) / nsectors; ++ ++ /* 4 non-ECC bytes are added before each ECC bytes section */ ++ bytes -= 4; ++ ++ /* and bytes has to be even. */ ++ if (bytes % 2) ++ bytes--; ++ ++ ecc->strength = bytes * 8 / fls(8 * ecc->size); ++ ++ for (i = 0; i < ARRAY_SIZE(strengths); i++) { ++ if (strengths[i] > ecc->strength) ++ break; ++ } ++ ++ if (!i) ++ ecc->strength = 0; ++ else ++ ecc->strength = strengths[i - 1]; ++ } ++ ++ if (ecc->size != 512 && ecc->size != 1024) ++ return -EINVAL; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ /* Prefer 1k ECC chunk over 512 ones */ ++ if (ecc->size == 512 && mtd->writesize > 512) { ++ ecc->size = 1024; ++ ecc->strength *= 2; ++ } ++ ++ /* Add ECC info retrieval from DT */ ++ for (i = 0; i < ARRAY_SIZE(strengths); i++) { ++ if (ecc->strength <= strengths[i]) { ++ /* ++ * Update ecc->strength value with the actual strength ++ * that will be used by the ECC engine. ++ */ ++ ecc->strength = strengths[i]; ++ break; ++ } ++ } ++ ++ if (i >= ARRAY_SIZE(strengths)) { ++ dev_err(nfc->dev, "unsupported strength\n"); ++ ret = -ENOTSUPP; ++ goto err; ++ } ++ ++ data->mode = i; ++ ++ /* HW ECC always request ECC bytes for 1024 bytes blocks */ ++ ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); ++ ++ /* HW ECC always work with even numbers of ECC bytes */ ++ ecc->bytes = ALIGN(ecc->bytes, 2); ++ ++ nsectors = mtd->writesize / ecc->size; ++ ++ if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob; ++ ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob; ++ mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); ++ ecc->priv = data; ++ ++ return 0; ++ ++err: ++ kfree(data); ++ ++ return ret; ++} ++ ++static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) ++{ ++ kfree(ecc->priv); ++} ++ ++static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, ++ struct nand_ecc_ctrl *ecc, ++ struct device_node *np) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); ++ int ret; ++ ++ ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); ++ if (ret) ++ return ret; ++ ++ if (nfc->dmac) { ++ ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; ++ ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; ++ ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; ++ nand->options |= NAND_USE_BOUNCE_BUFFER; ++ } else { ++ ecc->read_page = sunxi_nfc_hw_ecc_read_page; ++ ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; ++ ecc->write_page = sunxi_nfc_hw_ecc_write_page; ++ } ++ ++ /* TODO: support DMA for raw accesses and subpage write */ ++ ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; ++ ecc->read_oob_raw = nand_read_oob_std; ++ ecc->write_oob_raw = nand_write_oob_std; ++ ++ return 0; ++} ++ ++static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, ++ struct nand_ecc_ctrl *ecc, ++ struct device_node *np) ++{ ++ int ret; ++ ++ ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); ++ if (ret) ++ return ret; ++ ++ ecc->prepad = 4; ++ ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; ++ ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; ++ ecc->read_oob_raw = nand_read_oob_syndrome; ++ ecc->write_oob_raw = nand_write_oob_syndrome; ++ ++ return 0; ++} ++ ++static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) ++{ ++ switch (ecc->mode) { ++ case NAND_ECC_HW: ++ case NAND_ECC_HW_SYNDROME: ++ sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); ++ break; ++ case NAND_ECC_NONE: ++ default: ++ break; ++ } ++} ++ ++static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, ++ struct device_node *np) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ int ret; ++ ++ if (!ecc->size) { ++ ecc->size = nand->ecc_step_ds; ++ ecc->strength = nand->ecc_strength_ds; ++ } ++ ++ if (!ecc->size || !ecc->strength) ++ return -EINVAL; ++ ++ switch (ecc->mode) { ++ case NAND_ECC_HW: ++ ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); ++ if (ret) ++ return ret; ++ break; ++ case NAND_ECC_HW_SYNDROME: ++ ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np); ++ if (ret) ++ return ret; ++ break; ++ case NAND_ECC_NONE: ++ case NAND_ECC_SOFT: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, ++ struct device_node *np) ++{ ++ struct sunxi_nand_chip *chip; ++ struct mtd_info *mtd; ++ struct nand_chip *nand; ++ int nsels; ++ int ret; ++ int i; ++ u32 tmp; ++ ++ if (!of_get_property(np, "reg", &nsels)) ++ return -EINVAL; ++ ++ nsels /= sizeof(u32); ++ if (!nsels) { ++ dev_err(dev, "invalid reg property size\n"); ++ return -EINVAL; ++ } ++ ++ chip = devm_kzalloc(dev, ++ sizeof(*chip) + ++ (nsels * sizeof(struct sunxi_nand_chip_sel)), ++ GFP_KERNEL); ++ if (!chip) { ++ dev_err(dev, "could not allocate chip\n"); ++ return -ENOMEM; ++ } ++ ++ chip->nsels = nsels; ++ chip->selected = -1; ++ ++ for (i = 0; i < nsels; i++) { ++ ret = of_property_read_u32_index(np, "reg", i, &tmp); ++ if (ret) { ++ dev_err(dev, "could not retrieve reg property: %d\n", ++ ret); ++ return ret; ++ } ++ ++ if (tmp > NFC_MAX_CS) { ++ dev_err(dev, ++ "invalid reg value: %u (max CS = 7)\n", ++ tmp); ++ return -EINVAL; ++ } ++ ++ if (test_and_set_bit(tmp, &nfc->assigned_cs)) { ++ dev_err(dev, "CS %d already assigned\n", tmp); ++ return -EINVAL; ++ } ++ ++ chip->sels[i].cs = tmp; ++ ++ if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && ++ tmp < 2) { ++ chip->sels[i].rb.type = RB_NATIVE; ++ chip->sels[i].rb.info.nativeid = tmp; ++ } else { ++ ret = of_get_named_gpio(np, "rb-gpios", i); ++ if (ret >= 0) { ++ tmp = ret; ++ chip->sels[i].rb.type = RB_GPIO; ++ chip->sels[i].rb.info.gpio = tmp; ++ ret = devm_gpio_request(dev, tmp, "nand-rb"); ++ if (ret) ++ return ret; ++ ++ ret = gpio_direction_input(tmp); ++ if (ret) ++ return ret; ++ } else { ++ chip->sels[i].rb.type = RB_NONE; ++ } ++ } ++ } ++ ++ nand = &chip->nand; ++ /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ ++ nand->chip_delay = 200; ++ nand->controller = &nfc->controller; ++ /* ++ * Set the ECC mode to the default value in case nothing is specified ++ * in the DT. ++ */ ++ nand->ecc.mode = NAND_ECC_HW; ++ nand_set_flash_node(nand, np); ++ nand->select_chip = sunxi_nfc_select_chip; ++ nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; ++ nand->read_buf = sunxi_nfc_read_buf; ++ nand->write_buf = sunxi_nfc_write_buf; ++ nand->read_byte = sunxi_nfc_read_byte; ++ nand->setup_data_interface = sunxi_nfc_setup_data_interface; ++ ++ mtd = nand_to_mtd(nand); ++ mtd->dev.parent = dev; ++ ++ ret = nand_scan_ident(mtd, nsels, NULL); ++ if (ret) ++ return ret; ++ ++ if (nand->bbt_options & NAND_BBT_USE_FLASH) ++ nand->bbt_options |= NAND_BBT_NO_OOB; ++ ++ if (nand->options & NAND_NEED_SCRAMBLING) ++ nand->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ nand->options |= NAND_SUBPAGE_READ; ++ ++ ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); ++ if (ret) { ++ dev_err(dev, "ECC init failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = nand_scan_tail(mtd); ++ if (ret) { ++ dev_err(dev, "nand_scan_tail failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(dev, "failed to register mtd device: %d\n", ret); ++ nand_release(mtd); ++ return ret; ++ } ++ ++ list_add_tail(&chip->node, &nfc->chips); ++ ++ return 0; ++} ++ ++static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) ++{ ++ struct device_node *np = dev->of_node; ++ struct device_node *nand_np; ++ int nchips = of_get_child_count(np); ++ int ret; ++ ++ if (nchips > 8) { ++ dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); ++ return -EINVAL; ++ } ++ ++ for_each_child_of_node(np, nand_np) { ++ ret = sunxi_nand_chip_init(dev, nfc, nand_np); ++ if (ret) { ++ of_node_put(nand_np); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) ++{ ++ struct sunxi_nand_chip *chip; ++ ++ while (!list_empty(&nfc->chips)) { ++ chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, ++ node); ++ nand_release(nand_to_mtd(&chip->nand)); ++ sunxi_nand_ecc_cleanup(&chip->nand.ecc); ++ list_del(&chip->node); ++ } ++} ++ ++static int sunxi_nfc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *r; ++ struct sunxi_nfc *nfc; ++ int irq; ++ int ret; ++ ++ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ nfc->dev = dev; ++ nand_controller_init(&nfc->controller); ++ INIT_LIST_HEAD(&nfc->chips); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nfc->regs = devm_ioremap_resource(dev, r); ++ if (IS_ERR(nfc->regs)) ++ return PTR_ERR(nfc->regs); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "failed to retrieve irq\n"); ++ return irq; ++ } ++ ++ nfc->ahb_clk = devm_clk_get(dev, "ahb"); ++ if (IS_ERR(nfc->ahb_clk)) { ++ dev_err(dev, "failed to retrieve ahb clk\n"); ++ return PTR_ERR(nfc->ahb_clk); ++ } ++ ++ ret = clk_prepare_enable(nfc->ahb_clk); ++ if (ret) ++ return ret; ++ ++ nfc->mod_clk = devm_clk_get(dev, "mod"); ++ if (IS_ERR(nfc->mod_clk)) { ++ dev_err(dev, "failed to retrieve mod clk\n"); ++ ret = PTR_ERR(nfc->mod_clk); ++ goto out_ahb_clk_unprepare; ++ } ++ ++ ret = clk_prepare_enable(nfc->mod_clk); ++ if (ret) ++ goto out_ahb_clk_unprepare; ++ ++ nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); ++ if (IS_ERR(nfc->reset)) { ++ ret = PTR_ERR(nfc->reset); ++ goto out_mod_clk_unprepare; ++ } ++ ++ ret = reset_control_deassert(nfc->reset); ++ if (ret) { ++ dev_err(dev, "reset err %d\n", ret); ++ goto out_mod_clk_unprepare; ++ } ++ ++ ret = sunxi_nfc_rst(nfc); ++ if (ret) ++ goto out_ahb_reset_reassert; ++ ++ writel(0, nfc->regs + NFC_REG_INT); ++ ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, ++ 0, "sunxi-nand", nfc); ++ if (ret) ++ goto out_ahb_reset_reassert; ++ ++ nfc->dmac = dma_request_slave_channel(dev, "rxtx"); ++ if (nfc->dmac) { ++ struct dma_slave_config dmac_cfg = { }; ++ ++ dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA; ++ dmac_cfg.dst_addr = dmac_cfg.src_addr; ++ dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; ++ dmac_cfg.src_maxburst = 4; ++ dmac_cfg.dst_maxburst = 4; ++ dmaengine_slave_config(nfc->dmac, &dmac_cfg); ++ } else { ++ dev_warn(dev, "failed to request rxtx DMA channel\n"); ++ } ++ ++ platform_set_drvdata(pdev, nfc); ++ ++ ret = sunxi_nand_chips_init(dev, nfc); ++ if (ret) { ++ dev_err(dev, "failed to init nand chips\n"); ++ goto out_release_dmac; ++ } ++ ++ return 0; ++ ++out_release_dmac: ++ if (nfc->dmac) ++ dma_release_channel(nfc->dmac); ++out_ahb_reset_reassert: ++ reset_control_assert(nfc->reset); ++out_mod_clk_unprepare: ++ clk_disable_unprepare(nfc->mod_clk); ++out_ahb_clk_unprepare: ++ clk_disable_unprepare(nfc->ahb_clk); ++ ++ return ret; ++} ++ ++static int sunxi_nfc_remove(struct platform_device *pdev) ++{ ++ struct sunxi_nfc *nfc = platform_get_drvdata(pdev); ++ ++ sunxi_nand_chips_cleanup(nfc); ++ ++ reset_control_assert(nfc->reset); ++ ++ if (nfc->dmac) ++ dma_release_channel(nfc->dmac); ++ clk_disable_unprepare(nfc->mod_clk); ++ clk_disable_unprepare(nfc->ahb_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id sunxi_nfc_ids[] = { ++ { .compatible = "allwinner,sun4i-a10-nand" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); ++ ++static struct platform_driver sunxi_nfc_driver = { ++ .driver = { ++ .name = "sunxi_nand", ++ .of_match_table = sunxi_nfc_ids, ++ }, ++ .probe = sunxi_nfc_probe, ++ .remove = sunxi_nfc_remove, ++}; ++module_platform_driver(sunxi_nfc_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Boris BREZILLON"); ++MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); ++MODULE_ALIAS("platform:sunxi_nand"); +diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c +new file mode 100644 +index 00000000..ae5ad47 +--- /dev/null ++++ b/drivers/mtd/nand/raw/tango_nand.c +@@ -0,0 +1,688 @@ ++/* ++ * Copyright (C) 2016 Sigma Designs ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Offsets relative to chip->base */ ++#define PBUS_CMD 0 ++#define PBUS_ADDR 4 ++#define PBUS_DATA 8 ++ ++/* Offsets relative to reg_base */ ++#define NFC_STATUS 0x00 ++#define NFC_FLASH_CMD 0x04 ++#define NFC_DEVICE_CFG 0x08 ++#define NFC_TIMING1 0x0c ++#define NFC_TIMING2 0x10 ++#define NFC_XFER_CFG 0x14 ++#define NFC_PKT_0_CFG 0x18 ++#define NFC_PKT_N_CFG 0x1c ++#define NFC_BB_CFG 0x20 ++#define NFC_ADDR_PAGE 0x24 ++#define NFC_ADDR_OFFSET 0x28 ++#define NFC_XFER_STATUS 0x2c ++ ++/* NFC_STATUS values */ ++#define CMD_READY BIT(31) ++ ++/* NFC_FLASH_CMD values */ ++#define NFC_READ 1 ++#define NFC_WRITE 2 ++ ++/* NFC_XFER_STATUS values */ ++#define PAGE_IS_EMPTY BIT(16) ++ ++/* Offsets relative to mem_base */ ++#define METADATA 0x000 ++#define ERROR_REPORT 0x1c0 ++ ++/* ++ * Error reports are split in two bytes: ++ * byte 0 for the first packet in the page (PKT_0) ++ * byte 1 for other packets in the page (PKT_N, for N > 0) ++ * ERR_COUNT_PKT_N is the max error count over all but the first packet. ++ */ ++#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f) ++#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f) ++#define DECODE_FAIL_PKT_0(v) (((v) & BIT(7)) == 0) ++#define DECODE_FAIL_PKT_N(v) (((v) & BIT(15)) == 0) ++ ++/* Offsets relative to pbus_base */ ++#define PBUS_CS_CTRL 0x83c ++#define PBUS_PAD_MODE 0x8f0 ++ ++/* PBUS_CS_CTRL values */ ++#define PBUS_IORDY BIT(31) ++ ++/* ++ * PBUS_PAD_MODE values ++ * In raw mode, the driver communicates directly with the NAND chips. ++ * In NFC mode, the NAND Flash controller manages the communication. ++ * We use NFC mode for read and write; raw mode for everything else. ++ */ ++#define MODE_RAW 0 ++#define MODE_NFC BIT(31) ++ ++#define METADATA_SIZE 4 ++#define BBM_SIZE 6 ++#define FIELD_ORDER 15 ++ ++#define MAX_CS 4 ++ ++struct tango_nfc { ++ struct nand_controller hw; ++ void __iomem *reg_base; ++ void __iomem *mem_base; ++ void __iomem *pbus_base; ++ struct tango_chip *chips[MAX_CS]; ++ struct dma_chan *chan; ++ int freq_kHz; ++}; ++ ++#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw) ++ ++struct tango_chip { ++ struct nand_chip nand_chip; ++ void __iomem *base; ++ u32 timing1; ++ u32 timing2; ++ u32 xfer_cfg; ++ u32 pkt_0_cfg; ++ u32 pkt_n_cfg; ++ u32 bb_cfg; ++}; ++ ++#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip) ++ ++#define XFER_CFG(cs, page_count, steps, metadata_size) \ ++ ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size)) ++ ++#define PKT_CFG(size, strength) ((size) << 16 | (strength)) ++ ++#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size)) ++ ++#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3)) ++ ++static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) ++{ ++ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); ++ ++ if (ctrl & NAND_CLE) ++ writeb_relaxed(dat, tchip->base + PBUS_CMD); ++ ++ if (ctrl & NAND_ALE) ++ writeb_relaxed(dat, tchip->base + PBUS_ADDR); ++} ++ ++static int tango_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct tango_nfc *nfc = to_tango_nfc(chip->controller); ++ ++ return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY; ++} ++ ++static u8 tango_read_byte(struct mtd_info *mtd) ++{ ++ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); ++ ++ return readb_relaxed(tchip->base + PBUS_DATA); ++} ++ ++static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len) ++{ ++ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); ++ ++ ioread8_rep(tchip->base + PBUS_DATA, buf, len); ++} ++ ++static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len) ++{ ++ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); ++ ++ iowrite8_rep(tchip->base + PBUS_DATA, buf, len); ++} ++ ++static void tango_select_chip(struct mtd_info *mtd, int idx) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct tango_nfc *nfc = to_tango_nfc(chip->controller); ++ struct tango_chip *tchip = to_tango_chip(chip); ++ ++ if (idx < 0) ++ return; /* No "chip unselect" function */ ++ ++ writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1); ++ writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2); ++ writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG); ++ writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG); ++ writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG); ++ writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG); ++} ++ ++/* ++ * The controller does not check for bitflips in erased pages, ++ * therefore software must check instead. ++ */ ++static int check_erased_page(struct nand_chip *chip, u8 *buf) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 *meta = chip->oob_poi + BBM_SIZE; ++ u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE; ++ const int ecc_size = chip->ecc.bytes; ++ const int pkt_size = chip->ecc.size; ++ int i, res, meta_len, bitflips = 0; ++ ++ for (i = 0; i < chip->ecc.steps; ++i) { ++ meta_len = i ? 0 : METADATA_SIZE; ++ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, ++ meta, meta_len, ++ chip->ecc.strength); ++ if (res < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += res; ++ ++ bitflips = max(res, bitflips); ++ buf += pkt_size; ++ ecc += ecc_size; ++ } ++ ++ return bitflips; ++} ++ ++static int decode_error_report(struct nand_chip *chip) ++{ ++ u32 status, res; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct tango_nfc *nfc = to_tango_nfc(chip->controller); ++ ++ status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS); ++ if (status & PAGE_IS_EMPTY) ++ return 0; ++ ++ res = readl_relaxed(nfc->mem_base + ERROR_REPORT); ++ ++ if (DECODE_FAIL_PKT_0(res) || DECODE_FAIL_PKT_N(res)) ++ return -EBADMSG; ++ ++ /* ERR_COUNT_PKT_N is max, not sum, but that's all we have */ ++ mtd->ecc_stats.corrected += ++ ERR_COUNT_PKT_0(res) + ERR_COUNT_PKT_N(res); ++ ++ return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res)); ++} ++ ++static void tango_dma_callback(void *arg) ++{ ++ complete(arg); ++} ++ ++static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd, ++ const void *buf, int len, int page) ++{ ++ void __iomem *addr = nfc->reg_base + NFC_STATUS; ++ struct dma_chan *chan = nfc->chan; ++ struct dma_async_tx_descriptor *desc; ++ enum dma_transfer_direction tdir; ++ struct scatterlist sg; ++ struct completion tx_done; ++ int err = -EIO; ++ u32 res, val; ++ ++ sg_init_one(&sg, buf, len); ++ if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1) ++ return -EIO; ++ ++ tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; ++ desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT); ++ if (!desc) ++ goto dma_unmap; ++ ++ desc->callback = tango_dma_callback; ++ desc->callback_param = &tx_done; ++ init_completion(&tx_done); ++ ++ writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE); ++ ++ writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE); ++ writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET); ++ writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD); ++ ++ dmaengine_submit(desc); ++ dma_async_issue_pending(chan); ++ ++ res = wait_for_completion_timeout(&tx_done, HZ); ++ if (res > 0) ++ err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000); ++ ++ writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); ++ ++dma_unmap: ++ dma_unmap_sg(chan->device->dev, &sg, 1, dir); ++ ++ return err; ++} ++ ++static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ u8 *buf, int oob_required, int page) ++{ ++ struct tango_nfc *nfc = to_tango_nfc(chip->controller); ++ int err, res, len = mtd->writesize; ++ ++ if (oob_required) ++ chip->ecc.read_oob(mtd, chip, page); ++ ++ err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page); ++ if (err) ++ return err; ++ ++ res = decode_error_report(chip); ++ if (res < 0) { ++ chip->ecc.read_oob_raw(mtd, chip, page); ++ res = check_erased_page(chip, buf); ++ } ++ ++ return res; ++} ++ ++static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const u8 *buf, int oob_required, int page) ++{ ++ struct tango_nfc *nfc = to_tango_nfc(chip->controller); ++ int err, status, len = mtd->writesize; ++ ++ /* Calling tango_write_oob() would send PAGEPROG twice */ ++ if (oob_required) ++ return -ENOTSUPP; ++ ++ writel_relaxed(0xffffffff, nfc->mem_base + METADATA); ++ err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page); ++ if (err) ++ return err; ++ ++ status = chip->waitfunc(mtd, chip); ++ if (status & NAND_STATUS_FAIL) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ *pos += len; ++ ++ if (!*buf) { ++ /* skip over "len" bytes */ ++ nand_change_read_column_op(chip, *pos, NULL, 0, false); ++ } else { ++ tango_read_buf(mtd, *buf, len); ++ *buf += len; ++ } ++} ++ ++static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ ++ *pos += len; ++ ++ if (!*buf) { ++ /* skip over "len" bytes */ ++ nand_change_write_column_op(chip, *pos, NULL, 0, false); ++ } else { ++ tango_write_buf(mtd, *buf, len); ++ *buf += len; ++ } ++} ++ ++/* ++ * Physical page layout (not drawn to scale) ++ * ++ * NB: Bad Block Marker area splits PKT_N in two (N1, N2). ++ * ++ * +---+-----------------+-------+-----+-----------+-----+----+-------+ ++ * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N | ++ * +---+-----------------+-------+-----+-----------+-----+----+-------+ ++ * ++ * Logical page layout: ++ * ++ * +-----+---+-------+-----+-------+ ++ * oob = | BBM | M | ECC_0 | ... | ECC_N | ++ * +-----+---+-------+-----+-------+ ++ * ++ * +-----------------+-----+-----------------+ ++ * buf = | PKT_0 | ... | PKT_N | ++ * +-----------------+-----+-----------------+ ++ */ ++static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u8 *oob_orig = oob; ++ const int page_size = mtd->writesize; ++ const int ecc_size = chip->ecc.bytes; ++ const int pkt_size = chip->ecc.size; ++ int pos = 0; /* position within physical page */ ++ int rem = page_size; /* bytes remaining until BBM area */ ++ ++ if (oob) ++ oob += BBM_SIZE; ++ ++ aux_read(chip, &oob, METADATA_SIZE, &pos); ++ ++ while (rem > pkt_size) { ++ aux_read(chip, &buf, pkt_size, &pos); ++ aux_read(chip, &oob, ecc_size, &pos); ++ rem = page_size - pos; ++ } ++ ++ aux_read(chip, &buf, rem, &pos); ++ aux_read(chip, &oob_orig, BBM_SIZE, &pos); ++ aux_read(chip, &buf, pkt_size - rem, &pos); ++ aux_read(chip, &oob, ecc_size, &pos); ++} ++ ++static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ const u8 *oob_orig = oob; ++ const int page_size = mtd->writesize; ++ const int ecc_size = chip->ecc.bytes; ++ const int pkt_size = chip->ecc.size; ++ int pos = 0; /* position within physical page */ ++ int rem = page_size; /* bytes remaining until BBM area */ ++ ++ if (oob) ++ oob += BBM_SIZE; ++ ++ aux_write(chip, &oob, METADATA_SIZE, &pos); ++ ++ while (rem > pkt_size) { ++ aux_write(chip, &buf, pkt_size, &pos); ++ aux_write(chip, &oob, ecc_size, &pos); ++ rem = page_size - pos; ++ } ++ ++ aux_write(chip, &buf, rem, &pos); ++ aux_write(chip, &oob_orig, BBM_SIZE, &pos); ++ aux_write(chip, &buf, pkt_size - rem, &pos); ++ aux_write(chip, &oob, ecc_size, &pos); ++} ++ ++static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ u8 *buf, int oob_required, int page) ++{ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ raw_read(chip, buf, chip->oob_poi); ++ return 0; ++} ++ ++static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ const u8 *buf, int oob_required, int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ raw_write(chip, buf, chip->oob_poi); ++ return nand_prog_page_end_op(chip); ++} ++ ++static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ nand_read_page_op(chip, page, 0, NULL, 0); ++ raw_read(chip, NULL, chip->oob_poi); ++ return 0; ++} ++ ++static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, ++ int page) ++{ ++ nand_prog_page_begin_op(chip, page, 0, NULL, 0); ++ raw_write(chip, NULL, chip->oob_poi); ++ return nand_prog_page_end_op(chip); ++} ++ ++static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (idx >= ecc->steps) ++ return -ERANGE; ++ ++ res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx; ++ res->length = ecc->bytes; ++ ++ return 0; ++} ++ ++static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) ++{ ++ return -ERANGE; /* no free space in spare area */ ++} ++ ++static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = { ++ .ecc = oob_ecc, ++ .free = oob_free, ++}; ++ ++static u32 to_ticks(int kHz, int ps) ++{ ++ return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC); ++} ++ ++static int tango_set_timings(struct mtd_info *mtd, int csline, ++ const struct nand_data_interface *conf) ++{ ++ const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct tango_nfc *nfc = to_tango_nfc(chip->controller); ++ struct tango_chip *tchip = to_tango_chip(chip); ++ u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr; ++ int kHz = nfc->freq_kHz; ++ ++ if (IS_ERR(sdr)) ++ return PTR_ERR(sdr); ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max); ++ Textw = to_ticks(kHz, sdr->tWB_max); ++ Twc = to_ticks(kHz, sdr->tWC_min); ++ Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min); ++ ++ Tacc = to_ticks(kHz, sdr->tREA_max); ++ Thold = to_ticks(kHz, sdr->tREH_min); ++ Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min); ++ Textr = to_ticks(kHz, sdr->tRHZ_max); ++ ++ tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw); ++ tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr); ++ ++ return 0; ++} ++ ++static int chip_init(struct device *dev, struct device_node *np) ++{ ++ u32 cs; ++ int err, res; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ struct tango_chip *tchip; ++ struct nand_ecc_ctrl *ecc; ++ struct tango_nfc *nfc = dev_get_drvdata(dev); ++ ++ tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL); ++ if (!tchip) ++ return -ENOMEM; ++ ++ res = of_property_count_u32_elems(np, "reg"); ++ if (res < 0) ++ return res; ++ ++ if (res != 1) ++ return -ENOTSUPP; /* Multi-CS chips are not supported */ ++ ++ err = of_property_read_u32_index(np, "reg", 0, &cs); ++ if (err) ++ return err; ++ ++ if (cs >= MAX_CS) ++ return -EINVAL; ++ ++ chip = &tchip->nand_chip; ++ ecc = &chip->ecc; ++ mtd = nand_to_mtd(chip); ++ ++ chip->read_byte = tango_read_byte; ++ chip->write_buf = tango_write_buf; ++ chip->read_buf = tango_read_buf; ++ chip->select_chip = tango_select_chip; ++ chip->cmd_ctrl = tango_cmd_ctrl; ++ chip->dev_ready = tango_dev_ready; ++ chip->setup_data_interface = tango_set_timings; ++ chip->options = NAND_USE_BOUNCE_BUFFER | ++ NAND_NO_SUBPAGE_WRITE | ++ NAND_WAIT_TCCS; ++ chip->controller = &nfc->hw; ++ tchip->base = nfc->pbus_base + (cs * 256); ++ ++ nand_set_flash_node(chip, np); ++ mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops); ++ mtd->dev.parent = dev; ++ ++ err = nand_scan_ident(mtd, 1, NULL); ++ if (err) ++ return err; ++ ++ ecc->mode = NAND_ECC_HW; ++ ecc->algo = NAND_ECC_BCH; ++ ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); ++ ++ ecc->read_page_raw = tango_read_page_raw; ++ ecc->write_page_raw = tango_write_page_raw; ++ ecc->read_page = tango_read_page; ++ ecc->write_page = tango_write_page; ++ ecc->read_oob = tango_read_oob; ++ ecc->write_oob = tango_write_oob; ++ ++ err = nand_scan_tail(mtd); ++ if (err) ++ return err; ++ ++ tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE); ++ tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength); ++ tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength); ++ tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE); ++ ++ err = mtd_device_register(mtd, NULL, 0); ++ if (err) ++ return err; ++ ++ nfc->chips[cs] = tchip; ++ ++ return 0; ++} ++ ++static int tango_nand_remove(struct platform_device *pdev) ++{ ++ int cs; ++ struct tango_nfc *nfc = platform_get_drvdata(pdev); ++ ++ dma_release_channel(nfc->chan); ++ ++ for (cs = 0; cs < MAX_CS; ++cs) { ++ if (nfc->chips[cs]) ++ nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip)); ++ } ++ ++ return 0; ++} ++ ++static int tango_nand_probe(struct platform_device *pdev) ++{ ++ int err; ++ struct clk *clk; ++ struct resource *res; ++ struct tango_nfc *nfc; ++ struct device_node *np; ++ ++ nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nfc->reg_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(nfc->reg_base)) ++ return PTR_ERR(nfc->reg_base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ nfc->mem_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(nfc->mem_base)) ++ return PTR_ERR(nfc->mem_base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(nfc->pbus_base)) ++ return PTR_ERR(nfc->pbus_base); ++ ++ writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ nfc->chan = dma_request_chan(&pdev->dev, "rxtx"); ++ if (IS_ERR(nfc->chan)) ++ return PTR_ERR(nfc->chan); ++ ++ platform_set_drvdata(pdev, nfc); ++ nand_controller_init(&nfc->hw); ++ nfc->freq_kHz = clk_get_rate(clk) / 1000; ++ ++ for_each_child_of_node(pdev->dev.of_node, np) { ++ err = chip_init(&pdev->dev, np); ++ if (err) { ++ tango_nand_remove(pdev); ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id tango_nand_ids[] = { ++ { .compatible = "sigma,smp8758-nand" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, tango_nand_ids); ++ ++static struct platform_driver tango_nand_driver = { ++ .probe = tango_nand_probe, ++ .remove = tango_nand_remove, ++ .driver = { ++ .name = "tango-nand", ++ .of_match_table = tango_nand_ids, ++ }, ++}; ++ ++module_platform_driver(tango_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Sigma Designs"); ++MODULE_DESCRIPTION("Tango4 NAND Flash controller driver"); +diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c +new file mode 100644 +index 00000000..dcaa924 +--- /dev/null ++++ b/drivers/mtd/nand/raw/tmio_nand.c +@@ -0,0 +1,513 @@ ++/* ++ * Toshiba TMIO NAND flash controller driver ++ * ++ * Slightly murky pre-git history of the driver: ++ * ++ * Copyright (c) Ian Molton 2004, 2005, 2008 ++ * Original work, independent of sharps code. Included hardware ECC support. ++ * Hard ECC did not work for writes in the early revisions. ++ * Copyright (c) Dirk Opfer 2005. ++ * Modifications developed from sharps code but ++ * NOT containing any, ported onto Ians base. ++ * Copyright (c) Chris Humbert 2005 ++ * Copyright (c) Dmitry Baryshkov 2008 ++ * Minor fixes ++ * ++ * Parts copyright Sebastian Carlier ++ * ++ * This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * NAND Flash Host Controller Configuration Register ++ */ ++#define CCR_COMMAND 0x04 /* w Command */ ++#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */ ++#define CCR_INTP 0x3d /* b Interrupt Pin */ ++#define CCR_INTE 0x48 /* b Interrupt Enable */ ++#define CCR_EC 0x4a /* b Event Control */ ++#define CCR_ICC 0x4c /* b Internal Clock Control */ ++#define CCR_ECCC 0x5b /* b ECC Control */ ++#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */ ++#define CCR_NFM 0x61 /* b NAND Flash Monitor */ ++#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */ ++#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */ ++ ++/* ++ * NAND Flash Control Register ++ */ ++#define FCR_DATA 0x00 /* bwl Data Register */ ++#define FCR_MODE 0x04 /* b Mode Register */ ++#define FCR_STATUS 0x05 /* b Status Register */ ++#define FCR_ISR 0x06 /* b Interrupt Status Register */ ++#define FCR_IMR 0x07 /* b Interrupt Mask Register */ ++ ++/* FCR_MODE Register Command List */ ++#define FCR_MODE_DATA 0x94 /* Data Data_Mode */ ++#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */ ++#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */ ++ ++#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */ ++#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */ ++#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */ ++ ++#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */ ++#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */ ++ ++#define FCR_MODE_LED_OFF 0x00 /* LED OFF */ ++#define FCR_MODE_LED_ON 0x04 /* LED ON */ ++ ++#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */ ++#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */ ++ ++#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */ ++#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */ ++ ++#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */ ++#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */ ++ ++#define FCR_MODE_WE 0x80 ++#define FCR_MODE_ECC1 0x40 ++#define FCR_MODE_ECC0 0x20 ++#define FCR_MODE_CE 0x10 ++#define FCR_MODE_PCNT1 0x08 ++#define FCR_MODE_PCNT0 0x04 ++#define FCR_MODE_ALE 0x02 ++#define FCR_MODE_CLE 0x01 ++ ++#define FCR_STATUS_BUSY 0x80 ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct tmio_nand { ++ struct nand_chip chip; ++ ++ struct platform_device *dev; ++ ++ void __iomem *ccr; ++ void __iomem *fcr; ++ unsigned long fcr_base; ++ ++ unsigned int irq; ++ ++ /* for tmio_nand_read_byte */ ++ u8 read; ++ unsigned read_good:1; ++}; ++ ++static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct tmio_nand, chip); ++} ++ ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u8 mode; ++ ++ if (ctrl & NAND_NCE) { ++ mode = FCR_MODE_DATA; ++ ++ if (ctrl & NAND_CLE) ++ mode |= FCR_MODE_CLE; ++ else ++ mode &= ~FCR_MODE_CLE; ++ ++ if (ctrl & NAND_ALE) ++ mode |= FCR_MODE_ALE; ++ else ++ mode &= ~FCR_MODE_ALE; ++ } else { ++ mode = FCR_MODE_STANDBY; ++ } ++ ++ tmio_iowrite8(mode, tmio->fcr + FCR_MODE); ++ tmio->read_good = 0; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ tmio_iowrite8(cmd, chip->IO_ADDR_W); ++} ++ ++static int tmio_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ ++ return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY); ++} ++ ++static irqreturn_t tmio_irq(int irq, void *__tmio) ++{ ++ struct tmio_nand *tmio = __tmio; ++ struct nand_chip *nand_chip = &tmio->chip; ++ ++ /* disable RDYREQ interrupt */ ++ tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); ++ ++ if (unlikely(!waitqueue_active(&nand_chip->controller->wq))) ++ dev_warn(&tmio->dev->dev, "spurious interrupt\n"); ++ ++ wake_up(&nand_chip->controller->wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. ++ *This interrupt is normally disabled, but for long operations like ++ *erase and write, we enable it to wake us up. The irq handler ++ *disables the interrupt. ++ */ ++static int ++tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ long timeout; ++ u8 status; ++ ++ /* enable RDYREQ interrupt */ ++ tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); ++ tmio_iowrite8(0x81, tmio->fcr + FCR_IMR); ++ ++ timeout = wait_event_timeout(nand_chip->controller->wq, ++ tmio_nand_dev_ready(mtd), ++ msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20)); ++ ++ if (unlikely(!tmio_nand_dev_ready(mtd))) { ++ tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); ++ dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n", ++ nand_chip->state == FL_ERASING ? "erase" : "program", ++ nand_chip->state == FL_ERASING ? 400 : 20); ++ ++ } else if (unlikely(!timeout)) { ++ tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); ++ dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); ++ } ++ ++ nand_status_op(nand_chip, &status); ++ return status; ++} ++ ++/* ++ *The TMIO controller combines two 8-bit data bytes into one 16-bit ++ *word. This function separates them so nand_base.c works as expected, ++ *especially its NAND_CMD_READID routines. ++ * ++ *To prevent stale data from being read, tmio_nand_hwcontrol() clears ++ *tmio->read_good. ++ */ ++static u_char tmio_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ unsigned int data; ++ ++ if (tmio->read_good--) ++ return tmio->read; ++ ++ data = tmio_ioread16(tmio->fcr + FCR_DATA); ++ tmio->read = data >> 8; ++ return data; ++} ++ ++/* ++ *The TMIO controller converts an 8-bit NAND interface to a 16-bit ++ *bus interface, so all data reads and writes must be 16-bit wide. ++ *Thus, we implement 16-bit versions of the read, write, and verify ++ *buffer functions. ++ */ ++static void ++tmio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ ++ tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); ++} ++ ++static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ ++ tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); ++} ++ ++static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ ++ tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE); ++ tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */ ++ tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE); ++} ++ ++static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ unsigned int ecc; ++ ++ tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE); ++ ++ ecc = tmio_ioread16(tmio->fcr + FCR_DATA); ++ ecc_code[1] = ecc; /* 000-255 LP7-0 */ ++ ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ ++ ecc = tmio_ioread16(tmio->fcr + FCR_DATA); ++ ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ ++ ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ ++ ecc = tmio_ioread16(tmio->fcr + FCR_DATA); ++ ecc_code[3] = ecc; /* 256-511 LP15-8 */ ++ ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ ++ ++ tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE); ++ return 0; ++} ++ ++static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, ++ unsigned char *read_ecc, unsigned char *calc_ecc) ++{ ++ int r0, r1; ++ ++ /* assume ecc.size = 512 and ecc.bytes = 6 */ ++ r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256); ++ if (r0 < 0) ++ return r0; ++ r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256); ++ if (r1 < 0) ++ return r1; ++ return r0 + r1; ++} ++ ++static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) ++{ ++ const struct mfd_cell *cell = mfd_get_cell(dev); ++ int ret; ++ ++ if (cell->enable) { ++ ret = cell->enable(dev); ++ if (ret) ++ return ret; ++ } ++ ++ /* (4Ch) CLKRUN Enable 1st spcrunc */ ++ tmio_iowrite8(0x81, tmio->ccr + CCR_ICC); ++ ++ /* (10h)BaseAddress 0x1000 spba.spba2 */ ++ tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE); ++ tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2); ++ ++ /* (04h)Command Register I/O spcmd */ ++ tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND); ++ ++ /* (62h) Power Supply Control ssmpwc */ ++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ ++ tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC); ++ ++ /* (63h) Detect Control ssmdtc */ ++ tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC); ++ ++ /* Interrupt status register clear sintst */ ++ tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); ++ ++ /* After power supply, Media are reset smode */ ++ tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE); ++ tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE); ++ tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA); ++ ++ /* Standby Mode smode */ ++ tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE); ++ ++ mdelay(5); ++ ++ return 0; ++} ++ ++static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) ++{ ++ const struct mfd_cell *cell = mfd_get_cell(dev); ++ ++ tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); ++ if (cell->disable) ++ cell->disable(dev); ++} ++ ++static int tmio_probe(struct platform_device *dev) ++{ ++ struct tmio_nand_data *data = dev_get_platdata(&dev->dev); ++ struct resource *fcr = platform_get_resource(dev, ++ IORESOURCE_MEM, 0); ++ struct resource *ccr = platform_get_resource(dev, ++ IORESOURCE_MEM, 1); ++ int irq = platform_get_irq(dev, 0); ++ struct tmio_nand *tmio; ++ struct mtd_info *mtd; ++ struct nand_chip *nand_chip; ++ int retval; ++ ++ if (data == NULL) ++ dev_warn(&dev->dev, "NULL platform data!\n"); ++ ++ tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); ++ if (!tmio) ++ return -ENOMEM; ++ ++ tmio->dev = dev; ++ ++ platform_set_drvdata(dev, tmio); ++ nand_chip = &tmio->chip; ++ mtd = nand_to_mtd(nand_chip); ++ mtd->name = "tmio-nand"; ++ mtd->dev.parent = &dev->dev; ++ ++ tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); ++ if (!tmio->ccr) ++ return -EIO; ++ ++ tmio->fcr_base = fcr->start & 0xfffff; ++ tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr)); ++ if (!tmio->fcr) ++ return -EIO; ++ ++ retval = tmio_hw_init(dev, tmio); ++ if (retval) ++ return retval; ++ ++ /* Set address of NAND IO lines */ ++ nand_chip->IO_ADDR_R = tmio->fcr; ++ nand_chip->IO_ADDR_W = tmio->fcr; ++ ++ /* Set address of hardware control function */ ++ nand_chip->cmd_ctrl = tmio_nand_hwcontrol; ++ nand_chip->dev_ready = tmio_nand_dev_ready; ++ nand_chip->read_byte = tmio_nand_read_byte; ++ nand_chip->write_buf = tmio_nand_write_buf; ++ nand_chip->read_buf = tmio_nand_read_buf; ++ ++ /* set eccmode using hardware ECC */ ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ nand_chip->ecc.bytes = 6; ++ nand_chip->ecc.strength = 2; ++ nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; ++ nand_chip->ecc.calculate = tmio_nand_calculate_ecc; ++ nand_chip->ecc.correct = tmio_nand_correct_data; ++ ++ if (data) ++ nand_chip->badblock_pattern = data->badblock_pattern; ++ ++ /* 15 us command delay time */ ++ nand_chip->chip_delay = 15; ++ ++ retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0, ++ dev_name(&dev->dev), tmio); ++ if (retval) { ++ dev_err(&dev->dev, "request_irq error %d\n", retval); ++ goto err_irq; ++ } ++ ++ tmio->irq = irq; ++ nand_chip->waitfunc = tmio_nand_wait; ++ ++ /* Scan to find existence of the device */ ++ retval = nand_scan(mtd, 1); ++ if (retval) ++ goto err_irq; ++ ++ /* Register the partitions */ ++ retval = mtd_device_parse_register(mtd, ++ data ? data->part_parsers : NULL, ++ NULL, ++ data ? data->partition : NULL, ++ data ? data->num_partitions : 0); ++ if (!retval) ++ return retval; ++ ++ nand_release(mtd); ++ ++err_irq: ++ tmio_hw_stop(dev, tmio); ++ return retval; ++} ++ ++static int tmio_remove(struct platform_device *dev) ++{ ++ struct tmio_nand *tmio = platform_get_drvdata(dev); ++ ++ nand_release(nand_to_mtd(&tmio->chip)); ++ tmio_hw_stop(dev, tmio); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tmio_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ const struct mfd_cell *cell = mfd_get_cell(dev); ++ ++ if (cell->suspend) ++ cell->suspend(dev); ++ ++ tmio_hw_stop(dev, platform_get_drvdata(dev)); ++ return 0; ++} ++ ++static int tmio_resume(struct platform_device *dev) ++{ ++ const struct mfd_cell *cell = mfd_get_cell(dev); ++ ++ /* FIXME - is this required or merely another attack of the broken ++ * SHARP platform? Looks suspicious. ++ */ ++ tmio_hw_init(dev, platform_get_drvdata(dev)); ++ ++ if (cell->resume) ++ cell->resume(dev); ++ ++ return 0; ++} ++#else ++#define tmio_suspend NULL ++#define tmio_resume NULL ++#endif ++ ++static struct platform_driver tmio_driver = { ++ .driver.name = "tmio-nand", ++ .driver.owner = THIS_MODULE, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++ .suspend = tmio_suspend, ++ .resume = tmio_resume, ++}; ++ ++module_platform_driver(tmio_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); ++MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller"); ++MODULE_ALIAS("platform:tmio-nand"); +diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c +new file mode 100644 +index 00000000..dd232d8 +--- /dev/null ++++ b/drivers/mtd/nand/raw/txx9ndfmc.c +@@ -0,0 +1,423 @@ ++/* ++ * TXx9 NAND flash memory controller driver ++ * Based on RBTX49xx patch from CELF patch archive. ++ * ++ * 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. ++ * ++ * (C) Copyright TOSHIBA CORPORATION 2004-2007 ++ * All Rights Reserved. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* TXX9 NDFMC Registers */ ++#define TXX9_NDFDTR 0x00 ++#define TXX9_NDFMCR 0x04 ++#define TXX9_NDFSR 0x08 ++#define TXX9_NDFISR 0x0c ++#define TXX9_NDFIMR 0x10 ++#define TXX9_NDFSPR 0x14 ++#define TXX9_NDFRSTR 0x18 /* not TX4939 */ ++ ++/* NDFMCR : NDFMC Mode Control */ ++#define TXX9_NDFMCR_WE 0x80 ++#define TXX9_NDFMCR_ECC_ALL 0x60 ++#define TXX9_NDFMCR_ECC_RESET 0x60 ++#define TXX9_NDFMCR_ECC_READ 0x40 ++#define TXX9_NDFMCR_ECC_ON 0x20 ++#define TXX9_NDFMCR_ECC_OFF 0x00 ++#define TXX9_NDFMCR_CE 0x10 ++#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ ++#define TXX9_NDFMCR_ALE 0x02 ++#define TXX9_NDFMCR_CLE 0x01 ++/* TX4939 only */ ++#define TXX9_NDFMCR_X16 0x0400 ++#define TXX9_NDFMCR_DMAREQ_MASK 0x0300 ++#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 ++#define TXX9_NDFMCR_DMAREQ_128 0x0100 ++#define TXX9_NDFMCR_DMAREQ_256 0x0200 ++#define TXX9_NDFMCR_DMAREQ_512 0x0300 ++#define TXX9_NDFMCR_CS_MASK 0x0c ++#define TXX9_NDFMCR_CS(ch) ((ch) << 2) ++ ++/* NDFMCR : NDFMC Status */ ++#define TXX9_NDFSR_BUSY 0x80 ++/* TX4939 only */ ++#define TXX9_NDFSR_DMARUN 0x40 ++ ++/* NDFMCR : NDFMC Reset */ ++#define TXX9_NDFRSTR_RST 0x01 ++ ++struct txx9ndfmc_priv { ++ struct platform_device *dev; ++ struct nand_chip chip; ++ int cs; ++ const char *mtdname; ++}; ++ ++#define MAX_TXX9NDFMC_DEV 4 ++struct txx9ndfmc_drvdata { ++ struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; ++ void __iomem *base; ++ unsigned char hold; /* in gbusclock */ ++ unsigned char spw; /* in gbusclock */ ++ struct nand_controller hw_control; ++}; ++ ++static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); ++ return txx9_priv->dev; ++} ++ ++static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) ++{ ++ struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); ++ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); ++ ++ return drvdata->base + (reg << plat->shift); ++} ++ ++static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) ++{ ++ return __raw_readl(ndregaddr(dev, reg)); ++} ++ ++static void txx9ndfmc_write(struct platform_device *dev, ++ u32 val, unsigned int reg) ++{ ++ __raw_writel(val, ndregaddr(dev, reg)); ++} ++ ++static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd) ++{ ++ struct platform_device *dev = mtd_to_platdev(mtd); ++ ++ return txx9ndfmc_read(dev, TXX9_NDFDTR); ++} ++ ++static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct platform_device *dev = mtd_to_platdev(mtd); ++ void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); ++ u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); ++ ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); ++ while (len--) ++ __raw_writel(*buf++, ndfdtr); ++ txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); ++} ++ ++static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct platform_device *dev = mtd_to_platdev(mtd); ++ void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); ++ ++ while (len--) ++ *buf++ = __raw_readl(ndfdtr); ++} ++ ++static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); ++ struct platform_device *dev = txx9_priv->dev; ++ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); ++ ++ mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); ++ mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; ++ mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; ++ /* TXX9_NDFMCR_CE bit is 0:high 1:low */ ++ mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; ++ if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { ++ mcr &= ~TXX9_NDFMCR_CS_MASK; ++ mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); ++ } ++ txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); ++ } ++ if (cmd != NAND_CMD_NONE) ++ txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); ++ if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { ++ /* dummy write to update external latch */ ++ if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) ++ txx9ndfmc_write(dev, 0, TXX9_NDFDTR); ++ } ++ mmiowb(); ++} ++ ++static int txx9ndfmc_dev_ready(struct mtd_info *mtd) ++{ ++ struct platform_device *dev = mtd_to_platdev(mtd); ++ ++ return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); ++} ++ ++static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, ++ uint8_t *ecc_code) ++{ ++ struct platform_device *dev = mtd_to_platdev(mtd); ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int eccbytes; ++ u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); ++ ++ mcr &= ~TXX9_NDFMCR_ECC_ALL; ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); ++ for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { ++ ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); ++ ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); ++ ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); ++ ecc_code += 3; ++ } ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); ++ return 0; ++} ++ ++static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf, ++ unsigned char *read_ecc, unsigned char *calc_ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int eccsize; ++ int corrected = 0; ++ int stat; ++ ++ for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { ++ stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256); ++ if (stat < 0) ++ return stat; ++ corrected += stat; ++ buf += 256; ++ read_ecc += 3; ++ calc_ecc += 3; ++ } ++ return corrected; ++} ++ ++static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct platform_device *dev = mtd_to_platdev(mtd); ++ u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); ++ ++ mcr &= ~TXX9_NDFMCR_ECC_ALL; ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); ++ txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); ++} ++ ++static void txx9ndfmc_initialize(struct platform_device *dev) ++{ ++ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); ++ struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); ++ int tmout = 100; ++ ++ if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) ++ ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ ++ else { ++ /* reset NDFMC */ ++ txx9ndfmc_write(dev, ++ txx9ndfmc_read(dev, TXX9_NDFRSTR) | ++ TXX9_NDFRSTR_RST, ++ TXX9_NDFRSTR); ++ while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { ++ if (--tmout == 0) { ++ dev_err(&dev->dev, "reset failed.\n"); ++ break; ++ } ++ udelay(1); ++ } ++ } ++ /* setup Hold Time, Strobe Pulse Width */ ++ txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); ++ txx9ndfmc_write(dev, ++ (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? ++ TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); ++} ++ ++#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ ++ DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) ++ ++static int txx9ndfmc_nand_scan(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ int ret; ++ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (!ret) { ++ if (mtd->writesize >= 512) { ++ /* Hardware ECC 6 byte ECC per 512 Byte data */ ++ chip->ecc.size = 512; ++ chip->ecc.bytes = 6; ++ } ++ ret = nand_scan_tail(mtd); ++ } ++ return ret; ++} ++ ++static int __init txx9ndfmc_probe(struct platform_device *dev) ++{ ++ struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); ++ int hold, spw; ++ int i; ++ struct txx9ndfmc_drvdata *drvdata; ++ unsigned long gbusclk = plat->gbus_clock; ++ struct resource *res; ++ ++ drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ res = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ drvdata->base = devm_ioremap_resource(&dev->dev, res); ++ if (IS_ERR(drvdata->base)) ++ return PTR_ERR(drvdata->base); ++ ++ hold = plat->hold ?: 20; /* tDH */ ++ spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ ++ ++ hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); ++ spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); ++ if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) ++ hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ ++ spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ ++ hold = clamp(hold, 1, 15); ++ drvdata->hold = hold; ++ spw = clamp(spw, 1, 15); ++ drvdata->spw = spw; ++ dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", ++ (gbusclk + 500000) / 1000000, hold, spw); ++ ++ nand_controller_init(&drvdata->hw_control); ++ ++ platform_set_drvdata(dev, drvdata); ++ txx9ndfmc_initialize(dev); ++ ++ for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { ++ struct txx9ndfmc_priv *txx9_priv; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ ++ if (!(plat->ch_mask & (1 << i))) ++ continue; ++ txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), ++ GFP_KERNEL); ++ if (!txx9_priv) ++ continue; ++ chip = &txx9_priv->chip; ++ mtd = nand_to_mtd(chip); ++ mtd->dev.parent = &dev->dev; ++ ++ chip->read_byte = txx9ndfmc_read_byte; ++ chip->read_buf = txx9ndfmc_read_buf; ++ chip->write_buf = txx9ndfmc_write_buf; ++ chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; ++ chip->dev_ready = txx9ndfmc_dev_ready; ++ chip->ecc.calculate = txx9ndfmc_calculate_ecc; ++ chip->ecc.correct = txx9ndfmc_correct_data; ++ chip->ecc.hwctl = txx9ndfmc_enable_hwecc; ++ chip->ecc.mode = NAND_ECC_HW; ++ /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ ++ chip->ecc.size = 256; ++ chip->ecc.bytes = 3; ++ chip->ecc.strength = 1; ++ chip->chip_delay = 100; ++ chip->controller = &drvdata->hw_control; ++ ++ nand_set_controller_data(chip, txx9_priv); ++ txx9_priv->dev = dev; ++ ++ if (plat->ch_mask != 1) { ++ txx9_priv->cs = i; ++ txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", ++ dev_name(&dev->dev), i); ++ } else { ++ txx9_priv->cs = -1; ++ txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), ++ GFP_KERNEL); ++ } ++ if (!txx9_priv->mtdname) { ++ kfree(txx9_priv); ++ dev_err(&dev->dev, "Unable to allocate MTD name.\n"); ++ continue; ++ } ++ if (plat->wide_mask & (1 << i)) ++ chip->options |= NAND_BUSWIDTH_16; ++ ++ if (txx9ndfmc_nand_scan(mtd)) { ++ kfree(txx9_priv->mtdname); ++ kfree(txx9_priv); ++ continue; ++ } ++ mtd->name = txx9_priv->mtdname; ++ ++ mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); ++ drvdata->mtds[i] = mtd; ++ } ++ ++ return 0; ++} ++ ++static int __exit txx9ndfmc_remove(struct platform_device *dev) ++{ ++ struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); ++ int i; ++ ++ if (!drvdata) ++ return 0; ++ for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { ++ struct mtd_info *mtd = drvdata->mtds[i]; ++ struct nand_chip *chip; ++ struct txx9ndfmc_priv *txx9_priv; ++ ++ if (!mtd) ++ continue; ++ chip = mtd_to_nand(mtd); ++ txx9_priv = nand_get_controller_data(chip); ++ ++ nand_release(mtd); ++ kfree(txx9_priv->mtdname); ++ kfree(txx9_priv); ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int txx9ndfmc_resume(struct platform_device *dev) ++{ ++ if (platform_get_drvdata(dev)) ++ txx9ndfmc_initialize(dev); ++ return 0; ++} ++#else ++#define txx9ndfmc_resume NULL ++#endif ++ ++static struct platform_driver txx9ndfmc_driver = { ++ .remove = __exit_p(txx9ndfmc_remove), ++ .resume = txx9ndfmc_resume, ++ .driver = { ++ .name = "txx9ndfmc", ++ }, ++}; ++ ++module_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); ++MODULE_ALIAS("platform:txx9ndfmc"); +diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c +new file mode 100644 +index 00000000..f367144 +--- /dev/null ++++ b/drivers/mtd/nand/raw/vf610_nfc.c +@@ -0,0 +1,845 @@ ++/* ++ * Copyright 2009-2015 Freescale Semiconductor, Inc. and others ++ * ++ * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. ++ * Jason ported to M54418TWR and MVFA5 (VF610). ++ * Authors: Stefan Agner ++ * Bill Pringlemeir ++ * Shaohui Xie ++ * Jason Jin ++ * ++ * Based on original driver mpc5121_nfc.c. ++ * ++ * This 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. ++ * ++ * Limitations: ++ * - Untested on MPC5125 and M54418. ++ * - DMA and pipelining not used. ++ * - 2K pages or less. ++ * - HW ECC: Only 2K page with 64+ OOB. ++ * - HW ECC: Only 24 and 32-bit error correction implemented. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "vf610_nfc" ++ ++/* Register Offsets */ ++#define NFC_FLASH_CMD1 0x3F00 ++#define NFC_FLASH_CMD2 0x3F04 ++#define NFC_COL_ADDR 0x3F08 ++#define NFC_ROW_ADDR 0x3F0c ++#define NFC_ROW_ADDR_INC 0x3F14 ++#define NFC_FLASH_STATUS1 0x3F18 ++#define NFC_FLASH_STATUS2 0x3F1c ++#define NFC_CACHE_SWAP 0x3F28 ++#define NFC_SECTOR_SIZE 0x3F2c ++#define NFC_FLASH_CONFIG 0x3F30 ++#define NFC_IRQ_STATUS 0x3F38 ++ ++/* Addresses for NFC MAIN RAM BUFFER areas */ ++#define NFC_MAIN_AREA(n) ((n) * 0x1000) ++ ++#define PAGE_2K 0x0800 ++#define OOB_64 0x0040 ++#define OOB_MAX 0x0100 ++ ++/* ++ * NFC_CMD2[CODE] values. See section: ++ * - 31.4.7 Flash Command Code Description, Vybrid manual ++ * - 23.8.6 Flash Command Sequencer, MPC5125 manual ++ * ++ * Briefly these are bitmasks of controller cycles. ++ */ ++#define READ_PAGE_CMD_CODE 0x7EE0 ++#define READ_ONFI_PARAM_CMD_CODE 0x4860 ++#define PROGRAM_PAGE_CMD_CODE 0x7FC0 ++#define ERASE_CMD_CODE 0x4EC0 ++#define READ_ID_CMD_CODE 0x4804 ++#define RESET_CMD_CODE 0x4040 ++#define STATUS_READ_CMD_CODE 0x4068 ++ ++/* NFC ECC mode define */ ++#define ECC_BYPASS 0 ++#define ECC_45_BYTE 6 ++#define ECC_60_BYTE 7 ++ ++/*** Register Mask and bit definitions */ ++ ++/* NFC_FLASH_CMD1 Field */ ++#define CMD_BYTE2_MASK 0xFF000000 ++#define CMD_BYTE2_SHIFT 24 ++ ++/* NFC_FLASH_CM2 Field */ ++#define CMD_BYTE1_MASK 0xFF000000 ++#define CMD_BYTE1_SHIFT 24 ++#define CMD_CODE_MASK 0x00FFFF00 ++#define CMD_CODE_SHIFT 8 ++#define BUFNO_MASK 0x00000006 ++#define BUFNO_SHIFT 1 ++#define START_BIT BIT(0) ++ ++/* NFC_COL_ADDR Field */ ++#define COL_ADDR_MASK 0x0000FFFF ++#define COL_ADDR_SHIFT 0 ++ ++/* NFC_ROW_ADDR Field */ ++#define ROW_ADDR_MASK 0x00FFFFFF ++#define ROW_ADDR_SHIFT 0 ++#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 ++#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 ++#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 ++#define ROW_ADDR_CHIP_SEL_SHIFT 24 ++ ++/* NFC_FLASH_STATUS2 Field */ ++#define STATUS_BYTE1_MASK 0x000000FF ++ ++/* NFC_FLASH_CONFIG Field */ ++#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 ++#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 ++#define CONFIG_ECC_SRAM_REQ_BIT BIT(21) ++#define CONFIG_DMA_REQ_BIT BIT(20) ++#define CONFIG_ECC_MODE_MASK 0x000E0000 ++#define CONFIG_ECC_MODE_SHIFT 17 ++#define CONFIG_FAST_FLASH_BIT BIT(16) ++#define CONFIG_16BIT BIT(7) ++#define CONFIG_BOOT_MODE_BIT BIT(6) ++#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5) ++#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4) ++#define CONFIG_PAGE_CNT_MASK 0xF ++#define CONFIG_PAGE_CNT_SHIFT 0 ++ ++/* NFC_IRQ_STATUS Field */ ++#define IDLE_IRQ_BIT BIT(29) ++#define IDLE_EN_BIT BIT(20) ++#define CMD_DONE_CLEAR_BIT BIT(18) ++#define IDLE_CLEAR_BIT BIT(17) ++ ++/* ++ * ECC status - seems to consume 8 bytes (double word). The documented ++ * status byte is located in the lowest byte of the second word (which is ++ * the 4th or 7th byte depending on endianness). ++ * Calculate an offset to store the ECC status at the end of the buffer. ++ */ ++#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) ++ ++#define ECC_STATUS 0x4 ++#define ECC_STATUS_MASK 0x80 ++#define ECC_STATUS_ERR_COUNT 0x3F ++ ++enum vf610_nfc_alt_buf { ++ ALT_BUF_DATA = 0, ++ ALT_BUF_ID = 1, ++ ALT_BUF_STAT = 2, ++ ALT_BUF_ONFI = 3, ++}; ++ ++enum vf610_nfc_variant { ++ NFC_VFC610 = 1, ++}; ++ ++struct vf610_nfc { ++ struct nand_chip chip; ++ struct device *dev; ++ void __iomem *regs; ++ struct completion cmd_done; ++ uint buf_offset; ++ int write_sz; ++ /* Status and ID are in alternate locations. */ ++ enum vf610_nfc_alt_buf alt_buf; ++ enum vf610_nfc_variant variant; ++ struct clk *clk; ++ bool use_hw_ecc; ++ u32 ecc_mode; ++}; ++ ++static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd) ++{ ++ return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip); ++} ++ ++static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) ++{ ++ return readl(nfc->regs + reg); ++} ++ ++static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val) ++{ ++ writel(val, nfc->regs + reg); ++} ++ ++static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits) ++{ ++ vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits); ++} ++ ++static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits) ++{ ++ vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits); ++} ++ ++static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg, ++ u32 mask, u32 shift, u32 val) ++{ ++ vf610_nfc_write(nfc, reg, ++ (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift); ++} ++ ++static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src, ++ size_t n) ++{ ++ /* ++ * Use this accessor for the internal SRAM buffers. On the ARM ++ * Freescale Vybrid SoC it's known that the driver can treat ++ * the SRAM buffer as if it's memory. Other platform might need ++ * to treat the buffers differently. ++ * ++ * For the time being, use memcpy ++ */ ++ memcpy(dst, src, n); ++} ++ ++/* Clear flags for upcoming command */ ++static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc) ++{ ++ u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS); ++ ++ tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT; ++ vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp); ++} ++ ++static void vf610_nfc_done(struct vf610_nfc *nfc) ++{ ++ unsigned long timeout = msecs_to_jiffies(100); ++ ++ /* ++ * Barrier is needed after this write. This write need ++ * to be done before reading the next register the first ++ * time. ++ * vf610_nfc_set implicates such a barrier by using writel ++ * to write to the register. ++ */ ++ vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); ++ vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT); ++ ++ if (!wait_for_completion_timeout(&nfc->cmd_done, timeout)) ++ dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n"); ++ ++ vf610_nfc_clear_status(nfc); ++} ++ ++static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col) ++{ ++ u32 flash_id; ++ ++ if (col < 4) { ++ flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1); ++ flash_id >>= (3 - col) * 8; ++ } else { ++ flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2); ++ flash_id >>= 24; ++ } ++ ++ return flash_id & 0xff; ++} ++ ++static u8 vf610_nfc_get_status(struct vf610_nfc *nfc) ++{ ++ return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; ++} ++ ++static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1, ++ u32 cmd_code) ++{ ++ u32 tmp; ++ ++ vf610_nfc_clear_status(nfc); ++ ++ tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2); ++ tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); ++ tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; ++ tmp |= cmd_code << CMD_CODE_SHIFT; ++ vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp); ++} ++ ++static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1, ++ u32 cmd_byte2, u32 cmd_code) ++{ ++ u32 tmp; ++ ++ vf610_nfc_send_command(nfc, cmd_byte1, cmd_code); ++ ++ tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1); ++ tmp &= ~CMD_BYTE2_MASK; ++ tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; ++ vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp); ++} ++ ++static irqreturn_t vf610_nfc_irq(int irq, void *data) ++{ ++ struct mtd_info *mtd = data; ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ ++ vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); ++ complete(&nfc->cmd_done); ++ ++ return IRQ_HANDLED; ++} ++ ++static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page) ++{ ++ if (column != -1) { ++ if (nfc->chip.options & NAND_BUSWIDTH_16) ++ column = column / 2; ++ vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK, ++ COL_ADDR_SHIFT, column); ++ } ++ if (page != -1) ++ vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK, ++ ROW_ADDR_SHIFT, page); ++} ++ ++static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) ++{ ++ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, ++ CONFIG_ECC_MODE_MASK, ++ CONFIG_ECC_MODE_SHIFT, ecc_mode); ++} ++ ++static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) ++{ ++ vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); ++} ++ ++static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, ++ int column, int page) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; ++ ++ nfc->buf_offset = max(column, 0); ++ nfc->alt_buf = ALT_BUF_DATA; ++ ++ switch (command) { ++ case NAND_CMD_SEQIN: ++ /* Use valid column/page from preread... */ ++ vf610_nfc_addr_cycle(nfc, column, page); ++ nfc->buf_offset = 0; ++ ++ /* ++ * SEQIN => data => PAGEPROG sequence is done by the controller ++ * hence we do not need to issue the command here... ++ */ ++ return; ++ case NAND_CMD_PAGEPROG: ++ trfr_sz += nfc->write_sz; ++ vf610_nfc_transfer_size(nfc, trfr_sz); ++ vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN, ++ command, PROGRAM_PAGE_CMD_CODE); ++ if (nfc->use_hw_ecc) ++ vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); ++ else ++ vf610_nfc_ecc_mode(nfc, ECC_BYPASS); ++ break; ++ ++ case NAND_CMD_RESET: ++ vf610_nfc_transfer_size(nfc, 0); ++ vf610_nfc_send_command(nfc, command, RESET_CMD_CODE); ++ break; ++ ++ case NAND_CMD_READOOB: ++ trfr_sz += mtd->oobsize; ++ column = mtd->writesize; ++ vf610_nfc_transfer_size(nfc, trfr_sz); ++ vf610_nfc_send_commands(nfc, NAND_CMD_READ0, ++ NAND_CMD_READSTART, READ_PAGE_CMD_CODE); ++ vf610_nfc_addr_cycle(nfc, column, page); ++ vf610_nfc_ecc_mode(nfc, ECC_BYPASS); ++ break; ++ ++ case NAND_CMD_READ0: ++ trfr_sz += mtd->writesize + mtd->oobsize; ++ vf610_nfc_transfer_size(nfc, trfr_sz); ++ vf610_nfc_send_commands(nfc, NAND_CMD_READ0, ++ NAND_CMD_READSTART, READ_PAGE_CMD_CODE); ++ vf610_nfc_addr_cycle(nfc, column, page); ++ vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); ++ break; ++ ++ case NAND_CMD_PARAM: ++ nfc->alt_buf = ALT_BUF_ONFI; ++ trfr_sz = 3 * sizeof(struct nand_onfi_params); ++ vf610_nfc_transfer_size(nfc, trfr_sz); ++ vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE); ++ vf610_nfc_addr_cycle(nfc, -1, column); ++ vf610_nfc_ecc_mode(nfc, ECC_BYPASS); ++ break; ++ ++ case NAND_CMD_ERASE1: ++ vf610_nfc_transfer_size(nfc, 0); ++ vf610_nfc_send_commands(nfc, command, ++ NAND_CMD_ERASE2, ERASE_CMD_CODE); ++ vf610_nfc_addr_cycle(nfc, column, page); ++ break; ++ ++ case NAND_CMD_READID: ++ nfc->alt_buf = ALT_BUF_ID; ++ nfc->buf_offset = 0; ++ vf610_nfc_transfer_size(nfc, 0); ++ vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE); ++ vf610_nfc_addr_cycle(nfc, -1, column); ++ break; ++ ++ case NAND_CMD_STATUS: ++ nfc->alt_buf = ALT_BUF_STAT; ++ vf610_nfc_transfer_size(nfc, 0); ++ vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE); ++ break; ++ default: ++ return; ++ } ++ ++ vf610_nfc_done(nfc); ++ ++ nfc->use_hw_ecc = false; ++ nfc->write_sz = 0; ++} ++ ++static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ uint c = nfc->buf_offset; ++ ++ /* Alternate buffers are only supported through read_byte */ ++ WARN_ON(nfc->alt_buf); ++ ++ vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); ++ ++ nfc->buf_offset += len; ++} ++ ++static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ uint c = nfc->buf_offset; ++ uint l; ++ ++ l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); ++ vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); ++ ++ nfc->write_sz += l; ++ nfc->buf_offset += l; ++} ++ ++static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ u8 tmp; ++ uint c = nfc->buf_offset; ++ ++ switch (nfc->alt_buf) { ++ case ALT_BUF_ID: ++ tmp = vf610_nfc_get_id(nfc, c); ++ break; ++ case ALT_BUF_STAT: ++ tmp = vf610_nfc_get_status(nfc); ++ break; ++#ifdef __LITTLE_ENDIAN ++ case ALT_BUF_ONFI: ++ /* Reverse byte since the controller uses big endianness */ ++ c = nfc->buf_offset ^ 0x3; ++ /* fall-through */ ++#endif ++ default: ++ tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); ++ break; ++ } ++ nfc->buf_offset++; ++ return tmp; ++} ++ ++static u16 vf610_nfc_read_word(struct mtd_info *mtd) ++{ ++ u16 tmp; ++ ++ vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); ++ return tmp; ++} ++ ++/* If not provided, upper layers apply a fixed delay. */ ++static int vf610_nfc_dev_ready(struct mtd_info *mtd) ++{ ++ /* NFC handles R/B internally; always ready. */ ++ return 1; ++} ++ ++/* ++ * This function supports Vybrid only (MPC5125 would have full RB and four CS) ++ */ ++static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR); ++ ++ /* Vybrid only (MPC5125 would have full RB and four CS) */ ++ if (nfc->variant != NFC_VFC610) ++ return; ++ ++ tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); ++ ++ if (chip >= 0) { ++ tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; ++ tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT; ++ } ++ ++ vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); ++} ++ ++/* Count the number of 0's in buff up to max_bits */ ++static inline int count_written_bits(uint8_t *buff, int size, int max_bits) ++{ ++ uint32_t *buff32 = (uint32_t *)buff; ++ int k, written_bits = 0; ++ ++ for (k = 0; k < (size / 4); k++) { ++ written_bits += hweight32(~buff32[k]); ++ if (unlikely(written_bits > max_bits)) ++ break; ++ } ++ ++ return written_bits; ++} ++ ++static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, ++ uint8_t *oob, int page) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; ++ u8 ecc_status; ++ u8 ecc_count; ++ int flips_threshold = nfc->chip.ecc.strength / 2; ++ ++ ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; ++ ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; ++ ++ if (!(ecc_status & ECC_STATUS_MASK)) ++ return ecc_count; ++ ++ /* Read OOB without ECC unit enabled */ ++ vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); ++ vf610_nfc_read_buf(mtd, oob, mtd->oobsize); ++ ++ /* ++ * On an erased page, bit count (including OOB) should be zero or ++ * at least less then half of the ECC strength. ++ */ ++ return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob, ++ mtd->oobsize, NULL, 0, ++ flips_threshold); ++} ++ ++static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page) ++{ ++ int eccsize = chip->ecc.size; ++ int stat; ++ ++ nand_read_page_op(chip, page, 0, buf, eccsize); ++ if (oob_required) ++ vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ return 0; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ return stat; ++ } ++} ++ ++static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page) ++{ ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ ++ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); ++ if (oob_required) ++ vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); ++ ++ /* Always write whole page including OOB due to HW ECC */ ++ nfc->use_hw_ecc = true; ++ nfc->write_sz = mtd->writesize + mtd->oobsize; ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static const struct of_device_id vf610_nfc_dt_ids[] = { ++ { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids); ++ ++static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc) ++{ ++ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); ++ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); ++ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); ++ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); ++ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); ++ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); ++ ++ /* Disable virtual pages, only one elementary transfer unit */ ++ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, ++ CONFIG_PAGE_CNT_SHIFT, 1); ++} ++ ++static void vf610_nfc_init_controller(struct vf610_nfc *nfc) ++{ ++ if (nfc->chip.options & NAND_BUSWIDTH_16) ++ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); ++ else ++ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); ++ ++ if (nfc->chip.ecc.mode == NAND_ECC_HW) { ++ /* Set ECC status offset in SRAM */ ++ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, ++ CONFIG_ECC_SRAM_ADDR_MASK, ++ CONFIG_ECC_SRAM_ADDR_SHIFT, ++ ECC_SRAM_ADDR >> 3); ++ ++ /* Enable ECC status in SRAM */ ++ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); ++ } ++} ++ ++static int vf610_nfc_probe(struct platform_device *pdev) ++{ ++ struct vf610_nfc *nfc; ++ struct resource *res; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ struct device_node *child; ++ const struct of_device_id *of_id; ++ int err; ++ int irq; ++ ++ nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ nfc->dev = &pdev->dev; ++ chip = &nfc->chip; ++ mtd = nand_to_mtd(chip); ++ ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = nfc->dev; ++ mtd->name = DRV_NAME; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) ++ return -EINVAL; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ nfc->regs = devm_ioremap_resource(nfc->dev, res); ++ if (IS_ERR(nfc->regs)) ++ return PTR_ERR(nfc->regs); ++ ++ nfc->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(nfc->clk)) ++ return PTR_ERR(nfc->clk); ++ ++ err = clk_prepare_enable(nfc->clk); ++ if (err) { ++ dev_err(nfc->dev, "Unable to enable clock!\n"); ++ return err; ++ } ++ ++ of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev); ++ nfc->variant = (enum vf610_nfc_variant)of_id->data; ++ ++ for_each_available_child_of_node(nfc->dev->of_node, child) { ++ if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) { ++ ++ if (nand_get_flash_node(chip)) { ++ dev_err(nfc->dev, ++ "Only one NAND chip supported!\n"); ++ err = -EINVAL; ++ goto error; ++ } ++ ++ nand_set_flash_node(chip, child); ++ } ++ } ++ ++ if (!nand_get_flash_node(chip)) { ++ dev_err(nfc->dev, "NAND chip sub-node missing!\n"); ++ err = -ENODEV; ++ goto err_clk; ++ } ++ ++ chip->dev_ready = vf610_nfc_dev_ready; ++ chip->cmdfunc = vf610_nfc_command; ++ chip->read_byte = vf610_nfc_read_byte; ++ chip->read_word = vf610_nfc_read_word; ++ chip->read_buf = vf610_nfc_read_buf; ++ chip->write_buf = vf610_nfc_write_buf; ++ chip->select_chip = vf610_nfc_select_chip; ++ chip->onfi_set_features = nand_onfi_get_set_features_notsupp; ++ chip->onfi_get_features = nand_onfi_get_set_features_notsupp; ++ ++ chip->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ init_completion(&nfc->cmd_done); ++ ++ err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); ++ if (err) { ++ dev_err(nfc->dev, "Error requesting IRQ!\n"); ++ goto error; ++ } ++ ++ vf610_nfc_preinit_controller(nfc); ++ ++ /* first scan to find the device and get the page size */ ++ err = nand_scan_ident(mtd, 1, NULL); ++ if (err) ++ goto error; ++ ++ vf610_nfc_init_controller(nfc); ++ ++ /* Bad block options. */ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) ++ chip->bbt_options |= NAND_BBT_NO_OOB; ++ ++ /* Single buffer only, max 256 OOB minus ECC status */ ++ if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { ++ dev_err(nfc->dev, "Unsupported flash page size\n"); ++ err = -ENXIO; ++ goto error; ++ } ++ ++ if (chip->ecc.mode == NAND_ECC_HW) { ++ if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { ++ dev_err(nfc->dev, "Unsupported flash with hwecc\n"); ++ err = -ENXIO; ++ goto error; ++ } ++ ++ if (chip->ecc.size != mtd->writesize) { ++ dev_err(nfc->dev, "Step size needs to be page size\n"); ++ err = -ENXIO; ++ goto error; ++ } ++ ++ /* Only 64 byte ECC layouts known */ ++ if (mtd->oobsize > 64) ++ mtd->oobsize = 64; ++ ++ /* Use default large page ECC layout defined in NAND core */ ++ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ++ if (chip->ecc.strength == 32) { ++ nfc->ecc_mode = ECC_60_BYTE; ++ chip->ecc.bytes = 60; ++ } else if (chip->ecc.strength == 24) { ++ nfc->ecc_mode = ECC_45_BYTE; ++ chip->ecc.bytes = 45; ++ } else { ++ dev_err(nfc->dev, "Unsupported ECC strength\n"); ++ err = -ENXIO; ++ goto error; ++ } ++ ++ chip->ecc.read_page = vf610_nfc_read_page; ++ chip->ecc.write_page = vf610_nfc_write_page; ++ ++ chip->ecc.size = PAGE_2K; ++ } ++ ++ /* second phase scan */ ++ err = nand_scan_tail(mtd); ++ if (err) ++ goto error; ++ ++ platform_set_drvdata(pdev, mtd); ++ ++ /* Register device in MTD */ ++ return mtd_device_register(mtd, NULL, 0); ++ ++error: ++ of_node_put(nand_get_flash_node(chip)); ++err_clk: ++ clk_disable_unprepare(nfc->clk); ++ return err; ++} ++ ++static int vf610_nfc_remove(struct platform_device *pdev) ++{ ++ struct mtd_info *mtd = platform_get_drvdata(pdev); ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ ++ nand_release(mtd); ++ clk_disable_unprepare(nfc->clk); ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int vf610_nfc_suspend(struct device *dev) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ ++ clk_disable_unprepare(nfc->clk); ++ return 0; ++} ++ ++static int vf610_nfc_resume(struct device *dev) ++{ ++ int err; ++ ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ struct vf610_nfc *nfc = mtd_to_nfc(mtd); ++ ++ err = clk_prepare_enable(nfc->clk); ++ if (err) ++ return err; ++ ++ vf610_nfc_preinit_controller(nfc); ++ vf610_nfc_init_controller(nfc); ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume); ++ ++static struct platform_driver vf610_nfc_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = vf610_nfc_dt_ids, ++ .pm = &vf610_nfc_pm_ops, ++ }, ++ .probe = vf610_nfc_probe, ++ .remove = vf610_nfc_remove, ++}; ++ ++module_platform_driver(vf610_nfc_driver); ++ ++MODULE_AUTHOR("Stefan Agner "); ++MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c +new file mode 100644 +index 00000000..9926b4e +--- /dev/null ++++ b/drivers/mtd/nand/raw/xway_nand.c +@@ -0,0 +1,245 @@ ++/* ++ * 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. ++ * ++ * Copyright © 2012 John Crispin ++ * Copyright © 2016 Hauke Mehrtens ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++/* nand registers */ ++#define EBU_ADDSEL1 0x24 ++#define EBU_NAND_CON 0xB0 ++#define EBU_NAND_WAIT 0xB4 ++#define NAND_WAIT_RD BIT(0) /* NAND flash status output */ ++#define NAND_WAIT_WR_C BIT(3) /* NAND Write/Read complete */ ++#define EBU_NAND_ECC0 0xB8 ++#define EBU_NAND_ECC_AC 0xBC ++ ++/* ++ * nand commands ++ * The pins of the NAND chip are selected based on the address bits of the ++ * "register" read and write. There are no special registers, but an ++ * address range and the lower address bits are used to activate the ++ * correct line. For example when the bit (1 << 2) is set in the address ++ * the ALE pin will be activated. ++ */ ++#define NAND_CMD_ALE BIT(2) /* address latch enable */ ++#define NAND_CMD_CLE BIT(3) /* command latch enable */ ++#define NAND_CMD_CS BIT(4) /* chip select */ ++#define NAND_CMD_SE BIT(5) /* spare area access latch */ ++#define NAND_CMD_WP BIT(6) /* write protect */ ++#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE) ++#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE) ++#define NAND_WRITE_DATA (NAND_CMD_CS) ++#define NAND_READ_DATA (NAND_CMD_CS) ++ ++/* we need to tel the ebu which addr we mapped the nand to */ ++#define ADDSEL1_MASK(x) (x << 4) ++#define ADDSEL1_REGEN 1 ++ ++/* we need to tell the EBU that we have nand attached and set it up properly */ ++#define BUSCON1_SETUP (1 << 22) ++#define BUSCON1_BCGEN_RES (0x3 << 12) ++#define BUSCON1_WAITWRC2 (2 << 8) ++#define BUSCON1_WAITRDC2 (2 << 6) ++#define BUSCON1_HOLDC1 (1 << 4) ++#define BUSCON1_RECOVC1 (1 << 2) ++#define BUSCON1_CMULT4 1 ++ ++#define NAND_CON_CE (1 << 20) ++#define NAND_CON_OUT_CS1 (1 << 10) ++#define NAND_CON_IN_CS1 (1 << 8) ++#define NAND_CON_PRE_P (1 << 7) ++#define NAND_CON_WP_P (1 << 6) ++#define NAND_CON_SE_P (1 << 5) ++#define NAND_CON_CS_P (1 << 4) ++#define NAND_CON_CSMUX (1 << 1) ++#define NAND_CON_NANDM 1 ++ ++struct xway_nand_data { ++ struct nand_chip chip; ++ unsigned long csflags; ++ void __iomem *nandaddr; ++}; ++ ++static u8 xway_readb(struct mtd_info *mtd, int op) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct xway_nand_data *data = nand_get_controller_data(chip); ++ ++ return readb(data->nandaddr + op); ++} ++ ++static void xway_writeb(struct mtd_info *mtd, int op, u8 value) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct xway_nand_data *data = nand_get_controller_data(chip); ++ ++ writeb(value, data->nandaddr + op); ++} ++ ++static void xway_select_chip(struct mtd_info *mtd, int select) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct xway_nand_data *data = nand_get_controller_data(chip); ++ ++ switch (select) { ++ case -1: ++ ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON); ++ ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON); ++ spin_unlock_irqrestore(&ebu_lock, data->csflags); ++ break; ++ case 0: ++ spin_lock_irqsave(&ebu_lock, data->csflags); ++ ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON); ++ ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ if (cmd == NAND_CMD_NONE) ++ return; ++ ++ if (ctrl & NAND_CLE) ++ xway_writeb(mtd, NAND_WRITE_CMD, cmd); ++ else if (ctrl & NAND_ALE) ++ xway_writeb(mtd, NAND_WRITE_ADDR, cmd); ++ ++ while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) ++ ; ++} ++ ++static int xway_dev_ready(struct mtd_info *mtd) ++{ ++ return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD; ++} ++ ++static unsigned char xway_read_byte(struct mtd_info *mtd) ++{ ++ return xway_readb(mtd, NAND_READ_DATA); ++} ++ ++static void xway_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ buf[i] = xway_readb(mtd, NAND_WRITE_DATA); ++} ++ ++static void xway_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ xway_writeb(mtd, NAND_WRITE_DATA, buf[i]); ++} ++ ++/* ++ * Probe for the NAND device. ++ */ ++static int xway_nand_probe(struct platform_device *pdev) ++{ ++ struct xway_nand_data *data; ++ struct mtd_info *mtd; ++ struct resource *res; ++ int err; ++ u32 cs; ++ u32 cs_flag = 0; ++ ++ /* Allocate memory for the device structure (and zero it) */ ++ data = devm_kzalloc(&pdev->dev, sizeof(struct xway_nand_data), ++ GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ data->nandaddr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(data->nandaddr)) ++ return PTR_ERR(data->nandaddr); ++ ++ nand_set_flash_node(&data->chip, pdev->dev.of_node); ++ mtd = nand_to_mtd(&data->chip); ++ mtd->dev.parent = &pdev->dev; ++ ++ data->chip.cmd_ctrl = xway_cmd_ctrl; ++ data->chip.dev_ready = xway_dev_ready; ++ data->chip.select_chip = xway_select_chip; ++ data->chip.write_buf = xway_write_buf; ++ data->chip.read_buf = xway_read_buf; ++ data->chip.read_byte = xway_read_byte; ++ data->chip.chip_delay = 30; ++ ++ data->chip.ecc.mode = NAND_ECC_SOFT; ++ data->chip.ecc.algo = NAND_ECC_HAMMING; ++ ++ platform_set_drvdata(pdev, data); ++ nand_set_controller_data(&data->chip, data); ++ ++ /* load our CS from the DT. Either we find a valid 1 or default to 0 */ ++ err = of_property_read_u32(pdev->dev.of_node, "lantiq,cs", &cs); ++ if (!err && cs == 1) ++ cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1; ++ ++ /* setup the EBU to run in NAND mode on our base addr */ ++ ltq_ebu_w32(CPHYSADDR(data->nandaddr) ++ | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1); ++ ++ ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2 ++ | BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1 ++ | BUSCON1_CMULT4, LTQ_EBU_BUSCON1); ++ ++ ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P ++ | NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P ++ | cs_flag, EBU_NAND_CON); ++ ++ /* Scan to find existence of the device */ ++ err = nand_scan(mtd, 1); ++ if (err) ++ return err; ++ ++ err = mtd_device_register(mtd, NULL, 0); ++ if (err) ++ nand_release(mtd); ++ ++ return err; ++} ++ ++/* ++ * Remove a NAND device. ++ */ ++static int xway_nand_remove(struct platform_device *pdev) ++{ ++ struct xway_nand_data *data = platform_get_drvdata(pdev); ++ ++ nand_release(nand_to_mtd(&data->chip)); ++ ++ return 0; ++} ++ ++static const struct of_device_id xway_nand_match[] = { ++ { .compatible = "lantiq,nand-xway" }, ++ {}, ++}; ++ ++static struct platform_driver xway_nand_driver = { ++ .probe = xway_nand_probe, ++ .remove = xway_nand_remove, ++ .driver = { ++ .name = "lantiq,nand-xway", ++ .of_match_table = xway_nand_match, ++ }, ++}; ++ ++builtin_platform_driver(xway_nand_driver); +diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +index 2e3c940..fd886e4 100644 +--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h ++++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +@@ -237,7 +237,6 @@ struct snd_queue { + u64 *skbuff; + void *desc; + +-#define TSO_HEADER_SIZE 128 + /* For TSO segment's header */ + char *tso_hdrs; + dma_addr_t tso_hdrs_phys; +diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c +index 051ecc7..260c626 100644 +--- a/drivers/net/ethernet/freescale/fec_main.c ++++ b/drivers/net/ethernet/freescale/fec_main.c +@@ -222,7 +222,6 @@ enum imx_fec_type { + + #define COPYBREAK_DEFAULT 256 + +-#define TSO_HEADER_SIZE 128 + /* Max number of allowed TCP segments for software TSO */ + #define FEC_MAX_TSO_SEGS 100 + #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) +diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig +index 2664827..79fe2ac 100644 +--- a/drivers/net/ethernet/marvell/Kconfig ++++ b/drivers/net/ethernet/marvell/Kconfig +@@ -5,7 +5,7 @@ + config NET_VENDOR_MARVELL + bool "Marvell devices" + default y +- depends on PCI || CPU_PXA168 || MV64X60 || PPC32 || PLAT_ORION || INET ++ depends on PCI || CPU_PXA168 || MV64X60 || PPC32 || PLAT_ORION || INET || COMPILE_TEST + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y. + +@@ -18,7 +18,8 @@ if NET_VENDOR_MARVELL + + config MV643XX_ETH + tristate "Marvell Discovery (643XX) and Orion ethernet support" +- depends on (MV64X60 || PPC32 || PLAT_ORION) && INET ++ depends on (MV64X60 || PPC32 || PLAT_ORION || COMPILE_TEST) && INET ++ depends on HAS_DMA + select PHYLIB + select MVMDIO + ---help--- +@@ -55,7 +56,9 @@ config MVNETA_BM_ENABLE + + config MVNETA + tristate "Marvell Armada 370/38x/XP network interface support" +- depends on PLAT_ORION ++ depends on PLAT_ORION || COMPILE_TEST ++ depends on HAS_DMA ++ depends on !64BIT + select MVMDIO + select FIXED_PHY + ---help--- +@@ -76,12 +79,21 @@ config MVNETA_BM + that all dependencies are met. + + config MVPP2 +- tristate "Marvell Armada 375 network interface support" +- depends on MACH_ARMADA_375 ++ tristate "Marvell Armada 375/7K/8K network interface support" ++ depends on ARCH_MVEBU || COMPILE_TEST ++ depends on HAS_DMA + select MVMDIO + ---help--- + This driver supports the network interface units in the +- Marvell ARMADA 375 SoC. ++ Marvell ARMADA 375, 7K and 8K SoCs. ++ ++config MVPP2X ++ tristate "Marvell Armada 375/70xx/80xx network interface support" ++ depends on ARCH_MVEBU ++ select MVMDIO ++ ---help--- ++ This driver supports the network interface units in the ++ Marvell ARMADA 375/70xx/80xx SoC series + + config PXA168_ETH + tristate "Marvell pxa168 ethernet support" +diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile +index ff1bffa..b8317e3a 100644 +--- a/drivers/net/ethernet/marvell/Makefile ++++ b/drivers/net/ethernet/marvell/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_MVPP2) += mvpp2.o + obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o + obj-$(CONFIG_SKGE) += skge.o + obj-$(CONFIG_SKY2) += sky2.o ++obj-$(CONFIG_MVPP2X) += mvpp2x/ +diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c +index 526d07e..b98b051 100644 +--- a/drivers/net/ethernet/marvell/mv643xx_eth.c ++++ b/drivers/net/ethernet/marvell/mv643xx_eth.c +@@ -183,8 +183,6 @@ + #define DEFAULT_TX_QUEUE_SIZE 512 + #define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES) + +-#define TSO_HEADER_SIZE 128 +- + /* Max number of allowed TCP segments for software TSO */ + #define MV643XX_MAX_TSO_SEGS 100 + #define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index d98b874..69f59b5 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -280,9 +280,6 @@ + */ + #define MVNETA_RSS_LU_TABLE_SIZE 1 + +-/* TSO header size */ +-#define TSO_HEADER_SIZE 128 +- + /* Max number of Rx descriptors */ + #define MVNETA_MAX_RXD 128 + +diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c +index ff62dc7..b4e5d69 100644 +--- a/drivers/net/ethernet/marvell/mvpp2.c ++++ b/drivers/net/ethernet/marvell/mvpp2.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -25,20 +26,25 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +-#include ++#include + #include + #include + #include ++#include + +-/* RX Fifo Registers */ ++/* Fifo Registers */ + #define MVPP2_RX_DATA_FIFO_SIZE_REG(port) (0x00 + 4 * (port)) + #define MVPP2_RX_ATTR_FIFO_SIZE_REG(port) (0x20 + 4 * (port)) + #define MVPP2_RX_MIN_PKT_SIZE_REG 0x60 + #define MVPP2_RX_FIFO_INIT_REG 0x64 ++#define MVPP22_TX_FIFO_THRESH_REG(port) (0x8840 + 4 * (port)) ++#define MVPP22_TX_FIFO_SIZE_REG(port) (0x8860 + 4 * (port)) + + /* RX DMA Top Registers */ + #define MVPP2_RX_CTRL_REG(port) (0x140 + 4 * (port)) +@@ -50,13 +56,19 @@ + #define MVPP2_SNOOP_PKT_SIZE_MASK 0x1ff + #define MVPP2_SNOOP_BUF_HDR_MASK BIT(9) + #define MVPP2_RXQ_POOL_SHORT_OFFS 20 +-#define MVPP2_RXQ_POOL_SHORT_MASK 0x700000 ++#define MVPP21_RXQ_POOL_SHORT_MASK 0x700000 ++#define MVPP22_RXQ_POOL_SHORT_MASK 0xf00000 + #define MVPP2_RXQ_POOL_LONG_OFFS 24 +-#define MVPP2_RXQ_POOL_LONG_MASK 0x7000000 ++#define MVPP21_RXQ_POOL_LONG_MASK 0x7000000 ++#define MVPP22_RXQ_POOL_LONG_MASK 0xf000000 + #define MVPP2_RXQ_PACKET_OFFSET_OFFS 28 + #define MVPP2_RXQ_PACKET_OFFSET_MASK 0x70000000 + #define MVPP2_RXQ_DISABLE_MASK BIT(31) + ++/* Top Registers */ ++#define MVPP2_MH_REG(port) (0x5040 + 4 * (port)) ++#define MVPP2_DSA_EXTENDED BIT(5) ++ + /* Parser Registers */ + #define MVPP2_PRS_INIT_LOOKUP_REG 0x1000 + #define MVPP2_PRS_PORT_LU_MAX 0xf +@@ -76,6 +88,16 @@ + #define MVPP2_PRS_TCAM_CTRL_REG 0x1230 + #define MVPP2_PRS_TCAM_EN_MASK BIT(0) + ++/* RSS Registers */ ++#define MVPP22_RSS_INDEX 0x1500 ++#define MVPP22_RSS_INDEX_TABLE_ENTRY(idx) (idx) ++#define MVPP22_RSS_INDEX_TABLE(idx) ((idx) << 8) ++#define MVPP22_RSS_INDEX_QUEUE(idx) ((idx) << 16) ++#define MVPP22_RSS_TABLE_ENTRY 0x1508 ++#define MVPP22_RSS_TABLE 0x1510 ++#define MVPP22_RSS_TABLE_POINTER(p) (p) ++#define MVPP22_RSS_WIDTH 0x150c ++ + /* Classifier Registers */ + #define MVPP2_CLS_MODE_REG 0x1800 + #define MVPP2_CLS_MODE_ACTIVE_MASK BIT(0) +@@ -100,6 +122,7 @@ + /* Descriptor Manager Top Registers */ + #define MVPP2_RXQ_NUM_REG 0x2040 + #define MVPP2_RXQ_DESC_ADDR_REG 0x2044 ++#define MVPP22_DESC_ADDR_OFFS 8 + #define MVPP2_RXQ_DESC_SIZE_REG 0x2048 + #define MVPP2_RXQ_DESC_SIZE_MASK 0x3ff0 + #define MVPP2_RXQ_STATUS_UPDATE_REG(rxq) (0x3000 + 4 * (rxq)) +@@ -117,10 +140,10 @@ + #define MVPP2_TXQ_DESC_ADDR_REG 0x2084 + #define MVPP2_TXQ_DESC_SIZE_REG 0x2088 + #define MVPP2_TXQ_DESC_SIZE_MASK 0x3ff0 +-#define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 + #define MVPP2_TXQ_THRESH_REG 0x2094 +-#define MVPP2_TRANSMITTED_THRESH_OFFSET 16 +-#define MVPP2_TRANSMITTED_THRESH_MASK 0x3fff0000 ++#define MVPP2_TXQ_THRESH_OFFSET 16 ++#define MVPP2_TXQ_THRESH_MASK 0x3fff ++#define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 + #define MVPP2_TXQ_INDEX_REG 0x2098 + #define MVPP2_TXQ_PREF_BUF_REG 0x209c + #define MVPP2_PREF_BUF_PTR(desc) ((desc) & 0xfff) +@@ -141,6 +164,7 @@ + #define MVPP2_TXQ_RSVD_CLR_REG 0x20b8 + #define MVPP2_TXQ_RSVD_CLR_OFFSET 16 + #define MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu) (0x2100 + 4 * (cpu)) ++#define MVPP22_AGGR_TXQ_DESC_ADDR_OFFS 8 + #define MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu) (0x2140 + 4 * (cpu)) + #define MVPP2_AGGR_TXQ_DESC_SIZE_MASK 0x3ff0 + #define MVPP2_AGGR_TXQ_STATUS_REG(cpu) (0x2180 + 4 * (cpu)) +@@ -153,15 +177,62 @@ + #define MVPP2_WIN_REMAP(w) (0x4040 + ((w) << 2)) + #define MVPP2_BASE_ADDR_ENABLE 0x4060 + ++/* AXI Bridge Registers */ ++#define MVPP22_AXI_BM_WR_ATTR_REG 0x4100 ++#define MVPP22_AXI_BM_RD_ATTR_REG 0x4104 ++#define MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG 0x4110 ++#define MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG 0x4114 ++#define MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG 0x4118 ++#define MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG 0x411c ++#define MVPP22_AXI_RX_DATA_WR_ATTR_REG 0x4120 ++#define MVPP22_AXI_TX_DATA_RD_ATTR_REG 0x4130 ++#define MVPP22_AXI_RD_NORMAL_CODE_REG 0x4150 ++#define MVPP22_AXI_RD_SNOOP_CODE_REG 0x4154 ++#define MVPP22_AXI_WR_NORMAL_CODE_REG 0x4160 ++#define MVPP22_AXI_WR_SNOOP_CODE_REG 0x4164 ++ ++/* Values for AXI Bridge registers */ ++#define MVPP22_AXI_ATTR_CACHE_OFFS 0 ++#define MVPP22_AXI_ATTR_DOMAIN_OFFS 12 ++ ++#define MVPP22_AXI_CODE_CACHE_OFFS 0 ++#define MVPP22_AXI_CODE_DOMAIN_OFFS 4 ++ ++#define MVPP22_AXI_CODE_CACHE_NON_CACHE 0x3 ++#define MVPP22_AXI_CODE_CACHE_WR_CACHE 0x7 ++#define MVPP22_AXI_CODE_CACHE_RD_CACHE 0xb ++ ++#define MVPP22_AXI_CODE_DOMAIN_OUTER_DOM 2 ++#define MVPP22_AXI_CODE_DOMAIN_SYSTEM 3 ++ + /* Interrupt Cause and Mask registers */ ++#define MVPP2_ISR_TX_THRESHOLD_REG(port) (0x5140 + 4 * (port)) ++#define MVPP2_MAX_ISR_TX_THRESHOLD 0xfffff0 ++ + #define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) +-#define MVPP2_ISR_RXQ_GROUP_REG(rxq) (0x5400 + 4 * (rxq)) ++#define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 ++#define MVPP21_ISR_RXQ_GROUP_REG(port) (0x5400 + 4 * (port)) ++ ++#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 ++#define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf ++#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 ++#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 ++ ++#define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf ++#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 ++ ++#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 ++#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f ++#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 ++#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 ++ + #define MVPP2_ISR_ENABLE_REG(port) (0x5420 + 4 * (port)) + #define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff) + #define MVPP2_ISR_DISABLE_INTERRUPT(mask) (((mask) << 16) & 0xffff0000) + #define MVPP2_ISR_RX_TX_CAUSE_REG(port) (0x5480 + 4 * (port)) + #define MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff + #define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK 0xff0000 ++#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET 16 + #define MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK BIT(24) + #define MVPP2_CAUSE_FCS_ERR_MASK BIT(25) + #define MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK BIT(26) +@@ -187,6 +258,7 @@ + #define MVPP2_BM_BPPI_READ_PTR_REG(pool) (0x6100 + ((pool) * 4)) + #define MVPP2_BM_BPPI_PTRS_NUM_REG(pool) (0x6140 + ((pool) * 4)) + #define MVPP2_BM_BPPI_PTR_NUM_MASK 0x7ff ++#define MVPP22_BM_POOL_PTRS_NUM_MASK 0xfff8 + #define MVPP2_BM_BPPI_PREFETCH_FULL_MASK BIT(16) + #define MVPP2_BM_POOL_CTRL_REG(pool) (0x6200 + ((pool) * 4)) + #define MVPP2_BM_START_MASK BIT(0) +@@ -210,14 +282,19 @@ + #define MVPP2_BM_PHY_ALLOC_REG(pool) (0x6400 + ((pool) * 4)) + #define MVPP2_BM_PHY_ALLOC_GRNTD_MASK BIT(0) + #define MVPP2_BM_VIRT_ALLOC_REG 0x6440 ++#define MVPP22_BM_ADDR_HIGH_ALLOC 0x6444 ++#define MVPP22_BM_ADDR_HIGH_PHYS_MASK 0xff ++#define MVPP22_BM_ADDR_HIGH_VIRT_MASK 0xff00 ++#define MVPP22_BM_ADDR_HIGH_VIRT_SHIFT 8 + #define MVPP2_BM_PHY_RLS_REG(pool) (0x6480 + ((pool) * 4)) + #define MVPP2_BM_PHY_RLS_MC_BUFF_MASK BIT(0) + #define MVPP2_BM_PHY_RLS_PRIO_EN_MASK BIT(1) + #define MVPP2_BM_PHY_RLS_GRNTD_MASK BIT(2) + #define MVPP2_BM_VIRT_RLS_REG 0x64c0 +-#define MVPP2_BM_MC_RLS_REG 0x64c4 +-#define MVPP2_BM_MC_ID_MASK 0xfff +-#define MVPP2_BM_FORCE_RELEASE_MASK BIT(12) ++#define MVPP22_BM_ADDR_HIGH_RLS_REG 0x64c4 ++#define MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK 0xff ++#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK 0xff00 ++#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT 8 + + /* TX Scheduler registers */ + #define MVPP2_TXP_SCHED_PORT_INDEX_REG 0x8000 +@@ -253,44 +330,94 @@ + #define MVPP2_SRC_ADDR_HIGH 0x28 + #define MVPP2_PHY_AN_CFG0_REG 0x34 + #define MVPP2_PHY_AN_STOP_SMI0_MASK BIT(7) +-#define MVPP2_MIB_COUNTERS_BASE(port) (0x1000 + ((port) >> 1) * \ +- 0x400 + (port) * 0x400) +-#define MVPP2_MIB_LATE_COLLISION 0x7c +-#define MVPP2_ISR_SUM_MASK_REG 0x220c + #define MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG 0x305c +-#define MVPP2_EXT_GLOBAL_CTRL_DEFAULT 0x27 ++#define MVPP2_EXT_GLOBAL_CTRL_DEFAULT 0x27 + + /* Per-port registers */ + #define MVPP2_GMAC_CTRL_0_REG 0x0 +-#define MVPP2_GMAC_PORT_EN_MASK BIT(0) +-#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 +-#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc +-#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) ++#define MVPP2_GMAC_PORT_EN_MASK BIT(0) ++#define MVPP2_GMAC_PORT_TYPE_MASK BIT(1) ++#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 ++#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc ++#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) + #define MVPP2_GMAC_CTRL_1_REG 0x4 +-#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) +-#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) +-#define MVPP2_GMAC_PCS_LB_EN_BIT 6 +-#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) +-#define MVPP2_GMAC_SA_LOW_OFFS 7 ++#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) ++#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) ++#define MVPP2_GMAC_PCS_LB_EN_BIT 6 ++#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) ++#define MVPP2_GMAC_SA_LOW_OFFS 7 + #define MVPP2_GMAC_CTRL_2_REG 0x8 +-#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) +-#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) +-#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) +-#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) ++#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) ++#define MVPP2_GMAC_FLOW_CTRL_MASK GENMASK(2, 1) ++#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) ++#define MVPP2_GMAC_INTERNAL_CLK_MASK BIT(4) ++#define MVPP2_GMAC_DISABLE_PADDING BIT(5) ++#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) + #define MVPP2_GMAC_AUTONEG_CONFIG 0xc +-#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) +-#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) +-#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) +-#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) +-#define MVPP2_GMAC_AN_SPEED_EN BIT(7) +-#define MVPP2_GMAC_FC_ADV_EN BIT(9) +-#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) +-#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) ++#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) ++#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) ++#define MVPP2_GMAC_IN_BAND_AUTONEG BIT(2) ++#define MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS BIT(3) ++#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) ++#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) ++#define MVPP2_GMAC_AN_SPEED_EN BIT(7) ++#define MVPP2_GMAC_FC_ADV_EN BIT(9) ++#define MVPP2_GMAC_FLOW_CTRL_AUTONEG BIT(11) ++#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) ++#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) ++#define MVPP2_GMAC_STATUS0 0x10 ++#define MVPP2_GMAC_STATUS0_LINK_UP BIT(0) + #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c +-#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 +-#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 +-#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ ++#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 ++#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 ++#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ + MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) ++#define MVPP22_GMAC_INT_STAT 0x20 ++#define MVPP22_GMAC_INT_STAT_LINK BIT(1) ++#define MVPP22_GMAC_INT_MASK 0x24 ++#define MVPP22_GMAC_INT_MASK_LINK_STAT BIT(1) ++#define MVPP22_GMAC_CTRL_4_REG 0x90 ++#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) ++#define MVPP22_CTRL4_DP_CLK_SEL BIT(5) ++#define MVPP22_CTRL4_SYNC_BYPASS_DIS BIT(6) ++#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) ++#define MVPP22_GMAC_INT_SUM_MASK 0xa4 ++#define MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1) ++ ++/* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0, ++ * relative to port->base. ++ */ ++#define MVPP22_XLG_CTRL0_REG 0x100 ++#define MVPP22_XLG_CTRL0_PORT_EN BIT(0) ++#define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) ++#define MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN BIT(7) ++#define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) ++#define MVPP22_XLG_CTRL1_REG 0x104 ++#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS 0 ++#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK 0x1fff ++#define MVPP22_XLG_STATUS 0x10c ++#define MVPP22_XLG_STATUS_LINK_UP BIT(0) ++#define MVPP22_XLG_INT_STAT 0x114 ++#define MVPP22_XLG_INT_STAT_LINK BIT(1) ++#define MVPP22_XLG_INT_MASK 0x118 ++#define MVPP22_XLG_INT_MASK_LINK BIT(1) ++#define MVPP22_XLG_CTRL3_REG 0x11c ++#define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) ++#define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) ++#define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) ++#define MVPP22_XLG_EXT_INT_MASK 0x15c ++#define MVPP22_XLG_EXT_INT_MASK_XLG BIT(1) ++#define MVPP22_XLG_EXT_INT_MASK_GIG BIT(2) ++#define MVPP22_XLG_CTRL4_REG 0x184 ++#define MVPP22_XLG_CTRL4_FWD_FC BIT(5) ++#define MVPP22_XLG_CTRL4_FWD_PFC BIT(6) ++#define MVPP22_XLG_CTRL4_MACMODSELECT_GMAC BIT(12) ++ ++/* SMI registers. PPv2.2 only, relative to priv->iface_base. */ ++#define MVPP22_SMI_MISC_CFG_REG 0x1204 ++#define MVPP22_SMI_POLLING_EN BIT(10) ++ ++#define MVPP22_GMAC_BASE(port) (0x7000 + (port) * 0x1000 + 0xe00) + + #define MVPP2_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff + +@@ -298,13 +425,46 @@ + #define MVPP2_QUEUE_NEXT_DESC(q, index) \ + (((index) < (q)->last_desc) ? ((index) + 1) : 0) + ++/* XPCS registers. PPv2.2 only */ ++#define MVPP22_MPCS_BASE(port) (0x7000 + (port) * 0x1000) ++#define MVPP22_MPCS_CTRL 0x14 ++#define MVPP22_MPCS_CTRL_FWD_ERR_CONN BIT(10) ++#define MVPP22_MPCS_CLK_RESET 0x14c ++#define MAC_CLK_RESET_SD_TX BIT(0) ++#define MAC_CLK_RESET_SD_RX BIT(1) ++#define MAC_CLK_RESET_MAC BIT(2) ++#define MVPP22_MPCS_CLK_RESET_DIV_RATIO(n) ((n) << 4) ++#define MVPP22_MPCS_CLK_RESET_DIV_SET BIT(11) ++ ++/* XPCS registers. PPv2.2 only */ ++#define MVPP22_XPCS_BASE(port) (0x7400 + (port) * 0x1000) ++#define MVPP22_XPCS_CFG0 0x0 ++#define MVPP22_XPCS_CFG0_PCS_MODE(n) ((n) << 3) ++#define MVPP22_XPCS_CFG0_ACTIVE_LANE(n) ((n) << 5) ++ ++/* System controller registers. Accessed through a regmap. */ ++#define GENCONF_SOFT_RESET1 0x1108 ++#define GENCONF_SOFT_RESET1_GOP BIT(6) ++#define GENCONF_PORT_CTRL0 0x1110 ++#define GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT BIT(1) ++#define GENCONF_PORT_CTRL0_RX_DATA_SAMPLE BIT(29) ++#define GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR BIT(31) ++#define GENCONF_PORT_CTRL1 0x1114 ++#define GENCONF_PORT_CTRL1_EN(p) BIT(p) ++#define GENCONF_PORT_CTRL1_RESET(p) (BIT(p) << 28) ++#define GENCONF_CTRL0 0x1120 ++#define GENCONF_CTRL0_PORT0_RGMII BIT(0) ++#define GENCONF_CTRL0_PORT1_RGMII_MII BIT(1) ++#define GENCONF_CTRL0_PORT1_RGMII BIT(2) ++ + /* Various constants */ + + /* Coalescing */ +-#define MVPP2_TXDONE_COAL_PKTS_THRESH 15 ++#define MVPP2_TXDONE_COAL_PKTS_THRESH 64 + #define MVPP2_TXDONE_HRTIMER_PERIOD_NS 1000000UL ++#define MVPP2_TXDONE_COAL_USEC 1000 + #define MVPP2_RX_COAL_PKTS 32 +-#define MVPP2_RX_COAL_USEC 100 ++#define MVPP2_RX_COAL_USEC 64 + + /* The two bytes Marvell header. Either contains a special value used + * by Marvell switches when a specific hardware mode is enabled (not +@@ -318,6 +478,7 @@ + #define MVPP2_ETH_TYPE_LEN 2 + #define MVPP2_PPPOE_HDR_SIZE 8 + #define MVPP2_VLAN_TAG_LEN 4 ++#define MVPP2_VLAN_TAG_EDSA_LEN 8 + + /* Lbtd 802.3 type */ + #define MVPP2_IP_LBDT_TYPE 0xfffa +@@ -339,20 +500,23 @@ + /* Maximum number of TXQs used by single port */ + #define MVPP2_MAX_TXQ 8 + +-/* Maximum number of RXQs used by single port */ +-#define MVPP2_MAX_RXQ 8 ++/* MVPP2_MAX_TSO_SEGS is the maximum number of fragments to allow in the GSO ++ * skb. As we need a maxium of two descriptors per fragments (1 header, 1 data), ++ * multiply this value by two to count the maximum number of skb descs needed. ++ */ ++#define MVPP2_MAX_TSO_SEGS 300 ++#define MVPP2_MAX_SKB_DESCS (MVPP2_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) + + /* Dfault number of RXQs in use */ + #define MVPP2_DEFAULT_RXQ 4 + +-/* Total number of RXQs available to all ports */ +-#define MVPP2_RXQ_TOTAL_NUM (MVPP2_MAX_PORTS * MVPP2_MAX_RXQ) +- + /* Max number of Rx descriptors */ +-#define MVPP2_MAX_RXD 128 ++#define MVPP2_MAX_RXD_MAX 1024 ++#define MVPP2_MAX_RXD_DFLT 128 + + /* Max number of Tx descriptors */ +-#define MVPP2_MAX_TXD 1024 ++#define MVPP2_MAX_TXD_MAX 2048 ++#define MVPP2_MAX_TXD_DFLT 1024 + + /* Amount of Tx descriptors that can be reserved at once by CPU */ + #define MVPP2_CPU_DESC_CHUNK 64 +@@ -367,9 +531,22 @@ + #define MVPP2_TX_DESC_ALIGN (MVPP2_DESC_ALIGNED_SIZE - 1) + + /* RX FIFO constants */ +-#define MVPP2_RX_FIFO_PORT_DATA_SIZE 0x2000 +-#define MVPP2_RX_FIFO_PORT_ATTR_SIZE 0x80 +-#define MVPP2_RX_FIFO_PORT_MIN_PKT 0x80 ++#define MVPP2_RX_FIFO_PORT_DATA_SIZE_32KB 0x8000 ++#define MVPP2_RX_FIFO_PORT_DATA_SIZE_8KB 0x2000 ++#define MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB 0x1000 ++#define MVPP2_RX_FIFO_PORT_ATTR_SIZE_32KB 0x200 ++#define MVPP2_RX_FIFO_PORT_ATTR_SIZE_8KB 0x80 ++#define MVPP2_RX_FIFO_PORT_ATTR_SIZE_4KB 0x40 ++#define MVPP2_RX_FIFO_PORT_MIN_PKT 0x80 ++ ++/* TX FIFO constants */ ++#define MVPP22_TX_FIFO_DATA_SIZE_10KB 0xa ++#define MVPP22_TX_FIFO_DATA_SIZE_3KB 0x3 ++#define MVPP2_TX_FIFO_THRESHOLD_MIN 256 ++#define MVPP2_TX_FIFO_THRESHOLD_10KB \ ++ (MVPP22_TX_FIFO_DATA_SIZE_10KB * 1024 - MVPP2_TX_FIFO_THRESHOLD_MIN) ++#define MVPP2_TX_FIFO_THRESHOLD_3KB \ ++ (MVPP22_TX_FIFO_DATA_SIZE_3KB * 1024 - MVPP2_TX_FIFO_THRESHOLD_MIN) + + /* RX buffer constants */ + #define MVPP2_SKB_SHINFO_SIZE \ +@@ -423,6 +600,9 @@ enum mvpp2_tag_type { + #define MVPP2_PRS_TCAM_PROTO_MASK 0xff + #define MVPP2_PRS_TCAM_PROTO_MASK_L 0x3f + #define MVPP2_PRS_DBL_VLANS_MAX 100 ++#define MVPP2_PRS_CAST_MASK BIT(0) ++#define MVPP2_PRS_MCAST_VAL BIT(0) ++#define MVPP2_PRS_UCAST_VAL 0x0 + + /* Tcam structure: + * - lookup ID - 4 bits +@@ -443,35 +623,81 @@ enum mvpp2_tag_type { + #define MVPP2_PRS_TCAM_LU_BYTE 20 + #define MVPP2_PRS_TCAM_EN_OFFS(offs) ((offs) + 2) + #define MVPP2_PRS_TCAM_INV_WORD 5 ++ ++#define MVPP2_PRS_VID_TCAM_BYTE 2 ++ ++/* TCAM range for unicast and multicast filtering. We have 25 entries per port, ++ * with 4 dedicated to UC filtering and the rest to multicast filtering. ++ * Additionnally we reserve one entry for the broadcast address, and one for ++ * each port's own address. ++ */ ++#define MVPP2_PRS_MAC_UC_MC_FILT_MAX 25 ++#define MVPP2_PRS_MAC_RANGE_SIZE 80 ++ ++/* Number of entries per port dedicated to UC and MC filtering */ ++#define MVPP2_PRS_MAC_UC_FILT_MAX 4 ++#define MVPP2_PRS_MAC_MC_FILT_MAX (MVPP2_PRS_MAC_UC_MC_FILT_MAX - \ ++ MVPP2_PRS_MAC_UC_FILT_MAX) ++ ++/* There is a TCAM range reserved for VLAN filtering entries, range size is 33 ++ * 10 VLAN ID filter entries per port ++ * 1 default VLAN filter entry per port ++ * It is assumed that there are 3 ports for filter, not including loopback port ++ */ ++#define MVPP2_PRS_VLAN_FILT_MAX 11 ++#define MVPP2_PRS_VLAN_FILT_RANGE_SIZE 33 ++ ++#define MVPP2_PRS_VLAN_FILT_MAX_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 2) ++#define MVPP2_PRS_VLAN_FILT_DFLT_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 1) ++ + /* Tcam entries ID */ + #define MVPP2_PE_DROP_ALL 0 + #define MVPP2_PE_FIRST_FREE_TID 1 +-#define MVPP2_PE_LAST_FREE_TID (MVPP2_PRS_TCAM_SRAM_SIZE - 31) ++ ++/* MAC filtering range */ ++#define MVPP2_PE_MAC_RANGE_END (MVPP2_PE_VID_FILT_RANGE_START - 1) ++#define MVPP2_PE_MAC_RANGE_START (MVPP2_PE_MAC_RANGE_END - \ ++ MVPP2_PRS_MAC_RANGE_SIZE + 1) ++/* VLAN filtering range */ ++#define MVPP2_PE_VID_FILT_RANGE_END (MVPP2_PRS_TCAM_SRAM_SIZE - 31) ++#define MVPP2_PE_VID_FILT_RANGE_START (MVPP2_PE_VID_FILT_RANGE_END - \ ++ MVPP2_PRS_VLAN_FILT_RANGE_SIZE + 1) ++#define MVPP2_PE_LAST_FREE_TID (MVPP2_PE_MAC_RANGE_START - 1) + #define MVPP2_PE_IP6_EXT_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 30) +-#define MVPP2_PE_MAC_MC_IP6 (MVPP2_PRS_TCAM_SRAM_SIZE - 29) +-#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28) +-#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 27) +-#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 26) +-#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 19) +-#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18) +-#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17) +-#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16) +-#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15) +-#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14) +-#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 13) +-#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 12) +-#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 11) +-#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 10) +-#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 9) +-#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 8) +-#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 7) +-#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 6) +-#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 5) +-#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 4) +-#define MVPP2_PE_MAC_MC_ALL (MVPP2_PRS_TCAM_SRAM_SIZE - 3) +-#define MVPP2_PE_MAC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2) ++#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 29) ++#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28) ++#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 27) ++#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 22) ++#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 21) ++#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 20) ++#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 19) ++#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18) ++#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17) ++#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16) ++#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15) ++#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14) ++#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 13) ++#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 12) ++#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 11) ++#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 10) ++#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 9) ++#define MVPP2_PE_VID_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 8) ++#define MVPP2_PE_VID_EDSA_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 7) ++#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 6) ++#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 5) ++/* reserved */ ++#define MVPP2_PE_MAC_MC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 3) ++#define MVPP2_PE_MAC_UC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2) + #define MVPP2_PE_MAC_NON_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 1) + ++#define MVPP2_PRS_VID_PORT_FIRST(port) (MVPP2_PE_VID_FILT_RANGE_START + \ ++ ((port) * MVPP2_PRS_VLAN_FILT_MAX)) ++#define MVPP2_PRS_VID_PORT_LAST(port) (MVPP2_PRS_VID_PORT_FIRST(port) \ ++ + MVPP2_PRS_VLAN_FILT_MAX_ENTRY) ++/* Index of default vid filter for given port */ ++#define MVPP2_PRS_VID_PORT_DFLT(port) (MVPP2_PRS_VID_PORT_FIRST(port) \ ++ + MVPP2_PRS_VLAN_FILT_DFLT_ENTRY) ++ + /* Sram structure + * The fields are represented by MVPP2_PRS_TCAM_DATA_REG(3)->(0). + */ +@@ -514,31 +740,32 @@ enum mvpp2_tag_type { + /* Sram result info bits assignment */ + #define MVPP2_PRS_RI_MAC_ME_MASK 0x1 + #define MVPP2_PRS_RI_DSA_MASK 0x2 +-#define MVPP2_PRS_RI_VLAN_MASK 0xc +-#define MVPP2_PRS_RI_VLAN_NONE ~(BIT(2) | BIT(3)) ++#define MVPP2_PRS_RI_VLAN_MASK (BIT(2) | BIT(3)) ++#define MVPP2_PRS_RI_VLAN_NONE 0x0 + #define MVPP2_PRS_RI_VLAN_SINGLE BIT(2) + #define MVPP2_PRS_RI_VLAN_DOUBLE BIT(3) + #define MVPP2_PRS_RI_VLAN_TRIPLE (BIT(2) | BIT(3)) + #define MVPP2_PRS_RI_CPU_CODE_MASK 0x70 + #define MVPP2_PRS_RI_CPU_CODE_RX_SPEC BIT(4) +-#define MVPP2_PRS_RI_L2_CAST_MASK 0x600 +-#define MVPP2_PRS_RI_L2_UCAST ~(BIT(9) | BIT(10)) ++#define MVPP2_PRS_RI_L2_CAST_MASK (BIT(9) | BIT(10)) ++#define MVPP2_PRS_RI_L2_UCAST 0x0 + #define MVPP2_PRS_RI_L2_MCAST BIT(9) + #define MVPP2_PRS_RI_L2_BCAST BIT(10) + #define MVPP2_PRS_RI_PPPOE_MASK 0x800 +-#define MVPP2_PRS_RI_L3_PROTO_MASK 0x7000 +-#define MVPP2_PRS_RI_L3_UN ~(BIT(12) | BIT(13) | BIT(14)) ++#define MVPP2_PRS_RI_L3_PROTO_MASK (BIT(12) | BIT(13) | BIT(14)) ++#define MVPP2_PRS_RI_L3_UN 0x0 + #define MVPP2_PRS_RI_L3_IP4 BIT(12) + #define MVPP2_PRS_RI_L3_IP4_OPT BIT(13) + #define MVPP2_PRS_RI_L3_IP4_OTHER (BIT(12) | BIT(13)) + #define MVPP2_PRS_RI_L3_IP6 BIT(14) + #define MVPP2_PRS_RI_L3_IP6_EXT (BIT(12) | BIT(14)) + #define MVPP2_PRS_RI_L3_ARP (BIT(13) | BIT(14)) +-#define MVPP2_PRS_RI_L3_ADDR_MASK 0x18000 +-#define MVPP2_PRS_RI_L3_UCAST ~(BIT(15) | BIT(16)) ++#define MVPP2_PRS_RI_L3_ADDR_MASK (BIT(15) | BIT(16)) ++#define MVPP2_PRS_RI_L3_UCAST 0x0 + #define MVPP2_PRS_RI_L3_MCAST BIT(15) + #define MVPP2_PRS_RI_L3_BCAST (BIT(15) | BIT(16)) + #define MVPP2_PRS_RI_IP_FRAG_MASK 0x20000 ++#define MVPP2_PRS_RI_IP_FRAG_TRUE BIT(17) + #define MVPP2_PRS_RI_UDF3_MASK 0x300000 + #define MVPP2_PRS_RI_UDF3_RX_SPECIAL BIT(21) + #define MVPP2_PRS_RI_L4_PROTO_MASK 0x1c00000 +@@ -558,6 +785,7 @@ enum mvpp2_tag_type { + #define MVPP2_PRS_IPV6_EXT_AH_L4_AI_BIT BIT(4) + #define MVPP2_PRS_SINGLE_VLAN_AI 0 + #define MVPP2_PRS_DBL_VLAN_AI_BIT BIT(7) ++#define MVPP2_PRS_EDSA_VID_AI_BIT BIT(0) + + /* DSA/EDSA type */ + #define MVPP2_PRS_TAGGED true +@@ -580,6 +808,7 @@ enum mvpp2_prs_lookup { + MVPP2_PRS_LU_MAC, + MVPP2_PRS_LU_DSA, + MVPP2_PRS_LU_VLAN, ++ MVPP2_PRS_LU_VID, + MVPP2_PRS_LU_L2, + MVPP2_PRS_LU_PPPOE, + MVPP2_PRS_LU_IP4, +@@ -588,6 +817,12 @@ enum mvpp2_prs_lookup { + MVPP2_PRS_LU_LAST, + }; + ++/* L2 cast enum */ ++enum mvpp2_prs_l2_cast { ++ MVPP2_PRS_L2_UNI_CAST, ++ MVPP2_PRS_L2_MULTI_CAST, ++}; ++ + /* L3 cast enum */ + enum mvpp2_prs_l3_cast { + MVPP2_PRS_L3_UNI_CAST, +@@ -599,46 +834,119 @@ enum mvpp2_prs_l3_cast { + #define MVPP2_CLS_FLOWS_TBL_SIZE 512 + #define MVPP2_CLS_FLOWS_TBL_DATA_WORDS 3 + #define MVPP2_CLS_LKP_TBL_SIZE 64 ++#define MVPP2_CLS_RX_QUEUES 256 ++ ++/* RSS constants */ ++#define MVPP22_RSS_TABLE_ENTRIES 32 + + /* BM constants */ +-#define MVPP2_BM_POOLS_NUM 8 ++#define MVPP2_BM_JUMBO_BUF_NUM 512 + #define MVPP2_BM_LONG_BUF_NUM 1024 + #define MVPP2_BM_SHORT_BUF_NUM 2048 + #define MVPP2_BM_POOL_SIZE_MAX (16*1024 - MVPP2_BM_POOL_PTR_ALIGN/4) + #define MVPP2_BM_POOL_PTR_ALIGN 128 +-#define MVPP2_BM_SWF_LONG_POOL(port) ((port > 2) ? 2 : port) +-#define MVPP2_BM_SWF_SHORT_POOL 3 + + /* BM cookie (32 bits) definition */ + #define MVPP2_BM_COOKIE_POOL_OFFS 8 + #define MVPP2_BM_COOKIE_CPU_OFFS 24 + ++#define MVPP2_BM_SHORT_FRAME_SIZE 512 ++#define MVPP2_BM_LONG_FRAME_SIZE 2048 ++#define MVPP2_BM_JUMBO_FRAME_SIZE 10240 + /* BM short pool packet size + * These value assure that for SWF the total number + * of bytes allocated for each buffer will be 512 + */ +-#define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(512) ++#define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_SHORT_FRAME_SIZE) ++#define MVPP2_BM_LONG_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_LONG_FRAME_SIZE) ++#define MVPP2_BM_JUMBO_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_JUMBO_FRAME_SIZE) ++ ++#define MVPP21_ADDR_SPACE_SZ 0 ++#define MVPP22_ADDR_SPACE_SZ SZ_64K + +-enum mvpp2_bm_type { +- MVPP2_BM_FREE, +- MVPP2_BM_SWF_LONG, +- MVPP2_BM_SWF_SHORT ++#define MVPP2_MAX_THREADS 8 ++#define MVPP2_MAX_QVECS MVPP2_MAX_THREADS ++ ++enum mvpp2_bm_pool_log_num { ++ MVPP2_BM_SHORT, ++ MVPP2_BM_LONG, ++ MVPP2_BM_JUMBO, ++ MVPP2_BM_POOLS_NUM + }; + ++static struct { ++ int pkt_size; ++ int buf_num; ++} mvpp2_pools[MVPP2_BM_POOLS_NUM]; ++ ++/* GMAC MIB Counters register definitions */ ++#define MVPP21_MIB_COUNTERS_OFFSET 0x1000 ++#define MVPP21_MIB_COUNTERS_PORT_SZ 0x400 ++#define MVPP22_MIB_COUNTERS_OFFSET 0x0 ++#define MVPP22_MIB_COUNTERS_PORT_SZ 0x100 ++ ++#define MVPP2_MIB_GOOD_OCTETS_RCVD 0x0 ++#define MVPP2_MIB_BAD_OCTETS_RCVD 0x8 ++#define MVPP2_MIB_CRC_ERRORS_SENT 0xc ++#define MVPP2_MIB_UNICAST_FRAMES_RCVD 0x10 ++#define MVPP2_MIB_BROADCAST_FRAMES_RCVD 0x18 ++#define MVPP2_MIB_MULTICAST_FRAMES_RCVD 0x1c ++#define MVPP2_MIB_FRAMES_64_OCTETS 0x20 ++#define MVPP2_MIB_FRAMES_65_TO_127_OCTETS 0x24 ++#define MVPP2_MIB_FRAMES_128_TO_255_OCTETS 0x28 ++#define MVPP2_MIB_FRAMES_256_TO_511_OCTETS 0x2c ++#define MVPP2_MIB_FRAMES_512_TO_1023_OCTETS 0x30 ++#define MVPP2_MIB_FRAMES_1024_TO_MAX_OCTETS 0x34 ++#define MVPP2_MIB_GOOD_OCTETS_SENT 0x38 ++#define MVPP2_MIB_UNICAST_FRAMES_SENT 0x40 ++#define MVPP2_MIB_MULTICAST_FRAMES_SENT 0x48 ++#define MVPP2_MIB_BROADCAST_FRAMES_SENT 0x4c ++#define MVPP2_MIB_FC_SENT 0x54 ++#define MVPP2_MIB_FC_RCVD 0x58 ++#define MVPP2_MIB_RX_FIFO_OVERRUN 0x5c ++#define MVPP2_MIB_UNDERSIZE_RCVD 0x60 ++#define MVPP2_MIB_FRAGMENTS_RCVD 0x64 ++#define MVPP2_MIB_OVERSIZE_RCVD 0x68 ++#define MVPP2_MIB_JABBER_RCVD 0x6c ++#define MVPP2_MIB_MAC_RCV_ERROR 0x70 ++#define MVPP2_MIB_BAD_CRC_EVENT 0x74 ++#define MVPP2_MIB_COLLISION 0x78 ++#define MVPP2_MIB_LATE_COLLISION 0x7c ++ ++#define MVPP2_MIB_COUNTERS_STATS_DELAY (1 * HZ) ++ ++#define MVPP2_DESC_DMA_MASK DMA_BIT_MASK(40) ++ + /* Definitions */ + + /* Shared Packet Processor resources */ + struct mvpp2 { + /* Shared registers' base addresses */ +- void __iomem *base; + void __iomem *lms_base; ++ void __iomem *iface_base; ++ ++ /* On PPv2.2, each "software thread" can access the base ++ * register through a separate address space, each 64 KB apart ++ * from each other. Typically, such address spaces will be ++ * used per CPU. ++ */ ++ void __iomem *swth_base[MVPP2_MAX_THREADS]; ++ ++ /* On PPv2.2, some port control registers are located into the system ++ * controller space. These registers are accessible through a regmap. ++ */ ++ struct regmap *sysctrl_base; + + /* Common clocks */ + struct clk *pp_clk; + struct clk *gop_clk; ++ struct clk *mg_clk; ++ struct clk *mg_core_clk; ++ struct clk *axi_clk; + + /* List of pointers to port structures */ +- struct mvpp2_port **port_list; ++ int port_count; ++ struct mvpp2_port *port_list[MVPP2_MAX_PORTS]; + + /* Aggregated TXQs */ + struct mvpp2_tx_queue *aggr_txqs; +@@ -653,6 +961,16 @@ struct mvpp2 { + + /* Tclk value */ + u32 tclk; ++ ++ /* HW version */ ++ enum { MVPP21, MVPP22 } hw_version; ++ ++ /* Maximum number of RXQs per port */ ++ unsigned int max_port_rxqs; ++ ++ /* Workqueue to gather hardware statistics */ ++ char queue_name[30]; ++ struct workqueue_struct *stats_queue; + }; + + struct mvpp2_pcpu_stats { +@@ -671,25 +989,42 @@ struct mvpp2_port_pcpu { + struct tasklet_struct tx_done_tasklet; + }; + ++struct mvpp2_queue_vector { ++ int irq; ++ struct napi_struct napi; ++ enum { MVPP2_QUEUE_VECTOR_SHARED, MVPP2_QUEUE_VECTOR_PRIVATE } type; ++ int sw_thread_id; ++ u16 sw_thread_mask; ++ int first_rxq; ++ int nrxqs; ++ u32 pending_cause_rx; ++ struct mvpp2_port *port; ++}; ++ + struct mvpp2_port { + u8 id; + +- int irq; ++ /* Index of the port from the "group of ports" complex point ++ * of view ++ */ ++ int gop_id; ++ ++ int link_irq; + + struct mvpp2 *priv; + + /* Per-port registers' base address */ + void __iomem *base; ++ void __iomem *stats_base; + + struct mvpp2_rx_queue **rxqs; ++ unsigned int nrxqs; + struct mvpp2_tx_queue **txqs; ++ unsigned int ntxqs; + struct net_device *dev; + + int pkt_size; + +- u32 pending_cause_rx; +- struct napi_struct napi; +- + /* Per-CPU port control */ + struct mvpp2_port_pcpu __percpu *pcpu; + +@@ -699,9 +1034,15 @@ struct mvpp2_port { + u16 tx_ring_size; + u16 rx_ring_size; + struct mvpp2_pcpu_stats __percpu *stats; ++ u64 *ethtool_stats; ++ ++ /* Per-port work and its lock to gather hardware statistics */ ++ struct mutex gather_stats_lock; ++ struct delayed_work stats_work; + + phy_interface_t phy_interface; + struct device_node *phy_node; ++ struct phy *comphy; + unsigned int link; + unsigned int duplex; + unsigned int speed; +@@ -711,6 +1052,12 @@ struct mvpp2_port { + + /* Index of first port's physical RXQ */ + u8 first_rxq; ++ ++ struct mvpp2_queue_vector qvecs[MVPP2_MAX_QVECS]; ++ unsigned int nqvecs; ++ bool has_tx_irqs; ++ ++ u32 tx_time_coal; + }; + + /* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the +@@ -745,22 +1092,24 @@ struct mvpp2_port { + #define MVPP2_RXD_L3_IP6 BIT(30) + #define MVPP2_RXD_BUF_HDR BIT(31) + +-struct mvpp2_tx_desc { ++/* HW TX descriptor for PPv2.1 */ ++struct mvpp21_tx_desc { + u32 command; /* Options used by HW for packet transmitting.*/ + u8 packet_offset; /* the offset from the buffer beginning */ + u8 phys_txq; /* destination queue ID */ + u16 data_size; /* data size of transmitted packet in bytes */ +- u32 buf_phys_addr; /* physical addr of transmitted buffer */ ++ u32 buf_dma_addr; /* physical addr of transmitted buffer */ + u32 buf_cookie; /* cookie for access to TX buffer in tx path */ + u32 reserved1[3]; /* hw_cmd (for future use, BM, PON, PNC) */ + u32 reserved2; /* reserved (for future use) */ + }; + +-struct mvpp2_rx_desc { ++/* HW RX descriptor for PPv2.1 */ ++struct mvpp21_rx_desc { + u32 status; /* info about received packet */ + u16 reserved1; /* parser_info (for future use, PnC) */ + u16 data_size; /* size of received packet in bytes */ +- u32 buf_phys_addr; /* physical address of the buffer */ ++ u32 buf_dma_addr; /* physical address of the buffer */ + u32 buf_cookie; /* cookie for access to RX buffer in rx path */ + u16 reserved2; /* gem_port_id (for future use, PON) */ + u16 reserved3; /* csum_l4 (for future use, PnC) */ +@@ -771,12 +1120,51 @@ struct mvpp2_rx_desc { + u32 reserved8; + }; + ++/* HW TX descriptor for PPv2.2 */ ++struct mvpp22_tx_desc { ++ u32 command; ++ u8 packet_offset; ++ u8 phys_txq; ++ u16 data_size; ++ u64 reserved1; ++ u64 buf_dma_addr_ptp; ++ u64 buf_cookie_misc; ++}; ++ ++/* HW RX descriptor for PPv2.2 */ ++struct mvpp22_rx_desc { ++ u32 status; ++ u16 reserved1; ++ u16 data_size; ++ u32 reserved2; ++ u32 reserved3; ++ u64 buf_dma_addr_key_hash; ++ u64 buf_cookie_misc; ++}; ++ ++/* Opaque type used by the driver to manipulate the HW TX and RX ++ * descriptors ++ */ ++struct mvpp2_tx_desc { ++ union { ++ struct mvpp21_tx_desc pp21; ++ struct mvpp22_tx_desc pp22; ++ }; ++}; ++ ++struct mvpp2_rx_desc { ++ union { ++ struct mvpp21_rx_desc pp21; ++ struct mvpp22_rx_desc pp22; ++ }; ++}; ++ + struct mvpp2_txq_pcpu_buf { + /* Transmitted SKB */ + struct sk_buff *skb; + + /* Physical address of transmitted buffer */ +- dma_addr_t phys; ++ dma_addr_t dma; + + /* Size transmitted */ + size_t size; +@@ -794,6 +1182,9 @@ struct mvpp2_txq_pcpu { + */ + int count; + ++ int wake_threshold; ++ int stop_threshold; ++ + /* Number of Tx DMA descriptors reserved for each CPU */ + int reserved_num; + +@@ -805,6 +1196,10 @@ struct mvpp2_txq_pcpu { + + /* Index of the TX DMA descriptor to be cleaned up */ + int txq_get_index; ++ ++ /* DMA buffer for TSO headers */ ++ char *tso_headers; ++ dma_addr_t tso_headers_dma; + }; + + struct mvpp2_tx_queue { +@@ -823,16 +1218,13 @@ struct mvpp2_tx_queue { + /* Per-CPU control of physical Tx queues */ + struct mvpp2_txq_pcpu __percpu *pcpu; + +- /* Array of transmitted skb */ +- struct sk_buff **tx_skb; +- + u32 done_pkts_coal; + + /* Virtual address of thex Tx DMA descriptors array */ + struct mvpp2_tx_desc *descs; + + /* DMA address of the Tx DMA descriptors array */ +- dma_addr_t descs_phys; ++ dma_addr_t descs_dma; + + /* Index of the last Tx DMA descriptor */ + int last_desc; +@@ -855,7 +1247,7 @@ struct mvpp2_rx_queue { + struct mvpp2_rx_desc *descs; + + /* DMA address of the RX DMA descriptors array */ +- dma_addr_t descs_phys; ++ dma_addr_t descs_dma; + + /* Index of the last RX DMA descriptor */ + int last_desc; +@@ -915,52 +1307,41 @@ struct mvpp2_cls_lookup_entry { + struct mvpp2_bm_pool { + /* Pool number in the range 0-7 */ + int id; +- enum mvpp2_bm_type type; + + /* Buffer Pointers Pool External (BPPE) size */ + int size; ++ /* BPPE size in bytes */ ++ int size_bytes; + /* Number of buffers for this pool */ + int buf_num; + /* Pool buffer size */ + int buf_size; + /* Packet size */ + int pkt_size; ++ int frag_size; + + /* BPPE virtual base address */ + u32 *virt_addr; +- /* BPPE physical base address */ +- dma_addr_t phys_addr; ++ /* BPPE DMA base address */ ++ dma_addr_t dma_addr; + + /* Ports using BM pool */ + u32 port_map; +- +- /* Occupied buffers indicator */ +- atomic_t in_use; +- int in_use_thresh; + }; + +-struct mvpp2_buff_hdr { +- u32 next_buff_phys_addr; +- u32 next_buff_virt_addr; +- u16 byte_count; +- u16 info; +- u8 reserved1; /* bm_qset (for future use, BM) */ +-}; ++#define IS_TSO_HEADER(txq_pcpu, addr) \ ++ ((addr) >= (txq_pcpu)->tso_headers_dma && \ ++ (addr) < (txq_pcpu)->tso_headers_dma + \ ++ (txq_pcpu)->size * TSO_HEADER_SIZE) + +-/* Buffer header info bits */ +-#define MVPP2_B_HDR_INFO_MC_ID_MASK 0xfff +-#define MVPP2_B_HDR_INFO_MC_ID(info) ((info) & MVPP2_B_HDR_INFO_MC_ID_MASK) +-#define MVPP2_B_HDR_INFO_LAST_OFFS 12 +-#define MVPP2_B_HDR_INFO_LAST_MASK BIT(12) +-#define MVPP2_B_HDR_INFO_IS_LAST(info) \ +- ((info & MVPP2_B_HDR_INFO_LAST_MASK) >> MVPP2_B_HDR_INFO_LAST_OFFS) ++/* Queue modes */ ++#define MVPP2_QDIST_SINGLE_MODE 0 ++#define MVPP2_QDIST_MULTI_MODE 1 + +-/* Static declaractions */ ++static int queue_mode = MVPP2_QDIST_SINGLE_MODE; + +-/* Number of RXQs used by single port */ +-static int rxq_number = MVPP2_DEFAULT_RXQ; +-/* Number of TXQs used by single port */ +-static int txq_number = MVPP2_MAX_TXQ; ++module_param(queue_mode, int, 0444); ++MODULE_PARM_DESC(queue_mode, "Set queue_mode (single=0, multi=1)"); + + #define MVPP2_DRIVER_NAME "mvpp2" + #define MVPP2_DRIVER_VERSION "1.0" +@@ -969,12 +1350,190 @@ struct mvpp2_buff_hdr { + + static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data) + { +- writel(data, priv->base + offset); ++ writel(data, priv->swth_base[0] + offset); + } + + static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) + { +- return readl(priv->base + offset); ++ return readl(priv->swth_base[0] + offset); ++} ++ ++static u32 mvpp2_read_relaxed(struct mvpp2 *priv, u32 offset) ++{ ++ return readl_relaxed(priv->swth_base[0] + offset); ++} ++/* These accessors should be used to access: ++ * ++ * - per-CPU registers, where each CPU has its own copy of the ++ * register. ++ * ++ * MVPP2_BM_VIRT_ALLOC_REG ++ * MVPP2_BM_ADDR_HIGH_ALLOC ++ * MVPP22_BM_ADDR_HIGH_RLS_REG ++ * MVPP2_BM_VIRT_RLS_REG ++ * MVPP2_ISR_RX_TX_CAUSE_REG ++ * MVPP2_ISR_RX_TX_MASK_REG ++ * MVPP2_TXQ_NUM_REG ++ * MVPP2_AGGR_TXQ_UPDATE_REG ++ * MVPP2_TXQ_RSVD_REQ_REG ++ * MVPP2_TXQ_RSVD_RSLT_REG ++ * MVPP2_TXQ_SENT_REG ++ * MVPP2_RXQ_NUM_REG ++ * ++ * - global registers that must be accessed through a specific CPU ++ * window, because they are related to an access to a per-CPU ++ * register ++ * ++ * MVPP2_BM_PHY_ALLOC_REG (related to MVPP2_BM_VIRT_ALLOC_REG) ++ * MVPP2_BM_PHY_RLS_REG (related to MVPP2_BM_VIRT_RLS_REG) ++ * MVPP2_RXQ_THRESH_REG (related to MVPP2_RXQ_NUM_REG) ++ * MVPP2_RXQ_DESC_ADDR_REG (related to MVPP2_RXQ_NUM_REG) ++ * MVPP2_RXQ_DESC_SIZE_REG (related to MVPP2_RXQ_NUM_REG) ++ * MVPP2_RXQ_INDEX_REG (related to MVPP2_RXQ_NUM_REG) ++ * MVPP2_TXQ_PENDING_REG (related to MVPP2_TXQ_NUM_REG) ++ * MVPP2_TXQ_DESC_ADDR_REG (related to MVPP2_TXQ_NUM_REG) ++ * MVPP2_TXQ_DESC_SIZE_REG (related to MVPP2_TXQ_NUM_REG) ++ * MVPP2_TXQ_INDEX_REG (related to MVPP2_TXQ_NUM_REG) ++ * MVPP2_TXQ_PENDING_REG (related to MVPP2_TXQ_NUM_REG) ++ * MVPP2_TXQ_PREF_BUF_REG (related to MVPP2_TXQ_NUM_REG) ++ * MVPP2_TXQ_PREF_BUF_REG (related to MVPP2_TXQ_NUM_REG) ++ */ ++static void mvpp2_percpu_write(struct mvpp2 *priv, int cpu, ++ u32 offset, u32 data) ++{ ++ writel(data, priv->swth_base[cpu] + offset); ++} ++ ++static u32 mvpp2_percpu_read(struct mvpp2 *priv, int cpu, ++ u32 offset) ++{ ++ return readl(priv->swth_base[cpu] + offset); ++} ++ ++static void mvpp2_percpu_write_relaxed(struct mvpp2 *priv, int cpu, ++ u32 offset, u32 data) ++{ ++ writel_relaxed(data, priv->swth_base[cpu] + offset); ++} ++ ++static u32 mvpp2_percpu_read_relaxed(struct mvpp2 *priv, int cpu, ++ u32 offset) ++{ ++ return readl_relaxed(priv->swth_base[cpu] + offset); ++} ++ ++static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return tx_desc->pp21.buf_dma_addr; ++ else ++ return tx_desc->pp22.buf_dma_addr_ptp & MVPP2_DESC_DMA_MASK; ++} ++ ++static void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc, ++ dma_addr_t dma_addr) ++{ ++ dma_addr_t addr, offset; ++ ++ addr = dma_addr & ~MVPP2_TX_DESC_ALIGN; ++ offset = dma_addr & MVPP2_TX_DESC_ALIGN; ++ ++ if (port->priv->hw_version == MVPP21) { ++ tx_desc->pp21.buf_dma_addr = addr; ++ tx_desc->pp21.packet_offset = offset; ++ } else { ++ u64 val = (u64)addr; ++ ++ tx_desc->pp22.buf_dma_addr_ptp &= ~MVPP2_DESC_DMA_MASK; ++ tx_desc->pp22.buf_dma_addr_ptp |= val; ++ tx_desc->pp22.packet_offset = offset; ++ } ++} ++ ++static size_t mvpp2_txdesc_size_get(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return tx_desc->pp21.data_size; ++ else ++ return tx_desc->pp22.data_size; ++} ++ ++static void mvpp2_txdesc_size_set(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc, ++ size_t size) ++{ ++ if (port->priv->hw_version == MVPP21) ++ tx_desc->pp21.data_size = size; ++ else ++ tx_desc->pp22.data_size = size; ++} ++ ++static void mvpp2_txdesc_txq_set(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc, ++ unsigned int txq) ++{ ++ if (port->priv->hw_version == MVPP21) ++ tx_desc->pp21.phys_txq = txq; ++ else ++ tx_desc->pp22.phys_txq = txq; ++} ++ ++static void mvpp2_txdesc_cmd_set(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc, ++ unsigned int command) ++{ ++ if (port->priv->hw_version == MVPP21) ++ tx_desc->pp21.command = command; ++ else ++ tx_desc->pp22.command = command; ++} ++ ++static unsigned int mvpp2_txdesc_offset_get(struct mvpp2_port *port, ++ struct mvpp2_tx_desc *tx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return tx_desc->pp21.packet_offset; ++ else ++ return tx_desc->pp22.packet_offset; ++} ++ ++static dma_addr_t mvpp2_rxdesc_dma_addr_get(struct mvpp2_port *port, ++ struct mvpp2_rx_desc *rx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return rx_desc->pp21.buf_dma_addr; ++ else ++ return rx_desc->pp22.buf_dma_addr_key_hash & MVPP2_DESC_DMA_MASK; ++} ++ ++static unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port, ++ struct mvpp2_rx_desc *rx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return rx_desc->pp21.buf_cookie; ++ else ++ return rx_desc->pp22.buf_cookie_misc & MVPP2_DESC_DMA_MASK; ++} ++ ++static size_t mvpp2_rxdesc_size_get(struct mvpp2_port *port, ++ struct mvpp2_rx_desc *rx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return rx_desc->pp21.data_size; ++ else ++ return rx_desc->pp22.data_size; ++} ++ ++static u32 mvpp2_rxdesc_status_get(struct mvpp2_port *port, ++ struct mvpp2_rx_desc *rx_desc) ++{ ++ if (port->priv->hw_version == MVPP21) ++ return rx_desc->pp21.status; ++ else ++ return rx_desc->pp22.status; + } + + static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) +@@ -984,15 +1543,17 @@ static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) + txq_pcpu->txq_get_index = 0; + } + +-static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu, ++static void mvpp2_txq_inc_put(struct mvpp2_port *port, ++ struct mvpp2_txq_pcpu *txq_pcpu, + struct sk_buff *skb, + struct mvpp2_tx_desc *tx_desc) + { + struct mvpp2_txq_pcpu_buf *tx_buf = + txq_pcpu->buffs + txq_pcpu->txq_put_index; + tx_buf->skb = skb; +- tx_buf->size = tx_desc->data_size; +- tx_buf->phys = tx_desc->buf_phys_addr + tx_desc->packet_offset; ++ tx_buf->size = mvpp2_txdesc_size_get(port, tx_desc); ++ tx_buf->dma = mvpp2_txdesc_dma_addr_get(port, tx_desc) + ++ mvpp2_txdesc_offset_get(port, tx_desc); + txq_pcpu->txq_put_index++; + if (txq_pcpu->txq_put_index == txq_pcpu->size) + txq_pcpu->txq_put_index = 0; +@@ -1036,14 +1597,18 @@ static int mvpp2_prs_hw_write(struct mvpp2 *priv, struct mvpp2_prs_entry *pe) + return 0; + } + +-/* Read tcam entry from hw */ +-static int mvpp2_prs_hw_read(struct mvpp2 *priv, struct mvpp2_prs_entry *pe) ++/* Initialize tcam entry from hw */ ++static int mvpp2_prs_init_from_hw(struct mvpp2 *priv, ++ struct mvpp2_prs_entry *pe, int tid) + { + int i; + +- if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1) ++ if (tid > MVPP2_PRS_TCAM_SRAM_SIZE - 1) + return -EINVAL; + ++ memset(pe, 0, sizeof(*pe)); ++ pe->index = tid; ++ + /* Write tcam index - indirect access */ + mvpp2_write(priv, MVPP2_PRS_TCAM_IDX_REG, pe->index); + +@@ -1153,7 +1718,7 @@ static bool mvpp2_prs_tcam_data_cmp(struct mvpp2_prs_entry *pe, int offs, + int off = MVPP2_PRS_TCAM_DATA_BYTE(offs); + u16 tcam_data; + +- tcam_data = (8 << pe->tcam.byte[off + 1]) | pe->tcam.byte[off]; ++ tcam_data = (pe->tcam.byte[off + 1] << 8) | pe->tcam.byte[off]; + if (tcam_data != data) + return false; + return true; +@@ -1193,6 +1758,14 @@ static void mvpp2_prs_match_etype(struct mvpp2_prs_entry *pe, int offset, + mvpp2_prs_tcam_data_byte_set(pe, offset + 1, ethertype & 0xff, 0xff); + } + ++/* Set vid in tcam sw entry */ ++static void mvpp2_prs_match_vid(struct mvpp2_prs_entry *pe, int offset, ++ unsigned short vid) ++{ ++ mvpp2_prs_tcam_data_byte_set(pe, offset + 0, (vid & 0xf00) >> 8, 0xf); ++ mvpp2_prs_tcam_data_byte_set(pe, offset + 1, vid & 0xff, 0xff); ++} ++ + /* Set bits in sram sw entry */ + static void mvpp2_prs_sram_bits_set(struct mvpp2_prs_entry *pe, int bit_num, + int val) +@@ -1359,16 +1932,11 @@ static void mvpp2_prs_sram_offset_set(struct mvpp2_prs_entry *pe, + } + + /* Find parser flow entry */ +-static struct mvpp2_prs_entry *mvpp2_prs_flow_find(struct mvpp2 *priv, int flow) ++static int mvpp2_prs_flow_find(struct mvpp2 *priv, int flow) + { +- struct mvpp2_prs_entry *pe; ++ struct mvpp2_prs_entry pe; + int tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return NULL; +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); +- + /* Go through the all entires with MVPP2_PRS_LU_FLOWS */ + for (tid = MVPP2_PRS_TCAM_SRAM_SIZE - 1; tid >= 0; tid--) { + u8 bits; +@@ -1377,17 +1945,15 @@ static struct mvpp2_prs_entry *mvpp2_prs_flow_find(struct mvpp2 *priv, int flow) + priv->prs_shadow[tid].lu != MVPP2_PRS_LU_FLOWS) + continue; + +- pe->index = tid; +- mvpp2_prs_hw_read(priv, pe); +- bits = mvpp2_prs_sram_ai_get(pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); ++ bits = mvpp2_prs_sram_ai_get(&pe); + + /* Sram store classification lookup ID in AI bits [5:0] */ + if ((bits & MVPP2_PRS_FLOW_ID_MASK) == flow) +- return pe; ++ return tid; + } +- kfree(pe); + +- return NULL; ++ return -ENOENT; + } + + /* Return first free tcam index, seeking from start to end */ +@@ -1417,11 +1983,10 @@ static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add) + + if (priv->prs_shadow[MVPP2_PE_DROP_ALL].valid) { + /* Entry exist - update port only */ +- pe.index = MVPP2_PE_DROP_ALL; +- mvpp2_prs_hw_read(priv, &pe); ++ mvpp2_prs_init_from_hw(priv, &pe, MVPP2_PE_DROP_ALL); + } else { + /* Entry doesn't exist - create new */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); + pe.index = MVPP2_PE_DROP_ALL; + +@@ -1445,78 +2010,42 @@ static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add) + mvpp2_prs_hw_write(priv, &pe); + } + +-/* Set port to promiscuous mode */ +-static void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port, bool add) ++/* Set port to unicast or multicast promiscuous mode */ ++static void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port, ++ enum mvpp2_prs_l2_cast l2_cast, bool add) + { + struct mvpp2_prs_entry pe; ++ unsigned char cast_match; ++ unsigned int ri; ++ int tid; + +- /* Promiscuous mode - Accept unknown packets */ +- +- if (priv->prs_shadow[MVPP2_PE_MAC_PROMISCUOUS].valid) { +- /* Entry exist - update port only */ +- pe.index = MVPP2_PE_MAC_PROMISCUOUS; +- mvpp2_prs_hw_read(priv, &pe); ++ if (l2_cast == MVPP2_PRS_L2_UNI_CAST) { ++ cast_match = MVPP2_PRS_UCAST_VAL; ++ tid = MVPP2_PE_MAC_UC_PROMISCUOUS; ++ ri = MVPP2_PRS_RI_L2_UCAST; + } else { +- /* Entry doesn't exist - create new */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); +- mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); +- pe.index = MVPP2_PE_MAC_PROMISCUOUS; +- +- /* Continue - set next lookup */ +- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); +- +- /* Set result info bits */ +- mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_UCAST, +- MVPP2_PRS_RI_L2_CAST_MASK); +- +- /* Shift to ethertype */ +- mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN, +- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); +- +- /* Mask all ports */ +- mvpp2_prs_tcam_port_map_set(&pe, 0); +- +- /* Update shadow table */ +- mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); ++ cast_match = MVPP2_PRS_MCAST_VAL; ++ tid = MVPP2_PE_MAC_MC_PROMISCUOUS; ++ ri = MVPP2_PRS_RI_L2_MCAST; + } + +- /* Update port mask */ +- mvpp2_prs_tcam_port_set(&pe, port, add); +- +- mvpp2_prs_hw_write(priv, &pe); +-} +- +-/* Accept multicast */ +-static void mvpp2_prs_mac_multi_set(struct mvpp2 *priv, int port, int index, +- bool add) +-{ +- struct mvpp2_prs_entry pe; +- unsigned char da_mc; +- +- /* Ethernet multicast address first byte is +- * 0x01 for IPv4 and 0x33 for IPv6 +- */ +- da_mc = (index == MVPP2_PE_MAC_MC_ALL) ? 0x01 : 0x33; +- +- if (priv->prs_shadow[index].valid) { +- /* Entry exist - update port only */ +- pe.index = index; +- mvpp2_prs_hw_read(priv, &pe); ++ /* promiscuous mode - Accept unknown unicast or multicast packets */ ++ if (priv->prs_shadow[tid].valid) { ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + } else { +- /* Entry doesn't exist - create new */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); +- pe.index = index; ++ pe.index = tid; + + /* Continue - set next lookup */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); + + /* Set result info bits */ +- mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_MCAST, +- MVPP2_PRS_RI_L2_CAST_MASK); ++ mvpp2_prs_sram_ri_update(&pe, ri, MVPP2_PRS_RI_L2_CAST_MASK); + +- /* Update tcam entry data first byte */ +- mvpp2_prs_tcam_data_byte_set(&pe, 0, da_mc, 0xff); ++ /* Match UC or MC addresses */ ++ mvpp2_prs_tcam_data_byte_set(&pe, 0, cast_match, ++ MVPP2_PRS_CAST_MASK); + + /* Shift to ethertype */ + mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN, +@@ -1552,32 +2081,37 @@ static void mvpp2_prs_dsa_tag_set(struct mvpp2 *priv, int port, bool add, + + if (priv->prs_shadow[tid].valid) { + /* Entry exist - update port only */ +- pe.index = tid; +- mvpp2_prs_hw_read(priv, &pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + } else { + /* Entry doesn't exist - create new */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); + pe.index = tid; + +- /* Shift 4 bytes if DSA tag or 8 bytes in case of EDSA tag*/ +- mvpp2_prs_sram_shift_set(&pe, shift, +- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); +- + /* Update shadow table */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_DSA); + + if (tagged) { + /* Set tagged bit in DSA tag */ + mvpp2_prs_tcam_data_byte_set(&pe, 0, +- MVPP2_PRS_TCAM_DSA_TAGGED_BIT, +- MVPP2_PRS_TCAM_DSA_TAGGED_BIT); +- /* Clear all ai bits for next iteration */ +- mvpp2_prs_sram_ai_update(&pe, 0, +- MVPP2_PRS_SRAM_AI_MASK); +- /* If packet is tagged continue check vlans */ +- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ MVPP2_PRS_TCAM_DSA_TAGGED_BIT, ++ MVPP2_PRS_TCAM_DSA_TAGGED_BIT); ++ ++ /* Set ai bits for next iteration */ ++ if (extend) ++ mvpp2_prs_sram_ai_update(&pe, 1, ++ MVPP2_PRS_SRAM_AI_MASK); ++ else ++ mvpp2_prs_sram_ai_update(&pe, 0, ++ MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* If packet is tagged continue check vid filtering */ ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID); + } else { ++ /* Shift 4 bytes for DSA tag or 8 bytes for EDSA tag*/ ++ mvpp2_prs_sram_shift_set(&pe, shift, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ + /* Set result info bits to 'no vlans' */ + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, + MVPP2_PRS_RI_VLAN_MASK); +@@ -1615,11 +2149,10 @@ static void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2 *priv, int port, + + if (priv->prs_shadow[tid].valid) { + /* Entry exist - update port only */ +- pe.index = tid; +- mvpp2_prs_hw_read(priv, &pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + } else { + /* Entry doesn't exist - create new */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); + pe.index = tid; + +@@ -1664,17 +2197,11 @@ static void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2 *priv, int port, + } + + /* Search for existing single/triple vlan entry */ +-static struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2 *priv, +- unsigned short tpid, int ai) ++static int mvpp2_prs_vlan_find(struct mvpp2 *priv, unsigned short tpid, int ai) + { +- struct mvpp2_prs_entry *pe; ++ struct mvpp2_prs_entry pe; + int tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return NULL; +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); +- + /* Go through the all entries with MVPP2_PRS_LU_VLAN */ + for (tid = MVPP2_PE_FIRST_FREE_TID; + tid <= MVPP2_PE_LAST_FREE_TID; tid++) { +@@ -1685,19 +2212,17 @@ static struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2 *priv, + priv->prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN) + continue; + +- pe->index = tid; +- +- mvpp2_prs_hw_read(priv, pe); +- match = mvpp2_prs_tcam_data_cmp(pe, 0, swab16(tpid)); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); ++ match = mvpp2_prs_tcam_data_cmp(&pe, 0, swab16(tpid)); + if (!match) + continue; + + /* Get vlan type */ +- ri_bits = mvpp2_prs_sram_ri_get(pe); ++ ri_bits = mvpp2_prs_sram_ri_get(&pe); + ri_bits &= MVPP2_PRS_RI_VLAN_MASK; + + /* Get current ai value from tcam */ +- ai_bits = mvpp2_prs_tcam_ai_get(pe); ++ ai_bits = mvpp2_prs_tcam_ai_get(&pe); + /* Clear double vlan bit */ + ai_bits &= ~MVPP2_PRS_DBL_VLAN_AI_BIT; + +@@ -1706,34 +2231,31 @@ static struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2 *priv, + + if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE || + ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE) +- return pe; ++ return tid; + } +- kfree(pe); + +- return NULL; ++ return -ENOENT; + } + + /* Add/update single/triple vlan entry */ + static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai, + unsigned int port_map) + { +- struct mvpp2_prs_entry *pe; ++ struct mvpp2_prs_entry pe; + int tid_aux, tid; + int ret = 0; + +- pe = mvpp2_prs_vlan_find(priv, tpid, ai); ++ memset(&pe, 0, sizeof(pe)); + +- if (!pe) { ++ tid = mvpp2_prs_vlan_find(priv, tpid, ai); ++ ++ if (tid < 0) { + /* Create new tcam entry */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_LAST_FREE_TID, + MVPP2_PE_FIRST_FREE_TID); + if (tid < 0) + return tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return -ENOMEM; +- + /* Get last double vlan tid */ + for (tid_aux = MVPP2_PE_LAST_FREE_TID; + tid_aux >= MVPP2_PE_FIRST_FREE_TID; tid_aux--) { +@@ -1743,51 +2265,46 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai, + priv->prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN) + continue; + +- pe->index = tid_aux; +- mvpp2_prs_hw_read(priv, pe); +- ri_bits = mvpp2_prs_sram_ri_get(pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid_aux); ++ ri_bits = mvpp2_prs_sram_ri_get(&pe); + if ((ri_bits & MVPP2_PRS_RI_VLAN_MASK) == + MVPP2_PRS_RI_VLAN_DOUBLE) + break; + } + +- if (tid <= tid_aux) { +- ret = -EINVAL; +- goto error; +- } ++ if (tid <= tid_aux) ++ return -EINVAL; + +- memset(pe, 0 , sizeof(struct mvpp2_prs_entry)); +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); +- pe->index = tid; ++ memset(&pe, 0, sizeof(pe)); ++ pe.index = tid; ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); + +- mvpp2_prs_match_etype(pe, 0, tpid); ++ mvpp2_prs_match_etype(&pe, 0, tpid); ++ ++ /* VLAN tag detected, proceed with VID filtering */ ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID); + +- mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_L2); +- /* Shift 4 bytes - skip 1 vlan tag */ +- mvpp2_prs_sram_shift_set(pe, MVPP2_VLAN_TAG_LEN, +- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + /* Clear all ai bits for next iteration */ +- mvpp2_prs_sram_ai_update(pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); + + if (ai == MVPP2_PRS_SINGLE_VLAN_AI) { +- mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_SINGLE, ++ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_SINGLE, + MVPP2_PRS_RI_VLAN_MASK); + } else { + ai |= MVPP2_PRS_DBL_VLAN_AI_BIT; +- mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_TRIPLE, ++ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_TRIPLE, + MVPP2_PRS_RI_VLAN_MASK); + } +- mvpp2_prs_tcam_ai_update(pe, ai, MVPP2_PRS_SRAM_AI_MASK); ++ mvpp2_prs_tcam_ai_update(&pe, ai, MVPP2_PRS_SRAM_AI_MASK); + +- mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_VLAN); ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VLAN); ++ } else { ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + } + /* Update ports' mask */ +- mvpp2_prs_tcam_port_map_set(pe, port_map); ++ mvpp2_prs_tcam_port_map_set(&pe, port_map); + +- mvpp2_prs_hw_write(priv, pe); +- +-error: +- kfree(pe); ++ mvpp2_prs_hw_write(priv, &pe); + + return ret; + } +@@ -1806,18 +2323,12 @@ static int mvpp2_prs_double_vlan_ai_free_get(struct mvpp2 *priv) + } + + /* Search for existing double vlan entry */ +-static struct mvpp2_prs_entry *mvpp2_prs_double_vlan_find(struct mvpp2 *priv, +- unsigned short tpid1, +- unsigned short tpid2) ++static int mvpp2_prs_double_vlan_find(struct mvpp2 *priv, unsigned short tpid1, ++ unsigned short tpid2) + { +- struct mvpp2_prs_entry *pe; ++ struct mvpp2_prs_entry pe; + int tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return NULL; +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); +- + /* Go through the all entries with MVPP2_PRS_LU_VLAN */ + for (tid = MVPP2_PE_FIRST_FREE_TID; + tid <= MVPP2_PE_LAST_FREE_TID; tid++) { +@@ -1828,22 +2339,20 @@ static struct mvpp2_prs_entry *mvpp2_prs_double_vlan_find(struct mvpp2 *priv, + priv->prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN) + continue; + +- pe->index = tid; +- mvpp2_prs_hw_read(priv, pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + +- match = mvpp2_prs_tcam_data_cmp(pe, 0, swab16(tpid1)) +- && mvpp2_prs_tcam_data_cmp(pe, 4, swab16(tpid2)); ++ match = mvpp2_prs_tcam_data_cmp(&pe, 0, swab16(tpid1)) && ++ mvpp2_prs_tcam_data_cmp(&pe, 4, swab16(tpid2)); + + if (!match) + continue; + +- ri_mask = mvpp2_prs_sram_ri_get(pe) & MVPP2_PRS_RI_VLAN_MASK; ++ ri_mask = mvpp2_prs_sram_ri_get(&pe) & MVPP2_PRS_RI_VLAN_MASK; + if (ri_mask == MVPP2_PRS_RI_VLAN_DOUBLE) +- return pe; ++ return tid; + } +- kfree(pe); + +- return NULL; ++ return -ENOENT; + } + + /* Add or update double vlan entry */ +@@ -1851,28 +2360,24 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1, + unsigned short tpid2, + unsigned int port_map) + { +- struct mvpp2_prs_entry *pe; + int tid_aux, tid, ai, ret = 0; ++ struct mvpp2_prs_entry pe; ++ ++ memset(&pe, 0, sizeof(pe)); + +- pe = mvpp2_prs_double_vlan_find(priv, tpid1, tpid2); ++ tid = mvpp2_prs_double_vlan_find(priv, tpid1, tpid2); + +- if (!pe) { ++ if (tid < 0) { + /* Create new tcam entry */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return -ENOMEM; +- + /* Set ai value for new double vlan entry */ + ai = mvpp2_prs_double_vlan_ai_free_get(priv); +- if (ai < 0) { +- ret = ai; +- goto error; +- } ++ if (ai < 0) ++ return ai; + + /* Get first single/triple vlan tid */ + for (tid_aux = MVPP2_PE_FIRST_FREE_TID; +@@ -1883,47 +2388,44 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1, + priv->prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN) + continue; + +- pe->index = tid_aux; +- mvpp2_prs_hw_read(priv, pe); +- ri_bits = mvpp2_prs_sram_ri_get(pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid_aux); ++ ri_bits = mvpp2_prs_sram_ri_get(&pe); + ri_bits &= MVPP2_PRS_RI_VLAN_MASK; + if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE || + ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE) + break; + } + +- if (tid >= tid_aux) { +- ret = -ERANGE; +- goto error; +- } ++ if (tid >= tid_aux) ++ return -ERANGE; + +- memset(pe, 0, sizeof(struct mvpp2_prs_entry)); +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); +- pe->index = tid; ++ memset(&pe, 0, sizeof(pe)); ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ pe.index = tid; + + priv->prs_double_vlans[ai] = true; + +- mvpp2_prs_match_etype(pe, 0, tpid1); +- mvpp2_prs_match_etype(pe, 4, tpid2); ++ mvpp2_prs_match_etype(&pe, 0, tpid1); ++ mvpp2_prs_match_etype(&pe, 4, tpid2); + +- mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_VLAN); +- /* Shift 8 bytes - skip 2 vlan tags */ +- mvpp2_prs_sram_shift_set(pe, 2 * MVPP2_VLAN_TAG_LEN, ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ /* Shift 4 bytes - skip outer vlan tag */ ++ mvpp2_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_LEN, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); +- mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_DOUBLE, ++ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_DOUBLE, + MVPP2_PRS_RI_VLAN_MASK); +- mvpp2_prs_sram_ai_update(pe, ai | MVPP2_PRS_DBL_VLAN_AI_BIT, ++ mvpp2_prs_sram_ai_update(&pe, ai | MVPP2_PRS_DBL_VLAN_AI_BIT, + MVPP2_PRS_SRAM_AI_MASK); + +- mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_VLAN); ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VLAN); ++ } else { ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + } + + /* Update ports' mask */ +- mvpp2_prs_tcam_port_map_set(pe, port_map); +- mvpp2_prs_hw_write(priv, pe); ++ mvpp2_prs_tcam_port_map_set(&pe, port_map); ++ mvpp2_prs_hw_write(priv, &pe); + +-error: +- kfree(pe); + return ret; + } + +@@ -1938,13 +2440,13 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, + (proto != IPPROTO_IGMP)) + return -EINVAL; + +- /* Fragmented packet */ ++ /* Not fragmented packet */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); + pe.index = tid; + +@@ -1957,8 +2459,12 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, + MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, + MVPP2_PRS_IPV4_DIP_AI_BIT); +- mvpp2_prs_sram_ri_update(&pe, ri | MVPP2_PRS_RI_IP_FRAG_MASK, +- ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); ++ mvpp2_prs_sram_ri_update(&pe, ri, ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); ++ ++ mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00, ++ MVPP2_PRS_TCAM_PROTO_MASK_L); ++ mvpp2_prs_tcam_data_byte_set(&pe, 3, 0x00, ++ MVPP2_PRS_TCAM_PROTO_MASK); + + mvpp2_prs_tcam_data_byte_set(&pe, 5, proto, MVPP2_PRS_TCAM_PROTO_MASK); + mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); +@@ -1969,7 +2475,7 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_IP4); + mvpp2_prs_hw_write(priv, &pe); + +- /* Not fragmented packet */ ++ /* Fragmented packet */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) +@@ -1981,8 +2487,11 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, + pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; + mvpp2_prs_sram_ri_update(&pe, ri, ri_mask); + +- mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00, MVPP2_PRS_TCAM_PROTO_MASK_L); +- mvpp2_prs_tcam_data_byte_set(&pe, 3, 0x00, MVPP2_PRS_TCAM_PROTO_MASK); ++ mvpp2_prs_sram_ri_update(&pe, ri | MVPP2_PRS_RI_IP_FRAG_TRUE, ++ ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); ++ ++ mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00, 0x0); ++ mvpp2_prs_tcam_data_byte_set(&pe, 3, 0x00, 0x0); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_IP4); +@@ -2002,7 +2511,7 @@ static int mvpp2_prs_ip4_cast(struct mvpp2 *priv, unsigned short l3_cast) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); + pe.index = tid; + +@@ -2058,7 +2567,7 @@ static int mvpp2_prs_ip6_proto(struct mvpp2 *priv, unsigned short proto, + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); + pe.index = tid; + +@@ -2097,7 +2606,7 @@ static int mvpp2_prs_ip6_cast(struct mvpp2 *priv, unsigned short l3_cast) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); + pe.index = tid; + +@@ -2157,7 +2666,7 @@ static void mvpp2_prs_def_flow_init(struct mvpp2 *priv) + int port; + + for (port = 0; port < MVPP2_MAX_PORTS; port++) { +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + pe.index = MVPP2_PE_FIRST_DEFAULT_FLOW - port; + +@@ -2179,7 +2688,7 @@ static void mvpp2_prs_mh_init(struct mvpp2 *priv) + { + struct mvpp2_prs_entry pe; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + + pe.index = MVPP2_PE_MH_DEFAULT; + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MH); +@@ -2202,7 +2711,7 @@ static void mvpp2_prs_mac_init(struct mvpp2 *priv) + { + struct mvpp2_prs_entry pe; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + + /* Non-promiscuous mode for all ports - DROP unknown packets */ + pe.index = MVPP2_PE_MAC_NON_PROMISCUOUS; +@@ -2220,11 +2729,10 @@ static void mvpp2_prs_mac_init(struct mvpp2 *priv) + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); + mvpp2_prs_hw_write(priv, &pe); + +- /* place holders only - no ports */ ++ /* Create dummy entries for drop all and promiscuous modes */ + mvpp2_prs_mac_drop_all_set(priv, 0, false); +- mvpp2_prs_mac_promisc_set(priv, 0, false); +- mvpp2_prs_mac_multi_set(priv, MVPP2_PE_MAC_MC_ALL, 0, false); +- mvpp2_prs_mac_multi_set(priv, MVPP2_PE_MAC_MC_IP6, 0, false); ++ mvpp2_prs_mac_promisc_set(priv, 0, MVPP2_PRS_L2_UNI_CAST, false); ++ mvpp2_prs_mac_promisc_set(priv, 0, MVPP2_PRS_L2_MULTI_CAST, false); + } + + /* Set default entries for various types of dsa packets */ +@@ -2263,7 +2771,7 @@ static void mvpp2_prs_dsa_init(struct mvpp2 *priv) + MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); + + /* Set default entry, in case DSA or EDSA tag not found */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); + pe.index = MVPP2_PE_DSA_DEFAULT; + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); +@@ -2281,27 +2789,83 @@ static void mvpp2_prs_dsa_init(struct mvpp2 *priv) + mvpp2_prs_hw_write(priv, &pe); + } + +-/* Match basic ethertypes */ +-static int mvpp2_prs_etype_init(struct mvpp2 *priv) ++/* Initialize parser entries for VID filtering */ ++static void mvpp2_prs_vid_init(struct mvpp2 *priv) + { + struct mvpp2_prs_entry pe; +- int tid; + +- /* Ethertype: PPPoE */ +- tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, +- MVPP2_PE_LAST_FREE_TID); +- if (tid < 0) +- return tid; ++ memset(&pe, 0, sizeof(pe)); + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); +- mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); +- pe.index = tid; ++ /* Set default vid entry */ ++ pe.index = MVPP2_PE_VID_FLTR_DEFAULT; ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID); + +- mvpp2_prs_match_etype(&pe, 0, ETH_P_PPP_SES); ++ mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_EDSA_VID_AI_BIT); + +- mvpp2_prs_sram_shift_set(&pe, MVPP2_PPPOE_HDR_SIZE, ++ /* Skip VLAN header - Set offset to 4 bytes */ ++ mvpp2_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_LEN, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); +- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_PPPOE); ++ ++ /* Clear all ai bits for next iteration */ ++ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ ++ /* Unmask all ports */ ++ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID); ++ mvpp2_prs_hw_write(priv, &pe); ++ ++ /* Set default vid entry for extended DSA*/ ++ memset(&pe, 0, sizeof(pe)); ++ ++ /* Set default vid entry */ ++ pe.index = MVPP2_PE_VID_EDSA_FLTR_DEFAULT; ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID); ++ ++ mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_EDSA_VID_AI_BIT, ++ MVPP2_PRS_EDSA_VID_AI_BIT); ++ ++ /* Skip VLAN header - Set offset to 8 bytes */ ++ mvpp2_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_EDSA_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Clear all ai bits for next iteration */ ++ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ ++ /* Unmask all ports */ ++ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID); ++ mvpp2_prs_hw_write(priv, &pe); ++} ++ ++/* Match basic ethertypes */ ++static int mvpp2_prs_etype_init(struct mvpp2 *priv) ++{ ++ struct mvpp2_prs_entry pe; ++ int tid; ++ ++ /* Ethertype: PPPoE */ ++ tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(pe)); ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = tid; ++ ++ mvpp2_prs_match_etype(&pe, 0, ETH_P_PPP_SES); ++ ++ mvpp2_prs_sram_shift_set(&pe, MVPP2_PPPOE_HDR_SIZE, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_PPPOE); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_PPPOE_MASK, + MVPP2_PRS_RI_PPPOE_MASK); + +@@ -2319,7 +2883,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + +@@ -2349,7 +2913,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + +@@ -2383,7 +2947,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + +@@ -2448,7 +3012,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + +@@ -2545,11 +3109,12 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv) + return err; + + /* Set default double vlan entry */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); + pe.index = MVPP2_PE_VLAN_DBL; + +- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID); ++ + /* Clear ai for next iterations */ + mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_DOUBLE, +@@ -2565,7 +3130,7 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv) + mvpp2_prs_hw_write(priv, &pe); + + /* Set default vlan none entry */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); + pe.index = MVPP2_PE_VLAN_NONE; + +@@ -2595,7 +3160,7 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); + pe.index = tid; + +@@ -2645,7 +3210,7 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); + pe.index = tid; + +@@ -2672,7 +3237,7 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); + pe.index = tid; + +@@ -2730,7 +3295,7 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv) + return err; + + /* Default IPv4 entry for unknown protocols */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); + pe.index = MVPP2_PE_IP4_PROTO_UN; + +@@ -2755,7 +3320,7 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv) + mvpp2_prs_hw_write(priv, &pe); + + /* Default IPv4 entry for unicast address */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); + pe.index = MVPP2_PE_IP4_ADDR_UN; + +@@ -2823,7 +3388,7 @@ static int mvpp2_prs_ip6_init(struct mvpp2 *priv) + if (tid < 0) + return tid; + +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); + pe.index = tid; + +@@ -2844,7 +3409,7 @@ static int mvpp2_prs_ip6_init(struct mvpp2 *priv) + mvpp2_prs_hw_write(priv, &pe); + + /* Default IPv6 entry for unknown protocols */ +- memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); ++ memset(&pe, 0, sizeof(pe)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); + pe.index = MVPP2_PE_IP6_PROTO_UN; + +@@ -2912,6 +3477,192 @@ static int mvpp2_prs_ip6_init(struct mvpp2 *priv) + return 0; + } + ++/* Find tcam entry with matched pair */ ++static int mvpp2_prs_vid_range_find(struct mvpp2 *priv, int pmap, u16 vid, ++ u16 mask) ++{ ++ unsigned char byte[2], enable[2]; ++ struct mvpp2_prs_entry pe; ++ u16 rvid, rmask; ++ int tid; ++ ++ /* Go through the all entries with MVPP2_PRS_LU_VID */ ++ for (tid = MVPP2_PE_VID_FILT_RANGE_START; ++ tid <= MVPP2_PE_VID_FILT_RANGE_END; tid++) { ++ if (!priv->prs_shadow[tid].valid || ++ priv->prs_shadow[tid].lu != MVPP2_PRS_LU_VID) ++ continue; ++ ++ mvpp2_prs_init_from_hw(priv, &pe, tid); ++ ++ mvpp2_prs_tcam_data_byte_get(&pe, 2, &byte[0], &enable[0]); ++ mvpp2_prs_tcam_data_byte_get(&pe, 3, &byte[1], &enable[1]); ++ ++ rvid = ((byte[0] & 0xf) << 8) + byte[1]; ++ rmask = ((enable[0] & 0xf) << 8) + enable[1]; ++ ++ if (rvid != vid || rmask != mask) ++ continue; ++ ++ return tid; ++ } ++ ++ return -ENOENT; ++} ++ ++/* Write parser entry for VID filtering */ ++static int mvpp2_prs_vid_entry_add(struct mvpp2_port *port, u16 vid) ++{ ++ unsigned int vid_start = MVPP2_PE_VID_FILT_RANGE_START + ++ port->id * MVPP2_PRS_VLAN_FILT_MAX; ++ unsigned int mask = 0xfff, reg_val, shift; ++ struct mvpp2 *priv = port->priv; ++ struct mvpp2_prs_entry pe; ++ int tid; ++ ++ memset(&pe, 0, sizeof(pe)); ++ ++ /* Scan TCAM and see if entry with this already exist */ ++ tid = mvpp2_prs_vid_range_find(priv, (1 << port->id), vid, mask); ++ ++ reg_val = mvpp2_read(priv, MVPP2_MH_REG(port->id)); ++ if (reg_val & MVPP2_DSA_EXTENDED) ++ shift = MVPP2_VLAN_TAG_EDSA_LEN; ++ else ++ shift = MVPP2_VLAN_TAG_LEN; ++ ++ /* No such entry */ ++ if (tid < 0) { ++ ++ /* Go through all entries from first to last in vlan range */ ++ tid = mvpp2_prs_tcam_first_free(priv, vid_start, ++ vid_start + ++ MVPP2_PRS_VLAN_FILT_MAX_ENTRY); ++ ++ /* There isn't room for a new VID filter */ ++ if (tid < 0) ++ return tid; ++ ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID); ++ pe.index = tid; ++ ++ /* Mask all ports */ ++ mvpp2_prs_tcam_port_map_set(&pe, 0); ++ } else { ++ mvpp2_prs_init_from_hw(priv, &pe, tid); ++ } ++ ++ /* Enable the current port */ ++ mvpp2_prs_tcam_port_set(&pe, port->id, true); ++ ++ /* Continue - set next lookup */ ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ ++ /* Skip VLAN header - Set offset to 4 or 8 bytes */ ++ mvpp2_prs_sram_shift_set(&pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Set match on VID */ ++ mvpp2_prs_match_vid(&pe, MVPP2_PRS_VID_TCAM_BYTE, vid); ++ ++ /* Clear all ai bits for next iteration */ ++ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* Update shadow table */ ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID); ++ mvpp2_prs_hw_write(priv, &pe); ++ ++ return 0; ++} ++ ++/* Write parser entry for VID filtering */ ++static void mvpp2_prs_vid_entry_remove(struct mvpp2_port *port, u16 vid) ++{ ++ struct mvpp2 *priv = port->priv; ++ int tid; ++ ++ /* Scan TCAM and see if entry with this already exist */ ++ tid = mvpp2_prs_vid_range_find(priv, (1 << port->id), vid, 0xfff); ++ ++ /* No such entry */ ++ if (tid < 0) ++ return; ++ ++ mvpp2_prs_hw_inv(priv, tid); ++ priv->prs_shadow[tid].valid = false; ++} ++ ++/* Remove all existing VID filters on this port */ ++static void mvpp2_prs_vid_remove_all(struct mvpp2_port *port) ++{ ++ struct mvpp2 *priv = port->priv; ++ int tid; ++ ++ for (tid = MVPP2_PRS_VID_PORT_FIRST(port->id); ++ tid <= MVPP2_PRS_VID_PORT_LAST(port->id); tid++) { ++ if (priv->prs_shadow[tid].valid) ++ mvpp2_prs_vid_entry_remove(port, tid); ++ } ++} ++ ++/* Remove VID filering entry for this port */ ++static void mvpp2_prs_vid_disable_filtering(struct mvpp2_port *port) ++{ ++ unsigned int tid = MVPP2_PRS_VID_PORT_DFLT(port->id); ++ struct mvpp2 *priv = port->priv; ++ ++ /* Invalidate the guard entry */ ++ mvpp2_prs_hw_inv(priv, tid); ++ ++ priv->prs_shadow[tid].valid = false; ++} ++ ++/* Add guard entry that drops packets when no VID is matched on this port */ ++static void mvpp2_prs_vid_enable_filtering(struct mvpp2_port *port) ++{ ++ unsigned int tid = MVPP2_PRS_VID_PORT_DFLT(port->id); ++ struct mvpp2 *priv = port->priv; ++ unsigned int reg_val, shift; ++ struct mvpp2_prs_entry pe; ++ ++ if (priv->prs_shadow[tid].valid) ++ return; ++ ++ memset(&pe, 0, sizeof(pe)); ++ ++ pe.index = tid; ++ ++ reg_val = mvpp2_read(priv, MVPP2_MH_REG(port->id)); ++ if (reg_val & MVPP2_DSA_EXTENDED) ++ shift = MVPP2_VLAN_TAG_EDSA_LEN; ++ else ++ shift = MVPP2_VLAN_TAG_LEN; ++ ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID); ++ ++ /* Mask all ports */ ++ mvpp2_prs_tcam_port_map_set(&pe, 0); ++ ++ /* Update port mask */ ++ mvpp2_prs_tcam_port_set(&pe, port->id, true); ++ ++ /* Continue - set next lookup */ ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ ++ /* Skip VLAN header - Set offset to 4 or 8 bytes */ ++ mvpp2_prs_sram_shift_set(&pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Drop VLAN packets that don't belong to any VIDs on this port */ ++ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, ++ MVPP2_PRS_RI_DROP_MASK); ++ ++ /* Clear all ai bits for next iteration */ ++ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* Update shadow table */ ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID); ++ mvpp2_prs_hw_write(priv, &pe); ++} ++ + /* Parser default initialization */ + static int mvpp2_prs_default_init(struct platform_device *pdev, + struct mvpp2 *priv) +@@ -2937,7 +3688,7 @@ static int mvpp2_prs_default_init(struct platform_device *pdev, + mvpp2_prs_hw_inv(priv, index); + + priv->prs_shadow = devm_kcalloc(&pdev->dev, MVPP2_PRS_TCAM_SRAM_SIZE, +- sizeof(struct mvpp2_prs_shadow), ++ sizeof(*priv->prs_shadow), + GFP_KERNEL); + if (!priv->prs_shadow) + return -ENOMEM; +@@ -2955,6 +3706,8 @@ static int mvpp2_prs_default_init(struct platform_device *pdev, + + mvpp2_prs_dsa_init(priv); + ++ mvpp2_prs_vid_init(priv); ++ + err = mvpp2_prs_etype_init(priv); + if (err) + return err; +@@ -2998,21 +3751,16 @@ static bool mvpp2_prs_mac_range_equals(struct mvpp2_prs_entry *pe, + } + + /* Find tcam entry with matched pair */ +-static struct mvpp2_prs_entry * ++static int + mvpp2_prs_mac_da_range_find(struct mvpp2 *priv, int pmap, const u8 *da, + unsigned char *mask, int udf_type) + { +- struct mvpp2_prs_entry *pe; ++ struct mvpp2_prs_entry pe; + int tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return NULL; +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC); +- + /* Go through the all entires with MVPP2_PRS_LU_MAC */ +- for (tid = MVPP2_PE_FIRST_FREE_TID; +- tid <= MVPP2_PE_LAST_FREE_TID; tid++) { ++ for (tid = MVPP2_PE_MAC_RANGE_START; ++ tid <= MVPP2_PE_MAC_RANGE_END; tid++) { + unsigned int entry_pmap; + + if (!priv->prs_shadow[tid].valid || +@@ -3020,110 +3768,103 @@ static bool mvpp2_prs_mac_range_equals(struct mvpp2_prs_entry *pe, + (priv->prs_shadow[tid].udf != udf_type)) + continue; + +- pe->index = tid; +- mvpp2_prs_hw_read(priv, pe); +- entry_pmap = mvpp2_prs_tcam_port_map_get(pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); ++ entry_pmap = mvpp2_prs_tcam_port_map_get(&pe); + +- if (mvpp2_prs_mac_range_equals(pe, da, mask) && ++ if (mvpp2_prs_mac_range_equals(&pe, da, mask) && + entry_pmap == pmap) +- return pe; ++ return tid; + } +- kfree(pe); + +- return NULL; ++ return -ENOENT; + } + + /* Update parser's mac da entry */ +-static int mvpp2_prs_mac_da_accept(struct mvpp2 *priv, int port, +- const u8 *da, bool add) ++static int mvpp2_prs_mac_da_accept(struct mvpp2_port *port, const u8 *da, ++ bool add) + { +- struct mvpp2_prs_entry *pe; +- unsigned int pmap, len, ri; + unsigned char mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ struct mvpp2 *priv = port->priv; ++ unsigned int pmap, len, ri; ++ struct mvpp2_prs_entry pe; + int tid; + ++ memset(&pe, 0, sizeof(pe)); ++ + /* Scan TCAM and see if entry with this already exist */ +- pe = mvpp2_prs_mac_da_range_find(priv, (1 << port), da, mask, +- MVPP2_PRS_UDF_MAC_DEF); ++ tid = mvpp2_prs_mac_da_range_find(priv, BIT(port->id), da, mask, ++ MVPP2_PRS_UDF_MAC_DEF); + + /* No such entry */ +- if (!pe) { ++ if (tid < 0) { + if (!add) + return 0; + + /* Create new TCAM entry */ +- /* Find first range mac entry*/ +- for (tid = MVPP2_PE_FIRST_FREE_TID; +- tid <= MVPP2_PE_LAST_FREE_TID; tid++) +- if (priv->prs_shadow[tid].valid && +- (priv->prs_shadow[tid].lu == MVPP2_PRS_LU_MAC) && +- (priv->prs_shadow[tid].udf == +- MVPP2_PRS_UDF_MAC_RANGE)) +- break; +- + /* Go through the all entries from first to last */ +- tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, +- tid - 1); ++ tid = mvpp2_prs_tcam_first_free(priv, ++ MVPP2_PE_MAC_RANGE_START, ++ MVPP2_PE_MAC_RANGE_END); + if (tid < 0) + return tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return -ENOMEM; +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC); +- pe->index = tid; ++ pe.index = tid; + + /* Mask all ports */ +- mvpp2_prs_tcam_port_map_set(pe, 0); ++ mvpp2_prs_tcam_port_map_set(&pe, 0); ++ } else { ++ mvpp2_prs_init_from_hw(priv, &pe, tid); + } + ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ + /* Update port mask */ +- mvpp2_prs_tcam_port_set(pe, port, add); ++ mvpp2_prs_tcam_port_set(&pe, port->id, add); + + /* Invalidate the entry if no ports are left enabled */ +- pmap = mvpp2_prs_tcam_port_map_get(pe); ++ pmap = mvpp2_prs_tcam_port_map_get(&pe); + if (pmap == 0) { +- if (add) { +- kfree(pe); ++ if (add) + return -EINVAL; +- } +- mvpp2_prs_hw_inv(priv, pe->index); +- priv->prs_shadow[pe->index].valid = false; +- kfree(pe); ++ ++ mvpp2_prs_hw_inv(priv, pe.index); ++ priv->prs_shadow[pe.index].valid = false; + return 0; + } + + /* Continue - set next lookup */ +- mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_DSA); ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); + + /* Set match on DA */ + len = ETH_ALEN; + while (len--) +- mvpp2_prs_tcam_data_byte_set(pe, len, da[len], 0xff); ++ mvpp2_prs_tcam_data_byte_set(&pe, len, da[len], 0xff); + + /* Set result info bits */ +- if (is_broadcast_ether_addr(da)) ++ if (is_broadcast_ether_addr(da)) { + ri = MVPP2_PRS_RI_L2_BCAST; +- else if (is_multicast_ether_addr(da)) ++ } else if (is_multicast_ether_addr(da)) { + ri = MVPP2_PRS_RI_L2_MCAST; +- else +- ri = MVPP2_PRS_RI_L2_UCAST | MVPP2_PRS_RI_MAC_ME_MASK; ++ } else { ++ ri = MVPP2_PRS_RI_L2_UCAST; ++ ++ if (ether_addr_equal(da, port->dev->dev_addr)) ++ ri |= MVPP2_PRS_RI_MAC_ME_MASK; ++ } + +- mvpp2_prs_sram_ri_update(pe, ri, MVPP2_PRS_RI_L2_CAST_MASK | ++ mvpp2_prs_sram_ri_update(&pe, ri, MVPP2_PRS_RI_L2_CAST_MASK | + MVPP2_PRS_RI_MAC_ME_MASK); +- mvpp2_prs_shadow_ri_set(priv, pe->index, ri, MVPP2_PRS_RI_L2_CAST_MASK | ++ mvpp2_prs_shadow_ri_set(priv, pe.index, ri, MVPP2_PRS_RI_L2_CAST_MASK | + MVPP2_PRS_RI_MAC_ME_MASK); + + /* Shift to ethertype */ +- mvpp2_prs_sram_shift_set(pe, 2 * ETH_ALEN, ++ mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + + /* Update shadow table and hw entry */ +- priv->prs_shadow[pe->index].udf = MVPP2_PRS_UDF_MAC_DEF; +- mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_MAC); +- mvpp2_prs_hw_write(priv, pe); +- +- kfree(pe); ++ priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_MAC_DEF; ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); ++ mvpp2_prs_hw_write(priv, &pe); + + return 0; + } +@@ -3134,13 +3875,12 @@ static int mvpp2_prs_update_mac_da(struct net_device *dev, const u8 *da) + int err; + + /* Remove old parser entry */ +- err = mvpp2_prs_mac_da_accept(port->priv, port->id, dev->dev_addr, +- false); ++ err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, false); + if (err) + return err; + + /* Add new parser entry */ +- err = mvpp2_prs_mac_da_accept(port->priv, port->id, da, true); ++ err = mvpp2_prs_mac_da_accept(port, da, true); + if (err) + return err; + +@@ -3150,14 +3890,15 @@ static int mvpp2_prs_update_mac_da(struct net_device *dev, const u8 *da) + return 0; + } + +-/* Delete all port's multicast simple (not range) entries */ +-static void mvpp2_prs_mcast_del_all(struct mvpp2 *priv, int port) ++static void mvpp2_prs_mac_del_all(struct mvpp2_port *port) + { ++ struct mvpp2 *priv = port->priv; + struct mvpp2_prs_entry pe; ++ unsigned long pmap; + int index, tid; + +- for (tid = MVPP2_PE_FIRST_FREE_TID; +- tid <= MVPP2_PE_LAST_FREE_TID; tid++) { ++ for (tid = MVPP2_PE_MAC_RANGE_START; ++ tid <= MVPP2_PE_MAC_RANGE_END; tid++) { + unsigned char da[ETH_ALEN], da_mask[ETH_ALEN]; + + if (!priv->prs_shadow[tid].valid || +@@ -3165,18 +3906,28 @@ static void mvpp2_prs_mcast_del_all(struct mvpp2 *priv, int port) + (priv->prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF)) + continue; + +- /* Only simple mac entries */ +- pe.index = tid; +- mvpp2_prs_hw_read(priv, &pe); ++ mvpp2_prs_init_from_hw(priv, &pe, tid); ++ ++ pmap = mvpp2_prs_tcam_port_map_get(&pe); ++ ++ /* We only want entries active on this port */ ++ if (!test_bit(port->id, &pmap)) ++ continue; + + /* Read mac addr from entry */ + for (index = 0; index < ETH_ALEN; index++) + mvpp2_prs_tcam_data_byte_get(&pe, index, &da[index], + &da_mask[index]); + +- if (is_multicast_ether_addr(da) && !is_broadcast_ether_addr(da)) +- /* Delete this entry */ +- mvpp2_prs_mac_da_accept(priv, port, da, false); ++ /* Special cases : Don't remove broadcast and port's own ++ * address ++ */ ++ if (is_broadcast_ether_addr(da) || ++ ether_addr_equal(da, port->dev->dev_addr)) ++ continue; ++ ++ /* Remove entry from TCAM */ ++ mvpp2_prs_mac_da_accept(port, da, false); + } + } + +@@ -3233,13 +3984,15 @@ static int mvpp2_prs_tag_mode_set(struct mvpp2 *priv, int port, int type) + /* Set prs flow for the port */ + static int mvpp2_prs_def_flow(struct mvpp2_port *port) + { +- struct mvpp2_prs_entry *pe; ++ struct mvpp2_prs_entry pe; + int tid; + +- pe = mvpp2_prs_flow_find(port->priv, port->id); ++ memset(&pe, 0, sizeof(pe)); ++ ++ tid = mvpp2_prs_flow_find(port->priv, port->id); + + /* Such entry not exist */ +- if (!pe) { ++ if (tid < 0) { + /* Go through the all entires from last to first */ + tid = mvpp2_prs_tcam_first_free(port->priv, + MVPP2_PE_LAST_FREE_TID, +@@ -3247,24 +4000,21 @@ static int mvpp2_prs_def_flow(struct mvpp2_port *port) + if (tid < 0) + return tid; + +- pe = kzalloc(sizeof(*pe), GFP_KERNEL); +- if (!pe) +- return -ENOMEM; +- +- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); +- pe->index = tid; ++ pe.index = tid; + + /* Set flow ID*/ +- mvpp2_prs_sram_ai_update(pe, port->id, MVPP2_PRS_FLOW_ID_MASK); +- mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); ++ mvpp2_prs_sram_ai_update(&pe, port->id, MVPP2_PRS_FLOW_ID_MASK); ++ mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); + + /* Update shadow table */ +- mvpp2_prs_shadow_set(port->priv, pe->index, MVPP2_PRS_LU_FLOWS); ++ mvpp2_prs_shadow_set(port->priv, pe.index, MVPP2_PRS_LU_FLOWS); ++ } else { ++ mvpp2_prs_init_from_hw(port->priv, &pe, tid); + } + +- mvpp2_prs_tcam_port_map_set(pe, (1 << port->id)); +- mvpp2_prs_hw_write(port->priv, pe); +- kfree(pe); ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mvpp2_prs_tcam_port_map_set(&pe, (1 << port->id)); ++ mvpp2_prs_hw_write(port->priv, &pe); + + return 0; + } +@@ -3365,6 +4115,22 @@ static void mvpp2_cls_oversize_rxq_set(struct mvpp2_port *port) + mvpp2_write(port->priv, MVPP2_CLS_SWFWD_PCTRL_REG, val); + } + ++static void *mvpp2_frag_alloc(const struct mvpp2_bm_pool *pool) ++{ ++ if (likely(pool->frag_size <= PAGE_SIZE)) ++ return netdev_alloc_frag(pool->frag_size); ++ else ++ return kmalloc(pool->frag_size, GFP_ATOMIC); ++} ++ ++static void mvpp2_frag_free(const struct mvpp2_bm_pool *pool, void *data) ++{ ++ if (likely(pool->frag_size <= PAGE_SIZE)) ++ skb_free_frag(data); ++ else ++ kfree(data); ++} ++ + /* Buffer Manager configuration routines */ + + /* Create pool */ +@@ -3372,37 +4138,48 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev, + struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool, int size) + { +- int size_bytes; + u32 val; + +- size_bytes = sizeof(u32) * size; +- bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes, +- &bm_pool->phys_addr, ++ /* Number of buffer pointers must be a multiple of 16, as per ++ * hardware constraints ++ */ ++ if (!IS_ALIGNED(size, 16)) ++ return -EINVAL; ++ ++ /* PPv2.1 needs 8 bytes per buffer pointer, PPv2.2 needs 16 ++ * bytes per buffer pointer ++ */ ++ if (priv->hw_version == MVPP21) ++ bm_pool->size_bytes = 2 * sizeof(u32) * size; ++ else ++ bm_pool->size_bytes = 2 * sizeof(u64) * size; ++ ++ bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, bm_pool->size_bytes, ++ &bm_pool->dma_addr, + GFP_KERNEL); + if (!bm_pool->virt_addr) + return -ENOMEM; + +- if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVPP2_BM_POOL_PTR_ALIGN)) { +- dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, +- bm_pool->phys_addr); ++ if (!IS_ALIGNED((unsigned long)bm_pool->virt_addr, ++ MVPP2_BM_POOL_PTR_ALIGN)) { ++ dma_free_coherent(&pdev->dev, bm_pool->size_bytes, ++ bm_pool->virt_addr, bm_pool->dma_addr); + dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n", + bm_pool->id, MVPP2_BM_POOL_PTR_ALIGN); + return -ENOMEM; + } + + mvpp2_write(priv, MVPP2_BM_POOL_BASE_REG(bm_pool->id), +- bm_pool->phys_addr); ++ lower_32_bits(bm_pool->dma_addr)); + mvpp2_write(priv, MVPP2_BM_POOL_SIZE_REG(bm_pool->id), size); + + val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id)); + val |= MVPP2_BM_START_MASK; + mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val); + +- bm_pool->type = MVPP2_BM_FREE; + bm_pool->size = size; + bm_pool->pkt_size = 0; + bm_pool->buf_num = 0; +- atomic_set(&bm_pool->in_use, 0); + + return 0; + } +@@ -3420,43 +4197,103 @@ static void mvpp2_bm_pool_bufsize_set(struct mvpp2 *priv, + mvpp2_write(priv, MVPP2_POOL_BUF_SIZE_REG(bm_pool->id), val); + } + ++static void mvpp2_bm_bufs_get_addrs(struct device *dev, struct mvpp2 *priv, ++ struct mvpp2_bm_pool *bm_pool, ++ dma_addr_t *dma_addr, ++ phys_addr_t *phys_addr) ++{ ++ int cpu = get_cpu(); ++ ++ *dma_addr = mvpp2_percpu_read(priv, cpu, ++ MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); ++ *phys_addr = mvpp2_percpu_read(priv, cpu, MVPP2_BM_VIRT_ALLOC_REG); ++ ++ if (priv->hw_version == MVPP22) { ++ u32 val; ++ u32 dma_addr_highbits, phys_addr_highbits; ++ ++ val = mvpp2_percpu_read(priv, cpu, MVPP22_BM_ADDR_HIGH_ALLOC); ++ dma_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_PHYS_MASK); ++ phys_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_VIRT_MASK) >> ++ MVPP22_BM_ADDR_HIGH_VIRT_SHIFT; ++ ++ if (sizeof(dma_addr_t) == 8) ++ *dma_addr |= (u64)dma_addr_highbits << 32; ++ ++ if (sizeof(phys_addr_t) == 8) ++ *phys_addr |= (u64)phys_addr_highbits << 32; ++ } ++ ++ put_cpu(); ++} ++ + /* Free all buffers from the pool */ + static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv, +- struct mvpp2_bm_pool *bm_pool) ++ struct mvpp2_bm_pool *bm_pool, int buf_num) + { + int i; + +- for (i = 0; i < bm_pool->buf_num; i++) { +- dma_addr_t buf_phys_addr; +- u32 vaddr; ++ if (buf_num > bm_pool->buf_num) { ++ WARN(1, "Pool does not have so many bufs pool(%d) bufs(%d)\n", ++ bm_pool->id, buf_num); ++ buf_num = bm_pool->buf_num; ++ } ++ ++ for (i = 0; i < buf_num; i++) { ++ dma_addr_t buf_dma_addr; ++ phys_addr_t buf_phys_addr; ++ void *data; + +- /* Get buffer virtual address (indirect access) */ +- buf_phys_addr = mvpp2_read(priv, +- MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); +- vaddr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); ++ mvpp2_bm_bufs_get_addrs(dev, priv, bm_pool, ++ &buf_dma_addr, &buf_phys_addr); + +- dma_unmap_single(dev, buf_phys_addr, ++ dma_unmap_single(dev, buf_dma_addr, + bm_pool->buf_size, DMA_FROM_DEVICE); + +- if (!vaddr) ++ data = (void *)phys_to_virt(buf_phys_addr); ++ if (!data) + break; +- dev_kfree_skb_any((struct sk_buff *)vaddr); ++ ++ mvpp2_frag_free(bm_pool, data); + } + + /* Update BM driver with number of buffers removed from pool */ + bm_pool->buf_num -= i; + } + ++/* Check number of buffers in BM pool */ ++static int mvpp2_check_hw_buf_num(struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool) ++{ ++ int buf_num = 0; ++ ++ buf_num += mvpp2_read(priv, MVPP2_BM_POOL_PTRS_NUM_REG(bm_pool->id)) & ++ MVPP22_BM_POOL_PTRS_NUM_MASK; ++ buf_num += mvpp2_read(priv, MVPP2_BM_BPPI_PTRS_NUM_REG(bm_pool->id)) & ++ MVPP2_BM_BPPI_PTR_NUM_MASK; ++ ++ /* HW has one buffer ready which is not reflected in the counters */ ++ if (buf_num) ++ buf_num += 1; ++ ++ return buf_num; ++} ++ + /* Cleanup pool */ + static int mvpp2_bm_pool_destroy(struct platform_device *pdev, + struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool) + { ++ int buf_num; + u32 val; + +- mvpp2_bm_bufs_free(&pdev->dev, priv, bm_pool); +- if (bm_pool->buf_num) { +- WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id); ++ buf_num = mvpp2_check_hw_buf_num(priv, bm_pool); ++ mvpp2_bm_bufs_free(&pdev->dev, priv, bm_pool, buf_num); ++ ++ /* Check buffer counters after free */ ++ buf_num = mvpp2_check_hw_buf_num(priv, bm_pool); ++ if (buf_num) { ++ WARN(1, "cannot free all buffers in pool %d, buf_num left %d\n", ++ bm_pool->id, bm_pool->buf_num); + return 0; + } + +@@ -3464,9 +4301,9 @@ static int mvpp2_bm_pool_destroy(struct platform_device *pdev, + val |= MVPP2_BM_STOP_MASK; + mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val); + +- dma_free_coherent(&pdev->dev, sizeof(u32) * bm_pool->size, ++ dma_free_coherent(&pdev->dev, bm_pool->size_bytes, + bm_pool->virt_addr, +- bm_pool->phys_addr); ++ bm_pool->dma_addr); + return 0; + } + +@@ -3508,7 +4345,7 @@ static int mvpp2_bm_init(struct platform_device *pdev, struct mvpp2 *priv) + + /* Allocate and initialize BM pools */ + priv->bm_pools = devm_kcalloc(&pdev->dev, MVPP2_BM_POOLS_NUM, +- sizeof(struct mvpp2_bm_pool), GFP_KERNEL); ++ sizeof(*priv->bm_pools), GFP_KERNEL); + if (!priv->bm_pools) + return -ENOMEM; + +@@ -3518,21 +4355,39 @@ static int mvpp2_bm_init(struct platform_device *pdev, struct mvpp2 *priv) + return 0; + } + ++static void mvpp2_setup_bm_pool(void) ++{ ++ /* Short pool */ ++ mvpp2_pools[MVPP2_BM_SHORT].buf_num = MVPP2_BM_SHORT_BUF_NUM; ++ mvpp2_pools[MVPP2_BM_SHORT].pkt_size = MVPP2_BM_SHORT_PKT_SIZE; ++ ++ /* Long pool */ ++ mvpp2_pools[MVPP2_BM_LONG].buf_num = MVPP2_BM_LONG_BUF_NUM; ++ mvpp2_pools[MVPP2_BM_LONG].pkt_size = MVPP2_BM_LONG_PKT_SIZE; ++ ++ /* Jumbo pool */ ++ mvpp2_pools[MVPP2_BM_JUMBO].buf_num = MVPP2_BM_JUMBO_BUF_NUM; ++ mvpp2_pools[MVPP2_BM_JUMBO].pkt_size = MVPP2_BM_JUMBO_PKT_SIZE; ++} ++ + /* Attach long pool to rxq */ + static void mvpp2_rxq_long_pool_set(struct mvpp2_port *port, + int lrxq, int long_pool) + { +- u32 val; ++ u32 val, mask; + int prxq; + + /* Get queue physical ID */ + prxq = port->rxqs[lrxq]->id; + +- val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); +- val &= ~MVPP2_RXQ_POOL_LONG_MASK; +- val |= ((long_pool << MVPP2_RXQ_POOL_LONG_OFFS) & +- MVPP2_RXQ_POOL_LONG_MASK); ++ if (port->priv->hw_version == MVPP21) ++ mask = MVPP21_RXQ_POOL_LONG_MASK; ++ else ++ mask = MVPP22_RXQ_POOL_LONG_MASK; + ++ val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~mask; ++ val |= (long_pool << MVPP2_RXQ_POOL_LONG_OFFS) & mask; + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); + } + +@@ -3540,102 +4395,93 @@ static void mvpp2_rxq_long_pool_set(struct mvpp2_port *port, + static void mvpp2_rxq_short_pool_set(struct mvpp2_port *port, + int lrxq, int short_pool) + { +- u32 val; ++ u32 val, mask; + int prxq; + + /* Get queue physical ID */ + prxq = port->rxqs[lrxq]->id; + +- val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); +- val &= ~MVPP2_RXQ_POOL_SHORT_MASK; +- val |= ((short_pool << MVPP2_RXQ_POOL_SHORT_OFFS) & +- MVPP2_RXQ_POOL_SHORT_MASK); ++ if (port->priv->hw_version == MVPP21) ++ mask = MVPP21_RXQ_POOL_SHORT_MASK; ++ else ++ mask = MVPP22_RXQ_POOL_SHORT_MASK; + ++ val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~mask; ++ val |= (short_pool << MVPP2_RXQ_POOL_SHORT_OFFS) & mask; + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); + } + +-/* Allocate skb for BM pool */ +-static struct sk_buff *mvpp2_skb_alloc(struct mvpp2_port *port, +- struct mvpp2_bm_pool *bm_pool, +- dma_addr_t *buf_phys_addr, +- gfp_t gfp_mask) ++static void *mvpp2_buf_alloc(struct mvpp2_port *port, ++ struct mvpp2_bm_pool *bm_pool, ++ dma_addr_t *buf_dma_addr, ++ phys_addr_t *buf_phys_addr, ++ gfp_t gfp_mask) + { +- struct sk_buff *skb; +- dma_addr_t phys_addr; ++ dma_addr_t dma_addr; ++ void *data; + +- skb = __dev_alloc_skb(bm_pool->pkt_size, gfp_mask); +- if (!skb) ++ data = mvpp2_frag_alloc(bm_pool); ++ if (!data) + return NULL; + +- phys_addr = dma_map_single(port->dev->dev.parent, skb->head, +- MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), +- DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(port->dev->dev.parent, phys_addr))) { +- dev_kfree_skb_any(skb); ++ dma_addr = dma_map_single(port->dev->dev.parent, data, ++ MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), ++ DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(port->dev->dev.parent, dma_addr))) { ++ mvpp2_frag_free(bm_pool, data); + return NULL; + } +- *buf_phys_addr = phys_addr; +- +- return skb; +-} +- +-/* Set pool number in a BM cookie */ +-static inline u32 mvpp2_bm_cookie_pool_set(u32 cookie, int pool) +-{ +- u32 bm; +- +- bm = cookie & ~(0xFF << MVPP2_BM_COOKIE_POOL_OFFS); +- bm |= ((pool & 0xFF) << MVPP2_BM_COOKIE_POOL_OFFS); +- +- return bm; +-} ++ *buf_dma_addr = dma_addr; ++ *buf_phys_addr = virt_to_phys(data); + +-/* Get pool number from a BM cookie */ +-static inline int mvpp2_bm_cookie_pool_get(u32 cookie) +-{ +- return (cookie >> MVPP2_BM_COOKIE_POOL_OFFS) & 0xFF; ++ return data; + } + + /* Release buffer to BM */ + static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, +- u32 buf_phys_addr, u32 buf_virt_addr) ++ dma_addr_t buf_dma_addr, ++ phys_addr_t buf_phys_addr) + { +- mvpp2_write(port->priv, MVPP2_BM_VIRT_RLS_REG, buf_virt_addr); +- mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_phys_addr); +-} ++ int cpu = get_cpu(); + +-/* Release multicast buffer */ +-static void mvpp2_bm_pool_mc_put(struct mvpp2_port *port, int pool, +- u32 buf_phys_addr, u32 buf_virt_addr, +- int mc_id) +-{ +- u32 val = 0; ++ if (port->priv->hw_version == MVPP22) { ++ u32 val = 0; + +- val |= (mc_id & MVPP2_BM_MC_ID_MASK); +- mvpp2_write(port->priv, MVPP2_BM_MC_RLS_REG, val); ++ if (sizeof(dma_addr_t) == 8) ++ val |= upper_32_bits(buf_dma_addr) & ++ MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK; + +- mvpp2_bm_pool_put(port, pool, +- buf_phys_addr | MVPP2_BM_PHY_RLS_MC_BUFF_MASK, +- buf_virt_addr); +-} ++ if (sizeof(phys_addr_t) == 8) ++ val |= (upper_32_bits(buf_phys_addr) ++ << MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) & ++ MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK; + +-/* Refill BM pool */ +-static void mvpp2_pool_refill(struct mvpp2_port *port, u32 bm, +- u32 phys_addr, u32 cookie) +-{ +- int pool = mvpp2_bm_cookie_pool_get(bm); ++ mvpp2_percpu_write_relaxed(port->priv, cpu, ++ MVPP22_BM_ADDR_HIGH_RLS_REG, val); ++ } ++ ++ /* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply ++ * returned in the "cookie" field of the RX ++ * descriptor. Instead of storing the virtual address, we ++ * store the physical address ++ */ ++ mvpp2_percpu_write_relaxed(port->priv, cpu, ++ MVPP2_BM_VIRT_RLS_REG, buf_phys_addr); ++ mvpp2_percpu_write_relaxed(port->priv, cpu, ++ MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr); + +- mvpp2_bm_pool_put(port, pool, phys_addr, cookie); ++ put_cpu(); + } + + /* Allocate buffers for the pool */ + static int mvpp2_bm_bufs_add(struct mvpp2_port *port, + struct mvpp2_bm_pool *bm_pool, int buf_num) + { +- struct sk_buff *skb; + int i, buf_size, total_size; +- u32 bm; +- dma_addr_t phys_addr; ++ dma_addr_t dma_addr; ++ phys_addr_t phys_addr; ++ void *buf; + + buf_size = MVPP2_RX_BUF_SIZE(bm_pool->pkt_size); + total_size = MVPP2_RX_TOTAL_SIZE(buf_size); +@@ -3648,27 +4494,25 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, + return 0; + } + +- bm = mvpp2_bm_cookie_pool_set(0, bm_pool->id); + for (i = 0; i < buf_num; i++) { +- skb = mvpp2_skb_alloc(port, bm_pool, &phys_addr, GFP_KERNEL); +- if (!skb) ++ buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, ++ &phys_addr, GFP_KERNEL); ++ if (!buf) + break; + +- mvpp2_pool_refill(port, bm, (u32)phys_addr, (u32)skb); ++ mvpp2_bm_pool_put(port, bm_pool->id, dma_addr, ++ phys_addr); + } + + /* Update BM driver with number of buffers added to pool */ + bm_pool->buf_num += i; +- bm_pool->in_use_thresh = bm_pool->buf_num / 4; + + netdev_dbg(port->dev, +- "%s pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n", +- bm_pool->type == MVPP2_BM_SWF_SHORT ? "short" : " long", ++ "pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n", + bm_pool->id, bm_pool->pkt_size, buf_size, total_size); + + netdev_dbg(port->dev, +- "%s pool %d: %d of %d buffers added\n", +- bm_pool->type == MVPP2_BM_SWF_SHORT ? "short" : " long", ++ "pool %d: %d of %d buffers added\n", + bm_pool->id, i, buf_num); + return i; + } +@@ -3677,25 +4521,20 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, + * pool pointer on success + */ + static struct mvpp2_bm_pool * +-mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type, +- int pkt_size) ++mvpp2_bm_pool_use(struct mvpp2_port *port, unsigned pool, int pkt_size) + { + struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool]; + int num; + +- if (new_pool->type != MVPP2_BM_FREE && new_pool->type != type) { +- netdev_err(port->dev, "mixing pool types is forbidden\n"); ++ if (pool >= MVPP2_BM_POOLS_NUM) { ++ netdev_err(port->dev, "Invalid pool %d\n", pool); + return NULL; + } + +- if (new_pool->type == MVPP2_BM_FREE) +- new_pool->type = type; +- + /* Allocate buffers in case BM pool is used as long pool, but packet + * size doesn't match MTU or BM pool hasn't being used yet + */ +- if (((type == MVPP2_BM_SWF_LONG) && (pkt_size > new_pool->pkt_size)) || +- (new_pool->pkt_size == 0)) { ++ if (new_pool->pkt_size == 0) { + int pkts_num; + + /* Set default buffer number or free all the buffers in case +@@ -3703,14 +4542,15 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, + */ + pkts_num = new_pool->buf_num; + if (pkts_num == 0) +- pkts_num = type == MVPP2_BM_SWF_LONG ? +- MVPP2_BM_LONG_BUF_NUM : +- MVPP2_BM_SHORT_BUF_NUM; ++ pkts_num = mvpp2_pools[pool].buf_num; + else + mvpp2_bm_bufs_free(port->dev->dev.parent, +- port->priv, new_pool); ++ port->priv, new_pool, pkts_num); + + new_pool->pkt_size = pkt_size; ++ new_pool->frag_size = ++ SKB_DATA_ALIGN(MVPP2_RX_BUF_SIZE(pkt_size)) + ++ MVPP2_SKB_SHINFO_SIZE; + + /* Allocate buffers for this pool */ + num = mvpp2_bm_bufs_add(port, new_pool, pkts_num); +@@ -3731,32 +4571,43 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, + static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) + { + int rxq; ++ enum mvpp2_bm_pool_log_num long_log_pool, short_log_pool; ++ ++ /* If port pkt_size is higher than 1518B: ++ * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool ++ * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool ++ */ ++ if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) { ++ long_log_pool = MVPP2_BM_JUMBO; ++ short_log_pool = MVPP2_BM_LONG; ++ } else { ++ long_log_pool = MVPP2_BM_LONG; ++ short_log_pool = MVPP2_BM_SHORT; ++ } + + if (!port->pool_long) { + port->pool_long = +- mvpp2_bm_pool_use(port, MVPP2_BM_SWF_LONG_POOL(port->id), +- MVPP2_BM_SWF_LONG, +- port->pkt_size); ++ mvpp2_bm_pool_use(port, long_log_pool, ++ mvpp2_pools[long_log_pool].pkt_size); + if (!port->pool_long) + return -ENOMEM; + +- port->pool_long->port_map |= (1 << port->id); ++ port->pool_long->port_map |= BIT(port->id); + +- for (rxq = 0; rxq < rxq_number; rxq++) ++ for (rxq = 0; rxq < port->nrxqs; rxq++) + mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id); + } + + if (!port->pool_short) { + port->pool_short = +- mvpp2_bm_pool_use(port, MVPP2_BM_SWF_SHORT_POOL, +- MVPP2_BM_SWF_SHORT, +- MVPP2_BM_SHORT_PKT_SIZE); ++ mvpp2_bm_pool_use(port, short_log_pool, ++ mvpp2_pools[short_log_pool].pkt_size); + if (!port->pool_short) + return -ENOMEM; + +- port->pool_short->port_map |= (1 << port->id); ++ port->pool_short->port_map |= BIT(port->id); + +- for (rxq = 0; rxq < rxq_number; rxq++) ++ for (rxq = 0; rxq < port->nrxqs; rxq++) + mvpp2_rxq_short_pool_set(port, rxq, + port->pool_short->id); + } +@@ -3767,89 +4618,469 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) + static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu) + { + struct mvpp2_port *port = netdev_priv(dev); +- struct mvpp2_bm_pool *port_pool = port->pool_long; +- int num, pkts_num = port_pool->buf_num; ++ enum mvpp2_bm_pool_log_num new_long_pool; + int pkt_size = MVPP2_RX_PKT_SIZE(mtu); + +- /* Update BM pool with new buffer size */ +- mvpp2_bm_bufs_free(dev->dev.parent, port->priv, port_pool); +- if (port_pool->buf_num) { +- WARN(1, "cannot free all buffers in pool %d\n", port_pool->id); +- return -EIO; +- } +- +- port_pool->pkt_size = pkt_size; +- num = mvpp2_bm_bufs_add(port, port_pool, pkts_num); +- if (num != pkts_num) { +- WARN(1, "pool %d: %d of %d allocated\n", +- port_pool->id, num, pkts_num); +- return -EIO; ++ /* If port MTU is higher than 1518B: ++ * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool ++ * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool ++ */ ++ if (pkt_size > MVPP2_BM_LONG_PKT_SIZE) ++ new_long_pool = MVPP2_BM_JUMBO; ++ else ++ new_long_pool = MVPP2_BM_LONG; ++ ++ if (new_long_pool != port->pool_long->id) { ++ /* Remove port from old short & long pool */ ++ port->pool_long = mvpp2_bm_pool_use(port, port->pool_long->id, ++ port->pool_long->pkt_size); ++ port->pool_long->port_map &= ~BIT(port->id); ++ port->pool_long = NULL; ++ ++ port->pool_short = mvpp2_bm_pool_use(port, port->pool_short->id, ++ port->pool_short->pkt_size); ++ port->pool_short->port_map &= ~BIT(port->id); ++ port->pool_short = NULL; ++ ++ port->pkt_size = pkt_size; ++ ++ /* Add port to new short & long pool */ ++ mvpp2_swf_bm_pool_init(port); ++ ++ /* Update L4 checksum when jumbo enable/disable on port */ ++ if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) { ++ dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); ++ dev->hw_features &= ~(NETIF_F_IP_CSUM | ++ NETIF_F_IPV6_CSUM); ++ } else { ++ dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ++ dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ++ } + } + +- mvpp2_bm_pool_bufsize_set(port->priv, port_pool, +- MVPP2_RX_BUF_SIZE(port_pool->pkt_size)); + dev->mtu = mtu; ++ dev->wanted_features = dev->features; ++ + netdev_update_features(dev); + return 0; + } + + static inline void mvpp2_interrupts_enable(struct mvpp2_port *port) + { +- int cpu, cpu_mask = 0; ++ int i, sw_thread_mask = 0; ++ ++ for (i = 0; i < port->nqvecs; i++) ++ sw_thread_mask |= port->qvecs[i].sw_thread_mask; + +- for_each_present_cpu(cpu) +- cpu_mask |= 1 << cpu; + mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), +- MVPP2_ISR_ENABLE_INTERRUPT(cpu_mask)); ++ MVPP2_ISR_ENABLE_INTERRUPT(sw_thread_mask)); + } + + static inline void mvpp2_interrupts_disable(struct mvpp2_port *port) + { +- int cpu, cpu_mask = 0; ++ int i, sw_thread_mask = 0; ++ ++ for (i = 0; i < port->nqvecs; i++) ++ sw_thread_mask |= port->qvecs[i].sw_thread_mask; ++ ++ mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_DISABLE_INTERRUPT(sw_thread_mask)); ++} ++ ++static inline void mvpp2_qvec_interrupt_enable(struct mvpp2_queue_vector *qvec) ++{ ++ struct mvpp2_port *port = qvec->port; ++ ++ mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_ENABLE_INTERRUPT(qvec->sw_thread_mask)); ++} ++ ++static inline void mvpp2_qvec_interrupt_disable(struct mvpp2_queue_vector *qvec) ++{ ++ struct mvpp2_port *port = qvec->port; + +- for_each_present_cpu(cpu) +- cpu_mask |= 1 << cpu; + mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), +- MVPP2_ISR_DISABLE_INTERRUPT(cpu_mask)); ++ MVPP2_ISR_DISABLE_INTERRUPT(qvec->sw_thread_mask)); + } + +-/* Mask the current CPU's Rx/Tx interrupts */ ++/* Mask the current CPU's Rx/Tx interrupts ++ * Called by on_each_cpu(), guaranteed to run with migration disabled, ++ * using smp_processor_id() is OK. ++ */ + static void mvpp2_interrupts_mask(void *arg) + { + struct mvpp2_port *port = arg; + +- mvpp2_write(port->priv, MVPP2_ISR_RX_TX_MASK_REG(port->id), 0); ++ mvpp2_percpu_write(port->priv, smp_processor_id(), ++ MVPP2_ISR_RX_TX_MASK_REG(port->id), 0); + } + +-/* Unmask the current CPU's Rx/Tx interrupts */ ++/* Unmask the current CPU's Rx/Tx interrupts. ++ * Called by on_each_cpu(), guaranteed to run with migration disabled, ++ * using smp_processor_id() is OK. ++ */ + static void mvpp2_interrupts_unmask(void *arg) + { + struct mvpp2_port *port = arg; ++ u32 val; ++ ++ val = MVPP2_CAUSE_MISC_SUM_MASK | ++ MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ if (port->has_tx_irqs) ++ val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; ++ ++ mvpp2_percpu_write(port->priv, smp_processor_id(), ++ MVPP2_ISR_RX_TX_MASK_REG(port->id), val); ++} ++ ++static void ++mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) ++{ ++ u32 val; ++ int i; + +- mvpp2_write(port->priv, MVPP2_ISR_RX_TX_MASK_REG(port->id), +- (MVPP2_CAUSE_MISC_SUM_MASK | +- MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK)); ++ if (port->priv->hw_version != MVPP22) ++ return; ++ ++ if (mask) ++ val = 0; ++ else ++ val = MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ ++ for (i = 0; i < port->nqvecs; i++) { ++ struct mvpp2_queue_vector *v = port->qvecs + i; ++ ++ if (v->type != MVPP2_QUEUE_VECTOR_SHARED) ++ continue; ++ ++ mvpp2_percpu_write(port->priv, v->sw_thread_id, ++ MVPP2_ISR_RX_TX_MASK_REG(port->id), val); ++ } + } + + /* Port configuration routines */ + +-static void mvpp2_port_mii_set(struct mvpp2_port *port) ++static void mvpp22_gop_init_rgmii(struct mvpp2_port *port) + { ++ struct mvpp2 *priv = port->priv; + u32 val; + +- val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); ++ regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); ++ val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT; ++ regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); ++ ++ regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); ++ if (port->gop_id == 2) ++ val |= GENCONF_CTRL0_PORT0_RGMII | GENCONF_CTRL0_PORT1_RGMII; ++ else if (port->gop_id == 3) ++ val |= GENCONF_CTRL0_PORT1_RGMII_MII; ++ regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val); ++} ++ ++static void mvpp22_gop_init_sgmii(struct mvpp2_port *port) ++{ ++ struct mvpp2 *priv = port->priv; ++ u32 val; ++ ++ regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); ++ val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT | ++ GENCONF_PORT_CTRL0_RX_DATA_SAMPLE; ++ regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); ++ ++ if (port->gop_id > 1) { ++ regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); ++ if (port->gop_id == 2) ++ val &= ~GENCONF_CTRL0_PORT0_RGMII; ++ else if (port->gop_id == 3) ++ val &= ~GENCONF_CTRL0_PORT1_RGMII_MII; ++ regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val); ++ } ++} ++ ++static void mvpp22_gop_init_10gkr(struct mvpp2_port *port) ++{ ++ struct mvpp2 *priv = port->priv; ++ void __iomem *mpcs = priv->iface_base + MVPP22_MPCS_BASE(port->gop_id); ++ void __iomem *xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id); ++ u32 val; ++ ++ /* XPCS */ ++ val = readl(xpcs + MVPP22_XPCS_CFG0); ++ val &= ~(MVPP22_XPCS_CFG0_PCS_MODE(0x3) | ++ MVPP22_XPCS_CFG0_ACTIVE_LANE(0x3)); ++ val |= MVPP22_XPCS_CFG0_ACTIVE_LANE(2); ++ writel(val, xpcs + MVPP22_XPCS_CFG0); ++ ++ /* MPCS */ ++ val = readl(mpcs + MVPP22_MPCS_CTRL); ++ val &= ~MVPP22_MPCS_CTRL_FWD_ERR_CONN; ++ writel(val, mpcs + MVPP22_MPCS_CTRL); ++ ++ val = readl(mpcs + MVPP22_MPCS_CLK_RESET); ++ val &= ~(MVPP22_MPCS_CLK_RESET_DIV_RATIO(0x7) | MAC_CLK_RESET_MAC | ++ MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX); ++ val |= MVPP22_MPCS_CLK_RESET_DIV_RATIO(1); ++ writel(val, mpcs + MVPP22_MPCS_CLK_RESET); ++ ++ val &= ~MVPP22_MPCS_CLK_RESET_DIV_SET; ++ val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX; ++ writel(val, mpcs + MVPP22_MPCS_CLK_RESET); ++} ++ ++static int mvpp22_gop_init(struct mvpp2_port *port) ++{ ++ struct mvpp2 *priv = port->priv; ++ u32 val; ++ ++ if (!priv->sysctrl_base) ++ return 0; + + switch (port->phy_interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ if (port->gop_id == 0) ++ goto invalid_conf; ++ mvpp22_gop_init_rgmii(port); ++ break; + case PHY_INTERFACE_MODE_SGMII: +- val |= MVPP2_GMAC_INBAND_AN_MASK; ++ mvpp22_gop_init_sgmii(port); ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ if (port->gop_id != 0) ++ goto invalid_conf; ++ mvpp22_gop_init_10gkr(port); ++ break; ++ default: ++ goto unsupported_conf; ++ } ++ ++ regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL1, &val); ++ val |= GENCONF_PORT_CTRL1_RESET(port->gop_id) | ++ GENCONF_PORT_CTRL1_EN(port->gop_id); ++ regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL1, val); ++ ++ regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); ++ val |= GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR; ++ regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); ++ ++ regmap_read(priv->sysctrl_base, GENCONF_SOFT_RESET1, &val); ++ val |= GENCONF_SOFT_RESET1_GOP; ++ regmap_write(priv->sysctrl_base, GENCONF_SOFT_RESET1, val); ++ ++unsupported_conf: ++ return 0; ++ ++invalid_conf: ++ netdev_err(port->dev, "Invalid port configuration\n"); ++ return -EINVAL; ++} ++ ++static void mvpp22_gop_unmask_irq(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ if (phy_interface_mode_is_rgmii(port->phy_interface) || ++ port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ /* Enable the GMAC link status irq for this port */ ++ val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK); ++ val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT; ++ writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); ++ } ++ ++ if (port->gop_id == 0) { ++ /* Enable the XLG/GIG irqs for this port */ ++ val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); ++ if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) ++ val |= MVPP22_XLG_EXT_INT_MASK_XLG; ++ else ++ val |= MVPP22_XLG_EXT_INT_MASK_GIG; ++ writel(val, port->base + MVPP22_XLG_EXT_INT_MASK); ++ } ++} ++ ++static void mvpp22_gop_mask_irq(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ if (port->gop_id == 0) { ++ val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); ++ val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG | ++ MVPP22_XLG_EXT_INT_MASK_GIG); ++ writel(val, port->base + MVPP22_XLG_EXT_INT_MASK); ++ } ++ ++ if (phy_interface_mode_is_rgmii(port->phy_interface) || ++ port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK); ++ val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT; ++ writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); ++ } ++} ++ ++static void mvpp22_gop_setup_irq(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ if (phy_interface_mode_is_rgmii(port->phy_interface) || ++ port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ val = readl(port->base + MVPP22_GMAC_INT_MASK); ++ val |= MVPP22_GMAC_INT_MASK_LINK_STAT; ++ writel(val, port->base + MVPP22_GMAC_INT_MASK); ++ } ++ ++ if (port->gop_id == 0) { ++ val = readl(port->base + MVPP22_XLG_INT_MASK); ++ val |= MVPP22_XLG_INT_MASK_LINK; ++ writel(val, port->base + MVPP22_XLG_INT_MASK); ++ } ++ ++ mvpp22_gop_unmask_irq(port); ++} ++ ++static int mvpp22_comphy_init(struct mvpp2_port *port) ++{ ++ enum phy_mode mode; ++ int ret; ++ ++ if (!port->comphy) ++ return 0; ++ ++ switch (port->phy_interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ mode = PHY_MODE_SGMII; ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ mode = PHY_MODE_10GKR; + break; +- case PHY_INTERFACE_MODE_RGMII: +- val |= MVPP2_GMAC_PORT_RGMII_MASK; + default: ++ return -EINVAL; ++ } ++ ++ ret = phy_set_mode(port->comphy, mode); ++ if (ret) ++ return ret; ++ ++ return phy_power_on(port->comphy); ++} ++ ++static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); ++ val |= MVPP22_CTRL4_SYNC_BYPASS_DIS | MVPP22_CTRL4_DP_CLK_SEL | ++ MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; ++ val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; ++ writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); ++ } else if (phy_interface_mode_is_rgmii(port->phy_interface)) { ++ val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); ++ val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL | ++ MVPP22_CTRL4_SYNC_BYPASS_DIS | ++ MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; ++ val &= ~MVPP22_CTRL4_DP_CLK_SEL; ++ writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); ++ } ++ ++ /* The port is connected to a copper PHY */ ++ val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); ++ val &= ~MVPP2_GMAC_PORT_TYPE_MASK; ++ writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++ ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS | ++ MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG | ++ MVPP2_GMAC_AN_DUPLEX_EN; ++ if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) ++ val |= MVPP2_GMAC_IN_BAND_AUTONEG; ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++} ++ ++static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ /* Force link down */ ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val &= ~MVPP2_GMAC_FORCE_LINK_PASS; ++ val |= MVPP2_GMAC_FORCE_LINK_DOWN; ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ ++ /* Set the GMAC in a reset state */ ++ val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); ++ val |= MVPP2_GMAC_PORT_RESET_MASK; ++ writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); ++ ++ /* Configure the PCS and in-band AN */ ++ val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); ++ if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK; ++ } else if (phy_interface_mode_is_rgmii(port->phy_interface)) { + val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; + } ++ writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); ++ ++ mvpp2_port_mii_gmac_configure_mode(port); + ++ /* Unset the GMAC reset state */ ++ val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); ++ val &= ~MVPP2_GMAC_PORT_RESET_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); ++ ++ /* Stop forcing link down */ ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val &= ~MVPP2_GMAC_FORCE_LINK_DOWN; ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++} ++ ++static void mvpp2_port_mii_xlg_configure(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ if (port->gop_id != 0) ++ return; ++ ++ val = readl(port->base + MVPP22_XLG_CTRL0_REG); ++ val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; ++ writel(val, port->base + MVPP22_XLG_CTRL0_REG); ++ ++ val = readl(port->base + MVPP22_XLG_CTRL4_REG); ++ val &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC; ++ val |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC; ++ writel(val, port->base + MVPP22_XLG_CTRL4_REG); ++} ++ ++static void mvpp22_port_mii_set(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ /* Only GOP port 0 has an XLG MAC */ ++ if (port->gop_id == 0) { ++ val = readl(port->base + MVPP22_XLG_CTRL3_REG); ++ val &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK; ++ ++ if (port->phy_interface == PHY_INTERFACE_MODE_XAUI || ++ port->phy_interface == PHY_INTERFACE_MODE_10GKR) ++ val |= MVPP22_XLG_CTRL3_MACMODESELECT_10G; ++ else ++ val |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC; ++ ++ writel(val, port->base + MVPP22_XLG_CTRL3_REG); ++ } ++} ++ ++static void mvpp2_port_mii_set(struct mvpp2_port *port) ++{ ++ if (port->priv->hw_version == MVPP22) ++ mvpp22_port_mii_set(port); ++ ++ if (phy_interface_mode_is_rgmii(port->phy_interface) || ++ port->phy_interface == PHY_INTERFACE_MODE_SGMII) ++ mvpp2_port_mii_gmac_configure(port); ++ else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) ++ mvpp2_port_mii_xlg_configure(port); + } + + static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port) +@@ -3865,19 +5096,40 @@ static void mvpp2_port_enable(struct mvpp2_port *port) + { + u32 val; + +- val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); +- val |= MVPP2_GMAC_PORT_EN_MASK; +- val |= MVPP2_GMAC_MIB_CNTR_EN_MASK; +- writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++ /* Only GOP port 0 has an XLG MAC */ ++ if (port->gop_id == 0 && ++ (port->phy_interface == PHY_INTERFACE_MODE_XAUI || ++ port->phy_interface == PHY_INTERFACE_MODE_10GKR)) { ++ val = readl(port->base + MVPP22_XLG_CTRL0_REG); ++ val |= MVPP22_XLG_CTRL0_PORT_EN | ++ MVPP22_XLG_CTRL0_MAC_RESET_DIS; ++ val &= ~MVPP22_XLG_CTRL0_MIB_CNT_DIS; ++ writel(val, port->base + MVPP22_XLG_CTRL0_REG); ++ } else { ++ val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); ++ val |= MVPP2_GMAC_PORT_EN_MASK; ++ val |= MVPP2_GMAC_MIB_CNTR_EN_MASK; ++ writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++ } + } + + static void mvpp2_port_disable(struct mvpp2_port *port) + { + u32 val; + +- val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); +- val &= ~(MVPP2_GMAC_PORT_EN_MASK); +- writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++ /* Only GOP port 0 has an XLG MAC */ ++ if (port->gop_id == 0 && ++ (port->phy_interface == PHY_INTERFACE_MODE_XAUI || ++ port->phy_interface == PHY_INTERFACE_MODE_10GKR)) { ++ val = readl(port->base + MVPP22_XLG_CTRL0_REG); ++ val &= ~(MVPP22_XLG_CTRL0_PORT_EN | ++ MVPP22_XLG_CTRL0_MAC_RESET_DIS); ++ writel(val, port->base + MVPP22_XLG_CTRL0_REG); ++ } else { ++ val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); ++ val &= ~(MVPP2_GMAC_PORT_EN_MASK); ++ writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++ } + } + + /* Set IEEE 802.3x Flow Control Xon Packet Transmission Mode */ +@@ -3910,9 +5162,131 @@ static void mvpp2_port_loopback_set(struct mvpp2_port *port) + writel(val, port->base + MVPP2_GMAC_CTRL_1_REG); + } + ++struct mvpp2_ethtool_counter { ++ unsigned int offset; ++ const char string[ETH_GSTRING_LEN]; ++ bool reg_is_64b; ++}; ++ ++static u64 mvpp2_read_count(struct mvpp2_port *port, ++ const struct mvpp2_ethtool_counter *counter) ++{ ++ u64 val; ++ ++ val = readl(port->stats_base + counter->offset); ++ if (counter->reg_is_64b) ++ val += (u64)readl(port->stats_base + counter->offset + 4) << 32; ++ ++ return val; ++} ++ ++/* Due to the fact that software statistics and hardware statistics are, by ++ * design, incremented at different moments in the chain of packet processing, ++ * it is very likely that incoming packets could have been dropped after being ++ * counted by hardware but before reaching software statistics (most probably ++ * multicast packets), and in the oppposite way, during transmission, FCS bytes ++ * are added in between as well as TSO skb will be split and header bytes added. ++ * Hence, statistics gathered from userspace with ifconfig (software) and ++ * ethtool (hardware) cannot be compared. ++ */ ++static const struct mvpp2_ethtool_counter mvpp2_ethtool_regs[] = { ++ { MVPP2_MIB_GOOD_OCTETS_RCVD, "good_octets_received", true }, ++ { MVPP2_MIB_BAD_OCTETS_RCVD, "bad_octets_received" }, ++ { MVPP2_MIB_CRC_ERRORS_SENT, "crc_errors_sent" }, ++ { MVPP2_MIB_UNICAST_FRAMES_RCVD, "unicast_frames_received" }, ++ { MVPP2_MIB_BROADCAST_FRAMES_RCVD, "broadcast_frames_received" }, ++ { MVPP2_MIB_MULTICAST_FRAMES_RCVD, "multicast_frames_received" }, ++ { MVPP2_MIB_FRAMES_64_OCTETS, "frames_64_octets" }, ++ { MVPP2_MIB_FRAMES_65_TO_127_OCTETS, "frames_65_to_127_octet" }, ++ { MVPP2_MIB_FRAMES_128_TO_255_OCTETS, "frames_128_to_255_octet" }, ++ { MVPP2_MIB_FRAMES_256_TO_511_OCTETS, "frames_256_to_511_octet" }, ++ { MVPP2_MIB_FRAMES_512_TO_1023_OCTETS, "frames_512_to_1023_octet" }, ++ { MVPP2_MIB_FRAMES_1024_TO_MAX_OCTETS, "frames_1024_to_max_octet" }, ++ { MVPP2_MIB_GOOD_OCTETS_SENT, "good_octets_sent", true }, ++ { MVPP2_MIB_UNICAST_FRAMES_SENT, "unicast_frames_sent" }, ++ { MVPP2_MIB_MULTICAST_FRAMES_SENT, "multicast_frames_sent" }, ++ { MVPP2_MIB_BROADCAST_FRAMES_SENT, "broadcast_frames_sent" }, ++ { MVPP2_MIB_FC_SENT, "fc_sent" }, ++ { MVPP2_MIB_FC_RCVD, "fc_received" }, ++ { MVPP2_MIB_RX_FIFO_OVERRUN, "rx_fifo_overrun" }, ++ { MVPP2_MIB_UNDERSIZE_RCVD, "undersize_received" }, ++ { MVPP2_MIB_FRAGMENTS_RCVD, "fragments_received" }, ++ { MVPP2_MIB_OVERSIZE_RCVD, "oversize_received" }, ++ { MVPP2_MIB_JABBER_RCVD, "jabber_received" }, ++ { MVPP2_MIB_MAC_RCV_ERROR, "mac_receive_error" }, ++ { MVPP2_MIB_BAD_CRC_EVENT, "bad_crc_event" }, ++ { MVPP2_MIB_COLLISION, "collision" }, ++ { MVPP2_MIB_LATE_COLLISION, "late_collision" }, ++}; ++ ++static void mvpp2_ethtool_get_strings(struct net_device *netdev, u32 sset, ++ u8 *data) ++{ ++ if (sset == ETH_SS_STATS) { ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++) ++ memcpy(data + i * ETH_GSTRING_LEN, ++ &mvpp2_ethtool_regs[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static void mvpp2_gather_hw_statistics(struct work_struct *work) ++{ ++ struct delayed_work *del_work = to_delayed_work(work); ++ struct mvpp2_port *port = container_of(del_work, struct mvpp2_port, ++ stats_work); ++ u64 *pstats; ++ int i; ++ ++ mutex_lock(&port->gather_stats_lock); ++ ++ pstats = port->ethtool_stats; ++ for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++) ++ *pstats++ += mvpp2_read_count(port, &mvpp2_ethtool_regs[i]); ++ ++ /* No need to read again the counters right after this function if it ++ * was called asynchronously by the user (ie. use of ethtool). ++ */ ++ cancel_delayed_work(&port->stats_work); ++ queue_delayed_work(port->priv->stats_queue, &port->stats_work, ++ MVPP2_MIB_COUNTERS_STATS_DELAY); ++ ++ mutex_unlock(&port->gather_stats_lock); ++} ++ ++static void mvpp2_ethtool_get_stats(struct net_device *dev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ ++ /* Update statistics for the given port, then take the lock to avoid ++ * concurrent accesses on the ethtool_stats structure during its copy. ++ */ ++ mvpp2_gather_hw_statistics(&port->stats_work.work); ++ ++ mutex_lock(&port->gather_stats_lock); ++ memcpy(data, port->ethtool_stats, ++ sizeof(u64) * ARRAY_SIZE(mvpp2_ethtool_regs)); ++ mutex_unlock(&port->gather_stats_lock); ++} ++ ++static int mvpp2_ethtool_get_sset_count(struct net_device *dev, int sset) ++{ ++ if (sset == ETH_SS_STATS) ++ return ARRAY_SIZE(mvpp2_ethtool_regs); ++ ++ return -EOPNOTSUPP; ++} ++ + static void mvpp2_port_reset(struct mvpp2_port *port) + { + u32 val; ++ unsigned int i; ++ ++ /* Read the GOP statistics to reset the hardware counters */ ++ for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++) ++ mvpp2_read_count(port, &mvpp2_ethtool_regs[i]); + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) & + ~MVPP2_GMAC_PORT_RESET_MASK; +@@ -3935,21 +5309,35 @@ static inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port) + writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); + } + ++/* Change maximum receive size of the port */ ++static inline void mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP22_XLG_CTRL1_REG); ++ val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK; ++ val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) << ++ MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS; ++ writel(val, port->base + MVPP22_XLG_CTRL1_REG); ++} ++ + /* Set defaults to the MVPP2 port */ + static void mvpp2_defaults_set(struct mvpp2_port *port) + { + int tx_port_num, val, queue, ptxq, lrxq; + +- /* Configure port to loopback if needed */ +- if (port->flags & MVPP2_F_LOOPBACK) +- mvpp2_port_loopback_set(port); ++ if (port->priv->hw_version == MVPP21) { ++ /* Configure port to loopback if needed */ ++ if (port->flags & MVPP2_F_LOOPBACK) ++ mvpp2_port_loopback_set(port); + +- /* Update TX FIFO MIN Threshold */ +- val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); +- val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; +- /* Min. TX threshold must be less than minimal packet length */ +- val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2); +- writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); ++ /* Update TX FIFO MIN Threshold */ ++ val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); ++ val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; ++ /* Min. TX threshold must be less than minimal packet length */ ++ val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2); ++ writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); ++ } + + /* Disable Legacy WRR, Disable EJP, Release from reset */ + tx_port_num = mvpp2_egress_port(port); +@@ -3983,7 +5371,7 @@ static void mvpp2_defaults_set(struct mvpp2_port *port) + MVPP2_RX_LOW_LATENCY_PKT_SIZE(256)); + + /* Enable Rx cache snoop */ +- for (lrxq = 0; lrxq < rxq_number; lrxq++) { ++ for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { + queue = port->rxqs[lrxq]->id; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); + val |= MVPP2_SNOOP_PKT_SIZE_MASK | +@@ -4001,7 +5389,7 @@ static void mvpp2_ingress_enable(struct mvpp2_port *port) + u32 val; + int lrxq, queue; + +- for (lrxq = 0; lrxq < rxq_number; lrxq++) { ++ for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { + queue = port->rxqs[lrxq]->id; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); + val &= ~MVPP2_RXQ_DISABLE_MASK; +@@ -4014,7 +5402,7 @@ static void mvpp2_ingress_disable(struct mvpp2_port *port) + u32 val; + int lrxq, queue; + +- for (lrxq = 0; lrxq < rxq_number; lrxq++) { ++ for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { + queue = port->rxqs[lrxq]->id; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); + val |= MVPP2_RXQ_DISABLE_MASK; +@@ -4033,10 +5421,10 @@ static void mvpp2_egress_enable(struct mvpp2_port *port) + + /* Enable all initialized TXs. */ + qmap = 0; +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + struct mvpp2_tx_queue *txq = port->txqs[queue]; + +- if (txq->descs != NULL) ++ if (txq->descs) + qmap |= (1 << queue); + } + +@@ -4136,31 +5524,8 @@ static void mvpp2_rxq_offset_set(struct mvpp2_port *port, + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); + } + +-/* Obtain BM cookie information from descriptor */ +-static u32 mvpp2_bm_cookie_build(struct mvpp2_rx_desc *rx_desc) +-{ +- int pool = (rx_desc->status & MVPP2_RXD_BM_POOL_ID_MASK) >> +- MVPP2_RXD_BM_POOL_ID_OFFS; +- int cpu = smp_processor_id(); +- +- return ((pool & 0xFF) << MVPP2_BM_COOKIE_POOL_OFFS) | +- ((cpu & 0xFF) << MVPP2_BM_COOKIE_CPU_OFFS); +-} +- + /* Tx descriptors helper methods */ + +-/* Get number of Tx descriptors waiting to be transmitted by HW */ +-static int mvpp2_txq_pend_desc_num_get(struct mvpp2_port *port, +- struct mvpp2_tx_queue *txq) +-{ +- u32 val; +- +- mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); +- val = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG); +- +- return val & MVPP2_TXQ_PENDING_MASK; +-} +- + /* Get pointer to next Tx descriptor to be processed (send) by HW */ + static struct mvpp2_tx_desc * + mvpp2_txq_next_desc_get(struct mvpp2_tx_queue *txq) +@@ -4171,44 +5536,59 @@ static int mvpp2_txq_pend_desc_num_get(struct mvpp2_port *port, + return txq->descs + tx_desc; + } + +-/* Update HW with number of aggregated Tx descriptors to be sent */ ++/* Update HW with number of aggregated Tx descriptors to be sent ++ * ++ * Called only from mvpp2_tx(), so migration is disabled, using ++ * smp_processor_id() is OK. ++ */ + static void mvpp2_aggr_txq_pend_desc_add(struct mvpp2_port *port, int pending) + { + /* aggregated access - relevant TXQ number is written in TX desc */ +- mvpp2_write(port->priv, MVPP2_AGGR_TXQ_UPDATE_REG, pending); ++ mvpp2_percpu_write(port->priv, smp_processor_id(), ++ MVPP2_AGGR_TXQ_UPDATE_REG, pending); + } + + + /* Check if there are enough free descriptors in aggregated txq. + * If not, update the number of occupied descriptors and repeat the check. ++ * ++ * Called only from mvpp2_tx(), so migration is disabled, using ++ * smp_processor_id() is OK. + */ + static int mvpp2_aggr_desc_num_check(struct mvpp2 *priv, + struct mvpp2_tx_queue *aggr_txq, int num) + { +- if ((aggr_txq->count + num) > aggr_txq->size) { ++ if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE) { + /* Update number of occupied aggregated Tx descriptors */ + int cpu = smp_processor_id(); +- u32 val = mvpp2_read(priv, MVPP2_AGGR_TXQ_STATUS_REG(cpu)); ++ u32 val = mvpp2_read_relaxed(priv, ++ MVPP2_AGGR_TXQ_STATUS_REG(cpu)); + + aggr_txq->count = val & MVPP2_AGGR_TXQ_PENDING_MASK; + } + +- if ((aggr_txq->count + num) > aggr_txq->size) ++ if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE) + return -ENOMEM; + + return 0; + } + +-/* Reserved Tx descriptors allocation request */ ++/* Reserved Tx descriptors allocation request ++ * ++ * Called only from mvpp2_txq_reserved_desc_num_proc(), itself called ++ * only by mvpp2_tx(), so migration is disabled, using ++ * smp_processor_id() is OK. ++ */ + static int mvpp2_txq_alloc_reserved_desc(struct mvpp2 *priv, + struct mvpp2_tx_queue *txq, int num) + { + u32 val; ++ int cpu = smp_processor_id(); + + val = (txq->id << MVPP2_TXQ_RSVD_REQ_Q_OFFSET) | num; +- mvpp2_write(priv, MVPP2_TXQ_RSVD_REQ_REG, val); ++ mvpp2_percpu_write_relaxed(priv, cpu, MVPP2_TXQ_RSVD_REQ_REG, val); + +- val = mvpp2_read(priv, MVPP2_TXQ_RSVD_RSLT_REG); ++ val = mvpp2_percpu_read_relaxed(priv, cpu, MVPP2_TXQ_RSVD_RSLT_REG); + + return val & MVPP2_TXQ_RSVD_RSLT_MASK; + } +@@ -4267,7 +5647,7 @@ static void mvpp2_txq_desc_put(struct mvpp2_tx_queue *txq) + } + + /* Set Tx descriptors fields relevant for CSUM calculation */ +-static u32 mvpp2_txq_desc_csum(int l3_offs, __be16 l3_proto, ++static u32 mvpp2_txq_desc_csum(int l3_offs, int l3_proto, + int ip_hdr_len, int l4_proto) + { + u32 command; +@@ -4302,6 +5682,10 @@ static u32 mvpp2_txq_desc_csum(int l3_offs, __be16 l3_proto, + /* Get number of sent descriptors and decrement counter. + * The number of sent descriptors is returned. + * Per-CPU access ++ * ++ * Called only from mvpp2_txq_done(), called from mvpp2_tx() ++ * (migration disabled) and from the TX completion tasklet (migration ++ * disabled) so using smp_processor_id() is OK. + */ + static inline int mvpp2_txq_sent_desc_proc(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +@@ -4309,21 +5693,26 @@ static inline int mvpp2_txq_sent_desc_proc(struct mvpp2_port *port, + u32 val; + + /* Reading status reg resets transmitted descriptor counter */ +- val = mvpp2_read(port->priv, MVPP2_TXQ_SENT_REG(txq->id)); ++ val = mvpp2_percpu_read_relaxed(port->priv, smp_processor_id(), ++ MVPP2_TXQ_SENT_REG(txq->id)); + + return (val & MVPP2_TRANSMITTED_COUNT_MASK) >> + MVPP2_TRANSMITTED_COUNT_OFFSET; + } + ++/* Called through on_each_cpu(), so runs on all CPUs, with migration ++ * disabled, therefore using smp_processor_id() is OK. ++ */ + static void mvpp2_txq_sent_counter_clear(void *arg) + { + struct mvpp2_port *port = arg; + int queue; + +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + int id = port->txqs[queue]->id; + +- mvpp2_read(port->priv, MVPP2_TXQ_SENT_REG(id)); ++ mvpp2_percpu_read(port->priv, smp_processor_id(), ++ MVPP2_TXQ_SENT_REG(id)); + } + } + +@@ -4360,7 +5749,7 @@ static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) + mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); + } + +- for (txq = 0; txq < txq_number; txq++) { ++ for (txq = 0; txq < port->ntxqs; txq++) { + val = mvpp2_read(port->priv, + MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq)); + size = val & MVPP2_TXQ_TOKEN_SIZE_MAX; +@@ -4380,27 +5769,87 @@ static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) + * will be generated by HW. + */ + static void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port, +- struct mvpp2_rx_queue *rxq, u32 pkts) ++ struct mvpp2_rx_queue *rxq) ++{ ++ int cpu = get_cpu(); ++ ++ if (rxq->pkts_coal > MVPP2_OCCUPIED_THRESH_MASK) ++ rxq->pkts_coal = MVPP2_OCCUPIED_THRESH_MASK; ++ ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_NUM_REG, rxq->id); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_THRESH_REG, ++ rxq->pkts_coal); ++ ++ put_cpu(); ++} ++ ++/* For some reason in the LSP this is done on each CPU. Why ? */ ++static void mvpp2_tx_pkts_coal_set(struct mvpp2_port *port, ++ struct mvpp2_tx_queue *txq) + { ++ int cpu = get_cpu(); + u32 val; + +- val = (pkts & MVPP2_OCCUPIED_THRESH_MASK); +- mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); +- mvpp2_write(port->priv, MVPP2_RXQ_THRESH_REG, val); ++ if (txq->done_pkts_coal > MVPP2_TXQ_THRESH_MASK) ++ txq->done_pkts_coal = MVPP2_TXQ_THRESH_MASK; ++ ++ val = (txq->done_pkts_coal << MVPP2_TXQ_THRESH_OFFSET); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_THRESH_REG, val); ++ ++ put_cpu(); ++} ++ ++static u32 mvpp2_usec_to_cycles(u32 usec, unsigned long clk_hz) ++{ ++ u64 tmp = (u64)clk_hz * usec; ++ ++ do_div(tmp, USEC_PER_SEC); ++ ++ return tmp > U32_MAX ? U32_MAX : tmp; ++} ++ ++static u32 mvpp2_cycles_to_usec(u32 cycles, unsigned long clk_hz) ++{ ++ u64 tmp = (u64)cycles * USEC_PER_SEC; ++ ++ do_div(tmp, clk_hz); + +- rxq->pkts_coal = pkts; ++ return tmp > U32_MAX ? U32_MAX : tmp; + } + + /* Set the time delay in usec before Rx interrupt */ + static void mvpp2_rx_time_coal_set(struct mvpp2_port *port, +- struct mvpp2_rx_queue *rxq, u32 usec) ++ struct mvpp2_rx_queue *rxq) + { +- u32 val; ++ unsigned long freq = port->priv->tclk; ++ u32 val = mvpp2_usec_to_cycles(rxq->time_coal, freq); ++ ++ if (val > MVPP2_MAX_ISR_RX_THRESHOLD) { ++ rxq->time_coal = ++ mvpp2_cycles_to_usec(MVPP2_MAX_ISR_RX_THRESHOLD, freq); ++ ++ /* re-evaluate to get actual register value */ ++ val = mvpp2_usec_to_cycles(rxq->time_coal, freq); ++ } + +- val = (port->priv->tclk / USEC_PER_SEC) * usec; + mvpp2_write(port->priv, MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val); ++} ++ ++static void mvpp2_tx_time_coal_set(struct mvpp2_port *port) ++{ ++ unsigned long freq = port->priv->tclk; ++ u32 val = mvpp2_usec_to_cycles(port->tx_time_coal, freq); ++ ++ if (val > MVPP2_MAX_ISR_TX_THRESHOLD) { ++ port->tx_time_coal = ++ mvpp2_cycles_to_usec(MVPP2_MAX_ISR_TX_THRESHOLD, freq); ++ ++ /* re-evaluate to get actual register value */ ++ val = mvpp2_usec_to_cycles(port->tx_time_coal, freq); ++ } + +- rxq->time_coal = usec; ++ mvpp2_write(port->priv, MVPP2_ISR_TX_THRESHOLD_REG(port->id), val); + } + + /* Free Tx queue skbuffs */ +@@ -4414,8 +5863,9 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, + struct mvpp2_txq_pcpu_buf *tx_buf = + txq_pcpu->buffs + txq_pcpu->txq_get_index; + +- dma_unmap_single(port->dev->dev.parent, tx_buf->phys, +- tx_buf->size, DMA_TO_DEVICE); ++ if (!IS_TSO_HEADER(txq_pcpu, tx_buf->dma)) ++ dma_unmap_single(port->dev->dev.parent, tx_buf->dma, ++ tx_buf->size, DMA_TO_DEVICE); + if (tx_buf->skb) + dev_kfree_skb_any(tx_buf->skb); + +@@ -4457,11 +5907,12 @@ static void mvpp2_txq_done(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, + txq_pcpu->count -= tx_done; + + if (netif_tx_queue_stopped(nq)) +- if (txq_pcpu->size - txq_pcpu->count >= MAX_SKB_FRAGS + 1) ++ if (txq_pcpu->count <= txq_pcpu->wake_threshold) + netif_tx_wake_queue(nq); + } + +-static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) ++static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause, ++ int cpu) + { + struct mvpp2_tx_queue *txq; + struct mvpp2_txq_pcpu *txq_pcpu; +@@ -4472,7 +5923,7 @@ static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) + if (!txq) + break; + +- txq_pcpu = this_cpu_ptr(txq->pcpu); ++ txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + + if (txq_pcpu->count) { + mvpp2_txq_done(port, txq, txq_pcpu); +@@ -4488,28 +5939,36 @@ static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) + + /* Allocate and initialize descriptors for aggr TXQ */ + static int mvpp2_aggr_txq_init(struct platform_device *pdev, +- struct mvpp2_tx_queue *aggr_txq, +- int desc_num, int cpu, ++ struct mvpp2_tx_queue *aggr_txq, int cpu, + struct mvpp2 *priv) + { ++ u32 txq_dma; ++ + /* Allocate memory for TX descriptors */ +- aggr_txq->descs = dma_alloc_coherent(&pdev->dev, +- desc_num * MVPP2_DESC_ALIGNED_SIZE, +- &aggr_txq->descs_phys, GFP_KERNEL); ++ aggr_txq->descs = dma_zalloc_coherent(&pdev->dev, ++ MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, ++ &aggr_txq->descs_dma, GFP_KERNEL); + if (!aggr_txq->descs) + return -ENOMEM; + +- aggr_txq->last_desc = aggr_txq->size - 1; ++ aggr_txq->last_desc = MVPP2_AGGR_TXQ_SIZE - 1; + + /* Aggr TXQ no reset WA */ + aggr_txq->next_desc_to_proc = mvpp2_read(priv, + MVPP2_AGGR_TXQ_INDEX_REG(cpu)); + +- /* Set Tx descriptors queue starting address */ +- /* indirect access */ +- mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), +- aggr_txq->descs_phys); +- mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); ++ /* Set Tx descriptors queue starting address indirect ++ * access ++ */ ++ if (priv->hw_version == MVPP21) ++ txq_dma = aggr_txq->descs_dma; ++ else ++ txq_dma = aggr_txq->descs_dma >> ++ MVPP22_AGGR_TXQ_DESC_ADDR_OFFS; ++ ++ mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), txq_dma); ++ mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), ++ MVPP2_AGGR_TXQ_SIZE); + + return 0; + } +@@ -4519,12 +5978,15 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, + struct mvpp2_rx_queue *rxq) + + { ++ u32 rxq_dma; ++ int cpu; ++ + rxq->size = port->rx_ring_size; + + /* Allocate memory for RX descriptors */ + rxq->descs = dma_alloc_coherent(port->dev->dev.parent, + rxq->size * MVPP2_DESC_ALIGNED_SIZE, +- &rxq->descs_phys, GFP_KERNEL); ++ &rxq->descs_dma, GFP_KERNEL); + if (!rxq->descs) + return -ENOMEM; + +@@ -4534,17 +5996,23 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, + mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0); + + /* Set Rx descriptors queue starting address - indirect access */ +- mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); +- mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq->descs_phys); +- mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); +- mvpp2_write(port->priv, MVPP2_RXQ_INDEX_REG, 0); ++ cpu = get_cpu(); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_NUM_REG, rxq->id); ++ if (port->priv->hw_version == MVPP21) ++ rxq_dma = rxq->descs_dma; ++ else ++ rxq_dma = rxq->descs_dma >> MVPP22_DESC_ADDR_OFFS; ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_ADDR_REG, rxq_dma); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_INDEX_REG, 0); ++ put_cpu(); + + /* Set Offset */ + mvpp2_rxq_offset_set(port, rxq->id, NET_SKB_PAD); + + /* Set coalescing pkts and time */ +- mvpp2_rx_pkts_coal_set(port, rxq, rxq->pkts_coal); +- mvpp2_rx_time_coal_set(port, rxq, rxq->time_coal); ++ mvpp2_rx_pkts_coal_set(port, rxq); ++ mvpp2_rx_time_coal_set(port, rxq); + + /* Add number of descriptors ready for receiving packets */ + mvpp2_rxq_status_update(port, rxq->id, 0, rxq->size); +@@ -4564,10 +6032,15 @@ static void mvpp2_rxq_drop_pkts(struct mvpp2_port *port, + + for (i = 0; i < rx_received; i++) { + struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq); +- u32 bm = mvpp2_bm_cookie_build(rx_desc); ++ u32 status = mvpp2_rxdesc_status_get(port, rx_desc); ++ int pool; ++ ++ pool = (status & MVPP2_RXD_BM_POOL_ID_MASK) >> ++ MVPP2_RXD_BM_POOL_ID_OFFS; + +- mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr, +- rx_desc->buf_cookie); ++ mvpp2_bm_pool_put(port, pool, ++ mvpp2_rxdesc_dma_addr_get(port, rx_desc), ++ mvpp2_rxdesc_cookie_get(port, rx_desc)); + } + mvpp2_rxq_status_update(port, rxq->id, rx_received, rx_received); + } +@@ -4576,26 +6049,30 @@ static void mvpp2_rxq_drop_pkts(struct mvpp2_port *port, + static void mvpp2_rxq_deinit(struct mvpp2_port *port, + struct mvpp2_rx_queue *rxq) + { ++ int cpu; ++ + mvpp2_rxq_drop_pkts(port, rxq); + + if (rxq->descs) + dma_free_coherent(port->dev->dev.parent, + rxq->size * MVPP2_DESC_ALIGNED_SIZE, + rxq->descs, +- rxq->descs_phys); ++ rxq->descs_dma); + + rxq->descs = NULL; + rxq->last_desc = 0; + rxq->next_desc_to_proc = 0; +- rxq->descs_phys = 0; ++ rxq->descs_dma = 0; + + /* Clear Rx descriptors queue starting address and size; + * free descriptor number + */ + mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0); +- mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); +- mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, 0); +- mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, 0); ++ cpu = get_cpu(); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_NUM_REG, rxq->id); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_ADDR_REG, 0); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_SIZE_REG, 0); ++ put_cpu(); + } + + /* Create and initialize a Tx queue */ +@@ -4611,23 +6088,25 @@ static int mvpp2_txq_init(struct mvpp2_port *port, + /* Allocate memory for Tx descriptors */ + txq->descs = dma_alloc_coherent(port->dev->dev.parent, + txq->size * MVPP2_DESC_ALIGNED_SIZE, +- &txq->descs_phys, GFP_KERNEL); ++ &txq->descs_dma, GFP_KERNEL); + if (!txq->descs) + return -ENOMEM; + + txq->last_desc = txq->size - 1; + + /* Set Tx descriptors queue starting address - indirect access */ +- mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); +- mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, txq->descs_phys); +- mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, txq->size & +- MVPP2_TXQ_DESC_SIZE_MASK); +- mvpp2_write(port->priv, MVPP2_TXQ_INDEX_REG, 0); +- mvpp2_write(port->priv, MVPP2_TXQ_RSVD_CLR_REG, +- txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); +- val = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG); ++ cpu = get_cpu(); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_ADDR_REG, ++ txq->descs_dma); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_SIZE_REG, ++ txq->size & MVPP2_TXQ_DESC_SIZE_MASK); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_INDEX_REG, 0); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_RSVD_CLR_REG, ++ txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); ++ val = mvpp2_percpu_read(port->priv, cpu, MVPP2_TXQ_PENDING_REG); + val &= ~MVPP2_TXQ_PENDING_MASK; +- mvpp2_write(port->priv, MVPP2_TXQ_PENDING_REG, val); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PENDING_REG, val); + + /* Calculate base address in prefetch buffer. We reserve 16 descriptors + * for each existing TXQ. +@@ -4638,9 +6117,10 @@ static int mvpp2_txq_init(struct mvpp2_port *port, + desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) + + (txq->log_id * desc_per_txq); + +- mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, +- MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | +- MVPP2_PREF_BUF_THRESH(desc_per_txq/2)); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG, ++ MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | ++ MVPP2_PREF_BUF_THRESH(desc_per_txq / 2)); ++ put_cpu(); + + /* WRR / EJP configuration - indirect access */ + tx_port_num = mvpp2_egress_port(port); +@@ -4659,31 +6139,31 @@ static int mvpp2_txq_init(struct mvpp2_port *port, + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + txq_pcpu->size = txq->size; +- txq_pcpu->buffs = kmalloc(txq_pcpu->size * +- sizeof(struct mvpp2_txq_pcpu_buf), +- GFP_KERNEL); ++ txq_pcpu->buffs = kmalloc_array(txq_pcpu->size, ++ sizeof(*txq_pcpu->buffs), ++ GFP_KERNEL); + if (!txq_pcpu->buffs) +- goto error; ++ return -ENOMEM; + + txq_pcpu->count = 0; + txq_pcpu->reserved_num = 0; + txq_pcpu->txq_put_index = 0; + txq_pcpu->txq_get_index = 0; +- } ++ txq_pcpu->tso_headers = NULL; + +- return 0; ++ txq_pcpu->stop_threshold = txq->size - MVPP2_MAX_SKB_DESCS; ++ txq_pcpu->wake_threshold = txq_pcpu->stop_threshold / 2; + +-error: +- for_each_present_cpu(cpu) { +- txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); +- kfree(txq_pcpu->buffs); ++ txq_pcpu->tso_headers = ++ dma_alloc_coherent(port->dev->dev.parent, ++ txq_pcpu->size * TSO_HEADER_SIZE, ++ &txq_pcpu->tso_headers_dma, ++ GFP_KERNEL); ++ if (!txq_pcpu->tso_headers) ++ return -ENOMEM; + } + +- dma_free_coherent(port->dev->dev.parent, +- txq->size * MVPP2_DESC_ALIGNED_SIZE, +- txq->descs, txq->descs_phys); +- +- return -ENOMEM; ++ return 0; + } + + /* Free allocated TXQ resources */ +@@ -4696,25 +6176,35 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + kfree(txq_pcpu->buffs); ++ ++ if (txq_pcpu->tso_headers) ++ dma_free_coherent(port->dev->dev.parent, ++ txq_pcpu->size * TSO_HEADER_SIZE, ++ txq_pcpu->tso_headers, ++ txq_pcpu->tso_headers_dma); ++ ++ txq_pcpu->tso_headers = NULL; + } + + if (txq->descs) + dma_free_coherent(port->dev->dev.parent, + txq->size * MVPP2_DESC_ALIGNED_SIZE, +- txq->descs, txq->descs_phys); ++ txq->descs, txq->descs_dma); + + txq->descs = NULL; + txq->last_desc = 0; + txq->next_desc_to_proc = 0; +- txq->descs_phys = 0; ++ txq->descs_dma = 0; + + /* Set minimum bandwidth for disabled TXQs */ + mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->id), 0); + + /* Set Tx descriptors queue starting address and size */ +- mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); +- mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, 0); +- mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, 0); ++ cpu = get_cpu(); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_ADDR_REG, 0); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_SIZE_REG, 0); ++ put_cpu(); + } + + /* Cleanup Tx ports */ +@@ -4724,10 +6214,11 @@ static void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq) + int delay, pending, cpu; + u32 val; + +- mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); +- val = mvpp2_read(port->priv, MVPP2_TXQ_PREF_BUF_REG); ++ cpu = get_cpu(); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); ++ val = mvpp2_percpu_read(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG); + val |= MVPP2_TXQ_DRAIN_EN_MASK; +- mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG, val); + + /* The napi queue has been stopped so wait for all packets + * to be transmitted. +@@ -4743,11 +6234,14 @@ static void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq) + mdelay(1); + delay++; + +- pending = mvpp2_txq_pend_desc_num_get(port, txq); ++ pending = mvpp2_percpu_read(port->priv, cpu, ++ MVPP2_TXQ_PENDING_REG); ++ pending &= MVPP2_TXQ_PENDING_MASK; + } while (pending); + + val &= ~MVPP2_TXQ_DRAIN_EN_MASK; +- mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); ++ mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG, val); ++ put_cpu(); + + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); +@@ -4775,7 +6269,7 @@ static void mvpp2_cleanup_txqs(struct mvpp2_port *port) + val |= MVPP2_TX_PORT_FLUSH_MASK(port->id); + mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val); + +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + txq = port->txqs[queue]; + mvpp2_txq_clean(port, txq); + mvpp2_txq_deinit(port, txq); +@@ -4792,7 +6286,7 @@ static void mvpp2_cleanup_rxqs(struct mvpp2_port *port) + { + int queue; + +- for (queue = 0; queue < rxq_number; queue++) ++ for (queue = 0; queue < port->nrxqs; queue++) + mvpp2_rxq_deinit(port, port->rxqs[queue]); + } + +@@ -4801,7 +6295,7 @@ static int mvpp2_setup_rxqs(struct mvpp2_port *port) + { + int queue, err; + +- for (queue = 0; queue < rxq_number; queue++) { ++ for (queue = 0; queue < port->nrxqs; queue++) { + err = mvpp2_rxq_init(port, port->rxqs[queue]); + if (err) + goto err_cleanup; +@@ -4819,13 +6313,21 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port) + struct mvpp2_tx_queue *txq; + int queue, err; + +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + txq = port->txqs[queue]; + err = mvpp2_txq_init(port, txq); + if (err) + goto err_cleanup; + } + ++ if (port->has_tx_irqs) { ++ mvpp2_tx_time_coal_set(port); ++ for (queue = 0; queue < port->ntxqs; queue++) { ++ txq = port->txqs[queue]; ++ mvpp2_tx_pkts_coal_set(port, txq); ++ } ++ } ++ + on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1); + return 0; + +@@ -4837,13 +6339,97 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port) + /* The callback for per-port interrupt */ + static irqreturn_t mvpp2_isr(int irq, void *dev_id) + { +- struct mvpp2_port *port = (struct mvpp2_port *)dev_id; ++ struct mvpp2_queue_vector *qv = dev_id; ++ ++ mvpp2_qvec_interrupt_disable(qv); ++ ++ napi_schedule(&qv->napi); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Per-port interrupt for link status changes */ ++static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id) ++{ ++ struct mvpp2_port *port = (struct mvpp2_port *)dev_id; ++ struct net_device *dev = port->dev; ++ bool event = false, link = false; ++ u32 val; ++ ++ mvpp22_gop_mask_irq(port); ++ ++ if (port->gop_id == 0 && ++ port->phy_interface == PHY_INTERFACE_MODE_10GKR) { ++ val = readl(port->base + MVPP22_XLG_INT_STAT); ++ if (val & MVPP22_XLG_INT_STAT_LINK) { ++ event = true; ++ val = readl(port->base + MVPP22_XLG_STATUS); ++ if (val & MVPP22_XLG_STATUS_LINK_UP) ++ link = true; ++ } ++ } else if (phy_interface_mode_is_rgmii(port->phy_interface) || ++ port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ val = readl(port->base + MVPP22_GMAC_INT_STAT); ++ if (val & MVPP22_GMAC_INT_STAT_LINK) { ++ event = true; ++ val = readl(port->base + MVPP2_GMAC_STATUS0); ++ if (val & MVPP2_GMAC_STATUS0_LINK_UP) ++ link = true; ++ } ++ } ++ ++ if (!netif_running(dev) || !event) ++ goto handled; ++ ++ if (link) { ++ mvpp2_interrupts_enable(port); ++ ++ mvpp2_egress_enable(port); ++ mvpp2_ingress_enable(port); ++ netif_carrier_on(dev); ++ netif_tx_wake_all_queues(dev); ++ } else { ++ netif_tx_stop_all_queues(dev); ++ netif_carrier_off(dev); ++ mvpp2_ingress_disable(port); ++ mvpp2_egress_disable(port); ++ ++ mvpp2_interrupts_disable(port); ++ } ++ ++handled: ++ mvpp22_gop_unmask_irq(port); ++ return IRQ_HANDLED; ++} ++ ++static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port, ++ struct phy_device *phydev) ++{ ++ u32 val; + +- mvpp2_interrupts_disable(port); ++ if (port->phy_interface != PHY_INTERFACE_MODE_RGMII && ++ port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID && ++ port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ port->phy_interface != PHY_INTERFACE_MODE_SGMII) ++ return; + +- napi_schedule(&port->napi); ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | ++ MVPP2_GMAC_CONFIG_GMII_SPEED | ++ MVPP2_GMAC_CONFIG_FULL_DUPLEX | ++ MVPP2_GMAC_AN_SPEED_EN | ++ MVPP2_GMAC_AN_DUPLEX_EN); + +- return IRQ_HANDLED; ++ if (phydev->duplex) ++ val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; ++ ++ if (phydev->speed == SPEED_1000) ++ val |= MVPP2_GMAC_CONFIG_GMII_SPEED; ++ else if (phydev->speed == SPEED_100) ++ val |= MVPP2_GMAC_CONFIG_MII_SPEED; ++ ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + } + + /* Adjust link */ +@@ -4851,58 +6437,72 @@ static void mvpp2_link_event(struct net_device *dev) + { + struct mvpp2_port *port = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; +- int status_change = 0; ++ bool link_reconfigured = false; + u32 val; + + if (phydev->link) { ++ if (port->phy_interface != phydev->interface && port->comphy) { ++ /* disable current port for reconfiguration */ ++ mvpp2_interrupts_disable(port); ++ netif_carrier_off(port->dev); ++ mvpp2_port_disable(port); ++ phy_power_off(port->comphy); ++ ++ /* comphy reconfiguration */ ++ port->phy_interface = phydev->interface; ++ mvpp22_comphy_init(port); ++ ++ /* gop/mac reconfiguration */ ++ mvpp22_gop_init(port); ++ mvpp2_port_mii_set(port); ++ ++ link_reconfigured = true; ++ } ++ + if ((port->speed != phydev->speed) || + (port->duplex != phydev->duplex)) { +- u32 val; +- +- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); +- val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | +- MVPP2_GMAC_CONFIG_GMII_SPEED | +- MVPP2_GMAC_CONFIG_FULL_DUPLEX | +- MVPP2_GMAC_AN_SPEED_EN | +- MVPP2_GMAC_AN_DUPLEX_EN); +- +- if (phydev->duplex) +- val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; +- +- if (phydev->speed == SPEED_1000) +- val |= MVPP2_GMAC_CONFIG_GMII_SPEED; +- else if (phydev->speed == SPEED_100) +- val |= MVPP2_GMAC_CONFIG_MII_SPEED; +- +- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ mvpp2_gmac_set_autoneg(port, phydev); + + port->duplex = phydev->duplex; + port->speed = phydev->speed; + } + } + +- if (phydev->link != port->link) { +- if (!phydev->link) { +- port->duplex = -1; +- port->speed = 0; +- } +- ++ if (phydev->link != port->link || link_reconfigured) { + port->link = phydev->link; +- status_change = 1; +- } + +- if (status_change) { + if (phydev->link) { +- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); +- val |= (MVPP2_GMAC_FORCE_LINK_PASS | +- MVPP2_GMAC_FORCE_LINK_DOWN); +- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || ++ port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || ++ port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || ++ port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID || ++ port->phy_interface == PHY_INTERFACE_MODE_SGMII) { ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val |= (MVPP2_GMAC_FORCE_LINK_PASS | ++ MVPP2_GMAC_FORCE_LINK_DOWN); ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ } ++ ++ mvpp2_interrupts_enable(port); ++ mvpp2_port_enable(port); ++ + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); ++ netif_carrier_on(dev); ++ netif_tx_wake_all_queues(dev); + } else { ++ port->duplex = -1; ++ port->speed = 0; ++ ++ netif_tx_stop_all_queues(dev); ++ netif_carrier_off(dev); + mvpp2_ingress_disable(port); + mvpp2_egress_disable(port); ++ ++ mvpp2_port_disable(port); ++ mvpp2_interrupts_disable(port); + } ++ + phy_print_status(phydev); + } + } +@@ -4931,8 +6531,8 @@ static void mvpp2_tx_proc_cb(unsigned long data) + port_pcpu->timer_scheduled = false; + + /* Process all the Tx queues */ +- cause = (1 << txq_number) - 1; +- tx_todo = mvpp2_tx_done(port, cause); ++ cause = (1 << port->ntxqs) - 1; ++ tx_todo = mvpp2_tx_done(port, cause, smp_processor_id()); + + /* Set the timer in case not all the packets were processed */ + if (tx_todo) +@@ -4956,20 +6556,21 @@ static enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer) + static void mvpp2_rx_error(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) + { +- u32 status = rx_desc->status; ++ u32 status = mvpp2_rxdesc_status_get(port, rx_desc); ++ size_t sz = mvpp2_rxdesc_size_get(port, rx_desc); + + switch (status & MVPP2_RXD_ERR_CODE_MASK) { + case MVPP2_RXD_ERR_CRC: +- netdev_err(port->dev, "bad rx status %08x (crc error), size=%d\n", +- status, rx_desc->data_size); ++ netdev_err(port->dev, "bad rx status %08x (crc error), size=%zu\n", ++ status, sz); + break; + case MVPP2_RXD_ERR_OVERRUN: +- netdev_err(port->dev, "bad rx status %08x (overrun error), size=%d\n", +- status, rx_desc->data_size); ++ netdev_err(port->dev, "bad rx status %08x (overrun error), size=%zu\n", ++ status, sz); + break; + case MVPP2_RXD_ERR_RESOURCE: +- netdev_err(port->dev, "bad rx status %08x (resource error), size=%d\n", +- status, rx_desc->data_size); ++ netdev_err(port->dev, "bad rx status %08x (resource error), size=%zu\n", ++ status, sz); + break; + } + } +@@ -4994,23 +6595,20 @@ static void mvpp2_rx_csum(struct mvpp2_port *port, u32 status, + + /* Reuse skb if possible, or allocate a new skb and add it to BM pool */ + static int mvpp2_rx_refill(struct mvpp2_port *port, +- struct mvpp2_bm_pool *bm_pool, +- u32 bm, int is_recycle) ++ struct mvpp2_bm_pool *bm_pool, int pool) + { +- struct sk_buff *skb; +- dma_addr_t phys_addr; +- +- if (is_recycle && +- (atomic_read(&bm_pool->in_use) < bm_pool->in_use_thresh)) +- return 0; ++ dma_addr_t dma_addr; ++ phys_addr_t phys_addr; ++ void *buf; + + /* No recycle or too many buffers are in use, so allocate a new skb */ +- skb = mvpp2_skb_alloc(port, bm_pool, &phys_addr, GFP_ATOMIC); +- if (!skb) ++ buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, &phys_addr, ++ GFP_ATOMIC); ++ if (!buf) + return -ENOMEM; + +- mvpp2_pool_refill(port, bm, (u32)phys_addr, (u32)skb); +- atomic_dec(&bm_pool->in_use); ++ mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr); ++ + return 0; + } + +@@ -5020,15 +6618,14 @@ static u32 mvpp2_skb_tx_csum(struct mvpp2_port *port, struct sk_buff *skb) + if (skb->ip_summed == CHECKSUM_PARTIAL) { + int ip_hdr_len = 0; + u8 l4_proto; +- __be16 l3_proto = vlan_get_protocol(skb); + +- if (l3_proto == htons(ETH_P_IP)) { ++ if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *ip4h = ip_hdr(skb); + + /* Calculate IPv4 checksum and L4 checksum */ + ip_hdr_len = ip4h->ihl; + l4_proto = ip4h->protocol; +- } else if (l3_proto == htons(ETH_P_IPV6)) { ++ } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *ip6h = ipv6_hdr(skb); + + /* Read l4_protocol from one of IPv6 extra headers */ +@@ -5040,52 +6637,15 @@ static u32 mvpp2_skb_tx_csum(struct mvpp2_port *port, struct sk_buff *skb) + } + + return mvpp2_txq_desc_csum(skb_network_offset(skb), +- l3_proto, ip_hdr_len, l4_proto); ++ skb->protocol, ip_hdr_len, l4_proto); + } + + return MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE; + } + +-static void mvpp2_buff_hdr_rx(struct mvpp2_port *port, +- struct mvpp2_rx_desc *rx_desc) +-{ +- struct mvpp2_buff_hdr *buff_hdr; +- struct sk_buff *skb; +- u32 rx_status = rx_desc->status; +- u32 buff_phys_addr; +- u32 buff_virt_addr; +- u32 buff_phys_addr_next; +- u32 buff_virt_addr_next; +- int mc_id; +- int pool_id; +- +- pool_id = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> +- MVPP2_RXD_BM_POOL_ID_OFFS; +- buff_phys_addr = rx_desc->buf_phys_addr; +- buff_virt_addr = rx_desc->buf_cookie; +- +- do { +- skb = (struct sk_buff *)buff_virt_addr; +- buff_hdr = (struct mvpp2_buff_hdr *)skb->head; +- +- mc_id = MVPP2_B_HDR_INFO_MC_ID(buff_hdr->info); +- +- buff_phys_addr_next = buff_hdr->next_buff_phys_addr; +- buff_virt_addr_next = buff_hdr->next_buff_virt_addr; +- +- /* Release buffer */ +- mvpp2_bm_pool_mc_put(port, pool_id, buff_phys_addr, +- buff_virt_addr, mc_id); +- +- buff_phys_addr = buff_phys_addr_next; +- buff_virt_addr = buff_virt_addr_next; +- +- } while (!MVPP2_B_HDR_INFO_IS_LAST(buff_hdr->info)); +-} +- + /* Main rx processing */ +-static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, +- struct mvpp2_rx_queue *rxq) ++static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, ++ int rx_todo, struct mvpp2_rx_queue *rxq) + { + struct net_device *dev = port->dev; + int rx_received; +@@ -5102,23 +6662,24 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, + struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq); + struct mvpp2_bm_pool *bm_pool; + struct sk_buff *skb; +- dma_addr_t phys_addr; +- u32 bm, rx_status; ++ unsigned int frag_size; ++ dma_addr_t dma_addr; ++ phys_addr_t phys_addr; ++ u32 rx_status; + int pool, rx_bytes, err; ++ void *data; + + rx_done++; +- rx_status = rx_desc->status; +- rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE; +- phys_addr = rx_desc->buf_phys_addr; +- +- bm = mvpp2_bm_cookie_build(rx_desc); +- pool = mvpp2_bm_cookie_pool_get(bm); ++ rx_status = mvpp2_rxdesc_status_get(port, rx_desc); ++ rx_bytes = mvpp2_rxdesc_size_get(port, rx_desc); ++ rx_bytes -= MVPP2_MH_SIZE; ++ dma_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc); ++ phys_addr = mvpp2_rxdesc_cookie_get(port, rx_desc); ++ data = (void *)phys_to_virt(phys_addr); ++ ++ pool = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> ++ MVPP2_RXD_BM_POOL_ID_OFFS; + bm_pool = &port->priv->bm_pools[pool]; +- /* Check if buffer header is used */ +- if (rx_status & MVPP2_RXD_BUF_HDR) { +- mvpp2_buff_hdr_rx(port, rx_desc); +- continue; +- } + + /* In case of an error, release the requested buffer pointer + * to the Buffer Manager. This request process is controlled +@@ -5126,36 +6687,43 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, + * comprised by the RX descriptor. + */ + if (rx_status & MVPP2_RXD_ERR_SUMMARY) { +- err_drop_frame: ++err_drop_frame: + dev->stats.rx_errors++; + mvpp2_rx_error(port, rx_desc); + /* Return the buffer to the pool */ +- mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr, +- rx_desc->buf_cookie); ++ mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr); + continue; + } + +- skb = (struct sk_buff *)rx_desc->buf_cookie; ++ if (bm_pool->frag_size > PAGE_SIZE) ++ frag_size = 0; ++ else ++ frag_size = bm_pool->frag_size; ++ ++ skb = build_skb(data, frag_size); ++ if (!skb) { ++ netdev_warn(port->dev, "skb build failed\n"); ++ goto err_drop_frame; ++ } + +- err = mvpp2_rx_refill(port, bm_pool, bm, 0); ++ err = mvpp2_rx_refill(port, bm_pool, pool); + if (err) { + netdev_err(port->dev, "failed to refill BM pools\n"); + goto err_drop_frame; + } + +- dma_unmap_single(dev->dev.parent, phys_addr, ++ dma_unmap_single(dev->dev.parent, dma_addr, + bm_pool->buf_size, DMA_FROM_DEVICE); + + rcvd_pkts++; + rcvd_bytes += rx_bytes; +- atomic_inc(&bm_pool->in_use); + +- skb_reserve(skb, MVPP2_MH_SIZE); ++ skb_reserve(skb, MVPP2_MH_SIZE + NET_SKB_PAD); + skb_put(skb, rx_bytes); + skb->protocol = eth_type_trans(skb, dev); + mvpp2_rx_csum(port, rx_status, skb); + +- napi_gro_receive(&port->napi, skb); ++ napi_gro_receive(napi, skb); + } + + if (rcvd_pkts) { +@@ -5175,11 +6743,18 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, + } + + static inline void +-tx_desc_unmap_put(struct device *dev, struct mvpp2_tx_queue *txq, ++tx_desc_unmap_put(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, + struct mvpp2_tx_desc *desc) + { +- dma_unmap_single(dev, desc->buf_phys_addr, +- desc->data_size, DMA_TO_DEVICE); ++ struct mvpp2_txq_pcpu *txq_pcpu = this_cpu_ptr(txq->pcpu); ++ ++ dma_addr_t buf_dma_addr = ++ mvpp2_txdesc_dma_addr_get(port, desc); ++ size_t buf_sz = ++ mvpp2_txdesc_size_get(port, desc); ++ if (!IS_TSO_HEADER(txq_pcpu, buf_dma_addr)) ++ dma_unmap_single(port->dev->dev.parent, buf_dma_addr, ++ buf_sz, DMA_TO_DEVICE); + mvpp2_txq_desc_put(txq); + } + +@@ -5191,52 +6766,164 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, + struct mvpp2_txq_pcpu *txq_pcpu = this_cpu_ptr(txq->pcpu); + struct mvpp2_tx_desc *tx_desc; + int i; +- dma_addr_t buf_phys_addr; ++ dma_addr_t buf_dma_addr; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + void *addr = page_address(frag->page.p) + frag->page_offset; + + tx_desc = mvpp2_txq_next_desc_get(aggr_txq); +- tx_desc->phys_txq = txq->id; +- tx_desc->data_size = frag->size; ++ mvpp2_txdesc_txq_set(port, tx_desc, txq->id); ++ mvpp2_txdesc_size_set(port, tx_desc, frag->size); + +- buf_phys_addr = dma_map_single(port->dev->dev.parent, addr, +- tx_desc->data_size, ++ buf_dma_addr = dma_map_single(port->dev->dev.parent, addr, ++ frag->size, + DMA_TO_DEVICE); +- if (dma_mapping_error(port->dev->dev.parent, buf_phys_addr)) { ++ if (dma_mapping_error(port->dev->dev.parent, buf_dma_addr)) { + mvpp2_txq_desc_put(txq); +- goto error; ++ goto cleanup; + } + +- tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_ALIGN; +- tx_desc->buf_phys_addr = buf_phys_addr & (~MVPP2_TX_DESC_ALIGN); ++ mvpp2_txdesc_dma_addr_set(port, tx_desc, buf_dma_addr); + + if (i == (skb_shinfo(skb)->nr_frags - 1)) { + /* Last descriptor */ +- tx_desc->command = MVPP2_TXD_L_DESC; +- mvpp2_txq_inc_put(txq_pcpu, skb, tx_desc); ++ mvpp2_txdesc_cmd_set(port, tx_desc, ++ MVPP2_TXD_L_DESC); ++ mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); + } else { + /* Descriptor in the middle: Not First, Not Last */ +- tx_desc->command = 0; +- mvpp2_txq_inc_put(txq_pcpu, NULL, tx_desc); ++ mvpp2_txdesc_cmd_set(port, tx_desc, 0); ++ mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); + } + } + + return 0; +- +-error: ++cleanup: + /* Release all descriptors that were used to map fragments of + * this packet, as well as the corresponding DMA mappings + */ + for (i = i - 1; i >= 0; i--) { + tx_desc = txq->descs + i; +- tx_desc_unmap_put(port->dev->dev.parent, txq, tx_desc); ++ tx_desc_unmap_put(port, txq, tx_desc); + } + + return -ENOMEM; + } + ++static inline void mvpp2_tso_put_hdr(struct sk_buff *skb, ++ struct net_device *dev, ++ struct mvpp2_tx_queue *txq, ++ struct mvpp2_tx_queue *aggr_txq, ++ struct mvpp2_txq_pcpu *txq_pcpu, ++ int hdr_sz) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq); ++ dma_addr_t addr; ++ ++ mvpp2_txdesc_txq_set(port, tx_desc, txq->id); ++ mvpp2_txdesc_size_set(port, tx_desc, hdr_sz); ++ ++ addr = txq_pcpu->tso_headers_dma + ++ txq_pcpu->txq_put_index * TSO_HEADER_SIZE; ++ mvpp2_txdesc_dma_addr_set(port, tx_desc, addr); ++ ++ mvpp2_txdesc_cmd_set(port, tx_desc, mvpp2_skb_tx_csum(port, skb) | ++ MVPP2_TXD_F_DESC | ++ MVPP2_TXD_PADDING_DISABLE); ++ mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); ++} ++ ++static inline int mvpp2_tso_put_data(struct sk_buff *skb, ++ struct net_device *dev, struct tso_t *tso, ++ struct mvpp2_tx_queue *txq, ++ struct mvpp2_tx_queue *aggr_txq, ++ struct mvpp2_txq_pcpu *txq_pcpu, ++ int sz, bool left, bool last) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq); ++ dma_addr_t buf_dma_addr; ++ ++ mvpp2_txdesc_txq_set(port, tx_desc, txq->id); ++ mvpp2_txdesc_size_set(port, tx_desc, sz); ++ ++ buf_dma_addr = dma_map_single(dev->dev.parent, tso->data, sz, ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) { ++ mvpp2_txq_desc_put(txq); ++ return -ENOMEM; ++ } ++ ++ mvpp2_txdesc_dma_addr_set(port, tx_desc, buf_dma_addr); ++ ++ if (!left) { ++ mvpp2_txdesc_cmd_set(port, tx_desc, MVPP2_TXD_L_DESC); ++ if (last) { ++ mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); ++ return 0; ++ } ++ } else { ++ mvpp2_txdesc_cmd_set(port, tx_desc, 0); ++ } ++ ++ mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); ++ return 0; ++} ++ ++static int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev, ++ struct mvpp2_tx_queue *txq, ++ struct mvpp2_tx_queue *aggr_txq, ++ struct mvpp2_txq_pcpu *txq_pcpu) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ struct tso_t tso; ++ int hdr_sz = skb_transport_offset(skb) + tcp_hdrlen(skb); ++ int i, len, descs = 0; ++ ++ /* Check number of available descriptors */ ++ if (mvpp2_aggr_desc_num_check(port->priv, aggr_txq, ++ tso_count_descs(skb)) || ++ mvpp2_txq_reserved_desc_num_proc(port->priv, txq, txq_pcpu, ++ tso_count_descs(skb))) ++ return 0; ++ ++ tso_start(skb, &tso); ++ len = skb->len - hdr_sz; ++ while (len > 0) { ++ int left = min_t(int, skb_shinfo(skb)->gso_size, len); ++ char *hdr = txq_pcpu->tso_headers + ++ txq_pcpu->txq_put_index * TSO_HEADER_SIZE; ++ ++ len -= left; ++ descs++; ++ ++ tso_build_hdr(skb, hdr, &tso, left, len == 0); ++ mvpp2_tso_put_hdr(skb, dev, txq, aggr_txq, txq_pcpu, hdr_sz); ++ ++ while (left > 0) { ++ int sz = min_t(int, tso.size, left); ++ left -= sz; ++ descs++; ++ ++ if (mvpp2_tso_put_data(skb, dev, &tso, txq, aggr_txq, ++ txq_pcpu, sz, left, len == 0)) ++ goto release; ++ tso_build_data(skb, &tso, sz); ++ } ++ } ++ ++ return descs; ++ ++release: ++ for (i = descs - 1; i >= 0; i--) { ++ struct mvpp2_tx_desc *tx_desc = txq->descs + i; ++ tx_desc_unmap_put(port, txq, tx_desc); ++ } ++ return 0; ++} ++ + /* Main tx processing */ + static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) + { +@@ -5244,7 +6931,7 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) + struct mvpp2_tx_queue *txq, *aggr_txq; + struct mvpp2_txq_pcpu *txq_pcpu; + struct mvpp2_tx_desc *tx_desc; +- dma_addr_t buf_phys_addr; ++ dma_addr_t buf_dma_addr; + int frags = 0; + u16 txq_id; + u32 tx_cmd; +@@ -5254,6 +6941,10 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) + txq_pcpu = this_cpu_ptr(txq->pcpu); + aggr_txq = &port->priv->aggr_txqs[smp_processor_id()]; + ++ if (skb_is_gso(skb)) { ++ frags = mvpp2_tx_tso(skb, dev, txq, aggr_txq, txq_pcpu); ++ goto out; ++ } + frags = skb_shinfo(skb)->nr_frags + 1; + + /* Check number of available descriptors */ +@@ -5266,56 +6957,54 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) + + /* Get a descriptor for the first part of the packet */ + tx_desc = mvpp2_txq_next_desc_get(aggr_txq); +- tx_desc->phys_txq = txq->id; +- tx_desc->data_size = skb_headlen(skb); ++ mvpp2_txdesc_txq_set(port, tx_desc, txq->id); ++ mvpp2_txdesc_size_set(port, tx_desc, skb_headlen(skb)); + +- buf_phys_addr = dma_map_single(dev->dev.parent, skb->data, +- tx_desc->data_size, DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(dev->dev.parent, buf_phys_addr))) { ++ buf_dma_addr = dma_map_single(dev->dev.parent, skb->data, ++ skb_headlen(skb), DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) { + mvpp2_txq_desc_put(txq); + frags = 0; + goto out; + } +- tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_ALIGN; +- tx_desc->buf_phys_addr = buf_phys_addr & ~MVPP2_TX_DESC_ALIGN; ++ ++ mvpp2_txdesc_dma_addr_set(port, tx_desc, buf_dma_addr); + + tx_cmd = mvpp2_skb_tx_csum(port, skb); + + if (frags == 1) { + /* First and Last descriptor */ + tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC; +- tx_desc->command = tx_cmd; +- mvpp2_txq_inc_put(txq_pcpu, skb, tx_desc); ++ mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd); ++ mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); + } else { + /* First but not Last */ + tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_PADDING_DISABLE; +- tx_desc->command = tx_cmd; +- mvpp2_txq_inc_put(txq_pcpu, NULL, tx_desc); ++ mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd); ++ mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); + + /* Continue with other skb fragments */ + if (mvpp2_tx_frag_process(port, skb, aggr_txq, txq)) { +- tx_desc_unmap_put(port->dev->dev.parent, txq, tx_desc); ++ tx_desc_unmap_put(port, txq, tx_desc); + frags = 0; +- goto out; + } + } + +- txq_pcpu->reserved_num -= frags; +- txq_pcpu->count += frags; +- aggr_txq->count += frags; +- +- /* Enable transmit */ +- wmb(); +- mvpp2_aggr_txq_pend_desc_add(port, frags); +- +- if (txq_pcpu->size - txq_pcpu->count < MAX_SKB_FRAGS + 1) { +- struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); +- +- netif_tx_stop_queue(nq); +- } + out: + if (frags > 0) { + struct mvpp2_pcpu_stats *stats = this_cpu_ptr(port->stats); ++ struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); ++ ++ txq_pcpu->reserved_num -= frags; ++ txq_pcpu->count += frags; ++ aggr_txq->count += frags; ++ ++ /* Enable transmit */ ++ wmb(); ++ mvpp2_aggr_txq_pend_desc_add(port, frags); ++ ++ if (txq_pcpu->count >= txq_pcpu->stop_threshold) ++ netif_tx_stop_queue(nq); + + u64_stats_update_begin(&stats->syncp); + stats->tx_packets++; +@@ -5327,11 +7016,12 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) + } + + /* Finalize TX processing */ +- if (txq_pcpu->count >= txq->done_pkts_coal) ++ if (!port->has_tx_irqs && txq_pcpu->count >= txq->done_pkts_coal) + mvpp2_txq_done(port, txq, txq_pcpu); + + /* Set the timer in case not all frags were processed */ +- if (txq_pcpu->count <= frags && txq_pcpu->count > 0) { ++ if (!port->has_tx_irqs && txq_pcpu->count <= frags && ++ txq_pcpu->count > 0) { + struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu); + + mvpp2_timer_set(port_pcpu); +@@ -5352,9 +7042,13 @@ static inline void mvpp2_cause_error(struct net_device *dev, int cause) + + static int mvpp2_poll(struct napi_struct *napi, int budget) + { +- u32 cause_rx_tx, cause_rx, cause_misc; ++ u32 cause_rx_tx, cause_rx, cause_tx, cause_misc; + int rx_done = 0; + struct mvpp2_port *port = netdev_priv(napi->dev); ++ struct mvpp2_queue_vector *qv; ++ int cpu = smp_processor_id(); ++ ++ qv = container_of(napi, struct mvpp2_queue_vector, napi); + + /* Rx/Tx cause register + * +@@ -5366,24 +7060,30 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) + * + * Each CPU has its own Rx/Tx cause register + */ +- cause_rx_tx = mvpp2_read(port->priv, +- MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); +- cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; +- cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; ++ cause_rx_tx = mvpp2_percpu_read_relaxed(port->priv, qv->sw_thread_id, ++ MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); + ++ cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; + if (cause_misc) { + mvpp2_cause_error(port->dev, cause_misc); + + /* Clear the cause register */ + mvpp2_write(port->priv, MVPP2_ISR_MISC_CAUSE_REG, 0); +- mvpp2_write(port->priv, MVPP2_ISR_RX_TX_CAUSE_REG(port->id), +- cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); ++ mvpp2_percpu_write(port->priv, cpu, ++ MVPP2_ISR_RX_TX_CAUSE_REG(port->id), ++ cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); + } + +- cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; ++ if (cause_tx) { ++ cause_tx >>= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET; ++ mvpp2_tx_done(port, cause_tx, qv->sw_thread_id); ++ } + + /* Process RX packets */ +- cause_rx |= port->pending_cause_rx; ++ cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ cause_rx <<= qv->first_rxq; ++ cause_rx |= qv->pending_cause_rx; + while (cause_rx && budget > 0) { + int count; + struct mvpp2_rx_queue *rxq; +@@ -5392,7 +7092,7 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) + if (!rxq) + break; + +- count = mvpp2_rx(port, budget, rxq); ++ count = mvpp2_rx(port, napi, budget, rxq); + rx_done += count; + budget -= count; + if (budget > 0) { +@@ -5408,9 +7108,9 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) + cause_rx = 0; + napi_complete(napi); + +- mvpp2_interrupts_enable(port); ++ mvpp2_qvec_interrupt_enable(qv); + } +- port->pending_cause_rx = cause_rx; ++ qv->pending_cause_rx = cause_rx; + return rx_done; + } + +@@ -5418,17 +7118,32 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) + static void mvpp2_start_dev(struct mvpp2_port *port) + { + struct net_device *ndev = port->dev; ++ int i; ++ ++ if (port->gop_id == 0 && ++ (port->phy_interface == PHY_INTERFACE_MODE_XAUI || ++ port->phy_interface == PHY_INTERFACE_MODE_10GKR)) ++ mvpp2_xlg_max_rx_size_set(port); ++ else ++ mvpp2_gmac_max_rx_size_set(port); + +- mvpp2_gmac_max_rx_size_set(port); + mvpp2_txp_max_tx_size_set(port); + +- napi_enable(&port->napi); ++ for (i = 0; i < port->nqvecs; i++) ++ napi_enable(&port->qvecs[i].napi); + + /* Enable interrupts on all CPUs */ + mvpp2_interrupts_enable(port); + ++ if (port->priv->hw_version == MVPP22) { ++ mvpp22_comphy_init(port); ++ mvpp22_gop_init(port); ++ } ++ ++ mvpp2_port_mii_set(port); + mvpp2_port_enable(port); +- phy_start(ndev->phydev); ++ if (ndev->phydev) ++ phy_start(ndev->phydev); + netif_tx_start_all_queues(port->dev); + } + +@@ -5436,6 +7151,7 @@ static void mvpp2_start_dev(struct mvpp2_port *port) + static void mvpp2_stop_dev(struct mvpp2_port *port) + { + struct net_device *ndev = port->dev; ++ int i; + + /* Stop new packets from arriving to RXQs */ + mvpp2_ingress_disable(port); +@@ -5445,37 +7161,17 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) + /* Disable interrupts on all CPUs */ + mvpp2_interrupts_disable(port); + +- napi_disable(&port->napi); ++ for (i = 0; i < port->nqvecs; i++) ++ napi_disable(&port->qvecs[i].napi); + + netif_carrier_off(port->dev); + netif_tx_stop_all_queues(port->dev); + + mvpp2_egress_disable(port); + mvpp2_port_disable(port); +- phy_stop(ndev->phydev); +-} +- +-/* Return positive if MTU is valid */ +-static inline int mvpp2_check_mtu_valid(struct net_device *dev, int mtu) +-{ +- if (mtu < 68) { +- netdev_err(dev, "cannot change mtu to less than 68\n"); +- return -EINVAL; +- } +- +- /* 9676 == 9700 - 20 and rounding to 8 */ +- if (mtu > 9676) { +- netdev_info(dev, "illegal MTU value %d, round to 9676\n", mtu); +- mtu = 9676; +- } +- +- if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) { +- netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu, +- ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8)); +- mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8); +- } +- +- return mtu; ++ if (ndev->phydev) ++ phy_stop(ndev->phydev); ++ phy_power_off(port->comphy); + } + + static int mvpp2_check_ringparam_valid(struct net_device *dev, +@@ -5487,16 +7183,22 @@ static int mvpp2_check_ringparam_valid(struct net_device *dev, + if (ring->rx_pending == 0 || ring->tx_pending == 0) + return -EINVAL; + +- if (ring->rx_pending > MVPP2_MAX_RXD) +- new_rx_pending = MVPP2_MAX_RXD; ++ if (ring->rx_pending > MVPP2_MAX_RXD_MAX) ++ new_rx_pending = MVPP2_MAX_RXD_MAX; + else if (!IS_ALIGNED(ring->rx_pending, 16)) + new_rx_pending = ALIGN(ring->rx_pending, 16); + +- if (ring->tx_pending > MVPP2_MAX_TXD) +- new_tx_pending = MVPP2_MAX_TXD; ++ if (ring->tx_pending > MVPP2_MAX_TXD_MAX) ++ new_tx_pending = MVPP2_MAX_TXD_MAX; + else if (!IS_ALIGNED(ring->tx_pending, 32)) + new_tx_pending = ALIGN(ring->tx_pending, 32); + ++ /* The Tx ring size cannot be smaller than the minimum number of ++ * descriptors needed for TSO. ++ */ ++ if (new_tx_pending < MVPP2_MAX_SKB_DESCS) ++ new_tx_pending = ALIGN(MVPP2_MAX_SKB_DESCS, 32); ++ + if (ring->rx_pending != new_rx_pending) { + netdev_info(dev, "illegal Rx ring size value %d, round to %d\n", + ring->rx_pending, new_rx_pending); +@@ -5512,7 +7214,7 @@ static int mvpp2_check_ringparam_valid(struct net_device *dev, + return 0; + } + +-static void mvpp2_get_mac_address(struct mvpp2_port *port, unsigned char *addr) ++static void mvpp21_get_mac_address(struct mvpp2_port *port, unsigned char *addr) + { + u32 mac_addr_l, mac_addr_m, mac_addr_h; + +@@ -5531,6 +7233,10 @@ static int mvpp2_phy_connect(struct mvpp2_port *port) + { + struct phy_device *phy_dev; + ++ /* No PHY is attached */ ++ if (!port->phy_node) ++ return 0; ++ + phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0, + port->phy_interface); + if (!phy_dev) { +@@ -5551,25 +7257,105 @@ static void mvpp2_phy_disconnect(struct mvpp2_port *port) + { + struct net_device *ndev = port->dev; + ++ if (!ndev->phydev) ++ return; ++ + phy_disconnect(ndev->phydev); + } + ++static int mvpp2_irqs_init(struct mvpp2_port *port) ++{ ++ int err, i; ++ ++ for (i = 0; i < port->nqvecs; i++) { ++ struct mvpp2_queue_vector *qv = port->qvecs + i; ++ ++ if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) ++ irq_set_status_flags(qv->irq, IRQ_NO_BALANCING); ++ ++ err = request_irq(qv->irq, mvpp2_isr, 0, port->dev->name, qv); ++ if (err) ++ goto err; ++ ++ if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) ++ irq_set_affinity_hint(qv->irq, ++ cpumask_of(qv->sw_thread_id)); ++ } ++ ++ return 0; ++err: ++ for (i = 0; i < port->nqvecs; i++) { ++ struct mvpp2_queue_vector *qv = port->qvecs + i; ++ ++ irq_set_affinity_hint(qv->irq, NULL); ++ free_irq(qv->irq, qv); ++ } ++ ++ return err; ++} ++ ++static void mvpp2_irqs_deinit(struct mvpp2_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < port->nqvecs; i++) { ++ struct mvpp2_queue_vector *qv = port->qvecs + i; ++ ++ irq_set_affinity_hint(qv->irq, NULL); ++ irq_clear_status_flags(qv->irq, IRQ_NO_BALANCING); ++ free_irq(qv->irq, qv); ++ } ++} ++ ++static void mvpp22_init_rss(struct mvpp2_port *port) ++{ ++ struct mvpp2 *priv = port->priv; ++ int i; ++ ++ /* Set the table width: replace the whole classifier Rx queue number ++ * with the ones configured in RSS table entries. ++ */ ++ mvpp2_write(priv, MVPP22_RSS_INDEX, MVPP22_RSS_INDEX_TABLE(0)); ++ mvpp2_write(priv, MVPP22_RSS_WIDTH, 8); ++ ++ /* Loop through the classifier Rx Queues and map them to a RSS table. ++ * Map them all to the first table (0) by default. ++ */ ++ for (i = 0; i < MVPP2_CLS_RX_QUEUES; i++) { ++ mvpp2_write(priv, MVPP22_RSS_INDEX, MVPP22_RSS_INDEX_QUEUE(i)); ++ mvpp2_write(priv, MVPP22_RSS_TABLE, ++ MVPP22_RSS_TABLE_POINTER(0)); ++ } ++ ++ /* Configure the first table to evenly distribute the packets across ++ * real Rx Queues. The table entries map a hash to an port Rx Queue. ++ */ ++ for (i = 0; i < MVPP22_RSS_TABLE_ENTRIES; i++) { ++ u32 sel = MVPP22_RSS_INDEX_TABLE(0) | ++ MVPP22_RSS_INDEX_TABLE_ENTRY(i); ++ mvpp2_write(priv, MVPP22_RSS_INDEX, sel); ++ ++ mvpp2_write(priv, MVPP22_RSS_TABLE_ENTRY, i % port->nrxqs); ++ } ++ ++} ++ + static int mvpp2_open(struct net_device *dev) + { + struct mvpp2_port *port = netdev_priv(dev); ++ struct mvpp2 *priv = port->priv; + unsigned char mac_bcast[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int err; + +- err = mvpp2_prs_mac_da_accept(port->priv, port->id, mac_bcast, true); ++ err = mvpp2_prs_mac_da_accept(port, mac_bcast, true); + if (err) { + netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n"); + return err; + } +- err = mvpp2_prs_mac_da_accept(port->priv, port->id, +- dev->dev_addr, true); ++ err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, true); + if (err) { +- netdev_err(dev, "mvpp2_prs_mac_da_accept MC failed\n"); ++ netdev_err(dev, "mvpp2_prs_mac_da_accept own addr failed\n"); + return err; + } + err = mvpp2_prs_tag_mode_set(port->priv, port->id, MVPP2_TAG_TYPE_MH); +@@ -5596,28 +7382,51 @@ static int mvpp2_open(struct net_device *dev) + goto err_cleanup_rxqs; + } + +- err = request_irq(port->irq, mvpp2_isr, 0, dev->name, port); ++ err = mvpp2_irqs_init(port); + if (err) { +- netdev_err(port->dev, "cannot request IRQ %d\n", port->irq); ++ netdev_err(port->dev, "cannot init IRQs\n"); + goto err_cleanup_txqs; + } + ++ if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) { ++ err = request_irq(port->link_irq, mvpp2_link_status_isr, 0, ++ dev->name, port); ++ if (err) { ++ netdev_err(port->dev, "cannot request link IRQ %d\n", ++ port->link_irq); ++ goto err_free_irq; ++ } ++ ++ mvpp22_gop_setup_irq(port); ++ } ++ + /* In default link is down */ + netif_carrier_off(port->dev); + + err = mvpp2_phy_connect(port); + if (err < 0) +- goto err_free_irq; ++ goto err_free_link_irq; + + /* Unmask interrupts on all CPUs */ + on_each_cpu(mvpp2_interrupts_unmask, port, 1); ++ mvpp2_shared_interrupt_mask_unmask(port, false); + + mvpp2_start_dev(port); + ++ if (priv->hw_version == MVPP22) ++ mvpp22_init_rss(port); ++ ++ /* Start hardware statistics gathering */ ++ queue_delayed_work(priv->stats_queue, &port->stats_work, ++ MVPP2_MIB_COUNTERS_STATS_DELAY); ++ + return 0; + ++err_free_link_irq: ++ if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) ++ free_irq(port->link_irq, port); + err_free_irq: +- free_irq(port->irq, port); ++ mvpp2_irqs_deinit(port); + err_cleanup_txqs: + mvpp2_cleanup_txqs(port); + err_cleanup_rxqs: +@@ -5629,6 +7438,7 @@ static int mvpp2_stop(struct net_device *dev) + { + struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port_pcpu *port_pcpu; ++ struct mvpp2 *priv = port->priv; + int cpu; + + mvpp2_stop_dev(port); +@@ -5636,45 +7446,87 @@ static int mvpp2_stop(struct net_device *dev) + + /* Mask interrupts on all CPUs */ + on_each_cpu(mvpp2_interrupts_mask, port, 1); ++ mvpp2_shared_interrupt_mask_unmask(port, true); + +- free_irq(port->irq, port); +- for_each_present_cpu(cpu) { +- port_pcpu = per_cpu_ptr(port->pcpu, cpu); ++ if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) ++ free_irq(port->link_irq, port); + +- hrtimer_cancel(&port_pcpu->tx_done_timer); +- port_pcpu->timer_scheduled = false; +- tasklet_kill(&port_pcpu->tx_done_tasklet); ++ mvpp2_irqs_deinit(port); ++ if (!port->has_tx_irqs) { ++ for_each_present_cpu(cpu) { ++ port_pcpu = per_cpu_ptr(port->pcpu, cpu); ++ ++ hrtimer_cancel(&port_pcpu->tx_done_timer); ++ port_pcpu->timer_scheduled = false; ++ tasklet_kill(&port_pcpu->tx_done_tasklet); ++ } + } + mvpp2_cleanup_rxqs(port); + mvpp2_cleanup_txqs(port); + ++ cancel_delayed_work_sync(&port->stats_work); ++ ++ return 0; ++} ++ ++static int mvpp2_prs_mac_da_accept_list(struct mvpp2_port *port, ++ struct netdev_hw_addr_list *list) ++{ ++ struct netdev_hw_addr *ha; ++ int ret; ++ ++ netdev_hw_addr_list_for_each(ha, list) { ++ ret = mvpp2_prs_mac_da_accept(port, ha->addr, true); ++ if (ret) ++ return ret; ++ } ++ + return 0; + } + ++static void mvpp2_set_rx_promisc(struct mvpp2_port *port, bool enable) ++{ ++ if (!enable && (port->dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) ++ mvpp2_prs_vid_enable_filtering(port); ++ else ++ mvpp2_prs_vid_disable_filtering(port); ++ ++ mvpp2_prs_mac_promisc_set(port->priv, port->id, ++ MVPP2_PRS_L2_UNI_CAST, enable); ++ ++ mvpp2_prs_mac_promisc_set(port->priv, port->id, ++ MVPP2_PRS_L2_MULTI_CAST, enable); ++} ++ + static void mvpp2_set_rx_mode(struct net_device *dev) + { + struct mvpp2_port *port = netdev_priv(dev); +- struct mvpp2 *priv = port->priv; +- struct netdev_hw_addr *ha; +- int id = port->id; +- bool allmulti = dev->flags & IFF_ALLMULTI; +- +-retry: +- mvpp2_prs_mac_promisc_set(priv, id, dev->flags & IFF_PROMISC); +- mvpp2_prs_mac_multi_set(priv, id, MVPP2_PE_MAC_MC_ALL, allmulti); +- mvpp2_prs_mac_multi_set(priv, id, MVPP2_PE_MAC_MC_IP6, allmulti); +- +- /* Remove all port->id's mcast enries */ +- mvpp2_prs_mcast_del_all(priv, id); +- +- if (!allmulti) { +- netdev_for_each_mc_addr(ha, dev) { +- if (mvpp2_prs_mac_da_accept(priv, id, ha->addr, true)) { +- allmulti = true; +- goto retry; +- } +- } ++ ++ /* Clear the whole UC and MC list */ ++ mvpp2_prs_mac_del_all(port); ++ ++ if (dev->flags & IFF_PROMISC) { ++ mvpp2_set_rx_promisc(port, true); ++ return; ++ } ++ ++ mvpp2_set_rx_promisc(port, false); ++ ++ if (netdev_uc_count(dev) > MVPP2_PRS_MAC_UC_FILT_MAX || ++ mvpp2_prs_mac_da_accept_list(port, &dev->uc)) ++ mvpp2_prs_mac_promisc_set(port->priv, port->id, ++ MVPP2_PRS_L2_UNI_CAST, true); ++ ++ if (dev->flags & IFF_ALLMULTI) { ++ mvpp2_prs_mac_promisc_set(port->priv, port->id, ++ MVPP2_PRS_L2_MULTI_CAST, true); ++ return; + } ++ ++ if (netdev_mc_count(dev) > MVPP2_PRS_MAC_MC_FILT_MAX || ++ mvpp2_prs_mac_da_accept_list(port, &dev->mc)) ++ mvpp2_prs_mac_promisc_set(port->priv, port->id, ++ MVPP2_PRS_L2_MULTI_CAST, true); + } + + static int mvpp2_set_mac_address(struct net_device *dev, void *p) +@@ -5685,7 +7537,7 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) + + if (!is_valid_ether_addr(addr->sa_data)) { + err = -EADDRNOTAVAIL; +- goto error; ++ goto log_error; + } + + if (!netif_running(dev)) { +@@ -5695,7 +7547,7 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) + /* Reconfigure parser to accept the original MAC address */ + err = mvpp2_prs_update_mac_da(dev, dev->dev_addr); + if (err) +- goto error; ++ goto log_error; + } + + mvpp2_stop_dev(port); +@@ -5707,15 +7559,14 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) + /* Reconfigure parser accept the original MAC address */ + err = mvpp2_prs_update_mac_da(dev, dev->dev_addr); + if (err) +- goto error; ++ goto log_error; + out_start: + mvpp2_start_dev(port); + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); + return 0; +- +-error: +- netdev_err(dev, "fail to change MAC address\n"); ++log_error: ++ netdev_err(dev, "failed to change MAC address\n"); + return err; + } + +@@ -5724,10 +7575,10 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) + struct mvpp2_port *port = netdev_priv(dev); + int err; + +- mtu = mvpp2_check_mtu_valid(dev, mtu); +- if (mtu < 0) { +- err = mtu; +- goto error; ++ if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) { ++ netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu, ++ ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8)); ++ mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8); + } + + if (!netif_running(dev)) { +@@ -5740,7 +7591,7 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) + /* Reconfigure BM to the original MTU */ + err = mvpp2_bm_update_mtu(dev, dev->mtu); + if (err) +- goto error; ++ goto log_error; + } + + mvpp2_stop_dev(port); +@@ -5754,7 +7605,7 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) + /* Reconfigure BM to the original MTU */ + err = mvpp2_bm_update_mtu(dev, dev->mtu); + if (err) +- goto error; ++ goto log_error; + + out_start: + mvpp2_start_dev(port); +@@ -5762,9 +7613,8 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) + mvpp2_ingress_enable(port); + + return 0; +- +-error: +- netdev_err(dev, "fail to change MTU\n"); ++log_error: ++ netdev_err(dev, "failed to change MTU\n"); + return err; + } + +@@ -5806,16 +7656,58 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) + + static int mvpp2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { +- int ret; ++ int ret; ++ ++ if (!dev->phydev) ++ return -ENOTSUPP; ++ ++ ret = phy_mii_ioctl(dev->phydev, ifr, cmd); ++ if (!ret) ++ mvpp2_link_event(dev); ++ ++ return ret; ++} ++ ++static int mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ int ret; ++ ++ ret = mvpp2_prs_vid_entry_add(port, vid); ++ if (ret) ++ netdev_err(dev, "rx-vlan-filter offloading cannot accept more than %d VIDs per port\n", ++ MVPP2_PRS_VLAN_FILT_MAX - 1); ++ return ret; ++} ++ ++static int mvpp2_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ ++ mvpp2_prs_vid_entry_remove(port, vid); ++ return 0; ++} ++ ++static int mvpp2_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ netdev_features_t changed = dev->features ^ features; ++ struct mvpp2_port *port = netdev_priv(dev); + +- if (!dev->phydev) +- return -ENOTSUPP; ++ if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { ++ if (features & NETIF_F_HW_VLAN_CTAG_FILTER) { ++ mvpp2_prs_vid_enable_filtering(port); ++ } else { ++ /* Invalidate all registered VID filters for this ++ * port ++ */ ++ mvpp2_prs_vid_remove_all(port); + +- ret = phy_mii_ioctl(dev->phydev, ifr, cmd); +- if (!ret) +- mvpp2_link_event(dev); ++ mvpp2_prs_vid_disable_filtering(port); ++ } ++ } + +- return ret; ++ return 0; + } + + /* Ethtool methods */ +@@ -5827,19 +7719,27 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, + struct mvpp2_port *port = netdev_priv(dev); + int queue; + +- for (queue = 0; queue < rxq_number; queue++) { ++ for (queue = 0; queue < port->nrxqs; queue++) { + struct mvpp2_rx_queue *rxq = port->rxqs[queue]; + + rxq->time_coal = c->rx_coalesce_usecs; + rxq->pkts_coal = c->rx_max_coalesced_frames; +- mvpp2_rx_pkts_coal_set(port, rxq, rxq->pkts_coal); +- mvpp2_rx_time_coal_set(port, rxq, rxq->time_coal); ++ mvpp2_rx_pkts_coal_set(port, rxq); ++ mvpp2_rx_time_coal_set(port, rxq); ++ } ++ ++ if (port->has_tx_irqs) { ++ port->tx_time_coal = c->tx_coalesce_usecs; ++ mvpp2_tx_time_coal_set(port); + } + +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + struct mvpp2_tx_queue *txq = port->txqs[queue]; + + txq->done_pkts_coal = c->tx_max_coalesced_frames; ++ ++ if (port->has_tx_irqs) ++ mvpp2_tx_pkts_coal_set(port, txq); + } + + return 0; +@@ -5851,9 +7751,10 @@ static int mvpp2_ethtool_get_coalesce(struct net_device *dev, + { + struct mvpp2_port *port = netdev_priv(dev); + +- c->rx_coalesce_usecs = port->rxqs[0]->time_coal; +- c->rx_max_coalesced_frames = port->rxqs[0]->pkts_coal; +- c->tx_max_coalesced_frames = port->txqs[0]->done_pkts_coal; ++ c->rx_coalesce_usecs = port->rxqs[0]->time_coal; ++ c->rx_max_coalesced_frames = port->rxqs[0]->pkts_coal; ++ c->tx_max_coalesced_frames = port->txqs[0]->done_pkts_coal; ++ c->tx_coalesce_usecs = port->tx_time_coal; + return 0; + } + +@@ -5873,8 +7774,8 @@ static void mvpp2_ethtool_get_ringparam(struct net_device *dev, + { + struct mvpp2_port *port = netdev_priv(dev); + +- ring->rx_max_pending = MVPP2_MAX_RXD; +- ring->tx_max_pending = MVPP2_MAX_TXD; ++ ring->rx_max_pending = MVPP2_MAX_RXD_MAX; ++ ring->tx_max_pending = MVPP2_MAX_TXD_MAX; + ring->rx_pending = port->rx_ring_size; + ring->tx_pending = port->tx_ring_size; + } +@@ -5935,7 +7836,7 @@ static int mvpp2_ethtool_set_ringparam(struct net_device *dev, + err_clean_rxqs: + mvpp2_cleanup_rxqs(port); + err_out: +- netdev_err(dev, "fail to change ring parameters"); ++ netdev_err(dev, "failed to change ring parameters"); + return err; + } + +@@ -5950,6 +7851,9 @@ static int mvpp2_ethtool_set_ringparam(struct net_device *dev, + .ndo_change_mtu = mvpp2_change_mtu, + .ndo_get_stats64 = mvpp2_get_stats64, + .ndo_do_ioctl = mvpp2_ioctl, ++ .ndo_vlan_rx_add_vid = mvpp2_vlan_rx_add_vid, ++ .ndo_vlan_rx_kill_vid = mvpp2_vlan_rx_kill_vid, ++ .ndo_set_features = mvpp2_set_features, + }; + + static const struct ethtool_ops mvpp2_eth_tool_ops = { +@@ -5959,18 +7863,134 @@ static int mvpp2_ethtool_set_ringparam(struct net_device *dev, + .get_drvinfo = mvpp2_ethtool_get_drvinfo, + .get_ringparam = mvpp2_ethtool_get_ringparam, + .set_ringparam = mvpp2_ethtool_set_ringparam, ++ .get_strings = mvpp2_ethtool_get_strings, ++ .get_ethtool_stats = mvpp2_ethtool_get_stats, ++ .get_sset_count = mvpp2_ethtool_get_sset_count, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + }; + +-/* Driver initialization */ ++/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that ++ * had a single IRQ defined per-port. ++ */ ++static int mvpp2_simple_queue_vectors_init(struct mvpp2_port *port, ++ struct device_node *port_node) ++{ ++ struct mvpp2_queue_vector *v = &port->qvecs[0]; ++ ++ v->first_rxq = 0; ++ v->nrxqs = port->nrxqs; ++ v->type = MVPP2_QUEUE_VECTOR_SHARED; ++ v->sw_thread_id = 0; ++ v->sw_thread_mask = *cpumask_bits(cpu_online_mask); ++ v->port = port; ++ v->irq = irq_of_parse_and_map(port_node, 0); ++ if (v->irq <= 0) ++ return -EINVAL; ++ netif_napi_add(port->dev, &v->napi, mvpp2_poll, ++ NAPI_POLL_WEIGHT); ++ ++ port->nqvecs = 1; ++ ++ return 0; ++} + +-static void mvpp2_port_power_up(struct mvpp2_port *port) ++static int mvpp2_multi_queue_vectors_init(struct mvpp2_port *port, ++ struct device_node *port_node) + { +- mvpp2_port_mii_set(port); +- mvpp2_port_periodic_xon_disable(port); +- mvpp2_port_fc_adv_enable(port); +- mvpp2_port_reset(port); ++ struct mvpp2_queue_vector *v; ++ int i, ret; ++ ++ port->nqvecs = num_possible_cpus(); ++ if (queue_mode == MVPP2_QDIST_SINGLE_MODE) ++ port->nqvecs += 1; ++ ++ for (i = 0; i < port->nqvecs; i++) { ++ char irqname[16]; ++ ++ v = port->qvecs + i; ++ ++ v->port = port; ++ v->type = MVPP2_QUEUE_VECTOR_PRIVATE; ++ v->sw_thread_id = i; ++ v->sw_thread_mask = BIT(i); ++ ++ snprintf(irqname, sizeof(irqname), "tx-cpu%d", i); ++ ++ if (queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ v->first_rxq = i * MVPP2_DEFAULT_RXQ; ++ v->nrxqs = MVPP2_DEFAULT_RXQ; ++ } else if (queue_mode == MVPP2_QDIST_SINGLE_MODE && ++ i == (port->nqvecs - 1)) { ++ v->first_rxq = 0; ++ v->nrxqs = port->nrxqs; ++ v->type = MVPP2_QUEUE_VECTOR_SHARED; ++ strncpy(irqname, "rx-shared", sizeof(irqname)); ++ } ++ ++ v->irq = of_irq_get_byname(port_node, irqname); ++ if (v->irq <= 0) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ netif_napi_add(port->dev, &v->napi, mvpp2_poll, ++ NAPI_POLL_WEIGHT); ++ } ++ ++ return 0; ++ ++err: ++ for (i = 0; i < port->nqvecs; i++) ++ irq_dispose_mapping(port->qvecs[i].irq); ++ return ret; ++} ++ ++static int mvpp2_queue_vectors_init(struct mvpp2_port *port, ++ struct device_node *port_node) ++{ ++ if (port->has_tx_irqs) ++ return mvpp2_multi_queue_vectors_init(port, port_node); ++ else ++ return mvpp2_simple_queue_vectors_init(port, port_node); ++} ++ ++static void mvpp2_queue_vectors_deinit(struct mvpp2_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < port->nqvecs; i++) ++ irq_dispose_mapping(port->qvecs[i].irq); ++} ++ ++/* Configure Rx queue group interrupt for this port */ ++static void mvpp2_rx_irqs_setup(struct mvpp2_port *port) ++{ ++ struct mvpp2 *priv = port->priv; ++ u32 val; ++ int i; ++ ++ if (priv->hw_version == MVPP21) { ++ mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), ++ port->nrxqs); ++ return; ++ } ++ ++ /* Handle the more complicated PPv2.2 case */ ++ for (i = 0; i < port->nqvecs; i++) { ++ struct mvpp2_queue_vector *qv = port->qvecs + i; ++ ++ if (!qv->nrxqs) ++ continue; ++ ++ val = qv->sw_thread_id; ++ val |= port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET; ++ mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); ++ ++ val = qv->first_rxq; ++ val |= qv->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET; ++ mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); ++ } + } + + /* Initialize port HW */ +@@ -5981,14 +8001,22 @@ static int mvpp2_port_init(struct mvpp2_port *port) + struct mvpp2_txq_pcpu *txq_pcpu; + int queue, cpu, err; + +- if (port->first_rxq + rxq_number > MVPP2_RXQ_TOTAL_NUM) ++ /* Checks for hardware constraints */ ++ if (port->first_rxq + port->nrxqs > ++ MVPP2_MAX_PORTS * priv->max_port_rxqs) ++ return -EINVAL; ++ ++ if (port->nrxqs % 4 || (port->nrxqs > priv->max_port_rxqs) || ++ (port->ntxqs > MVPP2_MAX_TXQ)) + return -EINVAL; + + /* Disable port */ + mvpp2_egress_disable(port); + mvpp2_port_disable(port); + +- port->txqs = devm_kcalloc(dev, txq_number, sizeof(*port->txqs), ++ port->tx_time_coal = MVPP2_TXDONE_COAL_USEC; ++ ++ port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs), + GFP_KERNEL); + if (!port->txqs) + return -ENOMEM; +@@ -5996,13 +8024,15 @@ static int mvpp2_port_init(struct mvpp2_port *port) + /* Associate physical Tx queues to this port and initialize. + * The mapping is predefined. + */ +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + int queue_phy_id = mvpp2_txq_phys(port->id, queue); + struct mvpp2_tx_queue *txq; + + txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL); +- if (!txq) +- return -ENOMEM; ++ if (!txq) { ++ err = -ENOMEM; ++ goto err_free_percpu; ++ } + + txq->pcpu = alloc_percpu(struct mvpp2_txq_pcpu); + if (!txq->pcpu) { +@@ -6021,7 +8051,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) + port->txqs[queue] = txq; + } + +- port->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*port->rxqs), ++ port->rxqs = devm_kcalloc(dev, port->nrxqs, sizeof(*port->rxqs), + GFP_KERNEL); + if (!port->rxqs) { + err = -ENOMEM; +@@ -6029,7 +8059,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) + } + + /* Allocate and initialize Rx queue for this port */ +- for (queue = 0; queue < rxq_number; queue++) { ++ for (queue = 0; queue < port->nrxqs; queue++) { + struct mvpp2_rx_queue *rxq; + + /* Map physical Rx queue to port's logical Rx queue */ +@@ -6046,11 +8076,10 @@ static int mvpp2_port_init(struct mvpp2_port *port) + port->rxqs[queue] = rxq; + } + +- /* Configure Rx queue group interrupt for this port */ +- mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(port->id), rxq_number); ++ mvpp2_rx_irqs_setup(port); + + /* Create Rx descriptor rings */ +- for (queue = 0; queue < rxq_number; queue++) { ++ for (queue = 0; queue < port->nrxqs; queue++) { + struct mvpp2_rx_queue *rxq = port->rxqs[queue]; + + rxq->size = port->rx_ring_size; +@@ -6078,7 +8107,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) + return 0; + + err_free_percpu: +- for (queue = 0; queue < txq_number; queue++) { ++ for (queue = 0; queue < port->ntxqs; queue++) { + if (!port->txqs[queue]) + continue; + free_percpu(port->txqs[queue]->pcpu); +@@ -6086,38 +8115,93 @@ static int mvpp2_port_init(struct mvpp2_port *port) + return err; + } + ++/* Checks if the port DT description has the TX interrupts ++ * described. On PPv2.1, there are no such interrupts. On PPv2.2, ++ * there are available, but we need to keep support for old DTs. ++ */ ++static bool mvpp2_port_has_tx_irqs(struct mvpp2 *priv, ++ struct device_node *port_node) ++{ ++ char *irqs[5] = { "rx-shared", "tx-cpu0", "tx-cpu1", ++ "tx-cpu2", "tx-cpu3" }; ++ int ret, i; ++ ++ if (priv->hw_version == MVPP21) ++ return false; ++ ++ for (i = 0; i < 5; i++) { ++ ret = of_property_match_string(port_node, "interrupt-names", ++ irqs[i]); ++ if (ret < 0) ++ return false; ++ } ++ ++ return true; ++} ++ ++static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, ++ struct device_node *port_node, ++ char **mac_from) ++{ ++ struct mvpp2_port *port = netdev_priv(dev); ++ char hw_mac_addr[ETH_ALEN] = {0}; ++ const char *dt_mac_addr; ++ ++ dt_mac_addr = of_get_mac_address(port_node); ++ if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { ++ *mac_from = "device tree"; ++ ether_addr_copy(dev->dev_addr, dt_mac_addr); ++ return; ++ } ++ ++ if (priv->hw_version == MVPP21) { ++ mvpp21_get_mac_address(port, hw_mac_addr); ++ if (is_valid_ether_addr(hw_mac_addr)) { ++ *mac_from = "hardware"; ++ ether_addr_copy(dev->dev_addr, hw_mac_addr); ++ return; ++ } ++ } ++ ++ *mac_from = "random"; ++ eth_hw_addr_random(dev); ++} ++ + /* Ports initialization */ + static int mvpp2_port_probe(struct platform_device *pdev, + struct device_node *port_node, +- struct mvpp2 *priv, +- int *next_first_rxq) ++ struct mvpp2 *priv) + { + struct device_node *phy_node; ++ struct phy *comphy; + struct mvpp2_port *port; + struct mvpp2_port_pcpu *port_pcpu; + struct net_device *dev; + struct resource *res; +- const char *dt_mac_addr; +- const char *mac_from; +- char hw_mac_addr[ETH_ALEN]; ++ char *mac_from = ""; ++ unsigned int ntxqs, nrxqs; ++ bool has_tx_irqs; + u32 id; + int features; + int phy_mode; +- int priv_common_regs_num = 2; + int err, i, cpu; + +- dev = alloc_etherdev_mqs(sizeof(struct mvpp2_port), txq_number, +- rxq_number); ++ has_tx_irqs = mvpp2_port_has_tx_irqs(priv, port_node); ++ ++ if (!has_tx_irqs) ++ queue_mode = MVPP2_QDIST_SINGLE_MODE; ++ ++ ntxqs = MVPP2_MAX_TXQ; ++ if (priv->hw_version == MVPP22 && queue_mode == MVPP2_QDIST_MULTI_MODE) ++ nrxqs = MVPP2_DEFAULT_RXQ * num_possible_cpus(); ++ else ++ nrxqs = MVPP2_DEFAULT_RXQ; ++ ++ dev = alloc_etherdev_mqs(sizeof(*port), ntxqs, nrxqs); + if (!dev) + return -ENOMEM; + + phy_node = of_parse_phandle(port_node, "phy", 0); +- if (!phy_node) { +- dev_err(&pdev->dev, "missing phy\n"); +- err = -ENODEV; +- goto err_free_netdev; +- } +- + phy_mode = of_get_phy_mode(port_node); + if (phy_mode < 0) { + dev_err(&pdev->dev, "incorrect phy mode\n"); +@@ -6125,67 +8209,106 @@ static int mvpp2_port_probe(struct platform_device *pdev, + goto err_free_netdev; + } + ++ comphy = devm_of_phy_get(&pdev->dev, port_node, NULL); ++ if (IS_ERR(comphy)) { ++ if (PTR_ERR(comphy) == -EPROBE_DEFER) { ++ err = -EPROBE_DEFER; ++ goto err_free_netdev; ++ } ++ comphy = NULL; ++ } ++ + if (of_property_read_u32(port_node, "port-id", &id)) { + err = -EINVAL; + dev_err(&pdev->dev, "missing port-id value\n"); + goto err_free_netdev; + } + +- dev->tx_queue_len = MVPP2_MAX_TXD; ++ dev->tx_queue_len = MVPP2_MAX_TXD_MAX; + dev->watchdog_timeo = 5 * HZ; + dev->netdev_ops = &mvpp2_netdev_ops; + dev->ethtool_ops = &mvpp2_eth_tool_ops; + + port = netdev_priv(dev); ++ port->dev = dev; ++ port->ntxqs = ntxqs; ++ port->nrxqs = nrxqs; ++ port->priv = priv; ++ port->has_tx_irqs = has_tx_irqs; + +- port->irq = irq_of_parse_and_map(port_node, 0); +- if (port->irq <= 0) { +- err = -EINVAL; ++ err = mvpp2_queue_vectors_init(port, port_node); ++ if (err) + goto err_free_netdev; ++ ++ port->link_irq = of_irq_get_byname(port_node, "link"); ++ if (port->link_irq == -EPROBE_DEFER) { ++ err = -EPROBE_DEFER; ++ goto err_deinit_qvecs; + } ++ if (port->link_irq <= 0) ++ /* the link irq is optional */ ++ port->link_irq = 0; + + if (of_property_read_bool(port_node, "marvell,loopback")) + port->flags |= MVPP2_F_LOOPBACK; + +- port->priv = priv; + port->id = id; +- port->first_rxq = *next_first_rxq; ++ if (priv->hw_version == MVPP21) ++ port->first_rxq = port->id * port->nrxqs; ++ else ++ port->first_rxq = port->id * priv->max_port_rxqs; ++ + port->phy_node = phy_node; + port->phy_interface = phy_mode; ++ port->comphy = comphy; ++ ++ if (priv->hw_version == MVPP21) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + id); ++ port->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(port->base)) { ++ err = PTR_ERR(port->base); ++ goto err_free_irq; ++ } + +- res = platform_get_resource(pdev, IORESOURCE_MEM, +- priv_common_regs_num + id); +- port->base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(port->base)) { +- err = PTR_ERR(port->base); +- goto err_free_irq; ++ port->stats_base = port->priv->lms_base + ++ MVPP21_MIB_COUNTERS_OFFSET + ++ port->gop_id * MVPP21_MIB_COUNTERS_PORT_SZ; ++ } else { ++ if (of_property_read_u32(port_node, "gop-port-id", ++ &port->gop_id)) { ++ err = -EINVAL; ++ dev_err(&pdev->dev, "missing gop-port-id value\n"); ++ goto err_deinit_qvecs; ++ } ++ ++ port->base = priv->iface_base + MVPP22_GMAC_BASE(port->gop_id); ++ port->stats_base = port->priv->iface_base + ++ MVPP22_MIB_COUNTERS_OFFSET + ++ port->gop_id * MVPP22_MIB_COUNTERS_PORT_SZ; + } + +- /* Alloc per-cpu stats */ ++ /* Alloc per-cpu and ethtool stats */ + port->stats = netdev_alloc_pcpu_stats(struct mvpp2_pcpu_stats); + if (!port->stats) { + err = -ENOMEM; + goto err_free_irq; + } + +- dt_mac_addr = of_get_mac_address(port_node); +- if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { +- mac_from = "device tree"; +- ether_addr_copy(dev->dev_addr, dt_mac_addr); +- } else { +- mvpp2_get_mac_address(port, hw_mac_addr); +- if (is_valid_ether_addr(hw_mac_addr)) { +- mac_from = "hardware"; +- ether_addr_copy(dev->dev_addr, hw_mac_addr); +- } else { +- mac_from = "random"; +- eth_hw_addr_random(dev); +- } ++ port->ethtool_stats = devm_kcalloc(&pdev->dev, ++ ARRAY_SIZE(mvpp2_ethtool_regs), ++ sizeof(u64), GFP_KERNEL); ++ if (!port->ethtool_stats) { ++ err = -ENOMEM; ++ goto err_free_stats; + } + +- port->tx_ring_size = MVPP2_MAX_TXD; +- port->rx_ring_size = MVPP2_MAX_RXD; +- port->dev = dev; ++ mutex_init(&port->gather_stats_lock); ++ INIT_DELAYED_WORK(&port->stats_work, mvpp2_gather_hw_statistics); ++ ++ mvpp2_port_copy_mac_addr(dev, priv, port_node, &mac_from); ++ ++ port->tx_ring_size = MVPP2_MAX_TXD_DFLT; ++ port->rx_ring_size = MVPP2_MAX_RXD_DFLT; + SET_NETDEV_DEV(dev, &pdev->dev); + + err = mvpp2_port_init(port); +@@ -6193,7 +8316,13 @@ static int mvpp2_port_probe(struct platform_device *pdev, + dev_err(&pdev->dev, "failed to init port %d\n", id); + goto err_free_stats; + } +- mvpp2_port_power_up(port); ++ ++ mvpp2_port_periodic_xon_disable(port); ++ ++ if (priv->hw_version == MVPP21) ++ mvpp2_port_fc_adv_enable(port); ++ ++ mvpp2_port_reset(port); + + port->pcpu = alloc_percpu(struct mvpp2_port_pcpu); + if (!port->pcpu) { +@@ -6201,23 +8330,40 @@ static int mvpp2_port_probe(struct platform_device *pdev, + goto err_free_txq_pcpu; + } + +- for_each_present_cpu(cpu) { +- port_pcpu = per_cpu_ptr(port->pcpu, cpu); ++ if (!port->has_tx_irqs) { ++ for_each_present_cpu(cpu) { ++ port_pcpu = per_cpu_ptr(port->pcpu, cpu); + +- hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, +- HRTIMER_MODE_REL_PINNED); +- port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; +- port_pcpu->timer_scheduled = false; ++ hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL_PINNED); ++ port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; ++ port_pcpu->timer_scheduled = false; + +- tasklet_init(&port_pcpu->tx_done_tasklet, mvpp2_tx_proc_cb, +- (unsigned long)dev); ++ tasklet_init(&port_pcpu->tx_done_tasklet, ++ mvpp2_tx_proc_cb, ++ (unsigned long)dev); ++ } + } + +- netif_napi_add(dev, &port->napi, mvpp2_poll, NAPI_POLL_WEIGHT); +- features = NETIF_F_SG | NETIF_F_IP_CSUM; ++ features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_TSO; + dev->features = features | NETIF_F_RXCSUM; +- dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO; ++ dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO | ++ NETIF_F_HW_VLAN_CTAG_FILTER; ++ ++ if (port->pool_long->id == MVPP2_BM_JUMBO && port->id != 0) { ++ dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); ++ dev->hw_features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); ++ } ++ + dev->vlan_features |= features; ++ dev->gso_max_segs = MVPP2_MAX_TSO_SEGS; ++ dev->priv_flags |= IFF_UNICAST_FLT; ++ ++ /* MTU range: 68 - 9704 */ ++ dev->min_mtu = ETH_MIN_MTU; ++ /* 9704 == 9728 - 20 and rounding to 8 */ ++ dev->max_mtu = MVPP2_BM_JUMBO_PKT_SIZE; + + err = register_netdev(dev); + if (err < 0) { +@@ -6226,20 +8372,22 @@ static int mvpp2_port_probe(struct platform_device *pdev, + } + netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr); + +- /* Increment the first Rx queue number to be used by the next port */ +- *next_first_rxq += rxq_number; +- priv->port_list[id] = port; ++ priv->port_list[priv->port_count++] = port; ++ + return 0; + + err_free_port_pcpu: + free_percpu(port->pcpu); + err_free_txq_pcpu: +- for (i = 0; i < txq_number; i++) ++ for (i = 0; i < port->ntxqs; i++) + free_percpu(port->txqs[i]->pcpu); + err_free_stats: + free_percpu(port->stats); + err_free_irq: +- irq_dispose_mapping(port->irq); ++ if (port->link_irq) ++ irq_dispose_mapping(port->link_irq); ++err_deinit_qvecs: ++ mvpp2_queue_vectors_deinit(port); + err_free_netdev: + of_node_put(phy_node); + free_netdev(dev); +@@ -6255,9 +8403,11 @@ static void mvpp2_port_remove(struct mvpp2_port *port) + of_node_put(port->phy_node); + free_percpu(port->pcpu); + free_percpu(port->stats); +- for (i = 0; i < txq_number; i++) ++ for (i = 0; i < port->ntxqs; i++) + free_percpu(port->txqs[i]->pcpu); +- irq_dispose_mapping(port->irq); ++ mvpp2_queue_vectors_deinit(port); ++ if (port->link_irq) ++ irq_dispose_mapping(port->link_irq); + free_netdev(port->dev); + } + +@@ -6301,9 +8451,42 @@ static void mvpp2_rx_fifo_init(struct mvpp2 *priv) + + for (port = 0; port < MVPP2_MAX_PORTS; port++) { + mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port), +- MVPP2_RX_FIFO_PORT_DATA_SIZE); ++ MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB); ++ mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_PORT_ATTR_SIZE_4KB); ++ } ++ ++ mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG, ++ MVPP2_RX_FIFO_PORT_MIN_PKT); ++ mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1); ++} ++ ++static void mvpp22_rx_fifo_init(struct mvpp2 *priv) ++{ ++ int port; ++ ++ /* The FIFO size parameters are set depending on the maximum speed a ++ * given port can handle: ++ * - Port 0: 10Gbps ++ * - Port 1: 2.5Gbps ++ * - Ports 2 and 3: 1Gbps ++ */ ++ ++ mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(0), ++ MVPP2_RX_FIFO_PORT_DATA_SIZE_32KB); ++ mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(0), ++ MVPP2_RX_FIFO_PORT_ATTR_SIZE_32KB); ++ ++ mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(1), ++ MVPP2_RX_FIFO_PORT_DATA_SIZE_8KB); ++ mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(1), ++ MVPP2_RX_FIFO_PORT_ATTR_SIZE_8KB); ++ ++ for (port = 2; port < MVPP2_MAX_PORTS; port++) { ++ mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB); + mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), +- MVPP2_RX_FIFO_PORT_ATTR_SIZE); ++ MVPP2_RX_FIFO_PORT_ATTR_SIZE_4KB); + } + + mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG, +@@ -6311,6 +8494,81 @@ static void mvpp2_rx_fifo_init(struct mvpp2 *priv) + mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1); + } + ++/* Initialize Tx FIFO's: the total FIFO size is 19kB on PPv2.2 and 10G ++ * interfaces must have a Tx FIFO size of 10kB. As only port 0 can do 10G, ++ * configure its Tx FIFO size to 10kB and the others ports Tx FIFO size to 3kB. ++ */ ++static void mvpp22_tx_fifo_init(struct mvpp2 *priv) ++{ ++ int port, size, thrs; ++ ++ for (port = 0; port < MVPP2_MAX_PORTS; port++) { ++ if (port == 0) { ++ size = MVPP22_TX_FIFO_DATA_SIZE_10KB; ++ thrs = MVPP2_TX_FIFO_THRESHOLD_10KB; ++ } else { ++ size = MVPP22_TX_FIFO_DATA_SIZE_3KB; ++ thrs = MVPP2_TX_FIFO_THRESHOLD_3KB; ++ } ++ mvpp2_write(priv, MVPP22_TX_FIFO_SIZE_REG(port), size); ++ mvpp2_write(priv, MVPP22_TX_FIFO_THRESH_REG(port), thrs); ++ } ++} ++ ++static void mvpp2_axi_init(struct mvpp2 *priv) ++{ ++ u32 val, rdval, wrval; ++ ++ mvpp2_write(priv, MVPP22_BM_ADDR_HIGH_RLS_REG, 0x0); ++ ++ /* AXI Bridge Configuration */ ++ ++ rdval = MVPP22_AXI_CODE_CACHE_RD_CACHE ++ << MVPP22_AXI_ATTR_CACHE_OFFS; ++ rdval |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM ++ << MVPP22_AXI_ATTR_DOMAIN_OFFS; ++ ++ wrval = MVPP22_AXI_CODE_CACHE_WR_CACHE ++ << MVPP22_AXI_ATTR_CACHE_OFFS; ++ wrval |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM ++ << MVPP22_AXI_ATTR_DOMAIN_OFFS; ++ ++ /* BM */ ++ mvpp2_write(priv, MVPP22_AXI_BM_WR_ATTR_REG, wrval); ++ mvpp2_write(priv, MVPP22_AXI_BM_RD_ATTR_REG, rdval); ++ ++ /* Descriptors */ ++ mvpp2_write(priv, MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG, rdval); ++ mvpp2_write(priv, MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG, wrval); ++ mvpp2_write(priv, MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG, rdval); ++ mvpp2_write(priv, MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG, wrval); ++ ++ /* Buffer Data */ ++ mvpp2_write(priv, MVPP22_AXI_TX_DATA_RD_ATTR_REG, rdval); ++ mvpp2_write(priv, MVPP22_AXI_RX_DATA_WR_ATTR_REG, wrval); ++ ++ val = MVPP22_AXI_CODE_CACHE_NON_CACHE ++ << MVPP22_AXI_CODE_CACHE_OFFS; ++ val |= MVPP22_AXI_CODE_DOMAIN_SYSTEM ++ << MVPP22_AXI_CODE_DOMAIN_OFFS; ++ mvpp2_write(priv, MVPP22_AXI_RD_NORMAL_CODE_REG, val); ++ mvpp2_write(priv, MVPP22_AXI_WR_NORMAL_CODE_REG, val); ++ ++ val = MVPP22_AXI_CODE_CACHE_RD_CACHE ++ << MVPP22_AXI_CODE_CACHE_OFFS; ++ val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM ++ << MVPP22_AXI_CODE_DOMAIN_OFFS; ++ ++ mvpp2_write(priv, MVPP22_AXI_RD_SNOOP_CODE_REG, val); ++ ++ val = MVPP22_AXI_CODE_CACHE_WR_CACHE ++ << MVPP22_AXI_CODE_CACHE_OFFS; ++ val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM ++ << MVPP22_AXI_CODE_DOMAIN_OFFS; ++ ++ mvpp2_write(priv, MVPP22_AXI_WR_SNOOP_CODE_REG, val); ++} ++ + /* Initialize network controller common part HW */ + static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) + { +@@ -6318,26 +8576,28 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) + int err, i; + u32 val; + +- /* Checks for hardware constraints */ +- if (rxq_number % 4 || (rxq_number > MVPP2_MAX_RXQ) || +- (txq_number > MVPP2_MAX_TXQ)) { +- dev_err(&pdev->dev, "invalid queue size parameter\n"); +- return -EINVAL; +- } +- + /* MBUS windows configuration */ + dram_target_info = mv_mbus_dram_info(); + if (dram_target_info) + mvpp2_conf_mbus_windows(dram_target_info, priv); + ++ if (priv->hw_version == MVPP22) ++ mvpp2_axi_init(priv); ++ + /* Disable HW PHY polling */ +- val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG); +- val |= MVPP2_PHY_AN_STOP_SMI0_MASK; +- writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG); ++ if (priv->hw_version == MVPP21) { ++ val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG); ++ val |= MVPP2_PHY_AN_STOP_SMI0_MASK; ++ writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG); ++ } else { ++ val = readl(priv->iface_base + MVPP22_SMI_MISC_CFG_REG); ++ val &= ~MVPP22_SMI_POLLING_EN; ++ writel(val, priv->iface_base + MVPP22_SMI_MISC_CFG_REG); ++ } + + /* Allocate and initialize aggregated TXQs */ + priv->aggr_txqs = devm_kcalloc(&pdev->dev, num_present_cpus(), +- sizeof(struct mvpp2_tx_queue), ++ sizeof(*priv->aggr_txqs), + GFP_KERNEL); + if (!priv->aggr_txqs) + return -ENOMEM; +@@ -6345,21 +8605,22 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) + for_each_present_cpu(i) { + priv->aggr_txqs[i].id = i; + priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE; +- err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], +- MVPP2_AGGR_TXQ_SIZE, i, priv); ++ err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], i, priv); + if (err < 0) + return err; + } + +- /* Rx Fifo Init */ +- mvpp2_rx_fifo_init(priv); +- +- /* Reset Rx queue group interrupt configuration */ +- for (i = 0; i < MVPP2_MAX_PORTS; i++) +- mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(i), rxq_number); ++ /* Fifo Init */ ++ if (priv->hw_version == MVPP21) { ++ mvpp2_rx_fifo_init(priv); ++ } else { ++ mvpp22_rx_fifo_init(priv); ++ mvpp22_tx_fifo_init(priv); ++ } + +- writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, +- priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); ++ if (priv->hw_version == MVPP21) ++ writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, ++ priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); + + /* Allow cache snoop when transmiting packets */ + mvpp2_write(priv, MVPP2_TX_SNOOP_REG, 0x1); +@@ -6386,22 +8647,59 @@ static int mvpp2_probe(struct platform_device *pdev) + struct device_node *port_node; + struct mvpp2 *priv; + struct resource *res; +- int port_count, first_rxq; ++ void __iomem *base; ++ int i; + int err; + +- priv = devm_kzalloc(&pdev->dev, sizeof(struct mvpp2), GFP_KERNEL); ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + ++ priv->hw_version = ++ (unsigned long)of_device_get_match_data(&pdev->dev); ++ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(priv->base)) +- return PTR_ERR(priv->base); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (priv->hw_version == MVPP21) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ priv->lms_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->lms_base)) ++ return PTR_ERR(priv->lms_base); ++ } else { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ priv->iface_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->iface_base)) ++ return PTR_ERR(priv->iface_base); ++ ++ priv->sysctrl_base = ++ syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "marvell,system-controller"); ++ if (IS_ERR(priv->sysctrl_base)) ++ /* The system controller regmap is optional for dt ++ * compatibility reasons. When not provided, the ++ * configuration of the GoP relies on the ++ * firmware/bootloader. ++ */ ++ priv->sysctrl_base = NULL; ++ } ++ ++ mvpp2_setup_bm_pool(); ++ ++ for (i = 0; i < MVPP2_MAX_THREADS; i++) { ++ u32 addr_space_sz; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- priv->lms_base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(priv->lms_base)) +- return PTR_ERR(priv->lms_base); ++ addr_space_sz = (priv->hw_version == MVPP21 ? ++ MVPP21_ADDR_SPACE_SZ : MVPP22_ADDR_SPACE_SZ); ++ priv->swth_base[i] = base + i * addr_space_sz; ++ } ++ ++ if (priv->hw_version == MVPP21) ++ priv->max_port_rxqs = 8; ++ else ++ priv->max_port_rxqs = 32; + + priv->pp_clk = devm_clk_get(&pdev->dev, "pp_clk"); + if (IS_ERR(priv->pp_clk)) +@@ -6419,42 +8717,110 @@ static int mvpp2_probe(struct platform_device *pdev) + if (err < 0) + goto err_pp_clk; + ++ if (priv->hw_version == MVPP22) { ++ priv->mg_clk = devm_clk_get(&pdev->dev, "mg_clk"); ++ if (IS_ERR(priv->mg_clk)) { ++ err = PTR_ERR(priv->mg_clk); ++ goto err_gop_clk; ++ } ++ ++ err = clk_prepare_enable(priv->mg_clk); ++ if (err < 0) ++ goto err_gop_clk; ++ ++ priv->mg_core_clk = devm_clk_get(&pdev->dev, "mg_core_clk"); ++ if (IS_ERR(priv->mg_core_clk)) { ++ priv->mg_core_clk = NULL; ++ } else { ++ err = clk_prepare_enable(priv->mg_core_clk); ++ if (err < 0) ++ goto err_mg_clk; ++ } ++ ++ priv->axi_clk = devm_clk_get(&pdev->dev, "axi_clk"); ++ if (IS_ERR(priv->axi_clk)) { ++ err = PTR_ERR(priv->axi_clk); ++ if (err == -EPROBE_DEFER) ++ goto err_mg_core_clk; ++ priv->axi_clk = NULL; ++ } else { ++ err = clk_prepare_enable(priv->axi_clk); ++ if (err < 0) ++ goto err_mg_core_clk; ++ } ++ } ++ + /* Get system's tclk rate */ + priv->tclk = clk_get_rate(priv->pp_clk); + ++ if (priv->hw_version == MVPP22) { ++ err = dma_set_mask(&pdev->dev, MVPP2_DESC_DMA_MASK); ++ if (err) ++ goto err_axi_clk; ++ /* Sadly, the BM pools all share the same register to ++ * store the high 32 bits of their address. So they ++ * must all have the same high 32 bits, which forces ++ * us to restrict coherent memory to DMA_BIT_MASK(32). ++ */ ++ err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ if (err) ++ goto err_axi_clk; ++ } ++ + /* Initialize network controller */ + err = mvpp2_init(pdev, priv); + if (err < 0) { + dev_err(&pdev->dev, "failed to initialize controller\n"); +- goto err_gop_clk; ++ goto err_axi_clk; ++ } ++ ++ /* Initialize ports */ ++ for_each_available_child_of_node(dn, port_node) { ++ err = mvpp2_port_probe(pdev, port_node, priv); ++ if (err < 0) ++ goto err_port_probe; + } + +- port_count = of_get_available_child_count(dn); +- if (port_count == 0) { ++ if (priv->port_count == 0) { + dev_err(&pdev->dev, "no ports enabled\n"); + err = -ENODEV; +- goto err_gop_clk; ++ goto err_axi_clk; + } + +- priv->port_list = devm_kcalloc(&pdev->dev, port_count, +- sizeof(struct mvpp2_port *), +- GFP_KERNEL); +- if (!priv->port_list) { ++ /* Statistics must be gathered regularly because some of them (like ++ * packets counters) are 32-bit registers and could overflow quite ++ * quickly. For instance, a 10Gb link used at full bandwidth with the ++ * smallest packets (64B) will overflow a 32-bit counter in less than ++ * 30 seconds. Then, use a workqueue to fill 64-bit counters. ++ */ ++ snprintf(priv->queue_name, sizeof(priv->queue_name), ++ "stats-wq-%s%s", netdev_name(priv->port_list[0]->dev), ++ priv->port_count > 1 ? "+" : ""); ++ priv->stats_queue = create_singlethread_workqueue(priv->queue_name); ++ if (!priv->stats_queue) { + err = -ENOMEM; +- goto err_gop_clk; +- } +- +- /* Initialize ports */ +- first_rxq = 0; +- for_each_available_child_of_node(dn, port_node) { +- err = mvpp2_port_probe(pdev, port_node, priv, &first_rxq); +- if (err < 0) +- goto err_gop_clk; ++ goto err_port_probe; + } + + platform_set_drvdata(pdev, priv); + return 0; + ++err_port_probe: ++ i = 0; ++ for_each_available_child_of_node(dn, port_node) { ++ if (priv->port_list[i]) ++ mvpp2_port_remove(priv->port_list[i]); ++ i++; ++ } ++err_axi_clk: ++ clk_disable_unprepare(priv->axi_clk); ++ ++err_mg_core_clk: ++ if (priv->hw_version == MVPP22) ++ clk_disable_unprepare(priv->mg_core_clk); ++err_mg_clk: ++ if (priv->hw_version == MVPP22) ++ clk_disable_unprepare(priv->mg_clk); + err_gop_clk: + clk_disable_unprepare(priv->gop_clk); + err_pp_clk: +@@ -6469,9 +8835,14 @@ static int mvpp2_remove(struct platform_device *pdev) + struct device_node *port_node; + int i = 0; + ++ flush_workqueue(priv->stats_queue); ++ destroy_workqueue(priv->stats_queue); ++ + for_each_available_child_of_node(dn, port_node) { +- if (priv->port_list[i]) ++ if (priv->port_list[i]) { ++ mutex_destroy(&priv->port_list[i]->gather_stats_lock); + mvpp2_port_remove(priv->port_list[i]); ++ } + i++; + } + +@@ -6487,9 +8858,12 @@ static int mvpp2_remove(struct platform_device *pdev) + dma_free_coherent(&pdev->dev, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, + aggr_txq->descs, +- aggr_txq->descs_phys); ++ aggr_txq->descs_dma); + } + ++ clk_disable_unprepare(priv->axi_clk); ++ clk_disable_unprepare(priv->mg_core_clk); ++ clk_disable_unprepare(priv->mg_clk); + clk_disable_unprepare(priv->pp_clk); + clk_disable_unprepare(priv->gop_clk); + +@@ -6497,7 +8871,14 @@ static int mvpp2_remove(struct platform_device *pdev) + } + + static const struct of_device_id mvpp2_match[] = { +- { .compatible = "marvell,armada-375-pp2" }, ++ { ++ .compatible = "marvell,armada-375-pp2", ++ .data = (void *)MVPP21, ++ }, ++ { ++ .compatible = "marvell,armada-7k-pp22", ++ .data = (void *)MVPP22, ++ }, + { } + }; + MODULE_DEVICE_TABLE(of, mvpp2_match); +diff --git a/drivers/net/ethernet/marvell/mvpp2x/Makefile b/drivers/net/ethernet/marvell/mvpp2x/Makefile +new file mode 100644 +index 00000000..f9a1e3e +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/Makefile +@@ -0,0 +1,5 @@ ++ ++obj-$(CONFIG_MVPP2X) += mvpp2x.o ++ ++mvpp2x-objs := mv_pp2x_ethtool.o mv_pp2x_hw.o mv_pp2x_main.o mv_pp2x_debug.o ++mvpp2x-objs += mv_gop110_hw.o +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.c b/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.c +new file mode 100644 +index 00000000..1281986 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.c +@@ -0,0 +1,3564 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mv_pp2x.h" ++#include "mv_gop110_hw.h" ++#include "mv_pp2x_hw.h" ++ ++void mv_gop110_register_bases_dump(struct gop_hw *gop) ++{ ++ pr_info(" %-32s: 0x%p\n", "GMAC", gop->gop_110.gmac.base); ++ pr_info(" %-32s: 0x%p\n", "XLG_MAC", gop->gop_110.xlg_mac.base); ++ pr_info(" %-32s: 0x%p\n", "XMIB", gop->gop_110.xmib.base); ++ pr_info(" %-32s: 0x%p\n", "SMI", gop->gop_110.smi_base); ++ pr_info(" %-32s: 0x%p\n", "XSMI", gop->gop_110.xsmi_base); ++ pr_info(" %-32s: 0x%p\n", "MSPG", gop->gop_110.mspg.base); ++ pr_info(" %-32s: 0x%p\n", "XPCS", gop->gop_110.xpcs_base); ++ pr_info(" %-32s: 0x%p\n", "PTP", gop->gop_110.ptp.base); ++ pr_info(" %-32s: 0x%p\n", "RFU1", gop->gop_110.rfu1_base); ++} ++EXPORT_SYMBOL(mv_gop110_register_bases_dump); ++ ++/* print value of unit registers */ ++void mv_gop110_gmac_regs_dump(struct gop_hw *gop, int port) ++{ ++ int ind; ++ char reg_name[32]; ++ ++ mv_gop110_gmac_print(gop, "PORT_MAC_CTRL0", port, ++ MV_GMAC_PORT_CTRL0_REG); ++ mv_gop110_gmac_print(gop, "PORT_MAC_CTRL1", port, ++ MV_GMAC_PORT_CTRL1_REG); ++ mv_gop110_gmac_print(gop, "PORT_MAC_CTRL2", port, ++ MV_GMAC_PORT_CTRL2_REG); ++ mv_gop110_gmac_print(gop, "PORT_AUTO_NEG_CFG", port, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ mv_gop110_gmac_print(gop, "PORT_STATUS0", port, ++ MV_GMAC_PORT_STATUS0_REG); ++ mv_gop110_gmac_print(gop, "PORT_SERIAL_PARAM_CFG", port, ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_REG); ++ mv_gop110_gmac_print(gop, "PORT_FIFO_CFG_0", port, ++ MV_GMAC_PORT_FIFO_CFG_0_REG); ++ mv_gop110_gmac_print(gop, "PORT_FIFO_CFG_1", port, ++ MV_GMAC_PORT_FIFO_CFG_1_REG); ++ mv_gop110_gmac_print(gop, "PORT_SERDES_CFG0", port, ++ MV_GMAC_PORT_SERDES_CFG0_REG); ++ mv_gop110_gmac_print(gop, "PORT_SERDES_CFG1", port, ++ MV_GMAC_PORT_SERDES_CFG1_REG); ++ mv_gop110_gmac_print(gop, "PORT_SERDES_CFG2", port, ++ MV_GMAC_PORT_SERDES_CFG2_REG); ++ mv_gop110_gmac_print(gop, "PORT_SERDES_CFG3", port, ++ MV_GMAC_PORT_SERDES_CFG3_REG); ++ mv_gop110_gmac_print(gop, "PORT_PRBS_STATUS", port, ++ MV_GMAC_PORT_PRBS_STATUS_REG); ++ mv_gop110_gmac_print(gop, "PORT_PRBS_ERR_CNTR", port, ++ MV_GMAC_PORT_PRBS_ERR_CNTR_REG); ++ mv_gop110_gmac_print(gop, "PORT_STATUS1", port, ++ MV_GMAC_PORT_STATUS1_REG); ++ mv_gop110_gmac_print(gop, "PORT_MIB_CNTRS_CTRL", port, ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_REG); ++ mv_gop110_gmac_print(gop, "PORT_MAC_CTRL3", port, ++ MV_GMAC_PORT_CTRL3_REG); ++ mv_gop110_gmac_print(gop, "QSGMII", port, ++ MV_GMAC_QSGMII_REG); ++ mv_gop110_gmac_print(gop, "QSGMII_STATUS", port, ++ MV_GMAC_QSGMII_STATUS_REG); ++ mv_gop110_gmac_print(gop, "QSGMII_PRBS_CNTR", port, ++ MV_GMAC_QSGMII_PRBS_CNTR_REG); ++ for (ind = 0; ind < 8; ind++) { ++ sprintf(reg_name, "CCFC_PORT_SPEED_TIMER%d", ind); ++ mv_gop110_gmac_print(gop, reg_name, port, ++ MV_GMAC_CCFC_PORT_SPEED_TIMER_REG(ind)); ++ } ++ for (ind = 0; ind < 4; ind++) { ++ sprintf(reg_name, "FC_DSA_TAG%d", ind); ++ mv_gop110_gmac_print(gop, reg_name, port, ++ MV_GMAC_FC_DSA_TAG_REG(ind)); ++ } ++ mv_gop110_gmac_print(gop, "LINK_LEVEL_FLOW_CTRL_WIN_REG_0", port, ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0); ++ mv_gop110_gmac_print(gop, "LINK_LEVEL_FLOW_CTRL_WIN_REG_1", port, ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1); ++ mv_gop110_gmac_print(gop, "PORT_MAC_CTRL4", port, ++ MV_GMAC_PORT_CTRL4_REG); ++ mv_gop110_gmac_print(gop, "PORT_SERIAL_PARAM_1_CFG", port, ++ MV_GMAC_PORT_SERIAL_PARAM_1_CFG_REG); ++ mv_gop110_gmac_print(gop, "LPI_CTRL_0", port, ++ MV_GMAC_LPI_CTRL_0_REG); ++ mv_gop110_gmac_print(gop, "LPI_CTRL_1", port, ++ MV_GMAC_LPI_CTRL_1_REG); ++ mv_gop110_gmac_print(gop, "LPI_CTRL_2", port, ++ MV_GMAC_LPI_CTRL_2_REG); ++ mv_gop110_gmac_print(gop, "LPI_STATUS", port, ++ MV_GMAC_LPI_STATUS_REG); ++ mv_gop110_gmac_print(gop, "LPI_CNTR", port, ++ MV_GMAC_LPI_CNTR_REG); ++ mv_gop110_gmac_print(gop, "PULSE_1_MS_LOW", port, ++ MV_GMAC_PULSE_1_MS_LOW_REG); ++ mv_gop110_gmac_print(gop, "PULSE_1_MS_HIGH", port, ++ MV_GMAC_PULSE_1_MS_HIGH_REG); ++ mv_gop110_gmac_print(gop, "PORT_INT_MASK", port, ++ MV_GMAC_INTERRUPT_MASK_REG); ++ mv_gop110_gmac_print(gop, "INT_SUM_MASK", port, ++ MV_GMAC_INTERRUPT_SUM_MASK_REG); ++} ++EXPORT_SYMBOL(mv_gop110_gmac_regs_dump); ++ ++/* Set the MAC to reset or exit from reset */ ++int mv_gop110_gmac_reset(struct gop_hw *gop, int mac_num, enum mv_reset reset) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ reg_addr = MV_GMAC_PORT_CTRL2_REG; ++ ++ /* read - modify - write */ ++ val = mv_gop110_gmac_read(gop, mac_num, reg_addr); ++ if (reset == RESET) ++ val |= MV_GMAC_PORT_CTRL2_PORTMACRESET_MASK; ++ else ++ val &= ~MV_GMAC_PORT_CTRL2_PORTMACRESET_MASK; ++ mv_gop110_gmac_write(gop, mac_num, reg_addr, val); ++ ++ return 0; ++} ++ ++static void mv_gop110_gmac_rgmii_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val, thresh, an; ++ ++ /* configure minimal level of the Tx FIFO before the lower ++ * part starts to read a packet ++ */ ++ thresh = MV_RGMII_TX_FIFO_MIN_TH; ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK, ++ (thresh << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG, val); ++ ++ /* Disable bypass of sync module */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ val |= MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK; ++ /* configure DP clock select according to mode */ ++ val &= ~MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK; ++ val |= MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK; ++ val |= MV_GMAC_PORT_CTRL4_EXT_PIN_GMII_SEL_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL4_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL2_REG); ++ val |= MV_GMAC_PORT_CTRL2_CLK_125_BYPS_EN_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL2_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ /* configure GIG MAC to SGMII mode */ ++ val &= ~MV_GMAC_PORT_CTRL0_PORTTYPE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* configure AN 0xb8e8 */ ++ an = MV_GMAC_PORT_AUTO_NEG_CFG_AN_BYPASS_EN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG, an); ++} ++ ++static void mv_gop110_gmac_qsgmii_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val, thresh, an; ++ ++ /* configure minimal level of the Tx FIFO before the lower ++ * part starts to read a packet ++ */ ++ thresh = MV_SGMII_TX_FIFO_MIN_TH; ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK, ++ (thresh << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG, val); ++ ++ /* Disable bypass of sync module */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ val |= MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK; ++ /* configure DP clock select according to mode */ ++ val &= ~MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK; ++ val &= ~MV_GMAC_PORT_CTRL4_EXT_PIN_GMII_SEL_MASK; ++ /* configure QSGMII bypass according to mode */ ++ val &= ~MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL4_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ /* configure GIG MAC to SGMII mode */ ++ val &= ~MV_GMAC_PORT_CTRL0_PORTTYPE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* configure AN 0xB8EC */ ++ an = MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_AN_BYPASS_EN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG, an); ++} ++ ++static void mv_gop110_gmac_sgmii_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val, thresh, an; ++ ++ /* configure minimal level of the Tx FIFO before the lower ++ * part starts to read a packet ++ */ ++ thresh = MV_SGMII_TX_FIFO_MIN_TH; ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK, ++ (thresh << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG, val); ++ ++ /* Disable bypass of sync module */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ val |= MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK; ++ /* configure DP clock select according to mode */ ++ val &= ~MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK; ++ /* configure QSGMII bypass according to mode */ ++ val |= MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL4_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL2_REG); ++ val &= ~MV_GMAC_PORT_CTRL2_FC_MODE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL2_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ /* configure GIG MAC to SGMII mode */ ++ val &= ~MV_GMAC_PORT_CTRL0_PORTTYPE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* configure AN */ ++ an = MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_AN_BYPASS_EN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG, an); ++} ++ ++static void mv_gop110_gmac_sgmii2_5_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val, thresh, an; ++ ++ /* configure minimal level of the Tx FIFO before the lower ++ * part starts to read a packet ++ */ ++ thresh = MV_SGMII2_5_TX_FIFO_MIN_TH; ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK, ++ (thresh << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG, val); ++ ++ /* Disable bypass of sync module */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ val |= MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK; ++ /* configure DP clock select according to mode */ ++ val |= MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK; ++ /* configure QSGMII bypass according to mode */ ++ val |= MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL4_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL2_REG); ++ val &= ~MV_GMAC_PORT_CTRL2_FC_MODE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL2_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ /* configure GIG MAC to 1000Base-X mode connected to a ++ * fiber transceiver ++ */ ++ val &= ~MV_GMAC_PORT_CTRL0_PORTTYPE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* configure AN 0x9260 */ ++ an = MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG, an); ++} ++ ++static void mv_gop110_gmac_1000basex_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val, thresh, an; ++ ++ /* configure minimal level of the Tx FIFO before the lower ++ * part starts to read a packet ++ */ ++ thresh = MV_SGMII_TX_FIFO_MIN_TH; ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK, ++ (thresh << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG, val); ++ ++ /* Disable bypass of sync module */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ val |= MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK; ++ /* configure DP clock select according to mode */ ++ val &= ~MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK; ++ /* configure QSGMII bypass according to mode */ ++ val |= MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL4_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL2_REG); ++ val &= ~MV_GMAC_PORT_CTRL2_FC_MODE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL2_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ /* configure GIG MAC to 1000BASEX mode */ ++ val |= MV_GMAC_PORT_CTRL0_PORTTYPE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* In 1000BaseX mode, we can't negotiate speed (it's ++ * only 1000), and we do not want InBand autoneg ++ * bypass enabled (link interrupt storm risk ++ * otherwise). ++ */ ++ an = MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG, an); ++} ++ ++static void mv_gop110_gmac_2500basex_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val, thresh, an; ++ ++ /* configure minimal level of the Tx FIFO before the lower ++ * part starts to read a packet ++ */ ++ thresh = MV_SGMII2_5_TX_FIFO_MIN_TH; ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK, ++ (thresh << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_FIFO_CFG_1_REG, val); ++ ++ /* Disable bypass of sync module */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ val |= MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK; ++ /* configure DP clock select according to mode */ ++ val |= MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK; ++ /* configure QSGMII bypass according to mode */ ++ val |= MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL4_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL2_REG); ++ val &= ~MV_GMAC_PORT_CTRL2_FC_MODE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL2_REG, val); ++ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ /* configure GIG MAC to 1000Base-X mode connected to a ++ * fiber transceiver ++ */ ++ val |= MV_GMAC_PORT_CTRL0_PORTTYPE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* configure AN 0x9260 */ ++ an = MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_MASK | ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK; ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG, an); ++} ++ ++/* Set the internal mux's to the required MAC in the GOP */ ++int mv_gop110_gmac_mode_cfg(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ int mac_num = mac->gop_index; ++ ++ /* Set TX FIFO thresholds */ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_SGMII: ++ if (mac->flags & MV_EMAC_F_SGMII2_5) ++ mv_gop110_gmac_sgmii2_5_cfg(gop, mac_num); ++ else ++ mv_gop110_gmac_sgmii_cfg(gop, mac_num); ++ break; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ if (mac->flags & MV_EMAC_F_SGMII2_5) ++ mv_gop110_gmac_2500basex_cfg(gop, mac_num); ++ else ++ mv_gop110_gmac_1000basex_cfg(gop, mac_num); ++ break; ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ mv_gop110_gmac_rgmii_cfg(gop, mac_num); ++ break; ++ case PHY_INTERFACE_MODE_QSGMII: ++ mv_gop110_gmac_qsgmii_cfg(gop, mac_num); ++ break; ++ default: ++ return -1; ++ } ++ ++ /* Control packets are forwarded into the ingress pipe */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_SERIAL_PARAM_CFG_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_SERIAL_PARAM_CFG_FORWARD_802_3X_FC_EN_MASK, ++ (0x1 << MV_GMAC_PORT_SERIAL_PARAM_CFG_FORWARD_802_3X_FC_EN_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_SERIAL_PARAM_CFG_REG, val); ++ ++ /* Jumbo frame support - 0x1400*2= 0x2800 bytes */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ U32_SET_FIELD(val, MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_MASK, ++ (0x1400 << MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_OFFS)); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, val); ++ ++ /* PeriodicXonEn disable */ ++ reg_addr = MV_GMAC_PORT_CTRL1_REG; ++ val = mv_gop110_gmac_read(gop, mac_num, reg_addr); ++ val &= ~MV_GMAC_PORT_CTRL1_EN_PERIODIC_FC_XON_MASK; ++ mv_gop110_gmac_write(gop, mac_num, reg_addr, val); ++ ++ /* mask all ports interrupts */ ++ mv_gop110_gmac_port_link_event_mask(gop, mac_num); ++ ++ /* unmask link change interrupt */ ++ val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_INTERRUPT_MASK_REG); ++ val |= MV_GMAC_INTERRUPT_CAUSE_LINK_CHANGE_MASK; ++ val |= 1; /* unmask summary bit */ ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_INTERRUPT_MASK_REG, val); ++ ++ return 0; ++} ++ ++/* Configure MAC loopback */ ++int mv_gop110_gmac_loopback_cfg(struct gop_hw *gop, int mac_num, ++ enum mv_lb_type type) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ reg_addr = MV_GMAC_PORT_CTRL1_REG; ++ val = mv_gop110_gmac_read(gop, mac_num, reg_addr); ++ switch (type) { ++ case MV_DISABLE_LB: ++ val &= ~MV_GMAC_PORT_CTRL1_GMII_LOOPBACK_MASK; ++ break; ++ case MV_TX_2_RX_LB: ++ val |= MV_GMAC_PORT_CTRL1_GMII_LOOPBACK_MASK; ++ break; ++ case MV_RX_2_TX_LB: ++ default: ++ return -1; ++ } ++ mv_gop110_gmac_write(gop, mac_num, reg_addr, val); ++ ++ return 0; ++} ++ ++/* Get MAC link status */ ++bool mv_gop110_gmac_link_status_get(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ reg_addr = MV_GMAC_PORT_STATUS0_REG; ++ ++ val = mv_gop110_gmac_read(gop, mac_num, reg_addr); ++ return (val & 1) ? true : false; ++} ++ ++/* Enable port and MIB counters */ ++void mv_gop110_gmac_port_enable(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ reg_val |= MV_GMAC_PORT_CTRL0_PORTEN_MASK; ++ reg_val |= MV_GMAC_PORT_CTRL0_COUNT_EN_MASK; ++ ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, reg_val); ++} ++ ++/* Disable port */ ++void mv_gop110_gmac_port_disable(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ /* mask all ports interrupts */ ++ mv_gop110_gmac_port_link_event_mask(gop, mac_num); ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ reg_val &= ~MV_GMAC_PORT_CTRL0_PORTEN_MASK; ++ ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, reg_val); ++} ++ ++void mv_gop110_gmac_port_periodic_xon_set(struct gop_hw *gop, ++ int mac_num, int enable) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL1_REG); ++ ++ if (enable) ++ reg_val |= MV_GMAC_PORT_CTRL1_EN_PERIODIC_FC_XON_MASK; ++ else ++ reg_val &= ~MV_GMAC_PORT_CTRL1_EN_PERIODIC_FC_XON_MASK; ++ ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL1_REG, reg_val); ++} ++ ++int mv_gop110_gmac_link_status(struct gop_hw *gop, int mac_num, ++ struct mv_port_link_status *pstatus) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_STATUS0_REG); ++ ++ if (reg_val & MV_GMAC_PORT_STATUS0_GMIISPEED_MASK) ++ pstatus->speed = MV_PORT_SPEED_1000; ++ else if (reg_val & MV_GMAC_PORT_STATUS0_MIISPEED_MASK) ++ pstatus->speed = MV_PORT_SPEED_100; ++ else ++ pstatus->speed = MV_PORT_SPEED_10; ++ ++ if (reg_val & MV_GMAC_PORT_STATUS0_LINKUP_MASK) ++ pstatus->linkup = 1 /*TRUE*/; ++ else ++ pstatus->linkup = 0 /*FALSE*/; ++ ++ if (reg_val & MV_GMAC_PORT_STATUS0_FULLDX_MASK) ++ pstatus->duplex = MV_PORT_DUPLEX_FULL; ++ else ++ pstatus->duplex = MV_PORT_DUPLEX_HALF; ++ ++ if (reg_val & MV_GMAC_PORT_STATUS0_PORTTXPAUSE_MASK) ++ pstatus->tx_fc = MV_PORT_FC_ACTIVE; ++ else if (reg_val & MV_GMAC_PORT_STATUS0_TXFCEN_MASK) ++ pstatus->tx_fc = MV_PORT_FC_ENABLE; ++ else ++ pstatus->tx_fc = MV_PORT_FC_DISABLE; ++ ++ if (reg_val & MV_GMAC_PORT_STATUS0_PORTRXPAUSE_MASK) ++ pstatus->rx_fc = MV_PORT_FC_ACTIVE; ++ else if (reg_val & MV_GMAC_PORT_STATUS0_RXFCEN_MASK) ++ pstatus->rx_fc = MV_PORT_FC_ENABLE; ++ else ++ pstatus->rx_fc = MV_PORT_FC_DISABLE; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK) { ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK) ++ pstatus->autoneg_fc = MV_PORT_FC_AN_ASYM; ++ else ++ pstatus->autoneg_fc = MV_PORT_FC_AN_SYM; ++ } else { ++ pstatus->autoneg_fc = MV_PORT_FC_AN_NO; ++ } ++ ++ return 0; ++} ++ ++/* Change maximum receive size of the port */ ++int mv_gop110_gmac_max_rx_size_set(struct gop_hw *gop, ++ int mac_num, int max_rx_size) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL0_REG); ++ reg_val &= ~MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_MASK; ++ reg_val |= (((max_rx_size - MVPP2_MH_SIZE) / 2) << ++ MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_OFFS); ++ mv_gop110_gmac_write(gop, mac_num, MV_GMAC_PORT_CTRL0_REG, reg_val); ++ ++ return 0; ++} ++ ++/* Sets "Force Link Pass" and "Do Not Force Link Fail" bits. ++* This function should only be called when the port is disabled. ++* INPUT: ++* int port - port number ++* bool force_link_pass - Force Link Pass ++* bool force_link_fail - Force Link Failure ++* 0, 0 - normal state: detect link via PHY and connector ++* 1, 1 - prohibited state. ++*/ ++int mv_gop110_gmac_force_link_mode_set(struct gop_hw *gop, int mac_num, ++ bool force_link_up, ++ bool force_link_down) ++{ ++ u32 reg_val; ++ ++ /* Can't force link pass and link fail at the same time */ ++ if ((force_link_up) && (force_link_down)) ++ return -EINVAL; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (force_link_up) ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_UP_MASK; ++ else ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_UP_MASK; ++ ++ if (force_link_down) ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_DOWN_MASK; ++ else ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_DOWN_MASK; ++ ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG, reg_val); ++ ++ return 0; ++} ++ ++/* Get "Force Link Pass" and "Do Not Force Link Fail" bits. ++* INPUT: ++* int port - port number ++* OUTPUT: ++* bool *force_link_pass - Force Link Pass ++* bool *force_link_fail - Force Link Failure ++*/ ++int mv_gop110_gmac_force_link_mode_get(struct gop_hw *gop, int mac_num, ++ bool *force_link_up, ++ bool *force_link_down) ++{ ++ u32 reg_val; ++ ++ /* Can't force link pass and link fail at the same time */ ++ if ((!force_link_up) || (!force_link_down)) ++ return -EINVAL; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_UP_MASK) ++ *force_link_up = true; ++ else ++ *force_link_up = false; ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_DOWN_MASK) ++ *force_link_down = true; ++ else ++ *force_link_down = false; ++ ++ return 0; ++} ++ ++/* Sets port speed to Auto Negotiation / 1000 / 100 / 10 Mbps. ++* Sets port duplex to Auto Negotiation / Full / Half Duplex. ++*/ ++int mv_gop110_gmac_speed_duplex_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ enum mv_port_duplex duplex) ++{ ++ u32 reg_val; ++ ++ /* Check validity */ ++ if ((speed == MV_PORT_SPEED_1000) && (duplex == MV_PORT_DUPLEX_HALF)) ++ return -EINVAL; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ switch (speed) { ++ case MV_PORT_SPEED_AN: ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK; ++ /* the other bits don't matter in this case */ ++ break; ++ case MV_PORT_SPEED_2500: ++ case MV_PORT_SPEED_1000: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK; ++ /* the 100/10 bit doesn't matter in this case */ ++ break; ++ case MV_PORT_SPEED_100: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_MASK; ++ break; ++ case MV_PORT_SPEED_10: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_MASK; ++ break; ++ default: ++ pr_info("GMAC: Unexpected Speed value %d\n", speed); ++ return -EINVAL; ++ } ++ ++ switch (duplex) { ++ case MV_PORT_DUPLEX_AN: ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK; ++ /* the other bits don't matter in this case */ ++ break; ++ case MV_PORT_DUPLEX_HALF: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_MASK; ++ break; ++ case MV_PORT_DUPLEX_FULL: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_MASK; ++ break; ++ default: ++ pr_err("GMAC: Unexpected Duplex value %d\n", duplex); ++ return -EINVAL; ++ } ++ ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG, reg_val); ++ return 0; ++} ++ ++/* Gets port speed and duplex */ ++int mv_gop110_gmac_speed_duplex_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed *speed, ++ enum mv_port_duplex *duplex) ++{ ++ u32 reg_val; ++ ++ /* Check validity */ ++ if (!speed || !duplex) ++ return -EINVAL; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK) ++ *speed = MV_PORT_SPEED_AN; ++ else if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK) ++ *speed = MV_PORT_SPEED_1000; ++ else if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_MASK) ++ *speed = MV_PORT_SPEED_100; ++ else ++ *speed = MV_PORT_SPEED_10; ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK) ++ *duplex = MV_PORT_DUPLEX_AN; ++ else if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_MASK) ++ *duplex = MV_PORT_DUPLEX_FULL; ++ else ++ *duplex = MV_PORT_DUPLEX_HALF; ++ ++ return 0; ++} ++ ++/* Configure the port's Flow Control properties */ ++int mv_gop110_gmac_fc_set(struct gop_hw *gop, int mac_num, enum mv_port_fc fc) ++{ ++ u32 reg_val; ++ u32 fc_en; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ fc_en = mv_gop110_gmac_read(gop, mac_num, MV_GMAC_PORT_CTRL4_REG); ++ ++ switch (fc) { ++ case MV_PORT_FC_AN_NO: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK; ++ break; ++ ++ case MV_PORT_FC_AN_SYM: ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK; ++ break; ++ ++ case MV_PORT_FC_AN_ASYM: ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK; ++ break; ++ ++ case MV_PORT_FC_DISABLE: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK; ++ fc_en &= ~MV_GMAC_PORT_CTRL4_FC_EN_RX_MASK; ++ fc_en &= ~MV_GMAC_PORT_CTRL4_FC_EN_TX_MASK; ++ break; ++ ++ case MV_PORT_FC_TX_DISABLE: ++ fc_en &= ~MV_GMAC_PORT_CTRL4_FC_EN_TX_MASK; ++ break; ++ ++ case MV_PORT_FC_RX_DISABLE: ++ fc_en &= ~MV_GMAC_PORT_CTRL4_FC_EN_RX_MASK; ++ break; ++ ++ case MV_PORT_FC_ENABLE: ++ fc_en |= MV_GMAC_PORT_CTRL4_FC_EN_RX_MASK; ++ fc_en |= MV_GMAC_PORT_CTRL4_FC_EN_TX_MASK; ++ break; ++ ++ case MV_PORT_FC_TX_ENABLE: ++ fc_en |= MV_GMAC_PORT_CTRL4_FC_EN_TX_MASK; ++ break; ++ ++ case MV_PORT_FC_RX_ENABLE: ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK; ++ fc_en |= MV_GMAC_PORT_CTRL4_FC_EN_RX_MASK; ++ break; ++ ++ default: ++ pr_err("GMAC: Unexpected FlowControl value %d\n", fc); ++ return -EINVAL; ++ } ++ ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_PORT_CTRL4_REG, fc_en); ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG, reg_val); ++ return 0; ++} ++ ++/* Get Flow Control configuration of the port */ ++void mv_gop110_gmac_fc_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc *fc) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK) { ++ /* Auto negotiation is enabled */ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK) { ++ if (reg_val & ++ MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK) ++ *fc = MV_PORT_FC_AN_ASYM; ++ else ++ *fc = MV_PORT_FC_AN_SYM; ++ } else { ++ *fc = MV_PORT_FC_AN_NO; ++ } ++ } else { ++ /* Auto negotiation is disabled */ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_CTRL4_REG); ++ if ((reg_val & MV_GMAC_PORT_CTRL4_FC_EN_RX_MASK) && ++ (reg_val & MV_GMAC_PORT_CTRL4_FC_EN_TX_MASK)) ++ *fc = MV_PORT_FC_ENABLE; ++ else ++ *fc = MV_PORT_FC_DISABLE; ++ } ++} ++ ++int mv_gop110_gmac_port_link_speed_fc(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ int force_link_up) ++{ ++ if (force_link_up) { ++ if (mv_gop110_gmac_speed_duplex_set(gop, mac_num, speed, ++ MV_PORT_DUPLEX_FULL)) { ++ pr_err("mv_gop110_gmac_speed_duplex_set failed\n"); ++ return -EPERM; ++ } ++ if (mv_gop110_gmac_fc_set(gop, mac_num, MV_PORT_FC_ENABLE)) { ++ pr_err("mv_gop110_gmac_fc_set failed\n"); ++ return -EPERM; ++ } ++ if (mv_gop110_gmac_force_link_mode_set(gop, mac_num, 1, 0)) { ++ pr_err("mv_gop110_gmac_force_link_mode_set failed\n"); ++ return -EPERM; ++ } ++ } else { ++ if (mv_gop110_gmac_force_link_mode_set(gop, mac_num, 0, 0)) { ++ pr_err("mv_gop110_gmac_force_link_mode_set failed\n"); ++ return -EPERM; ++ } ++ if (mv_gop110_gmac_speed_duplex_set(gop, mac_num, ++ MV_PORT_SPEED_AN, ++ MV_PORT_DUPLEX_AN)) { ++ pr_err("mv_gop110_gmac_speed_duplex_set failed\n"); ++ return -EPERM; ++ } ++ if (mv_gop110_gmac_fc_set(gop, mac_num, MV_PORT_FC_AN_SYM)) { ++ pr_err("mv_gop110_gmac_fc_set failed\n"); ++ return -EPERM; ++ } ++ } ++ ++ return 0; ++} ++ ++void mv_gop110_gmac_port_link_event_mask(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_INTERRUPT_SUM_MASK_REG); ++ reg_val &= ~MV_GMAC_INTERRUPT_SUM_CAUSE_LINK_CHANGE_MASK; ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_INTERRUPT_SUM_MASK_REG, reg_val); ++} ++ ++void mv_gop110_gmac_port_link_event_unmask(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_INTERRUPT_SUM_MASK_REG); ++ reg_val |= MV_GMAC_INTERRUPT_SUM_CAUSE_LINK_CHANGE_MASK; ++ reg_val |= 1; /* unmask summary bit */ ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_INTERRUPT_SUM_MASK_REG, reg_val); ++} ++ ++void mv_gop110_gmac_port_link_event_clear(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_INTERRUPT_CAUSE_REG); ++} ++ ++int mv_gop110_gmac_port_autoneg_restart(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ /* enable AN and restart it */ ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_INBAND_RESTARTAN_MASK; ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG, reg_val); ++ return 0; ++} ++ ++void mv_gop110_mac_xpcs(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL4_REG); ++ ++ reg_val |= MV_XLG_MAC_CTRL4_USE_XPCS_MASK; ++ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL4_REG, reg_val); ++} ++ ++/************************************************************************* ++* mv_port_init ++* ++* DESCRIPTION: ++* Init physical port. Configures the port mode and all it's elements ++* accordingly. ++* Does not verify that the selected mode/port number is valid at the ++* core level. ++* ++* INPUTS: ++* port_num - physical port number ++* port_mode - port standard metric ++* ++* OUTPUTS: ++* None. ++* ++* RETURNS: ++* 0 - on success ++* 1 - on error ++* ++*************************************************************************/ ++int mv_gop110_port_init(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int num_of_act_lanes; ++ int mac_num = mac->gop_index; ++ ++ if (mac_num >= MVCPN110_GOP_MAC_NUM) { ++ pr_err("%s: illegal port number %d", __func__, mac_num); ++ return -1; ++ } ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ mv_gop110_xlg_mac_1G_cfg(gop, mac_num); ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ mv_gop110_gmac_reset(gop, mac_num, RESET); ++ /* configure PCS */ ++ mv_gop110_gpcs_mode_cfg(gop, mac_num, false); ++ ++ /* configure MAC */ ++ mv_gop110_gmac_mode_cfg(gop, mac); ++ ++ /* select proper Mac mode */ ++ mv_gop110_xlg_2_gig_mac_cfg(gop, mac_num); ++ ++ /* set InBand AutoNeg */ ++ mv_gop110_in_band_auto_neg(gop, mac_num, true); ++ /* mac unreset */ ++ mv_gop110_gmac_reset(gop, mac_num, UNRESET); ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ mv_gop110_xlg_mac_1G_cfg(gop, mac_num); ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ mv_gop110_gmac_reset(gop, mac_num, RESET); ++ /* configure PCS */ ++ mv_gop110_gpcs_mode_cfg(gop, mac_num, true); ++ ++ /* configure MAC */ ++ mv_gop110_gmac_mode_cfg(gop, mac); ++ /* select proper Mac mode */ ++ mv_gop110_xlg_2_gig_mac_cfg(gop, mac_num); ++ ++ /* set InBand AutoNeg */ ++ mv_gop110_in_band_auto_neg(gop, mac_num, true); ++ /* mac unreset */ ++ mv_gop110_gmac_reset(gop, mac_num, UNRESET); ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ break; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_xlg_mac_1G_cfg(gop, mac_num); ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ mv_gop110_gmac_reset(gop, mac_num, RESET); ++ /* configure PCS */ ++ mv_gop110_gpcs_mode_cfg(gop, mac_num, true); ++ ++ /* configure MAC */ ++ mv_gop110_gmac_mode_cfg(gop, mac); ++ /* select proper Mac mode */ ++ mv_gop110_xlg_2_gig_mac_cfg(gop, mac_num); ++ ++ /* set InBand AutoNeg */ ++ mv_gop110_in_band_auto_neg(gop, mac_num, false); ++ ++ /* mac unreset */ ++ mv_gop110_gmac_reset(gop, mac_num, UNRESET); ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ num_of_act_lanes = 4; ++ mac_num = 0; ++ /* configure PCS */ ++ mv_gop110_xpcs_mode(gop, num_of_act_lanes); ++ /* configure MAC */ ++ mv_gop110_xlg_mac_mode_cfg(gop, mac_num, num_of_act_lanes); ++ ++ /* pcs unreset */ ++ mv_gop110_xpcs_reset(gop, UNRESET); ++ /* mac unreset */ ++ mv_gop110_xlg_mac_reset(gop, mac_num, UNRESET); ++ break; ++ case PHY_INTERFACE_MODE_RXAUI: ++ num_of_act_lanes = 2; ++ mac_num = 0; ++ ++ /* configure XPCS in MAC */ ++ mv_gop110_mac_xpcs(gop, mac_num); ++ ++ /* configure PCS */ ++ mv_gop110_xpcs_mode(gop, num_of_act_lanes); ++ /* configure MAC */ ++ mv_gop110_xlg_mac_mode_cfg(gop, mac_num, num_of_act_lanes); ++ ++ /* pcs unreset */ ++ mv_gop110_xpcs_reset(gop, UNRESET); ++ ++ /* mac unreset */ ++ mv_gop110_xlg_mac_reset(gop, mac_num, UNRESET); ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ ++ num_of_act_lanes = 2; ++ /* configure MPCS */ ++ mv_gop110_mpcs_mode(gop, mac_num); ++ /* configure MAC */ ++ mv_gop110_xlg_mac_mode_cfg(gop, mac_num, num_of_act_lanes); ++ ++ /* mac unreset */ ++ mv_gop110_xlg_mac_reset(gop, mac_num, UNRESET); ++ break; ++ default: ++ pr_err("%s: Requested port mode (%d) not supported", ++ __func__, mac->phy_mode); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/************************************************************************** ++* mv_port_reset ++* ++* DESCRIPTION: ++* Clears the port mode and release all its resources ++* according to selected. ++* Does not verify that the selected mode/port number is valid at the core ++* level and actual terminated mode. ++* ++* INPUTS: ++* port_num - physical port number ++* port_mode - port standard metric ++* action - Power down or reset ++* ++* OUTPUTS: ++* None. ++* ++* RETURNS: ++* 0 - on success ++* 1 - on error ++* ++**************************************************************************/ ++int mv_gop110_port_reset(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int mac_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* set InBand AutoNeg */ ++ mv_gop110_in_band_auto_neg(gop, mac_num, false); ++ /* mac unreset */ ++ mv_gop110_gmac_reset(gop, mac_num, RESET); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ /* pcs unreset */ ++ mv_gop110_xpcs_reset(gop, RESET); ++ /* mac unreset */ ++ mv_gop110_xlg_mac_reset(gop, mac_num, RESET); ++ break; ++ case PHY_INTERFACE_MODE_RXAUI: ++ /* pcs unreset */ ++ mv_gop110_xpcs_reset(gop, RESET); ++ /* mac unreset */ ++ mv_gop110_xlg_mac_reset(gop, mac_num, RESET); ++ break; ++ /* Stefan: need to check KR case */ ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ /* mac unreset */ ++ mv_gop110_xlg_mac_reset(gop, mac_num, RESET); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ ++ /* TBD:serdes reset or power down if needed*/ ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------*/ ++void mv_gop110_port_enable(struct gop_hw *gop, struct mv_mac_data *mac, struct mv_pp2x_port *port) ++{ ++ int port_num = mac->gop_index; ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ int i; ++#endif ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_port_enable(gop, port_num); ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ mv_gop110_gmac_reset(gop, port_num, UNRESET); ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) ++ phy_send_command(port->comphy[0], COMPHY_COMMAND_DIGITAL_PWR_ON); ++#endif ++ mv_gop110_mpcs_clock_reset(gop, port_num, UNRESET); ++ mv_gop110_xlg_mac_reset(gop, port_num, UNRESET); ++ mv_gop110_xlg_mac_port_enable(gop, port_num); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) ++ for (i = 0; i < port->num_serdes_lanes; i++) ++ phy_send_command(port->comphy[i], COMPHY_COMMAND_DIGITAL_PWR_ON); ++#endif ++ mv_gop110_xpcs_reset(gop, UNRESET); ++ mv_gop110_xlg_mac_reset(gop, port_num, UNRESET); ++ mv_gop110_xlg_mac_port_enable(gop, port_num); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return; ++ } ++} ++ ++void mv_gop110_port_disable(struct gop_hw *gop, struct mv_mac_data *mac, struct mv_pp2x_port *port) ++{ ++ int port_num = mac->gop_index; ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ int i; ++#endif ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_port_disable(gop, port_num); ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ mv_gop110_gmac_reset(gop, port_num, RESET); ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_port_disable(gop, port_num); ++ mv_gop110_xlg_mac_reset(gop, port_num, RESET); ++ mv_gop110_mpcs_clock_reset(gop, port_num, RESET); ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) ++ phy_send_command(port->comphy[0], COMPHY_COMMAND_DIGITAL_PWR_OFF); ++#endif ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ mv_gop110_xlg_mac_port_disable(gop, port_num); ++ mv_gop110_xlg_mac_reset(gop, port_num, RESET); ++ mv_gop110_xpcs_reset(gop, RESET); ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) ++ for (i = 0; i < port->num_serdes_lanes; i++) ++ phy_send_command(port->comphy[i], COMPHY_COMMAND_DIGITAL_PWR_OFF); ++#endif ++ break; ++ ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return; ++ } ++} ++ ++void mv_gop110_port_periodic_xon_set(struct gop_hw *gop, ++ struct mv_mac_data *mac, ++ int enable) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_port_periodic_xon_set(gop, port_num, enable); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_port_periodic_xon_set(gop, port_num, enable); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return; ++ } ++} ++ ++bool mv_gop110_port_is_link_up(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return mv_gop110_gmac_link_status_get(gop, port_num); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ return mv_gop110_xlg_mac_link_status_get(gop, port_num); ++ break; ++ default: ++ pr_err("%s: Wrong port mode gop_port(%d), phy_mode(%d)", ++ __func__, port_num, mac->phy_mode); ++ return false; ++ } ++} ++ ++int mv_gop110_port_link_status(struct gop_hw *gop, struct mv_mac_data *mac, ++ struct mv_port_link_status *pstatus) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_link_status(gop, port_num, pstatus); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_link_status(gop, port_num, pstatus); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++bool mv_gop110_port_autoneg_status(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac->gop_index, MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_OFFS && ++ reg_val & MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK) ++ return true; ++ else ++ return false; ++} ++ ++int mv_gop110_check_port_type(struct gop_hw *gop, int port_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_gmac_read(gop, port_num, MV_GMAC_PORT_CTRL0_REG); ++ return (reg_val & MV_GMAC_PORT_CTRL0_PORTTYPE_MASK) >> ++ MV_GMAC_PORT_CTRL0_PORTTYPE_OFFS; ++} ++ ++void mv_gop110_gmac_set_autoneg(struct gop_hw *gop, struct mv_mac_data *mac, bool auto_neg) ++{ ++ u32 reg_val; ++ int mac_num = mac->gop_index; ++ ++ reg_val = mv_gop110_gmac_read(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ ++ if (auto_neg) { ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK; ++ reg_val |= MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK; ++ } ++ ++ else { ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK; ++ reg_val &= ~MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK; ++ } ++ ++ mv_gop110_gmac_write(gop, mac_num, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG, reg_val); ++} ++ ++int mv_gop110_port_regs(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ pr_info("\n[gop GMAC #%d registers]\n", port_num); ++ mv_gop110_gmac_regs_dump(gop, port_num); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ pr_info("\n[gop XLG MAC #%d registers]\n", port_num); ++ mv_gop110_xlg_mac_regs_dump(gop, port_num); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++int mv_gop110_port_events_mask(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_port_link_event_mask(gop, port_num); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_port_link_event_mask(gop, port_num); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++int mv_gop110_port_events_unmask(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_port_link_event_unmask(gop, port_num); ++ /* gige interrupt cause connected to CPU via XLG ++ * external interrupt cause ++ */ ++ mv_gop110_xlg_port_external_event_unmask(gop, 0, 2); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_port_external_event_unmask(gop, port_num, 1); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++int mv_gop110_port_events_clear(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_port_link_event_clear(gop, port_num); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_port_link_event_clear(gop, port_num); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++int mv_gop110_status_show(struct gop_hw *gop, struct mv_pp2x *pp2, int port_num) ++{ ++ struct mv_port_link_status port_status; ++ struct mv_pp2x_port *pp_port; ++ struct mv_mac_data *mac; ++ ++ pp_port = mv_pp2x_port_struct_get(pp2, port_num); ++ mac = &pp_port->mac_data; ++ ++ mv_gop110_port_link_status(gop, mac, &port_status); ++ ++ pr_info("-------------- Port %d configuration ----------------", ++ port_num); ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ pr_info("Port mode : RGMII"); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ pr_info("Port mode : SGMII"); ++ break; ++ case PHY_INTERFACE_MODE_QSGMII: ++ pr_info("Port mode : QSGMII"); ++ break; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ pr_info("Port mode : 1000BASEX"); ++ case PHY_INTERFACE_MODE_XAUI: ++ pr_info("Port mode : XAUI"); ++ break; ++ case PHY_INTERFACE_MODE_RXAUI: ++ pr_info("Port mode : RXAUI"); ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ pr_info("Port mode : KR"); ++ break; ++ case PHY_INTERFACE_MODE_SFI: ++ pr_info("Port mode : SFI"); ++ break; ++ case PHY_INTERFACE_MODE_XFI: ++ pr_info("Port mode : XFI"); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ ++ pr_info("\nLink status : %s", ++ (port_status.linkup) ? "link up" : "link down"); ++ pr_info("\n"); ++ ++ if ((mac->phy_mode == PHY_INTERFACE_MODE_SGMII) && ++ (mac->flags & MV_EMAC_F_SGMII2_5) && ++ (port_status.speed == MV_PORT_SPEED_1000)) ++ port_status.speed = MV_PORT_SPEED_2500; ++ ++ switch (port_status.speed) { ++ case MV_PORT_SPEED_AN: ++ pr_info("Port speed : AutoNeg"); ++ break; ++ case MV_PORT_SPEED_10: ++ pr_info("Port speed : 10M"); ++ break; ++ case MV_PORT_SPEED_100: ++ pr_info("Port speed : 100M"); ++ break; ++ case MV_PORT_SPEED_1000: ++ pr_info("Port speed : 1G"); ++ break; ++ case MV_PORT_SPEED_2500: ++ pr_info("Port speed : 2.5G"); ++ break; ++ case MV_PORT_SPEED_10000: ++ pr_info("Port speed : 10G"); ++ break; ++ default: ++ pr_err("%s: Wrong port speed (%d)\n", __func__, ++ port_status.speed); ++ return -1; ++ } ++ pr_info("\n"); ++ switch (port_status.duplex) { ++ case MV_PORT_DUPLEX_AN: ++ pr_info("Port duplex : AutoNeg"); ++ break; ++ case MV_PORT_DUPLEX_HALF: ++ pr_info("Port duplex : half"); ++ break; ++ case MV_PORT_DUPLEX_FULL: ++ pr_info("Port duplex : full"); ++ break; ++ default: ++ pr_err("%s: Wrong port duplex (%d)", __func__, ++ port_status.duplex); ++ return -1; ++ } ++ pr_info("\n"); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_gop110_status_show); ++ ++/* get port speed and duplex */ ++int mv_gop110_speed_duplex_get(struct gop_hw *gop, struct mv_mac_data *mac, ++ enum mv_port_speed *speed, ++ enum mv_port_duplex *duplex) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_speed_duplex_get(gop, port_num, speed, ++ duplex); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_speed_duplex_get(gop, port_num, speed, ++ duplex); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++/* set port speed and duplex */ ++int mv_gop110_speed_duplex_set(struct gop_hw *gop, struct mv_mac_data *mac, ++ enum mv_port_speed speed, ++ enum mv_port_duplex duplex) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_speed_duplex_set(gop, port_num, speed, duplex); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_speed_duplex_set(gop, port_num, speed, ++ duplex); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++int mv_gop110_autoneg_restart(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ mv_gop110_gmac_port_autoneg_restart(gop, port_num); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ pr_err("%s: on supported for port mode (%d)", __func__, ++ mac->phy_mode); ++ return -1; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++int mv_gop110_fl_cfg(struct gop_hw *gop, struct mv_mac_data *mac) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* disable AN */ ++ if (mac->flags & MV_EMAC_F_SGMII2_5) ++ mv_gop110_speed_duplex_set(gop, mac, ++ MV_PORT_SPEED_2500, ++ MV_PORT_DUPLEX_FULL); ++ else ++ mv_gop110_speed_duplex_set(gop, mac, ++ MV_PORT_SPEED_1000, ++ MV_PORT_DUPLEX_FULL); ++ /* force link */ ++ mv_gop110_gmac_force_link_mode_set(gop, port_num, true, false); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ return 0; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++/* set port ForceLinkUp and ForceLinkDown*/ ++int mv_gop110_force_link_mode_set(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool force_link_up, ++ bool force_link_down) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* force link */ ++ mv_gop110_gmac_force_link_mode_set(gop, port_num, ++ force_link_up, ++ force_link_down); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ return 0; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++/* get port ForceLinkUp and ForceLinkDown*/ ++int mv_gop110_force_link_mode_get(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool *force_link_up, ++ bool *force_link_down) ++{ ++ int port_num = mac->gop_index; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return mv_gop110_gmac_force_link_mode_get(gop, port_num, ++ force_link_up, ++ force_link_down); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ return 0; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++/* set port internal loopback*/ ++int mv_gop110_loopback_set(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool lb) ++{ ++ int port_num = mac->gop_index; ++ enum mv_lb_type type; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* set loopback */ ++ if (lb) ++ type = MV_TX_2_RX_LB; ++ else ++ type = MV_DISABLE_LB; ++ ++ mv_gop110_gmac_loopback_cfg(gop, port_num, type); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ return 0; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++ return 0; ++} ++ ++/* Set GPCS mode configuration */ ++int mv_gop110_gpcs_mode_cfg(struct gop_hw *gop, int pcs_num, bool en) ++{ ++ u32 val; ++ ++ val = mv_gop110_gmac_read(gop, pcs_num, MV_GMAC_PORT_CTRL2_REG); ++ ++ if (en) ++ val |= MV_GMAC_PORT_CTRL2_PCS_EN_MASK; ++ else ++ val &= ~MV_GMAC_PORT_CTRL2_PCS_EN_MASK; ++ ++ /* enable / disable PCS on this port */ ++ mv_gop110_gmac_write(gop, pcs_num, MV_GMAC_PORT_CTRL2_REG, val); ++ ++ return 0; ++} ++ ++/* Set InBand AutoNeg configuration */ ++int mv_gop110_in_band_auto_neg(struct gop_hw *gop, int pcs_num, bool en) ++{ ++ u32 reg_data; ++ ++ reg_data = mv_gop110_gmac_read(gop, pcs_num, MV_GMAC_PORT_CTRL2_REG); ++ if (en) ++ U32_SET_FIELD(reg_data, MV_GMAC_PORT_CTRL2_SGMII_MODE_MASK, ++ 1 << MV_GMAC_PORT_CTRL2_SGMII_MODE_OFFS); ++ else ++ U32_SET_FIELD(reg_data, MV_GMAC_PORT_CTRL2_SGMII_MODE_MASK, 0); ++ ++ mv_gop110_gmac_write(gop, pcs_num, MV_GMAC_PORT_CTRL2_REG, reg_data); ++ return 0; ++} ++ ++/* Init SMI interface */ ++int mv_gop110_smi_init(struct gop_hw *gop) ++{ ++ u32 val; ++ ++ /* not invert MDC */ ++ val = mv_gop110_smi_read(gop, MV_SMI_MISC_CFG_REG); ++ val &= ~MV_SMI_MISC_CFG_INVERT_MDC_MASK; ++ mv_gop110_smi_write(gop, MV_SMI_MISC_CFG_REG, val); ++ ++ return 0; ++} ++ ++/* Set SMI PHY address */ ++int mv_gop110_smi_phy_addr_cfg(struct gop_hw *gop, int port, int addr) ++{ ++ mv_gop110_smi_write(gop, MV_SMI_PHY_ADDRESS_REG(port), addr); ++ ++ return 0; ++} ++ ++/* print value of unit registers */ ++void mv_gop110_xlg_mac_regs_dump(struct gop_hw *gop, int port) ++{ ++ int timer; ++ char reg_name[16]; ++ ++ mv_gop110_xlg_mac_print(gop, "PORT_MAC_CTRL0", port, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_MAC_CTRL1", port, ++ MV_XLG_PORT_MAC_CTRL1_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_MAC_CTRL2", port, ++ MV_XLG_PORT_MAC_CTRL2_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_STATUS", port, ++ MV_XLG_MAC_PORT_STATUS_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_FIFOS_THRS_CFG", port, ++ MV_XLG_PORT_FIFOS_THRS_CFG_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_MAC_CTRL3", port, ++ MV_XLG_PORT_MAC_CTRL3_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_PER_PRIO_FLOW_CTRL_STATUS", port, ++ MV_XLG_PORT_PER_PRIO_FLOW_CTRL_STATUS_REG); ++ mv_gop110_xlg_mac_print(gop, "DEBUG_BUS_STATUS", port, ++ MV_XLG_DEBUG_BUS_STATUS_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_METAL_FIX", port, ++ MV_XLG_PORT_METAL_FIX_REG); ++ mv_gop110_xlg_mac_print(gop, "XG_MIB_CNTRS_CTRL", port, ++ MV_XLG_MIB_CNTRS_CTRL_REG); ++ for (timer = 0; timer < 8; timer++) { ++ sprintf(reg_name, "CNCCFC_TIMER%d", timer); ++ mv_gop110_xlg_mac_print(gop, reg_name, port, ++ MV_XLG_CNCCFC_TIMERI_REG(timer)); ++ } ++ mv_gop110_xlg_mac_print(gop, "PPFC_CTRL", port, ++ MV_XLG_MAC_PPFC_CTRL_REG); ++ mv_gop110_xlg_mac_print(gop, "FC_DSA_TAG_0", port, ++ MV_XLG_MAC_FC_DSA_TAG_0_REG); ++ mv_gop110_xlg_mac_print(gop, "FC_DSA_TAG_1", port, ++ MV_XLG_MAC_FC_DSA_TAG_1_REG); ++ mv_gop110_xlg_mac_print(gop, "FC_DSA_TAG_2", port, ++ MV_XLG_MAC_FC_DSA_TAG_2_REG); ++ mv_gop110_xlg_mac_print(gop, "FC_DSA_TAG_3", port, ++ MV_XLG_MAC_FC_DSA_TAG_3_REG); ++ mv_gop110_xlg_mac_print(gop, "DIC_BUDGET_COMPENSATION", port, ++ MV_XLG_MAC_DIC_BUDGET_COMPENSATION_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_MAC_CTRL4", port, ++ MV_XLG_PORT_MAC_CTRL4_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_MAC_CTRL5", port, ++ MV_XLG_PORT_MAC_CTRL5_REG); ++ mv_gop110_xlg_mac_print(gop, "EXT_CTRL", port, ++ MV_XLG_MAC_EXT_CTRL_REG); ++ mv_gop110_xlg_mac_print(gop, "MACRO_CTRL", port, ++ MV_XLG_MAC_MACRO_CTRL_REG); ++ mv_gop110_xlg_mac_print(gop, "MACRO_CTRL", port, ++ MV_XLG_MAC_MACRO_CTRL_REG); ++ mv_gop110_xlg_mac_print(gop, "PORT_INT_MASK", port, ++ MV_XLG_INTERRUPT_MASK_REG); ++ mv_gop110_xlg_mac_print(gop, "EXTERNAL_INT_MASK", port, ++ MV_XLG_EXTERNAL_INTERRUPT_MASK_REG); ++} ++EXPORT_SYMBOL(mv_gop110_xlg_mac_regs_dump); ++ ++/* Set the MAC to reset or exit from reset */ ++int mv_gop110_xlg_mac_reset(struct gop_hw *gop, int mac_num, ++ enum mv_reset reset) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ reg_addr = MV_XLG_PORT_MAC_CTRL0_REG; ++ ++ /* read - modify - write */ ++ val = mv_gop110_xlg_mac_read(gop, mac_num, reg_addr); ++ if (reset == RESET) ++ val &= ~MV_XLG_MAC_CTRL0_MACRESETN_MASK; ++ else ++ val |= MV_XLG_MAC_CTRL0_MACRESETN_MASK; ++ mv_gop110_xlg_mac_write(gop, mac_num, reg_addr, val); ++ ++ return 0; ++} ++ ++/* Set the internal mux's to the GMAC mode */ ++void mv_gop110_xlg_mac_1G_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 val; ++ ++ /* In CP110 only GoP 0 has XLG config ++ * In CP115 GoP 0 and GoP 2 has XLG config ++ */ ++ if (!(mac_num == 0) && !(mac_num == 2 && gop->gop_110.cp_version == MV_CP115)) ++ return; ++ ++ val = mv_gop110_xlg_mac_read(gop, mac_num, MV_XLG_PORT_MAC_CTRL4_REG); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_MASK, 1 << ++ MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_OFFS); ++ mv_gop110_xlg_mac_write(gop, mac_num, MV_XLG_PORT_MAC_CTRL4_REG, val); ++} ++ ++/* Set the internal mux's to the required MAC in the GOP */ ++int mv_gop110_xlg_mac_mode_cfg(struct gop_hw *gop, int mac_num, ++ int num_of_act_lanes) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ /* configure 10G MAC mode */ ++ reg_addr = MV_XLG_PORT_MAC_CTRL0_REG; ++ val = mv_gop110_xlg_mac_read(gop, mac_num, reg_addr); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL0_RXFCEN_MASK, ++ (1 << MV_XLG_MAC_CTRL0_RXFCEN_OFFS)); ++ mv_gop110_xlg_mac_write(gop, mac_num, reg_addr, val); ++ ++ reg_addr = MV_XLG_PORT_MAC_CTRL3_REG; ++ val = mv_gop110_xlg_mac_read(gop, mac_num, reg_addr); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL3_MACMODESELECT_MASK, ++ (1 << MV_XLG_MAC_CTRL3_MACMODESELECT_OFFS)); ++ mv_gop110_xlg_mac_write(gop, mac_num, reg_addr, val); ++ ++ reg_addr = MV_XLG_PORT_MAC_CTRL4_REG; ++ ++ /* read - modify - write */ ++ val = mv_gop110_xlg_mac_read(gop, mac_num, reg_addr); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_MASK, 0 << ++ MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_OFFS); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL4_FORWARD_PFC_EN_MASK, 1 << ++ MV_XLG_MAC_CTRL4_FORWARD_PFC_EN_OFFS); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL4_FORWARD_802_3X_FC_EN_MASK, 1 << ++ MV_XLG_MAC_CTRL4_FORWARD_802_3X_FC_EN_OFFS); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL4_EN_IDLE_CHECK_FOR_LINK_MASK, 0 << ++ MV_XLG_MAC_CTRL4_EN_IDLE_CHECK_FOR_LINK_OFFS); ++ mv_gop110_xlg_mac_write(gop, mac_num, reg_addr, val); ++ ++ /* Jumbo frame support - 0x1400*2= 0x2800 bytes */ ++ val = mv_gop110_xlg_mac_read(gop, mac_num, MV_XLG_PORT_MAC_CTRL1_REG); ++ U32_SET_FIELD(val, MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_MASK, 0x1400); ++ mv_gop110_xlg_mac_write(gop, mac_num, MV_XLG_PORT_MAC_CTRL1_REG, val); ++ ++ /* mask all port interrupts */ ++ mv_gop110_xlg_port_link_event_mask(gop, mac_num); ++ ++ /* unmask link change interrupt */ ++ val = mv_gop110_xlg_mac_read(gop, mac_num, MV_XLG_INTERRUPT_MASK_REG); ++ val |= MV_XLG_INTERRUPT_LINK_CHANGE_MASK; ++ val |= 1; /* unmask summary bit */ ++ mv_gop110_xlg_mac_write(gop, mac_num, MV_XLG_INTERRUPT_MASK_REG, val); ++ ++ return 0; ++} ++ ++/* Configure MAC loopback */ ++int mv_gop110_xlg_mac_loopback_cfg(struct gop_hw *gop, int mac_num, ++ enum mv_lb_type type) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ reg_addr = MV_XLG_PORT_MAC_CTRL1_REG; ++ val = mv_gop110_xlg_mac_read(gop, mac_num, reg_addr); ++ switch (type) { ++ case MV_DISABLE_LB: ++ val &= ~MV_XLG_MAC_CTRL1_MACLOOPBACKEN_MASK; ++ val &= ~MV_XLG_MAC_CTRL1_XGMIILOOPBACKEN_MASK; ++ break; ++ case MV_RX_2_TX_LB: ++ val &= ~MV_XLG_MAC_CTRL1_MACLOOPBACKEN_MASK; ++ val |= MV_XLG_MAC_CTRL1_XGMIILOOPBACKEN_MASK; ++ break; ++ case MV_TX_2_RX_LB: ++ val |= MV_XLG_MAC_CTRL1_MACLOOPBACKEN_MASK; ++ val |= MV_XLG_MAC_CTRL1_XGMIILOOPBACKEN_MASK; ++ break; ++ default: ++ return -1; ++ } ++ mv_gop110_xlg_mac_write(gop, mac_num, reg_addr, val); ++ return 0; ++} ++ ++/* Get MAC link status */ ++bool mv_gop110_xlg_mac_link_status_get(struct gop_hw *gop, int mac_num) ++{ ++ if (mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_MAC_PORT_STATUS_REG) & 1) ++ return true; ++ ++ return false; ++} ++ ++/* Enable port and MIB counters update */ ++void mv_gop110_xlg_mac_port_enable(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ reg_val |= MV_XLG_MAC_CTRL0_PORTEN_MASK; ++ reg_val &= ~MV_XLG_MAC_CTRL0_MIBCNTDIS_MASK; ++ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG, reg_val); ++} ++ ++/* Disable port */ ++void mv_gop110_xlg_mac_port_disable(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ /* mask all port interrupts */ ++ mv_gop110_xlg_port_link_event_mask(gop, mac_num); ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ reg_val &= ~MV_XLG_MAC_CTRL0_PORTEN_MASK; ++ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG, reg_val); ++} ++ ++void mv_gop110_xlg_mac_port_periodic_xon_set(struct gop_hw *gop, ++ int mac_num, ++ int enable) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ ++ if (enable) ++ reg_val |= MV_XLG_MAC_CTRL0_PERIODICXONEN_MASK; ++ else ++ reg_val &= ~MV_XLG_MAC_CTRL0_PERIODICXONEN_MASK; ++ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG, reg_val); ++} ++ ++int mv_gop110_xlg_mac_link_status(struct gop_hw *gop, ++ int mac_num, ++ struct mv_port_link_status *pstatus) ++{ ++ u32 reg_val; ++ u32 mac_mode; ++ u32 fc_en; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL3_REG); ++ mac_mode = (reg_val & MV_XLG_MAC_CTRL3_MACMODESELECT_MASK) >> ++ MV_XLG_MAC_CTRL3_MACMODESELECT_OFFS; ++ ++ /* speed and duplex */ ++ switch (mac_mode) { ++ case 0: ++ pstatus->speed = MV_PORT_SPEED_1000; ++ pstatus->duplex = MV_PORT_DUPLEX_AN; ++ break; ++ case 1: ++ pstatus->speed = MV_PORT_SPEED_10000; ++ pstatus->duplex = MV_PORT_DUPLEX_FULL; ++ break; ++ default: ++ return -1; ++ } ++ ++ /* link status */ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_MAC_PORT_STATUS_REG); ++ if (reg_val & MV_XLG_MAC_PORT_STATUS_LINKSTATUS_MASK) ++ pstatus->linkup = 1 /*TRUE*/; ++ else ++ pstatus->linkup = 0 /*FALSE*/; ++ ++ /* flow control status */ ++ fc_en = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ if (reg_val & MV_XLG_MAC_PORT_STATUS_PORTTXPAUSE_MASK) ++ pstatus->tx_fc = MV_PORT_FC_ACTIVE; ++ else if (fc_en & MV_XLG_MAC_CTRL0_TXFCEN_MASK) ++ pstatus->tx_fc = MV_PORT_FC_ENABLE; ++ else ++ pstatus->tx_fc = MV_PORT_FC_DISABLE; ++ ++ if (reg_val & MV_XLG_MAC_PORT_STATUS_PORTRXPAUSE_MASK) ++ pstatus->rx_fc = MV_PORT_FC_ACTIVE; ++ else if (fc_en & MV_XLG_MAC_CTRL0_RXFCEN_MASK) ++ pstatus->rx_fc = MV_PORT_FC_ENABLE; ++ else ++ pstatus->rx_fc = MV_PORT_FC_DISABLE; ++ ++ return 0; ++} ++ ++/* Change maximum receive size of the port */ ++int mv_gop110_xlg_mac_max_rx_size_set(struct gop_hw *gop, int mac_num, ++ int max_rx_size) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL1_REG); ++ reg_val &= ~MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_MASK; ++ reg_val |= (((max_rx_size - MVPP2_MH_SIZE) / 2) << ++ MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_OFFS); ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL1_REG, reg_val); ++ ++ return 0; ++} ++ ++/* Sets "Force Link Pass" and "Do Not Force Link Fail" bits. ++* This function should only be called when the port is disabled. ++* INPUT: ++* int port - port number ++* bool force_link_pass - Force Link Pass ++* bool force_link_fail - Force Link Failure ++* 0, 0 - normal state: detect link via PHY and connector ++* 1, 1 - prohibited state. ++*/ ++int mv_gop110_xlg_mac_force_link_mode_set(struct gop_hw *gop, int mac_num, ++ bool force_link_up, ++ bool force_link_down) ++{ ++ u32 reg_val; ++ ++ /* Can't force link pass and link fail at the same time */ ++ if ((force_link_up) && (force_link_down)) ++ return -EINVAL; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ ++ if (force_link_up) ++ reg_val |= MV_XLG_MAC_CTRL0_FORCELINKPASS_MASK; ++ else ++ reg_val &= ~MV_XLG_MAC_CTRL0_FORCELINKPASS_MASK; ++ ++ if (force_link_down) ++ reg_val |= MV_XLG_MAC_CTRL0_FORCELINKDOWN_MASK; ++ else ++ reg_val &= ~MV_XLG_MAC_CTRL0_FORCELINKDOWN_MASK; ++ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG, reg_val); ++ ++ return 0; ++} ++ ++/* Sets port speed to Auto Negotiation / 1000 / 100 / 10 Mbps. ++* Sets port duplex to Auto Negotiation / Full / Half Duplex. ++*/ ++int mv_gop110_xlg_mac_speed_duplex_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ enum mv_port_duplex duplex) ++{ ++ /* not supported */ ++ return -1; ++} ++ ++/* Gets port speed and duplex */ ++int mv_gop110_xlg_mac_speed_duplex_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed *speed, ++ enum mv_port_duplex *duplex) ++{ ++ /* not supported */ ++ return -1; ++} ++ ++/* Configure the port's Flow Control properties */ ++int mv_gop110_xlg_mac_fc_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc fc) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ ++ switch (fc) { ++ case MV_PORT_FC_DISABLE: ++ reg_val &= ~MV_XLG_MAC_CTRL0_RXFCEN_MASK; ++ reg_val &= ~MV_XLG_MAC_CTRL0_TXFCEN_MASK; ++ break; ++ ++ case MV_PORT_FC_ENABLE: ++ reg_val |= MV_XLG_MAC_CTRL0_RXFCEN_MASK; ++ reg_val |= MV_XLG_MAC_CTRL0_TXFCEN_MASK; ++ break; ++ ++ case MV_PORT_FC_TX_DISABLE: ++ reg_val &= ~MV_XLG_MAC_CTRL0_TXFCEN_MASK; ++ break; ++ ++ case MV_PORT_FC_RX_DISABLE: ++ reg_val &= ~MV_XLG_MAC_CTRL0_RXFCEN_MASK; ++ break; ++ ++ case MV_PORT_FC_TX_ENABLE: ++ reg_val |= MV_XLG_MAC_CTRL0_TXFCEN_MASK; ++ break; ++ ++ case MV_PORT_FC_RX_ENABLE: ++ reg_val |= MV_XLG_MAC_CTRL0_RXFCEN_MASK; ++ break; ++ ++ case MV_PORT_FC_AN_NO: ++ case MV_PORT_FC_AN_SYM: ++ case MV_PORT_FC_AN_ASYM: ++ default: ++ pr_err("XLG MAC: Unexpected FlowControl value %d\n", fc); ++ return -EINVAL; ++ } ++ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG, reg_val); ++ return 0; ++} ++ ++/* Get Flow Control configuration of the port */ ++void mv_gop110_xlg_mac_fc_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc *fc) ++{ ++ u32 reg_val; ++ ++ /* No auto negotiation for flow control */ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ ++ if ((reg_val & MV_XLG_MAC_CTRL0_RXFCEN_MASK) && ++ (reg_val & MV_XLG_MAC_CTRL0_TXFCEN_MASK)) ++ *fc = MV_PORT_FC_ENABLE; ++ else ++ *fc = MV_PORT_FC_DISABLE; ++} ++ ++int mv_gop110_xlg_mac_port_link_speed_fc(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ int force_link_up) ++{ ++ if (force_link_up) { ++ if (mv_gop110_xlg_mac_fc_set(gop, mac_num, ++ MV_PORT_FC_ENABLE)) { ++ pr_err("mv_gop110_xlg_mac_fc_set failed\n"); ++ return -EPERM; ++ } ++ if (mv_gop110_xlg_mac_force_link_mode_set(gop, mac_num, ++ 1, 0)) { ++ pr_err( ++ "mv_gop110_xlg_mac_force_link_mode_set failed\n"); ++ return -EPERM; ++ } ++ } else { ++ if (mv_gop110_xlg_mac_force_link_mode_set(gop, mac_num, ++ 0, 0)) { ++ pr_err( ++ "mv_gop110_xlg_mac_force_link_mode_set failed\n"); ++ return -EPERM; ++ } ++ if (mv_gop110_xlg_mac_fc_set(gop, mac_num, ++ MV_PORT_FC_AN_SYM)) { ++ pr_err("mv_gop110_xlg_mac_fc_set failed\n"); ++ return -EPERM; ++ } ++ } ++ ++ return 0; ++} ++ ++void mv_gop110_xlg_port_link_event_mask(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_EXTERNAL_INTERRUPT_MASK_REG); ++ reg_val &= ~(1 << 1); ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_EXTERNAL_INTERRUPT_MASK_REG, reg_val); ++} ++ ++void mv_gop110_xlg_port_external_event_unmask(struct gop_hw *gop, int mac_num, ++ int bit_2_open) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_EXTERNAL_INTERRUPT_MASK_REG); ++ reg_val |= (1 << bit_2_open); ++ reg_val |= 1; /* unmask summary bit */ ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_EXTERNAL_INTERRUPT_MASK_REG, reg_val); ++} ++ ++void mv_gop110_xlg_port_link_event_clear(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_INTERRUPT_CAUSE_REG); ++} ++ ++void mv_gop110_xlg_2_gig_mac_cfg(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_val; ++ ++ /* relevant only for MAC0 (XLG0 and GMAC0) */ ++ if (mac_num > 0) ++ return; ++ ++ /* configure 1Gig MAC mode */ ++ reg_val = mv_gop110_xlg_mac_read(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL3_REG); ++ U32_SET_FIELD(reg_val, MV_XLG_MAC_CTRL3_MACMODESELECT_MASK, ++ (0 << MV_XLG_MAC_CTRL3_MACMODESELECT_OFFS)); ++ mv_gop110_xlg_mac_write(gop, mac_num, ++ MV_XLG_PORT_MAC_CTRL3_REG, reg_val); ++} ++ ++/* print value of unit registers */ ++void mv_gop110_xpcs_gl_regs_dump(struct gop_hw *gop) ++{ ++ pr_info("\nXPCS Global registers]\n"); ++ mv_gop110_xpcs_global_print(gop, "GLOBAL_CFG_0", ++ MV_XPCS_GLOBAL_CFG_0_REG); ++ mv_gop110_xpcs_global_print(gop, "GLOBAL_CFG_1", ++ MV_XPCS_GLOBAL_CFG_1_REG); ++ mv_gop110_xpcs_global_print(gop, "GLOBAL_FIFO_THR_CFG", ++ MV_XPCS_GLOBAL_FIFO_THR_CFG_REG); ++ mv_gop110_xpcs_global_print(gop, "GLOBAL_MAX_IDLE_CNTR", ++ MV_XPCS_GLOBAL_MAX_IDLE_CNTR_REG); ++ mv_gop110_xpcs_global_print(gop, "GLOBAL_STATUS", ++ MV_XPCS_GLOBAL_STATUS_REG); ++ mv_gop110_xpcs_global_print(gop, "GLOBAL_DESKEW_ERR_CNTR", ++ MV_XPCS_GLOBAL_DESKEW_ERR_CNTR_REG); ++ mv_gop110_xpcs_global_print(gop, "TX_PCKTS_CNTR_LSB", ++ MV_XPCS_TX_PCKTS_CNTR_LSB_REG); ++ mv_gop110_xpcs_global_print(gop, "TX_PCKTS_CNTR_MSB", ++ MV_XPCS_TX_PCKTS_CNTR_MSB_REG); ++} ++EXPORT_SYMBOL(mv_gop110_xpcs_gl_regs_dump); ++ ++/* print value of unit registers */ ++void mv_gop110_xpcs_lane_regs_dump(struct gop_hw *gop, int lane) ++{ ++ pr_info("\nXPCS Lane #%d registers]\n", lane); ++ mv_gop110_xpcs_lane_print(gop, "LANE_CFG_0", lane, ++ MV_XPCS_LANE_CFG_0_REG); ++ mv_gop110_xpcs_lane_print(gop, "LANE_CFG_1", lane, ++ MV_XPCS_LANE_CFG_1_REG); ++ mv_gop110_xpcs_lane_print(gop, "LANE_STATUS", lane, ++ MV_XPCS_LANE_STATUS_REG); ++ mv_gop110_xpcs_lane_print(gop, "SYMBOL_ERR_CNTR", lane, ++ MV_XPCS_SYMBOL_ERR_CNTR_REG); ++ mv_gop110_xpcs_lane_print(gop, "DISPARITY_ERR_CNTR", lane, ++ MV_XPCS_DISPARITY_ERR_CNTR_REG); ++ mv_gop110_xpcs_lane_print(gop, "PRBS_ERR_CNTR", lane, ++ MV_XPCS_PRBS_ERR_CNTR_REG); ++ mv_gop110_xpcs_lane_print(gop, "RX_PCKTS_CNTR_LSB", lane, ++ MV_XPCS_RX_PCKTS_CNTR_LSB_REG); ++ mv_gop110_xpcs_lane_print(gop, "RX_PCKTS_CNTR_MSB", lane, ++ MV_XPCS_RX_PCKTS_CNTR_MSB_REG); ++ mv_gop110_xpcs_lane_print(gop, "RX_BAD_PCKTS_CNTR_LSB", lane, ++ MV_XPCS_RX_BAD_PCKTS_CNTR_LSB_REG); ++ mv_gop110_xpcs_lane_print(gop, "RX_BAD_PCKTS_CNTR_MSB", lane, ++ MV_XPCS_RX_BAD_PCKTS_CNTR_MSB_REG); ++ mv_gop110_xpcs_lane_print(gop, "CYCLIC_DATA_0", lane, ++ MV_XPCS_CYCLIC_DATA_0_REG); ++ mv_gop110_xpcs_lane_print(gop, "CYCLIC_DATA_1", lane, ++ MV_XPCS_CYCLIC_DATA_1_REG); ++ mv_gop110_xpcs_lane_print(gop, "CYCLIC_DATA_2", lane, ++ MV_XPCS_CYCLIC_DATA_2_REG); ++ mv_gop110_xpcs_lane_print(gop, "CYCLIC_DATA_3", lane, ++ MV_XPCS_CYCLIC_DATA_3_REG); ++} ++EXPORT_SYMBOL(mv_gop110_xpcs_lane_regs_dump); ++ ++/* Set PCS to reset or exit from reset */ ++int mv_gop110_xpcs_reset(struct gop_hw *gop, enum mv_reset reset) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ reg_addr = MV_XPCS_GLOBAL_CFG_0_REG; ++ ++ /* read - modify - write */ ++ val = mv_gop110_xpcs_global_read(gop, reg_addr); ++ if (reset == RESET) ++ val &= ~MV_XPCS_GLOBAL_CFG_0_PCSRESET_MASK; ++ else ++ val |= MV_XPCS_GLOBAL_CFG_0_PCSRESET_MASK; ++ mv_gop110_xpcs_global_write(gop, reg_addr, val); ++ ++ return 0; ++} ++ ++/* Set the internal mux's to the required PCS in the PI */ ++int mv_gop110_xpcs_mode(struct gop_hw *gop, int num_of_lanes) ++{ ++ u32 reg_addr; ++ u32 val; ++ int lane; ++ ++ switch (num_of_lanes) { ++ case 1: ++ lane = 0; ++ break; ++ case 2: ++ lane = 1; ++ break; ++ case 4: ++ lane = 2; ++ break; ++ default: ++ return -1; ++ } ++ ++ /* configure XG MAC mode */ ++ reg_addr = MV_XPCS_GLOBAL_CFG_0_REG; ++ val = mv_gop110_xpcs_global_read(gop, reg_addr); ++ val &= ~MV_XPCS_GLOBAL_CFG_0_PCSMODE_MASK; ++ U32_SET_FIELD(val, MV_XPCS_GLOBAL_CFG_0_PCSMODE_MASK, 0); ++ U32_SET_FIELD(val, MV_XPCS_GLOBAL_CFG_0_LANEACTIVE_MASK, (2 * lane) << ++ MV_XPCS_GLOBAL_CFG_0_LANEACTIVE_OFFS); ++ mv_gop110_xpcs_global_write(gop, reg_addr, val); ++ ++ return 0; ++} ++ ++int mv_gop110_mpcs_mode(struct gop_hw *gop, int mac_num) ++{ ++ u32 reg_addr; ++ u32 val; ++ ++ /* configure PCS40G COMMON CONTROL */ ++ reg_addr = PCS40G_COMMON_CONTROL; ++ val = mv_gop110_mpcs_global_read(gop, mac_num, reg_addr); ++ U32_SET_FIELD(val, FORWARD_ERROR_CORRECTION_MASK, ++ 0 << FORWARD_ERROR_CORRECTION_OFFSET); ++ ++ mv_gop110_mpcs_global_write(gop, mac_num, reg_addr, val); ++ ++ /* configure PCS CLOCK RESET */ ++ reg_addr = PCS_CLOCK_RESET; ++ val = mv_gop110_mpcs_global_read(gop, mac_num, reg_addr); ++ U32_SET_FIELD(val, CLK_DIVISION_RATIO_MASK, ++ 1 << CLK_DIVISION_RATIO_OFFSET); ++ ++ mv_gop110_mpcs_global_write(gop, mac_num, reg_addr, val); ++ ++ U32_SET_FIELD(val, CLK_DIV_PHASE_SET_MASK, ++ 0 << CLK_DIV_PHASE_SET_OFFSET); ++ U32_SET_FIELD(val, MAC_CLK_RESET_MASK, 1 << MAC_CLK_RESET_OFFSET); ++ U32_SET_FIELD(val, RX_SD_CLK_RESET_MASK, 1 << RX_SD_CLK_RESET_OFFSET); ++ U32_SET_FIELD(val, TX_SD_CLK_RESET_MASK, 1 << TX_SD_CLK_RESET_OFFSET); ++ ++ mv_gop110_mpcs_global_write(gop, mac_num, reg_addr, val); ++ ++ return 0; ++} ++ ++void mv_gop110_mpcs_clock_reset(struct gop_hw *gop, int mac_num, enum mv_reset reset) ++{ ++ u32 val, reg_addr, val1; ++ ++ if (reset == RESET) ++ val1 = 0x0; ++ else ++ val1 = 0x1; ++ ++ /* configure PCS CLOCK RESET */ ++ reg_addr = PCS_CLOCK_RESET; ++ val = mv_gop110_mpcs_global_read(gop, mac_num, reg_addr); ++ ++ U32_SET_FIELD(val, MAC_CLK_RESET_MASK, val1 << MAC_CLK_RESET_OFFSET); ++ U32_SET_FIELD(val, RX_SD_CLK_RESET_MASK, val1 << RX_SD_CLK_RESET_OFFSET); ++ U32_SET_FIELD(val, TX_SD_CLK_RESET_MASK, val1 << TX_SD_CLK_RESET_OFFSET); ++ ++ mv_gop110_mpcs_global_write(gop, mac_num, reg_addr, val); ++} ++ ++u64 mv_gop110_mib_read64(struct gop_hw *gop, int port, unsigned int offset) ++{ ++ u64 val, val2; ++ ++ val = mv_gop110_xmib_mac_read(gop, port, offset); ++ if (offset == MV_MIB_GOOD_OCTETS_RECEIVED_LOW || ++ offset == MV_MIB_GOOD_OCTETS_SENT_LOW) { ++ val2 = mv_gop110_xmib_mac_read(gop, port, offset + 4); ++ val += (val2 << 32); ++ } ++ ++ return val; ++} ++ ++static void mv_gop110_mib_print(struct gop_hw *gop, int port, u32 offset, ++ char *mib_name) ++{ ++ u64 val; ++ ++ val = mv_gop110_mib_read64(gop, port, offset); ++ pr_info(" %-32s: 0x%02x = %lld\n", mib_name, offset, val); ++} ++ ++void mv_gop110_mib_counters_show(struct gop_hw *gop, int port) ++{ ++ pr_info("\n[Rx]\n"); ++ mv_gop110_mib_print(gop, port, MV_MIB_GOOD_OCTETS_RECEIVED_LOW, ++ "GOOD_OCTETS_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_BAD_OCTETS_RECEIVED, ++ "BAD_OCTETS_RECEIVED"); ++ ++ mv_gop110_mib_print(gop, port, MV_MIB_UNICAST_FRAMES_RECEIVED, ++ "UNCAST_FRAMES_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_BROADCAST_FRAMES_RECEIVED, ++ "BROADCAST_FRAMES_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_MULTICAST_FRAMES_RECEIVED, ++ "MULTICAST_FRAMES_RECEIVED"); ++ ++ pr_info("\n[RMON]\n"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAMES_64_OCTETS, ++ "FRAMES_64_OCTETS"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAMES_65_TO_127_OCTETS, ++ "FRAMES_65_TO_127_OCTETS"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAMES_128_TO_255_OCTETS, ++ "FRAMES_128_TO_255_OCTETS"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAMES_256_TO_511_OCTETS, ++ "FRAMES_256_TO_511_OCTETS"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAMES_512_TO_1023_OCTETS, ++ "FRAMES_512_TO_1023_OCTETS"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAMES_1024_TO_MAX_OCTETS, ++ "FRAMES_1024_TO_MAX_OCTETS"); ++ ++ pr_info("\n[Tx]\n"); ++ mv_gop110_mib_print(gop, port, MV_MIB_GOOD_OCTETS_SENT_LOW, ++ "GOOD_OCTETS_SENT"); ++ mv_gop110_mib_print(gop, port, MV_MIB_UNICAST_FRAMES_SENT, ++ "UNICAST_FRAMES_SENT"); ++ mv_gop110_mib_print(gop, port, MV_MIB_MULTICAST_FRAMES_SENT, ++ "MULTICAST_FRAMES_SENT"); ++ mv_gop110_mib_print(gop, port, MV_MIB_BROADCAST_FRAMES_SENT, ++ "BROADCAST_FRAMES_SENT"); ++ mv_gop110_mib_print(gop, port, MV_MIB_CRC_ERRORS_SENT, ++ "CRC_ERRORS_SENT"); ++ ++ pr_info("\n[FC control]\n"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FC_RECEIVED, ++ "FC_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FC_SENT, ++ "FC_SENT"); ++ ++ pr_info("\n[Errors]\n"); ++ mv_gop110_mib_print(gop, port, MV_MIB_RX_FIFO_OVERRUN, ++ "RX_FIFO_OVERRUN"); ++ mv_gop110_mib_print(gop, port, MV_MIB_UNDERSIZE_RECEIVED, ++ "UNDERSIZE_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_FRAGMENTS_RECEIVED, ++ "FRAGMENTS_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_OVERSIZE_RECEIVED, ++ "OVERSIZE_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_JABBER_RECEIVED, ++ "JABBER_RECEIVED"); ++ mv_gop110_mib_print(gop, port, MV_MIB_MAC_RECEIVE_ERROR, ++ "MAC_RECEIVE_ERROR"); ++ mv_gop110_mib_print(gop, port, MV_MIB_BAD_CRC_EVENT, ++ "BAD_CRC_EVENT"); ++ mv_gop110_mib_print(gop, port, MV_MIB_COLLISION, ++ "COLLISION"); ++ /* This counter must be read last. Read it clear all the counters */ ++ mv_gop110_mib_print(gop, port, MV_MIB_LATE_COLLISION, ++ "LATE_COLLISION"); ++} ++EXPORT_SYMBOL(mv_gop110_mib_counters_show); ++ ++void mv_gop110_mib_counters_stat_update(struct gop_hw *gop, int port, struct gop_stat *gop_statistics) ++{ ++ struct mv_pp2x_hw *hw; ++ struct mv_pp2x *pp2; ++ struct mv_pp2x_port *pp_port; ++ unsigned long flags; ++ u64 val; ++ ++ hw = container_of(gop, struct mv_pp2x_hw, gop); ++ pp2 = container_of(hw, struct mv_pp2x, hw); ++ pp_port = mv_pp2x_port_struct_get_by_gop_index(pp2, port); ++ spin_lock_irqsave(&pp_port->mac_data.stats_spinlock, flags); ++ ++ gop_statistics->rx_byte += mv_gop110_mib_read64(gop, port, ++ MV_MIB_GOOD_OCTETS_RECEIVED_LOW); ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_UNICAST_FRAMES_RECEIVED); ++ gop_statistics->rx_unicast += val; ++ gop_statistics->rx_frames += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_BROADCAST_FRAMES_RECEIVED); ++ gop_statistics->rx_bcast += val; ++ gop_statistics->rx_frames += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_MULTICAST_FRAMES_RECEIVED); ++ gop_statistics->rx_mcast += val; ++ gop_statistics->rx_frames += val; ++ ++ gop_statistics->frames_64 += mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_64_OCTETS); ++ ++ gop_statistics->frames_65_to_127 += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FRAMES_65_TO_127_OCTETS); ++ ++ gop_statistics->frames_128_to_255 += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FRAMES_128_TO_255_OCTETS); ++ ++ gop_statistics->frames_256_to_511 += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FRAMES_256_TO_511_OCTETS); ++ ++ gop_statistics->frames_512_to_1023 += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FRAMES_512_TO_1023_OCTETS); ++ ++ gop_statistics->frames_1024_to_max += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FRAMES_1024_TO_MAX_OCTETS); ++ ++ gop_statistics->tx_byte += mv_gop110_mib_read64(gop, port, ++ MV_MIB_GOOD_OCTETS_SENT_LOW); ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_UNICAST_FRAMES_SENT); ++ gop_statistics->tx_unicast += val; ++ gop_statistics->tx_frames += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_MULTICAST_FRAMES_SENT); ++ gop_statistics->tx_mcast += val; ++ gop_statistics->tx_frames += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_BROADCAST_FRAMES_SENT); ++ gop_statistics->tx_bcast += val; ++ gop_statistics->tx_frames += val; ++ ++ gop_statistics->tx_crc_sent += mv_gop110_mib_read64(gop, port, ++ MV_MIB_CRC_ERRORS_SENT); ++ ++ gop_statistics->rx_pause += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FC_RECEIVED); ++ ++ gop_statistics->tx_pause += mv_gop110_mib_read64(gop, port, ++ MV_MIB_FC_SENT); ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_RX_FIFO_OVERRUN); ++ gop_statistics->rx_mac_overrun += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_UNDERSIZE_RECEIVED); ++ gop_statistics->rx_runt += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_FRAGMENTS_RECEIVED); ++ gop_statistics->rx_fragments_err += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_OVERSIZE_RECEIVED); ++ gop_statistics->rx_giant += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_JABBER_RECEIVED); ++ gop_statistics->rx_jabber += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_MAC_RECEIVE_ERROR); ++ gop_statistics->rx_mac_err += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ val = mv_gop110_mib_read64(gop, port, MV_MIB_BAD_CRC_EVENT); ++ gop_statistics->rx_crc += val; ++ gop_statistics->rx_sw_drop += val; ++ gop_statistics->rx_total_err += val; ++ ++ gop_statistics->collision += mv_gop110_mib_read64(gop, port, ++ MV_MIB_COLLISION); ++ ++ /* This counter must be read last. Read it clear all the counters */ ++ gop_statistics->late_collision += mv_gop110_mib_read64(gop, port, ++ MV_MIB_LATE_COLLISION); ++ ++ spin_unlock_irqrestore(&pp_port->mac_data.stats_spinlock, flags); ++} ++EXPORT_SYMBOL(mv_gop110_mib_counters_stat_update); ++ ++void mv_gop110_mib_counters_clear(struct gop_hw *gop, int port) ++{ ++ mv_gop110_mib_read64(gop, port, MV_MIB_GOOD_OCTETS_RECEIVED_LOW); ++ mv_gop110_mib_read64(gop, port, MV_MIB_UNICAST_FRAMES_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_BROADCAST_FRAMES_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_MULTICAST_FRAMES_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_64_OCTETS); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_65_TO_127_OCTETS); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_128_TO_255_OCTETS); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_256_TO_511_OCTETS); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_512_TO_1023_OCTETS); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAMES_1024_TO_MAX_OCTETS); ++ mv_gop110_mib_read64(gop, port, MV_MIB_GOOD_OCTETS_SENT_LOW); ++ mv_gop110_mib_read64(gop, port, MV_MIB_UNICAST_FRAMES_SENT); ++ mv_gop110_mib_read64(gop, port, MV_MIB_MULTICAST_FRAMES_SENT); ++ mv_gop110_mib_read64(gop, port, MV_MIB_BROADCAST_FRAMES_SENT); ++ mv_gop110_mib_read64(gop, port, MV_MIB_CRC_ERRORS_SENT); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FC_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FC_SENT); ++ mv_gop110_mib_read64(gop, port, MV_MIB_RX_FIFO_OVERRUN); ++ mv_gop110_mib_read64(gop, port, MV_MIB_UNDERSIZE_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_FRAGMENTS_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_OVERSIZE_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_JABBER_RECEIVED); ++ mv_gop110_mib_read64(gop, port, MV_MIB_MAC_RECEIVE_ERROR); ++ mv_gop110_mib_read64(gop, port, MV_MIB_BAD_CRC_EVENT); ++ mv_gop110_mib_read64(gop, port, MV_MIB_COLLISION); ++ ++ /* This counter must be read last. Read it clear all read counters. */ ++ mv_gop110_mib_read64(gop, port, MV_MIB_LATE_COLLISION); ++} ++ ++void mv_gop110_netc_active_port(struct gop_hw *gop, u32 port, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_PORTS_CONTROL_1); ++ reg &= ~(NETC_PORTS_ACTIVE_MASK(port)); ++ ++ val <<= NETC_PORTS_ACTIVE_OFFSET(port); ++ val &= NETC_PORTS_ACTIVE_MASK(port); ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_PORTS_CONTROL_1, reg); ++} ++ ++static void mv_gop110_netc_xaui_enable(struct gop_hw *gop, u32 port, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, SD1_CONTROL_1_REG); ++ reg &= ~SD1_CONTROL_XAUI_EN_MASK; ++ ++ val <<= SD1_CONTROL_XAUI_EN_OFFSET; ++ val &= SD1_CONTROL_XAUI_EN_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, SD1_CONTROL_1_REG, reg); ++} ++ ++static void mv_gop110_netc_rxaui0_enable(struct gop_hw *gop, u32 port, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, SD1_CONTROL_1_REG); ++ reg &= ~SD1_CONTROL_RXAUI0_L23_EN_MASK; ++ ++ val <<= SD1_CONTROL_RXAUI0_L23_EN_OFFSET; ++ val &= SD1_CONTROL_RXAUI0_L23_EN_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, SD1_CONTROL_1_REG, reg); ++} ++ ++static void mv_gop110_netc_rxaui1_enable(struct gop_hw *gop, u32 port, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, SD1_CONTROL_1_REG); ++ reg &= ~SD1_CONTROL_RXAUI1_L45_EN_MASK; ++ ++ val <<= SD1_CONTROL_RXAUI1_L45_EN_OFFSET; ++ val &= SD1_CONTROL_RXAUI1_L45_EN_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, SD1_CONTROL_1_REG, reg); ++} ++ ++static void mv_gop110_netc_mii_mode(struct gop_hw *gop, u32 port, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_CONTROL_0); ++ reg &= ~NETC_GBE_PORT1_MII_MODE_MASK; ++ ++ val <<= NETC_GBE_PORT1_MII_MODE_OFFSET; ++ val &= NETC_GBE_PORT1_MII_MODE_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_CONTROL_0, reg); ++} ++ ++static void mv_gop110_netc_gop_reset(struct gop_hw *gop, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_GOP_SOFT_RESET_1_REG); ++ reg &= ~NETC_GOP_SOFT_RESET_MASK; ++ ++ val <<= NETC_GOP_SOFT_RESET_OFFSET; ++ val &= NETC_GOP_SOFT_RESET_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_GOP_SOFT_RESET_1_REG, reg); ++} ++ ++static void mv_gop110_netc_gop_clock_logic_set(struct gop_hw *gop, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_PORTS_CONTROL_0); ++ reg &= ~NETC_CLK_DIV_PHASE_MASK; ++ ++ val <<= NETC_CLK_DIV_PHASE_OFFSET; ++ val &= NETC_CLK_DIV_PHASE_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_PORTS_CONTROL_0, reg); ++} ++ ++static void mv_gop110_netc_port_rf_reset(struct gop_hw *gop, u32 port, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_PORTS_CONTROL_1); ++ reg &= ~(NETC_PORT_GIG_RF_RESET_MASK(port)); ++ ++ val <<= NETC_PORT_GIG_RF_RESET_OFFSET(port); ++ val &= NETC_PORT_GIG_RF_RESET_MASK(port); ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_PORTS_CONTROL_1, reg); ++} ++ ++static void mv_gop110_netc_gbe_sgmii_mode_select(struct gop_hw *gop, u32 port, ++ u32 val) ++{ ++ u32 reg, mask, offset; ++ ++ if (port == 2) { ++ mask = NETC_GBE_PORT0_SGMII_MODE_MASK; ++ offset = NETC_GBE_PORT0_SGMII_MODE_OFFSET; ++ } else { ++ mask = NETC_GBE_PORT1_SGMII_MODE_MASK; ++ offset = NETC_GBE_PORT1_SGMII_MODE_OFFSET; ++ } ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_CONTROL_0); ++ reg &= ~mask; ++ ++ val <<= offset; ++ val &= mask; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_CONTROL_0, reg); ++} ++ ++static void mv_gop110_netc_bus_width_select(struct gop_hw *gop, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_PORTS_CONTROL_0); ++ reg &= ~NETC_BUS_WIDTH_SELECT_MASK; ++ ++ val <<= NETC_BUS_WIDTH_SELECT_OFFSET; ++ val &= NETC_BUS_WIDTH_SELECT_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_PORTS_CONTROL_0, reg); ++} ++ ++static void mv_gop110_netc_sample_stages_timing(struct gop_hw *gop, u32 val) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_PORTS_CONTROL_0); ++ reg &= ~NETC_GIG_RX_DATA_SAMPLE_MASK; ++ ++ val <<= NETC_GIG_RX_DATA_SAMPLE_OFFSET; ++ val &= NETC_GIG_RX_DATA_SAMPLE_MASK; ++ ++ reg |= val; ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_PORTS_CONTROL_0, reg); ++} ++ ++static void mv_gop110_netc_mac_to_xgmii(struct gop_hw *gop, u32 port, ++ enum mv_netc_phase phase) ++{ ++ switch (phase) { ++ case MV_NETC_FIRST_PHASE: ++ /* Set Bus Width to HB mode = 1 */ ++ mv_gop110_netc_bus_width_select(gop, 1); ++ /* Select RGMII mode */ ++ mv_gop110_netc_gbe_sgmii_mode_select(gop, port, ++ MV_NETC_GBE_XMII); ++ break; ++ case MV_NETC_SECOND_PHASE: ++ /* De-assert the relevant port HB reset */ ++ mv_gop110_netc_port_rf_reset(gop, port, 1); ++ break; ++ } ++} ++ ++static void mv_gop110_netc_mac_to_sgmii(struct gop_hw *gop, u32 port, ++ enum mv_netc_phase phase) ++{ ++ switch (phase) { ++ case MV_NETC_FIRST_PHASE: ++ /* Set Bus Width to HB mode = 1 */ ++ mv_gop110_netc_bus_width_select(gop, 1); ++ /* Select SGMII mode */ ++ if (port >= 1) ++ mv_gop110_netc_gbe_sgmii_mode_select(gop, port, ++ MV_NETC_GBE_SGMII); ++ ++ /* Configure the sample stages */ ++ mv_gop110_netc_sample_stages_timing(gop, 0); ++ /* Configure the ComPhy Selector */ ++ /* mv_gop110_netc_com_phy_selector_config(netComplex); */ ++ break; ++ case MV_NETC_SECOND_PHASE: ++ /* De-assert the relevant port HB reset */ ++ mv_gop110_netc_port_rf_reset(gop, port, 1); ++ break; ++ } ++} ++ ++static void mv_gop110_netc_mac_to_rxaui(struct gop_hw *gop, u32 port, ++ enum mv_netc_phase phase, ++ enum mv_netc_lanes lanes) ++{ ++ /* Currently only RXAUI0 supported */ ++ if (port != 0) ++ return; ++ ++ switch (phase) { ++ case MV_NETC_FIRST_PHASE: ++ /* RXAUI Serdes/s Clock alignment */ ++ if (lanes == MV_NETC_LANE_23) ++ mv_gop110_netc_rxaui0_enable(gop, port, 1); ++ else ++ mv_gop110_netc_rxaui1_enable(gop, port, 1); ++ break; ++ case MV_NETC_SECOND_PHASE: ++ /* De-assert the relevant port HB reset */ ++ mv_gop110_netc_port_rf_reset(gop, port, 1); ++ break; ++ } ++} ++ ++static void mv_gop110_netc_mac_to_xaui(struct gop_hw *gop, u32 port, ++ enum mv_netc_phase phase) ++{ ++ switch (phase) { ++ case MV_NETC_FIRST_PHASE: ++ /* RXAUI Serdes/s Clock alignment */ ++ mv_gop110_netc_xaui_enable(gop, port, 1); ++ break; ++ case MV_NETC_SECOND_PHASE: ++ /* De-assert the relevant port HB reset */ ++ mv_gop110_netc_port_rf_reset(gop, port, 1); ++ break; ++ } ++} ++ ++int mv_gop110_netc_init(struct gop_hw *gop, ++ u32 net_comp_config, enum mv_netc_phase phase) ++{ ++ u32 c = net_comp_config; ++ ++ if (c & MV_NETC_GE_MAC0_RXAUI_L23) ++ mv_gop110_netc_mac_to_rxaui(gop, 0, phase, MV_NETC_LANE_23); ++ ++ if (c & MV_NETC_GE_MAC0_RXAUI_L45) ++ mv_gop110_netc_mac_to_rxaui(gop, 0, phase, MV_NETC_LANE_45); ++ ++ if (c & MV_NETC_GE_MAC0_XAUI) ++ mv_gop110_netc_mac_to_xaui(gop, 0, phase); ++ ++ if (c & MV_NETC_GE_MAC2_SGMII) ++ mv_gop110_netc_mac_to_sgmii(gop, 2, phase); ++ else if (c & MV_NETC_GE_MAC2_RGMII) ++ mv_gop110_netc_mac_to_xgmii(gop, 2, phase); ++ ++ if (c & MV_NETC_GE_MAC3_SGMII) { ++ mv_gop110_netc_mac_to_sgmii(gop, 3, phase); ++ } else { ++ mv_gop110_netc_mac_to_xgmii(gop, 3, phase); ++ if (c & MV_NETC_GE_MAC3_RGMII) ++ mv_gop110_netc_mii_mode(gop, 3, MV_NETC_GBE_RGMII); ++ else ++ mv_gop110_netc_mii_mode(gop, 3, MV_NETC_GBE_MII); ++ } ++ ++ /* Activate gop ports 0, 2, 3 */ ++ mv_gop110_netc_active_port(gop, 0, 1); ++ mv_gop110_netc_active_port(gop, 2, 1); ++ mv_gop110_netc_active_port(gop, 3, 1); ++ ++ if (phase == MV_NETC_SECOND_PHASE) { ++ /* Enable the GOP internal clock logic */ ++ mv_gop110_netc_gop_clock_logic_set(gop, 1); ++ /* De-assert GOP unit reset */ ++ mv_gop110_netc_gop_reset(gop, 1); ++ } ++ return 0; ++} ++ ++void mv_gop110_netc_reconfig(struct mv_pp2x *priv, int port) ++{ ++ u32 net_comp_config = mvp_pp2x_gop110_netc_cfg_create(priv); ++ ++ switch (port) { ++ case MV_GOP_PORT0: ++ switch (net_comp_config) { ++ case MV_NETC_GE_MAC0_RXAUI_L23: ++ mv_gop110_netc_rxaui0_enable(&priv->hw.gop, port, 1); ++ break; ++ case MV_NETC_GE_MAC0_RXAUI_L45: ++ mv_gop110_netc_rxaui1_enable(&priv->hw.gop, port, 1); ++ break; ++ case MV_NETC_GE_MAC0_XAUI: ++ mv_gop110_netc_xaui_enable(&priv->hw.gop, port, 1); ++ break; ++ } ++ break; ++ case MV_GOP_PORT1: ++ pr_err("%s: Wrong gop port (%d)\n", __func__, port); ++ return; ++ case MV_GOP_PORT2: ++ if (net_comp_config & MV_NETC_GE_MAC2_SGMII) ++ mv_gop110_netc_gbe_sgmii_mode_select(&priv->hw.gop, port, ++ MV_NETC_GBE_SGMII); ++ else if (net_comp_config & MV_NETC_GE_MAC2_RGMII) ++ mv_gop110_netc_gbe_sgmii_mode_select(&priv->hw.gop, port, ++ MV_NETC_GBE_XMII); ++ break; ++ case MV_GOP_PORT3: ++ if (net_comp_config & MV_NETC_GE_MAC3_SGMII) { ++ mv_gop110_netc_gbe_sgmii_mode_select(&priv->hw.gop, port, ++ MV_NETC_GBE_SGMII); ++ } else { ++ mv_gop110_netc_gbe_sgmii_mode_select(&priv->hw.gop, port, ++ MV_NETC_GBE_XMII); ++ if (net_comp_config & MV_NETC_GE_MAC3_RGMII) ++ mv_gop110_netc_mii_mode(&priv->hw.gop, 3, MV_NETC_GBE_RGMII); ++ else ++ mv_gop110_netc_mii_mode(&priv->hw.gop, 3, MV_NETC_GBE_MII); ++ } ++ } ++ ++ mv_gop110_netc_port_rf_reset(&priv->hw.gop, port, 1); ++ mv_gop110_netc_active_port(&priv->hw.gop, port, 1); ++} ++ ++void mv_gop110_netc_xon_set(struct gop_hw *gop, enum mv_gop_port port, bool en) ++{ ++ u32 reg; ++ ++ reg = mv_gop110_rfu1_read(gop, MV_NETCOMP_PORTS_CONTROL_0); ++ ++ switch (port) { ++ case MV_GOP_PORT0: ++ U32_SET_FIELD(reg, NETC_PORT0_PAUSE_MASK, ++ en << NETC_PORT0_PAUSE_OFFSET); ++ break; ++ case MV_GOP_PORT1: ++ pr_err("%s: Wrong gop port (%d)\n", __func__, port); ++ break; ++ case MV_GOP_PORT2: ++ U32_SET_FIELD(reg, NETC_PORT2_PAUSE_MASK, ++ en << NETC_PORT2_PAUSE_OFFSET); ++ break; ++ case MV_GOP_PORT3: ++ U32_SET_FIELD(reg, NETC_PORT3_PAUSE_MASK, ++ en << NETC_PORT3_PAUSE_OFFSET); ++ break; ++ } ++ ++ mv_gop110_rfu1_write(gop, MV_NETCOMP_PORTS_CONTROL_0, reg); ++} ++EXPORT_SYMBOL(mv_gop110_netc_xon_set); ++ ++void mv_gop110_fca_send_periodic(struct gop_hw *gop, int mac_num, bool en) ++{ ++ int val; ++ ++ val = mv_gop110_fca_read(gop, mac_num, FCA_CONTROL_REG); ++ ++ U32_SET_FIELD(val, FCA_PORT_TYPE_MASK, ++ FCA_PORT_TYPE_B << FCA_PORT_TYPE_OFFSET); ++ U32_SET_FIELD(val, FCA_SEND_PERIODIC_MASK, ++ en << FCA_SEND_PERIODIC_OFFSET); ++ mv_gop110_fca_write(gop, mac_num, FCA_CONTROL_REG, val); ++} ++ ++void mv_gop110_fca_enable_periodic(struct gop_hw *gop, int mac_num, bool en) ++{ ++ int val; ++ ++ val = mv_gop110_fca_read(gop, mac_num, FCA_CONTROL_REG); ++ ++ U32_SET_FIELD(val, FCA_ENABLE_PERIODIC_MASK, ++ en << FCA_ENABLE_PERIODIC_OFFSET); ++ mv_gop110_fca_write(gop, mac_num, FCA_CONTROL_REG, val); ++} ++ ++void mv_gop110_fca_set_timer(struct gop_hw *gop, int mac_num, u32 lsb, u32 msb) ++{ ++ mv_gop110_fca_write(gop, mac_num, PERIODIC_COUNTER_LSB_REG, lsb); ++ mv_gop110_fca_write(gop, mac_num, PERIODIC_COUNTER_MSB_REG, msb); ++} ++ ++void mv_gop110_fca_set_periodic_timer(struct gop_hw *gop, int mac_num, u64 timer) ++{ ++ u32 lsb, msb; ++ ++ mv_gop110_fca_enable_periodic(gop, mac_num, false); ++ ++ lsb = lower_32_bits(timer); ++ msb = upper_32_bits(timer); ++ ++ mv_gop110_fca_set_timer(gop, mac_num, lsb, msb); ++ ++ mv_gop110_fca_enable_periodic(gop, mac_num, true); ++} ++EXPORT_SYMBOL(mv_gop110_fca_set_periodic_timer); ++ ++void mv_gop110_fca_tx_enable(struct gop_hw *gop, int mac_num, bool en) ++{ ++ int val; ++ ++ val = mv_gop110_fca_read(gop, mac_num, FCA_CONTROL_REG); ++ ++ U32_SET_FIELD(val, FCA_PORT_TYPE_MASK, ++ FCA_PORT_TYPE_B << FCA_PORT_TYPE_OFFSET); ++ U32_SET_FIELD(val, FCA_BYPASS_MASK, ++ en << FCA_BYPASS_OFFSET); ++ mv_gop110_fca_write(gop, mac_num, FCA_CONTROL_REG, val); ++} ++ ++bool mv_gop110_check_fca_tx_state(struct gop_hw *gop, int mac_num) ++{ ++ int val; ++ ++ val = mv_gop110_fca_read(gop, mac_num, FCA_CONTROL_REG); ++ ++ if (val & FCA_BYPASS_MASK) ++ return false; ++ ++ return true; ++} ++ ++/* Register dump for ethtool */ ++void mv_gop110_gmac_registers_dump(struct mv_pp2x_port *port, u32 *regs_buff) ++{ ++ int i; ++ int index = 0; ++ ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_CTRL0_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_CTRL1_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_CTRL2_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_AUTO_NEG_CFG_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_STATUS0_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_FIFO_CFG_0_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_FIFO_CFG_1_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_INTERRUPT_CAUSE_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_INTERRUPT_MASK_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_SERDES_CFG0_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_SERDES_CFG1_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_SERDES_CFG2_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_SERDES_CFG3_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_PRBS_STATUS_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_PRBS_ERR_CNTR_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_STATUS1_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_CTRL3_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_QSGMII_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_QSGMII_STATUS_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_QSGMII_PRBS_CNTR_REG); ++ for (i = 0; i < 8; i++) { ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_CCFC_PORT_SPEED_TIMER_REG(i)); ++ } ++ for (i = 0; i < 4; i++) { ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_FC_DSA_TAG_REG(i)); ++ } ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_CTRL4_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PORT_SERIAL_PARAM_1_CFG_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_INTERRUPT_SUM_CAUSE_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_INTERRUPT_SUM_MASK_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LPI_CTRL_0_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LPI_CTRL_1_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LPI_CTRL_2_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LPI_STATUS_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_LPI_CNTR_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PULSE_1_MS_LOW_REG); ++ regs_buff[index++] = mv_gop110_gmac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_GMAC_PULSE_1_MS_HIGH_REG); ++} ++ ++void mv_gop110_xlg_registers_dump(struct mv_pp2x_port *port, u32 *regs_buff) ++{ ++ int gop_port = port->mac_data.gop_index; ++ int index = 0; ++ ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL0_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL1_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL2_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL2_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_PORT_STATUS_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_FIFOS_THRS_CFG_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_INTERRUPT_CAUSE_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_INTERRUPT_MASK_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL3_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_PER_PRIO_FLOW_CTRL_STATUS_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_DEBUG_BUS_STATUS_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_METAL_FIX_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MIB_CNTRS_CTRL_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_CNCCFC_TIMERI_REG(gop_port)); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_EXTERNAL_INTERRUPT_CAUSE_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_EXTERNAL_INTERRUPT_MASK_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_FC_DSA_TAG_0_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_FC_DSA_TAG_1_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_FC_DSA_TAG_2_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_FC_DSA_TAG_3_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_DIC_BUDGET_COMPENSATION_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL4_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_PORT_MAC_CTRL5_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_EXT_CTRL_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_MACRO_CTRL_REG); ++ regs_buff[index++] = mv_gop110_xlg_mac_read(&port->priv->hw.gop, ++ port->mac_data.gop_index, ++ MV_XLG_MAC_DIC_PPM_IPG_REDUCE_REG); ++} ++ ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++static void mv_gop110_set_new_phy_mode(u32 speed, struct mv_mac_data *mac) ++{ ++ mac->flags &= ~(MV_EMAC_F_SGMII2_5 | MV_EMAC_F_5G); ++ ++ if (speed == SPEED_10000) { ++ mac->phy_mode = PHY_INTERFACE_MODE_SFI; ++ mac->speed = SPEED_10000; ++ } else if (speed == SPEED_5000) { ++ mac->phy_mode = PHY_INTERFACE_MODE_SFI; ++ mac->speed = SPEED_5000; ++ mac->flags |= MV_EMAC_F_5G; ++ } else if (speed == SPEED_2500) { ++ mac->phy_mode = PHY_INTERFACE_MODE_SGMII; ++ mac->speed = SPEED_2500; ++ mac->flags |= MV_EMAC_F_SGMII2_5; ++ } else { ++ mac->phy_mode = PHY_INTERFACE_MODE_SGMII; ++ mac->speed = SPEED_1000; ++ } ++} ++ ++static int mv_gop110_get_new_comphy_mode(u32 speed, int port_id) ++{ ++ if (speed == SPEED_10000 && (port_id == 0 || port_id == 1)) ++ return COMPHY_DEF(COMPHY_SFI_MODE, port_id, ++ COMPHY_SPEED_10_3125G, COMPHY_POLARITY_NO_INVERT); ++ else if (speed == SPEED_5000 && (port_id == 0 || port_id == 1)) ++ return COMPHY_DEF(COMPHY_SFI_MODE, port_id, ++ COMPHY_SPEED_5_15625G, COMPHY_POLARITY_NO_INVERT); ++ else if (speed == SPEED_2500) ++ return COMPHY_DEF(COMPHY_HS_SGMII_MODE, port_id, ++ COMPHY_SPEED_3_125G, COMPHY_POLARITY_NO_INVERT); ++ else if (speed == SPEED_1000 || speed == SPEED_100 || ++ speed == SPEED_10) ++ return COMPHY_DEF(COMPHY_SGMII_MODE, port_id, ++ COMPHY_SPEED_1_25G, COMPHY_POLARITY_NO_INVERT); ++ else ++ return -EINVAL; ++} ++#endif ++ ++void mv_gop110_max_rx_size_set(struct mv_pp2x_port *port) ++{ ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_max_rx_size_set(gop, mac->gop_index, ++ port->pkt_size); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_max_rx_size_set(gop, ++ mac->gop_index, port->pkt_size); ++ break; ++ default: ++ break; ++ } ++} ++ ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++/* Routine update comphy and GoP according to port speed. ++ * Speed could be changed by ethtool and phy driver. ++ */ ++int mv_gop110_update_comphy(struct mv_pp2x_port *port, u32 speed) ++{ ++ int comphy_old_mode, comphy_new_mode; ++ int err = 0; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ ++ comphy_new_mode = mv_gop110_get_new_comphy_mode(speed, port->id); ++ ++ if (comphy_new_mode < 0) { ++ pr_err("Port ID %d: unsupported speed set\n", port->id); ++ return comphy_new_mode; ++ } ++ ++ comphy_old_mode = phy_get_mode(port->comphy[0]); ++ ++ if (comphy_old_mode == comphy_new_mode) ++ return 0; ++ ++ /* If port is UP: ++ * 1. Shutdown down dev state. ++ * 2. Mask Link interrupt. ++ * 3. Turn down MAC. ++ * 4. Power off Serdes. ++ */ ++ if (mac->flags & MV_EMAC_F_PORT_UP) { ++ netif_carrier_off(port->dev); ++ mv_gop110_port_events_mask(gop, mac); ++ mv_gop110_port_disable(gop, mac, port); ++ phy_power_off(port->comphy[0]); ++ } ++ ++ err = phy_set_mode(port->comphy[0], comphy_new_mode); ++ if (err < 0) { ++ phy_set_mode(port->comphy[0], comphy_old_mode); ++ pr_err("Port ID %d: err %d COMPHY lane is busy\n", port->id, err); ++ goto out; ++ } ++ ++ mv_gop110_set_new_phy_mode(speed, mac); ++ ++ /* Reconfigure GoP and Serdes if port were initialized */ ++ if (mac->flags & MV_EMAC_F_INIT) { ++ mac->flags &= ~MV_EMAC_F_INIT; ++ mvcpn110_mac_hw_init(port); ++ } ++ ++ mv_gop110_netc_reconfig(port->priv, mac->gop_index); ++ ++out: ++ /* Turn ON port */ ++ if (mac->flags & MV_EMAC_F_PORT_UP) { ++ mv_gop110_port_disable(gop, mac, port); ++ mv_gop110_max_rx_size_set(port); ++ phy_power_on(port->comphy[0]); ++ mv_gop110_port_events_unmask(gop, mac); ++ mv_gop110_port_enable(gop, mac, port); ++ netif_carrier_on(port->dev); ++ } ++ ++ return err; ++} ++#endif ++ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.h b/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.h +new file mode 100644 +index 00000000..dd8d533 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw.h +@@ -0,0 +1,478 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#ifndef _MV_GOP_HW_H_ ++#define _MV_GOP_HW_H_ ++ ++/* Sets the field located at the specified in data. */ ++#define U32_SET_FIELD(data, mask, val) ((data) = (((data) & ~(mask)) | (val))) ++ ++/* port related */ ++enum mv_reset {RESET, UNRESET}; ++ ++enum mv_port_speed { ++ MV_PORT_SPEED_AN, ++ MV_PORT_SPEED_10, ++ MV_PORT_SPEED_100, ++ MV_PORT_SPEED_1000, ++ MV_PORT_SPEED_2500, ++ MV_PORT_SPEED_10000 ++}; ++ ++enum mv_port_duplex { ++ MV_PORT_DUPLEX_AN, ++ MV_PORT_DUPLEX_HALF, ++ MV_PORT_DUPLEX_FULL ++}; ++ ++enum mv_port_fc { ++ MV_PORT_FC_AN_NO, ++ MV_PORT_FC_AN_SYM, ++ MV_PORT_FC_AN_ASYM, ++ MV_PORT_FC_DISABLE, ++ MV_PORT_FC_TX_DISABLE, ++ MV_PORT_FC_RX_DISABLE, ++ MV_PORT_FC_ENABLE, ++ MV_PORT_FC_TX_ENABLE, ++ MV_PORT_FC_RX_ENABLE, ++ MV_PORT_FC_ACTIVE ++}; ++ ++struct mv_port_link_status { ++ int linkup; /*flag*/ ++ enum mv_port_speed speed; ++ enum mv_port_duplex duplex; ++ enum mv_port_fc rx_fc; ++ enum mv_port_fc tx_fc; ++ enum mv_port_fc autoneg_fc; ++}; ++ ++/* different loopback types can be configure on different levels: ++ * MAC, PCS, SERDES ++ */ ++enum mv_lb_type { ++ MV_DISABLE_LB, ++ MV_RX_2_TX_LB, ++ MV_TX_2_RX_LB, /* on SERDES level - analog loopback */ ++ MV_TX_2_RX_DIGITAL_LB /* on SERDES level - digital loopback */ ++}; ++ ++enum sd_media_mode {MV_RXAUI, MV_XAUI}; ++ ++/* Net Complex */ ++enum mv_netc_topology { ++ MV_NETC_GE_MAC0_RXAUI_L23 = BIT(0), ++ MV_NETC_GE_MAC0_RXAUI_L45 = BIT(1), ++ MV_NETC_GE_MAC0_XAUI = BIT(2), ++ MV_NETC_GE_MAC2_SGMII = BIT(3), ++ MV_NETC_GE_MAC2_RGMII = BIT(4), ++ MV_NETC_GE_MAC3_SGMII = BIT(5), ++ MV_NETC_GE_MAC3_RGMII = BIT(6), ++}; ++ ++enum mv_netc_phase { ++ MV_NETC_FIRST_PHASE, ++ MV_NETC_SECOND_PHASE, ++}; ++ ++enum mv_netc_sgmii_xmi_mode { ++ MV_NETC_GBE_SGMII, ++ MV_NETC_GBE_XMII, ++}; ++ ++enum mv_netc_mii_mode { ++ MV_NETC_GBE_RGMII, ++ MV_NETC_GBE_MII, ++}; ++ ++enum mv_netc_lanes { ++ MV_NETC_LANE_23, ++ MV_NETC_LANE_45, ++}; ++ ++enum mv_gop_port { ++ MV_GOP_PORT0 = 0, ++ MV_GOP_PORT1 = 1, ++ MV_GOP_PORT2 = 2, ++ MV_GOP_PORT3 = 3, ++}; ++ ++#define MV_RGMII_TX_FIFO_MIN_TH (0x41) ++#define MV_SGMII_TX_FIFO_MIN_TH (0x5) ++#define MV_SGMII2_5_TX_FIFO_MIN_TH (0xB) ++ ++static inline u32 mv_gop_gen_read(void __iomem *base, u32 offset) ++{ ++ void *reg_ptr = base + offset; ++ u32 val; ++ ++ val = readl(reg_ptr); ++ return val; ++} ++ ++static inline void mv_gop_gen_write(void __iomem *base, u32 offset, u32 data) ++{ ++ void *reg_ptr = base + offset; ++ ++ writel(data, reg_ptr); ++} ++ ++/* GOP port configuration functions */ ++int mv_gop110_port_init(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_port_reset(struct gop_hw *gop, struct mv_mac_data *mac); ++void mv_gop110_port_enable(struct gop_hw *gop, struct mv_mac_data *mac, struct mv_pp2x_port *port); ++void mv_gop110_port_disable(struct gop_hw *gop, struct mv_mac_data *mac, struct mv_pp2x_port *port); ++void mv_gop110_port_periodic_xon_set(struct gop_hw *gop, ++ struct mv_mac_data *mac, ++ int enable); ++bool mv_gop110_port_is_link_up(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_port_link_status(struct gop_hw *gop, struct mv_mac_data *mac, ++ struct mv_port_link_status *pstatus); ++bool mv_gop110_port_autoneg_status(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_check_port_type(struct gop_hw *gop, int port_num); ++void mv_gop110_gmac_set_autoneg(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool auto_neg); ++int mv_gop110_port_regs(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_port_events_mask(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_port_events_unmask(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_port_events_clear(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_status_show(struct gop_hw *gop, struct mv_pp2x *pp2, int port_num); ++int mv_gop110_speed_duplex_get(struct gop_hw *gop, struct mv_mac_data *mac, ++ enum mv_port_speed *speed, ++ enum mv_port_duplex *duplex); ++int mv_gop110_speed_duplex_set(struct gop_hw *gop, struct mv_mac_data *mac, ++ enum mv_port_speed speed, ++ enum mv_port_duplex duplex); ++int mv_gop110_autoneg_restart(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_fl_cfg(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_force_link_mode_set(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool force_link_up, ++ bool force_link_down); ++int mv_gop110_force_link_mode_get(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool *force_link_up, ++ bool *force_link_down); ++int mv_gop110_loopback_set(struct gop_hw *gop, struct mv_mac_data *mac, ++ bool lb); ++void mv_gop_reg_print(char *reg_name, u32 reg); ++ ++/* Gig PCS Functions */ ++int mv_gop110_gpcs_mode_cfg(struct gop_hw *gop, int pcs_num, bool en); ++int mv_gop110_in_band_auto_neg(struct gop_hw *gop, int pcs_num, bool en); ++ ++/* MPCS Functions */ ++ ++static inline u32 mv_gop110_mpcs_global_read(struct gop_hw *gop, int mac_num, u32 offset) ++{ ++ return mv_gop_gen_read(gop->gop_110.mspg.base, mac_num * gop->gop_110.mspg.obj_size + offset); ++} ++ ++static inline void mv_gop110_mpcs_global_write(struct gop_hw *gop, int mac_num, u32 offset, ++ u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.mspg.base, mac_num * gop->gop_110.mspg.obj_size + offset, data); ++} ++ ++static inline void mv_gop110_mpcs_global_print(struct gop_hw *gop, int mac_num, ++ char *reg_name, u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_mpcs_global_read(gop, mac_num, reg)); ++} ++ ++/* XPCS Functions */ ++ ++static inline u32 mv_gop110_xpcs_global_read(struct gop_hw *gop, u32 offset) ++{ ++ return mv_gop_gen_read(gop->gop_110.xpcs_base, offset); ++} ++ ++static inline void mv_gop110_xpcs_global_write(struct gop_hw *gop, u32 offset, ++ u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.xpcs_base, offset, data); ++} ++ ++static inline void mv_gop110_xpcs_global_print(struct gop_hw *gop, ++ char *reg_name, u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_xpcs_global_read(gop, reg)); ++} ++ ++static inline u32 mv_gop110_xpcs_lane_read(struct gop_hw *gop, int lane_num, ++ u32 offset) ++{ ++ return mv_gop_gen_read(gop->gop_110.xpcs_base, offset); ++} ++ ++static inline void mv_gop110_xpcs_lane_write(struct gop_hw *gop, int lane_num, ++ u32 offset, u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.xpcs_base, offset, data); ++} ++ ++static inline void mv_gop110_xpcs_lane_print(struct gop_hw *gop, ++ char *reg_name, ++ int lane_num, u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_xpcs_lane_read(gop, lane_num, reg)); ++} ++ ++void mv_gop110_xpcs_gl_regs_dump(struct gop_hw *gop); ++void mv_gop110_xpcs_lane_regs_dump(struct gop_hw *gop, int lane); ++int mv_gop110_xpcs_reset(struct gop_hw *gop, enum mv_reset reset); ++int mv_gop110_xpcs_mode(struct gop_hw *gop, int num_of_lanes); ++int mv_gop110_mpcs_mode(struct gop_hw *gop, int mac_num); ++void mv_gop110_mpcs_clock_reset(struct gop_hw *gop, int mac_num, enum mv_reset reset); ++ ++/* XLG MAC Functions */ ++static inline u32 mv_gop110_xlg_mac_read(struct gop_hw *gop, int mac_num, ++ u32 offset) ++{ ++ return(mv_gop_gen_read(gop->gop_110.xlg_mac.base, ++ mac_num * gop->gop_110.xlg_mac.obj_size + offset)); ++} ++ ++static inline void mv_gop110_xlg_mac_write(struct gop_hw *gop, int mac_num, ++ u32 offset, u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.xlg_mac.base, ++ mac_num * gop->gop_110.xlg_mac.obj_size + offset, data); ++} ++ ++static inline void mv_gop110_xlg_mac_print(struct gop_hw *gop, char *reg_name, ++ int mac_num, u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_xlg_mac_read(gop, mac_num, reg)); ++} ++ ++/* MIB MAC Functions */ ++static inline u32 mv_gop110_xmib_mac_read(struct gop_hw *gop, int mac_num, ++ u32 offset) ++{ ++ return(mv_gop_gen_read(gop->gop_110.xmib.base, ++ mac_num * gop->gop_110.xmib.obj_size + offset)); ++} ++ ++static inline void mv_gop110_xmib_mac_write(struct gop_hw *gop, int mac_num, ++ u32 offset, u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.xmib.base, ++ mac_num * gop->gop_110.xmib.obj_size + offset, data); ++} ++ ++static inline void mv_gop110_xmib_mac_print(struct gop_hw *gop, char *reg_name, ++ int mac_num, u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_xmib_mac_read(gop, mac_num, reg)); ++} ++ ++void mv_gop110_xlg_mac_regs_dump(struct gop_hw *gop, int port); ++int mv_gop110_xlg_mac_reset(struct gop_hw *gop, int mac_num, ++ enum mv_reset reset); ++int mv_gop110_xlg_mac_mode_cfg(struct gop_hw *gop, int mac_num, ++ int num_of_act_lanes); ++void mv_gop110_xlg_mac_1G_cfg(struct gop_hw *gop, int mac_num); ++int mv_gop110_xlg_mac_loopback_cfg(struct gop_hw *gop, int mac_num, ++ enum mv_lb_type type); ++ ++bool mv_gop110_xlg_mac_link_status_get(struct gop_hw *gop, int mac_num); ++void mv_gop110_xlg_mac_port_enable(struct gop_hw *gop, int mac_num); ++void mv_gop110_xlg_mac_port_disable(struct gop_hw *gop, int mac_num); ++void mv_gop110_xlg_mac_port_periodic_xon_set(struct gop_hw *gop, ++ int mac_num, ++ int enable); ++int mv_gop110_xlg_mac_link_status(struct gop_hw *gop, int mac_num, ++ struct mv_port_link_status *pstatus); ++int mv_gop110_xlg_mac_max_rx_size_set(struct gop_hw *gop, int mac_num, ++ int max_rx_size); ++int mv_gop110_xlg_mac_force_link_mode_set(struct gop_hw *gop, int mac_num, ++ bool force_link_up, ++ bool force_link_down); ++int mv_gop110_xlg_mac_speed_duplex_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ enum mv_port_duplex duplex); ++int mv_gop110_xlg_mac_speed_duplex_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed *speed, ++ enum mv_port_duplex *duplex); ++int mv_gop110_xlg_mac_fc_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc fc); ++void mv_gop110_xlg_mac_fc_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc *fc); ++int mv_gop110_xlg_mac_port_link_speed_fc(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ int force_link_up); ++void mv_gop110_xlg_port_link_event_mask(struct gop_hw *gop, int mac_num); ++void mv_gop110_xlg_port_external_event_unmask(struct gop_hw *gop, ++ int mac_num, ++ int bit_2_open); ++void mv_gop110_xlg_port_link_event_clear(struct gop_hw *gop, int mac_num); ++void mv_gop110_xlg_2_gig_mac_cfg(struct gop_hw *gop, int mac_num); ++ ++/* GMAC Functions */ ++static inline u32 mv_gop110_gmac_read(struct gop_hw *gop, int mac_num, ++ u32 offset) ++{ ++ return(mv_gop_gen_read(gop->gop_110.gmac.base, ++ mac_num * gop->gop_110.gmac.obj_size + offset)); ++} ++ ++static inline void mv_gop110_gmac_write(struct gop_hw *gop, int mac_num, ++ u32 offset, u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.gmac.base, ++ mac_num * gop->gop_110.gmac.obj_size + offset, data); ++} ++ ++static inline void mv_gop110_gmac_print(struct gop_hw *gop, char *reg_name, ++ int mac_num, u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_gmac_read(gop, mac_num, reg)); ++} ++ ++void mv_gop110_register_bases_dump(struct gop_hw *gop); ++void mv_gop110_gmac_regs_dump(struct gop_hw *gop, int port); ++int mv_gop110_gmac_reset(struct gop_hw *gop, int mac_num, ++ enum mv_reset reset); ++int mv_gop110_gmac_mode_cfg(struct gop_hw *gop, struct mv_mac_data *mac); ++int mv_gop110_gmac_loopback_cfg(struct gop_hw *gop, int mac_num, ++ enum mv_lb_type type); ++bool mv_gop110_gmac_link_status_get(struct gop_hw *gop, int mac_num); ++void mv_gop110_gmac_port_enable(struct gop_hw *gop, int mac_num); ++void mv_gop110_gmac_port_disable(struct gop_hw *gop, int mac_num); ++void mv_gop110_gmac_port_periodic_xon_set(struct gop_hw *gop, int mac_num, ++ int enable); ++int mv_gop110_gmac_link_status(struct gop_hw *gop, int mac_num, ++ struct mv_port_link_status *pstatus); ++int mv_gop110_gmac_max_rx_size_set(struct gop_hw *gop, int mac_num, ++ int max_rx_size); ++int mv_gop110_gmac_force_link_mode_set(struct gop_hw *gop, int mac_num, ++ bool force_link_up, ++ bool force_link_down); ++int mv_gop110_gmac_force_link_mode_get(struct gop_hw *gop, int mac_num, ++ bool *force_link_up, ++ bool *force_link_down); ++int mv_gop110_gmac_speed_duplex_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ enum mv_port_duplex duplex); ++int mv_gop110_gmac_speed_duplex_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed *speed, ++ enum mv_port_duplex *duplex); ++int mv_gop110_gmac_fc_set(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc fc); ++void mv_gop110_gmac_fc_get(struct gop_hw *gop, int mac_num, ++ enum mv_port_fc *fc); ++int mv_gop110_gmac_port_link_speed_fc(struct gop_hw *gop, int mac_num, ++ enum mv_port_speed speed, ++ int force_link_up); ++void mv_gop110_gmac_port_link_event_mask(struct gop_hw *gop, int mac_num); ++void mv_gop110_gmac_port_link_event_unmask(struct gop_hw *gop, int mac_num); ++void mv_gop110_gmac_port_link_event_clear(struct gop_hw *gop, int mac_num); ++int mv_gop110_gmac_port_autoneg_restart(struct gop_hw *gop, int mac_num); ++ ++/* SMI Functions */ ++static inline u32 mv_gop110_smi_read(struct gop_hw *gop, u32 offset) ++{ ++ return mv_gop_gen_read(gop->gop_110.smi_base, offset); ++} ++ ++static inline void mv_gop110_smi_write(struct gop_hw *gop, u32 offset, ++ u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.smi_base, offset, data); ++} ++ ++static inline void mv_gop110_smi_print(struct gop_hw *gop, char *reg_name, ++ u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_smi_read(gop, reg)); ++} ++ ++/* RFU1 Functions */ ++static inline u32 mv_gop110_rfu1_read(struct gop_hw *gop, u32 offset) ++{ ++ return mv_gop_gen_read(gop->gop_110.rfu1_base, offset); ++} ++ ++static inline void mv_gop110_rfu1_write(struct gop_hw *gop, u32 offset, ++ u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.rfu1_base, offset, data); ++} ++ ++static inline void mv_gop110_rfu1_print(struct gop_hw *gop, char *reg_name, ++ u32 reg) ++{ ++ pr_info(" %-32s: 0x%x = 0x%08x\n", reg_name, reg, ++ mv_gop110_rfu1_read(gop, reg)); ++} ++ ++int mv_gop110_smi_init(struct gop_hw *gop); ++int mv_gop110_smi_phy_addr_cfg(struct gop_hw *gop, int port, int addr); ++ ++/* MIB Functions */ ++u64 mv_gop110_mib_read64(struct gop_hw *gop, int port, unsigned int offset); ++void mv_gop110_mib_counters_show(struct gop_hw *gop, int port); ++void mv_gop110_mib_counters_stat_update(struct gop_hw *gop, int port, ++ struct gop_stat *gop_statistics); ++void mv_gop110_mib_counters_clear(struct gop_hw *gop, int port); ++ ++/* PTP Functions */ ++void mv_gop110_ptp_enable(struct gop_hw *gop, int port, bool state); ++ ++/*RFU Functions */ ++int mv_gop110_netc_init(struct gop_hw *gop, ++ u32 net_comp_config, enum mv_netc_phase phase); ++void mv_gop110_netc_reconfig(struct mv_pp2x *priv, int port); ++void mv_gop110_netc_active_port(struct gop_hw *gop, u32 port, u32 val); ++void mv_gop110_netc_xon_set(struct gop_hw *gop, enum mv_gop_port port, bool en); ++ ++/* FCA Functions */ ++void mv_gop110_fca_send_periodic(struct gop_hw *gop, int mac_num, bool en); ++void mv_gop110_fca_set_periodic_timer(struct gop_hw *gop, int mac_num, u64 timer); ++ ++void mv_gop110_fca_tx_enable(struct gop_hw *gop, int mac_num, bool en); ++ ++bool mv_gop110_check_fca_tx_state(struct gop_hw *gop, int mac_num); ++ ++static inline u32 mv_gop110_fca_read(struct gop_hw *gop, int mac_num, ++ u32 offset) ++{ ++ return mv_gop_gen_read(gop->gop_110.fca.base, ++ mac_num * gop->gop_110.fca.obj_size + offset); ++} ++ ++static inline void mv_gop110_fca_write(struct gop_hw *gop, int mac_num, ++ u32 offset, u32 data) ++{ ++ mv_gop_gen_write(gop->gop_110.fca.base, ++ mac_num * gop->gop_110.fca.obj_size + offset, data); ++} ++ ++/*Ethtool Functions */ ++void mv_gop110_gmac_registers_dump(struct mv_pp2x_port *port, u32 *regs_buff); ++void mv_gop110_xlg_registers_dump(struct mv_pp2x_port *port, u32 *regs_buff); ++ ++int mv_gop110_update_comphy(struct mv_pp2x_port *port, u32 speed); ++ ++#endif /* _MV_GOP_HW_H_ */ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw_type.h b/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw_type.h +new file mode 100644 +index 00000000..1e4c8b7 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_gop110_hw_type.h +@@ -0,0 +1,1989 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#ifndef _MV_GOP_HW_TYPE_H_ ++#define _MV_GOP_HW_TYPE_H_ ++ ++#include ++#include ++#include ++ ++#define MVCPN110_GOP_MAC_NUM 4 ++ ++/***********/ ++/*GMAC REGS */ ++/***********/ ++ ++/* Port Mac Control0 */ ++#define MV_GMAC_PORT_CTRL0_REG (0x0000) ++#define MV_GMAC_PORT_CTRL0_PORTEN_OFFS 0 ++#define MV_GMAC_PORT_CTRL0_PORTEN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL0_PORTEN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL0_PORTTYPE_OFFS 1 ++#define MV_GMAC_PORT_CTRL0_PORTTYPE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL0_PORTTYPE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_OFFS 2 ++#define MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_MASK \ ++ (0x00001fff << MV_GMAC_PORT_CTRL0_FRAMESIZELIMIT_OFFS) ++ ++#define MV_GMAC_PORT_CTRL0_COUNT_EN_OFFS 15 ++#define MV_GMAC_PORT_CTRL0_COUNT_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL0_COUNT_EN_OFFS) ++ ++/* Port Mac Control1 */ ++#define MV_GMAC_PORT_CTRL1_REG (0x0004) ++#define MV_GMAC_PORT_CTRL1_EN_RX_CRC_CHECK_OFFS 0 ++#define MV_GMAC_PORT_CTRL1_EN_RX_CRC_CHECK_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_EN_RX_CRC_CHECK_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_EN_PERIODIC_FC_XON_OFFS 1 ++#define MV_GMAC_PORT_CTRL1_EN_PERIODIC_FC_XON_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_EN_PERIODIC_FC_XON_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_MGMII_MODE_OFFS 2 ++#define MV_GMAC_PORT_CTRL1_MGMII_MODE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_MGMII_MODE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_PFC_CASCADE_PORT_ENABLE_OFFS 3 ++#define MV_GMAC_PORT_CTRL1_PFC_CASCADE_PORT_ENABLE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_PFC_CASCADE_PORT_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_DIS_EXCESSIVE_COL_OFFS 4 ++#define MV_GMAC_PORT_CTRL1_DIS_EXCESSIVE_COL_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_DIS_EXCESSIVE_COL_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_GMII_LOOPBACK_OFFS 5 ++#define MV_GMAC_PORT_CTRL1_GMII_LOOPBACK_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_GMII_LOOPBACK_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_PCS_LOOPBACK_OFFS 6 ++#define MV_GMAC_PORT_CTRL1_PCS_LOOPBACK_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_PCS_LOOPBACK_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_FC_SA_ADDR_LO_OFFS 7 ++#define MV_GMAC_PORT_CTRL1_FC_SA_ADDR_LO_MASK \ ++ (0x000000ff << MV_GMAC_PORT_CTRL1_FC_SA_ADDR_LO_OFFS) ++ ++#define MV_GMAC_PORT_CTRL1_EN_SHORT_PREAMBLE_OFFS 15 ++#define MV_GMAC_PORT_CTRL1_EN_SHORT_PREAMBLE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL1_EN_SHORT_PREAMBLE_OFFS) ++ ++/* Port Mac Control2 */ ++#define MV_GMAC_PORT_CTRL2_REG (0x0008) ++#define MV_GMAC_PORT_CTRL2_SGMII_MODE_OFFS 0 ++#define MV_GMAC_PORT_CTRL2_SGMII_MODE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_SGMII_MODE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_FC_MODE_OFFS 1 ++#define MV_GMAC_PORT_CTRL2_FC_MODE_MASK \ ++ (0x00000003 << MV_GMAC_PORT_CTRL2_FC_MODE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_PCS_EN_OFFS 3 ++#define MV_GMAC_PORT_CTRL2_PCS_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_PCS_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_RGMII_MODE_OFFS 4 ++#define MV_GMAC_PORT_CTRL2_RGMII_MODE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_RGMII_MODE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_DIS_PADING_OFFS 5 ++#define MV_GMAC_PORT_CTRL2_DIS_PADING_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_DIS_PADING_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_PORTMACRESET_OFFS 6 ++#define MV_GMAC_PORT_CTRL2_PORTMACRESET_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_PORTMACRESET_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_TX_DRAIN_OFFS 7 ++#define MV_GMAC_PORT_CTRL2_TX_DRAIN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_TX_DRAIN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_EN_MII_ODD_PRE_OFFS 8 ++#define MV_GMAC_PORT_CTRL2_EN_MII_ODD_PRE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_EN_MII_ODD_PRE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_CLK_125_BYPS_EN_OFFS 9 ++#define MV_GMAC_PORT_CTRL2_CLK_125_BYPS_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_CLK_125_BYPS_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_PRBS_CHECK_EN_OFFS 10 ++#define MV_GMAC_PORT_CTRL2_PRBS_CHECK_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_PRBS_CHECK_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_PRBS_GEN_EN_OFFS 11 ++#define MV_GMAC_PORT_CTRL2_PRBS_GEN_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_PRBS_GEN_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_SELECT_DATA_TO_TX_OFFS 12 ++#define MV_GMAC_PORT_CTRL2_SELECT_DATA_TO_TX_MASK \ ++ (0x00000003 << MV_GMAC_PORT_CTRL2_SELECT_DATA_TO_TX_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_EN_COL_ON_BP_OFFS 14 ++#define MV_GMAC_PORT_CTRL2_EN_COL_ON_BP_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_EN_COL_ON_BP_OFFS) ++ ++#define MV_GMAC_PORT_CTRL2_EARLY_REJECT_MODE_OFFS 15 ++#define MV_GMAC_PORT_CTRL2_EARLY_REJECT_MODE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL2_EARLY_REJECT_MODE_OFFS) ++ ++/* Port Auto-negotiation Configuration */ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_REG (0x000c) ++#define MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_DOWN_OFFS 0 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_DOWN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_DOWN_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_UP_OFFS 1 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_UP_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_FORCE_LINK_UP_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_OFFS 2 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_EN_PCS_AN_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_AN_BYPASS_EN_OFFS 3 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_AN_BYPASS_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_AN_BYPASS_EN_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_INBAND_RESTARTAN_OFFS 4 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_INBAND_RESTARTAN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_INBAND_RESTARTAN_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_OFFS 5 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_SET_MII_SPEED_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_OFFS 6 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_SET_GMII_SPEED_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_OFFS 7 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_EN_AN_SPEED_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_OFFS 9 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_ADV_PAUSE_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_OFFS 10 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_ADV_ASM_PAUSE_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_OFFS 11 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_EN_FC_AN_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_OFFS 12 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_SET_FULL_DX_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_OFFS 13 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_EN_FDX_AN_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_PHY_MODE_OFFS 14 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_PHY_MODE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_AUTO_NEG_CFG_PHY_MODE_OFFS) ++ ++#define MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_OFFS 15 ++#define MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_AUTO_NEG_CFG_CHOOSE_SAMPLE_TX_CONFIG_OFFS) ++ ++/* Port Status0 */ ++#define MV_GMAC_PORT_STATUS0_REG (0x0010) ++#define MV_GMAC_PORT_STATUS0_LINKUP_OFFS 0 ++#define MV_GMAC_PORT_STATUS0_LINKUP_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_LINKUP_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_GMIISPEED_OFFS 1 ++#define MV_GMAC_PORT_STATUS0_GMIISPEED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_GMIISPEED_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_MIISPEED_OFFS 2 ++#define MV_GMAC_PORT_STATUS0_MIISPEED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_MIISPEED_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_FULLDX_OFFS 3 ++#define MV_GMAC_PORT_STATUS0_FULLDX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_FULLDX_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_RXFCEN_OFFS 4 ++#define MV_GMAC_PORT_STATUS0_RXFCEN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_RXFCEN_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_TXFCEN_OFFS 5 ++#define MV_GMAC_PORT_STATUS0_TXFCEN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_TXFCEN_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_PORTRXPAUSE_OFFS 6 ++#define MV_GMAC_PORT_STATUS0_PORTRXPAUSE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_PORTRXPAUSE_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_PORTTXPAUSE_OFFS 7 ++#define MV_GMAC_PORT_STATUS0_PORTTXPAUSE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_PORTTXPAUSE_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_PORTIS_DOINGPRESSURE_OFFS 8 ++#define MV_GMAC_PORT_STATUS0_PORTIS_DOINGPRESSURE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_PORTIS_DOINGPRESSURE_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_PORTBUFFULL_OFFS 9 ++#define MV_GMAC_PORT_STATUS0_PORTBUFFULL_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_PORTBUFFULL_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_SYNCFAIL10MS_OFFS 10 ++#define MV_GMAC_PORT_STATUS0_SYNCFAIL10MS_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_SYNCFAIL10MS_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_ANDONE_OFFS 11 ++#define MV_GMAC_PORT_STATUS0_ANDONE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_ANDONE_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_INBAND_AUTONEG_BYPASSACT_OFFS 12 ++#define MV_GMAC_PORT_STATUS0_INBAND_AUTONEG_BYPASSACT_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_STATUS0_INBAND_AUTONEG_BYPASSACT_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_SERDESPLL_LOCKED_OFFS 13 ++#define MV_GMAC_PORT_STATUS0_SERDESPLL_LOCKED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_SERDESPLL_LOCKED_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_SYNCOK_OFFS 14 ++#define MV_GMAC_PORT_STATUS0_SYNCOK_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_SYNCOK_OFFS) ++ ++#define MV_GMAC_PORT_STATUS0_SQUELCHNOT_DETECTED_OFFS 15 ++#define MV_GMAC_PORT_STATUS0_SQUELCHNOT_DETECTED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS0_SQUELCHNOT_DETECTED_OFFS) ++ ++/* Port Serial Parameters Configuration */ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_REG (0x0014) ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_UNIDIRECTIONAL_ENABLE_OFFS 0 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_UNIDIRECTIONAL_ENABLE_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_UNIDIRECTIONAL_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_RETRANSMIT_COLLISION_DOMAIN_OFFS 1 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_RETRANSMIT_COLLISION_DOMAIN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_RETRANSMIT_COLLISION_DOMAIN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_PUMA2_BTS1444_EN_OFFS 2 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_PUMA2_BTS1444_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_PUMA2_BTS1444_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_FORWARD_802_3X_FC_EN_OFFS 3 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_FORWARD_802_3X_FC_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_FORWARD_802_3X_FC_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_BP_EN_OFFS 4 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_BP_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERIAL_PARAM_CFG_BP_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_RX_NEGEDGE_SAMPLE_EN_OFFS 5 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_RX_NEGEDGE_SAMPLE_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_RX_NEGEDGE_SAMPLE_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_COL_DOMAIN_LIMIT_OFFS 6 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_COL_DOMAIN_LIMIT_MASK \ ++ (0x0000003f << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_COL_DOMAIN_LIMIT_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_PERIODIC_TYPE_SELECT_OFFS 12 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_PERIODIC_TYPE_SELECT_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_PERIODIC_TYPE_SELECT_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_PER_PRIORITY_FC_EN_OFFS 13 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_PER_PRIORITY_FC_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_PER_PRIORITY_FC_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_TX_STANDARD_PRBS7_OFFS 14 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_TX_STANDARD_PRBS7_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_TX_STANDARD_PRBS7_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_REVERSE_PRBS_RX_OFFS 15 ++#define MV_GMAC_PORT_SERIAL_PARAM_CFG_REVERSE_PRBS_RX_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_CFG_REVERSE_PRBS_RX_OFFS) ++ ++/* Port Fifo Configuration 0 */ ++#define MV_GMAC_PORT_FIFO_CFG_0_REG (0x0018) ++#define MV_GMAC_PORT_FIFO_CFG_0_TX_FIFO_HIGH_WM_OFFS 0 ++#define MV_GMAC_PORT_FIFO_CFG_0_TX_FIFO_HIGH_WM_MASK \ ++ (0x000000ff << \ ++ MV_GMAC_PORT_FIFO_CFG_0_TX_FIFO_HIGH_WM_OFFS) ++ ++#define MV_GMAC_PORT_FIFO_CFG_0_TX_FIFO_LOW_WM_OFFS 8 ++#define MV_GMAC_PORT_FIFO_CFG_0_TX_FIFO_LOW_WM_MASK \ ++ (0x000000ff << \ ++ MV_GMAC_PORT_FIFO_CFG_0_TX_FIFO_LOW_WM_OFFS) ++ ++/* Port Fifo Configuration 1 */ ++#define MV_GMAC_PORT_FIFO_CFG_1_REG (0x001c) ++#define MV_GMAC_PORT_FIFO_CFG_1_RX_FIFO_MAX_TH_OFFS 0 ++#define MV_GMAC_PORT_FIFO_CFG_1_RX_FIFO_MAX_TH_MASK \ ++ (0x0000003f << MV_GMAC_PORT_FIFO_CFG_1_RX_FIFO_MAX_TH_OFFS) ++ ++#define MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS 6 ++#define MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_MASK \ ++ (0x000000ff << MV_GMAC_PORT_FIFO_CFG_1_TX_FIFO_MIN_TH_OFFS) ++ ++#define MV_GMAC_PORT_FIFO_CFG_1_PORT_EN_FIX_EN_OFFS 15 ++#define MV_GMAC_PORT_FIFO_CFG_1_PORT_EN_FIX_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_FIFO_CFG_1_PORT_EN_FIX_EN_OFFS) ++ ++/* Port Serdes Configuration0 */ ++#define MV_GMAC_PORT_SERDES_CFG0_REG (0x0028) ++#define MV_GMAC_PORT_SERDES_CFG0_SERDESRESET_OFFS 0 ++#define MV_GMAC_PORT_SERDES_CFG0_SERDESRESET_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_SERDESRESET_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_PU_TX_OFFS 1 ++#define MV_GMAC_PORT_SERDES_CFG0_PU_TX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_PU_TX_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_PU_RX_OFFS 2 ++#define MV_GMAC_PORT_SERDES_CFG0_PU_RX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_PU_RX_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_PU_PLL_OFFS 3 ++#define MV_GMAC_PORT_SERDES_CFG0_PU_PLL_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_PU_PLL_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_PU_IVREF_OFFS 4 ++#define MV_GMAC_PORT_SERDES_CFG0_PU_IVREF_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_PU_IVREF_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_TESTEN_OFFS 5 ++#define MV_GMAC_PORT_SERDES_CFG0_TESTEN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_TESTEN_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_DPHER_EN_OFFS 6 ++#define MV_GMAC_PORT_SERDES_CFG0_DPHER_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_DPHER_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_RUDI_INVALID_ENABLE_OFFS 7 ++#define MV_GMAC_PORT_SERDES_CFG0_RUDI_INVALID_ENABLE_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERDES_CFG0_RUDI_INVALID_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_ACK_OVERRIDE_ENABLE_OFFS 8 ++#define MV_GMAC_PORT_SERDES_CFG0_ACK_OVERRIDE_ENABLE_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERDES_CFG0_ACK_OVERRIDE_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_CONFIG_WORD_ENABLE_OFFS 9 ++#define MV_GMAC_PORT_SERDES_CFG0_CONFIG_WORD_ENABLE_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERDES_CFG0_CONFIG_WORD_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_SYNC_FAIL_INT_ENABLE_OFFS 10 ++#define MV_GMAC_PORT_SERDES_CFG0_SYNC_FAIL_INT_ENABLE_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERDES_CFG0_SYNC_FAIL_INT_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_MASTER_MODE_ENABLE_OFFS 11 ++#define MV_GMAC_PORT_SERDES_CFG0_MASTER_MODE_ENABLE_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERDES_CFG0_MASTER_MODE_ENABLE_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_TERM75_TX_OFFS 12 ++#define MV_GMAC_PORT_SERDES_CFG0_TERM75_TX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_TERM75_TX_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_OUTAMP_OFFS 13 ++#define MV_GMAC_PORT_SERDES_CFG0_OUTAMP_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_OUTAMP_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_BTS712_FIX_EN_OFFS 14 ++#define MV_GMAC_PORT_SERDES_CFG0_BTS712_FIX_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_BTS712_FIX_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERDES_CFG0_BTS156_FIX_EN_OFFS 15 ++#define MV_GMAC_PORT_SERDES_CFG0_BTS156_FIX_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_SERDES_CFG0_BTS156_FIX_EN_OFFS) ++ ++/* Port Serdes Configuration1 */ ++#define MV_GMAC_PORT_SERDES_CFG1_REG (0x002c) ++#define MV_GMAC_PORT_SERDES_CFG1_SMII_RX_10MB_CLK_EDGE_SEL_OFFS 0 ++#define MV_GMAC_PORT_SERDES_CFG1_SMII_RX_10MB_CLK_EDGE_SEL_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_SMII_RX_10MB_CLK_EDGE_SEL_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_SMII_TX_10MB_CLK_EDGE_SEL_OFFS 1 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_SMII_TX_10MB_CLK_EDGE_SEL_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_SMII_TX_10MB_CLK_EDGE_SEL_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_MEN_OFFS 2 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_MEN_MASK \ ++ (0x00000003 << MV_GMAC_GMAC_PORT_SERDES_CFG1_MEN_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_VCMS_OFFS 4 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_VCMS_MASK \ ++ (0x00000001 << MV_GMAC_GMAC_PORT_SERDES_CFG1_VCMS_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_USE_SIGDET_OFFS 5 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_USE_SIGDET_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_USE_SIGDET_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_EN_CRS_MASK_TX_OFFS 6 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_EN_CRS_MASK_TX_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_EN_CRS_MASK_TX_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_ENABLE_OFFS 7 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_ENABLE_MASK \ ++ (0x00000001 << MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_ENABLE_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_PHY_ADDRESS_OFFS 8 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_PHY_ADDRESS_MASK \ ++ (0x0000001f << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_PHY_ADDRESS_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_SIGDET_POLARITY_OFFS 13 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_SIGDET_POLARITY_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_SIGDET_POLARITY_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_INTERRUPT_POLARITY_OFFS 14 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_INTERRUPT_POLARITY_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_INTERRUPT_POLARITY_OFFS) ++ ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_SERDES_POLARITY_OFFS 15 ++#define MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_SERDES_POLARITY_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_GMAC_PORT_SERDES_CFG1_100FX_PCS_SERDES_POLARITY_OFFS) ++ ++/* Port Serdes Configuration2 */ ++#define MV_GMAC_PORT_SERDES_CFG2_REG (0x0030) ++#define MV_GMAC_PORT_SERDES_CFG2_AN_ADV_CONFIGURATION_OFFS 0 ++#define MV_GMAC_PORT_SERDES_CFG2_AN_ADV_CONFIGURATION_MASK \ ++ (0x0000ffff << \ ++ MV_GMAC_PORT_SERDES_CFG2_AN_ADV_CONFIGURATION_OFFS) ++ ++/* Port Serdes Configuration3 */ ++#define MV_GMAC_PORT_SERDES_CFG3_REG (0x0034) ++#define MV_GMAC_PORT_SERDES_CFG3_ABILITY_MATCH_STATUS_OFFS 0 ++#define MV_GMAC_PORT_SERDES_CFG3_ABILITY_MATCH_STATUS_MASK \ ++ (0x0000ffff << \ ++ MV_GMAC_PORT_SERDES_CFG3_ABILITY_MATCH_STATUS_OFFS) ++ ++/* Port Prbs Status */ ++#define MV_GMAC_PORT_PRBS_STATUS_REG (0x0038) ++#define MV_GMAC_PORT_PRBS_STATUS_PRBSCHECK_LOCKED_OFFS 0 ++#define MV_GMAC_PORT_PRBS_STATUS_PRBSCHECK_LOCKED_MASK \ ++ (0x00000001 << MV_GMAC_PORT_PRBS_STATUS_PRBSCHECK_LOCKED_OFFS) ++ ++#define MV_GMAC_PORT_PRBS_STATUS_PRBSCHECKRDY_OFFS 1 ++#define MV_GMAC_PORT_PRBS_STATUS_PRBSCHECKRDY_MASK \ ++ (0x00000001 << MV_GMAC_PORT_PRBS_STATUS_PRBSCHECKRDY_OFFS) ++ ++/* Port Prbs Error Counter */ ++#define MV_GMAC_PORT_PRBS_ERR_CNTR_REG (0x003c) ++#define MV_GMAC_PORT_PRBS_ERR_CNTR_PRBSBITERRCNT_OFFS 0 ++#define MV_GMAC_PORT_PRBS_ERR_CNTR_PRBSBITERRCNT_MASK \ ++ (0x0000ffff << MV_GMAC_PORT_PRBS_ERR_CNTR_PRBSBITERRCNT_OFFS) ++ ++/* Port Status1 */ ++#define MV_GMAC_PORT_STATUS1_REG (0x0040) ++#define MV_GMAC_PORT_STATUS1_MEDIAACTIVE_OFFS 0 ++#define MV_GMAC_PORT_STATUS1_MEDIAACTIVE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_STATUS1_MEDIAACTIVE_OFFS) ++ ++/* Port Mib Counters Control */ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_REG (0x0044) ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_COPY_TRIGGER_OFFS 0 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_COPY_TRIGGER_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_COPY_TRIGGER_OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_CLEAR_ON_READ__OFFS 1 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_CLEAR_ON_READ__MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_CLEAR_ON_READ__OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_RX_HISTOGRAM_EN_OFFS 2 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_RX_HISTOGRAM_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_MIB_CNTRS_CTRL_RX_HISTOGRAM_EN_OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_TX_HISTOGRAM_EN_OFFS 3 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_TX_HISTOGRAM_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_MIB_CNTRS_CTRL_TX_HISTOGRAM_EN_OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MFA1_BTT940_FIX_ENABLE__OFFS 4 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MFA1_BTT940_FIX_ENABLE__MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_MFA1_BTT940_FIX_ENABLE__OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_XCAT_BTS_340_EN__OFFS 5 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_XCAT_BTS_340_EN__MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_XCAT_BTS_340_EN__OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_4_COUNT_HIST_OFFS 6 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_4_COUNT_HIST_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_4_COUNT_HIST_OFFS) ++ ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_4_LIMIT_1518_1522_OFFS 7 ++#define MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_4_LIMIT_1518_1522_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_MIB_CNTRS_CTRL_MIB_4_LIMIT_1518_1522_OFFS) ++ ++/* Port Mac Control3 */ ++#define MV_GMAC_PORT_CTRL3_REG (0x0048) ++#define MV_GMAC_PORT_CTRL3_BUF_SIZE_OFFS 0 ++#define MV_GMAC_PORT_CTRL3_BUF_SIZE_MASK \ ++ (0x0000003f << MV_GMAC_PORT_CTRL3_BUF_SIZE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL3_IPG_DATA_OFFS 6 ++#define MV_GMAC_PORT_CTRL3_IPG_DATA_MASK \ ++ (0x000001ff << MV_GMAC_PORT_CTRL3_IPG_DATA_OFFS) ++ ++#define MV_GMAC_PORT_CTRL3_LLFC_GLOBAL_FC_ENABLE_OFFS 15 ++#define MV_GMAC_PORT_CTRL3_LLFC_GLOBAL_FC_ENABLE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL3_LLFC_GLOBAL_FC_ENABLE_OFFS) ++ ++/* QSGMII */ ++#define MV_GMAC_QSGMII_REG (0x004c) ++#define MV_GMAC_QSGMII_QSGMII_REG_OFFS 0 ++#define MV_GMAC_QSGMII_QSGMII_REG_MASK \ ++ (0x0000ffff << MV_GMAC_QSGMII_QSGMII_REG_OFFS) ++ ++/* Qsgmii Status */ ++#define MV_GMAC_QSGMII_STATUS_REG (0x0050) ++#define MV_GMAC_QSGMII_STATUS_QSGMII_STATUS_OFFS 0 ++#define MV_GMAC_QSGMII_STATUS_QSGMII_STATUS_MASK \ ++ (0x000000ff << MV_GMAC_QSGMII_STATUS_QSGMII_STATUS_OFFS) ++ ++/* Qsgmii Prbs Counter */ ++#define MV_GMAC_QSGMII_PRBS_CNTR_REG (0x0054) ++#define MV_GMAC_QSGMII_PRBS_CNTR_QSGMII_PRBS_ERR_CNT_REG_OFFS 0 ++#define MV_GMAC_QSGMII_PRBS_CNTR_QSGMII_PRBS_ERR_CNT_REG_MASK \ ++ (0x0000ffff << \ ++ MV_GMAC_QSGMII_PRBS_CNTR_QSGMII_PRBS_ERR_CNT_REG_OFFS) ++ ++/* Ccfc Port Speed Timer%p */ ++#define MV_GMAC_CCFC_PORT_SPEED_TIMER_REG(t) (0x0058 + t * 4) ++#define MV_GMAC_CCFC_PORT_SPEED_TIMER_PORTSPEEDTIMER_OFFS 0 ++#define MV_GMAC_CCFC_PORT_SPEED_TIMER_PORTSPEEDTIMER_MASK \ ++ (0x0000ffff << \ ++ MV_GMAC_CCFC_PORT_SPEED_TIMER_PORTSPEEDTIMER_OFFS) ++ ++/* Fc Dsa Tag %n */ ++#define MV_GMAC_FC_DSA_TAG_REG(n) (0x0078 + 4 * n) ++#define MV_GMAC_FC_DSA_TAG_DSATAGREGN_OFFS 0 ++#define MV_GMAC_FC_DSA_TAG_DSATAGREGN_MASK \ ++ (0x0000ffff << MV_GMAC_FC_DSA_TAG_DSATAGREGN_OFFS) ++ ++/* Link Level Flow Control Window Reg 0 */ ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0 (0x0088) ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0_LLFC_FC_WINDOW_REG0_OFFS 0 ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0_LLFC_FC_WINDOW_REG0_MASK \ ++ (0x0000ffff << \ ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0_LLFC_FC_WINDOW_REG0_OFFS) ++ ++/* Link Level Flow Control Window Reg 1 */ ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1 (0x008c) ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1_LLFC_FC_WINDOW_REG1_OFFS 0 ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1_LLFC_FC_WINDOW_REG1_MASK \ ++ (0x00007fff << \ ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1_LLFC_FC_WINDOW_REG1_OFFS) ++ ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1_LLFC_RATE_LIMIT_EN_OFFS 15 ++#define MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1_LLFC_RATE_LIMIT_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1_LLFC_RATE_LIMIT_EN_OFFS) ++ ++/* Port Mac Control4 */ ++#define MV_GMAC_PORT_CTRL4_REG (0x0090) ++#define MV_GMAC_PORT_CTRL4_EXT_PIN_GMII_SEL_OFFS 0 ++#define MV_GMAC_PORT_CTRL4_EXT_PIN_GMII_SEL_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_EXT_PIN_GMII_SEL_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_PREAMBLE_FIX_OFFS 1 ++#define MV_GMAC_PORT_CTRL4_PREAMBLE_FIX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_PREAMBLE_FIX_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_SQ_DETECT_FIX_EN_OFFS 2 ++#define MV_GMAC_PORT_CTRL4_SQ_DETECT_FIX_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_SQ_DETECT_FIX_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_FC_EN_RX_OFFS 3 ++#define MV_GMAC_PORT_CTRL4_FC_EN_RX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_FC_EN_RX_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_FC_EN_TX_OFFS 4 ++#define MV_GMAC_PORT_CTRL4_FC_EN_TX_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_FC_EN_TX_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_DP_CLK_SEL_OFFS 5 ++#define MV_GMAC_PORT_CTRL4_DP_CLK_SEL_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_DP_CLK_SEL_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_SYNC_BYPASS_OFFS 6 ++#define MV_GMAC_PORT_CTRL4_SYNC_BYPASS_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_SYNC_BYPASS_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_OFFS 7 ++#define MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_QSGMII_BYPASS_ACTIVE_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_COUNT_EXTERNAL_FC_EN_OFFS 8 ++#define MV_GMAC_PORT_CTRL4_COUNT_EXTERNAL_FC_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_COUNT_EXTERNAL_FC_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_MARVELL_HEADER_EN_OFFS 9 ++#define MV_GMAC_PORT_CTRL4_MARVELL_HEADER_EN_MASK \ ++ (0x00000001 << MV_GMAC_PORT_CTRL4_MARVELL_HEADER_EN_OFFS) ++ ++#define MV_GMAC_PORT_CTRL4_LEDS_NUMBER_OFFS 10 ++#define MV_GMAC_PORT_CTRL4_LEDS_NUMBER_MASK \ ++ (0x0000003f << MV_GMAC_PORT_CTRL4_LEDS_NUMBER_OFFS) ++ ++/* Port Serial Parameters 1 Configuration */ ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_REG (0x0094) ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_RX_STANDARD_PRBS7_OFFS 0 ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_RX_STANDARD_PRBS7_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_1_CFG_RX_STANDARD_PRBS7_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_FORWARD_PFC_EN_OFFS 1 ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_FORWARD_PFC_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_1_CFG_FORWARD_PFC_EN_OFFS) ++ ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_FORWARD_UNKNOWN_FC_EN_OFFS 2 ++#define MV_GMAC_PORT_SERIAL_PARAM_1_CFG_FORWARD_UNKNOWN_FC_EN_MASK \ ++ (0x00000001 << \ ++ MV_GMAC_PORT_SERIAL_PARAM_1_CFG_FORWARD_UNKNOWN_FC_EN_OFFS) ++ ++/* Lpi Control 0 */ ++#define MV_GMAC_LPI_CTRL_0_REG (0x00c0) ++#define MV_GMAC_LPI_CTRL_0_LI_LIMIT_OFFS 0 ++#define MV_GMAC_LPI_CTRL_0_LI_LIMIT_MASK \ ++ (0x000000ff << MV_GMAC_LPI_CTRL_0_LI_LIMIT_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_0_TS_LIMIT_OFFS 8 ++#define MV_GMAC_LPI_CTRL_0_TS_LIMIT_MASK \ ++ (0x000000ff << MV_GMAC_LPI_CTRL_0_TS_LIMIT_OFFS) ++ ++/* Lpi Control 1 */ ++#define MV_GMAC_LPI_CTRL_1_REG (0x00c4) ++#define MV_GMAC_LPI_CTRL_1_LPI_REQUEST_EN_OFFS 0 ++#define MV_GMAC_LPI_CTRL_1_LPI_REQUEST_EN_MASK \ ++ (0x00000001 << MV_GMAC_LPI_CTRL_1_LPI_REQUEST_EN_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_1_LPI_REQUEST_FORCE_OFFS 1 ++#define MV_GMAC_LPI_CTRL_1_LPI_REQUEST_FORCE_MASK \ ++ (0x00000001 << MV_GMAC_LPI_CTRL_1_LPI_REQUEST_FORCE_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_1_LPI_MANUAL_MODE_OFFS 2 ++#define MV_GMAC_LPI_CTRL_1_LPI_MANUAL_MODE_MASK \ ++ (0x00000001 << MV_GMAC_LPI_CTRL_1_LPI_MANUAL_MODE_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_1_EN_GTX_CLK_HALT_OFFS 3 ++#define MV_GMAC_LPI_CTRL_1_EN_GTX_CLK_HALT_MASK \ ++ (0x00000001 << MV_GMAC_LPI_CTRL_1_EN_GTX_CLK_HALT_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_1_TW_LIMIT_OFFS 4 ++#define MV_GMAC_LPI_CTRL_1_TW_LIMIT_MASK \ ++ (0x00000fff << MV_GMAC_LPI_CTRL_1_TW_LIMIT_OFFS) ++ ++/* Lpi Control 2 */ ++#define MV_GMAC_LPI_CTRL_2_REG (0x00c8) ++#define MV_GMAC_LPI_CTRL_2_LPI_CLK_DIV_OFFS 0 ++#define MV_GMAC_LPI_CTRL_2_LPI_CLK_DIV_MASK \ ++ (0x0000007f << MV_GMAC_LPI_CTRL_2_LPI_CLK_DIV_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_2_PCS_RX_ER_MASK_DISABLE_OFFS 7 ++#define MV_GMAC_LPI_CTRL_2_PCS_RX_ER_MASK_DISABLE_MASK \ ++ (0x00000001 << MV_GMAC_LPI_CTRL_2_PCS_RX_ER_MASK_DISABLE_OFFS) ++ ++#define MV_GMAC_LPI_CTRL_2_EN_GMII2MII_LPI_FIX_OFFS 8 ++#define MV_GMAC_LPI_CTRL_2_EN_GMII2MII_LPI_FIX_MASK \ ++ (0x00000001 << MV_GMAC_LPI_CTRL_2_EN_GMII2MII_LPI_FIX_OFFS) ++ ++/* Lpi Status */ ++#define MV_GMAC_LPI_STATUS_REG (0x00cc) ++#define MV_GMAC_LPI_STATUS_PCS_RX_LPI_STATUS_OFFS 0 ++#define MV_GMAC_LPI_STATUS_PCS_RX_LPI_STATUS_MASK \ ++ (0x00000001 << MV_GMAC_LPI_STATUS_PCS_RX_LPI_STATUS_OFFS) ++ ++#define MV_GMAC_LPI_STATUS_PCS_TX_LPI_STATUS_OFFS 1 ++#define MV_GMAC_LPI_STATUS_PCS_TX_LPI_STATUS_MASK \ ++ (0x00000001 << MV_GMAC_LPI_STATUS_PCS_TX_LPI_STATUS_OFFS) ++ ++#define MV_GMAC_LPI_STATUS_MAC_RX_LP_IDLE_STATUS_OFFS 2 ++#define MV_GMAC_LPI_STATUS_MAC_RX_LP_IDLE_STATUS_MASK \ ++ (0x00000001 << MV_GMAC_LPI_STATUS_MAC_RX_LP_IDLE_STATUS_OFFS) ++ ++#define MV_GMAC_LPI_STATUS_MAC_TX_LP_WAIT_STATUS_OFFS 3 ++#define MV_GMAC_LPI_STATUS_MAC_TX_LP_WAIT_STATUS_MASK \ ++ (0x00000001 << MV_GMAC_LPI_STATUS_MAC_TX_LP_WAIT_STATUS_OFFS) ++ ++#define MV_GMAC_LPI_STATUS_MAC_TX_LP_IDLE_STATUS_OFFS 4 ++#define MV_GMAC_LPI_STATUS_MAC_TX_LP_IDLE_STATUS_MASK \ ++ (0x00000001 << MV_GMAC_LPI_STATUS_MAC_TX_LP_IDLE_STATUS_OFFS) ++ ++/* Lpi Counter */ ++#define MV_GMAC_LPI_CNTR_REG (0x00d0) ++#define MV_GMAC_LPI_CNTR_LPI_COUNTER_OFFS 0 ++#define MV_GMAC_LPI_CNTR_LPI_COUNTER_MASK \ ++ (0x0000ffff << MV_GMAC_LPI_CNTR_LPI_COUNTER_OFFS) ++ ++/* Pulse 1 Ms Low */ ++#define MV_GMAC_PULSE_1_MS_LOW_REG (0x00d4) ++#define MV_GMAC_PULSE_1_MS_LOW_PULSE_1MS_MAX_LOW_OFFS 0 ++#define MV_GMAC_PULSE_1_MS_LOW_PULSE_1MS_MAX_LOW_MASK \ ++ (0x0000ffff << MV_GMAC_PULSE_1_MS_LOW_PULSE_1MS_MAX_LOW_OFFS) ++ ++/* Pulse 1 Ms High */ ++#define MV_GMAC_PULSE_1_MS_HIGH_REG (0x00d8) ++#define MV_GMAC_PULSE_1_MS_HIGH_PULSE_1MS_MAX_HIGH_OFFS 0 ++#define MV_GMAC_PULSE_1_MS_HIGH_PULSE_1MS_MAX_HIGH_MASK \ ++ (0x0000ffff << MV_GMAC_PULSE_1_MS_HIGH_PULSE_1MS_MAX_HIGH_OFFS) ++ ++/* Port Interrupt Cause */ ++#define MV_GMAC_INTERRUPT_CAUSE_REG (0x0020) ++/* Port Interrupt Mask */ ++#define MV_GMAC_INTERRUPT_MASK_REG (0x0024) ++#define MV_GMAC_INTERRUPT_CAUSE_LINK_CHANGE_OFFS 1 ++#define MV_GMAC_INTERRUPT_CAUSE_LINK_CHANGE_MASK (0x1 << \ ++ MV_GMAC_INTERRUPT_CAUSE_LINK_CHANGE_OFFS) ++#define MV_GMAC_TX_FIFO_UNDERRUN_OFFS 9 ++#define MV_GMAC_TX_FIFO_UNDERRUN_MASK (0x1 << \ ++ MV_GMAC_TX_FIFO_UNDERRUN_OFFS) ++ ++/* Port Interrupt Summary Cause */ ++#define MV_GMAC_INTERRUPT_SUM_CAUSE_REG (0x00A0) ++/* Port Interrupt Summary Mask */ ++#define MV_GMAC_INTERRUPT_SUM_MASK_REG (0x00A4) ++#define MV_GMAC_INTERRUPT_SUM_CAUSE_LINK_CHANGE_OFFS 1 ++#define MV_GMAC_INTERRUPT_SUM_CAUSE_LINK_CHANGE_MASK (0x1 << \ ++ MV_GMAC_INTERRUPT_SUM_CAUSE_LINK_CHANGE_OFFS) ++ ++/**************/ ++/* XLGMAC REGS */ ++/**************/ ++ ++/* Port Mac Control0 */ ++#define MV_XLG_PORT_MAC_CTRL0_REG (0x0000) ++#define MV_XLG_MAC_CTRL0_PORTEN_OFFS 0 ++#define MV_XLG_MAC_CTRL0_PORTEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_PORTEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_MACRESETN_OFFS 1 ++#define MV_XLG_MAC_CTRL0_MACRESETN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_MACRESETN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_FORCELINKDOWN_OFFS 2 ++#define MV_XLG_MAC_CTRL0_FORCELINKDOWN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_FORCELINKDOWN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_FORCELINKPASS_OFFS 3 ++#define MV_XLG_MAC_CTRL0_FORCELINKPASS_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_FORCELINKPASS_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_TXIPGMODE_OFFS 5 ++#define MV_XLG_MAC_CTRL0_TXIPGMODE_MASK \ ++ (0x00000003 << MV_XLG_MAC_CTRL0_TXIPGMODE_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_RXFCEN_OFFS 7 ++#define MV_XLG_MAC_CTRL0_RXFCEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_RXFCEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_TXFCEN_OFFS 8 ++#define MV_XLG_MAC_CTRL0_TXFCEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_TXFCEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_RXCRCCHECKEN_OFFS 9 ++#define MV_XLG_MAC_CTRL0_RXCRCCHECKEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_RXCRCCHECKEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_PERIODICXONEN_OFFS 10 ++#define MV_XLG_MAC_CTRL0_PERIODICXONEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_PERIODICXONEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_RXCRCSTRIPEN_OFFS 11 ++#define MV_XLG_MAC_CTRL0_RXCRCSTRIPEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_RXCRCSTRIPEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_PADDINGDIS_OFFS 13 ++#define MV_XLG_MAC_CTRL0_PADDINGDIS_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_PADDINGDIS_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_MIBCNTDIS_OFFS 14 ++#define MV_XLG_MAC_CTRL0_MIBCNTDIS_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_MIBCNTDIS_OFFS) ++ ++#define MV_XLG_MAC_CTRL0_PFC_CASCADE_PORT_ENABLE_OFFS 15 ++#define MV_XLG_MAC_CTRL0_PFC_CASCADE_PORT_ENABLE_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL0_PFC_CASCADE_PORT_ENABLE_OFFS) ++ ++/* Port Mac Control1 */ ++#define MV_XLG_PORT_MAC_CTRL1_REG (0x0004) ++#define MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_OFFS 0 ++#define MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_MASK \ ++ (0x00001fff << MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_OFFS) ++ ++#define MV_XLG_MAC_CTRL1_MACLOOPBACKEN_OFFS 13 ++#define MV_XLG_MAC_CTRL1_MACLOOPBACKEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL1_MACLOOPBACKEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL1_XGMIILOOPBACKEN_OFFS 14 ++#define MV_XLG_MAC_CTRL1_XGMIILOOPBACKEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL1_XGMIILOOPBACKEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL1_LOOPBACKCLOCKSELECT_OFFS 15 ++#define MV_XLG_MAC_CTRL1_LOOPBACKCLOCKSELECT_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL1_LOOPBACKCLOCKSELECT_OFFS) ++ ++/* Port Mac Control2 */ ++#define MV_XLG_PORT_MAC_CTRL2_REG (0x0008) ++#define MV_XLG_MAC_CTRL2_SALOW_7_0_OFFS 0 ++#define MV_XLG_MAC_CTRL2_SALOW_7_0_MASK \ ++ (0x000000ff << MV_XLG_MAC_CTRL2_SALOW_7_0_OFFS) ++ ++#define MV_XLG_MAC_CTRL2_UNIDIRECTIONALEN_OFFS 8 ++#define MV_XLG_MAC_CTRL2_UNIDIRECTIONALEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL2_UNIDIRECTIONALEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL2_FIXEDIPGBASE_OFFS 9 ++#define MV_XLG_MAC_CTRL2_FIXEDIPGBASE_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL2_FIXEDIPGBASE_OFFS) ++ ++#define MV_XLG_MAC_CTRL2_PERIODICXOFFEN_OFFS 10 ++#define MV_XLG_MAC_CTRL2_PERIODICXOFFEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL2_PERIODICXOFFEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL2_SIMPLEXMODEEN_OFFS 13 ++#define MV_XLG_MAC_CTRL2_SIMPLEXMODEEN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL2_SIMPLEXMODEEN_OFFS) ++ ++#define MV_XLG_MAC_CTRL2_FC_MODE_OFFS 14 ++#define MV_XLG_MAC_CTRL2_FC_MODE_MASK \ ++ (0x00000003 << MV_XLG_MAC_CTRL2_FC_MODE_OFFS) ++ ++/* Port Status */ ++#define MV_XLG_MAC_PORT_STATUS_REG (0x000c) ++#define MV_XLG_MAC_PORT_STATUS_LINKSTATUS_OFFS 0 ++#define MV_XLG_MAC_PORT_STATUS_LINKSTATUS_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_LINKSTATUS_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_REMOTEFAULT_OFFS 1 ++#define MV_XLG_MAC_PORT_STATUS_REMOTEFAULT_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_REMOTEFAULT_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_LOCALFAULT_OFFS 2 ++#define MV_XLG_MAC_PORT_STATUS_LOCALFAULT_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_LOCALFAULT_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_LINKSTATUSCLEAN_OFFS 3 ++#define MV_XLG_MAC_PORT_STATUS_LINKSTATUSCLEAN_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_LINKSTATUSCLEAN_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_LOCALFAULTCLEAN_OFFS 4 ++#define MV_XLG_MAC_PORT_STATUS_LOCALFAULTCLEAN_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_LOCALFAULTCLEAN_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_REMOTEFAULTCLEAN_OFFS 5 ++#define MV_XLG_MAC_PORT_STATUS_REMOTEFAULTCLEAN_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_REMOTEFAULTCLEAN_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_PORTRXPAUSE_OFFS 6 ++#define MV_XLG_MAC_PORT_STATUS_PORTRXPAUSE_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_PORTRXPAUSE_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_PORTTXPAUSE_OFFS 7 ++#define MV_XLG_MAC_PORT_STATUS_PORTTXPAUSE_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_PORTTXPAUSE_OFFS) ++ ++#define MV_XLG_MAC_PORT_STATUS_PFC_SYNC_FIFO_FULL_OFFS 8 ++#define MV_XLG_MAC_PORT_STATUS_PFC_SYNC_FIFO_FULL_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_STATUS_PFC_SYNC_FIFO_FULL_OFFS) ++ ++/* Port Fifos Thresholds Configuration */ ++#define MV_XLG_PORT_FIFOS_THRS_CFG_REG (0x0010) ++#define MV_XLG_MAC_PORT_FIFOS_THRS_CFG_RXFULLTHR_OFFS 0 ++#define MV_XLG_MAC_PORT_FIFOS_THRS_CFG_RXFULLTHR_MASK \ ++ (0x0000001f << MV_XLG_MAC_PORT_FIFOS_THRS_CFG_RXFULLTHR_OFFS) ++ ++#define MV_XLG_MAC_PORT_FIFOS_THRS_CFG_TXFIFOSIZE_OFFS 5 ++#define MV_XLG_MAC_PORT_FIFOS_THRS_CFG_TXFIFOSIZE_MASK \ ++ (0x0000003f << MV_XLG_MAC_PORT_FIFOS_THRS_CFG_TXFIFOSIZE_OFFS) ++ ++#define MV_XLG_MAC_PORT_FIFOS_THRS_CFG_TXRDTHR_OFFS 11 ++#define MV_XLG_MAC_PORT_FIFOS_THRS_CFG_TXRDTHR_MASK \ ++ (0x0000001f << MV_XLG_MAC_PORT_FIFOS_THRS_CFG_TXRDTHR_OFFS) ++ ++/* Port Mac Control3 */ ++#define MV_XLG_PORT_MAC_CTRL3_REG (0x001c) ++#define MV_XLG_MAC_CTRL3_BUFSIZE_OFFS 0 ++#define MV_XLG_MAC_CTRL3_BUFSIZE_MASK \ ++ (0x0000003f << MV_XLG_MAC_CTRL3_BUFSIZE_OFFS) ++ ++#define MV_XLG_MAC_CTRL3_XTRAIPG_OFFS 6 ++#define MV_XLG_MAC_CTRL3_XTRAIPG_MASK \ ++ (0x0000007f << MV_XLG_MAC_CTRL3_XTRAIPG_OFFS) ++ ++#define MV_XLG_MAC_CTRL3_MACMODESELECT_OFFS 13 ++#define MV_XLG_MAC_CTRL3_MACMODESELECT_MASK \ ++ (0x00000007 << MV_XLG_MAC_CTRL3_MACMODESELECT_OFFS) ++ ++/* Port Per Prio Flow Control Status */ ++#define MV_XLG_PORT_PER_PRIO_FLOW_CTRL_STATUS_REG (0x0020) ++#define MV_XLG_MAC_PORT_PER_PRIO_FLOW_CTRL_STATUS_PRIONSTATUS_OFFS 0 ++#define MV_XLG_MAC_PORT_PER_PRIO_FLOW_CTRL_STATUS_PRIONSTATUS_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_PORT_PER_PRIO_FLOW_CTRL_STATUS_PRIONSTATUS_OFFS) ++ ++/* Debug Bus Status */ ++#define MV_XLG_DEBUG_BUS_STATUS_REG (0x0024) ++#define MV_XLG_MAC_DEBUG_BUS_STATUS_DEBUG_BUS_OFFS 0 ++#define MV_XLG_MAC_DEBUG_BUS_STATUS_DEBUG_BUS_MASK \ ++ (0x0000ffff << MV_XLG_MAC_DEBUG_BUS_STATUS_DEBUG_BUS_OFFS) ++ ++/* Port Metal Fix */ ++#define MV_XLG_PORT_METAL_FIX_REG (0x002c) ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_EOP_IN_FIFO__OFFS 0 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_EOP_IN_FIFO__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_EOP_IN_FIFO__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_LTF_FIX__OFFS 1 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_LTF_FIX__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_LTF_FIX__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_HOLD_FIX__OFFS 2 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_HOLD_FIX__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_HOLD_FIX__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_LED_FIX__OFFS 3 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_LED_FIX__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_LED_FIX__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_PAD_PROTECT__OFFS 4 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_PAD_PROTECT__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_PAD_PROTECT__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_NX_BTS44__OFFS 5 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_NX_BTS44__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_NX_BTS44__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_NX_BTS42__OFFS 6 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_NX_BTS42__MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_NX_BTS42__OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_FLUSH_FIX_OFFS 7 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_FLUSH_FIX_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_FLUSH_FIX_OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_PORT_EN_FIX_OFFS 8 ++#define MV_XLG_MAC_PORT_METAL_FIX_EN_PORT_EN_FIX_MASK \ ++ (0x00000001 << MV_XLG_MAC_PORT_METAL_FIX_EN_PORT_EN_FIX_OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_SPARE_DEF0_BITS_OFFS 9 ++#define MV_XLG_MAC_PORT_METAL_FIX_SPARE_DEF0_BITS_MASK \ ++ (0x0000000f << MV_XLG_MAC_PORT_METAL_FIX_SPARE_DEF0_BITS_OFFS) ++ ++#define MV_XLG_MAC_PORT_METAL_FIX_SPARE_DEF1_BITS_OFFS 13 ++#define MV_XLG_MAC_PORT_METAL_FIX_SPARE_DEF1_BITS_MASK \ ++ (0x00000007 << MV_XLG_MAC_PORT_METAL_FIX_SPARE_DEF1_BITS_OFFS) ++ ++/* Xg Mib Counters Control */ ++#define MV_XLG_MIB_CNTRS_CTRL_REG (0x0030) ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGCAPTURETRIGGER_OFFS 0 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGCAPTURETRIGGER_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGCAPTURETRIGGER_OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGDONTCLEARAFTERREAD_OFFS 1 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGDONTCLEARAFTERREAD_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGDONTCLEARAFTERREAD_OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGRXHISTOGRAMEN_OFFS 2 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGRXHISTOGRAMEN_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGRXHISTOGRAMEN_OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGTXHISTOGRAMEN_OFFS 3 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGTXHISTOGRAMEN_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_XGTXHISTOGRAMEN_OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MFA1_BTT940_FIX_ENABLE__OFFS 4 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MFA1_BTT940_FIX_ENABLE__MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MFA1_BTT940_FIX_ENABLE__OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_LEDS_NUMBER_OFFS 5 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_LEDS_NUMBER_MASK \ ++ (0x0000003f << MV_XLG_MAC_XG_MIB_CNTRS_CTRL_LEDS_NUMBER_OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MIB_4_COUNT_HIST_OFFS 11 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MIB_4_COUNT_HIST_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MIB_4_COUNT_HIST_OFFS) ++ ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MIB_4_LIMIT_1518_1522_OFFS 12 ++#define MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MIB_4_LIMIT_1518_1522_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_XG_MIB_CNTRS_CTRL_MIB_4_LIMIT_1518_1522_OFFS) ++ ++/* Cn/ccfc Timer%i */ ++#define MV_XLG_CNCCFC_TIMERI_REG(t) ((0x0038 + t * 4)) ++#define MV_XLG_MAC_CNCCFC_TIMERI_PORTSPEEDTIMER_OFFS 0 ++#define MV_XLG_MAC_CNCCFC_TIMERI_PORTSPEEDTIMER_MASK \ ++ (0x0000ffff << MV_XLG_MAC_CNCCFC_TIMERI_PORTSPEEDTIMER_OFFS) ++ ++/* Ppfc Control */ ++#define MV_XLG_MAC_PPFC_CTRL_REG (0x0060) ++#define MV_XLG_MAC_PPFC_CTRL_GLOBAL_PAUSE_ENI_OFFS 0 ++#define MV_XLG_MAC_PPFC_CTRL_GLOBAL_PAUSE_ENI_MASK \ ++ (0x00000001 << MV_XLG_MAC_PPFC_CTRL_GLOBAL_PAUSE_ENI_OFFS) ++ ++#define MV_XLG_MAC_PPFC_CTRL_DIP_BTS_677_EN_OFFS 9 ++#define MV_XLG_MAC_PPFC_CTRL_DIP_BTS_677_EN_MASK \ ++ (0x00000001 << MV_XLG_MAC_PPFC_CTRL_DIP_BTS_677_EN_OFFS) ++ ++/* Fc Dsa Tag 0 */ ++#define MV_XLG_MAC_FC_DSA_TAG_0_REG (0x0068) ++#define MV_XLG_MAC_FC_DSA_TAG_0_DSATAGREG0_OFFS 0 ++#define MV_XLG_MAC_FC_DSA_TAG_0_DSATAGREG0_MASK \ ++ (0x0000ffff << MV_XLG_MAC_FC_DSA_TAG_0_DSATAGREG0_OFFS) ++ ++/* Fc Dsa Tag 1 */ ++#define MV_XLG_MAC_FC_DSA_TAG_1_REG (0x006c) ++#define MV_XLG_MAC_FC_DSA_TAG_1_DSATAGREG1_OFFS 0 ++#define MV_XLG_MAC_FC_DSA_TAG_1_DSATAGREG1_MASK \ ++ (0x0000ffff << MV_XLG_MAC_FC_DSA_TAG_1_DSATAGREG1_OFFS) ++ ++/* Fc Dsa Tag 2 */ ++#define MV_XLG_MAC_FC_DSA_TAG_2_REG (0x0070) ++#define MV_XLG_MAC_FC_DSA_TAG_2_DSATAGREG2_OFFS 0 ++#define MV_XLG_MAC_FC_DSA_TAG_2_DSATAGREG2_MASK \ ++ (0x0000ffff << MV_XLG_MAC_FC_DSA_TAG_2_DSATAGREG2_OFFS) ++ ++/* Fc Dsa Tag 3 */ ++#define MV_XLG_MAC_FC_DSA_TAG_3_REG (0x0074) ++#define MV_XLG_MAC_FC_DSA_TAG_3_DSATAGREG3_OFFS 0 ++#define MV_XLG_MAC_FC_DSA_TAG_3_DSATAGREG3_MASK \ ++ (0x0000ffff << MV_XLG_MAC_FC_DSA_TAG_3_DSATAGREG3_OFFS) ++ ++/* Dic Budget Compensation */ ++#define MV_XLG_MAC_DIC_BUDGET_COMPENSATION_REG (0x0080) ++#define MV_XLG_MAC_DIC_BUDGET_COMPENSATION_DIC_COUNTER_TO_ADD_8BYTES_OFFS 0 ++#define MV_XLG_MAC_DIC_BUDGET_COMPENSATION_DIC_COUNTER_TO_ADD_8BYTES_MASK \ ++ (0x0000ffff << \ ++ MV_XLG_MAC_DIC_BUDGET_COMPENSATION_DIC_COUNTER_TO_ADD_8BYTES_OFFS) ++ ++/* Port Mac Control4 */ ++#define MV_XLG_PORT_MAC_CTRL4_REG (0x0084) ++#define MV_XLG_MAC_CTRL4_LLFC_GLOBAL_FC_ENABLE_OFFS 0 ++#define MV_XLG_MAC_CTRL4_LLFC_GLOBAL_FC_ENABLE_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_LLFC_GLOBAL_FC_ENABLE_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_LED_STREAM_SELECT_OFFS 1 ++#define MV_XLG_MAC_CTRL4_LED_STREAM_SELECT_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_LED_STREAM_SELECT_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_DEBUG_BUS_SELECT_OFFS 2 ++#define MV_XLG_MAC_CTRL4_DEBUG_BUS_SELECT_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_DEBUG_BUS_SELECT_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_MASK_PCS_RESET_OFFS 3 ++#define MV_XLG_MAC_CTRL4_MASK_PCS_RESET_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_MASK_PCS_RESET_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_ENABLE_SHORT_PREAMBLE_FOR_XLG_OFFS 4 ++#define MV_XLG_MAC_CTRL4_ENABLE_SHORT_PREAMBLE_FOR_XLG_MASK \ ++ (0x00000001 << \ ++ MV_XLG_MAC_CTRL4_ENABLE_SHORT_PREAMBLE_FOR_XLG_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_FORWARD_802_3X_FC_EN_OFFS 5 ++#define MV_XLG_MAC_CTRL4_FORWARD_802_3X_FC_EN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_FORWARD_802_3X_FC_EN_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_FORWARD_PFC_EN_OFFS 6 ++#define MV_XLG_MAC_CTRL4_FORWARD_PFC_EN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_FORWARD_PFC_EN_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_FORWARD_UNKNOWN_FC_EN_OFFS 7 ++#define MV_XLG_MAC_CTRL4_FORWARD_UNKNOWN_FC_EN_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_FORWARD_UNKNOWN_FC_EN_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_USE_XPCS_OFFS 8 ++#define MV_XLG_MAC_CTRL4_USE_XPCS_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_USE_XPCS_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_DMA_INTERFACE_IS_64_BIT_OFFS 9 ++#define MV_XLG_MAC_CTRL4_DMA_INTERFACE_IS_64_BIT_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_DMA_INTERFACE_IS_64_BIT_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_TX_DMA_INTERFACE_BITS_OFFS 10 ++#define MV_XLG_MAC_CTRL4_TX_DMA_INTERFACE_BITS_MASK \ ++ (0x00000003 << MV_XLG_MAC_CTRL4_TX_DMA_INTERFACE_BITS_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_OFFS 12 ++#define MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_MAC_MODE_DMA_1G_OFFS) ++ ++#define MV_XLG_MAC_CTRL4_EN_IDLE_CHECK_FOR_LINK_OFFS 14 ++#define MV_XLG_MAC_CTRL4_EN_IDLE_CHECK_FOR_LINK_MASK \ ++ (0x00000001 << MV_XLG_MAC_CTRL4_EN_IDLE_CHECK_FOR_LINK_OFFS) ++ ++/* Port Mac Control5 */ ++#define MV_XLG_PORT_MAC_CTRL5_REG (0x0088) ++#define MV_XLG_MAC_CTRL5_TXIPGLENGTH_OFFS 0 ++#define MV_XLG_MAC_CTRL5_TXIPGLENGTH_MASK \ ++ (0x0000000f << MV_XLG_MAC_CTRL5_TXIPGLENGTH_OFFS) ++ ++#define MV_XLG_MAC_CTRL5_PREAMBLELENGTHTX_OFFS 4 ++#define MV_XLG_MAC_CTRL5_PREAMBLELENGTHTX_MASK \ ++ (0x00000007 << MV_XLG_MAC_CTRL5_PREAMBLELENGTHTX_OFFS) ++ ++#define MV_XLG_MAC_CTRL5_PREAMBLELENGTHRX_OFFS 7 ++#define MV_XLG_MAC_CTRL5_PREAMBLELENGTHRX_MASK \ ++ (0x00000007 << MV_XLG_MAC_CTRL5_PREAMBLELENGTHRX_OFFS) ++ ++#define MV_XLG_MAC_CTRL5_TXNUMCRCBYTES_OFFS 10 ++#define MV_XLG_MAC_CTRL5_TXNUMCRCBYTES_MASK \ ++ (0x00000007 << MV_XLG_MAC_CTRL5_TXNUMCRCBYTES_OFFS) ++ ++#define MV_XLG_MAC_CTRL5_RXNUMCRCBYTES_OFFS 13 ++#define MV_XLG_MAC_CTRL5_RXNUMCRCBYTES_MASK \ ++ (0x00000007 << MV_XLG_MAC_CTRL5_RXNUMCRCBYTES_OFFS) ++ ++/* External Control */ ++#define MV_XLG_MAC_EXT_CTRL_REG (0x0090) ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL0_OFFS 0 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL0_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL0_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL1_OFFS 1 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL1_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL1_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL2_OFFS 2 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL2_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL2_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL3_OFFS 3 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL3_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL3_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL4_OFFS 4 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL4_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL4_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL5_OFFS 5 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL5_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL5_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL6_OFFS 6 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL6_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL6_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL7_OFFS 7 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL7_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL7_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL8_OFFS 8 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL8_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL8_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL9_OFFS 9 ++#define MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL9_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXTERNAL_CTRL9_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_10_OFFS 10 ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_10_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXT_CTRL_10_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_11_OFFS 11 ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_11_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXT_CTRL_11_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_12_OFFS 12 ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_12_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXT_CTRL_12_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_13_OFFS 13 ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_13_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXT_CTRL_13_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_14_OFFS 14 ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_14_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXT_CTRL_14_OFFS) ++ ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_15_OFFS 15 ++#define MV_XLG_MAC_EXT_CTRL_EXT_CTRL_15_MASK \ ++ (0x00000001 << MV_XLG_MAC_EXT_CTRL_EXT_CTRL_15_OFFS) ++ ++/* Macro Control */ ++#define MV_XLG_MAC_MACRO_CTRL_REG (0x0094) ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_0_OFFS 0 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_0_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_0_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_1_OFFS 1 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_1_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_1_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_2_OFFS 2 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_2_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_2_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_3_OFFS 3 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_3_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_3_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_4_OFFS 4 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_4_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_4_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_5_OFFS 5 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_5_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_5_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_6_OFFS 6 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_6_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_6_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_7_OFFS 7 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_7_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_7_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_8_OFFS 8 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_8_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_8_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_9_OFFS 9 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_9_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_9_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_10_OFFS 10 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_10_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_10_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_11_OFFS 11 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_11_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_11_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_12_OFFS 12 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_12_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_12_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_13_OFFS 13 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_13_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_13_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_14_OFFS 14 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_14_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_14_OFFS) ++ ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_15_OFFS 15 ++#define MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_15_MASK \ ++ (0x00000001 << MV_XLG_MAC_MACRO_CTRL_MACRO_CTRL_15_OFFS) ++ ++#define MV_XLG_MAC_DIC_PPM_IPG_REDUCE_REG (0x0094) ++ ++/* Port Interrupt Cause */ ++#define MV_XLG_INTERRUPT_CAUSE_REG (0x0014) ++/* Port Interrupt Mask */ ++#define MV_XLG_INTERRUPT_MASK_REG (0x0018) ++#define MV_XLG_INTERRUPT_LINK_CHANGE_OFFS 1 ++#define MV_XLG_INTERRUPT_LINK_CHANGE_MASK (0x1 << \ ++ MV_XLG_INTERRUPT_LINK_CHANGE_OFFS) ++ ++/* Port Interrupt Summary Cause */ ++#define MV_XLG_EXTERNAL_INTERRUPT_CAUSE_REG (0x0058) ++/* Port Interrupt Summary Mask */ ++#define MV_XLG_EXTERNAL_INTERRUPT_MASK_REG (0x005C) ++#define MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_OFFS 1 ++#define MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_MASK (0x1 << \ ++ MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_OFFS) ++ ++/***********/ ++/*XPCS REGS */ ++/***********/ ++ ++/* Global Configuration 0 */ ++#define MV_XPCS_GLOBAL_CFG_0_REG (0x0) ++#define MV_XPCS_GLOBAL_CFG_0_PCSRESET_OFFS 0 ++#define MV_XPCS_GLOBAL_CFG_0_PCSRESET_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_0_PCSRESET_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_DESKEWRESET_OFFS 1 ++#define MV_XPCS_GLOBAL_CFG_0_DESKEWRESET_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_0_DESKEWRESET_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_TXRESET_OFFS 2 ++#define MV_XPCS_GLOBAL_CFG_0_TXRESET_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_0_TXRESET_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_PCSMODE_OFFS 3 ++#define MV_XPCS_GLOBAL_CFG_0_PCSMODE_MASK \ ++ (0x00000003 << MV_XPCS_GLOBAL_CFG_0_PCSMODE_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_LANEACTIVE_OFFS 5 ++#define MV_XPCS_GLOBAL_CFG_0_LANEACTIVE_MASK \ ++ (0x00000003 << MV_XPCS_GLOBAL_CFG_0_LANEACTIVE_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_INDIVIDUALMODE_OFFS 7 ++#define MV_XPCS_GLOBAL_CFG_0_INDIVIDUALMODE_MASK \ ++ (0x0000003f << MV_XPCS_GLOBAL_CFG_0_INDIVIDUALMODE_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_TXSMMODE_OFFS 13 ++#define MV_XPCS_GLOBAL_CFG_0_TXSMMODE_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_0_TXSMMODE_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_TXSMIDLECNTDISABLE_OFFS 14 ++#define MV_XPCS_GLOBAL_CFG_0_TXSMIDLECNTDISABLE_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_0_TXSMIDLECNTDISABLE_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_0_COMMADETCT2NDSYNCSMEN_OFFS 15 ++#define MV_XPCS_GLOBAL_CFG_0_COMMADETCT2NDSYNCSMEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_0_COMMADETCT2NDSYNCSMEN_OFFS) ++ ++/* Global Configuration 1 */ ++#define MV_XPCS_GLOBAL_CFG_1_REG (0x4) ++#define MV_XPCS_GLOBAL_CFG_1_MACLOOPBACKEN_OFFS 0 ++#define MV_XPCS_GLOBAL_CFG_1_MACLOOPBACKEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_MACLOOPBACKEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_PCSLOOPBACKEN_OFFS 1 ++#define MV_XPCS_GLOBAL_CFG_1_PCSLOOPBACKEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_PCSLOOPBACKEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_REPEATERMODEEN_OFFS 2 ++#define MV_XPCS_GLOBAL_CFG_1_REPEATERMODEEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_REPEATERMODEEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_LOOPBACKCLKSEL_OFFS 3 ++#define MV_XPCS_GLOBAL_CFG_1_LOOPBACKCLKSEL_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_LOOPBACKCLKSEL_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_DESKEWCLKSEL_OFFS 4 ++#define MV_XPCS_GLOBAL_CFG_1_DESKEWCLKSEL_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_DESKEWCLKSEL_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_TXSMREPEATERMODE_OFFS 5 ++#define MV_XPCS_GLOBAL_CFG_1_TXSMREPEATERMODE_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_TXSMREPEATERMODE_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_RXLOCKBYPASSEN_OFFS 6 ++#define MV_XPCS_GLOBAL_CFG_1_RXLOCKBYPASSEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_RXLOCKBYPASSEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_TXLOCKBYPASSEN_OFFS 7 ++#define MV_XPCS_GLOBAL_CFG_1_TXLOCKBYPASSEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_TXLOCKBYPASSEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_REMOTEFAULTDIS_OFFS 8 ++#define MV_XPCS_GLOBAL_CFG_1_REMOTEFAULTDIS_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_REMOTEFAULTDIS_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_SIGNALDETDOWNLOCALFAULTGENDIS_OFFS 9 ++#define MV_XPCS_GLOBAL_CFG_1_SIGNALDETDOWNLOCALFAULTGENDIS_MASK \ ++ (0x00000001 << \ ++ MV_XPCS_GLOBAL_CFG_1_SIGNALDETDOWNLOCALFAULTGENDIS_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_CJPATGENEN_OFFS 10 ++#define MV_XPCS_GLOBAL_CFG_1_CJPATGENEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_CJPATGENEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_CRPATGENEN_OFFS 11 ++#define MV_XPCS_GLOBAL_CFG_1_CRPATGENEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_CRPATGENEN_OFFS) ++ ++#define MV_XPCS_GLOBAL_CFG_1_CJRFORCEDISPEN_OFFS 12 ++#define MV_XPCS_GLOBAL_CFG_1_CJRFORCEDISPEN_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_CFG_1_CJRFORCEDISPEN_OFFS) ++ ++/* Global Fifo Threshold Configuration */ ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_REG (0x8) ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWTIMEOUTLIMIT_OFFS 0 ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWTIMEOUTLIMIT_MASK \ ++ (0x0000000f << \ ++ MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWTIMEOUTLIMIT_OFFS) ++ ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWFIFOWRADDRFIX_OFFS 4 ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWFIFOWRADDRFIX_MASK \ ++ (0x0000001f << \ ++ MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWFIFOWRADDRFIX_OFFS) ++ ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWFIFORDTH_OFFS 9 ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWFIFORDTH_MASK \ ++ (0x0000000f << MV_XPCS_GLOBAL_FIFO_THR_CFG_DESKEWFIFORDTH_OFFS) ++ ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_PPMFIFORDTH_OFFS 13 ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_PPMFIFORDTH_MASK \ ++ (0x00000007 << MV_XPCS_GLOBAL_FIFO_THR_CFG_PPMFIFORDTH_OFFS) ++ ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_PPMFIFOEXTRAIDLECHKDIS_OFFS 16 ++#define MV_XPCS_GLOBAL_FIFO_THR_CFG_PPMFIFOEXTRAIDLECHKDIS_MASK \ ++ (0x00000001 << \ ++ MV_XPCS_GLOBAL_FIFO_THR_CFG_PPMFIFOEXTRAIDLECHKDIS_OFFS) ++ ++/* Global Max Idle Counter */ ++#define MV_XPCS_GLOBAL_MAX_IDLE_CNTR_REG (0xc) ++#define MV_XPCS_GLOBAL_MAX_IDLE_CNTR_MAXIDLECNT_OFFS 0 ++#define MV_XPCS_GLOBAL_MAX_IDLE_CNTR_MAXIDLECNT_MASK \ ++ (0x0000ffff << MV_XPCS_GLOBAL_MAX_IDLE_CNTR_MAXIDLECNT_OFFS) ++ ++/* Global Status */ ++#define MV_XPCS_GLOBAL_STATUS_REG (0x10) ++#define MV_XPCS_GLOBAL_STATUS_LINKUP_OFFS 0 ++#define MV_XPCS_GLOBAL_STATUS_LINKUP_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_STATUS_LINKUP_OFFS) ++ ++#define MV_XPCS_GLOBAL_STATUS_DESKEWACQUIRED_OFFS 1 ++#define MV_XPCS_GLOBAL_STATUS_DESKEWACQUIRED_MASK \ ++ (0x00000001 << MV_XPCS_GLOBAL_STATUS_DESKEWACQUIRED_OFFS) ++ ++/* Global Deskew Error Counter */ ++#define MV_XPCS_GLOBAL_DESKEW_ERR_CNTR_REG (0x20) ++#define MV_XPCS_GLOBAL_DESKEW_ERR_CNTR_DESKEWERRCNT_OFFS 0 ++#define MV_XPCS_GLOBAL_DESKEW_ERR_CNTR_DESKEWERRCNT_MASK \ ++ (0x0000ffff << MV_XPCS_GLOBAL_DESKEW_ERR_CNTR_DESKEWERRCNT_OFFS) ++ ++/* Tx Packets Counter LSB */ ++#define MV_XPCS_TX_PCKTS_CNTR_LSB_REG (0x30) ++#define MV_XPCS_TX_PCKTS_CNTR_LSB_TXPCKTCNTRLSB_OFFS 0 ++#define MV_XPCS_TX_PCKTS_CNTR_LSB_TXPCKTCNTRLSB_MASK \ ++ (0x0000ffff << MV_XPCS_TX_PCKTS_CNTR_LSB_TXPCKTCNTRLSB_OFFS) ++ ++/* Tx Packets Counter MSB */ ++#define MV_XPCS_TX_PCKTS_CNTR_MSB_REG (0x34) ++#define MV_XPCS_TX_PCKTS_CNTR_MSB_TXPCKTCNTRMSB_OFFS 0 ++#define MV_XPCS_TX_PCKTS_CNTR_MSB_TXPCKTCNTRMSB_MASK \ ++ (0x0000ffff << MV_XPCS_TX_PCKTS_CNTR_MSB_TXPCKTCNTRMSB_OFFS) ++ ++/* XPCS per Lane registers */ ++ ++/* Lane Configuration 0 */ ++#define MV_XPCS_LANE_CFG_0_REG (0x50) ++#define MV_XPCS_LANE_CFG_0_TXRESETIND_OFFS 0 ++#define MV_XPCS_LANE_CFG_0_TXRESETIND_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_TXRESETIND_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_RXRESETIND_OFFS 1 ++#define MV_XPCS_LANE_CFG_0_RXRESETIND_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_RXRESETIND_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_INDIVIDUALLOOPBACK_OFFS 2 ++#define MV_XPCS_LANE_CFG_0_INDIVIDUALLOOPBACK_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_INDIVIDUALLOOPBACK_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_INDIVIDUALLINELOOPBACK_OFFS 3 ++#define MV_XPCS_LANE_CFG_0_INDIVIDUALLINELOOPBACK_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_INDIVIDUALLINELOOPBACK_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_TXSMBYPASSEN_OFFS 4 ++#define MV_XPCS_LANE_CFG_0_TXSMBYPASSEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_TXSMBYPASSEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_RXIDLEGENBYPASSEN_OFFS 5 ++#define MV_XPCS_LANE_CFG_0_RXIDLEGENBYPASSEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_RXIDLEGENBYPASSEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_SIGNALDETECTBYPASSEN_OFFS 6 ++#define MV_XPCS_LANE_CFG_0_SIGNALDETECTBYPASSEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_SIGNALDETECTBYPASSEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_CJPATCHKEN_OFFS 7 ++#define MV_XPCS_LANE_CFG_0_CJPATCHKEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_CJPATCHKEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_CRPATCHKEN_OFFS 8 ++#define MV_XPCS_LANE_CFG_0_CRPATCHKEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_CRPATCHKEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_PRBSCHECKEN_OFFS 11 ++#define MV_XPCS_LANE_CFG_0_PRBSCHECKEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_PRBSCHECKEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_TESTGENEN_OFFS 12 ++#define MV_XPCS_LANE_CFG_0_TESTGENEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_TESTGENEN_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_TESTMODE_OFFS 13 ++#define MV_XPCS_LANE_CFG_0_TESTMODE_MASK \ ++ (0x00000003 << MV_XPCS_LANE_CFG_0_TESTMODE_OFFS) ++ ++#define MV_XPCS_LANE_CFG_0_TESTMODEEN_OFFS 15 ++#define MV_XPCS_LANE_CFG_0_TESTMODEEN_MASK \ ++ (0x00000001 << MV_XPCS_LANE_CFG_0_TESTMODEEN_OFFS) ++ ++/* Lane Configuration 1 */ ++#define MV_XPCS_LANE_CFG_1_REG (0x54) ++#define MV_XPCS_LANE_CFG_1_LED0CTRL_OFFS 0 ++#define MV_XPCS_LANE_CFG_1_LED0CTRL_MASK \ ++ (0x0000000f << MV_XPCS_LANE_CFG_1_LED0CTRL_OFFS) ++ ++#define MV_XPCS_LANE_CFG_1_LED1CTRL_OFFS 4 ++#define MV_XPCS_LANE_CFG_1_LED1CTRL_MASK \ ++ (0x0000000f << MV_XPCS_LANE_CFG_1_LED1CTRL_OFFS) ++ ++#define MV_XPCS_LANE_CFG_1_TXSWPSEL_OFFS 8 ++#define MV_XPCS_LANE_CFG_1_TXSWPSEL_MASK \ ++ (0x00000007 << MV_XPCS_LANE_CFG_1_TXSWPSEL_OFFS) ++ ++#define MV_XPCS_LANE_CFG_1_RXSWPSEL_OFFS 11 ++#define MV_XPCS_LANE_CFG_1_RXSWPSEL_MASK \ ++ (0x00000007 << MV_XPCS_LANE_CFG_1_RXSWPSEL_OFFS) ++ ++/* Lane Status */ ++#define MV_XPCS_LANE_STATUS_REG (0x5c) ++#define MV_XPCS_LANE_STATUS_PRBSCHECKLOCKED_OFFS 0 ++#define MV_XPCS_LANE_STATUS_PRBSCHECKLOCKED_MASK \ ++ (0x00000001 << MV_XPCS_LANE_STATUS_PRBSCHECKLOCKED_OFFS) ++ ++#define MV_XPCS_LANE_STATUS_PLLLOCKED_OFFS 1 ++#define MV_XPCS_LANE_STATUS_PLLLOCKED_MASK \ ++ (0x00000001 << MV_XPCS_LANE_STATUS_PLLLOCKED_OFFS) ++ ++#define MV_XPCS_LANE_STATUS_SIGNALDETECTED_OFFS 2 ++#define MV_XPCS_LANE_STATUS_SIGNALDETECTED_MASK \ ++ (0x00000001 << MV_XPCS_LANE_STATUS_SIGNALDETECTED_OFFS) ++ ++#define MV_XPCS_LANE_STATUS_COMMADETECTED_OFFS 3 ++#define MV_XPCS_LANE_STATUS_COMMADETECTED_MASK \ ++ (0x00000001 << MV_XPCS_LANE_STATUS_COMMADETECTED_OFFS) ++ ++#define MV_XPCS_LANE_STATUS_SYNCOK_OFFS 4 ++#define MV_XPCS_LANE_STATUS_SYNCOK_MASK \ ++ (0x00000001 << MV_XPCS_LANE_STATUS_SYNCOK_OFFS) ++ ++/* Symbol Error Counter */ ++#define MV_XPCS_SYMBOL_ERR_CNTR_REG (0x68) ++#define MV_XPCS_SYMBOL_ERR_CNTR_SYMBOLERRCNT_OFFS 0 ++#define MV_XPCS_SYMBOL_ERR_CNTR_SYMBOLERRCNT_MASK \ ++ (0x0000ffff << MV_XPCS_SYMBOL_ERR_CNTR_SYMBOLERRCNT_OFFS) ++ ++/* Disparity Error Counter */ ++#define MV_XPCS_DISPARITY_ERR_CNTR_REG (0x6c) ++#define MV_XPCS_DISPARITY_ERR_CNTR_DISPARITYERRCNT_OFFS 0 ++#define MV_XPCS_DISPARITY_ERR_CNTR_DISPARITYERRCNT_MASK \ ++ (0x0000ffff << MV_XPCS_DISPARITY_ERR_CNTR_DISPARITYERRCNT_OFFS) ++ ++/* Prbs Error Counter */ ++#define MV_XPCS_PRBS_ERR_CNTR_REG (0x70) ++#define MV_XPCS_PRBS_ERR_CNTR_PRBSERRCNT_OFFS 0 ++#define MV_XPCS_PRBS_ERR_CNTR_PRBSERRCNT_MASK \ ++ (0x0000ffff << MV_XPCS_PRBS_ERR_CNTR_PRBSERRCNT_OFFS) ++ ++/* Rx Packets Counter LSB */ ++#define MV_XPCS_RX_PCKTS_CNTR_LSB_REG (0x74) ++#define MV_XPCS_RX_PCKTS_CNTR_LSB_RXPCKTCNTRLSB_OFFS 0 ++#define MV_XPCS_RX_PCKTS_CNTR_LSB_RXPCKTCNTRLSB_MASK \ ++ (0x0000ffff << MV_XPCS_RX_PCKTS_CNTR_LSB_RXPCKTCNTRLSB_OFFS) ++ ++/* Rx Packets Counter MSB */ ++#define MV_XPCS_RX_PCKTS_CNTR_MSB_REG (0x78) ++#define MV_XPCS_RX_PCKTS_CNTR_MSB_RXPCKTCNTRMSB_OFFS 0 ++#define MV_XPCS_RX_PCKTS_CNTR_MSB_RXPCKTCNTRMSB_MASK \ ++ (0x0000ffff << MV_XPCS_RX_PCKTS_CNTR_MSB_RXPCKTCNTRMSB_OFFS) ++ ++/* Rx Bad Packets Counter LSB */ ++#define MV_XPCS_RX_BAD_PCKTS_CNTR_LSB_REG (0x7c) ++#define MV_XPCS_RX_BAD_PCKTS_CNTR_LSB_RXBADPCKTCNTRLSB_OFFS 0 ++#define MV_XPCS_RX_BAD_PCKTS_CNTR_LSB_RXBADPCKTCNTRLSB_MASK \ ++ (0x0000ffff << \ ++ MV_XPCS_RX_BAD_PCKTS_CNTR_LSB_RXBADPCKTCNTRLSB_OFFS) ++ ++/* Rx Bad Packets Counter MSB */ ++#define MV_XPCS_RX_BAD_PCKTS_CNTR_MSB_REG (0x80) ++#define MV_XPCS_RX_BAD_PCKTS_CNTR_MSB_RXBADPCKTCNTRMSB_OFFS 0 ++#define MV_XPCS_RX_BAD_PCKTS_CNTR_MSB_RXBADPCKTCNTRMSB_MASK \ ++ (0x0000ffff << \ ++ MV_XPCS_RX_BAD_PCKTS_CNTR_MSB_RXBADPCKTCNTRMSB_OFFS) ++ ++/* Cyclic Data 0 */ ++#define MV_XPCS_CYCLIC_DATA_0_REG (0x84) ++#define MV_XPCS_CYCLIC_DATA_0_CYCLICDATA0_OFFS 0 ++#define MV_XPCS_CYCLIC_DATA_0_CYCLICDATA0_MASK \ ++ (0x000003ff << MV_XPCS_CYCLIC_DATA_0_CYCLICDATA0_OFFS) ++ ++/* Cyclic Data 1 */ ++#define MV_XPCS_CYCLIC_DATA_1_REG (0x88) ++#define MV_XPCS_CYCLIC_DATA_1_CYCLICDATA1_OFFS 0 ++#define MV_XPCS_CYCLIC_DATA_1_CYCLICDATA1_MASK \ ++ (0x000003ff << MV_XPCS_CYCLIC_DATA_1_CYCLICDATA1_OFFS) ++ ++/* Cyclic Data 2 */ ++#define MV_XPCS_CYCLIC_DATA_2_REG (0x8c) ++#define MV_XPCS_CYCLIC_DATA_2_CYCLICDATA2_OFFS 0 ++#define MV_XPCS_CYCLIC_DATA_2_CYCLICDATA2_MASK \ ++ (0x000003ff << MV_XPCS_CYCLIC_DATA_2_CYCLICDATA2_OFFS) ++ ++/* Cyclic Data 3 */ ++#define MV_XPCS_CYCLIC_DATA_3_REG (0x90) ++#define MV_XPCS_CYCLIC_DATA_3_CYCLICDATA3_OFFS 0 ++#define MV_XPCS_CYCLIC_DATA_3_CYCLICDATA3_MASK \ ++ (0x000003ff << MV_XPCS_CYCLIC_DATA_3_CYCLICDATA3_OFFS) ++ ++/*************/ ++/*SERDES REGS */ ++/*************/ ++ ++#define MV_SERDES_CFG_0_REG (0x00) ++ ++#define MV_SERDES_CFG_0_PU_PLL_OFFS 1 ++#define MV_SERDES_CFG_0_PU_PLL_MASK (0x00000001 << \ ++ MV_SERDES_CFG_0_PU_PLL_OFFS) ++#define MV_SERDES_CFG_0_RX_PLL_OFFS 11 ++#define MV_SERDES_CFG_0_RX_PLL_MASK (0x00000001 << \ ++ MV_SERDES_CFG_0_RX_PLL_OFFS) ++#define MV_SERDES_CFG_0_TX_PLL_OFFS 12 ++#define MV_SERDES_CFG_0_TX_PLL_MASK (0x00000001 << \ ++ MV_SERDES_CFG_0_TX_PLL_OFFS) ++#define MV_SERDES_CFG_0_MEDIA_MODE_OFFS 15 ++#define MV_SERDES_CFG_0_MEDIA_MODE_MASK (0x00000001 << \ ++ MV_SERDES_CFG_0_MEDIA_MODE_OFFS) ++ ++#define MV_SERDES_CFG_1_REG (0x04) ++#define MV_SERDES_CFG_1_ANALOG_RESET_OFFS 3 ++#define MV_SERDES_CFG_1_ANALOG_RESET_MASK \ ++ (0x00000001 << MV_SERDES_CFG_1_ANALOG_RESET_OFFS) ++ ++#define MV_SERDES_CFG_1_CORE_RESET_OFFS 5 ++#define MV_SERDES_CFG_1_CORE_RESET_MASK \ ++ (0x00000001 << MV_SERDES_CFG_1_CORE_RESET_OFFS) ++ ++#define MV_SERDES_CFG_1_DIGITAL_RESET_OFFS 6 ++#define MV_SERDES_CFG_1_DIGITAL_RESET_MASK \ ++ (0x00000001 << MV_SERDES_CFG_1_DIGITAL_RESET_OFFS) ++ ++#define MV_SERDES_CFG_1_TX_SYNC_EN_OFFS 7 ++#define MV_SERDES_CFG_1_TX_SYNC_EN_MASK \ ++ (0x00000001 << MV_SERDES_CFG_1_TX_SYNC_EN_OFFS) ++ ++#define MV_SERDES_CFG_2_REG (0x08) ++#define MV_SERDES_CFG_3_REG (0x0c) ++#define MV_SERDES_MISC_REG (0x14) ++ ++/*************/ ++/*SMI REGS */ ++/*************/ ++ ++#define MV_SMI_MANAGEMENT_BUSY_OFFS 28 ++#define MV_SMI_MANAGEMENT_BUSY_MASK \ ++ (0x1 << MV_SMI_MANAGEMENT_BUSY_OFFS) ++#define MV_SMI_MANAGEMENT_READ_VALID_OFFS 27 ++#define MV_SMI_MANAGEMENT_READ_VALID_MASK \ ++ (0x1 << MV_SMI_MANAGEMENT_READ_VALID_OFFS) ++#define MV_SMI_MANAGEMENT_OPCODE_OFFS 26 ++#define MV_SMI_MANAGEMENT_OPCODE_MASK \ ++ (0x1 << MV_SMI_MANAGEMENT_OPCODE_OFFS) ++#define MV_SMI_MANAGEMENT_REGAD_OFFS 21 ++#define MV_SMI_MANAGEMENT_REGAD_MASK \ ++ (0x1F << MV_SMI_MANAGEMENT_REGAD_OFFS) ++#define MV_SMI_MANAGEMENT_PHYAD_OFFS 16 ++#define MV_SMI_MANAGEMENT_PHYAD_MASK \ ++ (0x1F << MV_SMI_MANAGEMENT_PHYAD_OFFS) ++#define MV_SMI_MANAGEMENT_DATA_OFFS 0 ++#define MV_SMI_MANAGEMENT_DATA_MASK \ ++ (0xFFFF << MV_SMI_MANAGEMENT_DATA_OFFS) ++ ++/* SMI_MISC_CFG Register */ ++#define MV_SMI_MISC_CFG_REG (0x4) ++ ++#define MV_SMI_MISC_CFG_SMI_ACCELERATE_OFFS 0 ++#define MV_SMI_MISC_CFG_SMI_ACCELERATE_MASK \ ++ (0x1 << MV_SMI_MISC_CFG_SMI_ACCELERATE_OFFS) ++#define MV_SMI_MISC_CFG_SMI_FASTMDC_OFFS 1 ++#define MV_SMI_MISC_CFG_SMI_FASTMDC_MASK \ ++ (0x1 << MV_SMI_MISC_CFG_SMI_FASTMDC_OFFS) ++#define MV_SMI_MISC_CFG_FAST_MDC_DIVISION_SELECTOR_OFFS 2 ++#define MV_SMI_MISC_CFG_FAST_MDC_DIVISION_SELECTOR_MASK \ ++ (0x3 << MV_SMI_MISC_CFG_FAST_MDC_DIVISION_SELECTOR_OFFS) ++#define MV_SMI_MISC_CFG_ENABLE_MDIO_OUT_LATENCY_OFFS 4 ++#define MV_SMI_MISC_CFG_ENABLE_MDIO_OUT_LATENCY_MASK \ ++ (0x1 << MV_SMI_MISC_CFG_ENABLE_MDIO_OUT_LATENCY_OFFS) ++#define MV_SMI_MISC_CFG_AUTOPOLLNUMOFPORTS_OFFS 5 ++#define MV_SMI_MISC_CFG_AUTOPOLLNUMOFPORTS_MASK \ ++ (0x1F << MV_SMI_MISC_CFG_AUTOPOLLNUMOFPORTS_OFFS) ++#define MV_SMI_MISC_CFG_ENABLE_POLLING_OFFS 10 ++#define MV_SMI_MISC_CFG_ENABLE_POLLING_MASK \ ++ (0x1 << MV_SMI_MISC_CFG_ENABLE_POLLING_OFFS) ++#define MV_SMI_MISC_CFG_INVERT_MDC_OFFS 11 ++#define MV_SMI_MISC_CFG_INVERT_MDC_MASK \ ++ (0x1 << MV_SMI_MISC_CFG_INVERT_MDC_OFFS) ++ ++/* PHY_AN_CFG Register */ ++#define MV_SMI_PHY_AN_CFG_REG (0x8) ++ ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT0_OFFS 0 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT0_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT0_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT1_OFFS 1 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT1_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT1_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT2_OFFS 2 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT2_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT2_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT3_OFFS 3 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT3_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT3_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT4_OFFS 4 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT4_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT4_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT5_OFFS 5 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT5_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT5_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT6_OFFS 6 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT6_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT6_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT7_OFFS 7 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT7_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT7_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT8_OFFS 8 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT8_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT8_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT9_OFFS 9 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT9_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT9_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT10_OFFS 10 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT10_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT10_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT11_OFFS 11 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT11_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT11_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT12_OFFS 12 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT12_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT12_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT13_OFFS 13 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT13_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT13_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT14_OFFS 14 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT14_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT14_OFFS) ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT15_OFFS 15 ++#define MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT15_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_AUTOMEDIA_SELECTEN_PORT15_OFFS) ++#define MV_SMI_PHY_AN_CFG_SKIPSWRESET_SMI_OFFS 16 ++#define MV_SMI_PHY_AN_CFG_SKIPSWRESET_SMI_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_SKIPSWRESET_SMI_OFFS) ++#define MV_SMI_PHY_AN_CFG_STOP_AUTONEGSMI_OFFS 17 ++#define MV_SMI_PHY_AN_CFG_STOP_AUTONEGSMI_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_STOP_AUTONEGSMI_OFFS) ++#define MV_SMI_PHY_AN_CFG_MASTERSMI_OFFS 18 ++#define MV_SMI_PHY_AN_CFG_MASTERSMI_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_MASTERSMI_OFFS) ++#define MV_SMI_PHY_AN_CFG_SGMIIINBANDFCEN_OFFS 19 ++#define MV_SMI_PHY_AN_CFG_SGMIIINBANDFCEN_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_SGMIIINBANDFCEN_OFFS) ++#define MV_SMI_PHY_AN_CFG_FCADVSETFIBER_OFFS 20 ++#define MV_SMI_PHY_AN_CFG_FCADVSETFIBER_MASK \ ++ (0x1 << MV_SMI_PHY_AN_CFG_FCADVSETFIBER_OFFS) ++ ++/* PHY_ADDRESS_REGISTER0 Register */ ++#define MV_SMI_PHY_ADDRESS_REG(n) (0xC + 0x4 * n) ++#define MV_SMI_PHY_ADDRESS_PHYAD_OFFS 0 ++#define MV_SMI_PHY_ADDRESS_PHYAD_MASK \ ++ (0x1F << MV_SMI_PHY_ADDRESS_PHYAD_OFFS) ++ ++/*************/ ++/* MIB REGS */ ++/*************/ ++ ++/* GMAC_MIB Counters register definitions */ ++#define MV_MIB_GOOD_OCTETS_RECEIVED_LOW 0x0 ++#define MV_MIB_GOOD_OCTETS_RECEIVED_HIGH 0x4 ++#define MV_MIB_BAD_OCTETS_RECEIVED 0x8 ++#define MV_MIB_CRC_ERRORS_SENT 0xc ++#define MV_MIB_UNICAST_FRAMES_RECEIVED 0x10 ++/* Reserved 0x14 */ ++#define MV_MIB_BROADCAST_FRAMES_RECEIVED 0x18 ++#define MV_MIB_MULTICAST_FRAMES_RECEIVED 0x1c ++#define MV_MIB_FRAMES_64_OCTETS 0x20 ++#define MV_MIB_FRAMES_65_TO_127_OCTETS 0x24 ++#define MV_MIB_FRAMES_128_TO_255_OCTETS 0x28 ++#define MV_MIB_FRAMES_256_TO_511_OCTETS 0x2c ++#define MV_MIB_FRAMES_512_TO_1023_OCTETS 0x30 ++#define MV_MIB_FRAMES_1024_TO_MAX_OCTETS 0x34 ++#define MV_MIB_GOOD_OCTETS_SENT_LOW 0x38 ++#define MV_MIB_GOOD_OCTETS_SENT_HIGH 0x3c ++#define MV_MIB_UNICAST_FRAMES_SENT 0x40 ++/* Reserved 0x44 */ ++#define MV_MIB_MULTICAST_FRAMES_SENT 0x48 ++#define MV_MIB_BROADCAST_FRAMES_SENT 0x4c ++/* Reserved 0x50 */ ++#define MV_MIB_FC_SENT 0x54 ++#define MV_MIB_FC_RECEIVED 0x58 ++#define MV_MIB_RX_FIFO_OVERRUN 0x5c ++#define MV_MIB_UNDERSIZE_RECEIVED 0x60 ++#define MV_MIB_FRAGMENTS_RECEIVED 0x64 ++#define MV_MIB_OVERSIZE_RECEIVED 0x68 ++#define MV_MIB_JABBER_RECEIVED 0x6c ++#define MV_MIB_MAC_RECEIVE_ERROR 0x70 ++#define MV_MIB_BAD_CRC_EVENT 0x74 ++#define MV_MIB_COLLISION 0x78 ++#define MV_MIB_LATE_COLLISION 0x7c ++ ++/******************************************************************************/ ++/* System Soft Reset 1 */ ++#define MV_GOP_SOFT_RESET_1_REG 0x8 ++ ++#define NETC_GOP_SOFT_RESET_OFFSET 6 ++#define NETC_GOP_SOFT_RESET_MASK (0x1 << NETC_GOP_SOFT_RESET_OFFSET) ++ ++/* Ports Control 0 */ ++#define MV_NETCOMP_PORTS_CONTROL_0 (0x10) ++#define MV_NETCOMP_PORTS_CONTROL_0_DEF_VAL 0xe80002ff ++ ++#define NETC_CLK_DIV_PHASE_OFFSET 31 ++#define NETC_CLK_DIV_PHASE_MASK (0x1 << NETC_CLK_DIV_PHASE_OFFSET) ++ ++#define NETC_GIG_RX_DATA_SAMPLE_OFFSET 29 ++#define NETC_GIG_RX_DATA_SAMPLE_MASK (0x1 << NETC_GIG_RX_DATA_SAMPLE_OFFSET) ++ ++#define NETC_PORT3_PAUSE_OFFSET 6 ++#define NETC_PORT3_PAUSE_MASK (0x1 << NETC_PORT3_PAUSE_OFFSET) ++ ++#define NETC_PORT2_PAUSE_OFFSET 5 ++#define NETC_PORT2_PAUSE_MASK (0x1 << NETC_PORT2_PAUSE_OFFSET) ++ ++#define NETC_PORT1_PAUSE_OFFSET 4 ++#define NETC_PORT1_PAUSE_MASK (0x1 << NETC_PORT1_PAUSE_OFFSET) ++ ++#define NETC_PORT0_PAUSE_OFFSET 3 ++#define NETC_PORT0_PAUSE_MASK (0x1 << NETC_PORT0_PAUSE_OFFSET) ++ ++#define NETC_BUS_WIDTH_SELECT_OFFSET 1 ++#define NETC_BUS_WIDTH_SELECT_MASK (0x1 << NETC_BUS_WIDTH_SELECT_OFFSET) ++ ++#define NETC_GOP_ENABLE_OFFSET 0 ++#define NETC_GOP_ENABLE_MASK (0x1 << NETC_GOP_ENABLE_OFFSET) ++ ++/* Ports Control 1 */ ++#define MV_NETCOMP_PORTS_CONTROL_1 (0x14) ++#define MV_NETCOMP_PORTS_CONTROL_1_DEF_VAL 0x8d100 ++ ++#define NETC_PORT_GIG_RF_RESET_OFFSET(port) (28 + port) ++#define NETC_PORT_GIG_RF_RESET_MASK(port) \ ++ (0x1 << NETC_PORT_GIG_RF_RESET_OFFSET(port)) ++ ++#define NETC_PORTS_ACTIVE_OFFSET(port) (0 + port) ++#define NETC_PORTS_ACTIVE_MASK(port) (0x1 << NETC_PORTS_ACTIVE_OFFSET(port)) ++ ++/* Ports Status */ ++#define MV_NETCOMP_PORTS_STATUS (0x1C) ++#define NETC_PORTS_STATUS_OFFSET(port) (0 + port) ++#define NETC_PORTS_STATUS_MASK(port) (0x1 << NETC_PORTS_STATUS_OFFSET(port)) ++ ++/* Networking Complex Control 0 */ ++#define MV_NETCOMP_CONTROL_0 (0x20) ++#define MV_NETCOMP_CONTROL_0_DEF_VAL 0xb01f3000 ++ ++#define NETC_GBE_PORT1_MII_MODE_OFFSET 2 ++#define NETC_GBE_PORT1_MII_MODE_MASK \ ++ (0x1 << NETC_GBE_PORT1_MII_MODE_OFFSET) ++ ++#define NETC_GBE_PORT1_SGMII_MODE_OFFSET 1 ++#define NETC_GBE_PORT1_SGMII_MODE_MASK \ ++ (0x1 << NETC_GBE_PORT1_SGMII_MODE_OFFSET) ++ ++#define NETC_GBE_PORT0_SGMII_MODE_OFFSET 0 ++#define NETC_GBE_PORT0_SGMII_MODE_MASK \ ++ (0x1 << NETC_GBE_PORT0_SGMII_MODE_OFFSET) ++ ++/* SD1 Control1 */ ++#define SD1_CONTROL_1_REG (0x148) ++ ++#define SD1_CONTROL_XAUI_EN_OFFSET 28 ++#define SD1_CONTROL_XAUI_EN_MASK (0x1 << SD1_CONTROL_XAUI_EN_OFFSET) ++ ++#define SD1_CONTROL_RXAUI0_L23_EN_OFFSET 27 ++#define SD1_CONTROL_RXAUI0_L23_EN_MASK (0x1 << \ ++ SD1_CONTROL_RXAUI0_L23_EN_OFFSET) ++ ++#define SD1_CONTROL_RXAUI1_L45_EN_OFFSET 26 ++#define SD1_CONTROL_RXAUI1_L45_EN_MASK (0x1 << \ ++ SD1_CONTROL_RXAUI1_L45_EN_OFFSET) ++ ++/******************************************************************************/ ++ ++#define PCS40G_COMMON_CONTROL (0x014) ++ ++#define FORWARD_ERROR_CORRECTION_OFFSET 10 ++#define FORWARD_ERROR_CORRECTION_MASK (0x1 << FORWARD_ERROR_CORRECTION_OFFSET) ++ ++#define PCS_CLOCK_RESET (0x14C) ++ ++#define CLK_DIV_PHASE_SET_OFFSET 11 ++#define CLK_DIV_PHASE_SET_MASK (0x1 << CLK_DIV_PHASE_SET_OFFSET) ++ ++#define CLK_DIVISION_RATIO_OFFSET 4 ++#define CLK_DIVISION_RATIO_MASK (0x7 << CLK_DIVISION_RATIO_OFFSET) ++ ++#define MAC_CLK_RESET_OFFSET 2 ++#define MAC_CLK_RESET_MASK (0x1 << MAC_CLK_RESET_OFFSET) ++ ++#define RX_SD_CLK_RESET_OFFSET 1 ++#define RX_SD_CLK_RESET_MASK (0x1 << RX_SD_CLK_RESET_OFFSET) ++ ++#define TX_SD_CLK_RESET_OFFSET 0 ++#define TX_SD_CLK_RESET_MASK (0x1 << TX_SD_CLK_RESET_OFFSET) ++ ++/***********/ ++/*FCA REGS */ ++/***********/ ++ ++/* FCA control */ ++#define FCA_CONTROL_REG (0x000) ++#define FCA_RESET_OFFSET 0 ++#define FCA_RESET_MASK (0x1 << FCA_RESET_OFFSET) ++ ++#define FCA_BYPASS_OFFSET 1 ++#define FCA_BYPASS_MASK (0x1 << FCA_BYPASS_OFFSET) ++ ++#define FCA_PORT_TYPE_OFFSET 4 ++#define FCA_PORT_TYPE_MASK (0x111 << FCA_PORT_TYPE_OFFSET) ++ ++#define FCA_SEND_PERIODIC_OFFSET 7 ++#define FCA_SEND_PERIODIC_MASK (0x1 << FCA_SEND_PERIODIC_OFFSET) ++ ++#define FCA_ENABLE_PERIODIC_OFFSET 11 ++#define FCA_ENABLE_PERIODIC_MASK (0x1 << FCA_ENABLE_PERIODIC_OFFSET) ++ ++/* FCA periodic timer */ ++#define PERIODIC_COUNTER_LSB_REG (0x110) ++#define PERIODIC_COUNTER_MSB_REG (0x114) ++ ++#define FCA_PORT_TYPE_A 0x0 ++#define FCA_PORT_TYPE_B 0x1 ++#define FCA_PORT_TYPE_C 0x2 ++#define FCA_PORT_TYPE_D 0x3 ++#define FCA_PORT_TYPE_E 0x4 ++ ++#endif /*_MV_GOP_HW_TYPE_H_*/ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h +new file mode 100644 +index 00000000..dfea621 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h +@@ -0,0 +1,1017 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#ifndef _MVPP2_H_ ++#define _MVPP2_H_ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mv_pp2x_hw_type.h" ++#include "mv_gop110_hw_type.h" ++ ++#define MVPP2_DRIVER_NAME "mvpp2x" ++#define MVPP2_DRIVER_VERSION "1.0" ++ ++/* SKB magic, used for skb recycle, generated as: ++ * address[31 : 6] of skb->head (kmalloc aligned >=128B) ++ * CP110 pp2-cell-id in bits[5,4,3] + BM pool-Id in bits[2,1,0] (3 bits in BM-HW-descriptor) ++ */ ++#define MVPP2X_SKB_MAGIC_MASK 0xFFFFFFC0 ++#define MVPP2X_SKB_PP2_CELL_OFFS 3 ++#define MVPP2X_SKB_BPID_MASK 0x7 /* 3bits */ ++#define MVPP2X_SKB_CELL_MASK 0x7 /* 3bits */ ++ ++/* TX-check that RXed data-buffer(skb->head) is not replaced by NET-stack */ ++#define MVPP2X_SKB_MAGIC(skb) ((unsigned int)(((u64)skb->head) & MVPP2X_SKB_MAGIC_MASK)) ++ ++/* Cb to store magic with bpid in last 4 bytes out of cb[48]. ++ * IPv6 TCP consumes the most 44 bytes, so the last are safe to use. ++ */ ++#define MVPP2X_SKB_CB(skb) (*(unsigned int *)(&skb->cb[sizeof(skb->cb) - sizeof(int)])) ++ ++/* Set magic and bpid, magic[31:5], pp2_id[5:4], source pool[3:0] */ ++#define MVPP2X_SKB_MAGIC_BPID_SET(skb, magic_bpid) (MVPP2X_SKB_CB(skb) = magic_bpid) ++/* Get bpid */ ++#define MVPP2X_SKB_BPID_GET(skb) (MVPP2X_SKB_CB(skb) & MVPP2X_SKB_BPID_MASK) ++#define MVPP2X_SKB_PP2_CELL_GET(skb) ((MVPP2X_SKB_CB(skb) >> MVPP2X_SKB_PP2_CELL_OFFS) & \ ++ MVPP2X_SKB_CELL_MASK) ++ ++#define MVPP2X_SKB_RECYCLE_MAGIC_GET(skb) (MVPP2X_SKB_CB(skb) & MVPP2X_SKB_MAGIC_MASK) ++/* Recycle magic check */ ++#define MVPP2X_SKB_RECYCLE_MAGIC_IS_OK(skb) (MVPP2X_SKB_MAGIC(skb) == MVPP2X_SKB_RECYCLE_MAGIC_GET(skb)) ++ ++#define PFX MVPP2_DRIVER_NAME ": " ++ ++#define IRQ_NAME_SIZE (36) ++ ++#define STATS_DELAY 1000 /*mSec*/ ++ ++#define TSO_TXQ_LIMIT 100 ++#define TXQ_LIMIT (MAX_SKB_FRAGS + 2) ++ ++#define MV_ETH_SKB_SHINFO_SIZE SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) ++ ++/* START - Taken from mvPp2Commn.h, need to order TODO */ ++/*--------------------------------------------------------------------*/ ++/* PP2 COMMON DEFINETIONS */ ++/*--------------------------------------------------------------------*/ ++ ++#define MV_ERROR (-1) ++#define MV_OK (0) ++ ++#define WAY_MAX 1 ++ ++/*--------------------------------------------------------------------*/ ++/* PP2 COMMON DEFINETIONS */ ++/*--------------------------------------------------------------------*/ ++#define NOT_IN_USE (-1) ++#define IN_USE (1) ++#define BYTE_BITS 8 ++#define BYTE_MASK 0xFF ++#define DWORD_BITS_LEN 32 ++#define DWORD_BYTES_LEN 4 ++#define RETRIES_EXCEEDED 15000 ++#define ONE_BIT_MAX 1 ++#define UNI_MAX 7 ++#define ETH_PORTS_NUM 7 ++ ++/*--------------------------------------------------------------------*/ ++/* PNC COMMON DEFINETIONS */ ++/*--------------------------------------------------------------------*/ ++ ++/* HW_BYTE_OFFS ++ * return HW byte offset in 4 bytes register ++ * _offs_: native offset (LE) ++ * LE example: HW_BYTE_OFFS(1) = 1 ++ * BE example: HW_BYTE_OFFS(1) = 2 ++ */ ++ ++#if defined(__LITTLE_ENDIAN) ++#define HW_BYTE_OFFS(_offs_) (_offs_) ++#else ++#define HW_BYTE_OFFS(_offs_) ((3 - ((_offs_) % 4)) + (((_offs_) / 4) * 4)) ++#endif ++ ++#define SRAM_BIT_TO_BYTE(_bit_) HW_BYTE_OFFS((_bit_) / 8) ++ ++#define TCAM_DATA_BYTE_OFFS_LE(_offs_) (((_offs_) - \ ++ ((_offs_) % 2)) * 2 + ((_offs_) % 2)) ++#define TCAM_DATA_MASK_OFFS_LE(_offs_) (((_offs_) * 2) - ((_offs_) % 2) + 2) ++ ++/* TCAM_DATA_BYTE/MASK ++ * tcam data devide into 4 bytes registers ++ * each register include 2 bytes of data and 2 bytes of mask ++ * the next macros calc data/mask offset in 4 bytes register ++ * _offs_: native offset (LE) in data bytes array ++ * relevant only for TCAM data bytes ++ * used by PRS and CLS2 ++ */ ++#define TCAM_DATA_BYTE(_offs_) (HW_BYTE_OFFS(TCAM_DATA_BYTE_OFFS_LE(_offs_))) ++#define TCAM_DATA_MASK(_offs_) (HW_BYTE_OFFS(TCAM_DATA_MASK_OFFS_LE(_offs_))) ++ ++/*END - Taken from mvPp2Commn.h, need to order TODO */ ++/*--------------------------------------------------------------------*/ ++ ++#define __FILENAME__ (strrchr(__FILE__, '/') ? \ ++ strrchr(__FILE__, '/') + 1 : __FILE__) ++ ++#ifdef MVPP2_VERBOSE ++#define MVPP2_PRINT_LINE() \ ++ pr_info("Passed: %s(%d)\n", __func__, __LINE__) ++ ++#define MVPP2_PRINT_VAR(var) \ ++ pr_info("%s (%d): " #var "=0x%lx\n", __func__, __LINE__, (u64)var) ++#define MVPP2_PRINT_VAR_NAME(var, name) \ ++ pr_info("%s (%d): %s = 0x%lx\n", __func__, __LINE__, name, var) ++#else ++#define MVPP2_PRINT_LINE() ++#define MVPP2_PRINT_VAR(var) ++#define MVPP2_PRINT_VAR_NAME(var, name) ++#endif ++ ++/* Descriptor ring Macros */ ++#define MVPP2_QUEUE_NEXT_DESC(q, index) \ ++ (((index) < (q)->last_desc) ? ((index) + 1) : 0) ++ ++#define MVPP2_QUEUE_DESC_PTR(q, index) \ ++ ((q)->first_desc + index) ++ ++/* Various constants */ ++#define MVPP2_MAX_SW_THREADS 4 ++#define MVPP2_MAX_ADDR_SPACES 9 /* MVpp22 HW has 9 hif's(address spaces) ++ * Aggr/RX/TX queues, queue vectors and other per hif stuff ++ * Allocation would be limited by maximum number of hif's in ++ * mvpp22 HW. ++ */ ++#define MVPP2_MAX_CPUS_IN_AP 8 ++#define MVPP2_DEFAULT_RX_COUNT 8 ++#define MVPP2_DEFAULT_OTHER_COUNT 1 ++#define MVPP22_MAX_NUM_RXQ 32 ++ ++/* Coalescing */ ++#define MVPP2_TXDONE_COAL_PKTS 32 ++#define MVPP2_TXDONE_HRTIMER_USEC 100UL ++#define MVPP2_GUARD_TXDONE_HRTIMER_USEC (10 * USEC_PER_MSEC) ++#define MVPP2_TX_HRTIMER_PERIOD_NS 50000UL ++#define MVPP2_TXDONE_COAL_USEC 1000 ++ ++#define MVPP2_RX_COAL_PKTS 32 ++#define MVPP2_RX_COAL_USEC 64 ++ ++/* BM constants */ ++#define MVPP2_BM_POOLS_NUM 16 ++#define MVPP2_BM_POOLS_MAX_ALLOC_NUM 9 /* Max num of allowed BM pools ++ * allocations ++ * 0-2 pools used by Kernel ++ * 3-8 pools used by Netmap ++ */ ++#define MVPP2_BM_POOL_SIZE_MAX (16 * 1024 - \ ++ MVPP2_BM_POOL_PTR_ALIGN / 4) ++#define MVPP2_BM_POOL_PTR_ALIGN 128 ++ ++#define MVPP2_BM_SHORT_BUF_NUM 2048 ++#define MVPP2_BM_LONG_BUF_NUM 2048 ++#define MVPP2_BM_JUMBO_BUF_NUM 2048 ++#define MVPP2_BM_PER_CPU_THRESHOLD(num_of_cpus) (num_of_cpus * 2) ++ ++#define MVPP23_BM_UNPR_DIS 0 ++#define MVPP23_BM_UNPR_EN 1 ++ ++#define MVPP2_ALL_BUFS 0 ++ ++#define RX_TOTAL_SIZE(buf_size) ((buf_size) + MV_ETH_SKB_SHINFO_SIZE) ++#define RX_TRUE_SIZE(total_size) roundup_pow_of_two(total_size) ++extern u32 debug_param; ++ ++/* Convert cpu_id to sw_thread_id */ ++#define QV_THR_2_MASTER_AP_CPU(ap_id, sw_thread_id) ((ap_id * MVPP2_MAX_CPUS_IN_AP) + sw_thread_id) ++#define QV_CPU_2_ADR_SP(cpu_id, num_of_aps) (cpu_id / num_of_aps) ++ ++/* Used for define type of data saved in shadow: SKB or extended buffer or nothing */ ++#define MVPP2_ETH_SHADOW_SKB 0x1 ++#define MVPP2_ETH_SHADOW_EXT 0x2 ++#define MVPP2_ETH_SHADOW_REC 0x4 ++ ++#define MVPP2_UNIQUE_HASH 0x4567492 ++ ++#define MVPP2_EXTRA_BUF_SIZE 120 ++#define MVPP2_EXTRA_BUF_NUM (MVPP2_MAX_TXD * MVPP2_MAX_TXQ) ++#define MVPP2_SKB_NUM (MVPP2_MAX_RXD * MVPP2_MAX_RXQ_PER_CPU * MVPP2_MAX_PORTS) ++ ++enum mvppv2_version { ++ PPV21 = 21, ++ PPV22, ++ PPV23 ++}; ++ ++/* Communication Processor version enum */ ++enum mv_cp_version { ++ MV_CP110, ++ MV_CP115 ++}; ++ ++enum mv_pp2x_queue_vector_type { ++ MVPP2_RX_SHARED, ++ MVPP2_PRIVATE, ++ MVPP2_TX_SHARED ++}; ++ ++enum mv_pp2x_queue_distribution_mode { ++ /* All queues are shared. ++ * PPv2.1: this is the only supported mode. ++ * PPv2.2: Requires (N+1) interrupts. All rx_queues are ++ * configured on the additional interrupt. ++ */ ++ MVPP2_QDIST_SINGLE_MODE, ++ MVPP2_QDIST_MULTI_MODE, /* PPv2.2 only requires N interrupts */ ++ MVPP2_SINGLE_RESOURCE_MODE ++}; ++ ++enum mv_pp2x_cos_classifier { ++ MVPP2_COS_CLS_VLAN, /* CoS based on VLAN pri */ ++ MVPP2_COS_CLS_DSCP, ++ MVPP2_COS_CLS_VLAN_DSCP, /* CoS based on VLAN pri, */ ++ /*if untagged and IP, then based on DSCP */ ++ MVPP2_COS_CLS_DSCP_VLAN ++}; ++ ++enum mv_pp2x_rss_mode { ++ MVPP2_RSS_2T, /* non-frag UDP&TCP packet hash value ++ * is calculated based on 2T ++ */ ++ MVPP2_RSS_5T /* non-frag UDP&TCP packet hash value ++ * is calculated based on 5T ++ */ ++}; ++ ++struct gop_stat { ++ u64 rx_byte; ++ u64 rx_unicast; ++ u64 rx_mcast; ++ u64 rx_bcast; ++ u64 rx_frames; ++ u64 rx_pause; ++ u64 rx_mac_overrun; ++ u64 rx_crc; ++ u64 rx_runt; ++ u64 rx_giant; ++ u64 rx_fragments_err; ++ u64 rx_mac_err; ++ u64 rx_jabber; ++ u64 rx_ppv2_overrun; ++ u64 rx_cls_drop; ++ u64 rx_fullq_drop; ++ u64 rx_early_drop; ++ u64 rx_bm_drop; ++ u64 rx_total_err; ++ u64 rx_hw_drop; ++ u64 rx_sw_drop; ++ u64 rx_perq_fullq_drop[MVPP22_MAX_NUM_RXQ]; ++ u64 rx_perq_early_drop[MVPP22_MAX_NUM_RXQ]; ++ u64 rx_perq_bm_drop[MVPP22_MAX_NUM_RXQ]; ++ u64 tx_byte; ++ u64 tx_unicast; ++ u64 tx_mcast; ++ u64 tx_bcast; ++ u64 tx_frames; ++ u64 tx_pause; ++ u64 tx_crc_sent; ++ u64 collision; ++ u64 late_collision; ++ u64 frames_64; ++ u64 frames_65_to_127; ++ u64 frames_128_to_255; ++ u64 frames_256_to_511; ++ u64 frames_512_to_1023; ++ u64 frames_1024_to_max; ++}; ++ ++struct mv_mac_data { ++ u8 gop_index; ++ struct gop_stat gop_statistics; ++ u64 flags; ++ /* Whether a PHY is present, and if yes, at which address. */ ++ int phy_addr; ++ phy_interface_t phy_mode; /* RXAUI, SGMII, etc. */ ++ struct phy_device *phy_dev; ++ struct device_node *phy_node; ++ int link_irq; ++ char irq_name[IRQ_NAME_SIZE]; ++ bool force_link; ++ u32 autoneg; ++ u32 link; ++ u32 duplex; ++ u32 speed; ++ ++ /* Protect gop_statistics update by concurrent workqueue and ethtool */ ++ spinlock_t stats_spinlock; ++}; ++ ++/* Masks used for pp3_emac flags */ ++#define MV_EMAC_F_LINK_UP_BIT 0 ++#define MV_EMAC_F_INIT_BIT 1 ++#define MV_EMAC_F_SGMII2_5_BIT 2 ++#define MV_EMAC_F_PORT_UP_BIT 3 ++#define MV_EMAC_F_5G_BIT 4 ++ ++#define MV_EMAC_F_LINK_UP BIT(MV_EMAC_F_LINK_UP_BIT) ++#define MV_EMAC_F_INIT BIT(MV_EMAC_F_INIT_BIT) ++#define MV_EMAC_F_SGMII2_5 BIT(MV_EMAC_F_SGMII2_5_BIT) ++#define MV_EMAC_F_PORT_UP BIT(MV_EMAC_F_PORT_UP_BIT) ++#define MV_EMAC_F_5G BIT(MV_EMAC_F_5G_BIT) ++ ++#define MV_BM_LOCK BIT(0) ++#define MV_AGGR_QUEUE_LOCK BIT(1) ++ ++#define MVPP2_NO_LINK_IRQ 0 ++ ++/* Per-CPU Tx queue control */ ++struct mv_pp2x_txq_pcpu { ++ int cpu; ++ ++ /* Number of Tx DMA descriptors in the descriptor ring */ ++ int size; ++ ++ /* Number of Tx DMA descriptors reserved for each CPU */ ++ int reserved_num; ++ ++ /* Array of transmitted skb */ ++ struct sk_buff **tx_skb; ++ ++ /* Array of transmitted buffers' physical addresses */ ++ dma_addr_t *tx_buffs; ++ ++ int *data_size; ++ ++ /* Index of last TX DMA descriptor that was inserted */ ++ int txq_put_index; ++ ++ /* Index of the TX DMA descriptor to be cleaned up */ ++ int txq_get_index; ++}; ++ ++struct mv_pp2x_tx_queue { ++ /* Physical number of this Tx queue */ ++ u8 id; ++ ++ /* Logical number of this Tx queue */ ++ u8 log_id; ++ ++ /* Number of Tx DMA descriptors in the descriptor ring */ ++ int size; ++ ++ /* Per-CPU control of physical Tx queues */ ++ struct mv_pp2x_txq_pcpu *pcpu; ++ ++ u32 pkts_coal; ++ ++ /* Virtual pointer to address of the Tx DMA descriptors ++ * memory_allocation ++ */ ++ void *desc_mem; ++ ++ /* Virtual address of thex Tx DMA descriptors array */ ++ struct mv_pp2x_tx_desc *first_desc; ++ ++ /* DMA address of the Tx DMA descriptors array */ ++ dma_addr_t descs_phys; ++ ++ /* Index of the last Tx DMA descriptor */ ++ int last_desc; ++ ++ /* Index of the next Tx DMA descriptor to process */ ++ int next_desc_to_proc; ++}; ++ ++struct mv_pp2x_aggr_tx_queue { ++ /* Number of Tx DMA descriptors in the descriptor ring */ ++ int size; ++ ++ /* Number of currently used Tx DMA descriptor in the descriptor ring used by SW */ ++ int sw_count; ++ ++ /* Number of currently used Tx DMA descriptor in the descriptor ring used by HW */ ++ int hw_count; ++ ++ /* Index of the next Tx DMA descriptor to process */ ++ int next_desc_to_proc; ++ ++ /* Virtual pointer to address of the Aggr_Tx DMA descriptors array */ ++ struct mv_pp2x_tx_desc *first_desc; ++ ++ /* Index of the last Tx DMA descriptor */ ++ int last_desc; ++ ++ /* AGGR TX queue lock */ ++ spinlock_t spinlock; ++ ++ /* DMA address of the Tx DMA descriptors array */ ++ dma_addr_t descs_phys; ++ ++ /* Virtual pointer to address of the Aggr_Tx DMA descriptors ++ * memory_allocation ++ */ ++ void *desc_mem; ++ ++ /* Physical number of this Tx queue */ ++ u8 id; ++} __aligned(MVPP2_CACHE_LINE_SIZE); ++ ++struct mv_pp2x_rx_queue { ++ /* RX queue number, in the range 0-31 for physical RXQs */ ++ u8 id; ++ ++ /* Port's logic RXQ number to which physical RXQ is mapped */ ++ int log_id; ++ ++ /* Num of rx descriptors in the rx descriptor ring */ ++ int size; ++ ++ u32 pkts_coal; ++ u32 time_coal; ++ ++ /* Virtual pointer to address of the Rx DMA descriptors ++ * memory_allocation ++ */ ++ void *desc_mem; ++ ++ /* Virtual address of the RX DMA descriptors array */ ++ struct mv_pp2x_rx_desc *first_desc; ++ ++ /* DMA address of the RX DMA descriptors array */ ++ dma_addr_t descs_phys; ++ ++ /* Index of the last RX DMA descriptor */ ++ int last_desc; ++ ++ /* Index of the next RX DMA descriptor to process */ ++ int next_desc_to_proc; ++ ++ /* ID of port to which physical RXQ is mapped */ ++ int port; ++ ++}; ++ ++struct avanta_lp_gop_hw { ++ void __iomem *lms_base; ++}; ++ ++struct mv_mac_unit_desc { ++ void __iomem *base; ++ u32 obj_size; ++}; ++ ++struct cpn110_gop_hw { ++ enum mv_cp_version cp_version; ++ struct mv_mac_unit_desc gmac; ++ struct mv_mac_unit_desc xlg_mac; ++ struct mv_mac_unit_desc xmib; ++ struct mv_mac_unit_desc tai; ++ struct mv_mac_unit_desc ptp; ++ struct mv_mac_unit_desc fca; ++ struct mv_mac_unit_desc mspg; ++ void __iomem *smi_base; ++ void __iomem *xsmi_base; ++ void __iomem *xpcs_base; ++ void __iomem *rfu1_base; ++ void __iomem *cm3_base; ++ ++}; ++ ++struct gop_hw { ++ union { ++ struct avanta_lp_gop_hw gop_alp; ++ struct cpn110_gop_hw gop_110; ++ }; ++}; ++ ++struct mv_pp2x_hw { ++ /* Shared registers' base addresses */ ++ void __iomem *base; /* PPV22 base_address as received in ++ *devm_ioremap_resource(). ++ */ ++ void __iomem *lms_base; ++ void __iomem *cpu_base[MVPP2_MAX_ADDR_SPACES]; ++ ++ phys_addr_t phys_addr_start; ++ phys_addr_t phys_addr_end; ++ ++ struct gop_hw gop; ++ /* ppv22_base_address for each CPU. ++ * PPv2.2 - cpu_base[x] = base + ++ * cpu_index[smp_processor_id]*MV_PP2_SPACE_64K, ++ * for non-participating CPU it is NULL. ++ * PPv2.1 cpu_base[x] = base ++ */ ++ /* Common clocks */ ++ struct clk *pp_clk; ++ struct clk *gop_clk; ++ struct clk *gop_core_clk; ++ struct clk *mg_clk; ++ struct clk *mg_core_clk; ++ ++ u32 tclk; ++ ++ /* PRS shadow table */ ++ struct mv_pp2x_prs_shadow *prs_shadow; ++ /* PRS auxiliary table for double vlan entries control */ ++ bool *prs_double_vlans; ++ /* CLS shadow info for update in running time */ ++ struct mv_pp2x_cls_shadow *cls_shadow; ++ /* C2 shadow info */ ++ struct mv_pp2x_c2_shadow *c2_shadow; ++ ++ bool mv_pp2x_no_single_mode; ++ /* PPv23 BM underrun protect feature */ ++ bool mv_bm_underrun_protect; ++ ++ /* Protect classifier and parser configuration */ ++ spinlock_t cls_spinlock; ++}; ++ ++struct mv_pp2x_cos { ++ u8 cos_classifier; /* CoS based on VLAN or DSCP */ ++ u8 num_cos_queues; /* number of queue to do CoS */ ++ u8 default_cos; /* Default CoS value for non-IP or non-VLAN */ ++ u8 reserved; ++ u32 pri_map; /* 32 bits, each nibble maps a cos_value(0~7) ++ * to a queue. ++ */ ++}; ++ ++struct mv_pp2x_rss { ++ u8 rss_mode; /*UDP&TCP packets */ ++ u8 dflt_cpu; /*non-IP packet */ ++ u8 rss_en; ++}; ++ ++struct mv_pp2x_param_config { ++ u8 first_bm_pool; ++ u8 first_sw_thread; /* The index of the first PPv2.2 ++ * sub-address space for this NET_INSTANCE. ++ */ ++ u8 first_log_rxq; /* The first cos rx queue used in the port */ ++ u8 cell_index; /* The cell_index of the PPv22 ++ * (could be 0,1, set according to dtsi) ++ */ ++ enum mv_pp2x_queue_distribution_mode queue_mode; ++ u32 rx_cpu_map; /* The CPU that port bind, each port has a nibble ++ * indexed by port_id, nibble value is CPU id ++ */ ++ u8 uc_filter_max; /* The unicast filter list max, multiple of 4 */ ++ u8 mc_filter_max; /* The multicast filter list max, multiple of 4 */ ++ ++ u8 num_of_ap; /* Num of AP's in system */ ++ u8 spinlocks_bitmap; /* bitmap of required locks */ ++ bool recycling; /* indicate if recycle enabled */ ++}; ++ ++struct mv_pp2x_uio { ++ int num_maps; ++ struct uio_info u_info; ++}; ++ ++/* Shared Packet Processor resources */ ++struct mv_pp2x { ++ enum mvppv2_version pp2_version; /* Redundant, consider to delete. ++ * (prevents extra pointer lookup from ++ * mv_pp2x_platform_data) ++ */ ++ struct mv_pp2x_hw hw; ++ struct mv_pp2x_platform_data *pp2xdata; ++ ++ struct mv_pp2x_param_config pp2_cfg; ++ ++ struct platform_device *pdev; ++ ++ /* List of pointers to port structures */ ++ struct mv_pp2x_port **port_list; ++ u16 num_ports; ++ ++ /* Aggregated TXQs */ ++ u16 num_aggr_qs; ++ struct mv_pp2x_aggr_tx_queue *aggr_txqs; ++ u16 aggr_txqs_align_offs; ++ ++ /* BM pools */ ++ u16 num_pools; ++ struct mv_pp2x_bm_pool *bm_pools; ++ ++ /* Per-CPU CP control */ ++ struct mv_pp2x_cp_pcpu **pcpu; ++ ++ /* RX flow hash indir'n table, in pp22, the table contains the ++ * CPU idx according to weight ++ */ ++ u8 num_rss_tables; /* created for sysfs usage */ ++ u32 rx_indir_table[MVPP22_RSS_TBL_LINE_NUM]; ++ u32 l4_chksum_jumbo_port; ++ ++ u8 rx_count; ++ u8 other_count; ++ /* Spinlocks per hif to protect BM refill */ ++ spinlock_t bm_spinlock[MVPP2_BM_POOLS_MAX_ALLOC_NUM]; ++ ++ /* Spinlocks for CM3 shared memory configuration */ ++ spinlock_t mss_spinlock; ++ ++ struct delayed_work stats_task; ++ struct workqueue_struct *workqueue; ++ struct notifier_block cp_hotplug_nb; ++ struct mv_pp2x_uio uio; ++ ++}; ++ ++struct mv_pp2x_pcpu_stats { ++ struct u64_stats_sync syncp; ++ u64 rx_packets; ++ u64 rx_bytes; ++ u64 tx_packets; ++ u64 tx_bytes; ++}; ++ ++/* Per-CPU port control */ ++struct mv_pp2x_port_pcpu { ++ struct hrtimer tx_done_timer; ++ /* _scheduled: hrtimer already running OR not to be started */ ++ bool timer_scheduled; ++ bool guard_timer_scheduled; ++ ktime_t tx_time_coal_hrtmr; ++ /* Tasklet for egress finalization */ ++ struct tasklet_struct tx_done_tasklet; ++ int ext_buf_size; ++ struct list_head ext_buf_port_list; ++ struct mv_pp2x_ext_buf_pool *ext_buf_pool; ++ ++ /* tx-done guard timer fields */ ++ struct mv_pp2x_port *port; /* reference to get from tx_done_timer */ ++ bool tx_done_passed; /* tx-done passed since last guard-check */ ++ u8 txq_coal_is_zero_map; /* map tx queues (max=8) forced coal=Zero */ ++ u8 txq_busy_suspect_map; /* map suspect txq to be forced */ ++ ++ /* rx bm refill worker fields */ ++ struct work_struct bm_refill_work; ++ int bm_refill[MVPP2_BM_SWF_NUM_POOLS]; ++}; ++ ++/* Per-CPU CP control */ ++struct mv_pp2x_cp_pcpu { ++ struct list_head skb_port_list; ++ struct mv_pp2x_skb_pool *skb_pool; ++ int in_use[MVPP2_BM_POOLS_NUM]; ++ ++ struct hrtimer tx_timer; ++ struct tasklet_struct tx_tasklet; ++ bool tx_timer_scheduled; ++}; ++ ++struct sub_queue_vector { ++ struct napi_struct napi; ++ struct call_single_data csd; ++ u32 queue_mask; ++ u8 cpu_id; ++ struct queue_vector *parent; ++ u32 pending_cause_rx; ++}; ++ ++struct queue_vector { ++ u32 irq; ++ char irq_name[IRQ_NAME_SIZE]; ++ struct napi_struct napi; ++ enum mv_pp2x_queue_vector_type qv_type; ++ u16 sw_thread_id; /* address_space index used to ++ * retrieve interrupt_cause ++ */ ++ u16 sw_thread_mask; /* Mask for Interrupt PORT_ENABLE Register */ ++ u8 first_rx_queue; /* Relative to port */ ++ u8 num_rx_queues; ++ u32 pending_cause_rx; /* mask in absolute port_queues, not relative as ++ * in Ethernet Occupied Interrupt Cause (EthOccIC)) ++ */ ++ u8 ap_id; /* ID of master AP */ ++ struct sub_queue_vector **sub_vec; ++ u8 num_of_sub_vectors; ++ struct mv_pp2x_port *parent; ++ ++ /* spinlock to protect queue_vector interrupt HW mask */ ++ spinlock_t hw_mask_lock; ++ u32 queue_mask; ++ bool cold_cpu; ++}; ++ ++struct mv_pp2x_ptp_desc; /* per-port private PTP descriptor */ ++ ++struct uio_queue_vector { ++ u32 irq; ++ char irq_name[IRQ_NAME_SIZE]; ++ u16 sw_thread_id; ++ u8 first_rx_queue; /* Relative to port */ ++ u8 num_rx_queues; ++ u32 queue_mask; /* Bits are not relative to interrupt, b0-b31=q0-q31 */ ++ struct mv_pp2x_port *parent; ++}; ++ ++struct mv_pp2x_port_uio { ++ struct uio_info u_info; ++ u32 num_qvector; ++ struct uio_queue_vector q_vector[MVPP2_MAX_ADDR_SPACES]; ++}; ++ ++struct mv_pp2x_port { ++ u8 id; ++ ++ u8 num_irqs; ++ u32 *of_irqs; ++ ++ struct mv_pp2x *priv; ++ ++ struct mv_mac_data mac_data; ++ struct tasklet_struct link_change_tasklet; ++ ++ /* Per-port registers' base address */ ++ void __iomem *base; ++ ++ /* Index of port's first physical RXQ */ ++ u8 first_rxq; ++ ++ /* port's number of rx_queues */ ++ u8 num_rx_queues; ++ /* port's number of tx_queues */ ++ u8 num_tx_queues; ++ ++ /* port's configured number of rx_queues */ ++ u8 cfg_num_rx_queues; ++ /* port's configured number of tx_queues */ ++ u8 cfg_num_tx_queues; ++ ++ struct mv_pp2x_rx_queue **rxqs; /*Each Port has up tp 32 rxq_queues.*/ ++ struct mv_pp2x_tx_queue **txqs; ++ struct net_device *dev; ++ ++ int pkt_size; /* pkt_size determines which is pool_long: ++ * jumbo_pool or regular long_pool. ++ */ ++ ++ /* Per-CPU port control */ ++ struct mv_pp2x_port_pcpu **pcpu; ++ /* Flags */ ++ u64 flags; ++ ++ u16 tx_ring_size; ++ u16 rx_ring_size; ++ ++ u32 tx_time_coal; ++ struct mv_pp2x_pcpu_stats __percpu *stats; ++ ++ struct mv_pp2x_bm_pool *pool_long; /* Pointer to the pool_id ++ * (long or jumbo) ++ */ ++ struct mv_pp2x_bm_pool *pool_short; /* Pointer to the short pool_id */ ++ ++ struct phy **comphy; /* comphy handler */ ++ int num_serdes_lanes; /* number of lanes allocated by port */ ++ int txq_stop_limit; ++ ++ u32 num_qvector; ++ ++ /* port's configured number of qvectors */ ++ u32 cfg_num_qvector; ++ ++ /* q_vector is the parameter that will be passed to ++ * mv_pp2_isr(int irq, void *dev_id=q_vector) ++ */ ++ ++ u8 ap_id; ++ ++ struct queue_vector *q_vector; ++ ++ struct mv_pp2x_ptp_desc *ptp_desc; ++ struct mv_pp2x_cos cos_cfg; ++ struct mv_pp2x_rss rss_cfg; ++ struct notifier_block port_hotplug_nb; ++ bool port_hotplugged; ++ bool use_interrupts; /* Used by Netmap */ ++ bool interrupt_tx_done; ++ ++ struct mv_pp2x_port_uio uio; ++ bool flow_control; ++ ++ u64 tx_guard_trigger; ++}; ++ ++struct pp2x_hw_params { ++ u8 desc_queue_addr_shift; ++}; ++ ++struct mv_pp2x_platform_data { ++ enum mvppv2_version pp2x_ver; ++ u8 pp2x_max_port_rxqs; ++ u8 num_port_irq; ++ bool multi_addr_space; ++ bool interrupt_tx_done; ++ bool multi_hw_instance; ++ void (*mv_pp2x_rxq_short_pool_set)(struct mv_pp2x_hw *, int, int); ++ void (*mv_pp2x_rxq_long_pool_set)(struct mv_pp2x_hw *, int, int); ++ void (*mv_pp2x_port_queue_vectors_init)(struct mv_pp2x_port *); ++ void (*mv_pp2x_port_isr_rx_group_cfg)(struct mv_pp2x_port *); ++ struct pp2x_hw_params hw; ++}; ++ ++struct mv_pp2x_ext_buf_struct { ++ struct list_head ext_buf_list; ++ u8 *ext_buf_data; ++}; ++ ++struct mv_pp2x_skb_struct { ++ struct list_head skb_list; ++ struct sk_buff *skb; ++}; ++ ++struct mv_pp2x_ext_buf_pool { ++ int buf_pool_size; ++ int buf_pool_next_free; ++ int buf_pool_in_use; ++ struct mv_pp2x_ext_buf_struct *ext_buf_struct; ++}; ++ ++struct mv_pp2x_skb_pool { ++ int skb_pool_size; ++ int skb_pool_in_use; ++ int skb_pool_next_free; ++ struct mv_pp2x_skb_struct *skb_struct; ++}; ++ ++static inline struct mv_pp2x_port *mv_pp2x_port_struct_get(struct mv_pp2x *priv, ++ int port) ++{ ++ int i; ++ ++ for (i = 0; i < priv->num_ports; i++) { ++ if (priv->port_list[i]->id == port) ++ return priv->port_list[i]; ++ } ++ return NULL; ++} ++ ++static inline ++struct mv_pp2x_port *mv_pp2x_port_struct_get_by_gop_index(struct mv_pp2x *priv, ++ int gop_index) ++{ ++ int i; ++ ++ for (i = 0; i < priv->num_ports; i++) { ++ if (priv->port_list[i]->mac_data.gop_index == gop_index) ++ return priv->port_list[i]; ++ } ++ return NULL; ++} ++ ++static inline u8 mv_pp2x_cosval_queue_map(struct mv_pp2x_port *port, ++ u8 cos_value) ++{ ++ int cos_width, cos_mask; ++ ++ cos_width = ilog2(roundup_pow_of_two( ++ port->cos_cfg.num_cos_queues)); ++ cos_mask = (1 << cos_width) - 1; ++ ++ return((port->cos_cfg.pri_map >> ++ (cos_value * 4)) & cos_mask); ++} ++ ++static inline u8 mv_pp2x_bound_cpu_first_rxq_calc(struct mv_pp2x_port *port) ++{ ++ u8 cos_width, bind_cpu; ++ ++ cos_width = ilog2(roundup_pow_of_two( ++ port->cos_cfg.num_cos_queues)); ++ bind_cpu = (port->priv->pp2_cfg.rx_cpu_map >> (4 * port->id)) & 0xF; ++ ++ return(port->first_rxq + (bind_cpu << cos_width)); ++} ++ ++/* Swap RX descriptor to be BE */ ++static inline void mv_pp21_rx_desc_swap(struct mv_pp2x_rx_desc *rx_desc) ++{ ++ cpu_to_le32s(&rx_desc->status); ++ cpu_to_le16s(&rx_desc->rsrvd_parser); ++ cpu_to_le16s(&rx_desc->data_size); ++ cpu_to_le32s(&rx_desc->u.pp21.buf_phys_addr); ++ cpu_to_le32s(&rx_desc->u.pp21.buf_cookie); ++ cpu_to_le16s(&rx_desc->u.pp21.rsrvd_gem); ++ cpu_to_le16s(&rx_desc->u.pp21.rsrvd_l4csum); ++ cpu_to_le16s(&rx_desc->u.pp21.rsrvd_cls_info); ++ cpu_to_le32s(&rx_desc->u.pp21.rsrvd_flow_id); ++ cpu_to_le32s(&rx_desc->u.pp21.rsrvd_abs); ++} ++ ++static inline void mv_pp22_rx_desc_swap(struct mv_pp2x_rx_desc *rx_desc) ++{ ++ cpu_to_le32s(&rx_desc->status); ++ cpu_to_le16s(&rx_desc->rsrvd_parser); ++ cpu_to_le16s(&rx_desc->data_size); ++ cpu_to_le16s(&rx_desc->u.pp22.rsrvd_gem); ++ cpu_to_le16s(&rx_desc->u.pp22.rsrvd_l4csum); ++ cpu_to_le32s(&rx_desc->u.pp22.rsrvd_timestamp); ++ cpu_to_le64s(&rx_desc->u.pp22.buf_phys_addr_key_hash); ++ cpu_to_le64s(&rx_desc->u.pp22.buf_cookie_bm_qset_cls_info); ++} ++ ++/* Swap TX descriptor to be BE */ ++static inline void mv_pp21_tx_desc_swap(struct mv_pp2x_tx_desc *tx_desc) ++{ ++ cpu_to_le32s(&tx_desc->command); ++ cpu_to_le16s(&tx_desc->data_size); ++ cpu_to_le32s(&tx_desc->u.pp21.buf_phys_addr); ++ cpu_to_le32s(&tx_desc->u.pp21.buf_cookie); ++ cpu_to_le32s(&tx_desc->u.pp21.rsrvd_hw_cmd[0]); ++ cpu_to_le32s(&tx_desc->u.pp21.rsrvd_hw_cmd[1]); ++ cpu_to_le32s(&tx_desc->u.pp21.rsrvd_hw_cmd[2]); ++ cpu_to_le32s(&tx_desc->u.pp21.rsrvd1); ++} ++ ++static inline void mv_pp22_tx_desc_swap(struct mv_pp2x_tx_desc *tx_desc) ++{ ++ cpu_to_le32s(&tx_desc->command); ++ cpu_to_le16s(&tx_desc->data_size); ++ cpu_to_le64s(&tx_desc->u.pp22.rsrvd_hw_cmd1); ++ cpu_to_le64s(&tx_desc->u.pp22.buf_phys_addr_hw_cmd2); ++ cpu_to_le64s(&tx_desc->u.pp22.buf_cookie_bm_qset_hw_cmd3); ++} ++ ++struct mv_pp2x_pool_attributes { ++ char description[32]; ++ int pkt_size; ++ int buf_num; ++}; ++ ++char *mv_pp2x_pool_description_get(enum mv_pp2x_bm_pool_log_num log_id); ++ ++void mv_pp2x_bm_bufs_free(struct device *dev, struct mv_pp2x *priv, ++ struct mv_pp2x_bm_pool *bm_pool, int buf_num); ++int mv_pp2x_bm_bufs_add(struct mv_pp2x_port *port, ++ struct mv_pp2x_bm_pool *bm_pool, int buf_num); ++int mv_pp2x_bm_pool_ext_add(struct device *dev, struct mv_pp2x *priv, ++ u32 *pool_num, u32 pkt_size); ++int mv_pp2x_bm_pool_destroy(struct device *dev, struct mv_pp2x *priv, ++ struct mv_pp2x_bm_pool *bm_pool); ++int mv_pp2x_swf_bm_pool_assign(struct mv_pp2x_port *port, u32 rxq, ++ u32 long_id, u32 short_id); ++void mv_pp2x_bm_pool_update_fc(struct mv_pp2x_port *port, struct mv_pp2x_bm_pool *pool, ++ int add_num); ++void mv_pp2x_rxq_disable_fc(struct mv_pp2x_port *port); ++void mv_pp2x_rxq_enable_fc(struct mv_pp2x_port *port); ++int mv_pp2x_open(struct net_device *dev); ++int mv_pp2x_stop(struct net_device *dev); ++void mv_pp2x_txq_inc_put(enum mvppv2_version pp2_ver, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ struct sk_buff *skb, ++ struct mv_pp2x_tx_desc *tx_desc); ++int mv_pp2x_check_ringparam_valid(struct net_device *dev, ++ struct ethtool_ringparam *ring); ++void mv_pp2x_start_dev(struct mv_pp2x_port *port); ++void mv_pp2x_stop_dev(struct mv_pp2x_port *port); ++void mv_pp2x_cleanup_rxqs(struct mv_pp2x_port *port); ++int mv_pp2x_setup_rxqs(struct mv_pp2x_port *port); ++int mv_pp2x_setup_txqs(struct mv_pp2x_port *port); ++void mv_pp2x_cleanup_txqs(struct mv_pp2x_port *port); ++void mv_pp2x_set_ethtool_ops(struct net_device *netdev); ++void mv_pp2x_set_non_kernel_ethtool_ops(struct net_device *netdev); ++int mv_pp22_rss_rxfh_indir_set(struct mv_pp2x_port *port); ++int mv_pp2x_cos_classifier_set(struct mv_pp2x_port *port, ++ enum mv_pp2x_cos_classifier cos_mode); ++int mv_pp2x_cos_classifier_get(struct mv_pp2x_port *port); ++int mv_pp2x_cos_pri_map_set(struct mv_pp2x_port *port, int cos_pri_map); ++int mv_pp2x_cos_pri_map_get(struct mv_pp2x_port *port); ++int mv_pp2x_cos_default_value_set(struct mv_pp2x_port *port, int cos_value); ++int mv_pp2x_cos_default_value_get(struct mv_pp2x_port *port); ++int mv_pp22_rss_mode_set(struct mv_pp2x_port *port, int rss_mode); ++int mv_pp22_rss_default_cpu_set(struct mv_pp2x_port *port, int default_cpu); ++int mv_pp2x_txq_reserved_desc_num_proc(struct mv_pp2x *priv, ++ struct mv_pp2x_tx_queue *txq, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ int num, int cpu); ++int mv_pp2x_port_musdk_set(void *netdev_priv); ++int mv_pp2x_port_musdk_clear(void *netdev_priv); ++void mv_pp23_rx_fifo_fc_en(struct mv_pp2x *priv, int port, bool en); ++u32 mvp_pp2x_gop110_netc_cfg_create(struct mv_pp2x *priv); ++ ++#endif /*_MVPP2_H_*/ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.c b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.c +new file mode 100644 +index 00000000..83cfb1d +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.c +@@ -0,0 +1,99 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mv_pp2x.h" ++#include "mv_pp2x_hw.h" ++#include "mv_pp2x_debug.h" ++ ++int mv_pp2x_debug_param_set(u32 param) ++{ ++ debug_param = param; ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_debug_param_set); ++ ++int mv_pp2x_debug_param_get(void) ++{ ++ return debug_param; ++} ++EXPORT_SYMBOL(mv_pp2x_debug_param_get); ++ ++/* Extra debug */ ++void mv_pp2x_skb_dump(struct sk_buff *skb, int size, int access) ++{ ++ int i, j; ++ void *addr = skb->head + NET_SKB_PAD; ++ uintptr_t mem_addr = (uintptr_t)addr; ++ ++ DBG_MSG("skb=%p, buf=%p, ksize=%d\n", skb, skb->head, ++ (int)ksize(skb->head)); ++ ++ if (access == 0) ++ access = 1; ++ ++ if ((access != 4) && (access != 2) && (access != 1)) { ++ pr_err("%d wrong access size. Access must be 1 or 2 or 4\n", ++ access); ++ return; ++ } ++ mem_addr = round_down((uintptr_t)addr, 4); ++ size = round_up(size, 4); ++ addr = (void *)round_down((uintptr_t)addr, access); ++ ++ while (size > 0) { ++ DBG_MSG("%08lx: ", mem_addr); ++ i = 0; ++ /* 32 bytes in the line */ ++ while (i < 32) { ++ if (mem_addr >= (uintptr_t)addr) { ++ switch (access) { ++ case 1: ++ DBG_MSG("%02x ", ++ ioread8((void *)mem_addr)); ++ break; ++ ++ case 2: ++ DBG_MSG("%04x ", ++ ioread16((void *)mem_addr)); ++ break; ++ ++ case 4: ++ DBG_MSG("%08x ", ++ ioread32((void *)mem_addr)); ++ break; ++ } ++ } else { ++ for (j = 0; j < (access * 2 + 1); j++) ++ DBG_MSG(" "); ++ } ++ i += access; ++ mem_addr += access; ++ size -= access; ++ if (size <= 0) ++ break; ++ } ++ DBG_MSG("\n"); ++ } ++} ++ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.h b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.h +new file mode 100644 +index 00000000..8717b01 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_debug.h +@@ -0,0 +1,33 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#ifndef _MVPP2_DEBUG_H_ ++#define _MVPP2_DEBUG_H_ ++ ++#include ++#include ++ ++#define DBG_MSG(fmt, args...) printk(fmt, ## args) ++ ++void mv_pp2x_skb_dump(struct sk_buff *skb, int size, int access); ++ ++int mv_pp2x_debug_param_set(u32 param); ++ ++int mv_pp2x_debug_param_get(void); ++ ++#endif /* _MVPP2_DEBUG_H_ */ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c +new file mode 100644 +index 00000000..cffb86f +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c +@@ -0,0 +1,1343 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mv_pp2x.h" ++#include "mv_pp2x_hw.h" ++#include "mv_gop110_hw.h" ++ ++#define MV_PP2_STATS_LEN ARRAY_SIZE(mv_pp2x_gstrings_stats) ++#define MV_PP2_TEST_LEN ARRAY_SIZE(mv_pp2x_gstrings_test) ++#define MV_PP2_REGS_GMAC_LEN 54 ++#define MV_PP2_REGS_XLG_LEN 25 ++#define MV_PP2_TEST_MASK1 0xFFFF ++#define MV_PP2_TEST_MASK2 0x00FE ++#define MV_PP2_TEST_MASK3 0x0 ++#define MV_PP2_TEST_PATTERN1 0xFFFF ++#define MV_PP2_TEST_PATTERN2 0x00FE ++#define MV_PP2_TEST_PATTERN3 0x0 ++ ++static const char mv_pp2x_gstrings_test[][ETH_GSTRING_LEN] = { ++ "Link test (on/offline)", ++ "register test (on/offline)", ++}; ++ ++static const char mv_pp2x_gstrings_stats[][ETH_GSTRING_LEN] = { ++ /* device-specific stats */ ++ "rx_bytes", "rx_frames", "rx_unicast", "rx_mcast", "rx_bcast", ++ "tx_bytes", "tx_frames", "tx_unicast", "tx_mcast", "tx_bcast", ++ "rx_pause", "tx_pause", "rx_mac_overrun", "rx_crc", "rx_runt", ++ "rx_giant", "rx_fragments_err", "rx_mac_err", "rx_jabber", "rx_ppv2_overrun", ++ "rx_cls_drop", "rx_fullq_drop", "rx_early_drop", "rx_bm_drop", ++ "rx_total_err", "rx_sw_drop", "rx_hw_drop", "tx_crc_sent", ++ "tx_drop", "collision", "late_collision", "frames_64", "frames_65_to_127", ++ "frames_128_to_255", "frames_256_to_511", "frames_512_to_1023", "frames_1024_to_max", ++ "rx_fullq_drop_q0", "rx_fullq_drop_q1", "rx_fullq_drop_q2", "rx_fullq_drop_q3", ++ "rx_fullq_drop_q4", "rx_fullq_drop_q5", "rx_fullq_drop_q6", "rx_fullq_drop_q7", ++ "rx_fullq_drop_q8", "rx_fullq_drop_q9", "rx_fullq_drop_q10", "rx_fullq_drop_q11", ++ "rx_fullq_drop_q12", "rx_fullq_drop_q13", "rx_fullq_drop_q14", "rx_fullq_drop_q15", ++ "rx_fullq_drop_q16", "rx_fullq_drop_q17", "rx_fullq_drop_q18", "rx_fullq_drop_q19", ++ "rx_full_drop_q20", "rx_fullq_drop_q21", "rx_fullq_drop_q22", "rx_fullq_drop_q23", ++ "rx_fullq_drop_q24", "rx_fullq_drop_q25", "rx_fullq_drop_q26", "rx_fullq_drop_q27", ++ "rx_fullq_drop_q28", "rx_fullq_drop_q29", "rx_fullq_drop_q30", "rx_fullq_drop_q31", ++ "rx_earlyq_drop_q0", "rx_earlyq_drop_q1", "rx_earlyq_drop_q2", "rx_earlyq_drop_q3", ++ "rx_earlyq_drop_q4", "rx_earlyq_drop_q5", "rx_earlyq_drop_q6", "rx_earlyq_drop_q7", ++ "rx_earlyq_drop_q8", "rx_earlyq_drop_q9", "rx_earlyq_drop_q10", "rx_earlyq_drop_q11", ++ "rx_earlyq_drop_q12", "rx_earlyq_drop_q13", "rx_earlyq_drop_q14", "rx_earlyq_drop_q15", ++ "rx_earlyq_drop_q16", "rx_earlyq_drop_q17", "rx_earlyq_drop_q18", "rx_earlyq_drop_q19", ++ "rx_earlyq_drop_q20", "rx_earlyq_drop_q21", "rx_earlyq_drop_q22", "rx_earlyq_drop_q23", ++ "rx_earlyq_drop_q24", "rx_earlyq_drop_q25", "rx_earlyq_drop_q26", "rx_earlyq_drop_q27", ++ "rx_earlyq_drop_q28", "rx_earlyq_drop_q29", "rx_earlyq_drop_q30", "rx_earlyq_drop_q31", ++ "rx_bm_dropq_q0", "rx_bm_dropq_q1", "rx_bm_dropq_q2", "rx_bm_dropq_q3", ++ "rx_bm_dropq_q4", "rx_bm_dropq_q5", "rx_bm_dropq_q6", "rx_bm_dropq_q7", ++ "rx_bm_dropq_q8", "rx_bm_dropq_q9", "rx_bm_dropq_q10", "rx_bm_dropq_q11", ++ "rx_bm_dropq_q12", "rx_bm_dropq_q13", "rx_bm_dropq_q14", "rx_bm_dropq_q15", ++ "rx_bm_dropq_q16", "rx_bm_dropq_q17", "rx_bm_dropq_q18", "rx_bm_dropq_q19", ++ "rx_bm_dropq_q20", "rx_bm_dropq_q21", "rx_bm_dropq_q22", "rx_bm_dropq_q23", ++ "rx_bm_dropq_q24", "rx_bm_dropq_q25", "rx_bm_dropq_q26", "rx_bm_dropq_q27", ++ "rx_bm_dropq_q28", "rx_bm_dropq_q29", "rx_bm_dropq_q30", "rx_bm_dropq_q31", ++ "tx-guard-trigger", ++}; ++ ++int mv_pp2x_check_speed_duplex_valid(struct ethtool_cmd *cmd, ++ struct mv_port_link_status *pstatus) ++{ ++ switch (cmd->duplex) { ++ case DUPLEX_FULL: ++ pstatus->duplex = MV_PORT_DUPLEX_FULL; ++ break; ++ case DUPLEX_HALF: ++ pstatus->duplex = MV_PORT_DUPLEX_HALF; ++ break; ++ case DUPLEX_UNKNOWN: ++ if (cmd->speed == SPEED_1000) { ++ pstatus->duplex = MV_PORT_DUPLEX_FULL; ++ } else { ++ pstatus->duplex = MV_PORT_DUPLEX_FULL; ++ pr_err("Unknown duplex configuration, full duplex set\n"); ++ } ++ break; ++ default: ++ pr_err("Wrong duplex configuration\n"); ++ return -1; ++ } ++ ++ switch (cmd->speed) { ++ case SPEED_100: ++ pstatus->speed = MV_PORT_SPEED_100; ++ return 0; ++ case SPEED_10: ++ pstatus->speed = MV_PORT_SPEED_10; ++ return 0; ++ case SPEED_1000: ++ pstatus->speed = MV_PORT_SPEED_1000; ++ if (cmd->duplex) ++ return 0; ++ pr_err("1G port doesn't support half duplex\n"); ++ return -1; ++ default: ++ pr_err("Wrong speed configuration\n"); ++ return -1; ++ } ++} ++ ++int mv_pp2x_autoneg_gmac_check_valid(struct mv_mac_data *mac, struct gop_hw *gop, ++ struct ethtool_cmd *cmd, struct mv_port_link_status *pstatus) ++{ ++ int port_num = mac->gop_index; ++ int err; ++ ++ err = mv_gop110_check_port_type(gop, port_num); ++ if (err) { ++ if (cmd->autoneg) { ++ pr_err("GOP %d set to 1000Base-X and doesn't support autonegotiation\n", port_num); ++ return -EINVAL; ++ } ++ return 0; ++ } ++ if (!cmd->autoneg) { ++ err = mv_pp2x_check_speed_duplex_valid(cmd, pstatus); ++ if (err) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int mv_pp2x_autoneg_xlg_check_valid(struct mv_mac_data *mac, struct ethtool_cmd *cmd) ++{ ++ int port_num = mac->gop_index; ++ ++ if (cmd->autoneg) { ++ pr_err("XLG GOP %d doesn't support autonegotiation\n", port_num); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++void mv_pp2x_ethtool_valid_coalesce(struct ethtool_coalesce *c, ++ struct mv_pp2x_port *port) ++{ ++ u64 val; ++ ++ if (c->rx_max_coalesced_frames > MVPP2_MAX_OCCUPIED_THRESH) ++ pr_err("RX coalesced frames value too high, rounded to %d\n", ++ MVPP2_MAX_OCCUPIED_THRESH); ++ ++ if (c->tx_max_coalesced_frames > MVPP2_MAX_TRANSMITTED_THRESH) { ++ pr_err("TX coalesced frames value too high, rounded to %d\n", ++ MVPP2_MAX_TRANSMITTED_THRESH); ++ c->tx_max_coalesced_frames = MVPP2_MAX_TRANSMITTED_THRESH; ++ } ++ ++ val = (port->priv->hw.tclk / USEC_PER_SEC) * c->rx_coalesce_usecs; ++ if (val > MVPP2_MAX_ISR_RX_THRESHOLD) ++ pr_err("RX coalesced time value too high, rounded to %ld usecs\n", ++ (MVPP2_MAX_ISR_RX_THRESHOLD * USEC_PER_SEC) ++ / port->priv->hw.tclk); ++ ++ val = (port->priv->hw.tclk / USEC_PER_SEC) * c->tx_coalesce_usecs; ++ if (val > MVPP22_MAX_ISR_TX_THRESHOLD) { ++ pr_err("TX coalesced time value too high, rounded to %ld usecs\n", ++ (MVPP22_MAX_ISR_TX_THRESHOLD * USEC_PER_SEC) ++ / port->priv->hw.tclk); ++ c->tx_coalesce_usecs = ++ (MVPP22_MAX_ISR_TX_THRESHOLD * USEC_PER_SEC) ++ / port->priv->hw.tclk; ++ } ++} ++ ++/* Ethtool methods */ ++ ++/* Ethtool statistic */ ++static void mv_pp2x_eth_tool_get_ethtool_stats(struct net_device *dev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_mac_data *mac = &port->mac_data; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ int gop_port = mac->gop_index; ++ struct gop_stat *gop_statistics = &mac->gop_statistics; ++ int queue_num, i = 0; ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ ++ mv_gop110_mib_counters_stat_update(gop, gop_port, gop_statistics); ++ mv_pp2x_counters_stat_update(port, gop_statistics); ++ ++ data[i++] = gop_statistics->rx_byte; ++ data[i++] = gop_statistics->rx_frames; ++ data[i++] = gop_statistics->rx_unicast; ++ data[i++] = gop_statistics->rx_mcast; ++ data[i++] = gop_statistics->rx_bcast; ++ data[i++] = gop_statistics->tx_byte; ++ data[i++] = gop_statistics->tx_frames; ++ data[i++] = gop_statistics->tx_unicast; ++ data[i++] = gop_statistics->tx_mcast; ++ data[i++] = gop_statistics->tx_bcast; ++ data[i++] = gop_statistics->rx_pause; ++ data[i++] = gop_statistics->tx_pause; ++ data[i++] = gop_statistics->rx_mac_overrun; ++ data[i++] = gop_statistics->rx_crc; ++ data[i++] = gop_statistics->rx_runt; ++ data[i++] = gop_statistics->rx_giant; ++ data[i++] = gop_statistics->rx_fragments_err; ++ data[i++] = gop_statistics->rx_mac_err; ++ data[i++] = gop_statistics->rx_jabber; ++ data[i++] = gop_statistics->rx_ppv2_overrun; ++ data[i++] = gop_statistics->rx_cls_drop; ++ data[i++] = gop_statistics->rx_fullq_drop; ++ data[i++] = gop_statistics->rx_early_drop; ++ data[i++] = gop_statistics->rx_bm_drop; ++ data[i++] = gop_statistics->rx_total_err; ++ data[i++] = gop_statistics->rx_sw_drop; ++ data[i++] = gop_statistics->rx_hw_drop; ++ data[i++] = gop_statistics->tx_crc_sent; ++ data[i++] = dev->stats.tx_dropped; ++ data[i++] = gop_statistics->collision; ++ data[i++] = gop_statistics->late_collision; ++ data[i++] = gop_statistics->frames_64; ++ data[i++] = gop_statistics->frames_65_to_127; ++ data[i++] = gop_statistics->frames_128_to_255; ++ data[i++] = gop_statistics->frames_256_to_511; ++ data[i++] = gop_statistics->frames_512_to_1023; ++ data[i++] = gop_statistics->frames_1024_to_max; ++ for (queue_num = 0; queue_num < MVPP22_MAX_NUM_RXQ; queue_num++) ++ data[i++] = gop_statistics->rx_perq_fullq_drop[queue_num]; ++ ++ for (queue_num = 0; queue_num < MVPP22_MAX_NUM_RXQ; queue_num++) ++ data[i++] = gop_statistics->rx_perq_early_drop[queue_num]; ++ ++ for (queue_num = 0; queue_num < MVPP22_MAX_NUM_RXQ; queue_num++) ++ data[i++] = gop_statistics->rx_perq_bm_drop[queue_num]; ++ data[i++] = port->tx_guard_trigger; ++} ++ ++static void mv_pp2x_eth_tool_get_strings(struct net_device *dev, ++ u32 stringset, u8 *data) ++{ ++ switch (stringset) { ++ case ETH_SS_TEST: ++ memcpy(data, *mv_pp2x_gstrings_test, sizeof(mv_pp2x_gstrings_test)); ++ break; ++ case ETH_SS_STATS: ++ memcpy(data, *mv_pp2x_gstrings_stats, sizeof(mv_pp2x_gstrings_stats)); ++ break; ++ default: ++ break; ++ } ++} ++ ++static int mv_pp2x_eth_tool_get_sset_count(struct net_device *dev, int sset) ++{ ++ switch (sset) { ++ case ETH_SS_TEST: ++ return MV_PP2_TEST_LEN; ++ case ETH_SS_STATS: ++ return MV_PP2_STATS_LEN; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++/* Restart autonegotiation function */ ++int mv_pp2x_eth_tool_nway_reset(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ int err; ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ if (!(mac->flags & MV_EMAC_F_INIT)) { ++ pr_err("%s: interface %s is not initialized\n", __func__, dev->name); ++ return -EOPNOTSUPP; ++ } ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ err = mv_gop110_check_port_type(gop, mac->gop_index); ++ if (err) { ++ pr_err("GOP %d set to 1000Base-X\n", mac->gop_index); ++ return -EINVAL; ++ } ++ mv_gop110_autoneg_restart(gop, mac); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ pr_err("XLG GOP %d doesn't support autonegotiation\n", mac->gop_index); ++ return -ENODEV; ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)\n", __func__, mac->phy_mode); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Get pause fc settings for ethtools */ ++static void mv_pp2x_get_pauseparam(struct net_device *dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_port_link_status status; ++ struct mv_mac_data *mac = &port->mac_data; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ phy_interface_t phy_mode; ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ ++ phy_mode = port->mac_data.phy_mode; ++ ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_port_link_status(gop, mac, &status); ++ pause->autoneg = ++ (status.autoneg_fc ? AUTONEG_ENABLE : AUTONEG_DISABLE); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_port_link_status(gop, mac, &status); ++ pause->autoneg = AUTONEG_DISABLE; ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, phy_mode); ++ return; ++ } ++ ++ if (status.rx_fc == MV_PORT_FC_ACTIVE || status.rx_fc == MV_PORT_FC_ENABLE) ++ pause->rx_pause = 1; ++ ++ if (port->flow_control && (status.tx_fc == MV_PORT_FC_ENABLE || status.tx_fc == MV_PORT_FC_ACTIVE)) ++ pause->tx_pause = 1; ++} ++ ++/* Set pause fc settings for ethtools */ ++static int mv_pp2x_set_pauseparam(struct net_device *dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_mac_data *mac = &port->mac_data; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ int gop_port = mac->gop_index; ++ phy_interface_t phy_mode; ++ int err; ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ if (!(mac->flags & MV_EMAC_F_INIT)) { ++ pr_err("%s: interface %s is not initialized\n", __func__, dev->name); ++ return -EOPNOTSUPP; ++ } ++ ++ phy_mode = port->mac_data.phy_mode; ++ ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ if (mac->speed == SPEED_2500) { ++ err = mv_gop110_check_port_type(gop, gop_port); ++ if (err) { ++ pr_err("Peridot module doesn't support FC\n"); ++ return -EINVAL; ++ } ++ } ++ ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ ++ if (pause->autoneg) { ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_AN_SYM); ++ mv_gop110_autoneg_restart(gop, mac); ++ } else { ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_AN_NO); ++ } ++ ++ if (pause->rx_pause) ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_RX_ENABLE); ++ else ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_RX_DISABLE); ++ ++ if (pause->tx_pause) { ++ if (port->rx_ring_size < MSS_CP_CM3_THRESHOLD_START) { ++ pr_err("TX FC cannot be supported. "); ++ pr_err("Ring size is less than %d\n", MSS_CP_CM3_THRESHOLD_START); ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ return -EINVAL; ++ } ++ port->flow_control = true; ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_TX_ENABLE); ++ mv_pp2x_rxq_enable_fc(port); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_long, 1); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_short, 1); ++ if (port->priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_en(port->priv, port->id, true); ++ } else { ++ port->flow_control = false; ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_TX_DISABLE); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_long, -1); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_short, -1); ++ mv_pp2x_rxq_disable_fc(port); ++ if (port->priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_en(port->priv, port->id, false); ++ } ++ ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ if (pause->autoneg) { ++ pr_err("10G port doesn't support fc autoneg\n"); ++ return -EINVAL; ++ } ++ if (pause->rx_pause) ++ mv_gop110_xlg_mac_fc_set(gop, gop_port, MV_PORT_FC_RX_ENABLE); ++ else ++ mv_gop110_xlg_mac_fc_set(gop, gop_port, MV_PORT_FC_RX_DISABLE); ++ ++ if (pause->tx_pause) { ++ if (port->rx_ring_size < MSS_CP_CM3_THRESHOLD_START) { ++ pr_err("TX FC cannot be supported. "); ++ pr_err("Ring size is less than %d\n", MSS_CP_CM3_THRESHOLD_START); ++ return -EINVAL; ++ } ++ port->flow_control = true; ++ mv_gop110_xlg_mac_fc_set(gop, gop_port, MV_PORT_FC_TX_ENABLE); ++ mv_pp2x_rxq_enable_fc(port); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_long, 1); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_short, 1); ++ if (port->priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_en(port->priv, port->id, true); ++ } else { ++ port->flow_control = false; ++ mv_pp2x_bm_pool_update_fc(port, port->pool_long, -1); ++ mv_pp2x_bm_pool_update_fc(port, port->pool_short, -1); ++ mv_gop110_xlg_mac_fc_set(gop, gop_port, MV_PORT_FC_TX_DISABLE); ++ mv_pp2x_rxq_disable_fc(port); ++ if (port->priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_en(port->priv, port->id, false); ++ } ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, phy_mode); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* function differ between 2500 and 1000 speeds */ ++int mv_pp2x_get_gmii_speed(struct mv_pp2x_port *port) ++{ ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ int comphy_mode; ++#endif ++ int port_speed = SPEED_1000; ++ ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) { ++ comphy_mode = COMPHY_GET_MODE(phy_get_mode(port->comphy[0])); ++ if (comphy_mode == COMPHY_HS_SGMII_MODE) ++ port_speed = SPEED_2500; ++ } ++#endif ++ ++ return port_speed; ++} ++ ++/* Get settings (phy address, speed) for ethtools */ ++static int mv_pp2x_ethtool_get_settings(struct net_device *dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_port_link_status status; ++ phy_interface_t phy_mode; ++ ++ if (port->priv->pp2_version == PPV21) { ++ if (!port->mac_data.phy_dev) ++ return -ENODEV; ++ return phy_ethtool_gset(port->mac_data.phy_dev, cmd); ++ } ++ ++ /* No Phy device mngmt */ ++ if (!port->mac_data.phy_dev) { ++ /*for force link port, RXAUI port and link-down ports, ++ * follow old strategy ++ */ ++ ++ mv_gop110_port_link_status(&port->priv->hw.gop, ++ &port->mac_data, &status); ++ ++ if (status.linkup) { ++ switch (status.speed) { ++ case MV_PORT_SPEED_10000: ++ cmd->speed = SPEED_10000; ++ break; ++ case MV_PORT_SPEED_1000: ++ cmd->speed = mv_pp2x_get_gmii_speed(port); ++ break; ++ case MV_PORT_SPEED_100: ++ cmd->speed = SPEED_100; ++ break; ++ case MV_PORT_SPEED_10: ++ cmd->speed = SPEED_10; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (status.duplex == MV_PORT_DUPLEX_FULL) ++ cmd->duplex = 1; ++ else ++ cmd->duplex = 0; ++ } else { ++ cmd->speed = SPEED_UNKNOWN; ++ cmd->duplex = SPEED_UNKNOWN; ++ } ++ ++ phy_mode = port->mac_data.phy_mode; ++ if ((phy_mode == PHY_INTERFACE_MODE_XAUI) || ++ (phy_mode == PHY_INTERFACE_MODE_RXAUI) || ++ (phy_mode == PHY_INTERFACE_MODE_10GKR) || ++ (phy_mode == PHY_INTERFACE_MODE_SFI) || ++ (phy_mode == PHY_INTERFACE_MODE_XFI)) { ++ cmd->autoneg = AUTONEG_DISABLE; ++ cmd->supported = (SUPPORTED_10000baseT_Full | ++ SUPPORTED_FIBRE); ++ cmd->advertising = (ADVERTISED_10000baseT_Full | ++ ADVERTISED_FIBRE); ++ cmd->port = PORT_FIBRE; ++ cmd->transceiver = XCVR_EXTERNAL; ++ } else { ++ cmd->supported = (SUPPORTED_10baseT_Half | ++ SUPPORTED_10baseT_Full | ++ SUPPORTED_100baseT_Half | ++ SUPPORTED_100baseT_Full | ++ SUPPORTED_Autoneg | SUPPORTED_TP | ++ SUPPORTED_MII | SUPPORTED_1000baseT_Full); ++ cmd->advertising = (ADVERTISED_10baseT_Half | ++ ADVERTISED_10baseT_Full | ++ ADVERTISED_100baseT_Half | ++ ADVERTISED_100baseT_Full | ++ ADVERTISED_1000baseT_Full | ++ ADVERTISED_Autoneg | ADVERTISED_TP | ++ ADVERTISED_MII); ++ cmd->transceiver = XCVR_INTERNAL; ++ cmd->port = PORT_MII; ++ ++ /* check if speed and duplex are AN */ ++ if (mv_gop110_port_autoneg_status(&port->priv->hw.gop, ++ &port->mac_data)) { ++ cmd->autoneg = AUTONEG_ENABLE; ++ } else { ++ cmd->autoneg = AUTONEG_DISABLE; ++ } ++ } ++ ++ return 0; ++ } ++ ++ return phy_ethtool_gset(port->mac_data.phy_dev, cmd); ++} ++ ++void mv_pp2x_ethtool_set_gmac_config(struct mv_port_link_status status, struct gop_hw *gop, ++ int gop_port, struct mv_mac_data *mac, struct ethtool_cmd *cmd) ++{ ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ mv_gop110_gmac_set_autoneg(gop, mac, cmd->autoneg); ++ if (cmd->autoneg) { ++ mv_gop110_autoneg_restart(gop, mac); ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_AN_SYM); ++ } else { ++ mv_gop110_gmac_speed_duplex_set(gop, gop_port, status.speed, status.duplex); ++ mv_gop110_gmac_fc_set(gop, gop_port, MV_PORT_FC_AN_NO); ++ } ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++} ++ ++/* Set settings (phy address, speed) for ethtools */ ++static int mv_pp2x_ethtool_set_settings(struct net_device *dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int err; ++ struct mv_port_link_status status; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ int gop_port = mac->gop_index; ++ ++ /* PPv21 - only PHY should be configured ++ * PPv22 - set Serdes&GoP configuration and then configure PHY ++ */ ++ if (port->priv->pp2_version == PPV21) { ++ if (!port->mac_data.phy_dev) ++ return -ENODEV; ++ else ++ return phy_ethtool_sset(port->mac_data.phy_dev, cmd); ++ } ++ ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) { ++ if (phy_get_mode(port->comphy[0]) != COMPHY_RXAUI0) { ++ err = mv_gop110_update_comphy(port, (u32)cmd->speed); ++ if (err < 0) ++ return err; ++ } else if (cmd->speed != SPEED_10000) { ++ pr_err("RXAUI port cannot change speed\n"); ++ return -1; ++ } ++ } ++#endif ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ err = mv_pp2x_autoneg_gmac_check_valid(mac, gop, cmd, &status); ++ if (err < 0) ++ return err; ++ if (cmd->speed != SPEED_2500) ++ mv_pp2x_ethtool_set_gmac_config(status, gop, gop_port, mac, cmd); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ err = mv_pp2x_autoneg_xlg_check_valid(mac, cmd); ++ if (err < 0) ++ return err; ++ break; ++ default: ++ pr_err("Wrong port mode (%d)\n", mac->phy_mode); ++ return -1; ++ } ++ ++ if (port->mac_data.phy_dev) ++ return phy_ethtool_sset(port->mac_data.phy_dev, cmd); ++ ++ return 0; ++} ++ ++/* Set interrupt coalescing for ethtools */ ++static int mv_pp2x_ethtool_set_coalesce(struct net_device *dev, ++ struct ethtool_coalesce *c) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int queue; ++ ++ /* Check for not supported parameters */ ++ if ((c->rx_coalesce_usecs_irq) || ++ (c->rx_max_coalesced_frames_irq) || ++ (c->tx_coalesce_usecs_irq) || ++ (c->tx_max_coalesced_frames_irq) || ++ (c->stats_block_coalesce_usecs) || ++ (c->use_adaptive_rx_coalesce) || ++ (c->use_adaptive_tx_coalesce) || ++ (c->pkt_rate_low) || ++ (c->rx_coalesce_usecs_low) || ++ (c->rx_max_coalesced_frames_low) || ++ (c->tx_coalesce_usecs_low) || ++ (c->tx_max_coalesced_frames_low) || ++ (c->pkt_rate_high) || ++ (c->rx_coalesce_usecs_high) || ++ (c->rx_max_coalesced_frames_high) || ++ (c->tx_coalesce_usecs_high) || ++ (c->tx_max_coalesced_frames_high) || ++ (c->rate_sample_interval)) { ++ netdev_err(dev, "unsupported coalescing parameter\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ mv_pp2x_ethtool_valid_coalesce(c, port); ++ ++ for (queue = 0; queue < port->num_rx_queues; queue++) { ++ struct mv_pp2x_rx_queue *rxq = port->rxqs[queue]; ++ ++ rxq->time_coal = c->rx_coalesce_usecs; ++ rxq->pkts_coal = c->rx_max_coalesced_frames; ++ mv_pp2x_rx_pkts_coal_set(port, rxq); ++ mv_pp2x_rx_time_coal_set(port, rxq); ++ } ++ port->tx_time_coal = c->tx_coalesce_usecs; ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ struct mv_pp2x_tx_queue *txq = port->txqs[queue]; ++ ++ txq->pkts_coal = c->tx_max_coalesced_frames; ++ } ++ if (port->interrupt_tx_done) { ++ mv_pp2x_tx_done_time_coal_set(port, port->tx_time_coal); ++ mv_pp2x_tx_done_pkts_coal_set_all(port); ++ } else { ++ int cpu; ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ ++ for_each_present_cpu(cpu) { ++ port_pcpu = port->pcpu[cpu]; ++ port_pcpu->tx_time_coal_hrtmr = ++ ktime_set(0, port->tx_time_coal * NSEC_PER_USEC); ++ } ++ } ++ ++ return 0; ++} ++ ++/* get coalescing for ethtools */ ++static int mv_pp2x_ethtool_get_coalesce(struct net_device *dev, ++ struct ethtool_coalesce *c) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ c->rx_coalesce_usecs = port->rxqs[0]->time_coal; ++ c->rx_max_coalesced_frames = port->rxqs[0]->pkts_coal; ++ c->tx_max_coalesced_frames = port->txqs[0]->pkts_coal; ++ c->tx_coalesce_usecs = port->tx_time_coal; ++ ++ return 0; ++} ++ ++static void mv_pp2x_ethtool_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *drvinfo) ++{ ++ strlcpy(drvinfo->driver, MVPP2_DRIVER_NAME, ++ sizeof(drvinfo->driver)); ++ strlcpy(drvinfo->version, MVPP2_DRIVER_VERSION, ++ sizeof(drvinfo->version)); ++ strlcpy(drvinfo->bus_info, dev_name(&dev->dev), ++ sizeof(drvinfo->bus_info)); ++} ++ ++static void mv_pp2x_ethtool_get_ringparam(struct net_device *dev, ++ struct ethtool_ringparam *ring) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ ring->rx_max_pending = MVPP2_MAX_RXD; ++ ring->tx_max_pending = MVPP2_MAX_TXD; ++ ring->rx_pending = port->rx_ring_size; ++ ring->tx_pending = port->tx_ring_size; ++} ++ ++static int mv_pp2x_ethtool_set_ringparam(struct net_device *dev, ++ struct ethtool_ringparam *ring) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ u16 prev_rx_ring_size = port->rx_ring_size; ++ u16 prev_tx_ring_size = port->tx_ring_size; ++ int err; ++ ++ err = mv_pp2x_check_ringparam_valid(dev, ring); ++ if (err) ++ return err; ++ ++ if (!netif_running(dev)) { ++ port->rx_ring_size = ring->rx_pending; ++ port->tx_ring_size = ring->tx_pending; ++ return 0; ++ } ++ ++ if ((ring->rx_pending < MSS_CP_CM3_THRESHOLD_START) && port->flow_control) { ++ pr_warn("TX FC disabled. Ring size is less than %d\n", MSS_CP_CM3_THRESHOLD_START); ++ port->flow_control = false; ++ mv_gop110_gmac_fc_set(&port->priv->hw.gop, port->mac_data.gop_index, MV_PORT_FC_TX_DISABLE); ++ mv_pp2x_rxq_disable_fc(port); ++ if (port->priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_en(port->priv, port->id, false); ++ } ++ ++ /* The interface is running, so we have to force a ++ * reallocation of the queues ++ */ ++ mv_pp2x_stop_dev(port); ++ mv_pp2x_cleanup_rxqs(port); ++ mv_pp2x_cleanup_txqs(port); ++ ++ port->rx_ring_size = ring->rx_pending; ++ port->tx_ring_size = ring->tx_pending; ++ ++ err = mv_pp2x_setup_rxqs(port); ++ if (err) { ++ /* Reallocate Rx queues with the original ring size */ ++ port->rx_ring_size = prev_rx_ring_size; ++ ring->rx_pending = prev_rx_ring_size; ++ err = mv_pp2x_setup_rxqs(port); ++ if (err) ++ goto err_out; ++ } ++ err = mv_pp2x_setup_txqs(port); ++ if (err) { ++ /* Reallocate Tx queues with the original ring size */ ++ port->tx_ring_size = prev_tx_ring_size; ++ ring->tx_pending = prev_tx_ring_size; ++ err = mv_pp2x_setup_txqs(port); ++ if (err) ++ goto err_clean_rxqs; ++ } ++ ++ mv_pp2x_start_dev(port); ++ ++ return 0; ++ ++err_clean_rxqs: ++ mv_pp2x_cleanup_rxqs(port); ++err_out: ++ netdev_err(dev, "fail to change ring parameters"); ++ return err; ++} ++ ++static u32 mv_pp2x_ethtool_get_rxfh_indir_size(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return -EOPNOTSUPP; ++ ++ return ARRAY_SIZE(port->priv->rx_indir_table); ++} ++ ++static int mv_pp2x_get_rss_hash_opts(struct mv_pp2x_port *port, ++ struct ethtool_rxnfc *nfc) ++{ ++ switch (nfc->flow_type) { ++ case TCP_V4_FLOW: ++ case TCP_V6_FLOW: ++ nfc->data |= RXH_IP_SRC | RXH_IP_DST; ++ nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; ++ break; ++ case UDP_V4_FLOW: ++ case UDP_V6_FLOW: ++ nfc->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (port->rss_cfg.rss_mode == MVPP2_RSS_5T) ++ nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; ++ break; ++ case IPV4_FLOW: ++ case IPV6_FLOW: ++ nfc->data |= RXH_IP_SRC | RXH_IP_DST; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++static int mv_pp2x_ethtool_get_rxnfc(struct net_device *dev, ++ struct ethtool_rxnfc *cmd, ++ u32 *rules) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int ret = -EOPNOTSUPP; ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -EOPNOTSUPP; ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return -EOPNOTSUPP; ++ ++ if (!port) ++ return -EIO; ++ ++ switch (cmd->cmd) { ++ case ETHTOOL_GRXRINGS: ++ cmd->data = ARRAY_SIZE(port->priv->rx_indir_table); ++ ret = 0; ++ break; ++ case ETHTOOL_GRXFH: ++ ret = mv_pp2x_get_rss_hash_opts(port, cmd); ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mv_pp2x_set_rss_hash_opt(struct mv_pp2x_port *port, ++ struct ethtool_rxnfc *nfc) ++{ ++ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | ++ RXH_L4_B_0_1 | RXH_L4_B_2_3)) ++ return -EINVAL; ++ ++ switch (nfc->flow_type) { ++ case TCP_V4_FLOW: ++ case TCP_V6_FLOW: ++ if (!(nfc->data & RXH_IP_SRC) || ++ !(nfc->data & RXH_IP_DST) || ++ !(nfc->data & RXH_L4_B_0_1) || ++ !(nfc->data & RXH_L4_B_2_3)) ++ return -EINVAL; ++ break; ++ case UDP_V4_FLOW: ++ case UDP_V6_FLOW: ++ if (!(nfc->data & RXH_IP_SRC) || ++ !(nfc->data & RXH_IP_DST)) ++ return -EINVAL; ++ switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { ++ case 0: ++ mv_pp22_rss_mode_set(port, MVPP2_RSS_2T); ++ break; ++ case (RXH_L4_B_0_1 | RXH_L4_B_2_3): ++ mv_pp22_rss_mode_set(port, MVPP2_RSS_5T); ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int mv_pp2x_ethtool_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int ret = -EOPNOTSUPP; ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ /* Single mode doesn't support RSS features */ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -EOPNOTSUPP; ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return -EOPNOTSUPP; ++ ++ switch (cmd->cmd) { ++ case ETHTOOL_SRXFH: ++ ret = mv_pp2x_set_rss_hash_opt(port, cmd); ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mv_pp2x_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, ++ u8 *hfunc) ++{ ++ size_t copy_size; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ /* Single mode doesn't support RSS features */ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -EOPNOTSUPP; ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return -EOPNOTSUPP; ++ ++ if (hfunc) ++ *hfunc = ETH_RSS_HASH_TOP; ++ ++ if (!indir) ++ return 0; ++ ++ copy_size = ARRAY_SIZE(port->priv->rx_indir_table); ++ memcpy(indir, port->priv->rx_indir_table, copy_size * sizeof(u32)); ++ ++ return 0; ++} ++ ++static int mv_pp2x_ethtool_set_rxfh(struct net_device *dev, const u32 *indir, ++ const u8 *key, const u8 hfunc) ++{ ++ int i, err = 0; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ u32 rx_indir_table_orig[MVPP22_RSS_TBL_LINE_NUM]; ++ unsigned long flags; ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ /* Single mode doesn't support RSS features */ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -EOPNOTSUPP; ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return -EOPNOTSUPP; ++ ++ /* We require at least one supported parameter to be changed ++ * and no change in any of the unsupported parameters ++ */ ++ if (key || ++ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) ++ return -EOPNOTSUPP; ++ ++ if (!indir) ++ return 0; ++ ++ for (i = 0; i < ARRAY_SIZE(port->priv->rx_indir_table); i++) { ++ rx_indir_table_orig[i] = port->priv->rx_indir_table[i]; ++ port->priv->rx_indir_table[i] = indir[i]; ++ } ++ ++ spin_lock_irqsave(&port->priv->hw.cls_spinlock, flags); ++ ++ err = mv_pp22_rss_rxfh_indir_set(port); ++ if (err) { ++ netdev_err(dev, "fail to change rxfh indir table"); ++ /* Rollback rx_indir_table */ ++ for (i = 0; i < ARRAY_SIZE(port->priv->rx_indir_table); i++) ++ port->priv->rx_indir_table[i] = rx_indir_table_orig[i]; ++ mv_pp22_rss_rxfh_indir_set(port); ++ } ++ spin_unlock_irqrestore(&port->priv->hw.cls_spinlock, flags); ++ ++ return err; ++} ++ ++static int mv_pp2x_ethtool_get_regs_len(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_mac_data *mac = &port->mac_data; ++ ++ if (port->priv->pp2_version == PPV21) ++ return -EOPNOTSUPP; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return MV_PP2_REGS_GMAC_LEN * sizeof(u32); ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ return MV_PP2_REGS_XLG_LEN * sizeof(u32); ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return -1; ++ } ++} ++ ++/*ethtool get registers function */ ++static void mv_pp2x_ethtool_get_regs(struct net_device *dev, ++ struct ethtool_regs *regs, void *p) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_mac_data *mac = &port->mac_data; ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ ++ if (!port) { ++ netdev_err(dev, "%s is not supported on %s\n", ++ __func__, dev->name); ++ return; ++ } ++ ++ regs->version = port->priv->pp2_version; ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ memset(p, 0, MV_PP2_REGS_GMAC_LEN * sizeof(u32)); ++ mv_gop110_gmac_registers_dump(port, p); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ memset(p, 0, MV_PP2_REGS_XLG_LEN * sizeof(u32)); ++ mv_gop110_xlg_registers_dump(port, p); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, mac->phy_mode); ++ return; ++ } ++} ++ ++static u64 mv_pp2x_eth_tool_link_test(struct mv_pp2x_port *port) ++{ ++ struct mv_port_link_status status; ++ ++ pr_info("Link testing starting\n"); ++ ++ mv_gop110_port_link_status(&port->priv->hw.gop, ++ &port->mac_data, &status); ++ ++ if (status.linkup) ++ return 0; ++ return 1; ++} ++ ++static bool mv_pp2x_reg_pattern_test(void *reg, u32 offset, u32 mask, u32 write) ++{ ++ static const u32 test[] = {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF}; ++ u32 read, old; ++ int i; ++ ++ if (!mask) ++ return false; ++ old = mv_gop_gen_read(reg, offset); ++ ++ for (i = 0; i < ARRAY_SIZE(test); i++) { ++ mv_gop_gen_write(reg, offset, write & test[i]); ++ read = mv_gop_gen_read(reg, offset); ++ if (read != (write & test[i] & mask)) { ++ pr_err("pattern test reg %p(test 0x%08X write 0x%08X mask 0x%08X) failed: ", ++ reg, test[i], write, mask); ++ pr_err("got 0x%08X expected 0x%08X\n", read, (write & test[i] & mask)); ++ mv_gop_gen_write(reg, offset, old); ++ return true; ++ } ++ } ++ ++ mv_gop_gen_write(reg, offset, old); ++ ++ return false; ++} ++ ++static u64 mv_pp2x_eth_tool_reg_test(struct mv_pp2x_port *port) ++{ ++ int ind; ++ int err = 0; ++ struct mv_mac_data *mac = &port->mac_data; ++ int gop_port = mac->gop_index; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ void *reg = gop->gop_110.gmac.base + gop_port * gop->gop_110.gmac.obj_size; ++ ++ pr_info("Register testing starting\n"); ++ ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_CTRL0_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_CTRL1_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_CTRL2_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_AUTO_NEG_CFG_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_STATUS0_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_SERIAL_PARAM_CFG_REG, MV_PP2_TEST_MASK1, ++ MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_FIFO_CFG_0_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_FIFO_CFG_1_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_SERDES_CFG0_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_SERDES_CFG1_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_SERDES_CFG2_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_SERDES_CFG3_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_PRBS_STATUS_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_PRBS_ERR_CNTR_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_STATUS1_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_MIB_CNTRS_CTRL_REG, MV_PP2_TEST_MASK2, MV_PP2_TEST_PATTERN2); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_CTRL3_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_QSGMII_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_QSGMII_STATUS_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_QSGMII_PRBS_CNTR_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ for (ind = 0; ind < 8; ind++) ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_CCFC_PORT_SPEED_TIMER_REG(ind), MV_PP2_TEST_MASK1, ++ MV_PP2_TEST_PATTERN1); ++ ++ for (ind = 0; ind < 4; ind++) ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_FC_DSA_TAG_REG(ind), MV_PP2_TEST_MASK1, ++ MV_PP2_TEST_PATTERN1); ++ ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_0, MV_PP2_TEST_MASK1, ++ MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LINK_LEVEL_FLOW_CTRL_WINDOW_REG_1, MV_PP2_TEST_MASK1, ++ MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_CTRL4_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PORT_SERIAL_PARAM_1_CFG_REG, MV_PP2_TEST_MASK1, ++ MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LPI_CTRL_0_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LPI_CTRL_1_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LPI_CTRL_2_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LPI_STATUS_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_LPI_CNTR_REG, MV_PP2_TEST_MASK3, MV_PP2_TEST_PATTERN3); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PULSE_1_MS_LOW_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_PULSE_1_MS_HIGH_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_INTERRUPT_MASK_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ err += mv_pp2x_reg_pattern_test(reg, MV_GMAC_INTERRUPT_SUM_MASK_REG, MV_PP2_TEST_MASK1, MV_PP2_TEST_PATTERN1); ++ ++ if (err) ++ return 1; ++ return 0; ++} ++ ++static void mv_pp2x_eth_tool_diag_test(struct net_device *netdev, ++ struct ethtool_test *test, u64 *data) ++{ ++ struct mv_pp2x_port *port = netdev_priv(netdev); ++ int i; ++ struct mv_mac_data *mac = &port->mac_data; ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ ++ if (!(mac->flags & MV_EMAC_F_INIT)) { ++ pr_err("%s: interface %s is not initialized\n", __func__, netdev->name); ++ for (i = 0; i < MV_PP2_TEST_LEN; i++) ++ data[i] = -ENONET; ++ test->flags |= ETH_TEST_FL_FAILED; ++ return; ++ } ++ ++ memset(data, 0, MV_PP2_TEST_LEN * sizeof(u64)); ++ ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ pr_err("10G Phy mode (%d) do not support test\n", mac->phy_mode); ++ return; ++ default: ++ pr_err("%s: Wrong port mode (%d\n)", __func__, mac->phy_mode); ++ return; ++ } ++ ++ data[0] = mv_pp2x_eth_tool_link_test(port); ++ data[1] = mv_pp2x_eth_tool_reg_test(port); ++ for (i = 0; i < MV_PP2_TEST_LEN; i++) ++ test->flags |= data[i] ? ETH_TEST_FL_FAILED : 0; ++ ++ msleep_interruptible(4 * 1000); ++} ++ ++static void mv_pp2x_get_channels(struct net_device *netdev, ++ struct ethtool_channels *ch) ++{ ++ struct mv_pp2x_port *port = netdev_priv(netdev); ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ /* Only multi queue mode support rx_count(# of Hot CPU's) and ++ * other_count(# of adreess spaces used by cold CPU's) ++ */ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return; ++ ++ ch->rx_count = port->priv->rx_count; ++ ch->other_count = port->priv->other_count; ++} ++ ++static const struct ethtool_ops mv_pp2x_eth_tool_ops = { ++ .get_link = ethtool_op_get_link, ++ .get_settings = mv_pp2x_ethtool_get_settings, ++ .set_settings = mv_pp2x_ethtool_set_settings, ++ .set_coalesce = mv_pp2x_ethtool_set_coalesce, ++ .get_coalesce = mv_pp2x_ethtool_get_coalesce, ++ .nway_reset = mv_pp2x_eth_tool_nway_reset, ++ .get_drvinfo = mv_pp2x_ethtool_get_drvinfo, ++ .get_ethtool_stats = mv_pp2x_eth_tool_get_ethtool_stats, ++ .get_sset_count = mv_pp2x_eth_tool_get_sset_count, ++ .get_strings = mv_pp2x_eth_tool_get_strings, ++ .get_ringparam = mv_pp2x_ethtool_get_ringparam, ++ .set_ringparam = mv_pp2x_ethtool_set_ringparam, ++ .get_pauseparam = mv_pp2x_get_pauseparam, ++ .set_pauseparam = mv_pp2x_set_pauseparam, ++ .get_rxfh_indir_size = mv_pp2x_ethtool_get_rxfh_indir_size, ++ .get_rxnfc = mv_pp2x_ethtool_get_rxnfc, ++ .set_rxnfc = mv_pp2x_ethtool_set_rxnfc, ++ .get_rxfh = mv_pp2x_ethtool_get_rxfh, ++ .set_rxfh = mv_pp2x_ethtool_set_rxfh, ++ .get_regs_len = mv_pp2x_ethtool_get_regs_len, ++ .get_regs = mv_pp2x_ethtool_get_regs, ++ .self_test = mv_pp2x_eth_tool_diag_test, ++ .get_channels = mv_pp2x_get_channels, ++}; ++ ++void mv_pp2x_set_ethtool_ops(struct net_device *netdev) ++{ ++ netdev->ethtool_ops = &mv_pp2x_eth_tool_ops; ++} ++ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.c b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.c +new file mode 100644 +index 00000000..47c838b +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.c +@@ -0,0 +1,6772 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mv_pp2x.h" ++#include "mv_pp2x_hw.h" ++ ++/* Utility/helper methods */ ++ ++/*#define MVPP2_REG_BUF_SIZE (sizeof(last_used)/sizeof(last_used[0]))*/ ++#define MVPP2_REG_BUF_SIZE ARRAY_SIZE(last_used) ++ ++int mv_pp2x_ptr_validate(const void *ptr) ++{ ++ if (!ptr) { ++ pr_err("%s: null pointer.\n", __func__); ++ return MV_ERROR; ++ } ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_ptr_validate); ++ ++int mv_pp2x_range_validate(int value, int min, int max) ++{ ++ if (((value) > (max)) || ((value) < (min))) { ++ pr_err("%s: value 0x%X (%d) is out of range [0x%X , 0x%X].\n", ++ __func__, (value), (value), (min), (max)); ++ return MV_ERROR; ++ } ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_range_validate); ++ ++/* Parser configuration routines */ ++ ++/* Flow ID definetion array */ ++static struct mv_pp2x_prs_flow_id ++ mv_pp2x_prs_flow_id_array[MVPP2_PRS_FL_TCAM_NUM] = { ++ /***********#Flow ID#**************#Result Info#************/ ++ {MVPP2_PRS_FL_IP4_TCP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_UDP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_TCP_NF_TAG, {MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_NF_TAG, {MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_NF_TAG, {MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_UDP_NF_TAG, {MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_NF_TAG, {MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_NF_TAG, {MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_TCP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_TCP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_UDP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_UDP_NF_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_TCP_NF_TAG, {MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_TCP_NF_TAG, {MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_UDP_NF_TAG, {MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_UDP_NF_TAG, {MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_FALSE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_TCP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_UDP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_TCP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TCP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_UDP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP4 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP4_OPT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UDP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP4_OTHER | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_TCP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_TCP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_UDP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_UDP_FRAG_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_TCP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_TCP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_UDP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP6 | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_UDP_FRAG_TAG, {MVPP2_PRS_RI_L3_IP6_EXT | ++ MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_PROTO_MASK | ++ MVPP2_PRS_RI_IP_FRAG_MASK | ++ MVPP2_PRS_RI_L4_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OPT, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP4_OTHER, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP4_TAG, {MVPP2_PRS_RI_L3_IP4, ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TAG, {MVPP2_PRS_RI_L3_IP4_OPT, ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP4_TAG, {MVPP2_PRS_RI_L3_IP4_OTHER, ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_UNTAG, {MVPP2_PRS_RI_VLAN_NONE | ++ MVPP2_PRS_RI_L3_IP6_EXT, ++ MVPP2_PRS_RI_VLAN_MASK | ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_IP6_TAG, {MVPP2_PRS_RI_L3_IP6, ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ {MVPP2_PRS_FL_IP6_TAG, {MVPP2_PRS_RI_L3_IP6_EXT, ++ MVPP2_PRS_RI_L3_PROTO_MASK} }, ++ ++ {MVPP2_PRS_FL_NON_IP_UNTAG, {MVPP2_PRS_RI_VLAN_NONE, ++ MVPP2_PRS_RI_VLAN_MASK} }, ++ ++ {MVPP2_PRS_FL_NON_IP_TAG, {0, 0} }, ++}; ++ ++/* Array of bitmask to indicate flow id attribute */ ++static int mv_pp2x_prs_flow_id_attr_tbl[MVPP2_PRS_FL_LAST]; ++ ++/* Update parser tcam and sram hw entries */ ++int mv_pp2x_prs_hw_write(struct mv_pp2x_hw *hw, struct mv_pp2x_prs_entry *pe) ++{ ++ int i; ++ ++ if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1) ++ return -EINVAL; ++ ++ /* Clear entry invalidation bit */ ++ pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] &= ~MVPP2_PRS_TCAM_INV_MASK; ++ ++ /* Write sram index - indirect access */ ++ mv_pp2x_write(hw, MVPP2_PRS_SRAM_IDX_REG, pe->index); ++ for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) ++ mv_pp2x_write(hw, MVPP2_PRS_SRAM_DATA_REG(i), pe->sram.word[i]); ++ ++ /* Write tcam index - indirect access */ ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_IDX_REG, pe->index); ++ for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_DATA_REG(i), pe->tcam.word[i]); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_hw_write); ++ ++/* Read tcam entry from hw */ ++int mv_pp2x_prs_hw_read(struct mv_pp2x_hw *hw, struct mv_pp2x_prs_entry *pe) ++{ ++ int i; ++ ++ if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1) ++ return -EINVAL; ++ ++ /* Write tcam index - indirect access */ ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_IDX_REG, pe->index); ++ ++ pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] = mv_pp2x_read(hw, ++ MVPP2_PRS_TCAM_DATA_REG(MVPP2_PRS_TCAM_INV_WORD)); ++ if (pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] & MVPP2_PRS_TCAM_INV_MASK) ++ return MVPP2_PRS_TCAM_ENTRY_INVALID; ++ ++ for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) ++ pe->tcam.word[i] = mv_pp2x_read(hw, MVPP2_PRS_TCAM_DATA_REG(i)); ++ ++ /* Write sram index - indirect access */ ++ mv_pp2x_write(hw, MVPP2_PRS_SRAM_IDX_REG, pe->index); ++ for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) ++ pe->sram.word[i] = mv_pp2x_read(hw, MVPP2_PRS_SRAM_DATA_REG(i)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_hw_read); ++ ++void mv_pp2x_prs_sw_clear(struct mv_pp2x_prs_entry *pe) ++{ ++ memset(pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_clear); ++ ++/* Invalidate tcam hw entry */ ++void mv_pp2x_prs_hw_inv(struct mv_pp2x_hw *hw, int index) ++{ ++ /* Write index - indirect access */ ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_IDX_REG, index); ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_DATA_REG(MVPP2_PRS_TCAM_INV_WORD), ++ MVPP2_PRS_TCAM_INV_MASK); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_hw_inv); ++ ++/* Enable shadow table entry and set its lookup ID */ ++static void mv_pp2x_prs_shadow_set(struct mv_pp2x_hw *hw, int index, int lu) ++{ ++ hw->prs_shadow[index].valid = true; ++ hw->prs_shadow[index].lu = lu; ++} ++ ++/* Update ri fields in shadow table entry */ ++static void mv_pp2x_prs_shadow_ri_set(struct mv_pp2x_hw *hw, int index, ++ unsigned int ri, ++ unsigned int ri_mask) ++{ ++ hw->prs_shadow[index].ri_mask = ri_mask; ++ hw->prs_shadow[index].ri = ri; ++} ++ ++/* Update lookup field in tcam sw entry */ ++void mv_pp2x_prs_tcam_lu_set(struct mv_pp2x_prs_entry *pe, unsigned int lu) ++{ ++ unsigned int offset = MVPP2_PRS_TCAM_LU_BYTE; ++ unsigned int enable_off = ++ MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_LU_BYTE); ++ ++ pe->tcam.byte[HW_BYTE_OFFS(offset)] = lu; ++ pe->tcam.byte[HW_BYTE_OFFS(enable_off)] = MVPP2_PRS_LU_MASK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_tcam_lu_set); ++ ++/* Update mask for single port in tcam sw entry */ ++void mv_pp2x_prs_tcam_port_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int port, bool add) ++{ ++ int enable_off = ++ HW_BYTE_OFFS(MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE)); ++ ++ if (add) ++ pe->tcam.byte[enable_off] &= ~(1 << port); ++ else ++ pe->tcam.byte[enable_off] |= 1 << port; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_tcam_port_set); ++ ++/* Update port map in tcam sw entry */ ++void mv_pp2x_prs_tcam_port_map_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int ports) ++{ ++ unsigned char port_mask = MVPP2_PRS_PORT_MASK; ++ int enable_off = ++ HW_BYTE_OFFS(MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE)); ++ ++ pe->tcam.byte[HW_BYTE_OFFS(MVPP2_PRS_TCAM_PORT_BYTE)] = 0; ++ pe->tcam.byte[enable_off] &= ~port_mask; ++ pe->tcam.byte[enable_off] |= ~ports & MVPP2_PRS_PORT_MASK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_tcam_port_map_set); ++ ++/* Obtain port map from tcam sw entry */ ++static unsigned int mv_pp2x_prs_tcam_port_map_get(struct mv_pp2x_prs_entry *pe) ++{ ++ int enable_off = ++ HW_BYTE_OFFS(MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE)); ++ ++ return ~(pe->tcam.byte[enable_off]) & MVPP2_PRS_PORT_MASK; ++} ++ ++/* Set byte of data and its enable bits in tcam sw entry */ ++void mv_pp2x_prs_tcam_data_byte_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int offs, ++ unsigned char byte, ++ unsigned char enable) ++{ ++ pe->tcam.byte[TCAM_DATA_BYTE(offs)] = byte; ++ pe->tcam.byte[TCAM_DATA_MASK(offs)] = enable; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_tcam_data_byte_set); ++ ++/* Get byte of data and its enable bits from tcam sw entry */ ++static void mv_pp2x_prs_tcam_data_byte_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int offs, ++ unsigned char *byte, ++ unsigned char *enable) ++{ ++ *byte = pe->tcam.byte[TCAM_DATA_BYTE(offs)]; ++ *enable = pe->tcam.byte[TCAM_DATA_MASK(offs)]; ++} ++ ++/* Set dword of data and its enable bits in tcam sw entry */ ++static void mv_pp2x_prs_tcam_data_dword_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int offs, ++ unsigned int word, ++ unsigned int enable) ++{ ++ int index, offset; ++ unsigned char byte, byte_mask; ++ ++ for (index = 0; index < 4; index++) { ++ offset = (offs * 4) + index; ++ byte = ((unsigned char *)&word)[HW_BYTE_OFFS(index)]; ++ byte_mask = ((unsigned char *)&enable)[HW_BYTE_OFFS(index)]; ++ mv_pp2x_prs_tcam_data_byte_set(pe, offset, byte, byte_mask); ++ } ++} ++ ++/* Get dword of data and its enable bits from tcam sw entry */ ++static void mv_pp2x_prs_tcam_data_dword_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int offs, ++ unsigned int *word, ++ unsigned int *enable) ++{ ++ int index, offset; ++ unsigned char byte, mask; ++ ++ for (index = 0; index < 4; index++) { ++ offset = (offs * 4) + index; ++ mv_pp2x_prs_tcam_data_byte_get(pe, offset, &byte, &mask); ++ ((unsigned char *)word)[HW_BYTE_OFFS(index)] = byte; ++ ((unsigned char *)enable)[HW_BYTE_OFFS(index)] = mask; ++ } ++} ++ ++/* Compare tcam data bytes with a pattern */ ++static bool mv_pp2x_prs_tcam_data_cmp(struct mv_pp2x_prs_entry *pe, int offs, ++ u16 data) ++{ ++ u16 tcam_data; ++ ++ tcam_data = (pe->tcam.byte[TCAM_DATA_BYTE(offs + 1)] << 8) | pe->tcam.byte[TCAM_DATA_BYTE(offs)]; ++ if (tcam_data != data) ++ return false; ++ return true; ++} ++ ++/* Update ai bits in tcam sw entry */ ++void mv_pp2x_prs_tcam_ai_update(struct mv_pp2x_prs_entry *pe, ++ unsigned int bits, ++ unsigned int enable) ++{ ++ int i, ai_idx = MVPP2_PRS_TCAM_AI_BYTE; ++ ++ for (i = 0; i < MVPP2_PRS_AI_BITS; i++) { ++ if (!(enable & BIT(i))) ++ continue; ++ ++ if (bits & BIT(i)) ++ pe->tcam.byte[HW_BYTE_OFFS(ai_idx)] |= 1 << i; ++ else ++ pe->tcam.byte[HW_BYTE_OFFS(ai_idx)] &= ~(1 << i); ++ } ++ ++ pe->tcam.byte[HW_BYTE_OFFS(MVPP2_PRS_TCAM_EN_OFFS(ai_idx))] |= enable; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_tcam_ai_update); ++ ++/* Get ai bits from tcam sw entry */ ++static int mv_pp2x_prs_tcam_ai_get(struct mv_pp2x_prs_entry *pe) ++{ ++ return pe->tcam.byte[HW_BYTE_OFFS(MVPP2_PRS_TCAM_AI_BYTE)]; ++} ++ ++/* Set ethertype in tcam sw entry */ ++static void mv_pp2x_prs_match_etype(struct mv_pp2x_prs_entry *pe, int offset, ++ unsigned short ethertype) ++{ ++ mv_pp2x_prs_tcam_data_byte_set(pe, offset + 0, ethertype >> 8, 0xff); ++ mv_pp2x_prs_tcam_data_byte_set(pe, offset + 1, ethertype & 0xff, 0xff); ++} ++ ++/* Set vid in tcam sw entry */ ++static void mv_pp2x_prs_match_vid(struct mv_pp2x_prs_entry *pe, int offset, unsigned short vid) ++{ ++ mv_pp2x_prs_tcam_data_byte_set(pe, offset + 0, ((vid & MVPP2_PRS_VID_H_WORD) >> ++ MVPP2_PRS_VID_H_WORD_SHIFT), MVPP2_PRS_VID_H_WORD_MASK); ++ mv_pp2x_prs_tcam_data_byte_set(pe, offset + 1, vid & MVPP2_PRS_VID_L_WORD_MASK, MVPP2_PRS_VID_L_WORD_MASK); ++} ++ ++/* Set bits in sram sw entry */ ++static void mv_pp2x_prs_sram_bits_set(struct mv_pp2x_prs_entry *pe, int bit_num, ++ int val) ++{ ++ pe->sram.byte[SRAM_BIT_TO_BYTE(bit_num)] |= (val << (bit_num % 8)); ++} ++ ++/* Clear bits in sram sw entry */ ++static void mv_pp2x_prs_sram_bits_clear(struct mv_pp2x_prs_entry *pe, ++ int bit_num, int val) ++{ ++ pe->sram.byte[SRAM_BIT_TO_BYTE(bit_num)] &= ~(val << (bit_num % 8)); ++} ++ ++/* Update ri bits in sram sw entry */ ++void mv_pp2x_prs_sram_ri_update(struct mv_pp2x_prs_entry *pe, ++ unsigned int bits, unsigned int mask) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < MVPP2_PRS_SRAM_RI_CTRL_BITS; i++) { ++ int ri_off = MVPP2_PRS_SRAM_RI_OFFS; ++ ++ if (!(mask & BIT(i))) ++ continue; ++ ++ if (bits & BIT(i)) ++ mv_pp2x_prs_sram_bits_set(pe, ri_off + i, 1); ++ else ++ mv_pp2x_prs_sram_bits_clear(pe, ri_off + i, 1); ++ ++ mv_pp2x_prs_sram_bits_set(pe, ++ MVPP2_PRS_SRAM_RI_CTRL_OFFS + i, 1); ++ } ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sram_ri_update); ++ ++/* Obtain ri bits from sram sw entry */ ++static int mv_pp2x_prs_sram_ri_get(struct mv_pp2x_prs_entry *pe) ++{ ++ return pe->sram.word[MVPP2_PRS_SRAM_RI_WORD]; ++} ++ ++/* Update ai bits in sram sw entry */ ++void mv_pp2x_prs_sram_ai_update(struct mv_pp2x_prs_entry *pe, ++ unsigned int bits, unsigned int mask) ++{ ++ unsigned int i; ++ int ai_off = MVPP2_PRS_SRAM_AI_OFFS; ++ ++ for (i = 0; i < MVPP2_PRS_SRAM_AI_CTRL_BITS; i++) { ++ if (!(mask & BIT(i))) ++ continue; ++ ++ if (bits & BIT(i)) ++ mv_pp2x_prs_sram_bits_set(pe, ai_off + i, 1); ++ else ++ mv_pp2x_prs_sram_bits_clear(pe, ai_off + i, 1); ++ ++ mv_pp2x_prs_sram_bits_set(pe, ++ MVPP2_PRS_SRAM_AI_CTRL_OFFS + i, 1); ++ } ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sram_ai_update); ++ ++/* Read ai bits from sram sw entry */ ++static int mv_pp2x_prs_sram_ai_get(struct mv_pp2x_prs_entry *pe) ++{ ++ u8 bits; ++ int ai_off = SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_AI_OFFS); ++ int ai_en_off = ai_off + 1; ++ int ai_shift = MVPP2_PRS_SRAM_AI_OFFS % 8; ++ ++ bits = (pe->sram.byte[ai_off] >> ai_shift) | ++ (pe->sram.byte[ai_en_off] << (8 - ai_shift)); ++ ++ return bits; ++} ++ ++/* In sram sw entry set lookup ID field of the tcam key to be used in the next ++ * lookup interation ++ */ ++void mv_pp2x_prs_sram_next_lu_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int lu) ++{ ++ int sram_next_off = MVPP2_PRS_SRAM_NEXT_LU_OFFS; ++ ++ mv_pp2x_prs_sram_bits_clear(pe, sram_next_off, ++ MVPP2_PRS_SRAM_NEXT_LU_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, sram_next_off, lu); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sram_next_lu_set); ++ ++/* In the sram sw entry set sign and value of the next lookup offset ++ * and the offset value generated to the classifier ++ */ ++static void mv_pp2x_prs_sram_shift_set(struct mv_pp2x_prs_entry *pe, int shift, ++ unsigned int op) ++{ ++ /* Set sign */ ++ if (shift < 0) { ++ mv_pp2x_prs_sram_bits_set(pe, ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT, 1); ++ shift = 0 - shift; ++ } else { ++ mv_pp2x_prs_sram_bits_clear(pe, ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT, 1); ++ } ++ ++ /* Set value */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_SHIFT_OFFS)] = ++ (unsigned char)shift; ++ ++ /* Reset and set operation */ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS, op); ++ ++ /* Set base offset as current */ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS, 1); ++} ++ ++/* In the sram sw entry set sign and value of the user defined offset ++ * generated to the classifier ++ */ ++static void mv_pp2x_prs_sram_offset_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int type, int offset, ++ unsigned int op) ++{ ++ /* Set sign */ ++ if (offset < 0) { ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_SIGN_BIT, 1); ++ offset = 0 - offset; ++ } else { ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_SIGN_BIT, 1); ++ } ++ ++ /* Set value */ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_OFFS, ++ MVPP2_PRS_SRAM_UDF_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_OFFS, offset); ++ pe->sram.byte[SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_UDF_OFFS + ++ MVPP2_PRS_SRAM_UDF_BITS)] &= ++ ~(MVPP2_PRS_SRAM_UDF_MASK >> ++ (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8))); ++ pe->sram.byte[SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_UDF_OFFS + ++ MVPP2_PRS_SRAM_UDF_BITS)] |= ++ (offset >> (8 - ++ (MVPP2_PRS_SRAM_UDF_OFFS % 8))); ++ ++ /* Set offset type */ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_TYPE_OFFS, ++ MVPP2_PRS_SRAM_UDF_TYPE_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_TYPE_OFFS, type); ++ ++ /* Set offset operation */ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS, op); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + ++ MVPP2_PRS_SRAM_OP_SEL_UDF_BITS)] &= ++ ~(MVPP2_PRS_SRAM_OP_SEL_UDF_MASK >> ++ (8 - ++ (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8))); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + ++ MVPP2_PRS_SRAM_OP_SEL_UDF_BITS)] |= ++ (op >> (8 - ++ (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8))); ++ ++ /* Set base offset as current */ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS, 1); ++} ++ ++/* Find parser flow entry */ ++static struct mv_pp2x_prs_entry *mv_pp2x_prs_flow_find(struct mv_pp2x_hw *hw, ++ int flow, ++ unsigned int ri, ++ unsigned int ri_mask, ++ struct mv_pp2x_prs_entry *pe) ++{ ++ int tid; ++ unsigned int dword, enable; ++ ++ memset(pe, 0, sizeof(*pe)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); ++ ++ /* Go through the all entires with MVPP2_PRS_LU_FLOWS */ ++ for (tid = MVPP2_PRS_TCAM_SRAM_SIZE - 1; tid >= 0; tid--) { ++ u8 bits; ++ ++ if (!hw->prs_shadow[tid].valid || ++ hw->prs_shadow[tid].lu != MVPP2_PRS_LU_FLOWS) ++ continue; ++ ++ pe->index = tid; ++ mv_pp2x_prs_hw_read(hw, pe); ++ ++ /* Check result info, because there maybe several ++ * TCAM lines to generate the same flow ++ */ ++ mv_pp2x_prs_tcam_data_dword_get(pe, 0, &dword, &enable); ++ if ((dword != ri) || (enable != ri_mask)) ++ continue; ++ ++ bits = mv_pp2x_prs_sram_ai_get(pe); ++ ++ /* Sram store classification lookup ID in AI bits [5:0] */ ++ if ((bits & MVPP2_PRS_FLOW_ID_MASK) == flow) ++ return pe; ++ } ++ return NULL; ++} ++ ++/* Return first free tcam index, seeking from start to end */ ++static int mv_pp2x_prs_tcam_first_free(struct mv_pp2x_hw *hw, ++ unsigned char start, ++ unsigned char end) ++{ ++ int tid; ++ ++ if (start > end) ++ swap(start, end); ++ ++ if (end >= MVPP2_PRS_TCAM_SRAM_SIZE) ++ end = MVPP2_PRS_TCAM_SRAM_SIZE - 1; ++ ++ for (tid = start; tid <= end; tid++) { ++ if (!hw->prs_shadow[tid].valid) ++ return tid; ++ } ++ pr_err("Out of TCAM Entries !!: %s(%d)\n", __FILENAME__, __LINE__); ++ return -EINVAL; ++} ++ ++/* Enable/disable dropping all mac da's */ ++static void mv_pp2x_prs_mac_drop_all_set(struct mv_pp2x_hw *hw, ++ int port, bool add) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ if (hw->prs_shadow[MVPP2_PE_DROP_ALL].valid) { ++ /* Entry exist - update port only */ ++ pe.index = MVPP2_PE_DROP_ALL; ++ mv_pp2x_prs_hw_read(hw, &pe); ++ } else { ++ /* Entry doesn't exist - create new */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ pe.index = MVPP2_PE_DROP_ALL; ++ ++ /* Non-promiscuous mode for all ports - DROP unknown packets */ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, ++ MVPP2_PRS_RI_DROP_MASK); ++ ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MAC); ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, 0); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(&pe, port, add); ++ ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Set port to unicast promiscuous mode */ ++void mv_pp2x_prs_mac_uc_promisc_set(struct mv_pp2x_hw *hw, int port, bool add) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ /* Promiscuous mode - Accept unknown packets */ ++ ++ if (hw->prs_shadow[MVPP2_PE_MAC_UC_PROMISCUOUS].valid) { ++ /* Entry exist - update port only */ ++ pe.index = MVPP2_PE_MAC_UC_PROMISCUOUS; ++ mv_pp2x_prs_hw_read(hw, &pe); ++ } else { ++ /* Entry doesn't exist - create new */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ pe.index = MVPP2_PE_MAC_UC_PROMISCUOUS; ++ ++ /* Continue - set next lookup */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); ++ ++ /* Set result info bits */ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_UCAST, ++ MVPP2_PRS_RI_L2_CAST_MASK); ++ ++ /* Update tcam entry data first byte */ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_UCAST_VAL, ++ MVPP2_PRS_CAST_MASK); ++ ++ /* Shift to ethertype */ ++ mv_pp2x_prs_sram_shift_set(&pe, 2 * ETH_ALEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, 0); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MAC); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(&pe, port, add); ++ ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Set port to multicast promiscuous mode */ ++void mv_pp2x_prs_mac_mc_promisc_set(struct mv_pp2x_hw *hw, int port, bool add) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ if (hw->prs_shadow[MVPP2_PE_MAC_MC_PROMISCUOUS].valid) { ++ /* Entry exist - update port only */ ++ pe.index = MVPP2_PE_MAC_MC_PROMISCUOUS; ++ mv_pp2x_prs_hw_read(hw, &pe); ++ } else { ++ /* Entry doesn't exist - create new */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ pe.index = MVPP2_PE_MAC_MC_PROMISCUOUS; ++ ++ /* Continue - set next lookup */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); ++ ++ /* Set result info bits */ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_MCAST, ++ MVPP2_PRS_RI_L2_CAST_MASK); ++ ++ /* Update tcam entry data first byte */ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_MCAST_VAL, ++ MVPP2_PRS_CAST_MASK); ++ ++ /* Shift to ethertype */ ++ mv_pp2x_prs_sram_shift_set(&pe, 2 * ETH_ALEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, 0); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MAC); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(&pe, port, add); ++ ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Set entry for dsa packets */ ++static void mv_pp2x_prs_dsa_tag_set(struct mv_pp2x_hw *hw, int port, bool add, ++ bool tagged, bool extend) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid, shift; ++ ++ if (extend) { ++ tid = tagged ? MVPP2_PE_EDSA_TAGGED : MVPP2_PE_EDSA_UNTAGGED; ++ shift = 8; ++ } else { ++ tid = tagged ? MVPP2_PE_DSA_TAGGED : MVPP2_PE_DSA_UNTAGGED; ++ shift = 4; ++ } ++ ++ if (hw->prs_shadow[tid].valid) { ++ /* Entry exist - update port only */ ++ pe.index = tid; ++ mv_pp2x_prs_hw_read(hw, &pe); ++ } else { ++ /* Entry doesn't exist - create new */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); ++ pe.index = tid; ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_DSA); ++ ++ if (tagged) { ++ /* Set tagged bit in DSA tag */ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, ++ MVPP2_PRS_TCAM_DSA_TAGGED_BIT, ++ MVPP2_PRS_TCAM_DSA_TAGGED_BIT); ++ ++ /* Set ai bits for next iteration */ ++ if (extend) ++ mv_pp2x_prs_sram_ai_update(&pe, 1, ++ MVPP2_PRS_SRAM_AI_MASK); ++ else ++ mv_pp2x_prs_sram_ai_update(&pe, 0, ++ MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* Set result info bits to 'single vlan' */ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_SINGLE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ ++ /* If packet is tagged continue check vid filtering */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID); ++ } else { ++ /* Shift 4 bytes if DSA tag or 8 bytes in case of EDSA tag*/ ++ mv_pp2x_prs_sram_shift_set(&pe, shift, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Set result info bits to 'no vlans' */ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ } ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, 0); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(&pe, port, add); ++ ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Set entry for dsa ethertype */ ++static void mv_pp2x_prs_dsa_tag_ethertype_set(struct mv_pp2x_hw *hw, int port, ++ bool add, bool tagged, ++ bool extend) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid, shift, port_mask; ++ ++ if (extend) { ++ tid = tagged ? MVPP2_PE_ETYPE_EDSA_TAGGED : ++ MVPP2_PE_ETYPE_EDSA_UNTAGGED; ++ port_mask = 0; ++ shift = 8; ++ } else { ++ tid = tagged ? MVPP2_PE_ETYPE_DSA_TAGGED : ++ MVPP2_PE_ETYPE_DSA_UNTAGGED; ++ port_mask = MVPP2_PRS_PORT_MASK; ++ shift = 4; ++ } ++ ++ if (hw->prs_shadow[tid].valid) { ++ /* Entry exist - update port only */ ++ pe.index = tid; ++ mv_pp2x_prs_hw_read(hw, &pe); ++ } else { ++ /* Entry doesn't exist - create new */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); ++ pe.index = tid; ++ ++ /* Set ethertype */ ++ mv_pp2x_prs_match_etype(&pe, 0, ETH_P_EDSA); ++ mv_pp2x_prs_match_etype(&pe, 2, 0); ++ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DSA_MASK, ++ MVPP2_PRS_RI_DSA_MASK); ++ /* Shift ethertype + 2 byte reserved + tag*/ ++ mv_pp2x_prs_sram_shift_set(&pe, 2 + MVPP2_ETH_TYPE_LEN + shift, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_DSA); ++ ++ if (tagged) { ++ /* Set tagged bit in DSA tag */ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, ++ MVPP2_ETH_TYPE_LEN + 2 + 3, ++ MVPP2_PRS_TCAM_DSA_TAGGED_BIT, ++ MVPP2_PRS_TCAM_DSA_TAGGED_BIT); ++ /* Clear all ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(&pe, 0, ++ MVPP2_PRS_SRAM_AI_MASK); ++ /* If packet is tagged continue check vlans */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ } else { ++ /* Set result info bits to 'no vlans' */ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ } ++ /* Mask/unmask all ports, depending on dsa type */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, port_mask); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(&pe, port, add); ++ ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Search for existing single/triple vlan entry */ ++static struct mv_pp2x_prs_entry *mv_pp2x_prs_vlan_find(struct mv_pp2x_hw *hw, ++ unsigned short tpid, ++ int ai, ++ struct mv_pp2x_prs_entry *pe) ++{ ++ int tid; ++ ++ memset(pe, 0, sizeof(*pe)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); ++ ++ /* Go through the all entries with MVPP2_PRS_LU_VLAN */ ++ for (tid = MVPP2_PE_FIRST_FREE_TID; ++ tid <= MVPP2_PE_LAST_FREE_TID; tid++) { ++ unsigned int ri_bits, ai_bits; ++ bool match; ++ ++ if (!hw->prs_shadow[tid].valid || ++ hw->prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN) ++ continue; ++ ++ pe->index = tid; ++ ++ mv_pp2x_prs_hw_read(hw, pe); ++ match = mv_pp2x_prs_tcam_data_cmp(pe, 0, swab16(tpid)); ++ if (!match) ++ continue; ++ ++ /* Get vlan type */ ++ ri_bits = mv_pp2x_prs_sram_ri_get(pe); ++ ri_bits &= MVPP2_PRS_RI_VLAN_MASK; ++ ++ /* Get current ai value from tcam */ ++ ai_bits = mv_pp2x_prs_tcam_ai_get(pe); ++ /* Clear double vlan bit */ ++ ai_bits &= ~MVPP2_PRS_DBL_VLAN_AI_BIT; ++ ++ if (ai != ai_bits) ++ continue; ++ ++ if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE || ++ ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE) ++ return pe; ++ } ++ return NULL; ++} ++ ++/* Add/update single/triple vlan entry */ ++static int mv_pp2x_prs_vlan_add(struct mv_pp2x_hw *hw, unsigned short tpid, ++ int ai, unsigned int port_map) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ int tid_aux, tid; ++ ++ pe = mv_pp2x_prs_vlan_find(hw, tpid, ai, &l_pe); ++ ++ if (!pe) { ++ /* Create new tcam entry */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_LAST_FREE_TID, ++ MVPP2_PE_FIRST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ ++ /* Get last double vlan tid */ ++ for (tid_aux = MVPP2_PE_LAST_FREE_TID; ++ tid_aux >= MVPP2_PE_FIRST_FREE_TID; tid_aux--) { ++ unsigned int ri_bits; ++ ++ if (!hw->prs_shadow[tid_aux].valid || ++ hw->prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN) ++ continue; ++ ++ pe->index = tid_aux; ++ mv_pp2x_prs_hw_read(hw, pe); ++ ri_bits = mv_pp2x_prs_sram_ri_get(pe); ++ if ((ri_bits & MVPP2_PRS_RI_VLAN_MASK) == ++ MVPP2_PRS_RI_VLAN_DOUBLE) ++ break; ++ } ++ /* New-single should be after double/aux */ ++ if (tid <= tid_aux) ++ return -EINVAL; ++ ++ memset(pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); ++ pe->index = tid; ++ ++ mv_pp2x_prs_match_etype(pe, 0, tpid); ++ ++ mv_pp2x_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_VID); ++ ++ /* Do not shift now, will be shifted after VID is checked*/ ++ ++ /* Clear all ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ if (ai == MVPP2_PRS_SINGLE_VLAN_AI) { ++ mv_pp2x_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_SINGLE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ } else { ++ ai |= MVPP2_PRS_DBL_VLAN_AI_BIT; ++ mv_pp2x_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_TRIPLE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ } ++ mv_pp2x_prs_tcam_ai_update(pe, ai, MVPP2_PRS_SRAM_AI_MASK); ++ ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_VLAN); ++ } ++ /* Update ports' mask */ ++ mv_pp2x_prs_tcam_port_map_set(pe, port_map); ++ ++ mv_pp2x_prs_hw_write(hw, pe); ++ return 0; ++} ++ ++/* Get first free double vlan ai number */ ++static int mv_pp2x_prs_double_vlan_ai_free_get(struct mv_pp2x_hw *hw) ++{ ++ int i; ++ ++ for (i = 1; i < MVPP2_PRS_DBL_VLANS_MAX; i++) { ++ if (!hw->prs_double_vlans[i]) ++ return i; ++ } ++ ++ return -EINVAL; ++} ++ ++/* Search for existing double vlan entry */ ++static struct mv_pp2x_prs_entry *mv_pp2x_prs_double_vlan_find(struct mv_pp2x_hw *hw, ++ unsigned short tpid1, ++ unsigned short tpid2, ++ struct mv_pp2x_prs_entry *pe) ++{ ++ int tid; ++ ++ memset(pe, 0, sizeof(*pe)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); ++ ++ /* Go through the all entries with MVPP2_PRS_LU_VLAN */ ++ for (tid = MVPP2_PE_FIRST_FREE_TID; ++ tid <= MVPP2_PE_LAST_FREE_TID; tid++) { ++ unsigned int ri_mask; ++ bool match; ++ ++ if (!hw->prs_shadow[tid].valid || ++ hw->prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN) ++ continue; ++ ++ pe->index = tid; ++ mv_pp2x_prs_hw_read(hw, pe); ++ ++ match = mv_pp2x_prs_tcam_data_cmp(pe, 0, swab16(tpid1)) && ++ mv_pp2x_prs_tcam_data_cmp(pe, 4, swab16(tpid2)); ++ ++ if (!match) ++ continue; ++ ++ ri_mask = mv_pp2x_prs_sram_ri_get(pe) & MVPP2_PRS_RI_VLAN_MASK; ++ if (ri_mask == MVPP2_PRS_RI_VLAN_DOUBLE) ++ return pe; ++ } ++ return NULL; ++} ++ ++/* Add or update double vlan entry */ ++static int mv_pp2x_prs_double_vlan_add(struct mv_pp2x_hw *hw, ++ unsigned short tpid1, ++ unsigned short tpid2, ++ unsigned int port_map) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ int tid_aux, tid, ai; ++ ++ pe = mv_pp2x_prs_double_vlan_find(hw, tpid1, tpid2, &l_pe); ++ ++ if (!pe) { ++ /* Create new tcam entry */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ ++ /* Set ai value for new double vlan entry */ ++ ai = mv_pp2x_prs_double_vlan_ai_free_get(hw); ++ if (ai < 0) ++ return ai; ++ ++ /* Get first single/triple vlan tid */ ++ for (tid_aux = MVPP2_PE_FIRST_FREE_TID; ++ tid_aux <= MVPP2_PE_LAST_FREE_TID; tid_aux++) { ++ unsigned int ri_bits; ++ ++ if (!hw->prs_shadow[tid_aux].valid || ++ hw->prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN) ++ continue; ++ ++ pe->index = tid_aux; ++ mv_pp2x_prs_hw_read(hw, pe); ++ ri_bits = mv_pp2x_prs_sram_ri_get(pe); ++ ri_bits &= MVPP2_PRS_RI_VLAN_MASK; ++ if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE || ++ ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE) ++ break; ++ } ++ /* New-double should be before single/aux */ ++ if (tid >= tid_aux) ++ return -ERANGE; ++ ++ memset(pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); ++ pe->index = tid; ++ ++ hw->prs_double_vlans[ai] = true; ++ ++ mv_pp2x_prs_match_etype(pe, 0, tpid1); ++ mv_pp2x_prs_match_etype(pe, 4, tpid2); ++ ++ mv_pp2x_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_VLAN); ++ /* Shift 4 bytes - skip outer vlan tags */ ++ mv_pp2x_prs_sram_shift_set(pe, MVPP2_VLAN_TAG_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ mv_pp2x_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_DOUBLE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ mv_pp2x_prs_sram_ai_update(pe, ai | MVPP2_PRS_DBL_VLAN_AI_BIT, ++ MVPP2_PRS_SRAM_AI_MASK); ++ ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_VLAN); ++ } ++ ++ /* Update ports' mask */ ++ mv_pp2x_prs_tcam_port_map_set(pe, port_map); ++ mv_pp2x_prs_hw_write(hw, pe); ++ return 0; ++} ++ ++/* IPv4 header parsing for fragmentation and L4 offset */ ++static int mv_pp2x_prs_ip4_proto(struct mv_pp2x_hw *hw, unsigned short proto, ++ unsigned int ri, unsigned int ri_mask) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid; ++ ++ if ((proto != IPPROTO_TCP) && (proto != IPPROTO_UDP) && ++ (proto != IPPROTO_IGMP)) ++ return -EINVAL; ++ ++ /* Not fragmented packet */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ pe.index = tid; ++ ++ /* Set next lu to IPv4 */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ /* Set L4 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, ++ sizeof(struct iphdr) - 4, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ mv_pp2x_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, ++ MVPP2_PRS_IPV4_DIP_AI_BIT); ++ mv_pp2x_prs_sram_ri_update(&pe, ri | MVPP2_PRS_RI_IP_FRAG_FALSE, ++ ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); ++ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 2, 0x00, ++ MVPP2_PRS_TCAM_PROTO_MASK_L); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 3, 0x00, ++ MVPP2_PRS_TCAM_PROTO_MASK); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 5, proto, ++ MVPP2_PRS_TCAM_PROTO_MASK); ++ mv_pp2x_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Fragmented packet */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ pe.index = tid; ++ /* Clear ri before updating */ ++ pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; ++ pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; ++ mv_pp2x_prs_sram_ri_update(&pe, ri, ri_mask); ++ mv_pp2x_prs_sram_ri_update(&pe, ri | MVPP2_PRS_RI_IP_FRAG_TRUE, ++ ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); ++ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 2, 0x00, 0x0); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 3, 0x00, 0x0); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* IPv4 L3 multicast or broadcast */ ++static int mv_pp2x_prs_ip4_cast(struct mv_pp2x_hw *hw, unsigned short l3_cast) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int mask, tid; ++ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ pe.index = tid; ++ ++ switch (l3_cast) { ++ case MVPP2_PRS_L3_MULTI_CAST: ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_IPV4_MC, ++ MVPP2_PRS_IPV4_MC_MASK); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_MCAST, ++ MVPP2_PRS_RI_L3_ADDR_MASK); ++ break; ++ case MVPP2_PRS_L3_BROAD_CAST: ++ mask = MVPP2_PRS_IPV4_BC_MASK; ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, mask, mask); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 1, mask, mask); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 2, mask, mask); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 3, mask, mask); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_BCAST, ++ MVPP2_PRS_RI_L3_ADDR_MASK); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, ++ MVPP2_PRS_IPV4_DIP_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* Set entries for protocols over IPv6 */ ++static int mv_pp2x_prs_ip6_proto(struct mv_pp2x_hw *hw, unsigned short proto, ++ unsigned int ri, unsigned int ri_mask) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid; ++ ++ if ((proto != IPPROTO_TCP) && (proto != IPPROTO_UDP) && ++ (proto != IPPROTO_ICMPV6) && (proto != IPPROTO_IPIP)) ++ return -EINVAL; ++ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ pe.index = tid; ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_ri_update(&pe, ri, ri_mask); ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, ++ sizeof(struct ipv6hdr) - 6, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, proto, ++ MVPP2_PRS_TCAM_PROTO_MASK); ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, ++ MVPP2_PRS_IPV6_NO_EXT_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Write HW */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* IPv6 L3 multicast entry */ ++static int mv_pp2x_prs_ip6_cast(struct mv_pp2x_hw *hw, unsigned short l3_cast) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid; ++ ++ if (l3_cast != MVPP2_PRS_L3_MULTI_CAST) ++ return -EINVAL; ++ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ pe.index = tid; ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_MCAST, ++ MVPP2_PRS_RI_L3_ADDR_MASK); ++ mv_pp2x_prs_sram_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, ++ MVPP2_PRS_IPV6_NO_EXT_AI_BIT); ++ /* Shift back to IPv6 NH */ ++ mv_pp2x_prs_sram_shift_set(&pe, -18, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_IPV6_MC, ++ MVPP2_PRS_IPV6_MC_MASK); ++ mv_pp2x_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* Parser per-port initialization */ ++void mv_pp2x_prs_hw_port_init(struct mv_pp2x_hw *hw, int port, int lu_first, ++ int lu_max, int offset) ++{ ++ u32 val; ++ ++ /* Set lookup ID */ ++ val = mv_pp2x_read(hw, MVPP2_PRS_INIT_LOOKUP_REG); ++ val &= ~MVPP2_PRS_PORT_LU_MASK(port); ++ val |= MVPP2_PRS_PORT_LU_VAL(port, lu_first); ++ mv_pp2x_write(hw, MVPP2_PRS_INIT_LOOKUP_REG, val); ++ ++ /* Set maximum number of loops for packet received from port */ ++ val = mv_pp2x_read(hw, MVPP2_PRS_MAX_LOOP_REG(port)); ++ val &= ~MVPP2_PRS_MAX_LOOP_MASK(port); ++ val |= MVPP2_PRS_MAX_LOOP_VAL(port, lu_max); ++ mv_pp2x_write(hw, MVPP2_PRS_MAX_LOOP_REG(port), val); ++ ++ /* Set initial offset for packet header extraction for the first ++ * searching loop ++ */ ++ val = mv_pp2x_read(hw, MVPP2_PRS_INIT_OFFS_REG(port)); ++ val &= ~MVPP2_PRS_INIT_OFF_MASK(port); ++ val |= MVPP2_PRS_INIT_OFF_VAL(port, offset); ++ mv_pp2x_write(hw, MVPP2_PRS_INIT_OFFS_REG(port), val); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_hw_port_init); ++ ++/* Default flow entries initialization for all ports */ ++static void mv_pp2x_prs_def_flow_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int port; ++ ++ for (port = 0; port < MVPP2_MAX_PORTS; port++) { ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ pe.index = MVPP2_PE_FIRST_DEFAULT_FLOW - port; ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, 0); ++ ++ /* Set flow ID*/ ++ mv_pp2x_prs_sram_ai_update(&pe, port, MVPP2_PRS_FLOW_ID_MASK); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ } ++} ++ ++/* Set default entry for Marvell Header field */ ++static void mv_pp2x_prs_mh_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ ++ pe.index = MVPP2_PE_MH_DEFAULT; ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MH); ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_MH_SIZE, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MH); ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Drop flow control pause frames */ ++static void mv_pp2x_prs_drop_fc(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ unsigned int len; ++ unsigned char da[ETH_ALEN] = { ++ 0x01, 0x80, 0xC2, 0x00, 0x00, 0x01 }; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ ++ /* For all ports - drop flow control frames */ ++ pe.index = MVPP2_PE_FC_DROP; ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ ++ /* Set match on DA */ ++ len = ETH_ALEN; ++ while (len--) ++ mv_pp2x_prs_tcam_data_byte_set(&pe, len, da[len], 0xff); ++ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, ++ MVPP2_PRS_RI_DROP_MASK); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MAC); ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Set default entires (place holder) for promiscuous, non-promiscuous and ++ * multicast MAC addresses ++ */ ++static void mv_pp2x_prs_mac_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ ++ /* Non-promiscuous mode for all ports - DROP unknown packets */ ++ pe.index = MVPP2_PE_MAC_NON_PROMISCUOUS; ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); ++ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, ++ MVPP2_PRS_RI_DROP_MASK); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MAC); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* place holders only - no ports */ ++ mv_pp2x_prs_drop_fc(hw); ++ mv_pp2x_prs_mac_drop_all_set(hw, 0, false); ++ mv_pp2x_prs_mac_uc_promisc_set(hw, 0, false); ++ mv_pp2x_prs_mac_mc_promisc_set(hw, 0, false); ++} ++ ++/* Set default entries for various types of dsa packets */ ++static void mv_pp2x_prs_dsa_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ /* None tagged EDSA entry - place holder */ ++ mv_pp2x_prs_dsa_tag_set(hw, 0, false, MVPP2_PRS_UNTAGGED, ++ MVPP2_PRS_EDSA); ++ ++ /* Tagged EDSA entry - place holder */ ++ mv_pp2x_prs_dsa_tag_set(hw, 0, false, MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); ++ ++ /* None tagged DSA entry - place holder */ ++ mv_pp2x_prs_dsa_tag_set(hw, 0, false, MVPP2_PRS_UNTAGGED, ++ MVPP2_PRS_DSA); ++ ++ /* Tagged DSA entry - place holder */ ++ mv_pp2x_prs_dsa_tag_set(hw, 0, false, MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); ++ ++ /* None tagged EDSA ethertype entry - place holder*/ ++ mv_pp2x_prs_dsa_tag_ethertype_set(hw, 0, false, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); ++ ++ /* Tagged EDSA ethertype entry - place holder*/ ++ mv_pp2x_prs_dsa_tag_ethertype_set(hw, 0, false, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); ++ ++ /* None tagged DSA ethertype entry */ ++ mv_pp2x_prs_dsa_tag_ethertype_set(hw, 0, true, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); ++ ++ /* Tagged DSA ethertype entry */ ++ mv_pp2x_prs_dsa_tag_ethertype_set(hw, 0, true, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); ++ ++ /* Set default entry, in case DSA or EDSA tag not found */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); ++ pe.index = MVPP2_PE_DSA_DEFAULT; ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ ++ /* Shift 0 bytes */ ++ mv_pp2x_prs_sram_shift_set(&pe, 0, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_MAC); ++ ++ /* Clear all sram ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Match basic ethertypes */ ++static int mv_pp2x_prs_etype_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid; ++ ++ /* Ethertype: PPPoE */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, ETH_P_PPP_SES); ++ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_PPPOE_HDR_SIZE, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_PPPOE); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_PPPOE_MASK, ++ MVPP2_PRS_RI_PPPOE_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = false; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_PPPOE_MASK, ++ MVPP2_PRS_RI_PPPOE_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Ethertype: ARP */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, ETH_P_ARP); ++ ++ /* Generate flow in the next iteration*/ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_ARP, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* Set L3 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = true; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_L3_ARP, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Ethertype: LBTD */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, MVPP2_IP_LBDT_TYPE); ++ ++ /* Generate flow in the next iteration*/ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | ++ MVPP2_PRS_RI_UDF3_RX_SPECIAL, ++ MVPP2_PRS_RI_CPU_CODE_MASK | ++ MVPP2_PRS_RI_UDF3_MASK); ++ /* Set L3 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = true; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | ++ MVPP2_PRS_RI_UDF3_RX_SPECIAL, ++ MVPP2_PRS_RI_CPU_CODE_MASK | ++ MVPP2_PRS_RI_UDF3_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Ethertype: IPv4 without options */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, ETH_P_IP); ++ mv_pp2x_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_IPV4_HEAD | ++ MVPP2_PRS_IPV4_IHL, ++ MVPP2_PRS_IPV4_HEAD_MASK | ++ MVPP2_PRS_IPV4_IHL_MASK); ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* Skip eth_type + 4 bytes of IP header */ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ /* Set L3 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = false; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_L3_IP4, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Ethertype: IPv4 with options */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ pe.index = tid; ++ ++ /* Clear tcam data before updating */ ++ pe.tcam.byte[TCAM_DATA_BYTE(MVPP2_ETH_TYPE_LEN)] = 0x0; ++ pe.tcam.byte[TCAM_DATA_MASK(MVPP2_ETH_TYPE_LEN)] = 0x0; ++ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_IPV4_HEAD, ++ MVPP2_PRS_IPV4_HEAD_MASK); ++ ++ /* Clear ri before updating */ ++ pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; ++ pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = false; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_L3_IP4_OPT, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Ethertype: IPv6 without options */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, ETH_P_IPV6); ++ ++ /* Skip DIP of IPV6 header */ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 8 + ++ MVPP2_MAX_L3_ADDR_SIZE, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP6, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* Set L3 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = false; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_L3_IP6, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Default entry for MVPP2_PRS_LU_L2 - Unknown ethtype */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); ++ pe.index = MVPP2_PE_ETH_TYPE_UN; ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Generate flow in the next iteration*/ ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UN, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* Set L3 offset even it's unknown L3 */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_L2); ++ hw->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; ++ hw->prs_shadow[pe.index].finish = true; ++ mv_pp2x_prs_shadow_ri_set(hw, pe.index, MVPP2_PRS_RI_L3_UN, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ return 0; ++} ++ ++/* Configure vlan entries and detect up to 2 successive VLAN tags. ++ * Possible options: ++ * 0x88A8, 0x8100 (outer, inner) ++ * 0x8100, 0x8100 (outer, inner) ++ * 0x8100 ++ * 0x88A8 ++ */ ++static int mv_pp2x_prs_vlan_init(struct platform_device *pdev, ++ struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int err; ++ ++ hw->prs_double_vlans = devm_kcalloc(&pdev->dev, sizeof(bool), ++ MVPP2_PRS_DBL_VLANS_MAX, ++ GFP_KERNEL); ++ if (!hw->prs_double_vlans) ++ return -ENOMEM; ++ /* Double VLAN: 0x88A8, 0x8100 */ ++ err = mv_pp2x_prs_double_vlan_add(hw, ETH_P_8021AD, ETH_P_8021Q, ++ MVPP2_PRS_PORT_MASK); ++ if (err) ++ return err; ++ ++ /* Double VLAN: 0x8100, 0x8100 */ ++ err = mv_pp2x_prs_double_vlan_add(hw, ETH_P_8021Q, ETH_P_8021Q, ++ MVPP2_PRS_PORT_MASK); ++ if (err) ++ return err; ++ ++ /* Single VLAN: 0x88a8 */ ++ err = mv_pp2x_prs_vlan_add(hw, ETH_P_8021AD, MVPP2_PRS_SINGLE_VLAN_AI, ++ MVPP2_PRS_PORT_MASK); ++ if (err) ++ return err; ++ ++ /* Single VLAN: 0x8100 */ ++ err = mv_pp2x_prs_vlan_add(hw, ETH_P_8021Q, MVPP2_PRS_SINGLE_VLAN_AI, ++ MVPP2_PRS_PORT_MASK); ++ if (err) ++ return err; ++ ++ /* Set default double vlan entry */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ pe.index = MVPP2_PE_VLAN_DBL; ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID); ++ ++ /* Do not update offset, it is already positioned to inner vlan by double vlan parser entry*/ ++ ++ /* Clear ai for next iterations */ ++ mv_pp2x_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_DOUBLE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_DBL_VLAN_AI_BIT, ++ MVPP2_PRS_DBL_VLAN_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_VLAN); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Set default vlan none entry */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); ++ pe.index = MVPP2_PE_VLAN_NONE; ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, ++ MVPP2_PRS_RI_VLAN_MASK); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_VLAN); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* Initialize parser entries for VID filtering */ ++static void mv_pp2x_prs_vid_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ ++ /* Set default vid entry */ ++ pe.index = MVPP2_PE_VID_FLTR_DEFAULT; ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, 0, ++ MVPP2_PRS_EDSA_VID_AI_BIT); ++ ++ /* Skip VLAN header - Set offset to 4 bytes */ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Clear all ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_VID); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Set default vid entry for extended DSA*/ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ ++ /* Set default vid entry */ ++ pe.index = MVPP2_PE_VID_EDSA_FLTR_DEFAULT; ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_EDSA_VID_AI_BIT, ++ MVPP2_PRS_EDSA_VID_AI_BIT); ++ ++ /* Skip VLAN header - Set offset to 8 bytes */ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_EDSA_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Clear all ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); ++ ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_VID); ++ mv_pp2x_prs_hw_write(hw, &pe); ++} ++ ++/* Set entries for PPPoE ethertype */ ++static int mv_pp2x_prs_pppoe_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int tid; ++ ++ /* IPv4 over PPPoE with options */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, PPP_IP); ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* Skip eth_type + 4 bytes of IP header */ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ /* Set L3 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_PPPOE); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* IPv4 over PPPoE without options */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ pe.index = tid; ++ ++ mv_pp2x_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_IPV4_HEAD | MVPP2_PRS_IPV4_IHL, ++ MVPP2_PRS_IPV4_HEAD_MASK | ++ MVPP2_PRS_IPV4_IHL_MASK); ++ ++ /* Clear ri before updating */ ++ pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; ++ pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_PPPOE); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* IPv6 over PPPoE */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); ++ pe.index = tid; ++ ++ mv_pp2x_prs_match_etype(&pe, 0, PPP_IPV6); ++ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP6, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* Skip eth_type + 4 bytes of IPv6 header */ ++ mv_pp2x_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ /* Set L3 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_PPPOE); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Non-IP over PPPoE */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); ++ pe.index = tid; ++ ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UN, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ /* Set L3 offset even if it's unknown L3 */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_PPPOE); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* Initialize entries for IPv4 */ ++static int mv_pp2x_prs_ip4_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int err; ++ ++ /* Set entries for TCP, UDP and IGMP over IPv4 */ ++ err = mv_pp2x_prs_ip4_proto(hw, IPPROTO_TCP, MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_ip4_proto(hw, IPPROTO_UDP, MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_ip4_proto(hw, IPPROTO_IGMP, ++ MVPP2_PRS_RI_CPU_CODE_RX_SPEC | ++ MVPP2_PRS_RI_UDF3_RX_SPECIAL, ++ MVPP2_PRS_RI_CPU_CODE_MASK | ++ MVPP2_PRS_RI_UDF3_MASK); ++ ++ if (err) ++ return err; ++ ++ /* IPv4 Broadcast */ ++ err = mv_pp2x_prs_ip4_cast(hw, MVPP2_PRS_L3_BROAD_CAST); ++ ++ if (err) ++ return err; ++ ++ /* IPv4 Multicast */ ++ err = mv_pp2x_prs_ip4_cast(hw, MVPP2_PRS_L3_MULTI_CAST); ++ ++ if (err) ++ return err; ++ ++ /* Default IPv4 entry for unknown protocols */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ pe.index = MVPP2_PE_IP4_PROTO_UN; ++ ++ /* Set next lu to IPv4 */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ /* Set L4 offset */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, ++ sizeof(struct iphdr) - 4, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ mv_pp2x_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, ++ MVPP2_PRS_IPV4_DIP_AI_BIT); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Default IPv4 entry for unicast address */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ pe.index = MVPP2_PE_IP4_ADDR_UN; ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UCAST, ++ MVPP2_PRS_RI_L3_ADDR_MASK); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, ++ MVPP2_PRS_IPV4_DIP_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ return 0; ++} ++ ++/* Initialize entries for IPv6 */ ++static int mv_pp2x_prs_ip6_init(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_prs_entry pe; ++ int err; ++ ++ /* Set entries for TCP, UDP and ICMP over IPv6 */ ++ err = mv_pp2x_prs_ip6_proto(hw, IPPROTO_TCP, ++ MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_ip6_proto(hw, IPPROTO_UDP, ++ MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_ip6_proto(hw, IPPROTO_ICMPV6, ++ MVPP2_PRS_RI_CPU_CODE_RX_SPEC | ++ MVPP2_PRS_RI_UDF3_RX_SPECIAL, ++ MVPP2_PRS_RI_CPU_CODE_MASK | ++ MVPP2_PRS_RI_UDF3_MASK); ++ if (err) ++ return err; ++ ++ /* IPv4 is the last header. This is similar case as 6-TCP or 17-UDP */ ++ /* Result Info: UDF7=1, DS lite */ ++ err = mv_pp2x_prs_ip6_proto(hw, IPPROTO_IPIP, ++ MVPP2_PRS_RI_UDF7_IP6_LITE, ++ MVPP2_PRS_RI_UDF7_MASK); ++ if (err) ++ return err; ++ ++ /* IPv6 multicast */ ++ err = mv_pp2x_prs_ip6_cast(hw, MVPP2_PRS_L3_MULTI_CAST); ++ if (err) ++ return err; ++ ++ /* Default IPv6 entry for unknown protocols */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ pe.index = MVPP2_PE_IP6_PROTO_UN; ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ /* Set L4 offset relatively to our current place */ ++ mv_pp2x_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, ++ sizeof(struct ipv6hdr) - 4, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, ++ MVPP2_PRS_IPV6_NO_EXT_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Default IPv6 entry for unknown ext protocols */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ pe.index = MVPP2_PE_IP6_EXT_PROTO_UN; ++ ++ /* Finished: go to flowid generation */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); ++ mv_pp2x_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, ++ MVPP2_PRS_RI_L4_PROTO_MASK); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_EXT_AI_BIT, ++ MVPP2_PRS_IPV6_EXT_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP4); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ /* Default IPv6 entry for unicast address */ ++ memset(&pe, 0, sizeof(struct mv_pp2x_prs_entry)); ++ mv_pp2x_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ pe.index = MVPP2_PE_IP6_ADDR_UN; ++ ++ /* Finished: go to IPv6 again */ ++ mv_pp2x_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UCAST, ++ MVPP2_PRS_RI_L3_ADDR_MASK); ++ mv_pp2x_prs_sram_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, ++ MVPP2_PRS_IPV6_NO_EXT_AI_BIT); ++ /* Shift back to IPV6 NH */ ++ mv_pp2x_prs_sram_shift_set(&pe, -18, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ mv_pp2x_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); ++ /* Unmask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); ++ ++ /* Update shadow table and hw entry */ ++ mv_pp2x_prs_shadow_set(hw, pe.index, MVPP2_PRS_LU_IP6); ++ mv_pp2x_prs_hw_write(hw, &pe); ++ ++ return 0; ++} ++ ++/* Compare MAC DA with tcam entry data */ ++static bool mv_pp2x_prs_mac_range_equals(struct mv_pp2x_prs_entry *pe, ++ const u8 *da, unsigned char *mask) ++{ ++ unsigned char tcam_byte, tcam_mask; ++ int index; ++ ++ for (index = 0; index < ETH_ALEN; index++) { ++ mv_pp2x_prs_tcam_data_byte_get(pe, index, &tcam_byte, ++ &tcam_mask); ++ if (tcam_mask != mask[index]) ++ return false; ++ ++ if ((tcam_mask & tcam_byte) != (da[index] & mask[index])) ++ return false; ++ } ++ ++ return true; ++} ++ ++/* Find tcam entry with matched pair */ ++static struct mv_pp2x_prs_entry * ++mv_pp2x_prs_mac_da_range_find(struct mv_pp2x_hw *hw, int pmap, const u8 *da, ++ unsigned char *mask, int udf_type, ++ struct mv_pp2x_prs_entry *pe) ++{ ++ int tid; ++ ++ memset(pe, 0, sizeof(*pe)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC); ++ ++ /* Go through the all entires with MVPP2_PRS_LU_MAC */ ++ for (tid = MVPP2_PE_MAC_RANGE_START; ++ tid <= MVPP2_PE_MAC_RANGE_END; tid++) { ++ unsigned int entry_pmap; ++ ++ if (!hw->prs_shadow[tid].valid || ++ (hw->prs_shadow[tid].lu != MVPP2_PRS_LU_MAC) || ++ (hw->prs_shadow[tid].udf != udf_type)) ++ continue; ++ ++ pe->index = tid; ++ mv_pp2x_prs_hw_read(hw, pe); ++ entry_pmap = mv_pp2x_prs_tcam_port_map_get(pe); ++ ++ if (mv_pp2x_prs_mac_range_equals(pe, da, mask)) ++ return pe; ++ } ++ return NULL; ++} ++ ++/* Update parser's mac da entry */ ++int mv_pp2x_prs_mac_da_accept(struct mv_pp2x_port *port, const u8 *da, bool add) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ unsigned int pmap, len, ri, tid; ++ unsigned char mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ /* Scan TCAM and see if entry with this already exist */ ++ pe = mv_pp2x_prs_mac_da_range_find(hw, (1 << port->id), da, mask, ++ MVPP2_PRS_UDF_MAC_DEF, &l_pe); ++ /* No such entry */ ++ if (!pe) { ++ if (!add) ++ return 0; ++ ++ /* Create new TCAM entry */ ++ /* Go through all entries from first to last in MAC range */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, MVPP2_PE_MAC_RANGE_START, ++ MVPP2_PE_MAC_RANGE_END); ++ if (tid < 0) ++ return tid; ++ ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC); ++ pe->index = tid; ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(pe, 0); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(pe, port->id, add); ++ ++ /* Invalidate the entry if no ports are left enabled */ ++ pmap = mv_pp2x_prs_tcam_port_map_get(pe); ++ if (pmap == 0) { ++ if (add) ++ return -1; ++ mv_pp2x_prs_hw_inv(hw, pe->index); ++ hw->prs_shadow[pe->index].valid = false; ++ return 0; ++ } ++ ++ /* Continue - set next lookup */ ++ mv_pp2x_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_DSA); ++ ++ /* Set match on DA */ ++ len = ETH_ALEN; ++ while (len--) ++ mv_pp2x_prs_tcam_data_byte_set(pe, len, da[len], 0xff); ++ ++ /* Set result info bits */ ++ if (is_broadcast_ether_addr(da)) ++ ri = MVPP2_PRS_RI_L2_BCAST; ++ else if (is_multicast_ether_addr(da)) ++ ri = MVPP2_PRS_RI_L2_MCAST; ++ else ++ ri = MVPP2_PRS_RI_L2_UCAST; ++ /* Set M2M */ ++ if (ether_addr_equal(da, port->dev->dev_addr)) ++ ri |= MVPP2_PRS_RI_MAC_ME_MASK; ++ ++ mv_pp2x_prs_sram_ri_update(pe, ri, MVPP2_PRS_RI_L2_CAST_MASK | ++ MVPP2_PRS_RI_MAC_ME_MASK); ++ mv_pp2x_prs_shadow_ri_set(hw, pe->index, ri, MVPP2_PRS_RI_L2_CAST_MASK | ++ MVPP2_PRS_RI_MAC_ME_MASK); ++ ++ /* Shift to ethertype */ ++ mv_pp2x_prs_sram_shift_set(pe, 2 * ETH_ALEN, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Update shadow table and hw entry */ ++ hw->prs_shadow[pe->index].udf = MVPP2_PRS_UDF_MAC_DEF; ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_MAC); ++ mv_pp2x_prs_hw_write(hw, pe); ++ ++ return 0; ++} ++ ++int mv_pp2x_prs_update_mac_da(struct net_device *dev, const u8 *da) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ u8 old_da[ETH_ALEN]; ++ int err; ++ ++ if (ether_addr_equal(da, dev->dev_addr)) ++ return 0; ++ ++ /* Record old DA */ ++ ether_addr_copy(old_da, dev->dev_addr); ++ ++ /* Remove old parser entry */ ++ err = mv_pp2x_prs_mac_da_accept(port, dev->dev_addr, false); ++ if (err) ++ return err; ++ ++ /* Set addr in the device */ ++ ether_addr_copy(dev->dev_addr, da); ++ ++ /* Add new parser entry */ ++ err = mv_pp2x_prs_mac_da_accept(port, da, true); ++ if (err) { ++ /* Restore addr in the device */ ++ ether_addr_copy(dev->dev_addr, old_da); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static bool mv_pp2x_mac_in_uc_list(struct net_device *dev, const u8 *da) ++{ ++ struct netdev_hw_addr *ha; ++ ++ if (!netdev_uc_count(dev)) ++ return false; ++ ++ netdev_for_each_uc_addr(ha, dev) { ++ if (ether_addr_equal(da, dev->dev_addr)) ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool mv_pp2x_mac_in_mc_list(struct net_device *dev, const u8 *da) ++{ ++ struct netdev_hw_addr *ha; ++ ++ if (!netdev_mc_count(dev)) ++ return false; ++ ++ netdev_for_each_mc_addr(ha, dev) { ++ if (ether_addr_equal(da, dev->dev_addr)) ++ return true; ++ } ++ ++ return false; ++} ++ ++/* Delete port's uc/mc/bc simple (not range) entries with options */ ++void mv_pp2x_prs_mac_entry_del(struct mv_pp2x_port *port, ++ enum mv_pp2x_l2_cast l2_cast, ++ enum mv_pp2x_mac_del_option op) ++{ ++ struct mv_pp2x_prs_entry pe; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int index, tid; ++ ++ for (tid = MVPP2_PE_MAC_RANGE_START; ++ tid <= MVPP2_PE_MAC_RANGE_END; tid++) { ++ unsigned char da[ETH_ALEN], da_mask[ETH_ALEN]; ++ ++ if (!hw->prs_shadow[tid].valid || ++ (hw->prs_shadow[tid].lu != MVPP2_PRS_LU_MAC) || ++ (hw->prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF)) ++ continue; ++ ++ /* Only simple mac entries */ ++ pe.index = tid; ++ mv_pp2x_prs_hw_read(hw, &pe); ++ ++ /* Read mac addr from entry */ ++ for (index = 0; index < ETH_ALEN; index++) ++ mv_pp2x_prs_tcam_data_byte_get(&pe, index, &da[index], ++ &da_mask[index]); ++ switch (l2_cast) { ++ case MVPP2_PRS_MAC_UC: ++ /* Do not delete M2M entry */ ++ if (is_unicast_ether_addr(da) && ++ !ether_addr_equal(da, port->dev->dev_addr)) { ++ if (op == MVPP2_DEL_MAC_NOT_IN_LIST && ++ mv_pp2x_mac_in_uc_list(port->dev, da)) ++ continue; ++ /* Delete this entry */ ++ mv_pp2x_prs_mac_da_accept(port, da, false); ++ } ++ break; ++ case MVPP2_PRS_MAC_MC: ++ if (is_multicast_ether_addr(da) && ++ !is_broadcast_ether_addr(da)) { ++ if (op == MVPP2_DEL_MAC_NOT_IN_LIST && ++ mv_pp2x_mac_in_mc_list(port->dev, da)) ++ continue; ++ /* Delete this entry */ ++ mv_pp2x_prs_mac_da_accept(port, da, false); ++ } ++ break; ++ case MVPP2_PRS_MAC_BC: ++ if (is_broadcast_ether_addr(da)) ++ /* Delete this entry */ ++ mv_pp2x_prs_mac_da_accept(port, da, false); ++ break; ++ } ++ } ++} ++ ++/* Write parser entry for default VID filtering */ ++static int mv_pp2x_prs_vid_drop_entry_accept(struct net_device *dev, ++ unsigned int tid, ++ unsigned int shift, ++ bool add) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ unsigned int pmap; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ pe->index = tid; ++ ++ if (add) { ++ if (hw->prs_shadow[tid].valid) { ++ pr_info("Default vid drop entry already in place\n"); ++ return 0; ++ } ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VID); ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(pe, 0); ++ } else { ++ mv_pp2x_prs_hw_read(hw, pe); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(pe, port->id, add); ++ ++ /* Invalidate the entry if no ports are left enabled */ ++ pmap = mv_pp2x_prs_tcam_port_map_get(pe); ++ if (pmap == 0) { ++ if (add) ++ return -EPERM; ++ mv_pp2x_prs_hw_inv(hw, pe->index); ++ hw->prs_shadow[pe->index].valid = false; ++ return 0; ++ } ++ ++ /* Continue - set next lookup */ ++ mv_pp2x_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_L2); ++ ++ /* Skip VLAN header - Set offset to 4 or 8 bytes */ ++ mv_pp2x_prs_sram_shift_set(pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ mv_pp2x_prs_sram_ri_update(pe, MVPP2_PRS_RI_DROP_MASK, MVPP2_PRS_RI_DROP_MASK); ++ ++ /* Clear all ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_VID); ++ mv_pp2x_prs_hw_write(hw, pe); ++ return 0; ++} ++ ++/* Return first free tcam index, seeking from start to end */ ++static bool mv_pp2x_prs_tcam_vid_empty(struct mv_pp2x_hw *hw, unsigned char start, unsigned char end) ++{ ++ int tid; ++ ++ if (start > end) ++ swap(start, end); ++ ++ for (tid = start; tid <= end; tid++) { ++ if (hw->prs_shadow[tid].valid) ++ return false; ++ } ++ return true; ++} ++ ++/* Find tcam entry with matched pair */ ++static struct mv_pp2x_prs_entry * ++mv_pp2x_prs_vid_range_find(struct mv_pp2x_hw *hw, int pmap, u16 vid, u16 mask, ++ struct mv_pp2x_prs_entry *pe) ++{ ++ unsigned char byte[2], enable[2]; ++ u16 rvid, rmask; ++ int tid; ++ ++ memset(pe, 0, sizeof(*pe)); ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VID); ++ ++ /* Go through the all entires with MVPP2_PRS_LU_VID */ ++ for (tid = MVPP2_PE_VID_FILT_RANGE_START; ++ tid <= MVPP2_PE_VID_FILT_RANGE_END; tid++) { ++ if (!hw->prs_shadow[tid].valid || ++ (hw->prs_shadow[tid].lu != MVPP2_PRS_LU_VID)) ++ continue; ++ ++ pe->index = tid; ++ mv_pp2x_prs_hw_read(hw, pe); ++ mv_pp2x_prs_tcam_data_byte_get(pe, 2, &byte[0], &enable[0]); ++ mv_pp2x_prs_tcam_data_byte_get(pe, 3, &byte[1], &enable[1]); ++ rvid = ((byte[0] & MVPP2_PRS_VID_H_WORD_MASK) << MVPP2_PRS_VID_H_WORD_SHIFT) + byte[1]; ++ rmask = ((enable[0] & MVPP2_PRS_VID_H_WORD_MASK) << MVPP2_PRS_VID_H_WORD_SHIFT) + enable[1]; ++ ++ if ((rvid != vid) || (rmask != mask)) ++ continue; ++ ++ return pe; ++ } ++ return NULL; ++} ++ ++/* Write parser entry for VID filtering */ ++int mv_pp2x_prs_vid_entry_accept(struct net_device *dev, u16 proto, u16 vid, bool add) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ int tid; ++ int rc; ++ bool empty = false; ++ unsigned int pmap; ++ unsigned int mask = 0xfff; ++ unsigned int reg_val, shift; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ unsigned int vid_start = MVPP2_PE_VID_FILT_RANGE_START + port->id * MVPP2_PRS_VLAN_FILT_MAX; ++ ++ /* Scan TCAM and see if entry with this already exist */ ++ pe = mv_pp2x_prs_vid_range_find(hw, (1 << port->id), vid, mask, &l_pe); ++ ++ if (vid == 0) ++ /*no need to add vid 0 to HW*/ ++ return 0; ++ ++ /* Check configured start header */ ++ reg_val = mv_pp2x_read(hw, MVPP2_MH_REG(port->id)); ++ if (reg_val & MVPP2_DSA_EXTENDED) ++ shift = MVPP2_VLAN_TAG_EDSA_LEN; ++ else ++ shift = MVPP2_VLAN_TAG_LEN; ++ ++ /* No such entry */ ++ if (!pe) { ++ if (!add) ++ return 0; ++ ++ empty = mv_pp2x_prs_tcam_vid_empty(hw, vid_start, vid_start + MVPP2_PRS_VLAN_FILT_MAX_ENTRY); ++ if (empty) { ++ rc = mv_pp2x_prs_vid_drop_entry_accept(dev, ++ vid_start + MVPP2_PRS_VLAN_FILT_DFLT_ENTRY, ++ shift, true); ++ if (rc) { ++ netdev_err(dev, "failed to add default vid entry for non-match vlan packets (drop)\n"); ++ return rc; ++ } ++ } ++ ++ /* Create new TCAM entry */ ++ /* Go through all entries from first to last in vlan range */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, vid_start, vid_start + MVPP2_PRS_VLAN_FILT_MAX_ENTRY); ++ ++ if (tid < 0) ++ return tid; ++ ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VID); ++ pe->index = tid; ++ ++ /* Mask all ports */ ++ mv_pp2x_prs_tcam_port_map_set(pe, 0); ++ } ++ ++ /* Update port mask */ ++ mv_pp2x_prs_tcam_port_set(pe, port->id, add); ++ ++ /* Invalidate the entry if no ports are left enabled */ ++ pmap = mv_pp2x_prs_tcam_port_map_get(pe); ++ if (pmap == 0) { ++ if (add) ++ return -EPERM; ++ mv_pp2x_prs_hw_inv(hw, pe->index); ++ hw->prs_shadow[pe->index].valid = false; ++ empty = mv_pp2x_prs_tcam_vid_empty(hw, vid_start, vid_start + MVPP2_PRS_VLAN_FILT_MAX_ENTRY); ++ if (empty) { ++ rc = mv_pp2x_prs_vid_drop_entry_accept(dev, ++ vid_start + MVPP2_PRS_VLAN_FILT_DFLT_ENTRY, ++ shift, false); ++ if (rc) { ++ netdev_err(dev, "failed to remove default vid for non-match vlan packets (drop)\n"); ++ return rc; ++ } ++ } ++ return 0; ++ } ++ ++ /* Continue - set next lookup */ ++ mv_pp2x_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_L2); ++ ++ /* Skip VLAN header - Set offset to 4 or 8 bytes */ ++ mv_pp2x_prs_sram_shift_set(pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ ++ /* Set match on VID */ ++ mv_pp2x_prs_match_vid(pe, MVPP2_PRS_VID_TCAM_BYTE, vid); ++ ++ /* Clear all ai bits for next iteration */ ++ mv_pp2x_prs_sram_ai_update(pe, 0, MVPP2_PRS_SRAM_AI_MASK); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_VID); ++ mv_pp2x_prs_hw_write(hw, pe); ++ ++ return 0; ++} ++ ++int mv_pp2x_prs_tag_mode_set(struct mv_pp2x_hw *hw, int port, int type) ++{ ++ switch (type) { ++ case MVPP2_TAG_TYPE_EDSA: ++ /* Add port to EDSA entries */ ++ mv_pp2x_prs_dsa_tag_set(hw, port, true, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, true, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); ++ /* Remove port from DSA entries */ ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); ++ break; ++ ++ case MVPP2_TAG_TYPE_DSA: ++ /* Add port to DSA entries */ ++ mv_pp2x_prs_dsa_tag_set(hw, port, true, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, true, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); ++ /* Remove port from EDSA entries */ ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); ++ break; ++ ++ case MVPP2_TAG_TYPE_MH: ++ case MVPP2_TAG_TYPE_NONE: ++ /* Remove port form EDSA and DSA entries */ ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); ++ mv_pp2x_prs_dsa_tag_set(hw, port, false, ++ MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); ++ break; ++ ++ default: ++ if ((type < 0) || (type > MVPP2_TAG_TYPE_EDSA)) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* Set prs flow for the port */ ++int mv_pp2x_prs_def_flow(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int tid; ++ ++ pe = mv_pp2x_prs_flow_find(hw, port->id, 0, 0, &l_pe); ++ ++ /* Such entry not exist */ ++ if (!pe) { ++ /* Go through the all entires from last to first */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, ++ MVPP2_PE_LAST_FREE_TID, ++ MVPP2_PE_FIRST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); ++ pe->index = tid; ++ ++ /* Set flow ID*/ ++ mv_pp2x_prs_sram_ai_update(pe, port->id, ++ MVPP2_PRS_FLOW_ID_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_FLOWS); ++ } ++ ++ mv_pp2x_prs_tcam_port_map_set(pe, (1 << port->id)); ++ mv_pp2x_prs_hw_write(hw, pe); ++ return 0; ++} ++ ++/* Set prs dedicated flow for the port */ ++int mv_pp2x_prs_flow_id_gen(struct mv_pp2x_port *port, u32 flow_id, ++ u32 res, u32 res_mask) ++{ ++ struct mv_pp2x_prs_entry *pe; ++ struct mv_pp2x_prs_entry l_pe; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int tid; ++ unsigned int pmap = 0; ++ ++ pe = mv_pp2x_prs_flow_find(hw, flow_id, res, res_mask, &l_pe); ++ ++ /* Such entry not exist */ ++ if (!pe) { ++ pe = &l_pe; ++ memset(pe, 0, sizeof(*pe)); ++ ++ /* Go through the all entires from last to first */ ++ tid = mv_pp2x_prs_tcam_first_free(hw, ++ MVPP2_PE_LAST_FREE_TID, ++ MVPP2_PE_FIRST_FREE_TID); ++ if (tid < 0) ++ return tid; ++ ++ mv_pp2x_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); ++ pe->index = tid; ++ ++ mv_pp2x_prs_sram_ai_update(pe, flow_id, MVPP2_PRS_FLOW_ID_MASK); ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); ++ ++ /* Update shadow table */ ++ mv_pp2x_prs_shadow_set(hw, pe->index, MVPP2_PRS_LU_FLOWS); ++ ++ /*update result data and mask*/ ++ mv_pp2x_prs_tcam_data_dword_set(pe, 0, res, res_mask); ++ } else { ++ pmap = mv_pp2x_prs_tcam_port_map_get(pe); ++ } ++ ++ mv_pp2x_prs_tcam_port_map_set(pe, (1 << port->id) | pmap); ++ mv_pp2x_prs_hw_write(hw, pe); ++ ++ return 0; ++} ++ ++int mv_pp2x_prs_flow_set(struct mv_pp2x_port *port) ++{ ++ int index, ret; ++ ++ for (index = 0; index < MVPP2_PRS_FL_TCAM_NUM; index++) { ++ ret = mv_pp2x_prs_flow_id_gen(port, ++ mv_pp2x_prs_flow_id_array[index].flow_id, ++ mv_pp2x_prs_flow_id_array[index].prs_result.ri, ++ mv_pp2x_prs_flow_id_array[index].prs_result.ri_mask); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static void mv_pp2x_prs_flow_id_attr_set(int flow_id, int ri, int ri_mask) ++{ ++ int flow_attr = 0; ++ ++ flow_attr |= MVPP2_PRS_FL_ATTR_VLAN_BIT; ++ if (ri_mask & MVPP2_PRS_RI_VLAN_MASK && ++ (ri & MVPP2_PRS_RI_VLAN_MASK) == MVPP2_PRS_RI_VLAN_NONE) ++ flow_attr &= ~MVPP2_PRS_FL_ATTR_VLAN_BIT; ++ ++ if ((ri & MVPP2_PRS_RI_L3_PROTO_MASK) == MVPP2_PRS_RI_L3_IP4 || ++ (ri & MVPP2_PRS_RI_L3_PROTO_MASK) == MVPP2_PRS_RI_L3_IP4_OPT || ++ (ri & MVPP2_PRS_RI_L3_PROTO_MASK) == MVPP2_PRS_RI_L3_IP4_OTHER) ++ flow_attr |= MVPP2_PRS_FL_ATTR_IP4_BIT; ++ ++ if ((ri & MVPP2_PRS_RI_L3_PROTO_MASK) == MVPP2_PRS_RI_L3_IP6 || ++ (ri & MVPP2_PRS_RI_L3_PROTO_MASK) == MVPP2_PRS_RI_L3_IP6_EXT) ++ flow_attr |= MVPP2_PRS_FL_ATTR_IP6_BIT; ++ ++ if ((ri & MVPP2_PRS_RI_L3_PROTO_MASK) == MVPP2_PRS_RI_L3_ARP) ++ flow_attr |= MVPP2_PRS_FL_ATTR_ARP_BIT; ++ ++ if (ri & MVPP2_PRS_RI_IP_FRAG_MASK) ++ flow_attr |= MVPP2_PRS_FL_ATTR_FRAG_BIT; ++ ++ if ((ri & MVPP2_PRS_RI_L4_PROTO_MASK) == MVPP2_PRS_RI_L4_TCP) ++ flow_attr |= MVPP2_PRS_FL_ATTR_TCP_BIT; ++ ++ if ((ri & MVPP2_PRS_RI_L4_PROTO_MASK) == MVPP2_PRS_RI_L4_UDP) ++ flow_attr |= MVPP2_PRS_FL_ATTR_UDP_BIT; ++ ++ mv_pp2x_prs_flow_id_attr_tbl[flow_id] = flow_attr; ++} ++ ++/* Init lookup id attribute array */ ++void mv_pp2x_prs_flow_id_attr_init(void) ++{ ++ int index; ++ u32 ri, ri_mask, flow_id; ++ ++ for (index = 0; index < MVPP2_PRS_FL_TCAM_NUM; index++) { ++ ri = mv_pp2x_prs_flow_id_array[index].prs_result.ri; ++ ri_mask = mv_pp2x_prs_flow_id_array[index].prs_result.ri_mask; ++ flow_id = mv_pp2x_prs_flow_id_array[index].flow_id; ++ ++ mv_pp2x_prs_flow_id_attr_set(flow_id, ri, ri_mask); ++ } ++} ++ ++int mv_pp2x_prs_flow_id_attr_get(int flow_id) ++{ ++ return mv_pp2x_prs_flow_id_attr_tbl[flow_id]; ++} ++ ++/* Classifier configuration routines */ ++ ++/* Update classification flow table registers */ ++void mv_pp2x_cls_flow_write(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_flow_entry *fe) ++{ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_INDEX_REG, fe->index); ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_TBL0_REG, fe->data[0]); ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_TBL1_REG, fe->data[1]); ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_TBL2_REG, fe->data[2]); ++} ++EXPORT_SYMBOL(mv_pp2x_cls_flow_write); ++ ++void mv_pp2x_cls_flow_read(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_flow_entry *fe) ++{ ++ fe->index = index; ++ /*write index*/ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_INDEX_REG, index); ++ ++ fe->data[0] = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL0_REG); ++ fe->data[1] = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL1_REG); ++ fe->data[2] = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL2_REG); ++} ++EXPORT_SYMBOL(mv_pp2x_cls_flow_read); ++ ++/* Update classification lookup table register */ ++static void mv_pp2x_cls_lookup_write(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_lookup_entry *le) ++{ ++ u32 val; ++ ++ val = (le->way << MVPP2_CLS_LKP_INDEX_WAY_OFFS) | le->lkpid; ++ mv_pp2x_write(hw, MVPP2_CLS_LKP_INDEX_REG, val); ++ mv_pp2x_write(hw, MVPP2_CLS_LKP_TBL_REG, le->data); ++} ++ ++void mv_pp2x_cls_lookup_read(struct mv_pp2x_hw *hw, int lkpid, int way, ++ struct mv_pp2x_cls_lookup_entry *le) ++{ ++ unsigned int val = 0; ++ ++ /* write index reg */ ++ val = (way << MVPP2_CLS_LKP_INDEX_WAY_OFFS) | lkpid; ++ mv_pp2x_write(hw, MVPP2_CLS_LKP_INDEX_REG, val); ++ le->way = way; ++ le->lkpid = lkpid; ++ le->data = mv_pp2x_read(hw, MVPP2_CLS_LKP_TBL_REG); ++} ++ ++/* Operations on flow entry */ ++int mv_pp2x_cls_sw_flow_hek_num_set(struct mv_pp2x_cls_flow_entry *fe, ++ int num_of_fields) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(num_of_fields, 0, ++ MVPP2_CLS_FLOWS_TBL_FIELDS_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[1] &= ~MVPP2_FLOW_FIELDS_NUM_MASK; ++ fe->data[1] |= (num_of_fields << MVPP2_FLOW_FIELDS_NUM); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_hek_num_set); ++ ++int mv_pp2x_cls_sw_flow_hek_set(struct mv_pp2x_cls_flow_entry *fe, ++ int field_index, int field_id) ++{ ++ int num_of_fields; ++ ++ /* get current num_of_fields */ ++ num_of_fields = ((fe->data[1] & ++ MVPP2_FLOW_FIELDS_NUM_MASK) >> MVPP2_FLOW_FIELDS_NUM); ++ ++ if (num_of_fields < (field_index + 1)) { ++ pr_debug("%s:num of heks=%d ,idx(%d) out of range\n", ++ __func__, num_of_fields, field_index); ++ return -1; ++ } ++ ++ fe->data[2] &= ~MVPP2_FLOW_FIELD_MASK(field_index); ++ fe->data[2] |= (field_id << MVPP2_FLOW_FIELD_ID(field_index)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_hek_set); ++ ++static void mv_pp2x_cls_sw_flow_eng_set(struct mv_pp2x_cls_flow_entry *fe, ++ int engine, int is_last) ++{ ++ fe->data[0] &= ~MVPP2_FLOW_LAST_MASK; ++ fe->data[0] &= ~MVPP2_FLOW_ENGINE_MASK; ++ ++ fe->data[0] |= is_last; ++ fe->data[0] |= (engine << MVPP2_FLOW_ENGINE); ++ fe->data[0] |= MVPP2_FLOW_PORT_ID_SEL_MASK; ++} ++ ++/* To init flow table according to different flow */ ++static void mv_pp2x_cls_flow_cos(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_flow_entry *fe, ++ int lkpid, int cos_type) ++{ ++ int hek_num, field_id, lkp_type, is_last; ++ int entry_idx = hw->cls_shadow->flow_free_start; ++ ++ switch (cos_type) { ++ case MVPP2_COS_TYPE_VLAN: ++ lkp_type = MVPP2_CLS_LKP_VLAN_PRI; ++ break; ++ case MVPP2_COS_TYPE_DSCP: ++ lkp_type = MVPP2_CLS_LKP_DSCP_PRI; ++ break; ++ default: ++ lkp_type = MVPP2_CLS_LKP_DEFAULT; ++ break; ++ } ++ hek_num = 0; ++ if ((lkpid == MVPP2_PRS_FL_NON_IP_UNTAG && ++ cos_type == MVPP2_COS_TYPE_DEF) || ++ (lkpid == MVPP2_PRS_FL_NON_IP_TAG && ++ cos_type == MVPP2_COS_TYPE_VLAN)) ++ is_last = 1; ++ else ++ is_last = 0; ++ ++ /* Set SW */ ++ memset(fe, 0, sizeof(struct mv_pp2x_cls_flow_entry)); ++ mv_pp2x_cls_sw_flow_hek_num_set(fe, hek_num); ++ if (hek_num) ++ mv_pp2x_cls_sw_flow_hek_set(fe, 0, field_id); ++ mv_pp2x_cls_sw_flow_eng_set(fe, MVPP2_CLS_ENGINE_C2, is_last); ++ mv_pp2x_cls_sw_flow_extra_set(fe, lkp_type, MVPP2_CLS_FL_COS_PRI); ++ fe->index = entry_idx; ++ ++ /* Write HW */ ++ mv_pp2x_cls_flow_write(hw, fe); ++ ++ /* Update Shadow */ ++ if (cos_type == MVPP2_COS_TYPE_DEF) ++ hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_dflt = entry_idx; ++ else if (cos_type == MVPP2_COS_TYPE_VLAN) ++ hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_vlan = entry_idx; ++ else ++ hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_dscp = entry_idx; ++ ++ /* Update first available flow entry */ ++ hw->cls_shadow->flow_free_start++; ++} ++ ++/* Init flow entry for RSS hash in PP22 */ ++static void mv_pp2x_cls_flow_rss_hash(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_flow_entry *fe, ++ int lkpid, int rss_mode) ++{ ++ int field_id[4] = {0}; ++ int entry_idx = hw->cls_shadow->flow_free_start; ++ int lkpid_attr = mv_pp2x_prs_flow_id_attr_get(lkpid); ++ ++ /* IP4 packet */ ++ if (lkpid_attr & MVPP2_PRS_FL_ATTR_IP4_BIT) { ++ field_id[0] = MVPP2_CLS_FIELD_IP4SA; ++ field_id[1] = MVPP2_CLS_FIELD_IP4DA; ++ } else if (lkpid_attr & MVPP2_PRS_FL_ATTR_IP6_BIT) { ++ field_id[0] = MVPP2_CLS_FIELD_IP6SA; ++ field_id[1] = MVPP2_CLS_FIELD_IP6DA; ++ } ++ /* L4 port */ ++ field_id[2] = MVPP2_CLS_FIELD_L4SIP; ++ field_id[3] = MVPP2_CLS_FIELD_L4DIP; ++ ++ /* Set SW */ ++ memset(fe, 0, sizeof(struct mv_pp2x_cls_flow_entry)); ++ if (rss_mode == MVPP2_RSS_HASH_2T) { ++ mv_pp2x_cls_sw_flow_hek_num_set(fe, 2); ++ mv_pp2x_cls_sw_flow_eng_set(fe, MVPP2_CLS_ENGINE_C3HA, 1); ++ mv_pp2x_cls_sw_flow_hek_set(fe, 0, field_id[0]); ++ mv_pp2x_cls_sw_flow_hek_set(fe, 1, field_id[1]); ++ } else { ++ mv_pp2x_cls_sw_flow_hek_num_set(fe, 4); ++ mv_pp2x_cls_sw_flow_hek_set(fe, 0, field_id[0]); ++ mv_pp2x_cls_sw_flow_hek_set(fe, 1, field_id[1]); ++ mv_pp2x_cls_sw_flow_hek_set(fe, 2, field_id[2]); ++ mv_pp2x_cls_sw_flow_hek_set(fe, 3, field_id[3]); ++ mv_pp2x_cls_sw_flow_eng_set(fe, MVPP2_CLS_ENGINE_C3HB, 1); ++ } ++ mv_pp2x_cls_sw_flow_extra_set(fe, ++ MVPP2_CLS_LKP_HASH, MVPP2_CLS_FL_RSS_PRI); ++ fe->index = entry_idx; ++ ++ /* Update last for TCP & UDP NF flow */ ++ if (((lkpid_attr & (MVPP2_PRS_FL_ATTR_TCP_BIT | MVPP2_PRS_FL_ATTR_UDP_BIT)) && ++ !(lkpid_attr & MVPP2_PRS_FL_ATTR_FRAG_BIT))) { ++ if (!hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_rss1) { ++ if (rss_mode == MVPP2_RSS_HASH_2T) ++ mv_pp2x_cls_sw_flow_eng_set(fe, ++ MVPP2_CLS_ENGINE_C3HA, 0); ++ else ++ mv_pp2x_cls_sw_flow_eng_set(fe, ++ MVPP2_CLS_ENGINE_C3HB, 0); ++ } ++ } ++ ++ /* Write HW */ ++ mv_pp2x_cls_flow_write(hw, fe); ++ ++ /* Update Shadow */ ++ if (hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_rss1 == 0) ++ hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_rss1 = entry_idx; ++ else ++ hw->cls_shadow->flow_info[lkpid - ++ MVPP2_PRS_FL_START].flow_entry_rss2 = entry_idx; ++ ++ /* Update first available flow entry */ ++ hw->cls_shadow->flow_free_start++; ++} ++ ++/* Init cls flow table according to different flow id */ ++void mv_pp2x_cls_flow_tbl_config(struct mv_pp2x_hw *hw) ++{ ++ int lkpid, rss_mode, lkpid_attr; ++ struct mv_pp2x_cls_flow_entry fe; ++ ++ for (lkpid = MVPP2_PRS_FL_START; lkpid < MVPP2_PRS_FL_LAST; lkpid++) { ++ /* Get lookup id attribute */ ++ lkpid_attr = mv_pp2x_prs_flow_id_attr_get(lkpid); ++ /* Default rss hash is based on 5T */ ++ rss_mode = MVPP2_RSS_HASH_5T; ++ /* For frag packets or non-TCP&UDP, rss must be based on 2T */ ++ if ((lkpid_attr & MVPP2_PRS_FL_ATTR_FRAG_BIT) || ++ !(lkpid_attr & (MVPP2_PRS_FL_ATTR_TCP_BIT | ++ MVPP2_PRS_FL_ATTR_UDP_BIT))) ++ rss_mode = MVPP2_RSS_HASH_2T; ++ ++ /* For untagged IP packets, only need default ++ * rule and dscp rule ++ */ ++ if ((lkpid_attr & (MVPP2_PRS_FL_ATTR_IP4_BIT | ++ MVPP2_PRS_FL_ATTR_IP6_BIT)) && ++ (!(lkpid_attr & MVPP2_PRS_FL_ATTR_VLAN_BIT))) { ++ /* Default rule */ ++ mv_pp2x_cls_flow_cos(hw, &fe, lkpid, ++ MVPP2_COS_TYPE_DEF); ++ /* DSCP rule */ ++ mv_pp2x_cls_flow_cos(hw, &fe, lkpid, ++ MVPP2_COS_TYPE_DSCP); ++ /* RSS hash rule */ ++ if ((!(lkpid_attr & MVPP2_PRS_FL_ATTR_FRAG_BIT)) && ++ (lkpid_attr & (MVPP2_PRS_FL_ATTR_TCP_BIT | ++ MVPP2_PRS_FL_ATTR_UDP_BIT))) { ++ /* RSS hash rules for TCP & UDP rss mode update */ ++ mv_pp2x_cls_flow_rss_hash(hw, &fe, lkpid, ++ MVPP2_RSS_HASH_2T); ++ mv_pp2x_cls_flow_rss_hash(hw, &fe, lkpid, ++ MVPP2_RSS_HASH_5T); ++ } else { ++ mv_pp2x_cls_flow_rss_hash(hw, &fe, lkpid, ++ rss_mode); ++ } ++ } ++ ++ /* For tagged IP packets, only need vlan rule and dscp rule */ ++ if ((lkpid_attr & (MVPP2_PRS_FL_ATTR_IP4_BIT | ++ MVPP2_PRS_FL_ATTR_IP6_BIT)) && ++ (lkpid_attr & MVPP2_PRS_FL_ATTR_VLAN_BIT)) { ++ /* VLAN rule */ ++ mv_pp2x_cls_flow_cos(hw, &fe, lkpid, ++ MVPP2_COS_TYPE_VLAN); ++ /* DSCP rule */ ++ mv_pp2x_cls_flow_cos(hw, &fe, lkpid, ++ MVPP2_COS_TYPE_DSCP); ++ /* RSS hash rule */ ++ if ((!(lkpid_attr & MVPP2_PRS_FL_ATTR_FRAG_BIT)) && ++ (lkpid_attr & (MVPP2_PRS_FL_ATTR_TCP_BIT | ++ MVPP2_PRS_FL_ATTR_UDP_BIT))) { ++ /* RSS hash rules for TCP & UDP rss mode update */ ++ mv_pp2x_cls_flow_rss_hash(hw, &fe, lkpid, ++ MVPP2_RSS_HASH_2T); ++ mv_pp2x_cls_flow_rss_hash(hw, &fe, lkpid, ++ MVPP2_RSS_HASH_5T); ++ } else { ++ mv_pp2x_cls_flow_rss_hash(hw, &fe, lkpid, ++ rss_mode); ++ } ++ } ++ ++ /* For non-IP packets, only need default rule if untagged, ++ * vlan rule also needed if tagged ++ */ ++ if (!(lkpid_attr & (MVPP2_PRS_FL_ATTR_IP4_BIT | ++ MVPP2_PRS_FL_ATTR_IP6_BIT))) { ++ /* Default rule */ ++ mv_pp2x_cls_flow_cos(hw, &fe, lkpid, ++ MVPP2_COS_TYPE_DEF); ++ /* VLAN rule if tagged */ ++ if (lkpid_attr & MVPP2_PRS_FL_ATTR_VLAN_BIT) ++ mv_pp2x_cls_flow_cos(hw, &fe, lkpid, ++ MVPP2_COS_TYPE_VLAN); ++ } ++ } ++} ++ ++/* Update the flow index for flow of lkpid */ ++void mv_pp2x_cls_lkp_flow_set(struct mv_pp2x_hw *hw, int lkpid, int way, ++ int flow_idx) ++{ ++ struct mv_pp2x_cls_lookup_entry le; ++ ++ mv_pp2x_cls_lookup_read(hw, lkpid, way, &le); ++ mv_pp2x_cls_sw_lkp_flow_set(&le, flow_idx); ++ mv_pp2x_cls_lookup_write(hw, &le); ++} ++ ++int mv_pp2x_cls_lkp_port_way_set(struct mv_pp2x_hw *hw, int port, int way) ++{ ++ unsigned int val; ++ ++ if (mv_pp2x_range_validate(port, 0, MVPP2_MAX_PORTS - 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(way, 0, ONE_BIT_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ val = mv_pp2x_read(hw, MVPP2_CLS_PORT_WAY_REG); ++ if (way == 1) ++ val |= MVPP2_CLS_PORT_WAY_MASK(port); ++ else ++ val &= ~MVPP2_CLS_PORT_WAY_MASK(port); ++ mv_pp2x_write(hw, MVPP2_CLS_PORT_WAY_REG, val); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_lkp_port_way_set); ++ ++int mv_pp2x_cls_hw_udf_set(struct mv_pp2x_hw *hw, int udf_no, int offs_id, ++ int offs_bits, int size_bits) ++{ ++ unsigned int reg_val; ++ ++ if (mv_pp2x_range_validate(offs_id, 0, ++ MVPP2_CLS_UDF_OFFSET_ID_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(offs_bits, 0, ++ MVPP2_CLS_UDF_REL_OFFSET_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(size_bits, 0, ++ MVPP2_CLS_UDF_SIZE_MASK) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(udf_no, 0, ++ MVPP2_CLS_UDF_REGS_NUM - 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS_UDF_REG(udf_no)); ++ reg_val &= ~MVPP2_CLS_UDF_OFFSET_ID_MASK; ++ reg_val &= ~MVPP2_CLS_UDF_REL_OFFSET_MASK; ++ reg_val &= ~MVPP2_CLS_UDF_SIZE_MASK; ++ ++ reg_val |= (offs_id << MVPP2_CLS_UDF_OFFSET_ID_OFFS); ++ reg_val |= (offs_bits << MVPP2_CLS_UDF_REL_OFFSET_OFFS); ++ reg_val |= (size_bits << MVPP2_CLS_UDF_SIZE_OFFS); ++ ++ mv_pp2x_write(hw, MVPP2_CLS_UDF_REG(udf_no), reg_val); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_hw_udf_set); ++ ++/* Init lookup decoding table with lookup id */ ++void mv_pp2x_cls_lookup_tbl_config(struct mv_pp2x_hw *hw) ++{ ++ int index, flow_idx; ++ int data[MVPP2_LKP_PTR_NUM]; ++ struct mv_pp2x_cls_lookup_entry le; ++ struct mv_pp2x_cls_flow_info *flow_info; ++ ++ memset(&le, 0, sizeof(struct mv_pp2x_cls_lookup_entry)); ++ /* Enable classifier engine */ ++ mv_pp2x_cls_sw_lkp_en_set(&le, 1); ++ ++ for (index = 0; index < (MVPP2_PRS_FL_LAST - MVPP2_PRS_FL_START); ++ index++) { ++ int i, j; ++ ++ flow_info = &hw->cls_shadow->flow_info[index]; ++ /* Init data[] as invalid value */ ++ for (i = 0; i < MVPP2_LKP_PTR_NUM; i++) ++ data[i] = MVPP2_FLOW_TBL_SIZE; ++ le.lkpid = hw->cls_shadow->flow_info[index].lkpid; ++ /* Find the min non-zero one in flow_entry_dflt, ++ * flow_entry_vlan, and flow_entry_dscp ++ */ ++ j = 0; ++ if (flow_info->flow_entry_dflt) ++ data[j++] = flow_info->flow_entry_dflt; ++ if (flow_info->flow_entry_vlan) ++ data[j++] = flow_info->flow_entry_vlan; ++ if (flow_info->flow_entry_dscp) ++ data[j++] = flow_info->flow_entry_dscp; ++ /* Get the lookup table entry pointer */ ++ flow_idx = data[0]; ++ for (i = 0; i < j; i++) { ++ if (flow_idx > data[i]) ++ flow_idx = data[i]; ++ } ++ ++ /* Set flow pointer index */ ++ mv_pp2x_cls_sw_lkp_flow_set(&le, flow_idx); ++ ++ /* Set initial rx queue */ ++ mv_pp2x_cls_sw_lkp_rxq_set(&le, 0x0); ++ ++ le.way = 0; ++ ++ /* Update lookup ID table entry */ ++ mv_pp2x_cls_lookup_write(hw, &le); ++ ++ le.way = 1; ++ ++ /* Update lookup ID table entry */ ++ mv_pp2x_cls_lookup_write(hw, &le); ++ } ++} ++ ++/* Classifier default initialization */ ++int mv_pp2x_cls_init(struct platform_device *pdev, struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_cls_lookup_entry le; ++ struct mv_pp2x_cls_flow_entry fe; ++ int index; ++ ++ /* Enable classifier */ ++ mv_pp2x_write(hw, MVPP2_CLS_MODE_REG, MVPP2_CLS_MODE_ACTIVE_MASK); ++ ++ /* Clear classifier flow table */ ++ memset(&fe.data, 0, sizeof(fe.data)); ++ for (index = 0; index < MVPP2_CLS_FLOWS_TBL_SIZE; index++) { ++ fe.index = index; ++ mv_pp2x_cls_flow_write(hw, &fe); ++ } ++ ++ /* Clear classifier lookup table */ ++ le.data = 0; ++ for (index = 0; index < MVPP2_CLS_LKP_TBL_SIZE; index++) { ++ le.lkpid = index; ++ le.way = 0; ++ mv_pp2x_cls_lookup_write(hw, &le); ++ ++ le.way = 1; ++ mv_pp2x_cls_lookup_write(hw, &le); ++ } ++ ++ hw->cls_shadow = devm_kcalloc(&pdev->dev, 1, ++ sizeof(struct mv_pp2x_cls_shadow), ++ GFP_KERNEL); ++ if (!hw->cls_shadow) ++ return -ENOMEM; ++ ++ hw->cls_shadow->flow_info = devm_kcalloc(&pdev->dev, ++ (MVPP2_PRS_FL_LAST - MVPP2_PRS_FL_START), ++ sizeof(struct mv_pp2x_cls_flow_info), ++ GFP_KERNEL); ++ if (!hw->cls_shadow->flow_info) ++ return -ENOMEM; ++ ++ /* Start from entry 1 to allocate flow table */ ++ hw->cls_shadow->flow_free_start = 1; ++ hw->cls_shadow->flow_swap_area = MVPP2_CLS_FLOWS_TBL_SIZE - ++ MVPP2_CLS_FLOWS_TBL_SWAP_SIZE; ++ ++ for (index = 0; index < (MVPP2_PRS_FL_LAST - MVPP2_PRS_FL_START); ++ index++) ++ hw->cls_shadow->flow_info[index].lkpid = index + ++ MVPP2_PRS_FL_START; ++ ++ /* Init flow table */ ++ mv_pp2x_cls_flow_tbl_config(hw); ++ ++ /* Init lookup table */ ++ mv_pp2x_cls_lookup_tbl_config(hw); ++ ++ return 0; ++} ++ ++void mv_pp2x_cls_port_config(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_cls_lookup_entry le; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ u32 val; ++ ++ /* Set way for the port */ ++ val = mv_pp2x_read(hw, MVPP2_CLS_PORT_WAY_REG); ++ val &= ~MVPP2_CLS_PORT_WAY_MASK(port->id); ++ mv_pp2x_write(hw, MVPP2_CLS_PORT_WAY_REG, val); ++ ++ /* Pick the entry to be accessed in lookup ID decoding table ++ * according to the way and lkpid. ++ */ ++ le.lkpid = port->id; ++ le.way = 0; ++ le.data = 0; ++ ++ /* Set initial CPU queue for receiving packets */ ++ le.data &= ~MVPP2_CLS_LKP_TBL_RXQ_MASK; ++ le.data |= port->first_rxq; ++ ++ /* Disable classification engines */ ++ le.data &= ~MVPP2_CLS_LKP_TBL_LOOKUP_EN_MASK; ++ ++ /* Update lookup ID table entry */ ++ mv_pp2x_cls_lookup_write(hw, &le); ++} ++ ++/* Set CPU queue number for oversize packets */ ++void mv_pp2x_cls_oversize_rxq_set(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ mv_pp2x_write(hw, MVPP2_CLS_OVERSIZE_RXQ_LOW_REG(port->id), ++ port->first_rxq & MVPP2_CLS_OVERSIZE_RXQ_LOW_MASK); ++} ++ ++void mv_pp21_get_mac_address(struct mv_pp2x_port *port, unsigned char *addr) ++{ ++ u32 mac_addr_l, mac_addr_m, mac_addr_h; ++ ++ mac_addr_l = readl(port->priv->hw.base + MVPP2_GMAC_CTRL_1_REG) + ++ (port->id << MVPP2_GMAC_SA_LOW_OFFS); ++ mac_addr_m = readl(port->priv->hw.lms_base + MVPP2_SRC_ADDR_MIDDLE); ++ mac_addr_h = readl(port->priv->hw.lms_base + MVPP2_SRC_ADDR_HIGH); ++ addr[0] = (mac_addr_h >> 24) & 0xFF; ++ addr[1] = (mac_addr_h >> 16) & 0xFF; ++ addr[2] = (mac_addr_h >> 8) & 0xFF; ++ addr[3] = mac_addr_h & 0xFF; ++ addr[4] = mac_addr_m & 0xFF; ++ addr[5] = (mac_addr_l >> MVPP2_GMAC_SA_LOW_OFFS) & 0xFF; ++} ++ ++void mv_pp2x_cause_error(struct net_device *dev, int cause) ++{ ++ if (cause & MVPP2_CAUSE_FCS_ERR_MASK) ++ netdev_err(dev, "FCS error\n"); ++ if (cause & MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK) ++ netdev_err(dev, "rx fifo overrun error\n"); ++ if (cause & MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK) ++ netdev_err(dev, "tx fifo underrun error\n"); ++} ++ ++/* Display more error info */ ++void mv_pp2x_rx_error(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_desc *rx_desc) ++{ ++ u32 status = rx_desc->status; ++ ++ switch (status & MVPP2_RXD_ERR_CODE_MASK) { ++ case MVPP2_RXD_ERR_CRC: ++ pr_err_ratelimited("netdev: %s bad rx status %08x (crc error), size=%d\n", ++ port->dev->name, status, rx_desc->data_size); ++ break; ++ case MVPP2_RXD_ERR_OVERRUN: ++ pr_err_ratelimited("netdev: %s bad rx status %08x (overrun error), size=%d\n", ++ port->dev->name, status, rx_desc->data_size); ++ break; ++ case MVPP2_RXD_ERR_RESOURCE: ++ pr_err_ratelimited("netdev: %s bad rx status %08x (resource error), size=%d\n", ++ port->dev->name, status, rx_desc->data_size); ++ break; ++ } ++} ++ ++/* Handle RX checksum offload */ ++void mv_pp2x_rx_csum(struct mv_pp2x_port *port, u32 status, ++ struct sk_buff *skb) ++{ ++ if (((status & MVPP2_RXD_L3_IP4) && ++ !(status & MVPP2_RXD_IP4_HEADER_ERR)) || ++ (status & MVPP2_RXD_L3_IP6)) ++ if (((status & MVPP2_RXD_L4_UDP) || ++ (status & MVPP2_RXD_L4_TCP)) && ++ (status & MVPP2_RXD_L4_CSUM_OK)) { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb->csum_level = 1; ++ return; ++ } ++ ++ skb->ip_summed = CHECKSUM_NONE; ++} ++ ++/* Set the number of packets that will be received before Rx interrupt ++ * will be generated by HW. ++ */ ++void mv_pp2x_rx_pkts_coal_set(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq) ++{ ++ if (rxq->pkts_coal > MVPP2_MAX_OCCUPIED_THRESH) ++ rxq->pkts_coal = MVPP2_MAX_OCCUPIED_THRESH; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_RXQ_NUM_REG, rxq->id); ++ mv_pp2x_write(&port->priv->hw, MVPP2_RXQ_THRESH_REG, rxq->pkts_coal); ++} ++ ++/* Set the time delay in usec before Rx interrupt */ ++void mv_pp2x_rx_time_coal_set(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq) ++{ ++ u32 val; ++ ++ val = (port->priv->hw.tclk / USEC_PER_SEC) * rxq->time_coal; ++ if (val > MVPP2_MAX_ISR_RX_THRESHOLD) { ++ rxq->time_coal = (MVPP2_MAX_ISR_RX_THRESHOLD * ++ USEC_PER_SEC) / port->priv->hw.tclk; ++ val = MVPP2_MAX_ISR_RX_THRESHOLD; ++ } ++ mv_pp2x_write(&port->priv->hw, ++ MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val); ++} ++ ++/* Set given value of threshold for TX_DONE pkts coalescing */ ++void mv_pp2x_tx_done_pkts_coal_set_val(struct mv_pp2x_port *port, int address_space, u32 val) ++{ ++ struct mv_pp2x_tx_queue *txq; ++ int queue; ++ ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ txq = port->txqs[queue]; ++ mv_pp2x_relaxed_write(&port->priv->hw, MVPP2_TXQ_NUM_REG, txq->id, address_space); ++ mv_pp2x_relaxed_write(&port->priv->hw, MVPP2_TXQ_THRESH_REG, val, address_space); ++ } ++} ++ ++/* Set pre-configured threshold for TX_DONE pkts coalescing */ ++void mv_pp2x_tx_done_pkts_coal_set(struct mv_pp2x_port *port, int address_space) ++{ ++ int queue; ++ u32 val; ++ ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ struct mv_pp2x_tx_queue *txq = port->txqs[queue]; ++ ++ if (txq->pkts_coal > MVPP2_MAX_TRANSMITTED_THRESH) ++ txq->pkts_coal = MVPP2_MAX_TRANSMITTED_THRESH; ++ val = (txq->pkts_coal << MVPP2_TRANSMITTED_THRESH_OFFSET) & ++ MVPP2_TRANSMITTED_THRESH_MASK; ++ ++ mv_pp2x_relaxed_write(&port->priv->hw, MVPP2_TXQ_NUM_REG, txq->id, address_space); ++ mv_pp2x_relaxed_write(&port->priv->hw, MVPP2_TXQ_THRESH_REG, val, address_space); ++ } ++} ++ ++/* Set the time delay in usec before Rx interrupt */ ++void mv_pp2x_tx_done_time_coal_set(struct mv_pp2x_port *port, u32 usec) ++{ ++ u32 val; ++ ++ val = (port->priv->hw.tclk / USEC_PER_SEC) * usec; ++ if (val > MVPP22_MAX_ISR_TX_THRESHOLD) ++ val = MVPP22_MAX_ISR_TX_THRESHOLD; ++ mv_pp2x_write(&port->priv->hw, ++ MVPP22_ISR_TX_THRESHOLD_REG(port->id), val); ++} ++ ++/* Change maximum receive size of the port */ ++void mv_pp21_gmac_max_rx_size_set(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); ++ val &= ~MVPP2_GMAC_MAX_RX_SIZE_MASK; ++ val |= (((port->pkt_size - MVPP2_MH_SIZE) / 2) << ++ MVPP2_GMAC_MAX_RX_SIZE_OFFS); ++ writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++} ++ ++/* Set max sizes for Tx queues */ ++void mv_pp2x_txp_max_tx_size_set(struct mv_pp2x_port *port) ++{ ++ u32 val, size, mtu; ++ int txq, tx_port_num; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ mtu = port->pkt_size * 8; ++ if (mtu > MVPP2_TXP_MTU_MAX) ++ mtu = MVPP2_TXP_MTU_MAX; ++ ++ /* WA for wrong Token bucket update: Set MTU value = 3*real MTU value */ ++ mtu = 3 * mtu; ++ ++ /* Indirect access to registers */ ++ tx_port_num = mv_pp2x_egress_port(port); ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); ++ ++ /* Set MTU */ ++ val = mv_pp2x_read(hw, MVPP2_TXP_SCHED_MTU_REG); ++ val &= ~MVPP2_TXP_MTU_MAX; ++ val |= mtu; ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_MTU_REG, val); ++ ++ /* TXP token size and all TXQs token size must be larger that MTU */ ++ val = mv_pp2x_read(hw, MVPP2_TXP_SCHED_TOKEN_SIZE_REG); ++ size = val & MVPP2_TXP_TOKEN_SIZE_MAX; ++ if (size < mtu) { ++ size = mtu; ++ val &= ~MVPP2_TXP_TOKEN_SIZE_MAX; ++ val |= size; ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); ++ } ++ ++ for (txq = 0; txq < port->num_tx_queues; txq++) { ++ val = mv_pp2x_read(hw, ++ MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq)); ++ size = val & MVPP2_TXQ_TOKEN_SIZE_MAX; ++ ++ if (size < mtu) { ++ size = mtu; ++ val &= ~MVPP2_TXQ_TOKEN_SIZE_MAX; ++ val |= size; ++ mv_pp2x_write(hw, ++ MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq), ++ val); ++ } ++ } ++} ++ ++/* Set Tx descriptors fields relevant for CSUM calculation */ ++u32 mv_pp2x_txq_desc_csum(int l3_offs, int l3_proto, ++ int ip_hdr_len, int l4_proto) ++{ ++ u32 command; ++ ++ /* fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk, ++ * G_L4_chk, L4_type required only for checksum calculation ++ */ ++ command = (l3_offs << MVPP2_TXD_L3_OFF_SHIFT); ++ command |= (ip_hdr_len << MVPP2_TXD_IP_HLEN_SHIFT); ++ command |= MVPP2_TXD_IP_CSUM_DISABLE; ++ ++ if (l3_proto == ETH_P_IP) { ++ command &= ~MVPP2_TXD_IP_CSUM_DISABLE; /* enable IPv4 csum */ ++ command &= ~MVPP2_TXD_L3_IP6; /* enable IPv4 */ ++ } else { ++ command |= MVPP2_TXD_L3_IP6; /* enable IPv6 */ ++ } ++ ++ if (l4_proto == IPPROTO_TCP) { ++ command &= ~MVPP2_TXD_L4_UDP; /* enable TCP */ ++ command &= ~MVPP2_TXD_L4_CSUM_FRAG; /* generate L4 csum */ ++ } else if (l4_proto == IPPROTO_UDP) { ++ command |= MVPP2_TXD_L4_UDP; /* enable UDP */ ++ command &= ~MVPP2_TXD_L4_CSUM_FRAG; /* generate L4 csum */ ++ } else { ++ command |= MVPP2_TXD_L4_CSUM_NOT; ++ } ++ ++ return command; ++} ++ ++/* Get number of sent descriptors and decrement counter. ++ * The number of sent descriptors is returned. ++ * Per-CPU access ++ */ ++ ++ /* Tx descriptors helper methods */ ++ ++/* Get number of Tx descriptors waiting to be transmitted by HW */ ++int mv_pp2x_txq_pend_desc_num_get(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_queue *txq) ++{ ++ u32 val; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ mv_pp2x_write(hw, MVPP2_TXQ_NUM_REG, txq->id); ++ val = mv_pp2x_read(hw, MVPP2_TXQ_PENDING_REG); ++ ++ return val & MVPP2_TXQ_PENDING_MASK; ++} ++ ++/* Get pointer to next Tx descriptor to be processed (send) by HW */ ++struct mv_pp2x_tx_desc *mv_pp2x_txq_next_desc_get( ++ struct mv_pp2x_aggr_tx_queue *aggr_txq) ++{ ++ int tx_desc = aggr_txq->next_desc_to_proc; ++ ++ aggr_txq->next_desc_to_proc = MVPP2_QUEUE_NEXT_DESC(aggr_txq, tx_desc); ++ return aggr_txq->first_desc + tx_desc; ++} ++ ++/* Get pointer to previous aggregated TX descriptor for rollback when needed */ ++struct mv_pp2x_tx_desc *mv_pp2x_txq_prev_desc_get( ++ struct mv_pp2x_aggr_tx_queue *aggr_txq) ++{ ++ int tx_desc = aggr_txq->next_desc_to_proc; ++ ++ if (tx_desc > 0) ++ aggr_txq->next_desc_to_proc = tx_desc - 1; ++ else ++ aggr_txq->next_desc_to_proc = aggr_txq->last_desc; ++ ++ return (aggr_txq->first_desc + tx_desc); ++} ++ ++int mv_pp2x_aggr_desc_num_read(struct mv_pp2x *priv, int cpu) ++{ ++ u32 val = mv_pp2x_read(&priv->hw, MVPP2_AGGR_TXQ_STATUS_REG(cpu)); ++ ++ return(val & MVPP2_AGGR_TXQ_PENDING_MASK); ++} ++EXPORT_SYMBOL(mv_pp2x_aggr_desc_num_read); ++ ++/* Check if there are enough free descriptors in aggregated txq. ++ * If not, update the number of occupied descriptors and repeat the check. ++ */ ++int mv_pp2x_aggr_desc_num_check(struct mv_pp2x *priv, ++ struct mv_pp2x_aggr_tx_queue *aggr_txq, ++ int num, int cpu) ++{ ++ if ((aggr_txq->sw_count + aggr_txq->hw_count + num) > aggr_txq->size) { ++ /* Update number of occupied aggregated Tx descriptors */ ++ u32 val; ++ ++ val = mv_pp2x_relaxed_read(&priv->hw, ++ MVPP2_AGGR_TXQ_STATUS_REG(cpu), cpu); ++ ++ aggr_txq->hw_count = val & MVPP2_AGGR_TXQ_PENDING_MASK; ++ ++ if ((aggr_txq->sw_count + aggr_txq->hw_count + num) > aggr_txq->size) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++/* Reserved Tx descriptors allocation request */ ++int mv_pp2x_txq_alloc_reserved_desc(struct mv_pp2x *priv, ++ struct mv_pp2x_tx_queue *txq, int num, int cpu) ++{ ++ u32 val; ++ ++ val = (txq->id << MVPP2_TXQ_RSVD_REQ_Q_OFFSET) | num; ++ mv_pp2x_relaxed_write(&priv->hw, MVPP2_TXQ_RSVD_REQ_REG, val, cpu); ++ ++ val = mv_pp2x_relaxed_read(&priv->hw, MVPP2_TXQ_RSVD_RSLT_REG, cpu); ++ ++ return val & MVPP2_TXQ_RSVD_RSLT_MASK; ++} ++ ++/* Set rx queue offset */ ++void mv_pp2x_rxq_offset_set(struct mv_pp2x_port *port, ++ int prxq, int offset) ++{ ++ u32 val; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ /* Convert offset from bytes to units of 32 bytes */ ++ offset = offset >> 5; ++ ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~MVPP2_RXQ_PACKET_OFFSET_MASK; ++ ++ /* Offset is in */ ++ val |= ((offset << MVPP2_RXQ_PACKET_OFFSET_OFFS) & ++ MVPP2_RXQ_PACKET_OFFSET_MASK); ++ ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(prxq), val); ++} ++ ++/* Port configuration routines */ ++ ++void mv_pp21_port_mii_set(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); ++ ++ switch (port->mac_data.phy_mode) { ++ case PHY_INTERFACE_MODE_SGMII: ++ val |= MVPP2_GMAC_INBAND_AN_MASK; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ val |= MVPP2_GMAC_PORT_RGMII_MASK; ++ default: ++ val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; ++ } ++ ++ writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); ++} ++ ++void mv_pp21_port_fc_adv_enable(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val |= MVPP2_GMAC_FC_ADV_EN; ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++} ++ ++void mv_pp21_port_enable(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); ++ val |= MVPP2_GMAC_PORT_EN_MASK; ++ val |= MVPP2_GMAC_MIB_CNTR_EN_MASK; ++ writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++} ++ ++void mv_pp21_port_disable(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); ++ val &= ~(MVPP2_GMAC_PORT_EN_MASK); ++ writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); ++} ++ ++/* Set IEEE 802.3x Flow Control Xon Packet Transmission Mode */ ++void mv_pp21_port_periodic_xon_disable(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_1_REG) & ++ ~MVPP2_GMAC_PERIODIC_XON_EN_MASK; ++ writel(val, port->base + MVPP2_GMAC_CTRL_1_REG); ++} ++ ++/* Configure loopback port */ ++void mv_pp21_port_loopback_set(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_1_REG); ++ ++ if (port->mac_data.speed == 1000) ++ val |= MVPP2_GMAC_GMII_LB_EN_MASK; ++ else ++ val &= ~MVPP2_GMAC_GMII_LB_EN_MASK; ++ ++ if (port->mac_data.phy_mode == PHY_INTERFACE_MODE_SGMII) ++ val |= MVPP2_GMAC_PCS_LB_EN_MASK; ++ else ++ val &= ~MVPP2_GMAC_PCS_LB_EN_MASK; ++ ++ writel(val, port->base + MVPP2_GMAC_CTRL_1_REG); ++} ++ ++void mv_pp21_port_reset(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) & ++ ~MVPP2_GMAC_PORT_RESET_MASK; ++ writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); ++ ++ while (readl(port->base + MVPP2_GMAC_CTRL_2_REG) & ++ MVPP2_GMAC_PORT_RESET_MASK) ++ continue; ++} ++ ++/* Refill BM pool */ ++void mv_pp2x_pool_refill(struct mv_pp2x *priv, u32 pool, ++ dma_addr_t phys_addr, int cpu) ++{ ++ unsigned long flags; ++ ++ if (priv->pp2_cfg.spinlocks_bitmap & MV_BM_LOCK) ++ spin_lock_irqsave(&priv->bm_spinlock[cpu], flags); ++ ++ mv_pp2x_bm_pool_put(&priv->hw, pool, phys_addr, cpu); ++ ++ if (priv->pp2_cfg.spinlocks_bitmap & MV_BM_LOCK) ++ spin_unlock_irqrestore(&priv->bm_spinlock[cpu], flags); ++} ++ ++void mv_pp2x_pool_refill_virtual(struct mv_pp2x *priv, u32 pool, ++ dma_addr_t phys_addr, u8 *cookie) ++{ ++ int cpu = smp_processor_id(); ++ ++ mv_pp2x_bm_pool_put_virtual(&priv->hw, pool, phys_addr, cookie, cpu); ++} ++ ++/* Set pool buffer size */ ++void mv_pp2x_bm_pool_bufsize_set(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_bm_pool *bm_pool, int buf_size) ++{ ++ u32 val; ++ ++ bm_pool->buf_size = buf_size; ++ ++ val = ALIGN(buf_size, 1 << MVPP2_POOL_BUF_SIZE_OFFSET); ++ mv_pp2x_write(hw, MVPP2_POOL_BUF_SIZE_REG(bm_pool->id), val); ++} ++ ++/* Attach long pool to rxq */ ++void mv_pp21_rxq_long_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int long_pool) ++{ ++ u32 val; ++ ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~MVPP21_RXQ_POOL_LONG_MASK; ++ val |= ((long_pool << MVPP21_RXQ_POOL_LONG_OFFS) & ++ MVPP21_RXQ_POOL_LONG_MASK); ++ ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(prxq), val); ++} ++ ++/* Attach short pool to rxq */ ++void mv_pp21_rxq_short_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int short_pool) ++{ ++ u32 val; ++ ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~MVPP21_RXQ_POOL_SHORT_MASK; ++ val |= ((short_pool << MVPP21_RXQ_POOL_SHORT_OFFS) & ++ MVPP21_RXQ_POOL_SHORT_MASK); ++ ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(prxq), val); ++} ++ ++/* Attach long pool to rxq */ ++void mv_pp22_rxq_long_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int long_pool) ++{ ++ u32 val; ++ ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~MVPP22_RXQ_POOL_LONG_MASK; ++ val |= ((long_pool << MVPP22_RXQ_POOL_LONG_OFFS) & ++ MVPP22_RXQ_POOL_LONG_MASK); ++ ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(prxq), val); ++} ++ ++/* Attach short pool to rxq */ ++void mv_pp22_rxq_short_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int short_pool) ++{ ++ u32 val; ++ ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(prxq)); ++ val &= ~MVPP22_RXQ_POOL_SHORT_MASK; ++ val |= ((short_pool << MVPP22_RXQ_POOL_SHORT_OFFS) & ++ MVPP22_RXQ_POOL_SHORT_MASK); ++ ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(prxq), val); ++} ++ ++/* Enable/disable receiving packets */ ++void mv_pp2x_ingress_enable(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ int lrxq, queue; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ for (lrxq = 0; lrxq < port->num_rx_queues; lrxq++) { ++ queue = port->rxqs[lrxq]->id; ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(queue)); ++ val &= ~MVPP2_RXQ_DISABLE_MASK; ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(queue), val); ++ } ++} ++ ++void mv_pp2x_ingress_disable(struct mv_pp2x_port *port) ++{ ++ u32 val; ++ int lrxq, queue; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ for (lrxq = 0; lrxq < port->num_rx_queues; lrxq++) { ++ queue = port->rxqs[lrxq]->id; ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(queue)); ++ val |= MVPP2_RXQ_DISABLE_MASK; ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(queue), val); ++ } ++} ++ ++void mv_pp2x_egress_enable(struct mv_pp2x_port *port) ++{ ++ u32 qmap; ++ int queue; ++ int tx_port_num = mv_pp2x_egress_port(port); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ /* Don't do nothing for MUSDK ports since MUSDK manages ++ * its own queues ++ */ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return; ++ ++ /* Enable all initialized TXs. */ ++ qmap = 0; ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ struct mv_pp2x_tx_queue *txq = port->txqs[queue]; ++ ++ if (txq->first_desc) ++ qmap |= (1 << queue); ++ } ++ ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_Q_CMD_REG, qmap); ++ ++ pr_debug("tx_port_num=%d qmap=0x%x\n", tx_port_num, qmap); ++} ++ ++/* Disable transmit via physical egress queue ++ * - HW doesn't take descriptors from DRAM ++ */ ++void mv_pp2x_egress_disable(struct mv_pp2x_port *port) ++{ ++ u32 reg_data; ++ int delay; ++ int tx_port_num = mv_pp2x_egress_port(port); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ /* Don't do nothing for MUSDK ports since MUSDK manages ++ * its own queues ++ */ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return; ++ ++ /* Issue stop command for active channels only */ ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); ++ reg_data = (mv_pp2x_read(hw, MVPP2_TXP_SCHED_Q_CMD_REG)) & ++ MVPP2_TXP_SCHED_ENQ_MASK; ++ if (reg_data != 0) ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_Q_CMD_REG, ++ (reg_data << MVPP2_TXP_SCHED_DISQ_OFFSET)); ++ ++ /* Wait for all Tx activity to terminate. */ ++ delay = 0; ++ do { ++ if (delay >= MVPP2_TX_DISABLE_TIMEOUT_MSEC) { ++ netdev_warn(port->dev, ++ "Tx stop timed out, status=0x%08x\n", ++ reg_data); ++ break; ++ } ++ mdelay(1); ++ delay++; ++ ++ /* Check port TX Command register that all ++ * Tx queues are stopped ++ */ ++ reg_data = mv_pp2x_read(hw, MVPP2_TXP_SCHED_Q_CMD_REG); ++ } while (reg_data & MVPP2_TXP_SCHED_ENQ_MASK); ++} ++ ++/* Parser default initialization */ ++int mv_pp2x_prs_default_init(struct platform_device *pdev, ++ struct mv_pp2x_hw *hw) ++{ ++ int err, index, i; ++ ++ /* Enable tcam table */ ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_CTRL_REG, MVPP2_PRS_TCAM_EN_MASK); ++ ++ /* Clear all tcam and sram entries */ ++ for (index = 0; index < MVPP2_PRS_TCAM_SRAM_SIZE; index++) { ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_IDX_REG, index); ++ for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) ++ mv_pp2x_write(hw, MVPP2_PRS_TCAM_DATA_REG(i), 0); ++ ++ mv_pp2x_write(hw, MVPP2_PRS_SRAM_IDX_REG, index); ++ for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) ++ mv_pp2x_write(hw, MVPP2_PRS_SRAM_DATA_REG(i), 0); ++ } ++ ++ /* Invalidate all tcam entries */ ++ for (index = 0; index < MVPP2_PRS_TCAM_SRAM_SIZE; index++) ++ mv_pp2x_prs_hw_inv(hw, index); ++ ++ hw->prs_shadow = devm_kcalloc(&pdev->dev, MVPP2_PRS_TCAM_SRAM_SIZE, ++ sizeof(struct mv_pp2x_prs_shadow), ++ GFP_KERNEL); ++ ++ if (!hw->prs_shadow) ++ return -ENOMEM; ++ ++ /* Always start from lookup = 0 */ ++ for (index = 0; index < MVPP2_MAX_PORTS; index++) ++ mv_pp2x_prs_hw_port_init(hw, index, MVPP2_PRS_LU_MH, ++ MVPP2_PRS_PORT_LU_MAX, 0); ++ ++ mv_pp2x_prs_def_flow_init(hw); ++ ++ mv_pp2x_prs_mh_init(hw); ++ ++ mv_pp2x_prs_mac_init(hw); ++ ++ mv_pp2x_prs_dsa_init(hw); ++ ++ err = mv_pp2x_prs_etype_init(hw); ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_vlan_init(pdev, hw); ++ if (err) ++ return err; ++ ++ mv_pp2x_prs_vid_init(hw); ++ ++ err = mv_pp2x_prs_pppoe_init(hw); ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_ip6_init(hw); ++ if (err) ++ return err; ++ ++ err = mv_pp2x_prs_ip4_init(hw); ++ if (err) ++ return err; ++ return 0; ++} ++ ++/* shift to (current offset + shift) */ ++int mv_pp2x_prs_sw_sram_shift_set(struct mv_pp2x_prs_entry *pe, ++ int shift, unsigned int op) ++{ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(shift, 0 - MVPP2_PRS_SRAM_SHIFT_MASK, ++ MVPP2_PRS_SRAM_SHIFT_MASK) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(op, 0, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK) == MV_ERROR) ++ return MV_ERROR; ++ ++ /* Set sign */ ++ if (shift < 0) { ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT)] |= ++ (1 << (MVPP2_PRS_SRAM_SHIFT_SIGN_BIT % 8)); ++ shift = 0 - shift; ++ } else ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT)] &= ++ (~(1 << (MVPP2_PRS_SRAM_SHIFT_SIGN_BIT % 8))); ++ ++ /* Set offset */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_OFFS)] = (unsigned char)shift; ++ ++ /* Reset and Set operation */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS)] &= ++ ~(MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK << ++ (MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS % 8)); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS)] |= ++ (op << (MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS % 8)); ++ ++ /* Set base offset as current */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS)] &= ++ (~(1 << (MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS % 8))); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_shift_set); ++ ++int mv_pp2x_prs_sw_sram_shift_get(struct mv_pp2x_prs_entry *pe, int *shift) ++{ ++ int sign; ++ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ if (mv_pp2x_ptr_validate(shift) == MV_ERROR) ++ return MV_ERROR; ++ ++ sign = pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT)] & ++ (1 << (MVPP2_PRS_SRAM_SHIFT_SIGN_BIT % 8)); ++ *shift = ((int)(pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_OFFS)])) & ++ MVPP2_PRS_SRAM_SHIFT_MASK; ++ ++ if (sign == 1) ++ *shift *= -1; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_shift_get); ++ ++int mv_pp2x_prs_sw_sram_offset_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int type, int offset, ++ unsigned int op) ++{ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(offset, 0 - MVPP2_PRS_SRAM_UDF_MASK, ++ MVPP2_PRS_SRAM_UDF_MASK) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(type, 0, ++ MVPP2_PRS_SRAM_UDF_TYPE_MASK) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(op, 0, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_MASK) == MV_ERROR) ++ return MV_ERROR; ++ ++ /* Set offset sign */ ++ if (offset < 0) { ++ offset = 0 - offset; ++ /* set sram offset sign bit */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT)] |= ++ (1 << (MVPP2_PRS_SRAM_SHIFT_SIGN_BIT % 8)); ++ } else ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_SHIFT_SIGN_BIT)] &= ++ (~(1 << (MVPP2_PRS_SRAM_SHIFT_SIGN_BIT % 8))); ++ ++ /* set offset value */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_OFFS)] &= ++ (~(MVPP2_PRS_SRAM_UDF_MASK << ++ (MVPP2_PRS_SRAM_UDF_OFFS % 8))); ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_OFFS)] |= ++ (offset << (MVPP2_PRS_SRAM_UDF_OFFS % 8)); ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_OFFS + MVPP2_PRS_SRAM_UDF_BITS)] &= ++ ~(MVPP2_PRS_SRAM_UDF_MASK >> ++ (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8))); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_OFFS + MVPP2_PRS_SRAM_UDF_BITS)] |= ++ (offset >> (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8))); ++ ++ /* set offset type */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_TYPE_OFFS)] &= ++ ~(MVPP2_PRS_SRAM_UDF_TYPE_MASK << ++ (MVPP2_PRS_SRAM_UDF_TYPE_OFFS % 8)); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_TYPE_OFFS)] |= ++ (type << (MVPP2_PRS_SRAM_UDF_TYPE_OFFS % 8)); ++ ++ /* Set offset operation */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS)] &= ++ ~(MVPP2_PRS_SRAM_OP_SEL_UDF_MASK << ++ (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8)); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS)] |= ++ (op << (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8)); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + ++ MVPP2_PRS_SRAM_OP_SEL_UDF_BITS)] &= ++ ~(MVPP2_PRS_SRAM_OP_SEL_UDF_MASK >> ++ (8 - (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8))); ++ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + ++ MVPP2_PRS_SRAM_OP_SEL_UDF_BITS)] |= ++ (op >> (8 - (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8))); ++ ++ /* Set base offset as current */ ++ pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS)] &= ++ (~(1 << (MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS % 8))); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_offset_set); ++ ++int mv_pp2x_prs_sw_sram_offset_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *type, int *offset, ++ unsigned int *op) ++{ ++ int sign; ++ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(offset) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(type) == MV_ERROR) ++ return MV_ERROR; ++ ++ *type = pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_TYPE_OFFS)] >> ++ (MVPP2_PRS_SRAM_UDF_TYPE_OFFS % 8); ++ *type &= MVPP2_PRS_SRAM_UDF_TYPE_MASK; ++ ++ *offset = (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_OFFS)] >> ++ (MVPP2_PRS_SRAM_UDF_OFFS % 8)) & 0x7f; ++ *offset |= (pe->sram.byte[ ++ SRAM_BIT_TO_BYTE(MVPP2_PRS_SRAM_UDF_OFFS + ++ MVPP2_PRS_SRAM_UDF_BITS)] << ++ (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8))) & 0x80; ++ ++ *op = (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS)] >> ++ (MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS % 8)) & 0x7; ++ *op |= (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS + ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_BITS)] << ++ (8 - (MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS % 8))) & 0x18; ++ ++ /* if signed bit is tes */ ++ sign = pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_UDF_SIGN_BIT)] & ++ (1 << (MVPP2_PRS_SRAM_UDF_SIGN_BIT % 8)); ++ if (sign != 0) ++ *offset = 1 - (*offset); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_offset_get); ++ ++int mv_pp2x_prs_sw_sram_next_lu_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *lu) ++{ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(lu) == MV_ERROR) ++ return MV_ERROR; ++ ++ *lu = pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_NEXT_LU_OFFS)]; ++ *lu = ((*lu) >> MVPP2_PRS_SRAM_NEXT_LU_OFFS % 8); ++ *lu &= MVPP2_PRS_SRAM_NEXT_LU_MASK; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_next_lu_get); ++ ++int mv_pp2x_prs_sram_bit_get(struct mv_pp2x_prs_entry *pe, int bit_num, ++ unsigned int *bit) ++{ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ *bit = pe->sram.byte[SRAM_BIT_TO_BYTE(bit_num)] & ++ (1 << (bit_num % 8)); ++ *bit = (*bit) >> (bit_num % 8); ++ return MV_OK; ++} ++ ++void mv_pp2x_prs_sw_sram_lu_done_set(struct mv_pp2x_prs_entry *pe) ++{ ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_lu_done_set); ++ ++void mv_pp2x_prs_sw_sram_lu_done_clear(struct mv_pp2x_prs_entry *pe) ++{ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_lu_done_clear); ++ ++int mv_pp2x_prs_sw_sram_lu_done_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bit) ++{ ++ return mv_pp2x_prs_sram_bit_get(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, bit); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_lu_done_get); ++ ++void mv_pp2x_prs_sw_sram_flowid_set(struct mv_pp2x_prs_entry *pe) ++{ ++ mv_pp2x_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_flowid_set); ++ ++void mv_pp2x_prs_sw_sram_flowid_clear(struct mv_pp2x_prs_entry *pe) ++{ ++ mv_pp2x_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_flowid_clear); ++ ++int mv_pp2x_prs_sw_sram_flowid_gen_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bit) ++{ ++ return mv_pp2x_prs_sram_bit_get(pe, MVPP2_PRS_SRAM_LU_GEN_BIT, bit); ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_flowid_gen_get); ++ ++/* return RI and RI_UPDATE */ ++int mv_pp2x_prs_sw_sram_ri_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bits, unsigned int *enable) ++{ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(bits) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(enable) == MV_ERROR) ++ return MV_ERROR; ++ ++ *bits = pe->sram.word[MVPP2_PRS_SRAM_RI_OFFS / 32]; ++ *enable = pe->sram.word[MVPP2_PRS_SRAM_RI_CTRL_OFFS / 32]; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_ri_get); ++ ++int mv_pp2x_prs_sw_sram_ai_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bits, unsigned int *enable) ++{ ++ if (mv_pp2x_ptr_validate(pe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(bits) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(enable) == MV_ERROR) ++ return MV_ERROR; ++ ++ *bits = (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_AI_OFFS)] >> (MVPP2_PRS_SRAM_AI_OFFS % 8)) | ++ (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_AI_OFFS + ++ MVPP2_PRS_SRAM_AI_CTRL_BITS)] << ++ (8 - (MVPP2_PRS_SRAM_AI_OFFS % 8))); ++ ++ *enable = (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_AI_CTRL_OFFS)] >> ++ (MVPP2_PRS_SRAM_AI_CTRL_OFFS % 8)) | ++ (pe->sram.byte[SRAM_BIT_TO_BYTE( ++ MVPP2_PRS_SRAM_AI_CTRL_OFFS + ++ MVPP2_PRS_SRAM_AI_CTRL_BITS)] << ++ (8 - (MVPP2_PRS_SRAM_AI_CTRL_OFFS % 8))); ++ ++ *bits &= MVPP2_PRS_SRAM_AI_MASK; ++ *enable &= MVPP2_PRS_SRAM_AI_MASK; ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_prs_sw_sram_ai_get); ++ ++/*#include "mvPp2ClsHw.h" */ ++ ++/********************************************************************/ ++/***************** Classifier Top Public lkpid table APIs ********************/ ++/********************************************************************/ ++ ++/*------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_hw_lkp_read(struct mv_pp2x_hw *hw, int lkpid, int way, ++ struct mv_pp2x_cls_lookup_entry *fe) ++{ ++ unsigned int reg_val = 0; ++ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(way, 0, WAY_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(lkpid, 0, ++ MVPP2_CLS_FLOWS_TBL_SIZE) == MV_ERROR) ++ return MV_ERROR; ++ ++ /* write index reg */ ++ reg_val = (way << MVPP2_CLS_LKP_INDEX_WAY_OFFS) | ++ (lkpid << MVPP2_CLS_LKP_INDEX_LKP_OFFS); ++ mv_pp2x_write(hw, MVPP2_CLS_LKP_INDEX_REG, reg_val); ++ ++ fe->way = way; ++ fe->lkpid = lkpid; ++ ++ fe->data = mv_pp2x_read(hw, MVPP2_CLS_LKP_TBL_REG); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_hw_lkp_read); ++ ++int mv_pp2x_cls_hw_lkp_write(struct mv_pp2x_hw *hw, int lkpid, ++ int way, struct mv_pp2x_cls_lookup_entry *fe) ++{ ++ unsigned int reg_val = 0; ++ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(way, 0, 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(lkpid, 0, ++ MVPP2_CLS_FLOWS_TBL_SIZE) == MV_ERROR) ++ return MV_ERROR; ++ ++ /* write index reg */ ++ reg_val = (way << MVPP2_CLS_LKP_INDEX_WAY_OFFS) | ++ (lkpid << MVPP2_CLS_LKP_INDEX_LKP_OFFS); ++ mv_pp2x_write(hw, MVPP2_CLS_LKP_INDEX_REG, reg_val); ++ ++ /* write flowId reg */ ++ mv_pp2x_write(hw, MVPP2_CLS_LKP_TBL_REG, fe->data); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_hw_lkp_write); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_lkp_rxq_get(struct mv_pp2x_cls_lookup_entry *lkp, int *rxq) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(rxq) == MV_ERROR) ++ return MV_ERROR; ++ ++ *rxq = (lkp->data & MVPP2_FLOWID_RXQ_MASK) >> MVPP2_FLOWID_RXQ; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_rxq_get); ++ ++int mv_pp2x_cls_sw_lkp_rxq_set(struct mv_pp2x_cls_lookup_entry *lkp, int rxq) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(rxq, 0, ++ (1 << MVPP2_FLOWID_RXQ_BITS) - 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ lkp->data &= ~MVPP2_FLOWID_RXQ_MASK; ++ lkp->data |= (rxq << MVPP2_FLOWID_RXQ); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_rxq_set); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_lkp_en_get(struct mv_pp2x_cls_lookup_entry *lkp, int *en) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(en) == MV_ERROR) ++ return MV_ERROR; ++ ++ *en = (lkp->data & MVPP2_FLOWID_EN_MASK) >> MVPP2_FLOWID_EN; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_en_get); ++ ++int mv_pp2x_cls_sw_lkp_en_set(struct mv_pp2x_cls_lookup_entry *lkp, int en) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(en, 0, 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ lkp->data &= ~MVPP2_FLOWID_EN_MASK; ++ lkp->data |= (en << MVPP2_FLOWID_EN); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_en_set); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_lkp_flow_get(struct mv_pp2x_cls_lookup_entry *lkp, ++ int *flow_idx) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(flow_idx) == MV_ERROR) ++ return MV_ERROR; ++ ++ *flow_idx = (lkp->data & MVPP2_FLOWID_FLOW_MASK) >> MVPP2_FLOWID_FLOW; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_flow_get); ++ ++int mv_pp2x_cls_sw_lkp_flow_set(struct mv_pp2x_cls_lookup_entry *lkp, ++ int flow_idx) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(flow_idx, 0, ++ MVPP2_CLS_FLOWS_TBL_SIZE) == MV_ERROR) ++ return MV_ERROR; ++ ++ lkp->data &= ~MVPP2_FLOWID_FLOW_MASK; ++ lkp->data |= (flow_idx << MVPP2_FLOWID_FLOW); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_flow_set); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_lkp_mod_get(struct mv_pp2x_cls_lookup_entry *lkp, ++ int *mod_base) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(mod_base) == MV_ERROR) ++ return MV_ERROR; ++ ++ *mod_base = (lkp->data & MVPP2_FLOWID_MODE_MASK) >> MVPP2_FLOWID_MODE; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_mod_get); ++ ++int mv_pp2x_cls_sw_lkp_mod_set(struct mv_pp2x_cls_lookup_entry *lkp, ++ int mod_base) ++{ ++ if (mv_pp2x_ptr_validate(lkp) == MV_ERROR) ++ return MV_ERROR; ++ ++ /* TODO: what is the max value of mode base */ ++ if (mv_pp2x_range_validate(mod_base, 0, ++ (1 << MVPP2_FLOWID_MODE_BITS) - 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ lkp->data &= ~MVPP2_FLOWID_MODE_MASK; ++ lkp->data |= (mod_base << MVPP2_FLOWID_MODE); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_lkp_mod_set); ++ ++/*********************************************************************/ ++/***************** Classifier Top Public flows table APIs ********************/ ++/********************************************************************/ ++ ++int mv_pp2x_cls_hw_flow_read(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_flow_entry *fe) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(index, 0, ++ MVPP2_CLS_FLOWS_TBL_SIZE) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->index = index; ++ ++ /*write index*/ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_INDEX_REG, index); ++ ++ fe->data[0] = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL0_REG); ++ fe->data[1] = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL1_REG); ++ fe->data[2] = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL2_REG); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_hw_flow_read); ++ ++/*----------------------------------------------------------------------*/ ++/*PPv2.1 new feature MAS 3.18*/ ++ ++/*----------------------------------------------------------------------*/ ++int mv_pp2x_cls_sw_flow_hek_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *num_of_fields, int field_ids[]) ++{ ++ int index; ++ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(num_of_fields) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(field_ids) == MV_ERROR) ++ return MV_ERROR; ++ ++ *num_of_fields = (fe->data[1] & MVPP2_FLOW_FIELDS_NUM_MASK) >> ++ MVPP2_FLOW_FIELDS_NUM; ++ ++ for (index = 0; index < (*num_of_fields); index++) ++ field_ids[index] = ((fe->data[2] & ++ MVPP2_FLOW_FIELD_MASK(index)) >> ++ MVPP2_FLOW_FIELD_ID(index)); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_hek_get); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_flow_port_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *type, int *portid) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(type) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(portid) == MV_ERROR) ++ return MV_ERROR; ++ ++ *type = (fe->data[0] & MVPP2_FLOW_PORT_TYPE_MASK) >> ++ MVPP2_FLOW_PORT_TYPE; ++ *portid = (fe->data[0] & MVPP2_FLOW_PORT_ID_MASK) >> ++ MVPP2_FLOW_PORT_ID; ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_port_get); ++ ++int mv_pp2x_cls_sw_flow_port_set(struct mv_pp2x_cls_flow_entry *fe, ++ int type, int portid) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(type, 0, ++ ((1 << MVPP2_FLOW_PORT_TYPE_BITS) - 1)) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(portid, 0, ++ ((1 << MVPP2_FLOW_PORT_ID_BITS) - 1)) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[0] &= ~MVPP2_FLOW_PORT_ID_MASK; ++ fe->data[0] &= ~MVPP2_FLOW_PORT_TYPE_MASK; ++ ++ fe->data[0] |= (portid << MVPP2_FLOW_PORT_ID); ++ fe->data[0] |= (type << MVPP2_FLOW_PORT_TYPE); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_port_set); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_flow_portid_select(struct mv_pp2x_cls_flow_entry *fe, ++ int from) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(from, 0, 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (from) ++ fe->data[0] |= MVPP2_FLOW_PORT_ID_SEL_MASK; ++ else ++ fe->data[0] &= ~MVPP2_FLOW_PORT_ID_SEL_MASK; ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_portid_select); ++ ++int mv_pp2x_cls_sw_flow_pppoe_set(struct mv_pp2x_cls_flow_entry *fe, int mode) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(mode, 0, MVPP2_FLOW_PPPOE_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[0] &= ~MVPP2_FLOW_PPPOE_MASK; ++ fe->data[0] |= (mode << MVPP2_FLOW_PPPOE); ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_pppoe_set); ++ ++int mv_pp2x_cls_sw_flow_vlan_set(struct mv_pp2x_cls_flow_entry *fe, int mode) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(mode, 0, MVPP2_FLOW_VLAN_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[0] &= ~MVPP2_FLOW_VLAN_MASK; ++ fe->data[0] |= (mode << MVPP2_FLOW_VLAN); ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_vlan_set); ++ ++/*----------------------------------------------------------------------*/ ++int mv_pp2x_cls_sw_flow_macme_set(struct mv_pp2x_cls_flow_entry *fe, int mode) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(mode, 0, MVPP2_FLOW_MACME_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[0] &= ~MVPP2_FLOW_MACME_MASK; ++ fe->data[0] |= (mode << MVPP2_FLOW_MACME); ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_macme_set); ++ ++/*----------------------------------------------------------------------*/ ++int mv_pp2x_cls_sw_flow_udf7_set(struct mv_pp2x_cls_flow_entry *fe, int mode) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(mode, 0, MVPP2_FLOW_UDF7_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[0] &= ~MVPP2_FLOW_UDF7_MASK; ++ fe->data[0] |= (mode << MVPP2_FLOW_UDF7); ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_udf7_set); ++ ++int mv_pp2x_cls_sw_flow_seq_ctrl_set(struct mv_pp2x_cls_flow_entry *fe, ++ int mode) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(mode, 0, MVPP2_FLOW_ENGINE_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[1] &= ~MVPP2_FLOW_SEQ_CTRL_MASK; ++ fe->data[1] |= (mode << MVPP2_FLOW_SEQ_CTRL); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_seq_ctrl_set); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_flow_engine_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *engine, int *is_last) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(engine) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(is_last) == MV_ERROR) ++ return MV_ERROR; ++ ++ *engine = (fe->data[0] & MVPP2_FLOW_ENGINE_MASK) >> MVPP2_FLOW_ENGINE; ++ *is_last = fe->data[0] & MVPP2_FLOW_LAST_MASK; ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_engine_get); ++ ++/*----------------------------------------------------------------------*/ ++int mv_pp2x_cls_sw_flow_engine_set(struct mv_pp2x_cls_flow_entry *fe, ++ int engine, int is_last) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(is_last, 0, 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[0] &= ~MVPP2_FLOW_LAST_MASK; ++ fe->data[0] &= ~MVPP2_FLOW_ENGINE_MASK; ++ ++ fe->data[0] |= is_last; ++ fe->data[0] |= (engine << MVPP2_FLOW_ENGINE); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_engine_set); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_sw_flow_extra_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *type, int *prio) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(type) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(prio) == MV_ERROR) ++ return MV_ERROR; ++ ++ *type = (fe->data[1] & MVPP2_FLOW_LKP_TYPE_MASK) >> ++ MVPP2_FLOW_LKP_TYPE; ++ *prio = (fe->data[1] & MVPP2_FLOW_FIELD_PRIO_MASK) >> ++ MVPP2_FLOW_FIELD_PRIO; ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_extra_get); ++ ++int mv_pp2x_cls_sw_flow_extra_set(struct mv_pp2x_cls_flow_entry *fe, ++ int type, int prio) ++{ ++ if (mv_pp2x_ptr_validate(fe) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(type, 0, ++ MVPP2_FLOW_PORT_ID_MAX) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(prio, 0, ++ ((1 << MVPP2_FLOW_FIELD_ID_BITS) - 1)) == MV_ERROR) ++ return MV_ERROR; ++ ++ fe->data[1] &= ~MVPP2_FLOW_LKP_TYPE_MASK; ++ fe->data[1] |= (type << MVPP2_FLOW_LKP_TYPE); ++ ++ fe->data[1] &= ~MVPP2_FLOW_FIELD_PRIO_MASK; ++ fe->data[1] |= (prio << MVPP2_FLOW_FIELD_PRIO); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_sw_flow_extra_set); ++ ++/*----------------------------------------------------------------------*/ ++/* Classifier Top Public length change table APIs */ ++/*----------------------------------------------------------------------*/ ++ ++/*----------------------------------------------------------------------*/ ++int mv_pp2x_cls_hw_flow_hit_get(struct mv_pp2x_hw *hw, ++ int index, unsigned int *cnt) ++{ ++ if (mv_pp2x_range_validate(index, 0, ++ MVPP2_CLS_FLOWS_TBL_SIZE) == MV_ERROR) ++ return MV_ERROR; ++ ++ /*set index */ ++ mv_pp2x_write(hw, MVPP2_CNT_IDX_REG, MVPP2_CNT_IDX_FLOW(index)); ++ ++ if (cnt) ++ *cnt = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL_HIT_REG); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_hw_flow_hit_get); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_hw_lkp_hit_get(struct mv_pp2x_hw *hw, int lkpid, int way, ++ unsigned int *cnt) ++{ ++ if (mv_pp2x_range_validate(way, 0, 1) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(lkpid, 0, ++ MVPP2_CLS_LKP_TBL_SIZE) == MV_ERROR) ++ return MV_ERROR; ++ ++ /*set index */ ++ mv_pp2x_write(hw, MVPP2_CNT_IDX_REG, MVPP2_CNT_IDX_LKP(lkpid, way)); ++ ++ if (cnt) ++ *cnt = mv_pp2x_read(hw, MVPP2_CLS_LKP_TBL_HIT_REG); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_hw_lkp_hit_get); ++ ++/*----------------------------------------------------------------------*/ ++/* Classifier C2 engine QoS table Public APIs */ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_c2_qos_hw_read(struct mv_pp2x_hw *hw, int tbl_id, ++ int tbl_sel, int tbl_line, ++ struct mv_pp2x_cls_c2_qos_entry *qos) ++{ ++ unsigned int reg_val = 0; ++ ++ if (mv_pp2x_ptr_validate(qos) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(tbl_sel, 0, 1) == MV_ERROR) /* one bit */ ++ return MV_ERROR; ++ ++ if (tbl_sel == 1) { ++ /*dscp*/ ++ /* TODO define 8=DSCP_TBL_NUM 64=DSCP_TBL_LINES */ ++ if (mv_pp2x_range_validate(tbl_id, 0, ++ MVPP2_QOS_TBL_NUM_DSCP) == MV_ERROR) ++ return MV_ERROR; ++ if (mv_pp2x_range_validate(tbl_line, 0, ++ MVPP2_QOS_TBL_LINE_NUM_DSCP) == MV_ERROR) ++ return MV_ERROR; ++ } else { ++ /*pri*/ ++ /* TODO define 64=PRI_TBL_NUM 8=PRI_TBL_LINES */ ++ if (mv_pp2x_range_validate(tbl_id, 0, ++ MVPP2_QOS_TBL_NUM_PRI) == MV_ERROR) ++ return MV_ERROR; ++ if (mv_pp2x_range_validate(tbl_line, 0, ++ MVPP2_QOS_TBL_LINE_NUM_PRI) == MV_ERROR) ++ return MV_ERROR; ++ } ++ ++ qos->tbl_id = tbl_id; ++ qos->tbl_sel = tbl_sel; ++ qos->tbl_line = tbl_line; ++ ++ /* write index reg */ ++ reg_val |= (tbl_line << MVPP2_CLS2_DSCP_PRI_INDEX_LINE_OFF); ++ reg_val |= (tbl_sel << MVPP2_CLS2_DSCP_PRI_INDEX_SEL_OFF); ++ reg_val |= (tbl_id << MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_OFF); ++ ++ mv_pp2x_write(hw, MVPP2_CLS2_DSCP_PRI_INDEX_REG, reg_val); ++ ++ /* read data reg*/ ++ qos->data = mv_pp2x_read(hw, MVPP2_CLS2_QOS_TBL_REG); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_qos_hw_read); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2_cls_c2_qos_prio_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *prio) ++{ ++ if (mv_pp2x_ptr_validate(qos) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(prio) == MV_ERROR) ++ return MV_ERROR; ++ ++ *prio = (qos->data & MVPP2_CLS2_QOS_TBL_PRI_MASK) >> ++ MVPP2_CLS2_QOS_TBL_PRI_OFF; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2_cls_c2_qos_prio_get); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2_cls_c2_qos_dscp_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *dscp) ++{ ++ if (mv_pp2x_ptr_validate(qos) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(dscp) == MV_ERROR) ++ return MV_ERROR; ++ ++ *dscp = (qos->data & MVPP2_CLS2_QOS_TBL_DSCP_MASK) >> ++ MVPP2_CLS2_QOS_TBL_DSCP_OFF; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2_cls_c2_qos_dscp_get); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2_cls_c2_qos_color_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *color) ++{ ++ if (mv_pp2x_ptr_validate(qos) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(color) == MV_ERROR) ++ return MV_ERROR; ++ ++ *color = (qos->data & MVPP2_CLS2_QOS_TBL_COLOR_MASK) >> ++ MVPP2_CLS2_QOS_TBL_COLOR_OFF; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2_cls_c2_qos_color_get); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2_cls_c2_qos_gpid_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *gpid) ++{ ++ if (mv_pp2x_ptr_validate(qos) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(gpid) == MV_ERROR) ++ return MV_ERROR; ++ ++ *gpid = (qos->data & MVPP2_CLS2_QOS_TBL_GEMPORT_MASK) >> ++ MVPP2_CLS2_QOS_TBL_GEMPORT_OFF; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2_cls_c2_qos_gpid_get); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2_cls_c2_qos_queue_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *queue) ++{ ++ if (mv_pp2x_ptr_validate(qos) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(queue) == MV_ERROR) ++ return MV_ERROR; ++ ++ *queue = (qos->data & MVPP2_CLS2_QOS_TBL_QUEUENUM_MASK) >> ++ MVPP2_CLS2_QOS_TBL_QUEUENUM_OFF; ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2_cls_c2_qos_queue_get); ++ ++/*----------------------------------------------------------------------*/ ++/* Classifier C2 engine TCAM table Public APIs */ ++/*----------------------------------------------------------------------*/ ++ ++/* note: error is not returned if entry is invalid ++ * user should check c2->valid afer returned from this func ++ */ ++int mv_pp2x_cls_c2_hw_read(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_c2_entry *c2) ++{ ++ unsigned int reg_val; ++ int tcm_idx; ++ ++ if (mv_pp2x_ptr_validate(c2) == MV_ERROR) ++ return MV_ERROR; ++ ++ c2->index = index; ++ ++ /* write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, index); ++ ++ /* read inValid bit*/ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_TCAM_INV_REG); ++ c2->inv = (reg_val & MVPP2_CLS2_TCAM_INV_INVALID_MASK) >> ++ MVPP2_CLS2_TCAM_INV_INVALID_OFF; ++ ++ if (c2->inv) ++ return MV_OK; ++ ++ for (tcm_idx = 0; tcm_idx < MVPP2_CLS_C2_TCAM_WORDS; tcm_idx++) ++ c2->tcam.words[tcm_idx] = mv_pp2x_read(hw, ++ MVPP2_CLS2_TCAM_DATA_REG(tcm_idx)); ++ ++ /* read action_tbl 0x1B30 */ ++ c2->sram.regs.action_tbl = mv_pp2x_read(hw, MVPP2_CLS2_ACT_DATA_REG); ++ ++ /* read actions 0x1B60 */ ++ c2->sram.regs.actions = mv_pp2x_read(hw, MVPP2_CLS2_ACT_REG); ++ ++ /* read qos_attr 0x1B64 */ ++ c2->sram.regs.qos_attr = mv_pp2x_read(hw, MVPP2_CLS2_ACT_QOS_ATTR_REG); ++ ++ /* read hwf_attr 0x1B68 */ ++ c2->sram.regs.hwf_attr = mv_pp2x_read(hw, MVPP2_CLS2_ACT_HWF_ATTR_REG); ++ ++ /* read hwf_attr 0x1B6C */ ++ c2->sram.regs.rss_attr = mv_pp2x_read(hw, MVPP2_CLS2_ACT_DUP_ATTR_REG); ++ ++ /* read seq_attr 0x1B70 */ ++ c2->sram.regs.seq_attr = mv_pp2x_read(hw, MVPP22_CLS2_ACT_SEQ_ATTR_REG); ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_hw_read); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2_cls_c2_tcam_byte_get(struct mv_pp2x_cls_c2_entry *c2, ++ unsigned int offs, unsigned char *byte, ++ unsigned char *enable) ++{ ++ if (mv_pp2x_ptr_validate(c2) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(byte) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_ptr_validate(enable) == MV_ERROR) ++ return MV_ERROR; ++ ++ if (mv_pp2x_range_validate(offs, 0, 8) == MV_ERROR) ++ return MV_ERROR; ++ ++ *byte = c2->tcam.bytes[TCAM_DATA_BYTE(offs)]; ++ *enable = c2->tcam.bytes[TCAM_DATA_MASK(offs)]; ++ return MV_OK; ++} ++ ++/*----------------------------------------------------------------------*/ ++/* return EQUALS if tcam_data[off]&tcam_mask[off] = byte */ ++/*----------------------------------------------------------------------*/ ++/* Classifier C2 engine Hit counters Public APIs */ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_c2_hit_cntr_is_busy(struct mv_pp2x_hw *hw) ++{ ++ unsigned int reg_val; ++ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_HIT_CTR_REG); ++ reg_val &= MVPP2_CLS2_HIT_CTR_CLR_DONE_MASK; ++ reg_val >>= MVPP2_CLS2_HIT_CTR_CLR_DONE_OFF; ++ ++ return (1 - (int)reg_val); ++} ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_c2_hit_cntr_clear_all(struct mv_pp2x_hw *hw) ++{ ++ int iter = 0; ++ ++ /* wrirte clear bit*/ ++ mv_pp2x_write(hw, MVPP2_CLS2_HIT_CTR_CLR_REG, ++ (1 << MVPP2_CLS2_HIT_CTR_CLR_CLR_OFF)); ++ ++ while (mv_pp2x_cls_c2_hit_cntr_is_busy(hw)) ++ if (iter++ >= RETRIES_EXCEEDED) { ++ pr_debug("%s:Error - retries exceeded.\n", __func__); ++ return MV_ERROR; ++ } ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_hit_cntr_clear_all); ++ ++/*----------------------------------------------------------------------*/ ++ ++int mv_pp2x_cls_c2_hit_cntr_read(struct mv_pp2x_hw *hw, int index, u32 *cntr) ++{ ++ unsigned int value = 0; ++ ++ /* write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, index); ++ ++ value = mv_pp2x_read(hw, MVPP2_CLS2_HIT_CTR_REG); ++ ++ if (cntr) ++ *cntr = value; ++ ++ return MV_OK; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_hit_cntr_read); ++ ++void mv_pp2x_cls_flow_port_add(struct mv_pp2x_hw *hw, int index, int port_id) ++{ ++ u32 data; ++ ++ /* Write flow index */ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_INDEX_REG, index); ++ /* Read first data with port info */ ++ data = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL0_REG); ++ /* Add the port */ ++ data |= ((1 << port_id) << MVPP2_FLOW_PORT_ID); ++ /* Update the register */ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_TBL0_REG, data); ++} ++ ++void mv_pp2x_cls_flow_port_del(struct mv_pp2x_hw *hw, int index, int port_id) ++{ ++ u32 data; ++ ++ /* Write flow index */ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_INDEX_REG, index); ++ /* Read first data with port info */ ++ data = mv_pp2x_read(hw, MVPP2_CLS_FLOW_TBL0_REG); ++ /* Delete the port */ ++ data &= ~(((1 << port_id) << MVPP2_FLOW_PORT_ID)); ++ /* Update the register */ ++ mv_pp2x_write(hw, MVPP2_CLS_FLOW_TBL0_REG, data); ++} ++ ++/* The function prepare a temporary flow table for lkpid flow, ++ * in order to change the original one ++ */ ++void mv_pp2x_cls_flow_tbl_temp_copy(struct mv_pp2x_hw *hw, int lkpid, ++ int *temp_flow_idx) ++{ ++ struct mv_pp2x_cls_flow_entry fe; ++ int index = lkpid - MVPP2_PRS_FL_START; ++ int flow_start = hw->cls_shadow->flow_swap_area; ++ struct mv_pp2x_cls_flow_info *flow_info; ++ ++ flow_info = &hw->cls_shadow->flow_info[index]; ++ ++ if (flow_info->flow_entry_dflt) { ++ mv_pp2x_cls_flow_read(hw, flow_info->flow_entry_dflt, &fe); ++ fe.index = flow_start++; ++ mv_pp2x_cls_flow_write(hw, &fe); ++ } ++ if (flow_info->flow_entry_vlan) { ++ mv_pp2x_cls_flow_read(hw, flow_info->flow_entry_vlan, &fe); ++ fe.index = flow_start++; ++ mv_pp2x_cls_flow_write(hw, &fe); ++ } ++ if (flow_info->flow_entry_dscp) { ++ mv_pp2x_cls_flow_read(hw, flow_info->flow_entry_dscp, &fe); ++ fe.index = flow_start++; ++ mv_pp2x_cls_flow_write(hw, &fe); ++ } ++ if (flow_info->flow_entry_rss1) { ++ mv_pp2x_cls_flow_read(hw, flow_info->flow_entry_rss1, &fe); ++ fe.index = flow_start++; ++ mv_pp2x_cls_flow_write(hw, &fe); ++ } ++ if (flow_info->flow_entry_rss2) { ++ mv_pp2x_cls_flow_read(hw, flow_info->flow_entry_rss2, &fe); ++ fe.index = flow_start++; ++ mv_pp2x_cls_flow_write(hw, &fe); ++ } ++ ++ *temp_flow_idx = hw->cls_shadow->flow_swap_area; ++} ++ ++/* C2 rule and Qos table */ ++int mv_pp2x_cls_c2_hw_write(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_c2_entry *c2) ++{ ++ int tcm_idx; ++ ++ if (!c2 || index >= MVPP2_CLS_C2_TCAM_SIZE) ++ return -EINVAL; ++ ++ c2->index = index; ++ ++ /* write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, index); ++ ++ mv_pp2x_cls_c2_hw_inv(hw, index); ++ ++ /* write action_tbl CLSC2_ACT_DATA */ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_DATA_REG, c2->sram.regs.action_tbl); ++ ++ /* write actions CLSC2_ACT */ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_REG, c2->sram.regs.actions); ++ ++ /* write qos_attr CLSC2_ATTR0 */ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_QOS_ATTR_REG, c2->sram.regs.qos_attr); ++ ++ /* write hwf_attr CLSC2_ATTR1 */ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_HWF_ATTR_REG, c2->sram.regs.hwf_attr); ++ ++ /* write rss_attr CLSC2_ATTR2 */ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_DUP_ATTR_REG, c2->sram.regs.rss_attr); ++ ++ /* write valid bit*/ ++ c2->inv = 0; ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_INV_REG, ++ ((c2->inv) << MVPP2_CLS2_TCAM_INV_INVALID_OFF)); ++ ++ for (tcm_idx = 0; tcm_idx < MVPP2_CLS_C2_TCAM_WORDS; tcm_idx++) ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_DATA_REG(tcm_idx), ++ c2->tcam.words[tcm_idx]); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_hw_write); ++ ++int mv_pp2x_cls_c2_qos_hw_write(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_c2_qos_entry *qos) ++{ ++ unsigned int reg_val = 0; ++ ++ if (!qos || qos->tbl_sel > MVPP2_QOS_TBL_SEL_DSCP) ++ return -EINVAL; ++ ++ if (qos->tbl_sel == MVPP2_QOS_TBL_SEL_DSCP) { ++ /*dscp*/ ++ if (qos->tbl_id >= MVPP2_QOS_TBL_NUM_DSCP || ++ qos->tbl_line >= MVPP2_QOS_TBL_LINE_NUM_DSCP) ++ return -EINVAL; ++ } else { ++ /*pri*/ ++ if (qos->tbl_id >= MVPP2_QOS_TBL_NUM_PRI || ++ qos->tbl_line >= MVPP2_QOS_TBL_LINE_NUM_PRI) ++ return -EINVAL; ++ } ++ /* write index reg */ ++ reg_val |= (qos->tbl_line << MVPP2_CLS2_DSCP_PRI_INDEX_LINE_OFF); ++ reg_val |= (qos->tbl_sel << MVPP2_CLS2_DSCP_PRI_INDEX_SEL_OFF); ++ reg_val |= (qos->tbl_id << MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_OFF); ++ mv_pp2x_write(hw, MVPP2_CLS2_DSCP_PRI_INDEX_REG, reg_val); ++ ++ /* write data reg*/ ++ mv_pp2x_write(hw, MVPP2_CLS2_QOS_TBL_REG, qos->data); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_qos_hw_write); ++ ++int mv_pp2x_cls_c2_hw_inv(struct mv_pp2x_hw *hw, int index) ++{ ++ if (!hw || index >= MVPP2_CLS_C2_TCAM_SIZE) ++ return -EINVAL; ++ ++ /* write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, index); ++ ++ /* set invalid bit*/ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_INV_REG, (1 << ++ MVPP2_CLS2_TCAM_INV_INVALID_OFF)); ++ ++ /* trigger */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_DATA_REG(4), 0); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_hw_inv); ++ ++void mv_pp2x_cls_c2_hw_inv_all(struct mv_pp2x_hw *hw) ++{ ++ int index; ++ ++ for (index = 0; index < MVPP2_CLS_C2_TCAM_SIZE; index++) ++ mv_pp2x_cls_c2_hw_inv(hw, index); ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_hw_inv_all); ++ ++static void mv_pp2x_cls_c2_qos_hw_clear_all(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_cls_c2_qos_entry qos; ++ ++ memset(&qos, 0, sizeof(struct mv_pp2x_cls_c2_qos_entry)); ++ ++ /* clear DSCP tables */ ++ qos.tbl_sel = MVPP2_QOS_TBL_SEL_DSCP; ++ for (qos.tbl_id = 0; qos.tbl_id < MVPP2_QOS_TBL_NUM_DSCP; ++ qos.tbl_id++) { ++ for (qos.tbl_line = 0; qos.tbl_line < ++ MVPP2_QOS_TBL_LINE_NUM_DSCP; qos.tbl_line++) { ++ mv_pp2x_cls_c2_qos_hw_write(hw, &qos); ++ } ++ } ++ ++ /* clear PRIO tables */ ++ qos.tbl_sel = MVPP2_QOS_TBL_SEL_PRI; ++ for (qos.tbl_id = 0; qos.tbl_id < ++ MVPP2_QOS_TBL_NUM_PRI; qos.tbl_id++) ++ for (qos.tbl_line = 0; qos.tbl_line < ++ MVPP2_QOS_TBL_LINE_NUM_PRI; qos.tbl_line++) { ++ mv_pp2x_cls_c2_qos_hw_write(hw, &qos); ++ } ++} ++ ++int mv_pp2x_cls_c2_qos_tbl_set(struct mv_pp2x_cls_c2_entry *c2, ++ int tbl_id, int tbl_sel) ++{ ++ if (!c2 || tbl_sel > 1) ++ return -EINVAL; ++ ++ if (tbl_sel == 1) { ++ /*dscp*/ ++ if (tbl_id >= MVPP2_QOS_TBL_NUM_DSCP) ++ return -EINVAL; ++ } else { ++ /*pri*/ ++ if (tbl_id >= MVPP2_QOS_TBL_NUM_PRI) ++ return -EINVAL; ++ } ++ c2->sram.regs.action_tbl = (tbl_id << ++ MVPP2_CLS2_ACT_DATA_TBL_ID_OFF) | ++ (tbl_sel << MVPP2_CLS2_ACT_DATA_TBL_SEL_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_qos_tbl_set); ++ ++int mv_pp2x_cls_c2_color_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int from) ++{ ++ if (!c2 || cmd > MVPP2_COLOR_ACTION_TYPE_RED_LOCK) ++ return -EINVAL; ++ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_COLOR_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_COLOR_OFF); ++ ++ if (from == 1) ++ c2->sram.regs.action_tbl |= (1 << ++ MVPP2_CLS2_ACT_DATA_TBL_COLOR_OFF); ++ else ++ c2->sram.regs.action_tbl &= ~(1 << ++ MVPP2_CLS2_ACT_DATA_TBL_COLOR_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_color_set); ++ ++int mv_pp2x_cls_c2_prio_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int prio, int from) ++{ ++ if (!c2 || cmd > MVPP2_ACTION_TYPE_UPDT_LOCK || ++ prio >= MVPP2_QOS_TBL_LINE_NUM_PRI) ++ return -EINVAL; ++ ++ /*set command*/ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_PRI_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_PRI_OFF); ++ ++ /*set modify priority value*/ ++ c2->sram.regs.qos_attr &= ~MVPP2_CLS2_ACT_QOS_ATTR_PRI_MASK; ++ c2->sram.regs.qos_attr |= ((prio << MVPP2_CLS2_ACT_QOS_ATTR_PRI_OFF) & ++ MVPP2_CLS2_ACT_QOS_ATTR_PRI_MASK); ++ ++ if (from == 1) ++ c2->sram.regs.action_tbl |= (1 << ++ MVPP2_CLS2_ACT_DATA_TBL_PRI_DSCP_OFF); ++ else ++ c2->sram.regs.action_tbl &= ~(1 << ++ MVPP2_CLS2_ACT_DATA_TBL_PRI_DSCP_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_prio_set); ++ ++int mv_pp2x_cls_c2_dscp_set(struct mv_pp2x_cls_c2_entry *c2, ++ int cmd, int dscp, int from) ++{ ++ if (!c2 || cmd > MVPP2_ACTION_TYPE_UPDT_LOCK || ++ dscp >= MVPP2_QOS_TBL_LINE_NUM_DSCP) ++ return -EINVAL; ++ ++ /*set command*/ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_DSCP_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_DSCP_OFF); ++ ++ /*set modify DSCP value*/ ++ c2->sram.regs.qos_attr &= ~MVPP2_CLS2_ACT_QOS_ATTR_DSCP_MASK; ++ c2->sram.regs.qos_attr |= ((dscp << ++ MVPP2_CLS2_ACT_QOS_ATTR_DSCP_OFF) & ++ MVPP2_CLS2_ACT_QOS_ATTR_DSCP_MASK); ++ ++ if (from == 1) ++ c2->sram.regs.action_tbl |= (1 << ++ MVPP2_CLS2_ACT_DATA_TBL_PRI_DSCP_OFF); ++ else ++ c2->sram.regs.action_tbl &= ~(1 << ++ MVPP2_CLS2_ACT_DATA_TBL_PRI_DSCP_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_dscp_set); ++ ++int mv_pp2x_cls_c2_queue_low_set(struct mv_pp2x_cls_c2_entry *c2, ++ int cmd, int queue, int from) ++{ ++ if (!c2 || cmd > MVPP2_ACTION_TYPE_UPDT_LOCK || ++ queue >= (1 << MVPP2_CLS2_ACT_QOS_ATTR_QL_BITS)) ++ return -EINVAL; ++ ++ /*set command*/ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_QL_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_QL_OFF); ++ ++ /*set modify Low queue value*/ ++ c2->sram.regs.qos_attr &= ~MVPP2_CLS2_ACT_QOS_ATTR_QL_MASK; ++ c2->sram.regs.qos_attr |= ((queue << ++ MVPP2_CLS2_ACT_QOS_ATTR_QL_OFF) & ++ MVPP2_CLS2_ACT_QOS_ATTR_QL_MASK); ++ ++ if (from == 1) ++ c2->sram.regs.action_tbl |= (1 << ++ MVPP2_CLS2_ACT_DATA_TBL_LOW_Q_OFF); ++ else ++ c2->sram.regs.action_tbl &= ~(1 << ++ MVPP2_CLS2_ACT_DATA_TBL_LOW_Q_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_queue_low_set); ++ ++int mv_pp2x_cls_c2_queue_high_set(struct mv_pp2x_cls_c2_entry *c2, ++ int cmd, int queue, int from) ++{ ++ if (!c2 || cmd > MVPP2_ACTION_TYPE_UPDT_LOCK || ++ queue >= (1 << MVPP2_CLS2_ACT_QOS_ATTR_QH_BITS)) ++ return -EINVAL; ++ ++ /*set command*/ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_QH_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_QH_OFF); ++ ++ /*set modify High queue value*/ ++ c2->sram.regs.qos_attr &= ~MVPP2_CLS2_ACT_QOS_ATTR_QH_MASK; ++ c2->sram.regs.qos_attr |= ((queue << ++ MVPP2_CLS2_ACT_QOS_ATTR_QH_OFF) & ++ MVPP2_CLS2_ACT_QOS_ATTR_QH_MASK); ++ ++ if (from == 1) ++ c2->sram.regs.action_tbl |= (1 << ++ MVPP2_CLS2_ACT_DATA_TBL_HIGH_Q_OFF); ++ else ++ c2->sram.regs.action_tbl &= ~(1 << ++ MVPP2_CLS2_ACT_DATA_TBL_HIGH_Q_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_queue_high_set); ++ ++int mv_pp2x_cls_c2_forward_set(struct mv_pp2x_cls_c2_entry *c2, int cmd) ++{ ++ if (!c2 || cmd > MVPP2_FRWD_ACTION_TYPE_HWF_LOW_LATENCY_LOCK) ++ return -EINVAL; ++ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_FRWD_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_FRWD_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_forward_set); ++ ++int mv_pp2x_cls_c2_rss_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, int rss_en) ++{ ++ if (!c2 || cmd > MVPP2_ACTION_TYPE_UPDT_LOCK || rss_en >= ++ (1 << MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_BITS)) ++ return -EINVAL; ++ ++ c2->sram.regs.actions &= ~MVPP2_CLS2_ACT_RSS_MASK; ++ c2->sram.regs.actions |= (cmd << MVPP2_CLS2_ACT_RSS_OFF); ++ ++ c2->sram.regs.rss_attr &= ~MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_MASK; ++ c2->sram.regs.rss_attr |= (rss_en << ++ MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_rss_set); ++ ++int mv_pp2x_cls_c2_flow_id_en(struct mv_pp2x_cls_c2_entry *c2, int flowid_en) ++{ ++ if (!c2) ++ return -EINVAL; ++ ++ /*set Flow ID enable or disable*/ ++ if (flowid_en) ++ c2->sram.regs.actions |= (1 << MVPP2_CLS2_ACT_FLD_EN_OFF); ++ else ++ c2->sram.regs.actions &= ~(1 << MVPP2_CLS2_ACT_FLD_EN_OFF); ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_flow_id_en); ++ ++int mv_pp2x_cls_c2_tcam_byte_set(struct mv_pp2x_cls_c2_entry *c2, ++ unsigned int offs, unsigned char byte, ++ unsigned char enable) ++{ ++ if (!c2 || offs >= MVPP2_CLS_C2_TCAM_DATA_BYTES) ++ return -EINVAL; ++ ++ c2->tcam.bytes[TCAM_DATA_BYTE(offs)] = byte; ++ c2->tcam.bytes[TCAM_DATA_MASK(offs)] = enable; ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_tcam_byte_set); ++ ++int mv_pp2x_cls_c2_qos_queue_set(struct mv_pp2x_cls_c2_qos_entry *qos, ++ u8 queue) ++{ ++ if (!qos || queue >= (1 << MVPP2_CLS2_QOS_TBL_QUEUENUM_BITS)) ++ return -EINVAL; ++ ++ qos->data &= ~MVPP2_CLS2_QOS_TBL_QUEUENUM_MASK; ++ qos->data |= (((u32)queue) << MVPP2_CLS2_QOS_TBL_QUEUENUM_OFF); ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_qos_queue_set); ++ ++static int mv_pp2x_c2_tcam_set(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_c2_add_entry *c2_add_entry, ++ unsigned int c2_hw_idx) ++{ ++ int ret_code; ++ struct mv_pp2x_cls_c2_entry c2_entry; ++ int hek_offs; ++ unsigned char hek_byte[MVPP2_CLS_C2_HEK_OFF_MAX], ++ hek_byte_mask[MVPP2_CLS_C2_HEK_OFF_MAX]; ++ ++ if (!c2_add_entry || !hw || c2_hw_idx >= MVPP2_CLS_C2_TCAM_SIZE) ++ return -EINVAL; ++ ++ /* Clear C2 sw data */ ++ memset(&c2_entry, 0, sizeof(struct mv_pp2x_cls_c2_entry)); ++ ++ /* Set QOS table, selection and ID */ ++ ret_code = mv_pp2x_cls_c2_qos_tbl_set(&c2_entry, ++ c2_add_entry->qos_info.qos_tbl_index, ++ c2_add_entry->qos_info.qos_tbl_type); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set color, cmd and source */ ++ ret_code = mv_pp2x_cls_c2_color_set(&c2_entry, ++ c2_add_entry->action.color_act, ++ c2_add_entry->qos_info.color_src); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set priority(pbit), cmd, value(not from qos table) and source */ ++ ret_code = mv_pp2x_cls_c2_prio_set(&c2_entry, ++ c2_add_entry->action.pri_act, ++ c2_add_entry->qos_value.pri, ++ c2_add_entry->qos_info.pri_dscp_src); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set DSCP, cmd, value(not from qos table) and source */ ++ ret_code = mv_pp2x_cls_c2_dscp_set(&c2_entry, ++ c2_add_entry->action.dscp_act, ++ c2_add_entry->qos_value.dscp, ++ c2_add_entry->qos_info.pri_dscp_src); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set queue low, cmd, value, and source */ ++ ret_code = mv_pp2x_cls_c2_queue_low_set(&c2_entry, ++ c2_add_entry->action.q_low_act, ++ c2_add_entry->qos_value.q_low, ++ c2_add_entry->qos_info.q_low_src); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set queue high, cmd, value and source */ ++ ret_code = mv_pp2x_cls_c2_queue_high_set(&c2_entry, ++ c2_add_entry->action.q_high_act, ++ c2_add_entry->qos_value.q_high, ++ c2_add_entry->qos_info.q_high_src); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set forward */ ++ ret_code = mv_pp2x_cls_c2_forward_set(&c2_entry, ++ c2_add_entry->action.frwd_act); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set RSS */ ++ ret_code = mv_pp2x_cls_c2_rss_set(&c2_entry, ++ c2_add_entry->action.rss_act, ++ c2_add_entry->rss_en); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set flowID(not for multicast) */ ++ ret_code = mv_pp2x_cls_c2_flow_id_en(&c2_entry, ++ c2_add_entry->action.flowid_act); ++ if (ret_code) ++ return ret_code; ++ ++ /* Set C2 HEK */ ++ memset(hek_byte, 0, MVPP2_CLS_C2_HEK_OFF_MAX); ++ memset(hek_byte_mask, 0, MVPP2_CLS_C2_HEK_OFF_MAX); ++ ++ /* HEK offs 8, lookup type, port type */ ++ hek_byte[MVPP2_CLS_C2_HEK_OFF_LKP_PORT_TYPE] = ++ (c2_add_entry->port.port_type << ++ MVPP2_CLS_C2_HEK_PORT_TYPE_OFFS) | ++ (c2_add_entry->lkp_type << ++ MVPP2_CLS_C2_HEK_LKP_TYPE_OFFS); ++ hek_byte_mask[MVPP2_CLS_C2_HEK_OFF_LKP_PORT_TYPE] = ++ MVPP2_CLS_C2_HEK_PORT_TYPE_MASK | ++ ((c2_add_entry->lkp_type_mask << ++ MVPP2_CLS_C2_HEK_LKP_TYPE_OFFS) & ++ MVPP2_CLS_C2_HEK_LKP_TYPE_MASK); ++ /* HEK offs 9, port ID */ ++ hek_byte[MVPP2_CLS_C2_HEK_OFF_PORT_ID] = ++ c2_add_entry->port.port_value; ++ hek_byte_mask[MVPP2_CLS_C2_HEK_OFF_PORT_ID] = ++ c2_add_entry->port.port_mask; ++ ++ for (hek_offs = MVPP2_CLS_C2_HEK_OFF_PORT_ID; hek_offs >= ++ MVPP2_CLS_C2_HEK_OFF_BYTE0; hek_offs--) { ++ ret_code = mv_pp2x_cls_c2_tcam_byte_set(&c2_entry, ++ hek_offs, ++ hek_byte[hek_offs], ++ hek_byte_mask[hek_offs]); ++ if (ret_code) ++ return ret_code; ++ } ++ ++ /* Write C2 entry data to HW */ ++ ret_code = mv_pp2x_cls_c2_hw_write(hw, c2_hw_idx, &c2_entry); ++ if (ret_code) ++ return ret_code; ++ ++ return 0; ++} ++ ++/* C2 TCAM init */ ++int mv_pp2x_c2_init(struct platform_device *pdev, struct mv_pp2x_hw *hw) ++{ ++ int i; ++ ++ /* Invalid all C2 and QoS entries */ ++ mv_pp2x_cls_c2_hw_inv_all(hw); ++ ++ mv_pp2x_cls_c2_qos_hw_clear_all(hw); ++ ++ /* Set CLSC2_TCAM_CTRL to enable C2, or C2 does not work */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_CTRL_REG, ++ MVPP2_CLS2_TCAM_CTRL_EN_MASK); ++ ++ /* Allocate mem for c2 shadow */ ++ hw->c2_shadow = devm_kcalloc(&pdev->dev, 1, ++ sizeof(struct mv_pp2x_c2_shadow), ++ GFP_KERNEL); ++ if (!hw->c2_shadow) ++ return -ENOMEM; ++ ++ /* Init the rule idx to invalid value */ ++ for (i = 0; i < 8; i++) { ++ hw->c2_shadow->rule_idx_info[i].vlan_pri_idx = ++ MVPP2_CLS_C2_TCAM_SIZE; ++ hw->c2_shadow->rule_idx_info[i].dscp_pri_idx = ++ MVPP2_CLS_C2_TCAM_SIZE; ++ hw->c2_shadow->rule_idx_info[i].default_rule_idx = ++ MVPP2_CLS_C2_TCAM_SIZE; ++ } ++ hw->c2_shadow->c2_tcam_free_start = 0; ++ ++ return 0; ++} ++ ++static int mv_pp2x_c2_rule_add(struct mv_pp2x_port *port, ++ struct mv_pp2x_c2_add_entry *c2_add_entry) ++{ ++ int ret, lkp_type, c2_index = 0; ++ bool first_free_update = false; ++ struct mv_pp2x_c2_rule_idx *rule_idx; ++ ++ rule_idx = &port->priv->hw.c2_shadow->rule_idx_info[port->id]; ++ ++ if (!port || !c2_add_entry) ++ return -EINVAL; ++ ++ lkp_type = c2_add_entry->lkp_type; ++ /* Write rule in C2 TCAM */ ++ if (lkp_type == MVPP2_CLS_LKP_VLAN_PRI) { ++ if (rule_idx->vlan_pri_idx == MVPP2_CLS_C2_TCAM_SIZE) { ++ /* If the C2 rule is new, apply a free c2 rule index */ ++ c2_index = ++ port->priv->hw.c2_shadow->c2_tcam_free_start; ++ first_free_update = true; ++ } else { ++ /* If the C2 rule is exist one, ++ * take the C2 index from shadow ++ */ ++ c2_index = rule_idx->vlan_pri_idx; ++ first_free_update = false; ++ } ++ } else if (lkp_type == MVPP2_CLS_LKP_DSCP_PRI) { ++ if (rule_idx->dscp_pri_idx == MVPP2_CLS_C2_TCAM_SIZE) { ++ c2_index = ++ port->priv->hw.c2_shadow->c2_tcam_free_start; ++ first_free_update = true; ++ } else { ++ c2_index = rule_idx->dscp_pri_idx; ++ first_free_update = false; ++ } ++ } else if (lkp_type == MVPP2_CLS_LKP_DEFAULT) { ++ if (rule_idx->default_rule_idx == MVPP2_CLS_C2_TCAM_SIZE) { ++ c2_index = ++ port->priv->hw.c2_shadow->c2_tcam_free_start; ++ first_free_update = true; ++ } else { ++ c2_index = rule_idx->default_rule_idx; ++ first_free_update = false; ++ } ++ } else { ++ return -EINVAL; ++ } ++ ++ /* Write C2 TCAM HW */ ++ ret = mv_pp2x_c2_tcam_set(&port->priv->hw, c2_add_entry, c2_index); ++ if (ret) ++ return ret; ++ ++ /* Update first free rule */ ++ if (first_free_update) ++ port->priv->hw.c2_shadow->c2_tcam_free_start++; ++ ++ /* Update shadow */ ++ if (lkp_type == MVPP2_CLS_LKP_VLAN_PRI) ++ rule_idx->vlan_pri_idx = c2_index; ++ else if (lkp_type == MVPP2_CLS_LKP_DSCP_PRI) ++ rule_idx->dscp_pri_idx = c2_index; ++ else if (lkp_type == MVPP2_CLS_LKP_DEFAULT) ++ rule_idx->default_rule_idx = c2_index; ++ ++ return 0; ++} ++ ++/* Fill the qos table with queue */ ++static void mv_pp2x_cls_c2_qos_tbl_fill(struct mv_pp2x_port *port, ++ u8 tbl_sel, u8 tbl_id, u8 start_queue) ++{ ++ struct mv_pp2x_cls_c2_qos_entry qos_entry; ++ u32 pri, line_num; ++ u8 cos_value, cos_queue, queue; ++ ++ if (tbl_sel == MVPP2_QOS_TBL_SEL_PRI) ++ line_num = MVPP2_QOS_TBL_LINE_NUM_PRI; ++ else ++ line_num = MVPP2_QOS_TBL_LINE_NUM_DSCP; ++ ++ memset(&qos_entry, 0, sizeof(struct mv_pp2x_cls_c2_qos_entry)); ++ qos_entry.tbl_id = tbl_id; ++ qos_entry.tbl_sel = tbl_sel; ++ ++ /* Fill the QoS dscp/pbit table */ ++ for (pri = 0; pri < line_num; pri++) { ++ /* cos_value equal to dscp/8 or pbit value */ ++ cos_value = ((tbl_sel == MVPP2_QOS_TBL_SEL_PRI) ? ++ pri : (pri / 8)); ++ /* each nibble of pri_map stands for a cos-value, ++ * nibble value is the queue ++ */ ++ cos_queue = mv_pp2x_cosval_queue_map(port, cos_value); ++ qos_entry.tbl_line = pri; ++ /* map cos queue to physical queue */ ++ /* Physical queue contains 2 parts: port ID and CPU ID, ++ * CPU ID will be used in RSS ++ */ ++ queue = start_queue + cos_queue; ++ mv_pp2x_cls_c2_qos_queue_set(&qos_entry, queue); ++ mv_pp2x_cls_c2_qos_hw_write(&port->priv->hw, &qos_entry); ++ } ++} ++ ++static void mv_pp2x_cls_c2_entry_common_set(struct mv_pp2x_c2_add_entry *entry, ++ u8 port, ++ u8 lkp_type) ++{ ++ memset(entry, 0, sizeof(struct mv_pp2x_c2_add_entry)); ++ /* Port info */ ++ entry->port.port_type = MVPP2_SRC_PORT_TYPE_PHY; ++ entry->port.port_value = (1 << port); ++ entry->port.port_mask = 0xff; ++ /* Lookup type */ ++ entry->lkp_type = lkp_type; ++ entry->lkp_type_mask = 0x3F; ++ /* Action info */ ++ entry->action.color_act = MVPP2_COLOR_ACTION_TYPE_NO_UPDT_LOCK; ++ entry->action.pri_act = MVPP2_ACTION_TYPE_NO_UPDT_LOCK; ++ entry->action.dscp_act = MVPP2_ACTION_TYPE_NO_UPDT_LOCK; ++ entry->action.q_low_act = MVPP2_ACTION_TYPE_UPDT_LOCK; ++ entry->action.q_high_act = MVPP2_ACTION_TYPE_UPDT_LOCK; ++ entry->action.rss_act = MVPP2_ACTION_TYPE_UPDT_LOCK; ++ /* To CPU */ ++ entry->action.frwd_act = MVPP2_FRWD_ACTION_TYPE_SWF_LOCK; ++} ++ ++/* C2 rule set */ ++int mv_pp2x_cls_c2_rule_set(struct mv_pp2x_port *port, u8 start_queue) ++{ ++ struct mv_pp2x_c2_add_entry c2_init_entry; ++ int ret; ++ u8 cos_value, cos_queue, queue, lkp_type; ++ ++ /* QoS of pbit rule */ ++ for (lkp_type = MVPP2_CLS_LKP_VLAN_PRI; lkp_type <= ++ MVPP2_CLS_LKP_DEFAULT; lkp_type++) { ++ /* Set common part of C2 rule */ ++ mv_pp2x_cls_c2_entry_common_set(&c2_init_entry, ++ port->id, ++ lkp_type); ++ ++ /* QoS info */ ++ if (lkp_type != MVPP2_CLS_LKP_DEFAULT) { ++ u8 tbl_sel; ++ ++ /* QoS info from C2 QoS table */ ++ /* Set the QoS table index equal to port ID */ ++ c2_init_entry.qos_info.qos_tbl_index = port->id; ++ c2_init_entry.qos_info.q_low_src = ++ MVPP2_QOS_SRC_DSCP_PBIT_TBL; ++ c2_init_entry.qos_info.q_high_src = ++ MVPP2_QOS_SRC_DSCP_PBIT_TBL; ++ if (lkp_type == MVPP2_CLS_LKP_VLAN_PRI) { ++ c2_init_entry.qos_info.qos_tbl_type = ++ MVPP2_QOS_TBL_SEL_PRI; ++ tbl_sel = MVPP2_QOS_TBL_SEL_PRI; ++ } else if (lkp_type == MVPP2_CLS_LKP_DSCP_PRI) { ++ c2_init_entry.qos_info.qos_tbl_type = ++ MVPP2_QOS_TBL_SEL_DSCP; ++ tbl_sel = MVPP2_QOS_TBL_SEL_DSCP; ++ } ++ /* Fill qos table */ ++ mv_pp2x_cls_c2_qos_tbl_fill(port, ++ tbl_sel, ++ port->id, ++ start_queue); ++ } else { ++ /* QoS info from C2 action table */ ++ c2_init_entry.qos_info.q_low_src = ++ MVPP2_QOS_SRC_ACTION_TBL; ++ c2_init_entry.qos_info.q_high_src = ++ MVPP2_QOS_SRC_ACTION_TBL; ++ cos_value = port->cos_cfg.default_cos; ++ cos_queue = mv_pp2x_cosval_queue_map(port, cos_value); ++ /* map to physical queue */ ++ /* Physical queue contains 2 parts: port ID and CPU ID, ++ * CPU ID will be used in RSS ++ */ ++ queue = start_queue + cos_queue; ++ c2_init_entry.qos_value.q_low = ((u16)queue) & ++ ((1 << MVPP2_CLS2_ACT_QOS_ATTR_QL_BITS) - 1); ++ c2_init_entry.qos_value.q_high = ((u16)queue) >> ++ MVPP2_CLS2_ACT_QOS_ATTR_QL_BITS; ++ } ++ /* RSS En in PP22 */ ++ c2_init_entry.rss_en = port->rss_cfg.rss_en; ++ ++ /* Add rule to C2 TCAM */ ++ ret = mv_pp2x_c2_rule_add(port, &c2_init_entry); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cls_c2_rule_set); ++ ++/* The function get the queue in the C2 rule with input index */ ++u8 mv_pp2x_cls_c2_rule_queue_get(struct mv_pp2x_hw *hw, u32 rule_idx) ++{ ++ u32 reg_val; ++ u8 queue; ++ ++ /* Write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, rule_idx); ++ ++ /* Read Reg CLSC2_ATTR0 */ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_ACT_QOS_ATTR_REG); ++ queue = (reg_val & (MVPP2_CLS2_ACT_QOS_ATTR_QL_MASK | ++ MVPP2_CLS2_ACT_QOS_ATTR_QH_MASK)) >> ++ MVPP2_CLS2_ACT_QOS_ATTR_QL_OFF; ++ return queue; ++} ++ ++/* The function set the qos queue in one C2 rule */ ++void mv_pp2x_cls_c2_rule_queue_set(struct mv_pp2x_hw *hw, u32 rule_idx, ++ u8 queue) ++{ ++ u32 reg_val; ++ ++ /* Write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, rule_idx); ++ ++ /* Read Reg CLSC2_ATTR0 */ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_ACT_QOS_ATTR_REG); ++ /* Update Value */ ++ reg_val &= (~(MVPP2_CLS2_ACT_QOS_ATTR_QL_MASK | ++ MVPP2_CLS2_ACT_QOS_ATTR_QH_MASK)); ++ reg_val |= (((u32)queue) << MVPP2_CLS2_ACT_QOS_ATTR_QL_OFF); ++ ++ /* Write Reg CLSC2_ATTR0 */ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_QOS_ATTR_REG, reg_val); ++} ++ ++/* The function get the queue in the pbit table entry */ ++u8 mv_pp2x_cls_c2_pbit_tbl_queue_get(struct mv_pp2x_hw *hw, u8 tbl_id, ++ u8 tbl_line) ++{ ++ u8 queue; ++ u32 reg_val = 0; ++ ++ /* write index reg */ ++ reg_val |= (tbl_line << MVPP2_CLS2_DSCP_PRI_INDEX_LINE_OFF); ++ reg_val |= (MVPP2_QOS_TBL_SEL_PRI << ++ MVPP2_CLS2_DSCP_PRI_INDEX_SEL_OFF); ++ reg_val |= (tbl_id << MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_OFF); ++ mv_pp2x_write(hw, MVPP2_CLS2_DSCP_PRI_INDEX_REG, reg_val); ++ /* Read Reg CLSC2_DSCP_PRI */ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_QOS_TBL_REG); ++ queue = (reg_val & MVPP2_CLS2_QOS_TBL_QUEUENUM_MASK) >> ++ MVPP2_CLS2_QOS_TBL_QUEUENUM_OFF; ++ ++ return queue; ++} ++ ++/* The function set the queue in the pbit table entry */ ++void mv_pp2x_cls_c2_pbit_tbl_queue_set(struct mv_pp2x_hw *hw, ++ u8 tbl_id, u8 tbl_line, u8 queue) ++{ ++ u32 reg_val = 0; ++ ++ /* write index reg */ ++ reg_val |= (tbl_line << MVPP2_CLS2_DSCP_PRI_INDEX_LINE_OFF); ++ reg_val |= ++ (MVPP2_QOS_TBL_SEL_PRI << MVPP2_CLS2_DSCP_PRI_INDEX_SEL_OFF); ++ reg_val |= (tbl_id << MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_OFF); ++ mv_pp2x_write(hw, MVPP2_CLS2_DSCP_PRI_INDEX_REG, reg_val); ++ ++ /* Read Reg CLSC2_DSCP_PRI */ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_QOS_TBL_REG); ++ reg_val &= (~MVPP2_CLS2_QOS_TBL_QUEUENUM_MASK); ++ reg_val |= (((u32)queue) << MVPP2_CLS2_QOS_TBL_QUEUENUM_OFF); ++ ++ /* Write Reg CLSC2_DSCP_PRI */ ++ mv_pp2x_write(hw, MVPP2_CLS2_QOS_TBL_REG, reg_val); ++} ++ ++/* RSS */ ++/* The function will set rss table entry */ ++int mv_pp22_rss_tbl_entry_set(struct mv_pp2x_hw *hw, ++ struct mv_pp22_rss_entry *rss) ++{ ++ unsigned int reg_val = 0; ++ ++ if (!rss || rss->sel > MVPP22_RSS_ACCESS_TBL) ++ return -EINVAL; ++ ++ if (rss->sel == MVPP22_RSS_ACCESS_POINTER) { ++ if (rss->u.pointer.rss_tbl_ptr >= MVPP22_RSS_TBL_NUM) ++ return -EINVAL; ++ /* Write index */ ++ reg_val |= rss->u.pointer.rxq_idx << ++ MVPP22_RSS_IDX_RXQ_NUM_OFF; ++ mv_pp2x_write(hw, MVPP22_RSS_IDX_REG, reg_val); ++ /* Write entry */ ++ reg_val &= (~MVPP22_RSS_RXQ2RSS_TBL_POINT_MASK); ++ reg_val |= rss->u.pointer.rss_tbl_ptr << ++ MVPP22_RSS_RXQ2RSS_TBL_POINT_OFF; ++ mv_pp2x_write(hw, MVPP22_RSS_RXQ2RSS_TBL_REG, reg_val); ++ } else if (rss->sel == MVPP22_RSS_ACCESS_TBL) { ++ if (rss->u.entry.tbl_id >= MVPP22_RSS_TBL_NUM || ++ rss->u.entry.tbl_line >= MVPP22_RSS_TBL_LINE_NUM || ++ rss->u.entry.width >= MVPP22_RSS_WIDTH_MAX) ++ return -EINVAL; ++ /* Write index */ ++ reg_val |= (rss->u.entry.tbl_line << ++ MVPP22_RSS_IDX_ENTRY_NUM_OFF | ++ rss->u.entry.tbl_id << MVPP22_RSS_IDX_TBL_NUM_OFF); ++ mv_pp2x_write(hw, MVPP22_RSS_IDX_REG, reg_val); ++ /* Write entry */ ++ reg_val &= (~MVPP22_RSS_TBL_ENTRY_MASK); ++ reg_val |= (rss->u.entry.rxq << MVPP22_RSS_TBL_ENTRY_OFF); ++ mv_pp2x_write(hw, MVPP22_RSS_TBL_ENTRY_REG, reg_val); ++ reg_val &= (~MVPP22_RSS_WIDTH_MASK); ++ reg_val |= (rss->u.entry.width << MVPP22_RSS_WIDTH_OFF); ++ mv_pp2x_write(hw, MVPP22_RSS_WIDTH_REG, reg_val); ++ } ++ return 0; ++} ++ ++/* The function will get rss table entry */ ++int mv_pp22_rss_tbl_entry_get(struct mv_pp2x_hw *hw, ++ struct mv_pp22_rss_entry *rss) ++{ ++ unsigned int reg_val = 0; ++ ++ if (!rss || rss->sel > MVPP22_RSS_ACCESS_TBL) ++ return -EINVAL; ++ ++ if (rss->sel == MVPP22_RSS_ACCESS_POINTER) { ++ /* Set index */ ++ reg_val |= (rss->u.pointer.rxq_idx << ++ MVPP22_RSS_IDX_RXQ_NUM_OFF); ++ mv_pp2x_write(hw, MVPP22_RSS_IDX_REG, reg_val); ++ /* Read entry */ ++ rss->u.pointer.rss_tbl_ptr = ++ mv_pp2x_read(hw, MVPP22_RSS_RXQ2RSS_TBL_REG) & ++ MVPP22_RSS_RXQ2RSS_TBL_POINT_MASK; ++ } else if (rss->sel == MVPP22_RSS_ACCESS_TBL) { ++ if (rss->u.entry.tbl_id >= MVPP22_RSS_TBL_NUM || ++ rss->u.entry.tbl_line >= MVPP22_RSS_TBL_LINE_NUM) ++ return -EINVAL; ++ /* Read index */ ++ reg_val |= (rss->u.entry.tbl_line << ++ MVPP22_RSS_IDX_ENTRY_NUM_OFF | ++ rss->u.entry.tbl_id << ++ MVPP22_RSS_IDX_TBL_NUM_OFF); ++ mv_pp2x_write(hw, MVPP22_RSS_IDX_REG, reg_val); ++ /* Read entry */ ++ rss->u.entry.rxq = mv_pp2x_read(hw, ++ MVPP22_RSS_TBL_ENTRY_REG) & ++ MVPP22_RSS_TBL_ENTRY_MASK; ++ rss->u.entry.width = mv_pp2x_read(hw, ++ MVPP22_RSS_WIDTH_REG) & ++ MVPP22_RSS_WIDTH_MASK; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp22_rss_tbl_entry_get); ++ ++/* The function allocate a rss table for each phisical rxq, ++ * they have same cos priority ++ */ ++int mv_pp22_rss_rxq_set(struct mv_pp2x_port *port, u32 cos_width) ++{ ++ int rxq; ++ struct mv_pp22_rss_entry rss_entry; ++ int cos_mask = ((1 << cos_width) - 1); ++ ++ if (port->priv->pp2_version == PPV21) ++ return 0; ++ ++ memset(&rss_entry, 0, sizeof(struct mv_pp22_rss_entry)); ++ ++ rss_entry.sel = MVPP22_RSS_ACCESS_POINTER; ++ ++ for (rxq = 0; rxq < port->num_rx_queues; rxq++) { ++ rss_entry.u.pointer.rxq_idx = port->rxqs[rxq]->id; ++ rss_entry.u.pointer.rss_tbl_ptr = ++ port->rxqs[rxq]->id & cos_mask; ++ if (mv_pp22_rss_tbl_entry_set(&port->priv->hw, &rss_entry)) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void mv_pp22_c2_rss_attr_set(struct mv_pp2x_hw *hw, u32 index, bool en) ++{ ++ int reg_val; ++ ++ /* write index reg */ ++ mv_pp2x_write(hw, MVPP2_CLS2_TCAM_IDX_REG, index); ++ /* Update rss_attr in reg CLSC2_ATTR2 */ ++ reg_val = mv_pp2x_read(hw, MVPP2_CLS2_ACT_DUP_ATTR_REG); ++ if (en) ++ reg_val |= MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_MASK; ++ else ++ reg_val &= (~MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_MASK); ++ ++ mv_pp2x_write(hw, MVPP2_CLS2_ACT_DUP_ATTR_REG, reg_val); ++} ++ ++void mv_pp22_rss_c2_enable(struct mv_pp2x_port *port, bool en) ++{ ++ int lkp_type; ++ int c2_index[MVPP2_CLS_LKP_MAX]; ++ struct mv_pp2x_c2_rule_idx *rule_idx; ++ ++ rule_idx = &port->priv->hw.c2_shadow->rule_idx_info[port->id]; ++ ++ /* Get the C2 index from shadow */ ++ c2_index[MVPP2_CLS_LKP_VLAN_PRI] = rule_idx->vlan_pri_idx; ++ c2_index[MVPP2_CLS_LKP_DSCP_PRI] = rule_idx->dscp_pri_idx; ++ c2_index[MVPP2_CLS_LKP_DEFAULT] = rule_idx->default_rule_idx; ++ ++ /* For lookup type of MVPP2_CLS_LKP_HASH, ++ * there is no corresponding C2 rule, so skip it ++ */ ++ for (lkp_type = MVPP2_CLS_LKP_VLAN_PRI; lkp_type < MVPP2_CLS_LKP_MAX; ++ lkp_type++) ++ mv_pp22_c2_rss_attr_set(&port->priv->hw, ++ c2_index[lkp_type], ++ en); ++} ++ ++/* Initialize Tx FIFO's */ ++void mv_pp2x_tx_fifo_size_set(struct mv_pp2x_hw *hw, u32 port_id, u32 val) ++{ ++ mv_pp2x_write(hw, ++ MVPP22_TX_FIFO_SIZE_REG(port_id), ++ val & MVPP22_TX_FIFO_SIZE_MASK); ++} ++ ++void mv_pp2x_tx_fifo_threshold_set(struct mv_pp2x_hw *hw, u32 port_id, u32 val) ++{ ++ mv_pp2x_write(hw, ++ MVPP22_TX_FIFO_THRESH_REG(port_id), ++ val & MVPP22_TX_FIFO_THRESH_MASK); ++} ++ ++/* Check number of buffers in BM pool */ ++int mv_pp2x_check_hw_buf_num(struct mv_pp2x *priv, struct mv_pp2x_bm_pool *bm_pool) ++{ ++ int buf_num = 0; ++ ++ buf_num += mv_pp2x_read(&priv->hw, MVPP2_BM_POOL_PTRS_NUM_REG(bm_pool->id)) ++ & MVPP22_BM_POOL_PTRS_NUM_MASK; ++ buf_num += mv_pp2x_read(&priv->hw, MVPP2_BM_BPPI_PTRS_NUM_REG(bm_pool->id)) ++ & MVPP2_BM_BPPI_PTR_NUM_MASK; ++ ++ /* HW has one buffer ready and is not reflected in "external + internal" counters */ ++ if (buf_num) ++ return (buf_num + 1); ++ else ++ return 0; ++} ++ ++/* Update Mvpp2x counter statistic */ ++void mv_pp2x_counters_stat_update(struct mv_pp2x_port *port, ++ struct gop_stat *gop_statistics) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int val, queue; ++ unsigned long flags; ++ int num_of_queue_on_port; ++ ++ spin_lock_irqsave(&port->mac_data.stats_spinlock, flags); ++ ++ val = mv_pp2x_read(hw, MV_PP2_OVERRUN_DROP_REG(port->id)); ++ gop_statistics->rx_ppv2_overrun += val; ++ gop_statistics->rx_total_err += val; ++ gop_statistics->rx_sw_drop += val; ++ ++ val = mv_pp2x_read(hw, MV_PP2_CLS_DROP_REG(port->id)); ++ gop_statistics->rx_cls_drop += val; ++ gop_statistics->rx_hw_drop += val; ++ ++ for (queue = port->first_rxq; queue < (port->first_rxq + ++ port->num_rx_queues); queue++) { ++ num_of_queue_on_port = queue % MVPP22_MAX_NUM_RXQ; ++ mv_pp2x_write(hw, MVPP2_CNT_IDX_REG, queue); ++ val = mv_pp2x_read(hw, MVPP2_RX_PKT_FULLQ_DROP_REG); ++ gop_statistics->rx_fullq_drop += val; ++ gop_statistics->rx_hw_drop += val; ++ gop_statistics->rx_perq_fullq_drop[num_of_queue_on_port] += val; ++ ++ val = mv_pp2x_read(hw, MVPP2_RX_PKT_EARLY_DROP_REG); ++ gop_statistics->rx_early_drop += val; ++ gop_statistics->rx_hw_drop += val; ++ gop_statistics->rx_perq_early_drop[num_of_queue_on_port] += val; ++ ++ val = mv_pp2x_read(hw, MVPP2_RX_PKT_BM_DROP_REG); ++ gop_statistics->rx_bm_drop += val; ++ gop_statistics->rx_hw_drop += val; ++ gop_statistics->rx_perq_bm_drop[num_of_queue_on_port] += val; ++ } ++ ++ spin_unlock_irqrestore(&port->mac_data.stats_spinlock, flags); ++} ++EXPORT_SYMBOL(mv_pp2x_counters_stat_update); ++ ++/* Clear Mvpp2x counter statistic */ ++void mv_pp2x_counters_stat_clear(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int queue; ++ ++ mv_pp2x_read(hw, MV_PP2_OVERRUN_DROP_REG(port->id)); ++ mv_pp2x_read(hw, MV_PP2_CLS_DROP_REG(port->id)); ++ ++ preempt_disable(); ++ for (queue = port->first_rxq; queue < (port->first_rxq + ++ port->num_rx_queues); queue++) { ++ mv_pp2x_write(hw, MVPP2_CNT_IDX_REG, queue); ++ mv_pp2x_read(hw, MVPP2_RX_PKT_FULLQ_DROP_REG); ++ mv_pp2x_read(hw, MVPP2_RX_PKT_EARLY_DROP_REG); ++ mv_pp2x_read(hw, MVPP2_RX_PKT_BM_DROP_REG); ++ } ++ preempt_enable(); ++} ++ ++/* Routine check if FW is busy */ ++int mv_pp2x_dying_gasp_conf_busy(struct mv_pp2x_port *port) ++{ ++ int val, timeout = 0; ++ ++ /* Check that update command wasn't set. ++ * If update command was set, wait for firmware dying gasp update done. ++ */ ++ while (timeout < MSS_CP_DG_MAX_TIMEOUT) { ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_CON_REG); ++ ++ if (!(val & MSS_CP_DG_UPD_COM)) ++ return 0; ++ usleep_range(10, 20); ++ timeout++; ++ } ++ ++ return -EBUSY; ++} ++ ++/* Routine notify FW about dying gasp configuration update */ ++void mv_pp2x_dying_gasp_update_notify(struct mv_pp2x_port *port) ++{ ++ int val; ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_CON_REG); ++ val |= MSS_CP_DG_UPD_COM; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_CON_REG, val); ++} ++ ++/* Routine to set dying gasp packet destination MAC address */ ++void mv_pp2x_dying_gasp_set_dst_mac(struct mv_pp2x_port *port, unsigned char *mac_addr) ++{ ++ int len; ++ u32 val; ++ u32 mac_low = 0, mac_high = 0; ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ pr_warn("Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ /* Set DA MAC, see Dying Gasp programing model */ ++ for (len = 0; len < ETH_ALEN; len++) { ++ if (len < BYTES_IN_PP_REG) ++ mac_low |= (((u32)mac_addr[len] & BYTE_MASK) << (len * BITS_PER_BYTE)); ++ else ++ mac_high |= (((u32)mac_addr[len] & BYTE_MASK) << ((len - BYTES_IN_PP_REG) * BITS_PER_BYTE)); ++ } ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_DA_MAC_LOW_REG(port->id), mac_low); ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_DA_HIGH_SA_LOW_MAC_REG(port->id)); ++ val &= ~MSS_CP_DG_DA_LOW_MASK; ++ val |= mac_high; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_DA_HIGH_SA_LOW_MAC_REG(port->id), val); ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} ++ ++/* Routine to set dying gasp packet source MAC address */ ++void mv_pp2x_dying_gasp_set_src_mac(struct mv_pp2x_port *port, unsigned char *mac_addr) ++{ ++ int len; ++ u32 val; ++ u32 mac_low = 0, mac_high = 0; ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ WARN(1, "Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ /* Set SA MAC, see Dying Gasp programing model */ ++ for (len = 0; len < ETH_ALEN; len++) { ++ if (len < BYTES_IN_GOP_REG) ++ mac_low |= (((u32)mac_addr[len] & BYTE_MASK) << ((len + BYTES_IN_PP_REG) * BITS_PER_BYTE)); ++ else ++ mac_high |= (((u32)mac_addr[len] & BYTE_MASK) << ((len - BYTES_IN_GOP_REG) * BITS_PER_BYTE)); ++ } ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_SA_HIGH_MAC_REG(port->id), mac_high); ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_DA_HIGH_SA_LOW_MAC_REG(port->id)); ++ val &= ~MSS_CP_DG_SA_HIGH_MASK; ++ val |= (mac_low << MSS_CP_DG_SA_HIGH_MASK_OFFS); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_DA_HIGH_SA_LOW_MAC_REG(port->id), val); ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} ++ ++/* Routine to set dying gasp packet EtherType */ ++void mv_pp2x_dying_gasp_set_ether_type(struct mv_pp2x_port *port, u32 ether_type) ++{ ++ u32 val; ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ WARN(1, "Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_MAC_ETH_T_VLAN_REG(port->id)); ++ val &= ~MSS_CP_DG_MAC_ETH_T_MASK; ++ val |= (ether_type & MSS_CP_DG_MAC_ETH_T_MASK); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_MAC_ETH_T_VLAN_REG(port->id), val); ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} ++ ++/* Routine to set dying gasp packet VLAN */ ++void mv_pp2x_dying_gasp_set_vlan(struct mv_pp2x_port *port, u32 vlan) ++{ ++ u32 val; ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ WARN(1, "Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_MAC_ETH_T_VLAN_REG(port->id)); ++ val &= ~MSS_CP_DG_MAC_VLAN_MASK; ++ val |= ((vlan << MSS_CP_DG_MAC_VLAN_MASK_OFFS) & MSS_CP_DG_MAC_VLAN_MASK); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_MAC_ETH_T_VLAN_REG(port->id), val); ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} ++ ++/* Routine to set dying gasp packet data patern */ ++void mv_pp2x_dying_gasp_set_data_patern(struct mv_pp2x_port *port, unsigned char *data, int data_size) ++{ ++ int i, j, val; ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ WARN(1, "Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ if (data_size > MSS_CP_DG_DP_MAX_SIZE) { ++ WARN(1, "Port (%d): Dying gasp maximum data patern size is %d bytes\n", ++ port->id, MSS_CP_DG_DP_MAX_SIZE); ++ data_size = MSS_CP_DG_DP_MAX_SIZE; ++ } ++ ++ for (i = 0; i < (data_size / BYTES_IN_PP_REG); i++) { ++ val = 0; ++ for (j = 0; j < BYTES_IN_PP_REG; j++) ++ val |= ((data[i * BYTES_IN_PP_REG + j] & BYTE_MASK) << BITS_PER_BYTE * j); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_DATA_PATTERN_REG(port->id, i), val); ++ } ++ ++ if (data_size % BYTES_IN_PP_REG) { ++ val = 0; ++ for (j = 0; j < (data_size % BYTES_IN_PP_REG); j++) ++ val |= ((data[i * BYTES_IN_PP_REG + j] & BYTE_MASK) << BITS_PER_BYTE * j); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_DATA_PATTERN_REG(port->id, i), val); ++ } ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} ++ ++/* Routine to set dying gasp packet size and number packets transmitted during dying gasp event */ ++void mv_pp2x_dying_gasp_set_packet_control(struct mv_pp2x_port *port, int packets_count, int packet_size) ++{ ++ int val; ++ ++ if (packet_size > MSS_CP_DG_PACK_MAX_SIZE) { ++ WARN(1, "Port (%d): Dying gasp maximum packet size is %d bytes\n", ++ port->id, MSS_CP_DG_PACK_MAX_SIZE); ++ packet_size = MSS_CP_DG_PACK_MAX_SIZE; ++ } ++ ++ if (packets_count > MSS_CP_DG_MAX_PACK_COUNT) { ++ WARN(1, "Port (%d): Dying gasp maximum packet count is %d packets\n", ++ port->id, MSS_CP_DG_MAX_PACK_COUNT); ++ packets_count = MSS_CP_DG_MAX_PACK_COUNT; ++ } ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ WARN(1, "Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ val = (packets_count << MSS_CP_DG_MAX_PACK_OFFS) | packet_size; ++ ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_PACK_CON_REG(port->id), val); ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} ++ ++/* Routine to enable/disable dying gasp per port */ ++void mv_pp2x_dying_gasp_control(struct mv_pp2x_port *port, bool en) ++{ ++ int val; ++ ++ if (mv_pp2x_dying_gasp_conf_busy(port)) { ++ WARN(1, "Port (%d): Dying gasp config is busy\n", port->id); ++ return; ++ } ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_DG_CON_REG); ++ ++ if (en) ++ val |= (1 << port->id); ++ else ++ val &= ~(1 << port->id); ++ ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_DG_CON_REG, val); ++ ++ mv_pp2x_dying_gasp_update_notify(port); ++} +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.h b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.h +new file mode 100644 +index 00000000..401560e +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw.h +@@ -0,0 +1,817 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#ifndef _MVPP2_HW_H_ ++#define _MVPP2_HW_H_ ++ ++#include ++#include ++#include ++ ++#include ++ ++static inline void mv_pp2x_cm3_write(struct mv_pp2x_hw *hw, u32 offset, u32 data) ++{ ++ void *reg_ptr = hw->gop.gop_110.cm3_base + offset; ++ ++ writel(data, reg_ptr); ++} ++ ++static inline u32 mv_pp2x_cm3_read(struct mv_pp2x_hw *hw, u32 offset) ++{ ++ void *reg_ptr = hw->gop.gop_110.cm3_base + offset; ++ ++ return readl(reg_ptr); ++} ++ ++static inline void mv_pp2x_write(struct mv_pp2x_hw *hw, u32 offset, u32 data) ++{ ++ void *reg_ptr = hw->cpu_base[0] + offset; ++ ++ writel(data, reg_ptr); ++} ++ ++static inline void mv_pp2x_relaxed_write(struct mv_pp2x_hw *hw, u32 offset, u32 data, ++ int cpu) ++{ ++ void *reg_ptr; ++ ++ cpu = hw->mv_pp2x_no_single_mode * cpu; ++ reg_ptr = hw->cpu_base[cpu] + offset; ++ ++ writel_relaxed(data, reg_ptr); ++} ++ ++static inline u32 mv_pp2x_read(struct mv_pp2x_hw *hw, u32 offset) ++{ ++ u32 val; ++ ++ void *reg_ptr = hw->cpu_base[0] + offset; ++ ++ val = readl(reg_ptr); ++ ++ return val; ++} ++ ++static inline u32 mv_pp2x_relaxed_read(struct mv_pp2x_hw *hw, u32 offset, int cpu) ++{ ++ u32 val; ++ void *reg_ptr; ++ ++ cpu = hw->mv_pp2x_no_single_mode * cpu; ++ ++ reg_ptr = hw->cpu_base[cpu] + offset; ++ val = readl_relaxed(reg_ptr); ++ return val; ++} ++ ++static inline void mv_pp22_thread_write(struct mv_pp2x_hw *hw, u32 sw_thread, ++ u32 offset, u32 data) ++{ ++ writel(data, hw->base + sw_thread * MVPP2_ADDR_SPACE_SIZE + offset); ++} ++ ++static inline u32 mv_pp22_thread_read(struct mv_pp2x_hw *hw, u32 sw_thread, ++ u32 offset) ++{ ++ return readl(hw->base + sw_thread * MVPP2_ADDR_SPACE_SIZE + offset); ++} ++ ++static inline void mv_pp22_thread_relaxed_write(struct mv_pp2x_hw *hw, ++ u32 sw_thread, ++ u32 offset, u32 data) ++{ ++ writel_relaxed(data, hw->base + sw_thread * MVPP2_ADDR_SPACE_SIZE + offset); ++} ++ ++static inline u32 mv_pp22_thread_relaxed_read(struct mv_pp2x_hw *hw, ++ u32 sw_thread, ++ u32 offset) ++{ ++ return readl_relaxed(hw->base + sw_thread * MVPP2_ADDR_SPACE_SIZE + offset); ++} ++ ++static inline void mv_pp21_isr_rx_group_write(struct mv_pp2x_hw *hw, int port, ++ int num_rx_queues) ++{ ++ mv_pp2x_write(hw, MVPP21_ISR_RXQ_GROUP_REG(port), num_rx_queues); ++} ++ ++static inline void mv_pp22_isr_rx_group_write(struct mv_pp2x_hw *hw, int port, ++ int sub_group, ++ int start_queue, ++ int num_rx_queues) ++{ ++ int val; ++ ++ val = (port << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET) | sub_group; ++ mv_pp2x_write(hw, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); ++ val = (num_rx_queues << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET) | ++ start_queue; ++ mv_pp2x_write(hw, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); ++} ++ ++/* Get number of physical egress port */ ++static inline int mv_pp2x_egress_port(struct mv_pp2x_port *port) ++{ ++ return MVPP2_MAX_TCONT + port->id; ++} ++ ++/* Get number of physical TXQ */ ++static inline int mv_pp2x_txq_phys(int port, int txq) ++{ ++ return (MVPP2_MAX_TCONT + port) * MVPP2_MAX_TXQ + txq; ++} ++ ++/* Rx descriptors helper methods */ ++ ++/* Get number of Rx descriptors occupied by received packets */ ++static inline int mv_pp2x_rxq_received(struct mv_pp2x_port *port, int rxq_id) ++{ ++ u32 val = mv_pp2x_read(&port->priv->hw, MVPP2_RXQ_STATUS_REG(rxq_id)); ++ ++ return val & MVPP2_RXQ_OCCUPIED_MASK; ++} ++ ++/* Get number of Rx descriptors occupied by received packets */ ++static inline int mv_pp2x_rxq_free(struct mv_pp2x_port *port, int rxq_id) ++{ ++ u32 val = mv_pp2x_read(&port->priv->hw, MVPP2_RXQ_STATUS_REG(rxq_id)); ++ ++ return ((val & MVPP2_RXQ_NON_OCCUPIED_MASK) >> ++ MVPP2_RXQ_NON_OCCUPIED_OFFSET); ++} ++ ++/* Update Rx queue status with the number of occupied and available ++ * Rx descriptor slots. ++ */ ++static inline void mv_pp2x_rxq_status_update(struct mv_pp2x_port *port, ++ int rxq_id, ++ int used_count, ++ int free_count) ++{ ++ /* Decrement the number of used descriptors and increment count ++ * increment the number of free descriptors. ++ */ ++ u32 val = used_count | (free_count << MVPP2_RXQ_NUM_NEW_OFFSET); ++ ++ mv_pp2x_write(&port->priv->hw, ++ MVPP2_RXQ_STATUS_UPDATE_REG(rxq_id), val); ++} ++ ++/* Get pointer to next RX descriptor to be processed by SW */ ++static inline struct mv_pp2x_rx_desc * ++mv_pp2x_rxq_next_desc_get(struct mv_pp2x_rx_queue *rxq) ++{ ++ int rx_desc = rxq->next_desc_to_proc; ++ ++ rxq->next_desc_to_proc = MVPP2_QUEUE_NEXT_DESC(rxq, rx_desc); ++ /* For uneven descriptors, fetch next two descriptors (2*32B) */ ++ if (rx_desc & 0x1) ++ prefetch(rxq->first_desc + rxq->next_desc_to_proc); ++ return (rxq->first_desc + rx_desc); ++} ++ ++static inline struct mv_pp2x_rx_queue *mv_pp2x_get_rx_queue( ++ struct mv_pp2x_port *port, u32 cause) ++{ ++ int rx_queue = fls(cause) - 1; ++ ++ return port->rxqs[rx_queue]; ++} ++ ++static inline struct mv_pp2x_tx_queue *mv_pp2x_get_tx_queue( ++ struct mv_pp2x_port *port, u32 cause) ++{ ++ int tx_queue = fls(cause) - 1; ++ ++ return port->txqs[tx_queue]; ++} ++ ++static inline dma_addr_t mv_pp2x_bm_phys_addr_get(struct mv_pp2x_hw *hw, u32 pool) ++{ ++ dma_addr_t val; ++ ++ val = mv_pp2x_read(hw, MVPP2_BM_PHY_ALLOC_REG(pool)); ++ ++ /* Disregard BM_ADDR_HIGH_ALLOC if Buffer Manager failed to return buffer */ ++ if (!val) ++ return 0; ++ ++#ifdef CONFIG_PHYS_ADDR_T_64BIT ++ { ++ u64 val2; ++ ++ val2 = mv_pp2x_read(hw, MVPP22_BM_PHY_VIRT_HIGH_ALLOC_REG); ++ val2 &= MVPP22_BM_PHY_HIGH_ALLOC_MASK; ++ val |= (val2 << 32); ++ } ++#endif ++ ++ return val; ++} ++ ++static inline void mv_pp2x_bm_hw_pool_create(struct mv_pp2x_hw *hw, u32 pool, ++ dma_addr_t pool_addr, int size) ++{ ++ u32 val; ++ ++ mv_pp2x_write(hw, MVPP2_BM_POOL_BASE_ADDR_REG(pool), ++ lower_32_bits(pool_addr)); ++#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && defined(CONFIG_PHYS_ADDR_T_64BIT) ++ val = mv_pp2x_read(hw, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG); ++ val &= ~MVPP22_BM_POOL_BASE_ADDR_HIGH_MASK; ++ val |= (upper_32_bits(pool_addr) & MVPP22_ADDR_HIGH_MASK); ++ mv_pp2x_write(hw, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG, val); ++#endif ++ mv_pp2x_write(hw, MVPP2_BM_POOL_SIZE_REG(pool), size); ++ ++ val = mv_pp2x_read(hw, MVPP2_BM_POOL_CTRL_REG(pool)); ++ val |= MVPP2_BM_START_MASK; ++ val &= ~MVPP2_BM_LOW_THRESH_MASK; ++ val &= ~MVPP2_BM_HIGH_THRESH_MASK; ++ ++ /* Set 8 Pools BPPI threshold if BM underrun protection feature were enabled */ ++ if (hw->mv_bm_underrun_protect) { ++ val |= MVPP2_BM_LOW_THRESH_VALUE(MVPP23_BM_BPPI_8POOL_LOW_THRESH); ++ val |= MVPP2_BM_HIGH_THRESH_VALUE(MVPP23_BM_BPPI_8POOL_HIGH_THRESH); ++ } else { ++ val |= MVPP2_BM_LOW_THRESH_VALUE(MVPP2_BM_BPPI_LOW_THRESH); ++ val |= MVPP2_BM_HIGH_THRESH_VALUE(MVPP2_BM_BPPI_HIGH_THRESH); ++ } ++ ++ mv_pp2x_write(hw, MVPP2_BM_POOL_CTRL_REG(pool), val); ++} ++ ++static inline void mv_pp2x_bm_pool_put_virtual(struct mv_pp2x_hw *hw, u32 pool, ++ dma_addr_t buf_phys_addr, ++ u8 *buf_virt_addr, int cpu) ++{ ++#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && defined(CONFIG_PHYS_ADDR_T_64BIT) ++ mv_pp2x_relaxed_write(hw, MVPP22_BM_PHY_VIRT_HIGH_RLS_REG, ++ upper_32_bits(buf_phys_addr), cpu); ++#endif ++ ++ mv_pp2x_relaxed_write(hw, MVPP2_BM_VIRT_RLS_REG, ++ lower_32_bits((uintptr_t)buf_virt_addr), cpu); ++ ++ mv_pp2x_relaxed_write(hw, MVPP2_BM_PHY_RLS_REG(pool), ++ lower_32_bits(buf_phys_addr), cpu); ++} ++ ++/* Release buffer to BM */ ++static inline void mv_pp2x_bm_pool_put(struct mv_pp2x_hw *hw, u32 pool, ++ dma_addr_t buf_phys_addr, int cpu) ++{ ++#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && defined(CONFIG_PHYS_ADDR_T_64BIT) ++ mv_pp2x_relaxed_write(hw, MVPP22_BM_PHY_VIRT_HIGH_RLS_REG, ++ upper_32_bits(buf_phys_addr), cpu); ++#endif ++ ++ mv_pp2x_relaxed_write(hw, MVPP2_BM_PHY_RLS_REG(pool), ++ lower_32_bits(buf_phys_addr), cpu); ++} ++ ++/* Release multicast buffer */ ++static inline void mv_pp2x_bm_pool_mc_put(struct mv_pp2x_port *port, int pool, ++ u32 buf_phys_addr, ++ u32 buf_virt_addr, ++ int mc_id, int cpu) ++{ ++ u32 val = 0; ++ ++ val |= (mc_id & MVPP21_BM_MC_ID_MASK); ++ mv_pp2x_write(&port->priv->hw, MVPP21_BM_MC_RLS_REG, val); ++ /*TODO : YuvalC, this is just workaround to compile. ++ * Need to handle mv_pp2x_buff_hdr_rx(). ++ */ ++ mv_pp2x_bm_pool_put(&port->priv->hw, pool, ++ (dma_addr_t)(buf_phys_addr | ++ MVPP2_BM_PHY_RLS_MC_BUFF_MASK), cpu); ++} ++ ++static inline void mv_pp2x_port_interrupts_enable(struct mv_pp2x_port *port) ++{ ++ int sw_thread_mask = 0, i; ++ struct queue_vector *q_vec = port->q_vector; ++ ++ for (i = 0; i < port->num_qvector; i++) ++ sw_thread_mask |= q_vec[i].sw_thread_mask; ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_ENABLE_INTERRUPT(sw_thread_mask)); ++} ++ ++static inline void mv_pp22_port_uio_interrupts_enable(struct mv_pp2x_port *port) ++{ ++ int sw_thread_mask = 0, i; ++ struct uio_queue_vector *q_vec = &port->uio.q_vector[0]; ++ ++ for (i = 0; i < port->uio.num_qvector; i++) ++ sw_thread_mask |= (1 << q_vec[i].sw_thread_id); ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_ENABLE_INTERRUPT(sw_thread_mask)); ++} ++ ++static inline void mv_pp2x_port_interrupts_disable(struct mv_pp2x_port *port) ++{ ++ int sw_thread_mask = 0, i; ++ struct queue_vector *q_vec = port->q_vector; ++ ++ for (i = 0; i < port->num_qvector; i++) ++ sw_thread_mask |= q_vec[i].sw_thread_mask; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_DISABLE_INTERRUPT(sw_thread_mask)); ++} ++ ++static inline void mv_pp22_port_uio_interrupts_disable(struct mv_pp2x_port *port) ++{ ++ int sw_thread_mask = 0, i; ++ struct uio_queue_vector *q_vec = &port->uio.q_vector[0]; ++ ++ for (i = 0; i < port->uio.num_qvector; i++) ++ sw_thread_mask |= (1 << q_vec[i].sw_thread_id); ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_DISABLE_INTERRUPT(sw_thread_mask)); ++} ++ ++static inline void mv_pp2x_qvector_interrupt_enable(struct queue_vector *q_vec) ++{ ++ struct mv_pp2x_port *port = q_vec->parent; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_ENABLE_INTERRUPT(q_vec->sw_thread_mask)); ++} ++ ++static inline void mv_pp2x_qvector_interrupt_disable(struct queue_vector *q_vec) ++{ ++ struct mv_pp2x_port *port = q_vec->parent; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id), ++ MVPP2_ISR_DISABLE_INTERRUPT(q_vec->sw_thread_mask)); ++} ++ ++static inline u32 mv_pp2x_qvector_interrupt_state_get(struct queue_vector ++ *q_vec) ++{ ++ struct mv_pp2x_port *port = q_vec->parent; ++ u32 state; ++ ++ state = mv_pp2x_read(&port->priv->hw, MVPP2_ISR_ENABLE_REG(port->id)); ++ state &= MVPP2_ISR_ENABLE_INTERRUPT(q_vec->sw_thread_mask); ++ return state; ++} ++ ++static inline int mv_pp2x_txq_sent_desc_proc(struct mv_pp2x_port *port, ++ int sw_thread, ++ u8 txq_id) ++{ ++ u32 val; ++ ++ /* Reading status reg resets transmitted descriptor counter */ ++ if (port->priv->pp2_version == PPV21) { ++ sw_thread = 0; ++ val = mv_pp22_thread_relaxed_read(&port->priv->hw, ++ sw_thread, ++ MVPP21_TXQ_SENT_REG(txq_id)); ++ return (val & MVPP21_TRANSMITTED_COUNT_MASK) >> ++ MVPP21_TRANSMITTED_COUNT_OFFSET; ++ } ++ else { ++ val = mv_pp22_thread_relaxed_read(&port->priv->hw, ++ sw_thread, ++ MVPP22_TXQ_SENT_REG(txq_id)); ++ return (val & MVPP22_TRANSMITTED_COUNT_MASK) >> ++ MVPP22_TRANSMITTED_COUNT_OFFSET; ++ } ++} ++ ++static inline void mv_pp2x_txq_desc_put(struct mv_pp2x_tx_queue *txq) ++{ ++ if (txq->next_desc_to_proc == 0) ++ txq->next_desc_to_proc = txq->last_desc - 1; ++ else ++ txq->next_desc_to_proc--; ++} ++ ++static inline void mv_pp2x_txq_sent_counter_clear(void *arg) ++{ ++ struct mv_pp2x_port *port = arg; ++ int queue; ++ ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ int id = port->txqs[queue]->id; ++ ++ if (port->priv->pp2_version == PPV21) ++ mv_pp2x_read(&port->priv->hw, ++ MVPP21_TXQ_SENT_REG(id)); ++ else ++ mv_pp2x_read(&port->priv->hw, ++ MVPP22_TXQ_SENT_REG(id)); ++ } ++} ++ ++static inline u8 *mv_pp21_rxdesc_cookie_get( ++ struct mv_pp2x_rx_desc *rx_desc) ++{ ++ return((u8 *)((uintptr_t)rx_desc->u.pp21.buf_cookie)); ++} ++ ++static inline dma_addr_t mv_pp21_rxdesc_phys_addr_get( ++ struct mv_pp2x_rx_desc *rx_desc) ++{ ++ return (dma_addr_t)rx_desc->u.pp21.buf_phys_addr; ++} ++ ++/*YuvalC: Below functions are intended to support both aarch64 & aarch32 */ ++static inline u8 *mv_pp22_rxdesc_cookie_get( ++ struct mv_pp2x_rx_desc *rx_desc) ++{ ++ return((u8 *)((uintptr_t) ++ (rx_desc->u.pp22.buf_cookie_bm_qset_cls_info & ++ DMA_BIT_MASK(40)))); ++} ++ ++static inline dma_addr_t mv_pp22_rxdesc_phys_addr_get( ++ struct mv_pp2x_rx_desc *rx_desc) ++{ ++ return((dma_addr_t) ++ (rx_desc->u.pp22.buf_phys_addr_key_hash & ++ DMA_BIT_MASK(40))); ++} ++ ++static inline struct sk_buff *mv_pp21_txdesc_cookie_get( ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ return((struct sk_buff *)((uintptr_t)tx_desc->u.pp21.buf_cookie)); ++} ++ ++static inline dma_addr_t mv_pp21_txdesc_phys_addr_get( ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ return (dma_addr_t)tx_desc->u.pp21.buf_phys_addr; ++} ++ ++static inline struct sk_buff *mv_pp22_txdesc_cookie_get( ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ return((struct sk_buff *)((uintptr_t) ++ (tx_desc->u.pp22.buf_cookie_bm_qset_hw_cmd3 & ++ DMA_BIT_MASK(40)))); ++} ++ ++static inline dma_addr_t mv_pp22_txdesc_phys_addr_get( ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ return((dma_addr_t) ++ (tx_desc->u.pp22.buf_phys_addr_hw_cmd2 & DMA_BIT_MASK(40))); ++} ++ ++static inline dma_addr_t mv_pp2x_txdesc_phys_addr_get( ++ enum mvppv2_version pp2_ver, struct mv_pp2x_tx_desc *tx_desc) ++{ ++ if (pp2_ver == PPV21) ++ return mv_pp21_txdesc_phys_addr_get(tx_desc); ++ ++ return mv_pp22_txdesc_phys_addr_get(tx_desc); ++} ++ ++static inline void mv_pp21_txdesc_phys_addr_set(dma_addr_t phys_addr, ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ tx_desc->u.pp21.buf_phys_addr = phys_addr; ++} ++ ++static inline void mv_pp22_txdesc_phys_addr_set(dma_addr_t phys_addr, ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ u64 *buf_phys_addr_p = &tx_desc->u.pp22.buf_phys_addr_hw_cmd2; ++ ++#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && defined(CONFIG_PHYS_ADDR_T_64BIT) ++ *buf_phys_addr_p &= ~(DMA_BIT_MASK(40)); ++ *buf_phys_addr_p |= phys_addr & DMA_BIT_MASK(40); ++#else ++ *((dma_addr_t *)buf_phys_addr_p) = phys_addr; ++ *((u8 *)buf_phys_addr_p + sizeof(dma_addr_t)) = 0; /*5th byte*/ ++#endif ++} ++ ++static inline void mv_pp2x_txdesc_phys_addr_set(enum mvppv2_version pp2_ver, ++ dma_addr_t phys_addr, struct mv_pp2x_tx_desc *tx_desc) ++{ ++ if (pp2_ver == PPV21) ++ mv_pp21_txdesc_phys_addr_set(phys_addr, tx_desc); ++ else ++ mv_pp22_txdesc_phys_addr_set(phys_addr, tx_desc); ++} ++ ++int mv_pp2x_ptr_validate(const void *ptr); ++int mv_pp2x_range_validate(int value, int min, int max); ++ ++int mv_pp2x_prs_hw_read(struct mv_pp2x_hw *hw, struct mv_pp2x_prs_entry *pe); ++ ++int mv_pp2x_prs_default_init(struct platform_device *pdev, ++ struct mv_pp2x_hw *hw); ++void mv_pp2x_prs_mac_uc_promisc_set(struct mv_pp2x_hw *hw, int port, bool add); ++void mv_pp2x_prs_mac_mc_promisc_set(struct mv_pp2x_hw *hw, int port, bool add); ++int mv_pp2x_prs_mac_da_accept(struct mv_pp2x_port *port, ++ const u8 *da, bool add); ++int mv_pp2x_prs_vid_entry_accept(struct net_device *dev, u16 proto, u16 vid, bool add); ++int mv_pp2x_prs_def_flow(struct mv_pp2x_port *port); ++int mv_pp2x_prs_flow_set(struct mv_pp2x_port *port); ++void mv_pp2x_prs_mac_entry_del(struct mv_pp2x_port *port, ++ enum mv_pp2x_l2_cast l2_cast, ++ enum mv_pp2x_mac_del_option op); ++int mv_pp2x_prs_tag_mode_set(struct mv_pp2x_hw *hw, int port, int type); ++int mv_pp2x_prs_update_mac_da(struct net_device *dev, const u8 *da); ++void mv_pp2x_prs_flow_id_attr_init(void); ++int mv_pp2x_prs_flow_id_attr_get(int flow_id); ++ ++int mv_pp2x_cls_init(struct platform_device *pdev, struct mv_pp2x_hw *hw); ++void mv_pp2x_cls_port_config(struct mv_pp2x_port *port); ++void mv_pp2x_cls_config(struct mv_pp2x_hw *hw); ++void mv_pp2x_cls_oversize_rxq_set(struct mv_pp2x_port *port); ++void mv_pp2x_cls_lookup_read(struct mv_pp2x_hw *hw, int lkpid, int way, ++ struct mv_pp2x_cls_lookup_entry *le); ++void mv_pp2x_cls_flow_tbl_temp_copy(struct mv_pp2x_hw *hw, int lkpid, ++ int *temp_flow_idx); ++void mv_pp2x_cls_lkp_flow_set(struct mv_pp2x_hw *hw, int lkpid, int way, ++ int flow_idx); ++void mv_pp2x_cls_flow_port_add(struct mv_pp2x_hw *hw, int index, int port_id); ++void mv_pp2x_cls_flow_port_del(struct mv_pp2x_hw *hw, int index, int port_id); ++ ++void mv_pp2x_txp_max_tx_size_set(struct mv_pp2x_port *port); ++void mv_pp2x_tx_done_time_coal_set(struct mv_pp2x_port *port, u32 usec); ++void mv_pp21_gmac_max_rx_size_set(struct mv_pp2x_port *port); ++ ++int mv_pp2x_txq_pend_desc_num_get(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_queue *txq); ++u32 mv_pp2x_txq_desc_csum(int l3_offs, int l3_proto, ++ int ip_hdr_len, int l4_proto); ++struct mv_pp2x_tx_desc *mv_pp2x_txq_next_desc_get( ++ struct mv_pp2x_aggr_tx_queue *aggr_txq); ++struct mv_pp2x_tx_desc *mv_pp2x_txq_prev_desc_get( ++ struct mv_pp2x_aggr_tx_queue *aggr_txq); ++int mv_pp2x_txq_alloc_reserved_desc(struct mv_pp2x *priv, ++ struct mv_pp2x_tx_queue *txq, ++ int num, int cpu); ++int mv_pp2x_aggr_desc_num_read(struct mv_pp2x *priv, int cpu); ++int mv_pp2x_aggr_desc_num_check(struct mv_pp2x *priv, ++ struct mv_pp2x_aggr_tx_queue *aggr_txq, ++ int num, int cpu); ++void mv_pp2x_rxq_offset_set(struct mv_pp2x_port *port, ++ int prxq, int offset); ++void mv_pp2x_bm_pool_bufsize_set(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_bm_pool *bm_pool, ++ int buf_size); ++void mv_pp2x_pool_refill(struct mv_pp2x *priv, u32 pool, ++ dma_addr_t phys_addr, int cpu); ++ ++void mv_pp2x_pool_refill_virtual(struct mv_pp2x *priv, u32 pool, ++ dma_addr_t phys_addr, u8 *cookie); ++void mv_pp21_rxq_long_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int long_pool); ++void mv_pp21_rxq_short_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int short_pool); ++ ++void mv_pp22_rxq_long_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int long_pool); ++void mv_pp22_rxq_short_pool_set(struct mv_pp2x_hw *hw, ++ int prxq, int short_pool); ++ ++void mv_pp21_port_mii_set(struct mv_pp2x_port *port); ++void mv_pp21_port_fc_adv_enable(struct mv_pp2x_port *port); ++void mv_pp21_port_enable(struct mv_pp2x_port *port); ++void mv_pp21_port_disable(struct mv_pp2x_port *port); ++ ++void mv_pp2x_ingress_enable(struct mv_pp2x_port *port); ++void mv_pp2x_ingress_disable(struct mv_pp2x_port *port); ++void mv_pp2x_egress_enable(struct mv_pp2x_port *port); ++void mv_pp2x_egress_disable(struct mv_pp2x_port *port); ++ ++void mv_pp21_port_periodic_xon_disable(struct mv_pp2x_port *port); ++void mv_pp21_port_loopback_set(struct mv_pp2x_port *port); ++void mv_pp21_port_reset(struct mv_pp2x_port *port); ++ ++void mv_pp2x_rx_pkts_coal_set(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq); ++void mv_pp2x_rx_time_coal_set(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq); ++void mv_pp2x_tx_done_pkts_coal_set_val(struct mv_pp2x_port *port, int address_space, u32 val); ++void mv_pp2x_tx_done_pkts_coal_set(struct mv_pp2x_port *port, int address_space); ++void mv_pp2x_tx_done_pkts_coal_set_all(struct mv_pp2x_port *port); ++void mv_pp2x_cause_error(struct net_device *dev, int cause); ++void mv_pp2x_rx_error(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_desc *rx_desc); ++void mv_pp2x_rx_csum(struct mv_pp2x_port *port, u32 status, ++ struct sk_buff *skb); ++void mv_pp21_get_mac_address(struct mv_pp2x_port *port, unsigned char *addr); ++ ++int mv_pp2x_c2_init(struct platform_device *pdev, struct mv_pp2x_hw *hw); ++ ++int mv_pp2x_prs_sw_sram_shift_set(struct mv_pp2x_prs_entry *pe, int shift, ++ unsigned int op); ++int mv_pp2x_prs_sw_sram_shift_get(struct mv_pp2x_prs_entry *pe, int *shift); ++int mv_pp2x_prs_sw_sram_next_lu_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *lu); ++int mv_pp2x_prs_sram_bit_get(struct mv_pp2x_prs_entry *pe, int bit_num, ++ unsigned int *bit); ++int mv_pp2x_prs_sw_sram_lu_done_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bit); ++int mv_pp2x_prs_sw_sram_flowid_gen_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bit); ++int mv_pp2x_prs_sw_sram_ri_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bits, ++ unsigned int *enable); ++int mv_pp2x_prs_sw_sram_ai_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *bits, ++ unsigned int *enable); ++int mv_pp2x_prs_sw_sram_offset_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int type, ++ int offset, unsigned int op); ++int mv_pp2x_prs_sw_sram_offset_get(struct mv_pp2x_prs_entry *pe, ++ unsigned int *type, ++ int *offset, unsigned int *op); ++void mv_pp2x_prs_hw_port_init(struct mv_pp2x_hw *hw, int port, ++ int lu_first, int lu_max, int offset); ++void mv_pp2x_prs_sw_clear(struct mv_pp2x_prs_entry *pe); ++void mv_pp2x_prs_hw_inv(struct mv_pp2x_hw *hw, int index); ++void mv_pp2x_prs_tcam_lu_set(struct mv_pp2x_prs_entry *pe, unsigned int lu); ++void mv_pp2x_prs_tcam_port_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int port, bool add); ++void mv_pp2x_prs_tcam_port_map_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int ports); ++void mv_pp2x_prs_tcam_data_byte_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int offs, ++ unsigned char byte, ++ unsigned char enable); ++void mv_pp2x_prs_tcam_ai_update(struct mv_pp2x_prs_entry *pe, ++ unsigned int bits, ++ unsigned int enable); ++void mv_pp2x_prs_sram_ri_update(struct mv_pp2x_prs_entry *pe, ++ unsigned int bits, unsigned int mask); ++void mv_pp2x_prs_sram_ai_update(struct mv_pp2x_prs_entry *pe, ++ unsigned int bits, unsigned int mask); ++void mv_pp2x_prs_sram_next_lu_set(struct mv_pp2x_prs_entry *pe, ++ unsigned int lu); ++void mv_pp2x_prs_sw_sram_lu_done_set(struct mv_pp2x_prs_entry *pe); ++void mv_pp2x_prs_sw_sram_lu_done_clear(struct mv_pp2x_prs_entry *pe); ++void mv_pp2x_prs_sw_sram_flowid_set(struct mv_pp2x_prs_entry *pe); ++void mv_pp2x_prs_sw_sram_flowid_clear(struct mv_pp2x_prs_entry *pe); ++int mv_pp2x_prs_hw_write(struct mv_pp2x_hw *hw, struct mv_pp2x_prs_entry *pe); ++int mv_pp2x_cls_hw_lkp_read(struct mv_pp2x_hw *hw, int lkpid, int way, ++ struct mv_pp2x_cls_lookup_entry *fe); ++int mv_pp2x_cls_hw_lkp_write(struct mv_pp2x_hw *hw, int lkpid, int way, ++ struct mv_pp2x_cls_lookup_entry *fe); ++int mv_pp2x_cls_lkp_port_way_set(struct mv_pp2x_hw *hw, int port, int way); ++int mv_pp2x_cls_sw_lkp_rxq_get(struct mv_pp2x_cls_lookup_entry *lkp, int *rxq); ++int mv_pp2x_cls_sw_lkp_rxq_set(struct mv_pp2x_cls_lookup_entry *fe, int rxq); ++int mv_pp2x_cls_sw_lkp_en_get(struct mv_pp2x_cls_lookup_entry *lkp, int *en); ++int mv_pp2x_cls_sw_lkp_en_set(struct mv_pp2x_cls_lookup_entry *lkp, int en); ++int mv_pp2x_cls_sw_lkp_flow_get(struct mv_pp2x_cls_lookup_entry *lkp, ++ int *flow_idx); ++int mv_pp2x_cls_sw_lkp_flow_set(struct mv_pp2x_cls_lookup_entry *lkp, ++ int flow_idx); ++int mv_pp2x_cls_sw_lkp_mod_get(struct mv_pp2x_cls_lookup_entry *lkp, ++ int *mod_base); ++int mv_pp2x_cls_sw_lkp_mod_set(struct mv_pp2x_cls_lookup_entry *lkp, ++ int mod_base); ++int mv_pp2x_cls_hw_flow_read(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_flow_entry *fe); ++int mv_pp2x_cls_sw_flow_hek_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *num_of_fields, int field_ids[]); ++int mv_pp2x_cls_sw_flow_port_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *type, int *portid); ++ ++int mv_pp2x_cls_hw_lkp_hit_get(struct mv_pp2x_hw *hw, int lkpid, int way, ++ unsigned int *cnt); ++void mv_pp2x_cls_flow_write(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_flow_entry *fe); ++void mv_pp2x_cls_flow_read(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_flow_entry *fe); ++int mv_pp2x_cls_sw_flow_port_set(struct mv_pp2x_cls_flow_entry *fe, ++ int type, int portid); ++int mv_pp2x_cls_sw_flow_hek_num_set(struct mv_pp2x_cls_flow_entry *fe, ++ int num_of_fields); ++int mv_pp2x_cls_sw_flow_hek_set(struct mv_pp2x_cls_flow_entry *fe, ++ int field_index, int field_id); ++int mv_pp2x_cls_sw_flow_portid_select(struct mv_pp2x_cls_flow_entry *fe, ++ int from); ++int mv_pp2x_cls_sw_flow_pppoe_set(struct mv_pp2x_cls_flow_entry *fe, int mode); ++int mv_pp2x_cls_sw_flow_vlan_set(struct mv_pp2x_cls_flow_entry *fe, int mode); ++int mv_pp2x_cls_sw_flow_macme_set(struct mv_pp2x_cls_flow_entry *fe, int mode); ++int mv_pp2x_cls_sw_flow_udf7_set(struct mv_pp2x_cls_flow_entry *fe, int mode); ++int mv_pp2x_cls_sw_flow_seq_ctrl_set(struct mv_pp2x_cls_flow_entry *fe, ++ int mode); ++int mv_pp2x_cls_sw_flow_engine_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *engine, int *is_last); ++int mv_pp2x_cls_sw_flow_engine_set(struct mv_pp2x_cls_flow_entry *fe, ++ int engine, int is_last); ++int mv_pp2x_cls_sw_flow_extra_get(struct mv_pp2x_cls_flow_entry *fe, ++ int *type, int *prio); ++int mv_pp2x_cls_sw_flow_extra_set(struct mv_pp2x_cls_flow_entry *fe, ++ int type, int prio); ++int mv_pp2x_cls_hw_flow_hit_get(struct mv_pp2x_hw *hw, ++ int index, unsigned int *cnt); ++int mv_pp2x_cls_hw_udf_set(struct mv_pp2x_hw *hw, int udf_no, int offs_id, ++ int offs_bits, int size_bits); ++int mv_pp2x_cls_c2_qos_hw_read(struct mv_pp2x_hw *hw, int tbl_id, int tbl_sel, ++ int tbl_line, ++ struct mv_pp2x_cls_c2_qos_entry *qos); ++int mv_pp2x_cls_c2_qos_hw_write(struct mv_pp2x_hw *hw, ++ struct mv_pp2x_cls_c2_qos_entry *qos); ++int mv_pp2_cls_c2_qos_prio_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *prio); ++int mv_pp2_cls_c2_qos_dscp_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *dscp); ++int mv_pp2_cls_c2_qos_color_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *color); ++int mv_pp2_cls_c2_qos_gpid_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *gpid); ++int mv_pp2_cls_c2_qos_queue_get(struct mv_pp2x_cls_c2_qos_entry *qos, int *queue); ++int mv_pp2x_cls_c2_qos_tbl_set(struct mv_pp2x_cls_c2_entry *c2, int tbl_id, ++ int tbl_sel); ++int mv_pp2x_cls_c2_hw_write(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_c2_entry *c2); ++int mv_pp2x_cls_c2_hw_read(struct mv_pp2x_hw *hw, int index, ++ struct mv_pp2x_cls_c2_entry *c2); ++int mv_pp2x_cls_c2_hit_cntr_clear_all(struct mv_pp2x_hw *hw); ++int mv_pp2x_cls_c2_hit_cntr_read(struct mv_pp2x_hw *hw, int index, u32 *cntr); ++int mv_pp2x_cls_c2_rule_set(struct mv_pp2x_port *port, u8 start_queue); ++u8 mv_pp2x_cls_c2_rule_queue_get(struct mv_pp2x_hw *hw, u32 rule_idx); ++void mv_pp2x_cls_c2_rule_queue_set(struct mv_pp2x_hw *hw, u32 rule_idx, ++ u8 queue); ++u8 mv_pp2x_cls_c2_pbit_tbl_queue_get(struct mv_pp2x_hw *hw, u8 tbl_id, ++ u8 tbl_line); ++void mv_pp2x_cls_c2_pbit_tbl_queue_set(struct mv_pp2x_hw *hw, u8 tbl_id, ++ u8 tbl_line, u8 queue); ++int mv_pp2x_cls_c2_hw_inv(struct mv_pp2x_hw *hw, int index); ++void mv_pp2x_cls_c2_hw_inv_all(struct mv_pp2x_hw *hw); ++int mv_pp2x_cls_c2_tcam_byte_set(struct mv_pp2x_cls_c2_entry *c2, ++ unsigned int offs, ++ unsigned char byte, ++ unsigned char enable); ++int mv_pp2x_cls_c2_qos_queue_set(struct mv_pp2x_cls_c2_qos_entry *qos, ++ u8 queue); ++int mv_pp2x_cls_c2_color_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int from); ++int mv_pp2x_cls_c2_prio_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int prio, int from); ++int mv_pp2x_cls_c2_dscp_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int dscp, int from); ++int mv_pp2x_cls_c2_queue_low_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int queue, int from); ++int mv_pp2x_cls_c2_queue_high_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int queue, int from); ++int mv_pp2x_cls_c2_forward_set(struct mv_pp2x_cls_c2_entry *c2, int cmd); ++int mv_pp2x_cls_c2_rss_set(struct mv_pp2x_cls_c2_entry *c2, int cmd, ++ int rss_en); ++int mv_pp2x_cls_c2_flow_id_en(struct mv_pp2x_cls_c2_entry *c2, ++ int flowid_en); ++ ++int mv_pp22_rss_tbl_entry_set(struct mv_pp2x_hw *hw, ++ struct mv_pp22_rss_entry *rss); ++int mv_pp22_rss_tbl_entry_get(struct mv_pp2x_hw *hw, ++ struct mv_pp22_rss_entry *rss); ++ ++int mv_pp22_rss_rxq_set(struct mv_pp2x_port *port, u32 cos_width); ++ ++void mv_pp22_rss_c2_enable(struct mv_pp2x_port *port, bool en); ++ ++void mv_pp2x_tx_fifo_size_set(struct mv_pp2x_hw *hw, u32 port_id, u32 val); ++ ++void mv_pp2x_tx_fifo_threshold_set(struct mv_pp2x_hw *hw, u32 port_id, u32 val); ++ ++int mv_pp2x_check_hw_buf_num(struct mv_pp2x *priv, struct mv_pp2x_bm_pool *bm_pool); ++void mv_pp22_set_net_comp(struct mv_pp2x *priv); ++int mvcpn110_mac_hw_init(struct mv_pp2x_port *port); ++void mv_pp2x_counters_stat_update(struct mv_pp2x_port *port, ++ struct gop_stat *gop_statistics); ++void mv_pp2x_counters_stat_clear(struct mv_pp2x_port *port); ++void mv_pp2x_aggr_txq_pend_send(struct mv_pp2x_port *port, ++ struct mv_pp2x_cp_pcpu *cp_pcpu, ++ struct mv_pp2x_aggr_tx_queue *aggr_txq, int address_space); ++ ++/* Interface to CM3 processor for Dying Gasp feature */ ++void mv_pp2x_dying_gasp_set_dst_mac(struct mv_pp2x_port *port, unsigned char *mac_addr); ++void mv_pp2x_dying_gasp_set_src_mac(struct mv_pp2x_port *port, unsigned char *mac_addr); ++void mv_pp2x_dying_gasp_set_ether_type(struct mv_pp2x_port *port, u32 ether_type); ++void mv_pp2x_dying_gasp_set_vlan(struct mv_pp2x_port *port, u32 vlan); ++void mv_pp2x_dying_gasp_set_data_patern(struct mv_pp2x_port *port, unsigned char *data, int data_size); ++void mv_pp2x_dying_gasp_set_packet_control(struct mv_pp2x_port *port, int packets_count, int packet_size); ++void mv_pp2x_dying_gasp_control(struct mv_pp2x_port *port, bool en); ++ ++#endif /* _MVPP2_HW_H_ */ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw_type.h b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw_type.h +new file mode 100644 +index 00000000..460fab0 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_hw_type.h +@@ -0,0 +1,3038 @@ ++/* ++* *************************************************************************** ++* Copyright (C) 2016 Marvell International Ltd. ++* *************************************************************************** ++* 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 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, see . ++* *************************************************************************** ++*/ ++ ++#ifndef _MVPP2_HW_TYPE_H_ ++#define _MVPP2_HW_TYPE_H_ ++ ++#include ++#include ++#include ++#include ++ ++#define CREATE_MASK(pos, len) GENMASK((pos) + (len) - 1, (pos)) ++#define CREATE_MASK_ULL(pos, len) GENMASK_ULL((pos) + (len) - 1, (pos)) ++ ++#define AUTO_MASK(reg_name) CREATE_MASK(reg_name##_OFFS, reg_name##_SIZE) ++ ++/*All PPV22 Addresses are 40-bit */ ++#define MVPP22_ADDR_HIGH_SIZE 8 ++#define MVPP22_ADDR_HIGH_MASK ((1 << MVPP22_ADDR_HIGH_SIZE) - 1) ++ ++/*PPV22 ADDRESS SPACE */ ++#define MVPP2_ADDR_SPACE_SIZE (64 * 1024) ++ ++/*TODO*/ ++/*AXI_BRIDGE*/ ++/*AXI_CONTEXT*/ ++#define MVPP22_HIF_ALLOCATION_REG 0x8610 ++ ++#define MVPP22_PTP_BASE_OFFS 0x800 ++#define MVPP22_GOP_PORT_OFFS 0x1000 ++#define MVPP22_MIB_PORT_OFFS 0x100 ++/* COMPHY */ ++#define MAX_NUM_SERDES_LANES 6 ++#define COMPHY4_RXAUI_LANE0 4 ++ ++/* MSS Flow control */ ++#define MSS_CP_FC_COM_REG 0 ++#define FLOW_CONTROL_ENABLE_BIT BIT(0) ++#define FLOW_CONTROL_UPDATE_COMMAND_BIT BIT(31) ++#define FLOW_CONTROL_QUANTA 0xFFFF ++ ++#define MSS_CP_CM3_BUF_POOL_BASE 0x40 ++#define MSS_CP_CM3_BUF_POOL_OFFS 4 ++ ++#define MSS_CP_CM3_BUF_POOL_STOP_MASK 0xFFF ++#define MSS_CP_CM3_BUF_POOL_START_MASK (0xFFF << MSS_CP_CM3_BUF_POOL_START_OFFS) ++#define MSS_CP_CM3_BUF_POOL_START_OFFS 12 ++#define MSS_CP_CM3_BUF_POOL_PORTS_MASK (0xF << MSS_CP_CM3_BUF_POOL_PORTS_OFFS) ++#define MSS_CP_CM3_BUF_POOL_PORTS_OFFS 24 ++ ++#define MSS_CP_CM3_RXQ_TRESH_BASE 0x200 ++#define MSS_CP_CM3_RXQ_TRESH_OFFS 4 ++ ++#define MSS_CP_CM3_RXQ_TRESH_START_MASK 0xFFFF ++#define MSS_CP_CM3_RXQ_TRESH_STOP_MASK (0xFFFF << MSS_CP_CM3_RXQ_TRESH_STOP_OFFS) ++#define MSS_CP_CM3_RXQ_TRESH_STOP_OFFS 16 ++ ++#define MSS_CP_CM3_RXQ_ASS_BASE 0x80 ++#define MSS_CP_CM3_RXQ_ASS_REG(queue, first_rxq) (MSS_CP_CM3_RXQ_ASS_BASE + \ ++ ((queue + first_rxq) / MSS_CP_CM3_RXQ_ASS_PER_REG) \ ++ * MSS_CP_CM3_RXQ_ASS_OFFS) ++#define MSS_CP_CM3_RXQ_ASS_OFFS 4 ++#define MSS_CP_CM3_RXQ_ASS_PER_REG 4 ++#define MSS_CP_CM3_RXQ_ASS_PER_OFFS 8 ++#define MSS_CP_CM3_RXQ_ASS_PORTID_OFFS 0 ++#define MSS_CP_CM3_RXQ_ASS_PORTID_MASK 0x3 ++#define MSS_CP_CM3_RXQ_ASS_HOSTID_OFFS 2 ++#define MSS_CP_CM3_RXQ_ASS_HOSTID_MASK 0x3F ++ ++#define MSS_CP_CM3_THRESHOLD_STOP 768 ++#define MSS_CP_CM3_THRESHOLD_START 1024 ++ ++/* MSS Dying Gasp */ ++#define BYTE_MASK 0xFF ++#define BITS_PER_BYTE 8 ++#define BYTES_IN_PP_REG 4 ++#define BYTES_IN_GOP_REG 2 ++ ++#define MSS_CP_DG_BASE 0x400 ++ ++#define MSS_CP_DG_CON_REG MSS_CP_DG_BASE ++#define MSS_CP_DG_UPD_COM BIT(31) ++ ++#define MSS_CP_DG_CON_PORT_OFFS 0x4 ++#define MSS_CP_DG_CON_OFFS 0x4 ++#define MSS_CP_DG_PACK_CON_REG(port) (MSS_CP_DG_BASE + port * MSS_CP_DG_CON_PORT_OFFS + \ ++ MSS_CP_DG_CON_OFFS) ++ ++#define MSS_CP_DG_PH_OFFS 0x10 ++#define MSS_CP_DG_PH_PORT_OFFS 0x10 ++#define MSS_CP_DG_DA_MAC_LOW_REG(port) (MSS_CP_DG_BASE + MSS_CP_DG_PH_OFFS + \ ++ port * MSS_CP_DG_PH_PORT_OFFS) ++#define MSS_CP_DG_SA_LOW_OFFS 0x4 ++#define MSS_CP_DG_DA_LOW_MASK 0xFFFF ++#define MSS_CP_DG_DA_HIGH_SA_LOW_MAC_REG(port) (MSS_CP_DG_BASE + MSS_CP_DG_PH_OFFS + \ ++ port * MSS_CP_DG_PH_PORT_OFFS + \ ++ MSS_CP_DG_SA_LOW_OFFS) ++#define MSS_CP_DG_SA_HIGH_OFFS 0x8 ++#define MSS_CP_DG_SA_HIGH_MASK_OFFS 16 ++#define MSS_CP_DG_SA_HIGH_MASK (0xFFFF << MSS_CP_DG_SA_HIGH_MASK_OFFS) ++#define MSS_CP_DG_SA_HIGH_MAC_REG(port) (MSS_CP_DG_BASE + MSS_CP_DG_PH_OFFS + \ ++ port * MSS_CP_DG_PH_PORT_OFFS + \ ++ MSS_CP_DG_SA_HIGH_OFFS) ++ ++#define MSS_CP_DG_MAC_ETH_T_VLAN_OFFS 0xC ++#define MSS_CP_DG_MAC_ETH_T_MASK 0xFFFF ++#define MSS_CP_DG_MAC_VLAN_MASK_OFFS 16 ++#define MSS_CP_DG_MAC_VLAN_MASK (0xFFFF << MSS_CP_DG_MAC_VLAN_MASK_OFFS) ++ ++#define MSS_CP_DG_MAC_ETH_T_VLAN_REG(port) (MSS_CP_DG_BASE + MSS_CP_DG_PH_OFFS + \ ++ port * MSS_CP_DG_PH_PORT_OFFS + \ ++ MSS_CP_DG_MAC_ETH_T_VLAN_OFFS) ++#define MSS_CP_DG_MAC_VLAN_OFFS 0xE ++#define MSS_CP_DG_MAC_VLAN_REG(port) (MSS_CP_DG_BASE + MSS_CP_DG_PH_OFFS + \ ++ port * MSS_CP_DG_PH_PORT_OFFS + MSS_CP_DG_MAC_VLAN_OFFS) ++ ++#define MSS_CP_DG_DP_OFFS 0x40 ++#define MSS_CP_DG_DP_PORT_OFFS 0x40 ++#define MSS_CP_DG_REG_SIZE_BYTES 0x4 ++ ++#define MSS_CP_DG_DATA_PATTERN_REG(port, reg_i) (MSS_CP_DG_BASE + MSS_CP_DG_DP_OFFS + \ ++ port * MSS_CP_DG_DP_PORT_OFFS + \ ++ reg_i * MSS_CP_DG_REG_SIZE_BYTES) ++ ++#define MSS_CP_DG_MAX_PACK_OFFS 16 ++ ++#define MSS_CP_DG_DP_MAX_SIZE 64 ++#define MSS_CP_DG_PACK_MAX_SIZE (MSS_CP_DG_DP_MAX_SIZE + VLAN_HLEN + ETH_HLEN) ++#define MSS_CP_DG_MAX_PACK_COUNT 0x1FFF ++#define MSS_CP_DG_MAX_TIMEOUT 5000 ++ ++/* RX FIFO tresholld in 1KB granularity */ ++#define MVPP23_PORT0_FIFO_THRESHOLD (9 * 1024) ++#define MVPP23_PORT1_FIFO_THRESHOLD (4 * 1024) ++#define MVPP23_PORT2_FIFO_THRESHOLD (2 * 1024) ++ ++#define MVPP21_DESC_ADDR_SHIFT 0 /*Applies to RXQ, AGGR_TXQ*/ ++#define MVPP22_DESC_ADDR_SHIFT (9 - 1) /*Applies to RXQ, AGGR_TXQ*/ ++ ++/* RX Flow Control Registers */ ++#define MVPP2_RX_FLOW_CONTROL_REG(port) (0x150 + 4 * (port)) ++#define MVPP2_RX_FLOW_CONTROL_EN BIT(24) ++#define MVPP2_RX_FLOW_CONTROL_TRSH_OFFS 16 ++#define MVPP2_RX_FLOW_CONTROL_TRSH_MASK (0xFF << MVPP2_RX_FLOW_CONTROL_TRSH_OFFS) ++#define MVPP2_RX_FLOW_CONTROL_TRSH_UNIT 256 ++ ++/* RX Fifo Registers */ ++#define MVPP2_RX_DATA_FIFO_SIZE_REG(port) (0x00 + 4 * (port)) ++#define MVPP2_RX_ATTR_FIFO_SIZE_REG(port) (0x20 + 4 * (port)) ++#define MVPP2_RX_MIN_PKT_SIZE_REG 0x60 ++#define MVPP2_RX_FIFO_INIT_REG 0x64 ++ ++/* RX DMA Top Registers */ ++#define MVPP2_RX_CTRL_REG(port) (0x140 + 4 * (port)) ++#define MVPP2_RX_LOW_LATENCY_PKT_SIZE(s) (((s) & 0xfff) << 16) ++#define MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK BIT(31) ++#define MVPP2_POOL_BUF_SIZE_REG(pool) (0x180 + 4 * (pool)) ++#define MVPP2_POOL_BUF_SIZE_OFFSET 5 ++ ++/* RXQ_CONFIG_REF Generic+PPV21+PPV22 */ ++#define MVPP2_RXQ_CONFIG_REG(rxq) (0x800 + 4 * (rxq)) ++#define MVPP2_SNOOP_PKT_SIZE_MASK 0x1ff ++#define MVPP2_SNOOP_BUF_HDR_MASK BIT(9) ++#define MVPP2_RXQ_PACKET_OFFSET_OFFS 28 ++#define MVPP2_RXQ_PACKET_OFFSET_MASK 0x70000000 ++#define MVPP2_RXQ_DISABLE_MASK BIT(31) ++ ++#define MVPP21_RXQ_POOL_SHORT_OFFS 20 ++#define MVPP21_RXQ_POOL_SHORT_MASK 0x700000 ++#define MVPP21_RXQ_POOL_LONG_OFFS 24 ++#define MVPP21_RXQ_POOL_LONG_MASK 0x7000000 ++ ++#define MVPP22_RXQ_POOL_SHORT_OFFS 20 ++#define MVPP22_RXQ_POOL_SHORT_MASK 0xf00000 ++#define MVPP22_RXQ_POOL_LONG_OFFS 24 ++#define MVPP22_RXQ_POOL_LONG_MASK 0xf000000 ++#define MVPP22_RXQ_LLC_DEP_HDR_SIZE 0xf000 ++#define MVPP22_RXQ_LLC_DEP_ENABLE BIT(16) ++ ++#define MVPP21_ETH_RX_HWQ_REG(txq) (0xc00 + 4 * (txq)) ++#define MVPP21_ETH_RX_HWQ_POOL_SHORT_OFFS 0 ++#define MVPP21_ETH_RX_HWQ_POOL_SHORT_MASK 0x7 ++#define MVPP21_ETH_RX_HWQ_POOL_LONG_OFFS 4 ++#define MVPP21_ETH_RX_HWQ_POOL_LONG_MASK 0x70 ++#define MVPP21_ETH_RX_HWQ_DISABLE_MASK BIT(31) ++#define MVPP22_ETH_RX_HWQ_REG(txq) (0xe00 + 4 * (txq)) ++#define MVPP22_ETH_RX_HWQ_POOL_SHORT_OFFS 0 ++#define MVPP22_ETH_RX_HWQ_POOL_SHORT_MASK 0xf ++#define MVPP22_ETH_RX_HWQ_POOL_LONG_OFFS 4 ++#define MVPP22_ETH_RX_HWQ_POOL_LONG_MASK 0xf0 ++#define MVPP22_ETH_RX_HWQ_DISABLE_MASK BIT(31) ++ ++#define MVPP22_RX_HWF_SNOOP_REG (0x178) ++#define MVPP22_RX_HWF_SNOOP_ENABLE (BIT(0)) ++ ++/* AXI Bridge Registers */ ++#define MVPP22_AXI_BM_WR_ATTR_REG 0x4100 ++#define MVPP22_AXI_BM_RD_ATTR_REG 0x4104 ++#define MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG 0x4110 ++#define MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG 0x4114 ++#define MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG 0x4118 ++#define MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG 0x411c ++#define MVPP22_AXI_RX_DATA_WR_ATTR_REG 0x4120 ++#define MVPP22_AXI_TX_DATA_RD_ATTR_REG 0x4130 ++ ++#define MVPP22_AXI_ATTR_CACHE_OFFS 0 ++#define MVPP22_AXI_ATTR_CACHE_SIZE 4 ++#define MVPP22_AXI_ATTR_CACHE_MASK AUTO_MASK(MVPP22_AXI_ATTR_CACHE) ++ ++#define MVPP22_AXI_ATTR_QOS_OFFS 4 ++#define MVPP22_AXI_ATTR_QOS_SIZE 4 ++#define MVPP22_AXI_ATTR_QOS_MASK AUTO_MASK(MVPP22_AXI_ATTR_QOS) ++ ++#define MVPP22_AXI_ATTR_TC_OFFS 8 ++#define MVPP22_AXI_ATTR_TC_SIZE 4 ++#define MVPP22_AXI_ATTR_TC_MASK AUTO_MASK(MVPP22_AXI_ATTR_TC) ++ ++#define MVPP22_AXI_ATTR_DOMAIN_OFFS 12 ++#define MVPP22_AXI_ATTR_DOMAIN_SIZE 2 ++#define MVPP22_AXI_ATTR_DOMAIN_MASK AUTO_MASK(MVPP22_AXI_ATTR_DOMAIN) ++ ++#define MVPP22_AXI_ATTR_NON_CACHE ((0x3 << MVPP22_AXI_ATTR_DOMAIN_OFFS) + \ ++ (0x3 << MVPP22_AXI_ATTR_CACHE_OFFS)) ++ ++#define MVPP22_AXI_ATTR_SW_COH_WRITE ((0x0 << MVPP22_AXI_ATTR_DOMAIN_OFFS) + \ ++ (0x7 << MVPP22_AXI_ATTR_CACHE_OFFS)) ++ ++#define MVPP22_AXI_ATTR_SW_COH_READ ((0x0 << MVPP22_AXI_ATTR_DOMAIN_OFFS) + \ ++ (0xB << MVPP22_AXI_ATTR_CACHE_OFFS)) ++ ++#define MVPP22_AXI_ATTR_HW_COH_WRITE ((0x2 << MVPP22_AXI_ATTR_DOMAIN_OFFS) + \ ++ (0x7 << MVPP22_AXI_ATTR_CACHE_OFFS)) ++ ++#define MVPP22_AXI_ATTR_HW_COH_READ ((0x2 << MVPP22_AXI_ATTR_DOMAIN_OFFS) + \ ++ (0xB << MVPP22_AXI_ATTR_CACHE_OFFS)) ++ ++#define MVPP22_AXI_ATTR_SNOOP_CNTRL_BIT BIT(16) ++ ++#define MVPP22_AXI_RD_NORMAL_CODE_REG 0x4150 ++#define MVPP22_AXI_RD_SNOOP_CODE_REG 0x4154 ++#define MVPP22_AXI_WR_NORMAL_CODE_REG 0x4160 ++#define MVPP22_AXI_WR_SNOOP_CODE_REG 0x4164 ++#define MVPP22_AXI_WR_DEP_CODE_REG 0x4168 ++ ++#define MVPP22_AXI_CODE_CACHE_OFFS 0 ++#define MVPP22_AXI_CODE_CACHE_SIZE 4 ++#define MVPP22_AXI_CODE_CACHE_MASK AUTO_MASK(MVPP22_AXI_CODE_CACHE) ++ ++#define MVPP22_AXI_CODE_CACHE_NON_CACHE 0x3 ++#define MVPP22_AXI_CODE_CACHE_RD_CACHE 0xB ++#define MVPP22_AXI_CODE_CACHE_WR_CACHE 0x7 ++ ++#define MVPP22_AXI_CODE_DOMAIN_OFFS 4 ++#define MVPP22_AXI_CODE_DOMAIN_SIZE 2 ++#define MVPP22_AXI_CODE_DOMAIN_MASK AUTO_MASK(MVPP22_AXI_CODE_DOMAIN) ++ ++#define MVPP22_AXI_CODE_DOMAIN_OUTER_DOM 2 ++#define MVPP22_AXI_CODE_DOMAIN_SYSTEM 3 ++#define MVPP22_AXI_CODE_DOMAIN_NON_SHARE 0 ++ ++/* Top Reg file */ ++#define MVPP2_MH_REG(port) (0x5040 + 4 * (port)) ++ ++#define MVPP2_MH_EN_OFFS 0 ++#define MVPP2_MH_EN_MASK BIT(MVPP2_MH_EN_OFFS) ++ ++#define MVPP2_DSA_EN_OFFS 4 ++#define MVPP2_DSA_EN_MASK (0x3 << MVPP2_DSA_EN_OFFS) ++#define MVPP2_DSA_DISABLE 0 ++#define MVPP2_DSA_NON_EXTENDED (0x1 << MVPP2_DSA_EN_OFFS) ++#define MVPP2_DSA_EXTENDED (0x2 << MVPP2_DSA_EN_OFFS) ++ ++#define MVPP2_VER_ID_REG 0x50b0 ++ ++#define MVPP2_VER_PP22 0x10 ++#define MVPP2_VER_PP23 0x11 ++ ++/* Parser Registers */ ++#define MVPP2_PRS_INIT_LOOKUP_REG 0x1000 ++#define MVPP2_PRS_PORT_LU_MAX 0xf ++#define MVPP2_PRS_MAX_LOOP_MIN 0x1 ++#define MVPP2_PRS_PORT_LU_MASK(port) (0xff << ((port) * 4)) ++#define MVPP2_PRS_PORT_LU_VAL(port, val) ((val) << ((port) * 4)) ++#define MVPP2_PRS_INIT_OFFS_REG(port) (0x1004 + ((port) & 4)) ++#define MVPP2_PRS_INIT_OFF_MASK(port) (0x3f << (((port) % 4) * 8)) ++#define MVPP2_PRS_INIT_OFF_VAL(port, val) ((val) << (((port) % 4) * 8)) ++#define MVPP2_PRS_INIT_OFF_BITS 6 ++#define MVPP2_PRS_INIT_OFF_MAX ((1 << MVPP2_PRS_INIT_OFF_BITS) - 1) ++#define MVPP2_PRS_MAX_LOOP_REG(port) (0x100c + ((port) & 4)) ++#define MVPP2_PRS_MAX_LOOP_MASK(port) (0xff << (((port) % 4) * 8)) ++#define MVPP2_PRS_MAX_LOOP_VAL(port, val) ((val) << (((port) % 4) * 8)) ++#define MVPP2_PRS_TCAM_IDX_REG 0x1100 ++#define MVPP2_PRS_TCAM_DATA_REG(idx) (0x1104 + (idx) * 4) ++#define MVPP2_PRS_TCAM_INV_MASK BIT(31) ++#define MVPP2_PRS_SRAM_IDX_REG 0x1200 ++#define MVPP2_PRS_SRAM_DATA_REG(idx) (0x1204 + (idx) * 4) ++ ++#define MVPP2_PRS_EXP_REG 0x1214 ++#define MVPP2_PRS_EXP_MISS 0 ++#define MVPP2_PRS_EXP_EXEED 1 ++#define MVPP2_PRS_EXP_OF 2 ++ ++#define MVPP2_PRS_TCAM_CTRL_REG 0x1230 ++#define MVPP2_PRS_TCAM_EN_MASK BIT(0) ++#define MVPP2_PRS_INTR_CAUSE_REG (0x1020) ++#define MVPP2_PRS_INTR_MASK_REG (0x1024) ++ ++/*PPv2.1 MASS 3.20 new feature */ ++#define MVPP2_PRS_TCAM_HIT_IDX_REG 0x1240 ++/*----------------------------------------------------------------------*/ ++/*PPv2.1 MASS 3.20 new feature */ ++#define MVPP2_PRS_TCAM_HIT_CNT_REG 0x1244 ++#define MVPP2_PRS_TCAM_HIT_CNT_BITS 16 ++#define MVPP2_PRS_TCAM_HIT_CNT_OFFS 0 ++#define MVPP2_PRS_TCAM_HIT_CNT_MASK \ ++ (((1 << MVPP2_PRS_TCAM_HIT_CNT_BITS) - 1) << \ ++ MVPP2_PRS_TCAM_HIT_CNT_OFFS) ++ ++/* Classifier Registers */ ++#define MVPP2_CLS_MODE_REG 0x1800 ++#define MVPP2_CLS_MODE_ACTIVE_MASK BIT(0) ++#define MVPP2_CLS_PORT_WAY_REG 0x1810 ++#define MVPP2_CLS_PORT_WAY_MASK(port) (1 << (port)) ++#define MVPP2_CLS_LKP_INDEX_REG 0x1814 ++#define MVPP2_CLS_LKP_INDEX_WAY_OFFS 6 ++#define MVPP2_CLS_LKP_INDEX_LKP_OFFS 0 ++#define MVPP2_CLS_LKP_TBL_REG 0x1818 ++#define MVPP2_CLS_LKP_TBL_RXQ_MASK 0xff ++#define MVPP2_CLS_LKP_TBL_LOOKUP_EN_MASK BIT(25) ++#define MVPP22_CLS_LKP_TBL_SEL_REG 0x181c ++#define MVPP22_CLS_LKP_TBL_SEL_CDT_MASK BIT(0) ++#define MVPP22_CLS_LKP_TBL_SEL_FDT_MASK BIT(1) ++#define MVPP2_CLS_FLOW_INDEX_REG 0x1820 ++#define MVPP2_CLS_FLOW_TBL0_REG 0x1824 ++#define MVPP2_CLS_FLOW_TBL1_REG 0x1828 ++#define MVPP2_CLS_FLOW_TBL2_REG 0x182c ++ ++#define MVPP2_CLS_PORT_SPID_REG 0x1830 ++ ++#define MVPP2_CLS_PORT_SPID_BITS 2 ++#define MVPP2_CLS_PORT_SPID_MAX \ ++ ((1 << MVPP2_CLS_PORT_SPID_BITS) - 1) ++#define MVPP2_CLS_PORT_SPID_MASK(port) ((MVPP2_CLS_PORT_SPID_MAX) << \ ++ ((port) * MVPP2_CLS_PORT_SPID_BITS)) ++#define MVPP2_CLS_PORT_SPID_VAL(port, val) ((val) << \ ++ ((port) * MVPP2_CLS_PORT_SPID_BITS)) ++ ++/* PORT - SPID types */ ++#define MVPP2_PORT_SPID_MH 0 ++#define MVPP2_PORT_SPID_EXT_SWITCH 1 ++#define MVPP2_PORT_SPID_CAS_SWITCH 2 ++#define MVPP2_PORT_SPID_PORT_TRUNK 3 ++/*----------------------------------------------------------------------*/ ++ ++#define MVPP2_CLS_SPID_UNI_BASE_REG 0x1840 ++#define MVPP2_CLS_SPID_UNI_REG(spid) (MVPP2_CLS_SPID_UNI_BASE_REG + \ ++ (((spid) >> 3) * 4)) ++ ++#define MVPP2_CLS_SPID_MAX 31 ++#define MVPP2_CLS_SPID_UNI_REGS 4 ++#define MVPP2_CLS_SPID_UNI_BITS 3 ++#define MVPP2_CLS_SPID_UNI_FIXED_BITS 4 ++#define MVPP2_CLS_SPID_UNI_MAX ((1 << \ ++ MVPP2_CLS_SPID_UNI_BITS) - 1) ++#define MVPP2_CLS_SPID_UNI_OFFS(spid) (((spid) % 8) * \ ++ MVPP2_CLS_SPID_UNI_FIXED_BITS) ++#define MVPP2_CLS_SPID_UNI_MASK(spid) ((MVPP2_CLS_SPID_UNI_MAX) << \ ++ (MVPP2_CLS_SPID_UNI_OFFS(spid))) ++#define MVPP2_CLS_SPID_UNI_VAL(spid, val) ((val) << \ ++ (MVPP2_CLS_SPID_UNI_OFFS(spid))) ++ ++/*----------------------------------------------------------------------*/ ++#define MVPP2_CLS_GEM_VIRT_INDEX_REG 0x1A00 ++#define MVPP2_CLS_GEM_VIRT_INDEX_BITS (7) ++#define MVPP2_CLS_GEM_VIRT_INDEX_MAX (((1 << \ ++ MVPP2_CLS_GEM_VIRT_INDEX_BITS) - 1) << 0) ++ ++/*----------------------------------------------------------------------*/ ++ ++/* indirect rd/wr via index GEM_VIRT_INDEX */ ++#define MVPP2_CLS_GEM_VIRT_REGS_NUM 128 ++#define MVPP2_CLS_GEM_VIRT_REG 0x1A04 ++ ++#define MVPP2_CLS_GEM_VIRT_BITS 12 ++#define MVPP2_CLS_GEM_VIRT_MAX ((1 << \ ++ MVPP2_CLS_GEM_VIRT_BITS) - 1) ++#define MVPP2_CLS_GEM_VIRT_MASK (((1 << \ ++ MVPP2_CLS_GEM_VIRT_BITS) - 1) << 0) ++ ++/*----------------------------------------------------------------------*/ ++#define MVPP2_CLS_UDF_BASE_REG 0x1860 ++#define MVPP2_CLS_UDF_REG(index) (MVPP2_CLS_UDF_BASE_REG + \ ++ ((index) * 4)) /*index <=63*/ ++#define MVPP2_CLS_UDF_REGS_NUM 64 ++ ++#define MVPP2_CLS_UDF_BASE_REGS 8 ++#define MVPP2_CLS_UDF_OFFSET_ID_OFFS 0 ++#define MVPP2_CLS_UDF_OFFSET_ID_BITS 4 ++#define MVPP2_CLS_UDF_OFFSET_ID_MAX ((1 << \ ++ MVPP2_CLS_UDF_OFFSET_ID_BITS) - 1) ++#define MVPP2_CLS_UDF_OFFSET_ID_MASK \ ++ ((MVPP2_CLS_UDF_OFFSET_ID_MAX) << MVPP2_CLS_UDF_OFFSET_ID_OFFS) ++ ++#define MVPP2_CLS_UDF_OFFSET_PACKET 0 ++#define MVPP2_CLS_UDF_OFFSET_L3 1 ++#define MVPP2_CLS_UDF_OFFSET_L4 4 ++#define MVPP2_CLS_UDF_OFFSET_OUTVLAN 8 ++#define MVPP2_CLS_UDF_OFFSET_INVLAN 9 ++#define MVPP2_CLS_UDF_OFFSET_ETHTYPE 0xa ++ ++#define MVPP2_CLS_UDF_REL_OFFSET_OFFS 4 ++#define MVPP2_CLS_UDF_REL_OFFSET_BITS 11 ++#define MVPP2_CLS_UDF_REL_OFFSET_MAX ((1 << \ ++ MVPP2_CLS_UDF_REL_OFFSET_BITS) - 1) ++#define MVPP2_CLS_UDF_REL_OFFSET_MASK \ ++ ((MVPP2_CLS_UDF_REL_OFFSET_MAX) << MVPP2_CLS_UDF_REL_OFFSET_OFFS) ++ ++#define MVPP2_CLS_UDF_SIZE_OFFS 16 ++#define MVPP2_CLS_UDF_SIZE_BITS 8 ++#define MVPP2_CLS_UDF_SIZE_MAX ((1 << \ ++ MVPP2_CLS_UDF_SIZE_BITS) - 1) ++#define MVPP2_CLS_UDF_SIZE_MASK (((1 << \ ++ MVPP2_CLS_UDF_SIZE_BITS) - 1) << MVPP2_CLS_UDF_SIZE_OFFS) ++/*----------------------------------------------------------------------*/ ++ ++#define MVPP2_CLS_MTU_BASE_REG 0x1900 ++/* in PPv2.1 (feature MAS 3.7) num indicate an mtu reg index ++ * in PPv2.0 num (<=31) indicate eport number , 0-15 pon txq, 16-23 ethernet ++ */ ++#define MVPP2_CLS_MTU_REG(num) (MVPP2_CLS_MTU_BASE_REG + \ ++ ((num) * 4)) ++#define MVPP2_CLS_MTU_OFFS 0 ++#define MVPP2_CLS_MTU_BITS 16 ++#define MVPP2_CLS_MTU_MAX ((1 << \ ++ MVPP2_CLS_MTU_BITS) - 1) ++#define MVPP2_CLS_MTU_MASK (((1 << \ ++ MVPP2_CLS_MTU_BITS) - 1) << MVPP2_CLS_MTU_OFFS) ++/*----------------------------------------------------------------------*/ ++ ++#define MVPP2_CLS_OVERSIZE_RXQ_LOW_REG(port) (0x1980 + ((port) * 4)) ++#define MVPP2_CLS_OVERSIZE_RXQ_LOW_BITS 3 ++#define MVPP2_CLS_OVERSIZE_RXQ_LOW_MASK 0x7 ++#define MVPP2_CLS_SWFWD_P2HQ_REG(port) (0x19b0 + ((port) * 4)) ++#define MVPP2_CLS_SWFWD_PCTRL_REG 0x19d0 ++#define MVPP2_CLS_SWFWD_PCTRL_MASK(port) (1 << (port)) ++ ++/*PPv2.1 new feature MAS 3.14*/ ++#define MVPP2_CLS_SEQ_SIZE_REG 0x19D4 ++#define MVPP2_CLS_SEQ_SIZE_BITS 4 ++#define MVPP2_CLS_SEQ_INDEX_MAX 7 ++#define MVPP2_CLS_SEQ_SIZE_MAX 8 ++#define MVPP2_CLS_SEQ_SIZE_MASK(index) \ ++ (((1 << MVPP2_CLS_SEQ_SIZE_BITS) - 1) << \ ++ (MVPP2_CLS_SEQ_SIZE_BITS * (index))) ++#define MVPP2_CLS_SEQ_SIZE_VAL(index, val) ((val) << ((index) * \ ++ MVPP2_CLS_SEQ_SIZE_BITS)) ++ ++/*PPv2.1 new register MAS 3.18*/ ++#define MVPP2_CLS_PCTRL_BASE_REG 0x1880 ++#define MV_PP2_CLS_PCTRL_REG(port) (MVPP2_CLS_PCTRL_BASE_REG + \ ++ 4 * (port)) ++#define MVPP2_CLS_PCTRL_MH_OFFS 0 ++#define MVPP2_CLS_PCTRL_MH_BITS 16 ++#define MVPP2_CLS_PCTRL_MH_MASK (((1 << \ ++ MVPP2_CLS_PCTRL_MH_BITS) - 1) << MVPP2_CLS_PCTRL_MH_OFFS) ++ ++#define MVPP2_CLS_PCTRL_VIRT_EN_OFFS 16 ++#define MVPP2_CLS_PCTRL_VIRT_EN_MASK (1 << \ ++ MVPP2_CLS_PCTRL_VIRT_EN_OFFS) ++ ++#define MVPP2_CLS_PCTRL_UNI_EN_OFFS 17 ++#define MVPP2_CLS_PCTRL_UNI_EN_MASK (1 << \ ++ MVPP2_CLS_PCTRL_UNI_EN_OFFS) ++ ++/*----------------------------------------------------------------------*/ ++ ++#define MV_PP2_OVERRUN_DROP_REG(port) (0x7000 + 4 * (port)) ++#define MV_PP2_CLS_DROP_REG(port) (0x7020 + 4 * (port)) ++ ++#define MVPP2_CNT_IDX_REG 0x7040 ++/* LKP counters index */ ++#define MVPP2_CNT_IDX_LKP(lkp, way) ((way) << 6 | (lkp)) ++/* Flow counters index */ ++#define MVPP2_CNT_IDX_FLOW(index) (index) ++/* TX counters index */ ++#define MVPP2_CNT_IDX_TX(port, txq) (((16 + port) << 3) | (txq)) ++ ++#define MVPP2_TX_DESC_ENQ_REG 0x7100 ++#define MVPP2_TX_DESC_ENQ_TO_DRAM_REG 0x7104 ++#define MVPP2_TX_BUF_ENQ_TO_DRAM_REG 0x7108 ++#define MVPP2_TX_DESC_HWF_ENQ_REG 0x710c ++#define MVPP2_RX_DESC_ENQ_REG 0x7120 ++#define MVPP2_TX_PKT_DQ_REG 0x7130 ++ ++#define MVPP2_TX_PKT_FULLQ_DROP_REG 0x7200 ++#define MVPP2_TX_PKT_EARLY_DROP_REG 0x7204 ++#define MVPP2_TX_PKT_BM_DROP_REG 0x7208 ++#define MVPP2_TX_PKT_BM_MC_DROP_REG 0x720c ++#define MVPP2_RX_PKT_FULLQ_DROP_REG 0x7220 ++#define MVPP2_RX_PKT_EARLY_DROP_REG 0x7224 ++#define MVPP2_RX_PKT_BM_DROP_REG 0x7228 ++ ++#define MVPP2_BM_DROP_CNTR_REG(pool) (0x7300 + 4 * (pool)) ++#define MVPP2_BM_MC_DROP_CNTR_REG(pool) (0x7340 + 4 * (pool)) ++ ++#define MVPP2_PLCR_GREEN_CNTR_REG(plcr) (0x7400 + 4 * (plcr)) ++#define MVPP2_PLCR_YELLOW_CNTR_REG(plcr) (0x7500 + 4 * (plcr)) ++#define MVPP2_PLCR_RED_CNTR_REG(plcr) (0x7600 + 4 * (plcr)) ++ ++#define MVPP2_CLS_LKP_TBL_HIT_REG 0x7700 ++#define MVPP2_CLS_FLOW_TBL_HIT_REG 0x7704 ++#define MVPP2_CLS4_TBL_HIT_REG 0x7708 ++ ++#define MVPP2_V1_OVERFLOW_MC_DROP_REG 0x770c ++ ++/* Classifier C2 Engine Registers */ ++#define MVPP2_CLS2_TCAM_IDX_REG 0x1B00 ++#define MVPP2_CLS2_TCAM_DATA_REG(idx) (0x1B10 + (idx) * 4) ++#define MVPP2_CLS2_TCAM_INV_REG 0x1B24 ++#define MVPP2_CLS2_TCAM_INV_INVALID_OFF 31 ++#define MVPP2_CLS2_TCAM_INV_INVALID_MASK BIT(31) ++#define MVPP2_CLS2_ACT_DATA_REG 0x1B30 ++#define MVPP2_CLS2_ACT_DATA_TBL_ID_OFF 0 ++#define MVPP2_CLS2_ACT_DATA_TBL_ID_MASK 0x3F ++#define MVPP2_CLS2_ACT_DATA_TBL_SEL_OFF 6 ++#define MVPP2_CLS2_ACT_DATA_TBL_SEL_MASK 0x40 ++#define MVPP2_CLS2_ACT_DATA_TBL_PRI_DSCP_OFF 7 ++#define MVPP2_CLS2_ACT_DATA_TBL_PRI_DSCP_MASK 0x80 ++#define MVPP2_CLS2_ACT_DATA_TBL_GEM_ID_OFF 8 ++#define MVPP2_CLS2_ACT_DATA_TBL_GEM_ID_MASK 0x100 ++#define MVPP2_CLS2_ACT_DATA_TBL_LOW_Q_OFF 9 ++#define MVPP2_CLS2_ACT_DATA_TBL_LOW_Q_MASK 0x200 ++#define MVPP2_CLS2_ACT_DATA_TBL_HIGH_Q_OFF 10 ++#define MVPP2_CLS2_ACT_DATA_TBL_HIGH_Q_MASK 0x400 ++#define MVPP2_CLS2_ACT_DATA_TBL_COLOR_OFF 11 ++#define MVPP2_CLS2_ACT_DATA_TBL_COLOR_MASK 0x800 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_REG 0x1B40 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_LINE_OFF 0 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_LINE_BITS 6 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_LINE_MASK 0x0000003f ++#define MVPP2_CLS2_DSCP_PRI_INDEX_SEL_OFF 6 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_SEL_MASK BIT(6) ++#define MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_OFF 8 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_BITS 6 ++#define MVPP2_CLS2_DSCP_PRI_INDEX_TBL_ID_MASK 0x00003f00 ++#define MVPP2_CLS2_QOS_TBL_REG 0x1B44 ++#define MVPP2_CLS2_QOS_TBL_PRI_OFF 0 ++#define MVPP2_CLS2_QOS_TBL_PRI_BITS 3 ++#define MVPP2_CLS2_QOS_TBL_PRI_MASK 0x00000007 ++#define MVPP2_CLS2_QOS_TBL_DSCP_OFF 3 ++#define MVPP2_CLS2_QOS_TBL_DSCP_BITS 6 ++#define MVPP2_CLS2_QOS_TBL_DSCP_MASK 0x000001f8 ++#define MVPP2_CLS2_QOS_TBL_COLOR_OFF 9 ++#define MVPP2_CLS2_QOS_TBL_COLOR_BITS 3 ++#define MVPP2_CLS2_QOS_TBL_COLOR_MASK 0x00000e00 ++#define MVPP2_CLS2_QOS_TBL_GEMPORT_OFF 12 ++#define MVPP2_CLS2_QOS_TBL_GEMPORT_BITS 12 ++#define MVPP2_CLS2_QOS_TBL_GEMPORT_MASK 0x00fff000 ++#define MVPP2_CLS2_QOS_TBL_QUEUENUM_OFF 24 ++#define MVPP2_CLS2_QOS_TBL_QUEUENUM_BITS 8 ++#define MVPP2_CLS2_QOS_TBL_QUEUENUM_MASK 0xff000000 ++#define MVPP2_CLS2_HIT_CTR_REG 0x1B50 ++#define MVPP2_CLS2_HIT_CTR_OFF 0 ++#define MVPP2_CLS2_HIT_CTR_BITS 32 ++#define MVPP2_CLS2_HIT_CTR_MASK 0xffffffff ++#define MVPP2_CLS2_HIT_CTR_CLR_REG 0x1B54 ++#define MVPP2_CLS2_HIT_CTR_CLR_CLR_OFF 0 ++#define MVPP2_CLS2_HIT_CTR_CLR_CLR_MASK BIT(0) ++#define MVPP2_CLS2_HIT_CTR_CLR_DONE_OFF 1 ++#define MVPP2_CLS2_HIT_CTR_CLR_DONE_MASK BIT(1) ++#define MVPP2_CLS2_ACT_REG 0x1B60 ++#define MVPP2_CLS2_ACT_COLOR_OFF 0 ++#define MVPP2_CLS2_ACT_COLOR_BITS 3 ++#define MVPP2_CLS2_ACT_COLOR_MASK 0x00000007 ++#define MVPP2_CLS2_ACT_PRI_OFF 3 ++#define MVPP2_CLS2_ACT_PRI_BITS 2 ++#define MVPP2_CLS2_ACT_PRI_MASK 0x00000018 ++#define MVPP2_CLS2_ACT_DSCP_OFF 5 ++#define MVPP2_CLS2_ACT_DSCP_BITS 2 ++#define MVPP2_CLS2_ACT_DSCP_MASK 0x00000060 ++#define MVPP2_CLS2_ACT_GEM_OFF 7 ++#define MVPP2_CLS2_ACT_GEM_BITS 2 ++#define MVPP2_CLS2_ACT_GEM_MASK 0x00000180 ++#define MVPP2_CLS2_ACT_QL_OFF 9 ++#define MVPP2_CLS2_ACT_QL_BITS 2 ++#define MVPP2_CLS2_ACT_QL_MASK 0x00000600 ++#define MVPP2_CLS2_ACT_QH_OFF 11 ++#define MVPP2_CLS2_ACT_QH_BITS 2 ++#define MVPP2_CLS2_ACT_QH_MASK 0x00001800 ++#define MVPP2_CLS2_ACT_FRWD_OFF 13 ++#define MVPP2_CLS2_ACT_FRWD_BITS 3 ++#define MVPP2_CLS2_ACT_FRWD_MASK 0x0000e000 ++#define MVPP2_CLS2_ACT_PLCR_OFF 16 ++#define MVPP2_CLS2_ACT_PLCR_BITS 2 ++#define MVPP2_CLS2_ACT_PLCR_MASK 0x00030000 ++#define MVPP2_CLS2_ACT_FLD_EN_OFF 18 ++#define MVPP2_CLS2_ACT_FLD_EN_BITS 1 ++#define MVPP2_CLS2_ACT_FLD_EN_MASK 0x00040000 ++#define MVPP2_CLS2_ACT_RSS_OFF 19 ++#define MVPP2_CLS2_ACT_RSS_BITS 2 ++#define MVPP2_CLS2_ACT_RSS_MASK 0x00180000 ++#define MVPP2_CLS2_ACT_QOS_ATTR_REG 0x1B64 ++#define MVPP2_CLS2_ACT_QOS_ATTR_PRI_OFF 0 ++#define MVPP2_CLS2_ACT_QOS_ATTR_PRI_BITS 3 ++#define MVPP2_CLS2_ACT_QOS_ATTR_PRI_MASK 0x00000007 ++#define MVPP2_CLS2_ACT_QOS_ATTR_PRI_MAX ((1 << MVPP2_CLS2_ACT_QOS_ATTR_PRI_BITS) - 1) ++#define MVPP2_CLS2_ACT_QOS_ATTR_DSCP_OFF 3 ++#define MVPP2_CLS2_ACT_QOS_ATTR_DSCP_BITS 6 ++#define MVPP2_CLS2_ACT_QOS_ATTR_DSCP_MASK 0x000001f8 ++#define MVPP2_CLS2_ACT_QOS_ATTR_DSCP_MAX ((1 << MVPP2_CLS2_ACT_QOS_ATTR_DSCP_BITS) - 1) ++#define MVPP2_CLS2_ACT_QOS_ATTR_GEM_OFF 9 ++#define MVPP2_CLS2_ACT_QOS_ATTR_GEM_BITS 12 ++#define MVPP2_CLS2_ACT_QOS_ATTR_GEM_MASK 0x001ffe00 ++#define MVPP2_CLS2_ACT_QOS_ATTR_GEM_MAX ((1 << MVPP2_CLS2_ACT_QOS_ATTR_GEM_BITS) - 1) ++#define MVPP2_CLS2_ACT_QOS_ATTR_QL_OFF 21 ++#define MVPP2_CLS2_ACT_QOS_ATTR_QL_BITS 3 ++#define MVPP2_CLS2_ACT_QOS_ATTR_QL_MASK 0x00e00000 ++#define MVPP2_CLS2_ACT_QOS_ATTR_QH_OFF 24 ++#define MVPP2_CLS2_ACT_QOS_ATTR_QH_BITS 5 ++#define MVPP2_CLS2_ACT_QOS_ATTR_QH_MASK 0x1f000000 ++#define MVPP2_CLS2_ACT_HWF_ATTR_REG 0x1B68 ++#define MVPP2_CLS2_ACT_HWF_ATTR_DPTR_OFF 1 ++#define MVPP2_CLS2_ACT_HWF_ATTR_DPTR_BITS 15 ++#define MVPP2_CLS2_ACT_HWF_ATTR_DPTR_MASK 0x0000fffe ++#define MVPP2_CLS2_ACT_HWF_ATTR_DPTR_MAX ((1 << MVPP2_CLS2_ACT_HWF_ATTR_DPTR_BITS) - 1) ++#define MVPP2_CLS2_ACT_HWF_ATTR_IPTR_OFF 16 ++#define MVPP2_CLS2_ACT_HWF_ATTR_IPTR_BITS 8 ++#define MVPP2_CLS2_ACT_HWF_ATTR_IPTR_MASK 0x00ff0000 ++#define MVPP2_CLS2_ACT_HWF_ATTR_IPTR_MAX ((1 << MVPP2_CLS2_ACT_HWF_ATTR_IPTR_BITS) - 1) ++#define MVPP2_CLS2_ACT_HWF_ATTR_L4CHK_OFF 24 ++#define MVPP2_CLS2_ACT_HWF_ATTR_L4CHK_BITS 1 ++#define MVPP2_CLS2_ACT_HWF_ATTR_L4CHK_MASK 0x01000000 ++#define MVPP2_CLS2_ACT_HWF_ATTR_MTUIDX_OFF 25 ++#define MVPP2_CLS2_ACT_HWF_ATTR_MTUIDX_BITS 4 ++#define MVPP2_CLS2_ACT_HWF_ATTR_MTUIDX_MASK 0x1e000000 ++#define MVPP2_CLS2_ACT_DUP_ATTR_REG 0x1B6C ++#define MVPP2_CLS2_ACT_DUP_ATTR_DUPID_OFF 0 ++#define MVPP2_CLS2_ACT_DUP_ATTR_DUPID_BITS 8 ++#define MVPP2_CLS2_ACT_DUP_ATTR_DUPID_MASK 0x000000ff ++#define MVPP2_CLS2_ACT_DUP_ATTR_DUPCNT_OFF 8 ++#define MVPP2_CLS2_ACT_DUP_ATTR_DUPCNT_BITS 4 ++#define MVPP2_CLS2_ACT_DUP_ATTR_DUPCNT_MASK 0x00000f00 ++#define MVPP2_CLS2_ACT_DUP_ATTR_PLCRID_OFF 24 ++#define MVPP2_CLS2_ACT_DUP_ATTR_PLCRID_BITS 5 ++#define MVPP2_CLS2_ACT_DUP_ATTR_PLCRID_MASK 0x1f000000 ++#define MVPP2_CLS2_ACT_DUP_ATTR_PLCRBK_OFF 29 ++#define MVPP2_CLS2_ACT_DUP_ATTR_PLCRBK_BITS 1 ++#define MVPP2_CLS2_ACT_DUP_ATTR_PLCRBK_MASK 0x20000000 ++#define MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_OFF 30 ++#define MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_BITS 1 ++#define MVPP2_CLS2_ACT_DUP_ATTR_RSSEN_MASK 0x40000000 ++#define MVPP21_CLS2_ACT_SEQ_ATTR_REG 0x1B70 ++#define MVPP21_CLS2_ACT_SEQ_ATTR_ID 0 ++#define MVPP21_CLS2_ACT_SEQ_ATTR_ID_BITS 8 ++#define MVPP21_CLS2_ACT_SEQ_ATTR_ID_MASK 0x000000ff ++#define MVPP21_CLS2_ACT_SEQ_ATTR_MISS_OFF 8 ++#define MVPP21_CLS2_ACT_SEQ_ATTR_MISS_BITS 1 ++#define MVPP21_CLS2_ACT_SEQ_ATTR_MISS_MASK 0x00000100 ++#define MVPP22_CLS2_ACT_SEQ_ATTR_REG 0x1B70 ++#define MVPP22_CLS2_ACT_SEQ_ATTR_ID 0 ++#define MVPP22_CLS2_ACT_SEQ_ATTR_ID_BITS 16 ++#define MVPP22_CLS2_ACT_SEQ_ATTR_ID_MASK 0x0000ffff ++#define MVPP22_CLS2_ACT_SEQ_ATTR_MISS_OFF 16 ++#define MVPP22_CLS2_ACT_SEQ_ATTR_MISS_BITS 1 ++#define MVPP22_CLS2_ACT_SEQ_ATTR_MISS_MASK 0x0001000 ++#define MVPP2_CLS2_TCAM_CFG0_REG 0x1b80 ++#define MVPP2_CLS2_TCAM_CFG0_EN_OFF 0 ++#define MVPP2_CLS2_TCAM_CFG0_EN_MASK 0x00000001 ++#define MVPP2_CLS2_TCAM_CFG0_SIZE_OFF 1 ++#define MVPP2_CLS2_TCAM_CFG0_SIZE_MASK 0x0000001e ++#define MVPP2_CLS2_TCAM_CTRL_REG 0x1B90 ++#define MVPP2_CLS2_TCAM_CTRL_EN_OFF 0 ++#define MVPP2_CLS2_TCAM_CTRL_EN_MASK 0x0000001 ++ ++/* Classifier C2 QOS Table (DSCP/PRI Table) */ ++#define MVPP2_QOS_TBL_LINE_NUM_PRI 8 ++#define MVPP2_QOS_TBL_NUM_PRI 64 ++#define MVPP2_QOS_TBL_LINE_NUM_DSCP 64 ++#define MVPP2_QOS_TBL_NUM_DSCP 8 ++ ++/*------------------Classifier C3 Top Registers---------------------------*/ ++#define MVPP2_CLS3_KEY_CTRL_REG 0x1C10 ++#define KEY_CTRL_L4 0 ++#define KEY_CTRL_L4_BITS 3 ++#define KEY_CTRL_L4_MAX ((1 << \ ++ KEY_CTRL_L4_BITS) - 1) ++#define KEY_CTRL_L4_MASK (((1 << \ ++ KEY_CTRL_L4_BITS) - 1) << KEY_CTRL_L4) ++#define KEY_CTRL_LKP_TYPE 4 ++#define KEY_CTRL_LKP_TYPE_BITS 6 ++#define KEY_CTRL_LKP_TYPE_MAX ((1 << \ ++ KEY_CTRL_LKP_TYPE_BITS) - 1) ++#define KEY_CTRL_LKP_TYPE_MASK (((1 << \ ++ KEY_CTRL_LKP_TYPE_BITS) - 1) << KEY_CTRL_LKP_TYPE) ++#define KEY_CTRL_PRT_ID_TYPE 12 ++#define KEY_CTRL_PRT_ID_TYPE_BITS 2 ++#define KEY_CTRL_PRT_ID_TYPE_MAX ((1 << \ ++ KEY_CTRL_PRT_ID_TYPE_BITS) - 1) ++#define KEY_CTRL_PRT_ID_TYPE_MASK ((KEY_CTRL_PRT_ID_TYPE_MAX) << \ ++ KEY_CTRL_PRT_ID_TYPE) ++#define KEY_CTRL_PRT_ID 16 ++#define KEY_CTRL_PRT_ID_BITS 8 ++#define KEY_CTRL_PRT_ID_MAX ((1 << \ ++ KEY_CTRL_PRT_ID_BITS) - 1) ++#define KEY_CTRL_PRT_ID_MASK (((1 << \ ++ KEY_CTRL_PRT_ID_BITS) - 1) << KEY_CTRL_PRT_ID) ++#define KEY_CTRL_HEK_SIZE 24 ++#define KEY_CTRL_HEK_SIZE_BITS 6 ++#define KEY_CTRL_HEK_SIZE_MAX 36 ++#define KEY_CTRL_HEK_SIZE_MASK (((1 << \ ++ KEY_CTRL_HEK_SIZE_BITS) - 1) << KEY_CTRL_HEK_SIZE) ++ ++#define MVPP2_CLS3_KEY_HEK_REG(reg_num) (0x1C34 - 4 * (reg_num)) ++ ++#define MVPP2_CLS3_QRY_ACT_REG 0x1C40 ++#define MVPP2_CLS3_QRY_ACT 0 ++ ++#define MVPP2_CLS3_QRY_RES_HASH_REG(hash) (0x1C50 + 4 * (hash)) ++#define MVPP2_CLS3_HASH_BANKS_NUM 8 ++ ++#define MVPP2_CLS3_INIT_HIT_CNT_REG 0x1C80 ++#define MVPP2_CLS3_INIT_HIT_CNT_OFFS 6 ++#define MVPP2_CLS3_INIT_HIT_CNT_BITS 18 ++#define MVPP2_CLS3_INIT_HIT_CNT_MASK (((1 << \ ++ MVPP2_CLS3_INIT_HIT_CNT_BITS) - 1) << MVPP2_CLS3_INIT_HIT_CNT_OFFS) ++#define MVPP2_CLS3_INIT_HIT_CNT_MAX ((1 << \ ++ MVPP2_CLS3_INIT_HIT_CNT_BITS) - 1) ++ ++#define MVPP2_CLS3_HASH_OP_REG 0x1C84 ++#define MVPP2_CLS3_HASH_OP_TBL_ADDR 0 ++#define MVPP2_CLS3_HASH_OP_TBL_ADDR_BITS 12 ++#define MVPP2_CLS3_HASH_OP_TBL_ADDR_MAX ((1 << \ ++ MVPP2_CLS3_HASH_OP_TBL_ADDR_BITS) - 1) ++#define MVPP2_CLS3_HASH_OP_TBL_ADDR_MASK \ ++ ((MVPP2_CLS3_HASH_OP_TBL_ADDR_MAX) << MVPP2_CLS3_HASH_OP_TBL_ADDR) ++#define MVPP2_CLS3_MISS_PTR 12 ++#define MVPP2_CLS3_MISS_PTR_MASK BIT(MVPP2_CLS3_MISS_PTR) ++#define MVPP2_CLS3_HASH_OP_DEL 14 ++#define MVPP2_CLS3_HASH_OP_ADD 15 ++#define MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR 16 ++#define MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR_BITS 8 ++#define MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR_MAX ((1 << \ ++ MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR_BITS) - 1) ++#define MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR_MASK \ ++ ((MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR_MAX) << \ ++ MVPP2_CLS3_HASH_OP_EXT_TBL_ADDR) ++ ++#define MVPP2_CLS3_STATE_REG 0x1C8C ++#define MVPP2_CLS3_STATE_CPU_DONE 0 ++#define MVPP2_CLS3_STATE_CPU_DONE_MASK (1 << \ ++ MVPP2_CLS3_STATE_CPU_DONE) ++#define MVPP2_CLS3_STATE_CLEAR_CTR_DONE 1 ++#define MVPP2_CLS3_STATE_CLEAR_CTR_DONE_MASK (1 << \ ++ MVPP2_CLS3_STATE_CLEAR_CTR_DONE) ++#define MVPP2_CLS3_STATE_SC_DONE 2 ++#define MVPP2_CLS3_STATE_SC_DONE_MASK BIT(MVPP2_CLS3_STATE_SC_DONE) ++#define MVPP2_CLS3_STATE_OCCIPIED 8 ++#define MVPP2_CLS3_STATE_OCCIPIED_BITS 8 ++#define MVPP2_CLS3_STATE_OCCIPIED_MASK (((1 << \ ++ MVPP2_CLS3_STATE_OCCIPIED_BITS) - 1) << MVPP2_CLS3_STATE_OCCIPIED) ++ ++#define MVPP2_CLS3_STATE_SC_STATE 16 ++#define MVPP2_CLS3_STATE_SC_STATE_BITS 2 ++#define MVPP2_CLS3_STATE_SC_STATE_MASK (((1 << \ ++ MVPP2_CLS3_STATE_SC_STATE_BITS) - 1) << MVPP2_CLS3_STATE_SC_STATE) ++ ++/* SCAN STATUS ++ * 0 - scan compleat ++ * 1 - hit counter clear ++ * 3 - scan wait ++ * 4 - scan in progress ++ */ ++ ++#define MVPP2_CLS3_STATE_NO_OF_SC_RES 20 ++#define MVPP2_CLS3_STATE_NO_OF_SC_RES_BITS 9 ++#define MVPP2_CLS3_STATE_NO_OF_SC_RES_MASK (((1 << \ ++ MVPP2_CLS3_STATE_NO_OF_SC_RES_BITS) - 1) << \ ++ MVPP2_CLS3_STATE_NO_OF_SC_RES) ++ ++#define MVPP2_CLS3_DB_INDEX_REG 0x1C90 ++#define MVPP2_CLS3_DB_MISS_OFFS 12 ++#define MVPP2_CLS3_DB_MISS_MASK BIT(MVPP2_CLS3_DB_MISS_OFFS) ++ ++ /* 0-3 valid val*/ ++#define MVPP2_CLS3_HASH_DATA_REG(num) (0x1CA0 + 4 * (num)) ++#define MVPP2_CLS3_HASH_DATA_REG_NUM 4 ++#define MVPP2_CLS3_HASH_EXT_DATA_REG(num) (0x1CC0 + 4 * (num)) ++#define MVPP2_CLS3_HASH_EXT_DATA_REG_NUM 7 ++ ++#define MVPP2_CLS3_CLEAR_COUNTERS_REG 0x1D00 ++#define MVPP2_CLS3_CLEAR_COUNTERS 0 ++#define MVPP2_CLS3_CLEAR_COUNTERS_BITS 7 ++#define MVPP2_CLS3_CLEAR_ALL 0x3f ++#define MVPP2_CLS3_CLEAR_COUNTERS_MAX 0x3F ++#define MVPP2_CLS3_CLEAR_COUNTERS_MASK \ ++ ((MVPP2_CLS3_CLEAR_COUNTERS_MAX) << \ ++ MVPP2_CLS3_CLEAR_COUNTERS) ++ ++#define MVPP2_CLS3_HIT_COUNTER_REG 0x1D08 ++#define MVPP2_CLS3_HIT_COUNTER 0 ++#define MVPP2_CLS3_HIT_COUNTER_BITS 24 ++#define MVPP2_CLS3_HIT_COUNTER_MAX ((1 << \ ++ MVPP2_CLS3_HIT_COUNTER_BITS) - 1) ++#define MVPP2_CLS3_HIT_COUNTER_MASK \ ++ ((MVPP2_CLS3_HIT_COUNTER_MAX) << MVPP2_CLS3_HIT_COUNTER) ++ ++#define MVPP2_CLS3_SC_PROP_REG 0x1D10 ++#define MVPP2_CLS3_SC_PROP_TH_MODE 0 ++#define MVPP2_CLS3_SC_PROP_TH_MODE_MASK (1 << \ ++ MVPP2_CLS3_SC_PROP_TH_MODE) ++#define MVPP2_CLS3_SC_PROP_CLEAR 1 ++#define MVPP2_CLS3_SC_PROP_CLEAR_MASK (1 << \ ++ MVPP2_CLS3_SC_PROP_CLEAR) ++#define MVPP2_CLS3_SC_PROP_LKP_TYPE_EN 3 ++#define MVPP2_CLS3_SC_PROP_LKP_TYPE_EN_MASK (1 << \ ++ MVPP2_CLS3_SC_PROP_LKP_TYPE_EN) ++#define MVPP2_CLS3_SC_PROP_LKP_TYPE 4 ++#define MVPP2_CLS3_SC_PROP_LKP_TYPE_BITS 6 ++#define MVPP2_CLS3_SC_PROP_LKP_TYPE_MAX ((1 << \ ++ MVPP2_CLS3_SC_PROP_LKP_TYPE_BITS) - 1) ++#define MVPP2_CLS3_SC_PROP_LKP_TYPE_MASK \ ++ ((MVPP2_CLS3_SC_PROP_LKP_TYPE_MAX) << MVPP2_CLS3_SC_PROP_LKP_TYPE) ++#define MVPP2_CLS3_SC_PROP_START_ENTRY 16 ++#define MVPP2_CLS3_SC_PROP_START_ENTRY_MASK \ ++ ((MVPP2_CLS3_HASH_OP_TBL_ADDR_MAX) << MVPP2_CLS3_SC_PROP_START_ENTRY) ++ ++#define MVPP2_CLS3_SC_PROP_VAL_REG 0x1D14 ++#define MVPP2_CLS3_SC_PROP_VAL_DELAY 0 ++#define MVPP2_CLS3_SC_PROP_VAL_DELAY_BITS 16 ++#define MVPP2_CLS3_SC_PROP_VAL_DELAY_MAX ((1 << \ ++ MVPP2_CLS3_SC_PROP_VAL_DELAY_BITS) - 1) ++#define MVPP2_CLS3_SC_PROP_VAL_DELAY_MASK \ ++ (MVPP2_CLS3_SC_PROP_VAL_DELAY_MAX << MVPP2_CLS3_SC_PROP_VAL_DELAY) ++ ++#define MVPP2_CLS3_SC_TH_REG 0x1D18 ++#define MVPP2_CLS3_SC_TH 4 ++#define MVPP2_CLS3_SC_TH_BITS 20 ++#define MVPP2_CLS3_SC_TH_MAX ((1 << \ ++ MVPP2_CLS3_SC_TH_BITS) - 1) ++#define MVPP2_CLS3_SC_TH_MASK (((1 << \ ++ MVPP2_CLS3_SC_TH_BITS) - 1) << MVPP2_CLS3_SC_TH) ++ ++#define MVPP2_CLS3_SC_TIMER_REG 0x1D1c ++#define MVPP2_CLS3_SC_TIMER 0 ++#define MVPP2_CLS3_SC_TIMER_BITS 16 ++#define MVPP2_CLS3_SC_TIMER_MASK \ ++ (((1 << MVPP2_CLS3_SC_TIMER_BITS) - 1) << MVPP2_CLS3_SC_TIMER) ++ ++#define MVPP2_CLS3_SC_ACT_REG 0x1D20 ++#define MVPP2_CLS3_SC_ACT 0 ++ ++#define MVPP2_CLS3_SC_INDEX_REG 0x1D28 ++#define MVPP2_CLS3_SC_INDEX 0 ++ ++#define MVPP2_CLS3_SC_RES_REG 0x1D2C ++#define MVPP2_CLS3_SC_RES_ENTRY 0 ++#define MVPP2_CLS3_SC_RES_ENTRY_MASK \ ++ ((MVPP2_CLS3_HASH_OP_TBL_ADDR_MAX) << MVPP2_CLS3_SC_RES_ENTRY) ++#define MVPP2_CLS3_SC_RES_CTR 12 ++#define MVPP2_CLS3_SC_RES_CTR_MASK \ ++ ((MVPP2_CLS3_HIT_COUNTER_MAX) << MVPP2_CLS3_SC_RES_CTR) ++ ++#define MVPP2_CLS3_ACT_REG 0x1D40 ++ ++#define MVPP2_CLS3_ACT_QOS_ATTR_REG 0x1D44 ++ ++#define MVPP2_CLS3_ACT_HWF_ATTR_REG 0x1D48 ++ ++#define MVPP2_CLS3_ACT_DUP_ATTR_REG 0x1D4C ++#define MVPP2_CLS3_ACT_SEQ_L_ATTR_REG 0x1D50 ++#define MVPP2_CLS3_ACT_SEQ_H_ATTR_REG 0x1D54 ++#define MVPP2_CLS3_ACT_SEQ_SIZE 38 ++ ++/* Descriptor Manager Top Registers */ ++#define MVPP2_RXQ_NUM_REG 0x2040 ++ ++#define MVPP2_RXQ_DESC_ADDR_REG 0x2044 ++#define MVPP21_RXQ_DESC_ADDR_SHIFT MVPP21_DESC_ADDR_SHIFT ++#define MVPP21_RXQ_DESC_ADDR_MASK 0xfffffe00 ++ ++#define MVPP22_RXQ_DESC_ADDR_SHIFT MVPP22_DESC_ADDR_SHIFT ++#define MVPP22_RXQ_DESC_ADDR_MASK 0xfffffffe ++ ++#define MVPP2_RXQ_DESC_SIZE_REG 0x2048 ++#define MVPP2_RXQ_DESC_SIZE_MASK 0x3ff0 ++#define MVPP2_RXQ_STATUS_UPDATE_REG(rxq) (0x3000 + 4 * (rxq)) ++#define MVPP2_RXQ_NUM_PROCESSED_OFFSET 0 ++#define MVPP2_RXQ_NUM_NEW_OFFSET 16 ++#define MVPP2_RXQ_STATUS_REG(rxq) (0x3400 + 4 * (rxq)) ++#define MVPP2_RXQ_OCCUPIED_MASK 0x3fff ++#define MVPP2_RXQ_NON_OCCUPIED_OFFSET 16 ++#define MVPP2_RXQ_NON_OCCUPIED_MASK 0x3fff0000 ++#define MVPP2_RXQ_THRESH_REG 0x204c ++#define MVPP2_OCCUPIED_THRESH_OFFSET 0 ++#define MVPP2_MAX_OCCUPIED_THRESH 0x3fff ++#define MVPP2_NON_OCCUPIED_THRESH_OFFSET 16 ++#define MVPP2_NON_OCCUPIED_THRESH_MASK 0x3fff0000 ++#define MVPP2_OCCUPIED_THRESH_MASK MVPP2_MAX_OCCUPIED_THRESH ++#define MVPP2_RXQ_INDEX_REG 0x2050 ++#define MVPP2_TXQ_NUM_REG 0x2080 ++#define MVPP2_TXQ_DESC_ADDR_LOW_REG 0x2084 ++#define MVPP2_TXQ_DESC_ADDR_LOW_SHIFT 0 ++#define MVPP2_TXQ_DESC_ADDR_LOW_MASK 0xfffffe00 ++#define MVPP22_TXQ_DESC_ADDR_HIGH_REG 0x20a8 ++#define MVPP22_TXQ_DESC_ADDR_HIGH_MASK 0xff ++#define MVPP2_TXQ_DESC_SIZE_REG 0x2088 ++#define MVPP2_TXQ_DESC_HWF_SIZE_REG 0x208c ++#define MVPP2_TXQ_DESC_SIZE_MASK 0x3ff0 ++#define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 ++#define MVPP2_TXQ_THRESH_REG 0x2094 ++#define MVPP2_TRANSMITTED_THRESH_OFFSET 16 ++#define MVPP2_MAX_TRANSMITTED_THRESH 0x3fff ++#define MVPP2_TRANSMITTED_THRESH_MASK \ ++ ((MVPP2_MAX_TRANSMITTED_THRESH) << MVPP2_TRANSMITTED_THRESH_OFFSET) ++#define MVPP2_TXQ_INDEX_REG 0x2098 ++#define MVPP2_TXQ_PREF_BUF_REG 0x209c ++#define MVPP2_PREF_BUF_PTR(desc) ((desc) & 0xfff) ++#define MVPP2_PREF_BUF_SIZE_4 (BIT(12) | BIT(13)) ++#define MVPP2_PREF_BUF_SIZE_16 (BIT(12) | BIT(14)) ++#define MVPP2_PREF_BUF_THRESH(val) ((val) << 17) ++#define MVPP2_TXQ_DRAIN_EN_MASK BIT(31) ++#define MVPP2_TXQ_PENDING_REG 0x20a0 ++#define MVPP2_TXQ_PENDING_MASK 0x3fff ++#define MVPP2_TXQ_INT_STATUS_REG 0x20a4 ++ ++#define MVPP21_TXQ_SENT_REG(txq) (0x3c00 + 4 * (txq)) ++#define MVPP21_TRANSMITTED_COUNT_OFFSET 16 ++#define MVPP21_TRANSMITTED_COUNT_MASK 0x3fff0000 ++#define MVPP22_TXQ_SENT_REG(txq) (0x3e00 + 4 * (txq - 128)) ++#define MVPP22_TRANSMITTED_COUNT_OFFSET 16 ++#define MVPP22_TRANSMITTED_COUNT_MASK 0x3fff0000 ++ ++#define MVPP2_TXQ_RSVD_REQ_REG 0x20b0 ++#define MVPP2_TXQ_RSVD_REQ_Q_OFFSET 16 ++#define MVPP2_TXQ_RSVD_RSLT_REG 0x20b4 ++#define MVPP2_TXQ_RSVD_RSLT_MASK 0x3fff ++#define MVPP2_TXQ_RSVD_CLR_REG 0x20b8 ++#define MVPP2_TXQ_RSVD_CLR_OFFSET 16 ++#define MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu) (0x2100 + 4 * (cpu)) ++#define MVPP21_AGGR_TXQ_DESC_ADDR_SHIFT MVPP21_DESC_ADDR_SHIFT ++#define MVPP21_AGGR_TXQ_DESC_ADDR_MASK 0xfffffe00 ++#define MVPP22_AGGR_TXQ_DESC_ADDR_SHIFT MVPP22_DESC_ADDR_SHIFT ++#define MVPP22_AGGR_TXQ_DESC_ADDR_MASK 0xfffffffe ++ ++#define MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu) (0x2140 + 4 * (cpu)) ++#define MVPP2_AGGR_TXQ_DESC_SIZE_MASK 0x3ff0 ++#define MVPP2_AGGR_TXQ_STATUS_REG(cpu) (0x2180 + 4 * (cpu)) ++#define MVPP2_AGGR_TXQ_PENDING_MASK 0x3fff ++#define MVPP2_AGGR_TXQ_INDEX_REG(cpu) (0x21c0 + 4 * (cpu)) ++ ++/* MBUS bridge registers */ ++#define MVPP2_WIN_BASE(w) (0x4000 + ((w) << 2)) ++#define MVPP2_WIN_SIZE(w) (0x4020 + ((w) << 2)) ++#define MVPP2_WIN_REMAP(w) (0x4040 + ((w) << 2)) ++#define MVPP2_BASE_ADDR_ENABLE 0x4060 ++ ++/* Interrupt Cause and Mask registers */ ++#define MVPP22_ISR_TX_THRESHOLD_REG(port) (0x5140 + 4 * (port)) ++#define MVPP22_MAX_ISR_TX_THRESHOLD 0xfffff0 ++#define MVPP22_ISR_TX_THRESHOLD_MASK MVPP22_MAX_ISR_TX_THRESHOLD ++ ++#define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) ++#define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 ++#define MVPP2_ISR_RX_THRESHOLD_MASK MVPP2_MAX_ISR_RX_THRESHOLD ++ ++#define MVPP21_ISR_RXQ_GROUP_REG(port) (0x5400 + 4 * (port)) ++#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 ++#define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf ++#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 ++#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 ++ ++#define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf ++#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 ++ ++#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 ++#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f ++#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 ++#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 ++ ++#define MVPP2_ISR_ENABLE_REG(port) (0x5420 + 4 * (port)) ++#define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff) ++#define MVPP2_ISR_DISABLE_INTERRUPT(mask) (((mask) << 16) & 0xffff0000) ++#define MVPP2_ISR_RX_TX_CAUSE_REG(eth_port) (0x5480 + 4 * (eth_port)) ++#define MVPP21_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff ++#define MVPP22_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xff ++ ++#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK 0xff0000 ++#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET 16 ++ ++#define MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK BIT(24) ++#define MVPP2_CAUSE_FCS_ERR_MASK BIT(25) ++#define MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK BIT(26) ++#define MVPP2_CAUSE_TX_EXCEPTION_SUM_MASK BIT(29) ++#define MVPP2_CAUSE_RX_EXCEPTION_SUM_MASK BIT(30) ++#define MVPP2_CAUSE_MISC_SUM_MASK BIT(31) ++ ++#define MVPP2_ISR_RX_TX_MASK_REG(port) (0x54a0 + 4 * (port)) ++#define MVPP2_ISR_PON_RX_TX_MASK_REG 0x54bc ++#define MVPP2_PON_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff ++#define MVPP2_PON_CAUSE_TXP_OCCUP_DESC_ALL_MASK 0x3fc00000 ++#define MVPP2_PON_CAUSE_MISC_SUM_MASK BIT(31) ++ ++#define MV_PP21_ISR_RX_ERR_CAUSE_REG(port) (0x5500 + 4 * (port)) ++#define MV_PP21_ISR_RX_ERR_CAUSE_NONOCC_MASK 0xffff ++#define MV_PP21_ISR_RX_ERR_CAUSE_DESC_RES_MASK 0xffff0000 ++#define MV_PP21_ISR_RX_ERR_MASK_REG(port) (0x5520 + 4 * (port)) ++ ++#define MV_PP22_ISR_RX_ERR_CAUSE_REG(port) (0x5500 + 4 * (port)) ++#define MV_PP22_ISR_RX_ERR_CAUSE_NONOCC_MASK 0x00ff ++#define MV_PP22_ISR_RX_ERR_CAUSE_DESC_RES_MASK 0xff0000 ++#define MV_PP22_ISR_RX_ERR_MASK_REG(port) (0x5520 + 4 * (port)) ++ ++#define MV_PP2_ISR_TX_ERR_CAUSE_REG(eth_port) (0x5540 + 4 * (eth_port)) ++#define MV_PP2_ISR_TX_ERR_MASK_REG(eth_port) (0x5560 + 4 * (eth_port)) ++ ++#define MVPP2_ISR_MISC_CAUSE_REG 0x55b0 ++#define MVPP2_ISR_MISC_MASK_REG 0x55b4 ++ ++#define MVPP22_ISR_NO_BUF_CAUSE_REG 0x55b8 ++#define MVPP22_ISR_NO_BUF_MASK_REG 0x55bc ++ ++/* Buffer Manager registers */ ++#define MVPP2_BM_POOL_BASE_ADDR_REG(pool) (0x6000 + ((pool) * 4)) ++#define MVPP2_BM_POOL_BASE_ADDR_MASK 0xfffff80 ++#define MVPP2_BM_POOL_SIZE_REG(pool) (0x6040 + ((pool) * 4)) ++#define MVPP21_BM_POOL_SIZE_MASK 0xfff0 ++#define MVPP21_BM_POOL_SIZE_OFFSET 4 ++ ++#define MVPP2_BM_POOL_READ_PTR_REG(pool) (0x6080 + ((pool) * 4)) ++#define MVPP21_BM_POOL_READ_PTR_REG MVPP2_BM_POOL_READ_PTR_REG ++ ++#define MVPP21_BM_POOL_GET_READ_PTR_MASK 0xfff0 ++#define MVPP2_BM_POOL_PTRS_NUM_REG(pool) (0x60c0 + ((pool) * 4)) ++#define MVPP21_BM_POOL_PTRS_NUM_REG MVPP2_BM_POOL_PTRS_NUM_REG ++ ++#define MVPP21_BM_POOL_PTRS_NUM_MASK 0xfff0 ++ ++#define MVPP22_BM_POOL_SIZE_MASK 0xfff8 ++#define MVPP22_BM_POOL_SIZE_OFFSET 3 ++ ++/* Use PPV21 Pool Size both for PPV21/PPV22, deliberately ignore PPV22 */ ++#define MVPP2_BM_POOL_SIZE_MASK MVPP21_BM_POOL_SIZE_MASK ++#define MVPP2_BM_POOL_SIZE_OFFSET MVPP21_BM_POOL_SIZE_OFFSET ++#undef MVPP22_BM_POOL_SIZE_MASK ++#undef MVPP22_BM_POOL_SIZE_OFFSET ++ ++#define MVPP22_BM_POOL_READ_PTR_REG MVPP2_BM_POOL_READ_PTR_REG ++#define MVPP22_BM_POOL_GET_READ_PTR_MASK 0xfff8 ++#define MVPP22_BM_POOL_PTRS_NUM_REG MVPP2_BM_POOL_PTRS_NUM_REG ++#define MVPP22_BM_POOL_PTRS_NUM_MASK 0xfff8 ++ ++#define MVPP2_BM_BPPI_READ_PTR_REG(pool) (0x6100 + ((pool) * 4)) ++#define MVPP2_BM_BPPI_PTRS_NUM_REG(pool) (0x6140 + ((pool) * 4)) ++#define MVPP2_BM_BPPI_PTR_NUM_MASK 0x7ff ++#define MVPP2_BM_BPPI_PREFETCH_FULL_MASK BIT(16) ++#define MVPP2_BM_POOL_CTRL_REG(pool) (0x6200 + ((pool) * 4)) ++#define MVPP2_BM_START_MASK BIT(0) ++#define MVPP2_BM_STOP_MASK BIT(1) ++#define MVPP2_BM_STATE_MASK BIT(4) ++#define MVPP2_BM_LOW_THRESH_OFFS 8 ++#define MVPP2_BM_LOW_THRESH_MASK 0x7f00 ++#define MVPP2_BM_LOW_THRESH_VALUE(val) ((val) << \ ++ MVPP2_BM_LOW_THRESH_OFFS) ++#define MVPP2_BM_HIGH_THRESH_OFFS 16 ++#define MVPP2_BM_HIGH_THRESH_MASK 0x7f0000 ++#define MVPP2_BM_HIGH_THRESH_VALUE(val) ((val) << \ ++ MVPP2_BM_HIGH_THRESH_OFFS) ++#define MVPP2_BM_BPPI_HIGH_THRESH 0x1E ++#define MVPP2_BM_BPPI_LOW_THRESH 0x1C ++#define MVPP23_BM_BPPI_8POOL_HIGH_THRESH 0x34 ++#define MVPP23_BM_BPPI_8POOL_LOW_THRESH 0x28 ++#define MVPP2_BM_INTR_CAUSE_REG(pool) (0x6240 + ((pool) * 4)) ++#define MVPP2_BM_RELEASED_DELAY_MASK BIT(0) ++#define MVPP2_BM_ALLOC_FAILED_MASK BIT(1) ++#define MVPP2_BM_BPPE_EMPTY_MASK BIT(2) ++#define MVPP2_BM_BPPE_FULL_MASK BIT(3) ++#define MVPP2_BM_AVAILABLE_BP_LOW_MASK BIT(4) ++#define MVPP2_BM_INTR_MASK_REG(pool) (0x6280 + ((pool) * 4)) ++ ++#define MVPP21_BM_UNUSED_PTR_THRESH_REG(pool) (0x62c0 + ((pool) * 4)) ++#define MVPP21_BM_UNUSED_PTR_THRESH_MASK 0xfff0 ++#define MVPP22_BM_UNUSED_PTR_THRESH_REG(pool) (0x62c0 + ((pool) * 4)) ++#define MVPP22_BM_UNUSED_PTR_THRESH_MASK 0xfff8 ++ ++#define MVPP22_BM_POOL_BASE_ADDR_HIGH_REG 0x6310 ++#define MVPP22_BM_POOL_BASE_ADDR_HIGH_MASK 0xff ++#define MVPP23_BM_8POOL_MODE BIT(8) ++ ++#define MVPP2_BM_PHY_ALLOC_REG(pool) (0x6400 + ((pool) * 4)) ++#define MVPP2_BM_PHY_ALLOC_GRNTD_MASK BIT(0) ++#define MVPP2_BM_VIRT_ALLOC_REG 0x6440 ++ ++#define MVPP22_BM_PHY_VIRT_HIGH_ALLOC_REG 0x6444 ++#define MVPP22_BM_PHY_HIGH_ALLOC_OFFSET 0 ++#define MVPP22_BM_VIRT_HIGH_ALLOC_OFFSET 8 ++#define MVPP22_BM_VIRT_HIGH_ALLOC_MASK 0xff00 ++#define MVPP22_BM_PHY_HIGH_ALLOC_MASK 0xff ++ ++#define MVPP2_BM_PHY_RLS_REG(pool) (0x6480 + ((pool) * 4)) ++#define MVPP2_BM_PHY_RLS_MC_BUFF_MASK BIT(0) ++#define MVPP2_BM_PHY_RLS_PRIO_EN_MASK BIT(1) ++#define MVPP2_BM_PHY_RLS_GRNTD_MASK BIT(2) ++ ++#define MVPP2_BM_VIRT_RLS_REG 0x64c0 ++ ++#define MVPP21_BM_MC_RLS_REG 0x64c4 /* Not a mixup */ ++#define MVPP21_BM_MC_ID_MASK 0xfff ++#define MVPP21_BM_FORCE_RELEASE_MASK BIT(12) ++ ++#define MVPP22_BM_PHY_VIRT_HIGH_RLS_REG 0x64c4 /* Not a mixup */ ++ ++#define MVPP22_BM_PHY_HIGH_RLS_OFFSET 0 ++#define MVPP22_BM_VIRT_HIGH_RLS_OFFST 8 ++ ++#define MVPP22_BM_MC_RLS_REG 0x64d4 /* Not a mixup */ ++#define MVPP22_BM_MC_ID_MASK 0xfff ++#define MVPP22_BM_FORCE_RELEASE_MASK BIT(12) ++ ++#define MVPP2_BM_PRIO_CTRL_REG 0x6800 ++ ++#define MVPP2_BM_PRIO_IDX_REG 0x6810 ++#define MVPP2_BM_PRIO_IDX_BITS 8 ++#define MVPP2_BM_PRIO_IDX_MAX 255 ++#define MVPP2_BM_PRIO_IDX_MASK 0xff ++ ++#define MVPP2_BM_CPU_QSET_REG 0x6814 ++ ++#define MVPP2_BM_CPU_SHORT_QSET_OFFS 0 ++#define MVPP2_BM_CPU_SHORT_QSET_MASK (0x7f << \ ++ MVPP2_BM_CPU_SHORT_QSET_OFFS) ++ ++#define MVPP2_BM_CPU_LONG_QSET_OFFS 8 ++#define MVPP2_BM_CPU_LONG_QSET_MASK (0x7f << \ ++ MVPP2_BM_CPU_LONG_QSET_OFFS) ++ ++#define MVPP2_BM_HWF_QSET_REG 0x6818 ++ ++#define MVPP2_BM_HWF_SHORT_QSET_OFFS 0 ++#define MVPP2_BM_HWF_SHORT_QSET_MASK (0x7f << \ ++ MVPP2_BM_HWF_SHORT_QSET_OFFS) ++ ++#define MVPP2_BM_HWF_LONG_QSET_OFFS 8 ++#define MVPP2_BM_HWF_LONG_QSET_MASK (0x7f << \ ++ MVPP2_BM_HWF_LONG_QSET_OFFS) ++ ++#define MVPP2_BM_QSET_SET_MAX_REG 0x6820 ++ ++#define MVPP2_BM_QSET_MAX_SHARED_OFFS 0 ++#define MVPP2_BM_QSET_MAX_GRNTD_OFFS 16 ++ ++#define MVPP2_BM_QSET_MAX_SHARED_MASK (0xffff << \ ++ MVPP2_BM_QSET_MAX_SHARED_OFFS) ++#define MVPP2_BM_QSET_MAX_GRNTD_MASK (0xffff << \ ++ MVPP2_BM_QSET_MAX_GRNTD_OFFS) ++ ++#define MVPP2_BM_QSET_SET_CNTRS_REG 0x6824 ++ ++/* TX Scheduler registers */ ++#define MVPP2_TXP_SCHED_PORT_INDEX_REG 0x8000 ++#define MVPP2_TXP_SCHED_Q_CMD_REG 0x8004 ++#define MVPP2_TXP_SCHED_ENQ_MASK 0xff ++#define MVPP2_TXP_SCHED_DISQ_OFFSET 8 ++#define MVPP2_TXP_SCHED_CMD_1_REG 0x8010 ++#define MVPP2_TXP_SCHED_FIXED_PRIO_REG 0x8014 ++#define MVPP2_TXP_SCHED_PERIOD_REG 0x8018 ++#define MVPP2_TXP_SCHED_MTU_REG 0x801c ++#define MVPP2_TXP_MTU_MAX 0x7FFFF ++#define MVPP2_TXP_SCHED_REFILL_REG 0x8020 ++#define MVPP2_TXP_REFILL_TOKENS_OFFS 0 ++#define MVPP2_TXP_REFILL_TOKENS_MAX 0x7FFFF ++#define MVPP2_TXP_REFILL_TOKENS_ALL_MASK 0x7ffff ++#define MVPP2_TXP_REFILL_TOKENS_MASK(val) ((val) << \ ++ MVPP2_TXP_REFILL_TOKENS_OFFS) ++#define MVPP2_TXP_REFILL_PERIOD_MAX 0x3FF ++#define MVPP2_TXP_REFILL_PERIOD_ALL_MASK 0x3ff00000 ++#define MVPP2_TXP_REFILL_PERIOD_MASK(v) ((v) << 20) ++#define MVPP2_TXP_SCHED_TOKEN_SIZE_REG 0x8024 ++#define MVPP2_TXP_SCHED_TOKEN_CNTR_REG 0x8028 ++#define MVPP2_TXP_TOKEN_SIZE_MAX 0xffffffff ++#define MVPP2_TXQ_SCHED_REFILL_REG(q) (0x8040 + ((q) << 2)) ++#define MVPP2_TXQ_REFILL_TOKENS_OFFS 0 ++#define MVPP2_TXQ_REFILL_TOKENS_MAX 0x7FFFF ++#define MVPP2_TXQ_REFILL_TOKENS_ALL_MASK 0x7ffff ++#define MVPP2_TXQ_REFILL_TOKENS_MASK(val) ((val) << \ ++ MVPP2_TXQ_REFILL_TOKENS_OFFS) ++#define MVPP2_TXQ_REFILL_PERIOD_MAX 0x3FF ++#define MVPP2_TXQ_REFILL_PERIOD_ALL_MASK 0x3ff00000 ++#define MVPP2_TXQ_REFILL_PERIOD_MASK(v) ((v) << 20) ++#define MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(q) (0x8060 + ((q) << 2)) ++#define MVPP2_TXQ_TOKEN_SIZE_MAX 0x7fffffff ++#define MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(q) (0x8080 + ((q) << 2)) ++#define MVPP2_TXQ_TOKEN_CNTR_MAX 0xffffffff ++/* Transmit Queue Arbiter Configuration (TQxAC) */ ++#define MVPP2_TXQ_SCHED_WRR_REG(q) (0x80A0 + ((q) << 2)) ++#define MVPP2_TXQ_WRR_WEIGHT_OFFS 0 ++#define MVPP2_TXQ_WRR_WEIGHT_MAX 0xFF ++#define MVPP2_TXQ_WRR_WEIGHT_ALL_MASK (MVPP2_TXQ_WRR_WEIGHT_MAX << \ ++ MVPP2_TXQ_WRR_WEIGHT_OFFS) ++#define MVPP2_TXQ_WRR_WEIGHT_MASK(weigth) ((weigth) << \ ++ MVPP2_TXQ_WRR_WEIGHT_OFFS) ++#define MVPP2_TXQ_WRR_BYTE_COUNT_OFFS 8 ++#define MVPP2_TXQ_WRR_BYTE_COUNT_MASK (0x3FFFF << \ ++ MVPP2_TXQ_WRR_BYTE_COUNT_OFFS) ++ ++/* TX general registers */ ++#define MVPP2_TX_SNOOP_REG 0x8800 ++#define MVPP2_TX_SNOOP_EN_MASK BIT(0) ++#define MVPP2_TX_SNOOP_EN_MASK BIT(0) ++#define MVPP22_TX_SNOOP_HWF_EN_MASK BIT(1) ++ ++#define MVPP21_TX_FIFO_THRESH_REG 0x8804 ++#define MVPP21_TX_FIFO_THRESH_MASK 0x7ff ++#define MVPP22_TX_FIFO_THRESH_REG(eth_tx_port) (0x8840 + ((eth_tx_port) << 2)) ++#define MVPP22_TX_FIFO_THRESH_MASK 0x3fff ++ ++#define MVPP22_TX_FIFO_SIZE_REG(eth_tx_port) (0x8860 + ((eth_tx_port) << 2)) ++#define MVPP22_TX_FIFO_SIZE_MASK 0xf ++ ++#define MVPP2_TX_PORT_FLUSH_REG 0x8810 ++#define MVPP2_TX_PORT_FLUSH_MASK(port) (1 << (port)) ++ ++ /* Same for PPv21/PPv22 */ ++#define MVPP2_TX_BAD_FCS_CNTR_REG(eth_tx_port) (0x8940 + ((eth_tx_port) << 2)) ++ /* Same for PPv21/PPv22 */ ++#define MVPP2_TX_DROP_CNTR_REG(eth_tx_port) (0x8980 + ((eth_tx_port) << 2)) ++ ++#define MVPP2_TX_ETH_DSEC_THRESH_REG(eth_tx_port)(0x8a40 + \ ++ ((eth_tx_port) << 2)) ++#define MVPP2_TX_ETH_DSEC_THRESH_MASK 0x7f0 ++ ++#define MVPP22_TX_EGR_PIPE_DELAY_REG(eth_tx_port)(0x8a80 + \ ++ ((eth_tx_port) << 2)) ++#define MVPP22_TX_EGR_PIPE_DELAY_MASK 0x3fff ++#define MVPP22_TX_PTP_DISPATCH_ENABLE_MASK BIT(30) ++ ++#define MVPP22_TX_PORT_SHORT_HDR_REG 0x8ac0 ++#define MVPP22_TX_PORT_SHORT_HDR_MASK 0x7f ++ ++/* LMS registers */ ++#define MVPP2_SRC_ADDR_MIDDLE 0x24 ++#define MVPP2_SRC_ADDR_HIGH 0x28 ++#define MVPP2_PHY_AN_CFG0_REG 0x34 ++#define MVPP2_PHY_AN_STOP_SMI0_MASK BIT(7) ++#define MVPP2_MIB_COUNTERS_BASE(port) (0x1000 + ((port) >> 1) * \ ++ 0x400 + (port) * 0x400) ++#define MVPP2_MIB_LATE_COLLISION 0x7c ++#define MVPP2_ISR_SUM_MASK_REG 0x220c ++#define MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG 0x305c ++#define MVPP2_EXT_GLOBAL_CTRL_DEFAULT 0x27 ++ ++/* Per-port registers */ ++#define MVPP2_GMAC_CTRL_0_REG 0x0 ++#define MVPP2_GMAC_PORT_EN_MASK BIT(0) ++#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 ++#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc ++#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) ++#define MVPP2_GMAC_CTRL_1_REG 0x4 ++#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) ++#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) ++#define MVPP2_GMAC_PCS_LB_EN_BIT 6 ++#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) ++#define MVPP2_GMAC_SA_LOW_OFFS 7 ++#define MVPP2_GMAC_CTRL_2_REG 0x8 ++#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) ++#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) ++#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) ++#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) ++#define MVPP2_GMAC_AUTONEG_CONFIG 0xc ++#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) ++#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) ++#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) ++#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) ++#define MVPP2_GMAC_AN_SPEED_EN BIT(7) ++#define MVPP2_GMAC_FC_ADV_EN BIT(9) ++#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) ++#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) ++#define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c ++#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 ++#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 ++#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ ++ MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) ++ ++#define MVPP2_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff ++ ++/* The two bytes Marvell header. Either contains a special value used ++ * by Marvell switches when a specific hardware mode is enabled (not ++ * supported by this driver) or is filled automatically by zeroes on ++ * the RX side. Those two bytes being at the front of the Ethernet ++ * header, they allow to have the IP header aligned on a 4 bytes ++ * boundary automatically: the hardware skips those two bytes on its ++ * own. ++ */ ++#define MVPP2_MH_SIZE 2 ++#define MVPP2_ETH_TYPE_LEN 2 ++#define MVPP2_PPPOE_HDR_SIZE 8 ++#define MVPP2_VLAN_TAG_LEN 4 ++#define MVPP2_VLAN_TAG_EDSA_LEN 8 ++ ++/* Lbtd 802.3 type */ ++#define MVPP2_IP_LBDT_TYPE 0xfffa ++ ++#define MVPP2_CACHE_LINE_SIZE L1_CACHE_BYTES ++#define MVPP2_CACHE_LINE_MASK (L1_CACHE_BYTES - 1) ++ ++#define MVPP2_BM_BUFF_ALIGN_SIZE 32 ++#define MVPP2_TX_CSUM_MAX_SIZE 9800 ++ ++/* Timeout constants */ ++#define MVPP2_TX_DISABLE_TIMEOUT_MSEC 1000 ++#define MVPP2_TX_PENDING_TIMEOUT_MSEC 1000 ++ ++#define MVPP2_TX_MTU_MAX 0x7ffff ++ ++/* Maximum number of T-CONTs of PON port */ ++#define MVPP2_MAX_TCONT 16 ++ ++/* Maximum number of supported ports */ ++#define MVPP2_MAX_PORTS 4 ++ ++/* Maximum number of supported cells */ ++#define MVPP2_MAX_CELLS 4 ++ ++/* Maximum number of TXQs used by single port */ ++#define MVPP2_MAX_TXQ 8 ++ ++/* Maximum number of RXQs used by single port per CPU */ ++#define MVPP2_MAX_RXQ_PER_CPU 8 ++ ++/* Dfault number of RXQs in use */ ++#define MVPP2_DEFAULT_RXQ 4 ++ ++#define MVPP2_TXQ_TOTAL_NUM (128/*pon*/ + \ ++ MVPP2_MAX_PORTS * MVPP2_MAX_TXQ/*eth*/) ++ ++/* Max number of Rx descriptors */ ++#define MVPP2_MAX_RXD 1280 ++ ++/* Max number of Tx descriptors */ ++#define MVPP2_MAX_TXD 2048 ++ ++/* Amount of Tx descriptors that can be reserved at once by CPU */ ++#define MVPP2_CPU_DESC_CHUNK 128 ++ ++/* Max number of Tx descriptors in each aggregated queue */ ++#define MVPP2_AGGR_TXQ_SIZE 2048 ++ ++/* Descriptor aligned size */ ++#define MVPP2_DESC_ALIGNED_SIZE 32 ++#define MVPP2_DESC_Q_ALIGN 512 ++ ++#define MVPP2_DESCQ_MEM_SIZE(descs) (descs * MVPP2_DESC_ALIGNED_SIZE + \ ++ MVPP2_DESC_Q_ALIGN) ++#define MVPP2_DESCQ_MEM_ALIGN(mem) (ALIGN(mem, MVPP2_DESC_Q_ALIGN)) ++ ++/* TX descriptor data offset mask */ ++#define MVPP2_TX_DESC_DATA_OFFSET 0XFF ++ ++/* TX FIFO constants */ ++#define MVPP2_TX_FIFO_DATA_SIZE_10KB 0xa ++#define MVPP2_TX_FIFO_DATA_SIZE_3KB 0x3 ++ ++#define MVPP2_TX_FIFO_MINIMUM_THRESHOLD 256 ++#define MVPP2_TX_FIFO_THRESHOLD_10KB (MVPP2_TX_FIFO_DATA_SIZE_10KB * 1024 - \ ++ MVPP2_TX_FIFO_MINIMUM_THRESHOLD) ++#define MVPP2_TX_FIFO_THRESHOLD_3KB (MVPP2_TX_FIFO_DATA_SIZE_3KB * 1024 - \ ++ MVPP2_TX_FIFO_MINIMUM_THRESHOLD) ++ ++/* RX FIFO constants */ ++#define MVPP2_RX_FIFO_PORT_DATA_SIZE 0x2000 ++#define MVPP2_RX_FIFO_PORT_ATTR_SIZE 0x80 ++#define MVPP2_RX_FIFO_PORT_MIN_PKT 0x80 ++#define MVPP2_RX_FIFO_DATA_SIZE_32KB 0x8000 ++#define MVPP2_RX_FIFO_DATA_SIZE_8KB 0x2000 ++#define MVPP2_RX_FIFO_DATA_SIZE_4KB 0x1000 ++#define MVPP2_RX_FIFO_ATTR_SIZE_32KB 0x200 ++#define MVPP2_RX_FIFO_ATTR_SIZE_8KB 0x80 ++#define MVPP2_RX_FIFO_ATTR_SIZE_4KB 0x40 ++ ++/* RX buffer constants */ ++#define MVPP2_SKB_SHINFO_SIZE \ ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) ++ ++#define MVPP2_RX_PKT_SIZE(mtu) \ ++ ALIGN((mtu) + MVPP2_MH_SIZE + MVPP2_VLAN_TAG_LEN + \ ++ ETH_HLEN + ETH_FCS_LEN, MVPP2_BM_BUFF_ALIGN_SIZE) ++ ++#define MVPP2_RX_MTU_SIZE(pkt_size) \ ++ (pkt_size - MVPP2_MH_SIZE - MVPP2_VLAN_TAG_LEN - \ ++ ETH_HLEN - ETH_FCS_LEN) ++ ++#define MVPP2_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD) ++#define MVPP2_RX_TOTAL_SIZE(buf_size) ((buf_size) + MVPP2_SKB_SHINFO_SIZE) ++#define MVPP2_RX_MAX_PKT_SIZE(total_size) \ ++ ((total_size) - NET_SKB_PAD - MVPP2_SKB_SHINFO_SIZE) ++ ++/* IPv6 max L3 address size */ ++#define MVPP2_MAX_L3_ADDR_SIZE 16 ++ ++/* Port flags */ ++#define MVPP2_F_LOOPBACK BIT(0) /* Loopback port */ ++#define MVPP2_F_IFCAP_NETMAP BIT(1) /* netmap port */ ++#define MVPP2_F_IF_MUSDK BIT(2) /* musdk port */ ++/* BIT(3) reserved for MVPP2_F_IF_MUSDK_DOWN */ ++#define MVPP2_F_IF_TX_ON BIT(4) /* TX enabled in device-start */ ++ ++/* Marvell tag types */ ++enum mv_pp2x_tag_type { ++ MVPP2_TAG_TYPE_NONE = 0, ++ MVPP2_TAG_TYPE_MH = 1, ++ MVPP2_TAG_TYPE_DSA = 2, ++ MVPP2_TAG_TYPE_EDSA = 3, ++ MVPP2_TAG_TYPE_VLAN = 4, ++ MVPP2_TAG_TYPE_LAST = 5 ++}; ++ ++/* Parser constants */ ++#define MVPP2_PRS_TCAM_SRAM_SIZE 256 ++#define MVPP2_PRS_TCAM_WORDS 6 ++#define MVPP2_PRS_SRAM_WORDS 4 ++#define MVPP2_PRS_FLOW_ID_SIZE 64 ++#define MVPP2_PRS_FLOW_ID_MASK 0x3f ++#define MVPP2_PRS_TCAM_ENTRY_VALID 0 ++#define MVPP2_PRS_TCAM_ENTRY_INVALID 1 ++#define MVPP2_PRS_TCAM_DSA_TAGGED_BIT BIT(5) ++#define MVPP2_PRS_IPV4_HEAD 0x40 ++#define MVPP2_PRS_IPV4_HEAD_MASK 0xf0 ++#define MVPP2_PRS_IPV4_MC 0xe0 ++#define MVPP2_PRS_IPV4_MC_MASK 0xf0 ++#define MVPP2_PRS_IPV4_BC_MASK 0xff ++#define MVPP2_PRS_IPV4_IHL 0x5 ++#define MVPP2_PRS_IPV4_IHL_MASK 0xf ++#define MVPP2_PRS_IPV6_MC 0xff ++#define MVPP2_PRS_IPV6_MC_MASK 0xff ++#define MVPP2_PRS_IPV6_HOP_MASK 0xff ++#define MVPP2_PRS_TCAM_PROTO_MASK 0xff ++#define MVPP2_PRS_TCAM_PROTO_MASK_L 0x3f ++#define MVPP2_PRS_DBL_VLANS_MAX 100 ++#define MVPP2_PRS_CAST_MASK 0x1 ++#define MVPP2_PRS_MCAST_VAL 0x1 ++#define MVPP2_PRS_UCAST_VAL 0x0 ++ ++/* There is a TCAM range reserved for MAC entries, range size is 80 ++ * 1 BC MAC entry for all ports ++ * 4 M2M entries, 1 entry per port, and 4 ports in all ++ * 25 UC/MC MAC filter entries per port ++ * It is assumed that there are 3 ports for filter, not including loopback port ++ */ ++#define MVPP2_PRS_MAC_UC_MC_FILT_MAX 25 ++#define MVPP2_PRS_MAC_RANGE_SIZE 80 ++ ++/* There is a TCAM range reserved for VLAN filtering entries, range size is 33 ++ * 10 VLAN ID filter entries per port ++ * 1 default VLAN filter entry per port ++ * It is assumed that there are 3 ports for filter, not including loopback port ++ */ ++#define MVPP2_PRS_VLAN_FILT_MAX 11 ++#define MVPP2_PRS_VLAN_FILT_RANGE_SIZE 33 ++ ++#define MVPP2_PRS_VLAN_FILT_MAX_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 2) ++#define MVPP2_PRS_VLAN_FILT_DFLT_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 1) ++ ++#define MVPP2_PRS_VID_H_WORD 0xf00 ++#define MVPP2_PRS_VID_H_WORD_MASK 0xf ++#define MVPP2_PRS_VID_H_WORD_SHIFT 8 ++#define MVPP2_PRS_VID_L_WORD_MASK 0xff ++#define MVPP2_PRS_VID_TCAM_BYTE 2 ++ ++/* Tcam structure: ++ * - lookup ID - 4 bits ++ * - port ID - 1 byte ++ * - additional information - 1 byte ++ * - header data - 8 bytes ++ * The fields are represented by MVPP2_PRS_TCAM_DATA_REG(5)->(0). ++ */ ++#define MVPP2_PRS_AI_BITS 8 ++#define MVPP2_PRS_PORT_MASK 0xff ++#define MVPP2_PRS_LU_MASK 0xf ++#define MVPP2_PRS_TCAM_AI_BYTE 16 ++#define MVPP2_PRS_TCAM_PORT_BYTE 17 ++#define MVPP2_PRS_TCAM_LU_BYTE 20 ++#define MVPP2_PRS_TCAM_EN_OFFS(offs) ((offs) + 2) ++#define MVPP2_PRS_TCAM_INV_WORD 5 ++#define MVPP2_PRS_TCAM_INV_MASK BIT(31) ++ ++/* Tcam entries ID */ ++#define MVPP2_PE_DROP_ALL 0 ++#define MVPP2_PE_FIRST_FREE_TID 1 ++#define MVPP2_PE_MAC_RANGE_END (MVPP2_PE_VID_FILT_RANGE_START - 1) ++#define MVPP2_PE_MAC_RANGE_START (MVPP2_PE_MAC_RANGE_END - MVPP2_PRS_MAC_RANGE_SIZE + 1) ++#define MVPP2_PE_VID_FILT_RANGE_END (MVPP2_PRS_TCAM_SRAM_SIZE - 31) ++#define MVPP2_PE_VID_FILT_RANGE_START (MVPP2_PE_VID_FILT_RANGE_END - MVPP2_PRS_VLAN_FILT_RANGE_SIZE + 1) ++#define MVPP2_PE_LAST_FREE_TID (MVPP2_PE_MAC_RANGE_START - 1) ++#define MVPP2_PE_IP6_EXT_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 30) ++#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 29) ++#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28) ++#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 27) ++#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 22) ++#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 21) ++#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 20) ++#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 19) ++#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18) ++#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17) ++#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16) ++#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15) ++#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14) ++#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 13) ++#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 12) ++#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 11) ++#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 10) ++#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 9) ++#define MVPP2_PE_VID_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 8) ++#define MVPP2_PE_VID_EDSA_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 7) ++#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 6) ++#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 5) ++#define MVPP2_PE_FC_DROP (MVPP2_PRS_TCAM_SRAM_SIZE - 4) ++#define MVPP2_PE_MAC_MC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 3) ++#define MVPP2_PE_MAC_UC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2) ++#define MVPP2_PE_MAC_NON_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 1) ++ ++/* Sram structure ++ * The fields are represented by MVPP2_PRS_TCAM_DATA_REG(3)->(0). ++ */ ++#define MVPP2_PRS_SRAM_RI_OFFS 0 ++#define MVPP2_PRS_SRAM_RI_WORD 0 ++#define MVPP2_PRS_SRAM_RI_BITS 32 ++#define MVPP2_PRS_SRAM_RI_CTRL_OFFS 32 ++#define MVPP2_PRS_SRAM_RI_CTRL_WORD 1 ++#define MVPP2_PRS_SRAM_RI_CTRL_BITS 32 ++#define MVPP2_PRS_SRAM_SHIFT_OFFS 64 ++#define MVPP2_PRS_SRAM_SHIFT_BITS 8 ++#define MVPP2_PRS_SRAM_SHIFT_SIGN_BIT 72 ++#define MVPP2_PRS_SRAM_UDF_OFFS 73 ++#define MVPP2_PRS_SRAM_UDF_BITS 8 ++#define MVPP2_PRS_SRAM_UDF_MASK 0xff ++#define MVPP2_PRS_SRAM_UDF_SIGN_BIT 81 ++#define MVPP2_PRS_SRAM_UDF_TYPE_OFFS 82 ++#define MVPP2_PRS_SRAM_UDF_TYPE_MASK 0x7 ++#define MVPP2_PRS_SRAM_UDF_TYPE_L3 1 ++#define MVPP2_PRS_SRAM_UDF_TYPE_L4 4 ++#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS 85 ++#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_BITS 2 ++#define MVPP2_PRS_SRAM_OP_SEL_BITS 5 ++#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK 0x3 ++#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD 1 ++#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_IP4_ADD 2 ++#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_IP6_ADD 3 ++#define MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS 87 ++#define MVPP2_PRS_SRAM_OP_SEL_UDF_BITS 2 ++#define MVPP2_PRS_SRAM_OP_SEL_UDF_MASK 0x3 ++#define MVPP2_PRS_SRAM_OP_SEL_UDF_ADD 0 ++#define MVPP2_PRS_SRAM_OP_SEL_UDF_IP4_ADD 2 ++#define MVPP2_PRS_SRAM_OP_SEL_UDF_IP6_ADD 3 ++#define MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS 89 ++#define MVPP2_PRS_SRAM_AI_OFFS 90 ++#define MVPP2_PRS_SRAM_AI_CTRL_OFFS 98 ++#define MVPP2_PRS_SRAM_AI_CTRL_BITS 8 ++#define MVPP2_PRS_SRAM_AI_MASK 0xff ++#define MVPP2_PRS_SRAM_NEXT_LU_OFFS 106 ++#define MVPP2_PRS_SRAM_NEXT_LU_MASK 0xf ++#define MVPP2_PRS_SRAM_LU_DONE_BIT 110 ++#define MVPP2_PRS_SRAM_LU_GEN_BIT 111 ++ ++/* Sram result info bits assignment */ ++#define MVPP2_PRS_RI_MAC_ME_MASK 0x1 ++#define MVPP2_PRS_RI_DSA_MASK 0x2 ++#define MVPP2_PRS_RI_VLAN_OFFS 2 ++#define MVPP2_PRS_RI_VLAN_MASK 0xc ++#define MVPP2_PRS_RI_VLAN_NONE 0x0 ++#define MVPP2_PRS_RI_VLAN_SINGLE BIT(2) ++#define MVPP2_PRS_RI_VLAN_DOUBLE BIT(3) ++#define MVPP2_PRS_RI_VLAN_TRIPLE (BIT(2) | BIT(3)) ++#define MVPP2_PRS_RI_CPU_CODE_MASK 0x70 ++#define MVPP2_PRS_RI_CPU_CODE_RX_SPEC BIT(4) ++#define MVPP2_PRS_RI_L2_CAST_OFFS 9 ++#define MVPP2_PRS_RI_L2_CAST_MASK 0x600 ++#define MVPP2_PRS_RI_L2_UCAST 0x0 ++#define MVPP2_PRS_RI_L2_MCAST BIT(9) ++#define MVPP2_PRS_RI_L2_BCAST BIT(10) ++#define MVPP2_PRS_RI_PPPOE_MASK 0x800 ++#define MVPP2_PRS_RI_L3_PROTO_MASK 0x7000 ++#define MVPP2_PRS_RI_L3_UN 0x0 ++#define MVPP2_PRS_RI_L3_IP4 BIT(12) ++#define MVPP2_PRS_RI_L3_IP4_OPT BIT(13) ++#define MVPP2_PRS_RI_L3_IP4_OTHER (BIT(12) | BIT(13)) ++#define MVPP2_PRS_RI_L3_IP6 BIT(14) ++#define MVPP2_PRS_RI_L3_IP6_EXT (BIT(12) | BIT(14)) ++#define MVPP2_PRS_RI_L3_ARP (BIT(13) | BIT(14)) ++#define MVPP2_PRS_RI_L3_ADDR_MASK 0x18000 ++#define MVPP2_PRS_RI_L3_UCAST 0x0 ++#define MVPP2_PRS_RI_L3_MCAST BIT(15) ++#define MVPP2_PRS_RI_L3_BCAST (BIT(15) | BIT(16)) ++#define MVPP2_PRS_RI_IP_FRAG_MASK 0x20000 ++#define MVPP2_PRS_RI_IP_FRAG_TRUE BIT(17) ++#define MVPP2_PRS_RI_IP_FRAG_FALSE 0x0 ++#define MVPP2_PRS_RI_UDF3_MASK 0x300000 ++#define MVPP2_PRS_RI_UDF3_RX_SPECIAL BIT(21) ++#define MVPP2_PRS_RI_L4_PROTO_MASK 0x1c00000 ++#define MVPP2_PRS_RI_L4_TCP BIT(22) ++#define MVPP2_PRS_RI_L4_UDP BIT(23) ++#define MVPP2_PRS_RI_L4_OTHER (BIT(22) | BIT(23)) ++#define MVPP2_PRS_RI_UDF7_MASK 0x60000000 ++#define MVPP2_PRS_RI_UDF7_IP6_LITE BIT(29) ++#define MVPP2_PRS_RI_DROP_MASK 0x80000000 ++ ++/* Sram additional info bits assignment */ ++#define MVPP2_PRS_IPV4_DIP_AI_BIT BIT(0) ++#define MVPP2_PRS_IPV6_NO_EXT_AI_BIT BIT(0) ++#define MVPP2_PRS_IPV6_EXT_AI_BIT BIT(1) ++#define MVPP2_PRS_IPV6_EXT_AH_AI_BIT BIT(2) ++#define MVPP2_PRS_IPV6_EXT_AH_LEN_AI_BIT BIT(3) ++#define MVPP2_PRS_IPV6_EXT_AH_L4_AI_BIT BIT(4) ++#define MVPP2_PRS_SINGLE_VLAN_AI 0 ++#define MVPP2_PRS_DBL_VLAN_AI_BIT BIT(7) ++#define MVPP2_PRS_EDSA_VID_AI_BIT BIT(0) ++ ++#define MVPP2_PRS_SRAM_SHIFT_MASK ((1 << \ ++ MVPP2_PRS_SRAM_SHIFT_BITS) - 1) ++ ++/* DSA/EDSA type */ ++#define MVPP2_PRS_TAGGED true ++#define MVPP2_PRS_UNTAGGED false ++#define MVPP2_PRS_EDSA true ++#define MVPP2_PRS_DSA false ++ ++/* lkpid table structure */ ++#define MVPP2_FLOWID_RXQ 0 ++#define MVPP2_FLOWID_RXQ_BITS 8 ++#define MVPP2_FLOWID_RXQ_MASK (((1 << \ ++ MVPP2_FLOWID_RXQ_BITS) - 1) << MVPP2_FLOWID_RXQ) ++ ++#define MVPP2_FLOWID_MODE 8 ++#define MVPP2_FLOWID_MODE_BITS 8 ++#define MVPP2_FLOWID_MODE_MASK (((1 << \ ++ MVPP2_FLOWID_MODE_BITS) - 1) << MVPP2_FLOWID_MODE) ++#define MVPP2_FLOWID_MODE_MAX ((1 << MVPP2_FLOWID_MODE_BITS) - 1) ++ ++#define MVPP2_FLOWID_FLOW 16 ++#define MVPP2_FLOWID_FLOW_BITS 9 ++#define MVPP2_FLOWID_FLOW_MASK (((1 << \ ++ MVPP2_FLOWID_FLOW_BITS) - 1) << MVPP2_FLOWID_FLOW) ++ ++#define MVPP2_FLOWID_EN 25 /*one bit */ ++#define MVPP2_FLOWID_EN_MASK BIT(MVPP2_FLOWID_EN) ++ ++/* flow table structure */ ++#define MVPP2_FLOW_TBL_SIZE 512 ++/*------------------------- DWORD 0 --------------------------------- */ ++#define MVPP2_FLOW_LAST 0 ++#define MVPP2_FLOW_LAST_MASK 1 /*one bit*/ ++ ++#define MVPP2_FLOW_ENGINE 1 ++#define MVPP2_FLOW_ENGINE_BITS 3 ++#define MVPP2_FLOW_ENGINE_MASK (((1 << \ ++ MVPP2_FLOW_ENGINE_BITS) - 1) << MVPP2_FLOW_ENGINE) ++#define MVPP2_FLOW_ENGINE_MAX 7 /* valid value 1 - 7 */ ++ ++#define MVPP2_FLOW_PORT_ID 4 ++#define MVPP2_FLOW_PORT_ID_BITS 8 ++#define MVPP2_FLOW_PORT_ID_MASK (((1 << \ ++ MVPP2_FLOW_PORT_ID_BITS) - 1) << MVPP2_FLOW_PORT_ID) ++#define MVPP2_FLOW_PORT_ID_MAX ((1 << MVPP2_FLOW_PORT_ID_BITS) - 1) ++ ++#define MVPP2_FLOW_PORT_TYPE 12 ++#define MVPP2_FLOW_PORT_TYPE_BITS 2 ++#define MVPP2_FLOW_PORT_TYPE_MASK (((1 << \ ++ MVPP2_FLOW_PORT_TYPE_BITS) - 1) << MVPP2_FLOW_PORT_TYPE) ++#define MVPP2_FLOW_PORT_TYPE_MAX 2 /* valid value 0 - 2 */ ++ ++#define MVPP2_FLOW_PPPOE 14 ++#define MVPP2_FLOW_PPPOE_BITS 2 ++#define MVPP2_FLOW_PPPOE_MASK (((1 << \ ++ MVPP2_FLOW_PPPOE_BITS) - 1) << MVPP2_FLOW_PPPOE) ++#define MVPP2_FLOW_PPPOE_MAX 2 /* valid value 0 - 2 */ ++ ++#define MVPP2_FLOW_VLAN 16 ++#define MVPP2_FLOW_VLAN_BITS 3 ++#define MVPP2_FLOW_VLAN_MASK (((1 << \ ++ MVPP2_FLOW_VLAN_BITS) - 1) << MVPP2_FLOW_VLAN) ++#define MVPP2_FLOW_VLAN_MAX ((1 << MVPP2_FLOW_VLAN_BITS) - 1) ++ ++#define MVPP2_FLOW_MACME 19 ++#define MVPP2_FLOW_MACME_BITS 2 ++#define MVPP2_FLOW_MACME_MASK (((1 << \ ++ MVPP2_FLOW_MACME_BITS) - 1) << MVPP2_FLOW_MACME) ++#define MVPP2_FLOW_MACME_MAX 2 /* valid value 0 - 2 */ ++ ++#define MVPP2_FLOW_UDF7 21 ++#define MVPP2_FLOW_UDF7_BITS 2 ++#define MVPP2_FLOW_UDF7_MASK (((1 << \ ++ MVPP2_FLOW_UDF7_BITS) - 1) << MVPP2_FLOW_UDF7) ++#define MVPP2_FLOW_UDF7_MAX ((1 << MVPP2_FLOW_UDF7_BITS) - 1) ++ ++#define MVPP2_FLOW_PORT_ID_SEL 23 ++#define MVPP2_FLOW_PORT_ID_SEL_MASK BIT(MVPP2_FLOW_PORT_ID_SEL) ++ ++/*----------------------- DWORD 1 ------------------------------------ */ ++ ++#define MVPP2_FLOW_FIELDS_NUM 0 ++#define MVPP2_FLOW_FIELDS_NUM_BITS 3 ++#define MVPP2_FLOW_FIELDS_NUM_MASK (((1 << \ ++ MVPP2_FLOW_FIELDS_NUM_BITS) - 1) << MVPP2_FLOW_FIELDS_NUM) ++#define MVPP2_FLOW_FIELDS_NUM_MAX 4 /*valid vaue 0 - 4 */ ++ ++#define MVPP2_FLOW_LKP_TYPE 3 ++#define MVPP2_FLOW_LKP_TYPE_BITS 6 ++#define MVPP2_FLOW_LKP_TYPE_MASK (((1 << \ ++ MVPP2_FLOW_LKP_TYPE_BITS) - 1) << MVPP2_FLOW_LKP_TYPE) ++#define MVPP2_FLOW_LKP_TYPE_MAX ((1 << MVPP2_FLOW_LKP_TYPE_BITS) - 1) ++ ++#define MVPP2_FLOW_FIELD_PRIO 9 ++#define MVPP2_FLOW_FIELD_PRIO_BITS 6 ++#define MVPP2_FLOW_FIELD_PRIO_MASK (((1 << \ ++ MVPP2_FLOW_FIELD_PRIO_BITS) - 1) << MVPP2_FLOW_FIELD_PRIO) ++#define MVPP2_FLOW_FIELD_PRIO_MAX ((1 << MVPP2_FLOW_FIELD_PRIO_BITS) - 1) ++ ++#define MVPP2_FLOW_SEQ_CTRL 15 ++#define MVPP2_FLOW_SEQ_CTRL_BITS 3 ++#define MVPP2_FLOW_SEQ_CTRL_MASK (((1 << \ ++ MVPP2_FLOW_SEQ_CTRL_BITS) - 1) << MVPP2_FLOW_SEQ_CTRL) ++#define MVPP2_FLOW_SEQ_CTRL_MAX 4 ++ ++/*------------------------- DWORD 2 ---------------------------------- */ ++#define MVPP2_FLOW_FIELD0_ID 0 ++#define MVPP2_FLOW_FIELD1_ID 6 ++#define MVPP2_FLOW_FIELD2_ID 12 ++#define MVPP2_FLOW_FIELD3_ID 18 ++ ++#define MVPP2_FLOW_FIELD_ID_BITS 6 ++#define MVPP2_FLOW_FIELD_ID(num) (MVPP2_FLOW_FIELD0_ID + \ ++ (MVPP2_FLOW_FIELD_ID_BITS * (num))) ++#define MVPP2_FLOW_FIELD_MASK(num) (((1 << \ ++ MVPP2_FLOW_FIELD_ID_BITS) - 1) << (MVPP2_FLOW_FIELD_ID_BITS * (num))) ++#define MVPP2_FLOW_FIELD_MAX ((1 << MVPP2_FLOW_FIELD_ID_BITS) - 1) ++ ++/* lookup id attribute define */ ++#define MVPP2_PRS_FL_ATTR_VLAN_BIT BIT(0) ++#define MVPP2_PRS_FL_ATTR_IP4_BIT BIT(1) ++#define MVPP2_PRS_FL_ATTR_IP6_BIT BIT(2) ++#define MVPP2_PRS_FL_ATTR_ARP_BIT BIT(3) ++#define MVPP2_PRS_FL_ATTR_FRAG_BIT BIT(4) ++#define MVPP2_PRS_FL_ATTR_TCP_BIT BIT(5) ++#define MVPP2_PRS_FL_ATTR_UDP_BIT BIT(6) ++ ++/* PP22 RSS Registers */ ++#define MVPP22_RSS_IDX_REG 0x1500 ++#define MVPP22_RSS_IDX_ENTRY_NUM_OFF 0 ++#define MVPP22_RSS_IDX_ENTRY_NUM_MASK 0x1F ++#define MVPP22_RSS_IDX_TBL_NUM_OFF 8 ++#define MVPP22_RSS_IDX_TBL_NUM_MASK 0x700 ++#define MVPP22_RSS_IDX_RXQ_NUM_OFF 16 ++#define MVPP22_RSS_IDX_RXQ_NUM_MASK 0xFF0000 ++#define MVPP22_RSS_RXQ2RSS_TBL_REG 0x1504 ++#define MVPP22_RSS_RXQ2RSS_TBL_POINT_OFF 0 ++#define MVPP22_RSS_RXQ2RSS_TBL_POINT_MASK 0x7 ++#define MVPP22_RSS_TBL_ENTRY_REG 0x1508 ++#define MVPP22_RSS_TBL_ENTRY_OFF 0 ++#define MVPP22_RSS_TBL_ENTRY_MASK 0xFF ++#define MVPP22_RSS_WIDTH_REG 0x150c ++#define MVPP22_RSS_WIDTH_OFF 0 ++#define MVPP22_RSS_WIDTH_MASK 0xF ++#define MVPP22_RSS_HASH_SEL_REG 0x1510 ++#define MVPP22_RSS_HASH_SEL_OFF 0 ++#define MVPP22_RSS_HASH_SEL_MASK 0x1 ++/* RSS consant */ ++#define MVPP22_RSS_TBL_NUM 8 ++#define MVPP22_RSS_TBL_LINE_NUM 32 ++#define MVPP22_RSS_WIDTH_MAX 8 ++ ++/* MAC entries, shadow udf */ ++enum mv_pp2x_prs_udf { ++ MVPP2_PRS_UDF_MAC_DEF, ++ MVPP2_PRS_UDF_MAC_RANGE, ++ MVPP2_PRS_UDF_L2_DEF, ++ MVPP2_PRS_UDF_L2_DEF_COPY, ++ MVPP2_PRS_UDF_L2_USER, ++}; ++ ++/* L2 cast in parser result info */ ++enum mv_pp2x_l2_cast { ++ MVPP2_PRS_MAC_UC, ++ MVPP2_PRS_MAC_MC, ++ MVPP2_PRS_MAC_BC, ++}; ++ ++/* Lookup ID */ ++enum mv_pp2x_prs_lookup { ++ MVPP2_PRS_LU_MH, ++ MVPP2_PRS_LU_MAC, ++ MVPP2_PRS_LU_DSA, ++ MVPP2_PRS_LU_VLAN, ++ MVPP2_PRS_LU_VID, ++ MVPP2_PRS_LU_L2, ++ MVPP2_PRS_LU_PPPOE, ++ MVPP2_PRS_LU_IP4, ++ MVPP2_PRS_LU_IP6, ++ MVPP2_PRS_LU_FLOWS, ++ MVPP2_PRS_LU_LAST, ++}; ++ ++/* L3 cast enum */ ++enum mv_pp2x_prs_l3_cast { ++ MVPP2_PRS_L3_UNI_CAST, ++ MVPP2_PRS_L3_MULTI_CAST, ++ MVPP2_PRS_L3_BROAD_CAST ++}; ++ ++/* Packet flow ID */ ++enum mv_pp2x_prs_flow { ++ MVPP2_PRS_FL_START = 8, ++ MVPP2_PRS_FL_IP4_TCP_NF_UNTAG = MVPP2_PRS_FL_START, ++ MVPP2_PRS_FL_IP4_UDP_NF_UNTAG, ++ MVPP2_PRS_FL_IP4_TCP_NF_TAG, ++ MVPP2_PRS_FL_IP4_UDP_NF_TAG, ++ MVPP2_PRS_FL_IP6_TCP_NF_UNTAG, ++ MVPP2_PRS_FL_IP6_UDP_NF_UNTAG, ++ MVPP2_PRS_FL_IP6_TCP_NF_TAG, ++ MVPP2_PRS_FL_IP6_UDP_NF_TAG, ++ MVPP2_PRS_FL_IP4_TCP_FRAG_UNTAG, ++ MVPP2_PRS_FL_IP4_UDP_FRAG_UNTAG, ++ MVPP2_PRS_FL_IP4_TCP_FRAG_TAG, ++ MVPP2_PRS_FL_IP4_UDP_FRAG_TAG, ++ MVPP2_PRS_FL_IP6_TCP_FRAG_UNTAG, ++ MVPP2_PRS_FL_IP6_UDP_FRAG_UNTAG, ++ MVPP2_PRS_FL_IP6_TCP_FRAG_TAG, ++ MVPP2_PRS_FL_IP6_UDP_FRAG_TAG, ++ MVPP2_PRS_FL_IP4_UNTAG, /* non-TCP, non-UDP, same for below */ ++ MVPP2_PRS_FL_IP4_TAG, ++ MVPP2_PRS_FL_IP6_UNTAG, ++ MVPP2_PRS_FL_IP6_TAG, ++ MVPP2_PRS_FL_NON_IP_UNTAG, ++ MVPP2_PRS_FL_NON_IP_TAG, ++ MVPP2_PRS_FL_LAST, ++ MVPP2_PRS_FL_TCAM_NUM = 52, /* The parser TCAM lines needed to ++ *generate flow ID ++ */ ++}; ++ ++enum mv_pp2x_cls_engine_num { ++ MVPP2_CLS_ENGINE_C2 = 1, ++ MVPP2_CLS_ENGINE_C3A, ++ MVPP2_CLS_ENGINE_C3B, ++ MVPP2_CLS_ENGINE_C4, ++ MVPP2_CLS_ENGINE_C3HA = 6, ++ MVPP2_CLS_ENGINE_C3HB, ++}; ++ ++enum mv_pp2x_cls_lkp_type { ++ MVPP2_CLS_LKP_HASH = 0, ++ MVPP2_CLS_LKP_VLAN_PRI, ++ MVPP2_CLS_LKP_DSCP_PRI, ++ MVPP2_CLS_LKP_DEFAULT, ++ MVPP2_CLS_LKP_MAX, ++}; ++ ++enum mv_pp2x_cls_fl_pri { ++ MVPP2_CLS_FL_COS_PRI = 0, ++ MVPP2_CLS_FL_RSS_PRI, ++}; ++ ++enum mv_pp2x_cls_filed_id { ++ MVPP2_CLS_FIELD_IP4SA = 0x10, ++ MVPP2_CLS_FIELD_IP4DA = 0x11, ++ MVPP2_CLS_FIELD_IP6SA = 0x17, ++ MVPP2_CLS_FIELD_IP6DA = 0x1A, ++ MVPP2_CLS_FIELD_L4SIP = 0x1D, ++ MVPP2_CLS_FIELD_L4DIP = 0x1E, ++}; ++ ++enum mv_pp2x_cos_type { ++ MVPP2_COS_TYPE_DEF = 0, ++ MVPP2_COS_TYPE_VLAN, ++ MVPP2_COS_TYPE_DSCP, ++}; ++ ++enum mv_pp2x_rss_hash_mode { ++ MVPP2_RSS_HASH_2T = 0, ++ MVPP2_RSS_HASH_5T, ++}; ++ ++enum mv_pp2x_mac_del_option { ++ MVPP2_DEL_MAC_ALL = 0, ++ MVPP2_DEL_MAC_NOT_IN_LIST, ++}; ++ ++struct mv_pp2x_prs_result_info { ++ u32 ri; ++ u32 ri_mask; ++}; ++ ++struct mv_pp2x_prs_flow_id { ++ u32 flow_id; ++ struct mv_pp2x_prs_result_info prs_result; ++}; ++ ++/* Classifier constants */ ++#define MVPP2_CLS_FLOWS_TBL_SIZE 512 ++#define MVPP2_CLS_FLOWS_TBL_DATA_WORDS 3 ++#define MVPP2_CLS_FLOWS_TBL_FIELDS_MAX 4 ++#define MVPP2_CLS_FLOWS_TBL_SWAP_SIZE 20 ++ ++#define MVPP2_CLS_LKP_TBL_SIZE 64 ++ ++/* BM cookie (32 bits) definition */ ++#define MVPP2_BM_COOKIE_POOL_OFFS 8 ++#define MVPP2_BM_COOKIE_CPU_OFFS 24 ++ ++/* BM short pool packet size ++ * These value assure that for SWF the total number ++ * of bytes allocated for each buffer will be 512 ++ */ ++#define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_SHORT_FRAME_SIZE) ++#define MVPP2_BM_LONG_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_LONG_FRAME_SIZE) ++#define MVPP2_BM_JUMBO_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_JUMBO_FRAME_SIZE) ++ ++#define MVPP2_BM_SHORT_FRAME_SIZE 1024 ++#define MVPP2_BM_LONG_FRAME_SIZE 2048 ++#define MVPP2_BM_JUMBO_FRAME_SIZE 10240 ++ ++enum mv_pp2x_bm_pool_log_num { ++ MVPP2_BM_SWF_SHORT_POOL, ++ MVPP2_BM_SWF_LONG_POOL, ++ MVPP2_BM_SWF_JUMBO_POOL, ++ MVPP2_BM_SWF_NUM_POOLS ++}; ++ ++/* The mv_pp2x_tx_desc and mv_pp2x_rx_desc structures describe the ++ * layout of the transmit and reception DMA descriptors, and their ++ * layout is therefore defined by the hardware design ++ */ ++ ++#define MVPP2_TXD_L3_OFF_SHIFT 0 ++#define MVPP2_TXD_IP_HLEN_SHIFT 8 ++#define MVPP2_TXD_BUF_MOD BIT(7) ++#define MVPP2_TXD_L4_CSUM_FRAG BIT(13) ++#define MVPP2_TXD_L4_CSUM_NOT BIT(14) ++#define MVPP2_TXD_IP_CSUM_DISABLE BIT(15) ++#define MVPP2_TXD_PADDING_DISABLE BIT(23) ++#define MVPP2_TXD_L4_UDP BIT(24) ++#define MVPP2_TXD_L3_IP6 BIT(26) ++#define MVPP2_TXD_L_DESC BIT(28) ++#define MVPP2_TXD_F_DESC BIT(29) ++ ++#define MVPP2_RXD_ERR_SUMMARY BIT(15) ++#define MVPP2_RXD_ERR_CODE_MASK (BIT(13) | BIT(14)) ++#define MVPP2_RXD_ERR_CRC 0x0 ++#define MVPP2_RXD_ERR_OVERRUN BIT(13) ++#define MVPP2_RXD_ERR_RESOURCE (BIT(13) | BIT(14)) ++#define MVPP2_RXD_BM_POOL_ID_OFFS 16 ++#define MVPP2_RXD_BM_POOL_ID_MASK (BIT(16) | BIT(17) | BIT(18)) ++#define MVPP2_RXD_HWF_SYNC BIT(21) ++#define MVPP2_RXD_L4_CSUM_OK BIT(22) ++#define MVPP2_RXD_IP4_HEADER_ERR BIT(24) ++#define MVPP2_RXD_L4_TCP BIT(25) ++#define MVPP2_RXD_L4_UDP BIT(26) ++#define MVPP2_RXD_L3_IP4 BIT(28) ++#define MVPP2_RXD_L3_IP6 BIT(30) ++#define MVPP2_RXD_BUF_HDR BIT(31) ++/* Sub fields of "parserInfo" field */ ++#define MVPP2_RXD_LKP_ID_OFFS 0 ++#define MVPP2_RXD_LKP_ID_BITS 6 ++#define MVPP2_RXD_LKP_ID_MASK (((1 << \ ++ MVPP2_RXD_LKP_ID_BITS) - 1) << MVPP2_RXD_LKP_ID_OFFS) ++#define MVPP2_RXD_CPU_CODE_OFFS 6 ++#define MVPP2_RXD_CPU_CODE_BITS 3 ++#define MVPP2_RXD_CPU_CODE_MASK (((1 << \ ++ MVPP2_RXD_CPU_CODE_BITS) - 1) << MVPP2_RXD_CPU_CODE_OFFS) ++#define MVPP2_RXD_PPPOE_BIT 9 ++#define MVPP2_RXD_PPPOE_MASK BIT(MVPP2_RXD_PPPOE_BIT) ++#define MVPP2_RXD_L3_CAST_OFFS 10 ++#define MVPP2_RXD_L3_CAST_BITS 2 ++#define MVPP2_RXD_L3_CAST_MASK (((1 << \ ++ MVPP2_RXD_L3_CAST_BITS) - 1) << MVPP2_RXD_L3_CAST_OFFS) ++#define MVPP2_RXD_L2_CAST_OFFS 12 ++#define MVPP2_RXD_L2_CAST_BITS 2 ++#define MVPP2_RXD_L2_CAST_MASK (((1 << \ ++ MVPP2_RXD_L2_CAST_BITS) - 1) << MVPP2_RXD_L2_CAST_OFFS) ++#define MVPP2_RXD_VLAN_INFO_OFFS 14 ++#define MVPP2_RXD_VLAN_INFO_BITS 2 ++#define MVPP2_RXD_VLAN_INFO_MASK (((1 << \ ++ MVPP2_RXD_VLAN_INFO_BITS) - 1) << MVPP2_RXD_VLAN_INFO_OFFS) ++/* Bits of "bmQset" field */ ++#define MVPP2_RXD_BUFF_QSET_NUM_OFFS 0 ++#define MVPP2_RXD_BUFF_QSET_NUM_MASK (0x7f << MVPP2_RXD_BUFF_QSET_NUM_OFFS) ++#define MVPP2_RXD_BUFF_TYPE_OFFS 7 ++#define MVPP2_RXD_BUFF_TYPE_MASK (0x1 << MVPP2_RXD_BUFF_TYPE_OFFS) ++/* Bits of "status" field */ ++#define MVPP2_RXD_L3_OFFSET_OFFS 0 ++#define MVPP2_RXD_L3_OFFSET_MASK (0x7F << MVPP2_RXD_L3_OFFSET_OFFS) ++#define MVPP2_RXD_IP_HLEN_OFFS 8 ++#define MVPP2_RXD_IP_HLEN_MASK (0x1F << MVPP2_RXD_IP_HLEN_OFFS) ++#define MVPP2_RXD_ES_BIT 15 ++#define MVPP2_RXD_ES_MASK BIT(MVPP2_RXD_ES_BIT) ++#define MVPP2_RXD_HWF_SYNC_BIT 21 ++#define MVPP2_RXD_HWF_SYNC_MASK BIT(MVPP2_RXD_HWF_SYNC_BIT) ++#define MVPP2_RXD_L4_CHK_OK_BIT 22 ++#define MVPP2_RXD_L4_CHK_OK_MASK BIT(MVPP2_RXD_L4_CHK_OK_BIT) ++#define MVPP2_RXD_IP_FRAG_BIT 23 ++#define MVPP2_RXD_IP_FRAG_MASK BIT(MVPP2_RXD_IP_FRAG_BIT) ++#define MVPP2_RXD_IP4_HEADER_ERR_BIT 24 ++#define MVPP2_RXD_IP4_HEADER_ERR_MASK BIT(MVPP2_RXD_IP4_HEADER_ERR_BIT) ++#define MVPP2_RXD_L4_OFFS 25 ++#define MVPP2_RXD_L4_MASK (7 << MVPP2_RXD_L4_OFFS) ++/* Value 0 - N/A, 3-7 - User Defined */ ++#define MVPP2_RXD_L3_OFFS 28 ++#define MVPP2_RXD_L3_MASK (7 << MVPP2_RXD_L3_OFFS) ++/* Value 0 - N/A, 6-7 - User Defined */ ++#define MVPP2_RXD_L3_IP4_OPT (2 << MVPP2_RXD_L3_OFFS) ++#define MVPP2_RXD_L3_IP4_OTHER (3 << MVPP2_RXD_L3_OFFS) ++#define MVPP2_RXD_L3_IP6_EXT (5 << MVPP2_RXD_L3_OFFS) ++#define MVPP2_RXD_BUF_HDR_BIT 31 ++#define MVPP2_RXD_BUF_HDR_MASK BIT(MVPP2_RXD_BUF_HDR_BIT) ++/* status field MACROs */ ++#define MVPP2_RXD_L3_IS_IP4(status) (((status) & \ ++ MVPP2_RXD_L3_MASK) == MVPP2_RXD_L3_IP4) ++#define MVPP2_RXD_L3_IS_IP4_OPT(status) (((status) & \ ++ MVPP2_RXD_L3_MASK) == MVPP2_RXD_L3_IP4_OPT) ++#define MVPP2_RXD_L3_IS_IP4_OTHER(status) (((status) & \ ++ MVPP2_RXD_L3_MASK) == MVPP2_RXD_L3_IP4_OTHER) ++#define MVPP2_RXD_L3_IS_IP6(status) (((status) & \ ++ MVPP2_RXD_L3_MASK) == MVPP2_RXD_L3_IP6) ++#define MVPP2_RXD_L3_IS_IP6_EXT(status) (((status) & \ ++ MVPP2_RXD_L3_MASK) == MVPP2_RXD_L3_IP6_EXT) ++#define MVPP2_RXD_L4_IS_UDP(status) (((status) & \ ++ MVPP2_RXD_L4_MASK) == MVPP2_RXD_L4_UDP) ++#define MVPP2_RXD_L4_IS_TCP(status) (((status) & \ ++ MVPP2_RXD_L4_MASK) == MVPP2_RXD_L4_TCP) ++#define MVPP2_RXD_IP4_HDR_ERR(status) ((status) & \ ++ MVPP2_RXD_IP4_HEADER_ERR_MASK) ++#define MVPP2_RXD_IP4_FRG(status) ((status) & \ ++ MVPP2_RXD_IP_FRAG_MASK) ++#define MVPP2_RXD_L4_CHK_OK(status) ((status) & \ ++ MVPP2_RXD_L4_CHK_OK_MASK) ++ ++struct pp21_specific_tx_desc { ++ u32 buf_phys_addr; /* physical addr of transmitted buffer */ ++ u32 buf_cookie; /* cookie for access to TX buffer in tx path */ ++ u32 rsrvd_hw_cmd[3]; /* hw_cmd (for future use, BM, PON, PNC) */ ++ u32 rsrvd1; /* reserved (for future use) */ ++}; ++ ++struct pp22_specific_tx_desc { ++ u64 rsrvd_hw_cmd1; /* hw_cmd (BM, PON, PNC) */ ++ u64 buf_phys_addr_hw_cmd2; ++ u64 buf_cookie_bm_qset_hw_cmd3; ++ /* cookie for access to RX buffer in rx path */ ++ /* cookie for access to RX buffer in rx path */ ++ /* bm_qset (for future use, BM) */ ++ /* classify_info (for future use, PnC) */ ++}; ++ ++union pp2x_specific_tx_desc { ++ struct pp21_specific_tx_desc pp21; ++ struct pp22_specific_tx_desc pp22; ++}; ++ ++struct mv_pp2x_tx_desc { ++ u32 command; /* Options used by HW for packet xmitting */ ++ u8 packet_offset; /* the offset from the buffer beginning */ ++ u8 phys_txq; /* destination queue ID */ ++ u16 data_size; /* data size of transmitted packet in bytes */ ++ union pp2x_specific_tx_desc u; ++}; ++ ++struct pp21_specific_rx_desc { ++ u32 buf_phys_addr; /* physical address of the buffer */ ++ u32 buf_cookie; /* cookie for access to RX buffer in rx path */ ++ u16 rsrvd_gem; /* gem_port_id (for future use, PON) */ ++ u16 rsrvd_l4csum; /* csum_l4 (for future use, PnC) */ ++ u8 rsrvd_bm_qset; /* bm_qset (for future use, BM) */ ++ u8 rsrvd1; ++ u16 rsrvd_cls_info; /* classify_info (for future use, PnC) */ ++ u32 rsrvd_flow_id; /* flow_id (for future use, PnC) */ ++ u32 rsrvd_abs; ++}; ++ ++struct pp22_specific_rx_desc { ++ u16 rsrvd_gem; /* gem_port_id (for future use, PON) */ ++ u16 rsrvd_l4csum; /* csum_l4 (for future use, PnC) */ ++ u32 rsrvd_timestamp; ++ u64 buf_phys_addr_key_hash; ++ u64 buf_cookie_bm_qset_cls_info; ++ /* cookie for access to RX buffer in rx path */ ++ /* bm_qset (for future use, BM) */ ++ /* classify_info (for future use, PnC) */ ++}; ++ ++union pp2x_specific_rx_desc { ++ struct pp21_specific_rx_desc pp21; ++ struct pp22_specific_rx_desc pp22; ++}; ++ ++struct mv_pp2x_rx_desc { ++ u32 status; /* info about received packet */ ++ u16 rsrvd_parser; /* parser_info (for future use, PnC) */ ++ u16 data_size; /* size of received packet in bytes */ ++ union pp2x_specific_rx_desc u; ++}; ++ ++union mv_pp2x_prs_tcam_entry { ++ u32 word[MVPP2_PRS_TCAM_WORDS]; ++ u8 byte[MVPP2_PRS_TCAM_WORDS * 4]; ++}; ++ ++union mv_pp2x_prs_sram_entry { ++ u32 word[MVPP2_PRS_SRAM_WORDS]; ++ u8 byte[MVPP2_PRS_SRAM_WORDS * 4]; ++}; ++ ++struct mv_pp2x_prs_entry { ++ u32 index; ++ union mv_pp2x_prs_tcam_entry tcam; ++ union mv_pp2x_prs_sram_entry sram; ++}; ++ ++struct mv_pp2x_prs_shadow { ++ bool valid; ++ bool finish; ++ ++ /* Lookup ID */ ++ int lu; ++ ++ /* User defined offset */ ++ int udf; ++ ++ /* Result info */ ++ u32 ri; ++ u32 ri_mask; ++}; ++ ++struct mv_pp2x_cls_flow_entry { ++ u32 index; ++ u32 data[MVPP2_CLS_FLOWS_TBL_DATA_WORDS]; ++}; ++ ++struct mv_pp2x_cls_lookup_entry { ++ u32 lkpid; ++ u32 way; ++ u32 data; ++}; ++ ++struct mv_pp2x_cls_flow_info { ++ u32 lkpid; ++ /* The flow table entry index of CoS default rule */ ++ u32 flow_entry_dflt; ++ /* The flow table entry index of CoS VLAN rule */ ++ u32 flow_entry_vlan; ++ /* The flow table entry index of CoS DSCP rule */ ++ u32 flow_entry_dscp; ++ /* The flow table entry index of RSS rule */ ++ u32 flow_entry_rss1; ++ /* The flow table entry index of RSS rule for UDP packet to ++ * update hash mode ++ */ ++ u32 flow_entry_rss2; ++}; ++ ++/* The flow entry could become lkp pointer in lookup table */ ++enum mv_pp2x_cls_lkp_ptr_candidate { ++ MVPP2_LKP_PTR_FLOW_DEFAULT, ++ MVPP2_LKP_PTR_FLOW_VLAN, ++ MVPP2_LKP_PTR_FLOW_DSCP, ++ MVPP2_LKP_PTR_NUM ++}; ++ ++struct mv_pp2x_cls_shadow { ++ struct mv_pp2x_cls_flow_info *flow_info; ++ u32 flow_free_start; /* The start of free entry index in flow table */ ++ /* TODO: does need a spin_lock for flow_free_start? */ ++ u32 flow_swap_area; ++}; ++ ++/* Classifier engine2 and QoS structure */ ++ ++/* C2 constants */ ++#define MVPP2_CLS_C2_TCAM_SIZE 256 ++#define MVPP2_CLS_C2_TCAM_WORDS 5 ++#define MVPP2_CLS_C2_TCAM_DATA_BYTES 10 ++#define MVPP2_CLS_C2_SRAM_WORDS 5 ++#define MVPP2_CLS_C2_HEK_LKP_TYPE_OFFS 0 ++#define MVPP2_CLS_C2_HEK_LKP_TYPE_BITS 6 ++#define MVPP2_CLS_C2_HEK_LKP_TYPE_MASK (0x3F << \ ++ MVPP2_CLS_C2_HEK_LKP_TYPE_OFFS) ++#define MVPP2_CLS_C2_HEK_PORT_TYPE_OFFS 6 ++#define MVPP2_CLS_C2_HEK_PORT_TYPE_BITS 2 ++#define MVPP2_CLS_C2_HEK_PORT_TYPE_MASK (0x3 << \ ++ MVPP2_CLS_C2_HEK_PORT_TYPE_OFFS) ++#define MVPP2_CLS_C2_QOS_DSCP_TBL_SIZE 64 ++#define MVPP2_CLS_C2_QOS_PRIO_TBL_SIZE 8 ++#define MVPP2_CLS_C2_QOS_DSCP_TBL_NUM 8 ++#define MVPP2_CLS_C2_QOS_PRIO_TBL_NUM 64 ++ ++struct mv_pp2x_cls_c2_entry { ++ u32 index; ++ bool inv; ++ union { ++ u32 words[MVPP2_CLS_C2_TCAM_WORDS]; ++ u8 bytes[MVPP2_CLS_C2_TCAM_WORDS * 4]; ++ } tcam; ++ union { ++ u32 words[MVPP2_CLS_C2_SRAM_WORDS]; ++ struct { ++ u32 action_tbl; /* 0x1B30 */ ++ u32 actions; /* 0x1B60 */ ++ u32 qos_attr; /* 0x1B64*/ ++ u32 hwf_attr; /* 0x1B68 */ ++ u32 rss_attr; /* 0x1B6C */ ++ u32 seq_attr; /* 0x1B70 */ ++ } regs; ++ } sram; ++}; ++ ++enum mv_pp2x_cls2_hek_offs { ++ MVPP2_CLS_C2_HEK_OFF_BYTE0 = 0, ++ MVPP2_CLS_C2_HEK_OFF_BYTE1, ++ MVPP2_CLS_C2_HEK_OFF_BYTE2, ++ MVPP2_CLS_C2_HEK_OFF_BYTE3, ++ MVPP2_CLS_C2_HEK_OFF_BYTE4, ++ MVPP2_CLS_C2_HEK_OFF_BYTE5, ++ MVPP2_CLS_C2_HEK_OFF_BYTE6, ++ MVPP2_CLS_C2_HEK_OFF_BYTE7, ++ MVPP2_CLS_C2_HEK_OFF_LKP_PORT_TYPE, ++ MVPP2_CLS_C2_HEK_OFF_PORT_ID, ++ MVPP2_CLS_C2_HEK_OFF_MAX ++}; ++ ++struct mv_pp2x_cls_c2_qos_entry { ++ u32 tbl_id; ++ u32 tbl_sel; ++ u32 tbl_line; ++ u32 data; ++}; ++ ++enum mv_pp2x_src_port_type { ++ MVPP2_SRC_PORT_TYPE_PHY, ++ MVPP2_SRC_PORT_TYPE_UNI, ++ MVPP2_SRC_PORT_TYPE_VIR ++}; ++ ++struct mv_pp2x_src_port { ++ enum mv_pp2x_src_port_type port_type; ++ u32 port_value; ++ u32 port_mask; ++}; ++ ++enum mv_pp2x_qos_tbl_sel { ++ MVPP2_QOS_TBL_SEL_PRI = 0, ++ MVPP2_QOS_TBL_SEL_DSCP, ++}; ++ ++enum mv_pp2x_qos_src_tbl { ++ MVPP2_QOS_SRC_ACTION_TBL = 0, ++ MVPP2_QOS_SRC_DSCP_PBIT_TBL, ++}; ++ ++struct mv_pp2x_engine_qos_info { ++ /* dscp pri table or none */ ++ enum mv_pp2x_qos_tbl_sel qos_tbl_type; ++ /* dscp or pri table index */ ++ u32 qos_tbl_index; ++ /* policer id, 0xffff do not assign policer */ ++ u16 policer_id; ++ /* pri/dscp comes from qos or act tbl */ ++ enum mv_pp2x_qos_src_tbl pri_dscp_src; ++ /* gemport comes from qos or act tbl */ ++ enum mv_pp2x_qos_src_tbl gemport_src; ++ enum mv_pp2x_qos_src_tbl q_low_src; ++ enum mv_pp2x_qos_src_tbl q_high_src; ++ enum mv_pp2x_qos_src_tbl color_src; ++}; ++ ++enum mv_pp2x_color_action_type { ++ /* Do not update color */ ++ MVPP2_COLOR_ACTION_TYPE_NO_UPDT = 0, ++ /* Do not update color and lock */ ++ MVPP2_COLOR_ACTION_TYPE_NO_UPDT_LOCK, ++ /* Update to green */ ++ MVPP2_COLOR_ACTION_TYPE_GREEN, ++ /* Update to green and lock */ ++ MVPP2_COLOR_ACTION_TYPE_GREEN_LOCK, ++ /* Update to yellow */ ++ MVPP2_COLOR_ACTION_TYPE_YELLOW, ++ /* Update to yellow */ ++ MVPP2_COLOR_ACTION_TYPE_YELLOW_LOCK, ++ /* Update to red */ ++ MVPP2_COLOR_ACTION_TYPE_RED, ++ /* Update to red and lock */ ++ MVPP2_COLOR_ACTION_TYPE_RED_LOCK, ++}; ++ ++enum mv_pp2x_general_action_type { ++ /* The field will be not updated */ ++ MVPP2_ACTION_TYPE_NO_UPDT, ++ /* The field will be not updated and lock */ ++ MVPP2_ACTION_TYPE_NO_UPDT_LOCK, ++ /* The field will be updated */ ++ MVPP2_ACTION_TYPE_UPDT, ++ /* The field will be updated and lock */ ++ MVPP2_ACTION_TYPE_UPDT_LOCK, ++}; ++ ++enum mv_pp2x_flowid_action_type { ++ /* FlowID is disable */ ++ MVPP2_ACTION_FLOWID_DISABLE = 0, ++ /* FlowID is enable */ ++ MVPP2_ACTION_FLOWID_ENABLE, ++}; ++ ++enum mv_pp2x_frwd_action_type { ++ /* The decision will be not updated */ ++ MVPP2_FRWD_ACTION_TYPE_NO_UPDT, ++ /* The decision is not updated, and following no change to it */ ++ MVPP2_FRWD_ACTION_TYPE_NO_UPDT_LOCK, ++ /* The packet to CPU (Software Forwarding) */ ++ MVPP2_FRWD_ACTION_TYPE_SWF, ++ /* The packet to CPU, and following no change to it */ ++ MVPP2_FRWD_ACTION_TYPE_SWF_LOCK, ++ /* The packet to one transmit port (Hardware Forwarding) */ ++ MVPP2_FRWD_ACTION_TYPE_HWF, ++ /* The packet to one tx port, and following no change to it */ ++ MVPP2_FRWD_ACTION_TYPE_HWF_LOCK, ++ /* The pkt to one tx port, and maybe internal packets is used */ ++ MVPP2_FRWD_ACTION_TYPE_HWF_LOW_LATENCY, ++ /* Same to above, but following no change to it*/ ++ MVPP2_FRWD_ACTION_TYPE_HWF_LOW_LATENCY_LOCK, ++}; ++ ++struct mv_pp2x_engine_pkt_action { ++ enum mv_pp2x_color_action_type color_act; ++ enum mv_pp2x_general_action_type pri_act; ++ enum mv_pp2x_general_action_type dscp_act; ++ enum mv_pp2x_general_action_type gemp_act; ++ enum mv_pp2x_general_action_type q_low_act; ++ enum mv_pp2x_general_action_type q_high_act; ++ enum mv_pp2x_general_action_type rss_act; ++ enum mv_pp2x_flowid_action_type flowid_act; ++ enum mv_pp2x_frwd_action_type frwd_act; ++}; ++ ++struct mv_pp2x_qos_value { ++ u16 pri; ++ u16 dscp; ++ u16 gemp; ++ u16 q_low; ++ u16 q_high; ++}; ++ ++struct mv_pp2x_engine_pkt_mod { ++ u32 mod_cmd_idx; ++ u32 mod_data_idx; ++ u32 l4_chksum_update_flag; ++}; ++ ++struct mv_pp2x_duplicate_info { ++ /* pkt duplication flow id */ ++ u32 flow_id; ++ /* pkt duplication count */ ++ u32 flow_cnt; ++}; ++ ++/* The logic C2 entry, easy to understand and use */ ++struct mv_pp2x_c2_add_entry { ++ struct mv_pp2x_src_port port; ++ u8 lkp_type; ++ u8 lkp_type_mask; ++ /* priority in this look_type */ ++ u32 priority; ++ /* all the qos input */ ++ struct mv_pp2x_engine_qos_info qos_info; ++ /* update&lock info */ ++ struct mv_pp2x_engine_pkt_action action; ++ /* pri/dscp/gemport/qLow/qHigh */ ++ struct mv_pp2x_qos_value qos_value; ++ /* PMT cmd_idx and data_idx */ ++ struct mv_pp2x_engine_pkt_mod pkt_mod; ++ /* RSS enable or disable */ ++ int rss_en; ++ /* pkt duplication flow info */ ++ struct mv_pp2x_duplicate_info flow_info; ++}; ++ ++struct mv_pp2x_c2_rule_idx { ++ /* The TCAM rule index for VLAN pri check with QoS pbit table */ ++ u32 vlan_pri_idx; ++ /* The TCAM rule index for DSCP check with QoS dscp table */ ++ u32 dscp_pri_idx; ++ /* The default rule for flow untagged and non-IP */ ++ u32 default_rule_idx; ++}; ++ ++struct mv_pp2x_c2_shadow { ++ int c2_tcam_free_start; ++ /* Per src port */ ++ struct mv_pp2x_c2_rule_idx rule_idx_info[8]; ++}; ++ ++struct mv_pp2x_bm_pool { ++ /* Pool number in the range 0-7 */ ++ int id; ++ ++ /*Logical id, equals to index in parent priv */ ++ enum mv_pp2x_bm_pool_log_num log_id; ++ ++ /* Buffer Pointers Pool External (BPPE) size */ ++ int size; ++ /* Number of buffers for this pool */ ++ int buf_num; ++ /* Pool buffer size */ ++ int buf_size; ++ /* Packet size */ ++ int pkt_size; ++ int frag_size; ++ /* pool for external use (not kernel) */ ++ bool external_pool; ++ /* BPPE virtual base address */ ++ void *virt_addr; ++ /* BPPE physical base address */ ++ dma_addr_t phys_addr; ++ ++ /* Ports using BM pool */ ++ u32 port_map; ++ ++ int in_use_thresh; ++}; ++ ++struct mv_pp2x_buff_hdr { ++ u32 next_buff_phys_addr; ++ u32 next_buff_virt_addr; ++ u16 byte_count; ++ u16 info; ++ u8 reserved1; /* bm_qset (for future use, BM) */ ++}; ++ ++/* Buffer header info bits */ ++#define MVPP2_B_HDR_INFO_MC_ID_MASK 0xfff ++#define MVPP2_B_HDR_INFO_MC_ID(info) ((info) & MVPP2_B_HDR_INFO_MC_ID_MASK) ++#define MVPP2_B_HDR_INFO_LAST_OFFS 12 ++#define MVPP2_B_HDR_INFO_LAST_MASK BIT(12) ++#define MVPP2_B_HDR_INFO_IS_LAST(info) \ ++ ((info & MVPP2_B_HDR_INFO_LAST_MASK) >> MVPP2_B_HDR_INFO_LAST_OFFS) ++ ++/* Macroes */ ++#define MVPP2_RX_DESC_POOL(rx_desc) ((rx_desc->status & \ ++ MVPP2_RXD_BM_POOL_ID_MASK) >> MVPP2_RXD_BM_POOL_ID_OFFS) ++ ++/* RSS related definetions */ ++enum mv_pp22_rss_access_sel { ++ MVPP22_RSS_ACCESS_POINTER, ++ MVPP22_RSS_ACCESS_TBL, ++}; ++ ++enum mv_pp2_rss_hash_select { ++ MVPP2_RSS_HASH_0_4, ++ MVPP2_RSS_HASH_5_9, ++}; ++ ++/* Structure dexcribe RXQ and corresponding rss table */ ++struct mv_pp22_rss_tbl_ptr { ++ u8 rxq_idx; ++ u8 rss_tbl_ptr; ++}; ++ ++/* Normal RSS entry */ ++struct mv_pp22_rss_tbl_entry { ++ u8 tbl_id; ++ u8 tbl_line; ++ u8 width; ++ u8 rxq; ++}; ++ ++union mv_pp22_rss_access_entry { ++ struct mv_pp22_rss_tbl_ptr pointer; ++ struct mv_pp22_rss_tbl_entry entry; ++}; ++ ++struct mv_pp22_rss_entry { ++ enum mv_pp22_rss_access_sel sel; ++ union mv_pp22_rss_access_entry u; ++}; ++ ++/* C3 or other module definetions */ ++#define MVPP2_CLS_C3_HASH_TBL_SIZE (4096) ++#define MVPP2_CLS_C3_MISS_TBL_SIZE (64) ++#define MVPP2_CLS_C3_EXT_HEK_WORDS (9) ++#define MVPP2_CLS_C3_SRAM_WORDS (5) ++#define MVPP2_CLS_C3_EXT_TBL_SIZE (256) ++#define MVPP2_CLS_C3_HEK_WORDS (3) ++#define MVPP2_CLS_C3_HEK_BYTES 12 /* size in bytes */ ++#define MVPP2_CLS_C3_BANK_SIZE (512) ++#define MVPP2_CLS_C3_MAX_SEARCH_DEPTH (16) ++ ++/* Classifier C3 offsets in hash table */ ++#define KEY_OCCUPIED (116) ++#define KEY_FORMAT (115) ++#define KEY_PTR_EXT (107) ++ ++#define KEY_PRT_ID(ext_mode) ((ext_mode == 1) ? (99) : (107)) ++#define KEY_PRT_ID_MASK(ext_mode) (((1 << KEY_CTRL_PRT_ID_BITS) - 1) << (KEY_PRT_ID(ext_mode) % 32)) ++ ++#define KEY_PRT_ID_TYPE(ext_mode) ((ext_mode == 1) ? (97) : (105)) ++#define KEY_PRT_ID_TYPE_MASK(ext_mode) ((KEY_CTRL_PRT_ID_TYPE_MAX) << (KEY_PRT_ID_TYPE(ext_mode) % 32)) ++ ++#define KEY_LKP_TYPE(ext_mode) ((ext_mode == 1) ? (91) : (99)) ++#define KEY_LKP_TYPE_MASK(ext_mode) (((1 << KEY_CTRL_LKP_TYPE_BITS) - 1) << (KEY_LKP_TYPE(ext_mode) % 32)) ++ ++#define KEY_L4_INFO(ext_mode) ((ext_mode == 1) ? (88) : (96)) ++#define KEY_L4_INFO_MASK(ext_mode) (((1 << KEY_CTRL_L4_BITS) - 1) << (KEY_L4_INFO(ext_mode) % 32)) ++ ++#define KEY_CTRL_LKP_TYPE 4 ++#define KEY_CTRL_LKP_TYPE_BITS 6 ++ ++#define KEY_CTRL_LKP_TYPE_MAX ((1 << KEY_CTRL_LKP_TYPE_BITS) - 1) ++#define KEY_CTRL_LKP_TYPE_MASK (((1 << KEY_CTRL_LKP_TYPE_BITS) - 1) << KEY_CTRL_LKP_TYPE) ++ ++#define KEY_CTRL_PRT_ID_TYPE 12 ++#define KEY_CTRL_PRT_ID_TYPE_BITS 2 ++#define KEY_CTRL_PRT_ID_TYPE_MAX ((1 << KEY_CTRL_PRT_ID_TYPE_BITS) - 1) ++#define KEY_CTRL_PRT_ID_TYPE_MASK ((KEY_CTRL_PRT_ID_TYPE_MAX) << KEY_CTRL_PRT_ID_TYPE) ++ ++#define KEY_CTRL_PRT_ID 16 ++#define KEY_CTRL_PRT_ID_BITS 8 ++#define KEY_CTRL_PRT_ID_MAX ((1 << KEY_CTRL_PRT_ID_BITS) - 1) ++#define KEY_CTRL_PRT_ID_MASK (((1 << KEY_CTRL_PRT_ID_BITS) - 1) << KEY_CTRL_PRT_ID) ++ ++#define KEY_CTRL_HEK_SIZE 24 ++#define KEY_CTRL_HEK_SIZE_BITS 6 ++#define KEY_CTRL_HEK_SIZE_MAX 36 ++#define KEY_CTRL_HEK_SIZE_MASK (((1 << KEY_CTRL_HEK_SIZE_BITS) - 1) << KEY_CTRL_HEK_SIZE) ++ ++struct mv_pp2x_cls_c3_hash_pair { ++ u16 pair_num; ++ u16 old_idx[MVPP2_CLS_C3_MAX_SEARCH_DEPTH]; ++ u16 new_idx[MVPP2_CLS_C3_MAX_SEARCH_DEPTH]; ++}; ++ ++struct mv_pp2x_cls_c3_entry { ++ u32 index; ++ u32 ext_index; ++ ++ struct { ++ union { ++ u32 words[MVPP2_CLS_C3_EXT_HEK_WORDS]; ++ u8 bytes[MVPP2_CLS_C3_EXT_HEK_WORDS * 4]; ++ } hek; ++ u32 key_ctrl;/*0x1C10*/ ++ } key; ++ union { ++ u32 words[MVPP2_CLS_C3_SRAM_WORDS]; ++ struct { ++ u32 actions;/*0x1D40*/ ++ u32 qos_attr;/*0x1D44*/ ++ u32 hwf_attr;/*0x1D48*/ ++ u32 dup_attr;/*0x1D4C*/ ++ u32 seq_l_attr;/*0x1D50*/ ++ u32 seq_h_attr;/*0x1D54*/ ++ } regs; ++ } sram; ++}; ++ ++struct mv_pp2x_cls_c3_shadow_hash_entry { ++ /* valid if size > 0 */ ++ /* size include the extension*/ ++ int ext_ptr; ++ int size; ++}; ++ ++/* Classifier C4 Top Registers */ ++#define MVPP2_CLS4_PHY_TO_RL_REG(port) (0x1E00 + ((port) * 4)) ++#define MVPP2_CLS4_PHY_TO_RL_GRP 0 ++#define MVPP2_CLS4_PHY_TO_RL_GRP_BITS 3 ++#define MVPP2_CLS4_PHY_TO_RL_GRP_MASK (((1 << MVPP2_CLS4_PHY_TO_RL_GRP_BITS) - 1) << \ ++ MVPP2_CLS4_PHY_TO_RL_GRP) ++#define MVPP2_CLS4_PHY_TO_RL_RULE_NUM 4 ++#define MVPP2_CLS4_PHY_TO_RL_RULE_NUM_BITS 4 ++#define MVPP2_CLS4_PHY_TO_RL_RULE_NUM_MASK (((1 << MVPP2_CLS4_PHY_TO_RL_RULE_NUM_BITS) - 1) << \ ++ MVPP2_CLS4_PHY_TO_RL_RULE_NUM) ++ ++#define MVPP2_CLS4_UNI_TO_RL_REG(uni) (0x1E20 + ((uni) * 4)) ++#define MVPP2_CLS4_UNI_TO_RL_GRP 0 ++#define MVPP2_CLS4_UNI_TO_RL_RULE_NUM 4 ++ ++#define MVPP2_CLS4_RL_INDEX_REG (0x1E40) ++#define MVPP2_CLS4_RL_INDEX_RULE 0 ++#define MVPP2_CLS4_RL_INDEX_GRP 3 ++ ++#define MVPP2_CLS4_FATTR1_REG (0x1E50) ++#define MVPP2_CLS4_FATTR2_REG (0x1E54) ++#define MVPP2_CLS4_FATTR_REG_NUM 2 ++ ++#define MVPP2_CLS4_FATTR_ID(field) (((field) * 9) % 27) ++#define MVPP2_CLS4_FATTR_ID_BITS 6 ++#define MVPP2_CLS4_FATTR_ID_MAX ((1 << MVPP2_CLS4_FATTR_ID_BITS) - 1) ++#define MVPP2_CLS4_FATTR_ID_MASK(field) (MVPP2_CLS4_FATTR_ID_MAX << MVPP2_CLS4_FATTR_ID(field)) ++#define MVPP2_CLS4_FATTR_ID_VAL(field, reg_val) ((reg_val & MVPP2_CLS4_FATTR_ID_MASK(field)) >> \ ++ MVPP2_CLS4_FATTR_ID(field)) ++ ++#define MVPP2_CLS4_FATTR_OPCODE_BITS 3 ++#define MVPP2_CLS4_FATTR_OPCODE(field) ((((field) * 9) % 27) + MVPP2_CLS4_FATTR_ID_BITS) ++#define MVPP2_CLS4_FATTR_OPCODE_MAX ((1 << MVPP2_CLS4_FATTR_OPCODE_BITS) - 1) ++#define MVPP2_CLS4_FATTR_OPCODE_MASK(field) (MVPP2_CLS4_FATTR_OPCODE_MAX << MVPP2_CLS4_FATTR_OPCODE(field)) ++#define MVPP2_CLS4_FATTR_OPCODE_VAL(field, reg_val) ((reg_val & MVPP2_CLS4_FATTR_OPCODE_MASK(field)) >> \ ++ MVPP2_CLS4_FATTR_OPCODE(field)) ++ ++#define MVPP2_CLS4_FDATA1_REG (0x1E58) ++#define MVPP2_CLS4_FDATA2_REG (0x1E5C) ++#define MVPP2_CLS4_FDATA3_REG (0x1E60) ++#define MVPP2_CLS4_FDATA4_REG (0x1E64) ++#define MVPP2_CLS4_FDATA5_REG (0x1E68) ++#define MVPP2_CLS4_FDATA6_REG (0x1E6C) ++#define MVPP2_CLS4_FDATA7_REG (0x1E70) ++#define MVPP2_CLS4_FDATA8_REG (0x1E74) ++#define MVPP2_CLS4_FDATA_REG(reg_num) (0x1E58 + (4 * (reg_num))) ++#define MVPP2_CLS4_FDATA_REGS_NUM 8 ++ ++#define MVPP2_CLS4_FDATA7_L3INFO 16 ++#define MVPP2_CLS4_FDATA7_L3INFO_BITS 4 ++#define MVPP2_CLS4_L3INFO_MAX ((1 << MVPP2_CLS4_FDATA7_L3INFO_BITS) - 1) ++#define MVPP2_CLS4_L3INFO_MASK (MVPP2_CLS4_L3INFO_MAX << MVPP2_CLS4_FDATA7_L3INFO) ++#define MVPP2_CLS4_L3INFO_VAL(reg_val) (((reg_val) & MVPP2_CLS4_L3INFO_MASK) >> \ ++ MVPP2_CLS4_FDATA7_L3INFO) ++ ++#define MVPP2_CLS4_FDATA7_L4INFO 20 ++#define MVPP2_CLS4_FDATA7_L4INFO_BITS 4 ++#define MVPP2_CLS4_L4INFO_MAX ((1 << MVPP2_CLS4_FDATA7_L4INFO_BITS) - 1) ++#define MVPP2_CLS4_L4INFO_MASK (MVPP2_CLS4_L4INFO_MAX << MVPP2_CLS4_FDATA7_L4INFO) ++#define MVPP2_CLS4_L4INFO_VAL(reg_val) (((reg_val) & MVPP2_CLS4_L4INFO_MASK) >> \ ++ MVPP2_CLS4_FDATA7_L4INFO) ++ ++#define MVPP2_CLS4_FDATA7_MACME 24 ++#define MVPP2_CLS4_FDATA7_MACME_BITS 2 ++#define MVPP2_CLS4_MACME_MAX ((1 << MVPP2_CLS4_FDATA7_MACME_BITS) - 1) ++#define MVPP2_CLS4_MACME_MASK (MVPP2_CLS4_MACME_MAX << MVPP2_CLS4_FDATA7_MACME) ++#define MVPP2_CLS4_MACME_VAL(reg_val) (((reg_val) & MVPP2_CLS4_MACME_MASK) >> MVPP2_CLS4_FDATA7_MACME) ++ ++#define MVPP2_CLS4_FDATA7_PPPOE 26 ++#define MVPP2_CLS4_FDATA7_PPPOE_BITS 2 ++#define MVPP2_CLS4_PPPOE_MAX ((1 << MVPP2_CLS4_FDATA7_PPPOE_BITS) - 1) ++#define MVPP2_CLS4_PPPOE_MASK (MVPP2_CLS4_PPPOE_MAX << MVPP2_CLS4_FDATA7_PPPOE) ++#define MVPP2_CLS4_PPPOE_VAL(reg_val) (((reg_val) & MVPP2_CLS4_PPPOE_MASK) >> MVPP2_CLS4_FDATA7_PPPOE) ++ ++#define MVPP2_CLS4_FDATA7_VLAN 28 ++#define MVPP2_CLS4_FDATA7_VLAN_BITS 3 ++#define MVPP2_CLS4_VLAN_MAX ((1 << MVPP2_CLS4_FDATA7_VLAN_BITS) - 1) ++#define MVPP2_CLS4_VLAN_MASK (MVPP2_CLS4_VLAN_MAX << MVPP2_CLS4_FDATA7_VLAN) ++#define MVPP2_CLS4_VLAN_VAL(reg_val) (((reg_val) & MVPP2_CLS4_VLAN_MASK) >> MVPP2_CLS4_FDATA7_VLAN) ++ ++#define MVPP2_CLS4_ACT_REG (0x1E80) ++#define MVPP2_CLS4_ACT_QOS_ATTR_REG (0x1E84) ++#define MVPP2_CLS4_ACT_DUP_ATTR_REG (0x1E88) ++#define MVPP2_CNT_IDX_RULE(rule, set) ((rule) << 3 | (set)) ++#define MVPP2_CLS_C4_TBL_HIT_REG (0x7708) ++ ++/* Classifier C4 constants */ ++#define MVPP2_CLS_C4_GRP_SIZE (8) ++#define MVPP2_CLS_C4_GRPS_NUM (8) ++#define MVPP2_CLS_C4_TBL_WORDS (10) ++#define MVPP2_CLS_C4_TBL_DATA_WORDS (8) ++#define MVPP2_CLS_C4_SRAM_WORDS (3) ++#define MVPP2_CLS_C4_FIELDS_NUM (6) ++ ++/* C4 entry structure */ ++struct mv_pp2x_cls_c4_entry { ++ u32 rule_index; ++ u32 set_index; ++ union { ++ u32 words[MVPP2_CLS_C4_TBL_WORDS]; ++ struct { ++ u32 attr[MVPP2_CLS4_FATTR_REG_NUM]; ++ u32 fdata_arr[MVPP2_CLS_C4_TBL_DATA_WORDS]; ++ } regs; ++ } rules; ++ union { ++ u32 words[MVPP2_CLS_C4_SRAM_WORDS]; ++ struct { ++ u32 actions;/* 0x1E80 */ ++ u32 qos_attr;/* 0x1E84*/ ++ u32 dup_attr;/* 0x1E88 */ ++ } regs; ++ } sram; ++}; ++ ++/************** TX Packet Modification Registers *******************/ ++#define MVPP2_PME_TBL_IDX_REG (0x8400) ++#define MVPP2_PME_TBL_INSTR_REG (0x8480) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_TBL_DATA1_REG (0x8500) ++#define MVPP2_PME_TBL_DATA2_REG (0x8580) ++#define MVPP2_PME_TBL_DATA_BITS 16 ++#define MVPP2_PME_TBL_DATA_OFFS(idx) ((idx == 0) ? MVPP2_PME_TBL_DATA_BITS : 0) ++#define MVPP2_PME_TBL_DATA_MASK(idx) (((1 << MVPP2_PME_TBL_DATA_BITS) - 1) << MVPP2_PME_TBL_DATA_OFFS(idx)) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_TBL_STATUS_REG (0x8600) ++#define MVPP2_PME_TCONT_THRESH_REG (0x8604) ++#define MVPP2_PME_MTU_REG (0x8608) ++ ++#define MVPP2_PME_MAX_VLAN_ETH_TYPES 4 ++#define MVPP2_PME_VLAN_ETH_TYPE_REG(i) (0x8610 + ((i) << 2)) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_DEF_VLAN_CFG_REG (0x8620) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_MAX_DSA_ETH_TYPES 2 ++#define MVPP2_PME_DEF_DSA_CFG_REG(i) (0x8624 + ((i) << 2)) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_DEF_DSA_SRC_DEV_REG (0x8630) ++#define MVPP2_PME_DSA_SRC_DEV_OFFS 1 ++#define MVPP2_PME_DSA_SRC_DEV_BITS 4 ++#define MVPP2_PME_DSA_SRC_DEV_ALL_MASK (((1 << MVPP2_PME_DSA_SRC_DEV_BITS) - 1) << MVPP2_PME_DSA_SRC_DEV_OFFS) ++#define MVPP2_PME_DSA_SRC_DEV_MASK(dev) ((dev) << MVPP2_PME_DSA_SRC_DEV_OFFS) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_TTL_ZERO_FRWD_REG (0x8640) ++#define MVPP2_PME_TTL_ZERO_FRWD_BIT 0 ++#define MVPP2_PME_TTL_ZERO_FRWD_MASK BIT(MVPP2_PME_TTL_ZERO_FRWD_BIT) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_PME_PPPOE_ETYPE_REG (0x8650) ++#define MVPP2_PME_PPPOE_DATA_REG (0x8654) ++ ++#define MVPP2_PME_PPPOE_CODE_OFFS 0 ++#define MVPP2_PME_PPPOE_CODE_BITS 8 ++#define MVPP2_PME_PPPOE_CODE_ALL_MASK (((1 << MVPP2_PME_PPPOE_CODE_BITS) - 1) << MVPP2_PME_PPPOE_CODE_OFFS) ++#define MVPP2_PME_PPPOE_CODE_MASK(code) (((code) << MVPP2_PME_PPPOE_CODE_OFFS) & MVPP2_PME_PPPOE_CODE_ALL_MASK) ++ ++#define MVPP2_PME_PPPOE_TYPE_OFFS 8 ++#define MVPP2_PME_PPPOE_TYPE_BITS 4 ++#define MVPP2_PME_PPPOE_TYPE_ALL_MASK (((1 << MVPP2_PME_PPPOE_TYPE_BITS) - 1) << MVPP2_PME_PPPOE_TYPE_OFFS) ++#define MVPP2_PME_PPPOE_TYPE_MASK(type) (((type) << MVPP2_PME_PPPOE_TYPE_OFFS) & MVPP2_PME_PPPOE_TYPE_ALL_MASK) ++ ++#define MVPP2_PME_PPPOE_VER_OFFS 12 ++#define MVPP2_PME_PPPOE_VER_BITS 4 ++#define MVPP2_PME_PPPOE_VER_ALL_MASK (((1 << MVPP2_PME_PPPOE_VER_BITS) - 1) << MVPP2_PME_PPPOE_VER_OFFS) ++#define MVPP2_PME_PPPOE_VER_MASK(ver) (((ver) << MVPP2_PME_PPPOE_VER_OFFS) & MVPP2_PME_PPPOE_VER_ALL_MASK) ++ ++#define MVPP2_PME_PPPOE_LEN_REG (0x8658) ++#define MVPP2_PME_PPPOE_PROTO_REG (0x865c) ++ ++#define MVPP2_PME_PPPOE_PROTO_OFFS(i) ((i == 0) ? 0 : 16) ++#define MVPP2_PME_PPPOE_PROTO_BITS (16) ++#define MVPP2_PME_PPPOE_PROTO_ALL_MASK(i) (((1 << MVPP2_PME_PPPOE_PROTO_BITS) - 1) << \ ++ MVPP2_PME_PPPOE_PROTO_OFFS(i)) ++#define MVPP2_PME_PPPOE_PROTO_MASK(i, p) (((p) << MVPP2_PME_PPPOE_PROTO_OFFS(i)) & \ ++ MVPP2_PME_PPPOE_PROTO_ALL_MASK(i)) ++ ++#define MVPP2_PME_CONFIG_REG (0x8660) ++ ++#define MVPP2_PME_MAX_HDR_SIZE_OFFS 0 ++#define MVPP2_PME_MAX_HDR_SIZE_BITS 8 ++#define MVPP2_PME_MAX_HDR_SIZE_ALL_MASK (((1 << MVPP2_PME_MAX_HDR_SIZE_BITS) - 1) << \ ++ MVPP2_PME_MAX_HDR_SIZE_OFFS) ++#define MVPP2_PME_MAX_HDR_SIZE_MASK(size) (((size) << MVPP2_PME_MAX_HDR_SIZE_OFFS) & \ ++ MVPP2_PME_MAX_HDR_SIZE_ALL_MASK) ++ ++#define MVPP2_PME_MAX_INSTR_NUM_OFFS 16 ++#define MVPP2_PME_MAX_INSTR_NUM_BITS 8 ++#define MVPP2_PME_MAX_INSTR_NUM_ALL_MASK (((1 << MVPP2_PME_MAX_INSTR_NUM_BITS) - 1) << \ ++ MVPP2_PME_MAX_INSTR_NUM_OFFS) ++#define MVPP2_PME_MAX_INSTR_NUM_MASK(num) (((num) << MVPP2_PME_MAX_INSTR_NUM_OFFS) & \ ++ MVPP2_PME_MAX_INSTR_NUM_ALL_MASK) ++ ++#define MVPP2_PME_DROP_ON_ERR_BIT 24 ++#define MVPP2_PME_DROP_ON_ERR_MASK BIT(MVPP2_PME_DROP_ON_ERR_BIT) ++/*--------------------------------------------------------------------------*/ ++ ++#define MVPP2_PME_STATUS_1_REG (0x8664) ++#define MVPP2_PME_STATUS_2_REG(txp) (0x8700 + 4 * (txp)) ++#define MVPP2_PME_STATUS_3_REG(txp) (0x8780 + 4 * (txp)) ++ ++/* PME insructions table (MV_PP2_PME_TBL_INSTR_REG) fields definition */ ++#define MVPP2_PME_DATA_OFFS 0 ++#define MVPP2_PME_DATA_BITS 16 ++#define MVPP2_PME_DATA_MASK (((1 << MVPP2_PME_DATA_BITS) - 1) << MVPP2_PME_DATA_OFFS) ++ ++#define MVPP2_PME_CTRL_OFFS 16 ++#define MVPP2_PME_CTRL_BITS 16 ++#define MVPP2_PME_CTRL_MASK (((1 << MVPP2_PME_CTRL_BITS) - 1) << MVPP2_PME_CTRL_OFFS) ++ ++#define MVPP2_PME_CMD_OFFS 16 ++#define MVPP2_PME_CMD_BITS 5 ++#define MVPP2_PME_CMD_ALL_MASK (((1 << MVPP2_PME_CMD_BITS) - 1) << MVPP2_PME_CMD_OFFS) ++#define MVPP2_PME_CMD_MASK(cmd) ((cmd) << MVPP2_PME_CMD_OFFS) ++ ++#define MVPP2_PME_IP4_CSUM_BIT 21 ++#define MVPP2_PME_IP4_CSUM_MASK BIT(MVPP2_PME_IP4_CSUM_BIT) ++ ++#define MVPP2_PME_L4_CSUM_BIT 22 ++#define MVPP2_PME_L4_CSUM_MASK BIT(MVPP2_PME_L4_CSUM_BIT) ++ ++#define MVPP2_PME_LAST_BIT 23 ++#define MVPP2_PME_LAST_MASK BIT(MVPP2_PME_LAST_BIT) ++ ++#define MVPP2_PME_CMD_TYPE_OFFS 24 ++#define MVPP2_PME_CMD_TYPE_BITS 3 ++#define MVPP2_PME_CMD_TYPE_ALL_MASK (((1 << MVPP2_PME_CMD_TYPE_BITS) - 1) << MVPP2_PME_CMD_TYPE_OFFS) ++#define MVPP2_PME_CMD_TYPE_MASK(type) ((type) << MVPP2_PME_CMD_TYPE_OFFS) ++ ++#define MVPP2_TOTAL_TXP_NUM (16 + 3 - 1) ++ ++/* PME data1 and data2 fields MVPP2_PME_TBL_DATA1_REG and MVPP2_PME_TBL_DATA2_REG */ ++#define MVPP2_PME_TBL_DATA_BITS 16 ++#define MVPP2_PME_TBL_DATA_OFFS(idx) ((idx == 0) ? MVPP2_PME_TBL_DATA_BITS : 0) ++#define MVPP2_PME_TBL_DATA_MASK(idx) (((1 << MVPP2_PME_TBL_DATA_BITS) - 1) << MVPP2_PME_TBL_DATA_OFFS(idx)) ++ ++/* TX packet modification constants */ ++#define MVPP2_PME_INSTR_SIZE 2600 ++#define MVPP2_PME_DATA1_SIZE (46 * 1024 / 2) /* 46KBytes = 23K data of 2 bytes */ ++#define MVPP2_PME_DATA2_SIZE (4 * 1024 / 2) /* 4KBytes = 2K data of 2 bytes */ ++ ++enum mv_pp2x_pme_instr { ++ MVPP2_PME_CMD_NONE = 0, ++ MVPP2_PME_CMD_ADD_2B, ++ MVPP2_PME_CMD_CFG_VLAN, ++ MVPP2_PME_CMD_ADD_VLAN, ++ MVPP2_PME_CMD_CFG_DSA_1, ++ MVPP2_PME_CMD_CFG_DSA_2, ++ MVPP2_PME_CMD_ADD_DSA, ++ MVPP2_PME_CMD_DEL_BYTES, ++ MVPP2_PME_CMD_REPLACE_2B, ++ MVPP2_PME_CMD_REPLACE_LSB, ++ MVPP2_PME_CMD_REPLACE_MSB, ++ MVPP2_PME_CMD_REPLACE_VLAN, ++ MVPP2_PME_CMD_DEC_LSB, ++ MVPP2_PME_CMD_DEC_MSB, ++ MVPP2_PME_CMD_ADD_CALC_LEN, ++ MVPP2_PME_CMD_REPLACE_LEN, ++ MVPP2_PME_CMD_IPV4_CSUM, ++ MVPP2_PME_CMD_L4_CSUM, ++ MVPP2_PME_CMD_SKIP, ++ MVPP2_PME_CMD_JUMP, ++ MVPP2_PME_CMD_JUMP_SKIP, ++ MVPP2_PME_CMD_JUMP_SUB, ++ MVPP2_PME_CMD_PPPOE, ++ MVPP2_PME_CMD_STORE, ++ MVPP2_PME_CMD_ADD_IP4_CSUM, ++ MVPP2_PME_CMD_PPPOE_2, ++ MVPP2_PME_CMD_REPLACE_MID, ++ MVPP2_PME_CMD_ADD_MULT, ++ MVPP2_PME_CMD_REPLACE_MULT, ++ MVPP2_PME_CMD_REPLACE_REM_2B, ++ MVPP2_PME_CMD_ADD_IP6_HDR, ++ MVPP2_PME_CMD_DROP_PKT = 0x1f, ++ MVPP2_TMP_CMD_LAST ++}; ++ ++/* PME entry structure */ ++struct mv_pp2x_pme_entry { ++ int index; ++ u32 word; ++}; ++ ++/* MC */ ++/*-------------------------------------------------------------------------------*/ ++#define MVPP2_MC_INDEX_REG (0x160) ++#define MVPP2_MC_INDEX_MAX ((1 << MVPP2_CLS2_ACT_DUP_ATTR_DUPID_BITS) - 1) ++/*------------------------------------------------------------------------------*/ ++#define MVPP2_MC_DATA1_REG (0x164) ++#define MVPP2_MC_DATA1_DPTR 1 ++#define MVPP2_MC_DATA1_IPTR 16 ++/*------------------------------------------------------------------------------*/ ++#define MVPP2_MC_DATA2_REG (0x168) ++#define MVPP2_MC_DATA2_GEM_ID 0 ++#define MVPP2_MC_DATA2_PRI 12 ++#define MVPP2_MC_DATA2_DSCP 15 ++#define MVPP2_MC_DATA2_GEM_ID_EN BIT(21) ++#define MVPP2_MC_DATA2_PRI_EN BIT(22) ++#define MVPP2_MC_DATA2_DSCP_EN BIT(23) ++/*------------------------------------------------------------------------------*/ ++#define MVPP2_MC_DATA3_REG (0x16C) ++#define MVPP2_MC_DATA3_QUEUE 0 ++#define MVPP2_MC_DATA3_HWF_EN BIT(8) ++#define MVPP2_MC_DATA3_NEXT 16 ++#define MVPP2_MC_DATA3_NEXT_MASK (MVPP2_MC_INDEX_MAX << MVPP2_MC_DATA3_NEXT) ++ ++#define MVPP2_MC_TBL_SIZE 256 ++#define MVPP2_MC_WORDS 3 ++ ++/* MC entry structure */ ++struct mv_pp2x_mc_entry { ++ u32 index; ++ union { ++ u32 words[MVPP2_MC_WORDS]; ++ struct { ++ u32 data1;/* 0x164 */ ++ u32 data2;/* 0x168 */ ++ u32 data3;/* 0x16c */ ++ } regs; ++ } sram; ++}; ++ ++#endif /*_MVPP2_HW_TYPE_H_*/ ++ ++/* Policer */ ++#define MVPP2_PLCR_NUM 48 ++ ++/*********************************** RX Policer Registers *******************/ ++/* exist only in ppv2.0 */ ++#define MVPP2_PLCR_ENABLE_REG (0x1300) ++ ++#define MVPP2_PLCR_EN_OFFS 0 ++#define MVPP2_PLCR_EN_ALL_MASK (((1 << MVPP2_PLCR_NUM) - 1) << MVPP2_PLCR_EN_OFFS) ++#define MVPP2_PLCR_EN_MASK(plcr) ((1 << (plcr)) << MVPP2_PLCR_EN_OFFS) ++/*--------------------------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_BASE_PERIOD_REG (0x1304) ++ ++#define MVPP2_PLCR_BASE_PERIOD_OFFS 0 ++#define MVPP2_PLCR_BASE_PERIOD_BITS 16 ++#define MVPP2_PLCR_BASE_PERIOD_ALL_MASK \ ++ (((1 << MVPP2_PLCR_BASE_PERIOD_BITS) - 1) << MVPP2_PLCR_BASE_PERIOD_OFFS) ++#define MVPP2_PLCR_BASE_PERIOD_MASK(p) \ ++ (((p) << MVPP2_PLCR_BASE_PERIOD_OFFS) & MVPP2_PLCR_BASE_PERIOD_ALL_MASK) ++ ++#define MVPP2_PLCR_ADD_TOKENS_EN_BIT 16 ++#define MVPP2_PLCR_ADD_TOKENS_EN_MASK BIT(MVPP2_PLCR_ADD_TOKENS_EN_BIT) ++/*--------------------------------------------------------------------------------------------*/ ++#define MVPP2_PLCR_MODE_REG (0x1308) ++#define MVPP2_PLCR_MODE_BITS (3) ++#define MVPP2_PLCR_MODE_MASK (((1 << MVPP2_PLCR_MODE_BITS) - 1) << 0) ++ ++/*---------------------------------------------------------------------------------------------*/ ++/* exist only in ppv2.1*/ ++#define MVPP2_PLCR_TABLE_INDEX_REG (0x130c) ++#define MVPP2_PLCR_COMMIT_TOKENS_REG (0x1310) ++#define MVPP2_PLCR_EXCESS_TOKENS_REG (0x1314) ++/*---------------------------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_BUCKET_SIZE_REG (0x1318) ++ ++#define MVPP2_PLCR_COMMIT_SIZE_OFFS 0 ++#define MVPP2_PLCR_COMMIT_SIZE_BITS 16 ++#define MVPP2_PLCR_COMMIT_SIZE_ALL_MASK \ ++ (((1 << MVPP2_PLCR_COMMIT_SIZE_BITS) - 1) << MVPP2_PLCR_COMMIT_SIZE_OFFS) ++#define MVPP2_PLCR_COMMIT_SIZE_MASK(size) \ ++ (((size) << MVPP2_PLCR_COMMIT_SIZE_OFFS) & MVPP2_PLCR_COMMIT_SIZE_ALL_MASK) ++ ++#define MVPP2_PLCR_EXCESS_SIZE_OFFS 16 ++#define MVPP2_PLCR_EXCESS_SIZE_BITS 16 ++#define MVPP2_PLCR_EXCESS_SIZE_ALL_MASK \ ++ (((1 << MVPP2_PLCR_EXCESS_SIZE_BITS) - 1) << MVPP2_PLCR_EXCESS_SIZE_OFFS) ++#define MVPP2_PLCR_EXCESS_SIZE_MASK(size) \ ++ (((size) << MVPP2_PLCR_EXCESS_SIZE_OFFS) & MVPP2_PLCR_EXCESS_SIZE_ALL_MASK) ++/*---------------------------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_TOKEN_CFG_REG (0x131c) ++ ++#define MVPP2_PLCR_TOKEN_VALUE_OFFS 0 ++#define MVPP2_PLCR_TOKEN_VALUE_BITS 10 ++#define MVPP2_PLCR_TOKEN_VALUE_ALL_MASK \ ++ (((1 << MVPP2_PLCR_TOKEN_VALUE_BITS) - 1) << MVPP2_PLCR_TOKEN_VALUE_OFFS) ++#define MVPP2_PLCR_TOKEN_VALUE_MASK(val) \ ++ (((val) << MVPP2_PLCR_TOKEN_VALUE_OFFS) & MVPP2_PLCR_TOKEN_VALUE_ALL_MASK) ++ ++#define MVPP2_PLCR_TOKEN_TYPE_OFFS 12 ++#define MVPP2_PLCR_TOKEN_TYPE_BITS 3 ++#define MVPP2_PLCR_TOKEN_TYPE_ALL_MASK \ ++ (((1 << MVPP2_PLCR_TOKEN_TYPE_BITS) - 1) << MVPP2_PLCR_TOKEN_TYPE_OFFS) ++#define MVPP2_PLCR_TOKEN_TYPE_MASK(type) \ ++ (((type) << MVPP2_PLCR_TOKEN_TYPE_OFFS) & MVPP2_PLCR_TOKEN_TYPE_ALL_MASK) ++ ++#define MVPP2_PLCR_TOKEN_UNIT_BIT 31 ++#define MVPP2_PLCR_TOKEN_UNIT_MASK BIT(MVPP2_PLCR_TOKEN_UNIT_BIT) ++#define MVPP2_PLCR_TOKEN_UNIT_BYTES (0 << MVPP2_PLCR_TOKEN_UNIT_BIT) ++#define MVPP2_PLCR_TOKEN_UNIT_PKTS BIT(MVPP2_PLCR_TOKEN_UNIT_BIT) ++ ++#define MVPP2_PLCR_COLOR_MODE_BIT 30 ++#define MVPP2_PLCR_COLOR_MODE_MASK BIT(MVPP2_PLCR_COLOR_MODE_BIT) ++#define MVPP2_PLCR_COLOR_MODE_BLIND (0 << MVPP2_PLCR_COLOR_MODE_BIT) ++#define MVPP2_PLCR_COLOR_MODE_AWARE BIT(MVPP2_PLCR_COLOR_MODE_BIT) ++ ++#define MVPP2_PLCR_ENABLE_BIT 29 ++#define MVPP2_PLCR_ENABLE_MASK BIT(MVPP2_PLCR_ENABLE_BIT) ++/*---------------------------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_MIN_PKT_LEN_REG (0x1320) ++ ++#define MVPP2_PLCR_MIN_PKT_LEN_OFFS 0 ++#define MVPP2_PLCR_MIN_PKT_LEN_BITS 8 ++#define MVPP2_PLCR_MIN_PKT_LEN_ALL_MASK \ ++ (((1 << MVPP2_PLCR_MIN_PKT_LEN_BITS) - 1) << MVPP2_PLCR_MIN_PKT_LEN_OFFS) ++#define MVPP2_PLCR_MIN_PKT_LEN_MASK(len) \ ++ (((len) << MVPP2_PLCR_MIN_PKT_LEN_OFFS) & MVPP2_PLCR_MIN_PKT_LEN_ALL_MASK) ++/*---------------------------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_EDROP_EN_REG (0x1330) ++ ++#define MVPP2_PLCR_EDROP_EN_BIT 0 ++#define MVPP2_PLCR_EDROP_EN_MASK BIT(MVPP2_PLCR_EDROP_EN_BIT) ++/*---------------------------------------------------------------------------------------------*/ ++/*ppv2.1 policer early drop threshold mechanism changed*/ ++#define MVPP2_V0_PLCR_EDROP_THRESH_NUM 4 ++ ++#define MVPP2_V0_PLCR_EDROP_TR_OFFS(i) ((i % 2) ? 16 : 0) ++#define MVPP2_V0_PLCR_EDROP_TR_BITS 14 ++#define MVPP2_V0_PLCR_EDROP_TR_ALL_MASK(i) \ ++ (((1 << MVPP2_V0_PLCR_EDROP_TR_BITS) - 1) << MVPP2_V0_PLCR_EDROP_TR_OFFS(i)) ++#define MVPP2_V0_PLCR_EDROP_TR_MASK(i, tr) \ ++ (((tr) << MVPP2_V0_PLCR_EDROP_TR_OFFS(i)) & MVPP2_V0_PLCR_EDROP_TR_ALL_MASK(i)) ++ ++#define MVPP2_V0_PLCR_EDROP_CPU_TR_REG(i) (0x1340 + (((i) / 2) << 2)) ++#define MVPP2_V0_PLCR_EDROP_HWF_TR_REG(i) (0x1350 + (((i) / 2) << 2)) ++/*---------------------------------------------------------------------------------------------*/ ++/*ppv2.1 policer early drop threshold new mechanism*/ ++#define MVPP2_V1_PLCR_EDROP_THRESH_NUM 16 ++ ++#define MVPP2_V1_PLCR_EDROP_TR_OFFS 0 ++#define MVPP2_V1_PLCR_EDROP_TR_BITS 14 ++ ++#define MVPP2_V1_PLCR_EDROP_TR_MASK(i) \ ++ (((1 << MVPP2_V1_PLCR_EDROP_TR_BITS) - 1) << MVPP2_V1_PLCR_EDROP_TR_OFFS) ++ ++#define MVPP2_V1_PLCR_EDROP_CPU_TR_REG(i) (0x1380 + ((i) * 4)) ++#define MVPP2_V1_PLCR_EDROP_HWF_TR_REG(i) (0x13c0 + ((i) * 4)) ++ ++/*---------------------------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_EDROP_RXQ_REG (0x1348) ++#define MVPP2_PLCR_EDROP_RXQ_TR_REG (0x134c) ++/*--------------------------------------------------------------------------*/ ++ ++#define MVPP2_PLCR_EDROP_TXQ_REG (0x1358) ++#define MVPP2_PLCR_EDROP_TXQ_TR_REG (0x135c) ++/*--------------------------------------------------------------------------*/ ++#define MVPP2_V1_PLCR_PKT_GREEN_REG(pol) (0x7400 + 4 * (pol)) ++#define MVPP2_V1_PLCR_PKT_YELLOW_REG(pol) (0x7500 + 4 * (pol)) ++#define MVPP2_V1_PLCR_PKT_RED_REG(pol) (0x7600 + 4 * (pol)) ++/*---------------------------------------------------------------------------------------------*/ ++ +diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c +new file mode 100644 +index 00000000..f9a3409 +--- /dev/null ++++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c +@@ -0,0 +1,7966 @@ ++ /* ++ * *************************************************************************** ++ * Copyright (C) 2016 Marvell International Ltd. ++ * *************************************************************************** ++ * 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 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, see . ++ * *************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "mv_pp2x.h" ++#include "mv_pp2x_hw.h" ++#include "mv_gop110_hw.h" ++ ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ ++#ifdef CONFIG_MV_PTP_SERVICE ++/* inline PTP procedures */ ++#include ++/* non-inline init/config */ ++#include ++#include ++#include ++#endif ++ ++#define MVPP2_SKB_TEST_SIZE 64 ++#define MVPP2_ADDRESS 0xf2000000 ++#define CPN110_ADDRESS_SPACE_SIZE (16 * 1024 * 1024) ++ ++#define UIO_PP2_STRING "uio_pp_%d" ++#define UIO_PORT_STRING "uio_pp_port_%d:%d" ++ ++/* Declaractions */ ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++u8 mv_pp2x_num_cos_queues = 1; ++#else ++u8 mv_pp2x_num_cos_queues = 4; ++#endif ++ ++u8 mv_pp2x_rx_count = MVPP2_DEFAULT_RX_COUNT; ++ ++static u8 mv_pp2x_queue_mode = MVPP2_QDIST_MULTI_MODE; ++static u8 mv_bm_underrun_protect = MVPP23_BM_UNPR_EN; ++static u8 mv_pp2x_used_addr_spaces; ++static u8 rss_mode = MVPP2_RSS_5T; /* Set 5-tuple default RSS mode */ ++static u8 default_cpu; ++static u8 cos_classifer; ++static u32 pri_map = 0x76543210; /* As default, cos0--rxq0, cos1--rxq1, */ ++ /* cos2--rxq2, cos3--rxq3 cos4..cos7 */ ++static u8 default_cos = 3; /* As default, non-IP packet has highest CoS value */ ++static u16 rx_queue_size = MVPP2_MAX_RXD; ++static u16 tx_queue_size = MVPP2_MAX_TXD; ++static u16 buffer_scaling = 100; ++static u32 port_cpu_bind_map; ++static u8 first_bm_pool; ++static u8 first_log_rxq_queue; ++static u8 uc_filter_max = 4; ++static u16 stats_delay_msec = STATS_DELAY; ++static u16 stats_delay; ++static int auto_cell_index; ++static u8 txdone_tmr; ++ ++u32 debug_param; ++ ++struct mv_pp2x_pool_attributes mv_pp2x_pools[] = { ++ { ++ .description = "short", /* pkt_size=MVPP2_BM_SHORT_PKT_SIZE */ ++ .buf_num = MVPP2_BM_SHORT_BUF_NUM, ++ }, ++ { ++ .description = "long", /* pkt_size=MVPP2_BM_LONG_PKT_SIZE */ ++ .buf_num = MVPP2_BM_LONG_BUF_NUM, ++ }, ++ { ++ .description = "jumbo", /* pkt_size=MVPP2_BM_JUMBO_PKT_SIZE */ ++ .buf_num = MVPP2_BM_JUMBO_BUF_NUM, ++ } ++}; ++ ++module_param_named(num_cos_queues, mv_pp2x_num_cos_queues, byte, S_IRUGO); ++MODULE_PARM_DESC(num_cos_queues, "Set number of cos_queues (1-8), def=4"); ++ ++module_param_named(queue_mode, mv_pp2x_queue_mode, byte, S_IRUGO); ++MODULE_PARM_DESC(queue_mode, "Set queue_mode (single=0, multi=1)"); ++ ++module_param(rss_mode, byte, S_IRUGO); ++MODULE_PARM_DESC(rss_mode, "Set rss_mode (2T=0, 5T=1)"); ++ ++module_param(default_cpu, byte, S_IRUGO); ++MODULE_PARM_DESC(default_cpu, "Set default CPU for non RSS frames"); ++ ++module_param(cos_classifer, byte, S_IRUGO); ++MODULE_PARM_DESC(cos_classifer, ++ "Cos Classifier (vlan_pri=0, dscp=1, vlan_dscp=2, dscp_vlan=3)"); ++ ++module_param(pri_map, uint, S_IRUGO); ++MODULE_PARM_DESC(pri_map, "Set priority_map, nibble for each cos."); ++ ++module_param(default_cos, byte, S_IRUGO); ++MODULE_PARM_DESC(default_cos, ++ "Set default cos value(0-7) for unclassified traffic"); ++ ++module_param(rx_queue_size, ushort, S_IRUGO); ++MODULE_PARM_DESC(rx_queue_size, "Rx queue size"); ++ ++module_param(tx_queue_size, ushort, S_IRUGO); ++MODULE_PARM_DESC(tx_queue_size, "Tx queue size"); ++ ++module_param(buffer_scaling, ushort, S_IRUGO); ++MODULE_PARM_DESC(buffer_scaling, "Buffer scaling (TBD)"); ++ ++module_param(uc_filter_max, byte, S_IRUGO); ++MODULE_PARM_DESC(uc_filter_max, ++ "Set unicast filter max size, it is multiple of 4. def=4"); ++ ++module_param(debug_param, uint, S_IRUGO); ++MODULE_PARM_DESC(debug_param, ++ "Ad-hoc parameter, which can be used for various debug operations."); ++ ++module_param(stats_delay_msec, ushort, S_IRUGO); ++MODULE_PARM_DESC(stats_delay_msec, "Set statistic delay in msec, def=250"); ++ ++module_param_named(short_pool, mv_pp2x_pools[MVPP2_BM_SWF_SHORT_POOL].buf_num, uint, S_IRUGO); ++MODULE_PARM_DESC(short_pool, "Short pool size (0-8192), def=2048"); ++ ++module_param_named(long_pool, mv_pp2x_pools[MVPP2_BM_SWF_LONG_POOL].buf_num, uint, S_IRUGO); ++MODULE_PARM_DESC(long_pool, "Long pool size (0-8192), def=1024"); ++ ++module_param_named(jumbo_pool, mv_pp2x_pools[MVPP2_BM_SWF_JUMBO_POOL].buf_num, uint, S_IRUGO); ++MODULE_PARM_DESC(jumbo_pool, "Jumbo pool size (0-8192), def=512"); ++ ++module_param_named(rx_count, mv_pp2x_rx_count, byte, S_IRUGO); ++MODULE_PARM_DESC(rx_count, "Set rx_count 8 or 16, def = 8. Parameter configure number of HOT CPU's."); ++ ++module_param(txdone_tmr, byte, S_IRUGO); ++MODULE_PARM_DESC(txdone_tmr, "TX-done handling by 0:interrupt(default) or by 1:timer"); ++ ++module_param_named(bm_underrun_protect, mv_bm_underrun_protect, byte, S_IRUGO); ++MODULE_PARM_DESC(bm_underrun_protect, "Set BM underrun protect feature (0-1), def=1"); ++ ++/* BEGIN: module_params for testing only */ ++module_param(port_cpu_bind_map, uint, S_IRUGO); ++MODULE_PARM_DESC(port_cpu_bind_map, ++ "Set default port-to-cpu binding, nibble for each port. Relevant when queue_mode=multi-mode & rss is disabled"); ++ ++module_param(first_bm_pool, byte, S_IRUGO); ++MODULE_PARM_DESC(first_bm_pool, "First used buffer pool (0-11)"); ++ ++module_param(first_log_rxq_queue, byte, S_IRUGO); ++MODULE_PARM_DESC(first_log_rxq_queue, "First logical rx_queue (0-31)"); ++/* END: module_params for testing only */ ++ ++void set_device_base_address(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ dev->mem_start = (unsigned long)port->priv->hw.phys_addr_start; ++ dev->mem_end = (unsigned long)port->priv->hw.phys_addr_end; ++} ++ ++/* Number of RXQs used by single port */ ++static int mv_pp2x_rxq_number; ++/* Number of TXQs used by single port */ ++static int mv_pp2x_txq_number; ++ ++static inline int mv_pp2x_txq_count(struct mv_pp2x_txq_pcpu *txq_pcpu) ++{ ++ int index_modulo = (txq_pcpu->txq_put_index - txq_pcpu->txq_get_index + ++ txq_pcpu->size) % txq_pcpu->size; ++ ++ return index_modulo; ++} ++ ++static inline int mv_pp2x_txq_free_count(struct mv_pp2x_txq_pcpu *txq_pcpu) ++{ ++ int index_modulo = (txq_pcpu->txq_get_index - txq_pcpu->txq_put_index + ++ txq_pcpu->size) % txq_pcpu->size; ++ ++ if (unlikely(index_modulo == 0)) ++ return txq_pcpu->size; ++ ++ return index_modulo; ++} ++ ++static void mv_pp2x_txq_inc_get(struct mv_pp2x_txq_pcpu *txq_pcpu) ++{ ++ if (unlikely(txq_pcpu->txq_get_index == txq_pcpu->size - 1)) ++ txq_pcpu->txq_get_index = 0; ++ else ++ txq_pcpu->txq_get_index++; ++} ++ ++void mv_pp2x_txq_inc_error(struct mv_pp2x_txq_pcpu *txq_pcpu, int num) ++{ ++ for (; num > 0; num--) { ++ if (unlikely(txq_pcpu->txq_put_index < 1)) ++ txq_pcpu->txq_put_index = txq_pcpu->size - 1; ++ else ++ txq_pcpu->txq_put_index--; ++ txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = 0; ++ txq_pcpu->data_size[txq_pcpu->txq_put_index] = 0; ++ txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] = 0; ++ } ++} ++ ++void mv_pp2x_txq_inc_put(enum mvppv2_version pp2_ver, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ struct sk_buff *skb, ++ struct mv_pp2x_tx_desc *tx_desc) ++{ ++ txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb; ++ txq_pcpu->data_size[txq_pcpu->txq_put_index] = tx_desc->data_size; ++ txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] = ++ mv_pp2x_txdesc_phys_addr_get(pp2_ver, tx_desc); ++ if (unlikely(txq_pcpu->txq_put_index == txq_pcpu->size - 1)) ++ txq_pcpu->txq_put_index = 0; ++ else ++ txq_pcpu->txq_put_index++; ++#if defined(__BIG_ENDIAN) ++ if (pp2_ver == PPV21) ++ mv_pp21_tx_desc_swap(tx_desc); ++ else ++ mv_pp22_tx_desc_swap(tx_desc); ++#endif /* __BIG_ENDIAN */ ++} ++ ++static void mv_pp2x_txq_dec_put(struct mv_pp2x_txq_pcpu *txq_pcpu) ++{ ++ if (unlikely(txq_pcpu->txq_put_index == 0)) ++ txq_pcpu->txq_put_index = txq_pcpu->size - 1; ++ else ++ txq_pcpu->txq_put_index--; ++} ++ ++static void mv_pp2x_extra_pool_inc(struct mv_pp2x_ext_buf_pool *ext_buf_pool) ++{ ++ if (unlikely(ext_buf_pool->buf_pool_next_free == ext_buf_pool->buf_pool_size - 1)) ++ ext_buf_pool->buf_pool_next_free = 0; ++ else ++ ext_buf_pool->buf_pool_next_free++; ++} ++ ++static void mv_pp2x_skb_pool_inc(struct mv_pp2x_skb_pool *skb_pool) ++{ ++ if (unlikely(skb_pool->skb_pool_next_free == skb_pool->skb_pool_size - 1)) ++ skb_pool->skb_pool_next_free = 0; ++ else ++ skb_pool->skb_pool_next_free++; ++} ++ ++static u8 mv_pp2x_first_pool_get(struct mv_pp2x *priv) ++{ ++ return priv->pp2_cfg.first_bm_pool; ++} ++ ++static int mv_pp2x_pool_pkt_size_get(enum mv_pp2x_bm_pool_log_num log_id) ++{ ++ return mv_pp2x_pools[log_id].pkt_size; ++} ++ ++static int mv_pp2x_pool_buf_num_get(enum mv_pp2x_bm_pool_log_num log_id) ++{ ++ return mv_pp2x_pools[log_id].buf_num; ++} ++ ++char *mv_pp2x_pool_description_get(enum mv_pp2x_bm_pool_log_num log_id) ++{ ++ return mv_pp2x_pools[log_id].description; ++} ++EXPORT_SYMBOL(mv_pp2x_pool_description_get); ++ ++/* Buffer Manager configuration routines */ ++static void *mv_pp2x_frag_alloc(const struct mv_pp2x_bm_pool *pool) ++{ ++ if (likely(pool->frag_size <= PAGE_SIZE)) ++ return netdev_alloc_frag(pool->frag_size); ++ else ++ return kmalloc(pool->frag_size, GFP_ATOMIC); ++} ++ ++static void mv_pp2x_frag_free(const struct mv_pp2x_bm_pool *pool, void *data) ++{ ++ if (likely(pool->frag_size <= PAGE_SIZE)) ++ skb_free_frag(data); ++ else ++ kfree(data); ++} ++ ++static int mv_pp2x_rx_refill_new(struct mv_pp2x_port *port, ++ struct mv_pp2x_bm_pool *bm_pool, ++ u32 pool, int is_recycle, int cpu) ++{ ++ dma_addr_t phys_addr; ++ void *data; ++ struct mv_pp2x_cp_pcpu *cp_pcpu = port->priv->pcpu[cpu]; ++ ++ /* BM pool is refilled only if number of used buffers is bellow ++ * refill threshold. Number of used buffers could be decremented ++ * by recycling mechanism. ++ */ ++ if (is_recycle && ++ (cp_pcpu->in_use[bm_pool->id] < bm_pool->in_use_thresh)) ++ return 0; ++ ++ data = mv_pp2x_frag_alloc(bm_pool); ++ if (!data) ++ return -ENOMEM; ++ ++ phys_addr = dma_map_single(port->dev->dev.parent, data, ++ MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), ++ DMA_FROM_DEVICE); ++ ++ if (unlikely(dma_mapping_error(port->dev->dev.parent, phys_addr))) { ++ mv_pp2x_frag_free(bm_pool, data); ++ return -ENOMEM; ++ } ++ ++ mv_pp2x_pool_refill(port->priv, pool, phys_addr, cpu); ++ ++ /* Decrement only if refill called from RX */ ++ if (is_recycle) ++ cp_pcpu->in_use[bm_pool->id]--; ++ ++ return 0; ++} ++ ++/* Create pool */ ++static int mv_pp2x_bm_pool_create(struct device *dev, ++ struct mv_pp2x_hw *hw, ++ struct mv_pp2x_bm_pool *bm_pool, ++ int size, int pkt_size) ++{ ++ int size_bytes; ++ ++ /* Driver enforces size= x16 both for PPv21 and for PPv22, even though ++ * PPv22 HW allows size= x8 ++ */ ++ if (!IS_ALIGNED(size, (1 << MVPP21_BM_POOL_SIZE_OFFSET))) ++ return -EINVAL; ++ ++ /*YuvalC: Two pointers per buffer, existing bug fixed. */ ++ size_bytes = 2 * sizeof(uintptr_t) * size; ++ bm_pool->virt_addr = dma_alloc_coherent(dev, size_bytes, ++ &bm_pool->phys_addr, ++ GFP_KERNEL); ++ if (!bm_pool->virt_addr) ++ return -ENOMEM; ++ ++ if (!IS_ALIGNED((uintptr_t)bm_pool->virt_addr, ++ MVPP2_BM_POOL_PTR_ALIGN)) { ++ dma_free_coherent(dev, size_bytes, bm_pool->virt_addr, ++ bm_pool->phys_addr); ++ dev_err(dev, "BM pool %d is not %d bytes aligned\n", ++ bm_pool->id, MVPP2_BM_POOL_PTR_ALIGN); ++ return -ENOMEM; ++ } ++ ++ mv_pp2x_bm_hw_pool_create(hw, bm_pool->id, bm_pool->phys_addr, size); ++ ++ bm_pool->size = size; ++ bm_pool->pkt_size = pkt_size; ++ bm_pool->frag_size = SKB_DATA_ALIGN(MVPP2_RX_BUF_SIZE( ++ bm_pool->pkt_size)) + MVPP2_SKB_SHINFO_SIZE; ++ bm_pool->buf_num = 0; ++ mv_pp2x_bm_pool_bufsize_set(hw, bm_pool, ++ MVPP2_RX_BUF_SIZE(bm_pool->pkt_size)); ++ ++ return 0; ++} ++ ++void mv_pp2x_bm_bufs_free(struct device *dev, struct mv_pp2x *priv, ++ struct mv_pp2x_bm_pool *bm_pool, int buf_num) ++{ ++ int i; ++ ++ if (buf_num > bm_pool->buf_num) { ++ WARN(1, "Pool does not have so many bufs pool(%d) bufs(%d)\n", ++ bm_pool->id, buf_num); ++ buf_num = bm_pool->buf_num; ++ } ++ for (i = 0; i < buf_num; i++) { ++ u8 *virt_addr; ++ dma_addr_t phys_addr; ++ ++ /* Get buffer virtual address (indirect access) */ ++ phys_addr = mv_pp2x_bm_phys_addr_get(&priv->hw, bm_pool->id); ++ if (!phys_addr) ++ break; ++ if (!bm_pool->external_pool) { ++ dma_unmap_single(dev, phys_addr, ++ MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), DMA_TO_DEVICE); ++ virt_addr = phys_to_virt(dma_to_phys(dev, phys_addr)); ++ mv_pp2x_frag_free(bm_pool, virt_addr); ++ } ++ } ++ ++ /* Update BM driver with number of buffers removed from pool */ ++ bm_pool->buf_num -= i; ++} ++ ++/* Cleanup pool */ ++int mv_pp2x_bm_pool_destroy(struct device *dev, struct mv_pp2x *priv, ++ struct mv_pp2x_bm_pool *bm_pool) ++{ ++ u32 val; ++ int size_bytes, buf_num; ++ ++ buf_num = mv_pp2x_check_hw_buf_num(priv, bm_pool); ++ ++ mv_pp2x_bm_bufs_free(dev, priv, bm_pool, buf_num); ++ ++ /* Check buffer counters after free */ ++ buf_num = mv_pp2x_check_hw_buf_num(priv, bm_pool); ++ ++ if (buf_num) { ++ WARN(1, "cannot free all buffers in pool %d, buf_num left %d\n", ++ bm_pool->id, ++ bm_pool->buf_num); ++ return 0; ++ } ++ ++ val = mv_pp2x_read(&priv->hw, MVPP2_BM_POOL_CTRL_REG(bm_pool->id)); ++ val |= MVPP2_BM_STOP_MASK; ++ mv_pp2x_write(&priv->hw, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val); ++ ++ size_bytes = 2 * sizeof(uintptr_t) * bm_pool->size; ++ dma_free_coherent(dev, size_bytes, bm_pool->virt_addr, ++ bm_pool->phys_addr); ++ mv_pp2x_bm_pool_bufsize_set(&priv->hw, bm_pool, 0); ++ priv->num_pools--; ++ return 0; ++} ++ ++int mv_pp2x_bm_pool_ext_add(struct device *dev, struct mv_pp2x *priv, ++ u32 *pool_num, u32 pkt_size) ++{ ++ int err, size, enabled; ++ u8 first_pool = mv_pp2x_first_pool_get(priv); ++ u32 pool = priv->num_pools; ++ struct mv_pp2x_bm_pool *bm_pool; ++ struct mv_pp2x_hw *hw = &priv->hw; ++ ++ if ((priv->num_pools + 1) > MVPP2_BM_POOLS_MAX_ALLOC_NUM) { ++ dev_err(dev, "Unable to add pool. Max BM pool alloc reached %d\n", ++ priv->num_pools + 1); ++ return -ENOMEM; ++ } ++ ++ /* Check if pool is already active. Ignore request */ ++ enabled = mv_pp2x_read(hw, MVPP2_BM_POOL_CTRL_REG(pool)) & ++ MVPP2_BM_STATE_MASK; ++ ++ if (enabled) { ++ dev_info(dev, "%s pool %d already enabled. Ignoring request\n", ++ __func__, pool); ++ return 0; ++ } ++ ++ /* Mask BM interrupts */ ++ mv_pp2x_write(&priv->hw, MVPP2_BM_INTR_MASK_REG(first_pool + ++ pool), 0); ++ /* Clear BM cause register */ ++ mv_pp2x_write(&priv->hw, MVPP2_BM_INTR_CAUSE_REG(first_pool + ++ pool), 0); ++ ++ /* Create all pools with maximum size */ ++ size = MVPP2_BM_POOL_SIZE_MAX; ++ bm_pool = &priv->bm_pools[pool]; ++ bm_pool->log_id = pool; ++ bm_pool->id = first_pool + pool; ++ bm_pool->external_pool = true; ++ err = mv_pp2x_bm_pool_create(dev, hw, bm_pool, size, pkt_size); ++ if (err) ++ return err; ++ ++ *pool_num = pool; ++ priv->num_pools++; ++ return 0; ++} ++ ++static int mv_pp2x_bm_pools_init(struct platform_device *pdev, ++ struct mv_pp2x *priv, ++ u8 first_pool, u8 num_pools) ++{ ++ int i, err, size; ++ struct mv_pp2x_bm_pool *bm_pool; ++ struct mv_pp2x_hw *hw = &priv->hw; ++ ++ /* Create all pools with maximum size */ ++ size = MVPP2_BM_POOL_SIZE_MAX; ++ for (i = 0; i < num_pools; i++) { ++ bm_pool = &priv->bm_pools[i]; ++ bm_pool->log_id = i; ++ bm_pool->id = first_pool + i; ++ bm_pool->external_pool = false; ++ err = mv_pp2x_bm_pool_create(&pdev->dev, hw, bm_pool, size, ++ mv_pp2x_pool_pkt_size_get(bm_pool->log_id)); ++ if (err) ++ goto err_unroll_pools; ++ } ++ priv->num_pools = num_pools; ++ return 0; ++ ++err_unroll_pools: ++ dev_err(&pdev->dev, "failed to create BM pool %d, size %d\n", i, size); ++ for (i = i - 1; i >= 0; i--) ++ mv_pp2x_bm_pool_destroy(&pdev->dev, priv, &priv->bm_pools[i]); ++ return err; ++} ++ ++/* Routine enable PPv23 8 pool mode */ ++static void mv_pp23_bm_set_8pool_mode(struct mv_pp2x *priv) ++{ ++ int val; ++ ++ val = mv_pp2x_read(&priv->hw, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG); ++ val |= MVPP23_BM_8POOL_MODE; ++ mv_pp2x_write(&priv->hw, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG, val); ++} ++ ++static int mv_pp2x_bm_init(struct platform_device *pdev, struct mv_pp2x *priv) ++{ ++ int i, err, cpu; ++ u8 first_pool = mv_pp2x_first_pool_get(priv); ++ u8 num_pools = MVPP2_BM_SWF_NUM_POOLS; ++ ++ for (i = first_pool; i < (first_pool + num_pools); i++) { ++ /* Mask BM all interrupts */ ++ mv_pp2x_write(&priv->hw, MVPP2_BM_INTR_MASK_REG(i), 0); ++ /* Clear BM cause register */ ++ mv_pp2x_write(&priv->hw, MVPP2_BM_INTR_CAUSE_REG(i), 0); ++ } ++ ++ /* Allocate and initialize BM pools */ ++ priv->bm_pools = devm_kcalloc(&pdev->dev, MVPP2_BM_POOLS_NUM, ++ sizeof(struct mv_pp2x_bm_pool), ++ GFP_KERNEL); ++ if (!priv->bm_pools) ++ return -ENOMEM; ++ ++ /* On PPV22&23 high virtual and physical address buffer manager register should be ++ * initialized to 0 to avoid writing to the random addresses an 32 Bit systems. ++ */ ++ if (priv->pp2_version != PPV21) { ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ /* Reset the BM virtual and physical address high register */ ++ mv_pp2x_relaxed_write(&priv->hw, MVPP22_BM_PHY_VIRT_HIGH_RLS_REG, ++ 0, cpu); ++ } ++ } ++ ++ if (priv->pp2_version == PPV23 && mv_bm_underrun_protect == MVPP23_BM_UNPR_EN) { ++ priv->hw.mv_bm_underrun_protect = true; ++ mv_pp23_bm_set_8pool_mode(priv); ++ } else { ++ priv->hw.mv_bm_underrun_protect = false; ++ } ++ ++ err = mv_pp2x_bm_pools_init(pdev, priv, first_pool, num_pools); ++ if (err < 0) ++ return err; ++ ++ /* Init bm locks if needed */ ++ if (priv->pp2_cfg.spinlocks_bitmap & MV_BM_LOCK) ++ for (i = 0; i < MVPP2_BM_POOLS_MAX_ALLOC_NUM; i++) ++ spin_lock_init(&priv->bm_spinlock[i]); ++ ++ return 0; ++} ++ ++/* Allocate buffers for the pool */ ++int mv_pp2x_bm_bufs_add(struct mv_pp2x_port *port, ++ struct mv_pp2x_bm_pool *bm_pool, int buf_num) ++{ ++ int i, buf_size, total_size; ++ ++ buf_size = MVPP2_RX_BUF_SIZE(bm_pool->pkt_size); ++ total_size = MVPP2_RX_TOTAL_SIZE(buf_size); ++ ++ if (buf_num < 0 || ++ (buf_num + bm_pool->buf_num > bm_pool->size)) { ++ netdev_err(port->dev, ++ "cannot allocate %d buffers for pool %d\n", ++ buf_num, bm_pool->id); ++ return 0; ++ } ++ ++ for (i = 0; i < buf_num; i++) ++ mv_pp2x_rx_refill_new(port, bm_pool, (u32)bm_pool->id, 0, 0); ++ ++ /* Update BM driver with number of buffers added to pool */ ++ bm_pool->buf_num += i; ++ bm_pool->in_use_thresh = bm_pool->buf_num / MVPP2_BM_PER_CPU_THRESHOLD(num_present_cpus()); ++ ++ netdev_dbg(port->dev, ++ "%s pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n", ++ mv_pp2x_pool_description_get(bm_pool->log_id), ++ bm_pool->id, bm_pool->pkt_size, buf_size, total_size); ++ ++ netdev_dbg(port->dev, ++ "%s pool %d: %d of %d buffers added\n", ++ mv_pp2x_pool_description_get(bm_pool->log_id), ++ bm_pool->id, i, buf_num); ++ return i; ++} ++ ++static int mv_pp2x_bm_buf_calc(enum mv_pp2x_bm_pool_log_num log_pool, ++ u32 port_map) ++{ ++ /*TODO: Code algo based on ++ * port_map/num_rx_queues/num_tx_queues/queue_sizes ++ */ ++ int num_ports = hweight_long(port_map); ++ ++ return(num_ports * mv_pp2x_pool_buf_num_get(log_pool)); ++} ++ ++/* Routine disable/enable flow control for BM pool conditon */ ++void mv_pp2x_bm_pool_update_fc(struct mv_pp2x_port *port, struct mv_pp2x_bm_pool *pool, int en) ++{ ++ int val, cm3_state; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->priv->mss_spinlock, flags); ++ ++ /* Remove Flow control enable bit to prevent race between FW and Kernel ++ * If Flow control were enabled, it would be re-enabled. ++ */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_FC_COM_REG); ++ cm3_state = (val & FLOW_CONTROL_ENABLE_BIT); ++ val &= ~FLOW_CONTROL_ENABLE_BIT; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_FC_COM_REG, val); ++ ++ /* Check if BM pool should be enabled/disable */ ++ if (en > 0) { ++ /* Set BM pool start and stop thresholds per port */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_CM3_BUF_POOL_BASE + pool->id * MSS_CP_CM3_BUF_POOL_OFFS); ++ val |= (0x1 << (port->id + MSS_CP_CM3_BUF_POOL_PORTS_OFFS)); ++ val &= ~MSS_CP_CM3_BUF_POOL_START_MASK; ++ val |= (MSS_CP_CM3_THRESHOLD_START << MSS_CP_CM3_BUF_POOL_START_OFFS); ++ val &= ~MSS_CP_CM3_BUF_POOL_STOP_MASK; ++ val |= MSS_CP_CM3_THRESHOLD_STOP; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_CM3_BUF_POOL_BASE + pool->id * ++ MSS_CP_CM3_BUF_POOL_OFFS, val); ++ } else if (en < 0) { ++ /* Remove BM pool from the port */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_CM3_BUF_POOL_BASE + pool->id * MSS_CP_CM3_BUF_POOL_OFFS); ++ val &= ~(0x1 << (port->id + MSS_CP_CM3_BUF_POOL_PORTS_OFFS)); ++ ++ /* Zero BM pool start and stop thresholds to disable pool flow control ++ * if pool empty (not used by any port) ++ */ ++ if (!pool->buf_num) { ++ val &= ~MSS_CP_CM3_BUF_POOL_START_MASK; ++ val &= ~MSS_CP_CM3_BUF_POOL_STOP_MASK; ++ } ++ ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_CM3_BUF_POOL_BASE + pool->id * MSS_CP_CM3_BUF_POOL_OFFS, val); ++ } ++ ++ /* Notify Firmware that Flow control config space ready for update */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_FC_COM_REG); ++ val |= FLOW_CONTROL_UPDATE_COMMAND_BIT; ++ val |= cm3_state; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_FC_COM_REG, val); ++ ++ spin_unlock_irqrestore(&port->priv->mss_spinlock, flags); ++} ++ ++/* Notify the driver that BM pool is being used as specific type and return the ++ * pool pointer on success ++ */ ++ ++static struct mv_pp2x_bm_pool *mv_pp2x_bm_pool_use_internal( ++ struct mv_pp2x_port *port, enum mv_pp2x_bm_pool_log_num log_pool, ++ bool add_port) ++{ ++ int pkts_num, add_num, num; ++ struct mv_pp2x_bm_pool *pool = &port->priv->bm_pools[log_pool]; ++ ++ if (log_pool < MVPP2_BM_SWF_SHORT_POOL || ++ log_pool > MVPP2_BM_SWF_JUMBO_POOL) { ++ netdev_err(port->dev, "pool does not exist\n"); ++ return NULL; ++ } ++ ++ if (add_port) { ++ pkts_num = mv_pp2x_bm_buf_calc(log_pool, ++ pool->port_map | ++ (1 << port->id)); ++ } else { ++ pkts_num = mv_pp2x_bm_buf_calc(log_pool, ++ pool->port_map & ++ ~(1 << port->id)); ++ } ++ ++ add_num = pkts_num - pool->buf_num; ++ ++ /* Allocate buffers for this pool, or free or do nothing */ ++ if (add_num > 0) { ++ num = mv_pp2x_bm_bufs_add(port, pool, add_num); ++ if (num != add_num) { ++ WARN(1, "pool %d: %d of %d allocated\n", ++ pool->id, num, pkts_num); ++ /* We need to undo the bufs_add() allocations */ ++ return NULL; ++ } ++ } else if (add_num < 0) { ++ mv_pp2x_bm_bufs_free(port->dev->dev.parent, port->priv, pool, -add_num); ++ } ++ ++ return pool; ++} ++ ++static struct mv_pp2x_bm_pool *mv_pp2x_bm_pool_use( ++ struct mv_pp2x_port *port, ++ enum mv_pp2x_bm_pool_log_num log_pool) ++{ ++ return mv_pp2x_bm_pool_use_internal(port, log_pool, true); ++} ++ ++int mv_pp2x_swf_bm_pool_assign(struct mv_pp2x_port *port, u32 rxq, ++ u32 long_id, u32 short_id) ++{ ++ void (*cb_long_set)(struct mv_pp2x_hw *, int, int); ++ void (*cb_shrt_set)(struct mv_pp2x_hw *, int, int); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ if (rxq >= port->num_rx_queues) ++ return -ENOMEM; ++ ++ cb_long_set = port->priv->pp2xdata->mv_pp2x_rxq_long_pool_set; ++ cb_shrt_set = port->priv->pp2xdata->mv_pp2x_rxq_short_pool_set; ++ ++ if (long_id < MVPP2_BM_POOLS_NUM) ++ cb_long_set(hw, port->rxqs[rxq]->id, long_id); ++ if (short_id < MVPP2_BM_POOLS_NUM) ++ cb_shrt_set(hw, port->rxqs[rxq]->id, short_id); ++ return 0; ++} ++ ++/* Initialize pools for swf */ ++static int mv_pp2x_swf_bm_pool_init(struct mv_pp2x_port *port) ++{ ++ int rxq; ++ int sw_num_pools; /* Short+Long or Short+Long+Jumbo */ ++ enum mv_pp2x_bm_pool_log_num slj; /* sw ShortLongJumbo id */ ++ struct mv_pp2x_bm_pool *pools[MVPP2_BM_SWF_NUM_POOLS]; ++ ++ /* If port pkt_size is higher than 1518B: ++ * HW long pool is SWF_JUMBO, HW short pool is SWF_LONG ++ * else: HW long pool is SWF_LONG, HW short pool is SWF_SHORT ++ */ ++ sw_num_pools = (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) ? ++ MVPP2_BM_SWF_NUM_POOLS : (MVPP2_BM_SWF_NUM_POOLS - 1); ++ ++ for (slj = 0; slj < sw_num_pools; slj++) { ++ /* Allocate S,L(J) pools and/or return pointerS */ ++ pools[slj] = mv_pp2x_bm_pool_use(port, slj); ++ if (!pools[slj]) ++ return -ENOMEM; ++ pools[slj]->port_map |= (1 << port->id); ++ } ++ ++ if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) { ++ port->pool_long = pools[MVPP2_BM_SWF_JUMBO_POOL]; ++ port->pool_short = pools[MVPP2_BM_SWF_LONG_POOL]; ++ } else { ++ port->pool_long = pools[MVPP2_BM_SWF_LONG_POOL]; ++ port->pool_short = pools[MVPP2_BM_SWF_SHORT_POOL]; ++ } ++ ++ /* Assign new short&long pools to BM hw per RXQs */ ++ for (rxq = 0; rxq < port->num_rx_queues; rxq++) ++ mv_pp2x_swf_bm_pool_assign(port, rxq, ++ port->pool_long->id, ++ port->pool_short->id); ++ return 0; ++} ++ ++static int mv_pp2x_bm_update_mtu(struct net_device *dev, int mtu) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ enum mv_pp2x_bm_pool_log_num new_pool_long; ++ const netdev_features_t netif_f_ip_csum = ++ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ++ ++ if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) ++ new_pool_long = MVPP2_BM_SWF_JUMBO_POOL; ++ else ++ new_pool_long = MVPP2_BM_SWF_LONG_POOL; ++ ++ if (new_pool_long == port->pool_long->log_id) ++ return 0; ++ ++ /* TO or FROM jumbo transition required. Call for ++ * bm_pool_init() checks S/L/J pool allocations and sets ++ * correct pool_short/pool_long pointers upon pkt_size. ++ * ++ * The BM-HW could still use old pool and buffers set, ++ * so don't unmap ports from pools and don't free pool-buffers. ++ */ ++ if (mv_pp2x_swf_bm_pool_init(port)) ++ return -ENOMEM; ++ ++ if (port->flow_control) { ++ if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) { ++ mv_pp2x_bm_pool_update_fc(port, &port->priv->bm_pools[MVPP2_BM_SWF_JUMBO_POOL], 1); ++ mv_pp2x_bm_pool_update_fc(port, &port->priv->bm_pools[MVPP2_BM_SWF_SHORT_POOL], -1); ++ } else { ++ mv_pp2x_bm_pool_update_fc(port, &port->priv->bm_pools[MVPP2_BM_SWF_JUMBO_POOL], -1); ++ mv_pp2x_bm_pool_update_fc(port, &port->priv->bm_pools[MVPP2_BM_SWF_SHORT_POOL], 1); ++ } ++ } ++ ++ /* Update L4 checksum when jumbo enable/disable on port */ ++ if ((new_pool_long == MVPP2_BM_SWF_JUMBO_POOL) && ++ (port->id != port->priv->l4_chksum_jumbo_port)) { ++ dev->features &= ~netif_f_ip_csum; ++ dev->hw_features &= ~netif_f_ip_csum; ++ } else { ++ dev->features |= netif_f_ip_csum; ++ dev->hw_features |= netif_f_ip_csum; ++ } ++ dev->wanted_features = dev->features; ++ netdev_update_features(dev); ++ return 0; ++} ++ ++/* Set defaults to the MVPP2 port */ ++static void mv_pp2x_defaults_set(struct mv_pp2x_port *port) ++{ ++ int tx_port_num, val, queue, ptxq, lrxq; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ /* Configure port to loopback if needed */ ++ if (port->flags & MVPP2_F_LOOPBACK) { ++ if (port->priv->pp2_version == PPV21) ++ mv_pp21_port_loopback_set(port); ++ } ++ ++ /* Disable Legacy WRR, Disable EJP, Release from reset */ ++ tx_port_num = mv_pp2x_egress_port(port); ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, ++ tx_port_num); ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_CMD_1_REG, 0); ++ ++ /* Close bandwidth for all queues */ ++ for (queue = 0; queue < MVPP2_MAX_TXQ; queue++) { ++ ptxq = mv_pp2x_txq_phys(port->id, queue); ++ mv_pp2x_write(hw, ++ MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(ptxq), 0); ++ } ++ ++ /* Set refill period to 1 usec, refill tokens ++ * and bucket size to maximum ++ */ ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PERIOD_REG, ++ hw->tclk / USEC_PER_SEC); ++ val = mv_pp2x_read(hw, MVPP2_TXP_SCHED_REFILL_REG); ++ val &= ~MVPP2_TXP_REFILL_PERIOD_ALL_MASK; ++ val |= MVPP2_TXP_REFILL_PERIOD_MASK(1); ++ val |= MVPP2_TXP_REFILL_TOKENS_ALL_MASK; ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_REFILL_REG, val); ++ val = MVPP2_TXP_TOKEN_SIZE_MAX; ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); ++ ++ /* Set MaximumLowLatencyPacketSize value to 256 */ ++ mv_pp2x_write(hw, MVPP2_RX_CTRL_REG(port->id), ++ MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK | ++ MVPP2_RX_LOW_LATENCY_PKT_SIZE(256)); ++ ++ /* Disable Rx cache snoop */ ++ for (lrxq = 0; lrxq < port->num_rx_queues; lrxq++) { ++ queue = port->rxqs[lrxq]->id; ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(queue)); ++ val |= MVPP2_SNOOP_PKT_SIZE_MASK; ++ val |= MVPP2_SNOOP_BUF_HDR_MASK; ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(queue), val); ++ } ++ ++ /* Enable classifier high queue in forwarding port control*/ ++ val = mv_pp2x_read(hw, MVPP2_CLS_SWFWD_PCTRL_REG); ++ val &= ~(MVPP2_CLS_SWFWD_PCTRL_MASK(port->id)); ++ mv_pp2x_write(hw, MVPP2_CLS_SWFWD_PCTRL_REG, val); ++ ++ /* At default, mask all interrupts to all present cpus */ ++ mv_pp2x_port_interrupts_disable(port); ++} ++ ++static inline void *mv_pp2_extra_pool_get(struct mv_pp2x_port *port, int address_space) ++{ ++ void *ext_buf; ++ struct mv_pp2x_port_pcpu *port_pcpu = port->pcpu[address_space]; ++ struct mv_pp2x_ext_buf_struct *ext_buf_struct; ++ ++ if (!list_empty(&port_pcpu->ext_buf_port_list)) { ++ ext_buf_struct = list_last_entry(&port_pcpu->ext_buf_port_list, ++ struct mv_pp2x_ext_buf_struct, ext_buf_list); ++ list_del(&ext_buf_struct->ext_buf_list); ++ port_pcpu->ext_buf_pool->buf_pool_in_use--; ++ ++ ext_buf = ext_buf_struct->ext_buf_data; ++ ++ } else { ++ ext_buf = kmalloc(MVPP2_EXTRA_BUF_SIZE, GFP_ATOMIC); ++ } ++ ++ return ext_buf; ++} ++ ++static inline int mv_pp2_extra_pool_put(struct mv_pp2x_port *port, void *ext_buf, ++ int cpu) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu = port->pcpu[cpu]; ++ struct mv_pp2x_ext_buf_struct *ext_buf_struct; ++ ++ if (port_pcpu->ext_buf_pool->buf_pool_in_use >= port_pcpu->ext_buf_pool->buf_pool_size) { ++ kfree(ext_buf); ++ return 1; ++ } ++ port_pcpu->ext_buf_pool->buf_pool_in_use++; ++ ++ ext_buf_struct = ++ &port_pcpu->ext_buf_pool->ext_buf_struct[port_pcpu->ext_buf_pool->buf_pool_next_free]; ++ mv_pp2x_extra_pool_inc(port_pcpu->ext_buf_pool); ++ ext_buf_struct->ext_buf_data = ext_buf; ++ ++ list_add(&ext_buf_struct->ext_buf_list, ++ &port_pcpu->ext_buf_port_list); ++ ++ return 0; ++} ++ ++static inline struct sk_buff *mv_pp2_skb_pool_get(struct mv_pp2x_port *port, int address_space) ++{ ++ struct sk_buff *skb; ++ struct mv_pp2x_cp_pcpu *cp_pcpu = port->priv->pcpu[address_space]; ++ struct mv_pp2x_skb_struct *skb_struct; ++ ++ if (!list_empty(&cp_pcpu->skb_port_list)) { ++ skb_struct = list_last_entry(&cp_pcpu->skb_port_list, ++ struct mv_pp2x_skb_struct, skb_list); ++ list_del(&skb_struct->skb_list); ++ cp_pcpu->skb_pool->skb_pool_in_use--; ++ ++ skb = skb_struct->skb; ++ ++ } else { ++ skb = NULL; ++ } ++ ++ return skb; ++} ++ ++static inline int mv_pp2_skb_pool_put(struct mv_pp2x_port *port, struct sk_buff *skb, ++ int cpu) ++{ ++ struct mv_pp2x_cp_pcpu *cp_pcpu = port->priv->pcpu[cpu]; ++ struct mv_pp2x_skb_struct *skb_struct; ++ ++ if (cp_pcpu->skb_pool->skb_pool_in_use >= cp_pcpu->skb_pool->skb_pool_size) { ++ dev_kfree_skb_any(skb); ++ return 1; ++ } ++ cp_pcpu->skb_pool->skb_pool_in_use++; ++ ++ skb_struct = ++ &cp_pcpu->skb_pool->skb_struct[cp_pcpu->skb_pool->skb_pool_next_free]; ++ mv_pp2x_skb_pool_inc(cp_pcpu->skb_pool); ++ ++ skb_struct->skb = skb; ++ ++ list_add(&skb_struct->skb_list, ++ &cp_pcpu->skb_port_list); ++ ++ return 0; ++} ++ ++/* Check if there are enough reserved descriptors for transmission. ++ * If not, request chunk of reserved descriptors and check again. ++ */ ++int mv_pp2x_txq_reserved_desc_num_proc( ++ struct mv_pp2x *priv, ++ struct mv_pp2x_tx_queue *txq, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ int num, int cpu) ++{ ++ int req; ++ ++ if (likely(txq_pcpu->reserved_num >= num)) ++ return 0; ++ ++ /* Not enough descriptors reserved! Update the reserved descriptor ++ * count and check again. ++ */ ++ ++ /* Entire txq_size is used for SWF . Must be changed when HWF ++ * is implemented. ++ * There will always be at least one CHUNK available ++ */ ++ ++ req = MVPP2_CPU_DESC_CHUNK; ++ ++ txq_pcpu->reserved_num += mv_pp2x_txq_alloc_reserved_desc(priv, txq, ++ req, cpu); ++ ++ /* OK, the descriptor cound has been updated: check again. */ ++ if (unlikely(txq_pcpu->reserved_num < num)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++int mv_pp2x_tso_txq_reserved_desc_num_proc( ++ struct mv_pp2x *priv, ++ struct mv_pp2x_tx_queue *txq, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ int num, int cpu) ++{ ++ int req, cpu_desc, desc_count; ++ ++ if (txq_pcpu->reserved_num >= num) ++ return 0; ++ ++ /* Not enough descriptors reserved! Update the reserved descriptor ++ * count and check again. ++ */ ++ ++ /* Entire txq_size is used for SWF . Must be changed when HWF ++ * is implemented. ++ * There will always be at least one CHUNK available ++ */ ++ ++ req = MVPP2_CPU_DESC_CHUNK; ++ ++ if (unlikely((num - txq_pcpu->reserved_num) > req)) { ++ req = num - txq_pcpu->reserved_num; ++ desc_count = 0; ++ /* Compute total of used descriptors */ ++ for (cpu_desc = 0; cpu_desc < mv_pp2x_used_addr_spaces; cpu_desc++) { ++ int txq_count; ++ struct mv_pp2x_txq_pcpu *txq_pcpu_aux; ++ ++ txq_pcpu_aux = &txq->pcpu[cpu_desc]; ++ txq_count = mv_pp2x_txq_count(txq_pcpu_aux); ++ desc_count += txq_count; ++ } ++ desc_count += req; ++ ++ if (unlikely(desc_count > txq->size)) ++ return -ENOMEM; ++ } ++ ++ txq_pcpu->reserved_num += mv_pp2x_txq_alloc_reserved_desc(priv, txq, ++ req, cpu); ++ ++ /* OK, the descriptor cound has been updated: check again. */ ++ if (unlikely(txq_pcpu->reserved_num < num)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/* Avoid wrong tx_done calling for netif_tx_wake at time of ++ * dev-stop or linkDown processing by flag MVPP2_F_IF_TX_ON. ++ * Set/clear it on each cpu. ++ */ ++static void __mv_pp2x_txqs_on(void *arg) ++{ ++ ((struct mv_pp2x_port *)arg)->flags |= MVPP2_F_IF_TX_ON; ++} ++ ++static void __mv_pp2x_txqs_off(void *arg) ++{ ++ ((struct mv_pp2x_port *)arg)->flags &= ~MVPP2_F_IF_TX_ON; ++} ++ ++static inline bool mv_pp2x_tx_stopped(struct mv_pp2x_port *port) ++{ ++ return !(port->flags & MVPP2_F_IF_TX_ON); ++} ++ ++static void mv_pp2x_tx_start_all_queues(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ on_each_cpu(__mv_pp2x_txqs_on, port, 1); ++ netif_tx_start_all_queues(dev); ++} ++ ++static void mv_pp2x_tx_wake_all_queues(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ on_each_cpu(__mv_pp2x_txqs_on, port, 1); ++ netif_tx_wake_all_queues(dev); ++} ++ ++static void mv_pp2x_tx_stop_all_queues(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ on_each_cpu(__mv_pp2x_txqs_off, port, 1); ++ netif_tx_stop_all_queues(dev); ++} ++ ++/* Release the last allocated Tx descriptor. Useful to handle DMA ++ * mapping failures in the Tx path. ++ */ ++ ++/* Free Tx queue skbuffs */ ++static void mv_pp2x_txq_bufs_free(struct mv_pp2x_port *port, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ int num) ++{ ++ int i; ++ ++ for (i = 0; i < num; i++) { ++ dma_addr_t buf_phys_addr = ++ txq_pcpu->tx_buffs[txq_pcpu->txq_get_index]; ++ uintptr_t skb = (uintptr_t)txq_pcpu->tx_skb[txq_pcpu->txq_get_index]; ++ int data_size = txq_pcpu->data_size[txq_pcpu->txq_get_index]; ++ struct sk_buff *skb_rec; ++ ++ txq_pcpu->tx_buffs[txq_pcpu->txq_get_index] = 0; ++ txq_pcpu->tx_skb[txq_pcpu->txq_get_index] = 0; ++ txq_pcpu->data_size[txq_pcpu->txq_get_index] = 0; ++ ++ if (skb & MVPP2_ETH_SHADOW_EXT) { ++ /* Refill TSO external pool */ ++ skb &= ~MVPP2_ETH_SHADOW_EXT; ++ mv_pp2_extra_pool_put(port, (void *)skb, txq_pcpu->cpu); ++ mv_pp2x_txq_inc_get(txq_pcpu); ++ dma_unmap_single(port->dev->dev.parent, buf_phys_addr, ++ data_size, DMA_TO_DEVICE); ++ continue; ++ } else if (skb & MVPP2_ETH_SHADOW_REC) { ++ /* Release skb without data buffer, if data buffer were marked as ++ * recycled in TX routine. ++ */ ++ struct mv_pp2x_bm_pool *bm_pool; ++ struct mv_pp2x_cp_pcpu *cp_pcpu = port->priv->pcpu[txq_pcpu->cpu]; ++ ++ skb &= ~MVPP2_ETH_SHADOW_REC; ++ skb_rec = (struct sk_buff *)skb; ++ bm_pool = &port->priv->bm_pools[MVPP2X_SKB_BPID_GET(skb_rec)]; ++ cp_pcpu->in_use[bm_pool->id]--; ++ /* Do not release buffer of recycled skb */ ++ if (!skb_irq_freeable(skb_rec)) { ++ skb_rec->head = NULL; ++ dev_kfree_skb_any(skb_rec); ++ } else { ++ skb_rec->head = NULL; ++ mv_pp2_skb_pool_put(port, skb_rec, txq_pcpu->cpu); ++ } ++ mv_pp2x_txq_inc_get(txq_pcpu); ++ continue; ++ } ++ ++ dma_unmap_single(port->dev->dev.parent, buf_phys_addr, ++ data_size, DMA_TO_DEVICE); ++ ++ if (skb & MVPP2_ETH_SHADOW_SKB) { ++ skb &= ~MVPP2_ETH_SHADOW_SKB; ++ dev_kfree_skb_any((struct sk_buff *)skb); ++ } ++ mv_pp2x_txq_inc_get(txq_pcpu); ++ } ++} ++ ++static void mv_pp2x_txq_buf_free(struct mv_pp2x_port *port, uintptr_t skb, ++ dma_addr_t buf_phys_addr, int data_size, ++ int cpu) ++{ ++ dma_unmap_single(port->dev->dev.parent, buf_phys_addr, ++ data_size, DMA_TO_DEVICE); ++ ++ if (skb & MVPP2_ETH_SHADOW_EXT) { ++ skb &= ~MVPP2_ETH_SHADOW_EXT; ++ mv_pp2_extra_pool_put(port, (void *)skb, cpu); ++ return; ++ } ++ ++ if (skb & MVPP2_ETH_SHADOW_SKB) { ++ skb &= ~MVPP2_ETH_SHADOW_SKB; ++ dev_kfree_skb_any((struct sk_buff *)skb); ++ } ++} ++ ++/* Handle end of transmission */ ++static void mv_pp2x_txq_done(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_queue *txq, ++ struct mv_pp2x_txq_pcpu *txq_pcpu) ++{ ++ struct netdev_queue *nq = netdev_get_tx_queue(port->dev, (txq->log_id + ++ (txq_pcpu->cpu * mv_pp2x_txq_number))); ++ int tx_done; ++ ++#ifdef DEV_NETMAP ++ if (port->flags & MVPP2_F_IFCAP_NETMAP) { ++ if (netmap_tx_irq(port->dev, 0)) ++ return; /* cleaned ok */ ++ } ++#endif /* DEV_NETMAP */ ++ ++ tx_done = mv_pp2x_txq_sent_desc_proc(port, txq_pcpu->cpu, txq->id); ++ if (!tx_done) ++ return; ++ ++ mv_pp2x_txq_bufs_free(port, txq_pcpu, tx_done); ++ ++ if (netif_tx_queue_stopped(nq) && !mv_pp2x_tx_stopped(port)) ++ if (mv_pp2x_txq_free_count(txq_pcpu) >= port->txq_stop_limit) ++ netif_tx_wake_queue(nq); ++} ++ ++/* The Guard fixer, called for 2 opposite actions: ++ * Activate fix by set frame-coalescing to Zero (according to_zero_map) ++ * which forces the tx-done IRQ. Called by guard tasklet. ++ * Deactivate fixer ~ restore the coal-configration (to_zero_map=0) ++ * when/by tx-done activated. ++ */ ++static void mv_pp2x_tx_done_guard_force_irq(struct mv_pp2x_port *port, ++ int address_space, u8 to_zero_map) ++{ ++ int q; ++ u32 val, coal, qmask, xor; ++ struct mv_pp2x_hw *hw; ++ struct mv_pp2x_port_pcpu *port_pcpu = port->pcpu[address_space]; ++ ++ if (port_pcpu->txq_coal_is_zero_map == to_zero_map) ++ return; /* all current & requested are already the same */ ++ ++ hw = &port->priv->hw; ++ xor = port_pcpu->txq_coal_is_zero_map ^ to_zero_map; ++ /* Configuration num-of-frames coalescing is the same for all queues */ ++ coal = port->txqs[0]->pkts_coal << MVPP2_TRANSMITTED_THRESH_OFFSET; ++ ++ for (q = 0; q < port->num_tx_queues; q++) { ++ qmask = 1 << q; ++ if (!(xor & qmask)) ++ continue; ++ if (to_zero_map & qmask) { ++ port->tx_guard_trigger++; ++ val = 0; /* Set ZERO forcing the Interrupt */ ++ } else { ++ val = coal; /* Set/restore configured threshold */ ++ } ++ mv_pp22_thread_write(hw, address_space, MVPP2_TXQ_NUM_REG, port->txqs[q]->id); ++ mv_pp22_thread_write(hw, address_space, MVPP2_TXQ_THRESH_REG, val); ++ } ++ port_pcpu->txq_coal_is_zero_map = to_zero_map; ++} ++ ++static unsigned int mv_pp2x_tx_done(struct mv_pp2x_port *port, u32 cause, ++ int address_space) ++{ ++ struct mv_pp2x_tx_queue *txq; ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ unsigned int tx_todo = 0; ++ ++ /* Set/Restore "no-force" */ ++ mv_pp2x_tx_done_guard_force_irq(port, address_space, 0); ++ ++ while (cause) { ++ int txq_count; ++ ++ txq = mv_pp2x_get_tx_queue(port, cause); ++ if (!txq) ++ break; ++ ++ txq_pcpu = &txq->pcpu[address_space]; ++ txq_count = mv_pp2x_txq_count(txq_pcpu); ++ ++ if (txq_count) { ++ mv_pp2x_txq_done(port, txq, txq_pcpu); ++ /*Recalc after tx_done*/ ++ txq_count = mv_pp2x_txq_count(txq_pcpu); ++ ++ tx_todo += txq_count; ++ } ++ ++ cause &= ~(1 << txq->log_id); ++ } ++ return tx_todo; ++} ++ ++/* Rx/Tx queue initialization/cleanup methods */ ++ ++/* Allocate and initialize descriptors for aggr TXQ */ ++static int mv_pp2x_aggr_txq_init(struct platform_device *pdev, ++ struct mv_pp2x_aggr_tx_queue *aggr_txq, ++ int desc_num, int cpu, ++ struct mv_pp2x *priv) ++{ ++ struct mv_pp2x_hw *hw = &priv->hw; ++ dma_addr_t first_desc_phy; ++ ++ /* Allocate memory for TX descriptors, ensure it can be 512B aligned. */ ++ aggr_txq->desc_mem = dma_zalloc_coherent(&pdev->dev, ++ MVPP2_DESCQ_MEM_SIZE(desc_num), ++ &aggr_txq->descs_phys, GFP_KERNEL); ++ if (!aggr_txq->desc_mem) ++ return -ENOMEM; ++ ++ aggr_txq->first_desc = (struct mv_pp2x_tx_desc *) ++ MVPP2_DESCQ_MEM_ALIGN((uintptr_t)aggr_txq->desc_mem); ++ first_desc_phy = MVPP2_DESCQ_MEM_ALIGN(aggr_txq->descs_phys); ++ ++ pr_debug("first_desc=%p, desc_mem=%p\n", ++ aggr_txq->desc_mem, aggr_txq->first_desc); ++ ++ aggr_txq->last_desc = aggr_txq->size - 1; ++ ++ /* Aggr TXQ no reset WA */ ++ aggr_txq->next_desc_to_proc = mv_pp2x_read(hw, ++ MVPP2_AGGR_TXQ_INDEX_REG(cpu)); ++ ++ /* Set Tx descriptors queue starting address ++ * indirect access ++ */ ++ mv_pp2x_write(hw, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), ++ first_desc_phy >> ++ priv->pp2xdata->hw.desc_queue_addr_shift); ++ mv_pp2x_write(hw, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); ++ ++ /* Init aggregated locks if needed */ ++ if (priv->pp2_cfg.spinlocks_bitmap & MV_AGGR_QUEUE_LOCK) ++ spin_lock_init(&aggr_txq->spinlock); ++ ++ return 0; ++} ++ ++/* Routine set the number of non-occupied descriptors threshold that change ++ * interrupt error cause polled by FW Flow Control ++ */ ++void mv_pp2x_set_rxq_free_tresh(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq) ++{ ++ u32 val; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_RXQ_NUM_REG, rxq->id); ++ ++ val = mv_pp2x_read(&port->priv->hw, MVPP2_RXQ_THRESH_REG); ++ val &= ~MVPP2_NON_OCCUPIED_THRESH_MASK; ++ val |= MSS_CP_CM3_THRESHOLD_STOP << MVPP2_NON_OCCUPIED_THRESH_OFFSET; ++ mv_pp2x_write(&port->priv->hw, MVPP2_RXQ_THRESH_REG, val); ++} ++ ++/* Create a specified Rx queue */ ++static int mv_pp2x_rxq_init(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ dma_addr_t first_desc_phy; ++ ++ rxq->size = port->rx_ring_size; ++ ++ /* Allocate memory for RX descriptors */ ++ rxq->desc_mem = dma_alloc_coherent(port->dev->dev.parent, ++ MVPP2_DESCQ_MEM_SIZE(rxq->size), ++ &rxq->descs_phys, GFP_KERNEL); ++ if (!rxq->desc_mem) ++ return -ENOMEM; ++ ++ rxq->first_desc = (struct mv_pp2x_rx_desc *) ++ MVPP2_DESCQ_MEM_ALIGN((uintptr_t)rxq->desc_mem); ++ first_desc_phy = MVPP2_DESCQ_MEM_ALIGN(rxq->descs_phys); ++ ++ rxq->last_desc = rxq->size - 1; ++ ++ /* Zero occupied and non-occupied counters - direct access */ ++ mv_pp2x_write(hw, MVPP2_RXQ_STATUS_REG(rxq->id), 0); ++ ++ /* Set Rx descriptors queue starting address - indirect access */ ++ mv_pp2x_write(hw, MVPP2_RXQ_NUM_REG, rxq->id); ++ ++ mv_pp2x_write(hw, MVPP2_RXQ_DESC_ADDR_REG, (first_desc_phy >> ++ port->priv->pp2xdata->hw.desc_queue_addr_shift)); ++ mv_pp2x_write(hw, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); ++ mv_pp2x_write(hw, MVPP2_RXQ_INDEX_REG, 0); ++ ++ /* Set Offset */ ++ mv_pp2x_rxq_offset_set(port, rxq->id, NET_SKB_PAD); ++ ++ /* Set coalescing pkts and time */ ++ mv_pp2x_rx_pkts_coal_set(port, rxq); ++ mv_pp2x_rx_time_coal_set(port, rxq); ++ ++ /* Set the number of non occupied descriptors threshold */ ++ mv_pp2x_set_rxq_free_tresh(port, rxq); ++ ++ /* Add number of descriptors ready for receiving packets */ ++ mv_pp2x_rxq_status_update(port, rxq->id, 0, rxq->size); ++ ++ return 0; ++} ++ ++/* Push packets received by the RXQ to BM pool */ ++static void mv_pp2x_rxq_drop_pkts(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq) ++{ ++ int rx_received, i, cpu; ++ u8 *buf_cookie; ++ dma_addr_t buf_phys_addr; ++ struct mv_pp2x_bm_pool *bm_pool; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ ++ preempt_disable(); ++ rx_received = mv_pp2x_rxq_received(port, rxq->id); ++ preempt_enable(); ++ if (!rx_received) ++ return; ++ ++ cpu = get_cpu(); ++ cp_pcpu = port->priv->pcpu[cpu]; ++ for (i = 0; i < rx_received; i++) { ++ struct mv_pp2x_rx_desc *rx_desc = ++ mv_pp2x_rxq_next_desc_get(rxq); ++ ++#if defined(__BIG_ENDIAN) ++ if (port->priv->pp2_version == PPV21) ++ mv_pp21_rx_desc_swap(rx_desc); ++ else ++ mv_pp22_rx_desc_swap(rx_desc); ++#endif /* __BIG_ENDIAN */ ++ ++ bm_pool = &port->priv->bm_pools[MVPP2_RX_DESC_POOL(rx_desc)]; ++ ++ if (port->priv->pp2_version == PPV21) ++ buf_phys_addr = mv_pp21_rxdesc_phys_addr_get(rx_desc); ++ else ++ buf_phys_addr = mv_pp22_rxdesc_phys_addr_get(rx_desc); ++ ++ buf_cookie = phys_to_virt(dma_to_phys(port->dev->dev.parent, buf_phys_addr)); ++ ++ mv_pp2x_pool_refill(port->priv, MVPP2_RX_DESC_POOL(rx_desc), ++ buf_phys_addr, cpu); ++ cp_pcpu->in_use[bm_pool->id]--; ++ } ++ put_cpu(); ++ mv_pp2x_rxq_status_update(port, rxq->id, rx_received, rx_received); ++} ++ ++/* Cleanup Rx queue */ ++static void mv_pp2x_rxq_deinit(struct mv_pp2x_port *port, ++ struct mv_pp2x_rx_queue *rxq) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ mv_pp2x_rxq_drop_pkts(port, rxq); ++ ++ if (rxq->desc_mem) ++ dma_free_coherent(port->dev->dev.parent, ++ MVPP2_DESCQ_MEM_SIZE(rxq->size), ++ rxq->desc_mem, ++ rxq->descs_phys); ++ ++ rxq->first_desc = NULL; ++ rxq->desc_mem = NULL; ++ rxq->last_desc = 0; ++ rxq->next_desc_to_proc = 0; ++ rxq->descs_phys = 0; ++ ++ /* Clear Rx descriptors queue starting address and size; ++ * free descriptor number ++ */ ++ mv_pp2x_write(hw, MVPP2_RXQ_STATUS_REG(rxq->id), 0); ++ mv_pp2x_write(hw, MVPP2_RXQ_NUM_REG, rxq->id); ++ mv_pp2x_write(hw, MVPP2_RXQ_DESC_ADDR_REG, 0); ++ mv_pp2x_write(hw, MVPP2_RXQ_DESC_SIZE_REG, 0); ++} ++ ++/* Create and initialize a Tx queue */ ++static int mv_pp2x_txq_init(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_queue *txq) ++{ ++ u32 val; ++ int cpu, desc, desc_per_txq, tx_port_num; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ dma_addr_t first_desc_phy; ++ ++ txq->size = port->tx_ring_size; ++ ++ /* Allocate memory for Tx descriptors */ ++ txq->desc_mem = dma_alloc_coherent(port->dev->dev.parent, ++ MVPP2_DESCQ_MEM_SIZE(txq->size), ++ &txq->descs_phys, GFP_KERNEL); ++ if (!txq->desc_mem) ++ return -ENOMEM; ++ ++ txq->first_desc = (struct mv_pp2x_tx_desc *) ++ MVPP2_DESCQ_MEM_ALIGN((uintptr_t)txq->desc_mem); ++ first_desc_phy = MVPP2_DESCQ_MEM_ALIGN(txq->descs_phys); ++ ++ txq->last_desc = txq->size - 1; ++ ++ /* Set Tx descriptors queue starting address - indirect access */ ++ mv_pp2x_write(hw, MVPP2_TXQ_NUM_REG, txq->id); ++ mv_pp2x_write(hw, MVPP2_TXQ_DESC_ADDR_LOW_REG, ++ first_desc_phy >> MVPP2_TXQ_DESC_ADDR_LOW_SHIFT); ++ mv_pp2x_write(hw, MVPP2_TXQ_DESC_SIZE_REG, ++ txq->size & MVPP2_TXQ_DESC_SIZE_MASK); ++ mv_pp2x_write(hw, MVPP2_TXQ_INDEX_REG, 0); ++ mv_pp2x_write(hw, MVPP2_TXQ_RSVD_CLR_REG, ++ txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); ++ val = mv_pp2x_read(hw, MVPP2_TXQ_PENDING_REG); ++ val &= ~MVPP2_TXQ_PENDING_MASK; ++ mv_pp2x_write(hw, MVPP2_TXQ_PENDING_REG, val); ++ ++ /* Calculate base address in prefetch buffer. We reserve 16 descriptors ++ * for each existing TXQ. ++ * TCONTS for PON port must be continuous from 0 to MVPP2_MAX_TCONT ++ * GBE ports assumed to be continious from 0 to MVPP2_MAX_PORTS ++ */ ++ desc_per_txq = 16; ++ desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) + ++ (txq->log_id * desc_per_txq); ++ ++ mv_pp2x_write(hw, MVPP2_TXQ_PREF_BUF_REG, ++ MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | ++ MVPP2_PREF_BUF_THRESH(desc_per_txq / 2)); ++ ++ /* WRR / EJP configuration - indirect access */ ++ tx_port_num = mv_pp2x_egress_port(port); ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); ++ ++ val = mv_pp2x_read(hw, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id)); ++ val &= ~MVPP2_TXQ_REFILL_PERIOD_ALL_MASK; ++ val |= MVPP2_TXQ_REFILL_PERIOD_MASK(1); ++ val |= MVPP2_TXQ_REFILL_TOKENS_ALL_MASK; ++ mv_pp2x_write(hw, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id), val); ++ ++ val = MVPP2_TXQ_TOKEN_SIZE_MAX; ++ mv_pp2x_write(hw, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq->log_id), ++ val); ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ txq_pcpu = &txq->pcpu[cpu]; ++ txq_pcpu->size = txq->size; ++ txq_pcpu->tx_skb = kcalloc(txq_pcpu->size, ++ sizeof(*txq_pcpu->tx_skb), ++ GFP_KERNEL); ++ if (!txq_pcpu->tx_skb) ++ goto error; ++ ++ txq_pcpu->tx_buffs = kcalloc(txq_pcpu->size, ++ sizeof(dma_addr_t), GFP_KERNEL); ++ if (!txq_pcpu->tx_buffs) ++ goto error; ++ ++ txq_pcpu->data_size = kcalloc(txq_pcpu->size, ++ sizeof(int), GFP_KERNEL); ++ if (!txq_pcpu->data_size) ++ goto error; ++ ++ txq_pcpu->reserved_num = 0; ++ txq_pcpu->txq_put_index = 0; ++ txq_pcpu->txq_get_index = 0; ++ } ++ ++ return 0; ++ ++error: ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ txq_pcpu = &txq->pcpu[cpu]; ++ kfree(txq_pcpu->tx_skb); ++ kfree(txq_pcpu->tx_buffs); ++ kfree(txq_pcpu->data_size); ++ } ++ ++ dma_free_coherent(port->dev->dev.parent, ++ MVPP2_DESCQ_MEM_SIZE(txq->size), ++ txq->first_desc, txq->descs_phys); ++ ++ return -ENOMEM; ++} ++ ++/* Free allocated TXQ resources */ ++static void mv_pp2x_txq_deinit(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_queue *txq) ++{ ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int cpu; ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ preempt_disable(); ++ txq_pcpu = &txq->pcpu[cpu]; ++ kfree(txq_pcpu->tx_skb); ++ kfree(txq_pcpu->tx_buffs); ++ kfree(txq_pcpu->data_size); ++ preempt_enable(); ++ } ++ ++ if (txq->desc_mem) ++ dma_free_coherent(port->dev->dev.parent, ++ MVPP2_DESCQ_MEM_SIZE(txq->size), ++ txq->desc_mem, txq->descs_phys); ++ ++ txq->desc_mem = NULL; ++ txq->first_desc = NULL; ++ txq->last_desc = 0; ++ txq->next_desc_to_proc = 0; ++ txq->descs_phys = 0; ++ ++ /* Set minimum bandwidth for disabled TXQs */ ++ mv_pp2x_write(hw, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->id), 0); ++ ++ /* Set Tx descriptors queue starting address and size */ ++ mv_pp2x_write(hw, MVPP2_TXQ_NUM_REG, txq->id); ++ mv_pp2x_write(hw, MVPP2_TXQ_DESC_ADDR_LOW_REG, 0); ++ mv_pp2x_write(hw, MVPP2_TXQ_DESC_SIZE_REG, 0); ++} ++ ++/* Cleanup Tx ports */ ++static void mv_pp2x_txq_clean(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_queue *txq) ++{ ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ int delay, pending, cpu; ++ u32 val; ++ bool egress_en = false; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int tx_port_num = mv_pp2x_egress_port(port); ++ ++ mv_pp2x_write(hw, MVPP2_TXQ_NUM_REG, txq->id); ++ val = mv_pp2x_read(hw, MVPP2_TXQ_PREF_BUF_REG); ++ val |= MVPP2_TXQ_DRAIN_EN_MASK; ++ mv_pp2x_write(hw, MVPP2_TXQ_PREF_BUF_REG, val); ++ ++ /* The napi queue has been stopped so wait for all packets ++ * to be transmitted. ++ */ ++ ++ /* Enable egress queue in order to allow releasing all packets*/ ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); ++ val = mv_pp2x_read(hw, MVPP2_TXP_SCHED_Q_CMD_REG); ++ if (!(val & (1 << txq->log_id))) { ++ val |= 1 << txq->log_id; ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_Q_CMD_REG, val); ++ egress_en = true; ++ } ++ ++ delay = 0; ++ do { ++ if (delay >= MVPP2_TX_PENDING_TIMEOUT_MSEC) { ++ netdev_warn(port->dev, ++ "port %d: cleaning queue %d timed out\n", ++ port->id, txq->log_id); ++ break; ++ } ++ mdelay(1); ++ delay++; ++ ++ pending = mv_pp2x_txq_pend_desc_num_get(port, txq); ++ } while (pending); ++ ++ /* Disable egress queue */ ++ if (egress_en) { ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); ++ val = (mv_pp2x_read(hw, MVPP2_TXP_SCHED_Q_CMD_REG)) & ++ MVPP2_TXP_SCHED_ENQ_MASK; ++ val |= 1 << txq->log_id; ++ mv_pp2x_write(hw, MVPP2_TXP_SCHED_Q_CMD_REG, ++ (val << MVPP2_TXP_SCHED_DISQ_OFFSET)); ++ egress_en = false; ++ } ++ ++ val &= ~MVPP2_TXQ_DRAIN_EN_MASK; ++ mv_pp2x_write(hw, MVPP2_TXQ_PREF_BUF_REG, val); ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ int txq_count; ++ ++ preempt_disable(); ++ txq_pcpu = &txq->pcpu[cpu]; ++ ++ /* Release all packets */ ++ txq_count = mv_pp2x_txq_count(txq_pcpu); ++ mv_pp2x_txq_bufs_free(port, txq_pcpu, txq_count); ++ ++ /* Reset queue */ ++ txq_pcpu->txq_put_index = 0; ++ txq_pcpu->txq_get_index = 0; ++ preempt_enable(); ++ } ++} ++ ++/* Cleanup all Tx queues */ ++void mv_pp2x_cleanup_txqs(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_tx_queue *txq; ++ int queue, cpu; ++ u32 val; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ val = mv_pp2x_read(hw, MVPP2_TX_PORT_FLUSH_REG); ++ ++ /* Reset Tx ports and delete Tx queues */ ++ val |= MVPP2_TX_PORT_FLUSH_MASK(port->id); ++ mv_pp2x_write(hw, MVPP2_TX_PORT_FLUSH_REG, val); ++ ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ txq = port->txqs[queue]; ++ mv_pp2x_txq_clean(port, txq); ++ mv_pp2x_txq_deinit(port, txq); ++ } ++ ++ /* Mvpp21 and Mvpp22&23 has different per cpu register access. ++ * Mvpp21 - to access CPUx should run on CPUx ++ * Mvpp22&23 - CPUy can access CPUx from CPUx address space ++ * If added to support CPU hot plug feature supported only by Mvpp22 ++ */ ++ if (port->priv->pp2_version != PPV21) ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) ++ for (queue = 0; queue < port->num_tx_queues; queue++) ++ mv_pp2x_txq_sent_desc_proc(port, cpu, port->txqs[queue]->id); ++ else ++ on_each_cpu(mv_pp2x_txq_sent_counter_clear, port, 1); ++ ++ val &= ~MVPP2_TX_PORT_FLUSH_MASK(port->id); ++ mv_pp2x_write(hw, MVPP2_TX_PORT_FLUSH_REG, val); ++} ++ ++/* Routine calculate single queue shares address space */ ++static int mv_pp22_calc_shared_addr_space(struct mv_pp2x_port *port) ++{ ++ /* If number of CPU's greater or higher than number of CPU's, return last address space */ ++ if (num_active_cpus() >= MVPP2_MAX_ADDR_SPACES) ++ return MVPP2_MAX_ADDR_SPACES - 1; ++ ++ return num_active_cpus(); ++} ++ ++/* Routine enable flow control for RXQs conditon */ ++void mv_pp2x_rxq_enable_fc(struct mv_pp2x_port *port) ++{ ++ int val, cm3_state, host_id, queue; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->priv->mss_spinlock, flags); ++ ++ /* Remove Flow control enable bit to prevent race between FW and Kernel ++ * If Flow control were enabled, it would be re-enabled. ++ */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_FC_COM_REG); ++ cm3_state = (val & FLOW_CONTROL_ENABLE_BIT); ++ val &= ~FLOW_CONTROL_ENABLE_BIT; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_FC_COM_REG, val); ++ ++ /* Set same Flow control for all RXQs */ ++ for (queue = 0; queue < port->num_rx_queues; queue++) { ++ /* Set stop and start Flow control RXQ thresholds */ ++ val = MSS_CP_CM3_THRESHOLD_START; ++ val |= (MSS_CP_CM3_THRESHOLD_STOP << MSS_CP_CM3_RXQ_TRESH_STOP_OFFS); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_CM3_RXQ_TRESH_BASE + ((queue + port->first_rxq) ++ * MSS_CP_CM3_RXQ_TRESH_OFFS), val); ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_CM3_RXQ_ASS_REG(queue, port->first_rxq)); ++ /* Set RXQ port ID */ ++ val &= ~(MSS_CP_CM3_RXQ_ASS_PORTID_MASK << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) ++ * MSS_CP_CM3_RXQ_ASS_PER_OFFS)); ++ val |= (port->id << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) ++ * MSS_CP_CM3_RXQ_ASS_PER_OFFS)); ++ val &= ~(MSS_CP_CM3_RXQ_ASS_HOSTID_MASK << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) ++ * MSS_CP_CM3_RXQ_ASS_PER_OFFS + MSS_CP_CM3_RXQ_ASS_HOSTID_OFFS)); ++ ++ /* Calculate RXQ host ID: ++ * In Single queue mode: Host ID equal to Host ID used for shared RX interrupt ++ * In Multi queue mode: Host ID equal to number of RXQ ID / number of CoS queues ++ * In Single resource mode: Host ID always equal to 0 ++ */ ++ if (mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) ++ host_id = mv_pp22_calc_shared_addr_space(port); ++ else if (mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) ++ host_id = queue / mv_pp2x_num_cos_queues; ++ else ++ host_id = 0; ++ ++ /* Set RXQ host ID */ ++ val |= (host_id << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) ++ * MSS_CP_CM3_RXQ_ASS_PER_OFFS + MSS_CP_CM3_RXQ_ASS_HOSTID_OFFS)); ++ ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_CM3_RXQ_ASS_REG(queue, port->first_rxq), val); ++ } ++ ++ /* Notify Firmware that Flow control config space ready for update */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_FC_COM_REG); ++ val |= FLOW_CONTROL_UPDATE_COMMAND_BIT; ++ val |= cm3_state; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_FC_COM_REG, val); ++ ++ spin_unlock_irqrestore(&port->priv->mss_spinlock, flags); ++} ++ ++/* Routine disable flow control for RXQs conditon */ ++void mv_pp2x_rxq_disable_fc(struct mv_pp2x_port *port) ++{ ++ int val, cm3_state, queue; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->priv->mss_spinlock, flags); ++ ++ /* Remove Flow control enable bit to prevent race between FW and Kernel ++ * If Flow control were enabled, it would be re-enabled. ++ */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_FC_COM_REG); ++ cm3_state = (val & FLOW_CONTROL_ENABLE_BIT); ++ val &= ~FLOW_CONTROL_ENABLE_BIT; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_FC_COM_REG, val); ++ ++ /* Disable Flow control for all RXQs */ ++ for (queue = 0; queue < port->num_rx_queues; queue++) { ++ /* Set threshold 0 to disable Flow control */ ++ val = 0; ++ val |= (0 << MSS_CP_CM3_RXQ_TRESH_STOP_OFFS); ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_CM3_RXQ_TRESH_BASE + ((queue + port->first_rxq) ++ * MSS_CP_CM3_RXQ_TRESH_OFFS), val); ++ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_CM3_RXQ_ASS_REG(queue, port->first_rxq)); ++ ++ val &= ~(MSS_CP_CM3_RXQ_ASS_PORTID_MASK << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) ++ * MSS_CP_CM3_RXQ_ASS_PER_OFFS)); ++ val |= (0 << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) * MSS_CP_CM3_RXQ_ASS_PER_OFFS)); ++ val &= ~(MSS_CP_CM3_RXQ_ASS_HOSTID_MASK << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) ++ * MSS_CP_CM3_RXQ_ASS_PER_OFFS + MSS_CP_CM3_RXQ_ASS_HOSTID_OFFS)); ++ ++ val |= (0 << (((queue + port->first_rxq) % MSS_CP_CM3_RXQ_ASS_PER_REG) * MSS_CP_CM3_RXQ_ASS_PER_OFFS ++ + MSS_CP_CM3_RXQ_ASS_HOSTID_OFFS)); ++ ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_CM3_RXQ_ASS_REG(queue, port->first_rxq), val); ++ } ++ ++ /* Notify Firmware that Flow control config space ready for update */ ++ val = mv_pp2x_cm3_read(&port->priv->hw, MSS_CP_FC_COM_REG); ++ val |= FLOW_CONTROL_UPDATE_COMMAND_BIT; ++ val |= cm3_state; ++ mv_pp2x_cm3_write(&port->priv->hw, MSS_CP_FC_COM_REG, val); ++ ++ spin_unlock_irqrestore(&port->priv->mss_spinlock, flags); ++} ++ ++/* Cleanup all Rx queues */ ++void mv_pp2x_cleanup_rxqs(struct mv_pp2x_port *port) ++{ ++ int queue; ++ ++ if (port->flow_control) ++ mv_pp2x_rxq_disable_fc(port); ++ ++ for (queue = 0; queue < port->num_rx_queues; queue++) ++ mv_pp2x_rxq_deinit(port, port->rxqs[queue]); ++} ++ ++/* Init all Rx queues for port */ ++int mv_pp2x_setup_rxqs(struct mv_pp2x_port *port) ++{ ++ int queue, err; ++ ++ for (queue = 0; queue < port->num_rx_queues; queue++) { ++ err = mv_pp2x_rxq_init(port, port->rxqs[queue]); ++ if (err) ++ goto err_cleanup; ++ } ++ ++ if (port->flow_control) ++ mv_pp2x_rxq_enable_fc(port); ++ return 0; ++ ++err_cleanup: ++ mv_pp2x_cleanup_rxqs(port); ++ return err; ++} ++ ++/* Init all tx queues for port */ ++int mv_pp2x_setup_txqs(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_tx_queue *txq; ++ int queue, err, cpu; ++ ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ txq = port->txqs[queue]; ++ err = mv_pp2x_txq_init(port, txq); ++ if (err) ++ goto err_cleanup; ++ } ++ if (port->interrupt_tx_done) { ++ /* Set time-coalescing. The pkts_coal is set by start_dev */ ++ mv_pp2x_tx_done_time_coal_set(port, port->tx_time_coal); ++ } ++ ++ /* Mvpp21 and Mvpp22&23 has different per cpu register access. ++ * Mvpp21 - to access CPUx should run on CPUx ++ * Mvpp22&23 - CPUy can access CPUx from CPUx address space ++ * If added to support CPU hot plug feature supported only by Mvpp22 ++ */ ++ ++ if (port->priv->pp2_version != PPV21) ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) ++ for (queue = 0; queue < port->num_tx_queues; queue++) ++ mv_pp2x_txq_sent_desc_proc(port, cpu, port->txqs[queue]->id); ++ else ++ on_each_cpu(mv_pp2x_txq_sent_counter_clear, port, 1); ++ ++ return 0; ++ ++err_cleanup: ++ mv_pp2x_cleanup_txqs(port); ++ return err; ++} ++ ++void mv_pp2x_cleanup_irqs(struct mv_pp2x_port *port) ++{ ++ int qvec; ++ ++ /* Rx/TX irq's */ ++ for (qvec = 0; qvec < port->num_qvector; qvec++) { ++ irq_set_affinity_hint(port->q_vector[qvec].irq, NULL); ++ free_irq(port->q_vector[qvec].irq, &port->q_vector[qvec]); ++ } ++ ++ /* Link irq */ ++ if (port->priv->pp2_version != PPV21) ++ free_irq(port->mac_data.link_irq, port); ++} ++ ++void mv_pp22_cleanup_uio_irqs(struct mv_pp2x_port *port) ++{ ++ int qvec; ++ ++ /* Rx irq's */ ++ for (qvec = 0; qvec < port->uio.num_qvector; qvec++) ++ free_irq(port->uio.q_vector[qvec].irq, &port->uio.q_vector[qvec]); ++} ++ ++void mv_pp2x_interrupts_set_mask(struct queue_vector *q_vec, u32 queue_mask) ++{ ++ struct mv_pp2x_port *port = q_vec->parent; ++ ++ mv_pp22_thread_write(&port->priv->hw, q_vec->sw_thread_id, MVPP2_ISR_RX_TX_MASK_REG(port->id), ++ queue_mask); ++} ++ ++void mv_pp2x_tx_done_pkts_coal_set_all(struct mv_pp2x_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < mv_pp2x_used_addr_spaces; i++) ++ mv_pp2x_tx_done_pkts_coal_set(port, i); ++} ++ ++/** ++ * \brief wrapper for calling napi_schedule ++ * @param param parameters to pass to napi_schedule ++ * ++ * Used when scheduling on different CPUs ++ */ ++static void napi_schedule_wrapper(void *param) ++{ ++ struct napi_struct *napi = param; ++ ++ napi_schedule(napi); ++} ++ ++#ifdef CONFIG_UIO ++static irqreturn_t mv_pp22_uio_isr(int irq, void *dev_id) ++{ ++ u32 cause_rx, cause_rx_tx_mask; ++ struct uio_queue_vector *q_vec = (struct uio_queue_vector *)dev_id; ++ struct mv_pp2x_port *port = q_vec->parent; ++ ++ cause_rx = mv_pp22_thread_relaxed_read(&port->priv->hw, q_vec->sw_thread_id, ++ MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); ++ cause_rx &= MVPP22_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ ++ cause_rx_tx_mask = mv_pp22_thread_relaxed_read(&port->priv->hw, q_vec->sw_thread_id, ++ MVPP2_ISR_RX_TX_MASK_REG(port->id)); ++ ++ /* Mask any raised queues mutual to cause, and the q_vec's mask */ ++ cause_rx &= (q_vec->queue_mask >> q_vec->first_rx_queue); ++ cause_rx_tx_mask &= ~cause_rx; ++ ++ pr_debug("irq:%s cause_rx:0x%x, cause_rx_tx_mask:0x%x\n", q_vec->irq_name, cause_rx, cause_rx_tx_mask); ++ mv_pp22_thread_write(&port->priv->hw, q_vec->sw_thread_id, ++ MVPP2_ISR_RX_TX_MASK_REG(port->id), cause_rx_tx_mask); ++ uio_event_notify(&q_vec->parent->uio.u_info); ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++/* The callback for per-q_vector interrupt */ ++static irqreturn_t mv_pp2x_isr(int irq, void *dev_id) ++{ ++ struct queue_vector *q_vec = (struct queue_vector *)dev_id; ++ ++ /* In single mode only first sub queue vector do RX and TX done interrupt routine ++ * so its save to disable Interrupt. ++ * In multi queue mode only if CPU's share same interrupt, cause mask related to this ++ * CPU queues and NAPI scheduled on this CPU. ++ */ ++ if (!q_vec->num_of_sub_vectors || mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) { ++ mv_pp2x_qvector_interrupt_disable(q_vec); ++ napi_schedule(&q_vec->sub_vec[0]->napi); ++ } else { ++ u32 cause_rx_tx; ++ int i; ++ unsigned long flags = 0; ++ struct mv_pp2x_port *port = q_vec->parent; ++ ++ /* Check interrupt cause and which CPU should handle NAPI */ ++ cause_rx_tx = mv_pp22_thread_relaxed_read(&port->priv->hw, q_vec->sw_thread_id, ++ MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); ++ for (i = 0; i < q_vec->num_of_sub_vectors; i++) { ++ /* Find which sub queue vector should handle Interrupt cause and ++ * remove this cause from queue vector cause. ++ * It's done to protect scheduling of NAPI twice due to same cause. ++ */ ++ if ((cause_rx_tx & q_vec->sub_vec[i]->queue_mask) && ++ (q_vec->queue_mask & q_vec->sub_vec[i]->queue_mask)) { ++ spin_lock_irqsave(&q_vec->hw_mask_lock, flags); ++ /* HW_MASK shadow kept in SW, faster for Read-Modify-Write */ ++ q_vec->queue_mask &= (~q_vec->sub_vec[i]->queue_mask); ++ mv_pp2x_interrupts_set_mask(q_vec, q_vec->queue_mask); ++ spin_unlock_irqrestore(&q_vec->hw_mask_lock, flags); ++ } else { ++ continue; ++ } ++ ++ if (q_vec->sub_vec[i]->cpu_id == smp_processor_id()) { ++ napi_schedule(&q_vec->sub_vec[i]->napi); ++ } else { ++ struct call_single_data *csd = &q_vec->sub_vec[i]->csd; ++ ++ csd->func = napi_schedule_wrapper; ++ csd->info = &q_vec->sub_vec[i]->napi; ++ csd->flags = 0; ++ smp_call_function_single_async(q_vec->sub_vec[i]->cpu_id, csd); ++ } ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t mv_pp2_link_change_isr(int irq, void *data) ++{ ++ struct mv_pp2x_port *port = (struct mv_pp2x_port *)data; ++ ++ pr_debug("%s cpu_id(%d) irq(%d) pp_port(%d)\n", __func__, ++ smp_processor_id(), irq, port->id); ++ if (port->priv->pp2_version != PPV21) { ++ /* mask all events from this mac */ ++ mv_gop110_port_events_mask(&port->priv->hw.gop, &port->mac_data); ++ /* read cause register to clear event */ ++ mv_gop110_port_events_clear(&port->priv->hw.gop, &port->mac_data); ++ } ++ ++ if (!port->mac_data.phy_dev) ++ tasklet_schedule(&port->link_change_tasklet); ++ ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_UIO ++int mv_pp22_setup_uio_irqs(struct net_device *dev, struct mv_pp2x_port *port) ++{ ++ int qvec_id, err; ++ struct uio_queue_vector *qvec; ++ ++ for (qvec_id = 0; qvec_id < port->uio.num_qvector; qvec_id++) { ++ qvec = &port->uio.q_vector[qvec_id]; ++ if (!qvec->irq) ++ continue; ++ err = request_irq(qvec->irq, mv_pp22_uio_isr, 0, ++ qvec->irq_name, qvec); ++ pr_debug("%s interrupt request\n", qvec->irq_name); ++ if (err) { ++ netdev_err(dev, "cannot request IRQ %d\n", ++ qvec->irq); ++ goto err_cleanup; ++ } ++ } ++ return 0; ++ ++err_cleanup: ++ mv_pp2x_cleanup_irqs(port); ++ return err; ++} ++#endif ++ ++/* Routine configure irq's. ++ * Exist 3 type of irq's - MVPP2_PRIVATE, MVPP2_TX_SHARED and MVPP2_RX_SHARED ++ * MVPP2_PRIVATE - in multi queue mode used for TX done and RX ++ * CPU that use MVPP2_PRIVATE in multi queue mode called hot CPU's ++ * in single queue mode used only for TX done ++ * If there are 8 or less system, each cpu will has his own Interrupt. ++ * If there are more than 8 CPU's, number of CPU's will share same interrupt. ++ * MVPP2_RX_SHARED - exist only in single queue mode. In this mode all cpumask ++ * used. Interrupt could be handled by any CPU. RX routine always handled by single interrupt ++ * and single thread. ++ * MVPP2_TX_SHARED - exist only in multi queue mode. This interrupt handle TX done of cold CPU's ++ * IRQ balancing allowed only for single queue mode with in system with 8 or less CPU's. ++ */ ++int mv_pp2x_setup_irqs(struct net_device *dev, struct mv_pp2x_port *port) ++{ ++ int qvec_id, cpu, err; ++ struct queue_vector *qvec; ++ ++ /* Rx/TX irq's */ ++ for (qvec_id = 0; qvec_id < port->num_qvector; qvec_id++) { ++ qvec = &port->q_vector[qvec_id]; ++ if (!qvec->irq) ++ continue; ++ err = request_irq(qvec->irq, mv_pp2x_isr, 0, ++ qvec->irq_name, qvec); ++ pr_debug("%s interrupt request\n", qvec->irq_name); ++ if (qvec->qv_type == MVPP2_PRIVATE) { ++ if (!qvec->num_of_sub_vectors) { ++ cpu = QV_THR_2_MASTER_AP_CPU(qvec->ap_id, qvec->sw_thread_id); ++ irq_set_affinity_hint(qvec->irq, cpumask_of(cpu)); ++ } else { ++ cpumask_t cpus; ++ ++ cpumask_clear(&cpus); ++ if (port->priv->pp2_cfg.queue_mode == ++ MVPP2_QDIST_MULTI_MODE) ++ for (cpu = 0; cpu < qvec->num_of_sub_vectors; cpu++) ++ cpumask_set_cpu(qvec->sub_vec[cpu]->cpu_id, &cpus); ++ else ++ for (cpu = 0; cpu < qvec->num_of_sub_vectors; cpu++) ++ cpumask_set_cpu(QV_THR_2_MASTER_AP_CPU(qvec->ap_id, cpu), &cpus); ++ irq_set_affinity_hint(qvec->irq, &cpus); ++ } ++ if ((port->priv->pp2_cfg.queue_mode == ++ MVPP2_QDIST_MULTI_MODE) || qvec->num_of_sub_vectors) ++ irq_set_status_flags(qvec->irq, IRQ_NO_BALANCING); ++ } else if (qvec->qv_type == MVPP2_TX_SHARED) { ++ cpumask_t cpus; ++ ++ cpumask_clear(&cpus); ++ for (cpu = port->priv->rx_count; cpu < num_active_cpus(); cpu++) ++ cpumask_set_cpu(cpu, &cpus); ++ irq_set_affinity_hint(qvec->irq, &cpus); ++ irq_set_status_flags(qvec->irq, IRQ_NO_BALANCING); ++ } ++ if (err) { ++ netdev_err(dev, "cannot request IRQ %d\n", ++ qvec->irq); ++ goto err_cleanup; ++ } ++ } ++ /* Link irq */ ++ if (port->mac_data.link_irq != MVPP2_NO_LINK_IRQ) { ++ pr_debug("%s interrupt request\n", port->mac_data.irq_name); ++ err = request_irq(port->mac_data.link_irq, ++ mv_pp2_link_change_isr, 0, ++ port->mac_data.irq_name, port); ++ if (err) { ++ netdev_err(dev, "cannot request IRQ %d\n", ++ port->mac_data.link_irq); ++ goto err_cleanup; ++ } ++ if (!dev->irq) ++ dev->irq = port->mac_data.link_irq; ++ } ++ return 0; ++err_cleanup: ++ mv_pp2x_cleanup_irqs(port); ++ return err; ++} ++ ++/* Adjust link */ ++ ++/* Called from link_tasklet */ ++static void mv_pp22_dev_link_event(struct net_device *dev) ++{ ++ bool link_is_up; ++ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct gop_hw *gop = &port->priv->hw.gop; ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ ++ /* Check Link status on ethernet port */ ++ link_is_up = mv_gop110_port_is_link_up(gop, &port->mac_data); ++ ++ if (link_is_up) { ++ if (netif_carrier_ok(dev)) ++ return; ++ netif_carrier_on(dev); ++ if (!(port->flags & MVPP2_F_IF_MUSDK)) ++ mv_pp2x_tx_wake_all_queues(dev); ++ netdev_info(dev, "link up\n"); ++ port->mac_data.flags |= MV_EMAC_F_LINK_UP; ++ } else { ++ if (!netif_carrier_ok(dev)) ++ return; ++ netif_carrier_off(dev); ++ mv_pp2x_tx_stop_all_queues(dev); ++ netdev_info(dev, "link down\n"); ++ port->mac_data.flags &= ~MV_EMAC_F_LINK_UP; ++ } ++} ++ ++/* Called from phy_lib */ ++static void mv_pp21_link_event(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct phy_device *phydev = port->mac_data.phy_dev; ++ int status_change = 0; ++ u32 val; ++ ++ if (!phydev) ++ return; ++ ++ if (phydev->link) { ++ if ((port->mac_data.speed != phydev->speed) || ++ (port->mac_data.duplex != phydev->duplex)) { ++ u32 val; ++ ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | ++ MVPP2_GMAC_CONFIG_GMII_SPEED | ++ MVPP2_GMAC_CONFIG_FULL_DUPLEX | ++ MVPP2_GMAC_AN_SPEED_EN | ++ MVPP2_GMAC_AN_DUPLEX_EN); ++ ++ if (phydev->duplex) ++ val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; ++ ++ if (phydev->speed == SPEED_1000) ++ val |= MVPP2_GMAC_CONFIG_GMII_SPEED; ++ else if (phydev->speed == SPEED_100) ++ val |= MVPP2_GMAC_CONFIG_MII_SPEED; ++ ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ ++ port->mac_data.duplex = phydev->duplex; ++ port->mac_data.speed = phydev->speed; ++ } ++ } ++ ++ if (phydev->link != port->mac_data.link) { ++ if (!phydev->link) { ++ port->mac_data.duplex = -1; ++ port->mac_data.speed = 0; ++ } ++ ++ port->mac_data.link = phydev->link; ++ status_change = 1; ++ } ++ ++ if (status_change) { ++ if (phydev->link) { ++ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ val |= (MVPP2_GMAC_FORCE_LINK_PASS | ++ MVPP2_GMAC_FORCE_LINK_DOWN); ++ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); ++ mv_pp2x_egress_enable(port); ++ mv_pp2x_ingress_enable(port); ++ } else { ++ mv_pp2x_ingress_disable(port); ++ mv_pp2x_egress_disable(port); ++ } ++ } ++} ++ ++static void mv_pp22_link_event(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct phy_device *phydev = port->mac_data.phy_dev; ++ int status_change = 0; ++ ++ if (!phydev) ++ return; ++ ++ if (phydev->link) { ++ if ((port->mac_data.speed != phydev->speed) || ++ (port->mac_data.duplex != phydev->duplex)) { ++#ifdef CONFIG_PHY_MVEBU_COMPHY_CP110 ++ if (port->comphy) ++ if (phy_get_mode(port->comphy[0]) != COMPHY_RXAUI0) ++ mv_gop110_update_comphy(port, phydev->speed); ++#endif ++ port->mac_data.duplex = phydev->duplex; ++ port->mac_data.speed = phydev->speed; ++ } ++ port->mac_data.flags |= MV_EMAC_F_LINK_UP; ++ } ++ ++ if (phydev->link != port->mac_data.link) { ++ if (!phydev->link) { ++ port->mac_data.duplex = -1; ++ port->mac_data.speed = 0; ++ } ++ ++ port->mac_data.link = phydev->link; ++ status_change = 1; ++ } ++ ++ if (status_change) { ++ if (phydev->link) { ++ mv_gop110_port_events_mask(&port->priv->hw.gop, ++ &port->mac_data); ++ mv_gop110_port_enable(&port->priv->hw.gop, ++ &port->mac_data, port); ++ mv_pp2x_egress_enable(port); ++ mv_pp2x_ingress_enable(port); ++ netif_carrier_on(dev); ++ if (!(port->flags & MVPP2_F_IF_MUSDK)) ++ mv_pp2x_tx_wake_all_queues(dev); ++ mv_gop110_port_events_unmask(&port->priv->hw.gop, ++ &port->mac_data); ++ port->mac_data.flags |= MV_EMAC_F_LINK_UP; ++ netdev_info(dev, "link up\n"); ++ } else { ++ mv_pp2x_ingress_disable(port); ++ mv_pp2x_egress_disable(port); ++ mv_gop110_port_events_mask(&port->priv->hw.gop, ++ &port->mac_data); ++ mv_gop110_port_disable(&port->priv->hw.gop, ++ &port->mac_data, port); ++ netif_carrier_off(dev); ++ mv_pp2x_tx_stop_all_queues(dev); ++ port->mac_data.flags &= ~MV_EMAC_F_LINK_UP; ++ netdev_info(dev, "link down\n"); ++ } ++ } ++} ++ ++void mv_pp2_link_change_tasklet(unsigned long data) ++{ ++ struct net_device *dev = (struct net_device *)data; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ mv_pp22_dev_link_event(dev); ++ ++ /* Unmask interrupt */ ++ mv_gop110_port_events_unmask(&port->priv->hw.gop, &port->mac_data); ++} ++ ++static void mv_pp2x_tx_done_timer_set(struct mv_pp2x_port_pcpu *port_pcpu) ++{ ++ if (!port_pcpu->timer_scheduled) { ++ port_pcpu->timer_scheduled = true; ++ hrtimer_start(&port_pcpu->tx_done_timer, ++ port_pcpu->tx_time_coal_hrtmr, ++ HRTIMER_MODE_REL_PINNED); ++ } ++} ++ ++static inline void mv_pp2x_tx_done_guard_timer_set(struct mv_pp2x_port *port, ++ int address_space) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ ++ if (mv_pp2x_queue_mode == MVPP2_SINGLE_RESOURCE_MODE) { ++ /* Only CPU0 will drain aggregated queue, no guard for other CPUs */ ++ if (smp_processor_id()) ++ return; ++ } ++ ++ port_pcpu = port->pcpu[address_space]; ++ if (!port_pcpu->guard_timer_scheduled) { ++ port_pcpu->guard_timer_scheduled = true; ++ hrtimer_start(&port_pcpu->tx_done_timer, ++ port_pcpu->tx_time_coal_hrtmr, ++ HRTIMER_MODE_REL_PINNED); ++ } ++} ++ ++/* Set BULK transmit TX timer */ ++static void mv_pp2x_tx_timer_set(struct mv_pp2x_cp_pcpu *cp_pcpu) ++{ ++ ktime_t interval; ++ ++ if (!cp_pcpu->tx_timer_scheduled) { ++ cp_pcpu->tx_timer_scheduled = true; ++ interval = ktime_set(0, MVPP2_TX_HRTIMER_PERIOD_NS); ++ hrtimer_start(&cp_pcpu->tx_timer, interval, ++ HRTIMER_MODE_REL_PINNED); ++ } ++} ++ ++/* Cancel BULK transmit TX timer */ ++static inline void mv_pp2x_tx_timer_kill(struct mv_pp2x_cp_pcpu *cp_pcpu) ++{ ++ if (cp_pcpu->tx_timer_scheduled) { ++ cp_pcpu->tx_timer_scheduled = false; ++ hrtimer_cancel(&cp_pcpu->tx_timer); ++ } ++} ++ ++/* Singe mode: address_space equal to current CPU/number of AP's in system ++ * For example if its dual AP(16 CPU's) and current CPU = 13. address_space 6 would be used ++ * Multi mode: address_space for HOT CPU's equal to current CPU ++ * All cold CPU's share same address space for TX. ++ * So for cold CPU's address space equal to last address space used by HOT CPU's + 1. ++ * For example if its dual AP(16 CPU's), # of HOT CPU's = 8 and current CPU = 13. ++ * CPU would be indicated as cold_cpu=true and address_space = 8. ++ * Single resource mode use only first address space. ++ */ ++static int mv_pp2x_check_address_space(struct mv_pp2x *priv, int cpu, bool *cold_cpu) ++{ ++ int address_space; ++ ++ if (mv_pp2x_queue_mode == MVPP2_SINGLE_RESOURCE_MODE) { ++ address_space = 0; ++ } else if (mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) { ++ address_space = QV_CPU_2_ADR_SP(cpu, priv->pp2_cfg.num_of_ap); ++ } else if (cpu < priv->rx_count) { ++ address_space = QV_CPU_2_ADR_SP(cpu, (priv->rx_count / MVPP2_MAX_CPUS_IN_AP)); ++ } else { ++ address_space = priv->rx_count; ++ if (cold_cpu) ++ *cold_cpu = true; ++ } ++ ++ return address_space; ++} ++ ++static void mv_pp2x_tx_done_tasklet_cb(unsigned long data) ++{ ++ struct mv_pp2x_port *port = (void *)data; ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ unsigned int tx_todo, cause, cpu = smp_processor_id(); ++ u8 address_space; ++ ++ address_space = mv_pp2x_check_address_space(port->priv, cpu, NULL); ++ ++ port_pcpu = port->pcpu[address_space]; ++ ++ if (!netif_running(port->dev)) ++ return; ++ port_pcpu->timer_scheduled = false; ++ ++ /* Process all the Tx queues */ ++ cause = (1 << mv_pp2x_txq_number) - 1; ++ tx_todo = mv_pp2x_tx_done(port, cause, address_space); ++ ++ /* Set the timer in case not all the packets were processed */ ++ if (tx_todo) ++ mv_pp2x_tx_done_timer_set(port_pcpu); ++} ++ ++/* Guard timer and Guard tasklet callbacks making check logic upon flags ++ * guard_timer_scheduled, tx_done_passed, ++ * txq_coal_is_zero_map, txq_busy_suspect_map ++ */ ++static void mv_pp2x_tx_done_guard_tasklet_cb(unsigned long data) ++{ ++ struct mv_pp2x_port *port = (void *)data; ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ int cpu; ++ ++ /* stop_dev() has permanent setting for coal=0 */ ++ if (mv_pp2x_tx_stopped(port)) ++ return; ++ ++ cpu = get_cpu(); ++ port_pcpu = port->pcpu[cpu]; /* tasklet is per-cpu */ ++ ++ if (port_pcpu->tx_done_passed) ++ port_pcpu->tx_done_passed = false; ++ else /* Force IRQ */ ++ mv_pp2x_tx_done_guard_force_irq(port, cpu, ++ port_pcpu->txq_busy_suspect_map); ++ port_pcpu->txq_busy_suspect_map = 0; ++ ++ /* guard_timer_scheduled is already TRUE, just start the timer */ ++ hrtimer_start(&port_pcpu->tx_done_timer, ++ port_pcpu->tx_time_coal_hrtmr, HRTIMER_MODE_REL_PINNED); ++ ++ put_cpu(); ++} ++ ++static enum hrtimer_restart mv_pp2x_guard_timer_cb(struct hrtimer *timer) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu = container_of(timer, ++ struct mv_pp2x_port_pcpu, tx_done_timer); ++ struct mv_pp2x_port *port = port_pcpu->port; ++ struct mv_pp2x_tx_queue *txq; ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ u8 txq_nonempty_map = 0; ++ int q, cpu; ++ ktime_t time; ++ ++ if (port_pcpu->tx_done_passed) { ++ /* ok, tx-done was active since last checking */ ++ port_pcpu->tx_done_passed = false; ++ time = port_pcpu->tx_time_coal_hrtmr; /* regular long timer */ ++ goto timer_restart; ++ } ++ ++ cpu = smp_processor_id(); /* timer is per-cpu */ ++ ++ for (q = 0; q < mv_pp2x_txq_number; q++) { ++ txq = port->txqs[q]; ++ txq_pcpu = &txq->pcpu[cpu]; ++ if (mv_pp2x_txq_count(txq_pcpu)) ++ txq_nonempty_map |= 1 << q; ++ } ++ ++ if (!txq_nonempty_map || mv_pp2x_tx_stopped(port)) { ++ /* All queues are empty, guard-timer may be stopped now ++ * It would be started again on new transmit. ++ */ ++ port_pcpu->guard_timer_scheduled = false; ++ return HRTIMER_NORESTART; ++ } ++ ++ if (port_pcpu->txq_busy_suspect_map) { ++ /* Second-hit ~~ tx-done is really stalled. ++ * Activate the tasklet to fix. ++ * Keep guard_timer_scheduled=TRUE ++ */ ++ tasklet_schedule(&port_pcpu->tx_done_tasklet); ++ return HRTIMER_NORESTART; ++ } ++ ++ /* First-hit ~~ tx-done seems stalled. Schedule re-check with SHORT time ++ * bigger a bit than HW-coal-time-usec (1024=2^10 vs NSEC_PER_USEC) ++ */ ++ time = ktime_set(0, port->tx_time_coal << 10); ++ port_pcpu->txq_busy_suspect_map |= txq_nonempty_map; ++ ++timer_restart: ++ /* Keep guard_timer_scheduled=TRUE but set new expiration time */ ++ hrtimer_forward_now(timer, time); ++ return HRTIMER_RESTART; ++} ++ ++/* Update HW with number of aggregated Tx descriptors to be sent */ ++void mv_pp2x_aggr_txq_pend_desc_add(struct mv_pp2x_port *port, int pending, int address_space) ++{ ++ /* aggregated access - relevant TXQ number is written in TX desc */ ++ mv_pp22_thread_write(&port->priv->hw, address_space, MVPP2_AGGR_TXQ_UPDATE_REG, pending); ++} ++ ++/* TX to HW the pendings in aggregated TXQ; kill deferring TX hrtimer */ ++void mv_pp2x_aggr_txq_pend_send(struct mv_pp2x_port *port, ++ struct mv_pp2x_cp_pcpu *cp_pcpu, ++ struct mv_pp2x_aggr_tx_queue *aggr_txq, int address_space) ++{ ++ mv_pp2x_tx_timer_kill(cp_pcpu); ++ aggr_txq->hw_count += aggr_txq->sw_count; ++ mv_pp2x_aggr_txq_pend_desc_add(port, aggr_txq->sw_count, address_space); ++ aggr_txq->sw_count = 0; ++} ++ ++/* Tasklet transmit procedure */ ++static void mv_pp2x_tx_send_proc_cb(unsigned long data) ++{ ++ struct mv_pp2x *priv = (struct mv_pp2x *)data; ++ struct mv_pp2x_aggr_tx_queue *aggr_txq; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ int cpu = smp_processor_id(); ++ unsigned long flags = 0; ++ u8 address_space; ++ bool cold_cpu = false; ++ ++ address_space = mv_pp2x_check_address_space(priv, cpu, &cold_cpu); ++ ++ cp_pcpu = priv->pcpu[address_space]; ++ cp_pcpu->tx_timer_scheduled = false; ++ ++ aggr_txq = &priv->aggr_txqs[cpu]; ++ ++ if (likely(aggr_txq->sw_count > 0)) { ++ /* Check if current mode require Aggregate transmit lock or its cold CPU. ++ * All cold CPU share same address space and we should protect Aggregate transmit update ++ * procedure. ++ */ ++ if (priv->pp2_cfg.spinlocks_bitmap & MV_AGGR_QUEUE_LOCK || cold_cpu) ++ spin_lock_irqsave(&aggr_txq->spinlock, flags); ++ ++ aggr_txq->hw_count += aggr_txq->sw_count; ++ ++ mv_pp22_thread_write(&priv->hw, address_space, MVPP2_AGGR_TXQ_UPDATE_REG, aggr_txq->sw_count); ++ aggr_txq->sw_count = 0; ++ ++ if (priv->pp2_cfg.spinlocks_bitmap & MV_AGGR_QUEUE_LOCK || cold_cpu) ++ spin_unlock_irqrestore(&aggr_txq->spinlock, flags); ++ } ++} ++ ++static enum hrtimer_restart mv_pp2x_tx_done_timer_cb(struct hrtimer *timer) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu = container_of(timer, ++ struct mv_pp2x_port_pcpu, tx_done_timer); ++ ++ tasklet_schedule(&port_pcpu->tx_done_tasklet); ++ ++ return HRTIMER_NORESTART; ++} ++ ++static enum hrtimer_restart mv_pp2x_tx_hr_timer_cb(struct hrtimer *timer) ++{ ++ struct mv_pp2x_cp_pcpu *cp_pcpu = container_of(timer, ++ struct mv_pp2x_cp_pcpu, tx_timer); ++ ++ tasklet_schedule(&cp_pcpu->tx_tasklet); ++ ++ return HRTIMER_NORESTART; ++} ++ ++static void mv_pp2x_tx_done_init_on_open(struct mv_pp2x_port *port, bool open) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ int cpu; ++ ++ if (port->flags & MVPP2_F_LOOPBACK) ++ return; ++ ++ if (!open) ++ goto close; ++ ++ /* Init tx-done tasklets and variables */ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ port_pcpu = port->pcpu[cpu]; ++ ++ /* Timer works in tx-done or Guard mode. To eliminate per-packet ++ * mode checking each mode has own "_scheduled" flag. ++ * Set scheduled=FALSE for active mode and TRUE for inactive, so ++ * timer would never be started in inactive mode. ++ */ ++ if (port->interrupt_tx_done) { /* guard-mode */ ++ port_pcpu->txq_coal_is_zero_map = 0; ++ port_pcpu->txq_busy_suspect_map = 0; ++ port_pcpu->tx_done_passed = false; ++ ++ port_pcpu->timer_scheduled = true; /* never started */ ++ port_pcpu->guard_timer_scheduled = false; ++ tasklet_init(&port_pcpu->tx_done_tasklet, ++ mv_pp2x_tx_done_guard_tasklet_cb, ++ (unsigned long)port); ++ } else { ++ port_pcpu->timer_scheduled = false; ++ port_pcpu->guard_timer_scheduled = true; /* never started */ ++ tasklet_init(&port_pcpu->tx_done_tasklet, ++ mv_pp2x_tx_done_tasklet_cb, ++ (unsigned long)port); ++ } ++ } ++ return; ++close: ++ /* Kill tx-done timers and tasklets */ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ port_pcpu = port->pcpu[cpu]; ++ /* Say "scheduled" for both modes preventing restart on XMIT */ ++ port_pcpu->timer_scheduled = true; ++ port_pcpu->guard_timer_scheduled = true; ++ hrtimer_cancel(&port_pcpu->tx_done_timer); ++ tasklet_kill(&port_pcpu->tx_done_tasklet); ++ } ++} ++ ++static int mv_pp2x_tx_done_init(struct platform_device *pdev, ++ struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ int cpu; ++ ktime_t time; ++ bool guard_mode = port->interrupt_tx_done; ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ port_pcpu = devm_kcalloc(&pdev->dev, 1, sizeof(*port_pcpu), GFP_KERNEL); ++ if (!port_pcpu) ++ return -ENOMEM; ++ port->pcpu[cpu] = port_pcpu; ++ } ++ ++ if (port->flags & MVPP2_F_LOOPBACK) ++ return 0; ++ ++ if (guard_mode) ++ time = ktime_set(0, MVPP2_GUARD_TXDONE_HRTIMER_USEC * NSEC_PER_USEC); ++ else ++ time = ktime_set(0, port->tx_time_coal * NSEC_PER_USEC); ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ port_pcpu = port->pcpu[cpu]; ++ port_pcpu->tx_time_coal_hrtmr = time; ++ port_pcpu->port = port; ++ ++ hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL_PINNED); ++ if (guard_mode) ++ port_pcpu->tx_done_timer.function = mv_pp2x_guard_timer_cb; ++ else ++ port_pcpu->tx_done_timer.function = mv_pp2x_tx_done_timer_cb; ++ } ++ ++ return 0; ++} ++ ++/* The function calculate the width, such as cpu width, cos queue width */ ++static void mv_pp2x_width_calc(struct mv_pp2x_port *port, u32 *cpu_width, ++ u32 *cos_width, u32 *port_rxq_width) ++{ ++ struct mv_pp2x *pp2 = port->priv; ++ int hot_cpus = num_online_cpus(); ++ ++ if (hot_cpus > port->priv->rx_count) ++ hot_cpus = port->priv->rx_count; ++ ++ if (pp2) { ++ /* Calculate CPU width */ ++ if (cpu_width) ++ *cpu_width = ilog2(roundup_pow_of_two(hot_cpus)); ++ /* Calculate cos queue width */ ++ if (cos_width) ++ *cos_width = ilog2(roundup_pow_of_two(port->cos_cfg.num_cos_queues)); ++ /* Calculate rx queue width on the port */ ++ if (port_rxq_width) ++ *port_rxq_width = ilog2(roundup_pow_of_two(pp2->pp2xdata->pp2x_max_port_rxqs)); ++ } ++} ++ ++int mv_pp2x_update_flow_info(struct mv_pp2x_hw *hw) ++{ ++ struct mv_pp2x_cls_flow_info *flow_info; ++ struct mv_pp2x_cls_lookup_entry le; ++ struct mv_pp2x_cls_flow_entry fe; ++ int flow_index, lkp_type, prio, is_last, engine, update_rss2; ++ int i, j, err; ++ ++ for (i = 0; i < (MVPP2_PRS_FL_LAST - MVPP2_PRS_FL_START); i++) { ++ is_last = 0; ++ update_rss2 = 0; ++ flow_info = &hw->cls_shadow->flow_info[i]; ++ mv_pp2x_cls_lookup_read(hw, MVPP2_PRS_FL_START + i, 0, &le); ++ err = mv_pp2x_cls_sw_lkp_flow_get(&le, &flow_index); ++ if (err) ++ return err; ++ for (j = 0; is_last == 0; j++) { ++ mv_pp2x_cls_flow_read(hw, flow_index + j, &fe); ++ err = mv_pp2x_cls_sw_flow_engine_get(&fe, &engine, ++ &is_last); ++ if (err) ++ return err; ++ err = mv_pp2x_cls_sw_flow_extra_get(&fe, &lkp_type, ++ &prio); ++ if (err) ++ return err; ++ if (lkp_type == MVPP2_CLS_LKP_DEFAULT) { ++ flow_info->flow_entry_dflt = flow_index + j; ++ } else if (lkp_type == MVPP2_CLS_LKP_VLAN_PRI) { ++ flow_info->flow_entry_vlan = flow_index + j; ++ } else if (lkp_type == MVPP2_CLS_LKP_DSCP_PRI) { ++ flow_info->flow_entry_dscp = flow_index + j; ++ } else if (lkp_type == MVPP2_CLS_LKP_HASH) { ++ if (!update_rss2) { ++ flow_info->flow_entry_rss1 = ++ flow_index + j; ++ update_rss2 = 1; ++ } else { ++ flow_info->flow_entry_rss2 = ++ flow_index + j; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* CoS API */ ++ ++/* mv_pp2x_cos_classifier_set ++* -- The API supplies interface to config cos classifier: ++* 0: cos based on vlan pri; ++* 1: cos based on dscp; ++* 2: cos based on vlan for tagged packets, ++* and based on dscp for untagged IP packets; ++* 3: cos based on dscp for IP packets, and based on vlan for non-IP packets; ++*/ ++int mv_pp2x_cos_classifier_set(struct mv_pp2x_port *port, ++ enum mv_pp2x_cos_classifier cos_mode) ++{ ++ int index, flow_idx, lkpid; ++ int data[MVPP2_LKP_PTR_NUM]; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ struct mv_pp2x_cls_flow_info *flow_info; ++ ++ for (index = 0; index < (MVPP2_PRS_FL_LAST - MVPP2_PRS_FL_START); ++ index++) { ++ int i, j; ++ ++ flow_info = &hw->cls_shadow->flow_info[index]; ++ /* Init data[] as invalid value */ ++ for (i = 0; i < MVPP2_LKP_PTR_NUM; i++) ++ data[i] = MVPP2_FLOW_TBL_SIZE; ++ lkpid = index + MVPP2_PRS_FL_START; ++ /* Prepare a temp table for the lkpid */ ++ mv_pp2x_cls_flow_tbl_temp_copy(hw, lkpid, &flow_idx); ++ /* Update lookup table to temp flow table */ ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 0, flow_idx); ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 1, flow_idx); ++ /* Update original flow table */ ++ /* First, remove the port from original table */ ++ j = 0; ++ if (flow_info->flow_entry_dflt) ++ data[j++] = flow_info->flow_entry_dflt; ++ ++ if (flow_info->flow_entry_vlan) ++ data[j++] = flow_info->flow_entry_vlan; ++ ++ if (flow_info->flow_entry_dscp) ++ data[j++] = flow_info->flow_entry_dscp; ++ ++ for (i = 0; i < j; i++) { ++ if (data[i] != MVPP2_FLOW_TBL_SIZE) ++ mv_pp2x_cls_flow_port_del(hw, ++ data[i], ++ port->id); ++ } ++ ++ /* Second, update the port in original table */ ++ if (mv_pp2x_prs_flow_id_attr_get(lkpid) & ++ MVPP2_PRS_FL_ATTR_VLAN_BIT) { ++ if (cos_mode == MVPP2_COS_CLS_VLAN || ++ cos_mode == MVPP2_COS_CLS_VLAN_DSCP || ++ (cos_mode == MVPP2_COS_CLS_DSCP_VLAN && ++ lkpid == MVPP2_PRS_FL_NON_IP_TAG)) ++ mv_pp2x_cls_flow_port_add(hw, ++ flow_info->flow_entry_vlan, port->id); ++ /* Hanlde NON-IP tagged packet */ ++ else if (cos_mode == MVPP2_COS_CLS_DSCP && ++ lkpid == MVPP2_PRS_FL_NON_IP_TAG) ++ mv_pp2x_cls_flow_port_add(hw, ++ flow_info->flow_entry_dflt, port->id); ++ else if (cos_mode == MVPP2_COS_CLS_DSCP || ++ cos_mode == MVPP2_COS_CLS_DSCP_VLAN) ++ mv_pp2x_cls_flow_port_add(hw, ++ flow_info->flow_entry_dscp, port->id); ++ } else { ++ if (lkpid == MVPP2_PRS_FL_NON_IP_UNTAG || ++ cos_mode == MVPP2_COS_CLS_VLAN) ++ mv_pp2x_cls_flow_port_add(hw, ++ flow_info->flow_entry_dflt, port->id); ++ else if (cos_mode == MVPP2_COS_CLS_DSCP || ++ cos_mode == MVPP2_COS_CLS_VLAN_DSCP || ++ cos_mode == MVPP2_COS_CLS_DSCP_VLAN) ++ mv_pp2x_cls_flow_port_add(hw, ++ flow_info->flow_entry_dscp, port->id); ++ } ++ /* Restore lookup table */ ++ flow_idx = data[0]; ++ if (flow_info->flow_entry_rss1) ++ flow_idx = min_t(int, flow_info->flow_entry_rss1, flow_idx); ++ if (flow_info->flow_entry_rss2) ++ flow_idx = min_t(int, flow_info->flow_entry_rss2, flow_idx); ++ ++ for (i = 0; i < j; i++) { ++ if (flow_idx > data[i]) ++ flow_idx = data[i]; ++ } ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 0, flow_idx); ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 1, flow_idx); ++ } ++ ++ /* Update it in priv */ ++ port->cos_cfg.cos_classifier = cos_mode; ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cos_classifier_set); ++ ++/* mv_pp2x_cos_pri_map_set ++* -- Set priority_map per port, nibble for each cos value(0~7). ++*/ ++int mv_pp2x_cos_pri_map_set(struct mv_pp2x_port *port, int cos_pri_map) ++{ ++ int ret, prev_pri_map; ++ u8 bound_cpu_first_rxq; ++ ++ if (port->cos_cfg.pri_map == cos_pri_map) ++ return 0; ++ ++ prev_pri_map = port->cos_cfg.pri_map; ++ port->cos_cfg.pri_map = cos_pri_map; ++ ++ /* Update C2 rules with nre pri_map */ ++ bound_cpu_first_rxq = mv_pp2x_bound_cpu_first_rxq_calc(port); ++ ret = mv_pp2x_cls_c2_rule_set(port, bound_cpu_first_rxq); ++ if (ret) { ++ port->cos_cfg.pri_map = prev_pri_map; ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cos_pri_map_set); ++ ++/* mv_pp2x_cos_default_value_set ++* -- Set default cos value for untagged or non-IP packets per port. ++*/ ++int mv_pp2x_cos_default_value_set(struct mv_pp2x_port *port, int cos_value) ++{ ++ int ret, prev_cos_value; ++ u8 bound_cpu_first_rxq; ++ ++ if (port->cos_cfg.default_cos == cos_value) ++ return 0; ++ ++ prev_cos_value = port->cos_cfg.default_cos; ++ port->cos_cfg.default_cos = cos_value; ++ ++ /* Update C2 rules with the pri_map */ ++ bound_cpu_first_rxq = mv_pp2x_bound_cpu_first_rxq_calc(port); ++ ret = mv_pp2x_cls_c2_rule_set(port, bound_cpu_first_rxq); ++ if (ret) { ++ port->cos_cfg.default_cos = prev_cos_value; ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_cos_default_value_set); ++ ++/* RSS API */ ++ ++/* Translate CPU sequence number to real CPU ID */ ++static int mv_pp22_cpu_id_from_indir_tbl_get(struct mv_pp2x *pp2, ++ int cpu_seq, u32 *cpu_id) ++{ ++ int i; ++ int seq = 0; ++ unsigned long hot_cpu_mask = 0; ++ ++ bitmap_set(&hot_cpu_mask, 0, min_t(u32, num_online_cpus(), mv_pp2x_rx_count)); ++ ++ if (!pp2 || !cpu_id || cpu_seq >= 16) ++ return -EINVAL; ++ ++ for (i = 0; i < 16; i++) { ++ if (hot_cpu_mask & (1 << i)) { ++ if (seq == cpu_seq) { ++ *cpu_id = i; ++ return 0; ++ } ++ seq++; ++ } ++ } ++ ++ return -1; ++} ++ ++/* mv_pp22_rss_rxfh_indir_set ++* -- The API set the RSS table according to CPU weight from ethtool ++*/ ++int mv_pp22_rss_rxfh_indir_set(struct mv_pp2x_port *port) ++{ ++ struct mv_pp22_rss_entry rss_entry; ++ int rss_tbl, entry_idx; ++ u32 cos_width = 0, cpu_width = 0, cpu_id = 0; ++ int rss_tbl_needed = port->cos_cfg.num_cos_queues; ++ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -1; ++ ++ memset(&rss_entry, 0, sizeof(struct mv_pp22_rss_entry)); ++ ++ /* Calculate cpu and cos width */ ++ mv_pp2x_width_calc(port, &cpu_width, &cos_width, NULL); ++ ++ rss_entry.u.entry.width = cos_width + cpu_width; ++ ++ rss_entry.sel = MVPP22_RSS_ACCESS_TBL; ++ ++ for (rss_tbl = 0; rss_tbl < rss_tbl_needed; rss_tbl++) { ++ for (entry_idx = 0; entry_idx < MVPP22_RSS_TBL_LINE_NUM; entry_idx++) { ++ rss_entry.u.entry.tbl_id = rss_tbl; ++ rss_entry.u.entry.tbl_line = entry_idx; ++ if (mv_pp22_cpu_id_from_indir_tbl_get(port->priv, ++ port->priv->rx_indir_table[entry_idx], &cpu_id)) ++ return -1; ++ /* Value of rss_tbl equals to cos queue */ ++ rss_entry.u.entry.rxq = (cpu_id << cos_width) | rss_tbl; ++ if (mv_pp22_rss_tbl_entry_set(&port->priv->hw, &rss_entry)) ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++/* mv_pp22_rss_enable_set ++* -- The API enable or disable RSS on the port ++*/ ++void mv_pp22_rss_enable(struct mv_pp2x_port *port, bool en) ++{ ++ u8 bound_cpu_first_rxq; ++ ++ if (port->rss_cfg.rss_en == en) ++ return; ++ ++ bound_cpu_first_rxq = mv_pp2x_bound_cpu_first_rxq_calc(port); ++ ++ if (port->priv->pp2_cfg.queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ mv_pp22_rss_c2_enable(port, en); ++ port->rss_cfg.rss_en = en; ++ if (en) { ++ if (mv_pp22_rss_default_cpu_set(port, ++ port->rss_cfg.dflt_cpu)) { ++ netdev_err(port->dev, ++ "cannot set rss default cpu on port(%d)\n", ++ port->id); ++ port->rss_cfg.rss_en = 0; ++ } ++ } else { ++ if (mv_pp2x_cls_c2_rule_set(port, ++ bound_cpu_first_rxq)) { ++ netdev_err(port->dev, ++ "cannot set c2 and qos table on port(%d)\n", ++ port->id); ++ port->rss_cfg.rss_en = 1; ++ } ++ } ++ } ++} ++ ++/* mv_pp2x_rss_mode_set ++* -- The API to update RSS hash mode for non-fragemnt UDP packet per port. ++*/ ++int mv_pp22_rss_mode_set(struct mv_pp2x_port *port, int rss_mode) ++{ ++ int index, flow_idx, flow_idx_rss, lkpid, lkpid_attr; ++ int data[3]; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ struct mv_pp2x_cls_flow_info *flow_info; ++ int err; ++ ++ err = mv_pp2x_update_flow_info(hw); ++ if (err) { ++ netdev_err(port->dev, "cannot update flow info\n"); ++ return err; ++ } ++ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -1; ++ ++ if (rss_mode != MVPP2_RSS_2T && ++ rss_mode != MVPP2_RSS_5T) { ++ pr_err("Invalid rss mode:%d\n", rss_mode); ++ return -EINVAL; ++ } ++ ++ for (index = 0; index < (MVPP2_PRS_FL_LAST - MVPP2_PRS_FL_START); ++ index++) { ++ flow_info = &hw->cls_shadow->flow_info[index]; ++ data[0] = MVPP2_FLOW_TBL_SIZE; ++ data[1] = MVPP2_FLOW_TBL_SIZE; ++ data[2] = MVPP2_FLOW_TBL_SIZE; ++ lkpid = index + MVPP2_PRS_FL_START; ++ /* Get lookup ID attribute */ ++ lkpid_attr = mv_pp2x_prs_flow_id_attr_get(lkpid); ++ /* Only non-frag TCP & UDP can set rss mode */ ++ if ((lkpid_attr & (MVPP2_PRS_FL_ATTR_TCP_BIT | MVPP2_PRS_FL_ATTR_UDP_BIT)) && ++ !(lkpid_attr & MVPP2_PRS_FL_ATTR_FRAG_BIT)) { ++ /* Prepare a temp table for the lkpid */ ++ mv_pp2x_cls_flow_tbl_temp_copy(hw, lkpid, &flow_idx); ++ /* Update lookup table to temp flow table */ ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 0, flow_idx); ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 1, flow_idx); ++ /* Update original flow table */ ++ /* First, remove the port from original table */ ++ mv_pp2x_cls_flow_port_del(hw, ++ flow_info->flow_entry_rss1, port->id); ++ mv_pp2x_cls_flow_port_del(hw, ++ flow_info->flow_entry_rss2, port->id); ++ if (flow_info->flow_entry_dflt) ++ data[0] = flow_info->flow_entry_dflt; ++ if (flow_info->flow_entry_vlan) ++ data[1] = flow_info->flow_entry_vlan; ++ if (flow_info->flow_entry_dscp) ++ data[2] = flow_info->flow_entry_dscp; ++ /* Second, update port in original table with rss_mode*/ ++ if (rss_mode == MVPP2_RSS_2T) ++ flow_idx_rss = flow_info->flow_entry_rss1; ++ else ++ flow_idx_rss = flow_info->flow_entry_rss2; ++ mv_pp2x_cls_flow_port_add(hw, flow_idx_rss, port->id); ++ ++ /*Find the ptr of flow table, the min flow index */ ++ flow_idx_rss = min(flow_info->flow_entry_rss1, ++ flow_info->flow_entry_rss2); ++ flow_idx = min(min(data[0], data[1]), min(data[2], ++ flow_idx_rss)); ++ /*Third, restore lookup table */ ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 0, flow_idx); ++ mv_pp2x_cls_lkp_flow_set(hw, lkpid, 1, flow_idx); ++ } else ++ if (flow_info->flow_entry_rss1) { ++ flow_idx_rss = flow_info->flow_entry_rss1; ++ mv_pp2x_cls_flow_port_add(hw, flow_idx_rss, ++ port->id); ++ } ++ } ++ /* Record it in priv */ ++ port->rss_cfg.rss_mode = rss_mode; ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp22_rss_mode_set); ++ ++/* mv_pp22_rss_default_cpu_set ++* -- The API to update the default CPU to handle the non-IP packets. ++*/ ++int mv_pp22_rss_default_cpu_set(struct mv_pp2x_port *port, int default_cpu) ++{ ++ u8 index, queue, q_cpu_mask; ++ u32 cpu_width = 0, cos_width = 0; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ if (port->priv->pp2_cfg.queue_mode != MVPP2_QDIST_MULTI_MODE) ++ return -1; ++ ++ if (!(*cpumask_bits(cpu_online_mask) & (1 << default_cpu))) { ++ pr_err("Invalid default cpu id %d\n", default_cpu); ++ return -EINVAL; ++ } ++ ++ /* Calculate width */ ++ mv_pp2x_width_calc(port, &cpu_width, &cos_width, NULL); ++ q_cpu_mask = (1 << cpu_width) - 1; ++ ++ /* Update LSB[cpu_width + cos_width - 1 : cos_width] ++ * of queue (queue high and low) on c2 rule. ++ */ ++ index = hw->c2_shadow->rule_idx_info[port->id].default_rule_idx; ++ queue = mv_pp2x_cls_c2_rule_queue_get(hw, index); ++ queue &= ~(q_cpu_mask << cos_width); ++ queue |= (default_cpu << cos_width); ++ mv_pp2x_cls_c2_rule_queue_set(hw, index, queue); ++ ++ /* Update LSB[cpu_width + cos_width - 1 : cos_width] ++ * of queue on pbit table, table id equals to port id ++ */ ++ for (index = 0; index < MVPP2_QOS_TBL_LINE_NUM_PRI; index++) { ++ queue = mv_pp2x_cls_c2_pbit_tbl_queue_get(hw, port->id, index); ++ queue &= ~(q_cpu_mask << cos_width); ++ queue |= (default_cpu << cos_width); ++ mv_pp2x_cls_c2_pbit_tbl_queue_set(hw, port->id, index, queue); ++ } ++ ++ /* Update default cpu in cfg */ ++ port->rss_cfg.dflt_cpu = default_cpu; ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp22_rss_default_cpu_set); ++ ++/* Main RX/TX processing routines */ ++ ++/* Handle tx checksum */ ++static u32 mv_pp2x_skb_tx_csum(struct mv_pp2x_port *port, struct sk_buff *skb) ++{ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ int ip_hdr_len = 0; ++ u8 l4_proto; ++ ++ if (skb->protocol == htons(ETH_P_IP)) { ++ struct iphdr *ip4h = ip_hdr(skb); ++ ++ /* Calculate IPv4 checksum and L4 checksum */ ++ ip_hdr_len = ip4h->ihl; ++ l4_proto = ip4h->protocol; ++ } else if (skb->protocol == htons(ETH_P_IPV6)) { ++ struct ipv6hdr *ip6h = ipv6_hdr(skb); ++ ++ /* Read l4_protocol from one of IPv6 extra headers */ ++ if (skb_network_header_len(skb) > 0) ++ ip_hdr_len = (skb_network_header_len(skb) >> 2); ++ l4_proto = ip6h->nexthdr; ++ } else { ++ return MVPP2_TXD_L4_CSUM_NOT; ++ } ++ ++ return mv_pp2x_txq_desc_csum(skb_network_offset(skb), ++ ntohs(skb->protocol), ip_hdr_len, l4_proto); ++ } ++ ++ return MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE; ++} ++ ++static void mv_pp2x_set_skb_hash(struct mv_pp2x_rx_desc *rx_desc, u32 rx_status, ++ struct sk_buff *skb) ++{ ++ /* Store unique Marvell hash to indicate recycled skb. ++ * Hash is used as additional to skb->cb protection ++ * for recycling. skb->hash unused in xmit procedure since ++ * .ndo_select_queue callback implemented in driver. ++ */ ++ if ((rx_status & MVPP2_RXD_L4_UDP) || (rx_status & MVPP2_RXD_L4_TCP)) ++ skb_set_hash(skb, MVPP2_UNIQUE_HASH, PKT_HASH_TYPE_L4); ++ else ++ skb_set_hash(skb, MVPP2_UNIQUE_HASH, PKT_HASH_TYPE_L3); ++} ++ ++/* Function is similar to build_skb routine without sbk kmem_cache_alloc */ ++static void mv_pp2x_build_skb(struct sk_buff *skb, unsigned char *data, ++ unsigned int frag_size) ++{ ++ struct skb_shared_info *shinfo; ++ unsigned int size = frag_size ? : ksize(data); ++ ++ size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ ++ memset(skb, 0, offsetof(struct sk_buff, tail)); ++ skb->truesize = SKB_TRUESIZE(size); ++ atomic_set(&skb->users, 1); ++ skb->head = data; ++ skb->data = data; ++ skb_reset_tail_pointer(skb); ++ skb->end = skb->tail + size; ++ skb->mac_header = (typeof(skb->mac_header))~0U; ++ skb->transport_header = (typeof(skb->transport_header))~0U; ++ ++ /* make sure we initialize shinfo sequentially */ ++ shinfo = skb_shinfo(skb); ++ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); ++ atomic_set(&shinfo->dataref, 1); ++ kmemcheck_annotate_variable(shinfo->destructor_arg); ++ ++ if (skb && frag_size) { ++ skb->head_frag = 1; ++ if (page_is_pfmemalloc(virt_to_head_page(data))) ++ skb->pfmemalloc = 1; ++ } ++} ++ ++static void mv_pp2x_rx_bm_pool_refill(struct work_struct *work) ++{ ++ struct mv_pp2x_port_pcpu *port_pcpu = ++ container_of(work, struct mv_pp2x_port_pcpu, ++ bm_refill_work); ++ struct mv_pp2x_port *port = port_pcpu->port; ++ u8 address_space; ++ int pool; ++ ++ address_space = mv_pp2x_check_address_space(port->priv, ++ smp_processor_id(), NULL); ++ ++ for (pool = 0; pool < MVPP2_BM_SWF_NUM_POOLS; pool++) { ++ struct mv_pp2x_bm_pool *bm_pool = &port->priv->bm_pools[pool]; ++ ++ while (port_pcpu->bm_refill[pool]) { ++ int err; ++ ++ local_bh_disable(); ++ err = mv_pp2x_rx_refill_new(port, bm_pool, bm_pool->id, ++ port->priv->pp2_cfg.recycling, ++ address_space); ++ local_bh_enable(); ++ ++ if (err) { ++ schedule_work(&port_pcpu->bm_refill_work); ++ return; ++ } ++ port_pcpu->bm_refill[pool]--; ++ } ++ } ++} ++ ++/* Main rx processing */ ++static int mv_pp2x_rx(struct mv_pp2x_port *port, struct napi_struct *napi, ++ int rx_todo, struct mv_pp2x_rx_queue *rxq, int address_space) ++{ ++ struct net_device *dev = port->dev; ++ int rx_received, i; ++ u32 rcvd_pkts = 0; ++ u32 rcvd_bytes = 0; ++ u32 refill_array[MVPP2_BM_POOLS_NUM] = {0}; ++ u8 num_pool = MVPP2_BM_SWF_NUM_POOLS; ++ u8 first_bm_pool = port->priv->pp2_cfg.first_bm_pool; ++ struct mv_pp2x_cp_pcpu *cp_pcpu = port->priv->pcpu[address_space]; ++ ++#ifdef DEV_NETMAP ++ if (port->flags & MVPP2_F_IFCAP_NETMAP) { ++ int netmap_done = 0; ++ ++ if (netmap_rx_irq(port->dev, rxq->log_id, &netmap_done)) ++ return netmap_done; ++ /* Netmap implementation includes all queues in i/f.*/ ++ return 1; ++ } ++#endif /* DEV_NETMAP */ ++ ++ /* Get number of received packets and clamp the to-do */ ++ rx_received = mv_pp2x_rxq_received(port, rxq->id); ++ ++ if (rx_todo > rx_received) ++ rx_todo = rx_received; ++ ++ for (i = 0; i < rx_todo; i++) { ++ struct mv_pp2x_rx_desc *rx_desc = ++ mv_pp2x_rxq_next_desc_get(rxq); ++ struct mv_pp2x_bm_pool *bm_pool; ++ struct sk_buff *skb; ++ u32 rx_status, pool; ++ int rx_bytes; ++ dma_addr_t buf_phys_addr; ++ unsigned char *data; ++ ++#if defined(__BIG_ENDIAN) ++ if (port->priv->pp2_version == PPV21) ++ mv_pp21_rx_desc_swap(rx_desc); ++ else ++ mv_pp22_rx_desc_swap(rx_desc); ++#endif /* __BIG_ENDIAN */ ++ ++ rx_status = rx_desc->status; ++ rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE; ++ ++ pool = MVPP2_RX_DESC_POOL(rx_desc); ++ bm_pool = &port->priv->bm_pools[pool - first_bm_pool]; ++ ++ if (port->priv->pp2_version == PPV21) ++ buf_phys_addr = mv_pp21_rxdesc_phys_addr_get(rx_desc); ++ else ++ buf_phys_addr = mv_pp22_rxdesc_phys_addr_get(rx_desc); ++ ++ data = phys_to_virt(dma_to_phys(port->dev->dev.parent, buf_phys_addr)); ++ ++ /* Prefetch 128B packet_header */ ++ prefetch(data + NET_SKB_PAD); ++ dma_sync_single_for_cpu(dev->dev.parent, buf_phys_addr, ++ MVPP2_RX_BUF_SIZE(rx_desc->data_size), ++ DMA_FROM_DEVICE); ++ ++ /* In case of an error, release the requested buffer pointer ++ * to the Buffer Manager. This request process is controlled ++ * by the hardware, and the information about the buffer is ++ * comprised by the RX descriptor. ++ */ ++ if (unlikely(rx_status & MVPP2_RXD_ERR_SUMMARY)) { ++err_drop_frame: ++ dev->stats.rx_errors++; ++ mv_pp2x_rx_error(port, rx_desc); ++ mv_pp2x_pool_refill(port->priv, pool, buf_phys_addr, address_space); ++ continue; ++ } ++ /* Try to get skb from CP skb pool ++ * If get func return skb -> use mv_pp2x_build_skb to reset skb ++ * else -> use regular build_skb callback ++ */ ++ skb = mv_pp2_skb_pool_get(port, address_space); ++ ++ if (skb) ++ mv_pp2x_build_skb(skb, data, bm_pool->frag_size > PAGE_SIZE ? 0 : ++ bm_pool->frag_size); ++ else ++ skb = build_skb(data, bm_pool->frag_size > PAGE_SIZE ? 0 : ++ bm_pool->frag_size); ++ ++ if (unlikely(!skb)) { ++ netdev_warn(port->dev, "skb build failed\n"); ++ goto err_drop_frame; ++ } ++ ++ dma_unmap_single(dev->dev.parent, buf_phys_addr, ++ MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), ++ DMA_FROM_DEVICE); ++ refill_array[bm_pool->log_id]++; ++ cp_pcpu->in_use[bm_pool->id]++; ++ ++#ifdef MVPP2_VERBOSE ++ mv_pp2x_skb_dump(skb, rx_desc->data_size, 4); ++#endif ++ ++ rcvd_pkts++; ++ rcvd_bytes += rx_bytes; ++ skb_reserve(skb, MVPP2_MH_SIZE + NET_SKB_PAD); ++#ifdef CONFIG_MV_PTP_SERVICE ++ /* If packet is PTP fetch timestamp info and built into packet data */ ++ mv_pp2_is_pkt_ptp_rx_proc(port, rx_desc, rx_bytes, skb->data, rcvd_pkts); ++#endif ++ skb_put(skb, rx_bytes); ++ skb->protocol = eth_type_trans(skb, dev); ++ ++ if (likely(dev->features & NETIF_F_RXCSUM)) ++ mv_pp2x_rx_csum(port, rx_status, skb); ++ /* Store skb magic id sequence for recycling */ ++ MVPP2X_SKB_MAGIC_BPID_SET(skb, (MVPP2X_SKB_MAGIC(skb) | ++ (port->priv->pp2_cfg.cell_index << MVPP2X_SKB_PP2_CELL_OFFS) | ++ pool)); ++ ++ skb_record_rx_queue(skb, (u16)rxq->log_id); ++ mv_pp2x_set_skb_hash(rx_desc, rx_status, skb); ++ skb_mark_napi_id(skb, napi); ++ ++ napi_gro_receive(napi, skb); ++ } ++ ++ /* Refill pool */ ++ for (i = 0; i < num_pool; i++) { ++ int err; ++ struct mv_pp2x_bm_pool *refill_bm_pool; ++ ++ if (!refill_array[i]) ++ continue; ++ ++ refill_bm_pool = &port->priv->bm_pools[i]; ++ while (likely(refill_array[i]--)) { ++ err = mv_pp2x_rx_refill_new(port, refill_bm_pool, ++ refill_bm_pool->id, ++ port->priv->pp2_cfg.recycling, ++ address_space); ++ if (unlikely(err)) { ++ struct mv_pp2x_port_pcpu *port_pcpu = ++ port->pcpu[address_space]; ++ ++ pr_warn_ratelimited("failed to refill BM pools\n"); ++ ++ refill_array[i]++; ++ port_pcpu->bm_refill[i] += refill_array[i]; ++ ++ schedule_work(&port_pcpu->bm_refill_work); ++ break; ++ } ++ } ++ } ++ ++ if (likely(rcvd_pkts)) { ++ struct mv_pp2x_pcpu_stats *stats = this_cpu_ptr(port->stats); ++ ++ u64_stats_update_begin(&stats->syncp); ++ stats->rx_packets += rcvd_pkts; ++ stats->rx_bytes += rcvd_bytes; ++ u64_stats_update_end(&stats->syncp); ++ } ++ ++ /* Update Rx queue management counters */ ++ ++ mv_pp2x_rxq_status_update(port, rxq->id, rx_todo, rx_todo); ++ ++ return rx_todo; ++} ++ ++static inline void tx_desc_unmap_put(struct device *dev, ++ struct mv_pp2x_tx_queue *txq, struct mv_pp2x_tx_desc *desc) ++{ ++ dma_addr_t buf_phys_addr; ++ ++ buf_phys_addr = mv_pp2x_txdesc_phys_addr_get( ++ ((struct mv_pp2x *)(dev_get_drvdata(dev)))->pp2_version, desc); ++ dma_unmap_single(dev, buf_phys_addr, ++ desc->data_size, DMA_TO_DEVICE); ++ mv_pp2x_txq_desc_put(txq); ++} ++ ++/* Handle tx fragmentation processing */ ++static int mv_pp2x_tx_frag_process(struct mv_pp2x_port *port, ++ struct sk_buff *skb, struct mv_pp2x_aggr_tx_queue *aggr_txq, ++ struct mv_pp2x_tx_queue *txq, struct mv_pp2x_txq_pcpu *txq_pcpu) ++{ ++ struct mv_pp2x_tx_desc *tx_desc; ++ int i; ++ dma_addr_t buf_phys_addr; ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ void *addr = page_address(frag->page.p) + frag->page_offset; ++ ++ tx_desc = mv_pp2x_txq_next_desc_get(aggr_txq); ++ tx_desc->phys_txq = txq->id; ++ tx_desc->data_size = frag->size; ++ ++ buf_phys_addr = dma_map_single(port->dev->dev.parent, addr, ++ tx_desc->data_size, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(port->dev->dev.parent, buf_phys_addr)) { ++ mv_pp2x_txq_desc_put(txq); ++ goto error; ++ } ++ tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_DATA_OFFSET; ++ mv_pp2x_txdesc_phys_addr_set(port->priv->pp2_version, ++ buf_phys_addr & ~MVPP2_TX_DESC_DATA_OFFSET, tx_desc); ++ ++ if (i == (skb_shinfo(skb)->nr_frags - 1)) { ++ /* Last descriptor */ ++ tx_desc->command = MVPP2_TXD_L_DESC; ++ mv_pp2x_txq_inc_put(port->priv->pp2_version, txq_pcpu, ++ (struct sk_buff *)((uintptr_t)skb | MVPP2_ETH_SHADOW_SKB), ++ tx_desc); ++ ++ } else { ++ /* Descriptor in the middle: Not First, Not Last */ ++ tx_desc->command = 0; ++ mv_pp2x_txq_inc_put(port->priv->pp2_version, ++ txq_pcpu, NULL, tx_desc); ++ } ++ } ++ ++ return 0; ++ ++error: ++ /* Release all descriptors that were used to map fragments of ++ * this packet, as well as the corresponding DMA mappings ++ */ ++ mv_pp2x_txq_inc_error(txq_pcpu, i); ++ ++ for (i = i - 1; i >= 0; i--) { ++ tx_desc = txq->first_desc + i; ++ tx_desc_unmap_put(port->dev->dev.parent, txq, tx_desc); ++ } ++ ++ return -ENOMEM; ++} ++ ++static inline void mv_pp2x_tx_done_post_proc(struct mv_pp2x_tx_queue *txq, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, struct mv_pp2x_port *port, ++ int frags, int address_space) ++{ ++ int txq_count; ++ ++ /* This TX-DONE post-processing is actual for SW hrtimer only */ ++ if (port->interrupt_tx_done) ++ return; ++ ++ /* get number of sent frames but not handled as tx-done */ ++ txq_count = mv_pp2x_txq_count(txq_pcpu); ++ ++ /* Finalize TX processing */ ++ if (txq_count >= txq->pkts_coal) { ++ mv_pp2x_txq_done(port, txq, txq_pcpu); ++ /* Recalc after tx_done */ ++ txq_count = mv_pp2x_txq_count(txq_pcpu); ++ } ++ if (txq_count) { ++ /* Some frames present. Start timer to handle tx-done later */ ++ struct mv_pp2x_port_pcpu *port_pcpu = port->pcpu[address_space]; ++ ++ mv_pp2x_tx_done_timer_set(port_pcpu); ++ } ++} ++ ++/* Validate TSO */ ++static inline int mv_pp2_tso_validate(struct sk_buff *skb, struct net_device *dev) ++{ ++ if (!(dev->features & NETIF_F_TSO)) { ++ pr_err("skb_is_gso(skb) returns true but features is not NETIF_F_TSO\n"); ++ return 1; ++ } ++ if (skb_shinfo(skb)->frag_list) { ++ pr_err("frag_list is not null\n"); ++ return 1; ++ } ++ if (skb_shinfo(skb)->gso_segs == 1) { ++ pr_err("Only one TSO segment\n"); ++ return 1; ++ } ++ if (skb->len <= skb_shinfo(skb)->gso_size) { ++ pr_err("total_len (%d) less than gso_size (%d)\n", ++ skb->len, skb_shinfo(skb)->gso_size); ++ return 1; ++ } ++ if ((htons(ETH_P_IP) != skb->protocol) || (ip_hdr(skb)->protocol != IPPROTO_TCP) || ++ (!tcp_hdr(skb))) { ++ pr_err("Protocol is not TCP over IP\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline int mv_pp2_tso_build_hdr_desc(struct mv_pp2x_tx_desc *tx_desc, ++ u8 *data, struct mv_pp2x_port *port, ++ struct sk_buff *skb, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ u16 *mh, int hdr_len, int size, ++ u32 tcp_seq, u16 ip_id, int left_len) ++{ ++ struct iphdr *iph; ++ struct tcphdr *tcph; ++ u8 *mac; ++ dma_addr_t buf_phys_addr; ++ int mac_hdr_len = skb_network_offset(skb); ++ u8 *data_orig = data; ++ ++ /* Reserve 2 bytes for IP header alignment */ ++ mac = data + MVPP2_MH_SIZE; ++ iph = (struct iphdr *)(mac + mac_hdr_len); ++ ++ memcpy(mac, skb->data, hdr_len); ++ ++ if (iph) { ++ iph->id = htons(ip_id); ++ iph->tot_len = htons(size + hdr_len - mac_hdr_len); ++ } ++ ++ tcph = (struct tcphdr *)(mac + skb_transport_offset(skb)); ++ tcph->seq = htonl(tcp_seq); ++ ++ if (left_len) { ++ /* Clear all special flags for not last packet */ ++ tcph->psh = 0; ++ tcph->fin = 0; ++ tcph->rst = 0; ++ } ++ ++ if (mh) { ++ /* Start tarnsmit from MH - add 2 bytes to size */ ++ *((u16 *)data) = *mh; ++ /* increment ip_offset field in TX descriptor by 2 bytes */ ++ mac_hdr_len += MVPP2_MH_SIZE; ++ hdr_len += MVPP2_MH_SIZE; ++ } else { ++ /* Start transmit from MAC */ ++ data = mac; ++ } ++ ++ tx_desc->data_size = hdr_len; ++ tx_desc->command = mv_pp2x_txq_desc_csum(mac_hdr_len, ntohs(skb->protocol), ++ ((u8 *)tcph - (u8 *)iph) >> 2, IPPROTO_TCP); ++ tx_desc->command |= MVPP2_TXD_F_DESC; ++ ++ buf_phys_addr = dma_map_single(port->dev->dev.parent, data, ++ tx_desc->data_size, DMA_TO_DEVICE); ++ ++ if (unlikely(dma_mapping_error(port->dev->dev.parent, buf_phys_addr))) { ++ pr_err("%s dma_map_single error\n", __func__); ++ return -1; ++ } ++ ++ tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_DATA_OFFSET; ++ ++ mv_pp2x_txdesc_phys_addr_set(port->priv->pp2_version, ++ buf_phys_addr & ~MVPP2_TX_DESC_DATA_OFFSET, tx_desc); ++ ++ mv_pp2x_txq_inc_put(port->priv->pp2_version, ++ txq_pcpu, ++ (struct sk_buff *)((uintptr_t)data_orig | ++ MVPP2_ETH_SHADOW_EXT), tx_desc); ++ ++ return hdr_len; ++} ++ ++static inline int mv_pp2_tso_build_data_desc(struct mv_pp2x_port *port, ++ struct mv_pp2x_tx_desc *tx_desc, ++ struct sk_buff *skb, ++ struct mv_pp2x_txq_pcpu *txq_pcpu, ++ char *frag_ptr, int frag_size, ++ int data_left, int total_left) ++{ ++ dma_addr_t buf_phys_addr; ++ int size; ++ uintptr_t val = 0; ++ ++ size = min(frag_size, data_left); ++ ++ tx_desc->data_size = size; ++ ++ buf_phys_addr = dma_map_single(port->dev->dev.parent, frag_ptr, ++ size, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(port->dev->dev.parent, buf_phys_addr))) { ++ pr_err("%s dma_map_single error\n", __func__); ++ return -1; ++ } ++ ++ tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_DATA_OFFSET; ++ ++ mv_pp2x_txdesc_phys_addr_set(port->priv->pp2_version, ++ buf_phys_addr & ~MVPP2_TX_DESC_DATA_OFFSET, tx_desc); ++ ++ tx_desc->command = 0; ++ ++ if (size == data_left) { ++ /* last descriptor in the TCP packet */ ++ tx_desc->command = MVPP2_TXD_L_DESC; ++ ++ if (total_left == 0) { ++ /* last descriptor in SKB */ ++ val = ((uintptr_t)skb | MVPP2_ETH_SHADOW_SKB); ++ } ++ } ++ mv_pp2x_txq_inc_put(port->priv->pp2_version, txq_pcpu, ++ (struct sk_buff *)(val), tx_desc); ++ ++ return size; ++} ++ ++/* send tso packet */ ++static inline int mv_pp2_tx_tso(struct sk_buff *skb, struct net_device *dev, ++ struct mv_pp2x_tx_queue *txq, ++ struct mv_pp2x_aggr_tx_queue *aggr_txq, int address_space) ++{ ++ int frag = 0, i; ++ int total_len, hdr_len, size, frag_size, data_left; ++ int total_desc_num, total_bytes = 0, max_desc_num = 0; ++ char *frag_ptr; ++ struct mv_pp2x_tx_desc *tx_desc; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_pp2x_txq_pcpu *txq_pcpu = &txq->pcpu[address_space]; ++ u16 ip_id, *mh = NULL; ++ u32 tcp_seq = 0; ++ skb_frag_t *skb_frag_ptr; ++ const struct tcphdr *th = tcp_hdr(skb); ++ struct mv_pp2x_cp_pcpu *cp_pcpu = port->priv->pcpu[address_space]; ++ ++ if (unlikely(mv_pp2_tso_validate(skb, dev))) ++ return 0; ++ ++ /* Calculate expected number of TX descriptors */ ++ max_desc_num = skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags; ++ ++ /* Check number of available descriptors */ ++ if (unlikely(mv_pp2x_aggr_desc_num_check(port->priv, aggr_txq, max_desc_num, address_space) || ++ mv_pp2x_tso_txq_reserved_desc_num_proc(port->priv, txq, ++ txq_pcpu, max_desc_num, address_space))) { ++ return 0; ++ } ++ ++ if (unlikely(max_desc_num > port->txq_stop_limit)) ++ if (likely(mv_pp2x_txq_free_count(txq_pcpu) < max_desc_num)) ++ return 0; ++ ++ total_len = skb->len; ++ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); ++ ++ total_len -= hdr_len; ++ ip_id = ntohs(ip_hdr(skb)->id); ++ tcp_seq = ntohl(th->seq); ++ ++ frag_size = skb_headlen(skb); ++ frag_ptr = skb->data; ++ ++ if (unlikely(frag_size < hdr_len)) { ++ pr_err("frag_size=%d, hdr_len=%d\n", frag_size, hdr_len); ++ return 0; ++ } ++ ++ /* Skip header - we'll add header in another buffer (from extra pool) */ ++ frag_size -= hdr_len; ++ frag_ptr += hdr_len; ++ ++ /* A special case where the first skb's frag contains only the packet's header */ ++ if (frag_size == 0) { ++ skb_frag_ptr = &skb_shinfo(skb)->frags[frag]; ++ ++ /* Move to next segment */ ++ frag_size = skb_frag_ptr->size; ++ frag_ptr = page_address(skb_frag_ptr->page.p) + skb_frag_ptr->page_offset; ++ frag++; ++ } ++ total_desc_num = 0; ++ ++ /* Each iteration - create new TCP segment */ ++ while (likely(total_len > 0)) { ++ u8 *data; ++ ++ data_left = min((int)(skb_shinfo(skb)->gso_size), total_len); ++ ++ /* Sanity check */ ++ if (unlikely(total_desc_num >= max_desc_num)) { ++ pr_err("%s: Used TX descriptors number %d is larger than allocated %d\n", ++ __func__, total_desc_num, max_desc_num); ++ goto out_no_tx_desc; ++ } ++ ++ data = mv_pp2_extra_pool_get(port, address_space); ++ if (unlikely(!data)) { ++ pr_err("Can't allocate extra buffer for TSO\n"); ++ goto out_no_tx_desc; ++ } ++ tx_desc = mv_pp2x_txq_next_desc_get(aggr_txq); ++ tx_desc->phys_txq = txq->id; ++ ++ total_len -= data_left; ++ ++ /* prepare packet headers: MAC + IP + TCP */ ++ size = mv_pp2_tso_build_hdr_desc(tx_desc, data, port, skb, ++ txq_pcpu, mh, hdr_len, ++ data_left, tcp_seq, ip_id, ++ total_len); ++ if (unlikely(size < 0)) ++ goto out_no_tx_desc; ++ total_desc_num++; ++ ++ total_bytes += size; ++ ++ /* Update packet's IP ID */ ++ ip_id++; ++ ++ while (likely(data_left > 0)) { ++ /* Sanity check */ ++ if (unlikely(total_desc_num >= max_desc_num)) { ++ pr_err("%s: Used TX descriptors number %d is larger than allocated %d\n", ++ __func__, total_desc_num, max_desc_num); ++ goto out_no_tx_desc; ++ } ++ tx_desc = mv_pp2x_txq_next_desc_get(aggr_txq); ++ tx_desc->phys_txq = txq->id; ++ ++ size = mv_pp2_tso_build_data_desc(port, tx_desc, skb, txq_pcpu, ++ frag_ptr, frag_size, data_left, total_len); ++ ++ if (unlikely(size < 0)) ++ goto out_no_tx_desc; ++ ++ total_desc_num++; ++ total_bytes += size; ++ data_left -= size; ++ ++ /* Update TCP sequence number */ ++ tcp_seq += size; ++ ++ /* Update frag size, and offset */ ++ frag_size -= size; ++ frag_ptr += size; ++ ++ if ((frag_size == 0) && (frag < skb_shinfo(skb)->nr_frags)) { ++ skb_frag_ptr = &skb_shinfo(skb)->frags[frag]; ++ ++ /* Move to next segment */ ++ frag_size = skb_frag_ptr->size; ++ frag_ptr = page_address(skb_frag_ptr->page.p) + skb_frag_ptr->page_offset; ++ frag++; ++ } ++ } ++ } ++ ++ aggr_txq->sw_count += total_desc_num; ++ ++ if (!skb->xmit_more) { ++ mv_pp2x_tx_done_guard_timer_set(port, address_space); ++ /* Transmit TCP segment with bulked descriptors and cancel tx hr timer if exist */ ++ mv_pp2x_aggr_txq_pend_send(port, cp_pcpu, aggr_txq, address_space); ++ } ++ ++ txq_pcpu->reserved_num -= total_desc_num; ++ ++ return total_desc_num; ++ ++out_no_tx_desc: ++ /* No enough memory for packet header - rollback */ ++ pr_err("%s: No TX descriptors - rollback %d, txq_count=%d, nr_frags=%d, skb=%p, len=%d, gso_segs=%d\n", ++ __func__, total_desc_num, (aggr_txq->hw_count + aggr_txq->sw_count), skb_shinfo(skb)->nr_frags, ++ skb, skb->len, skb_shinfo(skb)->gso_segs); ++ ++ for (i = 0; i < total_desc_num; i++) { ++ struct sk_buff *shadow_skb; ++ dma_addr_t shadow_buf; ++ int data_size; ++ ++ mv_pp2x_txq_dec_put(txq_pcpu); ++ ++ shadow_skb = txq_pcpu->tx_skb[txq_pcpu->txq_put_index]; ++ shadow_buf = txq_pcpu->tx_buffs[txq_pcpu->txq_put_index]; ++ data_size = txq_pcpu->data_size[txq_pcpu->txq_put_index]; ++ ++ mv_pp2x_txq_buf_free(port, (uintptr_t)shadow_skb, shadow_buf, ++ data_size, address_space); ++ ++ mv_pp2x_txq_prev_desc_get(aggr_txq); ++ } ++ ++ return 0; ++} ++ ++/* Routine to check if skb recyclable. Data buffer cannot be recycled if: ++ * 1. skb is cloned, shared, nonlinear, zero copied. ++ * 2. skb with not align data buffer. ++ * 3. IRQs are disabled. ++ */ ++static inline bool mv_pp2x_skb_is_recycleable(const struct sk_buff *skb, int skb_size) ++{ ++ if (unlikely(irqs_disabled())) ++ return false; ++ ++ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY)) ++ return false; ++ ++ if (unlikely(skb_is_nonlinear(skb) || skb->fclone != SKB_FCLONE_UNAVAILABLE)) ++ return false; ++ ++ skb_size = SKB_DATA_ALIGN(skb_size + NET_SKB_PAD); ++ if (unlikely(skb_end_pointer(skb) - skb->head < skb_size)) ++ return false; ++ ++ if (unlikely(skb_shared(skb) || skb_cloned(skb))) ++ return false; ++ ++ return true; ++} ++ ++/* Routine to get BM pool from skb cb */ ++static inline struct mv_pp2x_bm_pool *mv_pp2x_skb_recycle_get_pool(struct mv_pp2x *priv, struct sk_buff *skb) ++{ ++ if (MVPP2X_SKB_RECYCLE_MAGIC_IS_OK(skb)) ++ return &priv->bm_pools[MVPP2X_SKB_BPID_GET(skb)]; ++ else ++ return NULL; ++} ++ ++/* Routine: ++ * 1. Check that it's save to recycle skb by mv_pp2x_skb_is_recycleable routine ++ * 2. Check if MVPP2 unique hash were set in RX routine. ++ * 3. Check that recycle is on same CPN. ++ * 4. Test skb->cb magic and return BM pool ID if its pass all criterions. ++ * Otherwise -1 returned. ++ */ ++static inline int mv_pp2x_skb_recycle_check(struct mv_pp2x *priv, struct sk_buff *skb, struct mv_pp2x_cp_pcpu *cp_pcpu) ++{ ++ struct mv_pp2x_bm_pool *bm_pool; ++ ++ if ((skb->hash == MVPP2_UNIQUE_HASH) && (MVPP2X_SKB_PP2_CELL_GET(skb) == priv->pp2_cfg.cell_index) && ++ priv->pp2_cfg.recycling) { ++ bm_pool = mv_pp2x_skb_recycle_get_pool(priv, skb); ++ if (bm_pool) ++ if (mv_pp2x_skb_is_recycleable(skb, bm_pool->pkt_size) && (cp_pcpu->in_use[bm_pool->id] > 0)) ++ return bm_pool->id; ++ } ++ ++ return -1; ++} ++ ++/* Main tx processing */ ++static int mv_pp2x_tx(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct mv_pp2x_tx_queue *txq; ++ struct mv_pp2x_aggr_tx_queue *aggr_txq; ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ struct netdev_queue *nq; ++ struct mv_pp2x_tx_desc *tx_desc; ++ dma_addr_t buf_phys_addr; ++ int frags = 0, pool_id; ++ u16 txq_id; ++ u32 tx_cmd, cpu = smp_processor_id(); ++ int address_space; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ u8 recycling; ++ unsigned long flags = 0; ++ bool cold_cpu = false; ++ ++ address_space = mv_pp2x_check_address_space(port->priv, cpu, &cold_cpu); ++ ++ cp_pcpu = port->priv->pcpu[address_space]; ++ ++ /* Set relevant physical TxQ and Linux netdev queue */ ++ txq_id = skb_get_queue_mapping(skb) % mv_pp2x_txq_number; ++ txq = port->txqs[txq_id]; ++ txq_pcpu = &txq->pcpu[address_space]; ++ aggr_txq = &port->priv->aggr_txqs[address_space]; ++ ++ /* Check if current mode require Aggregate transmit lock or its cold CPU. ++ * All cold CPU share same address space and we should protect Aggregate transmit update ++ * procedure. ++ */ ++ if ((port->priv->pp2_cfg.spinlocks_bitmap & MV_AGGR_QUEUE_LOCK) || cold_cpu) ++ spin_lock_irqsave(&aggr_txq->spinlock, flags); ++ ++ /* Prevent shadow_q override, stop tx_queue until tx_done is called*/ ++ if (unlikely(mv_pp2x_txq_free_count(txq_pcpu) < port->txq_stop_limit)) { ++ if ((txq->log_id + (address_space * mv_pp2x_txq_number)) == skb_get_queue_mapping(skb)) { ++ nq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); ++ netif_tx_stop_queue(nq); ++ } ++ frags = 0; ++ goto out; ++ } ++ ++ /* GSO/TSO */ ++ if (skb_is_gso(skb)) { ++ frags = mv_pp2_tx_tso(skb, dev, txq, aggr_txq, address_space); ++ goto out; ++ } ++ ++ frags = skb_shinfo(skb)->nr_frags + 1; ++ pr_debug("txq_id=%d, frags=%d\n", txq_id, frags); ++ ++ /* Check number of available descriptors */ ++ if (unlikely(mv_pp2x_aggr_desc_num_check(port->priv, aggr_txq, frags, address_space) || ++ mv_pp2x_txq_reserved_desc_num_proc(port->priv, txq, ++ txq_pcpu, frags, address_space))) { ++ frags = 0; ++ goto out; ++ } ++ ++ /* Get a descriptor for the first part of the packet */ ++ tx_desc = mv_pp2x_txq_next_desc_get(aggr_txq); ++ tx_desc->phys_txq = txq->id; ++ tx_desc->data_size = skb_headlen(skb); ++ pr_debug( ++ "tx_desc=%p, cmd(0x%x), pkt_offset(%d), phys_txq=%d, data_size=%d\n" ++ "rsrvd_hw_cmd1(0x%llx)\n" ++ "buf_phys_addr_hw_cmd2(0x%llx)\n" ++ "buf_cookie_bm_qset_hw_cmd3(0x%llx)\n" ++ "skb_len=%d, skb_data_len=%d\n", ++ tx_desc, tx_desc->command, tx_desc->packet_offset, ++ tx_desc->phys_txq, tx_desc->data_size, ++ tx_desc->u.pp22.rsrvd_hw_cmd1, ++ tx_desc->u.pp22.buf_phys_addr_hw_cmd2, ++ tx_desc->u.pp22.buf_cookie_bm_qset_hw_cmd3, ++ skb->len, skb->data_len); ++#ifdef MVPP2_VERBOSE ++ mv_pp2x_skb_dump(skb, tx_desc->data_size, 4); ++#endif ++ /* Mandatory tx_desc fields are always initialized, but ++ * service-specific fields are NOT cleared => old tx_desc impacts ++ * new TX by irrelevant data inside. Clear these fields before usage. ++ * The clear shold be done in an asm-optimized way. ++ * Bit-field and Byte access generates in assembler some additional asm commands. ++ * But Write32bitZero only to required fields is most effective. ++ */ ++#ifdef CONFIG_MV_PTP_SERVICE ++ /* Clearing the following fields: ++ * u32[2]: 0x08: L4iChk , DSATTag, DP, GEM_port_id/PTP_Descriptor[11:0] ++ * u32[5]: 0x14: Buffer_physical_pointer ,PTP Descriptor ++ */ ++ *((u32 *)tx_desc + 2) = 0; ++ *((u32 *)tx_desc + 5) = 0; ++#endif ++ ++ buf_phys_addr = dma_map_single(dev->dev.parent, skb->data, ++ tx_desc->data_size, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(dev->dev.parent, buf_phys_addr))) { ++ mv_pp2x_txq_desc_put(txq); ++ frags = 0; ++ goto out; ++ } ++ pr_debug("buf_phys_addr=%x\n", (unsigned int)buf_phys_addr); ++ ++ tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_DATA_OFFSET; ++ mv_pp2x_txdesc_phys_addr_set(port->priv->pp2_version, ++ buf_phys_addr & ~MVPP2_TX_DESC_DATA_OFFSET, tx_desc); ++ ++ tx_cmd = mv_pp2x_skb_tx_csum(port, skb); ++ ++ if (frags == 1) { ++ /* First and Last descriptor */ ++ /* Check if skb should be recycled */ ++ pool_id = mv_pp2x_skb_recycle_check(port->priv, skb, cp_pcpu); ++ /* If pool ID provided -> packet should be recycled. ++ * Set recycled field in TX descriptor and add skb recycle shadow. ++ */ ++ if (pool_id > -1) { ++ tx_cmd |= MVPP2_TXD_BUF_MOD; ++ tx_cmd |= ((pool_id << MVPP2_RXD_BM_POOL_ID_OFFS) & MVPP2_RXD_BM_POOL_ID_MASK); ++ recycling = MVPP2_ETH_SHADOW_REC; ++ } else { ++ recycling = MVPP2_ETH_SHADOW_SKB; ++ } ++ ++ tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC; ++ tx_desc->command = tx_cmd; ++ mv_pp2x_txq_inc_put(port->priv->pp2_version, ++ txq_pcpu, (struct sk_buff *)((uintptr_t)skb | ++ recycling), tx_desc); ++ } else { ++ /* First but not Last */ ++ tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_PADDING_DISABLE; ++ tx_desc->command = tx_cmd; ++ mv_pp2x_txq_inc_put(port->priv->pp2_version, ++ txq_pcpu, NULL, tx_desc); ++ ++ /* Continue with other skb fragments */ ++ if (unlikely(mv_pp2x_tx_frag_process(port, skb, aggr_txq, txq, txq_pcpu))) { ++ mv_pp2x_txq_inc_error(txq_pcpu, 1); ++ tx_desc_unmap_put(port->dev->dev.parent, txq, tx_desc); ++ frags = 0; ++ goto out; ++ } ++ } ++ txq_pcpu->reserved_num -= frags; ++ aggr_txq->sw_count += frags; ++ ++#ifdef CONFIG_MV_PTP_SERVICE ++ /* If packet is PTP add Time-Stamp request into the tx_desc */ ++ mv_pp2_is_pkt_ptp_tx_proc(port, tx_desc, skb); ++#endif ++ ++ /* Start 50 microseconds timer to transmit */ ++ if (!skb->xmit_more) { ++ mv_pp2x_tx_done_guard_timer_set(port, address_space); ++ if (skb->hash == MVPP2_UNIQUE_HASH && port->priv->pp2_cfg.recycling) ++ mv_pp2x_tx_timer_set(cp_pcpu); ++ else ++ mv_pp2x_aggr_txq_pend_send(port, cp_pcpu, aggr_txq, address_space); ++ } ++ ++out: ++ if (likely(frags > 0)) { ++ struct mv_pp2x_pcpu_stats *stats = this_cpu_ptr(port->stats); ++ ++ u64_stats_update_begin(&stats->syncp); ++ stats->tx_packets++; ++ stats->tx_bytes += skb->len; ++ u64_stats_update_end(&stats->syncp); ++ } else { ++ /* Transmit bulked descriptors*/ ++ if (aggr_txq->sw_count > 0) { ++ mv_pp2x_tx_done_guard_timer_set(port, address_space); ++ mv_pp2x_aggr_txq_pend_send(port, cp_pcpu, aggr_txq, address_space); ++ } ++ dev->stats.tx_dropped++; ++ dev_kfree_skb_any(skb); ++ } ++ /* PPV22 TX Post-Processing */ ++ ++#ifdef DEV_NETMAP ++ /* Don't check tx done for ports working in Netmap mode */ ++ if ((port->flags & MVPP2_F_IFCAP_NETMAP)) ++ return NETDEV_TX_OK; ++#endif ++ mv_pp2x_tx_done_post_proc(txq, txq_pcpu, port, frags, address_space); ++ ++ if ((port->priv->pp2_cfg.spinlocks_bitmap & MV_AGGR_QUEUE_LOCK) || cold_cpu) ++ spin_unlock_irqrestore(&aggr_txq->spinlock, flags); ++ ++ return NETDEV_TX_OK; ++} ++ ++static inline void mv_pp21_cause_misc_handle(struct mv_pp2x_port *port, ++ struct mv_pp2x_hw *hw, u32 cause_rx_tx) ++{ ++ u32 cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; ++ ++ if (cause_misc) { ++ mv_pp2x_cause_error(port->dev, cause_misc); ++ ++ /* Clear the cause register */ ++ mv_pp2x_write(hw, MVPP2_ISR_MISC_CAUSE_REG, 0); ++ mv_pp2x_write(hw, MVPP2_ISR_RX_TX_CAUSE_REG(port->id), ++ cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); ++ } ++} ++ ++static inline void mv_pp22_cause_misc_handle(struct mv_pp2x_port *port, ++ struct mv_pp2x_hw *hw, u32 cause_rx_tx, u16 sw_thread_id) ++{ ++ u32 cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; ++ ++ if (cause_misc) { ++ mv_pp2x_cause_error(port->dev, cause_misc); ++ ++ /* Clear the cause register */ ++ mv_pp22_thread_write(hw, sw_thread_id, MVPP2_ISR_MISC_CAUSE_REG, 0); ++ mv_pp22_thread_write(hw, sw_thread_id, MVPP2_ISR_RX_TX_CAUSE_REG(port->id), ++ cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); ++ } ++} ++ ++static inline int mv_pp2x_cause_rx_handle(struct mv_pp2x_port *port, ++ struct queue_vector *q_vec, struct sub_queue_vector *sub_q_vec, ++ struct napi_struct *napi, int budget, u32 cause_rx) ++{ ++ int rx_done = 0, count = 0; ++ struct mv_pp2x_rx_queue *rxq; ++ ++ while (cause_rx && budget > 0) { ++ rxq = mv_pp2x_get_rx_queue(port, cause_rx); ++ if (!rxq) ++ break; ++ ++ count = mv_pp2x_rx(port, napi, budget, rxq, q_vec->sw_thread_id); ++ rx_done += count; ++ budget -= count; ++ if (budget > 0) { ++ /* Clear the bit associated to this Rx queue ++ * so that next iteration will continue from ++ * the next Rx queue. ++ */ ++ cause_rx &= ~(1 << rxq->log_id); ++ } ++ } ++ ++#ifdef DEV_NETMAP ++ if ((port->flags & MVPP2_F_IFCAP_NETMAP)) { ++ napi_complete(napi); ++ sub_q_vec->pending_cause_rx = 0; ++ return rx_done; ++ } ++#endif ++ ++ if (budget > 0) { ++ cause_rx = 0; ++ napi_complete(napi); ++ if (!q_vec->num_of_sub_vectors || mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) { ++ mv_pp2x_qvector_interrupt_enable(q_vec); ++ } else { ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&q_vec->hw_mask_lock, flags); ++ q_vec->queue_mask |= sub_q_vec->queue_mask; ++ mv_pp2x_interrupts_set_mask(q_vec, q_vec->queue_mask); ++ spin_unlock_irqrestore(&q_vec->hw_mask_lock, flags); ++ } ++ } ++ sub_q_vec->pending_cause_rx = cause_rx; ++ ++ return rx_done; ++} ++ ++static int mv_pp21_poll(struct napi_struct *napi, int budget) ++{ ++ u32 cause_rx_tx, cause_rx; ++ int rx_done = 0; ++ struct mv_pp2x_port *port = netdev_priv(napi->dev); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ struct queue_vector *q_vec = container_of(napi, ++ struct queue_vector, napi); ++ ++ /* Rx/Tx cause register ++ * ++ * Bits 0-15: each bit indicates received packets on the Rx queue ++ * (bit 0 is for Rx queue 0). ++ * ++ * Bits 16-23: each bit indicates transmitted packets on the Tx queue ++ * (bit 16 is for Tx queue 0). ++ * ++ * Each CPU has its own Rx/Tx cause register ++ */ ++ cause_rx_tx = mv_pp2x_read(hw, MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); ++ ++ cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; ++ ++ /*Process misc errors */ ++ mv_pp21_cause_misc_handle(port, hw, cause_rx_tx); ++ ++ /* Process RX packets */ ++ cause_rx = cause_rx_tx & MVPP21_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ cause_rx |= q_vec->pending_cause_rx; ++ /* FIXME: Mvpp21 support */ ++ rx_done = mv_pp2x_cause_rx_handle(port, q_vec, (struct sub_queue_vector *)NULL, napi, budget, cause_rx); ++ ++ return rx_done; ++} ++ ++static int mv_pp22_poll(struct napi_struct *napi, int budget) ++{ ++ u32 cause_rx_tx, cause_rx, cause_tx; ++ int rx_done = 0; ++ struct mv_pp2x_port *port = netdev_priv(napi->dev); ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ ++ struct sub_queue_vector *sub_q_vec = container_of(napi, ++ struct sub_queue_vector, napi); ++ ++ struct queue_vector *q_vec = sub_q_vec->parent; ++ ++ /* Rx/Tx cause register ++ * Each CPU has its own Tx cause register ++ */ ++ ++ /*The read is in the q_vector's sw_thread_id address_space */ ++ cause_rx_tx = mv_pp22_thread_relaxed_read(hw, q_vec->sw_thread_id, ++ MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); ++ pr_debug("%s port_id(%d), cpuId(%d), sw_thread_id(%d), isr_tx_rx(0x%x)\n", ++ __func__, port->id, q_vec->sw_thread_id, q_vec->sw_thread_id, cause_rx_tx); ++ ++ /*Process misc errors */ ++ cause_rx_tx &= sub_q_vec->queue_mask; ++ mv_pp22_cause_misc_handle(port, hw, cause_rx_tx, q_vec->sw_thread_id); ++ /* Release TX descriptors */ ++ cause_tx = (cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK) >> ++ MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET; ++ if (cause_tx) { ++ mv_pp2x_tx_done(port, cause_tx, q_vec->sw_thread_id); ++ port->pcpu[q_vec->sw_thread_id]->tx_done_passed = true; ++ } ++ ++ /* Process RX packets */ ++ cause_rx = cause_rx_tx & MVPP22_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ /*Convert queues from subgroup-relative to port-relative */ ++ cause_rx <<= q_vec->first_rx_queue; ++ cause_rx |= sub_q_vec->pending_cause_rx; ++ ++ rx_done = mv_pp2x_cause_rx_handle(port, q_vec, sub_q_vec, napi, budget, cause_rx); ++ ++ return rx_done; ++} ++ ++void mv_pp2x_port_napi_enable(struct mv_pp2x_port *port) ++{ ++ int i, sub_vec_id; ++ struct queue_vector *qvec; ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ qvec = &port->q_vector[i]; ++ if (!qvec->num_of_sub_vectors || mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) ++ napi_enable(&qvec->sub_vec[0]->napi); ++ else ++ for (sub_vec_id = 0; sub_vec_id < qvec->num_of_sub_vectors; sub_vec_id++) ++ napi_enable(&qvec->sub_vec[sub_vec_id]->napi); ++ } ++} ++ ++void mv_pp2x_port_napi_disable(struct mv_pp2x_port *port) ++{ ++ int i, sub_vec_id; ++ struct queue_vector *qvec; ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ qvec = &port->q_vector[i]; ++ if (!qvec->num_of_sub_vectors || mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) ++ napi_disable(&qvec->sub_vec[0]->napi); ++ else ++ for (sub_vec_id = 0; sub_vec_id < qvec->num_of_sub_vectors; sub_vec_id++) ++ napi_disable(&qvec->sub_vec[sub_vec_id]->napi); ++ } ++} ++ ++static void mv_pp2x_port_napi_remove(struct mv_pp2x_port *port) ++{ ++ int i, sub_vec_id; ++ struct queue_vector *qvec; ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ qvec = &port->q_vector[i]; ++ if (!qvec->num_of_sub_vectors || mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) { ++ napi_hash_del(&qvec->sub_vec[0]->napi); ++ netif_napi_del(&qvec->sub_vec[0]->napi); ++ } else { ++ for (sub_vec_id = 0; sub_vec_id < qvec->num_of_sub_vectors; sub_vec_id++) { ++ napi_hash_del(&qvec->sub_vec[sub_vec_id]->napi); ++ netif_napi_del(&qvec->sub_vec[sub_vec_id]->napi); ++ } ++ } ++ } ++} ++ ++static void mv_pp2x_port_irqs_dispose_mapping(struct mv_pp2x_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < port->num_irqs; i++) ++ irq_dispose_mapping(port->of_irqs[i]); ++} ++ ++static void mv_serdes_port_init(struct mv_pp2x_port *port) ++{ ++ int mode, i; ++ ++ switch (port->mac_data.phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ if (port->mac_data.flags & MV_EMAC_F_SGMII2_5) ++ mode = COMPHY_DEF(COMPHY_HS_SGMII_MODE, port->id, ++ COMPHY_SPEED_3_125G, COMPHY_POLARITY_NO_INVERT); ++ else ++ mode = COMPHY_DEF(COMPHY_SGMII_MODE, port->id, ++ COMPHY_SPEED_1_25G, COMPHY_POLARITY_NO_INVERT); ++ phy_set_mode(port->comphy[0], mode); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ for (i = 0; i < port->num_serdes_lanes; i++) { ++ mode = COMPHY_DEF(COMPHY_RXAUI_MODE, i, ++ COMPHY_SPEED_10_3125G, COMPHY_POLARITY_NO_INVERT); ++ phy_set_mode(port->comphy[i], mode); ++ } ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ if (port->mac_data.flags & MV_EMAC_F_5G) ++ mode = COMPHY_DEF(COMPHY_SFI_MODE, port->id, ++ COMPHY_SPEED_5_15625G, COMPHY_POLARITY_NO_INVERT); ++ else ++ mode = COMPHY_DEF(COMPHY_SFI_MODE, port->id, ++ COMPHY_SPEED_10_3125G, COMPHY_POLARITY_NO_INVERT); ++ phy_set_mode(port->comphy[0], mode); ++ break; ++ case PHY_INTERFACE_MODE_XFI: ++ if (port->mac_data.flags & MV_EMAC_F_5G) ++ mode = COMPHY_DEF(COMPHY_XFI_MODE, port->id, ++ COMPHY_SPEED_5_15625G, COMPHY_POLARITY_NO_INVERT); ++ else ++ mode = COMPHY_DEF(COMPHY_XFI_MODE, port->id, ++ COMPHY_SPEED_10_3125G, COMPHY_POLARITY_NO_INVERT); ++ ++ phy_set_mode(port->comphy[0], mode); ++ break; ++ default: ++ pr_err("%s: Wrong port mode (%d)", __func__, port->mac_data.phy_mode); ++ } ++} ++ ++int mvcpn110_mac_hw_init(struct mv_pp2x_port *port) ++{ ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ int gop_port = mac->gop_index; ++ u64 timer; ++ ++ if (mac->flags & MV_EMAC_F_INIT) ++ return 0; ++ ++ /* configure port PHY address */ ++ mv_gop110_smi_phy_addr_cfg(gop, gop_port, mac->phy_addr); ++ ++ if (port->comphy) ++ mv_serdes_port_init(port); ++ ++ /* Set Flow Control timer x10 faster than pause quanta to ensure that link ++ * partner won't send taffic if port in XOFF mode. ++ */ ++ timer = (port->priv->hw.tclk / (USEC_PER_SEC * 10)) * FLOW_CONTROL_QUANTA; ++ ++ mv_gop110_fca_set_periodic_timer(gop, mac->gop_index, timer); ++ ++ mv_gop110_port_init(gop, mac); ++ ++ if (mac->force_link) ++ mv_gop110_fl_cfg(gop, mac); ++ ++ mac->flags |= MV_EMAC_F_INIT; ++ ++ return 0; ++} ++ ++/* Set hw internals when starting port */ ++void mv_pp2x_start_dev(struct mv_pp2x_port *port) ++{ ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ int mac_num = port->mac_data.gop_index; ++ int i; ++#ifdef DEV_NETMAP ++ if (port->flags & MVPP2_F_IFCAP_NETMAP) { ++ if (mv_pp2x_netmap_rxq_init_buffers(port)) ++ pr_debug("%s: Netmap rxq_init_buffers done\n", ++ __func__); ++ if (mv_pp2x_netmap_txq_init_buffers(port)) ++ pr_debug("%s: Netmap txq_init_buffers done\n", ++ __func__); ++ } ++#endif /* DEV_NETMAP */ ++ ++ if (port->interrupt_tx_done) /* set ZERO by stop_dev(), set CFG here */ ++ mv_pp2x_tx_done_pkts_coal_set_all(port); ++ ++ if (port->priv->pp2_version == PPV21) { ++ mv_pp21_gmac_max_rx_size_set(port); ++ } else { ++ switch (mac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mv_gop110_gmac_max_rx_size_set(gop, mac_num, ++ port->pkt_size); ++ break; ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ mv_gop110_xlg_mac_max_rx_size_set(gop, ++ mac_num, port->pkt_size); ++ break; ++ default: ++ break; ++ } ++ } ++ mv_pp2x_txp_max_tx_size_set(port); ++ ++ mv_pp2x_port_napi_enable(port); ++ ++ /* Enable RX/TX interrupts on all CPUs */ ++ mv_pp2x_port_interrupts_enable(port); ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ mv_pp22_port_uio_interrupts_enable(port); ++ ++ if (port->comphy) { ++ mv_gop110_port_disable(gop, mac, port); ++ for (i = 0; i < port->num_serdes_lanes; i++) ++ phy_power_on(port->comphy[i]); ++ } ++ ++ if (port->priv->pp2_version == PPV21) { ++ mv_pp21_port_enable(port); ++ } else { ++ if (!(port->flags & MVPP2_F_LOOPBACK)) { ++ mv_gop110_port_events_mask(gop, mac); ++ mv_gop110_port_enable(gop, mac, port); ++ } ++ } ++ ++ if (port->mac_data.phy_dev) { ++ phy_start(port->mac_data.phy_dev); ++ } else { ++ if (!(port->flags & MVPP2_F_LOOPBACK)) { ++ mv_pp22_dev_link_event(port->dev); ++ tasklet_init(&port->link_change_tasklet, mv_pp2_link_change_tasklet, ++ (unsigned long)(port->dev)); ++ } ++ } ++ ++ if (port->mac_data.phy_dev && !(port->flags & MVPP2_F_IF_MUSDK)) ++ mv_pp2x_tx_start_all_queues(port->dev); ++ ++ mv_pp2x_egress_enable(port); ++ mv_pp2x_ingress_enable(port); ++ /* Unmask link_event */ ++ if (port->priv->pp2_version != PPV21 && !(port->flags & MVPP2_F_LOOPBACK)) { ++ mv_gop110_port_events_unmask(gop, mac); ++ port->mac_data.flags |= MV_EMAC_F_PORT_UP; ++ } ++} ++ ++/* Drain pending packets */ ++static void mv_pp2x_send_pend_aggr_txq(void *arg) ++{ ++ struct mv_pp2x_port *port = arg; ++ struct mv_pp2x_aggr_tx_queue *aggr_txq; ++ int txq_id, address_space; ++ struct mv_pp2x_tx_queue *txq; ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ bool free_aggr = false; ++ ++ /* Flush coalesced tx-done packets for handling */ ++ if (port->interrupt_tx_done) ++ mv_pp2x_tx_done_pkts_coal_set_val(port, smp_processor_id(), 0); ++ ++ /* To avoid scheduling of pending packets on same address_space twice: ++ * Single mode only fist of all vectors related to this sub vec will drain pending packets ++ * Milti mode: hot CPU's - only fist of all vectors related to this sub vec will drain pending packets ++ * cold CPU's - only fist cold CPU will drain pending packets. ++ * Single resource mode: Only CPU 0 will drain pending packets. ++ */ ++ if (mv_pp2x_queue_mode == MVPP2_SINGLE_RESOURCE_MODE) { ++ /* Only CPU0 will drain aggregated queue */ ++ if (smp_processor_id()) ++ return; ++ address_space = 0; ++ } else if (mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) { ++ address_space = QV_CPU_2_ADR_SP(smp_processor_id(), port->priv->pp2_cfg.num_of_ap); ++ if ((address_space * port->priv->pp2_cfg.num_of_ap) != smp_processor_id()) ++ return; ++ } else if (smp_processor_id() < port->priv->rx_count) { ++ address_space = QV_CPU_2_ADR_SP(smp_processor_id(), (port->priv->rx_count / MVPP2_MAX_CPUS_IN_AP)); ++ if ((address_space * (port->priv->rx_count / MVPP2_MAX_CPUS_IN_AP)) != smp_processor_id()) ++ return; ++ } else { ++ /* Only first cold CPU will drain aggregated queue */ ++ address_space = port->priv->rx_count; ++ if (smp_processor_id() != address_space) ++ return; ++ } ++ ++ aggr_txq = &port->priv->aggr_txqs[address_space]; ++ ++ for (txq_id = 0; txq_id < port->num_tx_queues; txq_id++) { ++ txq = port->txqs[txq_id]; ++ txq_pcpu = &txq->pcpu[address_space]; ++ if (mv_pp2x_txq_free_count(txq_pcpu) != txq->size) { ++ free_aggr = true; ++ break; ++ } ++ } ++ ++ if (!aggr_txq->sw_count || !free_aggr) ++ return; /* no pendings */ ++ ++ /* Schedule Drain over the same tasklet-context ++ * which the regular TX is using (refer mv_pp2x_tx_send_proc_cb). ++ * So the Drain from the stop_dev and TX are unpreemptive and correct. ++ */ ++ cp_pcpu = port->priv->pcpu[address_space]; ++ tasklet_schedule(&cp_pcpu->tx_tasklet); ++} ++ ++/* Set hw internals when stopping port */ ++void mv_pp2x_stop_dev(struct mv_pp2x_port *port) ++{ ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ int i; ++ ++ /* Stop new packets arriving from RX-interrupts and Linux-TX */ ++ mv_pp2x_ingress_disable(port); ++ mv_pp2x_tx_stop_all_queues(port->dev); ++ ++ /* Drain pending aggregated TXQ on all CPUs */ ++ on_each_cpu(mv_pp2x_send_pend_aggr_txq, port, 1); ++ msleep(200); /* yield and wait for tx-tasklet and HW idle */ ++ netif_carrier_off(port->dev); ++ ++ /* Disable interrupts on all CPUs */ ++ mv_pp2x_port_interrupts_disable(port); ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ mv_pp22_port_uio_interrupts_disable(port); ++ ++ mv_pp2x_port_napi_disable(port); ++ mv_pp2x_egress_disable(port); ++ ++ if (port->comphy) ++ for (i = 0; i < port->num_serdes_lanes; i++) ++ phy_power_off(port->comphy[i]); ++ ++ if (port->priv->pp2_version == PPV21) { ++ mv_pp21_port_disable(port); ++ } else { ++ if (!(port->flags & MVPP2_F_LOOPBACK)) { ++ mv_gop110_port_events_mask(gop, mac); ++ mv_gop110_port_disable(gop, mac, port); ++ port->mac_data.flags &= ~MV_EMAC_F_LINK_UP; ++ port->mac_data.flags &= ~MV_EMAC_F_PORT_UP; ++ } ++ } ++ ++ if (port->mac_data.phy_dev) ++ phy_stop(port->mac_data.phy_dev); ++ else ++ if (!(port->flags & MVPP2_F_LOOPBACK)) ++ tasklet_kill(&port->link_change_tasklet); ++} ++ ++/* Return positive if MTU is valid */ ++static int mv_pp2x_check_mtu_valid(struct net_device *dev, int mtu) ++{ ++ if (mtu < 68) { ++ netdev_err(dev, "cannot change mtu to less than 68\n"); ++ return -EINVAL; ++ } ++ ++ if (MVPP2_RX_PKT_SIZE(mtu) > MVPP2_BM_JUMBO_PKT_SIZE) { ++ netdev_info(dev, "illegal MTU value %d, round to 9704\n", mtu); ++ mtu = MVPP2_RX_MTU_SIZE(MVPP2_BM_JUMBO_PKT_SIZE); ++ } ++ ++ return mtu; ++} ++ ++int mv_pp2x_check_ringparam_valid(struct net_device *dev, ++ struct ethtool_ringparam *ring) ++{ ++ u16 new_rx_pending = ring->rx_pending; ++ u16 new_tx_pending = ring->tx_pending; ++ ++ if (ring->rx_pending == 0 || ring->tx_pending == 0) ++ return -EINVAL; ++ ++ if (ring->rx_pending > MVPP2_MAX_RXD) ++ new_rx_pending = MVPP2_MAX_RXD; ++ else if (!IS_ALIGNED(ring->rx_pending, 16)) ++ new_rx_pending = ALIGN(ring->rx_pending, 16); ++ ++ if (ring->tx_pending > MVPP2_MAX_TXD) ++ new_tx_pending = MVPP2_MAX_TXD; ++ else if (!IS_ALIGNED(ring->tx_pending, 32)) ++ new_tx_pending = ALIGN(ring->tx_pending, 32); ++ ++ if (ring->rx_pending != new_rx_pending) { ++ netdev_info(dev, "illegal Rx ring size value %d, round to %d\n", ++ ring->rx_pending, new_rx_pending); ++ ring->rx_pending = new_rx_pending; ++ } ++ ++ if (ring->tx_pending != new_tx_pending) { ++ netdev_info(dev, "illegal Tx ring size value %d, round to %d\n", ++ ring->tx_pending, new_tx_pending); ++ ring->tx_pending = new_tx_pending; ++ } ++ ++ return 0; ++} ++ ++void mv_pp2x_check_queue_size_valid(struct mv_pp2x_port *port) ++{ ++ u16 tx_queue_size = port->tx_ring_size; ++ u16 rx_queue_size = port->rx_ring_size; ++ ++ if (tx_queue_size == 0) ++ port->tx_ring_size = MVPP2_MAX_TXD; ++ ++ if (rx_queue_size == 0) ++ port->rx_ring_size = MVPP2_MAX_RXD; ++ ++ if (port->tx_ring_size > MVPP2_MAX_TXD) ++ port->tx_ring_size = MVPP2_MAX_TXD; ++ else if (!IS_ALIGNED(port->tx_ring_size, 16)) ++ port->tx_ring_size = ALIGN(port->tx_ring_size, 16); ++ ++ if (port->rx_ring_size > MVPP2_MAX_RXD) ++ port->rx_ring_size = MVPP2_MAX_RXD; ++ else if (!IS_ALIGNED(port->rx_ring_size, 16)) ++ port->rx_ring_size = ALIGN(port->rx_ring_size, 16); ++ ++ if (tx_queue_size != port->tx_ring_size) ++ pr_err("illegal Tx queue size value %d, round to %d\n", ++ tx_queue_size, port->tx_ring_size); ++ ++ if (rx_queue_size != port->rx_ring_size) ++ pr_err("illegal Rx queue size value %d, round to %d\n", ++ rx_queue_size, port->rx_ring_size); ++} ++ ++static int mv_pp2x_phy_connect(struct mv_pp2x_port *port) ++{ ++ struct phy_device *phy_dev; ++ void (*fp)(struct net_device *); ++ ++ if (port->priv->pp2_version == PPV21) ++ fp = mv_pp21_link_event; ++ else ++ fp = mv_pp22_link_event; ++ ++ phy_dev = of_phy_connect(port->dev, port->mac_data.phy_node, ++ fp, 0, port->mac_data.phy_mode); ++ if (!phy_dev) { ++ dev_err(port->dev->dev.parent, "port ID: %d cannot connect to phy\n", port->id); ++ return -ENODEV; ++ } ++ phy_dev->supported &= PHY_GBIT_FEATURES; ++ phy_dev->supported &= ~SUPPORTED_1000baseT_Half; /* 1G-Half not supported */ ++ ++ phy_dev->advertising = phy_dev->supported; ++ ++ port->mac_data.phy_dev = phy_dev; ++ port->mac_data.link = 0; ++ port->mac_data.duplex = 0; ++ port->mac_data.speed = 0; ++ ++ return 0; ++} ++ ++static void mv_pp2x_phy_disconnect(struct mv_pp2x_port *port) ++{ ++ if (port->mac_data.phy_dev) { ++ phy_disconnect(port->mac_data.phy_dev); ++ port->mac_data.phy_dev = NULL; ++ } ++} ++ ++int mv_pp2x_open_cls(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ unsigned char mac_bcast[ETH_ALEN] = { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int err = 0; ++ u32 cpu_width = 0, cos_width = 0, port_rxq_width = 0; ++ u8 bound_cpu_first_rxq; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&hw->cls_spinlock, flags); ++ ++ /* Calculate width */ ++ mv_pp2x_width_calc(port, &cpu_width, &cos_width, &port_rxq_width); ++ if (cpu_width + cos_width > port_rxq_width) { ++ err = -1; ++ netdev_err(dev, "cpu or cos queue width invalid\n"); ++ goto exit; ++ } ++ ++ err = mv_pp2x_prs_mac_da_accept(port, mac_bcast, true); ++ if (err) { ++ netdev_err(dev, "mv_pp2x_prs_mac_da_accept BC failed\n"); ++ goto exit; ++ } ++ ++ err = mv_pp2x_prs_mac_da_accept(port, dev->dev_addr, true); ++ if (err) { ++ netdev_err(dev, "mv_pp2x_prs_mac_da_accept M2M failed\n"); ++ goto exit; ++ } ++ err = mv_pp2x_prs_tag_mode_set(hw, port->id, MVPP2_TAG_TYPE_MH); ++ ++ if (err) { ++ netdev_err(dev, "mv_pp2x_prs_tag_mode_set failed\n"); ++ goto exit; ++ } ++ ++ err = mv_pp2x_prs_def_flow(port); ++ if (err) { ++ netdev_err(dev, "mv_pp2x_prs_def_flow failed\n"); ++ goto exit; ++ } ++ ++ err = mv_pp2x_prs_flow_set(port); ++ if (err) { ++ netdev_err(dev, "mv_pp2x_prs_flow_set failed\n"); ++ goto exit; ++ } ++ ++ err = mv_pp2x_update_flow_info(hw); ++ if (err) { ++ netdev_err(port->dev, "cannot update flow info\n"); ++ goto exit; ++ } ++ ++ /* Set CoS classifier */ ++ err = mv_pp2x_cos_classifier_set(port, cos_classifer); ++ if (err) { ++ netdev_err(port->dev, "cannot set cos classifier\n"); ++ goto exit; ++ } ++ ++ /* Init C2 rules */ ++ bound_cpu_first_rxq = mv_pp2x_bound_cpu_first_rxq_calc(port); ++ err = mv_pp2x_cls_c2_rule_set(port, bound_cpu_first_rxq); ++ if (err) { ++ netdev_err(port->dev, "cannot init C2 rules\n"); ++ goto exit; ++ } ++ ++ if (port->priv->pp2_version == PPV21) ++ goto exit; ++ ++ /* Assign rss table for rxq belong to this port */ ++ err = mv_pp22_rss_rxq_set(port, cos_width); ++ if (err) { ++ netdev_err(port->dev, "cannot allocate rss table for rxq\n"); ++ goto exit; ++ } ++ ++ /* RSS related config */ ++ if (port->priv->pp2_cfg.queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ /* Set RSS mode */ ++ err = mv_pp22_rss_mode_set(port, port->rss_cfg.rss_mode); ++ if (err) { ++ netdev_err(port->dev, "cannot set rss mode\n"); ++ goto exit; ++ } ++ ++ /* Init RSS table */ ++ err = mv_pp22_rss_rxfh_indir_set(port); ++ if (err) { ++ netdev_err(port->dev, "cannot init rss rxfh indir\n"); ++ goto exit; ++ } ++ ++ /* Set rss default CPU only when rss enabled */ ++ if (port->rss_cfg.rss_en) { ++ err = mv_pp22_rss_default_cpu_set(port, ++ port->rss_cfg.dflt_cpu); ++ if (err) { ++ netdev_err(port->dev, "cannot set rss default cpu\n"); ++ goto exit; ++ } ++ } ++ } ++ ++exit: ++ spin_unlock_irqrestore(&hw->cls_spinlock, flags); ++ ++ return err; ++} ++ ++/* Mask the current CPU's Rx/Tx interrupts */ ++static inline void mv_pp21_interrupts_mask(void *arg) ++{ ++ struct mv_pp2x_port *port = arg; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_RX_TX_MASK_REG(port->id), 0); ++} ++ ++/* Unmask the current CPU's Rx/Tx interrupts */ ++static inline void mv_pp21_interrupts_unmask(void *arg) ++{ ++ struct mv_pp2x_port *port = arg; ++ u32 val; ++ ++ val = MVPP2_CAUSE_MISC_SUM_MASK | MVPP21_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ /* Don't unmask Tx done interrupts for ports working in Netmap mode*/ ++ if (!(port->flags & MVPP2_F_IFCAP_NETMAP) && port->priv->pp2xdata->interrupt_tx_done) ++ val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; ++ ++ mv_pp2x_write(&port->priv->hw, MVPP2_ISR_RX_TX_MASK_REG(port->id), val); ++} ++ ++/* Mask the private Rx/Tx interrupts */ ++static inline void mv_pp22_private_interrupt_mask(struct mv_pp2x_port *port, int address_space) ++{ ++ mv_pp2x_relaxed_write(&port->priv->hw, MVPP2_ISR_RX_TX_MASK_REG(port->id), 0, address_space); ++ mv_pp2x_relaxed_write(&port->priv->hw, MV_PP22_ISR_RX_ERR_MASK_REG(port->id), 0, address_space); ++} ++ ++/* Unmask he private Rx/Tx interrupts */ ++static inline void mv_pp22_private_interrupt_unmask(struct mv_pp2x_port *port, int address_space) ++{ ++ u32 val; ++ ++ val = MVPP2_CAUSE_MISC_SUM_MASK | MVPP22_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; ++ /* Don't unmask Tx done interrupts for ports working in Netmap mode*/ ++ if (!(port->flags & MVPP2_F_IFCAP_NETMAP) && port->priv->pp2xdata->interrupt_tx_done) ++ val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; ++ ++ mv_pp2x_relaxed_write(&port->priv->hw, MVPP2_ISR_RX_TX_MASK_REG(port->id), val, address_space); ++ mv_pp2x_relaxed_write(&port->priv->hw, MV_PP22_ISR_RX_ERR_MASK_REG(port->id), ++ MV_PP22_ISR_RX_ERR_CAUSE_NONOCC_MASK, address_space); ++} ++ ++/* Mask shared and private Rx/Tx interrupts */ ++static inline void mv_pp22_interrupts_mask(struct mv_pp2x_port *port) ++{ ++ struct queue_vector *q_vec = &port->q_vector[0]; ++ int i; ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ if (q_vec[i].qv_type == MVPP2_RX_SHARED) { ++ mv_pp22_thread_write(&port->priv->hw, q_vec[i].sw_thread_id, ++ MVPP2_ISR_RX_TX_MASK_REG(port->id), 0); ++ mv_pp22_thread_write(&port->priv->hw, ++ q_vec[i].sw_thread_id, ++ MV_PP22_ISR_RX_ERR_MASK_REG(port->id), 0); ++ } else { ++ mv_pp22_private_interrupt_mask(port, q_vec[i].sw_thread_id); ++ } ++ } ++} ++ ++/* Unmask shared and private Rx/Tx interrupts */ ++static inline void mv_pp22_interrupts_unmask(struct mv_pp2x_port *port) ++{ ++ struct queue_vector *q_vec = &port->q_vector[0]; ++ int i; ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ if (q_vec[i].qv_type == MVPP2_RX_SHARED) { ++ mv_pp22_thread_write(&port->priv->hw, q_vec[i].sw_thread_id, ++ MVPP2_ISR_RX_TX_MASK_REG(port->id), MVPP22_CAUSE_RXQ_OCCUP_DESC_ALL_MASK); ++ mv_pp22_thread_write(&port->priv->hw, ++ q_vec[i].sw_thread_id, ++ MV_PP22_ISR_RX_ERR_MASK_REG(port->id), ++ MV_PP22_ISR_RX_ERR_CAUSE_NONOCC_MASK); ++ } else { ++ mv_pp22_private_interrupt_unmask(port, q_vec[i].sw_thread_id); ++ } ++ } ++} ++ ++int mv_pp2x_open(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int err; ++ ++ set_device_base_address(dev); ++ ++ /* Allocate the Rx/Tx queues */ ++ err = mv_pp2x_setup_rxqs(port); ++ if (err) { ++ netdev_err(port->dev, "cannot allocate Rx queues\n"); ++ return err; ++ } ++ err = mv_pp2x_setup_txqs(port); ++ if (err) { ++ netdev_err(port->dev, "cannot allocate Tx queues\n"); ++ goto err_cleanup_rxqs; ++ } ++ err = mv_pp2x_setup_irqs(dev, port); ++ if (err) { ++ netdev_err(port->dev, "cannot allocate irq's\n"); ++ goto err_cleanup_txqs; ++ } ++#ifdef CONFIG_UIO ++ if (port->flags & MVPP2_F_IF_MUSDK) { ++ err = mv_pp22_setup_uio_irqs(dev, port); ++ if (err) { ++ netdev_err(port->dev, "cannot allocate uio irq's\n"); ++ goto err_free_all; ++ } ++ } ++#endif ++ ++ /* Only Mvpp22&23 support hot plug feature */ ++ if (port->priv->pp2_version != PPV21 && !(port->flags & (MVPP2_F_IF_MUSDK | MVPP2_F_LOOPBACK))) { ++ register_hotcpu_notifier(&port->port_hotplug_nb); ++ port->port_hotplugged = true; ++ } ++ ++ /* In default link is down */ ++ netif_carrier_off(port->dev); ++ ++ if (!(port->flags & MVPP2_F_IF_MUSDK)) { ++ /* Unmask interrupts on all CPUs */ ++ if (port->priv->pp2_version == PPV21) ++ on_each_cpu(mv_pp21_interrupts_unmask, port, 1); ++ else ++ /* Unmask shared and private interrupts */ ++ mv_pp22_interrupts_unmask(port); ++ ++ /* Port is init in uboot */ ++ } ++ if ((port->priv->pp2_version != PPV21) && !(port->flags & MVPP2_F_LOOPBACK)) ++ mvcpn110_mac_hw_init(port); ++ ++ mv_pp2x_tx_done_init_on_open(port, true); ++ ++ mv_pp2x_start_dev(port); ++ ++ /* Before rxq and port init, all ingress packets should be blocked ++ * in classifier ++ */ ++ if (!(port->flags & MVPP2_F_IF_MUSDK)) { ++ err = mv_pp2x_open_cls(dev); ++ if (err) ++ goto err_free_all; ++ } ++ ++ return 0; ++ ++err_free_all: ++ mv_pp2x_cleanup_irqs(port); ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ mv_pp22_cleanup_uio_irqs(port); ++err_cleanup_txqs: ++ mv_pp2x_cleanup_txqs(port); ++err_cleanup_rxqs: ++ mv_pp2x_cleanup_rxqs(port); ++ return err; ++} ++ ++int mv_pp2x_stop(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ ++ mv_pp2x_stop_dev(port); ++ ++ if (!(port->flags & MVPP2_F_IF_MUSDK)) { ++ /* Mask interrupts on all CPUs */ ++ if (port->priv->pp2_version == PPV21) ++ on_each_cpu(mv_pp21_interrupts_mask, port, 1); ++ else ++ /* Mask shared and private interrupts */ ++ mv_pp22_interrupts_mask(port); ++ } ++ mv_pp2x_cleanup_irqs(port); ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ mv_pp22_cleanup_uio_irqs(port); ++ ++ if (port->port_hotplugged) ++ unregister_hotcpu_notifier(&port->port_hotplug_nb); ++ ++ mv_pp2x_tx_done_init_on_open(port, false); ++ ++ mv_pp2x_cleanup_rxqs(port); ++ mv_pp2x_cleanup_txqs(port); ++ ++ return 0; ++} ++ ++static void mv_pp2x_set_rx_promisc(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int id = port->id; ++ ++ /* Enter promisc mode */ ++ /* Accept all: Multicast + Unicast */ ++ mv_pp2x_prs_mac_uc_promisc_set(hw, id, true); ++ mv_pp2x_prs_mac_mc_promisc_set(hw, id, true); ++ /* Remove all port->id's mcast enries */ ++ mv_pp2x_prs_mac_entry_del(port, MVPP2_PRS_MAC_MC, MVPP2_DEL_MAC_ALL); ++ /* Remove all port->id's ucast enries except M2M entry */ ++ mv_pp2x_prs_mac_entry_del(port, MVPP2_PRS_MAC_UC, MVPP2_DEL_MAC_ALL); ++} ++ ++static void mv_pp2x_set_rx_allmulti(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int id = port->id; ++ ++ /* Accept all multicast */ ++ mv_pp2x_prs_mac_mc_promisc_set(hw, id, true); ++ /* Remove all multicast filter entries from parser */ ++ mv_pp2x_prs_mac_entry_del(port, MVPP2_PRS_MAC_MC, MVPP2_DEL_MAC_ALL); ++} ++ ++static void mv_pp2x_set_rx_uc_multi(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int id = port->id; ++ ++ /* Accept all unicast */ ++ mv_pp2x_prs_mac_uc_promisc_set(hw, id, true); ++ /* Remove all unicast filter entries from parser */ ++ mv_pp2x_prs_mac_entry_del(port, MVPP2_PRS_MAC_UC, MVPP2_DEL_MAC_ALL); ++} ++ ++/* register unicast and multicast addresses */ ++static void mv_pp2x_set_rx_mode(struct net_device *dev) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ struct netdev_hw_addr *ha; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ int id = port->id; ++ int err; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&hw->cls_spinlock, flags); ++ ++ if (dev->flags & IFF_PROMISC) { ++ mv_pp2x_set_rx_promisc(port); ++ } else { ++ /* Put dev into UC promisc if MAC num greater than uc filter max */ ++ if (netdev_uc_count(dev) > port->priv->pp2_cfg.uc_filter_max) { ++ mv_pp2x_set_rx_uc_multi(port); ++ } else { ++ /* Remove old enries not in uc list except M2M entry */ ++ mv_pp2x_prs_mac_entry_del(port, ++ MVPP2_PRS_MAC_UC, ++ MVPP2_DEL_MAC_NOT_IN_LIST); ++ /* Add all entries into to uc mac addr filter list */ ++ netdev_for_each_uc_addr(ha, dev) { ++ err = mv_pp2x_prs_mac_da_accept(port, ++ ha->addr, true); ++ if (err) ++ netdev_err(dev, ++ "[%2x:%2x:%2x:%2x:%2x:%x]add fail\n", ++ ha->addr[0], ha->addr[1], ++ ha->addr[2], ha->addr[3], ++ ha->addr[4], ha->addr[5]); ++ } ++ /* Leave promisc mode */ ++ mv_pp2x_prs_mac_uc_promisc_set(hw, id, false); ++ } ++ ++ if (dev->flags & IFF_ALLMULTI) { ++ mv_pp2x_set_rx_allmulti(port); ++ } else { ++ /* Put dev allmulti if MAC num exceeds mc filter max */ ++ if (netdev_mc_count(dev) > ++ port->priv->pp2_cfg.mc_filter_max) { ++ mv_pp2x_set_rx_allmulti(port); ++ goto exit; ++ } ++ /* Remove old mcast entries not in mc list */ ++ mv_pp2x_prs_mac_entry_del(port, ++ MVPP2_PRS_MAC_MC, ++ MVPP2_DEL_MAC_NOT_IN_LIST); ++ /* Add all entries into to mc mac filter list */ ++ if (!netdev_mc_empty(dev)) { ++ netdev_for_each_mc_addr(ha, dev) { ++ err = ++ mv_pp2x_prs_mac_da_accept(port, ++ ha->addr, ++ true); ++ if (err) ++ netdev_err(dev, ++ "MAC[%2x:%2x:%2x:%2x:%2x:%2x] add failed\n", ++ ha->addr[0], ha->addr[1], ++ ha->addr[2], ha->addr[3], ++ ha->addr[4], ha->addr[5]); ++ } ++ } ++ /* Reject other MC mac entries */ ++ mv_pp2x_prs_mac_mc_promisc_set(hw, id, false); ++ } ++ } ++exit: ++ spin_unlock_irqrestore(&hw->cls_spinlock, flags); ++} ++ ++static int mv_pp2x_set_mac_address(struct net_device *dev, void *p) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ const struct sockaddr *addr = p; ++ int err = 0; ++ unsigned long flags = 0; ++ ++ if (!is_valid_ether_addr(addr->sa_data)) { ++ err = -EADDRNOTAVAIL; ++ goto error; ++ } ++ ++ spin_lock_irqsave(&port->priv->hw.cls_spinlock, flags); ++ err = mv_pp2x_prs_update_mac_da(dev, addr->sa_data); ++ if (!err) ++ goto exit; ++ /* Reconfigure parser to accept the original MAC address */ ++ err = mv_pp2x_prs_update_mac_da(dev, dev->dev_addr); ++ ++error: ++ netdev_err(dev, "fail to change MAC address\n"); ++exit: ++ spin_unlock_irqrestore(&port->priv->hw.cls_spinlock, flags); ++ ++ return err; ++} ++ ++static int mv_pp2x_change_mtu(struct net_device *dev, int mtu) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int err; ++ ++#ifdef DEV_NETMAP ++ if (port->flags & MVPP2_F_IFCAP_NETMAP) { ++ netdev_err(dev, "MTU can not be modified for port configured to Netmap mode\n"); ++ return -EPERM; ++ } ++#endif ++ if (port->flags & MVPP2_F_IF_MUSDK) { ++ netdev_err(dev, "MTU can not be modified for port in MUSDK mode\n"); ++ return -EPERM; ++ } ++ ++ mtu = mv_pp2x_check_mtu_valid(dev, mtu); ++ if (mtu < 0) ++ return -EINVAL; ++ ++ if (netif_running(dev)) ++ mv_pp2x_stop_dev(port); ++ ++ /* Set new pkt_size before bm-update */ ++ port->pkt_size = MVPP2_RX_PKT_SIZE(mtu); ++ err = mv_pp2x_bm_update_mtu(dev, mtu); ++ if (!err) { ++ dev->mtu = mtu; ++ } else { ++ /* Keep original MTU and restore original pkt_size */ ++ port->pkt_size = MVPP2_RX_PKT_SIZE(mtu); ++ netdev_err(dev, "fail to change MTU\n"); ++ } ++ if (netif_running(dev)) ++ mv_pp2x_start_dev(port); ++ return err; ++} ++ ++static int mv_pp2x_rx_add_vid(struct net_device *dev, u16 proto, u16 vid) ++{ ++ int err; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ unsigned long flags = 0; ++ ++ if (vid >= VLAN_N_VID) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&port->priv->hw.cls_spinlock, flags); ++ ++ err = mv_pp2x_prs_vid_entry_accept(dev, proto, vid, true); ++ ++ spin_unlock_irqrestore(&port->priv->hw.cls_spinlock, flags); ++ ++ return err; ++} ++ ++static int mv_pp2x_rx_kill_vid(struct net_device *dev, u16 proto, u16 vid) ++{ ++ int err; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ unsigned long flags = 0; ++ ++ if (vid >= VLAN_N_VID) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&port->priv->hw.cls_spinlock, flags); ++ ++ err = mv_pp2x_prs_vid_entry_accept(dev, proto, vid, false); ++ ++ spin_unlock_irqrestore(&port->priv->hw.cls_spinlock, flags); ++ ++ return err; ++} ++ ++static struct rtnl_link_stats64 * ++mv_pp2x_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ unsigned int start; ++ int cpu; ++ ++ for_each_online_cpu(cpu) { ++ struct mv_pp2x_pcpu_stats *cpu_stats; ++ u64 rx_packets; ++ u64 rx_bytes; ++ u64 tx_packets; ++ u64 tx_bytes; ++ ++ cpu_stats = per_cpu_ptr(port->stats, cpu); ++ do { ++ start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); ++ rx_packets = cpu_stats->rx_packets; ++ rx_bytes = cpu_stats->rx_bytes; ++ tx_packets = cpu_stats->tx_packets; ++ tx_bytes = cpu_stats->tx_bytes; ++ } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); ++ ++ stats->rx_packets += rx_packets; ++ stats->rx_bytes += rx_bytes; ++ stats->tx_packets += tx_packets; ++ stats->tx_bytes += tx_bytes; ++ } ++ ++ stats->rx_errors = dev->stats.rx_errors; ++ stats->rx_dropped = dev->stats.rx_dropped; ++ stats->tx_dropped = dev->stats.tx_dropped; ++ ++ return stats; ++} ++ ++static int mv_pp2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ++{ ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ int ret = 0; ++ ++ if (!port->mac_data.phy_dev) ++ return -ENOTSUPP; ++ ret = phy_mii_ioctl(port->mac_data.phy_dev, ifr, cmd); ++ if (!ret) ++ mv_pp21_link_event(dev); ++ return ret; ++} ++ ++/*of_irq_count is not exported */ ++int mv_pp2x_of_irq_count(struct device_node *dev) ++{ ++ struct of_phandle_args irq; ++ int nr = 0; ++ ++ while (of_irq_parse_one(dev, nr, &irq) == 0) ++ nr++; ++ ++ return nr; ++} ++ ++/* Currently only support LK-3.18 and above, no back support */ ++static int mv_pp2x_netdev_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ netdev_features_t changed = dev->features ^ features; ++ struct mv_pp2x_port *port = netdev_priv(dev); ++ unsigned long flags = 0; ++ ++ /* dev->features is not changed */ ++ if (!changed) ++ return 0; ++ ++ if (changed & NETIF_F_RXHASH) { ++ if (port->flags & MVPP2_F_IF_MUSDK) { ++ netdev_err(dev, "Hashing can not be modified for port in MUSDK mode\n"); ++ return -EPERM; ++ } ++ ++ spin_lock_irqsave(&port->priv->hw.cls_spinlock, flags); ++ if (features & NETIF_F_RXHASH) { ++ /* Enable RSS */ ++ mv_pp22_rss_enable(port, true); ++ } else { ++ /* Disable RSS */ ++ mv_pp22_rss_enable(port, false); ++ } ++ spin_unlock_irqrestore(&port->priv->hw.cls_spinlock, flags); ++ } ++ ++ if (changed & NETIF_F_TSO) { ++ if (features & NETIF_F_TSO) ++ port->txq_stop_limit = TSO_TXQ_LIMIT; ++ else ++ port->txq_stop_limit = TXQ_LIMIT; ++ } ++ ++ dev->features = features; ++ ++ return 0; ++} ++ ++u16 mv_pp2x_select_queue(struct net_device *dev, struct sk_buff *skb, ++ void *accel_priv, select_queue_fallback_t fallback) ++ ++{ ++ /* If packet in coming from Rx -> RxQ = TxQ, callback function used for packets from CPU Tx */ ++ if (skb->queue_mapping) ++ return ((skb->queue_mapping - 1) % mv_pp2x_txq_number) + (smp_processor_id() * mv_pp2x_txq_number); ++ else ++ return mv_pp2x_txq_number * fallback(dev, skb); ++} ++ ++/* Device ops */ ++static const struct net_device_ops mv_pp2x_netdev_ops = { ++ .ndo_open = mv_pp2x_open, ++ .ndo_stop = mv_pp2x_stop, ++ .ndo_start_xmit = mv_pp2x_tx, ++ .ndo_select_queue = mv_pp2x_select_queue, ++ .ndo_set_rx_mode = mv_pp2x_set_rx_mode, ++ .ndo_set_mac_address = mv_pp2x_set_mac_address, ++ .ndo_change_mtu = mv_pp2x_change_mtu, ++ .ndo_get_stats64 = mv_pp2x_get_stats64, ++ .ndo_do_ioctl = mv_pp2x_ioctl, ++ .ndo_set_features = mv_pp2x_netdev_set_features, ++ .ndo_vlan_rx_add_vid = mv_pp2x_rx_add_vid, ++ .ndo_vlan_rx_kill_vid = mv_pp2x_rx_kill_vid, ++}; ++ ++/* Driver initialization */ ++ ++static void mv_pp21_port_power_up(struct mv_pp2x_port *port) ++{ ++ mv_pp21_port_mii_set(port); ++ mv_pp21_port_periodic_xon_disable(port); ++ mv_pp21_port_fc_adv_enable(port); ++ mv_pp21_port_reset(port); ++} ++ ++static int mv_pp2x_port_txqs_init(struct device *dev, ++ struct mv_pp2x_port *port) ++{ ++ int queue, cpu; ++ struct mv_pp2x_txq_pcpu *txq_pcpu; ++ ++ if (port->priv->pp2xdata->interrupt_tx_done) { ++ /* PP2 HW-unit's timer */ ++ port->tx_time_coal = MVPP2_TXDONE_COAL_USEC; ++ } else { ++ /* High Resolutun linux timer */ ++ if (port->id == 0) /* Fast 10G port */ ++ port->tx_time_coal = MVPP2_TXDONE_HRTIMER_USEC; ++ else ++ port->tx_time_coal = MVPP2_TXDONE_HRTIMER_USEC * 4; ++ } ++ ++ for (queue = 0; queue < port->num_tx_queues; queue++) { ++ int queue_phy_id = mv_pp2x_txq_phys(port->id, queue); ++ struct mv_pp2x_tx_queue *txq; ++ ++ txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL); ++ if (!txq) ++ return -ENOMEM; ++ ++ txq->pcpu = devm_kcalloc(dev, mv_pp2x_used_addr_spaces, ++ sizeof(struct mv_pp2x_txq_pcpu), ++ GFP_KERNEL); ++ if (!txq->pcpu) ++ return(-ENOMEM); ++ ++ txq->id = queue_phy_id; ++ txq->log_id = queue; ++ txq->pkts_coal = MVPP2_TXDONE_COAL_PKTS; ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ txq_pcpu = &txq->pcpu[cpu]; ++ txq_pcpu->cpu = cpu; ++ } ++ ++ port->txqs[queue] = txq; ++ } ++ ++ return 0; ++} ++ ++static int mv_pp2x_port_rxqs_init(struct device *dev, ++ struct mv_pp2x_port *port) ++{ ++ int queue; ++ ++ /* Allocate and initialize Rx queue for this port */ ++ for (queue = 0; queue < port->num_rx_queues; queue++) { ++ struct mv_pp2x_rx_queue *rxq; ++ ++ /* Map physical Rx queue to port's logical Rx queue */ ++ rxq = devm_kzalloc(dev, sizeof(*rxq), GFP_KERNEL); ++ if (!rxq) ++ return(-ENOMEM); ++ /* Map this Rx queue to a physical queue */ ++ rxq->id = port->first_rxq + queue; ++ rxq->port = port->id; ++ rxq->log_id = queue; ++ ++ port->rxqs[queue] = rxq; ++ } ++ ++ return 0; ++} ++ ++static void mv_pp21_port_queue_vectors_init(struct mv_pp2x_port *port) ++{ ++ struct queue_vector *q_vec = &port->q_vector[0]; ++ ++ q_vec[0].first_rx_queue = 0; ++ q_vec[0].num_rx_queues = port->num_rx_queues; ++ q_vec[0].parent = port; ++ q_vec[0].pending_cause_rx = 0; ++ q_vec[0].qv_type = MVPP2_RX_SHARED; ++ q_vec[0].sw_thread_id = 0; ++ q_vec[0].sw_thread_mask = *cpumask_bits(cpu_online_mask); ++ q_vec[0].irq = port->of_irqs[0]; ++ netif_napi_add(port->dev, &q_vec[0].napi, mv_pp21_poll, ++ NAPI_POLL_WEIGHT); ++ ++ port->num_qvector = 1; ++} ++ ++/* Routine calculate number of private queue vectors */ ++static int mv_pp22_get_num_private_queue_vectors(struct mv_pp2x_port *port) ++{ ++ if (num_active_cpus() > MVPP2_MAX_CPUS_IN_AP || num_active_cpus() > port->priv->rx_count) ++ return min_t(u32, MVPP2_MAX_CPUS_IN_AP, port->priv->rx_count); ++ ++ return num_active_cpus(); ++} ++ ++/* Routine allocate hif(address space) for Kernel */ ++static void mv_pp22_allocate_hif_for_kernel(struct mv_pp2x_hw *hw, int hif_num) ++{ ++ int val; ++ ++ val = mv_pp2x_read(hw, MVPP22_HIF_ALLOCATION_REG); ++ val |= BIT(hif_num); ++ mv_pp2x_write(hw, MVPP22_HIF_ALLOCATION_REG, val); ++} ++ ++/* Routine calculate number of used sub_vector */ ++static struct sub_queue_vector *mv_pp22_allocate_sub_vector(struct mv_pp2x_port *port, struct queue_vector *q_vec, ++ u32 queue_mask) ++{ ++ struct sub_queue_vector *sub_q_vec; ++ ++ sub_q_vec = devm_kzalloc(port->dev->dev.parent, sizeof(*sub_q_vec), GFP_KERNEL); ++ netif_napi_add(port->dev, &sub_q_vec->napi, mv_pp22_poll, NAPI_POLL_WEIGHT); ++ napi_hash_add(&sub_q_vec->napi); ++ sub_q_vec->parent = q_vec; ++ sub_q_vec->queue_mask = queue_mask; ++ ++ return sub_q_vec; ++} ++ ++#ifdef CONFIG_UIO ++static void mv_pp22_uio_queue_vectors_init(struct mv_pp2x_port *port) ++{ ++ struct net_device *net_dev = port->dev; ++ struct device *parent_dev; ++ int i, cur_num_rxqs; ++ char str_common[32]; ++ struct uio_queue_vector *q_vector; ++ u32 cur_int_rx_mask, uio_rxq_mask; ++ ++ /* Allocate all rx_qs for UIO */ ++#define UIO_NUM_RXQS (MVPP22_MAX_NUM_RXQ) ++ ++ uio_rxq_mask = GENMASK((UIO_NUM_RXQS - 1), 0); ++ ++ pr_debug("(%s) %s uio_rxq_mask:0x%x\n", __func__, port->dev->name, uio_rxq_mask); ++ ++ parent_dev = net_dev->dev.parent; ++ ++ snprintf(str_common, sizeof(str_common), "%s.%s", dev_name(parent_dev), net_dev->name); ++ ++ /* Check all existing interrupts */ ++ q_vector = &port->uio.q_vector[0]; ++ for (cur_num_rxqs = 0, i = 0; uio_rxq_mask != 0; i++) { ++ q_vector->irq = port->of_irqs[i]; ++ q_vector->sw_thread_id = i; ++ q_vector->parent = port; ++ ++ q_vector->first_rx_queue = cur_num_rxqs; ++ cur_int_rx_mask = MVPP22_CAUSE_RXQ_OCCUP_DESC_ALL_MASK << q_vector->first_rx_queue; ++ q_vector->queue_mask = uio_rxq_mask & cur_int_rx_mask; ++ q_vector->num_rx_queues = hweight32(q_vector->queue_mask); ++ cur_num_rxqs += q_vector->num_rx_queues; ++ uio_rxq_mask &= ~(q_vector->queue_mask); ++ ++ snprintf(q_vector->irq_name, IRQ_NAME_SIZE, "%s.%s%d", str_common, "uio_int", i); ++ ++ pr_debug("%s, first_q:%d, num_qs:%d, q_mask:0x%x\n", q_vector->irq_name, ++ q_vector->first_rx_queue, q_vector->num_rx_queues, q_vector->queue_mask); ++ ++ q_vector++; ++ port->uio.num_qvector++; ++ } ++} ++#endif ++ ++/* Routine calculate number of used sub_vector */ ++static int mv_pp22_calculate_num_sub_vector(struct mv_pp2x_port *port) ++{ ++ int num_of_sub_vectors = 0, cold_cpu_address_spaces = 0; ++ int num_rx_queues = mv_pp2x_num_cos_queues * num_of_sub_vectors; ++ ++ /* Logic: If system has more than 8 CPU's, more than 1 of sub queue vector should be used ++ * for same interrupt and address space, number of sub queue vector = # of active CPU's / 8. ++ * Procedure also check if in multi queue mode exist cold CPU's. ++ */ ++ if (num_active_cpus() > MVPP2_MAX_CPUS_IN_AP) { ++ num_of_sub_vectors = num_active_cpus() / MVPP2_MAX_CPUS_IN_AP; ++ if (port->priv->rx_count < num_active_cpus() && mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ num_of_sub_vectors = ((port->priv->rx_count / MVPP2_MAX_CPUS_IN_AP) > 1) ? ++ (port->priv->rx_count / MVPP2_MAX_CPUS_IN_AP) : 0; ++ cold_cpu_address_spaces = port->priv->other_count; ++ if (num_rx_queues > MVPP22_MAX_NUM_RXQ) ++ WARN(1, "Number of requested RXQ's greater than amount of phys RXQ's\n"); ++ } else { ++ port->priv->other_count = 0; ++ } ++ } ++ ++ port->priv->other_count = cold_cpu_address_spaces; ++ ++ return num_of_sub_vectors; ++} ++ ++/* This procedure allocated queue vectors and sub queue vectors per driver configurations. ++ * For multi queue mode: ++ * If more than 8 CPU's in system and # of HOT CPU's above 8. More than one sub vector would be ++ * allocated per queue vector. Also for Cold CPU's MVPP2_TX_SHARED interrupt would be allocated. ++ * For example: If system has 16 CPU's and 8 Hot CPU's, then there would be 8 cold CPU's. ++ * For single queue mode: ++ * If more than 8 CPU's in system -> More than one sub vector would be allocated per queue vector ++ * for TX done procedure. All RX will be handled by same MVPP2_RX_SHARED interrupt and on address space. ++ * Single resource mode: Single queue vector, address space and MVPP2_PRIVATE Interrupt allocated. ++ */ ++static void mv_pp22_queue_vectors_init(struct mv_pp2x_port *port) ++{ ++ int address_space, num_private_q_vec, num_of_sub_vectors, i, num_rx_queues; ++ int sw_thread_index = 0, irq_index = 0, cpu_ap_ofset = 0; ++ struct queue_vector *q_vec; ++ struct sub_queue_vector *sub_q_vec; ++ unsigned long queue_mask = 0; ++ ++ bitmap_set(&queue_mask, 0, mv_pp2x_num_cos_queues); ++ ++ port->q_vector = devm_kcalloc(port->dev->dev.parent, MVPP2_MAX_ADDR_SPACES, ++ sizeof(struct queue_vector), GFP_KERNEL); ++ q_vec = port->q_vector; ++ ++ /* Init q_vec locks */ ++ for (address_space = 0; address_space < MVPP2_MAX_ADDR_SPACES; address_space++) ++ spin_lock_init(&q_vec[address_space].hw_mask_lock); ++ ++ /* For single resource mode skip sub vector allocation. ++ * In this mode single queue vector, address space and Interrupt used. ++ */ ++ address_space = 0; ++ if (mv_pp2x_queue_mode == MVPP2_SINGLE_RESOURCE_MODE) ++ goto last_queue_vector; ++ ++ /* Each cpu has queue_vector for private tx_done counters and/or ++ * private rx_queues ++ */ ++ num_private_q_vec = mv_pp22_get_num_private_queue_vectors(port); ++ ++ /* Calculate number of sub queue vectors */ ++ num_of_sub_vectors = mv_pp22_calculate_num_sub_vector(port); ++ ++ for (address_space = 0; address_space < num_private_q_vec; address_space++) { ++ q_vec[address_space].parent = port; ++ q_vec[address_space].qv_type = MVPP2_PRIVATE; ++ q_vec[address_space].sw_thread_id = sw_thread_index++; ++ mv_pp22_allocate_hif_for_kernel(&port->priv->hw, q_vec[address_space].sw_thread_id); ++ q_vec[address_space].sw_thread_mask = (1 << q_vec[address_space].sw_thread_id); ++ q_vec[address_space].pending_cause_rx = 0; ++ q_vec[address_space].num_of_sub_vectors = num_of_sub_vectors; ++ /* TODO: AP ID should be static varible */ ++ q_vec[address_space].ap_id = 0; ++ q_vec[address_space].queue_mask = 0; ++ q_vec[address_space].cold_cpu = false; ++ if (port->interrupt_tx_done || mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) ++ q_vec[address_space].irq = port->of_irqs[irq_index++]; ++ ++ if (mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ /* Check if there singe CPU per queue vector or # of sub vectors should be allocated ++ * for one queue vector. ++ */ ++ if (!num_of_sub_vectors) { ++ q_vec[address_space].num_rx_queues = mv_pp2x_num_cos_queues; ++ q_vec[address_space].first_rx_queue = address_space * mv_pp2x_num_cos_queues; ++ ++ q_vec[address_space].sub_vec = devm_kcalloc(port->dev->dev.parent, 1, ++ sizeof(*q_vec[address_space].sub_vec), GFP_KERNEL); ++ sub_q_vec = mv_pp22_allocate_sub_vector(port, &q_vec[address_space], queue_mask); ++ sub_q_vec->queue_mask |= (queue_mask << MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); ++ q_vec[address_space].queue_mask = sub_q_vec->queue_mask; ++ q_vec[address_space].sub_vec[0] = sub_q_vec; ++ } else { ++ /* Calculate CPU AP offset. AP0: CPU 0-7, AP1 CPU 8-15 and etc */ ++ cpu_ap_ofset = q_vec[address_space].ap_id * MVPP2_MAX_CPUS_IN_AP; ++ num_rx_queues = mv_pp2x_num_cos_queues * num_of_sub_vectors; ++ q_vec[address_space].sub_vec = devm_kcalloc(port->dev->dev.parent, num_of_sub_vectors, ++ sizeof(*q_vec[address_space].sub_vec), GFP_KERNEL); ++ for (i = 0; i < num_of_sub_vectors; i++) { ++ sub_q_vec = mv_pp22_allocate_sub_vector(port, &q_vec[address_space], ++ (queue_mask) << ++ (i * mv_pp2x_num_cos_queues)); ++ sub_q_vec->cpu_id = (address_space + cpu_ap_ofset) * num_of_sub_vectors + i; ++ q_vec[address_space].queue_mask |= sub_q_vec->queue_mask; ++ /* Only first sub_q_vec will handle TX done */ ++ if (i == 0) { ++ sub_q_vec->queue_mask |= ++ (queue_mask << MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); ++ q_vec[address_space].queue_mask |= ++ (queue_mask << MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); ++ } ++ q_vec[address_space].sub_vec[i] = sub_q_vec; ++ } ++ q_vec[address_space].num_rx_queues = num_rx_queues; ++ q_vec[address_space].first_rx_queue = address_space * num_rx_queues; ++ } ++ } else { ++ /* For single queue mode only sub vector will handle TX done for all CPU's in queue vector */ ++ q_vec[address_space].first_rx_queue = 0; ++ q_vec[address_space].num_rx_queues = 0; ++ q_vec[address_space].sub_vec = devm_kcalloc(port->dev->dev.parent, 1, ++ sizeof(*q_vec[address_space].sub_vec), GFP_KERNEL); ++ q_vec[address_space].queue_mask = (queue_mask << MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); ++ sub_q_vec = mv_pp22_allocate_sub_vector(port, &q_vec[address_space], ++ q_vec[address_space].queue_mask); ++ q_vec[address_space].sub_vec[0] = sub_q_vec; ++ } ++ port->num_qvector++; ++ } ++ ++last_queue_vector: ++ if (mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ /* Additional queue_vector's for TX Shared queue vectors */ ++ for (i = address_space; i < (num_private_q_vec + port->priv->other_count); i++) { ++ q_vec[address_space].parent = port; ++ q_vec[address_space].qv_type = MVPP2_TX_SHARED; ++ q_vec[address_space].sw_thread_id = sw_thread_index++; ++ mv_pp22_allocate_hif_for_kernel(&port->priv->hw, q_vec[address_space].sw_thread_id); ++ q_vec[address_space].sw_thread_mask = (1 << q_vec[address_space].sw_thread_id); ++ q_vec[address_space].pending_cause_rx = 0; ++ q_vec[address_space].num_of_sub_vectors = num_of_sub_vectors; ++ /* TODO: AP ID should be static varible */ ++ q_vec[address_space].ap_id = 0; ++ q_vec[address_space].first_rx_queue = 0; ++ q_vec[address_space].num_rx_queues = 0; ++ q_vec[address_space].irq = port->of_irqs[irq_index]; ++ q_vec[address_space].sub_vec = devm_kcalloc(port->dev->dev.parent, 1, ++ sizeof(*q_vec[address_space].sub_vec), GFP_KERNEL); ++ q_vec[address_space].queue_mask = (queue_mask << MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); ++ sub_q_vec = mv_pp22_allocate_sub_vector(port, &q_vec[address_space], ++ q_vec[address_space].queue_mask); ++ q_vec[address_space].sub_vec[0] = sub_q_vec; ++ q_vec[address_space].cold_cpu = true; ++ port->num_qvector++; ++ } ++ } else { ++ /* Additional queue_vector for Shared RX or Single resource mode */ ++ q_vec[address_space].parent = port; ++ q_vec[address_space].sw_thread_id = irq_index; ++ mv_pp22_allocate_hif_for_kernel(&port->priv->hw, q_vec[address_space].sw_thread_id); ++ q_vec[address_space].pending_cause_rx = 0; ++ q_vec[address_space].sub_vec = devm_kcalloc(port->dev->dev.parent, 1, ++ sizeof(*q_vec[address_space].sub_vec), GFP_KERNEL); ++ q_vec[address_space].sw_thread_mask = (1 << q_vec[address_space].sw_thread_id); ++ q_vec[address_space].irq = port->of_irqs[irq_index]; ++ ++ q_vec[address_space].qv_type = MVPP2_RX_SHARED; ++ /* Add TX done queue mask for single resource mode(should handle both TX done and RX) ++ * and set MVPP2_PRIVATE interrupt type(used by singe CPU 0) ++ */ ++ if (mv_pp2x_queue_mode == MVPP2_QDIST_SINGLE_MODE) { ++ q_vec[address_space].qv_type = MVPP2_RX_SHARED; ++ } else { ++ q_vec[address_space].qv_type = MVPP2_PRIVATE; ++ queue_mask |= (queue_mask << MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); ++ } ++ sub_q_vec = mv_pp22_allocate_sub_vector(port, &q_vec[address_space], queue_mask); ++ q_vec[address_space].queue_mask = sub_q_vec->queue_mask; ++ q_vec[address_space].sub_vec[0] = sub_q_vec; ++ q_vec[address_space].first_rx_queue = 0; ++ q_vec[address_space].num_rx_queues = port->num_rx_queues; ++ q_vec[address_space].cold_cpu = false; ++ q_vec[address_space].ap_id = 0; ++ port->num_qvector++; ++ } ++} ++ ++static void mv_pp2x_port_irq_names_update(struct mv_pp2x_port *port) ++{ ++ int i, cpu; ++ struct queue_vector *q_vec = &port->q_vector[0]; ++ char str_common[32]; ++ struct net_device *net_dev = port->dev; ++ struct device *parent_dev; ++ ++ parent_dev = net_dev->dev.parent; ++ ++ snprintf(str_common, sizeof(str_common), "%s.%s", ++ dev_name(parent_dev), net_dev->name); ++ ++ if (port->priv->pp2_version == PPV21) { ++ snprintf(q_vec[0].irq_name, IRQ_NAME_SIZE, "%s", str_common); ++ return; ++ } ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ if (!q_vec[i].irq) ++ continue; ++ if (q_vec[i].qv_type == MVPP2_PRIVATE) { ++ if (!q_vec[i].num_of_sub_vectors) { ++ cpu = QV_THR_2_MASTER_AP_CPU(q_vec[i].ap_id, q_vec[i].sw_thread_id); ++ snprintf(q_vec[i].irq_name, IRQ_NAME_SIZE, "%s.%s%d", ++ str_common, "cpu", cpu); ++ } else { ++ snprintf(q_vec[i].irq_name, IRQ_NAME_SIZE, "%s.%s_%d", ++ str_common, "sw_thread", q_vec[i].sw_thread_id); ++ } ++ } else if (q_vec[i].qv_type == MVPP2_TX_SHARED) { ++ snprintf(q_vec[i].irq_name, IRQ_NAME_SIZE, "%s.%s", ++ str_common, "tx_shared"); ++ } else { ++ snprintf(q_vec[i].irq_name, IRQ_NAME_SIZE, "%s.%s", ++ str_common, "rx_shared"); ++ } ++ } ++ snprintf(port->mac_data.irq_name, IRQ_NAME_SIZE, "%s.%s", str_common, ++ "link"); ++} ++ ++static void mv_pp21x_port_isr_rx_group_cfg(struct mv_pp2x_port *port) ++{ ++ mv_pp21_isr_rx_group_write(&port->priv->hw, port->id, ++ port->num_rx_queues); ++} ++ ++static void mv_pp22_port_isr_rx_group_cfg(struct mv_pp2x_port *port) ++{ ++ int i; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ u32 sw_thr_mask = 0; ++ ++ for (i = 0; i < port->num_qvector; i++) { ++ if (port->q_vector[i].num_rx_queues == 0) ++ continue; ++ mv_pp22_isr_rx_group_write(hw, port->id, ++ port->q_vector[i].sw_thread_id, ++ port->q_vector[i].first_rx_queue, ++ port->q_vector[i].num_rx_queues); ++ ++ sw_thr_mask |= (1 << port->q_vector[i].sw_thread_id); ++ } ++ for (i = 0; i < MVPP2_MAX_ADDR_SPACES; i++) ++ if ((sw_thr_mask & BIT(i)) == 0) ++ mv_pp22_isr_rx_group_write(hw, port->id, i, 0, 0); ++} ++ ++static void mv_pp22_port_uio_isr_rx_group_cfg(struct mv_pp2x_port *port) ++{ ++ int i; ++ struct mv_pp2x_hw *hw = &port->priv->hw; ++ u32 sw_thr_mask = 0; ++ ++ for (i = 0; i < port->uio.num_qvector; i++) { ++ if (port->uio.q_vector[i].num_rx_queues == 0) ++ continue; ++ mv_pp22_isr_rx_group_write(hw, port->id, ++ port->uio.q_vector[i].sw_thread_id, ++ port->uio.q_vector[i].first_rx_queue, ++ port->uio.q_vector[i].num_rx_queues); ++ ++ sw_thr_mask |= (1 << port->uio.q_vector[i].sw_thread_id); ++ } ++ for (i = 0; i < MVPP2_MAX_ADDR_SPACES; i++) ++ if ((sw_thr_mask & BIT(i)) == 0) ++ mv_pp22_isr_rx_group_write(hw, port->id, i, 0, 0); ++} ++ ++static int mv_pp2_init_emac_data(struct mv_pp2x_port *port, ++ struct device_node *emac_node) ++{ ++ struct device_node *fixed_link_node, *phy_node; ++ int phy_mode; ++ u32 speed, id; ++ ++ if (of_property_read_u32(emac_node, "port-id", &id)) ++ return -EINVAL; ++ ++ port->mac_data.gop_index = id; ++ port->mac_data.link_irq = irq_of_parse_and_map(emac_node, 0); ++ ++ phy_mode = of_get_phy_mode(emac_node); ++ ++ if (of_phy_is_fixed_link(emac_node)) { ++ port->mac_data.force_link = true; ++ port->mac_data.link = true; ++ fixed_link_node = of_get_child_by_name(emac_node, "fixed-link"); ++ port->mac_data.duplex = of_property_read_bool(fixed_link_node, ++ "full-duplex"); ++ if (port->mac_data.speed == SPEED_2500) ++ port->mac_data.flags |= MV_EMAC_F_SGMII2_5; ++ if (of_property_read_u32(fixed_link_node, "speed", ++ &port->mac_data.speed)) ++ return -EINVAL; ++ } else { ++ port->mac_data.force_link = false; ++ ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ speed = 0; ++ /* check phy speed */ ++ of_property_read_u32(emac_node, "phy-speed", &speed); ++ switch (speed) { ++ case SPEED_1000: ++ port->mac_data.speed = SPEED_1000; /* sgmii */ ++ break; ++ case SPEED_2500: ++ port->mac_data.speed = SPEED_2500; /* sgmii */ ++ port->mac_data.flags |= MV_EMAC_F_SGMII2_5; ++ break; ++ default: ++ port->mac_data.speed = SPEED_1000; /* sgmii */ ++ } ++ break; ++ case PHY_INTERFACE_MODE_RXAUI: ++ break; ++ case PHY_INTERFACE_MODE_QSGMII: ++ break; ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ break; ++ case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_SFI: ++ case PHY_INTERFACE_MODE_XFI: ++ speed = 0; ++ /* check phy speed */ ++ of_property_read_u32(emac_node, "phy-speed", &speed); ++ switch (speed) { ++ case SPEED_10000: ++ port->mac_data.speed = SPEED_10000; ++ break; ++ case SPEED_5000: ++ port->mac_data.speed = SPEED_5000; ++ port->mac_data.flags |= MV_EMAC_F_5G; ++ break; ++ default: ++ port->mac_data.speed = SPEED_10000; ++ } ++ break; ++ ++ default: ++ netdev_err(port->dev, "%s: incorrect phy-mode\n", __func__); ++ return -1; ++ } ++ } ++ port->mac_data.phy_mode = phy_mode; ++ pr_info("gop_mac(%d), phy_mode(%d)=%s phy-speed=%d\n", id, phy_mode, ++ phy_modes(phy_mode), port->mac_data.speed); ++ ++ phy_node = of_parse_phandle(emac_node, "phy", 0); ++ if (phy_node) { ++ port->mac_data.phy_node = phy_node; ++ if (of_property_read_u32(phy_node, "reg", ++ &port->mac_data.phy_addr)) ++ netdev_err(port->dev, "%s: NO PHY address on emac %d\n", ++ __func__, port->mac_data.gop_index); ++ ++ pr_debug("gop_mac(%d), phy_reg(%d)\n", id, ++ port->mac_data.phy_addr); ++ } else { ++ pr_debug("No PHY NODE on emac %d\n", id); ++ } ++return 0; ++} ++ ++u32 mvp_pp2x_gop110_netc_cfg_create(struct mv_pp2x *priv) ++{ ++ u32 val = 0; ++ int i; ++ struct mv_pp2x_port *port; ++ struct mv_mac_data *mac; ++ ++ for (i = 0; i < priv->num_ports; i++) { ++ port = priv->port_list[i]; ++ mac = &port->mac_data; ++ if (mac->gop_index == 0) { ++ if (mac->phy_mode == PHY_INTERFACE_MODE_XAUI) { ++ val |= MV_NETC_GE_MAC0_XAUI; ++ } else if (mac->phy_mode == PHY_INTERFACE_MODE_RXAUI) { ++ if ((port->comphy[0]->id % MAX_NUM_SERDES_LANES) == COMPHY4_RXAUI_LANE0) ++ val |= MV_NETC_GE_MAC0_RXAUI_L45; ++ else ++ val |= MV_NETC_GE_MAC0_RXAUI_L23; ++ } ++ } ++ if (mac->gop_index == 2) { ++ if (mac->phy_mode == PHY_INTERFACE_MODE_SGMII || ++ mac->phy_mode == PHY_INTERFACE_MODE_1000BASEX) ++ val |= MV_NETC_GE_MAC2_SGMII; ++ else if (mac->phy_mode == PHY_INTERFACE_MODE_RGMII_ID) ++ val |= MV_NETC_GE_MAC2_RGMII; ++ } ++ if (mac->gop_index == 3) { ++ if (mac->phy_mode == PHY_INTERFACE_MODE_SGMII || ++ mac->phy_mode == PHY_INTERFACE_MODE_1000BASEX) ++ val |= MV_NETC_GE_MAC3_SGMII; ++ else if (mac->phy_mode == PHY_INTERFACE_MODE_RGMII_ID) ++ val |= MV_NETC_GE_MAC3_RGMII; ++ } ++ } ++ return val; ++} ++ ++static void mv_pp2x_get_port_stats(struct mv_pp2x_port *port) ++{ ++ bool link_is_up; ++ struct mv_mac_data *mac = &port->mac_data; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ int gop_port = mac->gop_index; ++ struct gop_stat *gop_statistics = &mac->gop_statistics; ++ ++ if (port->priv->pp2_version == PPV21) ++ return; ++ if (!(port->flags & MVPP2_F_LOOPBACK)) { ++ link_is_up = mv_gop110_port_is_link_up(gop, &port->mac_data); ++ if (link_is_up) { ++ mv_gop110_mib_counters_stat_update(gop, gop_port, gop_statistics); ++ mv_pp2x_counters_stat_update(port, gop_statistics); ++ ++ /* Check if MII underrun run fix should be triggered */ ++ if ((mac->phy_mode == PHY_INTERFACE_MODE_SGMII) || ++ (mac->phy_mode == PHY_INTERFACE_MODE_1000BASEX) || ++ (mac->phy_mode == PHY_INTERFACE_MODE_RGMII_ID)) ++ goto underrun_fix; ++ } ++ } ++ return; ++ ++underrun_fix: ++ /* Underrun issue relevant only for half duplex mode */ ++ if (mv_gop110_gmac_read(gop, mac->gop_index, MV_GMAC_PORT_STATUS0_REG) & ++ MV_GMAC_PORT_STATUS0_FULLDX_MASK) ++ return; ++ ++ /* Trigger underrun fix if needed */ ++ if (mv_gop110_gmac_read(gop, mac->gop_index, MV_GMAC_INTERRUPT_CAUSE_REG) & ++ MV_GMAC_TX_FIFO_UNDERRUN_MASK) { ++ mv_gop110_force_link_mode_set(gop, mac, false, true); ++ mv_gop110_force_link_mode_set(gop, mac, false, false); ++ } ++} ++ ++static void mv_pp2x_get_device_stats(struct work_struct *work) ++{ ++ struct delayed_work *delay = to_delayed_work(work); ++ struct mv_pp2x *priv = container_of(delay, struct mv_pp2x, ++ stats_task); ++ int i; ++ ++ for (i = 0; i < priv->num_ports; i++) { ++ if (priv->port_list[i]) ++ mv_pp2x_get_port_stats(priv->port_list[i]); ++ } ++ ++ queue_delayed_work(priv->workqueue, &priv->stats_task, stats_delay); ++} ++ ++/* Initialize port HW */ ++static int mv_pp2x_port_hw_init(struct mv_pp2x_port *port) ++{ ++ struct mv_pp2x *priv = port->priv; ++ struct gop_hw *gop = &port->priv->hw.gop; ++ struct mv_mac_data *mac = &port->mac_data; ++ int err = 0; ++ ++ /* Disable port */ ++ mv_pp2x_egress_disable(port); ++ ++ if (port->priv->pp2_version == PPV21) ++ mv_pp21_port_disable(port); ++ else ++ if (!(port->flags & MVPP2_F_LOOPBACK)) ++ mv_gop110_port_disable(gop, mac, port); ++ ++ /* Configure Rx queue group interrupt for this port */ ++ priv->pp2xdata->mv_pp2x_port_isr_rx_group_cfg(port); ++ ++ mv_pp2x_ingress_disable(port); ++ ++ /* Port default configuration */ ++ mv_pp2x_defaults_set(port); ++ ++ /* Port's classifier configuration */ ++ mv_pp2x_cls_oversize_rxq_set(port); ++ mv_pp2x_cls_port_config(port); ++ ++ /* Initialize pools for swf */ ++ err = mv_pp2x_swf_bm_pool_init(port); ++ ++ return err; ++} ++ ++/* Initialize port */ ++static int mv_pp2x_port_init(struct mv_pp2x_port *port) ++{ ++ struct device *dev = port->dev->dev.parent; ++ int queue, err; ++ ++ /* Allocate queues */ ++ port->txqs = devm_kcalloc(dev, port->num_tx_queues, sizeof(*port->txqs), ++ GFP_KERNEL); ++ if (!port->txqs) ++ return -ENOMEM; ++ port->rxqs = devm_kcalloc(dev, port->num_rx_queues, sizeof(*port->rxqs), ++ GFP_KERNEL); ++ if (!port->rxqs) ++ return -ENOMEM; ++ ++ /* Associate physical Tx queues to port and initialize. */ ++ err = mv_pp2x_port_txqs_init(dev, port); ++ ++ if (err) ++ goto out; ++ ++ /* Associate physical Rx queues to port and initialize. */ ++ err = mv_pp2x_port_rxqs_init(dev, port); ++ if (err) ++ goto out; ++ ++ /* Create Rx descriptor rings */ ++ for (queue = 0; queue < port->num_rx_queues; queue++) { ++ struct mv_pp2x_rx_queue *rxq = port->rxqs[queue]; ++ ++ rxq->size = port->rx_ring_size; ++ rxq->pkts_coal = MVPP2_RX_COAL_PKTS; ++ rxq->time_coal = MVPP2_RX_COAL_USEC; ++ } ++ ++ /* Provide an initial Rx packet size */ ++ port->pkt_size = MVPP2_RX_PKT_SIZE(port->dev->mtu); ++ ++ /* Configure queue_vectors */ ++ port->priv->pp2xdata->mv_pp2x_port_queue_vectors_init(port); ++ ++ err = mv_pp2x_port_hw_init(port); ++ if (err) ++ goto out; ++ return 0; ++ ++out: ++ mv_pp2x_port_napi_remove(port); ++ ++ return err; ++} ++ ++static void mv_pp2x_port_init_config(struct mv_pp2x_port *port) ++{ ++ u32 mask; ++ ++ /* CoS init config */ ++ port->cos_cfg.cos_classifier = cos_classifer; ++ port->cos_cfg.default_cos = default_cos; ++ /* Set priority-map but mask-limited by up to max 8 num-cos-queues */ ++ if (mv_pp2x_num_cos_queues == 8) { ++ mask = 0xffffffff; ++ } else { ++ mask = 1 << (mv_pp2x_num_cos_queues * (BITS_PER_BYTE / 2)); ++ mask -= 1; ++ } ++ port->cos_cfg.num_cos_queues = mv_pp2x_num_cos_queues; ++ port->cos_cfg.pri_map = pri_map & mask; ++ ++ /* RSS init config */ ++ port->rss_cfg.dflt_cpu = default_cpu; ++ /* RSS is disabled as default, it can be update when running */ ++ port->rss_cfg.rss_en = 0; ++ port->rss_cfg.rss_mode = rss_mode; ++ port->priv->rx_count = mv_pp2x_rx_count; ++ port->priv->other_count = MVPP2_DEFAULT_OTHER_COUNT; ++} ++ ++/* Routine called by port CPU hot plug notifier. If port up callback set irq affinity for private interrupts, ++* unmask private interrupt, set packet coalescing and clear counters. ++*/ ++static int mv_pp2x_port_cpu_callback(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) ++{ ++ unsigned int cpu = (unsigned long)hcpu; ++ int qvec_id; ++ struct queue_vector *qvec; ++ struct mv_pp2x_port *port = container_of(nfb, struct mv_pp2x_port, port_hotplug_nb); ++ struct mv_pp2x_aggr_tx_queue *aggr_txq; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ ++ switch (action) { ++ case CPU_ONLINE: ++ case CPU_ONLINE_FROZEN: ++ for (qvec_id = 0; qvec_id < port->num_qvector; qvec_id++) { ++ qvec = &port->q_vector[qvec_id]; ++ if (!qvec->irq) ++ continue; ++ if (qvec->qv_type == MVPP2_PRIVATE && ++ (QV_THR_2_MASTER_AP_CPU(qvec->ap_id, qvec->sw_thread_id) == cpu)) { ++ irq_set_affinity_hint(qvec->irq, cpumask_of(cpu)); ++ mv_pp22_private_interrupt_unmask(port, cpu); ++ if (port->priv->pp2_cfg.queue_mode == MVPP2_QDIST_MULTI_MODE) ++ irq_set_status_flags(qvec->irq, IRQ_NO_BALANCING); ++ /* FIXME: A810 support*/ ++ } ++ } ++ if (port->interrupt_tx_done) ++ mv_pp2x_tx_done_pkts_coal_set(port, cpu); ++ break; ++ case CPU_DEAD: ++ case CPU_DEAD_FROZEN: ++ cp_pcpu = port->priv->pcpu[cpu]; ++ aggr_txq = &port->priv->aggr_txqs[cpu]; ++ mv_pp2x_tx_timer_kill(cp_pcpu); ++ aggr_txq->hw_count += aggr_txq->sw_count; ++ mv_pp22_thread_write(&port->priv->hw, cpu, MVPP2_AGGR_TXQ_UPDATE_REG, aggr_txq->sw_count); ++ aggr_txq->sw_count = 0; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static int mv_pp22_uio_mem_map(struct mv_pp2x_uio *pp2x_uio, struct resource *res) ++{ ++ struct uio_mem *uio_mem = &pp2x_uio->u_info.mem[pp2x_uio->num_maps]; ++ ++ if (pp2x_uio->num_maps >= MAX_UIO_MAPS) { ++ pr_err("Too many UIO maps requests\n"); ++ return -ENOSPC; ++ } ++ ++ uio_mem->memtype = UIO_MEM_PHYS; ++ uio_mem->addr = res->start & PAGE_MASK; ++ uio_mem->size = PAGE_ALIGN(resource_size(res)); ++ uio_mem->name = res->name; ++ ++ pp2x_uio->num_maps++; ++ ++ pr_debug("uio: addr(%llx) size(%llx) name(%s) map_num(%d)\n", ++ uio_mem->addr, uio_mem->size, uio_mem->name, ++ pp2x_uio->num_maps); ++ ++ return 0; ++} ++ ++int mv_pp2x_port_musdk_set(void *netdev_priv) ++{ ++ struct mv_pp2x_port *port = netdev_priv; ++ ++ if (port->flags & MVPP2_F_IF_MUSDK) ++ return 0; ++ ++ pr_debug("mv_pp22_uio_open %s\n", port->dev->name); ++ ++ /* Close device before updates */ ++ if (netif_running(port->dev)) { ++ rtnl_lock(); ++ dev_close(port->dev); ++ rtnl_unlock(); ++ } ++ /* Backup num configured entities */ ++ port->cfg_num_qvector = port->num_qvector; ++ port->cfg_num_rx_queues = port->num_rx_queues; ++ port->cfg_num_tx_queues = port->num_tx_queues; ++ ++ /* Set num configured entities to 0 */ ++ port->num_qvector = 0; ++ port->num_rx_queues = 0; ++ port->num_tx_queues = 0; ++ ++ /* For MUSDK port, run cls_open once */ ++ mv_pp2x_open_cls(port->dev); ++ ++ /* Reconfigure rxq_groups */ ++ mv_pp22_port_uio_isr_rx_group_cfg(port); ++ ++ port->flags |= MVPP2_F_IF_MUSDK; ++ ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_port_musdk_set); ++ ++int mv_pp2x_port_musdk_clear(void *netdev_priv) ++{ ++ struct mv_pp2x_port *port = netdev_priv; ++ int i; ++ ++ if (!(port->flags & MVPP2_F_IF_MUSDK)) ++ return 0; ++ ++ pr_debug("mv_pp22_uio_release %s\n", port->dev->name); ++ ++ /* Close device before updates */ ++ if (netif_running(port->dev)) { ++ rtnl_lock(); ++ dev_close(port->dev); ++ rtnl_unlock(); ++ } ++ /* Disable Interrupts separately here. ++ * interrupt_mask/unmask is not included in mv_pp2x_open()/mv_pp2x_close(), ++ * since it needs to be driven in synch w/ Userspace. ++ * mv_pp22_port_uio_interrupts_disable() is performed mv_pp2x_open()/mv_pp2x_close(), ++ * so the rx/tx interrupts are disabled during close() in any case. ++ */ ++ for (i = 0; i < port->uio.num_qvector; i++) ++ mv_pp22_private_interrupt_mask(port, port->uio.q_vector[i].sw_thread_id); ++ ++ /* Set num configured entities back to configured values */ ++ port->num_qvector = port->cfg_num_qvector; ++ port->num_rx_queues = port->cfg_num_rx_queues; ++ port->num_tx_queues = port->cfg_num_tx_queues; ++ ++ /* Reconfigure Rx queue group interrupt for this port */ ++ port->priv->pp2xdata->mv_pp2x_port_isr_rx_group_cfg(port); ++ ++ port->flags &= ~MVPP2_F_IF_MUSDK; ++ return 0; ++} ++EXPORT_SYMBOL(mv_pp2x_port_musdk_clear); ++ ++#ifdef CONFIG_UIO ++static int mv_pp22_uio_open(struct uio_info *info, struct inode *inode) ++{ ++ int err; ++ struct mv_pp2x_port *port = info->priv; ++ ++ err = mv_pp2x_port_musdk_set(port); ++ return err; ++} ++ ++static int mv_pp22_uio_release(struct uio_info *info, struct inode *inode) ++{ ++ int err; ++ struct mv_pp2x_port *port = info->priv; ++ ++ err = mv_pp2x_port_musdk_clear(port); ++ return err; ++} ++#endif ++ ++/* Ports initialization */ ++static int mv_pp2x_port_probe(struct platform_device *pdev, ++ struct device_node *port_node, ++ struct mv_pp2x *priv) ++{ ++ struct device_node *emac_node = NULL; ++ struct device_node *phy_node = NULL; ++ struct mv_pp2x_port *port; ++ struct mv_pp2x_port_pcpu *port_pcpu; ++ struct net_device *dev; ++ struct resource *res; ++ const char *dt_mac_addr = NULL; ++ const char *mac_from; ++ char hw_mac_addr[ETH_ALEN] = {0}; ++ u32 id; ++ int features, err = 0, i, cpu; ++ int priv_common_regs_num = 2; ++ struct mv_pp2x_ext_buf_struct *ext_buf_struct; ++ unsigned int *port_irqs; ++ int port_num_irq; ++ int phy_mode; ++ struct phy **comphy = NULL; ++ const char *musdk_status; ++ int statlen, serdes_lanes_count; ++ ++ if (of_property_read_bool(port_node, "marvell,loopback")) { ++ dev = alloc_netdev_mqs(sizeof(struct mv_pp2x_port), "pp2_lpbk%d", NET_NAME_UNKNOWN, ++ ether_setup, mv_pp2x_txq_number * num_active_cpus(), mv_pp2x_rxq_number); ++ } else { ++ dev = alloc_etherdev_mqs(sizeof(struct mv_pp2x_port), ++ mv_pp2x_txq_number * num_active_cpus(), mv_pp2x_rxq_number); ++ } ++ if (!dev) ++ return -ENOMEM; ++ ++ /* Setup XPS mapping */ ++ for (cpu = 0; cpu < num_online_cpus(); cpu++) ++ netif_set_xps_queue(dev, cpumask_of(cpu), cpu); ++ ++ /*Connect entities */ ++ port = netdev_priv(dev); ++ port->dev = dev; ++ SET_NETDEV_DEV(dev, &pdev->dev); ++ port->priv = priv; ++ port->flags = 0; ++ port->interrupt_tx_done = port->priv->pp2xdata->interrupt_tx_done; ++ if (txdone_tmr) ++ port->priv->pp2xdata->interrupt_tx_done = false; ++ ++ musdk_status = of_get_property(port_node, "musdk-status", &statlen); ++ ++ /* Disable Flow control by default */ ++ port->flow_control = false; ++ ++ mv_pp2x_port_init_config(port); ++ ++ if (of_property_read_u32(port_node, "port-id", &id)) { ++ err = -EINVAL; ++ dev_err(&pdev->dev, "missing port-id value\n"); ++ goto err_free_netdev; ++ } ++ port->id = id; ++ ++ if (priv->pp2_version == PPV21) { ++ phy_node = of_parse_phandle(port_node, "phy", 0); ++ if (!phy_node) { ++ dev_err(&pdev->dev, "missing phy\n"); ++ err = -ENODEV; ++ goto err_free_netdev; ++ } ++ ++ phy_mode = of_get_phy_mode(port_node); ++ if (phy_mode < 0) { ++ dev_err(&pdev->dev, "incorrect phy mode\n"); ++ err = phy_mode; ++ goto err_free_netdev; ++ } ++ port->mac_data.phy_mode = phy_mode; ++ port->mac_data.phy_node = phy_node; ++ emac_node = port_node; ++ } else { ++ if (of_property_read_bool(port_node, "marvell,loopback")) ++ port->flags |= MVPP2_F_LOOPBACK; ++ ++ if (!(port->flags & MVPP2_F_LOOPBACK)) { ++ emac_node = of_parse_phandle(port_node, "emac-data", 0); ++ if (!emac_node) { ++ dev_err(&pdev->dev, "missing emac-data\n"); ++ err = -EINVAL; ++ goto err_free_netdev; ++ } ++ /* Init emac_data, includes link interrupt */ ++ if (mv_pp2_init_emac_data(port, emac_node)) ++ goto err_free_netdev; ++ ++ serdes_lanes_count = of_property_count_strings(emac_node, "phy-names"); ++ ++ port->comphy = NULL; ++ ++ if (serdes_lanes_count > 0) { ++ bool serdes_error = false; ++ ++ comphy = devm_kzalloc(&pdev->dev, sizeof(*comphy) * serdes_lanes_count, GFP_KERNEL); ++ ++ for (i = 0; i < serdes_lanes_count; i++) { ++ comphy[i] = devm_of_phy_get_by_index(&pdev->dev, emac_node, i); ++ ++ if (IS_ERR(comphy[i])) ++ serdes_error = true; ++ } ++ ++ if (!serdes_error) ++ port->comphy = comphy; ++ } ++ ++ port->num_serdes_lanes = serdes_lanes_count; ++ } else { ++ port->mac_data.link_irq = MVPP2_NO_LINK_IRQ; ++ } ++ } ++ ++ if (port->mac_data.phy_node) { ++ err = mv_pp2x_phy_connect(port); ++ if (err < 0) ++ goto err_free_netdev; ++ } ++ ++ /* get MAC address */ ++ if (emac_node) ++ dt_mac_addr = of_get_mac_address(emac_node); ++ ++ if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { ++ mac_from = "device tree"; ++ ether_addr_copy(dev->dev_addr, dt_mac_addr); ++ } else { ++ if (priv->pp2_version == PPV21) ++ mv_pp21_get_mac_address(port, hw_mac_addr); ++ if (is_valid_ether_addr(hw_mac_addr)) { ++ mac_from = "hardware"; ++ ether_addr_copy(dev->dev_addr, hw_mac_addr); ++ } else { ++ mac_from = "random"; ++ eth_hw_addr_random(dev); ++ } ++ } ++ pr_info("mac_addr %x:%x:%x:%x:%x:%x", ++ dev->dev_addr[0], ++ dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], ++ dev->dev_addr[4], dev->dev_addr[5]); ++ ++ /* Tx/Rx Interrupt */ ++ port_num_irq = mv_pp2x_of_irq_count(port_node); ++ if (port_num_irq != priv->pp2xdata->num_port_irq) { ++ dev_err(&pdev->dev, ++ "port(%d)-number of irq's doesn't match hw\n", id); ++ goto err_free_netdev; ++ } ++ port_irqs = devm_kcalloc(&pdev->dev, port_num_irq, ++ sizeof(u32), GFP_KERNEL); ++ port->of_irqs = port_irqs; ++ port->num_irqs = 0; ++ for (i = 0; i < port_num_irq; i++) { ++ port_irqs[i] = irq_of_parse_and_map(port_node, i); ++ if (port_irqs[i] == 0) { ++ dev_err(&pdev->dev, ++ "Fail to parse port(%d), irq(%d)\n", id, i); ++ err = -EINVAL; ++ goto err_free_irq; ++ } ++ port->num_irqs++; ++ } ++ ++ dev->tx_queue_len = tx_queue_size; ++ dev->watchdog_timeo = 5 * HZ; ++ ++ port->num_tx_queues = mv_pp2x_txq_number; ++ port->num_rx_queues = mv_pp2x_rxq_number; ++ dev->netdev_ops = &mv_pp2x_netdev_ops; ++ mv_pp2x_set_ethtool_ops(dev); ++ ++ if (priv->pp2_version == PPV21) ++ port->first_rxq = (port->id) * mv_pp2x_rxq_number + ++ first_log_rxq_queue; ++ else ++ port->first_rxq = (port->id) * priv->pp2xdata->pp2x_max_port_rxqs + ++ first_log_rxq_queue; ++ ++ if (priv->pp2_version == PPV21) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, ++ priv_common_regs_num + id); ++ port->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(port->base)) { ++ err = PTR_ERR(port->base); ++ goto err_free_irq; ++ } ++ } ++ ++ /* Alloc per-cpu stats */ ++ port->stats = netdev_alloc_pcpu_stats(struct mv_pp2x_pcpu_stats); ++ if (!port->stats) { ++ err = -ENOMEM; ++ goto err_free_irq; ++ } ++ ++ port->tx_ring_size = tx_queue_size; ++ port->rx_ring_size = rx_queue_size; ++ ++ mv_pp2x_check_queue_size_valid(port); ++ ++ err = mv_pp2x_port_init(port); ++ if (err < 0) ++ goto err_free_stats; ++ ++ if (port->priv->pp2_version == PPV21) ++ mv_pp21_port_power_up(port); ++ ++ port->pcpu = devm_kcalloc(&pdev->dev, MVPP2_MAX_ADDR_SPACES, sizeof(*port->pcpu), ++ GFP_KERNEL); ++ if (!port->pcpu) { ++ err = -ENOMEM; ++ goto err_free_stats; ++ } ++ ++ err = mv_pp2x_tx_done_init(pdev, port); ++ if (err) ++ goto err_free_stats; ++ ++ /* Init pool of external buffers for TSO, fragmentation, ++ * RX BM pool refill work, etc ++ */ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ port_pcpu = port->pcpu[cpu]; ++ port_pcpu->ext_buf_size = MVPP2_EXTRA_BUF_SIZE; ++ ++ INIT_LIST_HEAD(&port_pcpu->ext_buf_port_list); ++ port_pcpu->ext_buf_pool = devm_kzalloc(port->dev->dev.parent, ++ sizeof(struct mv_pp2x_ext_buf_pool), GFP_ATOMIC); ++ port_pcpu->ext_buf_pool->buf_pool_size = MVPP2_EXTRA_BUF_NUM; ++ port_pcpu->ext_buf_pool->ext_buf_struct = ++ devm_kzalloc(port->dev->dev.parent, ++ sizeof(*ext_buf_struct) * MVPP2_EXTRA_BUF_NUM, GFP_ATOMIC); ++ ++ for (i = 0; i < MVPP2_EXTRA_BUF_NUM; i++) { ++ u8 *ext_buf = kmalloc(MVPP2_EXTRA_BUF_SIZE, GFP_ATOMIC); ++ ++ port_pcpu->ext_buf_pool->ext_buf_struct[i].ext_buf_data = ext_buf; ++ if (!ext_buf) { ++ pr_warn("\to %s Warning: %d of %d extra buffers allocated\n", ++ __func__, i, MVPP2_EXTRA_BUF_NUM); ++ break; ++ } ++ list_add(&port_pcpu->ext_buf_pool->ext_buf_struct[i].ext_buf_list, ++ &port_pcpu->ext_buf_port_list); ++ mv_pp2x_extra_pool_inc(port_pcpu->ext_buf_pool); ++ port_pcpu->ext_buf_pool->buf_pool_in_use++; ++ } ++ ++ INIT_WORK(&port_pcpu->bm_refill_work, ++ mv_pp2x_rx_bm_pool_refill); ++ } ++ ++ features = NETIF_F_SG; ++ dev->features = features | NETIF_F_RXCSUM | NETIF_F_IP_CSUM | ++ NETIF_F_IPV6_CSUM | NETIF_F_TSO; ++ dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO | ++ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO; ++ ++ /* Only when multi queue mode, rxhash is supported */ ++ if (mv_pp2x_queue_mode) ++ dev->hw_features |= NETIF_F_RXHASH; ++ ++ if (dev->features & NETIF_F_TSO) ++ port->txq_stop_limit = TSO_TXQ_LIMIT; ++ else ++ port->txq_stop_limit = TXQ_LIMIT; ++ ++ dev->vlan_features |= features; ++ ++ /* Add support for VLAN filtering */ ++ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; ++ ++ dev->priv_flags |= IFF_UNICAST_FLT; ++ ++ err = register_netdev(dev); ++ if (err < 0) { ++ dev_err(&pdev->dev, "failed to register netdev\n"); ++ goto err_free_stats; ++ } ++ ++#ifdef CONFIG_UIO ++ /* Register uio_device */ ++ if (priv->pp2_version != PPV21) { ++ port->uio.u_info.name = kasprintf(GFP_KERNEL, UIO_PORT_STRING, priv->pp2_cfg.cell_index, port->id); ++ port->uio.u_info.version = "0.1"; ++ port->uio.u_info.priv = port; ++ port->uio.u_info.open = mv_pp22_uio_open; ++ port->uio.u_info.release = mv_pp22_uio_release; ++ port->uio.u_info.irq = UIO_IRQ_CUSTOM; ++ ++ /* Init uio_interrupts once */ ++ mv_pp22_uio_queue_vectors_init(port); ++ ++ err = uio_register_device(&dev->dev, &port->uio.u_info); ++ if (err) { ++ dev_err(&dev->dev, "Failed to register uio device\n"); ++ goto err_unreg_netdev; ++ } ++ } ++#endif ++ ++ /* Clear MIB and mvpp2 counters statistic */ ++ mv_gop110_mib_counters_clear(&port->priv->hw.gop, port->mac_data.gop_index); ++ mv_pp2x_counters_stat_clear(port); ++ ++ mv_pp2x_port_irq_names_update(port); ++ ++ if (priv->pp2_version != PPV21) ++ port->port_hotplug_nb.notifier_call = mv_pp2x_port_cpu_callback; ++ ++ /* Init statistic lock */ ++ spin_lock_init(&port->mac_data.stats_spinlock); ++ ++ netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr); ++ ++ priv->port_list[priv->num_ports] = port; ++ priv->num_ports++; ++ ++#ifdef DEV_NETMAP ++ mv_pp2x_netmap_attach(port); ++#endif /* DEV_NETMAP */ ++#ifdef CONFIG_MV_PTP_SERVICE ++ mv_pp2x_ptp_init(pdev, port, id); ++#endif ++ ++ /* Populate network device of_node */ ++ dev->dev.of_node = port_node; ++ ++ return 0; ++ ++#ifdef CONFIG_UIO ++err_unreg_netdev: ++ unregister_netdev(dev); ++#endif ++err_free_stats: ++ free_percpu(port->stats); ++err_free_irq: ++ mv_pp2x_port_irqs_dispose_mapping(port); ++err_free_netdev: ++ free_netdev(dev); ++ ++ if (err) ++ dev_err(&pdev->dev, "failed to init port %d, err=%d\n", id, err); ++ ++ return err; ++} ++ ++/* Ports removal routine */ ++static void mv_pp2x_port_remove(struct mv_pp2x_port *port) ++{ ++#ifdef DEV_NETMAP ++ netmap_detach(port->dev); ++#endif /* DEV_NETMAP */ ++ ++ if (port->priv->pp2_version != PPV21) { ++#ifdef CONFIG_UIO ++ uio_unregister_device(&port->uio.u_info); ++#endif ++ kfree(port->uio.u_info.name); ++ } ++ ++ mv_pp2x_port_napi_remove(port); ++ unregister_netdev(port->dev); ++ ++ if (port->mac_data.phy_node) ++ mv_pp2x_phy_disconnect(port); /* do after unregister netdev */ ++ ++ free_percpu(port->stats); ++ mv_pp2x_port_irqs_dispose_mapping(port); ++ free_netdev(port->dev); ++} ++ ++/* Initialize decoding windows */ ++static void mv_pp2x_conf_mbus_windows(const struct mbus_dram_target_info *dram, ++ struct mv_pp2x_hw *hw) ++{ ++ u32 win_enable; ++ int i; ++ ++ for (i = 0; i < 6; i++) { ++ mv_pp2x_write(hw, MVPP2_WIN_BASE(i), 0); ++ mv_pp2x_write(hw, MVPP2_WIN_SIZE(i), 0); ++ ++ if (i < 4) ++ mv_pp2x_write(hw, MVPP2_WIN_REMAP(i), 0); ++ } ++ ++ win_enable = 0; ++ ++ for (i = 0; i < dram->num_cs; i++) { ++ const struct mbus_dram_window *cs = dram->cs + i; ++ ++ mv_pp2x_write(hw, MVPP2_WIN_BASE(i), ++ (cs->base & 0xffff0000) | (cs->mbus_attr << 8) | ++ dram->mbus_dram_target_id); ++ ++ mv_pp2x_write(hw, MVPP2_WIN_SIZE(i), ++ (cs->size - 1) & 0xffff0000); ++ ++ win_enable |= (1 << i); ++ } ++ ++ mv_pp2x_write(hw, MVPP2_BASE_ADDR_ENABLE, win_enable); ++} ++ ++/* Initialize network controller common part HW */ ++static int mv_pp2x_init(struct platform_device *pdev, struct mv_pp2x *priv) ++{ ++ int err, i, cpu; ++ int last_log_rx_queue; ++ u32 val; ++ const struct mbus_dram_target_info *dram_target_info; ++ u8 pp2_ver = priv->pp2xdata->pp2x_ver; ++ struct mv_pp2x_hw *hw = &priv->hw; ++ ++ /* Set statistic delay */ ++ stats_delay = (stats_delay_msec * HZ) / 1000; ++ ++ /* Checks for hardware constraints */ ++ if (mv_pp2x_num_cos_queues <= 0 || mv_pp2x_num_cos_queues > MVPP2_MAX_TXQ) { ++ dev_err(&pdev->dev, "Illegal num_cos_queue parameter\n"); ++ return -EINVAL; ++ } ++ ++ last_log_rx_queue = first_log_rxq_queue + mv_pp2x_rxq_number; ++ if (last_log_rx_queue > priv->pp2xdata->pp2x_max_port_rxqs) { ++ dev_err(&pdev->dev, "too high num_cos_queue parameter\n"); ++ return -EINVAL; ++ } ++ ++ /*TODO: YuvalC, replace this with a per-pp2x validation function. */ ++ if ((pp2_ver == PPV21) && (mv_pp2x_rxq_number % 4)) { ++ dev_err(&pdev->dev, "invalid num_cos_queue parameter\n"); ++ return -EINVAL; ++ } ++ ++ if (mv_pp2x_txq_number > MVPP2_MAX_TXQ) { ++ dev_err(&pdev->dev, "invalid num_cos_queue parameter\n"); ++ return -EINVAL; ++ } ++ ++ /* MBUS windows configuration */ ++ dram_target_info = mv_mbus_dram_info(); ++ if (dram_target_info) ++ mv_pp2x_conf_mbus_windows(dram_target_info, hw); ++ ++ if (priv->pp2_version != PPV21) { ++ mv_pp2x_write(hw, MVPP22_BM_PHY_VIRT_HIGH_RLS_REG, 0x0); ++ /*AXI Bridge Configuration */ ++ /* BM */ ++ mv_pp2x_write(hw, MVPP22_AXI_BM_WR_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_WRITE); ++ mv_pp2x_write(hw, MVPP22_AXI_BM_RD_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_READ); ++ ++ /* Descriptors */ ++ mv_pp2x_write(hw, MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_READ); ++ mv_pp2x_write(hw, MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_WRITE); ++ mv_pp2x_write(hw, MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_READ); ++ mv_pp2x_write(hw, MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_WRITE); ++ ++ /* Buffer Data */ ++ mv_pp2x_write(hw, MVPP22_AXI_TX_DATA_RD_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_READ); ++ mv_pp2x_write(hw, MVPP22_AXI_RX_DATA_WR_ATTR_REG, ++ MVPP22_AXI_ATTR_HW_COH_WRITE); ++ ++ val = MVPP22_AXI_CODE_CACHE_NON_CACHE << MVPP22_AXI_CODE_CACHE_OFFS; ++ val |= MVPP22_AXI_CODE_DOMAIN_SYSTEM << MVPP22_AXI_CODE_DOMAIN_OFFS; ++ mv_pp2x_write(hw, MVPP22_AXI_RD_NORMAL_CODE_REG, val); ++ mv_pp2x_write(hw, MVPP22_AXI_WR_NORMAL_CODE_REG, val); ++ ++ val = MVPP22_AXI_CODE_CACHE_RD_CACHE << MVPP22_AXI_CODE_CACHE_OFFS; ++ val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM << MVPP22_AXI_CODE_DOMAIN_OFFS; ++ ++ mv_pp2x_write(hw, MVPP22_AXI_RD_SNOOP_CODE_REG, val); ++ ++ val = MVPP22_AXI_CODE_CACHE_WR_CACHE << MVPP22_AXI_CODE_CACHE_OFFS; ++ val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM << MVPP22_AXI_CODE_DOMAIN_OFFS; ++ ++ mv_pp2x_write(hw, MVPP22_AXI_WR_SNOOP_CODE_REG, val); ++ } ++ ++ /* Disable HW PHY polling */ ++ if (priv->pp2_version == PPV21) { ++ val = readl(hw->lms_base + MVPP2_PHY_AN_CFG0_REG); ++ val |= MVPP2_PHY_AN_STOP_SMI0_MASK; ++ writel(val, hw->lms_base + MVPP2_PHY_AN_CFG0_REG); ++ writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, ++ hw->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); ++ } ++ ++ /* Allocate and initialize aggregated TXQs ++ * The aggr_txqs area should be aligned onto cache-line-size. ++ * So allocate cache-line-size more than needed, round-up the pointer ++ * but keep the offset between aligned and original pointers ++ * for further usage in free(aligned - offset). ++ * (offset is used instead of origin-ptr since it is more compact) ++ */ ++ val = sizeof(struct mv_pp2x_aggr_tx_queue) * mv_pp2x_used_addr_spaces + ++ MVPP2_CACHE_LINE_SIZE; ++ priv->aggr_txqs = devm_kcalloc(&pdev->dev, 1, val, GFP_KERNEL); ++ if (!priv->aggr_txqs) ++ return -ENOMEM; ++ val = (dma_addr_t)priv->aggr_txqs & MVPP2_CACHE_LINE_MASK; ++ if (!val) { ++ priv->aggr_txqs_align_offs = 0; ++ } else { ++ priv->aggr_txqs_align_offs = sizeof(struct mv_pp2x_aggr_tx_queue) - val; ++ priv->aggr_txqs = (void *)((u8 *)priv->aggr_txqs + ++ priv->aggr_txqs_align_offs); ++ } ++ ++ priv->num_aggr_qs = mv_pp2x_used_addr_spaces; ++ ++ i = 0; ++ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ priv->aggr_txqs[i].id = cpu; ++ priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE; ++ ++ err = mv_pp2x_aggr_txq_init(pdev, &priv->aggr_txqs[i], ++ MVPP2_AGGR_TXQ_SIZE, i, priv); ++ if (err < 0) ++ return err; ++ i++; ++ } ++ ++ /* Rx Fifo Init is done only in uboot */ ++ ++ /* Set cache snoop when transmiting packets */ ++ mv_pp2x_write(hw, MVPP2_TX_SNOOP_REG, 0x1); ++ ++ /* Buffer Manager initialization */ ++ err = mv_pp2x_bm_init(pdev, priv); ++ if (err < 0) ++ return err; ++ ++ /* Parser flow id attribute tbl init */ ++ mv_pp2x_prs_flow_id_attr_init(); ++ ++ /* Parser default initialization */ ++ err = mv_pp2x_prs_default_init(pdev, hw); ++ if (err < 0) ++ return err; ++ ++ /* Classifier default initialization */ ++ err = mv_pp2x_cls_init(pdev, hw); ++ if (err < 0) ++ return err; ++ ++ /* Classifier engine2 initialization */ ++ err = mv_pp2x_c2_init(pdev, hw); ++ if (err < 0) ++ return err; ++ ++ if (pp2_ver != PPV21) { ++ for (i = 0; i < 128; i++) { ++ val = mv_pp2x_read(hw, MVPP2_RXQ_CONFIG_REG(i)); ++ val |= MVPP2_RXQ_DISABLE_MASK; ++ mv_pp2x_write(hw, MVPP2_RXQ_CONFIG_REG(i), val); ++ } ++ } ++ ++ return 0; ++} ++ ++static struct mv_pp2x_platform_data pp21_pdata = { ++ .pp2x_ver = PPV21, ++ .pp2x_max_port_rxqs = 8, ++ .mv_pp2x_rxq_short_pool_set = mv_pp21_rxq_short_pool_set, ++ .mv_pp2x_rxq_long_pool_set = mv_pp21_rxq_long_pool_set, ++ .multi_addr_space = false, ++ .interrupt_tx_done = false, ++ .multi_hw_instance = false, ++ .mv_pp2x_port_queue_vectors_init = mv_pp21_port_queue_vectors_init, ++ .mv_pp2x_port_isr_rx_group_cfg = mv_pp21x_port_isr_rx_group_cfg, ++ .num_port_irq = 1, ++ .hw.desc_queue_addr_shift = MVPP21_DESC_ADDR_SHIFT, ++}; ++ ++static struct mv_pp2x_platform_data pp22_pdata = { ++ .pp2x_ver = PPV22, ++ .pp2x_max_port_rxqs = 32, ++ .mv_pp2x_rxq_short_pool_set = mv_pp22_rxq_short_pool_set, ++ .mv_pp2x_rxq_long_pool_set = mv_pp22_rxq_long_pool_set, ++ .multi_addr_space = true, ++ .interrupt_tx_done = true, ++ .multi_hw_instance = true, ++ .mv_pp2x_port_queue_vectors_init = mv_pp22_queue_vectors_init, ++ .mv_pp2x_port_isr_rx_group_cfg = mv_pp22_port_isr_rx_group_cfg, ++ .num_port_irq = 9, ++ .hw.desc_queue_addr_shift = MVPP22_DESC_ADDR_SHIFT, ++}; ++ ++static const struct of_device_id mv_pp2x_match_tbl[] = { ++ { ++ .compatible = "marvell,armada-375-pp2", ++ .data = &pp21_pdata, ++ }, ++ { ++ .compatible = "marvell,mv-pp22", ++ .data = &pp22_pdata, ++ }, ++ { } ++}; ++ ++static int mv_pp2x_init_config(struct mv_pp2x_param_config *pp2_cfg, ++ u32 cell_index) ++{ ++ pp2_cfg->cell_index = cell_index; ++ pp2_cfg->first_bm_pool = first_bm_pool; ++ pp2_cfg->first_sw_thread = 0; ++ pp2_cfg->first_log_rxq = first_log_rxq_queue; ++ pp2_cfg->queue_mode = mv_pp2x_queue_mode; ++ pp2_cfg->rx_cpu_map = port_cpu_bind_map; ++ pp2_cfg->uc_filter_max = uc_filter_max; ++ pp2_cfg->recycling = true; ++ pp2_cfg->mc_filter_max = MVPP2_PRS_MAC_UC_MC_FILT_MAX - uc_filter_max; ++ /* FIXME: Hardcoded no locks for MVPP2_QDIST_MULTI_MODE, to run 2APhase, need to change define. ++ * In 2APhase ethtool would be added. ++ * Add locks required during data path. ++ * In single queue mode aggregated queue should be locked if more than one AP's exist. ++ * Multi AP system has more than 8 CPU's, and mvpp22 HW has only 9 address spaces(9 physical aggregated queues) ++ * In multi queue mode aggregated queue and BM refill should be locked if rx_count less than number ++ * of address spaces - 1. ++ * Same as for physical aggregated queues, mvpp22 HW has only 9 copy's of buffer manager refill register. ++ * So in this case BM refill procedure should be protected. ++ * In single resource mode aggregated queue always should be locked, since single address spaces used by kernel. ++ * Singe thread deal with RX, so there is no need in BM lock. ++ */ ++ if (((num_active_cpus() > MVPP2_MAX_CPUS_IN_AP) && ((pp2_cfg->queue_mode == MVPP2_QDIST_SINGLE_MODE) || ++ (mv_pp2x_rx_count > (MVPP2_MAX_ADDR_SPACES - 1)))) || ++ (pp2_cfg->queue_mode == MVPP2_SINGLE_RESOURCE_MODE)) { ++ pp2_cfg->spinlocks_bitmap |= MV_AGGR_QUEUE_LOCK; ++ ++ if (pp2_cfg->queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ pp2_cfg->spinlocks_bitmap |= MV_BM_LOCK; ++ pp2_cfg->recycling = false; ++ } ++ } ++ ++ /* Calculate number of AP in system */ ++ pp2_cfg->num_of_ap = ((num_present_cpus() / MVPP2_MAX_CPUS_IN_AP) > 1) ? ++ (num_present_cpus() / MVPP2_MAX_CPUS_IN_AP) : 1; ++ ++ return 0; ++} ++ ++static void mv_pp22_init_rxfhindir(struct mv_pp2x *pp2) ++{ ++ int i; ++ int hot_cpus = num_online_cpus(); ++ ++ if (hot_cpus > mv_pp2x_rx_count) ++ hot_cpus = mv_pp2x_rx_count; ++ ++ for (i = 0; i < MVPP22_RSS_TBL_LINE_NUM; i++) ++ pp2->rx_indir_table[i] = i % hot_cpus; ++} ++ ++static int mv_pp2x_platform_data_get(struct platform_device *pdev, ++ struct mv_pp2x *priv, u32 *cell_index, int *port_count) ++{ ++ struct mv_pp2x_hw *hw = &priv->hw; ++ struct mv_pp2x_uio *uio = &priv->uio; ++ static bool cell_index_dts_flag; ++ const struct of_device_id *match; ++ struct device_node *dn = pdev->dev.of_node; ++ struct resource *res; ++ resource_size_t mspg_base, mspg_end; ++ int err; ++ ++ match = of_match_node(mv_pp2x_match_tbl, dn); ++ if (!match) ++ return -ENODEV; ++ ++ priv->pp2xdata = (struct mv_pp2x_platform_data *)match->data; ++ ++ if (of_property_read_u32(dn, "cell-index", cell_index)) { ++ *cell_index = auto_cell_index; ++ auto_cell_index++; ++ } else { ++ cell_index_dts_flag = true; ++ } ++ /* If cell_index is set by dts, and any previous device was set by ++ * auto_index, then fail. ++ */ ++ if (auto_cell_index && cell_index_dts_flag) ++ return -ENXIO; ++ /* PPV2 Address Space */ ++ if (priv->pp2xdata->pp2x_ver == PPV21) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ hw->phys_addr_start = res->start; ++ hw->phys_addr_end = res->end; ++ hw->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->base)) ++ return PTR_ERR(hw->base); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ hw->lms_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->lms_base)) ++ return PTR_ERR(hw->lms_base); ++ } else { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pp"); ++ hw->phys_addr_start = res->start; ++ hw->phys_addr_end = res->end; ++ hw->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->base)) ++ return PTR_ERR(hw->base); ++ /* Map "pp" to uio */ ++ err = mv_pp22_uio_mem_map(uio, res); ++ if (err) ++ return err; ++ ++ /* xmib */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "xmib"); ++ hw->gop.gop_110.xmib.base = ++ devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->gop.gop_110.xmib.base)) ++ return PTR_ERR(hw->gop.gop_110.xmib.base); ++ hw->gop.gop_110.xmib.obj_size = MVPP22_MIB_PORT_OFFS; ++ ++ /* skipped led */ ++ ++ /* smi */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "smi"); ++ hw->gop.gop_110.smi_base = ++ devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->gop.gop_110.smi_base)) ++ return PTR_ERR(hw->gop.gop_110.smi_base); ++ ++ /* rfu1 */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "rfu1"); ++ hw->gop.gop_110.rfu1_base = ++ devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->gop.gop_110.rfu1_base)) ++ return PTR_ERR(hw->gop.gop_110.rfu1_base); ++ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "cm3"); ++ hw->gop.gop_110.cm3_base = ++ devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->gop.gop_110.cm3_base)) ++ return PTR_ERR(hw->gop.gop_110.cm3_base); ++ ++ /* Map "cm3" to uio */ ++ err = mv_pp22_uio_mem_map(uio, res); ++ if (err) ++ return err; ++ ++ /* skipped tai */ ++ ++ /* xsmi */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "xsmi"); ++ hw->gop.gop_110.xsmi_base = ++ devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->gop.gop_110.xsmi_base)) ++ return PTR_ERR(hw->gop.gop_110.xsmi_base); ++ ++ /* MSPG - base register */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "mspg"); ++ hw->gop.gop_110.mspg.base = ++ devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hw->gop.gop_110.mspg.base)) ++ return PTR_ERR(hw->gop.gop_110.mspg.base); ++ hw->gop.gop_110.mspg.obj_size = MVPP22_GOP_PORT_OFFS; ++ mspg_base = res->start; ++ mspg_end = res->end; ++ ++ /* Map "mspg" to uio */ ++ err = mv_pp22_uio_mem_map(uio, res); ++ if (err) ++ return err; ++ ++ /* xpcs */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "xpcs"); ++ if ((res->start <= mspg_base) || (res->end >= mspg_end)) ++ return -ENXIO; ++ hw->gop.gop_110.xpcs_base = ++ (void *)(hw->gop.gop_110.mspg.base + ++ (res->start - mspg_base)); ++ ++ hw->gop.gop_110.ptp.base = ++ (void *)(hw->gop.gop_110.mspg.base + MVPP22_PTP_BASE_OFFS); ++ hw->gop.gop_110.ptp.obj_size = MVPP22_GOP_PORT_OFFS; ++ /* MSPG - gmac */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "gmac"); ++ if ((res->start <= mspg_base) || (res->end >= mspg_end)) ++ return -ENXIO; ++ hw->gop.gop_110.gmac.base = ++ (void *)(hw->gop.gop_110.mspg.base + ++ (res->start - mspg_base)); ++ hw->gop.gop_110.gmac.obj_size = MVPP22_GOP_PORT_OFFS; ++ ++ /* FCA - flow control*/ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "fca"); ++ if ((res->start <= mspg_base) || (res->end >= mspg_end)) ++ return -ENXIO; ++ hw->gop.gop_110.fca.base = ++ (void *)(hw->gop.gop_110.mspg.base + ++ (res->start - mspg_base)); ++ hw->gop.gop_110.fca.obj_size = MVPP22_GOP_PORT_OFFS; ++ ++ /* MSPG - xlg */ ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, "xlg"); ++ if ((res->start <= mspg_base) || (res->end >= mspg_end)) ++ return -ENXIO; ++ hw->gop.gop_110.xlg_mac.base = ++ (void *)(hw->gop.gop_110.mspg.base + ++ (res->start - mspg_base)); ++ hw->gop.gop_110.xlg_mac.obj_size = MVPP22_GOP_PORT_OFFS; ++ ++ /* Jumbo L4_checksum port */ ++ if (of_property_read_u32(dn, "l4_chksum_jumbo_port", ++ &priv->l4_chksum_jumbo_port)) ++ /* Init as a invalid value */ ++ priv->l4_chksum_jumbo_port = MVPP2_MAX_PORTS; ++ ++ hw->gop_core_clk = devm_clk_get(&pdev->dev, "gop_core_clk"); ++ if (IS_ERR(hw->gop_core_clk)) ++ return PTR_ERR(hw->gop_core_clk); ++ err = clk_prepare_enable(hw->gop_core_clk); ++ if (err < 0) ++ return err; ++ ++ hw->mg_core_clk = devm_clk_get(&pdev->dev, "mg_core_clk"); ++ if (IS_ERR(hw->mg_clk)) ++ return PTR_ERR(hw->mg_core_clk); ++ err = clk_prepare_enable(hw->mg_core_clk); ++ if (err < 0) ++ return err; ++ ++ hw->mg_clk = devm_clk_get(&pdev->dev, "mg_clk"); ++ if (IS_ERR(hw->mg_clk)) ++ return PTR_ERR(hw->mg_clk); ++ err = clk_prepare_enable(hw->mg_clk); ++ if (err < 0) ++ return err; ++ } ++ ++ hw->gop_clk = devm_clk_get(&pdev->dev, "gop_clk"); ++ if (IS_ERR(hw->gop_clk)) ++ return PTR_ERR(hw->gop_clk); ++ err = clk_prepare_enable(hw->gop_clk); ++ if (err < 0) ++ return err; ++ ++ hw->pp_clk = devm_clk_get(&pdev->dev, "pp_clk"); ++ if (IS_ERR(hw->pp_clk)) ++ return PTR_ERR(hw->pp_clk); ++ err = clk_prepare_enable(hw->pp_clk); ++ if (err < 0) ++ return err; ++ ++ /* Get system's tclk rate */ ++ hw->tclk = clk_get_rate(hw->pp_clk); ++ ++ *port_count = of_get_available_child_count(dn); ++ if (*port_count == 0) { ++ dev_err(&pdev->dev, "no ports enabled\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++/* Initialize Rx FIFO's */ ++static void mv_pp22_rx_fifo_init(struct mv_pp2x *priv) ++{ ++ int port; ++ ++ /* Port 0 maximum speed -10Gb/s port - required by spec RX FIFO size 32KB ++ * Port 1 and 2 maximum speed -2.5Gb/s port - required by spec RX FIFO size 8KB ++ */ ++ for (port = 0; port < (MVPP2_MAX_PORTS - 1); port++) { ++ if (port == 0) { ++ mv_pp2x_write(&priv->hw, MVPP2_RX_DATA_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_DATA_SIZE_32KB); ++ mv_pp2x_write(&priv->hw, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_ATTR_SIZE_32KB); ++ } else { ++ mv_pp2x_write(&priv->hw, MVPP2_RX_DATA_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_DATA_SIZE_8KB); ++ mv_pp2x_write(&priv->hw, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_ATTR_SIZE_8KB); ++ } ++ } ++ ++ mv_pp2x_write(&priv->hw, MVPP2_RX_MIN_PKT_SIZE_REG, ++ MVPP2_RX_FIFO_PORT_MIN_PKT); ++ mv_pp2x_write(&priv->hw, MVPP2_RX_FIFO_INIT_REG, 0x1); ++} ++ ++/* Configure Rx FIFO Flow control thresholds */ ++static void mv_pp23_rx_fifo_fc_set_tresh(struct mv_pp2x *priv) ++{ ++ int port, val; ++ ++ /* Port 0 maximum speed -10Gb/s port - required by spec RX FIFO threshold 9KB ++ * Port 1 maximum speed -5Gb/s port - required by spec RX FIFO threshold 4KB ++ * Port 2 maximum speed -1Gb/s port - required by spec RX FIFO threshold 2KB ++ */ ++ ++ /* Without loopback port */ ++ for (port = 0; port < (MVPP2_MAX_PORTS - 1); port++) { ++ if (port == 0) { ++ val = (MVPP23_PORT0_FIFO_THRESHOLD / MVPP2_RX_FLOW_CONTROL_TRSH_UNIT) ++ << MVPP2_RX_FLOW_CONTROL_TRSH_OFFS; ++ val &= MVPP2_RX_FLOW_CONTROL_TRSH_MASK; ++ mv_pp2x_write(&priv->hw, MVPP2_RX_FLOW_CONTROL_REG(port), val); ++ } else if (port == 1) { ++ val = (MVPP23_PORT1_FIFO_THRESHOLD / MVPP2_RX_FLOW_CONTROL_TRSH_UNIT) ++ << MVPP2_RX_FLOW_CONTROL_TRSH_OFFS; ++ val &= MVPP2_RX_FLOW_CONTROL_TRSH_MASK; ++ mv_pp2x_write(&priv->hw, MVPP2_RX_FLOW_CONTROL_REG(port), val); ++ } else { ++ val = (MVPP23_PORT2_FIFO_THRESHOLD / MVPP2_RX_FLOW_CONTROL_TRSH_UNIT) ++ << MVPP2_RX_FLOW_CONTROL_TRSH_OFFS; ++ val &= MVPP2_RX_FLOW_CONTROL_TRSH_MASK; ++ mv_pp2x_write(&priv->hw, MVPP2_RX_FLOW_CONTROL_REG(port), val); ++ } ++ } ++} ++ ++/* Configure Rx FIFO Flow control thresholds */ ++void mv_pp23_rx_fifo_fc_en(struct mv_pp2x *priv, int port, bool en) ++{ ++ int val; ++ ++ val = mv_pp2x_read(&priv->hw, MVPP2_RX_FLOW_CONTROL_REG(port)); ++ ++ if (en) ++ val |= MVPP2_RX_FLOW_CONTROL_EN; ++ else ++ val &= ~MVPP2_RX_FLOW_CONTROL_EN; ++ ++ mv_pp2x_write(&priv->hw, MVPP2_RX_FLOW_CONTROL_REG(port), val); ++} ++ ++/* Initialize Tx FIFO's */ ++static void mv_pp22_tx_fifo_init(struct mv_pp2x *priv) ++{ ++ int i; ++ ++ /* Check l4_chksum_jumbo_port */ ++ if (priv->l4_chksum_jumbo_port < MVPP2_MAX_PORTS) { ++ for (i = 0; i < priv->num_ports; i++) { ++ if (priv->port_list[i]->id == ++ priv->l4_chksum_jumbo_port) ++ break; ++ } ++ if (i == priv->num_ports) ++ WARN(1, "Unavailable l4_chksum_jumbo_port %d\n", ++ priv->l4_chksum_jumbo_port); ++ } else { ++ /* Find port with highest speed, allocate extra FIFO to it */ ++ for (i = 0; i < priv->num_ports; i++) { ++ phy_interface_t phy_mode = ++ priv->port_list[i]->mac_data.phy_mode; ++ ++ if ((phy_mode == PHY_INTERFACE_MODE_XAUI) || ++ (phy_mode == PHY_INTERFACE_MODE_RXAUI) || ++ (phy_mode == PHY_INTERFACE_MODE_10GKR) || ++ (phy_mode == PHY_INTERFACE_MODE_SFI) || ++ (phy_mode == PHY_INTERFACE_MODE_XFI)) { ++ /* Record l4_chksum_jumbo_port */ ++ priv->l4_chksum_jumbo_port = ++ priv->port_list[i]->id; ++ break; ++ } else if (priv->port_list[i]->mac_data.speed == 2500 && ++ (priv->l4_chksum_jumbo_port == ++ MVPP2_MAX_PORTS)) { ++ /* Only first 2.5G port may get extra FIFO */ ++ priv->l4_chksum_jumbo_port = ++ priv->port_list[i]->id; ++ } ++ } ++ /* First 1G port in list get extra FIFO */ ++ if (priv->l4_chksum_jumbo_port == MVPP2_MAX_PORTS) ++ priv->l4_chksum_jumbo_port = priv->port_list[0]->id; ++ } ++ ++ /* Set FIFO according to l4_chksum_jumbo_port. ++ * 10KB for l4_chksum_jumbo_port and 3KB for other ports. ++ * TX FIFO should be set for all ports, even if port not initialized. ++ */ ++ for (i = 0; i < MVPP2_MAX_PORTS; i++) { ++ if (i != priv->l4_chksum_jumbo_port) { ++ mv_pp2x_tx_fifo_size_set(&priv->hw, i, ++ MVPP2_TX_FIFO_DATA_SIZE_3KB); ++ mv_pp2x_tx_fifo_threshold_set(&priv->hw, i, ++ MVPP2_TX_FIFO_THRESHOLD_3KB); ++ } ++ } ++ mv_pp2x_tx_fifo_size_set(&priv->hw, ++ priv->l4_chksum_jumbo_port, ++ MVPP2_TX_FIFO_DATA_SIZE_10KB); ++ mv_pp2x_tx_fifo_threshold_set(&priv->hw, ++ priv->l4_chksum_jumbo_port, ++ MVPP2_TX_FIFO_THRESHOLD_10KB); ++} ++ ++/* Initialize Rx FIFO's */ ++static void mvpp21_rx_fifo_init(struct mv_pp2x *priv) ++{ ++ int port; ++ ++ for (port = 0; port < MVPP2_MAX_PORTS; port++) { ++ mv_pp2x_write(&priv->hw, MVPP2_RX_DATA_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_PORT_DATA_SIZE); ++ mv_pp2x_write(&priv->hw, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), ++ MVPP2_RX_FIFO_PORT_ATTR_SIZE); ++ } ++ ++ mv_pp2x_write(&priv->hw, MVPP2_RX_MIN_PKT_SIZE_REG, ++ MVPP2_RX_FIFO_PORT_MIN_PKT); ++ mv_pp2x_write(&priv->hw, MVPP2_RX_FIFO_INIT_REG, 0x1); ++} ++ ++/* Initialize Tx FIFO's */ ++static void mvpp21_tx_fifo_init(struct mv_pp2x *priv) ++{ ++ int val; ++ ++ /* Update TX FIFO MIN Threshold */ ++ val = mv_pp2x_read(&priv->hw, MVPP2_GMAC_PORT_FIFO_CFG_1_REG); ++ val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; ++ /* Min. TX threshold must be less than minimal packet length */ ++ val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2); ++ mv_pp2x_write(&priv->hw, MVPP2_GMAC_PORT_FIFO_CFG_1_REG, val); ++} ++ ++static void mv_pp21_fifo_init(struct mv_pp2x *priv) ++{ ++ mvpp21_rx_fifo_init(priv); ++ mvpp21_tx_fifo_init(priv); ++} ++ ++void mv_pp22_set_net_comp(struct mv_pp2x *priv) ++{ ++ u32 net_comp_config; ++ ++ /* Reset Netcomplex to default value */ ++ mv_gop110_rfu1_write(&priv->hw.gop, MV_NETCOMP_PORTS_CONTROL_0, ++ MV_NETCOMP_PORTS_CONTROL_0_DEF_VAL); ++ mv_gop110_rfu1_write(&priv->hw.gop, MV_NETCOMP_PORTS_CONTROL_1, ++ MV_NETCOMP_PORTS_CONTROL_1_DEF_VAL); ++ mv_gop110_rfu1_write(&priv->hw.gop, MV_NETCOMP_CONTROL_0, ++ MV_NETCOMP_CONTROL_0_DEF_VAL); ++ ++ net_comp_config = mvp_pp2x_gop110_netc_cfg_create(priv); ++ mv_gop110_netc_init(&priv->hw.gop, net_comp_config, MV_NETC_FIRST_PHASE); ++ mv_gop110_netc_init(&priv->hw.gop, net_comp_config, MV_NETC_SECOND_PHASE); ++} ++ ++/* Routine called by CP CPU hot plug notifier. Callback reconfigure RSS RX flow hash indir'n table */ ++static int mv_pp2x_cp_cpu_callback(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) ++{ ++ struct mv_pp2x *priv = container_of(nfb, struct mv_pp2x, cp_hotplug_nb); ++ unsigned int cpu = (unsigned long)hcpu; ++ int i; ++ struct mv_pp2x_port *port; ++ ++ /* RSS rebalanced to equal */ ++ mv_pp22_init_rxfhindir(priv); ++ ++ switch (action) { ++ case CPU_DEAD: ++ case CPU_DEAD_FROZEN: ++ /* If default CPU is down, CPU0 will be default CPU */ ++ for (i = 0; i < priv->num_ports; i++) { ++ port = priv->port_list[i]; ++ if (port && (cpu == port->rss_cfg.dflt_cpu)) { ++ port->rss_cfg.dflt_cpu = 0; ++ if (port->rss_cfg.rss_en && netif_running(port->dev)) ++ mv_pp22_rss_default_cpu_set(port, ++ port->rss_cfg.dflt_cpu); ++ } ++ } ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static int mv_pp2x_probe(struct platform_device *pdev) ++{ ++ struct mv_pp2x *priv; ++ struct mv_pp2x_hw *hw; ++ int port_count = 0, cpu; ++ int i, err, val; ++ u32 cell_index = 0; ++ struct device_node *dn = pdev->dev.of_node; ++ struct device_node *port_node; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ bool probe_defer = false; ++ unsigned long flags = 0; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(struct mv_pp2x), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ hw = &priv->hw; ++ priv->pdev = pdev; ++ ++ /* Init cls lock */ ++ spin_lock_init(&hw->cls_spinlock); ++ ++ err = mv_pp2x_platform_data_get(pdev, priv, &cell_index, &port_count); ++ if (err) { ++ if (err != -EPROBE_DEFER) { ++ dev_err(&pdev->dev, "mvpp2: platform_data get failed\n"); ++ } else { ++ /* Rollback auto_cell_index if it was used */ ++ if (auto_cell_index) ++ auto_cell_index--; ++ probe_defer = true; ++ } ++ goto err_clk; ++ } ++ priv->pp2_version = priv->pp2xdata->pp2x_ver; ++ ++ /* Retrieve dts defined cma-area (shared-dma-pool). ++ * Enables allocating cma_memory on DRAM that is physically close to the PPV2 device. ++ */ ++ of_reserved_mem_device_init(&pdev->dev); ++#if !defined MODULE ++ /* cma_get_base/dev_get_cma_area are Not exported by Kernel */ ++ { ++ phys_addr_t cma_addr; ++ ++ cma_addr = cma_get_base(dev_get_cma_area(&pdev->dev)); ++ dev_info(&pdev->dev, "cma_area base %pa size %ld MB\n", &cma_addr, ++ cma_get_size(dev_get_cma_area(&pdev->dev)) / SZ_1M); ++ } ++#endif ++ ++ mv_pp2x_used_addr_spaces = (mv_pp2x_queue_mode == MVPP2_SINGLE_RESOURCE_MODE) ? 1 : MVPP2_MAX_ADDR_SPACES; ++ ++ /* DMA Configruation */ ++ if (priv->pp2_version != PPV21) { ++ pdev->dev.dma_mask = kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL); ++ err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); ++ if (err == 0) ++ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(40)); ++ if (err) { ++ dev_err(&pdev->dev, "mvpp2: cannot set dma_mask\n"); ++ goto err_clk; ++ } ++ } ++ ++ /* Save cpu_present_mask + populate the per_cpu address space */ ++ for (i = 0; i < MVPP2_MAX_ADDR_SPACES; i++) { ++ hw->cpu_base[i] = hw->base; ++ if (priv->pp2xdata->multi_addr_space) ++ hw->cpu_base[i] += ++ i * MVPP2_ADDR_SPACE_SIZE; ++ } ++ ++ /* Differ between PPv22 and PPv23 */ ++ if (priv->pp2_version != PPV21) { ++ if (mv_pp2x_read(hw, MVPP2_VER_ID_REG) == MVPP2_VER_PP23) { ++ priv->pp2_version = PPV23; ++ priv->pp2xdata->pp2x_ver = PPV23; ++ hw->gop.gop_110.cp_version = MV_CP115; ++ } else { ++ hw->gop.gop_110.cp_version = MV_CP110; ++ } ++ } ++ ++ /* Init PP2 Configuration */ ++ err = mv_pp2x_init_config(&priv->pp2_cfg, cell_index); ++ priv->hw.mv_pp2x_no_single_mode = (mv_pp2x_queue_mode == MVPP2_SINGLE_RESOURCE_MODE) ? 0 : 1; ++ ++ if (err < 0) { ++ dev_err(&pdev->dev, "invalid driver parameters configured\n"); ++ goto err_clk; ++ } ++ ++ /* Init mss lock */ ++ spin_lock_init(&priv->mss_spinlock); ++ ++ /* Initialize network controller */ ++ err = mv_pp2x_init(pdev, priv); ++ if (err < 0) { ++ dev_err(&pdev->dev, "failed to initialize controller\n"); ++ goto err_clk; ++ } ++ ++ priv->port_list = devm_kcalloc(&pdev->dev, port_count, ++ sizeof(struct mv_pp2x_port *), ++ GFP_KERNEL); ++ if (!priv->port_list) { ++ err = -ENOMEM; ++ goto err_clk; ++ } ++ ++ priv->pcpu = devm_kcalloc(&pdev->dev, MVPP2_MAX_ADDR_SPACES, sizeof(*priv->pcpu), ++ GFP_KERNEL); ++ if (!priv->pcpu) { ++ err = -ENOMEM; ++ goto err_clk; ++ } ++ ++ /* Init per CPU CP skb list for skb recycling */ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ cp_pcpu = devm_kcalloc(&pdev->dev, 1, sizeof(*cp_pcpu), GFP_KERNEL); ++ priv->pcpu[cpu] = cp_pcpu; ++ ++ INIT_LIST_HEAD(&cp_pcpu->skb_port_list); ++ cp_pcpu->skb_pool = devm_kzalloc(&pdev->dev, ++ sizeof(struct mv_pp2x_skb_pool), GFP_ATOMIC); ++ cp_pcpu->skb_pool->skb_pool_size = MVPP2_SKB_NUM; ++ cp_pcpu->skb_pool->skb_struct = ++ devm_kzalloc(&pdev->dev, ++ sizeof(struct mv_pp2x_skb_struct) * MVPP2_SKB_NUM, GFP_ATOMIC); ++ } ++ ++ /* Init PP22&23 rxfhindir table evenly in probe */ ++ if (priv->pp2_version != PPV21) { ++ mv_pp22_init_rxfhindir(priv); ++ if (mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) ++ priv->num_rss_tables = mv_pp2x_num_cos_queues; ++ else ++ priv->num_rss_tables = 0; ++ } ++ ++#ifdef CONFIG_UIO ++ if (priv->pp2_version != PPV21) { ++ priv->uio.u_info.name = kasprintf(GFP_KERNEL, UIO_PP2_STRING, cell_index); ++ pr_debug("mv_pp2x_probe : %s\n", priv->uio.u_info.name); ++ priv->uio.u_info.version = "0.1"; ++ err = uio_register_device(&pdev->dev, &priv->uio.u_info); ++ if (err) { ++ dev_err(&pdev->dev, "Failed to register uio device\n"); ++ goto err_clk; ++ } ++ } ++#endif ++ ++ /* Initialize ports */ ++ for_each_available_child_of_node(dn, port_node) { ++ err = mv_pp2x_port_probe(pdev, port_node, priv); ++ if (err < 0) ++ goto err_uio; ++ } ++ /* Init hrtimer for tx transmit procedure. ++ * Instead of reg_write atfer each xmit callback, 50 microsecond ++ * hrtimer would be started. Hrtimer will reduce amount of accesses ++ * to transmit register and dmb() influence on network performance. ++ */ ++ for (cpu = 0; cpu < mv_pp2x_used_addr_spaces; cpu++) { ++ cp_pcpu = priv->pcpu[cpu]; ++ ++ hrtimer_init(&cp_pcpu->tx_timer, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL_PINNED); ++ cp_pcpu->tx_timer.function = mv_pp2x_tx_hr_timer_cb; ++ cp_pcpu->tx_timer_scheduled = false; ++ ++ tasklet_init(&cp_pcpu->tx_tasklet, ++ mv_pp2x_tx_send_proc_cb, (unsigned long)priv); ++ } ++ ++ if (priv->pp2_version != PPV21) { ++ /* Init tx&rx fifo for each port */ ++ mv_pp22_tx_fifo_init(priv); ++ mv_pp22_rx_fifo_init(priv); ++ mv_pp22_set_net_comp(priv); ++ if (priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_set_tresh(priv); ++ } else { ++ mv_pp21_fifo_init(priv); ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ ++ priv->workqueue = create_singlethread_workqueue("mv_pp2x"); ++ ++ if (!priv->workqueue) { ++ err = -ENOMEM; ++ goto err_uio; ++ } ++ ++ /* Only Mvpp22&23 support hot plug feature */ ++ if (priv->pp2_version != PPV21 && mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) { ++ priv->cp_hotplug_nb.notifier_call = mv_pp2x_cp_cpu_callback; ++ register_hotcpu_notifier(&priv->cp_hotplug_nb); ++ } ++ INIT_DELAYED_WORK(&priv->stats_task, mv_pp2x_get_device_stats); ++ ++ queue_delayed_work(priv->workqueue, &priv->stats_task, stats_delay); ++ ++ spin_lock_irqsave(&priv->mss_spinlock, flags); ++ ++ /* Enable global flow control. ++ * In this stage global flow control enabled, but still disabled per port. ++ */ ++ val = mv_pp2x_cm3_read(hw, MSS_CP_FC_COM_REG); ++ val |= FLOW_CONTROL_ENABLE_BIT; ++ mv_pp2x_cm3_write(hw, MSS_CP_FC_COM_REG, val); ++ ++ spin_unlock_irqrestore(&priv->mss_spinlock, flags); ++ ++ pr_debug("Platform Device Name : %s\n", kobject_name(&pdev->dev.kobj)); ++ return 0; ++err_uio: ++#ifdef CONFIG_UIO ++ if (priv->pp2_version != PPV21) ++ uio_unregister_device(&priv->uio.u_info); ++#endif ++err_clk: ++ clk_disable_unprepare(hw->gop_clk); ++ clk_disable_unprepare(hw->pp_clk); ++ if (priv->pp2_version != PPV21) { ++ clk_disable_unprepare(hw->gop_core_clk); ++ clk_disable_unprepare(hw->mg_clk); ++ clk_disable_unprepare(hw->mg_core_clk); ++ kfree(priv->uio.u_info.name); ++ } ++ if (probe_defer) ++ devm_kfree(&pdev->dev, priv); ++ return err; ++} ++ ++static int mv_pp2x_remove(struct platform_device *pdev) ++{ ++ struct mv_pp2x *priv = platform_get_drvdata(pdev); ++ struct mv_pp2x_hw *hw = &priv->hw; ++ int i, num_of_ports, num_of_pools; ++ struct mv_pp2x_cp_pcpu *cp_pcpu; ++ ++ if (priv->pp2_version != PPV21 && mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) ++ unregister_hotcpu_notifier(&priv->cp_hotplug_nb); ++ ++ if (priv->pp2_version != PPV21) { ++#ifdef CONFIG_UIO ++ uio_unregister_device(&priv->uio.u_info); ++#endif ++ kfree(priv->uio.u_info.name); ++ } ++ cancel_delayed_work(&priv->stats_task); ++ flush_workqueue(priv->workqueue); ++ destroy_workqueue(priv->workqueue); ++ ++ for (i = 0; i < mv_pp2x_used_addr_spaces; i++) { ++ cp_pcpu = priv->pcpu[i]; ++ tasklet_kill(&cp_pcpu->tx_tasklet); ++ } ++ ++ num_of_ports = priv->num_ports; ++ ++ for (i = 0; i < num_of_ports; i++) { ++ if (priv->port_list[i]) ++ mv_pp2x_port_remove(priv->port_list[i]); ++ priv->num_ports--; ++ } ++ ++ num_of_pools = priv->num_pools; ++ ++ for (i = 0; i < num_of_pools; i++) { ++ struct mv_pp2x_bm_pool *bm_pool = &priv->bm_pools[i]; ++ ++ mv_pp2x_bm_pool_destroy(&pdev->dev, priv, bm_pool); ++ } ++ ++ for (i = 0; i < mv_pp2x_used_addr_spaces; i++) { ++ struct mv_pp2x_aggr_tx_queue *aggr_txq = &priv->aggr_txqs[i]; ++ ++ dma_free_coherent(&pdev->dev, ++ MVPP2_DESCQ_MEM_SIZE(aggr_txq->size), ++ aggr_txq->desc_mem, ++ aggr_txq->descs_phys); ++ } ++ ++ kfree(pdev->dev.dma_mask); ++ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; ++ ++ clk_disable_unprepare(hw->pp_clk); ++ clk_disable_unprepare(hw->gop_clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++/* Ports initialization after suspend */ ++static int mv_pp2x_port_resume(struct platform_device *pdev, ++ struct device_node *port_node, ++ struct mv_pp2x *priv, struct mv_pp2x_port *port) ++{ ++ if (port->mac_data.phy_node) ++ mv_pp2x_phy_connect(port); ++ ++ mv_pp2x_port_hw_init(port); ++ ++ return 0; ++} ++ ++/* Routine configure mvpp2x HW after suspend */ ++static int mv_pp2x_probe_after_suspend(struct device *dev) ++{ ++ struct mv_pp2x *priv = dev_get_drvdata(dev); ++ int i, err; ++ struct platform_device *pdev = priv->pdev; ++ struct device_node *dn = pdev->dev.of_node; ++ struct device_node *port_node; ++ ++ /* Resume network controller */ ++ err = mv_pp2x_init(pdev, priv); ++ if (err < 0) { ++ dev_err(&pdev->dev, "failed to resume controller\n"); ++ return err; ++ } ++ ++ i = 0; ++ /* Resume ports */ ++ for_each_available_child_of_node(dn, port_node) { ++ mv_pp2x_port_resume(pdev, port_node, priv, priv->port_list[i]); ++ i++; ++ } ++ ++ if (priv->pp2_version != PPV21) { ++ /* Init tx&rx fifo for each port */ ++ mv_pp22_tx_fifo_init(priv); ++ mv_pp22_rx_fifo_init(priv); ++ mv_pp22_set_net_comp(priv); ++ if (priv->pp2_version == PPV23) ++ mv_pp23_rx_fifo_fc_set_tresh(priv); ++ } else { ++ mv_pp21_fifo_init(priv); ++ } ++ ++ return 0; ++} ++ ++/* Routine suspend to RAM CP */ ++static int mvpp2x_suspend(struct device *dev) ++{ ++ struct mv_pp2x *priv = dev_get_drvdata(dev); ++ int i, num_of_ports, num_of_pools; ++ struct platform_device *pdev = priv->pdev; ++ ++ num_of_ports = priv->num_ports; ++ for (i = 0; i < num_of_ports; i++) { ++ struct mv_mac_data *mac = &priv->port_list[i]->mac_data; ++ /* Stop interface if port is up */ ++ if (netif_running(priv->port_list[i]->dev)) ++ mv_pp2x_stop(priv->port_list[i]->dev); ++ ++ if (mac->phy_node) ++ mv_pp2x_phy_disconnect(priv->port_list[i]); ++ ++ /* Null all pointers to BM pools, pool's will be reconfigured in resume procedure */ ++ priv->port_list[i]->pool_short = NULL; ++ priv->port_list[i]->pool_long = NULL; ++ /* Missing flag will trigger GoP reconfiguration in resume routine */ ++ mac->flags &= ~MV_EMAC_F_INIT; ++ } ++ ++ for (i = 0; i < mv_pp2x_used_addr_spaces; i++) { ++ struct mv_pp2x_aggr_tx_queue *aggr_txq = &priv->aggr_txqs[i]; ++ ++ dma_free_coherent(&pdev->dev, ++ MVPP2_DESCQ_MEM_SIZE(aggr_txq->size), ++ aggr_txq->desc_mem, aggr_txq->descs_phys); ++ } ++ ++ devm_kfree(&pdev->dev, priv->hw.prs_shadow); ++ devm_kfree(&pdev->dev, priv->hw.cls_shadow); ++ devm_kfree(&pdev->dev, priv->hw.c2_shadow); ++ /* aggr_txqs is aligned round-up; restore the original by -offset */ ++ devm_kfree(&pdev->dev, ((u8 *)priv->aggr_txqs - priv->aggr_txqs_align_offs)); ++ ++ num_of_pools = priv->num_pools; ++ for (i = 0; i < num_of_pools; i++) { ++ struct mv_pp2x_bm_pool *bm_pool = &priv->bm_pools[i]; ++ ++ mv_pp2x_bm_pool_destroy(dev, priv, bm_pool); ++ } ++ ++ devm_kfree(&pdev->dev, priv->bm_pools); ++ ++ return 0; ++} ++ ++/* Routine resume CP after S2RAM */ ++static int mvpp2x_resume(struct device *dev) ++{ ++ struct mv_pp2x *priv; ++ int i, j, num_of_ports, err; ++ ++ err = mv_pp2x_probe_after_suspend(dev); ++ if (err < 0) ++ return err; ++ ++ priv = dev_get_drvdata(dev); ++ num_of_ports = priv->num_ports; ++ ++ for (i = 0; i < num_of_ports; i++) { ++ if (priv->port_list[i]->comphy) ++ for (j = 0; j < priv->port_list[i]->num_serdes_lanes; j++) ++ phy_init(priv->port_list[i]->comphy[j]); ++ /* Start interface if port was up before suspend */ ++ if (netif_running(priv->port_list[i]->dev)) ++ mv_pp2x_open(priv->port_list[i]->dev); ++ ++ if (priv->port_list[i]->dev->flags & IFF_PROMISC) ++ mv_pp2x_set_rx_promisc(priv->port_list[i]); ++ else if (priv->port_list[i]->dev->flags & IFF_ALLMULTI) ++ mv_pp2x_set_rx_allmulti(priv->port_list[i]); ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops mv_pp2x_pm_ops = { ++ .suspend = mvpp2x_suspend, ++ .resume = mvpp2x_resume, ++}; ++#endif /* CONFIG_PM_SLEEP */ ++ ++MODULE_DEVICE_TABLE(of, mv_pp2x_match_tbl); ++ ++static struct platform_driver mv_pp2x_driver = { ++ .probe = mv_pp2x_probe, ++ .remove = mv_pp2x_remove, ++ .driver = { ++ .name = MVPP2_DRIVER_NAME, ++ .of_match_table = mv_pp2x_match_tbl, ++#ifdef CONFIG_PM_SLEEP ++ .pm = &mv_pp2x_pm_ops, ++#endif ++ }, ++}; ++ ++static int mv_pp2x_rxq_number_get(void) ++{ ++ int rx_queue_num; ++ ++ if (mv_pp2x_queue_mode == MVPP2_QDIST_MULTI_MODE) ++ if (mv_pp2x_num_cos_queues * num_active_cpus() > MVPP22_MAX_NUM_RXQ) ++ rx_queue_num = MVPP22_MAX_NUM_RXQ; ++ else ++ rx_queue_num = mv_pp2x_num_cos_queues * num_active_cpus(); ++ else ++ rx_queue_num = mv_pp2x_num_cos_queues; ++ ++ return rx_queue_num; ++} ++ ++static int __init mpp2_module_init(void) ++{ ++ int ret = 0; ++ ++ mv_pp2x_rxq_number = mv_pp2x_rxq_number_get(); ++ mv_pp2x_txq_number = mv_pp2x_num_cos_queues; ++ ++ /* Compiler does not allow below Init in structure definition */ ++ mv_pp2x_pools[MVPP2_BM_SWF_SHORT_POOL].pkt_size = ++ MVPP2_BM_SHORT_PKT_SIZE; ++ mv_pp2x_pools[MVPP2_BM_SWF_LONG_POOL].pkt_size = ++ MVPP2_BM_LONG_PKT_SIZE; ++ mv_pp2x_pools[MVPP2_BM_SWF_JUMBO_POOL].pkt_size = ++ MVPP2_BM_JUMBO_PKT_SIZE; ++ ++ ret = platform_driver_register(&mv_pp2x_driver); ++ ++ return ret; ++} ++ ++static void __exit mpp2_module_exit(void) ++{ ++ platform_driver_unregister(&mv_pp2x_driver); ++} ++ ++module_init(mpp2_module_init); ++module_exit(mpp2_module_exit); ++ ++MODULE_DESCRIPTION("Marvell PPv2x Ethernet Driver - www.marvell.com"); ++MODULE_AUTHOR("Marvell"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c +index a296016..2058e5d 100644 +--- a/drivers/net/ethernet/nuvoton/w90p910_ether.c ++++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c +@@ -915,7 +915,6 @@ static u32 w90p910_get_link(struct net_device *dev) + .ndo_set_mac_address = w90p910_set_mac_address, + .ndo_do_ioctl = w90p910_ether_ioctl, + .ndo_validate_addr = eth_validate_addr, +- .ndo_change_mtu = eth_change_mtu, + }; + + static void get_mac_address(struct net_device *dev) +diff --git a/drivers/of/irq.c b/drivers/of/irq.c +index 393fea8..a389d7b 100644 +--- a/drivers/of/irq.c ++++ b/drivers/of/irq.c +@@ -364,7 +364,10 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar + */ + int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) + { +- int irq = irq_of_parse_and_map(dev, index); ++ int irq = of_irq_get(dev, index); ++ ++ if (irq < 0) ++ return irq; + + /* Only dereference the resource if both the + * resource and the irq are valid. */ +diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c +index 9a3f2a6..dc3a1c2 100644 +--- a/drivers/rtc/rtc-armada38x.c ++++ b/drivers/rtc/rtc-armada38x.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -23,17 +24,52 @@ + #define RTC_STATUS_ALARM1 BIT(0) + #define RTC_STATUS_ALARM2 BIT(1) + #define RTC_IRQ1_CONF 0x4 +-#define RTC_IRQ1_AL_EN BIT(0) +-#define RTC_IRQ1_FREQ_EN BIT(1) +-#define RTC_IRQ1_FREQ_1HZ BIT(2) ++#define RTC_IRQ2_CONF 0x8 ++#define RTC_IRQ_AL_EN BIT(0) ++#define RTC_IRQ_FREQ_EN BIT(1) ++#define RTC_IRQ_FREQ_1HZ BIT(2) ++#define RTC_CCR 0x18 ++#define RTC_CCR_MODE BIT(15) ++#define RTC_CONF_TEST 0x1C ++#define RTC_NOMINAL_TIMING BIT(13) ++ + #define RTC_TIME 0xC + #define RTC_ALARM1 0x10 +- +-#define SOC_RTC_INTERRUPT 0x8 +-#define SOC_RTC_ALARM1 BIT(0) +-#define SOC_RTC_ALARM2 BIT(1) +-#define SOC_RTC_ALARM1_MASK BIT(2) +-#define SOC_RTC_ALARM2_MASK BIT(3) ++#define RTC_ALARM2 0x14 ++ ++/* Armada38x SoC registers */ ++#define RTC_38X_BRIDGE_TIMING_CTL 0x0 ++#define RTC_38X_PERIOD_OFFS 0 ++#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) ++#define RTC_38X_READ_DELAY_OFFS 26 ++#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) ++ ++/* Armada 7K/8K registers */ ++#define RTC_8K_BRIDGE_TIMING_CTL0 0x0 ++#define RTC_8K_WRCLK_PERIOD_OFFS 0 ++#define RTC_8K_WRCLK_PERIOD_MASK (0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS) ++#define RTC_8K_WRCLK_SETUP_OFFS 16 ++#define RTC_8K_WRCLK_SETUP_MASK (0xFFFF << RTC_8K_WRCLK_SETUP_OFFS) ++#define RTC_8K_BRIDGE_TIMING_CTL1 0x4 ++#define RTC_8K_READ_DELAY_OFFS 0 ++#define RTC_8K_READ_DELAY_MASK (0xFFFF << RTC_8K_READ_DELAY_OFFS) ++ ++#define RTC_8K_ISR 0x10 ++#define RTC_8K_IMR 0x14 ++#define RTC_8K_ALARM2 BIT(0) ++ ++#define SOC_RTC_INTERRUPT 0x8 ++#define SOC_RTC_ALARM1 BIT(0) ++#define SOC_RTC_ALARM2 BIT(1) ++#define SOC_RTC_ALARM1_MASK BIT(2) ++#define SOC_RTC_ALARM2_MASK BIT(3) ++ ++#define SAMPLE_NR 100 ++ ++struct value_to_freq { ++ u32 value; ++ u8 freq; ++}; + + struct armada38x_rtc { + struct rtc_device *rtc_dev; +@@ -41,42 +77,179 @@ struct armada38x_rtc { + void __iomem *regs_soc; + spinlock_t lock; + int irq; ++ bool initialized; ++ struct value_to_freq *val_to_freq; ++ struct armada38x_rtc_data *data; ++}; ++ ++#define ALARM1 0 ++#define ALARM2 1 ++ ++#define ALARM_REG(base, alarm) ((base) + (alarm) * sizeof(u32)) ++ ++struct armada38x_rtc_data { ++ /* Initialize the RTC-MBUS bridge timing */ ++ void (*update_mbus_timing)(struct armada38x_rtc *rtc); ++ u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg); ++ void (*clear_isr)(struct armada38x_rtc *rtc); ++ void (*unmask_interrupt)(struct armada38x_rtc *rtc); ++ u32 alarm; + }; + + /* + * According to the datasheet, the OS should wait 5us after every + * register write to the RTC hard macro so that the required update + * can occur without holding off the system bus ++ * According to errata RES-3124064, Write to any RTC register ++ * may fail. As a workaround, before writing to RTC ++ * register, issue a dummy write of 0x0 twice to RTC Status ++ * register. ++ * According to the datasheet, the RTC time register should be written ++ * twice to guarantee the write is performed correctly + */ ++ + static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset) + { ++ writel(0, rtc->regs + RTC_STATUS); ++ writel(0, rtc->regs + RTC_STATUS); ++ writel(val, rtc->regs + offset); ++ udelay(5); + writel(val, rtc->regs + offset); + udelay(5); + } + ++/* Update RTC-MBUS bridge timing parameters */ ++static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc) ++{ ++ u32 reg; ++ ++ reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); ++ reg &= ~RTC_38X_PERIOD_MASK; ++ reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ ++ reg &= ~RTC_38X_READ_DELAY_MASK; ++ reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ ++ writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); ++} ++ ++static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc) ++{ ++ u32 reg; ++ ++ reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); ++ reg &= ~RTC_8K_WRCLK_PERIOD_MASK; ++ reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS; ++ reg &= ~RTC_8K_WRCLK_SETUP_MASK; ++ reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS; ++ writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); ++ ++ reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); ++ reg &= ~RTC_8K_READ_DELAY_MASK; ++ reg |= 0x3F << RTC_8K_READ_DELAY_OFFS; ++ writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); ++} ++ ++static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg) ++{ ++ return readl(rtc->regs + rtc_reg); ++} ++ ++static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg) ++{ ++ int i, index_max = 0, max = 0; ++ ++ for (i = 0; i < SAMPLE_NR; i++) { ++ rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg); ++ rtc->val_to_freq[i].freq = 0; ++ } ++ ++ for (i = 0; i < SAMPLE_NR; i++) { ++ int j = 0; ++ u32 value = rtc->val_to_freq[i].value; ++ ++ while (rtc->val_to_freq[j].freq) { ++ if (rtc->val_to_freq[j].value == value) { ++ rtc->val_to_freq[j].freq++; ++ break; ++ } ++ j++; ++ } ++ ++ if (!rtc->val_to_freq[j].freq) { ++ rtc->val_to_freq[j].value = value; ++ rtc->val_to_freq[j].freq = 1; ++ } ++ ++ if (rtc->val_to_freq[j].freq > max) { ++ index_max = j; ++ max = rtc->val_to_freq[j].freq; ++ } ++ ++ /* ++ * If a value already has half of the sample this is the most ++ * frequent one and we can stop the research right now ++ */ ++ if (max > SAMPLE_NR / 2) ++ break; ++ } ++ ++ return rtc->val_to_freq[index_max].value; ++} ++ ++static void armada38x_clear_isr(struct armada38x_rtc *rtc) ++{ ++ u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); ++ ++ writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); ++} ++ ++static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc) ++{ ++ u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); ++ ++ writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT); ++} ++ ++static void armada8k_clear_isr(struct armada38x_rtc *rtc) ++{ ++ writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR); ++} ++ ++static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc) ++{ ++ writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR); ++} ++ + static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) + { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); +- unsigned long time, time_check, flags; ++ unsigned long time, flags; + + spin_lock_irqsave(&rtc->lock, flags); +- time = readl(rtc->regs + RTC_TIME); +- /* +- * WA for failing time set attempts. As stated in HW ERRATA if +- * more than one second between two time reads is detected +- * then read once again. +- */ +- time_check = readl(rtc->regs + RTC_TIME); +- if ((time_check - time) > 1) +- time_check = readl(rtc->regs + RTC_TIME); +- ++ time = rtc->data->read_rtc_reg(rtc, RTC_TIME); + spin_unlock_irqrestore(&rtc->lock, flags); + +- rtc_time_to_tm(time_check, tm); ++ rtc_time_to_tm(time, tm); + + return 0; + } + ++static void armada38x_rtc_reset(struct armada38x_rtc *rtc) ++{ ++ u32 reg; ++ ++ reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST); ++ /* If bits [7:0] are non-zero, assume RTC was uninitialized */ ++ if (reg & 0xff) { ++ rtc_delayed_write(0, rtc, RTC_CONF_TEST); ++ msleep(500); /* Oscillator startup time */ ++ rtc_delayed_write(0, rtc, RTC_TIME); ++ rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc, ++ RTC_STATUS); ++ rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR); ++ } ++ rtc->initialized = true; ++} ++ + static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) + { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); +@@ -87,16 +260,12 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) + + if (ret) + goto out; +- /* +- * According to errata FE-3124064, Write to RTC TIME register +- * may fail. As a workaround, after writing to RTC TIME +- * register, issue a dummy write of 0x0 twice to RTC Status +- * register. +- */ ++ ++ if (!rtc->initialized) ++ armada38x_rtc_reset(rtc); ++ + spin_lock_irqsave(&rtc->lock, flags); + rtc_delayed_write(time, rtc, RTC_TIME); +- rtc_delayed_write(0, rtc, RTC_STATUS); +- rtc_delayed_write(0, rtc, RTC_STATUS); + spin_unlock_irqrestore(&rtc->lock, flags); + + out: +@@ -107,12 +276,14 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) + { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long time, flags; ++ u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); ++ u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + u32 val; + + spin_lock_irqsave(&rtc->lock, flags); + +- time = readl(rtc->regs + RTC_ALARM1); +- val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN; ++ time = rtc->data->read_rtc_reg(rtc, reg); ++ val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN; + + spin_unlock_irqrestore(&rtc->lock, flags); + +@@ -125,9 +296,10 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) + static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) + { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); ++ u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); ++ u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + unsigned long time, flags; + int ret = 0; +- u32 val; + + ret = rtc_tm_to_time(&alrm->time, &time); + +@@ -136,13 +308,11 @@ static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) + + spin_lock_irqsave(&rtc->lock, flags); + +- rtc_delayed_write(time, rtc, RTC_ALARM1); ++ rtc_delayed_write(time, rtc, reg); + + if (alrm->enabled) { +- rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF); +- val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); +- writel(val | SOC_RTC_ALARM1_MASK, +- rtc->regs_soc + SOC_RTC_INTERRUPT); ++ rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); ++ rtc->data->unmask_interrupt(rtc); + } + + spin_unlock_irqrestore(&rtc->lock, flags); +@@ -155,14 +325,15 @@ static int armada38x_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) + { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); ++ u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + if (enabled) +- rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF); ++ rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); + else +- rtc_delayed_write(0, rtc, RTC_IRQ1_CONF); ++ rtc_delayed_write(0, rtc, reg_irq); + + spin_unlock_irqrestore(&rtc->lock, flags); + +@@ -174,24 +345,23 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) + struct armada38x_rtc *rtc = data; + u32 val; + int event = RTC_IRQF | RTC_AF; ++ u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + + dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq); + + spin_lock(&rtc->lock); + +- val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); +- +- writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); +- val = readl(rtc->regs + RTC_IRQ1_CONF); +- /* disable all the interrupts for alarm 1 */ +- rtc_delayed_write(0, rtc, RTC_IRQ1_CONF); ++ rtc->data->clear_isr(rtc); ++ val = rtc->data->read_rtc_reg(rtc, reg_irq); ++ /* disable all the interrupts for alarm*/ ++ rtc_delayed_write(0, rtc, reg_irq); + /* Ack the event */ +- rtc_delayed_write(RTC_STATUS_ALARM1, rtc, RTC_STATUS); ++ rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS); + + spin_unlock(&rtc->lock); + +- if (val & RTC_IRQ1_FREQ_EN) { +- if (val & RTC_IRQ1_FREQ_1HZ) ++ if (val & RTC_IRQ_FREQ_EN) { ++ if (val & RTC_IRQ_FREQ_1HZ) + event |= RTC_UF; + else + event |= RTC_PF; +@@ -202,25 +372,172 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) + return IRQ_HANDLED; + } + +-static struct rtc_class_ops armada38x_rtc_ops = { ++/* ++ * The information given in the Armada 388 functional spec is complex. ++ * They give two different formulas for calculating the offset value, ++ * but when considering "Offset" as an 8-bit signed integer, they both ++ * reduce down to (we shall rename "Offset" as "val" here): ++ * ++ * val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768 ++ * ++ * Converting to time, f = 1/t: ++ * val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768 ++ * ++ * => t_measured / t_ideal = val * resolution + 1 ++ * ++ * "offset" in the RTC interface is defined as: ++ * t = t0 * (1 + offset * 1e-9) ++ * where t is the desired period, t0 is the measured period with a zero ++ * offset, which is t_measured above. With t0 = t_measured and t = t_ideal, ++ * offset = (t_ideal / t_measured - 1) / 1e-9 ++ * ++ * => t_ideal / t_measured = offset * 1e-9 + 1 ++ * ++ * so: ++ * ++ * offset * 1e-9 + 1 = 1 / (val * resolution + 1) ++ * ++ * We want "resolution" to be an integer, so resolution = R * 1e-9, giving ++ * offset = 1e18 / (val * R + 1e9) - 1e9 ++ * val = (1e18 / (offset + 1e9) - 1e9) / R ++ * with a common transformation: ++ * f(x) = 1e18 / (x + 1e9) - 1e9 ++ * offset = f(val * R) ++ * val = f(offset) / R ++ * ++ * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb). ++ */ ++static long armada38x_ppb_convert(long ppb) ++{ ++ long div = ppb + 1000000000L; ++ ++ return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L; ++} ++ ++static int armada38x_rtc_read_offset(struct device *dev, long *offset) ++{ ++ struct armada38x_rtc *rtc = dev_get_drvdata(dev); ++ unsigned long ccr, flags; ++ long ppb_cor; ++ ++ spin_lock_irqsave(&rtc->lock, flags); ++ ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR); ++ spin_unlock_irqrestore(&rtc->lock, flags); ++ ++ ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr; ++ /* ppb_cor + 1000000000L can never be zero */ ++ *offset = armada38x_ppb_convert(ppb_cor); ++ ++ return 0; ++} ++ ++static int armada38x_rtc_set_offset(struct device *dev, long offset) ++{ ++ struct armada38x_rtc *rtc = dev_get_drvdata(dev); ++ unsigned long ccr = 0; ++ long ppb_cor, off; ++ ++ /* ++ * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we ++ * need to clamp the input. This equates to -484270 .. 488558. ++ * Not only is this to stop out of range "off" but also to ++ * avoid the division by zero in armada38x_ppb_convert(). ++ */ ++ offset = clamp(offset, -484270L, 488558L); ++ ++ ppb_cor = armada38x_ppb_convert(offset); ++ ++ /* ++ * Use low update mode where possible, which gives a better ++ * resolution of correction. ++ */ ++ off = DIV_ROUND_CLOSEST(ppb_cor, 954); ++ if (off > 127 || off < -128) { ++ ccr = RTC_CCR_MODE; ++ off = DIV_ROUND_CLOSEST(ppb_cor, 3815); ++ } ++ ++ /* ++ * Armada 388 requires a bit pattern in bits 14..8 depending on ++ * the sign bit: { 0, ~S, S, S, S, S, S } ++ */ ++ ccr |= (off & 0x3fff) ^ 0x2000; ++ rtc_delayed_write(ccr, rtc, RTC_CCR); ++ ++ return 0; ++} ++ ++static const struct rtc_class_ops armada38x_rtc_ops = { + .read_time = armada38x_rtc_read_time, + .set_time = armada38x_rtc_set_time, + .read_alarm = armada38x_rtc_read_alarm, + .set_alarm = armada38x_rtc_set_alarm, + .alarm_irq_enable = armada38x_rtc_alarm_irq_enable, ++ .read_offset = armada38x_rtc_read_offset, ++ .set_offset = armada38x_rtc_set_offset, + }; + ++static const struct rtc_class_ops armada38x_rtc_ops_noirq = { ++ .read_time = armada38x_rtc_read_time, ++ .set_time = armada38x_rtc_set_time, ++ .read_alarm = armada38x_rtc_read_alarm, ++ .read_offset = armada38x_rtc_read_offset, ++ .set_offset = armada38x_rtc_set_offset, ++}; ++ ++static const struct armada38x_rtc_data armada38x_data = { ++ .update_mbus_timing = rtc_update_38x_mbus_timing_params, ++ .read_rtc_reg = read_rtc_register_38x_wa, ++ .clear_isr = armada38x_clear_isr, ++ .unmask_interrupt = armada38x_unmask_interrupt, ++ .alarm = ALARM1, ++}; ++ ++static const struct armada38x_rtc_data armada8k_data = { ++ .update_mbus_timing = rtc_update_8k_mbus_timing_params, ++ .read_rtc_reg = read_rtc_register, ++ .clear_isr = armada8k_clear_isr, ++ .unmask_interrupt = armada8k_unmask_interrupt, ++ .alarm = ALARM2, ++}; ++ ++#ifdef CONFIG_OF ++static const struct of_device_id armada38x_rtc_of_match_table[] = { ++ { ++ .compatible = "marvell,armada-380-rtc", ++ .data = &armada38x_data, ++ }, ++ { ++ .compatible = "marvell,armada-8k-rtc", ++ .data = &armada8k_data, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); ++#endif ++ + static __init int armada38x_rtc_probe(struct platform_device *pdev) + { ++ const struct rtc_class_ops *ops; + struct resource *res; + struct armada38x_rtc *rtc; ++ const struct of_device_id *match; + int ret; + ++ match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ + rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc), + GFP_KERNEL); + if (!rtc) + return -ENOMEM; + ++ rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR, ++ sizeof(struct value_to_freq), GFP_KERNEL); ++ if (!rtc->val_to_freq) ++ return -ENOMEM; ++ + spin_lock_init(&rtc->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc"); +@@ -242,19 +559,27 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) + 0, pdev->name, rtc) < 0) { + dev_warn(&pdev->dev, "Interrupt not available.\n"); + rtc->irq = -1; ++ } ++ platform_set_drvdata(pdev, rtc); ++ ++ if (rtc->irq != -1) { ++ device_init_wakeup(&pdev->dev, 1); ++ ops = &armada38x_rtc_ops; ++ } else { + /* + * If there is no interrupt available then we can't + * use the alarm + */ +- armada38x_rtc_ops.set_alarm = NULL; +- armada38x_rtc_ops.alarm_irq_enable = NULL; ++ ops = &armada38x_rtc_ops_noirq; + } +- platform_set_drvdata(pdev, rtc); +- if (rtc->irq != -1) +- device_init_wakeup(&pdev->dev, 1); ++ rtc->data = (struct armada38x_rtc_data *)match->data; ++ ++ ++ /* Update RTC-MBUS bridge timing parameters */ ++ rtc->data->update_mbus_timing(rtc); + + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, +- &armada38x_rtc_ops, THIS_MODULE); ++ ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); +@@ -280,6 +605,9 @@ static int armada38x_rtc_resume(struct device *dev) + if (device_may_wakeup(dev)) { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + ++ /* Update RTC-MBUS bridge timing parameters */ ++ rtc->data->update_mbus_timing(rtc); ++ + return disable_irq_wake(rtc->irq); + } + +@@ -290,14 +618,6 @@ static int armada38x_rtc_resume(struct device *dev) + static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops, + armada38x_rtc_suspend, armada38x_rtc_resume); + +-#ifdef CONFIG_OF +-static const struct of_device_id armada38x_rtc_of_match_table[] = { +- { .compatible = "marvell,armada-380-rtc", }, +- {} +-}; +-MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); +-#endif +- + static struct platform_driver armada38x_rtc_driver = { + .driver = { + .name = "armada38x-rtc", +diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c +index ca8b0b1..eb1b190 100644 +--- a/drivers/usb/host/xhci-plat.c ++++ b/drivers/usb/host/xhci-plat.c +@@ -143,6 +143,7 @@ static int xhci_plat_probe(struct platform_device *pdev) + struct resource *res; + struct usb_hcd *hcd; + struct clk *clk; ++ struct clk *reg_clk; + int ret; + int irq; + +@@ -185,17 +186,27 @@ static int xhci_plat_probe(struct platform_device *pdev) + hcd->rsrc_len = resource_size(res); + + /* +- * Not all platforms have a clk so it is not an error if the +- * clock does not exists. ++ * Not all platforms have clks so it is not an error if the ++ * clock do not exist. + */ ++ reg_clk = devm_clk_get(&pdev->dev, "reg"); ++ if (!IS_ERR(reg_clk)) { ++ ret = clk_prepare_enable(reg_clk); ++ if (ret) ++ goto put_hcd; ++ } else if (PTR_ERR(reg_clk) == -EPROBE_DEFER) { ++ ret = -EPROBE_DEFER; ++ goto put_hcd; ++ } ++ + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) +- goto put_hcd; ++ goto disable_reg_clk; + } else if (PTR_ERR(clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; +- goto put_hcd; ++ goto disable_reg_clk; + } + + xhci = hcd_to_xhci(hcd); +@@ -212,6 +223,7 @@ static int xhci_plat_probe(struct platform_device *pdev) + device_wakeup_enable(hcd->self.controller); + + xhci->clk = clk; ++ xhci->reg_clk = reg_clk; + xhci->main_hcd = hcd; + xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev, + dev_name(&pdev->dev), hcd); +@@ -262,8 +274,10 @@ static int xhci_plat_probe(struct platform_device *pdev) + usb_put_hcd(xhci->shared_hcd); + + disable_clk: +- if (!IS_ERR(clk)) +- clk_disable_unprepare(clk); ++ clk_disable_unprepare(clk); ++ ++disable_reg_clk: ++ clk_disable_unprepare(reg_clk); + + put_hcd: + usb_put_hcd(hcd); +@@ -276,6 +290,7 @@ static int xhci_plat_remove(struct platform_device *dev) + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct clk *clk = xhci->clk; ++ struct clk *reg_clk = xhci->reg_clk; + + xhci->xhc_state |= XHCI_STATE_REMOVING; + +@@ -285,8 +300,8 @@ static int xhci_plat_remove(struct platform_device *dev) + usb_remove_hcd(hcd); + usb_put_hcd(xhci->shared_hcd); + +- if (!IS_ERR(clk)) +- clk_disable_unprepare(clk); ++ clk_disable_unprepare(clk); ++ clk_disable_unprepare(reg_clk); + usb_put_hcd(hcd); + + return 0; +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +index de4771c..71d32c7 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -1565,8 +1565,9 @@ struct xhci_hcd { + /* msi-x vectors */ + int msix_count; + struct msix_entry *msix_entries; +- /* optional clock */ ++ /* optional clocks */ + struct clk *clk; ++ struct clk *reg_clk; + /* data structures */ + struct xhci_device_context_array *dcbaa; + struct xhci_ring *cmd_ring; +diff --git a/include/dt-bindings/interrupt-controller/mvebu-icu.h b/include/dt-bindings/interrupt-controller/mvebu-icu.h +new file mode 100644 +index 00000000..8249558 +--- /dev/null ++++ b/include/dt-bindings/interrupt-controller/mvebu-icu.h +@@ -0,0 +1,15 @@ ++/* ++ * This header provides constants for the MVEBU ICU driver. ++ */ ++ ++#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_MVEBU_ICU_H ++#define _DT_BINDINGS_INTERRUPT_CONTROLLER_MVEBU_ICU_H ++ ++/* interrupt specifier cell 0 */ ++ ++#define ICU_GRP_NSR 0x0 ++#define ICU_GRP_SR 0x1 ++#define ICU_GRP_SEI 0x4 ++#define ICU_GRP_REI 0x5 ++ ++#endif +diff --git a/include/dt-bindings/phy/phy-comphy-mvebu.h b/include/dt-bindings/phy/phy-comphy-mvebu.h +new file mode 100644 +index 00000000..ea174d7 +--- /dev/null ++++ b/include/dt-bindings/phy/phy-comphy-mvebu.h +@@ -0,0 +1,140 @@ ++#ifndef _DT_BINDINGS_PHY_COMPHY_MVEBU ++#define _DT_BINDINGS_PHY_COMPHY_MVEBU ++ ++/* A lane is described by 4 fields: ++ * - bit 1~0 represent comphy polarity invert ++ * - bit 7~2 represent comphy speed ++ * - bit 11~8 represent unit index ++ * - bit 16~12 represent mode ++ * - bit 17 represent comphy indication of clock source ++ * - bit 31~18 reserved ++ */ ++ ++#define COMPHY_INVERT_OFFSET 0 ++#define COMPHY_INVERT_LEN 2 ++#define COMPHY_INVERT_MASK COMPHY_MASK(COMPHY_INVERT_OFFSET, COMPHY_INVERT_LEN) ++#define COMPHY_SPEED_OFFSET (COMPHY_INVERT_OFFSET + COMPHY_INVERT_LEN) ++#define COMPHY_SPEED_LEN 6 ++#define COMPHY_SPEED_MASK COMPHY_MASK(COMPHY_SPEED_OFFSET, COMPHY_SPEED_LEN) ++#define COMPHY_UNIT_ID_OFFSET (COMPHY_SPEED_OFFSET + COMPHY_SPEED_LEN) ++#define COMPHY_UNIT_ID_LEN 4 ++#define COMPHY_UNIT_ID_MASK COMPHY_MASK(COMPHY_UNIT_ID_OFFSET, COMPHY_UNIT_ID_LEN) ++#define COMPHY_MODE_OFFSET (COMPHY_UNIT_ID_OFFSET + COMPHY_UNIT_ID_LEN) ++#define COMPHY_MODE_LEN 5 ++#define COMPHY_MODE_MASK COMPHY_MASK(COMPHY_MODE_OFFSET, COMPHY_MODE_LEN) ++#define COMPHY_CLK_SRC_OFFSET (COMPHY_MODE_OFFSET + COMPHY_MODE_LEN) ++#define COMPHY_CLK_SRC_LEN 1 ++#define COMPHY_CLK_SRC_MASK COMPHY_MASK(COMPHY_CLK_SRC_OFFSET, COMPHY_CLK_SRC_LEN) ++#define COMPHY_PCI_WIDTH_OFFSET (COMPHY_CLK_SRC_OFFSET + COMPHY_CLK_SRC_LEN) ++#define COMPHY_PCI_WIDTH_LEN 3 ++#define COMPHY_PCI_WIDTH_MASK COMPHY_MASK(COMPHY_PCI_WIDTH_OFFSET, COMPHY_PCI_WIDTH_LEN) ++#define COMPHY_DEF(mode, id, speed, invert) \ ++ (((mode) << COMPHY_MODE_OFFSET) | ((id) << COMPHY_UNIT_ID_OFFSET) | \ ++ ((speed) << COMPHY_SPEED_OFFSET) | ((invert) << COMPHY_INVERT_OFFSET)) ++ ++#define COMPHY_MASK(offset, len) (((1 << (len)) - 1) << (offset)) ++ ++/* Macro the extract the mode from lane description */ ++#define COMPHY_GET_MODE(x) (((x) & COMPHY_MODE_MASK) >> COMPHY_MODE_OFFSET) ++/* Macro the extract the unit index from lane description */ ++#define COMPHY_GET_ID(x) (((x) & COMPHY_UNIT_ID_MASK) >> COMPHY_UNIT_ID_OFFSET) ++/* Macro the extract the speed from lane description */ ++#define COMPHY_GET_SPEED(x) (((x) & COMPHY_SPEED_MASK) >> COMPHY_SPEED_OFFSET) ++/* Macro the extract the polarity invert from lane description */ ++#define COMPHY_GET_POLARITY_INVERT(x) (((x) & COMPHY_INVERT_MASK) >> COMPHY_INVERT_OFFSET) ++/* Macro the extract the clock source indication from lane description */ ++#define COMPHY_GET_CLK_SRC(x) (((x) & COMPHY_CLK_SRC_MASK) >> COMPHY_CLK_SRC_OFFSET) ++ ++/* Comphy unit index macro */ ++#define COMPHY_UNIT_ID0 0 ++#define COMPHY_UNIT_ID1 1 ++#define COMPHY_UNIT_ID2 2 ++#define COMPHY_UNIT_ID3 3 ++ ++/* Comphy description macro with default speed and invert */ ++#define COMPHY_UNUSED 0xFFFFFFFF ++#define COMPHY_SATA0 COMPHY_DEF(COMPHY_SATA_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_SATA1 COMPHY_DEF(COMPHY_SATA_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_SGMII0 COMPHY_DEF(COMPHY_SGMII_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* SGMII 1G */ ++#define COMPHY_SGMII1 COMPHY_DEF(COMPHY_SGMII_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* SGMII 1G */ ++#define COMPHY_SGMII2 COMPHY_DEF(COMPHY_SGMII_MODE, COMPHY_UNIT_ID2, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* SGMII 1G */ ++#define COMPHY_HS_SGMII0 COMPHY_DEF(COMPHY_HS_SGMII_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* SGMII 2.5G */ ++#define COMPHY_HS_SGMII1 COMPHY_DEF(COMPHY_HS_SGMII_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* SGMII 2.5G */ ++#define COMPHY_HS_SGMII2 COMPHY_DEF(COMPHY_HS_SGMII_MODE, COMPHY_UNIT_ID2, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* SGMII 2.5G */ ++#define COMPHY_USB3H0 COMPHY_DEF(COMPHY_USB3H_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_USB3H1 COMPHY_DEF(COMPHY_USB3H_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_USB3D0 COMPHY_DEF(COMPHY_USB3D_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_PCIE0 COMPHY_DEF(COMPHY_PCIE_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_PCIE1 COMPHY_DEF(COMPHY_PCIE_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_PCIE2 COMPHY_DEF(COMPHY_PCIE_MODE, COMPHY_UNIT_ID2, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_PCIE3 COMPHY_DEF(COMPHY_PCIE_MODE, COMPHY_UNIT_ID3, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_RXAUI0 COMPHY_DEF(COMPHY_RXAUI_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_RXAUI1 COMPHY_DEF(COMPHY_RXAUI_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_XFI0 COMPHY_DEF(COMPHY_XFI_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_SFI0 COMPHY_DEF(COMPHY_SFI_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_XFI1 COMPHY_DEF(COMPHY_XFI_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_SFI1 COMPHY_DEF(COMPHY_SFI_MODE, COMPHY_UNIT_ID1, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) ++#define COMPHY_USB3 COMPHY_DEF(COMPHY_USB3_MODE, COMPHY_UNIT_ID0, \ ++ COMPHY_SPEED_DEFAULT, COMPHY_POLARITY_NO_INVERT) /* USB3 Host&Device */ ++ ++#define COMPHY_SATA_MODE 0x1 ++#define COMPHY_SGMII_MODE 0x2 /* SGMII 1G */ ++#define COMPHY_HS_SGMII_MODE 0x3 /* SGMII 2.5G */ ++#define COMPHY_USB3H_MODE 0x4 ++#define COMPHY_USB3D_MODE 0x5 ++#define COMPHY_PCIE_MODE 0x6 ++#define COMPHY_RXAUI_MODE 0x7 ++#define COMPHY_XFI_MODE 0x8 ++#define COMPHY_SFI_MODE 0x9 ++#define COMPHY_USB3_MODE 0xa ++ ++/* Polarity invert macro */ ++#define COMPHY_POLARITY_NO_INVERT 0 ++#define COMPHY_POLARITY_TXD_INVERT 1 ++#define COMPHY_POLARITY_RXD_INVERT 2 ++#define COMPHY_POLARITY_ALL_INVERT (COMPHY_POLARITY_TXD_INVERT | COMPHY_POLARITY_RXD_INVERT) ++ ++/* COMPHY speed macro */ ++#define COMPHY_SPEED_1_25G 0 /* SGMII 1G */ ++#define COMPHY_SPEED_2_5G 1 ++#define COMPHY_SPEED_3_125G 2 /* SGMII 2.5G */ ++#define COMPHY_SPEED_5G 3 ++#define COMPHY_SPEED_5_15625G 4 /* XFI 5G */ ++#define COMPHY_SPEED_6G 5 ++#define COMPHY_SPEED_10_3125G 6 /* XFI 10G */ ++#define COMPHY_SPEED_MAX 0x3F ++/* The default speed for IO with fixed known speed */ ++#define COMPHY_SPEED_DEFAULT COMPHY_SPEED_MAX ++ ++/* Commands for comphy driver */ ++#define COMPHY_COMMAND_DIGITAL_PWR_OFF 0x00000001 ++#define COMPHY_COMMAND_DIGITAL_PWR_ON 0x00000002 ++#define COMPHY_COMMAND_PCIE_WIDTH_1 0x00000003 ++#define COMPHY_COMMAND_PCIE_WIDTH_2 0x00000004 ++#define COMPHY_COMMAND_PCIE_WIDTH_4 0x00000005 ++#define COMPHY_COMMAND_PCIE_WIDTH_UNSUPPORT 0x00000006 ++#define COMPHY_COMMAND_SFI_RX_TRAINING 0x00000007 ++ ++#endif /* _DT_BINDINGS_PHY_COMPHY_MVEBU */ ++ +diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h +index 2b953eb..5204030 100644 +--- a/include/linux/mmc/core.h ++++ b/include/linux/mmc/core.h +@@ -163,6 +163,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, + extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); + extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); + extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); ++extern int mmc_abort_tuning(struct mmc_host *host, u32 opcode); + extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); + + #define MMC_ERASE_ARG 0x00000000 +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index 0b24394..41175b3 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -93,8 +93,7 @@ struct mmc_host_ops { + */ + void (*post_req)(struct mmc_host *host, struct mmc_request *req, + int err); +- void (*pre_req)(struct mmc_host *host, struct mmc_request *req, +- bool is_first_req); ++ void (*pre_req)(struct mmc_host *host, struct mmc_request *req); + void (*request)(struct mmc_host *host, struct mmc_request *req); + + /* +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index 13f8052..a46bcad 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #include + +@@ -205,6 +206,15 @@ struct mtd_pairing_scheme { + + struct module; /* only needed for owner field in mtd_info */ + ++/** ++ * struct mtd_debug_info - debugging information for an MTD device. ++ * ++ * @dfs_dir: direntry object of the MTD device debugfs directory ++ */ ++struct mtd_debug_info { ++ struct dentry *dfs_dir; ++}; ++ + struct mtd_info { + u_char type; + uint32_t flags; +@@ -322,6 +332,7 @@ struct mtd_info { + int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs); + int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); + int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); ++ int (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len); + int (*_suspend) (struct mtd_info *mtd); + void (*_resume) (struct mtd_info *mtd); + void (*_reboot) (struct mtd_info *mtd); +@@ -349,6 +360,7 @@ struct mtd_info { + struct module *owner; + struct device dev; + int usecount; ++ struct mtd_debug_info dbg; + }; + + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, +@@ -385,11 +397,13 @@ static inline void mtd_set_of_node(struct mtd_info *mtd, + struct device_node *np) + { + mtd->dev.of_node = np; ++ if (!mtd->name) ++ of_property_read_string(np, "label", &mtd->name); + } + + static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd) + { +- return mtd->dev.of_node; ++ return dev_of_node(&mtd->dev); + } + + static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) +@@ -397,6 +411,18 @@ static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) + return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; + } + ++static inline int mtd_max_bad_blocks(struct mtd_info *mtd, ++ loff_t ofs, size_t len) ++{ ++ if (!mtd->_max_bad_blocks) ++ return -ENOTSUPP; ++ ++ if (mtd->size < (len + ofs) || ofs < 0) ++ return -EINVAL; ++ ++ return mtd->_max_bad_blocks(mtd, ofs, len); ++} ++ + int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, + struct mtd_pairing_info *info); + int mtd_pairing_info_to_wunit(struct mtd_info *mtd, +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index d8905a2..2d4c5da 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -44,12 +44,6 @@ int nand_scan_ident(struct mtd_info *mtd, int max_chips, + /* Internal helper for board drivers which need to override command function */ + void nand_wait_ready(struct mtd_info *mtd); + +-/* locks all blocks present in the device */ +-int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +- +-/* unlocks specified locked blocks */ +-int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +- + /* The maximum number of NAND chips in an array */ + #define NAND_MAX_CHIPS 8 + +@@ -89,10 +83,6 @@ int nand_scan_ident(struct mtd_info *mtd, int max_chips, + #define NAND_CMD_SET_FEATURES 0xef + #define NAND_CMD_RESET 0xff + +-#define NAND_CMD_LOCK 0x2a +-#define NAND_CMD_UNLOCK1 0x23 +-#define NAND_CMD_UNLOCK2 0x24 +- + /* Extended commands for large page devices */ + #define NAND_CMD_READSTART 0x30 + #define NAND_CMD_RNDOUTSTART 0xE0 +@@ -469,6 +459,44 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc) + } + + /** ++ * struct nand_ecc_step_info - ECC step information of ECC engine ++ * @stepsize: data bytes per ECC step ++ * @strengths: array of supported strengths ++ * @nstrengths: number of supported strengths ++ */ ++struct nand_ecc_step_info { ++ int stepsize; ++ const int *strengths; ++ int nstrengths; ++}; ++ ++/** ++ * struct nand_ecc_caps - capability of ECC engine ++ * @stepinfos: array of ECC step information ++ * @nstepinfos: number of ECC step information ++ * @calc_ecc_bytes: driver's hook to calculate ECC bytes per step ++ */ ++struct nand_ecc_caps { ++ const struct nand_ecc_step_info *stepinfos; ++ int nstepinfos; ++ int (*calc_ecc_bytes)(int step_size, int strength); ++}; ++ ++/* a shorthand to generate struct nand_ecc_caps with only one ECC stepsize */ ++#define NAND_ECC_CAPS_SINGLE(__name, __calc, __step, ...) \ ++static const int __name##_strengths[] = { __VA_ARGS__ }; \ ++static const struct nand_ecc_step_info __name##_stepinfo = { \ ++ .stepsize = __step, \ ++ .strengths = __name##_strengths, \ ++ .nstrengths = ARRAY_SIZE(__name##_strengths), \ ++}; \ ++static const struct nand_ecc_caps __name = { \ ++ .stepinfos = &__name##_stepinfo, \ ++ .nstepinfos = 1, \ ++ .calc_ecc_bytes = __calc, \ ++} ++ ++/** + * struct nand_ecc_ctrl - Control structure for ECC + * @mode: ECC mode + * @algo: ECC algorithm +@@ -584,6 +612,10 @@ struct nand_buffers { + * + * All these timings are expressed in picoseconds. + * ++ * @tBERS_max: Block erase time ++ * @tCCS_min: Change column setup time ++ * @tPROG_max: Page program time ++ * @tR_max: Page read time + * @tALH_min: ALE hold time + * @tADL_min: ALE to data loading time + * @tALS_min: ALE setup time +@@ -621,6 +653,10 @@ struct nand_buffers { + * @tWW_min: WP# transition to WE# low + */ + struct nand_sdr_timings { ++ u64 tBERS_max; ++ u32 tCCS_min; ++ u64 tPROG_max; ++ u64 tR_max; + u32 tALH_min; + u32 tADL_min; + u32 tALS_min; +@@ -1169,6 +1205,15 @@ int nand_check_erased_ecc_chunk(void *data, int datalen, + void *extraoob, int extraooblen, + int threshold); + ++int nand_check_ecc_caps(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail); ++ ++int nand_match_ecc_req(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail); ++ ++int nand_maximize_ecc(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail); ++ + /* Default write_oob implementation */ + int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); + +diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h +new file mode 100644 +index 00000000..992dd3e +--- /dev/null ++++ b/include/linux/mtd/rawnand.h +@@ -0,0 +1,1691 @@ ++/* ++ * Copyright © 2000-2010 David Woodhouse ++ * Steven J. Hill ++ * Thomas Gleixner ++ * ++ * 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. ++ * ++ * Info: ++ * Contains standard defines and IDs for NAND flash devices ++ * ++ * Changelog: ++ * See git changelog. ++ */ ++#ifndef __LINUX_MTD_RAWNAND_H ++#define __LINUX_MTD_RAWNAND_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct mtd_info; ++struct nand_flash_dev; ++struct device_node; ++ ++/* Scan and identify a NAND device */ ++int nand_scan(struct mtd_info *mtd, int max_chips); ++/* ++ * Separate phases of nand_scan(), allowing board driver to intervene ++ * and override command or ECC setup according to flash type. ++ */ ++int nand_scan_ident(struct mtd_info *mtd, int max_chips, ++ struct nand_flash_dev *table); ++int nand_scan_tail(struct mtd_info *mtd); ++ ++/* Unregister the MTD device and free resources held by the NAND device */ ++void nand_release(struct mtd_info *mtd); ++ ++/* Internal helper for board drivers which need to override command function */ ++void nand_wait_ready(struct mtd_info *mtd); ++ ++/* The maximum number of NAND chips in an array */ ++#define NAND_MAX_CHIPS 8 ++ ++/* ++ * Constants for hardware specific CLE/ALE/NCE function ++ * ++ * These are bits which can be or'ed to set/clear multiple ++ * bits in one go. ++ */ ++/* Select the chip by setting nCE to low */ ++#define NAND_NCE 0x01 ++/* Select the command latch by setting CLE to high */ ++#define NAND_CLE 0x02 ++/* Select the address latch by setting ALE to high */ ++#define NAND_ALE 0x04 ++ ++#define NAND_CTRL_CLE (NAND_NCE | NAND_CLE) ++#define NAND_CTRL_ALE (NAND_NCE | NAND_ALE) ++#define NAND_CTRL_CHANGE 0x80 ++ ++/* ++ * Standard NAND flash commands ++ */ ++#define NAND_CMD_READ0 0 ++#define NAND_CMD_READ1 1 ++#define NAND_CMD_RNDOUT 5 ++#define NAND_CMD_PAGEPROG 0x10 ++#define NAND_CMD_READOOB 0x50 ++#define NAND_CMD_ERASE1 0x60 ++#define NAND_CMD_STATUS 0x70 ++#define NAND_CMD_SEQIN 0x80 ++#define NAND_CMD_RNDIN 0x85 ++#define NAND_CMD_READID 0x90 ++#define NAND_CMD_ERASE2 0xd0 ++#define NAND_CMD_PARAM 0xec ++#define NAND_CMD_GET_FEATURES 0xee ++#define NAND_CMD_SET_FEATURES 0xef ++#define NAND_CMD_RESET 0xff ++ ++/* Extended commands for large page devices */ ++#define NAND_CMD_READSTART 0x30 ++#define NAND_CMD_RNDOUTSTART 0xE0 ++#define NAND_CMD_CACHEDPROG 0x15 ++ ++#define NAND_CMD_NONE -1 ++ ++/* Status bits */ ++#define NAND_STATUS_FAIL 0x01 ++#define NAND_STATUS_FAIL_N1 0x02 ++#define NAND_STATUS_TRUE_READY 0x20 ++#define NAND_STATUS_READY 0x40 ++#define NAND_STATUS_WP 0x80 ++ ++#define NAND_DATA_IFACE_CHECK_ONLY -1 ++ ++/* ++ * Constants for ECC_MODES ++ */ ++typedef enum { ++ NAND_ECC_NONE, ++ NAND_ECC_SOFT, ++ NAND_ECC_HW, ++ NAND_ECC_HW_SYNDROME, ++ NAND_ECC_HW_OOB_FIRST, ++ NAND_ECC_ON_DIE, ++} nand_ecc_modes_t; ++ ++enum nand_ecc_algo { ++ NAND_ECC_UNKNOWN, ++ NAND_ECC_HAMMING, ++ NAND_ECC_BCH, ++}; ++ ++/* ++ * Constants for Hardware ECC ++ */ ++/* Reset Hardware ECC for read */ ++#define NAND_ECC_READ 0 ++/* Reset Hardware ECC for write */ ++#define NAND_ECC_WRITE 1 ++/* Enable Hardware ECC before syndrome is read back from flash */ ++#define NAND_ECC_READSYN 2 ++ ++/* ++ * Enable generic NAND 'page erased' check. This check is only done when ++ * ecc.correct() returns -EBADMSG. ++ * Set this flag if your implementation does not fix bitflips in erased ++ * pages and you want to rely on the default implementation. ++ */ ++#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0) ++#define NAND_ECC_MAXIMIZE BIT(1) ++ ++/* Bit mask for flags passed to do_nand_read_ecc */ ++#define NAND_GET_DEVICE 0x80 ++ ++ ++/* ++ * Option constants for bizarre disfunctionality and real ++ * features. ++ */ ++/* Buswidth is 16 bit */ ++#define NAND_BUSWIDTH_16 0x00000002 ++/* Chip has cache program function */ ++#define NAND_CACHEPRG 0x00000008 ++/* ++ * Chip requires ready check on read (for auto-incremented sequential read). ++ * True only for small page devices; large page devices do not support ++ * autoincrement. ++ */ ++#define NAND_NEED_READRDY 0x00000100 ++ ++/* Chip does not allow subpage writes */ ++#define NAND_NO_SUBPAGE_WRITE 0x00000200 ++ ++/* Device is one of 'new' xD cards that expose fake nand command set */ ++#define NAND_BROKEN_XD 0x00000400 ++ ++/* Device behaves just like nand, but is readonly */ ++#define NAND_ROM 0x00000800 ++ ++/* Device supports subpage reads */ ++#define NAND_SUBPAGE_READ 0x00001000 ++ ++/* ++ * Some MLC NANDs need data scrambling to limit bitflips caused by repeated ++ * patterns. ++ */ ++#define NAND_NEED_SCRAMBLING 0x00002000 ++ ++/* Device needs 3rd row address cycle */ ++#define NAND_ROW_ADDR_3 0x00004000 ++ ++/* Options valid for Samsung large page devices */ ++#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG ++ ++/* Macros to identify the above */ ++#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) ++#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ)) ++#define NAND_HAS_SUBPAGE_WRITE(chip) !((chip)->options & NAND_NO_SUBPAGE_WRITE) ++ ++/* Non chip related options */ ++/* This option skips the bbt scan during initialization. */ ++#define NAND_SKIP_BBTSCAN 0x00010000 ++/* Chip may not exist, so silence any errors in scan */ ++#define NAND_SCAN_SILENT_NODEV 0x00040000 ++/* ++ * Autodetect nand buswidth with readid/onfi. ++ * This suppose the driver will configure the hardware in 8 bits mode ++ * when calling nand_scan_ident, and update its configuration ++ * before calling nand_scan_tail. ++ */ ++#define NAND_BUSWIDTH_AUTO 0x00080000 ++/* ++ * This option could be defined by controller drivers to protect against ++ * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers ++ */ ++#define NAND_USE_BOUNCE_BUFFER 0x00100000 ++ ++/* ++ * In case your controller is implementing ->cmd_ctrl() and is relying on the ++ * default ->cmdfunc() implementation, you may want to let the core handle the ++ * tCCS delay which is required when a column change (RNDIN or RNDOUT) is ++ * requested. ++ * If your controller already takes care of this delay, you don't need to set ++ * this flag. ++ */ ++#define NAND_WAIT_TCCS 0x00200000 ++ ++/* Options set by nand scan */ ++/* Nand scan has allocated controller struct */ ++#define NAND_CONTROLLER_ALLOC 0x80000000 ++ ++/* Cell info constants */ ++#define NAND_CI_CHIPNR_MSK 0x03 ++#define NAND_CI_CELLTYPE_MSK 0x0C ++#define NAND_CI_CELLTYPE_SHIFT 2 ++ ++/* Keep gcc happy */ ++struct nand_chip; ++ ++/* ONFI features */ ++#define ONFI_FEATURE_16_BIT_BUS (1 << 0) ++#define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7) ++ ++/* ONFI timing mode, used in both asynchronous and synchronous mode */ ++#define ONFI_TIMING_MODE_0 (1 << 0) ++#define ONFI_TIMING_MODE_1 (1 << 1) ++#define ONFI_TIMING_MODE_2 (1 << 2) ++#define ONFI_TIMING_MODE_3 (1 << 3) ++#define ONFI_TIMING_MODE_4 (1 << 4) ++#define ONFI_TIMING_MODE_5 (1 << 5) ++#define ONFI_TIMING_MODE_UNKNOWN (1 << 6) ++ ++/* ONFI feature address */ ++#define ONFI_FEATURE_ADDR_TIMING_MODE 0x1 ++ ++/* Vendor-specific feature address (Micron) */ ++#define ONFI_FEATURE_ADDR_READ_RETRY 0x89 ++#define ONFI_FEATURE_ON_DIE_ECC 0x90 ++#define ONFI_FEATURE_ON_DIE_ECC_EN BIT(3) ++ ++/* ONFI subfeature parameters length */ ++#define ONFI_SUBFEATURE_PARAM_LEN 4 ++ ++/* ONFI optional commands SET/GET FEATURES supported? */ ++#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2) ++ ++struct nand_onfi_params { ++ /* rev info and features block */ ++ /* 'O' 'N' 'F' 'I' */ ++ u8 sig[4]; ++ __le16 revision; ++ __le16 features; ++ __le16 opt_cmd; ++ u8 reserved0[2]; ++ __le16 ext_param_page_length; /* since ONFI 2.1 */ ++ u8 num_of_param_pages; /* since ONFI 2.1 */ ++ u8 reserved1[17]; ++ ++ /* manufacturer information block */ ++ char manufacturer[12]; ++ char model[20]; ++ u8 jedec_id; ++ __le16 date_code; ++ u8 reserved2[13]; ++ ++ /* memory organization block */ ++ __le32 byte_per_page; ++ __le16 spare_bytes_per_page; ++ __le32 data_bytes_per_ppage; ++ __le16 spare_bytes_per_ppage; ++ __le32 pages_per_block; ++ __le32 blocks_per_lun; ++ u8 lun_count; ++ u8 addr_cycles; ++ u8 bits_per_cell; ++ __le16 bb_per_lun; ++ __le16 block_endurance; ++ u8 guaranteed_good_blocks; ++ __le16 guaranteed_block_endurance; ++ u8 programs_per_page; ++ u8 ppage_attr; ++ u8 ecc_bits; ++ u8 interleaved_bits; ++ u8 interleaved_ops; ++ u8 reserved3[13]; ++ ++ /* electrical parameter block */ ++ u8 io_pin_capacitance_max; ++ __le16 async_timing_mode; ++ __le16 program_cache_timing_mode; ++ __le16 t_prog; ++ __le16 t_bers; ++ __le16 t_r; ++ __le16 t_ccs; ++ __le16 src_sync_timing_mode; ++ u8 src_ssync_features; ++ __le16 clk_pin_capacitance_typ; ++ __le16 io_pin_capacitance_typ; ++ __le16 input_pin_capacitance_typ; ++ u8 input_pin_capacitance_max; ++ u8 driver_strength_support; ++ __le16 t_int_r; ++ __le16 t_adl; ++ u8 reserved4[8]; ++ ++ /* vendor */ ++ __le16 vendor_revision; ++ u8 vendor[88]; ++ ++ __le16 crc; ++} __packed; ++ ++#define ONFI_CRC_BASE 0x4F4E ++ ++/* Extended ECC information Block Definition (since ONFI 2.1) */ ++struct onfi_ext_ecc_info { ++ u8 ecc_bits; ++ u8 codeword_size; ++ __le16 bb_per_lun; ++ __le16 block_endurance; ++ u8 reserved[2]; ++} __packed; ++ ++#define ONFI_SECTION_TYPE_0 0 /* Unused section. */ ++#define ONFI_SECTION_TYPE_1 1 /* for additional sections. */ ++#define ONFI_SECTION_TYPE_2 2 /* for ECC information. */ ++struct onfi_ext_section { ++ u8 type; ++ u8 length; ++} __packed; ++ ++#define ONFI_EXT_SECTION_MAX 8 ++ ++/* Extended Parameter Page Definition (since ONFI 2.1) */ ++struct onfi_ext_param_page { ++ __le16 crc; ++ u8 sig[4]; /* 'E' 'P' 'P' 'S' */ ++ u8 reserved0[10]; ++ struct onfi_ext_section sections[ONFI_EXT_SECTION_MAX]; ++ ++ /* ++ * The actual size of the Extended Parameter Page is in ++ * @ext_param_page_length of nand_onfi_params{}. ++ * The following are the variable length sections. ++ * So we do not add any fields below. Please see the ONFI spec. ++ */ ++} __packed; ++ ++struct jedec_ecc_info { ++ u8 ecc_bits; ++ u8 codeword_size; ++ __le16 bb_per_lun; ++ __le16 block_endurance; ++ u8 reserved[2]; ++} __packed; ++ ++/* JEDEC features */ ++#define JEDEC_FEATURE_16_BIT_BUS (1 << 0) ++ ++struct nand_jedec_params { ++ /* rev info and features block */ ++ /* 'J' 'E' 'S' 'D' */ ++ u8 sig[4]; ++ __le16 revision; ++ __le16 features; ++ u8 opt_cmd[3]; ++ __le16 sec_cmd; ++ u8 num_of_param_pages; ++ u8 reserved0[18]; ++ ++ /* manufacturer information block */ ++ char manufacturer[12]; ++ char model[20]; ++ u8 jedec_id[6]; ++ u8 reserved1[10]; ++ ++ /* memory organization block */ ++ __le32 byte_per_page; ++ __le16 spare_bytes_per_page; ++ u8 reserved2[6]; ++ __le32 pages_per_block; ++ __le32 blocks_per_lun; ++ u8 lun_count; ++ u8 addr_cycles; ++ u8 bits_per_cell; ++ u8 programs_per_page; ++ u8 multi_plane_addr; ++ u8 multi_plane_op_attr; ++ u8 reserved3[38]; ++ ++ /* electrical parameter block */ ++ __le16 async_sdr_speed_grade; ++ __le16 toggle_ddr_speed_grade; ++ __le16 sync_ddr_speed_grade; ++ u8 async_sdr_features; ++ u8 toggle_ddr_features; ++ u8 sync_ddr_features; ++ __le16 t_prog; ++ __le16 t_bers; ++ __le16 t_r; ++ __le16 t_r_multi_plane; ++ __le16 t_ccs; ++ __le16 io_pin_capacitance_typ; ++ __le16 input_pin_capacitance_typ; ++ __le16 clk_pin_capacitance_typ; ++ u8 driver_strength_support; ++ __le16 t_adl; ++ u8 reserved4[36]; ++ ++ /* ECC and endurance block */ ++ u8 guaranteed_good_blocks; ++ __le16 guaranteed_block_endurance; ++ struct jedec_ecc_info ecc_info[4]; ++ u8 reserved5[29]; ++ ++ /* reserved */ ++ u8 reserved6[148]; ++ ++ /* vendor */ ++ __le16 vendor_rev_num; ++ u8 reserved7[88]; ++ ++ /* CRC for Parameter Page */ ++ __le16 crc; ++} __packed; ++ ++/* The maximum expected count of bytes in the NAND ID sequence */ ++#define NAND_MAX_ID_LEN 8 ++ ++/** ++ * struct nand_id - NAND id structure ++ * @data: buffer containing the id bytes. ++ * @len: ID length. ++ */ ++struct nand_id { ++ u8 data[NAND_MAX_ID_LEN]; ++ int len; ++}; ++ ++/** ++ * struct nand_controller - Structure used to describe a NAND controller ++ * ++ * @lock: protection lock ++ * @active: the mtd device which holds the controller currently ++ * @wq: wait queue to sleep on if a NAND operation is in ++ * progress used instead of the per chip wait queue ++ * when a hw controller is available. ++ */ ++struct nand_controller { ++ spinlock_t lock; ++ struct nand_chip *active; ++ wait_queue_head_t wq; ++}; ++ ++static inline void nand_controller_init(struct nand_controller *nfc) ++{ ++ nfc->active = NULL; ++ spin_lock_init(&nfc->lock); ++ init_waitqueue_head(&nfc->wq); ++} ++ ++/** ++ * struct nand_ecc_step_info - ECC step information of ECC engine ++ * @stepsize: data bytes per ECC step ++ * @strengths: array of supported strengths ++ * @nstrengths: number of supported strengths ++ */ ++struct nand_ecc_step_info { ++ int stepsize; ++ const int *strengths; ++ int nstrengths; ++}; ++ ++/** ++ * struct nand_ecc_caps - capability of ECC engine ++ * @stepinfos: array of ECC step information ++ * @nstepinfos: number of ECC step information ++ * @calc_ecc_bytes: driver's hook to calculate ECC bytes per step ++ */ ++struct nand_ecc_caps { ++ const struct nand_ecc_step_info *stepinfos; ++ int nstepinfos; ++ int (*calc_ecc_bytes)(int step_size, int strength); ++}; ++ ++/* a shorthand to generate struct nand_ecc_caps with only one ECC stepsize */ ++#define NAND_ECC_CAPS_SINGLE(__name, __calc, __step, ...) \ ++static const int __name##_strengths[] = { __VA_ARGS__ }; \ ++static const struct nand_ecc_step_info __name##_stepinfo = { \ ++ .stepsize = __step, \ ++ .strengths = __name##_strengths, \ ++ .nstrengths = ARRAY_SIZE(__name##_strengths), \ ++}; \ ++static const struct nand_ecc_caps __name = { \ ++ .stepinfos = &__name##_stepinfo, \ ++ .nstepinfos = 1, \ ++ .calc_ecc_bytes = __calc, \ ++} ++ ++/** ++ * struct nand_ecc_ctrl - Control structure for ECC ++ * @mode: ECC mode ++ * @algo: ECC algorithm ++ * @steps: number of ECC steps per page ++ * @size: data bytes per ECC step ++ * @bytes: ECC bytes per step ++ * @strength: max number of correctible bits per ECC step ++ * @total: total number of ECC bytes per page ++ * @prepad: padding information for syndrome based ECC generators ++ * @postpad: padding information for syndrome based ECC generators ++ * @options: ECC specific options (see NAND_ECC_XXX flags defined above) ++ * @priv: pointer to private ECC control data ++ * @calc_buf: buffer for calculated ECC, size is oobsize. ++ * @code_buf: buffer for ECC read from flash, size is oobsize. ++ * @hwctl: function to control hardware ECC generator. Must only ++ * be provided if an hardware ECC is available ++ * @calculate: function for ECC calculation or readback from ECC hardware ++ * @correct: function for ECC correction, matching to ECC generator (sw/hw). ++ * Should return a positive number representing the number of ++ * corrected bitflips, -EBADMSG if the number of bitflips exceed ++ * ECC strength, or any other error code if the error is not ++ * directly related to correction. ++ * If -EBADMSG is returned the input buffers should be left ++ * untouched. ++ * @read_page_raw: function to read a raw page without ECC. This function ++ * should hide the specific layout used by the ECC ++ * controller and always return contiguous in-band and ++ * out-of-band data even if they're not stored ++ * contiguously on the NAND chip (e.g. ++ * NAND_ECC_HW_SYNDROME interleaves in-band and ++ * out-of-band data). ++ * @write_page_raw: function to write a raw page without ECC. This function ++ * should hide the specific layout used by the ECC ++ * controller and consider the passed data as contiguous ++ * in-band and out-of-band data. ECC controller is ++ * responsible for doing the appropriate transformations ++ * to adapt to its specific layout (e.g. ++ * NAND_ECC_HW_SYNDROME interleaves in-band and ++ * out-of-band data). ++ * @read_page: function to read a page according to the ECC generator ++ * requirements; returns maximum number of bitflips corrected in ++ * any single ECC step, -EIO hw error ++ * @read_subpage: function to read parts of the page covered by ECC; ++ * returns same as read_page() ++ * @write_subpage: function to write parts of the page covered by ECC. ++ * @write_page: function to write a page according to the ECC generator ++ * requirements. ++ * @write_oob_raw: function to write chip OOB data without ECC ++ * @read_oob_raw: function to read chip OOB data without ECC ++ * @read_oob: function to read chip OOB data ++ * @write_oob: function to write chip OOB data ++ */ ++struct nand_ecc_ctrl { ++ nand_ecc_modes_t mode; ++ enum nand_ecc_algo algo; ++ int steps; ++ int size; ++ int bytes; ++ int total; ++ int strength; ++ int prepad; ++ int postpad; ++ unsigned int options; ++ void *priv; ++ u8 *calc_buf; ++ u8 *code_buf; ++ void (*hwctl)(struct mtd_info *mtd, int mode); ++ int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, ++ uint8_t *ecc_code); ++ int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, ++ uint8_t *calc_ecc); ++ int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page); ++ int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page); ++ int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page); ++ int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, ++ uint32_t offs, uint32_t len, uint8_t *buf, int page); ++ int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip, ++ uint32_t offset, uint32_t data_len, ++ const uint8_t *data_buf, int oob_required, int page); ++ int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page); ++ int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, ++ int page); ++ int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, ++ int page); ++ int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page); ++ int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, ++ int page); ++}; ++ ++/** ++ * struct nand_sdr_timings - SDR NAND chip timings ++ * ++ * This struct defines the timing requirements of a SDR NAND chip. ++ * These information can be found in every NAND datasheets and the timings ++ * meaning are described in the ONFI specifications: ++ * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing ++ * Parameters) ++ * ++ * All these timings are expressed in picoseconds. ++ * ++ * @tBERS_max: Block erase time ++ * @tCCS_min: Change column setup time ++ * @tPROG_max: Page program time ++ * @tR_max: Page read time ++ * @tALH_min: ALE hold time ++ * @tADL_min: ALE to data loading time ++ * @tALS_min: ALE setup time ++ * @tAR_min: ALE to RE# delay ++ * @tCEA_max: CE# access time ++ * @tCEH_min: CE# high hold time ++ * @tCH_min: CE# hold time ++ * @tCHZ_max: CE# high to output hi-Z ++ * @tCLH_min: CLE hold time ++ * @tCLR_min: CLE to RE# delay ++ * @tCLS_min: CLE setup time ++ * @tCOH_min: CE# high to output hold ++ * @tCS_min: CE# setup time ++ * @tDH_min: Data hold time ++ * @tDS_min: Data setup time ++ * @tFEAT_max: Busy time for Set Features and Get Features ++ * @tIR_min: Output hi-Z to RE# low ++ * @tITC_max: Interface and Timing Mode Change time ++ * @tRC_min: RE# cycle time ++ * @tREA_max: RE# access time ++ * @tREH_min: RE# high hold time ++ * @tRHOH_min: RE# high to output hold ++ * @tRHW_min: RE# high to WE# low ++ * @tRHZ_max: RE# high to output hi-Z ++ * @tRLOH_min: RE# low to output hold ++ * @tRP_min: RE# pulse width ++ * @tRR_min: Ready to RE# low (data only) ++ * @tRST_max: Device reset time, measured from the falling edge of R/B# to the ++ * rising edge of R/B#. ++ * @tWB_max: WE# high to SR[6] low ++ * @tWC_min: WE# cycle time ++ * @tWH_min: WE# high hold time ++ * @tWHR_min: WE# high to RE# low ++ * @tWP_min: WE# pulse width ++ * @tWW_min: WP# transition to WE# low ++ */ ++struct nand_sdr_timings { ++ u64 tBERS_max; ++ u32 tCCS_min; ++ u64 tPROG_max; ++ u64 tR_max; ++ u32 tALH_min; ++ u32 tADL_min; ++ u32 tALS_min; ++ u32 tAR_min; ++ u32 tCEA_max; ++ u32 tCEH_min; ++ u32 tCH_min; ++ u32 tCHZ_max; ++ u32 tCLH_min; ++ u32 tCLR_min; ++ u32 tCLS_min; ++ u32 tCOH_min; ++ u32 tCS_min; ++ u32 tDH_min; ++ u32 tDS_min; ++ u32 tFEAT_max; ++ u32 tIR_min; ++ u32 tITC_max; ++ u32 tRC_min; ++ u32 tREA_max; ++ u32 tREH_min; ++ u32 tRHOH_min; ++ u32 tRHW_min; ++ u32 tRHZ_max; ++ u32 tRLOH_min; ++ u32 tRP_min; ++ u32 tRR_min; ++ u64 tRST_max; ++ u32 tWB_max; ++ u32 tWC_min; ++ u32 tWH_min; ++ u32 tWHR_min; ++ u32 tWP_min; ++ u32 tWW_min; ++}; ++ ++/** ++ * enum nand_data_interface_type - NAND interface timing type ++ * @NAND_SDR_IFACE: Single Data Rate interface ++ */ ++enum nand_data_interface_type { ++ NAND_SDR_IFACE, ++}; ++ ++/** ++ * struct nand_data_interface - NAND interface timing ++ * @type: type of the timing ++ * @timings: The timing, type according to @type ++ */ ++struct nand_data_interface { ++ enum nand_data_interface_type type; ++ union { ++ struct nand_sdr_timings sdr; ++ } timings; ++}; ++ ++/** ++ * nand_get_sdr_timings - get SDR timing from data interface ++ * @conf: The data interface ++ */ ++static inline const struct nand_sdr_timings * ++nand_get_sdr_timings(const struct nand_data_interface *conf) ++{ ++ if (conf->type != NAND_SDR_IFACE) ++ return ERR_PTR(-EINVAL); ++ ++ return &conf->timings.sdr; ++} ++ ++/** ++ * struct nand_manufacturer_ops - NAND Manufacturer operations ++ * @detect: detect the NAND memory organization and capabilities ++ * @init: initialize all vendor specific fields (like the ->read_retry() ++ * implementation) if any. ++ * @cleanup: the ->init() function may have allocated resources, ->cleanup() ++ * is here to let vendor specific code release those resources. ++ */ ++struct nand_manufacturer_ops { ++ void (*detect)(struct nand_chip *chip); ++ int (*init)(struct nand_chip *chip); ++ void (*cleanup)(struct nand_chip *chip); ++}; ++ ++/** ++ * struct nand_op_cmd_instr - Definition of a command instruction ++ * @opcode: the command to issue in one cycle ++ */ ++struct nand_op_cmd_instr { ++ u8 opcode; ++}; ++ ++/** ++ * struct nand_op_addr_instr - Definition of an address instruction ++ * @naddrs: length of the @addrs array ++ * @addrs: array containing the address cycles to issue ++ */ ++struct nand_op_addr_instr { ++ unsigned int naddrs; ++ const u8 *addrs; ++}; ++ ++/** ++ * struct nand_op_data_instr - Definition of a data instruction ++ * @len: number of data bytes to move ++ * @in: buffer to fill when reading from the NAND chip ++ * @out: buffer to read from when writing to the NAND chip ++ * @force_8bit: force 8-bit access ++ * ++ * Please note that "in" and "out" are inverted from the ONFI specification ++ * and are from the controller perspective, so a "in" is a read from the NAND ++ * chip while a "out" is a write to the NAND chip. ++ */ ++struct nand_op_data_instr { ++ unsigned int len; ++ union { ++ void *in; ++ const void *out; ++ } buf; ++ bool force_8bit; ++}; ++ ++/** ++ * struct nand_op_waitrdy_instr - Definition of a wait ready instruction ++ * @timeout_ms: maximum delay while waiting for the ready/busy pin in ms ++ */ ++struct nand_op_waitrdy_instr { ++ unsigned int timeout_ms; ++}; ++ ++/** ++ * enum nand_op_instr_type - Definition of all instruction types ++ * @NAND_OP_CMD_INSTR: command instruction ++ * @NAND_OP_ADDR_INSTR: address instruction ++ * @NAND_OP_DATA_IN_INSTR: data in instruction ++ * @NAND_OP_DATA_OUT_INSTR: data out instruction ++ * @NAND_OP_WAITRDY_INSTR: wait ready instruction ++ */ ++enum nand_op_instr_type { ++ NAND_OP_CMD_INSTR, ++ NAND_OP_ADDR_INSTR, ++ NAND_OP_DATA_IN_INSTR, ++ NAND_OP_DATA_OUT_INSTR, ++ NAND_OP_WAITRDY_INSTR, ++}; ++ ++/** ++ * struct nand_op_instr - Instruction object ++ * @type: the instruction type ++ * @cmd/@addr/@data/@waitrdy: extra data associated to the instruction. ++ * You'll have to use the appropriate element ++ * depending on @type ++ * @delay_ns: delay the controller should apply after the instruction has been ++ * issued on the bus. Most modern controllers have internal timings ++ * control logic, and in this case, the controller driver can ignore ++ * this field. ++ */ ++struct nand_op_instr { ++ enum nand_op_instr_type type; ++ union { ++ struct nand_op_cmd_instr cmd; ++ struct nand_op_addr_instr addr; ++ struct nand_op_data_instr data; ++ struct nand_op_waitrdy_instr waitrdy; ++ } ctx; ++ unsigned int delay_ns; ++}; ++ ++/* ++ * Special handling must be done for the WAITRDY timeout parameter as it usually ++ * is either tPROG (after a prog), tR (before a read), tRST (during a reset) or ++ * tBERS (during an erase) which all of them are u64 values that cannot be ++ * divided by usual kernel macros and must be handled with the special ++ * DIV_ROUND_UP_ULL() macro. ++ */ ++#define __DIVIDE(dividend, divisor) ({ \ ++ sizeof(dividend) == sizeof(u32) ? \ ++ DIV_ROUND_UP(dividend, divisor) : \ ++ DIV_ROUND_UP_ULL(dividend, divisor); \ ++ }) ++#define PSEC_TO_NSEC(x) __DIVIDE(x, 1000) ++#define PSEC_TO_MSEC(x) __DIVIDE(x, 1000000000) ++ ++#define NAND_OP_CMD(id, ns) \ ++ { \ ++ .type = NAND_OP_CMD_INSTR, \ ++ .ctx.cmd.opcode = id, \ ++ .delay_ns = ns, \ ++ } ++ ++#define NAND_OP_ADDR(ncycles, cycles, ns) \ ++ { \ ++ .type = NAND_OP_ADDR_INSTR, \ ++ .ctx.addr = { \ ++ .naddrs = ncycles, \ ++ .addrs = cycles, \ ++ }, \ ++ .delay_ns = ns, \ ++ } ++ ++#define NAND_OP_DATA_IN(l, b, ns) \ ++ { \ ++ .type = NAND_OP_DATA_IN_INSTR, \ ++ .ctx.data = { \ ++ .len = l, \ ++ .buf.in = b, \ ++ .force_8bit = false, \ ++ }, \ ++ .delay_ns = ns, \ ++ } ++ ++#define NAND_OP_DATA_OUT(l, b, ns) \ ++ { \ ++ .type = NAND_OP_DATA_OUT_INSTR, \ ++ .ctx.data = { \ ++ .len = l, \ ++ .buf.out = b, \ ++ .force_8bit = false, \ ++ }, \ ++ .delay_ns = ns, \ ++ } ++ ++#define NAND_OP_8BIT_DATA_IN(l, b, ns) \ ++ { \ ++ .type = NAND_OP_DATA_IN_INSTR, \ ++ .ctx.data = { \ ++ .len = l, \ ++ .buf.in = b, \ ++ .force_8bit = true, \ ++ }, \ ++ .delay_ns = ns, \ ++ } ++ ++#define NAND_OP_8BIT_DATA_OUT(l, b, ns) \ ++ { \ ++ .type = NAND_OP_DATA_OUT_INSTR, \ ++ .ctx.data = { \ ++ .len = l, \ ++ .buf.out = b, \ ++ .force_8bit = true, \ ++ }, \ ++ .delay_ns = ns, \ ++ } ++ ++#define NAND_OP_WAIT_RDY(tout_ms, ns) \ ++ { \ ++ .type = NAND_OP_WAITRDY_INSTR, \ ++ .ctx.waitrdy.timeout_ms = tout_ms, \ ++ .delay_ns = ns, \ ++ } ++ ++/** ++ * struct nand_subop - a sub operation ++ * @instrs: array of instructions ++ * @ninstrs: length of the @instrs array ++ * @first_instr_start_off: offset to start from for the first instruction ++ * of the sub-operation ++ * @last_instr_end_off: offset to end at (excluded) for the last instruction ++ * of the sub-operation ++ * ++ * Both @first_instr_start_off and @last_instr_end_off only apply to data or ++ * address instructions. ++ * ++ * When an operation cannot be handled as is by the NAND controller, it will ++ * be split by the parser into sub-operations which will be passed to the ++ * controller driver. ++ */ ++struct nand_subop { ++ const struct nand_op_instr *instrs; ++ unsigned int ninstrs; ++ unsigned int first_instr_start_off; ++ unsigned int last_instr_end_off; ++}; ++ ++unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop, ++ unsigned int op_id); ++unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, ++ unsigned int op_id); ++unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop, ++ unsigned int op_id); ++unsigned int nand_subop_get_data_len(const struct nand_subop *subop, ++ unsigned int op_id); ++ ++/** ++ * struct nand_op_parser_addr_constraints - Constraints for address instructions ++ * @maxcycles: maximum number of address cycles the controller can issue in a ++ * single step ++ */ ++struct nand_op_parser_addr_constraints { ++ unsigned int maxcycles; ++}; ++ ++/** ++ * struct nand_op_parser_data_constraints - Constraints for data instructions ++ * @maxlen: maximum data length that the controller can handle in a single step ++ */ ++struct nand_op_parser_data_constraints { ++ unsigned int maxlen; ++}; ++ ++/** ++ * struct nand_op_parser_pattern_elem - One element of a pattern ++ * @type: the instructuction type ++ * @optional: whether this element of the pattern is optional or mandatory ++ * @addr/@data: address or data constraint (number of cycles or data length) ++ */ ++struct nand_op_parser_pattern_elem { ++ enum nand_op_instr_type type; ++ bool optional; ++ union { ++ struct nand_op_parser_addr_constraints addr; ++ struct nand_op_parser_data_constraints data; ++ }; ++}; ++ ++#define NAND_OP_PARSER_PAT_CMD_ELEM(_opt) \ ++ { \ ++ .type = NAND_OP_CMD_INSTR, \ ++ .optional = _opt, \ ++ } ++ ++#define NAND_OP_PARSER_PAT_ADDR_ELEM(_opt, _maxcycles) \ ++ { \ ++ .type = NAND_OP_ADDR_INSTR, \ ++ .optional = _opt, \ ++ .addr.maxcycles = _maxcycles, \ ++ } ++ ++#define NAND_OP_PARSER_PAT_DATA_IN_ELEM(_opt, _maxlen) \ ++ { \ ++ .type = NAND_OP_DATA_IN_INSTR, \ ++ .optional = _opt, \ ++ .data.maxlen = _maxlen, \ ++ } ++ ++#define NAND_OP_PARSER_PAT_DATA_OUT_ELEM(_opt, _maxlen) \ ++ { \ ++ .type = NAND_OP_DATA_OUT_INSTR, \ ++ .optional = _opt, \ ++ .data.maxlen = _maxlen, \ ++ } ++ ++#define NAND_OP_PARSER_PAT_WAITRDY_ELEM(_opt) \ ++ { \ ++ .type = NAND_OP_WAITRDY_INSTR, \ ++ .optional = _opt, \ ++ } ++ ++/** ++ * struct nand_op_parser_pattern - NAND sub-operation pattern descriptor ++ * @elems: array of pattern elements ++ * @nelems: number of pattern elements in @elems array ++ * @exec: the function that will issue a sub-operation ++ * ++ * A pattern is a list of elements, each element reprensenting one instruction ++ * with its constraints. The pattern itself is used by the core to match NAND ++ * chip operation with NAND controller operations. ++ * Once a match between a NAND controller operation pattern and a NAND chip ++ * operation (or a sub-set of a NAND operation) is found, the pattern ->exec() ++ * hook is called so that the controller driver can issue the operation on the ++ * bus. ++ * ++ * Controller drivers should declare as many patterns as they support and pass ++ * this list of patterns (created with the help of the following macro) to ++ * the nand_op_parser_exec_op() helper. ++ */ ++struct nand_op_parser_pattern { ++ const struct nand_op_parser_pattern_elem *elems; ++ unsigned int nelems; ++ int (*exec)(struct nand_chip *chip, const struct nand_subop *subop); ++}; ++ ++#define NAND_OP_PARSER_PATTERN(_exec, ...) \ ++ { \ ++ .exec = _exec, \ ++ .elems = (struct nand_op_parser_pattern_elem[]) { __VA_ARGS__ }, \ ++ .nelems = sizeof((struct nand_op_parser_pattern_elem[]) { __VA_ARGS__ }) / \ ++ sizeof(struct nand_op_parser_pattern_elem), \ ++ } ++ ++/** ++ * struct nand_op_parser - NAND controller operation parser descriptor ++ * @patterns: array of supported patterns ++ * @npatterns: length of the @patterns array ++ * ++ * The parser descriptor is just an array of supported patterns which will be ++ * iterated by nand_op_parser_exec_op() everytime it tries to execute an ++ * NAND operation (or tries to determine if a specific operation is supported). ++ * ++ * It is worth mentioning that patterns will be tested in their declaration ++ * order, and the first match will be taken, so it's important to order patterns ++ * appropriately so that simple/inefficient patterns are placed at the end of ++ * the list. Usually, this is where you put single instruction patterns. ++ */ ++struct nand_op_parser { ++ const struct nand_op_parser_pattern *patterns; ++ unsigned int npatterns; ++}; ++ ++#define NAND_OP_PARSER(...) \ ++ { \ ++ .patterns = (struct nand_op_parser_pattern[]) { __VA_ARGS__ }, \ ++ .npatterns = sizeof((struct nand_op_parser_pattern[]) { __VA_ARGS__ }) / \ ++ sizeof(struct nand_op_parser_pattern), \ ++ } ++ ++/** ++ * struct nand_operation - NAND operation descriptor ++ * @instrs: array of instructions to execute ++ * @ninstrs: length of the @instrs array ++ * ++ * The actual operation structure that will be passed to chip->exec_op(). ++ */ ++struct nand_operation { ++ const struct nand_op_instr *instrs; ++ unsigned int ninstrs; ++}; ++ ++#define NAND_OPERATION(_instrs) \ ++ { \ ++ .instrs = _instrs, \ ++ .ninstrs = ARRAY_SIZE(_instrs), \ ++ } ++ ++int nand_op_parser_exec_op(struct nand_chip *chip, ++ const struct nand_op_parser *parser, ++ const struct nand_operation *op, bool check_only); ++ ++/** ++ * struct nand_chip - NAND Private Flash Chip Data ++ * @mtd: MTD device registered to the MTD framework ++ * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the ++ * flash device ++ * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the ++ * flash device. ++ * @read_byte: [REPLACEABLE] read one byte from the chip ++ * @read_word: [REPLACEABLE] read one word from the chip ++ * @write_byte: [REPLACEABLE] write a single byte to the chip on the ++ * low 8 I/O lines ++ * @write_buf: [REPLACEABLE] write data from the buffer to the chip ++ * @read_buf: [REPLACEABLE] read data from the chip into the buffer ++ * @select_chip: [REPLACEABLE] select chip nr ++ * @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers ++ * @block_markbad: [REPLACEABLE] mark a block bad ++ * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling ++ * ALE/CLE/nCE. Also used to write command and address ++ * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing ++ * device ready/busy line. If set to NULL no access to ++ * ready/busy is available and the ready/busy information ++ * is read from the chip status register. ++ * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing ++ * commands to the chip. ++ * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ++ * ready. ++ * @exec_op: controller specific method to execute NAND operations. ++ * This method replaces ->cmdfunc(), ++ * ->{read,write}_{buf,byte,word}(), ->dev_ready() and ++ * ->waifunc(). ++ * @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for ++ * setting the read-retry mode. Mostly needed for MLC NAND. ++ * @ecc: [BOARDSPECIFIC] ECC control structure ++ * @buf_align: minimum buffer alignment required by a platform ++ * @dummy_controller: dummy controller implementation for drivers that can ++ * only control a single chip ++ * @erase: [REPLACEABLE] erase function ++ * @scan_bbt: [REPLACEABLE] function to scan bad block table ++ * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring ++ * data from array to read regs (tR). ++ * @state: [INTERN] the current state of the NAND device ++ * @oob_poi: "poison value buffer," used for laying out OOB data ++ * before writing ++ * @page_shift: [INTERN] number of address bits in a page (column ++ * address bits). ++ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock ++ * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry ++ * @chip_shift: [INTERN] number of address bits in one chip ++ * @options: [BOARDSPECIFIC] various chip options. They can partly ++ * be set to inform nand_scan about special functionality. ++ * See the defines for further explanation. ++ * @bbt_options: [INTERN] bad block specific options. All options used ++ * here must come from bbm.h. By default, these options ++ * will be copied to the appropriate nand_bbt_descr's. ++ * @badblockpos: [INTERN] position of the bad block marker in the oob ++ * area. ++ * @badblockbits: [INTERN] minimum number of set bits in a good block's ++ * bad block marker position; i.e., BBM == 11110111b is ++ * not bad when badblockbits == 7 ++ * @bits_per_cell: [INTERN] number of bits per cell. i.e., 1 means SLC. ++ * @ecc_strength_ds: [INTERN] ECC correctability from the datasheet. ++ * Minimum amount of bit errors per @ecc_step_ds guaranteed ++ * to be correctable. If unknown, set to zero. ++ * @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds, ++ * also from the datasheet. It is the recommended ECC step ++ * size, if known; if unknown, set to zero. ++ * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is ++ * set to the actually used ONFI mode if the chip is ++ * ONFI compliant or deduced from the datasheet if ++ * the NAND chip is not ONFI compliant. ++ * @numchips: [INTERN] number of physical chips ++ * @chipsize: [INTERN] the size of one chip for multichip arrays ++ * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 ++ * @data_buf: [INTERN] buffer for data, size is (page size + oobsize). ++ * @pagebuf: [INTERN] holds the pagenumber which is currently in ++ * data_buf. ++ * @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is ++ * currently in data_buf. ++ * @subpagesize: [INTERN] holds the subpagesize ++ * @id: [INTERN] holds NAND ID ++ * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), ++ * non 0 if ONFI supported. ++ * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded), ++ * non 0 if JEDEC supported. ++ * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is ++ * supported, 0 otherwise. ++ * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is ++ * supported, 0 otherwise. ++ * @max_bb_per_die: [INTERN] the max number of bad blocks each die of a ++ * this nand device will encounter their life times. ++ * @blocks_per_die: [INTERN] The number of PEBs in a die ++ * @data_interface: [INTERN] NAND interface timing information ++ * @read_retries: [INTERN] the number of read retry modes supported ++ * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand ++ * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand ++ * @setup_data_interface: [OPTIONAL] setup the data interface and timing. If ++ * chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this ++ * means the configuration should not be applied but ++ * only checked. ++ * @bbt: [INTERN] bad block table pointer ++ * @bbt_td: [REPLACEABLE] bad block table descriptor for flash ++ * lookup. ++ * @bbt_md: [REPLACEABLE] bad block table mirror descriptor ++ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial ++ * bad block scan. ++ * @controller: [REPLACEABLE] a pointer to a hardware controller ++ * structure which is shared among multiple independent ++ * devices. ++ * @priv: [OPTIONAL] pointer to private chip data ++ * @manufacturer: [INTERN] Contains manufacturer information ++ */ ++ ++struct nand_chip { ++ struct mtd_info mtd; ++ void __iomem *IO_ADDR_R; ++ void __iomem *IO_ADDR_W; ++ ++ uint8_t (*read_byte)(struct mtd_info *mtd); ++ u16 (*read_word)(struct mtd_info *mtd); ++ void (*write_byte)(struct mtd_info *mtd, uint8_t byte); ++ void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); ++ void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); ++ void (*select_chip)(struct mtd_info *mtd, int chip); ++ int (*block_bad)(struct mtd_info *mtd, loff_t ofs); ++ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); ++ void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); ++ int (*dev_ready)(struct mtd_info *mtd); ++ void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, ++ int page_addr); ++ int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); ++ int (*exec_op)(struct nand_chip *chip, ++ const struct nand_operation *op, ++ bool check_only); ++ int (*erase)(struct mtd_info *mtd, int page); ++ int (*scan_bbt)(struct mtd_info *mtd); ++ int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip, ++ int feature_addr, uint8_t *subfeature_para); ++ int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, ++ int feature_addr, uint8_t *subfeature_para); ++ int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); ++ int (*setup_data_interface)(struct mtd_info *mtd, int chipnr, ++ const struct nand_data_interface *conf); ++ ++ int chip_delay; ++ unsigned int options; ++ unsigned int bbt_options; ++ ++ int page_shift; ++ int phys_erase_shift; ++ int bbt_erase_shift; ++ int chip_shift; ++ int numchips; ++ uint64_t chipsize; ++ int pagemask; ++ u8 *data_buf; ++ int pagebuf; ++ unsigned int pagebuf_bitflips; ++ int subpagesize; ++ uint8_t bits_per_cell; ++ uint16_t ecc_strength_ds; ++ uint16_t ecc_step_ds; ++ int onfi_timing_mode_default; ++ int badblockpos; ++ int badblockbits; ++ ++ struct nand_id id; ++ int onfi_version; ++ int jedec_version; ++ union { ++ struct nand_onfi_params onfi_params; ++ struct nand_jedec_params jedec_params; ++ }; ++ u16 max_bb_per_die; ++ u32 blocks_per_die; ++ ++ struct nand_data_interface data_interface; ++ ++ int read_retries; ++ ++ flstate_t state; ++ ++ uint8_t *oob_poi; ++ struct nand_controller *controller; ++ ++ struct nand_ecc_ctrl ecc; ++ unsigned long buf_align; ++ struct nand_controller dummy_controller; ++ ++ uint8_t *bbt; ++ struct nand_bbt_descr *bbt_td; ++ struct nand_bbt_descr *bbt_md; ++ ++ struct nand_bbt_descr *badblock_pattern; ++ ++ void *priv; ++ ++ struct { ++ const struct nand_manufacturer *desc; ++ void *priv; ++ } manufacturer; ++}; ++ ++static inline int nand_exec_op(struct nand_chip *chip, ++ const struct nand_operation *op) ++{ ++ if (!chip->exec_op) ++ return -ENOTSUPP; ++ ++ return chip->exec_op(chip, op, false); ++} ++ ++extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; ++extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops; ++ ++static inline void nand_set_flash_node(struct nand_chip *chip, ++ struct device_node *np) ++{ ++ mtd_set_of_node(&chip->mtd, np); ++} ++ ++static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) ++{ ++ return mtd_get_of_node(&chip->mtd); ++} ++ ++static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd) ++{ ++ return container_of(mtd, struct nand_chip, mtd); ++} ++ ++static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip) ++{ ++ return &chip->mtd; ++} ++ ++static inline void *nand_get_controller_data(struct nand_chip *chip) ++{ ++ return chip->priv; ++} ++ ++static inline void nand_set_controller_data(struct nand_chip *chip, void *priv) ++{ ++ chip->priv = priv; ++} ++ ++static inline void nand_set_manufacturer_data(struct nand_chip *chip, ++ void *priv) ++{ ++ chip->manufacturer.priv = priv; ++} ++ ++static inline void *nand_get_manufacturer_data(struct nand_chip *chip) ++{ ++ return chip->manufacturer.priv; ++} ++ ++/* ++ * NAND Flash Manufacturer ID Codes ++ */ ++#define NAND_MFR_TOSHIBA 0x98 ++#define NAND_MFR_ESMT 0xc8 ++#define NAND_MFR_SAMSUNG 0xec ++#define NAND_MFR_FUJITSU 0x04 ++#define NAND_MFR_NATIONAL 0x8f ++#define NAND_MFR_RENESAS 0x07 ++#define NAND_MFR_STMICRO 0x20 ++#define NAND_MFR_HYNIX 0xad ++#define NAND_MFR_MICRON 0x2c ++#define NAND_MFR_AMD 0x01 ++#define NAND_MFR_MACRONIX 0xc2 ++#define NAND_MFR_EON 0x92 ++#define NAND_MFR_SANDISK 0x45 ++#define NAND_MFR_INTEL 0x89 ++#define NAND_MFR_ATO 0x9b ++#define NAND_MFR_WINBOND 0xef ++ ++ ++/* ++ * A helper for defining older NAND chips where the second ID byte fully ++ * defined the chip, including the geometry (chip size, eraseblock size, page ++ * size). All these chips have 512 bytes NAND page size. ++ */ ++#define LEGACY_ID_NAND(nm, devid, chipsz, erasesz, opts) \ ++ { .name = (nm), {{ .dev_id = (devid) }}, .pagesize = 512, \ ++ .chipsize = (chipsz), .erasesize = (erasesz), .options = (opts) } ++ ++/* ++ * A helper for defining newer chips which report their page size and ++ * eraseblock size via the extended ID bytes. ++ * ++ * The real difference between LEGACY_ID_NAND and EXTENDED_ID_NAND is that with ++ * EXTENDED_ID_NAND, manufacturers overloaded the same device ID so that the ++ * device ID now only represented a particular total chip size (and voltage, ++ * buswidth), and the page size, eraseblock size, and OOB size could vary while ++ * using the same device ID. ++ */ ++#define EXTENDED_ID_NAND(nm, devid, chipsz, opts) \ ++ { .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \ ++ .options = (opts) } ++ ++#define NAND_ECC_INFO(_strength, _step) \ ++ { .strength_ds = (_strength), .step_ds = (_step) } ++#define NAND_ECC_STRENGTH(type) ((type)->ecc.strength_ds) ++#define NAND_ECC_STEP(type) ((type)->ecc.step_ds) ++ ++/** ++ * struct nand_flash_dev - NAND Flash Device ID Structure ++ * @name: a human-readable name of the NAND chip ++ * @dev_id: the device ID (the second byte of the full chip ID array) ++ * @mfr_id: manufecturer ID part of the full chip ID array (refers the same ++ * memory address as @id[0]) ++ * @dev_id: device ID part of the full chip ID array (refers the same memory ++ * address as @id[1]) ++ * @id: full device ID array ++ * @pagesize: size of the NAND page in bytes; if 0, then the real page size (as ++ * well as the eraseblock size) is determined from the extended NAND ++ * chip ID array) ++ * @chipsize: total chip size in MiB ++ * @erasesize: eraseblock size in bytes (determined from the extended ID if 0) ++ * @options: stores various chip bit options ++ * @id_len: The valid length of the @id. ++ * @oobsize: OOB size ++ * @ecc: ECC correctability and step information from the datasheet. ++ * @ecc.strength_ds: The ECC correctability from the datasheet, same as the ++ * @ecc_strength_ds in nand_chip{}. ++ * @ecc.step_ds: The ECC step required by the @ecc.strength_ds, same as the ++ * @ecc_step_ds in nand_chip{}, also from the datasheet. ++ * For example, the "4bit ECC for each 512Byte" can be set with ++ * NAND_ECC_INFO(4, 512). ++ * @onfi_timing_mode_default: the default ONFI timing mode entered after a NAND ++ * reset. Should be deduced from timings described ++ * in the datasheet. ++ * ++ */ ++struct nand_flash_dev { ++ char *name; ++ union { ++ struct { ++ uint8_t mfr_id; ++ uint8_t dev_id; ++ }; ++ uint8_t id[NAND_MAX_ID_LEN]; ++ }; ++ unsigned int pagesize; ++ unsigned int chipsize; ++ unsigned int erasesize; ++ unsigned int options; ++ uint16_t id_len; ++ uint16_t oobsize; ++ struct { ++ uint16_t strength_ds; ++ uint16_t step_ds; ++ } ecc; ++ int onfi_timing_mode_default; ++}; ++ ++/** ++ * struct nand_manufacturer - NAND Flash Manufacturer structure ++ * @name: Manufacturer name ++ * @id: manufacturer ID code of device. ++ * @ops: manufacturer operations ++*/ ++struct nand_manufacturer { ++ int id; ++ char *name; ++ const struct nand_manufacturer_ops *ops; ++}; ++ ++const struct nand_manufacturer *nand_get_manufacturer(u8 id); ++ ++static inline const char * ++nand_manufacturer_name(const struct nand_manufacturer *manufacturer) ++{ ++ return manufacturer ? manufacturer->name : "Unknown"; ++} ++ ++extern struct nand_flash_dev nand_flash_ids[]; ++ ++extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops; ++extern const struct nand_manufacturer_ops samsung_nand_manuf_ops; ++extern const struct nand_manufacturer_ops hynix_nand_manuf_ops; ++extern const struct nand_manufacturer_ops micron_nand_manuf_ops; ++extern const struct nand_manufacturer_ops amd_nand_manuf_ops; ++extern const struct nand_manufacturer_ops macronix_nand_manuf_ops; ++ ++int nand_default_bbt(struct mtd_info *mtd); ++int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); ++int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs); ++int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); ++int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, ++ int allowbbt); ++int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, uint8_t *buf); ++ ++/** ++ * struct platform_nand_chip - chip level device structure ++ * @nr_chips: max. number of chips to scan for ++ * @chip_offset: chip number offset ++ * @nr_partitions: number of partitions pointed to by partitions (or zero) ++ * @partitions: mtd partition list ++ * @chip_delay: R/B delay value in us ++ * @options: Option flags, e.g. 16bit buswidth ++ * @bbt_options: BBT option flags, e.g. NAND_BBT_USE_FLASH ++ * @part_probe_types: NULL-terminated array of probe types ++ */ ++struct platform_nand_chip { ++ int nr_chips; ++ int chip_offset; ++ int nr_partitions; ++ struct mtd_partition *partitions; ++ int chip_delay; ++ unsigned int options; ++ unsigned int bbt_options; ++ const char **part_probe_types; ++}; ++ ++/* Keep gcc happy */ ++struct platform_device; ++ ++/** ++ * struct platform_nand_ctrl - controller level device structure ++ * @probe: platform specific function to probe/setup hardware ++ * @remove: platform specific function to remove/teardown hardware ++ * @hwcontrol: platform specific hardware control structure ++ * @dev_ready: platform specific function to read ready/busy pin ++ * @select_chip: platform specific chip select function ++ * @cmd_ctrl: platform specific function for controlling ++ * ALE/CLE/nCE. Also used to write command and address ++ * @write_buf: platform specific function for write buffer ++ * @read_buf: platform specific function for read buffer ++ * @read_byte: platform specific function to read one byte from chip ++ * @priv: private data to transport driver specific settings ++ * ++ * All fields are optional and depend on the hardware driver requirements ++ */ ++struct platform_nand_ctrl { ++ int (*probe)(struct platform_device *pdev); ++ void (*remove)(struct platform_device *pdev); ++ void (*hwcontrol)(struct mtd_info *mtd, int cmd); ++ int (*dev_ready)(struct mtd_info *mtd); ++ void (*select_chip)(struct mtd_info *mtd, int chip); ++ void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); ++ void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); ++ void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); ++ unsigned char (*read_byte)(struct mtd_info *mtd); ++ void *priv; ++}; ++ ++/** ++ * struct platform_nand_data - container structure for platform-specific data ++ * @chip: chip level chip structure ++ * @ctrl: controller level device structure ++ */ ++struct platform_nand_data { ++ struct platform_nand_chip chip; ++ struct platform_nand_ctrl ctrl; ++}; ++ ++/* return the supported features. */ ++static inline int onfi_feature(struct nand_chip *chip) ++{ ++ return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0; ++} ++ ++/* return the supported asynchronous timing mode. */ ++static inline int onfi_get_async_timing_mode(struct nand_chip *chip) ++{ ++ if (!chip->onfi_version) ++ return ONFI_TIMING_MODE_UNKNOWN; ++ return le16_to_cpu(chip->onfi_params.async_timing_mode); ++} ++ ++/* return the supported synchronous timing mode. */ ++static inline int onfi_get_sync_timing_mode(struct nand_chip *chip) ++{ ++ if (!chip->onfi_version) ++ return ONFI_TIMING_MODE_UNKNOWN; ++ return le16_to_cpu(chip->onfi_params.src_sync_timing_mode); ++} ++ ++int onfi_fill_data_interface(struct nand_chip *chip, ++ enum nand_data_interface_type type, ++ int timing_mode); ++ ++/* ++ * Check if it is a SLC nand. ++ * The !nand_is_slc() can be used to check the MLC/TLC nand chips. ++ * We do not distinguish the MLC and TLC now. ++ */ ++static inline bool nand_is_slc(struct nand_chip *chip) ++{ ++ WARN(chip->bits_per_cell == 0, ++ "chip->bits_per_cell is used uninitialized\n"); ++ return chip->bits_per_cell == 1; ++} ++ ++/** ++ * Check if the opcode's address should be sent only on the lower 8 bits ++ * @command: opcode to check ++ */ ++static inline int nand_opcode_8bits(unsigned int command) ++{ ++ switch (command) { ++ case NAND_CMD_READID: ++ case NAND_CMD_PARAM: ++ case NAND_CMD_GET_FEATURES: ++ case NAND_CMD_SET_FEATURES: ++ return 1; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++/* return the supported JEDEC features. */ ++static inline int jedec_feature(struct nand_chip *chip) ++{ ++ return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features) ++ : 0; ++} ++ ++/* get timing characteristics from ONFI timing mode. */ ++const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode); ++ ++int nand_check_erased_ecc_chunk(void *data, int datalen, ++ void *ecc, int ecclen, ++ void *extraoob, int extraooblen, ++ int threshold); ++ ++int nand_check_ecc_caps(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail); ++ ++int nand_match_ecc_req(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail); ++ ++int nand_maximize_ecc(struct nand_chip *chip, ++ const struct nand_ecc_caps *caps, int oobavail); ++ ++/* Default write_oob implementation */ ++int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); ++ ++/* Default write_oob syndrome implementation */ ++int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, ++ int page); ++ ++/* Default read_oob implementation */ ++int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); ++ ++/* Default read_oob syndrome implementation */ ++int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, ++ int page); ++ ++/* Stub used by drivers that do not support GET/SET FEATURES operations */ ++int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, ++ struct nand_chip *chip, int addr, ++ u8 *subfeature_param); ++ ++/* Default read_page_raw implementation */ ++int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf, int oob_required, int page); ++ ++/* Default write_page_raw implementation */ ++int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf, int oob_required, int page); ++ ++/* Reset and initialize a NAND device */ ++int nand_reset(struct nand_chip *chip, int chipnr); ++ ++/* NAND operation helpers */ ++int nand_reset_op(struct nand_chip *chip); ++int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, ++ unsigned int len); ++int nand_status_op(struct nand_chip *chip, u8 *status); ++int nand_exit_status_op(struct nand_chip *chip); ++int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock); ++int nand_read_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, void *buf, unsigned int len); ++int nand_change_read_column_op(struct nand_chip *chip, ++ unsigned int offset_in_page, void *buf, ++ unsigned int len, bool force_8bit); ++int nand_read_oob_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, void *buf, unsigned int len); ++int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, const void *buf, ++ unsigned int len); ++int nand_prog_page_end_op(struct nand_chip *chip); ++int nand_prog_page_op(struct nand_chip *chip, unsigned int page, ++ unsigned int offset_in_page, const void *buf, ++ unsigned int len); ++int nand_change_write_column_op(struct nand_chip *chip, ++ unsigned int offset_in_page, const void *buf, ++ unsigned int len, bool force_8bit); ++int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, ++ bool force_8bit); ++int nand_write_data_op(struct nand_chip *chip, const void *buf, ++ unsigned int len, bool force_8bit); ++ ++/* Free resources held by the NAND device */ ++void nand_cleanup(struct nand_chip *chip); ++ ++/* Default extended ID decoding function */ ++void nand_decode_ext_id(struct nand_chip *chip); ++ ++/* ++ * External helper for controller drivers that have to implement the WAITRDY ++ * instruction and have no physical pin to check it. ++ */ ++int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms); ++ ++#endif /* __LINUX_MTD_RAWNAND_H */ +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index 2ecf0f3..daf7606 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1510,6 +1510,8 @@ enum netdev_priv_flags { + * @if_port: Selectable AUI, TP, ... + * @dma: DMA channel + * @mtu: Interface MTU value ++ * @min_mtu: Interface Minimum MTU value ++ * @max_mtu: Interface Maximum MTU value + * @type: Interface hardware type + * @hard_header_len: Maximum hardware header length. + * @min_header_len: Minimum hardware header length +@@ -1731,6 +1733,8 @@ struct net_device { + unsigned char dma; + + unsigned int mtu; ++ unsigned int min_mtu; ++ unsigned int max_mtu; + unsigned short type; + unsigned short hard_header_len; + unsigned short min_header_len; +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 867110c..974f009 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -81,6 +81,14 @@ + PHY_INTERFACE_MODE_MOCA, + PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, ++ PHY_INTERFACE_MODE_1000BASEX, ++ PHY_INTERFACE_MODE_2500BASEX, ++ PHY_INTERFACE_MODE_RXAUI, ++ PHY_INTERFACE_MODE_XAUI, ++ /* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */ ++ PHY_INTERFACE_MODE_10GKR, ++ PHY_INTERFACE_MODE_SFI, ++ PHY_INTERFACE_MODE_XFI, + PHY_INTERFACE_MODE_MAX, + } phy_interface_t; + +@@ -126,6 +134,16 @@ static inline const char *phy_modes(phy_interface_t interface) + return "qsgmii"; + case PHY_INTERFACE_MODE_TRGMII: + return "trgmii"; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return "1000base-x"; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return "2500base-x"; ++ case PHY_INTERFACE_MODE_RXAUI: ++ return "rxaui"; ++ case PHY_INTERFACE_MODE_XAUI: ++ return "xaui"; ++ case PHY_INTERFACE_MODE_10GKR: ++ return "10gbase-kr"; + default: + return "unknown"; + } +@@ -701,8 +719,7 @@ static inline bool phy_interface_mode_is_rgmii(phy_interface_t mode) + */ + static inline bool phy_interface_is_rgmii(struct phy_device *phydev) + { +- return phydev->interface >= PHY_INTERFACE_MODE_RGMII && +- phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID; ++ return phy_interface_mode_is_rgmii(phydev->interface); + }; + + /* +diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h +index 78bb0d7..10888a7 100644 +--- a/include/linux/phy/phy.h ++++ b/include/linux/phy/phy.h +@@ -27,6 +27,8 @@ enum phy_mode { + PHY_MODE_USB_HOST, + PHY_MODE_USB_DEVICE, + PHY_MODE_USB_OTG, ++ PHY_MODE_SGMII, ++ PHY_MODE_10GKR, + }; + + /** +@@ -289,7 +291,7 @@ static inline struct phy *devm_phy_get(struct device *dev, const char *string) + static inline struct phy *devm_phy_optional_get(struct device *dev, + const char *string) + { +- return ERR_PTR(-ENOSYS); ++ return NULL; + } + + static inline struct phy *devm_of_phy_get(struct device *dev, +diff --git a/include/linux/platform_data/mtd-nand-pxa3xx.h b/include/linux/platform_data/mtd-nand-pxa3xx.h +index 394d155..4fd0f59 100644 +--- a/include/linux/platform_data/mtd-nand-pxa3xx.h ++++ b/include/linux/platform_data/mtd-nand-pxa3xx.h +@@ -1,3 +1,4 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ + #ifndef __ASM_ARCH_PXA3XX_NAND_H + #define __ASM_ARCH_PXA3XX_NAND_H + +@@ -5,41 +6,22 @@ + #include + + /* +- * Current pxa3xx_nand controller has two chip select which +- * both be workable. +- * +- * Notice should be taken that: +- * When you want to use this feature, you should not enable the +- * keep configuration feature, for two chip select could be +- * attached with different nand chip. The different page size +- * and timing requirement make the keep configuration impossible. ++ * Current pxa3xx_nand controller has two chip select which both be workable but ++ * historically all platforms remaining on platform data used only one. Switch ++ * to device tree if you need more. + */ +- +-/* The max num of chip select current support */ +-#define NUM_CHIP_SELECT (2) + struct pxa3xx_nand_platform_data { +- +- /* the data flash bus is shared between the Static Memory +- * Controller and the Data Flash Controller, the arbiter +- * controls the ownership of the bus +- */ +- int enable_arbiter; +- +- /* allow platform code to keep OBM/bootloader defined NFC config */ +- int keep_config; +- +- /* indicate how many chip selects will be used */ +- int num_cs; +- +- /* use an flash-based bad block table */ +- bool flash_bbt; +- +- /* requested ECC strength and ECC step size */ ++ /* Keep OBM/bootloader NFC timing configuration */ ++ bool keep_config; ++ /* Use a flash-based bad block table */ ++ bool flash_bbt; ++ /* Requested ECC strength and ECC step size */ + int ecc_strength, ecc_step_size; +- +- const struct mtd_partition *parts[NUM_CHIP_SELECT]; +- unsigned int nr_parts[NUM_CHIP_SELECT]; ++ /* Partitions */ ++ const struct mtd_partition *parts; ++ unsigned int nr_parts; + }; + + extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info); ++ + #endif /* __ASM_ARCH_PXA3XX_NAND_H */ +diff --git a/include/net/tso.h b/include/net/tso.h +index b7be852..9a56c39 100644 +--- a/include/net/tso.h ++++ b/include/net/tso.h +@@ -3,6 +3,8 @@ + + #include + ++#define TSO_HEADER_SIZE 128 ++ + struct tso_t { + int next_frag_idx; + void *data; +diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h +index 3d3de5e..345b138 100644 +--- a/include/uapi/linux/if_ether.h ++++ b/include/uapi/linux/if_ether.h +@@ -36,6 +36,8 @@ + #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ + #define ETH_FCS_LEN 4 /* Octets in the FCS */ + ++#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ ++ + /* + * These are the defined Ethernet Protocol ID's. + */ +diff --git a/net/core/dev.c b/net/core/dev.c +index 8e187f9..d67def2 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6599,9 +6599,18 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) + if (new_mtu == dev->mtu) + return 0; + +- /* MTU must be positive. */ +- if (new_mtu < 0) ++ /* MTU must be positive, and in range */ ++ if (new_mtu < 0 || new_mtu < dev->min_mtu) { ++ net_err_ratelimited("%s: Invalid MTU %d requested, hw min %d\n", ++ dev->name, new_mtu, dev->min_mtu); + return -EINVAL; ++ } ++ ++ if (dev->max_mtu > 0 && new_mtu > dev->max_mtu) { ++ net_err_ratelimited("%s: Invalid MTU %d requested, hw max %d\n", ++ dev->name, new_mtu, dev->min_mtu); ++ return -EINVAL; ++ } + + if (!netif_device_present(dev)) + return -ENODEV; +-- +1.8.3.1 + diff --git a/patch/0001-arm64-default-config-for-sonic-patches.patch b/patch/0001-arm64-default-config-for-sonic-patches.patch new file mode 100644 index 000000000..de1652e0a --- /dev/null +++ b/patch/0001-arm64-default-config-for-sonic-patches.patch @@ -0,0 +1,74 @@ +From adf767005a35a2afeb8006c1023633af4de9d78e Mon Sep 17 00:00:00 2001 +From: Antony Rheneus +Date: Thu, 17 Oct 2019 18:31:57 +0530 +Subject: [PATCH] arm64 default config for sonic patches + +--- + debian/build/build_arm64_none_arm64/.config | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/debian/build/build_arm64_none_arm64/.config b/debian/build/build_arm64_none_arm64/.config +index 0ce4748..be5b136 100644 +--- a/debian/build/build_arm64_none_arm64/.config ++++ b/debian/build/build_arm64_none_arm64/.config +@@ -1247,6 +1247,7 @@ CONFIG_NET_ACT_POLICE=m + CONFIG_NET_ACT_GACT=m + CONFIG_GACT_PROB=y + CONFIG_NET_ACT_MIRRED=m ++# CONFIG_NET_ACT_SAMPLE is not set + CONFIG_NET_ACT_IPT=m + CONFIG_NET_ACT_NAT=m + CONFIG_NET_ACT_PEDIT=m +@@ -1289,6 +1290,7 @@ CONFIG_MPLS_IPTUNNEL=m + # CONFIG_HSR is not set + # CONFIG_NET_SWITCHDEV is not set + CONFIG_NET_L3_MASTER_DEV=y ++# CONFIG_CGROUP_L3MDEV is not set + # CONFIG_QRTR is not set + # CONFIG_NET_NCSI is not set + CONFIG_RPS=y +@@ -1472,6 +1474,7 @@ CONFIG_NFC_PN533=m + CONFIG_NFC_PN533_USB=m + # CONFIG_NFC_PN533_I2C is not set + # CONFIG_NFC_ST95HF is not set ++# CONFIG_PSAMPLE is not set + CONFIG_LWTUNNEL=y + CONFIG_DST_CACHE=y + CONFIG_NET_DEVLINK=m +@@ -1721,6 +1724,8 @@ CONFIG_EEPROM_LEGACY=m + CONFIG_EEPROM_MAX6875=m + CONFIG_EEPROM_93CX6=m + # CONFIG_EEPROM_93XX46 is not set ++# CONFIG_EEPROM_SFF_8436 is not set ++# CONFIG_EEPROM_OPTOE is not set + CONFIG_CB710_CORE=m + # CONFIG_CB710_DEBUG is not set + CONFIG_CB710_DEBUG_ASSUMPTIONS=y +@@ -2999,6 +3004,7 @@ CONFIG_I2C_MUX=m + # CONFIG_I2C_MUX_PINCTRL is not set + # CONFIG_I2C_MUX_REG is not set + # CONFIG_I2C_DEMUX_PINCTRL is not set ++# CONFIG_I2C_MUX_MLXCPLD is not set + CONFIG_I2C_HELPER_AUTO=y + CONFIG_I2C_SMBUS=m + CONFIG_I2C_ALGOBIT=m +@@ -3386,6 +3392,7 @@ CONFIG_SENSORS_MAX1668=m + CONFIG_SENSORS_MAX6639=m + CONFIG_SENSORS_MAX6642=m + CONFIG_SENSORS_MAX6650=m ++# CONFIG_SENSORS_MAX6620 is not set + # CONFIG_SENSORS_MAX6697 is not set + # CONFIG_SENSORS_MAX31790 is not set + # CONFIG_SENSORS_MCP3021 is not set +@@ -5397,6 +5404,8 @@ CONFIG_LEDS_LT3593=m + # + # CONFIG_LEDS_BLINKM is not set + # CONFIG_LEDS_SYSCON is not set ++# CONFIG_LEDS_MLXREG is not set ++# CONFIG_LEDS_USER is not set + + # + # LED Triggers +-- +2.7.4 + diff --git a/patch/0001-armhf-default-config-for-the-sonic-patches.patch b/patch/0001-armhf-default-config-for-the-sonic-patches.patch new file mode 100644 index 000000000..9f51f22c1 --- /dev/null +++ b/patch/0001-armhf-default-config-for-the-sonic-patches.patch @@ -0,0 +1,82 @@ +From 3a09f684413faf1c4f7d21d7d79914b54e087cf6 Mon Sep 17 00:00:00 2001 +From: Antony Rheneus +Date: Thu, 17 Oct 2019 10:48:29 +0530 +Subject: [PATCH] armhf default config for the sonic patches + +--- + debian/build/build_armhf_none_armmp/.config | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/debian/build/build_armhf_none_armmp/.config b/debian/build/build_armhf_none_armmp/.config +index 8f02d03..32a2698 100644 +--- a/debian/build/build_armhf_none_armmp/.config ++++ b/debian/build/build_armhf_none_armmp/.config +@@ -1501,6 +1501,7 @@ CONFIG_NET_ACT_POLICE=m + CONFIG_NET_ACT_GACT=m + CONFIG_GACT_PROB=y + CONFIG_NET_ACT_MIRRED=m ++# CONFIG_NET_ACT_SAMPLE is not set + CONFIG_NET_ACT_IPT=m + CONFIG_NET_ACT_NAT=m + CONFIG_NET_ACT_PEDIT=m +@@ -1543,6 +1544,7 @@ CONFIG_MPLS_IPTUNNEL=m + # CONFIG_HSR is not set + CONFIG_NET_SWITCHDEV=y + CONFIG_NET_L3_MASTER_DEV=y ++# CONFIG_CGROUP_L3MDEV is not set + # CONFIG_NET_NCSI is not set + CONFIG_RPS=y + CONFIG_RFS_ACCEL=y +@@ -1716,6 +1718,7 @@ CONFIG_NFC_PN533=m + CONFIG_NFC_PN533_USB=m + # CONFIG_NFC_PN533_I2C is not set + # CONFIG_NFC_ST95HF is not set ++# CONFIG_PSAMPLE is not set + CONFIG_LWTUNNEL=y + CONFIG_DST_CACHE=y + CONFIG_NET_DEVLINK=m +@@ -2010,6 +2013,8 @@ CONFIG_EEPROM_LEGACY=m + CONFIG_EEPROM_MAX6875=m + CONFIG_EEPROM_93CX6=m + # CONFIG_EEPROM_93XX46 is not set ++# CONFIG_EEPROM_SFF_8436 is not set ++# CONFIG_EEPROM_OPTOE is not set + CONFIG_CB710_CORE=m + # CONFIG_CB710_DEBUG is not set + CONFIG_CB710_DEBUG_ASSUMPTIONS=y +@@ -3385,6 +3390,7 @@ CONFIG_I2C_MUX_PCA954x=m + # CONFIG_I2C_MUX_PINCTRL is not set + # CONFIG_I2C_MUX_REG is not set + # CONFIG_I2C_DEMUX_PINCTRL is not set ++# CONFIG_I2C_MUX_MLXCPLD is not set + CONFIG_I2C_HELPER_AUTO=y + CONFIG_I2C_ALGOBIT=y + CONFIG_I2C_ALGOPCA=m +@@ -3818,6 +3824,7 @@ CONFIG_SENSORS_MAX1668=m + CONFIG_SENSORS_MAX6639=m + CONFIG_SENSORS_MAX6642=m + CONFIG_SENSORS_MAX6650=m ++# CONFIG_SENSORS_MAX6620 is not set + # CONFIG_SENSORS_MAX6697 is not set + # CONFIG_SENSORS_MAX31790 is not set + # CONFIG_SENSORS_MCP3021 is not set +@@ -6031,6 +6038,8 @@ CONFIG_LEDS_TCA6507=m + # + # CONFIG_LEDS_BLINKM is not set + # CONFIG_LEDS_SYSCON is not set ++# CONFIG_LEDS_MLXREG is not set ++# CONFIG_LEDS_USER is not set + + # + # LED Triggers +@@ -6434,6 +6443,7 @@ CONFIG_SPEAKUP_SYNTH_DUMMY=m + CONFIG_CHROME_PLATFORMS=y + # CONFIG_CROS_EC_CHARDEV is not set + CONFIG_CROS_EC_PROTO=y ++# CONFIG_MELLANOX_PLATFORM is not set + CONFIG_CLKDEV_LOOKUP=y + CONFIG_HAVE_CLK_PREPARE=y + CONFIG_COMMON_CLK=y +-- +2.7.4 + diff --git a/patch/0042-ARM64-PCI-Allow-userspace-to-mmap-PCI-resources.patch b/patch/0042-ARM64-PCI-Allow-userspace-to-mmap-PCI-resources.patch new file mode 100644 index 000000000..3705c9808 --- /dev/null +++ b/patch/0042-ARM64-PCI-Allow-userspace-to-mmap-PCI-resources.patch @@ -0,0 +1,59 @@ +From b5a750c95de583df71ab719ac9ae455bd6cfe7cc Mon Sep 17 00:00:00 2001 +From: gilt +Date: Mon, 26 Aug 2019 13:43:39 +0300 +Subject: [PATCH 4/6] ARM64/PCI: Allow userspace to mmap PCI resources + +Change-Id: I87ef6492e4956c77c75470ec010a4f11a9a8bdaf +--- + arch/arm64/include/asm/pci.h | 4 ++++ + arch/arm64/kernel/pci.c | 19 +++++++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h +index b9a7ba9..8a18915 100644 +--- a/arch/arm64/include/asm/pci.h ++++ b/arch/arm64/include/asm/pci.h +@@ -31,6 +31,10 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) + return -ENODEV; + } + ++#define HAVE_PCI_MMAP ++extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, ++ enum pci_mmap_state mmap_state, ++ int write_combine); + static inline int pci_proc_domain(struct pci_bus *bus) + { + return 1; +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index 1b3eb67..db6dd98 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -54,6 +54,25 @@ int pcibios_alloc_irq(struct pci_dev *dev) + return 0; + } + ++int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, ++ enum pci_mmap_state mmap_state, int write_combine) ++{ ++ if (mmap_state == pci_mmap_io) ++ return -EINVAL; ++ ++ /* ++ * Mark this as IO ++ */ ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot)) ++ return -EAGAIN; ++ ++ return 0; ++} ++ + /* + * raw_pci_read/write - Platform-specific PCI config space access. + */ +-- +1.8.3.1 + diff --git a/patch/0042-Marvell-a385-Micron-4G-flash-support.patch b/patch/0042-Marvell-a385-Micron-4G-flash-support.patch new file mode 100644 index 000000000..b6724c579 --- /dev/null +++ b/patch/0042-Marvell-a385-Micron-4G-flash-support.patch @@ -0,0 +1,41 @@ +From 5e248248ac5be0541cacb45b2b4d05df3741c2c6 Mon Sep 17 00:00:00 2001 +From: gilt +Date: Mon, 22 Jul 2019 13:44:06 +0300 +Subject: [PATCH 1/4] Micron 4G flash support + +Change-Id: Ib4dd7094920c124c213f6b3c81140845e5baa084 +--- + drivers/mtd/nand/pxa3xx_nand.c | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c +index 3b8911c..98e9a34 100644 +--- a/drivers/mtd/nand/pxa3xx_nand.c ++++ b/drivers/mtd/nand/pxa3xx_nand.c +@@ -1638,7 +1638,22 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, + ecc->size = info->chunk_size; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); + ecc->strength = 16; +- } else { ++ } ++ else if (strength == 8 && ecc_stepsize == 512 && page_size == 8192) { ++ info->ecc_bch = 1; ++ info->nfullchunks = 8; ++ info->ntotalchunks = 9; ++ info->chunk_size = 1024; ++ info->spare_size = 0; ++ info->last_chunk_size = 0; ++ info->last_spare_size = 160; ++ info->ecc_size = 32; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = info->chunk_size; ++ mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ++ ecc->strength = 16; ++ } ++ else { + dev_err(&info->pdev->dev, + "ECC strength %d at page size %d is not supported\n", + strength, page_size); +-- +2.6.3 + diff --git a/patch/0042-add-a7020-comexp-dts.patch b/patch/0042-add-a7020-comexp-dts.patch new file mode 100644 index 000000000..58f7a9aa0 --- /dev/null +++ b/patch/0042-add-a7020-comexp-dts.patch @@ -0,0 +1,169 @@ +From 5858434dd9a7301bd90730087b527b684a64ec2a Mon Sep 17 00:00:00 2001 +From: gilt +Date: Mon, 26 Aug 2019 14:58:10 +0300 +Subject: [PATCH 5/6] add a7020-comexp dts + +Change-Id: If4d6e2b704aad1616ad91871282e678eabb400dd +--- + arch/arm64/boot/dts/marvell/Makefile | 1 + + .../boot/dts/marvell/armada-7020-comexpress.dts | 136 +++++++++++++++++++++ + 2 files changed, 137 insertions(+) + create mode 100644 arch/arm64/boot/dts/marvell/armada-7020-comexpress.dts + +diff --git a/arch/arm64/boot/dts/marvell/Makefile b/arch/arm64/boot/dts/marvell/Makefile +index 55f86c8..da5c1db 100644 +--- a/arch/arm64/boot/dts/marvell/Makefile ++++ b/arch/arm64/boot/dts/marvell/Makefile +@@ -5,6 +5,7 @@ dtb-$(CONFIG_ARCH_BERLIN) += berlin4ct-stb.dtb + # Mvebu SoC Family + dtb-$(CONFIG_ARCH_MVEBU) += armada-3720-db.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-7020-amc.dtb ++dtb-$(CONFIG_ARCH_MVEBU) += armada-7020-comexpress.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-7040-db.dtb + dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-db.dtb + +diff --git a/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dts b/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dts +new file mode 100644 +index 00000000..f077419 +--- /dev/null ++++ b/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dts +@@ -0,0 +1,136 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR MIT) ++/* ++ * Copyright (C) 2019 Marvell Technology Group Ltd. ++ * ++ * Device Tree file for Marvell Armada 7020 AMC board. ++ */ ++ ++#include "armada-7020.dtsi" ++ ++/ { ++ model = "Marvell Armada-7020 COMEXPRESS board setup"; ++ compatible = "marvell,armada7020-comexpress", "marvell,armada7020", ++ "marvell,armada-ap806"; ++ ++ memory@0 { ++ device_type = "memory"; ++ reg = <0x0 0x0 0x0 0x40000000>; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ aliases { ++ ethernet0 = &cp0_emac0; ++ ethernet1 = &cp0_emac2; ++ }; ++ ++}; ++ ++&i2c0 { ++ status = "okay"; ++ clock-frequency = <100000>; ++}; ++ ++&spi0 { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&cpm_ethernet { ++ status = "okay"; ++}; ++ ++&cp0_emac0 { ++ status = "okay"; ++ phy-mode = "10gbase-kr"; ++}; ++ ++&cp0_emac2 { ++ status = "okay"; ++ phy = <&phy0>; ++ phy-mode = "rgmii-id"; ++}; ++ ++&cp0_ppv22 { ++ status = "okay"; ++ l4_chksum_jumbo_port = <0>; ++}; ++ ++&cp0_eth0 { ++ status = "okay"; ++}; ++ ++&cp0_eth1 { ++ status = "okay"; ++}; ++ ++&cpm_i2c0 { ++ status = "okay"; ++ clock-frequency = <100000>; ++}; ++ ++&cpm_mdio { ++ status = "okay"; ++ ++ phy0: ethernet-phy@1 { ++ reg = <0x10>; ++ }; ++}; ++ ++&cpm_nand { ++ status = "okay"; ++ ++ nand@0 { ++ reg = <0>; ++ label = "main-storage"; ++ nand-rb = <0>; ++ nand-ecc-mode = "hw"; ++ nand-on-flash-bbt; ++ nand-ecc-strength = <8>; ++ nand-ecc-step-size = <512>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "U-Boot"; ++ reg = <0 0x200000>; ++ }; ++ partition@200000 { ++ label = "Linux"; ++ reg = <0x200000 0xd00000>; ++ }; ++ partition@1000000 { ++ label = "Filesystem"; ++ reg = <0x1000000 0x3f000000>; ++ }; ++ }; ++ }; ++}; ++ ++&cpm_pcie0 { ++ status = "okay"; ++ num-lanes = <4>; ++ num-viewport = <8>; ++ ++ ranges = <0x81000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x00010000 ++ 0x82000000 0x0 0x00000000 0x8 0x00000000 0x2 0x00000000>; ++}; ++ ++&cpm_sata0 { ++ status = "okay"; ++}; ++ ++&cpm_sdhci0 { ++ status = "okay"; ++ bus-width = <4>; ++ no-1-8-v; ++ broken-cd; ++}; +-- +1.8.3.1 + diff --git a/patch/0042-arm64-additional-configs.patch b/patch/0042-arm64-additional-configs.patch new file mode 100644 index 000000000..36b6c26cf --- /dev/null +++ b/patch/0042-arm64-additional-configs.patch @@ -0,0 +1,535 @@ +From a961464da6c88aee4d94af4038c748a1e8a1f412 Mon Sep 17 00:00:00 2001 +From: Antony Rheneus +Date: Thu, 17 Oct 2019 18:57:48 +0530 +Subject: [PATCH] arm64 mandatory configs for boot + +--- + debian/build/build_arm64_none_arm64/.config | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/debian/build/build_arm64_none_arm64/.config b/debian/build/build_arm64_none_arm64/.config +index 0ce4748..be5b136 100644 +--- a/debian/build/build_arm64_none_arm64/.config ++++ b/debian/build/build_arm64_none_arm64/.config +@@ -490,6 +490,7 @@ + CONFIG_SPARSEMEM_VMEMMAP=y + CONFIG_HAVE_MEMBLOCK=y + CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y + # CONFIG_HAVE_BOOTMEM_INFO_NODE is not set + CONFIG_SPLIT_PTLOCK_CPUS=4 + CONFIG_MEMORY_BALLOON=y +@@ -502,12 +503,15 @@ + CONFIG_KSM=y + CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 + CONFIG_TRANSPARENT_HUGEPAGE=y +-# CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS is not set +-CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y ++CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y ++# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set + CONFIG_TRANSPARENT_HUGE_PAGECACHE=y + # CONFIG_CLEANCACHE is not set + CONFIG_FRONTSWAP=y +-# CONFIG_CMA is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++# CONFIG_CMA_DEBUGFS is not set ++CONFIG_CMA_AREAS=7 + CONFIG_ZSWAP=y + CONFIG_ZPOOL=y + CONFIG_ZBUD=y +@@ -1247,7 +1251,7 @@ + CONFIG_NET_ACT_GACT=m + CONFIG_GACT_PROB=y + CONFIG_NET_ACT_MIRRED=m +-# CONFIG_NET_ACT_SAMPLE is not set ++CONFIG_NET_ACT_SAMPLE=m + CONFIG_NET_ACT_IPT=m + CONFIG_NET_ACT_NAT=m + CONFIG_NET_ACT_PEDIT=m +@@ -1290,7 +1294,7 @@ + # CONFIG_HSR is not set + # CONFIG_NET_SWITCHDEV is not set + CONFIG_NET_L3_MASTER_DEV=y +-# CONFIG_CGROUP_L3MDEV is not set ++CONFIG_CGROUP_L3MDEV=y + # CONFIG_QRTR is not set + # CONFIG_NET_NCSI is not set + CONFIG_RPS=y +@@ -1474,7 +1478,7 @@ + CONFIG_NFC_PN533_USB=m + # CONFIG_NFC_PN533_I2C is not set + # CONFIG_NFC_ST95HF is not set +-# CONFIG_PSAMPLE is not set ++CONFIG_PSAMPLE=y + CONFIG_LWTUNNEL=y + CONFIG_DST_CACHE=y + CONFIG_NET_DEVLINK=m +@@ -1516,6 +1520,17 @@ + CONFIG_REGMAP_IRQ=y + CONFIG_DMA_SHARED_BUFFER=y + # CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 + + # + # Bus devices +@@ -1528,20 +1543,19 @@ + CONFIG_VEXPRESS_CONFIG=y + CONFIG_CONNECTOR=y + CONFIG_PROC_EVENTS=y +-CONFIG_MTD=m ++CONFIG_MTD=y + # CONFIG_MTD_TESTS is not set + # CONFIG_MTD_REDBOOT_PARTS is not set + # CONFIG_MTD_CMDLINE_PARTS is not set + # CONFIG_MTD_AFS_PARTS is not set +-CONFIG_MTD_OF_PARTS=m +-CONFIG_MTD_AR7_PARTS=m ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set + + # + # User Modules And Translation Layers + # +-CONFIG_MTD_BLKDEVS=m +-CONFIG_MTD_BLOCK=m +-CONFIG_MTD_BLOCK_RO=m ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y + # CONFIG_FTL is not set + # CONFIG_NFTL is not set + # CONFIG_INFTL is not set +@@ -1598,7 +1612,30 @@ + # Disk-On-Chip Device Drivers + # + # CONFIG_MTD_DOCG3 is not set +-# CONFIG_MTD_NAND is not set ++CONFIG_MTD_NAND_ECC=y ++CONFIG_MTD_NAND_ECC_SMC=y ++CONFIG_MTD_NAND=y ++CONFIG_MTD_NAND_BCH=y ++CONFIG_MTD_NAND_ECC_BCH=y ++CONFIG_MTD_SM_COMMON=y ++# CONFIG_MTD_NAND_DENALI_PCI is not set ++# CONFIG_MTD_NAND_DENALI_DT is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++CONFIG_MTD_NAND_RICOH=y ++CONFIG_MTD_NAND_DISKONCHIP=y ++CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED=y ++CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0x0 ++# CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH is not set ++# CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_CAFE is not set ++CONFIG_MTD_NAND_MARVELL=y ++# CONFIG_MTD_NAND_NANDSIM is not set ++CONFIG_MTD_NAND_BRCMNAND=y ++CONFIG_MTD_NAND_PLATFORM=y ++CONFIG_MTD_NAND_HISI504=y ++# CONFIG_MTD_NAND_QCOM is not set + CONFIG_MTD_ONENAND=m + CONFIG_MTD_ONENAND_VERIFY_WRITE=y + # CONFIG_MTD_ONENAND_GENERIC is not set +@@ -1629,7 +1666,7 @@ + CONFIG_OF_ADDRESS_PCI=y + CONFIG_OF_IRQ=y + CONFIG_OF_NET=y +-CONFIG_OF_MDIO=m ++CONFIG_OF_MDIO=y + CONFIG_OF_PCI=y + CONFIG_OF_PCI_IRQ=y + CONFIG_OF_RESERVED_MEM=y +@@ -1693,7 +1730,7 @@ + # CONFIG_DUMMY_IRQ is not set + # CONFIG_PHANTOM is not set + CONFIG_SGI_IOC4=m +-CONFIG_TIFM_CORE=m ++CONFIG_TIFM_CORE=y + CONFIG_TIFM_7XX1=m + CONFIG_ICS932S401=m + CONFIG_ENCLOSURE_SERVICES=m +@@ -1724,9 +1761,9 @@ + CONFIG_EEPROM_MAX6875=m + CONFIG_EEPROM_93CX6=m + # CONFIG_EEPROM_93XX46 is not set +-# CONFIG_EEPROM_SFF_8436 is not set +-# CONFIG_EEPROM_OPTOE is not set +-CONFIG_CB710_CORE=m ++CONFIG_EEPROM_SFF_8436=y ++CONFIG_EEPROM_OPTOE=y ++CONFIG_CB710_CORE=y + # CONFIG_CB710_DEBUG is not set + CONFIG_CB710_DEBUG_ASSUMPTIONS=y + +@@ -2245,8 +2282,10 @@ + CONFIG_NET_VENDOR_I825XX=y + CONFIG_JME=m + CONFIG_NET_VENDOR_MARVELL=y +-# CONFIG_MVMDIO is not set ++CONFIG_MVMDIO=y + # CONFIG_MVNETA_BM is not set ++CONFIG_MVPP2=y ++CONFIG_MVPP2X=y + CONFIG_SKGE=m + # CONFIG_SKGE_DEBUG is not set + CONFIG_SKGE_GENESIS=y +@@ -2365,7 +2404,7 @@ + CONFIG_SKFP=m + # CONFIG_HIPPI is not set + # CONFIG_NET_SB1000 is not set +-CONFIG_PHYLIB=m ++CONFIG_PHYLIB=y + CONFIG_SWPHY=y + + # +@@ -2395,12 +2434,12 @@ + CONFIG_DAVICOM_PHY=m + CONFIG_DP83848_PHY=m + CONFIG_DP83867_PHY=m +-CONFIG_FIXED_PHY=m ++CONFIG_FIXED_PHY=y + CONFIG_ICPLUS_PHY=m + # CONFIG_INTEL_XWAY_PHY is not set + CONFIG_LSI_ET1011C_PHY=m + CONFIG_LXT_PHY=m +-CONFIG_MARVELL_PHY=m ++CONFIG_MARVELL_PHY=y + CONFIG_MICREL_PHY=m + CONFIG_MICROCHIP_PHY=m + CONFIG_MICROSEMI_PHY=m +@@ -3004,7 +3043,7 @@ + # CONFIG_I2C_MUX_PINCTRL is not set + # CONFIG_I2C_MUX_REG is not set + # CONFIG_I2C_DEMUX_PINCTRL is not set +-# CONFIG_I2C_MUX_MLXCPLD is not set ++CONFIG_I2C_MUX_MLXCPLD=m + CONFIG_I2C_HELPER_AUTO=y + CONFIG_I2C_SMBUS=m + CONFIG_I2C_ALGOBIT=m +@@ -3392,7 +3431,7 @@ + CONFIG_SENSORS_MAX6639=m + CONFIG_SENSORS_MAX6642=m + CONFIG_SENSORS_MAX6650=m +-# CONFIG_SENSORS_MAX6620 is not set ++CONFIG_SENSORS_MAX6620=y + # CONFIG_SENSORS_MAX6697 is not set + # CONFIG_SENSORS_MAX31790 is not set + # CONFIG_SENSORS_MCP3021 is not set +@@ -5098,6 +5137,7 @@ + # CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set + CONFIG_USB_CHIPIDEA=m + CONFIG_USB_CHIPIDEA_OF=m ++CONFIG_USB_CHIPIDEA_PCI=m + CONFIG_USB_CHIPIDEA_UDC=y + CONFIG_USB_CHIPIDEA_HOST=y + # CONFIG_USB_ISP1760 is not set +@@ -5197,7 +5237,7 @@ + # USB Physical Layer drivers + # + CONFIG_USB_PHY=y +-# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_NOP_USB_XCEIV=m + # CONFIG_USB_GPIO_VBUS is not set + # CONFIG_USB_ISP1301 is not set + CONFIG_USB_MSM_OTG=m +@@ -5317,29 +5357,29 @@ + # + # MMC/SD/SDIO Host Controller Drivers + # +-CONFIG_MMC_ARMMMCI=m ++CONFIG_MMC_ARMMMCI=y + CONFIG_MMC_QCOM_DML=m +-CONFIG_MMC_SDHCI=m ++CONFIG_MMC_SDHCI=y + CONFIG_MMC_SDHCI_IO_ACCESSORS=y +-CONFIG_MMC_SDHCI_PCI=m ++CONFIG_MMC_SDHCI_PCI=y + CONFIG_MMC_RICOH_MMC=y +-# CONFIG_MMC_SDHCI_ACPI is not set +-CONFIG_MMC_SDHCI_PLTFM=m ++CONFIG_MMC_SDHCI_ACPI=y ++CONFIG_MMC_SDHCI_PLTFM=y + # CONFIG_MMC_SDHCI_OF_ARASAN is not set + # CONFIG_MMC_SDHCI_OF_AT91 is not set +-CONFIG_MMC_SDHCI_TEGRA=m ++CONFIG_MMC_SDHCI_TEGRA=y + # CONFIG_MMC_SDHCI_PXAV3 is not set + # CONFIG_MMC_SDHCI_F_SDH30 is not set +-CONFIG_MMC_SDHCI_IPROC=m +-CONFIG_MMC_SDHCI_MSM=m +-CONFIG_MMC_TIFM_SD=m +-CONFIG_MMC_SPI=m +-CONFIG_MMC_CB710=m +-CONFIG_MMC_VIA_SDMMC=m +-CONFIG_MMC_DW=m +-CONFIG_MMC_DW_PLTFM=m +-# CONFIG_MMC_DW_EXYNOS is not set +-CONFIG_MMC_DW_K3=m ++CONFIG_MMC_SDHCI_IPROC=y ++CONFIG_MMC_SDHCI_MSM=y ++CONFIG_MMC_TIFM_SD=y ++CONFIG_MMC_SPI=y ++CONFIG_MMC_CB710=y ++CONFIG_MMC_VIA_SDMMC=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_PLTFM=y ++CONFIG_MMC_DW_EXYNOS=y ++CONFIG_MMC_DW_K3=y + # CONFIG_MMC_DW_PCI is not set + CONFIG_MMC_VUB300=m + CONFIG_MMC_USHC=m +@@ -5348,6 +5388,7 @@ + CONFIG_MMC_REALTEK_USB=m + CONFIG_MMC_TOSHIBA_PCI=m + # CONFIG_MMC_MTK is not set ++CONFIG_MMC_SDHCI_XENON=y + CONFIG_MEMSTICK=m + # CONFIG_MEMSTICK_DEBUG is not set + +@@ -5404,8 +5445,8 @@ + # + # CONFIG_LEDS_BLINKM is not set + # CONFIG_LEDS_SYSCON is not set +-# CONFIG_LEDS_MLXREG is not set +-# CONFIG_LEDS_USER is not set ++CONFIG_LEDS_MLXREG=y ++CONFIG_LEDS_USER=y + + # + # LED Triggers +@@ -5567,7 +5608,7 @@ + # CONFIG_RTC_DRV_PL030 is not set + CONFIG_RTC_DRV_PL031=y + # CONFIG_RTC_DRV_MV is not set +-# CONFIG_RTC_DRV_ARMADA38X is not set ++CONFIG_RTC_DRV_ARMADA38X=y + CONFIG_RTC_DRV_PM8XXX=m + CONFIG_RTC_DRV_TEGRA=y + # CONFIG_RTC_DRV_SNVS is not set +@@ -5595,7 +5636,7 @@ + CONFIG_K3_DMA=m + CONFIG_MV_XOR=y + CONFIG_MV_XOR_V2=y +-# CONFIG_PL330_DMA is not set ++CONFIG_PL330_DMA=y + CONFIG_TEGRA20_APB_DMA=y + CONFIG_TEGRA210_ADMA=y + CONFIG_XGENE_DMA=m +@@ -5782,6 +5823,8 @@ + # CONFIG_STAGING_BOARD is not set + # CONFIG_LTE_GDM724X is not set + # CONFIG_FIREWIRE_SERIAL is not set ++CONFIG_MTD_SPINAND_MT29F=y ++CONFIG_MTD_SPINAND_ONDIEECC=y + # CONFIG_LNET is not set + # CONFIG_DGNC is not set + # CONFIG_GS_FPGABOOT is not set +@@ -6266,6 +6309,8 @@ + CONFIG_ARM_GIC_V3=y + CONFIG_ARM_GIC_V3_ITS=y + CONFIG_HISILICON_IRQ_MBIGEN=y ++CONFIG_MVEBU_GICP=y ++CONFIG_MVEBU_ICU=y + CONFIG_MVEBU_ODMI=y + CONFIG_MVEBU_PIC=y + CONFIG_PARTITION_PERCPU=y +@@ -6317,7 +6362,7 @@ + # CONFIG_LIBNVDIMM is not set + CONFIG_DEV_DAX=m + CONFIG_NR_DEV_DAX=32768 +-CONFIG_NVMEM=m ++CONFIG_NVMEM=y + CONFIG_QCOM_QFPROM=m + # CONFIG_MESON_EFUSE is not set + # CONFIG_STM is not set +@@ -6390,17 +6435,19 @@ + CONFIG_DCACHE_WORD_ACCESS=y + CONFIG_FS_IOMAP=y + # CONFIG_EXT2_FS is not set +-# CONFIG_EXT3_FS is not set +-CONFIG_EXT4_FS=m ++CONFIG_EXT3_FS=y ++# CONFIG_EXT3_FS_POSIX_ACL is not set ++# CONFIG_EXT3_FS_SECURITY is not set ++CONFIG_EXT4_FS=y + CONFIG_EXT4_USE_FOR_EXT2=y + CONFIG_EXT4_FS_POSIX_ACL=y + CONFIG_EXT4_FS_SECURITY=y + CONFIG_EXT4_ENCRYPTION=y + CONFIG_EXT4_FS_ENCRYPTION=y + # CONFIG_EXT4_DEBUG is not set +-CONFIG_JBD2=m ++CONFIG_JBD2=y + # CONFIG_JBD2_DEBUG is not set +-CONFIG_FS_MBCACHE=m ++CONFIG_FS_MBCACHE=y + CONFIG_REISERFS_FS=m + # CONFIG_REISERFS_CHECK is not set + # CONFIG_REISERFS_PROC_INFO is not set +@@ -6448,7 +6495,7 @@ + CONFIG_EXPORTFS_BLOCK_OPS=y + CONFIG_FILE_LOCKING=y + CONFIG_MANDATORY_FILE_LOCKING=y +-CONFIG_FS_ENCRYPTION=m ++CONFIG_FS_ENCRYPTION=y + CONFIG_FSNOTIFY=y + CONFIG_DNOTIFY=y + CONFIG_INOTIFY_USER=y +@@ -6557,7 +6604,7 @@ + # CONFIG_UBIFS_ATIME_SUPPORT is not set + # CONFIG_LOGFS is not set + # CONFIG_CRAMFS is not set +-CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS=y + CONFIG_SQUASHFS_FILE_CACHE=y + # CONFIG_SQUASHFS_FILE_DIRECT is not set + CONFIG_SQUASHFS_DECOMP_SINGLE=y +@@ -6936,7 +6983,7 @@ + # CONFIG_TEST_UUID is not set + # CONFIG_TEST_RHASHTABLE is not set + # CONFIG_TEST_HASH is not set +-# CONFIG_DMA_API_DEBUG is not set ++CONFIG_DMA_API_DEBUG=y + # CONFIG_TEST_LKM is not set + CONFIG_TEST_USER_COPY=m + CONFIG_TEST_BPF=m +@@ -6967,7 +7014,7 @@ + # CONFIG_PERSISTENT_KEYRINGS is not set + # CONFIG_BIG_KEYS is not set + # CONFIG_TRUSTED_KEYS is not set +-CONFIG_ENCRYPTED_KEYS=m ++CONFIG_ENCRYPTED_KEYS=y + # CONFIG_KEY_DH_OPERATIONS is not set + CONFIG_SECURITY_DMESG_RESTRICT=y + CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +@@ -7038,15 +7085,15 @@ + # + CONFIG_CRYPTO_ALGAPI=y + CONFIG_CRYPTO_ALGAPI2=y +-CONFIG_CRYPTO_AEAD=m ++CONFIG_CRYPTO_AEAD=y + CONFIG_CRYPTO_AEAD2=y +-CONFIG_CRYPTO_BLKCIPHER=m ++CONFIG_CRYPTO_BLKCIPHER=y + CONFIG_CRYPTO_BLKCIPHER2=y + CONFIG_CRYPTO_HASH=y + CONFIG_CRYPTO_HASH2=y +-CONFIG_CRYPTO_RNG=m ++CONFIG_CRYPTO_RNG=y + CONFIG_CRYPTO_RNG2=y +-CONFIG_CRYPTO_RNG_DEFAULT=m ++CONFIG_CRYPTO_RNG_DEFAULT=y + CONFIG_CRYPTO_AKCIPHER2=y + CONFIG_CRYPTO_KPP2=y + # CONFIG_CRYPTO_RSA is not set +@@ -7056,8 +7103,8 @@ + CONFIG_CRYPTO_MANAGER2=y + # CONFIG_CRYPTO_USER is not set + # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +-CONFIG_CRYPTO_GF128MUL=m +-CONFIG_CRYPTO_NULL=m ++CONFIG_CRYPTO_GF128MUL=y ++CONFIG_CRYPTO_NULL=y + CONFIG_CRYPTO_NULL2=y + CONFIG_CRYPTO_PCRYPT=m + CONFIG_CRYPTO_WORKQUEUE=y +@@ -7073,19 +7120,19 @@ + CONFIG_CRYPTO_CCM=m + CONFIG_CRYPTO_GCM=m + CONFIG_CRYPTO_CHACHA20POLY1305=m +-CONFIG_CRYPTO_SEQIV=m ++CONFIG_CRYPTO_SEQIV=y + CONFIG_CRYPTO_ECHAINIV=m + + # + # Block modes + # +-CONFIG_CRYPTO_CBC=m +-CONFIG_CRYPTO_CTR=m +-CONFIG_CRYPTO_CTS=m +-CONFIG_CRYPTO_ECB=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTR=y ++CONFIG_CRYPTO_CTS=y ++CONFIG_CRYPTO_ECB=y + CONFIG_CRYPTO_LRW=m + CONFIG_CRYPTO_PCBC=m +-CONFIG_CRYPTO_XTS=m ++CONFIG_CRYPTO_XTS=y + # CONFIG_CRYPTO_KEYWRAP is not set + + # +@@ -7099,7 +7146,7 @@ + # + # Digest + # +-CONFIG_CRYPTO_CRC32C=m ++CONFIG_CRYPTO_CRC32C=y + CONFIG_CRYPTO_CRC32=m + CONFIG_CRYPTO_CRCT10DIF=y + CONFIG_CRYPTO_GHASH=m +@@ -7154,12 +7201,12 @@ + # Random Number Generation + # + CONFIG_CRYPTO_ANSI_CPRNG=m +-CONFIG_CRYPTO_DRBG_MENU=m ++CONFIG_CRYPTO_DRBG_MENU=y + CONFIG_CRYPTO_DRBG_HMAC=y + # CONFIG_CRYPTO_DRBG_HASH is not set + # CONFIG_CRYPTO_DRBG_CTR is not set +-CONFIG_CRYPTO_DRBG=m +-CONFIG_CRYPTO_JITTERENTROPY=m ++CONFIG_CRYPTO_DRBG=y ++CONFIG_CRYPTO_JITTERENTROPY=y + CONFIG_CRYPTO_USER_API=m + CONFIG_CRYPTO_USER_API_HASH=m + CONFIG_CRYPTO_USER_API_SKCIPHER=m +@@ -7201,17 +7248,17 @@ + CONFIG_GENERIC_IO=y + CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y + CONFIG_CRC_CCITT=m +-CONFIG_CRC16=m ++CONFIG_CRC16=y + CONFIG_CRC_T10DIF=y +-CONFIG_CRC_ITU_T=m ++CONFIG_CRC_ITU_T=y + CONFIG_CRC32=y + # CONFIG_CRC32_SELFTEST is not set + CONFIG_CRC32_SLICEBY8=y + # CONFIG_CRC32_SLICEBY4 is not set + # CONFIG_CRC32_SARWATE is not set + # CONFIG_CRC32_BIT is not set +-CONFIG_CRC7=m +-CONFIG_LIBCRC32C=m ++CONFIG_CRC7=y ++CONFIG_LIBCRC32C=y + # CONFIG_CRC8 is not set + CONFIG_AUDIT_GENERIC=y + CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +@@ -7240,9 +7287,11 @@ + CONFIG_DECOMPRESS_LZO=y + CONFIG_DECOMPRESS_LZ4=y + CONFIG_GENERIC_ALLOCATOR=y +-CONFIG_REED_SOLOMON=m ++CONFIG_REED_SOLOMON=y + CONFIG_REED_SOLOMON_ENC8=y + CONFIG_REED_SOLOMON_DEC8=y ++CONFIG_REED_SOLOMON_DEC16=y ++CONFIG_BCH=y + CONFIG_TEXTSEARCH=y + CONFIG_TEXTSEARCH_KMP=m + CONFIG_TEXTSEARCH_BM=m +-- +2.4.0 + diff --git a/patch/0042-armhf-additional-configs.patch b/patch/0042-armhf-additional-configs.patch new file mode 100644 index 000000000..d9bbcc89f --- /dev/null +++ b/patch/0042-armhf-additional-configs.patch @@ -0,0 +1,89 @@ +From a961464da6c88aee4d94af4038c748a1e8a1f412 Mon Sep 17 00:00:00 2001 +From: Antony Rheneus +Date: Thu, 17 Oct 2019 18:57:48 +0530 +Subject: [PATCH] armhf mandatory configs for boot + +--- + debian/build/build_armhf_none_armmp/.config | 53 ++++++++++++++++------------- + 1 file changed, 30 insertions(+), 23 deletions(-) + +diff --git a/debian/build/build_armhf_none_armmp/.config b/debian/build/build_armhf_none_armmp/.config +index e997934..69288eb 100644 +--- a/debian/build/build_armhf_none_armmp/.config ++++ b/debian/build/build_armhf_none_armmp/.config +@@ -1793,16 +1793,16 @@ CONFIG_PROC_EVENTS=y + CONFIG_MTD=y + # CONFIG_MTD_TESTS is not set + # CONFIG_MTD_REDBOOT_PARTS is not set +-# CONFIG_MTD_CMDLINE_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y + # CONFIG_MTD_AFS_PARTS is not set +-CONFIG_MTD_OF_PARTS=m +-CONFIG_MTD_AR7_PARTS=m ++CONFIG_MTD_OF_PARTS=y ++CONFIG_MTD_AR7_PARTS=y + + # + # User Modules And Translation Layers + # +-CONFIG_MTD_BLKDEVS=m +-CONFIG_MTD_BLOCK=m ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y + CONFIG_MTD_BLOCK_RO=m + # CONFIG_FTL is not set + # CONFIG_NFTL is not set +@@ -1817,8 +1817,10 @@ CONFIG_MTD_SWAP=m + # + # RAM/ROM/Flash chip drivers + # +-# CONFIG_MTD_CFI is not set ++CONFIG_MTD_CFI=y + # CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_GEN_PROBE=y ++# CONFIG_MTD_CFI_ADV_OPTIONS is not set + CONFIG_MTD_MAP_BANK_WIDTH_1=y + CONFIG_MTD_MAP_BANK_WIDTH_2=y + CONFIG_MTD_MAP_BANK_WIDTH_4=y +@@ -1829,6 +1831,10 @@ CONFIG_MTD_CFI_I1=y + CONFIG_MTD_CFI_I2=y + # CONFIG_MTD_CFI_I4 is not set + # CONFIG_MTD_CFI_I8 is not set ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_CFI_STAA=y ++CONFIG_MTD_CFI_UTIL=y + CONFIG_MTD_RAM=m + # CONFIG_MTD_ROM is not set + # CONFIG_MTD_ABSENT is not set +@@ -1850,8 +1856,8 @@ CONFIG_MTD_PLATRAM=m + CONFIG_MTD_DATAFLASH=m + # CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set + # CONFIG_MTD_DATAFLASH_OTP is not set +-CONFIG_MTD_M25P80=m +-CONFIG_MTD_SST25L=m ++CONFIG_MTD_M25P80=y ++CONFIG_MTD_SST25L=y + # CONFIG_MTD_SLRAM is not set + # CONFIG_MTD_PHRAM is not set + # CONFIG_MTD_MTDRAM is not set +@@ -1878,7 +1884,7 @@ CONFIG_MTD_NAND_RICOH=m + # CONFIG_MTD_NAND_DISKONCHIP is not set + # CONFIG_MTD_NAND_DOCG4 is not set + CONFIG_MTD_NAND_CAFE=m +-CONFIG_MTD_NAND_PXA3xx=m ++CONFIG_MTD_NAND_PXA3xx=y + CONFIG_MTD_NAND_NANDSIM=m + CONFIG_MTD_NAND_GPMI_NAND=m + # CONFIG_MTD_NAND_BRCMNAND is not set +@@ -3824,7 +3830,7 @@ CONFIG_SENSORS_MAX1668=m + CONFIG_SENSORS_MAX6639=m + CONFIG_SENSORS_MAX6642=m + CONFIG_SENSORS_MAX6650=m +-# CONFIG_SENSORS_MAX6620 is not set ++CONFIG_SENSORS_MAX6620=m + # CONFIG_SENSORS_MAX6697 is not set + # CONFIG_SENSORS_MAX31790 is not set + # CONFIG_SENSORS_MCP3021 is not set +2.7.4 + diff --git a/patch/0042-armhf-proc-dma-kconfig.patch b/patch/0042-armhf-proc-dma-kconfig.patch new file mode 100644 index 000000000..6144cf7d2 --- /dev/null +++ b/patch/0042-armhf-proc-dma-kconfig.patch @@ -0,0 +1,37 @@ +From b8d69b46ddc0f78d2965e426da48082202d030a3 Mon Nov 19 00:00:00 2001 +From: Antony Rheneus +Date: Tue, 19 Nov 2019 15:45:07 +0530 +Subject: [PATCH] armhf proc dma Kconfig + +--- + arch/arm/Kconfig | 2 +- + arch/arm64/Kconfig | 3 +++ + 2 files changed, 4 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 32a2698..d50fff8 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -219,7 +219,7 @@ + bool + + config GENERIC_ISA_DMA +- bool ++ def_bool y + + config FIQ + bool +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 32a2698..d50fff8 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -205,6 +205,9 @@ + config HAVE_GENERIC_RCU_GUP + def_bool y + ++config GENERIC_ISA_DMA ++ def_bool y ++ + config ARCH_DMA_ADDR_T_64BIT + def_bool y + diff --git a/patch/series b/patch/series index a213a167e..2a5e8e453 100755 --- a/patch/series +++ b/patch/series @@ -1,4 +1,7 @@ # This series applies on GIT commit 18c5597832fcf6988111b05a9a1607ae148723c +0001-armhf-default-config-for-the-sonic-patches.patch +0001-arm64-default-config-for-sonic-patches.patch +0001-Marvell-support-for-armada7020-on-LK4.9.168.patch 0001-i2c-mlxcpld-add-master-driver-for-Mellanox-systems.patch 0002-i2c-mux-mlxcpld-add-driver-for-Mellanox-systems.patch 0003-platform-mellanox-Introduce-Mellanox-hardware-platfo.patch @@ -92,6 +95,12 @@ linux-4.16-firmware-dmi-handle-missing-DMI-data-gracefully.patch mellanox-backport-introduce-psample-a-new-genetlink-channel.patch mellanox-backport-introduce-tc-sample-action.patch kernel-enable-psample-and-act_sample-drivers.patch +0042-Marvell-a385-Micron-4G-flash-support.patch +0042-armhf-additional-configs.patch +0042-armhf-proc-dma-kconfig.patch +0042-ARM64-PCI-Allow-userspace-to-mmap-PCI-resources.patch +0042-add-a7020-comexp-dts.patch +0042-arm64-additional-configs.patch 0000-net-Fix-netdev-adjacency-tracking.patch 0000-net-ipv6-ll-anycast-mcast-routes-on-dev.patch 0001-net-ipv6-Allow-shorthand-delete-of-all-nexthops-in-m.patch