diff options
95 files changed, 2022 insertions, 850 deletions
| diff --git a/Documentation/devicetree/bindings/arm/msm/ssbi.txt b/Documentation/devicetree/bindings/arm/msm/ssbi.txt new file mode 100644 index 00000000000..54fd5ced340 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/ssbi.txt @@ -0,0 +1,18 @@ +* Qualcomm SSBI + +Some Qualcomm MSM devices contain a point-to-point serial bus used to +communicate with a limited range of devices (mostly power management +chips). + +These require the following properties: + +- compatible: "qcom,ssbi" + +- qcom,controller-type +  indicates the SSBI bus variant the controller should use to talk +  with the slave device.  This should be one of "ssbi", "ssbi2", or +  "pmic-arbiter".  The type chosen is determined by the attached +  slave. + +The slave device should be the single child node of the ssbi device +with a compatible field. diff --git a/MAINTAINERS b/MAINTAINERS index 4cf5fd334a0..8b9cbc780ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1031,6 +1031,7 @@ F:	drivers/mmc/host/msm_sdcc.h  F:	drivers/tty/serial/msm_serial.h  F:	drivers/tty/serial/msm_serial.c  F:	drivers/*/pm8???-* +F:	drivers/ssbi/  F:	include/linux/mfd/pm8xxx/  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/davidb/linux-msm.git  S:	Maintained diff --git a/arch/arm/boot/dts/msm8660-surf.dts b/arch/arm/boot/dts/msm8660-surf.dts index 31f2157cd7d..67f8670c4d6 100644 --- a/arch/arm/boot/dts/msm8660-surf.dts +++ b/arch/arm/boot/dts/msm8660-surf.dts @@ -38,4 +38,10 @@  		      <0x19c00000 0x1000>;  		interrupts = <0 195 0x0>;  	}; + +	qcom,ssbi@500000 { +		compatible = "qcom,ssbi"; +		reg = <0x500000 0x1000>; +		qcom,controller-type = "pmic-arbiter"; +	};  }; diff --git a/arch/arm/boot/dts/msm8960-cdp.dts b/arch/arm/boot/dts/msm8960-cdp.dts index 9e621b5ad3d..c9b09a813a4 100644 --- a/arch/arm/boot/dts/msm8960-cdp.dts +++ b/arch/arm/boot/dts/msm8960-cdp.dts @@ -38,4 +38,10 @@  		      <0x16400000 0x1000>;  		interrupts = <0 154 0x0>;  	}; + +	qcom,ssbi@500000 { +		compatible = "qcom,ssbi"; +		reg = <0x500000 0x1000>; +		qcom,controller-type = "pmic-arbiter"; +	};  }; diff --git a/drivers/Kconfig b/drivers/Kconfig index 202fa6d051b..78a956e286e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"  source "drivers/spi/Kconfig" +source "drivers/ssbi/Kconfig" +  source "drivers/hsi/Kconfig"  source "drivers/pps/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index dce39a95fa7..4865ed24708 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -114,6 +114,7 @@ obj-y				+= firmware/  obj-$(CONFIG_CRYPTO)		+= crypto/  obj-$(CONFIG_SUPERH)		+= sh/  obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/ +obj-$(CONFIG_SSBI)		+= ssbi/  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET  obj-y				+= clocksource/  endif diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 958238dda8f..40254f4df58 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -387,21 +387,9 @@ static struct pcmcia_driver pcmcia_driver = {  	.probe		= pcmcia_init_one,  	.remove		= pcmcia_remove_one,  }; - -static int __init pcmcia_init(void) -{ -	return pcmcia_register_driver(&pcmcia_driver); -} - -static void __exit pcmcia_exit(void) -{ -	pcmcia_unregister_driver(&pcmcia_driver); -} +module_pcmcia_driver(pcmcia_driver);  MODULE_AUTHOR("Alan Cox");  MODULE_DESCRIPTION("low-level driver for PCMCIA ATA");  MODULE_LICENSE("GPL");  MODULE_VERSION(DRV_VERSION); - -module_init(pcmcia_init); -module_exit(pcmcia_exit); diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 0d26851d6e4..6c3e3d43c71 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -934,17 +934,4 @@ static struct pcmcia_driver bluecard_driver = {  	.remove		= bluecard_detach,  	.id_table	= bluecard_ids,  }; - -static int __init init_bluecard_cs(void) -{ -	return pcmcia_register_driver(&bluecard_driver); -} - - -static void __exit exit_bluecard_cs(void) -{ -	pcmcia_unregister_driver(&bluecard_driver); -} - -module_init(init_bluecard_cs); -module_exit(exit_bluecard_cs); +module_pcmcia_driver(bluecard_driver); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 7ffd3f40714..a1aaa3ba2a4 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -760,17 +760,4 @@ static struct pcmcia_driver bt3c_driver = {  	.remove		= bt3c_detach,  	.id_table	= bt3c_ids,  }; - -static int __init init_bt3c_cs(void) -{ -	return pcmcia_register_driver(&bt3c_driver); -} - - -static void __exit exit_bt3c_cs(void) -{ -	pcmcia_unregister_driver(&bt3c_driver); -} - -module_init(init_bt3c_cs); -module_exit(exit_bt3c_cs); +module_pcmcia_driver(bt3c_driver); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 35a553a9061..beb262f2dc4 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -688,17 +688,4 @@ static struct pcmcia_driver btuart_driver = {  	.remove		= btuart_detach,  	.id_table	= btuart_ids,  }; - -static int __init init_btuart_cs(void) -{ -	return pcmcia_register_driver(&btuart_driver); -} - - -static void __exit exit_btuart_cs(void) -{ -	pcmcia_unregister_driver(&btuart_driver); -} - -module_init(init_btuart_cs); -module_exit(exit_btuart_cs); +module_pcmcia_driver(btuart_driver); diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 036cb366fe6..33f3a6950c0 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -628,17 +628,4 @@ static struct pcmcia_driver dtl1_driver = {  	.remove		= dtl1_detach,  	.id_table	= dtl1_ids,  }; - -static int __init init_dtl1_cs(void) -{ -	return pcmcia_register_driver(&dtl1_driver); -} - - -static void __exit exit_dtl1_cs(void) -{ -	pcmcia_unregister_driver(&dtl1_driver); -} - -module_init(init_dtl1_cs); -module_exit(exit_dtl1_cs); +module_pcmcia_driver(dtl1_driver); diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 25373df1dcf..974321a2508 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -804,8 +804,8 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  			printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",  			       i+1, -			       (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4), -			       (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF), +			       (int)(readb(apbs[i].RamIO + VERS) >> 4), +			       (int)(readb(apbs[i].RamIO + VERS) & 0xF),  			       boardname); diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index f05d85713fd..895d0b8fb9a 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -228,18 +228,7 @@ static struct platform_driver mxc_rnga_driver = {  	.remove = __exit_p(mxc_rnga_remove),  }; -static int __init mod_init(void) -{ -	return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe); -} - -static void __exit mod_exit(void) -{ -	platform_driver_unregister(&mxc_rnga_driver); -} - -module_init(mod_init); -module_exit(mod_exit); +module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe);  MODULE_AUTHOR("Freescale Semiconductor, Inc.");  MODULE_DESCRIPTION("H/W RNGA driver for i.MX"); diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c index 30991989d65..d34a24a0d48 100644 --- a/drivers/char/hw_random/tx4939-rng.c +++ b/drivers/char/hw_random/tx4939-rng.c @@ -166,18 +166,7 @@ static struct platform_driver tx4939_rng_driver = {  	.remove = tx4939_rng_remove,  }; -static int __init tx4939rng_init(void) -{ -	return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe); -} - -static void __exit tx4939rng_exit(void) -{ -	platform_driver_unregister(&tx4939_rng_driver); -} - -module_init(tx4939rng_init); -module_exit(tx4939rng_exit); +module_platform_driver_probe(tx4939_rng_driver, tx4939_rng_probe);  MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 3b22a606f79..2e2036e940f 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -371,7 +371,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index)  	dev = device_create(srom_class, &platform_bus,  			    MKDEV(srom_major, index), srom, "%d", index); -	return IS_ERR(dev) ? PTR_ERR(dev) : 0; +	return PTR_RET(dev);  }  /** srom_init() - Initialize the driver's module. */ diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index e6abfa02d8b..0a74b566118 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON)	+= hv_balloon.o  hv_vmbus-y := vmbus_drv.o \  		 hv.o connection.o channel.o \  		 channel_mgmt.o ring_buffer.o -hv_utils-y := hv_util.o hv_kvp.o +hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index ff1be167eb0..bad8128b283 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -165,8 +165,19 @@ static void vmbus_process_rescind_offer(struct work_struct *work)  	struct vmbus_channel *channel = container_of(work,  						     struct vmbus_channel,  						     work); +	unsigned long flags; +	struct vmbus_channel_relid_released msg;  	vmbus_device_unregister(channel->device_obj); +	memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); +	msg.child_relid = channel->offermsg.child_relid; +	msg.header.msgtype = CHANNELMSG_RELID_RELEASED; +	vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); + +	spin_lock_irqsave(&vmbus_connection.channel_lock, flags); +	list_del(&channel->listentry); +	spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); +	free_channel(channel);  }  void vmbus_free_channels(void) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 731158910c1..ae4923756d9 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -289,9 +289,8 @@ void hv_synic_init(void *arg)  	/* Check the version */  	rdmsrl(HV_X64_MSR_SVERSION, version); -	hv_context.event_dpc[cpu] = (struct tasklet_struct *) -					kmalloc(sizeof(struct tasklet_struct), -						GFP_ATOMIC); +	hv_context.event_dpc[cpu] = kmalloc(sizeof(struct tasklet_struct), +					    GFP_ATOMIC);  	if (hv_context.event_dpc[cpu] == NULL) {  		pr_err("Unable to allocate event dpc\n");  		goto cleanup; diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 37873213e24..d5225261ee5 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -412,13 +412,45 @@ struct dm_info_msg {   * End protocol definitions.   */ -static bool hot_add; +/* + * State to manage hot adding memory into the guest. + * The range start_pfn : end_pfn specifies the range + * that the host has asked us to hot add. The range + * start_pfn : ha_end_pfn specifies the range that we have + * currently hot added. We hot add in multiples of 128M + * chunks; it is possible that we may not be able to bring + * online all the pages in the region. The range + * covered_start_pfn : covered_end_pfn defines the pages that can + * be brough online. + */ + +struct hv_hotadd_state { +	struct list_head list; +	unsigned long start_pfn; +	unsigned long covered_start_pfn; +	unsigned long covered_end_pfn; +	unsigned long ha_end_pfn; +	unsigned long end_pfn; +}; + +struct balloon_state { +	__u32 num_pages; +	struct work_struct wrk; +}; + +struct hot_add_wrk { +	union dm_mem_page_range ha_page_range; +	union dm_mem_page_range ha_region_range; +	struct work_struct wrk; +}; + +static bool hot_add = true;  static bool do_hot_add;  /*   * Delay reporting memory pressure by   * the specified number of seconds.   */ -static uint pressure_report_delay = 30; +static uint pressure_report_delay = 45;  module_param(hot_add, bool, (S_IRUGO | S_IWUSR));  MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add"); @@ -446,6 +478,7 @@ enum hv_dm_state {  static __u8 recv_buffer[PAGE_SIZE];  static __u8 *send_buffer;  #define PAGES_IN_2M	512 +#define HA_CHUNK (32 * 1024)  struct hv_dynmem_device {  	struct hv_device *dev; @@ -459,7 +492,28 @@ struct hv_dynmem_device {  	unsigned int num_pages_ballooned;  	/* -	 * This thread handles both balloon/hot-add +	 * State to manage the ballooning (up) operation. +	 */ +	struct balloon_state balloon_wrk; + +	/* +	 * State to execute the "hot-add" operation. +	 */ +	struct hot_add_wrk ha_wrk; + +	/* +	 * This state tracks if the host has specified a hot-add +	 * region. +	 */ +	bool host_specified_ha_region; + +	/* +	 * State to synchronize hot-add. +	 */ +	struct completion  ol_waitevent; +	bool ha_waiting; +	/* +	 * This thread handles hot-add  	 * requests from the host as well as notifying  	 * the host with regards to memory pressure in  	 * the guest. @@ -467,6 +521,11 @@ struct hv_dynmem_device {  	struct task_struct *thread;  	/* +	 * A list of hot-add regions. +	 */ +	struct list_head ha_region_list; + +	/*  	 * We start with the highest version we can support  	 * and downgrade based on the host; we save here the  	 * next version to try. @@ -476,35 +535,329 @@ struct hv_dynmem_device {  static struct hv_dynmem_device dm_device; -static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg) +#ifdef CONFIG_MEMORY_HOTPLUG + +static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)  { +	int i; -	struct dm_hot_add_response resp; +	for (i = 0; i < size; i++) { +		struct page *pg; +		pg = pfn_to_page(start_pfn + i); +		__online_page_set_limits(pg); +		__online_page_increment_counters(pg); +		__online_page_free(pg); +	} +} + +static void hv_mem_hot_add(unsigned long start, unsigned long size, +				unsigned long pfn_count, +				struct hv_hotadd_state *has) +{ +	int ret = 0; +	int i, nid, t; +	unsigned long start_pfn; +	unsigned long processed_pfn; +	unsigned long total_pfn = pfn_count; + +	for (i = 0; i < (size/HA_CHUNK); i++) { +		start_pfn = start + (i * HA_CHUNK); +		has->ha_end_pfn +=  HA_CHUNK; + +		if (total_pfn > HA_CHUNK) { +			processed_pfn = HA_CHUNK; +			total_pfn -= HA_CHUNK; +		} else { +			processed_pfn = total_pfn; +			total_pfn = 0; +		} + +		has->covered_end_pfn +=  processed_pfn; + +		init_completion(&dm_device.ol_waitevent); +		dm_device.ha_waiting = true; + +		nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); +		ret = add_memory(nid, PFN_PHYS((start_pfn)), +				(HA_CHUNK << PAGE_SHIFT)); + +		if (ret) { +			pr_info("hot_add memory failed error is %d\n", ret); +			has->ha_end_pfn -= HA_CHUNK; +			has->covered_end_pfn -=  processed_pfn; +			break; +		} + +		/* +		 * Wait for the memory block to be onlined. +		 */ +		t = wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ); +		if (t == 0) { +			pr_info("hot_add memory timedout\n"); +			has->ha_end_pfn -= HA_CHUNK; +			has->covered_end_pfn -=  processed_pfn; +			break; +		} + +	} + +	return; +} + +static void hv_online_page(struct page *pg) +{ +	struct list_head *cur; +	struct hv_hotadd_state *has; +	unsigned long cur_start_pgp; +	unsigned long cur_end_pgp; + +	if (dm_device.ha_waiting) { +		dm_device.ha_waiting = false; +		complete(&dm_device.ol_waitevent); +	} + +	list_for_each(cur, &dm_device.ha_region_list) { +		has = list_entry(cur, struct hv_hotadd_state, list); +		cur_start_pgp = (unsigned long) +				pfn_to_page(has->covered_start_pfn); +		cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + +		if (((unsigned long)pg >= cur_start_pgp) && +			((unsigned long)pg < cur_end_pgp)) { +			/* +			 * This frame is currently backed; online the +			 * page. +			 */ +			__online_page_set_limits(pg); +			__online_page_increment_counters(pg); +			__online_page_free(pg); +			has->covered_start_pfn++; +		} +	} +} + +static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) +{ +	struct list_head *cur; +	struct hv_hotadd_state *has; +	unsigned long residual, new_inc; + +	if (list_empty(&dm_device.ha_region_list)) +		return false; + +	list_for_each(cur, &dm_device.ha_region_list) { +		has = list_entry(cur, struct hv_hotadd_state, list); + +		/* +		 * If the pfn range we are dealing with is not in the current +		 * "hot add block", move on. +		 */ +		if ((start_pfn >= has->end_pfn)) +			continue; +		/* +		 * If the current hot add-request extends beyond +		 * our current limit; extend it. +		 */ +		if ((start_pfn + pfn_cnt) > has->end_pfn) { +			residual = (start_pfn + pfn_cnt - has->end_pfn); +			/* +			 * Extend the region by multiples of HA_CHUNK. +			 */ +			new_inc = (residual / HA_CHUNK) * HA_CHUNK; +			if (residual % HA_CHUNK) +				new_inc += HA_CHUNK; + +			has->end_pfn += new_inc; +		} + +		/* +		 * If the current start pfn is not where the covered_end +		 * is, update it. +		 */ + +		if (has->covered_end_pfn != start_pfn) { +			has->covered_end_pfn = start_pfn; +			has->covered_start_pfn = start_pfn; +		} +		return true; + +	} -	if (do_hot_add) { +	return false; +} + +static unsigned long handle_pg_range(unsigned long pg_start, +					unsigned long pg_count) +{ +	unsigned long start_pfn = pg_start; +	unsigned long pfn_cnt = pg_count; +	unsigned long size; +	struct list_head *cur; +	struct hv_hotadd_state *has; +	unsigned long pgs_ol = 0; +	unsigned long old_covered_state; + +	if (list_empty(&dm_device.ha_region_list)) +		return 0; -		pr_info("Memory hot add not supported\n"); +	list_for_each(cur, &dm_device.ha_region_list) { +		has = list_entry(cur, struct hv_hotadd_state, list);  		/* -		 * Currently we do not support hot add. -		 * Just fail the request. +		 * If the pfn range we are dealing with is not in the current +		 * "hot add block", move on.  		 */ +		if ((start_pfn >= has->end_pfn)) +			continue; + +		old_covered_state = has->covered_end_pfn; + +		if (start_pfn < has->ha_end_pfn) { +			/* +			 * This is the case where we are backing pages +			 * in an already hot added region. Bring +			 * these pages online first. +			 */ +			pgs_ol = has->ha_end_pfn - start_pfn; +			if (pgs_ol > pfn_cnt) +				pgs_ol = pfn_cnt; +			hv_bring_pgs_online(start_pfn, pgs_ol); +			has->covered_end_pfn +=  pgs_ol; +			has->covered_start_pfn +=  pgs_ol; +			pfn_cnt -= pgs_ol; +		} + +		if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) { +			/* +			 * We have some residual hot add range +			 * that needs to be hot added; hot add +			 * it now. Hot add a multiple of +			 * of HA_CHUNK that fully covers the pages +			 * we have. +			 */ +			size = (has->end_pfn - has->ha_end_pfn); +			if (pfn_cnt <= size) { +				size = ((pfn_cnt / HA_CHUNK) * HA_CHUNK); +				if (pfn_cnt % HA_CHUNK) +					size += HA_CHUNK; +			} else { +				pfn_cnt = size; +			} +			hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has); +		} +		/* +		 * If we managed to online any pages that were given to us, +		 * we declare success. +		 */ +		return has->covered_end_pfn - old_covered_state; +  	} +	return 0; +} + +static unsigned long process_hot_add(unsigned long pg_start, +					unsigned long pfn_cnt, +					unsigned long rg_start, +					unsigned long rg_size) +{ +	struct hv_hotadd_state *ha_region = NULL; + +	if (pfn_cnt == 0) +		return 0; + +	if (!dm_device.host_specified_ha_region) +		if (pfn_covered(pg_start, pfn_cnt)) +			goto do_pg_range; + +	/* +	 * If the host has specified a hot-add range; deal with it first. +	 */ + +	if ((rg_size != 0) && (!dm_device.host_specified_ha_region)) { +		ha_region = kzalloc(sizeof(struct hv_hotadd_state), GFP_KERNEL); +		if (!ha_region) +			return 0; + +		INIT_LIST_HEAD(&ha_region->list); + +		list_add_tail(&ha_region->list, &dm_device.ha_region_list); +		ha_region->start_pfn = rg_start; +		ha_region->ha_end_pfn = rg_start; +		ha_region->covered_start_pfn = pg_start; +		ha_region->covered_end_pfn = pg_start; +		ha_region->end_pfn = rg_start + rg_size; +	} + +do_pg_range: +	/* +	 * Process the page range specified; bringing them +	 * online if possible. +	 */ +	return handle_pg_range(pg_start, pfn_cnt); +} + +#endif + +static void hot_add_req(struct work_struct *dummy) +{ +	struct dm_hot_add_response resp; +#ifdef CONFIG_MEMORY_HOTPLUG +	unsigned long pg_start, pfn_cnt; +	unsigned long rg_start, rg_sz; +#endif +	struct hv_dynmem_device *dm = &dm_device; +  	memset(&resp, 0, sizeof(struct dm_hot_add_response));  	resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE;  	resp.hdr.size = sizeof(struct dm_hot_add_response);  	resp.hdr.trans_id = atomic_inc_return(&trans_id); -	resp.page_count = 0; -	resp.result = 0; +#ifdef CONFIG_MEMORY_HOTPLUG +	pg_start = dm->ha_wrk.ha_page_range.finfo.start_page; +	pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt; + +	rg_start = dm->ha_wrk.ha_region_range.finfo.start_page; +	rg_sz = dm->ha_wrk.ha_region_range.finfo.page_cnt; + +	if ((rg_start == 0) && (!dm->host_specified_ha_region)) { +		unsigned long region_size; +		unsigned long region_start; + +		/* +		 * The host has not specified the hot-add region. +		 * Based on the hot-add page range being specified, +		 * compute a hot-add region that can cover the pages +		 * that need to be hot-added while ensuring the alignment +		 * and size requirements of Linux as it relates to hot-add. +		 */ +		region_start = pg_start; +		region_size = (pfn_cnt / HA_CHUNK) * HA_CHUNK; +		if (pfn_cnt % HA_CHUNK) +			region_size += HA_CHUNK; + +		region_start = (pg_start / HA_CHUNK) * HA_CHUNK; + +		rg_start = region_start; +		rg_sz = region_size; +	} + +	resp.page_count = process_hot_add(pg_start, pfn_cnt, +					rg_start, rg_sz); +#endif +	if (resp.page_count > 0) +		resp.result = 1; +	else +		resp.result = 0; + +	if (!do_hot_add || (resp.page_count == 0)) +		pr_info("Memory hot add failed\n");  	dm->state = DM_INITIALIZED;  	vmbus_sendpacket(dm->dev->channel, &resp,  			sizeof(struct dm_hot_add_response),  			(unsigned long)NULL,  			VM_PKT_DATA_INBAND, 0); -  }  static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) @@ -523,7 +876,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)  	}  } -unsigned long compute_balloon_floor(void) +static unsigned long compute_balloon_floor(void)  {  	unsigned long min_pages;  #define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) @@ -657,9 +1010,9 @@ static int  alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages, -static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req) +static void balloon_up(struct work_struct *dummy)  { -	int num_pages = req->num_pages; +	int num_pages = dm_device.balloon_wrk.num_pages;  	int num_ballooned = 0;  	struct dm_balloon_response *bl_resp;  	int alloc_unit; @@ -684,14 +1037,14 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)  		num_pages -= num_ballooned; -		num_ballooned = alloc_balloon_pages(dm, num_pages, +		num_ballooned = alloc_balloon_pages(&dm_device, num_pages,  						bl_resp, alloc_unit,  						 &alloc_error);  		if ((alloc_error) || (num_ballooned == num_pages)) {  			bl_resp->more_pages = 0;  			done = true; -			dm->state = DM_INITIALIZED; +			dm_device.state = DM_INITIALIZED;  		}  		/* @@ -719,7 +1072,7 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)  			pr_info("Balloon response failed\n");  			for (i = 0; i < bl_resp->range_count; i++) -				free_balloon_pages(dm, +				free_balloon_pages(&dm_device,  						 &bl_resp->range_array[i]);  			done = true; @@ -761,7 +1114,6 @@ static int dm_thread_func(void *dm_dev)  {  	struct hv_dynmem_device *dm = dm_dev;  	int t; -	unsigned long  scan_start;  	while (!kthread_should_stop()) {  		t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ); @@ -773,22 +1125,6 @@ static int dm_thread_func(void *dm_dev)  		if (t == 0)  			post_status(dm); -		scan_start = jiffies; -		switch (dm->state) { -		case DM_BALLOON_UP: -			balloon_up(dm, (struct dm_balloon *)recv_buffer); -			break; - -		case DM_HOT_ADD: -			hot_add_req(dm, (struct dm_hot_add *)recv_buffer); -			break; -		default: -			break; -		} - -		if (!time_in_range(jiffies, scan_start, scan_start + HZ)) -			post_status(dm); -  	}  	return 0; @@ -861,6 +1197,10 @@ static void balloon_onchannelcallback(void *context)  	struct dm_message *dm_msg;  	struct dm_header *dm_hdr;  	struct hv_dynmem_device *dm = hv_get_drvdata(dev); +	struct dm_balloon *bal_msg; +	struct dm_hot_add *ha_msg; +	union dm_mem_page_range *ha_pg_range; +	union dm_mem_page_range *ha_region;  	memset(recv_buffer, 0, sizeof(recv_buffer));  	vmbus_recvpacket(dev->channel, recv_buffer, @@ -882,8 +1222,12 @@ static void balloon_onchannelcallback(void *context)  			break;  		case DM_BALLOON_REQUEST: +			if (dm->state == DM_BALLOON_UP) +				pr_warn("Currently ballooning\n"); +			bal_msg = (struct dm_balloon *)recv_buffer;  			dm->state = DM_BALLOON_UP; -			complete(&dm->config_event); +			dm_device.balloon_wrk.num_pages = bal_msg->num_pages; +			schedule_work(&dm_device.balloon_wrk.wrk);  			break;  		case DM_UNBALLOON_REQUEST: @@ -893,8 +1237,31 @@ static void balloon_onchannelcallback(void *context)  			break;  		case DM_MEM_HOT_ADD_REQUEST: +			if (dm->state == DM_HOT_ADD) +				pr_warn("Currently hot-adding\n");  			dm->state = DM_HOT_ADD; -			complete(&dm->config_event); +			ha_msg = (struct dm_hot_add *)recv_buffer; +			if (ha_msg->hdr.size == sizeof(struct dm_hot_add)) { +				/* +				 * This is a normal hot-add request specifying +				 * hot-add memory. +				 */ +				ha_pg_range = &ha_msg->range; +				dm->ha_wrk.ha_page_range = *ha_pg_range; +				dm->ha_wrk.ha_region_range.page_range = 0; +			} else { +				/* +				 * Host is specifying that we first hot-add +				 * a region and then partially populate this +				 * region. +				 */ +				dm->host_specified_ha_region = true; +				ha_pg_range = &ha_msg->range; +				ha_region = &ha_pg_range[1]; +				dm->ha_wrk.ha_page_range = *ha_pg_range; +				dm->ha_wrk.ha_region_range = *ha_region; +			} +			schedule_work(&dm_device.ha_wrk.wrk);  			break;  		case DM_INFO_MESSAGE: @@ -937,6 +1304,10 @@ static int balloon_probe(struct hv_device *dev,  	dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;  	init_completion(&dm_device.host_event);  	init_completion(&dm_device.config_event); +	INIT_LIST_HEAD(&dm_device.ha_region_list); +	INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up); +	INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req); +	dm_device.host_specified_ha_region = false;  	dm_device.thread =  		 kthread_run(dm_thread_func, &dm_device, "hv_balloon"); @@ -945,6 +1316,10 @@ static int balloon_probe(struct hv_device *dev,  		goto probe_error1;  	} +#ifdef CONFIG_MEMORY_HOTPLUG +	set_online_page_callback(&hv_online_page); +#endif +  	hv_set_drvdata(dev, &dm_device);  	/*  	 * Initiate the hand shake with the host and negotiate @@ -962,8 +1337,7 @@ static int balloon_probe(struct hv_device *dev,  	ret = vmbus_sendpacket(dev->channel, &version_req,  				sizeof(struct dm_version_request),  				(unsigned long)NULL, -				VM_PKT_DATA_INBAND, -				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +				VM_PKT_DATA_INBAND, 0);  	if (ret)  		goto probe_error2; @@ -990,12 +1364,6 @@ static int balloon_probe(struct hv_device *dev,  	cap_msg.hdr.trans_id = atomic_inc_return(&trans_id);  	cap_msg.caps.cap_bits.balloon = 1; -	/* -	 * While we currently don't support hot-add, -	 * we still advertise this capability since the -	 * host requires that guests partcipating in the -	 * dynamic memory protocol support hot add. -	 */  	cap_msg.caps.cap_bits.hot_add = 1;  	/* @@ -1009,8 +1377,7 @@ static int balloon_probe(struct hv_device *dev,  	ret = vmbus_sendpacket(dev->channel, &cap_msg,  				sizeof(struct dm_capabilities),  				(unsigned long)NULL, -				VM_PKT_DATA_INBAND, -				VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +				VM_PKT_DATA_INBAND, 0);  	if (ret)  		goto probe_error2; @@ -1034,6 +1401,9 @@ static int balloon_probe(struct hv_device *dev,  	return 0;  probe_error2: +#ifdef CONFIG_MEMORY_HOTPLUG +	restore_online_page_callback(&hv_online_page); +#endif  	kthread_stop(dm_device.thread);  probe_error1: @@ -1046,13 +1416,26 @@ probe_error0:  static int balloon_remove(struct hv_device *dev)  {  	struct hv_dynmem_device *dm = hv_get_drvdata(dev); +	struct list_head *cur, *tmp; +	struct hv_hotadd_state *has;  	if (dm->num_pages_ballooned != 0)  		pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); +	cancel_work_sync(&dm->balloon_wrk.wrk); +	cancel_work_sync(&dm->ha_wrk.wrk); +  	vmbus_close(dev->channel);  	kthread_stop(dm->thread);  	kfree(send_buffer); +#ifdef CONFIG_MEMORY_HOTPLUG +	restore_online_page_callback(&hv_online_page); +#endif +	list_for_each_safe(cur, tmp, &dm->ha_region_list) { +		has = list_entry(cur, struct hv_hotadd_state, list); +		list_del(&has->list); +		kfree(has); +	}  	return 0;  } @@ -1079,14 +1462,7 @@ static int __init init_balloon_drv(void)  	return vmbus_driver_register(&balloon_drv);  } -static void exit_balloon_drv(void) -{ - -	vmbus_driver_unregister(&balloon_drv); -} -  module_init(init_balloon_drv); -module_exit(exit_balloon_drv);  MODULE_DESCRIPTION("Hyper-V Balloon");  MODULE_VERSION(HV_DRV_VERSION); diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c new file mode 100644 index 00000000000..8ad5653ce44 --- /dev/null +++ b/drivers/hv/hv_snapshot.c @@ -0,0 +1,287 @@ +/* + * An implementation of host initiated guest snapshot. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan <kys@microsoft.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT.  See the GNU General Public License for more + * details. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/net.h> +#include <linux/nls.h> +#include <linux/connector.h> +#include <linux/workqueue.h> +#include <linux/hyperv.h> + + + +/* + * Global state maintained for transaction that is being processed. + * Note that only one transaction can be active at any point in time. + * + * This state is set when we receive a request from the host; we + * cleanup this state when the transaction is completed - when we respond + * to the host with the key value. + */ + +static struct { +	bool active; /* transaction status - active or not */ +	int recv_len; /* number of bytes received. */ +	struct vmbus_channel *recv_channel; /* chn we got the request */ +	u64 recv_req_id; /* request ID. */ +	struct hv_vss_msg  *msg; /* current message */ +} vss_transaction; + + +static void vss_respond_to_host(int error); + +static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL }; +static const char vss_name[] = "vss_kernel_module"; +static __u8 *recv_buffer; + +static void vss_send_op(struct work_struct *dummy); +static DECLARE_WORK(vss_send_op_work, vss_send_op); + +/* + * Callback when data is received from user mode. + */ + +static void +vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ +	struct hv_vss_msg *vss_msg; + +	vss_msg = (struct hv_vss_msg *)msg->data; + +	if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) { +		pr_info("VSS daemon registered\n"); +		vss_transaction.active = false; +		if (vss_transaction.recv_channel != NULL) +			hv_vss_onchannelcallback(vss_transaction.recv_channel); +		return; + +	} +	vss_respond_to_host(vss_msg->error); +} + + +static void vss_send_op(struct work_struct *dummy) +{ +	int op = vss_transaction.msg->vss_hdr.operation; +	struct cn_msg *msg; +	struct hv_vss_msg *vss_msg; + +	msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC); +	if (!msg) +		return; + +	vss_msg = (struct hv_vss_msg *)msg->data; + +	msg->id.idx =  CN_VSS_IDX; +	msg->id.val = CN_VSS_VAL; + +	vss_msg->vss_hdr.operation = op; +	msg->len = sizeof(struct hv_vss_msg); + +	cn_netlink_send(msg, 0, GFP_ATOMIC); +	kfree(msg); + +	return; +} + +/* + * Send a response back to the host. + */ + +static void +vss_respond_to_host(int error) +{ +	struct icmsg_hdr *icmsghdrp; +	u32	buf_len; +	struct vmbus_channel *channel; +	u64	req_id; + +	/* +	 * If a transaction is not active; log and return. +	 */ + +	if (!vss_transaction.active) { +		/* +		 * This is a spurious call! +		 */ +		pr_warn("VSS: Transaction not active\n"); +		return; +	} +	/* +	 * Copy the global state for completing the transaction. Note that +	 * only one transaction can be active at a time. +	 */ + +	buf_len = vss_transaction.recv_len; +	channel = vss_transaction.recv_channel; +	req_id = vss_transaction.recv_req_id; +	vss_transaction.active = false; + +	icmsghdrp = (struct icmsg_hdr *) +			&recv_buffer[sizeof(struct vmbuspipe_hdr)]; + +	if (channel->onchannel_callback == NULL) +		/* +		 * We have raced with util driver being unloaded; +		 * silently return. +		 */ +		return; + +	icmsghdrp->status = error; + +	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + +	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, +				VM_PKT_DATA_INBAND, 0); + +} + +/* + * This callback is invoked when we get a VSS message from the host. + * The host ensures that only one VSS transaction can be active at a time. + */ + +void hv_vss_onchannelcallback(void *context) +{ +	struct vmbus_channel *channel = context; +	u32 recvlen; +	u64 requestid; +	struct hv_vss_msg *vss_msg; + + +	struct icmsg_hdr *icmsghdrp; +	struct icmsg_negotiate *negop = NULL; + +	if (vss_transaction.active) { +		/* +		 * We will defer processing this callback once +		 * the current transaction is complete. +		 */ +		vss_transaction.recv_channel = channel; +		return; +	} + +	vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, +			 &requestid); + +	if (recvlen > 0) { +		icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ +			sizeof(struct vmbuspipe_hdr)]; + +		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { +			vmbus_prep_negotiate_resp(icmsghdrp, negop, +				 recv_buffer, MAX_SRV_VER, MAX_SRV_VER); +			/* +			 * We currently negotiate the highest number the +			 * host has presented. If this version is not +			 * atleast 5.0, reject. +			 */ +			negop = (struct icmsg_negotiate *)&recv_buffer[ +				sizeof(struct vmbuspipe_hdr) + +				sizeof(struct icmsg_hdr)]; + +			if (negop->icversion_data[1].major < 5) +				negop->icframe_vercnt = 0; +		} else { +			vss_msg = (struct hv_vss_msg *)&recv_buffer[ +				sizeof(struct vmbuspipe_hdr) + +				sizeof(struct icmsg_hdr)]; + +			/* +			 * Stash away this global state for completing the +			 * transaction; note transactions are serialized. +			 */ + +			vss_transaction.recv_len = recvlen; +			vss_transaction.recv_channel = channel; +			vss_transaction.recv_req_id = requestid; +			vss_transaction.active = true; +			vss_transaction.msg = (struct hv_vss_msg *)vss_msg; + +			switch (vss_msg->vss_hdr.operation) { +				/* +				 * Initiate a "freeze/thaw" +				 * operation in the guest. +				 * We respond to the host once +				 * the operation is complete. +				 * +				 * We send the message to the +				 * user space daemon and the +				 * operation is performed in +				 * the daemon. +				 */ +			case VSS_OP_FREEZE: +			case VSS_OP_THAW: +				schedule_work(&vss_send_op_work); +				return; + +			case VSS_OP_HOT_BACKUP: +				vss_msg->vss_cf.flags = +					 VSS_HBU_NO_AUTO_RECOVERY; +				vss_respond_to_host(0); +				return; + +			case VSS_OP_GET_DM_INFO: +				vss_msg->dm_info.flags = 0; +				vss_respond_to_host(0); +				return; + +			default: +				vss_respond_to_host(0); +				return; + +			} + +		} + +		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION +			| ICMSGHDRFLAG_RESPONSE; + +		vmbus_sendpacket(channel, recv_buffer, +				       recvlen, requestid, +				       VM_PKT_DATA_INBAND, 0); +	} + +} + +int +hv_vss_init(struct hv_util_service *srv) +{ +	int err; + +	err = cn_add_callback(&vss_id, vss_name, vss_cn_callback); +	if (err) +		return err; +	recv_buffer = srv->recv_buffer; + +	/* +	 * When this driver loads, the user level daemon that +	 * processes the host requests may not yet be running. +	 * Defer processing channel callbacks until the daemon +	 * has registered. +	 */ +	vss_transaction.active = true; +	return 0; +} + +void hv_vss_deinit(void) +{ +	cn_del_callback(&vss_id); +	cancel_work_sync(&vss_send_op_work); +} diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 1d4cbd8e826..2f561c5dfe2 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -49,6 +49,12 @@ static struct hv_util_service util_kvp = {  	.util_deinit = hv_kvp_deinit,  }; +static struct hv_util_service util_vss = { +	.util_cb = hv_vss_onchannelcallback, +	.util_init = hv_vss_init, +	.util_deinit = hv_vss_deinit, +}; +  static void perform_shutdown(struct work_struct *dummy)  {  	orderly_poweroff(true); @@ -339,6 +345,10 @@ static const struct hv_vmbus_device_id id_table[] = {  	{ HV_KVP_GUID,  	  .driver_data = (unsigned long)&util_kvp  	}, +	/* VSS GUID */ +	{ HV_VSS_GUID, +	  .driver_data = (unsigned long)&util_vss +	},  	{ },  }; diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index 0246b1fddff..c276fde318e 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c @@ -480,6 +480,7 @@ static void tpci200_release_device(struct ipack_device *dev)  static int tpci200_create_device(struct tpci200_board *tpci200, int i)  { +	int ret;  	enum ipack_space space;  	struct ipack_device *dev =  		kzalloc(sizeof(struct ipack_device), GFP_KERNEL); @@ -495,7 +496,18 @@ static int tpci200_create_device(struct tpci200_board *tpci200, int i)  			+ tpci200_space_interval[space] * i;  		dev->region[space].size = tpci200_space_size[space];  	} -	return ipack_device_register(dev); + +	ret = ipack_device_init(dev); +	if (ret < 0) { +		ipack_put_device(dev); +		return ret; +	} + +	ret = ipack_device_add(dev); +	if (ret < 0) +		ipack_put_device(dev); + +	return ret;  }  static int tpci200_pci_probe(struct pci_dev *pdev, diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index 7ec6b208b1c..6e066c53acc 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -227,7 +227,7 @@ static int ipack_unregister_bus_member(struct device *dev, void *data)  	struct ipack_bus_device *bus = data;  	if (idev->bus == bus) -		ipack_device_unregister(idev); +		ipack_device_del(idev);  	return 1;  } @@ -419,7 +419,7 @@ out:  	return ret;  } -int ipack_device_register(struct ipack_device *dev) +int ipack_device_init(struct ipack_device *dev)  {  	int ret; @@ -428,6 +428,7 @@ int ipack_device_register(struct ipack_device *dev)  	dev->dev.parent = dev->bus->parent;  	dev_set_name(&dev->dev,  		     "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); +	device_initialize(&dev->dev);  	if (dev->bus->ops->set_clockrate(dev, 8))  		dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n"); @@ -447,19 +448,34 @@ int ipack_device_register(struct ipack_device *dev)  			dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n");  	} -	ret = device_register(&dev->dev); -	if (ret < 0) -		kfree(dev->id); +	return 0; +} +EXPORT_SYMBOL_GPL(ipack_device_init); -	return ret; +int ipack_device_add(struct ipack_device *dev) +{ +	return device_add(&dev->dev); +} +EXPORT_SYMBOL_GPL(ipack_device_add); + +void ipack_device_del(struct ipack_device *dev) +{ +	device_del(&dev->dev); +	ipack_put_device(dev); +} +EXPORT_SYMBOL_GPL(ipack_device_del); + +void ipack_get_device(struct ipack_device *dev) +{ +	get_device(&dev->dev);  } -EXPORT_SYMBOL_GPL(ipack_device_register); +EXPORT_SYMBOL_GPL(ipack_get_device); -void ipack_device_unregister(struct ipack_device *dev) +void ipack_put_device(struct ipack_device *dev)  { -	device_unregister(&dev->dev); +	put_device(&dev->dev);  } -EXPORT_SYMBOL_GPL(ipack_device_unregister); +EXPORT_SYMBOL_GPL(ipack_put_device);  static int __init ipack_init(void)  { diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index c21353d8e91..62b8030ee33 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -163,16 +163,4 @@ static struct pcmcia_driver avmcs_driver = {  	.remove	= avmcs_detach,  	.id_table = avmcs_ids,  }; - -static int __init avmcs_init(void) -{ -	return pcmcia_register_driver(&avmcs_driver); -} - -static void __exit avmcs_exit(void) -{ -	pcmcia_unregister_driver(&avmcs_driver); -} - -module_init(avmcs_init); -module_exit(avmcs_exit); +module_pcmcia_driver(avmcs_driver); diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index 4e676bcf850..baad94ec1f4 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -159,16 +159,4 @@ static struct pcmcia_driver avma1cs_driver = {  	.remove		= avma1cs_detach,  	.id_table	= avma1cs_ids,  }; - -static int __init init_avma1_cs(void) -{ -	return pcmcia_register_driver(&avma1cs_driver); -} - -static void __exit exit_avma1_cs(void) -{ -	pcmcia_unregister_driver(&avma1cs_driver); -} - -module_init(init_avma1_cs); -module_exit(exit_avma1_cs); +module_pcmcia_driver(avma1cs_driver); diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index ebe56918f6f..40f6fad79de 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -215,16 +215,4 @@ static struct pcmcia_driver elsa_cs_driver = {  	.suspend	= elsa_suspend,  	.resume		= elsa_resume,  }; - -static int __init init_elsa_cs(void) -{ -	return pcmcia_register_driver(&elsa_cs_driver); -} - -static void __exit exit_elsa_cs(void) -{ -	pcmcia_unregister_driver(&elsa_cs_driver); -} - -module_init(init_elsa_cs); -module_exit(exit_elsa_cs); +module_pcmcia_driver(elsa_cs_driver); diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index 90f81291641..92ef62d4caf 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -206,16 +206,4 @@ static struct pcmcia_driver sedlbauer_driver = {  	.suspend	= sedlbauer_suspend,  	.resume		= sedlbauer_resume,  }; - -static int __init init_sedlbauer_cs(void) -{ -	return pcmcia_register_driver(&sedlbauer_driver); -} - -static void __exit exit_sedlbauer_cs(void) -{ -	pcmcia_unregister_driver(&sedlbauer_driver); -} - -module_init(init_sedlbauer_cs); -module_exit(exit_sedlbauer_cs); +module_pcmcia_driver(sedlbauer_driver); diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index f2476ffb04f..b8dd1495875 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -197,16 +197,4 @@ static struct pcmcia_driver teles_cs_driver = {  	.suspend	= teles_suspend,  	.resume		= teles_resume,  }; - -static int __init init_teles_cs(void) -{ -	return pcmcia_register_driver(&teles_cs_driver); -} - -static void __exit exit_teles_cs(void) -{ -	pcmcia_unregister_driver(&teles_cs_driver); -} - -module_init(init_teles_cs); -module_exit(exit_teles_cs); +module_pcmcia_driver(teles_cs_driver); diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index df087369485..cadf1cc19aa 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -25,6 +25,7 @@  #include <linux/module.h>  #include <linux/list.h>  #include <linux/spinlock.h> +#include <linux/pm.h>  #include <memory/jedec_ddr.h>  #include "emif.h"  #include "of_memory.h" @@ -256,6 +257,41 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode)  	u32 temp;  	void __iomem *base = emif->base; +	/* +	 * Workaround for errata i743 - LPDDR2 Power-Down State is Not +	 * Efficient +	 * +	 * i743 DESCRIPTION: +	 * The EMIF supports power-down state for low power. The EMIF +	 * automatically puts the SDRAM into power-down after the memory is +	 * not accessed for a defined number of cycles and the +	 * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set to 0x4. +	 * As the EMIF supports automatic output impedance calibration, a ZQ +	 * calibration long command is issued every time it exits active +	 * power-down and precharge power-down modes. The EMIF waits and +	 * blocks any other command during this calibration. +	 * The EMIF does not allow selective disabling of ZQ calibration upon +	 * exit of power-down mode. Due to very short periods of power-down +	 * cycles, ZQ calibration overhead creates bandwidth issues and +	 * increases overall system power consumption. On the other hand, +	 * issuing ZQ calibration long commands when exiting self-refresh is +	 * still required. +	 * +	 * WORKAROUND +	 * Because there is no power consumption benefit of the power-down due +	 * to the calibration and there is a performance risk, the guideline +	 * is to not allow power-down state and, therefore, to not have set +	 * the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4. +	 */ +	if ((emif->plat_data->ip_rev == EMIF_4D) && +	    (EMIF_LP_MODE_PWR_DN == lpmode)) { +		WARN_ONCE(1, +			  "REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by" +			  "erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n"); +		/* rollback LP_MODE to Self-refresh mode */ +		lpmode = EMIF_LP_MODE_SELF_REFRESH; +	} +  	temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL);  	temp &= ~LP_MODE_MASK;  	temp |= (lpmode << LP_MODE_SHIFT); @@ -715,6 +751,8 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)  	u32 timeout_perf	= EMIF_LP_MODE_TIMEOUT_PERFORMANCE;  	u32 timeout_pwr		= EMIF_LP_MODE_TIMEOUT_POWER;  	u32 freq_threshold	= EMIF_LP_MODE_FREQ_THRESHOLD; +	u32 mask; +	u8 shift;  	struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs; @@ -728,37 +766,59 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)  	/* Timeout based on DDR frequency */  	timeout = freq >= freq_threshold ? timeout_perf : timeout_pwr; -	/* The value to be set in register is "log2(timeout) - 3" */ +	/* +	 * The value to be set in register is "log2(timeout) - 3" +	 * if timeout < 16 load 0 in register +	 * if timeout is not a power of 2, round to next highest power of 2 +	 */  	if (timeout < 16) {  		timeout = 0;  	} else { -		timeout = __fls(timeout) - 3;  		if (timeout & (timeout - 1)) -			timeout++; +			timeout <<= 1; +		timeout = __fls(timeout) - 3;  	}  	switch (lpmode) {  	case EMIF_LP_MODE_CLOCK_STOP: -		pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) | -					SR_TIM_MASK | PD_TIM_MASK; +		shift = CS_TIM_SHIFT; +		mask = CS_TIM_MASK;  		break;  	case EMIF_LP_MODE_SELF_REFRESH:  		/* Workaround for errata i735 */  		if (timeout < 6)  			timeout = 6; -		pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) | -					CS_TIM_MASK | PD_TIM_MASK; +		shift = SR_TIM_SHIFT; +		mask = SR_TIM_MASK;  		break;  	case EMIF_LP_MODE_PWR_DN: -		pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) | -					CS_TIM_MASK | SR_TIM_MASK; +		shift = PD_TIM_SHIFT; +		mask = PD_TIM_MASK;  		break;  	case EMIF_LP_MODE_DISABLE:  	default: -		pwr_mgmt_ctrl = CS_TIM_MASK | -					PD_TIM_MASK | SR_TIM_MASK; +		mask = 0; +		shift = 0; +		break;  	} +	/* Round to maximum in case of overflow, BUT warn! */ +	if (lpmode != EMIF_LP_MODE_DISABLE && timeout > mask >> shift) { +		pr_err("TIMEOUT Overflow - lpmode=%d perf=%d pwr=%d freq=%d\n", +		       lpmode, +		       timeout_perf, +		       timeout_pwr, +		       freq_threshold); +		WARN(1, "timeout=0x%02x greater than 0x%02x. Using max\n", +		     timeout, mask >> shift); +		timeout = mask >> shift; +	} + +	/* Setup required timing */ +	pwr_mgmt_ctrl = (timeout << shift) & mask; +	/* setup a default mask for rest of the modes */ +	pwr_mgmt_ctrl |= (SR_TIM_MASK | CS_TIM_MASK | PD_TIM_MASK) & +			  ~mask;  	/* No CS_TIM in EMIF_4D5 */  	if (ip_rev == EMIF_4D5) @@ -815,6 +875,8 @@ static void setup_registers(struct emif_data *emif, struct emif_regs *regs)  	writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW);  	writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW); +	writel(regs->pwr_mgmt_ctrl_shdw, +	       base + EMIF_POWER_MANAGEMENT_CTRL_SHDW);  	/* Settings specific for EMIF4D5 */  	if (emif->plat_data->ip_rev != EMIF_4D5) @@ -892,6 +954,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)  {  	u32		old_temp_level;  	irqreturn_t	ret = IRQ_HANDLED; +	struct emif_custom_configs *custom_configs;  	spin_lock_irqsave(&emif_lock, irq_state);  	old_temp_level = emif->temperature_level; @@ -904,6 +967,29 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)  		goto out;  	} +	custom_configs = emif->plat_data->custom_configs; + +	/* +	 * IF we detect higher than "nominal rating" from DDR sensor +	 * on an unsupported DDR part, shutdown system +	 */ +	if (custom_configs && !(custom_configs->mask & +				EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) { +		if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) { +			dev_err(emif->dev, +				"%s:NOT Extended temperature capable memory." +				"Converting MR4=0x%02x as shutdown event\n", +				__func__, emif->temperature_level); +			/* +			 * Temperature far too high - do kernel_power_off() +			 * from thread context +			 */ +			emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN; +			ret = IRQ_WAKE_THREAD; +			goto out; +		} +	} +  	if (emif->temperature_level < old_temp_level ||  		emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {  		/* @@ -965,7 +1051,14 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id)  	if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {  		dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n"); -		kernel_power_off(); + +		/* If we have Power OFF ability, use it, else try restarting */ +		if (pm_power_off) { +			kernel_power_off(); +		} else { +			WARN(1, "FIXME: NO pm_power_off!!! trying restart\n"); +			kernel_restart("SDRAM Over-temp Emergency restart"); +		}  		return IRQ_HANDLED;  	} @@ -1170,7 +1263,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,  {  	struct emif_custom_configs	*cust_cfgs = NULL;  	int				len; -	const int			*lpmode, *poll_intvl; +	const __be32			*lpmode, *poll_intvl;  	lpmode = of_get_property(np_emif, "low-power-mode", &len);  	poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len); @@ -1184,7 +1277,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,  	if (lpmode) {  		cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE; -		cust_cfgs->lpmode = *lpmode; +		cust_cfgs->lpmode = be32_to_cpup(lpmode);  		of_property_read_u32(np_emif,  				"low-power-mode-timeout-performance",  				&cust_cfgs->lpmode_timeout_performance); @@ -1199,9 +1292,13 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,  	if (poll_intvl) {  		cust_cfgs->mask |=  				EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL; -		cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl; +		cust_cfgs->temp_alert_poll_interval_ms = +						be32_to_cpup(poll_intvl);  	} +	if (of_find_property(np_emif, "extended-temp-part", &len)) +		cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART; +  	if (!is_custom_config_valid(cust_cfgs, emif->dev)) {  		devm_kfree(emif->dev, cust_cfgs);  		return; @@ -1407,7 +1504,7 @@ static struct emif_data *__init_or_module get_device_details(  	if (pd->timings) {  		temp = devm_kzalloc(dev, size, GFP_KERNEL);  		if (temp) { -			memcpy(temp, pd->timings, sizeof(*pd->timings)); +			memcpy(temp, pd->timings, size);  			pd->timings = temp;  		} else {  			dev_warn(dev, "%s:%d: allocation error\n", __func__, @@ -1841,18 +1938,8 @@ static struct platform_driver emif_driver = {  	},  }; -static int __init_or_module emif_register(void) -{ -	return platform_driver_probe(&emif_driver, emif_probe); -} - -static void __exit emif_unregister(void) -{ -	platform_driver_unregister(&emif_driver); -} +module_platform_driver_probe(emif_driver, emif_probe); -module_init(emif_register); -module_exit(emif_unregister);  MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");  MODULE_LICENSE("GPL");  MODULE_ALIAS("platform:emif"); diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c index 0b975986777..f4ae074badc 100644 --- a/drivers/memory/tegra30-mc.c +++ b/drivers/memory/tegra30-mc.c @@ -268,6 +268,7 @@ static const u32 tegra30_mc_ctx[] = {  	MC_INTMASK,  }; +#ifdef CONFIG_PM  static int tegra30_mc_suspend(struct device *dev)  {  	int i; @@ -291,6 +292,7 @@ static int tegra30_mc_resume(struct device *dev)  	mc_readl(mc, MC_TIMING_CONTROL);  	return 0;  } +#endif  static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,  			    tegra30_mc_suspend, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c346941a251..72933c756c8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -991,7 +991,7 @@ config MFD_PM8XXX  config MFD_PM8921_CORE  	tristate "Qualcomm PM8921 PMIC chip" -	depends on MSM_SSBI +	depends on SSBI  	select MFD_CORE  	select MFD_PM8XXX  	help diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index d4b297cbd80..ecc137ffa8c 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -17,7 +17,7 @@  #include <linux/platform_device.h>  #include <linux/slab.h>  #include <linux/err.h> -#include <linux/msm_ssbi.h> +#include <linux/ssbi.h>  #include <linux/mfd/core.h>  #include <linux/mfd/pm8xxx/pm8921.h>  #include <linux/mfd/pm8xxx/core.h> @@ -35,7 +35,7 @@ static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)  	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);  	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; -	return msm_ssbi_read(pmic->dev->parent, addr, val, 1); +	return ssbi_read(pmic->dev->parent, addr, val, 1);  }  static int pm8921_writeb(const struct device *dev, u16 addr, u8 val) @@ -43,7 +43,7 @@ static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)  	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);  	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; -	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1); +	return ssbi_write(pmic->dev->parent, addr, &val, 1);  }  static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf, @@ -52,7 +52,7 @@ static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,  	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);  	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; -	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt); +	return ssbi_read(pmic->dev->parent, addr, buf, cnt);  }  static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf, @@ -61,7 +61,7 @@ static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,  	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);  	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data; -	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt); +	return ssbi_write(pmic->dev->parent, addr, buf, cnt);  }  static int pm8921_read_irq_stat(const struct device *dev, int irq) @@ -124,7 +124,7 @@ static int pm8921_probe(struct platform_device *pdev)  	}  	/* Read PMIC chip revision */ -	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); +	rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));  	if (rc) {  		pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);  		goto err_read_rev; @@ -133,7 +133,7 @@ static int pm8921_probe(struct platform_device *pdev)  	rev = val;  	/* Read PMIC chip revision 2 */ -	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val)); +	rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));  	if (rc) {  		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",  			REG_HWREV_2, rc); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e83fdfe0c8c..69bb79d6dd5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -93,6 +93,14 @@ config ATMEL_TCB_CLKSRC_BLOCK  	  TC can be used for other purposes, such as PWM generation and  	  interval timing. +config DUMMY_IRQ +	tristate "Dummy IRQ handler" +	default n +	---help--- +	  This module accepts a single 'irq' parameter, which it should register for. +	  The sole purpose of this module is to help with debugging of systems on +	  which spurious IRQs would happen on disabled IRQ vector. +  config IBM_ASM  	tristate "Device driver for IBM RSA service processor"  	depends on X86 && PCI && INPUT diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 35a1463c72d..865cbc6a7ae 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o  obj-$(CONFIG_BMP085)		+= bmp085.o  obj-$(CONFIG_BMP085_I2C)	+= bmp085-i2c.o  obj-$(CONFIG_BMP085_SPI)	+= bmp085-spi.o +obj-$(CONFIG_DUMMY_IRQ)		+= dummy-irq.o  obj-$(CONFIG_ICS932S401)	+= ics932s401.o  obj-$(CONFIG_LKDTM)		+= lkdtm.o  obj-$(CONFIG_TIFM_CORE)       	+= tifm_core.o @@ -49,6 +50,5 @@ obj-y				+= carma/  obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o  obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/  obj-$(CONFIG_INTEL_MEI)		+= mei/ -obj-$(CONFIG_MAX8997_MUIC)	+= max8997-muic.o  obj-$(CONFIG_VMWARE_VMCI)	+= vmw_vmci/  obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 0e67f8263cd..1efb6a4ea39 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -700,9 +700,6 @@ static ssize_t apds990x_lux_calib_store(struct device *dev,  	if (strict_strtoul(buf, 0, &value))  		return -EINVAL; -	if (chip->lux_calib > APDS_RANGE) -		return -EINVAL; -  	chip->lux_calib = value;  	return len; diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index fe8616a8d28..48651ef0028 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -378,18 +378,7 @@ static struct platform_driver charlcd_driver = {  	.remove = __exit_p(charlcd_remove),  }; -static int __init charlcd_init(void) -{ -	return platform_driver_probe(&charlcd_driver, charlcd_probe); -} - -static void __exit charlcd_exit(void) -{ -	platform_driver_unregister(&charlcd_driver); -} - -module_init(charlcd_init); -module_exit(charlcd_exit); +module_platform_driver_probe(charlcd_driver, charlcd_probe);  MODULE_AUTHOR("Linus Walleij <triad@df.lth.se>");  MODULE_DESCRIPTION("ARM Character LCD Driver"); diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 28f5aaa19d4..494d0500bda 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -393,17 +393,7 @@ static struct platform_driver atmel_pwm_driver = {  	 */  }; -static int __init pwm_init(void) -{ -	return platform_driver_probe(&atmel_pwm_driver, pwm_probe); -} -module_init(pwm_init); - -static void __exit pwm_exit(void) -{ -	platform_driver_unregister(&atmel_pwm_driver); -} -module_exit(pwm_exit); +module_platform_driver_probe(atmel_pwm_driver, pwm_probe);  MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module");  MODULE_LICENSE("GPL"); diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c new file mode 100644 index 00000000000..7014167e2c6 --- /dev/null +++ b/drivers/misc/dummy-irq.c @@ -0,0 +1,59 @@ +/* + * Dummy IRQ handler driver. + * + * This module only registers itself as a handler that is specified to it + * by the 'irq' parameter. + * + * The sole purpose of this module is to help with debugging of systems on + * which spurious IRQs would happen on disabled IRQ vector. + * + * Copyright (C) 2013 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +static int irq; + +static irqreturn_t dummy_interrupt(int irq, void *dev_id) +{ +	static int count = 0; + +	if (count == 0) { +		printk(KERN_INFO "dummy-irq: interrupt occured on IRQ %d\n", +				irq); +		count++; +	} + +	return IRQ_NONE; +} + +static int __init dummy_irq_init(void) +{ +	if (request_irq(irq, &dummy_interrupt, IRQF_SHARED, "dummy_irq", &irq)) { +		printk(KERN_ERR "dummy-irq: cannot register IRQ %d\n", irq); +		return -EIO; +	} +	printk(KERN_INFO "dummy-irq: registered for IRQ %d\n", irq); +	return 0; +} + +static void __exit dummy_irq_exit(void) +{ +	printk(KERN_INFO "dummy-irq unloaded\n"); +	free_irq(irq, &irq); +} + +module_init(dummy_irq_init); +module_exit(dummy_irq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jiri Kosina"); +module_param(irq, uint, 0444); +MODULE_PARM_DESC(irq, "The IRQ to register for"); diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 16d7179e2f9..96787ec15ca 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -365,18 +365,7 @@ static struct platform_driver ep93xx_pwm_driver = {  	.remove		= __exit_p(ep93xx_pwm_remove),  }; -static int __init ep93xx_pwm_init(void) -{ -	return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe); -} - -static void __exit ep93xx_pwm_exit(void) -{ -	platform_driver_unregister(&ep93xx_pwm_driver); -} - -module_init(ep93xx_pwm_init); -module_exit(ep93xx_pwm_exit); +module_platform_driver_probe(ep93xx_pwm_driver, ep93xx_pwm_probe);  MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "  	      "H Hartley Sweeten <hsweeten@visionengravers.com>"); diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c86d7e3839a..9a5e8c72628 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -449,7 +449,7 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,  	struct mei_msg_hdr mei_hdr;  	struct mei_cl *cl = cb->cl;  	size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; -	size_t msg_slots = mei_data2slots(len); +	u32 msg_slots = mei_data2slots(len);  	mei_hdr.host_addr = cl->host_client_id;  	mei_hdr.me_addr = cl->me_client_id; @@ -566,12 +566,13 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,   */  int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)  { +	u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); -	if (((*slots) * sizeof(u32)) < (sizeof(struct mei_msg_hdr) -			+ sizeof(struct hbm_flow_control))) { +	if (*slots < msg_slots)  		return -EMSGSIZE; -	} -	*slots -= mei_data2slots(sizeof(struct hbm_flow_control)); + +	*slots -= msg_slots; +  	if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) {  		dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");  		return -EIO; diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 45ea7185c00..11a2a6538c0 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -222,6 +222,38 @@ static bool mei_me_hw_is_ready(struct mei_device *dev)  	return (hw->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA;  } +static int mei_me_hw_ready_wait(struct mei_device *dev) +{ +	int err; +	if (mei_me_hw_is_ready(dev)) +		return 0; + +	mutex_unlock(&dev->device_lock); +	err = wait_event_interruptible_timeout(dev->wait_hw_ready, +			dev->recvd_hw_ready, MEI_INTEROP_TIMEOUT); +	mutex_lock(&dev->device_lock); +	if (!err && !dev->recvd_hw_ready) { +		dev_err(&dev->pdev->dev, +			"wait hw ready failed. status = 0x%x\n", err); +		return -ETIMEDOUT; +	} + +	dev->recvd_hw_ready = false; +	return 0; +} + +static int mei_me_hw_start(struct mei_device *dev) +{ +	int ret = mei_me_hw_ready_wait(dev); +	if (ret) +		return ret; +	dev_dbg(&dev->pdev->dev, "hw is ready\n"); + +	mei_me_host_set_ready(dev); +	return ret; +} + +  /**   * mei_hbuf_filled_slots - gets number of device filled buffer slots   * @@ -295,10 +327,11 @@ static int mei_me_write_message(struct mei_device *dev,  			unsigned char *buf)  {  	struct mei_me_hw *hw = to_me_hw(dev); -	unsigned long rem, dw_cnt; +	unsigned long rem;  	unsigned long length = header->length;  	u32 *reg_buf = (u32 *)buf;  	u32 hcsr; +	u32 dw_cnt;  	int i;  	int empty_slots; @@ -423,8 +456,6 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)  {  	struct mei_device *dev = (struct mei_device *) dev_id;  	struct mei_cl_cb complete_list; -	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; -	struct mei_cl *cl;  	s32 slots;  	int rets;  	bool  bus_message_received; @@ -455,14 +486,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)  		if (mei_hw_is_ready(dev)) {  			dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); -			mei_host_set_ready(dev); - -			dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); -			/* link is established * start sending messages.  */ +			dev->recvd_hw_ready = true; +			wake_up_interruptible(&dev->wait_hw_ready); -			dev->dev_state = MEI_DEV_INIT_CLIENTS; - -			mei_hbm_start_req(dev);  			mutex_unlock(&dev->device_lock);  			return IRQ_HANDLED;  		} else { @@ -499,33 +525,19 @@ end:  		wake_up_interruptible(&dev->wait_recvd_msg);  		bus_message_received = false;  	} -	if (list_empty(&complete_list.list)) -		return IRQ_HANDLED; +	mei_irq_compl_handler(dev, &complete_list); -	list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) { -		cl = cb_pos->cl; -		list_del(&cb_pos->list); -		if (cl) { -			if (cl != &dev->iamthif_cl) { -				dev_dbg(&dev->pdev->dev, "completing call back.\n"); -				mei_irq_complete_handler(cl, cb_pos); -				cb_pos = NULL; -			} else if (cl == &dev->iamthif_cl) { -				mei_amthif_complete(dev, cb_pos); -			} -		} -	}  	return IRQ_HANDLED;  }  static const struct mei_hw_ops mei_me_hw_ops = { -	.host_set_ready = mei_me_host_set_ready,  	.host_is_ready = mei_me_host_is_ready,  	.hw_is_ready = mei_me_hw_is_ready,  	.hw_reset = mei_me_hw_reset, -	.hw_config  = mei_me_hw_config, +	.hw_config = mei_me_hw_config, +	.hw_start = mei_me_hw_start,  	.intr_clear = mei_me_intr_clear,  	.intr_enable = mei_me_intr_enable, diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 8518d3eeb83..80bd829fbd9 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -36,12 +36,6 @@ struct mei_me_hw {  struct mei_device *mei_me_dev_init(struct pci_dev *pdev); -/* get slots (dwords) from a message length + header (bytes) */ -static inline unsigned char mei_data2slots(size_t length) -{ -	return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); -} -  irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);  irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6ec530168af..fc3d97ce830 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -22,6 +22,7 @@  #include <linux/mei.h>  #include "mei_dev.h" +#include "hbm.h"  #include "client.h"  const char *mei_dev_state_str(int state) @@ -47,6 +48,7 @@ void mei_device_init(struct mei_device *dev)  	/* setup our list array */  	INIT_LIST_HEAD(&dev->file_list);  	mutex_init(&dev->device_lock); +	init_waitqueue_head(&dev->wait_hw_ready);  	init_waitqueue_head(&dev->wait_recvd_msg);  	init_waitqueue_head(&dev->wait_stop_wd);  	dev->dev_state = MEI_DEV_INITIALIZING; @@ -176,6 +178,20 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)  		dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",  			 mei_dev_state_str(dev->dev_state)); +	if (!interrupts_enabled) { +		dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); +		return; +	} + +	mei_hw_start(dev); + +	dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); +	/* link is established * start sending messages.  */ + +	dev->dev_state = MEI_DEV_INIT_CLIENTS; + +	mei_hbm_start_req(dev); +  	/* wake up all readings so they can be interrupted */  	mei_cl_all_read_wakeup(dev); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3535b2676c9..73fbce3e774 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -30,21 +30,21 @@  /** - * mei_complete_handler - processes completed operation. + * mei_cl_complete_handler - processes completed operation for a client   *   * @cl: private data of the file object. - * @cb_pos: callback block. + * @cb: callback block.   */ -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) +static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb)  { -	if (cb_pos->fop_type == MEI_FOP_WRITE) { -		mei_io_cb_free(cb_pos); -		cb_pos = NULL; +	if (cb->fop_type == MEI_FOP_WRITE) { +		mei_io_cb_free(cb); +		cb = NULL;  		cl->writing_state = MEI_WRITE_COMPLETE;  		if (waitqueue_active(&cl->tx_wait))  			wake_up_interruptible(&cl->tx_wait); -	} else if (cb_pos->fop_type == MEI_FOP_READ && +	} else if (cb->fop_type == MEI_FOP_READ &&  			MEI_READING == cl->reading_state) {  		cl->reading_state = MEI_READ_COMPLETE;  		if (waitqueue_active(&cl->rx_wait)) @@ -54,6 +54,31 @@ void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos)  }  /** + * mei_irq_compl_handler - dispatch complete handelers + *	for the completed callbacks + * + * @dev - mei device + * @compl_list - list of completed cbs + */ +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +{ +	struct mei_cl_cb *cb, *next; +	struct mei_cl *cl; + +	list_for_each_entry_safe(cb, next, &compl_list->list, list) { +		cl = cb->cl; +		list_del(&cb->list); +		if (!cl) +			continue; + +		dev_dbg(&dev->pdev->dev, "completing call back.\n"); +		if (cl == &dev->iamthif_cl) +			mei_amthif_complete(dev, cb); +		else +			mei_cl_complete_handler(cl, cb); +	} +} +/**   * _mei_irq_thread_state_ok - checks if mei header matches file private data   *   * @cl: private data of the file object @@ -153,25 +178,27 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,  				struct mei_cl *cl,  				struct mei_cl_cb *cmpl_list)  { -	if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + -			sizeof(struct hbm_client_connect_request))) -		return -EBADMSG; +	u32 msg_slots = +		mei_data2slots(sizeof(struct hbm_client_connect_request)); + +	if (*slots < msg_slots) +		return -EMSGSIZE; -	*slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); +	*slots -= msg_slots;  	if (mei_hbm_cl_disconnect_req(dev, cl)) {  		cl->status = 0;  		cb_pos->buf_idx = 0;  		list_move_tail(&cb_pos->list, &cmpl_list->list); -		return -EMSGSIZE; -	} else { -		cl->state = MEI_FILE_DISCONNECTING; -		cl->status = 0; -		cb_pos->buf_idx = 0; -		list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); -		cl->timer_count = MEI_CONNECT_TIMEOUT; +		return -EIO;  	} +	cl->state = MEI_FILE_DISCONNECTING; +	cl->status = 0; +	cb_pos->buf_idx = 0; +	list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); +	cl->timer_count = MEI_CONNECT_TIMEOUT; +  	return 0;  } @@ -192,14 +219,15 @@ static int _mei_irq_thread_read(struct mei_device *dev,	s32 *slots,  			struct mei_cl *cl,  			struct mei_cl_cb *cmpl_list)  { -	if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + -			sizeof(struct hbm_flow_control))) { +	u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); + +	if (*slots < msg_slots) {  		/* return the cancel routine */  		list_del(&cb_pos->list); -		return -EBADMSG; +		return -EMSGSIZE;  	} -	*slots -= mei_data2slots(sizeof(struct hbm_flow_control)); +	*slots -= msg_slots;  	if (mei_hbm_cl_flow_control_req(dev, cl)) {  		cl->status = -ENODEV; @@ -229,15 +257,19 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,  			struct mei_cl *cl,  			struct mei_cl_cb *cmpl_list)  { -	if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + -			sizeof(struct hbm_client_connect_request))) { +	u32 msg_slots = +		mei_data2slots(sizeof(struct hbm_client_connect_request)); + +	if (*slots < msg_slots) {  		/* return the cancel routine */  		list_del(&cb_pos->list); -		return -EBADMSG; +		return -EMSGSIZE;  	} +	*slots -=  msg_slots; +  	cl->state = MEI_FILE_CONNECTING; -	*slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); +  	if (mei_hbm_cl_connect_req(dev, cl)) {  		cl->status = -ENODEV;  		cb_pos->buf_idx = 0; @@ -266,7 +298,7 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,  	struct mei_msg_hdr mei_hdr;  	struct mei_cl *cl = cb->cl;  	size_t len = cb->request_buffer.size - cb->buf_idx; -	size_t msg_slots = mei_data2slots(len); +	u32 msg_slots = mei_data2slots(len);  	mei_hdr.host_addr = cl->host_client_id;  	mei_hdr.me_addr = cl->me_client_id; @@ -419,8 +451,7 @@ end:   *   * returns 0 on success, <0 on failure.   */ -int mei_irq_write_handler(struct mei_device *dev, -				struct mei_cl_cb *cmpl_list) +int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)  {  	struct mei_cl *cl; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index cb80166161f..1a4b50ca4b3 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -213,11 +213,11 @@ struct mei_cl {  /** struct mei_hw_ops   * - * @host_set_ready   - notify FW that host side is ready   * @host_is_ready    - query for host readiness   * @hw_is_ready      - query if hw is ready   * @hw_reset         - reset hw + * @hw_start         - start hw after reset   * @hw_config        - configure hw   * @intr_clear       - clear pending interrupts @@ -237,11 +237,11 @@ struct mei_cl {   */  struct mei_hw_ops { -	void (*host_set_ready) (struct mei_device *dev);  	bool (*host_is_ready) (struct mei_device *dev);  	bool (*hw_is_ready) (struct mei_device *dev);  	void (*hw_reset) (struct mei_device *dev, bool enable); +	int  (*hw_start) (struct mei_device *dev);  	void (*hw_config) (struct mei_device *dev);  	void (*intr_clear) (struct mei_device *dev); @@ -296,11 +296,14 @@ struct mei_device {  	 */  	struct mutex device_lock; /* device lock */  	struct delayed_work timer_work;	/* MEI timer delayed work (timeouts) */ + +	bool recvd_hw_ready;  	bool recvd_msg;  	/*  	 * waiting queue for receive message from FW  	 */ +	wait_queue_head_t wait_hw_ready;  	wait_queue_head_t wait_recvd_msg;  	wait_queue_head_t wait_stop_wd; @@ -374,6 +377,17 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec)  	return msecs_to_jiffies(sec * MSEC_PER_SEC);  } +/** + * mei_data2slots - get slots - number of (dwords) from a message length + *	+ size of the mei header + * @length - size of the messages in bytes + * returns  - number of slots + */ +static inline u32 mei_data2slots(size_t length) +{ +	return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); +} +  /*   * mei init function prototypes @@ -391,8 +405,7 @@ int mei_irq_read_handler(struct mei_device *dev,  		struct mei_cl_cb *cmpl_list, s32 *slots);  int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); - -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos); +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);  /*   * AMTHIF - AMT Host Interface Functions @@ -454,6 +467,11 @@ static inline void mei_hw_reset(struct mei_device *dev, bool enable)  	dev->ops->hw_reset(dev, enable);  } +static inline void mei_hw_start(struct mei_device *dev) +{ +	dev->ops->hw_start(dev); +} +  static inline void mei_clear_interrupts(struct mei_device *dev)  {  	dev->ops->intr_clear(dev); @@ -469,10 +487,6 @@ static inline void mei_disable_interrupts(struct mei_device *dev)  	dev->ops->intr_disable(dev);  } -static inline void mei_host_set_ready(struct mei_device *dev) -{ -	dev->ops->host_set_ready(dev); -}  static inline bool mei_host_is_ready(struct mei_device *dev)  {  	return dev->ops->host_is_ready(dev); diff --git a/drivers/misc/vmw_vmci/Kconfig b/drivers/misc/vmw_vmci/Kconfig index 39c2ecadb27..ea98f7e9ccd 100644 --- a/drivers/misc/vmw_vmci/Kconfig +++ b/drivers/misc/vmw_vmci/Kconfig @@ -4,7 +4,7 @@  config VMWARE_VMCI  	tristate "VMware VMCI Driver" -	depends on X86 && PCI +	depends on X86 && PCI && NET  	help  	  This is VMware's Virtual Machine Communication Interface.  It enables  	  high-speed communication between host and guest in a virtual diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 7009f17ad6c..50adbd155f3 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -543,25 +543,7 @@ static struct pcmcia_driver sdricoh_driver = {  	.suspend = sdricoh_pcmcia_suspend,  	.resume = sdricoh_pcmcia_resume,  }; - -/*****************************************************************************\ - *                                                                           * - * Driver init/exit                                                          * - *                                                                           * -\*****************************************************************************/ - -static int __init sdricoh_drv_init(void) -{ -	return pcmcia_register_driver(&sdricoh_driver); -} - -static void __exit sdricoh_drv_exit(void) -{ -	pcmcia_unregister_driver(&sdricoh_driver); -} - -module_init(sdricoh_drv_init); -module_exit(sdricoh_drv_exit); +module_pcmcia_driver(sdricoh_driver);  module_param(switchlocked, uint, 0444); diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c index 5bed4c4e250..74dc1875f9c 100644 --- a/drivers/net/arcnet/com20020_cs.c +++ b/drivers/net/arcnet/com20020_cs.c @@ -333,16 +333,4 @@ static struct pcmcia_driver com20020_cs_driver = {  	.suspend	= com20020_suspend,  	.resume		= com20020_resume,  }; - -static int __init init_com20020_cs(void) -{ -	return pcmcia_register_driver(&com20020_cs_driver); -} - -static void __exit exit_com20020_cs(void) -{ -	pcmcia_unregister_driver(&com20020_cs_driver); -} - -module_init(init_com20020_cs); -module_exit(exit_com20020_cs); +module_pcmcia_driver(com20020_cs_driver); diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c index 5c2f3fbbf5a..321c27e1c7f 100644 --- a/drivers/net/can/sja1000/ems_pcmcia.c +++ b/drivers/net/can/sja1000/ems_pcmcia.c @@ -316,15 +316,4 @@ static struct pcmcia_driver ems_pcmcia_driver = {  	.remove = ems_pcmcia_remove,  	.id_table = ems_pcmcia_tbl,  }; - -static int __init ems_pcmcia_init(void) -{ -	return pcmcia_register_driver(&ems_pcmcia_driver); -} -module_init(ems_pcmcia_init); - -static void __exit ems_pcmcia_exit(void) -{ -	pcmcia_unregister_driver(&ems_pcmcia_driver); -} -module_exit(ems_pcmcia_exit); +module_pcmcia_driver(ems_pcmcia_driver); diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c index 1a7020ba37f..0a707f70661 100644 --- a/drivers/net/can/sja1000/peak_pcmcia.c +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -740,15 +740,4 @@ static struct pcmcia_driver pcan_driver = {  	.remove = pcan_remove,  	.id_table = pcan_table,  }; - -static int __init pcan_init(void) -{ -	return pcmcia_register_driver(&pcan_driver); -} -module_init(pcan_init); - -static void __exit pcan_exit(void) -{ -	pcmcia_unregister_driver(&pcan_driver); -} -module_exit(pcan_exit); +module_pcmcia_driver(pcan_driver); diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c index c2c0a5bb0b2..498605f833d 100644 --- a/drivers/net/can/softing/softing_cs.c +++ b/drivers/net/can/softing/softing_cs.c @@ -27,7 +27,7 @@  #include "softing_platform.h"  static int softingcs_index; -static spinlock_t softingcs_index_lock; +static DEFINE_SPINLOCK(softingcs_index_lock);  static int softingcs_reset(struct platform_device *pdev, int v);  static int softingcs_enable_irq(struct platform_device *pdev, int v); @@ -340,19 +340,7 @@ static struct pcmcia_driver softingcs_driver = {  	.remove		= softingcs_remove,  }; -static int __init softingcs_start(void) -{ -	spin_lock_init(&softingcs_index_lock); -	return pcmcia_register_driver(&softingcs_driver); -} - -static void __exit softingcs_stop(void) -{ -	pcmcia_unregister_driver(&softingcs_driver); -} - -module_init(softingcs_start); -module_exit(softingcs_stop); +module_pcmcia_driver(softingcs_driver);  MODULE_DESCRIPTION("softing CANcard driver"  		", links PCMCIA card to softing driver"); diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c index ffd8de28a76..6fc994fa4ab 100644 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ b/drivers/net/ethernet/3com/3c574_cs.c @@ -1165,16 +1165,4 @@ static struct pcmcia_driver tc574_driver = {  	.suspend	= tc574_suspend,  	.resume		= tc574_resume,  }; - -static int __init init_tc574(void) -{ -	return pcmcia_register_driver(&tc574_driver); -} - -static void __exit exit_tc574(void) -{ -	pcmcia_unregister_driver(&tc574_driver); -} - -module_init(init_tc574); -module_exit(exit_tc574); +module_pcmcia_driver(tc574_driver); diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index a556c01e011..078480aaa16 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -928,16 +928,4 @@ static struct pcmcia_driver tc589_driver = {  	.suspend	= tc589_suspend,  	.resume		= tc589_resume,  }; - -static int __init init_tc589(void) -{ -	return pcmcia_register_driver(&tc589_driver); -} - -static void __exit exit_tc589(void) -{ -	pcmcia_unregister_driver(&tc589_driver); -} - -module_init(init_tc589); -module_exit(exit_tc589); +module_pcmcia_driver(tc589_driver); diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c index e1b3941bd14..d801c1410fb 100644 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -728,19 +728,7 @@ static struct pcmcia_driver axnet_cs_driver = {  	.suspend	= axnet_suspend,  	.resume		= axnet_resume,  }; - -static int __init init_axnet_cs(void) -{ -	return pcmcia_register_driver(&axnet_cs_driver); -} - -static void __exit exit_axnet_cs(void) -{ -	pcmcia_unregister_driver(&axnet_cs_driver); -} - -module_init(init_axnet_cs); -module_exit(exit_axnet_cs); +module_pcmcia_driver(axnet_cs_driver);  /*====================================================================*/ diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c index de1af0bfed4..46c5aadaca8 100644 --- a/drivers/net/ethernet/8390/pcnet_cs.c +++ b/drivers/net/ethernet/8390/pcnet_cs.c @@ -1694,16 +1694,4 @@ static struct pcmcia_driver pcnet_driver = {  	.suspend	= pcnet_suspend,  	.resume		= pcnet_resume,  }; - -static int __init init_pcnet_cs(void) -{ -    return pcmcia_register_driver(&pcnet_driver); -} - -static void __exit exit_pcnet_cs(void) -{ -    pcmcia_unregister_driver(&pcnet_driver); -} - -module_init(init_pcnet_cs); -module_exit(exit_pcnet_cs); +module_pcmcia_driver(pcnet_driver); diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 9f59bf63514..d4ed89130c5 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -1508,16 +1508,4 @@ static struct pcmcia_driver nmclan_cs_driver = {  	.suspend	= nmclan_suspend,  	.resume		= nmclan_resume,  }; - -static int __init init_nmclan_cs(void) -{ -	return pcmcia_register_driver(&nmclan_cs_driver); -} - -static void __exit exit_nmclan_cs(void) -{ -	pcmcia_unregister_driver(&nmclan_cs_driver); -} - -module_init(init_nmclan_cs); -module_exit(exit_nmclan_cs); +module_pcmcia_driver(nmclan_cs_driver); diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index 2418faf2251..ab98b77df30 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -705,19 +705,7 @@ static struct pcmcia_driver fmvj18x_cs_driver = {  	.suspend	= fmvj18x_suspend,  	.resume		= fmvj18x_resume,  }; - -static int __init init_fmvj18x_cs(void) -{ -	return pcmcia_register_driver(&fmvj18x_cs_driver); -} - -static void __exit exit_fmvj18x_cs(void) -{ -	pcmcia_unregister_driver(&fmvj18x_cs_driver); -} - -module_init(init_fmvj18x_cs); -module_exit(exit_fmvj18x_cs); +module_pcmcia_driver(fmvj18x_cs_driver);  /*====================================================================*/ diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 04393b5fef7..656d2e2ebfc 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -2054,16 +2054,4 @@ static struct pcmcia_driver smc91c92_cs_driver = {  	.suspend	= smc91c92_suspend,  	.resume		= smc91c92_resume,  }; - -static int __init init_smc91c92_cs(void) -{ -	return pcmcia_register_driver(&smc91c92_cs_driver); -} - -static void __exit exit_smc91c92_cs(void) -{ -	pcmcia_unregister_driver(&smc91c92_cs_driver); -} - -module_init(init_smc91c92_cs); -module_exit(exit_smc91c92_cs); +module_pcmcia_driver(smc91c92_cs_driver); diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index 98e09d0d3ce..1025b4e937d 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1775,21 +1775,7 @@ static struct pcmcia_driver xirc2ps_cs_driver = {  	.suspend	= xirc2ps_suspend,  	.resume		= xirc2ps_resume,  }; - -static int __init -init_xirc2ps_cs(void) -{ -	return pcmcia_register_driver(&xirc2ps_cs_driver); -} - -static void __exit -exit_xirc2ps_cs(void) -{ -	pcmcia_unregister_driver(&xirc2ps_cs_driver); -} - -module_init(init_xirc2ps_cs); -module_exit(exit_xirc2ps_cs); +module_pcmcia_driver(xirc2ps_cs_driver);  #ifndef MODULE  static int __init setup_xirc2ps_cs(char *str) diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index 956024a636e..14128fd265a 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -180,16 +180,7 @@ static struct pcmcia_driver airo_driver = {  	.suspend	= airo_suspend,  	.resume		= airo_resume,  }; - -static int __init airo_cs_init(void) -{ -	return pcmcia_register_driver(&airo_driver); -} - -static void __exit airo_cs_cleanup(void) -{ -	pcmcia_unregister_driver(&airo_driver); -} +module_pcmcia_driver(airo_driver);  /*      This program is free software; you can redistribute it and/or @@ -229,6 +220,3 @@ static void __exit airo_cs_cleanup(void)      IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE      POSSIBILITY OF SUCH DAMAGE.  */ - -module_init(airo_cs_init); -module_exit(airo_cs_cleanup); diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index b42930f457c..52257221921 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -245,16 +245,7 @@ static struct pcmcia_driver atmel_driver = {  	.suspend	= atmel_suspend,  	.resume		= atmel_resume,  }; - -static int __init atmel_cs_init(void) -{ -        return pcmcia_register_driver(&atmel_driver); -} - -static void __exit atmel_cs_cleanup(void) -{ -        pcmcia_unregister_driver(&atmel_driver); -} +module_pcmcia_driver(atmel_driver);  /*      This program is free software; you can redistribute it and/or @@ -294,6 +285,3 @@ static void __exit atmel_cs_cleanup(void)      IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE      POSSIBILITY OF SUCH DAMAGE.  */ - -module_init(atmel_cs_init); -module_exit(atmel_cs_cleanup); diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c index f2ea2ceec8a..55f2bd7f8f7 100644 --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -130,6 +130,10 @@ static struct pcmcia_driver b43_pcmcia_driver = {  	.resume		= b43_pcmcia_resume,  }; +/* + * These are not module init/exit functions! + * The module_pcmcia_driver() helper cannot be used here. + */  int b43_pcmcia_init(void)  {  	return pcmcia_register_driver(&b43_pcmcia_driver); diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 89e9d3a78c3..56cd01ca8ad 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -709,17 +709,4 @@ static struct pcmcia_driver hostap_driver = {  	.suspend	= hostap_cs_suspend,  	.resume		= hostap_cs_resume,  }; - -static int __init init_prism2_pccard(void) -{ -	return pcmcia_register_driver(&hostap_driver); -} - -static void __exit exit_prism2_pccard(void) -{ -	pcmcia_unregister_driver(&hostap_driver); -} - - -module_init(init_prism2_pccard); -module_exit(exit_prism2_pccard); +module_pcmcia_driver(hostap_driver); diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 16beaf39dc5..c94dd680267 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -999,7 +999,6 @@ static const struct pcmcia_device_id if_cs_ids[] = {  };  MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); -  static struct pcmcia_driver lbs_driver = {  	.owner		= THIS_MODULE,  	.name		= DRV_NAME, @@ -1007,26 +1006,4 @@ static struct pcmcia_driver lbs_driver = {  	.remove		= if_cs_detach,  	.id_table       = if_cs_ids,  }; - - -static int __init if_cs_init(void) -{ -	int ret; - -	lbs_deb_enter(LBS_DEB_CS); -	ret = pcmcia_register_driver(&lbs_driver); -	lbs_deb_leave(LBS_DEB_CS); -	return ret; -} - - -static void __exit if_cs_exit(void) -{ -	lbs_deb_enter(LBS_DEB_CS); -	pcmcia_unregister_driver(&lbs_driver); -	lbs_deb_leave(LBS_DEB_CS); -} - - -module_init(if_cs_init); -module_exit(if_cs_exit); +module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index d7dbc00bcfb..d21d9593931 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -338,18 +338,4 @@ static struct pcmcia_driver orinoco_driver = {  	.suspend	= orinoco_cs_suspend,  	.resume		= orinoco_cs_resume,  }; - -static int __init -init_orinoco_cs(void) -{ -	return pcmcia_register_driver(&orinoco_driver); -} - -static void __exit -exit_orinoco_cs(void) -{ -	pcmcia_unregister_driver(&orinoco_driver); -} - -module_init(init_orinoco_cs); -module_exit(exit_orinoco_cs); +module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index 6e28ee4e9c5..e2264bc12eb 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -318,18 +318,4 @@ static struct pcmcia_driver orinoco_driver = {  	.resume		= spectrum_cs_resume,  	.id_table       = spectrum_cs_ids,  }; - -static int __init -init_spectrum_cs(void) -{ -	return pcmcia_register_driver(&orinoco_driver); -} - -static void __exit -exit_spectrum_cs(void) -{ -	pcmcia_unregister_driver(&orinoco_driver); -} - -module_init(init_spectrum_cs); -module_exit(exit_spectrum_cs); +module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 730186d0449..38d2089f338 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -2013,19 +2013,7 @@ static struct pcmcia_driver wl3501_driver = {  	.suspend	= wl3501_suspend,  	.resume		= wl3501_resume,  }; - -static int __init wl3501_init_module(void) -{ -	return pcmcia_register_driver(&wl3501_driver); -} - -static void __exit wl3501_exit_module(void) -{ -	pcmcia_unregister_driver(&wl3501_driver); -} - -module_init(wl3501_init_module); -module_exit(wl3501_exit_module); +module_pcmcia_driver(wl3501_driver);  MODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, "  	      "Arnaldo Carvalho de Melo <acme@conectiva.com.br>," diff --git a/drivers/parport/parport_amiga.c b/drivers/parport/parport_amiga.c index ee78e0ee6e0..09503b8d12e 100644 --- a/drivers/parport/parport_amiga.c +++ b/drivers/parport/parport_amiga.c @@ -244,20 +244,7 @@ static struct platform_driver amiga_parallel_driver = {  	},  }; -static int __init amiga_parallel_init(void) -{ -	return platform_driver_probe(&amiga_parallel_driver, -				     amiga_parallel_probe); -} - -module_init(amiga_parallel_init); - -static void __exit amiga_parallel_exit(void) -{ -	platform_driver_unregister(&amiga_parallel_driver); -} - -module_exit(amiga_parallel_exit); +module_platform_driver_probe(amiga_parallel_driver, amiga_parallel_probe);  MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");  MODULE_DESCRIPTION("Parport Driver for Amiga builtin Port"); diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 067ad517c1f..e9b52e4a464 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -193,16 +193,4 @@ static struct pcmcia_driver parport_cs_driver = {  	.remove		= parport_detach,  	.id_table	= parport_ids,  }; - -static int __init init_parport_cs(void) -{ -	return pcmcia_register_driver(&parport_cs_driver); -} - -static void __exit exit_parport_cs(void) -{ -	pcmcia_unregister_driver(&parport_cs_driver); -} - -module_init(init_parport_cs); -module_exit(exit_parport_cs); +module_pcmcia_driver(parport_cs_driver); diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index 050773c3682..a5251cb5fb0 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -246,14 +246,14 @@ struct parport *parport_gsc_probe_port(unsigned long base,  		printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);  		return NULL;  	} -	ops = kmalloc (sizeof (struct parport_operations), GFP_KERNEL); +	ops = kmemdup(&parport_gsc_ops, sizeof(struct parport_operations), +		      GFP_KERNEL);  	if (!ops) {  		printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n",  			base);  		kfree (priv);  		return NULL;  	} -	memcpy (ops, &parport_gsc_ops, sizeof (struct parport_operations));  	priv->ctr = 0xc;  	priv->ctr_writable = 0xff;  	priv->dma_buf = 0; diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index 5c4b6a1db6c..dffd6d0bd15 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -284,12 +284,11 @@ static int bpp_probe(struct platform_device *op)  	size = resource_size(&op->resource[0]);  	dma = PARPORT_DMA_NONE; -	ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL); +	ops = kmemdup(&parport_sunbpp_ops, sizeof(struct parport_operations), +		      GFP_KERNEL);          if (!ops)  		goto out_unmap; -        memcpy (ops, &parport_sunbpp_ops, sizeof(struct parport_operations)); -  	dprintk(("register_port\n"));  	if (!(p = parport_register_port((unsigned long)base, irq, dma, ops)))  		goto out_free_ops; diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 3f56bc086cb..92ed045a5f9 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -476,10 +476,9 @@ int parport_proc_register(struct parport *port)  	struct parport_sysctl_table *t;  	int i; -	t = kmalloc(sizeof(*t), GFP_KERNEL); +	t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL);  	if (t == NULL)  		return -ENOMEM; -	memcpy(t, &parport_sysctl_template, sizeof(*t));  	t->device_dir[0].extra1 = port; @@ -523,10 +522,9 @@ int parport_device_proc_register(struct pardevice *device)  	struct parport_device_sysctl_table *t;  	struct parport * port = device->port; -	t = kmalloc(sizeof(*t), GFP_KERNEL); +	t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL);  	if (t == NULL)  		return -ENOMEM; -	memcpy(t, &parport_device_sysctl_template, sizeof(*t));  	t->dev_dir[0].child = t->parport_dir;  	t->parport_dir[0].child = t->port_dir; diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index 7d1609fa233..df82a349e96 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -220,16 +220,4 @@ static struct pcmcia_driver aha152x_cs_driver = {  	.id_table       = aha152x_ids,  	.resume		= aha152x_resume,  }; - -static int __init init_aha152x_cs(void) -{ -	return pcmcia_register_driver(&aha152x_cs_driver); -} - -static void __exit exit_aha152x_cs(void) -{ -	pcmcia_unregister_driver(&aha152x_cs_driver); -} - -module_init(init_aha152x_cs); -module_exit(exit_aha152x_cs); +module_pcmcia_driver(aha152x_cs_driver); diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index 714b248f5d5..ba84769e849 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -194,16 +194,4 @@ static struct pcmcia_driver fdomain_cs_driver = {  	.id_table       = fdomain_ids,  	.resume		= fdomain_resume,  }; - -static int __init init_fdomain_cs(void) -{ -	return pcmcia_register_driver(&fdomain_cs_driver); -} - -static void __exit exit_fdomain_cs(void) -{ -	pcmcia_unregister_driver(&fdomain_cs_driver); -} - -module_init(init_fdomain_cs); -module_exit(exit_fdomain_cs); +module_pcmcia_driver(fdomain_cs_driver); diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index b61a753eb89..76ca00cbc11 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -1773,19 +1773,4 @@ static struct pcmcia_driver nsp_driver = {  	.suspend	= nsp_cs_suspend,  	.resume		= nsp_cs_resume,  }; - -static int __init nsp_cs_init(void) -{ -	return pcmcia_register_driver(&nsp_driver); -} - -static void __exit nsp_cs_exit(void) -{ -	pcmcia_unregister_driver(&nsp_driver); -} - - -module_init(nsp_cs_init) -module_exit(nsp_cs_exit) - -/* end */ +module_pcmcia_driver(nsp_driver); diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c index bcaf89fe0c9..8d4fdc29224 100644 --- a/drivers/scsi/pcmcia/qlogic_stub.c +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -300,19 +300,8 @@ static struct pcmcia_driver qlogic_cs_driver = {  	.id_table       = qlogic_ids,  	.resume		= qlogic_resume,  }; - -static int __init init_qlogic_cs(void) -{ -	return pcmcia_register_driver(&qlogic_cs_driver); -} - -static void __exit exit_qlogic_cs(void) -{ -	pcmcia_unregister_driver(&qlogic_cs_driver); -} +module_pcmcia_driver(qlogic_cs_driver);  MODULE_AUTHOR("Tom Zerucha, Michael Griffith");  MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");  MODULE_LICENSE("GPL"); -module_init(init_qlogic_cs); -module_exit(exit_qlogic_cs); diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index f5b52731abd..55b0b2b38a6 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -881,18 +881,4 @@ static struct pcmcia_driver sym53c500_cs_driver = {  	.id_table       = sym53c500_ids,  	.resume		= sym53c500_resume,  }; - -static int __init -init_sym53c500_cs(void) -{ -	return pcmcia_register_driver(&sym53c500_cs_driver); -} - -static void __exit -exit_sym53c500_cs(void) -{ -	pcmcia_unregister_driver(&sym53c500_cs_driver); -} - -module_init(init_sym53c500_cs); -module_exit(exit_sym53c500_cs); +module_pcmcia_driver(sym53c500_cs_driver); diff --git a/drivers/ssbi/Kconfig b/drivers/ssbi/Kconfig new file mode 100644 index 00000000000..1ae4040afed --- /dev/null +++ b/drivers/ssbi/Kconfig @@ -0,0 +1,16 @@ +# +# SSBI bus support +# + +menu "Qualcomm MSM SSBI bus support" + +config SSBI +	tristate "Qualcomm Single-wire Serial Bus Interface (SSBI)" +	help +	  If you say yes to this option, support will be included for the +	  built-in SSBI interface on Qualcomm MSM family processors. + +	  This is required for communicating with Qualcomm PMICs and +	  other devices that have the SSBI interface. + +endmenu diff --git a/drivers/ssbi/Makefile b/drivers/ssbi/Makefile new file mode 100644 index 00000000000..38fb70c31ca --- /dev/null +++ b/drivers/ssbi/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SSBI) += ssbi.o diff --git a/drivers/ssbi/ssbi.c b/drivers/ssbi/ssbi.c new file mode 100644 index 00000000000..f32da0258a8 --- /dev/null +++ b/drivers/ssbi/ssbi.c @@ -0,0 +1,379 @@ +/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2010, Google Inc. + * + * Original authors: Code Aurora Forum + * + * Author: Dima Zavin <dima@android.com> + *  - Largely rewritten from original to not be an i2c driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/ssbi.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> + +/* SSBI 2.0 controller registers */ +#define SSBI2_CMD			0x0008 +#define SSBI2_RD			0x0010 +#define SSBI2_STATUS			0x0014 +#define SSBI2_MODE2			0x001C + +/* SSBI_CMD fields */ +#define SSBI_CMD_RDWRN			(1 << 24) + +/* SSBI_STATUS fields */ +#define SSBI_STATUS_RD_READY		(1 << 2) +#define SSBI_STATUS_READY		(1 << 1) +#define SSBI_STATUS_MCHN_BUSY		(1 << 0) + +/* SSBI_MODE2 fields */ +#define SSBI_MODE2_REG_ADDR_15_8_SHFT	0x04 +#define SSBI_MODE2_REG_ADDR_15_8_MASK	(0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) + +#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ +	(((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ +	SSBI_MODE2_REG_ADDR_15_8_MASK)) + +/* SSBI PMIC Arbiter command registers */ +#define SSBI_PA_CMD			0x0000 +#define SSBI_PA_RD_STATUS		0x0004 + +/* SSBI_PA_CMD fields */ +#define SSBI_PA_CMD_RDWRN		(1 << 24) +#define SSBI_PA_CMD_ADDR_MASK		0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ + +/* SSBI_PA_RD_STATUS fields */ +#define SSBI_PA_RD_STATUS_TRANS_DONE	(1 << 27) +#define SSBI_PA_RD_STATUS_TRANS_DENIED	(1 << 26) + +#define SSBI_TIMEOUT_US			100 + +struct ssbi { +	struct device		*slave; +	void __iomem		*base; +	spinlock_t		lock; +	enum ssbi_controller_type controller_type; +	int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); +	int (*write)(struct ssbi *, u16 addr, u8 *buf, int len); +}; + +#define to_ssbi(dev)	platform_get_drvdata(to_platform_device(dev)) + +static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) +{ +	return readl(ssbi->base + reg); +} + +static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) +{ +	writel(val, ssbi->base + reg); +} + +/* + * Via private exchange with one of the original authors, the hardware + * should generally finish a transaction in about 5us.  The worst + * case, is when using the arbiter and both other CPUs have just + * started trying to use the SSBI bus will result in a time of about + * 20us.  It should never take longer than this. + * + * As such, this wait merely spins, with a udelay. + */ +static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) +{ +	u32 timeout = SSBI_TIMEOUT_US; +	u32 val; + +	while (timeout--) { +		val = ssbi_readl(ssbi, SSBI2_STATUS); +		if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) +			return 0; +		udelay(1); +	} + +	return -ETIMEDOUT; +} + +static int +ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ +	u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); +	int ret = 0; + +	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { +		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); +		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); +		ssbi_writel(ssbi, mode2, SSBI2_MODE2); +	} + +	while (len) { +		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); +		if (ret) +			goto err; + +		ssbi_writel(ssbi, cmd, SSBI2_CMD); +		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); +		if (ret) +			goto err; +		*buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; +		len--; +	} + +err: +	return ret; +} + +static int +ssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ +	int ret = 0; + +	if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { +		u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); +		mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); +		ssbi_writel(ssbi, mode2, SSBI2_MODE2); +	} + +	while (len) { +		ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); +		if (ret) +			goto err; + +		ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); +		ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); +		if (ret) +			goto err; +		buf++; +		len--; +	} + +err: +	return ret; +} + +/* + * See ssbi_wait_mask for an explanation of the time and the + * busywait. + */ +static inline int +ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) +{ +	u32 timeout = SSBI_TIMEOUT_US; +	u32 rd_status = 0; + +	ssbi_writel(ssbi, cmd, SSBI_PA_CMD); + +	while (timeout--) { +		rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); + +		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) +			return -EPERM; + +		if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { +			if (data) +				*data = rd_status & 0xff; +			return 0; +		} +		udelay(1); +	} + +	return -ETIMEDOUT; +} + +static int +ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ +	u32 cmd; +	int ret = 0; + +	cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; + +	while (len) { +		ret = ssbi_pa_transfer(ssbi, cmd, buf); +		if (ret) +			goto err; +		buf++; +		len--; +	} + +err: +	return ret; +} + +static int +ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ +	u32 cmd; +	int ret = 0; + +	while (len) { +		cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; +		ret = ssbi_pa_transfer(ssbi, cmd, NULL); +		if (ret) +			goto err; +		buf++; +		len--; +	} + +err: +	return ret; +} + +int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) +{ +	struct ssbi *ssbi = to_ssbi(dev); +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&ssbi->lock, flags); +	ret = ssbi->read(ssbi, addr, buf, len); +	spin_unlock_irqrestore(&ssbi->lock, flags); + +	return ret; +} +EXPORT_SYMBOL_GPL(ssbi_read); + +int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) +{ +	struct ssbi *ssbi = to_ssbi(dev); +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&ssbi->lock, flags); +	ret = ssbi->write(ssbi, addr, buf, len); +	spin_unlock_irqrestore(&ssbi->lock, flags); + +	return ret; +} +EXPORT_SYMBOL_GPL(ssbi_write); + +static int ssbi_probe(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	struct resource *mem_res; +	struct ssbi *ssbi; +	int ret = 0; +	const char *type; + +	ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL); +	if (!ssbi) { +		pr_err("can not allocate ssbi_data\n"); +		return -ENOMEM; +	} + +	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!mem_res) { +		pr_err("missing mem resource\n"); +		ret = -EINVAL; +		goto err_get_mem_res; +	} + +	ssbi->base = ioremap(mem_res->start, resource_size(mem_res)); +	if (!ssbi->base) { +		pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start); +		ret = -EINVAL; +		goto err_ioremap; +	} +	platform_set_drvdata(pdev, ssbi); + +	type = of_get_property(np, "qcom,controller-type", NULL); +	if (type == NULL) { +		pr_err("Missing qcom,controller-type property\n"); +		ret = -EINVAL; +		goto err_ssbi_controller; +	} +	dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); +	if (strcmp(type, "ssbi") == 0) +		ssbi->controller_type = MSM_SBI_CTRL_SSBI; +	else if (strcmp(type, "ssbi2") == 0) +		ssbi->controller_type = MSM_SBI_CTRL_SSBI2; +	else if (strcmp(type, "pmic-arbiter") == 0) +		ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; +	else { +		pr_err("Unknown qcom,controller-type\n"); +		ret = -EINVAL; +		goto err_ssbi_controller; +	} + +	if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { +		ssbi->read = ssbi_pa_read_bytes; +		ssbi->write = ssbi_pa_write_bytes; +	} else { +		ssbi->read = ssbi_read_bytes; +		ssbi->write = ssbi_write_bytes; +	} + +	spin_lock_init(&ssbi->lock); + +	ret = of_platform_populate(np, NULL, NULL, &pdev->dev); +	if (ret) +		goto err_ssbi_controller; + +	return 0; + +err_ssbi_controller: +	platform_set_drvdata(pdev, NULL); +	iounmap(ssbi->base); +err_ioremap: +err_get_mem_res: +	kfree(ssbi); +	return ret; +} + +static int ssbi_remove(struct platform_device *pdev) +{ +	struct ssbi *ssbi = platform_get_drvdata(pdev); + +	platform_set_drvdata(pdev, NULL); +	iounmap(ssbi->base); +	kfree(ssbi); +	return 0; +} + +static struct of_device_id ssbi_match_table[] = { +	{ .compatible = "qcom,ssbi" }, +	{} +}; + +static struct platform_driver ssbi_driver = { +	.probe		= ssbi_probe, +	.remove		= ssbi_remove, +	.driver		= { +		.name	= "ssbi", +		.owner	= THIS_MODULE, +		.of_match_table = ssbi_match_table, +	}, +}; + +static int __init ssbi_init(void) +{ +	return platform_driver_register(&ssbi_driver); +} +module_init(ssbi_init); + +static void __exit ssbi_exit(void) +{ +	platform_driver_unregister(&ssbi_driver); +} +module_exit(ssbi_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:ssbi"); +MODULE_AUTHOR("Dima Zavin <dima@android.com>"); diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index b7d48b34639..1b74b88e1e1 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -852,18 +852,6 @@ static struct pcmcia_driver serial_cs_driver = {  	.suspend	= serial_suspend,  	.resume		= serial_resume,  }; - -static int __init init_serial_cs(void) -{ -	return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ -	pcmcia_unregister_driver(&serial_cs_driver); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); +module_pcmcia_driver(serial_cs_driver);  MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 3b6f50eaec9..469564e57a5 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -200,17 +200,4 @@ static struct pcmcia_driver sl811_cs_driver = {  	.remove		= sl811_cs_detach,  	.id_table	= sl811_ids,  }; - -/*====================================================================*/ - -static int __init init_sl811_cs(void) -{ -	return pcmcia_register_driver(&sl811_cs_driver); -} -module_init(init_sl811_cs); - -static void __exit exit_sl811_cs(void) -{ -	pcmcia_unregister_driver(&sl811_cs_driver); -} -module_exit(exit_sl811_cs); +module_pcmcia_driver(sl811_cs_driver); diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 950d354d50e..47e12cfc2a5 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -121,9 +121,9 @@ static int mxc_w1_probe(struct platform_device *pdev)  	mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	mdev->regs = devm_request_and_ioremap(&pdev->dev, res); -	if (!mdev->regs) -		return -EBUSY; +	mdev->regs = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(mdev->regs)) +		return PTR_ERR(mdev->regs);  	clk_prepare_enable(mdev->clk);  	__raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 762561fbabb..5e6a3c9e510 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -22,6 +22,16 @@ config W1_SLAVE_DS2408  	  Say Y here if you want to use a 1-wire  	  DS2408 8-Channel Addressable Switch device support +config W1_SLAVE_DS2408_READBACK +	bool "Read-back values written to DS2408's output register" +	depends on W1_SLAVE_DS2408 +	default y +	help +	  Enabling this will cause the driver to read back the values written +	  to the chip's output register in order to detect errors. + +	  This is slower but useful when debugging chips and/or busses. +  config W1_SLAVE_DS2413  	tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)"  	help diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 441ad3a3b58..e45eca1044b 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -178,6 +178,15 @@ static ssize_t w1_f29_write_output(  		w1_write_block(sl->master, w1_buf, 3);  		readBack = w1_read_8(sl->master); + +		if (readBack != W1_F29_SUCCESS_CONFIRM_BYTE) { +			if (w1_reset_resume_command(sl->master)) +				goto error; +			/* try again, the slave is ready for a command */ +			continue; +		} + +#ifdef CONFIG_W1_SLAVE_DS2408_READBACK  		/* here the master could read another byte which  		   would be the PIO reg (the actual pin logic state)  		   since in this driver we don't know which pins are @@ -186,11 +195,6 @@ static ssize_t w1_f29_write_output(  		if (w1_reset_resume_command(sl->master))  			goto error; -		if (readBack != 0xAA) { -			/* try again, the slave is ready for a command */ -			continue; -		} -  		/* go read back the output latches */  		/* (the direct effect of the write above) */  		w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; @@ -198,7 +202,9 @@ static ssize_t w1_f29_write_output(  		w1_buf[2] = 0;  		w1_write_block(sl->master, w1_buf, 3);  		/* read the result of the READ_PIO_REGS command */ -		if (w1_read_8(sl->master) == *buf) { +		if (w1_read_8(sl->master) == *buf) +#endif +		{  			/* success! */  			mutex_unlock(&sl->master->bus_mutex);  			dev_dbg(&sl->dev, @@ -297,8 +303,7 @@ error: -#define NB_SYSFS_BIN_FILES 6 -static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { +static struct bin_attribute w1_f29_sysfs_bin_files[] = {  	{  		.attr =	{  			.name = "state", @@ -357,7 +362,7 @@ static int w1_f29_add_slave(struct w1_slave *sl)  	int err = 0;  	int i; -	for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) +	for (i = 0; i < ARRAY_SIZE(w1_f29_sysfs_bin_files) && !err; ++i)  		err = sysfs_create_bin_file(  			&sl->dev.kobj,  			&(w1_f29_sysfs_bin_files[i])); @@ -371,7 +376,7 @@ static int w1_f29_add_slave(struct w1_slave *sl)  static void w1_f29_remove_slave(struct w1_slave *sl)  {  	int i; -	for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) +	for (i = ARRAY_SIZE(w1_f29_sysfs_bin_files) - 1; i >= 0; --i)  		sysfs_remove_bin_file(&sl->dev.kobj,  			&(w1_f29_sysfs_bin_files[i]));  } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index df77ba9a816..95d0850584d 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -27,6 +27,63 @@  #include <linux/types.h> + +/* + * Implementation of host controlled snapshot of the guest. + */ + +#define VSS_OP_REGISTER 128 + +enum hv_vss_op { +	VSS_OP_CREATE = 0, +	VSS_OP_DELETE, +	VSS_OP_HOT_BACKUP, +	VSS_OP_GET_DM_INFO, +	VSS_OP_BU_COMPLETE, +	/* +	 * Following operations are only supported with IC version >= 5.0 +	 */ +	VSS_OP_FREEZE, /* Freeze the file systems in the VM */ +	VSS_OP_THAW, /* Unfreeze the file systems */ +	VSS_OP_AUTO_RECOVER, +	VSS_OP_COUNT /* Number of operations, must be last */ +}; + + +/* + * Header for all VSS messages. + */ +struct hv_vss_hdr { +	__u8 operation; +	__u8 reserved[7]; +} __attribute__((packed)); + + +/* + * Flag values for the hv_vss_check_feature. Linux supports only + * one value. + */ +#define VSS_HBU_NO_AUTO_RECOVERY	0x00000005 + +struct hv_vss_check_feature { +	__u32 flags; +} __attribute__((packed)); + +struct hv_vss_check_dm_info { +	__u32 flags; +} __attribute__((packed)); + +struct hv_vss_msg { +	union { +		struct hv_vss_hdr vss_hdr; +		int error; +	}; +	union { +		struct hv_vss_check_feature vss_cf; +		struct hv_vss_check_dm_info dm_info; +	}; +} __attribute__((packed)); +  /*   * An implementation of HyperV key value pair (KVP) functionality for Linux.   * @@ -1253,6 +1310,14 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);  		}  /* + * VSS (Backup/Restore) GUID + */ +#define HV_VSS_GUID \ +	.guid = { \ +			0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42, \ +			0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4,  0x40 \ +		} +/*   * Common header for Hyper-V ICs   */ @@ -1356,6 +1421,10 @@ int hv_kvp_init(struct hv_util_service *);  void hv_kvp_deinit(void);  void hv_kvp_onchannelcallback(void *); +int hv_vss_init(struct hv_util_service *); +void hv_vss_deinit(void); +void hv_vss_onchannelcallback(void *); +  /*   * Negotiated version with the Host.   */ diff --git a/include/linux/ipack.h b/include/linux/ipack.h index fea12cbb2ae..1888e06ddf6 100644 --- a/include/linux/ipack.h +++ b/include/linux/ipack.h @@ -207,19 +207,41 @@ int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,  void ipack_driver_unregister(struct ipack_driver *edrv);  /** - *	ipack_device_register -- register an IPack device with the kernel - *	@dev: the new device to register. + *	ipack_device_init -- initialize an IPack device + * @dev: the new device to initialize.   * - *	Register a new IPack device ("module" in IndustryPack jargon). The call - *	is done by the carrier driver.  The carrier should populate the fields - *	bus and slot as well as the region array of @dev prior to calling this - *	function.  The rest of the fields will be allocated and populated - *	during registration. + * Initialize a new IPack device ("module" in IndustryPack jargon). The call + * is done by the carrier driver.  The carrier should populate the fields + * bus and slot as well as the region array of @dev prior to calling this + * function.  The rest of the fields will be allocated and populated + * during initalization.   * - *	Return zero on success or error code on failure. + * Return zero on success or error code on failure. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use ipack_put_device() to give up the + * reference initialized in this function instead. + */ +int ipack_device_init(struct ipack_device *dev); + +/** + *	ipack_device_add -- Add an IPack device + * @dev: the new device to add. + * + * Add a new IPack device. The call is done by the carrier driver + * after calling ipack_device_init(). + * + * Return zero on success or error code on failure. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use ipack_put_device() to give up the + * reference initialized in this function instead.   */ -int ipack_device_register(struct ipack_device *dev); -void ipack_device_unregister(struct ipack_device *dev); +int ipack_device_add(struct ipack_device *dev); +void ipack_device_del(struct ipack_device *dev); + +void ipack_get_device(struct ipack_device *dev); +void ipack_put_device(struct ipack_device *dev);  /**   * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table diff --git a/include/linux/platform_data/emif_plat.h b/include/linux/platform_data/emif_plat.h index 03378ca8406..5c19a2a647c 100644 --- a/include/linux/platform_data/emif_plat.h +++ b/include/linux/platform_data/emif_plat.h @@ -40,6 +40,7 @@  /* Custom config requests */  #define EMIF_CUSTOM_CONFIG_LPMODE			0x00000001  #define EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL	0x00000002 +#define EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART		0x00000004  #ifndef __ASSEMBLY__  /** diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h new file mode 100644 index 00000000000..44ef5da2147 --- /dev/null +++ b/include/linux/ssbi.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2010 Google, Inc. + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * Author: Dima Zavin <dima@android.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_SSBI_H +#define _LINUX_SSBI_H + +#include <linux/types.h> + +struct ssbi_slave_info { +	const char	*name; +	void		*platform_data; +}; + +enum ssbi_controller_type { +	MSM_SBI_CTRL_SSBI = 0, +	MSM_SBI_CTRL_SSBI2, +	MSM_SBI_CTRL_PMIC_ARBITER, +}; + +struct ssbi_platform_data { +	struct ssbi_slave_info	slave; +	enum ssbi_controller_type controller_type; +}; + +int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len); +int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len); +#endif diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 3bbbd78e143..2d56e428506 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -65,6 +65,18 @@ struct pcmcia_driver {  int pcmcia_register_driver(struct pcmcia_driver *driver);  void pcmcia_unregister_driver(struct pcmcia_driver *driver); +/** + * module_pcmcia_driver() - Helper macro for registering a pcmcia driver + * @__pcmcia_driver: pcmcia_driver struct + * + * Helper macro for pcmcia drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only use + * this macro once, and calling it replaces module_init() and module_exit(). + */ +#define module_pcmcia_driver(__pcmcia_driver) \ +	module_driver(__pcmcia_driver, pcmcia_register_driver, \ +			pcmcia_unregister_driver) +  /* for struct resource * array embedded in struct pcmcia_device */  enum {  	PCMCIA_IOPORT_0, diff --git a/include/uapi/linux/connector.h b/include/uapi/linux/connector.h index 8761a0349c7..4cb283505e4 100644 --- a/include/uapi/linux/connector.h +++ b/include/uapi/linux/connector.h @@ -44,8 +44,11 @@  #define CN_VAL_DRBD			0x1  #define CN_KVP_IDX			0x9	/* HyperV KVP */  #define CN_KVP_VAL			0x1	/* queries from the kernel */ +#define CN_VSS_IDX			0xA     /* HyperV VSS */ +#define CN_VSS_VAL			0x1     /* queries from the kernel */ -#define CN_NETLINK_USERS		10	/* Highest index + 1 */ + +#define CN_NETLINK_USERS		11	/* Highest index + 1 */  /*   * Maximum connector's message size. diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index f9b5229b272..8f489de5c4c 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -295,18 +295,5 @@ static struct pcmcia_driver pdacf_cs_driver = {  	.suspend	= pdacf_suspend,  	.resume		= pdacf_resume,  #endif -  }; - -static int __init init_pdacf(void) -{ -	return pcmcia_register_driver(&pdacf_cs_driver); -} - -static void __exit exit_pdacf(void) -{ -	pcmcia_unregister_driver(&pdacf_cs_driver); -} - -module_init(init_pdacf); -module_exit(exit_pdacf); +module_pcmcia_driver(pdacf_cs_driver); diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 8f9350475c7..d4db7ecaa6b 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -367,16 +367,4 @@ static struct pcmcia_driver vxp_cs_driver = {  	.resume		= vxp_resume,  #endif  }; - -static int __init init_vxpocket(void) -{ -	return pcmcia_register_driver(&vxp_cs_driver); -} - -static void __exit exit_vxpocket(void) -{ -	pcmcia_unregister_driver(&vxp_cs_driver); -} - -module_init(init_vxpocket); -module_exit(exit_vxpocket); +module_pcmcia_driver(vxp_cs_driver); diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index c800ea4c8bf..5a1f6489d18 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -102,6 +102,10 @@ static struct utsname uts_buf;  #define MAX_FILE_NAME 100  #define ENTRIES_PER_BLOCK 50 +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif +  struct kvp_record {  	char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];  	char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; @@ -1407,7 +1411,7 @@ netlink_send(int fd, struct cn_msg *msg)  int main(void)  { -	int fd, len, sock_opt; +	int fd, len, nl_group;  	int error;  	struct cn_msg *message;  	struct pollfd pfd; @@ -1443,7 +1447,7 @@ int main(void)  	addr.nl_family = AF_NETLINK;  	addr.nl_pad = 0;  	addr.nl_pid = 0; -	addr.nl_groups = CN_KVP_IDX; +	addr.nl_groups = 0;  	error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); @@ -1452,8 +1456,8 @@ int main(void)  		close(fd);  		exit(EXIT_FAILURE);  	} -	sock_opt = addr.nl_groups; -	setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); +	nl_group = CN_KVP_IDX; +	setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group));  	/*  	 * Register ourselves with the kernel.  	 */ @@ -1499,6 +1503,10 @@ int main(void)  		}  		incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; + +		if (incoming_msg->nlmsg_type != NLMSG_DONE) +			continue; +  		incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);  		hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c new file mode 100644 index 00000000000..95269952aa9 --- /dev/null +++ b/tools/hv/hv_vss_daemon.c @@ -0,0 +1,220 @@ +/* + * An implementation of the host initiated guest snapshot for Hyper-V. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan <kys@microsoft.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT.  See the GNU General Public License for more + * details. + * + */ + + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/poll.h> +#include <linux/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <arpa/inet.h> +#include <linux/connector.h> +#include <linux/hyperv.h> +#include <linux/netlink.h> +#include <syslog.h> + +static char vss_recv_buffer[4096]; +static char vss_send_buffer[4096]; +static struct sockaddr_nl addr; + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + + +static int vss_operate(int operation) +{ +	char *fs_op; +	char cmd[512]; +	char buf[512]; +	FILE *file; +	char *p; +	char *x; +	int error; + +	switch (operation) { +	case VSS_OP_FREEZE: +		fs_op = "-f "; +		break; +	case VSS_OP_THAW: +		fs_op = "-u "; +		break; +	} + +	file = popen("mount | awk '/^\/dev\// { print $3}'", "r"); +	if (file == NULL) +		return; + +	while ((p = fgets(buf, sizeof(buf), file)) != NULL) { +		x = strchr(p, '\n'); +		*x = '\0'; +		if (!strncmp(p, "/", sizeof("/"))) +			continue; + +		sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, p); +		syslog(LOG_INFO, "VSS cmd is %s\n", cmd); +		error = system(cmd); +	} +	pclose(file); + +	sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, "/"); +	syslog(LOG_INFO, "VSS cmd is %s\n", cmd); +	error = system(cmd); + +	return error; +} + +static int netlink_send(int fd, struct cn_msg *msg) +{ +	struct nlmsghdr *nlh; +	unsigned int size; +	struct msghdr message; +	char buffer[64]; +	struct iovec iov[2]; + +	size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); + +	nlh = (struct nlmsghdr *)buffer; +	nlh->nlmsg_seq = 0; +	nlh->nlmsg_pid = getpid(); +	nlh->nlmsg_type = NLMSG_DONE; +	nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); +	nlh->nlmsg_flags = 0; + +	iov[0].iov_base = nlh; +	iov[0].iov_len = sizeof(*nlh); + +	iov[1].iov_base = msg; +	iov[1].iov_len = size; + +	memset(&message, 0, sizeof(message)); +	message.msg_name = &addr; +	message.msg_namelen = sizeof(addr); +	message.msg_iov = iov; +	message.msg_iovlen = 2; + +	return sendmsg(fd, &message, 0); +} + +int main(void) +{ +	int fd, len, nl_group; +	int error; +	struct cn_msg *message; +	struct pollfd pfd; +	struct nlmsghdr *incoming_msg; +	struct cn_msg	*incoming_cn_msg; +	int	op; +	struct hv_vss_msg *vss_msg; + +	daemon(1, 0); +	openlog("Hyper-V VSS", 0, LOG_USER); +	syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); + +	fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); +	if (fd < 0) { +		syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); +		exit(EXIT_FAILURE); +	} +	addr.nl_family = AF_NETLINK; +	addr.nl_pad = 0; +	addr.nl_pid = 0; +	addr.nl_groups = 0; + + +	error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); +	if (error < 0) { +		syslog(LOG_ERR, "bind failed; error:%d", error); +		close(fd); +		exit(EXIT_FAILURE); +	} +	nl_group = CN_VSS_IDX; +	setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); +	/* +	 * Register ourselves with the kernel. +	 */ +	message = (struct cn_msg *)vss_send_buffer; +	message->id.idx = CN_VSS_IDX; +	message->id.val = CN_VSS_VAL; +	message->ack = 0; +	vss_msg = (struct hv_vss_msg *)message->data; +	vss_msg->vss_hdr.operation = VSS_OP_REGISTER; + +	message->len = sizeof(struct hv_vss_msg); + +	len = netlink_send(fd, message); +	if (len < 0) { +		syslog(LOG_ERR, "netlink_send failed; error:%d", len); +		close(fd); +		exit(EXIT_FAILURE); +	} + +	pfd.fd = fd; + +	while (1) { +		struct sockaddr *addr_p = (struct sockaddr *) &addr; +		socklen_t addr_l = sizeof(addr); +		pfd.events = POLLIN; +		pfd.revents = 0; +		poll(&pfd, 1, -1); + +		len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0, +				addr_p, &addr_l); + +		if (len < 0 || addr.nl_pid) { +			syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", +					addr.nl_pid, errno, strerror(errno)); +			close(fd); +			return -1; +		} + +		incoming_msg = (struct nlmsghdr *)vss_recv_buffer; + +		if (incoming_msg->nlmsg_type != NLMSG_DONE) +			continue; + +		incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); +		vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data; +		op = vss_msg->vss_hdr.operation; +		error =  HV_S_OK; + +		switch (op) { +		case VSS_OP_FREEZE: +		case VSS_OP_THAW: +			error = vss_operate(op); +			if (error) +				error = HV_E_FAIL; +			break; +		default: +			syslog(LOG_ERR, "Illegal op:%d\n", op); +		} +		vss_msg->error = error; +		len = netlink_send(fd, incoming_cn_msg); +		if (len < 0) { +			syslog(LOG_ERR, "net_link send failed; error:%d", len); +			exit(EXIT_FAILURE); +		} +	} + +} |