diff options
286 files changed, 6895 insertions, 2801 deletions
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 986946613542..ec0a38ef3145 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -23,7 +23,7 @@ Description: lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] - base: func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK] + base: func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK][MODULE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value uid:= decimal value @@ -53,6 +53,7 @@ Description: measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC measure func=FILE_CHECK mask=MAY_READ uid=0 + measure func=MODULE_CHECK uid=0 appraise fowner=0 The default policy measures all executables in bprm_check, diff --git a/Documentation/devicetree/bindings/arm/davinci/nand.txt b/Documentation/devicetree/bindings/arm/davinci/nand.txt index 49fc7ada929a..3545ea704b50 100644 --- a/Documentation/devicetree/bindings/arm/davinci/nand.txt +++ b/Documentation/devicetree/bindings/arm/davinci/nand.txt @@ -23,6 +23,9 @@ Recommended properties : - ti,davinci-nand-buswidth: buswidth 8 or 16 - ti,davinci-nand-use-bbt: use flash based bad block table support. +nand device bindings may contain additional sub-nodes describing +partitions of the address space. See partition.txt for more detail. + Example(da850 EVM ): nand_cs3@62000000 { compatible = "ti,davinci-nand"; @@ -35,4 +38,9 @@ nand_cs3@62000000 { ti,davinci-ecc-mode = "hw"; ti,davinci-ecc-bits = <4>; ti,davinci-nand-use-bbt; + + partition@180000 { + label = "ubifs"; + reg = <0x180000 0x7e80000>; + }; }; diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt new file mode 100644 index 000000000000..b04d03a1d499 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt @@ -0,0 +1,23 @@ +* Denali NAND controller + +Required properties: + - compatible : should be "denali,denali-nand-dt" + - reg : should contain registers location and length for data and reg. + - reg-names: Should contain the reg names "nand_data" and "denali_reg" + - interrupts : The interrupt number. + - dm-mask : DMA bit mask + +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + +Examples: + +nand: nand@ff900000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "denali,denali-nand-dt"; + reg = <0xff900000 0x100000>, <0xffb80000 0x10000>; + reg-names = "nand_data", "denali_reg"; + interrupts = <0 144 4>; + dma-mask = <0xffffffff>; +}; diff --git a/Documentation/devicetree/bindings/mtd/flctl-nand.txt b/Documentation/devicetree/bindings/mtd/flctl-nand.txt new file mode 100644 index 000000000000..427f46dc60ad --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/flctl-nand.txt @@ -0,0 +1,49 @@ +FLCTL NAND controller + +Required properties: +- compatible : "renesas,shmobile-flctl-sh7372" +- reg : Address range of the FLCTL +- interrupts : flste IRQ number +- nand-bus-width : bus width to NAND chip + +Optional properties: +- dmas: DMA specifier(s) +- dma-names: name for each DMA specifier. Valid names are + "data_tx", "data_rx", "ecc_tx", "ecc_rx" + +The DMA fields are not used yet in the driver but are listed here for +completing the bindings. + +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + +Example: + + flctl@e6a30000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "renesas,shmobile-flctl-sh7372"; + reg = <0xe6a30000 0x100>; + interrupts = <0x0d80>; + + nand-bus-width = <16>; + + dmas = <&dmac 1 /* data_tx */ + &dmac 2;> /* data_rx */ + dma-names = "data_tx", "data_rx"; + + system@0 { + label = "system"; + reg = <0x0 0x8000000>; + }; + + userdata@8000000 { + label = "userdata"; + reg = <0x8000000 0x10000000>; + }; + + cache@18000000 { + label = "cache"; + reg = <0x18000000 0x8000000>; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt index e2c663b354d2..e3ea32e7de3e 100644 --- a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt @@ -3,9 +3,7 @@ Required properties: - compatible : "st,spear600-fsmc-nand" - reg : Address range of the mtd chip -- reg-names: Should contain the reg names "fsmc_regs" and "nand_data" -- st,ale-off : Chip specific offset to ALE -- st,cle-off : Chip specific offset to CLE +- reg-names: Should contain the reg names "fsmc_regs", "nand_data", "nand_addr" and "nand_cmd" Optional properties: - bank-width : Width (in bytes) of the device. If not present, the width @@ -19,10 +17,10 @@ Example: #address-cells = <1>; #size-cells = <1>; reg = <0xd1800000 0x1000 /* FSMC Register */ - 0xd2000000 0x4000>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0xd2000000 0x0010 /* NAND Base DATA */ + 0xd2020000 0x0010 /* NAND Base ADDR */ + 0xd2010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; bank-width = <1>; nand-skip-bbtscan; diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt new file mode 100644 index 000000000000..6d3d57609470 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt @@ -0,0 +1,29 @@ +* MTD SPI driver for ST M25Pxx (and similar) serial flash chips + +Required properties: +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. +- compatible : Should be the manufacturer and the name of the chip. Bear in mind + the DT binding is not Linux-only, but in case of Linux, see the + "m25p_ids" table in drivers/mtd/devices/m25p80.c for the list of + supported chips. +- reg : Chip-Select number +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: +- m25p,fast-read : Use the "fast read" opcode to read data from the chip instead + of the usual "read" opcode. This opcode is not supported by + all chips and support for it can not be detected at runtime. + Refer to your chips' datasheet to check if this is supported + by your chip. + +Example: + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,m25p80"; + reg = <0>; + spi-max-frequency = <40000000>; + m25p,fast-read; + }; diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index 94de19b8f16b..dab7847fc800 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -23,6 +23,9 @@ file systems on embedded devices. unaligned accesses as implemented in the JFFS2 code via memcpy(). By defining "no-unaligned-direct-access", the flash will not be exposed directly to the MTD users (e.g. JFFS2) any more. + - linux,mtd-name: allow to specify the mtd name for retro capability with + physmap-flash drivers as boot loader pass the mtd partition via the old + device name physmap-flash. For JEDEC compatible devices, the following additional properties are defined: diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt new file mode 100644 index 000000000000..131e8c11d26f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt @@ -0,0 +1,23 @@ +TI SOC ECAP based APWM controller + +Required properties: +- compatible: Must be "ti,am33xx-ecap" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the ECAP: + "ecap<x>", <x> being the 0-based instance number from the HW spec + +Example: + +ecap0: ecap@0 { + compatible = "ti,am33xx-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + ti,hwmods = "ecap0"; +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt new file mode 100644 index 000000000000..4fc7079d822e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt @@ -0,0 +1,23 @@ +TI SOC EHRPWM based PWM controller + +Required properties: +- compatible : Must be "ti,am33xx-ehrpwm" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the EHRPWM: + "ehrpwm<x>", <x> being the 0-based instance number from the HW spec + +Example: + +ehrpwm0: ehrpwm@0 { + compatible = "ti,am33xx-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + ti,hwmods = "ehrpwm0"; +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt new file mode 100644 index 000000000000..f7eae77f8354 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt @@ -0,0 +1,31 @@ +TI SOC based PWM Subsystem + +Required properties: +- compatible: Must be "ti,am33xx-pwmss"; +- reg: physical base address and size of the registers map. +- address-cells: Specify the number of u32 entries needed in child nodes. + Should set to 1. +- size-cells: specify number of u32 entries needed to specify child nodes size + in reg property. Should set to 1. +- ranges: describes the address mapping of a memory-mapped bus. Should set to + physical address map of child's base address, physical address within + parent's address space and length of the address map. For am33xx, + 3 set of child register maps present, ECAP register space, EQEP + register space, EHRPWM register space. + +Also child nodes should also populated under PWMSS DT node. + +Example: +pwmss0: pwmss@48300000 { + compatible = "ti,am33xx-pwmss"; + reg = <0x48300000 0x10>; + ti,hwmods = "epwmss0"; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + ranges = <0x48300100 0x48300100 0x80 /* ECAP */ + 0x48300180 0x48300180 0x80 /* EQEP */ + 0x48300200 0x48300200 0x80>; /* EHRPWM */ + + /* child nodes go here */ +}; diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt index 73ec962bfe8c..06e67247859a 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm.txt @@ -37,10 +37,21 @@ device: pwm-names = "backlight"; }; +Note that in the example above, specifying the "pwm-names" is redundant +because the name "backlight" would be used as fallback anyway. + pwm-specifier typically encodes the chip-relative PWM number and the PWM -period in nanoseconds. Note that in the example above, specifying the -"pwm-names" is redundant because the name "backlight" would be used as -fallback anyway. +period in nanoseconds. + +Optionally, the pwm-specifier can encode a number of flags in a third cell: +- bit 0: PWM signal polarity (0: normal polarity, 1: inverse polarity) + +Example with optional PWM specifier for inverse polarity + + bl: backlight { + pwms = <&pwm 0 5000000 1>; + pwm-names = "backlight"; + }; 2) PWM controller nodes ----------------------- diff --git a/Documentation/devicetree/bindings/pwm/spear-pwm.txt b/Documentation/devicetree/bindings/pwm/spear-pwm.txt new file mode 100644 index 000000000000..3ac779d83386 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/spear-pwm.txt @@ -0,0 +1,18 @@ +== ST SPEAr SoC PWM controller == + +Required properties: +- compatible: should be one of: + - "st,spear320-pwm" + - "st,spear1340-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: number of cells used to specify PWM which is fixed to 2 on + SPEAr. The first cell specifies the per-chip index of the PWM to use and + the second cell is the period in nanoseconds. + +Example: + + pwm: pwm@a8000000 { + compatible ="st,spear320-pwm"; + reg = <0xa8000000 0x1000>; + #pwm-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt new file mode 100644 index 000000000000..2943ee5fce00 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers + +Supported PWMs: +On TWL4030 series: PWM1 and PWM2 +On TWL6030 series: PWM0 and PWM1 + +Required properties: +- compatible: "ti,twl4030-pwm" or "ti,twl6030-pwm" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwm: pwm { + compatible = "ti,twl6030-pwm"; + #pwm-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt new file mode 100644 index 000000000000..cb64f3acc10f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers connected to LED terminals + +Supported PWMs: +On TWL4030 series: PWMA and PWMB (connected to LEDA and LEDB terminals) +On TWL6030 series: LED PWM (mainly used as charging indicator LED) + +Required properties: +- compatible: "ti,twl4030-pwmled" or "ti,twl6030-pwmled" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwmled: pwmled { + compatible = "ti,twl6030-pwmled"; + #pwm-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt new file mode 100644 index 000000000000..bcc63678a9a5 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -0,0 +1,17 @@ +VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller + +Required properties: +- compatible: should be "via,vt8500-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. +- clocks: phandle to the PWM source clock + +Example: + +pwm1: pwm@d8220000 { + #pwm-cells = <2>; + compatible = "via,vt8500-pwm"; + reg = <0xd8220000 0x1000>; + clocks = <&clkpwm>; +}; diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt index 8cf24f6f0a99..7b53da5cb75b 100644 --- a/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt @@ -13,7 +13,7 @@ Recommended properties: Example: -spi@7000d600 { +spi@7000c380 { compatible = "nvidia,tegra20-sflash"; reg = <0x7000c380 0x80>; interrupts = <0 39 0x04>; diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt index f5b1ad1a1ec3..eefe15e3d95e 100644 --- a/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt @@ -13,7 +13,7 @@ Recommended properties: Example: -slink@7000d600 { +spi@7000d600 { compatible = "nvidia,tegra20-slink"; reg = <0x7000d600 0x200>; interrupts = <0 82 0x04>; diff --git a/Documentation/devicetree/bindings/spi/spi_atmel.txt b/Documentation/devicetree/bindings/spi/spi_atmel.txt new file mode 100644 index 000000000000..07e04cdc0c9e --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi_atmel.txt @@ -0,0 +1,26 @@ +Atmel SPI device + +Required properties: +- compatible : should be "atmel,at91rm9200-spi". +- reg: Address and length of the register set for the device +- interrupts: Should contain spi interrupt +- cs-gpios: chipselects + +Example: + +spi1: spi@fffcc000 { + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffcc000 0x4000>; + interrupts = <13 4 5>; + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&pioB 3 0>; + status = "okay"; + + mmc-slot@0 { + compatible = "mmc-spi-slot"; + reg = <0>; + gpios = <&pioC 4 0>; /* CD */ + spi-max-frequency = <25000000>; + }; +}; diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index f15cb74c4f78..406d82d5d2bb 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -373,7 +373,7 @@ Protocol: 2.00+ 1 Loadlin 2 bootsect-loader (0x20, all other values reserved) 3 Syslinux - 4 Etherboot/gPXE + 4 Etherboot/gPXE/iPXE 5 ELILO 7 GRUB 8 U-Boot @@ -381,6 +381,7 @@ Protocol: 2.00+ A Gujin B Qemu C Arcturus Networks uCbootloader + D kexec-tools E Extended (see ext_loader_type) F Special (0xFF = undefined) 10 Reserved diff --git a/Documentation/xtensa/atomctl.txt b/Documentation/xtensa/atomctl.txt new file mode 100644 index 000000000000..10a8d1ff35ec --- /dev/null +++ b/Documentation/xtensa/atomctl.txt @@ -0,0 +1,44 @@ +We Have Atomic Operation Control (ATOMCTL) Register. +This register determines the effect of using a S32C1I instruction +with various combinations of: + + 1. With and without an Coherent Cache Controller which + can do Atomic Transactions to the memory internally. + + 2. With and without An Intelligent Memory Controller which + can do Atomic Transactions itself. + +The Core comes up with a default value of for the three types of cache ops: + + 0x28: (WB: Internal, WT: Internal, BY:Exception) + +On the FPGA Cards we typically simulate an Intelligent Memory controller +which can implement RCW transactions. For FPGA cards with an External +Memory controller we let it to the atomic operations internally while +doing a Cached (WB) transaction and use the Memory RCW for un-cached +operations. + +For systems without an coherent cache controller, non-MX, we always +use the memory controllers RCW, thought non-MX controlers likely +support the Internal Operation. + +CUSTOMER-WARNING: + Virtually all customers buy their memory controllers from vendors that + don't support atomic RCW memory transactions and will likely want to + configure this register to not use RCW. + +Developers might find using RCW in Bypass mode convenient when testing +with the cache being bypassed; for example studying cache alias problems. + +See Section 4.3.12.4 of ISA; Bits: + + WB WT BY + 5 4 | 3 2 | 1 0 + 2 Bit + Field + Values WB - Write Back WT - Write Thru BY - Bypass +--------- --------------- ----------------- ---------------- + 0 Exception Exception Exception + 1 RCW Transaction RCW Transaction RCW Transaction + 2 Internal Operation Exception Reserved + 3 Reserved Reserved Reserved @@ -981,6 +981,12 @@ _modinst_post: _modinst_ $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modinst $(call cmd,depmod) +ifeq ($(CONFIG_MODULE_SIG), y) +PHONY += modules_sign +modules_sign: + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modsign +endif + else # CONFIG_MODULES # Modules not configured diff --git a/arch/Kconfig b/arch/Kconfig index 54ffd0f9df21..8e9e3246b2b4 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -113,6 +113,25 @@ config HAVE_EFFICIENT_UNALIGNED_ACCESS See Documentation/unaligned-memory-access.txt for more information on the topic of unaligned memory accesses. +config ARCH_USE_BUILTIN_BSWAP + bool + help + Modern versions of GCC (since 4.4) have builtin functions + for handling byte-swapping. Using these, instead of the old + inline assembler that the architecture code provides in the + __arch_bswapXX() macros, allows the compiler to see what's + happening and offers more opportunity for optimisation. In + particular, the compiler will be able to combine the byteswap + with a nearby load or store and use load-and-swap or + store-and-swap instructions if the architecture has them. It + should almost *never* result in code which is worse than the + hand-coded assembler in <asm/swab.h>. But just in case it + does, the use of the builtins is optional. + + Any architecture with load-and-swap or store-and-swap + instructions should set this. And it shouldn't hurt to set it + on architectures that don't have such instructions. + config HAVE_SYSCALL_WRAPPERS bool diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index 009096d1d2c3..b4ca60f4eb42 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -73,7 +73,7 @@ 400000 500000 600000 >; - status = "disable"; + status = "disabled"; }; ahb { @@ -118,15 +118,15 @@ compatible = "st,spear600-fsmc-nand"; #address-cells = <1>; #size-cells = <1>; - reg = <0xb0000000 0x1000 /* FSMC Register */ - 0xb0800000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; + reg = <0xb0000000 0x1000 /* FSMC Register*/ + 0xb0800000 0x0010 /* NAND Base DATA */ + 0xb0820000 0x0010 /* NAND Base ADDR */ + 0xb0810000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; interrupts = <0 20 0x4 0 21 0x4 0 22 0x4 0 23 0x4>; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; st,mode = <2>; status = "disabled"; }; @@ -144,7 +144,7 @@ compatible = "st,pcm-audio"; #address-cells = <0>; #size-cells = <0>; - status = "disable"; + status = "disabled"; }; smi: flash@ea000000 { diff --git a/arch/arm/boot/dts/spear300.dtsi b/arch/arm/boot/dts/spear300.dtsi index 090adc656015..f79b3dfaabe6 100644 --- a/arch/arm/boot/dts/spear300.dtsi +++ b/arch/arm/boot/dts/spear300.dtsi @@ -38,10 +38,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x94000000 0x1000 /* FSMC Register */ - 0x80000000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0x80000000 0x0010 /* NAND Base DATA */ + 0x80020000 0x0010 /* NAND Base ADDR */ + 0x80010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear310.dtsi b/arch/arm/boot/dts/spear310.dtsi index e814e5e97083..ab45b8c81982 100644 --- a/arch/arm/boot/dts/spear310.dtsi +++ b/arch/arm/boot/dts/spear310.dtsi @@ -33,10 +33,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x44000000 0x1000 /* FSMC Register */ - 0x40000000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x10000>; - st,cle-off = <0x20000>; + 0x40000000 0x0010 /* NAND Base DATA */ + 0x40020000 0x0010 /* NAND Base ADDR */ + 0x40010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear320.dtsi b/arch/arm/boot/dts/spear320.dtsi index c056a84deabf..caa5520b1fd4 100644 --- a/arch/arm/boot/dts/spear320.dtsi +++ b/arch/arm/boot/dts/spear320.dtsi @@ -40,10 +40,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x4c000000 0x1000 /* FSMC Register */ - 0x50000000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0x50000000 0x0010 /* NAND Base DATA */ + 0x50020000 0x0010 /* NAND Base ADDR */ + 0x50010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear600.dtsi b/arch/arm/boot/dts/spear600.dtsi index e051dde5181f..19f99dc4115e 100644 --- a/arch/arm/boot/dts/spear600.dtsi +++ b/arch/arm/boot/dts/spear600.dtsi @@ -76,10 +76,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0xd1800000 0x1000 /* FSMC Register */ - 0xd2000000 0x4000>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0xd2000000 0x0010 /* NAND Base DATA */ + 0xd2020000 0x0010 /* NAND Base ADDR */ + 0xd2010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/configs/nhk8815_defconfig b/arch/arm/configs/nhk8815_defconfig index 240b25eea565..86cfd2959c47 100644 --- a/arch/arm/configs/nhk8815_defconfig +++ b/arch/arm/configs/nhk8815_defconfig @@ -57,7 +57,7 @@ CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_ECC_SMC=y -CONFIG_MTD_NAND_NOMADIK=y +CONFIG_MTD_NAND_FSMC=y CONFIG_MTD_ONENAND=y CONFIG_MTD_ONENAND_VERIFY_WRITE=y CONFIG_MTD_ONENAND_GENERIC=y diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h index ac03bdb4ae44..4da7cde70b5d 100644 --- a/arch/arm/include/uapi/asm/unistd.h +++ b/arch/arm/include/uapi/asm/unistd.h @@ -405,6 +405,7 @@ #define __NR_process_vm_readv (__NR_SYSCALL_BASE+376) #define __NR_process_vm_writev (__NR_SYSCALL_BASE+377) /* 378 for kcmp */ +#define __NR_finit_module (__NR_SYSCALL_BASE+379) /* * This may need to be greater than __NR_last_syscall+1 in order to diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index 5935b6a02e6e..a4fda4e7a372 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S @@ -388,6 +388,7 @@ CALL(sys_process_vm_readv) CALL(sys_process_vm_writev) CALL(sys_ni_syscall) /* reserved for sys_kcmp */ + CALL(sys_finit_module) #ifndef syscalls_counted .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls #define syscalls_counted diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c index 5ccdf53c5a9d..98167a4319f7 100644 --- a/arch/arm/mach-nomadik/board-nhk8815.c +++ b/arch/arm/mach-nomadik/board-nhk8815.c @@ -19,6 +19,7 @@ #include <linux/gpio.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> +#include <linux/mtd/fsmc.h> #include <linux/mtd/onenand.h> #include <linux/mtd/partitions.h> #include <linux/i2c.h> @@ -33,7 +34,6 @@ #include <asm/mach/arch.h> #include <asm/mach/flash.h> #include <asm/mach/time.h> -#include <mach/fsmc.h> #include <mach/irqs.h> #include "cpu-8815.h" @@ -42,39 +42,34 @@ #define SRC_CR_INIT_MASK 0x00007fff #define SRC_CR_INIT_VAL 0x2aaa8000 +#define ALE_OFF 0x1000000 +#define CLE_OFF 0x800000 + /* These addresses span 16MB, so use three individual pages */ static struct resource nhk8815_nand_resources[] = { { + .name = "nand_data", + .start = 0x40000000, + .end = 0x40000000 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, { .name = "nand_addr", - .start = NAND_IO_ADDR, - .end = NAND_IO_ADDR + 0xfff, + .start = 0x40000000 + ALE_OFF, + .end = 0x40000000 +ALE_OFF + SZ_16K - 1, .flags = IORESOURCE_MEM, }, { .name = "nand_cmd", - .start = NAND_IO_CMD, - .end = NAND_IO_CMD + 0xfff, + .start = 0x40000000 + CLE_OFF, + .end = 0x40000000 + CLE_OFF + SZ_16K - 1, .flags = IORESOURCE_MEM, }, { - .name = "nand_data", - .start = NAND_IO_DATA, - .end = NAND_IO_DATA + 0xfff, + .name = "fsmc_regs", + .start = NOMADIK_FSMC_BASE, + .end = NOMADIK_FSMC_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, - } + }, }; -static int nhk8815_nand_init(void) -{ - /* FSMC setup for nand chip select (8-bit nand in 8815NHK) */ - writel(0x0000000E, FSMC_PCR(0)); - writel(0x000D0A00, FSMC_PMEM(0)); - writel(0x00100A00, FSMC_PATT(0)); - - /* enable access to the chip select area */ - writel(readl(FSMC_PCR(0)) | 0x04, FSMC_PCR(0)); - - return 0; -} - /* * These partitions are the same as those used in the 2.6.20 release * shipped by the vendor; the first two partitions are mandated @@ -108,20 +103,28 @@ static struct mtd_partition nhk8815_partitions[] = { } }; -static struct nomadik_nand_platform_data nhk8815_nand_data = { - .parts = nhk8815_partitions, - .nparts = ARRAY_SIZE(nhk8815_partitions), - .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING, - .init = nhk8815_nand_init, +static struct fsmc_nand_timings nhk8815_nand_timings = { + .thiz = 0, + .thold = 0x10, + .twait = 0x0A, + .tset = 0, +}; + +static struct fsmc_nand_platform_data nhk8815_nand_platform_data = { + .nand_timings = &nhk8815_nand_timings, + .partitions = nhk8815_partitions, + .nr_partitions = ARRAY_SIZE(nhk8815_partitions), + .width = FSMC_NAND_BW8, }; static struct platform_device nhk8815_nand_device = { - .name = "nomadik_nand", - .dev = { - .platform_data = &nhk8815_nand_data, + .name = "fsmc-nand", + .id = -1, + .resource = nhk8815_nand_resources, + .num_resources = ARRAY_SIZE(nhk8815_nand_resources), + .dev = { + .platform_data = &nhk8815_nand_platform_data, }, - .resource = nhk8815_nand_resources, - .num_resources = ARRAY_SIZE(nhk8815_nand_resources), }; /* These are the partitions for the OneNand device, different from above */ @@ -176,6 +179,10 @@ static struct platform_device nhk8815_onenand_device = { .num_resources = ARRAY_SIZE(nhk8815_onenand_resource), }; +/* bus control reg. and bus timing reg. for CS0..CS3 */ +#define FSMC_BCR(x) (NOMADIK_FSMC_VA + (x << 3)) +#define FSMC_BTR(x) (NOMADIK_FSMC_VA + (x << 3) + 0x04) + static void __init nhk8815_onenand_init(void) { #ifdef CONFIG_MTD_ONENAND diff --git a/arch/arm/mach-nomadik/include/mach/fsmc.h b/arch/arm/mach-nomadik/include/mach/fsmc.h deleted file mode 100644 index 8c2c05183685..000000000000 --- a/arch/arm/mach-nomadik/include/mach/fsmc.h +++ /dev/null @@ -1,29 +0,0 @@ - -/* Definitions for the Nomadik FSMC "Flexible Static Memory controller" */ - -#ifndef __ASM_ARCH_FSMC_H -#define __ASM_ARCH_FSMC_H - -#include <mach/hardware.h> -/* - * Register list - */ - -/* bus control reg. and bus timing reg. for CS0..CS3 */ -#define FSMC_BCR(x) (NOMADIK_FSMC_VA + (x << 3)) -#define FSMC_BTR(x) (NOMADIK_FSMC_VA + (x << 3) + 0x04) - -/* PC-card and NAND: - * PCR = control register - * PMEM = memory timing - * PATT = attribute timing - * PIO = I/O timing - * PECCR = ECC result - */ -#define FSMC_PCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x00) -#define FSMC_PMEM(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x08) -#define FSMC_PATT(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x0c) -#define FSMC_PIO(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x10) -#define FSMC_PECCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x14) - -#endif /* __ASM_ARCH_FSMC_H */ diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 60529e0b3d67..cf07e289b4ea 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -256,6 +256,11 @@ static struct spi_board_info rx51_peripherals_spi_board_info[] __initdata = { }, }; +static struct platform_device rx51_battery_device = { + .name = "rx51-battery", + .id = -1, +}; + static void rx51_charger_set_power(bool on) { gpio_set_value(RX51_USB_TRANSCEIVER_RST_GPIO, on); @@ -277,6 +282,7 @@ static void __init rx51_charger_init(void) WARN_ON(gpio_request_one(RX51_USB_TRANSCEIVER_RST_GPIO, GPIOF_OUT_INIT_HIGH, "isp1704_reset")); + platform_device_register(&rx51_battery_device); platform_device_register(&rx51_charger_device); } diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 12f3994c43db..8b204ae69002 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -250,6 +250,18 @@ static struct resource rtc_resources[] = { */ static struct resource fsmc_resources[] = { { + .name = "nand_addr", + .start = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_ALE, + .end = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_ALE + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "nand_cmd", + .start = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_CLE, + .end = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_CLE + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + { .name = "nand_data", .start = U300_NAND_CS0_PHYS_BASE, .end = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1, @@ -1492,8 +1504,6 @@ static struct fsmc_nand_platform_data nand_platform_data = { .nr_partitions = ARRAY_SIZE(u300_partitions), .options = NAND_SKIP_BBTSCAN, .width = FSMC_NAND_BW8, - .ale_off = PLAT_NAND_ALE, - .cle_off = PLAT_NAND_CLE, }; static struct platform_device nand_device = { diff --git a/arch/cris/kernel/module.c b/arch/cris/kernel/module.c index 37400f5869e6..51123f985eb5 100644 --- a/arch/cris/kernel/module.c +++ b/arch/cris/kernel/module.c @@ -32,8 +32,6 @@ #ifdef CONFIG_ETRAX_KMALLOCED_MODULES void *module_alloc(unsigned long size) { - if (size == 0) - return NULL; return kmalloc(size, GFP_KERNEL); } diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c index 5e34ccf39a49..2a625fb063e1 100644 --- a/arch/parisc/kernel/module.c +++ b/arch/parisc/kernel/module.c @@ -214,8 +214,6 @@ static inline int reassemble_22(int as22) void *module_alloc(unsigned long size) { - if (size == 0) - return NULL; /* using RWX means less protection for modules, but it's * easier than trying to map the text, data, init_text and * init_data correctly */ diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h index cec8aae5cbf8..97909d3b1d7b 100644 --- a/arch/powerpc/include/asm/systbl.h +++ b/arch/powerpc/include/asm/systbl.h @@ -356,3 +356,4 @@ COMPAT_SYS_SPU(sendmmsg) SYSCALL_SPU(setns) COMPAT_SYS(process_vm_readv) COMPAT_SYS(process_vm_writev) +SYSCALL(finit_module) diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index bcbbe413c606..29365e15ed7c 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -12,7 +12,7 @@ #include <uapi/asm/unistd.h> -#define __NR_syscalls 353 +#define __NR_syscalls 354 #define __NR__exit __NR_exit #define NR_syscalls __NR_syscalls diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h index 380b5d37a904..8c478c6c6b1e 100644 --- a/arch/powerpc/include/uapi/asm/unistd.h +++ b/arch/powerpc/include/uapi/asm/unistd.h @@ -375,6 +375,7 @@ #define __NR_setns 350 #define __NR_process_vm_readv 351 #define __NR_process_vm_writev 352 +#define __NR_finit_module 353 #endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */ diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c index f1ddc0d23679..4435488ebe25 100644 --- a/arch/sparc/kernel/module.c +++ b/arch/sparc/kernel/module.c @@ -43,10 +43,6 @@ void *module_alloc(unsigned long size) { void *ret; - /* We handle the zero case fine, unlike vmalloc */ - if (size == 0) - return NULL; - ret = module_map(size); if (ret) memset(ret, 0, size); diff --git a/arch/tile/kernel/module.c b/arch/tile/kernel/module.c index 243ffebe38d6..4918d91bc3a6 100644 --- a/arch/tile/kernel/module.c +++ b/arch/tile/kernel/module.c @@ -42,8 +42,6 @@ void *module_alloc(unsigned long size) int i = 0; int npages; - if (size == 0) - return NULL; npages = (size + PAGE_SIZE - 1) / PAGE_SIZE; pages = kmalloc(npages * sizeof(struct page *), GFP_KERNEL); if (pages == NULL) diff --git a/arch/unicore32/kernel/module.c b/arch/unicore32/kernel/module.c index 8fbe8577f5e6..16bd1495b934 100644 --- a/arch/unicore32/kernel/module.c +++ b/arch/unicore32/kernel/module.c @@ -27,9 +27,6 @@ void *module_alloc(unsigned long size) struct vm_struct *area; size = PAGE_ALIGN(size); - if (!size) - return NULL; - area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END); if (!area) return NULL; diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index fbd895562292..3286a92e662a 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c @@ -26,11 +26,6 @@ static void show_cpuinfo_core(struct seq_file *m, struct cpuinfo_x86 *c, #ifdef CONFIG_X86_32 static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) { - /* - * We use exception 16 if we have hardware math and we've either seen - * it or the CPU claims it is internal - */ - int fpu_exception = c->hard_math && (ignore_fpu_irq || cpu_has_fpu); seq_printf(m, "fdiv_bug\t: %s\n" "hlt_bug\t\t: %s\n" @@ -45,7 +40,7 @@ static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) c->f00f_bug ? "yes" : "no", c->coma_bug ? "yes" : "no", c->hard_math ? "yes" : "no", - fpu_exception ? "yes" : "no", + c->hard_math ? "yes" : "no", c->cpuid_level, c->wp_works_ok ? "yes" : "no"); } diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c index 6e03b0d69138..7dc4e459c2b3 100644 --- a/arch/x86/kernel/irqinit.c +++ b/arch/x86/kernel/irqinit.c @@ -42,39 +42,6 @@ * (these are usually mapped into the 0x30-0xff vector range) */ -#ifdef CONFIG_X86_32 -/* - * Note that on a 486, we don't want to do a SIGFPE on an irq13 - * as the irq is unreliable, and exception 16 works correctly - * (ie as explained in the intel literature). On a 386, you - * can't use exception 16 due to bad IBM design, so we have to - * rely on the less exact irq13. - * - * Careful.. Not only is IRQ13 unreliable, but it is also - * leads to races. IBM designers who came up with it should - * be shot. - */ - -static irqreturn_t math_error_irq(int cpl, void *dev_id) -{ - outb(0, 0xF0); - if (ignore_fpu_irq || !boot_cpu_data.hard_math) - return IRQ_NONE; - math_error(get_irq_regs(), 0, X86_TRAP_MF); - return IRQ_HANDLED; -} - -/* - * New motherboards sometimes make IRQ 13 be a PCI interrupt, - * so allow interrupt sharing. - */ -static struct irqaction fpu_irq = { - .handler = math_error_irq, - .name = "fpu", - .flags = IRQF_NO_THREAD, -}; -#endif - /* * IRQ2 is cascade interrupt to second interrupt controller */ @@ -242,13 +209,6 @@ void __init native_init_IRQ(void) setup_irq(2, &irq2); #ifdef CONFIG_X86_32 - /* - * External FPU? Set up irq13 if so, for - * original braindamaged IBM FERR coupling. - */ - if (boot_cpu_data.hard_math && !cpu_has_fpu) - setup_irq(FPU_IRQ, &fpu_irq); - irq_ctx_init(smp_processor_id()); #endif } diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index eb8586693e0b..ecffca11f4e9 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -69,9 +69,6 @@ asmlinkage int system_call(void); -/* Do we ignore FPU interrupts ? */ -char ignore_fpu_irq; - /* * The IDT has to be page-aligned to simplify the Pentium * F0 0F bug workaround. @@ -564,9 +561,6 @@ void math_error(struct pt_regs *regs, int error_code, int trapnr) dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) { -#ifdef CONFIG_X86_32 - ignore_fpu_irq = 1; -#endif exception_enter(regs); math_error(regs, error_code, X86_TRAP_MF); exception_exit(regs); diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index ee3c220ee500..05f404f53f59 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -356,3 +356,4 @@ 347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv 348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev 349 i386 kcmp sys_kcmp +350 i386 finit_module sys_finit_module diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl index a582bfed95bb..7c58c84b7bc8 100644 --- a/arch/x86/syscalls/syscall_64.tbl +++ b/arch/x86/syscalls/syscall_64.tbl @@ -319,6 +319,7 @@ 310 64 process_vm_readv sys_process_vm_readv 311 64 process_vm_writev sys_process_vm_writev 312 common kcmp sys_kcmp +313 common finit_module sys_finit_module # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 2481f267be29..73d34e77c39c 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -17,6 +17,7 @@ config XTENSA select GENERIC_KERNEL_EXECVE select ARCH_WANT_OPTIONAL_GPIOLIB select CLONE_BACKWARDS + select IRQ_DOMAIN help Xtensa processors are 32-bit RISC machines designed by Tensilica primarily for embedded systems. These processors are both @@ -150,6 +151,15 @@ config XTENSA_PLATFORM_S6105 select SERIAL_CONSOLE select NO_IOPORT +config XTENSA_PLATFORM_XTFPGA + bool "XTFPGA" + select SERIAL_CONSOLE + select ETHOC + select XTENSA_CALIBRATE_CCOUNT + help + XTFPGA is the name of Tensilica board family (LX60, LX110, LX200, ML605). + This hardware is capable of running a full Linux distribution. + endchoice @@ -177,6 +187,17 @@ config CMDLINE time by entering them here. As a minimum, you should specify the memory size and the root device (e.g., mem=64M root=/dev/nfs). +config USE_OF + bool "Flattened Device Tree support" + select OF + select OF_EARLY_FLATTREE + help + Include support for flattened device tree machine descriptions. + +config BUILTIN_DTB + string "DTB to build into the kernel image" + depends on OF + source "mm/Kconfig" source "drivers/pcmcia/Kconfig" diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug index 11c585295dd7..a34010e0e51c 100644 --- a/arch/xtensa/Kconfig.debug +++ b/arch/xtensa/Kconfig.debug @@ -2,6 +2,26 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -endmenu +config LD_NO_RELAX + bool "Disable linker relaxation" + default n + help + Enable this function to disable link-time optimizations. + The default linker behavior is to combine identical literal + values to reduce code size and remove unnecessary overhead from + assembler-generated 'longcall' sequences. + Enabling this option improves the link time but increases the + code size, and possibly execution time. + +config S32C1I_SELFTEST + bool "Perform S32C1I instruction self-test at boot" + default y + help + Enable this option to test S32C1I instruction behavior at boot. + Correct operation of this instruction requires some cooperation from hardware + external to the processor (such as bus bridge, bus fabric, or memory controller). + It is easy to make wrong hardware configuration, this test should catch it early. + Say 'N' on stable hardware. +endmenu diff --git a/arch/xtensa/Makefile b/arch/xtensa/Makefile index bb5ba61723f7..0aa72702f179 100644 --- a/arch/xtensa/Makefile +++ b/arch/xtensa/Makefile @@ -38,6 +38,7 @@ endif platform-$(CONFIG_XTENSA_PLATFORM_XT2000) := xt2000 platform-$(CONFIG_XTENSA_PLATFORM_ISS) := iss platform-$(CONFIG_XTENSA_PLATFORM_S6105) := s6105 +platform-$(CONFIG_XTENSA_PLATFORM_XTFPGA) := xtfpga PLATFORM = $(platform-y) export PLATFORM @@ -49,6 +50,17 @@ KBUILD_CFLAGS += -pipe -mlongcalls KBUILD_CFLAGS += $(call cc-option,-mforce-no-pic,) +ifneq ($(CONFIG_LD_NO_RELAX),) +LDFLAGS := --no-relax +endif + +ifeq ($(shell echo -e __XTENSA_EB__ | $(CC) -E - | grep -v "\#"),1) +CHECKFLAGS += -D__XTENSA_EB__ +endif +ifeq ($(shell echo -e __XTENSA_EL__ | $(CC) -E - | grep -v "\#"),1) +CHECKFLAGS += -D__XTENSA_EL__ +endif + vardirs := $(patsubst %,arch/xtensa/variants/%/,$(variant-y)) plfdirs := $(patsubst %,arch/xtensa/platforms/%/,$(platform-y)) @@ -75,6 +87,10 @@ core-y += $(buildvar) $(buildplf) libs-y += arch/xtensa/lib/ $(LIBGCC) +ifneq ($(CONFIG_BUILTIN_DTB),"") +core-$(CONFIG_OF) += arch/xtensa/boot/ +endif + boot := arch/xtensa/boot all: zImage @@ -84,7 +100,9 @@ bzImage : zImage zImage: vmlinux $(Q)$(MAKE) $(build)=$(boot) $@ +%.dtb: + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + define archhelp @echo '* zImage - Compressed kernel image (arch/xtensa/boot/images/zImage.*)' endef - diff --git a/arch/xtensa/boot/Makefile b/arch/xtensa/boot/Makefile index 4018f8994196..818647e815d7 100644 --- a/arch/xtensa/boot/Makefile +++ b/arch/xtensa/boot/Makefile @@ -22,12 +22,35 @@ subdir-y := lib # Subdirs for the boot loader(s) bootdir-$(CONFIG_XTENSA_PLATFORM_ISS) += boot-elf -bootdir-$(CONFIG_XTENSA_PLATFORM_XT2000) += boot-redboot boot-elf +bootdir-$(CONFIG_XTENSA_PLATFORM_XT2000) += boot-redboot boot-elf boot-uboot +bootdir-$(CONFIG_XTENSA_PLATFORM_XTFPGA) += boot-redboot boot-elf boot-uboot +BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB)).dtb.o +ifneq ($(CONFIG_BUILTIN_DTB),"") +obj-$(CONFIG_OF) += $(BUILTIN_DTB) +endif + +# Rule to build device tree blobs +$(obj)/%.dtb: $(src)/dts/%.dts FORCE + $(call if_changed_dep,dtc) + +clean-files := *.dtb.S + zImage Image: $(bootdir-y) $(bootdir-y): $(addprefix $(obj)/,$(subdir-y)) \ $(addprefix $(obj)/,$(host-progs)) $(Q)$(MAKE) $(build)=$(obj)/$@ $(MAKECMDGOALS) +OBJCOPYFLAGS = --strip-all -R .comment -R .note.gnu.build-id -O binary + +vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +vmlinux.bin.gz: vmlinux.bin FORCE + $(call if_changed,gzip) + +boot-elf: vmlinux.bin +boot-redboot: vmlinux.bin.gz +boot-uboot: vmlinux.bin.gz diff --git a/arch/xtensa/boot/boot-elf/Makefile b/arch/xtensa/boot/boot-elf/Makefile index f10992b89027..1fe01b78c124 100644 --- a/arch/xtensa/boot/boot-elf/Makefile +++ b/arch/xtensa/boot/boot-elf/Makefile @@ -4,9 +4,6 @@ # for more details. # -GZIP = gzip -GZIP_FLAGS = -v9fc - ifeq ($(BIG_ENDIAN),1) OBJCOPY_ARGS := -O elf32-xtensa-be else @@ -20,18 +17,17 @@ boot-y := bootstrap.o OBJS := $(addprefix $(obj)/,$(boot-y)) -vmlinux.tmp: vmlinux - $(OBJCOPY) --strip-all -R .comment -R .note.gnu.build-id -O binary \ - $^ $@ - -Image: vmlinux.tmp $(OBJS) arch/$(ARCH)/boot/boot-elf/boot.lds - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section image=vmlinux.tmp \ +$(obj)/Image.o: vmlinux.bin $(OBJS) + $(Q)$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section image=vmlinux.bin \ --set-section-flags image=contents,alloc,load,load,data \ - $(OBJS) $@.tmp - $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) \ - -T arch/$(ARCH)/boot/boot-elf/boot.lds \ - -o arch/$(ARCH)/boot/$@.elf $@.tmp + $(OBJS) $@ -zImage: Image +$(obj)/../Image.elf: $(obj)/Image.o $(obj)/boot.lds + $(Q)$(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) \ + -T $(obj)/boot.lds \ + --build-id=none \ + -o $@ $(obj)/Image.o + $(Q)$(kecho) ' Kernel: $@ is ready' +zImage: $(obj)/../Image.elf diff --git a/arch/xtensa/boot/boot-redboot/Makefile b/arch/xtensa/boot/boot-redboot/Makefile index 25a78c6b1530..8be8b9436981 100644 --- a/arch/xtensa/boot/boot-redboot/Makefile +++ b/arch/xtensa/boot/boot-redboot/Makefile @@ -4,8 +4,6 @@ # for more details. # -GZIP = gzip -GZIP_FLAGS = -v9fc ifeq ($(BIG_ENDIAN),1) OBJCOPY_ARGS := -O elf32-xtensa-be else @@ -21,17 +19,17 @@ LIBS := arch/xtensa/boot/lib/lib.a arch/xtensa/lib/lib.a LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) -vmlinux.tmp: vmlinux - $(OBJCOPY) --strip-all -R .comment -R .note.gnu.build-id -O binary \ - $^ $@ +$(obj)/zImage.o: vmlinux.bin.gz $(OBJS) + $(Q)$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section image=vmlinux.bin.gz \ + --set-section-flags image=contents,alloc,load,load,data \ + $(OBJS) $@ -vmlinux.tmp.gz: vmlinux.tmp - $(GZIP) $(GZIP_FLAGS) $^ > $@ +$(obj)/zImage.elf: $(obj)/zImage.o $(LIBS) + $(Q)$(LD) $(LD_ARGS) -o $@ $^ -L/xtensa-elf/lib $(LIBGCC) -zImage: vmlinux.tmp.gz $(OBJS) $(LIBS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section image=vmlinux.tmp.gz \ - --set-section-flags image=contents,alloc,load,load,data \ - $(OBJS) $@.tmp - $(LD) $(LD_ARGS) -o $@.elf $@.tmp $(LIBS) -L/xtensa-elf/lib $(LIBGCC) - $(OBJCOPY) -S -O binary $@.elf arch/$(ARCH)/boot/$@.redboot +$(obj)/../zImage.redboot: $(obj)/zImage.elf + $(Q)$(OBJCOPY) -S -O binary $< $@ + $(Q)$(kecho) ' Kernel: $@ is ready' + +zImage: $(obj)/../zImage.redboot diff --git a/arch/xtensa/boot/boot-uboot/Makefile b/arch/xtensa/boot/boot-uboot/Makefile new file mode 100644 index 000000000000..bfbf8af582f1 --- /dev/null +++ b/arch/xtensa/boot/boot-uboot/Makefile @@ -0,0 +1,14 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# + +UIMAGE_LOADADDR = 0xd0001000 +UIMAGE_COMPRESSION = gzip + +$(obj)/../uImage: vmlinux.bin.gz FORCE + $(call if_changed,uimage) + $(Q)$(kecho) ' Kernel: $@ is ready' + +zImage: $(obj)/../uImage diff --git a/arch/xtensa/boot/dts/lx60.dts b/arch/xtensa/boot/dts/lx60.dts new file mode 100644 index 000000000000..2eab3658e1bd --- /dev/null +++ b/arch/xtensa/boot/dts/lx60.dts @@ -0,0 +1,11 @@ +/dts-v1/; +/include/ "xtfpga.dtsi" +/include/ "xtfpga-flash-4m.dtsi" + +/ { + compatible = "xtensa,lx60"; + memory@0 { + device_type = "memory"; + reg = <0x00000000 0x04000000>; + }; +}; diff --git a/arch/xtensa/boot/dts/ml605.dts b/arch/xtensa/boot/dts/ml605.dts new file mode 100644 index 000000000000..6ed51d6554e6 --- /dev/null +++ b/arch/xtensa/boot/dts/ml605.dts @@ -0,0 +1,11 @@ +/dts-v1/; +/include/ "xtfpga.dtsi" +/include/ "xtfpga-flash-16m.dtsi" + +/ { + compatible = "xtensa,ml605"; + memory@0 { + device_type = "memory"; + reg = <0x00000000 0x08000000>; + }; +}; diff --git a/arch/xtensa/boot/dts/xtfpga-flash-16m.dtsi b/arch/xtensa/boot/dts/xtfpga-flash-16m.dtsi new file mode 100644 index 000000000000..e5703c7beeb6 --- /dev/null +++ b/arch/xtensa/boot/dts/xtfpga-flash-16m.dtsi @@ -0,0 +1,26 @@ +/ { + flash: flash@f8000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0xf8000000 0x01000000>; + bank-width = <2>; + device-width = <2>; + partition@0x0 { + label = "boot loader area"; + reg = <0x00000000 0x00400000>; + }; + partition@0x400000 { + label = "kernel image"; + reg = <0x00400000 0x00600000>; + }; + partition@0xa00000 { + label = "data"; + reg = <0x00a00000 0x005e0000>; + }; + partition@0xfe0000 { + label = "boot environment"; + reg = <0x00fe0000 0x00020000>; + }; + }; +}; diff --git a/arch/xtensa/boot/dts/xtfpga-flash-4m.dtsi b/arch/xtensa/boot/dts/xtfpga-flash-4m.dtsi new file mode 100644 index 000000000000..6f9c10d6b689 --- /dev/null +++ b/arch/xtensa/boot/dts/xtfpga-flash-4m.dtsi @@ -0,0 +1,18 @@ +/ { + flash: flash@f8000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0xf8000000 0x00400000>; + bank-width = <2>; + device-width = <2>; + partition@0x0 { + label = "boot loader area"; + reg = <0x00000000 0x003f0000>; + }; + partition@0x3f0000 { + label = "boot environment"; + reg = <0x003f0000 0x00010000>; + }; + }; +}; diff --git a/arch/xtensa/boot/dts/xtfpga.dtsi b/arch/xtensa/boot/dts/xtfpga.dtsi new file mode 100644 index 000000000000..7eda6ecf7eef --- /dev/null +++ b/arch/xtensa/boot/dts/xtfpga.dtsi @@ -0,0 +1,56 @@ +/ { + compatible = "xtensa,xtfpga"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&pic>; + + chosen { + bootargs = "earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x00000000 0x06000000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + compatible = "xtensa,cpu"; + reg = <0>; + /* Filled in by platform_setup from FPGA register + * clock-frequency = <100000000>; + */ + }; + }; + + pic: pic { + compatible = "xtensa,pic"; + /* one cell: internal irq number, + * two cells: second cell == 0: internal irq number + * second cell == 1: external irq number + */ + #interrupt-cells = <2>; + interrupt-controller; + }; + + serial0: serial@fd050020 { + device_type = "serial"; + compatible = "ns16550a"; + no-loopback-test; + reg = <0xfd050020 0x20>; + reg-shift = <2>; + interrupts = <0 1>; /* external irq 0 */ + /* Filled in by platform_setup from FPGA register + * clock-frequency = <100000000>; + */ + }; + + enet0: ethoc@fd030000 { + compatible = "opencores,ethoc"; + reg = <0xfd030000 0x4000 0xfd800000 0x4000>; + interrupts = <1 1>; /* external irq 1 */ + local-mac-address = [00 50 c2 13 6f 00]; + }; +}; diff --git a/arch/xtensa/include/asm/atomic.h b/arch/xtensa/include/asm/atomic.h index 24f50cada70c..c3f289174c10 100644 --- a/arch/xtensa/include/asm/atomic.h +++ b/arch/xtensa/include/asm/atomic.h @@ -66,19 +66,35 @@ */ static inline void atomic_add(int i, atomic_t * v) { - unsigned int vval; - - __asm__ __volatile__( - "rsil a15, "__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %2, 0 \n\t" - "add %0, %0, %1 \n\t" - "s32i %0, %2, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n" - : "=&a" (vval) - : "a" (i), "a" (v) - : "a15", "memory" - ); +#if XCHAL_HAVE_S32C1I + unsigned long tmp; + int result; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " add %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (result), "=&a" (tmp) + : "a" (i), "a" (v) + : "memory" + ); +#else + unsigned int vval; + + __asm__ __volatile__( + " rsil a15, "__stringify(LOCKLEVEL)"\n" + " l32i %0, %2, 0\n" + " add %0, %0, %1\n" + " s32i %0, %2, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); +#endif } /** @@ -90,19 +106,35 @@ static inline void atomic_add(int i, atomic_t * v) */ static inline void atomic_sub(int i, atomic_t *v) { - unsigned int vval; - - __asm__ __volatile__( - "rsil a15, "__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %2, 0 \n\t" - "sub %0, %0, %1 \n\t" - "s32i %0, %2, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n" - : "=&a" (vval) - : "a" (i), "a" (v) - : "a15", "memory" - ); +#if XCHAL_HAVE_S32C1I + unsigned long tmp; + int result; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " sub %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (result), "=&a" (tmp) + : "a" (i), "a" (v) + : "memory" + ); +#else + unsigned int vval; + + __asm__ __volatile__( + " rsil a15, "__stringify(LOCKLEVEL)"\n" + " l32i %0, %2, 0\n" + " sub %0, %0, %1\n" + " s32i %0, %2, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); +#endif } /* @@ -111,40 +143,78 @@ static inline void atomic_sub(int i, atomic_t *v) static inline int atomic_add_return(int i, atomic_t * v) { - unsigned int vval; - - __asm__ __volatile__( - "rsil a15,"__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %2, 0 \n\t" - "add %0, %0, %1 \n\t" - "s32i %0, %2, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n" - : "=&a" (vval) - : "a" (i), "a" (v) - : "a15", "memory" - ); - - return vval; +#if XCHAL_HAVE_S32C1I + unsigned long tmp; + int result; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " add %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + " add %0, %0, %2\n" + : "=&a" (result), "=&a" (tmp) + : "a" (i), "a" (v) + : "memory" + ); + + return result; +#else + unsigned int vval; + + __asm__ __volatile__( + " rsil a15,"__stringify(LOCKLEVEL)"\n" + " l32i %0, %2, 0\n" + " add %0, %0, %1\n" + " s32i %0, %2, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); + + return vval; +#endif } static inline int atomic_sub_return(int i, atomic_t * v) { - unsigned int vval; - - __asm__ __volatile__( - "rsil a15,"__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %2, 0 \n\t" - "sub %0, %0, %1 \n\t" - "s32i %0, %2, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n" - : "=&a" (vval) - : "a" (i), "a" (v) - : "a15", "memory" - ); - - return vval; +#if XCHAL_HAVE_S32C1I + unsigned long tmp; + int result; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " sub %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + " sub %0, %0, %2\n" + : "=&a" (result), "=&a" (tmp) + : "a" (i), "a" (v) + : "memory" + ); + + return result; +#else + unsigned int vval; + + __asm__ __volatile__( + " rsil a15,"__stringify(LOCKLEVEL)"\n" + " l32i %0, %2, 0\n" + " sub %0, %0, %1\n" + " s32i %0, %2, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (vval) + : "a" (i), "a" (v) + : "a15", "memory" + ); + + return vval; +#endif } /** @@ -251,38 +321,70 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u) static inline void atomic_clear_mask(unsigned int mask, atomic_t *v) { - unsigned int all_f = -1; - unsigned int vval; - - __asm__ __volatile__( - "rsil a15,"__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %2, 0 \n\t" - "xor %1, %4, %3 \n\t" - "and %0, %0, %4 \n\t" - "s32i %0, %2, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n" - : "=&a" (vval), "=a" (mask) - : "a" (v), "a" (all_f), "1" (mask) - : "a15", "memory" - ); +#if XCHAL_HAVE_S32C1I + unsigned long tmp; + int result; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " and %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (result), "=&a" (tmp) + : "a" (~mask), "a" (v) + : "memory" + ); +#else + unsigned int all_f = -1; + unsigned int vval; + + __asm__ __volatile__( + " rsil a15,"__stringify(LOCKLEVEL)"\n" + " l32i %0, %2, 0\n" + " xor %1, %4, %3\n" + " and %0, %0, %4\n" + " s32i %0, %2, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (vval), "=a" (mask) + : "a" (v), "a" (all_f), "1" (mask) + : "a15", "memory" + ); +#endif } static inline void atomic_set_mask(unsigned int mask, atomic_t *v) { - unsigned int vval; - - __asm__ __volatile__( - "rsil a15,"__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %2, 0 \n\t" - "or %0, %0, %1 \n\t" - "s32i %0, %2, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n" - : "=&a" (vval) - : "a" (mask), "a" (v) - : "a15", "memory" - ); +#if XCHAL_HAVE_S32C1I + unsigned long tmp; + int result; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " or %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (result), "=&a" (tmp) + : "a" (mask), "a" (v) + : "memory" + ); +#else + unsigned int vval; + + __asm__ __volatile__( + " rsil a15,"__stringify(LOCKLEVEL)"\n" + " l32i %0, %2, 0\n" + " or %0, %0, %1\n" + " s32i %0, %2, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (vval) + : "a" (mask), "a" (v) + : "a15", "memory" + ); +#endif } /* Atomic operations are already serializing */ @@ -294,4 +396,3 @@ static inline void atomic_set_mask(unsigned int mask, atomic_t *v) #endif /* __KERNEL__ */ #endif /* _XTENSA_ATOMIC_H */ - diff --git a/arch/xtensa/include/asm/barrier.h b/arch/xtensa/include/asm/barrier.h index 55707a8009d3..ef021677d536 100644 --- a/arch/xtensa/include/asm/barrier.h +++ b/arch/xtensa/include/asm/barrier.h @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2001 - 2005 Tensilica Inc. + * Copyright (C) 2001 - 2012 Tensilica Inc. */ #ifndef _XTENSA_SYSTEM_H @@ -12,8 +12,8 @@ #define smp_read_barrier_depends() do { } while(0) #define read_barrier_depends() do { } while(0) -#define mb() barrier() -#define rmb() mb() +#define mb() ({ __asm__ __volatile__("memw" : : : "memory"); }) +#define rmb() barrier() #define wmb() mb() #ifdef CONFIG_SMP diff --git a/arch/xtensa/include/asm/bitops.h b/arch/xtensa/include/asm/bitops.h index 5270197ddd36..84afe58d5d37 100644 --- a/arch/xtensa/include/asm/bitops.h +++ b/arch/xtensa/include/asm/bitops.h @@ -29,7 +29,6 @@ #define smp_mb__before_clear_bit() barrier() #define smp_mb__after_clear_bit() barrier() -#include <asm-generic/bitops/atomic.h> #include <asm-generic/bitops/non-atomic.h> #if XCHAL_HAVE_NSA @@ -104,6 +103,132 @@ static inline unsigned long __fls(unsigned long word) #endif #include <asm-generic/bitops/fls64.h> + +#if XCHAL_HAVE_S32C1I + +static inline void set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long tmp, value; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " or %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp), "=&a" (value) + : "a" (mask), "a" (p) + : "memory"); +} + +static inline void clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long tmp, value; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " and %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp), "=&a" (value) + : "a" (~mask), "a" (p) + : "memory"); +} + +static inline void change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long tmp, value; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " xor %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp), "=&a" (value) + : "a" (mask), "a" (p) + : "memory"); +} + +static inline int +test_and_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long tmp, value; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " or %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp), "=&a" (value) + : "a" (mask), "a" (p) + : "memory"); + + return tmp & mask; +} + +static inline int +test_and_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long tmp, value; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " and %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp), "=&a" (value) + : "a" (~mask), "a" (p) + : "memory"); + + return tmp & mask; +} + +static inline int +test_and_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long tmp, value; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + __asm__ __volatile__( + "1: l32i %1, %3, 0\n" + " wsr %1, scompare1\n" + " xor %0, %1, %2\n" + " s32c1i %0, %3, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp), "=&a" (value) + : "a" (mask), "a" (p) + : "memory"); + + return tmp & mask; +} + +#else + +#include <asm-generic/bitops/atomic.h> + +#endif /* XCHAL_HAVE_S32C1I */ + #include <asm-generic/bitops/find.h> #include <asm-generic/bitops/le.h> diff --git a/arch/xtensa/include/asm/bootparam.h b/arch/xtensa/include/asm/bootparam.h index 9983f2c1b7ee..0c25799facab 100644 --- a/arch/xtensa/include/asm/bootparam.h +++ b/arch/xtensa/include/asm/bootparam.h @@ -22,6 +22,7 @@ #define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ #define BP_TAG_SERIAL_BAUSRATE 0x1004 /* baud rate of current console. */ #define BP_TAG_SERIAL_PORT 0x1005 /* serial device of current console */ +#define BP_TAG_FDT 0x1006 /* flat device tree addr */ #define BP_TAG_FIRST 0x7B0B /* first tag with a version number */ #define BP_TAG_LAST 0x7E0B /* last tag */ @@ -31,15 +32,15 @@ /* All records are aligned to 4 bytes */ typedef struct bp_tag { - unsigned short id; /* tag id */ - unsigned short size; /* size of this record excluding the structure*/ - unsigned long data[0]; /* data */ + unsigned short id; /* tag id */ + unsigned short size; /* size of this record excluding the structure*/ + unsigned long data[0]; /* data */ } bp_tag_t; typedef struct meminfo { - unsigned long type; - unsigned long start; - unsigned long end; + unsigned long type; + unsigned long start; + unsigned long end; } meminfo_t; #define SYSMEM_BANKS_MAX 5 @@ -48,14 +49,11 @@ typedef struct meminfo { #define MEMORY_TYPE_NONE 0x2000 typedef struct sysmem_info { - int nr_banks; - meminfo_t bank[SYSMEM_BANKS_MAX]; + int nr_banks; + meminfo_t bank[SYSMEM_BANKS_MAX]; } sysmem_info_t; extern sysmem_info_t sysmem; #endif #endif - - - diff --git a/arch/xtensa/include/asm/cacheasm.h b/arch/xtensa/include/asm/cacheasm.h index 2c20a58f94cd..60e18773ecb8 100644 --- a/arch/xtensa/include/asm/cacheasm.h +++ b/arch/xtensa/include/asm/cacheasm.h @@ -174,4 +174,3 @@ __loop_cache_page \ar \as ihi XCHAL_ICACHE_LINEWIDTH .endm - diff --git a/arch/xtensa/include/asm/cacheflush.h b/arch/xtensa/include/asm/cacheflush.h index 569fec4f9a20..127cd48883c4 100644 --- a/arch/xtensa/include/asm/cacheflush.h +++ b/arch/xtensa/include/asm/cacheflush.h @@ -104,7 +104,8 @@ static inline void __invalidate_icache_page_alias(unsigned long virt, #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 extern void flush_dcache_page(struct page*); extern void flush_cache_range(struct vm_area_struct*, ulong, ulong); -extern void flush_cache_page(struct vm_area_struct*, unsigned long, unsigned long); +extern void flush_cache_page(struct vm_area_struct*, + unsigned long, unsigned long); #else diff --git a/arch/xtensa/include/asm/checksum.h b/arch/xtensa/include/asm/checksum.h index e4d831a30772..aed7ad68ca46 100644 --- a/arch/xtensa/include/asm/checksum.h +++ b/arch/xtensa/include/asm/checksum.h @@ -36,8 +36,9 @@ asmlinkage __wsum csum_partial(const void *buff, int len, __wsum sum); * better 64-bit) boundary */ -asmlinkage __wsum csum_partial_copy_generic(const void *src, void *dst, int len, __wsum sum, - int *src_err_ptr, int *dst_err_ptr); +asmlinkage __wsum csum_partial_copy_generic(const void *src, void *dst, + int len, __wsum sum, + int *src_err_ptr, int *dst_err_ptr); /* * Note: when you get a NULL pointer exception here this means someone @@ -54,7 +55,7 @@ __wsum csum_partial_copy_nocheck(const void *src, void *dst, static inline __wsum csum_partial_copy_from_user(const void __user *src, void *dst, - int len, __wsum sum, int *err_ptr) + int len, __wsum sum, int *err_ptr) { return csum_partial_copy_generic((__force const void *)src, dst, len, sum, err_ptr, NULL); @@ -112,7 +113,8 @@ static __inline__ __sum16 ip_fast_csum(const void *iph, unsigned int ihl) /* Since the input registers which are loaded with iph and ihl are modified, we must also specify them as outputs, or gcc will assume they contain their original values. */ - : "=r" (sum), "=r" (iph), "=r" (ihl), "=&r" (tmp), "=&r" (endaddr) + : "=r" (sum), "=r" (iph), "=r" (ihl), "=&r" (tmp), + "=&r" (endaddr) : "1" (iph), "2" (ihl) : "memory"); @@ -168,7 +170,7 @@ static __inline__ __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, static __inline__ __sum16 ip_compute_csum(const void *buff, int len) { - return csum_fold (csum_partial(buff, len, 0)); + return csum_fold (csum_partial(buff, len, 0)); } #define _HAVE_ARCH_IPV6_CSUM @@ -238,11 +240,12 @@ static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr, * Copy and checksum to user */ #define HAVE_CSUM_COPY_USER -static __inline__ __wsum csum_and_copy_to_user(const void *src, void __user *dst, - int len, __wsum sum, int *err_ptr) +static __inline__ __wsum csum_and_copy_to_user(const void *src, + void __user *dst, int len, + __wsum sum, int *err_ptr) { if (access_ok(VERIFY_WRITE, dst, len)) - return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr); + return csum_partial_copy_generic(src,dst,len,sum,NULL,err_ptr); if (len) *err_ptr = -EFAULT; diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h index 64dad04a9d27..d9ab131bc1aa 100644 --- a/arch/xtensa/include/asm/cmpxchg.h +++ b/arch/xtensa/include/asm/cmpxchg.h @@ -22,17 +22,30 @@ static inline unsigned long __cmpxchg_u32(volatile int *p, int old, int new) { - __asm__ __volatile__("rsil a15, "__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %1, 0 \n\t" - "bne %0, %2, 1f \n\t" - "s32i %3, %1, 0 \n\t" - "1: \n\t" - "wsr a15, ps \n\t" - "rsync \n\t" - : "=&a" (old) - : "a" (p), "a" (old), "r" (new) - : "a15", "memory"); - return old; +#if XCHAL_HAVE_S32C1I + __asm__ __volatile__( + " wsr %2, scompare1\n" + " s32c1i %0, %1, 0\n" + : "+a" (new) + : "a" (p), "a" (old) + : "memory" + ); + + return new; +#else + __asm__ __volatile__( + " rsil a15, "__stringify(LOCKLEVEL)"\n" + " l32i %0, %1, 0\n" + " bne %0, %2, 1f\n" + " s32i %3, %1, 0\n" + "1:\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (old) + : "a" (p), "a" (old), "r" (new) + : "a15", "memory"); + return old; +#endif } /* This function doesn't exist, so you'll get a linker error * if something tries to do an invalid cmpxchg(). */ @@ -93,19 +106,36 @@ static inline unsigned long __cmpxchg_local(volatile void *ptr, static inline unsigned long xchg_u32(volatile int * m, unsigned long val) { - unsigned long tmp; - __asm__ __volatile__("rsil a15, "__stringify(LOCKLEVEL)"\n\t" - "l32i %0, %1, 0 \n\t" - "s32i %2, %1, 0 \n\t" - "wsr a15, ps \n\t" - "rsync \n\t" - : "=&a" (tmp) - : "a" (m), "a" (val) - : "a15", "memory"); - return tmp; +#if XCHAL_HAVE_S32C1I + unsigned long tmp, result; + __asm__ __volatile__( + "1: l32i %1, %2, 0\n" + " mov %0, %3\n" + " wsr %1, scompare1\n" + " s32c1i %0, %2, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (result), "=&a" (tmp) + : "a" (m), "a" (val) + : "memory" + ); + return result; +#else + unsigned long tmp; + __asm__ __volatile__( + " rsil a15, "__stringify(LOCKLEVEL)"\n" + " l32i %0, %1, 0\n" + " s32i %2, %1, 0\n" + " wsr a15, ps\n" + " rsync\n" + : "=&a" (tmp) + : "a" (m), "a" (val) + : "a15", "memory"); + return tmp; +#endif } -#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) +#define xchg(ptr,x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) /* * This only works if the compiler isn't horribly bad at optimizing. diff --git a/arch/xtensa/include/asm/current.h b/arch/xtensa/include/asm/current.h index 8d1eb5d78649..47e46dcf5d49 100644 --- a/arch/xtensa/include/asm/current.h +++ b/arch/xtensa/include/asm/current.h @@ -30,7 +30,7 @@ static inline struct task_struct *get_current(void) #define GET_CURRENT(reg,sp) \ GET_THREAD_INFO(reg,sp); \ - l32i reg, reg, TI_TASK \ + l32i reg, reg, TI_TASK \ #endif diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 58c0a4fd4003..61fc5faeb46c 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -19,9 +19,9 @@ extern unsigned long loops_per_jiffy; static inline void __delay(unsigned long loops) { - /* 2 cycles per loop. */ - __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 2, 1b" - : "=r" (loops) : "0" (loops)); + /* 2 cycles per loop. */ + __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 2, 1b" + : "=r" (loops) : "0" (loops)); } static __inline__ u32 xtensa_get_ccount(void) @@ -46,4 +46,3 @@ static __inline__ void udelay (unsigned long usecs) } #endif - diff --git a/arch/xtensa/include/asm/dma-mapping.h b/arch/xtensa/include/asm/dma-mapping.h index 492c95790ad5..4acb5feba1fb 100644 --- a/arch/xtensa/include/asm/dma-mapping.h +++ b/arch/xtensa/include/asm/dma-mapping.h @@ -16,6 +16,8 @@ #include <linux/mm.h> #include <linux/scatterlist.h> +#define DMA_ERROR_CODE (~(dma_addr_t)0x0) + /* * DMA-consistent mapping functions. */ @@ -98,8 +100,8 @@ dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, } static inline void -dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, - enum dma_data_direction direction) +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) { consistent_sync((void *)bus_to_virt(dma_handle), size, direction); } diff --git a/arch/xtensa/include/asm/elf.h b/arch/xtensa/include/asm/elf.h index 5293312bc6a4..264d5fa450d8 100644 --- a/arch/xtensa/include/asm/elf.h +++ b/arch/xtensa/include/asm/elf.h @@ -168,11 +168,11 @@ extern void xtensa_elf_core_copy_regs (xtensa_gregset_t *, struct pt_regs *); */ #define ELF_PLAT_INIT(_r, load_addr) \ - do { _r->areg[0]=0; /*_r->areg[1]=0;*/ _r->areg[2]=0; _r->areg[3]=0; \ - _r->areg[4]=0; _r->areg[5]=0; _r->areg[6]=0; _r->areg[7]=0; \ - _r->areg[8]=0; _r->areg[9]=0; _r->areg[10]=0; _r->areg[11]=0; \ - _r->areg[12]=0; _r->areg[13]=0; _r->areg[14]=0; _r->areg[15]=0; \ - } while (0) + do { _r->areg[0]=0; /*_r->areg[1]=0;*/ _r->areg[2]=0; _r->areg[3]=0; \ + _r->areg[4]=0; _r->areg[5]=0; _r->areg[6]=0; _r->areg[7]=0; \ + _r->areg[8]=0; _r->areg[9]=0; _r->areg[10]=0; _r->areg[11]=0; \ + _r->areg[12]=0; _r->areg[13]=0; _r->areg[14]=0; _r->areg[15]=0; \ + } while (0) typedef struct { xtregs_opt_t opt; diff --git a/arch/xtensa/include/asm/highmem.h b/arch/xtensa/include/asm/highmem.h index 0a046ca5a687..80be15124697 100644 --- a/arch/xtensa/include/asm/highmem.h +++ b/arch/xtensa/include/asm/highmem.h @@ -14,4 +14,3 @@ extern void flush_cache_kmaps(void); #endif - diff --git a/arch/xtensa/include/asm/initialize_mmu.h b/arch/xtensa/include/asm/initialize_mmu.h new file mode 100644 index 000000000000..e1f8ba4061ed --- /dev/null +++ b/arch/xtensa/include/asm/initialize_mmu.h @@ -0,0 +1,55 @@ +/* + * arch/xtensa/include/asm/initialize_mmu.h + * + * Initializes MMU: + * + * For the new V3 MMU we remap the TLB from virtual == physical + * to the standard Linux mapping used in earlier MMU's. + * + * The the MMU we also support a new configuration register that + * specifies how the S32C1I instruction operates with the cache + * controller. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * Copyright (C) 2008 - 2012 Tensilica, Inc. + * + * Marc Gauthier <marc@tensilica.com> + * Pete Delaney <piet@tensilica.com> + */ + +#ifndef _XTENSA_INITIALIZE_MMU_H +#define _XTENSA_INITIALIZE_MMU_H + +#ifdef __ASSEMBLY__ + +#define XTENSA_HWVERSION_RC_2009_0 230000 + + .macro initialize_mmu + +#if XCHAL_HAVE_S32C1I && (XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RC_2009_0) +/* + * We Have Atomic Operation Control (ATOMCTL) Register; Initialize it. + * For details see Documentation/xtensa/atomctl.txt + */ +#if XCHAL_DCACHE_IS_COHERENT + movi a3, 0x25 /* For SMP/MX -- internal for writeback, + * RCW otherwise + */ +#else + movi a3, 0x29 /* non-MX -- Most cores use Std Memory + * Controlers which usually can't use RCW + */ +#endif + wsr a3, atomctl +#endif /* XCHAL_HAVE_S32C1I && + * (XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RC_2009_0) + */ + + .endm + +#endif /*__ASSEMBLY__*/ + +#endif /* _XTENSA_INITIALIZE_MMU_H */ diff --git a/arch/xtensa/include/asm/mmu_context.h b/arch/xtensa/include/asm/mmu_context.h index feb10af96519..d43525a286bb 100644 --- a/arch/xtensa/include/asm/mmu_context.h +++ b/arch/xtensa/include/asm/mmu_context.h @@ -107,7 +107,7 @@ activate_mm(struct mm_struct *prev, struct mm_struct *next) static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk) + struct task_struct *tsk) { unsigned long asid = asid_cache; diff --git a/arch/xtensa/include/asm/nommu_context.h b/arch/xtensa/include/asm/nommu_context.h index 599e7a2e729d..3407cf7989b7 100644 --- a/arch/xtensa/include/asm/nommu_context.h +++ b/arch/xtensa/include/asm/nommu_context.h @@ -2,7 +2,7 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) { } -static inline int init_new_context(struct task_struct *tsk, struct mm_struct *mm) +static inline int init_new_context(struct task_struct *tsk,struct mm_struct *mm) { return 0; } diff --git a/arch/xtensa/include/asm/page.h b/arch/xtensa/include/asm/page.h index 7a5591a71f85..47f582333f6b 100644 --- a/arch/xtensa/include/asm/page.h +++ b/arch/xtensa/include/asm/page.h @@ -29,19 +29,19 @@ * PAGE_SHIFT determines the page size */ -#define PAGE_SHIFT 12 -#define PAGE_SIZE (__XTENSA_UL_CONST(1) << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) +#define PAGE_SHIFT 12 +#define PAGE_SIZE (__XTENSA_UL_CONST(1) << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) #ifdef CONFIG_MMU -#define PAGE_OFFSET XCHAL_KSEG_CACHED_VADDR -#define MAX_MEM_PFN XCHAL_KSEG_SIZE +#define PAGE_OFFSET XCHAL_KSEG_CACHED_VADDR +#define MAX_MEM_PFN XCHAL_KSEG_SIZE #else -#define PAGE_OFFSET 0 -#define MAX_MEM_PFN (PLATFORM_DEFAULT_MEM_START + PLATFORM_DEFAULT_MEM_SIZE) +#define PAGE_OFFSET 0 +#define MAX_MEM_PFN (PLATFORM_DEFAULT_MEM_START + PLATFORM_DEFAULT_MEM_SIZE) #endif -#define PGTABLE_START 0x80000000 +#define PGTABLE_START 0x80000000 /* * Cache aliasing: @@ -161,7 +161,9 @@ extern void copy_user_page(void*, void*, unsigned long, struct page*); #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) -#define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && ((pfn) - ARCH_PFN_OFFSET) < max_mapnr) +#define pfn_valid(pfn) \ + ((pfn) >= ARCH_PFN_OFFSET && ((pfn) - ARCH_PFN_OFFSET) < max_mapnr) + #ifdef CONFIG_DISCONTIGMEM # error CONFIG_DISCONTIGMEM not supported #endif diff --git a/arch/xtensa/include/asm/pci-bridge.h b/arch/xtensa/include/asm/pci-bridge.h index 00fcbd7c534a..0b68c76ec1e6 100644 --- a/arch/xtensa/include/asm/pci-bridge.h +++ b/arch/xtensa/include/asm/pci-bridge.h @@ -35,7 +35,7 @@ struct pci_space { struct pci_controller { int index; /* used for pci_controller_num */ struct pci_controller *next; - struct pci_bus *bus; + struct pci_bus *bus; void *arch_data; int first_busno; diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index 05244f07dd31..614be031a79a 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -53,7 +53,7 @@ struct pci_dev; /* Map a range of PCI memory or I/O space for a device into user space */ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); + enum pci_mmap_state mmap_state, int write_combine); /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ #define HAVE_PCI_MMAP 1 diff --git a/arch/xtensa/include/asm/pgalloc.h b/arch/xtensa/include/asm/pgalloc.h index 40cf9bceda2c..cf914c8c249a 100644 --- a/arch/xtensa/include/asm/pgalloc.h +++ b/arch/xtensa/include/asm/pgalloc.h @@ -42,7 +42,7 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) extern struct kmem_cache *pgtable_cache; -static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { return kmem_cache_alloc(pgtable_cache, GFP_KERNEL|__GFP_REPEAT); diff --git a/arch/xtensa/include/asm/pgtable.h b/arch/xtensa/include/asm/pgtable.h index b03c043ce75b..c90ea5bfa1b4 100644 --- a/arch/xtensa/include/asm/pgtable.h +++ b/arch/xtensa/include/asm/pgtable.h @@ -284,7 +284,7 @@ struct vm_area_struct; static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, - pte_t *ptep) + pte_t *ptep) { pte_t pte = *ptep; if (!pte_young(pte)) @@ -304,8 +304,8 @@ ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - pte_t pte = *ptep; - update_pte(ptep, pte_wrprotect(pte)); + pte_t pte = *ptep; + update_pte(ptep, pte_wrprotect(pte)); } /* to find an entry in a kernel page-table-directory */ @@ -399,7 +399,7 @@ extern void update_mmu_cache(struct vm_area_struct * vma, */ #define io_remap_pfn_range(vma,from,pfn,size,prot) \ - remap_pfn_range(vma, from, pfn, size, prot) + remap_pfn_range(vma, from, pfn, size, prot) typedef pte_t *pte_addr_t; diff --git a/arch/xtensa/include/asm/platform.h b/arch/xtensa/include/asm/platform.h index 7d936e58e9be..ec098b68fb9a 100644 --- a/arch/xtensa/include/asm/platform.h +++ b/arch/xtensa/include/asm/platform.h @@ -75,4 +75,3 @@ extern int platform_pcibios_fixup (void); extern void platform_calibrate_ccount (void); #endif /* _XTENSA_PLATFORM_H */ - diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index 2d630e7399ca..e5fb6b0abdf4 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -89,7 +89,7 @@ #define MAKE_PC_FROM_RA(ra,sp) (((ra) & 0x3fffffff) | ((sp) & 0xc0000000)) typedef struct { - unsigned long seg; + unsigned long seg; } mm_segment_t; struct thread_struct { @@ -145,10 +145,10 @@ struct thread_struct { * set_thread_state in signal.c depends on it. */ #define USER_PS_VALUE ((1 << PS_WOE_BIT) | \ - (1 << PS_CALLINC_SHIFT) | \ - (USER_RING << PS_RING_SHIFT) | \ - (1 << PS_UM_BIT) | \ - (1 << PS_EXCM_BIT)) + (1 << PS_CALLINC_SHIFT) | \ + (USER_RING << PS_RING_SHIFT) | \ + (1 << PS_UM_BIT) | \ + (1 << PS_EXCM_BIT)) /* Clearing a0 terminates the backtrace. */ #define start_thread(regs, new_pc, new_sp) \ diff --git a/arch/xtensa/include/asm/prom.h b/arch/xtensa/include/asm/prom.h new file mode 100644 index 000000000000..f3d7cd2c0de7 --- /dev/null +++ b/arch/xtensa/include/asm/prom.h @@ -0,0 +1,6 @@ +#ifndef _XTENSA_ASM_PROM_H +#define _XTENSA_ASM_PROM_H + +#define HAVE_ARCH_DEVTREE_FIXUPS + +#endif /* _XTENSA_ASM_PROM_H */ diff --git a/arch/xtensa/include/asm/ptrace.h b/arch/xtensa/include/asm/ptrace.h index da21c17f23aa..58bf6fd3f913 100644 --- a/arch/xtensa/include/asm/ptrace.h +++ b/arch/xtensa/include/asm/ptrace.h @@ -37,7 +37,7 @@ struct pt_regs { unsigned long windowstart; /* 52 */ unsigned long syscall; /* 56 */ unsigned long icountlevel; /* 60 */ - int reserved[1]; /* 64 */ + unsigned long scompare1; /* 64 */ /* Additional configurable registers that are used by the compiler. */ xtregs_opt_t xtregs_opt; @@ -55,7 +55,7 @@ struct pt_regs { # define arch_has_single_step() (1) # define task_pt_regs(tsk) ((struct pt_regs*) \ - (task_stack_page(tsk) + KERNEL_STACK_SIZE - (XCHAL_NUM_AREGS-16)*4) - 1) + (task_stack_page(tsk) + KERNEL_STACK_SIZE - (XCHAL_NUM_AREGS-16)*4) - 1) # define user_mode(regs) (((regs)->ps & 0x00000020)!=0) # define instruction_pointer(regs) ((regs)->pc) diff --git a/arch/xtensa/include/asm/regs.h b/arch/xtensa/include/asm/regs.h index 8a8aa61ccc8d..76096a4e5b8d 100644 --- a/arch/xtensa/include/asm/regs.h +++ b/arch/xtensa/include/asm/regs.h @@ -52,6 +52,10 @@ #define EXCCAUSE_SPECULATION 7 #define EXCCAUSE_PRIVILEGED 8 #define EXCCAUSE_UNALIGNED 9 +#define EXCCAUSE_INSTR_DATA_ERROR 12 +#define EXCCAUSE_LOAD_STORE_DATA_ERROR 13 +#define EXCCAUSE_INSTR_ADDR_ERROR 14 +#define EXCCAUSE_LOAD_STORE_ADDR_ERROR 15 #define EXCCAUSE_ITLB_MISS 16 #define EXCCAUSE_ITLB_MULTIHIT 17 #define EXCCAUSE_ITLB_PRIVILEGE 18 @@ -105,4 +109,3 @@ #define DEBUGCAUSE_ICOUNT_BIT 0 /* ICOUNT would incr. to zero */ #endif /* _XTENSA_SPECREG_H */ - diff --git a/arch/xtensa/include/asm/spinlock.h b/arch/xtensa/include/asm/spinlock.h index 8ff23649581b..03975906b36f 100644 --- a/arch/xtensa/include/asm/spinlock.h +++ b/arch/xtensa/include/asm/spinlock.h @@ -11,6 +11,192 @@ #ifndef _XTENSA_SPINLOCK_H #define _XTENSA_SPINLOCK_H -#include <linux/spinlock.h> +/* + * spinlock + * + * There is at most one owner of a spinlock. There are not different + * types of spinlock owners like there are for rwlocks (see below). + * + * When trying to obtain a spinlock, the function "spins" forever, or busy- + * waits, until the lock is obtained. When spinning, presumably some other + * owner will soon give up the spinlock making it available to others. Use + * the trylock functions to avoid spinning forever. + * + * possible values: + * + * 0 nobody owns the spinlock + * 1 somebody owns the spinlock + */ + +#define __raw_spin_is_locked(x) ((x)->slock != 0) +#define __raw_spin_unlock_wait(lock) \ + do { while (__raw_spin_is_locked(lock)) cpu_relax(); } while (0) + +#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) + +static inline void __raw_spin_lock(raw_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( + " movi %0, 0\n" + " wsr %0, scompare1\n" + "1: movi %0, 1\n" + " s32c1i %0, %1, 0\n" + " bnez %0, 1b\n" + : "=&a" (tmp) + : "a" (&lock->slock) + : "memory"); +} + +/* Returns 1 if the lock is obtained, 0 otherwise. */ + +static inline int __raw_spin_trylock(raw_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( + " movi %0, 0\n" + " wsr %0, scompare1\n" + " movi %0, 1\n" + " s32c1i %0, %1, 0\n" + : "=&a" (tmp) + : "a" (&lock->slock) + : "memory"); + + return tmp == 0 ? 1 : 0; +} + +static inline void __raw_spin_unlock(raw_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( + " movi %0, 0\n" + " s32ri %0, %1, 0\n" + : "=&a" (tmp) + : "a" (&lock->slock) + : "memory"); +} + +/* + * rwlock + * + * Read-write locks are really a more flexible spinlock. They allow + * multiple readers but only one writer. Write ownership is exclusive + * (i.e., all other readers and writers are blocked from ownership while + * there is a write owner). These rwlocks are unfair to writers. Writers + * can be starved for an indefinite time by readers. + * + * possible values: + * + * 0 nobody owns the rwlock + * >0 one or more readers own the rwlock + * (the positive value is the actual number of readers) + * 0x80000000 one writer owns the rwlock, no other writers, no readers + */ + +#define __raw_write_can_lock(x) ((x)->lock == 0) + +static inline void __raw_write_lock(raw_rwlock_t *rw) +{ + unsigned long tmp; + + __asm__ __volatile__( + " movi %0, 0\n" + " wsr %0, scompare1\n" + "1: movi %0, 1\n" + " slli %0, %0, 31\n" + " s32c1i %0, %1, 0\n" + " bnez %0, 1b\n" + : "=&a" (tmp) + : "a" (&rw->lock) + : "memory"); +} + +/* Returns 1 if the lock is obtained, 0 otherwise. */ + +static inline int __raw_write_trylock(raw_rwlock_t *rw) +{ + unsigned long tmp; + + __asm__ __volatile__( + " movi %0, 0\n" + " wsr %0, scompare1\n" + " movi %0, 1\n" + " slli %0, %0, 31\n" + " s32c1i %0, %1, 0\n" + : "=&a" (tmp) + : "a" (&rw->lock) + : "memory"); + + return tmp == 0 ? 1 : 0; +} + +static inline void __raw_write_unlock(raw_rwlock_t *rw) +{ + unsigned long tmp; + + __asm__ __volatile__( + " movi %0, 0\n" + " s32ri %0, %1, 0\n" + : "=&a" (tmp) + : "a" (&rw->lock) + : "memory"); +} + +static inline void __raw_read_lock(raw_rwlock_t *rw) +{ + unsigned long tmp; + unsigned long result; + + __asm__ __volatile__( + "1: l32i %1, %2, 0\n" + " bltz %1, 1b\n" + " wsr %1, scompare1\n" + " addi %0, %1, 1\n" + " s32c1i %0, %2, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (result), "=&a" (tmp) + : "a" (&rw->lock) + : "memory"); +} + +/* Returns 1 if the lock is obtained, 0 otherwise. */ + +static inline int __raw_read_trylock(raw_rwlock_t *rw) +{ + unsigned long result; + unsigned long tmp; + + __asm__ __volatile__( + " l32i %1, %2, 0\n" + " addi %0, %1, 1\n" + " bltz %0, 1f\n" + " wsr %1, scompare1\n" + " s32c1i %0, %2, 0\n" + " sub %0, %0, %1\n" + "1:\n" + : "=&a" (result), "=&a" (tmp) + : "a" (&rw->lock) + : "memory"); + + return result == 0; +} + +static inline void __raw_read_unlock(raw_rwlock_t *rw) +{ + unsigned long tmp1, tmp2; + + __asm__ __volatile__( + "1: l32i %1, %2, 0\n" + " addi %0, %1, -1\n" + " wsr %1, scompare1\n" + " s32c1i %0, %2, 0\n" + " bne %0, %1, 1b\n" + : "=&a" (tmp1), "=&a" (tmp2) + : "a" (&rw->lock) + : "memory"); +} #endif /* _XTENSA_SPINLOCK_H */ diff --git a/arch/xtensa/include/asm/syscall.h b/arch/xtensa/include/asm/syscall.h index b00c928d4cce..8d5e47fad095 100644 --- a/arch/xtensa/include/asm/syscall.h +++ b/arch/xtensa/include/asm/syscall.h @@ -25,9 +25,10 @@ asmlinkage long xtensa_fadvise64_64(int, int, /* Should probably move to linux/syscalls.h */ struct pollfd; asmlinkage long sys_pselect6(int n, fd_set __user *inp, fd_set __user *outp, - fd_set __user *exp, struct timespec __user *tsp, void __user *sig); + fd_set __user *exp, struct timespec __user *tsp, + void __user *sig); asmlinkage long sys_ppoll(struct pollfd __user *ufds, unsigned int nfds, - struct timespec __user *tsp, const sigset_t __user *sigmask, - size_t sigsetsize); -asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, - size_t sigsetsize); + struct timespec __user *tsp, + const sigset_t __user *sigmask, + size_t sigsetsize); +asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize); diff --git a/arch/xtensa/include/asm/traps.h b/arch/xtensa/include/asm/traps.h new file mode 100644 index 000000000000..54f70440185e --- /dev/null +++ b/arch/xtensa/include/asm/traps.h @@ -0,0 +1,23 @@ +/* + * arch/xtensa/include/asm/traps.h + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Tensilica Inc. + */ +#ifndef _XTENSA_TRAPS_H +#define _XTENSA_TRAPS_H + +#include <asm/ptrace.h> + +/* + * handler must be either of the following: + * void (*)(struct pt_regs *regs); + * void (*)(struct pt_regs *regs, unsigned long exccause); + */ +extern void * __init trap_set_handler(int cause, void *handler); +extern void do_unhandled(struct pt_regs *regs, unsigned long exccause); + +#endif /* _XTENSA_TRAPS_H */ diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h index 6e4bb3b791ab..fd686dc45d1a 100644 --- a/arch/xtensa/include/asm/uaccess.h +++ b/arch/xtensa/include/asm/uaccess.h @@ -180,7 +180,8 @@ #define segment_eq(a,b) ((a).seg == (b).seg) #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) -#define __user_ok(addr,size) (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) +#define __user_ok(addr,size) \ + (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) #define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size))) #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size)) @@ -234,10 +235,10 @@ do { \ int __cb; \ retval = 0; \ switch (size) { \ - case 1: __put_user_asm(x,ptr,retval,1,"s8i",__cb); break; \ - case 2: __put_user_asm(x,ptr,retval,2,"s16i",__cb); break; \ - case 4: __put_user_asm(x,ptr,retval,4,"s32i",__cb); break; \ - case 8: { \ + case 1: __put_user_asm(x,ptr,retval,1,"s8i",__cb); break; \ + case 2: __put_user_asm(x,ptr,retval,2,"s16i",__cb); break; \ + case 4: __put_user_asm(x,ptr,retval,4,"s32i",__cb); break; \ + case 8: { \ __typeof__(*ptr) __v64 = x; \ retval = __copy_to_user(ptr,&__v64,8); \ break; \ @@ -291,7 +292,7 @@ do { \ * __check_align_* macros still work. */ #define __put_user_asm(x, addr, err, align, insn, cb) \ - __asm__ __volatile__( \ +__asm__ __volatile__( \ __check_align_##align \ "1: "insn" %2, %3, 0 \n" \ "2: \n" \ @@ -301,8 +302,8 @@ do { \ " .long 2b \n" \ "5: \n" \ " l32r %1, 4b \n" \ - " movi %0, %4 \n" \ - " jx %1 \n" \ + " movi %0, %4 \n" \ + " jx %1 \n" \ " .previous \n" \ " .section __ex_table,\"a\" \n" \ " .long 1b, 5b \n" \ @@ -334,13 +335,13 @@ extern long __get_user_bad(void); do { \ int __cb; \ retval = 0; \ - switch (size) { \ - case 1: __get_user_asm(x,ptr,retval,1,"l8ui",__cb); break; \ - case 2: __get_user_asm(x,ptr,retval,2,"l16ui",__cb); break; \ - case 4: __get_user_asm(x,ptr,retval,4,"l32i",__cb); break; \ - case 8: retval = __copy_from_user(&x,ptr,8); break; \ - default: (x) = __get_user_bad(); \ - } \ + switch (size) { \ + case 1: __get_user_asm(x,ptr,retval,1,"l8ui",__cb); break; \ + case 2: __get_user_asm(x,ptr,retval,2,"l16ui",__cb); break; \ + case 4: __get_user_asm(x,ptr,retval,4,"l32i",__cb); break; \ + case 8: retval = __copy_from_user(&x,ptr,8); break; \ + default: (x) = __get_user_bad(); \ + } \ } while (0) @@ -349,7 +350,7 @@ do { \ * __check_align_* macros still work. */ #define __get_user_asm(x, addr, err, align, insn, cb) \ - __asm__ __volatile__( \ +__asm__ __volatile__( \ __check_align_##align \ "1: "insn" %2, %3, 0 \n" \ "2: \n" \ @@ -360,8 +361,8 @@ do { \ "5: \n" \ " l32r %1, 4b \n" \ " movi %2, 0 \n" \ - " movi %0, %4 \n" \ - " jx %1 \n" \ + " movi %0, %4 \n" \ + " jx %1 \n" \ " .previous \n" \ " .section __ex_table,\"a\" \n" \ " .long 1b, 5b \n" \ @@ -421,8 +422,10 @@ __generic_copy_from_user(void *to, const void *from, unsigned long n) #define copy_to_user(to,from,n) __generic_copy_to_user((to),(from),(n)) #define copy_from_user(to,from,n) __generic_copy_from_user((to),(from),(n)) -#define __copy_to_user(to,from,n) __generic_copy_to_user_nocheck((to),(from),(n)) -#define __copy_from_user(to,from,n) __generic_copy_from_user_nocheck((to),(from),(n)) +#define __copy_to_user(to,from,n) \ + __generic_copy_to_user_nocheck((to),(from),(n)) +#define __copy_from_user(to,from,n) \ + __generic_copy_from_user_nocheck((to),(from),(n)) #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index f36cef5a62ff..c3a59d992ac0 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile @@ -23,13 +23,13 @@ obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o # # Replicate rules in scripts/Makefile.build -sed-y = -e 's/\*(\(\.[a-z]*it\|\.ref\|\)\.text)/*(\1.literal \1.text)/g' \ - -e 's/\.text\.unlikely/.literal.unlikely .text.unlikely/g' \ +sed-y = -e 's/\*(\(\.[a-z]*it\|\.ref\|\)\.text)/*(\1.literal \1.text)/g' \ + -e 's/\.text\.unlikely/.literal.unlikely .text.unlikely/g' \ -e 's/\*(\(\.text\.[a-z]*\))/*(\1.literal \1)/g' quiet_cmd__cpp_lds_S = LDS $@ - cmd__cpp_lds_S = $(CPP) $(cpp_flags) -P -C -Uxtensa -D__ASSEMBLY__ $< \ - | sed $(sed-y) >$@ +cmd__cpp_lds_S = $(CPP) $(cpp_flags) -P -C -Uxtensa -D__ASSEMBLY__ $< \ + | sed $(sed-y) >$@ $(obj)/vmlinux.lds: $(src)/vmlinux.lds.S FORCE $(call if_changed_dep,_cpp_lds_S) diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S index 934ae58e2c79..aa2e87b8566a 100644 --- a/arch/xtensa/kernel/align.S +++ b/arch/xtensa/kernel/align.S @@ -442,7 +442,7 @@ ENTRY(fast_unaligned) mov a1, a2 rsr a0, ps - bbsi.l a2, PS_UM_BIT, 1f # jump if user mode + bbsi.l a2, PS_UM_BIT, 1f # jump if user mode movi a0, _kernel_exception jx a0 @@ -450,6 +450,6 @@ ENTRY(fast_unaligned) 1: movi a0, _user_exception jx a0 +ENDPROC(fast_unaligned) #endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */ - diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index 7dc3f9157185..0701fad170db 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c @@ -41,6 +41,7 @@ int main(void) DEFINE(PT_SAR, offsetof (struct pt_regs, sar)); DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel)); DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall)); + DEFINE(PT_SCOMPARE1, offsetof(struct pt_regs, scompare1)); DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0])); DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0])); DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1])); @@ -91,7 +92,8 @@ int main(void) #endif DEFINE(THREAD_XTREGS_USER, offsetof (struct thread_info, xtregs_user)); DEFINE(XTREGS_USER_SIZE, sizeof(xtregs_user_t)); - DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds)); + DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, \ + thread.current_ds)); /* struct mm_struct */ DEFINE(MM_USERS, offsetof(struct mm_struct, mm_users)); @@ -108,4 +110,3 @@ int main(void) return 0; } - diff --git a/arch/xtensa/kernel/coprocessor.S b/arch/xtensa/kernel/coprocessor.S index 54c3be313bfa..647657484866 100644 --- a/arch/xtensa/kernel/coprocessor.S +++ b/arch/xtensa/kernel/coprocessor.S @@ -43,10 +43,13 @@ /* IO protection is currently unsupported. */ ENTRY(fast_io_protect) + wsr a0, excsave1 movi a0, unrecoverable_exception callx0 a0 +ENDPROC(fast_io_protect) + #if XTENSA_HAVE_COPROCESSORS /* @@ -139,6 +142,7 @@ ENTRY(fast_io_protect) */ ENTRY(coprocessor_save) + entry a1, 32 s32i a0, a1, 0 movi a0, .Lsave_cp_regs_jump_table @@ -150,7 +154,10 @@ ENTRY(coprocessor_save) 1: l32i a0, a1, 0 retw +ENDPROC(coprocessor_save) + ENTRY(coprocessor_load) + entry a1, 32 s32i a0, a1, 0 movi a0, .Lload_cp_regs_jump_table @@ -162,8 +169,10 @@ ENTRY(coprocessor_load) 1: l32i a0, a1, 0 retw +ENDPROC(coprocessor_load) + /* - * coprocessor_flush(struct task_info*, index) + * coprocessor_flush(struct task_info*, index) * a2 a3 * coprocessor_restore(struct task_info*, index) * a2 a3 @@ -178,6 +187,7 @@ ENTRY(coprocessor_load) ENTRY(coprocessor_flush) + entry a1, 32 s32i a0, a1, 0 movi a0, .Lsave_cp_regs_jump_table @@ -191,6 +201,8 @@ ENTRY(coprocessor_flush) 1: l32i a0, a1, 0 retw +ENDPROC(coprocessor_flush) + ENTRY(coprocessor_restore) entry a1, 32 s32i a0, a1, 0 @@ -205,6 +217,8 @@ ENTRY(coprocessor_restore) 1: l32i a0, a1, 0 retw +ENDPROC(coprocessor_restore) + /* * Entry condition: * @@ -220,10 +234,12 @@ ENTRY(coprocessor_restore) */ ENTRY(fast_coprocessor_double) + wsr a0, excsave1 movi a0, unrecoverable_exception callx0 a0 +ENDPROC(fast_coprocessor_double) ENTRY(fast_coprocessor) @@ -327,9 +343,14 @@ ENTRY(fast_coprocessor) rfe +ENDPROC(fast_coprocessor) + .data + ENTRY(coprocessor_owner) + .fill XCHAL_CP_MAX, 4, 0 -#endif /* XTENSA_HAVE_COPROCESSORS */ +END(coprocessor_owner) +#endif /* XTENSA_HAVE_COPROCESSORS */ diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index 90bfc1dbc13d..3777fec85e7c 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -219,6 +219,7 @@ _user_exception: j common_exception +ENDPROC(user_exception) /* * First-level exit handler for kernel exceptions @@ -371,6 +372,13 @@ common_exception: s32i a2, a1, PT_LBEG s32i a3, a1, PT_LEND + /* Save SCOMPARE1 */ + +#if XCHAL_HAVE_S32C1I + rsr a2, scompare1 + s32i a2, a1, PT_SCOMPARE1 +#endif + /* Save optional registers. */ save_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT @@ -432,6 +440,12 @@ common_exception_return: load_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT + /* Restore SCOMPARE1 */ + +#if XCHAL_HAVE_S32C1I + l32i a2, a1, PT_SCOMPARE1 + wsr a2, scompare1 +#endif wsr a3, ps /* disable interrupts */ _bbci.l a3, PS_UM_BIT, kernel_exception_exit @@ -641,6 +655,8 @@ common_exception_exit: l32i a1, a1, PT_AREG1 rfde +ENDPROC(kernel_exception) + /* * Debug exception handler. * @@ -701,6 +717,7 @@ ENTRY(debug_exception) /* Debug exception while in exception mode. */ 1: j 1b // FIXME!! +ENDPROC(debug_exception) /* * We get here in case of an unrecoverable exception. @@ -751,6 +768,7 @@ ENTRY(unrecoverable_exception) 1: j 1b +ENDPROC(unrecoverable_exception) /* -------------------------- FAST EXCEPTION HANDLERS ----------------------- */ @@ -856,7 +874,7 @@ ENTRY(fast_alloca) _bnei a0, 1, 1f # no 'movsp a1, ax': jump - /* Move the save area. This implies the use of the L32E + /* Move the save area. This implies the use of the L32E * and S32E instructions, because this move must be done with * the user's PS.RING privilege levels, not with ring 0 * (kernel's) privileges currently active with PS.EXCM @@ -929,6 +947,7 @@ ENTRY(fast_alloca) l32i a2, a2, PT_AREG2 rfe +ENDPROC(fast_alloca) /* * fast system calls. @@ -966,6 +985,8 @@ ENTRY(fast_syscall_kernel) j kernel_exception +ENDPROC(fast_syscall_kernel) + ENTRY(fast_syscall_user) /* Skip syscall. */ @@ -983,19 +1004,21 @@ ENTRY(fast_syscall_user) j user_exception -ENTRY(fast_syscall_unrecoverable) +ENDPROC(fast_syscall_user) - /* Restore all states. */ +ENTRY(fast_syscall_unrecoverable) - l32i a0, a2, PT_AREG0 # restore a0 - xsr a2, depc # restore a2, depc - rsr a3, excsave1 + /* Restore all states. */ - wsr a0, excsave1 - movi a0, unrecoverable_exception - callx0 a0 + l32i a0, a2, PT_AREG0 # restore a0 + xsr a2, depc # restore a2, depc + rsr a3, excsave1 + wsr a0, excsave1 + movi a0, unrecoverable_exception + callx0 a0 +ENDPROC(fast_syscall_unrecoverable) /* * sysxtensa syscall handler @@ -1101,7 +1124,7 @@ CATCH movi a2, -EINVAL rfe - +ENDPROC(fast_syscall_xtensa) /* fast_syscall_spill_registers. @@ -1160,6 +1183,8 @@ ENTRY(fast_syscall_spill_registers) movi a2, 0 rfe +ENDPROC(fast_syscall_spill_registers) + /* Fixup handler. * * We get here if the spill routine causes an exception, e.g. tlb miss. @@ -1228,9 +1253,9 @@ fast_syscall_spill_registers_fixup: movi a3, exc_table rsr a0, exccause - addx4 a0, a0, a3 # find entry in table - l32i a0, a0, EXC_TABLE_FAST_USER # load handler - jx a0 + addx4 a0, a0, a3 # find entry in table + l32i a0, a0, EXC_TABLE_FAST_USER # load handler + jx a0 fast_syscall_spill_registers_fixup_return: @@ -1432,7 +1457,7 @@ ENTRY(_spill_registers) rsr a0, ps _bbci.l a0, PS_UM_BIT, 1f - /* User space: Setup a dummy frame and kill application. + /* User space: Setup a dummy frame and kill application. * Note: We assume EXC_TABLE_KSTK contains a valid stack pointer. */ @@ -1464,6 +1489,8 @@ ENTRY(_spill_registers) callx0 a0 # should not return 1: j 1b +ENDPROC(_spill_registers) + #ifdef CONFIG_MMU /* * We should never get here. Bail out! @@ -1475,6 +1502,8 @@ ENTRY(fast_second_level_miss_double_kernel) callx0 a0 # should not return 1: j 1b +ENDPROC(fast_second_level_miss_double_kernel) + /* First-level entry handler for user, kernel, and double 2nd-level * TLB miss exceptions. Note that for now, user and kernel miss * exceptions share the same entry point and are handled identically. @@ -1682,6 +1711,7 @@ ENTRY(fast_second_level_miss) j _kernel_exception 1: j _user_exception +ENDPROC(fast_second_level_miss) /* * StoreProhibitedException @@ -1777,6 +1807,9 @@ ENTRY(fast_store_prohibited) bbsi.l a2, PS_UM_BIT, 1f j _kernel_exception 1: j _user_exception + +ENDPROC(fast_store_prohibited) + #endif /* CONFIG_MMU */ /* @@ -1787,6 +1820,7 @@ ENTRY(fast_store_prohibited) */ ENTRY(system_call) + entry a1, 32 /* regs->syscall = regs->areg[2] */ @@ -1831,6 +1865,8 @@ ENTRY(system_call) callx4 a4 retw +ENDPROC(system_call) + /* * Task switch. @@ -1899,6 +1935,7 @@ ENTRY(_switch_to) retw +ENDPROC(_switch_to) ENTRY(ret_from_fork) @@ -1914,6 +1951,8 @@ ENTRY(ret_from_fork) j common_exception_return +ENDPROC(ret_from_fork) + /* * Kernel thread creation helper * On entry, set up by copy_thread: a2 = thread_fn, a3 = thread_fn arg diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index bdc50788f35e..91d9095284de 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -18,6 +18,7 @@ #include <asm/processor.h> #include <asm/page.h> #include <asm/cacheasm.h> +#include <asm/initialize_mmu.h> #include <linux/init.h> #include <linux/linkage.h> @@ -47,16 +48,19 @@ */ __HEAD - .globl _start -_start: _j 2f +ENTRY(_start) + + _j 2f .align 4 1: .word _startup 2: l32r a0, 1b jx a0 +ENDPROC(_start) + .section .init.text, "ax" - .align 4 -_startup: + +ENTRY(_startup) /* Disable interrupts and exceptions. */ @@ -107,7 +111,7 @@ _startup: /* Disable all timers. */ .set _index, 0 - .rept XCHAL_NUM_TIMERS - 1 + .rept XCHAL_NUM_TIMERS wsr a0, SREG_CCOMPARE + _index .set _index, _index + 1 .endr @@ -120,7 +124,7 @@ _startup: /* Disable coprocessors. */ -#if XCHAL_CP_NUM > 0 +#if XCHAL_HAVE_CP wsr a0, cpenable #endif @@ -152,6 +156,8 @@ _startup: isync + initialize_mmu + /* Unpack data sections * * The linker script used to build the Linux kernel image @@ -230,6 +236,7 @@ _startup: should_never_return: j should_never_return +ENDPROC(_startup) /* * BSS section @@ -239,6 +246,8 @@ __PAGE_ALIGNED_BSS #ifdef CONFIG_MMU ENTRY(swapper_pg_dir) .fill PAGE_SIZE, 1, 0 +END(swapper_pg_dir) #endif ENTRY(empty_zero_page) .fill PAGE_SIZE, 1, 0 +END(empty_zero_page) diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index a6ce3e563739..6f4f9749cff7 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -18,6 +18,8 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel_stat.h> +#include <linux/irqdomain.h> +#include <linux/of.h> #include <asm/uaccess.h> #include <asm/platform.h> @@ -26,19 +28,22 @@ static unsigned int cached_irq_mask; atomic_t irq_err_count; +static struct irq_domain *root_domain; + /* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific * handlers). */ -asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); + int irq = irq_find_mapping(root_domain, hwirq); - if (irq >= NR_IRQS) { + if (hwirq >= NR_IRQS) { printk(KERN_EMERG "%s: cannot handle IRQ %d\n", - __func__, irq); + __func__, hwirq); } irq_enter(); @@ -71,40 +76,39 @@ int arch_show_interrupts(struct seq_file *p, int prec) static void xtensa_irq_mask(struct irq_data *d) { - cached_irq_mask &= ~(1 << d->irq); + cached_irq_mask &= ~(1 << d->hwirq); set_sr (cached_irq_mask, intenable); } static void xtensa_irq_unmask(struct irq_data *d) { - cached_irq_mask |= 1 << d->irq; + cached_irq_mask |= 1 << d->hwirq; set_sr (cached_irq_mask, intenable); } static void xtensa_irq_enable(struct irq_data *d) { - variant_irq_enable(d->irq); + variant_irq_enable(d->hwirq); xtensa_irq_unmask(d); } static void xtensa_irq_disable(struct irq_data *d) { xtensa_irq_mask(d); - variant_irq_disable(d->irq); + variant_irq_disable(d->hwirq); } static void xtensa_irq_ack(struct irq_data *d) { - set_sr(1 << d->irq, intclear); + set_sr(1 << d->hwirq, intclear); } static int xtensa_irq_retrigger(struct irq_data *d) { - set_sr (1 << d->irq, INTSET); + set_sr(1 << d->hwirq, intset); return 1; } - static struct irq_chip xtensa_irq_chip = { .name = "xtensa", .irq_enable = xtensa_irq_enable, @@ -115,37 +119,99 @@ static struct irq_chip xtensa_irq_chip = { .irq_retrigger = xtensa_irq_retrigger, }; -void __init init_IRQ(void) +static int xtensa_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) { - int index; - - for (index = 0; index < XTENSA_NR_IRQS; index++) { - int mask = 1 << index; - - if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) - irq_set_chip_and_handler(index, &xtensa_irq_chip, - handle_simple_irq); + u32 mask = 1 << hw; + + if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) { + irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + handle_simple_irq, "level"); + irq_set_status_flags(irq, IRQ_LEVEL); + } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) { + irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + handle_edge_irq, "edge"); + irq_clear_status_flags(irq, IRQ_LEVEL); + } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) { + irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + handle_level_irq, "level"); + irq_set_status_flags(irq, IRQ_LEVEL); + } else if (mask & XCHAL_INTTYPE_MASK_TIMER) { + irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + handle_edge_irq, "edge"); + irq_clear_status_flags(irq, IRQ_LEVEL); + } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */ + /* XCHAL_INTTYPE_MASK_NMI */ + + irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, + handle_level_irq, "level"); + irq_set_status_flags(irq, IRQ_LEVEL); + } + return 0; +} - else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) - irq_set_chip_and_handler(index, &xtensa_irq_chip, - handle_edge_irq); +static unsigned map_ext_irq(unsigned ext_irq) +{ + unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL; + unsigned i; - else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) - irq_set_chip_and_handler(index, &xtensa_irq_chip, - handle_level_irq); + for (i = 0; mask; ++i, mask >>= 1) { + if ((mask & 1) && ext_irq-- == 0) + return i; + } + return XCHAL_NUM_INTERRUPTS; +} - else if (mask & XCHAL_INTTYPE_MASK_TIMER) - irq_set_chip_and_handler(index, &xtensa_irq_chip, - handle_edge_irq); +/* + * Device Tree IRQ specifier translation function which works with one or + * two cell bindings. First cell value maps directly to the hwirq number. + * Second cell if present specifies whether hwirq number is external (1) or + * internal (0). + */ +int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + if (WARN_ON(intsize < 1 || intsize > 2)) + return -EINVAL; + if (intsize == 2 && intspec[1] == 1) { + unsigned int_irq = map_ext_irq(intspec[0]); + if (int_irq < XCHAL_NUM_INTERRUPTS) + *out_hwirq = int_irq; + else + return -EINVAL; + } else { + *out_hwirq = intspec[0]; + } + *out_type = IRQ_TYPE_NONE; + return 0; +} - else /* XCHAL_INTTYPE_MASK_WRITE_ERROR */ - /* XCHAL_INTTYPE_MASK_NMI */ +static const struct irq_domain_ops xtensa_irq_domain_ops = { + .xlate = xtensa_irq_domain_xlate, + .map = xtensa_irq_map, +}; - irq_set_chip_and_handler(index, &xtensa_irq_chip, - handle_level_irq); - } +void __init init_IRQ(void) +{ + struct device_node *intc = NULL; cached_irq_mask = 0; + set_sr(~0, intclear); + +#ifdef CONFIG_OF + /* The interrupt controller device node is mandatory */ + intc = of_find_compatible_node(NULL, NULL, "xtensa,pic"); + BUG_ON(!intc); + + root_domain = irq_domain_add_linear(intc, NR_IRQS, + &xtensa_irq_domain_ops, NULL); +#else + root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, + &xtensa_irq_domain_ops, NULL); +#endif + irq_set_default_host(root_domain); variant_init_irq(); } diff --git a/arch/xtensa/kernel/module.c b/arch/xtensa/kernel/module.c index 451dda928c93..b715237bae61 100644 --- a/arch/xtensa/kernel/module.c +++ b/arch/xtensa/kernel/module.c @@ -53,7 +53,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, struct module *mod) { unsigned int i; - Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; Elf32_Sym *sym; unsigned char *location; uint32_t value; diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c index 97230e46cbe7..44bf21c3769a 100644 --- a/arch/xtensa/kernel/platform.c +++ b/arch/xtensa/kernel/platform.c @@ -44,4 +44,3 @@ _F(void, calibrate_ccount, (void), ccount_per_jiffy = 10 * (1000000UL/HZ); }); #endif - diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index 1accf28da5f5..0dd5784416d3 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c @@ -108,7 +108,7 @@ void coprocessor_flush_all(struct thread_info *ti) void cpu_idle(void) { - local_irq_enable(); + local_irq_enable(); /* endless idle loop with no priority at all */ while (1) { diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 33eea4c16f12..61fb2e9e9035 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -154,7 +154,7 @@ int ptrace_setxregs(struct task_struct *child, void __user *uregs) coprocessor_flush_all(ti); coprocessor_release_all(ti); - ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0, + ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0, sizeof(xtregs_coprocessor_t)); #endif ret |= __copy_from_user(®s->xtregs_opt, &xtregs->opt, @@ -343,4 +343,3 @@ void do_syscall_trace_leave(struct pt_regs *regs) && (current->ptrace & PT_PTRACED)) do_syscall_trace(); } - diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index b237988ba6d7..24c1a57abb40 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -22,6 +22,11 @@ #include <linux/bootmem.h> #include <linux/kernel.h> +#ifdef CONFIG_OF +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#endif + #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) # include <linux/console.h> #endif @@ -42,6 +47,7 @@ #include <asm/page.h> #include <asm/setup.h> #include <asm/param.h> +#include <asm/traps.h> #include <platform/hardware.h> @@ -64,6 +70,11 @@ int initrd_is_mapped = 0; extern int initrd_below_start_ok; #endif +#ifdef CONFIG_OF +extern u32 __dtb_start[]; +void *dtb_start = __dtb_start; +#endif + unsigned char aux_device_present; extern unsigned long loops_per_jiffy; @@ -83,6 +94,8 @@ extern void init_mmu(void); static inline void init_mmu(void) { } #endif +extern int mem_reserve(unsigned long, unsigned long, int); +extern void bootmem_init(void); extern void zones_init(void); /* @@ -104,28 +117,33 @@ typedef struct tagtable { /* parse current tag */ -static int __init parse_tag_mem(const bp_tag_t *tag) +static int __init add_sysmem_bank(unsigned long type, unsigned long start, + unsigned long end) { - meminfo_t *mi = (meminfo_t*)(tag->data); - - if (mi->type != MEMORY_TYPE_CONVENTIONAL) - return -1; - if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) { printk(KERN_WARNING - "Ignoring memory bank 0x%08lx size %ldKB\n", - (unsigned long)mi->start, - (unsigned long)mi->end - (unsigned long)mi->start); + "Ignoring memory bank 0x%08lx size %ldKB\n", + start, end - start); return -EINVAL; } - sysmem.bank[sysmem.nr_banks].type = mi->type; - sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start); - sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_MASK; + sysmem.bank[sysmem.nr_banks].type = type; + sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(start); + sysmem.bank[sysmem.nr_banks].end = end & PAGE_MASK; sysmem.nr_banks++; return 0; } +static int __init parse_tag_mem(const bp_tag_t *tag) +{ + meminfo_t *mi = (meminfo_t *)(tag->data); + + if (mi->type != MEMORY_TYPE_CONVENTIONAL) + return -1; + + return add_sysmem_bank(mi->type, mi->start, mi->end); +} + __tagtable(BP_TAG_MEMORY, parse_tag_mem); #ifdef CONFIG_BLK_DEV_INITRD @@ -142,12 +160,31 @@ static int __init parse_tag_initrd(const bp_tag_t* tag) __tagtable(BP_TAG_INITRD, parse_tag_initrd); +#ifdef CONFIG_OF + +static int __init parse_tag_fdt(const bp_tag_t *tag) +{ + dtb_start = (void *)(tag->data[0]); + return 0; +} + +__tagtable(BP_TAG_FDT, parse_tag_fdt); + +void __init early_init_dt_setup_initrd_arch(unsigned long start, + unsigned long end) +{ + initrd_start = (void *)__va(start); + initrd_end = (void *)__va(end); + initrd_below_start_ok = 1; +} + +#endif /* CONFIG_OF */ + #endif /* CONFIG_BLK_DEV_INITRD */ static int __init parse_tag_cmdline(const bp_tag_t* tag) { - strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE); - command_line[COMMAND_LINE_SIZE - 1] = '\0'; + strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE); return 0; } @@ -185,6 +222,58 @@ static int __init parse_bootparam(const bp_tag_t* tag) return 0; } +#ifdef CONFIG_OF + +void __init early_init_dt_add_memory_arch(u64 base, u64 size) +{ + size &= PAGE_MASK; + add_sysmem_bank(MEMORY_TYPE_CONVENTIONAL, base, base + size); +} + +void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) +{ + return __alloc_bootmem(size, align, 0); +} + +void __init early_init_devtree(void *params) +{ + /* Setup flat device-tree pointer */ + initial_boot_params = params; + + /* Retrieve various informations from the /chosen node of the + * device-tree, including the platform type, initrd location and + * size, TCE reserve, and more ... + */ + if (!command_line[0]) + of_scan_flat_dt(early_init_dt_scan_chosen, command_line); + + /* Scan memory nodes and rebuild MEMBLOCKs */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + if (sysmem.nr_banks == 0) + of_scan_flat_dt(early_init_dt_scan_memory, NULL); +} + +static void __init copy_devtree(void) +{ + void *alloc = early_init_dt_alloc_memory_arch( + be32_to_cpu(initial_boot_params->totalsize), 0); + if (alloc) { + memcpy(alloc, initial_boot_params, + be32_to_cpu(initial_boot_params->totalsize)); + initial_boot_params = alloc; + } +} + +static int __init xtensa_device_probe(void) +{ + of_platform_populate(NULL, NULL, NULL, NULL); + return 0; +} + +device_initcall(xtensa_device_probe); + +#endif /* CONFIG_OF */ + /* * Initialize architecture. (Early stage) */ @@ -193,14 +282,14 @@ void __init init_arch(bp_tag_t *bp_start) { sysmem.nr_banks = 0; -#ifdef CONFIG_CMDLINE_BOOL - strcpy(command_line, default_command_line); -#endif - /* Parse boot parameters */ - if (bp_start) - parse_bootparam(bp_start); + if (bp_start) + parse_bootparam(bp_start); + +#ifdef CONFIG_OF + early_init_devtree(dtb_start); +#endif if (sysmem.nr_banks == 0) { sysmem.nr_banks = 1; @@ -209,6 +298,11 @@ void __init init_arch(bp_tag_t *bp_start) + PLATFORM_DEFAULT_MEM_SIZE; } +#ifdef CONFIG_CMDLINE_BOOL + if (!command_line[0]) + strlcpy(command_line, default_command_line, COMMAND_LINE_SIZE); +#endif + /* Early hook for platforms */ platform_init(bp_start); @@ -235,15 +329,130 @@ extern char _UserExceptionVector_text_end; extern char _DoubleExceptionVector_literal_start; extern char _DoubleExceptionVector_text_end; -void __init setup_arch(char **cmdline_p) + +#ifdef CONFIG_S32C1I_SELFTEST +#if XCHAL_HAVE_S32C1I + +static int __initdata rcw_word, rcw_probe_pc, rcw_exc; + +/* + * Basic atomic compare-and-swap, that records PC of S32C1I for probing. + * + * If *v == cmp, set *v = set. Return previous *v. + */ +static inline int probed_compare_swap(int *v, int cmp, int set) +{ + int tmp; + + __asm__ __volatile__( + " movi %1, 1f\n" + " s32i %1, %4, 0\n" + " wsr %2, scompare1\n" + "1: s32c1i %0, %3, 0\n" + : "=a" (set), "=&a" (tmp) + : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set) + : "memory" + ); + return set; +} + +/* Handle probed exception */ + +void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause) +{ + if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */ + regs->pc += 3; /* skip the s32c1i instruction */ + rcw_exc = exccause; + } else { + do_unhandled(regs, exccause); + } +} + +/* Simple test of S32C1I (soc bringup assist) */ + +void __init check_s32c1i(void) +{ + int n, cause1, cause2; + void *handbus, *handdata, *handaddr; /* temporarily saved handlers */ + + rcw_probe_pc = 0; + handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, + do_probed_exception); + handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, + do_probed_exception); + handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, + do_probed_exception); + + /* First try an S32C1I that does not store: */ + rcw_exc = 0; + rcw_word = 1; + n = probed_compare_swap(&rcw_word, 0, 2); + cause1 = rcw_exc; + + /* took exception? */ + if (cause1 != 0) { + /* unclean exception? */ + if (n != 2 || rcw_word != 1) + panic("S32C1I exception error"); + } else if (rcw_word != 1 || n != 1) { + panic("S32C1I compare error"); + } + + /* Then an S32C1I that stores: */ + rcw_exc = 0; + rcw_word = 0x1234567; + n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde); + cause2 = rcw_exc; + + if (cause2 != 0) { + /* unclean exception? */ + if (n != 0xabcde || rcw_word != 0x1234567) + panic("S32C1I exception error (b)"); + } else if (rcw_word != 0xabcde || n != 0x1234567) { + panic("S32C1I store error"); + } + + /* Verify consistency of exceptions: */ + if (cause1 || cause2) { + pr_warn("S32C1I took exception %d, %d\n", cause1, cause2); + /* If emulation of S32C1I upon bus error gets implemented, + we can get rid of this panic for single core (not SMP) */ + panic("S32C1I exceptions not currently supported"); + } + if (cause1 != cause2) + panic("inconsistent S32C1I exceptions"); + + trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus); + trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata); + trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr); +} + +#else /* XCHAL_HAVE_S32C1I */ + +/* This condition should not occur with a commercially deployed processor. + Display reminder for early engr test or demo chips / FPGA bitstreams */ +void __init check_s32c1i(void) +{ + pr_warn("Processor configuration lacks atomic compare-and-swap support!\n"); +} + +#endif /* XCHAL_HAVE_S32C1I */ +#else /* CONFIG_S32C1I_SELFTEST */ + +void __init check_s32c1i(void) { - extern int mem_reserve(unsigned long, unsigned long, int); - extern void bootmem_init(void); +} + +#endif /* CONFIG_S32C1I_SELFTEST */ - memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; + +void __init setup_arch(char **cmdline_p) +{ + strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; + check_s32c1i(); + /* Reserve some memory regions */ #ifdef CONFIG_BLK_DEV_INITRD @@ -251,7 +460,7 @@ void __init setup_arch(char **cmdline_p) initrd_is_mapped = mem_reserve(__pa(initrd_start), __pa(initrd_end), 0); initrd_below_start_ok = 1; - } else { + } else { initrd_start = 0; } #endif @@ -275,8 +484,12 @@ void __init setup_arch(char **cmdline_p) bootmem_init(); - platform_setup(cmdline_p); +#ifdef CONFIG_OF + copy_devtree(); + unflatten_device_tree(); +#endif + platform_setup(cmdline_p); paging_init(); zones_init(); @@ -326,7 +539,7 @@ c_show(struct seq_file *f, void *slot) "core ID\t\t: " XCHAL_CORE_ID "\n" "build ID\t: 0x%x\n" "byte order\t: %s\n" - "cpu MHz\t\t: %lu.%02lu\n" + "cpu MHz\t\t: %lu.%02lu\n" "bogomips\t: %lu.%02lu\n", XCHAL_BUILD_UNIQUE_ID, XCHAL_HAVE_BE ? "big" : "little", @@ -381,6 +594,9 @@ c_show(struct seq_file *f, void *slot) #if XCHAL_HAVE_FP "fpu " #endif +#if XCHAL_HAVE_S32C1I + "s32c1i " +#endif "\n"); /* Registers. */ @@ -412,7 +628,7 @@ c_show(struct seq_file *f, void *slot) "icache size\t: %d\n" "icache flags\t: " #if XCHAL_ICACHE_LINE_LOCKABLE - "lock" + "lock " #endif "\n" "dcache line size: %d\n" @@ -420,10 +636,10 @@ c_show(struct seq_file *f, void *slot) "dcache size\t: %d\n" "dcache flags\t: " #if XCHAL_DCACHE_IS_WRITEBACK - "writeback" + "writeback " #endif #if XCHAL_DCACHE_LINE_LOCKABLE - "lock" + "lock " #endif "\n", XCHAL_ICACHE_LINESIZE, @@ -465,4 +681,3 @@ const struct seq_operations cpuinfo_op = }; #endif /* CONFIG_PROC_FS */ - diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index 63c566f627bc..de34d6be91cd 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -212,7 +212,7 @@ restore_sigcontext(struct pt_regs *regs, struct rt_sigframe __user *frame) if (err) return err; - /* The signal handler may have used coprocessors in which + /* The signal handler may have used coprocessors in which * case they are still enabled. We disable them to force a * reloading of the original task's CP state by the lazy * context-switching mechanisms of CP exception handling. @@ -396,7 +396,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info, */ /* Set up registers for signal handler */ - start_thread(regs, (unsigned long) ka->sa.sa_handler, + start_thread(regs, (unsigned long) ka->sa.sa_handler, (unsigned long) frame); /* Set up a stack frame for a call4 @@ -424,9 +424,9 @@ give_sigsegv: return -EFAULT; } -asmlinkage long xtensa_sigaltstack(const stack_t __user *uss, +asmlinkage long xtensa_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, - long a2, long a3, long a4, long a5, + long a2, long a3, long a4, long a5, struct pt_regs *regs) { return do_sigaltstack(uss, uoss, regs->areg[1]); diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c index 5702065f472a..54fa8425cee2 100644 --- a/arch/xtensa/kernel/syscall.c +++ b/arch/xtensa/kernel/syscall.c @@ -52,4 +52,3 @@ asmlinkage long xtensa_fadvise64_64(int fd, int advice, { return sys_fadvise64_64(fd, offset, len, advice); } - diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index ac62f9cf1e10..ffb474104311 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -22,6 +22,7 @@ #include <linux/irq.h> #include <linux/profile.h> #include <linux/delay.h> +#include <linux/irqdomain.h> #include <asm/timex.h> #include <asm/platform.h> @@ -31,7 +32,7 @@ unsigned long ccount_per_jiffy; /* per 1/HZ */ unsigned long nsec_per_ccount; /* nsec per ccount increment */ #endif -static cycle_t ccount_read(void) +static cycle_t ccount_read(struct clocksource *cs) { return (cycle_t)get_ccount(); } @@ -52,6 +53,7 @@ static struct irqaction timer_irqaction = { void __init time_init(void) { + unsigned int irq; #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT printk("Calibrating CPU frequency "); platform_calibrate_ccount(); @@ -62,7 +64,8 @@ void __init time_init(void) /* Initialize the linux timer interrupt. */ - setup_irq(LINUX_TIMER_INT, &timer_irqaction); + irq = irq_create_mapping(NULL, LINUX_TIMER_INT); + setup_irq(irq, &timer_irqaction); set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY); } diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 5caf2b64d43a..01e0111bf787 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -293,6 +293,17 @@ do_debug(struct pt_regs *regs) } +/* Set exception C handler - for temporary use when probing exceptions */ + +void * __init trap_set_handler(int cause, void *handler) +{ + unsigned long *entry = &exc_table[EXC_TABLE_DEFAULT / 4 + cause]; + void *previous = (void *)*entry; + *entry = (unsigned long)handler; + return previous; +} + + /* * Initialize dispatch tables. * @@ -397,7 +408,8 @@ static inline void spill_registers(void) "wsr a13, sar\n\t" "wsr a14, ps\n\t" :: "a" (&a0), "a" (&ps) - : "a2", "a3", "a4", "a7", "a11", "a12", "a13", "a14", "a15", "memory"); + : "a2", "a3", "a4", "a7", "a11", "a12", "a13", "a14", "a15", + "memory"); } void show_trace(struct task_struct *task, unsigned long *sp) @@ -452,7 +464,7 @@ void show_stack(struct task_struct *task, unsigned long *sp) if (!sp) sp = stack_pointer(task); - stack = sp; + stack = sp; printk("\nStack: "); @@ -523,5 +535,3 @@ void die(const char * str, struct pt_regs * regs, long err) do_exit(err); } - - diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S index 4462c1e595c2..68df35f66ce3 100644 --- a/arch/xtensa/kernel/vectors.S +++ b/arch/xtensa/kernel/vectors.S @@ -79,6 +79,8 @@ ENTRY(_UserExceptionVector) l32i a0, a0, EXC_TABLE_FAST_USER # load handler jx a0 +ENDPROC(_UserExceptionVector) + /* * Kernel exception vector. (Exceptions with PS.UM == 0, PS.EXCM == 0) * @@ -103,6 +105,7 @@ ENTRY(_KernelExceptionVector) l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler address jx a0 +ENDPROC(_KernelExceptionVector) /* * Double exception vector (Exceptions with PS.EXCM == 1) @@ -225,7 +228,13 @@ ENTRY(_DoubleExceptionVector) /* Window overflow/underflow exception. Get stack pointer. */ mov a3, a2 - movi a2, exc_table + /* This explicit literal and the following references to it are made + * in order to fit DoubleExceptionVector.literals into the available + * 16-byte gap before DoubleExceptionVector.text in the absence of + * link time relaxation. See kernel/vmlinux.lds.S + */ + .literal .Lexc_table, exc_table + l32r a2, .Lexc_table l32i a2, a2, EXC_TABLE_KSTK /* Check for overflow/underflow exception, jump if overflow. */ @@ -255,7 +264,7 @@ ENTRY(_DoubleExceptionVector) s32i a0, a2, PT_AREG0 wsr a3, excsave1 # save a3 - movi a3, exc_table + l32r a3, .Lexc_table rsr a0, exccause s32i a0, a2, PT_DEPC # mark it as a regular exception @@ -267,7 +276,7 @@ ENTRY(_DoubleExceptionVector) /* a0: depc, a1: a1, a2: a2, a3: trashed, depc: a0, excsave1: a3 */ - movi a3, exc_table + l32r a3, .Lexc_table s32i a2, a3, EXC_TABLE_DOUBLE_SAVE # temporary variable /* Enter critical section. */ @@ -296,7 +305,7 @@ ENTRY(_DoubleExceptionVector) /* a0: avail, a1: a1, a2: kstk, a3: avail, depc: a2, excsave: a3 */ - movi a3, exc_table + l32r a3, .Lexc_table rsr a0, exccause addx4 a0, a0, a3 l32i a0, a0, EXC_TABLE_FAST_USER @@ -338,6 +347,7 @@ ENTRY(_DoubleExceptionVector) .end literal_prefix +ENDPROC(_DoubleExceptionVector) /* * Debug interrupt vector @@ -349,9 +359,11 @@ ENTRY(_DoubleExceptionVector) .section .DebugInterruptVector.text, "ax" ENTRY(_DebugInterruptVector) + xsr a0, SREG_EXCSAVE + XCHAL_DEBUGLEVEL jx a0 +ENDPROC(_DebugInterruptVector) /* Window overflow and underflow handlers. @@ -363,38 +375,43 @@ ENTRY(_DebugInterruptVector) * we try to access any page that would cause a page fault early. */ +#define ENTRY_ALIGN64(name) \ + .globl name; \ + .align 64; \ + name: + .section .WindowVectors.text, "ax" /* 4-Register Window Overflow Vector (Handler) */ - .align 64 -.global _WindowOverflow4 -_WindowOverflow4: +ENTRY_ALIGN64(_WindowOverflow4) + s32e a0, a5, -16 s32e a1, a5, -12 s32e a2, a5, -8 s32e a3, a5, -4 rfwo +ENDPROC(_WindowOverflow4) + /* 4-Register Window Underflow Vector (Handler) */ - .align 64 -.global _WindowUnderflow4 -_WindowUnderflow4: +ENTRY_ALIGN64(_WindowUnderflow4) + l32e a0, a5, -16 l32e a1, a5, -12 l32e a2, a5, -8 l32e a3, a5, -4 rfwu +ENDPROC(_WindowUnderflow4) /* 8-Register Window Overflow Vector (Handler) */ - .align 64 -.global _WindowOverflow8 -_WindowOverflow8: +ENTRY_ALIGN64(_WindowOverflow8) + s32e a0, a9, -16 l32e a0, a1, -12 s32e a2, a9, -8 @@ -406,11 +423,12 @@ _WindowOverflow8: s32e a7, a0, -20 rfwo +ENDPROC(_WindowOverflow8) + /* 8-Register Window Underflow Vector (Handler) */ - .align 64 -.global _WindowUnderflow8 -_WindowUnderflow8: +ENTRY_ALIGN64(_WindowUnderflow8) + l32e a1, a9, -12 l32e a0, a9, -16 l32e a7, a1, -12 @@ -422,12 +440,12 @@ _WindowUnderflow8: l32e a7, a7, -20 rfwu +ENDPROC(_WindowUnderflow8) /* 12-Register Window Overflow Vector (Handler) */ - .align 64 -.global _WindowOverflow12 -_WindowOverflow12: +ENTRY_ALIGN64(_WindowOverflow12) + s32e a0, a13, -16 l32e a0, a1, -12 s32e a1, a13, -12 @@ -443,11 +461,12 @@ _WindowOverflow12: s32e a11, a0, -20 rfwo +ENDPROC(_WindowOverflow12) + /* 12-Register Window Underflow Vector (Handler) */ - .align 64 -.global _WindowUnderflow12 -_WindowUnderflow12: +ENTRY_ALIGN64(_WindowUnderflow12) + l32e a1, a13, -12 l32e a0, a13, -16 l32e a11, a1, -12 @@ -463,6 +482,6 @@ _WindowUnderflow12: l32e a11, a11, -20 rfwu - .text - +ENDPROC(_WindowUnderflow12) + .text diff --git a/arch/xtensa/lib/checksum.S b/arch/xtensa/lib/checksum.S index df397f932d0e..4eb573d2720e 100644 --- a/arch/xtensa/lib/checksum.S +++ b/arch/xtensa/lib/checksum.S @@ -41,10 +41,11 @@ .text ENTRY(csum_partial) - /* - * Experiments with Ethernet and SLIP connections show that buf - * is aligned on either a 2-byte or 4-byte boundary. - */ + + /* + * Experiments with Ethernet and SLIP connections show that buf + * is aligned on either a 2-byte or 4-byte boundary. + */ entry sp, 32 extui a5, a2, 0, 2 bnez a5, 8f /* branch if 2-byte aligned */ @@ -170,7 +171,7 @@ ENTRY(csum_partial) 3: j 5b /* branch to handle the remaining byte */ - +ENDPROC(csum_partial) /* * Copy from ds while checksumming, otherwise like csum_partial @@ -211,6 +212,7 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, */ ENTRY(csum_partial_copy_generic) + entry sp, 32 mov a12, a3 mov a11, a4 @@ -367,6 +369,8 @@ DST( s8i a8, a3, 1 ) 6: j 4b /* process the possible trailing odd byte */ +ENDPROC(csum_partial_copy_generic) + # Exception handler: .section .fixup, "ax" @@ -406,4 +410,3 @@ DST( s8i a8, a3, 1 ) retw .previous - diff --git a/arch/xtensa/lib/memcopy.S b/arch/xtensa/lib/memcopy.S index c48b80acb5f0..b1c219acabe7 100644 --- a/arch/xtensa/lib/memcopy.S +++ b/arch/xtensa/lib/memcopy.S @@ -210,8 +210,10 @@ memcpy: _beqz a4, .Ldone # avoid loading anything for zero-length copies # copy 16 bytes per iteration for word-aligned dst and unaligned src ssa8 a3 # set shift amount from byte offset -#define SIM_CHECKS_ALIGNMENT 1 /* set to 1 when running on ISS (simulator) with the - lint or ferret client, or 0 to save a few cycles */ + +/* set to 1 when running on ISS (simulator) with the + lint or ferret client, or 0 to save a few cycles */ +#define SIM_CHECKS_ALIGNMENT 1 #if XCHAL_UNALIGNED_LOAD_EXCEPTION || SIM_CHECKS_ALIGNMENT and a11, a3, a8 # save unalignment offset for below sub a3, a3, a11 # align a3 diff --git a/arch/xtensa/lib/pci-auto.c b/arch/xtensa/lib/pci-auto.c index a71733ae1193..34d05abbd921 100644 --- a/arch/xtensa/lib/pci-auto.c +++ b/arch/xtensa/lib/pci-auto.c @@ -241,8 +241,8 @@ int __init pciauto_bus_scan(struct pci_controller *pci_ctrl, int current_bus) unsigned char header_type; struct pci_dev *dev = &pciauto_dev; - pciauto_dev.bus = &pciauto_bus; - pciauto_dev.sysdata = pci_ctrl; + pciauto_dev.bus = &pciauto_bus; + pciauto_dev.sysdata = pci_ctrl; pciauto_bus.ops = pci_ctrl->ops; /* @@ -345,8 +345,3 @@ int __init pciauto_bus_scan(struct pci_controller *pci_ctrl, int current_bus) } return sub_bus; } - - - - - diff --git a/arch/xtensa/lib/strncpy_user.S b/arch/xtensa/lib/strncpy_user.S index 9f603cdaaa68..1ad0ecf45368 100644 --- a/arch/xtensa/lib/strncpy_user.S +++ b/arch/xtensa/lib/strncpy_user.S @@ -166,7 +166,7 @@ __strncpy_user: retw .Lz1: # byte 1 is zero #ifdef __XTENSA_EB__ - extui a9, a9, 16, 16 + extui a9, a9, 16, 16 #endif /* __XTENSA_EB__ */ EX(s16i, a9, a11, 0, fixup_s) addi a11, a11, 1 # advance dst pointer @@ -174,7 +174,7 @@ __strncpy_user: retw .Lz2: # byte 2 is zero #ifdef __XTENSA_EB__ - extui a9, a9, 16, 16 + extui a9, a9, 16, 16 #endif /* __XTENSA_EB__ */ EX(s16i, a9, a11, 0, fixup_s) movi a9, 0 diff --git a/arch/xtensa/lib/strnlen_user.S b/arch/xtensa/lib/strnlen_user.S index 23f2a89816a1..4c03b1e581e9 100644 --- a/arch/xtensa/lib/strnlen_user.S +++ b/arch/xtensa/lib/strnlen_user.S @@ -145,4 +145,3 @@ __strnlen_user: lenfixup: movi a2, 0 retw - diff --git a/arch/xtensa/lib/usercopy.S b/arch/xtensa/lib/usercopy.S index 46d60314bb16..ace1892a875e 100644 --- a/arch/xtensa/lib/usercopy.S +++ b/arch/xtensa/lib/usercopy.S @@ -318,4 +318,3 @@ l_fixup: /* Ignore memset return value in a6. */ /* a2 still contains bytes not copied. */ retw - diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c index 85df4655d326..81edeab82d17 100644 --- a/arch/xtensa/mm/cache.c +++ b/arch/xtensa/mm/cache.c @@ -118,7 +118,7 @@ void flush_dcache_page(struct page *page) * For now, flush the whole cache. FIXME?? */ -void flush_cache_range(struct vm_area_struct* vma, +void flush_cache_range(struct vm_area_struct* vma, unsigned long start, unsigned long end) { __flush_invalidate_dcache_all(); @@ -133,7 +133,7 @@ void flush_cache_range(struct vm_area_struct* vma, */ void flush_cache_page(struct vm_area_struct* vma, unsigned long address, - unsigned long pfn) + unsigned long pfn) { /* Note that we have to use the 'alias' address to avoid multi-hit */ @@ -166,14 +166,14 @@ update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep) if (!PageReserved(page) && test_bit(PG_arch_1, &page->flags)) { - unsigned long vaddr = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK); unsigned long paddr = (unsigned long) page_address(page); unsigned long phys = page_to_phys(page); + unsigned long tmp = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK); __flush_invalidate_dcache_page(paddr); - __flush_invalidate_dcache_page_alias(vaddr, phys); - __invalidate_icache_page_alias(vaddr, phys); + __flush_invalidate_dcache_page_alias(tmp, phys); + __invalidate_icache_page_alias(tmp, phys); clear_bit(PG_arch_1, &page->flags); } @@ -195,7 +195,7 @@ update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep) #if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK -void copy_to_user_page(struct vm_area_struct *vma, struct page *page, +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, const void *src, unsigned long len) { @@ -205,8 +205,8 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page, /* Flush and invalidate user page if aliased. */ if (alias) { - unsigned long temp = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK); - __flush_invalidate_dcache_page_alias(temp, phys); + unsigned long t = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK); + __flush_invalidate_dcache_page_alias(t, phys); } /* Copy data */ @@ -219,12 +219,11 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page, */ if (alias) { - unsigned long temp = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK); + unsigned long t = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK); __flush_invalidate_dcache_range((unsigned long) dst, len); - if ((vma->vm_flags & VM_EXEC) != 0) { - __invalidate_icache_page_alias(temp, phys); - } + if ((vma->vm_flags & VM_EXEC) != 0) + __invalidate_icache_page_alias(t, phys); } else if ((vma->vm_flags & VM_EXEC) != 0) { __flush_dcache_range((unsigned long)dst,len); @@ -245,8 +244,8 @@ extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page, */ if (alias) { - unsigned long temp = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK); - __flush_invalidate_dcache_page_alias(temp, phys); + unsigned long t = TLBTEMP_BASE_1 + (vaddr & DCACHE_ALIAS_MASK); + __flush_invalidate_dcache_page_alias(t, phys); } memcpy(dst, src, len); diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index 245b08f7eaf4..4b7bc8db170f 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -254,4 +254,3 @@ bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) die("Oops", regs, sig); do_exit(sig); } - diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index db955179da2d..7a5156ffebb6 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -75,15 +75,15 @@ int __init mem_reserve(unsigned long start, unsigned long end, int must_exist) sysmem.nr_banks++; } sysmem.bank[i].end = start; + + } else if (end < sysmem.bank[i].end) { + sysmem.bank[i].start = end; + } else { - if (end < sysmem.bank[i].end) - sysmem.bank[i].start = end; - else { - /* remove entry */ - sysmem.nr_banks--; - sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start; - sysmem.bank[i].end = sysmem.bank[sysmem.nr_banks].end; - } + /* remove entry */ + sysmem.nr_banks--; + sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start; + sysmem.bank[i].end = sysmem.bank[sysmem.nr_banks].end; } return -1; } diff --git a/arch/xtensa/mm/misc.S b/arch/xtensa/mm/misc.S index b048406d8756..d97ed1ba7b0a 100644 --- a/arch/xtensa/mm/misc.S +++ b/arch/xtensa/mm/misc.S @@ -29,6 +29,7 @@ */ ENTRY(clear_page) + entry a1, 16 movi a3, 0 @@ -45,6 +46,8 @@ ENTRY(clear_page) retw +ENDPROC(clear_page) + /* * copy_page and copy_user_page are the same for non-cache-aliased configs. * @@ -53,6 +56,7 @@ ENTRY(clear_page) */ ENTRY(copy_page) + entry a1, 16 __loopi a2, a4, PAGE_SIZE, 32 @@ -84,6 +88,8 @@ ENTRY(copy_page) retw +ENDPROC(copy_page) + #ifdef CONFIG_MMU /* * If we have to deal with cache aliasing, we use temporary memory mappings @@ -109,6 +115,7 @@ ENTRY(__tlbtemp_mapping_start) */ ENTRY(clear_user_page) + entry a1, 32 /* Mark page dirty and determine alias. */ @@ -164,6 +171,8 @@ ENTRY(clear_user_page) retw +ENDPROC(clear_user_page) + /* * copy_page_user (void *to, void *from, unsigned long vaddr, struct page *page) * a2 a3 a4 a5 @@ -171,7 +180,7 @@ ENTRY(clear_user_page) ENTRY(copy_user_page) - entry a1, 32 + entry a1, 32 /* Mark page dirty and determine alias for destination. */ @@ -262,6 +271,8 @@ ENTRY(copy_user_page) retw +ENDPROC(copy_user_page) + #endif #if (DCACHE_WAY_SIZE > PAGE_SIZE) @@ -272,6 +283,7 @@ ENTRY(copy_user_page) */ ENTRY(__flush_invalidate_dcache_page_alias) + entry sp, 16 movi a7, 0 # required for exception handler @@ -287,6 +299,7 @@ ENTRY(__flush_invalidate_dcache_page_alias) retw +ENDPROC(__flush_invalidate_dcache_page_alias) #endif ENTRY(__tlbtemp_mapping_itlb) @@ -294,6 +307,7 @@ ENTRY(__tlbtemp_mapping_itlb) #if (ICACHE_WAY_SIZE > PAGE_SIZE) ENTRY(__invalidate_icache_page_alias) + entry sp, 16 addi a6, a3, (PAGE_KERNEL_EXEC | _PAGE_HW_WRITE) @@ -307,11 +321,14 @@ ENTRY(__invalidate_icache_page_alias) isync retw +ENDPROC(__invalidate_icache_page_alias) + #endif /* End of special treatment in tlb miss exception */ ENTRY(__tlbtemp_mapping_end) + #endif /* CONFIG_MMU /* @@ -319,6 +336,7 @@ ENTRY(__tlbtemp_mapping_end) */ ENTRY(__invalidate_icache_page) + entry sp, 16 ___invalidate_icache_page a2 a3 @@ -326,11 +344,14 @@ ENTRY(__invalidate_icache_page) retw +ENDPROC(__invalidate_icache_page) + /* * void __invalidate_dcache_page(ulong start) */ ENTRY(__invalidate_dcache_page) + entry sp, 16 ___invalidate_dcache_page a2 a3 @@ -338,11 +359,14 @@ ENTRY(__invalidate_dcache_page) retw +ENDPROC(__invalidate_dcache_page) + /* * void __flush_invalidate_dcache_page(ulong start) */ ENTRY(__flush_invalidate_dcache_page) + entry sp, 16 ___flush_invalidate_dcache_page a2 a3 @@ -350,11 +374,14 @@ ENTRY(__flush_invalidate_dcache_page) dsync retw +ENDPROC(__flush_invalidate_dcache_page) + /* * void __flush_dcache_page(ulong start) */ ENTRY(__flush_dcache_page) + entry sp, 16 ___flush_dcache_page a2 a3 @@ -362,11 +389,14 @@ ENTRY(__flush_dcache_page) dsync retw +ENDPROC(__flush_dcache_page) + /* * void __invalidate_icache_range(ulong start, ulong size) */ ENTRY(__invalidate_icache_range) + entry sp, 16 ___invalidate_icache_range a2 a3 a4 @@ -374,11 +404,14 @@ ENTRY(__invalidate_icache_range) retw +ENDPROC(__invalidate_icache_range) + /* * void __flush_invalidate_dcache_range(ulong start, ulong size) */ ENTRY(__flush_invalidate_dcache_range) + entry sp, 16 ___flush_invalidate_dcache_range a2 a3 a4 @@ -386,11 +419,14 @@ ENTRY(__flush_invalidate_dcache_range) retw +ENDPROC(__flush_invalidate_dcache_range) + /* * void _flush_dcache_range(ulong start, ulong size) */ ENTRY(__flush_dcache_range) + entry sp, 16 ___flush_dcache_range a2 a3 a4 @@ -398,22 +434,28 @@ ENTRY(__flush_dcache_range) retw +ENDPROC(__flush_dcache_range) + /* * void _invalidate_dcache_range(ulong start, ulong size) */ ENTRY(__invalidate_dcache_range) + entry sp, 16 ___invalidate_dcache_range a2 a3 a4 retw +ENDPROC(__invalidate_dcache_range) + /* * void _invalidate_icache_all(void) */ ENTRY(__invalidate_icache_all) + entry sp, 16 ___invalidate_icache_all a2 a3 @@ -421,11 +463,14 @@ ENTRY(__invalidate_icache_all) retw +ENDPROC(__invalidate_icache_all) + /* * void _flush_invalidate_dcache_all(void) */ ENTRY(__flush_invalidate_dcache_all) + entry sp, 16 ___flush_invalidate_dcache_all a2 a3 @@ -433,11 +478,14 @@ ENTRY(__flush_invalidate_dcache_all) retw +ENDPROC(__flush_invalidate_dcache_all) + /* * void _invalidate_dcache_all(void) */ ENTRY(__invalidate_dcache_all) + entry sp, 16 ___invalidate_dcache_all a2 a3 @@ -445,3 +493,4 @@ ENTRY(__invalidate_dcache_all) retw +ENDPROC(__invalidate_dcache_all) diff --git a/arch/xtensa/mm/mmu.c b/arch/xtensa/mm/mmu.c index ca81654f3ec2..0f77f9d3bb8b 100644 --- a/arch/xtensa/mm/mmu.c +++ b/arch/xtensa/mm/mmu.c @@ -37,7 +37,7 @@ void __init init_mmu(void) /* Set rasid register to a known value. */ - set_rasid_register(ASID_USER_FIRST); + set_rasid_register(ASID_INSERT(ASID_USER_FIRST)); /* Set PTEVADDR special register to the start of the page * table, which is in kernel mappable space (ie. not diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c index e2700b21395b..5411aa67c68e 100644 --- a/arch/xtensa/mm/tlb.c +++ b/arch/xtensa/mm/tlb.c @@ -63,7 +63,7 @@ void flush_tlb_all (void) void flush_tlb_mm(struct mm_struct *mm) { if (mm == current->active_mm) { - int flags; + unsigned long flags; local_save_flags(flags); __get_new_mmu_context(mm); __load_mmu_context(mm); @@ -82,7 +82,7 @@ void flush_tlb_mm(struct mm_struct *mm) #endif void flush_tlb_range (struct vm_area_struct *vma, - unsigned long start, unsigned long end) + unsigned long start, unsigned long end) { struct mm_struct *mm = vma->vm_mm; unsigned long flags; @@ -100,7 +100,7 @@ void flush_tlb_range (struct vm_area_struct *vma, int oldpid = get_rasid_register(); set_rasid_register (ASID_INSERT(mm->context)); start &= PAGE_MASK; - if (vma->vm_flags & VM_EXEC) + if (vma->vm_flags & VM_EXEC) while(start < end) { invalidate_itlb_mapping(start); invalidate_dtlb_mapping(start); @@ -130,7 +130,7 @@ void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) local_save_flags(flags); - oldpid = get_rasid_register(); + oldpid = get_rasid_register(); if (vma->vm_flags & VM_EXEC) invalidate_itlb_mapping(page); @@ -140,4 +140,3 @@ void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) local_irq_restore(flags); } - diff --git a/arch/xtensa/platforms/iss/include/platform/serial.h b/arch/xtensa/platforms/iss/include/platform/serial.h index e69de29bb2d1..16aec542d435 100644 --- a/arch/xtensa/platforms/iss/include/platform/serial.h +++ b/arch/xtensa/platforms/iss/include/platform/serial.h @@ -0,0 +1,15 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Tensilica Inc. + */ + +#ifndef __ASM_XTENSA_ISS_SERIAL_H +#define __ASM_XTENSA_ISS_SERIAL_H + +/* Have no meaning on ISS, but needed for 8250_early.c */ +#define BASE_BAUD 0 + +#endif /* __ASM_XTENSA_ISS_SERIAL_H */ diff --git a/arch/xtensa/platforms/iss/include/platform/simcall.h b/arch/xtensa/platforms/iss/include/platform/simcall.h index bd78192e2fc9..b5a4edf02d76 100644 --- a/arch/xtensa/platforms/iss/include/platform/simcall.h +++ b/arch/xtensa/platforms/iss/include/platform/simcall.h @@ -74,13 +74,12 @@ static inline int __simc(int a, int b, int c, int d, int e, int f) "mov %1, a3\n" : "=a" (ret), "=a" (errno), "+r"(a1), "+r"(b1) : "r"(c1), "r"(d1), "r"(e1), "r"(f1) - : ); + : "memory"); return ret; } static inline int simc_open(const char *file, int flags, int mode) { - wmb(); return __simc(SYS_open, (int) file, flags, mode, 0, 0); } @@ -91,19 +90,16 @@ static inline int simc_close(int fd) static inline int simc_ioctl(int fd, int request, void *arg) { - wmb(); return __simc(SYS_ioctl, fd, request, (int) arg, 0, 0); } static inline int simc_read(int fd, void *buf, size_t count) { - rmb(); return __simc(SYS_read, fd, (int) buf, count, 0, 0); } static inline int simc_write(int fd, const void *buf, size_t count) { - wmb(); return __simc(SYS_write, fd, (int) buf, count, 0, 0); } @@ -111,7 +107,6 @@ static inline int simc_poll(int fd) { struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; - wmb(); return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&tv, 0, 0); } diff --git a/arch/xtensa/platforms/xtfpga/Makefile b/arch/xtensa/platforms/xtfpga/Makefile new file mode 100644 index 000000000000..b9ae206340cd --- /dev/null +++ b/arch/xtensa/platforms/xtfpga/Makefile @@ -0,0 +1,9 @@ +# Makefile for the Tensilica xtavnet Emulation Board +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are in the main makefile... + +obj-y = setup.o lcd.o diff --git a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h new file mode 100644 index 000000000000..4416773cbde5 --- /dev/null +++ b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h @@ -0,0 +1,69 @@ +/* + * arch/xtensa/platform/xtavnet/include/platform/hardware.h + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2006 Tensilica Inc. + */ + +/* + * This file contains the hardware configuration of the XTAVNET boards. + */ + +#ifndef __XTENSA_XTAVNET_HARDWARE_H +#define __XTENSA_XTAVNET_HARDWARE_H + +/* By default NO_IRQ is defined to 0 in Linux, but we use the + interrupt 0 for UART... */ +#define NO_IRQ -1 + +/* Memory configuration. */ + +#define PLATFORM_DEFAULT_MEM_START 0x00000000 +#define PLATFORM_DEFAULT_MEM_SIZE 0x04000000 + +/* Interrupt configuration. */ + +#define PLATFORM_NR_IRQS 10 + +/* Default assignment of LX60 devices to external interrupts. */ + +#ifdef CONFIG_ARCH_HAS_SMP +#define DUART16552_INTNUM XCHAL_EXTINT3_NUM +#define OETH_IRQ XCHAL_EXTINT4_NUM +#else +#define DUART16552_INTNUM XCHAL_EXTINT0_NUM +#define OETH_IRQ XCHAL_EXTINT1_NUM +#endif + +/* + * Device addresses and parameters. + */ + +/* UART */ +#define DUART16552_PADDR (XCHAL_KIO_PADDR + 0x0D050020) +/* LCD instruction and data addresses. */ +#define LCD_INSTR_ADDR ((char *)IOADDR(0x0D040000)) +#define LCD_DATA_ADDR ((char *)IOADDR(0x0D040004)) + +/* Misc. */ +#define XTFPGA_FPGAREGS_VADDR IOADDR(0x0D020000) +/* Clock frequency in Hz (read-only): */ +#define XTFPGA_CLKFRQ_VADDR (XTFPGA_FPGAREGS_VADDR + 0x04) +/* Setting of 8 DIP switches: */ +#define DIP_SWITCHES_VADDR (XTFPGA_FPGAREGS_VADDR + 0x0C) +/* Software reset (write 0xdead): */ +#define XTFPGA_SWRST_VADDR (XTFPGA_FPGAREGS_VADDR + 0x10) + +/* OpenCores Ethernet controller: */ + /* regs + RX/TX descriptors */ +#define OETH_REGS_PADDR (XCHAL_KIO_PADDR + 0x0D030000) +#define OETH_REGS_SIZE 0x1000 +#define OETH_SRAMBUFF_PADDR (XCHAL_KIO_PADDR + 0x0D800000) + + /* 5*rx buffs + 5*tx buffs */ +#define OETH_SRAMBUFF_SIZE (5 * 0x600 + 5 * 0x600) + +#endif /* __XTENSA_XTAVNET_HARDWARE_H */ diff --git a/arch/xtensa/platforms/xtfpga/include/platform/lcd.h b/arch/xtensa/platforms/xtfpga/include/platform/lcd.h new file mode 100644 index 000000000000..0e435645af5a --- /dev/null +++ b/arch/xtensa/platforms/xtfpga/include/platform/lcd.h @@ -0,0 +1,20 @@ +/* + * arch/xtensa/platform/xtavnet/include/platform/lcd.h + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001, 2006 Tensilica Inc. + */ + +#ifndef __XTENSA_XTAVNET_LCD_H +#define __XTENSA_XTAVNET_LCD_H + +/* Display string STR at position POS on the LCD. */ +void lcd_disp_at_pos(char *str, unsigned char pos); + +/* Shift the contents of the LCD display left or right. */ +void lcd_shiftleft(void); +void lcd_shiftright(void); +#endif diff --git a/arch/xtensa/platforms/xtfpga/include/platform/serial.h b/arch/xtensa/platforms/xtfpga/include/platform/serial.h new file mode 100644 index 000000000000..14d8f7beebfd --- /dev/null +++ b/arch/xtensa/platforms/xtfpga/include/platform/serial.h @@ -0,0 +1,18 @@ +/* + * arch/xtensa/platform/xtavnet/include/platform/serial.h + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001, 2006 Tensilica Inc. + */ + +#ifndef __ASM_XTENSA_XTAVNET_SERIAL_H +#define __ASM_XTENSA_XTAVNET_SERIAL_H + +#include <platform/hardware.h> + +#define BASE_BAUD (*(long *)XTFPGA_CLKFRQ_VADDR / 16) + +#endif /* __ASM_XTENSA_XTAVNET_SERIAL_H */ diff --git a/arch/xtensa/platforms/xtfpga/lcd.c b/arch/xtensa/platforms/xtfpga/lcd.c new file mode 100644 index 000000000000..2872301598df --- /dev/null +++ b/arch/xtensa/platforms/xtfpga/lcd.c @@ -0,0 +1,76 @@ +/* + * Driver for the LCD display on the Tensilica LX60 Board. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001, 2006 Tensilica Inc. + */ + +/* + * + * FIXME: this code is from the examples from the LX60 user guide. + * + * The lcd_pause function does busy waiting, which is probably not + * great. Maybe the code could be changed to use kernel timers, or + * change the hardware to not need to wait. + */ + +#include <linux/init.h> +#include <linux/io.h> + +#include <platform/hardware.h> +#include <platform/lcd.h> +#include <linux/delay.h> + +#define LCD_PAUSE_ITERATIONS 4000 +#define LCD_CLEAR 0x1 +#define LCD_DISPLAY_ON 0xc + +/* 8bit and 2 lines display */ +#define LCD_DISPLAY_MODE8BIT 0x38 +#define LCD_DISPLAY_POS 0x80 +#define LCD_SHIFT_LEFT 0x18 +#define LCD_SHIFT_RIGHT 0x1c + +static int __init lcd_init(void) +{ + *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT; + mdelay(5); + *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT; + udelay(200); + *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT; + udelay(50); + *LCD_INSTR_ADDR = LCD_DISPLAY_ON; + udelay(50); + *LCD_INSTR_ADDR = LCD_CLEAR; + mdelay(10); + lcd_disp_at_pos("XTENSA LINUX", 0); + return 0; +} + +void lcd_disp_at_pos(char *str, unsigned char pos) +{ + *LCD_INSTR_ADDR = LCD_DISPLAY_POS | pos; + udelay(100); + while (*str != 0) { + *LCD_DATA_ADDR = *str; + udelay(200); + str++; + } +} + +void lcd_shiftleft(void) +{ + *LCD_INSTR_ADDR = LCD_SHIFT_LEFT; + udelay(50); +} + +void lcd_shiftright(void) +{ + *LCD_INSTR_ADDR = LCD_SHIFT_RIGHT; + udelay(50); +} + +arch_initcall(lcd_init); diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c new file mode 100644 index 000000000000..4b9951a4569d --- /dev/null +++ b/arch/xtensa/platforms/xtfpga/setup.c @@ -0,0 +1,301 @@ +/* + * + * arch/xtensa/platform/xtavnet/setup.c + * + * ... + * + * Authors: Chris Zankel <chris@zankel.net> + * Joe Taylor <joe@tensilica.com> + * + * Copyright 2001 - 2006 Tensilica Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/kdev_t.h> +#include <linux/types.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/of.h> + +#include <asm/timex.h> +#include <asm/processor.h> +#include <asm/platform.h> +#include <asm/bootparam.h> +#include <platform/lcd.h> +#include <platform/hardware.h> + +void platform_halt(void) +{ + lcd_disp_at_pos(" HALT ", 0); + local_irq_disable(); + while (1) + cpu_relax(); +} + +void platform_power_off(void) +{ + lcd_disp_at_pos("POWEROFF", 0); + local_irq_disable(); + while (1) + cpu_relax(); +} + +void platform_restart(void) +{ + /* Flush and reset the mmu, simulate a processor reset, and + * jump to the reset vector. */ + + + __asm__ __volatile__ ("movi a2, 15\n\t" + "wsr a2, icountlevel\n\t" + "movi a2, 0\n\t" + "wsr a2, icount\n\t" + "wsr a2, ibreakenable\n\t" + "wsr a2, lcount\n\t" + "movi a2, 0x1f\n\t" + "wsr a2, ps\n\t" + "isync\n\t" + "jx %0\n\t" + : + : "a" (XCHAL_RESET_VECTOR_VADDR) + : "a2" + ); + + /* control never gets here */ +} + +void __init platform_setup(char **cmdline) +{ +} + +#ifdef CONFIG_OF + +static void __init update_clock_frequency(struct device_node *node) +{ + struct property *newfreq; + u32 freq; + + if (!of_property_read_u32(node, "clock-frequency", &freq) && freq != 0) + return; + + newfreq = kzalloc(sizeof(*newfreq) + sizeof(u32), GFP_KERNEL); + if (!newfreq) + return; + newfreq->value = newfreq + 1; + newfreq->length = sizeof(freq); + newfreq->name = kstrdup("clock-frequency", GFP_KERNEL); + if (!newfreq->name) { + kfree(newfreq); + return; + } + + *(u32 *)newfreq->value = cpu_to_be32(*(u32 *)XTFPGA_CLKFRQ_VADDR); + prom_update_property(node, newfreq); +} + +#define MAC_LEN 6 +static void __init update_local_mac(struct device_node *node) +{ + struct property *newmac; + const u8* macaddr; + int prop_len; + + macaddr = of_get_property(node, "local-mac-address", &prop_len); + if (macaddr == NULL || prop_len != MAC_LEN) + return; + + newmac = kzalloc(sizeof(*newmac) + MAC_LEN, GFP_KERNEL); + if (newmac == NULL) + return; + + newmac->value = newmac + 1; + newmac->length = MAC_LEN; + newmac->name = kstrdup("local-mac-address", GFP_KERNEL); + if (newmac->name == NULL) { + kfree(newmac); + return; + } + + memcpy(newmac->value, macaddr, MAC_LEN); + ((u8*)newmac->value)[5] = (*(u32*)DIP_SWITCHES_VADDR) & 0x3f; + prom_update_property(node, newmac); +} + +static int __init machine_setup(void) +{ + struct device_node *serial; + struct device_node *eth = NULL; + + for_each_compatible_node(serial, NULL, "ns16550a") + update_clock_frequency(serial); + + if ((eth = of_find_compatible_node(eth, NULL, "opencores,ethoc"))) + update_local_mac(eth); + return 0; +} +arch_initcall(machine_setup); + +#endif + +/* early initialization */ + +void __init platform_init(bp_tag_t *first) +{ +} + +/* Heartbeat. */ + +void platform_heartbeat(void) +{ +} + +#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT + +void platform_calibrate_ccount(void) +{ + long clk_freq = 0; +#ifdef CONFIG_OF + struct device_node *cpu = + of_find_compatible_node(NULL, NULL, "xtensa,cpu"); + if (cpu) { + u32 freq; + update_clock_frequency(cpu); + if (!of_property_read_u32(cpu, "clock-frequency", &freq)) + clk_freq = freq; + } +#endif + if (!clk_freq) + clk_freq = *(long *)XTFPGA_CLKFRQ_VADDR; + + ccount_per_jiffy = clk_freq / HZ; + nsec_per_ccount = 1000000000UL / clk_freq; +} + +#endif + +#ifndef CONFIG_OF + +#include <linux/serial_8250.h> +#include <linux/if.h> +#include <net/ethoc.h> + +/*---------------------------------------------------------------------------- + * Ethernet -- OpenCores Ethernet MAC (ethoc driver) + */ + +static struct resource ethoc_res[] __initdata = { + [0] = { /* register space */ + .start = OETH_REGS_PADDR, + .end = OETH_REGS_PADDR + OETH_REGS_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { /* buffer space */ + .start = OETH_SRAMBUFF_PADDR, + .end = OETH_SRAMBUFF_PADDR + OETH_SRAMBUFF_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { /* IRQ number */ + .start = OETH_IRQ, + .end = OETH_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct ethoc_platform_data ethoc_pdata __initdata = { + /* + * The MAC address for these boards is 00:50:c2:13:6f:xx. + * The last byte (here as zero) is read from the DIP switches on the + * board. + */ + .hwaddr = { 0x00, 0x50, 0xc2, 0x13, 0x6f, 0 }, + .phy_id = -1, +}; + +static struct platform_device ethoc_device __initdata = { + .name = "ethoc", + .id = -1, + .num_resources = ARRAY_SIZE(ethoc_res), + .resource = ethoc_res, + .dev = { + .platform_data = ðoc_pdata, + }, +}; + +/*---------------------------------------------------------------------------- + * UART + */ + +static struct resource serial_resource __initdata = { + .start = DUART16552_PADDR, + .end = DUART16552_PADDR + 0x1f, + .flags = IORESOURCE_MEM, +}; + +static struct plat_serial8250_port serial_platform_data[] __initdata = { + [0] = { + .mapbase = DUART16552_PADDR, + .irq = DUART16552_INTNUM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | + UPF_IOREMAP, + .iotype = UPIO_MEM32, + .regshift = 2, + .uartclk = 0, /* set in xtavnet_init() */ + }, + { }, +}; + +static struct platform_device xtavnet_uart __initdata = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = serial_platform_data, + }, + .num_resources = 1, + .resource = &serial_resource, +}; + +/* platform devices */ +static struct platform_device *platform_devices[] __initdata = { + ðoc_device, + &xtavnet_uart, +}; + + +static int __init xtavnet_init(void) +{ + /* Ethernet MAC address. */ + ethoc_pdata.hwaddr[5] = *(u32 *)DIP_SWITCHES_VADDR; + + /* Clock rate varies among FPGA bitstreams; board specific FPGA register + * reports the actual clock rate. + */ + serial_platform_data[0].uartclk = *(long *)XTFPGA_CLKFRQ_VADDR; + + + /* register platform devices */ + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); + + /* ETHOC driver is a bit quiet; at least display Ethernet MAC, so user + * knows whether they set it correctly on the DIP switches. + */ + pr_info("XTFPGA: Ethernet MAC %pM\n", ethoc_pdata.hwaddr); + + return 0; +} + +/* + * Register to be done during do_initcalls(). + */ +arch_initcall(xtavnet_init); + +#endif /* CONFIG_OF */ diff --git a/arch/xtensa/variants/s6000/gpio.c b/arch/xtensa/variants/s6000/gpio.c index b89541ba39ab..da9e85c13b08 100644 --- a/arch/xtensa/variants/s6000/gpio.c +++ b/arch/xtensa/variants/s6000/gpio.c @@ -164,7 +164,7 @@ static void demux_irqs(unsigned int irq, struct irq_desc *desc) int cirq; chip->irq_mask(&desc->irq_data); - chip->irq_ack(&desc->irq_data)); + chip->irq_ack(&desc->irq_data); pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask; cirq = IRQ_BASE - 1; while (pending) { @@ -173,7 +173,7 @@ static void demux_irqs(unsigned int irq, struct irq_desc *desc) pending >>= n; generic_handle_irq(cirq); } - chip->irq_unmask(&desc->irq_data)); + chip->irq_unmask(&desc->irq_data); } extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS]; diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index e162999bf916..c62c788b3289 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -13,12 +13,13 @@ #include <linux/export.h> #include <linux/bcma/bcma.h> -static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset) +u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset) { bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); } +EXPORT_SYMBOL_GPL(bcma_chipco_pll_read); void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) { diff --git a/drivers/char/random.c b/drivers/char/random.c index b86eae9b77df..85e81ec1451e 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -399,7 +399,6 @@ static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); static struct fasync_struct *fasync; -#if 0 static bool debug; module_param(debug, bool, 0644); #define DEBUG_ENT(fmt, arg...) do { \ @@ -410,9 +409,6 @@ module_param(debug, bool, 0644); blocking_pool.entropy_count,\ nonblocking_pool.entropy_count,\ ## arg); } while (0) -#else -#define DEBUG_ENT(fmt, arg...) do {} while (0) -#endif /********************************************************************** * @@ -437,6 +433,7 @@ struct entropy_store { int entropy_count; int entropy_total; unsigned int initialized:1; + bool last_data_init; __u8 last_data[EXTRACT_SIZE]; }; @@ -829,7 +826,7 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) bytes = min_t(int, bytes, sizeof(tmp)); DEBUG_ENT("going to reseed %s with %d bits " - "(%d of %d requested)\n", + "(%zu of %d requested)\n", r->name, bytes * 8, nbytes * 8, r->entropy_count); bytes = extract_entropy(r->pull, tmp, bytes, @@ -860,7 +857,7 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, spin_lock_irqsave(&r->lock, flags); BUG_ON(r->entropy_count > r->poolinfo->POOLBITS); - DEBUG_ENT("trying to extract %d bits from %s\n", + DEBUG_ENT("trying to extract %zu bits from %s\n", nbytes * 8, r->name); /* Can we pull enough? */ @@ -882,7 +879,7 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, } } - DEBUG_ENT("debiting %d entropy credits from %s%s\n", + DEBUG_ENT("debiting %zu entropy credits from %s%s\n", nbytes * 8, r->name, r->limit ? "" : " (unlimited)"); spin_unlock_irqrestore(&r->lock, flags); @@ -957,6 +954,10 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, ssize_t ret = 0, i; __u8 tmp[EXTRACT_SIZE]; + /* if last_data isn't primed, we need EXTRACT_SIZE extra bytes */ + if (fips_enabled && !r->last_data_init) + nbytes += EXTRACT_SIZE; + trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_); xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, min, reserved); @@ -967,6 +968,17 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, if (fips_enabled) { unsigned long flags; + + /* prime last_data value if need be, per fips 140-2 */ + if (!r->last_data_init) { + spin_lock_irqsave(&r->lock, flags); + memcpy(r->last_data, tmp, EXTRACT_SIZE); + r->last_data_init = true; + nbytes -= EXTRACT_SIZE; + spin_unlock_irqrestore(&r->lock, flags); + extract_buf(r, tmp); + } + spin_lock_irqsave(&r->lock, flags); if (!memcmp(tmp, r->last_data, EXTRACT_SIZE)) panic("Hardware RNG duplicated output!\n"); @@ -1086,6 +1098,7 @@ static void init_std_data(struct entropy_store *r) r->entropy_count = 0; r->entropy_total = 0; + r->last_data_init = false; mix_pool_bytes(r, &now, sizeof(now), NULL); for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) { if (!arch_get_random_long(&rv)) @@ -1142,11 +1155,16 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) if (n > SEC_XFER_SIZE) n = SEC_XFER_SIZE; - DEBUG_ENT("reading %d bits\n", n*8); + DEBUG_ENT("reading %zu bits\n", n*8); n = extract_entropy_user(&blocking_pool, buf, n); - DEBUG_ENT("read got %d bits (%d still needed)\n", + if (n < 0) { + retval = n; + break; + } + + DEBUG_ENT("read got %zd bits (%zd still needed)\n", n*8, (nbytes-n)*8); if (n == 0) { @@ -1171,10 +1189,6 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) continue; } - if (n < 0) { - retval = n; - break; - } count += n; buf += n; nbytes -= n; diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c index 517a8ff7121e..6b4c70f7d23d 100644 --- a/drivers/clk/clk-nomadik.c +++ b/drivers/clk/clk-nomadik.c @@ -20,6 +20,7 @@ void __init nomadik_clk_init(void) clk_register_clkdev(clk, NULL, "gpio.2"); clk_register_clkdev(clk, NULL, "gpio.3"); clk_register_clkdev(clk, NULL, "rng"); + clk_register_clkdev(clk, NULL, "fsmc-nand"); /* * The 2.4 MHz TIMCLK reference clock is active at boot time, this is diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index a9151337d5b9..33d20be87db5 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -579,7 +579,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, * at this point the buffer should be dead, so * no new sync objects can be attached. */ - sync_obj = driver->sync_obj_ref(&bo->sync_obj); + sync_obj = driver->sync_obj_ref(bo->sync_obj); spin_unlock(&bdev->fence_lock); atomic_set(&bo->reserved, 0); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index d6cc77a53c7e..5f306f79da0c 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -921,6 +921,7 @@ static int __init i8042_platform_init(void) int retval; #ifdef CONFIG_X86 + u8 a20_on = 0xdf; /* Just return if pre-detection shows no i8042 controller exist */ if (!x86_platform.i8042_detect()) return -ENODEV; @@ -960,6 +961,14 @@ static int __init i8042_platform_init(void) if (dmi_check_system(i8042_dmi_dritek_table)) i8042_dritek = true; + + /* + * A20 was already enabled during early kernel init. But some buggy + * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to + * resume from S3. So we do it here and hope that nothing breaks. + */ + i8042_command(&a20_on, 0x10d1); + i8042_command(NULL, 0x00ff); /* Null command for SMM firmware */ #endif /* CONFIG_X86 */ return retval; diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c index 945393129952..7c057a05adb6 100644 --- a/drivers/mtd/ar7part.c +++ b/drivers/mtd/ar7part.c @@ -26,19 +26,16 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/bootmem.h> -#include <linux/magic.h> #include <linux/module.h> +#include <uapi/linux/magic.h> + #define AR7_PARTS 4 #define ROOT_OFFSET 0xe0000 #define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42) #define LOADER_MAGIC2 le32_to_cpu(0xfeed1281) -#ifndef SQUASHFS_MAGIC -#define SQUASHFS_MAGIC 0x73717368 -#endif - struct ar7_bin_rec { unsigned int checksum; unsigned int length; diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index 63d2a64331f7..6eeb84c81bc2 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -37,8 +37,7 @@ #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */ -#define BCM63XX_MIN_CFE_SIZE 0x10000 /* always at least 64KiB */ -#define BCM63XX_MIN_NVRAM_SIZE 0x10000 /* always at least 64KiB */ +#define BCM63XX_CFE_BLOCK_SIZE 0x10000 /* always at least 64KiB */ #define BCM63XX_CFE_MAGIC_OFFSET 0x4e0 @@ -79,7 +78,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, unsigned int rootfsaddr, kerneladdr, spareaddr; unsigned int rootfslen, kernellen, sparelen, totallen; unsigned int cfelen, nvramlen; - int namelen = 0; + unsigned int cfe_erasesize; int i; u32 computed_crc; bool rootfs_first = false; @@ -87,8 +86,11 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, if (bcm63xx_detect_cfe(master)) return -EINVAL; - cfelen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_CFE_SIZE); - nvramlen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_NVRAM_SIZE); + cfe_erasesize = max_t(uint32_t, master->erasesize, + BCM63XX_CFE_BLOCK_SIZE); + + cfelen = cfe_erasesize; + nvramlen = cfe_erasesize; /* Allocate memory for buffer */ buf = vmalloc(sizeof(struct bcm_tag)); @@ -121,7 +123,6 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE; spareaddr = roundup(totallen, master->erasesize) + cfelen; - sparelen = master->size - spareaddr - nvramlen; if (rootfsaddr < kerneladdr) { /* default Broadcom layout */ @@ -139,19 +140,15 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, rootfslen = 0; rootfsaddr = 0; spareaddr = cfelen; - sparelen = master->size - cfelen - nvramlen; } + sparelen = master->size - spareaddr - nvramlen; /* Determine number of partitions */ - namelen = 8; - if (rootfslen > 0) { + if (rootfslen > 0) nrparts++; - namelen += 6; - } - if (kernellen > 0) { + + if (kernellen > 0) nrparts++; - namelen += 6; - } /* Ask kernel for more memory */ parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); @@ -193,17 +190,16 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, parts[curpart].name = "nvram"; parts[curpart].offset = master->size - nvramlen; parts[curpart].size = nvramlen; + curpart++; /* Global partition "linux" to make easy firmware upgrade */ - curpart++; parts[curpart].name = "linux"; parts[curpart].offset = cfelen; parts[curpart].size = master->size - cfelen - nvramlen; for (i = 0; i < nrparts; i++) - pr_info("Partition %d is %s offset %lx and length %lx\n", i, - parts[i].name, (long unsigned int)(parts[i].offset), - (long unsigned int)(parts[i].size)); + pr_info("Partition %d is %s offset %llx and length %llx\n", i, + parts[i].name, parts[i].offset, parts[i].size); pr_info("Spare partition is offset %x and length %x\n", spareaddr, sparelen); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 5ff5c4a16943..b86197286f24 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1536,8 +1536,20 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, UDELAY(map, chip, adr, 1); } - /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); + /* + * Recovery from write-buffer programming failures requires + * the write-to-buffer-reset sequence. Since the last part + * of the sequence also works as a normal reset, we can run + * the same commands regardless of why we are here. + * See e.g. + * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf + */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, + cfi->device_type, NULL); + cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); xip_enable(map, chip, adr); /* FIXME - should have reset delay before continuing */ diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index aed1b8a63c9f..c533f27d863f 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -56,8 +56,8 @@ /* special size referring to all the remaining space in a partition */ -#define SIZE_REMAINING UINT_MAX -#define OFFSET_CONTINUOUS UINT_MAX +#define SIZE_REMAINING ULLONG_MAX +#define OFFSET_CONTINUOUS ULLONG_MAX struct cmdline_mtd_partition { struct cmdline_mtd_partition *next; @@ -89,7 +89,7 @@ static struct mtd_partition * newpart(char *s, int extra_mem_size) { struct mtd_partition *parts; - unsigned long size, offset = OFFSET_CONTINUOUS; + unsigned long long size, offset = OFFSET_CONTINUOUS; char *name; int name_len; unsigned char *extra_mem; @@ -104,7 +104,8 @@ static struct mtd_partition * newpart(char *s, } else { size = memparse(s, &s); if (size < PAGE_SIZE) { - printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); + printk(KERN_ERR ERRP "partition size too small (%llx)\n", + size); return ERR_PTR(-EINVAL); } } @@ -296,7 +297,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) { - unsigned long offset; + unsigned long long offset; int i, err; struct cmdline_mtd_partition *part; const char *mtd_id = master->name; @@ -308,48 +309,52 @@ static int parse_cmdline_partitions(struct mtd_info *master, return err; } + /* + * Search for the partition definition matching master->name. + * If master->name is not set, stop at first partition definition. + */ for (part = partitions; part; part = part->next) { - if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) { - for (i = 0, offset = 0; i < part->num_parts; i++) { - if (part->parts[i].offset == OFFSET_CONTINUOUS) - part->parts[i].offset = offset; - else - offset = part->parts[i].offset; - - if (part->parts[i].size == SIZE_REMAINING) - part->parts[i].size = master->size - offset; - - if (part->parts[i].size == 0) { - printk(KERN_WARNING ERRP - "%s: skipping zero sized partition\n", - part->mtd_id); - part->num_parts--; - memmove(&part->parts[i], - &part->parts[i + 1], - sizeof(*part->parts) * (part->num_parts - i)); - continue; - } - - if (offset + part->parts[i].size > master->size) { - printk(KERN_WARNING ERRP - "%s: partitioning exceeds flash size, truncating\n", - part->mtd_id); - part->parts[i].size = master->size - offset; - } - offset += part->parts[i].size; - } - - *pparts = kmemdup(part->parts, - sizeof(*part->parts) * part->num_parts, - GFP_KERNEL); - if (!*pparts) - return -ENOMEM; - - return part->num_parts; + if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) + break; + } + + if (!part) + return 0; + + for (i = 0, offset = 0; i < part->num_parts; i++) { + if (part->parts[i].offset == OFFSET_CONTINUOUS) + part->parts[i].offset = offset; + else + offset = part->parts[i].offset; + + if (part->parts[i].size == SIZE_REMAINING) + part->parts[i].size = master->size - offset; + + if (part->parts[i].size == 0) { + printk(KERN_WARNING ERRP + "%s: skipping zero sized partition\n", + part->mtd_id); + part->num_parts--; + memmove(&part->parts[i], &part->parts[i + 1], + sizeof(*part->parts) * (part->num_parts - i)); + continue; } + + if (offset + part->parts[i].size > master->size) { + printk(KERN_WARNING ERRP + "%s: partitioning exceeds flash size, truncating\n", + part->mtd_id); + part->parts[i].size = master->size - offset; + } + offset += part->parts[i].size; } - return 0; + *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, + GFP_KERNEL); + if (!*pparts) + return -ENOMEM; + + return part->num_parts; } diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 2dc5a6f3fd57..4714584aa993 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -66,7 +66,7 @@ out: return err; } -static int __devexit bcm47xxsflash_remove(struct platform_device *pdev) +static int bcm47xxsflash_remove(struct platform_device *pdev) { struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); @@ -77,7 +77,7 @@ static int __devexit bcm47xxsflash_remove(struct platform_device *pdev) } static struct platform_driver bcma_sflash_driver = { - .remove = __devexit_p(bcm47xxsflash_remove), + .remove = bcm47xxsflash_remove, .driver = { .name = "bcma_sflash", .owner = THIS_MODULE, diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 681e2ee0f2d6..e081bfeaaf7d 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -62,6 +62,7 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) memset(page_address(page), 0xff, PAGE_SIZE); set_page_dirty(page); unlock_page(page); + balance_dirty_pages_ratelimited(mapping); break; } @@ -152,6 +153,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, memcpy(page_address(page) + offset, buf, cpylen); set_page_dirty(page); unlock_page(page); + balance_dirty_pages_ratelimited(mapping); } page_cache_release(page); @@ -433,7 +435,7 @@ static int __init block2mtd_init(void) } -static void __devexit block2mtd_exit(void) +static void block2mtd_exit(void) { struct list_head *pos, *next; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index d34d83b8f9c2..8510ccb9c6f0 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1440,7 +1440,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, oobdelta = mtd->ecclayout->oobavail; break; default: - oobdelta = 0; + return -EINVAL; } if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) || (ofs % DOC_LAYOUT_PAGE_SIZE)) diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 706b847b46b3..88b3fd3e18a7 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -70,8 +70,6 @@ static unsigned long __initdata doc_locations[] = { 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, #endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#else -#warning Unknown architecture for DiskOnChip. No default probe locations defined #endif 0xffffffff }; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 03838bab1f59..4eeeb2d7f6ea 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -73,14 +73,6 @@ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 5 -#ifdef CONFIG_M25PXX_USE_FAST_READ -#define OPCODE_READ OPCODE_FAST_READ -#define FAST_READ_DUMMY_BYTE 1 -#else -#define OPCODE_READ OPCODE_NORM_READ -#define FAST_READ_DUMMY_BYTE 0 -#endif - #define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) /****************************************************************************/ @@ -93,6 +85,7 @@ struct m25p { u16 addr_width; u8 erase_opcode; u8 *command; + bool fast_read; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -168,6 +161,7 @@ static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) { switch (JEDEC_MFR(jedec_id)) { case CFI_MFR_MACRONIX: + case 0xEF /* winbond */: flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; return spi_write(flash->spi, flash->command, 1); default: @@ -342,6 +336,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct m25p *flash = mtd_to_m25p(mtd); struct spi_transfer t[2]; struct spi_message m; + uint8_t opcode; pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), __func__, (u32)from, len); @@ -354,7 +349,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, * Should add 1 byte DUMMY_BYTE. */ t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE; + t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; @@ -376,12 +371,14 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, */ /* Set up the write data buffer. */ - flash->command[0] = OPCODE_READ; + opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ; + flash->command[0] = opcode; m25p_addr2cmd(flash, from, flash->command); spi_sync(flash->spi, &m); - *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE; + *retlen = m.actual_length - m25p_cmdsz(flash) - + (flash->fast_read ? 1 : 0); mutex_unlock(&flash->lock); @@ -664,7 +661,8 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, /* Micron */ - { "n25q128", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, /* Spansion -- single (large) sector size only, at least @@ -745,6 +743,8 @@ static const struct spi_device_id m25p_ids[] = { { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, @@ -756,7 +756,7 @@ static const struct spi_device_id m25p_ids[] = { }; MODULE_DEVICE_TABLE(spi, m25p_ids); -static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) +static const struct spi_device_id *jedec_probe(struct spi_device *spi) { int tmp; u8 code = OPCODE_RDID; @@ -801,7 +801,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) * matches what the READ command supports, at least until this driver * understands FAST_READ (for clocks over 25 MHz). */ -static int __devinit m25p_probe(struct spi_device *spi) +static int m25p_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); struct flash_platform_data *data; @@ -809,9 +809,10 @@ static int __devinit m25p_probe(struct spi_device *spi) struct flash_info *info; unsigned i; struct mtd_part_parser_data ppdata; + struct device_node __maybe_unused *np = spi->dev.of_node; #ifdef CONFIG_MTD_OF_PARTS - if (!of_device_is_available(spi->dev.of_node)) + if (!of_device_is_available(np)) return -ENODEV; #endif @@ -863,7 +864,8 @@ static int __devinit m25p_probe(struct spi_device *spi) flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); + flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0), + GFP_KERNEL); if (!flash->command) { kfree(flash); return -ENOMEM; @@ -920,6 +922,16 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->page_size = info->page_size; flash->mtd.writebufsize = flash->page_size; + flash->fast_read = false; +#ifdef CONFIG_OF + if (np && of_property_read_bool(np, "m25p,fast-read")) + flash->fast_read = true; +#endif + +#ifdef CONFIG_M25PXX_USE_FAST_READ + flash->fast_read = true; +#endif + if (info->addr_width) flash->addr_width = info->addr_width; else { @@ -961,7 +973,7 @@ static int __devinit m25p_probe(struct spi_device *spi) } -static int __devexit m25p_remove(struct spi_device *spi) +static int m25p_remove(struct spi_device *spi) { struct m25p *flash = dev_get_drvdata(&spi->dev); int status; @@ -983,7 +995,7 @@ static struct spi_driver m25p80_driver = { }, .id_table = m25p_ids, .probe = m25p_probe, - .remove = __devexit_p(m25p_remove), + .remove = m25p_remove, /* REVISIT: many of these chips have deep power-down modes, which * should clearly be entered on suspend() to minimize power use. diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 928fb0e6d73a..ea7ea7b595d8 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -618,7 +618,7 @@ static char *otp_setup(struct mtd_info *device, char revision) /* * Register DataFlash device with MTD subsystem. */ -static int __devinit +static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, int pagesize, int pageoffset, char revision) { @@ -679,7 +679,7 @@ add_dataflash_otp(struct spi_device *spi, char *name, return err; } -static inline int __devinit +static inline int add_dataflash(struct spi_device *spi, char *name, int nr_pages, int pagesize, int pageoffset) { @@ -705,7 +705,7 @@ struct flash_info { #define IS_POW2PS 0x0001 /* uses 2^N byte pages */ }; -static struct flash_info __devinitdata dataflash_data [] = { +static struct flash_info dataflash_data[] = { /* * NOTE: chips with SUP_POW2PS (rev D and up) need two entries, @@ -740,7 +740,7 @@ static struct flash_info __devinitdata dataflash_data [] = { { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS}, }; -static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +static struct flash_info *jedec_probe(struct spi_device *spi) { int tmp; uint8_t code = OP_READ_ID; @@ -823,7 +823,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11 * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 */ -static int __devinit dataflash_probe(struct spi_device *spi) +static int dataflash_probe(struct spi_device *spi) { int status; struct flash_info *info; @@ -897,7 +897,7 @@ static int __devinit dataflash_probe(struct spi_device *spi) return status; } -static int __devexit dataflash_remove(struct spi_device *spi) +static int dataflash_remove(struct spi_device *spi) { struct dataflash *flash = dev_get_drvdata(&spi->dev); int status; @@ -920,7 +920,7 @@ static struct spi_driver dataflash_driver = { }, .probe = dataflash_probe, - .remove = __devexit_p(dataflash_remove), + .remove = dataflash_remove, /* FIXME: investigate suspend and resume... */ }; diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index dcc3c9511530..2d2c2a5d4d2a 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -756,7 +756,7 @@ err_probe: #ifdef CONFIG_OF -static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev, +static int spear_smi_probe_config_dt(struct platform_device *pdev, struct device_node *np) { struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev); @@ -799,7 +799,7 @@ static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev, return 0; } #else -static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev, +static int spear_smi_probe_config_dt(struct platform_device *pdev, struct device_node *np) { return -ENOSYS; @@ -901,7 +901,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev, * and do proper init for any found one. * Returns 0 on success, non zero otherwise */ -static int __devinit spear_smi_probe(struct platform_device *pdev) +static int spear_smi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct spear_smi_plat_data *pdata = NULL; @@ -1016,7 +1016,7 @@ err: * * free all allocations and delete the partitions. */ -static int __devexit spear_smi_remove(struct platform_device *pdev) +static int spear_smi_remove(struct platform_device *pdev) { struct spear_smi *dev; struct spear_snor_flash *flash; @@ -1092,20 +1092,9 @@ static struct platform_driver spear_smi_driver = { #endif }, .probe = spear_smi_probe, - .remove = __devexit_p(spear_smi_remove), + .remove = spear_smi_remove, }; - -static int spear_smi_init(void) -{ - return platform_driver_register(&spear_smi_driver); -} -module_init(spear_smi_init); - -static void spear_smi_exit(void) -{ - platform_driver_unregister(&spear_smi_driver); -} -module_exit(spear_smi_exit); +module_platform_driver(spear_smi_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.hashim@st.com>"); diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index ab8a2f4c8d60..8091b0163694 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -64,7 +64,7 @@ struct flash_info { #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) -static struct flash_info __devinitdata sst25l_flash_info[] = { +static struct flash_info sst25l_flash_info[] = { {"sst25lf020a", 0xbf43, 256, 1024, 4096}, {"sst25lf040a", 0xbf44, 256, 2048, 4096}, }; @@ -313,7 +313,7 @@ out: return ret; } -static struct flash_info *__devinit sst25l_match_device(struct spi_device *spi) +static struct flash_info *sst25l_match_device(struct spi_device *spi) { struct flash_info *flash_info = NULL; struct spi_message m; @@ -353,7 +353,7 @@ static struct flash_info *__devinit sst25l_match_device(struct spi_device *spi) return flash_info; } -static int __devinit sst25l_probe(struct spi_device *spi) +static int sst25l_probe(struct spi_device *spi) { struct flash_info *flash_info; struct sst25l_flash *flash; @@ -411,7 +411,7 @@ static int __devinit sst25l_probe(struct spi_device *spi) return 0; } -static int __devexit sst25l_remove(struct spi_device *spi) +static int sst25l_remove(struct spi_device *spi) { struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); int ret; @@ -428,7 +428,7 @@ static struct spi_driver sst25l_driver = { .owner = THIS_MODULE, }, .probe = sst25l_probe, - .remove = __devexit_p(sst25l_remove), + .remove = sst25l_remove, }; module_spi_driver(sst25l_driver); diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index df304868bebb..62ba82c396c2 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -358,13 +358,6 @@ config MTD_IXP2000 IXP2000 based board and would like to use the flash chips on it, say 'Y'. -config MTD_FORTUNET - tristate "CFI Flash device mapped on the FortuNet board" - depends on MTD_CFI && SA1100_FORTUNET - help - This enables access to the Flash on the FortuNet board. If you - have such a board, say 'Y'. - config MTD_AUTCPU12 bool "NV-RAM mapping AUTCPU12 board" depends on ARCH_AUTCPU12 diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index a0240edd1961..4ded28711bc1 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_IMPA7) += impa7.o -obj-$(CONFIG_MTD_FORTUNET) += fortunet.o obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index e2875d6fe129..f7207b0a76dc 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -100,8 +100,8 @@ static void amd76xrom_cleanup(struct amd76xrom_window *window) } -static int __devinit amd76xrom_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) +static int amd76xrom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; @@ -289,7 +289,7 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, } -static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) +static void amd76xrom_remove_one(struct pci_dev *pdev) { struct amd76xrom_window *window = &amd76xrom_window; @@ -347,4 +347,3 @@ module_exit(cleanup_amd76xrom); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>"); MODULE_DESCRIPTION("MTD map driver for BIOS chips on the AMD76X southbridge"); - diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index 76fb594bb1d9..a2dc2ae4b24e 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -33,7 +33,7 @@ struct autcpu12_nvram_priv { struct map_info map; }; -static int __devinit autcpu12_nvram_probe(struct platform_device *pdev) +static int autcpu12_nvram_probe(struct platform_device *pdev) { map_word tmp, save0, save1; struct resource *res; @@ -105,7 +105,7 @@ static int __devinit autcpu12_nvram_probe(struct platform_device *pdev) return -ENOMEM; } -static int __devexit autcpu12_nvram_remove(struct platform_device *pdev) +static int autcpu12_nvram_remove(struct platform_device *pdev) { struct autcpu12_nvram_priv *priv = platform_get_drvdata(pdev); @@ -121,7 +121,7 @@ static struct platform_driver autcpu12_nvram_driver = { .owner = THIS_MODULE, }, .probe = autcpu12_nvram_probe, - .remove = __devexit_p(autcpu12_nvram_remove), + .remove = autcpu12_nvram_remove, }; module_platform_driver(autcpu12_nvram_driver); diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index ef5cde84a8b3..f833edfaab79 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -30,7 +30,8 @@ #include <linux/io.h> #include <asm/unaligned.h> -#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) +#define pr_devinit(fmt, args...) \ + ({ static const char __fmt[] = fmt; printk(__fmt, ## args); }) #define DRIVER_NAME "bfin-async-flash" @@ -123,7 +124,7 @@ static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const voi static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; -static int __devinit bfin_flash_probe(struct platform_device *pdev) +static int bfin_flash_probe(struct platform_device *pdev) { int ret; struct physmap_flash_data *pdata = pdev->dev.platform_data; @@ -172,7 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) return 0; } -static int __devexit bfin_flash_remove(struct platform_device *pdev) +static int bfin_flash_remove(struct platform_device *pdev) { struct async_state *state = platform_get_drvdata(pdev); gpio_free(state->enet_flash_pin); @@ -184,7 +185,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev) static struct platform_driver bfin_flash_driver = { .probe = bfin_flash_probe, - .remove = __devexit_p(bfin_flash_remove), + .remove = bfin_flash_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 3d0e762fa5f2..586a1c77e48a 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -112,8 +112,8 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window) } -static int __devinit ck804xrom_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) +static int ck804xrom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; @@ -320,7 +320,7 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, } -static void __devexit ck804xrom_remove_one (struct pci_dev *pdev) +static void ck804xrom_remove_one(struct pci_dev *pdev) { struct ck804xrom_window *window = &ck804xrom_window; diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index 08322b1c3e81..ff8681a25831 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -144,7 +144,7 @@ static void esb2rom_cleanup(struct esb2rom_window *window) pci_dev_put(window->pdev); } -static int __devinit esb2rom_init_one(struct pci_dev *pdev, +static int esb2rom_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; @@ -378,13 +378,13 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, return 0; } -static void __devexit esb2rom_remove_one (struct pci_dev *pdev) +static void esb2rom_remove_one(struct pci_dev *pdev) { struct esb2rom_window *window = &esb2rom_window; esb2rom_cleanup(window); } -static struct pci_device_id esb2rom_pci_tbl[] __devinitdata = { +static struct pci_device_id esb2rom_pci_tbl[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c deleted file mode 100644 index 956e2e4f30ea..000000000000 --- a/drivers/mtd/maps/fortunet.c +++ /dev/null @@ -1,277 +0,0 @@ -/* fortunet.c memory map - * - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/string.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -#define MAX_NUM_REGIONS 4 -#define MAX_NUM_PARTITIONS 8 - -#define DEF_WINDOW_ADDR_PHY 0x00000000 -#define DEF_WINDOW_SIZE 0x00800000 // 8 Mega Bytes - -#define MTD_FORTUNET_PK "MTD FortuNet: " - -#define MAX_NAME_SIZE 128 - -struct map_region -{ - int window_addr_physical; - int altbankwidth; - struct map_info map_info; - struct mtd_info *mymtd; - struct mtd_partition parts[MAX_NUM_PARTITIONS]; - char map_name[MAX_NAME_SIZE]; - char parts_name[MAX_NUM_PARTITIONS][MAX_NAME_SIZE]; -}; - -static struct map_region map_regions[MAX_NUM_REGIONS]; -static int map_regions_set[MAX_NUM_REGIONS] = {0,0,0,0}; -static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; - - - -struct map_info default_map = { - .size = DEF_WINDOW_SIZE, - .bankwidth = 4, -}; - -static char * __init get_string_option(char *dest,int dest_size,char *sor) -{ - if(!dest_size) - return sor; - dest_size--; - while(*sor) - { - if(*sor==',') - { - sor++; - break; - } - else if(*sor=='\"') - { - sor++; - while(*sor) - { - if(*sor=='\"') - { - sor++; - break; - } - *dest = *sor; - dest++; - sor++; - dest_size--; - if(!dest_size) - { - *dest = 0; - return sor; - } - } - } - else - { - *dest = *sor; - dest++; - sor++; - dest_size--; - if(!dest_size) - { - *dest = 0; - return sor; - } - } - } - *dest = 0; - return sor; -} - -static int __init MTD_New_Region(char *line) -{ - char string[MAX_NAME_SIZE]; - int params[6]; - get_options (get_string_option(string,sizeof(string),line),6,params); - if(params[0]<1) - { - printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " - " name,region-number[,base,size,bankwidth,altbankwidth]\n"); - return 1; - } - if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) - { - printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", - params[1],MAX_NUM_REGIONS-1); - return 1; - } - memset(&map_regions[params[1]],0,sizeof(map_regions[params[1]])); - memcpy(&map_regions[params[1]].map_info, - &default_map,sizeof(map_regions[params[1]].map_info)); - map_regions_set[params[1]] = 1; - map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; - map_regions[params[1]].altbankwidth = 2; - map_regions[params[1]].mymtd = NULL; - map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; - strcpy(map_regions[params[1]].map_info.name,string); - if(params[0]>1) - { - map_regions[params[1]].window_addr_physical = params[2]; - } - if(params[0]>2) - { - map_regions[params[1]].map_info.size = params[3]; - } - if(params[0]>3) - { - map_regions[params[1]].map_info.bankwidth = params[4]; - } - if(params[0]>4) - { - map_regions[params[1]].altbankwidth = params[5]; - } - return 1; -} - -static int __init MTD_New_Partition(char *line) -{ - char string[MAX_NAME_SIZE]; - int params[4]; - get_options (get_string_option(string,sizeof(string),line),4,params); - if(params[0]<3) - { - printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition " - " name,region-number,size,offset\n"); - return 1; - } - if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) - { - printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", - params[1],MAX_NUM_REGIONS-1); - return 1; - } - if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) - { - printk(MTD_FORTUNET_PK "Out of space for partition in this region\n"); - return 1; - } - map_regions[params[1]].parts[map_regions_parts[params[1]]].name = - map_regions[params[1]]. parts_name[map_regions_parts[params[1]]]; - strcpy(map_regions[params[1]].parts[map_regions_parts[params[1]]].name,string); - map_regions[params[1]].parts[map_regions_parts[params[1]]].size = - params[2]; - map_regions[params[1]].parts[map_regions_parts[params[1]]].offset = - params[3]; - map_regions[params[1]].parts[map_regions_parts[params[1]]].mask_flags = 0; - map_regions_parts[params[1]]++; - return 1; -} - -__setup("MTD_Region=", MTD_New_Region); -__setup("MTD_Partition=", MTD_New_Partition); - -/* Backwards-spelling-compatibility */ -__setup("MTD_Partion=", MTD_New_Partition); - -static int __init init_fortunet(void) -{ - int ix,iy; - for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++) - { - if(map_regions_parts[ix]&&(!map_regions_set[ix])) - { - printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n", - ix); - memset(&map_regions[ix],0,sizeof(map_regions[ix])); - memcpy(&map_regions[ix].map_info,&default_map, - sizeof(map_regions[ix].map_info)); - map_regions_set[ix] = 1; - map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; - map_regions[ix].altbankwidth = 2; - map_regions[ix].mymtd = NULL; - map_regions[ix].map_info.name = map_regions[ix].map_name; - strcpy(map_regions[ix].map_info.name,"FORTUNET"); - } - if(map_regions_set[ix]) - { - iy++; - printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically " - " address %x size %x\n", - map_regions[ix].map_info.name, - map_regions[ix].window_addr_physical, - map_regions[ix].map_info.size); - - map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, - - map_regions[ix].map_info.virt = - ioremap_nocache( - map_regions[ix].window_addr_physical, - map_regions[ix].map_info.size); - if(!map_regions[ix].map_info.virt) - { - int j = 0; - printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", - map_regions[ix].map_info.name); - for (j = 0 ; j < ix; j++) - iounmap(map_regions[j].map_info.virt); - return -ENXIO; - } - simple_map_init(&map_regions[ix].map_info); - - printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n", - map_regions[ix].map_info.name, - map_regions[ix].map_info.virt); - map_regions[ix].mymtd = do_map_probe("cfi_probe", - &map_regions[ix].map_info); - if((!map_regions[ix].mymtd)&&( - map_regions[ix].altbankwidth!=map_regions[ix].map_info.bankwidth)) - { - printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate bankwidth " - "for %s flash.\n", - map_regions[ix].map_info.name); - map_regions[ix].map_info.bankwidth = - map_regions[ix].altbankwidth; - map_regions[ix].mymtd = do_map_probe("cfi_probe", - &map_regions[ix].map_info); - } - map_regions[ix].mymtd->owner = THIS_MODULE; - mtd_device_register(map_regions[ix].mymtd, - map_regions[ix].parts, - map_regions_parts[ix]); - } - } - if(iy) - return 0; - return -ENXIO; -} - -static void __exit cleanup_fortunet(void) -{ - int ix; - for(ix=0;ix<MAX_NUM_REGIONS;ix++) - { - if(map_regions_set[ix]) - { - if( map_regions[ix].mymtd ) - { - mtd_device_unregister(map_regions[ix].mymtd); - map_destroy( map_regions[ix].mymtd ); - } - iounmap((void *)map_regions[ix].map_info.virt); - } - } -} - -module_init(init_fortunet); -module_exit(cleanup_fortunet); - -MODULE_AUTHOR("FortuNet, Inc."); -MODULE_DESCRIPTION("MTD map driver for FortuNet boards"); diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c index e4de96ba52b3..7b643de2500b 100644 --- a/drivers/mtd/maps/gpio-addr-flash.c +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -26,7 +26,8 @@ #include <linux/slab.h> #include <linux/types.h> -#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) +#define pr_devinit(fmt, args...) \ + ({ static const char __fmt[] = fmt; printk(__fmt, ## args); }) #define DRIVER_NAME "gpio-addr-flash" #define PFX DRIVER_NAME ": " @@ -142,7 +143,8 @@ static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) * * See gf_copy_from() caveat. */ -static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void gf_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) { struct async_state *state = gf_map_info_to_state(map); @@ -185,7 +187,7 @@ static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; * ... * }; */ -static int __devinit gpio_flash_probe(struct platform_device *pdev) +static int gpio_flash_probe(struct platform_device *pdev) { size_t i, arr_size; struct physmap_flash_data *pdata; @@ -258,7 +260,7 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev) return 0; } -static int __devexit gpio_flash_remove(struct platform_device *pdev) +static int gpio_flash_remove(struct platform_device *pdev) { struct async_state *state = platform_get_drvdata(pdev); size_t i = 0; @@ -273,7 +275,7 @@ static int __devexit gpio_flash_remove(struct platform_device *pdev) static struct platform_driver gpio_flash_driver = { .probe = gpio_flash_probe, - .remove = __devexit_p(gpio_flash_remove), + .remove = gpio_flash_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 6689dcb3124d..c7478e18f485 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -84,8 +84,8 @@ static void ichxrom_cleanup(struct ichxrom_window *window) } -static int __devinit ichxrom_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) +static int ichxrom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; struct ichxrom_window *window = &ichxrom_window; @@ -315,13 +315,13 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, } -static void __devexit ichxrom_remove_one (struct pci_dev *pdev) +static void ichxrom_remove_one(struct pci_dev *pdev) { struct ichxrom_window *window = &ichxrom_window; ichxrom_cleanup(window); } -static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { +static struct pci_device_id ichxrom_pci_tbl[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 93f03175c82d..3ee2ad1dcbe7 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -63,24 +63,24 @@ struct vr_nor_mtd { #define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */ #define TIMING_MASK 0x3FFF0000 -static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p) +static void vr_nor_destroy_partitions(struct vr_nor_mtd *p) { mtd_device_unregister(p->info); } -static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p) +static int vr_nor_init_partitions(struct vr_nor_mtd *p) { /* register the flash bank */ /* partition the flash bank */ return mtd_device_parse_register(p->info, NULL, NULL, NULL, 0); } -static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p) +static void vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p) { map_destroy(p->info); } -static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p) +static int vr_nor_mtd_setup(struct vr_nor_mtd *p) { static const char *probe_types[] = { "cfi_probe", "jedec_probe", NULL }; @@ -96,7 +96,7 @@ static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p) return 0; } -static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p) +static void vr_nor_destroy_maps(struct vr_nor_mtd *p) { unsigned int exp_timing_cs0; @@ -116,7 +116,7 @@ static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p) * Initialize the map_info structure and map the flash. * Returns 0 on success, nonzero otherwise. */ -static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p) +static int vr_nor_init_maps(struct vr_nor_mtd *p) { unsigned long csr_phys, csr_len; unsigned long win_phys, win_len; @@ -176,7 +176,7 @@ static struct pci_device_id vr_nor_pci_ids[] = { {0,} }; -static void __devexit vr_nor_pci_remove(struct pci_dev *dev) +static void vr_nor_pci_remove(struct pci_dev *dev) { struct vr_nor_mtd *p = pci_get_drvdata(dev); @@ -189,7 +189,7 @@ static void __devexit vr_nor_pci_remove(struct pci_dev *dev) pci_disable_device(dev); } -static int __devinit +static int vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct vr_nor_mtd *p = NULL; @@ -256,7 +256,7 @@ vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) static struct pci_driver vr_nor_pci_driver = { .name = DRV_NAME, .probe = vr_nor_pci_probe, - .remove = __devexit_p(vr_nor_pci_remove), + .remove = vr_nor_pci_remove, .id_table = vr_nor_pci_ids, }; diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c index c03456f17004..3c3c791eb96a 100644 --- a/drivers/mtd/maps/lantiq-flash.c +++ b/drivers/mtd/maps/lantiq-flash.c @@ -45,7 +45,7 @@ struct ltq_mtd { }; static const char ltq_map_name[] = "ltq_nor"; -static const char *ltq_probe_types[] __devinitconst = { +static const char *ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL }; static map_word @@ -109,7 +109,7 @@ ltq_copy_to(struct map_info *map, unsigned long to, spin_unlock_irqrestore(&ebu_lock, flags); } -static int __devinit +static int ltq_mtd_probe(struct platform_device *pdev) { struct mtd_part_parser_data ppdata; @@ -185,7 +185,7 @@ err_out: return err; } -static int __devexit +static int ltq_mtd_remove(struct platform_device *pdev) { struct ltq_mtd *ltq_mtd = platform_get_drvdata(pdev); @@ -209,7 +209,7 @@ MODULE_DEVICE_TABLE(of, ltq_mtd_match); static struct platform_driver ltq_mtd_driver = { .probe = ltq_mtd_probe, - .remove = __devexit_p(ltq_mtd_remove), + .remove = ltq_mtd_remove, .driver = { .name = "ltq-nor", .owner = THIS_MODULE, diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index 3c7ad17fca78..ab0fead56b83 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -125,7 +125,7 @@ static int latch_addr_flash_remove(struct platform_device *dev) return 0; } -static int __devinit latch_addr_flash_probe(struct platform_device *dev) +static int latch_addr_flash_probe(struct platform_device *dev) { struct latch_addr_flash_data *latch_addr_data; struct latch_addr_flash_info *info; @@ -218,7 +218,7 @@ done: static struct platform_driver latch_addr_flash_driver = { .probe = latch_addr_flash_probe, - .remove = __devexit_p(latch_addr_flash_remove), + .remove = latch_addr_flash_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 1c30c1a307f4..ed82914966f5 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -253,7 +253,7 @@ static struct pci_device_id mtd_pci_ids[] = { * Generic code follows. */ -static int __devinit +static int mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct mtd_pci_info *info = (struct mtd_pci_info *)id->driver_data; @@ -308,7 +308,7 @@ out: return err; } -static void __devexit +static void mtd_pci_remove(struct pci_dev *dev) { struct mtd_info *mtd = pci_get_drvdata(dev); @@ -326,7 +326,7 @@ mtd_pci_remove(struct pci_dev *dev) static struct pci_driver mtd_pci_driver = { .name = "MTD PCI", .probe = mtd_pci_probe, - .remove = __devexit_p(mtd_pci_remove), + .remove = mtd_pci_remove, .id_table = mtd_pci_ids, }; diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 6f19acadb06c..37cdc201652f 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -77,7 +77,7 @@ static int of_flash_remove(struct platform_device *dev) /* Helper function to handle probing of the obsolete "direct-mapped" * compatible binding, which has an extra "probe-type" property * describing the type of flash probe necessary. */ -static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev, +static struct mtd_info *obsolete_probe(struct platform_device *dev, struct map_info *map) { struct device_node *dp = dev->dev.of_node; @@ -116,7 +116,7 @@ static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev, information. */ static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL }; -static const char ** __devinit of_get_probes(struct device_node *dp) +static const char **of_get_probes(struct device_node *dp) { const char *cp; int cplen; @@ -145,14 +145,14 @@ static const char ** __devinit of_get_probes(struct device_node *dp) return res; } -static void __devinit of_free_probes(const char **probes) +static void of_free_probes(const char **probes) { if (probes != part_probe_types_def) kfree(probes); } static struct of_device_id of_flash_match[]; -static int __devinit of_flash_probe(struct platform_device *dev) +static int of_flash_probe(struct platform_device *dev) { const char **part_probe_types; const struct of_device_id *match; @@ -170,6 +170,7 @@ static int __devinit of_flash_probe(struct platform_device *dev) resource_size_t res_size; struct mtd_part_parser_data ppdata; bool map_indirect; + const char *mtd_name; match = of_match_device(of_flash_match, &dev->dev); if (!match) @@ -178,6 +179,8 @@ static int __devinit of_flash_probe(struct platform_device *dev) reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); + of_property_read_string(dp, "linux,mtd-name", &mtd_name); + /* * Get number of "reg" tuples. Scan for MTD devices on area's * described by each "reg" region. This makes it possible (including @@ -234,7 +237,7 @@ static int __devinit of_flash_probe(struct platform_device *dev) goto err_out; } - info->list[i].map.name = dev_name(&dev->dev); + info->list[i].map.name = mtd_name ?: dev_name(&dev->dev); info->list[i].map.phys = res.start; info->list[i].map.size = res_size; info->list[i].map.bankwidth = be32_to_cpup(width); @@ -282,6 +285,7 @@ static int __devinit of_flash_probe(struct platform_device *dev) } err = 0; + info->cmtd = NULL; if (info->list_size == 1) { info->cmtd = info->list[0].mtd; } else if (info->list_size > 1) { @@ -290,9 +294,10 @@ static int __devinit of_flash_probe(struct platform_device *dev) */ info->cmtd = mtd_concat_create(mtd_list, info->list_size, dev_name(&dev->dev)); - if (info->cmtd == NULL) - err = -ENXIO; } + if (info->cmtd == NULL) + err = -ENXIO; + if (err) goto err_out; diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index 65bd1cd4d627..afea93b515d5 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -58,7 +58,7 @@ static void pismo_set_vpp(struct platform_device *pdev, int on) pismo->vpp(pismo->vpp_data, on); } -static unsigned int __devinit pismo_width_to_bytes(unsigned int width) +static unsigned int pismo_width_to_bytes(unsigned int width) { width &= 15; if (width > 2) @@ -66,7 +66,7 @@ static unsigned int __devinit pismo_width_to_bytes(unsigned int width) return 1 << width; } -static int __devinit pismo_eeprom_read(struct i2c_client *client, void *buf, +static int pismo_eeprom_read(struct i2c_client *client, void *buf, u8 addr, size_t size) { int ret; @@ -88,7 +88,7 @@ static int __devinit pismo_eeprom_read(struct i2c_client *client, void *buf, return ret == ARRAY_SIZE(msg) ? size : -EIO; } -static int __devinit pismo_add_device(struct pismo_data *pismo, int i, +static int pismo_add_device(struct pismo_data *pismo, int i, struct pismo_mem *region, const char *name, void *pdata, size_t psize) { struct platform_device *dev; @@ -129,7 +129,7 @@ static int __devinit pismo_add_device(struct pismo_data *pismo, int i, return ret; } -static int __devinit pismo_add_nor(struct pismo_data *pismo, int i, +static int pismo_add_nor(struct pismo_data *pismo, int i, struct pismo_mem *region) { struct physmap_flash_data data = { @@ -143,7 +143,7 @@ static int __devinit pismo_add_nor(struct pismo_data *pismo, int i, &data, sizeof(data)); } -static int __devinit pismo_add_sram(struct pismo_data *pismo, int i, +static int pismo_add_sram(struct pismo_data *pismo, int i, struct pismo_mem *region) { struct platdata_mtd_ram data = { @@ -154,7 +154,7 @@ static int __devinit pismo_add_sram(struct pismo_data *pismo, int i, &data, sizeof(data)); } -static void __devinit pismo_add_one(struct pismo_data *pismo, int i, +static void pismo_add_one(struct pismo_data *pismo, int i, const struct pismo_cs_block *cs, phys_addr_t base) { struct device *dev = &pismo->client->dev; @@ -197,7 +197,7 @@ static void __devinit pismo_add_one(struct pismo_data *pismo, int i, } } -static int __devexit pismo_remove(struct i2c_client *client) +static int pismo_remove(struct i2c_client *client) { struct pismo_data *pismo = i2c_get_clientdata(client); int i; @@ -210,7 +210,7 @@ static int __devexit pismo_remove(struct i2c_client *client) return 0; } -static int __devinit pismo_probe(struct i2c_client *client, +static int pismo_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); @@ -267,7 +267,7 @@ static struct i2c_driver pismo_driver = { .owner = THIS_MODULE, }, .probe = pismo_probe, - .remove = __devexit_p(pismo_remove), + .remove = pismo_remove, .id_table = pismo_id, }; diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 81884c277405..43e3dbb976d9 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -49,7 +49,7 @@ struct pxa2xx_flash_info { static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; -static int __devinit pxa2xx_flash_probe(struct platform_device *pdev) +static int pxa2xx_flash_probe(struct platform_device *pdev) { struct flash_platform_data *flash = pdev->dev.platform_data; struct pxa2xx_flash_info *info; @@ -105,7 +105,7 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev) return 0; } -static int __devexit pxa2xx_flash_remove(struct platform_device *dev) +static int pxa2xx_flash_remove(struct platform_device *dev) { struct pxa2xx_flash_info *info = platform_get_drvdata(dev); @@ -139,7 +139,7 @@ static struct platform_driver pxa2xx_flash_driver = { .owner = THIS_MODULE, }, .probe = pxa2xx_flash_probe, - .remove = __devexit_p(pxa2xx_flash_remove), + .remove = pxa2xx_flash_remove, .shutdown = pxa2xx_flash_shutdown, }; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index a675bdbcb0fe..f694417cf7e6 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -149,8 +149,8 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla plat->exit(); } -static struct sa_info *__devinit -sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat) +static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev, + struct flash_platform_data *plat) { struct sa_info *info; int nr, size, i, ret = 0; @@ -246,7 +246,7 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat) static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; -static int __devinit sa1100_mtd_probe(struct platform_device *pdev) +static int sa1100_mtd_probe(struct platform_device *pdev) { struct flash_platform_data *plat = pdev->dev.platform_data; struct sa_info *info; diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 9dcbc684abdb..71796137e97b 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -69,7 +69,7 @@ static struct map_info scb2_map = { }; static int region_fail; -static int __devinit +static int scb2_fixup_mtd(struct mtd_info *mtd) { int i; @@ -133,7 +133,7 @@ scb2_fixup_mtd(struct mtd_info *mtd) /* CSB5's 'Function Control Register' has bits for decoding @ >= 0xffc00000 */ #define CSB5_FCR 0x41 #define CSB5_FCR_DECODE_ALL 0x0e -static int __devinit +static int scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) { u8 reg; @@ -197,7 +197,7 @@ scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) return 0; } -static void __devexit +static void scb2_flash_remove(struct pci_dev *dev) { if (!scb2_mtd) @@ -231,7 +231,7 @@ static struct pci_driver scb2_flash_driver = { .name = "Intel SCB2 BIOS Flash", .id_table = scb2_flash_pci_ids, .probe = scb2_flash_probe, - .remove = __devexit_p(scb2_flash_remove), + .remove = scb2_flash_remove, }; module_pci_driver(scb2_flash_driver); diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 175e537b444f..d467f3b11c96 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -108,7 +108,7 @@ int uflash_devinit(struct platform_device *op, struct device_node *dp) return 0; } -static int __devinit uflash_probe(struct platform_device *op) +static int uflash_probe(struct platform_device *op) { struct device_node *dp = op->dev.of_node; @@ -121,7 +121,7 @@ static int __devinit uflash_probe(struct platform_device *op) return uflash_devinit(op, dp); } -static int __devexit uflash_remove(struct platform_device *op) +static int uflash_remove(struct platform_device *op) { struct uflash_dev *up = dev_get_drvdata(&op->dev); @@ -155,7 +155,7 @@ static struct platform_driver uflash_driver = { .of_match_table = uflash_match, }, .probe = uflash_probe, - .remove = __devexit_p(uflash_remove), + .remove = uflash_remove, }; module_platform_driver(uflash_driver); diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 2e2b0945edc7..6b223cfe92b7 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -596,7 +596,7 @@ fail_name: } /* Handles very basic info about the flash, queries for details */ -static int __devinit vmu_connect(struct maple_device *mdev) +static int vmu_connect(struct maple_device *mdev) { unsigned long test_flash_data, basic_flash_data; int c, error; @@ -690,7 +690,7 @@ fail_nomem: return error; } -static void __devexit vmu_disconnect(struct maple_device *mdev) +static void vmu_disconnect(struct maple_device *mdev) { struct memcard *card; struct mdev_part *mpart; @@ -772,7 +772,7 @@ static void vmu_file_error(struct maple_device *mdev, void *recvbuf) } -static int __devinit probe_maple_vmu(struct device *dev) +static int probe_maple_vmu(struct device *dev) { int error; struct maple_device *mdev = to_maple_dev(dev); @@ -789,7 +789,7 @@ static int __devinit probe_maple_vmu(struct device *dev) return 0; } -static int __devexit remove_maple_vmu(struct device *dev) +static int remove_maple_vmu(struct device *dev) { struct maple_device *mdev = to_maple_dev(dev); @@ -802,7 +802,7 @@ static struct maple_driver vmu_flash_driver = { .drv = { .name = "Dreamcast_visual_memory", .probe = probe_maple_vmu, - .remove = __devexit_p(remove_maple_vmu), + .remove = remove_maple_vmu, }, }; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index f1f06715d4e0..5ad39bb5ab4c 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -32,7 +32,6 @@ #include <linux/hdreg.h> #include <linux/init.h> #include <linux/mutex.h> -#include <linux/kthread.h> #include <asm/uaccess.h> #include "mtdcore.h" @@ -121,16 +120,14 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev) { - if (kthread_should_stop()) - return 1; - return dev->bg_stop; } EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background); -static int mtd_blktrans_thread(void *arg) +static void mtd_blktrans_work(struct work_struct *work) { - struct mtd_blktrans_dev *dev = arg; + struct mtd_blktrans_dev *dev = + container_of(work, struct mtd_blktrans_dev, work); struct mtd_blktrans_ops *tr = dev->tr; struct request_queue *rq = dev->rq; struct request *req = NULL; @@ -138,7 +135,7 @@ static int mtd_blktrans_thread(void *arg) spin_lock_irq(rq->queue_lock); - while (!kthread_should_stop()) { + while (1) { int res; dev->bg_stop = false; @@ -156,15 +153,7 @@ static int mtd_blktrans_thread(void *arg) background_done = !dev->bg_stop; continue; } - set_current_state(TASK_INTERRUPTIBLE); - - if (kthread_should_stop()) - set_current_state(TASK_RUNNING); - - spin_unlock_irq(rq->queue_lock); - schedule(); - spin_lock_irq(rq->queue_lock); - continue; + break; } spin_unlock_irq(rq->queue_lock); @@ -185,8 +174,6 @@ static int mtd_blktrans_thread(void *arg) __blk_end_request_all(req, -EIO); spin_unlock_irq(rq->queue_lock); - - return 0; } static void mtd_blktrans_request(struct request_queue *rq) @@ -199,10 +186,8 @@ static void mtd_blktrans_request(struct request_queue *rq) if (!dev) while ((req = blk_fetch_request(rq)) != NULL) __blk_end_request_all(req, -ENODEV); - else { - dev->bg_stop = true; - wake_up_process(dev->thread); - } + else + queue_work(dev->wq, &dev->work); } static int blktrans_open(struct block_device *bdev, fmode_t mode) @@ -325,7 +310,7 @@ unlock: return ret; } -static const struct block_device_operations mtd_blktrans_ops = { +static const struct block_device_operations mtd_block_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, @@ -401,7 +386,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->private_data = new; gd->major = tr->major; gd->first_minor = (new->devnum) << tr->part_bits; - gd->fops = &mtd_blktrans_ops; + gd->fops = &mtd_block_ops; if (tr->part_bits) if (new->devnum < 26) @@ -437,14 +422,13 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->queue = new->rq; - /* Create processing thread */ - /* TODO: workqueue ? */ - new->thread = kthread_run(mtd_blktrans_thread, new, - "%s%d", tr->name, new->mtd->index); - if (IS_ERR(new->thread)) { - ret = PTR_ERR(new->thread); + /* Create processing workqueue */ + new->wq = alloc_workqueue("%s%d", 0, 0, + tr->name, new->mtd->index); + if (!new->wq) goto error4; - } + INIT_WORK(&new->work, mtd_blktrans_work); + gd->driverfs_dev = &new->mtd->dev; if (new->readonly) @@ -484,9 +468,8 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) /* Stop new requests to arrive */ del_gendisk(old->disk); - - /* Stop the thread */ - kthread_stop(old->thread); + /* Stop workqueue. This will perform any pending request. */ + destroy_workqueue(old->wq); /* Kill current requests */ spin_lock_irqsave(&old->queue_lock, flags); diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index f5b3f91fa1cc..97bb8f6304d4 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -271,7 +271,7 @@ static void find_next_position(struct mtdoops_context *cxt) if (count[0] == 0xffffffff && count[1] == 0xffffffff) mark_page_unused(cxt, page); - if (count[0] == 0xffffffff) + if (count[0] == 0xffffffff || count[1] != MTDOOPS_KERNMSG_MAGIC) continue; if (maxcount == 0xffffffff) { maxcount = count[0]; @@ -289,14 +289,13 @@ static void find_next_position(struct mtdoops_context *cxt) } } if (maxcount == 0xffffffff) { - cxt->nextpage = 0; - cxt->nextcount = 1; - schedule_work(&cxt->work_erase); - return; + cxt->nextpage = cxt->oops_pages - 1; + cxt->nextcount = 0; + } + else { + cxt->nextpage = maxpos; + cxt->nextcount = maxcount; } - - cxt->nextpage = maxpos; - cxt->nextcount = maxcount; mtdoops_inc_counter(cxt); } diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index dae191b3c081..5819eb575210 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -50,16 +50,30 @@ config MTD_NAND_MUSEUM_IDS of these chips were reused by later, larger chips. config MTD_NAND_DENALI - depends on PCI + tristate "Support Denali NAND controller" + help + Enable support for the Denali NAND controller. This should be + combined with either the PCI or platform drivers to provide device + registration. + +config MTD_NAND_DENALI_PCI tristate "Support Denali NAND controller on Intel Moorestown" + depends on PCI && MTD_NAND_DENALI 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" + depends on HAVE_CLK && MTD_NAND_DENALI + 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 + 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 @@ -433,6 +447,14 @@ config MTD_NAND_GPMI_NAND block, such as SD card. So pay attention to it when you enable the GPMI. +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 @@ -499,12 +521,6 @@ config MTD_NAND_MXC This enables the driver for the NAND flash controller on the MXC processors. -config MTD_NAND_NOMADIK - tristate "ST Nomadik 8815 NAND support" - depends on ARCH_NOMADIK - help - Driver for the NAND flash controller on the Nomadik, with ECC. - config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" depends on SUPERH || ARCH_SHMOBILE diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 6c7f2b3ca8ae..d76d91205691 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -11,6 +11,8 @@ 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_PPCHAMELEONEVB) += ppchameleonevb.o @@ -45,11 +47,11 @@ 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_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o +obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 9e7723aa7acc..f1d71cdc8aac 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -173,7 +173,7 @@ static const struct gpio _mandatory_gpio[] = { /* * Main initialization routine */ -static int __devinit ams_delta_init(struct platform_device *pdev) +static int ams_delta_init(struct platform_device *pdev) { struct nand_chip *this; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -270,7 +270,7 @@ out_free: /* * Clean up routine */ -static int __devexit ams_delta_cleanup(struct platform_device *pdev) +static int ams_delta_cleanup(struct platform_device *pdev) { void __iomem *io_base = platform_get_drvdata(pdev); @@ -289,7 +289,7 @@ static int __devexit ams_delta_cleanup(struct platform_device *pdev) static struct platform_driver ams_delta_nand_driver = { .probe = ams_delta_init, - .remove = __devexit_p(ams_delta_cleanup), + .remove = ams_delta_cleanup, .driver = { .name = "ams-delta-nand", .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 92623ac2015a..90bdca61c797 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -331,13 +331,13 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) * 12-bits 20-bytes 21-bytes * 24-bits 39-bytes 42-bytes */ -static int __devinit pmecc_get_ecc_bytes(int cap, int sector_size) +static int pmecc_get_ecc_bytes(int cap, int sector_size) { int m = 12 + sector_size / 512; return (m * cap + 7) / 8; } -static void __devinit pmecc_config_ecc_layout(struct nand_ecclayout *layout, +static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, int oobsize, int ecc_len) { int i; @@ -353,7 +353,7 @@ static void __devinit pmecc_config_ecc_layout(struct nand_ecclayout *layout, oobsize - ecc_len - layout->oobfree[0].offset; } -static void __devinit __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) +static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) { int table_size; @@ -375,7 +375,7 @@ static void pmecc_data_free(struct atmel_nand_host *host) kfree(host->pmecc_delta); } -static int __devinit pmecc_data_alloc(struct atmel_nand_host *host) +static int pmecc_data_alloc(struct atmel_nand_host *host) { const int cap = host->pmecc_corr_cap; @@ -724,6 +724,7 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, struct atmel_nand_host *host = nand_chip->priv; int i, err_nbr, eccbytes; uint8_t *buf_pos; + int total_err = 0; eccbytes = nand_chip->ecc.bytes; for (i = 0; i < eccbytes; i++) @@ -751,12 +752,13 @@ normal_check: pmecc_correct_data(mtd, buf_pos, ecc, i, host->pmecc_bytes_per_sector, err_nbr); mtd->ecc_stats.corrected += err_nbr; + total_err += err_nbr; } } pmecc_stat >>= 1; } - return 0; + return total_err; } static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, @@ -768,6 +770,7 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, uint32_t *eccpos = chip->ecc.layout->eccpos; uint32_t stat; unsigned long end_time; + int bitflips = 0; pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); @@ -790,11 +793,14 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, } stat = pmecc_readl_relaxed(host->ecc, ISR); - if (stat != 0) - if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0) - return -EIO; + if (stat != 0) { + bitflips = pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]); + if (bitflips < 0) + /* uncorrectable errors */ + return 0; + } - return 0; + return bitflips; } static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, @@ -1206,7 +1212,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) } #if defined(CONFIG_OF) -static int __devinit atmel_of_init_port(struct atmel_nand_host *host, +static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { u32 val, table_offset; @@ -1293,7 +1299,7 @@ static int __devinit atmel_of_init_port(struct atmel_nand_host *host, return 0; } #else -static int __devinit atmel_of_init_port(struct atmel_nand_host *host, +static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { return -EINVAL; diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 5c47b200045a..217459d02b2f 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -382,7 +382,7 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i while(!this->dev_ready(mtd)); } -static int __devinit find_nand_cs(unsigned long nand_base) +static int find_nand_cs(unsigned long nand_base) { void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); @@ -403,7 +403,7 @@ static int __devinit find_nand_cs(unsigned long nand_base) return -ENODEV; } -static int __devinit au1550nd_probe(struct platform_device *pdev) +static int au1550nd_probe(struct platform_device *pdev) { struct au1550nd_platdata *pd; struct au1550nd_ctx *ctx; @@ -491,7 +491,7 @@ out1: return ret; } -static int __devexit au1550nd_remove(struct platform_device *pdev) +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); @@ -509,7 +509,7 @@ static struct platform_driver au1550nd_driver = { .owner = THIS_MODULE, }, .probe = au1550nd_probe, - .remove = __devexit_p(au1550nd_remove), + .remove = au1550nd_remove, }; module_platform_driver(au1550nd_driver); diff --git a/drivers/mtd/nand/bcm47xxnflash/Makefile b/drivers/mtd/nand/bcm47xxnflash/Makefile new file mode 100644 index 000000000000..f05b119e134b --- /dev/null +++ b/drivers/mtd/nand/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/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h new file mode 100644 index 000000000000..0bdb2ce4da75 --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h @@ -0,0 +1,22 @@ +#ifndef __BCM47XXNFLASH_H +#define __BCM47XXNFLASH_H + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +struct bcm47xxnflash { + struct bcma_drv_cc *cc; + + struct nand_chip nand_chip; + struct mtd_info mtd; + + 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/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c new file mode 100644 index 000000000000..2b8b05bec3dd --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/main.c @@ -0,0 +1,108 @@ +/* + * BCM47XX NAND flash driver + * + * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/bcma/bcma.h> + +#include "bcm47xxnflash.h" + +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; + int err = 0; + + b47n = kzalloc(sizeof(*b47n), GFP_KERNEL); + if (!b47n) { + err = -ENOMEM; + goto out; + } + + b47n->nand_chip.priv = b47n; + b47n->mtd.owner = THIS_MODULE; + b47n->mtd.priv = &b47n->nand_chip; /* Required */ + 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); + goto err_init; + } + + err = mtd_device_parse_register(&b47n->mtd, probes, NULL, NULL, 0); + if (err) { + pr_err("Failed to register MTD device: %d\n", err); + goto err_dev_reg; + } + + return 0; + +err_dev_reg: +err_init: + kfree(b47n); +out: + return err; +} + +static int __devexit bcm47xxnflash_remove(struct platform_device *pdev) +{ + struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); + + if (nflash->mtd) + mtd_device_unregister(nflash->mtd); + + return 0; +} + +static struct platform_driver bcm47xxnflash_driver = { + .remove = __devexit_p(bcm47xxnflash_remove), + .driver = { + .name = "bcma_nflash", + .owner = THIS_MODULE, + }, +}; + +static int __init bcm47xxnflash_init(void) +{ + int err; + + /* + * Platform device "bcma_nflash" exists on SoCs and is registered very + * early, it won't be added during runtime (use platform_driver_probe). + */ + err = platform_driver_probe(&bcm47xxnflash_driver, bcm47xxnflash_probe); + if (err) + pr_err("Failed to register serial flash driver: %d\n", err); + + return err; +} + +static void __exit bcm47xxnflash_exit(void) +{ + platform_driver_unregister(&bcm47xxnflash_driver); +} + +module_init(bcm47xxnflash_init); +module_exit(bcm47xxnflash_exit); diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c new file mode 100644 index 000000000000..86c9a79b89b3 --- /dev/null +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -0,0 +1,413 @@ +/* + * BCM47XX NAND flash driver + * + * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/bcma/bcma.h> + +#include "bcm47xxnflash.h" + +/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has + * shown 164 retries as maxiumum. */ +#define NFLASH_READY_RETRIES 1000 + +#define NFLASH_SECTOR_SIZE 512 + +#define NCTL_CMD0 0x00010000 +#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 = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + 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 | 0x00040000 | 0x00020000 | + 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 = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + 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 + **************************************************/ + +/* 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; +} + +/* + * 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 = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + 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: + pr_warn("Chip reset not implemented yet\n"); + 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 = 0x00040000 | 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 | 0x00040000 | 0x00020000 | 0x00010000; + 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, 0x00010000 | + 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 = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + 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 = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + 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 = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + 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) +{ + 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; + 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.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) { + freq = 100000000; + } else { + freq = bcma_chipco_pll_read(b47n->cc, 4); + freq = (freq * 0xFFF) >> 3; + freq = (freq * 25000000) >> 3; + } + 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(&b47n->mtd, 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/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index ab0caa74eb43..4271e948d1e2 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -658,7 +658,7 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) /* * Device management interface */ -static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info) +static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) { struct mtd_info *mtd = &info->mtd; struct mtd_partition *parts = info->platform->partitions; @@ -667,7 +667,7 @@ static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info) return mtd_device_register(mtd, parts, nr); } -static int __devexit bf5xx_nand_remove(struct platform_device *pdev) +static int bf5xx_nand_remove(struct platform_device *pdev) { struct bf5xx_nand_info *info = to_nand_info(pdev); @@ -725,7 +725,7 @@ static int bf5xx_nand_scan(struct mtd_info *mtd) * it can allocate all necessary resources then calls the * nand layer to look for devices */ -static int __devinit bf5xx_nand_probe(struct platform_device *pdev) +static int bf5xx_nand_probe(struct platform_device *pdev) { struct bf5xx_nand_platform *plat = to_nand_plat(pdev); struct bf5xx_nand_info *info = NULL; @@ -865,7 +865,7 @@ static int bf5xx_nand_resume(struct platform_device *dev) /* driver device registration */ static struct platform_driver bf5xx_nand_driver = { .probe = bf5xx_nand_probe, - .remove = __devexit_p(bf5xx_nand_remove), + .remove = bf5xx_nand_remove, .suspend = bf5xx_nand_suspend, .resume = bf5xx_nand_resume, .driver = { diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 2bb7170502c2..010d61266536 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -585,7 +585,7 @@ static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) } /* F_2[X]/(X**6+X+1) */ -static unsigned short __devinit gf64_mul(u8 a, u8 b) +static unsigned short gf64_mul(u8 a, u8 b) { u8 c; unsigned int i; @@ -604,7 +604,7 @@ static unsigned short __devinit gf64_mul(u8 a, u8 b) } /* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */ -static u16 __devinit gf4096_mul(u16 a, u16 b) +static u16 gf4096_mul(u16 a, u16 b) { u8 ah, al, bh, bl, ch, cl; @@ -619,14 +619,14 @@ static u16 __devinit gf4096_mul(u16 a, u16 b) return (ch << 6) ^ cl; } -static int __devinit cafe_mul(int x) +static int cafe_mul(int x) { if (x == 0) return 1; return gf4096_mul(x, 0xe01); } -static int __devinit cafe_nand_probe(struct pci_dev *pdev, +static int cafe_nand_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mtd_info *mtd; @@ -821,7 +821,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, return err; } -static void __devexit cafe_nand_remove(struct pci_dev *pdev) +static void cafe_nand_remove(struct pci_dev *pdev) { struct mtd_info *mtd = pci_get_drvdata(pdev); struct cafe_priv *cafe = mtd->priv; @@ -887,7 +887,7 @@ static struct pci_driver cafe_nand_pci_driver = { .name = "CAFÉ NAND", .id_table = cafe_nand_tbl, .probe = cafe_nand_probe, - .remove = __devexit_p(cafe_nand_remove), + .remove = cafe_nand_remove, .resume = cafe_nand_resume, }; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index adb6c3ef37fb..2cdeab8bebc4 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -237,6 +237,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) 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; @@ -247,8 +248,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) goto out_ior; } - this->ecc.strength = 1; - new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs); cs553x_mtd[cs] = new_mtd; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 945047ad0952..3502606f6480 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -821,9 +821,16 @@ syndrome_done: if (ret < 0) goto err_scan; - ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, - pdata->nr_parts); - + if (pdata->parts) + ret = mtd_device_parse_register(&info->mtd, NULL, NULL, + pdata->parts, pdata->nr_parts); + else { + struct mtd_part_parser_data ppdata; + + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(&info->mtd, NULL, &ppdata, + NULL, 0); + } if (ret < 0) goto err_scan; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index e706a237170f..0c8bb6bf8424 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -16,14 +16,12 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ - #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/wait.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/pci.h> #include <linux/mtd/mtd.h> #include <linux/module.h> @@ -89,13 +87,6 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." * format the bank into the proper bits for the controller */ #define BANK(x) ((x) << 24) -/* 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 */ } -}; - /* forward declarations */ static void clear_interrupts(struct denali_nand_info *denali); static uint32_t wait_for_irq(struct denali_nand_info *denali, @@ -699,7 +690,7 @@ static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) if (comp_res == 0) { /* timeout */ - printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n", intr_status, irq_mask); intr_status = 0; @@ -1305,8 +1296,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, /* TODO: Read OOB data */ break; default: - printk(KERN_ERR ": unsupported command" - " received 0x%x\n", cmd); + pr_err(": unsupported command received 0x%x\n", cmd); break; } } @@ -1425,107 +1415,48 @@ void denali_drv_init(struct denali_nand_info *denali) denali->irq_status = 0; } -/* driver entry point */ -static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +int denali_init(struct denali_nand_info *denali) { - int ret = -ENODEV; - resource_size_t csr_base, mem_base; - unsigned long csr_len, mem_len; - struct denali_nand_info *denali; - - denali = kzalloc(sizeof(*denali), GFP_KERNEL); - if (!denali) - return -ENOMEM; + int ret; - ret = pci_enable_device(dev); - if (ret) { - printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); - goto failed_alloc_memery; - } - - if (id->driver_data == INTEL_CE4100) { + if (denali->platform == INTEL_CE4100) { /* Due to a silicon limitation, we can only support * ONFI timing mode 1 and below. */ if (onfi_timing_mode < -1 || onfi_timing_mode > 1) { - printk(KERN_ERR "Intel CE4100 only supports" - " ONFI timing mode 1 or below\n"); - ret = -EINVAL; - goto failed_enable_dev; - } - denali->platform = 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 { - denali->platform = INTEL_MRST; - 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; + pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n"); + return -EINVAL; } } /* Is 32-bit DMA supported? */ - ret = dma_set_mask(&dev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32)); if (ret) { - printk(KERN_ERR "Spectra: no usable DMA configuration\n"); - goto failed_enable_dev; + pr_err("Spectra: no usable DMA configuration\n"); + return ret; } - denali->buf.dma_buf = dma_map_single(&dev->dev, denali->buf.buf, + denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf, DENALI_BUF_SIZE, DMA_BIDIRECTIONAL); - if (dma_mapping_error(&dev->dev, denali->buf.dma_buf)) { - dev_err(&dev->dev, "Spectra: failed to map DMA buffer\n"); - goto failed_enable_dev; - } - - pci_set_master(dev); - denali->dev = &dev->dev; - denali->mtd.dev.parent = &dev->dev; - - ret = pci_request_regions(dev, DENALI_NAND_NAME); - if (ret) { - printk(KERN_ERR "Spectra: Unable to request memory regions\n"); - goto failed_dma_map; - } - - denali->flash_reg = ioremap_nocache(csr_base, csr_len); - if (!denali->flash_reg) { - printk(KERN_ERR "Spectra: Unable to remap memory region\n"); - ret = -ENOMEM; - goto failed_req_regions; - } - - denali->flash_mem = ioremap_nocache(mem_base, mem_len); - if (!denali->flash_mem) { - printk(KERN_ERR "Spectra: ioremap_nocache failed!"); - ret = -ENOMEM; - goto failed_remap_reg; + if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) { + dev_err(denali->dev, "Spectra: failed to map DMA buffer\n"); + return -EIO; } - + denali->mtd.dev.parent = denali->dev; denali_hw_init(denali); denali_drv_init(denali); /* denali_isr register is done after all the hardware * initilization is finished*/ - if (request_irq(dev->irq, denali_isr, IRQF_SHARED, + if (request_irq(denali->irq, denali_isr, IRQF_SHARED, DENALI_NAND_NAME, denali)) { - printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); - ret = -ENODEV; - goto failed_remap_mem; + pr_err("Spectra: Unable to allocate IRQ\n"); + return -ENODEV; } /* now that our ISR is registered, we can enable interrupts */ denali_set_intr_modes(denali, true); - - pci_set_drvdata(dev, denali); - denali->mtd.name = "denali-nand"; denali->mtd.owner = THIS_MODULE; denali->mtd.priv = &denali->nand; @@ -1549,8 +1480,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) */ if (denali->mtd.writesize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) { ret = -ENODEV; - printk(KERN_ERR "Spectra: device size not supported by this " - "version of MTD."); + pr_err("Spectra: device size not supported by this version of MTD."); goto failed_req_irq; } @@ -1602,8 +1532,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } else if (denali->mtd.oobsize < (denali->bbtskipbytes + ECC_8BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE))) { - printk(KERN_ERR "Your NAND chip OOB is not large enough to" - " contain 8bit ECC correction codes"); + pr_err("Your NAND chip OOB is not large enough to \ + contain 8bit ECC correction codes"); goto failed_req_irq; } else { denali->nand.ecc.strength = 8; @@ -1655,56 +1585,24 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ret = mtd_device_register(&denali->mtd, NULL, 0); if (ret) { - dev_err(&dev->dev, "Spectra: Failed to register MTD: %d\n", + dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n", ret); goto failed_req_irq; } return 0; failed_req_irq: - denali_irq_cleanup(dev->irq, denali); -failed_remap_mem: - iounmap(denali->flash_mem); -failed_remap_reg: - iounmap(denali->flash_reg); -failed_req_regions: - pci_release_regions(dev); -failed_dma_map: - dma_unmap_single(&dev->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - DMA_BIDIRECTIONAL); -failed_enable_dev: - pci_disable_device(dev); -failed_alloc_memery: - kfree(denali); + denali_irq_cleanup(denali->irq, denali); + return ret; } +EXPORT_SYMBOL(denali_init); /* driver exit point */ -static void denali_pci_remove(struct pci_dev *dev) +void denali_remove(struct denali_nand_info *denali) { - struct denali_nand_info *denali = pci_get_drvdata(dev); - - nand_release(&denali->mtd); - - denali_irq_cleanup(dev->irq, denali); - - iounmap(denali->flash_reg); - iounmap(denali->flash_mem); - pci_release_regions(dev); - pci_disable_device(dev); - dma_unmap_single(&dev->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - DMA_BIDIRECTIONAL); - pci_set_drvdata(dev, NULL); - kfree(denali); + denali_irq_cleanup(denali->irq, denali); + dma_unmap_single(denali->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + DMA_BIDIRECTIONAL); } - -MODULE_DEVICE_TABLE(pci, denali_pci_ids); - -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); +EXPORT_SYMBOL(denali_remove); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index fabb9d56b39e..cec5712862c9 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -466,6 +466,7 @@ struct nand_buf { #define INTEL_CE4100 1 #define INTEL_MRST 2 +#define DT 3 struct denali_nand_info { struct mtd_info mtd; @@ -487,6 +488,7 @@ struct denali_nand_info { uint32_t irq_status; int irq_debug_array[32]; int idx; + int irq; uint32_t devnum; /* represent how many nands connected */ uint32_t fwblks; /* represent how many blocks FW used */ @@ -496,4 +498,7 @@ struct denali_nand_info { uint32_t max_banks; }; +extern int denali_init(struct denali_nand_info *denali); +extern void denali_remove(struct denali_nand_info *denali); + #endif /*_LLD_NAND_*/ diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c new file mode 100644 index 000000000000..546f8cb5688d --- /dev/null +++ b/drivers/mtd/nand/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 <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include "denali.h" + +struct denali_dt { + struct denali_nand_info denali; + struct clk *clk; +}; + +static void __iomem *request_and_map(struct device *dev, + const struct resource *res) +{ + void __iomem *ptr; + + if (!devm_request_mem_region(dev, res->start, resource_size(res), + "denali-dt")) { + dev_err(dev, "unable to request %s\n", res->name); + return NULL; + } + + ptr = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!res) + dev_err(dev, "ioremap_nocache of %s failed!", res->name); + + return ptr; +} + +static const struct of_device_id denali_nand_dt_ids[] = { + { .compatible = "denali,denali-nand-dt" }, + { /* sentinel */ } + }; + +MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); + +static u64 denali_dma_mask; + +static int denali_dt_probe(struct platform_device *ofdev) +{ + struct resource *denali_reg, *nand_data; + struct denali_dt *dt; + struct denali_nand_info *denali; + int ret; + const struct of_device_id *of_id; + + of_id = of_match_device(denali_nand_dt_ids, &ofdev->dev); + if (of_id) { + ofdev->id_entry = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + + dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + denali = &dt->denali; + + denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg"); + nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data"); + if (!denali_reg || !nand_data) { + dev_err(&ofdev->dev, "resources not completely defined\n"); + return -EINVAL; + } + + denali->platform = DT; + denali->dev = &ofdev->dev; + denali->irq = platform_get_irq(ofdev, 0); + if (denali->irq < 0) { + dev_err(&ofdev->dev, "no irq defined\n"); + return -ENXIO; + } + + denali->flash_reg = request_and_map(&ofdev->dev, denali_reg); + if (!denali->flash_reg) + return -ENOMEM; + + denali->flash_mem = request_and_map(&ofdev->dev, nand_data); + if (!denali->flash_mem) + return -ENOMEM; + + if (!of_property_read_u32(ofdev->dev.of_node, + "dma-mask", (u32 *)&denali_dma_mask)) { + denali->dev->dma_mask = &denali_dma_mask; + } else { + denali->dev->dma_mask = NULL; + } + + dt->clk = clk_get(&ofdev->dev, NULL); + if (IS_ERR(dt->clk)) { + dev_err(&ofdev->dev, "no clk available\n"); + return PTR_ERR(dt->clk); + } + clk_prepare_enable(dt->clk); + + ret = denali_init(denali); + if (ret) + goto out_disable_clk; + + platform_set_drvdata(ofdev, dt); + return 0; + +out_disable_clk: + clk_disable_unprepare(dt->clk); + clk_put(dt->clk); + + return ret; +} + +static int denali_dt_remove(struct platform_device *ofdev) +{ + struct denali_dt *dt = platform_get_drvdata(ofdev); + + denali_remove(&dt->denali); + clk_disable(dt->clk); + clk_put(dt->clk); + + return 0; +} + +static struct platform_driver denali_dt_driver = { + .probe = denali_dt_probe, + .remove = denali_dt_remove, + .driver = { + .name = "denali-nand-dt", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(denali_nand_dt_ids), + }, +}; + +static int __init denali_init_dt(void) +{ + return platform_driver_register(&denali_dt_driver); +} +module_init(denali_init_dt); + +static void __exit denali_exit_dt(void) +{ + platform_driver_unregister(&denali_dt_driver); +} +module_exit(denali_exit_dt); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jamie Iles"); +MODULE_DESCRIPTION("DT driver for Denali NAND controller"); diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c new file mode 100644 index 000000000000..e3e46623b2b4 --- /dev/null +++ b/drivers/mtd/nand/denali_pci.c @@ -0,0 +1,144 @@ +/* + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/slab.h> + +#include "denali.h" + +#define DENALI_NAND_NAME "denali-nand-pci" + +/* List of platforms this NAND controller has be integrated into */ +static DEFINE_PCI_DEVICE_TABLE(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); + +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pci_enable_device(dev); + if (ret) { + pr_err("Spectra: pci_enable_device failed.\n"); + goto failed_alloc_memery; + } + + if (id->driver_data == INTEL_CE4100) { + denali->platform = 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 { + denali->platform = INTEL_MRST; + 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; + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + pr_err("Spectra: Unable to request memory regions\n"); + goto failed_enable_dev; + } + + denali->flash_reg = ioremap_nocache(csr_base, csr_len); + if (!denali->flash_reg) { + pr_err("Spectra: Unable to remap memory region\n"); + ret = -ENOMEM; + goto failed_req_regions; + } + + denali->flash_mem = ioremap_nocache(mem_base, mem_len); + if (!denali->flash_mem) { + pr_err("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->flash_mem); +failed_remap_reg: + iounmap(denali->flash_reg); +failed_req_regions: + pci_release_regions(dev); +failed_enable_dev: + pci_disable_device(dev); +failed_alloc_memery: + kfree(denali); + + 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->flash_reg); + iounmap(denali->flash_mem); + pci_release_regions(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + kfree(denali); +} + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; + +static int denali_init_pci(void) +{ + pr_info("Spectra MTD driver built on %s @ %s\n", __DATE__, __TIME__); + return pci_register_driver(&denali_pci_driver); +} +module_init(denali_init_pci); + +static void denali_exit_pci(void) +{ + pci_unregister_driver(&denali_pci_driver); +} +module_exit(denali_exit_pci); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 256eb30f6180..81fa5784f98b 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -53,8 +53,6 @@ static unsigned long __initdata doc_locations[] = { 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, #endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#else -#warning Unknown architecture for DiskOnChip. No default probe locations defined #endif 0xffffffff }; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 799da5d1c857..18fa4489e52e 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -46,6 +46,25 @@ #include <linux/bitrev.h> /* + * 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). @@ -113,6 +132,7 @@ struct docg4_priv { #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 @@ -122,6 +142,8 @@ struct docg4_priv { #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 */ @@ -190,17 +212,20 @@ struct docg4_priv { #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 */ /* - * Oob bytes 0 - 6 are available to the user. - * Byte 7 is hamming ecc for first 7 bytes. Bytes 8 - 14 are hw-generated ecc. + * 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 struct nand_ecclayout docg4_oobinfo = { .eccbytes = 9, .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, - .oobavail = 7, - .oobfree = { {0, 7} } + .oobavail = 5, + .oobfree = { {.offset = 2, .length = 5} } }; /* @@ -611,6 +636,14 @@ static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) 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); @@ -691,6 +724,15 @@ static void docg4_command(struct mtd_info *mtd, unsigned command, int column, 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 */ @@ -979,16 +1021,15 @@ static int __init read_factory_bbt(struct mtd_info *mtd) struct docg4_priv *doc = nand->priv; uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); uint8_t *buf; - int i, block, status; + 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); - status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); - if (status) - goto exit; + docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); /* * If no memory-based bbt was created, exit. This will happen if module @@ -1000,6 +1041,20 @@ static int __init read_factory_bbt(struct mtd_info *mtd) 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 @@ -1019,7 +1074,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd) } exit: kfree(buf); - return status; + return 0; } static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index cc1480a5e4c1..20657209a472 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -109,20 +109,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { }; /* - * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset - * 1, so we have to adjust bad block pattern. This pattern should be used for - * x8 chips only. So far hardware does not support x16 chips anyway. - */ -static u8 scan_ff_pattern[] = { 0xff, }; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern, -}; - -/* * 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. @@ -699,7 +685,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; - chip->badblock_pattern = &largepage_memorybased; } } else { dev_err(priv->dev, @@ -814,7 +799,7 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) static DEFINE_MUTEX(fsl_elbc_nand_mutex); -static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) +static int fsl_elbc_nand_probe(struct platform_device *pdev) { struct fsl_lbc_regs __iomem *lbc; struct fsl_elbc_mtd *priv; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 3551a99076ba..ad6222627fed 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -389,7 +389,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, timing = IFC_FIR_OP_RBCD; out_be32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | (timing << IFC_NAND_FIR0_OP2_SHIFT)); out_be32(&ifc->ifc_nand.nand_fcr0, @@ -754,7 +754,7 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) /* READID */ out_be32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | + (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)); out_be32(&ifc->ifc_nand.nand_fcr0, @@ -922,7 +922,7 @@ static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, static DEFINE_MUTEX(fsl_ifc_nand_mutex); -static int __devinit fsl_ifc_nand_probe(struct platform_device *dev) +static int fsl_ifc_nand_probe(struct platform_device *dev) { struct fsl_ifc_regs __iomem *ifc; struct fsl_ifc_mtd *priv; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 45df542b9c61..5a8f5c4ce512 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -152,7 +152,7 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) fun_wait_rnb(fun); } -static int __devinit fun_chip_init(struct fsl_upm_nand *fun, +static int fun_chip_init(struct fsl_upm_nand *fun, const struct device_node *upm_np, const struct resource *io_res) { @@ -201,7 +201,7 @@ err: return ret; } -static int __devinit fun_probe(struct platform_device *ofdev) +static int fun_probe(struct platform_device *ofdev) { struct fsl_upm_nand *fun; struct resource io_res; @@ -318,7 +318,7 @@ err1: return ret; } -static int __devexit fun_remove(struct platform_device *ofdev) +static int fun_remove(struct platform_device *ofdev) { struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); int i; @@ -350,7 +350,7 @@ static struct platform_driver of_fun_driver = { .of_match_table = of_fun_match, }, .probe = fun_probe, - .remove = __devexit_p(fun_remove), + .remove = fun_remove, }; module_platform_driver(of_fun_driver); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 38d26240d8b1..1d7446434b0e 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -361,7 +361,7 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *this = mtd->priv; struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - void *__iomem *regs = host->regs_va; + void __iomem *regs = host->regs_va; unsigned int bank = host->bank; if (ctrl & NAND_CTRL_CHANGE) { @@ -383,13 +383,13 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) pc |= FSMC_ENABLE; else pc &= ~FSMC_ENABLE; - writel(pc, FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC)); } mb(); if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); + writeb_relaxed(cmd, this->IO_ADDR_W); } /* @@ -426,14 +426,18 @@ static void fsmc_nand_setup(void __iomem *regs, uint32_t bank, tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (busw) - writel(value | FSMC_DEVWID_16, FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(value | FSMC_DEVWID_16, + FSMC_NAND_REG(regs, bank, PC)); else - writel(value | FSMC_DEVWID_8, FSMC_NAND_REG(regs, bank, PC)); + writel_relaxed(value | FSMC_DEVWID_8, + FSMC_NAND_REG(regs, bank, PC)); - writel(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, FSMC_NAND_REG(regs, bank, PC)); - writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, COMM)); - writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, ATTRIB)); + writel_relaxed(thiz | thold | twait | tset, + FSMC_NAND_REG(regs, bank, COMM)); + writel_relaxed(thiz | thold | twait | tset, + FSMC_NAND_REG(regs, bank, ATTRIB)); } /* @@ -446,11 +450,11 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) void __iomem *regs = host->regs_va; uint32_t bank = host->bank; - writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, FSMC_NAND_REG(regs, bank, PC)); - writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, FSMC_NAND_REG(regs, bank, PC)); - writel(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, + writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, FSMC_NAND_REG(regs, bank, PC)); } @@ -470,7 +474,7 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; do { - if (readl(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) + if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) break; else cond_resched(); @@ -481,25 +485,25 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, return -ETIMEDOUT; } - ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1)); + 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(FSMC_NAND_REG(regs, bank, ECC2)); + 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(FSMC_NAND_REG(regs, bank, ECC3)); + 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(FSMC_NAND_REG(regs, bank, STS)); + ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS)); ecc[12] = (uint8_t) (ecc_tmp >> 16); return 0; @@ -519,7 +523,7 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, uint32_t bank = host->bank; uint32_t ecc_tmp; - ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1)); + 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); @@ -601,7 +605,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, dma_async_issue_pending(chan); ret = - wait_for_completion_interruptible_timeout(&host->dma_access_complete, + wait_for_completion_timeout(&host->dma_access_complete, msecs_to_jiffies(3000)); if (ret <= 0) { chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); @@ -628,10 +632,10 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) uint32_t *p = (uint32_t *)buf; len = len >> 2; for (i = 0; i < len; i++) - writel(p[i], chip->IO_ADDR_W); + writel_relaxed(p[i], chip->IO_ADDR_W); } else { for (i = 0; i < len; i++) - writeb(buf[i], chip->IO_ADDR_W); + writeb_relaxed(buf[i], chip->IO_ADDR_W); } } @@ -651,10 +655,10 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) uint32_t *p = (uint32_t *)buf; len = len >> 2; for (i = 0; i < len; i++) - p[i] = readl(chip->IO_ADDR_R); + p[i] = readl_relaxed(chip->IO_ADDR_R); } else { for (i = 0; i < len; i++) - buf[i] = readb(chip->IO_ADDR_R); + buf[i] = readb_relaxed(chip->IO_ADDR_R); } } @@ -783,7 +787,7 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, uint32_t num_err, i; uint32_t ecc1, ecc2, ecc3, ecc4; - num_err = (readl(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; + num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; /* no bit flipping */ if (likely(num_err == 0)) @@ -826,10 +830,10 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - ecc1 = readl(FSMC_NAND_REG(regs, bank, ECC1)); - ecc2 = readl(FSMC_NAND_REG(regs, bank, ECC2)); - ecc3 = readl(FSMC_NAND_REG(regs, bank, ECC3)); - ecc4 = readl(FSMC_NAND_REG(regs, bank, STS)); + 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; @@ -860,7 +864,7 @@ static bool filter(struct dma_chan *chan, void *slave) } #ifdef CONFIG_OF -static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, +static int fsmc_nand_probe_config_dt(struct platform_device *pdev, struct device_node *np) { struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -876,15 +880,13 @@ static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, return -EINVAL; } } - of_property_read_u32(np, "st,ale-off", &pdata->ale_off); - of_property_read_u32(np, "st,cle-off", &pdata->cle_off); if (of_get_property(np, "nand-skip-bbtscan", NULL)) pdata->options = NAND_SKIP_BBTSCAN; return 0; } #else -static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, +static int fsmc_nand_probe_config_dt(struct platform_device *pdev, struct device_node *np) { return -ENOSYS; @@ -935,41 +937,28 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (!res) return -EINVAL; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory data resourse\n"); - return -ENOENT; - } - - host->data_pa = (dma_addr_t)res->start; - host->data_va = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + host->data_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->data_va) { dev_err(&pdev->dev, "data ioremap failed\n"); return -ENOMEM; } + host->data_pa = (dma_addr_t)res->start; - if (!devm_request_mem_region(&pdev->dev, res->start + pdata->ale_off, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory ale resourse\n"); - return -ENOENT; - } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); + if (!res) + return -EINVAL; - host->addr_va = devm_ioremap(&pdev->dev, res->start + pdata->ale_off, - resource_size(res)); + host->addr_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->addr_va) { dev_err(&pdev->dev, "ale ioremap failed\n"); return -ENOMEM; } - if (!devm_request_mem_region(&pdev->dev, res->start + pdata->cle_off, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory cle resourse\n"); - return -ENOENT; - } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); + if (!res) + return -EINVAL; - host->cmd_va = devm_ioremap(&pdev->dev, res->start + pdata->cle_off, - resource_size(res)); + host->cmd_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->cmd_va) { dev_err(&pdev->dev, "ale ioremap failed\n"); return -ENOMEM; @@ -979,14 +968,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (!res) return -EINVAL; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory regs resourse\n"); - return -ENOENT; - } - - host->regs_va = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + host->regs_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->regs_va) { dev_err(&pdev->dev, "regs ioremap failed\n"); return -ENOMEM; diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index bc73bc5f2713..e789e3f51710 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -90,14 +90,14 @@ static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - writesb(this->IO_ADDR_W, buf, len); + iowrite8_rep(this->IO_ADDR_W, buf, len); } static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; - readsb(this->IO_ADDR_R, buf, len); + ioread8_rep(this->IO_ADDR_R, buf, len); } static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, @@ -106,7 +106,7 @@ static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, struct nand_chip *this = mtd->priv; if (IS_ALIGNED((unsigned long)buf, 2)) { - writesw(this->IO_ADDR_W, buf, len>>1); + iowrite16_rep(this->IO_ADDR_W, buf, len>>1); } else { int i; unsigned short *ptr = (unsigned short *)buf; @@ -121,7 +121,7 @@ static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) struct nand_chip *this = mtd->priv; if (IS_ALIGNED((unsigned long)buf, 2)) { - readsw(this->IO_ADDR_R, buf, len>>1); + ioread16_rep(this->IO_ADDR_R, buf, len>>1); } else { int i; unsigned short *ptr = (unsigned short *)buf; @@ -134,7 +134,11 @@ static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) static int gpio_nand_devready(struct mtd_info *mtd) { struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - return gpio_get_value(gpiomtd->plat.gpio_rdy); + + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) + return gpio_get_value(gpiomtd->plat.gpio_rdy); + + return 1; } #ifdef CONFIG_OF @@ -227,7 +231,7 @@ gpio_nand_get_io_sync(struct platform_device *pdev) return platform_get_resource(pdev, IORESOURCE_MEM, 1); } -static int __devexit gpio_nand_remove(struct platform_device *dev) +static int gpio_nand_remove(struct platform_device *dev) { struct gpiomtd *gpiomtd = platform_get_drvdata(dev); struct resource *res; @@ -252,7 +256,8 @@ static int __devexit gpio_nand_remove(struct platform_device *dev) gpio_free(gpiomtd->plat.gpio_nce); if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_free(gpiomtd->plat.gpio_nwp); - gpio_free(gpiomtd->plat.gpio_rdy); + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) + gpio_free(gpiomtd->plat.gpio_rdy); kfree(gpiomtd); @@ -277,7 +282,7 @@ static void __iomem *request_and_remap(struct resource *res, size_t size, return ptr; } -static int __devinit gpio_nand_probe(struct platform_device *dev) +static int gpio_nand_probe(struct platform_device *dev) { struct gpiomtd *gpiomtd; struct nand_chip *this; @@ -336,10 +341,12 @@ static int __devinit gpio_nand_probe(struct platform_device *dev) if (ret) goto err_cle; gpio_direction_output(gpiomtd->plat.gpio_cle, 0); - ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); - if (ret) - goto err_rdy; - gpio_direction_input(gpiomtd->plat.gpio_rdy); + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { + ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); + if (ret) + goto err_rdy; + gpio_direction_input(gpiomtd->plat.gpio_rdy); + } this->IO_ADDR_W = this->IO_ADDR_R; @@ -386,7 +393,8 @@ static int __devinit gpio_nand_probe(struct platform_device *dev) err_wp: if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - gpio_free(gpiomtd->plat.gpio_rdy); + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) + gpio_free(gpiomtd->plat.gpio_rdy); err_rdy: gpio_free(gpiomtd->plat.gpio_cle); err_cle: diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 3502accd4bc3..d84699c7968e 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -18,7 +18,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <linux/mtd/gpmi-nand.h> #include <linux/delay.h> #include <linux/clk.h> @@ -166,6 +165,15 @@ int gpmi_init(struct gpmi_nand_data *this) 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); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index d79696b2f19b..5cd141f7bfc2 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -25,7 +25,6 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/module.h> -#include <linux/mtd/gpmi-nand.h> #include <linux/mtd/partitions.h> #include <linux/pinctrl/consumer.h> #include <linux/of.h> @@ -33,6 +32,12 @@ #include <linux/of_mtd.h> #include "gpmi-nand.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" +#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "gpmi-dma" + /* add our owner bbt descriptor */ static uint8_t scan_ff_pattern[] = { 0xff }; static struct nand_bbt_descr gpmi_bbt_descr = { @@ -222,7 +227,7 @@ void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) ret = dma_map_sg(this->dev, sgl, 1, dr); if (ret == 0) - pr_err("map failed.\n"); + pr_err("DMA mapping failed.\n"); this->direct_dma_map_ok = false; } @@ -314,7 +319,7 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this, return 0; } -static int __devinit +static int acquire_register_block(struct gpmi_nand_data *this, const char *res_name) { struct platform_device *pdev = this->pdev; @@ -355,7 +360,7 @@ static void release_register_block(struct gpmi_nand_data *this) res->bch_regs = NULL; } -static int __devinit +static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) { struct platform_device *pdev = this->pdev; @@ -422,7 +427,7 @@ static void release_dma_channels(struct gpmi_nand_data *this) } } -static int __devinit acquire_dma_channels(struct gpmi_nand_data *this) +static int acquire_dma_channels(struct gpmi_nand_data *this) { struct platform_device *pdev = this->pdev; struct resource *r_dma; @@ -456,7 +461,7 @@ static int __devinit acquire_dma_channels(struct gpmi_nand_data *this) dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); if (!dma_chan) { - pr_err("dma_request_channel failed.\n"); + pr_err("Failed to request DMA channel.\n"); goto acquire_err; } @@ -487,7 +492,7 @@ static char *extra_clks_for_mx6q[GPMI_CLK_MAX] = { "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", }; -static int __devinit gpmi_get_clks(struct gpmi_nand_data *this) +static int gpmi_get_clks(struct gpmi_nand_data *this) { struct resources *r = &this->resources; char **extra_clks = NULL; @@ -533,7 +538,7 @@ err_clock: return -ENOMEM; } -static int __devinit acquire_resources(struct gpmi_nand_data *this) +static int acquire_resources(struct gpmi_nand_data *this) { struct pinctrl *pinctrl; int ret; @@ -583,7 +588,7 @@ static void release_resources(struct gpmi_nand_data *this) release_dma_channels(this); } -static int __devinit init_hardware(struct gpmi_nand_data *this) +static int init_hardware(struct gpmi_nand_data *this) { int ret; @@ -625,7 +630,8 @@ static int read_page_prepare(struct gpmi_nand_data *this, length, DMA_FROM_DEVICE); if (dma_mapping_error(dev, dest_phys)) { if (alt_size < length) { - pr_err("Alternate buffer is too small\n"); + pr_err("%s, Alternate buffer is too small\n", + __func__); return -ENOMEM; } goto map_failed; @@ -675,7 +681,8 @@ static int send_page_prepare(struct gpmi_nand_data *this, DMA_TO_DEVICE); if (dma_mapping_error(dev, source_phys)) { if (alt_size < length) { - pr_err("Alternate buffer is too small\n"); + pr_err("%s, Alternate buffer is too small\n", + __func__); return -ENOMEM; } goto map_failed; @@ -763,7 +770,7 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) error_alloc: gpmi_free_dma_buffer(this); - pr_err("allocate DMA buffer ret!!\n"); + pr_err("Error allocating DMA buffers!\n"); return -ENOMEM; } @@ -1474,7 +1481,7 @@ static int gpmi_set_geometry(struct gpmi_nand_data *this) /* Set up the NFC geometry which is used by BCH. */ ret = bch_set_geometry(this); if (ret) { - pr_err("set geometry ret : %d\n", ret); + pr_err("Error setting BCH geometry : %d\n", ret); return ret; } @@ -1535,7 +1542,7 @@ static void gpmi_nfc_exit(struct gpmi_nand_data *this) gpmi_free_dma_buffer(this); } -static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) +static int gpmi_nfc_init(struct gpmi_nand_data *this) { struct mtd_info *mtd = &this->mtd; struct nand_chip *chip = &this->nand; @@ -1618,7 +1625,7 @@ static const struct of_device_id gpmi_nand_id_table[] = { }; MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); -static int __devinit gpmi_nand_probe(struct platform_device *pdev) +static int gpmi_nand_probe(struct platform_device *pdev) { struct gpmi_nand_data *this; const struct of_device_id *of_id; @@ -1668,7 +1675,7 @@ exit_acquire_resources: return ret; } -static int __devexit gpmi_nand_remove(struct platform_device *pdev) +static int gpmi_nand_remove(struct platform_device *pdev) { struct gpmi_nand_data *this = platform_get_drvdata(pdev); @@ -1685,7 +1692,7 @@ static struct platform_driver gpmi_nand_driver = { .of_match_table = gpmi_nand_id_table, }, .probe = gpmi_nand_probe, - .remove = __devexit_p(gpmi_nand_remove), + .remove = gpmi_nand_remove, .id_table = gpmi_ids, }; module_platform_driver(gpmi_nand_driver); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 7ac25c1e58f9..3d93a5e39090 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -130,7 +130,6 @@ struct gpmi_nand_data { /* System Interface */ struct device *dev; struct platform_device *pdev; - struct gpmi_nand_platform_data *pdata; /* Resources */ struct resources resources; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 100b6775e175..8d415f014e1d 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -316,13 +316,17 @@ err: return ret; } -static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) +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 __devinit 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) { +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; int gpio; char gpio_name[9]; @@ -400,7 +404,7 @@ notfound_gpio: return ret; } -static int __devinit jz_nand_probe(struct platform_device *pdev) +static int jz_nand_probe(struct platform_device *pdev) { int ret; struct jz_nand *nand; @@ -541,7 +545,7 @@ err_free: return ret; } -static int __devexit jz_nand_remove(struct platform_device *pdev) +static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); struct jz_nand_platform_data *pdata = pdev->dev.platform_data; @@ -573,7 +577,7 @@ static int __devexit jz_nand_remove(struct platform_device *pdev) static struct platform_driver jz_nand_driver = { .probe = jz_nand_probe, - .remove = __devexit_p(jz_nand_remove), + .remove = jz_nand_remove, .driver = { .name = "jz4740-nand", .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index c29b7ac1f6af..f182befa7360 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -655,7 +655,7 @@ static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev) /* * Probe for NAND controller */ -static int __devinit lpc32xx_nand_probe(struct platform_device *pdev) +static int lpc32xx_nand_probe(struct platform_device *pdev) { struct lpc32xx_nand_host *host; struct mtd_info *mtd; @@ -845,7 +845,7 @@ err_exit1: /* * Remove NAND device */ -static int __devexit lpc32xx_nand_remove(struct platform_device *pdev) +static int lpc32xx_nand_remove(struct platform_device *pdev) { struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); struct mtd_info *mtd = &host->mtd; @@ -907,7 +907,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); static struct platform_driver lpc32xx_nand_driver = { .probe = lpc32xx_nand_probe, - .remove = __devexit_p(lpc32xx_nand_remove), + .remove = lpc32xx_nand_remove, .resume = lpc32xx_nand_resume, .suspend = lpc32xx_nand_suspend, .driver = { diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 32409c45d479..030b78c62895 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -755,7 +755,7 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) /* * Probe for NAND controller */ -static int __devinit lpc32xx_nand_probe(struct platform_device *pdev) +static int lpc32xx_nand_probe(struct platform_device *pdev) { struct lpc32xx_nand_host *host; struct mtd_info *mtd; @@ -949,7 +949,7 @@ err_exit1: /* * Remove NAND device. */ -static int __devexit lpc32xx_nand_remove(struct platform_device *pdev) +static int lpc32xx_nand_remove(struct platform_device *pdev) { uint32_t tmp; struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); @@ -1021,7 +1021,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); static struct platform_driver lpc32xx_nand_driver = { .probe = lpc32xx_nand_probe, - .remove = __devexit_p(lpc32xx_nand_remove), + .remove = lpc32xx_nand_remove, .resume = lpc32xx_nand_resume, .suspend = lpc32xx_nand_suspend, .driver = { diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index f776c8577b8c..3c9cdcbc4cba 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -626,7 +626,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) iounmap(prv->csreg); } -static int __devinit mpc5121_nfc_probe(struct platform_device *op) +static int mpc5121_nfc_probe(struct platform_device *op) { struct device_node *rootnode, *dn = op->dev.of_node; struct device *dev = &op->dev; @@ -827,7 +827,7 @@ error: return retval; } -static int __devexit mpc5121_nfc_remove(struct platform_device *op) +static int mpc5121_nfc_remove(struct platform_device *op) { struct device *dev = &op->dev; struct mtd_info *mtd = dev_get_drvdata(dev); @@ -841,14 +841,14 @@ static int __devexit mpc5121_nfc_remove(struct platform_device *op) return 0; } -static struct of_device_id mpc5121_nfc_match[] __devinitdata = { +static struct of_device_id mpc5121_nfc_match[] = { { .compatible = "fsl,mpc5121-nfc", }, {}, }; static struct platform_driver mpc5121_nfc_driver = { .probe = mpc5121_nfc_probe, - .remove = __devexit_p(mpc5121_nfc_remove), + .remove = mpc5121_nfc_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 022dcdc256fb..45204e41a028 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -266,7 +266,8 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL }; +static const char const *part_probes[] = { + "cmdlinepart", "RedBoot", "ofpart", NULL }; static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) { @@ -1378,7 +1379,7 @@ static int __init mxcnd_probe_dt(struct mxc_nand_host *host) } #endif -static int __devinit mxcnd_probe(struct platform_device *pdev) +static int mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; struct mtd_info *mtd; @@ -1556,12 +1557,13 @@ static int __devinit mxcnd_probe(struct platform_device *pdev) return 0; escan: - clk_disable_unprepare(host->clk); + if (host->clk_act) + clk_disable_unprepare(host->clk); return err; } -static int __devexit mxcnd_remove(struct platform_device *pdev) +static int mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); @@ -1580,7 +1582,7 @@ static struct platform_driver mxcnd_driver = { }, .id_table = mxcnd_devtype, .probe = mxcnd_probe, - .remove = __devexit_p(mxcnd_remove), + .remove = mxcnd_remove, }; module_platform_driver(mxcnd_driver); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1a03b7f673ce..8323ac991ad1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -93,8 +93,7 @@ static struct nand_ecclayout nand_oob_128 = { .length = 78} } }; -static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, - int new_state); +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); @@ -130,15 +129,12 @@ static int check_offs_len(struct mtd_info *mtd, * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * - * Deselect, release chip lock and wake up anyone waiting on the device. + * 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->priv; - /* De-select the NAND device */ - chip->select_chip(mtd, -1); - /* Release the controller and the chip */ spin_lock(&chip->controller->lock); chip->controller->active = NULL; @@ -160,7 +156,7 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) } /** - * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * @mtd: MTD device structure * @@ -303,7 +299,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); /* Select the NAND device */ chip->select_chip(mtd, chipnr); @@ -333,8 +329,10 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) i++; } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); - if (getchip) + if (getchip) { + chip->select_chip(mtd, -1); nand_release_device(mtd); + } return res; } @@ -383,7 +381,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) struct mtd_oob_ops ops; loff_t wr_ofs = ofs; - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(mtd, FL_WRITING); ops.datbuf = NULL; ops.oobbuf = buf; @@ -492,7 +490,7 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - unsigned long timeo = jiffies + 2; + unsigned long timeo = jiffies + msecs_to_jiffies(20); /* 400ms timeout */ if (in_interrupt() || oops_in_progress) @@ -750,15 +748,15 @@ static void panic_nand_get_device(struct nand_chip *chip, /** * 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 * * Get the device and lock it for exclusive access */ static int -nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) +nand_get_device(struct mtd_info *mtd, int new_state) { + struct nand_chip *chip = mtd->priv; spinlock_t *lock = &chip->controller->lock; wait_queue_head_t *wq = &chip->controller->wq; DECLARE_WAITQUEUE(wait, current); @@ -865,6 +863,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) led_trigger_event(nand_led_trigger, LED_OFF); status = (int)chip->read_byte(mtd); + /* This can happen if in case of timeout or buggy dev_ready */ + WARN_ON(!(status & NAND_STATUS_READY)); return status; } @@ -899,7 +899,7 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, /* Call wait ready function */ status = chip->waitfunc(mtd, chip); /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { pr_debug("%s: error status = 0x%08x\n", __func__, status); ret = -EIO; @@ -932,7 +932,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ofs + len == mtd->size) len -= mtd->erasesize; - nand_get_device(chip, mtd, FL_UNLOCKING); + nand_get_device(mtd, FL_UNLOCKING); /* Shift to get chip number */ chipnr = ofs >> chip->chip_shift; @@ -950,6 +950,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ret = __nand_unlock(mtd, ofs, len, 0); out: + chip->select_chip(mtd, -1); nand_release_device(mtd); return ret; @@ -981,7 +982,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (check_offs_len(mtd, ofs, len)) ret = -EINVAL; - nand_get_device(chip, mtd, FL_LOCKING); + nand_get_device(mtd, FL_LOCKING); /* Shift to get chip number */ chipnr = ofs >> chip->chip_shift; @@ -1004,7 +1005,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) /* Call wait ready function */ status = chip->waitfunc(mtd, chip); /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { pr_debug("%s: error status = 0x%08x\n", __func__, status); ret = -EIO; @@ -1014,6 +1015,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ret = __nand_unlock(mtd, ofs, len, 0x1); out: + chip->select_chip(mtd, -1); nand_release_device(mtd); return ret; @@ -1550,6 +1552,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, chipnr); } } + chip->select_chip(mtd, -1); ops->retlen = ops->len - (size_t) readlen; if (oob) @@ -1577,11 +1580,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { - struct nand_chip *chip = mtd->priv; struct mtd_oob_ops ops; int ret; - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; @@ -1804,6 +1806,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, chipnr); } } + chip->select_chip(mtd, -1); ops->oobretlen = ops->ooblen - readlen; @@ -1827,7 +1830,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; @@ -1839,7 +1841,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -2186,8 +2188,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, chip->select_chip(mtd, chipnr); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) - return -EIO; + if (nand_check_wp(mtd)) { + ret = -EIO; + goto err_out; + } realpage = (int)(to >> chip->page_shift); page = realpage & chip->pagemask; @@ -2199,8 +2203,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, chip->pagebuf = -1; /* Don't allow multipage oob writes with offset */ - if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) - return -EINVAL; + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { + ret = -EINVAL; + goto err_out; + } while (1) { int bytes = mtd->writesize; @@ -2251,6 +2257,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ops->retlen = ops->len - writelen; if (unlikely(oob)) ops->oobretlen = ops->ooblen; + +err_out: + chip->select_chip(mtd, -1); return ret; } @@ -2302,11 +2311,10 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { - struct nand_chip *chip = mtd->priv; struct mtd_oob_ops ops; int ret; - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(mtd, FL_WRITING); ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; @@ -2377,8 +2385,10 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) + 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) @@ -2391,6 +2401,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, else status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + chip->select_chip(mtd, -1); + if (status) return status; @@ -2408,7 +2420,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; @@ -2420,7 +2431,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; } - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(mtd, FL_WRITING); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -2513,7 +2524,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, return -EINVAL; /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_ERASING); + nand_get_device(mtd, FL_ERASING); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); @@ -2623,6 +2634,7 @@ 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 */ @@ -2658,12 +2670,10 @@ erase_exit: */ static void nand_sync(struct mtd_info *mtd) { - struct nand_chip *chip = mtd->priv; - pr_debug("%s: called\n", __func__); /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_SYNCING); + nand_get_device(mtd, FL_SYNCING); /* Release it and go back */ nand_release_device(mtd); } @@ -2749,9 +2759,7 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, */ static int nand_suspend(struct mtd_info *mtd) { - struct nand_chip *chip = mtd->priv; - - return nand_get_device(chip, mtd, FL_PM_SUSPENDED); + return nand_get_device(mtd, FL_PM_SUSPENDED); } /** @@ -2849,6 +2857,8 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int i; int val; + /* ONFI need to be probed in 8 bits mode */ + WARN_ON(chip->options & NAND_BUSWIDTH_16); /* Try ONFI for unknown chip or LP */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || @@ -2913,7 +2923,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, * * 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 2). This is a helper function for nand_id_len(). Returns non-zero + * 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) @@ -3242,11 +3252,15 @@ ident_done: break; } - /* - * Check, if buswidth is correct. Hardware drivers should set - * chip correct! - */ - if (busw != (chip->options & NAND_BUSWIDTH_16)) { + if (chip->options & NAND_BUSWIDTH_AUTO) { + WARN_ON(chip->options & NAND_BUSWIDTH_16); + chip->options |= busw; + nand_set_defaults(chip, busw); + } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct! + */ pr_info("NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, mtd->name); @@ -3285,10 +3299,10 @@ ident_done: chip->cmdfunc = nand_command_lp; pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," - " page size: %d, OOB size: %d\n", + " %dMiB, page size: %d, OOB size: %d\n", *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, chip->onfi_version ? chip->onfi_params.model : type->name, - mtd->writesize, mtd->oobsize); + (int)(chip->chipsize >> 20), mtd->writesize, mtd->oobsize); return type; } @@ -3327,6 +3341,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, return PTR_ERR(type); } + chip->select_chip(mtd, -1); + /* Check for a chip array */ for (i = 1; i < maxchips; i++) { chip->select_chip(mtd, i); @@ -3336,8 +3352,11 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != chip->read_byte(mtd) || - nand_dev_id != chip->read_byte(mtd)) + nand_dev_id != chip->read_byte(mtd)) { + chip->select_chip(mtd, -1); break; + } + chip->select_chip(mtd, -1); } if (i > 1) pr_info("%d NAND chips detected\n", i); @@ -3596,9 +3615,6 @@ int nand_scan_tail(struct mtd_info *mtd) /* Initialize state */ chip->state = FL_READY; - /* De-select the device */ - chip->select_chip(mtd, -1); - /* Invalidate the pagebuffer reference */ chip->pagebuf = -1; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index c3c13e64a2f0..818b65c85d12 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -42,6 +42,8 @@ #include <linux/sched.h> #include <linux/fs.h> #include <linux/pagemap.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -105,7 +107,6 @@ static char *weakblocks = NULL; static char *weakpages = NULL; static unsigned int bitflips = 0; static char *gravepages = NULL; -static unsigned int rptwear = 0; static unsigned int overridesize = 0; static char *cache_file = NULL; static unsigned int bbt; @@ -130,7 +131,6 @@ module_param(weakblocks, charp, 0400); module_param(weakpages, charp, 0400); module_param(bitflips, uint, 0400); module_param(gravepages, charp, 0400); -module_param(rptwear, uint, 0400); module_param(overridesize, uint, 0400); module_param(cache_file, charp, 0400); module_param(bbt, uint, 0400); @@ -162,7 +162,6 @@ MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (z 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(rptwear, "Number of erases between reporting wear, if not zero"); 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"); @@ -286,6 +285,11 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " /* 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. */ @@ -365,6 +369,8 @@ struct nandsim { void *file_buf; struct page *held_pages[NS_MAX_HELD_PAGES]; int held_cnt; + + struct nandsim_debug_info dbg; }; /* @@ -442,11 +448,123 @@ 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; -static unsigned int rptwear_cnt = 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 nandsim_debug_info *dbg = &dev->dbg; + 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); + + NS_ERR("cannot create \"nandsim\" debugfs directory, err %d\n", + err); + return err; + } + 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); +} + /* * Allocate array of page pointers, create slab allocation for an array * and initialize the array by NULL pointers. @@ -911,8 +1029,6 @@ static int setup_wear_reporting(struct mtd_info *mtd) { size_t mem; - if (!rptwear) - return 0; wear_eb_count = div_u64(mtd->size, mtd->erasesize); mem = wear_eb_count * sizeof(unsigned long); if (mem / sizeof(unsigned long) != wear_eb_count) { @@ -929,64 +1045,18 @@ static int setup_wear_reporting(struct mtd_info *mtd) static void update_wear(unsigned int erase_block_no) { - unsigned long wmin = -1, wmax = 0, avg; - unsigned long deciles[10], decile_max[10], tot = 0; - unsigned int i; - 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); - rptwear_cnt += 1; - if (rptwear_cnt < rptwear) - return; - rptwear_cnt = 0; - /* 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 */ - NS_INFO("*** Wear Report ***\n"); - NS_INFO("Total numbers of erases: %lu\n", tot); - NS_INFO("Number of erase blocks: %u\n", wear_eb_count); - NS_INFO("Average number of erases: %lu\n", avg); - NS_INFO("Maximum number of erases: %lu\n", wmax); - NS_INFO("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; - NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", - from, - decile_max[i], - deciles[i]); - } - NS_INFO("*** End of Wear Report ***\n"); } /* @@ -2327,6 +2397,9 @@ 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; @@ -2366,6 +2439,7 @@ static void __exit ns_cleanup_module(void) struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv; 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/ndfc.c b/drivers/mtd/nand/ndfc.c index 5fd3f010e3ae..8e148f1478fd 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -197,7 +197,7 @@ err: return ret; } -static int __devinit ndfc_probe(struct platform_device *ofdev) +static int ndfc_probe(struct platform_device *ofdev) { struct ndfc_controller *ndfc; const __be32 *reg; @@ -256,7 +256,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev) return 0; } -static int __devexit ndfc_remove(struct platform_device *ofdev) +static int ndfc_remove(struct platform_device *ofdev) { struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); @@ -279,7 +279,7 @@ static struct platform_driver ndfc_driver = { .of_match_table = ndfc_match, }, .probe = ndfc_probe, - .remove = __devexit_p(ndfc_remove), + .remove = ndfc_remove, }; module_platform_driver(ndfc_driver); diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c deleted file mode 100644 index 9ee0c4edfacf..000000000000 --- a/drivers/mtd/nand/nomadik_nand.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * drivers/mtd/nand/nomadik_nand.c - * - * Overview: - * Driver for on-board NAND flash on Nomadik Platforms - * - * Copyright © 2007 STMicroelectronics Pvt. Ltd. - * Author: Sachin Verma <sachin.verma@st.com> - * - * Copyright © 2009 Alessandro Rubini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/nand_ecc.h> -#include <linux/platform_device.h> -#include <linux/mtd/partitions.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/platform_data/mtd-nomadik-nand.h> -#include <mach/fsmc.h> - -#include <mtd/mtd-abi.h> - -struct nomadik_nand_host { - struct mtd_info mtd; - struct nand_chip nand; - void __iomem *data_va; - void __iomem *cmd_va; - void __iomem *addr_va; - struct nand_bbt_descr *bbt_desc; -}; - -static struct nand_ecclayout nomadik_ecc_layout = { - .eccbytes = 3 * 4, - .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ - 0x02, 0x03, 0x04, - 0x12, 0x13, 0x14, - 0x22, 0x23, 0x24, - 0x32, 0x33, 0x34}, - /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */ - .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, -}; - -static void nomadik_ecc_control(struct mtd_info *mtd, int mode) -{ - /* No need to enable hw ecc, it's on by default */ -} - -static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *nand = mtd->priv; - struct nomadik_nand_host *host = nand->priv; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, host->cmd_va); - else - writeb(cmd, host->addr_va); -} - -static int nomadik_nand_probe(struct platform_device *pdev) -{ - struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; - struct nomadik_nand_host *host; - struct mtd_info *mtd; - struct nand_chip *nand; - struct resource *res; - int ret = 0; - - /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL); - if (!host) { - dev_err(&pdev->dev, "Failed to allocate device structure.\n"); - return -ENOMEM; - } - - /* Call the client's init function, if any */ - if (pdata->init) - ret = pdata->init(); - if (ret < 0) { - dev_err(&pdev->dev, "Init function failed\n"); - goto err; - } - - /* ioremap three regions */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); - if (!res) { - ret = -EIO; - goto err_unmap; - } - host->addr_va = ioremap(res->start, resource_size(res)); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) { - ret = -EIO; - goto err_unmap; - } - host->data_va = ioremap(res->start, resource_size(res)); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); - if (!res) { - ret = -EIO; - goto err_unmap; - } - host->cmd_va = ioremap(res->start, resource_size(res)); - - if (!host->addr_va || !host->data_va || !host->cmd_va) { - ret = -ENOMEM; - goto err_unmap; - } - - /* Link all private pointers */ - mtd = &host->mtd; - nand = &host->nand; - mtd->priv = nand; - nand->priv = host; - - host->mtd.owner = THIS_MODULE; - nand->IO_ADDR_R = host->data_va; - nand->IO_ADDR_W = host->data_va; - nand->cmd_ctrl = nomadik_cmd_ctrl; - - /* - * This stanza declares ECC_HW but uses soft routines. It's because - * HW claims to make the calculation but not the correction. However, - * I haven't managed to get the desired data out of it until now. - */ - nand->ecc.mode = NAND_ECC_SOFT; - nand->ecc.layout = &nomadik_ecc_layout; - nand->ecc.hwctl = nomadik_ecc_control; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - - nand->options = pdata->options; - - /* - * Scan to find existence of the device - */ - if (nand_scan(&host->mtd, 1)) { - ret = -ENXIO; - goto err_unmap; - } - - mtd_device_register(&host->mtd, pdata->parts, pdata->nparts); - - platform_set_drvdata(pdev, host); - return 0; - - err_unmap: - if (host->cmd_va) - iounmap(host->cmd_va); - if (host->data_va) - iounmap(host->data_va); - if (host->addr_va) - iounmap(host->addr_va); - err: - kfree(host); - return ret; -} - -/* - * Clean up routine - */ -static int nomadik_nand_remove(struct platform_device *pdev) -{ - struct nomadik_nand_host *host = platform_get_drvdata(pdev); - struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; - - if (pdata->exit) - pdata->exit(); - - if (host) { - nand_release(&host->mtd); - iounmap(host->cmd_va); - iounmap(host->data_va); - iounmap(host->addr_va); - kfree(host); - } - return 0; -} - -static int nomadik_nand_suspend(struct device *dev) -{ - struct nomadik_nand_host *host = dev_get_drvdata(dev); - int ret = 0; - if (host) - ret = mtd_suspend(&host->mtd); - return ret; -} - -static int nomadik_nand_resume(struct device *dev) -{ - struct nomadik_nand_host *host = dev_get_drvdata(dev); - if (host) - mtd_resume(&host->mtd); - return 0; -} - -static const struct dev_pm_ops nomadik_nand_pm_ops = { - .suspend = nomadik_nand_suspend, - .resume = nomadik_nand_resume, -}; - -static struct platform_driver nomadik_nand_driver = { - .probe = nomadik_nand_probe, - .remove = nomadik_nand_remove, - .driver = { - .owner = THIS_MODULE, - .name = "nomadik_nand", - .pm = &nomadik_nand_pm_ops, - }, -}; - -module_platform_driver(nomadik_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)"); -MODULE_DESCRIPTION("NAND driver for Nomadik Platform"); diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 94dc46bc118c..a6191198d259 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -246,7 +246,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand) spin_unlock(&nand->lock); } -static int __devinit nuc900_nand_probe(struct platform_device *pdev) +static int nuc900_nand_probe(struct platform_device *pdev) { struct nuc900_nand *nuc900_nand; struct nand_chip *chip; @@ -317,7 +317,7 @@ fail1: kfree(nuc900_nand); return retval; } -static int __devexit nuc900_nand_remove(struct platform_device *pdev) +static int nuc900_nand_remove(struct platform_device *pdev) { struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); struct resource *res; @@ -340,7 +340,7 @@ static int __devexit nuc900_nand_remove(struct platform_device *pdev) static struct platform_driver nuc900_nand_driver = { .probe = nuc900_nand_probe, - .remove = __devexit_p(nuc900_nand_remove), + .remove = nuc900_nand_remove, .driver = { .name = "nuc900-fmi", .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 1f34ba104ef4..0002d5e94f0d 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1323,7 +1323,7 @@ static void omap3_free_bch(struct mtd_info *mtd) } #endif /* CONFIG_MTD_NAND_OMAP_BCH */ -static int __devinit omap_nand_probe(struct platform_device *pdev) +static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; struct omap_nand_platform_data *pdata; diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index aefaf8cd31ef..cd72b9299f6b 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -194,7 +194,7 @@ no_res: return ret; } -static int __devexit orion_nand_remove(struct platform_device *pdev) +static int orion_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); struct nand_chip *nc = mtd->priv; @@ -223,7 +223,7 @@ static struct of_device_id orion_nand_of_match_table[] = { #endif static struct platform_driver orion_nand_driver = { - .remove = __devexit_p(orion_nand_remove), + .remove = orion_nand_remove, .driver = { .name = "orion_nand", .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 1440e51cedcc..5a67082c07ee 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -89,7 +89,7 @@ int pasemi_device_ready(struct mtd_info *mtd) return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); } -static int __devinit pasemi_nand_probe(struct platform_device *ofdev) +static int pasemi_nand_probe(struct platform_device *ofdev) { struct pci_dev *pdev; struct device_node *np = ofdev->dev.of_node; @@ -184,7 +184,7 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev) return err; } -static int __devexit pasemi_nand_remove(struct platform_device *ofdev) +static int pasemi_nand_remove(struct platform_device *ofdev) { struct nand_chip *chip; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index a47ee68a0cfa..c004566a9ad2 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -28,7 +28,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL }; /* * Probe for the NAND device. */ -static int __devinit plat_nand_probe(struct platform_device *pdev) +static int plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; struct mtd_part_parser_data ppdata; @@ -134,7 +134,7 @@ out_free: /* * Remove a NAND device. */ -static int __devexit plat_nand_remove(struct platform_device *pdev) +static int plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); struct platform_nand_data *pdata = pdev->dev.platform_data; @@ -160,7 +160,7 @@ MODULE_DEVICE_TABLE(of, plat_nand_match); static struct platform_driver plat_nand_driver = { .probe = plat_nand_probe, - .remove = __devexit_p(plat_nand_remove), + .remove = plat_nand_remove, .driver = { .name = "gen_nand", .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 79ded48e7427..df954b4dcba2 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -730,11 +730,14 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_set *set) { - if (set) + if (set) { mtd->mtd.name = set->name; - return mtd_device_parse_register(&mtd->mtd, NULL, NULL, + return mtd_device_parse_register(&mtd->mtd, NULL, NULL, set->partitions, set->nr_partitions); + } + + return -ENODEV; } /** diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index f48ac5d80bbf..57b3971c9c0a 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -23,11 +23,18 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/completion.h> #include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/sh_dma.h> #include <linux/slab.h> #include <linux/string.h> @@ -106,6 +113,84 @@ static void wait_completion(struct sh_flctl *flctl) 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 = pdev->dev.platform_data; + 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 *)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.slave_id = pdata->slave_id_fifo0_tx; + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl); + 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 *)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.slave_id = pdata->slave_id_fifo0_rx; + cfg.direction = DMA_DEV_TO_MEM; + cfg.dst_addr = 0; + cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl); + 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); @@ -225,7 +310,7 @@ static enum flctl_ecc_res_t wait_recfifo_ready for (i = 0; i < 3; i++) { uint8_t org; - int index; + unsigned int index; data = readl(ecc_reg[i]); @@ -261,6 +346,70 @@ static void wait_wecfifo_ready(struct sh_flctl *flctl) 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 = -EINVAL; + 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_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); + + 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) { + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + 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; @@ -279,11 +428,20 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int 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)); - buf[i] = be32_to_cpu(buf[i]); } + +convert: + for (i = 0; i < len_4align; i++) + buf[i] = be32_to_cpu(buf[i]); } static enum flctl_ecc_res_t read_ecfiforeg @@ -305,28 +463,39 @@ static enum flctl_ecc_res_t read_ecfiforeg return res; } -static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) +static void write_fiforeg(struct sh_flctl *flctl, int rlen, + unsigned int offset) { int i, len_4align; - unsigned long *data = (unsigned long *)&flctl->done_buff[offset]; - void *fifo_addr = (void *)FLDTFIFO(flctl); + 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(data[i]), fifo_addr); + writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); } } -static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, int offset) +static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, + unsigned int offset) { int i, len_4align; - unsigned long *data = (unsigned long *)&flctl->done_buff[offset]; + 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(cpu_to_be32(data[i]), FLECFIFO(flctl)); + writel(buf[i], FLECFIFO(flctl)); } } @@ -750,41 +919,35 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr) static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - int index = flctl->index; - memcpy(&flctl->done_buff[index], buf, len); + 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); - int index = flctl->index; uint8_t data; - data = flctl->done_buff[index]; + 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); - int index = flctl->index; - uint16_t data; - uint16_t *buf = (uint16_t *)&flctl->done_buff[index]; + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index]; - data = *buf; - flctl->index += 2; - return data; + 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); - int index = flctl->index; - memcpy(buf, &flctl->done_buff[index], len); + memcpy(buf, &flctl->done_buff[flctl->index], len); flctl->index += len; } @@ -858,7 +1021,74 @@ static irqreturn_t flctl_handle_flste(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit flctl_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +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; + struct device_node *dn = dev->of_node; + int ret; + + 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) { + dev_err(dev, "%s: failed to allocate config data\n", __func__); + return NULL; + } + + /* set SoC specific options */ + pdata->flcmncr_val = config->flcmncr_val; + pdata->has_hwecc = config->has_hwecc; + pdata->use_holden = config->use_holden; + + /* parse user defined options */ + ret = of_get_nand_bus_width(dn); + if (ret == 16) + pdata->flcmncr_val |= SEL_16BIT; + else if (ret != 8) { + dev_err(dev, "%s: invalid bus width\n", __func__); + return NULL; + } + + return pdata; +} +#else /* CONFIG_OF */ +#define of_flctl_match NULL +static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static int flctl_probe(struct platform_device *pdev) { struct resource *res; struct sh_flctl *flctl; @@ -867,12 +1097,7 @@ static int __devinit flctl_probe(struct platform_device *pdev) struct sh_flctl_platform_data *pdata; int ret = -ENXIO; int irq; - - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } + struct mtd_part_parser_data ppdata = {}; flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL); if (!flctl) { @@ -904,6 +1129,17 @@ static int __devinit flctl_probe(struct platform_device *pdev) goto err_flste; } + if (pdev->dev.of_node) + pdata = flctl_parse_dt(&pdev->dev); + else + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "no setup data defined\n"); + ret = -EINVAL; + goto err_pdata; + } + platform_set_drvdata(pdev, flctl); flctl_mtd = &flctl->mtd; nand = &flctl->chip; @@ -932,6 +1168,8 @@ static int __devinit flctl_probe(struct platform_device *pdev) 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; @@ -944,12 +1182,16 @@ static int __devinit flctl_probe(struct platform_device *pdev) if (ret) goto err_chip; - mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(flctl_mtd, NULL, &ppdata, pdata->parts, + pdata->nr_parts); return 0; err_chip: + flctl_release_dma(flctl); pm_runtime_disable(&pdev->dev); +err_pdata: free_irq(irq, flctl); err_flste: iounmap(flctl->reg); @@ -958,10 +1200,11 @@ err_iomap: return ret; } -static int __devexit flctl_remove(struct platform_device *pdev) +static int flctl_remove(struct platform_device *pdev) { struct sh_flctl *flctl = platform_get_drvdata(pdev); + flctl_release_dma(flctl); nand_release(&flctl->mtd); pm_runtime_disable(&pdev->dev); free_irq(platform_get_irq(pdev, 0), flctl); @@ -976,6 +1219,7 @@ static struct platform_driver flctl_driver = { .driver = { .name = "sh_flctl", .owner = THIS_MODULE, + .of_match_table = of_flctl_match, }, }; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 3421e3762a5a..127bc4271821 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -106,7 +106,7 @@ static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, /* * Main initialization routine */ -static int __devinit sharpsl_nand_probe(struct platform_device *pdev) +static int sharpsl_nand_probe(struct platform_device *pdev) { struct nand_chip *this; struct resource *r; @@ -205,7 +205,7 @@ err_get_res: /* * Clean up routine */ -static int __devexit sharpsl_nand_remove(struct platform_device *pdev) +static int sharpsl_nand_remove(struct platform_device *pdev) { struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); @@ -228,7 +228,7 @@ static struct platform_driver sharpsl_nand_driver = { .owner = THIS_MODULE, }, .probe = sharpsl_nand_probe, - .remove = __devexit_p(sharpsl_nand_remove), + .remove = sharpsl_nand_remove, }; module_platform_driver(sharpsl_nand_driver); diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index f3f28fafbf7a..09dde7d27178 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -140,7 +140,7 @@ static int socrates_nand_device_ready(struct mtd_info *mtd) /* * Probe for the NAND device. */ -static int __devinit socrates_nand_probe(struct platform_device *ofdev) +static int socrates_nand_probe(struct platform_device *ofdev) { struct socrates_nand_host *host; struct mtd_info *mtd; @@ -220,7 +220,7 @@ out: /* * Remove a NAND device. */ -static int __devexit socrates_nand_remove(struct platform_device *ofdev) +static int socrates_nand_remove(struct platform_device *ofdev) { struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); struct mtd_info *mtd = &host->mtd; @@ -251,7 +251,7 @@ static struct platform_driver socrates_nand_driver = { .of_match_table = socrates_nand_match, }, .probe = socrates_nand_probe, - .remove = __devexit_p(socrates_nand_remove), + .remove = socrates_nand_remove, }; module_platform_driver(socrates_nand_driver); diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index d9127e2ed808..dbd3aa574eaf 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -71,7 +71,10 @@ static int parse_ofpart_partitions(struct mtd_info *master, (*pparts)[i].name = (char *)partname; if (of_get_property(pp, "read-only", &len)) - (*pparts)[i].mask_flags = MTD_WRITEABLE; + (*pparts)[i].mask_flags |= MTD_WRITEABLE; + + if (of_get_property(pp, "lock", &len)) + (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK; i++; } diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 1c4f97c63e62..9f11562f849d 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -35,7 +35,7 @@ struct onenand_info { struct onenand_chip onenand; }; -static int __devinit generic_onenand_probe(struct platform_device *pdev) +static int generic_onenand_probe(struct platform_device *pdev) { struct onenand_info *info; struct onenand_platform_data *pdata = pdev->dev.platform_data; @@ -88,7 +88,7 @@ out_free_info: return err; } -static int __devexit generic_onenand_remove(struct platform_device *pdev) +static int generic_onenand_remove(struct platform_device *pdev) { struct onenand_info *info = platform_get_drvdata(pdev); struct resource *res = pdev->resource; @@ -112,7 +112,7 @@ static struct platform_driver generic_onenand_driver = { .owner = THIS_MODULE, }, .probe = generic_onenand_probe, - .remove = __devexit_p(generic_onenand_remove), + .remove = generic_onenand_remove, }; module_platform_driver(generic_onenand_driver); diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 00cd3da29435..065f3fe02a2f 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -630,7 +630,7 @@ static int omap2_onenand_disable(struct mtd_info *mtd) return ret; } -static int __devinit omap2_onenand_probe(struct platform_device *pdev) +static int omap2_onenand_probe(struct platform_device *pdev) { struct omap_onenand_platform_data *pdata; struct omap2_onenand *c; @@ -799,7 +799,7 @@ err_kfree: return r; } -static int __devexit omap2_onenand_remove(struct platform_device *pdev) +static int omap2_onenand_remove(struct platform_device *pdev) { struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); @@ -822,7 +822,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev) static struct platform_driver omap2_onenand_driver = { .probe = omap2_onenand_probe, - .remove = __devexit_p(omap2_onenand_remove), + .remove = omap2_onenand_remove, .shutdown = omap2_onenand_shutdown, .driver = { .name = DRIVER_NAME, diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 8e4b3f2742ba..33f2a8fb8df9 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -1053,7 +1053,7 @@ onenand_fail: return err; } -static int __devexit s3c_onenand_remove(struct platform_device *pdev) +static int s3c_onenand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); @@ -1130,7 +1130,7 @@ static struct platform_driver s3c_onenand_driver = { }, .id_table = s3c_onenand_driver_ids, .probe = s3c_onenand_probe, - .remove = __devexit_p(s3c_onenand_remove), + .remove = s3c_onenand_remove, }; module_platform_driver(s3c_onenand_driver); diff --git a/drivers/mtd/tests/mtd_nandbiterrs.c b/drivers/mtd/tests/mtd_nandbiterrs.c index cc8d62cb280c..207bf9a9972f 100644 --- a/drivers/mtd/tests/mtd_nandbiterrs.c +++ b/drivers/mtd/tests/mtd_nandbiterrs.c @@ -39,6 +39,9 @@ * this program; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -47,8 +50,6 @@ #include <linux/mtd/nand.h> #include <linux/slab.h> -#define msg(FMT, VA...) pr_info("mtd_nandbiterrs: "FMT, ##VA) - static int dev; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -103,7 +104,7 @@ static int erase_block(void) struct erase_info ei; loff_t addr = eraseblock * mtd->erasesize; - msg("erase_block\n"); + pr_info("erase_block\n"); memset(&ei, 0, sizeof(struct erase_info)); ei.mtd = mtd; @@ -112,7 +113,7 @@ static int erase_block(void) err = mtd_erase(mtd, &ei); if (err || ei.state == MTD_ERASE_FAILED) { - msg("error %d while erasing\n", err); + pr_err("error %d while erasing\n", err); if (!err) err = -EIO; return err; @@ -128,11 +129,11 @@ static int write_page(int log) size_t written; if (log) - msg("write_page\n"); + pr_info("write_page\n"); err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer); if (err || written != mtd->writesize) { - msg("error: write failed at %#llx\n", (long long)offset); + pr_err("error: write failed at %#llx\n", (long long)offset); if (!err) err = -EIO; } @@ -147,7 +148,7 @@ static int rewrite_page(int log) struct mtd_oob_ops ops; if (log) - msg("rewrite page\n"); + pr_info("rewrite page\n"); ops.mode = MTD_OPS_RAW; /* No ECC */ ops.len = mtd->writesize; @@ -160,7 +161,7 @@ static int rewrite_page(int log) err = mtd_write_oob(mtd, offset, &ops); if (err || ops.retlen != mtd->writesize) { - msg("error: write_oob failed (%d)\n", err); + pr_err("error: write_oob failed (%d)\n", err); if (!err) err = -EIO; } @@ -177,7 +178,7 @@ static int read_page(int log) struct mtd_ecc_stats oldstats; if (log) - msg("read_page\n"); + pr_info("read_page\n"); /* Saving last mtd stats */ memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats)); @@ -187,7 +188,7 @@ static int read_page(int log) err = mtd->ecc_stats.corrected - oldstats.corrected; if (err < 0 || read != mtd->writesize) { - msg("error: read failed at %#llx\n", (long long)offset); + pr_err("error: read failed at %#llx\n", (long long)offset); if (err >= 0) err = -EIO; } @@ -201,11 +202,11 @@ static int verify_page(int log) unsigned i, errs = 0; if (log) - msg("verify_page\n"); + pr_info("verify_page\n"); for (i = 0; i < mtd->writesize; i++) { if (rbuffer[i] != hash(i+seed)) { - msg("Error: page offset %u, expected %02x, got %02x\n", + pr_err("Error: page offset %u, expected %02x, got %02x\n", i, hash(i+seed), rbuffer[i]); errs++; } @@ -230,13 +231,13 @@ static int insert_biterror(unsigned byte) for (bit = 7; bit >= 0; bit--) { if (CBIT(wbuffer[byte], bit)) { BCLR(wbuffer[byte], bit); - msg("Inserted biterror @ %u/%u\n", byte, bit); + pr_info("Inserted biterror @ %u/%u\n", byte, bit); return 0; } } byte++; } - msg("biterror: Failed to find a '1' bit\n"); + pr_err("biterror: Failed to find a '1' bit\n"); return -EIO; } @@ -248,7 +249,7 @@ static int incremental_errors_test(void) unsigned i; unsigned errs_per_subpage = 0; - msg("incremental biterrors test\n"); + pr_info("incremental biterrors test\n"); for (i = 0; i < mtd->writesize; i++) wbuffer[i] = hash(i+seed); @@ -265,9 +266,9 @@ static int incremental_errors_test(void) err = read_page(1); if (err > 0) - msg("Read reported %d corrected bit errors\n", err); + pr_info("Read reported %d corrected bit errors\n", err); if (err < 0) { - msg("After %d biterrors per subpage, read reported error %d\n", + pr_err("After %d biterrors per subpage, read reported error %d\n", errs_per_subpage, err); err = 0; goto exit; @@ -275,11 +276,11 @@ static int incremental_errors_test(void) err = verify_page(1); if (err) { - msg("ECC failure, read data is incorrect despite read success\n"); + pr_err("ECC failure, read data is incorrect despite read success\n"); goto exit; } - msg("Successfully corrected %d bit errors per subpage\n", + pr_info("Successfully corrected %d bit errors per subpage\n", errs_per_subpage); for (i = 0; i < subcount; i++) { @@ -311,7 +312,7 @@ static int overwrite_test(void) memset(bitstats, 0, sizeof(bitstats)); - msg("overwrite biterrors test\n"); + pr_info("overwrite biterrors test\n"); for (i = 0; i < mtd->writesize; i++) wbuffer[i] = hash(i+seed); @@ -329,18 +330,18 @@ static int overwrite_test(void) err = read_page(0); if (err >= 0) { if (err >= MAXBITS) { - msg("Implausible number of bit errors corrected\n"); + pr_info("Implausible number of bit errors corrected\n"); err = -EIO; break; } bitstats[err]++; if (err > max_corrected) { max_corrected = err; - msg("Read reported %d corrected bit errors\n", + pr_info("Read reported %d corrected bit errors\n", err); } } else { /* err < 0 */ - msg("Read reported error %d\n", err); + pr_info("Read reported error %d\n", err); err = 0; break; } @@ -348,7 +349,7 @@ static int overwrite_test(void) err = verify_page(0); if (err) { bitstats[max_corrected] = opno; - msg("ECC failure, read data is incorrect despite read success\n"); + pr_info("ECC failure, read data is incorrect despite read success\n"); break; } @@ -357,9 +358,9 @@ static int overwrite_test(void) /* At this point bitstats[0] contains the number of ops with no bit * errors, bitstats[1] the number of ops with 1 bit error, etc. */ - msg("Bit error histogram (%d operations total):\n", opno); + pr_info("Bit error histogram (%d operations total):\n", opno); for (i = 0; i < max_corrected; i++) - msg("Page reads with %3d corrected bit errors: %d\n", + pr_info("Page reads with %3d corrected bit errors: %d\n", i, bitstats[i]); exit: @@ -370,36 +371,36 @@ static int __init mtd_nandbiterrs_init(void) { int err = 0; - msg("\n"); - msg("==================================================\n"); - msg("MTD device: %d\n", dev); + printk("\n"); + printk(KERN_INFO "==================================================\n"); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - msg("error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); goto exit_mtddev; } if (mtd->type != MTD_NANDFLASH) { - msg("this test requires NAND flash\n"); + pr_info("this test requires NAND flash\n"); err = -ENODEV; goto exit_nand; } - msg("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n", + pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n", (unsigned long long)mtd->size, mtd->erasesize, mtd->writesize, mtd->oobsize); subsize = mtd->writesize >> mtd->subpage_sft; subcount = mtd->writesize / subsize; - msg("Device uses %d subpages of %d bytes\n", subcount, subsize); + pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize); offset = page_offset * mtd->writesize; eraseblock = mtd_div_by_eb(offset, mtd); - msg("Using page=%u, offset=%llu, eraseblock=%u\n", + pr_info("Using page=%u, offset=%llu, eraseblock=%u\n", page_offset, offset, eraseblock); wbuffer = kmalloc(mtd->writesize, GFP_KERNEL); @@ -432,8 +433,8 @@ static int __init mtd_nandbiterrs_init(void) goto exit_error; err = -EIO; - msg("finished successfully.\n"); - msg("==================================================\n"); + pr_info("finished successfully.\n"); + printk(KERN_INFO "==================================================\n"); exit_error: kfree(rbuffer); diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c index b437fa425077..1eee264509a8 100644 --- a/drivers/mtd/tests/mtd_nandecctest.c +++ b/drivers/mtd/tests/mtd_nandecctest.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/list.h> @@ -264,13 +266,13 @@ static int nand_ecc_test_run(const size_t size) correct_data, size); if (err) { - pr_err("mtd_nandecctest: not ok - %s-%zd\n", + pr_err("not ok - %s-%zd\n", nand_ecc_test[i].name, size); dump_data_ecc(error_data, error_ecc, correct_data, correct_ecc, size); break; } - pr_info("mtd_nandecctest: ok - %s-%zd\n", + pr_info("ok - %s-%zd\n", nand_ecc_test[i].name, size); } error: diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index ed9b62827f1b..e827fa8cd844 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -19,6 +19,8 @@ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <asm/div64.h> #include <linux/init.h> #include <linux/module.h> @@ -28,8 +30,6 @@ #include <linux/slab.h> #include <linux/sched.h> -#define PRINT_PREF KERN_INFO "mtd_oobtest: " - static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -80,13 +80,12 @@ static int erase_eraseblock(int ebnum) err = mtd_erase(mtd, &ei); if (err) { - printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + pr_err("error %d while erasing EB %d\n", err, ebnum); return err; } if (ei.state == MTD_ERASE_FAILED) { - printk(PRINT_PREF "some erase error occurred at EB %d\n", - ebnum); + pr_err("some erase error occurred at EB %d\n", ebnum); return -EIO; } @@ -98,7 +97,7 @@ static int erase_whole_device(void) int err; unsigned int i; - printk(PRINT_PREF "erasing whole device\n"); + pr_info("erasing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -107,7 +106,7 @@ static int erase_whole_device(void) return err; cond_resched(); } - printk(PRINT_PREF "erased %u eraseblocks\n", i); + pr_info("erased %u eraseblocks\n", i); return 0; } @@ -141,9 +140,9 @@ static int write_eraseblock(int ebnum) ops.oobbuf = writebuf; err = mtd_write_oob(mtd, addr, &ops); if (err || ops.oobretlen != use_len) { - printk(PRINT_PREF "error: writeoob failed at %#llx\n", + pr_err("error: writeoob failed at %#llx\n", (long long)addr); - printk(PRINT_PREF "error: use_len %d, use_offset %d\n", + pr_err("error: use_len %d, use_offset %d\n", use_len, use_offset); errcnt += 1; return err ? err : -1; @@ -160,7 +159,7 @@ static int write_whole_device(void) int err; unsigned int i; - printk(PRINT_PREF "writing OOBs of whole device\n"); + pr_info("writing OOBs of whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -168,10 +167,10 @@ static int write_whole_device(void) if (err) return err; if (i % 256 == 0) - printk(PRINT_PREF "written up to eraseblock %u\n", i); + pr_info("written up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "written %u eraseblocks\n", i); + pr_info("written %u eraseblocks\n", i); return 0; } @@ -194,17 +193,17 @@ static int verify_eraseblock(int ebnum) ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); if (err || ops.oobretlen != use_len) { - printk(PRINT_PREF "error: readoob failed at %#llx\n", + pr_err("error: readoob failed at %#llx\n", (long long)addr); errcnt += 1; return err ? err : -1; } if (memcmp(readbuf, writebuf, use_len)) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; if (errcnt > 1000) { - printk(PRINT_PREF "error: too many errors\n"); + pr_err("error: too many errors\n"); return -1; } } @@ -221,29 +220,28 @@ static int verify_eraseblock(int ebnum) ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); if (err || ops.oobretlen != mtd->ecclayout->oobavail) { - printk(PRINT_PREF "error: readoob failed at " - "%#llx\n", (long long)addr); + pr_err("error: readoob failed at %#llx\n", + (long long)addr); errcnt += 1; return err ? err : -1; } if (memcmp(readbuf + use_offset, writebuf, use_len)) { - printk(PRINT_PREF "error: verify failed at " - "%#llx\n", (long long)addr); + pr_err("error: verify failed at %#llx\n", + (long long)addr); errcnt += 1; if (errcnt > 1000) { - printk(PRINT_PREF "error: too many " - "errors\n"); + pr_err("error: too many errors\n"); return -1; } } for (k = 0; k < use_offset; ++k) if (readbuf[k] != 0xff) { - printk(PRINT_PREF "error: verify 0xff " + pr_err("error: verify 0xff " "failed at %#llx\n", (long long)addr); errcnt += 1; if (errcnt > 1000) { - printk(PRINT_PREF "error: too " + pr_err("error: too " "many errors\n"); return -1; } @@ -251,12 +249,12 @@ static int verify_eraseblock(int ebnum) for (k = use_offset + use_len; k < mtd->ecclayout->oobavail; ++k) if (readbuf[k] != 0xff) { - printk(PRINT_PREF "error: verify 0xff " + pr_err("error: verify 0xff " "failed at %#llx\n", (long long)addr); errcnt += 1; if (errcnt > 1000) { - printk(PRINT_PREF "error: too " + pr_err("error: too " "many errors\n"); return -1; } @@ -286,17 +284,17 @@ static int verify_eraseblock_in_one_go(int ebnum) ops.oobbuf = readbuf; err = mtd_read_oob(mtd, addr, &ops); if (err || ops.oobretlen != len) { - printk(PRINT_PREF "error: readoob failed at %#llx\n", + pr_err("error: readoob failed at %#llx\n", (long long)addr); errcnt += 1; return err ? err : -1; } if (memcmp(readbuf, writebuf, len)) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; if (errcnt > 1000) { - printk(PRINT_PREF "error: too many errors\n"); + pr_err("error: too many errors\n"); return -1; } } @@ -309,7 +307,7 @@ static int verify_all_eraseblocks(void) int err; unsigned int i; - printk(PRINT_PREF "verifying all eraseblocks\n"); + pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -317,10 +315,10 @@ static int verify_all_eraseblocks(void) if (err) return err; if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); return 0; } @@ -331,7 +329,7 @@ static int is_block_bad(int ebnum) ret = mtd_block_isbad(mtd, addr); if (ret) - printk(PRINT_PREF "block %d is bad\n", ebnum); + pr_info("block %d is bad\n", ebnum); return ret; } @@ -341,18 +339,18 @@ static int scan_for_bad_eraseblocks(void) bbt = kmalloc(ebcnt, GFP_KERNEL); if (!bbt) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } - printk(PRINT_PREF "scanning for bad eraseblocks\n"); + pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; if (bbt[i]) bad += 1; cond_resched(); } - printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); return 0; } @@ -368,22 +366,22 @@ static int __init mtd_oobtest_init(void) printk(KERN_INFO "=================================================\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); - printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } - printk(PRINT_PREF "MTD device: %d\n", dev); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); return err; } if (mtd->type != MTD_NANDFLASH) { - printk(PRINT_PREF "this test requires NAND flash\n"); + pr_info("this test requires NAND flash\n"); goto out; } @@ -392,7 +390,7 @@ static int __init mtd_oobtest_init(void) ebcnt = tmp; pgcnt = mtd->erasesize / mtd->writesize; - printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + pr_info("MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " "eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, @@ -401,12 +399,12 @@ static int __init mtd_oobtest_init(void) err = -ENOMEM; readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!readbuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!writebuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } @@ -420,7 +418,7 @@ static int __init mtd_oobtest_init(void) vary_offset = 0; /* First test: write all OOB, read it back and verify */ - printk(PRINT_PREF "test 1 of 5\n"); + pr_info("test 1 of 5\n"); err = erase_whole_device(); if (err) @@ -440,7 +438,7 @@ static int __init mtd_oobtest_init(void) * Second test: write all OOB, a block at a time, read it back and * verify. */ - printk(PRINT_PREF "test 2 of 5\n"); + pr_info("test 2 of 5\n"); err = erase_whole_device(); if (err) @@ -453,7 +451,7 @@ static int __init mtd_oobtest_init(void) /* Check all eraseblocks */ simple_srand(3); - printk(PRINT_PREF "verifying all eraseblocks\n"); + pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -461,16 +459,16 @@ static int __init mtd_oobtest_init(void) if (err) goto out; if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); /* * Third test: write OOB at varying offsets and lengths, read it back * and verify. */ - printk(PRINT_PREF "test 3 of 5\n"); + pr_info("test 3 of 5\n"); err = erase_whole_device(); if (err) @@ -503,7 +501,7 @@ static int __init mtd_oobtest_init(void) vary_offset = 0; /* Fourth test: try to write off end of device */ - printk(PRINT_PREF "test 4 of 5\n"); + pr_info("test 4 of 5\n"); err = erase_whole_device(); if (err) @@ -522,14 +520,14 @@ static int __init mtd_oobtest_init(void) ops.ooboffs = mtd->ecclayout->oobavail; ops.datbuf = NULL; ops.oobbuf = writebuf; - printk(PRINT_PREF "attempting to start write past end of OOB\n"); - printk(PRINT_PREF "an error is expected...\n"); + pr_info("attempting to start write past end of OOB\n"); + pr_info("an error is expected...\n"); err = mtd_write_oob(mtd, addr0, &ops); if (err) { - printk(PRINT_PREF "error occurred as expected\n"); + pr_info("error occurred as expected\n"); err = 0; } else { - printk(PRINT_PREF "error: can write past end of OOB\n"); + pr_err("error: can write past end of OOB\n"); errcnt += 1; } @@ -542,19 +540,19 @@ static int __init mtd_oobtest_init(void) ops.ooboffs = mtd->ecclayout->oobavail; ops.datbuf = NULL; ops.oobbuf = readbuf; - printk(PRINT_PREF "attempting to start read past end of OOB\n"); - printk(PRINT_PREF "an error is expected...\n"); + pr_info("attempting to start read past end of OOB\n"); + pr_info("an error is expected...\n"); err = mtd_read_oob(mtd, addr0, &ops); if (err) { - printk(PRINT_PREF "error occurred as expected\n"); + pr_info("error occurred as expected\n"); err = 0; } else { - printk(PRINT_PREF "error: can read past end of OOB\n"); + pr_err("error: can read past end of OOB\n"); errcnt += 1; } if (bbt[ebcnt - 1]) - printk(PRINT_PREF "skipping end of device tests because last " + pr_info("skipping end of device tests because last " "block is bad\n"); else { /* Attempt to write off end of device */ @@ -566,14 +564,14 @@ static int __init mtd_oobtest_init(void) ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = writebuf; - printk(PRINT_PREF "attempting to write past end of device\n"); - printk(PRINT_PREF "an error is expected...\n"); + pr_info("attempting to write past end of device\n"); + pr_info("an error is expected...\n"); err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { - printk(PRINT_PREF "error occurred as expected\n"); + pr_info("error occurred as expected\n"); err = 0; } else { - printk(PRINT_PREF "error: wrote past end of device\n"); + pr_err("error: wrote past end of device\n"); errcnt += 1; } @@ -586,14 +584,14 @@ static int __init mtd_oobtest_init(void) ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = readbuf; - printk(PRINT_PREF "attempting to read past end of device\n"); - printk(PRINT_PREF "an error is expected...\n"); + pr_info("attempting to read past end of device\n"); + pr_info("an error is expected...\n"); err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { - printk(PRINT_PREF "error occurred as expected\n"); + pr_info("error occurred as expected\n"); err = 0; } else { - printk(PRINT_PREF "error: read past end of device\n"); + pr_err("error: read past end of device\n"); errcnt += 1; } @@ -610,14 +608,14 @@ static int __init mtd_oobtest_init(void) ops.ooboffs = 1; ops.datbuf = NULL; ops.oobbuf = writebuf; - printk(PRINT_PREF "attempting to write past end of device\n"); - printk(PRINT_PREF "an error is expected...\n"); + pr_info("attempting to write past end of device\n"); + pr_info("an error is expected...\n"); err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { - printk(PRINT_PREF "error occurred as expected\n"); + pr_info("error occurred as expected\n"); err = 0; } else { - printk(PRINT_PREF "error: wrote past end of device\n"); + pr_err("error: wrote past end of device\n"); errcnt += 1; } @@ -630,20 +628,20 @@ static int __init mtd_oobtest_init(void) ops.ooboffs = 1; ops.datbuf = NULL; ops.oobbuf = readbuf; - printk(PRINT_PREF "attempting to read past end of device\n"); - printk(PRINT_PREF "an error is expected...\n"); + pr_info("attempting to read past end of device\n"); + pr_info("an error is expected...\n"); err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); if (err) { - printk(PRINT_PREF "error occurred as expected\n"); + pr_info("error occurred as expected\n"); err = 0; } else { - printk(PRINT_PREF "error: read past end of device\n"); + pr_err("error: read past end of device\n"); errcnt += 1; } } /* Fifth test: write / read across block boundaries */ - printk(PRINT_PREF "test 5 of 5\n"); + pr_info("test 5 of 5\n"); /* Erase all eraseblocks */ err = erase_whole_device(); @@ -652,7 +650,7 @@ static int __init mtd_oobtest_init(void) /* Write all eraseblocks */ simple_srand(11); - printk(PRINT_PREF "writing OOBs of whole device\n"); + pr_info("writing OOBs of whole device\n"); for (i = 0; i < ebcnt - 1; ++i) { int cnt = 2; int pg; @@ -674,17 +672,16 @@ static int __init mtd_oobtest_init(void) if (err) goto out; if (i % 256 == 0) - printk(PRINT_PREF "written up to eraseblock " - "%u\n", i); + pr_info("written up to eraseblock %u\n", i); cond_resched(); addr += mtd->writesize; } } - printk(PRINT_PREF "written %u eraseblocks\n", i); + pr_info("written %u eraseblocks\n", i); /* Check all eraseblocks */ simple_srand(11); - printk(PRINT_PREF "verifying all eraseblocks\n"); + pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt - 1; ++i) { if (bbt[i] || bbt[i + 1]) continue; @@ -702,28 +699,28 @@ static int __init mtd_oobtest_init(void) if (err) goto out; if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; if (errcnt > 1000) { - printk(PRINT_PREF "error: too many errors\n"); + pr_err("error: too many errors\n"); goto out; } } if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); - printk(PRINT_PREF "finished with %d errors\n", errcnt); + pr_info("finished with %d errors\n", errcnt); out: kfree(bbt); kfree(writebuf); kfree(readbuf); put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred\n", err); + pr_info("error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; } diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 252ddb092fb2..f93a76f88113 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -19,6 +19,8 @@ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <asm/div64.h> #include <linux/init.h> #include <linux/module.h> @@ -28,8 +30,6 @@ #include <linux/slab.h> #include <linux/sched.h> -#define PRINT_PREF KERN_INFO "mtd_pagetest: " - static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -79,12 +79,12 @@ static int erase_eraseblock(int ebnum) err = mtd_erase(mtd, &ei); if (err) { - printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + pr_err("error %d while erasing EB %d\n", err, ebnum); return err; } if (ei.state == MTD_ERASE_FAILED) { - printk(PRINT_PREF "some erase error occurred at EB %d\n", + pr_err("some erase error occurred at EB %d\n", ebnum); return -EIO; } @@ -102,7 +102,7 @@ static int write_eraseblock(int ebnum) cond_resched(); err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf); if (err || written != mtd->erasesize) - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", (long long)addr); return err; @@ -131,7 +131,7 @@ static int verify_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr0); return err; } @@ -139,7 +139,7 @@ static int verify_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)(addrn - bufsize)); return err; } @@ -148,12 +148,12 @@ static int verify_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); break; } if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } @@ -166,7 +166,7 @@ static int verify_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr0); return err; } @@ -174,7 +174,7 @@ static int verify_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)(addrn - bufsize)); return err; } @@ -183,14 +183,14 @@ static int verify_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); return err; } memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); set_random_data(boundary + pgsize, pgsize); if (memcmp(twopages, boundary, bufsize)) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } @@ -206,10 +206,10 @@ static int crosstest(void) loff_t addr, addr0, addrn; unsigned char *pp1, *pp2, *pp3, *pp4; - printk(PRINT_PREF "crosstest\n"); + pr_info("crosstest\n"); pp1 = kmalloc(pgsize * 4, GFP_KERNEL); if (!pp1) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } pp2 = pp1 + pgsize; @@ -231,7 +231,7 @@ static int crosstest(void) if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; @@ -243,7 +243,7 @@ static int crosstest(void) if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; @@ -251,12 +251,12 @@ static int crosstest(void) /* Read first page to pp2 */ addr = addr0; - printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); + pr_info("reading page at %#llx\n", (long long)addr); err = mtd_read(mtd, addr, pgsize, &read, pp2); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; @@ -264,12 +264,12 @@ static int crosstest(void) /* Read last page to pp3 */ addr = addrn - pgsize; - printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); + pr_info("reading page at %#llx\n", (long long)addr); err = mtd_read(mtd, addr, pgsize, &read, pp3); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; @@ -277,25 +277,25 @@ static int crosstest(void) /* Read first page again to pp4 */ addr = addr0; - printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); + pr_info("reading page at %#llx\n", (long long)addr); err = mtd_read(mtd, addr, pgsize, &read, pp4); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; } /* pp2 and pp4 should be the same */ - printk(PRINT_PREF "verifying pages read at %#llx match\n", + pr_info("verifying pages read at %#llx match\n", (long long)addr0); if (memcmp(pp2, pp4, pgsize)) { - printk(PRINT_PREF "verify failed!\n"); + pr_err("verify failed!\n"); errcnt += 1; } else if (!err) - printk(PRINT_PREF "crosstest ok\n"); + pr_info("crosstest ok\n"); kfree(pp1); return err; } @@ -307,7 +307,7 @@ static int erasecrosstest(void) loff_t addr0; char *readbuf = twopages; - printk(PRINT_PREF "erasecrosstest\n"); + pr_info("erasecrosstest\n"); ebnum = 0; addr0 = 0; @@ -320,79 +320,79 @@ static int erasecrosstest(void) while (ebnum2 && bbt[ebnum2]) ebnum2 -= 1; - printk(PRINT_PREF "erasing block %d\n", ebnum); + pr_info("erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; - printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); + pr_info("writing 1st page of block %d\n", ebnum); set_random_data(writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_info("error: write failed at %#llx\n", (long long)addr0); return err ? err : -1; } - printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); + pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); err = mtd_read(mtd, addr0, pgsize, &read, readbuf); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr0); return err ? err : -1; } - printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); + pr_info("verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { - printk(PRINT_PREF "verify failed!\n"); + pr_err("verify failed!\n"); errcnt += 1; return -1; } - printk(PRINT_PREF "erasing block %d\n", ebnum); + pr_info("erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; - printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); + pr_info("writing 1st page of block %d\n", ebnum); set_random_data(writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", (long long)addr0); return err ? err : -1; } - printk(PRINT_PREF "erasing block %d\n", ebnum2); + pr_info("erasing block %d\n", ebnum2); err = erase_eraseblock(ebnum2); if (err) return err; - printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); + pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); err = mtd_read(mtd, addr0, pgsize, &read, readbuf); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr0); return err ? err : -1; } - printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); + pr_info("verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { - printk(PRINT_PREF "verify failed!\n"); + pr_err("verify failed!\n"); errcnt += 1; return -1; } if (!err) - printk(PRINT_PREF "erasecrosstest ok\n"); + pr_info("erasecrosstest ok\n"); return err; } @@ -402,7 +402,7 @@ static int erasetest(void) int err = 0, i, ebnum, ok = 1; loff_t addr0; - printk(PRINT_PREF "erasetest\n"); + pr_info("erasetest\n"); ebnum = 0; addr0 = 0; @@ -411,40 +411,40 @@ static int erasetest(void) ebnum += 1; } - printk(PRINT_PREF "erasing block %d\n", ebnum); + pr_info("erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; - printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); + pr_info("writing 1st page of block %d\n", ebnum); set_random_data(writebuf, pgsize); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", (long long)addr0); return err ? err : -1; } - printk(PRINT_PREF "erasing block %d\n", ebnum); + pr_info("erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; - printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); + pr_info("reading 1st page of block %d\n", ebnum); err = mtd_read(mtd, addr0, pgsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr0); return err ? err : -1; } - printk(PRINT_PREF "verifying 1st page of block %d is all 0xff\n", + pr_info("verifying 1st page of block %d is all 0xff\n", ebnum); for (i = 0; i < pgsize; ++i) if (twopages[i] != 0xff) { - printk(PRINT_PREF "verifying all 0xff failed at %d\n", + pr_err("verifying all 0xff failed at %d\n", i); errcnt += 1; ok = 0; @@ -452,7 +452,7 @@ static int erasetest(void) } if (ok && !err) - printk(PRINT_PREF "erasetest ok\n"); + pr_info("erasetest ok\n"); return err; } @@ -464,7 +464,7 @@ static int is_block_bad(int ebnum) ret = mtd_block_isbad(mtd, addr); if (ret) - printk(PRINT_PREF "block %d is bad\n", ebnum); + pr_info("block %d is bad\n", ebnum); return ret; } @@ -474,18 +474,18 @@ static int scan_for_bad_eraseblocks(void) bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } - printk(PRINT_PREF "scanning for bad eraseblocks\n"); + pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; if (bbt[i]) bad += 1; cond_resched(); } - printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); return 0; } @@ -499,22 +499,22 @@ static int __init mtd_pagetest_init(void) printk(KERN_INFO "=================================================\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); - printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } - printk(PRINT_PREF "MTD device: %d\n", dev); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); return err; } if (mtd->type != MTD_NANDFLASH) { - printk(PRINT_PREF "this test requires NAND flash\n"); + pr_info("this test requires NAND flash\n"); goto out; } @@ -524,7 +524,7 @@ static int __init mtd_pagetest_init(void) pgcnt = mtd->erasesize / mtd->writesize; pgsize = mtd->writesize; - printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + pr_info("MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " "eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, @@ -534,17 +534,17 @@ static int __init mtd_pagetest_init(void) bufsize = pgsize * 2; writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!writebuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } twopages = kmalloc(bufsize, GFP_KERNEL); if (!twopages) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } boundary = kmalloc(bufsize, GFP_KERNEL); if (!boundary) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } @@ -553,7 +553,7 @@ static int __init mtd_pagetest_init(void) goto out; /* Erase all eraseblocks */ - printk(PRINT_PREF "erasing whole device\n"); + pr_info("erasing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -562,11 +562,11 @@ static int __init mtd_pagetest_init(void) goto out; cond_resched(); } - printk(PRINT_PREF "erased %u eraseblocks\n", i); + pr_info("erased %u eraseblocks\n", i); /* Write all eraseblocks */ simple_srand(1); - printk(PRINT_PREF "writing whole device\n"); + pr_info("writing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -574,14 +574,14 @@ static int __init mtd_pagetest_init(void) if (err) goto out; if (i % 256 == 0) - printk(PRINT_PREF "written up to eraseblock %u\n", i); + pr_info("written up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "written %u eraseblocks\n", i); + pr_info("written %u eraseblocks\n", i); /* Check all eraseblocks */ simple_srand(1); - printk(PRINT_PREF "verifying all eraseblocks\n"); + pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -589,10 +589,10 @@ static int __init mtd_pagetest_init(void) if (err) goto out; if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); err = crosstest(); if (err) @@ -606,7 +606,7 @@ static int __init mtd_pagetest_init(void) if (err) goto out; - printk(PRINT_PREF "finished with %d errors\n", errcnt); + pr_info("finished with %d errors\n", errcnt); out: kfree(bbt); @@ -615,7 +615,7 @@ out: kfree(writebuf); put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred\n", err); + pr_info("error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; } diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 121aba189cec..266de04b6d29 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -19,6 +19,8 @@ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -27,8 +29,6 @@ #include <linux/slab.h> #include <linux/sched.h> -#define PRINT_PREF KERN_INFO "mtd_readtest: " - static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -51,12 +51,12 @@ static int read_eraseblock_by_page(int ebnum) void *oobbuf = iobuf1; for (i = 0; i < pgcnt; i++) { - memset(buf, 0 , pgcnt); + memset(buf, 0 , pgsize); ret = mtd_read(mtd, addr, pgsize, &read, buf); if (ret == -EUCLEAN) ret = 0; if (ret || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); if (!err) err = ret; @@ -77,7 +77,7 @@ static int read_eraseblock_by_page(int ebnum) ret = mtd_read_oob(mtd, addr, &ops); if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != mtd->oobsize) { - printk(PRINT_PREF "error: read oob failed at " + pr_err("error: read oob failed at " "%#llx\n", (long long)addr); if (!err) err = ret; @@ -99,7 +99,7 @@ static void dump_eraseblock(int ebnum) char line[128]; int pg, oob; - printk(PRINT_PREF "dumping eraseblock %d\n", ebnum); + pr_info("dumping eraseblock %d\n", ebnum); n = mtd->erasesize; for (i = 0; i < n;) { char *p = line; @@ -112,7 +112,7 @@ static void dump_eraseblock(int ebnum) } if (!mtd->oobsize) return; - printk(PRINT_PREF "dumping oob from eraseblock %d\n", ebnum); + pr_info("dumping oob from eraseblock %d\n", ebnum); n = mtd->oobsize; for (pg = 0, i = 0; pg < pgcnt; pg++) for (oob = 0; oob < n;) { @@ -134,7 +134,7 @@ static int is_block_bad(int ebnum) ret = mtd_block_isbad(mtd, addr); if (ret) - printk(PRINT_PREF "block %d is bad\n", ebnum); + pr_info("block %d is bad\n", ebnum); return ret; } @@ -144,21 +144,21 @@ static int scan_for_bad_eraseblocks(void) bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } if (!mtd_can_have_bb(mtd)) return 0; - printk(PRINT_PREF "scanning for bad eraseblocks\n"); + pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; if (bbt[i]) bad += 1; cond_resched(); } - printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); return 0; } @@ -171,21 +171,21 @@ static int __init mtd_readtest_init(void) printk(KERN_INFO "=================================================\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); return -EINVAL; } - printk(PRINT_PREF "MTD device: %d\n", dev); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: Cannot get MTD device\n"); + pr_err("error: Cannot get MTD device\n"); return err; } if (mtd->writesize == 1) { - printk(PRINT_PREF "not NAND flash, assume page size is 512 " + pr_info("not NAND flash, assume page size is 512 " "bytes.\n"); pgsize = 512; } else @@ -196,7 +196,7 @@ static int __init mtd_readtest_init(void) ebcnt = tmp; pgcnt = mtd->erasesize / pgsize; - printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + pr_info("MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " "eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, @@ -205,12 +205,12 @@ static int __init mtd_readtest_init(void) err = -ENOMEM; iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!iobuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); if (!iobuf1) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } @@ -219,7 +219,7 @@ static int __init mtd_readtest_init(void) goto out; /* Read all eraseblocks 1 page at a time */ - printk(PRINT_PREF "testing page read\n"); + pr_info("testing page read\n"); for (i = 0; i < ebcnt; ++i) { int ret; @@ -235,9 +235,9 @@ static int __init mtd_readtest_init(void) } if (err) - printk(PRINT_PREF "finished with errors\n"); + pr_info("finished with errors\n"); else - printk(PRINT_PREF "finished\n"); + pr_info("finished\n"); out: @@ -246,7 +246,7 @@ out: kfree(bbt); put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred\n", err); + pr_info("error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; } diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 42b0f7456fc4..596cbea8df4c 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -19,6 +19,8 @@ * Author: Adrian Hunter <adrian.hunter@nokia.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -28,8 +30,6 @@ #include <linux/sched.h> #include <linux/random.h> -#define PRINT_PREF KERN_INFO "mtd_speedtest: " - static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -70,12 +70,12 @@ static int erase_eraseblock(int ebnum) err = mtd_erase(mtd, &ei); if (err) { - printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + pr_err("error %d while erasing EB %d\n", err, ebnum); return err; } if (ei.state == MTD_ERASE_FAILED) { - printk(PRINT_PREF "some erase error occurred at EB %d\n", + pr_err("some erase error occurred at EB %d\n", ebnum); return -EIO; } @@ -96,13 +96,13 @@ static int multiblock_erase(int ebnum, int blocks) err = mtd_erase(mtd, &ei); if (err) { - printk(PRINT_PREF "error %d while erasing EB %d, blocks %d\n", + pr_err("error %d while erasing EB %d, blocks %d\n", err, ebnum, blocks); return err; } if (ei.state == MTD_ERASE_FAILED) { - printk(PRINT_PREF "some erase error occurred at EB %d," + pr_err("some erase error occurred at EB %d," "blocks %d\n", ebnum, blocks); return -EIO; } @@ -134,7 +134,7 @@ static int write_eraseblock(int ebnum) err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf); if (err || written != mtd->erasesize) { - printk(PRINT_PREF "error: write failed at %#llx\n", addr); + pr_err("error: write failed at %#llx\n", addr); if (!err) err = -EINVAL; } @@ -152,7 +152,7 @@ static int write_eraseblock_by_page(int ebnum) for (i = 0; i < pgcnt; i++) { err = mtd_write(mtd, addr, pgsize, &written, buf); if (err || written != pgsize) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", addr); if (!err) err = -EINVAL; @@ -175,7 +175,7 @@ static int write_eraseblock_by_2pages(int ebnum) for (i = 0; i < n; i++) { err = mtd_write(mtd, addr, sz, &written, buf); if (err || written != sz) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", addr); if (!err) err = -EINVAL; @@ -187,7 +187,7 @@ static int write_eraseblock_by_2pages(int ebnum) if (pgcnt % 2) { err = mtd_write(mtd, addr, pgsize, &written, buf); if (err || written != pgsize) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", addr); if (!err) err = -EINVAL; @@ -208,7 +208,7 @@ static int read_eraseblock(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != mtd->erasesize) { - printk(PRINT_PREF "error: read failed at %#llx\n", addr); + pr_err("error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; } @@ -229,7 +229,7 @@ static int read_eraseblock_by_page(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; @@ -255,7 +255,7 @@ static int read_eraseblock_by_2pages(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != sz) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; @@ -270,7 +270,7 @@ static int read_eraseblock_by_2pages(int ebnum) if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; @@ -287,7 +287,7 @@ static int is_block_bad(int ebnum) ret = mtd_block_isbad(mtd, addr); if (ret) - printk(PRINT_PREF "block %d is bad\n", ebnum); + pr_info("block %d is bad\n", ebnum); return ret; } @@ -321,21 +321,21 @@ static int scan_for_bad_eraseblocks(void) bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } if (!mtd_can_have_bb(mtd)) goto out; - printk(PRINT_PREF "scanning for bad eraseblocks\n"); + pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; if (bbt[i]) bad += 1; cond_resched(); } - printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); out: goodebcnt = ebcnt - bad; return 0; @@ -351,25 +351,25 @@ static int __init mtd_speedtest_init(void) printk(KERN_INFO "=================================================\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); - printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } if (count) - printk(PRINT_PREF "MTD device: %d count: %d\n", dev, count); + pr_info("MTD device: %d count: %d\n", dev, count); else - printk(PRINT_PREF "MTD device: %d\n", dev); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); return err; } if (mtd->writesize == 1) { - printk(PRINT_PREF "not NAND flash, assume page size is 512 " + pr_info("not NAND flash, assume page size is 512 " "bytes.\n"); pgsize = 512; } else @@ -380,7 +380,7 @@ static int __init mtd_speedtest_init(void) ebcnt = tmp; pgcnt = mtd->erasesize / pgsize; - printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + pr_info("MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " "eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, @@ -392,7 +392,7 @@ static int __init mtd_speedtest_init(void) err = -ENOMEM; iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!iobuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } @@ -407,7 +407,7 @@ static int __init mtd_speedtest_init(void) goto out; /* Write all eraseblocks, 1 eraseblock at a time */ - printk(PRINT_PREF "testing eraseblock write speed\n"); + pr_info("testing eraseblock write speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -419,10 +419,10 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed); + pr_info("eraseblock write speed is %ld KiB/s\n", speed); /* Read all eraseblocks, 1 eraseblock at a time */ - printk(PRINT_PREF "testing eraseblock read speed\n"); + pr_info("testing eraseblock read speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -434,14 +434,14 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "eraseblock read speed is %ld KiB/s\n", speed); + pr_info("eraseblock read speed is %ld KiB/s\n", speed); err = erase_whole_device(); if (err) goto out; /* Write all eraseblocks, 1 page at a time */ - printk(PRINT_PREF "testing page write speed\n"); + pr_info("testing page write speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -453,10 +453,10 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "page write speed is %ld KiB/s\n", speed); + pr_info("page write speed is %ld KiB/s\n", speed); /* Read all eraseblocks, 1 page at a time */ - printk(PRINT_PREF "testing page read speed\n"); + pr_info("testing page read speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -468,14 +468,14 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "page read speed is %ld KiB/s\n", speed); + pr_info("page read speed is %ld KiB/s\n", speed); err = erase_whole_device(); if (err) goto out; /* Write all eraseblocks, 2 pages at a time */ - printk(PRINT_PREF "testing 2 page write speed\n"); + pr_info("testing 2 page write speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -487,10 +487,10 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "2 page write speed is %ld KiB/s\n", speed); + pr_info("2 page write speed is %ld KiB/s\n", speed); /* Read all eraseblocks, 2 pages at a time */ - printk(PRINT_PREF "testing 2 page read speed\n"); + pr_info("testing 2 page read speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -502,10 +502,10 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "2 page read speed is %ld KiB/s\n", speed); + pr_info("2 page read speed is %ld KiB/s\n", speed); /* Erase all eraseblocks */ - printk(PRINT_PREF "Testing erase speed\n"); + pr_info("Testing erase speed\n"); start_timing(); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -517,12 +517,12 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed); + pr_info("erase speed is %ld KiB/s\n", speed); /* Multi-block erase all eraseblocks */ for (k = 1; k < 7; k++) { blocks = 1 << k; - printk(PRINT_PREF "Testing %dx multi-block erase speed\n", + pr_info("Testing %dx multi-block erase speed\n", blocks); start_timing(); for (i = 0; i < ebcnt; ) { @@ -541,16 +541,16 @@ static int __init mtd_speedtest_init(void) } stop_timing(); speed = calc_speed(); - printk(PRINT_PREF "%dx multi-block erase speed is %ld KiB/s\n", + pr_info("%dx multi-block erase speed is %ld KiB/s\n", blocks, speed); } - printk(PRINT_PREF "finished\n"); + pr_info("finished\n"); out: kfree(iobuf); kfree(bbt); put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred\n", err); + pr_info("error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; } diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index cb268cebf01a..3729f679ae5d 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -19,6 +19,8 @@ * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -29,8 +31,6 @@ #include <linux/vmalloc.h> #include <linux/random.h> -#define PRINT_PREF KERN_INFO "mtd_stresstest: " - static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -94,12 +94,12 @@ static int erase_eraseblock(int ebnum) err = mtd_erase(mtd, &ei); if (unlikely(err)) { - printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + pr_err("error %d while erasing EB %d\n", err, ebnum); return err; } if (unlikely(ei.state == MTD_ERASE_FAILED)) { - printk(PRINT_PREF "some erase error occurred at EB %d\n", + pr_err("some erase error occurred at EB %d\n", ebnum); return -EIO; } @@ -114,7 +114,7 @@ static int is_block_bad(int ebnum) ret = mtd_block_isbad(mtd, addr); if (ret) - printk(PRINT_PREF "block %d is bad\n", ebnum); + pr_info("block %d is bad\n", ebnum); return ret; } @@ -137,7 +137,7 @@ static int do_read(void) if (mtd_is_bitflip(err)) err = 0; if (unlikely(err || read != len)) { - printk(PRINT_PREF "error: read failed at 0x%llx\n", + pr_err("error: read failed at 0x%llx\n", (long long)addr); if (!err) err = -EINVAL; @@ -174,7 +174,7 @@ static int do_write(void) addr = eb * mtd->erasesize + offs; err = mtd_write(mtd, addr, len, &written, writebuf); if (unlikely(err || written != len)) { - printk(PRINT_PREF "error: write failed at 0x%llx\n", + pr_err("error: write failed at 0x%llx\n", (long long)addr); if (!err) err = -EINVAL; @@ -203,21 +203,21 @@ static int scan_for_bad_eraseblocks(void) bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } if (!mtd_can_have_bb(mtd)) return 0; - printk(PRINT_PREF "scanning for bad eraseblocks\n"); + pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; if (bbt[i]) bad += 1; cond_resched(); } - printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); return 0; } @@ -231,22 +231,22 @@ static int __init mtd_stresstest_init(void) printk(KERN_INFO "=================================================\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); - printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } - printk(PRINT_PREF "MTD device: %d\n", dev); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); return err; } if (mtd->writesize == 1) { - printk(PRINT_PREF "not NAND flash, assume page size is 512 " + pr_info("not NAND flash, assume page size is 512 " "bytes.\n"); pgsize = 512; } else @@ -257,14 +257,14 @@ static int __init mtd_stresstest_init(void) ebcnt = tmp; pgcnt = mtd->erasesize / pgsize; - printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + pr_info("MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " "eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, pgsize, ebcnt, pgcnt, mtd->oobsize); if (ebcnt < 2) { - printk(PRINT_PREF "error: need at least 2 eraseblocks\n"); + pr_err("error: need at least 2 eraseblocks\n"); err = -ENOSPC; goto out_put_mtd; } @@ -277,7 +277,7 @@ static int __init mtd_stresstest_init(void) writebuf = vmalloc(bufsize); offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); if (!readbuf || !writebuf || !offsets) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out; } for (i = 0; i < ebcnt; i++) @@ -290,16 +290,16 @@ static int __init mtd_stresstest_init(void) goto out; /* Do operations */ - printk(PRINT_PREF "doing operations\n"); + pr_info("doing operations\n"); for (op = 0; op < count; op++) { if ((op & 1023) == 0) - printk(PRINT_PREF "%d operations done\n", op); + pr_info("%d operations done\n", op); err = do_operation(); if (err) goto out; cond_resched(); } - printk(PRINT_PREF "finished, %d operations done\n", op); + pr_info("finished, %d operations done\n", op); out: kfree(offsets); @@ -309,7 +309,7 @@ out: out_put_mtd: put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred\n", err); + pr_info("error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; } diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c index 9667bf535282..c880c2229c59 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -19,6 +19,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -27,8 +29,6 @@ #include <linux/slab.h> #include <linux/sched.h> -#define PRINT_PREF KERN_INFO "mtd_subpagetest: " - static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -82,12 +82,12 @@ static int erase_eraseblock(int ebnum) err = mtd_erase(mtd, &ei); if (err) { - printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + pr_err("error %d while erasing EB %d\n", err, ebnum); return err; } if (ei.state == MTD_ERASE_FAILED) { - printk(PRINT_PREF "some erase error occurred at EB %d\n", + pr_err("some erase error occurred at EB %d\n", ebnum); return -EIO; } @@ -100,7 +100,7 @@ static int erase_whole_device(void) int err; unsigned int i; - printk(PRINT_PREF "erasing whole device\n"); + pr_info("erasing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -109,7 +109,7 @@ static int erase_whole_device(void) return err; cond_resched(); } - printk(PRINT_PREF "erased %u eraseblocks\n", i); + pr_info("erased %u eraseblocks\n", i); return 0; } @@ -122,11 +122,11 @@ static int write_eraseblock(int ebnum) set_random_data(writebuf, subpgsize); err = mtd_write(mtd, addr, subpgsize, &written, writebuf); if (unlikely(err || written != subpgsize)) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", (long long)addr); if (written != subpgsize) { - printk(PRINT_PREF " write size: %#x\n", subpgsize); - printk(PRINT_PREF " written: %#zx\n", written); + pr_err(" write size: %#x\n", subpgsize); + pr_err(" written: %#zx\n", written); } return err ? err : -1; } @@ -136,11 +136,11 @@ static int write_eraseblock(int ebnum) set_random_data(writebuf, subpgsize); err = mtd_write(mtd, addr, subpgsize, &written, writebuf); if (unlikely(err || written != subpgsize)) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", (long long)addr); if (written != subpgsize) { - printk(PRINT_PREF " write size: %#x\n", subpgsize); - printk(PRINT_PREF " written: %#zx\n", written); + pr_err(" write size: %#x\n", subpgsize); + pr_err(" written: %#zx\n", written); } return err ? err : -1; } @@ -160,12 +160,12 @@ static int write_eraseblock2(int ebnum) set_random_data(writebuf, subpgsize * k); err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf); if (unlikely(err || written != subpgsize * k)) { - printk(PRINT_PREF "error: write failed at %#llx\n", + pr_err("error: write failed at %#llx\n", (long long)addr); if (written != subpgsize) { - printk(PRINT_PREF " write size: %#x\n", + pr_err(" write size: %#x\n", subpgsize * k); - printk(PRINT_PREF " written: %#08zx\n", + pr_err(" written: %#08zx\n", written); } return err ? err : -1; @@ -198,23 +198,23 @@ static int verify_eraseblock(int ebnum) err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { if (mtd_is_bitflip(err) && read == subpgsize) { - printk(PRINT_PREF "ECC correction at %#llx\n", + pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); - printk(PRINT_PREF "------------- written----------------\n"); + pr_info("------------- written----------------\n"); print_subpage(writebuf); - printk(PRINT_PREF "------------- read ------------------\n"); + pr_info("------------- read ------------------\n"); print_subpage(readbuf); - printk(PRINT_PREF "-------------------------------------\n"); + pr_info("-------------------------------------\n"); errcnt += 1; } @@ -225,23 +225,23 @@ static int verify_eraseblock(int ebnum) err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { if (mtd_is_bitflip(err) && read == subpgsize) { - printk(PRINT_PREF "ECC correction at %#llx\n", + pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { - printk(PRINT_PREF "error: read failed at %#llx\n", + pr_err("error: read failed at %#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_info("error: verify failed at %#llx\n", (long long)addr); - printk(PRINT_PREF "------------- written----------------\n"); + pr_info("------------- written----------------\n"); print_subpage(writebuf); - printk(PRINT_PREF "------------- read ------------------\n"); + pr_info("------------- read ------------------\n"); print_subpage(readbuf); - printk(PRINT_PREF "-------------------------------------\n"); + pr_info("-------------------------------------\n"); errcnt += 1; } @@ -262,17 +262,17 @@ static int verify_eraseblock2(int ebnum) err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf); if (unlikely(err || read != subpgsize * k)) { if (mtd_is_bitflip(err) && read == subpgsize * k) { - printk(PRINT_PREF "ECC correction at %#llx\n", + pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { - printk(PRINT_PREF "error: read failed at " + pr_err("error: read failed at " "%#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { - printk(PRINT_PREF "error: verify failed at %#llx\n", + pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } @@ -295,17 +295,17 @@ static int verify_eraseblock_ff(int ebnum) err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { if (mtd_is_bitflip(err) && read == subpgsize) { - printk(PRINT_PREF "ECC correction at %#llx\n", + pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { - printk(PRINT_PREF "error: read failed at " + pr_err("error: read failed at " "%#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { - printk(PRINT_PREF "error: verify 0xff failed at " + pr_err("error: verify 0xff failed at " "%#llx\n", (long long)addr); errcnt += 1; } @@ -320,7 +320,7 @@ static int verify_all_eraseblocks_ff(void) int err; unsigned int i; - printk(PRINT_PREF "verifying all eraseblocks for 0xff\n"); + pr_info("verifying all eraseblocks for 0xff\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -328,10 +328,10 @@ static int verify_all_eraseblocks_ff(void) if (err) return err; if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); return 0; } @@ -342,7 +342,7 @@ static int is_block_bad(int ebnum) ret = mtd_block_isbad(mtd, addr); if (ret) - printk(PRINT_PREF "block %d is bad\n", ebnum); + pr_info("block %d is bad\n", ebnum); return ret; } @@ -352,18 +352,18 @@ static int scan_for_bad_eraseblocks(void) bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); return -ENOMEM; } - printk(PRINT_PREF "scanning for bad eraseblocks\n"); + pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; if (bbt[i]) bad += 1; cond_resched(); } - printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); return 0; } @@ -377,22 +377,22 @@ static int __init mtd_subpagetest_init(void) printk(KERN_INFO "=================================================\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); - printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } - printk(PRINT_PREF "MTD device: %d\n", dev); + pr_info("MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); return err; } if (mtd->type != MTD_NANDFLASH) { - printk(PRINT_PREF "this test requires NAND flash\n"); + pr_info("this test requires NAND flash\n"); goto out; } @@ -402,7 +402,7 @@ static int __init mtd_subpagetest_init(void) ebcnt = tmp; pgcnt = mtd->erasesize / mtd->writesize; - printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + pr_info("MTD device size %llu, eraseblock size %u, " "page size %u, subpage size %u, count of eraseblocks %u, " "pages per eraseblock %u, OOB size %u\n", (unsigned long long)mtd->size, mtd->erasesize, @@ -412,12 +412,12 @@ static int __init mtd_subpagetest_init(void) bufsize = subpgsize * 32; writebuf = kmalloc(bufsize, GFP_KERNEL); if (!writebuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_info("error: cannot allocate memory\n"); goto out; } readbuf = kmalloc(bufsize, GFP_KERNEL); if (!readbuf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_info("error: cannot allocate memory\n"); goto out; } @@ -429,7 +429,7 @@ static int __init mtd_subpagetest_init(void) if (err) goto out; - printk(PRINT_PREF "writing whole device\n"); + pr_info("writing whole device\n"); simple_srand(1); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) @@ -438,13 +438,13 @@ static int __init mtd_subpagetest_init(void) if (unlikely(err)) goto out; if (i % 256 == 0) - printk(PRINT_PREF "written up to eraseblock %u\n", i); + pr_info("written up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "written %u eraseblocks\n", i); + pr_info("written %u eraseblocks\n", i); simple_srand(1); - printk(PRINT_PREF "verifying all eraseblocks\n"); + pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -452,10 +452,10 @@ static int __init mtd_subpagetest_init(void) if (unlikely(err)) goto out; if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); err = erase_whole_device(); if (err) @@ -467,7 +467,7 @@ static int __init mtd_subpagetest_init(void) /* Write all eraseblocks */ simple_srand(3); - printk(PRINT_PREF "writing whole device\n"); + pr_info("writing whole device\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -475,14 +475,14 @@ static int __init mtd_subpagetest_init(void) if (unlikely(err)) goto out; if (i % 256 == 0) - printk(PRINT_PREF "written up to eraseblock %u\n", i); + pr_info("written up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "written %u eraseblocks\n", i); + pr_info("written %u eraseblocks\n", i); /* Check all eraseblocks */ simple_srand(3); - printk(PRINT_PREF "verifying all eraseblocks\n"); + pr_info("verifying all eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; @@ -490,10 +490,10 @@ static int __init mtd_subpagetest_init(void) if (unlikely(err)) goto out; if (i % 256 == 0) - printk(PRINT_PREF "verified up to eraseblock %u\n", i); + pr_info("verified up to eraseblock %u\n", i); cond_resched(); } - printk(PRINT_PREF "verified %u eraseblocks\n", i); + pr_info("verified %u eraseblocks\n", i); err = erase_whole_device(); if (err) @@ -503,7 +503,7 @@ static int __init mtd_subpagetest_init(void) if (err) goto out; - printk(PRINT_PREF "finished with %d errors\n", errcnt); + pr_info("finished with %d errors\n", errcnt); out: kfree(bbt); @@ -511,7 +511,7 @@ out: kfree(writebuf); put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred\n", err); + pr_info("error %d occurred\n", err); printk(KERN_INFO "=================================================\n"); return err; } diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c index b65861bc7b8e..c4cde1e9eddb 100644 --- a/drivers/mtd/tests/mtd_torturetest.c +++ b/drivers/mtd/tests/mtd_torturetest.c @@ -23,6 +23,8 @@ * damage caused by this program. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -31,7 +33,6 @@ #include <linux/slab.h> #include <linux/sched.h> -#define PRINT_PREF KERN_INFO "mtd_torturetest: " #define RETRIES 3 static int eb = 8; @@ -107,12 +108,12 @@ static inline int erase_eraseblock(int ebnum) err = mtd_erase(mtd, &ei); if (err) { - printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + pr_err("error %d while erasing EB %d\n", err, ebnum); return err; } if (ei.state == MTD_ERASE_FAILED) { - printk(PRINT_PREF "some erase error occurred at EB %d\n", + pr_err("some erase error occurred at EB %d\n", ebnum); return -EIO; } @@ -139,40 +140,40 @@ static inline int check_eraseblock(int ebnum, unsigned char *buf) retry: err = mtd_read(mtd, addr, len, &read, check_buf); if (mtd_is_bitflip(err)) - printk(PRINT_PREF "single bit flip occurred at EB %d " + pr_err("single bit flip occurred at EB %d " "MTD reported that it was fixed.\n", ebnum); else if (err) { - printk(PRINT_PREF "error %d while reading EB %d, " + pr_err("error %d while reading EB %d, " "read %zd\n", err, ebnum, read); return err; } if (read != len) { - printk(PRINT_PREF "failed to read %zd bytes from EB %d, " + pr_err("failed to read %zd bytes from EB %d, " "read only %zd, but no error reported\n", len, ebnum, read); return -EIO; } if (memcmp(buf, check_buf, len)) { - printk(PRINT_PREF "read wrong data from EB %d\n", ebnum); + pr_err("read wrong data from EB %d\n", ebnum); report_corrupt(check_buf, buf); if (retries++ < RETRIES) { /* Try read again */ yield(); - printk(PRINT_PREF "re-try reading data from EB %d\n", + pr_info("re-try reading data from EB %d\n", ebnum); goto retry; } else { - printk(PRINT_PREF "retried %d times, still errors, " + pr_info("retried %d times, still errors, " "give-up\n", RETRIES); return -EINVAL; } } if (retries != 0) - printk(PRINT_PREF "only attempt number %d was OK (!!!)\n", + pr_info("only attempt number %d was OK (!!!)\n", retries); return 0; @@ -191,12 +192,12 @@ static inline int write_pattern(int ebnum, void *buf) } err = mtd_write(mtd, addr, len, &written, buf); if (err) { - printk(PRINT_PREF "error %d while writing EB %d, written %zd" + pr_err("error %d while writing EB %d, written %zd" " bytes\n", err, ebnum, written); return err; } if (written != len) { - printk(PRINT_PREF "written only %zd bytes of %zd, but no error" + pr_info("written only %zd bytes of %zd, but no error" " reported\n", written, len); return -EIO; } @@ -211,64 +212,64 @@ static int __init tort_init(void) printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); - printk(PRINT_PREF "Warning: this program is trying to wear out your " + pr_info("Warning: this program is trying to wear out your " "flash, stop it if this is not wanted.\n"); if (dev < 0) { - printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n"); - printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n"); + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); return -EINVAL; } - printk(PRINT_PREF "MTD device: %d\n", dev); - printk(PRINT_PREF "torture %d eraseblocks (%d-%d) of mtd%d\n", + pr_info("MTD device: %d\n", dev); + pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n", ebcnt, eb, eb + ebcnt - 1, dev); if (pgcnt) - printk(PRINT_PREF "torturing just %d pages per eraseblock\n", + pr_info("torturing just %d pages per eraseblock\n", pgcnt); - printk(PRINT_PREF "write verify %s\n", check ? "enabled" : "disabled"); + pr_info("write verify %s\n", check ? "enabled" : "disabled"); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - printk(PRINT_PREF "error: cannot get MTD device\n"); + pr_err("error: cannot get MTD device\n"); return err; } if (mtd->writesize == 1) { - printk(PRINT_PREF "not NAND flash, assume page size is 512 " + pr_info("not NAND flash, assume page size is 512 " "bytes.\n"); pgsize = 512; } else pgsize = mtd->writesize; if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { - printk(PRINT_PREF "error: invalid pgcnt value %d\n", pgcnt); + pr_err("error: invalid pgcnt value %d\n", pgcnt); goto out_mtd; } err = -ENOMEM; patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); if (!patt_5A5) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out_mtd; } patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); if (!patt_A5A) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out_patt_5A5; } patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); if (!patt_FF) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out_patt_A5A; } check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!check_buf) { - printk(PRINT_PREF "error: cannot allocate memory\n"); + pr_err("error: cannot allocate memory\n"); goto out_patt_FF; } @@ -295,13 +296,13 @@ static int __init tort_init(void) err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); if (err < 0) { - printk(PRINT_PREF "block_isbad() returned %d " + pr_info("block_isbad() returned %d " "for EB %d\n", err, i); goto out; } if (err) { - printk("EB %d is bad. Skip it.\n", i); + pr_err("EB %d is bad. Skip it.\n", i); bad_ebs[i - eb] = 1; } } @@ -329,7 +330,7 @@ static int __init tort_init(void) continue; err = check_eraseblock(i, patt_FF); if (err) { - printk(PRINT_PREF "verify failed" + pr_info("verify failed" " for 0xFF... pattern\n"); goto out; } @@ -362,7 +363,7 @@ static int __init tort_init(void) patt = patt_A5A; err = check_eraseblock(i, patt); if (err) { - printk(PRINT_PREF "verify failed for %s" + pr_info("verify failed for %s" " pattern\n", ((eb + erase_cycles) & 1) ? "0x55AA55..." : "0xAA55AA..."); @@ -380,7 +381,7 @@ static int __init tort_init(void) stop_timing(); ms = (finish.tv_sec - start.tv_sec) * 1000 + (finish.tv_usec - start.tv_usec) / 1000; - printk(PRINT_PREF "%08u erase cycles done, took %lu " + pr_info("%08u erase cycles done, took %lu " "milliseconds (%lu seconds)\n", erase_cycles, ms, ms / 1000); start_timing(); @@ -391,7 +392,7 @@ static int __init tort_init(void) } out: - printk(PRINT_PREF "finished after %u erase cycles\n", + pr_info("finished after %u erase cycles\n", erase_cycles); kfree(check_buf); out_patt_FF: @@ -403,7 +404,7 @@ out_patt_5A5: out_mtd: put_mtd_device(mtd); if (err) - printk(PRINT_PREF "error %d occurred during torturing\n", err); + pr_info("error %d occurred during torturing\n", err); printk(KERN_INFO "=================================================\n"); return err; } @@ -441,9 +442,9 @@ static void report_corrupt(unsigned char *read, unsigned char *written) &bits) >= 0) pages++; - printk(PRINT_PREF "verify fails on %d pages, %d bytes/%d bits\n", + pr_info("verify fails on %d pages, %d bytes/%d bits\n", pages, bytes, bits); - printk(PRINT_PREF "The following is a list of all differences between" + pr_info("The following is a list of all differences between" " what was read from flash and what was expected\n"); for (i = 0; i < check_len; i += pgsize) { @@ -457,7 +458,7 @@ static void report_corrupt(unsigned char *read, unsigned char *written) printk("-------------------------------------------------------" "----------------------------------\n"); - printk(PRINT_PREF "Page %zd has %d bytes/%d bits failing verify," + pr_info("Page %zd has %d bytes/%d bits failing verify," " starting at offset 0x%x\n", (mtd->erasesize - check_len + i) / pgsize, bytes, bits, first); diff --git a/drivers/of/base.c b/drivers/of/base.c index db8d211a0d05..2390ddb22d60 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -629,7 +629,7 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from, read_unlock(&devtree_lock); return np; } -EXPORT_SYMBOL(of_find_matching_node); +EXPORT_SYMBOL(of_find_matching_node_and_match); /** * of_modalias_node - Lookup appropriate modalias for a device node diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index adb3a4b59cb3..6ba047f5ac2c 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -239,44 +239,37 @@ static bool is_full_charged(struct charger_manager *cm) int uV; /* If there is no battery, it cannot be charged */ - if (!is_batt_present(cm)) { - val.intval = 0; - goto out; - } + if (!is_batt_present(cm)) + return false; if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) { + val.intval = 0; + /* Not full if capacity of fuel gauge isn't full */ ret = cm->fuel_gauge->get_property(cm->fuel_gauge, POWER_SUPPLY_PROP_CHARGE_FULL, &val); - if (!ret && val.intval > desc->fullbatt_full_capacity) { - val.intval = 1; - goto out; - } + if (!ret && val.intval > desc->fullbatt_full_capacity) + return true; } /* Full, if it's over the fullbatt voltage */ if (desc->fullbatt_uV > 0) { ret = get_batt_uV(cm, &uV); - if (!ret && uV >= desc->fullbatt_uV) { - val.intval = 1; - goto out; - } + if (!ret && uV >= desc->fullbatt_uV) + return true; } /* Full, if the capacity is more than fullbatt_soc */ if (cm->fuel_gauge && desc->fullbatt_soc > 0) { + val.intval = 0; + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, POWER_SUPPLY_PROP_CAPACITY, &val); - if (!ret && val.intval >= desc->fullbatt_soc) { - val.intval = 1; - goto out; - } + if (!ret && val.intval >= desc->fullbatt_soc) + return true; } - val.intval = 0; - -out: - return val.intval ? true : false; + return false; } /** @@ -489,8 +482,9 @@ static void fullbatt_vchk(struct work_struct *work) return; } - diff = desc->fullbatt_uV; - diff -= batt_uV; + diff = desc->fullbatt_uV - batt_uV; + if (diff < 0) + return; dev_info(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ed81720e7b2b..e513cd998170 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -112,6 +112,17 @@ config PWM_SAMSUNG To compile this driver as a module, choose M here: the module will be called pwm-samsung. +config PWM_SPEAR + tristate "STMicroelectronics SPEAr PWM support" + depends on PLAT_SPEAR + depends on OF + help + Generic PWM framework driver for the PWM controller on ST + SPEAr SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-spear. + config PWM_TEGRA tristate "NVIDIA Tegra PWM support" depends on ARCH_TEGRA @@ -125,6 +136,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the ECAP APWM controller found on AM33XX TI SOC @@ -135,6 +147,7 @@ config PWM_TIECAP config PWM_TIEHRPWM tristate "EHRPWM PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the EHRPWM controller found on AM33XX TI SOC @@ -142,14 +155,32 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. -config PWM_TWL6030 - tristate "TWL6030 PWM support" +config PWM_TIPWMSS + bool + depends on SOC_AM33XX && (PWM_TIEHRPWM || PWM_TIECAP) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + +config PWM_TWL + tristate "TWL4030/6030 PWM support" + depends on TWL4030_CORE + help + Generic PWM framework driver for TWL4030/6030. + + To compile this driver as a module, choose M here: the module + will be called pwm-twl. + +config PWM_TWL_LED + tristate "TWL4030/6030 PWM support for LED drivers" depends on TWL4030_CORE help - Generic PWM framework driver for TWL6030. + Generic PWM framework driver for TWL4030/6030 LED terminals. To compile this driver as a module, choose M here: the module - will be called pwm-twl6030. + will be called pwm-twl-led. config PWM_VT8500 tristate "vt8500 pwm support" diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index acfe4821c58b..62a2963cfe58 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -8,8 +8,11 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o +obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o -obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o +obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o +obj-$(CONFIG_PWM_TWL) += pwm-twl.o +obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f5acdaa52707..903138b18842 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -32,6 +32,9 @@ #define MAX_PWMS 1024 +/* flags in the third cell of the DT PWM specifier */ +#define PWM_SPEC_POLARITY (1 << 0) + static DEFINE_MUTEX(pwm_lookup_lock); static LIST_HEAD(pwm_lookup_list); static DEFINE_MUTEX(pwm_lock); @@ -129,6 +132,32 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) return 0; } +struct pwm_device * +of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 3) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + if (args->args[2] & PWM_SPEC_POLARITY) + pwm_set_polarity(pwm, PWM_POLARITY_INVERSED); + else + pwm_set_polarity(pwm, PWM_POLARITY_NORMAL); + + return pwm; +} +EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); + static struct pwm_device * of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) { diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 8f26e9fcea97..65a86bdeabed 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -235,7 +235,7 @@ static int imx_pwm_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(imx_pwm_dt_ids, &pdev->dev); - struct imx_pwm_data *data; + const struct imx_pwm_data *data; struct imx_chip *imx; struct resource *r; int ret = 0; diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 015a82235620..14106440294f 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -49,9 +49,24 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, c = 0; /* 0 set division by 256 */ period_cycles = c; + /* The duty-cycle value is as follows: + * + * DUTY-CYCLE HIGH LEVEL + * 1 99.9% + * 25 90.0% + * 128 50.0% + * 220 10.0% + * 255 0.1% + * 0 0.0% + * + * In other words, the register value is duty-cycle % 256 with + * duty-cycle in the range 1-256. + */ c = 256 * duty_ns; do_div(c, period_ns); - duty_cycles = c; + if (c > 255) + c = 255; + duty_cycles = 256 - c; writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles), lpc32xx->base + (pwm->hwpwm << 2)); @@ -106,6 +121,7 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) lpc32xx->chip.dev = &pdev->dev; lpc32xx->chip.ops = &lpc32xx_pwm_ops; lpc32xx->chip.npwm = 2; + lpc32xx->chip.base = -1; ret = pwmchip_add(&lpc32xx->chip); if (ret < 0) { @@ -121,8 +137,11 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) static int lpc32xx_pwm_remove(struct platform_device *pdev) { struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < lpc32xx->chip.npwm; i++) + pwm_disable(&lpc32xx->chip.pwms[i]); - clk_disable(lpc32xx->clk); return pwmchip_remove(&lpc32xx->chip); } diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index e9b15d099c03..5207e6cd8648 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -222,6 +222,7 @@ static int s3c_pwm_probe(struct platform_device *pdev) /* calculate base of control bits in TCON */ s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; + s3c->pwm_id = id; s3c->chip.dev = &pdev->dev; s3c->chip.ops = &s3c_pwm_ops; s3c->chip.base = -1; diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c new file mode 100644 index 000000000000..83b21d9d5cf9 --- /dev/null +++ b/drivers/pwm/pwm-spear.c @@ -0,0 +1,276 @@ +/* + * ST Microelectronics SPEAr Pulse Width Modulator driver + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim <shiraz.hashim@st.com> + * + * 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 <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define NUM_PWM 4 + +/* PWM registers and bits definitions */ +#define PWMCR 0x00 /* Control Register */ +#define PWMCR_PWM_ENABLE 0x1 +#define PWMCR_PRESCALE_SHIFT 2 +#define PWMCR_MIN_PRESCALE 0x00 +#define PWMCR_MAX_PRESCALE 0x3FFF + +#define PWMDCR 0x04 /* Duty Cycle Register */ +#define PWMDCR_MIN_DUTY 0x0001 +#define PWMDCR_MAX_DUTY 0xFFFF + +#define PWMPCR 0x08 /* Period Register */ +#define PWMPCR_MIN_PERIOD 0x0001 +#define PWMPCR_MAX_PERIOD 0xFFFF + +/* Following only available on 13xx SoCs */ +#define PWMMCR 0x3C /* Master Control Register */ +#define PWMMCR_PWM_ENABLE 0x1 + +/** + * struct spear_pwm_chip - struct representing pwm chip + * + * @mmio_base: base address of pwm chip + * @clk: pointer to clk structure of pwm chip + * @chip: linux pwm chip representation + * @dev: pointer to device structure of pwm chip + */ +struct spear_pwm_chip { + void __iomem *mmio_base; + struct clk *clk; + struct pwm_chip chip; + struct device *dev; +}; + +static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct spear_pwm_chip, chip); +} + +static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num, + unsigned long offset) +{ + return readl_relaxed(chip->mmio_base + (num << 4) + offset); +} + +static inline void spear_pwm_writel(struct spear_pwm_chip *chip, + unsigned int num, unsigned long offset, + unsigned long val) +{ + writel_relaxed(val, chip->mmio_base + (num << 4) + offset); +} + +static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u64 val, div, clk_rate; + unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; + int ret; + + /* + * Find pv, dc and prescale to suit duty_ns and period_ns. This is done + * according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ + clk_rate = clk_get_rate(pc->clk); + while (1) { + div = 1000000000; + div *= 1 + prescale; + val = clk_rate * period_ns; + pv = div64_u64(val, div); + val = clk_rate * duty_ns; + dc = div64_u64(val, div); + + /* if duty_ns and period_ns are not achievable then return */ + if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) + return -EINVAL; + + /* + * if pv and dc have crossed their upper limit, then increase + * prescale and recalculate pv and dc. + */ + if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { + if (++prescale > PWMCR_MAX_PRESCALE) + return -EINVAL; + continue; + } + break; + } + + /* + * NOTE: the clock to PWM has to be enabled first before writing to the + * registers. + */ + ret = clk_enable(pc->clk); + if (ret) + return ret; + + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, + prescale << PWMCR_PRESCALE_SHIFT); + spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc); + spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv); + clk_disable(pc->clk); + + return 0; +} + +static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + int rc = 0; + u32 val; + + rc = clk_enable(pc->clk); + if (!rc) + return rc; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val |= PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + return 0; +} + +static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u32 val; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val &= ~PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + clk_disable(pc->clk); +} + +static const struct pwm_ops spear_pwm_ops = { + .config = spear_pwm_config, + .enable = spear_pwm_enable, + .disable = spear_pwm_disable, + .owner = THIS_MODULE, +}; + +static int spear_pwm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spear_pwm_chip *pc; + struct resource *r; + int ret; + u32 val; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resources defined\n"); + return -ENODEV; + } + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pc->mmio_base) + return -EADDRNOTAVAIL; + + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return PTR_ERR(pc->clk); + + pc->dev = &pdev->dev; + platform_set_drvdata(pdev, pc); + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &spear_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = NUM_PWM; + + ret = clk_prepare(pc->clk); + if (!ret) + return ret; + + if (of_device_is_compatible(np, "st,spear1340-pwm")) { + ret = clk_enable(pc->clk); + if (!ret) { + clk_unprepare(pc->clk); + return ret; + } + /* + * Following enables PWM chip, channels would still be + * enabled individually through their control register + */ + val = readl_relaxed(pc->mmio_base + PWMMCR); + val |= PWMMCR_PWM_ENABLE; + writel_relaxed(val, pc->mmio_base + PWMMCR); + + clk_disable(pc->clk); + } + + ret = pwmchip_add(&pc->chip); + if (!ret) { + clk_unprepare(pc->clk); + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + } + + return ret; +} + +static int spear_pwm_remove(struct platform_device *pdev) +{ + struct spear_pwm_chip *pc = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < NUM_PWM; i++) + pwm_disable(&pc->chip.pwms[i]); + + /* clk was prepared in probe, hence unprepare it here */ + clk_unprepare(pc->clk); + return pwmchip_remove(&pc->chip); +} + +static struct of_device_id spear_pwm_of_match[] = { + { .compatible = "st,spear320-pwm" }, + { .compatible = "st,spear1340-pwm" }, + { } +}; + +MODULE_DEVICE_TABLE(of, spear_pwm_of_match); + +static struct platform_driver spear_pwm_driver = { + .driver = { + .name = "spear-pwm", + .of_match_table = spear_pwm_of_match, + }, + .probe = spear_pwm_probe, + .remove = spear_pwm_remove, +}; + +module_platform_driver(spear_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Shiraz Hashim <shiraz.hashim@st.com>"); +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>"); +MODULE_ALIAS("platform:spear-pwm"); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 87c091b245cc..5cf016dd9822 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -25,6 +25,10 @@ #include <linux/clk.h> #include <linux/pm_runtime.h> #include <linux/pwm.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> + +#include "pwm-tipwmss.h" /* ECAP registers and bits definitions */ #define CAP1 0x08 @@ -184,12 +188,24 @@ static const struct pwm_ops ecap_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ecap_of_match[] = { + { .compatible = "ti,am33xx-ecap" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ecap_of_match); + static int ecap_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ecap_pwm_chip *pc; + u16 status; + struct pinctrl *pinctrl; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, "unable to select pin group\n"); pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -211,6 +227,8 @@ static int ecap_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ecap_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = 1; @@ -231,14 +249,40 @@ static int ecap_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_ECAPCLK_EN); + if (!(status & PWMSS_ECAPCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int ecap_pwm_remove(struct platform_device *pdev) { struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -246,7 +290,9 @@ static int ecap_pwm_remove(struct platform_device *pdev) static struct platform_driver ecap_pwm_driver = { .driver = { - .name = "ecap", + .name = "ecap", + .owner = THIS_MODULE, + .of_match_table = ecap_of_match, }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 9ffd389d0c8b..72a6dd40c9ec 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -25,6 +25,10 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/pm_runtime.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> + +#include "pwm-tipwmss.h" /* EHRPWM registers and bits definitions */ @@ -115,6 +119,7 @@ struct ehrpwm_pwm_chip { void __iomem *mmio_base; unsigned long period_cycles[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL]; + struct clk *tbclk; }; static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) @@ -335,6 +340,9 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Channels polarity can be configured from action qualifier module */ configure_polarity(pc, pwm->hwpwm); + /* Enable TBCLK before enabling PWM device */ + clk_enable(pc->tbclk); + /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); return 0; @@ -363,6 +371,9 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); + /* Disabling TBCLK on PWM disable */ + clk_disable(pc->tbclk); + /* Stop Time base counter */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); @@ -392,12 +403,24 @@ static const struct pwm_ops ehrpwm_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ehrpwm_of_match[] = { + { .compatible = "ti,am33xx-ehrpwm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehrpwm_of_match); + static int ehrpwm_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ehrpwm_pwm_chip *pc; + u16 status; + struct pinctrl *pinctrl; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, "unable to select pin group\n"); pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -419,6 +442,8 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ehrpwm_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = NUM_PWM_CHANNEL; @@ -432,6 +457,13 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) if (!pc->mmio_base) return -EADDRNOTAVAIL; + /* Acquire tbclk for Time Base EHRPWM submodule */ + pc->tbclk = devm_clk_get(&pdev->dev, "tbclk"); + if (IS_ERR(pc->tbclk)) { + dev_err(&pdev->dev, "Failed to get tbclk\n"); + return PTR_ERR(pc->tbclk); + } + ret = pwmchip_add(&pc->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); @@ -439,14 +471,40 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_EPWMCLK_EN); + if (!(status & PWMSS_EPWMCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int ehrpwm_pwm_remove(struct platform_device *pdev) { struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_EPWMCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -454,7 +512,9 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) static struct platform_driver ehrpwm_pwm_driver = { .driver = { - .name = "ehrpwm", + .name = "ehrpwm", + .owner = THIS_MODULE, + .of_match_table = ehrpwm_of_match, }, .probe = ehrpwm_pwm_probe, .remove = ehrpwm_pwm_remove, diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c new file mode 100644 index 000000000000..3448a1c88590 --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.c @@ -0,0 +1,139 @@ +/* + * TI PWM Subsystem driver + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/pm_runtime.h> +#include <linux/of_device.h> + +#include "pwm-tipwmss.h" + +#define PWMSS_CLKCONFIG 0x8 /* Clock gating reg */ +#define PWMSS_CLKSTATUS 0xc /* Clock gating status reg */ + +struct pwmss_info { + void __iomem *mmio_base; + struct mutex pwmss_lock; + u16 pwmss_clkconfig; +}; + +u16 pwmss_submodule_state_change(struct device *dev, int set) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + u16 val; + + mutex_lock(&info->pwmss_lock); + val = readw(info->mmio_base + PWMSS_CLKCONFIG); + val |= set; + writew(val , info->mmio_base + PWMSS_CLKCONFIG); + mutex_unlock(&info->pwmss_lock); + + return readw(info->mmio_base + PWMSS_CLKSTATUS); +} +EXPORT_SYMBOL(pwmss_submodule_state_change); + +static const struct of_device_id pwmss_of_match[] = { + { .compatible = "ti,am33xx-pwmss" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pwmss_of_match); + +static int pwmss_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct pwmss_info *info; + struct device_node *node = pdev->dev.of_node; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + mutex_init(&info->pwmss_lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + info->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!info->mmio_base) + return -EADDRNOTAVAIL; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + platform_set_drvdata(pdev, info); + + /* Populate all the child nodes here... */ + ret = of_platform_populate(node, NULL, NULL, &pdev->dev); + if (ret) + dev_err(&pdev->dev, "no child node found\n"); + + return ret; +} + +static int pwmss_remove(struct platform_device *pdev) +{ + struct pwmss_info *info = platform_get_drvdata(pdev); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + mutex_destroy(&info->pwmss_lock); + return 0; +} + +static int pwmss_suspend(struct device *dev) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + + info->pwmss_clkconfig = readw(info->mmio_base + PWMSS_CLKCONFIG); + pm_runtime_put_sync(dev); + return 0; +} + +static int pwmss_resume(struct device *dev) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + writew(info->pwmss_clkconfig, info->mmio_base + PWMSS_CLKCONFIG); + return 0; +} + +static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume); + +static struct platform_driver pwmss_driver = { + .driver = { + .name = "pwmss", + .owner = THIS_MODULE, + .pm = &pwmss_pm_ops, + .of_match_table = pwmss_of_match, + }, + .probe = pwmss_probe, + .remove = pwmss_remove, +}; + +module_platform_driver(pwmss_driver); + +MODULE_DESCRIPTION("PWM Subsystem driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-tipwmss.h b/drivers/pwm/pwm-tipwmss.h new file mode 100644 index 000000000000..11f76a1e266b --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.h @@ -0,0 +1,39 @@ +/* + * TI PWM Subsystem driver + * + * 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. + * + */ + +#ifndef __TIPWMSS_H +#define __TIPWMSS_H + +#ifdef CONFIG_PWM_TIPWMSS +/* PWM substem clock gating */ +#define PWMSS_ECAPCLK_EN BIT(0) +#define PWMSS_ECAPCLK_STOP_REQ BIT(1) +#define PWMSS_EPWMCLK_EN BIT(8) +#define PWMSS_EPWMCLK_STOP_REQ BIT(9) + +#define PWMSS_ECAPCLK_EN_ACK BIT(0) +#define PWMSS_EPWMCLK_EN_ACK BIT(8) + +extern u16 pwmss_submodule_state_change(struct device *dev, int set); +#else +static inline u16 pwmss_submodule_state_change(struct device *dev, int set) +{ + /* return success status value */ + return 0xFFFF; +} +#endif +#endif /* __TIPWMSS_H */ diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c new file mode 100644 index 000000000000..9dfa0f3eca30 --- /dev/null +++ b/drivers/pwm/pwm-twl-led.c @@ -0,0 +1,344 @@ +/* + * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver + * + * Copyright (C) 2012 Texas Instruments + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> + * + * This driver is a complete rewrite of the former pwm-twl6030.c authorded by: + * Hemanth V <hemanthv@ti.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. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/i2c/twl.h> +#include <linux/slab.h> + +/* + * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030. + * To generate the signal on TWL4030: + * - LEDA uses PWMA + * - LEDB uses PWMB + * TWL6030 has one LED pin with dedicated LEDPWM + */ + +#define TWL4030_LED_MAX 0x7f +#define TWL6030_LED_MAX 0xff + +/* Registers, bits and macro for TWL4030 */ +#define TWL4030_LEDEN_REG 0x00 +#define TWL4030_PWMA_REG 0x01 + +#define TWL4030_LEDXON (1 << 0) +#define TWL4030_LEDXPWM (1 << 4) +#define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM) +#define TWL4030_LED_TOGGLE(led, x) ((x) << (led)) + +/* Register, bits and macro for TWL6030 */ +#define TWL6030_LED_PWM_CTRL1 0xf4 +#define TWL6030_LED_PWM_CTRL2 0xf5 + +#define TWL6040_LED_MODE_HW 0x00 +#define TWL6040_LED_MODE_ON 0x01 +#define TWL6040_LED_MODE_OFF 0x02 +#define TWL6040_LED_MODE_MASK 0x03 + +struct twl_pwmled_chip { + struct pwm_chip chip; + struct mutex mutex; +}; + +static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) +{ + return container_of(chip, struct twl_pwmled_chip, chip); +} + +static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1; + u8 pwm_config[2] = { 1, 0 }; + int base, ret; + + /* + * To configure the duty period: + * On-cycle is set to 1 (the minimum allowed value) + * The off time of 0 is not configurable, so the mapping is: + * 0 -> off cycle = 2, + * 1 -> off cycle = 2, + * 2 -> off cycle = 3, + * 126 - > off cycle 127, + * 127 - > off cycle 1 + * When on cycle == off cycle the PWM will be always on + */ + if (duty_cycle == 1) + duty_cycle = 2; + else if (duty_cycle > TWL4030_LED_MAX) + duty_cycle = 1; + + base = pwm->hwpwm * 2 + TWL4030_PWMA_REG; + + pwm_config[1] = duty_cycle; + + ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); + goto out; + } + + val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); + + ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwmled_disable(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); + goto out; + } + + val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); + + ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns; + u8 on_time; + int ret; + + on_time = duty_cycle & 0xff; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time, + TWL6030_LED_PWM_CTRL1); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_ON; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl6030_pwmled_disable(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_OFF; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_OFF; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_HW; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static const struct pwm_ops twl4030_pwmled_ops = { + .enable = twl4030_pwmled_enable, + .disable = twl4030_pwmled_disable, + .config = twl4030_pwmled_config, +}; + +static const struct pwm_ops twl6030_pwmled_ops = { + .enable = twl6030_pwmled_enable, + .disable = twl6030_pwmled_disable, + .config = twl6030_pwmled_config, + .request = twl6030_pwmled_request, + .free = twl6030_pwmled_free, +}; + +static int twl_pwmled_probe(struct platform_device *pdev) +{ + struct twl_pwmled_chip *twl; + int ret; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (twl_class_is_4030()) { + twl->chip.ops = &twl4030_pwmled_ops; + twl->chip.npwm = 2; + } else { + twl->chip.ops = &twl6030_pwmled_ops; + twl->chip.npwm = 1; + } + + twl->chip.dev = &pdev->dev; + twl->chip.base = -1; + + mutex_init(&twl->mutex); + + ret = pwmchip_add(&twl->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl); + + return 0; +} + +static int twl_pwmled_remove(struct platform_device *pdev) +{ + struct twl_pwmled_chip *twl = platform_get_drvdata(pdev); + + return pwmchip_remove(&twl->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id twl_pwmled_of_match[] = { + { .compatible = "ti,twl4030-pwmled" }, + { .compatible = "ti,twl6030-pwmled" }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_pwmled_of_match); +#endif + +static struct platform_driver twl_pwmled_driver = { + .driver = { + .name = "twl-pwmled", + .of_match_table = of_match_ptr(twl_pwmled_of_match), + }, + .probe = twl_pwmled_probe, + .remove = twl_pwmled_remove, +}; +module_platform_driver(twl_pwmled_driver); + +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); +MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs"); +MODULE_ALIAS("platform:twl-pwmled"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c new file mode 100644 index 000000000000..e65db95d5e59 --- /dev/null +++ b/drivers/pwm/pwm-twl.c @@ -0,0 +1,359 @@ +/* + * Driver for TWL4030/6030 Generic Pulse Width Modulator + * + * Copyright (C) 2012 Texas Instruments + * Author: Peter Ujfalusi <peter.ujfalusi@ti.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. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/i2c/twl.h> +#include <linux/slab.h> + +/* + * This driver handles the PWMs of TWL4030 and TWL6030. + * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1 + * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2 + */ + +#define TWL_PWM_MAX 0x7f + +/* Registers, bits and macro for TWL4030 */ +#define TWL4030_GPBR1_REG 0x0c +#define TWL4030_PMBR1_REG 0x0d + +/* GPBR1 register bits */ +#define TWL4030_PWMXCLK_ENABLE (1 << 0) +#define TWL4030_PWMX_ENABLE (1 << 2) +#define TWL4030_PWMX_BITS (TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE) +#define TWL4030_PWM_TOGGLE(pwm, x) ((x) << (pwm)) + +/* PMBR1 register bits */ +#define TWL4030_GPIO6_PWM0_MUTE_MASK (0x03 << 2) +#define TWL4030_GPIO6_PWM0_MUTE_PWM0 (0x01 << 2) +#define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK (0x03 << 4) +#define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1 (0x03 << 4) + +/* Register, bits and macro for TWL6030 */ +#define TWL6030_TOGGLE3_REG 0x92 + +#define TWL6030_PWMXR (1 << 0) +#define TWL6030_PWMXS (1 << 1) +#define TWL6030_PWMXEN (1 << 2) +#define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3)) + +struct twl_pwm_chip { + struct pwm_chip chip; + struct mutex mutex; + u8 twl6030_toggle3; + u8 twl4030_pwm_mux; +}; + +static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip) +{ + return container_of(chip, struct twl_pwm_chip, chip); +} + +static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; + u8 pwm_config[2] = { 1, 0 }; + int base, ret; + + /* + * To configure the duty period: + * On-cycle is set to 1 (the minimum allowed value) + * The off time of 0 is not configurable, so the mapping is: + * 0 -> off cycle = 2, + * 1 -> off cycle = 2, + * 2 -> off cycle = 3, + * 126 - > off cycle 127, + * 127 - > off cycle 1 + * When on cycle == off cycle the PWM will be always on + */ + if (duty_cycle == 1) + duty_cycle = 2; + else if (duty_cycle > TWL_PWM_MAX) + duty_cycle = 1; + + base = pwm->hwpwm * 3; + + pwm_config[1] = duty_cycle; + + ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + goto out; + } + + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + goto out; + } + + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val, mask, bits; + + if (pwm->hwpwm == 1) { + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; + bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1; + } else { + mask = TWL4030_GPIO6_PWM0_MUTE_MASK; + bits = TWL4030_GPIO6_PWM0_MUTE_PWM0; + } + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + goto out; + } + + /* Save the current MUX configuration for the PWM */ + twl->twl4030_pwm_mux &= ~mask; + twl->twl4030_pwm_mux |= (val & mask); + + /* Select PWM functionality */ + val &= ~mask; + val |= bits; + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val, mask; + + if (pwm->hwpwm == 1) + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; + else + mask = TWL4030_GPIO6_PWM0_MUTE_MASK; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + goto out; + } + + /* Restore the MUX configuration for the PWM */ + val &= ~mask; + val |= (twl->twl4030_pwm_mux & mask); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + val = twl->twl6030_toggle3; + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + goto out; + } + + twl->twl6030_toggle3 = val; +out: + mutex_unlock(&twl->mutex); + return 0; +} + +static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + val = twl->twl6030_toggle3; + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read TOGGLE3\n", pwm->label); + goto out; + } + + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + goto out; + } + + twl->twl6030_toggle3 = val; +out: + mutex_unlock(&twl->mutex); +} + +static const struct pwm_ops twl4030_pwm_ops = { + .config = twl_pwm_config, + .enable = twl4030_pwm_enable, + .disable = twl4030_pwm_disable, + .request = twl4030_pwm_request, + .free = twl4030_pwm_free, +}; + +static const struct pwm_ops twl6030_pwm_ops = { + .config = twl_pwm_config, + .enable = twl6030_pwm_enable, + .disable = twl6030_pwm_disable, +}; + +static int twl_pwm_probe(struct platform_device *pdev) +{ + struct twl_pwm_chip *twl; + int ret; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (twl_class_is_4030()) + twl->chip.ops = &twl4030_pwm_ops; + else + twl->chip.ops = &twl6030_pwm_ops; + + twl->chip.dev = &pdev->dev; + twl->chip.base = -1; + twl->chip.npwm = 2; + + mutex_init(&twl->mutex); + + ret = pwmchip_add(&twl->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl); + + return 0; +} + +static int twl_pwm_remove(struct platform_device *pdev) +{ + struct twl_pwm_chip *twl = platform_get_drvdata(pdev); + + return pwmchip_remove(&twl->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id twl_pwm_of_match[] = { + { .compatible = "ti,twl4030-pwm" }, + { .compatible = "ti,twl6030-pwm" }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_pwm_of_match); +#endif + +static struct platform_driver twl_pwm_driver = { + .driver = { + .name = "twl-pwm", + .of_match_table = of_match_ptr(twl_pwm_of_match), + }, + .probe = twl_pwm_probe, + .remove = twl_pwm_remove, +}; +module_platform_driver(twl_pwm_driver); + +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); +MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030"); +MODULE_ALIAS("platform:twl-pwm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-twl6030.c b/drivers/pwm/pwm-twl6030.c deleted file mode 100644 index 378a7e286366..000000000000 --- a/drivers/pwm/pwm-twl6030.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * twl6030_pwm.c - * Driver for PHOENIX (TWL6030) Pulse Width Modulator - * - * Copyright (C) 2010 Texas Instruments - * Author: Hemanth V <hemanthv@ti.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. - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/pwm.h> -#include <linux/i2c/twl.h> -#include <linux/slab.h> - -#define LED_PWM_CTRL1 0xF4 -#define LED_PWM_CTRL2 0xF5 - -/* Max value for CTRL1 register */ -#define PWM_CTRL1_MAX 255 - -/* Pull down disable */ -#define PWM_CTRL2_DIS_PD (1 << 6) - -/* Current control 2.5 milli Amps */ -#define PWM_CTRL2_CURR_02 (2 << 4) - -/* LED supply source */ -#define PWM_CTRL2_SRC_VAC (1 << 2) - -/* LED modes */ -#define PWM_CTRL2_MODE_HW (0 << 0) -#define PWM_CTRL2_MODE_SW (1 << 0) -#define PWM_CTRL2_MODE_DIS (2 << 0) - -#define PWM_CTRL2_MODE_MASK 0x3 - -struct twl6030_pwm_chip { - struct pwm_chip chip; -}; - -static int twl6030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) -{ - int ret; - u8 val; - - /* Configure PWM */ - val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | - PWM_CTRL2_MODE_HW; - - ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); - if (ret < 0) { - dev_err(chip->dev, "%s: Failed to configure PWM, Error %d\n", - pwm->label, ret); - return ret; - } - - return 0; -} - -static int twl6030_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - u8 duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns; - int ret; - - ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1); - if (ret < 0) { - pr_err("%s: Failed to configure PWM, Error %d\n", - pwm->label, ret); - return ret; - } - - return 0; -} - -static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - int ret; - u8 val; - - ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); - if (ret < 0) { - dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", - pwm->label, ret); - return ret; - } - - /* Change mode to software control */ - val &= ~PWM_CTRL2_MODE_MASK; - val |= PWM_CTRL2_MODE_SW; - - ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); - if (ret < 0) { - dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", - pwm->label, ret); - return ret; - } - - twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); - return 0; -} - -static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - int ret; - u8 val; - - ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); - if (ret < 0) { - dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", - pwm->label, ret); - return; - } - - val &= ~PWM_CTRL2_MODE_MASK; - val |= PWM_CTRL2_MODE_HW; - - ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); - if (ret < 0) { - dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", - pwm->label, ret); - } -} - -static const struct pwm_ops twl6030_pwm_ops = { - .request = twl6030_pwm_request, - .config = twl6030_pwm_config, - .enable = twl6030_pwm_enable, - .disable = twl6030_pwm_disable, -}; - -static int twl6030_pwm_probe(struct platform_device *pdev) -{ - struct twl6030_pwm_chip *twl6030; - int ret; - - twl6030 = devm_kzalloc(&pdev->dev, sizeof(*twl6030), GFP_KERNEL); - if (!twl6030) - return -ENOMEM; - - twl6030->chip.dev = &pdev->dev; - twl6030->chip.ops = &twl6030_pwm_ops; - twl6030->chip.base = -1; - twl6030->chip.npwm = 1; - - ret = pwmchip_add(&twl6030->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, twl6030); - - return 0; -} - -static int twl6030_pwm_remove(struct platform_device *pdev) -{ - struct twl6030_pwm_chip *twl6030 = platform_get_drvdata(pdev); - - return pwmchip_remove(&twl6030->chip); -} - -static struct platform_driver twl6030_pwm_driver = { - .driver = { - .name = "twl6030-pwm", - }, - .probe = twl6030_pwm_probe, - .remove = twl6030_pwm_remove, -}; -module_platform_driver(twl6030_pwm_driver); - -MODULE_ALIAS("platform:twl6030-pwm"); -MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index ad14389b7144..b0ba2d403439 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -1,7 +1,8 @@ /* * drivers/pwm/pwm-vt8500.c * - * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,14 +22,24 @@ #include <linux/io.h> #include <linux/pwm.h> #include <linux/delay.h> +#include <linux/clk.h> #include <asm/div64.h> -#define VT8500_NR_PWMS 4 +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> + +/* + * SoC architecture allocates register space for 4 PWMs but only + * 2 are currently implemented. + */ +#define VT8500_NR_PWMS 2 struct vt8500_chip { struct pwm_chip chip; void __iomem *base; + struct clk *clk; }; #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) @@ -51,8 +62,15 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, struct vt8500_chip *vt8500 = to_vt8500_chip(chip); unsigned long long c; unsigned long period_cycles, prescale, pv, dc; + int err; - c = 25000000/2; /* wild guess --- need to implement clocks */ + err = clk_enable(vt8500->clk); + if (err < 0) { + dev_err(chip->dev, "failed to enable clock\n"); + return err; + } + + c = clk_get_rate(vt8500->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -64,8 +82,10 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (pv > 4095) pv = 4095; - if (prescale > 1023) + if (prescale > 1023) { + clk_disable(vt8500->clk); return -EINVAL; + } c = (unsigned long long)pv * duty_ns; do_div(c, period_ns); @@ -80,13 +100,21 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3)); writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4)); + clk_disable(vt8500->clk); return 0; } static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { + int err; struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + err = clk_enable(vt8500->clk); + if (err < 0) { + dev_err(chip->dev, "failed to enable clock\n"); + return err; + } + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); writel(5, vt8500->base + (pwm->hwpwm << 4)); return 0; @@ -98,6 +126,8 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); writel(0, vt8500->base + (pwm->hwpwm << 4)); + + clk_disable(vt8500->clk); } static struct pwm_ops vt8500_pwm_ops = { @@ -107,12 +137,24 @@ static struct pwm_ops vt8500_pwm_ops = { .owner = THIS_MODULE, }; -static int __devinit pwm_probe(struct platform_device *pdev) +static const struct of_device_id vt8500_pwm_dt_ids[] = { + { .compatible = "via,vt8500-pwm", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids); + +static int vt8500_pwm_probe(struct platform_device *pdev) { struct vt8500_chip *chip; struct resource *r; + struct device_node *np = pdev->dev.of_node; int ret; + if (!np) { + dev_err(&pdev->dev, "invalid devicetree node\n"); + return -EINVAL; + } + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); @@ -124,6 +166,12 @@ static int __devinit pwm_probe(struct platform_device *pdev) chip->chip.base = -1; chip->chip.npwm = VT8500_NR_PWMS; + chip->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(chip->clk)) { + dev_err(&pdev->dev, "clock source not specified\n"); + return PTR_ERR(chip->clk); + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "no memory resource defined\n"); @@ -131,18 +179,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) } chip->base = devm_request_and_ioremap(&pdev->dev, r); - if (chip->base == NULL) + if (!chip->base) return -EADDRNOTAVAIL; + ret = clk_prepare(chip->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to prepare clock\n"); + return ret; + } + ret = pwmchip_add(&chip->chip); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip\n"); return ret; + } platform_set_drvdata(pdev, chip); return ret; } -static int __devexit pwm_remove(struct platform_device *pdev) +static int vt8500_pwm_remove(struct platform_device *pdev) { struct vt8500_chip *chip; @@ -150,28 +206,22 @@ static int __devexit pwm_remove(struct platform_device *pdev) if (chip == NULL) return -ENODEV; + clk_unprepare(chip->clk); + return pwmchip_remove(&chip->chip); } -static struct platform_driver pwm_driver = { +static struct platform_driver vt8500_pwm_driver = { + .probe = vt8500_pwm_probe, + .remove = vt8500_pwm_remove, .driver = { .name = "vt8500-pwm", .owner = THIS_MODULE, + .of_match_table = vt8500_pwm_dt_ids, }, - .probe = pwm_probe, - .remove = __devexit_p(pwm_remove), }; +module_platform_driver(vt8500_pwm_driver); -static int __init pwm_init(void) -{ - return platform_driver_register(&pwm_driver); -} -arch_initcall(pwm_init); - -static void __exit pwm_exit(void) -{ - platform_driver_unregister(&pwm_driver); -} -module_exit(pwm_exit); - -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VT8500 PWM Driver"); +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 75c0c4f5fdf2..ab34497bcfee 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -20,6 +20,7 @@ #include <linux/spi/spi.h> #include <linux/slab.h> #include <linux/platform_data/atmel.h> +#include <linux/of.h> #include <asm/io.h> #include <asm/gpio.h> @@ -768,6 +769,10 @@ static int atmel_spi_setup(struct spi_device *spi) /* chipselect must have been muxed as GPIO (e.g. in board setup) */ npcs_pin = (unsigned int)spi->controller_data; + + if (gpio_is_valid(spi->cs_gpio)) + npcs_pin = spi->cs_gpio; + asd = spi->controller_state; if (!asd) { asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL); @@ -937,8 +942,9 @@ static int atmel_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; - master->num_chipselect = 4; + master->num_chipselect = master->dev.of_node ? 0 : 4; master->setup = atmel_spi_setup; master->transfer = atmel_spi_transfer; master->cleanup = atmel_spi_cleanup; @@ -1064,11 +1070,20 @@ static int atmel_spi_resume(struct platform_device *pdev) #define atmel_spi_resume NULL #endif +#if defined(CONFIG_OF) +static const struct of_device_id atmel_spi_dt_ids[] = { + { .compatible = "atmel,at91rm9200-spi" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids); +#endif static struct platform_driver atmel_spi_driver = { .driver = { .name = "atmel_spi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_spi_dt_ids), }, .suspend = atmel_spi_suspend, .resume = atmel_spi_resume, diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 4dd7b7ce5c5a..ad93231a8038 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -215,6 +215,10 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) writel(0, regs + S3C64XX_SPI_PACKET_CNT); val = readl(regs + S3C64XX_SPI_CH_CFG); + val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); + writel(val, regs + S3C64XX_SPI_CH_CFG); + + val = readl(regs + S3C64XX_SPI_CH_CFG); val |= S3C64XX_SPI_CH_SW_RST; val &= ~S3C64XX_SPI_CH_HS_EN; writel(val, regs + S3C64XX_SPI_CH_CFG); @@ -248,10 +252,6 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) val = readl(regs + S3C64XX_SPI_MODE_CFG); val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); writel(val, regs + S3C64XX_SPI_MODE_CFG); - - val = readl(regs + S3C64XX_SPI_CH_CFG); - val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); - writel(val, regs + S3C64XX_SPI_CH_CFG); } static void s3c64xx_spi_dmacb(void *data) @@ -771,8 +771,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, if (list_is_last(&xfer->transfer_list, &msg->transfers)) cs_toggle = 1; - else - disable_cs(sdd, spi); } msg->actual_length += xfer->len; diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 32f7b55fce09..60cfae51c713 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -290,7 +290,7 @@ static int hspi_probe(struct platform_device *pdev) } clk = clk_get(NULL, "shyway_clk"); - if (!clk) { + if (IS_ERR(clk)) { dev_err(&pdev->dev, "shyway_clk is required\n"); ret = -EINVAL; goto error0; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ab095acdb2a8..19ee901577da 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -824,6 +824,7 @@ static void of_register_spi_devices(struct spi_master *master) struct spi_device *spi; struct device_node *nc; const __be32 *prop; + char modalias[SPI_NAME_SIZE + 4]; int rc; int len; @@ -887,7 +888,9 @@ static void of_register_spi_devices(struct spi_master *master) spi->dev.of_node = nc; /* Register the new device */ - request_module(spi->modalias); + snprintf(modalias, sizeof(modalias), "%s%s", SPI_MODULE_PREFIX, + spi->modalias); + request_module(modalias); rc = spi_add_device(spi); if (rc) { dev_err(&master->dev, "spi_device register error %s\n", diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 0c96eb52c797..03310721712f 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -417,14 +417,16 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_unlock(&c->erase_completion_lock); ret = jffs2_prealloc_raw_node_refs(c, jeb, 1); - if (ret) - return ret; + /* Just lock it again and continue. Nothing much can change because we hold c->alloc_sem anyway. In fact, it's not entirely clear why we hold c->erase_completion_lock in the majority of this function... but that's a question for another (more caffeine-rich) day. */ spin_lock(&c->erase_completion_lock); + if (ret) + return ret; + waste = jeb->free_size; jffs2_link_node_ref(c, jeb, (jeb->offset + c->sector_size - waste) | REF_OBSOLETE, diff --git a/include/linux/asn1.h b/include/linux/asn1.h index 5c3f4e4b9a23..eed6982860ba 100644 --- a/include/linux/asn1.h +++ b/include/linux/asn1.h @@ -64,4 +64,6 @@ enum asn1_tag { ASN1_LONG_TAG = 31 /* Long form tag */ }; +#define ASN1_INDEFINITE_LENGTH 0x80 + #endif /* _LINUX_ASN1_H */ diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 93b1e091b1e9..e0ce311011c0 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -350,6 +350,7 @@ extern void bcma_core_set_clockmode(struct bcma_device *core, enum bcma_clkmode clkmode); extern void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on); +extern u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset); #define BCMA_DMA_TRANSLATION_MASK 0xC0000000 #define BCMA_DMA_TRANSLATION_NONE 0x00000000 #define BCMA_DMA_TRANSLATION_DMA32_CMT 0x40000000 /* Client Mode Translation for 32-bit DMA */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index acb4f7bbbd32..f94bc83011ed 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1188,14 +1188,25 @@ static inline int queue_discard_alignment(struct request_queue *q) static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector_t sector) { - sector_t alignment = sector << 9; - alignment = sector_div(alignment, lim->discard_granularity); + unsigned int alignment, granularity, offset; if (!lim->max_discard_sectors) return 0; - alignment = lim->discard_granularity + lim->discard_alignment - alignment; - return sector_div(alignment, lim->discard_granularity); + /* Why are these in bytes, not sectors? */ + alignment = lim->discard_alignment >> 9; + granularity = lim->discard_granularity >> 9; + if (!granularity) + return 0; + + /* Offset of the partition start in 'granularity' sectors */ + offset = sector_div(sector, granularity); + + /* And why do we do this modulus *again* in blkdev_issue_discard()? */ + offset = (granularity + alignment - offset) % granularity; + + /* Turn it back into bytes, gaah */ + return offset << 9; } static inline int bdev_discard_alignment(struct block_device *bdev) diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h index 412bc6c2b023..662fd1b4c42a 100644 --- a/include/linux/compiler-gcc4.h +++ b/include/linux/compiler-gcc4.h @@ -31,6 +31,8 @@ #define __linktime_error(message) __attribute__((__error__(message))) +#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) + #if __GNUC_MINOR__ >= 5 /* * Mark a position in code as unreachable. This can be used to @@ -63,3 +65,13 @@ #define __compiletime_warning(message) __attribute__((warning(message))) #define __compiletime_error(message) __attribute__((error(message))) #endif + +#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP +#if __GNUC_MINOR__ >= 4 +#define __HAVE_BUILTIN_BSWAP32__ +#define __HAVE_BUILTIN_BSWAP64__ +#endif +#if __GNUC_MINOR__ >= 8 || (defined(__powerpc__) && __GNUC_MINOR__ >= 6) +#define __HAVE_BUILTIN_BSWAP16__ +#endif +#endif diff --git a/include/linux/compiler-intel.h b/include/linux/compiler-intel.h index d8e636e5607d..973ce10c40b6 100644 --- a/include/linux/compiler-intel.h +++ b/include/linux/compiler-intel.h @@ -29,3 +29,10 @@ #endif #define uninitialized_var(x) x + +#ifndef __HAVE_BUILTIN_BSWAP16__ +/* icc has this, but it's called _bswap16 */ +#define __HAVE_BUILTIN_BSWAP16__ +#define __builtin_bswap16 _bswap16 +#endif + diff --git a/include/linux/compiler.h b/include/linux/compiler.h index b121554f1fe2..dd852b73b286 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -44,6 +44,10 @@ extern void __chk_io_ptr(const volatile void __iomem *); # define __rcu #endif +/* Indirect macros required for expanded argument pasting, eg. __LINE__. */ +#define ___PASTE(a,b) a##b +#define __PASTE(a,b) ___PASTE(a,b) + #ifdef __KERNEL__ #ifdef __GNUC__ @@ -166,6 +170,11 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); (typeof(ptr)) (__ptr + (off)); }) #endif +/* Not-quite-unique ID. */ +#ifndef __UNIQUE_ID +# define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__) +#endif + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ diff --git a/include/linux/ima.h b/include/linux/ima.h index 2c7223d7e73b..86c361e947b9 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -18,6 +18,7 @@ extern int ima_bprm_check(struct linux_binprm *bprm); extern int ima_file_check(struct file *file, int mask); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); +extern int ima_module_check(struct file *file); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -40,6 +41,11 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) return 0; } +static inline int ima_module_check(struct file *file) +{ + return 0; +} + #endif /* CONFIG_IMA_H */ #ifdef CONFIG_IMA_APPRAISE diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index d6a58065c09c..137b4198fc03 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -16,17 +16,15 @@ /* Chosen so that structs with an unsigned long line up. */ #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long)) -#define ___module_cat(a,b) __mod_ ## a ## b -#define __module_cat(a,b) ___module_cat(a,b) #ifdef MODULE #define __MODULE_INFO(tag, name, info) \ -static const char __module_cat(name,__LINE__)[] \ +static const char __UNIQUE_ID(name)[] \ __used __attribute__((section(".modinfo"), unused, aligned(1))) \ = __stringify(tag) "=" info #else /* !MODULE */ /* This struct is here for syntactic coherency, it is not used */ #define __MODULE_INFO(tag, name, info) \ - struct __module_cat(name,__LINE__) {} + struct __UNIQUE_ID(name) {} #endif #define __MODULE_PARM_TYPE(name, _type) \ __MODULE_INFO(parmtype, name##type, #name ":" _type) diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index ed270bd2e4df..4eb0a50d0c55 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -23,6 +23,7 @@ #include <linux/mutex.h> #include <linux/kref.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> struct hd_geometry; struct mtd_info; @@ -43,7 +44,8 @@ struct mtd_blktrans_dev { struct kref ref; struct gendisk *disk; struct attribute_group *disk_attributes; - struct task_struct *thread; + struct workqueue_struct *wq; + struct work_struct work; struct request_queue *rq; spinlock_t queue_lock; void *priv; diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 0f6fea73a1f6..407d1e556c39 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -92,12 +92,26 @@ * Others use readb/writeb */ #if defined(__arm__) -#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)))) -#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) +static inline u8 ReadDOC_(u32 __iomem *addr, unsigned long reg) +{ + return __raw_readl(addr + reg); +} +static inline void WriteDOC_(u8 data, u32 __iomem *addr, unsigned long reg) +{ + __raw_writel(data, addr + reg); + wmb(); +} #define DOC_IOREMAP_LEN 0x8000 #elif defined(__ppc__) -#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)))) -#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) +static inline u8 ReadDOC_(u16 __iomem *addr, unsigned long reg) +{ + return __raw_readw(addr + reg); +} +static inline void WriteDOC_(u8 data, u16 __iomem *addr, unsigned long reg) +{ + __raw_writew(data, addr + reg); + wmb(); +} #define DOC_IOREMAP_LEN 0x4000 #else #define ReadDOC_(adr, reg) readb((void __iomem *)(adr) + (reg)) diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h index b20029221fb1..d6ed61ef451d 100644 --- a/include/linux/mtd/fsmc.h +++ b/include/linux/mtd/fsmc.h @@ -155,9 +155,6 @@ struct fsmc_nand_platform_data { unsigned int width; unsigned int bank; - /* CLE, ALE offsets */ - unsigned int cle_off; - unsigned int ale_off; enum access_mode mode; void (*select_bank)(uint32_t bank, uint32_t busw); diff --git a/include/linux/mtd/gpmi-nand.h b/include/linux/mtd/gpmi-nand.h deleted file mode 100644 index ed3c4e09f3d1..000000000000 --- a/include/linux/mtd/gpmi-nand.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef __MACH_MXS_GPMI_NAND_H__ -#define __MACH_MXS_GPMI_NAND_H__ - -/* The size of the resources is fixed. */ -#define GPMI_NAND_RES_SIZE 6 - -/* Resource names for the GPMI NAND driver. */ -#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" -#define GPMI_NAND_GPMI_INTERRUPT_RES_NAME "GPMI NAND GPMI Interrupt" -#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" -#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" -#define GPMI_NAND_DMA_CHANNELS_RES_NAME "GPMI NAND DMA Channels" -#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "gpmi-dma" - -/** - * struct gpmi_nand_platform_data - GPMI NAND driver platform data. - * - * This structure communicates platform-specific information to the GPMI NAND - * driver that can't be expressed as resources. - * - * @platform_init: A pointer to a function the driver will call to - * initialize the platform (e.g., set up the pin mux). - * @min_prop_delay_in_ns: Minimum propagation delay of GPMI signals to and - * from the NAND Flash device, in nanoseconds. - * @max_prop_delay_in_ns: Maximum propagation delay of GPMI signals to and - * from the NAND Flash device, in nanoseconds. - * @max_chip_count: The maximum number of chips for which the driver - * should configure the hardware. This value most - * likely reflects the number of pins that are - * connected to a NAND Flash device. If this is - * greater than the SoC hardware can support, the - * driver will print a message and fail to initialize. - * @partitions: An optional pointer to an array of partition - * descriptions. - * @partition_count: The number of elements in the partitions array. - */ -struct gpmi_nand_platform_data { - /* SoC hardware information. */ - int (*platform_init)(void); - - /* NAND Flash information. */ - unsigned int min_prop_delay_in_ns; - unsigned int max_prop_delay_in_ns; - unsigned int max_chip_count; - - /* Medium information. */ - struct mtd_partition *partitions; - unsigned partition_count; -}; -#endif diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 3595a0236b0f..f6eb4332ac92 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -328,7 +328,7 @@ static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word static inline map_word map_word_load(struct map_info *map, const void *ptr) { - map_word r; + map_word r = {{0} }; if (map_bankwidth_is_1(map)) r.x[0] = *(unsigned char *)ptr; @@ -391,7 +391,7 @@ static inline map_word map_word_ff(struct map_info *map) static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) { - map_word r; + map_word uninitialized_var(r); if (map_bankwidth_is_1(map)) r.x[0] = __raw_readb(map->virt + ofs); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 81d61e704599..f9ac2897b86b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -98,7 +98,7 @@ struct mtd_oob_ops { }; #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32 -#define MTD_MAX_ECCPOS_ENTRIES_LARGE 448 +#define MTD_MAX_ECCPOS_ENTRIES_LARGE 640 /* * Internal ECC layout control structure. For historical reasons, there is a * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 24e915957e4f..7ccb3c59ed60 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -219,6 +219,13 @@ typedef enum { #define NAND_OWN_BUFFERS 0x00020000 /* 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 /* Options set by nand scan */ /* Nand scan has allocated controller struct */ @@ -471,8 +478,8 @@ struct nand_buffers { * non 0 if ONFI supported. * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is * supported, 0 otherwise. - * @onfi_set_features [REPLACEABLE] set the features for ONFI nand - * @onfi_get_features [REPLACEABLE] get the features for ONFI nand + * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand + * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand * @ecclayout: [REPLACEABLE] the default ECC placement scheme * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h index 01e4b15b280e..1c28f8879b1c 100644 --- a/include/linux/mtd/sh_flctl.h +++ b/include/linux/mtd/sh_flctl.h @@ -20,6 +20,7 @@ #ifndef __SH_FLCTL_H__ #define __SH_FLCTL_H__ +#include <linux/completion.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> @@ -107,6 +108,7 @@ #define ESTERINTE (0x1 << 24) /* ECC error interrupt enable */ #define AC1CLR (0x1 << 19) /* ECC FIFO clear */ #define AC0CLR (0x1 << 18) /* Data FIFO clear */ +#define DREQ0EN (0x1 << 16) /* FLDTFIFODMA Request Enable */ #define ECERB (0x1 << 9) /* ECC error */ #define STERB (0x1 << 8) /* Status error */ #define STERINTE (0x1 << 4) /* Status error enable */ @@ -138,6 +140,8 @@ enum flctl_ecc_res_t { FL_TIMEOUT }; +struct dma_chan; + struct sh_flctl { struct mtd_info mtd; struct nand_chip chip; @@ -147,7 +151,7 @@ struct sh_flctl { uint8_t done_buff[2048 + 64]; /* max size 2048 + 64 */ int read_bytes; - int index; + unsigned int index; int seqin_column; /* column in SEQIN cmd */ int seqin_page_addr; /* page_addr in SEQIN cmd */ uint32_t seqin_read_cmd; /* read cmd in SEQIN cmd */ @@ -161,6 +165,11 @@ struct sh_flctl { unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */ unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */ unsigned qos_request:1; /* QoS request to prevent deep power shutdown */ + + /* DMA related objects */ + struct dma_chan *chan_fifo0_rx; + struct dma_chan *chan_fifo0_tx; + struct completion dma_complete; }; struct sh_flctl_platform_data { @@ -170,6 +179,9 @@ struct sh_flctl_platform_data { unsigned has_hwecc:1; unsigned use_holden:1; + + unsigned int slave_id_fifo0_tx; + unsigned int slave_id_fifo0_rx; }; static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo) diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index b47d2040c9f2..3863a4dbdf18 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -100,6 +100,7 @@ extern int of_platform_populate(struct device_node *root, #if !defined(CONFIG_OF_ADDRESS) struct of_dev_auxdata; +struct device; static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, diff --git a/include/linux/platform_data/mtd-nomadik-nand.h b/include/linux/platform_data/mtd-nomadik-nand.h deleted file mode 100644 index c3c8254c22a5..000000000000 --- a/include/linux/platform_data/mtd-nomadik-nand.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __ASM_ARCH_NAND_H -#define __ASM_ARCH_NAND_H - -struct nomadik_nand_platform_data { - struct mtd_partition *parts; - int nparts; - int options; - int (*init) (void); - int (*exit) (void); -}; - -#define NAND_IO_DATA 0x40000000 -#define NAND_IO_CMD 0x40800000 -#define NAND_IO_ADDR 0x41000000 - -#endif /* __ASM_ARCH_NAND_H */ diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 112b31436848..6d661f32e0e4 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -171,6 +171,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); +struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, + const struct of_phandle_args *args); + struct pwm_device *pwm_get(struct device *dev, const char *consumer); void pwm_put(struct pwm_device *pwm); diff --git a/include/linux/security.h b/include/linux/security.h index 05e88bdcf7d9..0f6afc657f77 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -694,6 +694,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * userspace to load a kernel module with the given name. * @kmod_name name of the module requested by the kernel * Return 0 if successful. + * @kernel_module_from_file: + * Load a kernel module from userspace. + * @file contains the file structure pointing to the file containing + * the kernel module to load. If the module is being loaded from a blob, + * this argument will be NULL. + * Return 0 if permission is granted. * @task_fix_setuid: * Update the module's state after setting one or more of the user * identity attributes of the current process. The @flags parameter @@ -1508,6 +1514,7 @@ struct security_operations { int (*kernel_act_as)(struct cred *new, u32 secid); int (*kernel_create_files_as)(struct cred *new, struct inode *inode); int (*kernel_module_request)(char *kmod_name); + int (*kernel_module_from_file)(struct file *file); int (*task_fix_setuid) (struct cred *new, const struct cred *old, int flags); int (*task_setpgid) (struct task_struct *p, pid_t pgid); @@ -1765,6 +1772,7 @@ void security_transfer_creds(struct cred *new, const struct cred *old); int security_kernel_act_as(struct cred *new, u32 secid); int security_kernel_create_files_as(struct cred *new, struct inode *inode); int security_kernel_module_request(char *kmod_name); +int security_kernel_module_from_file(struct file *file); int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags); int security_task_setpgid(struct task_struct *p, pid_t pgid); @@ -2278,6 +2286,11 @@ static inline int security_kernel_module_request(char *kmod_name) return 0; } +static inline int security_kernel_module_from_file(struct file *file) +{ + return 0; +} + static inline int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 36c3b07c5119..6caee34bf8a2 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -880,4 +880,5 @@ asmlinkage long sys_process_vm_writev(pid_t pid, asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2); +asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags); #endif diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 6e595ba545f4..2c531f478410 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -690,9 +690,11 @@ __SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \ compat_sys_process_vm_writev) #define __NR_kcmp 272 __SYSCALL(__NR_kcmp, sys_kcmp) +#define __NR_finit_module 273 +__SYSCALL(__NR_finit_module, sys_finit_module) #undef __NR_syscalls -#define __NR_syscalls 273 +#define __NR_syscalls 274 /* * All syscalls below here should go away really, diff --git a/include/uapi/linux/module.h b/include/uapi/linux/module.h new file mode 100644 index 000000000000..38da4258b12f --- /dev/null +++ b/include/uapi/linux/module.h @@ -0,0 +1,8 @@ +#ifndef _UAPI_LINUX_MODULE_H +#define _UAPI_LINUX_MODULE_H + +/* Flags for sys_finit_module: */ +#define MODULE_INIT_IGNORE_MODVERSIONS 1 +#define MODULE_INIT_IGNORE_VERMAGIC 2 + +#endif /* _UAPI_LINUX_MODULE_H */ diff --git a/include/uapi/linux/swab.h b/include/uapi/linux/swab.h index e811474724c2..0e011eb91b5d 100644 --- a/include/uapi/linux/swab.h +++ b/include/uapi/linux/swab.h @@ -45,7 +45,9 @@ static inline __attribute_const__ __u16 __fswab16(__u16 val) { -#ifdef __arch_swab16 +#ifdef __HAVE_BUILTIN_BSWAP16__ + return __builtin_bswap16(val); +#elif defined (__arch_swab16) return __arch_swab16(val); #else return ___constant_swab16(val); @@ -54,7 +56,9 @@ static inline __attribute_const__ __u16 __fswab16(__u16 val) static inline __attribute_const__ __u32 __fswab32(__u32 val) { -#ifdef __arch_swab32 +#ifdef __HAVE_BUILTIN_BSWAP32__ + return __builtin_bswap32(val); +#elif defined(__arch_swab32) return __arch_swab32(val); #else return ___constant_swab32(val); @@ -63,7 +67,9 @@ static inline __attribute_const__ __u32 __fswab32(__u32 val) static inline __attribute_const__ __u64 __fswab64(__u64 val) { -#ifdef __arch_swab64 +#ifdef __HAVE_BUILTIN_BSWAP64__ + return __builtin_bswap64(val); +#elif defined (__arch_swab64) return __arch_swab64(val); #elif defined(__SWAB_64_THRU_32__) __u32 h = val >> 32; diff --git a/kernel/Makefile b/kernel/Makefile index ac0d533eb7de..6c072b6da239 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -54,7 +54,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o -obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o +obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificate.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o @@ -137,10 +137,14 @@ ifeq ($(CONFIG_MODULE_SIG),y) # # Pull the signing certificate and any extra certificates into the kernel # + +quiet_cmd_touch = TOUCH $@ + cmd_touch = touch $@ + extra_certificates: - touch $@ + $(call cmd,touch) -kernel/modsign_pubkey.o: signing_key.x509 extra_certificates +kernel/modsign_certificate.o: signing_key.x509 extra_certificates ############################################################################### # diff --git a/kernel/modsign_certificate.S b/kernel/modsign_certificate.S new file mode 100644 index 000000000000..246b4c6e6135 --- /dev/null +++ b/kernel/modsign_certificate.S @@ -0,0 +1,19 @@ +/* SYMBOL_PREFIX defined on commandline from CONFIG_SYMBOL_PREFIX */ +#ifndef SYMBOL_PREFIX +#define ASM_SYMBOL(sym) sym +#else +#define PASTE2(x,y) x##y +#define PASTE(x,y) PASTE2(x,y) +#define ASM_SYMBOL(sym) PASTE(SYMBOL_PREFIX, sym) +#endif + +#define GLOBAL(name) \ + .globl ASM_SYMBOL(name); \ + ASM_SYMBOL(name): + + .section ".init.data","aw" + +GLOBAL(modsign_certificate_list) + .incbin "signing_key.x509" + .incbin "extra_certificates" +GLOBAL(modsign_certificate_list_end) diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c index 767e559dfb10..045504fffbb2 100644 --- a/kernel/modsign_pubkey.c +++ b/kernel/modsign_pubkey.c @@ -20,12 +20,6 @@ struct key *modsign_keyring; extern __initdata const u8 modsign_certificate_list[]; extern __initdata const u8 modsign_certificate_list_end[]; -asm(".section .init.data,\"aw\"\n" - SYMBOL_PREFIX "modsign_certificate_list:\n" - ".incbin \"signing_key.x509\"\n" - ".incbin \"extra_certificates\"\n" - SYMBOL_PREFIX "modsign_certificate_list_end:" - ); /* * We need to make sure ccache doesn't cache the .o file as it doesn't notice diff --git a/kernel/module.c b/kernel/module.c index 808bd62e1723..250092c1d57d 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -21,6 +21,7 @@ #include <linux/ftrace_event.h> #include <linux/init.h> #include <linux/kallsyms.h> +#include <linux/file.h> #include <linux/fs.h> #include <linux/sysfs.h> #include <linux/kernel.h> @@ -28,6 +29,7 @@ #include <linux/vmalloc.h> #include <linux/elf.h> #include <linux/proc_fs.h> +#include <linux/security.h> #include <linux/seq_file.h> #include <linux/syscalls.h> #include <linux/fcntl.h> @@ -59,6 +61,7 @@ #include <linux/pfn.h> #include <linux/bsearch.h> #include <linux/fips.h> +#include <uapi/linux/module.h> #include "module-internal.h" #define CREATE_TRACE_POINTS @@ -2279,7 +2282,7 @@ static void layout_symtab(struct module *mod, struct load_info *info) Elf_Shdr *symsect = info->sechdrs + info->index.sym; Elf_Shdr *strsect = info->sechdrs + info->index.str; const Elf_Sym *src; - unsigned int i, nsrc, ndst, strtab_size; + unsigned int i, nsrc, ndst, strtab_size = 0; /* Put symbol section at end of init part of module. */ symsect->sh_flags |= SHF_ALLOC; @@ -2290,9 +2293,6 @@ static void layout_symtab(struct module *mod, struct load_info *info) src = (void *)info->hdr + symsect->sh_offset; nsrc = symsect->sh_size / sizeof(*src); - /* strtab always starts with a nul, so offset 0 is the empty string. */ - strtab_size = 1; - /* Compute total space required for the core symbols' strtab. */ for (ndst = i = 0; i < nsrc; i++) { if (i == 0 || @@ -2334,7 +2334,6 @@ static void add_kallsyms(struct module *mod, const struct load_info *info) mod->core_symtab = dst = mod->module_core + info->symoffs; mod->core_strtab = s = mod->module_core + info->stroffs; src = mod->symtab; - *s++ = 0; for (ndst = i = 0; i < mod->num_symtab; i++) { if (i == 0 || is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) { @@ -2375,7 +2374,7 @@ static void dynamic_debug_remove(struct _ddebug *debug) void * __weak module_alloc(unsigned long size) { - return size == 0 ? NULL : vmalloc_exec(size); + return vmalloc_exec(size); } static void *module_alloc_update_bounds(unsigned long size) @@ -2422,18 +2421,17 @@ static inline void kmemleak_load_module(const struct module *mod, #endif #ifdef CONFIG_MODULE_SIG -static int module_sig_check(struct load_info *info, - const void *mod, unsigned long *_len) +static int module_sig_check(struct load_info *info) { int err = -ENOKEY; - unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; - unsigned long len = *_len; + const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; + const void *mod = info->hdr; - if (len > markerlen && - memcmp(mod + len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { + if (info->len > markerlen && + memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { /* We truncate the module to discard the signature */ - *_len -= markerlen; - err = mod_verify_sig(mod, _len); + info->len -= markerlen; + err = mod_verify_sig(mod, &info->len); } if (!err) { @@ -2451,59 +2449,107 @@ static int module_sig_check(struct load_info *info, return err; } #else /* !CONFIG_MODULE_SIG */ -static int module_sig_check(struct load_info *info, - void *mod, unsigned long *len) +static int module_sig_check(struct load_info *info) { return 0; } #endif /* !CONFIG_MODULE_SIG */ -/* Sets info->hdr, info->len and info->sig_ok. */ -static int copy_and_check(struct load_info *info, - const void __user *umod, unsigned long len, - const char __user *uargs) +/* Sanity checks against invalid binaries, wrong arch, weird elf version. */ +static int elf_header_check(struct load_info *info) +{ + if (info->len < sizeof(*(info->hdr))) + return -ENOEXEC; + + if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0 + || info->hdr->e_type != ET_REL + || !elf_check_arch(info->hdr) + || info->hdr->e_shentsize != sizeof(Elf_Shdr)) + return -ENOEXEC; + + if (info->hdr->e_shoff >= info->len + || (info->hdr->e_shnum * sizeof(Elf_Shdr) > + info->len - info->hdr->e_shoff)) + return -ENOEXEC; + + return 0; +} + +/* Sets info->hdr and info->len. */ +static int copy_module_from_user(const void __user *umod, unsigned long len, + struct load_info *info) { int err; - Elf_Ehdr *hdr; - if (len < sizeof(*hdr)) + info->len = len; + if (info->len < sizeof(*(info->hdr))) return -ENOEXEC; + err = security_kernel_module_from_file(NULL); + if (err) + return err; + /* Suck in entire file: we'll want most of it. */ - if ((hdr = vmalloc(len)) == NULL) + info->hdr = vmalloc(info->len); + if (!info->hdr) return -ENOMEM; - if (copy_from_user(hdr, umod, len) != 0) { - err = -EFAULT; - goto free_hdr; + if (copy_from_user(info->hdr, umod, info->len) != 0) { + vfree(info->hdr); + return -EFAULT; } - err = module_sig_check(info, hdr, &len); + return 0; +} + +/* Sets info->hdr and info->len. */ +static int copy_module_from_fd(int fd, struct load_info *info) +{ + struct file *file; + int err; + struct kstat stat; + loff_t pos; + ssize_t bytes = 0; + + file = fget(fd); + if (!file) + return -ENOEXEC; + + err = security_kernel_module_from_file(file); if (err) - goto free_hdr; + goto out; - /* Sanity checks against insmoding binaries or wrong arch, - weird elf version */ - if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0 - || hdr->e_type != ET_REL - || !elf_check_arch(hdr) - || hdr->e_shentsize != sizeof(Elf_Shdr)) { - err = -ENOEXEC; - goto free_hdr; - } + err = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat); + if (err) + goto out; - if (hdr->e_shoff >= len || - hdr->e_shnum * sizeof(Elf_Shdr) > len - hdr->e_shoff) { - err = -ENOEXEC; - goto free_hdr; + if (stat.size > INT_MAX) { + err = -EFBIG; + goto out; + } + info->hdr = vmalloc(stat.size); + if (!info->hdr) { + err = -ENOMEM; + goto out; } - info->hdr = hdr; - info->len = len; - return 0; + pos = 0; + while (pos < stat.size) { + bytes = kernel_read(file, pos, (char *)(info->hdr) + pos, + stat.size - pos); + if (bytes < 0) { + vfree(info->hdr); + err = bytes; + goto out; + } + if (bytes == 0) + break; + pos += bytes; + } + info->len = pos; -free_hdr: - vfree(hdr); +out: + fput(file); return err; } @@ -2512,7 +2558,7 @@ static void free_copy(struct load_info *info) vfree(info->hdr); } -static int rewrite_section_headers(struct load_info *info) +static int rewrite_section_headers(struct load_info *info, int flags) { unsigned int i; @@ -2540,7 +2586,10 @@ static int rewrite_section_headers(struct load_info *info) } /* Track but don't keep modinfo and version sections. */ - info->index.vers = find_sec(info, "__versions"); + if (flags & MODULE_INIT_IGNORE_MODVERSIONS) + info->index.vers = 0; /* Pretend no __versions section! */ + else + info->index.vers = find_sec(info, "__versions"); info->index.info = find_sec(info, ".modinfo"); info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC; info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC; @@ -2555,7 +2604,7 @@ static int rewrite_section_headers(struct load_info *info) * Return the temporary module pointer (we'll replace it with the final * one when we move the module sections around). */ -static struct module *setup_load_info(struct load_info *info) +static struct module *setup_load_info(struct load_info *info, int flags) { unsigned int i; int err; @@ -2566,7 +2615,7 @@ static struct module *setup_load_info(struct load_info *info) info->secstrings = (void *)info->hdr + info->sechdrs[info->hdr->e_shstrndx].sh_offset; - err = rewrite_section_headers(info); + err = rewrite_section_headers(info, flags); if (err) return ERR_PTR(err); @@ -2604,11 +2653,14 @@ static struct module *setup_load_info(struct load_info *info) return mod; } -static int check_modinfo(struct module *mod, struct load_info *info) +static int check_modinfo(struct module *mod, struct load_info *info, int flags) { const char *modmagic = get_modinfo(info, "vermagic"); int err; + if (flags & MODULE_INIT_IGNORE_VERMAGIC) + modmagic = NULL; + /* This is allowed: modprobe --force will invalidate it. */ if (!modmagic) { err = try_to_force_load(mod, "bad vermagic"); @@ -2738,20 +2790,23 @@ static int move_module(struct module *mod, struct load_info *info) memset(ptr, 0, mod->core_size); mod->module_core = ptr; - ptr = module_alloc_update_bounds(mod->init_size); - /* - * The pointer to this block is stored in the module structure - * which is inside the block. This block doesn't need to be - * scanned as it contains data and code that will be freed - * after the module is initialized. - */ - kmemleak_ignore(ptr); - if (!ptr && mod->init_size) { - module_free(mod, mod->module_core); - return -ENOMEM; - } - memset(ptr, 0, mod->init_size); - mod->module_init = ptr; + if (mod->init_size) { + ptr = module_alloc_update_bounds(mod->init_size); + /* + * The pointer to this block is stored in the module structure + * which is inside the block. This block doesn't need to be + * scanned as it contains data and code that will be freed + * after the module is initialized. + */ + kmemleak_ignore(ptr); + if (!ptr) { + module_free(mod, mod->module_core); + return -ENOMEM; + } + memset(ptr, 0, mod->init_size); + mod->module_init = ptr; + } else + mod->module_init = NULL; /* Transfer each section which specifies SHF_ALLOC */ pr_debug("final section addresses:\n"); @@ -2844,18 +2899,18 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr, return 0; } -static struct module *layout_and_allocate(struct load_info *info) +static struct module *layout_and_allocate(struct load_info *info, int flags) { /* Module within temporary copy. */ struct module *mod; Elf_Shdr *pcpusec; int err; - mod = setup_load_info(info); + mod = setup_load_info(info, flags); if (IS_ERR(mod)) return mod; - err = check_modinfo(mod, info); + err = check_modinfo(mod, info, flags); if (err) return ERR_PTR(err); @@ -2942,33 +2997,124 @@ static bool finished_loading(const char *name) return ret; } +/* Call module constructors. */ +static void do_mod_ctors(struct module *mod) +{ +#ifdef CONFIG_CONSTRUCTORS + unsigned long i; + + for (i = 0; i < mod->num_ctors; i++) + mod->ctors[i](); +#endif +} + +/* This is where the real work happens */ +static int do_init_module(struct module *mod) +{ + int ret = 0; + + blocking_notifier_call_chain(&module_notify_list, + MODULE_STATE_COMING, mod); + + /* Set RO and NX regions for core */ + set_section_ro_nx(mod->module_core, + mod->core_text_size, + mod->core_ro_size, + mod->core_size); + + /* Set RO and NX regions for init */ + set_section_ro_nx(mod->module_init, + mod->init_text_size, + mod->init_ro_size, + mod->init_size); + + do_mod_ctors(mod); + /* Start the module */ + if (mod->init != NULL) + ret = do_one_initcall(mod->init); + if (ret < 0) { + /* Init routine failed: abort. Try to protect us from + buggy refcounters. */ + mod->state = MODULE_STATE_GOING; + synchronize_sched(); + module_put(mod); + blocking_notifier_call_chain(&module_notify_list, + MODULE_STATE_GOING, mod); + free_module(mod); + wake_up_all(&module_wq); + return ret; + } + if (ret > 0) { + printk(KERN_WARNING +"%s: '%s'->init suspiciously returned %d, it should follow 0/-E convention\n" +"%s: loading module anyway...\n", + __func__, mod->name, ret, + __func__); + dump_stack(); + } + + /* Now it's a first class citizen! */ + mod->state = MODULE_STATE_LIVE; + blocking_notifier_call_chain(&module_notify_list, + MODULE_STATE_LIVE, mod); + + /* We need to finish all async code before the module init sequence is done */ + async_synchronize_full(); + + mutex_lock(&module_mutex); + /* Drop initial reference. */ + module_put(mod); + trim_init_extable(mod); +#ifdef CONFIG_KALLSYMS + mod->num_symtab = mod->core_num_syms; + mod->symtab = mod->core_symtab; + mod->strtab = mod->core_strtab; +#endif + unset_module_init_ro_nx(mod); + module_free(mod, mod->module_init); + mod->module_init = NULL; + mod->init_size = 0; + mod->init_ro_size = 0; + mod->init_text_size = 0; + mutex_unlock(&module_mutex); + wake_up_all(&module_wq); + + return 0; +} + +static int may_init_module(void) +{ + if (!capable(CAP_SYS_MODULE) || modules_disabled) + return -EPERM; + + return 0; +} + /* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */ -static struct module *load_module(void __user *umod, - unsigned long len, - const char __user *uargs) +static int load_module(struct load_info *info, const char __user *uargs, + int flags) { - struct load_info info = { NULL, }; struct module *mod, *old; long err; - pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n", - umod, len, uargs); + err = module_sig_check(info); + if (err) + goto free_copy; - /* Copy in the blobs from userspace, check they are vaguely sane. */ - err = copy_and_check(&info, umod, len, uargs); + err = elf_header_check(info); if (err) - return ERR_PTR(err); + goto free_copy; /* Figure out module layout, and allocate all the memory. */ - mod = layout_and_allocate(&info); + mod = layout_and_allocate(info, flags); if (IS_ERR(mod)) { err = PTR_ERR(mod); goto free_copy; } #ifdef CONFIG_MODULE_SIG - mod->sig_ok = info.sig_ok; + mod->sig_ok = info->sig_ok; if (!mod->sig_ok) add_taint_module(mod, TAINT_FORCED_MODULE); #endif @@ -2980,25 +3126,25 @@ static struct module *load_module(void __user *umod, /* Now we've got everything in the final locations, we can * find optional sections. */ - find_module_sections(mod, &info); + find_module_sections(mod, info); err = check_module_license_and_versions(mod); if (err) goto free_unload; /* Set up MODINFO_ATTR fields */ - setup_modinfo(mod, &info); + setup_modinfo(mod, info); /* Fix up syms, so that st_value is a pointer to location. */ - err = simplify_symbols(mod, &info); + err = simplify_symbols(mod, info); if (err < 0) goto free_modinfo; - err = apply_relocations(mod, &info); + err = apply_relocations(mod, info); if (err < 0) goto free_modinfo; - err = post_relocation(mod, &info); + err = post_relocation(mod, info); if (err < 0) goto free_modinfo; @@ -3038,14 +3184,14 @@ again: } /* This has to be done once we're sure module name is unique. */ - dynamic_debug_setup(info.debug, info.num_debug); + dynamic_debug_setup(info->debug, info->num_debug); /* Find duplicate symbols */ err = verify_export_symbols(mod); if (err < 0) goto ddebug; - module_bug_finalize(info.hdr, info.sechdrs, mod); + module_bug_finalize(info->hdr, info->sechdrs, mod); list_add_rcu(&mod->list, &modules); mutex_unlock(&module_mutex); @@ -3056,16 +3202,17 @@ again: goto unlink; /* Link in to syfs. */ - err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp); + err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp); if (err < 0) goto unlink; /* Get rid of temporary copy. */ - free_copy(&info); + free_copy(info); /* Done! */ trace_module_load(mod); - return mod; + + return do_init_module(mod); unlink: mutex_lock(&module_mutex); @@ -3074,7 +3221,7 @@ again: module_bug_cleanup(mod); wake_up_all(&module_wq); ddebug: - dynamic_debug_remove(info.debug); + dynamic_debug_remove(info->debug); unlock: mutex_unlock(&module_mutex); synchronize_sched(); @@ -3086,106 +3233,52 @@ again: free_unload: module_unload_free(mod); free_module: - module_deallocate(mod, &info); + module_deallocate(mod, info); free_copy: - free_copy(&info); - return ERR_PTR(err); -} - -/* Call module constructors. */ -static void do_mod_ctors(struct module *mod) -{ -#ifdef CONFIG_CONSTRUCTORS - unsigned long i; - - for (i = 0; i < mod->num_ctors; i++) - mod->ctors[i](); -#endif + free_copy(info); + return err; } -/* This is where the real work happens */ SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs) { - struct module *mod; - int ret = 0; + int err; + struct load_info info = { }; - /* Must have permission */ - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; + err = may_init_module(); + if (err) + return err; - /* Do all the hard work */ - mod = load_module(umod, len, uargs); - if (IS_ERR(mod)) - return PTR_ERR(mod); + pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n", + umod, len, uargs); - blocking_notifier_call_chain(&module_notify_list, - MODULE_STATE_COMING, mod); + err = copy_module_from_user(umod, len, &info); + if (err) + return err; - /* Set RO and NX regions for core */ - set_section_ro_nx(mod->module_core, - mod->core_text_size, - mod->core_ro_size, - mod->core_size); + return load_module(&info, uargs, 0); +} - /* Set RO and NX regions for init */ - set_section_ro_nx(mod->module_init, - mod->init_text_size, - mod->init_ro_size, - mod->init_size); +SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) +{ + int err; + struct load_info info = { }; - do_mod_ctors(mod); - /* Start the module */ - if (mod->init != NULL) - ret = do_one_initcall(mod->init); - if (ret < 0) { - /* Init routine failed: abort. Try to protect us from - buggy refcounters. */ - mod->state = MODULE_STATE_GOING; - synchronize_sched(); - module_put(mod); - blocking_notifier_call_chain(&module_notify_list, - MODULE_STATE_GOING, mod); - free_module(mod); - wake_up_all(&module_wq); - return ret; - } - if (ret > 0) { - printk(KERN_WARNING -"%s: '%s'->init suspiciously returned %d, it should follow 0/-E convention\n" -"%s: loading module anyway...\n", - __func__, mod->name, ret, - __func__); - dump_stack(); - } + err = may_init_module(); + if (err) + return err; - /* Now it's a first class citizen! */ - mod->state = MODULE_STATE_LIVE; - blocking_notifier_call_chain(&module_notify_list, - MODULE_STATE_LIVE, mod); + pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); - /* We need to finish all async code before the module init sequence is done */ - async_synchronize_full(); + if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS + |MODULE_INIT_IGNORE_VERMAGIC)) + return -EINVAL; - mutex_lock(&module_mutex); - /* Drop initial reference. */ - module_put(mod); - trim_init_extable(mod); -#ifdef CONFIG_KALLSYMS - mod->num_symtab = mod->core_num_syms; - mod->symtab = mod->core_symtab; - mod->strtab = mod->core_strtab; -#endif - unset_module_init_ro_nx(mod); - module_free(mod, mod->module_init); - mod->module_init = NULL; - mod->init_size = 0; - mod->init_ro_size = 0; - mod->init_text_size = 0; - mutex_unlock(&module_mutex); - wake_up_all(&module_wq); + err = copy_module_from_fd(fd, &info); + if (err) + return err; - return 0; + return load_module(&info, uargs, flags); } static inline int within(unsigned long addr, void *start, unsigned long size) diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index d73840271dce..a278cad1d5d6 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -9,6 +9,7 @@ #include <asm/uaccess.h> #include <linux/kernel_stat.h> #include <trace/events/timer.h> +#include <linux/random.h> /* * Called after updating RLIMIT_CPU to run cpu timer and update @@ -470,6 +471,8 @@ static void cleanup_timers(struct list_head *head, */ void posix_cpu_timers_exit(struct task_struct *tsk) { + add_device_randomness((const void*) &tsk->se.sum_exec_runtime, + sizeof(unsigned long long)); cleanup_timers(tsk->cpu_timers, tsk->utime, tsk->stime, tsk->se.sum_exec_runtime); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index dbff751e4086..395084d4ce16 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -25,6 +25,7 @@ cond_syscall(sys_swapoff); cond_syscall(sys_kexec_load); cond_syscall(compat_sys_kexec_load); cond_syscall(sys_init_module); +cond_syscall(sys_finit_module); cond_syscall(sys_delete_module); cond_syscall(sys_socketpair); cond_syscall(sys_bind); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 997c6a16ec22..75a2ab3d0b02 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -344,6 +344,10 @@ static void watchdog_enable(unsigned int cpu) { struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); + /* kick off the timer for the hardlockup detector */ + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = watchdog_timer_fn; + if (!watchdog_enabled) { kthread_park(current); return; @@ -352,10 +356,6 @@ static void watchdog_enable(unsigned int cpu) /* Enable the perf event */ watchdog_nmi_enable(cpu); - /* kick off the timer for the hardlockup detector */ - hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer->function = watchdog_timer_fn; - /* done here because hrtimer_start can only pin to smp_processor_id() */ hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL_PINNED); @@ -369,9 +369,6 @@ static void watchdog_disable(unsigned int cpu) { struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); - if (!watchdog_enabled) - return; - watchdog_set_prio(SCHED_NORMAL, 0); hrtimer_cancel(hrtimer); /* disable the perf event */ diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c index 5293d2433029..11b9b01fda6b 100644 --- a/lib/asn1_decoder.c +++ b/lib/asn1_decoder.c @@ -81,7 +81,7 @@ next_tag: goto next_tag; } - if (unlikely((tag & 0x1f) == 0x1f)) { + if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) { do { if (unlikely(datalen - dp < 2)) goto data_overrun_error; @@ -96,7 +96,7 @@ next_tag: goto next_tag; } - if (unlikely(len == 0x80)) { + if (unlikely(len == ASN1_INDEFINITE_LENGTH)) { /* Indefinite length */ if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5)) goto indefinite_len_primitive; @@ -222,7 +222,7 @@ next_op: if (unlikely(dp >= datalen - 1)) goto data_overrun_error; tag = data[dp++]; - if (unlikely((tag & 0x1f) == 0x1f)) + if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) goto long_tag_not_supported; if (op & ASN1_OP_MATCH__ANY) { @@ -254,7 +254,7 @@ next_op: len = data[dp++]; if (len > 0x7f) { - if (unlikely(len == 0x80)) { + if (unlikely(len == ASN1_INDEFINITE_LENGTH)) { /* Indefinite length */ if (unlikely(!(tag & ASN1_CONS_BIT))) goto indefinite_len_primitive; diff --git a/scripts/Makefile.modsign b/scripts/Makefile.modsign new file mode 100644 index 000000000000..abfda626dbad --- /dev/null +++ b/scripts/Makefile.modsign @@ -0,0 +1,32 @@ +# ========================================================================== +# Signing modules +# ========================================================================== + +PHONY := __modsign +__modsign: + +include scripts/Kbuild.include + +__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) +modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o))) + +PHONY += $(modules) +__modsign: $(modules) + @: + +quiet_cmd_sign_ko = SIGN [M] $(2)/$(notdir $@) + cmd_sign_ko = $(mod_sign_cmd) $(2)/$(notdir $@) + +# Modules built outside the kernel source tree go into extra by default +INSTALL_MOD_DIR ?= extra +ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D)) + +modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) + +$(modules): + $(call cmd,sign_ko,$(MODLIB)/$(modinst_dir)) + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable se we can use it in if_changed and friends. + +.PHONY: $(PHONY) diff --git a/security/capability.c b/security/capability.c index b14a30c234b8..0fe5a026aef8 100644 --- a/security/capability.c +++ b/security/capability.c @@ -395,6 +395,11 @@ static int cap_kernel_module_request(char *kmod_name) return 0; } +static int cap_kernel_module_from_file(struct file *file) +{ + return 0; +} + static int cap_task_setpgid(struct task_struct *p, pid_t pgid) { return 0; @@ -967,6 +972,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, kernel_act_as); set_to_cap_if_null(ops, kernel_create_files_as); set_to_cap_if_null(ops, kernel_module_request); + set_to_cap_if_null(ops, kernel_module_from_file); set_to_cap_if_null(ops, task_fix_setuid); set_to_cap_if_null(ops, task_setpgid); set_to_cap_if_null(ops, task_getpgid); diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6ee8826662cc..3b2adb794f15 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -127,7 +127,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR }; +enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index b356884fb3ef..0cea3db21657 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -100,12 +100,12 @@ err_out: * ima_get_action - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) - * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) + * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP, MODULE_CHECK) * * The policy is defined in terms of keypairs: * subj=, obj=, type=, func=, mask=, fsmagic= * subj,obj, and type: are LSM specific. - * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP + * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK * mask: contains the permission mask * fsmagic: hex value * diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 73c9a268253e..45de18e9a6f2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -280,6 +280,27 @@ int ima_file_check(struct file *file, int mask) } EXPORT_SYMBOL_GPL(ima_file_check); +/** + * ima_module_check - based on policy, collect/store/appraise measurement. + * @file: pointer to the file to be measured/appraised + * + * Measure/appraise kernel modules based on policy. + * + * Always return 0 and audit dentry_open failures. + * Return code is based upon measurement appraisal. + */ +int ima_module_check(struct file *file) +{ + int rc; + + if (!file) + rc = INTEGRITY_UNKNOWN; + else + rc = process_measurement(file, file->f_dentry->d_name.name, + MAY_EXEC, MODULE_CHECK); + return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; +} + static int __init init_ima(void) { int error; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index c7dacd2eab7a..af7d182d5a46 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -80,6 +80,7 @@ static struct ima_rule_entry default_rules[] = { .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, + {.action = MEASURE,.func = MODULE_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -401,6 +402,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /* PATH_CHECK is for backwards compat */ else if (strcmp(args[0].from, "PATH_CHECK") == 0) entry->func = FILE_CHECK; + else if (strcmp(args[0].from, "MODULE_CHECK") == 0) + entry->func = MODULE_CHECK; else if (strcmp(args[0].from, "FILE_MMAP") == 0) entry->func = FILE_MMAP; else if (strcmp(args[0].from, "BPRM_CHECK") == 0) diff --git a/security/security.c b/security/security.c index 8dcd4ae10a5f..daa97f4ac9d1 100644 --- a/security/security.c +++ b/security/security.c @@ -820,6 +820,16 @@ int security_kernel_module_request(char *kmod_name) return security_ops->kernel_module_request(kmod_name); } +int security_kernel_module_from_file(struct file *file) +{ + int ret; + + ret = security_ops->kernel_module_from_file(file); + if (ret) + return ret; + return ima_module_check(file); +} + int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags) { |