kernel: remove linux 4.3 support
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 48955
This commit is contained in:
		| @@ -27,7 +27,7 @@ define KernelPackage/crypto-aead | ||||
| 	CONFIG_CRYPTO_AEAD2 | ||||
|   FILES:=$(LINUX_DIR)/crypto/aead.ko | ||||
|   AUTOLOAD:=$(call AutoLoad,09,aead,1) | ||||
|   $(call AddDepends/crypto, +LINUX_4_3:kmod-crypto-null +LINUX_4_4:kmod-crypto-null) | ||||
|   $(call AddDepends/crypto, +LINUX_4_4:kmod-crypto-null) | ||||
| endef | ||||
|  | ||||
| $(eval $(call KernelPackage,crypto-aead)) | ||||
|   | ||||
| @@ -1005,7 +1005,7 @@ $(eval $(call KernelPackage,rxrpc)) | ||||
| define KernelPackage/mpls | ||||
|   SUBMENU:=$(NETWORK_SUPPORT_MENU) | ||||
|   TITLE:=MPLS support | ||||
|   DEPENDS:=@!(LINUX_3_18||LINUX_4_1||LINUX_4_3) | ||||
|   DEPENDS:=@!(LINUX_3_18||LINUX_4_1) | ||||
|   KCONFIG:= \ | ||||
| 	CONFIG_MPLS=y \ | ||||
| 	CONFIG_LWTUNNEL=y \ | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| Upstream changed the default rootfs to tmpfs when none has been passed | ||||
| to the kernel - this doesn't fit our purposes, so change it back. | ||||
|  | ||||
| Signed-off-by: Imre Kaloz <kaloz@openwrt.org> | ||||
|  | ||||
| --- a/init/do_mounts.c | ||||
| +++ b/init/do_mounts.c | ||||
| @@ -633,6 +633,7 @@ int __init init_rootfs(void) | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| +#if 0 | ||||
|  	if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] && | ||||
|  		(!root_fs_names || strstr(root_fs_names, "tmpfs"))) { | ||||
|  		err = shmem_init(); | ||||
| @@ -640,6 +641,9 @@ int __init init_rootfs(void) | ||||
|  	} else { | ||||
|  		err = init_ramfs_fs(); | ||||
|  	} | ||||
| +#else | ||||
| +	err = init_ramfs_fs(); | ||||
| +#endif | ||||
|   | ||||
|  	if (err) | ||||
|  		unregister_filesystem(&rootfs_fs_type); | ||||
| @@ -1,44 +0,0 @@ | ||||
| commit 55acca90da52b85299c033354e51ddaa7b73e019 | ||||
| Author: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date:   Fri Sep 18 22:08:17 2015 +0200 | ||||
|  | ||||
|     brcmfmac: Add support for the BCM4365 and BCM4366 PCIE devices. | ||||
|      | ||||
|     This patch adds support for the BCM4365 and BCM4366 11ac Wave2 | ||||
|     PCIE devices. | ||||
|      | ||||
|     Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
|     Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
|     Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
|     Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
|     Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
|  | ||||
| --- a/include/linux/bcma/bcma.h | ||||
| +++ b/include/linux/bcma/bcma.h | ||||
| @@ -151,6 +151,8 @@ struct bcma_host_ops { | ||||
|  #define BCMA_CORE_PCIE2			0x83C	/* PCI Express Gen2 */ | ||||
|  #define BCMA_CORE_USB30_DEV		0x83D | ||||
|  #define BCMA_CORE_ARM_CR4		0x83E | ||||
| +#define BCMA_CORE_ARM_CA7		0x847 | ||||
| +#define BCMA_CORE_SYS_MEM		0x849 | ||||
|  #define BCMA_CORE_DEFAULT		0xFFF | ||||
|   | ||||
|  #define BCMA_MAX_NR_CORES		16 | ||||
| --- a/drivers/bcma/main.c | ||||
| +++ b/drivers/bcma/main.c | ||||
| @@ -436,13 +436,8 @@ int bcma_bus_register(struct bcma_bus *b | ||||
|  	} | ||||
|   | ||||
|  	dev = bcma_bus_get_host_dev(bus); | ||||
| -	/* TODO: remove check for IS_BUILTIN(CONFIG_BCMA) check when | ||||
| -	 * of_default_bus_match_table is exported or in some other way | ||||
| -	 * accessible. This is just a temporary workaround. | ||||
| -	 */ | ||||
| -	if (IS_BUILTIN(CONFIG_BCMA) && dev) { | ||||
| -		of_platform_populate(dev->of_node, of_default_bus_match_table, | ||||
| -				     NULL, dev); | ||||
| +	if (dev) { | ||||
| +		of_platform_default_populate(dev->of_node, NULL, dev); | ||||
|  	} | ||||
|   | ||||
|  	/* Cores providing flash access go before SPROM init */ | ||||
| @@ -1,686 +0,0 @@ | ||||
| --- a/arch/mips/bcm47xx/Kconfig | ||||
| +++ b/arch/mips/bcm47xx/Kconfig | ||||
| @@ -4,6 +4,7 @@ config BCM47XX_SSB | ||||
|  	bool "SSB Support for Broadcom BCM47XX" | ||||
|  	select SYS_HAS_CPU_BMIPS32_3300 | ||||
|  	select SSB | ||||
| +	select SSB_HOST_SOC | ||||
|  	select SSB_DRIVER_MIPS | ||||
|  	select SSB_DRIVER_EXTIF | ||||
|  	select SSB_EMBEDDED | ||||
| --- a/drivers/ssb/Kconfig | ||||
| +++ b/drivers/ssb/Kconfig | ||||
| @@ -80,6 +80,15 @@ config SSB_SDIOHOST | ||||
|   | ||||
|  	  If unsure, say N | ||||
|   | ||||
| +config SSB_HOST_SOC | ||||
| +	bool "Support for SSB bus on SoC" | ||||
| +	depends on SSB | ||||
| +	help | ||||
| +	  Host interface for a SSB directly mapped into memory. This is | ||||
| +	  for some Broadcom SoCs from the BCM47xx and BCM53xx lines. | ||||
| + | ||||
| +	  If unsure, say N | ||||
| + | ||||
|  config SSB_SILENT | ||||
|  	bool "No SSB kernel messages" | ||||
|  	depends on SSB && EXPERT | ||||
| --- a/drivers/ssb/Makefile | ||||
| +++ b/drivers/ssb/Makefile | ||||
| @@ -5,8 +5,9 @@ ssb-$(CONFIG_SSB_SPROM)			+= sprom.o | ||||
|   | ||||
|  # host support | ||||
|  ssb-$(CONFIG_SSB_PCIHOST)		+= pci.o pcihost_wrapper.o | ||||
| -ssb-$(CONFIG_SSB_PCMCIAHOST)		+= pcmcia.o | ||||
| +ssb-$(CONFIG_SSB_PCMCIAHOST)		+= pcmcia.o bridge_pcmcia_80211.o | ||||
|  ssb-$(CONFIG_SSB_SDIOHOST)		+= sdio.o | ||||
| +ssb-$(CONFIG_SSB_HOST_SOC)		+= host_soc.o | ||||
|   | ||||
|  # built-in drivers | ||||
|  ssb-y					+= driver_chipcommon.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/ssb/bridge_pcmcia_80211.c | ||||
| @@ -0,0 +1,128 @@ | ||||
| +/* | ||||
| + * Broadcom 43xx PCMCIA-SSB bridge module | ||||
| + * | ||||
| + * Copyright (c) 2007 Michael Buesch <m@bues.ch> | ||||
| + * | ||||
| + * Licensed under the GNU/GPL. See COPYING for details. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/ssb/ssb.h> | ||||
| +#include <linux/slab.h> | ||||
| +#include <linux/module.h> | ||||
| + | ||||
| +#include <pcmcia/cistpl.h> | ||||
| +#include <pcmcia/ciscode.h> | ||||
| +#include <pcmcia/ds.h> | ||||
| +#include <pcmcia/cisreg.h> | ||||
| + | ||||
| +#include "ssb_private.h" | ||||
| + | ||||
| +static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = { | ||||
| +	PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), | ||||
| +	PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476), | ||||
| +	PCMCIA_DEVICE_NULL, | ||||
| +}; | ||||
| + | ||||
| +MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl); | ||||
| + | ||||
| +static int ssb_host_pcmcia_probe(struct pcmcia_device *dev) | ||||
| +{ | ||||
| +	struct ssb_bus *ssb; | ||||
| +	int err = -ENOMEM; | ||||
| +	int res = 0; | ||||
| + | ||||
| +	ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); | ||||
| +	if (!ssb) | ||||
| +		goto out_error; | ||||
| + | ||||
| +	err = -ENODEV; | ||||
| + | ||||
| +	dev->config_flags |= CONF_ENABLE_IRQ; | ||||
| + | ||||
| +	dev->resource[2]->flags |=  WIN_ENABLE | WIN_DATA_WIDTH_16 | | ||||
| +			 WIN_USE_WAIT; | ||||
| +	dev->resource[2]->start = 0; | ||||
| +	dev->resource[2]->end = SSB_CORE_SIZE; | ||||
| +	res = pcmcia_request_window(dev, dev->resource[2], 250); | ||||
| +	if (res != 0) | ||||
| +		goto err_kfree_ssb; | ||||
| + | ||||
| +	res = pcmcia_map_mem_page(dev, dev->resource[2], 0); | ||||
| +	if (res != 0) | ||||
| +		goto err_disable; | ||||
| + | ||||
| +	if (!dev->irq) | ||||
| +		goto err_disable; | ||||
| + | ||||
| +	res = pcmcia_enable_device(dev); | ||||
| +	if (res != 0) | ||||
| +		goto err_disable; | ||||
| + | ||||
| +	err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start); | ||||
| +	if (err) | ||||
| +		goto err_disable; | ||||
| +	dev->priv = ssb; | ||||
| + | ||||
| +	return 0; | ||||
| + | ||||
| +err_disable: | ||||
| +	pcmcia_disable_device(dev); | ||||
| +err_kfree_ssb: | ||||
| +	kfree(ssb); | ||||
| +out_error: | ||||
| +	ssb_err("Initialization failed (%d, %d)\n", res, err); | ||||
| +	return err; | ||||
| +} | ||||
| + | ||||
| +static void ssb_host_pcmcia_remove(struct pcmcia_device *dev) | ||||
| +{ | ||||
| +	struct ssb_bus *ssb = dev->priv; | ||||
| + | ||||
| +	ssb_bus_unregister(ssb); | ||||
| +	pcmcia_disable_device(dev); | ||||
| +	kfree(ssb); | ||||
| +	dev->priv = NULL; | ||||
| +} | ||||
| + | ||||
| +#ifdef CONFIG_PM | ||||
| +static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev) | ||||
| +{ | ||||
| +	struct ssb_bus *ssb = dev->priv; | ||||
| + | ||||
| +	return ssb_bus_suspend(ssb); | ||||
| +} | ||||
| + | ||||
| +static int ssb_host_pcmcia_resume(struct pcmcia_device *dev) | ||||
| +{ | ||||
| +	struct ssb_bus *ssb = dev->priv; | ||||
| + | ||||
| +	return ssb_bus_resume(ssb); | ||||
| +} | ||||
| +#else /* CONFIG_PM */ | ||||
| +# define ssb_host_pcmcia_suspend		NULL | ||||
| +# define ssb_host_pcmcia_resume		NULL | ||||
| +#endif /* CONFIG_PM */ | ||||
| + | ||||
| +static struct pcmcia_driver ssb_host_pcmcia_driver = { | ||||
| +	.owner		= THIS_MODULE, | ||||
| +	.name		= "ssb-pcmcia", | ||||
| +	.id_table	= ssb_host_pcmcia_tbl, | ||||
| +	.probe		= ssb_host_pcmcia_probe, | ||||
| +	.remove		= ssb_host_pcmcia_remove, | ||||
| +	.suspend	= ssb_host_pcmcia_suspend, | ||||
| +	.resume		= ssb_host_pcmcia_resume, | ||||
| +}; | ||||
| + | ||||
| +/* | ||||
| + * These are not module init/exit functions! | ||||
| + * The module_pcmcia_driver() helper cannot be used here. | ||||
| + */ | ||||
| +int ssb_host_pcmcia_init(void) | ||||
| +{ | ||||
| +	return pcmcia_register_driver(&ssb_host_pcmcia_driver); | ||||
| +} | ||||
| + | ||||
| +void ssb_host_pcmcia_exit(void) | ||||
| +{ | ||||
| +	pcmcia_unregister_driver(&ssb_host_pcmcia_driver); | ||||
| +} | ||||
| --- /dev/null | ||||
| +++ b/drivers/ssb/host_soc.c | ||||
| @@ -0,0 +1,173 @@ | ||||
| +/* | ||||
| + * Sonics Silicon Backplane SoC host related functions. | ||||
| + * Subsystem core | ||||
| + * | ||||
| + * Copyright 2005, Broadcom Corporation | ||||
| + * Copyright 2006, 2007, Michael Buesch <m@bues.ch> | ||||
| + * | ||||
| + * Licensed under the GNU/GPL. See COPYING for details. | ||||
| + */ | ||||
| + | ||||
| +#include <linux/ssb/ssb.h> | ||||
| + | ||||
| +#include "ssb_private.h" | ||||
| + | ||||
| +static u8 ssb_host_soc_read8(struct ssb_device *dev, u16 offset) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	return readb(bus->mmio + offset); | ||||
| +} | ||||
| + | ||||
| +static u16 ssb_host_soc_read16(struct ssb_device *dev, u16 offset) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	return readw(bus->mmio + offset); | ||||
| +} | ||||
| + | ||||
| +static u32 ssb_host_soc_read32(struct ssb_device *dev, u16 offset) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	return readl(bus->mmio + offset); | ||||
| +} | ||||
| + | ||||
| +#ifdef CONFIG_SSB_BLOCKIO | ||||
| +static void ssb_host_soc_block_read(struct ssb_device *dev, void *buffer, | ||||
| +				    size_t count, u16 offset, u8 reg_width) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| +	void __iomem *addr; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	addr = bus->mmio + offset; | ||||
| + | ||||
| +	switch (reg_width) { | ||||
| +	case sizeof(u8): { | ||||
| +		u8 *buf = buffer; | ||||
| + | ||||
| +		while (count) { | ||||
| +			*buf = __raw_readb(addr); | ||||
| +			buf++; | ||||
| +			count--; | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +	case sizeof(u16): { | ||||
| +		__le16 *buf = buffer; | ||||
| + | ||||
| +		SSB_WARN_ON(count & 1); | ||||
| +		while (count) { | ||||
| +			*buf = (__force __le16)__raw_readw(addr); | ||||
| +			buf++; | ||||
| +			count -= 2; | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +	case sizeof(u32): { | ||||
| +		__le32 *buf = buffer; | ||||
| + | ||||
| +		SSB_WARN_ON(count & 3); | ||||
| +		while (count) { | ||||
| +			*buf = (__force __le32)__raw_readl(addr); | ||||
| +			buf++; | ||||
| +			count -= 4; | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +	default: | ||||
| +		SSB_WARN_ON(1); | ||||
| +	} | ||||
| +} | ||||
| +#endif /* CONFIG_SSB_BLOCKIO */ | ||||
| + | ||||
| +static void ssb_host_soc_write8(struct ssb_device *dev, u16 offset, u8 value) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	writeb(value, bus->mmio + offset); | ||||
| +} | ||||
| + | ||||
| +static void ssb_host_soc_write16(struct ssb_device *dev, u16 offset, u16 value) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	writew(value, bus->mmio + offset); | ||||
| +} | ||||
| + | ||||
| +static void ssb_host_soc_write32(struct ssb_device *dev, u16 offset, u32 value) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	writel(value, bus->mmio + offset); | ||||
| +} | ||||
| + | ||||
| +#ifdef CONFIG_SSB_BLOCKIO | ||||
| +static void ssb_host_soc_block_write(struct ssb_device *dev, const void *buffer, | ||||
| +				     size_t count, u16 offset, u8 reg_width) | ||||
| +{ | ||||
| +	struct ssb_bus *bus = dev->bus; | ||||
| +	void __iomem *addr; | ||||
| + | ||||
| +	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| +	addr = bus->mmio + offset; | ||||
| + | ||||
| +	switch (reg_width) { | ||||
| +	case sizeof(u8): { | ||||
| +		const u8 *buf = buffer; | ||||
| + | ||||
| +		while (count) { | ||||
| +			__raw_writeb(*buf, addr); | ||||
| +			buf++; | ||||
| +			count--; | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +	case sizeof(u16): { | ||||
| +		const __le16 *buf = buffer; | ||||
| + | ||||
| +		SSB_WARN_ON(count & 1); | ||||
| +		while (count) { | ||||
| +			__raw_writew((__force u16)(*buf), addr); | ||||
| +			buf++; | ||||
| +			count -= 2; | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +	case sizeof(u32): { | ||||
| +		const __le32 *buf = buffer; | ||||
| + | ||||
| +		SSB_WARN_ON(count & 3); | ||||
| +		while (count) { | ||||
| +			__raw_writel((__force u32)(*buf), addr); | ||||
| +			buf++; | ||||
| +			count -= 4; | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +	default: | ||||
| +		SSB_WARN_ON(1); | ||||
| +	} | ||||
| +} | ||||
| +#endif /* CONFIG_SSB_BLOCKIO */ | ||||
| + | ||||
| +/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */ | ||||
| +const struct ssb_bus_ops ssb_host_soc_ops = { | ||||
| +	.read8		= ssb_host_soc_read8, | ||||
| +	.read16		= ssb_host_soc_read16, | ||||
| +	.read32		= ssb_host_soc_read32, | ||||
| +	.write8		= ssb_host_soc_write8, | ||||
| +	.write16	= ssb_host_soc_write16, | ||||
| +	.write32	= ssb_host_soc_write32, | ||||
| +#ifdef CONFIG_SSB_BLOCKIO | ||||
| +	.block_read	= ssb_host_soc_block_read, | ||||
| +	.block_write	= ssb_host_soc_block_write, | ||||
| +#endif | ||||
| +}; | ||||
| --- a/drivers/ssb/main.c | ||||
| +++ b/drivers/ssb/main.c | ||||
| @@ -596,166 +596,6 @@ error: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	return readb(bus->mmio + offset); | ||||
| -} | ||||
| - | ||||
| -static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	return readw(bus->mmio + offset); | ||||
| -} | ||||
| - | ||||
| -static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	return readl(bus->mmio + offset); | ||||
| -} | ||||
| - | ||||
| -#ifdef CONFIG_SSB_BLOCKIO | ||||
| -static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer, | ||||
| -			       size_t count, u16 offset, u8 reg_width) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| -	void __iomem *addr; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	addr = bus->mmio + offset; | ||||
| - | ||||
| -	switch (reg_width) { | ||||
| -	case sizeof(u8): { | ||||
| -		u8 *buf = buffer; | ||||
| - | ||||
| -		while (count) { | ||||
| -			*buf = __raw_readb(addr); | ||||
| -			buf++; | ||||
| -			count--; | ||||
| -		} | ||||
| -		break; | ||||
| -	} | ||||
| -	case sizeof(u16): { | ||||
| -		__le16 *buf = buffer; | ||||
| - | ||||
| -		SSB_WARN_ON(count & 1); | ||||
| -		while (count) { | ||||
| -			*buf = (__force __le16)__raw_readw(addr); | ||||
| -			buf++; | ||||
| -			count -= 2; | ||||
| -		} | ||||
| -		break; | ||||
| -	} | ||||
| -	case sizeof(u32): { | ||||
| -		__le32 *buf = buffer; | ||||
| - | ||||
| -		SSB_WARN_ON(count & 3); | ||||
| -		while (count) { | ||||
| -			*buf = (__force __le32)__raw_readl(addr); | ||||
| -			buf++; | ||||
| -			count -= 4; | ||||
| -		} | ||||
| -		break; | ||||
| -	} | ||||
| -	default: | ||||
| -		SSB_WARN_ON(1); | ||||
| -	} | ||||
| -} | ||||
| -#endif /* CONFIG_SSB_BLOCKIO */ | ||||
| - | ||||
| -static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	writeb(value, bus->mmio + offset); | ||||
| -} | ||||
| - | ||||
| -static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	writew(value, bus->mmio + offset); | ||||
| -} | ||||
| - | ||||
| -static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	writel(value, bus->mmio + offset); | ||||
| -} | ||||
| - | ||||
| -#ifdef CONFIG_SSB_BLOCKIO | ||||
| -static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer, | ||||
| -				size_t count, u16 offset, u8 reg_width) | ||||
| -{ | ||||
| -	struct ssb_bus *bus = dev->bus; | ||||
| -	void __iomem *addr; | ||||
| - | ||||
| -	offset += dev->core_index * SSB_CORE_SIZE; | ||||
| -	addr = bus->mmio + offset; | ||||
| - | ||||
| -	switch (reg_width) { | ||||
| -	case sizeof(u8): { | ||||
| -		const u8 *buf = buffer; | ||||
| - | ||||
| -		while (count) { | ||||
| -			__raw_writeb(*buf, addr); | ||||
| -			buf++; | ||||
| -			count--; | ||||
| -		} | ||||
| -		break; | ||||
| -	} | ||||
| -	case sizeof(u16): { | ||||
| -		const __le16 *buf = buffer; | ||||
| - | ||||
| -		SSB_WARN_ON(count & 1); | ||||
| -		while (count) { | ||||
| -			__raw_writew((__force u16)(*buf), addr); | ||||
| -			buf++; | ||||
| -			count -= 2; | ||||
| -		} | ||||
| -		break; | ||||
| -	} | ||||
| -	case sizeof(u32): { | ||||
| -		const __le32 *buf = buffer; | ||||
| - | ||||
| -		SSB_WARN_ON(count & 3); | ||||
| -		while (count) { | ||||
| -			__raw_writel((__force u32)(*buf), addr); | ||||
| -			buf++; | ||||
| -			count -= 4; | ||||
| -		} | ||||
| -		break; | ||||
| -	} | ||||
| -	default: | ||||
| -		SSB_WARN_ON(1); | ||||
| -	} | ||||
| -} | ||||
| -#endif /* CONFIG_SSB_BLOCKIO */ | ||||
| - | ||||
| -/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */ | ||||
| -static const struct ssb_bus_ops ssb_ssb_ops = { | ||||
| -	.read8		= ssb_ssb_read8, | ||||
| -	.read16		= ssb_ssb_read16, | ||||
| -	.read32		= ssb_ssb_read32, | ||||
| -	.write8		= ssb_ssb_write8, | ||||
| -	.write16	= ssb_ssb_write16, | ||||
| -	.write32	= ssb_ssb_write32, | ||||
| -#ifdef CONFIG_SSB_BLOCKIO | ||||
| -	.block_read	= ssb_ssb_block_read, | ||||
| -	.block_write	= ssb_ssb_block_write, | ||||
| -#endif | ||||
| -}; | ||||
| - | ||||
|  static int ssb_fetch_invariants(struct ssb_bus *bus, | ||||
|  				ssb_invariants_func_t get_invariants) | ||||
|  { | ||||
| @@ -876,7 +716,6 @@ int ssb_bus_pcibus_register(struct ssb_b | ||||
|   | ||||
|  	return err; | ||||
|  } | ||||
| -EXPORT_SYMBOL(ssb_bus_pcibus_register); | ||||
|  #endif /* CONFIG_SSB_PCIHOST */ | ||||
|   | ||||
|  #ifdef CONFIG_SSB_PCMCIAHOST | ||||
| @@ -898,7 +737,6 @@ int ssb_bus_pcmciabus_register(struct ss | ||||
|   | ||||
|  	return err; | ||||
|  } | ||||
| -EXPORT_SYMBOL(ssb_bus_pcmciabus_register); | ||||
|  #endif /* CONFIG_SSB_PCMCIAHOST */ | ||||
|   | ||||
|  #ifdef CONFIG_SSB_SDIOHOST | ||||
| @@ -923,13 +761,14 @@ int ssb_bus_sdiobus_register(struct ssb_ | ||||
|  EXPORT_SYMBOL(ssb_bus_sdiobus_register); | ||||
|  #endif /* CONFIG_SSB_PCMCIAHOST */ | ||||
|   | ||||
| +#ifdef CONFIG_SSB_HOST_SOC | ||||
|  int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr, | ||||
|  			    ssb_invariants_func_t get_invariants) | ||||
|  { | ||||
|  	int err; | ||||
|   | ||||
|  	bus->bustype = SSB_BUSTYPE_SSB; | ||||
| -	bus->ops = &ssb_ssb_ops; | ||||
| +	bus->ops = &ssb_host_soc_ops; | ||||
|   | ||||
|  	err = ssb_bus_register(bus, get_invariants, baseaddr); | ||||
|  	if (!err) { | ||||
| @@ -939,6 +778,7 @@ int ssb_bus_ssbbus_register(struct ssb_b | ||||
|   | ||||
|  	return err; | ||||
|  } | ||||
| +#endif | ||||
|   | ||||
|  int __ssb_driver_register(struct ssb_driver *drv, struct module *owner) | ||||
|  { | ||||
| @@ -1465,6 +1305,12 @@ static int __init ssb_modinit(void) | ||||
|  		/* don't fail SSB init because of this */ | ||||
|  		err = 0; | ||||
|  	} | ||||
| +	err = ssb_host_pcmcia_init(); | ||||
| +	if (err) { | ||||
| +		ssb_err("PCMCIA host initialization failed\n"); | ||||
| +		/* don't fail SSB init because of this */ | ||||
| +		err = 0; | ||||
| +	} | ||||
|  	err = ssb_gige_init(); | ||||
|  	if (err) { | ||||
|  		ssb_err("SSB Broadcom Gigabit Ethernet driver initialization failed\n"); | ||||
| @@ -1482,6 +1328,7 @@ fs_initcall(ssb_modinit); | ||||
|  static void __exit ssb_modexit(void) | ||||
|  { | ||||
|  	ssb_gige_exit(); | ||||
| +	ssb_host_pcmcia_exit(); | ||||
|  	b43_pci_ssb_bridge_exit(); | ||||
|  	bus_unregister(&ssb_bustype); | ||||
|  } | ||||
| --- a/drivers/ssb/pcmcia.c | ||||
| +++ b/drivers/ssb/pcmcia.c | ||||
| @@ -147,8 +147,7 @@ error: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -int ssb_pcmcia_switch_core(struct ssb_bus *bus, | ||||
| -			   struct ssb_device *dev) | ||||
| +static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) | ||||
|  { | ||||
|  	int err; | ||||
|   | ||||
| --- a/drivers/ssb/sdio.c | ||||
| +++ b/drivers/ssb/sdio.c | ||||
| @@ -200,7 +200,7 @@ out: | ||||
|  } | ||||
|   | ||||
|  /* host must be already claimed */ | ||||
| -int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) | ||||
| +static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) | ||||
|  { | ||||
|  	u8 coreidx = dev->core_index; | ||||
|  	u32 sbaddr; | ||||
| --- a/drivers/ssb/ssb_private.h | ||||
| +++ b/drivers/ssb/ssb_private.h | ||||
| @@ -85,8 +85,6 @@ static inline int ssb_pci_init(struct ss | ||||
|   | ||||
|  /* pcmcia.c */ | ||||
|  #ifdef CONFIG_SSB_PCMCIAHOST | ||||
| -extern int ssb_pcmcia_switch_core(struct ssb_bus *bus, | ||||
| -				  struct ssb_device *dev); | ||||
|  extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | ||||
|  				     u8 coreidx); | ||||
|  extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, | ||||
| @@ -96,13 +94,10 @@ extern int ssb_pcmcia_get_invariants(str | ||||
|  extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus); | ||||
|  extern void ssb_pcmcia_exit(struct ssb_bus *bus); | ||||
|  extern int ssb_pcmcia_init(struct ssb_bus *bus); | ||||
| +extern int ssb_host_pcmcia_init(void); | ||||
| +extern void ssb_host_pcmcia_exit(void); | ||||
|  extern const struct ssb_bus_ops ssb_pcmcia_ops; | ||||
|  #else /* CONFIG_SSB_PCMCIAHOST */ | ||||
| -static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus, | ||||
| -					 struct ssb_device *dev) | ||||
| -{ | ||||
| -	return 0; | ||||
| -} | ||||
|  static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | ||||
|  					    u8 coreidx) | ||||
|  { | ||||
| @@ -124,6 +119,13 @@ static inline int ssb_pcmcia_init(struct | ||||
|  { | ||||
|  	return 0; | ||||
|  } | ||||
| +static inline int ssb_host_pcmcia_init(void) | ||||
| +{ | ||||
| +	return 0; | ||||
| +} | ||||
| +static inline void ssb_host_pcmcia_exit(void) | ||||
| +{ | ||||
| +} | ||||
|  #endif /* CONFIG_SSB_PCMCIAHOST */ | ||||
|   | ||||
|  /* sdio.c */ | ||||
| @@ -132,9 +134,7 @@ extern int ssb_sdio_get_invariants(struc | ||||
|  				     struct ssb_init_invariants *iv); | ||||
|   | ||||
|  extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset); | ||||
| -extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev); | ||||
|  extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx); | ||||
| -extern int ssb_sdio_hardware_setup(struct ssb_bus *bus); | ||||
|  extern void ssb_sdio_exit(struct ssb_bus *bus); | ||||
|  extern int ssb_sdio_init(struct ssb_bus *bus); | ||||
|   | ||||
| @@ -144,19 +144,10 @@ static inline u32 ssb_sdio_scan_read32(s | ||||
|  { | ||||
|  	return 0; | ||||
|  } | ||||
| -static inline int ssb_sdio_switch_core(struct ssb_bus *bus, | ||||
| -					 struct ssb_device *dev) | ||||
| -{ | ||||
| -	return 0; | ||||
| -} | ||||
|  static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) | ||||
|  { | ||||
|  	return 0; | ||||
|  } | ||||
| -static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus) | ||||
| -{ | ||||
| -	return 0; | ||||
| -} | ||||
|  static inline void ssb_sdio_exit(struct ssb_bus *bus) | ||||
|  { | ||||
|  } | ||||
| @@ -166,6 +157,13 @@ static inline int ssb_sdio_init(struct s | ||||
|  } | ||||
|  #endif /* CONFIG_SSB_SDIOHOST */ | ||||
|   | ||||
| +/************************************************** | ||||
| + * host_soc.c | ||||
| + **************************************************/ | ||||
| + | ||||
| +#ifdef CONFIG_SSB_HOST_SOC | ||||
| +extern const struct ssb_bus_ops ssb_host_soc_ops; | ||||
| +#endif | ||||
|   | ||||
|  /* scan.c */ | ||||
|  extern const char *ssb_core_name(u16 coreid); | ||||
| @@ -1,49 +0,0 @@ | ||||
| --- a/drivers/bcma/main.c | ||||
| +++ b/drivers/bcma/main.c | ||||
| @@ -668,11 +668,36 @@ static int bcma_device_uevent(struct dev | ||||
|  			      core->id.rev, core->id.class); | ||||
|  } | ||||
|   | ||||
| -static int __init bcma_modinit(void) | ||||
| +static unsigned int bcma_bus_registered; | ||||
| + | ||||
| +/* | ||||
| + * If built-in, bus has to be registered early, before any driver calls | ||||
| + * bcma_driver_register. | ||||
| + * Otherwise registering driver would trigger BUG in driver_register. | ||||
| + */ | ||||
| +static int __init bcma_init_bus_register(void) | ||||
|  { | ||||
|  	int err; | ||||
|   | ||||
| +	if (bcma_bus_registered) | ||||
| +		return 0; | ||||
| + | ||||
|  	err = bus_register(&bcma_bus_type); | ||||
| +	if (!err) | ||||
| +		bcma_bus_registered = 1; | ||||
| + | ||||
| +	return err; | ||||
| +} | ||||
| +#ifndef MODULE | ||||
| +fs_initcall(bcma_init_bus_register); | ||||
| +#endif | ||||
| + | ||||
| +/* Main initialization has to be done with SPI/mtd/NAND/SPROM available */ | ||||
| +static int __init bcma_modinit(void) | ||||
| +{ | ||||
| +	int err; | ||||
| + | ||||
| +	err = bcma_init_bus_register(); | ||||
|  	if (err) | ||||
|  		return err; | ||||
|   | ||||
| @@ -691,7 +716,7 @@ static int __init bcma_modinit(void) | ||||
|   | ||||
|  	return err; | ||||
|  } | ||||
| -fs_initcall(bcma_modinit); | ||||
| +module_init(bcma_modinit); | ||||
|   | ||||
|  static void __exit bcma_modexit(void) | ||||
|  { | ||||
| @@ -1,444 +0,0 @@ | ||||
| --- a/drivers/bcma/driver_chipcommon.c | ||||
| +++ b/drivers/bcma/driver_chipcommon.c | ||||
| @@ -15,6 +15,8 @@ | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/bcma/bcma.h> | ||||
|   | ||||
| +static void bcma_chipco_serial_init(struct bcma_drv_cc *cc); | ||||
| + | ||||
|  static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset, | ||||
|  					 u32 mask, u32 value) | ||||
|  { | ||||
| @@ -115,6 +117,8 @@ int bcma_chipco_watchdog_register(struct | ||||
|   | ||||
|  void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc) | ||||
|  { | ||||
| +	struct bcma_bus *bus = cc->core->bus; | ||||
| + | ||||
|  	if (cc->early_setup_done) | ||||
|  		return; | ||||
|   | ||||
| @@ -129,6 +133,9 @@ void bcma_core_chipcommon_early_init(str | ||||
|  	if (cc->capabilities & BCMA_CC_CAP_PMU) | ||||
|  		bcma_pmu_early_init(cc); | ||||
|   | ||||
| +	if (IS_BUILTIN(CONFIG_BCM47XX) && bus->hosttype == BCMA_HOSTTYPE_SOC) | ||||
| +		bcma_chipco_serial_init(cc); | ||||
| + | ||||
|  	cc->early_setup_done = true; | ||||
|  } | ||||
|   | ||||
| @@ -185,11 +192,12 @@ u32 bcma_chipco_watchdog_timer_set(struc | ||||
|  			ticks = 2; | ||||
|  		else if (ticks > maxt) | ||||
|  			ticks = maxt; | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks); | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks); | ||||
|  	} else { | ||||
|  		struct bcma_bus *bus = cc->core->bus; | ||||
|   | ||||
|  		if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4707 && | ||||
| +		    bus->chipinfo.id != BCMA_CHIP_ID_BCM47094 && | ||||
|  		    bus->chipinfo.id != BCMA_CHIP_ID_BCM53018) | ||||
|  			bcma_core_set_clockmode(cc->core, | ||||
|  						ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC); | ||||
| @@ -314,9 +322,9 @@ u32 bcma_chipco_gpio_pulldown(struct bcm | ||||
|  	return res; | ||||
|  } | ||||
|   | ||||
| -#ifdef CONFIG_BCMA_DRIVER_MIPS | ||||
| -void bcma_chipco_serial_init(struct bcma_drv_cc *cc) | ||||
| +static void bcma_chipco_serial_init(struct bcma_drv_cc *cc) | ||||
|  { | ||||
| +#if IS_BUILTIN(CONFIG_BCM47XX) | ||||
|  	unsigned int irq; | ||||
|  	u32 baud_base; | ||||
|  	u32 i; | ||||
| @@ -358,5 +366,5 @@ void bcma_chipco_serial_init(struct bcma | ||||
|  		ports[i].baud_base = baud_base; | ||||
|  		ports[i].reg_shift = 0; | ||||
|  	} | ||||
| +#endif /* CONFIG_BCM47XX */ | ||||
|  } | ||||
| -#endif /* CONFIG_BCMA_DRIVER_MIPS */ | ||||
| --- a/drivers/bcma/driver_chipcommon_pmu.c | ||||
| +++ b/drivers/bcma/driver_chipcommon_pmu.c | ||||
| @@ -15,44 +15,44 @@ | ||||
|   | ||||
|  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); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset); | ||||
| +	bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_ADDR); | ||||
| +	return bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_DATA); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_chipco_pll_read); | ||||
|   | ||||
|  void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) | ||||
|  { | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); | ||||
| -	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset); | ||||
| +	bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_ADDR); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, value); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_chipco_pll_write); | ||||
|   | ||||
|  void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, | ||||
|  			     u32 set) | ||||
|  { | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); | ||||
| -	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); | ||||
| -	bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset); | ||||
| +	bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_ADDR); | ||||
| +	bcma_pmu_maskset32(cc, BCMA_CC_PMU_PLLCTL_DATA, mask, set); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset); | ||||
|   | ||||
|  void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, | ||||
|  				 u32 offset, u32 mask, u32 set) | ||||
|  { | ||||
| -	bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset); | ||||
| -	bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); | ||||
| -	bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_CHIPCTL_ADDR, offset); | ||||
| +	bcma_pmu_read32(cc, BCMA_CC_PMU_CHIPCTL_ADDR); | ||||
| +	bcma_pmu_maskset32(cc, BCMA_CC_PMU_CHIPCTL_DATA, mask, set); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset); | ||||
|   | ||||
|  void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, | ||||
|  				u32 set) | ||||
|  { | ||||
| -	bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset); | ||||
| -	bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR); | ||||
| -	bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_REGCTL_ADDR, offset); | ||||
| +	bcma_pmu_read32(cc, BCMA_CC_PMU_REGCTL_ADDR); | ||||
| +	bcma_pmu_maskset32(cc, BCMA_CC_PMU_REGCTL_DATA, mask, set); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset); | ||||
|   | ||||
| @@ -60,18 +60,18 @@ static u32 bcma_pmu_xtalfreq(struct bcma | ||||
|  { | ||||
|  	u32 ilp_ctl, alp_hz; | ||||
|   | ||||
| -	if (!(bcma_cc_read32(cc, BCMA_CC_PMU_STAT) & | ||||
| +	if (!(bcma_pmu_read32(cc, BCMA_CC_PMU_STAT) & | ||||
|  	      BCMA_CC_PMU_STAT_EXT_LPO_AVAIL)) | ||||
|  		return 0; | ||||
|   | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PMU_XTAL_FREQ, | ||||
| -			BIT(BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT)); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_XTAL_FREQ, | ||||
| +			 BIT(BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT)); | ||||
|  	usleep_range(1000, 2000); | ||||
|   | ||||
| -	ilp_ctl = bcma_cc_read32(cc, BCMA_CC_PMU_XTAL_FREQ); | ||||
| +	ilp_ctl = bcma_pmu_read32(cc, BCMA_CC_PMU_XTAL_FREQ); | ||||
|  	ilp_ctl &= BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK; | ||||
|   | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PMU_XTAL_FREQ, 0); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_XTAL_FREQ, 0); | ||||
|   | ||||
|  	alp_hz = ilp_ctl * 32768 / 4; | ||||
|  	return (alp_hz + 50000) / 100000 * 100; | ||||
| @@ -127,8 +127,8 @@ static void bcma_pmu2_pll_init0(struct b | ||||
|  		mask = (u32)~(BCMA_RES_4314_HT_AVAIL | | ||||
|  			      BCMA_RES_4314_MACPHY_CLK_AVAIL); | ||||
|   | ||||
| -		bcma_cc_mask32(cc, BCMA_CC_PMU_MINRES_MSK, mask); | ||||
| -		bcma_cc_mask32(cc, BCMA_CC_PMU_MAXRES_MSK, mask); | ||||
| +		bcma_pmu_mask32(cc, BCMA_CC_PMU_MINRES_MSK, mask); | ||||
| +		bcma_pmu_mask32(cc, BCMA_CC_PMU_MAXRES_MSK, mask); | ||||
|  		bcma_wait_value(cc->core, BCMA_CLKCTLST, | ||||
|  				BCMA_CLKCTLST_HAVEHT, 0, 20000); | ||||
|  		break; | ||||
| @@ -140,7 +140,7 @@ static void bcma_pmu2_pll_init0(struct b | ||||
|   | ||||
|  	/* Flush */ | ||||
|  	if (cc->pmu.rev >= 2) | ||||
| -		bcma_cc_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD); | ||||
| +		bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD); | ||||
|   | ||||
|  	/* TODO: Do we need to update OTP? */ | ||||
|  } | ||||
| @@ -195,9 +195,9 @@ static void bcma_pmu_resources_init(stru | ||||
|   | ||||
|  	/* Set the resource masks. */ | ||||
|  	if (min_msk) | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk); | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk); | ||||
|  	if (max_msk) | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk); | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk); | ||||
|   | ||||
|  	/* | ||||
|  	 * Add some delay; allow resources to come up and settle. | ||||
| @@ -269,23 +269,33 @@ static void bcma_pmu_workarounds(struct | ||||
|   | ||||
|  void bcma_pmu_early_init(struct bcma_drv_cc *cc) | ||||
|  { | ||||
| +	struct bcma_bus *bus = cc->core->bus; | ||||
|  	u32 pmucap; | ||||
|   | ||||
| -	pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP); | ||||
| +	if (cc->core->id.rev >= 35 && | ||||
| +	    cc->capabilities_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) { | ||||
| +		cc->pmu.core = bcma_find_core(bus, BCMA_CORE_PMU); | ||||
| +		if (!cc->pmu.core) | ||||
| +			bcma_warn(bus, "Couldn't find expected PMU core"); | ||||
| +	} | ||||
| +	if (!cc->pmu.core) | ||||
| +		cc->pmu.core = cc->core; | ||||
| + | ||||
| +	pmucap = bcma_pmu_read32(cc, BCMA_CC_PMU_CAP); | ||||
|  	cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION); | ||||
|   | ||||
| -	bcma_debug(cc->core->bus, "Found rev %u PMU (capabilities 0x%08X)\n", | ||||
| -		   cc->pmu.rev, pmucap); | ||||
| +	bcma_debug(bus, "Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev, | ||||
| +		   pmucap); | ||||
|  } | ||||
|   | ||||
|  void bcma_pmu_init(struct bcma_drv_cc *cc) | ||||
|  { | ||||
|  	if (cc->pmu.rev == 1) | ||||
| -		bcma_cc_mask32(cc, BCMA_CC_PMU_CTL, | ||||
| -			      ~BCMA_CC_PMU_CTL_NOILPONW); | ||||
| +		bcma_pmu_mask32(cc, BCMA_CC_PMU_CTL, | ||||
| +				~BCMA_CC_PMU_CTL_NOILPONW); | ||||
|  	else | ||||
| -		bcma_cc_set32(cc, BCMA_CC_PMU_CTL, | ||||
| -			     BCMA_CC_PMU_CTL_NOILPONW); | ||||
| +		bcma_pmu_set32(cc, BCMA_CC_PMU_CTL, | ||||
| +			       BCMA_CC_PMU_CTL_NOILPONW); | ||||
|   | ||||
|  	bcma_pmu_pll_init(cc); | ||||
|  	bcma_pmu_resources_init(cc); | ||||
| @@ -472,8 +482,8 @@ u32 bcma_pmu_get_cpu_clock(struct bcma_d | ||||
|  static void bcma_pmu_spuravoid_pll_write(struct bcma_drv_cc *cc, u32 offset, | ||||
|  					 u32 value) | ||||
|  { | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, offset); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, value); | ||||
|  } | ||||
|   | ||||
|  void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid) | ||||
| @@ -497,20 +507,20 @@ void bcma_pmu_spuravoid_pllupdate(struct | ||||
|  		       bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0; | ||||
|   | ||||
|  		/* RMW only the P1 divider */ | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, | ||||
|  				BCMA_CC_PMU_PLL_CTL0 + phypll_offset); | ||||
| -		tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); | ||||
| +		tmp = bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_DATA); | ||||
|  		tmp &= (~(BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK)); | ||||
|  		tmp |= (bcm5357_bcm43236_p1div[spuravoid] << BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT); | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp); | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, tmp); | ||||
|   | ||||
|  		/* RMW only the int feedback divider */ | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_ADDR, | ||||
|  				BCMA_CC_PMU_PLL_CTL2 + phypll_offset); | ||||
| -		tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); | ||||
| +		tmp = bcma_pmu_read32(cc, BCMA_CC_PMU_PLLCTL_DATA); | ||||
|  		tmp &= ~(BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK); | ||||
|  		tmp |= (bcm5357_bcm43236_ndiv[spuravoid]) << BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT; | ||||
| -		bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp); | ||||
| +		bcma_pmu_write32(cc, BCMA_CC_PMU_PLLCTL_DATA, tmp); | ||||
|   | ||||
|  		tmp = BCMA_CC_PMU_CTL_PLL_UPD; | ||||
|  		break; | ||||
| @@ -646,7 +656,7 @@ void bcma_pmu_spuravoid_pllupdate(struct | ||||
|  		break; | ||||
|  	} | ||||
|   | ||||
| -	tmp |= bcma_cc_read32(cc, BCMA_CC_PMU_CTL); | ||||
| -	bcma_cc_write32(cc, BCMA_CC_PMU_CTL, tmp); | ||||
| +	tmp |= bcma_pmu_read32(cc, BCMA_CC_PMU_CTL); | ||||
| +	bcma_pmu_write32(cc, BCMA_CC_PMU_CTL, tmp); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_pmu_spuravoid_pllupdate); | ||||
| --- a/drivers/bcma/driver_chipcommon_sflash.c | ||||
| +++ b/drivers/bcma/driver_chipcommon_sflash.c | ||||
| @@ -38,6 +38,7 @@ static const struct bcma_sflash_tbl_e bc | ||||
|  	{ "M25P32", 0x15, 0x10000, 64, }, | ||||
|  	{ "M25P64", 0x16, 0x10000, 128, }, | ||||
|  	{ "M25FL128", 0x17, 0x10000, 256, }, | ||||
| +	{ "MX25L25635F", 0x18, 0x10000, 512, }, | ||||
|  	{ NULL }, | ||||
|  }; | ||||
|   | ||||
| --- a/drivers/bcma/scan.c | ||||
| +++ b/drivers/bcma/scan.c | ||||
| @@ -98,6 +98,9 @@ static const struct bcma_device_id_name | ||||
|  	{ BCMA_CORE_SHIM, "SHIM" }, | ||||
|  	{ BCMA_CORE_PCIE2, "PCIe Gen2" }, | ||||
|  	{ BCMA_CORE_ARM_CR4, "ARM CR4" }, | ||||
| +	{ BCMA_CORE_GCI, "GCI" }, | ||||
| +	{ BCMA_CORE_CMEM, "CNDS DDR2/3 memory controller" }, | ||||
| +	{ BCMA_CORE_ARM_CA7, "ARM CA7" }, | ||||
|  	{ BCMA_CORE_DEFAULT, "Default" }, | ||||
|  }; | ||||
|   | ||||
| @@ -315,6 +318,8 @@ static int bcma_get_next_core(struct bcm | ||||
|  		switch (core->id.id) { | ||||
|  		case BCMA_CORE_4706_MAC_GBIT_COMMON: | ||||
|  		case BCMA_CORE_NS_CHIPCOMMON_B: | ||||
| +		case BCMA_CORE_PMU: | ||||
| +		case BCMA_CORE_GCI: | ||||
|  		/* Not used yet: case BCMA_CORE_OOB_ROUTER: */ | ||||
|  			break; | ||||
|  		default: | ||||
| --- a/drivers/net/wireless/b43/main.c | ||||
| +++ b/drivers/net/wireless/b43/main.c | ||||
| @@ -1216,10 +1216,10 @@ void b43_wireless_core_phy_pll_reset(str | ||||
|  	case B43_BUS_BCMA: | ||||
|  		bcma_cc = &dev->dev->bdev->bus->drv_cc; | ||||
|   | ||||
| -		bcma_cc_write32(bcma_cc, BCMA_CC_CHIPCTL_ADDR, 0); | ||||
| -		bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); | ||||
| -		bcma_cc_set32(bcma_cc, BCMA_CC_CHIPCTL_DATA, 0x4); | ||||
| -		bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); | ||||
| +		bcma_cc_write32(bcma_cc, BCMA_CC_PMU_CHIPCTL_ADDR, 0); | ||||
| +		bcma_cc_mask32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, ~0x4); | ||||
| +		bcma_cc_set32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, 0x4); | ||||
| +		bcma_cc_mask32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, ~0x4); | ||||
|  		break; | ||||
|  #endif | ||||
|  #ifdef CONFIG_B43_SSB | ||||
| --- a/include/linux/bcma/bcma.h | ||||
| +++ b/include/linux/bcma/bcma.h | ||||
| @@ -151,6 +151,8 @@ struct bcma_host_ops { | ||||
|  #define BCMA_CORE_PCIE2			0x83C	/* PCI Express Gen2 */ | ||||
|  #define BCMA_CORE_USB30_DEV		0x83D | ||||
|  #define BCMA_CORE_ARM_CR4		0x83E | ||||
| +#define BCMA_CORE_GCI			0x840 | ||||
| +#define BCMA_CORE_CMEM			0x846	/* CNDS DDR2/3 memory controller */ | ||||
|  #define BCMA_CORE_ARM_CA7		0x847 | ||||
|  #define BCMA_CORE_SYS_MEM		0x849 | ||||
|  #define BCMA_CORE_DEFAULT		0xFFF | ||||
| @@ -199,6 +201,7 @@ struct bcma_host_ops { | ||||
|  #define  BCMA_PKG_ID_BCM4707	1 | ||||
|  #define  BCMA_PKG_ID_BCM4708	2 | ||||
|  #define  BCMA_PKG_ID_BCM4709	0 | ||||
| +#define BCMA_CHIP_ID_BCM47094	53030 | ||||
|  #define BCMA_CHIP_ID_BCM53018	53018 | ||||
|   | ||||
|  /* Board types (on PCI usually equals to the subsystem dev id) */ | ||||
| --- a/include/linux/bcma/bcma_driver_chipcommon.h | ||||
| +++ b/include/linux/bcma/bcma_driver_chipcommon.h | ||||
| @@ -217,6 +217,11 @@ | ||||
|  #define	 BCMA_CC_CLKDIV_JTAG_SHIFT	8 | ||||
|  #define	 BCMA_CC_CLKDIV_UART		0x000000FF | ||||
|  #define BCMA_CC_CAP_EXT			0x00AC		/* Capabilities */ | ||||
| +#define  BCMA_CC_CAP_EXT_SECI_PRESENT	0x00000001 | ||||
| +#define  BCMA_CC_CAP_EXT_GSIO_PRESENT	0x00000002 | ||||
| +#define  BCMA_CC_CAP_EXT_GCI_PRESENT	0x00000004 | ||||
| +#define  BCMA_CC_CAP_EXT_SECI_PUART_PRESENT		0x00000008    /* UART present */ | ||||
| +#define  BCMA_CC_CAP_EXT_AOB_PRESENT	0x00000040 | ||||
|  #define BCMA_CC_PLLONDELAY		0x00B0		/* Rev >= 4 only */ | ||||
|  #define BCMA_CC_FREFSELDELAY		0x00B4		/* Rev >= 4 only */ | ||||
|  #define BCMA_CC_SLOWCLKCTL		0x00B8		/* 6 <= Rev <= 9 only */ | ||||
| @@ -351,12 +356,12 @@ | ||||
|  #define BCMA_CC_PMU_RES_REQTS		0x0640 /* PMU res req timer sel */ | ||||
|  #define BCMA_CC_PMU_RES_REQT		0x0644 /* PMU res req timer */ | ||||
|  #define BCMA_CC_PMU_RES_REQM		0x0648 /* PMU res req mask */ | ||||
| -#define BCMA_CC_CHIPCTL_ADDR		0x0650 | ||||
| -#define BCMA_CC_CHIPCTL_DATA		0x0654 | ||||
| -#define BCMA_CC_REGCTL_ADDR		0x0658 | ||||
| -#define BCMA_CC_REGCTL_DATA		0x065C | ||||
| -#define BCMA_CC_PLLCTL_ADDR		0x0660 | ||||
| -#define BCMA_CC_PLLCTL_DATA		0x0664 | ||||
| +#define BCMA_CC_PMU_CHIPCTL_ADDR	0x0650 | ||||
| +#define BCMA_CC_PMU_CHIPCTL_DATA	0x0654 | ||||
| +#define BCMA_CC_PMU_REGCTL_ADDR		0x0658 | ||||
| +#define BCMA_CC_PMU_REGCTL_DATA		0x065C | ||||
| +#define BCMA_CC_PMU_PLLCTL_ADDR		0x0660 | ||||
| +#define BCMA_CC_PMU_PLLCTL_DATA		0x0664 | ||||
|  #define BCMA_CC_PMU_STRAPOPT		0x0668 /* (corerev >= 28) */ | ||||
|  #define BCMA_CC_PMU_XTAL_FREQ		0x066C /* (pmurev >= 10) */ | ||||
|  #define  BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK	0x00001FFF | ||||
| @@ -566,6 +571,7 @@ | ||||
|   * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) | ||||
|   */ | ||||
|  struct bcma_chipcommon_pmu { | ||||
| +	struct bcma_device *core;	/* Can be separated core or just ChipCommon one */ | ||||
|  	u8 rev;			/* PMU revision */ | ||||
|  	u32 crystalfreq;	/* The active crystal frequency (in kHz) */ | ||||
|  }; | ||||
| @@ -662,6 +668,19 @@ struct bcma_drv_cc_b { | ||||
|  #define bcma_cc_maskset32(cc, offset, mask, set) \ | ||||
|  	bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set)) | ||||
|   | ||||
| +/* PMU registers access */ | ||||
| +#define bcma_pmu_read32(cc, offset) \ | ||||
| +	bcma_read32((cc)->pmu.core, offset) | ||||
| +#define bcma_pmu_write32(cc, offset, val) \ | ||||
| +	bcma_write32((cc)->pmu.core, offset, val) | ||||
| + | ||||
| +#define bcma_pmu_mask32(cc, offset, mask) \ | ||||
| +	bcma_pmu_write32(cc, offset, bcma_pmu_read32(cc, offset) & (mask)) | ||||
| +#define bcma_pmu_set32(cc, offset, set) \ | ||||
| +	bcma_pmu_write32(cc, offset, bcma_pmu_read32(cc, offset) | (set)) | ||||
| +#define bcma_pmu_maskset32(cc, offset, mask, set) \ | ||||
| +	bcma_pmu_write32(cc, offset, (bcma_pmu_read32(cc, offset) & (mask)) | (set)) | ||||
| + | ||||
|  extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks); | ||||
|   | ||||
|  extern u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc); | ||||
| --- a/drivers/bcma/bcma_private.h | ||||
| +++ b/drivers/bcma/bcma_private.h | ||||
| @@ -48,7 +48,6 @@ void bcma_core_chipcommon_early_init(str | ||||
|  void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); | ||||
|  void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable); | ||||
|  #ifdef CONFIG_BCMA_DRIVER_MIPS | ||||
| -void bcma_chipco_serial_init(struct bcma_drv_cc *cc); | ||||
|  extern struct platform_device bcma_pflash_dev; | ||||
|  #endif /* CONFIG_BCMA_DRIVER_MIPS */ | ||||
|   | ||||
| --- a/drivers/bcma/driver_gpio.c | ||||
| +++ b/drivers/bcma/driver_gpio.c | ||||
| @@ -197,6 +197,7 @@ int bcma_gpio_init(struct bcma_drv_cc *c | ||||
|  	case BCMA_CHIP_ID_BCM4707: | ||||
|  	case BCMA_CHIP_ID_BCM5357: | ||||
|  	case BCMA_CHIP_ID_BCM53572: | ||||
| +	case BCMA_CHIP_ID_BCM47094: | ||||
|  		chip->ngpio	= 32; | ||||
|  		break; | ||||
|  	default: | ||||
| --- a/drivers/bcma/driver_mips.c | ||||
| +++ b/drivers/bcma/driver_mips.c | ||||
| @@ -337,12 +337,9 @@ static void bcma_core_mips_flash_detect( | ||||
|   | ||||
|  void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) | ||||
|  { | ||||
| -	struct bcma_bus *bus = mcore->core->bus; | ||||
| - | ||||
|  	if (mcore->early_setup_done) | ||||
|  		return; | ||||
|   | ||||
| -	bcma_chipco_serial_init(&bus->drv_cc); | ||||
|  	bcma_core_mips_flash_detect(mcore); | ||||
|   | ||||
|  	mcore->early_setup_done = true; | ||||
| --- a/drivers/bcma/host_pci.c | ||||
| +++ b/drivers/bcma/host_pci.c | ||||
| @@ -294,7 +294,7 @@ static const struct pci_device_id bcma_p | ||||
|  	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) }, | ||||
|  	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, | ||||
|  	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4360) }, | ||||
| -	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) }, | ||||
| +	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0016) }, | ||||
|  	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) }, | ||||
|  	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) }, | ||||
|  	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) }, | ||||
| @@ -1,505 +0,0 @@ | ||||
| Subject: netfilter: conntrack: cache route for forwarded connections | ||||
|  | ||||
| ... to avoid per-packet FIB lookup if possible. | ||||
|  | ||||
| The cached dst is re-used provided the input interface | ||||
| is the same as that of the previous packet in the same direction. | ||||
|  | ||||
| If not, the cached dst is invalidated. | ||||
|  | ||||
| For ipv6 we also need to store sernum, else dst_check doesn't work, | ||||
| pointed out by Eric Dumazet. | ||||
|  | ||||
| This should speed up forwarding when conntrack is already in use | ||||
| anyway, especially when using reverse path filtering -- active RPF | ||||
| enforces two FIB lookups for each packet. | ||||
|  | ||||
| Before the routing cache removal this didn't matter since RPF was performed | ||||
| only when route cache didn't yield a result; but without route cache it | ||||
| comes at higher price. | ||||
|  | ||||
| Julian Anastasov suggested to add NETDEV_UNREGISTER handler to | ||||
| avoid holding on to dsts of 'frozen' conntracks. | ||||
|  | ||||
| Signed-off-by: Florian Westphal <fw@strlen.de> | ||||
|  | ||||
| --- a/include/net/netfilter/nf_conntrack_extend.h | ||||
| +++ b/include/net/netfilter/nf_conntrack_extend.h | ||||
| @@ -30,6 +30,9 @@ enum nf_ct_ext_id { | ||||
|  #if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) | ||||
|  	NF_CT_EXT_SYNPROXY, | ||||
|  #endif | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE) | ||||
| +	NF_CT_EXT_RTCACHE, | ||||
| +#endif | ||||
|  	NF_CT_EXT_NUM, | ||||
|  }; | ||||
|   | ||||
| @@ -43,6 +46,7 @@ enum nf_ct_ext_id { | ||||
|  #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout | ||||
|  #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels | ||||
|  #define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy | ||||
| +#define NF_CT_EXT_RTCACHE_TYPE struct nf_conn_rtcache | ||||
|   | ||||
|  /* Extensions: optional stuff which isn't permanently in struct. */ | ||||
|  struct nf_ct_ext { | ||||
| --- /dev/null | ||||
| +++ b/include/net/netfilter/nf_conntrack_rtcache.h | ||||
| @@ -0,0 +1,34 @@ | ||||
| +#include <linux/gfp.h> | ||||
| +#include <net/netfilter/nf_conntrack.h> | ||||
| +#include <net/netfilter/nf_conntrack_extend.h> | ||||
| + | ||||
| +struct dst_entry; | ||||
| + | ||||
| +struct nf_conn_dst_cache { | ||||
| +	struct dst_entry *dst; | ||||
| +	int iif; | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) | ||||
| +	u32 cookie; | ||||
| +#endif | ||||
| + | ||||
| +}; | ||||
| + | ||||
| +struct nf_conn_rtcache { | ||||
| +	struct nf_conn_dst_cache cached_dst[IP_CT_DIR_MAX]; | ||||
| +}; | ||||
| + | ||||
| +static inline | ||||
| +struct nf_conn_rtcache *nf_ct_rtcache_find(const struct nf_conn *ct) | ||||
| +{ | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE) | ||||
| +	return nf_ct_ext_find(ct, NF_CT_EXT_RTCACHE); | ||||
| +#else | ||||
| +	return NULL; | ||||
| +#endif | ||||
| +} | ||||
| + | ||||
| +static inline int nf_conn_rtcache_iif_get(const struct nf_conn_rtcache *rtc, | ||||
| +					  enum ip_conntrack_dir dir) | ||||
| +{ | ||||
| +	return rtc->cached_dst[dir].iif; | ||||
| +} | ||||
| --- a/net/netfilter/Kconfig | ||||
| +++ b/net/netfilter/Kconfig | ||||
| @@ -114,6 +114,18 @@ config NF_CONNTRACK_EVENTS | ||||
|   | ||||
|  	  If unsure, say `N'. | ||||
|   | ||||
| +config NF_CONNTRACK_RTCACHE | ||||
| +	tristate "Cache route entries in conntrack objects" | ||||
| +	depends on NETFILTER_ADVANCED | ||||
| +	depends on NF_CONNTRACK | ||||
| +	help | ||||
| +	  If this option is enabled, the connection tracking code will | ||||
| +	  cache routing information for each connection that is being | ||||
| +	  forwarded, at a cost of 32 bytes per conntrack object. | ||||
| + | ||||
| +	  To compile it as a module, choose M here.  If unsure, say N. | ||||
| +	  The module will be called nf_conntrack_rtcache. | ||||
| + | ||||
|  config NF_CONNTRACK_TIMEOUT | ||||
|  	bool  'Connection tracking timeout' | ||||
|  	depends on NETFILTER_ADVANCED | ||||
| --- a/net/netfilter/Makefile | ||||
| +++ b/net/netfilter/Makefile | ||||
| @@ -18,6 +18,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += n | ||||
|  # connection tracking | ||||
|  obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o | ||||
|   | ||||
| +# optional conntrack route cache extension | ||||
| +obj-$(CONFIG_NF_CONNTRACK_RTCACHE) += nf_conntrack_rtcache.o | ||||
| + | ||||
|  # SCTP protocol connection tracking | ||||
|  obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o | ||||
|  obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o | ||||
| --- /dev/null | ||||
| +++ b/net/netfilter/nf_conntrack_rtcache.c | ||||
| @@ -0,0 +1,387 @@ | ||||
| +/* route cache for netfilter. | ||||
| + * | ||||
| + * (C) 2014 Red Hat GmbH | ||||
| + * | ||||
| + * This program is free software; you can redistribute it and/or modify | ||||
| + * it under the terms of the GNU General Public License version 2 as | ||||
| + * published by the Free Software Foundation. | ||||
| + */ | ||||
| + | ||||
| +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| + | ||||
| +#include <linux/types.h> | ||||
| +#include <linux/netfilter.h> | ||||
| +#include <linux/skbuff.h> | ||||
| +#include <linux/stddef.h> | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/netdevice.h> | ||||
| +#include <linux/export.h> | ||||
| +#include <linux/module.h> | ||||
| + | ||||
| +#include <net/dst.h> | ||||
| + | ||||
| +#include <net/netfilter/nf_conntrack.h> | ||||
| +#include <net/netfilter/nf_conntrack_core.h> | ||||
| +#include <net/netfilter/nf_conntrack_extend.h> | ||||
| +#include <net/netfilter/nf_conntrack_rtcache.h> | ||||
| + | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) | ||||
| +#include <net/ip6_fib.h> | ||||
| +#endif | ||||
| + | ||||
| +static void __nf_conn_rtcache_destroy(struct nf_conn_rtcache *rtc, | ||||
| +				      enum ip_conntrack_dir dir) | ||||
| +{ | ||||
| +	struct dst_entry *dst = rtc->cached_dst[dir].dst; | ||||
| + | ||||
| +	dst_release(dst); | ||||
| +} | ||||
| + | ||||
| +static void nf_conn_rtcache_destroy(struct nf_conn *ct) | ||||
| +{ | ||||
| +	struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct); | ||||
| + | ||||
| +	if (!rtc) | ||||
| +		return; | ||||
| + | ||||
| +	__nf_conn_rtcache_destroy(rtc, IP_CT_DIR_ORIGINAL); | ||||
| +	__nf_conn_rtcache_destroy(rtc, IP_CT_DIR_REPLY); | ||||
| +} | ||||
| + | ||||
| +static void nf_ct_rtcache_ext_add(struct nf_conn *ct) | ||||
| +{ | ||||
| +	struct nf_conn_rtcache *rtc; | ||||
| + | ||||
| +	rtc = nf_ct_ext_add(ct, NF_CT_EXT_RTCACHE, GFP_ATOMIC); | ||||
| +	if (rtc) { | ||||
| +		rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif = -1; | ||||
| +		rtc->cached_dst[IP_CT_DIR_ORIGINAL].dst = NULL; | ||||
| +		rtc->cached_dst[IP_CT_DIR_REPLY].iif = -1; | ||||
| +		rtc->cached_dst[IP_CT_DIR_REPLY].dst = NULL; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static struct nf_conn_rtcache *nf_ct_rtcache_find_usable(struct nf_conn *ct) | ||||
| +{ | ||||
| +	if (nf_ct_is_untracked(ct)) | ||||
| +		return NULL; | ||||
| +	return nf_ct_rtcache_find(ct); | ||||
| +} | ||||
| + | ||||
| +static struct dst_entry * | ||||
| +nf_conn_rtcache_dst_get(const struct nf_conn_rtcache *rtc, | ||||
| +			enum ip_conntrack_dir dir) | ||||
| +{ | ||||
| +	return rtc->cached_dst[dir].dst; | ||||
| +} | ||||
| + | ||||
| +static u32 nf_rtcache_get_cookie(int pf, const struct dst_entry *dst) | ||||
| +{ | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) | ||||
| +	if (pf == NFPROTO_IPV6) { | ||||
| +		const struct rt6_info *rt = (const struct rt6_info *)dst; | ||||
| + | ||||
| +		if (rt->rt6i_node) | ||||
| +			return (u32)rt->rt6i_node->fn_sernum; | ||||
| +	} | ||||
| +#endif | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void nf_conn_rtcache_dst_set(int pf, | ||||
| +				    struct nf_conn_rtcache *rtc, | ||||
| +				    struct dst_entry *dst, | ||||
| +				    enum ip_conntrack_dir dir, int iif) | ||||
| +{ | ||||
| +	if (rtc->cached_dst[dir].iif != iif) | ||||
| +		rtc->cached_dst[dir].iif = iif; | ||||
| + | ||||
| +	if (rtc->cached_dst[dir].dst != dst) { | ||||
| +		struct dst_entry *old; | ||||
| + | ||||
| +		dst_hold(dst); | ||||
| + | ||||
| +		old = xchg(&rtc->cached_dst[dir].dst, dst); | ||||
| +		dst_release(old); | ||||
| + | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) | ||||
| +		if (pf == NFPROTO_IPV6) | ||||
| +			rtc->cached_dst[dir].cookie = | ||||
| +				nf_rtcache_get_cookie(pf, dst); | ||||
| +#endif | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static void nf_conn_rtcache_dst_obsolete(struct nf_conn_rtcache *rtc, | ||||
| +					 enum ip_conntrack_dir dir) | ||||
| +{ | ||||
| +	struct dst_entry *old; | ||||
| + | ||||
| +	pr_debug("Invalidate iif %d for dir %d on cache %p\n", | ||||
| +		 rtc->cached_dst[dir].iif, dir, rtc); | ||||
| + | ||||
| +	old = xchg(&rtc->cached_dst[dir].dst, NULL); | ||||
| +	dst_release(old); | ||||
| +	rtc->cached_dst[dir].iif = -1; | ||||
| +} | ||||
| + | ||||
| +static unsigned int nf_rtcache_in(const struct nf_hook_ops *ops, | ||||
| +				  struct sk_buff *skb, | ||||
| +				  const struct nf_hook_state *state) | ||||
| +{ | ||||
| +	struct nf_conn_rtcache *rtc; | ||||
| +	enum ip_conntrack_info ctinfo; | ||||
| +	enum ip_conntrack_dir dir; | ||||
| +	struct dst_entry *dst; | ||||
| +	struct nf_conn *ct; | ||||
| +	int iif; | ||||
| +	u32 cookie; | ||||
| + | ||||
| +	if (skb_dst(skb) || skb->sk) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	ct = nf_ct_get(skb, &ctinfo); | ||||
| +	if (!ct) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	rtc = nf_ct_rtcache_find_usable(ct); | ||||
| +	if (!rtc) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	/* if iif changes, don't use cache and let ip stack | ||||
| +	 * do route lookup. | ||||
| +	 * | ||||
| +	 * If rp_filter is enabled it might toss skb, so | ||||
| +	 * we don't want to avoid these checks. | ||||
| +	 */ | ||||
| +	dir = CTINFO2DIR(ctinfo); | ||||
| +	iif = nf_conn_rtcache_iif_get(rtc, dir); | ||||
| +	if (state->in->ifindex != iif) { | ||||
| +		pr_debug("ct %p, iif %d, cached iif %d, skip cached entry\n", | ||||
| +			 ct, iif, state->in->ifindex); | ||||
| +		return NF_ACCEPT; | ||||
| +	} | ||||
| +	dst = nf_conn_rtcache_dst_get(rtc, dir); | ||||
| +	if (dst == NULL) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	cookie = nf_rtcache_get_cookie(ops->pf, dst); | ||||
| + | ||||
| +	dst = dst_check(dst, cookie); | ||||
| +	pr_debug("obtained dst %p for skb %p, cookie %d\n", dst, skb, cookie); | ||||
| +	if (likely(dst)) | ||||
| +		skb_dst_set_noref(skb, dst); | ||||
| +	else | ||||
| +		nf_conn_rtcache_dst_obsolete(rtc, dir); | ||||
| + | ||||
| +	return NF_ACCEPT; | ||||
| +} | ||||
| + | ||||
| +static unsigned int nf_rtcache_forward(const struct nf_hook_ops *ops, | ||||
| +				       struct sk_buff *skb, | ||||
| +				       const struct nf_hook_state *state) | ||||
| +{ | ||||
| +	struct nf_conn_rtcache *rtc; | ||||
| +	enum ip_conntrack_info ctinfo; | ||||
| +	enum ip_conntrack_dir dir; | ||||
| +	struct nf_conn *ct; | ||||
| +	struct dst_entry *dst = skb_dst(skb); | ||||
| +	int iif; | ||||
| + | ||||
| +	ct = nf_ct_get(skb, &ctinfo); | ||||
| +	if (!ct) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	if (dst && dst_xfrm(dst)) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	if (!nf_ct_is_confirmed(ct)) { | ||||
| +		if (WARN_ON(nf_ct_rtcache_find(ct))) | ||||
| +			return NF_ACCEPT; | ||||
| +		nf_ct_rtcache_ext_add(ct); | ||||
| +		return NF_ACCEPT; | ||||
| +	} | ||||
| + | ||||
| +	rtc = nf_ct_rtcache_find_usable(ct); | ||||
| +	if (!rtc) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	dir = CTINFO2DIR(ctinfo); | ||||
| +	iif = nf_conn_rtcache_iif_get(rtc, dir); | ||||
| +	pr_debug("ct %p, skb %p, dir %d, iif %d, cached iif %d\n", | ||||
| +		 ct, skb, dir, iif, state->in->ifindex); | ||||
| +	if (likely(state->in->ifindex == iif)) | ||||
| +		return NF_ACCEPT; | ||||
| + | ||||
| +	nf_conn_rtcache_dst_set(ops->pf, rtc, skb_dst(skb), dir, state->in->ifindex); | ||||
| +	return NF_ACCEPT; | ||||
| +} | ||||
| + | ||||
| +static int nf_rtcache_dst_remove(struct nf_conn *ct, void *data) | ||||
| +{ | ||||
| +	struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct); | ||||
| +	struct net_device *dev = data; | ||||
| + | ||||
| +	if (!rtc) | ||||
| +		return 0; | ||||
| + | ||||
| +	if (dev->ifindex == rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif || | ||||
| +	    dev->ifindex == rtc->cached_dst[IP_CT_DIR_REPLY].iif) { | ||||
| +		nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_ORIGINAL); | ||||
| +		nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_REPLY); | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int nf_rtcache_netdev_event(struct notifier_block *this, | ||||
| +				   unsigned long event, void *ptr) | ||||
| +{ | ||||
| +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||||
| +	struct net *net = dev_net(dev); | ||||
| + | ||||
| +	if (event == NETDEV_DOWN) | ||||
| +		nf_ct_iterate_cleanup(net, nf_rtcache_dst_remove, dev, 0, 0); | ||||
| + | ||||
| +	return NOTIFY_DONE; | ||||
| +} | ||||
| + | ||||
| +static struct notifier_block nf_rtcache_notifier = { | ||||
| +	.notifier_call = nf_rtcache_netdev_event, | ||||
| +}; | ||||
| + | ||||
| +static struct nf_hook_ops rtcache_ops[] = { | ||||
| +	{ | ||||
| +		.hook		= nf_rtcache_in, | ||||
| +		.owner		= THIS_MODULE, | ||||
| +		.pf		= NFPROTO_IPV4, | ||||
| +		.hooknum	= NF_INET_PRE_ROUTING, | ||||
| +		.priority       = NF_IP_PRI_LAST, | ||||
| +	}, | ||||
| +	{ | ||||
| +		.hook           = nf_rtcache_forward, | ||||
| +		.owner          = THIS_MODULE, | ||||
| +		.pf             = NFPROTO_IPV4, | ||||
| +		.hooknum        = NF_INET_FORWARD, | ||||
| +		.priority       = NF_IP_PRI_LAST, | ||||
| +	}, | ||||
| +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) | ||||
| +	{ | ||||
| +		.hook		= nf_rtcache_in, | ||||
| +		.owner		= THIS_MODULE, | ||||
| +		.pf		= NFPROTO_IPV6, | ||||
| +		.hooknum	= NF_INET_PRE_ROUTING, | ||||
| +		.priority       = NF_IP_PRI_LAST, | ||||
| +	}, | ||||
| +	{ | ||||
| +		.hook           = nf_rtcache_forward, | ||||
| +		.owner          = THIS_MODULE, | ||||
| +		.pf             = NFPROTO_IPV6, | ||||
| +		.hooknum        = NF_INET_FORWARD, | ||||
| +		.priority       = NF_IP_PRI_LAST, | ||||
| +	}, | ||||
| +#endif | ||||
| +}; | ||||
| + | ||||
| +static struct nf_ct_ext_type rtcache_extend __read_mostly = { | ||||
| +	.len	= sizeof(struct nf_conn_rtcache), | ||||
| +	.align	= __alignof__(struct nf_conn_rtcache), | ||||
| +	.id	= NF_CT_EXT_RTCACHE, | ||||
| +	.destroy = nf_conn_rtcache_destroy, | ||||
| +}; | ||||
| + | ||||
| +static int __init nf_conntrack_rtcache_init(void) | ||||
| +{ | ||||
| +	int ret = nf_ct_extend_register(&rtcache_extend); | ||||
| + | ||||
| +	if (ret < 0) { | ||||
| +		pr_err("nf_conntrack_rtcache: Unable to register extension\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	ret = nf_register_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops)); | ||||
| +	if (ret < 0) { | ||||
| +		nf_ct_extend_unregister(&rtcache_extend); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	ret = register_netdevice_notifier(&nf_rtcache_notifier); | ||||
| +	if (ret) { | ||||
| +		nf_unregister_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops)); | ||||
| +		nf_ct_extend_unregister(&rtcache_extend); | ||||
| +	} | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int nf_rtcache_ext_remove(struct nf_conn *ct, void *data) | ||||
| +{ | ||||
| +	struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct); | ||||
| + | ||||
| +	return rtc != NULL; | ||||
| +} | ||||
| + | ||||
| +static bool __exit nf_conntrack_rtcache_wait_for_dying(struct net *net) | ||||
| +{ | ||||
| +	bool wait = false; | ||||
| +	int cpu; | ||||
| + | ||||
| +	for_each_possible_cpu(cpu) { | ||||
| +		struct nf_conntrack_tuple_hash *h; | ||||
| +		struct hlist_nulls_node *n; | ||||
| +		struct nf_conn *ct; | ||||
| +		struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); | ||||
| + | ||||
| +		rcu_read_lock(); | ||||
| +		spin_lock_bh(&pcpu->lock); | ||||
| + | ||||
| +		hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) { | ||||
| +			ct = nf_ct_tuplehash_to_ctrack(h); | ||||
| +			if (nf_ct_rtcache_find(ct) != NULL) { | ||||
| +				wait = true; | ||||
| +				break; | ||||
| +			} | ||||
| +		} | ||||
| +		spin_unlock_bh(&pcpu->lock); | ||||
| +		rcu_read_unlock(); | ||||
| +	} | ||||
| + | ||||
| +	return wait; | ||||
| +} | ||||
| + | ||||
| +static void __exit nf_conntrack_rtcache_fini(void) | ||||
| +{ | ||||
| +	struct net *net; | ||||
| +	int count = 0; | ||||
| + | ||||
| +	/* remove hooks so no new connections get rtcache extension */ | ||||
| +	nf_unregister_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops)); | ||||
| + | ||||
| +	synchronize_net(); | ||||
| + | ||||
| +	unregister_netdevice_notifier(&nf_rtcache_notifier); | ||||
| + | ||||
| +	rtnl_lock(); | ||||
| + | ||||
| +	/* zap all conntracks with rtcache extension */ | ||||
| +	for_each_net(net) | ||||
| +		nf_ct_iterate_cleanup(net, nf_rtcache_ext_remove, NULL, 0, 0); | ||||
| + | ||||
| +	for_each_net(net) { | ||||
| +		/* .. and make sure they're gone from dying list, too */ | ||||
| +		while (nf_conntrack_rtcache_wait_for_dying(net)) { | ||||
| +			msleep(200); | ||||
| +			WARN_ONCE(++count > 25, "Waiting for all rtcache conntracks to go away\n"); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	rtnl_unlock(); | ||||
| +	synchronize_net(); | ||||
| +	nf_ct_extend_unregister(&rtcache_extend); | ||||
| +} | ||||
| +module_init(nf_conntrack_rtcache_init); | ||||
| +module_exit(nf_conntrack_rtcache_fini); | ||||
| + | ||||
| +MODULE_LICENSE("GPL"); | ||||
| +MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); | ||||
| +MODULE_DESCRIPTION("Conntrack route cache extension"); | ||||
| @@ -1,22 +0,0 @@ | ||||
| --- a/arch/mips/boot/compressed/string.c | ||||
| +++ b/arch/mips/boot/compressed/string.c | ||||
| @@ -26,3 +26,19 @@ void *memset(void *s, int c, size_t n) | ||||
|  		ss[i] = c; | ||||
|  	return s; | ||||
|  } | ||||
| + | ||||
| +void *memmove(void *__dest, __const void *__src, size_t count) | ||||
| +{ | ||||
| +	unsigned char *d = __dest; | ||||
| +	const unsigned char *s = __src; | ||||
| + | ||||
| +	if (__dest == __src) | ||||
| +		return __dest; | ||||
| + | ||||
| +	if (__dest < __src) | ||||
| +		return memcpy(__dest, __src, count); | ||||
| + | ||||
| +	while (count--) | ||||
| +		d[count] = s[count]; | ||||
| +	return __dest; | ||||
| +} | ||||
| @@ -1,24 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Mon, 13 Apr 2015 15:54:04 +0200 | ||||
| Subject: [PATCH] bgmac: fix MAC soft-reset bit for corerev > 4 | ||||
|  | ||||
| Only core revisions older than 4 use BGMAC_CMDCFG_SR_REV0 | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/ethernet/broadcom/bgmac.h | ||||
| +++ b/drivers/net/ethernet/broadcom/bgmac.h | ||||
| @@ -199,9 +199,9 @@ | ||||
|  #define  BGMAC_CMDCFG_TAI			0x00000200 | ||||
|  #define  BGMAC_CMDCFG_HD			0x00000400	/* Set if in half duplex mode */ | ||||
|  #define  BGMAC_CMDCFG_HD_SHIFT			10 | ||||
| -#define  BGMAC_CMDCFG_SR_REV0			0x00000800	/* Set to reset mode, for other revs */ | ||||
| -#define  BGMAC_CMDCFG_SR_REV4			0x00002000	/* Set to reset mode, only for core rev 4 */ | ||||
| -#define  BGMAC_CMDCFG_SR(rev)  ((rev == 4) ? BGMAC_CMDCFG_SR_REV4 : BGMAC_CMDCFG_SR_REV0) | ||||
| +#define  BGMAC_CMDCFG_SR_REV0			0x00000800	/* Set to reset mode, for core rev 0-3 */ | ||||
| +#define  BGMAC_CMDCFG_SR_REV4			0x00002000	/* Set to reset mode, for core rev >= 4 */ | ||||
| +#define  BGMAC_CMDCFG_SR(rev)  ((rev >= 4) ? BGMAC_CMDCFG_SR_REV4 : BGMAC_CMDCFG_SR_REV0) | ||||
|  #define  BGMAC_CMDCFG_ML			0x00008000	/* Set to activate mac loopback mode */ | ||||
|  #define  BGMAC_CMDCFG_AE			0x00400000 | ||||
|  #define  BGMAC_CMDCFG_CFE			0x00800000 | ||||
| @@ -1,28 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Mon, 13 Apr 2015 15:56:26 +0200 | ||||
| Subject: [PATCH] bgmac: reset all 4 GMAC cores on init | ||||
|  | ||||
| On a BCM4709 based device, I found that GMAC cores may be enabled at | ||||
| probe time, but only become usable after a full reset. | ||||
| Disable cores before re-enabling them to ensure that they are properly | ||||
| reset. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/ethernet/broadcom/bgmac.c | ||||
| +++ b/drivers/net/ethernet/broadcom/bgmac.c | ||||
| @@ -1641,8 +1641,11 @@ static int bgmac_probe(struct bcma_devic | ||||
|  			ns_core = bcma_find_core_unit(core->bus, | ||||
|  						      BCMA_CORE_MAC_GBIT, | ||||
|  						      ns_gmac); | ||||
| -			if (ns_core && !bcma_core_is_enabled(ns_core)) | ||||
| -				bcma_core_enable(ns_core, 0); | ||||
| +			if (!ns_core) | ||||
| +				continue; | ||||
| + | ||||
| +			bcma_core_disable(ns_core, 0); | ||||
| +			bcma_core_enable(ns_core, 0); | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| @@ -1,808 +0,0 @@ | ||||
| --- a/drivers/mtd/devices/m25p80.c | ||||
| +++ b/drivers/mtd/devices/m25p80.c | ||||
| @@ -31,7 +31,6 @@ | ||||
|  struct m25p { | ||||
|  	struct spi_device	*spi; | ||||
|  	struct spi_nor		spi_nor; | ||||
| -	struct mtd_info		mtd; | ||||
|  	u8			command[MAX_CMD_SIZE]; | ||||
|  }; | ||||
|   | ||||
| @@ -62,8 +61,7 @@ static int m25p_cmdsz(struct spi_nor *no | ||||
|  	return 1 + nor->addr_width; | ||||
|  } | ||||
|   | ||||
| -static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, | ||||
| -			int wr_en) | ||||
| +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) | ||||
|  { | ||||
|  	struct m25p *flash = nor->priv; | ||||
|  	struct spi_device *spi = flash->spi; | ||||
| @@ -159,7 +157,7 @@ static int m25p80_erase(struct spi_nor * | ||||
|  	struct m25p *flash = nor->priv; | ||||
|   | ||||
|  	dev_dbg(nor->dev, "%dKiB at 0x%08x\n", | ||||
| -		flash->mtd.erasesize / 1024, (u32)offset); | ||||
| +		flash->spi_nor.mtd.erasesize / 1024, (u32)offset); | ||||
|   | ||||
|  	/* Set up command buffer. */ | ||||
|  	flash->command[0] = nor->erase_opcode; | ||||
| @@ -201,11 +199,10 @@ static int m25p_probe(struct spi_device | ||||
|  	nor->read_reg = m25p80_read_reg; | ||||
|   | ||||
|  	nor->dev = &spi->dev; | ||||
| -	nor->mtd = &flash->mtd; | ||||
| +	nor->flash_node = spi->dev.of_node; | ||||
|  	nor->priv = flash; | ||||
|   | ||||
|  	spi_set_drvdata(spi, flash); | ||||
| -	flash->mtd.priv = nor; | ||||
|  	flash->spi = spi; | ||||
|   | ||||
|  	if (spi->mode & SPI_RX_QUAD) | ||||
| @@ -214,7 +211,7 @@ static int m25p_probe(struct spi_device | ||||
|  		mode = SPI_NOR_DUAL; | ||||
|   | ||||
|  	if (data && data->name) | ||||
| -		flash->mtd.name = data->name; | ||||
| +		nor->mtd.name = data->name; | ||||
|   | ||||
|  	/* For some (historical?) reason many platforms provide two different | ||||
|  	 * names in flash_platform_data: "name" and "type". Quite often name is | ||||
| @@ -232,7 +229,7 @@ static int m25p_probe(struct spi_device | ||||
|   | ||||
|  	ppdata.of_node = spi->dev.of_node; | ||||
|   | ||||
| -	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, | ||||
| +	return mtd_device_parse_register(&nor->mtd, NULL, &ppdata, | ||||
|  			data ? data->parts : NULL, | ||||
|  			data ? data->nr_parts : 0); | ||||
|  } | ||||
| @@ -243,7 +240,7 @@ static int m25p_remove(struct spi_device | ||||
|  	struct m25p	*flash = spi_get_drvdata(spi); | ||||
|   | ||||
|  	/* Clean up MTD stuff. */ | ||||
| -	return mtd_device_unregister(&flash->mtd); | ||||
| +	return mtd_device_unregister(&flash->spi_nor.mtd); | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| @@ -304,7 +301,6 @@ MODULE_DEVICE_TABLE(of, m25p_of_table); | ||||
|  static struct spi_driver m25p80_driver = { | ||||
|  	.driver = { | ||||
|  		.name	= "m25p80", | ||||
| -		.owner	= THIS_MODULE, | ||||
|  		.of_match_table = m25p_of_table, | ||||
|  	}, | ||||
|  	.id_table	= m25p_ids, | ||||
| --- a/drivers/mtd/spi-nor/spi-nor.c | ||||
| +++ b/drivers/mtd/spi-nor/spi-nor.c | ||||
| @@ -16,15 +16,26 @@ | ||||
|  #include <linux/device.h> | ||||
|  #include <linux/mutex.h> | ||||
|  #include <linux/math64.h> | ||||
| +#include <linux/sizes.h> | ||||
|   | ||||
| -#include <linux/mtd/cfi.h> | ||||
|  #include <linux/mtd/mtd.h> | ||||
|  #include <linux/of_platform.h> | ||||
|  #include <linux/spi/flash.h> | ||||
|  #include <linux/mtd/spi-nor.h> | ||||
|   | ||||
|  /* Define max times to check status register before we give up. */ | ||||
| -#define	MAX_READY_WAIT_JIFFIES	(40 * HZ) /* M25P16 specs 40s max chip erase */ | ||||
| + | ||||
| +/* | ||||
| + * For everything but full-chip erase; probably could be much smaller, but kept | ||||
| + * around for safety for now | ||||
| + */ | ||||
| +#define DEFAULT_READY_WAIT_JIFFIES		(40UL * HZ) | ||||
| + | ||||
| +/* | ||||
| + * For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up | ||||
| + * for larger flash | ||||
| + */ | ||||
| +#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES	(40UL * HZ) | ||||
|   | ||||
|  #define SPI_NOR_MAX_ID_LEN	6 | ||||
|   | ||||
| @@ -145,7 +156,7 @@ static inline int spi_nor_read_dummy_cyc | ||||
|  static inline int write_sr(struct spi_nor *nor, u8 val) | ||||
|  { | ||||
|  	nor->cmd_buf[0] = val; | ||||
| -	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); | ||||
| +	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| @@ -154,7 +165,7 @@ static inline int write_sr(struct spi_no | ||||
|   */ | ||||
|  static inline int write_enable(struct spi_nor *nor) | ||||
|  { | ||||
| -	return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); | ||||
| +	return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| @@ -162,7 +173,7 @@ static inline int write_enable(struct sp | ||||
|   */ | ||||
|  static inline int write_disable(struct spi_nor *nor) | ||||
|  { | ||||
| -	return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); | ||||
| +	return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); | ||||
|  } | ||||
|   | ||||
|  static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) | ||||
| @@ -179,16 +190,16 @@ static inline int set_4byte(struct spi_n | ||||
|  	u8 cmd; | ||||
|   | ||||
|  	switch (JEDEC_MFR(info)) { | ||||
| -	case CFI_MFR_ST: /* Micron, actually */ | ||||
| +	case SNOR_MFR_MICRON: | ||||
|  		/* Some Micron need WREN command; all will accept it */ | ||||
|  		need_wren = true; | ||||
| -	case CFI_MFR_MACRONIX: | ||||
| -	case 0xEF /* winbond */: | ||||
| +	case SNOR_MFR_MACRONIX: | ||||
| +	case SNOR_MFR_WINBOND: | ||||
|  		if (need_wren) | ||||
|  			write_enable(nor); | ||||
|   | ||||
|  		cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; | ||||
| -		status = nor->write_reg(nor, cmd, NULL, 0, 0); | ||||
| +		status = nor->write_reg(nor, cmd, NULL, 0); | ||||
|  		if (need_wren) | ||||
|  			write_disable(nor); | ||||
|   | ||||
| @@ -196,7 +207,7 @@ static inline int set_4byte(struct spi_n | ||||
|  	default: | ||||
|  		/* Spansion style */ | ||||
|  		nor->cmd_buf[0] = enable << 7; | ||||
| -		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); | ||||
| +		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); | ||||
|  	} | ||||
|  } | ||||
|  static inline int spi_nor_sr_ready(struct spi_nor *nor) | ||||
| @@ -233,12 +244,13 @@ static int spi_nor_ready(struct spi_nor | ||||
|   * Service routine to read status register until ready, or timeout occurs. | ||||
|   * Returns non-zero if error. | ||||
|   */ | ||||
| -static int spi_nor_wait_till_ready(struct spi_nor *nor) | ||||
| +static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, | ||||
| +						unsigned long timeout_jiffies) | ||||
|  { | ||||
|  	unsigned long deadline; | ||||
|  	int timeout = 0, ret; | ||||
|   | ||||
| -	deadline = jiffies + MAX_READY_WAIT_JIFFIES; | ||||
| +	deadline = jiffies + timeout_jiffies; | ||||
|   | ||||
|  	while (!timeout) { | ||||
|  		if (time_after_eq(jiffies, deadline)) | ||||
| @@ -258,6 +270,12 @@ static int spi_nor_wait_till_ready(struc | ||||
|  	return -ETIMEDOUT; | ||||
|  } | ||||
|   | ||||
| +static int spi_nor_wait_till_ready(struct spi_nor *nor) | ||||
| +{ | ||||
| +	return spi_nor_wait_till_ready_with_timeout(nor, | ||||
| +						    DEFAULT_READY_WAIT_JIFFIES); | ||||
| +} | ||||
| + | ||||
|  /* | ||||
|   * Erase the whole flash memory | ||||
|   * | ||||
| @@ -265,9 +283,9 @@ static int spi_nor_wait_till_ready(struc | ||||
|   */ | ||||
|  static int erase_chip(struct spi_nor *nor) | ||||
|  { | ||||
| -	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); | ||||
| +	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); | ||||
|   | ||||
| -	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); | ||||
| +	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); | ||||
|  } | ||||
|   | ||||
|  static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) | ||||
| @@ -321,6 +339,8 @@ static int spi_nor_erase(struct mtd_info | ||||
|   | ||||
|  	/* whole-chip erase? */ | ||||
|  	if (len == mtd->size) { | ||||
| +		unsigned long timeout; | ||||
| + | ||||
|  		write_enable(nor); | ||||
|   | ||||
|  		if (erase_chip(nor)) { | ||||
| @@ -328,7 +348,16 @@ static int spi_nor_erase(struct mtd_info | ||||
|  			goto erase_err; | ||||
|  		} | ||||
|   | ||||
| -		ret = spi_nor_wait_till_ready(nor); | ||||
| +		/* | ||||
| +		 * Scale the timeout linearly with the size of the flash, with | ||||
| +		 * a minimum calibrated to an old 2MB flash. We could try to | ||||
| +		 * pull these from CFI/SFDP, but these values should be good | ||||
| +		 * enough for now. | ||||
| +		 */ | ||||
| +		timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, | ||||
| +			      CHIP_ERASE_2MB_READY_WAIT_JIFFIES * | ||||
| +			      (unsigned long)(mtd->size / SZ_2M)); | ||||
| +		ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); | ||||
|  		if (ret) | ||||
|  			goto erase_err; | ||||
|   | ||||
| @@ -371,72 +400,171 @@ erase_err: | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, | ||||
| +				 uint64_t *len) | ||||
| +{ | ||||
| +	struct mtd_info *mtd = &nor->mtd; | ||||
| +	u8 mask = SR_BP2 | SR_BP1 | SR_BP0; | ||||
| +	int shift = ffs(mask) - 1; | ||||
| +	int pow; | ||||
| + | ||||
| +	if (!(sr & mask)) { | ||||
| +		/* No protection */ | ||||
| +		*ofs = 0; | ||||
| +		*len = 0; | ||||
| +	} else { | ||||
| +		pow = ((sr & mask) ^ mask) >> shift; | ||||
| +		*len = mtd->size >> pow; | ||||
| +		*ofs = mtd->size - *len; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * Return 1 if the entire region is locked, 0 otherwise | ||||
| + */ | ||||
| +static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, | ||||
| +			    u8 sr) | ||||
| +{ | ||||
| +	loff_t lock_offs; | ||||
| +	uint64_t lock_len; | ||||
| + | ||||
| +	stm_get_locked_range(nor, sr, &lock_offs, &lock_len); | ||||
| + | ||||
| +	return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * Lock a region of the flash. Compatible with ST Micro and similar flash. | ||||
| + * Supports only the block protection bits BP{0,1,2} in the status register | ||||
| + * (SR). Does not support these features found in newer SR bitfields: | ||||
| + *   - TB: top/bottom protect - only handle TB=0 (top protect) | ||||
| + *   - SEC: sector/block protect - only handle SEC=0 (block protect) | ||||
| + *   - CMP: complement protect - only support CMP=0 (range is not complemented) | ||||
| + * | ||||
| + * Sample table portion for 8MB flash (Winbond w25q64fw): | ||||
| + * | ||||
| + *   SEC  |  TB   |  BP2  |  BP1  |  BP0  |  Prot Length  | Protected Portion | ||||
| + *  -------------------------------------------------------------------------- | ||||
| + *    X   |   X   |   0   |   0   |   0   |  NONE         | NONE | ||||
| + *    0   |   0   |   0   |   0   |   1   |  128 KB       | Upper 1/64 | ||||
| + *    0   |   0   |   0   |   1   |   0   |  256 KB       | Upper 1/32 | ||||
| + *    0   |   0   |   0   |   1   |   1   |  512 KB       | Upper 1/16 | ||||
| + *    0   |   0   |   1   |   0   |   0   |  1 MB         | Upper 1/8 | ||||
| + *    0   |   0   |   1   |   0   |   1   |  2 MB         | Upper 1/4 | ||||
| + *    0   |   0   |   1   |   1   |   0   |  4 MB         | Upper 1/2 | ||||
| + *    X   |   X   |   1   |   1   |   1   |  8 MB         | ALL | ||||
| + * | ||||
| + * Returns negative on errors, 0 on success. | ||||
| + */ | ||||
|  static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) | ||||
|  { | ||||
| -	struct mtd_info *mtd = nor->mtd; | ||||
| -	uint32_t offset = ofs; | ||||
| -	uint8_t status_old, status_new; | ||||
| -	int ret = 0; | ||||
| +	struct mtd_info *mtd = &nor->mtd; | ||||
| +	u8 status_old, status_new; | ||||
| +	u8 mask = SR_BP2 | SR_BP1 | SR_BP0; | ||||
| +	u8 shift = ffs(mask) - 1, pow, val; | ||||
|   | ||||
|  	status_old = read_sr(nor); | ||||
|   | ||||
| -	if (offset < mtd->size - (mtd->size / 2)) | ||||
| -		status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; | ||||
| -	else if (offset < mtd->size - (mtd->size / 4)) | ||||
| -		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; | ||||
| -	else if (offset < mtd->size - (mtd->size / 8)) | ||||
| -		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; | ||||
| -	else if (offset < mtd->size - (mtd->size / 16)) | ||||
| -		status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; | ||||
| -	else if (offset < mtd->size - (mtd->size / 32)) | ||||
| -		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; | ||||
| -	else if (offset < mtd->size - (mtd->size / 64)) | ||||
| -		status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; | ||||
| -	else | ||||
| -		status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; | ||||
| +	/* SPI NOR always locks to the end */ | ||||
| +	if (ofs + len != mtd->size) { | ||||
| +		/* Does combined region extend to end? */ | ||||
| +		if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len, | ||||
| +				      status_old)) | ||||
| +			return -EINVAL; | ||||
| +		len = mtd->size - ofs; | ||||
| +	} | ||||
| + | ||||
| +	/* | ||||
| +	 * Need smallest pow such that: | ||||
| +	 * | ||||
| +	 *   1 / (2^pow) <= (len / size) | ||||
| +	 * | ||||
| +	 * so (assuming power-of-2 size) we do: | ||||
| +	 * | ||||
| +	 *   pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) | ||||
| +	 */ | ||||
| +	pow = ilog2(mtd->size) - ilog2(len); | ||||
| +	val = mask - (pow << shift); | ||||
| +	if (val & ~mask) | ||||
| +		return -EINVAL; | ||||
| +	/* Don't "lock" with no region! */ | ||||
| +	if (!(val & mask)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	status_new = (status_old & ~mask) | val; | ||||
|   | ||||
|  	/* Only modify protection if it will not unlock other areas */ | ||||
| -	if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > | ||||
| -				(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { | ||||
| -		write_enable(nor); | ||||
| -		ret = write_sr(nor, status_new); | ||||
| -	} | ||||
| +	if ((status_new & mask) <= (status_old & mask)) | ||||
| +		return -EINVAL; | ||||
|   | ||||
| -	return ret; | ||||
| +	write_enable(nor); | ||||
| +	return write_sr(nor, status_new); | ||||
|  } | ||||
|   | ||||
| +/* | ||||
| + * Unlock a region of the flash. See stm_lock() for more info | ||||
| + * | ||||
| + * Returns negative on errors, 0 on success. | ||||
| + */ | ||||
|  static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) | ||||
|  { | ||||
| -	struct mtd_info *mtd = nor->mtd; | ||||
| -	uint32_t offset = ofs; | ||||
| +	struct mtd_info *mtd = &nor->mtd; | ||||
|  	uint8_t status_old, status_new; | ||||
| -	int ret = 0; | ||||
| +	u8 mask = SR_BP2 | SR_BP1 | SR_BP0; | ||||
| +	u8 shift = ffs(mask) - 1, pow, val; | ||||
|   | ||||
|  	status_old = read_sr(nor); | ||||
|   | ||||
| -	if (offset+len > mtd->size - (mtd->size / 64)) | ||||
| -		status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); | ||||
| -	else if (offset+len > mtd->size - (mtd->size / 32)) | ||||
| -		status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; | ||||
| -	else if (offset+len > mtd->size - (mtd->size / 16)) | ||||
| -		status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; | ||||
| -	else if (offset+len > mtd->size - (mtd->size / 8)) | ||||
| -		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; | ||||
| -	else if (offset+len > mtd->size - (mtd->size / 4)) | ||||
| -		status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; | ||||
| -	else if (offset+len > mtd->size - (mtd->size / 2)) | ||||
| -		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; | ||||
| -	else | ||||
| -		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; | ||||
| +	/* Cannot unlock; would unlock larger region than requested */ | ||||
| +	if (stm_is_locked_sr(nor, status_old, ofs - mtd->erasesize, | ||||
| +			     mtd->erasesize)) | ||||
| +		return -EINVAL; | ||||
|   | ||||
| -	/* Only modify protection if it will not lock other areas */ | ||||
| -	if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < | ||||
| -				(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { | ||||
| -		write_enable(nor); | ||||
| -		ret = write_sr(nor, status_new); | ||||
| +	/* | ||||
| +	 * Need largest pow such that: | ||||
| +	 * | ||||
| +	 *   1 / (2^pow) >= (len / size) | ||||
| +	 * | ||||
| +	 * so (assuming power-of-2 size) we do: | ||||
| +	 * | ||||
| +	 *   pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) | ||||
| +	 */ | ||||
| +	pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len)); | ||||
| +	if (ofs + len == mtd->size) { | ||||
| +		val = 0; /* fully unlocked */ | ||||
| +	} else { | ||||
| +		val = mask - (pow << shift); | ||||
| +		/* Some power-of-two sizes are not supported */ | ||||
| +		if (val & ~mask) | ||||
| +			return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	return ret; | ||||
| +	status_new = (status_old & ~mask) | val; | ||||
| + | ||||
| +	/* Only modify protection if it will not lock other areas */ | ||||
| +	if ((status_new & mask) >= (status_old & mask)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	write_enable(nor); | ||||
| +	return write_sr(nor, status_new); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * Check if a region of the flash is (completely) locked. See stm_lock() for | ||||
| + * more info. | ||||
| + * | ||||
| + * Returns 1 if entire region is locked, 0 if any portion is unlocked, and | ||||
| + * negative on errors. | ||||
| + */ | ||||
| +static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) | ||||
| +{ | ||||
| +	int status; | ||||
| + | ||||
| +	status = read_sr(nor); | ||||
| +	if (status < 0) | ||||
| +		return status; | ||||
| + | ||||
| +	return stm_is_locked_sr(nor, ofs, len, status); | ||||
|  } | ||||
|   | ||||
|  static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||||
| @@ -469,6 +597,21 @@ static int spi_nor_unlock(struct mtd_inf | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||||
| +{ | ||||
| +	struct spi_nor *nor = mtd_to_spi_nor(mtd); | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = nor->flash_is_locked(nor, ofs, len); | ||||
| + | ||||
| +	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  /* Used when the "_ext_id" is two bytes at most */ | ||||
|  #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\ | ||||
|  		.id = {							\ | ||||
| @@ -585,6 +728,7 @@ static const struct flash_info spi_nor_i | ||||
|   | ||||
|  	/* Micron */ | ||||
|  	{ "n25q032",	 INFO(0x20ba16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) }, | ||||
| +	{ "n25q032a",	 INFO(0x20bb16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) }, | ||||
|  	{ "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "n25q064a",    INFO(0x20bb17, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, SPI_NOR_QUAD_READ) }, | ||||
| @@ -618,12 +762,13 @@ static const struct flash_info spi_nor_i | ||||
|  	{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) }, | ||||
|  	{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) }, | ||||
|  	{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) }, | ||||
| -	{ "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K) }, | ||||
| -	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) }, | ||||
| +	{ "s25fl004k",  INFO(0xef4013,      0,  64 * 1024,   8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
| +	{ "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
| +	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) }, | ||||
|  	{ "s25fl132k",  INFO(0x014016,      0,  64 * 1024,  64, SECT_4K) }, | ||||
|  	{ "s25fl164k",  INFO(0x014017,      0,  64 * 1024, 128, SECT_4K) }, | ||||
| -	{ "s25fl204k",  INFO(0x014013,      0,  64 * 1024,   8, SECT_4K) }, | ||||
| +	{ "s25fl204k",  INFO(0x014013,      0,  64 * 1024,   8, SECT_4K | SPI_NOR_DUAL_READ) }, | ||||
|   | ||||
|  	/* SST -- large erase sizes are "overlays", "sectors" are 4K */ | ||||
|  	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) }, | ||||
| @@ -635,6 +780,7 @@ static const struct flash_info spi_nor_i | ||||
|  	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) }, | ||||
|  	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) }, | ||||
|  	{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024,  4, SECT_4K) }, | ||||
| +	{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SECT_4K) }, | ||||
|  	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) }, | ||||
|  	{ "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, | ||||
|   | ||||
| @@ -683,10 +829,11 @@ static const struct flash_info spi_nor_i | ||||
|  	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) }, | ||||
|  	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) }, | ||||
|  	{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) }, | ||||
| -	{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64, SECT_4K) }, | ||||
| +	{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, | ||||
|  	{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, | ||||
| -	{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K) }, | ||||
| +	{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
| +	{ "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||
|  	{ "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) }, | ||||
|  	{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) }, | ||||
|  	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, | ||||
| @@ -868,8 +1015,7 @@ static int macronix_quad_enable(struct s | ||||
|  	val = read_sr(nor); | ||||
|  	write_enable(nor); | ||||
|   | ||||
| -	nor->cmd_buf[0] = val | SR_QUAD_EN_MX; | ||||
| -	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); | ||||
| +	write_sr(nor, val | SR_QUAD_EN_MX); | ||||
|   | ||||
|  	if (spi_nor_wait_till_ready(nor)) | ||||
|  		return 1; | ||||
| @@ -894,7 +1040,7 @@ static int write_sr_cr(struct spi_nor *n | ||||
|  	nor->cmd_buf[0] = val & 0xff; | ||||
|  	nor->cmd_buf[1] = (val >> 8); | ||||
|   | ||||
| -	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); | ||||
| +	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2); | ||||
|  } | ||||
|   | ||||
|  static int spansion_quad_enable(struct spi_nor *nor) | ||||
| @@ -936,7 +1082,7 @@ static int micron_quad_enable(struct spi | ||||
|   | ||||
|  	/* set EVCR, enable quad I/O */ | ||||
|  	nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON; | ||||
| -	ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0); | ||||
| +	ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1); | ||||
|  	if (ret < 0) { | ||||
|  		dev_err(nor->dev, "error while writing EVCR register\n"); | ||||
|  		return ret; | ||||
| @@ -965,14 +1111,14 @@ static int set_quad_mode(struct spi_nor | ||||
|  	int status; | ||||
|   | ||||
|  	switch (JEDEC_MFR(info)) { | ||||
| -	case CFI_MFR_MACRONIX: | ||||
| +	case SNOR_MFR_MACRONIX: | ||||
|  		status = macronix_quad_enable(nor); | ||||
|  		if (status) { | ||||
|  			dev_err(nor->dev, "Macronix quad-read not enabled\n"); | ||||
|  			return -EINVAL; | ||||
|  		} | ||||
|  		return status; | ||||
| -	case CFI_MFR_ST: | ||||
| +	case SNOR_MFR_MICRON: | ||||
|  		status = micron_quad_enable(nor); | ||||
|  		if (status) { | ||||
|  			dev_err(nor->dev, "Micron quad-read not enabled\n"); | ||||
| @@ -1004,8 +1150,8 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|  { | ||||
|  	const struct flash_info *info = NULL; | ||||
|  	struct device *dev = nor->dev; | ||||
| -	struct mtd_info *mtd = nor->mtd; | ||||
| -	struct device_node *np = dev->of_node; | ||||
| +	struct mtd_info *mtd = &nor->mtd; | ||||
| +	struct device_node *np = nor->flash_node; | ||||
|  	int ret; | ||||
|  	int i; | ||||
|   | ||||
| @@ -1048,19 +1194,21 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|  	mutex_init(&nor->lock); | ||||
|   | ||||
|  	/* | ||||
| -	 * Atmel, SST and Intel/Numonyx serial nor tend to power | ||||
| -	 * up with the software protection bits set | ||||
| +	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up | ||||
| +	 * with the software protection bits set | ||||
|  	 */ | ||||
|   | ||||
| -	if (JEDEC_MFR(info) == CFI_MFR_ATMEL || | ||||
| -	    JEDEC_MFR(info) == CFI_MFR_INTEL || | ||||
| -	    JEDEC_MFR(info) == CFI_MFR_SST) { | ||||
| +	if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || | ||||
| +	    JEDEC_MFR(info) == SNOR_MFR_INTEL || | ||||
| +	    JEDEC_MFR(info) == SNOR_MFR_SST || | ||||
| +	    JEDEC_MFR(info) == SNOR_MFR_WINBOND) { | ||||
|  		write_enable(nor); | ||||
|  		write_sr(nor, 0); | ||||
|  	} | ||||
|   | ||||
|  	if (!mtd->name) | ||||
|  		mtd->name = dev_name(dev); | ||||
| +	mtd->priv = nor; | ||||
|  	mtd->type = MTD_NORFLASH; | ||||
|  	mtd->writesize = 1; | ||||
|  	mtd->flags = MTD_CAP_NORFLASH; | ||||
| @@ -1068,15 +1216,18 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|  	mtd->_erase = spi_nor_erase; | ||||
|  	mtd->_read = spi_nor_read; | ||||
|   | ||||
| -	/* nor protection support for STmicro chips */ | ||||
| -	if (JEDEC_MFR(info) == CFI_MFR_ST) { | ||||
| +	/* NOR protection support for STmicro/Micron chips and similar */ | ||||
| +	if (JEDEC_MFR(info) == SNOR_MFR_MICRON || | ||||
| +	    JEDEC_MFR(info) == SNOR_MFR_WINBOND) { | ||||
|  		nor->flash_lock = stm_lock; | ||||
|  		nor->flash_unlock = stm_unlock; | ||||
| +		nor->flash_is_locked = stm_is_locked; | ||||
|  	} | ||||
|   | ||||
| -	if (nor->flash_lock && nor->flash_unlock) { | ||||
| +	if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) { | ||||
|  		mtd->_lock = spi_nor_lock; | ||||
|  		mtd->_unlock = spi_nor_unlock; | ||||
| +		mtd->_is_locked = spi_nor_is_locked; | ||||
|  	} | ||||
|   | ||||
|  	/* sst nor chips use AAI word program */ | ||||
| @@ -1163,7 +1314,7 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|  	else if (mtd->size > 0x1000000) { | ||||
|  		/* enable 4-byte addressing if the device exceeds 16MiB */ | ||||
|  		nor->addr_width = 4; | ||||
| -		if (JEDEC_MFR(info) == CFI_MFR_AMD) { | ||||
| +		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) { | ||||
|  			/* Dedicated 4-byte command set */ | ||||
|  			switch (nor->flash_read) { | ||||
|  			case SPI_NOR_QUAD: | ||||
| --- a/include/linux/mtd/spi-nor.h | ||||
| +++ b/include/linux/mtd/spi-nor.h | ||||
| @@ -10,6 +10,23 @@ | ||||
|  #ifndef __LINUX_MTD_SPI_NOR_H | ||||
|  #define __LINUX_MTD_SPI_NOR_H | ||||
|   | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/mtd/cfi.h> | ||||
| + | ||||
| +/* | ||||
| + * Manufacturer IDs | ||||
| + * | ||||
| + * The first byte returned from the flash after sending opcode SPINOR_OP_RDID. | ||||
| + * Sometimes these are the same as CFI IDs, but sometimes they aren't. | ||||
| + */ | ||||
| +#define SNOR_MFR_ATMEL		CFI_MFR_ATMEL | ||||
| +#define SNOR_MFR_INTEL		CFI_MFR_INTEL | ||||
| +#define SNOR_MFR_MICRON		CFI_MFR_ST /* ST Micro <--> Micron */ | ||||
| +#define SNOR_MFR_MACRONIX	CFI_MFR_MACRONIX | ||||
| +#define SNOR_MFR_SPANSION	CFI_MFR_AMD | ||||
| +#define SNOR_MFR_SST		CFI_MFR_SST | ||||
| +#define SNOR_MFR_WINBOND	0xef | ||||
| + | ||||
|  /* | ||||
|   * Note on opcode nomenclature: some opcodes have a format like | ||||
|   * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number | ||||
| @@ -61,24 +78,24 @@ | ||||
|  #define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */ | ||||
|   | ||||
|  /* Status Register bits. */ | ||||
| -#define SR_WIP			1	/* Write in progress */ | ||||
| -#define SR_WEL			2	/* Write enable latch */ | ||||
| +#define SR_WIP			BIT(0)	/* Write in progress */ | ||||
| +#define SR_WEL			BIT(1)	/* Write enable latch */ | ||||
|  /* meaning of other SR_* bits may differ between vendors */ | ||||
| -#define SR_BP0			4	/* Block protect 0 */ | ||||
| -#define SR_BP1			8	/* Block protect 1 */ | ||||
| -#define SR_BP2			0x10	/* Block protect 2 */ | ||||
| -#define SR_SRWD			0x80	/* SR write protect */ | ||||
| +#define SR_BP0			BIT(2)	/* Block protect 0 */ | ||||
| +#define SR_BP1			BIT(3)	/* Block protect 1 */ | ||||
| +#define SR_BP2			BIT(4)	/* Block protect 2 */ | ||||
| +#define SR_SRWD			BIT(7)	/* SR write protect */ | ||||
|   | ||||
| -#define SR_QUAD_EN_MX		0x40	/* Macronix Quad I/O */ | ||||
| +#define SR_QUAD_EN_MX		BIT(6)	/* Macronix Quad I/O */ | ||||
|   | ||||
|  /* Enhanced Volatile Configuration Register bits */ | ||||
| -#define EVCR_QUAD_EN_MICRON    0x80    /* Micron Quad I/O */ | ||||
| +#define EVCR_QUAD_EN_MICRON	BIT(7)	/* Micron Quad I/O */ | ||||
|   | ||||
|  /* Flag Status Register bits */ | ||||
| -#define FSR_READY		0x80 | ||||
| +#define FSR_READY		BIT(7) | ||||
|   | ||||
|  /* Configuration Register bits. */ | ||||
| -#define CR_QUAD_EN_SPAN		0x2	/* Spansion Quad I/O */ | ||||
| +#define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */ | ||||
|   | ||||
|  enum read_mode { | ||||
|  	SPI_NOR_NORMAL = 0, | ||||
| @@ -87,33 +104,6 @@ enum read_mode { | ||||
|  	SPI_NOR_QUAD, | ||||
|  }; | ||||
|   | ||||
| -/** | ||||
| - * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer | ||||
| - * @wren:		command for "Write Enable", or 0x00 for not required | ||||
| - * @cmd:		command for operation | ||||
| - * @cmd_pins:		number of pins to send @cmd (1, 2, 4) | ||||
| - * @addr:		address for operation | ||||
| - * @addr_pins:		number of pins to send @addr (1, 2, 4) | ||||
| - * @addr_width:		number of address bytes | ||||
| - *			(3,4, or 0 for address not required) | ||||
| - * @mode:		mode data | ||||
| - * @mode_pins:		number of pins to send @mode (1, 2, 4) | ||||
| - * @mode_cycles:	number of mode cycles (0 for mode not required) | ||||
| - * @dummy_cycles:	number of dummy cycles (0 for dummy not required) | ||||
| - */ | ||||
| -struct spi_nor_xfer_cfg { | ||||
| -	u8		wren; | ||||
| -	u8		cmd; | ||||
| -	u8		cmd_pins; | ||||
| -	u32		addr; | ||||
| -	u8		addr_pins; | ||||
| -	u8		addr_width; | ||||
| -	u8		mode; | ||||
| -	u8		mode_pins; | ||||
| -	u8		mode_cycles; | ||||
| -	u8		dummy_cycles; | ||||
| -}; | ||||
| - | ||||
|  #define SPI_NOR_MAX_CMD_SIZE	8 | ||||
|  enum spi_nor_ops { | ||||
|  	SPI_NOR_OPS_READ = 0, | ||||
| @@ -127,11 +117,14 @@ enum spi_nor_option_flags { | ||||
|  	SNOR_F_USE_FSR		= BIT(0), | ||||
|  }; | ||||
|   | ||||
| +struct mtd_info; | ||||
| + | ||||
|  /** | ||||
|   * struct spi_nor - Structure for defining a the SPI NOR layer | ||||
|   * @mtd:		point to a mtd_info structure | ||||
|   * @lock:		the lock for the read/write/erase/lock/unlock operations | ||||
|   * @dev:		point to a spi device, or a spi nor controller device. | ||||
| + * @flash_node:		point to a device node describing this flash instance. | ||||
|   * @page_size:		the page size of the SPI NOR | ||||
|   * @addr_width:		number of address bytes | ||||
|   * @erase_opcode:	the opcode for erasing a sector | ||||
| @@ -141,28 +134,28 @@ enum spi_nor_option_flags { | ||||
|   * @flash_read:		the mode of the read | ||||
|   * @sst_write_second:	used by the SST write operation | ||||
|   * @flags:		flag options for the current SPI-NOR (SNOR_F_*) | ||||
| - * @cfg:		used by the read_xfer/write_xfer | ||||
|   * @cmd_buf:		used by the write_reg | ||||
|   * @prepare:		[OPTIONAL] do some preparations for the | ||||
|   *			read/write/erase/lock/unlock operations | ||||
|   * @unprepare:		[OPTIONAL] do some post work after the | ||||
|   *			read/write/erase/lock/unlock operations | ||||
| - * @read_xfer:		[OPTIONAL] the read fundamental primitive | ||||
| - * @write_xfer:		[OPTIONAL] the writefundamental primitive | ||||
|   * @read_reg:		[DRIVER-SPECIFIC] read out the register | ||||
|   * @write_reg:		[DRIVER-SPECIFIC] write data to the register | ||||
|   * @read:		[DRIVER-SPECIFIC] read data from the SPI NOR | ||||
|   * @write:		[DRIVER-SPECIFIC] write data to the SPI NOR | ||||
|   * @erase:		[DRIVER-SPECIFIC] erase a sector of the SPI NOR | ||||
|   *			at the offset @offs | ||||
| - * @lock:		[FLASH-SPECIFIC] lock a region of the SPI NOR | ||||
| - * @unlock:		[FLASH-SPECIFIC] unlock a region of the SPI NOR | ||||
| + * @flash_lock:		[FLASH-SPECIFIC] lock a region of the SPI NOR | ||||
| + * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR | ||||
| + * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is | ||||
| + *			completely locked | ||||
|   * @priv:		the private data | ||||
|   */ | ||||
|  struct spi_nor { | ||||
| -	struct mtd_info		*mtd; | ||||
| +	struct mtd_info		mtd; | ||||
|  	struct mutex		lock; | ||||
|  	struct device		*dev; | ||||
| +	struct device_node	*flash_node; | ||||
|  	u32			page_size; | ||||
|  	u8			addr_width; | ||||
|  	u8			erase_opcode; | ||||
| @@ -172,18 +165,12 @@ struct spi_nor { | ||||
|  	enum read_mode		flash_read; | ||||
|  	bool			sst_write_second; | ||||
|  	u32			flags; | ||||
| -	struct spi_nor_xfer_cfg	cfg; | ||||
|  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE]; | ||||
|   | ||||
|  	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); | ||||
|  	void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); | ||||
| -	int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, | ||||
| -			 u8 *buf, size_t len); | ||||
| -	int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, | ||||
| -			  u8 *buf, size_t len); | ||||
|  	int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); | ||||
| -	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, | ||||
| -			int write_enable); | ||||
| +	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); | ||||
|   | ||||
|  	int (*read)(struct spi_nor *nor, loff_t from, | ||||
|  			size_t len, size_t *retlen, u_char *read_buf); | ||||
| @@ -193,6 +180,7 @@ struct spi_nor { | ||||
|   | ||||
|  	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); | ||||
|  	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); | ||||
| +	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); | ||||
|   | ||||
|  	void *priv; | ||||
|  }; | ||||
| @@ -1,101 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Sun, 24 Jan 2016 01:03:51 +0100 | ||||
| Subject: [PATCH] MIPS: fix cache flushing for highmem pages | ||||
|  | ||||
| Most cache flush ops were no-op for highmem pages. This led to nasty | ||||
| segfaults and (in the case of page_address(page) == NULL) kernel | ||||
| crashes. | ||||
|  | ||||
| Fix this by always flushing highmem pages using kmap/kunmap_atomic | ||||
| around the actual cache flush. This might be a bit inefficient, but at | ||||
| least it's stable. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/arch/mips/mm/cache.c | ||||
| +++ b/arch/mips/mm/cache.c | ||||
| @@ -14,6 +14,7 @@ | ||||
|  #include <linux/sched.h> | ||||
|  #include <linux/syscalls.h> | ||||
|  #include <linux/mm.h> | ||||
| +#include <linux/highmem.h> | ||||
|   | ||||
|  #include <asm/cacheflush.h> | ||||
|  #include <asm/processor.h> | ||||
| @@ -78,18 +79,29 @@ SYSCALL_DEFINE3(cacheflush, unsigned lon | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static void | ||||
| +flush_highmem_page(struct page *page) | ||||
| +{ | ||||
| +	void *addr = kmap_atomic(page); | ||||
| +	flush_data_cache_page((unsigned long)addr); | ||||
| +	kunmap_atomic(addr); | ||||
| +} | ||||
| + | ||||
|  void __flush_dcache_page(struct page *page) | ||||
|  { | ||||
|  	struct address_space *mapping = page_mapping(page); | ||||
|  	unsigned long addr; | ||||
|   | ||||
| -	if (PageHighMem(page)) | ||||
| -		return; | ||||
|  	if (mapping && !mapping_mapped(mapping)) { | ||||
|  		SetPageDcacheDirty(page); | ||||
|  		return; | ||||
|  	} | ||||
|   | ||||
| +	if (PageHighMem(page)) { | ||||
| +		flush_highmem_page(page); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
|  	/* | ||||
|  	 * We could delay the flush for the !page_mapping case too.  But that | ||||
|  	 * case is for exec env/arg pages and those are %99 certainly going to | ||||
| @@ -105,6 +117,11 @@ void __flush_anon_page(struct page *page | ||||
|  { | ||||
|  	unsigned long addr = (unsigned long) page_address(page); | ||||
|   | ||||
| +	if (PageHighMem(page)) { | ||||
| +		flush_highmem_page(page); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
|  	if (pages_do_alias(addr, vmaddr)) { | ||||
|  		if (page_mapped(page) && !Page_dcache_dirty(page)) { | ||||
|  			void *kaddr; | ||||
| @@ -123,8 +140,10 @@ void __flush_icache_page(struct vm_area_ | ||||
|  { | ||||
|  	unsigned long addr; | ||||
|   | ||||
| -	if (PageHighMem(page)) | ||||
| +	if (PageHighMem(page)) { | ||||
| +		flush_highmem_page(page); | ||||
|  		return; | ||||
| +	} | ||||
|   | ||||
|  	addr = (unsigned long) page_address(page); | ||||
|  	flush_data_cache_page(addr); | ||||
| @@ -142,12 +161,17 @@ void __update_cache(struct vm_area_struc | ||||
|  	if (unlikely(!pfn_valid(pfn))) | ||||
|  		return; | ||||
|  	page = pfn_to_page(pfn); | ||||
| -	if (page_mapping(page) && Page_dcache_dirty(page)) { | ||||
| +	if (!Page_dcache_dirty(page) || !page_mapping(page)) | ||||
| +		return; | ||||
| + | ||||
| +	if (PageHighMem(page)) { | ||||
| +		flush_highmem_page(page); | ||||
| +	} else { | ||||
|  		addr = (unsigned long) page_address(page); | ||||
|  		if (exec || pages_do_alias(addr, address & PAGE_MASK)) | ||||
|  			flush_data_cache_page(addr); | ||||
| -		ClearPageDcacheDirty(page); | ||||
|  	} | ||||
| +	ClearPageDcacheDirty(page); | ||||
|  } | ||||
|   | ||||
|  unsigned long _page_cachable_default; | ||||
| @@ -1,82 +0,0 @@ | ||||
| From 1e311820ec3055e3f08e687de6564692a7cec675 Mon Sep 17 00:00:00 2001 | ||||
| From: Florian Fainelli <florian@openwrt.org> | ||||
| Date: Mon, 28 Jan 2013 20:06:29 +0100 | ||||
| Subject: [PATCH 11/12] USB: EHCI: add ignore_oc flag to disable overcurrent | ||||
|  checking | ||||
|  | ||||
| This patch adds an ignore_oc flag which can be set by EHCI controller | ||||
| not supporting or wanting to disable overcurrent checking. The EHCI | ||||
| platform data in include/linux/usb/ehci_pdriver.h is also augmented to | ||||
| take advantage of this new flag. | ||||
|  | ||||
| Signed-off-by: Florian Fainelli <florian@openwrt.org> | ||||
| --- | ||||
|  drivers/usb/host/ehci-hcd.c      |    2 +- | ||||
|  drivers/usb/host/ehci-hub.c      |    4 ++-- | ||||
|  drivers/usb/host/ehci-platform.c |    1 + | ||||
|  drivers/usb/host/ehci.h          |    1 + | ||||
|  include/linux/usb/ehci_pdriver.h |    1 + | ||||
|  5 files changed, 6 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/drivers/usb/host/ehci-hcd.c | ||||
| +++ b/drivers/usb/host/ehci-hcd.c | ||||
| @@ -639,7 +639,7 @@ static int ehci_run (struct usb_hcd *hcd | ||||
|  		"USB %x.%x started, EHCI %x.%02x%s\n", | ||||
|  		((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), | ||||
|  		temp >> 8, temp & 0xff, | ||||
| -		ignore_oc ? ", overcurrent ignored" : ""); | ||||
| +		(ignore_oc || ehci->ignore_oc) ? ", overcurrent ignored" : ""); | ||||
|   | ||||
|  	ehci_writel(ehci, INTR_MASK, | ||||
|  		    &ehci->regs->intr_enable); /* Turn On Interrupts */ | ||||
| --- a/drivers/usb/host/ehci-hub.c | ||||
| +++ b/drivers/usb/host/ehci-hub.c | ||||
| @@ -634,7 +634,7 @@ ehci_hub_status_data (struct usb_hcd *hc | ||||
|  	 * always set, seem to clear PORT_OCC and PORT_CSC when writing to | ||||
|  	 * PORT_POWER; that's surprising, but maybe within-spec. | ||||
|  	 */ | ||||
| -	if (!ignore_oc) | ||||
| +	if (!ignore_oc && !ehci->ignore_oc) | ||||
|  		mask = PORT_CSC | PORT_PEC | PORT_OCC; | ||||
|  	else | ||||
|  		mask = PORT_CSC | PORT_PEC; | ||||
| @@ -996,7 +996,7 @@ int ehci_hub_control( | ||||
|  		if (temp & PORT_PEC) | ||||
|  			status |= USB_PORT_STAT_C_ENABLE << 16; | ||||
|   | ||||
| -		if ((temp & PORT_OCC) && !ignore_oc){ | ||||
| +		if ((temp & PORT_OCC) && (!ignore_oc && !ehci->ignore_oc)){ | ||||
|  			status |= USB_PORT_STAT_C_OVERCURRENT << 16; | ||||
|   | ||||
|  			/* | ||||
| --- a/drivers/usb/host/ehci-platform.c | ||||
| +++ b/drivers/usb/host/ehci-platform.c | ||||
| @@ -251,6 +251,8 @@ static int ehci_platform_probe(struct pl | ||||
|  		hcd->has_tt = 1; | ||||
|  	if (pdata->reset_on_resume) | ||||
|  		priv->reset_on_resume = true; | ||||
| +	if (pdata->ignore_oc) | ||||
| +		ehci->ignore_oc = 1; | ||||
|   | ||||
|  #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO | ||||
|  	if (ehci->big_endian_mmio) { | ||||
| --- a/drivers/usb/host/ehci.h | ||||
| +++ b/drivers/usb/host/ehci.h | ||||
| @@ -227,6 +227,7 @@ struct ehci_hcd {			/* one per controlle | ||||
|  	unsigned		frame_index_bug:1; /* MosChip (AKA NetMos) */ | ||||
|  	unsigned		need_oc_pp_cycle:1; /* MPC834X port power */ | ||||
|  	unsigned		imx28_write_fix:1; /* For Freescale i.MX28 */ | ||||
| +	unsigned		ignore_oc:1; | ||||
|   | ||||
|  	/* required for usb32 quirk */ | ||||
|  	#define OHCI_CTRL_HCFS          (3 << 6) | ||||
| --- a/include/linux/usb/ehci_pdriver.h | ||||
| +++ b/include/linux/usb/ehci_pdriver.h | ||||
| @@ -49,6 +49,7 @@ struct usb_ehci_pdata { | ||||
|  	unsigned	no_io_watchdog:1; | ||||
|  	unsigned	reset_on_resume:1; | ||||
|  	unsigned	dma_mask_64:1; | ||||
| +	unsigned	ignore_oc:1; | ||||
|   | ||||
|  	/* Turn on all power and clocks */ | ||||
|  	int (*power_on)(struct platform_device *pdev); | ||||
| @@ -1,17 +0,0 @@ | ||||
| Fix a regression in the af_packet code that was breaking PPPoE | ||||
|  | ||||
| pppd sends packets with only a header and no payload. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  | ||||
| --- a/net/packet/af_packet.c | ||||
| +++ b/net/packet/af_packet.c | ||||
| @@ -2329,7 +2329,7 @@ static void tpacket_destruct_skb(struct | ||||
|  static bool ll_header_truncated(const struct net_device *dev, int len) | ||||
|  { | ||||
|  	/* net device doesn't like empty head */ | ||||
| -	if (unlikely(len <= dev->hard_header_len)) { | ||||
| +	if (unlikely(len < dev->hard_header_len)) { | ||||
|  		net_warn_ratelimited("%s: packet size is too short (%d <= %d)\n", | ||||
|  				     current->comm, len, dev->hard_header_len); | ||||
|  		return true; | ||||
| @@ -1,86 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Fri, 10 Apr 2015 13:35:29 +0200 | ||||
| Subject: [PATCH] jffs2: use .rename2 and add RENAME_WHITEOUT support | ||||
|  | ||||
| It is required for renames on overlayfs | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/fs/jffs2/dir.c | ||||
| +++ b/fs/jffs2/dir.c | ||||
| @@ -35,7 +35,7 @@ static int jffs2_mkdir (struct inode *,s | ||||
|  static int jffs2_rmdir (struct inode *,struct dentry *); | ||||
|  static int jffs2_mknod (struct inode *,struct dentry *,umode_t,dev_t); | ||||
|  static int jffs2_rename (struct inode *, struct dentry *, | ||||
| -			 struct inode *, struct dentry *); | ||||
| +			 struct inode *, struct dentry *, unsigned int); | ||||
|   | ||||
|  const struct file_operations jffs2_dir_operations = | ||||
|  { | ||||
| @@ -57,7 +57,7 @@ const struct inode_operations jffs2_dir_ | ||||
|  	.mkdir =	jffs2_mkdir, | ||||
|  	.rmdir =	jffs2_rmdir, | ||||
|  	.mknod =	jffs2_mknod, | ||||
| -	.rename =	jffs2_rename, | ||||
| +	.rename2 =	jffs2_rename, | ||||
|  	.get_acl =	jffs2_get_acl, | ||||
|  	.set_acl =	jffs2_set_acl, | ||||
|  	.setattr =	jffs2_setattr, | ||||
| @@ -757,8 +757,27 @@ static int jffs2_mknod (struct inode *di | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static int jffs2_whiteout(struct inode *old_dir, struct dentry *old_dentry) | ||||
| +{ | ||||
| +	struct dentry *wh; | ||||
| +	int err; | ||||
| + | ||||
| +	wh = d_alloc(old_dentry->d_parent, &old_dentry->d_name); | ||||
| +	if (!wh) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	err = jffs2_mknod(old_dir, wh, S_IFCHR | WHITEOUT_MODE, | ||||
| +			  WHITEOUT_DEV); | ||||
| +	if (err) | ||||
| +		return err; | ||||
| + | ||||
| +	d_rehash(wh); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, | ||||
| -			 struct inode *new_dir_i, struct dentry *new_dentry) | ||||
| +			 struct inode *new_dir_i, struct dentry *new_dentry, | ||||
| +			 unsigned int flags) | ||||
|  { | ||||
|  	int ret; | ||||
|  	struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); | ||||
| @@ -766,6 +785,9 @@ static int jffs2_rename (struct inode *o | ||||
|  	uint8_t type; | ||||
|  	uint32_t now; | ||||
|   | ||||
| +	if (flags & ~RENAME_WHITEOUT) | ||||
| +		return -EINVAL; | ||||
| + | ||||
|  	/* The VFS will check for us and prevent trying to rename a | ||||
|  	 * file over a directory and vice versa, but if it's a directory, | ||||
|  	 * the VFS can't check whether the victim is empty. The filesystem | ||||
| @@ -829,9 +851,14 @@ static int jffs2_rename (struct inode *o | ||||
|  	if (d_is_dir(old_dentry) && !victim_f) | ||||
|  		inc_nlink(new_dir_i); | ||||
|   | ||||
| -	/* Unlink the original */ | ||||
| -	ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), | ||||
| -			      old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); | ||||
| +	if (flags & RENAME_WHITEOUT) | ||||
| +		/* Replace with whiteout */ | ||||
| +		ret = jffs2_whiteout(old_dir_i, old_dentry); | ||||
| +	else | ||||
| +		/* Unlink the original */ | ||||
| +		ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), | ||||
| +				      old_dentry->d_name.name, | ||||
| +				      old_dentry->d_name.len, NULL, now); | ||||
|   | ||||
|  	/* We don't touch inode->i_nlink */ | ||||
|   | ||||
| @@ -1,58 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Sat, 25 Apr 2015 12:41:32 +0200 | ||||
| Subject: [PATCH] jffs2: add RENAME_EXCHANGE support | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/fs/jffs2/dir.c | ||||
| +++ b/fs/jffs2/dir.c | ||||
| @@ -785,7 +785,7 @@ static int jffs2_rename (struct inode *o | ||||
|  	uint8_t type; | ||||
|  	uint32_t now; | ||||
|   | ||||
| -	if (flags & ~RENAME_WHITEOUT) | ||||
| +	if (flags & ~(RENAME_WHITEOUT | RENAME_EXCHANGE)) | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	/* The VFS will check for us and prevent trying to rename a | ||||
| @@ -793,7 +793,7 @@ static int jffs2_rename (struct inode *o | ||||
|  	 * the VFS can't check whether the victim is empty. The filesystem | ||||
|  	 * needs to do that for itself. | ||||
|  	 */ | ||||
| -	if (d_really_is_positive(new_dentry)) { | ||||
| +	if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) { | ||||
|  		victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); | ||||
|  		if (d_is_dir(new_dentry)) { | ||||
|  			struct jffs2_full_dirent *fd; | ||||
| @@ -828,7 +828,7 @@ static int jffs2_rename (struct inode *o | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	if (victim_f) { | ||||
| +	if (victim_f && !(flags & RENAME_EXCHANGE)) { | ||||
|  		/* There was a victim. Kill it off nicely */ | ||||
|  		if (d_is_dir(new_dentry)) | ||||
|  			clear_nlink(d_inode(new_dentry)); | ||||
| @@ -854,6 +854,12 @@ static int jffs2_rename (struct inode *o | ||||
|  	if (flags & RENAME_WHITEOUT) | ||||
|  		/* Replace with whiteout */ | ||||
|  		ret = jffs2_whiteout(old_dir_i, old_dentry); | ||||
| +	else if (flags & RENAME_EXCHANGE) | ||||
| +		/* Replace the original */ | ||||
| +		ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i), | ||||
| +				    d_inode(new_dentry)->i_ino, type, | ||||
| +				    old_dentry->d_name.name, old_dentry->d_name.len, | ||||
| +				    now); | ||||
|  	else | ||||
|  		/* Unlink the original */ | ||||
|  		ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), | ||||
| @@ -880,7 +886,7 @@ static int jffs2_rename (struct inode *o | ||||
|  		return ret; | ||||
|  	} | ||||
|   | ||||
| -	if (d_is_dir(old_dentry)) | ||||
| +	if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE)) | ||||
|  		drop_nlink(old_dir_i); | ||||
|   | ||||
|  	new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); | ||||
| @@ -1,54 +0,0 @@ | ||||
| From: Stephen Hemminger <stephen@networkplumber.org> | ||||
| Subject: bridge: allow receiption on disabled port | ||||
|  | ||||
| When an ethernet device is enslaved to a bridge, and the bridge STP | ||||
| detects loss of carrier (or operational state down), then normally | ||||
| packet receiption is blocked. | ||||
|  | ||||
| This breaks control applications like WPA which maybe expecting to | ||||
| receive packets to negotiate to bring link up. The bridge needs to | ||||
| block forwarding packets from these disabled ports, but there is no | ||||
| hard requirement to not allow local packet delivery. | ||||
|  | ||||
| Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  | ||||
| --- a/net/bridge/br_input.c | ||||
| +++ b/net/bridge/br_input.c | ||||
| @@ -211,11 +211,13 @@ EXPORT_SYMBOL_GPL(br_handle_frame_finish | ||||
|  static int br_handle_local_finish(struct sock *sk, struct sk_buff *skb) | ||||
|  { | ||||
|  	struct net_bridge_port *p = br_port_get_rcu(skb->dev); | ||||
| -	u16 vid = 0; | ||||
| +	if (p->state != BR_STATE_DISABLED) { | ||||
| +		u16 vid = 0; | ||||
|   | ||||
| -	/* check if vlan is allowed, to avoid spoofing */ | ||||
| -	if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid)) | ||||
| -		br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false); | ||||
| +		/* check if vlan is allowed, to avoid spoofing */ | ||||
| +		if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid)) | ||||
| +			br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false); | ||||
| +	} | ||||
|  	return 0;	 /* process further */ | ||||
|  } | ||||
|   | ||||
| @@ -289,6 +291,18 @@ rx_handler_result_t br_handle_frame(stru | ||||
|   | ||||
|  forward: | ||||
|  	switch (p->state) { | ||||
| +	case BR_STATE_DISABLED: | ||||
| +		if (ether_addr_equal(p->br->dev->dev_addr, dest)) | ||||
| +			skb->pkt_type = PACKET_HOST; | ||||
| + | ||||
| +		if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, NULL, skb, skb->dev, NULL, | ||||
| +			br_handle_local_finish)) | ||||
| +			break; | ||||
| + | ||||
| +		BR_INPUT_SKB_CB(skb)->brdev = p->br->dev; | ||||
| +		br_pass_frame_up(skb); | ||||
| +		break; | ||||
| + | ||||
|  	case BR_STATE_FORWARDING: | ||||
|  		rhook = rcu_dereference(br_should_route_hook); | ||||
|  		if (rhook) { | ||||
| @@ -1,778 +0,0 @@ | ||||
| From 2c58080407554e1bac8fd50d23cb02420524caed Mon Sep 17 00:00:00 2001 | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Mon, 12 Aug 2013 12:50:22 +0200 | ||||
| Subject: [PATCH] MIPS: partially inline dma ops | ||||
|  | ||||
| Several DMA ops are no-op on many platforms, and the indirection through | ||||
| the mips_dma_map_ops function table is causing the compiler to emit | ||||
| unnecessary code. | ||||
|  | ||||
| Inlining visibly improves network performance in my tests (on a 24Kc | ||||
| based system), and also slightly reduces code size of a few drivers. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  arch/mips/Kconfig                   |   4 + | ||||
|  arch/mips/include/asm/dma-mapping.h | 360 +++++++++++++++++++++++++++++++++++- | ||||
|  arch/mips/mm/dma-default.c          | 163 ++-------------- | ||||
|  3 files changed, 373 insertions(+), 154 deletions(-) | ||||
|  | ||||
| --- a/arch/mips/Kconfig | ||||
| +++ b/arch/mips/Kconfig | ||||
| @@ -1586,6 +1586,7 @@ config CPU_CAVIUM_OCTEON | ||||
|  	select USB_EHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN | ||||
|  	select USB_OHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN | ||||
|  	select MIPS_L1_CACHE_SHIFT_7 | ||||
| +	select SYS_HAS_DMA_OPS | ||||
|  	help | ||||
|  	  The Cavium Octeon processor is a highly integrated chip containing | ||||
|  	  many ethernet hardware widgets for networking tasks. The processor | ||||
| @@ -1881,6 +1882,9 @@ config MIPS_MALTA_PM | ||||
|  	bool | ||||
|  	default y | ||||
|   | ||||
| +config SYS_HAS_DMA_OPS | ||||
| +	bool | ||||
| + | ||||
|  # | ||||
|  # CPU may reorder R->R, R->W, W->R, W->W | ||||
|  # Reordering beyond LL and SC is handled in WEAK_REORDERING_BEYOND_LLSC | ||||
| --- a/arch/mips/include/asm/dma-mapping.h | ||||
| +++ b/arch/mips/include/asm/dma-mapping.h | ||||
| @@ -1,9 +1,16 @@ | ||||
|  #ifndef _ASM_DMA_MAPPING_H | ||||
|  #define _ASM_DMA_MAPPING_H | ||||
|   | ||||
| +#include <linux/kmemcheck.h> | ||||
| +#include <linux/bug.h> | ||||
|  #include <linux/scatterlist.h> | ||||
| +#include <linux/dma-debug.h> | ||||
| +#include <linux/dma-attrs.h> | ||||
| + | ||||
|  #include <asm/dma-coherence.h> | ||||
|  #include <asm/cache.h> | ||||
| +#include <asm/cpu-type.h> | ||||
| +#include <asm-generic/dma-coherent.h> | ||||
|   | ||||
|  #ifndef CONFIG_SGI_IP27 /* Kludge to fix 2.6.39 build for IP27 */ | ||||
|  #include <dma-coherence.h> | ||||
| @@ -11,12 +18,53 @@ | ||||
|   | ||||
|  extern struct dma_map_ops *mips_dma_map_ops; | ||||
|   | ||||
| +void __dma_sync(struct page *page, unsigned long offset, size_t size, | ||||
| +		enum dma_data_direction direction); | ||||
| +void *mips_dma_alloc_coherent(struct device *dev, size_t size, | ||||
| +			      dma_addr_t *dma_handle, gfp_t gfp, | ||||
| +			      struct dma_attrs *attrs); | ||||
| +void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||||
| +			    dma_addr_t dma_handle, struct dma_attrs *attrs); | ||||
| + | ||||
|  static inline struct dma_map_ops *get_dma_ops(struct device *dev) | ||||
|  { | ||||
| +#ifdef CONFIG_SYS_HAS_DMA_OPS | ||||
|  	if (dev && dev->archdata.dma_ops) | ||||
|  		return dev->archdata.dma_ops; | ||||
|  	else | ||||
|  		return mips_dma_map_ops; | ||||
| +#else | ||||
| +	return NULL; | ||||
| +#endif | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * The affected CPUs below in 'cpu_needs_post_dma_flush()' can | ||||
| + * speculatively fill random cachelines with stale data at any time, | ||||
| + * requiring an extra flush post-DMA. | ||||
| + * | ||||
| + * Warning on the terminology - Linux calls an uncached area coherent; | ||||
| + * MIPS terminology calls memory areas with hardware maintained coherency | ||||
| + * coherent. | ||||
| + * | ||||
| + * Note that the R14000 and R16000 should also be checked for in this | ||||
| + * condition.  However this function is only called on non-I/O-coherent | ||||
| + * systems and only the R10000 and R12000 are used in such systems, the | ||||
| + * SGI IP28 Indigo² rsp. SGI IP32 aka O2. | ||||
| + */ | ||||
| +static inline int cpu_needs_post_dma_flush(struct device *dev) | ||||
| +{ | ||||
| +	return !plat_device_is_coherent(dev) && | ||||
| +	       (boot_cpu_type() == CPU_R10000 || | ||||
| +		boot_cpu_type() == CPU_R12000 || | ||||
| +		boot_cpu_type() == CPU_BMIPS5000); | ||||
| +} | ||||
| + | ||||
| +static inline struct page *dma_addr_to_page(struct device *dev, | ||||
| +	dma_addr_t dma_addr) | ||||
| +{ | ||||
| +	return pfn_to_page( | ||||
| +		plat_dma_addr_to_phys(dev, dma_addr) >> PAGE_SHIFT); | ||||
|  } | ||||
|   | ||||
|  static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) | ||||
| @@ -29,9 +77,399 @@ static inline bool dma_capable(struct de | ||||
|   | ||||
|  static inline void dma_mark_clean(void *addr, size_t size) {} | ||||
|   | ||||
| -#include <asm-generic/dma-mapping-common.h> | ||||
| +static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr, | ||||
| +					      size_t size, | ||||
| +					      enum dma_data_direction dir, | ||||
| +					      struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	unsigned long offset = (unsigned long)ptr & ~PAGE_MASK; | ||||
| +	struct page *page = virt_to_page(ptr); | ||||
| +	dma_addr_t addr; | ||||
| + | ||||
| +	kmemcheck_mark_initialized(ptr, size); | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		addr = ops->map_page(dev, page, offset, size, dir, attrs); | ||||
| +	} else { | ||||
| +		if (!plat_device_is_coherent(dev)) | ||||
| +			__dma_sync(page, offset, size, dir); | ||||
| + | ||||
| +		addr = plat_map_dma_mem_page(dev, page) + offset; | ||||
| +	} | ||||
| +	debug_dma_map_page(dev, page, offset, size, dir, addr, true); | ||||
| +	return addr; | ||||
| +} | ||||
| + | ||||
| +static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr, | ||||
| +					  size_t size, | ||||
| +					  enum dma_data_direction dir, | ||||
| +					  struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ops->unmap_page(dev, addr, size, dir, attrs); | ||||
| +	} else { | ||||
| +		if (cpu_needs_post_dma_flush(dev)) | ||||
| +			__dma_sync(dma_addr_to_page(dev, addr), | ||||
| +				   addr & ~PAGE_MASK, size, dir); | ||||
| +		plat_post_dma_flush(dev); | ||||
| +		plat_unmap_dma_mem(dev, addr, size, dir); | ||||
| +	} | ||||
| +	debug_dma_unmap_page(dev, addr, size, dir, true); | ||||
| +} | ||||
| + | ||||
| +/* | ||||
| + * dma_maps_sg_attrs returns 0 on error and > 0 on success. | ||||
| + * It should never return a value < 0. | ||||
| + */ | ||||
| +static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, | ||||
| +				   int nents, enum dma_data_direction dir, | ||||
| +				   struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	int i, ents; | ||||
| +	struct scatterlist *s; | ||||
| + | ||||
| +	for_each_sg(sg, s, nents, i) | ||||
| +		kmemcheck_mark_initialized(sg_virt(s), s->length); | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ents = ops->map_sg(dev, sg, nents, dir, attrs); | ||||
| +	} else { | ||||
| +		for_each_sg(sg, s, nents, i) { | ||||
| +			struct page *page = sg_page(s); | ||||
| + | ||||
| +			if (!plat_device_is_coherent(dev)) | ||||
| +				__dma_sync(page, s->offset, s->length, dir); | ||||
| +#ifdef CONFIG_NEED_SG_DMA_LENGTH | ||||
| +			s->dma_length = s->length; | ||||
| +#endif | ||||
| +			s->dma_address = | ||||
| +				plat_map_dma_mem_page(dev, page) + s->offset; | ||||
| +		} | ||||
| +		ents = nents; | ||||
| +	} | ||||
| +	BUG_ON(ents < 0); | ||||
| +	debug_dma_map_sg(dev, sg, nents, ents, dir); | ||||
| + | ||||
| +	return ents; | ||||
| +} | ||||
| + | ||||
| +static inline void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, | ||||
| +				      int nents, enum dma_data_direction dir, | ||||
| +				      struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	struct scatterlist *s; | ||||
| +	int i; | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	debug_dma_unmap_sg(dev, sg, nents, dir); | ||||
| +	if (ops) { | ||||
| +		ops->unmap_sg(dev, sg, nents, dir, attrs); | ||||
| +		return; | ||||
| +	} | ||||
| +	for_each_sg(sg, s, nents, i) { | ||||
| +		if (!plat_device_is_coherent(dev) && dir != DMA_TO_DEVICE) | ||||
| +			__dma_sync(sg_page(s), s->offset, s->length, dir); | ||||
| +		plat_unmap_dma_mem(dev, s->dma_address, s->length, dir); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||||
| +				      size_t offset, size_t size, | ||||
| +				      enum dma_data_direction dir) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	dma_addr_t addr; | ||||
| + | ||||
| +	kmemcheck_mark_initialized(page_address(page) + offset, size); | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		addr = ops->map_page(dev, page, offset, size, dir, NULL); | ||||
| +	} else { | ||||
| +		if (!plat_device_is_coherent(dev)) | ||||
| +			__dma_sync(page, offset, size, dir); | ||||
| + | ||||
| +		addr = plat_map_dma_mem_page(dev, page) + offset; | ||||
| +	} | ||||
| +	debug_dma_map_page(dev, page, offset, size, dir, addr, false); | ||||
| + | ||||
| +	return addr; | ||||
| +} | ||||
| + | ||||
| +static inline void dma_unmap_page(struct device *dev, dma_addr_t addr, | ||||
| +				  size_t size, enum dma_data_direction dir) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ops->unmap_page(dev, addr, size, dir, NULL); | ||||
| +	} else { | ||||
| +		if (cpu_needs_post_dma_flush(dev)) | ||||
| +			__dma_sync(dma_addr_to_page(dev, addr), | ||||
| +				   addr & ~PAGE_MASK, size, dir); | ||||
| +		plat_post_dma_flush(dev); | ||||
| +		plat_unmap_dma_mem(dev, addr, size, dir); | ||||
| +	} | ||||
| +	debug_dma_unmap_page(dev, addr, size, dir, false); | ||||
| +} | ||||
| + | ||||
| +static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, | ||||
| +					   size_t size, | ||||
| +					   enum dma_data_direction dir) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ops->sync_single_for_cpu(dev, addr, size, dir); | ||||
| +	} else { | ||||
| +		if (cpu_needs_post_dma_flush(dev)) | ||||
| +			__dma_sync(dma_addr_to_page(dev, addr), | ||||
| +				   addr & ~PAGE_MASK, size, dir); | ||||
| +		plat_post_dma_flush(dev); | ||||
| +	} | ||||
| +	debug_dma_sync_single_for_cpu(dev, addr, size, dir); | ||||
| +} | ||||
| + | ||||
| +static inline void dma_sync_single_for_device(struct device *dev, | ||||
| +					      dma_addr_t addr, size_t size, | ||||
| +					      enum dma_data_direction dir) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) | ||||
| +		ops->sync_single_for_device(dev, addr, size, dir); | ||||
| +	else if (!plat_device_is_coherent(dev)) | ||||
| +		__dma_sync(dma_addr_to_page(dev, addr), | ||||
| +			   addr & ~PAGE_MASK, size, dir); | ||||
| +	debug_dma_sync_single_for_device(dev, addr, size, dir); | ||||
| +} | ||||
| + | ||||
| +static inline void dma_sync_single_range_for_cpu(struct device *dev, | ||||
| +						 dma_addr_t addr, | ||||
| +						 unsigned long offset, | ||||
| +						 size_t size, | ||||
| +						 enum dma_data_direction dir) | ||||
| +{ | ||||
| +	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ops->sync_single_for_cpu(dev, addr + offset, size, dir); | ||||
| +	} else { | ||||
| +		if (cpu_needs_post_dma_flush(dev)) | ||||
| +			__dma_sync(dma_addr_to_page(dev, addr + offset), | ||||
| +				   (addr + offset) & ~PAGE_MASK, size, dir); | ||||
| +		plat_post_dma_flush(dev); | ||||
| +	} | ||||
| + | ||||
| +	debug_dma_sync_single_range_for_cpu(dev, addr, offset, size, dir); | ||||
| +} | ||||
| + | ||||
| +static inline void dma_sync_single_range_for_device(struct device *dev, | ||||
| +						    dma_addr_t addr, | ||||
| +						    unsigned long offset, | ||||
| +						    size_t size, | ||||
| +						    enum dma_data_direction dir) | ||||
| +{ | ||||
| +	const struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) | ||||
| +		ops->sync_single_for_device(dev, addr + offset, size, dir); | ||||
| +	else if (!plat_device_is_coherent(dev)) | ||||
| +		__dma_sync(dma_addr_to_page(dev, addr + offset), | ||||
| +			   (addr + offset) & ~PAGE_MASK, size, dir); | ||||
| +	debug_dma_sync_single_range_for_device(dev, addr, offset, size, dir); | ||||
| +} | ||||
| + | ||||
| +static inline void | ||||
| +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | ||||
| +		    int nelems, enum dma_data_direction dir) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	struct scatterlist *s; | ||||
| +	int i; | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ops->sync_sg_for_cpu(dev, sg, nelems, dir); | ||||
| +	} else if (cpu_needs_post_dma_flush(dev)) { | ||||
| +		for_each_sg(sg, s, nelems, i) | ||||
| +			__dma_sync(sg_page(s), s->offset, s->length, dir); | ||||
| +	} | ||||
| +	plat_post_dma_flush(dev); | ||||
| +	debug_dma_sync_sg_for_cpu(dev, sg, nelems, dir); | ||||
| +} | ||||
| + | ||||
| +static inline void | ||||
| +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | ||||
| +		       int nelems, enum dma_data_direction dir) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	struct scatterlist *s; | ||||
| +	int i; | ||||
| + | ||||
| +	BUG_ON(!valid_dma_direction(dir)); | ||||
| +	if (ops) { | ||||
| +		ops->sync_sg_for_device(dev, sg, nelems, dir); | ||||
| +	} else if (!plat_device_is_coherent(dev)) { | ||||
| +		for_each_sg(sg, s, nelems, i) | ||||
| +			__dma_sync(sg_page(s), s->offset, s->length, dir); | ||||
| +	} | ||||
| +	debug_dma_sync_sg_for_device(dev, sg, nelems, dir); | ||||
| + | ||||
| +} | ||||
| + | ||||
| +#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, NULL) | ||||
| +#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, NULL) | ||||
| +#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, NULL) | ||||
| +#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, NULL) | ||||
| + | ||||
| +extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, | ||||
| +			   void *cpu_addr, dma_addr_t dma_addr, size_t size); | ||||
| + | ||||
| +/** | ||||
| + * dma_mmap_attrs - map a coherent DMA allocation into user space | ||||
| + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices | ||||
| + * @vma: vm_area_struct describing requested user mapping | ||||
| + * @cpu_addr: kernel CPU-view address returned from dma_alloc_attrs | ||||
| + * @handle: device-view address returned from dma_alloc_attrs | ||||
| + * @size: size of memory originally requested in dma_alloc_attrs | ||||
| + * @attrs: attributes of mapping properties requested in dma_alloc_attrs | ||||
| + * | ||||
| + * Map a coherent DMA buffer previously allocated by dma_alloc_attrs | ||||
| + * into user space.  The coherent DMA buffer must not be freed by the | ||||
| + * driver until the user space mapping has been released. | ||||
| + */ | ||||
| +static inline int | ||||
| +dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, | ||||
| +	       dma_addr_t dma_addr, size_t size, struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	BUG_ON(!ops); | ||||
| +	if (ops && ops->mmap) | ||||
| +		return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs); | ||||
| +	return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size); | ||||
| +} | ||||
| + | ||||
| +#define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, NULL) | ||||
| + | ||||
| +int | ||||
| +dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, | ||||
| +		       void *cpu_addr, dma_addr_t dma_addr, size_t size); | ||||
| + | ||||
| +static inline int | ||||
| +dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt, void *cpu_addr, | ||||
| +		      dma_addr_t dma_addr, size_t size, struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	BUG_ON(!ops); | ||||
| +	if (ops && ops->get_sgtable) | ||||
| +		return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size, | ||||
| +					attrs); | ||||
| +	return dma_common_get_sgtable(dev, sgt, cpu_addr, dma_addr, size); | ||||
| +} | ||||
| + | ||||
| +#define dma_get_sgtable(d, t, v, h, s) dma_get_sgtable_attrs(d, t, v, h, s, NULL) | ||||
| + | ||||
| +static inline int dma_supported(struct device *dev, u64 mask) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| +	if (ops) | ||||
| +		return ops->dma_supported(dev, mask); | ||||
| +	return plat_dma_supported(dev, mask); | ||||
| +} | ||||
| + | ||||
| +static inline int dma_mapping_error(struct device *dev, u64 mask) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	debug_dma_mapping_error(dev, mask); | ||||
| +	if (ops) | ||||
| +		return ops->mapping_error(dev, mask); | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static inline int | ||||
| +dma_set_mask(struct device *dev, u64 mask) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	if(!dev->dma_mask || !dma_supported(dev, mask)) | ||||
| +		return -EIO; | ||||
| + | ||||
| +	if (ops && ops->set_dma_mask) | ||||
| +		return ops->set_dma_mask(dev, mask); | ||||
| + | ||||
| +	*dev->dma_mask = mask; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
|   | ||||
|  extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, | ||||
|  	       enum dma_data_direction direction); | ||||
|   | ||||
| +#define dma_alloc_coherent(d,s,h,f)	dma_alloc_attrs(d,s,h,f,NULL) | ||||
| + | ||||
| +static inline void *dma_alloc_attrs(struct device *dev, size_t size, | ||||
| +				    dma_addr_t *dma_handle, gfp_t gfp, | ||||
| +				    struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	void *ret; | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	if (ops) | ||||
| +		ret = ops->alloc(dev, size, dma_handle, gfp, attrs); | ||||
| +	else | ||||
| +		ret = mips_dma_alloc_coherent(dev, size, dma_handle, gfp, | ||||
| +					      attrs); | ||||
| + | ||||
| +	debug_dma_alloc_coherent(dev, size, *dma_handle, ret); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) | ||||
| + | ||||
| +static inline void dma_free_attrs(struct device *dev, size_t size, | ||||
| +				  void *vaddr, dma_addr_t dma_handle, | ||||
| +				  struct dma_attrs *attrs) | ||||
| +{ | ||||
| +	struct dma_map_ops *ops = get_dma_ops(dev); | ||||
| + | ||||
| +	if (ops) | ||||
| +		ops->free(dev, size, vaddr, dma_handle, attrs); | ||||
| +	else | ||||
| +		mips_dma_free_coherent(dev, size, vaddr, dma_handle, attrs); | ||||
| + | ||||
| +	debug_dma_free_coherent(dev, size, vaddr, dma_handle); | ||||
| +} | ||||
| + | ||||
| +static inline void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||||
| +		dma_addr_t *dma_handle, gfp_t gfp) | ||||
| +{ | ||||
| +	DEFINE_DMA_ATTRS(attrs); | ||||
| + | ||||
| +	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); | ||||
| +	return dma_alloc_attrs(dev, size, dma_handle, gfp, &attrs); | ||||
| +} | ||||
| + | ||||
| +static inline void dma_free_noncoherent(struct device *dev, size_t size, | ||||
| +		void *cpu_addr, dma_addr_t dma_handle) | ||||
| +{ | ||||
| +	DEFINE_DMA_ATTRS(attrs); | ||||
| + | ||||
| +	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs); | ||||
| +	dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs); | ||||
| +} | ||||
| + | ||||
| + | ||||
|  #endif /* _ASM_DMA_MAPPING_H */ | ||||
| --- a/arch/mips/mm/dma-default.c | ||||
| +++ b/arch/mips/mm/dma-default.c | ||||
| @@ -46,35 +46,6 @@ static int __init setnocoherentio(char * | ||||
|  early_param("nocoherentio", setnocoherentio); | ||||
|  #endif | ||||
|   | ||||
| -static inline struct page *dma_addr_to_page(struct device *dev, | ||||
| -	dma_addr_t dma_addr) | ||||
| -{ | ||||
| -	return pfn_to_page( | ||||
| -		plat_dma_addr_to_phys(dev, dma_addr) >> PAGE_SHIFT); | ||||
| -} | ||||
| - | ||||
| -/* | ||||
| - * The affected CPUs below in 'cpu_needs_post_dma_flush()' can | ||||
| - * speculatively fill random cachelines with stale data at any time, | ||||
| - * requiring an extra flush post-DMA. | ||||
| - * | ||||
| - * Warning on the terminology - Linux calls an uncached area coherent; | ||||
| - * MIPS terminology calls memory areas with hardware maintained coherency | ||||
| - * coherent. | ||||
| - * | ||||
| - * Note that the R14000 and R16000 should also be checked for in this | ||||
| - * condition.  However this function is only called on non-I/O-coherent | ||||
| - * systems and only the R10000 and R12000 are used in such systems, the | ||||
| - * SGI IP28 Indigo² rsp. SGI IP32 aka O2. | ||||
| - */ | ||||
| -static inline int cpu_needs_post_dma_flush(struct device *dev) | ||||
| -{ | ||||
| -	return !plat_device_is_coherent(dev) && | ||||
| -	       (boot_cpu_type() == CPU_R10000 || | ||||
| -		boot_cpu_type() == CPU_R12000 || | ||||
| -		boot_cpu_type() == CPU_BMIPS5000); | ||||
| -} | ||||
| - | ||||
|  static gfp_t massage_gfp_flags(const struct device *dev, gfp_t gfp) | ||||
|  { | ||||
|  	gfp_t dma_flag; | ||||
| @@ -129,7 +100,7 @@ static void *mips_dma_alloc_noncoherent( | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static void *mips_dma_alloc_coherent(struct device *dev, size_t size, | ||||
| +void *mips_dma_alloc_coherent(struct device *dev, size_t size, | ||||
|  	dma_addr_t * dma_handle, gfp_t gfp, struct dma_attrs *attrs) | ||||
|  { | ||||
|  	void *ret; | ||||
| @@ -165,6 +136,7 @@ static void *mips_dma_alloc_coherent(str | ||||
|   | ||||
|  	return ret; | ||||
|  } | ||||
| +EXPORT_SYMBOL(mips_dma_alloc_coherent); | ||||
|   | ||||
|   | ||||
|  static void mips_dma_free_noncoherent(struct device *dev, size_t size, | ||||
| @@ -174,7 +146,7 @@ static void mips_dma_free_noncoherent(st | ||||
|  	free_pages((unsigned long) vaddr, get_order(size)); | ||||
|  } | ||||
|   | ||||
| -static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||||
| +void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||||
|  	dma_addr_t dma_handle, struct dma_attrs *attrs) | ||||
|  { | ||||
|  	unsigned long addr = (unsigned long) vaddr; | ||||
| @@ -196,40 +168,7 @@ static void mips_dma_free_coherent(struc | ||||
|  	if (!dma_release_from_contiguous(dev, page, count)) | ||||
|  		__free_pages(page, get_order(size)); | ||||
|  } | ||||
| - | ||||
| -static int mips_dma_mmap(struct device *dev, struct vm_area_struct *vma, | ||||
| -	void *cpu_addr, dma_addr_t dma_addr, size_t size, | ||||
| -	struct dma_attrs *attrs) | ||||
| -{ | ||||
| -	unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||||
| -	unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; | ||||
| -	unsigned long addr = (unsigned long)cpu_addr; | ||||
| -	unsigned long off = vma->vm_pgoff; | ||||
| -	unsigned long pfn; | ||||
| -	int ret = -ENXIO; | ||||
| - | ||||
| -	if (!plat_device_is_coherent(dev) && !hw_coherentio) | ||||
| -		addr = CAC_ADDR(addr); | ||||
| - | ||||
| -	pfn = page_to_pfn(virt_to_page((void *)addr)); | ||||
| - | ||||
| -	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) | ||||
| -		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | ||||
| -	else | ||||
| -		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||||
| - | ||||
| -	if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) | ||||
| -		return ret; | ||||
| - | ||||
| -	if (off < count && user_count <= (count - off)) { | ||||
| -		ret = remap_pfn_range(vma, vma->vm_start, | ||||
| -				      pfn + off, | ||||
| -				      user_count << PAGE_SHIFT, | ||||
| -				      vma->vm_page_prot); | ||||
| -	} | ||||
| - | ||||
| -	return ret; | ||||
| -} | ||||
| +EXPORT_SYMBOL(mips_dma_free_coherent); | ||||
|   | ||||
|  static inline void __dma_sync_virtual(void *addr, size_t size, | ||||
|  	enum dma_data_direction direction) | ||||
| @@ -258,7 +197,7 @@ static inline void __dma_sync_virtual(vo | ||||
|   * If highmem is not configured then the bulk of this loop gets | ||||
|   * optimized out. | ||||
|   */ | ||||
| -static inline void __dma_sync(struct page *page, | ||||
| +void __dma_sync(struct page *page, | ||||
|  	unsigned long offset, size_t size, enum dma_data_direction direction) | ||||
|  { | ||||
|  	size_t left = size; | ||||
| @@ -288,120 +227,7 @@ static inline void __dma_sync(struct pag | ||||
|  		left -= len; | ||||
|  	} while (left); | ||||
|  } | ||||
| - | ||||
| -static void mips_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, | ||||
| -	size_t size, enum dma_data_direction direction, struct dma_attrs *attrs) | ||||
| -{ | ||||
| -	if (cpu_needs_post_dma_flush(dev)) | ||||
| -		__dma_sync(dma_addr_to_page(dev, dma_addr), | ||||
| -			   dma_addr & ~PAGE_MASK, size, direction); | ||||
| -	plat_post_dma_flush(dev); | ||||
| -	plat_unmap_dma_mem(dev, dma_addr, size, direction); | ||||
| -} | ||||
| - | ||||
| -static int mips_dma_map_sg(struct device *dev, struct scatterlist *sglist, | ||||
| -	int nents, enum dma_data_direction direction, struct dma_attrs *attrs) | ||||
| -{ | ||||
| -	int i; | ||||
| -	struct scatterlist *sg; | ||||
| - | ||||
| -	for_each_sg(sglist, sg, nents, i) { | ||||
| -		if (!plat_device_is_coherent(dev)) | ||||
| -			__dma_sync(sg_page(sg), sg->offset, sg->length, | ||||
| -				   direction); | ||||
| -#ifdef CONFIG_NEED_SG_DMA_LENGTH | ||||
| -		sg->dma_length = sg->length; | ||||
| -#endif | ||||
| -		sg->dma_address = plat_map_dma_mem_page(dev, sg_page(sg)) + | ||||
| -				  sg->offset; | ||||
| -	} | ||||
| - | ||||
| -	return nents; | ||||
| -} | ||||
| - | ||||
| -static dma_addr_t mips_dma_map_page(struct device *dev, struct page *page, | ||||
| -	unsigned long offset, size_t size, enum dma_data_direction direction, | ||||
| -	struct dma_attrs *attrs) | ||||
| -{ | ||||
| -	if (!plat_device_is_coherent(dev)) | ||||
| -		__dma_sync(page, offset, size, direction); | ||||
| - | ||||
| -	return plat_map_dma_mem_page(dev, page) + offset; | ||||
| -} | ||||
| - | ||||
| -static void mips_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | ||||
| -	int nhwentries, enum dma_data_direction direction, | ||||
| -	struct dma_attrs *attrs) | ||||
| -{ | ||||
| -	int i; | ||||
| -	struct scatterlist *sg; | ||||
| - | ||||
| -	for_each_sg(sglist, sg, nhwentries, i) { | ||||
| -		if (!plat_device_is_coherent(dev) && | ||||
| -		    direction != DMA_TO_DEVICE) | ||||
| -			__dma_sync(sg_page(sg), sg->offset, sg->length, | ||||
| -				   direction); | ||||
| -		plat_unmap_dma_mem(dev, sg->dma_address, sg->length, direction); | ||||
| -	} | ||||
| -} | ||||
| - | ||||
| -static void mips_dma_sync_single_for_cpu(struct device *dev, | ||||
| -	dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) | ||||
| -{ | ||||
| -	if (cpu_needs_post_dma_flush(dev)) | ||||
| -		__dma_sync(dma_addr_to_page(dev, dma_handle), | ||||
| -			   dma_handle & ~PAGE_MASK, size, direction); | ||||
| -	plat_post_dma_flush(dev); | ||||
| -} | ||||
| - | ||||
| -static void mips_dma_sync_single_for_device(struct device *dev, | ||||
| -	dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) | ||||
| -{ | ||||
| -	if (!plat_device_is_coherent(dev)) | ||||
| -		__dma_sync(dma_addr_to_page(dev, dma_handle), | ||||
| -			   dma_handle & ~PAGE_MASK, size, direction); | ||||
| -} | ||||
| - | ||||
| -static void mips_dma_sync_sg_for_cpu(struct device *dev, | ||||
| -	struct scatterlist *sglist, int nelems, | ||||
| -	enum dma_data_direction direction) | ||||
| -{ | ||||
| -	int i; | ||||
| -	struct scatterlist *sg; | ||||
| - | ||||
| -	if (cpu_needs_post_dma_flush(dev)) { | ||||
| -		for_each_sg(sglist, sg, nelems, i) { | ||||
| -			__dma_sync(sg_page(sg), sg->offset, sg->length, | ||||
| -				   direction); | ||||
| -		} | ||||
| -	} | ||||
| -	plat_post_dma_flush(dev); | ||||
| -} | ||||
| - | ||||
| -static void mips_dma_sync_sg_for_device(struct device *dev, | ||||
| -	struct scatterlist *sglist, int nelems, | ||||
| -	enum dma_data_direction direction) | ||||
| -{ | ||||
| -	int i; | ||||
| -	struct scatterlist *sg; | ||||
| - | ||||
| -	if (!plat_device_is_coherent(dev)) { | ||||
| -		for_each_sg(sglist, sg, nelems, i) { | ||||
| -			__dma_sync(sg_page(sg), sg->offset, sg->length, | ||||
| -				   direction); | ||||
| -		} | ||||
| -	} | ||||
| -} | ||||
| - | ||||
| -int mips_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) | ||||
| -{ | ||||
| -	return 0; | ||||
| -} | ||||
| - | ||||
| -int mips_dma_supported(struct device *dev, u64 mask) | ||||
| -{ | ||||
| -	return plat_dma_supported(dev, mask); | ||||
| -} | ||||
| +EXPORT_SYMBOL(__dma_sync); | ||||
|   | ||||
|  void dma_cache_sync(struct device *dev, void *vaddr, size_t size, | ||||
|  			 enum dma_data_direction direction) | ||||
| @@ -414,24 +240,10 @@ void dma_cache_sync(struct device *dev, | ||||
|   | ||||
|  EXPORT_SYMBOL(dma_cache_sync); | ||||
|   | ||||
| -static struct dma_map_ops mips_default_dma_map_ops = { | ||||
| -	.alloc = mips_dma_alloc_coherent, | ||||
| -	.free = mips_dma_free_coherent, | ||||
| -	.mmap = mips_dma_mmap, | ||||
| -	.map_page = mips_dma_map_page, | ||||
| -	.unmap_page = mips_dma_unmap_page, | ||||
| -	.map_sg = mips_dma_map_sg, | ||||
| -	.unmap_sg = mips_dma_unmap_sg, | ||||
| -	.sync_single_for_cpu = mips_dma_sync_single_for_cpu, | ||||
| -	.sync_single_for_device = mips_dma_sync_single_for_device, | ||||
| -	.sync_sg_for_cpu = mips_dma_sync_sg_for_cpu, | ||||
| -	.sync_sg_for_device = mips_dma_sync_sg_for_device, | ||||
| -	.mapping_error = mips_dma_mapping_error, | ||||
| -	.dma_supported = mips_dma_supported | ||||
| -}; | ||||
| - | ||||
| -struct dma_map_ops *mips_dma_map_ops = &mips_default_dma_map_ops; | ||||
| +#ifdef CONFIG_SYS_HAS_DMA_OPS | ||||
| +struct dma_map_ops *mips_dma_map_ops = NULL; | ||||
|  EXPORT_SYMBOL(mips_dma_map_ops); | ||||
| +#endif | ||||
|   | ||||
|  #define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16) | ||||
|   | ||||
| @@ -1,183 +0,0 @@ | ||||
| From 3e7056c3a369e9ef9ca804bc626b60ef6b62ee27 Mon Sep 17 00:00:00 2001 | ||||
| From: Hauke Mehrtens <hauke@hauke-m.de> | ||||
| Date: Sun, 17 May 2015 18:48:38 +0200 | ||||
| Subject: [PATCH 2/3] mtd: part: add generic parsing of linux,part-probe | ||||
|  | ||||
| This moves the linux,part-probe device tree parsing code from | ||||
| physmap_of.c to mtdpart.c. Now all drivers can use this feature by just | ||||
| providing a reference to their device tree node in struct | ||||
| mtd_part_parser_data. | ||||
|  | ||||
| Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> | ||||
| --- | ||||
|  Documentation/devicetree/bindings/mtd/nand.txt | 16 +++++++++ | ||||
|  drivers/mtd/maps/physmap_of.c                  | 46 +------------------------- | ||||
|  drivers/mtd/mtdpart.c                          | 45 +++++++++++++++++++++++++ | ||||
|  3 files changed, 62 insertions(+), 45 deletions(-) | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/mtd/nand.txt | ||||
| +++ b/Documentation/devicetree/bindings/mtd/nand.txt | ||||
| @@ -12,6 +12,22 @@ | ||||
|  - nand-ecc-step-size: integer representing the number of data bytes | ||||
|  		      that are covered by a single ECC step. | ||||
|   | ||||
| +- linux,part-probe: list of name as strings of the partition parser | ||||
| +		    which should be used to parse the partition table. | ||||
| +		    They will be tried in the specified ordering and | ||||
| +		    the next one will be used if the previous one | ||||
| +		    failed. | ||||
| + | ||||
| +		    Example: linux,part-probe = "cmdlinepart", "ofpart"; | ||||
| + | ||||
| +		    This is also the default value, which will be used | ||||
| +		    if this attribute is not specified. It could be | ||||
| +		    that the flash driver in use overwrote the default | ||||
| +		    value and uses some other default. | ||||
| + | ||||
| +		    Possible values are: bcm47xxpart, afs, ar7part, | ||||
| +		    ofoldpart, ofpart, bcm63xxpart, RedBoot, cmdlinepart | ||||
| + | ||||
|  The ECC strength and ECC step size properties define the correction capability | ||||
|  of a controller. Together, they say a controller can correct "{strength} bit | ||||
|  errors per {size} bytes". | ||||
| --- a/drivers/mtd/maps/physmap_of.c | ||||
| +++ b/drivers/mtd/maps/physmap_of.c | ||||
| @@ -112,47 +112,9 @@ static struct mtd_info *obsolete_probe(s | ||||
|  static const char * const part_probe_types_def[] = { | ||||
|  	"cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL }; | ||||
|   | ||||
| -static const char * const *of_get_probes(struct device_node *dp) | ||||
| -{ | ||||
| -	const char *cp; | ||||
| -	int cplen; | ||||
| -	unsigned int l; | ||||
| -	unsigned int count; | ||||
| -	const char **res; | ||||
| - | ||||
| -	cp = of_get_property(dp, "linux,part-probe", &cplen); | ||||
| -	if (cp == NULL) | ||||
| -		return part_probe_types_def; | ||||
| - | ||||
| -	count = 0; | ||||
| -	for (l = 0; l != cplen; l++) | ||||
| -		if (cp[l] == 0) | ||||
| -			count++; | ||||
| - | ||||
| -	res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL); | ||||
| -	if (!res) | ||||
| -		return NULL; | ||||
| -	count = 0; | ||||
| -	while (cplen > 0) { | ||||
| -		res[count] = cp; | ||||
| -		l = strlen(cp) + 1; | ||||
| -		cp += l; | ||||
| -		cplen -= l; | ||||
| -		count++; | ||||
| -	} | ||||
| -	return res; | ||||
| -} | ||||
| - | ||||
| -static void of_free_probes(const char * const *probes) | ||||
| -{ | ||||
| -	if (probes != part_probe_types_def) | ||||
| -		kfree(probes); | ||||
| -} | ||||
| - | ||||
|  static const struct of_device_id of_flash_match[]; | ||||
|  static int of_flash_probe(struct platform_device *dev) | ||||
|  { | ||||
| -	const char * const *part_probe_types; | ||||
|  	const struct of_device_id *match; | ||||
|  	struct device_node *dp = dev->dev.of_node; | ||||
|  	struct resource res; | ||||
| @@ -312,14 +274,8 @@ static int of_flash_probe(struct platfor | ||||
|  		goto err_out; | ||||
|   | ||||
|  	ppdata.of_node = dp; | ||||
| -	part_probe_types = of_get_probes(dp); | ||||
| -	if (!part_probe_types) { | ||||
| -		err = -ENOMEM; | ||||
| -		goto err_out; | ||||
| -	} | ||||
| -	mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata, | ||||
| +	mtd_device_parse_register(info->cmtd, part_probe_types_def, &ppdata, | ||||
|  			NULL, 0); | ||||
| -	of_free_probes(part_probe_types); | ||||
|   | ||||
|  	kfree(mtd_list); | ||||
|   | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -29,6 +29,7 @@ | ||||
|  #include <linux/kmod.h> | ||||
|  #include <linux/mtd/mtd.h> | ||||
|  #include <linux/mtd/partitions.h> | ||||
| +#include <linux/of.h> | ||||
|  #include <linux/err.h> | ||||
|  #include <linux/kconfig.h> | ||||
|   | ||||
| @@ -719,6 +720,42 @@ void deregister_mtd_parser(struct mtd_pa | ||||
|  EXPORT_SYMBOL_GPL(deregister_mtd_parser); | ||||
|   | ||||
|  /* | ||||
| + * Parses the linux,part-probe device tree property. | ||||
| + * When a non null value is returned it has to be freed with kfree() by | ||||
| + * the caller. | ||||
| + */ | ||||
| +static const char * const *of_get_probes(struct device_node *dp) | ||||
| +{ | ||||
| +	const char *cp; | ||||
| +	int cplen; | ||||
| +	unsigned int l; | ||||
| +	unsigned int count; | ||||
| +	const char **res; | ||||
| + | ||||
| +	cp = of_get_property(dp, "linux,part-probe", &cplen); | ||||
| +	if (cp == NULL) | ||||
| +		return NULL; | ||||
| + | ||||
| +	count = 0; | ||||
| +	for (l = 0; l != cplen; l++) | ||||
| +		if (cp[l] == 0) | ||||
| +			count++; | ||||
| + | ||||
| +	res = kzalloc((count + 1) * sizeof(*res), GFP_KERNEL); | ||||
| +	if (!res) | ||||
| +		return NULL; | ||||
| +	count = 0; | ||||
| +	while (cplen > 0) { | ||||
| +		res[count] = cp; | ||||
| +		l = strlen(cp) + 1; | ||||
| +		cp += l; | ||||
| +		cplen -= l; | ||||
| +		count++; | ||||
| +	} | ||||
| +	return res; | ||||
| +} | ||||
| + | ||||
| +/* | ||||
|   * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you | ||||
|   * are changing this array! | ||||
|   */ | ||||
| @@ -754,6 +791,13 @@ int parse_mtd_partitions(struct mtd_info | ||||
|  { | ||||
|  	struct mtd_part_parser *parser; | ||||
|  	int ret = 0; | ||||
| +	const char *const *types_of = NULL; | ||||
| + | ||||
| +	if (data && data->of_node) { | ||||
| +		types_of = of_get_probes(data->of_node); | ||||
| +		if (types_of != NULL) | ||||
| +			types = types_of; | ||||
| +	} | ||||
|   | ||||
|  	if (!types) | ||||
|  		types = default_mtd_part_types; | ||||
| @@ -772,6 +816,7 @@ int parse_mtd_partitions(struct mtd_info | ||||
|  			break; | ||||
|  		} | ||||
|  	} | ||||
| +	kfree(types_of); | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Thu, 26 Nov 2015 17:03:46 +0100 | ||||
| Subject: [PATCH] Revert "mtd: spi-nor: disable protection for Winbond flash at | ||||
|  startup" | ||||
|  | ||||
| This reverts commit c6fc2171b249e73745c497b578b417a2946f1b2f. | ||||
|  | ||||
| This commit is breaking read access on at least s25fl064k, but also | ||||
| possibly other Spansion flash chips. | ||||
|  | ||||
| Any mtd read seems to succeed, but simply returns a zero-filled buffer. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/mtd/spi-nor/spi-nor.c | ||||
| +++ b/drivers/mtd/spi-nor/spi-nor.c | ||||
| @@ -1194,14 +1194,13 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|  	mutex_init(&nor->lock); | ||||
|   | ||||
|  	/* | ||||
| -	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up | ||||
| -	 * with the software protection bits set | ||||
| +	 * Atmel, SST and Intel/Numonyx serial nor tend to power | ||||
| +	 * up with the software protection bits set | ||||
|  	 */ | ||||
|   | ||||
|  	if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || | ||||
|  	    JEDEC_MFR(info) == SNOR_MFR_INTEL || | ||||
| -	    JEDEC_MFR(info) == SNOR_MFR_SST || | ||||
| -	    JEDEC_MFR(info) == SNOR_MFR_WINBOND) { | ||||
| +	    JEDEC_MFR(info) == SNOR_MFR_SST) { | ||||
|  		write_enable(nor); | ||||
|  		write_sr(nor, 0); | ||||
|  	} | ||||
| @@ -1,38 +0,0 @@ | ||||
| From 72fc448c4c970bdbba604ab340f16080b4f3bb17 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Thu, 26 Nov 2015 08:52:09 +0100 | ||||
| Subject: [PATCH] mtd: spi-nor: include mtd.h header for struct mtd_info | ||||
|  definition | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| So far struct spi_nor was using just a pointer to struct mtd_info so it | ||||
| wasn't needed to have it fully defined there. After recent change we | ||||
| embed whole struct so we need to include a proper header. | ||||
|  | ||||
| Fixes: 1976367173a4 ("mtd: spi-nor: embed struct mtd_info within struct spi_nor") | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| --- | ||||
|  include/linux/mtd/spi-nor.h | 3 +-- | ||||
|  1 file changed, 1 insertion(+), 2 deletions(-) | ||||
|  | ||||
| --- a/include/linux/mtd/spi-nor.h | ||||
| +++ b/include/linux/mtd/spi-nor.h | ||||
| @@ -12,6 +12,7 @@ | ||||
|   | ||||
|  #include <linux/bitops.h> | ||||
|  #include <linux/mtd/cfi.h> | ||||
| +#include <linux/mtd/mtd.h> | ||||
|   | ||||
|  /* | ||||
|   * Manufacturer IDs | ||||
| @@ -117,8 +118,6 @@ enum spi_nor_option_flags { | ||||
|  	SNOR_F_USE_FSR		= BIT(0), | ||||
|  }; | ||||
|   | ||||
| -struct mtd_info; | ||||
| - | ||||
|  /** | ||||
|   * struct spi_nor - Structure for defining a the SPI NOR layer | ||||
|   * @mtd:		point to a mtd_info structure | ||||
| @@ -1,33 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Sat, 5 Dec 2015 02:03:32 +0100 | ||||
| Subject: [PATCH] mtd: bcm47xxpart: limit scanned flash area on BCM47XX (MIPS) | ||||
|  only | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| We allowed using bcm47xxpart on BCM5301X arch with commit: | ||||
| 9e3afa5f5c7 ("mtd: bcm47xxpart: allow enabling on ARCH_BCM_5301X") | ||||
|  | ||||
| BCM5301X devices may contain some partitions in higher memory, e.g. | ||||
| Netgear R8000 has board_data at 0x2600000. To detect them we should | ||||
| use size limit on MIPS only. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| --- | ||||
|  drivers/mtd/bcm47xxpart.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/bcm47xxpart.c | ||||
| +++ b/drivers/mtd/bcm47xxpart.c | ||||
| @@ -118,8 +118,8 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  	/* Parse block by block looking for magics */ | ||||
|  	for (offset = 0; offset <= master->size - blocksize; | ||||
|  	     offset += blocksize) { | ||||
| -		/* Nothing more in higher memory */ | ||||
| -		if (offset >= 0x2000000) | ||||
| +		/* Nothing more in higher memory on BCM47XX (MIPS) */ | ||||
| +		if (config_enabled(CONFIG_BCM47XX) && offset >= 0x2000000) | ||||
|  			break; | ||||
|   | ||||
|  		if (curr_part >= BCM47XXPART_MAX_PARTS) { | ||||
| @@ -1,92 +0,0 @@ | ||||
| From dfe4b4c732365fc1d83c2d2fd9cc18054ae850b7 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Sun, 6 Dec 2015 11:24:05 +0100 | ||||
| Subject: [PATCH] mtd: bcm47xxpart: don't fail because of bit-flips | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| Bit-flip errors may occur on NAND flashes and are harmless. Handle them | ||||
| gracefully as read content is still reliable and can be parsed. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| --- | ||||
|  drivers/mtd/bcm47xxpart.c | 38 ++++++++++++++++++++++---------------- | ||||
|  1 file changed, 22 insertions(+), 16 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/bcm47xxpart.c | ||||
| +++ b/drivers/mtd/bcm47xxpart.c | ||||
| @@ -66,11 +66,13 @@ static const char *bcm47xxpart_trx_data_ | ||||
|  { | ||||
|  	uint32_t buf; | ||||
|  	size_t bytes_read; | ||||
| +	int err; | ||||
|   | ||||
| -	if (mtd_read(master, offset, sizeof(buf), &bytes_read, | ||||
| -		     (uint8_t *)&buf) < 0) { | ||||
| -		pr_err("mtd_read error while parsing (offset: 0x%X)!\n", | ||||
| -			offset); | ||||
| +	err  = mtd_read(master, offset, sizeof(buf), &bytes_read, | ||||
| +			(uint8_t *)&buf); | ||||
| +	if (err && !mtd_is_bitflip(err)) { | ||||
| +		pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", | ||||
| +			offset, err); | ||||
|  		goto out_default; | ||||
|  	} | ||||
|   | ||||
| @@ -95,6 +97,7 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  	int trx_part = -1; | ||||
|  	int last_trx_part = -1; | ||||
|  	int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; | ||||
| +	int err; | ||||
|   | ||||
|  	/* | ||||
|  	 * Some really old flashes (like AT45DB*) had smaller erasesize-s, but | ||||
| @@ -128,10 +131,11 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  		} | ||||
|   | ||||
|  		/* Read beginning of the block */ | ||||
| -		if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, | ||||
| -			     &bytes_read, (uint8_t *)buf) < 0) { | ||||
| -			pr_err("mtd_read error while parsing (offset: 0x%X)!\n", | ||||
| -			       offset); | ||||
| +		err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, | ||||
| +			       &bytes_read, (uint8_t *)buf); | ||||
| +		if (err && !mtd_is_bitflip(err)) { | ||||
| +			pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", | ||||
| +			       offset, err); | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| @@ -254,10 +258,11 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  		} | ||||
|   | ||||
|  		/* Read middle of the block */ | ||||
| -		if (mtd_read(master, offset + 0x8000, 0x4, | ||||
| -			     &bytes_read, (uint8_t *)buf) < 0) { | ||||
| -			pr_err("mtd_read error while parsing (offset: 0x%X)!\n", | ||||
| -			       offset); | ||||
| +		err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read, | ||||
| +			       (uint8_t *)buf); | ||||
| +		if (err && !mtd_is_bitflip(err)) { | ||||
| +			pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", | ||||
| +			       offset, err); | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| @@ -277,10 +282,11 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  		} | ||||
|   | ||||
|  		offset = master->size - possible_nvram_sizes[i]; | ||||
| -		if (mtd_read(master, offset, 0x4, &bytes_read, | ||||
| -			     (uint8_t *)buf) < 0) { | ||||
| -			pr_err("mtd_read error while reading at offset 0x%X!\n", | ||||
| -			       offset); | ||||
| +		err = mtd_read(master, offset, 0x4, &bytes_read, | ||||
| +			       (uint8_t *)buf); | ||||
| +		if (err && !mtd_is_bitflip(err)) { | ||||
| +			pr_err("mtd_read error while reading (offset 0x%X): %d\n", | ||||
| +			       offset, err); | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| @@ -1,96 +0,0 @@ | ||||
| From fe29727caa7fe434fcb3166df2a62672bc516b54 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz> | ||||
| Date: Wed, 4 Nov 2015 16:23:37 +0100 | ||||
| Subject: [PATCH 2/2] USB: qmi_wwan: Add quirk for Quectel EC20 Mini PCIe | ||||
|  module | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This device has same vendor and product IDs as G2K devices, but it has | ||||
| different number of interfaces(4 vs 5) and also different interface | ||||
| layout where EC20 has QMI on interface 4 instead of 0. | ||||
|  | ||||
| lsusb output: | ||||
|  | ||||
| 	Bus 002 Device 003: ID 05c6:9215 Qualcomm, Inc. Acer Gobi 2000 | ||||
| 	Device Descriptor: | ||||
| 	  bLength                18 | ||||
| 	  bDescriptorType         1 | ||||
| 	  bcdUSB               2.00 | ||||
| 	  bDeviceClass            0 (Defined at Interface level) | ||||
| 	  bDeviceSubClass         0 | ||||
| 	  bDeviceProtocol         0 | ||||
| 	  bMaxPacketSize0        64 | ||||
| 	  idVendor           0x05c6 Qualcomm, Inc. | ||||
| 	  idProduct          0x9215 Acer Gobi 2000 Wireless Modem | ||||
| 	  bcdDevice            2.32 | ||||
| 	  iManufacturer           1 Quectel | ||||
| 	  iProduct                2 Quectel LTE Module | ||||
| 	  iSerial                 0 | ||||
| 	  bNumConfigurations      1 | ||||
| 	  Configuration Descriptor: | ||||
| 	    bLength                 9 | ||||
| 	    bDescriptorType         2 | ||||
| 	    wTotalLength          209 | ||||
| 	    bNumInterfaces          5 | ||||
| 	    bConfigurationValue     1 | ||||
| 	    iConfiguration          0 | ||||
| 	    bmAttributes         0xa0 | ||||
| 	      (Bus Powered) | ||||
| 	      Remote Wakeup | ||||
| 	    MaxPower              500mA | ||||
|  | ||||
| Signed-off-by: Petr Štetiar <ynezz@true.cz> | ||||
| --- | ||||
|  drivers/net/usb/qmi_wwan.c |   21 +++++++++++++++++++++ | ||||
|  1 file changed, 21 insertions(+) | ||||
|  | ||||
| --- a/drivers/net/usb/qmi_wwan.c | ||||
| +++ b/drivers/net/usb/qmi_wwan.c | ||||
| @@ -823,6 +823,7 @@ static const struct usb_device_id produc | ||||
|  	{QMI_GOBI_DEVICE(0x05c6, 0x9245)},	/* Samsung Gobi 2000 Modem device (VL176) */ | ||||
|  	{QMI_GOBI_DEVICE(0x03f0, 0x251d)},	/* HP Gobi 2000 Modem device (VP412) */ | ||||
|  	{QMI_GOBI_DEVICE(0x05c6, 0x9215)},	/* Acer Gobi 2000 Modem device (VP413) */ | ||||
| +	{QMI_FIXED_INTF(0x05c6, 0x9215, 4)},	/* Quectel EC20 Mini PCIe */ | ||||
|  	{QMI_GOBI_DEVICE(0x05c6, 0x9265)},	/* Asus Gobi 2000 Modem device (VR305) */ | ||||
|  	{QMI_GOBI_DEVICE(0x05c6, 0x9235)},	/* Top Global Gobi 2000 Modem device (VR306) */ | ||||
|  	{QMI_GOBI_DEVICE(0x05c6, 0x9275)},	/* iRex Technologies Gobi 2000 Modem device (VR307) */ | ||||
| @@ -854,10 +855,24 @@ static const struct usb_device_id produc | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(usb, products); | ||||
|   | ||||
| +static bool quectel_ec20_detected(struct usb_interface *intf) | ||||
| +{ | ||||
| +	struct usb_device *dev = interface_to_usbdev(intf); | ||||
| + | ||||
| +	if (dev->actconfig && | ||||
| +	    le16_to_cpu(dev->descriptor.idVendor) == 0x05c6 && | ||||
| +	    le16_to_cpu(dev->descriptor.idProduct) == 0x9215 && | ||||
| +	    dev->actconfig->desc.bNumInterfaces == 5) | ||||
| +		return true; | ||||
| + | ||||
| +	return false; | ||||
| +} | ||||
| + | ||||
|  static int qmi_wwan_probe(struct usb_interface *intf, | ||||
|  			  const struct usb_device_id *prod) | ||||
|  { | ||||
|  	struct usb_device_id *id = (struct usb_device_id *)prod; | ||||
| +	struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; | ||||
|   | ||||
|  	/* Workaround to enable dynamic IDs.  This disables usbnet | ||||
|  	 * blacklisting functionality.  Which, if required, can be | ||||
| @@ -869,6 +884,12 @@ static int qmi_wwan_probe(struct usb_int | ||||
|  		id->driver_info = (unsigned long)&qmi_wwan_info; | ||||
|  	} | ||||
|   | ||||
| +	/* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */ | ||||
| +	if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) { | ||||
| +		dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n"); | ||||
| +		return -ENODEV; | ||||
| +	} | ||||
| + | ||||
|  	return usbnet_probe(intf, id); | ||||
|  } | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/scripts/setlocalversion | ||||
| +++ b/scripts/setlocalversion | ||||
| @@ -165,7 +165,7 @@ else | ||||
|  	# annotated or signed tagged state (as git describe only | ||||
|  	# looks at signed or annotated tags - git tag -a/-s) and | ||||
|  	# LOCALVERSION= is not specified | ||||
| -	if test "${LOCALVERSION+set}" != "set"; then | ||||
| +	if test "${CONFIG_LOCALVERSION+set}" != "set"; then | ||||
|  		scm=$(scm_version --short) | ||||
|  		res="$res${scm:++}" | ||||
|  	fi | ||||
| @@ -1,14 +0,0 @@ | ||||
| --- a/Makefile | ||||
| +++ b/Makefile | ||||
| @@ -607,9 +607,9 @@ include arch/$(SRCARCH)/Makefile | ||||
|  KBUILD_CFLAGS	+= $(call cc-option,-fno-delete-null-pointer-checks,) | ||||
|   | ||||
|  ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE | ||||
| -KBUILD_CFLAGS	+= -Os $(call cc-disable-warning,maybe-uninitialized,) | ||||
| +KBUILD_CFLAGS	+= -Os $(EXTRA_OPTIMIZATION) $(call cc-disable-warning,maybe-uninitialized,) | ||||
|  else | ||||
| -KBUILD_CFLAGS	+= -O2 | ||||
| +KBUILD_CFLAGS	+= -O2 -fno-reorder-blocks -fno-tree-ch $(EXTRA_OPTIMIZATION) | ||||
|  endif | ||||
|   | ||||
|  # Tell gcc to never replace conditional load with a non-conditional one | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/Makefile | ||||
| +++ b/Makefile | ||||
| @@ -398,7 +398,7 @@ KBUILD_CFLAGS_KERNEL := | ||||
|  KBUILD_AFLAGS   := -D__ASSEMBLY__ | ||||
|  KBUILD_AFLAGS_MODULE  := -DMODULE | ||||
|  KBUILD_CFLAGS_MODULE  := -DMODULE | ||||
| -KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds | ||||
| +KBUILD_LDFLAGS_MODULE = -T $(srctree)/scripts/module-common.lds $(if $(CONFIG_PROFILING),,-s) | ||||
|   | ||||
|  # Read KERNELRELEASE from include/config/kernel.release (if it exists) | ||||
|  KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) | ||||
| @@ -1,108 +0,0 @@ | ||||
| --- a/scripts/kallsyms.c | ||||
| +++ b/scripts/kallsyms.c | ||||
| @@ -58,6 +58,7 @@ static struct addr_range percpu_range = | ||||
|  static struct sym_entry *table; | ||||
|  static unsigned int table_size, table_cnt; | ||||
|  static int all_symbols = 0; | ||||
| +static int uncompressed = 0; | ||||
|  static int absolute_percpu = 0; | ||||
|  static char symbol_prefix_char = '\0'; | ||||
|  static unsigned long long kernel_start_addr = 0; | ||||
| @@ -403,6 +404,9 @@ static void write_src(void) | ||||
|   | ||||
|  	free(markers); | ||||
|   | ||||
| +	if (uncompressed) | ||||
| +		return; | ||||
| + | ||||
|  	output_label("kallsyms_token_table"); | ||||
|  	off = 0; | ||||
|  	for (i = 0; i < 256; i++) { | ||||
| @@ -461,6 +465,9 @@ static void *find_token(unsigned char *s | ||||
|  { | ||||
|  	int i; | ||||
|   | ||||
| +	if (uncompressed) | ||||
| +		return NULL; | ||||
| + | ||||
|  	for (i = 0; i < len - 1; i++) { | ||||
|  		if (str[i] == token[0] && str[i+1] == token[1]) | ||||
|  			return &str[i]; | ||||
| @@ -533,6 +540,9 @@ static void optimize_result(void) | ||||
|  { | ||||
|  	int i, best; | ||||
|   | ||||
| +	if (uncompressed) | ||||
| +		return; | ||||
| + | ||||
|  	/* using the '\0' symbol last allows compress_symbols to use standard | ||||
|  	 * fast string functions */ | ||||
|  	for (i = 255; i >= 0; i--) { | ||||
| @@ -703,7 +713,9 @@ int main(int argc, char **argv) | ||||
|  			} else if (strncmp(argv[i], "--page-offset=", 14) == 0) { | ||||
|  				const char *p = &argv[i][14]; | ||||
|  				kernel_start_addr = strtoull(p, NULL, 16); | ||||
| -			} else | ||||
| +			} else if (strcmp(argv[i], "--uncompressed") == 0) | ||||
| +				uncompressed = 1; | ||||
| +			else | ||||
|  				usage(); | ||||
|  		} | ||||
|  	} else if (argc != 1) | ||||
| --- a/init/Kconfig | ||||
| +++ b/init/Kconfig | ||||
| @@ -1345,6 +1345,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW | ||||
|  	  the unaligned access emulation. | ||||
|  	  see arch/parisc/kernel/unaligned.c for reference | ||||
|   | ||||
| +config KALLSYMS_UNCOMPRESSED | ||||
| +	bool "Keep kallsyms uncompressed" | ||||
| +	depends on KALLSYMS | ||||
| +	help | ||||
| +		Normally kallsyms contains compressed symbols (using a token table), | ||||
| +		reducing the uncompressed kernel image size. Keeping the symbol table | ||||
| +		uncompressed significantly improves the size of this part in compressed | ||||
| +		kernel images. | ||||
| + | ||||
| +		Say N unless you need compressed kernel images to be small. | ||||
| + | ||||
|  config HAVE_PCSPKR_PLATFORM | ||||
|  	bool | ||||
|   | ||||
| --- a/scripts/link-vmlinux.sh | ||||
| +++ b/scripts/link-vmlinux.sh | ||||
| @@ -90,6 +90,10 @@ kallsyms() | ||||
|  		kallsymopt="${kallsymopt} --absolute-percpu" | ||||
|  	fi | ||||
|   | ||||
| +	if [ -n "${CONFIG_KALLSYMS_UNCOMPRESSED}" ]; then | ||||
| +		kallsymopt="${kallsymopt} --uncompressed" | ||||
| +	fi | ||||
| + | ||||
|  	local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL}               \ | ||||
|  		      ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}" | ||||
|   | ||||
| --- a/kernel/kallsyms.c | ||||
| +++ b/kernel/kallsyms.c | ||||
| @@ -109,6 +109,11 @@ static unsigned int kallsyms_expand_symb | ||||
|  	 * For every byte on the compressed symbol data, copy the table | ||||
|  	 * entry for that byte. | ||||
|  	 */ | ||||
| +#ifdef CONFIG_KALLSYMS_UNCOMPRESSED | ||||
| +	memcpy(result, data + 1, len - 1); | ||||
| +	result += len - 1; | ||||
| +	len = 0; | ||||
| +#endif | ||||
|  	while (len) { | ||||
|  		tptr = &kallsyms_token_table[kallsyms_token_index[*data]]; | ||||
|  		data++; | ||||
| @@ -141,6 +146,9 @@ tail: | ||||
|   */ | ||||
|  static char kallsyms_get_symbol_type(unsigned int off) | ||||
|  { | ||||
| +#ifdef CONFIG_KALLSYMS_UNCOMPRESSED | ||||
| +	return kallsyms_names[off + 1]; | ||||
| +#endif | ||||
|  	/* | ||||
|  	 * Get just the first code, look it up in the token table, | ||||
|  	 * and return the first char from this token. | ||||
| @@ -1,194 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Subject: [PATCH] build: add a hack for removing non-essential module info | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
| --- a/include/linux/module.h | ||||
| +++ b/include/linux/module.h | ||||
| @@ -169,9 +169,10 @@ void trim_init_extable(struct module *m) | ||||
|   | ||||
|  /* Generic info of form tag = "info" */ | ||||
|  #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) | ||||
| +#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info) | ||||
|   | ||||
|  /* For userspace: you can also call me... */ | ||||
| -#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias) | ||||
| +#define MODULE_ALIAS(_alias) MODULE_INFO_STRIP(alias, _alias) | ||||
|   | ||||
|  /* Soft module dependencies. See man modprobe.d for details. | ||||
|   * Example: MODULE_SOFTDEP("pre: module-foo module-bar post: module-baz") | ||||
| @@ -212,12 +213,12 @@ void trim_init_extable(struct module *m) | ||||
|   * Author(s), use "Name <email>" or just "Name", for multiple | ||||
|   * authors use multiple MODULE_AUTHOR() statements/lines. | ||||
|   */ | ||||
| -#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) | ||||
| +#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author) | ||||
|   | ||||
|  /* What your module does. */ | ||||
| -#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) | ||||
| +#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description) | ||||
|   | ||||
| -#ifdef MODULE | ||||
| +#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED) | ||||
|  /* Creates an alias so file2alias.c can find device table. */ | ||||
|  #define MODULE_DEVICE_TABLE(type, name)					\ | ||||
|  extern const typeof(name) __mod_##type##__##name##_device_table		\ | ||||
| @@ -244,7 +245,9 @@ extern const typeof(name) __mod_##type## | ||||
|   */ | ||||
|   | ||||
|  #if defined(MODULE) || !defined(CONFIG_SYSFS) | ||||
| -#define MODULE_VERSION(_version) MODULE_INFO(version, _version) | ||||
| +#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version) | ||||
| +#elif defined(CONFIG_MODULE_STRIPPED) | ||||
| +#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version) | ||||
|  #else | ||||
|  #define MODULE_VERSION(_version)					\ | ||||
|  	static struct module_version_attribute ___modver_attr = {	\ | ||||
| @@ -266,7 +269,7 @@ extern const typeof(name) __mod_##type## | ||||
|  /* Optional firmware file (or files) needed by the module | ||||
|   * format is simply firmware file name.  Multiple firmware | ||||
|   * files require multiple MODULE_FIRMWARE() specifiers */ | ||||
| -#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware) | ||||
| +#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware) | ||||
|   | ||||
|  /* Given an address, look for it in the exception tables */ | ||||
|  const struct exception_table_entry *search_exception_tables(unsigned long add); | ||||
| --- a/include/linux/moduleparam.h | ||||
| +++ b/include/linux/moduleparam.h | ||||
| @@ -16,6 +16,16 @@ | ||||
|  /* Chosen so that structs with an unsigned long line up. */ | ||||
|  #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long)) | ||||
|   | ||||
| +/* This struct is here for syntactic coherency, it is not used */ | ||||
| +#define __MODULE_INFO_DISABLED(name)					  \ | ||||
| +  struct __UNIQUE_ID(name) {} | ||||
| + | ||||
| +#ifdef CONFIG_MODULE_STRIPPED | ||||
| +#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name) | ||||
| +#else | ||||
| +#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info) | ||||
| +#endif | ||||
| + | ||||
|  #ifdef MODULE | ||||
|  #define __MODULE_INFO(tag, name, info)					  \ | ||||
|  static const char __UNIQUE_ID(name)[]					  \ | ||||
| @@ -23,8 +33,7 @@ static const char __UNIQUE_ID(name)[] | ||||
|    = __stringify(tag) "=" info | ||||
|  #else  /* !MODULE */ | ||||
|  /* This struct is here for syntactic coherency, it is not used */ | ||||
| -#define __MODULE_INFO(tag, name, info)					  \ | ||||
| -  struct __UNIQUE_ID(name) {} | ||||
| +#define __MODULE_INFO(tag, name, info) __MODULE_INFO_DISABLED(name) | ||||
|  #endif | ||||
|  #define __MODULE_PARM_TYPE(name, _type)					  \ | ||||
|    __MODULE_INFO(parmtype, name##type, #name ":" _type) | ||||
| @@ -32,7 +41,7 @@ static const char __UNIQUE_ID(name)[] | ||||
|  /* One for each parameter, describing how to use it.  Some files do | ||||
|     multiple of these per line, so can't just use MODULE_INFO. */ | ||||
|  #define MODULE_PARM_DESC(_parm, desc) \ | ||||
| -	__MODULE_INFO(parm, _parm, #_parm ":" desc) | ||||
| +	__MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc) | ||||
|   | ||||
|  struct kernel_param; | ||||
|   | ||||
| --- a/init/Kconfig | ||||
| +++ b/init/Kconfig | ||||
| @@ -2026,6 +2026,13 @@ config MODULE_COMPRESS_XZ | ||||
|   | ||||
|  endchoice | ||||
|   | ||||
| +config MODULE_STRIPPED | ||||
| +	bool "Reduce module size" | ||||
| +	depends on MODULES | ||||
| +	help | ||||
| +	  Remove module parameter descriptions, author info, version, aliases, | ||||
| +	  device tables, etc. | ||||
| + | ||||
|  endif # MODULES | ||||
|   | ||||
|  config MODULES_TREE_LOOKUP | ||||
| --- a/kernel/module.c | ||||
| +++ b/kernel/module.c | ||||
| @@ -2840,6 +2840,7 @@ static struct module *setup_load_info(st | ||||
|   | ||||
|  static int check_modinfo(struct module *mod, struct load_info *info, int flags) | ||||
|  { | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  	const char *modmagic = get_modinfo(info, "vermagic"); | ||||
|  	int err; | ||||
|   | ||||
| @@ -2865,6 +2866,7 @@ static int check_modinfo(struct module * | ||||
|  		pr_warn("%s: module is from the staging directory, the quality " | ||||
|  			"is unknown, you have been warned.\n", mod->name); | ||||
|  	} | ||||
| +#endif | ||||
|   | ||||
|  	/* Set up license info based on the info section */ | ||||
|  	set_license(mod, get_modinfo(info, "license")); | ||||
| --- a/scripts/mod/modpost.c | ||||
| +++ b/scripts/mod/modpost.c | ||||
| @@ -1960,7 +1960,9 @@ static void read_symbols(char *modname) | ||||
|  		symname = remove_dot(info.strtab + sym->st_name); | ||||
|   | ||||
|  		handle_modversions(mod, &info, sym, symname); | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  		handle_moddevtable(mod, &info, sym, symname); | ||||
| +#endif | ||||
|  	} | ||||
|  	if (!is_vmlinux(modname) || | ||||
|  	     (is_vmlinux(modname) && vmlinux_section_warnings)) | ||||
| @@ -2104,7 +2106,9 @@ static void add_header(struct buffer *b, | ||||
|  	buf_printf(b, "#include <linux/vermagic.h>\n"); | ||||
|  	buf_printf(b, "#include <linux/compiler.h>\n"); | ||||
|  	buf_printf(b, "\n"); | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  	buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); | ||||
| +#endif | ||||
|  	buf_printf(b, "\n"); | ||||
|  	buf_printf(b, "__visible struct module __this_module\n"); | ||||
|  	buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n"); | ||||
| @@ -2121,16 +2125,20 @@ static void add_header(struct buffer *b, | ||||
|   | ||||
|  static void add_intree_flag(struct buffer *b, int is_intree) | ||||
|  { | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  	if (is_intree) | ||||
|  		buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); | ||||
| +#endif | ||||
|  } | ||||
|   | ||||
|  static void add_staging_flag(struct buffer *b, const char *name) | ||||
|  { | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  	static const char *staging_dir = "drivers/staging"; | ||||
|   | ||||
|  	if (strncmp(staging_dir, name, strlen(staging_dir)) == 0) | ||||
|  		buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); | ||||
| +#endif | ||||
|  } | ||||
|   | ||||
|  /** | ||||
| @@ -2223,11 +2231,13 @@ static void add_depends(struct buffer *b | ||||
|   | ||||
|  static void add_srcversion(struct buffer *b, struct module *mod) | ||||
|  { | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  	if (mod->srcversion[0]) { | ||||
|  		buf_printf(b, "\n"); | ||||
|  		buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", | ||||
|  			   mod->srcversion); | ||||
|  	} | ||||
| +#endif | ||||
|  } | ||||
|   | ||||
|  static void write_if_changed(struct buffer *b, const char *fname) | ||||
| @@ -2458,7 +2468,9 @@ int main(int argc, char **argv) | ||||
|  		add_staging_flag(&buf, mod->name); | ||||
|  		err |= add_versions(&buf, mod); | ||||
|  		add_depends(&buf, mod, modules); | ||||
| +#ifndef CONFIG_MODULE_STRIPPED | ||||
|  		add_moddevtable(&buf, mod); | ||||
| +#endif | ||||
|  		add_srcversion(&buf, mod); | ||||
|   | ||||
|  		sprintf(fname, "%s.mod.c", mod->name); | ||||
| @@ -1,36 +0,0 @@ | ||||
| --- a/lib/vsprintf.c | ||||
| +++ b/lib/vsprintf.c | ||||
| @@ -618,8 +618,10 @@ char *symbol_string(char *buf, char *end | ||||
|  		    struct printf_spec spec, const char *fmt) | ||||
|  { | ||||
|  	unsigned long value; | ||||
| -#ifdef CONFIG_KALLSYMS | ||||
|  	char sym[KSYM_SYMBOL_LEN]; | ||||
| +#ifndef CONFIG_KALLSYMS | ||||
| +	struct module *mod; | ||||
| +	int len; | ||||
|  #endif | ||||
|   | ||||
|  	if (fmt[1] == 'R') | ||||
| @@ -633,15 +635,15 @@ char *symbol_string(char *buf, char *end | ||||
|  		sprint_symbol(sym, value); | ||||
|  	else | ||||
|  		sprint_symbol_no_offset(sym, value); | ||||
| - | ||||
| -	return string(buf, end, sym, spec); | ||||
|  #else | ||||
| -	spec.field_width = 2 * sizeof(void *); | ||||
| -	spec.flags |= SPECIAL | SMALL | ZEROPAD; | ||||
| -	spec.base = 16; | ||||
| +	len = snprintf(sym, sizeof(sym), "0x%lx", value); | ||||
|   | ||||
| -	return number(buf, end, value, spec); | ||||
| +	mod = __module_address(value); | ||||
| +	if (mod) | ||||
| +		snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]", | ||||
| +			 mod->name, mod->module_core, mod->core_size); | ||||
|  #endif | ||||
| +	return string(buf, end, sym, spec); | ||||
|  } | ||||
|   | ||||
|  static noinline_for_stack | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,51 +0,0 @@ | ||||
| --- a/tools/include/tools/be_byteshift.h | ||||
| +++ b/tools/include/tools/be_byteshift.h | ||||
| @@ -1,6 +1,10 @@ | ||||
|  #ifndef _TOOLS_BE_BYTESHIFT_H | ||||
|  #define _TOOLS_BE_BYTESHIFT_H | ||||
|   | ||||
| +#ifndef __linux__ | ||||
| +#include "linux_types.h" | ||||
| +#endif | ||||
| + | ||||
|  #include <stdint.h> | ||||
|   | ||||
|  static inline uint16_t __get_unaligned_be16(const uint8_t *p) | ||||
| --- a/tools/include/tools/le_byteshift.h | ||||
| +++ b/tools/include/tools/le_byteshift.h | ||||
| @@ -1,6 +1,10 @@ | ||||
|  #ifndef _TOOLS_LE_BYTESHIFT_H | ||||
|  #define _TOOLS_LE_BYTESHIFT_H | ||||
|   | ||||
| +#ifndef __linux__ | ||||
| +#include "linux_types.h" | ||||
| +#endif | ||||
| + | ||||
|  #include <stdint.h> | ||||
|   | ||||
|  static inline uint16_t __get_unaligned_le16(const uint8_t *p) | ||||
| --- /dev/null | ||||
| +++ b/tools/include/tools/linux_types.h | ||||
| @@ -0,0 +1,22 @@ | ||||
| +#ifndef __LINUX_TYPES_H | ||||
| +#define __LINUX_TYPES_H | ||||
| + | ||||
| +#include <stdint.h> | ||||
| + | ||||
| +typedef uint8_t __u8; | ||||
| +typedef uint8_t __be8; | ||||
| +typedef uint8_t __le8; | ||||
| + | ||||
| +typedef uint16_t __u16; | ||||
| +typedef uint16_t __be16; | ||||
| +typedef uint16_t __le16; | ||||
| + | ||||
| +typedef uint32_t __u32; | ||||
| +typedef uint32_t __be32; | ||||
| +typedef uint32_t __le32; | ||||
| + | ||||
| +typedef uint64_t __u64; | ||||
| +typedef uint64_t __be64; | ||||
| +typedef uint64_t __le64; | ||||
| + | ||||
| +#endif | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/include/uapi/linux/spi/spidev.h | ||||
| +++ b/include/uapi/linux/spi/spidev.h | ||||
| @@ -111,7 +111,7 @@ struct spi_ioc_transfer { | ||||
|   | ||||
|  /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */ | ||||
|  #define SPI_MSGSIZE(N) \ | ||||
| -	((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ | ||||
| +	((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \ | ||||
|  		? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) | ||||
|  #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) | ||||
|   | ||||
| @@ -1,536 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
|  | ||||
| use -ffunction-sections, -fdata-sections and --gc-sections | ||||
|  | ||||
| In combination with kernel symbol export stripping this significantly reduces | ||||
| the kernel image size. Used on both ARM and MIPS architectures. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| Signed-off-by: Jonas Gorski <jogo@openwrt.org> | ||||
| Signed-off-by: Gabor Juhos <juhosg@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/arch/mips/Makefile | ||||
| +++ b/arch/mips/Makefile | ||||
| @@ -89,10 +89,14 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin | ||||
|  # | ||||
|  cflags-y			+= -G 0 -mno-abicalls -fno-pic -pipe | ||||
|  cflags-y			+= -msoft-float | ||||
| -LDFLAGS_vmlinux			+= -G 0 -static -n -nostdlib | ||||
| +LDFLAGS_vmlinux			+= -G 0 -static -n -nostdlib --gc-sections | ||||
|  KBUILD_AFLAGS_MODULE		+= -mlong-calls | ||||
|  KBUILD_CFLAGS_MODULE		+= -mlong-calls | ||||
|   | ||||
| +ifndef CONFIG_FUNCTION_TRACER | ||||
| +KBUILD_CFLAGS_KERNEL		+= -ffunction-sections -fdata-sections | ||||
| +endif | ||||
| + | ||||
|  # | ||||
|  # pass -msoft-float to GAS if it supports it.  However on newer binutils | ||||
|  # (specifically newer than 2.24.51.20140728) we then also need to explicitly | ||||
| --- a/arch/mips/kernel/vmlinux.lds.S | ||||
| +++ b/arch/mips/kernel/vmlinux.lds.S | ||||
| @@ -67,7 +67,7 @@ SECTIONS | ||||
|  	/* Exception table for data bus errors */ | ||||
|  	__dbe_table : { | ||||
|  		__start___dbe_table = .; | ||||
| -		*(__dbe_table) | ||||
| +		KEEP(*(__dbe_table)) | ||||
|  		__stop___dbe_table = .; | ||||
|  	} | ||||
|   | ||||
| @@ -112,7 +112,7 @@ SECTIONS | ||||
|  	. = ALIGN(4); | ||||
|  	.mips.machines.init : AT(ADDR(.mips.machines.init) - LOAD_OFFSET) { | ||||
|  		__mips_machines_start = .; | ||||
| -		*(.mips.machines.init) | ||||
| +		KEEP(*(.mips.machines.init)) | ||||
|  		__mips_machines_end = .; | ||||
|  	} | ||||
|   | ||||
| --- a/include/asm-generic/vmlinux.lds.h | ||||
| +++ b/include/asm-generic/vmlinux.lds.h | ||||
| @@ -89,7 +89,7 @@ | ||||
|  #ifdef CONFIG_FTRACE_MCOUNT_RECORD | ||||
|  #define MCOUNT_REC()	. = ALIGN(8);				\ | ||||
|  			VMLINUX_SYMBOL(__start_mcount_loc) = .; \ | ||||
| -			*(__mcount_loc)				\ | ||||
| +			KEEP(*(__mcount_loc))			\ | ||||
|  			VMLINUX_SYMBOL(__stop_mcount_loc) = .; | ||||
|  #else | ||||
|  #define MCOUNT_REC() | ||||
| @@ -97,7 +97,7 @@ | ||||
|   | ||||
|  #ifdef CONFIG_TRACE_BRANCH_PROFILING | ||||
|  #define LIKELY_PROFILE()	VMLINUX_SYMBOL(__start_annotated_branch_profile) = .; \ | ||||
| -				*(_ftrace_annotated_branch)			      \ | ||||
| +				KEEP(*(_ftrace_annotated_branch))		      \ | ||||
|  				VMLINUX_SYMBOL(__stop_annotated_branch_profile) = .; | ||||
|  #else | ||||
|  #define LIKELY_PROFILE() | ||||
| @@ -105,7 +105,7 @@ | ||||
|   | ||||
|  #ifdef CONFIG_PROFILE_ALL_BRANCHES | ||||
|  #define BRANCH_PROFILE()	VMLINUX_SYMBOL(__start_branch_profile) = .;   \ | ||||
| -				*(_ftrace_branch)			      \ | ||||
| +				KEEP(*(_ftrace_branch))			      \ | ||||
|  				VMLINUX_SYMBOL(__stop_branch_profile) = .; | ||||
|  #else | ||||
|  #define BRANCH_PROFILE() | ||||
| @@ -114,7 +114,7 @@ | ||||
|  #ifdef CONFIG_KPROBES | ||||
|  #define KPROBE_BLACKLIST()	. = ALIGN(8);				      \ | ||||
|  				VMLINUX_SYMBOL(__start_kprobe_blacklist) = .; \ | ||||
| -				*(_kprobe_blacklist)			      \ | ||||
| +				KEEP(*(_kprobe_blacklist))		      \ | ||||
|  				VMLINUX_SYMBOL(__stop_kprobe_blacklist) = .; | ||||
|  #else | ||||
|  #define KPROBE_BLACKLIST() | ||||
| @@ -123,10 +123,10 @@ | ||||
|  #ifdef CONFIG_EVENT_TRACING | ||||
|  #define FTRACE_EVENTS()	. = ALIGN(8);					\ | ||||
|  			VMLINUX_SYMBOL(__start_ftrace_events) = .;	\ | ||||
| -			*(_ftrace_events)				\ | ||||
| +			KEEP(*(_ftrace_events))				\ | ||||
|  			VMLINUX_SYMBOL(__stop_ftrace_events) = .;	\ | ||||
|  			VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .;	\ | ||||
| -			*(_ftrace_enum_map)				\ | ||||
| +			KEEP(*(_ftrace_enum_map))			\ | ||||
|  			VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .; | ||||
|  #else | ||||
|  #define FTRACE_EVENTS() | ||||
| @@ -134,7 +134,7 @@ | ||||
|   | ||||
|  #ifdef CONFIG_TRACING | ||||
|  #define TRACE_PRINTKS() VMLINUX_SYMBOL(__start___trace_bprintk_fmt) = .;      \ | ||||
| -			 *(__trace_printk_fmt) /* Trace_printk fmt' pointer */ \ | ||||
| +			 KEEP(*(__trace_printk_fmt)) /* Trace_printk fmt' pointer */ \ | ||||
|  			 VMLINUX_SYMBOL(__stop___trace_bprintk_fmt) = .; | ||||
|  #define TRACEPOINT_STR() VMLINUX_SYMBOL(__start___tracepoint_str) = .;	\ | ||||
|  			 *(__tracepoint_str) /* Trace_printk fmt' pointer */ \ | ||||
| @@ -147,7 +147,7 @@ | ||||
|  #ifdef CONFIG_FTRACE_SYSCALLS | ||||
|  #define TRACE_SYSCALLS() . = ALIGN(8);					\ | ||||
|  			 VMLINUX_SYMBOL(__start_syscalls_metadata) = .;	\ | ||||
| -			 *(__syscalls_metadata)				\ | ||||
| +			 KEEP(*(__syscalls_metadata))			\ | ||||
|  			 VMLINUX_SYMBOL(__stop_syscalls_metadata) = .; | ||||
|  #else | ||||
|  #define TRACE_SYSCALLS() | ||||
| @@ -169,8 +169,8 @@ | ||||
|  #define _OF_TABLE_1(name)						\ | ||||
|  	. = ALIGN(8);							\ | ||||
|  	VMLINUX_SYMBOL(__##name##_of_table) = .;			\ | ||||
| -	*(__##name##_of_table)						\ | ||||
| -	*(__##name##_of_table_end) | ||||
| +	KEEP(*(__##name##_of_table))					\ | ||||
| +	KEEP(*(__##name##_of_table_end)) | ||||
|   | ||||
|  #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc) | ||||
|  #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip) | ||||
| @@ -184,7 +184,7 @@ | ||||
|  #define KERNEL_DTB()							\ | ||||
|  	STRUCT_ALIGN();							\ | ||||
|  	VMLINUX_SYMBOL(__dtb_start) = .;				\ | ||||
| -	*(.dtb.init.rodata)						\ | ||||
| +	KEEP(*(.dtb.init.rodata))					\ | ||||
|  	VMLINUX_SYMBOL(__dtb_end) = .; | ||||
|   | ||||
|  /* .data section */ | ||||
| @@ -200,16 +200,17 @@ | ||||
|  	/* implement dynamic printk debug */				\ | ||||
|  	. = ALIGN(8);                                                   \ | ||||
|  	VMLINUX_SYMBOL(__start___jump_table) = .;                       \ | ||||
| -	*(__jump_table)                                                 \ | ||||
| +	KEEP(*(__jump_table))                                           \ | ||||
|  	VMLINUX_SYMBOL(__stop___jump_table) = .;                        \ | ||||
|  	. = ALIGN(8);							\ | ||||
|  	VMLINUX_SYMBOL(__start___verbose) = .;                          \ | ||||
| -	*(__verbose)                                                    \ | ||||
| +	KEEP(*(__verbose))                                              \ | ||||
|  	VMLINUX_SYMBOL(__stop___verbose) = .;				\ | ||||
|  	LIKELY_PROFILE()		       				\ | ||||
|  	BRANCH_PROFILE()						\ | ||||
|  	TRACE_PRINTKS()							\ | ||||
| -	TRACEPOINT_STR() | ||||
| +	TRACEPOINT_STR()                                                \ | ||||
| +	*(.data.[a-zA-Z_]*) | ||||
|   | ||||
|  /* | ||||
|   * Data section helpers | ||||
| @@ -263,35 +264,35 @@ | ||||
|  	/* PCI quirks */						\ | ||||
|  	.pci_fixup        : AT(ADDR(.pci_fixup) - LOAD_OFFSET) {	\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_early) = .;		\ | ||||
| -		*(.pci_fixup_early)					\ | ||||
| +		KEEP(*(.pci_fixup_early))				\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_early) = .;		\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_header) = .;		\ | ||||
| -		*(.pci_fixup_header)					\ | ||||
| +		KEEP(*(.pci_fixup_header))				\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_header) = .;		\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_final) = .;		\ | ||||
| -		*(.pci_fixup_final)					\ | ||||
| +		KEEP(*(.pci_fixup_final))				\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_final) = .;		\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_enable) = .;		\ | ||||
| -		*(.pci_fixup_enable)					\ | ||||
| +		KEEP(*(.pci_fixup_enable))				\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_enable) = .;		\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_resume) = .;		\ | ||||
| -		*(.pci_fixup_resume)					\ | ||||
| +		KEEP(*(.pci_fixup_resume))				\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_resume) = .;		\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .;	\ | ||||
| -		*(.pci_fixup_resume_early)				\ | ||||
| +		KEEP(*(.pci_fixup_resume_early))			\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .;	\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .;		\ | ||||
| -		*(.pci_fixup_suspend)					\ | ||||
| +		KEEP(*(.pci_fixup_suspend))				\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .;		\ | ||||
|  		VMLINUX_SYMBOL(__start_pci_fixups_suspend_late) = .;	\ | ||||
| -		*(.pci_fixup_suspend_late)				\ | ||||
| +		KEEP(*(.pci_fixup_suspend_late))			\ | ||||
|  		VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .;	\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Built-in firmware blobs */					\ | ||||
|  	.builtin_fw        : AT(ADDR(.builtin_fw) - LOAD_OFFSET) {	\ | ||||
|  		VMLINUX_SYMBOL(__start_builtin_fw) = .;			\ | ||||
| -		*(.builtin_fw)						\ | ||||
| +		KEEP(*(.builtin_fw))					\ | ||||
|  		VMLINUX_SYMBOL(__end_builtin_fw) = .;			\ | ||||
|  	}								\ | ||||
|  									\ | ||||
| @@ -300,49 +301,49 @@ | ||||
|  	/* Kernel symbol table: Normal symbols */			\ | ||||
|  	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab) = .;			\ | ||||
| -		*(SORT(___ksymtab+*))					\ | ||||
| +		KEEP(*(SORT(___ksymtab+*)))				\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab) = .;			\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: GPL-only symbols */			\ | ||||
|  	__ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {	\ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab_gpl) = .;		\ | ||||
| -		*(SORT(___ksymtab_gpl+*))				\ | ||||
| +		KEEP(*(SORT(___ksymtab_gpl+*)))				\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .;		\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: Normal unused symbols */		\ | ||||
|  	__ksymtab_unused  : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) {	\ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab_unused) = .;		\ | ||||
| -		*(SORT(___ksymtab_unused+*))				\ | ||||
| +		KEEP(*(SORT(___ksymtab_unused+*)))			\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab_unused) = .;		\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: GPL-only unused symbols */		\ | ||||
|  	__ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .;	\ | ||||
| -		*(SORT(___ksymtab_unused_gpl+*))			\ | ||||
| +		KEEP(*(SORT(___ksymtab_unused_gpl+*)))			\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .;	\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: GPL-future-only symbols */		\ | ||||
|  	__ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .;	\ | ||||
| -		*(SORT(___ksymtab_gpl_future+*))			\ | ||||
| +		KEEP(*(SORT(___ksymtab_gpl_future+*)))			\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .;	\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: Normal symbols */			\ | ||||
|  	__kcrctab         : AT(ADDR(__kcrctab) - LOAD_OFFSET) {		\ | ||||
|  		VMLINUX_SYMBOL(__start___kcrctab) = .;			\ | ||||
| -		*(SORT(___kcrctab+*))					\ | ||||
| +		KEEP(*(SORT(___kcrctab+*)))				\ | ||||
|  		VMLINUX_SYMBOL(__stop___kcrctab) = .;			\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: GPL-only symbols */			\ | ||||
|  	__kcrctab_gpl     : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) {	\ | ||||
|  		VMLINUX_SYMBOL(__start___kcrctab_gpl) = .;		\ | ||||
| -		*(SORT(___kcrctab_gpl+*))				\ | ||||
| +		KEEP(*(SORT(___kcrctab_gpl+*)))				\ | ||||
|  		VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .;		\ | ||||
|  	}								\ | ||||
|  									\ | ||||
| @@ -356,14 +357,14 @@ | ||||
|  	/* Kernel symbol table: GPL-only unused symbols */		\ | ||||
|  	__kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \ | ||||
|  		VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .;	\ | ||||
| -		*(SORT(___kcrctab_unused_gpl+*))			\ | ||||
| +		KEEP(*(SORT(___kcrctab_unused_gpl+*)))			\ | ||||
|  		VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .;	\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: GPL-future-only symbols */		\ | ||||
|  	__kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \ | ||||
|  		VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .;	\ | ||||
| -		*(SORT(___kcrctab_gpl_future+*))			\ | ||||
| +		KEEP(*(SORT(___kcrctab_gpl_future+*)))			\ | ||||
|  		VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .;	\ | ||||
|  	}								\ | ||||
|  									\ | ||||
| @@ -382,14 +383,14 @@ | ||||
|  	/* Built-in module parameters. */				\ | ||||
|  	__param : AT(ADDR(__param) - LOAD_OFFSET) {			\ | ||||
|  		VMLINUX_SYMBOL(__start___param) = .;			\ | ||||
| -		*(__param)						\ | ||||
| +		KEEP(*(__param))					\ | ||||
|  		VMLINUX_SYMBOL(__stop___param) = .;			\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Built-in module versions. */					\ | ||||
|  	__modver : AT(ADDR(__modver) - LOAD_OFFSET) {			\ | ||||
|  		VMLINUX_SYMBOL(__start___modver) = .;			\ | ||||
| -		*(__modver)						\ | ||||
| +		KEEP(*(__modver))					\ | ||||
|  		VMLINUX_SYMBOL(__stop___modver) = .;			\ | ||||
|  		. = ALIGN((align));					\ | ||||
|  		VMLINUX_SYMBOL(__end_rodata) = .;			\ | ||||
| @@ -443,7 +444,7 @@ | ||||
|  #define ENTRY_TEXT							\ | ||||
|  		ALIGN_FUNCTION();					\ | ||||
|  		VMLINUX_SYMBOL(__entry_text_start) = .;			\ | ||||
| -		*(.entry.text)						\ | ||||
| +		KEEP(*(.entry.text))					\ | ||||
|  		VMLINUX_SYMBOL(__entry_text_end) = .; | ||||
|   | ||||
|  #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||||
| @@ -471,7 +472,7 @@ | ||||
|  	. = ALIGN(align);						\ | ||||
|  	__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {		\ | ||||
|  		VMLINUX_SYMBOL(__start___ex_table) = .;			\ | ||||
| -		*(__ex_table)						\ | ||||
| +		KEEP(*(__ex_table))						\ | ||||
|  		VMLINUX_SYMBOL(__stop___ex_table) = .;			\ | ||||
|  	} | ||||
|   | ||||
| @@ -487,9 +488,9 @@ | ||||
|  #ifdef CONFIG_CONSTRUCTORS | ||||
|  #define KERNEL_CTORS()	. = ALIGN(8);			   \ | ||||
|  			VMLINUX_SYMBOL(__ctors_start) = .; \ | ||||
| -			*(.ctors)			   \ | ||||
| +			KEEP(*(.ctors))			   \ | ||||
|  			*(SORT(.init_array.*))		   \ | ||||
| -			*(.init_array)			   \ | ||||
| +			KEEP(*(.init_array))		   \ | ||||
|  			VMLINUX_SYMBOL(__ctors_end) = .; | ||||
|  #else | ||||
|  #define KERNEL_CTORS() | ||||
| @@ -540,7 +541,7 @@ | ||||
|  #define SBSS(sbss_align)						\ | ||||
|  	. = ALIGN(sbss_align);						\ | ||||
|  	.sbss : AT(ADDR(.sbss) - LOAD_OFFSET) {				\ | ||||
| -		*(.sbss)						\ | ||||
| +		*(.sbss .sbss.*)					\ | ||||
|  		*(.scommon)						\ | ||||
|  	} | ||||
|   | ||||
| @@ -558,7 +559,7 @@ | ||||
|  		BSS_FIRST_SECTIONS					\ | ||||
|  		*(.bss..page_aligned)					\ | ||||
|  		*(.dynbss)						\ | ||||
| -		*(.bss)							\ | ||||
| +		*(.bss .bss.*)						\ | ||||
|  		*(COMMON)						\ | ||||
|  	} | ||||
|   | ||||
| @@ -607,7 +608,7 @@ | ||||
|  	. = ALIGN(8);							\ | ||||
|  	__bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) {		\ | ||||
|  		VMLINUX_SYMBOL(__start___bug_table) = .;		\ | ||||
| -		*(__bug_table)						\ | ||||
| +		KEEP(*(__bug_table))					\ | ||||
|  		VMLINUX_SYMBOL(__stop___bug_table) = .;			\ | ||||
|  	} | ||||
|  #else | ||||
| @@ -619,7 +620,7 @@ | ||||
|  	. = ALIGN(4);							\ | ||||
|  	.tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) {		\ | ||||
|  		VMLINUX_SYMBOL(__tracedata_start) = .;			\ | ||||
| -		*(.tracedata)						\ | ||||
| +		KEEP(*(.tracedata))					\ | ||||
|  		VMLINUX_SYMBOL(__tracedata_end) = .;			\ | ||||
|  	} | ||||
|  #else | ||||
| @@ -636,17 +637,17 @@ | ||||
|  #define INIT_SETUP(initsetup_align)					\ | ||||
|  		. = ALIGN(initsetup_align);				\ | ||||
|  		VMLINUX_SYMBOL(__setup_start) = .;			\ | ||||
| -		*(.init.setup)						\ | ||||
| +		KEEP(*(.init.setup))					\ | ||||
|  		VMLINUX_SYMBOL(__setup_end) = .; | ||||
|   | ||||
|  #define INIT_CALLS_LEVEL(level)						\ | ||||
|  		VMLINUX_SYMBOL(__initcall##level##_start) = .;		\ | ||||
| -		*(.initcall##level##.init)				\ | ||||
| -		*(.initcall##level##s.init)				\ | ||||
| +		KEEP(*(.initcall##level##.init))			\ | ||||
| +		KEEP(*(.initcall##level##s.init))			\ | ||||
|   | ||||
|  #define INIT_CALLS							\ | ||||
|  		VMLINUX_SYMBOL(__initcall_start) = .;			\ | ||||
| -		*(.initcallearly.init)					\ | ||||
| +		KEEP(*(.initcallearly.init))				\ | ||||
|  		INIT_CALLS_LEVEL(0)					\ | ||||
|  		INIT_CALLS_LEVEL(1)					\ | ||||
|  		INIT_CALLS_LEVEL(2)					\ | ||||
| @@ -660,21 +661,21 @@ | ||||
|   | ||||
|  #define CON_INITCALL							\ | ||||
|  		VMLINUX_SYMBOL(__con_initcall_start) = .;		\ | ||||
| -		*(.con_initcall.init)					\ | ||||
| +		KEEP(*(.con_initcall.init))				\ | ||||
|  		VMLINUX_SYMBOL(__con_initcall_end) = .; | ||||
|   | ||||
|  #define SECURITY_INITCALL						\ | ||||
|  		VMLINUX_SYMBOL(__security_initcall_start) = .;		\ | ||||
| -		*(.security_initcall.init)				\ | ||||
| +		KEEP(*(.security_initcall.init))			\ | ||||
|  		VMLINUX_SYMBOL(__security_initcall_end) = .; | ||||
|   | ||||
|  #ifdef CONFIG_BLK_DEV_INITRD | ||||
|  #define INIT_RAM_FS							\ | ||||
|  	. = ALIGN(4);							\ | ||||
|  	VMLINUX_SYMBOL(__initramfs_start) = .;				\ | ||||
| -	*(.init.ramfs)							\ | ||||
| +	KEEP(*(.init.ramfs))						\ | ||||
|  	. = ALIGN(8);							\ | ||||
| -	*(.init.ramfs.info) | ||||
| +	KEEP(*(.init.ramfs.info)) | ||||
|  #else | ||||
|  #define INIT_RAM_FS | ||||
|  #endif | ||||
| --- a/arch/arm/Makefile | ||||
| +++ b/arch/arm/Makefile | ||||
| @@ -22,11 +22,16 @@ endif | ||||
|  ifeq ($(CONFIG_ARM_MODULE_PLTS),y) | ||||
|  LDFLAGS_MODULE	+= -T $(srctree)/arch/arm/kernel/module.lds | ||||
|  endif | ||||
| +LDFLAGS_vmlinux += --gc-sections | ||||
|   | ||||
|  OBJCOPYFLAGS	:=-O binary -R .comment -S | ||||
|  GZFLAGS		:=-9 | ||||
|  #KBUILD_CFLAGS	+=-pipe | ||||
|   | ||||
| +ifndef CONFIG_FUNCTION_TRACER | ||||
| +KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections | ||||
| +endif | ||||
| + | ||||
|  # Never generate .eh_frame | ||||
|  KBUILD_CFLAGS	+= $(call cc-option,-fno-dwarf2-cfi-asm) | ||||
|   | ||||
| --- a/arch/arm/kernel/vmlinux.lds.S | ||||
| +++ b/arch/arm/kernel/vmlinux.lds.S | ||||
| @@ -15,13 +15,13 @@ | ||||
|  #define PROC_INFO							\ | ||||
|  	. = ALIGN(4);							\ | ||||
|  	VMLINUX_SYMBOL(__proc_info_begin) = .;				\ | ||||
| -	*(.proc.info.init)						\ | ||||
| +	KEEP(*(.proc.info.init))					\ | ||||
|  	VMLINUX_SYMBOL(__proc_info_end) = .; | ||||
|   | ||||
|  #define IDMAP_TEXT							\ | ||||
|  	ALIGN_FUNCTION();						\ | ||||
|  	VMLINUX_SYMBOL(__idmap_text_start) = .;				\ | ||||
| -	*(.idmap.text)							\ | ||||
| +	KEEP(*(.idmap.text))						\ | ||||
|  	VMLINUX_SYMBOL(__idmap_text_end) = .;				\ | ||||
|  	. = ALIGN(PAGE_SIZE);						\ | ||||
|  	VMLINUX_SYMBOL(__hyp_idmap_text_start) = .;			\ | ||||
| @@ -102,7 +102,7 @@ SECTIONS | ||||
|  		_stext = .;		/* Text and read-only data	*/ | ||||
|  			IDMAP_TEXT | ||||
|  			__exception_text_start = .; | ||||
| -			*(.exception.text) | ||||
| +			KEEP(*(.exception.text)) | ||||
|  			__exception_text_end = .; | ||||
|  			IRQENTRY_TEXT | ||||
|  			TEXT_TEXT | ||||
| @@ -126,7 +126,7 @@ SECTIONS | ||||
|  	__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { | ||||
|  		__start___ex_table = .; | ||||
|  #ifdef CONFIG_MMU | ||||
| -		*(__ex_table) | ||||
| +		KEEP(*(__ex_table)) | ||||
|  #endif | ||||
|  		__stop___ex_table = .; | ||||
|  	} | ||||
| @@ -138,12 +138,12 @@ SECTIONS | ||||
|  	. = ALIGN(8); | ||||
|  	.ARM.unwind_idx : { | ||||
|  		__start_unwind_idx = .; | ||||
| -		*(.ARM.exidx*) | ||||
| +		KEEP(*(.ARM.exidx*)) | ||||
|  		__stop_unwind_idx = .; | ||||
|  	} | ||||
|  	.ARM.unwind_tab : { | ||||
|  		__start_unwind_tab = .; | ||||
| -		*(.ARM.extab*) | ||||
| +		KEEP(*(.ARM.extab*)) | ||||
|  		__stop_unwind_tab = .; | ||||
|  	} | ||||
|  #endif | ||||
| @@ -166,14 +166,14 @@ SECTIONS | ||||
|  	 */ | ||||
|  	__vectors_start = .; | ||||
|  	.vectors 0 : AT(__vectors_start) { | ||||
| -		*(.vectors) | ||||
| +		KEEP(*(.vectors)) | ||||
|  	} | ||||
|  	. = __vectors_start + SIZEOF(.vectors); | ||||
|  	__vectors_end = .; | ||||
|   | ||||
|  	__stubs_start = .; | ||||
|  	.stubs 0x1000 : AT(__stubs_start) { | ||||
| -		*(.stubs) | ||||
| +		KEEP(*(.stubs)) | ||||
|  	} | ||||
|  	. = __stubs_start + SIZEOF(.stubs); | ||||
|  	__stubs_end = .; | ||||
| @@ -187,24 +187,24 @@ SECTIONS | ||||
|  	} | ||||
|  	.init.arch.info : { | ||||
|  		__arch_info_begin = .; | ||||
| -		*(.arch.info.init) | ||||
| +		KEEP(*(.arch.info.init)) | ||||
|  		__arch_info_end = .; | ||||
|  	} | ||||
|  	.init.tagtable : { | ||||
|  		__tagtable_begin = .; | ||||
| -		*(.taglist.init) | ||||
| +		KEEP(*(.taglist.init)) | ||||
|  		__tagtable_end = .; | ||||
|  	} | ||||
|  #ifdef CONFIG_SMP_ON_UP | ||||
|  	.init.smpalt : { | ||||
|  		__smpalt_begin = .; | ||||
| -		*(.alt.smp.init) | ||||
| +		KEEP(*(.alt.smp.init)) | ||||
|  		__smpalt_end = .; | ||||
|  	} | ||||
|  #endif | ||||
|  	.init.pv_table : { | ||||
|  		__pv_table_begin = .; | ||||
| -		*(.pv_table) | ||||
| +		KEEP(*(.pv_table)) | ||||
|  		__pv_table_end = .; | ||||
|  	} | ||||
|  	.init.data : { | ||||
| --- a/arch/arm/boot/compressed/Makefile | ||||
| +++ b/arch/arm/boot/compressed/Makefile | ||||
| @@ -105,6 +105,7 @@ ifeq ($(CONFIG_FUNCTION_TRACER),y) | ||||
|  ORIG_CFLAGS := $(KBUILD_CFLAGS) | ||||
|  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) | ||||
|  endif | ||||
| +KBUILD_CFLAGS_KERNEL := $(patsubst -f%-sections,,$(KBUILD_CFLAGS_KERNEL)) | ||||
|   | ||||
|  ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) | ||||
|  asflags-y := -DZIMAGE | ||||
| @@ -1,88 +0,0 @@ | ||||
| --- a/include/asm-generic/vmlinux.lds.h | ||||
| +++ b/include/asm-generic/vmlinux.lds.h | ||||
| @@ -54,6 +54,16 @@ | ||||
|  #define LOAD_OFFSET 0 | ||||
|  #endif | ||||
|   | ||||
| +#ifndef SYMTAB_KEEP | ||||
| +#define SYMTAB_KEEP KEEP(*(SORT(___ksymtab+*))) | ||||
| +#define SYMTAB_KEEP_GPL KEEP(*(SORT(___ksymtab_gpl+*))) | ||||
| +#endif | ||||
| + | ||||
| +#ifndef SYMTAB_DISCARD | ||||
| +#define SYMTAB_DISCARD | ||||
| +#define SYMTAB_DISCARD_GPL | ||||
| +#endif | ||||
| + | ||||
|  #include <linux/export.h> | ||||
|   | ||||
|  /* Align . to a 8 byte boundary equals to maximum function alignment. */ | ||||
| @@ -301,14 +311,14 @@ | ||||
|  	/* Kernel symbol table: Normal symbols */			\ | ||||
|  	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab) = .;			\ | ||||
| -		KEEP(*(SORT(___ksymtab+*)))				\ | ||||
| +		SYMTAB_KEEP						\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab) = .;			\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: GPL-only symbols */			\ | ||||
|  	__ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {	\ | ||||
|  		VMLINUX_SYMBOL(__start___ksymtab_gpl) = .;		\ | ||||
| -		KEEP(*(SORT(___ksymtab_gpl+*)))				\ | ||||
| +		SYMTAB_KEEP_GPL						\ | ||||
|  		VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .;		\ | ||||
|  	}								\ | ||||
|  									\ | ||||
| @@ -370,7 +380,7 @@ | ||||
|  									\ | ||||
|  	/* Kernel symbol table: strings */				\ | ||||
|          __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {	\ | ||||
| -		*(__ksymtab_strings)					\ | ||||
| +		*(__ksymtab_strings+*)					\ | ||||
|  	}								\ | ||||
|  									\ | ||||
|  	/* __*init sections */						\ | ||||
| @@ -694,6 +704,8 @@ | ||||
|  	EXIT_TEXT							\ | ||||
|  	EXIT_DATA							\ | ||||
|  	EXIT_CALL							\ | ||||
| +	SYMTAB_DISCARD							\ | ||||
| +	SYMTAB_DISCARD_GPL						\ | ||||
|  	*(.discard)							\ | ||||
|  	*(.discard.*)							\ | ||||
|  	} | ||||
| --- a/scripts/Makefile.build | ||||
| +++ b/scripts/Makefile.build | ||||
| @@ -299,7 +299,7 @@ targets += $(extra-y) $(MAKECMDGOALS) $( | ||||
|  # Linker scripts preprocessor (.lds.S -> .lds) | ||||
|  # --------------------------------------------------------------------------- | ||||
|  quiet_cmd_cpp_lds_S = LDS     $@ | ||||
| -      cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -C -U$(ARCH) \ | ||||
| +      cmd_cpp_lds_S = $(CPP) $(EXTRA_LDSFLAGS) $(cpp_flags) -P -C -U$(ARCH) \ | ||||
|  	                     -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $< | ||||
|   | ||||
|  $(obj)/%.lds: $(src)/%.lds.S FORCE | ||||
| --- a/include/linux/export.h | ||||
| +++ b/include/linux/export.h | ||||
| @@ -52,12 +52,19 @@ extern struct module __this_module; | ||||
|  #define __CRC_SYMBOL(sym, sec) | ||||
|  #endif | ||||
|   | ||||
| +#ifdef MODULE | ||||
| +#define __EXPORT_SUFFIX(sym) | ||||
| +#else | ||||
| +#define __EXPORT_SUFFIX(sym) "+" #sym | ||||
| +#endif | ||||
| + | ||||
|  /* For every exported symbol, place a struct in the __ksymtab section */ | ||||
|  #define __EXPORT_SYMBOL(sym, sec)				\ | ||||
|  	extern typeof(sym) sym;					\ | ||||
|  	__CRC_SYMBOL(sym, sec)					\ | ||||
|  	static const char __kstrtab_##sym[]			\ | ||||
| -	__attribute__((section("__ksymtab_strings"), aligned(1))) \ | ||||
| +	__attribute__((section("__ksymtab_strings"		\ | ||||
| +	  __EXPORT_SUFFIX(sym)), aligned(1)))			\ | ||||
|  	= VMLINUX_SYMBOL_STR(sym);				\ | ||||
|  	extern const struct kernel_symbol __ksymtab_##sym;	\ | ||||
|  	__visible const struct kernel_symbol __ksymtab_##sym	\ | ||||
| @@ -1,58 +0,0 @@ | ||||
| --- a/scripts/Makefile.lib | ||||
| +++ b/scripts/Makefile.lib | ||||
| @@ -324,7 +324,7 @@ cmd_bzip2 = (cat $(filter-out FORCE,$^) | ||||
|   | ||||
|  quiet_cmd_lzma = LZMA    $@ | ||||
|  cmd_lzma = (cat $(filter-out FORCE,$^) | \ | ||||
| -	lzma -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ | ||||
| +	lzma e -d20 -lc1 -lp2 -pb2 -eos -si -so && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ | ||||
|  	(rm -f $@ ; false) | ||||
|   | ||||
|  quiet_cmd_lzo = LZO     $@ | ||||
| --- a/scripts/gen_initramfs_list.sh | ||||
| +++ b/scripts/gen_initramfs_list.sh | ||||
| @@ -226,7 +226,7 @@ cpio_list= | ||||
|  output="/dev/stdout" | ||||
|  output_file="" | ||||
|  is_cpio_compressed= | ||||
| -compr="gzip -n -9 -f" | ||||
| +compr="gzip -n -9 -f -" | ||||
|   | ||||
|  arg="$1" | ||||
|  case "$arg" in | ||||
| @@ -242,13 +242,13 @@ case "$arg" in | ||||
|  		output=${cpio_list} | ||||
|  		echo "$output_file" | grep -q "\.gz$" \ | ||||
|                  && [ -x "`which gzip 2> /dev/null`" ] \ | ||||
| -                && compr="gzip -n -9 -f" | ||||
| +                && compr="gzip -n -9 -f -" | ||||
|  		echo "$output_file" | grep -q "\.bz2$" \ | ||||
|                  && [ -x "`which bzip2 2> /dev/null`" ] \ | ||||
| -                && compr="bzip2 -9 -f" | ||||
| +                && compr="bzip2 -9 -f -" | ||||
|  		echo "$output_file" | grep -q "\.lzma$" \ | ||||
|                  && [ -x "`which lzma 2> /dev/null`" ] \ | ||||
| -                && compr="lzma -9 -f" | ||||
| +                && compr="lzma e -d20 -lc1 -lp2 -pb2 -eos -si -so" | ||||
|  		echo "$output_file" | grep -q "\.xz$" \ | ||||
|                  && [ -x "`which xz 2> /dev/null`" ] \ | ||||
|                  && compr="xz --check=crc32 --lzma2=dict=1MiB" | ||||
| @@ -315,7 +315,7 @@ if [ ! -z ${output_file} ]; then | ||||
|  	if [ "${is_cpio_compressed}" = "compressed" ]; then | ||||
|  		cat ${cpio_tfile} > ${output_file} | ||||
|  	else | ||||
| -		(cat ${cpio_tfile} | ${compr}  - > ${output_file}) \ | ||||
| +		(cat ${cpio_tfile} | ${compr} > ${output_file}) \ | ||||
|  		|| (rm -f ${output_file} ; false) | ||||
|  	fi | ||||
|  	[ -z ${cpio_file} ] && rm ${cpio_tfile} | ||||
| --- a/lib/decompress.c | ||||
| +++ b/lib/decompress.c | ||||
| @@ -48,6 +48,7 @@ static const struct compress_format comp | ||||
|  	{ {0x1f, 0x9e}, "gzip", gunzip }, | ||||
|  	{ {0x42, 0x5a}, "bzip2", bunzip2 }, | ||||
|  	{ {0x5d, 0x00}, "lzma", unlzma }, | ||||
| +	{ {0x6d, 0x00}, "lzma-openwrt", unlzma }, | ||||
|  	{ {0xfd, 0x37}, "xz", unxz }, | ||||
|  	{ {0x89, 0x4c}, "lzo", unlzo }, | ||||
|  	{ {0x02, 0x21}, "lz4", unlz4 }, | ||||
| @@ -1,18 +0,0 @@ | ||||
| --- a/net/netfilter/Kconfig | ||||
| +++ b/net/netfilter/Kconfig | ||||
| @@ -218,7 +218,6 @@ config NF_CONNTRACK_FTP | ||||
|   | ||||
|  config NF_CONNTRACK_H323 | ||||
|  	tristate "H.323 protocol support" | ||||
| -	depends on IPV6 || IPV6=n | ||||
|  	depends on NETFILTER_ADVANCED | ||||
|  	help | ||||
|  	  H.323 is a VoIP signalling protocol from ITU-T. As one of the most | ||||
| @@ -929,7 +928,6 @@ config NETFILTER_XT_TARGET_SECMARK | ||||
|   | ||||
|  config NETFILTER_XT_TARGET_TCPMSS | ||||
|  	tristate '"TCPMSS" target support' | ||||
| -	depends on IPV6 || IPV6=n | ||||
|  	default m if NETFILTER_ADVANCED=n | ||||
|  	---help--- | ||||
|  	  This option adds a `TCPMSS' target, which allows you to alter the | ||||
| @@ -1,18 +0,0 @@ | ||||
| --- a/sound/core/Kconfig | ||||
| +++ b/sound/core/Kconfig | ||||
| @@ -16,13 +16,13 @@ config SND_DMAENGINE_PCM | ||||
|  	tristate | ||||
|   | ||||
|  config SND_HWDEP | ||||
| -	tristate | ||||
| +	tristate "Sound hardware support" | ||||
|   | ||||
|  config SND_RAWMIDI | ||||
|  	tristate | ||||
|   | ||||
|  config SND_COMPRESS_OFFLOAD | ||||
| -	tristate | ||||
| +	tristate "Compression offloading support" | ||||
|   | ||||
|  # To be effective this also requires INPUT - users should say: | ||||
|  #    select SND_JACK if INPUT=y || INPUT=SND | ||||
| @@ -1,10 +0,0 @@ | ||||
| --- a/drivers/crypto/Kconfig | ||||
| +++ b/drivers/crypto/Kconfig | ||||
| @@ -163,6 +163,7 @@ config CRYPTO_DEV_MV_CESA | ||||
|  	tristate "Marvell's Cryptographic Engine" | ||||
|  	depends on PLAT_ORION | ||||
|  	select CRYPTO_AES | ||||
| +	select CRYPTO_HASH2 | ||||
|  	select CRYPTO_BLKCIPHER | ||||
|  	select CRYPTO_HASH | ||||
|  	select SRAM | ||||
| @@ -1,29 +0,0 @@ | ||||
| --- a/drivers/ssb/Kconfig | ||||
| +++ b/drivers/ssb/Kconfig | ||||
| @@ -29,6 +29,7 @@ config SSB_SPROM | ||||
|  config SSB_BLOCKIO | ||||
|  	bool | ||||
|  	depends on SSB | ||||
| +	default y | ||||
|   | ||||
|  config SSB_PCIHOST_POSSIBLE | ||||
|  	bool | ||||
| @@ -49,7 +50,7 @@ config SSB_PCIHOST | ||||
|  config SSB_B43_PCI_BRIDGE | ||||
|  	bool | ||||
|  	depends on SSB_PCIHOST | ||||
| -	default n | ||||
| +	default y | ||||
|   | ||||
|  config SSB_PCMCIAHOST_POSSIBLE | ||||
|  	bool | ||||
| --- a/drivers/bcma/Kconfig | ||||
| +++ b/drivers/bcma/Kconfig | ||||
| @@ -17,6 +17,7 @@ config BCMA | ||||
|  config BCMA_BLOCKIO | ||||
|  	bool | ||||
|  	depends on BCMA | ||||
| +	default y | ||||
|   | ||||
|  config BCMA_HOST_PCI_POSSIBLE | ||||
|  	bool | ||||
| @@ -1,23 +0,0 @@ | ||||
| --- a/lib/Kconfig | ||||
| +++ b/lib/Kconfig | ||||
| @@ -332,16 +332,16 @@ config BCH_CONST_T | ||||
|  # Textsearch support is select'ed if needed | ||||
|  # | ||||
|  config TEXTSEARCH | ||||
| -	bool | ||||
| +	boolean	"Textsearch support" | ||||
|   | ||||
|  config TEXTSEARCH_KMP | ||||
| -	tristate | ||||
| +	tristate "Textsearch KMP" | ||||
|   | ||||
|  config TEXTSEARCH_BM | ||||
| -	tristate | ||||
| +	tristate "Textsearch BM" | ||||
|   | ||||
|  config TEXTSEARCH_FSM | ||||
| -	tristate | ||||
| +	tristate "Textsearch FSM" | ||||
|   | ||||
|  config BTREE | ||||
|  	bool | ||||
| @@ -1,31 +0,0 @@ | ||||
| --- a/net/wireless/Kconfig | ||||
| +++ b/net/wireless/Kconfig | ||||
| @@ -191,7 +191,7 @@ config CFG80211_WEXT_EXPORT | ||||
|  	  wext compatibility symbols to be exported. | ||||
|   | ||||
|  config LIB80211 | ||||
| -	tristate | ||||
| +	tristate "LIB80211" | ||||
|  	default n | ||||
|  	help | ||||
|  	  This options enables a library of common routines used | ||||
| @@ -200,13 +200,16 @@ config LIB80211 | ||||
|  	  Drivers should select this themselves if needed. | ||||
|   | ||||
|  config LIB80211_CRYPT_WEP | ||||
| -	tristate | ||||
| +	tristate "LIB80211_CRYPT_WEP" | ||||
| +	select LIB80211 | ||||
|   | ||||
|  config LIB80211_CRYPT_CCMP | ||||
| -	tristate | ||||
| +	tristate "LIB80211_CRYPT_CCMP" | ||||
| +	select LIB80211 | ||||
|   | ||||
|  config LIB80211_CRYPT_TKIP | ||||
| -	tristate | ||||
| +	tristate "LIB80211_CRYPT_TKIP" | ||||
| +	select LIB80211 | ||||
|   | ||||
|  config LIB80211_DEBUG | ||||
|  	bool "lib80211 debugging messages" | ||||
| @@ -1,47 +0,0 @@ | ||||
| --- a/crypto/Kconfig | ||||
| +++ b/crypto/Kconfig | ||||
| @@ -32,7 +32,7 @@ config CRYPTO_FIPS | ||||
|  	  this is. | ||||
|   | ||||
|  config CRYPTO_ALGAPI | ||||
| -	tristate | ||||
| +	tristate "ALGAPI" | ||||
|  	select CRYPTO_ALGAPI2 | ||||
|  	help | ||||
|  	  This option provides the API for cryptographic algorithms. | ||||
| @@ -41,7 +41,7 @@ config CRYPTO_ALGAPI2 | ||||
|  	tristate | ||||
|   | ||||
|  config CRYPTO_AEAD | ||||
| -	tristate | ||||
| +	tristate "AEAD" | ||||
|  	select CRYPTO_AEAD2 | ||||
|  	select CRYPTO_ALGAPI | ||||
|   | ||||
| @@ -52,7 +52,7 @@ config CRYPTO_AEAD2 | ||||
|  	select CRYPTO_RNG2 | ||||
|   | ||||
|  config CRYPTO_BLKCIPHER | ||||
| -	tristate | ||||
| +	tristate "BLKCIPHER" | ||||
|  	select CRYPTO_BLKCIPHER2 | ||||
|  	select CRYPTO_ALGAPI | ||||
|   | ||||
| @@ -63,7 +63,7 @@ config CRYPTO_BLKCIPHER2 | ||||
|  	select CRYPTO_WORKQUEUE | ||||
|   | ||||
|  config CRYPTO_HASH | ||||
| -	tristate | ||||
| +	tristate "HASH" | ||||
|  	select CRYPTO_HASH2 | ||||
|  	select CRYPTO_ALGAPI | ||||
|   | ||||
| @@ -72,7 +72,7 @@ config CRYPTO_HASH2 | ||||
|  	select CRYPTO_ALGAPI2 | ||||
|   | ||||
|  config CRYPTO_RNG | ||||
| -	tristate | ||||
| +	tristate "RNG" | ||||
|  	select CRYPTO_RNG2 | ||||
|  	select CRYPTO_ALGAPI | ||||
|   | ||||
| @@ -1,22 +0,0 @@ | ||||
| --- a/net/wireless/Kconfig | ||||
| +++ b/net/wireless/Kconfig | ||||
| @@ -1,5 +1,5 @@ | ||||
|  config WIRELESS_EXT | ||||
| -	bool | ||||
| +	bool "Wireless extensions" | ||||
|   | ||||
|  config WEXT_CORE | ||||
|  	def_bool y | ||||
| @@ -11,10 +11,10 @@ config WEXT_PROC | ||||
|  	depends on WEXT_CORE | ||||
|   | ||||
|  config WEXT_SPY | ||||
| -	bool | ||||
| +	bool "WEXT_SPY" | ||||
|   | ||||
|  config WEXT_PRIV | ||||
| -	bool | ||||
| +	bool "WEXT_PRIV" | ||||
|   | ||||
|  config CFG80211 | ||||
|  	tristate "cfg80211 - wireless configuration API" | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/net/netfilter/Kconfig | ||||
| +++ b/net/netfilter/Kconfig | ||||
| @@ -10,7 +10,7 @@ config NETFILTER_INGRESS | ||||
|  	  infrastructure. | ||||
|   | ||||
|  config NETFILTER_NETLINK | ||||
| -	tristate | ||||
| +	tristate "Netfilter NFNETLINK interface" | ||||
|   | ||||
|  config NETFILTER_NETLINK_ACCT | ||||
|  tristate "Netfilter NFACCT over NFNETLINK interface" | ||||
| @@ -1,87 +0,0 @@ | ||||
| --- a/drivers/base/regmap/Kconfig | ||||
| +++ b/drivers/base/regmap/Kconfig | ||||
| @@ -3,29 +3,35 @@ | ||||
|  # subsystems should select the appropriate symbols. | ||||
|   | ||||
|  config REGMAP | ||||
| -	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) | ||||
|  	select LZO_COMPRESS | ||||
|  	select LZO_DECOMPRESS | ||||
|  	select IRQ_DOMAIN if REGMAP_IRQ | ||||
| -	bool | ||||
| +	tristate "Regmap" | ||||
|   | ||||
|  config REGMAP_AC97 | ||||
| +	select REGMAP | ||||
|  	tristate | ||||
|   | ||||
|  config REGMAP_I2C | ||||
| -	tristate | ||||
| +	tristate "Regmap I2C" | ||||
| +	select REGMAP | ||||
|  	depends on I2C | ||||
|   | ||||
|  config REGMAP_SPI | ||||
| -	tristate | ||||
| +	tristate "Regmap SPI" | ||||
| +	select REGMAP | ||||
| +	depends on SPI_MASTER | ||||
|  	depends on SPI | ||||
|   | ||||
|  config REGMAP_SPMI | ||||
| +	select REGMAP | ||||
|  	tristate | ||||
|  	depends on SPMI | ||||
|   | ||||
|  config REGMAP_MMIO | ||||
| -	tristate | ||||
| +	tristate "Regmap MMIO" | ||||
| +	select REGMAP | ||||
|   | ||||
|  config REGMAP_IRQ | ||||
| +	select REGMAP | ||||
|  	bool | ||||
| --- a/include/linux/regmap.h | ||||
| +++ b/include/linux/regmap.h | ||||
| @@ -65,7 +65,7 @@ struct reg_sequence { | ||||
|  	unsigned int delay_us; | ||||
|  }; | ||||
|   | ||||
| -#ifdef CONFIG_REGMAP | ||||
| +#if IS_ENABLED(CONFIG_REGMAP) | ||||
|   | ||||
|  enum regmap_endian { | ||||
|  	/* Unspecified -> 0 -> Backwards compatible default */ | ||||
| --- a/drivers/base/regmap/Makefile | ||||
| +++ b/drivers/base/regmap/Makefile | ||||
| @@ -1,9 +1,11 @@ | ||||
|  # For include/trace/define_trace.h to include trace.h | ||||
|  CFLAGS_regmap.o := -I$(src) | ||||
|   | ||||
| -obj-$(CONFIG_REGMAP) += regmap.o regcache.o | ||||
| -obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o | ||||
| -obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o | ||||
| +regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-lzo.o regcache-flat.o | ||||
| +ifdef CONFIG_DEBUG_FS | ||||
| +regmap-core-objs += regmap-debugfs.o | ||||
| +endif | ||||
| +obj-$(CONFIG_REGMAP) += regmap-core.o | ||||
|  obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o | ||||
|  obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o | ||||
|  obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o | ||||
| --- a/drivers/base/regmap/regmap.c | ||||
| +++ b/drivers/base/regmap/regmap.c | ||||
| @@ -13,6 +13,7 @@ | ||||
|  #include <linux/device.h> | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/export.h> | ||||
| +#include <linux/module.h> | ||||
|  #include <linux/mutex.h> | ||||
|  #include <linux/err.h> | ||||
|  #include <linux/of.h> | ||||
| @@ -2852,3 +2853,5 @@ static int __init regmap_initcall(void) | ||||
|  	return 0; | ||||
|  } | ||||
|  postcore_initcall(regmap_initcall); | ||||
| + | ||||
| +MODULE_LICENSE("GPL"); | ||||
| @@ -1,38 +0,0 @@ | ||||
| --- a/crypto/Kconfig | ||||
| +++ b/crypto/Kconfig | ||||
| @@ -119,11 +119,11 @@ config CRYPTO_MANAGER | ||||
|   | ||||
|  config CRYPTO_MANAGER2 | ||||
|  	def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y) | ||||
| -	select CRYPTO_AEAD2 | ||||
| -	select CRYPTO_HASH2 | ||||
| -	select CRYPTO_BLKCIPHER2 | ||||
| -	select CRYPTO_PCOMP2 | ||||
| -	select CRYPTO_AKCIPHER2 | ||||
| +	select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||
| +	select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||
| +	select CRYPTO_BLKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||
| +	select CRYPTO_PCOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||
| +	select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||
|   | ||||
|  config CRYPTO_USER | ||||
|  	tristate "Userspace cryptographic algorithm configuration" | ||||
| --- a/crypto/algboss.c | ||||
| +++ b/crypto/algboss.c | ||||
| @@ -248,12 +248,16 @@ static int cryptomgr_schedule_test(struc | ||||
|  	type = alg->cra_flags; | ||||
|   | ||||
|  	/* This piece of crap needs to disappear into per-type test hooks. */ | ||||
| +#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS | ||||
| +	type |= CRYPTO_ALG_TESTED; | ||||
| +#else | ||||
|  	if (!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) & | ||||
|  	      CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV) && | ||||
|  	    ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == | ||||
|  	     CRYPTO_ALG_TYPE_BLKCIPHER ? alg->cra_blkcipher.ivsize : | ||||
|  					 alg->cra_ablkcipher.ivsize)) | ||||
|  		type |= CRYPTO_ALG_TESTED; | ||||
| +#endif | ||||
|   | ||||
|  	param->type = type; | ||||
|   | ||||
| @@ -1,36 +0,0 @@ | ||||
| --- a/lib/Kconfig | ||||
| +++ b/lib/Kconfig | ||||
| @@ -216,26 +216,26 @@ config 842_DECOMPRESS | ||||
|  	tristate | ||||
|   | ||||
|  config ZLIB_INFLATE | ||||
| -	tristate | ||||
| +	tristate "ZLIB inflate support" | ||||
|   | ||||
|  config ZLIB_DEFLATE | ||||
| -	tristate | ||||
| +	tristate "ZLIB deflate support" | ||||
|  	select BITREVERSE | ||||
|   | ||||
|  config LZO_COMPRESS | ||||
| -	tristate | ||||
| +	tristate "LZO compress support" | ||||
|   | ||||
|  config LZO_DECOMPRESS | ||||
| -	tristate | ||||
| +	tristate "LZO decompress support" | ||||
|   | ||||
|  config LZ4_COMPRESS | ||||
| -	tristate | ||||
| +	tristate "LZ4 compress support" | ||||
|   | ||||
|  config LZ4HC_COMPRESS | ||||
| -	tristate | ||||
| +	tristate "LZ4HC compress support" | ||||
|   | ||||
|  config LZ4_DECOMPRESS | ||||
| -	tristate | ||||
| +	tristate "LZ4 decompress support" | ||||
|   | ||||
|  source "lib/xz/Kconfig" | ||||
|   | ||||
| @@ -1,34 +0,0 @@ | ||||
| From 8b05e325824d3b38e52a7748b3b5dc34dc1c0f6d Mon Sep 17 00:00:00 2001 | ||||
| From: David Heidelberger <david.heidelberger@ixit.cz> | ||||
| Date: Mon, 29 Jun 2015 14:37:54 +0200 | ||||
| Subject: [PATCH 1/3] uapi/kernel.h: glibc specific inclusion of sysinfo.h | ||||
|  | ||||
| including sysinfo.h from kernel.h makes no sense whatsoever, | ||||
| but removing it breaks glibc's userspace header, | ||||
| which includes kernel.h instead of sysinfo.h from their sys/sysinfo.h. | ||||
| this seems to be a historical mistake. | ||||
| on musl, including any header that uses kernel.h directly or indirectly | ||||
| plus sys/sysinfo.h will produce a compile error due to redefinition of | ||||
| struct sysinfo from sys/sysinfo.h. | ||||
| so for now, only include it on glibc or when including from kernel | ||||
| in order not to break their headers. | ||||
|  | ||||
| Signed-off-by: John Spencer <maillist-linux@barfooze.de> | ||||
| Signed-off-by: David Heidelberger <david.heidelberger@ixit.cz> | ||||
| Signed-off-by: Jonas Gorski <jogo@openwrt.org> | ||||
| --- | ||||
|  include/uapi/linux/kernel.h | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
|  | ||||
| --- a/include/uapi/linux/kernel.h | ||||
| +++ b/include/uapi/linux/kernel.h | ||||
| @@ -1,7 +1,9 @@ | ||||
|  #ifndef _UAPI_LINUX_KERNEL_H | ||||
|  #define _UAPI_LINUX_KERNEL_H | ||||
|   | ||||
| +#if defined(__KERNEL__) || defined( __GLIBC__) | ||||
|  #include <linux/sysinfo.h> | ||||
| +#endif | ||||
|   | ||||
|  /* | ||||
|   * 'kernel.h' contains some often-used function prototypes etc | ||||
| @@ -1,81 +0,0 @@ | ||||
| From f972afc2509eebcb00d370256c55b112a3b5ffca Mon Sep 17 00:00:00 2001 | ||||
| From: David Heidelberger <david.heidelberger@ixit.cz> | ||||
| Date: Mon, 29 Jun 2015 16:50:40 +0200 | ||||
| Subject: [PATCH 2/3] uapi/libc-compat.h: do not rely on __GLIBC__ | ||||
|  | ||||
| Musl provides the same structs as glibc, but does not provide a define to | ||||
| allow its detection. Since the absence of __GLIBC__ also can mean that it | ||||
| is included from the kernel, change the __GLIBC__ detection to | ||||
| !__KERNEL__, which should always be true when included from userspace. | ||||
|  | ||||
| Signed-off-by: John Spencer <maillist-linux@barfooze.de> | ||||
| Tested-by: David Heidelberger <david.heidelberger@ixit.cz> | ||||
| Signed-off-by: Jonas Gorski <jogo@openwrt.org> | ||||
| --- | ||||
|  include/uapi/linux/libc-compat.h | 18 +++++++++--------- | ||||
|  1 file changed, 9 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/include/uapi/linux/libc-compat.h | ||||
| +++ b/include/uapi/linux/libc-compat.h | ||||
| @@ -48,13 +48,13 @@ | ||||
|  #ifndef _UAPI_LIBC_COMPAT_H | ||||
|  #define _UAPI_LIBC_COMPAT_H | ||||
|   | ||||
| -/* We have included glibc headers... */ | ||||
| -#if defined(__GLIBC__) | ||||
| +/* We have included libc headers... */ | ||||
| +#if !defined(__KERNEL__) | ||||
|   | ||||
| -/* Coordinate with glibc netinet/in.h header. */ | ||||
| +/* Coordinate with libc netinet/in.h header. */ | ||||
|  #if defined(_NETINET_IN_H) | ||||
|   | ||||
| -/* GLIBC headers included first so don't define anything | ||||
| +/* LIBC headers included first so don't define anything | ||||
|   * that would already be defined. */ | ||||
|  #define __UAPI_DEF_IN_ADDR		0 | ||||
|  #define __UAPI_DEF_IN_IPPROTO		0 | ||||
| @@ -68,7 +68,7 @@ | ||||
|   * if the glibc code didn't define them. This guard matches | ||||
|   * the guard in glibc/inet/netinet/in.h which defines the | ||||
|   * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ | ||||
| -#if defined(__USE_MISC) || defined (__USE_GNU) | ||||
| +#if !defined(__GLIBC__) || defined(__USE_MISC) || defined (__USE_GNU) | ||||
|  #define __UAPI_DEF_IN6_ADDR_ALT		0 | ||||
|  #else | ||||
|  #define __UAPI_DEF_IN6_ADDR_ALT		1 | ||||
| @@ -83,7 +83,7 @@ | ||||
|  #else | ||||
|   | ||||
|  /* Linux headers included first, and we must define everything | ||||
| - * we need. The expectation is that glibc will check the | ||||
| + * we need. The expectation is that the libc will check the | ||||
|   * __UAPI_DEF_* defines and adjust appropriately. */ | ||||
|  #define __UAPI_DEF_IN_ADDR		1 | ||||
|  #define __UAPI_DEF_IN_IPPROTO		1 | ||||
| @@ -93,7 +93,7 @@ | ||||
|  #define __UAPI_DEF_IN_CLASS		1 | ||||
|   | ||||
|  #define __UAPI_DEF_IN6_ADDR		1 | ||||
| -/* We unconditionally define the in6_addr macros and glibc must | ||||
| +/* We unconditionally define the in6_addr macros and the libc must | ||||
|   * coordinate. */ | ||||
|  #define __UAPI_DEF_IN6_ADDR_ALT		1 | ||||
|  #define __UAPI_DEF_SOCKADDR_IN6		1 | ||||
| @@ -115,7 +115,7 @@ | ||||
|  /* If we did not see any headers from any supported C libraries, | ||||
|   * or we are being included in the kernel, then define everything | ||||
|   * that we need. */ | ||||
| -#else /* !defined(__GLIBC__) */ | ||||
| +#else /* defined(__KERNEL__) */ | ||||
|   | ||||
|  /* Definitions for in.h */ | ||||
|  #define __UAPI_DEF_IN_ADDR		1 | ||||
| @@ -138,6 +138,6 @@ | ||||
|  /* Definitions for xattr.h */ | ||||
|  #define __UAPI_DEF_XATTR		1 | ||||
|   | ||||
| -#endif /* __GLIBC__ */ | ||||
| +#endif /* __KERNEL__ */ | ||||
|   | ||||
|  #endif /* _UAPI_LIBC_COMPAT_H */ | ||||
| @@ -1,67 +0,0 @@ | ||||
| From fcbb6fed85ea9ff4feb4f1ebd4f0f235fdaf06b6 Mon Sep 17 00:00:00 2001 | ||||
| From: David Heidelberger <david.heidelberger@ixit.cz> | ||||
| Date: Mon, 29 Jun 2015 16:53:03 +0200 | ||||
| Subject: [PATCH 3/3] uapi/if_ether.h: prevent redefinition of struct ethhdr | ||||
|  | ||||
| Musl provides its own ethhdr struct definition. Add a guard to prevent | ||||
| its definition of the appropriate musl header has already been included. | ||||
|  | ||||
| Signed-off-by: John Spencer <maillist-linux@barfooze.de> | ||||
| Tested-by: David Heidelberger <david.heidelberger@ixit.cz> | ||||
| Signed-off-by: Jonas Gorski <jogo@openwrt.org> | ||||
| --- | ||||
|  include/uapi/linux/if_ether.h    |  3 +++ | ||||
|  include/uapi/linux/libc-compat.h | 11 +++++++++++ | ||||
|  2 files changed, 14 insertions(+) | ||||
|  | ||||
| --- a/include/uapi/linux/if_ether.h | ||||
| +++ b/include/uapi/linux/if_ether.h | ||||
| @@ -22,6 +22,7 @@ | ||||
|  #define _UAPI_LINUX_IF_ETHER_H | ||||
|   | ||||
|  #include <linux/types.h> | ||||
| +#include <linux/libc-compat.h> | ||||
|   | ||||
|  /* | ||||
|   *	IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble | ||||
| @@ -135,11 +136,13 @@ | ||||
|   *	This is an Ethernet frame header. | ||||
|   */ | ||||
|   | ||||
| +#if __UAPI_DEF_ETHHDR | ||||
|  struct ethhdr { | ||||
|  	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/ | ||||
|  	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/ | ||||
|  	__be16		h_proto;		/* packet type ID field	*/ | ||||
|  } __attribute__((packed)); | ||||
| +#endif | ||||
|   | ||||
|   | ||||
|  #endif /* _UAPI_LINUX_IF_ETHER_H */ | ||||
| --- a/include/uapi/linux/libc-compat.h | ||||
| +++ b/include/uapi/linux/libc-compat.h | ||||
| @@ -51,6 +51,14 @@ | ||||
|  /* We have included libc headers... */ | ||||
|  #if !defined(__KERNEL__) | ||||
|   | ||||
| +/* musl defines the ethhdr struct itself in its netinet/if_ether.h. | ||||
| + * Glibc just includes the kernel header and uses a different guard. */ | ||||
| +#if defined(_NETINET_IF_ETHER_H) | ||||
| +#define __UAPI_DEF_ETHHDR		0 | ||||
| +#else | ||||
| +#define __UAPI_DEF_ETHHDR		1 | ||||
| +#endif | ||||
| + | ||||
|  /* Coordinate with libc netinet/in.h header. */ | ||||
|  #if defined(_NETINET_IN_H) | ||||
|   | ||||
| @@ -117,6 +125,9 @@ | ||||
|   * that we need. */ | ||||
|  #else /* defined(__KERNEL__) */ | ||||
|   | ||||
| +/* Definitions for if_ether.h */ | ||||
| +#define __UAPI_DEF_ETHHDR 		1 | ||||
| + | ||||
|  /* Definitions for in.h */ | ||||
|  #define __UAPI_DEF_IN_ADDR		1 | ||||
|  #define __UAPI_DEF_IN_IPPROTO		1 | ||||
| @@ -1,39 +0,0 @@ | ||||
| From: Mark Miller <mark@mirell.org> | ||||
|  | ||||
| This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on | ||||
| certain Broadcom chipsets running CFE in order to load the kernel. | ||||
|  | ||||
| Signed-off-by: Mark Miller <mark@mirell.org> | ||||
| Acked-by: Rob Landley <rob@landley.net> | ||||
| --- | ||||
| --- a/arch/mips/Kconfig | ||||
| +++ b/arch/mips/Kconfig | ||||
| @@ -1003,9 +1003,6 @@ config FW_ARC | ||||
|  config ARCH_MAY_HAVE_PC_FDC | ||||
|  	bool | ||||
|   | ||||
| -config BOOT_RAW | ||||
| -	bool | ||||
| - | ||||
|  config CEVT_BCM1480 | ||||
|  	bool | ||||
|   | ||||
| @@ -2733,6 +2730,18 @@ choice | ||||
|  		  if you don't intend to always append a DTB. | ||||
|  endchoice | ||||
|   | ||||
| +config BOOT_RAW | ||||
| +	bool "Enable the kernel to be executed from the load address" | ||||
| +	default n | ||||
| +	help | ||||
| +	 Allow the kernel to be executed from the load address for | ||||
| +	 bootloaders which cannot read the ELF format. This places | ||||
| +	 a jump to start_kernel at the load address. | ||||
| + | ||||
| +	 If unsure, say N. | ||||
| + | ||||
| + | ||||
| + | ||||
|  endmenu | ||||
|   | ||||
|  config LOCKDEP_SUPPORT | ||||
| @@ -1,28 +0,0 @@ | ||||
| --- a/arch/mips/Kconfig | ||||
| +++ b/arch/mips/Kconfig | ||||
| @@ -1091,6 +1091,10 @@ config SYNC_R4K | ||||
|  config MIPS_MACHINE | ||||
|  	def_bool n | ||||
|   | ||||
| +config IMAGE_CMDLINE_HACK | ||||
| +	bool "OpenWrt specific image command line hack" | ||||
| +	default n | ||||
| + | ||||
|  config NO_IOPORT_MAP | ||||
|  	def_bool n | ||||
|   | ||||
| --- a/arch/mips/kernel/head.S | ||||
| +++ b/arch/mips/kernel/head.S | ||||
| @@ -80,6 +80,12 @@ FEXPORT(__kernel_entry) | ||||
|  	j	kernel_entry | ||||
|  #endif | ||||
|   | ||||
| +#ifdef CONFIG_IMAGE_CMDLINE_HACK | ||||
| +	.ascii	"CMDLINE:" | ||||
| +EXPORT(__image_cmdline) | ||||
| +	.fill	0x400 | ||||
| +#endif /* CONFIG_IMAGE_CMDLINE_HACK */ | ||||
| + | ||||
|  	__REF | ||||
|   | ||||
|  NESTED(kernel_entry, 16, sp)			# kernel entry point | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/arch/mips/Makefile | ||||
| +++ b/arch/mips/Makefile | ||||
| @@ -87,7 +87,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin | ||||
|  # machines may also.  Since BFD is incredibly buggy with respect to | ||||
|  # crossformat linking we rely on the elf2ecoff tool for format conversion. | ||||
|  # | ||||
| -cflags-y			+= -G 0 -mno-abicalls -fno-pic -pipe | ||||
| +cflags-y			+= -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely | ||||
|  cflags-y			+= -msoft-float | ||||
|  LDFLAGS_vmlinux			+= -G 0 -static -n -nostdlib --gc-sections | ||||
|  KBUILD_AFLAGS_MODULE		+= -mlong-calls | ||||
| @@ -1,106 +0,0 @@ | ||||
| From:   Manuel Lauss <manuel.lauss@gmail.com> | ||||
| Subject: [RFC PATCH v4 2/2] MIPS: make FPU emulator optional | ||||
| Date:   Mon,  7 Apr 2014 12:57:04 +0200 | ||||
| Message-Id: <1396868224-252888-2-git-send-email-manuel.lauss@gmail.com> | ||||
|  | ||||
| This small patch makes the MIPS FPU emulator optional. The kernel | ||||
| kills float-users on systems without a hardware FPU by sending a SIGILL. | ||||
|  | ||||
| Disabling the emulator shrinks vmlinux by about 54kBytes (32bit, | ||||
| optimizing for size). | ||||
|  | ||||
| Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com> | ||||
| --- | ||||
| v4: rediffed because of patch 1/2, should now work with micromips as well | ||||
| v3: updated patch description with size savings. | ||||
| v2: incorporated changes suggested by Jonas Gorski | ||||
|     force the fpu emulator on for micromips: relocating the parts | ||||
|     of the mmips code in the emulator to other areas would be a | ||||
|     much larger change; I went the cheap route instead with this. | ||||
|  | ||||
|  arch/mips/Kbuild                     |  2 +- | ||||
|  arch/mips/Kconfig                    | 14 ++++++++++++++ | ||||
|  arch/mips/include/asm/fpu.h          |  5 +++-- | ||||
|  arch/mips/include/asm/fpu_emulator.h | 15 +++++++++++++++ | ||||
|  4 files changed, 33 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/arch/mips/Kconfig | ||||
| +++ b/arch/mips/Kconfig | ||||
| @@ -2680,6 +2680,20 @@ config MIPS_O32_FP64_SUPPORT | ||||
|   | ||||
|  	  If unsure, say N. | ||||
|   | ||||
| +config MIPS_FPU_EMULATOR | ||||
| +	bool "MIPS FPU Emulator" | ||||
| +	default y | ||||
| +	help | ||||
| +	  This option lets you disable the built-in MIPS FPU (Coprocessor 1) | ||||
| +	  emulator, which handles floating-point instructions on processors | ||||
| +	  without a hardware FPU.  It is generally a good idea to keep the | ||||
| +	  emulator built-in, unless you are perfectly sure you have a | ||||
| +	  complete soft-float environment.  With the emulator disabled, all | ||||
| +	  users of float operations will be killed with an illegal instr- | ||||
| +	  uction exception. | ||||
| + | ||||
| +	  Say Y, please. | ||||
| + | ||||
|  config USE_OF | ||||
|  	bool | ||||
|  	select OF | ||||
| --- a/arch/mips/Makefile | ||||
| +++ b/arch/mips/Makefile | ||||
| @@ -285,7 +285,7 @@ OBJCOPYFLAGS		+= --remove-section=.regin | ||||
|  head-y := arch/mips/kernel/head.o | ||||
|   | ||||
|  libs-y			+= arch/mips/lib/ | ||||
| -libs-y			+= arch/mips/math-emu/ | ||||
| +libs-$(CONFIG_MIPS_FPU_EMULATOR)	+= arch/mips/math-emu/ | ||||
|   | ||||
|  # See arch/mips/Kbuild for content of core part of the kernel | ||||
|  core-y += arch/mips/ | ||||
| --- a/arch/mips/include/asm/fpu.h | ||||
| +++ b/arch/mips/include/asm/fpu.h | ||||
| @@ -223,8 +223,10 @@ static inline int init_fpu(void) | ||||
|  		/* Restore FRE */ | ||||
|  		write_c0_config5(config5); | ||||
|  		enable_fpu_hazard(); | ||||
| -	} else | ||||
| +	} else if (IS_ENABLED(CONFIG_MIPS_FPU_EMULATOR)) | ||||
|  		fpu_emulator_init_fpu(); | ||||
| +	else | ||||
| +		ret = SIGILL; | ||||
|   | ||||
|  	return ret; | ||||
|  } | ||||
| --- a/arch/mips/include/asm/fpu_emulator.h | ||||
| +++ b/arch/mips/include/asm/fpu_emulator.h | ||||
| @@ -30,6 +30,7 @@ | ||||
|  #include <asm/local.h> | ||||
|  #include <asm/processor.h> | ||||
|   | ||||
| +#ifdef CONFIG_MIPS_FPU_EMULATOR | ||||
|  #ifdef CONFIG_DEBUG_FS | ||||
|   | ||||
|  struct mips_fpu_emulator_stats { | ||||
| @@ -66,6 +67,21 @@ extern int do_dsemulret(struct pt_regs * | ||||
|  extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, | ||||
|  				    struct mips_fpu_struct *ctx, int has_fpu, | ||||
|  				    void *__user *fault_addr); | ||||
| +#else	/* no CONFIG_MIPS_FPU_EMULATOR */ | ||||
| +static inline int do_dsemulret(struct pt_regs *xcp) | ||||
| +{ | ||||
| +	return 0;	/* 0 means error, should never get here anyway */ | ||||
| +} | ||||
| + | ||||
| +static inline int fpu_emulator_cop1Handler(struct pt_regs *xcp, | ||||
| +				struct mips_fpu_struct *ctx, int has_fpu, | ||||
| +				void *__user *fault_addr) | ||||
| +{ | ||||
| +	*fault_addr = NULL; | ||||
| +	return SIGILL;	/* we don't speak MIPS FPU */ | ||||
| +} | ||||
| +#endif	/* CONFIG_MIPS_FPU_EMULATOR */ | ||||
| + | ||||
|  int process_fpemu_return(int sig, void __user *fault_addr, | ||||
|  			 unsigned long fcr31); | ||||
|  int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | ||||
| @@ -1,352 +0,0 @@ | ||||
| --- a/arch/mips/Makefile | ||||
| +++ b/arch/mips/Makefile | ||||
| @@ -90,8 +90,13 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin | ||||
|  cflags-y			+= -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely | ||||
|  cflags-y			+= -msoft-float | ||||
|  LDFLAGS_vmlinux			+= -G 0 -static -n -nostdlib --gc-sections | ||||
| +ifdef CONFIG_64BIT | ||||
|  KBUILD_AFLAGS_MODULE		+= -mlong-calls | ||||
|  KBUILD_CFLAGS_MODULE		+= -mlong-calls | ||||
| +else | ||||
| +KBUILD_AFLAGS_MODULE		+= -mno-long-calls | ||||
| +KBUILD_CFLAGS_MODULE		+= -mno-long-calls | ||||
| +endif | ||||
|   | ||||
|  ifndef CONFIG_FUNCTION_TRACER | ||||
|  KBUILD_CFLAGS_KERNEL		+= -ffunction-sections -fdata-sections | ||||
| --- a/arch/mips/include/asm/module.h | ||||
| +++ b/arch/mips/include/asm/module.h | ||||
| @@ -11,6 +11,11 @@ struct mod_arch_specific { | ||||
|  	const struct exception_table_entry *dbe_start; | ||||
|  	const struct exception_table_entry *dbe_end; | ||||
|  	struct mips_hi16 *r_mips_hi16_list; | ||||
| + | ||||
| +	void *phys_plt_tbl; | ||||
| +	void *virt_plt_tbl; | ||||
| +	unsigned int phys_plt_offset; | ||||
| +	unsigned int virt_plt_offset; | ||||
|  }; | ||||
|   | ||||
|  typedef uint8_t Elf64_Byte;		/* Type for a 8-bit quantity.  */ | ||||
| --- a/arch/mips/kernel/module.c | ||||
| +++ b/arch/mips/kernel/module.c | ||||
| @@ -43,14 +43,221 @@ struct mips_hi16 { | ||||
|  static LIST_HEAD(dbe_list); | ||||
|  static DEFINE_SPINLOCK(dbe_lock); | ||||
|   | ||||
| -#ifdef MODULE_START | ||||
| +/* | ||||
| + * Get the potential max trampolines size required of the init and | ||||
| + * non-init sections. Only used if we cannot find enough contiguous | ||||
| + * physically mapped memory to put the module into. | ||||
| + */ | ||||
| +static unsigned int | ||||
| +get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, | ||||
| +             const char *secstrings, unsigned int symindex, bool is_init) | ||||
| +{ | ||||
| +	unsigned long ret = 0; | ||||
| +	unsigned int i, j; | ||||
| +	Elf_Sym *syms; | ||||
| + | ||||
| +	/* Everything marked ALLOC (this includes the exported symbols) */ | ||||
| +	for (i = 1; i < hdr->e_shnum; ++i) { | ||||
| +		unsigned int info = sechdrs[i].sh_info; | ||||
| + | ||||
| +		if (sechdrs[i].sh_type != SHT_REL | ||||
| +		    && sechdrs[i].sh_type != SHT_RELA) | ||||
| +			continue; | ||||
| + | ||||
| +		/* Not a valid relocation section? */ | ||||
| +		if (info >= hdr->e_shnum) | ||||
| +			continue; | ||||
| + | ||||
| +		/* Don't bother with non-allocated sections */ | ||||
| +		if (!(sechdrs[info].sh_flags & SHF_ALLOC)) | ||||
| +			continue; | ||||
| + | ||||
| +		/* If it's called *.init*, and we're not init, we're | ||||
| +                   not interested */ | ||||
| +		if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) | ||||
| +		    != is_init) | ||||
| +			continue; | ||||
| + | ||||
| +		syms = (Elf_Sym *) sechdrs[symindex].sh_addr; | ||||
| +		if (sechdrs[i].sh_type == SHT_REL) { | ||||
| +			Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr; | ||||
| +			unsigned int size = sechdrs[i].sh_size / sizeof(*rel); | ||||
| + | ||||
| +			for (j = 0; j < size; ++j) { | ||||
| +				Elf_Sym *sym; | ||||
| + | ||||
| +				if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26) | ||||
| +					continue; | ||||
| + | ||||
| +				sym = syms + ELF_MIPS_R_SYM(rel[j]); | ||||
| +				if (!is_init && sym->st_shndx != SHN_UNDEF) | ||||
| +					continue; | ||||
| + | ||||
| +				ret += 4 * sizeof(int); | ||||
| +			} | ||||
| +		} else { | ||||
| +			Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr; | ||||
| +			unsigned int size = sechdrs[i].sh_size / sizeof(*rela); | ||||
| + | ||||
| +			for (j = 0; j < size; ++j) { | ||||
| +				Elf_Sym *sym; | ||||
| + | ||||
| +				if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26) | ||||
| +					continue; | ||||
| + | ||||
| +				sym = syms + ELF_MIPS_R_SYM(rela[j]); | ||||
| +				if (!is_init && sym->st_shndx != SHN_UNDEF) | ||||
| +					continue; | ||||
| + | ||||
| +				ret += 4 * sizeof(int); | ||||
| +			} | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +#ifndef MODULE_START | ||||
| +static void *alloc_phys(unsigned long size) | ||||
| +{ | ||||
| +	unsigned order; | ||||
| +	struct page *page; | ||||
| +	struct page *p; | ||||
| + | ||||
| +	size = PAGE_ALIGN(size); | ||||
| +	order = get_order(size); | ||||
| + | ||||
| +	page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN | | ||||
| +			__GFP_THISNODE, order); | ||||
| +	if (!page) | ||||
| +		return NULL; | ||||
| + | ||||
| +	split_page(page, order); | ||||
| + | ||||
| +	/* mark all pages except for the last one */ | ||||
| +	for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p) | ||||
| +		set_bit(PG_owner_priv_1, &p->flags); | ||||
| + | ||||
| +	for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p) | ||||
| +		__free_page(p); | ||||
| + | ||||
| +	return page_address(page); | ||||
| +} | ||||
| +#endif | ||||
| + | ||||
| +static void free_phys(void *ptr) | ||||
| +{ | ||||
| +	struct page *page; | ||||
| +	bool free; | ||||
| + | ||||
| +	page = virt_to_page(ptr); | ||||
| +	do { | ||||
| +		free = test_and_clear_bit(PG_owner_priv_1, &page->flags); | ||||
| +		__free_page(page); | ||||
| +		page++; | ||||
| +	} while (free); | ||||
| +} | ||||
| + | ||||
| + | ||||
|  void *module_alloc(unsigned long size) | ||||
|  { | ||||
| +#ifdef MODULE_START | ||||
|  	return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END, | ||||
|  				GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, | ||||
|  				__builtin_return_address(0)); | ||||
| +#else | ||||
| +	void *ptr; | ||||
| + | ||||
| +	if (size == 0) | ||||
| +		return NULL; | ||||
| + | ||||
| +	ptr = alloc_phys(size); | ||||
| + | ||||
| +	/* If we failed to allocate physically contiguous memory, | ||||
| +	 * fall back to regular vmalloc. The module loader code will | ||||
| +	 * create jump tables to handle long jumps */ | ||||
| +	if (!ptr) | ||||
| +		return vmalloc(size); | ||||
| + | ||||
| +	return ptr; | ||||
| +#endif | ||||
|  } | ||||
| + | ||||
| +static inline bool is_phys_addr(void *ptr) | ||||
| +{ | ||||
| +#ifdef CONFIG_64BIT | ||||
| +	return (KSEGX((unsigned long)ptr) == CKSEG0); | ||||
| +#else | ||||
| +	return (KSEGX(ptr) == KSEG0); | ||||
|  #endif | ||||
| +} | ||||
| + | ||||
| +/* Free memory returned from module_alloc */ | ||||
| +void module_memfree(void *module_region) | ||||
| +{ | ||||
| +	if (is_phys_addr(module_region)) | ||||
| +		free_phys(module_region); | ||||
| +	else | ||||
| +		vfree(module_region); | ||||
| +} | ||||
| + | ||||
| +static void *__module_alloc(int size, bool phys) | ||||
| +{ | ||||
| +	void *ptr; | ||||
| + | ||||
| +	if (phys) | ||||
| +		ptr = kmalloc(size, GFP_KERNEL); | ||||
| +	else | ||||
| +		ptr = vmalloc(size); | ||||
| +	return ptr; | ||||
| +} | ||||
| + | ||||
| +static void __module_free(void *ptr) | ||||
| +{ | ||||
| +	if (is_phys_addr(ptr)) | ||||
| +		kfree(ptr); | ||||
| +	else | ||||
| +		vfree(ptr); | ||||
| +} | ||||
| + | ||||
| +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, | ||||
| +			      char *secstrings, struct module *mod) | ||||
| +{ | ||||
| +	unsigned int symindex = 0; | ||||
| +	unsigned int core_size, init_size; | ||||
| +	int i; | ||||
| + | ||||
| +	mod->arch.phys_plt_offset = 0; | ||||
| +	mod->arch.virt_plt_offset = 0; | ||||
| +	mod->arch.phys_plt_tbl = NULL; | ||||
| +	mod->arch.virt_plt_tbl = NULL; | ||||
| + | ||||
| +	if (IS_ENABLED(CONFIG_64BIT)) | ||||
| +		return 0; | ||||
| + | ||||
| +	for (i = 1; i < hdr->e_shnum; i++) | ||||
| +		if (sechdrs[i].sh_type == SHT_SYMTAB) | ||||
| +			symindex = i; | ||||
| + | ||||
| +	core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false); | ||||
| +	init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true); | ||||
| + | ||||
| +	if ((core_size + init_size) == 0) | ||||
| +		return 0; | ||||
| + | ||||
| +	mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1); | ||||
| +	if (!mod->arch.phys_plt_tbl) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0); | ||||
| +	if (!mod->arch.virt_plt_tbl) { | ||||
| +		__module_free(mod->arch.phys_plt_tbl); | ||||
| +		mod->arch.phys_plt_tbl = NULL; | ||||
| +		return -ENOMEM; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
|   | ||||
|  int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) | ||||
|  { | ||||
| @@ -64,8 +271,39 @@ static int apply_r_mips_32_rel(struct mo | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static Elf_Addr add_plt_entry_to(unsigned *plt_offset, | ||||
| +				 void *start, Elf_Addr v) | ||||
| +{ | ||||
| +	unsigned *tramp = start + *plt_offset; | ||||
| +	*plt_offset += 4 * sizeof(int); | ||||
| + | ||||
| +	/* adjust carry for addiu */ | ||||
| +	if (v & 0x00008000) | ||||
| +		v += 0x10000; | ||||
| + | ||||
| +	tramp[0] = 0x3c190000 | (v >> 16);      /* lui t9, hi16 */ | ||||
| +	tramp[1] = 0x27390000 | (v & 0xffff);   /* addiu t9, t9, lo16 */ | ||||
| +	tramp[2] = 0x03200008;                  /* jr t9 */ | ||||
| +	tramp[3] = 0x00000000;                  /* nop */ | ||||
| + | ||||
| +	return (Elf_Addr) tramp; | ||||
| +} | ||||
| + | ||||
| +static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v) | ||||
| +{ | ||||
| +	if (is_phys_addr(location)) | ||||
| +		return add_plt_entry_to(&me->arch.phys_plt_offset, | ||||
| +				me->arch.phys_plt_tbl, v); | ||||
| +	else | ||||
| +		return add_plt_entry_to(&me->arch.virt_plt_offset, | ||||
| +				me->arch.virt_plt_tbl, v); | ||||
| + | ||||
| +} | ||||
| + | ||||
|  static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) | ||||
|  { | ||||
| +	u32 ofs = *location & 0x03ffffff; | ||||
| + | ||||
|  	if (v % 4) { | ||||
|  		pr_err("module %s: dangerous R_MIPS_26 REL relocation\n", | ||||
|  		       me->name); | ||||
| @@ -73,14 +311,17 @@ static int apply_r_mips_26_rel(struct mo | ||||
|  	} | ||||
|   | ||||
|  	if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { | ||||
| -		printk(KERN_ERR | ||||
| -		       "module %s: relocation overflow\n", | ||||
| -		       me->name); | ||||
| -		return -ENOEXEC; | ||||
| +		v = add_plt_entry(me, location, v + (ofs << 2)); | ||||
| +		if (!v) { | ||||
| +			printk(KERN_ERR | ||||
| +				"module %s: relocation overflow\n", me->name); | ||||
| +			return -ENOEXEC; | ||||
| +		} | ||||
| +		ofs = 0; | ||||
|  	} | ||||
|   | ||||
|  	*location = (*location & ~0x03ffffff) | | ||||
| -		    ((*location + (v >> 2)) & 0x03ffffff); | ||||
| +		    ((ofs + (v >> 2)) & 0x03ffffff); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -287,9 +528,33 @@ int module_finalize(const Elf_Ehdr *hdr, | ||||
|  		list_add(&me->arch.dbe_list, &dbe_list); | ||||
|  		spin_unlock_irq(&dbe_lock); | ||||
|  	} | ||||
| + | ||||
| +	/* Get rid of the fixup trampoline if we're running the module | ||||
| +	 * from physically mapped address space */ | ||||
| +	if (me->arch.phys_plt_offset == 0) { | ||||
| +		__module_free(me->arch.phys_plt_tbl); | ||||
| +		me->arch.phys_plt_tbl = NULL; | ||||
| +	} | ||||
| +	if (me->arch.virt_plt_offset == 0) { | ||||
| +		__module_free(me->arch.virt_plt_tbl); | ||||
| +		me->arch.virt_plt_tbl = NULL; | ||||
| +	} | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +void module_arch_freeing_init(struct module *mod) | ||||
| +{ | ||||
| +	if (mod->arch.phys_plt_tbl) { | ||||
| +		__module_free(mod->arch.phys_plt_tbl); | ||||
| +		mod->arch.phys_plt_tbl = NULL; | ||||
| +	} | ||||
| +	if (mod->arch.virt_plt_tbl) { | ||||
| +		__module_free(mod->arch.virt_plt_tbl); | ||||
| +		mod->arch.virt_plt_tbl = NULL; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  void module_arch_cleanup(struct module *mod) | ||||
|  { | ||||
|  	spin_lock_irq(&dbe_lock); | ||||
| @@ -1,83 +0,0 @@ | ||||
| --- a/arch/mips/include/asm/string.h | ||||
| +++ b/arch/mips/include/asm/string.h | ||||
| @@ -133,11 +133,44 @@ strncmp(__const__ char *__cs, __const__ | ||||
|   | ||||
|  #define __HAVE_ARCH_MEMSET | ||||
|  extern void *memset(void *__s, int __c, size_t __count); | ||||
| +#define memset(__s, __c, len)					\ | ||||
| +({								\ | ||||
| +	size_t __len = (len);					\ | ||||
| +	void *__ret;						\ | ||||
| +	if (__builtin_constant_p(len) && __len >= 64)		\ | ||||
| +		__ret = memset((__s), (__c), __len);		\ | ||||
| +	else							\ | ||||
| +		__ret = __builtin_memset((__s), (__c), __len);	\ | ||||
| +	__ret;							\ | ||||
| +}) | ||||
|   | ||||
|  #define __HAVE_ARCH_MEMCPY | ||||
|  extern void *memcpy(void *__to, __const__ void *__from, size_t __n); | ||||
| +#define memcpy(dst, src, len)					\ | ||||
| +({								\ | ||||
| +	size_t __len = (len);					\ | ||||
| +	void *__ret;						\ | ||||
| +	if (__builtin_constant_p(len) && __len >= 64)		\ | ||||
| +		__ret = memcpy((dst), (src), __len);		\ | ||||
| +	else							\ | ||||
| +		__ret = __builtin_memcpy((dst), (src), __len);	\ | ||||
| +	__ret;							\ | ||||
| +}) | ||||
|   | ||||
|  #define __HAVE_ARCH_MEMMOVE | ||||
|  extern void *memmove(void *__dest, __const__ void *__src, size_t __n); | ||||
| +#define memmove(dst, src, len)					\ | ||||
| +({								\ | ||||
| +	size_t __len = (len);					\ | ||||
| +	void *__ret;						\ | ||||
| +	if (__builtin_constant_p(len) && __len >= 64)		\ | ||||
| +		__ret = memmove((dst), (src), __len);		\ | ||||
| +	else							\ | ||||
| +		__ret = __builtin_memmove((dst), (src), __len);	\ | ||||
| +	__ret;							\ | ||||
| +}) | ||||
| + | ||||
| +#define __HAVE_ARCH_MEMCMP | ||||
| +#define memcmp(src1, src2, len) __builtin_memcmp((src1), (src2), (len)) | ||||
|   | ||||
|  #endif /* _ASM_STRING_H */ | ||||
| --- a/arch/mips/lib/Makefile | ||||
| +++ b/arch/mips/lib/Makefile | ||||
| @@ -4,7 +4,7 @@ | ||||
|   | ||||
|  lib-y	+= bitops.o csum_partial.o delay.o memcpy.o memset.o \ | ||||
|  	   mips-atomic.o strlen_user.o strncpy_user.o \ | ||||
| -	   strnlen_user.o uncached.o | ||||
| +	   strnlen_user.o uncached.o memcmp.o | ||||
|   | ||||
|  obj-y			+= iomap.o | ||||
|  obj-$(CONFIG_PCI)	+= iomap-pci.o | ||||
| --- /dev/null | ||||
| +++ b/arch/mips/lib/memcmp.c | ||||
| @@ -0,0 +1,22 @@ | ||||
| +/* | ||||
| + *  copied from linux/lib/string.c | ||||
| + * | ||||
| + *  Copyright (C) 1991, 1992  Linus Torvalds | ||||
| + */ | ||||
| + | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/string.h> | ||||
| + | ||||
| +#undef memcmp | ||||
| +int memcmp(const void *cs, const void *ct, size_t count) | ||||
| +{ | ||||
| +	const unsigned char *su1, *su2; | ||||
| +	int res = 0; | ||||
| + | ||||
| +	for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) | ||||
| +		if ((res = *su1 - *su2) != 0) | ||||
| +			break; | ||||
| +	return res; | ||||
| +} | ||||
| +EXPORT_SYMBOL(memcmp); | ||||
| + | ||||
| @@ -1,17 +0,0 @@ | ||||
| Adjust highmem offset to 0x10000000 to ensure that all kmalloc allocations | ||||
| stay within the same 256M boundary. This ensures that -mlong-calls is not | ||||
| needed on systems with more than 256M RAM. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
| --- a/arch/mips/include/asm/mach-generic/spaces.h | ||||
| +++ b/arch/mips/include/asm/mach-generic/spaces.h | ||||
| @@ -44,7 +44,7 @@ | ||||
|   * Memory above this physical address will be considered highmem. | ||||
|   */ | ||||
|  #ifndef HIGHMEM_START | ||||
| -#define HIGHMEM_START		_AC(0x20000000, UL) | ||||
| +#define HIGHMEM_START		_AC(0x10000000, UL) | ||||
|  #endif | ||||
|   | ||||
|  #endif /* CONFIG_32BIT */ | ||||
| @@ -1,32 +0,0 @@ | ||||
| --- a/arch/mips/mm/cache.c | ||||
| +++ b/arch/mips/mm/cache.c | ||||
| @@ -39,6 +39,7 @@ void (*__flush_cache_vunmap)(void); | ||||
|   | ||||
|  void (*__flush_kernel_vmap_range)(unsigned long vaddr, int size); | ||||
|  EXPORT_SYMBOL_GPL(__flush_kernel_vmap_range); | ||||
| +EXPORT_SYMBOL(__flush_cache_all); | ||||
|  void (*__invalidate_kernel_vmap_range)(unsigned long vaddr, int size); | ||||
|   | ||||
|  /* MIPS specific cache operations */ | ||||
| --- a/fs/fuse/dev.c | ||||
| +++ b/fs/fuse/dev.c | ||||
| @@ -19,6 +19,9 @@ | ||||
|  #include <linux/pipe_fs_i.h> | ||||
|  #include <linux/swap.h> | ||||
|  #include <linux/splice.h> | ||||
| +#ifdef CONFIG_MIPS | ||||
| +#include <asm/cacheflush.h> | ||||
| +#endif | ||||
|   | ||||
|  MODULE_ALIAS_MISCDEV(FUSE_MINOR); | ||||
|  MODULE_ALIAS("devname:fuse"); | ||||
| @@ -802,6 +805,9 @@ static int fuse_copy_fill(struct fuse_co | ||||
|  static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) | ||||
|  { | ||||
|  	unsigned ncpy = min(*size, cs->len); | ||||
| +#ifdef CONFIG_MIPS | ||||
| +	__flush_cache_all(); | ||||
| +#endif | ||||
|  	if (val) { | ||||
|  		void *pgaddr = kmap_atomic(cs->pg); | ||||
|  		void *buf = pgaddr + cs->offset; | ||||
| @@ -1,13 +0,0 @@ | ||||
| --- a/arch/arm/kernel/module.c | ||||
| +++ b/arch/arm/kernel/module.c | ||||
| @@ -88,6 +88,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons | ||||
|  			return -ENOEXEC; | ||||
|  		} | ||||
|   | ||||
| +		if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) && | ||||
| +		    ELF_ST_BIND(sym->st_info) == STB_WEAK) | ||||
| +			continue; | ||||
| + | ||||
|  		loc = dstsec->sh_addr + rel->r_offset; | ||||
|   | ||||
|  		switch (ELF32_R_TYPE(rel->r_info)) { | ||||
| @@ -1,31 +0,0 @@ | ||||
| Upstream doesn't optimize the kernel and bootwrappers for ppc44x because | ||||
| they still want to support gcc 3.3 -- well, we don't. | ||||
|  | ||||
| --- a/arch/powerpc/Makefile | ||||
| +++ b/arch/powerpc/Makefile | ||||
| @@ -207,7 +207,8 @@ ifeq ($(CONFIG_FUNCTION_TRACER),y) | ||||
|  KBUILD_CFLAGS		+= -mno-sched-epilog | ||||
|  endif | ||||
|   | ||||
| -cpu-as-$(CONFIG_4xx)		+= -Wa,-m405 | ||||
| +cpu-as-$(CONFIG_40x)		+= -Wa,-m405 | ||||
| +cpu-as-$(CONFIG_44x)		+= -Wa,-m440 | ||||
|  cpu-as-$(CONFIG_ALTIVEC)	+= -Wa,-maltivec | ||||
|  cpu-as-$(CONFIG_E200)		+= -Wa,-me200 | ||||
|   | ||||
| --- a/arch/powerpc/boot/Makefile | ||||
| +++ b/arch/powerpc/boot/Makefile | ||||
| @@ -48,10 +48,10 @@ BOOTCFLAGS	+= -I$(obj) -I$(srctree)/$(ob | ||||
|  DTC_FLAGS	?= -p 1024 | ||||
|   | ||||
|  $(obj)/4xx.o: BOOTCFLAGS += -mcpu=405 | ||||
| -$(obj)/ebony.o: BOOTCFLAGS += -mcpu=405 | ||||
| +$(obj)/ebony.o: BOOTCFLAGS += -mcpu=440 | ||||
|  $(obj)/cuboot-hotfoot.o: BOOTCFLAGS += -mcpu=405 | ||||
| -$(obj)/cuboot-taishan.o: BOOTCFLAGS += -mcpu=405 | ||||
| -$(obj)/cuboot-katmai.o: BOOTCFLAGS += -mcpu=405 | ||||
| +$(obj)/cuboot-taishan.o: BOOTCFLAGS += -mcpu=440 | ||||
| +$(obj)/cuboot-katmai.o: BOOTCFLAGS += -mcpu=440 | ||||
|  $(obj)/cuboot-acadia.o: BOOTCFLAGS += -mcpu=405 | ||||
|  $(obj)/treeboot-walnut.o: BOOTCFLAGS += -mcpu=405 | ||||
|  $(obj)/treeboot-iss4xx.o: BOOTCFLAGS += -mcpu=405 | ||||
| @@ -1,10 +0,0 @@ | ||||
| --- a/arch/powerpc/Makefile | ||||
| +++ b/arch/powerpc/Makefile | ||||
| @@ -169,7 +169,6 @@ CPP		= $(CC) -E $(KBUILD_CFLAGS) | ||||
|   | ||||
|  CHECKFLAGS	+= -m$(CONFIG_WORD_SIZE) -D__powerpc__ -D__powerpc$(CONFIG_WORD_SIZE)__ | ||||
|   | ||||
| -KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o | ||||
|   | ||||
|  ifeq ($(CONFIG_476FPE_ERR46),y) | ||||
|  	KBUILD_LDFLAGS_MODULE += --ppc476-workaround \ | ||||
| @@ -1,298 +0,0 @@ | ||||
| From d8582dcf1ed66eee88a11e4760f42c0d6c8822be Mon Sep 17 00:00:00 2001 | ||||
| From: Yousong Zhou <yszhou4tech@gmail.com> | ||||
| Date: Sat, 31 Jan 2015 22:26:03 +0800 | ||||
| Subject: [PATCH 331/331] MIPS: kexec: Accept command line parameters from | ||||
|  userspace. | ||||
|  | ||||
| Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com> | ||||
| --- | ||||
|  arch/mips/kernel/machine_kexec.c   |  153 +++++++++++++++++++++++++++++++----- | ||||
|  arch/mips/kernel/machine_kexec.h   |   20 +++++ | ||||
|  arch/mips/kernel/relocate_kernel.S |   21 +++-- | ||||
|  3 files changed, 167 insertions(+), 27 deletions(-) | ||||
|  create mode 100644 arch/mips/kernel/machine_kexec.h | ||||
|  | ||||
| --- a/arch/mips/kernel/machine_kexec.c | ||||
| +++ b/arch/mips/kernel/machine_kexec.c | ||||
| @@ -10,45 +10,145 @@ | ||||
|  #include <linux/mm.h> | ||||
|  #include <linux/delay.h> | ||||
|   | ||||
| +#include <asm/bootinfo.h> | ||||
|  #include <asm/cacheflush.h> | ||||
|  #include <asm/page.h> | ||||
| - | ||||
| -extern const unsigned char relocate_new_kernel[]; | ||||
| -extern const size_t relocate_new_kernel_size; | ||||
| - | ||||
| -extern unsigned long kexec_start_address; | ||||
| -extern unsigned long kexec_indirection_page; | ||||
| +#include <asm/uaccess.h> | ||||
| +#include "machine_kexec.h" | ||||
|   | ||||
|  int (*_machine_kexec_prepare)(struct kimage *) = NULL; | ||||
|  void (*_machine_kexec_shutdown)(void) = NULL; | ||||
|  void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL; | ||||
| + | ||||
|  #ifdef CONFIG_SMP | ||||
|  void (*relocated_kexec_smp_wait) (void *); | ||||
|  atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0); | ||||
|  #endif | ||||
|   | ||||
| -int | ||||
| -machine_kexec_prepare(struct kimage *kimage) | ||||
| +static void machine_kexec_print_args(void) | ||||
|  { | ||||
| +	unsigned long argc = (int)kexec_args[0]; | ||||
| +	int i; | ||||
| + | ||||
| +	pr_info("kexec_args[0] (argc): %lu\n", argc); | ||||
| +	pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]); | ||||
| +	pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]); | ||||
| +	pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]); | ||||
| + | ||||
| +	for (i = 0; i < argc; i++) { | ||||
| +		pr_info("kexec_argv[%d] = %p, %s\n", | ||||
| +				i, kexec_argv[i], kexec_argv[i]); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static void machine_kexec_init_argv(struct kimage *image) | ||||
| +{ | ||||
| +	void __user *buf = NULL; | ||||
| +	size_t bufsz; | ||||
| +	size_t size; | ||||
| +	int i; | ||||
| + | ||||
| +	bufsz = 0; | ||||
| +	for (i = 0; i < image->nr_segments; i++) { | ||||
| +		struct kexec_segment *seg; | ||||
| + | ||||
| +		seg = &image->segment[i]; | ||||
| +		if (seg->bufsz < 6) | ||||
| +			continue; | ||||
| + | ||||
| +		if (strncmp((char *) seg->buf, "kexec ", 6)) | ||||
| +			continue; | ||||
| + | ||||
| +		buf = seg->buf; | ||||
| +		bufsz = seg->bufsz; | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	if (!buf) | ||||
| +		return; | ||||
| + | ||||
| +	size = KEXEC_COMMAND_LINE_SIZE; | ||||
| +	size = min(size, bufsz); | ||||
| +	if (size < bufsz) | ||||
| +		pr_warn("kexec command line truncated to %zd bytes\n", size); | ||||
| + | ||||
| +	/* Copy to kernel space */ | ||||
| +	copy_from_user(kexec_argv_buf, buf, size); | ||||
| +	kexec_argv_buf[size - 1] = 0; | ||||
| +} | ||||
| + | ||||
| +static void machine_kexec_parse_argv(struct kimage *image) | ||||
| +{ | ||||
| +	char *reboot_code_buffer; | ||||
| +	int reloc_delta; | ||||
| +	char *ptr; | ||||
| +	int argc; | ||||
| +	int i; | ||||
| + | ||||
| +	ptr = kexec_argv_buf; | ||||
| +	argc = 0; | ||||
| + | ||||
| +	/* | ||||
| +	 * convert command line string to array of parameters | ||||
| +	 * (as bootloader does). | ||||
| +	 */ | ||||
| +	while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) { | ||||
| +		if (*ptr == ' ') { | ||||
| +			*ptr++ = '\0'; | ||||
| +			continue; | ||||
| +		} | ||||
| + | ||||
| +		kexec_argv[argc++] = ptr; | ||||
| +		ptr = strchr(ptr, ' '); | ||||
| +	} | ||||
| + | ||||
| +	if (!argc) | ||||
| +		return; | ||||
| + | ||||
| +	kexec_args[0] = argc; | ||||
| +	kexec_args[1] = (unsigned long)kexec_argv; | ||||
| +	kexec_args[2] = 0; | ||||
| +	kexec_args[3] = 0; | ||||
| + | ||||
| +	reboot_code_buffer = page_address(image->control_code_page); | ||||
| +	reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel; | ||||
| + | ||||
| +	kexec_args[1] += reloc_delta; | ||||
| +	for (i = 0; i < argc; i++) | ||||
| +		kexec_argv[i] += reloc_delta; | ||||
| +} | ||||
| + | ||||
| +int machine_kexec_prepare(struct kimage *kimage) | ||||
| +{ | ||||
| +	/* | ||||
| +	 * Whenever arguments passed from kexec-tools, Init the arguments as | ||||
| +	 * the original ones to try avoiding booting failure. | ||||
| +	 */ | ||||
| + | ||||
| +	kexec_args[0] = fw_arg0; | ||||
| +	kexec_args[1] = fw_arg1; | ||||
| +	kexec_args[2] = fw_arg2; | ||||
| +	kexec_args[3] = fw_arg3; | ||||
| + | ||||
| +	machine_kexec_init_argv(kimage); | ||||
| +	machine_kexec_parse_argv(kimage); | ||||
| + | ||||
|  	if (_machine_kexec_prepare) | ||||
|  		return _machine_kexec_prepare(kimage); | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -void | ||||
| -machine_kexec_cleanup(struct kimage *kimage) | ||||
| +void machine_kexec_cleanup(struct kimage *kimage) | ||||
|  { | ||||
|  } | ||||
|   | ||||
| -void | ||||
| -machine_shutdown(void) | ||||
| +void machine_shutdown(void) | ||||
|  { | ||||
|  	if (_machine_kexec_shutdown) | ||||
|  		_machine_kexec_shutdown(); | ||||
|  } | ||||
|   | ||||
| -void | ||||
| -machine_crash_shutdown(struct pt_regs *regs) | ||||
| +void machine_crash_shutdown(struct pt_regs *regs) | ||||
|  { | ||||
|  	if (_machine_crash_shutdown) | ||||
|  		_machine_crash_shutdown(regs); | ||||
| @@ -66,10 +166,12 @@ machine_kexec(struct kimage *image) | ||||
|  	unsigned long *ptr; | ||||
|   | ||||
|  	reboot_code_buffer = | ||||
| -	  (unsigned long)page_address(image->control_code_page); | ||||
| +		(unsigned long)page_address(image->control_code_page); | ||||
| +	pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer); | ||||
|   | ||||
|  	kexec_start_address = | ||||
|  		(unsigned long) phys_to_virt(image->start); | ||||
| +	pr_info("kexec_start_address = %p\n", (void *)kexec_start_address); | ||||
|   | ||||
|  	if (image->type == KEXEC_TYPE_DEFAULT) { | ||||
|  		kexec_indirection_page = | ||||
| @@ -77,9 +179,19 @@ machine_kexec(struct kimage *image) | ||||
|  	} else { | ||||
|  		kexec_indirection_page = (unsigned long)&image->head; | ||||
|  	} | ||||
| +	pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page); | ||||
|   | ||||
| -	memcpy((void*)reboot_code_buffer, relocate_new_kernel, | ||||
| -	       relocate_new_kernel_size); | ||||
| +	pr_info("Where is memcpy: %p\n", memcpy); | ||||
| +	pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n", | ||||
| +		(void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end); | ||||
| +	pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE, | ||||
| +		(void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer); | ||||
| +	memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel, | ||||
| +	       KEXEC_RELOCATE_NEW_KERNEL_SIZE); | ||||
| + | ||||
| +	pr_info("Before _print_args().\n"); | ||||
| +	machine_kexec_print_args(); | ||||
| +	pr_info("Before eval loop.\n"); | ||||
|   | ||||
|  	/* | ||||
|  	 * The generic kexec code builds a page list with physical | ||||
| @@ -98,15 +210,16 @@ machine_kexec(struct kimage *image) | ||||
|  	/* | ||||
|  	 * we do not want to be bothered. | ||||
|  	 */ | ||||
| +	pr_info("Before irq_disable.\n"); | ||||
|  	local_irq_disable(); | ||||
|   | ||||
| -	printk("Will call new kernel at %08lx\n", image->start); | ||||
| -	printk("Bye ...\n"); | ||||
| +	pr_info("Will call new kernel at %08lx\n", image->start); | ||||
| +	pr_info("Bye ...\n"); | ||||
|  	__flush_cache_all(); | ||||
|  #ifdef CONFIG_SMP | ||||
|  	/* All secondary cpus now may jump to kexec_wait cycle */ | ||||
|  	relocated_kexec_smp_wait = reboot_code_buffer + | ||||
| -		(void *)(kexec_smp_wait - relocate_new_kernel); | ||||
| +		(void *)(kexec_smp_wait - kexec_relocate_new_kernel); | ||||
|  	smp_wmb(); | ||||
|  	atomic_set(&kexec_ready_to_reboot, 1); | ||||
|  #endif | ||||
| --- /dev/null | ||||
| +++ b/arch/mips/kernel/machine_kexec.h | ||||
| @@ -0,0 +1,20 @@ | ||||
| +#ifndef _MACHINE_KEXEC_H | ||||
| +#define _MACHINE_KEXEC_H | ||||
| + | ||||
| +#ifndef __ASSEMBLY__ | ||||
| +extern const unsigned char kexec_relocate_new_kernel[]; | ||||
| +extern unsigned long kexec_relocate_new_kernel_end; | ||||
| +extern unsigned long kexec_start_address; | ||||
| +extern unsigned long kexec_indirection_page; | ||||
| + | ||||
| +extern char kexec_argv_buf[]; | ||||
| +extern char *kexec_argv[]; | ||||
| + | ||||
| +#define KEXEC_RELOCATE_NEW_KERNEL_SIZE	((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel) | ||||
| +#endif /* !__ASSEMBLY__ */ | ||||
| + | ||||
| +#define KEXEC_COMMAND_LINE_SIZE		256 | ||||
| +#define KEXEC_ARGV_SIZE			(KEXEC_COMMAND_LINE_SIZE / 16) | ||||
| +#define KEXEC_MAX_ARGC			(KEXEC_ARGV_SIZE / sizeof(long)) | ||||
| + | ||||
| +#endif | ||||
| --- a/arch/mips/kernel/relocate_kernel.S | ||||
| +++ b/arch/mips/kernel/relocate_kernel.S | ||||
| @@ -12,8 +12,9 @@ | ||||
|  #include <asm/mipsregs.h> | ||||
|  #include <asm/stackframe.h> | ||||
|  #include <asm/addrspace.h> | ||||
| +#include "machine_kexec.h" | ||||
|   | ||||
| -LEAF(relocate_new_kernel) | ||||
| +LEAF(kexec_relocate_new_kernel) | ||||
|  	PTR_L a0,	arg0 | ||||
|  	PTR_L a1,	arg1 | ||||
|  	PTR_L a2,	arg2 | ||||
| @@ -98,7 +99,7 @@ done: | ||||
|  #endif | ||||
|  	/* jump to kexec_start_address */ | ||||
|  	j		s1 | ||||
| -	END(relocate_new_kernel) | ||||
| +	END(kexec_relocate_new_kernel) | ||||
|   | ||||
|  #ifdef CONFIG_SMP | ||||
|  /* | ||||
| @@ -184,9 +185,15 @@ kexec_indirection_page: | ||||
|  	PTR		0 | ||||
|  	.size		kexec_indirection_page, PTRSIZE | ||||
|   | ||||
| -relocate_new_kernel_end: | ||||
| +kexec_argv_buf: | ||||
| +	EXPORT(kexec_argv_buf) | ||||
| +	.skip		KEXEC_COMMAND_LINE_SIZE | ||||
| +	.size		kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE | ||||
| + | ||||
| +kexec_argv: | ||||
| +	EXPORT(kexec_argv) | ||||
| +	.skip		KEXEC_ARGV_SIZE | ||||
| +	.size		kexec_argv, KEXEC_ARGV_SIZE | ||||
|   | ||||
| -relocate_new_kernel_size: | ||||
| -	EXPORT(relocate_new_kernel_size) | ||||
| -	PTR		relocate_new_kernel_end - relocate_new_kernel | ||||
| -	.size		relocate_new_kernel_size, PTRSIZE | ||||
| +kexec_relocate_new_kernel_end: | ||||
| +	EXPORT(kexec_relocate_new_kernel_end) | ||||
| @@ -1,146 +0,0 @@ | ||||
| --- a/drivers/mtd/Kconfig | ||||
| +++ b/drivers/mtd/Kconfig | ||||
| @@ -12,6 +12,23 @@ menuconfig MTD | ||||
|   | ||||
|  if MTD | ||||
|   | ||||
| +menu "OpenWrt specific MTD options" | ||||
| + | ||||
| +config MTD_ROOTFS_ROOT_DEV | ||||
| +	bool "Automatically set 'rootfs' partition to be root filesystem" | ||||
| +	default y | ||||
| + | ||||
| +config MTD_SPLIT_FIRMWARE | ||||
| +	bool "Automatically split firmware partition for kernel+rootfs" | ||||
| +	default y | ||||
| + | ||||
| +config MTD_SPLIT_FIRMWARE_NAME | ||||
| +	string "Firmware partition name" | ||||
| +	depends on MTD_SPLIT_FIRMWARE | ||||
| +	default "firmware" | ||||
| + | ||||
| +endmenu | ||||
| + | ||||
|  config MTD_TESTS | ||||
|  	tristate "MTD tests support (DANGEROUS)" | ||||
|  	depends on m | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -29,11 +29,13 @@ | ||||
|  #include <linux/kmod.h> | ||||
|  #include <linux/mtd/mtd.h> | ||||
|  #include <linux/mtd/partitions.h> | ||||
| +#include <linux/magic.h> | ||||
|  #include <linux/of.h> | ||||
|  #include <linux/err.h> | ||||
|  #include <linux/kconfig.h> | ||||
|   | ||||
|  #include "mtdcore.h" | ||||
| +#include "mtdsplit/mtdsplit.h" | ||||
|   | ||||
|  /* Our partition linked list */ | ||||
|  static LIST_HEAD(mtd_partitions); | ||||
| @@ -47,13 +49,14 @@ struct mtd_part { | ||||
|  	struct list_head list; | ||||
|  }; | ||||
|   | ||||
| +static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part); | ||||
| + | ||||
|  /* | ||||
|   * Given a pointer to the MTD object in the mtd_part structure, we can retrieve | ||||
|   * the pointer to that structure with this macro. | ||||
|   */ | ||||
|  #define PART(x)  ((struct mtd_part *)(x)) | ||||
|   | ||||
| - | ||||
|  /* | ||||
|   * MTD methods which simply translate the effective address and pass through | ||||
|   * to the _real_ device. | ||||
| @@ -579,8 +582,10 @@ static int mtd_add_partition_attrs(struc | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -int mtd_add_partition(struct mtd_info *master, const char *name, | ||||
| -		      long long offset, long long length) | ||||
| + | ||||
| +static int | ||||
| +__mtd_add_partition(struct mtd_info *master, const char *name, | ||||
| +		    long long offset, long long length, bool dup_check) | ||||
|  { | ||||
|  	struct mtd_partition part; | ||||
|  	struct mtd_part *new; | ||||
| @@ -612,6 +617,7 @@ int mtd_add_partition(struct mtd_info *m | ||||
|  	mutex_unlock(&mtd_partitions_mutex); | ||||
|   | ||||
|  	add_mtd_device(&new->mtd); | ||||
| +	mtd_partition_split(master, new); | ||||
|   | ||||
|  	mtd_add_partition_attrs(new); | ||||
|   | ||||
| @@ -619,6 +625,12 @@ int mtd_add_partition(struct mtd_info *m | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(mtd_add_partition); | ||||
|   | ||||
| +int mtd_add_partition(struct mtd_info *master, const char *name, | ||||
| +		      long long offset, long long length) | ||||
| +{ | ||||
| +	return __mtd_add_partition(master, name, offset, length, true); | ||||
| +} | ||||
| + | ||||
|  int mtd_del_partition(struct mtd_info *master, int partno) | ||||
|  { | ||||
|  	struct mtd_part *slave, *next; | ||||
| @@ -644,6 +656,35 @@ int mtd_del_partition(struct mtd_info *m | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(mtd_del_partition); | ||||
|   | ||||
| +#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
| +#define SPLIT_FIRMWARE_NAME	CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
| +#else | ||||
| +#define SPLIT_FIRMWARE_NAME	"unused" | ||||
| +#endif | ||||
| + | ||||
| +static void split_firmware(struct mtd_info *master, struct mtd_part *part) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +void __weak arch_split_mtd_part(struct mtd_info *master, const char *name, | ||||
| +                                int offset, int size) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part) | ||||
| +{ | ||||
| +	static int rootfs_found = 0; | ||||
| + | ||||
| +	if (rootfs_found) | ||||
| +		return; | ||||
| + | ||||
| +	if (!strcmp(part->mtd.name, SPLIT_FIRMWARE_NAME) && | ||||
| +	    config_enabled(CONFIG_MTD_SPLIT_FIRMWARE)) | ||||
| +		split_firmware(master, part); | ||||
| + | ||||
| +	arch_split_mtd_part(master, part->mtd.name, part->offset, | ||||
| +			    part->mtd.size); | ||||
| +} | ||||
|  /* | ||||
|   * This function, given a master MTD object and a partition table, creates | ||||
|   * and registers slave MTD objects which are bound to the master according to | ||||
| @@ -673,6 +714,7 @@ int add_mtd_partitions(struct mtd_info * | ||||
|  		mutex_unlock(&mtd_partitions_mutex); | ||||
|   | ||||
|  		add_mtd_device(&slave->mtd); | ||||
| +		mtd_partition_split(master, slave); | ||||
|  		mtd_add_partition_attrs(slave); | ||||
|   | ||||
|  		cur_offset = slave->offset + slave->mtd.size; | ||||
| --- a/include/linux/mtd/partitions.h | ||||
| +++ b/include/linux/mtd/partitions.h | ||||
| @@ -84,5 +84,7 @@ int mtd_add_partition(struct mtd_info *m | ||||
|  		      long long offset, long long length); | ||||
|  int mtd_del_partition(struct mtd_info *master, int partno); | ||||
|  uint64_t mtd_get_device_size(const struct mtd_info *mtd); | ||||
| +extern void __weak arch_split_mtd_part(struct mtd_info *master, | ||||
| +				       const char *name, int offset, int size); | ||||
|   | ||||
|  #endif | ||||
| @@ -1,113 +0,0 @@ | ||||
| From 02cff0ccaa6d364f5c1eeea83f47ac80ccc967d4 Mon Sep 17 00:00:00 2001 | ||||
| From: Gabor Juhos <juhosg@openwrt.org> | ||||
| Date: Tue, 3 Sep 2013 18:11:50 +0200 | ||||
| Subject: [PATCH] mtd: add support for different partition parser types | ||||
|  | ||||
| Signed-off-by: Gabor Juhos <juhosg@openwrt.org> | ||||
| --- | ||||
|  drivers/mtd/mtdpart.c          |   56 ++++++++++++++++++++++++++++++++++++++++ | ||||
|  include/linux/mtd/partitions.h |   11 ++++++++ | ||||
|  2 files changed, 67 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -745,6 +745,30 @@ static struct mtd_part_parser *get_parti | ||||
|   | ||||
|  #define put_partition_parser(p) do { module_put((p)->owner); } while (0) | ||||
|   | ||||
| +static struct mtd_part_parser * | ||||
| +get_partition_parser_by_type(enum mtd_parser_type type, | ||||
| +			     struct mtd_part_parser *start) | ||||
| +{ | ||||
| +	struct mtd_part_parser *p, *ret = NULL; | ||||
| + | ||||
| +	spin_lock(&part_parser_lock); | ||||
| + | ||||
| +	p = list_prepare_entry(start, &part_parsers, list); | ||||
| +	if (start) | ||||
| +		put_partition_parser(start); | ||||
| + | ||||
| +	list_for_each_entry_continue(p, &part_parsers, list) { | ||||
| +		if (p->type == type && try_module_get(p->owner)) { | ||||
| +			ret = p; | ||||
| +			break; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	spin_unlock(&part_parser_lock); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  void register_mtd_parser(struct mtd_part_parser *p) | ||||
|  { | ||||
|  	spin_lock(&part_parser_lock); | ||||
| @@ -862,6 +886,38 @@ int parse_mtd_partitions(struct mtd_info | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +int parse_mtd_partitions_by_type(struct mtd_info *master, | ||||
| +				 enum mtd_parser_type type, | ||||
| +				 struct mtd_partition **pparts, | ||||
| +				 struct mtd_part_parser_data *data) | ||||
| +{ | ||||
| +	struct mtd_part_parser *prev = NULL; | ||||
| +	int ret = 0; | ||||
| + | ||||
| +	while (1) { | ||||
| +		struct mtd_part_parser *parser; | ||||
| + | ||||
| +		parser = get_partition_parser_by_type(type, prev); | ||||
| +		if (!parser) | ||||
| +			break; | ||||
| + | ||||
| +		ret = (*parser->parse_fn)(master, pparts, data); | ||||
| + | ||||
| +		if (ret > 0) { | ||||
| +			put_partition_parser(parser); | ||||
| +			printk(KERN_NOTICE | ||||
| +			       "%d %s partitions found on MTD device %s\n", | ||||
| +			       ret, parser->name, master->name); | ||||
| +			break; | ||||
| +		} | ||||
| + | ||||
| +		prev = parser; | ||||
| +	} | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(parse_mtd_partitions_by_type); | ||||
| + | ||||
|  int mtd_is_partition(const struct mtd_info *mtd) | ||||
|  { | ||||
|  	struct mtd_part *part; | ||||
| --- a/include/linux/mtd/partitions.h | ||||
| +++ b/include/linux/mtd/partitions.h | ||||
| @@ -68,12 +68,17 @@ struct mtd_part_parser_data { | ||||
|   * Functions dealing with the various ways of partitioning the space | ||||
|   */ | ||||
|   | ||||
| +enum mtd_parser_type { | ||||
| +	MTD_PARSER_TYPE_DEVICE = 0, | ||||
| +}; | ||||
| + | ||||
|  struct mtd_part_parser { | ||||
|  	struct list_head list; | ||||
|  	struct module *owner; | ||||
|  	const char *name; | ||||
|  	int (*parse_fn)(struct mtd_info *, struct mtd_partition **, | ||||
|  			struct mtd_part_parser_data *); | ||||
| +	enum mtd_parser_type type; | ||||
|  }; | ||||
|   | ||||
|  extern void register_mtd_parser(struct mtd_part_parser *parser); | ||||
| @@ -87,4 +92,9 @@ uint64_t mtd_get_device_size(const struc | ||||
|  extern void __weak arch_split_mtd_part(struct mtd_info *master, | ||||
|  				       const char *name, int offset, int size); | ||||
|   | ||||
| +int parse_mtd_partitions_by_type(struct mtd_info *master, | ||||
| +				 enum mtd_parser_type type, | ||||
| +				 struct mtd_partition **pparts, | ||||
| +				 struct mtd_part_parser_data *data); | ||||
| + | ||||
|  #endif | ||||
| @@ -1,72 +0,0 @@ | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -656,6 +656,37 @@ int mtd_del_partition(struct mtd_info *m | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(mtd_del_partition); | ||||
|   | ||||
| +static int | ||||
| +run_parsers_by_type(struct mtd_part *slave, enum mtd_parser_type type) | ||||
| +{ | ||||
| +	struct mtd_partition *parts; | ||||
| +	int nr_parts; | ||||
| +	int i; | ||||
| + | ||||
| +	nr_parts = parse_mtd_partitions_by_type(&slave->mtd, type, &parts, | ||||
| +						NULL); | ||||
| +	if (nr_parts <= 0) | ||||
| +		return nr_parts; | ||||
| + | ||||
| +	if (WARN_ON(!parts)) | ||||
| +		return 0; | ||||
| + | ||||
| +	for (i = 0; i < nr_parts; i++) { | ||||
| +		/* adjust partition offsets */ | ||||
| +		parts[i].offset += slave->offset; | ||||
| + | ||||
| +		__mtd_add_partition(slave->master, | ||||
| +				    parts[i].name, | ||||
| +				    parts[i].offset, | ||||
| +				    parts[i].size, | ||||
| +				    false); | ||||
| +	} | ||||
| + | ||||
| +	kfree(parts); | ||||
| + | ||||
| +	return nr_parts; | ||||
| +} | ||||
| + | ||||
|  #ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
|  #define SPLIT_FIRMWARE_NAME	CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
|  #else | ||||
| @@ -664,6 +695,7 @@ EXPORT_SYMBOL_GPL(mtd_del_partition); | ||||
|   | ||||
|  static void split_firmware(struct mtd_info *master, struct mtd_part *part) | ||||
|  { | ||||
| +	run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); | ||||
|  } | ||||
|   | ||||
|  void __weak arch_split_mtd_part(struct mtd_info *master, const char *name, | ||||
| @@ -678,6 +710,12 @@ static void mtd_partition_split(struct m | ||||
|  	if (rootfs_found) | ||||
|  		return; | ||||
|   | ||||
| +	if (!strcmp(part->mtd.name, "rootfs")) { | ||||
| +		run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); | ||||
| + | ||||
| +		rootfs_found = 1; | ||||
| +	} | ||||
| + | ||||
|  	if (!strcmp(part->mtd.name, SPLIT_FIRMWARE_NAME) && | ||||
|  	    config_enabled(CONFIG_MTD_SPLIT_FIRMWARE)) | ||||
|  		split_firmware(master, part); | ||||
| --- a/include/linux/mtd/partitions.h | ||||
| +++ b/include/linux/mtd/partitions.h | ||||
| @@ -70,6 +70,8 @@ struct mtd_part_parser_data { | ||||
|   | ||||
|  enum mtd_parser_type { | ||||
|  	MTD_PARSER_TYPE_DEVICE = 0, | ||||
| +	MTD_PARSER_TYPE_ROOTFS, | ||||
| +	MTD_PARSER_TYPE_FIRMWARE, | ||||
|  }; | ||||
|   | ||||
|  struct mtd_part_parser { | ||||
| @@ -1,22 +0,0 @@ | ||||
| --- a/drivers/mtd/Kconfig | ||||
| +++ b/drivers/mtd/Kconfig | ||||
| @@ -27,6 +27,8 @@ config MTD_SPLIT_FIRMWARE_NAME | ||||
|  	depends on MTD_SPLIT_FIRMWARE | ||||
|  	default "firmware" | ||||
|   | ||||
| +source "drivers/mtd/mtdsplit/Kconfig" | ||||
| + | ||||
|  endmenu | ||||
|   | ||||
|  config MTD_TESTS | ||||
| --- a/drivers/mtd/Makefile | ||||
| +++ b/drivers/mtd/Makefile | ||||
| @@ -6,6 +6,8 @@ | ||||
|  obj-$(CONFIG_MTD)		+= mtd.o | ||||
|  mtd-y				:= mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o | ||||
|   | ||||
| +obj-$(CONFIG_MTD_SPLIT)		+= mtdsplit/ | ||||
| + | ||||
|  obj-$(CONFIG_MTD_OF_PARTS)	+= ofpart.o | ||||
|  obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o | ||||
|  obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o | ||||
| @@ -1,101 +0,0 @@ | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -453,14 +453,12 @@ static struct mtd_part *allocate_partiti | ||||
|  	if (slave->offset == MTDPART_OFS_APPEND) | ||||
|  		slave->offset = cur_offset; | ||||
|  	if (slave->offset == MTDPART_OFS_NXTBLK) { | ||||
| -		slave->offset = cur_offset; | ||||
| -		if (mtd_mod_by_eb(cur_offset, master) != 0) { | ||||
| -			/* Round up to next erasesize */ | ||||
| -			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; | ||||
| +		/* Round up to next erasesize */ | ||||
| +		slave->offset = mtd_roundup_to_eb(cur_offset, master); | ||||
| +		if (slave->offset != cur_offset) | ||||
|  			printk(KERN_NOTICE "Moving partition %d: " | ||||
|  			       "0x%012llx -> 0x%012llx\n", partno, | ||||
|  			       (unsigned long long)cur_offset, (unsigned long long)slave->offset); | ||||
| -		} | ||||
|  	} | ||||
|  	if (slave->offset == MTDPART_OFS_RETAIN) { | ||||
|  		slave->offset = cur_offset; | ||||
| @@ -687,6 +685,17 @@ run_parsers_by_type(struct mtd_part *sla | ||||
|  	return nr_parts; | ||||
|  } | ||||
|   | ||||
| +static inline unsigned long | ||||
| +mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len) | ||||
| +{ | ||||
| +	unsigned long mask = mtd->erasesize - 1; | ||||
| + | ||||
| +	len += offset & mask; | ||||
| +	len = (len + mask) & ~mask; | ||||
| +	len -= offset & mask; | ||||
| +	return len; | ||||
| +} | ||||
| + | ||||
|  #ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
|  #define SPLIT_FIRMWARE_NAME	CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
|  #else | ||||
| @@ -973,6 +982,24 @@ int mtd_is_partition(const struct mtd_in | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(mtd_is_partition); | ||||
|   | ||||
| +struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd) | ||||
| +{ | ||||
| +	if (!mtd_is_partition(mtd)) | ||||
| +		return (struct mtd_info *)mtd; | ||||
| + | ||||
| +	return PART(mtd)->master; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(mtdpart_get_master); | ||||
| + | ||||
| +uint64_t mtdpart_get_offset(const struct mtd_info *mtd) | ||||
| +{ | ||||
| +	if (!mtd_is_partition(mtd)) | ||||
| +		return 0; | ||||
| + | ||||
| +	return PART(mtd)->offset; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(mtdpart_get_offset); | ||||
| + | ||||
|  /* Returns the size of the entire flash chip */ | ||||
|  uint64_t mtd_get_device_size(const struct mtd_info *mtd) | ||||
|  { | ||||
| --- a/include/linux/mtd/partitions.h | ||||
| +++ b/include/linux/mtd/partitions.h | ||||
| @@ -90,6 +90,8 @@ int mtd_is_partition(const struct mtd_in | ||||
|  int mtd_add_partition(struct mtd_info *master, const char *name, | ||||
|  		      long long offset, long long length); | ||||
|  int mtd_del_partition(struct mtd_info *master, int partno); | ||||
| +struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd); | ||||
| +uint64_t mtdpart_get_offset(const struct mtd_info *mtd); | ||||
|  uint64_t mtd_get_device_size(const struct mtd_info *mtd); | ||||
|  extern void __weak arch_split_mtd_part(struct mtd_info *master, | ||||
|  				       const char *name, int offset, int size); | ||||
| --- a/include/linux/mtd/mtd.h | ||||
| +++ b/include/linux/mtd/mtd.h | ||||
| @@ -334,6 +334,24 @@ static inline uint32_t mtd_mod_by_eb(uin | ||||
|  	return do_div(sz, mtd->erasesize); | ||||
|  } | ||||
|   | ||||
| +static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd) | ||||
| +{ | ||||
| +	if (mtd_mod_by_eb(sz, mtd) == 0) | ||||
| +		return sz; | ||||
| + | ||||
| +	/* Round up to next erase block */ | ||||
| +	return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize; | ||||
| +} | ||||
| + | ||||
| +static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd) | ||||
| +{ | ||||
| +	if (mtd_mod_by_eb(sz, mtd) == 0) | ||||
| +		return sz; | ||||
| + | ||||
| +	/* Round down to the start of the current erase block */ | ||||
| +	return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize; | ||||
| +} | ||||
| + | ||||
|  static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) | ||||
|  { | ||||
|  	if (mtd->writesize_shift) | ||||
| @@ -1,70 +0,0 @@ | ||||
| --- a/drivers/mtd/Kconfig | ||||
| +++ b/drivers/mtd/Kconfig | ||||
| @@ -27,6 +27,11 @@ config MTD_SPLIT_FIRMWARE_NAME | ||||
|  	depends on MTD_SPLIT_FIRMWARE | ||||
|  	default "firmware" | ||||
|   | ||||
| +config MTD_UIMAGE_SPLIT | ||||
| +	bool "Enable split support for firmware partitions containing a uImage" | ||||
| +	depends on MTD_SPLIT_FIRMWARE | ||||
| +	default y | ||||
| + | ||||
|  source "drivers/mtd/mtdsplit/Kconfig" | ||||
|   | ||||
|  endmenu | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -696,6 +696,37 @@ mtd_pad_erasesize(struct mtd_info *mtd, | ||||
|  	return len; | ||||
|  } | ||||
|   | ||||
| +#define UBOOT_MAGIC	0x27051956 | ||||
| + | ||||
| +static void split_uimage(struct mtd_info *master, struct mtd_part *part) | ||||
| +{ | ||||
| +	struct { | ||||
| +		__be32 magic; | ||||
| +		__be32 pad[2]; | ||||
| +		__be32 size; | ||||
| +	} hdr; | ||||
| +	size_t len; | ||||
| + | ||||
| +	if (mtd_read(master, part->offset, sizeof(hdr), &len, (void *) &hdr)) | ||||
| +		return; | ||||
| + | ||||
| +	if (len != sizeof(hdr) || hdr.magic != cpu_to_be32(UBOOT_MAGIC)) | ||||
| +		return; | ||||
| + | ||||
| +	len = be32_to_cpu(hdr.size) + 0x40; | ||||
| +	len = mtd_pad_erasesize(master, part->offset, len); | ||||
| +	if (len + master->erasesize > part->mtd.size) | ||||
| +		return; | ||||
| + | ||||
| +	if (config_enabled(CONFIG_MTD_SPLIT_UIMAGE_FW)) | ||||
| +		pr_err("Dedicated partitioner didn't split firmware partition, please fill a bug report!\n"); | ||||
| +	else | ||||
| +		pr_warn("Support for built-in firmware splitter will be removed, please use CONFIG_MTD_SPLIT_UIMAGE_FW\n"); | ||||
| + | ||||
| +	__mtd_add_partition(master, "rootfs", part->offset + len, | ||||
| +			    part->mtd.size - len, false); | ||||
| +} | ||||
| + | ||||
|  #ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
|  #define SPLIT_FIRMWARE_NAME	CONFIG_MTD_SPLIT_FIRMWARE_NAME | ||||
|  #else | ||||
| @@ -704,7 +735,14 @@ mtd_pad_erasesize(struct mtd_info *mtd, | ||||
|   | ||||
|  static void split_firmware(struct mtd_info *master, struct mtd_part *part) | ||||
|  { | ||||
| -	run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); | ||||
| +	if (ret > 0) | ||||
| +		return; | ||||
| + | ||||
| +	if (config_enabled(CONFIG_MTD_UIMAGE_SPLIT)) | ||||
| +		split_uimage(master, part); | ||||
|  } | ||||
|   | ||||
|  void __weak arch_split_mtd_part(struct mtd_info *master, const char *name, | ||||
| @@ -1,18 +0,0 @@ | ||||
| --- a/include/linux/mtd/partitions.h | ||||
| +++ b/include/linux/mtd/partitions.h | ||||
| @@ -35,6 +35,7 @@ | ||||
|   * Note: writeable partitions require their size and offset be | ||||
|   * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). | ||||
|   */ | ||||
| +struct mtd_info; | ||||
|   | ||||
|  struct mtd_partition { | ||||
|  	const char *name;		/* identifier string */ | ||||
| @@ -50,7 +51,6 @@ struct mtd_partition { | ||||
|  #define MTDPART_SIZ_FULL	(0) | ||||
|   | ||||
|   | ||||
| -struct mtd_info; | ||||
|  struct device_node; | ||||
|   | ||||
|  /** | ||||
| @@ -1,142 +0,0 @@ | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -37,6 +37,8 @@ | ||||
|  #include "mtdcore.h" | ||||
|  #include "mtdsplit/mtdsplit.h" | ||||
|   | ||||
| +#define MTD_ERASE_PARTIAL	0x8000 /* partition only covers parts of an erase block */ | ||||
| + | ||||
|  /* Our partition linked list */ | ||||
|  static LIST_HEAD(mtd_partitions); | ||||
|  static DEFINE_MUTEX(mtd_partitions_mutex); | ||||
| @@ -235,13 +237,61 @@ static int part_erase(struct mtd_info *m | ||||
|  	struct mtd_part *part = PART(mtd); | ||||
|  	int ret; | ||||
|   | ||||
| + | ||||
| +	instr->partial_start = false; | ||||
| +	if (mtd->flags & MTD_ERASE_PARTIAL) { | ||||
| +		size_t readlen = 0; | ||||
| +		u64 mtd_ofs; | ||||
| + | ||||
| +		instr->erase_buf = kmalloc(part->master->erasesize, GFP_ATOMIC); | ||||
| +		if (!instr->erase_buf) | ||||
| +			return -ENOMEM; | ||||
| + | ||||
| +		mtd_ofs = part->offset + instr->addr; | ||||
| +		instr->erase_buf_ofs = do_div(mtd_ofs, part->master->erasesize); | ||||
| + | ||||
| +		if (instr->erase_buf_ofs > 0) { | ||||
| +			instr->addr -= instr->erase_buf_ofs; | ||||
| +			ret = mtd_read(part->master, | ||||
| +				instr->addr + part->offset, | ||||
| +				part->master->erasesize, | ||||
| +				&readlen, instr->erase_buf); | ||||
| + | ||||
| +			instr->len += instr->erase_buf_ofs; | ||||
| +			instr->partial_start = true; | ||||
| +		} else { | ||||
| +			mtd_ofs = part->offset + part->mtd.size; | ||||
| +			instr->erase_buf_ofs = part->master->erasesize - | ||||
| +				do_div(mtd_ofs, part->master->erasesize); | ||||
| + | ||||
| +			if (instr->erase_buf_ofs > 0) { | ||||
| +				instr->len += instr->erase_buf_ofs; | ||||
| +				ret = mtd_read(part->master, | ||||
| +					part->offset + instr->addr + | ||||
| +					instr->len - part->master->erasesize, | ||||
| +					part->master->erasesize, &readlen, | ||||
| +					instr->erase_buf); | ||||
| +			} else { | ||||
| +				ret = 0; | ||||
| +			} | ||||
| +		} | ||||
| +		if (ret < 0) { | ||||
| +			kfree(instr->erase_buf); | ||||
| +			return ret; | ||||
| +		} | ||||
| + | ||||
| +	} | ||||
| + | ||||
|  	instr->addr += part->offset; | ||||
|  	ret = part->master->_erase(part->master, instr); | ||||
|  	if (ret) { | ||||
|  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) | ||||
|  			instr->fail_addr -= part->offset; | ||||
|  		instr->addr -= part->offset; | ||||
| +		if (mtd->flags & MTD_ERASE_PARTIAL) | ||||
| +			kfree(instr->erase_buf); | ||||
|  	} | ||||
| + | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| @@ -249,7 +299,25 @@ void mtd_erase_callback(struct erase_inf | ||||
|  { | ||||
|  	if (instr->mtd->_erase == part_erase) { | ||||
|  		struct mtd_part *part = PART(instr->mtd); | ||||
| +		size_t wrlen = 0; | ||||
|   | ||||
| +		if (instr->mtd->flags & MTD_ERASE_PARTIAL) { | ||||
| +			if (instr->partial_start) { | ||||
| +				part->master->_write(part->master, | ||||
| +					instr->addr, instr->erase_buf_ofs, | ||||
| +					&wrlen, instr->erase_buf); | ||||
| +				instr->addr += instr->erase_buf_ofs; | ||||
| +			} else { | ||||
| +				instr->len -= instr->erase_buf_ofs; | ||||
| +				part->master->_write(part->master, | ||||
| +					instr->addr + instr->len, | ||||
| +					instr->erase_buf_ofs, &wrlen, | ||||
| +					instr->erase_buf + | ||||
| +					part->master->erasesize - | ||||
| +					instr->erase_buf_ofs); | ||||
| +			} | ||||
| +			kfree(instr->erase_buf); | ||||
| +		} | ||||
|  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) | ||||
|  			instr->fail_addr -= part->offset; | ||||
|  		instr->addr -= part->offset; | ||||
| @@ -522,17 +590,20 @@ static struct mtd_part *allocate_partiti | ||||
|  	if ((slave->mtd.flags & MTD_WRITEABLE) && | ||||
|  	    mtd_mod_by_eb(slave->offset, &slave->mtd)) { | ||||
|  		/* Doesn't start on a boundary of major erase size */ | ||||
| -		/* FIXME: Let it be writable if it is on a boundary of | ||||
| -		 * _minor_ erase size though */ | ||||
| -		slave->mtd.flags &= ~MTD_WRITEABLE; | ||||
| -		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", | ||||
| -			part->name); | ||||
| +		slave->mtd.flags |= MTD_ERASE_PARTIAL; | ||||
| +		if (((u32) slave->mtd.size) > master->erasesize) | ||||
| +			slave->mtd.flags &= ~MTD_WRITEABLE; | ||||
| +		else | ||||
| +			slave->mtd.erasesize = slave->mtd.size; | ||||
|  	} | ||||
|  	if ((slave->mtd.flags & MTD_WRITEABLE) && | ||||
| -	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { | ||||
| -		slave->mtd.flags &= ~MTD_WRITEABLE; | ||||
| -		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", | ||||
| -			part->name); | ||||
| +	    mtd_mod_by_eb(slave->offset + slave->mtd.size, &slave->mtd)) { | ||||
| +		slave->mtd.flags |= MTD_ERASE_PARTIAL; | ||||
| + | ||||
| +		if ((u32) slave->mtd.size > master->erasesize) | ||||
| +			slave->mtd.flags &= ~MTD_WRITEABLE; | ||||
| +		else | ||||
| +			slave->mtd.erasesize = slave->mtd.size; | ||||
|  	} | ||||
|   | ||||
|  	slave->mtd.ecclayout = master->ecclayout; | ||||
| --- a/include/linux/mtd/mtd.h | ||||
| +++ b/include/linux/mtd/mtd.h | ||||
| @@ -55,6 +55,10 @@ struct erase_info { | ||||
|  	u_long priv; | ||||
|  	u_char state; | ||||
|  	struct erase_info *next; | ||||
| + | ||||
| +	u8 *erase_buf; | ||||
| +	u32 erase_buf_ofs; | ||||
| +	bool partial_start; | ||||
|  }; | ||||
|   | ||||
|  struct mtd_erase_region_info { | ||||
| @@ -1,18 +0,0 @@ | ||||
| --- a/drivers/mtd/mtdpart.c | ||||
| +++ b/drivers/mtd/mtdpart.c | ||||
| @@ -336,7 +336,14 @@ static int part_lock(struct mtd_info *mt | ||||
|  static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||||
|  { | ||||
|  	struct mtd_part *part = PART(mtd); | ||||
| -	return part->master->_unlock(part->master, ofs + part->offset, len); | ||||
| + | ||||
| +	ofs += part->offset; | ||||
| +	if (mtd->flags & MTD_ERASE_PARTIAL) { | ||||
| +		/* round up len to next erasesize and round down offset to prev block */ | ||||
| +		len = (mtd_div_by_eb(len, part->master) + 1) * part->master->erasesize; | ||||
| +		ofs &= ~(part->master->erasesize - 1); | ||||
| +	} | ||||
| +	return part->master->_unlock(part->master, ofs, len); | ||||
|  } | ||||
|   | ||||
|  static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||||
| @@ -1,30 +0,0 @@ | ||||
| --- a/drivers/mtd/redboot.c | ||||
| +++ b/drivers/mtd/redboot.c | ||||
| @@ -265,14 +265,21 @@ static int parse_redboot_partitions(stru | ||||
|  #endif | ||||
|  		names += strlen(names)+1; | ||||
|   | ||||
| -#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED | ||||
|  		if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { | ||||
| -			i++; | ||||
| -			parts[i].offset = parts[i-1].size + parts[i-1].offset; | ||||
| -			parts[i].size = fl->next->img->flash_base - parts[i].offset; | ||||
| -			parts[i].name = nullname; | ||||
| -		} | ||||
| +			if (!strcmp(parts[i].name, "rootfs")) { | ||||
| +				parts[i].size = fl->next->img->flash_base; | ||||
| +				parts[i].size &= ~(master->erasesize - 1); | ||||
| +				parts[i].size -= parts[i].offset; | ||||
| +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED | ||||
| +				nrparts--; | ||||
| +			} else { | ||||
| +				i++; | ||||
| +				parts[i].offset = parts[i-1].size + parts[i-1].offset; | ||||
| +				parts[i].size = fl->next->img->flash_base - parts[i].offset; | ||||
| +				parts[i].name = nullname; | ||||
|  #endif | ||||
| +			} | ||||
| +		} | ||||
|  		tmp_fl = fl; | ||||
|  		fl = fl->next; | ||||
|  		kfree(tmp_fl); | ||||
| @@ -1,35 +0,0 @@ | ||||
| --- a/drivers/mtd/Kconfig | ||||
| +++ b/drivers/mtd/Kconfig | ||||
| @@ -179,6 +179,22 @@ config MTD_BCM47XX_PARTS | ||||
|  	  This provides partitions parser for devices based on BCM47xx | ||||
|  	  boards. | ||||
|   | ||||
| +config MTD_MYLOADER_PARTS | ||||
| +	tristate "MyLoader partition parsing" | ||||
| +	depends on ADM5120 || ATH25 || ATH79 | ||||
| +	---help--- | ||||
| +	  MyLoader is a bootloader which allows the user to define partitions | ||||
| +	  in flash devices, by putting a table in the second erase block | ||||
| +	  on the device, similar to a partition table. This table gives the  | ||||
| +	  offsets and lengths of the user defined partitions. | ||||
| + | ||||
| +	  If you need code which can detect and parse these tables, and | ||||
| +	  register MTD 'partitions' corresponding to each image detected, | ||||
| +	  enable this option. | ||||
| + | ||||
| +	  You will still need the parsing functions to be called by the driver | ||||
| +	  for your particular device. It won't happen automatically. | ||||
| + | ||||
|  comment "User Modules And Translation Layers" | ||||
|   | ||||
|  # | ||||
| --- a/drivers/mtd/Makefile | ||||
| +++ b/drivers/mtd/Makefile | ||||
| @@ -15,6 +15,7 @@ obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o | ||||
|  obj-$(CONFIG_MTD_AR7_PARTS)	+= ar7part.o | ||||
|  obj-$(CONFIG_MTD_BCM63XX_PARTS)	+= bcm63xxpart.o | ||||
|  obj-$(CONFIG_MTD_BCM47XX_PARTS)	+= bcm47xxpart.o | ||||
| +obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o | ||||
|   | ||||
|  # 'Users' - code which presents functionality to userspace. | ||||
|  obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o | ||||
| @@ -1,34 +0,0 @@ | ||||
| From 841e59ba3e496d86ca5f069204d5e5c1ad43c01d Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Tue, 27 Jan 2015 22:29:21 +0100 | ||||
| Subject: [PATCH] mtd: bcm47xxpart: support for Xiaomi specific board_data | ||||
|  partition | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| --- | ||||
|  drivers/mtd/bcm47xxpart.c | 4 +++- | ||||
|  1 file changed, 3 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/mtd/bcm47xxpart.c | ||||
| +++ b/drivers/mtd/bcm47xxpart.c | ||||
| @@ -33,6 +33,7 @@ | ||||
|  /* Magics */ | ||||
|  #define BOARD_DATA_MAGIC		0x5246504D	/* MPFR */ | ||||
|  #define BOARD_DATA_MAGIC2		0xBD0D0BBD | ||||
| +#define BOARD_DATA_XIAOMI_MAGIC		0x474D4442	/* GMDB */ | ||||
|  #define CFE_MAGIC			0x43464531	/* 1EFC */ | ||||
|  #define FACTORY_MAGIC			0x59544346	/* FCTY */ | ||||
|  #define NVRAM_HEADER			0x48534C46	/* FLSH */ | ||||
| @@ -267,7 +268,8 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  		} | ||||
|   | ||||
|  		/* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */ | ||||
| -		if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) { | ||||
| +		if (buf[0x000 / 4] == BOARD_DATA_MAGIC2 || | ||||
| +		    le32_to_cpu(buf[0x000 / 4]) == BOARD_DATA_XIAOMI_MAGIC) { | ||||
|  			bcm47xxpart_add_part(&parts[curr_part++], "board_data", | ||||
|  					     offset, MTD_WRITEABLE); | ||||
|  			continue; | ||||
| @@ -1,42 +0,0 @@ | ||||
| From fd54aa583296f9adfb1f519affbc10ba521eb809 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Wed, 28 Jan 2015 22:14:41 +0100 | ||||
| Subject: [PATCH] mtd: bcm47xxpart: detect T_Meter partition | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| It can be found on many Netgear devices. It consists of many 0x30 blocks | ||||
| starting with 4D 54. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| --- | ||||
|  drivers/mtd/bcm47xxpart.c | 10 ++++++++++ | ||||
|  1 file changed, 10 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/bcm47xxpart.c | ||||
| +++ b/drivers/mtd/bcm47xxpart.c | ||||
| @@ -39,6 +39,7 @@ | ||||
|  #define NVRAM_HEADER			0x48534C46	/* FLSH */ | ||||
|  #define POT_MAGIC1			0x54544f50	/* POTT */ | ||||
|  #define POT_MAGIC2			0x504f		/* OP */ | ||||
| +#define T_METER_MAGIC			0x4D540000	/* MT */ | ||||
|  #define ML_MAGIC1			0x39685a42 | ||||
|  #define ML_MAGIC2			0x26594131 | ||||
|  #define TRX_MAGIC			0x30524448 | ||||
| @@ -180,6 +181,15 @@ static int bcm47xxpart_parse(struct mtd_ | ||||
|  					     MTD_WRITEABLE); | ||||
|  			continue; | ||||
|  		} | ||||
| + | ||||
| +		/* T_Meter */ | ||||
| +		if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC && | ||||
| +		    (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC && | ||||
| +		    (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) { | ||||
| +			bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset, | ||||
| +					     MTD_WRITEABLE); | ||||
| +			continue; | ||||
| +		} | ||||
|   | ||||
|  		/* TRX */ | ||||
|  		if (buf[0x000 / 4] == TRX_MAGIC) { | ||||
| @@ -1,108 +0,0 @@ | ||||
| --- a/drivers/mtd/devices/block2mtd.c | ||||
| +++ b/drivers/mtd/devices/block2mtd.c | ||||
| @@ -26,6 +26,7 @@ | ||||
|  #include <linux/list.h> | ||||
|  #include <linux/init.h> | ||||
|  #include <linux/mtd/mtd.h> | ||||
| +#include <linux/mtd/partitions.h> | ||||
|  #include <linux/mutex.h> | ||||
|  #include <linux/mount.h> | ||||
|  #include <linux/slab.h> | ||||
| @@ -219,7 +220,7 @@ static void block2mtd_free_device(struct | ||||
|   | ||||
|   | ||||
|  static struct block2mtd_dev *add_device(char *devname, int erase_size, | ||||
| -		int timeout) | ||||
| +		const char *mtdname, int timeout) | ||||
|  { | ||||
|  #ifndef MODULE | ||||
|  	int i; | ||||
| @@ -227,6 +228,7 @@ static struct block2mtd_dev *add_device( | ||||
|  	const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL; | ||||
|  	struct block_device *bdev = ERR_PTR(-ENODEV); | ||||
|  	struct block2mtd_dev *dev; | ||||
| +	struct mtd_partition *part; | ||||
|  	char *name; | ||||
|   | ||||
|  	if (!devname) | ||||
| @@ -283,13 +285,16 @@ static struct block2mtd_dev *add_device( | ||||
|   | ||||
|  	/* Setup the MTD structure */ | ||||
|  	/* make the name contain the block device in */ | ||||
| -	name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); | ||||
| +	if (!mtdname) | ||||
| +		mtdname = devname; | ||||
| +	name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL); | ||||
|  	if (!name) | ||||
|  		goto err_destroy_mutex; | ||||
|   | ||||
| +	strcpy(name, mtdname); | ||||
|  	dev->mtd.name = name; | ||||
|   | ||||
| -	dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; | ||||
| +	dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(erase_size - 1); | ||||
|  	dev->mtd.erasesize = erase_size; | ||||
|  	dev->mtd.writesize = 1; | ||||
|  	dev->mtd.writebufsize = PAGE_SIZE; | ||||
| @@ -302,7 +307,11 @@ static struct block2mtd_dev *add_device( | ||||
|  	dev->mtd.priv = dev; | ||||
|  	dev->mtd.owner = THIS_MODULE; | ||||
|   | ||||
| -	if (mtd_device_register(&dev->mtd, NULL, 0)) { | ||||
| +	part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); | ||||
| +	part->name = name; | ||||
| +	part->offset = 0; | ||||
| +	part->size = dev->mtd.size; | ||||
| +	if (mtd_device_register(&dev->mtd, part, 1)) { | ||||
|  		/* Device didn't get added, so free the entry */ | ||||
|  		goto err_destroy_mutex; | ||||
|  	} | ||||
| @@ -310,8 +319,7 @@ static struct block2mtd_dev *add_device( | ||||
|  	list_add(&dev->list, &blkmtd_device_list); | ||||
|  	pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", | ||||
|  		dev->mtd.index, | ||||
| -		dev->mtd.name + strlen("block2mtd: "), | ||||
| -		dev->mtd.erasesize >> 10, dev->mtd.erasesize); | ||||
| +		mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize); | ||||
|  	return dev; | ||||
|   | ||||
|  err_destroy_mutex: | ||||
| @@ -384,7 +392,7 @@ static int block2mtd_setup2(const char * | ||||
|  	/* 80 for device, 12 for erase size, 80 for name, 8 for timeout */ | ||||
|  	char buf[80 + 12 + 80 + 8]; | ||||
|  	char *str = buf; | ||||
| -	char *token[2]; | ||||
| +	char *token[3]; | ||||
|  	char *name; | ||||
|  	size_t erase_size = PAGE_SIZE; | ||||
|  	unsigned long timeout = MTD_DEFAULT_TIMEOUT; | ||||
| @@ -398,7 +406,7 @@ static int block2mtd_setup2(const char * | ||||
|  	strcpy(str, val); | ||||
|  	kill_final_newline(str); | ||||
|   | ||||
| -	for (i = 0; i < 2; i++) | ||||
| +	for (i = 0; i < 3; i++) | ||||
|  		token[i] = strsep(&str, ","); | ||||
|   | ||||
|  	if (str) { | ||||
| @@ -424,8 +432,10 @@ static int block2mtd_setup2(const char * | ||||
|  			return 0; | ||||
|  		} | ||||
|  	} | ||||
| +	if (token[2] && (strlen(token[2]) + 1 > 80)) | ||||
| +		pr_err("mtd device name too long\n"); | ||||
|   | ||||
| -	add_device(name, erase_size, timeout); | ||||
| +	add_device(name, erase_size, token[2], timeout); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -459,7 +469,7 @@ static int block2mtd_setup(const char *v | ||||
|   | ||||
|   | ||||
|  module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); | ||||
| -MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\""); | ||||
| +MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>[,<name>]]\""); | ||||
|   | ||||
|  static int __init block2mtd_init(void) | ||||
|  { | ||||
| @@ -1,39 +0,0 @@ | ||||
| --- a/drivers/mtd/devices/block2mtd.c | ||||
| +++ b/drivers/mtd/devices/block2mtd.c | ||||
| @@ -392,7 +392,7 @@ static int block2mtd_setup2(const char * | ||||
|  	/* 80 for device, 12 for erase size, 80 for name, 8 for timeout */ | ||||
|  	char buf[80 + 12 + 80 + 8]; | ||||
|  	char *str = buf; | ||||
| -	char *token[3]; | ||||
| +	char *token[4]; | ||||
|  	char *name; | ||||
|  	size_t erase_size = PAGE_SIZE; | ||||
|  	unsigned long timeout = MTD_DEFAULT_TIMEOUT; | ||||
| @@ -406,7 +406,7 @@ static int block2mtd_setup2(const char * | ||||
|  	strcpy(str, val); | ||||
|  	kill_final_newline(str); | ||||
|   | ||||
| -	for (i = 0; i < 3; i++) | ||||
| +	for (i = 0; i < 4; i++) | ||||
|  		token[i] = strsep(&str, ","); | ||||
|   | ||||
|  	if (str) { | ||||
| @@ -435,6 +435,9 @@ static int block2mtd_setup2(const char * | ||||
|  	if (token[2] && (strlen(token[2]) + 1 > 80)) | ||||
|  		pr_err("mtd device name too long\n"); | ||||
|   | ||||
| +	if (token[3] && kstrtoul(token[3], 0, &timeout)) | ||||
| +		pr_err("invalid timeout\n"); | ||||
| + | ||||
|  	add_device(name, erase_size, token[2], timeout); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -469,7 +472,7 @@ static int block2mtd_setup(const char *v | ||||
|   | ||||
|   | ||||
|  module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); | ||||
| -MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>[,<name>]]\""); | ||||
| +MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>[,<name>[,<timeout>]]]\""); | ||||
|   | ||||
|  static int __init block2mtd_init(void) | ||||
|  { | ||||
| @@ -1,37 +0,0 @@ | ||||
| --- | ||||
|  drivers/mtd/nand/plat_nand.c |   13 ++++++++++++- | ||||
|  include/linux/mtd/nand.h     |    1 + | ||||
|  2 files changed, 13 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/include/linux/mtd/nand.h | ||||
| +++ b/include/linux/mtd/nand.h | ||||
| @@ -869,6 +869,7 @@ struct platform_nand_chip { | ||||
|  	unsigned int options; | ||||
|  	unsigned int bbt_options; | ||||
|  	const char **part_probe_types; | ||||
| +	int (*chip_fixup)(struct mtd_info *mtd); | ||||
|  }; | ||||
|   | ||||
|  /* Keep gcc happy */ | ||||
| --- a/drivers/mtd/nand/plat_nand.c | ||||
| +++ b/drivers/mtd/nand/plat_nand.c | ||||
| @@ -88,7 +88,18 @@ static int plat_nand_probe(struct platfo | ||||
|  	} | ||||
|   | ||||
|  	/* Scan to find existence of the device */ | ||||
| -	if (nand_scan(&data->mtd, pdata->chip.nr_chips)) { | ||||
| +	if (nand_scan_ident(&data->mtd, pdata->chip.nr_chips, NULL)) { | ||||
| +		err = -ENXIO; | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	if (pdata->chip.chip_fixup) { | ||||
| +		err = pdata->chip.chip_fixup(&data->mtd); | ||||
| +		if (err) | ||||
| +			goto out; | ||||
| +	} | ||||
| + | ||||
| +	if (nand_scan_tail(&data->mtd)) { | ||||
|  		err = -ENXIO; | ||||
|  		goto out; | ||||
|  	} | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/drivers/mtd/nand/nand_ecc.c | ||||
| +++ b/drivers/mtd/nand/nand_ecc.c | ||||
| @@ -507,7 +507,7 @@ int __nand_correct_data(unsigned char *b | ||||
|  		return 1;	/* error in ECC data; no action needed */ | ||||
|   | ||||
|  	pr_err("%s: uncorrectable ECC error\n", __func__); | ||||
| -	return -1; | ||||
| +	return -EBADMSG; | ||||
|  } | ||||
|  EXPORT_SYMBOL(__nand_correct_data); | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| --- a/drivers/mtd/chips/cfi_cmdset_0002.c | ||||
| +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | ||||
| @@ -809,7 +809,7 @@ static int get_chip(struct map_info *map | ||||
|  		return 0; | ||||
|   | ||||
|  	case FL_ERASING: | ||||
| -		if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || | ||||
| +		if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) || | ||||
|  		    !(mode == FL_READY || mode == FL_POINT || | ||||
|  		    (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) | ||||
|  			goto sleep; | ||||
| @@ -1,18 +0,0 @@ | ||||
| From: George Kashperko <george@znau.edu.ua> | ||||
|  | ||||
| Issue map read after Write Buffer Load command to ensure chip is ready | ||||
| to receive data. | ||||
| Signed-off-by: George Kashperko <george@znau.edu.ua> | ||||
| --- | ||||
|  drivers/mtd/chips/cfi_cmdset_0002.c |    1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
| --- a/drivers/mtd/chips/cfi_cmdset_0002.c | ||||
| +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | ||||
| @@ -1830,6 +1830,7 @@ static int __xipram do_write_buffer(stru | ||||
|   | ||||
|  	/* Write Buffer Load */ | ||||
|  	map_write(map, CMD(0x25), cmd_adr); | ||||
| +	(void) map_read(map, cmd_adr); | ||||
|   | ||||
|  	chip->state = FL_WRITING_TO_BUFFER; | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| Disable software protection bits for Macronix flashes. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  | ||||
| --- a/drivers/mtd/spi-nor/spi-nor.c | ||||
| +++ b/drivers/mtd/spi-nor/spi-nor.c | ||||
| @@ -1200,6 +1200,7 @@ int spi_nor_scan(struct spi_nor *nor, co | ||||
|   | ||||
|  	if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || | ||||
|  	    JEDEC_MFR(info) == SNOR_MFR_INTEL || | ||||
| +	    JEDEC_MFR(info) == SNOR_MFR_MACRONIX || | ||||
|  	    JEDEC_MFR(info) == SNOR_MFR_SST) { | ||||
|  		write_enable(nor); | ||||
|  		write_sr(nor, 0); | ||||
| @@ -1,26 +0,0 @@ | ||||
| --- a/drivers/mtd/mtdcore.c | ||||
| +++ b/drivers/mtd/mtdcore.c | ||||
| @@ -39,6 +39,7 @@ | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/reboot.h> | ||||
|  #include <linux/kconfig.h> | ||||
| +#include <linux/root_dev.h> | ||||
|   | ||||
|  #include <linux/mtd/mtd.h> | ||||
|  #include <linux/mtd/partitions.h> | ||||
| @@ -456,6 +457,15 @@ int add_mtd_device(struct mtd_info *mtd) | ||||
|  	   of this try_ nonsense, and no bitching about it | ||||
|  	   either. :) */ | ||||
|  	__module_get(THIS_MODULE); | ||||
| + | ||||
| +	if (!strcmp(mtd->name, "rootfs") && | ||||
| +	    config_enabled(CONFIG_MTD_ROOTFS_ROOT_DEV) && | ||||
| +	    ROOT_DEV == 0) { | ||||
| +		pr_notice("mtd: device %d (%s) set to be root filesystem\n", | ||||
| +			  mtd->index, mtd->name); | ||||
| +		ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index); | ||||
| +	} | ||||
| + | ||||
|  	return 0; | ||||
|   | ||||
|  fail_added: | ||||
| @@ -1,76 +0,0 @@ | ||||
| From 8a52e4100d7c3a4a1dfddfa02b8864a9b0068c13 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Sat, 17 May 2014 03:36:18 +0200 | ||||
| Subject: [PATCH 1/5] ubi: auto-attach mtd device named "ubi" or "data" on boot | ||||
| To: openwrt-devel@lists.openwrt.org | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| --- | ||||
|  drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 36 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/build.c | ||||
| +++ b/drivers/mtd/ubi/build.c | ||||
| @@ -1200,6 +1200,49 @@ static struct mtd_info * __init open_mtd | ||||
|  	return mtd; | ||||
|  } | ||||
|   | ||||
| +/* | ||||
| + * This function tries attaching mtd partitions named either "ubi" or "data" | ||||
| + * during boot. | ||||
| + */ | ||||
| +static void __init ubi_auto_attach(void) | ||||
| +{ | ||||
| +	int err; | ||||
| +	struct mtd_info *mtd; | ||||
| + | ||||
| +	/* try attaching mtd device named "ubi" or "data" */ | ||||
| +	mtd = open_mtd_device("ubi"); | ||||
| +	if (IS_ERR(mtd)) | ||||
| +		mtd = open_mtd_device("data"); | ||||
| + | ||||
| +	if (!IS_ERR(mtd)) { | ||||
| +		size_t len; | ||||
| +		char magic[4]; | ||||
| + | ||||
| +		/* check for a valid ubi magic */ | ||||
| +		err = mtd_read(mtd, 0, 4, &len, (void *) magic); | ||||
| +		if (!err && len == 4 && strncmp(magic, "UBI#", 4)) { | ||||
| +			pr_err("UBI error: no valid UBI magic found inside mtd%d", mtd->index); | ||||
| +			put_mtd_device(mtd); | ||||
| +			return; | ||||
| +		} | ||||
| + | ||||
| +		/* auto-add only media types where UBI makes sense */ | ||||
| +		if (mtd->type == MTD_NANDFLASH || | ||||
| +		    mtd->type == MTD_NORFLASH || | ||||
| +		    mtd->type == MTD_DATAFLASH || | ||||
| +		    mtd->type == MTD_MLCNANDFLASH) { | ||||
| +			mutex_lock(&ubi_devices_mutex); | ||||
| +			pr_notice("UBI: auto-attach mtd%d", mtd->index); | ||||
| +			err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0); | ||||
| +			mutex_unlock(&ubi_devices_mutex); | ||||
| +			if (err < 0) { | ||||
| +				pr_err("UBI error: cannot attach mtd%d", mtd->index); | ||||
| +				put_mtd_device(mtd); | ||||
| +			} | ||||
| +		} | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  static int __init ubi_init(void) | ||||
|  { | ||||
|  	int err, i, k; | ||||
| @@ -1283,6 +1326,12 @@ static int __init ubi_init(void) | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +	/* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd | ||||
| +	 * parameter was given */ | ||||
| +	if (config_enabled(CONFIG_MTD_ROOTFS_ROOT_DEV) && | ||||
| +	    !ubi_is_module() && !mtd_devs) | ||||
| +		ubi_auto_attach(); | ||||
| + | ||||
|  	err = ubiblock_init(); | ||||
|  	if (err) { | ||||
|  		pr_err("UBI error: block: cannot initialize, error %d", err); | ||||
| @@ -1,69 +0,0 @@ | ||||
| From 0f3966579815f889bb2fcb4846152c35f65e79c4 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Thu, 15 May 2014 21:06:33 +0200 | ||||
| Subject: [PATCH 2/5] ubi: auto-create ubiblock device for rootfs | ||||
| To: openwrt-devel@lists.openwrt.org | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| --- | ||||
|  drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 42 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/block.c | ||||
| +++ b/drivers/mtd/ubi/block.c | ||||
| @@ -628,6 +628,44 @@ static void __init ubiblock_create_from_ | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +#define UBIFS_NODE_MAGIC  0x06101831 | ||||
| +static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	uint32_t magic_of, magic; | ||||
| +	ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4); | ||||
| +	if (ret) | ||||
| +		return 0; | ||||
| +	magic = le32_to_cpu(magic_of); | ||||
| +	return magic == UBIFS_NODE_MAGIC; | ||||
| +} | ||||
| + | ||||
| +static void __init ubiblock_create_auto_rootfs(void) | ||||
| +{ | ||||
| +	int ubi_num, ret, is_ubifs; | ||||
| +	struct ubi_volume_desc *desc; | ||||
| +	struct ubi_volume_info vi; | ||||
| + | ||||
| +	for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) { | ||||
| +		desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY); | ||||
| +		if (IS_ERR(desc)) | ||||
| +			continue; | ||||
| + | ||||
| +		ubi_get_volume_info(desc, &vi); | ||||
| +		is_ubifs = ubi_vol_is_ubifs(desc); | ||||
| +		ubi_close_volume(desc); | ||||
| +		if (is_ubifs) | ||||
| +			break; | ||||
| + | ||||
| +		ret = ubiblock_create(&vi); | ||||
| +		if (ret) | ||||
| +			pr_err("UBI error: block: can't add '%s' volume, err=%d\n", | ||||
| +				vi.name, ret); | ||||
| +		/* always break if we get here */ | ||||
| +		break; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  static void ubiblock_remove_all(void) | ||||
|  { | ||||
|  	struct ubiblock *next; | ||||
| @@ -658,6 +696,10 @@ int __init ubiblock_init(void) | ||||
|  	 */ | ||||
|  	ubiblock_create_from_param(); | ||||
|   | ||||
| +	/* auto-attach "rootfs" volume if existing and non-ubifs */ | ||||
| +	if (config_enabled(CONFIG_MTD_ROOTFS_ROOT_DEV)) | ||||
| +		ubiblock_create_auto_rootfs(); | ||||
| + | ||||
|  	/* | ||||
|  	 * Block devices are only created upon user requests, so we ignore | ||||
|  	 * existing volumes. | ||||
| @@ -1,53 +0,0 @@ | ||||
| From eea9e1785e4c05c2a3444506aabafa0ae958538f Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Sat, 17 May 2014 03:35:02 +0200 | ||||
| Subject: [PATCH 4/5] try auto-mounting ubi0:rootfs in init/do_mounts.c | ||||
| To: openwrt-devel@lists.openwrt.org | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| --- | ||||
|  init/do_mounts.c | 26 +++++++++++++++++++++++++- | ||||
|  1 file changed, 25 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/init/do_mounts.c | ||||
| +++ b/init/do_mounts.c | ||||
| @@ -438,7 +438,27 @@ retry: | ||||
|  out: | ||||
|  	put_page(page); | ||||
|  } | ||||
| -  | ||||
| + | ||||
| +static int __init mount_ubi_rootfs(void) | ||||
| +{ | ||||
| +	int flags = MS_SILENT; | ||||
| +	int err, tried = 0; | ||||
| + | ||||
| +	while (tried < 2) { | ||||
| +		err = do_mount_root("ubi0:rootfs", "ubifs", flags, \ | ||||
| +					root_mount_data); | ||||
| +		switch (err) { | ||||
| +			case -EACCES: | ||||
| +				flags |= MS_RDONLY; | ||||
| +				tried++; | ||||
| +			default: | ||||
| +				return err; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	return -EINVAL; | ||||
| +} | ||||
| + | ||||
|  #ifdef CONFIG_ROOT_NFS | ||||
|   | ||||
|  #define NFSROOT_TIMEOUT_MIN	5 | ||||
| @@ -532,6 +552,10 @@ void __init mount_root(void) | ||||
|  			change_floppy("root floppy"); | ||||
|  	} | ||||
|  #endif | ||||
| +#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV | ||||
| +	if (!mount_ubi_rootfs()) | ||||
| +		return; | ||||
| +#endif | ||||
|  #ifdef CONFIG_BLOCK | ||||
|  	{ | ||||
|  		int err = create_dev("/dev/root", ROOT_DEV); | ||||
| @@ -1,37 +0,0 @@ | ||||
| From cd68d1b12b5ea4c01a664c064179ada42bf55d3d Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Thu, 15 May 2014 20:55:42 +0200 | ||||
| Subject: [PATCH 5/5] ubi: set ROOT_DEV to ubiblock "rootfs" if unset | ||||
| To: openwrt-devel@lists.openwrt.org | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| --- | ||||
|  drivers/mtd/ubi/block.c | 10 ++++++++++ | ||||
|  1 file changed, 10 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/block.c | ||||
| +++ b/drivers/mtd/ubi/block.c | ||||
| @@ -50,6 +50,7 @@ | ||||
|  #include <linux/scatterlist.h> | ||||
|  #include <linux/idr.h> | ||||
|  #include <asm/div64.h> | ||||
| +#include <linux/root_dev.h> | ||||
|   | ||||
|  #include "ubi-media.h" | ||||
|  #include "ubi.h" | ||||
| @@ -448,6 +449,15 @@ int ubiblock_create(struct ubi_volume_in | ||||
|  	add_disk(dev->gd); | ||||
|  	dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", | ||||
|  		 dev->ubi_num, dev->vol_id, vi->name); | ||||
| + | ||||
| +	if (!strcmp(vi->name, "rootfs") && | ||||
| +	    config_enabled(CONFIG_MTD_ROOTFS_ROOT_DEV) && | ||||
| +	    ROOT_DEV == 0) { | ||||
| +		pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n", | ||||
| +			  dev->ubi_num, dev->vol_id, vi->name); | ||||
| +		ROOT_DEV = MKDEV(gd->major, gd->first_minor); | ||||
| +	} | ||||
| + | ||||
|  	return 0; | ||||
|   | ||||
|  out_free_queue: | ||||
| @@ -1,51 +0,0 @@ | ||||
| --- a/drivers/mtd/ubi/attach.c | ||||
| +++ b/drivers/mtd/ubi/attach.c | ||||
| @@ -803,6 +803,13 @@ out_unlock: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) | ||||
| +{ | ||||
| +	return ech->padding1[0] == 'E' && | ||||
| +	       ech->padding1[1] == 'O' && | ||||
| +	       ech->padding1[2] == 'F'; | ||||
| +} | ||||
| + | ||||
|  /** | ||||
|   * scan_peb - scan and process UBI headers of a PEB. | ||||
|   * @ubi: UBI device description object | ||||
| @@ -833,9 +840,21 @@ static int scan_peb(struct ubi_device *u | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
| -	err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); | ||||
| -	if (err < 0) | ||||
| -		return err; | ||||
| +	if (!ai->eof_found) { | ||||
| +		err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); | ||||
| +		if (err < 0) | ||||
| +			return err; | ||||
| + | ||||
| +		if (ec_hdr_has_eof(ech)) { | ||||
| +			pr_notice("UBI: EOF marker found, PEBs from %d will be erased", | ||||
| +				pnum); | ||||
| +			ai->eof_found = true; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	if (ai->eof_found) | ||||
| +		err = UBI_IO_FF_BITFLIPS; | ||||
| + | ||||
|  	switch (err) { | ||||
|  	case 0: | ||||
|  		break; | ||||
| --- a/drivers/mtd/ubi/ubi.h | ||||
| +++ b/drivers/mtd/ubi/ubi.h | ||||
| @@ -739,6 +739,7 @@ struct ubi_attach_info { | ||||
|  	int mean_ec; | ||||
|  	uint64_t ec_sum; | ||||
|  	int ec_count; | ||||
| +	bool eof_found; | ||||
|  	struct kmem_cache *aeb_slab_cache; | ||||
|  }; | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| --- a/fs/Kconfig | ||||
| +++ b/fs/Kconfig | ||||
| @@ -30,6 +30,7 @@ source "fs/ocfs2/Kconfig" | ||||
|  source "fs/btrfs/Kconfig" | ||||
|  source "fs/nilfs2/Kconfig" | ||||
|  source "fs/f2fs/Kconfig" | ||||
| +source "fs/yaffs2/Kconfig" | ||||
|   | ||||
|  config FS_DAX | ||||
|  	bool "Direct Access (DAX) support" | ||||
| --- a/fs/Makefile | ||||
| +++ b/fs/Makefile | ||||
| @@ -125,3 +125,5 @@ obj-y				+= exofs/ # Multiple modules | ||||
|  obj-$(CONFIG_CEPH_FS)		+= ceph/ | ||||
|  obj-$(CONFIG_PSTORE)		+= pstore/ | ||||
|  obj-$(CONFIG_EFIVAR_FS)		+= efivarfs/ | ||||
| +obj-$(CONFIG_YAFFS_FS)		+= yaffs2/ | ||||
| + | ||||
| @@ -1,239 +0,0 @@ | ||||
| Subject: yaffs: fix compat tags handling | ||||
|  | ||||
| Signed-off-by: Gabor Juhos <juhosg@openwrt.org> | ||||
| --- | ||||
| --- a/fs/yaffs2/yaffs_tagscompat.c | ||||
| +++ b/fs/yaffs2/yaffs_tagscompat.c | ||||
| @@ -17,7 +17,9 @@ | ||||
|  #include "yaffs_getblockinfo.h" | ||||
|  #include "yaffs_trace.h" | ||||
|   | ||||
| +#if 0 | ||||
|  static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); | ||||
| +#endif | ||||
|   | ||||
|   | ||||
|  /********** Tags ECC calculations  *********/ | ||||
| @@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +#if 0 | ||||
|  /********** Tags **********/ | ||||
|   | ||||
|  static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, | ||||
| @@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya | ||||
|  	if(!dev->tagger.mark_bad_fn) | ||||
|  		dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; | ||||
|  } | ||||
| +#else | ||||
| + | ||||
| +#include "yaffs_packedtags1.h" | ||||
| + | ||||
| +static int yaffs_tags_compat_write(struct yaffs_dev *dev, | ||||
| +				   int nand_chunk, | ||||
| +				   const u8 *data, | ||||
| +				   const struct yaffs_ext_tags *tags) | ||||
| +{ | ||||
| +	struct yaffs_packed_tags1 pt1; | ||||
| +	u8 tag_buf[9]; | ||||
| +	int retval; | ||||
| + | ||||
| +	/* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */ | ||||
| +	compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12); | ||||
| +	compile_time_assertion(sizeof(struct yaffs_tags) == 8); | ||||
| + | ||||
| +	yaffs_pack_tags1(&pt1, tags); | ||||
| +	yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1); | ||||
| + | ||||
| +	/* When deleting a chunk, the upper layer provides only skeletal | ||||
| +	 * tags, one with is_deleted set.  However, we need to update the | ||||
| +	 * tags, not erase them completely.  So we use the NAND write property | ||||
| +	 * that only zeroed-bits stick and set tag bytes to all-ones and | ||||
| +	 * zero just the (not) deleted bit. | ||||
| +	 */ | ||||
| +	if (!dev->param.tags_9bytes) { | ||||
| +		if (tags->is_deleted) { | ||||
| +			memset(&pt1, 0xff, 8); | ||||
| +			/* clear delete status bit to indicate deleted */ | ||||
| +			pt1.deleted = 0; | ||||
| +		} | ||||
| +		memcpy(tag_buf, &pt1, 8); | ||||
| +	} else { | ||||
| +		if (tags->is_deleted) { | ||||
| +			memset(tag_buf, 0xff, 8); | ||||
| +			tag_buf[8] = 0; | ||||
| +		} else { | ||||
| +			memcpy(tag_buf, &pt1, 8); | ||||
| +			tag_buf[8] = 0xff; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, | ||||
| +			data, | ||||
| +			(data) ? dev->data_bytes_per_chunk : 0, | ||||
| +			tag_buf, | ||||
| +			(dev->param.tags_9bytes) ? 9 : 8); | ||||
| + | ||||
| +	return retval; | ||||
| +} | ||||
| + | ||||
| +/* Return with empty extended tags but add ecc_result. | ||||
| + */ | ||||
| +static int return_empty_tags(struct yaffs_ext_tags *tags, | ||||
| +			     enum yaffs_ecc_result ecc_result, | ||||
| +			     int retval) | ||||
| +{ | ||||
| +	if (tags) { | ||||
| +		memset(tags, 0, sizeof(*tags)); | ||||
| +		tags->ecc_result = ecc_result; | ||||
| +	} | ||||
| + | ||||
| +	return retval; | ||||
| +} | ||||
| + | ||||
| +static int yaffs_tags_compat_read(struct yaffs_dev *dev, | ||||
| +				  int nand_chunk, | ||||
| +				  u8 *data, | ||||
| +				  struct yaffs_ext_tags *tags) | ||||
| +{ | ||||
| +	struct yaffs_packed_tags1 pt1; | ||||
| +	enum yaffs_ecc_result ecc_result; | ||||
| +	int retval; | ||||
| +	int deleted; | ||||
| +	u8 tag_buf[9]; | ||||
| + | ||||
| +	retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, | ||||
| +			data, dev->param.total_bytes_per_chunk, | ||||
| +			tag_buf, | ||||
| +			(dev->param.tags_9bytes) ? 9 : 8, | ||||
| +			&ecc_result); | ||||
| + | ||||
| +	switch (ecc_result) { | ||||
| +	case YAFFS_ECC_RESULT_NO_ERROR: | ||||
| +	case YAFFS_ECC_RESULT_FIXED: | ||||
| +		break; | ||||
| + | ||||
| +	case YAFFS_ECC_RESULT_UNFIXED: | ||||
| +	default: | ||||
| +		return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0); | ||||
| +		tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk); | ||||
| +		return YAFFS_FAIL; | ||||
| +	} | ||||
| + | ||||
| +	/* Check for a blank/erased chunk. */ | ||||
| +	if (yaffs_check_ff(tag_buf, 8)) { | ||||
| +		/* when blank, upper layers want ecc_result to be <= NO_ERROR */ | ||||
| +		return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR, | ||||
| +					 YAFFS_OK); | ||||
| +	} | ||||
| + | ||||
| +	memcpy(&pt1, tag_buf, 8); | ||||
| + | ||||
| +	if (!dev->param.tags_9bytes) { | ||||
| +		/* Read deleted status (bit) then return it to it's non-deleted | ||||
| +		 * state before performing tags mini-ECC check. pt1.deleted is | ||||
| +		 * inverted. | ||||
| +		 */ | ||||
| +		deleted = !pt1.deleted; | ||||
| +		pt1.deleted = 1; | ||||
| +	} else { | ||||
| +		deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0; | ||||
| +	} | ||||
| + | ||||
| +	/* Check the packed tags mini-ECC and correct if necessary/possible. */ | ||||
| +	retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1); | ||||
| +	switch (retval) { | ||||
| +	case 0: | ||||
| +		/* no tags error, use MTD result */ | ||||
| +		break; | ||||
| +	case 1: | ||||
| +		/* recovered tags-ECC error */ | ||||
| +		dev->n_tags_ecc_fixed++; | ||||
| +		if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR) | ||||
| +			ecc_result = YAFFS_ECC_RESULT_FIXED; | ||||
| +		break; | ||||
| +	default: | ||||
| +		/* unrecovered tags-ECC error */ | ||||
| +		dev->n_tags_ecc_unfixed++; | ||||
| +		return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, | ||||
| +					 YAFFS_FAIL); | ||||
| +	} | ||||
| + | ||||
| +	/* Unpack the tags to extended form and set ECC result. | ||||
| +	 * [set should_be_ff just to keep yaffs_unpack_tags1 happy] | ||||
| +	 */ | ||||
| +	pt1.should_be_ff = 0xffffffff; | ||||
| +	yaffs_unpack_tags1(tags, &pt1); | ||||
| +	tags->ecc_result = ecc_result; | ||||
| + | ||||
| +	/* Set deleted state */ | ||||
| +	tags->is_deleted = deleted; | ||||
| +	return YAFFS_OK; | ||||
| +} | ||||
| + | ||||
| +static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no) | ||||
| +{ | ||||
| +	return dev->drv.drv_mark_bad_fn(dev, block_no); | ||||
| +} | ||||
| + | ||||
| +static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, | ||||
| +					 int block_no, | ||||
| +					 enum yaffs_block_state *state, | ||||
| +					 u32 *seq_number) | ||||
| +{ | ||||
| +	struct yaffs_ext_tags tags; | ||||
| +	int retval; | ||||
| + | ||||
| +	yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no); | ||||
| + | ||||
| +	*seq_number = 0; | ||||
| + | ||||
| +	retval = dev->drv.drv_check_bad_fn(dev, block_no); | ||||
| +	if (retval == YAFFS_FAIL) { | ||||
| +		*state = YAFFS_BLOCK_STATE_DEAD; | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block, | ||||
| +			       NULL, &tags); | ||||
| + | ||||
| +	if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) { | ||||
| +		yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad", | ||||
| +			    block_no); | ||||
| +		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN; | ||||
| +	} else if (tags.chunk_used) { | ||||
| +		*seq_number = tags.seq_number; | ||||
| +		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN; | ||||
| +	} else { | ||||
| +		*state = YAFFS_BLOCK_STATE_EMPTY; | ||||
| +	} | ||||
| + | ||||
| +	retval = YAFFS_OK; | ||||
| + | ||||
| +out: | ||||
| +	yaffs_trace(YAFFS_TRACE_MTD, | ||||
| +		    "block query returns seq %u state %d", | ||||
| +		    *seq_number, *state); | ||||
| + | ||||
| +	return retval; | ||||
| +} | ||||
| + | ||||
| +void yaffs_tags_compat_install(struct yaffs_dev *dev) | ||||
| +{ | ||||
| +	if (dev->param.is_yaffs2) | ||||
| +		return; | ||||
| + | ||||
| +	if (!dev->tagger.write_chunk_tags_fn) | ||||
| +		dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write; | ||||
| + | ||||
| +	if (!dev->tagger.read_chunk_tags_fn) | ||||
| +		dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read; | ||||
| + | ||||
| +	if (!dev->tagger.query_block_fn) | ||||
| +		dev->tagger.query_block_fn = yaffs_tags_compat_query_block; | ||||
| + | ||||
| +	if (!dev->tagger.mark_bad_fn) | ||||
| +		dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; | ||||
| +} | ||||
| +#endif | ||||
| @@ -1,115 +0,0 @@ | ||||
| Subject: yaffs: add support for tags-9bytes mount option | ||||
|  | ||||
| Signed-off-by: Gabor Juhos <juhosg@openwrt.org> | ||||
| --- | ||||
| --- a/fs/yaffs2/yaffs_vfs.c | ||||
| +++ b/fs/yaffs2/yaffs_vfs.c | ||||
| @@ -2644,6 +2644,7 @@ static const struct super_operations yaf | ||||
|   | ||||
|  struct yaffs_options { | ||||
|  	int inband_tags; | ||||
| +	int tags_9bytes; | ||||
|  	int skip_checkpoint_read; | ||||
|  	int skip_checkpoint_write; | ||||
|  	int no_cache; | ||||
| @@ -2683,6 +2684,8 @@ static int yaffs_parse_options(struct ya | ||||
|   | ||||
|  		if (!strcmp(cur_opt, "inband-tags")) { | ||||
|  			options->inband_tags = 1; | ||||
| +		} else if (!strcmp(cur_opt, "tags-9bytes")) { | ||||
| +			options->tags_9bytes = 1; | ||||
|  		} else if (!strcmp(cur_opt, "tags-ecc-off")) { | ||||
|  			options->tags_ecc_on = 0; | ||||
|  			options->tags_ecc_overridden = 1; | ||||
| @@ -2756,7 +2759,6 @@ static struct super_block *yaffs_interna | ||||
|  	struct yaffs_param *param; | ||||
|   | ||||
|  	int read_only = 0; | ||||
| -	int inband_tags = 0; | ||||
|   | ||||
|  	struct yaffs_options options; | ||||
|   | ||||
| @@ -2796,6 +2798,9 @@ static struct super_block *yaffs_interna | ||||
|   | ||||
|  	memset(&options, 0, sizeof(options)); | ||||
|   | ||||
| +	if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS)) | ||||
| +		options.tags_9bytes = 1; | ||||
| + | ||||
|  	if (yaffs_parse_options(&options, data_str)) { | ||||
|  		/* Option parsing failed */ | ||||
|  		return NULL; | ||||
| @@ -2829,17 +2834,22 @@ static struct super_block *yaffs_interna | ||||
|  	} | ||||
|   | ||||
|  	/* Added NCB 26/5/2006 for completeness */ | ||||
| -	if (yaffs_version == 2 && !options.inband_tags | ||||
| -	    && WRITE_SIZE(mtd) == 512) { | ||||
| +	if (yaffs_version == 2 && | ||||
| +	    (!options.inband_tags || options.tags_9bytes) && | ||||
| +	    WRITE_SIZE(mtd) == 512) { | ||||
|  		yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); | ||||
|  		yaffs_version = 1; | ||||
|  	} | ||||
|   | ||||
| -	if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || | ||||
| -	    options.inband_tags) | ||||
| -		inband_tags = 1; | ||||
| +	if (yaffs_version == 2 && | ||||
| +	    mtd->oobavail < sizeof(struct yaffs_packed_tags2)) { | ||||
| +		yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags"); | ||||
| +		options.inband_tags = 1; | ||||
| +	} | ||||
|   | ||||
| -	if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) | ||||
| +	err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags, | ||||
| +			       options.tags_9bytes); | ||||
| +	if (err < 0) | ||||
|  		return NULL; | ||||
|   | ||||
|  	/* OK, so if we got here, we have an MTD that's NAND and looks | ||||
| @@ -2896,7 +2906,8 @@ static struct super_block *yaffs_interna | ||||
|   | ||||
|  	param->n_reserved_blocks = 5; | ||||
|  	param->n_caches = (options.no_cache) ? 0 : 10; | ||||
| -	param->inband_tags = inband_tags; | ||||
| +	param->inband_tags = options.inband_tags; | ||||
| +	param->tags_9bytes = options.tags_9bytes; | ||||
|   | ||||
|  	param->enable_xattr = 1; | ||||
|  	if (options.lazy_loading_overridden) | ||||
| --- a/fs/yaffs2/yaffs_mtdif.c | ||||
| +++ b/fs/yaffs2/yaffs_mtdif.c | ||||
| @@ -278,7 +278,8 @@ struct mtd_info * yaffs_get_mtd_device(d | ||||
|  	return mtd; | ||||
|  } | ||||
|   | ||||
| -int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) | ||||
| +int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, | ||||
| +		     int tags_9bytes) | ||||
|  { | ||||
|  	if (yaffs_version == 2) { | ||||
|  		if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || | ||||
| @@ -297,6 +298,12 @@ int yaffs_verify_mtd(struct mtd_info *mt | ||||
|  			); | ||||
|  			return -1; | ||||
|  		} | ||||
| + | ||||
| +		if (tags_9bytes && mtd->oobavail < 9) { | ||||
| +			yaffs_trace(YAFFS_TRACE_ALWAYS, | ||||
| +				    "MTD device does not support 9-byte tags"); | ||||
| +			return -1; | ||||
| +		} | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
| --- a/fs/yaffs2/yaffs_mtdif.h | ||||
| +++ b/fs/yaffs2/yaffs_mtdif.h | ||||
| @@ -21,5 +21,6 @@ | ||||
|  void yaffs_mtd_drv_install(struct yaffs_dev *dev); | ||||
|  struct mtd_info * yaffs_get_mtd_device(dev_t sdev); | ||||
|  void yaffs_put_mtd_device(struct mtd_info *mtd); | ||||
| -int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); | ||||
| +int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, | ||||
| +		     int tags_9bytes); | ||||
|  #endif | ||||
| @@ -1,29 +0,0 @@ | ||||
| --- a/fs/yaffs2/yaffs_vfs.c | ||||
| +++ b/fs/yaffs2/yaffs_vfs.c | ||||
| @@ -774,7 +774,25 @@ static int yaffs_sync_object(struct file | ||||
|  } | ||||
|   | ||||
|   | ||||
| -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) | ||||
| +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) | ||||
| +static const struct file_operations yaffs_file_operations = { | ||||
| +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) | ||||
| +	.read = new_sync_read, | ||||
| +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) */ | ||||
| +	.read_iter = generic_file_read_iter, | ||||
| +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) | ||||
| +	.write = new_sync_write, | ||||
| +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) */ | ||||
| +	.write_iter = generic_file_write_iter, | ||||
| +	.mmap = generic_file_mmap, | ||||
| +	.flush = yaffs_file_flush, | ||||
| +	.fsync = yaffs_sync_object, | ||||
| +	.splice_read = generic_file_splice_read, | ||||
| +	.splice_write = iter_file_splice_write, | ||||
| +	.llseek = generic_file_llseek, | ||||
| +}; | ||||
| + | ||||
| +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) | ||||
|  static const struct file_operations yaffs_file_operations = { | ||||
|  	.read = do_sync_read, | ||||
|  	.write = do_sync_write, | ||||
| @@ -1,95 +0,0 @@ | ||||
| --- a/fs/yaffs2/yaffs_vfs.c | ||||
| +++ b/fs/yaffs2/yaffs_vfs.c | ||||
| @@ -283,7 +283,7 @@ static int yaffs_readpage_nolock(struct | ||||
|  		(long long)pos, | ||||
|  		(unsigned)PAGE_CACHE_SIZE); | ||||
|   | ||||
| -	obj = yaffs_dentry_to_obj(f->f_dentry); | ||||
| +	obj = yaffs_dentry_to_obj(f->f_path.dentry); | ||||
|   | ||||
|  	dev = obj->my_dev; | ||||
|   | ||||
| @@ -481,7 +481,7 @@ static ssize_t yaffs_hold_space(struct f | ||||
|   | ||||
|  	int n_free_chunks; | ||||
|   | ||||
| -	obj = yaffs_dentry_to_obj(f->f_dentry); | ||||
| +	obj = yaffs_dentry_to_obj(f->f_path.dentry); | ||||
|   | ||||
|  	dev = obj->my_dev; | ||||
|   | ||||
| @@ -499,7 +499,7 @@ static void yaffs_release_space(struct f | ||||
|  	struct yaffs_obj *obj; | ||||
|  	struct yaffs_dev *dev; | ||||
|   | ||||
| -	obj = yaffs_dentry_to_obj(f->f_dentry); | ||||
| +	obj = yaffs_dentry_to_obj(f->f_path.dentry); | ||||
|   | ||||
|  	dev = obj->my_dev; | ||||
|   | ||||
| @@ -591,7 +591,7 @@ static ssize_t yaffs_file_write(struct f | ||||
|  	struct inode *inode; | ||||
|  	struct yaffs_dev *dev; | ||||
|   | ||||
| -	obj = yaffs_dentry_to_obj(f->f_dentry); | ||||
| +	obj = yaffs_dentry_to_obj(f->f_path.dentry); | ||||
|   | ||||
|  	if (!obj) { | ||||
|  		yaffs_trace(YAFFS_TRACE_OS, | ||||
| @@ -603,7 +603,7 @@ static ssize_t yaffs_file_write(struct f | ||||
|   | ||||
|  	yaffs_gross_lock(dev); | ||||
|   | ||||
| -	inode = f->f_dentry->d_inode; | ||||
| +	inode = f->f_path.dentry->d_inode; | ||||
|   | ||||
|  	if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) | ||||
|  		ipos = inode->i_size; | ||||
| @@ -727,7 +727,7 @@ static int yaffs_file_flush(struct file | ||||
|  static int yaffs_file_flush(struct file *file) | ||||
|  #endif | ||||
|  { | ||||
| -	struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); | ||||
| +	struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_path.dentry); | ||||
|   | ||||
|  	struct yaffs_dev *dev = obj->my_dev; | ||||
|   | ||||
| @@ -1734,7 +1734,7 @@ static int yaffs_iterate(struct file *f, | ||||
|   | ||||
|  	char name[YAFFS_MAX_NAME_LENGTH + 1]; | ||||
|   | ||||
| -	obj = yaffs_dentry_to_obj(f->f_dentry); | ||||
| +	obj = yaffs_dentry_to_obj(f->f_path.dentry); | ||||
|  	dev = obj->my_dev; | ||||
|   | ||||
|  	yaffs_gross_lock(dev); | ||||
| @@ -1798,14 +1798,14 @@ static int yaffs_readdir(struct file *f, | ||||
|  	struct yaffs_obj *obj; | ||||
|  	struct yaffs_dev *dev; | ||||
|  	struct yaffs_search_context *sc; | ||||
| -	struct inode *inode = f->f_dentry->d_inode; | ||||
| +	struct inode *inode = f->f_path.dentry->d_inode; | ||||
|  	unsigned long offset, curoffs; | ||||
|  	struct yaffs_obj *l; | ||||
|  	int ret_val = 0; | ||||
|   | ||||
|  	char name[YAFFS_MAX_NAME_LENGTH + 1]; | ||||
|   | ||||
| -	obj = yaffs_dentry_to_obj(f->f_dentry); | ||||
| +	obj = yaffs_dentry_to_obj(f->f_path.dentry); | ||||
|  	dev = obj->my_dev; | ||||
|   | ||||
|  	yaffs_gross_lock(dev); | ||||
| @@ -1839,10 +1839,10 @@ static int yaffs_readdir(struct file *f, | ||||
|  	if (offset == 1) { | ||||
|  		yaffs_trace(YAFFS_TRACE_OS, | ||||
|  			"yaffs_readdir: entry .. ino %d", | ||||
| -			(int)f->f_dentry->d_parent->d_inode->i_ino); | ||||
| +			(int)f->f_path.dentry->d_parent->d_inode->i_ino); | ||||
|  		yaffs_gross_unlock(dev); | ||||
|  		if (filldir(dirent, "..", 2, offset, | ||||
| -			    f->f_dentry->d_parent->d_inode->i_ino, | ||||
| +			    f->f_path.dentry->d_parent->d_inode->i_ino, | ||||
|  			    DT_DIR) < 0) { | ||||
|  			yaffs_gross_lock(dev); | ||||
|  			goto out; | ||||
| @@ -1,25 +0,0 @@ | ||||
| From f31b7c0efa255dd17a5f584022a319387f09b0d8 Mon Sep 17 00:00:00 2001 | ||||
| From: Jonas Gorski <jonas.gorski@gmail.com> | ||||
| Date: Tue, 12 Apr 2011 19:55:41 +0200 | ||||
| Subject: [PATCH] squashfs: update xz compressor options struct. | ||||
|  | ||||
| Update the xz compressor options struct to match the squashfs userspace | ||||
| one. | ||||
| --- | ||||
|  fs/squashfs/xz_wrapper.c |    4 +++- | ||||
|  1 files changed, 3 insertions(+), 1 deletions(-) | ||||
|  | ||||
| --- a/fs/squashfs/xz_wrapper.c | ||||
| +++ b/fs/squashfs/xz_wrapper.c | ||||
| @@ -40,8 +40,10 @@ struct squashfs_xz { | ||||
|  }; | ||||
|   | ||||
|  struct disk_comp_opts { | ||||
| -	__le32 dictionary_size; | ||||
|  	__le32 flags; | ||||
| +	__le16 bit_opts; | ||||
| +	__le16 fb; | ||||
| +	__le32 dictionary_size; | ||||
|  }; | ||||
|   | ||||
|  struct comp_opts { | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,56 +0,0 @@ | ||||
| --- a/fs/jffs2/build.c | ||||
| +++ b/fs/jffs2/build.c | ||||
| @@ -114,6 +114,16 @@ static int jffs2_build_filesystem(struct | ||||
|  	dbg_fsbuild("scanned flash completely\n"); | ||||
|  	jffs2_dbg_dump_block_lists_nolock(c); | ||||
|   | ||||
| +	if (c->flags & (1 << 7)) { | ||||
| +		printk("%s(): unlocking the mtd device... ", __func__); | ||||
| +		mtd_unlock(c->mtd, 0, c->mtd->size); | ||||
| +		printk("done.\n"); | ||||
| + | ||||
| +		printk("%s(): erasing all blocks after the end marker... ", __func__); | ||||
| +		jffs2_erase_pending_blocks(c, -1); | ||||
| +		printk("done.\n"); | ||||
| +	} | ||||
| + | ||||
|  	dbg_fsbuild("pass 1 starting\n"); | ||||
|  	c->flags |= JFFS2_SB_FLAG_BUILDING; | ||||
|  	/* Now scan the directory tree, increasing nlink according to every dirent found. */ | ||||
| --- a/fs/jffs2/scan.c | ||||
| +++ b/fs/jffs2/scan.c | ||||
| @@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in | ||||
|  		/* reset summary info for next eraseblock scan */ | ||||
|  		jffs2_sum_reset_collected(s); | ||||
|   | ||||
| -		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), | ||||
| -						buf_size, s); | ||||
| +		if (c->flags & (1 << 7)) { | ||||
| +			if (mtd_block_isbad(c->mtd, jeb->offset)) | ||||
| +				ret = BLK_STATE_BADBLOCK; | ||||
| +			else | ||||
| +				ret = BLK_STATE_ALLFF; | ||||
| +		} else | ||||
| +			ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), | ||||
| +							buf_size, s); | ||||
|   | ||||
|  		if (ret < 0) | ||||
|  			goto out; | ||||
| @@ -561,6 +567,17 @@ full_scan: | ||||
|  			return err; | ||||
|  	} | ||||
|   | ||||
| +	if ((buf[0] == 0xde) && | ||||
| +		(buf[1] == 0xad) && | ||||
| +		(buf[2] == 0xc0) && | ||||
| +		(buf[3] == 0xde)) { | ||||
| +		/* end of filesystem. erase everything after this point */ | ||||
| +		printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); | ||||
| +		c->flags |= (1 << 7); | ||||
| + | ||||
| +		return BLK_STATE_ALLFF; | ||||
| +	} | ||||
| + | ||||
|  	/* We temporarily use 'ofs' as a pointer into the buffer/jeb */ | ||||
|  	ofs = 0; | ||||
|  	max_ofs = EMPTY_SCAN_SIZE(c->sector_size); | ||||
| @@ -1,146 +0,0 @@ | ||||
| --- a/crypto/Kconfig | ||||
| +++ b/crypto/Kconfig | ||||
| @@ -1536,6 +1536,13 @@ config CRYPTO_LZ4HC | ||||
|  	help | ||||
|  	  This is the LZ4 high compression mode algorithm. | ||||
|   | ||||
| +config CRYPTO_XZ | ||||
| +	tristate "XZ compression algorithm" | ||||
| +	select CRYPTO_ALGAPI | ||||
| +	select XZ_DEC | ||||
| +	help | ||||
| +	  This is the XZ algorithm. Only decompression is supported for now. | ||||
| + | ||||
|  comment "Random Number Generation" | ||||
|   | ||||
|  config CRYPTO_ANSI_CPRNG | ||||
| --- a/crypto/Makefile | ||||
| +++ b/crypto/Makefile | ||||
| @@ -103,6 +103,7 @@ obj-$(CONFIG_CRYPTO_AUTHENC) += authenc. | ||||
|  obj-$(CONFIG_CRYPTO_LZO) += lzo.o | ||||
|  obj-$(CONFIG_CRYPTO_LZ4) += lz4.o | ||||
|  obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o | ||||
| +obj-$(CONFIG_CRYPTO_XZ) += xz.o | ||||
|  obj-$(CONFIG_CRYPTO_842) += 842.o | ||||
|  obj-$(CONFIG_CRYPTO_RNG2) += rng.o | ||||
|  obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o | ||||
| --- /dev/null | ||||
| +++ b/crypto/xz.c | ||||
| @@ -0,0 +1,117 @@ | ||||
| +/* | ||||
| + * Cryptographic API. | ||||
| + * | ||||
| + * XZ decompression support. | ||||
| + * | ||||
| + * Copyright (c) 2012 Gabor Juhos <juhosg@openwrt.org> | ||||
| + * | ||||
| + * 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/init.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/crypto.h> | ||||
| +#include <linux/xz.h> | ||||
| +#include <linux/interrupt.h> | ||||
| +#include <linux/mm.h> | ||||
| +#include <linux/net.h> | ||||
| + | ||||
| +struct xz_comp_ctx { | ||||
| +	struct xz_dec	*decomp_state; | ||||
| +	struct xz_buf	decomp_buf; | ||||
| +}; | ||||
| + | ||||
| +static int crypto_xz_decomp_init(struct xz_comp_ctx *ctx) | ||||
| +{ | ||||
| +	ctx->decomp_state = xz_dec_init(XZ_SINGLE, 0); | ||||
| +	if (!ctx->decomp_state) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void crypto_xz_decomp_exit(struct xz_comp_ctx *ctx) | ||||
| +{ | ||||
| +	xz_dec_end(ctx->decomp_state); | ||||
| +} | ||||
| + | ||||
| +static int crypto_xz_init(struct crypto_tfm *tfm) | ||||
| +{ | ||||
| +	struct xz_comp_ctx *ctx = crypto_tfm_ctx(tfm); | ||||
| + | ||||
| +	return crypto_xz_decomp_init(ctx); | ||||
| +} | ||||
| + | ||||
| +static void crypto_xz_exit(struct crypto_tfm *tfm) | ||||
| +{ | ||||
| +	struct xz_comp_ctx *ctx = crypto_tfm_ctx(tfm); | ||||
| + | ||||
| +	crypto_xz_decomp_exit(ctx); | ||||
| +} | ||||
| + | ||||
| +static int crypto_xz_compress(struct crypto_tfm *tfm, const u8 *src, | ||||
| +			      unsigned int slen, u8 *dst, unsigned int *dlen) | ||||
| +{ | ||||
| +	return -EOPNOTSUPP; | ||||
| +} | ||||
| + | ||||
| +static int crypto_xz_decompress(struct crypto_tfm *tfm, const u8 *src, | ||||
| +				unsigned int slen, u8 *dst, unsigned int *dlen) | ||||
| +{ | ||||
| +	struct xz_comp_ctx *dctx = crypto_tfm_ctx(tfm); | ||||
| +	struct xz_buf *xz_buf = &dctx->decomp_buf; | ||||
| +	int ret; | ||||
| + | ||||
| +	memset(xz_buf, '\0', sizeof(struct xz_buf)); | ||||
| + | ||||
| +	xz_buf->in = (u8 *) src; | ||||
| +	xz_buf->in_pos = 0; | ||||
| +	xz_buf->in_size = slen; | ||||
| +	xz_buf->out = (u8 *) dst; | ||||
| +	xz_buf->out_pos = 0; | ||||
| +	xz_buf->out_size = *dlen; | ||||
| + | ||||
| +	ret = xz_dec_run(dctx->decomp_state, xz_buf); | ||||
| +	if (ret != XZ_STREAM_END) { | ||||
| +		ret = -EINVAL; | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	*dlen = xz_buf->out_pos; | ||||
| +	ret = 0; | ||||
| + | ||||
| +out: | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static struct crypto_alg crypto_xz_alg = { | ||||
| +	.cra_name		= "xz", | ||||
| +	.cra_flags		= CRYPTO_ALG_TYPE_COMPRESS, | ||||
| +	.cra_ctxsize		= sizeof(struct xz_comp_ctx), | ||||
| +	.cra_module		= THIS_MODULE, | ||||
| +	.cra_list		= LIST_HEAD_INIT(crypto_xz_alg.cra_list), | ||||
| +	.cra_init		= crypto_xz_init, | ||||
| +	.cra_exit		= crypto_xz_exit, | ||||
| +	.cra_u			= { .compress = { | ||||
| +	.coa_compress 		= crypto_xz_compress, | ||||
| +	.coa_decompress  	= crypto_xz_decompress } } | ||||
| +}; | ||||
| + | ||||
| +static int __init crypto_xz_mod_init(void) | ||||
| +{ | ||||
| +	return crypto_register_alg(&crypto_xz_alg); | ||||
| +} | ||||
| + | ||||
| +static void __exit crypto_xz_mod_exit(void) | ||||
| +{ | ||||
| +	crypto_unregister_alg(&crypto_xz_alg); | ||||
| +} | ||||
| + | ||||
| +module_init(crypto_xz_mod_init); | ||||
| +module_exit(crypto_xz_mod_exit); | ||||
| + | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| +MODULE_DESCRIPTION("Crypto XZ decompression support"); | ||||
| +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau