diff options
Diffstat (limited to 'drivers')
477 files changed, 16717 insertions, 3787 deletions
diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 1e5d8a40101..fefc2ca7cc3 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -405,7 +405,7 @@ int apei_estatus_check(const struct acpi_hest_generic_status *estatus)  		return rc;  	data_len = estatus->data_length;  	gdata = (struct acpi_hest_generic_data *)(estatus + 1); -	while (data_len > sizeof(*gdata)) { +	while (data_len >= sizeof(*gdata)) {  		gedata_len = gdata->error_data_length;  		if (gedata_len > data_len - sizeof(*gdata))  			return -EINVAL; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0ac546d5e53..5ff17306612 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -646,6 +646,7 @@ static void handle_root_bridge_insertion(acpi_handle handle)  static void handle_root_bridge_removal(struct acpi_device *device)  { +	acpi_status status;  	struct acpi_eject_event *ej_event;  	ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); @@ -661,7 +662,9 @@ static void handle_root_bridge_removal(struct acpi_device *device)  	ej_event->device = device;  	ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; -	acpi_bus_hot_remove_device(ej_event); +	status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); +	if (ACPI_FAILURE(status)) +		kfree(ej_event);  }  static void _handle_hotplug_event_root(struct work_struct *work) @@ -676,8 +679,9 @@ static void _handle_hotplug_event_root(struct work_struct *work)  	handle = hp_work->handle;  	type = hp_work->type; -	root = acpi_pci_find_root(handle); +	acpi_scan_lock_acquire(); +	root = acpi_pci_find_root(handle);  	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);  	switch (type) { @@ -711,6 +715,7 @@ static void _handle_hotplug_event_root(struct work_struct *work)  		break;  	} +	acpi_scan_lock_release();  	kfree(hp_work); /* allocated in handle_hotplug_event_bridge */  	kfree(buffer.pointer);  } diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 53e7ac9403a..e854582f29a 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -465,7 +465,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)  	return result;  } -static int acpi_processor_get_performance_info(struct acpi_processor *pr) +int acpi_processor_get_performance_info(struct acpi_processor *pr)  {  	int result = 0;  	acpi_status status = AE_OK; @@ -509,7 +509,7 @@ static int acpi_processor_get_performance_info(struct acpi_processor *pr)  #endif  	return result;  } - +EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info);  int acpi_processor_notify_smm(struct module *calling_module)  {  	acpi_status status; diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 24213033fba..9c1a435d10e 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -193,6 +193,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {  	},  	{  	.callback = init_nvs_nosave, +	.ident = "Sony Vaio VGN-FW21M", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), +		DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21M"), +		}, +	}, +	{ +	.callback = init_nvs_nosave,  	.ident = "Sony Vaio VPCEB17FX",  	.matches = {  		DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c index 093c4355496..1f44e56cc65 100644 --- a/drivers/amba/tegra-ahb.c +++ b/drivers/amba/tegra-ahb.c @@ -158,7 +158,7 @@ int tegra_ahb_enable_smmu(struct device_node *dn)  EXPORT_SYMBOL(tegra_ahb_enable_smmu);  #endif -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM  static int tegra_ahb_suspend(struct device *dev)  {  	int i; diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 3e751b74615..a5a3ebcbdd2 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -59,15 +59,16 @@ config ATA_ACPI  	  option libata.noacpi=1  config SATA_ZPODD -	bool "SATA Zero Power ODD Support" +	bool "SATA Zero Power Optical Disc Drive (ZPODD) support"  	depends on ATA_ACPI  	default n  	help -	  This option adds support for SATA ZPODD. It requires both -	  ODD and the platform support, and if enabled, will automatically -	  power on/off the ODD when certain condition is satisfied. This -	  does not impact user's experience of the ODD, only power is saved -	  when ODD is not in use(i.e. no disc inside). +	  This option adds support for SATA Zero Power Optical Disc +	  Drive (ZPODD). It requires both the ODD and the platform +	  support, and if enabled, will automatically power on/off the +	  ODD when certain condition is satisfied. This does not impact +	  end user's experience of the ODD, only power is saved when +	  the ODD is not in use (i.e. no disc inside).  	  If unsure, say N. diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index a99112cfd8b..6a67b07de49 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -281,6 +281,8 @@ static const struct pci_device_id ahci_pci_tbl[] = {  	{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */  	{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */  	{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */ +	{ PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */ +	{ PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */  	{ PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */  	{ PCI_VDEVICE(INTEL, 0x8d04), board_ahci }, /* Wellsburg RAID */  	{ PCI_VDEVICE(INTEL, 0x8d06), board_ahci }, /* Wellsburg RAID */ diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index d2ba439cfe5..ffdd32d2260 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1547,6 +1547,10 @@ static bool piix_broken_system_poweroff(struct pci_dev *pdev)  static int prefer_ms_hyperv = 1;  module_param(prefer_ms_hyperv, int, 0); +MODULE_PARM_DESC(prefer_ms_hyperv, +	"Prefer Hyper-V paravirtualization drivers instead of ATA, " +	"0 - Use ATA drivers, " +	"1 (Default) - Use the paravirtualization drivers.");  static void piix_ignore_devices_quirk(struct ata_host *host)  { diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index beea3115577..8a52dab412e 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -1027,7 +1027,7 @@ static void ata_acpi_register_power_resource(struct ata_device *dev)  	handle = ata_dev_acpi_handle(dev);  	if (handle) -		acpi_dev_pm_remove_dependent(handle, &sdev->sdev_gendev); +		acpi_dev_pm_add_dependent(handle, &sdev->sdev_gendev);  }  static void ata_acpi_unregister_power_resource(struct ata_device *dev) diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index 70b0e01372b..6ef27e98c50 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -661,18 +661,7 @@ static struct platform_driver pata_s3c_driver = {  	},  }; -static int __init pata_s3c_init(void) -{ -	return platform_driver_probe(&pata_s3c_driver, pata_s3c_probe); -} - -static void __exit pata_s3c_exit(void) -{ -	platform_driver_unregister(&pata_s3c_driver); -} - -module_init(pata_s3c_init); -module_exit(pata_s3c_exit); +module_platform_driver_probe(pata_s3c_driver, pata_s3c_probe);  MODULE_AUTHOR("Abhilash Kesavan, <a.kesavan@samsung.com>");  MODULE_DESCRIPTION("low-level driver for Samsung PATA controller"); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 124b2c1d9c0..608f82fed63 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1511,8 +1511,7 @@ error_exit_with_cleanup:  	if (hcr_base)  		iounmap(hcr_base); -	if (host_priv) -		kfree(host_priv); +	kfree(host_priv);  	return retval;  } diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 5dc0daed8fa..b81ddfea1da 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -532,11 +532,11 @@ config BLK_DEV_RBD  	  If unsure, say N.  config BLK_DEV_RSXX -	tristate "RamSam PCIe Flash SSD Device Driver" +	tristate "IBM FlashSystem 70/80 PCIe SSD Device Driver"  	depends on PCI  	help  	  Device driver for IBM's high speed PCIe SSD -	  storage devices: RamSan-70 and RamSan-80. +	  storage devices: FlashSystem-70 and FlashSystem-80.  	  To compile this driver as a module, choose M here: the  	  module will be called rsxx. diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index ade58bc8f3c..1c1b8e544aa 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -4206,7 +4206,7 @@ static int cciss_find_cfgtables(ctlr_info_t *h)  	if (rc)  		return rc;  	h->cfgtable = remap_pci_mem(pci_resource_start(h->pdev, -		cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable)); +		cfg_base_addr_index) + cfg_offset, sizeof(*h->cfgtable));  	if (!h->cfgtable)  		return -ENOMEM;  	rc = write_driver_ver_to_cfgtable(h->cfgtable); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 747bb2af69d..fe5f6403417 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1044,12 +1044,29 @@ static int loop_clr_fd(struct loop_device *lo)  	lo->lo_state = Lo_unbound;  	/* This is safe: open() is still holding a reference. */  	module_put(THIS_MODULE); -	if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev) -		ioctl_by_bdev(bdev, BLKRRPART, 0);  	lo->lo_flags = 0;  	if (!part_shift)  		lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;  	mutex_unlock(&lo->lo_ctl_mutex); + +	/* +	 * Remove all partitions, since BLKRRPART won't remove user +	 * added partitions when max_part=0 +	 */ +	if (bdev) { +		struct disk_part_iter piter; +		struct hd_struct *part; + +		mutex_lock_nested(&bdev->bd_mutex, 1); +		invalidate_partition(bdev->bd_disk, 0); +		disk_part_iter_init(&piter, bdev->bd_disk, +					DISK_PITER_INCL_EMPTY); +		while ((part = disk_part_iter_next(&piter))) +			delete_partition(bdev->bd_disk, part->partno); +		disk_part_iter_exit(&piter); +		mutex_unlock(&bdev->bd_mutex); +	} +  	/*  	 * Need not hold lo_ctl_mutex to fput backing file.  	 * Calling fput holding lo_ctl_mutex triggers a circular @@ -1623,6 +1640,7 @@ static int loop_add(struct loop_device **l, int i)  		goto out_free_dev;  	i = err; +	err = -ENOMEM;  	lo->lo_queue = blk_alloc_queue(GFP_KERNEL);  	if (!lo->lo_queue)  		goto out_free_dev; diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c index 1788f491e0f..076ae7f1b78 100644 --- a/drivers/block/mg_disk.c +++ b/drivers/block/mg_disk.c @@ -890,8 +890,10 @@ static int mg_probe(struct platform_device *plat_dev)  	gpio_direction_output(host->rst, 1);  	/* reset out pin */ -	if (!(prv_data->dev_attr & MG_DEV_MASK)) +	if (!(prv_data->dev_attr & MG_DEV_MASK)) { +		err = -EINVAL;  		goto probe_err_3a; +	}  	if (prv_data->dev_attr != MG_BOOT_DEV) {  		rsc = platform_get_resource_byname(plat_dev, IORESOURCE_IO, diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 11cc9522cdd..92250af84e7 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -4224,6 +4224,7 @@ static int mtip_pci_probe(struct pci_dev *pdev,  	dd->isr_workq = create_workqueue(dd->workq_name);  	if (!dd->isr_workq) {  		dev_warn(&pdev->dev, "Can't create wq %d\n", dd->instance); +		rv = -ENOMEM;  		goto block_initialize_err;  	} @@ -4282,7 +4283,8 @@ static int mtip_pci_probe(struct pci_dev *pdev,  	INIT_WORK(&dd->work[7].work, mtip_workq_sdbf7);  	pci_set_master(pdev); -	if (pci_enable_msi(pdev)) { +	rv = pci_enable_msi(pdev); +	if (rv) {  		dev_warn(&pdev->dev,  			"Unable to enable MSI interrupt.\n");  		goto block_initialize_err; diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 07fb2dfaae1..9dcefe40380 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -135,6 +135,7 @@ static inline void _nvme_check_size(void)  	BUILD_BUG_ON(sizeof(struct nvme_id_ctrl) != 4096);  	BUILD_BUG_ON(sizeof(struct nvme_id_ns) != 4096);  	BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64); +	BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);  }  typedef void (*nvme_completion_fn)(struct nvme_dev *, void *, @@ -237,7 +238,8 @@ static void *free_cmdid(struct nvme_queue *nvmeq, int cmdid,  		*fn = special_completion;  		return CMD_CTX_INVALID;  	} -	*fn = info[cmdid].fn; +	if (fn) +		*fn = info[cmdid].fn;  	ctx = info[cmdid].ctx;  	info[cmdid].fn = special_completion;  	info[cmdid].ctx = CMD_CTX_COMPLETED; @@ -335,6 +337,7 @@ nvme_alloc_iod(unsigned nseg, unsigned nbytes, gfp_t gfp)  		iod->offset = offsetof(struct nvme_iod, sg[nseg]);  		iod->npages = -1;  		iod->length = nbytes; +		iod->nents = 0;  	}  	return iod; @@ -375,7 +378,8 @@ static void bio_completion(struct nvme_dev *dev, void *ctx,  	struct bio *bio = iod->private;  	u16 status = le16_to_cpup(&cqe->status) >> 1; -	dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents, +	if (iod->nents) +		dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents,  			bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);  	nvme_free_iod(dev, iod);  	if (status) { @@ -589,7 +593,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,  	result = nvme_map_bio(nvmeq->q_dmadev, iod, bio, dma_dir, psegs);  	if (result < 0) -		goto free_iod; +		goto free_cmdid;  	length = result;  	cmnd->rw.command_id = cmdid; @@ -609,6 +613,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,  	return 0; + free_cmdid: +	free_cmdid(nvmeq, cmdid, NULL);   free_iod:  	nvme_free_iod(nvmeq->dev, iod);   nomem: @@ -835,8 +841,8 @@ static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns,  	return nvme_submit_admin_cmd(dev, &c, NULL);  } -static int nvme_get_features(struct nvme_dev *dev, unsigned fid, -				unsigned nsid, dma_addr_t dma_addr) +static int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid, +					dma_addr_t dma_addr, u32 *result)  {  	struct nvme_command c; @@ -846,7 +852,7 @@ static int nvme_get_features(struct nvme_dev *dev, unsigned fid,  	c.features.prp1 = cpu_to_le64(dma_addr);  	c.features.fid = cpu_to_le32(fid); -	return nvme_submit_admin_cmd(dev, &c, NULL); +	return nvme_submit_admin_cmd(dev, &c, result);  }  static int nvme_set_features(struct nvme_dev *dev, unsigned fid, @@ -906,6 +912,10 @@ static void nvme_free_queue(struct nvme_dev *dev, int qid)  	spin_lock_irq(&nvmeq->q_lock);  	nvme_cancel_ios(nvmeq, false); +	while (bio_list_peek(&nvmeq->sq_cong)) { +		struct bio *bio = bio_list_pop(&nvmeq->sq_cong); +		bio_endio(bio, -EIO); +	}  	spin_unlock_irq(&nvmeq->q_lock);  	irq_set_affinity_hint(vector, NULL); @@ -1230,12 +1240,17 @@ static int nvme_user_admin_cmd(struct nvme_dev *dev,  	if (length != cmd.data_len)  		status = -ENOMEM;  	else -		status = nvme_submit_admin_cmd(dev, &c, NULL); +		status = nvme_submit_admin_cmd(dev, &c, &cmd.result);  	if (cmd.data_len) {  		nvme_unmap_user_pages(dev, cmd.opcode & 1, iod);  		nvme_free_iod(dev, iod);  	} + +	if (!status && copy_to_user(&ucmd->result, &cmd.result, +							sizeof(cmd.result))) +		status = -EFAULT; +  	return status;  } @@ -1523,9 +1538,9 @@ static int nvme_dev_add(struct nvme_dev *dev)  			continue;  		res = nvme_get_features(dev, NVME_FEAT_LBA_RANGE, i, -							dma_addr + 4096); +							dma_addr + 4096, NULL);  		if (res) -			continue; +			memset(mem + 4096, 0, 4096);  		ns = nvme_alloc_ns(dev, i, mem, mem + 4096);  		if (ns) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 6c81a4c040b..f556f8a8b3f 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1264,6 +1264,32 @@ static bool obj_request_done_test(struct rbd_obj_request *obj_request)  	return atomic_read(&obj_request->done) != 0;  } +static void +rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request) +{ +	dout("%s: obj %p img %p result %d %llu/%llu\n", __func__, +		obj_request, obj_request->img_request, obj_request->result, +		obj_request->xferred, obj_request->length); +	/* +	 * ENOENT means a hole in the image.  We zero-fill the +	 * entire length of the request.  A short read also implies +	 * zero-fill to the end of the request.  Either way we +	 * update the xferred count to indicate the whole request +	 * was satisfied. +	 */ +	BUG_ON(obj_request->type != OBJ_REQUEST_BIO); +	if (obj_request->result == -ENOENT) { +		zero_bio_chain(obj_request->bio_list, 0); +		obj_request->result = 0; +		obj_request->xferred = obj_request->length; +	} else if (obj_request->xferred < obj_request->length && +			!obj_request->result) { +		zero_bio_chain(obj_request->bio_list, obj_request->xferred); +		obj_request->xferred = obj_request->length; +	} +	obj_request_done_set(obj_request); +} +  static void rbd_obj_request_complete(struct rbd_obj_request *obj_request)  {  	dout("%s: obj %p cb %p\n", __func__, obj_request, @@ -1284,23 +1310,10 @@ static void rbd_osd_read_callback(struct rbd_obj_request *obj_request)  {  	dout("%s: obj %p result %d %llu/%llu\n", __func__, obj_request,  		obj_request->result, obj_request->xferred, obj_request->length); -	/* -	 * ENOENT means a hole in the object.  We zero-fill the -	 * entire length of the request.  A short read also implies -	 * zero-fill to the end of the request.  Either way we -	 * update the xferred count to indicate the whole request -	 * was satisfied. -	 */ -	if (obj_request->result == -ENOENT) { -		zero_bio_chain(obj_request->bio_list, 0); -		obj_request->result = 0; -		obj_request->xferred = obj_request->length; -	} else if (obj_request->xferred < obj_request->length && -			!obj_request->result) { -		zero_bio_chain(obj_request->bio_list, obj_request->xferred); -		obj_request->xferred = obj_request->length; -	} -	obj_request_done_set(obj_request); +	if (obj_request->img_request) +		rbd_img_obj_request_read_callback(obj_request); +	else +		obj_request_done_set(obj_request);  }  static void rbd_osd_write_callback(struct rbd_obj_request *obj_request) diff --git a/drivers/block/rsxx/Makefile b/drivers/block/rsxx/Makefile index f35cd0b71f7..b1c53c0aa45 100644 --- a/drivers/block/rsxx/Makefile +++ b/drivers/block/rsxx/Makefile @@ -1,2 +1,2 @@  obj-$(CONFIG_BLK_DEV_RSXX) += rsxx.o -rsxx-y := config.o core.o cregs.o dev.o dma.o +rsxx-objs := config.o core.o cregs.o dev.o dma.o diff --git a/drivers/block/rsxx/config.c b/drivers/block/rsxx/config.c index a295e7e9ee4..10cd530d3e1 100644 --- a/drivers/block/rsxx/config.c +++ b/drivers/block/rsxx/config.c @@ -29,15 +29,13 @@  #include "rsxx_priv.h"  #include "rsxx_cfg.h" -static void initialize_config(void *config) +static void initialize_config(struct rsxx_card_cfg *cfg)  { -	struct rsxx_card_cfg *cfg = config; -  	cfg->hdr.version = RSXX_CFG_VERSION;  	cfg->data.block_size        = RSXX_HW_BLK_SIZE;  	cfg->data.stripe_size       = RSXX_HW_BLK_SIZE; -	cfg->data.vendor_id         = RSXX_VENDOR_ID_TMS_IBM; +	cfg->data.vendor_id         = RSXX_VENDOR_ID_IBM;  	cfg->data.cache_order       = (-1);  	cfg->data.intr_coal.mode    = RSXX_INTR_COAL_DISABLED;  	cfg->data.intr_coal.count   = 0; @@ -181,7 +179,7 @@ int rsxx_load_config(struct rsxx_cardinfo *card)  	} else {  		dev_info(CARD_TO_DEV(card),  			"Initializing card configuration.\n"); -		initialize_config(card); +		initialize_config(&card->config);  		st = rsxx_save_config(card);  		if (st)  			return st; diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index e5162487686..5af21f2db29 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -30,6 +30,7 @@  #include <linux/reboot.h>  #include <linux/slab.h>  #include <linux/bitops.h> +#include <linux/delay.h>  #include <linux/genhd.h>  #include <linux/idr.h> @@ -39,8 +40,8 @@  #define NO_LEGACY 0 -MODULE_DESCRIPTION("IBM RamSan PCIe Flash SSD Device Driver"); -MODULE_AUTHOR("IBM <support@ramsan.com>"); +MODULE_DESCRIPTION("IBM FlashSystem 70/80 PCIe SSD Device Driver"); +MODULE_AUTHOR("Joshua Morris/Philip Kelleher, IBM");  MODULE_LICENSE("GPL");  MODULE_VERSION(DRIVER_VERSION); @@ -52,6 +53,13 @@ static DEFINE_IDA(rsxx_disk_ida);  static DEFINE_SPINLOCK(rsxx_ida_lock);  /*----------------- Interrupt Control & Handling -------------------*/ + +static void rsxx_mask_interrupts(struct rsxx_cardinfo *card) +{ +	card->isr_mask = 0; +	card->ier_mask = 0; +} +  static void __enable_intr(unsigned int *mask, unsigned int intr)  {  	*mask |= intr; @@ -71,7 +79,8 @@ static void __disable_intr(unsigned int *mask, unsigned int intr)   */  void rsxx_enable_ier(struct rsxx_cardinfo *card, unsigned int intr)  { -	if (unlikely(card->halt)) +	if (unlikely(card->halt) || +	    unlikely(card->eeh_state))  		return;  	__enable_intr(&card->ier_mask, intr); @@ -80,6 +89,9 @@ void rsxx_enable_ier(struct rsxx_cardinfo *card, unsigned int intr)  void rsxx_disable_ier(struct rsxx_cardinfo *card, unsigned int intr)  { +	if (unlikely(card->eeh_state)) +		return; +  	__disable_intr(&card->ier_mask, intr);  	iowrite32(card->ier_mask, card->regmap + IER);  } @@ -87,7 +99,8 @@ void rsxx_disable_ier(struct rsxx_cardinfo *card, unsigned int intr)  void rsxx_enable_ier_and_isr(struct rsxx_cardinfo *card,  				 unsigned int intr)  { -	if (unlikely(card->halt)) +	if (unlikely(card->halt) || +	    unlikely(card->eeh_state))  		return;  	__enable_intr(&card->isr_mask, intr); @@ -97,6 +110,9 @@ void rsxx_enable_ier_and_isr(struct rsxx_cardinfo *card,  void rsxx_disable_ier_and_isr(struct rsxx_cardinfo *card,  				  unsigned int intr)  { +	if (unlikely(card->eeh_state)) +		return; +  	__disable_intr(&card->isr_mask, intr);  	__disable_intr(&card->ier_mask, intr);  	iowrite32(card->ier_mask, card->regmap + IER); @@ -115,6 +131,9 @@ static irqreturn_t rsxx_isr(int irq, void *pdata)  	do {  		reread_isr = 0; +		if (unlikely(card->eeh_state)) +			break; +  		isr = ioread32(card->regmap + ISR);  		if (isr == 0xffffffff) {  			/* @@ -161,9 +180,9 @@ static irqreturn_t rsxx_isr(int irq, void *pdata)  }  /*----------------- Card Event Handler -------------------*/ -static char *rsxx_card_state_to_str(unsigned int state) +static const char * const rsxx_card_state_to_str(unsigned int state)  { -	static char *state_strings[] = { +	static const char * const state_strings[] = {  		"Unknown", "Shutdown", "Starting", "Formatting",  		"Uninitialized", "Good", "Shutting Down",  		"Fault", "Read Only Fault", "dStroying" @@ -304,6 +323,192 @@ static int card_shutdown(struct rsxx_cardinfo *card)  	return 0;  } +static int rsxx_eeh_frozen(struct pci_dev *dev) +{ +	struct rsxx_cardinfo *card = pci_get_drvdata(dev); +	int i; +	int st; + +	dev_warn(&dev->dev, "IBM FlashSystem PCI: preparing for slot reset.\n"); + +	card->eeh_state = 1; +	rsxx_mask_interrupts(card); + +	/* +	 * We need to guarantee that the write for eeh_state and masking +	 * interrupts does not become reordered. This will prevent a possible +	 * race condition with the EEH code. +	 */ +	wmb(); + +	pci_disable_device(dev); + +	st = rsxx_eeh_save_issued_dmas(card); +	if (st) +		return st; + +	rsxx_eeh_save_issued_creg(card); + +	for (i = 0; i < card->n_targets; i++) { +		if (card->ctrl[i].status.buf) +			pci_free_consistent(card->dev, STATUS_BUFFER_SIZE8, +					    card->ctrl[i].status.buf, +					    card->ctrl[i].status.dma_addr); +		if (card->ctrl[i].cmd.buf) +			pci_free_consistent(card->dev, COMMAND_BUFFER_SIZE8, +					    card->ctrl[i].cmd.buf, +					    card->ctrl[i].cmd.dma_addr); +	} + +	return 0; +} + +static void rsxx_eeh_failure(struct pci_dev *dev) +{ +	struct rsxx_cardinfo *card = pci_get_drvdata(dev); +	int i; + +	dev_err(&dev->dev, "IBM FlashSystem PCI: disabling failed card.\n"); + +	card->eeh_state = 1; + +	for (i = 0; i < card->n_targets; i++) +		del_timer_sync(&card->ctrl[i].activity_timer); + +	rsxx_eeh_cancel_dmas(card); +} + +static int rsxx_eeh_fifo_flush_poll(struct rsxx_cardinfo *card) +{ +	unsigned int status; +	int iter = 0; + +	/* We need to wait for the hardware to reset */ +	while (iter++ < 10) { +		status = ioread32(card->regmap + PCI_RECONFIG); + +		if (status & RSXX_FLUSH_BUSY) { +			ssleep(1); +			continue; +		} + +		if (status & RSXX_FLUSH_TIMEOUT) +			dev_warn(CARD_TO_DEV(card), "HW: flash controller timeout\n"); +		return 0; +	} + +	/* Hardware failed resetting itself. */ +	return -1; +} + +static pci_ers_result_t rsxx_error_detected(struct pci_dev *dev, +					    enum pci_channel_state error) +{ +	int st; + +	if (dev->revision < RSXX_EEH_SUPPORT) +		return PCI_ERS_RESULT_NONE; + +	if (error == pci_channel_io_perm_failure) { +		rsxx_eeh_failure(dev); +		return PCI_ERS_RESULT_DISCONNECT; +	} + +	st = rsxx_eeh_frozen(dev); +	if (st) { +		dev_err(&dev->dev, "Slot reset setup failed\n"); +		rsxx_eeh_failure(dev); +		return PCI_ERS_RESULT_DISCONNECT; +	} + +	return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t rsxx_slot_reset(struct pci_dev *dev) +{ +	struct rsxx_cardinfo *card = pci_get_drvdata(dev); +	unsigned long flags; +	int i; +	int st; + +	dev_warn(&dev->dev, +		"IBM FlashSystem PCI: recovering from slot reset.\n"); + +	st = pci_enable_device(dev); +	if (st) +		goto failed_hw_setup; + +	pci_set_master(dev); + +	st = rsxx_eeh_fifo_flush_poll(card); +	if (st) +		goto failed_hw_setup; + +	rsxx_dma_queue_reset(card); + +	for (i = 0; i < card->n_targets; i++) { +		st = rsxx_hw_buffers_init(dev, &card->ctrl[i]); +		if (st) +			goto failed_hw_buffers_init; +	} + +	if (card->config_valid) +		rsxx_dma_configure(card); + +	/* Clears the ISR register from spurious interrupts */ +	st = ioread32(card->regmap + ISR); + +	card->eeh_state = 0; + +	st = rsxx_eeh_remap_dmas(card); +	if (st) +		goto failed_remap_dmas; + +	spin_lock_irqsave(&card->irq_lock, flags); +	if (card->n_targets & RSXX_MAX_TARGETS) +		rsxx_enable_ier_and_isr(card, CR_INTR_ALL_G); +	else +		rsxx_enable_ier_and_isr(card, CR_INTR_ALL_C); +	spin_unlock_irqrestore(&card->irq_lock, flags); + +	rsxx_kick_creg_queue(card); + +	for (i = 0; i < card->n_targets; i++) { +		spin_lock(&card->ctrl[i].queue_lock); +		if (list_empty(&card->ctrl[i].queue)) { +			spin_unlock(&card->ctrl[i].queue_lock); +			continue; +		} +		spin_unlock(&card->ctrl[i].queue_lock); + +		queue_work(card->ctrl[i].issue_wq, +				&card->ctrl[i].issue_dma_work); +	} + +	dev_info(&dev->dev, "IBM FlashSystem PCI: recovery complete.\n"); + +	return PCI_ERS_RESULT_RECOVERED; + +failed_hw_buffers_init: +failed_remap_dmas: +	for (i = 0; i < card->n_targets; i++) { +		if (card->ctrl[i].status.buf) +			pci_free_consistent(card->dev, +					STATUS_BUFFER_SIZE8, +					card->ctrl[i].status.buf, +					card->ctrl[i].status.dma_addr); +		if (card->ctrl[i].cmd.buf) +			pci_free_consistent(card->dev, +					COMMAND_BUFFER_SIZE8, +					card->ctrl[i].cmd.buf, +					card->ctrl[i].cmd.dma_addr); +	} +failed_hw_setup: +	rsxx_eeh_failure(dev); +	return PCI_ERS_RESULT_DISCONNECT; + +} +  /*----------------- Driver Initialization & Setup -------------------*/  /* Returns:   0 if the driver is compatible with the device  	     -1 if the driver is NOT compatible with the device */ @@ -383,6 +588,7 @@ static int rsxx_pci_probe(struct pci_dev *dev,  	spin_lock_init(&card->irq_lock);  	card->halt = 0; +	card->eeh_state = 0;  	spin_lock_irq(&card->irq_lock);  	rsxx_disable_ier_and_isr(card, CR_INTR_ALL); @@ -538,9 +744,6 @@ static void rsxx_pci_remove(struct pci_dev *dev)  	rsxx_disable_ier_and_isr(card, CR_INTR_EVENT);  	spin_unlock_irqrestore(&card->irq_lock, flags); -	/* Prevent work_structs from re-queuing themselves. */ -	card->halt = 1; -  	cancel_work_sync(&card->event_work);  	rsxx_destroy_dev(card); @@ -549,6 +752,10 @@ static void rsxx_pci_remove(struct pci_dev *dev)  	spin_lock_irqsave(&card->irq_lock, flags);  	rsxx_disable_ier_and_isr(card, CR_INTR_ALL);  	spin_unlock_irqrestore(&card->irq_lock, flags); + +	/* Prevent work_structs from re-queuing themselves. */ +	card->halt = 1; +  	free_irq(dev->irq, card);  	if (!force_legacy) @@ -592,11 +799,14 @@ static void rsxx_pci_shutdown(struct pci_dev *dev)  	card_shutdown(card);  } +static const struct pci_error_handlers rsxx_err_handler = { +	.error_detected = rsxx_error_detected, +	.slot_reset     = rsxx_slot_reset, +}; +  static DEFINE_PCI_DEVICE_TABLE(rsxx_pci_ids) = { -	{PCI_DEVICE(PCI_VENDOR_ID_TMS_IBM, PCI_DEVICE_ID_RS70_FLASH)}, -	{PCI_DEVICE(PCI_VENDOR_ID_TMS_IBM, PCI_DEVICE_ID_RS70D_FLASH)}, -	{PCI_DEVICE(PCI_VENDOR_ID_TMS_IBM, PCI_DEVICE_ID_RS80_FLASH)}, -	{PCI_DEVICE(PCI_VENDOR_ID_TMS_IBM, PCI_DEVICE_ID_RS81_FLASH)}, +	{PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_FS70_FLASH)}, +	{PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_FS80_FLASH)},  	{0,},  }; @@ -609,6 +819,7 @@ static struct pci_driver rsxx_pci_driver = {  	.remove		= rsxx_pci_remove,  	.suspend	= rsxx_pci_suspend,  	.shutdown	= rsxx_pci_shutdown, +	.err_handler    = &rsxx_err_handler,  };  static int __init rsxx_core_init(void) diff --git a/drivers/block/rsxx/cregs.c b/drivers/block/rsxx/cregs.c index 80bbe639fcc..4b5c020a0a6 100644 --- a/drivers/block/rsxx/cregs.c +++ b/drivers/block/rsxx/cregs.c @@ -58,7 +58,7 @@ static struct kmem_cache *creg_cmd_pool;  #error Unknown endianess!!! Aborting...  #endif -static void copy_to_creg_data(struct rsxx_cardinfo *card, +static int copy_to_creg_data(struct rsxx_cardinfo *card,  			      int cnt8,  			      void *buf,  			      unsigned int stream) @@ -66,6 +66,9 @@ static void copy_to_creg_data(struct rsxx_cardinfo *card,  	int i = 0;  	u32 *data = buf; +	if (unlikely(card->eeh_state)) +		return -EIO; +  	for (i = 0; cnt8 > 0; i++, cnt8 -= 4) {  		/*  		 * Firmware implementation makes it necessary to byte swap on @@ -76,10 +79,12 @@ static void copy_to_creg_data(struct rsxx_cardinfo *card,  		else  			iowrite32(data[i], card->regmap + CREG_DATA(i));  	} + +	return 0;  } -static void copy_from_creg_data(struct rsxx_cardinfo *card, +static int copy_from_creg_data(struct rsxx_cardinfo *card,  				int cnt8,  				void *buf,  				unsigned int stream) @@ -87,6 +92,9 @@ static void copy_from_creg_data(struct rsxx_cardinfo *card,  	int i = 0;  	u32 *data = buf; +	if (unlikely(card->eeh_state)) +		return -EIO; +  	for (i = 0; cnt8 > 0; i++, cnt8 -= 4) {  		/*  		 * Firmware implementation makes it necessary to byte swap on @@ -97,41 +105,31 @@ static void copy_from_creg_data(struct rsxx_cardinfo *card,  		else  			data[i] = ioread32(card->regmap + CREG_DATA(i));  	} -} - -static struct creg_cmd *pop_active_cmd(struct rsxx_cardinfo *card) -{ -	struct creg_cmd *cmd; -	/* -	 * Spin lock is needed because this can be called in atomic/interrupt -	 * context. -	 */ -	spin_lock_bh(&card->creg_ctrl.lock); -	cmd = card->creg_ctrl.active_cmd; -	card->creg_ctrl.active_cmd = NULL; -	spin_unlock_bh(&card->creg_ctrl.lock); - -	return cmd; +	return 0;  }  static void creg_issue_cmd(struct rsxx_cardinfo *card, struct creg_cmd *cmd)  { +	int st; + +	if (unlikely(card->eeh_state)) +		return; +  	iowrite32(cmd->addr, card->regmap + CREG_ADD);  	iowrite32(cmd->cnt8, card->regmap + CREG_CNT);  	if (cmd->op == CREG_OP_WRITE) { -		if (cmd->buf) -			copy_to_creg_data(card, cmd->cnt8, -					  cmd->buf, cmd->stream); +		if (cmd->buf) { +			st = copy_to_creg_data(card, cmd->cnt8, +					       cmd->buf, cmd->stream); +			if (st) +				return; +		}  	} -	/* -	 * Data copy must complete before initiating the command. This is -	 * needed for weakly ordered processors (i.e. PowerPC), so that all -	 * neccessary registers are written before we kick the hardware. -	 */ -	wmb(); +	if (unlikely(card->eeh_state)) +		return;  	/* Setting the valid bit will kick off the command. */  	iowrite32(cmd->op, card->regmap + CREG_CMD); @@ -196,11 +194,11 @@ static int creg_queue_cmd(struct rsxx_cardinfo *card,  	cmd->cb_private = cb_private;  	cmd->status	= 0; -	spin_lock(&card->creg_ctrl.lock); +	spin_lock_bh(&card->creg_ctrl.lock);  	list_add_tail(&cmd->list, &card->creg_ctrl.queue);  	card->creg_ctrl.q_depth++;  	creg_kick_queue(card); -	spin_unlock(&card->creg_ctrl.lock); +	spin_unlock_bh(&card->creg_ctrl.lock);  	return 0;  } @@ -210,7 +208,11 @@ static void creg_cmd_timed_out(unsigned long data)  	struct rsxx_cardinfo *card = (struct rsxx_cardinfo *) data;  	struct creg_cmd *cmd; -	cmd = pop_active_cmd(card); +	spin_lock(&card->creg_ctrl.lock); +	cmd = card->creg_ctrl.active_cmd; +	card->creg_ctrl.active_cmd = NULL; +	spin_unlock(&card->creg_ctrl.lock); +  	if (cmd == NULL) {  		card->creg_ctrl.creg_stats.creg_timeout++;  		dev_warn(CARD_TO_DEV(card), @@ -247,7 +249,11 @@ static void creg_cmd_done(struct work_struct *work)  	if (del_timer_sync(&card->creg_ctrl.cmd_timer) == 0)  		card->creg_ctrl.creg_stats.failed_cancel_timer++; -	cmd = pop_active_cmd(card); +	spin_lock_bh(&card->creg_ctrl.lock); +	cmd = card->creg_ctrl.active_cmd; +	card->creg_ctrl.active_cmd = NULL; +	spin_unlock_bh(&card->creg_ctrl.lock); +  	if (cmd == NULL) {  		dev_err(CARD_TO_DEV(card),  			"Spurious creg interrupt!\n"); @@ -287,7 +293,7 @@ static void creg_cmd_done(struct work_struct *work)  			goto creg_done;  		} -		copy_from_creg_data(card, cnt8, cmd->buf, cmd->stream); +		st = copy_from_creg_data(card, cnt8, cmd->buf, cmd->stream);  	}  creg_done: @@ -296,10 +302,10 @@ creg_done:  	kmem_cache_free(creg_cmd_pool, cmd); -	spin_lock(&card->creg_ctrl.lock); +	spin_lock_bh(&card->creg_ctrl.lock);  	card->creg_ctrl.active = 0;  	creg_kick_queue(card); -	spin_unlock(&card->creg_ctrl.lock); +	spin_unlock_bh(&card->creg_ctrl.lock);  }  static void creg_reset(struct rsxx_cardinfo *card) @@ -324,7 +330,7 @@ static void creg_reset(struct rsxx_cardinfo *card)  		"Resetting creg interface for recovery\n");  	/* Cancel outstanding commands */ -	spin_lock(&card->creg_ctrl.lock); +	spin_lock_bh(&card->creg_ctrl.lock);  	list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) {  		list_del(&cmd->list);  		card->creg_ctrl.q_depth--; @@ -345,7 +351,7 @@ static void creg_reset(struct rsxx_cardinfo *card)  		card->creg_ctrl.active = 0;  	} -	spin_unlock(&card->creg_ctrl.lock); +	spin_unlock_bh(&card->creg_ctrl.lock);  	card->creg_ctrl.reset = 0;  	spin_lock_irqsave(&card->irq_lock, flags); @@ -399,12 +405,12 @@ static int __issue_creg_rw(struct rsxx_cardinfo *card,  		return st;  	/* -	 * This timeout is neccessary for unresponsive hardware. The additional +	 * This timeout is necessary for unresponsive hardware. The additional  	 * 20 seconds to used to guarantee that each cregs requests has time to  	 * complete.  	 */ -	timeout = msecs_to_jiffies((CREG_TIMEOUT_MSEC * -				card->creg_ctrl.q_depth) + 20000); +	timeout = msecs_to_jiffies(CREG_TIMEOUT_MSEC * +				   card->creg_ctrl.q_depth + 20000);  	/*  	 * The creg interface is guaranteed to complete. It has a timeout @@ -690,6 +696,32 @@ int rsxx_reg_access(struct rsxx_cardinfo *card,  	return 0;  } +void rsxx_eeh_save_issued_creg(struct rsxx_cardinfo *card) +{ +	struct creg_cmd *cmd = NULL; + +	cmd = card->creg_ctrl.active_cmd; +	card->creg_ctrl.active_cmd = NULL; + +	if (cmd) { +		del_timer_sync(&card->creg_ctrl.cmd_timer); + +		spin_lock_bh(&card->creg_ctrl.lock); +		list_add(&cmd->list, &card->creg_ctrl.queue); +		card->creg_ctrl.q_depth++; +		card->creg_ctrl.active = 0; +		spin_unlock_bh(&card->creg_ctrl.lock); +	} +} + +void rsxx_kick_creg_queue(struct rsxx_cardinfo *card) +{ +	spin_lock_bh(&card->creg_ctrl.lock); +	if (!list_empty(&card->creg_ctrl.queue)) +		creg_kick_queue(card); +	spin_unlock_bh(&card->creg_ctrl.lock); +} +  /*------------ Initialization & Setup --------------*/  int rsxx_creg_setup(struct rsxx_cardinfo *card)  { @@ -712,7 +744,7 @@ void rsxx_creg_destroy(struct rsxx_cardinfo *card)  	int cnt = 0;  	/* Cancel outstanding commands */ -	spin_lock(&card->creg_ctrl.lock); +	spin_lock_bh(&card->creg_ctrl.lock);  	list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) {  		list_del(&cmd->list);  		if (cmd->cb) @@ -737,7 +769,7 @@ void rsxx_creg_destroy(struct rsxx_cardinfo *card)  			"Canceled active creg command\n");  		kmem_cache_free(creg_cmd_pool, cmd);  	} -	spin_unlock(&card->creg_ctrl.lock); +	spin_unlock_bh(&card->creg_ctrl.lock);  	cancel_work_sync(&card->creg_ctrl.done_work);  } diff --git a/drivers/block/rsxx/dma.c b/drivers/block/rsxx/dma.c index 63176e67662..0607513cfb4 100644 --- a/drivers/block/rsxx/dma.c +++ b/drivers/block/rsxx/dma.c @@ -28,7 +28,7 @@  struct rsxx_dma {  	struct list_head	 list;  	u8			 cmd; -	unsigned int		 laddr;     /* Logical address on the ramsan */ +	unsigned int		 laddr;     /* Logical address */  	struct {  		u32		 off;  		u32		 cnt; @@ -81,9 +81,6 @@ enum rsxx_hw_status {  	HW_STATUS_FAULT		= 0x08,  }; -#define STATUS_BUFFER_SIZE8     4096 -#define COMMAND_BUFFER_SIZE8    4096 -  static struct kmem_cache *rsxx_dma_pool;  struct dma_tracker { @@ -122,7 +119,7 @@ static unsigned int rsxx_get_dma_tgt(struct rsxx_cardinfo *card, u64 addr8)  	return tgt;  } -static void rsxx_dma_queue_reset(struct rsxx_cardinfo *card) +void rsxx_dma_queue_reset(struct rsxx_cardinfo *card)  {  	/* Reset all DMA Command/Status Queues */  	iowrite32(DMA_QUEUE_RESET, card->regmap + RESET); @@ -210,7 +207,8 @@ static void dma_intr_coal_auto_tune(struct rsxx_cardinfo *card)  	u32 q_depth = 0;  	u32 intr_coal; -	if (card->config.data.intr_coal.mode != RSXX_INTR_COAL_AUTO_TUNE) +	if (card->config.data.intr_coal.mode != RSXX_INTR_COAL_AUTO_TUNE || +	    unlikely(card->eeh_state))  		return;  	for (i = 0; i < card->n_targets; i++) @@ -223,31 +221,26 @@ static void dma_intr_coal_auto_tune(struct rsxx_cardinfo *card)  }  /*----------------- RSXX DMA Handling -------------------*/ -static void rsxx_complete_dma(struct rsxx_cardinfo *card, +static void rsxx_complete_dma(struct rsxx_dma_ctrl *ctrl,  				  struct rsxx_dma *dma,  				  unsigned int status)  {  	if (status & DMA_SW_ERR) -		printk_ratelimited(KERN_ERR -				   "SW Error in DMA(cmd x%02x, laddr x%08x)\n", -				   dma->cmd, dma->laddr); +		ctrl->stats.dma_sw_err++;  	if (status & DMA_HW_FAULT) -		printk_ratelimited(KERN_ERR -				   "HW Fault in DMA(cmd x%02x, laddr x%08x)\n", -				   dma->cmd, dma->laddr); +		ctrl->stats.dma_hw_fault++;  	if (status & DMA_CANCELLED) -		printk_ratelimited(KERN_ERR -				   "DMA Cancelled(cmd x%02x, laddr x%08x)\n", -				   dma->cmd, dma->laddr); +		ctrl->stats.dma_cancelled++;  	if (dma->dma_addr) -		pci_unmap_page(card->dev, dma->dma_addr, get_dma_size(dma), +		pci_unmap_page(ctrl->card->dev, dma->dma_addr, +			       get_dma_size(dma),  			       dma->cmd == HW_CMD_BLK_WRITE ?  					   PCI_DMA_TODEVICE :  					   PCI_DMA_FROMDEVICE);  	if (dma->cb) -		dma->cb(card, dma->cb_data, status ? 1 : 0); +		dma->cb(ctrl->card, dma->cb_data, status ? 1 : 0);  	kmem_cache_free(rsxx_dma_pool, dma);  } @@ -330,14 +323,15 @@ static void rsxx_handle_dma_error(struct rsxx_dma_ctrl *ctrl,  	if (requeue_cmd)  		rsxx_requeue_dma(ctrl, dma);  	else -		rsxx_complete_dma(ctrl->card, dma, status); +		rsxx_complete_dma(ctrl, dma, status);  }  static void dma_engine_stalled(unsigned long data)  {  	struct rsxx_dma_ctrl *ctrl = (struct rsxx_dma_ctrl *)data; -	if (atomic_read(&ctrl->stats.hw_q_depth) == 0) +	if (atomic_read(&ctrl->stats.hw_q_depth) == 0 || +	    unlikely(ctrl->card->eeh_state))  		return;  	if (ctrl->cmd.idx != ioread32(ctrl->regmap + SW_CMD_IDX)) { @@ -369,7 +363,8 @@ static void rsxx_issue_dmas(struct work_struct *work)  	ctrl = container_of(work, struct rsxx_dma_ctrl, issue_dma_work);  	hw_cmd_buf = ctrl->cmd.buf; -	if (unlikely(ctrl->card->halt)) +	if (unlikely(ctrl->card->halt) || +	    unlikely(ctrl->card->eeh_state))  		return;  	while (1) { @@ -397,7 +392,7 @@ static void rsxx_issue_dmas(struct work_struct *work)  		 */  		if (unlikely(ctrl->card->dma_fault)) {  			push_tracker(ctrl->trackers, tag); -			rsxx_complete_dma(ctrl->card, dma, DMA_CANCELLED); +			rsxx_complete_dma(ctrl, dma, DMA_CANCELLED);  			continue;  		} @@ -432,19 +427,15 @@ static void rsxx_issue_dmas(struct work_struct *work)  	/* Let HW know we've queued commands. */  	if (cmds_pending) { -		/* -		 * We must guarantee that the CPU writes to 'ctrl->cmd.buf' -		 * (which is in PCI-consistent system-memory) from the loop -		 * above make it into the coherency domain before the -		 * following PIO "trigger" updating the cmd.idx.  A WMB is -		 * sufficient. We need not explicitly CPU cache-flush since -		 * the memory is a PCI-consistent (ie; coherent) mapping. -		 */ -		wmb(); -  		atomic_add(cmds_pending, &ctrl->stats.hw_q_depth);  		mod_timer(&ctrl->activity_timer,  			  jiffies + DMA_ACTIVITY_TIMEOUT); + +		if (unlikely(ctrl->card->eeh_state)) { +			del_timer_sync(&ctrl->activity_timer); +			return; +		} +  		iowrite32(ctrl->cmd.idx, ctrl->regmap + SW_CMD_IDX);  	}  } @@ -463,7 +454,8 @@ static void rsxx_dma_done(struct work_struct *work)  	hw_st_buf = ctrl->status.buf;  	if (unlikely(ctrl->card->halt) || -	    unlikely(ctrl->card->dma_fault)) +	    unlikely(ctrl->card->dma_fault) || +	    unlikely(ctrl->card->eeh_state))  		return;  	count = le16_to_cpu(hw_st_buf[ctrl->status.idx].count); @@ -508,7 +500,7 @@ static void rsxx_dma_done(struct work_struct *work)  		if (status)  			rsxx_handle_dma_error(ctrl, dma, status);  		else -			rsxx_complete_dma(ctrl->card, dma, 0); +			rsxx_complete_dma(ctrl, dma, 0);  		push_tracker(ctrl->trackers, tag); @@ -727,20 +719,54 @@ bvec_err:  /*----------------- DMA Engine Initialization & Setup -------------------*/ +int rsxx_hw_buffers_init(struct pci_dev *dev, struct rsxx_dma_ctrl *ctrl) +{ +	ctrl->status.buf = pci_alloc_consistent(dev, STATUS_BUFFER_SIZE8, +				&ctrl->status.dma_addr); +	ctrl->cmd.buf = pci_alloc_consistent(dev, COMMAND_BUFFER_SIZE8, +				&ctrl->cmd.dma_addr); +	if (ctrl->status.buf == NULL || ctrl->cmd.buf == NULL) +		return -ENOMEM; + +	memset(ctrl->status.buf, 0xac, STATUS_BUFFER_SIZE8); +	iowrite32(lower_32_bits(ctrl->status.dma_addr), +		ctrl->regmap + SB_ADD_LO); +	iowrite32(upper_32_bits(ctrl->status.dma_addr), +		ctrl->regmap + SB_ADD_HI); + +	memset(ctrl->cmd.buf, 0x83, COMMAND_BUFFER_SIZE8); +	iowrite32(lower_32_bits(ctrl->cmd.dma_addr), ctrl->regmap + CB_ADD_LO); +	iowrite32(upper_32_bits(ctrl->cmd.dma_addr), ctrl->regmap + CB_ADD_HI); + +	ctrl->status.idx = ioread32(ctrl->regmap + HW_STATUS_CNT); +	if (ctrl->status.idx > RSXX_MAX_OUTSTANDING_CMDS) { +		dev_crit(&dev->dev, "Failed reading status cnt x%x\n", +			ctrl->status.idx); +		return -EINVAL; +	} +	iowrite32(ctrl->status.idx, ctrl->regmap + HW_STATUS_CNT); +	iowrite32(ctrl->status.idx, ctrl->regmap + SW_STATUS_CNT); + +	ctrl->cmd.idx = ioread32(ctrl->regmap + HW_CMD_IDX); +	if (ctrl->cmd.idx > RSXX_MAX_OUTSTANDING_CMDS) { +		dev_crit(&dev->dev, "Failed reading cmd cnt x%x\n", +			ctrl->status.idx); +		return -EINVAL; +	} +	iowrite32(ctrl->cmd.idx, ctrl->regmap + HW_CMD_IDX); +	iowrite32(ctrl->cmd.idx, ctrl->regmap + SW_CMD_IDX); + +	return 0; +} +  static int rsxx_dma_ctrl_init(struct pci_dev *dev,  				  struct rsxx_dma_ctrl *ctrl)  {  	int i; +	int st;  	memset(&ctrl->stats, 0, sizeof(ctrl->stats)); -	ctrl->status.buf = pci_alloc_consistent(dev, STATUS_BUFFER_SIZE8, -						&ctrl->status.dma_addr); -	ctrl->cmd.buf = pci_alloc_consistent(dev, COMMAND_BUFFER_SIZE8, -					     &ctrl->cmd.dma_addr); -	if (ctrl->status.buf == NULL || ctrl->cmd.buf == NULL) -		return -ENOMEM; -  	ctrl->trackers = vmalloc(DMA_TRACKER_LIST_SIZE8);  	if (!ctrl->trackers)  		return -ENOMEM; @@ -770,35 +796,9 @@ static int rsxx_dma_ctrl_init(struct pci_dev *dev,  	INIT_WORK(&ctrl->issue_dma_work, rsxx_issue_dmas);  	INIT_WORK(&ctrl->dma_done_work, rsxx_dma_done); -	memset(ctrl->status.buf, 0xac, STATUS_BUFFER_SIZE8); -	iowrite32(lower_32_bits(ctrl->status.dma_addr), -		  ctrl->regmap + SB_ADD_LO); -	iowrite32(upper_32_bits(ctrl->status.dma_addr), -		  ctrl->regmap + SB_ADD_HI); - -	memset(ctrl->cmd.buf, 0x83, COMMAND_BUFFER_SIZE8); -	iowrite32(lower_32_bits(ctrl->cmd.dma_addr), ctrl->regmap + CB_ADD_LO); -	iowrite32(upper_32_bits(ctrl->cmd.dma_addr), ctrl->regmap + CB_ADD_HI); - -	ctrl->status.idx = ioread32(ctrl->regmap + HW_STATUS_CNT); -	if (ctrl->status.idx > RSXX_MAX_OUTSTANDING_CMDS) { -		dev_crit(&dev->dev, "Failed reading status cnt x%x\n", -			 ctrl->status.idx); -		return -EINVAL; -	} -	iowrite32(ctrl->status.idx, ctrl->regmap + HW_STATUS_CNT); -	iowrite32(ctrl->status.idx, ctrl->regmap + SW_STATUS_CNT); - -	ctrl->cmd.idx = ioread32(ctrl->regmap + HW_CMD_IDX); -	if (ctrl->cmd.idx > RSXX_MAX_OUTSTANDING_CMDS) { -		dev_crit(&dev->dev, "Failed reading cmd cnt x%x\n", -			 ctrl->status.idx); -		return -EINVAL; -	} -	iowrite32(ctrl->cmd.idx, ctrl->regmap + HW_CMD_IDX); -	iowrite32(ctrl->cmd.idx, ctrl->regmap + SW_CMD_IDX); - -	wmb(); +	st = rsxx_hw_buffers_init(dev, ctrl); +	if (st) +		return st;  	return 0;  } @@ -834,7 +834,7 @@ static int rsxx_dma_stripe_setup(struct rsxx_cardinfo *card,  	return 0;  } -static int rsxx_dma_configure(struct rsxx_cardinfo *card) +int rsxx_dma_configure(struct rsxx_cardinfo *card)  {  	u32 intr_coal; @@ -980,6 +980,103 @@ void rsxx_dma_destroy(struct rsxx_cardinfo *card)  	}  } +int rsxx_eeh_save_issued_dmas(struct rsxx_cardinfo *card) +{ +	int i; +	int j; +	int cnt; +	struct rsxx_dma *dma; +	struct list_head *issued_dmas; + +	issued_dmas = kzalloc(sizeof(*issued_dmas) * card->n_targets, +			      GFP_KERNEL); +	if (!issued_dmas) +		return -ENOMEM; + +	for (i = 0; i < card->n_targets; i++) { +		INIT_LIST_HEAD(&issued_dmas[i]); +		cnt = 0; +		for (j = 0; j < RSXX_MAX_OUTSTANDING_CMDS; j++) { +			dma = get_tracker_dma(card->ctrl[i].trackers, j); +			if (dma == NULL) +				continue; + +			if (dma->cmd == HW_CMD_BLK_WRITE) +				card->ctrl[i].stats.writes_issued--; +			else if (dma->cmd == HW_CMD_BLK_DISCARD) +				card->ctrl[i].stats.discards_issued--; +			else +				card->ctrl[i].stats.reads_issued--; + +			list_add_tail(&dma->list, &issued_dmas[i]); +			push_tracker(card->ctrl[i].trackers, j); +			cnt++; +		} + +		spin_lock(&card->ctrl[i].queue_lock); +		list_splice(&issued_dmas[i], &card->ctrl[i].queue); + +		atomic_sub(cnt, &card->ctrl[i].stats.hw_q_depth); +		card->ctrl[i].stats.sw_q_depth += cnt; +		card->ctrl[i].e_cnt = 0; + +		list_for_each_entry(dma, &card->ctrl[i].queue, list) { +			if (dma->dma_addr) +				pci_unmap_page(card->dev, dma->dma_addr, +					       get_dma_size(dma), +					       dma->cmd == HW_CMD_BLK_WRITE ? +					       PCI_DMA_TODEVICE : +					       PCI_DMA_FROMDEVICE); +		} +		spin_unlock(&card->ctrl[i].queue_lock); +	} + +	kfree(issued_dmas); + +	return 0; +} + +void rsxx_eeh_cancel_dmas(struct rsxx_cardinfo *card) +{ +	struct rsxx_dma *dma; +	struct rsxx_dma *tmp; +	int i; + +	for (i = 0; i < card->n_targets; i++) { +		spin_lock(&card->ctrl[i].queue_lock); +		list_for_each_entry_safe(dma, tmp, &card->ctrl[i].queue, list) { +			list_del(&dma->list); + +			rsxx_complete_dma(&card->ctrl[i], dma, DMA_CANCELLED); +		} +		spin_unlock(&card->ctrl[i].queue_lock); +	} +} + +int rsxx_eeh_remap_dmas(struct rsxx_cardinfo *card) +{ +	struct rsxx_dma *dma; +	int i; + +	for (i = 0; i < card->n_targets; i++) { +		spin_lock(&card->ctrl[i].queue_lock); +		list_for_each_entry(dma, &card->ctrl[i].queue, list) { +			dma->dma_addr = pci_map_page(card->dev, dma->page, +					dma->pg_off, get_dma_size(dma), +					dma->cmd == HW_CMD_BLK_WRITE ? +					PCI_DMA_TODEVICE : +					PCI_DMA_FROMDEVICE); +			if (!dma->dma_addr) { +				spin_unlock(&card->ctrl[i].queue_lock); +				kmem_cache_free(rsxx_dma_pool, dma); +				return -ENOMEM; +			} +		} +		spin_unlock(&card->ctrl[i].queue_lock); +	} + +	return 0; +}  int rsxx_dma_init(void)  { diff --git a/drivers/block/rsxx/rsxx.h b/drivers/block/rsxx/rsxx.h index 2e50b65902b..24ba3642bd8 100644 --- a/drivers/block/rsxx/rsxx.h +++ b/drivers/block/rsxx/rsxx.h @@ -27,15 +27,17 @@  /*----------------- IOCTL Definitions -------------------*/ +#define RSXX_MAX_DATA 8 +  struct rsxx_reg_access {  	__u32 addr;  	__u32 cnt;  	__u32 stat;  	__u32 stream; -	__u32 data[8]; +	__u32 data[RSXX_MAX_DATA];  }; -#define RSXX_MAX_REG_CNT	(8 * (sizeof(__u32))) +#define RSXX_MAX_REG_CNT	(RSXX_MAX_DATA * (sizeof(__u32)))  #define RSXX_IOC_MAGIC 'r' diff --git a/drivers/block/rsxx/rsxx_cfg.h b/drivers/block/rsxx/rsxx_cfg.h index c025fe5fdb7..f384c943846 100644 --- a/drivers/block/rsxx/rsxx_cfg.h +++ b/drivers/block/rsxx/rsxx_cfg.h @@ -58,7 +58,7 @@ struct rsxx_card_cfg {  };  /* Vendor ID Values */ -#define RSXX_VENDOR_ID_TMS_IBM		0 +#define RSXX_VENDOR_ID_IBM		0  #define RSXX_VENDOR_ID_DSI		1  #define RSXX_VENDOR_COUNT		2 diff --git a/drivers/block/rsxx/rsxx_priv.h b/drivers/block/rsxx/rsxx_priv.h index a1ac907d8f4..382e8bf5c03 100644 --- a/drivers/block/rsxx/rsxx_priv.h +++ b/drivers/block/rsxx/rsxx_priv.h @@ -45,16 +45,13 @@  struct proc_cmd; -#define PCI_VENDOR_ID_TMS_IBM		0x15B6 -#define PCI_DEVICE_ID_RS70_FLASH	0x0019 -#define PCI_DEVICE_ID_RS70D_FLASH	0x001A -#define PCI_DEVICE_ID_RS80_FLASH	0x001C -#define PCI_DEVICE_ID_RS81_FLASH	0x001E +#define PCI_DEVICE_ID_FS70_FLASH	0x04A9 +#define PCI_DEVICE_ID_FS80_FLASH	0x04AA  #define RS70_PCI_REV_SUPPORTED	4  #define DRIVER_NAME "rsxx" -#define DRIVER_VERSION "3.7" +#define DRIVER_VERSION "4.0"  /* Block size is 4096 */  #define RSXX_HW_BLK_SHIFT		12 @@ -67,6 +64,9 @@ struct proc_cmd;  #define RSXX_MAX_OUTSTANDING_CMDS	255  #define RSXX_CS_IDX_MASK		0xff +#define STATUS_BUFFER_SIZE8     4096 +#define COMMAND_BUFFER_SIZE8    4096 +  #define RSXX_MAX_TARGETS	8  struct dma_tracker_list; @@ -91,6 +91,9 @@ struct rsxx_dma_stats {  	u32 discards_failed;  	u32 done_rescheduled;  	u32 issue_rescheduled; +	u32 dma_sw_err; +	u32 dma_hw_fault; +	u32 dma_cancelled;  	u32 sw_q_depth;		/* Number of DMAs on the SW queue. */  	atomic_t hw_q_depth;	/* Number of DMAs queued to HW. */  }; @@ -116,6 +119,7 @@ struct rsxx_dma_ctrl {  struct rsxx_cardinfo {  	struct pci_dev		*dev;  	unsigned int		halt; +	unsigned int		eeh_state;  	void			__iomem *regmap;  	spinlock_t		irq_lock; @@ -224,6 +228,7 @@ enum rsxx_pci_regmap {  	PERF_RD512_HI	= 0xac,  	PERF_WR512_LO	= 0xb0,  	PERF_WR512_HI	= 0xb4, +	PCI_RECONFIG	= 0xb8,  };  enum rsxx_intr { @@ -237,6 +242,8 @@ enum rsxx_intr {  	CR_INTR_DMA5	= 0x00000080,  	CR_INTR_DMA6	= 0x00000100,  	CR_INTR_DMA7	= 0x00000200, +	CR_INTR_ALL_C	= 0x0000003f, +	CR_INTR_ALL_G	= 0x000003ff,  	CR_INTR_DMA_ALL = 0x000003f5,  	CR_INTR_ALL	= 0xffffffff,  }; @@ -253,8 +260,14 @@ enum rsxx_pci_reset {  	DMA_QUEUE_RESET		= 0x00000001,  }; +enum rsxx_hw_fifo_flush { +	RSXX_FLUSH_BUSY		= 0x00000002, +	RSXX_FLUSH_TIMEOUT	= 0x00000004, +}; +  enum rsxx_pci_revision {  	RSXX_DISCARD_SUPPORT = 2, +	RSXX_EEH_SUPPORT     = 3,  };  enum rsxx_creg_cmd { @@ -360,11 +373,17 @@ int rsxx_dma_setup(struct rsxx_cardinfo *card);  void rsxx_dma_destroy(struct rsxx_cardinfo *card);  int rsxx_dma_init(void);  void rsxx_dma_cleanup(void); +void rsxx_dma_queue_reset(struct rsxx_cardinfo *card); +int rsxx_dma_configure(struct rsxx_cardinfo *card);  int rsxx_dma_queue_bio(struct rsxx_cardinfo *card,  			   struct bio *bio,  			   atomic_t *n_dmas,  			   rsxx_dma_cb cb,  			   void *cb_data); +int rsxx_hw_buffers_init(struct pci_dev *dev, struct rsxx_dma_ctrl *ctrl); +int rsxx_eeh_save_issued_dmas(struct rsxx_cardinfo *card); +void rsxx_eeh_cancel_dmas(struct rsxx_cardinfo *card); +int rsxx_eeh_remap_dmas(struct rsxx_cardinfo *card);  /***** cregs.c *****/  int rsxx_creg_write(struct rsxx_cardinfo *card, u32 addr, @@ -389,10 +408,11 @@ int rsxx_creg_setup(struct rsxx_cardinfo *card);  void rsxx_creg_destroy(struct rsxx_cardinfo *card);  int rsxx_creg_init(void);  void rsxx_creg_cleanup(void); -  int rsxx_reg_access(struct rsxx_cardinfo *card,  			struct rsxx_reg_access __user *ucmd,  			int read); +void rsxx_eeh_save_issued_creg(struct rsxx_cardinfo *card); +void rsxx_kick_creg_queue(struct rsxx_cardinfo *card); diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index de1f319f7bd..dd5b2fed97e 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -164,7 +164,7 @@ static void make_response(struct xen_blkif *blkif, u64 id,  #define foreach_grant_safe(pos, n, rbtree, node) \  	for ((pos) = container_of(rb_first((rbtree)), typeof(*(pos)), node), \ -	     (n) = rb_next(&(pos)->node); \ +	     (n) = (&(pos)->node != NULL) ? rb_next(&(pos)->node) : NULL; \  	     &(pos)->node != NULL; \  	     (pos) = container_of(n, typeof(*(pos)), node), \  	     (n) = (&(pos)->node != NULL) ? rb_next(&(pos)->node) : NULL) @@ -381,8 +381,8 @@ irqreturn_t xen_blkif_be_int(int irq, void *dev_id)  static void print_stats(struct xen_blkif *blkif)  { -	pr_info("xen-blkback (%s): oo %3d  |  rd %4d  |  wr %4d  |  f %4d" -		 "  |  ds %4d\n", +	pr_info("xen-blkback (%s): oo %3llu  |  rd %4llu  |  wr %4llu  |  f %4llu" +		 "  |  ds %4llu\n",  		 current->comm, blkif->st_oo_req,  		 blkif->st_rd_req, blkif->st_wr_req,  		 blkif->st_f_req, blkif->st_ds_req); @@ -442,7 +442,7 @@ int xen_blkif_schedule(void *arg)  }  struct seg_buf { -	unsigned long buf; +	unsigned int offset;  	unsigned int nsec;  };  /* @@ -621,30 +621,21 @@ static int xen_blkbk_map(struct blkif_request *req,  				 * If this is a new persistent grant  				 * save the handler  				 */ -				persistent_gnts[i]->handle = map[j].handle; -				persistent_gnts[i]->dev_bus_addr = -					map[j++].dev_bus_addr; +				persistent_gnts[i]->handle = map[j++].handle;  			}  			pending_handle(pending_req, i) =  				persistent_gnts[i]->handle;  			if (ret)  				continue; - -			seg[i].buf = persistent_gnts[i]->dev_bus_addr | -				(req->u.rw.seg[i].first_sect << 9);  		} else { -			pending_handle(pending_req, i) = map[j].handle; +			pending_handle(pending_req, i) = map[j++].handle;  			bitmap_set(pending_req->unmap_seg, i, 1); -			if (ret) { -				j++; +			if (ret)  				continue; -			} - -			seg[i].buf = map[j++].dev_bus_addr | -				(req->u.rw.seg[i].first_sect << 9);  		} +		seg[i].offset = (req->u.rw.seg[i].first_sect << 9);  	}  	return ret;  } @@ -679,6 +670,16 @@ static int dispatch_discard_io(struct xen_blkif *blkif,  	return err;  } +static int dispatch_other_io(struct xen_blkif *blkif, +			     struct blkif_request *req, +			     struct pending_req *pending_req) +{ +	free_req(pending_req); +	make_response(blkif, req->u.other.id, req->operation, +		      BLKIF_RSP_EOPNOTSUPP); +	return -EIO; +} +  static void xen_blk_drain_io(struct xen_blkif *blkif)  {  	atomic_set(&blkif->drain, 1); @@ -800,17 +801,30 @@ __do_block_io_op(struct xen_blkif *blkif)  		/* Apply all sanity checks to /private copy/ of request. */  		barrier(); -		if (unlikely(req.operation == BLKIF_OP_DISCARD)) { + +		switch (req.operation) { +		case BLKIF_OP_READ: +		case BLKIF_OP_WRITE: +		case BLKIF_OP_WRITE_BARRIER: +		case BLKIF_OP_FLUSH_DISKCACHE: +			if (dispatch_rw_block_io(blkif, &req, pending_req)) +				goto done; +			break; +		case BLKIF_OP_DISCARD:  			free_req(pending_req);  			if (dispatch_discard_io(blkif, &req)) -				break; -		} else if (dispatch_rw_block_io(blkif, &req, pending_req)) +				goto done;  			break; +		default: +			if (dispatch_other_io(blkif, &req, pending_req)) +				goto done; +			break; +		}  		/* Yield point for this unbounded loop. */  		cond_resched();  	} - +done:  	return more_to_do;  } @@ -904,7 +918,8 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,  		pr_debug(DRV_PFX "access denied: %s of [%llu,%llu] on dev=%04x\n",  			 operation == READ ? "read" : "write",  			 preq.sector_number, -			 preq.sector_number + preq.nr_sects, preq.dev); +			 preq.sector_number + preq.nr_sects, +			 blkif->vbd.pdevice);  		goto fail_response;  	} @@ -947,7 +962,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,  		       (bio_add_page(bio,  				     pages[i],  				     seg[i].nsec << 9, -				     seg[i].buf & ~PAGE_MASK) == 0)) { +				     seg[i].offset) == 0)) {  			bio = bio_alloc(GFP_KERNEL, nseg-i);  			if (unlikely(bio == NULL)) @@ -977,13 +992,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,  		bio->bi_end_io  = end_block_io_op;  	} -	/* -	 * We set it one so that the last submit_bio does not have to call -	 * atomic_inc. -	 */  	atomic_set(&pending_req->pendcnt, nbio); - -	/* Get a reference count for the disk queue and start sending I/O */  	blk_start_plug(&plug);  	for (i = 0; i < nbio; i++) @@ -1011,6 +1020,7 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif,   fail_put_bio:  	for (i = 0; i < nbio; i++)  		bio_put(biolist[i]); +	atomic_set(&pending_req->pendcnt, 1);  	__end_block_io_op(pending_req, -EINVAL);  	msleep(1); /* back off a bit */  	return -EIO; diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index 6072390c7f5..60103e2517b 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -77,11 +77,18 @@ struct blkif_x86_32_request_discard {  	uint64_t       nr_sectors;  } __attribute__((__packed__)); +struct blkif_x86_32_request_other { +	uint8_t        _pad1; +	blkif_vdev_t   _pad2; +	uint64_t       id;           /* private guest value, echoed in resp  */ +} __attribute__((__packed__)); +  struct blkif_x86_32_request {  	uint8_t        operation;    /* BLKIF_OP_???                         */  	union {  		struct blkif_x86_32_request_rw rw;  		struct blkif_x86_32_request_discard discard; +		struct blkif_x86_32_request_other other;  	} u;  } __attribute__((__packed__)); @@ -113,11 +120,19 @@ struct blkif_x86_64_request_discard {  	uint64_t       nr_sectors;  } __attribute__((__packed__)); +struct blkif_x86_64_request_other { +	uint8_t        _pad1; +	blkif_vdev_t   _pad2; +	uint32_t       _pad3;        /* offsetof(blkif_..,u.discard.id)==8   */ +	uint64_t       id;           /* private guest value, echoed in resp  */ +} __attribute__((__packed__)); +  struct blkif_x86_64_request {  	uint8_t        operation;    /* BLKIF_OP_???                         */  	union {  		struct blkif_x86_64_request_rw rw;  		struct blkif_x86_64_request_discard discard; +		struct blkif_x86_64_request_other other;  	} u;  } __attribute__((__packed__)); @@ -172,7 +187,6 @@ struct persistent_gnt {  	struct page *page;  	grant_ref_t gnt;  	grant_handle_t handle; -	uint64_t dev_bus_addr;  	struct rb_node node;  }; @@ -208,13 +222,13 @@ struct xen_blkif {  	/* statistics */  	unsigned long		st_print; -	int			st_rd_req; -	int			st_wr_req; -	int			st_oo_req; -	int			st_f_req; -	int			st_ds_req; -	int			st_rd_sect; -	int			st_wr_sect; +	unsigned long long			st_rd_req; +	unsigned long long			st_wr_req; +	unsigned long long			st_oo_req; +	unsigned long long			st_f_req; +	unsigned long long			st_ds_req; +	unsigned long long			st_rd_sect; +	unsigned long long			st_wr_sect;  	wait_queue_head_t	waiting_to_free;  }; @@ -278,6 +292,11 @@ static inline void blkif_get_x86_32_req(struct blkif_request *dst,  		dst->u.discard.nr_sectors = src->u.discard.nr_sectors;  		break;  	default: +		/* +		 * Don't know how to translate this op. Only get the +		 * ID so failure can be reported to the frontend. +		 */ +		dst->u.other.id = src->u.other.id;  		break;  	}  } @@ -309,6 +328,11 @@ static inline void blkif_get_x86_64_req(struct blkif_request *dst,  		dst->u.discard.nr_sectors = src->u.discard.nr_sectors;  		break;  	default: +		/* +		 * Don't know how to translate this op. Only get the +		 * ID so failure can be reported to the frontend. +		 */ +		dst->u.other.id = src->u.other.id;  		break;  	}  } diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 5e237f630c4..8bfd1bcf95e 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -230,13 +230,13 @@ int __init xen_blkif_interface_init(void)  	}								\  	static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) -VBD_SHOW(oo_req,  "%d\n", be->blkif->st_oo_req); -VBD_SHOW(rd_req,  "%d\n", be->blkif->st_rd_req); -VBD_SHOW(wr_req,  "%d\n", be->blkif->st_wr_req); -VBD_SHOW(f_req,  "%d\n", be->blkif->st_f_req); -VBD_SHOW(ds_req,  "%d\n", be->blkif->st_ds_req); -VBD_SHOW(rd_sect, "%d\n", be->blkif->st_rd_sect); -VBD_SHOW(wr_sect, "%d\n", be->blkif->st_wr_sect); +VBD_SHOW(oo_req,  "%llu\n", be->blkif->st_oo_req); +VBD_SHOW(rd_req,  "%llu\n", be->blkif->st_rd_req); +VBD_SHOW(wr_req,  "%llu\n", be->blkif->st_wr_req); +VBD_SHOW(f_req,  "%llu\n", be->blkif->st_f_req); +VBD_SHOW(ds_req,  "%llu\n", be->blkif->st_ds_req); +VBD_SHOW(rd_sect, "%llu\n", be->blkif->st_rd_sect); +VBD_SHOW(wr_sect, "%llu\n", be->blkif->st_wr_sect);  static struct attribute *xen_vbdstat_attrs[] = {  	&dev_attr_oo_req.attr, diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index c3dae2e0f29..a894f88762d 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -44,7 +44,7 @@  #include <linux/mutex.h>  #include <linux/scatterlist.h>  #include <linux/bitmap.h> -#include <linux/llist.h> +#include <linux/list.h>  #include <xen/xen.h>  #include <xen/xenbus.h> @@ -68,13 +68,12 @@ enum blkif_state {  struct grant {  	grant_ref_t gref;  	unsigned long pfn; -	struct llist_node node; +	struct list_head node;  };  struct blk_shadow {  	struct blkif_request req;  	struct request *request; -	unsigned long frame[BLKIF_MAX_SEGMENTS_PER_REQUEST];  	struct grant *grants_used[BLKIF_MAX_SEGMENTS_PER_REQUEST];  }; @@ -105,7 +104,7 @@ struct blkfront_info  	struct work_struct work;  	struct gnttab_free_callback callback;  	struct blk_shadow shadow[BLK_RING_SIZE]; -	struct llist_head persistent_gnts; +	struct list_head persistent_gnts;  	unsigned int persistent_gnts_c;  	unsigned long shadow_free;  	unsigned int feature_flush; @@ -165,6 +164,69 @@ static int add_id_to_freelist(struct blkfront_info *info,  	return 0;  } +static int fill_grant_buffer(struct blkfront_info *info, int num) +{ +	struct page *granted_page; +	struct grant *gnt_list_entry, *n; +	int i = 0; + +	while(i < num) { +		gnt_list_entry = kzalloc(sizeof(struct grant), GFP_NOIO); +		if (!gnt_list_entry) +			goto out_of_memory; + +		granted_page = alloc_page(GFP_NOIO); +		if (!granted_page) { +			kfree(gnt_list_entry); +			goto out_of_memory; +		} + +		gnt_list_entry->pfn = page_to_pfn(granted_page); +		gnt_list_entry->gref = GRANT_INVALID_REF; +		list_add(&gnt_list_entry->node, &info->persistent_gnts); +		i++; +	} + +	return 0; + +out_of_memory: +	list_for_each_entry_safe(gnt_list_entry, n, +	                         &info->persistent_gnts, node) { +		list_del(&gnt_list_entry->node); +		__free_page(pfn_to_page(gnt_list_entry->pfn)); +		kfree(gnt_list_entry); +		i--; +	} +	BUG_ON(i != 0); +	return -ENOMEM; +} + +static struct grant *get_grant(grant_ref_t *gref_head, +                               struct blkfront_info *info) +{ +	struct grant *gnt_list_entry; +	unsigned long buffer_mfn; + +	BUG_ON(list_empty(&info->persistent_gnts)); +	gnt_list_entry = list_first_entry(&info->persistent_gnts, struct grant, +	                                  node); +	list_del(&gnt_list_entry->node); + +	if (gnt_list_entry->gref != GRANT_INVALID_REF) { +		info->persistent_gnts_c--; +		return gnt_list_entry; +	} + +	/* Assign a gref to this page */ +	gnt_list_entry->gref = gnttab_claim_grant_reference(gref_head); +	BUG_ON(gnt_list_entry->gref == -ENOSPC); +	buffer_mfn = pfn_to_mfn(gnt_list_entry->pfn); +	gnttab_grant_foreign_access_ref(gnt_list_entry->gref, +	                                info->xbdev->otherend_id, +	                                buffer_mfn, 0); +	return gnt_list_entry; +} +  static const char *op_name(int op)  {  	static const char *const names[] = { @@ -293,7 +355,6 @@ static int blkif_ioctl(struct block_device *bdev, fmode_t mode,  static int blkif_queue_request(struct request *req)  {  	struct blkfront_info *info = req->rq_disk->private_data; -	unsigned long buffer_mfn;  	struct blkif_request *ring_req;  	unsigned long id;  	unsigned int fsect, lsect; @@ -306,7 +367,6 @@ static int blkif_queue_request(struct request *req)  	 */  	bool new_persistent_gnts;  	grant_ref_t gref_head; -	struct page *granted_page;  	struct grant *gnt_list_entry = NULL;  	struct scatterlist *sg; @@ -370,41 +430,8 @@ static int blkif_queue_request(struct request *req)  			fsect = sg->offset >> 9;  			lsect = fsect + (sg->length >> 9) - 1; -			if (info->persistent_gnts_c) { -				BUG_ON(llist_empty(&info->persistent_gnts)); -				gnt_list_entry = llist_entry( -					llist_del_first(&info->persistent_gnts), -					struct grant, node); - -				ref = gnt_list_entry->gref; -				buffer_mfn = pfn_to_mfn(gnt_list_entry->pfn); -				info->persistent_gnts_c--; -			} else { -				ref = gnttab_claim_grant_reference(&gref_head); -				BUG_ON(ref == -ENOSPC); - -				gnt_list_entry = -					kmalloc(sizeof(struct grant), -							 GFP_ATOMIC); -				if (!gnt_list_entry) -					return -ENOMEM; - -				granted_page = alloc_page(GFP_ATOMIC); -				if (!granted_page) { -					kfree(gnt_list_entry); -					return -ENOMEM; -				} - -				gnt_list_entry->pfn = -					page_to_pfn(granted_page); -				gnt_list_entry->gref = ref; - -				buffer_mfn = pfn_to_mfn(page_to_pfn( -								granted_page)); -				gnttab_grant_foreign_access_ref(ref, -					info->xbdev->otherend_id, -					buffer_mfn, 0); -			} +			gnt_list_entry = get_grant(&gref_head, info); +			ref = gnt_list_entry->gref;  			info->shadow[id].grants_used[i] = gnt_list_entry; @@ -435,7 +462,6 @@ static int blkif_queue_request(struct request *req)  				kunmap_atomic(shared_data);  			} -			info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);  			ring_req->u.rw.seg[i] =  					(struct blkif_request_segment) {  						.gref       = ref, @@ -790,9 +816,8 @@ static void blkif_restart_queue(struct work_struct *work)  static void blkif_free(struct blkfront_info *info, int suspend)  { -	struct llist_node *all_gnts; -	struct grant *persistent_gnt, *tmp; -	struct llist_node *n; +	struct grant *persistent_gnt; +	struct grant *n;  	/* Prevent new requests being issued until we fix things up. */  	spin_lock_irq(&info->io_lock); @@ -803,22 +828,20 @@ static void blkif_free(struct blkfront_info *info, int suspend)  		blk_stop_queue(info->rq);  	/* Remove all persistent grants */ -	if (info->persistent_gnts_c) { -		all_gnts = llist_del_all(&info->persistent_gnts); -		persistent_gnt = llist_entry(all_gnts, typeof(*(persistent_gnt)), node); -		while (persistent_gnt) { -			gnttab_end_foreign_access(persistent_gnt->gref, 0, 0UL); +	if (!list_empty(&info->persistent_gnts)) { +		list_for_each_entry_safe(persistent_gnt, n, +		                         &info->persistent_gnts, node) { +			list_del(&persistent_gnt->node); +			if (persistent_gnt->gref != GRANT_INVALID_REF) { +				gnttab_end_foreign_access(persistent_gnt->gref, +				                          0, 0UL); +				info->persistent_gnts_c--; +			}  			__free_page(pfn_to_page(persistent_gnt->pfn)); -			tmp = persistent_gnt; -			n = persistent_gnt->node.next; -			if (n) -				persistent_gnt = llist_entry(n, typeof(*(persistent_gnt)), node); -			else -				persistent_gnt = NULL; -			kfree(tmp); +			kfree(persistent_gnt);  		} -		info->persistent_gnts_c = 0;  	} +	BUG_ON(info->persistent_gnts_c != 0);  	/* No more gnttab callback work. */  	gnttab_cancel_free_callback(&info->callback); @@ -875,7 +898,7 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info,  	}  	/* Add the persistent grant into the list of free grants */  	for (i = 0; i < s->req.u.rw.nr_segments; i++) { -		llist_add(&s->grants_used[i]->node, &info->persistent_gnts); +		list_add(&s->grants_used[i]->node, &info->persistent_gnts);  		info->persistent_gnts_c++;  	}  } @@ -1013,6 +1036,12 @@ static int setup_blkring(struct xenbus_device *dev,  	sg_init_table(info->sg, BLKIF_MAX_SEGMENTS_PER_REQUEST); +	/* Allocate memory for grants */ +	err = fill_grant_buffer(info, BLK_RING_SIZE * +	                              BLKIF_MAX_SEGMENTS_PER_REQUEST); +	if (err) +		goto fail; +  	err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));  	if (err < 0) {  		free_page((unsigned long)sring); @@ -1171,7 +1200,7 @@ static int blkfront_probe(struct xenbus_device *dev,  	spin_lock_init(&info->io_lock);  	info->xbdev = dev;  	info->vdevice = vdevice; -	init_llist_head(&info->persistent_gnts); +	INIT_LIST_HEAD(&info->persistent_gnts);  	info->persistent_gnts_c = 0;  	info->connected = BLKIF_STATE_DISCONNECTED;  	INIT_WORK(&info->work, blkif_restart_queue); @@ -1203,11 +1232,10 @@ static int blkif_recover(struct blkfront_info *info)  	int j;  	/* Stage 1: Make a safe copy of the shadow state. */ -	copy = kmalloc(sizeof(info->shadow), +	copy = kmemdup(info->shadow, sizeof(info->shadow),  		       GFP_NOIO | __GFP_REPEAT | __GFP_HIGH);  	if (!copy)  		return -ENOMEM; -	memcpy(copy, info->shadow, sizeof(info->shadow));  	/* Stage 2: Set up free list. */  	memset(&info->shadow, 0, sizeof(info->shadow)); @@ -1236,7 +1264,7 @@ static int blkif_recover(struct blkfront_info *info)  				gnttab_grant_foreign_access_ref(  					req->u.rw.seg[j].gref,  					info->xbdev->otherend_id, -					pfn_to_mfn(info->shadow[req->u.rw.id].frame[j]), +					pfn_to_mfn(copy[i].grants_used[j]->pfn),  					0);  		}  		info->shadow[req->u.rw.id].req = *req; diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index a8a41e07a22..6aab00ef437 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -73,9 +73,13 @@ static struct usb_device_id ath3k_table[] = {  	{ USB_DEVICE(0x03F0, 0x311D) },  	/* Atheros AR3012 with sflash firmware*/ +	{ USB_DEVICE(0x0CF3, 0x0036) },  	{ USB_DEVICE(0x0CF3, 0x3004) }, +	{ USB_DEVICE(0x0CF3, 0x3008) },  	{ USB_DEVICE(0x0CF3, 0x311D) }, +	{ USB_DEVICE(0x0CF3, 0x817a) },  	{ USB_DEVICE(0x13d3, 0x3375) }, +	{ USB_DEVICE(0x04CA, 0x3004) },  	{ USB_DEVICE(0x04CA, 0x3005) },  	{ USB_DEVICE(0x04CA, 0x3006) },  	{ USB_DEVICE(0x04CA, 0x3008) }, @@ -105,9 +109,13 @@ MODULE_DEVICE_TABLE(usb, ath3k_table);  static struct usb_device_id ath3k_blist_tbl[] = {  	/* Atheros AR3012 with sflash firmware*/ +	{ USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7e351e34547..2cc5f774a29 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -131,9 +131,13 @@ static struct usb_device_id blacklist_table[] = {  	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },  	/* Atheros 3012 with sflash firmware */ +	{ USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, +	{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },  	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 10fd71ccf58..6bf4d47324e 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -92,14 +92,22 @@ static int probe_common(struct virtio_device *vdev)  {  	int err; +	if (vq) { +		/* We only support one device for now */ +		return -EBUSY; +	}  	/* We expect a single virtqueue. */  	vq = virtio_find_single_vq(vdev, random_recv_done, "input"); -	if (IS_ERR(vq)) -		return PTR_ERR(vq); +	if (IS_ERR(vq)) { +		err = PTR_ERR(vq); +		vq = NULL; +		return err; +	}  	err = hwrng_register(&virtio_hwrng);  	if (err) {  		vdev->config->del_vqs(vdev); +		vq = NULL;  		return err;  	} @@ -112,6 +120,7 @@ static void remove_common(struct virtio_device *vdev)  	busy = false;  	hwrng_unregister(&virtio_hwrng);  	vdev->config->del_vqs(vdev); +	vq = NULL;  }  static int virtrng_probe(struct virtio_device *vdev) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8..a64caefdba1 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -63,6 +63,14 @@ config CLK_TWL6040  	  McPDM. McPDM module is using the external bit clock on the McPDM bus  	  as functional clock. +config COMMON_CLK_AXI_CLKGEN +	tristate "AXI clkgen driver" +	depends on ARCH_ZYNQ || MICROBLAZE +	help +	---help--- +	  Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx +	  FPGAs. It is commonly used in Analog Devices' reference designs. +  endmenu  source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d92..79e98e41672 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-factor.o  obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o  obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o  obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o +obj-$(CONFIG_COMMON_CLK)	+= clk-composite.o  # SoCs specific  obj-$(CONFIG_ARCH_BCM2835)	+= clk-bcm2835.o @@ -23,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)  obj-$(CONFIG_ARCH_MMP)		+= mmp/  endif  obj-$(CONFIG_MACH_LOONGSON1)	+= clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/  obj-$(CONFIG_ARCH_U8500)	+= ux500/  obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o  obj-$(CONFIG_ARCH_ZYNQ)		+= clk-zynq.o @@ -31,6 +33,7 @@ obj-$(CONFIG_ARCH_TEGRA)	+= tegra/  obj-$(CONFIG_X86)		+= x86/  # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o  obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o  obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o  obj-$(CONFIG_CLK_TWL6040)	+= clk-twl6040.o diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 00000000000..8137327847c --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + *  Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + * + */ + +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/err.h> + +#define AXI_CLKGEN_REG_UPDATE_ENABLE	0x04 +#define AXI_CLKGEN_REG_CLK_OUT1		0x08 +#define AXI_CLKGEN_REG_CLK_OUT2		0x0c +#define AXI_CLKGEN_REG_CLK_DIV		0x10 +#define AXI_CLKGEN_REG_CLK_FB1		0x14 +#define AXI_CLKGEN_REG_CLK_FB2		0x18 +#define AXI_CLKGEN_REG_LOCK1		0x1c +#define AXI_CLKGEN_REG_LOCK2		0x20 +#define AXI_CLKGEN_REG_LOCK3		0x24 +#define AXI_CLKGEN_REG_FILTER1		0x28 +#define AXI_CLKGEN_REG_FILTER2		0x2c + +struct axi_clkgen { +	void __iomem *base; +	struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ +	switch (m) { +	case 0: +		return 0x01001990; +	case 1: +		return 0x01001190; +	case 2: +		return 0x01009890; +	case 3: +		return 0x01001890; +	case 4: +		return 0x01008890; +	case 5 ... 8: +		return 0x01009090; +	case 9 ... 11: +		return 0x01000890; +	case 12: +		return 0x08009090; +	case 13 ... 22: +		return 0x01001090; +	case 23 ... 36: +		return 0x01008090; +	case 37 ... 46: +		return 0x08001090; +	default: +		return 0x08008090; +	} +} + +static const uint32_t axi_clkgen_lock_table[] = { +	0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, +	0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, +	0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, +	0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, +	0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, +	0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, +	0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, +	0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, +	0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ +	if (m < ARRAY_SIZE(axi_clkgen_lock_table)) +		return axi_clkgen_lock_table[m]; +	return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, +	unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ +	unsigned long d, d_min, d_max, _d_min, _d_max; +	unsigned long m, m_min, m_max; +	unsigned long f, dout, best_f, fvco; + +	fin /= 1000; +	fout /= 1000; + +	best_f = ULONG_MAX; +	*best_d = 0; +	*best_m = 0; +	*best_dout = 0; + +	d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); +	d_max = min_t(unsigned long, fin / fpfd_min, 80); + +	m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); +	m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + +	for (m = m_min; m <= m_max; m++) { +		_d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); +		_d_max = min(d_max, fin * m / fvco_min); + +		for (d = _d_min; d <= _d_max; d++) { +			fvco = fin * m / d; + +			dout = DIV_ROUND_CLOSEST(fvco, fout); +			dout = clamp_t(unsigned long, dout, 1, 128); +			f = fvco / dout; +			if (abs(f - fout) < abs(best_f - fout)) { +				best_f = f; +				*best_d = d; +				*best_m = m; +				*best_dout = dout; +				if (best_f == fout) +					return; +			} +		} +	} +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, +	unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ +	if (divider == 1) +		*nocount = 1; +	else +		*nocount = 0; + +	*high = divider / 2; +	*edge = divider % 2; +	*low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, +	unsigned int reg, unsigned int val) +{ +	writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, +	unsigned int reg, unsigned int *val) +{ +	*val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ +	return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, +	unsigned long rate, unsigned long parent_rate) +{ +	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); +	unsigned int d, m, dout; +	unsigned int nocount; +	unsigned int high; +	unsigned int edge; +	unsigned int low; +	uint32_t filter; +	uint32_t lock; + +	if (parent_rate == 0 || rate == 0) +		return -EINVAL; + +	axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + +	if (d == 0 || dout == 0 || m == 0) +		return -EINVAL; + +	filter = axi_clkgen_lookup_filter(m - 1); +	lock = axi_clkgen_lookup_lock(m - 1); + +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + +	axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, +		(high << 6) | low); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, +		(edge << 7) | (nocount << 6)); + +	axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, +		(edge << 13) | (nocount << 12) | (high << 6) | low); + +	axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, +		(high << 6) | low); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, +		(edge << 7) | (nocount << 6)); + +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, +		(((lock >> 16) & 0x1f) << 10) | 0x1); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, +		(((lock >> 24) & 0x1f) << 10) | 0x3e9); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + +	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + +	return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, +	unsigned long *parent_rate) +{ +	unsigned int d, m, dout; + +	axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + +	if (d == 0 || dout == 0 || m == 0) +		return -EINVAL; + +	return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, +	unsigned long parent_rate) +{ +	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); +	unsigned int d, m, dout; +	unsigned int reg; +	unsigned long long tmp; + +	axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); +	dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); +	axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); +	d = (reg & 0x3f) + ((reg >> 6) & 0x3f); +	axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); +	m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + +	if (d == 0 || dout == 0) +		return 0; + +	tmp = (unsigned long long)(parent_rate / d) * m; +	do_div(tmp, dout); + +	if (tmp > ULONG_MAX) +		return ULONG_MAX; + +	return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { +	.recalc_rate = axi_clkgen_recalc_rate, +	.round_rate = axi_clkgen_round_rate, +	.set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ +	struct axi_clkgen *axi_clkgen; +	struct clk_init_data init; +	const char *parent_name; +	const char *clk_name; +	struct resource *mem; +	struct clk *clk; + +	axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); +	if (!axi_clkgen) +		return -ENOMEM; + +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); +	if (IS_ERR(axi_clkgen->base)) +		return PTR_ERR(axi_clkgen->base); + +	parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); +	if (!parent_name) +		return -EINVAL; + +	clk_name = pdev->dev.of_node->name; +	of_property_read_string(pdev->dev.of_node, "clock-output-names", +		&clk_name); + +	init.name = clk_name; +	init.ops = &axi_clkgen_ops; +	init.flags = 0; +	init.parent_names = &parent_name; +	init.num_parents = 1; + +	axi_clkgen->clk_hw.init = &init; +	clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); +	if (IS_ERR(clk)) +		return PTR_ERR(clk); + +	return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, +				    clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ +	of_clk_del_provider(pdev->dev.of_node); + +	return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { +	{ .compatible = "adi,axi-clkgen-1.00.a" }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +static struct platform_driver axi_clkgen_driver = { +	.driver = { +		.name = "adi-axi-clkgen", +		.owner = THIS_MODULE, +		.of_match_table = axi_clkgen_ids, +	}, +	.probe = axi_clkgen_probe, +	.remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 00000000000..097dee4fd20 --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 NVIDIA CORPORATION.  All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> + +#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + +static u8 clk_composite_get_parent(struct clk_hw *hw) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *mux_ops = composite->mux_ops; +	struct clk_hw *mux_hw = composite->mux_hw; + +	mux_hw->clk = hw->clk; + +	return mux_ops->get_parent(mux_hw); +} + +static int clk_composite_set_parent(struct clk_hw *hw, u8 index) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *mux_ops = composite->mux_ops; +	struct clk_hw *mux_hw = composite->mux_hw; + +	mux_hw->clk = hw->clk; + +	return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, +					    unsigned long parent_rate) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *div_ops = composite->div_ops; +	struct clk_hw *div_hw = composite->div_hw; + +	div_hw->clk = hw->clk; + +	return div_ops->recalc_rate(div_hw, parent_rate); +} + +static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, +				  unsigned long *prate) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *div_ops = composite->div_ops; +	struct clk_hw *div_hw = composite->div_hw; + +	div_hw->clk = hw->clk; + +	return div_ops->round_rate(div_hw, rate, prate); +} + +static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, +			       unsigned long parent_rate) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *div_ops = composite->div_ops; +	struct clk_hw *div_hw = composite->div_hw; + +	div_hw->clk = hw->clk; + +	return div_ops->set_rate(div_hw, rate, parent_rate); +} + +static int clk_composite_is_enabled(struct clk_hw *hw) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *gate_ops = composite->gate_ops; +	struct clk_hw *gate_hw = composite->gate_hw; + +	gate_hw->clk = hw->clk; + +	return gate_ops->is_enabled(gate_hw); +} + +static int clk_composite_enable(struct clk_hw *hw) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *gate_ops = composite->gate_ops; +	struct clk_hw *gate_hw = composite->gate_hw; + +	gate_hw->clk = hw->clk; + +	return gate_ops->enable(gate_hw); +} + +static void clk_composite_disable(struct clk_hw *hw) +{ +	struct clk_composite *composite = to_clk_composite(hw); +	const struct clk_ops *gate_ops = composite->gate_ops; +	struct clk_hw *gate_hw = composite->gate_hw; + +	gate_hw->clk = hw->clk; + +	gate_ops->disable(gate_hw); +} + +struct clk *clk_register_composite(struct device *dev, const char *name, +			const char **parent_names, int num_parents, +			struct clk_hw *mux_hw, const struct clk_ops *mux_ops, +			struct clk_hw *div_hw, const struct clk_ops *div_ops, +			struct clk_hw *gate_hw, const struct clk_ops *gate_ops, +			unsigned long flags) +{ +	struct clk *clk; +	struct clk_init_data init; +	struct clk_composite *composite; +	struct clk_ops *clk_composite_ops; + +	composite = kzalloc(sizeof(*composite), GFP_KERNEL); +	if (!composite) { +		pr_err("%s: could not allocate composite clk\n", __func__); +		return ERR_PTR(-ENOMEM); +	} + +	init.name = name; +	init.flags = flags | CLK_IS_BASIC; +	init.parent_names = parent_names; +	init.num_parents = num_parents; + +	clk_composite_ops = &composite->ops; + +	if (mux_hw && mux_ops) { +		if (!mux_ops->get_parent || !mux_ops->set_parent) { +			clk = ERR_PTR(-EINVAL); +			goto err; +		} + +		composite->mux_hw = mux_hw; +		composite->mux_ops = mux_ops; +		clk_composite_ops->get_parent = clk_composite_get_parent; +		clk_composite_ops->set_parent = clk_composite_set_parent; +	} + +	if (div_hw && div_ops) { +		if (!div_ops->recalc_rate || !div_ops->round_rate || +		    !div_ops->set_rate) { +			clk = ERR_PTR(-EINVAL); +			goto err; +		} + +		composite->div_hw = div_hw; +		composite->div_ops = div_ops; +		clk_composite_ops->recalc_rate = clk_composite_recalc_rate; +		clk_composite_ops->round_rate = clk_composite_round_rate; +		clk_composite_ops->set_rate = clk_composite_set_rate; +	} + +	if (gate_hw && gate_ops) { +		if (!gate_ops->is_enabled || !gate_ops->enable || +		    !gate_ops->disable) { +			clk = ERR_PTR(-EINVAL); +			goto err; +		} + +		composite->gate_hw = gate_hw; +		composite->gate_ops = gate_ops; +		clk_composite_ops->is_enabled = clk_composite_is_enabled; +		clk_composite_ops->enable = clk_composite_enable; +		clk_composite_ops->disable = clk_composite_disable; +	} + +	init.ops = clk_composite_ops; +	composite->hw.init = &init; + +	clk = clk_register(dev, &composite->hw); +	if (IS_ERR(clk)) +		goto err; + +	if (composite->mux_hw) +		composite->mux_hw->clk = clk; + +	if (composite->div_hw) +		composite->div_hw->clk = clk; + +	if (composite->gate_hw) +		composite->gate_hw->clk = clk; + +	return clk; + +err: +	kfree(composite); +	return clk; +} diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 508c032edce..25b1734560d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -32,6 +32,7 @@  static u8 clk_mux_get_parent(struct clk_hw *hw)  {  	struct clk_mux *mux = to_clk_mux(hw); +	int num_parents = __clk_get_num_parents(hw->clk);  	u32 val;  	/* @@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)  	 * val = 0x4 really means "bit 2, index starts at bit 0"  	 */  	val = readl(mux->reg) >> mux->shift; -	val &= (1 << mux->width) - 1; +	val &= mux->mask; + +	if (mux->table) { +		int i; + +		for (i = 0; i < num_parents; i++) +			if (mux->table[i] == val) +				return i; +		return -EINVAL; +	}  	if (val && (mux->flags & CLK_MUX_INDEX_BIT))  		val = ffs(val) - 1; @@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)  	if (val && (mux->flags & CLK_MUX_INDEX_ONE))  		val--; -	if (val >= __clk_get_num_parents(hw->clk)) +	if (val >= num_parents)  		return -EINVAL;  	return val; @@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)  	u32 val;  	unsigned long flags = 0; -	if (mux->flags & CLK_MUX_INDEX_BIT) -		index = (1 << ffs(index)); +	if (mux->table) +		index = mux->table[index]; -	if (mux->flags & CLK_MUX_INDEX_ONE) -		index++; +	else { +		if (mux->flags & CLK_MUX_INDEX_BIT) +			index = (1 << ffs(index)); + +		if (mux->flags & CLK_MUX_INDEX_ONE) +			index++; +	}  	if (mux->lock)  		spin_lock_irqsave(mux->lock, flags);  	val = readl(mux->reg); -	val &= ~(((1 << mux->width) - 1) << mux->shift); +	val &= ~(mux->mask << mux->shift);  	val |= index << mux->shift;  	writel(val, mux->reg); @@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = {  };  EXPORT_SYMBOL_GPL(clk_mux_ops); -struct clk *clk_register_mux(struct device *dev, const char *name, +struct clk *clk_register_mux_table(struct device *dev, const char *name,  		const char **parent_names, u8 num_parents, unsigned long flags, -		void __iomem *reg, u8 shift, u8 width, -		u8 clk_mux_flags, spinlock_t *lock) +		void __iomem *reg, u8 shift, u32 mask, +		u8 clk_mux_flags, u32 *table, spinlock_t *lock)  {  	struct clk_mux *mux;  	struct clk *clk; @@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name,  	/* struct clk_mux assignments */  	mux->reg = reg;  	mux->shift = shift; -	mux->width = width; +	mux->mask = mask;  	mux->flags = clk_mux_flags;  	mux->lock = lock; +	mux->table = table;  	mux->hw.init = &init;  	clk = clk_register(dev, &mux->hw); @@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name,  	return clk;  } + +struct clk *clk_register_mux(struct device *dev, const char *name, +		const char **parent_names, u8 num_parents, unsigned long flags, +		void __iomem *reg, u8 shift, u8 width, +		u8 clk_mux_flags, spinlock_t *lock) +{ +	u32 mask = BIT(width) - 1; + +	return clk_register_mux_table(dev, name, parent_names, num_parents, +				      flags, reg, shift, mask, clk_mux_flags, +				      NULL, lock); +} diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index f8e9d0c27be..643ca653fef 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void)  	for (i = pll1; i < maxclk; i++) {  		prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); -		BUG_ON(!prima2_clks[i]); +		BUG_ON(IS_ERR(prima2_clks[i]));  	}  	clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");  	clk_register_clkdev(prima2_clks[io],  NULL, "io"); diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index b5538bba7a1..09c63315e57 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -157,7 +157,7 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate,  	divisor =  parent_rate / rate;  	/* If prate / rate would be decimal, incr the divisor */ -	if (rate * divisor < *prate) +	if (rate * divisor < parent_rate)  		divisor++;  	if (divisor == cdev->div_mask + 1) diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c index b14a25f3925..32062977f45 100644 --- a/drivers/clk/clk-zynq.c +++ b/drivers/clk/clk-zynq.c @@ -20,6 +20,7 @@  #include <linux/slab.h>  #include <linux/kernel.h>  #include <linux/clk-provider.h> +#include <linux/clk/zynq.h>  static void __iomem *slcr_base; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed87b240580..0230c9d9597 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -19,14 +19,77 @@  #include <linux/of.h>  #include <linux/device.h>  #include <linux/init.h> +#include <linux/sched.h>  static DEFINE_SPINLOCK(enable_lock);  static DEFINE_MUTEX(prepare_lock); +static struct task_struct *prepare_owner; +static struct task_struct *enable_owner; + +static int prepare_refcnt; +static int enable_refcnt; +  static HLIST_HEAD(clk_root_list);  static HLIST_HEAD(clk_orphan_list);  static LIST_HEAD(clk_notifier_list); +/***           locking             ***/ +static void clk_prepare_lock(void) +{ +	if (!mutex_trylock(&prepare_lock)) { +		if (prepare_owner == current) { +			prepare_refcnt++; +			return; +		} +		mutex_lock(&prepare_lock); +	} +	WARN_ON_ONCE(prepare_owner != NULL); +	WARN_ON_ONCE(prepare_refcnt != 0); +	prepare_owner = current; +	prepare_refcnt = 1; +} + +static void clk_prepare_unlock(void) +{ +	WARN_ON_ONCE(prepare_owner != current); +	WARN_ON_ONCE(prepare_refcnt == 0); + +	if (--prepare_refcnt) +		return; +	prepare_owner = NULL; +	mutex_unlock(&prepare_lock); +} + +static unsigned long clk_enable_lock(void) +{ +	unsigned long flags; + +	if (!spin_trylock_irqsave(&enable_lock, flags)) { +		if (enable_owner == current) { +			enable_refcnt++; +			return flags; +		} +		spin_lock_irqsave(&enable_lock, flags); +	} +	WARN_ON_ONCE(enable_owner != NULL); +	WARN_ON_ONCE(enable_refcnt != 0); +	enable_owner = current; +	enable_refcnt = 1; +	return flags; +} + +static void clk_enable_unlock(unsigned long flags) +{ +	WARN_ON_ONCE(enable_owner != current); +	WARN_ON_ONCE(enable_refcnt == 0); + +	if (--enable_refcnt) +		return; +	enable_owner = NULL; +	spin_unlock_irqrestore(&enable_lock, flags); +} +  /***        debugfs support        ***/  #ifdef CONFIG_COMMON_CLK_DEBUG @@ -69,7 +132,7 @@ static int clk_summary_show(struct seq_file *s, void *data)  	seq_printf(s, "   clock                        enable_cnt  prepare_cnt  rate\n");  	seq_printf(s, "---------------------------------------------------------------------\n"); -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	hlist_for_each_entry(c, &clk_root_list, child_node)  		clk_summary_show_subtree(s, c, 0); @@ -77,7 +140,7 @@ static int clk_summary_show(struct seq_file *s, void *data)  	hlist_for_each_entry(c, &clk_orphan_list, child_node)  		clk_summary_show_subtree(s, c, 0); -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return 0;  } @@ -130,7 +193,7 @@ static int clk_dump(struct seq_file *s, void *data)  	seq_printf(s, "{"); -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	hlist_for_each_entry(c, &clk_root_list, child_node) {  		if (!first_node) @@ -144,7 +207,7 @@ static int clk_dump(struct seq_file *s, void *data)  		clk_dump_subtree(s, c, 0);  	} -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	seq_printf(s, "}");  	return 0; @@ -316,7 +379,7 @@ static int __init clk_debug_init(void)  	if (!orphandir)  		return -ENOMEM; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	hlist_for_each_entry(clk, &clk_root_list, child_node)  		clk_debug_create_subtree(clk, rootdir); @@ -326,7 +389,7 @@ static int __init clk_debug_init(void)  	inited = 1; -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return 0;  } @@ -336,6 +399,31 @@ static inline int clk_debug_register(struct clk *clk) { return 0; }  #endif  /* caller must hold prepare_lock */ +static void clk_unprepare_unused_subtree(struct clk *clk) +{ +	struct clk *child; + +	if (!clk) +		return; + +	hlist_for_each_entry(child, &clk->children, child_node) +		clk_unprepare_unused_subtree(child); + +	if (clk->prepare_count) +		return; + +	if (clk->flags & CLK_IGNORE_UNUSED) +		return; + +	if (__clk_is_prepared(clk)) { +		if (clk->ops->unprepare_unused) +			clk->ops->unprepare_unused(clk->hw); +		else if (clk->ops->unprepare) +			clk->ops->unprepare(clk->hw); +	} +} + +/* caller must hold prepare_lock */  static void clk_disable_unused_subtree(struct clk *clk)  {  	struct clk *child; @@ -347,7 +435,7 @@ static void clk_disable_unused_subtree(struct clk *clk)  	hlist_for_each_entry(child, &clk->children, child_node)  		clk_disable_unused_subtree(child); -	spin_lock_irqsave(&enable_lock, flags); +	flags = clk_enable_lock();  	if (clk->enable_count)  		goto unlock_out; @@ -368,7 +456,7 @@ static void clk_disable_unused_subtree(struct clk *clk)  	}  unlock_out: -	spin_unlock_irqrestore(&enable_lock, flags); +	clk_enable_unlock(flags);  out:  	return; @@ -378,7 +466,7 @@ static int clk_disable_unused(void)  {  	struct clk *clk; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	hlist_for_each_entry(clk, &clk_root_list, child_node)  		clk_disable_unused_subtree(clk); @@ -386,7 +474,13 @@ static int clk_disable_unused(void)  	hlist_for_each_entry(clk, &clk_orphan_list, child_node)  		clk_disable_unused_subtree(clk); -	mutex_unlock(&prepare_lock); +	hlist_for_each_entry(clk, &clk_root_list, child_node) +		clk_unprepare_unused_subtree(clk); + +	hlist_for_each_entry(clk, &clk_orphan_list, child_node) +		clk_unprepare_unused_subtree(clk); + +	clk_prepare_unlock();  	return 0;  } @@ -451,6 +545,27 @@ unsigned long __clk_get_flags(struct clk *clk)  	return !clk ? 0 : clk->flags;  } +bool __clk_is_prepared(struct clk *clk) +{ +	int ret; + +	if (!clk) +		return false; + +	/* +	 * .is_prepared is optional for clocks that can prepare +	 * fall back to software usage counter if it is missing +	 */ +	if (!clk->ops->is_prepared) { +		ret = clk->prepare_count ? 1 : 0; +		goto out; +	} + +	ret = clk->ops->is_prepared(clk->hw); +out: +	return !!ret; +} +  bool __clk_is_enabled(struct clk *clk)  {  	int ret; @@ -548,9 +663,9 @@ void __clk_unprepare(struct clk *clk)   */  void clk_unprepare(struct clk *clk)  { -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	__clk_unprepare(clk); -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  }  EXPORT_SYMBOL_GPL(clk_unprepare); @@ -596,9 +711,9 @@ int clk_prepare(struct clk *clk)  {  	int ret; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	ret = __clk_prepare(clk); -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } @@ -640,9 +755,9 @@ void clk_disable(struct clk *clk)  {  	unsigned long flags; -	spin_lock_irqsave(&enable_lock, flags); +	flags = clk_enable_lock();  	__clk_disable(clk); -	spin_unlock_irqrestore(&enable_lock, flags); +	clk_enable_unlock(flags);  }  EXPORT_SYMBOL_GPL(clk_disable); @@ -693,9 +808,9 @@ int clk_enable(struct clk *clk)  	unsigned long flags;  	int ret; -	spin_lock_irqsave(&enable_lock, flags); +	flags = clk_enable_lock();  	ret = __clk_enable(clk); -	spin_unlock_irqrestore(&enable_lock, flags); +	clk_enable_unlock(flags);  	return ret;  } @@ -740,9 +855,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate)  {  	unsigned long ret; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	ret = __clk_round_rate(clk, rate); -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } @@ -837,13 +952,13 @@ unsigned long clk_get_rate(struct clk *clk)  {  	unsigned long rate; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))  		__clk_recalc_rates(clk, 0);  	rate = __clk_get_rate(clk); -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return rate;  } @@ -974,7 +1089,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even  	int ret = NOTIFY_DONE;  	if (clk->rate == clk->new_rate) -		return 0; +		return NULL;  	if (clk->notifier_count) {  		ret = __clk_notify(clk, event, clk->rate, clk->new_rate); @@ -1048,7 +1163,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)  	int ret = 0;  	/* prevent racing with updates to the clock topology */ -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	/* bail early if nothing to do */  	if (rate == clk->rate) @@ -1080,7 +1195,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)  	clk_change_rate(top);  out: -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } @@ -1096,9 +1211,9 @@ struct clk *clk_get_parent(struct clk *clk)  {  	struct clk *parent; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	parent = __clk_get_parent(clk); -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return parent;  } @@ -1242,19 +1357,19 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)  		__clk_prepare(parent);  	/* FIXME replace with clk_is_enabled(clk) someday */ -	spin_lock_irqsave(&enable_lock, flags); +	flags = clk_enable_lock();  	if (clk->enable_count)  		__clk_enable(parent); -	spin_unlock_irqrestore(&enable_lock, flags); +	clk_enable_unlock(flags);  	/* change clock input source */  	ret = clk->ops->set_parent(clk->hw, i);  	/* clean up old prepare and enable */ -	spin_lock_irqsave(&enable_lock, flags); +	flags = clk_enable_lock();  	if (clk->enable_count)  		__clk_disable(old_parent); -	spin_unlock_irqrestore(&enable_lock, flags); +	clk_enable_unlock(flags);  	if (clk->prepare_count)  		__clk_unprepare(old_parent); @@ -1286,7 +1401,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)  		return -ENOSYS;  	/* prevent racing with updates to the clock topology */ -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	if (clk->parent == parent)  		goto out; @@ -1315,7 +1430,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)  	__clk_reparent(clk, parent);  out: -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } @@ -1338,7 +1453,7 @@ int __clk_init(struct device *dev, struct clk *clk)  	if (!clk)  		return -EINVAL; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	/* check to see if a clock with this name is already registered */  	if (__clk_lookup(clk->name)) { @@ -1462,7 +1577,7 @@ int __clk_init(struct device *dev, struct clk *clk)  	clk_debug_register(clk);  out: -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } @@ -1696,7 +1811,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)  	if (!clk || !nb)  		return -EINVAL; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	/* search the list of notifiers for this clk */  	list_for_each_entry(cn, &clk_notifier_list, node) @@ -1720,7 +1835,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)  	clk->notifier_count++;  out: -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } @@ -1745,7 +1860,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)  	if (!clk || !nb)  		return -EINVAL; -	mutex_lock(&prepare_lock); +	clk_prepare_lock();  	list_for_each_entry(cn, &clk_notifier_list, node)  		if (cn->clk == clk) @@ -1766,7 +1881,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)  		ret = -ENOENT;  	} -	mutex_unlock(&prepare_lock); +	clk_prepare_unlock();  	return ret;  } diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c index b24d56067c8..5301bce8957 100644 --- a/drivers/clk/mxs/clk.c +++ b/drivers/clk/mxs/clk.c @@ -13,6 +13,7 @@  #include <linux/io.h>  #include <linux/jiffies.h>  #include <linux/spinlock.h> +#include "clk.h"  DEFINE_SPINLOCK(mxs_lock); diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 82abea366b7..35e7e2698e1 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -960,47 +960,47 @@ void __init spear1340_clk_init(void)  			SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock);  	clk_register_clkdev(clk, NULL, "d0100000.spdif-in"); -	clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0, +	clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0,  			SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "acp_clk"); -	clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0, +	clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "e2800000.gpio"); -	clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0, +	clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB,  			0, &_lock);  	clk_register_clkdev(clk, NULL, "video_dec"); -	clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0, +	clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB,  			0, &_lock);  	clk_register_clkdev(clk, NULL, "video_enc"); -	clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0, +	clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "spear_vip"); -	clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0, +	clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "d0200000.cam0"); -	clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0, +	clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "d0300000.cam1"); -	clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0, +	clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "d0400000.cam2"); -	clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0, +	clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0,  			SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0,  			&_lock);  	clk_register_clkdev(clk, NULL, "d0500000.cam3"); diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 00000000000..b5bac917612 --- /dev/null +++ b/drivers/clk/sunxi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 00000000000..88523f91d9b --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Emilio López <emilio@elopez.com.ar> + * + * 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. + * + * Adjustable factor-based clock implementation + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/string.h> + +#include <linux/delay.h> + +#include "clk-factors.h" + +/* + * DOC: basic adjustable factor-based clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + *        clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) + * parent - fixed parent.  No clk_set_parent support + */ + +struct clk_factors { +	struct clk_hw hw; +	void __iomem *reg; +	struct clk_factors_config *config; +	void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); +	spinlock_t *lock; +}; + +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + +#define SETMASK(len, pos)		(((-1U) >> (31-len))  << (pos)) +#define CLRMASK(len, pos)		(~(SETMASK(len, pos))) +#define FACTOR_GET(bit, len, reg)	(((reg) & SETMASK(len, bit)) >> (bit)) + +#define FACTOR_SET(bit, len, reg, val) \ +	(((reg) & CLRMASK(len, bit)) | (val << (bit))) + +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, +					     unsigned long parent_rate) +{ +	u8 n = 1, k = 0, p = 0, m = 0; +	u32 reg; +	unsigned long rate; +	struct clk_factors *factors = to_clk_factors(hw); +	struct clk_factors_config *config = factors->config; + +	/* Fetch the register value */ +	reg = readl(factors->reg); + +	/* Get each individual factor if applicable */ +	if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) +		n = FACTOR_GET(config->nshift, config->nwidth, reg); +	if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) +		k = FACTOR_GET(config->kshift, config->kwidth, reg); +	if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) +		m = FACTOR_GET(config->mshift, config->mwidth, reg); +	if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) +		p = FACTOR_GET(config->pshift, config->pwidth, reg); + +	/* Calculate the rate */ +	rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + +	return rate; +} + +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, +				   unsigned long *parent_rate) +{ +	struct clk_factors *factors = to_clk_factors(hw); +	factors->get_factors((u32 *)&rate, (u32)*parent_rate, +			     NULL, NULL, NULL, NULL); + +	return rate; +} + +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, +				unsigned long parent_rate) +{ +	u8 n, k, m, p; +	u32 reg; +	struct clk_factors *factors = to_clk_factors(hw); +	struct clk_factors_config *config = factors->config; +	unsigned long flags = 0; + +	factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + +	if (factors->lock) +		spin_lock_irqsave(factors->lock, flags); + +	/* Fetch the register value */ +	reg = readl(factors->reg); + +	/* Set up the new factors - macros do not do anything if width is 0 */ +	reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); +	reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); +	reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); +	reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + +	/* Apply them now */ +	writel(reg, factors->reg); + +	/* delay 500us so pll stabilizes */ +	__delay((rate >> 20) * 500 / 2); + +	if (factors->lock) +		spin_unlock_irqrestore(factors->lock, flags); + +	return 0; +} + +static const struct clk_ops clk_factors_ops = { +	.recalc_rate = clk_factors_recalc_rate, +	.round_rate = clk_factors_round_rate, +	.set_rate = clk_factors_set_rate, +}; + +/** + * clk_register_factors - register a factors clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust factors + * @config: shift and width of factors n, k, m and p + * @get_factors: function to calculate the factors for a given frequency + * @lock: shared register lock for this clock + */ +struct clk *clk_register_factors(struct device *dev, const char *name, +				 const char *parent_name, +				 unsigned long flags, void __iomem *reg, +				 struct clk_factors_config *config, +				 void (*get_factors)(u32 *rate, u32 parent, +						     u8 *n, u8 *k, u8 *m, u8 *p), +				 spinlock_t *lock) +{ +	struct clk_factors *factors; +	struct clk *clk; +	struct clk_init_data init; + +	/* allocate the factors */ +	factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); +	if (!factors) { +		pr_err("%s: could not allocate factors clk\n", __func__); +		return ERR_PTR(-ENOMEM); +	} + +	init.name = name; +	init.ops = &clk_factors_ops; +	init.flags = flags; +	init.parent_names = (parent_name ? &parent_name : NULL); +	init.num_parents = (parent_name ? 1 : 0); + +	/* struct clk_factors assignments */ +	factors->reg = reg; +	factors->config = config; +	factors->lock = lock; +	factors->hw.init = &init; +	factors->get_factors = get_factors; + +	/* register the clock */ +	clk = clk_register(dev, &factors->hw); + +	if (IS_ERR(clk)) +		kfree(factors); + +	return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 00000000000..f49851cc438 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h @@ -0,0 +1,27 @@ +#ifndef __MACH_SUNXI_CLK_FACTORS_H +#define __MACH_SUNXI_CLK_FACTORS_H + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> + +#define SUNXI_FACTORS_NOT_APPLICABLE	(0) + +struct clk_factors_config { +	u8 nshift; +	u8 nwidth; +	u8 kshift; +	u8 kwidth; +	u8 mshift; +	u8 mwidth; +	u8 pshift; +	u8 pwidth; +}; + +struct clk *clk_register_factors(struct device *dev, const char *name, +				 const char *parent_name, +				 unsigned long flags, void __iomem *reg, +				 struct clk_factors_config *config, +				 void (*get_factors) (u32 *rate, u32 parent_rate, +						      u8 *n, u8 *k, u8 *m, u8 *p), +				 spinlock_t *lock); +#endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 00000000000..d528a249669 --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -0,0 +1,362 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López <emilio@elopez.com.ar> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/sunxi.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk-factors.h" + +static DEFINE_SPINLOCK(clk_lock); + +/** + * sunxi_osc_clk_setup() - Setup function for gatable oscillator + */ + +#define SUNXI_OSC24M_GATE	0 + +static void __init sunxi_osc_clk_setup(struct device_node *node) +{ +	struct clk *clk; +	const char *clk_name = node->name; +	const char *parent; +	void *reg; + +	reg = of_iomap(node, 0); + +	parent = of_clk_get_parent_name(node, 0); + +	clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED, +				reg, SUNXI_OSC24M_GATE, 0, &clk_lock); + +	if (clk) { +		of_clk_add_provider(node, of_clk_src_simple_get, clk); +		clk_register_clkdev(clk, clk_name, NULL); +	} +} + + + +/** + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + * parent_rate is always 24Mhz + */ + +static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, +				   u8 *n, u8 *k, u8 *m, u8 *p) +{ +	u8 div; + +	/* Normalize value to a 6M multiple */ +	div = *freq / 6000000; +	*freq = 6000000 * div; + +	/* we were called to round the frequency, we can now return */ +	if (n == NULL) +		return; + +	/* m is always zero for pll1 */ +	*m = 0; + +	/* k is 1 only on these cases */ +	if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) +		*k = 1; +	else +		*k = 0; + +	/* p will be 3 for divs under 10 */ +	if (div < 10) +		*p = 3; + +	/* p will be 2 for divs between 10 - 20 and odd divs under 32 */ +	else if (div < 20 || (div < 32 && (div & 1))) +		*p = 2; + +	/* p will be 1 for even divs under 32, divs under 40 and odd pairs +	 * of divs between 40-62 */ +	else if (div < 40 || (div < 64 && (div & 2))) +		*p = 1; + +	/* any other entries have p = 0 */ +	else +		*p = 0; + +	/* calculate a suitable n based on k and p */ +	div <<= *p; +	div /= (*k + 1); +	*n = div / 4; +} + + + +/** + * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * APB1 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, +				   u8 *n, u8 *k, u8 *m, u8 *p) +{ +	u8 calcm, calcp; + +	if (parent_rate < *freq) +		*freq = parent_rate; + +	parent_rate = (parent_rate + (*freq - 1)) / *freq; + +	/* Invalid rate! */ +	if (parent_rate > 32) +		return; + +	if (parent_rate <= 4) +		calcp = 0; +	else if (parent_rate <= 8) +		calcp = 1; +	else if (parent_rate <= 16) +		calcp = 2; +	else +		calcp = 3; + +	calcm = (parent_rate >> calcp) - 1; + +	*freq = (parent_rate >> calcp) / (calcm + 1); + +	/* we were called to round the frequency, we can now return */ +	if (n == NULL) +		return; + +	*m = calcm; +	*p = calcp; +} + + + +/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +struct factors_data { +	struct clk_factors_config *table; +	void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); +}; + +static struct clk_factors_config pll1_config = { +	.nshift = 8, +	.nwidth = 5, +	.kshift = 4, +	.kwidth = 2, +	.mshift = 0, +	.mwidth = 2, +	.pshift = 16, +	.pwidth = 2, +}; + +static struct clk_factors_config apb1_config = { +	.mshift = 0, +	.mwidth = 5, +	.pshift = 16, +	.pwidth = 2, +}; + +static const __initconst struct factors_data pll1_data = { +	.table = &pll1_config, +	.getter = sunxi_get_pll1_factors, +}; + +static const __initconst struct factors_data apb1_data = { +	.table = &apb1_config, +	.getter = sunxi_get_apb1_factors, +}; + +static void __init sunxi_factors_clk_setup(struct device_node *node, +					   struct factors_data *data) +{ +	struct clk *clk; +	const char *clk_name = node->name; +	const char *parent; +	void *reg; + +	reg = of_iomap(node, 0); + +	parent = of_clk_get_parent_name(node, 0); + +	clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED, +				   reg, data->table, data->getter, &clk_lock); + +	if (clk) { +		of_clk_add_provider(node, of_clk_src_simple_get, clk); +		clk_register_clkdev(clk, clk_name, NULL); +	} +} + + + +/** + * sunxi_mux_clk_setup() - Setup function for muxes + */ + +#define SUNXI_MUX_GATE_WIDTH	2 + +struct mux_data { +	u8 shift; +}; + +static const __initconst struct mux_data cpu_data = { +	.shift = 16, +}; + +static const __initconst struct mux_data apb1_mux_data = { +	.shift = 24, +}; + +static void __init sunxi_mux_clk_setup(struct device_node *node, +				       struct mux_data *data) +{ +	struct clk *clk; +	const char *clk_name = node->name; +	const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL); +	void *reg; +	int i = 0; + +	reg = of_iomap(node, 0); + +	while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) +		i++; + +	clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, +			       data->shift, SUNXI_MUX_GATE_WIDTH, +			       0, &clk_lock); + +	if (clk) { +		of_clk_add_provider(node, of_clk_src_simple_get, clk); +		clk_register_clkdev(clk, clk_name, NULL); +	} +} + + + +/** + * sunxi_divider_clk_setup() - Setup function for simple divider clocks + */ + +#define SUNXI_DIVISOR_WIDTH	2 + +struct div_data { +	u8 shift; +	u8 pow; +}; + +static const __initconst struct div_data axi_data = { +	.shift = 0, +	.pow = 0, +}; + +static const __initconst struct div_data ahb_data = { +	.shift = 4, +	.pow = 1, +}; + +static const __initconst struct div_data apb0_data = { +	.shift = 8, +	.pow = 1, +}; + +static void __init sunxi_divider_clk_setup(struct device_node *node, +					   struct div_data *data) +{ +	struct clk *clk; +	const char *clk_name = node->name; +	const char *clk_parent; +	void *reg; + +	reg = of_iomap(node, 0); + +	clk_parent = of_clk_get_parent_name(node, 0); + +	clk = clk_register_divider(NULL, clk_name, clk_parent, 0, +				   reg, data->shift, SUNXI_DIVISOR_WIDTH, +				   data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, +				   &clk_lock); +	if (clk) { +		of_clk_add_provider(node, of_clk_src_simple_get, clk); +		clk_register_clkdev(clk, clk_name, NULL); +	} +} + + +/* Matches for of_clk_init */ +static const __initconst struct of_device_id clk_match[] = { +	{.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, +	{.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,}, +	{} +}; + +/* Matches for factors clocks */ +static const __initconst struct of_device_id clk_factors_match[] = { +	{.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,}, +	{.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,}, +	{} +}; + +/* Matches for divider clocks */ +static const __initconst struct of_device_id clk_div_match[] = { +	{.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,}, +	{.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,}, +	{.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,}, +	{} +}; + +/* Matches for mux clocks */ +static const __initconst struct of_device_id clk_mux_match[] = { +	{.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, +	{.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, +	{} +}; + +static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, +					      void *function) +{ +	struct device_node *np; +	const struct div_data *data; +	const struct of_device_id *match; +	void (*setup_function)(struct device_node *, const void *) = function; + +	for_each_matching_node(np, clk_match) { +		match = of_match_node(clk_match, np); +		data = match->data; +		setup_function(np, data); +	} +} + +void __init sunxi_init_clocks(void) +{ +	/* Register all the simple sunxi clocks on DT */ +	of_clk_init(clk_match); + +	/* Register factor clocks */ +	of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); + +	/* Register divider clocks */ +	of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); + +	/* Register mux clocks */ +	of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); +} diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 2b41b0f4f73..f49fac2d193 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -9,3 +9,4 @@ obj-y					+= clk-super.o  obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o  obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index 6dd533251e7..bafee9895a2 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -41,7 +41,9 @@ static DEFINE_SPINLOCK(periph_ref_lock);  #define write_rst_clr(val, gate) \  	writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg)) -#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32)) +#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32)) + +#define LVL2_CLK_GATE_OVRE 0x554  /* Peripheral gate clock ops */  static int clk_periph_is_enabled(struct clk_hw *hw) @@ -83,6 +85,13 @@ static int clk_periph_enable(struct clk_hw *hw)  		}  	} +	if (gate->flags & TEGRA_PERIPH_WAR_1005168) { +		writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); +		writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE); +		udelay(1); +		writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE); +	} +  	spin_unlock_irqrestore(&periph_ref_lock, flags);  	return 0; diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 788486e6331..b2309d37a96 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -16,6 +16,7 @@  #include <linux/clk.h>  #include <linux/clk-provider.h> +#include <linux/export.h>  #include <linux/slab.h>  #include <linux/err.h> @@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c)  	tegra_periph_reset(gate, 0);  } +EXPORT_SYMBOL(tegra_periph_reset_deassert);  void tegra_periph_reset_assert(struct clk *c)  { @@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c)  	tegra_periph_reset(gate, 1);  } +EXPORT_SYMBOL(tegra_periph_reset_assert);  const struct clk_ops tegra_clk_periph_ops = {  	.get_parent = clk_periph_get_parent, @@ -170,14 +173,15 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = {  static struct clk *_tegra_clk_register_periph(const char *name,  			const char **parent_names, int num_parents,  			struct tegra_clk_periph *periph, -			void __iomem *clk_base, u32 offset, bool div) +			void __iomem *clk_base, u32 offset, bool div, +			unsigned long flags)  {  	struct clk *clk;  	struct clk_init_data init;  	init.name = name;  	init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops; -	init.flags = div ? 0 : CLK_SET_RATE_PARENT; +	init.flags = flags;  	init.parent_names = parent_names;  	init.num_parents = num_parents; @@ -202,10 +206,10 @@ static struct clk *_tegra_clk_register_periph(const char *name,  struct clk *tegra_clk_register_periph(const char *name,  		const char **parent_names, int num_parents,  		struct tegra_clk_periph *periph, void __iomem *clk_base, -		u32 offset) +		u32 offset, unsigned long flags)  {  	return _tegra_clk_register_periph(name, parent_names, num_parents, -			periph, clk_base, offset, true); +			periph, clk_base, offset, true, flags);  }  struct clk *tegra_clk_register_periph_nodiv(const char *name, @@ -214,5 +218,5 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,  		u32 offset)  {  	return _tegra_clk_register_periph(name, parent_names, num_parents, -			periph, clk_base, offset, false); +			periph, clk_base, offset, false, CLK_SET_RATE_PARENT);  } diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 165f24734c1..17c2cc086eb 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved. + * Copyright (c) 2012, 2013, NVIDIA CORPORATION.  All rights reserved.   *   * This program is free software; you can redistribute it and/or modify it   * under the terms and conditions of the GNU General Public License, @@ -79,6 +79,48 @@  #define PLLE_SS_CTRL 0x68  #define PLLE_SS_DISABLE (7 << 10) +#define PLLE_AUX_PLLP_SEL	BIT(2) +#define PLLE_AUX_ENABLE_SWCTL	BIT(4) +#define PLLE_AUX_SEQ_ENABLE	BIT(24) +#define PLLE_AUX_PLLRE_SEL	BIT(28) + +#define PLLE_MISC_PLLE_PTS	BIT(8) +#define PLLE_MISC_IDDQ_SW_VALUE	BIT(13) +#define PLLE_MISC_IDDQ_SW_CTRL	BIT(14) +#define PLLE_MISC_VREG_BG_CTRL_SHIFT	4 +#define PLLE_MISC_VREG_BG_CTRL_MASK	(3 << PLLE_MISC_VREG_BG_CTRL_SHIFT) +#define PLLE_MISC_VREG_CTRL_SHIFT	2 +#define PLLE_MISC_VREG_CTRL_MASK	(2 << PLLE_MISC_VREG_CTRL_SHIFT) + +#define PLLCX_MISC_STROBE	BIT(31) +#define PLLCX_MISC_RESET	BIT(30) +#define PLLCX_MISC_SDM_DIV_SHIFT 28 +#define PLLCX_MISC_SDM_DIV_MASK (0x3 << PLLCX_MISC_SDM_DIV_SHIFT) +#define PLLCX_MISC_FILT_DIV_SHIFT 26 +#define PLLCX_MISC_FILT_DIV_MASK (0x3 << PLLCX_MISC_FILT_DIV_SHIFT) +#define PLLCX_MISC_ALPHA_SHIFT 18 +#define PLLCX_MISC_DIV_LOW_RANGE \ +		((0x1 << PLLCX_MISC_SDM_DIV_SHIFT) | \ +		(0x1 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_DIV_HIGH_RANGE \ +		((0x2 << PLLCX_MISC_SDM_DIV_SHIFT) | \ +		(0x2 << PLLCX_MISC_FILT_DIV_SHIFT)) +#define PLLCX_MISC_COEF_LOW_RANGE \ +		((0x14 << PLLCX_MISC_KA_SHIFT) | (0x38 << PLLCX_MISC_KB_SHIFT)) +#define PLLCX_MISC_KA_SHIFT 2 +#define PLLCX_MISC_KB_SHIFT 9 +#define PLLCX_MISC_DEFAULT (PLLCX_MISC_COEF_LOW_RANGE | \ +			    (0x19 << PLLCX_MISC_ALPHA_SHIFT) | \ +			    PLLCX_MISC_DIV_LOW_RANGE | \ +			    PLLCX_MISC_RESET) +#define PLLCX_MISC1_DEFAULT 0x000d2308 +#define PLLCX_MISC2_DEFAULT 0x30211200 +#define PLLCX_MISC3_DEFAULT 0x200 + +#define PMC_PLLM_WB0_OVERRIDE	0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2	0x2b0 +#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27) +  #define PMC_SATA_PWRGT 0x1ac  #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)  #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) @@ -101,6 +143,24 @@  #define divn_max(p) (divn_mask(p))  #define divp_max(p) (1 << (divp_mask(p))) + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */ +#define PLLXC_PDIV_MAX			14 + +/* non-monotonic mapping below is not a typo */ +static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = { +	/* PDIV: 0, 1, 2, 3, 4, 5, 6,  7,  8,  9, 10, 11, 12, 13, 14 */ +	/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 +}; + +#define PLLCX_PDIV_MAX 7 +static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = { +	/* PDIV: 0, 1, 2, 3, 4, 5,  6,  7 */ +	/* p: */ 1, 2, 3, 4, 6, 8, 12, 16 +}; +#endif +  static void clk_pll_enable_lock(struct tegra_clk_pll *pll)  {  	u32 val; @@ -108,25 +168,36 @@ static void clk_pll_enable_lock(struct tegra_clk_pll *pll)  	if (!(pll->flags & TEGRA_PLL_USE_LOCK))  		return; +	if (!(pll->flags & TEGRA_PLL_HAS_LOCK_ENABLE)) +		return; +  	val = pll_readl_misc(pll);  	val |= BIT(pll->params->lock_enable_bit_idx);  	pll_writel_misc(val, pll);  } -static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll, -				 void __iomem *lock_addr, u32 lock_bit_idx) +static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll)  {  	int i; -	u32 val; +	u32 val, lock_mask; +	void __iomem *lock_addr;  	if (!(pll->flags & TEGRA_PLL_USE_LOCK)) {  		udelay(pll->params->lock_delay);  		return 0;  	} +	lock_addr = pll->clk_base; +	if (pll->flags & TEGRA_PLL_LOCK_MISC) +		lock_addr += pll->params->misc_reg; +	else +		lock_addr += pll->params->base_reg; + +	lock_mask = pll->params->lock_mask; +  	for (i = 0; i < pll->params->lock_delay; i++) {  		val = readl_relaxed(lock_addr); -		if (val & BIT(lock_bit_idx)) { +		if ((val & lock_mask) == lock_mask) {  			udelay(PLL_POST_LOCK_DELAY);  			return 0;  		} @@ -155,7 +226,7 @@ static int clk_pll_is_enabled(struct clk_hw *hw)  	return val & PLL_BASE_ENABLE ? 1 : 0;  } -static int _clk_pll_enable(struct clk_hw *hw) +static void _clk_pll_enable(struct clk_hw *hw)  {  	struct tegra_clk_pll *pll = to_clk_pll(hw);  	u32 val; @@ -163,7 +234,8 @@ static int _clk_pll_enable(struct clk_hw *hw)  	clk_pll_enable_lock(pll);  	val = pll_readl_base(pll); -	val &= ~PLL_BASE_BYPASS; +	if (pll->flags & TEGRA_PLL_BYPASS) +		val &= ~PLL_BASE_BYPASS;  	val |= PLL_BASE_ENABLE;  	pll_writel_base(val, pll); @@ -172,11 +244,6 @@ static int _clk_pll_enable(struct clk_hw *hw)  		val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;  		writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);  	} - -	clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg, -			      pll->params->lock_bit_idx); - -	return 0;  }  static void _clk_pll_disable(struct clk_hw *hw) @@ -185,7 +252,9 @@ static void _clk_pll_disable(struct clk_hw *hw)  	u32 val;  	val = pll_readl_base(pll); -	val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); +	if (pll->flags & TEGRA_PLL_BYPASS) +		val &= ~PLL_BASE_BYPASS; +	val &= ~PLL_BASE_ENABLE;  	pll_writel_base(val, pll);  	if (pll->flags & TEGRA_PLLM) { @@ -204,7 +273,9 @@ static int clk_pll_enable(struct clk_hw *hw)  	if (pll->lock)  		spin_lock_irqsave(pll->lock, flags); -	ret = _clk_pll_enable(hw); +	_clk_pll_enable(hw); + +	ret = clk_pll_wait_for_lock(pll);  	if (pll->lock)  		spin_unlock_irqrestore(pll->lock, flags); @@ -241,8 +312,6 @@ static int _get_table_rate(struct clk_hw *hw,  	if (sel->input_rate == 0)  		return -EINVAL; -	BUG_ON(sel->p < 1); -  	cfg->input_rate = sel->input_rate;  	cfg->output_rate = sel->output_rate;  	cfg->m = sel->m; @@ -257,6 +326,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,  		      unsigned long rate, unsigned long parent_rate)  {  	struct tegra_clk_pll *pll = to_clk_pll(hw); +	struct pdiv_map *p_tohw = pll->params->pdiv_tohw;  	unsigned long cfreq;  	u32 p_div = 0; @@ -290,88 +360,119 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,  	     cfg->output_rate <<= 1)  		p_div++; -	cfg->p = 1 << p_div;  	cfg->m = parent_rate / cfreq;  	cfg->n = cfg->output_rate / cfreq;  	cfg->cpcon = OUT_OF_TABLE_CPCON;  	if (cfg->m > divm_max(pll) || cfg->n > divn_max(pll) || -	    cfg->p > divp_max(pll) || cfg->output_rate > pll->params->vco_max) { +	    (1 << p_div) > divp_max(pll) +	    || cfg->output_rate > pll->params->vco_max) {  		pr_err("%s: Failed to set %s rate %lu\n",  		       __func__, __clk_get_name(hw->clk), rate);  		return -EINVAL;  	} +	if (p_tohw) { +		p_div = 1 << p_div; +		while (p_tohw->pdiv) { +			if (p_div <= p_tohw->pdiv) { +				cfg->p = p_tohw->hw_val; +				break; +			} +			p_tohw++; +		} +		if (!p_tohw->pdiv) +			return -EINVAL; +	} else +		cfg->p = p_div; +  	return 0;  } -static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, -			unsigned long rate) +static void _update_pll_mnp(struct tegra_clk_pll *pll, +			    struct tegra_clk_pll_freq_table *cfg)  { -	struct tegra_clk_pll *pll = to_clk_pll(hw); -	unsigned long flags = 0; -	u32 divp, val, old_base; -	int state; - -	divp = __ffs(cfg->p); - -	if (pll->flags & TEGRA_PLLU) -		divp ^= 1; +	u32 val; -	if (pll->lock) -		spin_lock_irqsave(pll->lock, flags); +	val = pll_readl_base(pll); -	old_base = val = pll_readl_base(pll);  	val &= ~((divm_mask(pll) << pll->divm_shift) |  		 (divn_mask(pll) << pll->divn_shift) |  		 (divp_mask(pll) << pll->divp_shift));  	val |= ((cfg->m << pll->divm_shift) |  		(cfg->n << pll->divn_shift) | -		(divp << pll->divp_shift)); -	if (val == old_base) { -		if (pll->lock) -			spin_unlock_irqrestore(pll->lock, flags); -		return 0; +		(cfg->p << pll->divp_shift)); + +	pll_writel_base(val, pll); +} + +static void _get_pll_mnp(struct tegra_clk_pll *pll, +			 struct tegra_clk_pll_freq_table *cfg) +{ +	u32 val; + +	val = pll_readl_base(pll); + +	cfg->m = (val >> pll->divm_shift) & (divm_mask(pll)); +	cfg->n = (val >> pll->divn_shift) & (divn_mask(pll)); +	cfg->p = (val >> pll->divp_shift) & (divp_mask(pll)); +} + +static void _update_pll_cpcon(struct tegra_clk_pll *pll, +			      struct tegra_clk_pll_freq_table *cfg, +			      unsigned long rate) +{ +	u32 val; + +	val = pll_readl_misc(pll); + +	val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); +	val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; + +	if (pll->flags & TEGRA_PLL_SET_LFCON) { +		val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); +		if (cfg->n >= PLLDU_LFCON_SET_DIVN) +			val |= 1 << PLL_MISC_LFCON_SHIFT; +	} else if (pll->flags & TEGRA_PLL_SET_DCCON) { +		val &= ~(1 << PLL_MISC_DCCON_SHIFT); +		if (rate >= (pll->params->vco_max >> 1)) +			val |= 1 << PLL_MISC_DCCON_SHIFT;  	} +	pll_writel_misc(val, pll); +} + +static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, +			unsigned long rate) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	int state, ret = 0; +  	state = clk_pll_is_enabled(hw); -	if (state) { +	if (state)  		_clk_pll_disable(hw); -		val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE); -	} -	pll_writel_base(val, pll); -	if (pll->flags & TEGRA_PLL_HAS_CPCON) { -		val = pll_readl_misc(pll); -		val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT); -		val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT; -		if (pll->flags & TEGRA_PLL_SET_LFCON) { -			val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT); -			if (cfg->n >= PLLDU_LFCON_SET_DIVN) -				val |= 0x1 << PLL_MISC_LFCON_SHIFT; -		} else if (pll->flags & TEGRA_PLL_SET_DCCON) { -			val &= ~(0x1 << PLL_MISC_DCCON_SHIFT); -			if (rate >= (pll->params->vco_max >> 1)) -				val |= 0x1 << PLL_MISC_DCCON_SHIFT; -		} -		pll_writel_misc(val, pll); -	} +	_update_pll_mnp(pll, cfg); -	if (pll->lock) -		spin_unlock_irqrestore(pll->lock, flags); +	if (pll->flags & TEGRA_PLL_HAS_CPCON) +		_update_pll_cpcon(pll, cfg, rate); -	if (state) -		clk_pll_enable(hw); +	if (state) { +		_clk_pll_enable(hw); +		ret = clk_pll_wait_for_lock(pll); +	} -	return 0; +	return ret;  }  static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,  			unsigned long parent_rate)  {  	struct tegra_clk_pll *pll = to_clk_pll(hw); -	struct tegra_clk_pll_freq_table cfg; +	struct tegra_clk_pll_freq_table cfg, old_cfg; +	unsigned long flags = 0; +	int ret = 0;  	if (pll->flags & TEGRA_PLL_FIXED) {  		if (rate != pll->fixed_rate) { @@ -387,7 +488,18 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,  	    _calc_rate(hw, &cfg, rate, parent_rate))  		return -EINVAL; -	return _program_pll(hw, &cfg, rate); +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_get_pll_mnp(pll, &old_cfg); + +	if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) +		ret = _program_pll(hw, &cfg, rate); + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret;  }  static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -409,7 +521,7 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,  		return -EINVAL;  	output_rate *= cfg.n; -	do_div(output_rate, cfg.m * cfg.p); +	do_div(output_rate, cfg.m * (1 << cfg.p));  	return output_rate;  } @@ -418,11 +530,15 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,  					 unsigned long parent_rate)  {  	struct tegra_clk_pll *pll = to_clk_pll(hw); -	u32 val = pll_readl_base(pll); -	u32 divn = 0, divm = 0, divp = 0; +	struct tegra_clk_pll_freq_table cfg; +	struct pdiv_map *p_tohw = pll->params->pdiv_tohw; +	u32 val;  	u64 rate = parent_rate; +	int pdiv; + +	val = pll_readl_base(pll); -	if (val & PLL_BASE_BYPASS) +	if ((pll->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS))  		return parent_rate;  	if ((pll->flags & TEGRA_PLL_FIXED) && !(val & PLL_BASE_OVERRIDE)) { @@ -435,16 +551,29 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,  		return pll->fixed_rate;  	} -	divp = (val >> pll->divp_shift) & (divp_mask(pll)); -	if (pll->flags & TEGRA_PLLU) -		divp ^= 1; +	_get_pll_mnp(pll, &cfg); -	divn = (val >> pll->divn_shift) & (divn_mask(pll)); -	divm = (val >> pll->divm_shift) & (divm_mask(pll)); -	divm *= (1 << divp); +	if (p_tohw) { +		while (p_tohw->pdiv) { +			if (cfg.p == p_tohw->hw_val) { +				pdiv = p_tohw->pdiv; +				break; +			} +			p_tohw++; +		} + +		if (!p_tohw->pdiv) { +			WARN_ON(1); +			pdiv = 1; +		} +	} else +		pdiv = 1 << cfg.p; + +	cfg.m *= pdiv; + +	rate *= cfg.n; +	do_div(rate, cfg.m); -	rate *= divn; -	do_div(rate, divm);  	return rate;  } @@ -538,8 +667,8 @@ static int clk_plle_enable(struct clk_hw *hw)  	val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE);  	pll_writel_base(val, pll); -	clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->misc_reg, -			      pll->params->lock_bit_idx); +	clk_pll_wait_for_lock(pll); +  	return 0;  } @@ -577,28 +706,531 @@ const struct clk_ops tegra_clk_plle_ops = {  	.enable = clk_plle_enable,  }; -static struct clk *_tegra_clk_register_pll(const char *name, -		const char *parent_name, void __iomem *clk_base, -		void __iomem *pmc, unsigned long flags, -		unsigned long fixed_rate, -		struct tegra_clk_pll_params *pll_params, u8 pll_flags, -		struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock, -		const struct clk_ops *ops) +#ifdef CONFIG_ARCH_TEGRA_114_SOC + +static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params, +			   unsigned long parent_rate) +{ +	if (parent_rate > pll_params->cf_max) +		return 2; +	else +		return 1; +} + +static int clk_pll_iddq_enable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; + +	u32 val; +	int ret; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	val = pll_readl(pll->params->iddq_reg, pll); +	val &= ~BIT(pll->params->iddq_bit_idx); +	pll_writel(val, pll->params->iddq_reg, pll); +	udelay(2); + +	_clk_pll_enable(hw); + +	ret = clk_pll_wait_for_lock(pll); + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return 0; +} + +static void clk_pll_iddq_disable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; +	u32 val; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_clk_pll_disable(hw); + +	val = pll_readl(pll->params->iddq_reg, pll); +	val |= BIT(pll->params->iddq_bit_idx); +	pll_writel(val, pll->params->iddq_reg, pll); +	udelay(2); + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); +} + +static int _calc_dynamic_ramp_rate(struct clk_hw *hw, +				struct tegra_clk_pll_freq_table *cfg, +				unsigned long rate, unsigned long parent_rate) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned int p; + +	if (!rate) +		return -EINVAL; + +	p = DIV_ROUND_UP(pll->params->vco_min, rate); +	cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); +	cfg->p = p; +	cfg->output_rate = rate * cfg->p; +	cfg->n = cfg->output_rate * cfg->m / parent_rate; + +	if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) +		return -EINVAL; + +	return 0; +} + +static int _pll_ramp_calc_pll(struct clk_hw *hw, +			      struct tegra_clk_pll_freq_table *cfg, +			      unsigned long rate, unsigned long parent_rate) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	int err = 0; + +	err = _get_table_rate(hw, cfg, rate, parent_rate); +	if (err < 0) +		err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate); +	else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { +			WARN_ON(1); +			err = -EINVAL; +			goto out; +	} + +	if (!cfg->p || (cfg->p >  pll->params->max_p)) +		err = -EINVAL; + +out: +	return err; +} + +static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, +				unsigned long parent_rate) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	struct tegra_clk_pll_freq_table cfg, old_cfg; +	unsigned long flags = 0; +	int ret = 0; +	u8 old_p; + +	ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); +	if (ret < 0) +		return ret; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_get_pll_mnp(pll, &old_cfg); + +	old_p = pllxc_p[old_cfg.p]; +	if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) { +		cfg.p -= 1; +		ret = _program_pll(hw, &cfg, rate); +	} + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret; +} + +static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, +				unsigned long *prate) +{ +	struct tegra_clk_pll_freq_table cfg; +	int ret = 0; +	u64 output_rate = *prate; + +	ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); +	if (ret < 0) +		return ret; + +	output_rate *= cfg.n; +	do_div(output_rate, cfg.m * cfg.p); + +	return output_rate; +} + +static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, +				unsigned long parent_rate) +{ +	struct tegra_clk_pll_freq_table cfg; +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; +	int state, ret = 0; +	u32 val; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	state = clk_pll_is_enabled(hw); +	if (state) { +		if (rate != clk_get_rate(hw->clk)) { +			pr_err("%s: Cannot change active PLLM\n", __func__); +			ret = -EINVAL; +			goto out; +		} +		goto out; +	} + +	ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); +	if (ret < 0) +		goto out; + +	cfg.p -= 1; + +	val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); +	if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { +		val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); +		val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) : +			(val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK); +		writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); + +		val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); +		val &= ~(divn_mask(pll) | divm_mask(pll)); +		val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift); +		writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); +	} else +		_update_pll_mnp(pll, &cfg); + + +out: +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret; +} + +static void _pllcx_strobe(struct tegra_clk_pll *pll) +{ +	u32 val; + +	val = pll_readl_misc(pll); +	val |= PLLCX_MISC_STROBE; +	pll_writel_misc(val, pll); +	udelay(2); + +	val &= ~PLLCX_MISC_STROBE; +	pll_writel_misc(val, pll); +} + +static int clk_pllc_enable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	u32 val; +	int ret = 0; +	unsigned long flags = 0; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_clk_pll_enable(hw); +	udelay(2); + +	val = pll_readl_misc(pll); +	val &= ~PLLCX_MISC_RESET; +	pll_writel_misc(val, pll); +	udelay(2); + +	_pllcx_strobe(pll); + +	ret = clk_pll_wait_for_lock(pll); + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret; +} + +static void _clk_pllc_disable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	u32 val; + +	_clk_pll_disable(hw); + +	val = pll_readl_misc(pll); +	val |= PLLCX_MISC_RESET; +	pll_writel_misc(val, pll); +	udelay(2); +} + +static void clk_pllc_disable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_clk_pllc_disable(hw); + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); +} + +static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, +					unsigned long input_rate, u32 n) +{ +	u32 val, n_threshold; + +	switch (input_rate) { +	case 12000000: +		n_threshold = 70; +		break; +	case 13000000: +	case 26000000: +		n_threshold = 71; +		break; +	case 16800000: +		n_threshold = 55; +		break; +	case 19200000: +		n_threshold = 48; +		break; +	default: +		pr_err("%s: Unexpected reference rate %lu\n", +			__func__, input_rate); +		return -EINVAL; +	} + +	val = pll_readl_misc(pll); +	val &= ~(PLLCX_MISC_SDM_DIV_MASK | PLLCX_MISC_FILT_DIV_MASK); +	val |= n <= n_threshold ? +		PLLCX_MISC_DIV_LOW_RANGE : PLLCX_MISC_DIV_HIGH_RANGE; +	pll_writel_misc(val, pll); + +	return 0; +} + +static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, +				unsigned long parent_rate) +{ +	struct tegra_clk_pll_freq_table cfg; +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; +	int state, ret = 0; +	u32 val; +	u16 old_m, old_n; +	u8 old_p; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); +	if (ret < 0) +		goto out; + +	val = pll_readl_base(pll); +	old_m = (val >> pll->divm_shift) & (divm_mask(pll)); +	old_n = (val >> pll->divn_shift) & (divn_mask(pll)); +	old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))]; + +	if (cfg.m != old_m) { +		WARN_ON(1); +		goto out; +	} + +	if (old_n == cfg.n && old_p == cfg.p) +		goto out; + +	cfg.p -= 1; + +	state = clk_pll_is_enabled(hw); +	if (state) +		_clk_pllc_disable(hw); + +	ret = _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); +	if (ret < 0) +		goto out; + +	_update_pll_mnp(pll, &cfg); + +	if (state) +		ret = clk_pllc_enable(hw); + +out: +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret; +} + +static long _pllre_calc_rate(struct tegra_clk_pll *pll, +			     struct tegra_clk_pll_freq_table *cfg, +			     unsigned long rate, unsigned long parent_rate) +{ +	u16 m, n; +	u64 output_rate = parent_rate; + +	m = _pll_fixed_mdiv(pll->params, parent_rate); +	n = rate * m / parent_rate; + +	output_rate *= n; +	do_div(output_rate, m); + +	if (cfg) { +		cfg->m = m; +		cfg->n = n; +	} + +	return output_rate; +} +static int clk_pllre_set_rate(struct clk_hw *hw, unsigned long rate, +				unsigned long parent_rate) +{ +	struct tegra_clk_pll_freq_table cfg, old_cfg; +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; +	int state, ret = 0; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_pllre_calc_rate(pll, &cfg, rate, parent_rate); +	_get_pll_mnp(pll, &old_cfg); +	cfg.p = old_cfg.p; + +	if (cfg.m != old_cfg.m || cfg.n != old_cfg.n) { +		state = clk_pll_is_enabled(hw); +		if (state) +			_clk_pll_disable(hw); + +		_update_pll_mnp(pll, &cfg); + +		if (state) { +			_clk_pll_enable(hw); +			ret = clk_pll_wait_for_lock(pll); +		} +	} + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret; +} + +static unsigned long clk_pllre_recalc_rate(struct clk_hw *hw, +					 unsigned long parent_rate) +{ +	struct tegra_clk_pll_freq_table cfg; +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	u64 rate = parent_rate; + +	_get_pll_mnp(pll, &cfg); + +	rate *= cfg.n; +	do_div(rate, cfg.m); + +	return rate; +} + +static long clk_pllre_round_rate(struct clk_hw *hw, unsigned long rate, +				 unsigned long *prate) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); + +	return _pllre_calc_rate(pll, NULL, rate, *prate); +} + +static int clk_plle_tegra114_enable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	struct tegra_clk_pll_freq_table sel; +	u32 val; +	int ret; +	unsigned long flags = 0; +	unsigned long input_rate = clk_get_rate(clk_get_parent(hw->clk)); + +	if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate)) +		return -EINVAL; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	val = pll_readl_base(pll); +	val &= ~BIT(29); /* Disable lock override */ +	pll_writel_base(val, pll); + +	val = pll_readl(pll->params->aux_reg, pll); +	val |= PLLE_AUX_ENABLE_SWCTL; +	val &= ~PLLE_AUX_SEQ_ENABLE; +	pll_writel(val, pll->params->aux_reg, pll); +	udelay(1); + +	val = pll_readl_misc(pll); +	val |= PLLE_MISC_LOCK_ENABLE; +	val |= PLLE_MISC_IDDQ_SW_CTRL; +	val &= ~PLLE_MISC_IDDQ_SW_VALUE; +	val |= PLLE_MISC_PLLE_PTS; +	val |= PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK; +	pll_writel_misc(val, pll); +	udelay(5); + +	val = pll_readl(PLLE_SS_CTRL, pll); +	val |= PLLE_SS_DISABLE; +	pll_writel(val, PLLE_SS_CTRL, pll); + +	val = pll_readl_base(pll); +	val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); +	val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); +	val |= sel.m << pll->divm_shift; +	val |= sel.n << pll->divn_shift; +	val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; +	pll_writel_base(val, pll); +	udelay(1); + +	_clk_pll_enable(hw); +	ret = clk_pll_wait_for_lock(pll); + +	if (ret < 0) +		goto out; + +	/* TODO: enable hw control of xusb brick pll */ + +out: +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); + +	return ret; +} + +static void clk_plle_tegra114_disable(struct clk_hw *hw) +{ +	struct tegra_clk_pll *pll = to_clk_pll(hw); +	unsigned long flags = 0; +	u32 val; + +	if (pll->lock) +		spin_lock_irqsave(pll->lock, flags); + +	_clk_pll_disable(hw); + +	val = pll_readl_misc(pll); +	val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE; +	pll_writel_misc(val, pll); +	udelay(1); + +	if (pll->lock) +		spin_unlock_irqrestore(pll->lock, flags); +} +#endif + +static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, +		void __iomem *pmc, unsigned long fixed_rate, +		struct tegra_clk_pll_params *pll_params, u32 pll_flags, +		struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)  {  	struct tegra_clk_pll *pll; -	struct clk *clk; -	struct clk_init_data init;  	pll = kzalloc(sizeof(*pll), GFP_KERNEL);  	if (!pll)  		return ERR_PTR(-ENOMEM); -	init.name = name; -	init.ops = ops; -	init.flags = flags; -	init.parent_names = (parent_name ? &parent_name : NULL); -	init.num_parents = (parent_name ? 1 : 0); -  	pll->clk_base = clk_base;  	pll->pmc = pmc; @@ -615,34 +1247,336 @@ static struct clk *_tegra_clk_register_pll(const char *name,  	pll->divm_shift = PLL_BASE_DIVM_SHIFT;  	pll->divm_width = PLL_BASE_DIVM_WIDTH; +	return pll; +} + +static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll, +		const char *name, const char *parent_name, unsigned long flags, +		const struct clk_ops *ops) +{ +	struct clk_init_data init; + +	init.name = name; +	init.ops = ops; +	init.flags = flags; +	init.parent_names = (parent_name ? &parent_name : NULL); +	init.num_parents = (parent_name ? 1 : 0); +  	/* Data in .init is copied by clk_register(), so stack variable OK */  	pll->hw.init = &init; -	clk = clk_register(NULL, &pll->hw); -	if (IS_ERR(clk)) -		kfree(pll); - -	return clk; +	return clk_register(NULL, &pll->hw);  }  struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,  		void __iomem *clk_base, void __iomem *pmc,  		unsigned long flags, unsigned long fixed_rate, -		struct tegra_clk_pll_params *pll_params, u8 pll_flags, +		struct tegra_clk_pll_params *pll_params, u32 pll_flags,  		struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)  { -	return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, -			flags, fixed_rate, pll_params, pll_flags, freq_table, -			lock, &tegra_clk_pll_ops); +	struct tegra_clk_pll *pll; +	struct clk *clk; + +	pll_flags |= TEGRA_PLL_BYPASS; +	pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; +	pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, +			      freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_pll_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk;  }  struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,  		void __iomem *clk_base, void __iomem *pmc,  		unsigned long flags, unsigned long fixed_rate, -		struct tegra_clk_pll_params *pll_params, u8 pll_flags, +		struct tegra_clk_pll_params *pll_params, u32 pll_flags,  		struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock)  { -	return _tegra_clk_register_pll(name, parent_name, clk_base, pmc, -			flags, fixed_rate, pll_params, pll_flags, freq_table, -			lock, &tegra_clk_plle_ops); +	struct tegra_clk_pll *pll; +	struct clk *clk; + +	pll_flags |= TEGRA_PLL_LOCK_MISC | TEGRA_PLL_BYPASS; +	pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; +	pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, +			      freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_plle_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk; +} + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +const struct clk_ops tegra_clk_pllxc_ops = { +	.is_enabled = clk_pll_is_enabled, +	.enable = clk_pll_iddq_enable, +	.disable = clk_pll_iddq_disable, +	.recalc_rate = clk_pll_recalc_rate, +	.round_rate = clk_pll_ramp_round_rate, +	.set_rate = clk_pllxc_set_rate, +}; + +const struct clk_ops tegra_clk_pllm_ops = { +	.is_enabled = clk_pll_is_enabled, +	.enable = clk_pll_iddq_enable, +	.disable = clk_pll_iddq_disable, +	.recalc_rate = clk_pll_recalc_rate, +	.round_rate = clk_pll_ramp_round_rate, +	.set_rate = clk_pllm_set_rate, +}; + +const struct clk_ops tegra_clk_pllc_ops = { +	.is_enabled = clk_pll_is_enabled, +	.enable = clk_pllc_enable, +	.disable = clk_pllc_disable, +	.recalc_rate = clk_pll_recalc_rate, +	.round_rate = clk_pll_ramp_round_rate, +	.set_rate = clk_pllc_set_rate, +}; + +const struct clk_ops tegra_clk_pllre_ops = { +	.is_enabled = clk_pll_is_enabled, +	.enable = clk_pll_iddq_enable, +	.disable = clk_pll_iddq_disable, +	.recalc_rate = clk_pllre_recalc_rate, +	.round_rate = clk_pllre_round_rate, +	.set_rate = clk_pllre_set_rate, +}; + +const struct clk_ops tegra_clk_plle_tegra114_ops = { +	.is_enabled =  clk_pll_is_enabled, +	.enable = clk_plle_tegra114_enable, +	.disable = clk_plle_tegra114_disable, +	.recalc_rate = clk_pll_recalc_rate, +}; + + +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, +			  void __iomem *clk_base, void __iomem *pmc, +			  unsigned long flags, unsigned long fixed_rate, +			  struct tegra_clk_pll_params *pll_params, +			  u32 pll_flags, +			  struct tegra_clk_pll_freq_table *freq_table, +			  spinlock_t *lock) +{ +	struct tegra_clk_pll *pll; +	struct clk *clk; + +	if (!pll_params->pdiv_tohw) +		return ERR_PTR(-EINVAL); + +	pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; +	pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, +			      freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_pllxc_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk; +} + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, +			  void __iomem *clk_base, void __iomem *pmc, +			  unsigned long flags, unsigned long fixed_rate, +			  struct tegra_clk_pll_params *pll_params, +			  u32 pll_flags, +			  struct tegra_clk_pll_freq_table *freq_table, +			  spinlock_t *lock, unsigned long parent_rate) +{ +	u32 val; +	struct tegra_clk_pll *pll; +	struct clk *clk; + +	pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; +	pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, +			      freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	/* program minimum rate by default */ + +	val = pll_readl_base(pll); +	if (val & PLL_BASE_ENABLE) +		WARN_ON(val & pll_params->iddq_bit_idx); +	else { +		int m; + +		m = _pll_fixed_mdiv(pll_params, parent_rate); +		val = m << PLL_BASE_DIVM_SHIFT; +		val |= (pll_params->vco_min / parent_rate) +				<< PLL_BASE_DIVN_SHIFT; +		pll_writel_base(val, pll); +	} + +	/* disable lock override */ + +	val = pll_readl_misc(pll); +	val &= ~BIT(29); +	pll_writel_misc(val, pll); + +	pll_flags |= TEGRA_PLL_LOCK_MISC; +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_pllre_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk; +} + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, +			  void __iomem *clk_base, void __iomem *pmc, +			  unsigned long flags, unsigned long fixed_rate, +			  struct tegra_clk_pll_params *pll_params, +			  u32 pll_flags, +			  struct tegra_clk_pll_freq_table *freq_table, +			  spinlock_t *lock) +{ +	struct tegra_clk_pll *pll; +	struct clk *clk; + +	if (!pll_params->pdiv_tohw) +		return ERR_PTR(-EINVAL); + +	pll_flags |= TEGRA_PLL_BYPASS; +	pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; +	pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, +			      freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_pllm_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk; +} + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, +			  void __iomem *clk_base, void __iomem *pmc, +			  unsigned long flags, unsigned long fixed_rate, +			  struct tegra_clk_pll_params *pll_params, +			  u32 pll_flags, +			  struct tegra_clk_pll_freq_table *freq_table, +			  spinlock_t *lock) +{ +	struct clk *parent, *clk; +	struct pdiv_map *p_tohw = pll_params->pdiv_tohw; +	struct tegra_clk_pll *pll; +	struct tegra_clk_pll_freq_table cfg; +	unsigned long parent_rate; + +	if (!p_tohw) +		return ERR_PTR(-EINVAL); + +	parent = __clk_lookup(parent_name); +	if (IS_ERR(parent)) { +		WARN(1, "parent clk %s of %s must be registered first\n", +			name, parent_name); +		return ERR_PTR(-EINVAL); +	} + +	pll_flags |= TEGRA_PLL_BYPASS; +	pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, +			      freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	parent_rate = __clk_get_rate(parent); + +	/* +	 * Most of PLLC register fields are shadowed, and can not be read +	 * directly from PLL h/w. Hence, actual PLLC boot state is unknown. +	 * Initialize PLL to default state: disabled, reset; shadow registers +	 * loaded with default parameters; dividers are preset for half of +	 * minimum VCO rate (the latter assured that shadowed divider settings +	 * are within supported range). +	 */ + +	cfg.m = _pll_fixed_mdiv(pll_params, parent_rate); +	cfg.n = cfg.m * pll_params->vco_min / parent_rate; + +	while (p_tohw->pdiv) { +		if (p_tohw->pdiv == 2) { +			cfg.p = p_tohw->hw_val; +			break; +		} +		p_tohw++; +	} + +	if (!p_tohw->pdiv) { +		WARN_ON(1); +		return ERR_PTR(-EINVAL); +	} + +	pll_writel_base(0, pll); +	_update_pll_mnp(pll, &cfg); + +	pll_writel_misc(PLLCX_MISC_DEFAULT, pll); +	pll_writel(PLLCX_MISC1_DEFAULT, pll_params->ext_misc_reg[0], pll); +	pll_writel(PLLCX_MISC2_DEFAULT, pll_params->ext_misc_reg[1], pll); +	pll_writel(PLLCX_MISC3_DEFAULT, pll_params->ext_misc_reg[2], pll); + +	_pllcx_update_dynamic_coef(pll, parent_rate, cfg.n); + +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_pllc_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk; +} + +struct clk *tegra_clk_register_plle_tegra114(const char *name, +				const char *parent_name, +				void __iomem *clk_base, unsigned long flags, +				unsigned long fixed_rate, +				struct tegra_clk_pll_params *pll_params, +				struct tegra_clk_pll_freq_table *freq_table, +				spinlock_t *lock) +{ +	struct tegra_clk_pll *pll; +	struct clk *clk; +	u32 val, val_aux; + +	pll = _tegra_init_pll(clk_base, NULL, fixed_rate, pll_params, +			      TEGRA_PLL_HAS_LOCK_ENABLE, freq_table, lock); +	if (IS_ERR(pll)) +		return ERR_CAST(pll); + +	/* ensure parent is set to pll_re_vco */ + +	val = pll_readl_base(pll); +	val_aux = pll_readl(pll_params->aux_reg, pll); + +	if (val & PLL_BASE_ENABLE) { +		if (!(val_aux & PLLE_AUX_PLLRE_SEL)) +			WARN(1, "pll_e enabled with unsupported parent %s\n", +			  (val & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : "pll_ref"); +	} else { +		val_aux |= PLLE_AUX_PLLRE_SEL; +		pll_writel(val, pll_params->aux_reg, pll); +	} + +	clk = _tegra_clk_register_pll(pll, name, parent_name, flags, +				      &tegra_clk_plle_tegra114_ops); +	if (IS_ERR(clk)) +		kfree(pll); + +	return clk;  } +#endif diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c new file mode 100644 index 00000000000..d78e16ee161 --- /dev/null +++ b/drivers/clk/tegra/clk-tegra114.c @@ -0,0 +1,2085 @@ +/* + * Copyright (c) 2012, 2013, NVIDIA CORPORATION.  All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/delay.h> +#include <linux/clk/tegra.h> + +#include "clk.h" + +#define RST_DEVICES_L			0x004 +#define RST_DEVICES_H			0x008 +#define RST_DEVICES_U			0x00C +#define RST_DEVICES_V			0x358 +#define RST_DEVICES_W			0x35C +#define RST_DEVICES_X			0x28C +#define RST_DEVICES_SET_L		0x300 +#define RST_DEVICES_CLR_L		0x304 +#define RST_DEVICES_SET_H		0x308 +#define RST_DEVICES_CLR_H		0x30c +#define RST_DEVICES_SET_U		0x310 +#define RST_DEVICES_CLR_U		0x314 +#define RST_DEVICES_SET_V		0x430 +#define RST_DEVICES_CLR_V		0x434 +#define RST_DEVICES_SET_W		0x438 +#define RST_DEVICES_CLR_W		0x43c +#define RST_DEVICES_NUM			5 + +#define CLK_OUT_ENB_L			0x010 +#define CLK_OUT_ENB_H			0x014 +#define CLK_OUT_ENB_U			0x018 +#define CLK_OUT_ENB_V			0x360 +#define CLK_OUT_ENB_W			0x364 +#define CLK_OUT_ENB_X			0x280 +#define CLK_OUT_ENB_SET_L		0x320 +#define CLK_OUT_ENB_CLR_L		0x324 +#define CLK_OUT_ENB_SET_H		0x328 +#define CLK_OUT_ENB_CLR_H		0x32c +#define CLK_OUT_ENB_SET_U		0x330 +#define CLK_OUT_ENB_CLR_U		0x334 +#define CLK_OUT_ENB_SET_V		0x440 +#define CLK_OUT_ENB_CLR_V		0x444 +#define CLK_OUT_ENB_SET_W		0x448 +#define CLK_OUT_ENB_CLR_W		0x44c +#define CLK_OUT_ENB_SET_X		0x284 +#define CLK_OUT_ENB_CLR_X		0x288 +#define CLK_OUT_ENB_NUM			6 + +#define PLLC_BASE 0x80 +#define PLLC_MISC2 0x88 +#define PLLC_MISC 0x8c +#define PLLC2_BASE 0x4e8 +#define PLLC2_MISC 0x4ec +#define PLLC3_BASE 0x4fc +#define PLLC3_MISC 0x500 +#define PLLM_BASE 0x90 +#define PLLM_MISC 0x9c +#define PLLP_BASE 0xa0 +#define PLLP_MISC 0xac +#define PLLX_BASE 0xe0 +#define PLLX_MISC 0xe4 +#define PLLX_MISC2 0x514 +#define PLLX_MISC3 0x518 +#define PLLD_BASE 0xd0 +#define PLLD_MISC 0xdc +#define PLLD2_BASE 0x4b8 +#define PLLD2_MISC 0x4bc +#define PLLE_BASE 0xe8 +#define PLLE_MISC 0xec +#define PLLA_BASE 0xb0 +#define PLLA_MISC 0xbc +#define PLLU_BASE 0xc0 +#define PLLU_MISC 0xcc +#define PLLRE_BASE 0x4c4 +#define PLLRE_MISC 0x4c8 + +#define PLL_MISC_LOCK_ENABLE 18 +#define PLLC_MISC_LOCK_ENABLE 24 +#define PLLDU_MISC_LOCK_ENABLE 22 +#define PLLE_MISC_LOCK_ENABLE 9 +#define PLLRE_MISC_LOCK_ENABLE 30 + +#define PLLC_IDDQ_BIT 26 +#define PLLX_IDDQ_BIT 3 +#define PLLRE_IDDQ_BIT 16 + +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11) +#define PLLRE_MISC_LOCK BIT(24) +#define PLLCX_BASE_LOCK (BIT(26)|BIT(27)) + +#define PLLE_AUX 0x48c +#define PLLC_OUT 0x84 +#define PLLM_OUT 0x94 +#define PLLP_OUTA 0xa4 +#define PLLP_OUTB 0xa8 +#define PLLA_OUT 0xb4 + +#define AUDIO_SYNC_CLK_I2S0 0x4a0 +#define AUDIO_SYNC_CLK_I2S1 0x4a4 +#define AUDIO_SYNC_CLK_I2S2 0x4a8 +#define AUDIO_SYNC_CLK_I2S3 0x4ac +#define AUDIO_SYNC_CLK_I2S4 0x4b0 +#define AUDIO_SYNC_CLK_SPDIF 0x4b4 + +#define AUDIO_SYNC_DOUBLER 0x49c + +#define PMC_CLK_OUT_CNTRL 0x1a8 +#define PMC_DPD_PADS_ORIDE 0x1c +#define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 +#define PMC_CTRL 0 +#define PMC_CTRL_BLINK_ENB 7 + +#define OSC_CTRL			0x50 +#define OSC_CTRL_OSC_FREQ_SHIFT		28 +#define OSC_CTRL_PLL_REF_DIV_SHIFT	26 + +#define PLLXC_SW_MAX_P			6 + +#define CCLKG_BURST_POLICY 0x368 +#define CCLKLP_BURST_POLICY 0x370 +#define SCLK_BURST_POLICY 0x028 +#define SYSTEM_CLK_RATE 0x030 + +#define UTMIP_PLL_CFG2 0x488 +#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xffff) << 6) +#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2) +#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4) + +#define UTMIP_PLL_CFG1 0x484 +#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 6) +#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17) +#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15) +#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14) +#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12) + +#define UTMIPLL_HW_PWRDN_CFG0			0x52c +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE	BIT(25) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE	BIT(24) +#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET	BIT(6) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE	BIT(5) +#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL	BIT(4) +#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL	BIT(2) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE	BIT(1) +#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL	BIT(0) + +#define CLK_SOURCE_I2S0 0x1d8 +#define CLK_SOURCE_I2S1 0x100 +#define CLK_SOURCE_I2S2 0x104 +#define CLK_SOURCE_NDFLASH 0x160 +#define CLK_SOURCE_I2S3 0x3bc +#define CLK_SOURCE_I2S4 0x3c0 +#define CLK_SOURCE_SPDIF_OUT 0x108 +#define CLK_SOURCE_SPDIF_IN 0x10c +#define CLK_SOURCE_PWM 0x110 +#define CLK_SOURCE_ADX 0x638 +#define CLK_SOURCE_AMX 0x63c +#define CLK_SOURCE_HDA 0x428 +#define CLK_SOURCE_HDA2CODEC_2X 0x3e4 +#define CLK_SOURCE_SBC1 0x134 +#define CLK_SOURCE_SBC2 0x118 +#define CLK_SOURCE_SBC3 0x11c +#define CLK_SOURCE_SBC4 0x1b4 +#define CLK_SOURCE_SBC5 0x3c8 +#define CLK_SOURCE_SBC6 0x3cc +#define CLK_SOURCE_SATA_OOB 0x420 +#define CLK_SOURCE_SATA 0x424 +#define CLK_SOURCE_NDSPEED 0x3f8 +#define CLK_SOURCE_VFIR 0x168 +#define CLK_SOURCE_SDMMC1 0x150 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC3 0x1bc +#define CLK_SOURCE_SDMMC4 0x164 +#define CLK_SOURCE_VDE 0x1c8 +#define CLK_SOURCE_CSITE 0x1d4 +#define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_TRACE 0x634 +#define CLK_SOURCE_OWR 0x1cc +#define CLK_SOURCE_NOR 0x1d0 +#define CLK_SOURCE_MIPI 0x174 +#define CLK_SOURCE_I2C1 0x124 +#define CLK_SOURCE_I2C2 0x198 +#define CLK_SOURCE_I2C3 0x1b8 +#define CLK_SOURCE_I2C4 0x3c4 +#define CLK_SOURCE_I2C5 0x128 +#define CLK_SOURCE_UARTA 0x178 +#define CLK_SOURCE_UARTB 0x17c +#define CLK_SOURCE_UARTC 0x1a0 +#define CLK_SOURCE_UARTD 0x1c0 +#define CLK_SOURCE_UARTE 0x1c4 +#define CLK_SOURCE_UARTA_DBG 0x178 +#define CLK_SOURCE_UARTB_DBG 0x17c +#define CLK_SOURCE_UARTC_DBG 0x1a0 +#define CLK_SOURCE_UARTD_DBG 0x1c0 +#define CLK_SOURCE_UARTE_DBG 0x1c4 +#define CLK_SOURCE_3D 0x158 +#define CLK_SOURCE_2D 0x15c +#define CLK_SOURCE_VI_SENSOR 0x1a8 +#define CLK_SOURCE_VI 0x148 +#define CLK_SOURCE_EPP 0x16c +#define CLK_SOURCE_MSENC 0x1f0 +#define CLK_SOURCE_TSEC 0x1f4 +#define CLK_SOURCE_HOST1X 0x180 +#define CLK_SOURCE_HDMI 0x18c +#define CLK_SOURCE_DISP1 0x138 +#define CLK_SOURCE_DISP2 0x13c +#define CLK_SOURCE_CILAB 0x614 +#define CLK_SOURCE_CILCD 0x618 +#define CLK_SOURCE_CILE 0x61c +#define CLK_SOURCE_DSIALP 0x620 +#define CLK_SOURCE_DSIBLP 0x624 +#define CLK_SOURCE_TSENSOR 0x3b8 +#define CLK_SOURCE_D_AUDIO 0x3d0 +#define CLK_SOURCE_DAM0 0x3d8 +#define CLK_SOURCE_DAM1 0x3dc +#define CLK_SOURCE_DAM2 0x3e0 +#define CLK_SOURCE_ACTMON 0x3e8 +#define CLK_SOURCE_EXTERN1 0x3ec +#define CLK_SOURCE_EXTERN2 0x3f0 +#define CLK_SOURCE_EXTERN3 0x3f4 +#define CLK_SOURCE_I2CSLOW 0x3fc +#define CLK_SOURCE_SE 0x42c +#define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_SOC_THERM 0x644 +#define CLK_SOURCE_XUSB_HOST_SRC 0x600 +#define CLK_SOURCE_XUSB_FALCON_SRC 0x604 +#define CLK_SOURCE_XUSB_FS_SRC 0x608 +#define CLK_SOURCE_XUSB_SS_SRC 0x610 +#define CLK_SOURCE_XUSB_DEV_SRC 0x60c +#define CLK_SOURCE_EMC 0x19c + +static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; + +static void __iomem *clk_base; +static void __iomem *pmc_base; + +static DEFINE_SPINLOCK(pll_d_lock); +static DEFINE_SPINLOCK(pll_d2_lock); +static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(pll_div_lock); +static DEFINE_SPINLOCK(pll_re_lock); +static DEFINE_SPINLOCK(clk_doubler_lock); +static DEFINE_SPINLOCK(clk_out_lock); +static DEFINE_SPINLOCK(sysrate_lock); + +static struct pdiv_map pllxc_p[] = { +	{ .pdiv = 1, .hw_val = 0 }, +	{ .pdiv = 2, .hw_val = 1 }, +	{ .pdiv = 3, .hw_val = 2 }, +	{ .pdiv = 4, .hw_val = 3 }, +	{ .pdiv = 5, .hw_val = 4 }, +	{ .pdiv = 6, .hw_val = 5 }, +	{ .pdiv = 8, .hw_val = 6 }, +	{ .pdiv = 10, .hw_val = 7 }, +	{ .pdiv = 12, .hw_val = 8 }, +	{ .pdiv = 16, .hw_val = 9 }, +	{ .pdiv = 12, .hw_val = 10 }, +	{ .pdiv = 16, .hw_val = 11 }, +	{ .pdiv = 20, .hw_val = 12 }, +	{ .pdiv = 24, .hw_val = 13 }, +	{ .pdiv = 32, .hw_val = 14 }, +	{ .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { +	{ 12000000, 624000000, 104, 0, 2}, +	{ 12000000, 600000000, 100, 0, 2}, +	{ 13000000, 600000000,  92, 0, 2},	/* actual: 598.0 MHz */ +	{ 16800000, 600000000,  71, 0, 2},	/* actual: 596.4 MHz */ +	{ 19200000, 600000000,  62, 0, 2},	/* actual: 595.2 MHz */ +	{ 26000000, 600000000,  92, 1, 2},	/* actual: 598.0 MHz */ +	{ 0, 0, 0, 0, 0, 0 }, +}; + +static struct tegra_clk_pll_params pll_c_params = { +	.input_min = 12000000, +	.input_max = 800000000, +	.cf_min = 12000000, +	.cf_max = 19200000,	/* s/w policy, h/w capability 50 MHz */ +	.vco_min = 600000000, +	.vco_max = 1400000000, +	.base_reg = PLLC_BASE, +	.misc_reg = PLLC_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE, +	.lock_delay = 300, +	.iddq_reg = PLLC_MISC, +	.iddq_bit_idx = PLLC_IDDQ_BIT, +	.max_p = PLLXC_SW_MAX_P, +	.dyn_ramp_reg = PLLC_MISC2, +	.stepa_shift = 17, +	.stepb_shift = 9, +	.pdiv_tohw = pllxc_p, +}; + +static struct pdiv_map pllc_p[] = { +	{ .pdiv = 1, .hw_val = 0 }, +	{ .pdiv = 2, .hw_val = 1 }, +	{ .pdiv = 4, .hw_val = 3 }, +	{ .pdiv = 8, .hw_val = 5 }, +	{ .pdiv = 16, .hw_val = 7 }, +	{ .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = { +	{12000000, 600000000, 100, 0, 2}, +	{13000000, 600000000, 92, 0, 2},	/* actual: 598.0 MHz */ +	{16800000, 600000000, 71, 0, 2},	/* actual: 596.4 MHz */ +	{19200000, 600000000, 62, 0, 2},	/* actual: 595.2 MHz */ +	{26000000, 600000000, 92, 1, 2},	/* actual: 598.0 MHz */ +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_c2_params = { +	.input_min = 12000000, +	.input_max = 48000000, +	.cf_min = 12000000, +	.cf_max = 19200000, +	.vco_min = 600000000, +	.vco_max = 1200000000, +	.base_reg = PLLC2_BASE, +	.misc_reg = PLLC2_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, +	.lock_delay = 300, +	.pdiv_tohw = pllc_p, +	.ext_misc_reg[0] = 0x4f0, +	.ext_misc_reg[1] = 0x4f4, +	.ext_misc_reg[2] = 0x4f8, +}; + +static struct tegra_clk_pll_params pll_c3_params = { +	.input_min = 12000000, +	.input_max = 48000000, +	.cf_min = 12000000, +	.cf_max = 19200000, +	.vco_min = 600000000, +	.vco_max = 1200000000, +	.base_reg = PLLC3_BASE, +	.misc_reg = PLLC3_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, +	.lock_delay = 300, +	.pdiv_tohw = pllc_p, +	.ext_misc_reg[0] = 0x504, +	.ext_misc_reg[1] = 0x508, +	.ext_misc_reg[2] = 0x50c, +}; + +static struct pdiv_map pllm_p[] = { +	{ .pdiv = 1, .hw_val = 0 }, +	{ .pdiv = 2, .hw_val = 1 }, +	{ .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { +	{12000000, 800000000, 66, 0, 1},	/* actual: 792.0 MHz */ +	{13000000, 800000000, 61, 0, 1},	/* actual: 793.0 MHz */ +	{16800000, 800000000, 47, 0, 1},	/* actual: 789.6 MHz */ +	{19200000, 800000000, 41, 0, 1},	/* actual: 787.2 MHz */ +	{26000000, 800000000, 61, 1, 1},	/* actual: 793.0 MHz */ +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_m_params = { +	.input_min = 12000000, +	.input_max = 500000000, +	.cf_min = 12000000, +	.cf_max = 19200000,	/* s/w policy, h/w capability 50 MHz */ +	.vco_min = 400000000, +	.vco_max = 1066000000, +	.base_reg = PLLM_BASE, +	.misc_reg = PLLM_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, +	.lock_delay = 300, +	.max_p = 2, +	.pdiv_tohw = pllm_p, +}; + +static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { +	{12000000, 216000000, 432, 12, 1, 8}, +	{13000000, 216000000, 432, 13, 1, 8}, +	{16800000, 216000000, 360, 14, 1, 8}, +	{19200000, 216000000, 360, 16, 1, 8}, +	{26000000, 216000000, 432, 26, 1, 8}, +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_p_params = { +	.input_min = 2000000, +	.input_max = 31000000, +	.cf_min = 1000000, +	.cf_max = 6000000, +	.vco_min = 200000000, +	.vco_max = 700000000, +	.base_reg = PLLP_BASE, +	.misc_reg = PLLP_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, +	.lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { +	{9600000, 282240000, 147, 5, 0, 4}, +	{9600000, 368640000, 192, 5, 0, 4}, +	{9600000, 240000000, 200, 8, 0, 8}, + +	{28800000, 282240000, 245, 25, 0, 8}, +	{28800000, 368640000, 320, 25, 0, 8}, +	{28800000, 240000000, 200, 24, 0, 8}, +	{0, 0, 0, 0, 0, 0}, +}; + + +static struct tegra_clk_pll_params pll_a_params = { +	.input_min = 2000000, +	.input_max = 31000000, +	.cf_min = 1000000, +	.cf_max = 6000000, +	.vco_min = 200000000, +	.vco_max = 700000000, +	.base_reg = PLLA_BASE, +	.misc_reg = PLLA_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, +	.lock_delay = 300, +}; + +static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { +	{12000000, 216000000, 864, 12, 2, 12}, +	{13000000, 216000000, 864, 13, 2, 12}, +	{16800000, 216000000, 720, 14, 2, 12}, +	{19200000, 216000000, 720, 16, 2, 12}, +	{26000000, 216000000, 864, 26, 2, 12}, + +	{12000000, 594000000, 594, 12, 0, 12}, +	{13000000, 594000000, 594, 13, 0, 12}, +	{16800000, 594000000, 495, 14, 0, 12}, +	{19200000, 594000000, 495, 16, 0, 12}, +	{26000000, 594000000, 594, 26, 0, 12}, + +	{12000000, 1000000000, 1000, 12, 0, 12}, +	{13000000, 1000000000, 1000, 13, 0, 12}, +	{19200000, 1000000000, 625, 12, 0, 12}, +	{26000000, 1000000000, 1000, 26, 0, 12}, + +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_d_params = { +	.input_min = 2000000, +	.input_max = 40000000, +	.cf_min = 1000000, +	.cf_max = 6000000, +	.vco_min = 500000000, +	.vco_max = 1000000000, +	.base_reg = PLLD_BASE, +	.misc_reg = PLLD_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, +	.lock_delay = 1000, +}; + +static struct tegra_clk_pll_params pll_d2_params = { +	.input_min = 2000000, +	.input_max = 40000000, +	.cf_min = 1000000, +	.cf_max = 6000000, +	.vco_min = 500000000, +	.vco_max = 1000000000, +	.base_reg = PLLD2_BASE, +	.misc_reg = PLLD2_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, +	.lock_delay = 1000, +}; + +static struct pdiv_map pllu_p[] = { +	{ .pdiv = 1, .hw_val = 1 }, +	{ .pdiv = 2, .hw_val = 0 }, +	{ .pdiv = 0, .hw_val = 0 }, +}; + +static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { +	{12000000, 480000000, 960, 12, 0, 12}, +	{13000000, 480000000, 960, 13, 0, 12}, +	{16800000, 480000000, 400, 7, 0, 5}, +	{19200000, 480000000, 200, 4, 0, 3}, +	{26000000, 480000000, 960, 26, 0, 12}, +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_u_params = { +	.input_min = 2000000, +	.input_max = 40000000, +	.cf_min = 1000000, +	.cf_max = 6000000, +	.vco_min = 480000000, +	.vco_max = 960000000, +	.base_reg = PLLU_BASE, +	.misc_reg = PLLU_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, +	.lock_delay = 1000, +	.pdiv_tohw = pllu_p, +}; + +static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { +	/* 1 GHz */ +	{12000000, 1000000000, 83, 0, 1},	/* actual: 996.0 MHz */ +	{13000000, 1000000000, 76, 0, 1},	/* actual: 988.0 MHz */ +	{16800000, 1000000000, 59, 0, 1},	/* actual: 991.2 MHz */ +	{19200000, 1000000000, 52, 0, 1},	/* actual: 998.4 MHz */ +	{26000000, 1000000000, 76, 1, 1},	/* actual: 988.0 MHz */ + +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_x_params = { +	.input_min = 12000000, +	.input_max = 800000000, +	.cf_min = 12000000, +	.cf_max = 19200000,	/* s/w policy, h/w capability 50 MHz */ +	.vco_min = 700000000, +	.vco_max = 2400000000U, +	.base_reg = PLLX_BASE, +	.misc_reg = PLLX_MISC, +	.lock_mask = PLL_BASE_LOCK, +	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, +	.lock_delay = 300, +	.iddq_reg = PLLX_MISC3, +	.iddq_bit_idx = PLLX_IDDQ_BIT, +	.max_p = PLLXC_SW_MAX_P, +	.dyn_ramp_reg = PLLX_MISC2, +	.stepa_shift = 16, +	.stepb_shift = 24, +	.pdiv_tohw = pllxc_p, +}; + +static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { +	/* PLLE special case: use cpcon field to store cml divider value */ +	{336000000, 100000000, 100, 21, 16, 11}, +	{312000000, 100000000, 200, 26, 24, 13}, +	{0, 0, 0, 0, 0, 0}, +}; + +static struct tegra_clk_pll_params pll_e_params = { +	.input_min = 12000000, +	.input_max = 1000000000, +	.cf_min = 12000000, +	.cf_max = 75000000, +	.vco_min = 1600000000, +	.vco_max = 2400000000U, +	.base_reg = PLLE_BASE, +	.misc_reg = PLLE_MISC, +	.aux_reg = PLLE_AUX, +	.lock_mask = PLLE_MISC_LOCK, +	.lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, +	.lock_delay = 300, +}; + +static struct tegra_clk_pll_params pll_re_vco_params = { +	.input_min = 12000000, +	.input_max = 1000000000, +	.cf_min = 12000000, +	.cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */ +	.vco_min = 300000000, +	.vco_max = 600000000, +	.base_reg = PLLRE_BASE, +	.misc_reg = PLLRE_MISC, +	.lock_mask = PLLRE_MISC_LOCK, +	.lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE, +	.lock_delay = 300, +	.iddq_reg = PLLRE_MISC, +	.iddq_bit_idx = PLLRE_IDDQ_BIT, +}; + +/* Peripheral clock registers */ + +static struct tegra_clk_periph_regs periph_l_regs = { +	.enb_reg = CLK_OUT_ENB_L, +	.enb_set_reg = CLK_OUT_ENB_SET_L, +	.enb_clr_reg = CLK_OUT_ENB_CLR_L, +	.rst_reg = RST_DEVICES_L, +	.rst_set_reg = RST_DEVICES_SET_L, +	.rst_clr_reg = RST_DEVICES_CLR_L, +}; + +static struct tegra_clk_periph_regs periph_h_regs = { +	.enb_reg = CLK_OUT_ENB_H, +	.enb_set_reg = CLK_OUT_ENB_SET_H, +	.enb_clr_reg = CLK_OUT_ENB_CLR_H, +	.rst_reg = RST_DEVICES_H, +	.rst_set_reg = RST_DEVICES_SET_H, +	.rst_clr_reg = RST_DEVICES_CLR_H, +}; + +static struct tegra_clk_periph_regs periph_u_regs = { +	.enb_reg = CLK_OUT_ENB_U, +	.enb_set_reg = CLK_OUT_ENB_SET_U, +	.enb_clr_reg = CLK_OUT_ENB_CLR_U, +	.rst_reg = RST_DEVICES_U, +	.rst_set_reg = RST_DEVICES_SET_U, +	.rst_clr_reg = RST_DEVICES_CLR_U, +}; + +static struct tegra_clk_periph_regs periph_v_regs = { +	.enb_reg = CLK_OUT_ENB_V, +	.enb_set_reg = CLK_OUT_ENB_SET_V, +	.enb_clr_reg = CLK_OUT_ENB_CLR_V, +	.rst_reg = RST_DEVICES_V, +	.rst_set_reg = RST_DEVICES_SET_V, +	.rst_clr_reg = RST_DEVICES_CLR_V, +}; + +static struct tegra_clk_periph_regs periph_w_regs = { +	.enb_reg = CLK_OUT_ENB_W, +	.enb_set_reg = CLK_OUT_ENB_SET_W, +	.enb_clr_reg = CLK_OUT_ENB_CLR_W, +	.rst_reg = RST_DEVICES_W, +	.rst_set_reg = RST_DEVICES_SET_W, +	.rst_clr_reg = RST_DEVICES_CLR_W, +}; + +/* possible OSC frequencies in Hz */ +static unsigned long tegra114_input_freq[] = { +	[0] = 13000000, +	[1] = 16800000, +	[4] = 19200000, +	[5] = 38400000, +	[8] = 12000000, +	[9] = 48000000, +	[12] = 260000000, +}; + +#define MASK(x) (BIT(x) - 1) + +#define TEGRA_INIT_DATA_MUX(_name, _con_id, _dev_id, _parents, _offset,	\ +			    _clk_num, _regs, _gate_flags, _clk_id)	\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num,	\ +			periph_clk_enb_refcnt, _gate_flags, _clk_id,	\ +			_parents##_idx, 0) + +#define TEGRA_INIT_DATA_MUX_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ +			    _clk_num, _regs, _gate_flags, _clk_id, flags)\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			30, MASK(2), 0, 0, 8, 1, 0, _regs, _clk_num,	\ +			periph_clk_enb_refcnt, _gate_flags, _clk_id,	\ +			_parents##_idx, flags) + +#define TEGRA_INIT_DATA_MUX8(_name, _con_id, _dev_id, _parents, _offset, \ +			     _clk_num, _regs, _gate_flags, _clk_id)	\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			29, MASK(3), 0, 0, 8, 1, 0, _regs, _clk_num,	\ +			periph_clk_enb_refcnt, _gate_flags, _clk_id,	\ +			_parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT(_name, _con_id, _dev_id, _parents, _offset,	\ +			    _clk_num, _regs, _gate_flags, _clk_id)	\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ +			_clk_num, periph_clk_enb_refcnt, _gate_flags,	\ +			_clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_INT_FLAGS(_name, _con_id, _dev_id, _parents, _offset,\ +			    _clk_num, _regs, _gate_flags, _clk_id, flags)\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ +			_clk_num, periph_clk_enb_refcnt, _gate_flags,	\ +			_clk_id, _parents##_idx, flags) + +#define TEGRA_INIT_DATA_INT8(_name, _con_id, _dev_id, _parents, _offset,\ +			    _clk_num, _regs, _gate_flags, _clk_id)	\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs,\ +			_clk_num, periph_clk_enb_refcnt, _gate_flags,	\ +			_clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_UART(_name, _con_id, _dev_id, _parents, _offset,\ +			     _clk_num, _regs, _clk_id)			\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART, _regs,\ +			_clk_num, periph_clk_enb_refcnt, 0, _clk_id,	\ +			_parents##_idx, 0) + +#define TEGRA_INIT_DATA_I2C(_name, _con_id, _dev_id, _parents, _offset,\ +			     _clk_num, _regs, _clk_id)			\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			30, MASK(2), 0, 0, 16, 0, 0, _regs, _clk_num,	\ +			periph_clk_enb_refcnt, 0, _clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_NODIV(_name, _con_id, _dev_id, _parents, _offset, \ +			      _mux_shift, _mux_mask, _clk_num, _regs,	\ +			      _gate_flags, _clk_id)			\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset,\ +			_mux_shift, _mux_mask, 0, 0, 0, 0, 0, _regs,	\ +			_clk_num, periph_clk_enb_refcnt, _gate_flags,	\ +			_clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_XUSB(_name, _con_id, _dev_id, _parents, _offset, \ +			     _clk_num, _regs, _gate_flags, _clk_id)	 \ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parents, _offset, \ +			29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT, _regs, \ +			_clk_num, periph_clk_enb_refcnt, _gate_flags,	 \ +			_clk_id, _parents##_idx, 0) + +#define TEGRA_INIT_DATA_AUDIO(_name, _con_id, _dev_id, _offset,  _clk_num,\ +				 _regs, _gate_flags, _clk_id)		\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, mux_d_audio_clk,	\ +			_offset, 16, 0xE01F, 0, 0, 8, 1, 0, _regs, _clk_num, \ +			periph_clk_enb_refcnt, _gate_flags , _clk_id,	\ +			mux_d_audio_clk_idx, 0) + +enum tegra114_clk { +	rtc = 4, timer = 5, uarta = 6, sdmmc2 = 9, i2s1 = 11, i2c1 = 12, +	ndflash = 13, sdmmc1 = 14, sdmmc4 = 15, pwm = 17, i2s2 = 18, epp = 19, +	gr_2d = 21, usbd = 22, isp = 23, gr_3d = 24, disp2 = 26, disp1 = 27, +	host1x = 28, vcp = 29, i2s0 = 30, apbdma = 34, kbc = 36, kfuse = 40, +	sbc1 = 41, nor = 42, sbc2 = 44, sbc3 = 46, i2c5 = 47, dsia = 48, +	mipi = 50, hdmi = 51, csi = 52, i2c2 = 54, uartc = 55, mipi_cal = 56, +	emc, usb2, usb3, vde = 61, bsea = 62, bsev = 63, uartd = 65, +	i2c3 = 67, sbc4 = 68, sdmmc3 = 69, owr = 71, csite = 73, +	la = 76, trace = 77, soc_therm = 78, dtv = 79, ndspeed = 80, +	i2cslow = 81, dsib = 82, tsec = 83, xusb_host = 89, msenc = 91, +	csus = 92, mselect = 99, tsensor = 100, i2s3 = 101, i2s4 = 102, +	i2c4 = 103, sbc5 = 104, sbc6 = 105, d_audio, apbif = 107, dam0, dam1, +	dam2, hda2codec_2x = 111, audio0_2x = 113, audio1_2x, audio2_2x, +	audio3_2x, audio4_2x, spdif_2x, actmon = 119, extern1 = 120, +	extern2 = 121, extern3 = 122, hda = 125, se = 127, hda2hdmi = 128, +	cilab = 144, cilcd = 145, cile = 146, dsialp = 147, dsiblp = 148, +	dds = 150, dp2 = 152, amx = 153, adx = 154, xusb_ss = 156, uartb = 192, +	vfir, spdif_in, spdif_out, vi, vi_sensor, fuse, fuse_burn, clk_32k, +	clk_m, clk_m_div2, clk_m_div4, pll_ref, pll_c, pll_c_out1, pll_c2, +	pll_c3, pll_m, pll_m_out1, pll_p, pll_p_out1, pll_p_out2, pll_p_out3, +	pll_p_out4, pll_a, pll_a_out0, pll_d, pll_d_out0, pll_d2, pll_d2_out0, +	pll_u, pll_u_480M, pll_u_60M, pll_u_48M, pll_u_12M, pll_x, pll_x_out0, +	pll_re_vco, pll_re_out, pll_e_out0, spdif_in_sync, i2s0_sync, +	i2s1_sync, i2s2_sync, i2s3_sync, i2s4_sync, vimclk_sync, audio0, +	audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, +	blink, xusb_host_src = 252, xusb_falcon_src, xusb_fs_src, xusb_ss_src, +	xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, + +	/* Mux clocks */ + +	audio0_mux = 300, audio1_mux, audio2_mux, audio3_mux, audio4_mux, +	spdif_mux, clk_out_1_mux, clk_out_2_mux, clk_out_3_mux, dsia_mux, +	dsib_mux, clk_max, +}; + +struct utmi_clk_param { +	/* Oscillator Frequency in KHz */ +	u32 osc_frequency; +	/* UTMIP PLL Enable Delay Count  */ +	u8 enable_delay_count; +	/* UTMIP PLL Stable count */ +	u8 stable_count; +	/*  UTMIP PLL Active delay count */ +	u8 active_delay_count; +	/* UTMIP PLL Xtal frequency count */ +	u8 xtal_freq_count; +}; + +static const struct utmi_clk_param utmi_parameters[] = { +	{.osc_frequency = 13000000, .enable_delay_count = 0x02, +	 .stable_count = 0x33, .active_delay_count = 0x05, +	 .xtal_freq_count = 0x7F}, +	{.osc_frequency = 19200000, .enable_delay_count = 0x03, +	 .stable_count = 0x4B, .active_delay_count = 0x06, +	 .xtal_freq_count = 0xBB}, +	{.osc_frequency = 12000000, .enable_delay_count = 0x02, +	 .stable_count = 0x2F, .active_delay_count = 0x04, +	 .xtal_freq_count = 0x76}, +	{.osc_frequency = 26000000, .enable_delay_count = 0x04, +	 .stable_count = 0x66, .active_delay_count = 0x09, +	 .xtal_freq_count = 0xFE}, +	{.osc_frequency = 16800000, .enable_delay_count = 0x03, +	 .stable_count = 0x41, .active_delay_count = 0x0A, +	 .xtal_freq_count = 0xA4}, +}; + +/* peripheral mux definitions */ + +#define MUX_I2S_SPDIF(_id)						\ +static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \ +							   #_id, "pll_p",\ +							   "clk_m"}; +MUX_I2S_SPDIF(audio0) +MUX_I2S_SPDIF(audio1) +MUX_I2S_SPDIF(audio2) +MUX_I2S_SPDIF(audio3) +MUX_I2S_SPDIF(audio4) +MUX_I2S_SPDIF(audio) + +#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL +#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm_clkm[] = { +	"pll_p", "pll_c", "pll_m", "clk_m" +}; +#define mux_pllp_pllc_pllm_clkm_idx NULL + +static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" }; +#define mux_pllp_pllc_pllm_idx NULL + +static const char *mux_pllp_pllc_clk32_clkm[] = { +	"pll_p", "pll_c", "clk_32k", "clk_m" +}; +#define mux_pllp_pllc_clk32_clkm_idx NULL + +static const char *mux_plla_pllc_pllp_clkm[] = { +	"pll_a_out0", "pll_c", "pll_p", "clk_m" +}; +#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = { +	"pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m" +}; +static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = { +	[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, +}; + +static const char *mux_pllp_clkm[] = { +	"pll_p", "clk_m" +}; +static u32 mux_pllp_clkm_idx[] = { +	[0] = 0, [1] = 3, +}; + +static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = { +	"pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx + +static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = { +	"pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c", +	"pll_d2_out0", "clk_m" +}; +#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL + +static const char *mux_pllm_pllc_pllp_plla[] = { +	"pll_m", "pll_c", "pll_p", "pll_a_out0" +}; +#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx + +static const char *mux_pllp_pllc_clkm[] = { +	"pll_p", "pll_c", "pll_m" +}; +static u32 mux_pllp_pllc_clkm_idx[] = { +	[0] = 0, [1] = 1, [2] = 3, +}; + +static const char *mux_pllp_pllc_clkm_clk32[] = { +	"pll_p", "pll_c", "clk_m", "clk_32k" +}; +#define mux_pllp_pllc_clkm_clk32_idx NULL + +static const char *mux_plla_clk32_pllp_clkm_plle[] = { +	"pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0" +}; +#define mux_plla_clk32_pllp_clkm_plle_idx NULL + +static const char *mux_clkm_pllp_pllc_pllre[] = { +	"clk_m", "pll_p", "pll_c", "pll_re_out" +}; +static u32 mux_clkm_pllp_pllc_pllre_idx[] = { +	[0] = 0, [1] = 1, [2] = 3, [3] = 5, +}; + +static const char *mux_clkm_48M_pllp_480M[] = { +	"clk_m", "pll_u_48M", "pll_p", "pll_u_480M" +}; +#define mux_clkm_48M_pllp_480M_idx NULL + +static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = { +	"clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref" +}; +static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = { +	[0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7, +}; + +static const char *mux_plld_out0_plld2_out0[] = { +	"pll_d_out0", "pll_d2_out0", +}; +#define mux_plld_out0_plld2_out0_idx NULL + +static const char *mux_d_audio_clk[] = { +	"pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync", +	"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; +static u32 mux_d_audio_clk_idx[] = { +	[0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001, +	[5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007, +}; + +static const char *mux_pllmcp_clkm[] = { +	"pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud", +}; + +static const struct clk_div_table pll_re_div_table[] = { +	{ .val = 0, .div = 1 }, +	{ .val = 1, .div = 2 }, +	{ .val = 2, .div = 3 }, +	{ .val = 3, .div = 4 }, +	{ .val = 4, .div = 5 }, +	{ .val = 5, .div = 6 }, +	{ .val = 0, .div = 0 }, +}; + +static struct clk *clks[clk_max]; +static struct clk_onecell_data clk_data; + +static unsigned long osc_freq; +static unsigned long pll_ref_freq; + +static int __init tegra114_osc_clk_init(void __iomem *clk_base) +{ +	struct clk *clk; +	u32 val, pll_ref_div; + +	val = readl_relaxed(clk_base + OSC_CTRL); + +	osc_freq = tegra114_input_freq[val >> OSC_CTRL_OSC_FREQ_SHIFT]; +	if (!osc_freq) { +		WARN_ON(1); +		return -EINVAL; +	} + +	/* clk_m */ +	clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT, +				      osc_freq); +	clk_register_clkdev(clk, "clk_m", NULL); +	clks[clk_m] = clk; + +	/* pll_ref */ +	val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; +	pll_ref_div = 1 << val; +	clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", +					CLK_SET_RATE_PARENT, 1, pll_ref_div); +	clk_register_clkdev(clk, "pll_ref", NULL); +	clks[pll_ref] = clk; + +	pll_ref_freq = osc_freq / pll_ref_div; + +	return 0; +} + +static void __init tegra114_fixed_clk_init(void __iomem *clk_base) +{ +	struct clk *clk; + +	/* clk_32k */ +	clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, CLK_IS_ROOT, +				      32768); +	clk_register_clkdev(clk, "clk_32k", NULL); +	clks[clk_32k] = clk; + +	/* clk_m_div2 */ +	clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", +					CLK_SET_RATE_PARENT, 1, 2); +	clk_register_clkdev(clk, "clk_m_div2", NULL); +	clks[clk_m_div2] = clk; + +	/* clk_m_div4 */ +	clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", +					CLK_SET_RATE_PARENT, 1, 4); +	clk_register_clkdev(clk, "clk_m_div4", NULL); +	clks[clk_m_div4] = clk; + +} + +static __init void tegra114_utmi_param_configure(void __iomem *clk_base) +{ +	u32 reg; +	int i; + +	for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) { +		if (osc_freq == utmi_parameters[i].osc_frequency) +			break; +	} + +	if (i >= ARRAY_SIZE(utmi_parameters)) { +		pr_err("%s: Unexpected oscillator freq %lu\n", __func__, +		       osc_freq); +		return; +	} + +	reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); + +	/* Program UTMIP PLL stable and active counts */ +	/* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */ +	reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0); +	reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); + +	reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); + +	reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i]. +					    active_delay_count); + +	/* Remove power downs from UTMIP PLL control bits */ +	reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN; +	reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN; +	reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN; + +	writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); + +	/* Program UTMIP PLL delay and oscillator frequency counts */ +	reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); +	reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + +	reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i]. +					    enable_delay_count); + +	reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0); +	reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i]. +					   xtal_freq_count); + +	/* Remove power downs from UTMIP PLL control bits */ +	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; +	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN; +	reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP; +	reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN; +	writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + +	/* Setup HW control of UTMIPLL */ +	reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); +	reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET; +	reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL; +	reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE; +	writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + +	reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); +	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; +	reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; +	writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); + +	udelay(1); + +	/* Setup SW override of UTMIPLL assuming USB2.0 +	   ports are assigned to USB2 */ +	reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); +	reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL; +	reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE; +	writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); + +	udelay(1); + +	/* Enable HW control UTMIPLL */ +	reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0); +	reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE; +	writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0); +} + +static void __init _clip_vco_min(struct tegra_clk_pll_params *pll_params) +{ +	pll_params->vco_min = +		DIV_ROUND_UP(pll_params->vco_min, pll_ref_freq) * pll_ref_freq; +} + +static int __init _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params, +				      void __iomem *clk_base) +{ +	u32 val; +	u32 step_a, step_b; + +	switch (pll_ref_freq) { +	case 12000000: +	case 13000000: +	case 26000000: +		step_a = 0x2B; +		step_b = 0x0B; +		break; +	case 16800000: +		step_a = 0x1A; +		step_b = 0x09; +		break; +	case 19200000: +		step_a = 0x12; +		step_b = 0x08; +		break; +	default: +		pr_err("%s: Unexpected reference rate %lu\n", +			__func__, pll_ref_freq); +		WARN_ON(1); +		return -EINVAL; +	} + +	val = step_a << pll_params->stepa_shift; +	val |= step_b << pll_params->stepb_shift; +	writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg); + +	return 0; +} + +static void __init _init_iddq(struct tegra_clk_pll_params *pll_params, +			      void __iomem *clk_base) +{ +	u32 val, val_iddq; + +	val = readl_relaxed(clk_base + pll_params->base_reg); +	val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg); + +	if (val & BIT(30)) +		WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx)); +	else { +		val_iddq |= BIT(pll_params->iddq_bit_idx); +		writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg); +	} +} + +static void __init tegra114_pll_init(void __iomem *clk_base, +				     void __iomem *pmc) +{ +	u32 val; +	struct clk *clk; + +	/* PLLC */ +	_clip_vco_min(&pll_c_params); +	if (_setup_dynamic_ramp(&pll_c_params, clk_base) >= 0) { +		_init_iddq(&pll_c_params, clk_base); +		clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base, +				pmc, 0, 0, &pll_c_params, TEGRA_PLL_USE_LOCK, +				pll_c_freq_table, NULL); +		clk_register_clkdev(clk, "pll_c", NULL); +		clks[pll_c] = clk; + +		/* PLLC_OUT1 */ +		clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c", +				clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP, +				8, 8, 1, NULL); +		clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div", +					clk_base + PLLC_OUT, 1, 0, +					CLK_SET_RATE_PARENT, 0, NULL); +		clk_register_clkdev(clk, "pll_c_out1", NULL); +		clks[pll_c_out1] = clk; +	} + +	/* PLLC2 */ +	_clip_vco_min(&pll_c2_params); +	clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, 0, +			     &pll_c2_params, TEGRA_PLL_USE_LOCK, +			     pll_cx_freq_table, NULL); +	clk_register_clkdev(clk, "pll_c2", NULL); +	clks[pll_c2] = clk; + +	/* PLLC3 */ +	_clip_vco_min(&pll_c3_params); +	clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0, 0, +			     &pll_c3_params, TEGRA_PLL_USE_LOCK, +			     pll_cx_freq_table, NULL); +	clk_register_clkdev(clk, "pll_c3", NULL); +	clks[pll_c3] = clk; + +	/* PLLP */ +	clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base, pmc, 0, +			    408000000, &pll_p_params, +			    TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, +			    pll_p_freq_table, NULL); +	clk_register_clkdev(clk, "pll_p", NULL); +	clks[pll_p] = clk; + +	/* PLLP_OUT1 */ +	clk = tegra_clk_register_divider("pll_p_out1_div", "pll_p", +				clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | +				TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); +	clk = tegra_clk_register_pll_out("pll_p_out1", "pll_p_out1_div", +				clk_base + PLLP_OUTA, 1, 0, +				CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, +				&pll_div_lock); +	clk_register_clkdev(clk, "pll_p_out1", NULL); +	clks[pll_p_out1] = clk; + +	/* PLLP_OUT2 */ +	clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", +				clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | +				TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, +				&pll_div_lock); +	clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", +				clk_base + PLLP_OUTA, 17, 16, +				CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, +				&pll_div_lock); +	clk_register_clkdev(clk, "pll_p_out2", NULL); +	clks[pll_p_out2] = clk; + +	/* PLLP_OUT3 */ +	clk = tegra_clk_register_divider("pll_p_out3_div", "pll_p", +				clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | +				TEGRA_DIVIDER_ROUND_UP, 8, 8, 1, &pll_div_lock); +	clk = tegra_clk_register_pll_out("pll_p_out3", "pll_p_out3_div", +				clk_base + PLLP_OUTB, 1, 0, +				CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, +				&pll_div_lock); +	clk_register_clkdev(clk, "pll_p_out3", NULL); +	clks[pll_p_out3] = clk; + +	/* PLLP_OUT4 */ +	clk = tegra_clk_register_divider("pll_p_out4_div", "pll_p", +				clk_base + PLLP_OUTB, 0, TEGRA_DIVIDER_FIXED | +				TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, +				&pll_div_lock); +	clk = tegra_clk_register_pll_out("pll_p_out4", "pll_p_out4_div", +				clk_base + PLLP_OUTB, 17, 16, +				CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, +				&pll_div_lock); +	clk_register_clkdev(clk, "pll_p_out4", NULL); +	clks[pll_p_out4] = clk; + +	/* PLLM */ +	_clip_vco_min(&pll_m_params); +	clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc, +			     CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, 0, +			     &pll_m_params, TEGRA_PLL_USE_LOCK, +			     pll_m_freq_table, NULL); +	clk_register_clkdev(clk, "pll_m", NULL); +	clks[pll_m] = clk; + +	/* PLLM_OUT1 */ +	clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m", +				clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP, +				8, 8, 1, NULL); +	clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div", +				clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED | +				CLK_SET_RATE_PARENT, 0, NULL); +	clk_register_clkdev(clk, "pll_m_out1", NULL); +	clks[pll_m_out1] = clk; + +	/* PLLM_UD */ +	clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m", +					CLK_SET_RATE_PARENT, 1, 1); + +	/* PLLX */ +	_clip_vco_min(&pll_x_params); +	if (_setup_dynamic_ramp(&pll_x_params, clk_base) >= 0) { +		_init_iddq(&pll_x_params, clk_base); +		clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, +				pmc, CLK_IGNORE_UNUSED, 0, &pll_x_params, +				TEGRA_PLL_USE_LOCK, pll_x_freq_table, NULL); +		clk_register_clkdev(clk, "pll_x", NULL); +		clks[pll_x] = clk; +	} + +	/* PLLX_OUT0 */ +	clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x", +					CLK_SET_RATE_PARENT, 1, 2); +	clk_register_clkdev(clk, "pll_x_out0", NULL); +	clks[pll_x_out0] = clk; + +	/* PLLU */ +	val = readl(clk_base + pll_u_params.base_reg); +	val &= ~BIT(24); /* disable PLLU_OVERRIDE */ +	writel(val, clk_base + pll_u_params.base_reg); + +	clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, pmc, 0, +			    0, &pll_u_params, TEGRA_PLLU | +			    TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | +			    TEGRA_PLL_USE_LOCK, pll_u_freq_table, &pll_u_lock); +	clk_register_clkdev(clk, "pll_u", NULL); +	clks[pll_u] = clk; + +	tegra114_utmi_param_configure(clk_base); + +	/* PLLU_480M */ +	clk = clk_register_gate(NULL, "pll_u_480M", "pll_u", +				CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, +				22, 0, &pll_u_lock); +	clk_register_clkdev(clk, "pll_u_480M", NULL); +	clks[pll_u_480M] = clk; + +	/* PLLU_60M */ +	clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u", +					CLK_SET_RATE_PARENT, 1, 8); +	clk_register_clkdev(clk, "pll_u_60M", NULL); +	clks[pll_u_60M] = clk; + +	/* PLLU_48M */ +	clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u", +					CLK_SET_RATE_PARENT, 1, 10); +	clk_register_clkdev(clk, "pll_u_48M", NULL); +	clks[pll_u_48M] = clk; + +	/* PLLU_12M */ +	clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u", +					CLK_SET_RATE_PARENT, 1, 40); +	clk_register_clkdev(clk, "pll_u_12M", NULL); +	clks[pll_u_12M] = clk; + +	/* PLLD */ +	clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0, +			    0, &pll_d_params, +			    TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | +			    TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d_lock); +	clk_register_clkdev(clk, "pll_d", NULL); +	clks[pll_d] = clk; + +	/* PLLD_OUT0 */ +	clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d", +					CLK_SET_RATE_PARENT, 1, 2); +	clk_register_clkdev(clk, "pll_d_out0", NULL); +	clks[pll_d_out0] = clk; + +	/* PLLD2 */ +	clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc, 0, +			    0, &pll_d2_params, +			    TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | +			    TEGRA_PLL_USE_LOCK, pll_d_freq_table, &pll_d2_lock); +	clk_register_clkdev(clk, "pll_d2", NULL); +	clks[pll_d2] = clk; + +	/* PLLD2_OUT0 */ +	clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", +					CLK_SET_RATE_PARENT, 1, 2); +	clk_register_clkdev(clk, "pll_d2_out0", NULL); +	clks[pll_d2_out0] = clk; + +	/* PLLA */ +	clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, pmc, 0, +			    0, &pll_a_params, TEGRA_PLL_HAS_CPCON | +			    TEGRA_PLL_USE_LOCK, pll_a_freq_table, NULL); +	clk_register_clkdev(clk, "pll_a", NULL); +	clks[pll_a] = clk; + +	/* PLLA_OUT0 */ +	clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a", +				clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP, +				8, 8, 1, NULL); +	clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div", +				clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED | +				CLK_SET_RATE_PARENT, 0, NULL); +	clk_register_clkdev(clk, "pll_a_out0", NULL); +	clks[pll_a_out0] = clk; + +	/* PLLRE */ +	_clip_vco_min(&pll_re_vco_params); +	clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc, +			     0, 0, &pll_re_vco_params, TEGRA_PLL_USE_LOCK, +			     NULL, &pll_re_lock, pll_ref_freq); +	clk_register_clkdev(clk, "pll_re_vco", NULL); +	clks[pll_re_vco] = clk; + +	clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0, +					 clk_base + PLLRE_BASE, 16, 4, 0, +					 pll_re_div_table, &pll_re_lock); +	clk_register_clkdev(clk, "pll_re_out", NULL); +	clks[pll_re_out] = clk; + +	/* PLLE */ +	clk = tegra_clk_register_plle_tegra114("pll_e_out0", "pll_re_vco", +				      clk_base, 0, 100000000, &pll_e_params, +				      pll_e_freq_table, NULL); +	clk_register_clkdev(clk, "pll_e_out0", NULL); +	clks[pll_e_out0] = clk; +} + +static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync", +	"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", +}; + +static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2", +	"clk_m_div4", "extern1", +}; + +static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2", +	"clk_m_div4", "extern2", +}; + +static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2", +	"clk_m_div4", "extern3", +}; + +static void __init tegra114_audio_clk_init(void __iomem *clk_base) +{ +	struct clk *clk; + +	/* spdif_in_sync */ +	clk = tegra_clk_register_sync_source("spdif_in_sync", 24000000, +					     24000000); +	clk_register_clkdev(clk, "spdif_in_sync", NULL); +	clks[spdif_in_sync] = clk; + +	/* i2s0_sync */ +	clk = tegra_clk_register_sync_source("i2s0_sync", 24000000, 24000000); +	clk_register_clkdev(clk, "i2s0_sync", NULL); +	clks[i2s0_sync] = clk; + +	/* i2s1_sync */ +	clk = tegra_clk_register_sync_source("i2s1_sync", 24000000, 24000000); +	clk_register_clkdev(clk, "i2s1_sync", NULL); +	clks[i2s1_sync] = clk; + +	/* i2s2_sync */ +	clk = tegra_clk_register_sync_source("i2s2_sync", 24000000, 24000000); +	clk_register_clkdev(clk, "i2s2_sync", NULL); +	clks[i2s2_sync] = clk; + +	/* i2s3_sync */ +	clk = tegra_clk_register_sync_source("i2s3_sync", 24000000, 24000000); +	clk_register_clkdev(clk, "i2s3_sync", NULL); +	clks[i2s3_sync] = clk; + +	/* i2s4_sync */ +	clk = tegra_clk_register_sync_source("i2s4_sync", 24000000, 24000000); +	clk_register_clkdev(clk, "i2s4_sync", NULL); +	clks[i2s4_sync] = clk; + +	/* vimclk_sync */ +	clk = tegra_clk_register_sync_source("vimclk_sync", 24000000, 24000000); +	clk_register_clkdev(clk, "vimclk_sync", NULL); +	clks[vimclk_sync] = clk; + +	/* audio0 */ +	clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, +			       ARRAY_SIZE(mux_audio_sync_clk), 0, +			       clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, +			       NULL); +	clks[audio0_mux] = clk; +	clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, +				clk_base + AUDIO_SYNC_CLK_I2S0, 4, +				CLK_GATE_SET_TO_DISABLE, NULL); +	clk_register_clkdev(clk, "audio0", NULL); +	clks[audio0] = clk; + +	/* audio1 */ +	clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, +			       ARRAY_SIZE(mux_audio_sync_clk), 0, +			       clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, +			       NULL); +	clks[audio1_mux] = clk; +	clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, +				clk_base + AUDIO_SYNC_CLK_I2S1, 4, +				CLK_GATE_SET_TO_DISABLE, NULL); +	clk_register_clkdev(clk, "audio1", NULL); +	clks[audio1] = clk; + +	/* audio2 */ +	clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, +			       ARRAY_SIZE(mux_audio_sync_clk), 0, +			       clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, +			       NULL); +	clks[audio2_mux] = clk; +	clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, +				clk_base + AUDIO_SYNC_CLK_I2S2, 4, +				CLK_GATE_SET_TO_DISABLE, NULL); +	clk_register_clkdev(clk, "audio2", NULL); +	clks[audio2] = clk; + +	/* audio3 */ +	clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, +			       ARRAY_SIZE(mux_audio_sync_clk), 0, +			       clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, +			       NULL); +	clks[audio3_mux] = clk; +	clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, +				clk_base + AUDIO_SYNC_CLK_I2S3, 4, +				CLK_GATE_SET_TO_DISABLE, NULL); +	clk_register_clkdev(clk, "audio3", NULL); +	clks[audio3] = clk; + +	/* audio4 */ +	clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, +			       ARRAY_SIZE(mux_audio_sync_clk), 0, +			       clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, +			       NULL); +	clks[audio4_mux] = clk; +	clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, +				clk_base + AUDIO_SYNC_CLK_I2S4, 4, +				CLK_GATE_SET_TO_DISABLE, NULL); +	clk_register_clkdev(clk, "audio4", NULL); +	clks[audio4] = clk; + +	/* spdif */ +	clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, +			       ARRAY_SIZE(mux_audio_sync_clk), 0, +			       clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, +			       NULL); +	clks[spdif_mux] = clk; +	clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, +				clk_base + AUDIO_SYNC_CLK_SPDIF, 4, +				CLK_GATE_SET_TO_DISABLE, NULL); +	clk_register_clkdev(clk, "spdif", NULL); +	clks[spdif] = clk; + +	/* audio0_2x */ +	clk = clk_register_fixed_factor(NULL, "audio0_doubler", "audio0", +					CLK_SET_RATE_PARENT, 2, 1); +	clk = tegra_clk_register_divider("audio0_div", "audio0_doubler", +				clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 24, 1, +				0, &clk_doubler_lock); +	clk = tegra_clk_register_periph_gate("audio0_2x", "audio0_div", +				  TEGRA_PERIPH_NO_RESET, clk_base, +				  CLK_SET_RATE_PARENT, 113, &periph_v_regs, +				  periph_clk_enb_refcnt); +	clk_register_clkdev(clk, "audio0_2x", NULL); +	clks[audio0_2x] = clk; + +	/* audio1_2x */ +	clk = clk_register_fixed_factor(NULL, "audio1_doubler", "audio1", +					CLK_SET_RATE_PARENT, 2, 1); +	clk = tegra_clk_register_divider("audio1_div", "audio1_doubler", +				clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 25, 1, +				0, &clk_doubler_lock); +	clk = tegra_clk_register_periph_gate("audio1_2x", "audio1_div", +				  TEGRA_PERIPH_NO_RESET, clk_base, +				  CLK_SET_RATE_PARENT, 114, &periph_v_regs, +				  periph_clk_enb_refcnt); +	clk_register_clkdev(clk, "audio1_2x", NULL); +	clks[audio1_2x] = clk; + +	/* audio2_2x */ +	clk = clk_register_fixed_factor(NULL, "audio2_doubler", "audio2", +					CLK_SET_RATE_PARENT, 2, 1); +	clk = tegra_clk_register_divider("audio2_div", "audio2_doubler", +				clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 26, 1, +				0, &clk_doubler_lock); +	clk = tegra_clk_register_periph_gate("audio2_2x", "audio2_div", +				  TEGRA_PERIPH_NO_RESET, clk_base, +				  CLK_SET_RATE_PARENT, 115, &periph_v_regs, +				  periph_clk_enb_refcnt); +	clk_register_clkdev(clk, "audio2_2x", NULL); +	clks[audio2_2x] = clk; + +	/* audio3_2x */ +	clk = clk_register_fixed_factor(NULL, "audio3_doubler", "audio3", +					CLK_SET_RATE_PARENT, 2, 1); +	clk = tegra_clk_register_divider("audio3_div", "audio3_doubler", +				clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 27, 1, +				0, &clk_doubler_lock); +	clk = tegra_clk_register_periph_gate("audio3_2x", "audio3_div", +				  TEGRA_PERIPH_NO_RESET, clk_base, +				  CLK_SET_RATE_PARENT, 116, &periph_v_regs, +				  periph_clk_enb_refcnt); +	clk_register_clkdev(clk, "audio3_2x", NULL); +	clks[audio3_2x] = clk; + +	/* audio4_2x */ +	clk = clk_register_fixed_factor(NULL, "audio4_doubler", "audio4", +					CLK_SET_RATE_PARENT, 2, 1); +	clk = tegra_clk_register_divider("audio4_div", "audio4_doubler", +				clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 28, 1, +				0, &clk_doubler_lock); +	clk = tegra_clk_register_periph_gate("audio4_2x", "audio4_div", +				  TEGRA_PERIPH_NO_RESET, clk_base, +				  CLK_SET_RATE_PARENT, 117, &periph_v_regs, +				  periph_clk_enb_refcnt); +	clk_register_clkdev(clk, "audio4_2x", NULL); +	clks[audio4_2x] = clk; + +	/* spdif_2x */ +	clk = clk_register_fixed_factor(NULL, "spdif_doubler", "spdif", +					CLK_SET_RATE_PARENT, 2, 1); +	clk = tegra_clk_register_divider("spdif_div", "spdif_doubler", +				clk_base + AUDIO_SYNC_DOUBLER, 0, 0, 29, 1, +				0, &clk_doubler_lock); +	clk = tegra_clk_register_periph_gate("spdif_2x", "spdif_div", +				  TEGRA_PERIPH_NO_RESET, clk_base, +				  CLK_SET_RATE_PARENT, 118, +				  &periph_v_regs, periph_clk_enb_refcnt); +	clk_register_clkdev(clk, "spdif_2x", NULL); +	clks[spdif_2x] = clk; +} + +static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) +{ +	struct clk *clk; + +	/* clk_out_1 */ +	clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, +			       ARRAY_SIZE(clk_out1_parents), 0, +			       pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, +			       &clk_out_lock); +	clks[clk_out_1_mux] = clk; +	clk = clk_register_gate(NULL, "clk_out_1", "clk_out_1_mux", 0, +				pmc_base + PMC_CLK_OUT_CNTRL, 2, 0, +				&clk_out_lock); +	clk_register_clkdev(clk, "extern1", "clk_out_1"); +	clks[clk_out_1] = clk; + +	/* clk_out_2 */ +	clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, +			       ARRAY_SIZE(clk_out1_parents), 0, +			       pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, +			       &clk_out_lock); +	clks[clk_out_2_mux] = clk; +	clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, +				pmc_base + PMC_CLK_OUT_CNTRL, 10, 0, +				&clk_out_lock); +	clk_register_clkdev(clk, "extern2", "clk_out_2"); +	clks[clk_out_2] = clk; + +	/* clk_out_3 */ +	clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, +			       ARRAY_SIZE(clk_out1_parents), 0, +			       pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, +			       &clk_out_lock); +	clks[clk_out_3_mux] = clk; +	clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, +				pmc_base + PMC_CLK_OUT_CNTRL, 18, 0, +				&clk_out_lock); +	clk_register_clkdev(clk, "extern3", "clk_out_3"); +	clks[clk_out_3] = clk; + +	/* blink */ +	clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0, +				pmc_base + PMC_DPD_PADS_ORIDE, +				PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL); +	clk = clk_register_gate(NULL, "blink", "blink_override", 0, +				pmc_base + PMC_CTRL, +				PMC_CTRL_BLINK_ENB, 0, NULL); +	clk_register_clkdev(clk, "blink", NULL); +	clks[blink] = clk; + +} + +static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", +			       "pll_p_out3", "pll_p_out2", "unused", +			       "clk_32k", "pll_m_out1" }; + +static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", +					"pll_p", "pll_p_out4", "unused", +					"unused", "pll_x" }; + +static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", +					 "pll_p", "pll_p_out4", "unused", +					 "unused", "pll_x", "pll_x_out0" }; + +static void __init tegra114_super_clk_init(void __iomem *clk_base) +{ +	struct clk *clk; + +	/* CCLKG */ +	clk = tegra_clk_register_super_mux("cclk_g", cclk_g_parents, +					ARRAY_SIZE(cclk_g_parents), +					CLK_SET_RATE_PARENT, +					clk_base + CCLKG_BURST_POLICY, +					0, 4, 0, 0, NULL); +	clk_register_clkdev(clk, "cclk_g", NULL); +	clks[cclk_g] = clk; + +	/* CCLKLP */ +	clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents, +					ARRAY_SIZE(cclk_lp_parents), +					CLK_SET_RATE_PARENT, +					clk_base + CCLKLP_BURST_POLICY, +					0, 4, 8, 9, NULL); +	clk_register_clkdev(clk, "cclk_lp", NULL); +	clks[cclk_lp] = clk; + +	/* SCLK */ +	clk = tegra_clk_register_super_mux("sclk", sclk_parents, +					ARRAY_SIZE(sclk_parents), +					CLK_SET_RATE_PARENT, +					clk_base + SCLK_BURST_POLICY, +					0, 4, 0, 0, NULL); +	clk_register_clkdev(clk, "sclk", NULL); +	clks[sclk] = clk; + +	/* HCLK */ +	clk = clk_register_divider(NULL, "hclk_div", "sclk", 0, +				   clk_base + SYSTEM_CLK_RATE, 4, 2, 0, +				   &sysrate_lock); +	clk = clk_register_gate(NULL, "hclk", "hclk_div", CLK_SET_RATE_PARENT | +				CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, +				7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); +	clk_register_clkdev(clk, "hclk", NULL); +	clks[hclk] = clk; + +	/* PCLK */ +	clk = clk_register_divider(NULL, "pclk_div", "hclk", 0, +				   clk_base + SYSTEM_CLK_RATE, 0, 2, 0, +				   &sysrate_lock); +	clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT | +				CLK_IGNORE_UNUSED, clk_base + SYSTEM_CLK_RATE, +				3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock); +	clk_register_clkdev(clk, "pclk", NULL); +	clks[pclk] = clk; +} + +static struct tegra_periph_init_data tegra_periph_clk_list[] = { +	TEGRA_INIT_DATA_MUX("i2s0", NULL, "tegra30-i2s.0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s0), +	TEGRA_INIT_DATA_MUX("i2s1", NULL, "tegra30-i2s.1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s1), +	TEGRA_INIT_DATA_MUX("i2s2", NULL, "tegra30-i2s.2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, &periph_l_regs, TEGRA_PERIPH_ON_APB, i2s2), +	TEGRA_INIT_DATA_MUX("i2s3", NULL, "tegra30-i2s.3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s3), +	TEGRA_INIT_DATA_MUX("i2s4", NULL, "tegra30-i2s.4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, &periph_v_regs, TEGRA_PERIPH_ON_APB, i2s4), +	TEGRA_INIT_DATA_MUX("spdif_out", "spdif_out", "tegra30-spdif", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_out), +	TEGRA_INIT_DATA_MUX("spdif_in", "spdif_in", "tegra30-spdif", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, &periph_l_regs, TEGRA_PERIPH_ON_APB, spdif_in), +	TEGRA_INIT_DATA_MUX("pwm", NULL, "pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, &periph_l_regs, TEGRA_PERIPH_ON_APB, pwm), +	TEGRA_INIT_DATA_MUX("adx", NULL, "adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, &periph_w_regs, TEGRA_PERIPH_ON_APB, adx), +	TEGRA_INIT_DATA_MUX("amx", NULL, "amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, &periph_w_regs, TEGRA_PERIPH_ON_APB, amx), +	TEGRA_INIT_DATA_MUX("hda", "hda", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda), +	TEGRA_INIT_DATA_MUX("hda2codec_2x", "hda2codec", "tegra30-hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, &periph_v_regs, TEGRA_PERIPH_ON_APB, hda2codec_2x), +	TEGRA_INIT_DATA_MUX("sbc1", NULL, "tegra11-spi.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc1), +	TEGRA_INIT_DATA_MUX("sbc2", NULL, "tegra11-spi.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc2), +	TEGRA_INIT_DATA_MUX("sbc3", NULL, "tegra11-spi.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, &periph_h_regs, TEGRA_PERIPH_ON_APB, sbc3), +	TEGRA_INIT_DATA_MUX("sbc4", NULL, "tegra11-spi.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, &periph_u_regs, TEGRA_PERIPH_ON_APB, sbc4), +	TEGRA_INIT_DATA_MUX("sbc5", NULL, "tegra11-spi.4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc5), +	TEGRA_INIT_DATA_MUX("sbc6", NULL, "tegra11-spi.5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, &periph_v_regs, TEGRA_PERIPH_ON_APB, sbc6), +	TEGRA_INIT_DATA_MUX8("ndflash", NULL, "tegra_nand", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), +	TEGRA_INIT_DATA_MUX8("ndspeed", NULL, "tegra_nand_speed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, &periph_u_regs, TEGRA_PERIPH_ON_APB, ndspeed), +	TEGRA_INIT_DATA_MUX("vfir", NULL, "vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, &periph_l_regs, TEGRA_PERIPH_ON_APB, vfir), +	TEGRA_INIT_DATA_MUX("sdmmc1", NULL, "sdhci-tegra.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, &periph_l_regs, 0, sdmmc1), +	TEGRA_INIT_DATA_MUX("sdmmc2", NULL, "sdhci-tegra.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, &periph_l_regs, 0, sdmmc2), +	TEGRA_INIT_DATA_MUX("sdmmc3", NULL, "sdhci-tegra.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, &periph_u_regs, 0, sdmmc3), +	TEGRA_INIT_DATA_MUX("sdmmc4", NULL, "sdhci-tegra.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, &periph_l_regs, 0, sdmmc4), +	TEGRA_INIT_DATA_INT("vde", NULL, "vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, &periph_h_regs, 0, vde), +	TEGRA_INIT_DATA_MUX_FLAGS("csite", NULL, "csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, &periph_u_regs, TEGRA_PERIPH_ON_APB, csite, CLK_IGNORE_UNUSED), +	TEGRA_INIT_DATA_MUX("la", NULL, "la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, &periph_u_regs, TEGRA_PERIPH_ON_APB, la), +	TEGRA_INIT_DATA_MUX("trace", NULL, "trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, &periph_u_regs, TEGRA_PERIPH_ON_APB, trace), +	TEGRA_INIT_DATA_MUX("owr", NULL, "tegra_w1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, &periph_u_regs, TEGRA_PERIPH_ON_APB, owr), +	TEGRA_INIT_DATA_MUX("nor", NULL, "tegra-nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, &periph_h_regs, 0, nor), +	TEGRA_INIT_DATA_MUX("mipi", NULL, "mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, &periph_h_regs, TEGRA_PERIPH_ON_APB, mipi), +	TEGRA_INIT_DATA_I2C("i2c1", "div-clk", "tegra11-i2c.0", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, &periph_l_regs, i2c1), +	TEGRA_INIT_DATA_I2C("i2c2", "div-clk", "tegra11-i2c.1", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, &periph_h_regs, i2c2), +	TEGRA_INIT_DATA_I2C("i2c3", "div-clk", "tegra11-i2c.2", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, &periph_u_regs, i2c3), +	TEGRA_INIT_DATA_I2C("i2c4", "div-clk", "tegra11-i2c.3", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, &periph_v_regs, i2c4), +	TEGRA_INIT_DATA_I2C("i2c5", "div-clk", "tegra11-i2c.4", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, &periph_h_regs, i2c5), +	TEGRA_INIT_DATA_UART("uarta", NULL, "tegra_uart.0", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, &periph_l_regs, uarta), +	TEGRA_INIT_DATA_UART("uartb", NULL, "tegra_uart.1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, &periph_l_regs, uartb), +	TEGRA_INIT_DATA_UART("uartc", NULL, "tegra_uart.2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, &periph_h_regs, uartc), +	TEGRA_INIT_DATA_UART("uartd", NULL, "tegra_uart.3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, &periph_u_regs, uartd), +	TEGRA_INIT_DATA_INT("3d", NULL, "3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, &periph_l_regs, 0, gr_3d), +	TEGRA_INIT_DATA_INT("2d", NULL, "2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, &periph_l_regs, 0, gr_2d), +	TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), +	TEGRA_INIT_DATA_INT8("vi", "vi", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), +	TEGRA_INIT_DATA_INT8("epp", NULL, "epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), +	TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_h_regs, TEGRA_PERIPH_WAR_1005168, msenc), +	TEGRA_INIT_DATA_INT8("tsec", NULL, "tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, &periph_u_regs, 0, tsec), +	TEGRA_INIT_DATA_INT8("host1x", NULL, "host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), +	TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), +	TEGRA_INIT_DATA_MUX("cilab", "cilab", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, &periph_w_regs, 0, cilab), +	TEGRA_INIT_DATA_MUX("cilcd", "cilcd", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, &periph_w_regs, 0, cilcd), +	TEGRA_INIT_DATA_MUX("cile", "cile", "tegra_camera", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, &periph_w_regs, 0, cile), +	TEGRA_INIT_DATA_MUX("dsialp", "dsialp", "tegradc.0", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, &periph_w_regs, 0, dsialp), +	TEGRA_INIT_DATA_MUX("dsiblp", "dsiblp", "tegradc.1", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, &periph_w_regs, 0, dsiblp), +	TEGRA_INIT_DATA_MUX("tsensor", NULL, "tegra-tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, &periph_v_regs, TEGRA_PERIPH_ON_APB, tsensor), +	TEGRA_INIT_DATA_MUX("actmon", NULL, "actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, &periph_v_regs, 0, actmon), +	TEGRA_INIT_DATA_MUX8("extern1", NULL, "extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, &periph_v_regs, 0, extern1), +	TEGRA_INIT_DATA_MUX8("extern2", NULL, "extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, &periph_v_regs, 0, extern2), +	TEGRA_INIT_DATA_MUX8("extern3", NULL, "extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, &periph_v_regs, 0, extern3), +	TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow), +	TEGRA_INIT_DATA_INT8("se", NULL, "se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, TEGRA_PERIPH_ON_APB, se), +	TEGRA_INIT_DATA_INT_FLAGS("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect, CLK_IGNORE_UNUSED), +	TEGRA_INIT_DATA_MUX8("soc_therm", NULL, "soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, &periph_u_regs, TEGRA_PERIPH_ON_APB, soc_therm), +	TEGRA_INIT_DATA_XUSB("xusb_host_src", "host_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, &periph_w_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_host_src), +	TEGRA_INIT_DATA_XUSB("xusb_falcon_src", "falcon_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_falcon_src), +	TEGRA_INIT_DATA_XUSB("xusb_fs_src", "fs_src", "tegra_xhci", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_fs_src), +	TEGRA_INIT_DATA_XUSB("xusb_ss_src", "ss_src", "tegra_xhci", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_ss_src), +	TEGRA_INIT_DATA_XUSB("xusb_dev_src", "dev_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, &periph_u_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_dev_src), +	TEGRA_INIT_DATA_AUDIO("d_audio", "d_audio", "tegra30-ahub", CLK_SOURCE_D_AUDIO, 106, &periph_v_regs, TEGRA_PERIPH_ON_APB, d_audio), +	TEGRA_INIT_DATA_AUDIO("dam0", NULL, "tegra30-dam.0", CLK_SOURCE_DAM0, 108, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam0), +	TEGRA_INIT_DATA_AUDIO("dam1", NULL, "tegra30-dam.1", CLK_SOURCE_DAM1, 109, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam1), +	TEGRA_INIT_DATA_AUDIO("dam2", NULL, "tegra30-dam.2", CLK_SOURCE_DAM2, 110, &periph_v_regs, TEGRA_PERIPH_ON_APB, dam2), +}; + +static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { +	TEGRA_INIT_DATA_NODIV("disp1", NULL, "tegradc.0", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, &periph_l_regs, 0, disp1), +	TEGRA_INIT_DATA_NODIV("disp2", NULL, "tegradc.1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, &periph_l_regs, 0, disp2), +}; + +static __init void tegra114_periph_clk_init(void __iomem *clk_base) +{ +	struct tegra_periph_init_data *data; +	struct clk *clk; +	int i; +	u32 val; + +	/* apbdma */ +	clk = tegra_clk_register_periph_gate("apbdma", "clk_m", 0, clk_base, +				  0, 34, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[apbdma] = clk; + +	/* rtc */ +	clk = tegra_clk_register_periph_gate("rtc", "clk_32k", +				    TEGRA_PERIPH_ON_APB | +				    TEGRA_PERIPH_NO_RESET, clk_base, +				    0, 4, &periph_l_regs, +				    periph_clk_enb_refcnt); +	clk_register_clkdev(clk, NULL, "rtc-tegra"); +	clks[rtc] = clk; + +	/* kbc */ +	clk = tegra_clk_register_periph_gate("kbc", "clk_32k", +				    TEGRA_PERIPH_ON_APB | +				    TEGRA_PERIPH_NO_RESET, clk_base, +				    0, 36, &periph_h_regs, +				    periph_clk_enb_refcnt); +	clks[kbc] = clk; + +	/* timer */ +	clk = tegra_clk_register_periph_gate("timer", "clk_m", 0, clk_base, +				  0, 5, &periph_l_regs, +				  periph_clk_enb_refcnt); +	clk_register_clkdev(clk, NULL, "timer"); +	clks[timer] = clk; + +	/* kfuse */ +	clk = tegra_clk_register_periph_gate("kfuse", "clk_m", +				  TEGRA_PERIPH_ON_APB, clk_base,  0, 40, +				  &periph_h_regs, periph_clk_enb_refcnt); +	clks[kfuse] = clk; + +	/* fuse */ +	clk = tegra_clk_register_periph_gate("fuse", "clk_m", +				  TEGRA_PERIPH_ON_APB, clk_base,  0, 39, +				  &periph_h_regs, periph_clk_enb_refcnt); +	clks[fuse] = clk; + +	/* fuse_burn */ +	clk = tegra_clk_register_periph_gate("fuse_burn", "clk_m", +				  TEGRA_PERIPH_ON_APB, clk_base,  0, 39, +				  &periph_h_regs, periph_clk_enb_refcnt); +	clks[fuse_burn] = clk; + +	/* apbif */ +	clk = tegra_clk_register_periph_gate("apbif", "clk_m", +				  TEGRA_PERIPH_ON_APB, clk_base,  0, 107, +				  &periph_v_regs, periph_clk_enb_refcnt); +	clks[apbif] = clk; + +	/* hda2hdmi */ +	clk = tegra_clk_register_periph_gate("hda2hdmi", "clk_m", +				    TEGRA_PERIPH_ON_APB, clk_base,  0, 128, +				    &periph_w_regs, periph_clk_enb_refcnt); +	clks[hda2hdmi] = clk; + +	/* vcp */ +	clk = tegra_clk_register_periph_gate("vcp", "clk_m", 0, clk_base,  0, +				  29, &periph_l_regs, +				  periph_clk_enb_refcnt); +	clks[vcp] = clk; + +	/* bsea */ +	clk = tegra_clk_register_periph_gate("bsea", "clk_m", 0, clk_base, +				  0, 62, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[bsea] = clk; + +	/* bsev */ +	clk = tegra_clk_register_periph_gate("bsev", "clk_m", 0, clk_base, +				  0, 63, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[bsev] = clk; + +	/* mipi-cal */ +	clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base, +				   0, 56, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[mipi_cal] = clk; + +	/* usbd */ +	clk = tegra_clk_register_periph_gate("usbd", "clk_m", 0, clk_base, +				  0, 22, &periph_l_regs, +				  periph_clk_enb_refcnt); +	clks[usbd] = clk; + +	/* usb2 */ +	clk = tegra_clk_register_periph_gate("usb2", "clk_m", 0, clk_base, +				  0, 58, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[usb2] = clk; + +	/* usb3 */ +	clk = tegra_clk_register_periph_gate("usb3", "clk_m", 0, clk_base, +				  0, 59, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[usb3] = clk; + +	/* csi */ +	clk = tegra_clk_register_periph_gate("csi", "pll_p_out3", 0, clk_base, +				   0, 52, &periph_h_regs, +				  periph_clk_enb_refcnt); +	clks[csi] = clk; + +	/* isp */ +	clk = tegra_clk_register_periph_gate("isp", "clk_m", 0, clk_base, 0, +				  23, &periph_l_regs, +				  periph_clk_enb_refcnt); +	clks[isp] = clk; + +	/* csus */ +	clk = tegra_clk_register_periph_gate("csus", "clk_m", +				  TEGRA_PERIPH_NO_RESET, clk_base, 0, 92, +				  &periph_u_regs, periph_clk_enb_refcnt); +	clks[csus] = clk; + +	/* dds */ +	clk = tegra_clk_register_periph_gate("dds", "clk_m", +				  TEGRA_PERIPH_ON_APB, clk_base, 0, 150, +				  &periph_w_regs, periph_clk_enb_refcnt); +	clks[dds] = clk; + +	/* dp2 */ +	clk = tegra_clk_register_periph_gate("dp2", "clk_m", +				  TEGRA_PERIPH_ON_APB, clk_base, 0, 152, +				  &periph_w_regs, periph_clk_enb_refcnt); +	clks[dp2] = clk; + +	/* dtv */ +	clk = tegra_clk_register_periph_gate("dtv", "clk_m", +				    TEGRA_PERIPH_ON_APB, clk_base, 0, 79, +				    &periph_u_regs, periph_clk_enb_refcnt); +	clks[dtv] = clk; + +	/* dsia */ +	clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, +			       ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, +			       clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock); +	clks[dsia_mux] = clk; +	clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base, +				    0, 48, &periph_h_regs, +				    periph_clk_enb_refcnt); +	clks[dsia] = clk; + +	/* dsib */ +	clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0, +			       ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, +			       clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock); +	clks[dsib_mux] = clk; +	clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base, +				    0, 82, &periph_u_regs, +				    periph_clk_enb_refcnt); +	clks[dsib] = clk; + +	/* xusb_hs_src */ +	val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); +	val |= BIT(25); /* always select PLLU_60M */ +	writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); + +	clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, +					1, 1); +	clks[xusb_hs_src] = clk; + +	/* xusb_host */ +	clk = tegra_clk_register_periph_gate("xusb_host", "xusb_host_src", 0, +				    clk_base, 0, 89, &periph_u_regs, +				    periph_clk_enb_refcnt); +	clks[xusb_host] = clk; + +	/* xusb_ss */ +	clk = tegra_clk_register_periph_gate("xusb_ss", "xusb_ss_src", 0, +				    clk_base, 0, 156, &periph_w_regs, +				    periph_clk_enb_refcnt); +	clks[xusb_host] = clk; + +	/* xusb_dev */ +	clk = tegra_clk_register_periph_gate("xusb_dev", "xusb_dev_src", 0, +				    clk_base, 0, 95, &periph_u_regs, +				    periph_clk_enb_refcnt); +	clks[xusb_dev] = clk; + +	/* emc */ +	clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, +			       ARRAY_SIZE(mux_pllmcp_clkm), 0, +			       clk_base + CLK_SOURCE_EMC, +			       29, 3, 0, NULL); +	clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, +				CLK_IGNORE_UNUSED, 57, &periph_h_regs, +				periph_clk_enb_refcnt); +	clks[emc] = clk; + +	for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) { +		data = &tegra_periph_clk_list[i]; +		clk = tegra_clk_register_periph(data->name, data->parent_names, +				data->num_parents, &data->periph, +				clk_base, data->offset, data->flags); +		clks[data->clk_id] = clk; +	} + +	for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) { +		data = &tegra_periph_nodiv_clk_list[i]; +		clk = tegra_clk_register_periph_nodiv(data->name, +				data->parent_names, data->num_parents, +				&data->periph, clk_base, data->offset); +		clks[data->clk_id] = clk; +	} +} + +static struct tegra_cpu_car_ops tegra114_cpu_car_ops; + +static const struct of_device_id pmc_match[] __initconst = { +	{ .compatible = "nvidia,tegra114-pmc" }, +	{}, +}; + +static __initdata struct tegra_clk_init_table init_table[] = { +	{uarta, pll_p, 408000000, 0}, +	{uartb, pll_p, 408000000, 0}, +	{uartc, pll_p, 408000000, 0}, +	{uartd, pll_p, 408000000, 0}, +	{pll_a, clk_max, 564480000, 1}, +	{pll_a_out0, clk_max, 11289600, 1}, +	{extern1, pll_a_out0, 0, 1}, +	{clk_out_1_mux, extern1, 0, 1}, +	{clk_out_1, clk_max, 0, 1}, +	{i2s0, pll_a_out0, 11289600, 0}, +	{i2s1, pll_a_out0, 11289600, 0}, +	{i2s2, pll_a_out0, 11289600, 0}, +	{i2s3, pll_a_out0, 11289600, 0}, +	{i2s4, pll_a_out0, 11289600, 0}, +	{clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ +}; + +static void __init tegra114_clock_apply_init_table(void) +{ +	tegra_init_from_table(init_table, clks, clk_max); +} + +void __init tegra114_clock_init(struct device_node *np) +{ +	struct device_node *node; +	int i; + +	clk_base = of_iomap(np, 0); +	if (!clk_base) { +		pr_err("ioremap tegra114 CAR failed\n"); +		return; +	} + +	node = of_find_matching_node(NULL, pmc_match); +	if (!node) { +		pr_err("Failed to find pmc node\n"); +		WARN_ON(1); +		return; +	} + +	pmc_base = of_iomap(node, 0); +	if (!pmc_base) { +		pr_err("Can't map pmc registers\n"); +		WARN_ON(1); +		return; +	} + +	if (tegra114_osc_clk_init(clk_base) < 0) +		return; + +	tegra114_fixed_clk_init(clk_base); +	tegra114_pll_init(clk_base, pmc_base); +	tegra114_periph_clk_init(clk_base); +	tegra114_audio_clk_init(clk_base); +	tegra114_pmc_clk_init(pmc_base); +	tegra114_super_clk_init(clk_base); + +	for (i = 0; i < ARRAY_SIZE(clks); i++) { +		if (IS_ERR(clks[i])) { +			pr_err +			    ("Tegra114 clk %d: register failed with %ld\n", +			     i, PTR_ERR(clks[i])); +		} +		if (!clks[i]) +			clks[i] = ERR_PTR(-EINVAL); +	} + +	clk_data.clks = clks; +	clk_data.clk_num = ARRAY_SIZE(clks); +	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + +	tegra_clk_apply_init_table = tegra114_clock_apply_init_table; + +	tegra_cpu_car_ops = &tegra114_cpu_car_ops; +} diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 143ce1f899a..b0405b67f49 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -86,8 +86,8 @@  #define PLLE_BASE 0xe8  #define PLLE_MISC 0xec -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11)  #define PLL_MISC_LOCK_ENABLE 18  #define PLLDU_MISC_LOCK_ENABLE 22 @@ -236,7 +236,7 @@ enum tegra20_clk {  	dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2,  	usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3,  	pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb, -	iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2, +	iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev2, cdev1,  	uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve,  	osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0,  	pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1, @@ -248,125 +248,125 @@ static struct clk *clks[clk_max];  static struct clk_onecell_data clk_data;  static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { -	{ 12000000, 600000000, 600, 12, 1, 8 }, -	{ 13000000, 600000000, 600, 13, 1, 8 }, -	{ 19200000, 600000000, 500, 16, 1, 6 }, -	{ 26000000, 600000000, 600, 26, 1, 8 }, +	{ 12000000, 600000000, 600, 12, 0, 8 }, +	{ 13000000, 600000000, 600, 13, 0, 8 }, +	{ 19200000, 600000000, 500, 16, 0, 6 }, +	{ 26000000, 600000000, 600, 26, 0, 8 },  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { -	{ 12000000, 666000000, 666, 12, 1, 8}, -	{ 13000000, 666000000, 666, 13, 1, 8}, -	{ 19200000, 666000000, 555, 16, 1, 8}, -	{ 26000000, 666000000, 666, 26, 1, 8}, -	{ 12000000, 600000000, 600, 12, 1, 8}, -	{ 13000000, 600000000, 600, 13, 1, 8}, -	{ 19200000, 600000000, 375, 12, 1, 6}, -	{ 26000000, 600000000, 600, 26, 1, 8}, +	{ 12000000, 666000000, 666, 12, 0, 8}, +	{ 13000000, 666000000, 666, 13, 0, 8}, +	{ 19200000, 666000000, 555, 16, 0, 8}, +	{ 26000000, 666000000, 666, 26, 0, 8}, +	{ 12000000, 600000000, 600, 12, 0, 8}, +	{ 13000000, 600000000, 600, 13, 0, 8}, +	{ 19200000, 600000000, 375, 12, 0, 6}, +	{ 26000000, 600000000, 600, 26, 0, 8},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { -	{ 12000000, 216000000, 432, 12, 2, 8}, -	{ 13000000, 216000000, 432, 13, 2, 8}, -	{ 19200000, 216000000, 90,   4, 2, 1}, -	{ 26000000, 216000000, 432, 26, 2, 8}, -	{ 12000000, 432000000, 432, 12, 1, 8}, -	{ 13000000, 432000000, 432, 13, 1, 8}, -	{ 19200000, 432000000, 90,   4, 1, 1}, -	{ 26000000, 432000000, 432, 26, 1, 8}, +	{ 12000000, 216000000, 432, 12, 1, 8}, +	{ 13000000, 216000000, 432, 13, 1, 8}, +	{ 19200000, 216000000, 90,   4, 1, 1}, +	{ 26000000, 216000000, 432, 26, 1, 8}, +	{ 12000000, 432000000, 432, 12, 0, 8}, +	{ 13000000, 432000000, 432, 13, 0, 8}, +	{ 19200000, 432000000, 90,   4, 0, 1}, +	{ 26000000, 432000000, 432, 26, 0, 8},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { -	{ 28800000, 56448000, 49, 25, 1, 1}, -	{ 28800000, 73728000, 64, 25, 1, 1}, -	{ 28800000, 24000000,  5,  6, 1, 1}, +	{ 28800000, 56448000, 49, 25, 0, 1}, +	{ 28800000, 73728000, 64, 25, 0, 1}, +	{ 28800000, 24000000,  5,  6, 0, 1},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { -	{ 12000000, 216000000, 216, 12, 1, 4}, -	{ 13000000, 216000000, 216, 13, 1, 4}, -	{ 19200000, 216000000, 135, 12, 1, 3}, -	{ 26000000, 216000000, 216, 26, 1, 4}, +	{ 12000000, 216000000, 216, 12, 0, 4}, +	{ 13000000, 216000000, 216, 13, 0, 4}, +	{ 19200000, 216000000, 135, 12, 0, 3}, +	{ 26000000, 216000000, 216, 26, 0, 4}, -	{ 12000000, 594000000, 594, 12, 1, 8}, -	{ 13000000, 594000000, 594, 13, 1, 8}, -	{ 19200000, 594000000, 495, 16, 1, 8}, -	{ 26000000, 594000000, 594, 26, 1, 8}, +	{ 12000000, 594000000, 594, 12, 0, 8}, +	{ 13000000, 594000000, 594, 13, 0, 8}, +	{ 19200000, 594000000, 495, 16, 0, 8}, +	{ 26000000, 594000000, 594, 26, 0, 8}, -	{ 12000000, 1000000000, 1000, 12, 1, 12}, -	{ 13000000, 1000000000, 1000, 13, 1, 12}, -	{ 19200000, 1000000000, 625,  12, 1, 8}, -	{ 26000000, 1000000000, 1000, 26, 1, 12}, +	{ 12000000, 1000000000, 1000, 12, 0, 12}, +	{ 13000000, 1000000000, 1000, 13, 0, 12}, +	{ 19200000, 1000000000, 625,  12, 0, 8}, +	{ 26000000, 1000000000, 1000, 26, 0, 12},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { -	{ 12000000, 480000000, 960, 12, 2, 0}, -	{ 13000000, 480000000, 960, 13, 2, 0}, -	{ 19200000, 480000000, 200, 4,  2, 0}, -	{ 26000000, 480000000, 960, 26, 2, 0}, +	{ 12000000, 480000000, 960, 12, 0, 0}, +	{ 13000000, 480000000, 960, 13, 0, 0}, +	{ 19200000, 480000000, 200, 4,  0, 0}, +	{ 26000000, 480000000, 960, 26, 0, 0},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {  	/* 1 GHz */ -	{ 12000000, 1000000000, 1000, 12, 1, 12}, -	{ 13000000, 1000000000, 1000, 13, 1, 12}, -	{ 19200000, 1000000000, 625,  12, 1, 8}, -	{ 26000000, 1000000000, 1000, 26, 1, 12}, +	{ 12000000, 1000000000, 1000, 12, 0, 12}, +	{ 13000000, 1000000000, 1000, 13, 0, 12}, +	{ 19200000, 1000000000, 625,  12, 0, 8}, +	{ 26000000, 1000000000, 1000, 26, 0, 12},  	/* 912 MHz */ -	{ 12000000, 912000000,  912,  12, 1, 12}, -	{ 13000000, 912000000,  912,  13, 1, 12}, -	{ 19200000, 912000000,  760,  16, 1, 8}, -	{ 26000000, 912000000,  912,  26, 1, 12}, +	{ 12000000, 912000000,  912,  12, 0, 12}, +	{ 13000000, 912000000,  912,  13, 0, 12}, +	{ 19200000, 912000000,  760,  16, 0, 8}, +	{ 26000000, 912000000,  912,  26, 0, 12},  	/* 816 MHz */ -	{ 12000000, 816000000,  816,  12, 1, 12}, -	{ 13000000, 816000000,  816,  13, 1, 12}, -	{ 19200000, 816000000,  680,  16, 1, 8}, -	{ 26000000, 816000000,  816,  26, 1, 12}, +	{ 12000000, 816000000,  816,  12, 0, 12}, +	{ 13000000, 816000000,  816,  13, 0, 12}, +	{ 19200000, 816000000,  680,  16, 0, 8}, +	{ 26000000, 816000000,  816,  26, 0, 12},  	/* 760 MHz */ -	{ 12000000, 760000000,  760,  12, 1, 12}, -	{ 13000000, 760000000,  760,  13, 1, 12}, -	{ 19200000, 760000000,  950,  24, 1, 8}, -	{ 26000000, 760000000,  760,  26, 1, 12}, +	{ 12000000, 760000000,  760,  12, 0, 12}, +	{ 13000000, 760000000,  760,  13, 0, 12}, +	{ 19200000, 760000000,  950,  24, 0, 8}, +	{ 26000000, 760000000,  760,  26, 0, 12},  	/* 750 MHz */ -	{ 12000000, 750000000,  750,  12, 1, 12}, -	{ 13000000, 750000000,  750,  13, 1, 12}, -	{ 19200000, 750000000,  625,  16, 1, 8}, -	{ 26000000, 750000000,  750,  26, 1, 12}, +	{ 12000000, 750000000,  750,  12, 0, 12}, +	{ 13000000, 750000000,  750,  13, 0, 12}, +	{ 19200000, 750000000,  625,  16, 0, 8}, +	{ 26000000, 750000000,  750,  26, 0, 12},  	/* 608 MHz */ -	{ 12000000, 608000000,  608,  12, 1, 12}, -	{ 13000000, 608000000,  608,  13, 1, 12}, -	{ 19200000, 608000000,  380,  12, 1, 8}, -	{ 26000000, 608000000,  608,  26, 1, 12}, +	{ 12000000, 608000000,  608,  12, 0, 12}, +	{ 13000000, 608000000,  608,  13, 0, 12}, +	{ 19200000, 608000000,  380,  12, 0, 8}, +	{ 26000000, 608000000,  608,  26, 0, 12},  	/* 456 MHz */ -	{ 12000000, 456000000,  456,  12, 1, 12}, -	{ 13000000, 456000000,  456,  13, 1, 12}, -	{ 19200000, 456000000,  380,  16, 1, 8}, -	{ 26000000, 456000000,  456,  26, 1, 12}, +	{ 12000000, 456000000,  456,  12, 0, 12}, +	{ 13000000, 456000000,  456,  13, 0, 12}, +	{ 19200000, 456000000,  380,  16, 0, 8}, +	{ 26000000, 456000000,  456,  26, 0, 12},  	/* 312 MHz */ -	{ 12000000, 312000000,  312,  12, 1, 12}, -	{ 13000000, 312000000,  312,  13, 1, 12}, -	{ 19200000, 312000000,  260,  16, 1, 8}, -	{ 26000000, 312000000,  312,  26, 1, 12}, +	{ 12000000, 312000000,  312,  12, 0, 12}, +	{ 13000000, 312000000,  312,  13, 0, 12}, +	{ 19200000, 312000000,  260,  16, 0, 8}, +	{ 26000000, 312000000,  312,  26, 0, 12},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { -	{ 12000000, 100000000,  200,  24, 1, 0 }, +	{ 12000000, 100000000,  200,  24, 0, 0 },  	{ 0, 0, 0, 0, 0, 0 },  }; @@ -380,7 +380,7 @@ static struct tegra_clk_pll_params pll_c_params = {  	.vco_max = 1400000000,  	.base_reg = PLLC_BASE,  	.misc_reg = PLLC_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -394,7 +394,7 @@ static struct tegra_clk_pll_params pll_m_params = {  	.vco_max = 1200000000,  	.base_reg = PLLM_BASE,  	.misc_reg = PLLM_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -408,7 +408,7 @@ static struct tegra_clk_pll_params pll_p_params = {  	.vco_max = 1400000000,  	.base_reg = PLLP_BASE,  	.misc_reg = PLLP_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -422,7 +422,7 @@ static struct tegra_clk_pll_params pll_a_params = {  	.vco_max = 1400000000,  	.base_reg = PLLA_BASE,  	.misc_reg = PLLA_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -436,11 +436,17 @@ static struct tegra_clk_pll_params pll_d_params = {  	.vco_max = 1000000000,  	.base_reg = PLLD_BASE,  	.misc_reg = PLLD_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,  	.lock_delay = 1000,  }; +static struct pdiv_map pllu_p[] = { +	{ .pdiv = 1, .hw_val = 1 }, +	{ .pdiv = 2, .hw_val = 0 }, +	{ .pdiv = 0, .hw_val = 0 }, +}; +  static struct tegra_clk_pll_params pll_u_params = {  	.input_min = 2000000,  	.input_max = 40000000, @@ -450,9 +456,10 @@ static struct tegra_clk_pll_params pll_u_params = {  	.vco_max = 960000000,  	.base_reg = PLLU_BASE,  	.misc_reg = PLLU_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,  	.lock_delay = 1000, +	.pdiv_tohw = pllu_p,  };  static struct tegra_clk_pll_params pll_x_params = { @@ -464,7 +471,7 @@ static struct tegra_clk_pll_params pll_x_params = {  	.vco_max = 1200000000,  	.base_reg = PLLX_BASE,  	.misc_reg = PLLX_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -478,7 +485,7 @@ static struct tegra_clk_pll_params pll_e_params = {  	.vco_max = 0,  	.base_reg = PLLE_BASE,  	.misc_reg = PLLE_MISC, -	.lock_bit_idx = PLLE_MISC_LOCK, +	.lock_mask = PLLE_MISC_LOCK,  	.lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,  	.lock_delay = 0,  }; @@ -711,8 +718,8 @@ static void tegra20_pll_init(void)  }  static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", -				      "pll_p_cclk", "pll_p_out4_cclk", -				      "pll_p_out3_cclk", "clk_d", "pll_x" }; +				      "pll_p", "pll_p_out4", +				      "pll_p_out3", "clk_d", "pll_x" };  static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",  				      "pll_p_out3", "pll_p_out2", "clk_d",  				      "clk_32k", "pll_m_out1" }; @@ -721,38 +728,6 @@ static void tegra20_super_clk_init(void)  {  	struct clk *clk; -	/* -	 * DIV_U71 dividers for CCLK, these dividers are used only -	 * if parent clock is fixed rate. -	 */ - -	/* -	 * Clock input to cclk divided from pll_p using -	 * U71 divider of cclk. -	 */ -	clk = tegra_clk_register_divider("pll_p_cclk", "pll_p", -				clk_base + SUPER_CCLK_DIVIDER, 0, -				TEGRA_DIVIDER_INT, 16, 8, 1, NULL); -	clk_register_clkdev(clk, "pll_p_cclk", NULL); - -	/* -	 * Clock input to cclk divided from pll_p_out3 using -	 * U71 divider of cclk. -	 */ -	clk = tegra_clk_register_divider("pll_p_out3_cclk", "pll_p_out3", -				clk_base + SUPER_CCLK_DIVIDER, 0, -				TEGRA_DIVIDER_INT, 16, 8, 1, NULL); -	clk_register_clkdev(clk, "pll_p_out3_cclk", NULL); - -	/* -	 * Clock input to cclk divided from pll_p_out4 using -	 * U71 divider of cclk. -	 */ -	clk = tegra_clk_register_divider("pll_p_out4_cclk", "pll_p_out4", -				clk_base + SUPER_CCLK_DIVIDER, 0, -				TEGRA_DIVIDER_INT, 16, 8, 1, NULL); -	clk_register_clkdev(clk, "pll_p_out4_cclk", NULL); -  	/* CCLK */  	clk = tegra_clk_register_super_mux("cclk", cclk_parents,  			      ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, @@ -1044,7 +1019,7 @@ static void __init tegra20_periph_clk_init(void)  		data = &tegra_periph_clk_list[i];  		clk = tegra_clk_register_periph(data->name, data->parent_names,  				data->num_parents, &data->periph, -				clk_base, data->offset); +				clk_base, data->offset, data->flags);  		clk_register_clkdev(clk, data->con_id, data->dev_id);  		clks[data->clk_id] = clk;  	} @@ -1279,9 +1254,16 @@ static __initdata struct tegra_clk_init_table init_table[] = {  	{host1x, pll_c, 150000000, 0},  	{disp1, pll_p, 600000000, 0},  	{disp2, pll_p, 600000000, 0}, +	{gr2d, pll_c, 300000000, 0}, +	{gr3d, pll_c, 300000000, 0},  	{clk_max, clk_max, 0, 0}, /* This MUST be the last entry */  }; +static void __init tegra20_clock_apply_init_table(void) +{ +	tegra_init_from_table(init_table, clks, clk_max); +} +  /*   * Some clocks may be used by different drivers depending on the board   * configuration.  List those here to register them twice in the clock lookup @@ -1292,7 +1274,6 @@ static struct tegra_clk_duplicate tegra_clk_duplicates[] = {  	TEGRA_CLK_DUPLICATE(usbd,   "tegra-ehci.0", NULL),  	TEGRA_CLK_DUPLICATE(usbd,   "tegra-otg",    NULL),  	TEGRA_CLK_DUPLICATE(cclk,   NULL,           "cpu"), -	TEGRA_CLK_DUPLICATE(twd,    "smp_twd",      NULL),  	TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* Must be the last entry */  }; @@ -1349,7 +1330,7 @@ void __init tegra20_clock_init(struct device_node *np)  	clk_data.clk_num = ARRAY_SIZE(clks);  	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); -	tegra_init_from_table(init_table, clks, clk_max); +	tegra_clk_apply_init_table = tegra20_clock_apply_init_table;  	tegra_cpu_car_ops = &tegra20_cpu_car_ops;  } diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 32c61cb6d0b..2dc0c560261 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -116,8 +116,8 @@  #define PLLDU_MISC_LOCK_ENABLE 22  #define PLLE_MISC_LOCK_ENABLE 9 -#define PLL_BASE_LOCK 27 -#define PLLE_MISC_LOCK 11 +#define PLL_BASE_LOCK BIT(27) +#define PLLE_MISC_LOCK BIT(11)  #define PLLE_AUX 0x48c  #define PLLC_OUT 0x84 @@ -330,7 +330,7 @@ enum tegra30_clk {  	usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3,  	pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2cslow,  	dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92, -	cdev1, cdev2, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4, +	cdev2, cdev1, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4,  	i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x,  	atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x,  	spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda, @@ -374,164 +374,170 @@ static const struct utmi_clk_param utmi_parameters[] = {  };  static struct tegra_clk_pll_freq_table pll_c_freq_table[] = { -	{ 12000000, 1040000000, 520,  6, 1, 8}, -	{ 13000000, 1040000000, 480,  6, 1, 8}, -	{ 16800000, 1040000000, 495,  8, 1, 8},	/* actual: 1039.5 MHz */ -	{ 19200000, 1040000000, 325,  6, 1, 6}, -	{ 26000000, 1040000000, 520, 13, 1, 8}, +	{ 12000000, 1040000000, 520,  6, 0, 8}, +	{ 13000000, 1040000000, 480,  6, 0, 8}, +	{ 16800000, 1040000000, 495,  8, 0, 8},	/* actual: 1039.5 MHz */ +	{ 19200000, 1040000000, 325,  6, 0, 6}, +	{ 26000000, 1040000000, 520, 13, 0, 8}, -	{ 12000000, 832000000, 416,  6, 1, 8}, -	{ 13000000, 832000000, 832, 13, 1, 8}, -	{ 16800000, 832000000, 396,  8, 1, 8},	/* actual: 831.6 MHz */ -	{ 19200000, 832000000, 260,  6, 1, 8}, -	{ 26000000, 832000000, 416, 13, 1, 8}, +	{ 12000000, 832000000, 416,  6, 0, 8}, +	{ 13000000, 832000000, 832, 13, 0, 8}, +	{ 16800000, 832000000, 396,  8, 0, 8},	/* actual: 831.6 MHz */ +	{ 19200000, 832000000, 260,  6, 0, 8}, +	{ 26000000, 832000000, 416, 13, 0, 8}, -	{ 12000000, 624000000, 624, 12, 1, 8}, -	{ 13000000, 624000000, 624, 13, 1, 8}, -	{ 16800000, 600000000, 520, 14, 1, 8}, -	{ 19200000, 624000000, 520, 16, 1, 8}, -	{ 26000000, 624000000, 624, 26, 1, 8}, +	{ 12000000, 624000000, 624, 12, 0, 8}, +	{ 13000000, 624000000, 624, 13, 0, 8}, +	{ 16800000, 600000000, 520, 14, 0, 8}, +	{ 19200000, 624000000, 520, 16, 0, 8}, +	{ 26000000, 624000000, 624, 26, 0, 8}, -	{ 12000000, 600000000, 600, 12, 1, 8}, -	{ 13000000, 600000000, 600, 13, 1, 8}, -	{ 16800000, 600000000, 500, 14, 1, 8}, -	{ 19200000, 600000000, 375, 12, 1, 6}, -	{ 26000000, 600000000, 600, 26, 1, 8}, +	{ 12000000, 600000000, 600, 12, 0, 8}, +	{ 13000000, 600000000, 600, 13, 0, 8}, +	{ 16800000, 600000000, 500, 14, 0, 8}, +	{ 19200000, 600000000, 375, 12, 0, 6}, +	{ 26000000, 600000000, 600, 26, 0, 8}, -	{ 12000000, 520000000, 520, 12, 1, 8}, -	{ 13000000, 520000000, 520, 13, 1, 8}, -	{ 16800000, 520000000, 495, 16, 1, 8},	/* actual: 519.75 MHz */ -	{ 19200000, 520000000, 325, 12, 1, 6}, -	{ 26000000, 520000000, 520, 26, 1, 8}, +	{ 12000000, 520000000, 520, 12, 0, 8}, +	{ 13000000, 520000000, 520, 13, 0, 8}, +	{ 16800000, 520000000, 495, 16, 0, 8},	/* actual: 519.75 MHz */ +	{ 19200000, 520000000, 325, 12, 0, 6}, +	{ 26000000, 520000000, 520, 26, 0, 8}, -	{ 12000000, 416000000, 416, 12, 1, 8}, -	{ 13000000, 416000000, 416, 13, 1, 8}, -	{ 16800000, 416000000, 396, 16, 1, 8},	/* actual: 415.8 MHz */ -	{ 19200000, 416000000, 260, 12, 1, 6}, -	{ 26000000, 416000000, 416, 26, 1, 8}, +	{ 12000000, 416000000, 416, 12, 0, 8}, +	{ 13000000, 416000000, 416, 13, 0, 8}, +	{ 16800000, 416000000, 396, 16, 0, 8},	/* actual: 415.8 MHz */ +	{ 19200000, 416000000, 260, 12, 0, 6}, +	{ 26000000, 416000000, 416, 26, 0, 8},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_m_freq_table[] = { -	{ 12000000, 666000000, 666, 12, 1, 8}, -	{ 13000000, 666000000, 666, 13, 1, 8}, -	{ 16800000, 666000000, 555, 14, 1, 8}, -	{ 19200000, 666000000, 555, 16, 1, 8}, -	{ 26000000, 666000000, 666, 26, 1, 8}, -	{ 12000000, 600000000, 600, 12, 1, 8}, -	{ 13000000, 600000000, 600, 13, 1, 8}, -	{ 16800000, 600000000, 500, 14, 1, 8}, -	{ 19200000, 600000000, 375, 12, 1, 6}, -	{ 26000000, 600000000, 600, 26, 1, 8}, +	{ 12000000, 666000000, 666, 12, 0, 8}, +	{ 13000000, 666000000, 666, 13, 0, 8}, +	{ 16800000, 666000000, 555, 14, 0, 8}, +	{ 19200000, 666000000, 555, 16, 0, 8}, +	{ 26000000, 666000000, 666, 26, 0, 8}, +	{ 12000000, 600000000, 600, 12, 0, 8}, +	{ 13000000, 600000000, 600, 13, 0, 8}, +	{ 16800000, 600000000, 500, 14, 0, 8}, +	{ 19200000, 600000000, 375, 12, 0, 6}, +	{ 26000000, 600000000, 600, 26, 0, 8},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { -	{ 12000000, 216000000, 432, 12, 2, 8}, -	{ 13000000, 216000000, 432, 13, 2, 8}, -	{ 16800000, 216000000, 360, 14, 2, 8}, -	{ 19200000, 216000000, 360, 16, 2, 8}, -	{ 26000000, 216000000, 432, 26, 2, 8}, +	{ 12000000, 216000000, 432, 12, 1, 8}, +	{ 13000000, 216000000, 432, 13, 1, 8}, +	{ 16800000, 216000000, 360, 14, 1, 8}, +	{ 19200000, 216000000, 360, 16, 1, 8}, +	{ 26000000, 216000000, 432, 26, 1, 8},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { -	{ 9600000, 564480000, 294, 5, 1, 4}, -	{ 9600000, 552960000, 288, 5, 1, 4}, -	{ 9600000, 24000000,  5,   2, 1, 1}, +	{ 9600000, 564480000, 294, 5, 0, 4}, +	{ 9600000, 552960000, 288, 5, 0, 4}, +	{ 9600000, 24000000,  5,   2, 0, 1}, -	{ 28800000, 56448000, 49, 25, 1, 1}, -	{ 28800000, 73728000, 64, 25, 1, 1}, -	{ 28800000, 24000000,  5,  6, 1, 1}, +	{ 28800000, 56448000, 49, 25, 0, 1}, +	{ 28800000, 73728000, 64, 25, 0, 1}, +	{ 28800000, 24000000,  5,  6, 0, 1},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { -	{ 12000000, 216000000, 216, 12, 1, 4}, -	{ 13000000, 216000000, 216, 13, 1, 4}, -	{ 16800000, 216000000, 180, 14, 1, 4}, -	{ 19200000, 216000000, 180, 16, 1, 4}, -	{ 26000000, 216000000, 216, 26, 1, 4}, +	{ 12000000, 216000000, 216, 12, 0, 4}, +	{ 13000000, 216000000, 216, 13, 0, 4}, +	{ 16800000, 216000000, 180, 14, 0, 4}, +	{ 19200000, 216000000, 180, 16, 0, 4}, +	{ 26000000, 216000000, 216, 26, 0, 4}, -	{ 12000000, 594000000, 594, 12, 1, 8}, -	{ 13000000, 594000000, 594, 13, 1, 8}, -	{ 16800000, 594000000, 495, 14, 1, 8}, -	{ 19200000, 594000000, 495, 16, 1, 8}, -	{ 26000000, 594000000, 594, 26, 1, 8}, +	{ 12000000, 594000000, 594, 12, 0, 8}, +	{ 13000000, 594000000, 594, 13, 0, 8}, +	{ 16800000, 594000000, 495, 14, 0, 8}, +	{ 19200000, 594000000, 495, 16, 0, 8}, +	{ 26000000, 594000000, 594, 26, 0, 8}, -	{ 12000000, 1000000000, 1000, 12, 1, 12}, -	{ 13000000, 1000000000, 1000, 13, 1, 12}, -	{ 19200000, 1000000000, 625,  12, 1, 8}, -	{ 26000000, 1000000000, 1000, 26, 1, 12}, +	{ 12000000, 1000000000, 1000, 12, 0, 12}, +	{ 13000000, 1000000000, 1000, 13, 0, 12}, +	{ 19200000, 1000000000, 625,  12, 0, 8}, +	{ 26000000, 1000000000, 1000, 26, 0, 12},  	{ 0, 0, 0, 0, 0, 0 },  }; +static struct pdiv_map pllu_p[] = { +	{ .pdiv = 1, .hw_val = 1 }, +	{ .pdiv = 2, .hw_val = 0 }, +	{ .pdiv = 0, .hw_val = 0 }, +}; +  static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { -	{ 12000000, 480000000, 960, 12, 2, 12}, -	{ 13000000, 480000000, 960, 13, 2, 12}, -	{ 16800000, 480000000, 400, 7,  2, 5}, -	{ 19200000, 480000000, 200, 4,  2, 3}, -	{ 26000000, 480000000, 960, 26, 2, 12}, +	{ 12000000, 480000000, 960, 12, 0, 12}, +	{ 13000000, 480000000, 960, 13, 0, 12}, +	{ 16800000, 480000000, 400, 7,  0, 5}, +	{ 19200000, 480000000, 200, 4,  0, 3}, +	{ 26000000, 480000000, 960, 26, 0, 12},  	{ 0, 0, 0, 0, 0, 0 },  };  static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {  	/* 1.7 GHz */ -	{ 12000000, 1700000000, 850,  6,  1, 8}, -	{ 13000000, 1700000000, 915,  7,  1, 8},	/* actual: 1699.2 MHz */ -	{ 16800000, 1700000000, 708,  7,  1, 8},	/* actual: 1699.2 MHz */ -	{ 19200000, 1700000000, 885,  10, 1, 8},	/* actual: 1699.2 MHz */ -	{ 26000000, 1700000000, 850,  13, 1, 8}, +	{ 12000000, 1700000000, 850,  6,  0, 8}, +	{ 13000000, 1700000000, 915,  7,  0, 8},	/* actual: 1699.2 MHz */ +	{ 16800000, 1700000000, 708,  7,  0, 8},	/* actual: 1699.2 MHz */ +	{ 19200000, 1700000000, 885,  10, 0, 8},	/* actual: 1699.2 MHz */ +	{ 26000000, 1700000000, 850,  13, 0, 8},  	/* 1.6 GHz */ -	{ 12000000, 1600000000, 800,  6,  1, 8}, -	{ 13000000, 1600000000, 738,  6,  1, 8},	/* actual: 1599.0 MHz */ -	{ 16800000, 1600000000, 857,  9,  1, 8},	/* actual: 1599.7 MHz */ -	{ 19200000, 1600000000, 500,  6,  1, 8}, -	{ 26000000, 1600000000, 800,  13, 1, 8}, +	{ 12000000, 1600000000, 800,  6,  0, 8}, +	{ 13000000, 1600000000, 738,  6,  0, 8},	/* actual: 1599.0 MHz */ +	{ 16800000, 1600000000, 857,  9,  0, 8},	/* actual: 1599.7 MHz */ +	{ 19200000, 1600000000, 500,  6,  0, 8}, +	{ 26000000, 1600000000, 800,  13, 0, 8},  	/* 1.5 GHz */ -	{ 12000000, 1500000000, 750,  6,  1, 8}, -	{ 13000000, 1500000000, 923,  8,  1, 8},	/* actual: 1499.8 MHz */ -	{ 16800000, 1500000000, 625,  7,  1, 8}, -	{ 19200000, 1500000000, 625,  8,  1, 8}, -	{ 26000000, 1500000000, 750,  13, 1, 8}, +	{ 12000000, 1500000000, 750,  6,  0, 8}, +	{ 13000000, 1500000000, 923,  8,  0, 8},	/* actual: 1499.8 MHz */ +	{ 16800000, 1500000000, 625,  7,  0, 8}, +	{ 19200000, 1500000000, 625,  8,  0, 8}, +	{ 26000000, 1500000000, 750,  13, 0, 8},  	/* 1.4 GHz */ -	{ 12000000, 1400000000, 700,  6,  1, 8}, -	{ 13000000, 1400000000, 969,  9,  1, 8},	/* actual: 1399.7 MHz */ -	{ 16800000, 1400000000, 1000, 12, 1, 8}, -	{ 19200000, 1400000000, 875,  12, 1, 8}, -	{ 26000000, 1400000000, 700,  13, 1, 8}, +	{ 12000000, 1400000000, 700,  6,  0, 8}, +	{ 13000000, 1400000000, 969,  9,  0, 8},	/* actual: 1399.7 MHz */ +	{ 16800000, 1400000000, 1000, 12, 0, 8}, +	{ 19200000, 1400000000, 875,  12, 0, 8}, +	{ 26000000, 1400000000, 700,  13, 0, 8},  	/* 1.3 GHz */ -	{ 12000000, 1300000000, 975,  9,  1, 8}, -	{ 13000000, 1300000000, 1000, 10, 1, 8}, -	{ 16800000, 1300000000, 928,  12, 1, 8},	/* actual: 1299.2 MHz */ -	{ 19200000, 1300000000, 812,  12, 1, 8},	/* actual: 1299.2 MHz */ -	{ 26000000, 1300000000, 650,  13, 1, 8}, +	{ 12000000, 1300000000, 975,  9,  0, 8}, +	{ 13000000, 1300000000, 1000, 10, 0, 8}, +	{ 16800000, 1300000000, 928,  12, 0, 8},	/* actual: 1299.2 MHz */ +	{ 19200000, 1300000000, 812,  12, 0, 8},	/* actual: 1299.2 MHz */ +	{ 26000000, 1300000000, 650,  13, 0, 8},  	/* 1.2 GHz */ -	{ 12000000, 1200000000, 1000, 10, 1, 8}, -	{ 13000000, 1200000000, 923,  10, 1, 8},	/* actual: 1199.9 MHz */ -	{ 16800000, 1200000000, 1000, 14, 1, 8}, -	{ 19200000, 1200000000, 1000, 16, 1, 8}, -	{ 26000000, 1200000000, 600,  13, 1, 8}, +	{ 12000000, 1200000000, 1000, 10, 0, 8}, +	{ 13000000, 1200000000, 923,  10, 0, 8},	/* actual: 1199.9 MHz */ +	{ 16800000, 1200000000, 1000, 14, 0, 8}, +	{ 19200000, 1200000000, 1000, 16, 0, 8}, +	{ 26000000, 1200000000, 600,  13, 0, 8},  	/* 1.1 GHz */ -	{ 12000000, 1100000000, 825,  9,  1, 8}, -	{ 13000000, 1100000000, 846,  10, 1, 8},	/* actual: 1099.8 MHz */ -	{ 16800000, 1100000000, 982,  15, 1, 8},	/* actual: 1099.8 MHz */ -	{ 19200000, 1100000000, 859,  15, 1, 8},	/* actual: 1099.5 MHz */ -	{ 26000000, 1100000000, 550,  13, 1, 8}, +	{ 12000000, 1100000000, 825,  9,  0, 8}, +	{ 13000000, 1100000000, 846,  10, 0, 8},	/* actual: 1099.8 MHz */ +	{ 16800000, 1100000000, 982,  15, 0, 8},	/* actual: 1099.8 MHz */ +	{ 19200000, 1100000000, 859,  15, 0, 8},	/* actual: 1099.5 MHz */ +	{ 26000000, 1100000000, 550,  13, 0, 8},  	/* 1 GHz */ -	{ 12000000, 1000000000, 1000, 12, 1, 8}, -	{ 13000000, 1000000000, 1000, 13, 1, 8}, -	{ 16800000, 1000000000, 833,  14, 1, 8},	/* actual: 999.6 MHz */ -	{ 19200000, 1000000000, 625,  12, 1, 8}, -	{ 26000000, 1000000000, 1000, 26, 1, 8}, +	{ 12000000, 1000000000, 1000, 12, 0, 8}, +	{ 13000000, 1000000000, 1000, 13, 0, 8}, +	{ 16800000, 1000000000, 833,  14, 0, 8},	/* actual: 999.6 MHz */ +	{ 19200000, 1000000000, 625,  12, 0, 8}, +	{ 26000000, 1000000000, 1000, 26, 0, 8},  	{ 0, 0, 0, 0, 0, 0 },  }; @@ -553,7 +559,7 @@ static struct tegra_clk_pll_params pll_c_params = {  	.vco_max = 1400000000,  	.base_reg = PLLC_BASE,  	.misc_reg = PLLC_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -567,7 +573,7 @@ static struct tegra_clk_pll_params pll_m_params = {  	.vco_max = 1200000000,  	.base_reg = PLLM_BASE,  	.misc_reg = PLLM_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -581,7 +587,7 @@ static struct tegra_clk_pll_params pll_p_params = {  	.vco_max = 1400000000,  	.base_reg = PLLP_BASE,  	.misc_reg = PLLP_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -595,7 +601,7 @@ static struct tegra_clk_pll_params pll_a_params = {  	.vco_max = 1400000000,  	.base_reg = PLLA_BASE,  	.misc_reg = PLLA_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -609,7 +615,7 @@ static struct tegra_clk_pll_params pll_d_params = {  	.vco_max = 1000000000,  	.base_reg = PLLD_BASE,  	.misc_reg = PLLD_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,  	.lock_delay = 1000,  }; @@ -623,7 +629,7 @@ static struct tegra_clk_pll_params pll_d2_params = {  	.vco_max = 1000000000,  	.base_reg = PLLD2_BASE,  	.misc_reg = PLLD2_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,  	.lock_delay = 1000,  }; @@ -637,9 +643,10 @@ static struct tegra_clk_pll_params pll_u_params = {  	.vco_max = 960000000,  	.base_reg = PLLU_BASE,  	.misc_reg = PLLU_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,  	.lock_delay = 1000, +	.pdiv_tohw = pllu_p,  };  static struct tegra_clk_pll_params pll_x_params = { @@ -651,7 +658,7 @@ static struct tegra_clk_pll_params pll_x_params = {  	.vco_max = 1700000000,  	.base_reg = PLLX_BASE,  	.misc_reg = PLLX_MISC, -	.lock_bit_idx = PLL_BASE_LOCK, +	.lock_mask = PLL_BASE_LOCK,  	.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -665,7 +672,7 @@ static struct tegra_clk_pll_params pll_e_params = {  	.vco_max = 2400000000U,  	.base_reg = PLLE_BASE,  	.misc_reg = PLLE_MISC, -	.lock_bit_idx = PLLE_MISC_LOCK, +	.lock_mask = PLLE_MISC_LOCK,  	.lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,  	.lock_delay = 300,  }; @@ -1661,7 +1668,7 @@ static void __init tegra30_periph_clk_init(void)  		data = &tegra_periph_clk_list[i];  		clk = tegra_clk_register_periph(data->name, data->parent_names,  				data->num_parents, &data->periph, -				clk_base, data->offset); +				clk_base, data->offset, data->flags);  		clk_register_clkdev(clk, data->con_id, data->dev_id);  		clks[data->clk_id] = clk;  	} @@ -1911,9 +1918,16 @@ static __initdata struct tegra_clk_init_table init_table[] = {  	{disp1, pll_p, 600000000, 0},  	{disp2, pll_p, 600000000, 0},  	{twd, clk_max, 0, 1}, +	{gr2d, pll_c, 300000000, 0}, +	{gr3d, pll_c, 300000000, 0},  	{clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */  }; +static void __init tegra30_clock_apply_init_table(void) +{ +	tegra_init_from_table(init_table, clks, clk_max); +} +  /*   * Some clocks may be used by different drivers depending on the board   * configuration.  List those here to register them twice in the clock lookup @@ -1931,7 +1945,6 @@ static struct tegra_clk_duplicate tegra_clk_duplicates[] = {  	TEGRA_CLK_DUPLICATE(cml1, "tegra_sata_cml", NULL),  	TEGRA_CLK_DUPLICATE(cml0, "tegra_pcie", "cml"),  	TEGRA_CLK_DUPLICATE(pciex, "tegra_pcie", "pciex"), -	TEGRA_CLK_DUPLICATE(twd, "smp_twd", NULL),  	TEGRA_CLK_DUPLICATE(vcp, "nvavp", "vcp"),  	TEGRA_CLK_DUPLICATE(clk_max, NULL, NULL), /* MUST be the last entry */  }; @@ -1988,7 +2001,7 @@ void __init tegra30_clock_init(struct device_node *np)  	clk_data.clk_num = ARRAY_SIZE(clks);  	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); -	tegra_init_from_table(init_table, clks, clk_max); +	tegra_clk_apply_init_table = tegra30_clock_apply_init_table;  	tegra_cpu_car_ops = &tegra30_cpu_car_ops;  } diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index a603b9af0ad..923ca7ee469 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -22,7 +22,8 @@  #include "clk.h"  /* Global data of Tegra CPU CAR ops */ -struct tegra_cpu_car_ops *tegra_cpu_car_ops; +static struct tegra_cpu_car_ops dummy_car_ops; +struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;  void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,  				struct clk *clks[], int clk_max) @@ -76,6 +77,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,  static const struct of_device_id tegra_dt_clk_match[] = {  	{ .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init },  	{ .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init }, +	{ .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init },  	{ }  }; @@ -83,3 +85,13 @@ void __init tegra_clocks_init(void)  {  	of_clk_init(tegra_dt_clk_match);  } + +tegra_clk_apply_init_table_func tegra_clk_apply_init_table; + +void __init tegra_clocks_apply_init_table(void) +{ +	if (!tegra_clk_apply_init_table) +		return; + +	tegra_clk_apply_init_table(); +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 0744731c622..e0565620d68 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -1,4 +1,4 @@ -/* +	/*   * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.   *   * This program is free software; you can redistribute it and/or modify it @@ -117,6 +117,17 @@ struct tegra_clk_pll_freq_table {  };  /** + * struct pdiv_map - map post divider to hw value + * + * @pdiv:		post divider + * @hw_val:		value to be written to the PLL hw + */ +struct pdiv_map { +	u8 pdiv; +	u8 hw_val; +}; + +/**   * struct clk_pll_params - PLL parameters   *   * @input_min:			Minimum input frequency @@ -143,9 +154,18 @@ struct tegra_clk_pll_params {  	u32		base_reg;  	u32		misc_reg;  	u32		lock_reg; -	u32		lock_bit_idx; +	u32		lock_mask;  	u32		lock_enable_bit_idx; +	u32		iddq_reg; +	u32		iddq_bit_idx; +	u32		aux_reg; +	u32		dyn_ramp_reg; +	u32		ext_misc_reg[3]; +	int		stepa_shift; +	int		stepb_shift;  	int		lock_delay; +	int		max_p; +	struct pdiv_map *pdiv_tohw;  };  /** @@ -182,12 +202,16 @@ struct tegra_clk_pll_params {   * TEGRA_PLL_FIXED - We are not supposed to change output frequency   *     of some plls.   * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. + * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the + *     base register. + * TEGRA_PLL_BYPASS - PLL has bypass bit + * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring   */  struct tegra_clk_pll {  	struct clk_hw	hw;  	void __iomem	*clk_base;  	void __iomem	*pmc; -	u8		flags; +	u32		flags;  	unsigned long	fixed_rate;  	spinlock_t	*lock;  	u8		divn_shift; @@ -210,20 +234,64 @@ struct tegra_clk_pll {  #define TEGRA_PLLM BIT(5)  #define TEGRA_PLL_FIXED BIT(6)  #define TEGRA_PLLE_CONFIGURE BIT(7) +#define TEGRA_PLL_LOCK_MISC BIT(8) +#define TEGRA_PLL_BYPASS BIT(9) +#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10)  extern const struct clk_ops tegra_clk_pll_ops;  extern const struct clk_ops tegra_clk_plle_ops;  struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,  		void __iomem *clk_base, void __iomem *pmc,  		unsigned long flags, unsigned long fixed_rate, -		struct tegra_clk_pll_params *pll_params, u8 pll_flags, +		struct tegra_clk_pll_params *pll_params, u32 pll_flags,  		struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); +  struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,  		void __iomem *clk_base, void __iomem *pmc,  		unsigned long flags, unsigned long fixed_rate, -		struct tegra_clk_pll_params *pll_params, u8 pll_flags, +		struct tegra_clk_pll_params *pll_params, u32 pll_flags,  		struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock); +struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, +			    void __iomem *clk_base, void __iomem *pmc, +			    unsigned long flags, unsigned long fixed_rate, +			    struct tegra_clk_pll_params *pll_params, +			    u32 pll_flags, +			    struct tegra_clk_pll_freq_table *freq_table, +			    spinlock_t *lock); + +struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, +			   void __iomem *clk_base, void __iomem *pmc, +			   unsigned long flags, unsigned long fixed_rate, +			   struct tegra_clk_pll_params *pll_params, +			   u32 pll_flags, +			   struct tegra_clk_pll_freq_table *freq_table, +			   spinlock_t *lock); + +struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name, +			   void __iomem *clk_base, void __iomem *pmc, +			   unsigned long flags, unsigned long fixed_rate, +			   struct tegra_clk_pll_params *pll_params, +			   u32 pll_flags, +			   struct tegra_clk_pll_freq_table *freq_table, +			   spinlock_t *lock); + +struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, +			   void __iomem *clk_base, void __iomem *pmc, +			   unsigned long flags, unsigned long fixed_rate, +			   struct tegra_clk_pll_params *pll_params, +			   u32 pll_flags, +			   struct tegra_clk_pll_freq_table *freq_table, +			   spinlock_t *lock, unsigned long parent_rate); + +struct clk *tegra_clk_register_plle_tegra114(const char *name, +				const char *parent_name, +				void __iomem *clk_base, unsigned long flags, +				unsigned long fixed_rate, +				struct tegra_clk_pll_params *pll_params, +				struct tegra_clk_pll_freq_table *freq_table, +				spinlock_t *lock); +  /**   * struct tegra_clk_pll_out - PLL divider down clock   * @@ -290,6 +358,7 @@ struct tegra_clk_periph_regs {   * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the   *     bus to flush the write operation in apb bus. This flag indicates   *     that this peripheral is in apb bus. + * TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug   */  struct tegra_clk_periph_gate {  	u32			magic; @@ -309,6 +378,7 @@ struct tegra_clk_periph_gate {  #define TEGRA_PERIPH_NO_RESET BIT(0)  #define TEGRA_PERIPH_MANUAL_RESET BIT(1)  #define TEGRA_PERIPH_ON_APB BIT(2) +#define TEGRA_PERIPH_WAR_1005168 BIT(3)  void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert);  extern const struct clk_ops tegra_clk_periph_gate_ops; @@ -349,21 +419,22 @@ extern const struct clk_ops tegra_clk_periph_ops;  struct clk *tegra_clk_register_periph(const char *name,  		const char **parent_names, int num_parents,  		struct tegra_clk_periph *periph, void __iomem *clk_base, -		u32 offset); +		u32 offset, unsigned long flags);  struct clk *tegra_clk_register_periph_nodiv(const char *name,  		const char **parent_names, int num_parents,  		struct tegra_clk_periph *periph, void __iomem *clk_base,  		u32 offset); -#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags,		\ +#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags,		\  			 _div_shift, _div_width, _div_frac_width,	\  			 _div_flags, _clk_num, _enb_refcnt, _regs,	\ -			 _gate_flags)					\ +			 _gate_flags, _table)				\  	{								\  		.mux = {						\  			.flags = _mux_flags,				\  			.shift = _mux_shift,				\ -			.width = _mux_width,				\ +			.mask = _mux_mask,				\ +			.table = _table,				\  		},							\  		.divider = {						\  			.flags = _div_flags,				\ @@ -391,28 +462,41 @@ struct tegra_periph_init_data {  	u32 offset;  	const char *con_id;  	const char *dev_id; +	unsigned long flags;  }; -#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ -			_mux_shift, _mux_width, _mux_flags, _div_shift,	\ +#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ +			_mux_shift, _mux_mask, _mux_flags, _div_shift,	\  			_div_width, _div_frac_width, _div_flags, _regs,	\ -			_clk_num, _enb_refcnt, _gate_flags, _clk_id)	\ +			_clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\ +			_flags) \  	{								\  		.name = _name,						\  		.clk_id = _clk_id,					\  		.parent_names = _parent_names,				\  		.num_parents = ARRAY_SIZE(_parent_names),		\ -		.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width,	\ +		.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask,	\  					   _mux_flags, _div_shift,	\  					   _div_width, _div_frac_width,	\  					   _div_flags, _clk_num,	\  					   _enb_refcnt, _regs,		\ -					   _gate_flags),		\ +					   _gate_flags, _table),	\  		.offset = _offset,					\  		.con_id = _con_id,					\  		.dev_id = _dev_id,					\ +		.flags = _flags						\  	} +#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ +			_mux_shift, _mux_width, _mux_flags, _div_shift,	\ +			_div_width, _div_frac_width, _div_flags, _regs,	\ +			_clk_num, _enb_refcnt, _gate_flags, _clk_id)	\ +	TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ +			_mux_shift, BIT(_mux_width) - 1, _mux_flags,	\ +			_div_shift, _div_width, _div_frac_width, _div_flags, \ +			_regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ +			NULL, 0) +  /**   * struct clk_super_mux - super clock   * @@ -499,4 +583,13 @@ void tegra30_clock_init(struct device_node *np);  static inline void tegra30_clock_init(struct device_node *np) {}  #endif /* CONFIG_ARCH_TEGRA_3x_SOC */ +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_clock_init(struct device_node *np); +#else +static inline void tegra114_clock_init(struct device_node *np) {} +#endif /* CONFIG_ARCH_TEGRA114_SOC */ + +typedef void (*tegra_clk_apply_init_table_func)(void); +extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; +  #endif /* TEGRA_CLK_H */ diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 74faa7e3cf5..293a2885441 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -20,15 +20,23 @@  struct clk_prcmu {  	struct clk_hw hw;  	u8 cg_sel; +	int is_prepared;  	int is_enabled; +	int opp_requested;  };  /* PRCMU clock operations. */  static int clk_prcmu_prepare(struct clk_hw *hw)  { +	int ret;  	struct clk_prcmu *clk = to_clk_prcmu(hw); -	return prcmu_request_clock(clk->cg_sel, true); + +	ret = prcmu_request_clock(clk->cg_sel, true); +	if (!ret) +		clk->is_prepared = 1; + +	return ret;;  }  static void clk_prcmu_unprepare(struct clk_hw *hw) @@ -36,7 +44,15 @@ static void clk_prcmu_unprepare(struct clk_hw *hw)  	struct clk_prcmu *clk = to_clk_prcmu(hw);  	if (prcmu_request_clock(clk->cg_sel, false))  		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, -			hw->init->name); +			__clk_get_name(hw->clk)); +	else +		clk->is_prepared = 0; +} + +static int clk_prcmu_is_prepared(struct clk_hw *hw) +{ +	struct clk_prcmu *clk = to_clk_prcmu(hw); +	return clk->is_prepared;  }  static int clk_prcmu_enable(struct clk_hw *hw) @@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,  	return prcmu_set_clock_rate(clk->cg_sel, rate);  } -static int request_ape_opp100(bool enable) -{ -	static int reqs; -	int err = 0; - -	if (enable) { -		if (!reqs) -			err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, -							"clock", 100); -		if (!err) -			reqs++; -	} else { -		reqs--; -		if (!reqs) -			prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, -						"clock"); -	} -	return err; -} -  static int clk_prcmu_opp_prepare(struct clk_hw *hw)  {  	int err;  	struct clk_prcmu *clk = to_clk_prcmu(hw); -	err = request_ape_opp100(true); -	if (err) { -		pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", -			__func__, hw->init->name); -		return err; +	if (!clk->opp_requested) { +		err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, +						(char *)__clk_get_name(hw->clk), +						100); +		if (err) { +			pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", +				__func__, __clk_get_name(hw->clk)); +			return err; +		} +		clk->opp_requested = 1;  	}  	err = prcmu_request_clock(clk->cg_sel, true); -	if (err) -		request_ape_opp100(false); +	if (err) { +		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, +					(char *)__clk_get_name(hw->clk)); +		clk->opp_requested = 0; +		return err; +	} -	return err; +	clk->is_prepared = 1; +	return 0;  }  static void clk_prcmu_opp_unprepare(struct clk_hw *hw)  {  	struct clk_prcmu *clk = to_clk_prcmu(hw); -	if (prcmu_request_clock(clk->cg_sel, false)) -		goto out_error; -	if (request_ape_opp100(false)) -		goto out_error; -	return; +	if (prcmu_request_clock(clk->cg_sel, false)) { +		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, +			__clk_get_name(hw->clk)); +		return; +	} + +	if (clk->opp_requested) { +		prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, +					(char *)__clk_get_name(hw->clk)); +		clk->opp_requested = 0; +	} -out_error: -	pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, -		hw->init->name); +	clk->is_prepared = 0;  }  static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) @@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)  	int err;  	struct clk_prcmu *clk = to_clk_prcmu(hw); -	err = prcmu_request_ape_opp_100_voltage(true); -	if (err) { -		pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n", -			__func__, hw->init->name); -		return err; +	if (!clk->opp_requested) { +		err = prcmu_request_ape_opp_100_voltage(true); +		if (err) { +			pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", +				__func__, __clk_get_name(hw->clk)); +			return err; +		} +		clk->opp_requested = 1;  	}  	err = prcmu_request_clock(clk->cg_sel, true); -	if (err) +	if (err) {  		prcmu_request_ape_opp_100_voltage(false); +		clk->opp_requested = 0; +		return err; +	} -	return err; +	clk->is_prepared = 1; +	return 0;  }  static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)  {  	struct clk_prcmu *clk = to_clk_prcmu(hw); -	if (prcmu_request_clock(clk->cg_sel, false)) -		goto out_error; -	if (prcmu_request_ape_opp_100_voltage(false)) -		goto out_error; -	return; +	if (prcmu_request_clock(clk->cg_sel, false)) { +		pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, +			__clk_get_name(hw->clk)); +		return; +	} + +	if (clk->opp_requested) { +		prcmu_request_ape_opp_100_voltage(false); +		clk->opp_requested = 0; +	} -out_error: -	pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, -		hw->init->name); +	clk->is_prepared = 0;  }  static struct clk_ops clk_prcmu_scalable_ops = {  	.prepare = clk_prcmu_prepare,  	.unprepare = clk_prcmu_unprepare, +	.is_prepared = clk_prcmu_is_prepared,  	.enable = clk_prcmu_enable,  	.disable = clk_prcmu_disable,  	.is_enabled = clk_prcmu_is_enabled, @@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = {  static struct clk_ops clk_prcmu_gate_ops = {  	.prepare = clk_prcmu_prepare,  	.unprepare = clk_prcmu_unprepare, +	.is_prepared = clk_prcmu_is_prepared,  	.enable = clk_prcmu_enable,  	.disable = clk_prcmu_disable,  	.is_enabled = clk_prcmu_is_enabled, @@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = {  static struct clk_ops clk_prcmu_opp_gate_ops = {  	.prepare = clk_prcmu_opp_prepare,  	.unprepare = clk_prcmu_opp_unprepare, +	.is_prepared = clk_prcmu_is_prepared,  	.enable = clk_prcmu_enable,  	.disable = clk_prcmu_disable,  	.is_enabled = clk_prcmu_is_enabled, @@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = {  static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {  	.prepare = clk_prcmu_opp_volt_prepare,  	.unprepare = clk_prcmu_opp_volt_unprepare, +	.is_prepared = clk_prcmu_is_prepared,  	.enable = clk_prcmu_enable,  	.disable = clk_prcmu_disable,  	.is_enabled = clk_prcmu_is_enabled, @@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name,  	}  	clk->cg_sel = cg_sel; +	clk->is_prepared = 1;  	clk->is_enabled = 1; +	clk->opp_requested = 0;  	/* "rate" can be used for changing the initial frequency */  	if (rate)  		prcmu_set_clock_rate(cg_sel, rate); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index d98e7e1ee59..29ba35e6a14 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -31,6 +31,9 @@ config SUNXI_TIMER  config VT8500_TIMER  	bool +config CADENCE_TTC_TIMER +	bool +  config CLKSRC_NOMADIK_MTU  	bool  	depends on (ARCH_NOMADIK || ARCH_U8500) @@ -68,3 +71,8 @@ config CLKSRC_METAG_GENERIC  	def_bool y if METAG  	help  	  This option enables support for the Meta per-thread timers. + +config CLKSRC_EXYNOS_MCT +	def_bool y if ARCH_EXYNOS +	help +	  Support for Multi Core Timer controller on Exynos SoCs. diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283aec5b..cd1f09cbd61 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -19,6 +19,8 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o  obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o  obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_timer.o  obj-$(CONFIG_VT8500_TIMER)	+= vt8500_timer.o +obj-$(CONFIG_CADENCE_TTC_TIMER)	+= cadence_ttc_timer.o +obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o  obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o  obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c new file mode 100644 index 00000000000..685bc60e210 --- /dev/null +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -0,0 +1,436 @@ +/* + * This file contains driver for the Cadence Triple Timer Counter Rev 06 + * + *  Copyright (C) 2011-2013 Xilinx + * + * based on arch/mips/kernel/time.c timer driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/clockchips.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/slab.h> +#include <linux/clk-provider.h> + +/* + * This driver configures the 2 16-bit count-up timers as follows: + * + * T1: Timer 1, clocksource for generic timekeeping + * T2: Timer 2, clockevent source for hrtimers + * T3: Timer 3, <unused> + * + * The input frequency to the timer module for emulation is 2.5MHz which is + * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, + * the timers are clocked at 78.125KHz (12.8 us resolution). + + * The input frequency to the timer module in silicon is configurable and + * obtained from device tree. The pre-scaler of 32 is used. + */ + +/* + * Timer Register Offset Definitions of Timer 1, Increment base address by 4 + * and use same offsets for Timer 2 + */ +#define TTC_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */ +#define TTC_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */ +#define TTC_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */ +#define TTC_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */ +#define TTC_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */ +#define TTC_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */ + +#define TTC_CNT_CNTRL_DISABLE_MASK	0x1 + +/* + * Setup the timers to use pre-scaling, using a fixed value for now that will + * work across most input frequency, but it may need to be more dynamic + */ +#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */ +#define PRESCALE		2048	/* The exponent must match this */ +#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1) +#define CLK_CNTRL_PRESCALE_EN	1 +#define CNT_CNTRL_RESET		(1 << 4) + +/** + * struct ttc_timer - This definition defines local timer structure + * + * @base_addr:	Base address of timer + * @clk:	Associated clock source + * @clk_rate_change_nb	Notifier block for clock rate changes + */ +struct ttc_timer { +	void __iomem *base_addr; +	struct clk *clk; +	struct notifier_block clk_rate_change_nb; +}; + +#define to_ttc_timer(x) \ +		container_of(x, struct ttc_timer, clk_rate_change_nb) + +struct ttc_timer_clocksource { +	struct ttc_timer	ttc; +	struct clocksource	cs; +}; + +#define to_ttc_timer_clksrc(x) \ +		container_of(x, struct ttc_timer_clocksource, cs) + +struct ttc_timer_clockevent { +	struct ttc_timer		ttc; +	struct clock_event_device	ce; +}; + +#define to_ttc_timer_clkevent(x) \ +		container_of(x, struct ttc_timer_clockevent, ce) + +/** + * ttc_set_interval - Set the timer interval value + * + * @timer:	Pointer to the timer instance + * @cycles:	Timer interval ticks + **/ +static void ttc_set_interval(struct ttc_timer *timer, +					unsigned long cycles) +{ +	u32 ctrl_reg; + +	/* Disable the counter, set the counter value  and re-enable counter */ +	ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET); +	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; +	__raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); + +	__raw_writel(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET); + +	/* +	 * Reset the counter (0x10) so that it starts from 0, one-shot +	 * mode makes this needed for timing to be right. +	 */ +	ctrl_reg |= CNT_CNTRL_RESET; +	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; +	__raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); +} + +/** + * ttc_clock_event_interrupt - Clock event timer interrupt handler + * + * @irq:	IRQ number of the Timer + * @dev_id:	void pointer to the ttc_timer instance + * + * returns: Always IRQ_HANDLED - success + **/ +static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) +{ +	struct ttc_timer_clockevent *ttce = dev_id; +	struct ttc_timer *timer = &ttce->ttc; + +	/* Acknowledge the interrupt and call event handler */ +	__raw_readl(timer->base_addr + TTC_ISR_OFFSET); + +	ttce->ce.event_handler(&ttce->ce); + +	return IRQ_HANDLED; +} + +/** + * __ttc_clocksource_read - Reads the timer counter register + * + * returns: Current timer counter register value + **/ +static cycle_t __ttc_clocksource_read(struct clocksource *cs) +{ +	struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc; + +	return (cycle_t)__raw_readl(timer->base_addr + +				TTC_COUNT_VAL_OFFSET); +} + +/** + * ttc_set_next_event - Sets the time interval for next event + * + * @cycles:	Timer interval ticks + * @evt:	Address of clock event instance + * + * returns: Always 0 - success + **/ +static int ttc_set_next_event(unsigned long cycles, +					struct clock_event_device *evt) +{ +	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); +	struct ttc_timer *timer = &ttce->ttc; + +	ttc_set_interval(timer, cycles); +	return 0; +} + +/** + * ttc_set_mode - Sets the mode of timer + * + * @mode:	Mode to be set + * @evt:	Address of clock event instance + **/ +static void ttc_set_mode(enum clock_event_mode mode, +					struct clock_event_device *evt) +{ +	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); +	struct ttc_timer *timer = &ttce->ttc; +	u32 ctrl_reg; + +	switch (mode) { +	case CLOCK_EVT_MODE_PERIODIC: +		ttc_set_interval(timer, +				DIV_ROUND_CLOSEST(clk_get_rate(ttce->ttc.clk), +					PRESCALE * HZ)); +		break; +	case CLOCK_EVT_MODE_ONESHOT: +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_SHUTDOWN: +		ctrl_reg = __raw_readl(timer->base_addr + +					TTC_CNT_CNTRL_OFFSET); +		ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; +		__raw_writel(ctrl_reg, +				timer->base_addr + TTC_CNT_CNTRL_OFFSET); +		break; +	case CLOCK_EVT_MODE_RESUME: +		ctrl_reg = __raw_readl(timer->base_addr + +					TTC_CNT_CNTRL_OFFSET); +		ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; +		__raw_writel(ctrl_reg, +				timer->base_addr + TTC_CNT_CNTRL_OFFSET); +		break; +	} +} + +static int ttc_rate_change_clocksource_cb(struct notifier_block *nb, +		unsigned long event, void *data) +{ +	struct clk_notifier_data *ndata = data; +	struct ttc_timer *ttc = to_ttc_timer(nb); +	struct ttc_timer_clocksource *ttccs = container_of(ttc, +			struct ttc_timer_clocksource, ttc); + +	switch (event) { +	case POST_RATE_CHANGE: +		/* +		 * Do whatever is necessary to maintain a proper time base +		 * +		 * I cannot find a way to adjust the currently used clocksource +		 * to the new frequency. __clocksource_updatefreq_hz() sounds +		 * good, but does not work. Not sure what's that missing. +		 * +		 * This approach works, but triggers two clocksource switches. +		 * The first after unregister to clocksource jiffies. And +		 * another one after the register to the newly registered timer. +		 * +		 * Alternatively we could 'waste' another HW timer to ping pong +		 * between clock sources. That would also use one register and +		 * one unregister call, but only trigger one clocksource switch +		 * for the cost of another HW timer used by the OS. +		 */ +		clocksource_unregister(&ttccs->cs); +		clocksource_register_hz(&ttccs->cs, +				ndata->new_rate / PRESCALE); +		/* fall through */ +	case PRE_RATE_CHANGE: +	case ABORT_RATE_CHANGE: +	default: +		return NOTIFY_DONE; +	} +} + +static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) +{ +	struct ttc_timer_clocksource *ttccs; +	int err; + +	ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); +	if (WARN_ON(!ttccs)) +		return; + +	ttccs->ttc.clk = clk; + +	err = clk_prepare_enable(ttccs->ttc.clk); +	if (WARN_ON(err)) { +		kfree(ttccs); +		return; +	} + +	ttccs->ttc.clk_rate_change_nb.notifier_call = +		ttc_rate_change_clocksource_cb; +	ttccs->ttc.clk_rate_change_nb.next = NULL; +	if (clk_notifier_register(ttccs->ttc.clk, +				&ttccs->ttc.clk_rate_change_nb)) +		pr_warn("Unable to register clock notifier.\n"); + +	ttccs->ttc.base_addr = base; +	ttccs->cs.name = "ttc_clocksource"; +	ttccs->cs.rating = 200; +	ttccs->cs.read = __ttc_clocksource_read; +	ttccs->cs.mask = CLOCKSOURCE_MASK(16); +	ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; + +	/* +	 * Setup the clock source counter to be an incrementing counter +	 * with no interrupt and it rolls over at 0xFFFF. Pre-scale +	 * it by 32 also. Let it start running now. +	 */ +	__raw_writel(0x0,  ttccs->ttc.base_addr + TTC_IER_OFFSET); +	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, +		     ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); +	__raw_writel(CNT_CNTRL_RESET, +		     ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET); + +	err = clocksource_register_hz(&ttccs->cs, +			clk_get_rate(ttccs->ttc.clk) / PRESCALE); +	if (WARN_ON(err)) { +		kfree(ttccs); +		return; +	} +} + +static int ttc_rate_change_clockevent_cb(struct notifier_block *nb, +		unsigned long event, void *data) +{ +	struct clk_notifier_data *ndata = data; +	struct ttc_timer *ttc = to_ttc_timer(nb); +	struct ttc_timer_clockevent *ttcce = container_of(ttc, +			struct ttc_timer_clockevent, ttc); + +	switch (event) { +	case POST_RATE_CHANGE: +	{ +		unsigned long flags; + +		/* +		 * clockevents_update_freq should be called with IRQ disabled on +		 * the CPU the timer provides events for. The timer we use is +		 * common to both CPUs, not sure if we need to run on both +		 * cores. +		 */ +		local_irq_save(flags); +		clockevents_update_freq(&ttcce->ce, +				ndata->new_rate / PRESCALE); +		local_irq_restore(flags); + +		/* fall through */ +	} +	case PRE_RATE_CHANGE: +	case ABORT_RATE_CHANGE: +	default: +		return NOTIFY_DONE; +	} +} + +static void __init ttc_setup_clockevent(struct clk *clk, +						void __iomem *base, u32 irq) +{ +	struct ttc_timer_clockevent *ttcce; +	int err; + +	ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); +	if (WARN_ON(!ttcce)) +		return; + +	ttcce->ttc.clk = clk; + +	err = clk_prepare_enable(ttcce->ttc.clk); +	if (WARN_ON(err)) { +		kfree(ttcce); +		return; +	} + +	ttcce->ttc.clk_rate_change_nb.notifier_call = +		ttc_rate_change_clockevent_cb; +	ttcce->ttc.clk_rate_change_nb.next = NULL; +	if (clk_notifier_register(ttcce->ttc.clk, +				&ttcce->ttc.clk_rate_change_nb)) +		pr_warn("Unable to register clock notifier.\n"); + +	ttcce->ttc.base_addr = base; +	ttcce->ce.name = "ttc_clockevent"; +	ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; +	ttcce->ce.set_next_event = ttc_set_next_event; +	ttcce->ce.set_mode = ttc_set_mode; +	ttcce->ce.rating = 200; +	ttcce->ce.irq = irq; +	ttcce->ce.cpumask = cpu_possible_mask; + +	/* +	 * Setup the clock event timer to be an interval timer which +	 * is prescaled by 32 using the interval interrupt. Leave it +	 * disabled for now. +	 */ +	__raw_writel(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET); +	__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, +		     ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); +	__raw_writel(0x1,  ttcce->ttc.base_addr + TTC_IER_OFFSET); + +	err = request_irq(irq, ttc_clock_event_interrupt, +			  IRQF_DISABLED | IRQF_TIMER, +			  ttcce->ce.name, ttcce); +	if (WARN_ON(err)) { +		kfree(ttcce); +		return; +	} + +	clockevents_config_and_register(&ttcce->ce, +			clk_get_rate(ttcce->ttc.clk) / PRESCALE, 1, 0xfffe); +} + +/** + * ttc_timer_init - Initialize the timer + * + * Initializes the timer hardware and register the clock source and clock event + * timers with Linux kernal timer framework + */ +static void __init ttc_timer_init(struct device_node *timer) +{ +	unsigned int irq; +	void __iomem *timer_baseaddr; +	struct clk *clk; +	static int initialized; + +	if (initialized) +		return; + +	initialized = 1; + +	/* +	 * Get the 1st Triple Timer Counter (TTC) block from the device tree +	 * and use it. Note that the event timer uses the interrupt and it's the +	 * 2nd TTC hence the irq_of_parse_and_map(,1) +	 */ +	timer_baseaddr = of_iomap(timer, 0); +	if (!timer_baseaddr) { +		pr_err("ERROR: invalid timer base address\n"); +		BUG(); +	} + +	irq = irq_of_parse_and_map(timer, 1); +	if (irq <= 0) { +		pr_err("ERROR: invalid interrupt number\n"); +		BUG(); +	} + +	clk = of_clk_get_by_name(timer, "cpu_1x"); +	if (IS_ERR(clk)) { +		pr_err("ERROR: timer input clock not found\n"); +		BUG(); +	} + +	ttc_setup_clocksource(clk, timer_baseaddr); +	ttc_setup_clockevent(clk, timer_baseaddr + 4, irq); + +	pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); +} + +CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init); diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index e6a553cb73e..4329a29a531 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -399,7 +399,18 @@ static struct platform_driver em_sti_device_driver = {  	}  }; -module_platform_driver(em_sti_device_driver); +static int __init em_sti_init(void) +{ +	return platform_driver_register(&em_sti_device_driver); +} + +static void __exit em_sti_exit(void) +{ +	platform_driver_unregister(&em_sti_device_driver); +} + +subsys_initcall(em_sti_init); +module_exit(em_sti_exit);  MODULE_AUTHOR("Magnus Damm");  MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver"); diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c new file mode 100644 index 00000000000..b078d7cbc93 --- /dev/null +++ b/drivers/clocksource/exynos_mct.c @@ -0,0 +1,555 @@ +/* linux/arch/arm/mach-exynos4/mct.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + *		http://www.samsung.com + * + * EXYNOS4 MCT(Multi-Core Timer) support + * + * 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/sched.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/percpu.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/clocksource.h> + +#include <asm/localtimer.h> + +#include <plat/cpu.h> + +#include <mach/map.h> +#include <mach/irqs.h> +#include <asm/mach/time.h> + +#define EXYNOS4_MCTREG(x)		(x) +#define EXYNOS4_MCT_G_CNT_L		EXYNOS4_MCTREG(0x100) +#define EXYNOS4_MCT_G_CNT_U		EXYNOS4_MCTREG(0x104) +#define EXYNOS4_MCT_G_CNT_WSTAT		EXYNOS4_MCTREG(0x110) +#define EXYNOS4_MCT_G_COMP0_L		EXYNOS4_MCTREG(0x200) +#define EXYNOS4_MCT_G_COMP0_U		EXYNOS4_MCTREG(0x204) +#define EXYNOS4_MCT_G_COMP0_ADD_INCR	EXYNOS4_MCTREG(0x208) +#define EXYNOS4_MCT_G_TCON		EXYNOS4_MCTREG(0x240) +#define EXYNOS4_MCT_G_INT_CSTAT		EXYNOS4_MCTREG(0x244) +#define EXYNOS4_MCT_G_INT_ENB		EXYNOS4_MCTREG(0x248) +#define EXYNOS4_MCT_G_WSTAT		EXYNOS4_MCTREG(0x24C) +#define _EXYNOS4_MCT_L_BASE		EXYNOS4_MCTREG(0x300) +#define EXYNOS4_MCT_L_BASE(x)		(_EXYNOS4_MCT_L_BASE + (0x100 * x)) +#define EXYNOS4_MCT_L_MASK		(0xffffff00) + +#define MCT_L_TCNTB_OFFSET		(0x00) +#define MCT_L_ICNTB_OFFSET		(0x08) +#define MCT_L_TCON_OFFSET		(0x20) +#define MCT_L_INT_CSTAT_OFFSET		(0x30) +#define MCT_L_INT_ENB_OFFSET		(0x34) +#define MCT_L_WSTAT_OFFSET		(0x40) +#define MCT_G_TCON_START		(1 << 8) +#define MCT_G_TCON_COMP0_AUTO_INC	(1 << 1) +#define MCT_G_TCON_COMP0_ENABLE		(1 << 0) +#define MCT_L_TCON_INTERVAL_MODE	(1 << 2) +#define MCT_L_TCON_INT_START		(1 << 1) +#define MCT_L_TCON_TIMER_START		(1 << 0) + +#define TICK_BASE_CNT	1 + +enum { +	MCT_INT_SPI, +	MCT_INT_PPI +}; + +enum { +	MCT_G0_IRQ, +	MCT_G1_IRQ, +	MCT_G2_IRQ, +	MCT_G3_IRQ, +	MCT_L0_IRQ, +	MCT_L1_IRQ, +	MCT_L2_IRQ, +	MCT_L3_IRQ, +	MCT_NR_IRQS, +}; + +static void __iomem *reg_base; +static unsigned long clk_rate; +static unsigned int mct_int_type; +static int mct_irqs[MCT_NR_IRQS]; + +struct mct_clock_event_device { +	struct clock_event_device *evt; +	unsigned long base; +	char name[10]; +}; + +static void exynos4_mct_write(unsigned int value, unsigned long offset) +{ +	unsigned long stat_addr; +	u32 mask; +	u32 i; + +	__raw_writel(value, reg_base + offset); + +	if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { +		stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; +		switch (offset & EXYNOS4_MCT_L_MASK) { +		case MCT_L_TCON_OFFSET: +			mask = 1 << 3;		/* L_TCON write status */ +			break; +		case MCT_L_ICNTB_OFFSET: +			mask = 1 << 1;		/* L_ICNTB write status */ +			break; +		case MCT_L_TCNTB_OFFSET: +			mask = 1 << 0;		/* L_TCNTB write status */ +			break; +		default: +			return; +		} +	} else { +		switch (offset) { +		case EXYNOS4_MCT_G_TCON: +			stat_addr = EXYNOS4_MCT_G_WSTAT; +			mask = 1 << 16;		/* G_TCON write status */ +			break; +		case EXYNOS4_MCT_G_COMP0_L: +			stat_addr = EXYNOS4_MCT_G_WSTAT; +			mask = 1 << 0;		/* G_COMP0_L write status */ +			break; +		case EXYNOS4_MCT_G_COMP0_U: +			stat_addr = EXYNOS4_MCT_G_WSTAT; +			mask = 1 << 1;		/* G_COMP0_U write status */ +			break; +		case EXYNOS4_MCT_G_COMP0_ADD_INCR: +			stat_addr = EXYNOS4_MCT_G_WSTAT; +			mask = 1 << 2;		/* G_COMP0_ADD_INCR w status */ +			break; +		case EXYNOS4_MCT_G_CNT_L: +			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; +			mask = 1 << 0;		/* G_CNT_L write status */ +			break; +		case EXYNOS4_MCT_G_CNT_U: +			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; +			mask = 1 << 1;		/* G_CNT_U write status */ +			break; +		default: +			return; +		} +	} + +	/* Wait maximum 1 ms until written values are applied */ +	for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++) +		if (__raw_readl(reg_base + stat_addr) & mask) { +			__raw_writel(mask, reg_base + stat_addr); +			return; +		} + +	panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset); +} + +/* Clocksource handling */ +static void exynos4_mct_frc_start(u32 hi, u32 lo) +{ +	u32 reg; + +	exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L); +	exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U); + +	reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); +	reg |= MCT_G_TCON_START; +	exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON); +} + +static cycle_t exynos4_frc_read(struct clocksource *cs) +{ +	unsigned int lo, hi; +	u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U); + +	do { +		hi = hi2; +		lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L); +		hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U); +	} while (hi != hi2); + +	return ((cycle_t)hi << 32) | lo; +} + +static void exynos4_frc_resume(struct clocksource *cs) +{ +	exynos4_mct_frc_start(0, 0); +} + +struct clocksource mct_frc = { +	.name		= "mct-frc", +	.rating		= 400, +	.read		= exynos4_frc_read, +	.mask		= CLOCKSOURCE_MASK(64), +	.flags		= CLOCK_SOURCE_IS_CONTINUOUS, +	.resume		= exynos4_frc_resume, +}; + +static void __init exynos4_clocksource_init(void) +{ +	exynos4_mct_frc_start(0, 0); + +	if (clocksource_register_hz(&mct_frc, clk_rate)) +		panic("%s: can't register clocksource\n", mct_frc.name); +} + +static void exynos4_mct_comp0_stop(void) +{ +	unsigned int tcon; + +	tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); +	tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC); + +	exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON); +	exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB); +} + +static void exynos4_mct_comp0_start(enum clock_event_mode mode, +				    unsigned long cycles) +{ +	unsigned int tcon; +	cycle_t comp_cycle; + +	tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON); + +	if (mode == CLOCK_EVT_MODE_PERIODIC) { +		tcon |= MCT_G_TCON_COMP0_AUTO_INC; +		exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR); +	} + +	comp_cycle = exynos4_frc_read(&mct_frc) + cycles; +	exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L); +	exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U); + +	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB); + +	tcon |= MCT_G_TCON_COMP0_ENABLE; +	exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON); +} + +static int exynos4_comp_set_next_event(unsigned long cycles, +				       struct clock_event_device *evt) +{ +	exynos4_mct_comp0_start(evt->mode, cycles); + +	return 0; +} + +static void exynos4_comp_set_mode(enum clock_event_mode mode, +				  struct clock_event_device *evt) +{ +	unsigned long cycles_per_jiffy; +	exynos4_mct_comp0_stop(); + +	switch (mode) { +	case CLOCK_EVT_MODE_PERIODIC: +		cycles_per_jiffy = +			(((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); +		exynos4_mct_comp0_start(mode, cycles_per_jiffy); +		break; + +	case CLOCK_EVT_MODE_ONESHOT: +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_SHUTDOWN: +	case CLOCK_EVT_MODE_RESUME: +		break; +	} +} + +static struct clock_event_device mct_comp_device = { +	.name		= "mct-comp", +	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, +	.rating		= 250, +	.set_next_event	= exynos4_comp_set_next_event, +	.set_mode	= exynos4_comp_set_mode, +}; + +static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id) +{ +	struct clock_event_device *evt = dev_id; + +	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT); + +	evt->event_handler(evt); + +	return IRQ_HANDLED; +} + +static struct irqaction mct_comp_event_irq = { +	.name		= "mct_comp_irq", +	.flags		= IRQF_TIMER | IRQF_IRQPOLL, +	.handler	= exynos4_mct_comp_isr, +	.dev_id		= &mct_comp_device, +}; + +static void exynos4_clockevent_init(void) +{ +	mct_comp_device.cpumask = cpumask_of(0); +	clockevents_config_and_register(&mct_comp_device, clk_rate, +					0xf, 0xffffffff); +	setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq); +} + +#ifdef CONFIG_LOCAL_TIMERS + +static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick); + +/* Clock event handling */ +static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt) +{ +	unsigned long tmp; +	unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START; +	unsigned long offset = mevt->base + MCT_L_TCON_OFFSET; + +	tmp = __raw_readl(reg_base + offset); +	if (tmp & mask) { +		tmp &= ~mask; +		exynos4_mct_write(tmp, offset); +	} +} + +static void exynos4_mct_tick_start(unsigned long cycles, +				   struct mct_clock_event_device *mevt) +{ +	unsigned long tmp; + +	exynos4_mct_tick_stop(mevt); + +	tmp = (1 << 31) | cycles;	/* MCT_L_UPDATE_ICNTB */ + +	/* update interrupt count buffer */ +	exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET); + +	/* enable MCT tick interrupt */ +	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET); + +	tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET); +	tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START | +	       MCT_L_TCON_INTERVAL_MODE; +	exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET); +} + +static int exynos4_tick_set_next_event(unsigned long cycles, +				       struct clock_event_device *evt) +{ +	struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); + +	exynos4_mct_tick_start(cycles, mevt); + +	return 0; +} + +static inline void exynos4_tick_set_mode(enum clock_event_mode mode, +					 struct clock_event_device *evt) +{ +	struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); +	unsigned long cycles_per_jiffy; + +	exynos4_mct_tick_stop(mevt); + +	switch (mode) { +	case CLOCK_EVT_MODE_PERIODIC: +		cycles_per_jiffy = +			(((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); +		exynos4_mct_tick_start(cycles_per_jiffy, mevt); +		break; + +	case CLOCK_EVT_MODE_ONESHOT: +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_SHUTDOWN: +	case CLOCK_EVT_MODE_RESUME: +		break; +	} +} + +static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) +{ +	struct clock_event_device *evt = mevt->evt; + +	/* +	 * This is for supporting oneshot mode. +	 * Mct would generate interrupt periodically +	 * without explicit stopping. +	 */ +	if (evt->mode != CLOCK_EVT_MODE_PERIODIC) +		exynos4_mct_tick_stop(mevt); + +	/* Clear the MCT tick interrupt */ +	if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) { +		exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); +		return 1; +	} else { +		return 0; +	} +} + +static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) +{ +	struct mct_clock_event_device *mevt = dev_id; +	struct clock_event_device *evt = mevt->evt; + +	exynos4_mct_tick_clear(mevt); + +	evt->event_handler(evt); + +	return IRQ_HANDLED; +} + +static struct irqaction mct_tick0_event_irq = { +	.name		= "mct_tick0_irq", +	.flags		= IRQF_TIMER | IRQF_NOBALANCING, +	.handler	= exynos4_mct_tick_isr, +}; + +static struct irqaction mct_tick1_event_irq = { +	.name		= "mct_tick1_irq", +	.flags		= IRQF_TIMER | IRQF_NOBALANCING, +	.handler	= exynos4_mct_tick_isr, +}; + +static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt) +{ +	struct mct_clock_event_device *mevt; +	unsigned int cpu = smp_processor_id(); + +	mevt = this_cpu_ptr(&percpu_mct_tick); +	mevt->evt = evt; + +	mevt->base = EXYNOS4_MCT_L_BASE(cpu); +	sprintf(mevt->name, "mct_tick%d", cpu); + +	evt->name = mevt->name; +	evt->cpumask = cpumask_of(cpu); +	evt->set_next_event = exynos4_tick_set_next_event; +	evt->set_mode = exynos4_tick_set_mode; +	evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; +	evt->rating = 450; +	clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1), +					0xf, 0x7fffffff); + +	exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); + +	if (mct_int_type == MCT_INT_SPI) { +		if (cpu == 0) { +			mct_tick0_event_irq.dev_id = mevt; +			evt->irq = mct_irqs[MCT_L0_IRQ]; +			setup_irq(evt->irq, &mct_tick0_event_irq); +		} else { +			mct_tick1_event_irq.dev_id = mevt; +			evt->irq = mct_irqs[MCT_L1_IRQ]; +			setup_irq(evt->irq, &mct_tick1_event_irq); +			irq_set_affinity(evt->irq, cpumask_of(1)); +		} +	} else { +		enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); +	} + +	return 0; +} + +static void exynos4_local_timer_stop(struct clock_event_device *evt) +{ +	unsigned int cpu = smp_processor_id(); +	evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); +	if (mct_int_type == MCT_INT_SPI) +		if (cpu == 0) +			remove_irq(evt->irq, &mct_tick0_event_irq); +		else +			remove_irq(evt->irq, &mct_tick1_event_irq); +	else +		disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); +} + +static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = { +	.setup	= exynos4_local_timer_setup, +	.stop	= exynos4_local_timer_stop, +}; +#endif /* CONFIG_LOCAL_TIMERS */ + +static void __init exynos4_timer_resources(void __iomem *base) +{ +	struct clk *mct_clk; +	mct_clk = clk_get(NULL, "xtal"); + +	clk_rate = clk_get_rate(mct_clk); + +	reg_base = base; +	if (!reg_base) +		panic("%s: unable to ioremap mct address space\n", __func__); + +#ifdef CONFIG_LOCAL_TIMERS +	if (mct_int_type == MCT_INT_PPI) { +		int err; + +		err = request_percpu_irq(mct_irqs[MCT_L0_IRQ], +					 exynos4_mct_tick_isr, "MCT", +					 &percpu_mct_tick); +		WARN(err, "MCT: can't request IRQ %d (%d)\n", +		     mct_irqs[MCT_L0_IRQ], err); +	} + +	local_timer_register(&exynos4_mct_tick_ops); +#endif /* CONFIG_LOCAL_TIMERS */ +} + +void __init mct_init(void) +{ +	if (soc_is_exynos4210()) { +		mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0; +		mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0; +		mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1; +		mct_int_type = MCT_INT_SPI; +	} else { +		panic("unable to determine mct controller type\n"); +	} + +	exynos4_timer_resources(S5P_VA_SYSTIMER); +	exynos4_clocksource_init(); +	exynos4_clockevent_init(); +} + +static void __init mct_init_dt(struct device_node *np, unsigned int int_type) +{ +	u32 nr_irqs, i; + +	mct_int_type = int_type; + +	/* This driver uses only one global timer interrupt */ +	mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ); + +	/* +	 * Find out the number of local irqs specified. The local +	 * timer irqs are specified after the four global timer +	 * irqs are specified. +	 */ +	nr_irqs = of_irq_count(np); +	for (i = MCT_L0_IRQ; i < nr_irqs; i++) +		mct_irqs[i] = irq_of_parse_and_map(np, i); + +	exynos4_timer_resources(of_iomap(np, 0)); +	exynos4_clocksource_init(); +	exynos4_clockevent_init(); +} + + +static void __init mct_init_spi(struct device_node *np) +{ +	return mct_init_dt(np, MCT_INT_SPI); +} + +static void __init mct_init_ppi(struct device_node *np) +{ +	return mct_init_dt(np, MCT_INT_PPI); +} +CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi); +CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi); diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 488c14cc8db..08d0c418c94 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -54,62 +54,100 @@ struct sh_cmt_priv {  	struct clocksource cs;  	unsigned long total_cycles;  	bool cs_enabled; + +	/* callbacks for CMSTR and CMCSR access */ +	unsigned long (*read_control)(void __iomem *base, unsigned long offs); +	void (*write_control)(void __iomem *base, unsigned long offs, +			      unsigned long value); + +	/* callbacks for CMCNT and CMCOR access */ +	unsigned long (*read_count)(void __iomem *base, unsigned long offs); +	void (*write_count)(void __iomem *base, unsigned long offs, +			    unsigned long value);  }; -static DEFINE_RAW_SPINLOCK(sh_cmt_lock); +/* Examples of supported CMT timer register layouts and I/O access widths: + * + * "16-bit counter and 16-bit control" as found on sh7263: + * CMSTR 0xfffec000 16-bit + * CMCSR 0xfffec002 16-bit + * CMCNT 0xfffec004 16-bit + * CMCOR 0xfffec006 16-bit + * + * "32-bit counter and 16-bit control" as found on sh7372, sh73a0, r8a7740: + * CMSTR 0xffca0000 16-bit + * CMCSR 0xffca0060 16-bit + * CMCNT 0xffca0064 32-bit + * CMCOR 0xffca0068 32-bit + */ + +static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) +{ +	return ioread16(base + (offs << 1)); +} + +static unsigned long sh_cmt_read32(void __iomem *base, unsigned long offs) +{ +	return ioread32(base + (offs << 2)); +} + +static void sh_cmt_write16(void __iomem *base, unsigned long offs, +			   unsigned long value) +{ +	iowrite16(value, base + (offs << 1)); +} + +static void sh_cmt_write32(void __iomem *base, unsigned long offs, +			   unsigned long value) +{ +	iowrite32(value, base + (offs << 2)); +} -#define CMSTR -1 /* shared register */  #define CMCSR 0 /* channel register */  #define CMCNT 1 /* channel register */  #define CMCOR 2 /* channel register */ -static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) +static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p)  {  	struct sh_timer_config *cfg = p->pdev->dev.platform_data; -	void __iomem *base = p->mapbase; -	unsigned long offs; -	if (reg_nr == CMSTR) { -		offs = 0; -		base -= cfg->channel_offset; -	} else -		offs = reg_nr; +	return p->read_control(p->mapbase - cfg->channel_offset, 0); +} -	if (p->width == 16) -		offs <<= 1; -	else { -		offs <<= 2; -		if ((reg_nr == CMCNT) || (reg_nr == CMCOR)) -			return ioread32(base + offs); -	} +static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) +{ +	return p->read_control(p->mapbase, CMCSR); +} -	return ioread16(base + offs); +static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) +{ +	return p->read_count(p->mapbase, CMCNT);  } -static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, -				unsigned long value) +static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, +				      unsigned long value)  {  	struct sh_timer_config *cfg = p->pdev->dev.platform_data; -	void __iomem *base = p->mapbase; -	unsigned long offs; -	if (reg_nr == CMSTR) { -		offs = 0; -		base -= cfg->channel_offset; -	} else -		offs = reg_nr; +	p->write_control(p->mapbase - cfg->channel_offset, 0, value); +} -	if (p->width == 16) -		offs <<= 1; -	else { -		offs <<= 2; -		if ((reg_nr == CMCNT) || (reg_nr == CMCOR)) { -			iowrite32(value, base + offs); -			return; -		} -	} +static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, +				      unsigned long value) +{ +	p->write_control(p->mapbase, CMCSR, value); +} -	iowrite16(value, base + offs); +static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p, +				      unsigned long value) +{ +	p->write_count(p->mapbase, CMCNT, value); +} + +static inline void sh_cmt_write_cmcor(struct sh_cmt_priv *p, +				      unsigned long value) +{ +	p->write_count(p->mapbase, CMCOR, value);  }  static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, @@ -118,15 +156,15 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,  	unsigned long v1, v2, v3;  	int o1, o2; -	o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit; +	o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit;  	/* Make sure the timer value is stable. Stolen from acpi_pm.c */  	do {  		o2 = o1; -		v1 = sh_cmt_read(p, CMCNT); -		v2 = sh_cmt_read(p, CMCNT); -		v3 = sh_cmt_read(p, CMCNT); -		o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit; +		v1 = sh_cmt_read_cmcnt(p); +		v2 = sh_cmt_read_cmcnt(p); +		v3 = sh_cmt_read_cmcnt(p); +		o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit;  	} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)  			  || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); @@ -134,6 +172,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,  	return v2;  } +static DEFINE_RAW_SPINLOCK(sh_cmt_lock);  static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)  { @@ -142,14 +181,14 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)  	/* start stop register shared by multiple timer channels */  	raw_spin_lock_irqsave(&sh_cmt_lock, flags); -	value = sh_cmt_read(p, CMSTR); +	value = sh_cmt_read_cmstr(p);  	if (start)  		value |= 1 << cfg->timer_bit;  	else  		value &= ~(1 << cfg->timer_bit); -	sh_cmt_write(p, CMSTR, value); +	sh_cmt_write_cmstr(p, value);  	raw_spin_unlock_irqrestore(&sh_cmt_lock, flags);  } @@ -173,14 +212,14 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)  	/* configure channel, periodic mode and maximum timeout */  	if (p->width == 16) {  		*rate = clk_get_rate(p->clk) / 512; -		sh_cmt_write(p, CMCSR, 0x43); +		sh_cmt_write_cmcsr(p, 0x43);  	} else {  		*rate = clk_get_rate(p->clk) / 8; -		sh_cmt_write(p, CMCSR, 0x01a4); +		sh_cmt_write_cmcsr(p, 0x01a4);  	} -	sh_cmt_write(p, CMCOR, 0xffffffff); -	sh_cmt_write(p, CMCNT, 0); +	sh_cmt_write_cmcor(p, 0xffffffff); +	sh_cmt_write_cmcnt(p, 0);  	/*  	 * According to the sh73a0 user's manual, as CMCNT can be operated @@ -194,12 +233,12 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)  	 * take RCLKx2 at maximum.  	 */  	for (k = 0; k < 100; k++) { -		if (!sh_cmt_read(p, CMCNT)) +		if (!sh_cmt_read_cmcnt(p))  			break;  		udelay(1);  	} -	if (sh_cmt_read(p, CMCNT)) { +	if (sh_cmt_read_cmcnt(p)) {  		dev_err(&p->pdev->dev, "cannot clear CMCNT\n");  		ret = -ETIMEDOUT;  		goto err1; @@ -222,7 +261,7 @@ static void sh_cmt_disable(struct sh_cmt_priv *p)  	sh_cmt_start_stop_ch(p, 0);  	/* disable interrupts in CMT block */ -	sh_cmt_write(p, CMCSR, 0); +	sh_cmt_write_cmcsr(p, 0);  	/* stop clock */  	clk_disable(p->clk); @@ -270,7 +309,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,  		if (new_match > p->max_match_value)  			new_match = p->max_match_value; -		sh_cmt_write(p, CMCOR, new_match); +		sh_cmt_write_cmcor(p, new_match);  		now = sh_cmt_get_counter(p, &has_wrapped);  		if (has_wrapped && (new_match > p->match_value)) { @@ -346,7 +385,7 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)  	struct sh_cmt_priv *p = dev_id;  	/* clear flags */ -	sh_cmt_write(p, CMCSR, sh_cmt_read(p, CMCSR) & p->clear_bits); +	sh_cmt_write_cmcsr(p, sh_cmt_read_cmcsr(p) & p->clear_bits);  	/* update clock source counter to begin with if enabled  	 * the wrap flag should be cleared by the timer specific @@ -625,14 +664,6 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name,  			   unsigned long clockevent_rating,  			   unsigned long clocksource_rating)  { -	if (p->width == (sizeof(p->max_match_value) * 8)) -		p->max_match_value = ~0; -	else -		p->max_match_value = (1 << p->width) - 1; - -	p->match_value = p->max_match_value; -	raw_spin_lock_init(&p->lock); -  	if (clockevent_rating)  		sh_cmt_register_clockevent(p, name, clockevent_rating); @@ -657,8 +688,6 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)  		goto err0;  	} -	platform_set_drvdata(pdev, p); -  	res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);  	if (!res) {  		dev_err(&p->pdev->dev, "failed to get I/O memory\n"); @@ -693,32 +722,51 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)  		goto err1;  	} +	p->read_control = sh_cmt_read16; +	p->write_control = sh_cmt_write16; +  	if (resource_size(res) == 6) {  		p->width = 16; +		p->read_count = sh_cmt_read16; +		p->write_count = sh_cmt_write16;  		p->overflow_bit = 0x80;  		p->clear_bits = ~0x80;  	} else {  		p->width = 32; +		p->read_count = sh_cmt_read32; +		p->write_count = sh_cmt_write32;  		p->overflow_bit = 0x8000;  		p->clear_bits = ~0xc000;  	} +	if (p->width == (sizeof(p->max_match_value) * 8)) +		p->max_match_value = ~0; +	else +		p->max_match_value = (1 << p->width) - 1; + +	p->match_value = p->max_match_value; +	raw_spin_lock_init(&p->lock); +  	ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev),  			      cfg->clockevent_rating,  			      cfg->clocksource_rating);  	if (ret) {  		dev_err(&p->pdev->dev, "registration failed\n"); -		goto err1; +		goto err2;  	}  	p->cs_enabled = false;  	ret = setup_irq(irq, &p->irqaction);  	if (ret) {  		dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); -		goto err1; +		goto err2;  	} +	platform_set_drvdata(pdev, p); +  	return 0; +err2: +	clk_put(p->clk);  err1:  	iounmap(p->mapbase); @@ -751,7 +799,6 @@ static int sh_cmt_probe(struct platform_device *pdev)  	ret = sh_cmt_setup(p, pdev);  	if (ret) {  		kfree(p); -		platform_set_drvdata(pdev, NULL);  		pm_runtime_idle(&pdev->dev);  		return ret;  	} @@ -791,7 +838,7 @@ static void __exit sh_cmt_exit(void)  }  early_platform_init("earlytimer", &sh_cmt_device_driver); -module_init(sh_cmt_init); +subsys_initcall(sh_cmt_init);  module_exit(sh_cmt_exit);  MODULE_AUTHOR("Magnus Damm"); diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 83943e27cfa..4aac9ee0d0c 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -386,7 +386,7 @@ static void __exit sh_mtu2_exit(void)  }  early_platform_init("earlytimer", &sh_mtu2_device_driver); -module_init(sh_mtu2_init); +subsys_initcall(sh_mtu2_init);  module_exit(sh_mtu2_exit);  MODULE_AUTHOR("Magnus Damm"); diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index b4502edce2a..78b8dae4962 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -549,7 +549,7 @@ static void __exit sh_tmu_exit(void)  }  early_platform_init("earlytimer", &sh_tmu_device_driver); -module_init(sh_tmu_init); +subsys_initcall(sh_tmu_init);  module_exit(sh_tmu_exit);  MODULE_AUTHOR("Magnus Damm"); diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 4086b916715..0ce85e29769 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -23,7 +23,7 @@  #include <linux/of_address.h>  #include <linux/of_irq.h>  #include <linux/sunxi_timer.h> -#include <linux/clk-provider.h> +#include <linux/clk/sunxi.h>  #define TIMER_CTL_REG		0x00  #define TIMER_CTL_ENABLE		(1 << 0) @@ -123,7 +123,7 @@ void __init sunxi_timer_init(void)  	if (irq <= 0)  		panic("Can't parse IRQ"); -	of_clk_init(NULL); +	sunxi_init_clocks();  	clk = of_clk_get(node, 0);  	if (IS_ERR(clk)) diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 2e4d8a666c3..ae877b021b5 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -172,7 +172,7 @@ static void __init tegra20_init_timer(struct device_node *np)  		BUG();  	} -	clk = clk_get_sys("timer", NULL); +	clk = of_clk_get(np, 0);  	if (IS_ERR(clk)) {  		pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n");  		rate = 12000000; @@ -235,7 +235,7 @@ static void __init tegra20_init_rtc(struct device_node *np)  	 * rtc registers are used by read_persistent_clock, keep the rtc clock  	 * enabled  	 */ -	clk = clk_get_sys("rtc-tegra", NULL); +	clk = of_clk_get(np, 0);  	if (IS_ERR(clk))  		pr_warn("Unable to get rtc-tegra clock\n");  	else diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 937bc286591..57a8774f0b4 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -730,7 +730,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)  	    policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {  		cpumask_copy(policy->cpus, perf->shared_cpu_map);  	} -	cpumask_copy(policy->related_cpus, perf->shared_cpu_map);  #ifdef CONFIG_SMP  	dmi_check_system(sw_any_bug_dmi_table); @@ -742,7 +741,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)  	if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) {  		cpumask_clear(policy->cpus);  		cpumask_set_cpu(cpu, policy->cpus); -		cpumask_copy(policy->related_cpus, cpu_sibling_mask(cpu));  		policy->shared_type = CPUFREQ_SHARED_TYPE_HW;  		pr_info_once(PFX "overriding BIOS provided _PSD data\n");  	} diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 2fd779eb1ed..bfd6273fd87 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -180,15 +180,19 @@ static void cpufreq_stats_free_sysfs(unsigned int cpu)  {  	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); -	if (!cpufreq_frequency_get_table(cpu)) +	if (!policy)  		return; -	if (policy && !policy_is_shared(policy)) { +	if (!cpufreq_frequency_get_table(cpu)) +		goto put_ref; + +	if (!policy_is_shared(policy)) {  		pr_debug("%s: Free sysfs stat\n", __func__);  		sysfs_remove_group(&policy->kobj, &stats_attr_group);  	} -	if (policy) -		cpufreq_cpu_put(policy); + +put_ref: +	cpufreq_cpu_put(policy);  }  static int cpufreq_stats_create_table(struct cpufreq_policy *policy, diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index f6dd1e76112..ad72922919e 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -358,14 +358,14 @@ static void intel_pstate_sysfs_expose_params(void)  static int intel_pstate_min_pstate(void)  {  	u64 value; -	rdmsrl(0xCE, value); +	rdmsrl(MSR_PLATFORM_INFO, value);  	return (value >> 40) & 0xFF;  }  static int intel_pstate_max_pstate(void)  {  	u64 value; -	rdmsrl(0xCE, value); +	rdmsrl(MSR_PLATFORM_INFO, value);  	return (value >> 8) & 0xFF;  } @@ -373,7 +373,7 @@ static int intel_pstate_turbo_pstate(void)  {  	u64 value;  	int nont, ret; -	rdmsrl(0x1AD, value); +	rdmsrl(MSR_NHM_TURBO_RATIO_LIMIT, value);  	nont = intel_pstate_max_pstate();  	ret = ((value) & 255);  	if (ret <= nont) @@ -454,7 +454,7 @@ static inline void intel_pstate_calc_busy(struct cpudata *cpu,  					sample->idletime_us * 100,  					sample->duration_us);  	core_pct = div64_u64(sample->aperf * 100, sample->mperf); -	sample->freq = cpu->pstate.turbo_pstate * core_pct * 1000; +	sample->freq = cpu->pstate.max_pstate * core_pct * 1000;  	sample->core_pct_busy = div_s64((sample->pstate_pct_busy * core_pct),  					100); @@ -752,6 +752,29 @@ static struct cpufreq_driver intel_pstate_driver = {  static int __initdata no_load; +static int intel_pstate_msrs_not_valid(void) +{ +	/* Check that all the msr's we are using are valid. */ +	u64 aperf, mperf, tmp; + +	rdmsrl(MSR_IA32_APERF, aperf); +	rdmsrl(MSR_IA32_MPERF, mperf); + +	if (!intel_pstate_min_pstate() || +		!intel_pstate_max_pstate() || +		!intel_pstate_turbo_pstate()) +		return -ENODEV; + +	rdmsrl(MSR_IA32_APERF, tmp); +	if (!(tmp - aperf)) +		return -ENODEV; + +	rdmsrl(MSR_IA32_MPERF, tmp); +	if (!(tmp - mperf)) +		return -ENODEV; + +	return 0; +}  static int __init intel_pstate_init(void)  {  	int cpu, rc = 0; @@ -764,6 +787,9 @@ static int __init intel_pstate_init(void)  	if (!id)  		return -ENODEV; +	if (intel_pstate_msrs_not_valid()) +		return -ENODEV; +  	pr_info("Intel P-state driver initializing.\n");  	all_cpu_data = vmalloc(sizeof(void *) * num_possible_cpus()); diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index b2a0a0726a5..cf268b14ae9 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -1650,11 +1650,7 @@ struct caam_alg_template {  };  static struct caam_alg_template driver_algs[] = { -	/* -	 * single-pass ipsec_esp descriptor -	 * authencesn(*,*) is also registered, although not present -	 * explicitly here. -	 */ +	/* single-pass ipsec_esp descriptor */  	{  		.name = "authenc(hmac(md5),cbc(aes))",  		.driver_name = "authenc-hmac-md5-cbc-aes-caam", @@ -2217,9 +2213,7 @@ static int __init caam_algapi_init(void)  	for (i = 0; i < ARRAY_SIZE(driver_algs); i++) {  		/* TODO: check if h/w supports alg */  		struct caam_crypto_alg *t_alg; -		bool done = false; -authencesn:  		t_alg = caam_alg_alloc(ctrldev, &driver_algs[i]);  		if (IS_ERR(t_alg)) {  			err = PTR_ERR(t_alg); @@ -2233,25 +2227,8 @@ authencesn:  			dev_warn(ctrldev, "%s alg registration failed\n",  				t_alg->crypto_alg.cra_driver_name);  			kfree(t_alg); -		} else { +		} else  			list_add_tail(&t_alg->entry, &priv->alg_list); -			if (driver_algs[i].type == CRYPTO_ALG_TYPE_AEAD && -			    !memcmp(driver_algs[i].name, "authenc", 7) && -			    !done) { -				char *name; - -				name = driver_algs[i].name; -				memmove(name + 10, name + 7, strlen(name) - 7); -				memcpy(name + 7, "esn", 3); - -				name = driver_algs[i].driver_name; -				memmove(name + 10, name + 7, strlen(name) - 7); -				memcpy(name + 7, "esn", 3); - -				done = true; -				goto authencesn; -			} -		}  	}  	if (!list_empty(&priv->alg_list))  		dev_info(ctrldev, "%s algorithms registered in /proc/crypto\n", diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h index cf15e781380..762aeff626a 100644 --- a/drivers/crypto/caam/compat.h +++ b/drivers/crypto/caam/compat.h @@ -23,7 +23,6 @@  #include <linux/types.h>  #include <linux/debugfs.h>  #include <linux/circ_buf.h> -#include <linux/string.h>  #include <net/xfrm.h>  #include <crypto/algapi.h> diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 09b184adf31..5b2b5e61e4f 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -38,7 +38,6 @@  #include <linux/spinlock.h>  #include <linux/rtnetlink.h>  #include <linux/slab.h> -#include <linux/string.h>  #include <crypto/algapi.h>  #include <crypto/aes.h> @@ -1974,11 +1973,7 @@ struct talitos_alg_template {  };  static struct talitos_alg_template driver_algs[] = { -	/* -	 * AEAD algorithms. These use a single-pass ipsec_esp descriptor. -	 * authencesn(*,*) is also registered, although not present -	 * explicitly here. -	 */ +	/* AEAD algorithms.  These use a single-pass ipsec_esp descriptor */  	{	.type = CRYPTO_ALG_TYPE_AEAD,  		.alg.crypto = {  			.cra_name = "authenc(hmac(sha1),cbc(aes))", @@ -2820,9 +2815,7 @@ static int talitos_probe(struct platform_device *ofdev)  		if (hw_supports(dev, driver_algs[i].desc_hdr_template)) {  			struct talitos_crypto_alg *t_alg;  			char *name = NULL; -			bool authenc = false; -authencesn:  			t_alg = talitos_alg_alloc(dev, &driver_algs[i]);  			if (IS_ERR(t_alg)) {  				err = PTR_ERR(t_alg); @@ -2837,8 +2830,6 @@ authencesn:  				err = crypto_register_alg(  						&t_alg->algt.alg.crypto);  				name = t_alg->algt.alg.crypto.cra_driver_name; -				authenc = authenc ? !authenc : -					  !(bool)memcmp(name, "authenc", 7);  				break;  			case CRYPTO_ALG_TYPE_AHASH:  				err = crypto_register_ahash( @@ -2851,25 +2842,8 @@ authencesn:  				dev_err(dev, "%s alg registration failed\n",  					name);  				kfree(t_alg); -			} else { +			} else  				list_add_tail(&t_alg->entry, &priv->alg_list); -				if (authenc) { -					struct crypto_alg *alg = -						&driver_algs[i].alg.crypto; - -					name = alg->cra_name; -					memmove(name + 10, name + 7, -						strlen(name) - 7); -					memcpy(name + 7, "esn", 3); - -					name = alg->cra_driver_name; -					memmove(name + 10, name + 7, -						strlen(name) - 7); -					memcpy(name + 7, "esn", 3); - -					goto authencesn; -				} -			}  		}  	}  	if (!list_empty(&priv->alg_list)) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c599558faed..43a5329d448 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1001,6 +1001,13 @@ static inline void convert_burst(u32 *maxburst)  		*maxburst = 0;  } +static inline void convert_slave_id(struct dw_dma_chan *dwc) +{ +	struct dw_dma *dw = to_dw_dma(dwc->chan.device); + +	dwc->dma_sconfig.slave_id -= dw->request_line_base; +} +  static int  set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)  { @@ -1015,6 +1022,7 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)  	convert_burst(&dwc->dma_sconfig.src_maxburst);  	convert_burst(&dwc->dma_sconfig.dst_maxburst); +	convert_slave_id(dwc);  	return 0;  } @@ -1276,9 +1284,9 @@ static struct dma_chan *dw_dma_xlate(struct of_phandle_args *dma_spec,  	if (dma_spec->args_count != 3)  		return NULL; -	fargs.req = be32_to_cpup(dma_spec->args+0); -	fargs.src = be32_to_cpup(dma_spec->args+1); -	fargs.dst = be32_to_cpup(dma_spec->args+2); +	fargs.req = dma_spec->args[0]; +	fargs.src = dma_spec->args[1]; +	fargs.dst = dma_spec->args[2];  	if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS ||  		    fargs.src >= dw->nr_masters || @@ -1628,6 +1636,7 @@ dw_dma_parse_dt(struct platform_device *pdev)  static int dw_probe(struct platform_device *pdev)  { +	const struct platform_device_id *match;  	struct dw_dma_platform_data *pdata;  	struct resource		*io;  	struct dw_dma		*dw; @@ -1711,6 +1720,11 @@ static int dw_probe(struct platform_device *pdev)  		memcpy(dw->data_width, pdata->data_width, 4);  	} +	/* Get the base request line if set */ +	match = platform_get_device_id(pdev); +	if (match) +		dw->request_line_base = (unsigned int)match->driver_data; +  	/* Calculate all channel mask before DMA setup */  	dw->all_chan_mask = (1 << nr_channels) - 1; @@ -1906,7 +1920,8 @@ MODULE_DEVICE_TABLE(of, dw_dma_id_table);  #endif  static const struct platform_device_id dw_dma_ids[] = { -	{ "INTL9C60", 0 }, +	/* Name,	Request Line Base */ +	{ "INTL9C60",	(kernel_ulong_t)16 },  	{ }  }; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index cf0ce5c77d6..4d02c3669b7 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -247,6 +247,7 @@ struct dw_dma {  	/* hardware configuration */  	unsigned char		nr_masters;  	unsigned char		data_width[4]; +	unsigned int		request_line_base;  	struct dw_dma_chan	chan[0];  }; diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 910b0116c12..e1d13c463c9 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2048,12 +2048,18 @@ static int init_csrows(struct mem_ctl_info *mci)  		edac_dbg(1, "MC node: %d, csrow: %d\n",  			    pvt->mc_node_id, i); -		if (row_dct0) +		if (row_dct0) {  			nr_pages = amd64_csrow_nr_pages(pvt, 0, i); +			csrow->channels[0]->dimm->nr_pages = nr_pages; +		}  		/* K8 has only one DCT */ -		if (boot_cpu_data.x86 != 0xf && row_dct1) -			nr_pages += amd64_csrow_nr_pages(pvt, 1, i); +		if (boot_cpu_data.x86 != 0xf && row_dct1) { +			int row_dct1_pages = amd64_csrow_nr_pages(pvt, 1, i); + +			csrow->channels[1]->dimm->nr_pages = row_dct1_pages; +			nr_pages += row_dct1_pages; +		}  		mtype = amd64_determine_memory_type(pvt, i); @@ -2072,9 +2078,7 @@ static int init_csrows(struct mem_ctl_info *mci)  			dimm = csrow->channels[j]->dimm;  			dimm->mtype = mtype;  			dimm->edac_mode = edac_mode; -			dimm->nr_pages = nr_pages;  		} -		csrow->nr_pages = nr_pages;  	}  	return empty; @@ -2419,7 +2423,6 @@ static int amd64_init_one_instance(struct pci_dev *F2)  	mci->pvt_info = pvt;  	mci->pdev = &pvt->F2->dev; -	mci->csbased = 1;  	setup_mci_misc_attrs(mci, fam_type); diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index cdb81aa73ab..27e86d93826 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -86,7 +86,7 @@ static void edac_mc_dump_dimm(struct dimm_info *dimm, int number)  	edac_dimm_info_location(dimm, location, sizeof(location));  	edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n", -		 dimm->mci->mem_is_per_rank ? "rank" : "dimm", +		 dimm->mci->csbased ? "rank" : "dimm",  		 number, location, dimm->csrow, dimm->cschannel);  	edac_dbg(4, "  dimm = %p\n", dimm);  	edac_dbg(4, "  dimm->label = '%s'\n", dimm->label); @@ -341,7 +341,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,  	memcpy(mci->layers, layers, sizeof(*layer) * n_layers);  	mci->nr_csrows = tot_csrows;  	mci->num_cschannel = tot_channels; -	mci->mem_is_per_rank = per_rank; +	mci->csbased = per_rank;  	/*  	 * Alocate and fill the csrow/channels structs @@ -1235,7 +1235,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,  			 * incrementing the compat API counters  			 */  			edac_dbg(4, "%s csrows map: (%d,%d)\n", -				 mci->mem_is_per_rank ? "rank" : "dimm", +				 mci->csbased ? "rank" : "dimm",  				 dimm->csrow, dimm->cschannel);  			if (row == -1)  				row = dimm->csrow; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 4f4b6137d74..5899a76eec3 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -143,7 +143,7 @@ static const char *edac_caps[] = {   * and the per-dimm/per-rank one   */  #define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \ -	struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store) +	static struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)  struct dev_ch_attribute {  	struct device_attribute attr; @@ -180,9 +180,6 @@ static ssize_t csrow_size_show(struct device *dev,  	int i;  	u32 nr_pages = 0; -	if (csrow->mci->csbased) -		return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); -  	for (i = 0; i < csrow->nr_channels; i++)  		nr_pages += csrow->channels[i]->dimm->nr_pages;  	return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); @@ -612,7 +609,7 @@ static int edac_create_dimm_object(struct mem_ctl_info *mci,  	device_initialize(&dimm->dev);  	dimm->dev.parent = &mci->dev; -	if (mci->mem_is_per_rank) +	if (mci->csbased)  		dev_set_name(&dimm->dev, "rank%d", index);  	else  		dev_set_name(&dimm->dev, "dimm%d", index); @@ -778,14 +775,10 @@ static ssize_t mci_size_mb_show(struct device *dev,  	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {  		struct csrow_info *csrow = mci->csrows[csrow_idx]; -		if (csrow->mci->csbased) { -			total_pages += csrow->nr_pages; -		} else { -			for (j = 0; j < csrow->nr_channels; j++) { -				struct dimm_info *dimm = csrow->channels[j]->dimm; +		for (j = 0; j < csrow->nr_channels; j++) { +			struct dimm_info *dimm = csrow->channels[j]->dimm; -				total_pages += dimm->nr_pages; -			} +			total_pages += dimm->nr_pages;  		}  	} diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index b70e3815c45..8f3c947b002 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -32,6 +32,38 @@  #define	DEV_NAME			"max77693-muic"  #define	DELAY_MS_DEFAULT		20000		/* unit: millisecond */ +/* + * Default value of MAX77693 register to bring up MUIC device. + * If user don't set some initial value for MUIC device through platform data, + * extcon-max77693 driver use 'default_init_data' to bring up base operation + * of MAX77693 MUIC device. + */ +struct max77693_reg_data default_init_data[] = { +	{ +		/* STATUS2 - [3]ChgDetRun */ +		.addr = MAX77693_MUIC_REG_STATUS2, +		.data = STATUS2_CHGDETRUN_MASK, +	}, { +		/* INTMASK1 - Unmask [3]ADC1KM,[0]ADCM */ +		.addr = MAX77693_MUIC_REG_INTMASK1, +		.data = INTMASK1_ADC1K_MASK +			| INTMASK1_ADC_MASK, +	}, { +		/* INTMASK2 - Unmask [0]ChgTypM */ +		.addr = MAX77693_MUIC_REG_INTMASK2, +		.data = INTMASK2_CHGTYP_MASK, +	}, { +		/* INTMASK3 - Mask all of interrupts */ +		.addr = MAX77693_MUIC_REG_INTMASK3, +		.data = 0x0, +	}, { +		/* CDETCTRL2 */ +		.addr = MAX77693_MUIC_REG_CDETCTRL2, +		.data = CDETCTRL2_VIDRMEN_MASK +			| CDETCTRL2_DXOVPEN_MASK, +	}, +}; +  enum max77693_muic_adc_debounce_time {  	ADC_DEBOUNCE_TIME_5MS = 0,  	ADC_DEBOUNCE_TIME_10MS, @@ -1045,8 +1077,9 @@ static int max77693_muic_probe(struct platform_device *pdev)  {  	struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);  	struct max77693_platform_data *pdata = dev_get_platdata(max77693->dev); -	struct max77693_muic_platform_data *muic_pdata = pdata->muic_data;  	struct max77693_muic_info *info; +	struct max77693_reg_data *init_data; +	int num_init_data;  	int delay_jiffies;  	int ret;  	int i; @@ -1145,15 +1178,25 @@ static int max77693_muic_probe(struct platform_device *pdev)  		goto err_irq;  	} -	/* Initialize MUIC register by using platform data */ -	for (i = 0 ; i < muic_pdata->num_init_data ; i++) { -		enum max77693_irq_source irq_src = MAX77693_IRQ_GROUP_NR; + +	/* Initialize MUIC register by using platform data or default data */ +	if (pdata->muic_data) { +		init_data = pdata->muic_data->init_data; +		num_init_data = pdata->muic_data->num_init_data; +	} else { +		init_data = default_init_data; +		num_init_data = ARRAY_SIZE(default_init_data); +	} + +	for (i = 0 ; i < num_init_data ; i++) { +		enum max77693_irq_source irq_src +				= MAX77693_IRQ_GROUP_NR;  		max77693_write_reg(info->max77693->regmap_muic, -				muic_pdata->init_data[i].addr, -				muic_pdata->init_data[i].data); +				init_data[i].addr, +				init_data[i].data); -		switch (muic_pdata->init_data[i].addr) { +		switch (init_data[i].addr) {  		case MAX77693_MUIC_REG_INTMASK1:  			irq_src = MUIC_INT1;  			break; @@ -1167,22 +1210,40 @@ static int max77693_muic_probe(struct platform_device *pdev)  		if (irq_src < MAX77693_IRQ_GROUP_NR)  			info->max77693->irq_masks_cur[irq_src] -				= muic_pdata->init_data[i].data; +				= init_data[i].data;  	} -	/* -	 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB -	 * h/w path of COMP2/COMN1 on CONTROL1 register. -	 */ -	if (muic_pdata->path_uart) -		info->path_uart = muic_pdata->path_uart; -	else -		info->path_uart = CONTROL1_SW_UART; +	if (pdata->muic_data) { +		struct max77693_muic_platform_data *muic_pdata = pdata->muic_data; -	if (muic_pdata->path_usb) -		info->path_usb = muic_pdata->path_usb; -	else +		/* +		 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB +		 * h/w path of COMP2/COMN1 on CONTROL1 register. +		 */ +		if (muic_pdata->path_uart) +			info->path_uart = muic_pdata->path_uart; +		else +			info->path_uart = CONTROL1_SW_UART; + +		if (muic_pdata->path_usb) +			info->path_usb = muic_pdata->path_usb; +		else +			info->path_usb = CONTROL1_SW_USB; + +		/* +		 * Default delay time for detecting cable state +		 * after certain time. +		 */ +		if (muic_pdata->detcable_delay_ms) +			delay_jiffies = +				msecs_to_jiffies(muic_pdata->detcable_delay_ms); +		else +			delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); +	} else {  		info->path_usb = CONTROL1_SW_USB; +		info->path_uart = CONTROL1_SW_UART; +		delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); +	}  	/* Set initial path for UART */  	 max77693_muic_set_path(info, info->path_uart, true); @@ -1208,10 +1269,6 @@ static int max77693_muic_probe(struct platform_device *pdev)  	 * driver should notify cable state to upper layer.  	 */  	INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq); -	if (muic_pdata->detcable_delay_ms) -		delay_jiffies = msecs_to_jiffies(muic_pdata->detcable_delay_ms); -	else -		delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT);  	schedule_delayed_work(&info->wq_detcable, delay_jiffies);  	return ret; diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index e636d950ad6..69641bcae32 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -712,29 +712,45 @@ static int max8997_muic_probe(struct platform_device *pdev)  		goto err_irq;  	} -	/* Initialize registers according to platform data */  	if (pdata->muic_pdata) { -		struct max8997_muic_platform_data *mdata = info->muic_pdata; +		struct max8997_muic_platform_data *muic_pdata +			= pdata->muic_pdata; -		for (i = 0; i < mdata->num_init_data; i++) { -			max8997_write_reg(info->muic, mdata->init_data[i].addr, -					mdata->init_data[i].data); +		/* Initialize registers according to platform data */ +		for (i = 0; i < muic_pdata->num_init_data; i++) { +			max8997_write_reg(info->muic, +					muic_pdata->init_data[i].addr, +					muic_pdata->init_data[i].data);  		} -	} -	/* -	 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB -	 * h/w path of COMP2/COMN1 on CONTROL1 register. -	 */ -	if (pdata->muic_pdata->path_uart) -		info->path_uart = pdata->muic_pdata->path_uart; -	else -		info->path_uart = CONTROL1_SW_UART; +		/* +		 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB +		 * h/w path of COMP2/COMN1 on CONTROL1 register. +		 */ +		if (muic_pdata->path_uart) +			info->path_uart = muic_pdata->path_uart; +		else +			info->path_uart = CONTROL1_SW_UART; -	if (pdata->muic_pdata->path_usb) -		info->path_usb = pdata->muic_pdata->path_usb; -	else +		if (muic_pdata->path_usb) +			info->path_usb = muic_pdata->path_usb; +		else +			info->path_usb = CONTROL1_SW_USB; + +		/* +		 * Default delay time for detecting cable state +		 * after certain time. +		 */ +		if (muic_pdata->detcable_delay_ms) +			delay_jiffies = +				msecs_to_jiffies(muic_pdata->detcable_delay_ms); +		else +			delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); +	} else { +		info->path_uart = CONTROL1_SW_UART;  		info->path_usb = CONTROL1_SW_USB; +		delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); +	}  	/* Set initial path for UART */  	 max8997_muic_set_path(info, info->path_uart, true); @@ -751,10 +767,6 @@ static int max8997_muic_probe(struct platform_device *pdev)  	 * driver should notify cable state to upper layer.  	 */  	INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq); -	if (pdata->muic_pdata->detcable_delay_ms) -		delay_jiffies = msecs_to_jiffies(pdata->muic_pdata->detcable_delay_ms); -	else -		delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT);  	schedule_delayed_work(&info->wq_detcable, delay_jiffies);  	return 0; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 9b00072a020..42c759a4d04 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -53,6 +53,24 @@ config EFI_VARS  	  Subsequent efibootmgr releases may be found at:  	  <http://linux.dell.com/efibootmgr> +config EFI_VARS_PSTORE +	bool "Register efivars backend for pstore" +	depends on EFI_VARS && PSTORE +	default y +	help +	  Say Y here to enable use efivars as a backend to pstore. This +	  will allow writing console messages, crash dumps, or anything +	  else supported by pstore to EFI variables. + +config EFI_VARS_PSTORE_DEFAULT_DISABLE +	bool "Disable using efivars as a pstore backend by default" +	depends on EFI_VARS_PSTORE +	default n +	help +	  Saying Y here will disable the use of efivars as a storage +	  backend for pstore by default. This setting can be overridden +	  using the efivars module's pstore_disable parameter. +  config EFI_PCDP  	bool "Console device selection via EFI PCDP or HCDP table"  	depends on ACPI && EFI && IA64 diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index fe62aa39223..7acafb80fd4 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -103,6 +103,11 @@ MODULE_VERSION(EFIVARS_VERSION);   */  #define GUID_LEN 36 +static bool efivars_pstore_disable = +	IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); + +module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); +  /*   * The maximum size of VariableName + Data = 1024   * Therefore, it's reasonable to save that much @@ -165,6 +170,7 @@ efivar_create_sysfs_entry(struct efivars *efivars,  static void efivar_update_sysfs_entries(struct work_struct *);  static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); +static bool efivar_wq_enabled = true;  /* Return the number of unicode characters in data */  static unsigned long @@ -1309,9 +1315,7 @@ static const struct inode_operations efivarfs_dir_inode_operations = {  	.create = efivarfs_create,  }; -static struct pstore_info efi_pstore_info; - -#ifdef CONFIG_PSTORE +#ifdef CONFIG_EFI_VARS_PSTORE  static int efi_pstore_open(struct pstore_info *psi)  { @@ -1441,7 +1445,7 @@ static int efi_pstore_write(enum pstore_type_id type,  	spin_unlock_irqrestore(&efivars->lock, flags); -	if (reason == KMSG_DUMP_OOPS) +	if (reason == KMSG_DUMP_OOPS && efivar_wq_enabled)  		schedule_work(&efivar_work);  	*id = part; @@ -1514,38 +1518,6 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,  	return 0;  } -#else -static int efi_pstore_open(struct pstore_info *psi) -{ -	return 0; -} - -static int efi_pstore_close(struct pstore_info *psi) -{ -	return 0; -} - -static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, int *count, -			       struct timespec *timespec, -			       char **buf, struct pstore_info *psi) -{ -	return -1; -} - -static int efi_pstore_write(enum pstore_type_id type, -		enum kmsg_dump_reason reason, u64 *id, -		unsigned int part, int count, size_t size, -		struct pstore_info *psi) -{ -	return 0; -} - -static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, -			    struct timespec time, struct pstore_info *psi) -{ -	return 0; -} -#endif  static struct pstore_info efi_pstore_info = {  	.owner		= THIS_MODULE, @@ -1557,6 +1529,24 @@ static struct pstore_info efi_pstore_info = {  	.erase		= efi_pstore_erase,  }; +static void efivar_pstore_register(struct efivars *efivars) +{ +	efivars->efi_pstore_info = efi_pstore_info; +	efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); +	if (efivars->efi_pstore_info.buf) { +		efivars->efi_pstore_info.bufsize = 1024; +		efivars->efi_pstore_info.data = efivars; +		spin_lock_init(&efivars->efi_pstore_info.buf_lock); +		pstore_register(&efivars->efi_pstore_info); +	} +} +#else +static void efivar_pstore_register(struct efivars *efivars) +{ +	return; +} +#endif +  static ssize_t efivar_create(struct file *filp, struct kobject *kobj,  			     struct bin_attribute *bin_attr,  			     char *buf, loff_t pos, size_t count) @@ -1716,6 +1706,31 @@ static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor)  	return found;  } +/* + * Returns the size of variable_name, in bytes, including the + * terminating NULL character, or variable_name_size if no NULL + * character is found among the first variable_name_size bytes. + */ +static unsigned long var_name_strnsize(efi_char16_t *variable_name, +				       unsigned long variable_name_size) +{ +	unsigned long len; +	efi_char16_t c; + +	/* +	 * The variable name is, by definition, a NULL-terminated +	 * string, so make absolutely sure that variable_name_size is +	 * the value we expect it to be. If not, return the real size. +	 */ +	for (len = 2; len <= variable_name_size; len += sizeof(c)) { +		c = variable_name[(len / sizeof(c)) - 1]; +		if (!c) +			break; +	} + +	return min(len, variable_name_size); +} +  static void efivar_update_sysfs_entries(struct work_struct *work)  {  	struct efivars *efivars = &__efivars; @@ -1756,10 +1771,13 @@ static void efivar_update_sysfs_entries(struct work_struct *work)  		if (!found) {  			kfree(variable_name);  			break; -		} else +		} else { +			variable_name_size = var_name_strnsize(variable_name, +							       variable_name_size);  			efivar_create_sysfs_entry(efivars,  						  variable_name_size,  						  variable_name, &vendor); +		}  	}  } @@ -1958,6 +1976,35 @@ void unregister_efivars(struct efivars *efivars)  }  EXPORT_SYMBOL_GPL(unregister_efivars); +/* + * Print a warning when duplicate EFI variables are encountered and + * disable the sysfs workqueue since the firmware is buggy. + */ +static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, +			     unsigned long len16) +{ +	size_t i, len8 = len16 / sizeof(efi_char16_t); +	char *s8; + +	/* +	 * Disable the workqueue since the algorithm it uses for +	 * detecting new variables won't work with this buggy +	 * implementation of GetNextVariableName(). +	 */ +	efivar_wq_enabled = false; + +	s8 = kzalloc(len8, GFP_KERNEL); +	if (!s8) +		return; + +	for (i = 0; i < len8; i++) +		s8[i] = s16[i]; + +	printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", +	       s8, vendor_guid); +	kfree(s8); +} +  int register_efivars(struct efivars *efivars,  		     const struct efivar_operations *ops,  		     struct kobject *parent_kobj) @@ -2006,6 +2053,24 @@ int register_efivars(struct efivars *efivars,  						&vendor_guid);  		switch (status) {  		case EFI_SUCCESS: +			variable_name_size = var_name_strnsize(variable_name, +							       variable_name_size); + +			/* +			 * Some firmware implementations return the +			 * same variable name on multiple calls to +			 * get_next_variable(). Terminate the loop +			 * immediately as there is no guarantee that +			 * we'll ever see a different variable name, +			 * and may end up looping here forever. +			 */ +			if (variable_is_present(variable_name, &vendor_guid)) { +				dup_variable_bug(variable_name, &vendor_guid, +						 variable_name_size); +				status = EFI_NOT_FOUND; +				break; +			} +  			efivar_create_sysfs_entry(efivars,  						  variable_name_size,  						  variable_name, @@ -2025,15 +2090,8 @@ int register_efivars(struct efivars *efivars,  	if (error)  		unregister_efivars(efivars); -	efivars->efi_pstore_info = efi_pstore_info; - -	efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); -	if (efivars->efi_pstore_info.buf) { -		efivars->efi_pstore_info.bufsize = 1024; -		efivars->efi_pstore_info.data = efivars; -		spin_lock_init(&efivars->efi_pstore_info.buf_lock); -		pstore_register(&efivars->efi_pstore_info); -	} +	if (!efivars_pstore_disable) +		efivar_pstore_register(efivars);  	register_filesystem(&efivarfs_type); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 93aaadf99f2..b166e30b3bc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -227,12 +227,6 @@ config GPIO_TS5500  	  blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600  	  LCD port. -config GPIO_VT8500 -	bool "VIA/Wondermedia SoC GPIO Support" -	depends on ARCH_VT8500 -	help -	  Say yes here to support the VT8500/WM8505/WM8650 GPIO controller. -  config GPIO_XILINX  	bool "Xilinx GPIO support"  	depends on PPC_OF || MICROBLAZE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 22e07bc9fcb..a274d7df3c8 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -80,7 +80,6 @@ obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o  obj-$(CONFIG_GPIO_UCB1400)	+= gpio-ucb1400.o  obj-$(CONFIG_GPIO_VIPERBOARD)	+= gpio-viperboard.o  obj-$(CONFIG_GPIO_VR41XX)	+= gpio-vr41xx.o -obj-$(CONFIG_GPIO_VT8500)	+= gpio-vt8500.o  obj-$(CONFIG_GPIO_VX855)	+= gpio-vx855.o  obj-$(CONFIG_GPIO_WM831X)	+= gpio-wm831x.o  obj-$(CONFIG_GPIO_WM8350)	+= gpio-wm8350.o diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 7472182967c..61a6fde6c08 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -42,6 +42,7 @@  #include <linux/io.h>  #include <linux/of_irq.h>  #include <linux/of_device.h> +#include <linux/clk.h>  #include <linux/pinctrl/consumer.h>  /* @@ -496,6 +497,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)  	struct resource *res;  	struct irq_chip_generic *gc;  	struct irq_chip_type *ct; +	struct clk *clk;  	unsigned int ngpios;  	int soc_variant;  	int i, cpu, id; @@ -529,6 +531,11 @@ static int mvebu_gpio_probe(struct platform_device *pdev)  		return id;  	} +	clk = devm_clk_get(&pdev->dev, NULL); +	/* Not all SoCs require a clock.*/ +	if (!IS_ERR(clk)) +		clk_prepare_enable(clk); +  	mvchip->soc_variant = soc_variant;  	mvchip->chip.label = dev_name(&pdev->dev);  	mvchip->chip.dev = &pdev->dev; diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index b3643ff007e..99e0fa49fcb 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -1122,8 +1122,12 @@ int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset)  #ifdef CONFIG_PLAT_S3C24XX  static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset)  { -	if (offset < 4) -		return IRQ_EINT0 + offset; +	if (offset < 4) { +		if (soc_is_s3c2412()) +			return IRQ_EINT0_2412 + offset; +		else +			return IRQ_EINT0 + offset; +	}  	if (offset < 8)  		return IRQ_EINT4 + offset - 4; @@ -3024,6 +3028,7 @@ static __init int samsung_gpiolib_init(void)  	static const struct of_device_id exynos_pinctrl_ids[] = {  		{ .compatible = "samsung,exynos4210-pinctrl", },  		{ .compatible = "samsung,exynos4x12-pinctrl", }, +		{ .compatible = "samsung,exynos5250-pinctrl", },  		{ .compatible = "samsung,exynos5440-pinctrl", },  	};  	for_each_matching_node(pctrl_np, exynos_pinctrl_ids) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 414ad912232..e3956359202 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -72,6 +72,7 @@ struct tegra_gpio_bank {  	u32 oe[4];  	u32 int_enb[4];  	u32 int_lvl[4]; +	u32 wake_enb[4];  #endif  }; @@ -333,15 +334,31 @@ static int tegra_gpio_suspend(struct device *dev)  			bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio));  			bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio));  			bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio)); + +			/* Enable gpio irq for wake up source */ +			tegra_gpio_writel(bank->wake_enb[p], +					  GPIO_INT_ENB(gpio));  		}  	}  	local_irq_restore(flags);  	return 0;  } -static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable) +static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)  {  	struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); +	int gpio = d->hwirq; +	u32 port, bit, mask; + +	port = GPIO_PORT(gpio); +	bit = GPIO_BIT(gpio); +	mask = BIT(bit); + +	if (enable) +		bank->wake_enb[port] |= mask; +	else +		bank->wake_enb[port] &= ~mask; +  	return irq_set_irq_wake(bank->irq, enable);  }  #endif @@ -353,7 +370,7 @@ static struct irq_chip tegra_gpio_irq_chip = {  	.irq_unmask	= tegra_gpio_irq_unmask,  	.irq_set_type	= tegra_gpio_irq_set_type,  #ifdef CONFIG_PM_SLEEP -	.irq_set_wake	= tegra_gpio_wake_enable, +	.irq_set_wake	= tegra_gpio_irq_set_wake,  #endif  }; diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c deleted file mode 100644 index 81683ca35ac..00000000000 --- a/drivers/gpio/gpio-vt8500.c +++ /dev/null @@ -1,355 +0,0 @@ -/* drivers/gpio/gpio-vt8500.c - * - * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> - * Based on arch/arm/mach-vt8500/gpio.c: - * - Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - */ - -#include <linux/module.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/gpio.h> -#include <linux/platform_device.h> -#include <linux/bitops.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/of_device.h> - -/* -	We handle GPIOs by bank, each bank containing up to 32 GPIOs covered -	by one set of registers (although not all may be valid). - -	Because different SoC's have different register offsets, we pass the -	register offsets as data in vt8500_gpio_dt_ids[]. - -	A value of NO_REG is used to indicate that this register is not -	supported. Only used for ->en at the moment. -*/ - -#define NO_REG	0xFFFF - -/* - * struct vt8500_gpio_bank_regoffsets - * @en: offset to enable register of the bank - * @dir: offset to direction register of the bank - * @data_out: offset to the data out register of the bank - * @data_in: offset to the data in register of the bank - * @ngpio: highest valid pin in this bank - */ - -struct vt8500_gpio_bank_regoffsets { -	unsigned int	en; -	unsigned int	dir; -	unsigned int	data_out; -	unsigned int	data_in; -	unsigned char	ngpio; -}; - -struct vt8500_gpio_data { -	unsigned int				num_banks; -	struct vt8500_gpio_bank_regoffsets	banks[]; -}; - -#define VT8500_BANK(__en, __dir, __out, __in, __ngpio)		\ -{								\ -	.en = __en,						\ -	.dir = __dir,						\ -	.data_out = __out,					\ -	.data_in = __in,					\ -	.ngpio = __ngpio,					\ -} - -static struct vt8500_gpio_data vt8500_data = { -	.num_banks	= 7, -	.banks	= { -		VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), -		VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26), -		VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28), -		VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31), -		VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19), -		VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19), -		VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23), -	}, -}; - -static struct vt8500_gpio_data wm8505_data = { -	.num_banks	= 10, -	.banks	= { -		VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), -		VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8), -		VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32), -		VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6), -		VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16), -		VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25), -		VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5), -		VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5), -		VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), -		VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), -		VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), -	}, -}; - -/* - * No information about which bits are valid so we just make - * them all available until its figured out. - */ -static struct vt8500_gpio_data wm8650_data = { -	.num_banks	= 9, -	.banks	= { -		VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32), -		VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32), -		VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32), -		VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32), -		VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32), -		VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32), -		VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32), -		VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32), -		VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32), -		VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), -	}, -}; - -struct vt8500_gpio_chip { -	struct gpio_chip		chip; - -	const struct vt8500_gpio_bank_regoffsets *regs; -	void __iomem	*base; -}; - -struct vt8500_data { -	struct vt8500_gpio_chip *chip; -	void __iomem *iobase; -	int num_banks; -}; - - -#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) - -static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset) -{ -	u32 val; -	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - -	if (vt8500_chip->regs->en == NO_REG) -		return 0; - -	val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); -	val |= BIT(offset); -	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); - -	return 0; -} - -static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset) -{ -	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); -	u32 val; - -	if (vt8500_chip->regs->en == NO_REG) -		return; - -	val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); -	val &= ~BIT(offset); -	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); -} - -static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ -	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - -	u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); -	val &= ~BIT(offset); -	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); - -	return 0; -} - -static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, -								int value) -{ -	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - -	u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); -	val |= BIT(offset); -	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); - -	if (value) { -		val = readl_relaxed(vt8500_chip->base + -						vt8500_chip->regs->data_out); -		val |= BIT(offset); -		writel_relaxed(val, vt8500_chip->base + -						vt8500_chip->regs->data_out); -	} -	return 0; -} - -static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset) -{ -	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - -	return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >> -								offset) & 1; -} - -static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset, -								int value) -{ -	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); - -	u32 val = readl_relaxed(vt8500_chip->base + -						vt8500_chip->regs->data_out); -	if (value) -		val |= BIT(offset); -	else -		val &= ~BIT(offset); - -	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out); -} - -static int vt8500_of_xlate(struct gpio_chip *gc, -			    const struct of_phandle_args *gpiospec, u32 *flags) -{ -	/* bank if specificed in gpiospec->args[0] */ -	if (flags) -		*flags = gpiospec->args[2]; - -	return gpiospec->args[1]; -} - -static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base, -				const struct vt8500_gpio_data *data) -{ -	struct vt8500_data *priv; -	struct vt8500_gpio_chip *vtchip; -	struct gpio_chip *chip; -	int i; -	int pin_cnt = 0; - -	priv = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_data), GFP_KERNEL); -	if (!priv) { -		dev_err(&pdev->dev, "failed to allocate memory\n"); -		return -ENOMEM; -	} - -	priv->chip = devm_kzalloc(&pdev->dev, -			sizeof(struct vt8500_gpio_chip) * data->num_banks, -			GFP_KERNEL); -	if (!priv->chip) { -		dev_err(&pdev->dev, "failed to allocate chip memory\n"); -		return -ENOMEM; -	} - -	priv->iobase = base; -	priv->num_banks = data->num_banks; -	platform_set_drvdata(pdev, priv); - -	vtchip = priv->chip; - -	for (i = 0; i < data->num_banks; i++) { -		vtchip[i].base = base; -		vtchip[i].regs = &data->banks[i]; - -		chip = &vtchip[i].chip; - -		chip->of_xlate = vt8500_of_xlate; -		chip->of_gpio_n_cells = 3; -		chip->of_node = pdev->dev.of_node; - -		chip->request = vt8500_gpio_request; -		chip->free = vt8500_gpio_free; -		chip->direction_input = vt8500_gpio_direction_input; -		chip->direction_output = vt8500_gpio_direction_output; -		chip->get = vt8500_gpio_get_value; -		chip->set = vt8500_gpio_set_value; -		chip->can_sleep = 0; -		chip->base = pin_cnt; -		chip->ngpio = data->banks[i].ngpio; - -		pin_cnt += data->banks[i].ngpio; - -		gpiochip_add(chip); -	} -	return 0; -} - -static struct of_device_id vt8500_gpio_dt_ids[] = { -	{ .compatible = "via,vt8500-gpio", .data = &vt8500_data, }, -	{ .compatible = "wm,wm8505-gpio", .data = &wm8505_data, }, -	{ .compatible = "wm,wm8650-gpio", .data = &wm8650_data, }, -	{ /* Sentinel */ }, -}; - -static int vt8500_gpio_probe(struct platform_device *pdev) -{ -	int ret; -	void __iomem *gpio_base; -	struct resource *res; -	const struct of_device_id *of_id = -				of_match_device(vt8500_gpio_dt_ids, &pdev->dev); - -	if (!of_id) { -		dev_err(&pdev->dev, "No matching driver data\n"); -		return -ENODEV; -	} - -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) { -		dev_err(&pdev->dev, "Unable to get IO resource\n"); -		return -ENODEV; -	} - -	gpio_base = devm_request_and_ioremap(&pdev->dev, res); -	if (!gpio_base) { -		dev_err(&pdev->dev, "Unable to map GPIO registers\n"); -		return -ENOMEM; -	} - -	ret = vt8500_add_chips(pdev, gpio_base, of_id->data); - -	return ret; -} - -static int vt8500_gpio_remove(struct platform_device *pdev) -{ -	int i; -	int ret; -	struct vt8500_data *priv = platform_get_drvdata(pdev); -	struct vt8500_gpio_chip *vtchip = priv->chip; - -	for (i = 0; i < priv->num_banks; i++) { -		ret = gpiochip_remove(&vtchip[i].chip); -		if (ret) -			dev_warn(&pdev->dev, "gpiochip_remove returned %d\n", -				 ret); -	} - -	return 0; -} - -static struct platform_driver vt8500_gpio_driver = { -	.probe		= vt8500_gpio_probe, -	.remove		= vt8500_gpio_remove, -	.driver		= { -		.name	= "vt8500-gpio", -		.owner	= THIS_MODULE, -		.of_match_table = vt8500_gpio_dt_ids, -	}, -}; - -module_platform_driver(vt8500_gpio_driver); - -MODULE_DESCRIPTION("VT8500 GPIO Driver"); -MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a71a54a3e3f..5150df6cba0 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -193,7 +193,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)  	if (!np)  		return; -	do { +	for (;; index++) {  		ret = of_parse_phandle_with_args(np, "gpio-ranges",  				"#gpio-range-cells", index, &pinspec);  		if (ret) @@ -222,8 +222,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)  		if (ret)  			break; - -	} while (index++); +	}  }  #else diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c194f4e680a..e2acfdbf7d3 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1634,7 +1634,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,  	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;  	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;  	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; -	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4; +	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4;  	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);  	/* ignore tiny modes */ @@ -1715,6 +1715,7 @@ set_size:  	}  	mode->type = DRM_MODE_TYPE_DRIVER; +	mode->vrefresh = drm_mode_vrefresh(mode);  	drm_mode_set_name(mode);  	return mode; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 36493ce71f9..98cc14725ba 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -38,11 +38,12 @@  /* position control register for hardware window 0, 2 ~ 4.*/  #define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)  #define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16) -/* size control register for hardware window 0. */ -#define VIDOSD_C_SIZE_W0	(VIDOSD_BASE + 0x08) -/* alpha control register for hardware window 1 ~ 4. */ -#define VIDOSD_C(win)		(VIDOSD_BASE + 0x18 + (win) * 16) -/* size control register for hardware window 1 ~ 4. */ +/* + * size control register for hardware windows 0 and alpha control register + * for hardware windows 1 ~ 4 + */ +#define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16) +/* size control register for hardware windows 1 ~ 2. */  #define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)  #define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8) @@ -50,9 +51,9 @@  #define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)  /* color key control register for hardware window 1 ~ 4. */ -#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + (x * 8)) +#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))  /* color key value register for hardware window 1 ~ 4. */ -#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + (x * 8)) +#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))  /* FIMD has totally five hardware windows. */  #define WINDOWS_NR	5 @@ -109,9 +110,9 @@ struct fimd_context {  #ifdef CONFIG_OF  static const struct of_device_id fimd_driver_dt_match[] = { -	{ .compatible = "samsung,exynos4-fimd", +	{ .compatible = "samsung,exynos4210-fimd",  	  .data = &exynos4_fimd_driver_data }, -	{ .compatible = "samsung,exynos5-fimd", +	{ .compatible = "samsung,exynos5250-fimd",  	  .data = &exynos5_fimd_driver_data },  	{},  }; @@ -581,7 +582,7 @@ static void fimd_win_commit(struct device *dev, int zpos)  	if (win != 3 && win != 4) {  		u32 offset = VIDOSD_D(win);  		if (win == 0) -			offset = VIDOSD_C_SIZE_W0; +			offset = VIDOSD_C(win);  		val = win_data->ovl_width * win_data->ovl_height;  		writel(val, ctx->regs + offset); diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 3b0da0378ac..47a493c8a71 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -48,8 +48,14 @@  /* registers for base address */  #define G2D_SRC_BASE_ADDR		0x0304 +#define G2D_SRC_COLOR_MODE		0x030C +#define G2D_SRC_LEFT_TOP		0x0310 +#define G2D_SRC_RIGHT_BOTTOM		0x0314  #define G2D_SRC_PLANE2_BASE_ADDR	0x0318  #define G2D_DST_BASE_ADDR		0x0404 +#define G2D_DST_COLOR_MODE		0x040C +#define G2D_DST_LEFT_TOP		0x0410 +#define G2D_DST_RIGHT_BOTTOM		0x0414  #define G2D_DST_PLANE2_BASE_ADDR	0x0418  #define G2D_PAT_BASE_ADDR		0x0500  #define G2D_MSK_BASE_ADDR		0x0520 @@ -82,7 +88,7 @@  #define G2D_DMA_LIST_DONE_COUNT_OFFSET	17  /* G2D_DMA_HOLD_CMD */ -#define G2D_USET_HOLD			(1 << 2) +#define G2D_USER_HOLD			(1 << 2)  #define G2D_LIST_HOLD			(1 << 1)  #define G2D_BITBLT_HOLD			(1 << 0) @@ -91,13 +97,27 @@  #define G2D_START_NHOLT			(1 << 1)  #define G2D_START_BITBLT		(1 << 0) +/* buffer color format */ +#define G2D_FMT_XRGB8888		0 +#define G2D_FMT_ARGB8888		1 +#define G2D_FMT_RGB565			2 +#define G2D_FMT_XRGB1555		3 +#define G2D_FMT_ARGB1555		4 +#define G2D_FMT_XRGB4444		5 +#define G2D_FMT_ARGB4444		6 +#define G2D_FMT_PACKED_RGB888		7 +#define G2D_FMT_A8			11 +#define G2D_FMT_L8			12 + +/* buffer valid length */ +#define G2D_LEN_MIN			1 +#define G2D_LEN_MAX			8000 +  #define G2D_CMDLIST_SIZE		(PAGE_SIZE / 4)  #define G2D_CMDLIST_NUM			64  #define G2D_CMDLIST_POOL_SIZE		(G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM)  #define G2D_CMDLIST_DATA_NUM		(G2D_CMDLIST_SIZE / sizeof(u32) - 2) -#define MAX_BUF_ADDR_NR			6 -  /* maximum buffer pool size of userptr is 64MB as default */  #define MAX_POOL		(64 * 1024 * 1024) @@ -106,6 +126,17 @@ enum {  	BUF_TYPE_USERPTR,  }; +enum g2d_reg_type { +	REG_TYPE_NONE = -1, +	REG_TYPE_SRC, +	REG_TYPE_SRC_PLANE2, +	REG_TYPE_DST, +	REG_TYPE_DST_PLANE2, +	REG_TYPE_PAT, +	REG_TYPE_MSK, +	MAX_REG_TYPE_NR +}; +  /* cmdlist data structure */  struct g2d_cmdlist {  	u32		head; @@ -113,6 +144,42 @@ struct g2d_cmdlist {  	u32		last;	/* last data offset */  }; +/* + * A structure of buffer description + * + * @format: color format + * @left_x: the x coordinates of left top corner + * @top_y: the y coordinates of left top corner + * @right_x: the x coordinates of right bottom corner + * @bottom_y: the y coordinates of right bottom corner + * + */ +struct g2d_buf_desc { +	unsigned int	format; +	unsigned int	left_x; +	unsigned int	top_y; +	unsigned int	right_x; +	unsigned int	bottom_y; +}; + +/* + * A structure of buffer information + * + * @map_nr: manages the number of mapped buffers + * @reg_types: stores regitster type in the order of requested command + * @handles: stores buffer handle in its reg_type position + * @types: stores buffer type in its reg_type position + * @descs: stores buffer description in its reg_type position + * + */ +struct g2d_buf_info { +	unsigned int		map_nr; +	enum g2d_reg_type	reg_types[MAX_REG_TYPE_NR]; +	unsigned long		handles[MAX_REG_TYPE_NR]; +	unsigned int		types[MAX_REG_TYPE_NR]; +	struct g2d_buf_desc	descs[MAX_REG_TYPE_NR]; +}; +  struct drm_exynos_pending_g2d_event {  	struct drm_pending_event	base;  	struct drm_exynos_g2d_event	event; @@ -131,14 +198,11 @@ struct g2d_cmdlist_userptr {  	bool			in_pool;  	bool			out_of_list;  }; -  struct g2d_cmdlist_node {  	struct list_head	list;  	struct g2d_cmdlist	*cmdlist; -	unsigned int		map_nr; -	unsigned long		handles[MAX_BUF_ADDR_NR]; -	unsigned int		obj_type[MAX_BUF_ADDR_NR];  	dma_addr_t		dma_addr; +	struct g2d_buf_info	buf_info;  	struct drm_exynos_pending_g2d_event	*event;  }; @@ -188,6 +252,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)  	struct exynos_drm_subdrv *subdrv = &g2d->subdrv;  	int nr;  	int ret; +	struct g2d_buf_info *buf_info;  	init_dma_attrs(&g2d->cmdlist_dma_attrs);  	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs); @@ -209,11 +274,17 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)  	}  	for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { +		unsigned int i; +  		node[nr].cmdlist =  			g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE;  		node[nr].dma_addr =  			g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; +		buf_info = &node[nr].buf_info; +		for (i = 0; i < MAX_REG_TYPE_NR; i++) +			buf_info->reg_types[i] = REG_TYPE_NONE; +  		list_add_tail(&node[nr].list, &g2d->free_cmdlist);  	} @@ -450,7 +521,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,  						DMA_BIDIRECTIONAL);  	if (ret < 0) {  		DRM_ERROR("failed to map sgt with dma region.\n"); -		goto err_free_sgt; +		goto err_sg_free_table;  	}  	g2d_userptr->dma_addr = sgt->sgl[0].dma_address; @@ -467,8 +538,10 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,  	return &g2d_userptr->dma_addr; -err_free_sgt: +err_sg_free_table:  	sg_free_table(sgt); + +err_free_sgt:  	kfree(sgt);  	sgt = NULL; @@ -506,36 +579,172 @@ static void g2d_userptr_free_all(struct drm_device *drm_dev,  	g2d->current_pool = 0;  } +static enum g2d_reg_type g2d_get_reg_type(int reg_offset) +{ +	enum g2d_reg_type reg_type; + +	switch (reg_offset) { +	case G2D_SRC_BASE_ADDR: +	case G2D_SRC_COLOR_MODE: +	case G2D_SRC_LEFT_TOP: +	case G2D_SRC_RIGHT_BOTTOM: +		reg_type = REG_TYPE_SRC; +		break; +	case G2D_SRC_PLANE2_BASE_ADDR: +		reg_type = REG_TYPE_SRC_PLANE2; +		break; +	case G2D_DST_BASE_ADDR: +	case G2D_DST_COLOR_MODE: +	case G2D_DST_LEFT_TOP: +	case G2D_DST_RIGHT_BOTTOM: +		reg_type = REG_TYPE_DST; +		break; +	case G2D_DST_PLANE2_BASE_ADDR: +		reg_type = REG_TYPE_DST_PLANE2; +		break; +	case G2D_PAT_BASE_ADDR: +		reg_type = REG_TYPE_PAT; +		break; +	case G2D_MSK_BASE_ADDR: +		reg_type = REG_TYPE_MSK; +		break; +	default: +		reg_type = REG_TYPE_NONE; +		DRM_ERROR("Unknown register offset![%d]\n", reg_offset); +		break; +	}; + +	return reg_type; +} + +static unsigned long g2d_get_buf_bpp(unsigned int format) +{ +	unsigned long bpp; + +	switch (format) { +	case G2D_FMT_XRGB8888: +	case G2D_FMT_ARGB8888: +		bpp = 4; +		break; +	case G2D_FMT_RGB565: +	case G2D_FMT_XRGB1555: +	case G2D_FMT_ARGB1555: +	case G2D_FMT_XRGB4444: +	case G2D_FMT_ARGB4444: +		bpp = 2; +		break; +	case G2D_FMT_PACKED_RGB888: +		bpp = 3; +		break; +	default: +		bpp = 1; +		break; +	} + +	return bpp; +} + +static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, +						enum g2d_reg_type reg_type, +						unsigned long size) +{ +	unsigned int width, height; +	unsigned long area; + +	/* +	 * check source and destination buffers only. +	 * so the others are always valid. +	 */ +	if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST) +		return true; + +	width = buf_desc->right_x - buf_desc->left_x; +	if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { +		DRM_ERROR("width[%u] is out of range!\n", width); +		return false; +	} + +	height = buf_desc->bottom_y - buf_desc->top_y; +	if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { +		DRM_ERROR("height[%u] is out of range!\n", height); +		return false; +	} + +	area = (unsigned long)width * (unsigned long)height * +					g2d_get_buf_bpp(buf_desc->format); +	if (area > size) { +		DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size); +		return false; +	} + +	return true; +} +  static int g2d_map_cmdlist_gem(struct g2d_data *g2d,  				struct g2d_cmdlist_node *node,  				struct drm_device *drm_dev,  				struct drm_file *file)  {  	struct g2d_cmdlist *cmdlist = node->cmdlist; +	struct g2d_buf_info *buf_info = &node->buf_info;  	int offset; +	int ret;  	int i; -	for (i = 0; i < node->map_nr; i++) { +	for (i = 0; i < buf_info->map_nr; i++) { +		struct g2d_buf_desc *buf_desc; +		enum g2d_reg_type reg_type; +		int reg_pos;  		unsigned long handle;  		dma_addr_t *addr; -		offset = cmdlist->last - (i * 2 + 1); -		handle = cmdlist->data[offset]; +		reg_pos = cmdlist->last - 2 * (i + 1); + +		offset = cmdlist->data[reg_pos]; +		handle = cmdlist->data[reg_pos + 1]; + +		reg_type = g2d_get_reg_type(offset); +		if (reg_type == REG_TYPE_NONE) { +			ret = -EFAULT; +			goto err; +		} + +		buf_desc = &buf_info->descs[reg_type]; + +		if (buf_info->types[reg_type] == BUF_TYPE_GEM) { +			unsigned long size; + +			size = exynos_drm_gem_get_size(drm_dev, handle, file); +			if (!size) { +				ret = -EFAULT; +				goto err; +			} + +			if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, +									size)) { +				ret = -EFAULT; +				goto err; +			} -		if (node->obj_type[i] == BUF_TYPE_GEM) {  			addr = exynos_drm_gem_get_dma_addr(drm_dev, handle,  								file);  			if (IS_ERR(addr)) { -				node->map_nr = i; -				return -EFAULT; +				ret = -EFAULT; +				goto err;  			}  		} else {  			struct drm_exynos_g2d_userptr g2d_userptr;  			if (copy_from_user(&g2d_userptr, (void __user *)handle,  				sizeof(struct drm_exynos_g2d_userptr))) { -				node->map_nr = i; -				return -EFAULT; +				ret = -EFAULT; +				goto err; +			} + +			if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, +							g2d_userptr.size)) { +				ret = -EFAULT; +				goto err;  			}  			addr = g2d_userptr_get_dma_addr(drm_dev, @@ -544,16 +753,21 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d,  							file,  							&handle);  			if (IS_ERR(addr)) { -				node->map_nr = i; -				return -EFAULT; +				ret = -EFAULT; +				goto err;  			}  		} -		cmdlist->data[offset] = *addr; -		node->handles[i] = handle; +		cmdlist->data[reg_pos + 1] = *addr; +		buf_info->reg_types[i] = reg_type; +		buf_info->handles[reg_type] = handle;  	}  	return 0; + +err: +	buf_info->map_nr = i; +	return ret;  }  static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, @@ -561,22 +775,33 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d,  				  struct drm_file *filp)  {  	struct exynos_drm_subdrv *subdrv = &g2d->subdrv; +	struct g2d_buf_info *buf_info = &node->buf_info;  	int i; -	for (i = 0; i < node->map_nr; i++) { -		unsigned long handle = node->handles[i]; +	for (i = 0; i < buf_info->map_nr; i++) { +		struct g2d_buf_desc *buf_desc; +		enum g2d_reg_type reg_type; +		unsigned long handle; -		if (node->obj_type[i] == BUF_TYPE_GEM) +		reg_type = buf_info->reg_types[i]; + +		buf_desc = &buf_info->descs[reg_type]; +		handle = buf_info->handles[reg_type]; + +		if (buf_info->types[reg_type] == BUF_TYPE_GEM)  			exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle,  							filp);  		else  			g2d_userptr_put_dma_addr(subdrv->drm_dev, handle,  							false); -		node->handles[i] = 0; +		buf_info->reg_types[i] = REG_TYPE_NONE; +		buf_info->handles[reg_type] = 0; +		buf_info->types[reg_type] = 0; +		memset(buf_desc, 0x00, sizeof(*buf_desc));  	} -	node->map_nr = 0; +	buf_info->map_nr = 0;  }  static void g2d_dma_start(struct g2d_data *g2d, @@ -589,10 +814,6 @@ static void g2d_dma_start(struct g2d_data *g2d,  	pm_runtime_get_sync(g2d->dev);  	clk_enable(g2d->gate_clk); -	/* interrupt enable */ -	writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, -			g2d->regs + G2D_INTEN); -  	writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR);  	writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND);  } @@ -643,7 +864,6 @@ static void g2d_runqueue_worker(struct work_struct *work)  	struct g2d_data *g2d = container_of(work, struct g2d_data,  					    runqueue_work); -  	mutex_lock(&g2d->runqueue_mutex);  	clk_disable(g2d->gate_clk);  	pm_runtime_put_sync(g2d->dev); @@ -724,20 +944,14 @@ static int g2d_check_reg_offset(struct device *dev,  	int i;  	for (i = 0; i < nr; i++) { -		index = cmdlist->last - 2 * (i + 1); +		struct g2d_buf_info *buf_info = &node->buf_info; +		struct g2d_buf_desc *buf_desc; +		enum g2d_reg_type reg_type; +		unsigned long value; -		if (for_addr) { -			/* check userptr buffer type. */ -			reg_offset = (cmdlist->data[index] & -					~0x7fffffff) >> 31; -			if (reg_offset) { -				node->obj_type[i] = BUF_TYPE_USERPTR; -				cmdlist->data[index] &= ~G2D_BUF_USERPTR; -			} -		} +		index = cmdlist->last - 2 * (i + 1);  		reg_offset = cmdlist->data[index] & ~0xfffff000; -  		if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END)  			goto err;  		if (reg_offset % 4) @@ -753,8 +967,60 @@ static int g2d_check_reg_offset(struct device *dev,  			if (!for_addr)  				goto err; -			if (node->obj_type[i] != BUF_TYPE_USERPTR) -				node->obj_type[i] = BUF_TYPE_GEM; +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			/* check userptr buffer type. */ +			if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { +				buf_info->types[reg_type] = BUF_TYPE_USERPTR; +				cmdlist->data[index] &= ~G2D_BUF_USERPTR; +			} else +				buf_info->types[reg_type] = BUF_TYPE_GEM; +			break; +		case G2D_SRC_COLOR_MODE: +		case G2D_DST_COLOR_MODE: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			buf_desc = &buf_info->descs[reg_type]; +			value = cmdlist->data[index + 1]; + +			buf_desc->format = value & 0xf; +			break; +		case G2D_SRC_LEFT_TOP: +		case G2D_DST_LEFT_TOP: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			buf_desc = &buf_info->descs[reg_type]; +			value = cmdlist->data[index + 1]; + +			buf_desc->left_x = value & 0x1fff; +			buf_desc->top_y = (value & 0x1fff0000) >> 16; +			break; +		case G2D_SRC_RIGHT_BOTTOM: +		case G2D_DST_RIGHT_BOTTOM: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			buf_desc = &buf_info->descs[reg_type]; +			value = cmdlist->data[index + 1]; + +			buf_desc->right_x = value & 0x1fff; +			buf_desc->bottom_y = (value & 0x1fff0000) >> 16;  			break;  		default:  			if (for_addr) @@ -860,9 +1126,23 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,  	cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR;  	cmdlist->data[cmdlist->last++] = 0; +	/* +	 * 'LIST_HOLD' command should be set to the DMA_HOLD_CMD_REG +	 * and GCF bit should be set to INTEN register if user wants +	 * G2D interrupt event once current command list execution is +	 * finished. +	 * Otherwise only ACF bit should be set to INTEN register so +	 * that one interrupt is occured after all command lists +	 * have been completed. +	 */  	if (node->event) { +		cmdlist->data[cmdlist->last++] = G2D_INTEN; +		cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF | G2D_INTEN_GCF;  		cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD;  		cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; +	} else { +		cmdlist->data[cmdlist->last++] = G2D_INTEN; +		cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF;  	}  	/* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ @@ -887,7 +1167,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,  	if (ret < 0)  		goto err_free_event; -	node->map_nr = req->cmd_buf_nr; +	node->buf_info.map_nr = req->cmd_buf_nr;  	if (req->cmd_buf_nr) {  		struct drm_exynos_g2d_cmd *cmd_buf; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 67e17ce112b..0e6fe000578 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -164,6 +164,27 @@ out:  	exynos_gem_obj = NULL;  } +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, +						unsigned int gem_handle, +						struct drm_file *file_priv) +{ +	struct exynos_drm_gem_obj *exynos_gem_obj; +	struct drm_gem_object *obj; + +	obj = drm_gem_object_lookup(dev, file_priv, gem_handle); +	if (!obj) { +		DRM_ERROR("failed to lookup gem object.\n"); +		return 0; +	} + +	exynos_gem_obj = to_exynos_gem_obj(obj); + +	drm_gem_object_unreference_unlocked(obj); + +	return exynos_gem_obj->buffer->size; +} + +  struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,  						      unsigned long size)  { diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 35ebac47dc2..468766bee45 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -130,6 +130,11 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,  int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,  				      struct drm_file *file_priv); +/* get buffer size to gem handle. */ +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, +						unsigned int gem_handle, +						struct drm_file *file_priv); +  /* initialize gem object. */  int exynos_drm_gem_init_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 13ccbd4bcfa..9504b0cd825 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -117,13 +117,12 @@ static struct edid *vidi_get_edid(struct device *dev,  	}  	edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH; -	edid = kzalloc(edid_len, GFP_KERNEL); +	edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);  	if (!edid) {  		DRM_DEBUG_KMS("failed to allocate edid\n");  		return ERR_PTR(-ENOMEM);  	} -	memcpy(edid, ctx->raw_edid, edid_len);  	return edid;  } @@ -563,12 +562,11 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,  			return -EINVAL;  		}  		edid_len = (1 + raw_edid->extensions) * EDID_LENGTH; -		ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL); +		ctx->raw_edid = kmemdup(raw_edid, edid_len, GFP_KERNEL);  		if (!ctx->raw_edid) {  			DRM_DEBUG_KMS("failed to allocate raw_edid.\n");  			return -ENOMEM;  		} -		memcpy(ctx->raw_edid, raw_edid, edid_len);  	} else {  		/*  		 * with connection = 0, free raw_edid diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e919aba29b3..2f4f72f0704 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -818,7 +818,7 @@ static void mixer_win_disable(void *ctx, int win)  	mixer_ctx->win_data[win].enabled = false;  } -int mixer_check_timing(void *ctx, struct fb_videomode *timing) +static int mixer_check_timing(void *ctx, struct fb_videomode *timing)  {  	struct mixer_context *mixer_ctx = ctx;  	u32 w, h; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index aae31489c89..7299ea45dd0 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -103,7 +103,7 @@ static const char *cache_level_str(int type)  static void  describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)  { -	seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", +	seq_printf(m, "%pK: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s",  		   &obj->base,  		   get_pin_flag(obj),  		   get_tiling_flag(obj), diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 0a8eceb7590..e9b57893db2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -125,6 +125,11 @@ MODULE_PARM_DESC(preliminary_hw_support,  		"Enable Haswell and ValleyView Support. "  		"(default: false)"); +int i915_disable_power_well __read_mostly = 0; +module_param_named(disable_power_well, i915_disable_power_well, int, 0600); +MODULE_PARM_DESC(disable_power_well, +		 "Disable the power well when possible (default: false)"); +  static struct drm_driver driver;  extern int intel_agp_enabled; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e95337c9745..01769e2a995 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1398,6 +1398,7 @@ extern int i915_enable_fbc __read_mostly;  extern bool i915_enable_hangcheck __read_mostly;  extern int i915_enable_ppgtt __read_mostly;  extern unsigned int i915_preliminary_hw_support __read_mostly; +extern int i915_disable_power_well __read_mostly;  extern int i915_suspend(struct drm_device *dev, pm_message_t state);  extern int i915_resume(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 2f2daebd0ee..3b11ab0fbc9 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -732,6 +732,8 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,  		   int count)  {  	int i; +	int relocs_total = 0; +	int relocs_max = INT_MAX / sizeof(struct drm_i915_gem_relocation_entry);  	for (i = 0; i < count; i++) {  		char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr; @@ -740,10 +742,13 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,  		if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS)  			return -EINVAL; -		/* First check for malicious input causing overflow */ -		if (exec[i].relocation_count > -		    INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) +		/* First check for malicious input causing overflow in +		 * the worst case where we need to allocate the entire +		 * relocation tree as a single array. +		 */ +		if (exec[i].relocation_count > relocs_max - relocs_total)  			return -EINVAL; +		relocs_total += exec[i].relocation_count;  		length = exec[i].relocation_count *  			sizeof(struct drm_i915_gem_relocation_entry); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 287b42c9d1a..b20d50192fc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5771,6 +5771,11 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,  		num_connectors++;  	} +	if (is_cpu_edp) +		intel_crtc->cpu_transcoder = TRANSCODER_EDP; +	else +		intel_crtc->cpu_transcoder = pipe; +  	/* We are not sure yet this won't happen. */  	WARN(!HAS_PCH_LPT(dev), "Unexpected PCH type %d\n",  	     INTEL_PCH_TYPE(dev)); @@ -5837,11 +5842,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,  	int pipe = intel_crtc->pipe;  	int ret; -	if (IS_HASWELL(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) -		intel_crtc->cpu_transcoder = TRANSCODER_EDP; -	else -		intel_crtc->cpu_transcoder = pipe; -  	drm_vblank_pre_modeset(dev, pipe);  	ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6f728e5ee79..d7d4afe0134 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -820,6 +820,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,  	struct intel_link_m_n m_n;  	int pipe = intel_crtc->pipe;  	enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; +	int target_clock;  	/*  	 * Find the lane count in the intel_encoder private @@ -835,13 +836,22 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,  		}  	} +	target_clock = mode->clock; +	for_each_encoder_on_crtc(dev, crtc, intel_encoder) { +		if (intel_encoder->type == INTEL_OUTPUT_EDP) { +			target_clock = intel_edp_target_clock(intel_encoder, +							      mode); +			break; +		} +	} +  	/*  	 * Compute the GMCH and Link ratios. The '3' here is  	 * the number of bytes_per_pixel post-LUT, which we always  	 * set up for 8-bits of R/G/B, or 3 bytes total.  	 */  	intel_link_compute_m_n(intel_crtc->bpp, lane_count, -			       mode->clock, adjusted_mode->clock, &m_n); +			       target_clock, adjusted_mode->clock, &m_n);  	if (IS_HASWELL(dev)) {  		I915_WRITE(PIPE_DATA_M1(cpu_transcoder), @@ -1930,7 +1940,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)  		for (i = 0; i < intel_dp->lane_count; i++)  			if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)  				break; -		if (i == intel_dp->lane_count && voltage_tries == 5) { +		if (i == intel_dp->lane_count) {  			++loop_tries;  			if (loop_tries == 5) {  				DRM_DEBUG_KMS("too many full retries, give up\n"); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index acf8aec9ada..ef4744e1bf0 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -203,7 +203,13 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)  	algo->data = bus;  } -#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4) +/* + * gmbus on gen4 seems to be able to generate legacy interrupts even when in MSI + * mode. This results in spurious interrupt warnings if the legacy irq no. is + * shared with another device. The kernel then disables that interrupt source + * and so prevents the other device from working properly. + */ +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)  static int  gmbus_wait_hw_status(struct drm_i915_private *dev_priv,  		     u32 gmbus2_status, @@ -214,6 +220,9 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv,  	u32 gmbus2 = 0;  	DEFINE_WAIT(wait); +	if (!HAS_GMBUS_IRQ(dev_priv->dev)) +		gmbus4_irq_en = 0; +  	/* Important: The hw handles only the first bit, so set only one! Since  	 * we also need to check for NAKs besides the hw ready/idle signal, we  	 * need to wake up periodically and check that ourselves. */ diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index a3730e0289e..bee8cb6108a 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -321,9 +321,6 @@ void intel_panel_enable_backlight(struct drm_device *dev,  	if (dev_priv->backlight_level == 0)  		dev_priv->backlight_level = intel_panel_get_max_backlight(dev); -	dev_priv->backlight_enabled = true; -	intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); -  	if (INTEL_INFO(dev)->gen >= 4) {  		uint32_t reg, tmp; @@ -359,12 +356,12 @@ void intel_panel_enable_backlight(struct drm_device *dev,  	}  set_level: -	/* Check the current backlight level and try to set again if it's zero. -	 * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically -	 * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written. +	/* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. +	 * BLC_PWM_CPU_CTL may be cleared to zero automatically when these +	 * registers are set.  	 */ -	if (!intel_panel_get_backlight(dev)) -		intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); +	dev_priv->backlight_enabled = true; +	intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);  }  static void intel_panel_init_backlight(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index a1794c6df1b..adca00783e6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4079,6 +4079,9 @@ void intel_set_power_well(struct drm_device *dev, bool enable)  	if (!IS_HASWELL(dev))  		return; +	if (!i915_disable_power_well && !enable) +		return; +  	tmp = I915_READ(HSW_PWR_WELL_DRIVER);  	is_enabled = tmp & HSW_PWR_WELL_STATE;  	enable_requested = tmp & HSW_PWR_WELL_ENABLE; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a274b9906ef..fe22bb780e1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -382,19 +382,19 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock)  	m = n = p = 0;  	vcomax = 800000;  	vcomin = 400000; -	pllreffreq = 3333; +	pllreffreq = 33333;  	delta = 0xffffffff;  	permitteddelta = clock * 5 / 1000; -	for (testp = 16; testp > 0; testp--) { +	for (testp = 16; testp > 0; testp >>= 1) {  		if (clock * testp > vcomax)  			continue;  		if (clock * testp < vcomin)  			continue;  		for (testm = 1; testm < 33; testm++) { -			for (testn = 1; testn < 257; testn++) { +			for (testn = 17; testn < 257; testn++) {  				computed = (pllreffreq * testn) /  					(testm * testp);  				if (computed > clock) @@ -404,11 +404,11 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock)  				if (tmpdelta < delta) {  					delta = tmpdelta;  					n = testn - 1; -					m = (testm - 1) | ((n >> 1) & 0x80); +					m = (testm - 1);  					p = testp - 1;  				}  				if ((clock * testp) >= 600000) -					p |= 80; +					p |= 0x80;  			}  		}  	} diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c index 0daab62ea14..3b2e7b6304d 100644 --- a/drivers/gpu/drm/nouveau/core/core/object.c +++ b/drivers/gpu/drm/nouveau/core/core/object.c @@ -278,7 +278,6 @@ nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)  	struct nouveau_object *parent = NULL;  	struct nouveau_object *namedb = NULL;  	struct nouveau_handle *handle = NULL; -	int ret = -EINVAL;  	parent = nouveau_handle_ref(client, _parent);  	if (!parent) @@ -295,7 +294,7 @@ nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)  	}  	nouveau_object_ref(NULL, &parent); -	return ret; +	return handle ? 0 : -EINVAL;  }  int diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 5fa13267bd9..02e369f8044 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -544,13 +544,13 @@ nv50_disp_curs_ofuncs = {  static void  nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)  { -	nv_mask(event->priv, 0x61002c, (1 << head), (1 << head)); +	nv_mask(event->priv, 0x61002c, (4 << head), (4 << head));  }  static void  nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)  { -	nv_mask(event->priv, 0x61002c, (1 << head), (0 << head)); +	nv_mask(event->priv, 0x61002c, (4 << head), 0);  }  static int diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h index 6b17b614629..0b20fc0d19c 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h @@ -4,7 +4,7 @@  #include <core/device.h>  #include <core/subdev.h> -enum nouveau_therm_mode { +enum nouveau_therm_fan_mode {  	NOUVEAU_THERM_CTRL_NONE = 0,  	NOUVEAU_THERM_CTRL_MANUAL = 1,  	NOUVEAU_THERM_CTRL_AUTO = 2, diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c index f794dc89a3b..a00a5a76e2d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c @@ -134,7 +134,7 @@ nouveau_therm_alarm(struct nouveau_alarm *alarm)  }  int -nouveau_therm_mode(struct nouveau_therm *therm, int mode) +nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode)  {  	struct nouveau_therm_priv *priv = (void *)therm;  	struct nouveau_device *device = nv_device(therm); @@ -149,10 +149,15 @@ nouveau_therm_mode(struct nouveau_therm *therm, int mode)  	    (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0))  		return -EINVAL; +	/* do not allow automatic fan management if the thermal sensor is +	 * not available */ +	if (priv->mode == 2 && therm->temp_get(therm) < 0) +		return -EINVAL; +  	if (priv->mode == mode)  		return 0; -	nv_info(therm, "Thermal management: %s\n", name[mode]); +	nv_info(therm, "fan management: %s\n", name[mode]);  	nouveau_therm_update(therm, mode);  	return 0;  } @@ -213,7 +218,7 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,  		priv->fan->bios.max_duty = value;  		return 0;  	case NOUVEAU_THERM_ATTR_FAN_MODE: -		return nouveau_therm_mode(therm, value); +		return nouveau_therm_fan_mode(therm, value);  	case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:  		priv->bios_sensor.thrs_fan_boost.temp = value;  		priv->sensor.program_alarms(therm); @@ -263,7 +268,7 @@ _nouveau_therm_init(struct nouveau_object *object)  		return ret;  	if (priv->suspend >= 0) -		nouveau_therm_mode(therm, priv->mode); +		nouveau_therm_fan_mode(therm, priv->mode);  	priv->sensor.program_alarms(therm);  	return 0;  } @@ -313,11 +318,12 @@ nouveau_therm_create_(struct nouveau_object *parent,  int  nouveau_therm_preinit(struct nouveau_therm *therm)  { -	nouveau_therm_ic_ctor(therm);  	nouveau_therm_sensor_ctor(therm); +	nouveau_therm_ic_ctor(therm);  	nouveau_therm_fan_ctor(therm); -	nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_NONE); +	nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_NONE); +	nouveau_therm_sensor_preinit(therm);  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c index e24090bac19..8b3adec5fbb 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c @@ -32,6 +32,7 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,  			struct i2c_board_info *info)  {  	struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c); +	struct nvbios_therm_sensor *sensor = &priv->bios_sensor;  	struct i2c_client *client;  	request_module("%s%s", I2C_MODULE_PREFIX, info->type); @@ -46,8 +47,9 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,  	}  	nv_info(priv, -		"Found an %s at address 0x%x (controlled by lm_sensors)\n", -		info->type, info->addr); +		"Found an %s at address 0x%x (controlled by lm_sensors, " +		"temp offset %+i C)\n", +		info->type, info->addr, sensor->offset_constant);  	priv->ic = client;  	return true; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c index 0f5363edb96..a70d1b7e397 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c @@ -29,54 +29,83 @@ struct nv40_therm_priv {  	struct nouveau_therm_priv base;  }; +enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 }; + +static enum nv40_sensor_style +nv40_sensor_style(struct nouveau_therm *therm) +{ +	struct nouveau_device *device = nv_device(therm); + +	switch (device->chipset) { +	case 0x43: +	case 0x44: +	case 0x4a: +	case 0x47: +		return OLD_STYLE; + +	case 0x46: +	case 0x49: +	case 0x4b: +	case 0x4e: +	case 0x4c: +	case 0x67: +	case 0x68: +	case 0x63: +		return NEW_STYLE; +	default: +		return INVALID_STYLE; +	} +} +  static int  nv40_sensor_setup(struct nouveau_therm *therm)  { -	struct nouveau_device *device = nv_device(therm); +	enum nv40_sensor_style style = nv40_sensor_style(therm);  	/* enable ADC readout and disable the ALARM threshold */ -	if (device->chipset >= 0x46) { +	if (style == NEW_STYLE) {  		nv_mask(therm, 0x15b8, 0x80000000, 0);  		nv_wr32(therm, 0x15b0, 0x80003fff); -		mdelay(10); /* wait for the temperature to stabilize */ +		mdelay(20); /* wait for the temperature to stabilize */  		return nv_rd32(therm, 0x15b4) & 0x3fff; -	} else { +	} else if (style == OLD_STYLE) {  		nv_wr32(therm, 0x15b0, 0xff); +		mdelay(20); /* wait for the temperature to stabilize */  		return nv_rd32(therm, 0x15b4) & 0xff; -	} +	} else +		return -ENODEV;  }  static int  nv40_temp_get(struct nouveau_therm *therm)  {  	struct nouveau_therm_priv *priv = (void *)therm; -	struct nouveau_device *device = nv_device(therm);  	struct nvbios_therm_sensor *sensor = &priv->bios_sensor; +	enum nv40_sensor_style style = nv40_sensor_style(therm);  	int core_temp; -	if (device->chipset >= 0x46) { +	if (style == NEW_STYLE) {  		nv_wr32(therm, 0x15b0, 0x80003fff);  		core_temp = nv_rd32(therm, 0x15b4) & 0x3fff; -	} else { +	} else if (style == OLD_STYLE) {  		nv_wr32(therm, 0x15b0, 0xff);  		core_temp = nv_rd32(therm, 0x15b4) & 0xff; -	} - -	/* Setup the sensor if the temperature is 0 */ -	if (core_temp == 0) -		core_temp = nv40_sensor_setup(therm); +	} else +		return -ENODEV; -	if (sensor->slope_div == 0) -		sensor->slope_div = 1; -	if (sensor->offset_den == 0) -		sensor->offset_den = 1; -	if (sensor->slope_mult < 1) -		sensor->slope_mult = 1; +	/* if the slope or the offset is unset, do no use the sensor */ +	if (!sensor->slope_div || !sensor->slope_mult || +	    !sensor->offset_num || !sensor->offset_den) +	    return -ENODEV;  	core_temp = core_temp * sensor->slope_mult / sensor->slope_div;  	core_temp = core_temp + sensor->offset_num / sensor->offset_den;  	core_temp = core_temp + sensor->offset_constant - 8; +	/* reserve negative temperatures for errors */ +	if (core_temp < 0) +		core_temp = 0; +  	return core_temp;  } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index 06b98706b3f..438d9824b77 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -102,7 +102,7 @@ struct nouveau_therm_priv {  	struct i2c_client *ic;  }; -int nouveau_therm_mode(struct nouveau_therm *therm, int mode); +int nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode);  int nouveau_therm_attr_get(struct nouveau_therm *therm,  		       enum nouveau_therm_attr_type type);  int nouveau_therm_attr_set(struct nouveau_therm *therm, @@ -122,6 +122,7 @@ int nouveau_therm_fan_sense(struct nouveau_therm *therm);  int nouveau_therm_preinit(struct nouveau_therm *); +void nouveau_therm_sensor_preinit(struct nouveau_therm *);  void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,  					     enum nouveau_therm_thrs thrs,  					     enum nouveau_therm_thrs_state st); diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c index b37624af829..470f6a47b65 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c @@ -34,10 +34,6 @@ nouveau_therm_temp_set_defaults(struct nouveau_therm *therm)  {  	struct nouveau_therm_priv *priv = (void *)therm; -	priv->bios_sensor.slope_mult = 1; -	priv->bios_sensor.slope_div = 1; -	priv->bios_sensor.offset_num = 0; -	priv->bios_sensor.offset_den = 1;  	priv->bios_sensor.offset_constant = 0;  	priv->bios_sensor.thrs_fan_boost.temp = 90; @@ -60,11 +56,6 @@ nouveau_therm_temp_safety_checks(struct nouveau_therm *therm)  	struct nouveau_therm_priv *priv = (void *)therm;  	struct nvbios_therm_sensor *s = &priv->bios_sensor; -	if (!priv->bios_sensor.slope_div) -		priv->bios_sensor.slope_div = 1; -	if (!priv->bios_sensor.offset_den) -		priv->bios_sensor.offset_den = 1; -  	/* enforce a minimum hysteresis on thresholds */  	s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);  	s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2); @@ -106,16 +97,16 @@ void nouveau_therm_sensor_event(struct nouveau_therm *therm,  	const char *thresolds[] = {  		"fanboost", "downclock", "critical", "shutdown"  	}; -	uint8_t temperature = therm->temp_get(therm); +	int temperature = therm->temp_get(therm);  	if (thrs < 0 || thrs > 3)  		return;  	if (dir == NOUVEAU_THERM_THRS_FALLING) -		nv_info(therm, "temperature (%u C) went below the '%s' threshold\n", +		nv_info(therm, "temperature (%i C) went below the '%s' threshold\n",  			temperature, thresolds[thrs]);  	else -		nv_info(therm, "temperature (%u C) hit the '%s' threshold\n", +		nv_info(therm, "temperature (%i C) hit the '%s' threshold\n",  			temperature, thresolds[thrs]);  	active = (dir == NOUVEAU_THERM_THRS_RISING); @@ -123,7 +114,7 @@ void nouveau_therm_sensor_event(struct nouveau_therm *therm,  	case NOUVEAU_THERM_THRS_FANBOOST:  		if (active) {  			nouveau_therm_fan_set(therm, true, 100); -			nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_AUTO); +			nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_AUTO);  		}  		break;  	case NOUVEAU_THERM_THRS_DOWNCLOCK: @@ -202,7 +193,7 @@ alarm_timer_callback(struct nouveau_alarm *alarm)  					     NOUVEAU_THERM_THRS_SHUTDOWN);  	/* schedule the next poll in one second */ -	if (list_empty(&alarm->head)) +	if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head))  		ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm);  	spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); @@ -225,6 +216,17 @@ nouveau_therm_program_alarms_polling(struct nouveau_therm *therm)  	alarm_timer_callback(&priv->sensor.therm_poll_alarm);  } +void +nouveau_therm_sensor_preinit(struct nouveau_therm *therm) +{ +	const char *sensor_avail = "yes"; + +	if (therm->temp_get(therm) < 0) +		sensor_avail = "no"; + +	nv_info(therm, "internal sensor: %s\n", sensor_avail); +} +  int  nouveau_therm_sensor_ctor(struct nouveau_therm *therm)  { diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 41241922263..3b6dc883e15 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -116,6 +116,11 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,  {  	struct nouveau_abi16_ntfy *ntfy, *temp; +	/* wait for all activity to stop before releasing notify object, which +	 * may be still in use */ +	if (chan->chan && chan->ntfy) +		nouveau_channel_idle(chan->chan); +  	/* cleanup notifier state */  	list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {  		nouveau_abi16_ntfy_fini(chan, ntfy); diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 11ca82148ed..7ff10711a4d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -801,7 +801,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,  		stride  = 16 * 4;  		height  = amount / stride; -		if (new_mem->mem_type == TTM_PL_VRAM && +		if (old_mem->mem_type == TTM_PL_VRAM &&  		    nouveau_bo_tile_layout(nvbo)) {  			ret = RING_SPACE(chan, 8);  			if (ret) @@ -823,7 +823,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,  			BEGIN_NV04(chan, NvSubCopy, 0x0200, 1);  			OUT_RING  (chan, 1);  		} -		if (old_mem->mem_type == TTM_PL_VRAM && +		if (new_mem->mem_type == TTM_PL_VRAM &&  		    nouveau_bo_tile_layout(nvbo)) {  			ret = RING_SPACE(chan, 8);  			if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index bb54098c6d9..936b442a6ab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -402,8 +402,12 @@ nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)  	struct drm_device *dev = dev_get_drvdata(d);  	struct nouveau_drm *drm = nouveau_drm(dev);  	struct nouveau_therm *therm = nouveau_therm(drm->device); +	int temp = therm->temp_get(therm); -	return snprintf(buf, PAGE_SIZE, "%d\n", therm->temp_get(therm) * 1000); +	if (temp < 0) +		return temp; + +	return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000);  }  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,  						  NULL, 0); @@ -871,7 +875,12 @@ static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR,  			  nouveau_hwmon_get_pwm1_max,  			  nouveau_hwmon_set_pwm1_max, 0); -static struct attribute *hwmon_attributes[] = { +static struct attribute *hwmon_default_attributes[] = { +	&sensor_dev_attr_name.dev_attr.attr, +	&sensor_dev_attr_update_rate.dev_attr.attr, +	NULL +}; +static struct attribute *hwmon_temp_attributes[] = {  	&sensor_dev_attr_temp1_input.dev_attr.attr,  	&sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,  	&sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, @@ -882,8 +891,6 @@ static struct attribute *hwmon_attributes[] = {  	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,  	&sensor_dev_attr_temp1_emergency.dev_attr.attr,  	&sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, -	&sensor_dev_attr_name.dev_attr.attr, -	&sensor_dev_attr_update_rate.dev_attr.attr,  	NULL  };  static struct attribute *hwmon_fan_rpm_attributes[] = { @@ -898,8 +905,11 @@ static struct attribute *hwmon_pwm_fan_attributes[] = {  	NULL  }; -static const struct attribute_group hwmon_attrgroup = { -	.attrs = hwmon_attributes, +static const struct attribute_group hwmon_default_attrgroup = { +	.attrs = hwmon_default_attributes, +}; +static const struct attribute_group hwmon_temp_attrgroup = { +	.attrs = hwmon_temp_attributes,  };  static const struct attribute_group hwmon_fan_rpm_attrgroup = {  	.attrs = hwmon_fan_rpm_attributes, @@ -931,13 +941,22 @@ nouveau_hwmon_init(struct drm_device *dev)  	}  	dev_set_drvdata(hwmon_dev, dev); -	/* default sysfs entries */ -	ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup); +	/* set the default attributes */ +	ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup);  	if (ret) {  		if (ret)  			goto error;  	} +	/* if the card has a working thermal sensor */ +	if (therm->temp_get(therm) >= 0) { +		ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); +		if (ret) { +			if (ret) +				goto error; +		} +	} +  	/* if the card has a pwm fan */  	/*XXX: incorrect, need better detection for this, some boards have  	 *     the gpio entries for pwm fan control even when there's no @@ -979,11 +998,10 @@ nouveau_hwmon_fini(struct drm_device *dev)  	struct nouveau_pm *pm = nouveau_pm(dev);  	if (pm->hwmon) { -		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup); -		sysfs_remove_group(&pm->hwmon->kobj, -				   &hwmon_pwm_fan_attrgroup); -		sysfs_remove_group(&pm->hwmon->kobj, -				   &hwmon_fan_rpm_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_default_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_temp_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup);  		hwmon_device_unregister(pm->hwmon);  	} diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 87a5a56ed35..7f0e6c3f37d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -524,6 +524,8 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  	swap_interval <<= 4;  	if (swap_interval == 0)  		swap_interval |= 0x100; +	if (chan == NULL) +		evo_sync(crtc->dev);  	push = evo_wait(sync, 128);  	if (unlikely(push == NULL)) @@ -586,8 +588,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  		sync->addr ^= 0x10;  		sync->data++;  		FIRE_RING (chan); -	} else { -		evo_sync(crtc->dev);  	}  	/* queue the flip */ @@ -2276,6 +2276,7 @@ nv50_display_create(struct drm_device *dev)  			NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n",  				     dcbe->location, dcbe->type,  				     ffs(dcbe->or) - 1, ret); +			ret = 0;  		}  	} diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index d4c633e1286..27769e724b6 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -468,13 +468,19 @@ static void cayman_gpu_init(struct radeon_device *rdev)  		    (rdev->pdev->device == 0x9907) ||  		    (rdev->pdev->device == 0x9908) ||  		    (rdev->pdev->device == 0x9909) || +		    (rdev->pdev->device == 0x990B) || +		    (rdev->pdev->device == 0x990C) || +		    (rdev->pdev->device == 0x990F) ||  		    (rdev->pdev->device == 0x9910) || -		    (rdev->pdev->device == 0x9917)) { +		    (rdev->pdev->device == 0x9917) || +		    (rdev->pdev->device == 0x9999)) {  			rdev->config.cayman.max_simds_per_se = 6;  			rdev->config.cayman.max_backends_per_se = 2;  		} else if ((rdev->pdev->device == 0x9903) ||  			   (rdev->pdev->device == 0x9904) ||  			   (rdev->pdev->device == 0x990A) || +			   (rdev->pdev->device == 0x990D) || +			   (rdev->pdev->device == 0x990E) ||  			   (rdev->pdev->device == 0x9913) ||  			   (rdev->pdev->device == 0x9918)) {  			rdev->config.cayman.max_simds_per_se = 4; @@ -483,6 +489,9 @@ static void cayman_gpu_init(struct radeon_device *rdev)  			   (rdev->pdev->device == 0x9990) ||  			   (rdev->pdev->device == 0x9991) ||  			   (rdev->pdev->device == 0x9994) || +			   (rdev->pdev->device == 0x9995) || +			   (rdev->pdev->device == 0x9996) || +			   (rdev->pdev->device == 0x999A) ||  			   (rdev->pdev->device == 0x99A0)) {  			rdev->config.cayman.max_simds_per_se = 3;  			rdev->config.cayman.max_backends_per_se = 1; @@ -616,11 +625,22 @@ static void cayman_gpu_init(struct radeon_device *rdev)  	WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config);  	WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config); -	tmp = gb_addr_config & NUM_PIPES_MASK; -	tmp = r6xx_remap_render_backend(rdev, tmp, -					rdev->config.cayman.max_backends_per_se * -					rdev->config.cayman.max_shader_engines, -					CAYMAN_MAX_BACKENDS, disabled_rb_mask); +	if ((rdev->config.cayman.max_backends_per_se == 1) && +	    (rdev->flags & RADEON_IS_IGP)) { +		if ((disabled_rb_mask & 3) == 1) { +			/* RB0 disabled, RB1 enabled */ +			tmp = 0x11111111; +		} else { +			/* RB1 disabled, RB0 enabled */ +			tmp = 0x00000000; +		} +	} else { +		tmp = gb_addr_config & NUM_PIPES_MASK; +		tmp = r6xx_remap_render_backend(rdev, tmp, +						rdev->config.cayman.max_backends_per_se * +						rdev->config.cayman.max_shader_engines, +						CAYMAN_MAX_BACKENDS, disabled_rb_mask); +	}  	WREG32(GB_BACKEND_MAP, tmp);  	cgts_tcc_disable = 0xffff0000; @@ -1771,6 +1791,7 @@ int cayman_resume(struct radeon_device *rdev)  int cayman_suspend(struct radeon_device *rdev)  {  	r600_audio_fini(rdev); +	radeon_vm_manager_fini(rdev);  	cayman_cp_enable(rdev, false);  	cayman_dma_stop(rdev);  	evergreen_irq_suspend(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index bedda9caadd..6e05a2e75a4 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -122,10 +122,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,  		goto out_cleanup;  	} -	/* r100 doesn't have dma engine so skip the test */ -	/* also, VRAM-to-VRAM test doesn't make much sense for DMA */ -	/* skip it as well if domains are the same */ -	if ((rdev->asic->copy.dma) && (sdomain != ddomain)) { +	if (rdev->asic->copy.dma) {  		time = radeon_benchmark_do_move(rdev, size, saddr, daddr,  						RADEON_BENCHMARK_COPY_DMA, n);  		if (time < 0) @@ -135,13 +132,15 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,  						     sdomain, ddomain, "dma");  	} -	time = radeon_benchmark_do_move(rdev, size, saddr, daddr, -					RADEON_BENCHMARK_COPY_BLIT, n); -	if (time < 0) -		goto out_cleanup; -	if (time > 0) -		radeon_benchmark_log_results(n, size, time, -					     sdomain, ddomain, "blit"); +	if (rdev->asic->copy.blit) { +		time = radeon_benchmark_do_move(rdev, size, saddr, daddr, +						RADEON_BENCHMARK_COPY_BLIT, n); +		if (time < 0) +			goto out_cleanup; +		if (time > 0) +			radeon_benchmark_log_results(n, size, time, +						     sdomain, ddomain, "blit"); +	}  out_cleanup:  	if (sobj) { diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 9128120da04..bafbe321695 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -4469,6 +4469,7 @@ int si_resume(struct radeon_device *rdev)  int si_suspend(struct radeon_device *rdev)  { +	radeon_vm_manager_fini(rdev);  	si_cp_enable(rdev, false);  	cayman_dma_stop(rdev);  	si_irq_suspend(rdev); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92e47e5c956..c4388776f4e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -590,6 +590,9 @@  #define USB_VENDOR_ID_MONTEREY		0x0566  #define USB_DEVICE_ID_GENIUS_KB29E	0x3004 +#define USB_VENDOR_ID_MSI		0x1770 +#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL	0xff00 +  #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400  #define USB_DEVICE_ID_N_S_HARMONY	0xc359 @@ -684,6 +687,9 @@  #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001		0x3001  #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008		0x3008 +#define USB_VENDOR_ID_REALTEK		0x0bda +#define USB_DEVICE_ID_REALTEK_READER	0x0152 +  #define USB_VENDOR_ID_ROCCAT		0x1e7d  #define USB_DEVICE_ID_ROCCAT_ARVO	0x30d4  #define USB_DEVICE_ID_ROCCAT_ISKU	0x319c diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 7a1ebb867cf..82e9211b3ca 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -621,6 +621,7 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,  {  	struct mt_device *td = hid_get_drvdata(hid);  	__s32 quirks = td->mtclass.quirks; +	struct input_dev *input = field->hidinput->input;  	if (hid->claimed & HID_CLAIMED_INPUT) {  		switch (usage->hid) { @@ -670,13 +671,16 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,  			break;  		default: +			if (usage->type) +				input_event(input, usage->type, usage->code, +						value);  			return;  		}  		if (usage->usage_index + 1 == field->report_count) {  			/* we only take into account the last report. */  			if (usage->hid == td->last_slot_field) -				mt_complete_slot(td, field->hidinput->input); +				mt_complete_slot(td, input);  			if (field->index == td->last_field_index  				&& td->num_received >= td->num_expected) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index e0e6abf1cd3..19b8360f233 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -73,6 +73,7 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, +	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, @@ -80,6 +81,7 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET }, +	{ USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c index 41df29f59b0..ebbb9f4f27a 100644 --- a/drivers/hwmon/lineage-pem.c +++ b/drivers/hwmon/lineage-pem.c @@ -422,6 +422,7 @@ static struct attribute *pem_input_attributes[] = {  	&sensor_dev_attr_in2_input.dev_attr.attr,  	&sensor_dev_attr_curr1_input.dev_attr.attr,  	&sensor_dev_attr_power1_input.dev_attr.attr, +	NULL  };  static const struct attribute_group pem_input_group = { @@ -432,6 +433,7 @@ static struct attribute *pem_fan_attributes[] = {  	&sensor_dev_attr_fan1_input.dev_attr.attr,  	&sensor_dev_attr_fan2_input.dev_attr.attr,  	&sensor_dev_attr_fan3_input.dev_attr.attr, +	NULL  };  static const struct attribute_group pem_fan_group = { diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h index 668ff472132..5cde94e56f1 100644 --- a/drivers/hwmon/lm75.h +++ b/drivers/hwmon/lm75.h @@ -25,7 +25,7 @@      which contains this code, we don't worry about the wasted space.  */ -#include <linux/hwmon.h> +#include <linux/kernel.h>  /* straight from the datasheet */  #define LM75_TEMP_MIN (-55000) diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index a58de38e23d..6d6130752f9 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -59,7 +59,7 @@ enum chips { ltc2978, ltc3880 };  struct ltc2978_data {  	enum chips id;  	int vin_min, vin_max; -	int temp_min, temp_max; +	int temp_min, temp_max[2];  	int vout_min[8], vout_max[8];  	int iout_max[2];  	int temp2_max; @@ -113,9 +113,10 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page,  		ret = pmbus_read_word_data(client, page,  					   LTC2978_MFR_TEMPERATURE_PEAK);  		if (ret >= 0) { -			if (lin11_to_val(ret) > lin11_to_val(data->temp_max)) -				data->temp_max = ret; -			ret = data->temp_max; +			if (lin11_to_val(ret) +			    > lin11_to_val(data->temp_max[page])) +				data->temp_max[page] = ret; +			ret = data->temp_max[page];  		}  		break;  	case PMBUS_VIRT_RESET_VOUT_HISTORY: @@ -266,7 +267,7 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,  		break;  	case PMBUS_VIRT_RESET_TEMP_HISTORY:  		data->temp_min = 0x7bff; -		data->temp_max = 0x7c00; +		data->temp_max[page] = 0x7c00;  		ret = ltc2978_clear_peaks(client, page, data->id);  		break;  	default: @@ -323,7 +324,8 @@ static int ltc2978_probe(struct i2c_client *client,  	data->vin_min = 0x7bff;  	data->vin_max = 0x7c00;  	data->temp_min = 0x7bff; -	data->temp_max = 0x7c00; +	for (i = 0; i < ARRAY_SIZE(data->temp_max); i++) +		data->temp_max[i] = 0x7c00;  	data->temp2_max = 0x7c00;  	switch (data->id) { diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 80eef50c50f..9add60920ac 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -766,12 +766,14 @@ static ssize_t pmbus_show_label(struct device *dev,  static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr)  {  	if (data->num_attributes >= data->max_attributes - 1) { -		data->max_attributes += PMBUS_ATTR_ALLOC_SIZE; -		data->group.attrs = krealloc(data->group.attrs, -					     sizeof(struct attribute *) * -					     data->max_attributes, GFP_KERNEL); -		if (data->group.attrs == NULL) +		int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; +		void *new_attrs = krealloc(data->group.attrs, +					   new_max_attrs * sizeof(void *), +					   GFP_KERNEL); +		if (!new_attrs)  			return -ENOMEM; +		data->group.attrs = new_attrs; +		data->max_attributes = new_max_attrs;  	}  	data->group.attrs[data->num_attributes++] = attr; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 46cde098c11..e380c6eef3a 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -4,7 +4,6 @@  menuconfig I2C  	tristate "I2C support" -	depends on !S390  	select RT_MUTEXES  	---help---  	  I2C (pronounce: I-squared-C) is a slow serial bus protocol used in @@ -76,6 +75,7 @@ config I2C_HELPER_AUTO  config I2C_SMBUS  	tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO +	depends on GENERIC_HARDIRQS  	help  	  Say Y here if you want support for SMBus extensions to the I2C  	  specification. At the moment, the only supported extension is diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a3725de9238..adfee98486b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -114,7 +114,7 @@ config I2C_I801  config I2C_ISCH  	tristate "Intel SCH SMBus 1.0" -	depends on PCI +	depends on PCI && GENERIC_HARDIRQS  	select LPC_SCH  	help  	  Say Y here if you want to use SMBus controller on the Intel SCH @@ -543,6 +543,7 @@ config I2C_NUC900  config I2C_OCORES  	tristate "OpenCores I2C Controller" +	depends on GENERIC_HARDIRQS  	help  	  If you say yes to this option, support will be included for the  	  OpenCores I2C controller. For details see @@ -777,7 +778,7 @@ config I2C_DIOLAN_U2C  config I2C_PARPORT  	tristate "Parallel port adapter" -	depends on PARPORT +	depends on PARPORT && GENERIC_HARDIRQS  	select I2C_ALGOBIT  	select I2C_SMBUS  	help @@ -802,6 +803,7 @@ config I2C_PARPORT  config I2C_PARPORT_LIGHT  	tristate "Parallel port adapter (light)" +	depends on GENERIC_HARDIRQS  	select I2C_ALGOBIT  	select I2C_SMBUS  	help diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index e9205ee8cf9..130f02cc9d9 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -80,6 +80,7 @@  /* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */  #define PCI_DEVICE_ID_INTEL_S1200_SMT0	0x0c59  #define PCI_DEVICE_ID_INTEL_S1200_SMT1	0x0c5a +#define PCI_DEVICE_ID_INTEL_AVOTON_SMT	0x1f15  #define ISMT_DESC_ENTRIES	32	/* number of descriptor entries */  #define ISMT_MAX_RETRIES	3	/* number of SMBus retries to attempt */ @@ -185,6 +186,7 @@ struct ismt_priv {  static const DEFINE_PCI_DEVICE_TABLE(ismt_ids) = {  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) }, +	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },  	{ 0, }  }; diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 36704e3ab3f..b714776b6dd 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -411,7 +411,11 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)  	int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;  	u32 clk_divisor; -	tegra_i2c_clock_enable(i2c_dev); +	err = tegra_i2c_clock_enable(i2c_dev); +	if (err < 0) { +		dev_err(i2c_dev->dev, "Clock enable failed %d\n", err); +		return err; +	}  	tegra_periph_reset_assert(i2c_dev->div_clk);  	udelay(2); @@ -628,7 +632,12 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],  	if (i2c_dev->is_suspended)  		return -EBUSY; -	tegra_i2c_clock_enable(i2c_dev); +	ret = tegra_i2c_clock_enable(i2c_dev); +	if (ret < 0) { +		dev_err(i2c_dev->dev, "Clock enable failed %d\n", ret); +		return ret; +	} +  	for (i = 0; i < num; i++) {  		enum msg_end_type end_type = MSG_END_STOP;  		if (i < (num - 1)) { diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index f3b8f9a6a89..966a18a5d12 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -3,7 +3,7 @@   *   * Copyright (c) 2010 Ericsson AB.   * - * Author: Guenter Roeck <guenter.roeck@ericsson.com> + * Author: Guenter Roeck <linux@roeck-us.net>   *   * Derived from:   *  pca954x.c diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 0198324a8b0..bd33473f8e3 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -62,7 +62,7 @@ st_sensors_match_odr_error:  int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)  {  	int err; -	struct st_sensor_odr_avl odr_out; +	struct st_sensor_odr_avl odr_out = {0, 0};  	struct st_sensor_data *sdata = iio_priv(indio_dev);  	err = st_sensors_match_odr(sdata->sensor, odr, &odr_out); @@ -114,7 +114,7 @@ st_sensors_match_odr_error:  static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs)  { -	int err, i; +	int err, i = 0;  	struct st_sensor_data *sdata = iio_priv(indio_dev);  	err = st_sensors_match_fs(sdata->sensor, fs, &i); @@ -139,14 +139,13 @@ st_accel_set_fullscale_error:  int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)  { -	bool found;  	u8 tmp_value;  	int err = -EINVAL; -	struct st_sensor_odr_avl odr_out; +	bool found = false; +	struct st_sensor_odr_avl odr_out = {0, 0};  	struct st_sensor_data *sdata = iio_priv(indio_dev);  	if (enable) { -		found = false;  		tmp_value = sdata->sensor->pw.value_on;  		if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&  			(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index 2fe1d4edcb2..74f2d52795f 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -27,7 +27,6 @@  #define AD5064_ADDR(x)				((x) << 20)  #define AD5064_CMD(x)				((x) << 24) -#define AD5064_ADDR_DAC(chan)			(chan)  #define AD5064_ADDR_ALL_DAC			0xF  #define AD5064_CMD_WRITE_INPUT_N		0x0 @@ -131,15 +130,15 @@ static int ad5064_write(struct ad5064_state *st, unsigned int cmd,  }  static int ad5064_sync_powerdown_mode(struct ad5064_state *st, -	unsigned int channel) +	const struct iio_chan_spec *chan)  {  	unsigned int val;  	int ret; -	val = (0x1 << channel); +	val = (0x1 << chan->address); -	if (st->pwr_down[channel]) -		val |= st->pwr_down_mode[channel] << 8; +	if (st->pwr_down[chan->channel]) +		val |= st->pwr_down_mode[chan->channel] << 8;  	ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0); @@ -169,7 +168,7 @@ static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev,  	mutex_lock(&indio_dev->mlock);  	st->pwr_down_mode[chan->channel] = mode + 1; -	ret = ad5064_sync_powerdown_mode(st, chan->channel); +	ret = ad5064_sync_powerdown_mode(st, chan);  	mutex_unlock(&indio_dev->mlock);  	return ret; @@ -205,7 +204,7 @@ static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,  	mutex_lock(&indio_dev->mlock);  	st->pwr_down[chan->channel] = pwr_down; -	ret = ad5064_sync_powerdown_mode(st, chan->channel); +	ret = ad5064_sync_powerdown_mode(st, chan);  	mutex_unlock(&indio_dev->mlock);  	return ret ? ret : len;  } @@ -258,7 +257,7 @@ static int ad5064_write_raw(struct iio_dev *indio_dev,  	switch (mask) {  	case IIO_CHAN_INFO_RAW: -		if (val > (1 << chan->scan_type.realbits) || val < 0) +		if (val >= (1 << chan->scan_type.realbits) || val < 0)  			return -EINVAL;  		mutex_lock(&indio_dev->mlock); @@ -292,34 +291,44 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {  	{ },  }; -#define AD5064_CHANNEL(chan, bits) {				\ +#define AD5064_CHANNEL(chan, addr, bits) {			\  	.type = IIO_VOLTAGE,					\  	.indexed = 1,						\  	.output = 1,						\  	.channel = (chan),					\  	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |		\  	IIO_CHAN_INFO_SCALE_SEPARATE_BIT,			\ -	.address = AD5064_ADDR_DAC(chan),			\ +	.address = addr,					\  	.scan_type = IIO_ST('u', (bits), 16, 20 - (bits)),	\  	.ext_info = ad5064_ext_info,				\  }  #define DECLARE_AD5064_CHANNELS(name, bits) \  const struct iio_chan_spec name[] = { \ -	AD5064_CHANNEL(0, bits), \ -	AD5064_CHANNEL(1, bits), \ -	AD5064_CHANNEL(2, bits), \ -	AD5064_CHANNEL(3, bits), \ -	AD5064_CHANNEL(4, bits), \ -	AD5064_CHANNEL(5, bits), \ -	AD5064_CHANNEL(6, bits), \ -	AD5064_CHANNEL(7, bits), \ +	AD5064_CHANNEL(0, 0, bits), \ +	AD5064_CHANNEL(1, 1, bits), \ +	AD5064_CHANNEL(2, 2, bits), \ +	AD5064_CHANNEL(3, 3, bits), \ +	AD5064_CHANNEL(4, 4, bits), \ +	AD5064_CHANNEL(5, 5, bits), \ +	AD5064_CHANNEL(6, 6, bits), \ +	AD5064_CHANNEL(7, 7, bits), \ +} + +#define DECLARE_AD5065_CHANNELS(name, bits) \ +const struct iio_chan_spec name[] = { \ +	AD5064_CHANNEL(0, 0, bits), \ +	AD5064_CHANNEL(1, 3, bits), \  }  static DECLARE_AD5064_CHANNELS(ad5024_channels, 12);  static DECLARE_AD5064_CHANNELS(ad5044_channels, 14);  static DECLARE_AD5064_CHANNELS(ad5064_channels, 16); +static DECLARE_AD5065_CHANNELS(ad5025_channels, 12); +static DECLARE_AD5065_CHANNELS(ad5045_channels, 14); +static DECLARE_AD5065_CHANNELS(ad5065_channels, 16); +  static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {  	[ID_AD5024] = {  		.shared_vref = false, @@ -328,7 +337,7 @@ static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {  	},  	[ID_AD5025] = {  		.shared_vref = false, -		.channels = ad5024_channels, +		.channels = ad5025_channels,  		.num_channels = 2,  	},  	[ID_AD5044] = { @@ -338,7 +347,7 @@ static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {  	},  	[ID_AD5045] = {  		.shared_vref = false, -		.channels = ad5044_channels, +		.channels = ad5045_channels,  		.num_channels = 2,  	},  	[ID_AD5064] = { @@ -353,7 +362,7 @@ static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {  	},  	[ID_AD5065] = {  		.shared_vref = false, -		.channels = ad5064_channels, +		.channels = ad5065_channels,  		.num_channels = 2,  	},  	[ID_AD5628_1] = { @@ -429,6 +438,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,  {  	struct iio_dev *indio_dev;  	struct ad5064_state *st; +	unsigned int midscale;  	unsigned int i;  	int ret; @@ -465,11 +475,6 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,  			goto error_free_reg;  	} -	for (i = 0; i < st->chip_info->num_channels; ++i) { -		st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K; -		st->dac_cache[i] = 0x8000; -	} -  	indio_dev->dev.parent = dev;  	indio_dev->name = name;  	indio_dev->info = &ad5064_info; @@ -477,6 +482,13 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,  	indio_dev->channels = st->chip_info->channels;  	indio_dev->num_channels = st->chip_info->num_channels; +	midscale = (1 << indio_dev->channels[0].scan_type.realbits) /  2; + +	for (i = 0; i < st->chip_info->num_channels; ++i) { +		st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K; +		st->dac_cache[i] = midscale; +	} +  	ret = iio_device_register(indio_dev);  	if (ret)  		goto error_disable_reg; diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index b5cfa3a354c..361b2328453 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -5,6 +5,7 @@  config INV_MPU6050_IIO  	tristate "Invensense MPU6050 devices"  	depends on I2C && SYSFS +	select IIO_BUFFER  	select IIO_TRIGGERED_BUFFER  	help  	  This driver supports the Invensense MPU6050 devices. diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 565bfb161c1..a3fde52840c 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1575,6 +1575,12 @@ static int c4iw_reconnect(struct c4iw_ep *ep)  	neigh = dst_neigh_lookup(ep->dst,  			&ep->com.cm_id->remote_addr.sin_addr.s_addr); +	if (!neigh) { +		pr_err("%s - cannot alloc neigh.\n", __func__); +		err = -ENOMEM; +		goto fail4; +	} +  	/* get a l2t entry */  	if (neigh->dev->flags & IFF_LOOPBACK) {  		PDBG("%s LOOPBACK\n", __func__); @@ -3053,6 +3059,12 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)  	dst = &rt->dst;  	neigh = dst_neigh_lookup_skb(dst, skb); +	if (!neigh) { +		pr_err("%s - failed to allocate neigh!\n", +		       __func__); +		goto free_dst; +	} +  	if (neigh->dev->flags & IFF_LOOPBACK) {  		pdev = ip_dev_find(&init_net, iph->daddr);  		e = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 17ba4f8bc12..70b1808a08f 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -186,8 +186,10 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,  	wq->rq.queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev),  					  wq->rq.memsize, &(wq->rq.dma_addr),  					  GFP_KERNEL); -	if (!wq->rq.queue) +	if (!wq->rq.queue) { +		ret = -ENOMEM;  		goto free_sq; +	}  	PDBG("%s sq base va 0x%p pa 0x%llx rq base va 0x%p pa 0x%llx\n",  		__func__, wq->sq.queue,  		(unsigned long long)virt_to_phys(wq->sq.queue), diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index 439c35d4a66..ea93870266e 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -620,7 +620,7 @@ void ipath_ib_rcv(struct ipath_ibdev *dev, void *rhdr, void *data,  		goto bail;  	} -	opcode = be32_to_cpu(ohdr->bth[0]) >> 24; +	opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0x7f;  	dev->opstats[opcode].n_bytes += tlen;  	dev->opstats[opcode].n_packets++; diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c index e0d79b2395e..add98d01476 100644 --- a/drivers/infiniband/hw/mlx4/cm.c +++ b/drivers/infiniband/hw/mlx4/cm.c @@ -362,7 +362,6 @@ void mlx4_ib_cm_paravirt_init(struct mlx4_ib_dev *dev)  	INIT_LIST_HEAD(&dev->sriov.cm_list);  	dev->sriov.sl_id_map = RB_ROOT;  	idr_init(&dev->sriov.pv_id_table); -	idr_pre_get(&dev->sriov.pv_id_table, GFP_KERNEL);  }  /* slave = -1 ==> all slaves */ diff --git a/drivers/infiniband/hw/qib/Kconfig b/drivers/infiniband/hw/qib/Kconfig index 8349f9c5064..1e603a37506 100644 --- a/drivers/infiniband/hw/qib/Kconfig +++ b/drivers/infiniband/hw/qib/Kconfig @@ -1,7 +1,7 @@  config INFINIBAND_QIB -	tristate "QLogic PCIe HCA support" +	tristate "Intel PCIe HCA support"  	depends on 64BIT  	---help--- -	This is a low-level driver for QLogic PCIe QLE InfiniBand host -	channel adapters.  This driver does not support the QLogic +	This is a low-level driver for Intel PCIe QLE InfiniBand host +	channel adapters.  This driver does not support the Intel  	HyperTransport card (model QHT7140). diff --git a/drivers/infiniband/hw/qib/qib_driver.c b/drivers/infiniband/hw/qib/qib_driver.c index 5423edcab51..216092477df 100644 --- a/drivers/infiniband/hw/qib/qib_driver.c +++ b/drivers/infiniband/hw/qib/qib_driver.c @@ -1,4 +1,5 @@  /* + * Copyright (c) 2013 Intel Corporation. All rights reserved.   * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved.   * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.   * @@ -63,8 +64,8 @@ MODULE_PARM_DESC(compat_ddr_negotiate,  		 "Attempt pre-IBTA 1.2 DDR speed negotiation");  MODULE_LICENSE("Dual BSD/GPL"); -MODULE_AUTHOR("QLogic <support@qlogic.com>"); -MODULE_DESCRIPTION("QLogic IB driver"); +MODULE_AUTHOR("Intel <ibsupport@intel.com>"); +MODULE_DESCRIPTION("Intel IB driver");  MODULE_VERSION(QIB_DRIVER_VERSION);  /* diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c index a099ac171e2..0232ae56b1f 100644 --- a/drivers/infiniband/hw/qib/qib_iba6120.c +++ b/drivers/infiniband/hw/qib/qib_iba6120.c @@ -1,4 +1,5 @@  /* + * Copyright (c) 2013 Intel Corporation. All rights reserved.   * Copyright (c) 2006, 2007, 2008, 2009, 2010 QLogic Corporation.   * All rights reserved.   * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. @@ -51,7 +52,7 @@ static u32 qib_6120_iblink_state(u64);  /*   * This file contains all the chip-specific register information and - * access functions for the QLogic QLogic_IB PCI-Express chip. + * access functions for the Intel Intel_IB PCI-Express chip.   *   */ diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c index 50e33aa0b4e..173f805790d 100644 --- a/drivers/infiniband/hw/qib/qib_init.c +++ b/drivers/infiniband/hw/qib/qib_init.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 2012 Intel Corporation.  All rights reserved. + * Copyright (c) 2012, 2013 Intel Corporation.  All rights reserved.   * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.   * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.   * @@ -1138,7 +1138,7 @@ void qib_disable_after_error(struct qib_devdata *dd)  static void qib_remove_one(struct pci_dev *);  static int qib_init_one(struct pci_dev *, const struct pci_device_id *); -#define DRIVER_LOAD_MSG "QLogic " QIB_DRV_NAME " loaded: " +#define DRIVER_LOAD_MSG "Intel " QIB_DRV_NAME " loaded: "  #define PFX QIB_DRV_NAME ": "  static DEFINE_PCI_DEVICE_TABLE(qib_pci_tbl) = { @@ -1355,7 +1355,7 @@ static int qib_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  		dd = qib_init_iba6120_funcs(pdev, ent);  #else  		qib_early_err(&pdev->dev, -			"QLogic PCIE device 0x%x cannot work if CONFIG_PCI_MSI is not enabled\n", +			"Intel PCIE device 0x%x cannot work if CONFIG_PCI_MSI is not enabled\n",  			ent->device);  		dd = ERR_PTR(-ENODEV);  #endif @@ -1371,7 +1371,7 @@ static int qib_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  	default:  		qib_early_err(&pdev->dev, -			"Failing on unknown QLogic deviceid 0x%x\n", +			"Failing on unknown Intel deviceid 0x%x\n",  			ent->device);  		ret = -ENODEV;  	} diff --git a/drivers/infiniband/hw/qib/qib_sd7220.c b/drivers/infiniband/hw/qib/qib_sd7220.c index 50a8a0d4fe6..08a6c6d39e5 100644 --- a/drivers/infiniband/hw/qib/qib_sd7220.c +++ b/drivers/infiniband/hw/qib/qib_sd7220.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 2012 Intel Corporation. All rights reserved. + * Copyright (c) 2013 Intel Corporation. All rights reserved.   * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.   * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.   * @@ -44,7 +44,7 @@  #include "qib.h"  #include "qib_7220.h" -#define SD7220_FW_NAME "qlogic/sd7220.fw" +#define SD7220_FW_NAME "intel/sd7220.fw"  MODULE_FIRMWARE(SD7220_FW_NAME);  /* diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c index ba51a4715a1..7c0ab16a2fe 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.c +++ b/drivers/infiniband/hw/qib/qib_verbs.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 2012 Intel Corporation.  All rights reserved. + * Copyright (c) 2012, 2013 Intel Corporation.  All rights reserved.   * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.   * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.   * @@ -2224,7 +2224,7 @@ int qib_register_ib_device(struct qib_devdata *dd)  	ibdev->dma_ops = &qib_dma_mapping_ops;  	snprintf(ibdev->node_desc, sizeof(ibdev->node_desc), -		 "QLogic Infiniband HCA %s", init_utsname()->nodename); +		 "Intel Infiniband HCA %s", init_utsname()->nodename);  	ret = ib_register_device(ibdev, qib_create_port_files);  	if (ret) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 67b0c1d2367..1ef880de3a4 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -758,9 +758,13 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_  		if (++priv->tx_outstanding == ipoib_sendq_size) {  			ipoib_dbg(priv, "TX ring 0x%x full, stopping kernel net queue\n",  				  tx->qp->qp_num); -			if (ib_req_notify_cq(priv->send_cq, IB_CQ_NEXT_COMP)) -				ipoib_warn(priv, "request notify on send CQ failed\n");  			netif_stop_queue(dev); +			rc = ib_req_notify_cq(priv->send_cq, +				IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); +			if (rc < 0) +				ipoib_warn(priv, "request notify on send CQ failed\n"); +			else if (rc) +				ipoib_send_comp_handler(priv->send_cq, dev);  		}  	}  } diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 7cd74e29cbc..9135606c864 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -158,14 +158,10 @@ static unsigned int get_time_pit(void)  #define GET_TIME(x)	rdtscl(x)  #define DELTA(x,y)	((y)-(x))  #define TIME_NAME	"TSC" -#elif defined(__alpha__) +#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_TILE)  #define GET_TIME(x)	do { x = get_cycles(); } while (0)  #define DELTA(x,y)	((y)-(x)) -#define TIME_NAME	"PCC" -#elif defined(CONFIG_MN10300) || defined(CONFIG_TILE) -#define GET_TIME(x)	do { x = get_cycles(); } while (0) -#define DELTA(x, y)	((x) - (y)) -#define TIME_NAME	"TSC" +#define TIME_NAME	"get_cycles"  #else  #define FAKE_TIME  static unsigned long analog_faketime = 0; diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 2fb0d76a04c..208de7cbb7f 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -70,8 +70,6 @@  #define TC3589x_EVT_INT_CLR	0x2  #define TC3589x_KBD_INT_CLR	0x1 -#define TC3589x_KBD_KEYMAP_SIZE     64 -  /**   * struct tc_keypad - data structure used by keypad driver   * @tc3589x:    pointer to tc35893 @@ -88,7 +86,7 @@ struct tc_keypad {  	const struct tc3589x_keypad_platform_data *board;  	unsigned int krow;  	unsigned int kcol; -	unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE]; +	unsigned short *keymap;  	bool keypad_stopped;  }; @@ -338,12 +336,14 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)  	error = matrix_keypad_build_keymap(plat->keymap_data, NULL,  					   TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL, -					   keypad->keymap, input); +					   NULL, input);  	if (error) {  		dev_err(&pdev->dev, "Failed to build keymap\n");  		goto err_free_mem;  	} +	keypad->keymap = input->keycode; +  	input_set_capability(input, EV_MSC, MSC_SCAN);  	if (!plat->no_autorepeat)  		__set_bit(EV_REP, input->evbit); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 7b99fc7c943..0238e0e1433 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -490,6 +490,29 @@ static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p)  	f->y_map |= (p[5] & 0x20) << 6;  } +static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p) +{ +	f->first_mp = !!(p[0] & 0x02); +	f->is_mp = !!(p[0] & 0x20); + +	f->fingers = ((p[0] & 0x6) >> 1 | +		     (p[0] & 0x10) >> 2); +	f->x_map = ((p[2] & 0x60) >> 5) | +		   ((p[4] & 0x7f) << 2) | +		   ((p[5] & 0x7f) << 9) | +		   ((p[3] & 0x07) << 16) | +		   ((p[3] & 0x70) << 15) | +		   ((p[0] & 0x01) << 22); +	f->y_map = (p[1] & 0x7f) | +		   ((p[2] & 0x1f) << 7); + +	f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); +	f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); +	f->z = (p[0] & 4) ? 0 : p[5] & 0x7f; + +	alps_decode_buttons_v3(f, p); +} +  static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)  {  	struct alps_data *priv = psmouse->private; @@ -874,7 +897,8 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)  	}  	/* Bytes 2 - pktsize should have 0 in the highest bit */ -	if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && +	if (priv->proto_version != ALPS_PROTO_V5 && +	    psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&  	    (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {  		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",  			    psmouse->pktcnt - 1, @@ -994,8 +1018,7 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,  	return 0;  } -static int alps_enter_command_mode(struct psmouse *psmouse, -				   unsigned char *resp) +static int alps_enter_command_mode(struct psmouse *psmouse)  {  	unsigned char param[4]; @@ -1004,14 +1027,12 @@ static int alps_enter_command_mode(struct psmouse *psmouse,  		return -1;  	} -	if (param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) { +	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) && +	    param[0] != 0x73) {  		psmouse_dbg(psmouse,  			    "unknown response while entering command mode\n");  		return -1;  	} - -	if (resp) -		*resp = param[2];  	return 0;  } @@ -1176,7 +1197,7 @@ static int alps_passthrough_mode_v3(struct psmouse *psmouse,  {  	int reg_val, ret = -1; -	if (alps_enter_command_mode(psmouse, NULL)) +	if (alps_enter_command_mode(psmouse))  		return -1;  	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008); @@ -1216,7 +1237,7 @@ static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)  {  	int ret = -EIO, reg_val; -	if (alps_enter_command_mode(psmouse, NULL)) +	if (alps_enter_command_mode(psmouse))  		goto error;  	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08); @@ -1279,7 +1300,7 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)  		 * supported by this driver. If bit 1 isn't set the packet  		 * format is different.  		 */ -		if (alps_enter_command_mode(psmouse, NULL) || +		if (alps_enter_command_mode(psmouse) ||  		    alps_command_mode_write_reg(psmouse,  						reg_base + 0x08, 0x82) ||  		    alps_exit_command_mode(psmouse)) @@ -1306,7 +1327,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse)  	    alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)  		goto error; -	if (alps_enter_command_mode(psmouse, NULL) || +	if (alps_enter_command_mode(psmouse) ||  	    alps_absolute_mode_v3(psmouse)) {  		psmouse_err(psmouse, "Failed to enter absolute mode\n");  		goto error; @@ -1381,7 +1402,7 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)  			priv->flags &= ~ALPS_DUALPOINT;  	} -	if (alps_enter_command_mode(psmouse, NULL) || +	if (alps_enter_command_mode(psmouse) ||  	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 ||  	    alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))  		goto error; @@ -1431,7 +1452,7 @@ static int alps_hw_init_v4(struct psmouse *psmouse)  	struct ps2dev *ps2dev = &psmouse->ps2dev;  	unsigned char param[4]; -	if (alps_enter_command_mode(psmouse, NULL)) +	if (alps_enter_command_mode(psmouse))  		goto error;  	if (alps_absolute_mode_v4(psmouse)) { @@ -1499,6 +1520,23 @@ error:  	return -1;  } +static int alps_hw_init_dolphin_v1(struct psmouse *psmouse) +{ +	struct ps2dev *ps2dev = &psmouse->ps2dev; +	unsigned char param[2]; + +	/* This is dolphin "v1" as empirically defined by florin9doi */ +	param[0] = 0x64; +	param[1] = 0x28; + +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || +	    ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || +	    ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) +		return -1; + +	return 0; +} +  static void alps_set_defaults(struct alps_data *priv)  {  	priv->byte0 = 0x8f; @@ -1532,6 +1570,21 @@ static void alps_set_defaults(struct alps_data *priv)  		priv->nibble_commands = alps_v4_nibble_commands;  		priv->addr_command = PSMOUSE_CMD_DISABLE;  		break; +	case ALPS_PROTO_V5: +		priv->hw_init = alps_hw_init_dolphin_v1; +		priv->process_packet = alps_process_packet_v3; +		priv->decode_fields = alps_decode_dolphin; +		priv->set_abs_params = alps_set_abs_params_mt; +		priv->nibble_commands = alps_v3_nibble_commands; +		priv->addr_command = PSMOUSE_CMD_RESET_WRAP; +		priv->byte0 = 0xc8; +		priv->mask0 = 0xc8; +		priv->flags = 0; +		priv->x_max = 1360; +		priv->y_max = 660; +		priv->x_bits = 23; +		priv->y_bits = 12; +		break;  	}  } @@ -1592,6 +1645,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)  	if (alps_match_table(psmouse, priv, e7, ec) == 0) {  		return 0; +	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 && +		   ec[0] == 0x73 && ec[1] == 0x01) { +		priv->proto_version = ALPS_PROTO_V5; +		alps_set_defaults(priv); + +		return 0;  	} else if (ec[0] == 0x88 && ec[1] == 0x08) {  		priv->proto_version = ALPS_PROTO_V3;  		alps_set_defaults(priv); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 970480551b6..eee59853b9c 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -16,6 +16,7 @@  #define ALPS_PROTO_V2	2  #define ALPS_PROTO_V3	3  #define ALPS_PROTO_V4	4 +#define ALPS_PROTO_V5	5  /**   * struct alps_model_info - touchpad ID table diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c index 1673dc6c809..f51765fff05 100644 --- a/drivers/input/mouse/cypress_ps2.c +++ b/drivers/input/mouse/cypress_ps2.c @@ -236,6 +236,13 @@ static int cypress_read_fw_version(struct psmouse *psmouse)  	cytp->fw_version = param[2] & FW_VERSION_MASX;  	cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; +	/* +	 * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to +	 * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594. +	 */ +	if (cytp->fw_version >= 11) +		cytp->tp_metrics_supported = 0; +  	psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);  	psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",  		 cytp->tp_metrics_supported); @@ -258,6 +265,9 @@ static int cypress_read_tp_metrics(struct psmouse *psmouse)  	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;  	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; +	if (!cytp->tp_metrics_supported) +		return 0; +  	memset(param, 0, sizeof(param));  	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {  		/* Update trackpad parameters. */ @@ -315,18 +325,15 @@ static int cypress_read_tp_metrics(struct psmouse *psmouse)  static int cypress_query_hardware(struct psmouse *psmouse)  { -	struct cytp_data *cytp = psmouse->private;  	int ret;  	ret = cypress_read_fw_version(psmouse);  	if (ret)  		return ret; -	if (cytp->tp_metrics_supported) { -		ret = cypress_read_tp_metrics(psmouse); -		if (ret) -			return ret; -	} +	ret = cypress_read_tp_metrics(psmouse); +	if (ret) +		return ret;  	return 0;  } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 41b6fbf6011..1daa97913b7 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -2017,6 +2017,9 @@ static const struct wacom_features wacom_features_0x100 =  static const struct wacom_features wacom_features_0x101 =  	{ "Wacom ISDv4 101",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,  	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x10D = +	{ "Wacom ISDv4 10D",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255, +	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };  static const struct wacom_features wacom_features_0x4001 =  	{ "Wacom ISDv4 4001",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,  	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2201,6 +2204,7 @@ const struct usb_device_id wacom_ids[] = {  	{ USB_DEVICE_WACOM(0xEF) },  	{ USB_DEVICE_WACOM(0x100) },  	{ USB_DEVICE_WACOM(0x101) }, +	{ USB_DEVICE_WACOM(0x10D) },  	{ USB_DEVICE_WACOM(0x4001) },  	{ USB_DEVICE_WACOM(0x47) },  	{ USB_DEVICE_WACOM(0xF4) }, diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 4f702b3ec1a..434c3df250c 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -236,7 +236,12 @@ static void __ads7846_disable(struct ads7846 *ts)  /* Must be called with ts->lock held */  static void __ads7846_enable(struct ads7846 *ts)  { -	regulator_enable(ts->reg); +	int error; + +	error = regulator_enable(ts->reg); +	if (error != 0) +		dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error); +  	ads7846_restart(ts);  } diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 4a29ddf6bf1..1443532fe6c 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -314,15 +314,27 @@ static int mms114_start(struct mms114_data *data)  	struct i2c_client *client = data->client;  	int error; -	if (data->core_reg) -		regulator_enable(data->core_reg); -	if (data->io_reg) -		regulator_enable(data->io_reg); +	error = regulator_enable(data->core_reg); +	if (error) { +		dev_err(&client->dev, "Failed to enable avdd: %d\n", error); +		return error; +	} + +	error = regulator_enable(data->io_reg); +	if (error) { +		dev_err(&client->dev, "Failed to enable vdd: %d\n", error); +		regulator_disable(data->core_reg); +		return error; +	} +  	mdelay(MMS114_POWERON_DELAY);  	error = mms114_setup_regs(data); -	if (error < 0) +	if (error < 0) { +		regulator_disable(data->io_reg); +		regulator_disable(data->core_reg);  		return error; +	}  	if (data->pdata->cfg_pin)  		data->pdata->cfg_pin(true); @@ -335,16 +347,20 @@ static int mms114_start(struct mms114_data *data)  static void mms114_stop(struct mms114_data *data)  {  	struct i2c_client *client = data->client; +	int error;  	disable_irq(client->irq);  	if (data->pdata->cfg_pin)  		data->pdata->cfg_pin(false); -	if (data->io_reg) -		regulator_disable(data->io_reg); -	if (data->core_reg) -		regulator_disable(data->core_reg); +	error = regulator_disable(data->io_reg); +	if (error) +		dev_warn(&client->dev, "Failed to disable vdd: %d\n", error); + +	error = regulator_disable(data->core_reg); +	if (error) +		dev_warn(&client->dev, "Failed to disable avdd: %d\n", error);  }  static int mms114_input_open(struct input_dev *dev) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 5c514d0711d..c332fb98480 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -130,7 +130,7 @@ config IRQ_REMAP  # OMAP IOMMU support  config OMAP_IOMMU  	bool "OMAP IOMMU Support" -	depends on ARCH_OMAP +	depends on ARCH_OMAP2PLUS  	select IOMMU_API  config OMAP_IOVMM diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 98f555dafb5..b287ca33833 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2466,18 +2466,16 @@ static int device_change_notifier(struct notifier_block *nb,  		/* allocate a protection domain if a device is added */  		dma_domain = find_protection_domain(devid); -		if (dma_domain) -			goto out; -		dma_domain = dma_ops_domain_alloc(); -		if (!dma_domain) -			goto out; -		dma_domain->target_dev = devid; - -		spin_lock_irqsave(&iommu_pd_list_lock, flags); -		list_add_tail(&dma_domain->list, &iommu_pd_list); -		spin_unlock_irqrestore(&iommu_pd_list_lock, flags); +		if (!dma_domain) { +			dma_domain = dma_ops_domain_alloc(); +			if (!dma_domain) +				goto out; +			dma_domain->target_dev = devid; -		dev_data = get_dev_data(dev); +			spin_lock_irqsave(&iommu_pd_list_lock, flags); +			list_add_tail(&dma_domain->list, &iommu_pd_list); +			spin_unlock_irqrestore(&iommu_pd_list_lock, flags); +		}  		dev->archdata.dma_ops = &amd_iommu_dma_ops; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index b6ecddb63cd..e3c2d74b768 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -980,7 +980,7 @@ static void __init free_iommu_all(void)   *     BIOS should disable L2B micellaneous clock gating by setting   *     L2_L2B_CK_GATE_CONTROL[CKGateL2BMiscDisable](D0F2xF4_x90[2]) = 1b   */ -static void __init amd_iommu_erratum_746_workaround(struct amd_iommu *iommu) +static void amd_iommu_erratum_746_workaround(struct amd_iommu *iommu)  {  	u32 value; diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index d56f8c17c5f..7c11ff368d0 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -2,7 +2,6 @@  #include <linux/cpumask.h>  #include <linux/kernel.h>  #include <linux/string.h> -#include <linux/cpumask.h>  #include <linux/errno.h>  #include <linux/msi.h>  #include <linux/irq.h> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969e5ef..4a33351c25d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -25,6 +25,14 @@ config ARM_VIC_NR  	  The maximum number of VICs available in the system, for  	  power management. +config RENESAS_INTC_IRQPIN +	bool +	select IRQ_DOMAIN + +config RENESAS_IRQC +	bool +	select IRQ_DOMAIN +  config VERSATILE_FPGA_IRQ  	bool  	select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87bdf1..acf98953272 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -2,10 +2,13 @@ obj-$(CONFIG_IRQCHIP)			+= irqchip.o  obj-$(CONFIG_ARCH_BCM2835)		+= irq-bcm2835.o  obj-$(CONFIG_ARCH_EXYNOS)		+= exynos-combiner.o +obj-$(CONFIG_ARCH_S3C24XX)		+= irq-s3c24xx.o  obj-$(CONFIG_METAG)			+= irq-metag-ext.o  obj-$(CONFIG_METAG_PERFCOUNTER_IRQS)	+= irq-metag.o  obj-$(CONFIG_ARCH_SUNXI)		+= irq-sunxi.o  obj-$(CONFIG_ARCH_SPEAR3XX)		+= spear-shirq.o  obj-$(CONFIG_ARM_GIC)			+= irq-gic.o  obj-$(CONFIG_ARM_VIC)			+= irq-vic.o +obj-$(CONFIG_RENESAS_INTC_IRQPIN)	+= irq-renesas-intc-irqpin.o +obj-$(CONFIG_RENESAS_IRQC)		+= irq-renesas-irqc.o  obj-$(CONFIG_VERSATILE_FPGA_IRQ)	+= irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 644d7246842..a32e0d5aa45 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -648,7 +648,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)  	/* Convert our logical CPU mask into a physical one. */  	for_each_cpu(cpu, mask) -		map |= 1 << cpu_logical_map(cpu); +		map |= gic_cpu_map[cpu];  	/*  	 * Ensure that stores to Normal memory are visible to the diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c new file mode 100644 index 00000000000..5a68e5accec --- /dev/null +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -0,0 +1,547 @@ +/* + * Renesas INTC External IRQ Pin Driver + * + *  Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> + +#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ + +#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ +#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ +#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ +#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ +#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ +#define INTC_IRQPIN_REG_NR 5 + +/* INTC external IRQ PIN hardware register access: + * + * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) + * PRIO is read-write 32-bit with 4-bits per IRQ (**) + * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) + * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * + * (*) May be accessed by more than one driver instance - lock needed + * (**) Read-modify-write access by one driver instance - lock needed + * (***) Accessed by one driver instance only - no locking needed + */ + +struct intc_irqpin_iomem { +	void __iomem *iomem; +	unsigned long (*read)(void __iomem *iomem); +	void (*write)(void __iomem *iomem, unsigned long data); +	int width; +}; + +struct intc_irqpin_irq { +	int hw_irq; +	int requested_irq; +	int domain_irq; +	struct intc_irqpin_priv *p; +}; + +struct intc_irqpin_priv { +	struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; +	struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; +	struct renesas_intc_irqpin_config config; +	unsigned int number_of_irqs; +	struct platform_device *pdev; +	struct irq_chip irq_chip; +	struct irq_domain *irq_domain; +	bool shared_irqs; +	u8 shared_irq_mask; +}; + +static unsigned long intc_irqpin_read32(void __iomem *iomem) +{ +	return ioread32(iomem); +} + +static unsigned long intc_irqpin_read8(void __iomem *iomem) +{ +	return ioread8(iomem); +} + +static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) +{ +	iowrite32(data, iomem); +} + +static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) +{ +	iowrite8(data, iomem); +} + +static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, +					     int reg) +{ +	struct intc_irqpin_iomem *i = &p->iomem[reg]; + +	return i->read(i->iomem); +} + +static inline void intc_irqpin_write(struct intc_irqpin_priv *p, +				     int reg, unsigned long data) +{ +	struct intc_irqpin_iomem *i = &p->iomem[reg]; + +	i->write(i->iomem, data); +} + +static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, +						   int reg, int hw_irq) +{ +	return BIT((p->iomem[reg].width - 1) - hw_irq); +} + +static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, +					       int reg, int hw_irq) +{ +	intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); +} + +static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ + +static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, +					  int reg, int shift, +					  int width, int value) +{ +	unsigned long flags; +	unsigned long tmp; + +	raw_spin_lock_irqsave(&intc_irqpin_lock, flags); + +	tmp = intc_irqpin_read(p, reg); +	tmp &= ~(((1 << width) - 1) << shift); +	tmp |= value << shift; +	intc_irqpin_write(p, reg, tmp); + +	raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); +} + +static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, +					 int irq, int do_mask) +{ +	int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ +	int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ + +	intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, +				      shift, bitfield_width, +				      do_mask ? 0 : (1 << bitfield_width) - 1); +} + +static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) +{ +	int bitfield_width = p->config.sense_bitfield_width; +	int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ + +	dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); + +	if (value >= (1 << bitfield_width)) +		return -EINVAL; + +	intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, +				      bitfield_width, value); +	return 0; +} + +static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) +{ +	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", +		str, i->requested_irq, i->hw_irq, i->domain_irq); +} + +static void intc_irqpin_irq_enable(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	intc_irqpin_dbg(&p->irq[hw_irq], "enable"); +	intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); +} + +static void intc_irqpin_irq_disable(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	intc_irqpin_dbg(&p->irq[hw_irq], "disable"); +	intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); +} + +static void intc_irqpin_shared_irq_enable(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); +	intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); + +	p->shared_irq_mask &= ~BIT(hw_irq); +} + +static void intc_irqpin_shared_irq_disable(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	intc_irqpin_dbg(&p->irq[hw_irq], "shared disable"); +	intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); + +	p->shared_irq_mask |= BIT(hw_irq); +} + +static void intc_irqpin_irq_enable_force(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + +	intc_irqpin_irq_enable(d); + +	/* enable interrupt through parent interrupt controller, +	 * assumes non-shared interrupt with 1:1 mapping +	 * needed for busted IRQs on some SoCs like sh73a0 +	 */ +	irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); +} + +static void intc_irqpin_irq_disable_force(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + +	/* disable interrupt through parent interrupt controller, +	 * assumes non-shared interrupt with 1:1 mapping +	 * needed for busted IRQs on some SoCs like sh73a0 +	 */ +	irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); +	intc_irqpin_irq_disable(d); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { +	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), +	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), +	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), +	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), +	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), +}; + +static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) +{ +	unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + +	if (!(value & INTC_IRQ_SENSE_VALID)) +		return -EINVAL; + +	return intc_irqpin_set_sense(p, irqd_to_hwirq(d), +				     value ^ INTC_IRQ_SENSE_VALID); +} + +static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) +{ +	struct intc_irqpin_irq *i = dev_id; +	struct intc_irqpin_priv *p = i->p; +	unsigned long bit; + +	intc_irqpin_dbg(i, "demux1"); +	bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); + +	if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { +		intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); +		intc_irqpin_dbg(i, "demux2"); +		generic_handle_irq(i->domain_irq); +		return IRQ_HANDLED; +	} +	return IRQ_NONE; +} + +static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) +{ +	struct intc_irqpin_priv *p = dev_id; +	unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE); +	irqreturn_t status = IRQ_NONE; +	int k; + +	for (k = 0; k < 8; k++) { +		if (reg_source & BIT(7 - k)) { +			if (BIT(k) & p->shared_irq_mask) +				continue; + +			status |= intc_irqpin_irq_handler(irq, &p->irq[k]); +		} +	} + +	return status; +} + +static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, +				      irq_hw_number_t hw) +{ +	struct intc_irqpin_priv *p = h->host_data; + +	p->irq[hw].domain_irq = virq; +	p->irq[hw].hw_irq = hw; + +	intc_irqpin_dbg(&p->irq[hw], "map"); +	irq_set_chip_data(virq, h->host_data); +	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); +	set_irq_flags(virq, IRQF_VALID); /* kill me now */ +	return 0; +} + +static struct irq_domain_ops intc_irqpin_irq_domain_ops = { +	.map	= intc_irqpin_irq_domain_map, +	.xlate  = irq_domain_xlate_twocell, +}; + +static int intc_irqpin_probe(struct platform_device *pdev) +{ +	struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; +	struct intc_irqpin_priv *p; +	struct intc_irqpin_iomem *i; +	struct resource *io[INTC_IRQPIN_REG_NR]; +	struct resource *irq; +	struct irq_chip *irq_chip; +	void (*enable_fn)(struct irq_data *d); +	void (*disable_fn)(struct irq_data *d); +	const char *name = dev_name(&pdev->dev); +	int ref_irq; +	int ret; +	int k; + +	p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); +	if (!p) { +		dev_err(&pdev->dev, "failed to allocate driver data\n"); +		ret = -ENOMEM; +		goto err0; +	} + +	/* deal with driver instance configuration */ +	if (pdata) +		memcpy(&p->config, pdata, sizeof(*pdata)); +	if (!p->config.sense_bitfield_width) +		p->config.sense_bitfield_width = 4; /* default to 4 bits */ + +	p->pdev = pdev; +	platform_set_drvdata(pdev, p); + +	/* get hold of manadatory IOMEM */ +	for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { +		io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); +		if (!io[k]) { +			dev_err(&pdev->dev, "not enough IOMEM resources\n"); +			ret = -EINVAL; +			goto err0; +		} +	} + +	/* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ +	for (k = 0; k < INTC_IRQPIN_MAX; k++) { +		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); +		if (!irq) +			break; + +		p->irq[k].p = p; +		p->irq[k].requested_irq = irq->start; +	} + +	p->number_of_irqs = k; +	if (p->number_of_irqs < 1) { +		dev_err(&pdev->dev, "not enough IRQ resources\n"); +		ret = -EINVAL; +		goto err0; +	} + +	/* ioremap IOMEM and setup read/write callbacks */ +	for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { +		i = &p->iomem[k]; + +		switch (resource_size(io[k])) { +		case 1: +			i->width = 8; +			i->read = intc_irqpin_read8; +			i->write = intc_irqpin_write8; +			break; +		case 4: +			i->width = 32; +			i->read = intc_irqpin_read32; +			i->write = intc_irqpin_write32; +			break; +		default: +			dev_err(&pdev->dev, "IOMEM size mismatch\n"); +			ret = -EINVAL; +			goto err0; +		} + +		i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, +						resource_size(io[k])); +		if (!i->iomem) { +			dev_err(&pdev->dev, "failed to remap IOMEM\n"); +			ret = -ENXIO; +			goto err0; +		} +	} + +	/* mask all interrupts using priority */ +	for (k = 0; k < p->number_of_irqs; k++) +		intc_irqpin_mask_unmask_prio(p, k, 1); + +	/* clear all pending interrupts */ +	intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0); + +	/* scan for shared interrupt lines */ +	ref_irq = p->irq[0].requested_irq; +	p->shared_irqs = true; +	for (k = 1; k < p->number_of_irqs; k++) { +		if (ref_irq != p->irq[k].requested_irq) { +			p->shared_irqs = false; +			break; +		} +	} + +	/* use more severe masking method if requested */ +	if (p->config.control_parent) { +		enable_fn = intc_irqpin_irq_enable_force; +		disable_fn = intc_irqpin_irq_disable_force; +	} else if (!p->shared_irqs) { +		enable_fn = intc_irqpin_irq_enable; +		disable_fn = intc_irqpin_irq_disable; +	} else { +		enable_fn = intc_irqpin_shared_irq_enable; +		disable_fn = intc_irqpin_shared_irq_disable; +	} + +	irq_chip = &p->irq_chip; +	irq_chip->name = name; +	irq_chip->irq_mask = disable_fn; +	irq_chip->irq_unmask = enable_fn; +	irq_chip->irq_enable = enable_fn; +	irq_chip->irq_disable = disable_fn; +	irq_chip->irq_set_type = intc_irqpin_irq_set_type; +	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE; + +	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, +					      p->number_of_irqs, +					      p->config.irq_base, +					      &intc_irqpin_irq_domain_ops, p); +	if (!p->irq_domain) { +		ret = -ENXIO; +		dev_err(&pdev->dev, "cannot initialize irq domain\n"); +		goto err0; +	} + +	if (p->shared_irqs) { +		/* request one shared interrupt */ +		if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq, +				intc_irqpin_shared_irq_handler, +				IRQF_SHARED, name, p)) { +			dev_err(&pdev->dev, "failed to request low IRQ\n"); +			ret = -ENOENT; +			goto err1; +		} +	} else { +		/* request interrupts one by one */ +		for (k = 0; k < p->number_of_irqs; k++) { +			if (devm_request_irq(&pdev->dev, +					p->irq[k].requested_irq, +					intc_irqpin_irq_handler, +					0, name, &p->irq[k])) { +				dev_err(&pdev->dev, +					"failed to request low IRQ\n"); +				ret = -ENOENT; +				goto err1; +			} +		} +	} + +	/* unmask all interrupts on prio level */ +	for (k = 0; k < p->number_of_irqs; k++) +		intc_irqpin_mask_unmask_prio(p, k, 0); + +	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + +	/* warn in case of mismatch if irq base is specified */ +	if (p->config.irq_base) { +		if (p->config.irq_base != p->irq[0].domain_irq) +			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", +				 p->config.irq_base, p->irq[0].domain_irq); +	} + +	return 0; + +err1: +	irq_domain_remove(p->irq_domain); +err0: +	return ret; +} + +static int intc_irqpin_remove(struct platform_device *pdev) +{ +	struct intc_irqpin_priv *p = platform_get_drvdata(pdev); + +	irq_domain_remove(p->irq_domain); + +	return 0; +} + +static const struct of_device_id intc_irqpin_dt_ids[] = { +	{ .compatible = "renesas,intc-irqpin", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); + +static struct platform_driver intc_irqpin_device_driver = { +	.probe		= intc_irqpin_probe, +	.remove		= intc_irqpin_remove, +	.driver		= { +		.name	= "renesas_intc_irqpin", +		.of_match_table = intc_irqpin_dt_ids, +		.owner  = THIS_MODULE, +	} +}; + +static int __init intc_irqpin_init(void) +{ +	return platform_driver_register(&intc_irqpin_device_driver); +} +postcore_initcall(intc_irqpin_init); + +static void __exit intc_irqpin_exit(void) +{ +	platform_driver_unregister(&intc_irqpin_device_driver); +} +module_exit(intc_irqpin_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c new file mode 100644 index 00000000000..927bff373aa --- /dev/null +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -0,0 +1,307 @@ +/* + * Renesas IRQC Driver + * + *  Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_data/irq-renesas-irqc.h> + +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ + +#define IRQC_REQ_STS 0x00 +#define IRQC_EN_STS 0x04 +#define IRQC_EN_SET 0x08 +#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) +#define DETECT_STATUS 0x100 +#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) + +struct irqc_irq { +	int hw_irq; +	int requested_irq; +	int domain_irq; +	struct irqc_priv *p; +}; + +struct irqc_priv { +	void __iomem *iomem; +	void __iomem *cpu_int_base; +	struct irqc_irq irq[IRQC_IRQ_MAX]; +	struct renesas_irqc_config config; +	unsigned int number_of_irqs; +	struct platform_device *pdev; +	struct irq_chip irq_chip; +	struct irq_domain *irq_domain; +}; + +static void irqc_dbg(struct irqc_irq *i, char *str) +{ +	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", +		str, i->requested_irq, i->hw_irq, i->domain_irq); +} + +static void irqc_irq_enable(struct irq_data *d) +{ +	struct irqc_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	irqc_dbg(&p->irq[hw_irq], "enable"); +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); +} + +static void irqc_irq_disable(struct irq_data *d) +{ +	struct irqc_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	irqc_dbg(&p->irq[hw_irq], "disable"); +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { +	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), +	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), +	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ +	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ +	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c),  /* Synchronous */ +}; + +static int irqc_irq_set_type(struct irq_data *d, unsigned int type) +{ +	struct irqc_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); +	unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; +	unsigned long tmp; + +	irqc_dbg(&p->irq[hw_irq], "sense"); + +	if (!(value & INTC_IRQ_SENSE_VALID)) +		return -EINVAL; + +	tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); +	tmp &= ~0x3f; +	tmp |= value ^ INTC_IRQ_SENSE_VALID; +	iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); +	return 0; +} + +static irqreturn_t irqc_irq_handler(int irq, void *dev_id) +{ +	struct irqc_irq *i = dev_id; +	struct irqc_priv *p = i->p; +	unsigned long bit = BIT(i->hw_irq); + +	irqc_dbg(i, "demux1"); + +	if (ioread32(p->iomem + DETECT_STATUS) & bit) { +		iowrite32(bit, p->iomem + DETECT_STATUS); +		irqc_dbg(i, "demux2"); +		generic_handle_irq(i->domain_irq); +		return IRQ_HANDLED; +	} +	return IRQ_NONE; +} + +static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, +			       irq_hw_number_t hw) +{ +	struct irqc_priv *p = h->host_data; + +	p->irq[hw].domain_irq = virq; +	p->irq[hw].hw_irq = hw; + +	irqc_dbg(&p->irq[hw], "map"); +	irq_set_chip_data(virq, h->host_data); +	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); +	set_irq_flags(virq, IRQF_VALID); /* kill me now */ +	return 0; +} + +static struct irq_domain_ops irqc_irq_domain_ops = { +	.map	= irqc_irq_domain_map, +	.xlate  = irq_domain_xlate_twocell, +}; + +static int irqc_probe(struct platform_device *pdev) +{ +	struct renesas_irqc_config *pdata = pdev->dev.platform_data; +	struct irqc_priv *p; +	struct resource *io; +	struct resource *irq; +	struct irq_chip *irq_chip; +	const char *name = dev_name(&pdev->dev); +	int ret; +	int k; + +	p = kzalloc(sizeof(*p), GFP_KERNEL); +	if (!p) { +		dev_err(&pdev->dev, "failed to allocate driver data\n"); +		ret = -ENOMEM; +		goto err0; +	} + +	/* deal with driver instance configuration */ +	if (pdata) +		memcpy(&p->config, pdata, sizeof(*pdata)); + +	p->pdev = pdev; +	platform_set_drvdata(pdev, p); + +	/* get hold of manadatory IOMEM */ +	io = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!io) { +		dev_err(&pdev->dev, "not enough IOMEM resources\n"); +		ret = -EINVAL; +		goto err1; +	} + +	/* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ +	for (k = 0; k < IRQC_IRQ_MAX; k++) { +		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); +		if (!irq) +			break; + +		p->irq[k].p = p; +		p->irq[k].requested_irq = irq->start; +	} + +	p->number_of_irqs = k; +	if (p->number_of_irqs < 1) { +		dev_err(&pdev->dev, "not enough IRQ resources\n"); +		ret = -EINVAL; +		goto err1; +	} + +	/* ioremap IOMEM and setup read/write callbacks */ +	p->iomem = ioremap_nocache(io->start, resource_size(io)); +	if (!p->iomem) { +		dev_err(&pdev->dev, "failed to remap IOMEM\n"); +		ret = -ENXIO; +		goto err2; +	} + +	p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ + +	irq_chip = &p->irq_chip; +	irq_chip->name = name; +	irq_chip->irq_mask = irqc_irq_disable; +	irq_chip->irq_unmask = irqc_irq_enable; +	irq_chip->irq_enable = irqc_irq_enable; +	irq_chip->irq_disable = irqc_irq_disable; +	irq_chip->irq_set_type = irqc_irq_set_type; +	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE; + +	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, +					      p->number_of_irqs, +					      p->config.irq_base, +					      &irqc_irq_domain_ops, p); +	if (!p->irq_domain) { +		ret = -ENXIO; +		dev_err(&pdev->dev, "cannot initialize irq domain\n"); +		goto err2; +	} + +	/* request interrupts one by one */ +	for (k = 0; k < p->number_of_irqs; k++) { +		if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, +				0, name, &p->irq[k])) { +			dev_err(&pdev->dev, "failed to request IRQ\n"); +			ret = -ENOENT; +			goto err3; +		} +	} + +	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + +	/* warn in case of mismatch if irq base is specified */ +	if (p->config.irq_base) { +		if (p->config.irq_base != p->irq[0].domain_irq) +			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", +				 p->config.irq_base, p->irq[0].domain_irq); +	} + +	return 0; +err3: +	for (; k >= 0; k--) +		free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); + +	irq_domain_remove(p->irq_domain); +err2: +	iounmap(p->iomem); +err1: +	kfree(p); +err0: +	return ret; +} + +static int irqc_remove(struct platform_device *pdev) +{ +	struct irqc_priv *p = platform_get_drvdata(pdev); +	int k; + +	for (k = 0; k < p->number_of_irqs; k++) +		free_irq(p->irq[k].requested_irq, &p->irq[k]); + +	irq_domain_remove(p->irq_domain); +	iounmap(p->iomem); +	kfree(p); +	return 0; +} + +static const struct of_device_id irqc_dt_ids[] = { +	{ .compatible = "renesas,irqc", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, irqc_dt_ids); + +static struct platform_driver irqc_device_driver = { +	.probe		= irqc_probe, +	.remove		= irqc_remove, +	.driver		= { +		.name	= "renesas_irqc", +		.of_match_table	= irqc_dt_ids, +		.owner	= THIS_MODULE, +	} +}; + +static int __init irqc_init(void) +{ +	return platform_driver_register(&irqc_device_driver); +} +postcore_initcall(irqc_init); + +static void __exit irqc_exit(void) +{ +	platform_driver_unregister(&irqc_device_driver); +} +module_exit(irqc_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas IRQC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c new file mode 100644 index 00000000000..5e40b3424df --- /dev/null +++ b/drivers/irqchip/irq-s3c24xx.c @@ -0,0 +1,1355 @@ +/* + * S3C24XX IRQ handling + * + * Copyright (c) 2003-2004 Simtec Electronics + *	Ben Dooks <ben@simtec.co.uk> + * Copyright (c) 2012 Heiko Stuebner <heiko@sntech.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. +*/ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/device.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> + +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#include <mach/regs-irq.h> +#include <mach/regs-gpio.h> + +#include <plat/cpu.h> +#include <plat/regs-irqtype.h> +#include <plat/pm.h> + +#include "irqchip.h" + +#define S3C_IRQTYPE_NONE	0 +#define S3C_IRQTYPE_EINT	1 +#define S3C_IRQTYPE_EDGE	2 +#define S3C_IRQTYPE_LEVEL	3 + +struct s3c_irq_data { +	unsigned int type; +	unsigned long offset; +	unsigned long parent_irq; + +	/* data gets filled during init */ +	struct s3c_irq_intc *intc; +	unsigned long sub_bits; +	struct s3c_irq_intc *sub_intc; +}; + +/* + * Sructure holding the controller data + * @reg_pending		register holding pending irqs + * @reg_intpnd		special register intpnd in main intc + * @reg_mask		mask register + * @domain		irq_domain of the controller + * @parent		parent controller for ext and sub irqs + * @irqs		irq-data, always s3c_irq_data[32] + */ +struct s3c_irq_intc { +	void __iomem		*reg_pending; +	void __iomem		*reg_intpnd; +	void __iomem		*reg_mask; +	struct irq_domain	*domain; +	struct s3c_irq_intc	*parent; +	struct s3c_irq_data	*irqs; +}; + +/* + * Array holding pointers to the global controller structs + * [0] ... main_intc + * [1] ... sub_intc + * [2] ... main_intc2 on s3c2416 + */ +static struct s3c_irq_intc *s3c_intc[3]; + +static void s3c_irq_mask(struct irq_data *data) +{ +	struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); +	struct s3c_irq_intc *intc = irq_data->intc; +	struct s3c_irq_intc *parent_intc = intc->parent; +	struct s3c_irq_data *parent_data; +	unsigned long mask; +	unsigned int irqno; + +	mask = __raw_readl(intc->reg_mask); +	mask |= (1UL << irq_data->offset); +	__raw_writel(mask, intc->reg_mask); + +	if (parent_intc) { +		parent_data = &parent_intc->irqs[irq_data->parent_irq]; + +		/* check to see if we need to mask the parent IRQ +		 * The parent_irq is always in main_intc, so the hwirq +		 * for find_mapping does not need an offset in any case. +		 */ +		if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { +			irqno = irq_find_mapping(parent_intc->domain, +					 irq_data->parent_irq); +			s3c_irq_mask(irq_get_irq_data(irqno)); +		} +	} +} + +static void s3c_irq_unmask(struct irq_data *data) +{ +	struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); +	struct s3c_irq_intc *intc = irq_data->intc; +	struct s3c_irq_intc *parent_intc = intc->parent; +	unsigned long mask; +	unsigned int irqno; + +	mask = __raw_readl(intc->reg_mask); +	mask &= ~(1UL << irq_data->offset); +	__raw_writel(mask, intc->reg_mask); + +	if (parent_intc) { +		irqno = irq_find_mapping(parent_intc->domain, +					 irq_data->parent_irq); +		s3c_irq_unmask(irq_get_irq_data(irqno)); +	} +} + +static inline void s3c_irq_ack(struct irq_data *data) +{ +	struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); +	struct s3c_irq_intc *intc = irq_data->intc; +	unsigned long bitval = 1UL << irq_data->offset; + +	__raw_writel(bitval, intc->reg_pending); +	if (intc->reg_intpnd) +		__raw_writel(bitval, intc->reg_intpnd); +} + +static int s3c_irq_type(struct irq_data *data, unsigned int type) +{ +	switch (type) { +	case IRQ_TYPE_NONE: +		break; +	case IRQ_TYPE_EDGE_RISING: +	case IRQ_TYPE_EDGE_FALLING: +	case IRQ_TYPE_EDGE_BOTH: +		irq_set_handler(data->irq, handle_edge_irq); +		break; +	case IRQ_TYPE_LEVEL_LOW: +	case IRQ_TYPE_LEVEL_HIGH: +		irq_set_handler(data->irq, handle_level_irq); +		break; +	default: +		pr_err("No such irq type %d", type); +		return -EINVAL; +	} + +	return 0; +} + +static int s3c_irqext_type_set(void __iomem *gpcon_reg, +			       void __iomem *extint_reg, +			       unsigned long gpcon_offset, +			       unsigned long extint_offset, +			       unsigned int type) +{ +	unsigned long newvalue = 0, value; + +	/* Set the GPIO to external interrupt mode */ +	value = __raw_readl(gpcon_reg); +	value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); +	__raw_writel(value, gpcon_reg); + +	/* Set the external interrupt to pointed trigger type */ +	switch (type) +	{ +		case IRQ_TYPE_NONE: +			pr_warn("No edge setting!\n"); +			break; + +		case IRQ_TYPE_EDGE_RISING: +			newvalue = S3C2410_EXTINT_RISEEDGE; +			break; + +		case IRQ_TYPE_EDGE_FALLING: +			newvalue = S3C2410_EXTINT_FALLEDGE; +			break; + +		case IRQ_TYPE_EDGE_BOTH: +			newvalue = S3C2410_EXTINT_BOTHEDGE; +			break; + +		case IRQ_TYPE_LEVEL_LOW: +			newvalue = S3C2410_EXTINT_LOWLEV; +			break; + +		case IRQ_TYPE_LEVEL_HIGH: +			newvalue = S3C2410_EXTINT_HILEV; +			break; + +		default: +			pr_err("No such irq type %d", type); +			return -EINVAL; +	} + +	value = __raw_readl(extint_reg); +	value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); +	__raw_writel(value, extint_reg); + +	return 0; +} + +static int s3c_irqext_type(struct irq_data *data, unsigned int type) +{ +	void __iomem *extint_reg; +	void __iomem *gpcon_reg; +	unsigned long gpcon_offset, extint_offset; + +	if ((data->hwirq >= 4) && (data->hwirq <= 7)) { +		gpcon_reg = S3C2410_GPFCON; +		extint_reg = S3C24XX_EXTINT0; +		gpcon_offset = (data->hwirq) * 2; +		extint_offset = (data->hwirq) * 4; +	} else if ((data->hwirq >= 8) && (data->hwirq <= 15)) { +		gpcon_reg = S3C2410_GPGCON; +		extint_reg = S3C24XX_EXTINT1; +		gpcon_offset = (data->hwirq - 8) * 2; +		extint_offset = (data->hwirq - 8) * 4; +	} else if ((data->hwirq >= 16) && (data->hwirq <= 23)) { +		gpcon_reg = S3C2410_GPGCON; +		extint_reg = S3C24XX_EXTINT2; +		gpcon_offset = (data->hwirq - 8) * 2; +		extint_offset = (data->hwirq - 16) * 4; +	} else { +		return -EINVAL; +	} + +	return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset, +				   extint_offset, type); +} + +static int s3c_irqext0_type(struct irq_data *data, unsigned int type) +{ +	void __iomem *extint_reg; +	void __iomem *gpcon_reg; +	unsigned long gpcon_offset, extint_offset; + +	if ((data->hwirq >= 0) && (data->hwirq <= 3)) { +		gpcon_reg = S3C2410_GPFCON; +		extint_reg = S3C24XX_EXTINT0; +		gpcon_offset = (data->hwirq) * 2; +		extint_offset = (data->hwirq) * 4; +	} else { +		return -EINVAL; +	} + +	return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset, +				   extint_offset, type); +} + +static struct irq_chip s3c_irq_chip = { +	.name		= "s3c", +	.irq_ack	= s3c_irq_ack, +	.irq_mask	= s3c_irq_mask, +	.irq_unmask	= s3c_irq_unmask, +	.irq_set_type	= s3c_irq_type, +	.irq_set_wake	= s3c_irq_wake +}; + +static struct irq_chip s3c_irq_level_chip = { +	.name		= "s3c-level", +	.irq_mask	= s3c_irq_mask, +	.irq_unmask	= s3c_irq_unmask, +	.irq_ack	= s3c_irq_ack, +	.irq_set_type	= s3c_irq_type, +}; + +static struct irq_chip s3c_irqext_chip = { +	.name		= "s3c-ext", +	.irq_mask	= s3c_irq_mask, +	.irq_unmask	= s3c_irq_unmask, +	.irq_ack	= s3c_irq_ack, +	.irq_set_type	= s3c_irqext_type, +	.irq_set_wake	= s3c_irqext_wake +}; + +static struct irq_chip s3c_irq_eint0t4 = { +	.name		= "s3c-ext0", +	.irq_ack	= s3c_irq_ack, +	.irq_mask	= s3c_irq_mask, +	.irq_unmask	= s3c_irq_unmask, +	.irq_set_wake	= s3c_irq_wake, +	.irq_set_type	= s3c_irqext0_type, +}; + +static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) +{ +	struct irq_chip *chip = irq_desc_get_chip(desc); +	struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); +	struct s3c_irq_intc *intc = irq_data->intc; +	struct s3c_irq_intc *sub_intc = irq_data->sub_intc; +	unsigned long src; +	unsigned long msk; +	unsigned int n; +	unsigned int offset; + +	/* we're using individual domains for the non-dt case +	 * and one big domain for the dt case where the subintc +	 * starts at hwirq number 32. +	 */ +	offset = (intc->domain->of_node) ? 32 : 0; + +	chained_irq_enter(chip, desc); + +	src = __raw_readl(sub_intc->reg_pending); +	msk = __raw_readl(sub_intc->reg_mask); + +	src &= ~msk; +	src &= irq_data->sub_bits; + +	while (src) { +		n = __ffs(src); +		src &= ~(1 << n); +		irq = irq_find_mapping(sub_intc->domain, offset + n); +		generic_handle_irq(irq); +	} + +	chained_irq_exit(chip, desc); +} + +static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, +				      struct pt_regs *regs, int intc_offset) +{ +	int pnd; +	int offset; +	int irq; + +	pnd = __raw_readl(intc->reg_intpnd); +	if (!pnd) +		return false; + +	/* non-dt machines use individual domains */ +	if (!intc->domain->of_node) +		intc_offset = 0; + +	/* We have a problem that the INTOFFSET register does not always +	 * show one interrupt. Occasionally we get two interrupts through +	 * the prioritiser, and this causes the INTOFFSET register to show +	 * what looks like the logical-or of the two interrupt numbers. +	 * +	 * Thanks to Klaus, Shannon, et al for helping to debug this problem +	 */ +	offset = __raw_readl(intc->reg_intpnd + 4); + +	/* Find the bit manually, when the offset is wrong. +	 * The pending register only ever contains the one bit of the next +	 * interrupt to handle. +	 */ +	if (!(pnd & (1 << offset))) +		offset =  __ffs(pnd); + +	irq = irq_find_mapping(intc->domain, intc_offset + offset); +	handle_IRQ(irq, regs); +	return true; +} + +asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) +{ +	do { +		if (likely(s3c_intc[0])) +			if (s3c24xx_handle_intc(s3c_intc[0], regs, 0)) +				continue; + +		if (s3c_intc[2]) +			if (s3c24xx_handle_intc(s3c_intc[2], regs, 64)) +				continue; + +		break; +	} while (1); +} + +#ifdef CONFIG_FIQ +/** + * s3c24xx_set_fiq - set the FIQ routing + * @irq: IRQ number to route to FIQ on processor. + * @on: Whether to route @irq to the FIQ, or to remove the FIQ routing. + * + * Change the state of the IRQ to FIQ routing depending on @irq and @on. If + * @on is true, the @irq is checked to see if it can be routed and the + * interrupt controller updated to route the IRQ. If @on is false, the FIQ + * routing is cleared, regardless of which @irq is specified. + */ +int s3c24xx_set_fiq(unsigned int irq, bool on) +{ +	u32 intmod; +	unsigned offs; + +	if (on) { +		offs = irq - FIQ_START; +		if (offs > 31) +			return -EINVAL; + +		intmod = 1 << offs; +	} else { +		intmod = 0; +	} + +	__raw_writel(intmod, S3C2410_INTMOD); +	return 0; +} + +EXPORT_SYMBOL_GPL(s3c24xx_set_fiq); +#endif + +static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, +							irq_hw_number_t hw) +{ +	struct s3c_irq_intc *intc = h->host_data; +	struct s3c_irq_data *irq_data = &intc->irqs[hw]; +	struct s3c_irq_intc *parent_intc; +	struct s3c_irq_data *parent_irq_data; +	unsigned int irqno; + +	/* attach controller pointer to irq_data */ +	irq_data->intc = intc; +	irq_data->offset = hw; + +	parent_intc = intc->parent; + +	/* set handler and flags */ +	switch (irq_data->type) { +	case S3C_IRQTYPE_NONE: +		return 0; +	case S3C_IRQTYPE_EINT: +		/* On the S3C2412, the EINT0to3 have a parent irq +		 * but need the s3c_irq_eint0t4 chip +		 */ +		if (parent_intc && (!soc_is_s3c2412() || hw >= 4)) +			irq_set_chip_and_handler(virq, &s3c_irqext_chip, +						 handle_edge_irq); +		else +			irq_set_chip_and_handler(virq, &s3c_irq_eint0t4, +						 handle_edge_irq); +		break; +	case S3C_IRQTYPE_EDGE: +		if (parent_intc || intc->reg_pending == S3C2416_SRCPND2) +			irq_set_chip_and_handler(virq, &s3c_irq_level_chip, +						 handle_edge_irq); +		else +			irq_set_chip_and_handler(virq, &s3c_irq_chip, +						 handle_edge_irq); +		break; +	case S3C_IRQTYPE_LEVEL: +		if (parent_intc) +			irq_set_chip_and_handler(virq, &s3c_irq_level_chip, +						 handle_level_irq); +		else +			irq_set_chip_and_handler(virq, &s3c_irq_chip, +						 handle_level_irq); +		break; +	default: +		pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type); +		return -EINVAL; +	} + +	irq_set_chip_data(virq, irq_data); + +	set_irq_flags(virq, IRQF_VALID); + +	if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { +		if (irq_data->parent_irq > 31) { +			pr_err("irq-s3c24xx: parent irq %lu is out of range\n", +			       irq_data->parent_irq); +			goto err; +		} + +		parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; +		parent_irq_data->sub_intc = intc; +		parent_irq_data->sub_bits |= (1UL << hw); + +		/* attach the demuxer to the parent irq */ +		irqno = irq_find_mapping(parent_intc->domain, +					 irq_data->parent_irq); +		if (!irqno) { +			pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n", +			       irq_data->parent_irq); +			goto err; +		} +		irq_set_chained_handler(irqno, s3c_irq_demux); +	} + +	return 0; + +err: +	set_irq_flags(virq, 0); + +	/* the only error can result from bad mapping data*/ +	return -EINVAL; +} + +static struct irq_domain_ops s3c24xx_irq_ops = { +	.map = s3c24xx_irq_map, +	.xlate = irq_domain_xlate_twocell, +}; + +static void s3c24xx_clear_intc(struct s3c_irq_intc *intc) +{ +	void __iomem *reg_source; +	unsigned long pend; +	unsigned long last; +	int i; + +	/* if intpnd is set, read the next pending irq from there */ +	reg_source = intc->reg_intpnd ? intc->reg_intpnd : intc->reg_pending; + +	last = 0; +	for (i = 0; i < 4; i++) { +		pend = __raw_readl(reg_source); + +		if (pend == 0 || pend == last) +			break; + +		__raw_writel(pend, intc->reg_pending); +		if (intc->reg_intpnd) +			__raw_writel(pend, intc->reg_intpnd); + +		pr_info("irq: clearing pending status %08x\n", (int)pend); +		last = pend; +	} +} + +static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, +				       struct s3c_irq_data *irq_data, +				       struct s3c_irq_intc *parent, +				       unsigned long address) +{ +	struct s3c_irq_intc *intc; +	void __iomem *base = (void *)0xf6000000; /* static mapping */ +	int irq_num; +	int irq_start; +	int ret; + +	intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); +	if (!intc) +		return ERR_PTR(-ENOMEM); + +	intc->irqs = irq_data; + +	if (parent) +		intc->parent = parent; + +	/* select the correct data for the controller. +	 * Need to hard code the irq num start and offset +	 * to preserve the static mapping for now +	 */ +	switch (address) { +	case 0x4a000000: +		pr_debug("irq: found main intc\n"); +		intc->reg_pending = base; +		intc->reg_mask = base + 0x08; +		intc->reg_intpnd = base + 0x10; +		irq_num = 32; +		irq_start = S3C2410_IRQ(0); +		break; +	case 0x4a000018: +		pr_debug("irq: found subintc\n"); +		intc->reg_pending = base + 0x18; +		intc->reg_mask = base + 0x1c; +		irq_num = 29; +		irq_start = S3C2410_IRQSUB(0); +		break; +	case 0x4a000040: +		pr_debug("irq: found intc2\n"); +		intc->reg_pending = base + 0x40; +		intc->reg_mask = base + 0x48; +		intc->reg_intpnd = base + 0x50; +		irq_num = 8; +		irq_start = S3C2416_IRQ(0); +		break; +	case 0x560000a4: +		pr_debug("irq: found eintc\n"); +		base = (void *)0xfd000000; + +		intc->reg_mask = base + 0xa4; +		intc->reg_pending = base + 0x08; +		irq_num = 24; +		irq_start = S3C2410_IRQ(32); +		break; +	default: +		pr_err("irq: unsupported controller address\n"); +		ret = -EINVAL; +		goto err; +	} + +	/* now that all the data is complete, init the irq-domain */ +	s3c24xx_clear_intc(intc); +	intc->domain = irq_domain_add_legacy(np, irq_num, irq_start, +					     0, &s3c24xx_irq_ops, +					     intc); +	if (!intc->domain) { +		pr_err("irq: could not create irq-domain\n"); +		ret = -EINVAL; +		goto err; +	} + +	set_handle_irq(s3c24xx_handle_irq); + +	return intc; + +err: +	kfree(intc); +	return ERR_PTR(ret); +} + +static struct s3c_irq_data init_eint[32] = { +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ +}; + +#ifdef CONFIG_CPU_S3C2410 +static struct s3c_irq_data init_s3c2410base[32] = { +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2410subint[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +}; + +void __init s3c2410_init_irq(void) +{ +#ifdef CONFIG_FIQ +	init_FIQ(FIQ_START); +#endif + +	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, +					0x4a000000); +	if (IS_ERR(s3c_intc[0])) { +		pr_err("irq: could not create main interrupt controller\n"); +		return; +	} + +	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2410subint[0], +					s3c_intc[0], 0x4a000018); +	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); +} +#endif + +#ifdef CONFIG_CPU_S3C2412 +static struct s3c_irq_data init_s3c2412base[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* SDI/CF */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2412eint[32] = { +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 0 }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 1 }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 2 }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 3 }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ +	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ +}; + +static struct s3c_irq_data init_s3c2412subint[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +	{ .type = S3C_IRQTYPE_NONE, }, +	{ .type = S3C_IRQTYPE_NONE, }, +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* SDI */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* CF */ +}; + +void s3c2412_init_irq(void) +{ +	pr_info("S3C2412: IRQ Support\n"); + +#ifdef CONFIG_FIQ +	init_FIQ(FIQ_START); +#endif + +	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, +					0x4a000000); +	if (IS_ERR(s3c_intc[0])) { +		pr_err("irq: could not create main interrupt controller\n"); +		return; +	} + +	s3c24xx_init_intc(NULL, &init_s3c2412eint[0], s3c_intc[0], 0x560000a4); +	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2412subint[0], +					s3c_intc[0], 0x4a000018); +} +#endif + +#ifdef CONFIG_CPU_S3C2416 +static struct s3c_irq_data init_s3c2416base[32] = { +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* LCD */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* DMA */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */ +	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* NAND */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ +	{ .type = S3C_IRQTYPE_NONE, }, +	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2416subint[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ +}; + +static struct s3c_irq_data init_s3c2416_second[32] = { +	{ .type = S3C_IRQTYPE_EDGE }, /* 2D */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE }, /* PCM0 */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_EDGE }, /* I2S0 */ +}; + +void __init s3c2416_init_irq(void) +{ +	pr_info("S3C2416: IRQ Support\n"); + +#ifdef CONFIG_FIQ +	init_FIQ(FIQ_START); +#endif + +	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, +					0x4a000000); +	if (IS_ERR(s3c_intc[0])) { +		pr_err("irq: could not create main interrupt controller\n"); +		return; +	} + +	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); +	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2416subint[0], +					s3c_intc[0], 0x4a000018); + +	s3c_intc[2] = s3c24xx_init_intc(NULL, &init_s3c2416_second[0], +					NULL, 0x4a000040); +} + +#endif + +#ifdef CONFIG_CPU_S3C2440 +static struct s3c_irq_data init_s3c2440base[32] = { +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2440subint[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ +}; + +void __init s3c2440_init_irq(void) +{ +	pr_info("S3C2440: IRQ Support\n"); + +#ifdef CONFIG_FIQ +	init_FIQ(FIQ_START); +#endif + +	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, +					0x4a000000); +	if (IS_ERR(s3c_intc[0])) { +		pr_err("irq: could not create main interrupt controller\n"); +		return; +	} + +	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); +	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0], +					s3c_intc[0], 0x4a000018); +} +#endif + +#ifdef CONFIG_CPU_S3C2442 +static struct s3c_irq_data init_s3c2442base[32] = { +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + +static struct s3c_irq_data init_s3c2442subint[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ +}; + +void __init s3c2442_init_irq(void) +{ +	pr_info("S3C2442: IRQ Support\n"); + +#ifdef CONFIG_FIQ +	init_FIQ(FIQ_START); +#endif + +	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, +					0x4a000000); +	if (IS_ERR(s3c_intc[0])) { +		pr_err("irq: could not create main interrupt controller\n"); +		return; +	} + +	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); +	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2442subint[0], +					s3c_intc[0], 0x4a000018); +} +#endif + +#ifdef CONFIG_CPU_S3C2443 +static struct s3c_irq_data init_s3c2443base[32] = { +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ +	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* LCD */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* DMA */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* CFON */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* NAND */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */ +	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */ +	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ +}; + + +static struct s3c_irq_data init_s3c2443subint[32] = { +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ +	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ +	{ .type = S3C_IRQTYPE_NONE }, /* reserved */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD1 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ +	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ +}; + +void __init s3c2443_init_irq(void) +{ +	pr_info("S3C2443: IRQ Support\n"); + +#ifdef CONFIG_FIQ +	init_FIQ(FIQ_START); +#endif + +	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, +					0x4a000000); +	if (IS_ERR(s3c_intc[0])) { +		pr_err("irq: could not create main interrupt controller\n"); +		return; +	} + +	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); +	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0], +					s3c_intc[0], 0x4a000018); +} +#endif + +#ifdef CONFIG_OF +static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, +							irq_hw_number_t hw) +{ +	unsigned int ctrl_num = hw / 32; +	unsigned int intc_hw = hw % 32; +	struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; +	struct s3c_irq_intc *parent_intc = intc->parent; +	struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; + +	/* attach controller pointer to irq_data */ +	irq_data->intc = intc; +	irq_data->offset = intc_hw; + +	if (!parent_intc) +		irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq); +	else +		irq_set_chip_and_handler(virq, &s3c_irq_level_chip, +					 handle_edge_irq); + +	irq_set_chip_data(virq, irq_data); + +	set_irq_flags(virq, IRQF_VALID); + +	return 0; +} + +/* Translate our of irq notation + * format: <ctrl_num ctrl_irq parent_irq type> + */ +static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n, +			const u32 *intspec, unsigned int intsize, +			irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ +	struct s3c_irq_intc *intc; +	struct s3c_irq_intc *parent_intc; +	struct s3c_irq_data *irq_data; +	struct s3c_irq_data *parent_irq_data; +	int irqno; + +	if (WARN_ON(intsize < 4)) +		return -EINVAL; + +	if (intspec[0] > 2 || !s3c_intc[intspec[0]]) { +		pr_err("controller number %d invalid\n", intspec[0]); +		return -EINVAL; +	} +	intc = s3c_intc[intspec[0]]; + +	*out_hwirq = intspec[0] * 32 + intspec[2]; +	*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; + +	parent_intc = intc->parent; +	if (parent_intc) { +		irq_data = &intc->irqs[intspec[2]]; +		irq_data->parent_irq = intspec[1]; +		parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; +		parent_irq_data->sub_intc = intc; +		parent_irq_data->sub_bits |= (1UL << intspec[2]); + +		/* parent_intc is always s3c_intc[0], so no offset */ +		irqno = irq_create_mapping(parent_intc->domain, intspec[1]); +		if (irqno < 0) { +			pr_err("irq: could not map parent interrupt\n"); +			return irqno; +		} + +		irq_set_chained_handler(irqno, s3c_irq_demux); +	} + +	return 0; +} + +static struct irq_domain_ops s3c24xx_irq_ops_of = { +	.map = s3c24xx_irq_map_of, +	.xlate = s3c24xx_irq_xlate_of, +}; + +struct s3c24xx_irq_of_ctrl { +	char			*name; +	unsigned long		offset; +	struct s3c_irq_intc	**handle; +	struct s3c_irq_intc	**parent; +	struct irq_domain_ops	*ops; +}; + +static int __init s3c_init_intc_of(struct device_node *np, +			struct device_node *interrupt_parent, +			struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl) +{ +	struct s3c_irq_intc *intc; +	struct s3c24xx_irq_of_ctrl *ctrl; +	struct irq_domain *domain; +	void __iomem *reg_base; +	int i; + +	reg_base = of_iomap(np, 0); +	if (!reg_base) { +		pr_err("irq-s3c24xx: could not map irq registers\n"); +		return -EINVAL; +	} + +	domain = irq_domain_add_linear(np, num_ctrl * 32, +						     &s3c24xx_irq_ops_of, NULL); +	if (!domain) { +		pr_err("irq: could not create irq-domain\n"); +		return -EINVAL; +	} + +	for (i = 0; i < num_ctrl; i++) { +		ctrl = &s3c_ctrl[i]; + +		pr_debug("irq: found controller %s\n", ctrl->name); + +		intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); +		if (!intc) +			return -ENOMEM; + +		intc->domain = domain; +		intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32, +				     GFP_KERNEL); +		if (!intc->irqs) { +			kfree(intc); +			return -ENOMEM; +		} + +		if (ctrl->parent) { +			intc->reg_pending = reg_base + ctrl->offset; +			intc->reg_mask = reg_base + ctrl->offset + 0x4; + +			if (*(ctrl->parent)) { +				intc->parent = *(ctrl->parent); +			} else { +				pr_warn("irq: parent of %s missing\n", +					ctrl->name); +				kfree(intc->irqs); +				kfree(intc); +				continue; +			} +		} else { +			intc->reg_pending = reg_base + ctrl->offset; +			intc->reg_mask = reg_base + ctrl->offset + 0x08; +			intc->reg_intpnd = reg_base + ctrl->offset + 0x10; +		} + +		s3c24xx_clear_intc(intc); +		s3c_intc[i] = intc; +	} + +	set_handle_irq(s3c24xx_handle_irq); + +	return 0; +} + +static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { +	{ +		.name = "intc", +		.offset = 0, +	}, { +		.name = "subintc", +		.offset = 0x18, +		.parent = &s3c_intc[0], +	} +}; + +int __init s3c2410_init_intc_of(struct device_node *np, +			struct device_node *interrupt_parent, +			struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) +{ +	return s3c_init_intc_of(np, interrupt_parent, +				s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl)); +} +IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of); + +static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { +	{ +		.name = "intc", +		.offset = 0, +	}, { +		.name = "subintc", +		.offset = 0x18, +		.parent = &s3c_intc[0], +	}, { +		.name = "intc2", +		.offset = 0x40, +	} +}; + +int __init s3c2416_init_intc_of(struct device_node *np, +			struct device_node *interrupt_parent, +			struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) +{ +	return s3c_init_intc_of(np, interrupt_parent, +				s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl)); +} +IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of); +#endif diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig index 5313c9ea44d..d9edcc94c2a 100644 --- a/drivers/isdn/hisax/Kconfig +++ b/drivers/isdn/hisax/Kconfig @@ -237,7 +237,8 @@ config HISAX_MIC  config HISAX_NETJET  	bool "NETjet card" -	depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN))) +	depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN))) +	depends on VIRT_TO_BUS  	help  	  This enables HiSax support for the NetJet from Traverse  	  Technologies. @@ -248,7 +249,8 @@ config HISAX_NETJET  config HISAX_NETJET_U  	bool "NETspider U card" -	depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN))) +	depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN))) +	depends on VIRT_TO_BUS  	help  	  This enables HiSax support for the Netspider U interface ISDN card  	  from Traverse Technologies. diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index d8a7d832341..ebaebdf30f9 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -902,7 +902,9 @@ isdn_tty_send_msg(modem_info *info, atemu *m, char *msg)  	int j;  	int l; -	l = strlen(msg); +	l = min(strlen(msg), sizeof(cmd.parm) - sizeof(cmd.parm.cmsg) +		+ sizeof(cmd.parm.cmsg.para) - 2); +  	if (!l) {  		isdn_tty_modem_result(RESULT_ERROR, info);  		return; diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 3c955e10a61..c6083132c4b 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -1025,6 +1025,8 @@ void dm_bufio_prefetch(struct dm_bufio_client *c,  {  	struct blk_plug plug; +	BUG_ON(dm_bufio_in_request()); +  	blk_start_plug(&plug);  	dm_bufio_lock(c); diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index fbd3625f274..83e995fece8 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -83,6 +83,8 @@ struct cache_disk_superblock {  	__le32 read_misses;  	__le32 write_hits;  	__le32 write_misses; + +	__le32 policy_version[CACHE_POLICY_VERSION_SIZE];  } __packed;  struct dm_cache_metadata { @@ -109,6 +111,7 @@ struct dm_cache_metadata {  	bool clean_when_opened:1;  	char policy_name[CACHE_POLICY_NAME_SIZE]; +	unsigned policy_version[CACHE_POLICY_VERSION_SIZE];  	size_t policy_hint_size;  	struct dm_cache_statistics stats;  }; @@ -268,7 +271,8 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)  	memset(disk_super->uuid, 0, sizeof(disk_super->uuid));  	disk_super->magic = cpu_to_le64(CACHE_SUPERBLOCK_MAGIC);  	disk_super->version = cpu_to_le32(CACHE_VERSION); -	memset(disk_super->policy_name, 0, CACHE_POLICY_NAME_SIZE); +	memset(disk_super->policy_name, 0, sizeof(disk_super->policy_name)); +	memset(disk_super->policy_version, 0, sizeof(disk_super->policy_version));  	disk_super->policy_hint_size = 0;  	r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root, @@ -284,7 +288,6 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)  	disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);  	disk_super->data_block_size = cpu_to_le32(cmd->data_block_size);  	disk_super->cache_blocks = cpu_to_le32(0); -	memset(disk_super->policy_name, 0, sizeof(disk_super->policy_name));  	disk_super->read_hits = cpu_to_le32(0);  	disk_super->read_misses = cpu_to_le32(0); @@ -478,6 +481,9 @@ static void read_superblock_fields(struct dm_cache_metadata *cmd,  	cmd->data_block_size = le32_to_cpu(disk_super->data_block_size);  	cmd->cache_blocks = to_cblock(le32_to_cpu(disk_super->cache_blocks));  	strncpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name)); +	cmd->policy_version[0] = le32_to_cpu(disk_super->policy_version[0]); +	cmd->policy_version[1] = le32_to_cpu(disk_super->policy_version[1]); +	cmd->policy_version[2] = le32_to_cpu(disk_super->policy_version[2]);  	cmd->policy_hint_size = le32_to_cpu(disk_super->policy_hint_size);  	cmd->stats.read_hits = le32_to_cpu(disk_super->read_hits); @@ -572,6 +578,9 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,  	disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));  	disk_super->cache_blocks = cpu_to_le32(from_cblock(cmd->cache_blocks));  	strncpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name)); +	disk_super->policy_version[0] = cpu_to_le32(cmd->policy_version[0]); +	disk_super->policy_version[1] = cpu_to_le32(cmd->policy_version[1]); +	disk_super->policy_version[2] = cpu_to_le32(cmd->policy_version[2]);  	disk_super->read_hits = cpu_to_le32(cmd->stats.read_hits);  	disk_super->read_misses = cpu_to_le32(cmd->stats.read_misses); @@ -854,18 +863,43 @@ struct thunk {  	bool hints_valid;  }; +static bool policy_unchanged(struct dm_cache_metadata *cmd, +			     struct dm_cache_policy *policy) +{ +	const char *policy_name = dm_cache_policy_get_name(policy); +	const unsigned *policy_version = dm_cache_policy_get_version(policy); +	size_t policy_hint_size = dm_cache_policy_get_hint_size(policy); + +	/* +	 * Ensure policy names match. +	 */ +	if (strncmp(cmd->policy_name, policy_name, sizeof(cmd->policy_name))) +		return false; + +	/* +	 * Ensure policy major versions match. +	 */ +	if (cmd->policy_version[0] != policy_version[0]) +		return false; + +	/* +	 * Ensure policy hint sizes match. +	 */ +	if (cmd->policy_hint_size != policy_hint_size) +		return false; + +	return true; +} +  static bool hints_array_initialized(struct dm_cache_metadata *cmd)  {  	return cmd->hint_root && cmd->policy_hint_size;  }  static bool hints_array_available(struct dm_cache_metadata *cmd, -				  const char *policy_name) +				  struct dm_cache_policy *policy)  { -	bool policy_names_match = !strncmp(cmd->policy_name, policy_name, -					   sizeof(cmd->policy_name)); - -	return cmd->clean_when_opened && policy_names_match && +	return cmd->clean_when_opened && policy_unchanged(cmd, policy) &&  		hints_array_initialized(cmd);  } @@ -899,7 +933,8 @@ static int __load_mapping(void *context, uint64_t cblock, void *leaf)  	return r;  } -static int __load_mappings(struct dm_cache_metadata *cmd, const char *policy_name, +static int __load_mappings(struct dm_cache_metadata *cmd, +			   struct dm_cache_policy *policy,  			   load_mapping_fn fn, void *context)  {  	struct thunk thunk; @@ -909,18 +944,19 @@ static int __load_mappings(struct dm_cache_metadata *cmd, const char *policy_nam  	thunk.cmd = cmd;  	thunk.respect_dirty_flags = cmd->clean_when_opened; -	thunk.hints_valid = hints_array_available(cmd, policy_name); +	thunk.hints_valid = hints_array_available(cmd, policy);  	return dm_array_walk(&cmd->info, cmd->root, __load_mapping, &thunk);  } -int dm_cache_load_mappings(struct dm_cache_metadata *cmd, const char *policy_name, +int dm_cache_load_mappings(struct dm_cache_metadata *cmd, +			   struct dm_cache_policy *policy,  			   load_mapping_fn fn, void *context)  {  	int r;  	down_read(&cmd->root_lock); -	r = __load_mappings(cmd, policy_name, fn, context); +	r = __load_mappings(cmd, policy, fn, context);  	up_read(&cmd->root_lock);  	return r; @@ -979,7 +1015,7 @@ static int __dirty(struct dm_cache_metadata *cmd, dm_cblock_t cblock, bool dirty  		/* nothing to be done */  		return 0; -	value = pack_value(oblock, flags | (dirty ? M_DIRTY : 0)); +	value = pack_value(oblock, (flags & ~M_DIRTY) | (dirty ? M_DIRTY : 0));  	__dm_bless_for_disk(&value);  	r = dm_array_set_value(&cmd->info, cmd->root, from_cblock(cblock), @@ -1070,13 +1106,15 @@ static int begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *po  	__le32 value;  	size_t hint_size;  	const char *policy_name = dm_cache_policy_get_name(policy); +	const unsigned *policy_version = dm_cache_policy_get_version(policy);  	if (!policy_name[0] ||  	    (strlen(policy_name) > sizeof(cmd->policy_name) - 1))  		return -EINVAL; -	if (strcmp(cmd->policy_name, policy_name)) { +	if (!policy_unchanged(cmd, policy)) {  		strncpy(cmd->policy_name, policy_name, sizeof(cmd->policy_name)); +		memcpy(cmd->policy_version, policy_version, sizeof(cmd->policy_version));  		hint_size = dm_cache_policy_get_hint_size(policy);  		if (!hint_size) diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h index 135864ea0ee..f45cef21f3d 100644 --- a/drivers/md/dm-cache-metadata.h +++ b/drivers/md/dm-cache-metadata.h @@ -89,7 +89,7 @@ typedef int (*load_mapping_fn)(void *context, dm_oblock_t oblock,  			       dm_cblock_t cblock, bool dirty,  			       uint32_t hint, bool hint_valid);  int dm_cache_load_mappings(struct dm_cache_metadata *cmd, -			   const char *policy_name, +			   struct dm_cache_policy *policy,  			   load_mapping_fn fn,  			   void *context); diff --git a/drivers/md/dm-cache-policy-cleaner.c b/drivers/md/dm-cache-policy-cleaner.c index cc05d70b3cb..b04d1f904d0 100644 --- a/drivers/md/dm-cache-policy-cleaner.c +++ b/drivers/md/dm-cache-policy-cleaner.c @@ -17,7 +17,6 @@  /*----------------------------------------------------------------*/  #define DM_MSG_PREFIX "cache cleaner" -#define CLEANER_VERSION "1.0.0"  /* Cache entry struct. */  struct wb_cache_entry { @@ -434,6 +433,7 @@ static struct dm_cache_policy *wb_create(dm_cblock_t cache_size,  static struct dm_cache_policy_type wb_policy_type = {  	.name = "cleaner", +	.version = {1, 0, 0},  	.hint_size = 0,  	.owner = THIS_MODULE,  	.create = wb_create @@ -446,7 +446,10 @@ static int __init wb_init(void)  	if (r < 0)  		DMERR("register failed %d", r);  	else -		DMINFO("version " CLEANER_VERSION " loaded"); +		DMINFO("version %u.%u.%u loaded", +		       wb_policy_type.version[0], +		       wb_policy_type.version[1], +		       wb_policy_type.version[2]);  	return r;  } diff --git a/drivers/md/dm-cache-policy-internal.h b/drivers/md/dm-cache-policy-internal.h index 52a75beeced..0928abdc49f 100644 --- a/drivers/md/dm-cache-policy-internal.h +++ b/drivers/md/dm-cache-policy-internal.h @@ -117,6 +117,8 @@ void dm_cache_policy_destroy(struct dm_cache_policy *p);   */  const char *dm_cache_policy_get_name(struct dm_cache_policy *p); +const unsigned *dm_cache_policy_get_version(struct dm_cache_policy *p); +  size_t dm_cache_policy_get_hint_size(struct dm_cache_policy *p);  /*----------------------------------------------------------------*/ diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c index 96415325507..dc112a7137f 100644 --- a/drivers/md/dm-cache-policy-mq.c +++ b/drivers/md/dm-cache-policy-mq.c @@ -14,7 +14,6 @@  #include <linux/vmalloc.h>  #define DM_MSG_PREFIX "cache-policy-mq" -#define MQ_VERSION	"1.0.0"  static struct kmem_cache *mq_entry_cache; @@ -1133,6 +1132,7 @@ bad_cache_alloc:  static struct dm_cache_policy_type mq_policy_type = {  	.name = "mq", +	.version = {1, 0, 0},  	.hint_size = 4,  	.owner = THIS_MODULE,  	.create = mq_create @@ -1140,6 +1140,7 @@ static struct dm_cache_policy_type mq_policy_type = {  static struct dm_cache_policy_type default_policy_type = {  	.name = "default", +	.version = {1, 0, 0},  	.hint_size = 4,  	.owner = THIS_MODULE,  	.create = mq_create @@ -1164,7 +1165,10 @@ static int __init mq_init(void)  	r = dm_cache_policy_register(&default_policy_type);  	if (!r) { -		DMINFO("version " MQ_VERSION " loaded"); +		DMINFO("version %u.%u.%u loaded", +		       mq_policy_type.version[0], +		       mq_policy_type.version[1], +		       mq_policy_type.version[2]);  		return 0;  	} diff --git a/drivers/md/dm-cache-policy.c b/drivers/md/dm-cache-policy.c index 2cbf5fdaac5..21c03c570c0 100644 --- a/drivers/md/dm-cache-policy.c +++ b/drivers/md/dm-cache-policy.c @@ -150,6 +150,14 @@ const char *dm_cache_policy_get_name(struct dm_cache_policy *p)  }  EXPORT_SYMBOL_GPL(dm_cache_policy_get_name); +const unsigned *dm_cache_policy_get_version(struct dm_cache_policy *p) +{ +	struct dm_cache_policy_type *t = p->private; + +	return t->version; +} +EXPORT_SYMBOL_GPL(dm_cache_policy_get_version); +  size_t dm_cache_policy_get_hint_size(struct dm_cache_policy *p)  {  	struct dm_cache_policy_type *t = p->private; diff --git a/drivers/md/dm-cache-policy.h b/drivers/md/dm-cache-policy.h index f0f51b26054..558bdfdabf5 100644 --- a/drivers/md/dm-cache-policy.h +++ b/drivers/md/dm-cache-policy.h @@ -196,6 +196,7 @@ struct dm_cache_policy {   * We maintain a little register of the different policy types.   */  #define CACHE_POLICY_NAME_SIZE 16 +#define CACHE_POLICY_VERSION_SIZE 3  struct dm_cache_policy_type {  	/* For use by the register code only. */ @@ -206,6 +207,7 @@ struct dm_cache_policy_type {  	 * what gets passed on the target line to select your policy.  	 */  	char name[CACHE_POLICY_NAME_SIZE]; +	unsigned version[CACHE_POLICY_VERSION_SIZE];  	/*  	 * Policies may store a hint for each each cache block. diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 0f4e84b15c3..66120bd46d1 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -142,6 +142,7 @@ struct cache {  	spinlock_t lock;  	struct bio_list deferred_bios;  	struct bio_list deferred_flush_bios; +	struct bio_list deferred_writethrough_bios;  	struct list_head quiesced_migrations;  	struct list_head completed_migrations;  	struct list_head need_commit_migrations; @@ -158,7 +159,7 @@ struct cache {  	/*  	 * origin_blocks entries, discarded if set.  	 */ -	sector_t discard_block_size; /* a power of 2 times sectors per block */ +	uint32_t discard_block_size; /* a power of 2 times sectors per block */  	dm_dblock_t discard_nr_blocks;  	unsigned long *discard_bitset; @@ -199,6 +200,11 @@ struct per_bio_data {  	bool tick:1;  	unsigned req_nr:2;  	struct dm_deferred_entry *all_io_entry; + +	/* writethrough fields */ +	struct cache *cache; +	dm_cblock_t cblock; +	bio_end_io_t *saved_bi_end_io;  };  struct dm_cache_migration { @@ -412,17 +418,24 @@ static bool block_size_is_power_of_two(struct cache *cache)  	return cache->sectors_per_block_shift >= 0;  } +static dm_block_t block_div(dm_block_t b, uint32_t n) +{ +	do_div(b, n); + +	return b; +} +  static dm_dblock_t oblock_to_dblock(struct cache *cache, dm_oblock_t oblock)  { -	sector_t discard_blocks = cache->discard_block_size; +	uint32_t discard_blocks = cache->discard_block_size;  	dm_block_t b = from_oblock(oblock);  	if (!block_size_is_power_of_two(cache)) -		(void) sector_div(discard_blocks, cache->sectors_per_block); +		discard_blocks = discard_blocks / cache->sectors_per_block;  	else  		discard_blocks >>= cache->sectors_per_block_shift; -	(void) sector_div(b, discard_blocks); +	b = block_div(b, discard_blocks);  	return to_dblock(b);  } @@ -609,6 +622,56 @@ static void issue(struct cache *cache, struct bio *bio)  	spin_unlock_irqrestore(&cache->lock, flags);  } +static void defer_writethrough_bio(struct cache *cache, struct bio *bio) +{ +	unsigned long flags; + +	spin_lock_irqsave(&cache->lock, flags); +	bio_list_add(&cache->deferred_writethrough_bios, bio); +	spin_unlock_irqrestore(&cache->lock, flags); + +	wake_worker(cache); +} + +static void writethrough_endio(struct bio *bio, int err) +{ +	struct per_bio_data *pb = get_per_bio_data(bio); +	bio->bi_end_io = pb->saved_bi_end_io; + +	if (err) { +		bio_endio(bio, err); +		return; +	} + +	remap_to_cache(pb->cache, bio, pb->cblock); + +	/* +	 * We can't issue this bio directly, since we're in interrupt +	 * context.  So it get's put on a bio list for processing by the +	 * worker thread. +	 */ +	defer_writethrough_bio(pb->cache, bio); +} + +/* + * When running in writethrough mode we need to send writes to clean blocks + * to both the cache and origin devices.  In future we'd like to clone the + * bio and send them in parallel, but for now we're doing them in + * series as this is easier. + */ +static void remap_to_origin_then_cache(struct cache *cache, struct bio *bio, +				       dm_oblock_t oblock, dm_cblock_t cblock) +{ +	struct per_bio_data *pb = get_per_bio_data(bio); + +	pb->cache = cache; +	pb->cblock = cblock; +	pb->saved_bi_end_io = bio->bi_end_io; +	bio->bi_end_io = writethrough_endio; + +	remap_to_origin_clear_discard(pb->cache, bio, oblock); +} +  /*----------------------------------------------------------------   * Migration processing   * @@ -1002,7 +1065,7 @@ static void process_discard_bio(struct cache *cache, struct bio *bio)  	dm_block_t end_block = bio->bi_sector + bio_sectors(bio);  	dm_block_t b; -	(void) sector_div(end_block, cache->discard_block_size); +	end_block = block_div(end_block, cache->discard_block_size);  	for (b = start_block; b < end_block; b++)  		set_discard(cache, to_dblock(b)); @@ -1070,14 +1133,9 @@ static void process_bio(struct cache *cache, struct prealloc *structs,  		inc_hit_counter(cache, bio);  		pb->all_io_entry = dm_deferred_entry_inc(cache->all_io_ds); -		if (is_writethrough_io(cache, bio, lookup_result.cblock)) { -			/* -			 * No need to mark anything dirty in write through mode. -			 */ -			pb->req_nr == 0 ? -				remap_to_cache(cache, bio, lookup_result.cblock) : -				remap_to_origin_clear_discard(cache, bio, block); -		} else +		if (is_writethrough_io(cache, bio, lookup_result.cblock)) +			remap_to_origin_then_cache(cache, bio, block, lookup_result.cblock); +		else  			remap_to_cache_dirty(cache, bio, block, lookup_result.cblock);  		issue(cache, bio); @@ -1086,17 +1144,8 @@ static void process_bio(struct cache *cache, struct prealloc *structs,  	case POLICY_MISS:  		inc_miss_counter(cache, bio);  		pb->all_io_entry = dm_deferred_entry_inc(cache->all_io_ds); - -		if (pb->req_nr != 0) { -			/* -			 * This is a duplicate writethrough io that is no -			 * longer needed because the block has been demoted. -			 */ -			bio_endio(bio, 0); -		} else { -			remap_to_origin_clear_discard(cache, bio, block); -			issue(cache, bio); -		} +		remap_to_origin_clear_discard(cache, bio, block); +		issue(cache, bio);  		break;  	case POLICY_NEW: @@ -1217,6 +1266,23 @@ static void process_deferred_flush_bios(struct cache *cache, bool submit_bios)  		submit_bios ? generic_make_request(bio) : bio_io_error(bio);  } +static void process_deferred_writethrough_bios(struct cache *cache) +{ +	unsigned long flags; +	struct bio_list bios; +	struct bio *bio; + +	bio_list_init(&bios); + +	spin_lock_irqsave(&cache->lock, flags); +	bio_list_merge(&bios, &cache->deferred_writethrough_bios); +	bio_list_init(&cache->deferred_writethrough_bios); +	spin_unlock_irqrestore(&cache->lock, flags); + +	while ((bio = bio_list_pop(&bios))) +		generic_make_request(bio); +} +  static void writeback_some_dirty_blocks(struct cache *cache)  {  	int r = 0; @@ -1313,6 +1379,7 @@ static int more_work(struct cache *cache)  	else  		return !bio_list_empty(&cache->deferred_bios) ||  			!bio_list_empty(&cache->deferred_flush_bios) || +			!bio_list_empty(&cache->deferred_writethrough_bios) ||  			!list_empty(&cache->quiesced_migrations) ||  			!list_empty(&cache->completed_migrations) ||  			!list_empty(&cache->need_commit_migrations); @@ -1331,6 +1398,8 @@ static void do_worker(struct work_struct *ws)  		writeback_some_dirty_blocks(cache); +		process_deferred_writethrough_bios(cache); +  		if (commit_if_needed(cache)) {  			process_deferred_flush_bios(cache, false); @@ -1756,8 +1825,11 @@ static int create_cache_policy(struct cache *cache, struct cache_args *ca,  	}  	r = set_config_values(cache->policy, ca->policy_argc, ca->policy_argv); -	if (r) +	if (r) { +		*error = "Error setting cache policy's config values";  		dm_cache_policy_destroy(cache->policy); +		cache->policy = NULL; +	}  	return r;  } @@ -1793,8 +1865,6 @@ static sector_t calculate_discard_block_size(sector_t cache_block_size,  #define DEFAULT_MIGRATION_THRESHOLD (2048 * 100) -static unsigned cache_num_write_bios(struct dm_target *ti, struct bio *bio); -  static int cache_create(struct cache_args *ca, struct cache **result)  {  	int r = 0; @@ -1821,9 +1891,6 @@ static int cache_create(struct cache_args *ca, struct cache **result)  	memcpy(&cache->features, &ca->features, sizeof(cache->features)); -	if (cache->features.write_through) -		ti->num_write_bios = cache_num_write_bios; -  	cache->callbacks.congested_fn = cache_is_congested;  	dm_table_add_target_callbacks(ti->table, &cache->callbacks); @@ -1835,7 +1902,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)  	/* FIXME: factor out this whole section */  	origin_blocks = cache->origin_sectors = ca->origin_sectors; -	(void) sector_div(origin_blocks, ca->block_size); +	origin_blocks = block_div(origin_blocks, ca->block_size);  	cache->origin_blocks = to_oblock(origin_blocks);  	cache->sectors_per_block = ca->block_size; @@ -1848,7 +1915,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)  		dm_block_t cache_size = ca->cache_sectors;  		cache->sectors_per_block_shift = -1; -		(void) sector_div(cache_size, ca->block_size); +		cache_size = block_div(cache_size, ca->block_size);  		cache->cache_size = to_cblock(cache_size);  	} else {  		cache->sectors_per_block_shift = __ffs(ca->block_size); @@ -1873,6 +1940,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)  	spin_lock_init(&cache->lock);  	bio_list_init(&cache->deferred_bios);  	bio_list_init(&cache->deferred_flush_bios); +	bio_list_init(&cache->deferred_writethrough_bios);  	INIT_LIST_HEAD(&cache->quiesced_migrations);  	INIT_LIST_HEAD(&cache->completed_migrations);  	INIT_LIST_HEAD(&cache->need_commit_migrations); @@ -2002,6 +2070,8 @@ static int cache_ctr(struct dm_target *ti, unsigned argc, char **argv)  		goto out;  	r = cache_create(ca, &cache); +	if (r) +		goto out;  	r = copy_ctr_args(cache, argc - 3, (const char **)argv + 3);  	if (r) { @@ -2016,20 +2086,6 @@ out:  	return r;  } -static unsigned cache_num_write_bios(struct dm_target *ti, struct bio *bio) -{ -	int r; -	struct cache *cache = ti->private; -	dm_oblock_t block = get_bio_block(cache, bio); -	dm_cblock_t cblock; - -	r = policy_lookup(cache->policy, block, &cblock); -	if (r < 0) -		return 2;	/* assume the worst */ - -	return (!r && !is_dirty(cache, cblock)) ? 2 : 1; -} -  static int cache_map(struct dm_target *ti, struct bio *bio)  {  	struct cache *cache = ti->private; @@ -2097,18 +2153,12 @@ static int cache_map(struct dm_target *ti, struct bio *bio)  		inc_hit_counter(cache, bio);  		pb->all_io_entry = dm_deferred_entry_inc(cache->all_io_ds); -		if (is_writethrough_io(cache, bio, lookup_result.cblock)) { -			/* -			 * No need to mark anything dirty in write through mode. -			 */ -			pb->req_nr == 0 ? -				remap_to_cache(cache, bio, lookup_result.cblock) : -				remap_to_origin_clear_discard(cache, bio, block); -			cell_defer(cache, cell, false); -		} else { +		if (is_writethrough_io(cache, bio, lookup_result.cblock)) +			remap_to_origin_then_cache(cache, bio, block, lookup_result.cblock); +		else  			remap_to_cache_dirty(cache, bio, block, lookup_result.cblock); -			cell_defer(cache, cell, false); -		} + +		cell_defer(cache, cell, false);  		break;  	case POLICY_MISS: @@ -2319,8 +2369,7 @@ static int cache_preresume(struct dm_target *ti)  	}  	if (!cache->loaded_mappings) { -		r = dm_cache_load_mappings(cache->cmd, -					   dm_cache_policy_get_name(cache->policy), +		r = dm_cache_load_mappings(cache->cmd, cache->policy,  					   load_mapping, cache);  		if (r) {  			DMERR("could not load cache mappings"); @@ -2535,7 +2584,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)  static struct target_type cache_target = {  	.name = "cache", -	.version = {1, 0, 0}, +	.version = {1, 1, 0},  	.module = THIS_MODULE,  	.ctr = cache_ctr,  	.dtr = cache_dtr, diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 009339d6282..004ad1652b7 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -1577,6 +1577,11 @@ static bool data_dev_supports_discard(struct pool_c *pt)  	return q && blk_queue_discard(q);  } +static bool is_factor(sector_t block_size, uint32_t n) +{ +	return !sector_div(block_size, n); +} +  /*   * If discard_passdown was enabled verify that the data device   * supports discards.  Disable discard_passdown if not. @@ -1602,7 +1607,7 @@ static void disable_passdown_if_not_supported(struct pool_c *pt)  	else if (data_limits->discard_granularity > block_size)  		reason = "discard granularity larger than a block"; -	else if (block_size & (data_limits->discard_granularity - 1)) +	else if (!is_factor(block_size, data_limits->discard_granularity))  		reason = "discard granularity not a factor of block size";  	if (reason) { @@ -2544,7 +2549,7 @@ static struct target_type pool_target = {  	.name = "thin-pool",  	.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |  		    DM_TARGET_IMMUTABLE, -	.version = {1, 6, 1}, +	.version = {1, 7, 0},  	.module = THIS_MODULE,  	.ctr = pool_ctr,  	.dtr = pool_dtr, @@ -2831,7 +2836,7 @@ static int thin_iterate_devices(struct dm_target *ti,  static struct target_type thin_target = {  	.name = "thin", -	.version = {1, 7, 1}, +	.version = {1, 8, 0},  	.module	= THIS_MODULE,  	.ctr = thin_ctr,  	.dtr = thin_dtr, diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c index 6ad538375c3..a746f1d21c6 100644 --- a/drivers/md/dm-verity.c +++ b/drivers/md/dm-verity.c @@ -93,6 +93,13 @@ struct dm_verity_io {  	 */  }; +struct dm_verity_prefetch_work { +	struct work_struct work; +	struct dm_verity *v; +	sector_t block; +	unsigned n_blocks; +}; +  static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)  {  	return (struct shash_desc *)(io + 1); @@ -424,15 +431,18 @@ static void verity_end_io(struct bio *bio, int error)   * The root buffer is not prefetched, it is assumed that it will be cached   * all the time.   */ -static void verity_prefetch_io(struct dm_verity *v, struct dm_verity_io *io) +static void verity_prefetch_io(struct work_struct *work)  { +	struct dm_verity_prefetch_work *pw = +		container_of(work, struct dm_verity_prefetch_work, work); +	struct dm_verity *v = pw->v;  	int i;  	for (i = v->levels - 2; i >= 0; i--) {  		sector_t hash_block_start;  		sector_t hash_block_end; -		verity_hash_at_level(v, io->block, i, &hash_block_start, NULL); -		verity_hash_at_level(v, io->block + io->n_blocks - 1, i, &hash_block_end, NULL); +		verity_hash_at_level(v, pw->block, i, &hash_block_start, NULL); +		verity_hash_at_level(v, pw->block + pw->n_blocks - 1, i, &hash_block_end, NULL);  		if (!i) {  			unsigned cluster = ACCESS_ONCE(dm_verity_prefetch_cluster); @@ -452,6 +462,25 @@ no_prefetch_cluster:  		dm_bufio_prefetch(v->bufio, hash_block_start,  				  hash_block_end - hash_block_start + 1);  	} + +	kfree(pw); +} + +static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io) +{ +	struct dm_verity_prefetch_work *pw; + +	pw = kmalloc(sizeof(struct dm_verity_prefetch_work), +		GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); + +	if (!pw) +		return; + +	INIT_WORK(&pw->work, verity_prefetch_io); +	pw->v = v; +	pw->block = io->block; +	pw->n_blocks = io->n_blocks; +	queue_work(v->verify_wq, &pw->work);  }  /* @@ -498,7 +527,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)  	memcpy(io->io_vec, bio_iovec(bio),  	       io->io_vec_size * sizeof(struct bio_vec)); -	verity_prefetch_io(v, io); +	verity_submit_prefetch(v, io);  	generic_make_request(bio); @@ -858,7 +887,7 @@ bad:  static struct target_type verity_target = {  	.name		= "verity", -	.version	= {1, 1, 1}, +	.version	= {1, 2, 0},  	.module		= THIS_MODULE,  	.ctr		= verity_ctr,  	.dtr		= verity_dtr, diff --git a/drivers/md/md.c b/drivers/md/md.c index fcb878f8879..aeceedfc530 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7663,10 +7663,8 @@ static int remove_and_add_spares(struct mddev *mddev)  				removed++;  			}  		} -	if (removed) -		sysfs_notify(&mddev->kobj, NULL, -			     "degraded"); - +	if (removed && mddev->kobj.sd) +		sysfs_notify(&mddev->kobj, NULL, "degraded");  	rdev_for_each(rdev, mddev) {  		if (rdev->raid_disk >= 0 && diff --git a/drivers/md/md.h b/drivers/md/md.h index eca59c3074e..d90fb1a879e 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -506,7 +506,7 @@ static inline char * mdname (struct mddev * mddev)  static inline int sysfs_link_rdev(struct mddev *mddev, struct md_rdev *rdev)  {  	char nm[20]; -	if (!test_bit(Replacement, &rdev->flags)) { +	if (!test_bit(Replacement, &rdev->flags) && mddev->kobj.sd) {  		sprintf(nm, "rd%d", rdev->raid_disk);  		return sysfs_create_link(&mddev->kobj, &rdev->kobj, nm);  	} else @@ -516,7 +516,7 @@ static inline int sysfs_link_rdev(struct mddev *mddev, struct md_rdev *rdev)  static inline void sysfs_unlink_rdev(struct mddev *mddev, struct md_rdev *rdev)  {  	char nm[20]; -	if (!test_bit(Replacement, &rdev->flags)) { +	if (!test_bit(Replacement, &rdev->flags) && mddev->kobj.sd) {  		sprintf(nm, "rd%d", rdev->raid_disk);  		sysfs_remove_link(&mddev->kobj, nm);  	} diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c index c4f28133ef8..b88757cd0d1 100644 --- a/drivers/md/persistent-data/dm-btree-remove.c +++ b/drivers/md/persistent-data/dm-btree-remove.c @@ -139,15 +139,8 @@ struct child {  	struct btree_node *n;  }; -static struct dm_btree_value_type le64_type = { -	.context = NULL, -	.size = sizeof(__le64), -	.inc = NULL, -	.dec = NULL, -	.equal = NULL -}; - -static int init_child(struct dm_btree_info *info, struct btree_node *parent, +static int init_child(struct dm_btree_info *info, struct dm_btree_value_type *vt, +		      struct btree_node *parent,  		      unsigned index, struct child *result)  {  	int r, inc; @@ -164,7 +157,7 @@ static int init_child(struct dm_btree_info *info, struct btree_node *parent,  	result->n = dm_block_data(result->block);  	if (inc) -		inc_children(info->tm, result->n, &le64_type); +		inc_children(info->tm, result->n, vt);  	*((__le64 *) value_ptr(parent, index)) =  		cpu_to_le64(dm_block_location(result->block)); @@ -236,7 +229,7 @@ static void __rebalance2(struct dm_btree_info *info, struct btree_node *parent,  }  static int rebalance2(struct shadow_spine *s, struct dm_btree_info *info, -		      unsigned left_index) +		      struct dm_btree_value_type *vt, unsigned left_index)  {  	int r;  	struct btree_node *parent; @@ -244,11 +237,11 @@ static int rebalance2(struct shadow_spine *s, struct dm_btree_info *info,  	parent = dm_block_data(shadow_current(s)); -	r = init_child(info, parent, left_index, &left); +	r = init_child(info, vt, parent, left_index, &left);  	if (r)  		return r; -	r = init_child(info, parent, left_index + 1, &right); +	r = init_child(info, vt, parent, left_index + 1, &right);  	if (r) {  		exit_child(info, &left);  		return r; @@ -368,7 +361,7 @@ static void __rebalance3(struct dm_btree_info *info, struct btree_node *parent,  }  static int rebalance3(struct shadow_spine *s, struct dm_btree_info *info, -		      unsigned left_index) +		      struct dm_btree_value_type *vt, unsigned left_index)  {  	int r;  	struct btree_node *parent = dm_block_data(shadow_current(s)); @@ -377,17 +370,17 @@ static int rebalance3(struct shadow_spine *s, struct dm_btree_info *info,  	/*  	 * FIXME: fill out an array?  	 */ -	r = init_child(info, parent, left_index, &left); +	r = init_child(info, vt, parent, left_index, &left);  	if (r)  		return r; -	r = init_child(info, parent, left_index + 1, ¢er); +	r = init_child(info, vt, parent, left_index + 1, ¢er);  	if (r) {  		exit_child(info, &left);  		return r;  	} -	r = init_child(info, parent, left_index + 2, &right); +	r = init_child(info, vt, parent, left_index + 2, &right);  	if (r) {  		exit_child(info, &left);  		exit_child(info, ¢er); @@ -434,7 +427,8 @@ static int get_nr_entries(struct dm_transaction_manager *tm,  }  static int rebalance_children(struct shadow_spine *s, -			      struct dm_btree_info *info, uint64_t key) +			      struct dm_btree_info *info, +			      struct dm_btree_value_type *vt, uint64_t key)  {  	int i, r, has_left_sibling, has_right_sibling;  	uint32_t child_entries; @@ -472,13 +466,13 @@ static int rebalance_children(struct shadow_spine *s,  	has_right_sibling = i < (le32_to_cpu(n->header.nr_entries) - 1);  	if (!has_left_sibling) -		r = rebalance2(s, info, i); +		r = rebalance2(s, info, vt, i);  	else if (!has_right_sibling) -		r = rebalance2(s, info, i - 1); +		r = rebalance2(s, info, vt, i - 1);  	else -		r = rebalance3(s, info, i - 1); +		r = rebalance3(s, info, vt, i - 1);  	return r;  } @@ -529,7 +523,7 @@ static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info,  		if (le32_to_cpu(n->header.flags) & LEAF_NODE)  			return do_leaf(n, key, index); -		r = rebalance_children(s, info, key); +		r = rebalance_children(s, info, vt, key);  		if (r)  			break; @@ -550,6 +544,14 @@ static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info,  	return r;  } +static struct dm_btree_value_type le64_type = { +	.context = NULL, +	.size = sizeof(__le64), +	.inc = NULL, +	.dec = NULL, +	.equal = NULL +}; +  int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,  		    uint64_t *keys, dm_block_t *new_root)  { diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 3ee2912889e..24909eb13fe 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -671,9 +671,11 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)  			bi->bi_next = NULL;  			if (rrdev)  				set_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags); -			trace_block_bio_remap(bdev_get_queue(bi->bi_bdev), -					      bi, disk_devt(conf->mddev->gendisk), -					      sh->dev[i].sector); + +			if (conf->mddev->gendisk) +				trace_block_bio_remap(bdev_get_queue(bi->bi_bdev), +						      bi, disk_devt(conf->mddev->gendisk), +						      sh->dev[i].sector);  			generic_make_request(bi);  		}  		if (rrdev) { @@ -701,9 +703,10 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)  			rbi->bi_io_vec[0].bv_offset = 0;  			rbi->bi_size = STRIPE_SIZE;  			rbi->bi_next = NULL; -			trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev), -					      rbi, disk_devt(conf->mddev->gendisk), -					      sh->dev[i].sector); +			if (conf->mddev->gendisk) +				trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev), +						      rbi, disk_devt(conf->mddev->gendisk), +						      sh->dev[i].sector);  			generic_make_request(rbi);  		}  		if (!rdev && !rrdev) { @@ -2280,17 +2283,6 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,  	int level = conf->level;  	if (rcw) { -		/* if we are not expanding this is a proper write request, and -		 * there will be bios with new data to be drained into the -		 * stripe cache -		 */ -		if (!expand) { -			sh->reconstruct_state = reconstruct_state_drain_run; -			set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); -		} else -			sh->reconstruct_state = reconstruct_state_run; - -		set_bit(STRIPE_OP_RECONSTRUCT, &s->ops_request);  		for (i = disks; i--; ) {  			struct r5dev *dev = &sh->dev[i]; @@ -2303,6 +2295,21 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,  				s->locked++;  			}  		} +		/* if we are not expanding this is a proper write request, and +		 * there will be bios with new data to be drained into the +		 * stripe cache +		 */ +		if (!expand) { +			if (!s->locked) +				/* False alarm, nothing to do */ +				return; +			sh->reconstruct_state = reconstruct_state_drain_run; +			set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); +		} else +			sh->reconstruct_state = reconstruct_state_run; + +		set_bit(STRIPE_OP_RECONSTRUCT, &s->ops_request); +  		if (s->locked + conf->max_degraded == disks)  			if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state))  				atomic_inc(&conf->pending_full_writes); @@ -2311,11 +2318,6 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,  		BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) ||  			test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); -		sh->reconstruct_state = reconstruct_state_prexor_drain_run; -		set_bit(STRIPE_OP_PREXOR, &s->ops_request); -		set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); -		set_bit(STRIPE_OP_RECONSTRUCT, &s->ops_request); -  		for (i = disks; i--; ) {  			struct r5dev *dev = &sh->dev[i];  			if (i == pd_idx) @@ -2330,6 +2332,13 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,  				s->locked++;  			}  		} +		if (!s->locked) +			/* False alarm - nothing to do */ +			return; +		sh->reconstruct_state = reconstruct_state_prexor_drain_run; +		set_bit(STRIPE_OP_PREXOR, &s->ops_request); +		set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); +		set_bit(STRIPE_OP_RECONSTRUCT, &s->ops_request);  	}  	/* keep the parity disk(s) locked while asynchronous operations @@ -2564,6 +2573,8 @@ handle_failed_sync(struct r5conf *conf, struct stripe_head *sh,  	int i;  	clear_bit(STRIPE_SYNCING, &sh->state); +	if (test_and_clear_bit(R5_Overlap, &sh->dev[sh->pd_idx].flags)) +		wake_up(&conf->wait_for_overlap);  	s->syncing = 0;  	s->replacing = 0;  	/* There is nothing more to do for sync/check/repair. @@ -2737,6 +2748,7 @@ static void handle_stripe_clean_event(struct r5conf *conf,  {  	int i;  	struct r5dev *dev; +	int discard_pending = 0;  	for (i = disks; i--; )  		if (sh->dev[i].written) { @@ -2765,9 +2777,23 @@ static void handle_stripe_clean_event(struct r5conf *conf,  						STRIPE_SECTORS,  					 !test_bit(STRIPE_DEGRADED, &sh->state),  						0); -			} -		} else if (test_bit(R5_Discard, &sh->dev[i].flags)) -			clear_bit(R5_Discard, &sh->dev[i].flags); +			} else if (test_bit(R5_Discard, &dev->flags)) +				discard_pending = 1; +		} +	if (!discard_pending && +	    test_bit(R5_Discard, &sh->dev[sh->pd_idx].flags)) { +		clear_bit(R5_Discard, &sh->dev[sh->pd_idx].flags); +		clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); +		if (sh->qd_idx >= 0) { +			clear_bit(R5_Discard, &sh->dev[sh->qd_idx].flags); +			clear_bit(R5_UPTODATE, &sh->dev[sh->qd_idx].flags); +		} +		/* now that discard is done we can proceed with any sync */ +		clear_bit(STRIPE_DISCARD, &sh->state); +		if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state)) +			set_bit(STRIPE_HANDLE, &sh->state); + +	}  	if (test_and_clear_bit(STRIPE_FULL_WRITE, &sh->state))  		if (atomic_dec_and_test(&conf->pending_full_writes)) @@ -2826,8 +2852,10 @@ static void handle_stripe_dirtying(struct r5conf *conf,  	set_bit(STRIPE_HANDLE, &sh->state);  	if (rmw < rcw && rmw > 0) {  		/* prefer read-modify-write, but need to get some data */ -		blk_add_trace_msg(conf->mddev->queue, "raid5 rmw %llu %d", -				  (unsigned long long)sh->sector, rmw); +		if (conf->mddev->queue) +			blk_add_trace_msg(conf->mddev->queue, +					  "raid5 rmw %llu %d", +					  (unsigned long long)sh->sector, rmw);  		for (i = disks; i--; ) {  			struct r5dev *dev = &sh->dev[i];  			if ((dev->towrite || i == sh->pd_idx) && @@ -2877,7 +2905,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,  				}  			}  		} -		if (rcw) +		if (rcw && conf->mddev->queue)  			blk_add_trace_msg(conf->mddev->queue, "raid5 rcw %llu %d %d %d",  					  (unsigned long long)sh->sector,  					  rcw, qread, test_bit(STRIPE_DELAYED, &sh->state)); @@ -3417,9 +3445,15 @@ static void handle_stripe(struct stripe_head *sh)  		return;  	} -	if (test_and_clear_bit(STRIPE_SYNC_REQUESTED, &sh->state)) { -		set_bit(STRIPE_SYNCING, &sh->state); -		clear_bit(STRIPE_INSYNC, &sh->state); +	if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state)) { +		spin_lock(&sh->stripe_lock); +		/* Cannot process 'sync' concurrently with 'discard' */ +		if (!test_bit(STRIPE_DISCARD, &sh->state) && +		    test_and_clear_bit(STRIPE_SYNC_REQUESTED, &sh->state)) { +			set_bit(STRIPE_SYNCING, &sh->state); +			clear_bit(STRIPE_INSYNC, &sh->state); +		} +		spin_unlock(&sh->stripe_lock);  	}  	clear_bit(STRIPE_DELAYED, &sh->state); @@ -3579,6 +3613,8 @@ static void handle_stripe(struct stripe_head *sh)  	    test_bit(STRIPE_INSYNC, &sh->state)) {  		md_done_sync(conf->mddev, STRIPE_SECTORS, 1);  		clear_bit(STRIPE_SYNCING, &sh->state); +		if (test_and_clear_bit(R5_Overlap, &sh->dev[sh->pd_idx].flags)) +			wake_up(&conf->wait_for_overlap);  	}  	/* If the failed drives are just a ReadError, then we might need @@ -3982,9 +4018,10 @@ static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio)  		atomic_inc(&conf->active_aligned_reads);  		spin_unlock_irq(&conf->device_lock); -		trace_block_bio_remap(bdev_get_queue(align_bi->bi_bdev), -				      align_bi, disk_devt(mddev->gendisk), -				      raid_bio->bi_sector); +		if (mddev->gendisk) +			trace_block_bio_remap(bdev_get_queue(align_bi->bi_bdev), +					      align_bi, disk_devt(mddev->gendisk), +					      raid_bio->bi_sector);  		generic_make_request(align_bi);  		return 1;  	} else { @@ -4078,7 +4115,8 @@ static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule)  		}  		spin_unlock_irq(&conf->device_lock);  	} -	trace_block_unplug(mddev->queue, cnt, !from_schedule); +	if (mddev->queue) +		trace_block_unplug(mddev->queue, cnt, !from_schedule);  	kfree(cb);  } @@ -4141,6 +4179,13 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)  		sh = get_active_stripe(conf, logical_sector, 0, 0, 0);  		prepare_to_wait(&conf->wait_for_overlap, &w,  				TASK_UNINTERRUPTIBLE); +		set_bit(R5_Overlap, &sh->dev[sh->pd_idx].flags); +		if (test_bit(STRIPE_SYNCING, &sh->state)) { +			release_stripe(sh); +			schedule(); +			goto again; +		} +		clear_bit(R5_Overlap, &sh->dev[sh->pd_idx].flags);  		spin_lock_irq(&sh->stripe_lock);  		for (d = 0; d < conf->raid_disks; d++) {  			if (d == sh->pd_idx || d == sh->qd_idx) @@ -4153,6 +4198,7 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)  				goto again;  			}  		} +		set_bit(STRIPE_DISCARD, &sh->state);  		finish_wait(&conf->wait_for_overlap, &w);  		for (d = 0; d < conf->raid_disks; d++) {  			if (d == sh->pd_idx || d == sh->qd_idx) diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 18b2c4a8a1f..b0b663b119a 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -221,10 +221,6 @@ struct stripe_head {  	struct stripe_operations {  		int 		     target, target2;  		enum sum_check_flags zero_sum_result; -		#ifdef CONFIG_MULTICORE_RAID456 -		unsigned long	     request; -		wait_queue_head_t    wait_for_ops; -		#endif  	} ops;  	struct r5dev {  		/* rreq and rvec are used for the replacement device when @@ -323,6 +319,7 @@ enum {  	STRIPE_COMPUTE_RUN,  	STRIPE_OPS_REQ_PENDING,  	STRIPE_ON_UNPLUG_LIST, +	STRIPE_DISCARD,  };  /* diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index d4e7567b367..0b899cb6cda 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -724,7 +724,7 @@ static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)  	if (enable) {  		if (is_code(code, M5MOLS_RESTYPE_MONITOR))  			ret = m5mols_start_monitor(info); -		if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) +		else if (is_code(code, M5MOLS_RESTYPE_CAPTURE))  			ret = m5mols_start_capture(info);  		else  			ret = -EINVAL; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index ccd18e4ee78..54579e4c740 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -250,17 +250,19 @@ static u8 SRAM_Table[][60] =     vdelay	start of active video in 2 * field lines relative to  		trailing edge of /VRESET pulse (VDELAY register).     sheight	height of active video in 2 * field lines. +   extraheight	Added to sheight for cropcap.bounds.height only     videostart0	ITU-R frame line number of the line corresponding  		to vdelay in the first field. */  #define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth,	 \ -		vdelay, sheight, videostart0)				 \ +		vdelay, sheight, extraheight, videostart0)		 \  	.cropcap.bounds.left = minhdelayx1,				 \  	/* * 2 because vertically we count field lines times two, */	 \  	/* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */		 \  	.cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \  	/* 4 is a safety margin at the end of the line. */		 \  	.cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4,	 \ -	.cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY,	 \ +	.cropcap.bounds.height = (sheight) + (extraheight) + (vdelay) -	 \ +				 MIN_VDELAY,				 \  	.cropcap.defrect.left = hdelayx1,				 \  	.cropcap.defrect.top = (videostart0) * 2,			 \  	.cropcap.defrect.width = swidth,				 \ @@ -301,9 +303,10 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* totalwidth */ 1135,  			/* sqwidth */ 944,  			/* vdelay */ 0x20, -		/* bt878 (and bt848?) can capture another -		   line below active video. */ -			/* sheight */ (576 + 2) + 0x20 - 2, +			/* sheight */ 576, +			/* bt878 (and bt848?) can capture another +			   line below active video. */ +			/* extraheight */ 2,  			/* videostart0 */ 23)  	},{  		.v4l2_id        = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, @@ -330,6 +333,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 780,  			/* vdelay */ 0x1a,  			/* sheight */ 480, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	},{  		.v4l2_id        = V4L2_STD_SECAM, @@ -355,6 +359,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 944,  			/* vdelay */ 0x20,  			/* sheight */ 576, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	},{  		.v4l2_id        = V4L2_STD_PAL_Nc, @@ -380,6 +385,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 780,  			/* vdelay */ 0x1a,  			/* sheight */ 576, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	},{  		.v4l2_id        = V4L2_STD_PAL_M, @@ -405,6 +411,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 780,  			/* vdelay */ 0x1a,  			/* sheight */ 480, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	},{  		.v4l2_id        = V4L2_STD_PAL_N, @@ -430,6 +437,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 944,  			/* vdelay */ 0x20,  			/* sheight */ 576, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	},{  		.v4l2_id        = V4L2_STD_NTSC_M_JP, @@ -455,6 +463,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 780,  			/* vdelay */ 0x16,  			/* sheight */ 480, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	},{  		/* that one hopefully works with the strange timing @@ -484,6 +493,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = {  			/* sqwidth */ 944,  			/* vdelay */ 0x1a,  			/* sheight */ 480, +			/* extraheight */ 0,  			/* videostart0 */ 23)  	}  }; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 82d9f6ac12f..33b5ffc8d66 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1054,16 +1054,18 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc)  static int gsc_m2m_resume(struct gsc_dev *gsc)  { +	struct gsc_ctx *ctx;  	unsigned long flags;  	spin_lock_irqsave(&gsc->slock, flags);  	/* Clear for full H/W setup in first run after resume */ +	ctx = gsc->m2m.ctx;  	gsc->m2m.ctx = NULL;  	spin_unlock_irqrestore(&gsc->slock, flags);  	if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state)) -		gsc_m2m_job_finish(gsc->m2m.ctx, -				    VB2_BUF_STATE_ERROR); +		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +  	return 0;  } @@ -1204,7 +1206,7 @@ static int gsc_resume(struct device *dev)  	/* Do not resume if the device was idle before system suspend */  	spin_lock_irqsave(&gsc->slock, flags);  	if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) || -	    !gsc_m2m_active(gsc)) { +	    !gsc_m2m_opened(gsc)) {  		spin_unlock_irqrestore(&gsc->slock, flags);  		return 0;  	} diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index e3916bde45c..0f513dd19f8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -850,16 +850,18 @@ static int fimc_m2m_suspend(struct fimc_dev *fimc)  static int fimc_m2m_resume(struct fimc_dev *fimc)  { +	struct fimc_ctx *ctx;  	unsigned long flags;  	spin_lock_irqsave(&fimc->slock, flags);  	/* Clear for full H/W setup in first run after resume */ +	ctx = fimc->m2m.ctx;  	fimc->m2m.ctx = NULL;  	spin_unlock_irqrestore(&fimc->slock, flags);  	if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state)) -		fimc_m2m_job_finish(fimc->m2m.ctx, -				    VB2_BUF_STATE_ERROR); +		fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +  	return 0;  } diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index f0af0754a7b..ac9663ce2a4 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -128,10 +128,10 @@ static const u32 src_pixfmt_map[8][3] = {  void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)  {  	enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code; -	unsigned int i = ARRAY_SIZE(src_pixfmt_map); +	int i = ARRAY_SIZE(src_pixfmt_map);  	u32 cfg; -	while (i-- >= 0) { +	while (--i >= 0) {  		if (src_pixfmt_map[i][0] == pixelcode)  			break;  	} @@ -224,9 +224,9 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)  		{ V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },  	};  	u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); -	unsigned int i = ARRAY_SIZE(pixcode); +	int i = ARRAY_SIZE(pixcode); -	while (i-- >= 0) +	while (--i >= 0)  		if (pixcode[i][0] == dev->fmt->mbus_code)  			break;  	cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index bfc4206935c..bbc35de7db2 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1408,6 +1408,7 @@ static const struct v4l2_ctrl_config fimc_lite_ctrl = {  	.id	= V4L2_CTRL_CLASS_USER | 0x1001,  	.type	= V4L2_CTRL_TYPE_BOOLEAN,  	.name	= "Test Pattern 640x480", +	.step	= 1,  };  static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index a17fcb2d5d4..cd38d708ab5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -827,7 +827,7 @@ static int fimc_md_link_notify(struct media_pad *source,  	struct fimc_pipeline *pipeline;  	struct v4l2_subdev *sd;  	struct mutex *lock; -	int ret = 0; +	int i, ret = 0;  	int ref_count;  	if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -854,29 +854,28 @@ static int fimc_md_link_notify(struct media_pad *source,  		return 0;  	} +	mutex_lock(lock); +	ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; +  	if (!(flags & MEDIA_LNK_FL_ENABLED)) { -		int i; -		mutex_lock(lock); -		ret = __fimc_pipeline_close(pipeline); +		if (ref_count > 0) { +			ret = __fimc_pipeline_close(pipeline); +			if (!ret && fimc) +				fimc_ctrls_delete(fimc->vid_cap.ctx); +		}  		for (i = 0; i < IDX_MAX; i++)  			pipeline->subdevs[i] = NULL; -		if (fimc) -			fimc_ctrls_delete(fimc->vid_cap.ctx); -		mutex_unlock(lock); -		return ret; +	} else if (ref_count > 0) { +		/* +		 * Link activation. Enable power of pipeline elements only if +		 * the pipeline is already in use, i.e. its video node is open. +		 * Recreate the controls destroyed during the link deactivation. +		 */ +		ret = __fimc_pipeline_open(pipeline, +					   source->entity, true); +		if (!ret && fimc) +			ret = fimc_capture_ctrls_create(fimc);  	} -	/* -	 * Link activation. Enable power of pipeline elements only if the -	 * pipeline is already in use, i.e. its video node is opened. -	 * Recreate the controls destroyed during the link deactivation. -	 */ -	mutex_lock(lock); - -	ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; -	if (ref_count > 0) -		ret = __fimc_pipeline_open(pipeline, source->entity, true); -	if (!ret && fimc) -		ret = fimc_capture_ctrls_create(fimc);  	mutex_unlock(lock);  	return ret ? -EPIPE : ret; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e84703c314c..1cb6d57987c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -276,7 +276,7 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)  	unsigned int frame_type;  	dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); -	frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); +	frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx);  	/* If frame is same as previous then skip and do not dequeue */  	if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 2356fd52a16..4f6b553c4b2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -232,6 +232,7 @@ static struct mfc_control controls[] = {  		.minimum = 0,  		.maximum = 1,  		.default_value = 0, +		.step = 1,  		.menu_skip_mask = 0,  	},  	{ diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 19f3563c61d..5a79c333d45 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -291,7 +291,7 @@ config IR_TTUSBIR  config IR_RX51  	tristate "Nokia N900 IR transmitter diode" -	depends on OMAP_DM_TIMER && LIRC && !ARCH_MULTIPLATFORM +	depends on OMAP_DM_TIMER && ARCH_OMAP2PLUS && LIRC && !ARCH_MULTIPLATFORM  	---help---  	   Say Y or M here if you want to enable support for the IR  	   transmitter diode built in the Nokia N900 (RX51) device. diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index a9d355230e8..768aaf62d5d 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -10,7 +10,7 @@ ifeq ($(CONFIG_COMPAT),y)    videodev-objs += v4l2-compat-ioctl32.o  endif -obj-$(CONFIG_VIDEO_DEV) += videodev.o +obj-$(CONFIG_VIDEO_V4L2) += videodev.o  obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o  obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 671f5b171c7..c346941a251 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -858,6 +858,7 @@ config EZX_PCAP  config AB8500_CORE  	bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"  	depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU +	select POWER_SUPPLY  	select MFD_CORE  	select IRQ_DOMAIN  	help diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index b1f3561b023..5f341a50ee5 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -594,9 +594,12 @@ static int ab8500_gpadc_runtime_suspend(struct device *dev)  static int ab8500_gpadc_runtime_resume(struct device *dev)  {  	struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); +	int ret; -	regulator_enable(gpadc->regu); -	return 0; +	ret = regulator_enable(gpadc->regu); +	if (ret) +		dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret); +	return ret;  }  static int ab8500_gpadc_runtime_idle(struct device *dev) @@ -643,7 +646,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)  	}  	/* VTVout LDO used to power up ab8500-GPADC */ -	gpadc->regu = regulator_get(&pdev->dev, "vddadc"); +	gpadc->regu = devm_regulator_get(&pdev->dev, "vddadc");  	if (IS_ERR(gpadc->regu)) {  		ret = PTR_ERR(gpadc->regu);  		dev_err(gpadc->dev, "failed to get vtvout LDO\n"); @@ -652,7 +655,11 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, gpadc); -	regulator_enable(gpadc->regu); +	ret = regulator_enable(gpadc->regu); +	if (ret) { +		dev_err(gpadc->dev, "Failed to enable vtvout LDO: %d\n", ret); +		goto fail_enable; +	}  	pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);  	pm_runtime_use_autosuspend(gpadc->dev); @@ -663,6 +670,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)  	list_add_tail(&gpadc->node, &ab8500_gpadc_list);  	dev_dbg(gpadc->dev, "probe success\n");  	return 0; + +fail_enable:  fail_irq:  	free_irq(gpadc->irq, gpadc);  fail: diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 6b5edf64de2..4febc5c7fde 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -460,15 +460,15 @@ static void omap_usbhs_init(struct device *dev)  	switch (omap->usbhs_rev) {  	case OMAP_USBHS_REV1: -		omap_usbhs_rev1_hostconfig(omap, reg); +		reg = omap_usbhs_rev1_hostconfig(omap, reg);  		break;  	case OMAP_USBHS_REV2: -		omap_usbhs_rev2_hostconfig(omap, reg); +		reg = omap_usbhs_rev2_hostconfig(omap, reg);  		break;  	default:	/* newer revisions */ -		omap_usbhs_rev2_hostconfig(omap, reg); +		reg = omap_usbhs_rev2_hostconfig(omap, reg);  		break;  	} diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index bbdbc50a3cc..73bf76df104 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -257,9 +257,24 @@ static struct regmap_irq_chip palmas_irq_chip = {  			PALMAS_INT1_MASK),  }; -static void palmas_dt_to_pdata(struct device_node *node, +static int palmas_set_pdata_irq_flag(struct i2c_client *i2c,  		struct palmas_platform_data *pdata)  { +	struct irq_data *irq_data = irq_get_irq_data(i2c->irq); +	if (!irq_data) { +		dev_err(&i2c->dev, "Invalid IRQ: %d\n", i2c->irq); +		return -EINVAL; +	} + +	pdata->irq_flags = irqd_get_trigger_type(irq_data); +	dev_info(&i2c->dev, "Irq flag is 0x%08x\n", pdata->irq_flags); +	return 0; +} + +static void palmas_dt_to_pdata(struct i2c_client *i2c, +		struct palmas_platform_data *pdata) +{ +	struct device_node *node = i2c->dev.of_node;  	int ret;  	u32 prop; @@ -283,6 +298,8 @@ static void palmas_dt_to_pdata(struct device_node *node,  		pdata->power_ctrl = PALMAS_POWER_CTRL_NSLEEP_MASK |  					PALMAS_POWER_CTRL_ENABLE1_MASK |  					PALMAS_POWER_CTRL_ENABLE2_MASK; +	if (i2c->irq) +		palmas_set_pdata_irq_flag(i2c, pdata);  }  static int palmas_i2c_probe(struct i2c_client *i2c, @@ -304,7 +321,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  		if (!pdata)  			return -ENOMEM; -		palmas_dt_to_pdata(node, pdata); +		palmas_dt_to_pdata(i2c, pdata);  	}  	if (!pdata) @@ -344,6 +361,19 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  		}  	} +	/* Change interrupt line output polarity */ +	if (pdata->irq_flags & IRQ_TYPE_LEVEL_HIGH) +		reg = PALMAS_POLARITY_CTRL_INT_POLARITY; +	else +		reg = 0; +	ret = palmas_update_bits(palmas, PALMAS_PU_PD_OD_BASE, +			PALMAS_POLARITY_CTRL, PALMAS_POLARITY_CTRL_INT_POLARITY, +			reg); +	if (ret < 0) { +		dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret); +		goto err; +	} +  	/* Change IRQ into clear on read mode for efficiency */  	slave = PALMAS_BASE_TO_SLAVE(PALMAS_INTERRUPT_BASE);  	addr = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE, PALMAS_INT_CTRL); @@ -352,7 +382,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c,  	regmap_write(palmas->regmap[slave], addr, reg);  	ret = regmap_add_irq_chip(palmas->regmap[slave], palmas->irq, -			IRQF_ONESHOT | IRQF_TRIGGER_LOW, 0, &palmas_irq_chip, +			IRQF_ONESHOT | pdata->irq_flags, 0, &palmas_irq_chip,  			&palmas->irq_data);  	if (ret < 0)  		goto err; diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 4658b5bdcd8..aeb8e40ab42 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -169,6 +169,7 @@ err:  void tps65912_device_exit(struct tps65912 *tps65912)  {  	mfd_remove_devices(tps65912->dev); +	tps65912_irq_exit(tps65912);  	kfree(tps65912);  } diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index e16edca9267..d2ab222138c 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c @@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);   * Disable the resource.   * The function returns with error or the content of the register   */ -int twl4030_audio_disable_resource(unsigned id) +int twl4030_audio_disable_resource(enum twl4030_audio_res id)  {  	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);  	int val; diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 88ff9dc8330..942b666a2a0 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -800,7 +800,7 @@ static int twl4030_madc_remove(struct platform_device *pdev)  static struct platform_driver twl4030_madc_driver = {  	.probe = twl4030_madc_probe, -	.remove = __exit_p(twl4030_madc_remove), +	.remove = twl4030_madc_remove,  	.driver = {  		   .name = "twl4030_madc",  		   .owner = THIS_MODULE, diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 45ea7185c00..642c6223fa6 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -152,6 +152,20 @@ static void mei_me_intr_disable(struct mei_device *dev)  }  /** + * mei_me_hw_reset_release - release device from the reset + * + * @dev: the device structure + */ +static void mei_me_hw_reset_release(struct mei_device *dev) +{ +	struct mei_me_hw *hw = to_me_hw(dev); +	u32 hcsr = mei_hcsr_read(hw); + +	hcsr |= H_IG; +	hcsr &= ~H_RST; +	mei_hcsr_set(hw, hcsr); +} +/**   * mei_me_hw_reset - resets fw via mei csr register.   *   * @dev: the device structure @@ -169,18 +183,14 @@ static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable)  	if (intr_enable)  		hcsr |= H_IE;  	else -		hcsr &= ~H_IE; - -	mei_hcsr_set(hw, hcsr); - -	hcsr = mei_hcsr_read(hw) | H_IG; -	hcsr &= ~H_RST; +		hcsr |= ~H_IE;  	mei_hcsr_set(hw, hcsr); -	hcsr = mei_hcsr_read(hw); +	if (dev->dev_state == MEI_DEV_POWER_DOWN) +		mei_me_hw_reset_release(dev); -	dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", hcsr); +	dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", mei_hcsr_read(hw));  }  /** @@ -466,7 +476,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)  			mutex_unlock(&dev->device_lock);  			return IRQ_HANDLED;  		} else { -			dev_dbg(&dev->pdev->dev, "FW not ready.\n"); +			dev_dbg(&dev->pdev->dev, "Reset Completed.\n"); +			mei_me_hw_reset_release(dev);  			mutex_unlock(&dev->device_lock);  			return IRQ_HANDLED;  		} diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6ec530168af..356179991a2 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -183,6 +183,24 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)  	mei_cl_all_write_clear(dev);  } +void mei_stop(struct mei_device *dev) +{ +	dev_dbg(&dev->pdev->dev, "stopping the device.\n"); + +	mutex_lock(&dev->device_lock); + +	cancel_delayed_work(&dev->timer_work); + +	mei_wd_stop(dev); + +	dev->dev_state = MEI_DEV_POWER_DOWN; +	mei_reset(dev, 0); + +	mutex_unlock(&dev->device_lock); + +	flush_scheduled_work(); +} + diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index cb80166161f..97873812e33 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -381,6 +381,7 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec)  void mei_device_init(struct mei_device *dev);  void mei_reset(struct mei_device *dev, int interrupts);  int mei_hw_init(struct mei_device *dev); +void mei_stop(struct mei_device *dev);  /*   *  MEI interrupt functions prototype diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b40ec0601ab..b8b5c9c3ad0 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -247,44 +247,14 @@ static void mei_remove(struct pci_dev *pdev)  	hw = to_me_hw(dev); -	mutex_lock(&dev->device_lock); - -	cancel_delayed_work(&dev->timer_work); -	mei_wd_stop(dev); +	dev_err(&pdev->dev, "stop\n"); +	mei_stop(dev);  	mei_pdev = NULL; -	if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) { -		dev->iamthif_cl.state = MEI_FILE_DISCONNECTING; -		mei_cl_disconnect(&dev->iamthif_cl); -	} -	if (dev->wd_cl.state == MEI_FILE_CONNECTED) { -		dev->wd_cl.state = MEI_FILE_DISCONNECTING; -		mei_cl_disconnect(&dev->wd_cl); -	} - -	/* Unregistering watchdog device */  	mei_watchdog_unregister(dev); -	/* remove entry if already in list */ -	dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); - -	if (dev->open_handle_count > 0) -		dev->open_handle_count--; -	mei_cl_unlink(&dev->wd_cl); - -	if (dev->open_handle_count > 0) -		dev->open_handle_count--; -	mei_cl_unlink(&dev->iamthif_cl); - -	dev->iamthif_current_cb = NULL; -	dev->me_clients_num = 0; - -	mutex_unlock(&dev->device_lock); - -	flush_scheduled_work(); -  	/* disable interrupts */  	mei_disable_interrupts(dev); @@ -308,28 +278,20 @@ static int mei_pci_suspend(struct device *device)  {  	struct pci_dev *pdev = to_pci_dev(device);  	struct mei_device *dev = pci_get_drvdata(pdev); -	int err;  	if (!dev)  		return -ENODEV; -	mutex_lock(&dev->device_lock); -	cancel_delayed_work(&dev->timer_work); +	dev_err(&pdev->dev, "suspend\n"); -	/* Stop watchdog if exists */ -	err = mei_wd_stop(dev); -	/* Set new mei state */ -	if (dev->dev_state == MEI_DEV_ENABLED || -	    dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { -		dev->dev_state = MEI_DEV_POWER_DOWN; -		mei_reset(dev, 0); -	} -	mutex_unlock(&dev->device_lock); +	mei_stop(dev); + +	mei_disable_interrupts(dev);  	free_irq(pdev->irq, dev);  	pci_disable_msi(pdev); -	return err; +	return 0;  }  static int mei_pci_resume(struct device *device) diff --git a/drivers/misc/vmw_vmci/vmci_datagram.c b/drivers/misc/vmw_vmci/vmci_datagram.c index ed5c433cd49..f3cdd904fe4 100644 --- a/drivers/misc/vmw_vmci/vmci_datagram.c +++ b/drivers/misc/vmw_vmci/vmci_datagram.c @@ -42,9 +42,11 @@ struct datagram_entry {  struct delayed_datagram_info {  	struct datagram_entry *entry; -	struct vmci_datagram msg;  	struct work_struct work;  	bool in_dg_host_queue; +	/* msg and msg_payload must be together. */ +	struct vmci_datagram msg; +	u8 msg_payload[];  };  /* Number of in-flight host->host datagrams */ diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 63fb265e0da..8d6794cdf89 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -25,14 +25,93 @@  #include <mach/dma.h> -#include <mach/regs-sdi.h> -  #include <linux/platform_data/mmc-s3cmci.h>  #include "s3cmci.h"  #define DRIVER_NAME "s3c-mci" +#define S3C2410_SDICON			(0x00) +#define S3C2410_SDIPRE			(0x04) +#define S3C2410_SDICMDARG		(0x08) +#define S3C2410_SDICMDCON		(0x0C) +#define S3C2410_SDICMDSTAT		(0x10) +#define S3C2410_SDIRSP0			(0x14) +#define S3C2410_SDIRSP1			(0x18) +#define S3C2410_SDIRSP2			(0x1C) +#define S3C2410_SDIRSP3			(0x20) +#define S3C2410_SDITIMER		(0x24) +#define S3C2410_SDIBSIZE		(0x28) +#define S3C2410_SDIDCON			(0x2C) +#define S3C2410_SDIDCNT			(0x30) +#define S3C2410_SDIDSTA			(0x34) +#define S3C2410_SDIFSTA			(0x38) + +#define S3C2410_SDIDATA			(0x3C) +#define S3C2410_SDIIMSK			(0x40) + +#define S3C2440_SDIDATA			(0x40) +#define S3C2440_SDIIMSK			(0x3C) + +#define S3C2440_SDICON_SDRESET		(1 << 8) +#define S3C2410_SDICON_SDIOIRQ		(1 << 3) +#define S3C2410_SDICON_FIFORESET	(1 << 1) +#define S3C2410_SDICON_CLOCKTYPE	(1 << 0) + +#define S3C2410_SDICMDCON_LONGRSP	(1 << 10) +#define S3C2410_SDICMDCON_WAITRSP	(1 << 9) +#define S3C2410_SDICMDCON_CMDSTART	(1 << 8) +#define S3C2410_SDICMDCON_SENDERHOST	(1 << 6) +#define S3C2410_SDICMDCON_INDEX		(0x3f) + +#define S3C2410_SDICMDSTAT_CRCFAIL	(1 << 12) +#define S3C2410_SDICMDSTAT_CMDSENT	(1 << 11) +#define S3C2410_SDICMDSTAT_CMDTIMEOUT	(1 << 10) +#define S3C2410_SDICMDSTAT_RSPFIN	(1 << 9) + +#define S3C2440_SDIDCON_DS_WORD		(2 << 22) +#define S3C2410_SDIDCON_TXAFTERRESP	(1 << 20) +#define S3C2410_SDIDCON_RXAFTERCMD	(1 << 19) +#define S3C2410_SDIDCON_BLOCKMODE	(1 << 17) +#define S3C2410_SDIDCON_WIDEBUS		(1 << 16) +#define S3C2410_SDIDCON_DMAEN		(1 << 15) +#define S3C2410_SDIDCON_STOP		(1 << 14) +#define S3C2440_SDIDCON_DATSTART	(1 << 14) + +#define S3C2410_SDIDCON_XFER_RXSTART	(2 << 12) +#define S3C2410_SDIDCON_XFER_TXSTART	(3 << 12) + +#define S3C2410_SDIDCON_BLKNUM_MASK	(0xFFF) + +#define S3C2410_SDIDSTA_SDIOIRQDETECT	(1 << 9) +#define S3C2410_SDIDSTA_FIFOFAIL	(1 << 8) +#define S3C2410_SDIDSTA_CRCFAIL		(1 << 7) +#define S3C2410_SDIDSTA_RXCRCFAIL	(1 << 6) +#define S3C2410_SDIDSTA_DATATIMEOUT	(1 << 5) +#define S3C2410_SDIDSTA_XFERFINISH	(1 << 4) +#define S3C2410_SDIDSTA_TXDATAON	(1 << 1) +#define S3C2410_SDIDSTA_RXDATAON	(1 << 0) + +#define S3C2440_SDIFSTA_FIFORESET	(1 << 16) +#define S3C2440_SDIFSTA_FIFOFAIL	(3 << 14) +#define S3C2410_SDIFSTA_TFDET		(1 << 13) +#define S3C2410_SDIFSTA_RFDET		(1 << 12) +#define S3C2410_SDIFSTA_COUNTMASK	(0x7f) + +#define S3C2410_SDIIMSK_RESPONSECRC	(1 << 17) +#define S3C2410_SDIIMSK_CMDSENT		(1 << 16) +#define S3C2410_SDIIMSK_CMDTIMEOUT	(1 << 15) +#define S3C2410_SDIIMSK_RESPONSEND	(1 << 14) +#define S3C2410_SDIIMSK_SDIOIRQ		(1 << 12) +#define S3C2410_SDIIMSK_FIFOFAIL	(1 << 11) +#define S3C2410_SDIIMSK_CRCSTATUS	(1 << 10) +#define S3C2410_SDIIMSK_DATACRC		(1 << 9) +#define S3C2410_SDIIMSK_DATATIMEOUT	(1 << 8) +#define S3C2410_SDIIMSK_DATAFINISH	(1 << 7) +#define S3C2410_SDIIMSK_TXFIFOHALF	(1 << 4) +#define S3C2410_SDIIMSK_RXFIFOLAST	(1 << 2) +#define S3C2410_SDIIMSK_RXFIFOHALF	(1 << 0) +  enum dbg_channels {  	dbg_err   = (1 << 0),  	dbg_debug = (1 << 1), diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 63feb75cc8e..9279a9174f8 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -19,6 +19,12 @@  /* 10 parts were found on sflash on Netgear WNDR4500 */  #define BCM47XXPART_MAX_PARTS		12 +/* + * Amount of bytes we read when analyzing each block of flash memory. + * Set it big enough to allow detecting partition and reading important data. + */ +#define BCM47XXPART_BYTES_TO_READ	0x404 +  /* Magics */  #define BOARD_DATA_MAGIC		0x5246504D	/* MPFR */  #define POT_MAGIC1			0x54544f50	/* POTT */ @@ -57,17 +63,15 @@ static int bcm47xxpart_parse(struct mtd_info *master,  	struct trx_header *trx;  	int trx_part = -1;  	int last_trx_part = -1; -	int max_bytes_to_read = 0x8004; +	int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };  	if (blocksize <= 0x10000)  		blocksize = 0x10000; -	if (blocksize == 0x20000) -		max_bytes_to_read = 0x18004;  	/* Alloc */  	parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,  			GFP_KERNEL); -	buf = kzalloc(max_bytes_to_read, GFP_KERNEL); +	buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL);  	/* Parse block by block looking for magics */  	for (offset = 0; offset <= master->size - blocksize; @@ -82,7 +86,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,  		}  		/* Read beginning of the block */ -		if (mtd_read(master, offset, max_bytes_to_read, +		if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,  			     &bytes_read, (uint8_t *)buf) < 0) {  			pr_err("mtd_read error while parsing (offset: 0x%X)!\n",  			       offset); @@ -96,20 +100,6 @@ static int bcm47xxpart_parse(struct mtd_info *master,  			continue;  		} -		/* Standard NVRAM */ -		if (buf[0x000 / 4] == NVRAM_HEADER || -		    buf[0x1000 / 4] == NVRAM_HEADER || -		    buf[0x8000 / 4] == NVRAM_HEADER || -		    (blocksize == 0x20000 && ( -		      buf[0x10000 / 4] == NVRAM_HEADER || -		      buf[0x11000 / 4] == NVRAM_HEADER || -		      buf[0x18000 / 4] == NVRAM_HEADER))) { -			bcm47xxpart_add_part(&parts[curr_part++], "nvram", -					     offset, 0); -			offset = rounddown(offset, blocksize); -			continue; -		} -  		/*  		 * board_data starts with board_id which differs across boards,  		 * but we can use 'MPFR' (hopefully) magic at 0x100 @@ -178,6 +168,30 @@ static int bcm47xxpart_parse(struct mtd_info *master,  			continue;  		}  	} + +	/* Look for NVRAM at the end of the last block. */ +	for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) { +		if (curr_part > BCM47XXPART_MAX_PARTS) { +			pr_warn("Reached maximum number of partitions, scanning stopped!\n"); +			break; +		} + +		offset = master->size - possible_nvram_sizes[i]; +		if (mtd_read(master, offset, 0x4, &bytes_read, +			     (uint8_t *)buf) < 0) { +			pr_err("mtd_read error while reading at offset 0x%X!\n", +			       offset); +			continue; +		} + +		/* Standard NVRAM */ +		if (buf[0] == NVRAM_HEADER) { +			bcm47xxpart_add_part(&parts[curr_part++], "nvram", +					     master->size - blocksize, 0); +			break; +		} +	} +  	kfree(buf);  	/* diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 43214151b88..42c63927609 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1523,6 +1523,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  					oobreadlen -= toread;  				}  			} + +			if (chip->options & NAND_NEED_READRDY) { +				/* Apply delay or wait for ready/busy pin */ +				if (!chip->dev_ready) +					udelay(chip->chip_delay); +				else +					nand_wait_ready(mtd); +			}  		} else {  			memcpy(buf, chip->buffers->databuf + col, bytes);  			buf += bytes; @@ -1787,6 +1795,14 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,  		len = min(len, readlen);  		buf = nand_transfer_oob(chip, buf, ops, len); +		if (chip->options & NAND_NEED_READRDY) { +			/* Apply delay or wait for ready/busy pin */ +			if (!chip->dev_ready) +				udelay(chip->chip_delay); +			else +				nand_wait_ready(mtd); +		} +  		readlen -= len;  		if (!readlen)  			break; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index e3aa2748a6e..9c612388e5d 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -22,49 +22,51 @@  *	512	512 Byte page size  */  struct nand_flash_dev nand_flash_ids[] = { +#define SP_OPTIONS NAND_NEED_READRDY +#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)  #ifdef CONFIG_MTD_NAND_MUSEUM_IDS -	{"NAND 1MiB 5V 8-bit",		0x6e, 256, 1, 0x1000, 0}, -	{"NAND 2MiB 5V 8-bit",		0x64, 256, 2, 0x1000, 0}, -	{"NAND 4MiB 5V 8-bit",		0x6b, 512, 4, 0x2000, 0}, -	{"NAND 1MiB 3,3V 8-bit",	0xe8, 256, 1, 0x1000, 0}, -	{"NAND 1MiB 3,3V 8-bit",	0xec, 256, 1, 0x1000, 0}, -	{"NAND 2MiB 3,3V 8-bit",	0xea, 256, 2, 0x1000, 0}, -	{"NAND 4MiB 3,3V 8-bit",	0xd5, 512, 4, 0x2000, 0}, -	{"NAND 4MiB 3,3V 8-bit",	0xe3, 512, 4, 0x2000, 0}, -	{"NAND 4MiB 3,3V 8-bit",	0xe5, 512, 4, 0x2000, 0}, -	{"NAND 8MiB 3,3V 8-bit",	0xd6, 512, 8, 0x2000, 0}, +	{"NAND 1MiB 5V 8-bit",		0x6e, 256, 1, 0x1000, SP_OPTIONS}, +	{"NAND 2MiB 5V 8-bit",		0x64, 256, 2, 0x1000, SP_OPTIONS}, +	{"NAND 4MiB 5V 8-bit",		0x6b, 512, 4, 0x2000, SP_OPTIONS}, +	{"NAND 1MiB 3,3V 8-bit",	0xe8, 256, 1, 0x1000, SP_OPTIONS}, +	{"NAND 1MiB 3,3V 8-bit",	0xec, 256, 1, 0x1000, SP_OPTIONS}, +	{"NAND 2MiB 3,3V 8-bit",	0xea, 256, 2, 0x1000, SP_OPTIONS}, +	{"NAND 4MiB 3,3V 8-bit",	0xd5, 512, 4, 0x2000, SP_OPTIONS}, +	{"NAND 4MiB 3,3V 8-bit",	0xe3, 512, 4, 0x2000, SP_OPTIONS}, +	{"NAND 4MiB 3,3V 8-bit",	0xe5, 512, 4, 0x2000, SP_OPTIONS}, +	{"NAND 8MiB 3,3V 8-bit",	0xd6, 512, 8, 0x2000, SP_OPTIONS}, -	{"NAND 8MiB 1,8V 8-bit",	0x39, 512, 8, 0x2000, 0}, -	{"NAND 8MiB 3,3V 8-bit",	0xe6, 512, 8, 0x2000, 0}, -	{"NAND 8MiB 1,8V 16-bit",	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, -	{"NAND 8MiB 3,3V 16-bit",	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +	{"NAND 8MiB 1,8V 8-bit",	0x39, 512, 8, 0x2000, SP_OPTIONS}, +	{"NAND 8MiB 3,3V 8-bit",	0xe6, 512, 8, 0x2000, SP_OPTIONS}, +	{"NAND 8MiB 1,8V 16-bit",	0x49, 512, 8, 0x2000, SP_OPTIONS16}, +	{"NAND 8MiB 3,3V 16-bit",	0x59, 512, 8, 0x2000, SP_OPTIONS16},  #endif -	{"NAND 16MiB 1,8V 8-bit",	0x33, 512, 16, 0x4000, 0}, -	{"NAND 16MiB 3,3V 8-bit",	0x73, 512, 16, 0x4000, 0}, -	{"NAND 16MiB 1,8V 16-bit",	0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, -	{"NAND 16MiB 3,3V 16-bit",	0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, +	{"NAND 16MiB 1,8V 8-bit",	0x33, 512, 16, 0x4000, SP_OPTIONS}, +	{"NAND 16MiB 3,3V 8-bit",	0x73, 512, 16, 0x4000, SP_OPTIONS}, +	{"NAND 16MiB 1,8V 16-bit",	0x43, 512, 16, 0x4000, SP_OPTIONS16}, +	{"NAND 16MiB 3,3V 16-bit",	0x53, 512, 16, 0x4000, SP_OPTIONS16}, -	{"NAND 32MiB 1,8V 8-bit",	0x35, 512, 32, 0x4000, 0}, -	{"NAND 32MiB 3,3V 8-bit",	0x75, 512, 32, 0x4000, 0}, -	{"NAND 32MiB 1,8V 16-bit",	0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, -	{"NAND 32MiB 3,3V 16-bit",	0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, +	{"NAND 32MiB 1,8V 8-bit",	0x35, 512, 32, 0x4000, SP_OPTIONS}, +	{"NAND 32MiB 3,3V 8-bit",	0x75, 512, 32, 0x4000, SP_OPTIONS}, +	{"NAND 32MiB 1,8V 16-bit",	0x45, 512, 32, 0x4000, SP_OPTIONS16}, +	{"NAND 32MiB 3,3V 16-bit",	0x55, 512, 32, 0x4000, SP_OPTIONS16}, -	{"NAND 64MiB 1,8V 8-bit",	0x36, 512, 64, 0x4000, 0}, -	{"NAND 64MiB 3,3V 8-bit",	0x76, 512, 64, 0x4000, 0}, -	{"NAND 64MiB 1,8V 16-bit",	0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, -	{"NAND 64MiB 3,3V 16-bit",	0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, +	{"NAND 64MiB 1,8V 8-bit",	0x36, 512, 64, 0x4000, SP_OPTIONS}, +	{"NAND 64MiB 3,3V 8-bit",	0x76, 512, 64, 0x4000, SP_OPTIONS}, +	{"NAND 64MiB 1,8V 16-bit",	0x46, 512, 64, 0x4000, SP_OPTIONS16}, +	{"NAND 64MiB 3,3V 16-bit",	0x56, 512, 64, 0x4000, SP_OPTIONS16}, -	{"NAND 128MiB 1,8V 8-bit",	0x78, 512, 128, 0x4000, 0}, -	{"NAND 128MiB 1,8V 8-bit",	0x39, 512, 128, 0x4000, 0}, -	{"NAND 128MiB 3,3V 8-bit",	0x79, 512, 128, 0x4000, 0}, -	{"NAND 128MiB 1,8V 16-bit",	0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, -	{"NAND 128MiB 1,8V 16-bit",	0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, -	{"NAND 128MiB 3,3V 16-bit",	0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, -	{"NAND 128MiB 3,3V 16-bit",	0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, +	{"NAND 128MiB 1,8V 8-bit",	0x78, 512, 128, 0x4000, SP_OPTIONS}, +	{"NAND 128MiB 1,8V 8-bit",	0x39, 512, 128, 0x4000, SP_OPTIONS}, +	{"NAND 128MiB 3,3V 8-bit",	0x79, 512, 128, 0x4000, SP_OPTIONS}, +	{"NAND 128MiB 1,8V 16-bit",	0x72, 512, 128, 0x4000, SP_OPTIONS16}, +	{"NAND 128MiB 1,8V 16-bit",	0x49, 512, 128, 0x4000, SP_OPTIONS16}, +	{"NAND 128MiB 3,3V 16-bit",	0x74, 512, 128, 0x4000, SP_OPTIONS16}, +	{"NAND 128MiB 3,3V 16-bit",	0x59, 512, 128, 0x4000, SP_OPTIONS16}, -	{"NAND 256MiB 3,3V 8-bit",	0x71, 512, 256, 0x4000, 0}, +	{"NAND 256MiB 3,3V 8-bit",	0x71, 512, 256, 0x4000, SP_OPTIONS},  	/*  	 * These are the new chips with large page size. The pagesize and the diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 7bd068a6056..6bbd90e1123 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1746,6 +1746,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)  	bond_compute_features(bond); +	bond_update_speed_duplex(new_slave); +  	read_lock(&bond->lock);  	new_slave->last_arp_rx = jiffies - @@ -1798,8 +1800,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)  		new_slave->link == BOND_LINK_DOWN ? "DOWN" :  			(new_slave->link == BOND_LINK_UP ? "UP" : "BACK")); -	bond_update_speed_duplex(new_slave); -  	if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {  		/* if there is a primary slave, remember it */  		if (strcmp(bond->params.primary, new_slave->dev->name) == 0) { @@ -1964,7 +1964,6 @@ static int __bond_release_one(struct net_device *bond_dev,  	}  	block_netpoll_tx(); -	call_netdevice_notifiers(NETDEV_RELEASE, bond_dev);  	write_lock_bh(&bond->lock);  	slave = bond_get_slave_by_dev(bond, slave_dev); @@ -2066,8 +2065,10 @@ static int __bond_release_one(struct net_device *bond_dev,  	write_unlock_bh(&bond->lock);  	unblock_netpoll_tx(); -	if (bond->slave_cnt == 0) +	if (bond->slave_cnt == 0) {  		call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); +		call_netdevice_notifiers(NETDEV_RELEASE, bond->dev); +	}  	bond_compute_features(bond);  	if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) && @@ -2373,8 +2374,6 @@ static void bond_miimon_commit(struct bonding *bond)  				bond_set_backup_slave(slave);  			} -			bond_update_speed_duplex(slave); -  			pr_info("%s: link status definitely up for interface %s, %u Mbps %s duplex.\n",  				bond->dev->name, slave->dev->name,  				slave->speed, slave->duplex ? "full" : "half"); diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 1c9e09fbdff..db103e03ba0 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -183,6 +183,11 @@ int bond_create_slave_symlinks(struct net_device *master,  	sprintf(linkname, "slave_%s", slave->name);  	ret = sysfs_create_link(&(master->dev.kobj), &(slave->dev.kobj),  				linkname); + +	/* free the master link created earlier in case of error */ +	if (ret) +		sysfs_remove_link(&(slave->dev.kobj), "master"); +  	return ret;  } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index a923bc4d5a1..4046f97378c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2760,6 +2760,7 @@ load_error2:  	bp->port.pmf = 0;  load_error1:  	bnx2x_napi_disable(bp); +	bnx2x_del_all_napi(bp);  	/* clear pf_load status, as it was already set */  	if (IS_PF(bp)) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 568205436a1..91ecd6a00d0 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -2139,12 +2139,12 @@ static u8 bnx2x_dcbnl_get_cap(struct net_device *netdev, int capid, u8 *cap)  			break;  		default:  			BNX2X_ERR("Non valid capability ID\n"); -			rval = -EINVAL; +			rval = 1;  			break;  		}  	} else {  		DP(BNX2X_MSG_DCB, "DCB disabled\n"); -		rval = -EINVAL; +		rval = 1;  	}  	DP(BNX2X_MSG_DCB, "capid %d:%x\n", capid, *cap); @@ -2170,12 +2170,12 @@ static int bnx2x_dcbnl_get_numtcs(struct net_device *netdev, int tcid, u8 *num)  			break;  		default:  			BNX2X_ERR("Non valid TC-ID\n"); -			rval = -EINVAL; +			rval = 1;  			break;  		}  	} else {  		DP(BNX2X_MSG_DCB, "DCB disabled\n"); -		rval = -EINVAL; +		rval = 1;  	}  	return rval; @@ -2188,7 +2188,7 @@ static int bnx2x_dcbnl_set_numtcs(struct net_device *netdev, int tcid, u8 num)  	return -EINVAL;  } -static u8  bnx2x_dcbnl_get_pfc_state(struct net_device *netdev) +static u8 bnx2x_dcbnl_get_pfc_state(struct net_device *netdev)  {  	struct bnx2x *bp = netdev_priv(netdev);  	DP(BNX2X_MSG_DCB, "state = %d\n", bp->dcbx_local_feat.pfc.enabled); @@ -2390,12 +2390,12 @@ static u8 bnx2x_dcbnl_get_featcfg(struct net_device *netdev, int featid,  			break;  		default:  			BNX2X_ERR("Non valid featrue-ID\n"); -			rval = -EINVAL; +			rval = 1;  			break;  		}  	} else {  		DP(BNX2X_MSG_DCB, "DCB disabled\n"); -		rval = -EINVAL; +		rval = 1;  	}  	return rval; @@ -2431,12 +2431,12 @@ static u8 bnx2x_dcbnl_set_featcfg(struct net_device *netdev, int featid,  			break;  		default:  			BNX2X_ERR("Non valid featrue-ID\n"); -			rval = -EINVAL; +			rval = 1;  			break;  		}  	} else {  		DP(BNX2X_MSG_DCB, "dcbnl call not valid\n"); -		rval = -EINVAL; +		rval = 1;  	}  	return rval; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 31c5787970d..77ebae0ac64 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -8647,7 +8647,9 @@ void bnx2x_handle_module_detect_int(struct link_params *params)  						MDIO_WC_DEVAD,  						MDIO_WC_REG_DIGITAL5_MISC6,  						&rx_tx_in_reset); -				if (!rx_tx_in_reset) { +				if ((!rx_tx_in_reset) && +				    (params->link_flags & +				     PHY_INITIALIZED)) {  					bnx2x_warpcore_reset_lane(bp, phy, 1);  					bnx2x_warpcore_config_sfi(phy, params);  					bnx2x_warpcore_reset_lane(bp, phy, 0); @@ -12527,6 +12529,8 @@ int bnx2x_phy_init(struct link_params *params, struct link_vars *vars)  	vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;  	vars->mac_type = MAC_TYPE_NONE;  	vars->phy_flags = 0; +	vars->check_kr2_recovery_cnt = 0; +	params->link_flags = PHY_INITIALIZED;  	/* Driver opens NIG-BRB filters */  	bnx2x_set_rx_filter(params, 1);  	/* Check if link flap can be avoided */ @@ -12691,6 +12695,7 @@ int bnx2x_lfa_reset(struct link_params *params,  	struct bnx2x *bp = params->bp;  	vars->link_up = 0;  	vars->phy_flags = 0; +	params->link_flags &= ~PHY_INITIALIZED;  	if (!params->lfa_base)  		return bnx2x_link_reset(params, vars, 1);  	/* @@ -13411,6 +13416,7 @@ static void bnx2x_disable_kr2(struct link_params *params,  	vars->link_attr_sync &= ~LINK_ATTR_SYNC_KR2_ENABLE;  	bnx2x_update_link_attr(params, vars->link_attr_sync); +	vars->check_kr2_recovery_cnt = CHECK_KR2_RECOVERY_CNT;  	/* Restart AN on leading lane */  	bnx2x_warpcore_restart_AN_KR(phy, params);  } @@ -13439,6 +13445,15 @@ static void bnx2x_check_kr2_wa(struct link_params *params,  		return;  	} +	/* Once KR2 was disabled, wait 5 seconds before checking KR2 recovery +	 * since some switches tend to reinit the AN process and clear the +	 * advertised BP/NP after ~2 seconds causing the KR2 to be disabled +	 * and recovered many times +	 */ +	if (vars->check_kr2_recovery_cnt > 0) { +		vars->check_kr2_recovery_cnt--; +		return; +	}  	lane = bnx2x_get_warpcore_lane(phy, params);  	CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,  			  MDIO_AER_BLOCK_AER_REG, lane); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index be5c195d03d..56c2aae4e2c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -309,6 +309,7 @@ struct link_params {  				req_flow_ctrl is set to AUTO */  	u16 link_flags;  #define LINK_FLAGS_INT_DISABLED		(1<<0) +#define PHY_INITIALIZED		(1<<1)  	u32 lfa_base;  }; @@ -342,7 +343,8 @@ struct link_vars {  	u32 link_status;  	u32 eee_status;  	u8 fault_detected; -	u8 rsrv1; +	u8 check_kr2_recovery_cnt; +#define CHECK_KR2_RECOVERY_CNT	5  	u16 periodic_flags;  #define PERIODIC_FLAGS_LINK_EVENT	0x0001 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index 364e37ecbc5..198f6f1c9ad 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -459,8 +459,9 @@ struct bnx2x_fw_port_stats_old {  #define UPDATE_QSTAT(s, t) \  	do { \ -		qstats->t##_hi = qstats_old->t##_hi + le32_to_cpu(s.hi); \  		qstats->t##_lo = qstats_old->t##_lo + le32_to_cpu(s.lo); \ +		qstats->t##_hi = qstats_old->t##_hi + le32_to_cpu(s.hi) \ +			+ ((qstats->t##_lo < qstats_old->t##_lo) ? 1 : 0); \  	} while (0)  #define UPDATE_QSTAT_OLD(f) \ diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index fdb9b565541..67d2663b397 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -1869,6 +1869,8 @@ static void tg3_link_report(struct tg3 *tp)  		tg3_ump_link_report(tp);  	} + +	tp->link_up = netif_carrier_ok(tp->dev);  }  static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl) @@ -2522,12 +2524,6 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp)  	return err;  } -static void tg3_carrier_on(struct tg3 *tp) -{ -	netif_carrier_on(tp->dev); -	tp->link_up = true; -} -  static void tg3_carrier_off(struct tg3 *tp)  {  	netif_carrier_off(tp->dev); @@ -2553,7 +2549,7 @@ static int tg3_phy_reset(struct tg3 *tp)  		return -EBUSY;  	if (netif_running(tp->dev) && tp->link_up) { -		tg3_carrier_off(tp); +		netif_carrier_off(tp->dev);  		tg3_link_report(tp);  	} @@ -4134,6 +4130,14 @@ static void tg3_phy_copper_begin(struct tg3 *tp)  		tp->link_config.active_speed = tp->link_config.speed;  		tp->link_config.active_duplex = tp->link_config.duplex; +		if (tg3_asic_rev(tp) == ASIC_REV_5714) { +			/* With autoneg disabled, 5715 only links up when the +			 * advertisement register has the configured speed +			 * enabled. +			 */ +			tg3_writephy(tp, MII_ADVERTISE, ADVERTISE_ALL); +		} +  		bmcr = 0;  		switch (tp->link_config.speed) {  		default: @@ -4262,9 +4266,9 @@ static bool tg3_test_and_report_link_chg(struct tg3 *tp, int curr_link_up)  {  	if (curr_link_up != tp->link_up) {  		if (curr_link_up) { -			tg3_carrier_on(tp); +			netif_carrier_on(tp->dev);  		} else { -			tg3_carrier_off(tp); +			netif_carrier_off(tp->dev);  			if (tp->phy_flags & TG3_PHYFLG_MII_SERDES)  				tp->phy_flags &= ~TG3_PHYFLG_PARALLEL_DETECT;  		} diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 4ce62031f62..8049268ce0f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -497,8 +497,9 @@ int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len,  }  #define EEPROM_STAT_ADDR   0x7bfc -#define VPD_BASE           0  #define VPD_LEN            512 +#define VPD_BASE           0x400 +#define VPD_BASE_OLD       0  /**   *	t4_seeprom_wp - enable/disable EEPROM write protection @@ -524,7 +525,7 @@ int t4_seeprom_wp(struct adapter *adapter, bool enable)  int get_vpd_params(struct adapter *adapter, struct vpd_params *p)  {  	u32 cclk_param, cclk_val; -	int i, ret; +	int i, ret, addr;  	int ec, sn;  	u8 *vpd, csum;  	unsigned int vpdr_len, kw_offset, id_len; @@ -533,7 +534,12 @@ int get_vpd_params(struct adapter *adapter, struct vpd_params *p)  	if (!vpd)  		return -ENOMEM; -	ret = pci_read_vpd(adapter->pdev, VPD_BASE, VPD_LEN, vpd); +	ret = pci_read_vpd(adapter->pdev, VPD_BASE, sizeof(u32), vpd); +	if (ret < 0) +		goto out; +	addr = *vpd == 0x82 ? VPD_BASE : VPD_BASE_OLD; + +	ret = pci_read_vpd(adapter->pdev, addr, VPD_LEN, vpd);  	if (ret < 0)  		goto out; diff --git a/drivers/net/ethernet/dec/tulip/Kconfig b/drivers/net/ethernet/dec/tulip/Kconfig index 0c37fb2cc86..1df33c799c0 100644 --- a/drivers/net/ethernet/dec/tulip/Kconfig +++ b/drivers/net/ethernet/dec/tulip/Kconfig @@ -108,6 +108,7 @@ config TULIP_DM910X  config DE4X5  	tristate "Generic DECchip & DIGITAL EtherWORKS PCI/EISA"  	depends on (PCI || EISA) +	depends on VIRT_TO_BUS || ALPHA || PPC || SPARC  	select CRC32  	---help---  	  This is support for the DIGITAL series of PCI/EISA Ethernet cards. diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 28ceb841418..29aff55f2ee 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -349,6 +349,7 @@ struct be_adapter {  	struct pci_dev *pdev;  	struct net_device *netdev; +	u8 __iomem *csr;	/* CSR BAR used only for BE2/3 */  	u8 __iomem *db;		/* Door Bell */  	struct mutex mbox_lock; /* For serializing mbox cmds to BE card */ diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 071aea79d21..3c9b4f12e3e 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -473,19 +473,17 @@ static int be_mbox_notify_wait(struct be_adapter *adapter)  	return 0;  } -static int be_POST_stage_get(struct be_adapter *adapter, u16 *stage) +static u16 be_POST_stage_get(struct be_adapter *adapter)  {  	u32 sem; -	u32 reg = skyhawk_chip(adapter) ? SLIPORT_SEMAPHORE_OFFSET_SH : -					  SLIPORT_SEMAPHORE_OFFSET_BE; -	pci_read_config_dword(adapter->pdev, reg, &sem); -	*stage = sem & POST_STAGE_MASK; - -	if ((sem >> POST_ERR_SHIFT) & POST_ERR_MASK) -		return -1; +	if (BEx_chip(adapter)) +		sem  = ioread32(adapter->csr + SLIPORT_SEMAPHORE_OFFSET_BEx);  	else -		return 0; +		pci_read_config_dword(adapter->pdev, +				      SLIPORT_SEMAPHORE_OFFSET_SH, &sem); + +	return sem & POST_STAGE_MASK;  }  int lancer_wait_ready(struct be_adapter *adapter) @@ -579,19 +577,17 @@ int be_fw_wait_ready(struct be_adapter *adapter)  	}  	do { -		status = be_POST_stage_get(adapter, &stage); -		if (status) { -			dev_err(dev, "POST error; stage=0x%x\n", stage); -			return -1; -		} else if (stage != POST_STAGE_ARMFW_RDY) { -			if (msleep_interruptible(2000)) { -				dev_err(dev, "Waiting for POST aborted\n"); -				return -EINTR; -			} -			timeout += 2; -		} else { +		stage = be_POST_stage_get(adapter); +		if (stage == POST_STAGE_ARMFW_RDY)  			return 0; + +		dev_info(dev, "Waiting for POST, %ds elapsed\n", +			 timeout); +		if (msleep_interruptible(2000)) { +			dev_err(dev, "Waiting for POST aborted\n"); +			return -EINTR;  		} +		timeout += 2;  	} while (timeout < 60);  	dev_err(dev, "POST timeout; stage=0x%x\n", stage); diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index 541d4530d5b..62dc220695f 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -32,8 +32,8 @@  #define MPU_EP_CONTROL 		0  /********** MPU semphore: used for SH & BE  *************/ -#define SLIPORT_SEMAPHORE_OFFSET_BE		0x7c -#define SLIPORT_SEMAPHORE_OFFSET_SH		0x94 +#define SLIPORT_SEMAPHORE_OFFSET_BEx		0xac  /* CSR BAR offset */ +#define SLIPORT_SEMAPHORE_OFFSET_SH		0x94  /* PCI-CFG offset */  #define POST_STAGE_MASK				0x0000FFFF  #define POST_ERR_MASK				0x1  #define POST_ERR_SHIFT				31 diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 3860888ac71..08e54f3d288 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -3688,6 +3688,8 @@ static void be_netdev_init(struct net_device *netdev)  static void be_unmap_pci_bars(struct be_adapter *adapter)  { +	if (adapter->csr) +		pci_iounmap(adapter->pdev, adapter->csr);  	if (adapter->db)  		pci_iounmap(adapter->pdev, adapter->db);  } @@ -3721,6 +3723,12 @@ static int be_map_pci_bars(struct be_adapter *adapter)  	adapter->if_type = (sli_intf & SLI_INTF_IF_TYPE_MASK) >>  				SLI_INTF_IF_TYPE_SHIFT; +	if (BEx_chip(adapter) && be_physfn(adapter)) { +		adapter->csr = pci_iomap(adapter->pdev, 2, 0); +		if (adapter->csr == NULL) +			return -ENOMEM; +	} +  	addr = pci_iomap(adapter->pdev, db_bar(adapter), 0);  	if (addr == NULL)  		goto pci_map_err; @@ -4329,6 +4337,8 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev)  	pci_restore_state(pdev);  	/* Check if card is ok and fw is ready */ +	dev_info(&adapter->pdev->dev, +		 "Waiting for FW to be ready after EEH reset\n");  	status = be_fw_wait_ready(adapter);  	if (status)  		return PCI_ERS_RESULT_DISCONNECT; diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 069a155d16e..911d0253dbb 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -934,24 +934,28 @@ static void fec_enet_adjust_link(struct net_device *ndev)  		goto spin_unlock;  	} -	/* Duplex link change */  	if (phy_dev->link) { -		if (fep->full_duplex != phy_dev->duplex) { -			fec_restart(ndev, phy_dev->duplex); -			/* prevent unnecessary second fec_restart() below */ +		if (!fep->link) {  			fep->link = phy_dev->link;  			status_change = 1;  		} -	} -	/* Link on or off change */ -	if (phy_dev->link != fep->link) { -		fep->link = phy_dev->link; -		if (phy_dev->link) +		if (fep->full_duplex != phy_dev->duplex) +			status_change = 1; + +		if (phy_dev->speed != fep->speed) { +			fep->speed = phy_dev->speed; +			status_change = 1; +		} + +		/* if any of the above changed restart the FEC */ +		if (status_change)  			fec_restart(ndev, phy_dev->duplex); -		else +	} else { +		if (fep->link) {  			fec_stop(ndev); -		status_change = 1; +			status_change = 1; +		}  	}  spin_unlock: @@ -1328,7 +1332,7 @@ static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)  static void fec_enet_free_buffers(struct net_device *ndev)  {  	struct fec_enet_private *fep = netdev_priv(ndev); -	int i; +	unsigned int i;  	struct sk_buff *skb;  	struct bufdesc	*bdp; @@ -1352,7 +1356,7 @@ static void fec_enet_free_buffers(struct net_device *ndev)  static int fec_enet_alloc_buffers(struct net_device *ndev)  {  	struct fec_enet_private *fep = netdev_priv(ndev); -	int i; +	unsigned int i;  	struct sk_buff *skb;  	struct bufdesc	*bdp; @@ -1437,6 +1441,7 @@ fec_enet_close(struct net_device *ndev)  	struct fec_enet_private *fep = netdev_priv(ndev);  	/* Don't know what to do yet. */ +	napi_disable(&fep->napi);  	fep->opened = 0;  	netif_stop_queue(ndev);  	fec_stop(ndev); @@ -1593,7 +1598,7 @@ static int fec_enet_init(struct net_device *ndev)  	struct fec_enet_private *fep = netdev_priv(ndev);  	struct bufdesc *cbd_base;  	struct bufdesc *bdp; -	int i; +	unsigned int i;  	/* Allocate memory for buffer descriptors. */  	cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma, diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index f5390071efd..eb437296283 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -240,6 +240,7 @@ struct fec_enet_private {  	phy_interface_t	phy_interface;  	int	link;  	int	full_duplex; +	int	speed;  	struct	completion mdio_done;  	int	irq[FEC_IRQ_NUM];  	int	bufdesc_ex; diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 1f17ca0f220..0d8df400a47 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -128,6 +128,7 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev)  	spin_unlock_irqrestore(&fep->tmreg_lock, flags);  } +EXPORT_SYMBOL(fec_ptp_start_cyclecounter);  /**   * fec_ptp_adjfreq - adjust ptp cycle frequency @@ -318,6 +319,7 @@ int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)  	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?  	    -EFAULT : 0;  } +EXPORT_SYMBOL(fec_ptp_ioctl);  /**   * fec_time_keep - call timecounter_read every second to avoid timer overrun @@ -383,3 +385,4 @@ void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev)  		pr_info("registered PHC device on %s\n", ndev->name);  	}  } +EXPORT_SYMBOL(fec_ptp_init); diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 2c1813737f6..f91a8f3f9d4 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -36,6 +36,7 @@  #include <linux/delay.h>  #include <linux/vmalloc.h>  #include <linux/mdio.h> +#include <linux/pm_runtime.h>  #include "e1000.h" @@ -2229,7 +2230,19 @@ static int e1000e_get_ts_info(struct net_device *netdev,  	return 0;  } +static int e1000e_ethtool_begin(struct net_device *netdev) +{ +	return pm_runtime_get_sync(netdev->dev.parent); +} + +static void e1000e_ethtool_complete(struct net_device *netdev) +{ +	pm_runtime_put_sync(netdev->dev.parent); +} +  static const struct ethtool_ops e1000_ethtool_ops = { +	.begin			= e1000e_ethtool_begin, +	.complete		= e1000e_ethtool_complete,  	.get_settings		= e1000_get_settings,  	.set_settings		= e1000_set_settings,  	.get_drvinfo		= e1000_get_drvinfo, diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index dff7bff8b8e..121a865c7fb 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -782,6 +782,59 @@ release:  }  /** + *  e1000_k1_workaround_lpt_lp - K1 workaround on Lynxpoint-LP + *  @hw:   pointer to the HW structure + *  @link: link up bool flag + * + *  When K1 is enabled for 1Gbps, the MAC can miss 2 DMA completion indications + *  preventing further DMA write requests.  Workaround the issue by disabling + *  the de-assertion of the clock request when in 1Gpbs mode. + **/ +static s32 e1000_k1_workaround_lpt_lp(struct e1000_hw *hw, bool link) +{ +	u32 fextnvm6 = er32(FEXTNVM6); +	s32 ret_val = 0; + +	if (link && (er32(STATUS) & E1000_STATUS_SPEED_1000)) { +		u16 kmrn_reg; + +		ret_val = hw->phy.ops.acquire(hw); +		if (ret_val) +			return ret_val; + +		ret_val = +		    e1000e_read_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG, +						&kmrn_reg); +		if (ret_val) +			goto release; + +		ret_val = +		    e1000e_write_kmrn_reg_locked(hw, +						 E1000_KMRNCTRLSTA_K1_CONFIG, +						 kmrn_reg & +						 ~E1000_KMRNCTRLSTA_K1_ENABLE); +		if (ret_val) +			goto release; + +		usleep_range(10, 20); + +		ew32(FEXTNVM6, fextnvm6 | E1000_FEXTNVM6_REQ_PLL_CLK); + +		ret_val = +		    e1000e_write_kmrn_reg_locked(hw, +						 E1000_KMRNCTRLSTA_K1_CONFIG, +						 kmrn_reg); +release: +		hw->phy.ops.release(hw); +	} else { +		/* clear FEXTNVM6 bit 8 on link down or 10/100 */ +		ew32(FEXTNVM6, fextnvm6 & ~E1000_FEXTNVM6_REQ_PLL_CLK); +	} + +	return ret_val; +} + +/**   *  e1000_check_for_copper_link_ich8lan - Check for link (Copper)   *  @hw: pointer to the HW structure   * @@ -818,6 +871,14 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)  			return ret_val;  	} +	/* Work-around I218 hang issue */ +	if ((hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPTLP_I218_LM) || +	    (hw->adapter->pdev->device == E1000_DEV_ID_PCH_LPTLP_I218_V)) { +		ret_val = e1000_k1_workaround_lpt_lp(hw, link); +		if (ret_val) +			return ret_val; +	} +  	/* Clear link partner's EEE ability */  	hw->dev_spec.ich8lan.eee_lp_ability = 0; @@ -3954,8 +4015,16 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw)  	phy_ctrl = er32(PHY_CTRL);  	phy_ctrl |= E1000_PHY_CTRL_GBE_DISABLE; +  	if (hw->phy.type == e1000_phy_i217) { -		u16 phy_reg; +		u16 phy_reg, device_id = hw->adapter->pdev->device; + +		if ((device_id == E1000_DEV_ID_PCH_LPTLP_I218_LM) || +		    (device_id == E1000_DEV_ID_PCH_LPTLP_I218_V)) { +			u32 fextnvm6 = er32(FEXTNVM6); + +			ew32(FEXTNVM6, fextnvm6 & ~E1000_FEXTNVM6_REQ_PLL_CLK); +		}  		ret_val = hw->phy.ops.acquire(hw);  		if (ret_val) diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.h b/drivers/net/ethernet/intel/e1000e/ich8lan.h index b6d3174d7d2..8bf4655c2e1 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.h +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.h @@ -92,6 +92,8 @@  #define E1000_FEXTNVM4_BEACON_DURATION_8USEC	0x7  #define E1000_FEXTNVM4_BEACON_DURATION_16USEC	0x3 +#define E1000_FEXTNVM6_REQ_PLL_CLK	0x00000100 +  #define PCIE_ICH8_SNOOP_ALL	PCIE_NO_SNOOP_ALL  #define E1000_ICH_RAR_ENTRIES	7 diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index a177b8b65c4..948b86ffa4f 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4303,6 +4303,7 @@ static int e1000_open(struct net_device *netdev)  	netif_start_queue(netdev);  	adapter->idle_check = true; +	hw->mac.get_link_status = true;  	pm_runtime_put(&pdev->dev);  	/* fire a link status change interrupt to start the watchdog */ @@ -4662,6 +4663,7 @@ static void e1000_phy_read_status(struct e1000_adapter *adapter)  	    (adapter->hw.phy.media_type == e1000_media_type_copper)) {  		int ret_val; +		pm_runtime_get_sync(&adapter->pdev->dev);  		ret_val = e1e_rphy(hw, MII_BMCR, &phy->bmcr);  		ret_val |= e1e_rphy(hw, MII_BMSR, &phy->bmsr);  		ret_val |= e1e_rphy(hw, MII_ADVERTISE, &phy->advertise); @@ -4672,6 +4674,7 @@ static void e1000_phy_read_status(struct e1000_adapter *adapter)  		ret_val |= e1e_rphy(hw, MII_ESTATUS, &phy->estatus);  		if (ret_val)  			e_warn("Error reading PHY register\n"); +		pm_runtime_put_sync(&adapter->pdev->dev);  	} else {  		/* Do not read PHY registers if link is not up  		 * Set values to typical power-on defaults @@ -5887,8 +5890,7 @@ release:  	return retval;  } -static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake, -			    bool runtime) +static int __e1000_shutdown(struct pci_dev *pdev, bool runtime)  {  	struct net_device *netdev = pci_get_drvdata(pdev);  	struct e1000_adapter *adapter = netdev_priv(netdev); @@ -5912,10 +5914,6 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake,  	}  	e1000e_reset_interrupt_capability(adapter); -	retval = pci_save_state(pdev); -	if (retval) -		return retval; -  	status = er32(STATUS);  	if (status & E1000_STATUS_LU)  		wufc &= ~E1000_WUFC_LNKC; @@ -5971,13 +5969,6 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake,  		ew32(WUFC, 0);  	} -	*enable_wake = !!wufc; - -	/* make sure adapter isn't asleep if manageability is enabled */ -	if ((adapter->flags & FLAG_MNG_PT_ENABLED) || -	    (hw->mac.ops.check_mng_mode(hw))) -		*enable_wake = true; -  	if (adapter->hw.phy.type == e1000_phy_igp_3)  		e1000e_igp3_phy_powerdown_workaround_ich8lan(&adapter->hw); @@ -5986,27 +5977,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake,  	 */  	e1000e_release_hw_control(adapter); -	pci_disable_device(pdev); - -	return 0; -} - -static void e1000_power_off(struct pci_dev *pdev, bool sleep, bool wake) -{ -	if (sleep && wake) { -		pci_prepare_to_sleep(pdev); -		return; -	} - -	pci_wake_from_d3(pdev, wake); -	pci_set_power_state(pdev, PCI_D3hot); -} - -static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep, -                                    bool wake) -{ -	struct net_device *netdev = pci_get_drvdata(pdev); -	struct e1000_adapter *adapter = netdev_priv(netdev); +	pci_clear_master(pdev);  	/* The pci-e switch on some quad port adapters will report a  	 * correctable error when the MAC transitions from D0 to D3.  To @@ -6021,12 +5992,13 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep,  		pcie_capability_write_word(us_dev, PCI_EXP_DEVCTL,  					   (devctl & ~PCI_EXP_DEVCTL_CERE)); -		e1000_power_off(pdev, sleep, wake); +		pci_save_state(pdev); +		pci_prepare_to_sleep(pdev);  		pcie_capability_write_word(us_dev, PCI_EXP_DEVCTL, devctl); -	} else { -		e1000_power_off(pdev, sleep, wake);  	} + +	return 0;  }  #ifdef CONFIG_PCIEASPM @@ -6084,9 +6056,7 @@ static int __e1000_resume(struct pci_dev *pdev)  	if (aspm_disable_flag)  		e1000e_disable_aspm(pdev, aspm_disable_flag); -	pci_set_power_state(pdev, PCI_D0); -	pci_restore_state(pdev); -	pci_save_state(pdev); +	pci_set_master(pdev);  	e1000e_set_interrupt_capability(adapter);  	if (netif_running(netdev)) { @@ -6152,14 +6122,8 @@ static int __e1000_resume(struct pci_dev *pdev)  static int e1000_suspend(struct device *dev)  {  	struct pci_dev *pdev = to_pci_dev(dev); -	int retval; -	bool wake; - -	retval = __e1000_shutdown(pdev, &wake, false); -	if (!retval) -		e1000_complete_shutdown(pdev, true, wake); -	return retval; +	return __e1000_shutdown(pdev, false);  }  static int e1000_resume(struct device *dev) @@ -6182,13 +6146,10 @@ static int e1000_runtime_suspend(struct device *dev)  	struct net_device *netdev = pci_get_drvdata(pdev);  	struct e1000_adapter *adapter = netdev_priv(netdev); -	if (e1000e_pm_ready(adapter)) { -		bool wake; - -		__e1000_shutdown(pdev, &wake, true); -	} +	if (!e1000e_pm_ready(adapter)) +		return 0; -	return 0; +	return __e1000_shutdown(pdev, true);  }  static int e1000_idle(struct device *dev) @@ -6226,12 +6187,7 @@ static int e1000_runtime_resume(struct device *dev)  static void e1000_shutdown(struct pci_dev *pdev)  { -	bool wake = false; - -	__e1000_shutdown(pdev, &wake, false); - -	if (system_state == SYSTEM_POWER_OFF) -		e1000_complete_shutdown(pdev, false, wake); +	__e1000_shutdown(pdev, false);  }  #ifdef CONFIG_NET_POLL_CONTROLLER @@ -6352,9 +6308,9 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)  			"Cannot re-enable PCI device after reset.\n");  		result = PCI_ERS_RESULT_DISCONNECT;  	} else { -		pci_set_master(pdev);  		pdev->state_saved = true;  		pci_restore_state(pdev); +		pci_set_master(pdev);  		pci_enable_wake(pdev, PCI_D3hot, 0);  		pci_enable_wake(pdev, PCI_D3cold, 0); @@ -6783,7 +6739,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	/* initialize the wol settings based on the eeprom settings */  	adapter->wol = adapter->eeprom_wol; -	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); + +	/* make sure adapter isn't asleep if manageability is enabled */ +	if (adapter->wol || (adapter->flags & FLAG_MNG_PT_ENABLED) || +	    (hw->mac.ops.check_mng_mode(hw))) +		device_wakeup_enable(&pdev->dev);  	/* save off EEPROM version number */  	e1000_read_nvm(&adapter->hw, 5, 1, &adapter->eeprom_vers); diff --git a/drivers/net/ethernet/intel/e1000e/regs.h b/drivers/net/ethernet/intel/e1000e/regs.h index 794fe149766..a7e6a3e3725 100644 --- a/drivers/net/ethernet/intel/e1000e/regs.h +++ b/drivers/net/ethernet/intel/e1000e/regs.h @@ -42,6 +42,7 @@  #define E1000_FEXTNVM	0x00028	/* Future Extended NVM - RW */  #define E1000_FEXTNVM3	0x0003C	/* Future Extended NVM 3 - RW */  #define E1000_FEXTNVM4	0x00024	/* Future Extended NVM 4 - RW */ +#define E1000_FEXTNVM6	0x00010	/* Future Extended NVM 6 - RW */  #define E1000_FEXTNVM7	0x000E4	/* Future Extended NVM 7 - RW */  #define E1000_FCT	0x00030	/* Flow Control Type - RW */  #define E1000_VET	0x00038	/* VLAN Ether Type - RW */ diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 84e7e0909de..12b1d848080 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -1361,11 +1361,16 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)  	switch (hw->phy.type) {  	case e1000_phy_i210:  	case e1000_phy_m88: -		if (hw->phy.id == I347AT4_E_PHY_ID || -		    hw->phy.id == M88E1112_E_PHY_ID) +		switch (hw->phy.id) { +		case I347AT4_E_PHY_ID: +		case M88E1112_E_PHY_ID: +		case I210_I_PHY_ID:  			ret_val = igb_copper_link_setup_m88_gen2(hw); -		else +			break; +		default:  			ret_val = igb_copper_link_setup_m88(hw); +			break; +		}  		break;  	case e1000_phy_igp_3:  		ret_val = igb_copper_link_setup_igp(hw); @@ -1813,27 +1818,32 @@ out:   **/  void igb_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf)  { -	u32 dtxswc; +	u32 reg_val, reg_offset;  	switch (hw->mac.type) {  	case e1000_82576: +		reg_offset = E1000_DTXSWC; +		break;  	case e1000_i350: -		dtxswc = rd32(E1000_DTXSWC); -		if (enable) { -			dtxswc |= (E1000_DTXSWC_MAC_SPOOF_MASK | -				   E1000_DTXSWC_VLAN_SPOOF_MASK); -			/* The PF can spoof - it has to in order to -			 * support emulation mode NICs */ -			dtxswc ^= (1 << pf | 1 << (pf + MAX_NUM_VFS)); -		} else { -			dtxswc &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | -				    E1000_DTXSWC_VLAN_SPOOF_MASK); -		} -		wr32(E1000_DTXSWC, dtxswc); +		reg_offset = E1000_TXSWC;  		break;  	default: -		break; +		return; +	} + +	reg_val = rd32(reg_offset); +	if (enable) { +		reg_val |= (E1000_DTXSWC_MAC_SPOOF_MASK | +			     E1000_DTXSWC_VLAN_SPOOF_MASK); +		/* The PF can spoof - it has to in order to +		 * support emulation mode NICs +		 */ +		reg_val ^= (1 << pf | 1 << (pf + MAX_NUM_VFS)); +	} else { +		reg_val &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | +			     E1000_DTXSWC_VLAN_SPOOF_MASK);  	} +	wr32(reg_offset, reg_val);  }  /** diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index d27edbc6392..25151401c2a 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -447,7 +447,7 @@ struct igb_adapter {  #endif  	struct i2c_algo_bit_data i2c_algo;  	struct i2c_adapter i2c_adap; -	struct igb_i2c_client_list *i2c_clients; +	struct i2c_client *i2c_client;  };  #define IGB_FLAG_HAS_MSI		(1 << 0) diff --git a/drivers/net/ethernet/intel/igb/igb_hwmon.c b/drivers/net/ethernet/intel/igb/igb_hwmon.c index 0a9b073d0b0..0478a1abe54 100644 --- a/drivers/net/ethernet/intel/igb/igb_hwmon.c +++ b/drivers/net/ethernet/intel/igb/igb_hwmon.c @@ -39,6 +39,10 @@  #include <linux/pci.h>  #ifdef CONFIG_IGB_HWMON +static struct i2c_board_info i350_sensor_info = { +	I2C_BOARD_INFO("i350bb", (0Xf8 >> 1)), +}; +  /* hwmon callback functions */  static ssize_t igb_hwmon_show_location(struct device *dev,  					 struct device_attribute *attr, @@ -188,6 +192,7 @@ int igb_sysfs_init(struct igb_adapter *adapter)  	unsigned int i;  	int n_attrs;  	int rc = 0; +	struct i2c_client *client = NULL;  	/* If this method isn't defined we don't support thermals */  	if (adapter->hw.mac.ops.init_thermal_sensor_thresh == NULL) @@ -198,6 +203,15 @@ int igb_sysfs_init(struct igb_adapter *adapter)  		if (rc)  			goto exit; +	/* init i2c_client */ +	client = i2c_new_device(&adapter->i2c_adap, &i350_sensor_info); +	if (client == NULL) { +		dev_info(&adapter->pdev->dev, +			"Failed to create new i2c device..\n"); +		goto exit; +	} +	adapter->i2c_client = client; +  	/* Allocation space for max attributes  	 * max num sensors * values (loc, temp, max, caution)  	 */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ed79a1c53b5..8496adfc6a6 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1923,10 +1923,6 @@ void igb_set_fw_version(struct igb_adapter *adapter)  	return;  } -static const struct i2c_board_info i350_sensor_info = { -	I2C_BOARD_INFO("i350bb", 0Xf8), -}; -  /*  igb_init_i2c - Init I2C interface   *  @adapter: pointer to adapter structure   * @@ -2546,8 +2542,8 @@ static void igb_probe_vfs(struct igb_adapter *adapter)  	if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211))  		return; -	igb_enable_sriov(pdev, max_vfs);  	pci_sriov_set_totalvfs(pdev, 7); +	igb_enable_sriov(pdev, max_vfs);  #endif /* CONFIG_PCI_IOV */  } @@ -2656,7 +2652,7 @@ static int igb_sw_init(struct igb_adapter *adapter)  		if (max_vfs > 7) {  			dev_warn(&pdev->dev,  				 "Maximum of 7 VFs per PF, using max\n"); -			adapter->vfs_allocated_count = 7; +			max_vfs = adapter->vfs_allocated_count = 7;  		} else  			adapter->vfs_allocated_count = max_vfs;  		if (adapter->vfs_allocated_count) @@ -6227,13 +6223,6 @@ static struct sk_buff *igb_build_rx_buffer(struct igb_ring *rx_ring,  	/* If we spanned a buffer we have a huge mess so test for it */  	BUG_ON(unlikely(!igb_test_staterr(rx_desc, E1000_RXD_STAT_EOP))); -	/* Guarantee this function can be used by verifying buffer sizes */ -	BUILD_BUG_ON(SKB_WITH_OVERHEAD(IGB_RX_BUFSZ) < (NET_SKB_PAD + -							NET_IP_ALIGN + -							IGB_TS_HDR_LEN + -							ETH_FRAME_LEN + -							ETH_FCS_LEN)); -  	rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean];  	page = rx_buffer->page;  	prefetchw(page); @@ -7724,67 +7713,6 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)  	}  } -static DEFINE_SPINLOCK(i2c_clients_lock); - -/*  igb_get_i2c_client - returns matching client - *  in adapters's client list. - *  @adapter: adapter struct - *  @dev_addr: device address of i2c needed. - */ -static struct i2c_client * -igb_get_i2c_client(struct igb_adapter *adapter, u8 dev_addr) -{ -	ulong flags; -	struct igb_i2c_client_list *client_list; -	struct i2c_client *client = NULL; -	struct i2c_board_info client_info = { -		I2C_BOARD_INFO("igb", 0x00), -	}; - -	spin_lock_irqsave(&i2c_clients_lock, flags); -	client_list = adapter->i2c_clients; - -	/* See if we already have an i2c_client */ -	while (client_list) { -		if (client_list->client->addr == (dev_addr >> 1)) { -			client = client_list->client; -			goto exit; -		} else { -			client_list = client_list->next; -		} -	} - -	/* no client_list found, create a new one */ -	client_list = kzalloc(sizeof(*client_list), GFP_ATOMIC); -	if (client_list == NULL) -		goto exit; - -	/* dev_addr passed to us is left-shifted by 1 bit -	 * i2c_new_device call expects it to be flush to the right. -	 */ -	client_info.addr = dev_addr >> 1; -	client_info.platform_data = adapter; -	client_list->client = i2c_new_device(&adapter->i2c_adap, &client_info); -	if (client_list->client == NULL) { -		dev_info(&adapter->pdev->dev, -			"Failed to create new i2c device..\n"); -		goto err_no_client; -	} - -	/* insert new client at head of list */ -	client_list->next = adapter->i2c_clients; -	adapter->i2c_clients = client_list; - -	client = client_list->client; -	goto exit; - -err_no_client: -	kfree(client_list); -exit: -	spin_unlock_irqrestore(&i2c_clients_lock, flags); -	return client; -} -  /*  igb_read_i2c_byte - Reads 8 bit word over I2C   *  @hw: pointer to hardware structure   *  @byte_offset: byte offset to read @@ -7798,7 +7726,7 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,  				u8 dev_addr, u8 *data)  {  	struct igb_adapter *adapter = container_of(hw, struct igb_adapter, hw); -	struct i2c_client *this_client = igb_get_i2c_client(adapter, dev_addr); +	struct i2c_client *this_client = adapter->i2c_client;  	s32 status;  	u16 swfw_mask = 0; @@ -7835,7 +7763,7 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,  				 u8 dev_addr, u8 data)  {  	struct igb_adapter *adapter = container_of(hw, struct igb_adapter, hw); -	struct i2c_client *this_client = igb_get_i2c_client(adapter, dev_addr); +	struct i2c_client *this_client = adapter->i2c_client;  	s32 status;  	u16 swfw_mask = E1000_SWFW_PHY0_SM; diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 0987822359f..0a237507ee8 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -740,7 +740,7 @@ void igb_ptp_init(struct igb_adapter *adapter)  	case e1000_82576:  		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);  		adapter->ptp_caps.owner = THIS_MODULE; -		adapter->ptp_caps.max_adj = 1000000000; +		adapter->ptp_caps.max_adj = 999999881;  		adapter->ptp_caps.n_ext_ts = 0;  		adapter->ptp_caps.pps = 0;  		adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82576; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index c3db6cd69b6..2b6cb5ca48e 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -944,9 +944,17 @@ free_queue_irqs:  		free_irq(adapter->msix_entries[vector].vector,  			 adapter->q_vector[vector]);  	} -	pci_disable_msix(adapter->pdev); -	kfree(adapter->msix_entries); -	adapter->msix_entries = NULL; +	/* This failure is non-recoverable - it indicates the system is +	 * out of MSIX vector resources and the VF driver cannot run +	 * without them.  Set the number of msix vectors to zero +	 * indicating that not enough can be allocated.  The error +	 * will be returned to the user indicating device open failed. +	 * Any further attempts to force the driver to open will also +	 * fail.  The only way to recover is to unload the driver and +	 * reload it again.  If the system has recovered some MSIX +	 * vectors then it may succeed. +	 */ +	adapter->num_msix_vectors = 0;  	return err;  } @@ -2572,6 +2580,15 @@ static int ixgbevf_open(struct net_device *netdev)  	struct ixgbe_hw *hw = &adapter->hw;  	int err; +	/* A previous failure to open the device because of a lack of +	 * available MSIX vector resources may have reset the number +	 * of msix vectors variable to zero.  The only way to recover +	 * is to unload/reload the driver and hope that the system has +	 * been able to recover some MSIX vector resources. +	 */ +	if (!adapter->num_msix_vectors) +		return -ENOMEM; +  	/* disallow open during test */  	if (test_bit(__IXGBEVF_TESTING, &adapter->state))  		return -EBUSY; @@ -2628,7 +2645,6 @@ static int ixgbevf_open(struct net_device *netdev)  err_req_irq:  	ixgbevf_down(adapter); -	ixgbevf_free_irq(adapter);  err_setup_rx:  	ixgbevf_free_all_rx_resources(adapter);  err_setup_tx: diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 6a2127489af..bfdb0686039 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -769,7 +769,7 @@ ltq_etop_probe(struct platform_device *pdev)  	return 0;  err_free: -	kfree(dev); +	free_netdev(dev);  err_out:  	return err;  } diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 29140502b71..6562c736a1d 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1081,6 +1081,45 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq)  /* mii management interface *************************************************/ +static void mv643xx_adjust_pscr(struct mv643xx_eth_private *mp) +{ +	u32 pscr = rdlp(mp, PORT_SERIAL_CONTROL); +	u32 autoneg_disable = FORCE_LINK_PASS | +	             DISABLE_AUTO_NEG_SPEED_GMII | +		     DISABLE_AUTO_NEG_FOR_FLOW_CTRL | +		     DISABLE_AUTO_NEG_FOR_DUPLEX; + +	if (mp->phy->autoneg == AUTONEG_ENABLE) { +		/* enable auto negotiation */ +		pscr &= ~autoneg_disable; +		goto out_write; +	} + +	pscr |= autoneg_disable; + +	if (mp->phy->speed == SPEED_1000) { +		/* force gigabit, half duplex not supported */ +		pscr |= SET_GMII_SPEED_TO_1000; +		pscr |= SET_FULL_DUPLEX_MODE; +		goto out_write; +	} + +	pscr &= ~SET_GMII_SPEED_TO_1000; + +	if (mp->phy->speed == SPEED_100) +		pscr |= SET_MII_SPEED_TO_100; +	else +		pscr &= ~SET_MII_SPEED_TO_100; + +	if (mp->phy->duplex == DUPLEX_FULL) +		pscr |= SET_FULL_DUPLEX_MODE; +	else +		pscr &= ~SET_FULL_DUPLEX_MODE; + +out_write: +	wrlp(mp, PORT_SERIAL_CONTROL, pscr); +} +  static irqreturn_t mv643xx_eth_err_irq(int irq, void *dev_id)  {  	struct mv643xx_eth_shared_private *msp = dev_id; @@ -1499,6 +1538,7 @@ static int  mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)  {  	struct mv643xx_eth_private *mp = netdev_priv(dev); +	int ret;  	if (mp->phy == NULL)  		return -EINVAL; @@ -1508,7 +1548,10 @@ mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)  	 */  	cmd->advertising &= ~ADVERTISED_1000baseT_Half; -	return phy_ethtool_sset(mp->phy, cmd); +	ret = phy_ethtool_sset(mp->phy, cmd); +	if (!ret) +		mv643xx_adjust_pscr(mp); +	return ret;  }  static void mv643xx_eth_get_drvinfo(struct net_device *dev, @@ -2442,11 +2485,15 @@ static int mv643xx_eth_stop(struct net_device *dev)  static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)  {  	struct mv643xx_eth_private *mp = netdev_priv(dev); +	int ret; -	if (mp->phy != NULL) -		return phy_mii_ioctl(mp->phy, ifr, cmd); +	if (mp->phy == NULL) +		return -ENOTSUPP; -	return -EOPNOTSUPP; +	ret = phy_mii_ioctl(mp->phy, ifr, cmd); +	if (!ret) +		mv643xx_adjust_pscr(mp); +	return ret;  }  static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 7e64033d7de..0706623cfb9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -226,7 +226,7 @@ void __mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn)  static void mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn)  { -	u64 in_param; +	u64 in_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index bb4d8d99f36..f278b10ef71 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -565,34 +565,38 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv)  	struct mlx4_en_dev *mdev = priv->mdev;  	struct mlx4_dev *dev = mdev->dev;  	int qpn = priv->base_qpn; -	u64 mac = mlx4_en_mac_to_u64(priv->dev->dev_addr); - -	en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", -	       priv->dev->dev_addr); -	mlx4_unregister_mac(dev, priv->port, mac); +	u64 mac; -	if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) { +	if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { +		mac = mlx4_en_mac_to_u64(priv->dev->dev_addr); +		en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", +		       priv->dev->dev_addr); +		mlx4_unregister_mac(dev, priv->port, mac); +	} else {  		struct mlx4_mac_entry *entry;  		struct hlist_node *tmp;  		struct hlist_head *bucket; -		unsigned int mac_hash; +		unsigned int i; -		mac_hash = priv->dev->dev_addr[MLX4_EN_MAC_HASH_IDX]; -		bucket = &priv->mac_hash[mac_hash]; -		hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { -			if (ether_addr_equal_64bits(entry->mac, -						    priv->dev->dev_addr)) { -				en_dbg(DRV, priv, "Releasing qp: port %d, MAC %pM, qpn %d\n", -				       priv->port, priv->dev->dev_addr, qpn); +		for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { +			bucket = &priv->mac_hash[i]; +			hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { +				mac = mlx4_en_mac_to_u64(entry->mac); +				en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", +				       entry->mac);  				mlx4_en_uc_steer_release(priv, entry->mac,  							 qpn, entry->reg_id); -				mlx4_qp_release_range(dev, qpn, 1); +				mlx4_unregister_mac(dev, priv->port, mac);  				hlist_del_rcu(&entry->hlist);  				kfree_rcu(entry, rcu); -				break;  			}  		} + +		en_dbg(DRV, priv, "Releasing qp: port %d, qpn %d\n", +		       priv->port, qpn); +		mlx4_qp_release_range(dev, qpn, 1); +		priv->flags &= ~MLX4_EN_FLAG_FORCE_PROMISC;  	}  } @@ -650,28 +654,10 @@ u64 mlx4_en_mac_to_u64(u8 *addr)  	return mac;  } -static int mlx4_en_set_mac(struct net_device *dev, void *addr) -{ -	struct mlx4_en_priv *priv = netdev_priv(dev); -	struct mlx4_en_dev *mdev = priv->mdev; -	struct sockaddr *saddr = addr; - -	if (!is_valid_ether_addr(saddr->sa_data)) -		return -EADDRNOTAVAIL; - -	memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); -	queue_work(mdev->workqueue, &priv->mac_task); -	return 0; -} - -static void mlx4_en_do_set_mac(struct work_struct *work) +static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv)  { -	struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, -						 mac_task); -	struct mlx4_en_dev *mdev = priv->mdev;  	int err = 0; -	mutex_lock(&mdev->state_lock);  	if (priv->port_up) {  		/* Remove old MAC and insert the new one */  		err = mlx4_en_replace_mac(priv, priv->base_qpn, @@ -683,7 +669,26 @@ static void mlx4_en_do_set_mac(struct work_struct *work)  	} else  		en_dbg(HW, priv, "Port is down while registering mac, exiting...\n"); +	return err; +} + +static int mlx4_en_set_mac(struct net_device *dev, void *addr) +{ +	struct mlx4_en_priv *priv = netdev_priv(dev); +	struct mlx4_en_dev *mdev = priv->mdev; +	struct sockaddr *saddr = addr; +	int err; + +	if (!is_valid_ether_addr(saddr->sa_data)) +		return -EADDRNOTAVAIL; + +	memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); + +	mutex_lock(&mdev->state_lock); +	err = mlx4_en_do_set_mac(priv);  	mutex_unlock(&mdev->state_lock); + +	return err;  }  static void mlx4_en_clear_list(struct net_device *dev) @@ -1348,7 +1353,7 @@ static void mlx4_en_do_get_stats(struct work_struct *work)  		queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);  	}  	if (mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port]) { -		queue_work(mdev->workqueue, &priv->mac_task); +		mlx4_en_do_set_mac(priv);  		mdev->mac_removed[MLX4_MAX_PORTS + 1 - priv->port] = 0;  	}  	mutex_unlock(&mdev->state_lock); @@ -1632,6 +1637,17 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)  	/* Flush multicast filter */  	mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG); +	/* Remove flow steering rules for the port*/ +	if (mdev->dev->caps.steering_mode == +	    MLX4_STEERING_MODE_DEVICE_MANAGED) { +		ASSERT_RTNL(); +		list_for_each_entry_safe(flow, tmp_flow, +					 &priv->ethtool_list, list) { +			mlx4_flow_detach(mdev->dev, flow->id); +			list_del(&flow->list); +		} +	} +  	mlx4_en_destroy_drop_qp(priv);  	/* Free TX Rings */ @@ -1652,17 +1668,6 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)  	if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN))  		mdev->mac_removed[priv->port] = 1; -	/* Remove flow steering rules for the port*/ -	if (mdev->dev->caps.steering_mode == -	    MLX4_STEERING_MODE_DEVICE_MANAGED) { -		ASSERT_RTNL(); -		list_for_each_entry_safe(flow, tmp_flow, -					 &priv->ethtool_list, list) { -			mlx4_flow_detach(mdev->dev, flow->id); -			list_del(&flow->list); -		} -	} -  	/* Free RX Rings */  	for (i = 0; i < priv->rx_ring_num; i++) {  		mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); @@ -1828,9 +1833,11 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)  	}  #ifdef CONFIG_RFS_ACCEL -	priv->dev->rx_cpu_rmap = alloc_irq_cpu_rmap(priv->mdev->dev->caps.comp_pool); -	if (!priv->dev->rx_cpu_rmap) -		goto err; +	if (priv->mdev->dev->caps.comp_pool) { +		priv->dev->rx_cpu_rmap = alloc_irq_cpu_rmap(priv->mdev->dev->caps.comp_pool); +		if (!priv->dev->rx_cpu_rmap) +			goto err; +	}  #endif  	return 0; @@ -2078,7 +2085,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,  	priv->msg_enable = MLX4_EN_MSG_LEVEL;  	spin_lock_init(&priv->stats_lock);  	INIT_WORK(&priv->rx_mode_task, mlx4_en_do_set_rx_mode); -	INIT_WORK(&priv->mac_task, mlx4_en_do_set_mac);  	INIT_WORK(&priv->watchdog_task, mlx4_en_restart);  	INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate);  	INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats); diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 251ae2f9311..8e3123a1df8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -771,7 +771,7 @@ int mlx4_MAP_EQ_wrapper(struct mlx4_dev *dev, int slave,  	struct mlx4_slave_event_eq_info *event_eq =  		priv->mfunc.master.slave_state[slave].event_eq;  	u32 in_modifier = vhcr->in_modifier; -	u32 eqn = in_modifier & 0x1FF; +	u32 eqn = in_modifier & 0x3FF;  	u64 in_param =  vhcr->in_param;  	int err = 0;  	int i; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 50917eb3013..f6245579962 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -787,6 +787,14 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave,  	bmme_flags &= ~MLX4_BMME_FLAG_TYPE_2_WIN;  	MLX4_PUT(outbox->buf, bmme_flags, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); +	/* turn off device-managed steering capability if not enabled */ +	if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) { +		MLX4_GET(field, outbox->buf, +			 QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET); +		field &= 0x7f; +		MLX4_PUT(outbox->buf, field, +			 QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET); +	}  	return 0;  } diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index d180bc46826..16abde20e1f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1555,7 +1555,7 @@ void __mlx4_counter_free(struct mlx4_dev *dev, u32 idx)  void mlx4_counter_free(struct mlx4_dev *dev, u32 idx)  { -	u64 in_param; +	u64 in_param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(&in_param, idx); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index cf883345af8..d738454116a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1235,7 +1235,7 @@ int mlx4_get_qp_per_mgm(struct mlx4_dev *dev);  static inline void set_param_l(u64 *arg, u32 val)  { -	*((u32 *)arg) = val; +	*arg = (*arg & 0xffffffff00000000ULL) | (u64) val;  }  static inline void set_param_h(u64 *arg, u32 val) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index c313d7e943a..f710b7ce0dc 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -509,7 +509,6 @@ struct mlx4_en_priv {  	struct mlx4_en_cq rx_cq[MAX_RX_RINGS];  	struct mlx4_qp drop_qp;  	struct work_struct rx_mode_task; -	struct work_struct mac_task;  	struct work_struct watchdog_task;  	struct work_struct linkstate_task;  	struct delayed_work stats_task; diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 602ca9bf78e..f91719a08cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -183,7 +183,7 @@ u32 __mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order)  static u32 mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order)  { -	u64 in_param; +	u64 in_param = 0;  	u64 out_param;  	int err; @@ -240,7 +240,7 @@ void __mlx4_free_mtt_range(struct mlx4_dev *dev, u32 offset, int order)  static void mlx4_free_mtt_range(struct mlx4_dev *dev, u32 offset, int order)  { -	u64 in_param; +	u64 in_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { @@ -351,7 +351,7 @@ void __mlx4_mpt_release(struct mlx4_dev *dev, u32 index)  static void mlx4_mpt_release(struct mlx4_dev *dev, u32 index)  { -	u64 in_param; +	u64 in_param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(&in_param, index); @@ -374,7 +374,7 @@ int __mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index)  static int mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index)  { -	u64 param; +	u64 param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(¶m, index); @@ -395,7 +395,7 @@ void __mlx4_mpt_free_icm(struct mlx4_dev *dev, u32 index)  static void mlx4_mpt_free_icm(struct mlx4_dev *dev, u32 index)  { -	u64 in_param; +	u64 in_param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(&in_param, index); diff --git a/drivers/net/ethernet/mellanox/mlx4/pd.c b/drivers/net/ethernet/mellanox/mlx4/pd.c index 1ac88637ad9..00f223acada 100644 --- a/drivers/net/ethernet/mellanox/mlx4/pd.c +++ b/drivers/net/ethernet/mellanox/mlx4/pd.c @@ -101,7 +101,7 @@ void __mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn)  void mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn)  { -	u64 in_param; +	u64 in_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 719ead15e49..10c57c86388 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -175,7 +175,7 @@ EXPORT_SYMBOL_GPL(__mlx4_register_mac);  int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)  { -	u64 out_param; +	u64 out_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { @@ -222,7 +222,7 @@ EXPORT_SYMBOL_GPL(__mlx4_unregister_mac);  void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)  { -	u64 out_param; +	u64 out_param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(&out_param, port); @@ -361,7 +361,7 @@ out:  int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index)  { -	u64 out_param; +	u64 out_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { @@ -406,7 +406,7 @@ out:  void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index)  { -	u64 in_param; +	u64 in_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 81e2abe07bb..e891b058c1b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -222,7 +222,7 @@ int __mlx4_qp_reserve_range(struct mlx4_dev *dev, int cnt, int align,  int mlx4_qp_reserve_range(struct mlx4_dev *dev, int cnt, int align, int *base)  { -	u64 in_param; +	u64 in_param = 0;  	u64 out_param;  	int err; @@ -255,7 +255,7 @@ void __mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt)  void mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt)  { -	u64 in_param; +	u64 in_param = 0;  	int err;  	if (mlx4_is_mfunc(dev)) { @@ -319,7 +319,7 @@ err_out:  static int mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn)  { -	u64 param; +	u64 param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(¶m, qpn); @@ -344,7 +344,7 @@ void __mlx4_qp_free_icm(struct mlx4_dev *dev, int qpn)  static void mlx4_qp_free_icm(struct mlx4_dev *dev, int qpn)  { -	u64 in_param; +	u64 in_param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(&in_param, qpn); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 083fb48dc3d..1391b52f443 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -99,6 +99,7 @@ struct res_qp {  	struct list_head	mcg_list;  	spinlock_t		mcg_spl;  	int			local_qpn; +	atomic_t		ref_count;  };  enum res_mtt_states { @@ -197,6 +198,7 @@ enum res_fs_rule_states {  struct res_fs_rule {  	struct res_common	com; +	int			qpn;  };  static void *res_tracker_lookup(struct rb_root *root, u64 res_id) @@ -355,7 +357,7 @@ static int mpt_mask(struct mlx4_dev *dev)  	return dev->caps.num_mpts - 1;  } -static void *find_res(struct mlx4_dev *dev, int res_id, +static void *find_res(struct mlx4_dev *dev, u64 res_id,  		      enum mlx4_resource type)  {  	struct mlx4_priv *priv = mlx4_priv(dev); @@ -447,6 +449,7 @@ static struct res_common *alloc_qp_tr(int id)  	ret->local_qpn = id;  	INIT_LIST_HEAD(&ret->mcg_list);  	spin_lock_init(&ret->mcg_spl); +	atomic_set(&ret->ref_count, 0);  	return &ret->com;  } @@ -554,7 +557,7 @@ static struct res_common *alloc_xrcdn_tr(int id)  	return &ret->com;  } -static struct res_common *alloc_fs_rule_tr(u64 id) +static struct res_common *alloc_fs_rule_tr(u64 id, int qpn)  {  	struct res_fs_rule *ret; @@ -564,7 +567,7 @@ static struct res_common *alloc_fs_rule_tr(u64 id)  	ret->com.res_id = id;  	ret->com.state = RES_FS_RULE_ALLOCATED; - +	ret->qpn = qpn;  	return &ret->com;  } @@ -602,7 +605,7 @@ static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave,  		ret = alloc_xrcdn_tr(id);  		break;  	case RES_FS_RULE: -		ret = alloc_fs_rule_tr(id); +		ret = alloc_fs_rule_tr(id, extra);  		break;  	default:  		return NULL; @@ -671,10 +674,14 @@ undo:  static int remove_qp_ok(struct res_qp *res)  { -	if (res->com.state == RES_QP_BUSY) +	if (res->com.state == RES_QP_BUSY || atomic_read(&res->ref_count) || +	    !list_empty(&res->mcg_list)) { +		pr_err("resource tracker: fail to remove qp, state %d, ref_count %d\n", +		       res->com.state, atomic_read(&res->ref_count));  		return -EBUSY; -	else if (res->com.state != RES_QP_RESERVED) +	} else if (res->com.state != RES_QP_RESERVED) {  		return -EPERM; +	}  	return 0;  } @@ -2990,6 +2997,9 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  	u8 steer_type_mask = 2;  	enum mlx4_steer_type type = (gid[7] & steer_type_mask) >> 1; +	if (dev->caps.steering_mode != MLX4_STEERING_MODE_B0) +		return -EINVAL; +  	qpn = vhcr->in_modifier & 0xffffff;  	err = get_res(dev, slave, qpn, RES_QP, &rqp);  	if (err) @@ -3121,6 +3131,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  	struct list_head *rlist = &tracker->slave_list[slave].res_list[RES_MAC];  	int err;  	int qpn; +	struct res_qp *rqp;  	struct mlx4_net_trans_rule_hw_ctrl *ctrl;  	struct _rule_hw  *rule_header;  	int header_id; @@ -3131,7 +3142,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  	ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;  	qpn = be32_to_cpu(ctrl->qpn) & 0xffffff; -	err = get_res(dev, slave, qpn, RES_QP, NULL); +	err = get_res(dev, slave, qpn, RES_QP, &rqp);  	if (err) {  		pr_err("Steering rule with qpn 0x%x rejected.\n", qpn);  		return err; @@ -3172,14 +3183,16 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  	if (err)  		goto err_put; -	err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, 0); +	err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, qpn);  	if (err) {  		mlx4_err(dev, "Fail to add flow steering resources.\n ");  		/* detach rule*/  		mlx4_cmd(dev, vhcr->out_param, 0, 0,  			 MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,  			 MLX4_CMD_NATIVE); +		goto err_put;  	} +	atomic_inc(&rqp->ref_count);  err_put:  	put_res(dev, slave, qpn, RES_QP);  	return err; @@ -3192,20 +3205,35 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,  					 struct mlx4_cmd_info *cmd)  {  	int err; +	struct res_qp *rqp; +	struct res_fs_rule *rrule;  	if (dev->caps.steering_mode !=  	    MLX4_STEERING_MODE_DEVICE_MANAGED)  		return -EOPNOTSUPP; +	err = get_res(dev, slave, vhcr->in_param, RES_FS_RULE, &rrule); +	if (err) +		return err; +	/* Release the rule form busy state before removal */ +	put_res(dev, slave, vhcr->in_param, RES_FS_RULE); +	err = get_res(dev, slave, rrule->qpn, RES_QP, &rqp); +	if (err) +		return err; +  	err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0);  	if (err) {  		mlx4_err(dev, "Fail to remove flow steering resources.\n "); -		return err; +		goto out;  	}  	err = mlx4_cmd(dev, vhcr->in_param, 0, 0,  		       MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,  		       MLX4_CMD_NATIVE); +	if (!err) +		atomic_dec(&rqp->ref_count); +out: +	put_res(dev, slave, rrule->qpn, RES_QP);  	return err;  } @@ -3803,6 +3831,7 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave)  	mutex_lock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex);  	/*VLAN*/  	rem_slave_macs(dev, slave); +	rem_slave_fs_rule(dev, slave);  	rem_slave_qps(dev, slave);  	rem_slave_srqs(dev, slave);  	rem_slave_cqs(dev, slave); @@ -3811,6 +3840,5 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave)  	rem_slave_mtts(dev, slave);  	rem_slave_counters(dev, slave);  	rem_slave_xrcdns(dev, slave); -	rem_slave_fs_rule(dev, slave);  	mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex);  } diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index feda6c00829..e329fe1f11b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -149,7 +149,7 @@ void __mlx4_srq_free_icm(struct mlx4_dev *dev, int srqn)  static void mlx4_srq_free_icm(struct mlx4_dev *dev, int srqn)  { -	u64 in_param; +	u64 in_param = 0;  	if (mlx4_is_mfunc(dev)) {  		set_param_l(&in_param, srqn); diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index c4122c86f82..efa29b712d5 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1472,7 +1472,8 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)  	}  	platform_set_drvdata(pdev, ndev); -	if (lpc_mii_init(pldat) != 0) +	ret = lpc_mii_init(pldat); +	if (ret)  		goto err_out_unregister_netdev;  	netdev_info(ndev, "LPC mac at 0x%08x irq %d\n", diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 39ab4d09faa..73ce7dd6b95 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -1726,9 +1726,9 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,  			skb->protocol = eth_type_trans(skb, netdev);  			if (tcp_ip_status & PCH_GBE_RXD_ACC_STAT_TCPIPOK) -				skb->ip_summed = CHECKSUM_NONE; -			else  				skb->ip_summed = CHECKSUM_UNNECESSARY; +			else +				skb->ip_summed = CHECKSUM_NONE;  			napi_gro_receive(&adapter->napi, skb);  			(*work_done)++; diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 33e96176e4d..bf5e3cf97c4 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2220,6 +2220,7 @@ static void sh_eth_tsu_init(struct sh_eth_private *mdp)  /* MDIO bus release function */  static int sh_mdio_release(struct net_device *ndev)  { +	struct sh_eth_private *mdp = netdev_priv(ndev);  	struct mii_bus *bus = dev_get_drvdata(&ndev->dev);  	/* unregister mdio bus */ @@ -2234,6 +2235,9 @@ static int sh_mdio_release(struct net_device *ndev)  	/* free bitbang info */  	free_mdio_bitbang(bus); +	/* free bitbang memory */ +	kfree(mdp->bitbang); +  	return 0;  } @@ -2262,6 +2266,7 @@ static int sh_mdio_init(struct net_device *ndev, int id,  	bitbang->ctrl.ops = &bb_ops;  	/* MII controller setting */ +	mdp->bitbang = bitbang;  	mdp->mii_bus = alloc_mdio_bitbang(&bitbang->ctrl);  	if (!mdp->mii_bus) {  		ret = -ENOMEM; @@ -2441,6 +2446,11 @@ static int sh_eth_drv_probe(struct platform_device *pdev)  		}  		mdp->tsu_addr = ioremap(rtsu->start,  					resource_size(rtsu)); +		if (mdp->tsu_addr == NULL) { +			ret = -ENOMEM; +			dev_err(&pdev->dev, "TSU ioremap failed.\n"); +			goto out_release; +		}  		mdp->port = devno % 2;  		ndev->features = NETIF_F_HW_VLAN_FILTER;  	} diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index bae84fd2e73..e6655678458 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -705,6 +705,7 @@ struct sh_eth_private {  	const u16 *reg_offset;  	void __iomem *addr;  	void __iomem *tsu_addr; +	struct bb_info *bitbang;  	u32 num_rx_ring;  	u32 num_tx_ring;  	dma_addr_t rx_desc_dma; diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index 50247dfe8f5..d2f790df6dc 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -171,9 +171,9 @@ static inline void efx_device_detach_sync(struct efx_nic *efx)  	 * TX scheduler is stopped when we're done and before  	 * netif_device_present() becomes false.  	 */ -	netif_tx_lock(dev); +	netif_tx_lock_bh(dev);  	netif_device_detach(dev); -	netif_tx_unlock(dev); +	netif_tx_unlock_bh(dev);  }  #endif /* EFX_EFX_H */ diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 0ad790cc473..eaa8e874a3c 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -376,7 +376,8 @@ efx_may_push_tx_desc(struct efx_tx_queue *tx_queue, unsigned int write_count)  		return false;  	tx_queue->empty_read_count = 0; -	return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0; +	return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0 +		&& tx_queue->write_count - write_count == 1;  }  /* For each entry inserted into the software descriptor ring, create a diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 879ff5849bb..bb579a6128c 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -215,7 +215,7 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue)  		rx_buf = efx_rx_buffer(rx_queue, index);  		rx_buf->dma_addr = dma_addr + EFX_PAGE_IP_ALIGN;  		rx_buf->u.page = page; -		rx_buf->page_offset = page_offset; +		rx_buf->page_offset = page_offset + EFX_PAGE_IP_ALIGN;  		rx_buf->len = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN;  		rx_buf->flags = EFX_RX_BUF_PAGE;  		++rx_queue->added_count; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 01ffbc48698..df32a090d08 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -905,7 +905,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,  	/* If there is no more tx desc left free then we need to  	 * tell the kernel to stop sending us tx frames.  	 */ -	if (unlikely(cpdma_check_free_tx_desc(priv->txch))) +	if (unlikely(!cpdma_check_free_tx_desc(priv->txch)))  		netif_stop_queue(ndev);  	return NETDEV_TX_OK; @@ -1364,7 +1364,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,  		struct platform_device *mdio;  		parp = of_get_property(slave_node, "phy_id", &lenp); -		if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { +		if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {  			pr_err("Missing slave[%d] phy_id property\n", i);  			ret = -EINVAL;  			goto error_ret; diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 52c05366599..ae1b77aa199 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1102,7 +1102,7 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)  	/* If there is no more tx desc left free then we need to  	 * tell the kernel to stop sending us tx frames.  	 */ -	if (unlikely(cpdma_check_free_tx_desc(priv->txchan))) +	if (unlikely(!cpdma_check_free_tx_desc(priv->txchan)))  		netif_stop_queue(ndev);  	return NETDEV_TX_OK; diff --git a/drivers/net/hippi/rrunner.c b/drivers/net/hippi/rrunner.c index e5b19b05690..3c4d6274bb9 100644 --- a/drivers/net/hippi/rrunner.c +++ b/drivers/net/hippi/rrunner.c @@ -202,6 +202,9 @@ static int rr_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)  	return 0;   out: +	if (rrpriv->evt_ring) +		pci_free_consistent(pdev, EVT_RING_SIZE, rrpriv->evt_ring, +				    rrpriv->evt_ring_dma);  	if (rrpriv->rx_ring)  		pci_free_consistent(pdev, RX_TOTAL_SIZE, rrpriv->rx_ring,  				    rrpriv->rx_ring_dma); diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 417b2af1aa8..73abbc1655d 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -660,6 +660,7 @@ void macvlan_common_setup(struct net_device *dev)  	ether_setup(dev);  	dev->priv_flags	       &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); +	dev->priv_flags	       |= IFF_UNICAST_FLT;  	dev->netdev_ops		= &macvlan_netdev_ops;  	dev->destructor		= free_netdev;  	dev->header_ops		= &macvlan_hard_header_ops, diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 37add21a3d7..59ac143dec2 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -666,6 +666,7 @@ static int netconsole_netdev_event(struct notifier_block *this,  		goto done;  	spin_lock_irqsave(&target_list_lock, flags); +restart:  	list_for_each_entry(nt, &target_list, list) {  		netconsole_target_get(nt);  		if (nt->np.dev == dev) { @@ -678,15 +679,17 @@ static int netconsole_netdev_event(struct notifier_block *this,  			case NETDEV_UNREGISTER:  				/*  				 * rtnl_lock already held +				 * we might sleep in __netpoll_cleanup()  				 */ -				if (nt->np.dev) { -					__netpoll_cleanup(&nt->np); -					dev_put(nt->np.dev); -					nt->np.dev = NULL; -				} +				spin_unlock_irqrestore(&target_list_lock, flags); +				__netpoll_cleanup(&nt->np); +				spin_lock_irqsave(&target_list_lock, flags); +				dev_put(nt->np.dev); +				nt->np.dev = NULL;  				nt->enabled = 0;  				stopped = true; -				break; +				netconsole_target_put(nt); +				goto restart;  			}  		}  		netconsole_target_put(nt); diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 05c5efe8459..bf341929787 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1138,6 +1138,8 @@ static int team_port_del(struct team *team, struct net_device *port_dev)  	netdev_upper_dev_unlink(port_dev, dev);  	team_port_disable_netpoll(port);  	vlan_vids_del_by_dev(port_dev, dev); +	dev_uc_unsync(port_dev, dev); +	dev_mc_unsync(port_dev, dev);  	dev_close(port_dev);  	team_port_leave(team, port); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 2c6a22e278e..b7c457adc0d 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -747,6 +747,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)  		goto drop;  	skb_orphan(skb); +	nf_reset(skb); +  	/* Enqueue packet */  	skb_queue_tail(&tfile->socket.sk->sk_receive_queue, skb); diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 3b6e9b83342..7c769d8e25a 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -268,7 +268,7 @@ config USB_NET_SMSC75XX  	select CRC16  	select CRC32  	help -	  This option adds support for SMSC LAN95XX based USB 2.0 +	  This option adds support for SMSC LAN75XX based USB 2.0  	  Gigabit Ethernet adapters.  config USB_NET_SMSC95XX diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 248d2dc765a..16c84299729 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -68,18 +68,9 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)  	struct cdc_ncm_ctx *ctx;  	struct usb_driver *subdriver = ERR_PTR(-ENODEV);  	int ret = -ENODEV; -	u8 data_altsetting = CDC_NCM_DATA_ALTSETTING_NCM; +	u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf);  	struct cdc_mbim_state *info = (void *)&dev->data; -	/* see if interface supports MBIM alternate setting */ -	if (intf->num_altsetting == 2) { -		if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) -			usb_set_interface(dev->udev, -					  intf->cur_altsetting->desc.bInterfaceNumber, -					  CDC_NCM_COMM_ALTSETTING_MBIM); -		data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM; -	} -  	/* Probably NCM, defer for cdc_ncm_bind */  	if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))  		goto err; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 61b74a2b89a..4709fa3497c 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -55,6 +55,14 @@  #define	DRIVER_VERSION				"14-Mar-2012" +#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM) +static bool prefer_mbim = true; +#else +static bool prefer_mbim; +#endif +module_param(prefer_mbim, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(prefer_mbim, "Prefer MBIM setting on dual NCM/MBIM functions"); +  static void cdc_ncm_txpath_bh(unsigned long param);  static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);  static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); @@ -550,9 +558,12 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)  }  EXPORT_SYMBOL_GPL(cdc_ncm_unbind); -static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) +/* Select the MBIM altsetting iff it is preferred and available, + * returning the number of the corresponding data interface altsetting + */ +u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)  { -	int ret; +	struct usb_host_interface *alt;  	/* The MBIM spec defines a NCM compatible default altsetting,  	 * which we may have matched: @@ -568,23 +579,27 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)  	 *   endpoint descriptors, shall be constructed according to  	 *   the rules given in section 6 (USB Device Model) of this  	 *   specification." -	 * -	 * Do not bind to such interfaces, allowing cdc_mbim to handle -	 * them  	 */ -#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM) -	if ((intf->num_altsetting == 2) && -	    !usb_set_interface(dev->udev, -			       intf->cur_altsetting->desc.bInterfaceNumber, -			       CDC_NCM_COMM_ALTSETTING_MBIM)) { -		if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) -			return -ENODEV; -		else -			usb_set_interface(dev->udev, -					  intf->cur_altsetting->desc.bInterfaceNumber, -					  CDC_NCM_COMM_ALTSETTING_NCM); +	if (prefer_mbim && intf->num_altsetting == 2) { +		alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); +		if (alt && cdc_ncm_comm_intf_is_mbim(alt) && +		    !usb_set_interface(dev->udev, +				       intf->cur_altsetting->desc.bInterfaceNumber, +				       CDC_NCM_COMM_ALTSETTING_MBIM)) +			return CDC_NCM_DATA_ALTSETTING_MBIM;  	} -#endif +	return CDC_NCM_DATA_ALTSETTING_NCM; +} +EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); + +static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) +{ +	int ret; + +	/* MBIM backwards compatible function? */ +	cdc_ncm_select_altsetting(dev, intf); +	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) +		return -ENODEV;  	/* NCM data altsetting is always 1 */  	ret = cdc_ncm_bind_common(dev, intf, 1); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index efb5c7c33a2..968d5d50751 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -139,16 +139,9 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)  	BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); -	/* control and data is shared? */ -	if (intf->cur_altsetting->desc.bNumEndpoints == 3) { -		info->control = intf; -		info->data = intf; -		goto shared; -	} - -	/* else require a single interrupt status endpoint on control intf */ -	if (intf->cur_altsetting->desc.bNumEndpoints != 1) -		goto err; +	/* set up initial state */ +	info->control = intf; +	info->data = intf;  	/* and a number of CDC descriptors */  	while (len > 3) { @@ -207,25 +200,14 @@ next_desc:  		buf += h->bLength;  	} -	/* did we find all the required ones? */ -	if (!(found & (1 << USB_CDC_HEADER_TYPE)) || -	    !(found & (1 << USB_CDC_UNION_TYPE))) { -		dev_err(&intf->dev, "CDC functional descriptors missing\n"); -		goto err; -	} - -	/* verify CDC Union */ -	if (desc->bInterfaceNumber != cdc_union->bMasterInterface0) { -		dev_err(&intf->dev, "bogus CDC Union: master=%u\n", cdc_union->bMasterInterface0); -		goto err; -	} - -	/* need to save these for unbind */ -	info->control = intf; -	info->data = usb_ifnum_to_if(dev->udev,	cdc_union->bSlaveInterface0); -	if (!info->data) { -		dev_err(&intf->dev, "bogus CDC Union: slave=%u\n", cdc_union->bSlaveInterface0); -		goto err; +	/* Use separate control and data interfaces if we found a CDC Union */ +	if (cdc_union) { +		info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); +		if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) { +			dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n", +				cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); +			goto err; +		}  	}  	/* errors aren't fatal - we can live with the dynamic address */ @@ -235,11 +217,12 @@ next_desc:  	}  	/* claim data interface and set it up */ -	status = usb_driver_claim_interface(driver, info->data, dev); -	if (status < 0) -		goto err; +	if (info->control != info->data) { +		status = usb_driver_claim_interface(driver, info->data, dev); +		if (status < 0) +			goto err; +	} -shared:  	status = qmi_wwan_register_subdriver(dev);  	if (status < 0 && info->control != info->data) {  		usb_set_intfdata(info->data, NULL); diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 4aad350e4da..eae7a03d4f9 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -2958,6 +2958,7 @@ vmxnet3_probe_device(struct pci_dev *pdev,  	adapter->num_rx_queues = num_rx_queues;  	adapter->num_tx_queues = num_tx_queues; +	adapter->rx_buf_per_pkt = 1;  	size = sizeof(struct Vmxnet3_TxQueueDesc) * adapter->num_tx_queues;  	size += sizeof(struct Vmxnet3_RxQueueDesc) * adapter->num_rx_queues; diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index a0feb17a023..63a124340cb 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -472,6 +472,12 @@ vmxnet3_set_ringparam(struct net_device *netdev,  						VMXNET3_RX_RING_MAX_SIZE)  		return -EINVAL; +	/* if adapter not yet initialized, do nothing */ +	if (adapter->rx_buf_per_pkt == 0) { +		netdev_err(netdev, "adapter not completely initialized, " +			   "ring size cannot be changed yet\n"); +		return -EOPNOTSUPP; +	}  	/* round it up to a multiple of VMXNET3_RING_SIZE_ALIGN */  	new_tx_ring_size = (param->tx_pending + VMXNET3_RING_SIZE_MASK) & diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 3198384689d..35418146fa1 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -70,10 +70,10 @@  /*   * Version numbers   */ -#define VMXNET3_DRIVER_VERSION_STRING   "1.1.29.0-k" +#define VMXNET3_DRIVER_VERSION_STRING   "1.1.30.0-k"  /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ -#define VMXNET3_DRIVER_VERSION_NUM      0x01011D00 +#define VMXNET3_DRIVER_VERSION_NUM      0x01011E00  #if defined(CONFIG_PCI_MSI)  	/* RSS only makes sense if MSI-X is supported. */ diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index f10e58ac9c1..7cee7a3068e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -961,6 +961,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)  	iph->ttl	= ttl ? : ip4_dst_hoplimit(&rt->dst);  	tunnel_ip_select_ident(skb, old_iph, &rt->dst); +	nf_reset(skb); +  	vxlan_set_owner(dev, skb);  	/* See iptunnel_xmit() */ @@ -1504,6 +1506,14 @@ static __net_init int vxlan_init_net(struct net *net)  static __net_exit void vxlan_exit_net(struct net *net)  {  	struct vxlan_net *vn = net_generic(net, vxlan_net_id); +	struct vxlan_dev *vxlan; +	unsigned h; + +	rtnl_lock(); +	for (h = 0; h < VNI_HASH_SIZE; ++h) +		hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) +			dev_close(vxlan->dev); +	rtnl_unlock();  	if (vn->sock) {  		sk_release_kernel(vn->sock->sk); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 4cc13940c89..f76c3ca07a4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -1023,6 +1023,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,  					  AR_PHY_AGC_CONTROL_FLTR_CAL   |  					  AR_PHY_AGC_CONTROL_PKDET_CAL; +	/* Use chip chainmask only for calibration */  	ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);  	if (rtt) { @@ -1150,6 +1151,9 @@ skip_tx_iqcal:  		ar9003_hw_rtt_disable(ah);  	} +	/* Revert chainmask to runtime parameters */ +	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); +  	/* Initialize list pointers */  	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index ade3afb21f9..39c84ecf6a4 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -28,21 +28,21 @@ void ath_tx_complete_poll_work(struct work_struct *work)  	int i;  	bool needreset = false; -	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) -		if (ATH_TXQ_SETUP(sc, i)) { -			txq = &sc->tx.txq[i]; -			ath_txq_lock(sc, txq); -			if (txq->axq_depth) { -				if (txq->axq_tx_inprogress) { -					needreset = true; -					ath_txq_unlock(sc, txq); -					break; -				} else { -					txq->axq_tx_inprogress = true; -				} +	for (i = 0; i < IEEE80211_NUM_ACS; i++) { +		txq = sc->tx.txq_map[i]; + +		ath_txq_lock(sc, txq); +		if (txq->axq_depth) { +			if (txq->axq_tx_inprogress) { +				needreset = true; +				ath_txq_unlock(sc, txq); +				break; +			} else { +				txq->axq_tx_inprogress = true;  			} -			ath_txq_unlock_complete(sc, txq);  		} +		ath_txq_unlock_complete(sc, txq); +	}  	if (needreset) {  		ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index 3630a41df50..c353b5f19c8 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -475,6 +475,7 @@ il3945_tx_skb(struct il_priv *il,  	dma_addr_t txcmd_phys;  	int txq_id = skb_get_queue_mapping(skb);  	u16 len, idx, hdr_len; +	u16 firstlen, secondlen;  	u8 id;  	u8 unicast;  	u8 sta_id; @@ -589,21 +590,22 @@ il3945_tx_skb(struct il_priv *il,  	len =  	    sizeof(struct il3945_tx_cmd) + sizeof(struct il_cmd_header) +  	    hdr_len; -	len = (len + 3) & ~3; +	firstlen = (len + 3) & ~3;  	/* Physical address of this Tx command's header (not MAC header!),  	 * within command buffer array. */  	txcmd_phys = -	    pci_map_single(il->pci_dev, &out_cmd->hdr, len, PCI_DMA_TODEVICE); +	    pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen, +			   PCI_DMA_TODEVICE);  	if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys)))  		goto drop_unlock;  	/* Set up TFD's 2nd entry to point directly to remainder of skb,  	 * if any (802.11 null frames have no payload). */ -	len = skb->len - hdr_len; -	if (len) { +	secondlen = skb->len - hdr_len; +	if (secondlen > 0) {  		phys_addr = -		    pci_map_single(il->pci_dev, skb->data + hdr_len, len, +		    pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen,  				   PCI_DMA_TODEVICE);  		if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr)))  			goto drop_unlock; @@ -611,12 +613,12 @@ il3945_tx_skb(struct il_priv *il,  	/* Add buffer containing Tx command and MAC(!) header to TFD's  	 * first entry */ -	il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, len, 1, 0); +	il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0);  	dma_unmap_addr_set(out_meta, mapping, txcmd_phys); -	dma_unmap_len_set(out_meta, len, len); -	if (len) -		il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, len, 0, -					       U32_PAD(len)); +	dma_unmap_len_set(out_meta, len, firstlen); +	if (secondlen > 0) +		il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, 0, +					       U32_PAD(secondlen));  	if (!ieee80211_has_morefrags(hdr->frame_control)) {  		txq->need_update = 1; diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index 94ef33838bc..b775769f832 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -151,7 +151,7 @@ int iwl_send_add_sta(struct iwl_priv *priv,  		       sta_id, sta->sta.addr, flags & CMD_ASYNC ?  "a" : "");  	if (!(flags & CMD_ASYNC)) { -		cmd.flags |= CMD_WANT_SKB | CMD_WANT_HCMD; +		cmd.flags |= CMD_WANT_SKB;  		might_sleep();  	} diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 10f01793d7a..81aa91fab5a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -363,7 +363,7 @@ TRACE_EVENT(iwlwifi_dev_hcmd,  		__entry->flags = cmd->flags;  		memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr)); -		for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { +		for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {  			if (!cmd->len[i])  				continue;  			memcpy((u8 *)__get_dynamic_array(hcmd) + offset, diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 6f228bb2b84..fbfd2d13711 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1102,7 +1102,6 @@ void iwl_drv_stop(struct iwl_drv *drv)  /* shared module parameters */  struct iwl_mod_params iwlwifi_mod_params = { -	.amsdu_size_8K = 1,  	.restart_fw = 1,  	.plcp_check = true,  	.bt_coex_active = true, @@ -1207,7 +1206,7 @@ MODULE_PARM_DESC(11n_disable,  	"disable 11n functionality, bitmap: 1: full, 2: agg TX, 4: agg RX");  module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K,  		   int, S_IRUGO); -MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)");  module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, int, S_IRUGO);  MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index e5e3a79eae2..2c2a729092f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -91,7 +91,7 @@ enum iwl_power_level {   * @sw_crypto: using hardware encryption, default = 0   * @disable_11n: disable 11n capabilities, default = 0,   *	use IWL_DISABLE_HT_* constants - * @amsdu_size_8K: enable 8K amsdu size, default = 1 + * @amsdu_size_8K: enable 8K amsdu size, default = 0   * @restart_fw: restart firmware, default = 1   * @plcp_check: enable plcp health check, default = true   * @wd_disable: enable stuck queue check, default = 0 diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 8c7bec6b9a0..0cac2b7af78 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -186,19 +186,13 @@ struct iwl_rx_packet {   * @CMD_ASYNC: Return right away and don't want for the response   * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the   *	response. The caller needs to call iwl_free_resp when done. - * @CMD_WANT_HCMD: The caller needs to get the HCMD that was sent in the - *	response handler. Chunks flagged by %IWL_HCMD_DFL_NOCOPY won't be - *	copied. The pointer passed to the response handler is in the transport - *	ownership and don't need to be freed by the op_mode. This also means - *	that the pointer is invalidated after the op_mode's handler returns.   * @CMD_ON_DEMAND: This command is sent by the test mode pipe.   */  enum CMD_MODE {  	CMD_SYNC		= 0,  	CMD_ASYNC		= BIT(0),  	CMD_WANT_SKB		= BIT(1), -	CMD_WANT_HCMD		= BIT(2), -	CMD_ON_DEMAND		= BIT(3), +	CMD_ON_DEMAND		= BIT(2),  };  #define DEF_CMD_PAYLOAD_SIZE 320 @@ -217,7 +211,11 @@ struct iwl_device_cmd {  #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) -#define IWL_MAX_CMD_TFDS	2 +/* + * number of transfer buffers (fragments) per transmit frame descriptor; + * this is just the driver's idea, the hardware supports 20 + */ +#define IWL_MAX_CMD_TBS_PER_TFD	2  /**   * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command @@ -254,15 +252,15 @@ enum iwl_hcmd_dataflag {   * @id: id of the host command   */  struct iwl_host_cmd { -	const void *data[IWL_MAX_CMD_TFDS]; +	const void *data[IWL_MAX_CMD_TBS_PER_TFD];  	struct iwl_rx_packet *resp_pkt;  	unsigned long _rx_page_addr;  	u32 _rx_page_order;  	int handler_status;  	u32 flags; -	u16 len[IWL_MAX_CMD_TFDS]; -	u8 dataflags[IWL_MAX_CMD_TFDS]; +	u16 len[IWL_MAX_CMD_TBS_PER_TFD]; +	u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD];  	u8 id;  }; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 23eebda848b..2adb61f103f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -762,18 +762,20 @@ struct iwl_phy_context_cmd {  #define IWL_RX_INFO_PHY_CNT 8  #define IWL_RX_INFO_AGC_IDX 1  #define IWL_RX_INFO_RSSI_AB_IDX 2 -#define IWL_RX_INFO_RSSI_C_IDX 3 -#define IWL_OFDM_AGC_DB_MSK 0xfe00 -#define IWL_OFDM_AGC_DB_POS 9 +#define IWL_OFDM_AGC_A_MSK 0x0000007f +#define IWL_OFDM_AGC_A_POS 0 +#define IWL_OFDM_AGC_B_MSK 0x00003f80 +#define IWL_OFDM_AGC_B_POS 7 +#define IWL_OFDM_AGC_CODE_MSK 0x3fe00000 +#define IWL_OFDM_AGC_CODE_POS 20  #define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff -#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00  #define IWL_OFDM_RSSI_A_POS 0 +#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00 +#define IWL_OFDM_RSSI_ALLBAND_A_POS 8  #define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000 -#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000  #define IWL_OFDM_RSSI_B_POS 16 -#define IWL_OFDM_RSSI_INBAND_C_MSK 0x00ff -#define IWL_OFDM_RSSI_ALLBAND_C_MSK 0xff00 -#define IWL_OFDM_RSSI_C_POS 0 +#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000 +#define IWL_OFDM_RSSI_ALLBAND_B_POS 24  /**   * struct iwl_rx_phy_info - phy info diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index d3d959db03a..500f818dba0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -79,17 +79,8 @@  #define UCODE_VALID_OK	cpu_to_le32(0x1)  /* Default calibration values for WkP - set to INIT image w/o running */ -static const u8 wkp_calib_values_bb_filter[] = { 0xbf, 0x00, 0x5f, 0x00, 0x2f, -						 0x00, 0x18, 0x00 }; -static const u8 wkp_calib_values_rx_dc[] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, -					     0x7f, 0x7f, 0x7f }; -static const u8 wkp_calib_values_tx_lo[] = { 0x00, 0x00, 0x00, 0x00 }; -static const u8 wkp_calib_values_tx_iq[] = { 0xff, 0x00, 0xff, 0x00, 0x00, -					     0x00 }; -static const u8 wkp_calib_values_rx_iq[] = { 0xff, 0x00, 0x00, 0x00 };  static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };  static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 }; -static const u8 wkp_calib_values_xtal[] = { 0xd2, 0xd2 };  struct iwl_calib_default_data {  	u16 size; @@ -99,12 +90,7 @@ struct iwl_calib_default_data {  #define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}  static const struct iwl_calib_default_data wkp_calib_default_data[12] = { -	[5] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_dc), -	[6] = CALIB_SIZE_N_DATA(wkp_calib_values_bb_filter), -	[7] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_lo), -	[8] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq),  	[9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew), -	[10] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq),  	[11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),  }; @@ -241,20 +227,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,  	return 0;  } -#define IWL_HW_REV_ID_RAINBOW	0x2 -#define IWL_PROJ_TYPE_LHP	0x5 - -static u32 iwl_mvm_build_phy_cfg(struct iwl_mvm *mvm) -{ -	struct iwl_nvm_data *data = mvm->nvm_data; -	/* Temp calls to static definitions, will be changed to CSR calls */ -	u8 hw_rev_id = IWL_HW_REV_ID_RAINBOW; -	u8 project_type = IWL_PROJ_TYPE_LHP; - -	return data->radio_cfg_dash | (data->radio_cfg_step << 2) | -		(hw_rev_id << 4) | ((project_type & 0x7f) << 6) | -		(data->valid_tx_ant << 16) | (data->valid_rx_ant << 20); -}  static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)  { @@ -262,7 +234,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)  	enum iwl_ucode_type ucode_type = mvm->cur_ucode;  	/* Set parameters */ -	phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_build_phy_cfg(mvm)); +	phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config);  	phy_cfg_cmd.calib_control.event_trigger =  		mvm->fw->default_calib[ucode_type].event_trigger;  	phy_cfg_cmd.calib_control.flow_trigger = @@ -275,103 +247,6 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)  				    sizeof(phy_cfg_cmd), &phy_cfg_cmd);  } -/* Starting with the new PHY DB implementation - New calibs are enabled */ -/* Value - 0x405e7 */ -#define IWL_CALIB_DEFAULT_FLOW_INIT	(IWL_CALIB_CFG_XTAL_IDX		|\ -					 IWL_CALIB_CFG_TEMPERATURE_IDX	|\ -					 IWL_CALIB_CFG_VOLTAGE_READ_IDX	|\ -					 IWL_CALIB_CFG_DC_IDX		|\ -					 IWL_CALIB_CFG_BB_FILTER_IDX	|\ -					 IWL_CALIB_CFG_LO_LEAKAGE_IDX	|\ -					 IWL_CALIB_CFG_TX_IQ_IDX	|\ -					 IWL_CALIB_CFG_RX_IQ_IDX	|\ -					 IWL_CALIB_CFG_AGC_IDX) - -#define IWL_CALIB_DEFAULT_EVENT_INIT	0x0 - -/* Value 0x41567 */ -#define IWL_CALIB_DEFAULT_FLOW_RUN	(IWL_CALIB_CFG_XTAL_IDX		|\ -					 IWL_CALIB_CFG_TEMPERATURE_IDX	|\ -					 IWL_CALIB_CFG_VOLTAGE_READ_IDX	|\ -					 IWL_CALIB_CFG_BB_FILTER_IDX	|\ -					 IWL_CALIB_CFG_DC_IDX		|\ -					 IWL_CALIB_CFG_TX_IQ_IDX	|\ -					 IWL_CALIB_CFG_RX_IQ_IDX	|\ -					 IWL_CALIB_CFG_SENSITIVITY_IDX	|\ -					 IWL_CALIB_CFG_AGC_IDX) - -#define IWL_CALIB_DEFAULT_EVENT_RUN	(IWL_CALIB_CFG_XTAL_IDX		|\ -					 IWL_CALIB_CFG_TEMPERATURE_IDX	|\ -					 IWL_CALIB_CFG_VOLTAGE_READ_IDX	|\ -					 IWL_CALIB_CFG_TX_PWR_IDX	|\ -					 IWL_CALIB_CFG_DC_IDX		|\ -					 IWL_CALIB_CFG_TX_IQ_IDX	|\ -					 IWL_CALIB_CFG_SENSITIVITY_IDX) - -/* - * Sets the calibrations trigger values that will be sent to the FW for runtime - * and init calibrations. - * The ones given in the FW TLV are not correct. - */ -static void iwl_set_default_calib_trigger(struct iwl_mvm *mvm) -{ -	struct iwl_tlv_calib_ctrl default_calib; - -	/* -	 * WkP FW TLV calib bits are wrong, overwrite them. -	 * This defines the dynamic calibrations which are implemented in the -	 * uCode both for init(flow) calculation and event driven calibs. -	 */ - -	/* Init Image */ -	default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_INIT); -	default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_INIT); - -	if (default_calib.event_trigger != -	    mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger) -		IWL_ERR(mvm, -			"Updating the event calib for INIT image: 0x%x -> 0x%x\n", -			mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger, -			default_calib.event_trigger); -	if (default_calib.flow_trigger != -	    mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger) -		IWL_ERR(mvm, -			"Updating the flow calib for INIT image: 0x%x -> 0x%x\n", -			mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger, -			default_calib.flow_trigger); - -	memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_INIT], -	       &default_calib, sizeof(struct iwl_tlv_calib_ctrl)); -	IWL_ERR(mvm, -		"Setting uCode init calibrations event 0x%x, trigger 0x%x\n", -		default_calib.event_trigger, -		default_calib.flow_trigger); - -	/* Run time image */ -	default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_RUN); -	default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_RUN); - -	if (default_calib.event_trigger != -	    mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger) -		IWL_ERR(mvm, -			"Updating the event calib for RT image: 0x%x -> 0x%x\n", -			mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger, -			default_calib.event_trigger); -	if (default_calib.flow_trigger != -	    mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger) -		IWL_ERR(mvm, -			"Updating the flow calib for RT image: 0x%x -> 0x%x\n", -			mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger, -			default_calib.flow_trigger); - -	memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_REGULAR], -	       &default_calib, sizeof(struct iwl_tlv_calib_ctrl)); -	IWL_ERR(mvm, -		"Setting uCode runtime calibs event 0x%x, trigger 0x%x\n", -		default_calib.event_trigger, -		default_calib.flow_trigger); -} -  static int iwl_set_default_calibrations(struct iwl_mvm *mvm)  {  	u8 cmd_raw[16]; /* holds the variable size commands */ @@ -446,8 +321,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)  	ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);  	WARN_ON(ret); -	/* Override the calibrations from TLV and the const of fw */ -	iwl_set_default_calib_trigger(mvm); +	/* Send TX valid antennas before triggering calibrations */ +	ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant); +	if (ret) +		goto error;  	/* WkP doesn't have all calibrations, need to set default values */  	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 537711b1047..bdae700c769 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -80,7 +80,8 @@  #define IWL_INVALID_MAC80211_QUEUE	0xff  #define IWL_MVM_MAX_ADDRESSES		2 -#define IWL_RSSI_OFFSET 44 +/* RSSI offset for WkP */ +#define IWL_RSSI_OFFSET 50  enum iwl_mvm_tx_fifo {  	IWL_MVM_TX_FIFO_BK = 0, diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index aa59adf87db..d0f9c1e0475 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -624,12 +624,8 @@ static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)  	ieee80211_free_txskb(mvm->hw, skb);  } -static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) +static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)  { -	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - -	iwl_mvm_dump_nic_error_log(mvm); -  	iwl_abort_notification_waits(&mvm->notif_wait);  	/* @@ -663,9 +659,21 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)  	}  } +static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) +{ +	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + +	iwl_mvm_dump_nic_error_log(mvm); + +	iwl_mvm_nic_restart(mvm); +} +  static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)  { +	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); +  	WARN_ON(1); +	iwl_mvm_nic_restart(mvm);  }  static const struct iwl_op_mode_ops iwl_mvm_ops = { diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 3f40ab05bbd..b0b190d0ec2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -131,33 +131,42 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,  static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,  			     struct iwl_rx_phy_info *phy_info)  { -	u32 rssi_a, rssi_b, rssi_c, max_rssi, agc_db; +	int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; +	int rssi_all_band_a, rssi_all_band_b; +	u32 agc_a, agc_b, max_agc;  	u32 val; -	/* Find max rssi among 3 possible receivers. +	/* Find max rssi among 2 possible receivers.  	 * These values are measured by the Digital Signal Processor (DSP).  	 * They should stay fairly constant even as the signal strength varies,  	 * if the radio's Automatic Gain Control (AGC) is working right.  	 * AGC value (see below) will provide the "interesting" info.  	 */ +	val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]); +	agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS; +	agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS; +	max_agc = max_t(u32, agc_a, agc_b); +  	val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);  	rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;  	rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS; -	val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_C_IDX]); -	rssi_c = (val & IWL_OFDM_RSSI_INBAND_C_MSK) >> IWL_OFDM_RSSI_C_POS; - -	val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]); -	agc_db = (val & IWL_OFDM_AGC_DB_MSK) >> IWL_OFDM_AGC_DB_POS; +	rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >> +				IWL_OFDM_RSSI_ALLBAND_A_POS; +	rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >> +				IWL_OFDM_RSSI_ALLBAND_B_POS; -	max_rssi = max_t(u32, rssi_a, rssi_b); -	max_rssi = max_t(u32, max_rssi, rssi_c); +	/* +	 * dBm = rssi dB - agc dB - constant. +	 * Higher AGC (higher radio gain) means lower signal. +	 */ +	rssi_a_dbm = rssi_a - IWL_RSSI_OFFSET - agc_a; +	rssi_b_dbm = rssi_b - IWL_RSSI_OFFSET - agc_b; +	max_rssi_dbm = max_t(int, rssi_a_dbm, rssi_b_dbm); -	IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", -			rssi_a, rssi_b, rssi_c, max_rssi, agc_db); +	IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", +			rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); -	/* dBm = max_rssi dB - agc dB - constant. -	 * Higher AGC (higher radio gain) means lower signal. */ -	return max_rssi - agc_db - IWL_RSSI_OFFSET; +	return max_rssi_dbm;  }  /* diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 861a7f9f8e7..274f44e2ef6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -770,6 +770,16 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	u16 txq_id;  	int err; + +	/* +	 * If mac80211 is cleaning its state, then say that we finished since +	 * our state has been cleared anyway. +	 */ +	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { +		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); +		return 0; +	} +  	spin_lock_bh(&mvmsta->lock);  	txq_id = tid_data->txq_id; diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 6b67ce3f679..6645efe5c03 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -607,12 +607,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,  		/* Single frame failure in an AMPDU queue => send BAR */  		if (txq_id >= IWL_FIRST_AMPDU_QUEUE && -		    !(info->flags & IEEE80211_TX_STAT_ACK)) { -			/* there must be only one skb in the skb_list */ -			WARN_ON_ONCE(skb_freed > 1 || -				     !skb_queue_empty(&skbs)); +		    !(info->flags & IEEE80211_TX_STAT_ACK))  			info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; -		}  		/* W/A FW bug: seq_ctl is wrong when the queue is flushed */  		if (status == TX_STATUS_FAIL_FIFO_FLUSHED) { diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 3d62e805535..148843e7f34 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -137,10 +137,6 @@ static inline int iwl_queue_dec_wrap(int index, int n_bd)  struct iwl_cmd_meta {  	/* only for SYNC commands, iff the reply skb is wanted */  	struct iwl_host_cmd *source; - -	DEFINE_DMA_UNMAP_ADDR(mapping); -	DEFINE_DMA_UNMAP_LEN(len); -  	u32 flags;  }; @@ -185,25 +181,36 @@ struct iwl_queue {  /*   * The FH will write back to the first TB only, so we need   * to copy some data into the buffer regardless of whether - * it should be mapped or not. This indicates how much to - * copy, even for HCMDs it must be big enough to fit the - * DRAM scratch from the TX cmd, at least 16 bytes. + * it should be mapped or not. This indicates how big the + * first TB must be to include the scratch buffer. Since + * the scratch is 4 bytes at offset 12, it's 16 now. If we + * make it bigger then allocations will be bigger and copy + * slower, so that's probably not useful.   */ -#define IWL_HCMD_MIN_COPY_SIZE	16 +#define IWL_HCMD_SCRATCHBUF_SIZE	16  struct iwl_pcie_txq_entry {  	struct iwl_device_cmd *cmd; -	struct iwl_device_cmd *copy_cmd;  	struct sk_buff *skb;  	/* buffer to free after command completes */  	const void *free_buf;  	struct iwl_cmd_meta meta;  }; +struct iwl_pcie_txq_scratch_buf { +	struct iwl_cmd_header hdr; +	u8 buf[8]; +	__le32 scratch; +}; +  /**   * struct iwl_txq - Tx Queue for DMA   * @q: generic Rx/Tx queue descriptor   * @tfds: transmit frame descriptors (DMA memory) + * @scratchbufs: start of command headers, including scratch buffers, for + *	the writeback -- this is DMA memory and an array holding one buffer + *	for each command on the queue + * @scratchbufs_dma: DMA address for the scratchbufs start   * @entries: transmit entries (driver state)   * @lock: queue lock   * @stuck_timer: timer that fires if queue gets stuck @@ -217,6 +224,8 @@ struct iwl_pcie_txq_entry {  struct iwl_txq {  	struct iwl_queue q;  	struct iwl_tfd *tfds; +	struct iwl_pcie_txq_scratch_buf *scratchbufs; +	dma_addr_t scratchbufs_dma;  	struct iwl_pcie_txq_entry *entries;  	spinlock_t lock;  	struct timer_list stuck_timer; @@ -225,6 +234,13 @@ struct iwl_txq {  	u8 active;  }; +static inline dma_addr_t +iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) +{ +	return txq->scratchbufs_dma + +	       sizeof(struct iwl_pcie_txq_scratch_buf) * idx; +} +  /**   * struct iwl_trans_pcie - PCIe transport specific data   * @rxq: all the RX queue data diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index b0ae06d2456..567e67ad1f6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -637,22 +637,14 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,  		index = SEQ_TO_INDEX(sequence);  		cmd_index = get_cmd_index(&txq->q, index); -		if (reclaim) { -			struct iwl_pcie_txq_entry *ent; -			ent = &txq->entries[cmd_index]; -			cmd = ent->copy_cmd; -			WARN_ON_ONCE(!cmd && ent->meta.flags & CMD_WANT_HCMD); -		} else { +		if (reclaim) +			cmd = txq->entries[cmd_index].cmd; +		else  			cmd = NULL; -		}  		err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);  		if (reclaim) { -			/* The original command isn't needed any more */ -			kfree(txq->entries[cmd_index].copy_cmd); -			txq->entries[cmd_index].copy_cmd = NULL; -			/* nor is the duplicated part of the command */  			kfree(txq->entries[cmd_index].free_buf);  			txq->entries[cmd_index].free_buf = NULL;  		} diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8b625a7f568..8595c16f74d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -191,12 +191,9 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)  	}  	for (i = q->read_ptr; i != q->write_ptr; -	     i = iwl_queue_inc_wrap(i, q->n_bd)) { -		struct iwl_tx_cmd *tx_cmd = -			(struct iwl_tx_cmd *)txq->entries[i].cmd->payload; +	     i = iwl_queue_inc_wrap(i, q->n_bd))  		IWL_ERR(trans, "scratch %d = 0x%08x\n", i, -			get_unaligned_le32(&tx_cmd->scratch)); -	} +			le32_to_cpu(txq->scratchbufs[i].scratch));  	iwl_op_mode_nic_error(trans->op_mode);  } @@ -367,8 +364,8 @@ static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd)  }  static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, -			       struct iwl_cmd_meta *meta, struct iwl_tfd *tfd, -			       enum dma_data_direction dma_dir) +			       struct iwl_cmd_meta *meta, +			       struct iwl_tfd *tfd)  {  	int i;  	int num_tbs; @@ -382,17 +379,12 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,  		return;  	} -	/* Unmap tx_cmd */ -	if (num_tbs) -		dma_unmap_single(trans->dev, -				dma_unmap_addr(meta, mapping), -				dma_unmap_len(meta, len), -				DMA_BIDIRECTIONAL); +	/* first TB is never freed - it's the scratchbuf data */ -	/* Unmap chunks, if any. */  	for (i = 1; i < num_tbs; i++)  		dma_unmap_single(trans->dev, iwl_pcie_tfd_tb_get_addr(tfd, i), -				 iwl_pcie_tfd_tb_get_len(tfd, i), dma_dir); +				 iwl_pcie_tfd_tb_get_len(tfd, i), +				 DMA_TO_DEVICE);  	tfd->num_tbs = 0;  } @@ -406,8 +398,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,   * Does NOT advance any TFD circular buffer read/write indexes   * Does NOT free the TFD itself (which is within circular buffer)   */ -static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq, -				  enum dma_data_direction dma_dir) +static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)  {  	struct iwl_tfd *tfd_tmp = txq->tfds; @@ -418,8 +409,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq,  	lockdep_assert_held(&txq->lock);  	/* We have only q->n_window txq->entries, but we use q->n_bd tfds */ -	iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], -			   dma_dir); +	iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);  	/* free SKB */  	if (txq->entries) { @@ -479,6 +469,7 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans,  {  	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);  	size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; +	size_t scratchbuf_sz;  	int i;  	if (WARN_ON(txq->entries || txq->tfds)) @@ -514,9 +505,25 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans,  		IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz);  		goto error;  	} + +	BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs)); +	BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) != +			sizeof(struct iwl_cmd_header) + +			offsetof(struct iwl_tx_cmd, scratch)); + +	scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num; + +	txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz, +					      &txq->scratchbufs_dma, +					      GFP_KERNEL); +	if (!txq->scratchbufs) +		goto err_free_tfds; +  	txq->q.id = txq_id;  	return 0; +err_free_tfds: +	dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->q.dma_addr);  error:  	if (txq->entries && txq_id == trans_pcie->cmd_queue)  		for (i = 0; i < slots_num; i++) @@ -565,22 +572,13 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)  	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);  	struct iwl_txq *txq = &trans_pcie->txq[txq_id];  	struct iwl_queue *q = &txq->q; -	enum dma_data_direction dma_dir;  	if (!q->n_bd)  		return; -	/* In the command queue, all the TBs are mapped as BIDI -	 * so unmap them as such. -	 */ -	if (txq_id == trans_pcie->cmd_queue) -		dma_dir = DMA_BIDIRECTIONAL; -	else -		dma_dir = DMA_TO_DEVICE; -  	spin_lock_bh(&txq->lock);  	while (q->write_ptr != q->read_ptr) { -		iwl_pcie_txq_free_tfd(trans, txq, dma_dir); +		iwl_pcie_txq_free_tfd(trans, txq);  		q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);  	}  	spin_unlock_bh(&txq->lock); @@ -610,7 +608,6 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)  	if (txq_id == trans_pcie->cmd_queue)  		for (i = 0; i < txq->q.n_window; i++) {  			kfree(txq->entries[i].cmd); -			kfree(txq->entries[i].copy_cmd);  			kfree(txq->entries[i].free_buf);  		} @@ -619,6 +616,10 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)  		dma_free_coherent(dev, sizeof(struct iwl_tfd) *  				  txq->q.n_bd, txq->tfds, txq->q.dma_addr);  		txq->q.dma_addr = 0; + +		dma_free_coherent(dev, +				  sizeof(*txq->scratchbufs) * txq->q.n_window, +				  txq->scratchbufs, txq->scratchbufs_dma);  	}  	kfree(txq->entries); @@ -962,7 +963,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,  		iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq); -		iwl_pcie_txq_free_tfd(trans, txq, DMA_TO_DEVICE); +		iwl_pcie_txq_free_tfd(trans, txq);  	}  	iwl_pcie_txq_progress(trans_pcie, txq); @@ -1152,29 +1153,29 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,  	void *dup_buf = NULL;  	dma_addr_t phys_addr;  	int idx; -	u16 copy_size, cmd_size, dma_size; +	u16 copy_size, cmd_size, scratch_size;  	bool had_nocopy = false;  	int i;  	u32 cmd_pos; -	const u8 *cmddata[IWL_MAX_CMD_TFDS]; -	u16 cmdlen[IWL_MAX_CMD_TFDS]; +	const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; +	u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];  	copy_size = sizeof(out_cmd->hdr);  	cmd_size = sizeof(out_cmd->hdr);  	/* need one for the header if the first is NOCOPY */ -	BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); +	BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1); -	for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { +	for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {  		cmddata[i] = cmd->data[i];  		cmdlen[i] = cmd->len[i];  		if (!cmd->len[i])  			continue; -		/* need at least IWL_HCMD_MIN_COPY_SIZE copied */ -		if (copy_size < IWL_HCMD_MIN_COPY_SIZE) { -			int copy = IWL_HCMD_MIN_COPY_SIZE - copy_size; +		/* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ +		if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { +			int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;  			if (copy > cmdlen[i])  				copy = cmdlen[i]; @@ -1260,15 +1261,15 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,  	/* and copy the data that needs to be copied */  	cmd_pos = offsetof(struct iwl_device_cmd, payload);  	copy_size = sizeof(out_cmd->hdr); -	for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { +	for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {  		int copy = 0;  		if (!cmd->len)  			continue; -		/* need at least IWL_HCMD_MIN_COPY_SIZE copied */ -		if (copy_size < IWL_HCMD_MIN_COPY_SIZE) { -			copy = IWL_HCMD_MIN_COPY_SIZE - copy_size; +		/* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ +		if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { +			copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;  			if (copy > cmd->len[i])  				copy = cmd->len[i]; @@ -1286,50 +1287,38 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,  		}  	} -	WARN_ON_ONCE(txq->entries[idx].copy_cmd); - -	/* -	 * since out_cmd will be the source address of the FH, it will write -	 * the retry count there. So when the user needs to receivce the HCMD -	 * that corresponds to the response in the response handler, it needs -	 * to set CMD_WANT_HCMD. -	 */ -	if (cmd->flags & CMD_WANT_HCMD) { -		txq->entries[idx].copy_cmd = -			kmemdup(out_cmd, cmd_pos, GFP_ATOMIC); -		if (unlikely(!txq->entries[idx].copy_cmd)) { -			idx = -ENOMEM; -			goto out; -		} -	} -  	IWL_DEBUG_HC(trans,  		     "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n",  		     get_cmd_string(trans_pcie, out_cmd->hdr.cmd),  		     out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence),  		     cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); -	/* -	 * If the entire command is smaller than IWL_HCMD_MIN_COPY_SIZE, we must -	 * still map at least that many bytes for the hardware to write back to. -	 * We have enough space, so that's not a problem. -	 */ -	dma_size = max_t(u16, copy_size, IWL_HCMD_MIN_COPY_SIZE); - -	phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, dma_size, -				   DMA_BIDIRECTIONAL); -	if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { -		idx = -ENOMEM; -		goto out; -	} +	/* start the TFD with the scratchbuf */ +	scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); +	memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); +	iwl_pcie_txq_build_tfd(trans, txq, +			       iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), +			       scratch_size, 1); -	dma_unmap_addr_set(out_meta, mapping, phys_addr); -	dma_unmap_len_set(out_meta, len, dma_size); +	/* map first command fragment, if any remains */ +	if (copy_size > scratch_size) { +		phys_addr = dma_map_single(trans->dev, +					   ((u8 *)&out_cmd->hdr) + scratch_size, +					   copy_size - scratch_size, +					   DMA_TO_DEVICE); +		if (dma_mapping_error(trans->dev, phys_addr)) { +			iwl_pcie_tfd_unmap(trans, out_meta, +					   &txq->tfds[q->write_ptr]); +			idx = -ENOMEM; +			goto out; +		} -	iwl_pcie_txq_build_tfd(trans, txq, phys_addr, copy_size, 1); +		iwl_pcie_txq_build_tfd(trans, txq, phys_addr, +				       copy_size - scratch_size, 0); +	}  	/* map the remaining (adjusted) nocopy/dup fragments */ -	for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { +	for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {  		const void *data = cmddata[i];  		if (!cmdlen[i]) @@ -1340,11 +1329,10 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,  		if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP)  			data = dup_buf;  		phys_addr = dma_map_single(trans->dev, (void *)data, -					   cmdlen[i], DMA_BIDIRECTIONAL); +					   cmdlen[i], DMA_TO_DEVICE);  		if (dma_mapping_error(trans->dev, phys_addr)) {  			iwl_pcie_tfd_unmap(trans, out_meta, -					   &txq->tfds[q->write_ptr], -					   DMA_BIDIRECTIONAL); +					   &txq->tfds[q->write_ptr]);  			idx = -ENOMEM;  			goto out;  		} @@ -1418,7 +1406,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,  	cmd = txq->entries[cmd_index].cmd;  	meta = &txq->entries[cmd_index].meta; -	iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); +	iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]);  	/* Input error checking is done when commands are added to queue. */  	if (meta->flags & CMD_WANT_SKB) { @@ -1597,10 +1585,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,  	struct iwl_cmd_meta *out_meta;  	struct iwl_txq *txq;  	struct iwl_queue *q; -	dma_addr_t phys_addr = 0; -	dma_addr_t txcmd_phys; -	dma_addr_t scratch_phys; -	u16 len, firstlen, secondlen; +	dma_addr_t tb0_phys, tb1_phys, scratch_phys; +	void *tb1_addr; +	u16 len, tb1_len, tb2_len;  	u8 wait_write_ptr = 0;  	__le16 fc = hdr->frame_control;  	u8 hdr_len = ieee80211_hdrlen(fc); @@ -1638,85 +1625,80 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,  		cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) |  			    INDEX_TO_SEQ(q->write_ptr))); +	tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr); +	scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + +		       offsetof(struct iwl_tx_cmd, scratch); + +	tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); +	tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); +  	/* Set up first empty entry in queue's array of Tx/cmd buffers */  	out_meta = &txq->entries[q->write_ptr].meta;  	/* -	 * Use the first empty entry in this queue's command buffer array -	 * to contain the Tx command and MAC header concatenated together -	 * (payload data will be in another buffer). -	 * Size of this varies, due to varying MAC header length. -	 * If end is not dword aligned, we'll have 2 extra bytes at the end -	 * of the MAC header (device reads on dword boundaries). -	 * We'll tell device about this padding later. +	 * The second TB (tb1) points to the remainder of the TX command +	 * and the 802.11 header - dword aligned size +	 * (This calculation modifies the TX command, so do it before the +	 * setup of the first TB)  	 */ -	len = sizeof(struct iwl_tx_cmd) + -		sizeof(struct iwl_cmd_header) + hdr_len; -	firstlen = (len + 3) & ~3; +	len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + +	      hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; +	tb1_len = (len + 3) & ~3;  	/* Tell NIC about any 2-byte padding after MAC header */ -	if (firstlen != len) +	if (tb1_len != len)  		tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; -	/* Physical address of this Tx command's header (not MAC header!), -	 * within command buffer array. */ -	txcmd_phys = dma_map_single(trans->dev, -				    &dev_cmd->hdr, firstlen, -				    DMA_BIDIRECTIONAL); -	if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) -		goto out_err; -	dma_unmap_addr_set(out_meta, mapping, txcmd_phys); -	dma_unmap_len_set(out_meta, len, firstlen); +	/* The first TB points to the scratchbuf data - min_copy bytes */ +	memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, +	       IWL_HCMD_SCRATCHBUF_SIZE); +	iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, +			       IWL_HCMD_SCRATCHBUF_SIZE, 1); -	if (!ieee80211_has_morefrags(fc)) { -		txq->need_update = 1; -	} else { -		wait_write_ptr = 1; -		txq->need_update = 0; -	} +	/* there must be data left over for TB1 or this code must be changed */ +	BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); -	/* Set up TFD's 2nd entry to point directly to remainder of skb, -	 * if any (802.11 null frames have no payload). */ -	secondlen = skb->len - hdr_len; -	if (secondlen > 0) { -		phys_addr = dma_map_single(trans->dev, skb->data + hdr_len, -					   secondlen, DMA_TO_DEVICE); -		if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { -			dma_unmap_single(trans->dev, -					 dma_unmap_addr(out_meta, mapping), -					 dma_unmap_len(out_meta, len), -					 DMA_BIDIRECTIONAL); +	/* map the data for TB1 */ +	tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE; +	tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); +	if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) +		goto out_err; +	iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0); + +	/* +	 * Set up TFD's third entry to point directly to remainder +	 * of skb, if any (802.11 null frames have no payload). +	 */ +	tb2_len = skb->len - hdr_len; +	if (tb2_len > 0) { +		dma_addr_t tb2_phys = dma_map_single(trans->dev, +						     skb->data + hdr_len, +						     tb2_len, DMA_TO_DEVICE); +		if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { +			iwl_pcie_tfd_unmap(trans, out_meta, +					   &txq->tfds[q->write_ptr]);  			goto out_err;  		} +		iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0);  	} -	/* Attach buffers to TFD */ -	iwl_pcie_txq_build_tfd(trans, txq, txcmd_phys, firstlen, 1); -	if (secondlen > 0) -		iwl_pcie_txq_build_tfd(trans, txq, phys_addr, secondlen, 0); - -	scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + -				offsetof(struct iwl_tx_cmd, scratch); - -	/* take back ownership of DMA buffer to enable update */ -	dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, -				DMA_BIDIRECTIONAL); -	tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); -	tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); -  	/* Set up entry for this TFD in Tx byte-count array */  	iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); -	dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, -				   DMA_BIDIRECTIONAL); -  	trace_iwlwifi_dev_tx(trans->dev, skb,  			     &txq->tfds[txq->q.write_ptr],  			     sizeof(struct iwl_tfd), -			     &dev_cmd->hdr, firstlen, -			     skb->data + hdr_len, secondlen); +			     &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, +			     skb->data + hdr_len, tb2_len);  	trace_iwlwifi_dev_tx_data(trans->dev, skb, -				  skb->data + hdr_len, secondlen); +				  skb->data + hdr_len, tb2_len); + +	if (!ieee80211_has_morefrags(fc)) { +		txq->need_update = 1; +	} else { +		wait_write_ptr = 1; +		txq->need_update = 0; +	}  	/* start timer if queue currently empty */  	if (txq->need_update && q->read_ptr == q->write_ptr && diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 20a6c555587..b5c8b962ce1 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -157,6 +157,20 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,  		return -1;  	} +	cmd_code = le16_to_cpu(host_cmd->command); +	cmd_size = le16_to_cpu(host_cmd->size); + +	if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET && +	    cmd_code != HostCmd_CMD_FUNC_SHUTDOWN && +	    cmd_code != HostCmd_CMD_FUNC_INIT) { +		dev_err(adapter->dev, +			"DNLD_CMD: FW in reset state, ignore cmd %#x\n", +			cmd_code); +		mwifiex_complete_cmd(adapter, cmd_node); +		mwifiex_insert_cmd_to_free_q(adapter, cmd_node); +		return -1; +	} +  	/* Set command sequence number */  	adapter->seq_num++;  	host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO @@ -168,9 +182,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,  	adapter->curr_cmd = cmd_node;  	spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); -	cmd_code = le16_to_cpu(host_cmd->command); -	cmd_size = le16_to_cpu(host_cmd->size); -  	/* Adjust skb length */  	if (cmd_node->cmd_skb->len > cmd_size)  		/* @@ -484,8 +495,6 @@ int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no,  	ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid,  				     data_buf); -	if (!ret) -		ret = mwifiex_wait_queue_complete(adapter);  	return ret;  } @@ -588,9 +597,10 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,  	if (cmd_no == HostCmd_CMD_802_11_SCAN) {  		mwifiex_queue_scan_cmd(priv, cmd_node);  	} else { -		adapter->cmd_queued = cmd_node;  		mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);  		queue_work(adapter->workqueue, &adapter->main_work); +		if (cmd_node->wait_q_enabled) +			ret = mwifiex_wait_queue_complete(adapter, cmd_node);  	}  	return ret; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index e38aa9b3663..0ff4c37ab42 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -709,6 +709,14 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)  		return ret;  	} +	/* cancel current command */ +	if (adapter->curr_cmd) { +		dev_warn(adapter->dev, "curr_cmd is still in processing\n"); +		del_timer(&adapter->cmd_timer); +		mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); +		adapter->curr_cmd = NULL; +	} +  	/* shut down mwifiex */  	dev_dbg(adapter->dev, "info: shutdown mwifiex...\n"); diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 246aa62a481..2fe0ceba440 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -1117,10 +1117,9 @@ mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,  		adhoc_join->bss_descriptor.bssid,  		adhoc_join->bss_descriptor.ssid); -	for (i = 0; bss_desc->supported_rates[i] && -			i < MWIFIEX_SUPPORTED_RATES; -			i++) -			; +	for (i = 0; i < MWIFIEX_SUPPORTED_RATES && +		    bss_desc->supported_rates[i]; i++) +		;  	rates_size = i;  	/* Copy Data Rates from the Rates recorded in scan response */ diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 553adfb0aa8..7035ade9af7 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -723,7 +723,6 @@ struct mwifiex_adapter {  	u16 cmd_wait_q_required;  	struct mwifiex_wait_queue cmd_wait_q;  	u8 scan_wait_q_woken; -	struct cmd_ctrl_node *cmd_queued;  	spinlock_t queue_lock;		/* lock for tx queues */  	struct completion fw_load;  	u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; @@ -1018,7 +1017,8 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,  			struct mwifiex_multicast_list *mcast_list);  int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,  			    struct net_device *dev); -int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter); +int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, +				struct cmd_ctrl_node *cmd_queued);  int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,  		      struct cfg80211_ssid *req_ssid);  int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index bb60c2754a9..d215b4d3c51 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1388,10 +1388,13 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,  			list_del(&cmd_node->list);  			spin_unlock_irqrestore(&adapter->scan_pending_q_lock,  					       flags); -			adapter->cmd_queued = cmd_node;  			mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,  							true);  			queue_work(adapter->workqueue, &adapter->main_work); + +			/* Perform internal scan synchronously */ +			if (!priv->scan_request) +				mwifiex_wait_queue_complete(adapter, cmd_node);  		} else {  			spin_unlock_irqrestore(&adapter->scan_pending_q_lock,  					       flags); @@ -1946,9 +1949,6 @@ int mwifiex_request_scan(struct mwifiex_private *priv,  		/* Normal scan */  		ret = mwifiex_scan_networks(priv, NULL); -	if (!ret) -		ret = mwifiex_wait_queue_complete(priv->adapter); -  	up(&priv->async_sem);  	return ret; diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 9f33c92c90f..13100f8de3d 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -54,16 +54,10 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,   * This function waits on a cmd wait queue. It also cancels the pending   * request after waking up, in case of errors.   */ -int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) +int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, +				struct cmd_ctrl_node *cmd_queued)  {  	int status; -	struct cmd_ctrl_node *cmd_queued; - -	if (!adapter->cmd_queued) -		return 0; - -	cmd_queued = adapter->cmd_queued; -	adapter->cmd_queued = NULL;  	dev_dbg(adapter->dev, "cmd pending\n");  	atomic_inc(&adapter->cmd_pending); diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 44d6ead4334..2bf4efa3318 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -55,10 +55,10 @@ config RT61PCI  config RT2800PCI  	tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" -	depends on PCI || RALINK_RT288X || RALINK_RT305X +	depends on PCI || SOC_RT288X || SOC_RT305X  	select RT2800_LIB  	select RT2X00_LIB_PCI if PCI -	select RT2X00_LIB_SOC if RALINK_RT288X || RALINK_RT305X +	select RT2X00_LIB_SOC if SOC_RT288X || SOC_RT305X  	select RT2X00_LIB_FIRMWARE  	select RT2X00_LIB_CRYPTO  	select CRC_CCITT diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 48a01aa21f1..ded73da4de0 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -89,7 +89,7 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token)  	rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);  } -#if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) +#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)  static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)  {  	void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); @@ -107,7 +107,7 @@ static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)  {  	return -ENOMEM;  } -#endif /* CONFIG_RALINK_RT288X || CONFIG_RALINK_RT305X */ +#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */  #ifdef CONFIG_PCI  static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) @@ -1177,7 +1177,7 @@ MODULE_DEVICE_TABLE(pci, rt2800pci_device_table);  #endif /* CONFIG_PCI */  MODULE_LICENSE("GPL"); -#if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) +#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)  static int rt2800soc_probe(struct platform_device *pdev)  {  	return rt2x00soc_probe(pdev, &rt2800pci_ops); @@ -1194,7 +1194,7 @@ static struct platform_driver rt2800soc_driver = {  	.suspend	= rt2x00soc_suspend,  	.resume		= rt2x00soc_resume,  }; -#endif /* CONFIG_RALINK_RT288X || CONFIG_RALINK_RT305X */ +#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */  #ifdef CONFIG_PCI  static int rt2800pci_probe(struct pci_dev *pci_dev, @@ -1217,7 +1217,7 @@ static int __init rt2800pci_init(void)  {  	int ret = 0; -#if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) +#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)  	ret = platform_driver_register(&rt2800soc_driver);  	if (ret)  		return ret; @@ -1225,7 +1225,7 @@ static int __init rt2800pci_init(void)  #ifdef CONFIG_PCI  	ret = pci_register_driver(&rt2800pci_driver);  	if (ret) { -#if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) +#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)  		platform_driver_unregister(&rt2800soc_driver);  #endif  		return ret; @@ -1240,7 +1240,7 @@ static void __exit rt2800pci_exit(void)  #ifdef CONFIG_PCI  	pci_unregister_driver(&rt2800pci_driver);  #endif -#if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) +#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X)  	platform_driver_unregister(&rt2800soc_driver);  #endif  } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index b1ccff474c7..c08d0f4c5f3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -1377,74 +1377,57 @@ void rtl92cu_card_disable(struct ieee80211_hw *hw)  void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid)  { -	/* dummy routine needed for callback from rtl_op_configure_filter() */ -} - -/*========================================================================== */ - -static void _rtl92cu_set_check_bssid(struct ieee80211_hw *hw, -			      enum nl80211_iftype type) -{  	struct rtl_priv *rtlpriv = rtl_priv(hw); -	u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR);  	struct rtl_hal *rtlhal = rtl_hal(rtlpriv); -	struct rtl_phy *rtlphy = &(rtlpriv->phy); -	u8 filterout_non_associated_bssid = false; +	u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR); -	switch (type) { -	case NL80211_IFTYPE_ADHOC: -	case NL80211_IFTYPE_STATION: -		filterout_non_associated_bssid = true; -		break; -	case NL80211_IFTYPE_UNSPECIFIED: -	case NL80211_IFTYPE_AP: -	default: -		break; -	} -	if (filterout_non_associated_bssid) { +	if (rtlpriv->psc.rfpwr_state != ERFON) +		return; + +	if (check_bssid) { +		u8 tmp;  		if (IS_NORMAL_CHIP(rtlhal->version)) { -			switch (rtlphy->current_io_type) { -			case IO_CMD_RESUME_DM_BY_SCAN: -				reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); -				rtlpriv->cfg->ops->set_hw_reg(hw, -						 HW_VAR_RCR, (u8 *)(®_rcr)); -				/* enable update TSF */ -				_rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4)); -				break; -			case IO_CMD_PAUSE_DM_BY_SCAN: -				reg_rcr &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN); -				rtlpriv->cfg->ops->set_hw_reg(hw, -						 HW_VAR_RCR, (u8 *)(®_rcr)); -				/* disable update TSF */ -				_rtl92cu_set_bcn_ctrl_reg(hw, BIT(4), 0); -				break; -			} +			reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); +			tmp = BIT(4);  		} else { -			reg_rcr |= (RCR_CBSSID); -			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, -						      (u8 *)(®_rcr)); -			_rtl92cu_set_bcn_ctrl_reg(hw, 0, (BIT(4)|BIT(5))); +			reg_rcr |= RCR_CBSSID; +			tmp = BIT(4) | BIT(5);  		} -	} else if (filterout_non_associated_bssid == false) { +		rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, +					      (u8 *) (®_rcr)); +		_rtl92cu_set_bcn_ctrl_reg(hw, 0, tmp); +	} else { +		u8 tmp;  		if (IS_NORMAL_CHIP(rtlhal->version)) { -			reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); -			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, -						      (u8 *)(®_rcr)); -			_rtl92cu_set_bcn_ctrl_reg(hw, BIT(4), 0); +			reg_rcr &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN); +			tmp = BIT(4);  		} else { -			reg_rcr &= (~RCR_CBSSID); -			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, -						      (u8 *)(®_rcr)); -			_rtl92cu_set_bcn_ctrl_reg(hw, (BIT(4)|BIT(5)), 0); +			reg_rcr &= ~RCR_CBSSID; +			tmp = BIT(4) | BIT(5);  		} +		reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); +		rtlpriv->cfg->ops->set_hw_reg(hw, +					      HW_VAR_RCR, (u8 *) (®_rcr)); +		_rtl92cu_set_bcn_ctrl_reg(hw, tmp, 0);  	}  } +/*========================================================================== */ +  int rtl92cu_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type)  { +	struct rtl_priv *rtlpriv = rtl_priv(hw); +  	if (_rtl92cu_set_media_status(hw, type))  		return -EOPNOTSUPP; -	_rtl92cu_set_check_bssid(hw, type); + +	if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { +		if (type != NL80211_IFTYPE_AP) +			rtl92cu_set_check_bssid(hw, true); +	} else { +		rtl92cu_set_check_bssid(hw, false); +	} +  	return 0;  } @@ -2058,8 +2041,6 @@ void rtl92cu_update_hal_rate_table(struct ieee80211_hw *hw,  			       (shortgi_rate << 4) | (shortgi_rate);  	}  	rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value); -	RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, "%x\n", -		 rtl_read_dword(rtlpriv, REG_ARFR0));  }  void rtl92cu_update_hal_rate_mask(struct ieee80211_hw *hw, u8 rssi_level) diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 156b52732f3..5847d6d0881 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -851,6 +851,7 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,  	if (unlikely(!_urb)) {  		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,  			 "Can't allocate urb. Drop skb!\n"); +		kfree_skb(skb);  		return;  	}  	_rtl_submit_tx_urb(hw, _urb); diff --git a/drivers/of/base.c b/drivers/of/base.c index 321d3ef0500..c6443de58fb 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -746,6 +746,64 @@ struct device_node *of_find_node_by_phandle(phandle handle)  EXPORT_SYMBOL(of_find_node_by_phandle);  /** + * of_find_property_value_of_size + * + * @np:		device node from which the property value is to be read. + * @propname:	name of the property to be searched. + * @len:	requested length of property value + * + * Search for a property in a device node and valid the requested size. + * Returns the property value on success, -EINVAL if the property does not + *  exist, -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + */ +static void *of_find_property_value_of_size(const struct device_node *np, +			const char *propname, u32 len) +{ +	struct property *prop = of_find_property(np, propname, NULL); + +	if (!prop) +		return ERR_PTR(-EINVAL); +	if (!prop->value) +		return ERR_PTR(-ENODATA); +	if (len > prop->length) +		return ERR_PTR(-EOVERFLOW); + +	return prop->value; +} + +/** + * of_property_read_u32_index - Find and read a u32 from a multi-value property. + * + * @np:		device node from which the property value is to be read. + * @propname:	name of the property to be searched. + * @index:	index of the u32 in the list of values + * @out_value:	pointer to return value, modified only if no error. + * + * Search for a property in a device node and read nth 32-bit value from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u32 value can be decoded. + */ +int of_property_read_u32_index(const struct device_node *np, +				       const char *propname, +				       u32 index, u32 *out_value) +{ +	const u32 *val = of_find_property_value_of_size(np, propname, +					((index + 1) * sizeof(*out_value))); + +	if (IS_ERR(val)) +		return PTR_ERR(val); + +	*out_value = be32_to_cpup(((__be32 *)val) + index); +	return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u32_index); + +/**   * of_property_read_u8_array - Find and read an array of u8 from a property.   *   * @np:		device node from which the property value is to be read. @@ -766,17 +824,12 @@ EXPORT_SYMBOL(of_find_node_by_phandle);  int of_property_read_u8_array(const struct device_node *np,  			const char *propname, u8 *out_values, size_t sz)  { -	struct property *prop = of_find_property(np, propname, NULL); -	const u8 *val; +	const u8 *val = of_find_property_value_of_size(np, propname, +						(sz * sizeof(*out_values))); -	if (!prop) -		return -EINVAL; -	if (!prop->value) -		return -ENODATA; -	if ((sz * sizeof(*out_values)) > prop->length) -		return -EOVERFLOW; +	if (IS_ERR(val)) +		return PTR_ERR(val); -	val = prop->value;  	while (sz--)  		*out_values++ = *val++;  	return 0; @@ -804,17 +857,12 @@ EXPORT_SYMBOL_GPL(of_property_read_u8_array);  int of_property_read_u16_array(const struct device_node *np,  			const char *propname, u16 *out_values, size_t sz)  { -	struct property *prop = of_find_property(np, propname, NULL); -	const __be16 *val; +	const __be16 *val = of_find_property_value_of_size(np, propname, +						(sz * sizeof(*out_values))); -	if (!prop) -		return -EINVAL; -	if (!prop->value) -		return -ENODATA; -	if ((sz * sizeof(*out_values)) > prop->length) -		return -EOVERFLOW; +	if (IS_ERR(val)) +		return PTR_ERR(val); -	val = prop->value;  	while (sz--)  		*out_values++ = be16_to_cpup(val++);  	return 0; @@ -841,17 +889,12 @@ int of_property_read_u32_array(const struct device_node *np,  			       const char *propname, u32 *out_values,  			       size_t sz)  { -	struct property *prop = of_find_property(np, propname, NULL); -	const __be32 *val; +	const __be32 *val = of_find_property_value_of_size(np, propname, +						(sz * sizeof(*out_values))); -	if (!prop) -		return -EINVAL; -	if (!prop->value) -		return -ENODATA; -	if ((sz * sizeof(*out_values)) > prop->length) -		return -EOVERFLOW; +	if (IS_ERR(val)) +		return PTR_ERR(val); -	val = prop->value;  	while (sz--)  		*out_values++ = be32_to_cpup(val++);  	return 0; @@ -874,15 +917,13 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_array);  int of_property_read_u64(const struct device_node *np, const char *propname,  			 u64 *out_value)  { -	struct property *prop = of_find_property(np, propname, NULL); +	const __be32 *val = of_find_property_value_of_size(np, propname, +						sizeof(*out_value)); -	if (!prop) -		return -EINVAL; -	if (!prop->value) -		return -ENODATA; -	if (sizeof(*out_value) > prop->length) -		return -EOVERFLOW; -	*out_value = of_read_number(prop->value, 2); +	if (IS_ERR(val)) +		return PTR_ERR(val); + +	*out_value = of_read_number(val, 2);  	return 0;  }  EXPORT_SYMBOL_GPL(of_property_read_u64); diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index ab886b7ee32..b41ac7756a4 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -100,6 +100,27 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)  	return min((size_t)(image - rom), size);  } +static loff_t pci_find_rom(struct pci_dev *pdev, size_t *size) +{ +	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; +	loff_t start; + +	/* assign the ROM an address if it doesn't have one */ +	if (res->parent == NULL && pci_assign_resource(pdev, PCI_ROM_RESOURCE)) +		return 0; +	start = pci_resource_start(pdev, PCI_ROM_RESOURCE); +	*size = pci_resource_len(pdev, PCI_ROM_RESOURCE); + +	if (*size == 0) +		return 0; + +	/* Enable ROM space decodes */ +	if (pci_enable_rom(pdev)) +		return 0; + +	return start; +} +  /**   * pci_map_rom - map a PCI ROM to kernel space   * @pdev: pointer to pci device struct @@ -114,21 +135,15 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)  void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)  {  	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; -	loff_t start; +	loff_t start = 0;  	void __iomem *rom;  	/* -	 * Some devices may provide ROMs via a source other than the BAR -	 */ -	if (pdev->rom && pdev->romlen) { -		*size = pdev->romlen; -		return phys_to_virt(pdev->rom); -	/*  	 * IORESOURCE_ROM_SHADOW set on x86, x86_64 and IA64 supports legacy  	 * memory map if the VGA enable bit of the Bridge Control register is  	 * set for embedded VGA.  	 */ -	} else if (res->flags & IORESOURCE_ROM_SHADOW) { +	if (res->flags & IORESOURCE_ROM_SHADOW) {  		/* primary video rom always starts here */  		start = (loff_t)0xC0000;  		*size = 0x20000; /* cover C000:0 through E000:0 */ @@ -139,21 +154,21 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)  			return (void __iomem *)(unsigned long)  				pci_resource_start(pdev, PCI_ROM_RESOURCE);  		} else { -			/* assign the ROM an address if it doesn't have one */ -			if (res->parent == NULL && -			    pci_assign_resource(pdev,PCI_ROM_RESOURCE)) -				return NULL; -			start = pci_resource_start(pdev, PCI_ROM_RESOURCE); -			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE); -			if (*size == 0) -				return NULL; - -			/* Enable ROM space decodes */ -			if (pci_enable_rom(pdev)) -				return NULL; +			start = pci_find_rom(pdev, size);  		}  	} +	/* +	 * Some devices may provide ROMs via a source other than the BAR +	 */ +	if (!start && pdev->rom && pdev->romlen) { +		*size = pdev->romlen; +		return phys_to_virt(pdev->rom); +	} + +	if (!start) +		return NULL; +  	rom = ioremap(start, *size);  	if (!rom) {  		/* restore enable if ioremap fails */ diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 34f51d2d90d..35e94009829 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -229,6 +229,7 @@ config PINCTRL_EXYNOS5440  source "drivers/pinctrl/mvebu/Kconfig"  source "drivers/pinctrl/sh-pfc/Kconfig"  source "drivers/pinctrl/spear/Kconfig" +source "drivers/pinctrl/vt8500/Kconfig"  config PINCTRL_XWAY  	bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f82cc5baf76..a5a52c83c13 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_PLAT_ORION)        += mvebu/  obj-$(CONFIG_ARCH_SHMOBILE)	+= sh-pfc/  obj-$(CONFIG_SUPERH)		+= sh-pfc/  obj-$(CONFIG_PLAT_SPEAR)	+= spear/ +obj-$(CONFIG_ARCH_VT8500)	+= vt8500/ diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index c689c04a4f5..2d2f0a43d36 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -620,7 +620,7 @@ int mvebu_pinctrl_probe(struct platform_device *pdev)  		/* special soc specific control */  		if (ctrl->mpp_get || ctrl->mpp_set) { -			if (!ctrl->name || !ctrl->mpp_set || !ctrl->mpp_set) { +			if (!ctrl->name || !ctrl->mpp_get || !ctrl->mpp_set) {  				dev_err(&pdev->dev, "wrong soc control info\n");  				return -EINVAL;  			} diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index ac8d382a79b..d611ecfcbf7 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -622,7 +622,7 @@ static const struct file_operations pinconf_dbg_pinname_fops = {  static int pinconf_dbg_state_print(struct seq_file *s, void *d)  {  	if (strlen(dbg_state_name)) -		seq_printf(s, "%s\n", dbg_pinname); +		seq_printf(s, "%s\n", dbg_state_name);  	else  		seq_printf(s, "No pin state set\n");  	return 0; diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index e3ed8cb072a..bfda73d64ee 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -90,7 +90,7 @@ static inline void pinconf_init_device_debugfs(struct dentry *devroot,   * pin config.   */ -#ifdef CONFIG_GENERIC_PINCONF +#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_DEBUG_FS)  void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,  			      struct seq_file *s, unsigned pin); diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c index caecdd37306..c542a97c82f 100644 --- a/drivers/pinctrl/pinctrl-abx500.c +++ b/drivers/pinctrl/pinctrl-abx500.c @@ -422,7 +422,7 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,  	}  	/* check if pin use AlternateFunction register */ -	if ((af.alt_bit1 == UNUSED) && (af.alt_bit1 == UNUSED)) +	if ((af.alt_bit1 == UNUSED) && (af.alt_bit2 == UNUSED))  		return mode;  	/*  	 * if pin GPIOSEL bit is set and pin supports alternate function, diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 75933a6aa82..efb7f10e902 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1277,21 +1277,80 @@ static int alt_gpio_irq_type(struct irq_data *d, unsigned type)  }  #ifdef CONFIG_PM + +static u32 wakeups[MAX_GPIO_BANKS]; +static u32 backups[MAX_GPIO_BANKS]; +  static int gpio_irq_set_wake(struct irq_data *d, unsigned state)  {  	struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);  	unsigned	bank = at91_gpio->pioc_idx; +	unsigned mask = 1 << d->hwirq;  	if (unlikely(bank >= MAX_GPIO_BANKS))  		return -EINVAL; +	if (state) +		wakeups[bank] |= mask; +	else +		wakeups[bank] &= ~mask; +  	irq_set_irq_wake(at91_gpio->pioc_virq, state);  	return 0;  } + +void at91_pinctrl_gpio_suspend(void) +{ +	int i; + +	for (i = 0; i < gpio_banks; i++) { +		void __iomem  *pio; + +		if (!gpio_chips[i]) +			continue; + +		pio = gpio_chips[i]->regbase; + +		backups[i] = __raw_readl(pio + PIO_IMR); +		__raw_writel(backups[i], pio + PIO_IDR); +		__raw_writel(wakeups[i], pio + PIO_IER); + +		if (!wakeups[i]) { +			clk_unprepare(gpio_chips[i]->clock); +			clk_disable(gpio_chips[i]->clock); +		} else { +			printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", +			       'A'+i, wakeups[i]); +		} +	} +} + +void at91_pinctrl_gpio_resume(void) +{ +	int i; + +	for (i = 0; i < gpio_banks; i++) { +		void __iomem  *pio; + +		if (!gpio_chips[i]) +			continue; + +		pio = gpio_chips[i]->regbase; + +		if (!wakeups[i]) { +			if (clk_prepare(gpio_chips[i]->clock) == 0) +				clk_enable(gpio_chips[i]->clock); +		} + +		__raw_writel(wakeups[i], pio + PIO_IDR); +		__raw_writel(backups[i], pio + PIO_IER); +	} +} +  #else  #define gpio_irq_set_wake	NULL -#endif +#endif /* CONFIG_PM */  static struct irq_chip gpio_irqchip = {  	.name		= "GPIO", diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index 4eb6d2c4e4d..2a2e427d765 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -699,11 +699,6 @@ static int bcm2835_pctl_dt_node_to_map_pull(struct bcm2835_pinctrl *pc,  	return 0;  } -static inline u32 prop_u32(struct property *p, int i) -{ -	return be32_to_cpup(((__be32 *)p->value) + i); -} -  static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,  		struct device_node *np,  		struct pinctrl_map **map, unsigned *num_maps) @@ -761,7 +756,9 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,  		return -ENOMEM;  	for (i = 0; i < num_pins; i++) { -		pin = prop_u32(pins, i); +		err = of_property_read_u32_index(np, "brcm,pins", i, &pin); +		if (err) +			goto out;  		if (pin >= ARRAY_SIZE(bcm2835_gpio_pins)) {  			dev_err(pc->dev, "%s: invalid brcm,pins value %d\n",  				of_node_full_name(np), pin); @@ -770,14 +767,20 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,  		}  		if (num_funcs) { -			func = prop_u32(funcs, (num_funcs > 1) ? i : 0); +			err = of_property_read_u32_index(np, "brcm,function", +					(num_funcs > 1) ? i : 0, &func); +			if (err) +				goto out;  			err = bcm2835_pctl_dt_node_to_map_func(pc, np, pin,  							func, &cur_map);  			if (err)  				goto out;  		}  		if (num_pulls) { -			pull = prop_u32(pulls, (num_pulls > 1) ? i : 0); +			err = of_property_read_u32_index(np, "brcm,pull", +					(num_funcs > 1) ? i : 0, &pull); +			if (err) +				goto out;  			err = bcm2835_pctl_dt_node_to_map_pull(pc, np, pin,  							pull, &cur_map);  			if (err) diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 538b9ddaadf..8738933a57d 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -677,3 +677,111 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {  		.label		= "exynos4x12-gpio-ctrl3",  	},  }; + +/* pin banks of exynos5250 pin-controller 0 */ +static struct samsung_pin_bank exynos5250_pin_banks0[] = { +	EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00), +	EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04), +	EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpa2", 0x08), +	EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpb0", 0x0c), +	EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpb1", 0x10), +	EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpb2", 0x14), +	EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpb3", 0x18), +	EXYNOS_PIN_BANK_EINTG(7, 0x0E0, "gpc0", 0x1c), +	EXYNOS_PIN_BANK_EINTG(4, 0x100, "gpc1", 0x20), +	EXYNOS_PIN_BANK_EINTG(7, 0x120, "gpc2", 0x24), +	EXYNOS_PIN_BANK_EINTG(7, 0x140, "gpc3", 0x28), +	EXYNOS_PIN_BANK_EINTG(4, 0x160, "gpd0", 0x2c), +	EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpd1", 0x30), +	EXYNOS_PIN_BANK_EINTG(7, 0x2E0, "gpc4", 0x34), +	EXYNOS_PIN_BANK_EINTN(6, 0x1A0, "gpy0"), +	EXYNOS_PIN_BANK_EINTN(4, 0x1C0, "gpy1"), +	EXYNOS_PIN_BANK_EINTN(6, 0x1E0, "gpy2"), +	EXYNOS_PIN_BANK_EINTN(8, 0x200, "gpy3"), +	EXYNOS_PIN_BANK_EINTN(8, 0x220, "gpy4"), +	EXYNOS_PIN_BANK_EINTN(8, 0x240, "gpy5"), +	EXYNOS_PIN_BANK_EINTN(8, 0x260, "gpy6"), +	EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00), +	EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04), +	EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08), +	EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c), +}; + +/* pin banks of exynos5250 pin-controller 1 */ +static struct samsung_pin_bank exynos5250_pin_banks1[] = { +	EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpe0", 0x00), +	EXYNOS_PIN_BANK_EINTG(2, 0x020, "gpe1", 0x04), +	EXYNOS_PIN_BANK_EINTG(4, 0x040, "gpf0", 0x08), +	EXYNOS_PIN_BANK_EINTG(4, 0x060, "gpf1", 0x0c), +	EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpg0", 0x10), +	EXYNOS_PIN_BANK_EINTG(8, 0x0A0, "gpg1", 0x14), +	EXYNOS_PIN_BANK_EINTG(2, 0x0C0, "gpg2", 0x18), +	EXYNOS_PIN_BANK_EINTG(4, 0x0E0, "gph0", 0x1c), +	EXYNOS_PIN_BANK_EINTG(8, 0x100, "gph1", 0x20), +}; + +/* pin banks of exynos5250 pin-controller 2 */ +static struct samsung_pin_bank exynos5250_pin_banks2[] = { +	EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpv0", 0x00), +	EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpv1", 0x04), +	EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpv2", 0x08), +	EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpv3", 0x0c), +	EXYNOS_PIN_BANK_EINTG(2, 0x0C0, "gpv4", 0x10), +}; + +/* pin banks of exynos5250 pin-controller 3 */ +static struct samsung_pin_bank exynos5250_pin_banks3[] = { +	EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz", 0x00), +}; + +/* + * Samsung pinctrl driver data for Exynos5250 SoC. Exynos5250 SoC includes + * four gpio/pin-mux/pinconfig controllers. + */ +struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { +	{ +		/* pin-controller instance 0 data */ +		.pin_banks	= exynos5250_pin_banks0, +		.nr_banks	= ARRAY_SIZE(exynos5250_pin_banks0), +		.geint_con	= EXYNOS_GPIO_ECON_OFFSET, +		.geint_mask	= EXYNOS_GPIO_EMASK_OFFSET, +		.geint_pend	= EXYNOS_GPIO_EPEND_OFFSET, +		.weint_con	= EXYNOS_WKUP_ECON_OFFSET, +		.weint_mask	= EXYNOS_WKUP_EMASK_OFFSET, +		.weint_pend	= EXYNOS_WKUP_EPEND_OFFSET, +		.svc		= EXYNOS_SVC_OFFSET, +		.eint_gpio_init = exynos_eint_gpio_init, +		.eint_wkup_init = exynos_eint_wkup_init, +		.label		= "exynos5250-gpio-ctrl0", +	}, { +		/* pin-controller instance 1 data */ +		.pin_banks	= exynos5250_pin_banks1, +		.nr_banks	= ARRAY_SIZE(exynos5250_pin_banks1), +		.geint_con	= EXYNOS_GPIO_ECON_OFFSET, +		.geint_mask	= EXYNOS_GPIO_EMASK_OFFSET, +		.geint_pend	= EXYNOS_GPIO_EPEND_OFFSET, +		.svc		= EXYNOS_SVC_OFFSET, +		.eint_gpio_init = exynos_eint_gpio_init, +		.label		= "exynos5250-gpio-ctrl1", +	}, { +		/* pin-controller instance 2 data */ +		.pin_banks	= exynos5250_pin_banks2, +		.nr_banks	= ARRAY_SIZE(exynos5250_pin_banks2), +		.geint_con	= EXYNOS_GPIO_ECON_OFFSET, +		.geint_mask	= EXYNOS_GPIO_EMASK_OFFSET, +		.geint_pend	= EXYNOS_GPIO_EPEND_OFFSET, +		.svc		= EXYNOS_SVC_OFFSET, +		.eint_gpio_init = exynos_eint_gpio_init, +		.label		= "exynos5250-gpio-ctrl2", +	}, { +		/* pin-controller instance 3 data */ +		.pin_banks	= exynos5250_pin_banks3, +		.nr_banks	= ARRAY_SIZE(exynos5250_pin_banks3), +		.geint_con	= EXYNOS_GPIO_ECON_OFFSET, +		.geint_mask	= EXYNOS_GPIO_EMASK_OFFSET, +		.geint_pend	= EXYNOS_GPIO_EPEND_OFFSET, +		.svc		= EXYNOS_SVC_OFFSET, +		.eint_gpio_init = exynos_eint_gpio_init, +		.label		= "exynos5250-gpio-ctrl3", +	}, +}; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index f206df17565..3d5cf639aa4 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -948,6 +948,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = {  		.data = (void *)exynos4210_pin_ctrl },  	{ .compatible = "samsung,exynos4x12-pinctrl",  		.data = (void *)exynos4x12_pin_ctrl }, +	{ .compatible = "samsung,exynos5250-pinctrl", +		.data = (void *)exynos5250_pin_ctrl },  	{},  };  MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index e2d4e67f7e8..ee964aadce0 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -237,5 +237,6 @@ struct samsung_pmx_func {  /* list of all exported SoC specific data */  extern struct samsung_pin_ctrl exynos4210_pin_ctrl[];  extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; +extern struct samsung_pin_ctrl exynos5250_pin_ctrl[];  #endif /* __PINCTRL_SAMSUNG_H */ diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 1a00658b3ea..bd83c8b01cd 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -194,6 +194,11 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,  	}  	if (!gpio_range) { +		/* +		 * A pin should not be freed more times than allocated. +		 */ +		if (WARN_ON(!desc->mux_usecount)) +			return NULL;  		desc->mux_usecount--;  		if (desc->mux_usecount)  			return NULL; diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c index 709008e9412..6f15c03077a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c @@ -2733,9 +2733,9 @@ static struct pinmux_data_reg pinmux_data_regs[] = {  	{ },  }; -/* IRQ pins through INTCS with IRQ0->15 from 0x200 and IRQ16-31 from 0x3200 */ -#define EXT_IRQ16L(n) intcs_evt2irq(0x200 + ((n) << 5)) -#define EXT_IRQ16H(n) intcs_evt2irq(0x3200 + ((n - 16) << 5)) +/* External IRQ pins mapped at IRQPIN_BASE */ +#define EXT_IRQ16L(n) irq_pin(n) +#define EXT_IRQ16H(n) irq_pin(n)  static struct pinmux_irq pinmux_irqs[] = {  	PINMUX_IRQ(EXT_IRQ16H(19), PORT9_FN0), diff --git a/drivers/pinctrl/vt8500/Kconfig b/drivers/pinctrl/vt8500/Kconfig new file mode 100644 index 00000000000..55724a73d94 --- /dev/null +++ b/drivers/pinctrl/vt8500/Kconfig @@ -0,0 +1,52 @@ +# +# VIA/Wondermedia PINCTRL drivers +# + +if ARCH_VT8500 + +config PINCTRL_WMT +	bool +	select PINMUX +	select GENERIC_PINCONF + +config PINCTRL_VT8500 +	bool "VIA VT8500 pin controller driver" +	depends on ARCH_WM8505 +	select PINCTRL_WMT +	help +	  Say yes here to support the gpio/pin control module on +	  VIA VT8500 SoCs. + +config PINCTRL_WM8505 +	bool "Wondermedia WM8505 pin controller driver" +	depends on ARCH_WM8505 +	select PINCTRL_WMT +	help +	  Say yes here to support the gpio/pin control module on +	  Wondermedia WM8505 SoCs. + +config PINCTRL_WM8650 +	bool "Wondermedia WM8650 pin controller driver" +	depends on ARCH_WM8505 +	select PINCTRL_WMT +	help +	  Say yes here to support the gpio/pin control module on +	  Wondermedia WM8650 SoCs. + +config PINCTRL_WM8750 +	bool "Wondermedia WM8750 pin controller driver" +	depends on ARCH_WM8750 +	select PINCTRL_WMT +	help +	  Say yes here to support the gpio/pin control module on +	  Wondermedia WM8750 SoCs. + +config PINCTRL_WM8850 +	bool "Wondermedia WM8850 pin controller driver" +	depends on ARCH_WM8850 +	select PINCTRL_WMT +	help +	  Say yes here to support the gpio/pin control module on +	  Wondermedia WM8850 SoCs. + +endif diff --git a/drivers/pinctrl/vt8500/Makefile b/drivers/pinctrl/vt8500/Makefile new file mode 100644 index 00000000000..24ec45dd0d8 --- /dev/null +++ b/drivers/pinctrl/vt8500/Makefile @@ -0,0 +1,8 @@ +# VIA/Wondermedia pinctrl support + +obj-$(CONFIG_PINCTRL_WMT)	+= pinctrl-wmt.o +obj-$(CONFIG_PINCTRL_VT8500)	+= pinctrl-vt8500.o +obj-$(CONFIG_PINCTRL_WM8505)	+= pinctrl-wm8505.o +obj-$(CONFIG_PINCTRL_WM8650)	+= pinctrl-wm8650.o +obj-$(CONFIG_PINCTRL_WM8750)	+= pinctrl-wm8750.o +obj-$(CONFIG_PINCTRL_WM8850)	+= pinctrl-wm8850.o diff --git a/drivers/pinctrl/vt8500/pinctrl-vt8500.c b/drivers/pinctrl/vt8500/pinctrl-vt8500.c new file mode 100644 index 00000000000..f2fe9f85cfa --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-vt8500.c @@ -0,0 +1,501 @@ +/* + * Pinctrl data for VIA VT8500 SoC + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers vt8500_banks[] = { +	WMT_PINCTRL_BANK(NO_REG, 0x3C, 0x5C, 0x7C, NO_REG, NO_REG),	/* 0 */ +	WMT_PINCTRL_BANK(0x00, 0x20, 0x40, 0x60, NO_REG, NO_REG),	/* 1 */ +	WMT_PINCTRL_BANK(0x04, 0x24, 0x44, 0x64, NO_REG, NO_REG),	/* 2 */ +	WMT_PINCTRL_BANK(0x08, 0x28, 0x48, 0x68, NO_REG, NO_REG),	/* 3 */ +	WMT_PINCTRL_BANK(0x0C, 0x2C, 0x4C, 0x6C, NO_REG, NO_REG),	/* 4 */ +	WMT_PINCTRL_BANK(0x10, 0x30, 0x50, 0x70, NO_REG, NO_REG),	/* 5 */ +	WMT_PINCTRL_BANK(0x14, 0x34, 0x54, 0x74, NO_REG, NO_REG),	/* 6 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0	WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1	WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2	WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3	WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4	WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5	WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6	WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7	WMT_PIN(0, 7) +#define WMT_PIN_EXTGPIO8	WMT_PIN(0, 8) +#define WMT_PIN_UART0RTS	WMT_PIN(1, 0) +#define WMT_PIN_UART0TXD	WMT_PIN(1, 1) +#define WMT_PIN_UART0CTS	WMT_PIN(1, 2) +#define WMT_PIN_UART0RXD	WMT_PIN(1, 3) +#define WMT_PIN_UART1RTS	WMT_PIN(1, 4) +#define WMT_PIN_UART1TXD	WMT_PIN(1, 5) +#define WMT_PIN_UART1CTS	WMT_PIN(1, 6) +#define WMT_PIN_UART1RXD	WMT_PIN(1, 7) +#define WMT_PIN_SPI0CLK		WMT_PIN(1, 8) +#define WMT_PIN_SPI0SS		WMT_PIN(1, 9) +#define WMT_PIN_SPI0MISO	WMT_PIN(1, 10) +#define WMT_PIN_SPI0MOSI	WMT_PIN(1, 11) +#define WMT_PIN_SPI1CLK		WMT_PIN(1, 12) +#define WMT_PIN_SPI1SS		WMT_PIN(1, 13) +#define WMT_PIN_SPI1MISO	WMT_PIN(1, 14) +#define WMT_PIN_SPI1MOSI	WMT_PIN(1, 15) +#define WMT_PIN_SPI2CLK		WMT_PIN(1, 16) +#define WMT_PIN_SPI2SS		WMT_PIN(1, 17) +#define WMT_PIN_SPI2MISO	WMT_PIN(1, 18) +#define WMT_PIN_SPI2MOSI	WMT_PIN(1, 19) +#define WMT_PIN_SDDATA0		WMT_PIN(2, 0) +#define WMT_PIN_SDDATA1		WMT_PIN(2, 1) +#define WMT_PIN_SDDATA2		WMT_PIN(2, 2) +#define WMT_PIN_SDDATA3		WMT_PIN(2, 3) +#define WMT_PIN_MMCDATA0	WMT_PIN(2, 4) +#define WMT_PIN_MMCDATA1	WMT_PIN(2, 5) +#define WMT_PIN_MMCDATA2	WMT_PIN(2, 6) +#define WMT_PIN_MMCDATA3	WMT_PIN(2, 7) +#define WMT_PIN_SDCLK		WMT_PIN(2, 8) +#define WMT_PIN_SDWP		WMT_PIN(2, 9) +#define WMT_PIN_SDCMD		WMT_PIN(2, 10) +#define WMT_PIN_MSDATA0		WMT_PIN(2, 16) +#define WMT_PIN_MSDATA1		WMT_PIN(2, 17) +#define WMT_PIN_MSDATA2		WMT_PIN(2, 18) +#define WMT_PIN_MSDATA3		WMT_PIN(2, 19) +#define WMT_PIN_MSCLK		WMT_PIN(2, 20) +#define WMT_PIN_MSBS		WMT_PIN(2, 21) +#define WMT_PIN_MSINS		WMT_PIN(2, 22) +#define WMT_PIN_I2C0SCL		WMT_PIN(2, 24) +#define WMT_PIN_I2C0SDA		WMT_PIN(2, 25) +#define WMT_PIN_I2C1SCL		WMT_PIN(2, 26) +#define WMT_PIN_I2C1SDA		WMT_PIN(2, 27) +#define WMT_PIN_MII0RXD0	WMT_PIN(3, 0) +#define WMT_PIN_MII0RXD1	WMT_PIN(3, 1) +#define WMT_PIN_MII0RXD2	WMT_PIN(3, 2) +#define WMT_PIN_MII0RXD3	WMT_PIN(3, 3) +#define WMT_PIN_MII0RXCLK	WMT_PIN(3, 4) +#define WMT_PIN_MII0RXDV	WMT_PIN(3, 5) +#define WMT_PIN_MII0RXERR	WMT_PIN(3, 6) +#define WMT_PIN_MII0PHYRST	WMT_PIN(3, 7) +#define WMT_PIN_MII0TXD0	WMT_PIN(3, 8) +#define WMT_PIN_MII0TXD1	WMT_PIN(3, 9) +#define WMT_PIN_MII0TXD2	WMT_PIN(3, 10) +#define WMT_PIN_MII0TXD3	WMT_PIN(3, 11) +#define WMT_PIN_MII0TXCLK	WMT_PIN(3, 12) +#define WMT_PIN_MII0TXEN	WMT_PIN(3, 13) +#define WMT_PIN_MII0TXERR	WMT_PIN(3, 14) +#define WMT_PIN_MII0PHYPD	WMT_PIN(3, 15) +#define WMT_PIN_MII0COL		WMT_PIN(3, 16) +#define WMT_PIN_MII0CRS		WMT_PIN(3, 17) +#define WMT_PIN_MII0MDIO	WMT_PIN(3, 18) +#define WMT_PIN_MII0MDC		WMT_PIN(3, 19) +#define WMT_PIN_SEECS		WMT_PIN(3, 20) +#define WMT_PIN_SEECK		WMT_PIN(3, 21) +#define WMT_PIN_SEEDI		WMT_PIN(3, 22) +#define WMT_PIN_SEEDO		WMT_PIN(3, 23) +#define WMT_PIN_IDEDREQ0	WMT_PIN(3, 24) +#define WMT_PIN_IDEDREQ1	WMT_PIN(3, 25) +#define WMT_PIN_IDEIOW		WMT_PIN(3, 26) +#define WMT_PIN_IDEIOR		WMT_PIN(3, 27) +#define WMT_PIN_IDEDACK		WMT_PIN(3, 28) +#define WMT_PIN_IDEIORDY	WMT_PIN(3, 29) +#define WMT_PIN_IDEINTRQ	WMT_PIN(3, 30) +#define WMT_PIN_VDIN0		WMT_PIN(4, 0) +#define WMT_PIN_VDIN1		WMT_PIN(4, 1) +#define WMT_PIN_VDIN2		WMT_PIN(4, 2) +#define WMT_PIN_VDIN3		WMT_PIN(4, 3) +#define WMT_PIN_VDIN4		WMT_PIN(4, 4) +#define WMT_PIN_VDIN5		WMT_PIN(4, 5) +#define WMT_PIN_VDIN6		WMT_PIN(4, 6) +#define WMT_PIN_VDIN7		WMT_PIN(4, 7) +#define WMT_PIN_VDOUT0		WMT_PIN(4, 8) +#define WMT_PIN_VDOUT1		WMT_PIN(4, 9) +#define WMT_PIN_VDOUT2		WMT_PIN(4, 10) +#define WMT_PIN_VDOUT3		WMT_PIN(4, 11) +#define WMT_PIN_VDOUT4		WMT_PIN(4, 12) +#define WMT_PIN_VDOUT5		WMT_PIN(4, 13) +#define WMT_PIN_NANDCLE0	WMT_PIN(4, 14) +#define WMT_PIN_NANDCLE1	WMT_PIN(4, 15) +#define WMT_PIN_VDOUT6_7	WMT_PIN(4, 16) +#define WMT_PIN_VHSYNC		WMT_PIN(4, 17) +#define WMT_PIN_VVSYNC		WMT_PIN(4, 18) +#define WMT_PIN_TSDIN0		WMT_PIN(5, 8) +#define WMT_PIN_TSDIN1		WMT_PIN(5, 9) +#define WMT_PIN_TSDIN2		WMT_PIN(5, 10) +#define WMT_PIN_TSDIN3		WMT_PIN(5, 11) +#define WMT_PIN_TSDIN4		WMT_PIN(5, 12) +#define WMT_PIN_TSDIN5		WMT_PIN(5, 13) +#define WMT_PIN_TSDIN6		WMT_PIN(5, 14) +#define WMT_PIN_TSDIN7		WMT_PIN(5, 15) +#define WMT_PIN_TSSYNC		WMT_PIN(5, 16) +#define WMT_PIN_TSVALID		WMT_PIN(5, 17) +#define WMT_PIN_TSCLK		WMT_PIN(5, 18) +#define WMT_PIN_LCDD0		WMT_PIN(6, 0) +#define WMT_PIN_LCDD1		WMT_PIN(6, 1) +#define WMT_PIN_LCDD2		WMT_PIN(6, 2) +#define WMT_PIN_LCDD3		WMT_PIN(6, 3) +#define WMT_PIN_LCDD4		WMT_PIN(6, 4) +#define WMT_PIN_LCDD5		WMT_PIN(6, 5) +#define WMT_PIN_LCDD6		WMT_PIN(6, 6) +#define WMT_PIN_LCDD7		WMT_PIN(6, 7) +#define WMT_PIN_LCDD8		WMT_PIN(6, 8) +#define WMT_PIN_LCDD9		WMT_PIN(6, 9) +#define WMT_PIN_LCDD10		WMT_PIN(6, 10) +#define WMT_PIN_LCDD11		WMT_PIN(6, 11) +#define WMT_PIN_LCDD12		WMT_PIN(6, 12) +#define WMT_PIN_LCDD13		WMT_PIN(6, 13) +#define WMT_PIN_LCDD14		WMT_PIN(6, 14) +#define WMT_PIN_LCDD15		WMT_PIN(6, 15) +#define WMT_PIN_LCDD16		WMT_PIN(6, 16) +#define WMT_PIN_LCDD17		WMT_PIN(6, 17) +#define WMT_PIN_LCDCLK		WMT_PIN(6, 18) +#define WMT_PIN_LCDDEN		WMT_PIN(6, 19) +#define WMT_PIN_LCDLINE		WMT_PIN(6, 20) +#define WMT_PIN_LCDFRM		WMT_PIN(6, 21) +#define WMT_PIN_LCDBIAS		WMT_PIN(6, 22) + +static const struct pinctrl_pin_desc vt8500_pins[] = { +	PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO8, "extgpio8"), +	PINCTRL_PIN(WMT_PIN_UART0RTS, "uart0_rts"), +	PINCTRL_PIN(WMT_PIN_UART0TXD, "uart0_txd"), +	PINCTRL_PIN(WMT_PIN_UART0CTS, "uart0_cts"), +	PINCTRL_PIN(WMT_PIN_UART0RXD, "uart0_rxd"), +	PINCTRL_PIN(WMT_PIN_UART1RTS, "uart1_rts"), +	PINCTRL_PIN(WMT_PIN_UART1TXD, "uart1_txd"), +	PINCTRL_PIN(WMT_PIN_UART1CTS, "uart1_cts"), +	PINCTRL_PIN(WMT_PIN_UART1RXD, "uart1_rxd"), +	PINCTRL_PIN(WMT_PIN_SPI0CLK, "spi0_clk"), +	PINCTRL_PIN(WMT_PIN_SPI0SS, "spi0_ss"), +	PINCTRL_PIN(WMT_PIN_SPI0MISO, "spi0_miso"), +	PINCTRL_PIN(WMT_PIN_SPI0MOSI, "spi0_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI1CLK, "spi1_clk"), +	PINCTRL_PIN(WMT_PIN_SPI1SS, "spi1_ss"), +	PINCTRL_PIN(WMT_PIN_SPI1MISO, "spi1_miso"), +	PINCTRL_PIN(WMT_PIN_SPI1MOSI, "spi1_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI2CLK, "spi2_clk"), +	PINCTRL_PIN(WMT_PIN_SPI2SS, "spi2_ss"), +	PINCTRL_PIN(WMT_PIN_SPI2MISO, "spi2_miso"), +	PINCTRL_PIN(WMT_PIN_SPI2MOSI, "spi2_mosi"), +	PINCTRL_PIN(WMT_PIN_SDDATA0, "sd_data0"), +	PINCTRL_PIN(WMT_PIN_SDDATA1, "sd_data1"), +	PINCTRL_PIN(WMT_PIN_SDDATA2, "sd_data2"), +	PINCTRL_PIN(WMT_PIN_SDDATA3, "sd_data3"), +	PINCTRL_PIN(WMT_PIN_MMCDATA0, "mmc_data0"), +	PINCTRL_PIN(WMT_PIN_MMCDATA1, "mmc_data1"), +	PINCTRL_PIN(WMT_PIN_MMCDATA2, "mmc_data2"), +	PINCTRL_PIN(WMT_PIN_MMCDATA3, "mmc_data3"), +	PINCTRL_PIN(WMT_PIN_SDCLK, "sd_clk"), +	PINCTRL_PIN(WMT_PIN_SDWP, "sd_wp"), +	PINCTRL_PIN(WMT_PIN_SDCMD, "sd_cmd"), +	PINCTRL_PIN(WMT_PIN_MSDATA0, "ms_data0"), +	PINCTRL_PIN(WMT_PIN_MSDATA1, "ms_data1"), +	PINCTRL_PIN(WMT_PIN_MSDATA2, "ms_data2"), +	PINCTRL_PIN(WMT_PIN_MSDATA3, "ms_data3"), +	PINCTRL_PIN(WMT_PIN_MSCLK, "ms_clk"), +	PINCTRL_PIN(WMT_PIN_MSBS, "ms_bs"), +	PINCTRL_PIN(WMT_PIN_MSINS, "ms_ins"), +	PINCTRL_PIN(WMT_PIN_I2C0SCL, "i2c0_scl"), +	PINCTRL_PIN(WMT_PIN_I2C0SDA, "i2c0_sda"), +	PINCTRL_PIN(WMT_PIN_I2C1SCL, "i2c1_scl"), +	PINCTRL_PIN(WMT_PIN_I2C1SDA, "i2c1_sda"), +	PINCTRL_PIN(WMT_PIN_MII0RXD0, "mii0_rxd0"), +	PINCTRL_PIN(WMT_PIN_MII0RXD1, "mii0_rxd1"), +	PINCTRL_PIN(WMT_PIN_MII0RXD2, "mii0_rxd2"), +	PINCTRL_PIN(WMT_PIN_MII0RXD3, "mii0_rxd3"), +	PINCTRL_PIN(WMT_PIN_MII0RXCLK, "mii0_rxclk"), +	PINCTRL_PIN(WMT_PIN_MII0RXDV, "mii0_rxdv"), +	PINCTRL_PIN(WMT_PIN_MII0RXERR, "mii0_rxerr"), +	PINCTRL_PIN(WMT_PIN_MII0PHYRST, "mii0_phyrst"), +	PINCTRL_PIN(WMT_PIN_MII0TXD0, "mii0_txd0"), +	PINCTRL_PIN(WMT_PIN_MII0TXD1, "mii0_txd1"), +	PINCTRL_PIN(WMT_PIN_MII0TXD2, "mii0_txd2"), +	PINCTRL_PIN(WMT_PIN_MII0TXD3, "mii0_txd3"), +	PINCTRL_PIN(WMT_PIN_MII0TXCLK, "mii0_txclk"), +	PINCTRL_PIN(WMT_PIN_MII0TXEN, "mii0_txen"), +	PINCTRL_PIN(WMT_PIN_MII0TXERR, "mii0_txerr"), +	PINCTRL_PIN(WMT_PIN_MII0PHYPD, "mii0_phypd"), +	PINCTRL_PIN(WMT_PIN_MII0COL, "mii0_col"), +	PINCTRL_PIN(WMT_PIN_MII0CRS, "mii0_crs"), +	PINCTRL_PIN(WMT_PIN_MII0MDIO, "mii0_mdio"), +	PINCTRL_PIN(WMT_PIN_MII0MDC, "mii0_mdc"), +	PINCTRL_PIN(WMT_PIN_SEECS, "see_cs"), +	PINCTRL_PIN(WMT_PIN_SEECK, "see_ck"), +	PINCTRL_PIN(WMT_PIN_SEEDI, "see_di"), +	PINCTRL_PIN(WMT_PIN_SEEDO, "see_do"), +	PINCTRL_PIN(WMT_PIN_IDEDREQ0, "ide_dreq0"), +	PINCTRL_PIN(WMT_PIN_IDEDREQ1, "ide_dreq1"), +	PINCTRL_PIN(WMT_PIN_IDEIOW, "ide_iow"), +	PINCTRL_PIN(WMT_PIN_IDEIOR, "ide_ior"), +	PINCTRL_PIN(WMT_PIN_IDEDACK, "ide_dack"), +	PINCTRL_PIN(WMT_PIN_IDEIORDY, "ide_iordy"), +	PINCTRL_PIN(WMT_PIN_IDEINTRQ, "ide_intrq"), +	PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), +	PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), +	PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), +	PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), +	PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), +	PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), +	PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), +	PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), +	PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), +	PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), +	PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), +	PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), +	PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), +	PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), +	PINCTRL_PIN(WMT_PIN_NANDCLE0, "nand_cle0"), +	PINCTRL_PIN(WMT_PIN_NANDCLE1, "nand_cle1"), +	PINCTRL_PIN(WMT_PIN_VDOUT6_7, "vdout6_7"), +	PINCTRL_PIN(WMT_PIN_VHSYNC, "vhsync"), +	PINCTRL_PIN(WMT_PIN_VVSYNC, "vvsync"), +	PINCTRL_PIN(WMT_PIN_TSDIN0, "tsdin0"), +	PINCTRL_PIN(WMT_PIN_TSDIN1, "tsdin1"), +	PINCTRL_PIN(WMT_PIN_TSDIN2, "tsdin2"), +	PINCTRL_PIN(WMT_PIN_TSDIN3, "tsdin3"), +	PINCTRL_PIN(WMT_PIN_TSDIN4, "tsdin4"), +	PINCTRL_PIN(WMT_PIN_TSDIN5, "tsdin5"), +	PINCTRL_PIN(WMT_PIN_TSDIN6, "tsdin6"), +	PINCTRL_PIN(WMT_PIN_TSDIN7, "tsdin7"), +	PINCTRL_PIN(WMT_PIN_TSSYNC, "tssync"), +	PINCTRL_PIN(WMT_PIN_TSVALID, "tsvalid"), +	PINCTRL_PIN(WMT_PIN_TSCLK, "tsclk"), +	PINCTRL_PIN(WMT_PIN_LCDD0, "lcd_d0"), +	PINCTRL_PIN(WMT_PIN_LCDD1, "lcd_d1"), +	PINCTRL_PIN(WMT_PIN_LCDD2, "lcd_d2"), +	PINCTRL_PIN(WMT_PIN_LCDD3, "lcd_d3"), +	PINCTRL_PIN(WMT_PIN_LCDD4, "lcd_d4"), +	PINCTRL_PIN(WMT_PIN_LCDD5, "lcd_d5"), +	PINCTRL_PIN(WMT_PIN_LCDD6, "lcd_d6"), +	PINCTRL_PIN(WMT_PIN_LCDD7, "lcd_d7"), +	PINCTRL_PIN(WMT_PIN_LCDD8, "lcd_d8"), +	PINCTRL_PIN(WMT_PIN_LCDD9, "lcd_d9"), +	PINCTRL_PIN(WMT_PIN_LCDD10, "lcd_d10"), +	PINCTRL_PIN(WMT_PIN_LCDD11, "lcd_d11"), +	PINCTRL_PIN(WMT_PIN_LCDD12, "lcd_d12"), +	PINCTRL_PIN(WMT_PIN_LCDD13, "lcd_d13"), +	PINCTRL_PIN(WMT_PIN_LCDD14, "lcd_d14"), +	PINCTRL_PIN(WMT_PIN_LCDD15, "lcd_d15"), +	PINCTRL_PIN(WMT_PIN_LCDD16, "lcd_d16"), +	PINCTRL_PIN(WMT_PIN_LCDD17, "lcd_d17"), +	PINCTRL_PIN(WMT_PIN_LCDCLK, "lcd_clk"), +	PINCTRL_PIN(WMT_PIN_LCDDEN, "lcd_den"), +	PINCTRL_PIN(WMT_PIN_LCDLINE, "lcd_line"), +	PINCTRL_PIN(WMT_PIN_LCDFRM, "lcd_frm"), +	PINCTRL_PIN(WMT_PIN_LCDBIAS, "lcd_bias"), +}; + +/* Order of these names must match the above list */ +static const char * const vt8500_groups[] = { +	"extgpio0", +	"extgpio1", +	"extgpio2", +	"extgpio3", +	"extgpio4", +	"extgpio5", +	"extgpio6", +	"extgpio7", +	"extgpio8", +	"uart0_rts", +	"uart0_txd", +	"uart0_cts", +	"uart0_rxd", +	"uart1_rts", +	"uart1_txd", +	"uart1_cts", +	"uart1_rxd", +	"spi0_clk", +	"spi0_ss", +	"spi0_miso", +	"spi0_mosi", +	"spi1_clk", +	"spi1_ss", +	"spi1_miso", +	"spi1_mosi", +	"spi2_clk", +	"spi2_ss", +	"spi2_miso", +	"spi2_mosi", +	"sd_data0", +	"sd_data1", +	"sd_data2", +	"sd_data3", +	"mmc_data0", +	"mmc_data1", +	"mmc_data2", +	"mmc_data3", +	"sd_clk", +	"sd_wp", +	"sd_cmd", +	"ms_data0", +	"ms_data1", +	"ms_data2", +	"ms_data3", +	"ms_clk", +	"ms_bs", +	"ms_ins", +	"i2c0_scl", +	"i2c0_sda", +	"i2c1_scl", +	"i2c1_sda", +	"mii0_rxd0", +	"mii0_rxd1", +	"mii0_rxd2", +	"mii0_rxd3", +	"mii0_rxclk", +	"mii0_rxdv", +	"mii0_rxerr", +	"mii0_phyrst", +	"mii0_txd0", +	"mii0_txd1", +	"mii0_txd2", +	"mii0_txd3", +	"mii0_txclk", +	"mii0_txen", +	"mii0_txerr", +	"mii0_phypd", +	"mii0_col", +	"mii0_crs", +	"mii0_mdio", +	"mii0_mdc", +	"see_cs", +	"see_ck", +	"see_di", +	"see_do", +	"ide_dreq0", +	"ide_dreq1", +	"ide_iow", +	"ide_ior", +	"ide_dack", +	"ide_iordy", +	"ide_intrq", +	"vdin0", +	"vdin1", +	"vdin2", +	"vdin3", +	"vdin4", +	"vdin5", +	"vdin6", +	"vdin7", +	"vdout0", +	"vdout1", +	"vdout2", +	"vdout3", +	"vdout4", +	"vdout5", +	"nand_cle0", +	"nand_cle1", +	"vdout6_7", +	"vhsync", +	"vvsync", +	"tsdin0", +	"tsdin1", +	"tsdin2", +	"tsdin3", +	"tsdin4", +	"tsdin5", +	"tsdin6", +	"tsdin7", +	"tssync", +	"tsvalid", +	"tsclk", +	"lcd_d0", +	"lcd_d1", +	"lcd_d2", +	"lcd_d3", +	"lcd_d4", +	"lcd_d5", +	"lcd_d6", +	"lcd_d7", +	"lcd_d8", +	"lcd_d9", +	"lcd_d10", +	"lcd_d11", +	"lcd_d12", +	"lcd_d13", +	"lcd_d14", +	"lcd_d15", +	"lcd_d16", +	"lcd_d17", +	"lcd_clk", +	"lcd_den", +	"lcd_line", +	"lcd_frm", +	"lcd_bias", +}; + +static int vt8500_pinctrl_probe(struct platform_device *pdev) +{ +	struct wmt_pinctrl_data *data; + +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); +	if (!data) { +		dev_err(&pdev->dev, "failed to allocate data\n"); +		return -ENOMEM; +	} + +	data->banks = vt8500_banks; +	data->nbanks = ARRAY_SIZE(vt8500_banks); +	data->pins = vt8500_pins; +	data->npins = ARRAY_SIZE(vt8500_pins); +	data->groups = vt8500_groups; +	data->ngroups = ARRAY_SIZE(vt8500_groups); + +	return wmt_pinctrl_probe(pdev, data); +} + +static int vt8500_pinctrl_remove(struct platform_device *pdev) +{ +	return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { +	{ .compatible = "via,vt8500-pinctrl" }, +	{ /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { +	.probe	= vt8500_pinctrl_probe, +	.remove	= vt8500_pinctrl_remove, +	.driver = { +		.name	= "pinctrl-vt8500", +		.owner	= THIS_MODULE, +		.of_match_table	= wmt_pinctrl_of_match, +	}, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_DESCRIPTION("VIA VT8500 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8505.c b/drivers/pinctrl/vt8500/pinctrl-wm8505.c new file mode 100644 index 00000000000..483ba732694 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8505.c @@ -0,0 +1,532 @@ +/* + * Pinctrl data for Wondermedia WM8505 SoC + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8505_banks[] = { +	WMT_PINCTRL_BANK(0x64, 0x8C, 0xB4, 0xDC, NO_REG, NO_REG),	/* 0 */ +	WMT_PINCTRL_BANK(0x40, 0x68, 0x90, 0xB8, NO_REG, NO_REG),	/* 1 */ +	WMT_PINCTRL_BANK(0x44, 0x6C, 0x94, 0xBC, NO_REG, NO_REG),	/* 2 */ +	WMT_PINCTRL_BANK(0x48, 0x70, 0x98, 0xC0, NO_REG, NO_REG),	/* 3 */ +	WMT_PINCTRL_BANK(0x4C, 0x74, 0x9C, 0xC4, NO_REG, NO_REG),	/* 4 */ +	WMT_PINCTRL_BANK(0x50, 0x78, 0xA0, 0xC8, NO_REG, NO_REG),	/* 5 */ +	WMT_PINCTRL_BANK(0x54, 0x7C, 0xA4, 0xD0, NO_REG, NO_REG),	/* 6 */ +	WMT_PINCTRL_BANK(0x58, 0x80, 0xA8, 0xD4, NO_REG, NO_REG),	/* 7 */ +	WMT_PINCTRL_BANK(0x5C, 0x84, 0xAC, 0xD8, NO_REG, NO_REG),	/* 8 */ +	WMT_PINCTRL_BANK(0x60, 0x88, 0xB0, 0xDC, NO_REG, NO_REG),	/* 9 */ +	WMT_PINCTRL_BANK(0x500, 0x504, 0x508, 0x50C, NO_REG, NO_REG),	/* 10 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0	WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1	WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2	WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3	WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4	WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5	WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6	WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7	WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0		WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1		WMT_PIN(0, 17) +#define WMT_PIN_WAKEUP2		WMT_PIN(0, 18) +#define WMT_PIN_WAKEUP3		WMT_PIN(0, 19) +#define WMT_PIN_SUSGPIO0	WMT_PIN(0, 21) +#define WMT_PIN_SDDATA0		WMT_PIN(1, 0) +#define WMT_PIN_SDDATA1		WMT_PIN(1, 1) +#define WMT_PIN_SDDATA2		WMT_PIN(1, 2) +#define WMT_PIN_SDDATA3		WMT_PIN(1, 3) +#define WMT_PIN_MMCDATA0	WMT_PIN(1, 4) +#define WMT_PIN_MMCDATA1	WMT_PIN(1, 5) +#define WMT_PIN_MMCDATA2	WMT_PIN(1, 6) +#define WMT_PIN_MMCDATA3	WMT_PIN(1, 7) +#define WMT_PIN_VDIN0		WMT_PIN(2, 0) +#define WMT_PIN_VDIN1		WMT_PIN(2, 1) +#define WMT_PIN_VDIN2		WMT_PIN(2, 2) +#define WMT_PIN_VDIN3		WMT_PIN(2, 3) +#define WMT_PIN_VDIN4		WMT_PIN(2, 4) +#define WMT_PIN_VDIN5		WMT_PIN(2, 5) +#define WMT_PIN_VDIN6		WMT_PIN(2, 6) +#define WMT_PIN_VDIN7		WMT_PIN(2, 7) +#define WMT_PIN_VDOUT0		WMT_PIN(2, 8) +#define WMT_PIN_VDOUT1		WMT_PIN(2, 9) +#define WMT_PIN_VDOUT2		WMT_PIN(2, 10) +#define WMT_PIN_VDOUT3		WMT_PIN(2, 11) +#define WMT_PIN_VDOUT4		WMT_PIN(2, 12) +#define WMT_PIN_VDOUT5		WMT_PIN(2, 13) +#define WMT_PIN_VDOUT6		WMT_PIN(2, 14) +#define WMT_PIN_VDOUT7		WMT_PIN(2, 15) +#define WMT_PIN_VDOUT8		WMT_PIN(2, 16) +#define WMT_PIN_VDOUT9		WMT_PIN(2, 17) +#define WMT_PIN_VDOUT10		WMT_PIN(2, 18) +#define WMT_PIN_VDOUT11		WMT_PIN(2, 19) +#define WMT_PIN_VDOUT12		WMT_PIN(2, 20) +#define WMT_PIN_VDOUT13		WMT_PIN(2, 21) +#define WMT_PIN_VDOUT14		WMT_PIN(2, 22) +#define WMT_PIN_VDOUT15		WMT_PIN(2, 23) +#define WMT_PIN_VDOUT16		WMT_PIN(2, 24) +#define WMT_PIN_VDOUT17		WMT_PIN(2, 25) +#define WMT_PIN_VDOUT18		WMT_PIN(2, 26) +#define WMT_PIN_VDOUT19		WMT_PIN(2, 27) +#define WMT_PIN_VDOUT20		WMT_PIN(2, 28) +#define WMT_PIN_VDOUT21		WMT_PIN(2, 29) +#define WMT_PIN_VDOUT22		WMT_PIN(2, 30) +#define WMT_PIN_VDOUT23		WMT_PIN(2, 31) +#define WMT_PIN_VHSYNC		WMT_PIN(3, 0) +#define WMT_PIN_VVSYNC		WMT_PIN(3, 1) +#define WMT_PIN_VGAHSYNC	WMT_PIN(3, 2) +#define WMT_PIN_VGAVSYNC	WMT_PIN(3, 3) +#define WMT_PIN_VDHSYNC		WMT_PIN(3, 4) +#define WMT_PIN_VDVSYNC		WMT_PIN(3, 5) +#define WMT_PIN_NORD0		WMT_PIN(4, 0) +#define WMT_PIN_NORD1		WMT_PIN(4, 1) +#define WMT_PIN_NORD2		WMT_PIN(4, 2) +#define WMT_PIN_NORD3		WMT_PIN(4, 3) +#define WMT_PIN_NORD4		WMT_PIN(4, 4) +#define WMT_PIN_NORD5		WMT_PIN(4, 5) +#define WMT_PIN_NORD6		WMT_PIN(4, 6) +#define WMT_PIN_NORD7		WMT_PIN(4, 7) +#define WMT_PIN_NORD8		WMT_PIN(4, 8) +#define WMT_PIN_NORD9		WMT_PIN(4, 9) +#define WMT_PIN_NORD10		WMT_PIN(4, 10) +#define WMT_PIN_NORD11		WMT_PIN(4, 11) +#define WMT_PIN_NORD12		WMT_PIN(4, 12) +#define WMT_PIN_NORD13		WMT_PIN(4, 13) +#define WMT_PIN_NORD14		WMT_PIN(4, 14) +#define WMT_PIN_NORD15		WMT_PIN(4, 15) +#define WMT_PIN_NORA0		WMT_PIN(5, 0) +#define WMT_PIN_NORA1		WMT_PIN(5, 1) +#define WMT_PIN_NORA2		WMT_PIN(5, 2) +#define WMT_PIN_NORA3		WMT_PIN(5, 3) +#define WMT_PIN_NORA4		WMT_PIN(5, 4) +#define WMT_PIN_NORA5		WMT_PIN(5, 5) +#define WMT_PIN_NORA6		WMT_PIN(5, 6) +#define WMT_PIN_NORA7		WMT_PIN(5, 7) +#define WMT_PIN_NORA8		WMT_PIN(5, 8) +#define WMT_PIN_NORA9		WMT_PIN(5, 9) +#define WMT_PIN_NORA10		WMT_PIN(5, 10) +#define WMT_PIN_NORA11		WMT_PIN(5, 11) +#define WMT_PIN_NORA12		WMT_PIN(5, 12) +#define WMT_PIN_NORA13		WMT_PIN(5, 13) +#define WMT_PIN_NORA14		WMT_PIN(5, 14) +#define WMT_PIN_NORA15		WMT_PIN(5, 15) +#define WMT_PIN_NORA16		WMT_PIN(5, 16) +#define WMT_PIN_NORA17		WMT_PIN(5, 17) +#define WMT_PIN_NORA18		WMT_PIN(5, 18) +#define WMT_PIN_NORA19		WMT_PIN(5, 19) +#define WMT_PIN_NORA20		WMT_PIN(5, 20) +#define WMT_PIN_NORA21		WMT_PIN(5, 21) +#define WMT_PIN_NORA22		WMT_PIN(5, 22) +#define WMT_PIN_NORA23		WMT_PIN(5, 23) +#define WMT_PIN_NORA24		WMT_PIN(5, 24) +#define WMT_PIN_AC97SDI		WMT_PIN(6, 0) +#define WMT_PIN_AC97SYNC	WMT_PIN(6, 1) +#define WMT_PIN_AC97SDO		WMT_PIN(6, 2) +#define WMT_PIN_AC97BCLK	WMT_PIN(6, 3) +#define WMT_PIN_AC97RST		WMT_PIN(6, 4) +#define WMT_PIN_SFDO		WMT_PIN(7, 0) +#define WMT_PIN_SFCS0		WMT_PIN(7, 1) +#define WMT_PIN_SFCS1		WMT_PIN(7, 2) +#define WMT_PIN_SFCLK		WMT_PIN(7, 3) +#define WMT_PIN_SFDI		WMT_PIN(7, 4) +#define WMT_PIN_SPI0CLK		WMT_PIN(8, 0) +#define WMT_PIN_SPI0MISO	WMT_PIN(8, 1) +#define WMT_PIN_SPI0MOSI	WMT_PIN(8, 2) +#define WMT_PIN_SPI0SS		WMT_PIN(8, 3) +#define WMT_PIN_SPI1CLK		WMT_PIN(8, 4) +#define WMT_PIN_SPI1MISO	WMT_PIN(8, 5) +#define WMT_PIN_SPI1MOSI	WMT_PIN(8, 6) +#define WMT_PIN_SPI1SS		WMT_PIN(8, 7) +#define WMT_PIN_SPI2CLK		WMT_PIN(8, 8) +#define WMT_PIN_SPI2MISO	WMT_PIN(8, 9) +#define WMT_PIN_SPI2MOSI	WMT_PIN(8, 10) +#define WMT_PIN_SPI2SS		WMT_PIN(8, 11) +#define WMT_PIN_UART0_RTS	WMT_PIN(9, 0) +#define WMT_PIN_UART0_TXD	WMT_PIN(9, 1) +#define WMT_PIN_UART0_CTS	WMT_PIN(9, 2) +#define WMT_PIN_UART0_RXD	WMT_PIN(9, 3) +#define WMT_PIN_UART1_RTS	WMT_PIN(9, 4) +#define WMT_PIN_UART1_TXD	WMT_PIN(9, 5) +#define WMT_PIN_UART1_CTS	WMT_PIN(9, 6) +#define WMT_PIN_UART1_RXD	WMT_PIN(9, 7) +#define WMT_PIN_UART2_RTS	WMT_PIN(9, 8) +#define WMT_PIN_UART2_TXD	WMT_PIN(9, 9) +#define WMT_PIN_UART2_CTS	WMT_PIN(9, 10) +#define WMT_PIN_UART2_RXD	WMT_PIN(9, 11) +#define WMT_PIN_UART3_RTS	WMT_PIN(9, 12) +#define WMT_PIN_UART3_TXD	WMT_PIN(9, 13) +#define WMT_PIN_UART3_CTS	WMT_PIN(9, 14) +#define WMT_PIN_UART3_RXD	WMT_PIN(9, 15) +#define WMT_PIN_I2C0SCL		WMT_PIN(10, 0) +#define WMT_PIN_I2C0SDA		WMT_PIN(10, 1) +#define WMT_PIN_I2C1SCL		WMT_PIN(10, 2) +#define WMT_PIN_I2C1SDA		WMT_PIN(10, 3) +#define WMT_PIN_I2C2SCL		WMT_PIN(10, 4) +#define WMT_PIN_I2C2SDA		WMT_PIN(10, 5) + +static const struct pinctrl_pin_desc wm8505_pins[] = { +	PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), +	PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), +	PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), +	PINCTRL_PIN(WMT_PIN_WAKEUP2, "wakeup2"), +	PINCTRL_PIN(WMT_PIN_WAKEUP3, "wakeup3"), +	PINCTRL_PIN(WMT_PIN_SUSGPIO0, "susgpio0"), +	PINCTRL_PIN(WMT_PIN_SDDATA0, "sd_data0"), +	PINCTRL_PIN(WMT_PIN_SDDATA1, "sd_data1"), +	PINCTRL_PIN(WMT_PIN_SDDATA2, "sd_data2"), +	PINCTRL_PIN(WMT_PIN_SDDATA3, "sd_data3"), +	PINCTRL_PIN(WMT_PIN_MMCDATA0, "mmc_data0"), +	PINCTRL_PIN(WMT_PIN_MMCDATA1, "mmc_data1"), +	PINCTRL_PIN(WMT_PIN_MMCDATA2, "mmc_data2"), +	PINCTRL_PIN(WMT_PIN_MMCDATA3, "mmc_data3"), +	PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), +	PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), +	PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), +	PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), +	PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), +	PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), +	PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), +	PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), +	PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), +	PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), +	PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), +	PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), +	PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), +	PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), +	PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), +	PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), +	PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), +	PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), +	PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), +	PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), +	PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), +	PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), +	PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), +	PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), +	PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), +	PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), +	PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), +	PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), +	PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), +	PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), +	PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), +	PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), +	PINCTRL_PIN(WMT_PIN_VHSYNC, "v_hsync"), +	PINCTRL_PIN(WMT_PIN_VVSYNC, "v_vsync"), +	PINCTRL_PIN(WMT_PIN_VGAHSYNC, "vga_hsync"), +	PINCTRL_PIN(WMT_PIN_VGAVSYNC, "vga_vsync"), +	PINCTRL_PIN(WMT_PIN_VDHSYNC, "vd_hsync"), +	PINCTRL_PIN(WMT_PIN_VDVSYNC, "vd_vsync"), +	PINCTRL_PIN(WMT_PIN_NORD0, "nor_d0"), +	PINCTRL_PIN(WMT_PIN_NORD1, "nor_d1"), +	PINCTRL_PIN(WMT_PIN_NORD2, "nor_d2"), +	PINCTRL_PIN(WMT_PIN_NORD3, "nor_d3"), +	PINCTRL_PIN(WMT_PIN_NORD4, "nor_d4"), +	PINCTRL_PIN(WMT_PIN_NORD5, "nor_d5"), +	PINCTRL_PIN(WMT_PIN_NORD6, "nor_d6"), +	PINCTRL_PIN(WMT_PIN_NORD7, "nor_d7"), +	PINCTRL_PIN(WMT_PIN_NORD8, "nor_d8"), +	PINCTRL_PIN(WMT_PIN_NORD9, "nor_d9"), +	PINCTRL_PIN(WMT_PIN_NORD10, "nor_d10"), +	PINCTRL_PIN(WMT_PIN_NORD11, "nor_d11"), +	PINCTRL_PIN(WMT_PIN_NORD12, "nor_d12"), +	PINCTRL_PIN(WMT_PIN_NORD13, "nor_d13"), +	PINCTRL_PIN(WMT_PIN_NORD14, "nor_d14"), +	PINCTRL_PIN(WMT_PIN_NORD15, "nor_d15"), +	PINCTRL_PIN(WMT_PIN_NORA0, "nor_a0"), +	PINCTRL_PIN(WMT_PIN_NORA1, "nor_a1"), +	PINCTRL_PIN(WMT_PIN_NORA2, "nor_a2"), +	PINCTRL_PIN(WMT_PIN_NORA3, "nor_a3"), +	PINCTRL_PIN(WMT_PIN_NORA4, "nor_a4"), +	PINCTRL_PIN(WMT_PIN_NORA5, "nor_a5"), +	PINCTRL_PIN(WMT_PIN_NORA6, "nor_a6"), +	PINCTRL_PIN(WMT_PIN_NORA7, "nor_a7"), +	PINCTRL_PIN(WMT_PIN_NORA8, "nor_a8"), +	PINCTRL_PIN(WMT_PIN_NORA9, "nor_a9"), +	PINCTRL_PIN(WMT_PIN_NORA10, "nor_a10"), +	PINCTRL_PIN(WMT_PIN_NORA11, "nor_a11"), +	PINCTRL_PIN(WMT_PIN_NORA12, "nor_a12"), +	PINCTRL_PIN(WMT_PIN_NORA13, "nor_a13"), +	PINCTRL_PIN(WMT_PIN_NORA14, "nor_a14"), +	PINCTRL_PIN(WMT_PIN_NORA15, "nor_a15"), +	PINCTRL_PIN(WMT_PIN_NORA16, "nor_a16"), +	PINCTRL_PIN(WMT_PIN_NORA17, "nor_a17"), +	PINCTRL_PIN(WMT_PIN_NORA18, "nor_a18"), +	PINCTRL_PIN(WMT_PIN_NORA19, "nor_a19"), +	PINCTRL_PIN(WMT_PIN_NORA20, "nor_a20"), +	PINCTRL_PIN(WMT_PIN_NORA21, "nor_a21"), +	PINCTRL_PIN(WMT_PIN_NORA22, "nor_a22"), +	PINCTRL_PIN(WMT_PIN_NORA23, "nor_a23"), +	PINCTRL_PIN(WMT_PIN_NORA24, "nor_a24"), +	PINCTRL_PIN(WMT_PIN_AC97SDI, "ac97_sdi"), +	PINCTRL_PIN(WMT_PIN_AC97SYNC, "ac97_sync"), +	PINCTRL_PIN(WMT_PIN_AC97SDO, "ac97_sdo"), +	PINCTRL_PIN(WMT_PIN_AC97BCLK, "ac97_bclk"), +	PINCTRL_PIN(WMT_PIN_AC97RST, "ac97_rst"), +	PINCTRL_PIN(WMT_PIN_SFDO, "sf_do"), +	PINCTRL_PIN(WMT_PIN_SFCS0, "sf_cs0"), +	PINCTRL_PIN(WMT_PIN_SFCS1, "sf_cs1"), +	PINCTRL_PIN(WMT_PIN_SFCLK, "sf_clk"), +	PINCTRL_PIN(WMT_PIN_SFDI, "sf_di"), +	PINCTRL_PIN(WMT_PIN_SPI0CLK, "spi0_clk"), +	PINCTRL_PIN(WMT_PIN_SPI0MISO, "spi0_miso"), +	PINCTRL_PIN(WMT_PIN_SPI0MOSI, "spi0_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI0SS, "spi0_ss"), +	PINCTRL_PIN(WMT_PIN_SPI1CLK, "spi1_clk"), +	PINCTRL_PIN(WMT_PIN_SPI1MISO, "spi1_miso"), +	PINCTRL_PIN(WMT_PIN_SPI1MOSI, "spi1_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI1SS, "spi1_ss"), +	PINCTRL_PIN(WMT_PIN_SPI2CLK, "spi2_clk"), +	PINCTRL_PIN(WMT_PIN_SPI2MISO, "spi2_miso"), +	PINCTRL_PIN(WMT_PIN_SPI2MOSI, "spi2_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI2SS, "spi2_ss"), +	PINCTRL_PIN(WMT_PIN_UART0_RTS, "uart0_rts"), +	PINCTRL_PIN(WMT_PIN_UART0_TXD, "uart0_txd"), +	PINCTRL_PIN(WMT_PIN_UART0_CTS, "uart0_cts"), +	PINCTRL_PIN(WMT_PIN_UART0_RXD, "uart0_rxd"), +	PINCTRL_PIN(WMT_PIN_UART1_RTS, "uart1_rts"), +	PINCTRL_PIN(WMT_PIN_UART1_TXD, "uart1_txd"), +	PINCTRL_PIN(WMT_PIN_UART1_CTS, "uart1_cts"), +	PINCTRL_PIN(WMT_PIN_UART1_RXD, "uart1_rxd"), +	PINCTRL_PIN(WMT_PIN_UART2_RTS, "uart2_rts"), +	PINCTRL_PIN(WMT_PIN_UART2_TXD, "uart2_txd"), +	PINCTRL_PIN(WMT_PIN_UART2_CTS, "uart2_cts"), +	PINCTRL_PIN(WMT_PIN_UART2_RXD, "uart2_rxd"), +	PINCTRL_PIN(WMT_PIN_UART3_RTS, "uart3_rts"), +	PINCTRL_PIN(WMT_PIN_UART3_TXD, "uart3_txd"), +	PINCTRL_PIN(WMT_PIN_UART3_CTS, "uart3_cts"), +	PINCTRL_PIN(WMT_PIN_UART3_RXD, "uart3_rxd"), +	PINCTRL_PIN(WMT_PIN_I2C0SCL, "i2c0_scl"), +	PINCTRL_PIN(WMT_PIN_I2C0SDA, "i2c0_sda"), +	PINCTRL_PIN(WMT_PIN_I2C1SCL, "i2c1_scl"), +	PINCTRL_PIN(WMT_PIN_I2C1SDA, "i2c1_sda"), +	PINCTRL_PIN(WMT_PIN_I2C2SCL, "i2c2_scl"), +	PINCTRL_PIN(WMT_PIN_I2C2SDA, "i2c2_sda"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8505_groups[] = { +	"extgpio0", +	"extgpio1", +	"extgpio2", +	"extgpio3", +	"extgpio4", +	"extgpio5", +	"extgpio6", +	"extgpio7", +	"wakeup0", +	"wakeup1", +	"wakeup2", +	"wakeup3", +	"susgpio0", +	"sd_data0", +	"sd_data1", +	"sd_data2", +	"sd_data3", +	"mmc_data0", +	"mmc_data1", +	"mmc_data2", +	"mmc_data3", +	"vdin0", +	"vdin1", +	"vdin2", +	"vdin3", +	"vdin4", +	"vdin5", +	"vdin6", +	"vdin7", +	"vdout0", +	"vdout1", +	"vdout2", +	"vdout3", +	"vdout4", +	"vdout5", +	"vdout6", +	"vdout7", +	"vdout8", +	"vdout9", +	"vdout10", +	"vdout11", +	"vdout12", +	"vdout13", +	"vdout14", +	"vdout15", +	"vdout16", +	"vdout17", +	"vdout18", +	"vdout19", +	"vdout20", +	"vdout21", +	"vdout22", +	"vdout23", +	"v_hsync", +	"v_vsync", +	"vga_hsync", +	"vga_vsync", +	"vd_hsync", +	"vd_vsync", +	"nor_d0", +	"nor_d1", +	"nor_d2", +	"nor_d3", +	"nor_d4", +	"nor_d5", +	"nor_d6", +	"nor_d7", +	"nor_d8", +	"nor_d9", +	"nor_d10", +	"nor_d11", +	"nor_d12", +	"nor_d13", +	"nor_d14", +	"nor_d15", +	"nor_a0", +	"nor_a1", +	"nor_a2", +	"nor_a3", +	"nor_a4", +	"nor_a5", +	"nor_a6", +	"nor_a7", +	"nor_a8", +	"nor_a9", +	"nor_a10", +	"nor_a11", +	"nor_a12", +	"nor_a13", +	"nor_a14", +	"nor_a15", +	"nor_a16", +	"nor_a17", +	"nor_a18", +	"nor_a19", +	"nor_a20", +	"nor_a21", +	"nor_a22", +	"nor_a23", +	"nor_a24", +	"ac97_sdi", +	"ac97_sync", +	"ac97_sdo", +	"ac97_bclk", +	"ac97_rst", +	"sf_do", +	"sf_cs0", +	"sf_cs1", +	"sf_clk", +	"sf_di", +	"spi0_clk", +	"spi0_miso", +	"spi0_mosi", +	"spi0_ss", +	"spi1_clk", +	"spi1_miso", +	"spi1_mosi", +	"spi1_ss", +	"spi2_clk", +	"spi2_miso", +	"spi2_mosi", +	"spi2_ss", +	"uart0_rts", +	"uart0_txd", +	"uart0_cts", +	"uart0_rxd", +	"uart1_rts", +	"uart1_txd", +	"uart1_cts", +	"uart1_rxd", +	"uart2_rts", +	"uart2_txd", +	"uart2_cts", +	"uart2_rxd", +	"uart3_rts", +	"uart3_txd", +	"uart3_cts", +	"uart3_rxd", +	"i2c0_scl", +	"i2c0_sda", +	"i2c1_scl", +	"i2c1_sda", +	"i2c2_scl", +	"i2c2_sda", +}; + +static int wm8505_pinctrl_probe(struct platform_device *pdev) +{ +	struct wmt_pinctrl_data *data; + +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); +	if (!data) { +		dev_err(&pdev->dev, "failed to allocate data\n"); +		return -ENOMEM; +	} + +	data->banks = wm8505_banks; +	data->nbanks = ARRAY_SIZE(wm8505_banks); +	data->pins = wm8505_pins; +	data->npins = ARRAY_SIZE(wm8505_pins); +	data->groups = wm8505_groups; +	data->ngroups = ARRAY_SIZE(wm8505_groups); + +	return wmt_pinctrl_probe(pdev, data); +} + +static int wm8505_pinctrl_remove(struct platform_device *pdev) +{ +	return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { +	{ .compatible = "wm,wm8505-pinctrl" }, +	{ /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { +	.probe	= wm8505_pinctrl_probe, +	.remove	= wm8505_pinctrl_remove, +	.driver = { +		.name	= "pinctrl-wm8505", +		.owner	= THIS_MODULE, +		.of_match_table	= wmt_pinctrl_of_match, +	}, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_DESCRIPTION("Wondermedia WM8505 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8650.c b/drivers/pinctrl/vt8500/pinctrl-wm8650.c new file mode 100644 index 00000000000..7de57f06315 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8650.c @@ -0,0 +1,370 @@ +/* + * Pinctrl data for Wondermedia WM8650 SoC + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8650_banks[] = { +	WMT_PINCTRL_BANK(0x40, 0x80, 0xC0, 0x00, 0x480, 0x4C0),		/* 0 */ +	WMT_PINCTRL_BANK(0x44, 0x84, 0xC4, 0x04, 0x484, 0x4C4),		/* 1 */ +	WMT_PINCTRL_BANK(0x48, 0x88, 0xC8, 0x08, 0x488, 0x4C8),		/* 2 */ +	WMT_PINCTRL_BANK(0x4C, 0x8C, 0xCC, 0x0C, 0x48C, 0x4CC),		/* 3 */ +	WMT_PINCTRL_BANK(0x50, 0x90, 0xD0, 0x10, 0x490, 0x4D0),		/* 4 */ +	WMT_PINCTRL_BANK(0x54, 0x94, 0xD4, 0x14, 0x494, 0x4D4),		/* 5 */ +	WMT_PINCTRL_BANK(0x58, 0x98, 0xD8, 0x18, 0x498, 0x4D8),		/* 6 */ +	WMT_PINCTRL_BANK(0x5C, 0x9C, 0xDC, 0x1C, 0x49C, 0x4DC),		/* 7 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0	WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1	WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2	WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3	WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4	WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5	WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6	WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7	WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0		WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1		WMT_PIN(0, 17) +#define WMT_PIN_SUSGPIO0	WMT_PIN(0, 21) +#define WMT_PIN_SD0CD		WMT_PIN(0, 28) +#define WMT_PIN_SD1CD		WMT_PIN(0, 29) +#define WMT_PIN_VDOUT0		WMT_PIN(1, 0) +#define WMT_PIN_VDOUT1		WMT_PIN(1, 1) +#define WMT_PIN_VDOUT2		WMT_PIN(1, 2) +#define WMT_PIN_VDOUT3		WMT_PIN(1, 3) +#define WMT_PIN_VDOUT4		WMT_PIN(1, 4) +#define WMT_PIN_VDOUT5		WMT_PIN(1, 5) +#define WMT_PIN_VDOUT6		WMT_PIN(1, 6) +#define WMT_PIN_VDOUT7		WMT_PIN(1, 7) +#define WMT_PIN_VDOUT8		WMT_PIN(1, 8) +#define WMT_PIN_VDOUT9		WMT_PIN(1, 9) +#define WMT_PIN_VDOUT10		WMT_PIN(1, 10) +#define WMT_PIN_VDOUT11		WMT_PIN(1, 11) +#define WMT_PIN_VDOUT12		WMT_PIN(1, 12) +#define WMT_PIN_VDOUT13		WMT_PIN(1, 13) +#define WMT_PIN_VDOUT14		WMT_PIN(1, 14) +#define WMT_PIN_VDOUT15		WMT_PIN(1, 15) +#define WMT_PIN_VDOUT16		WMT_PIN(1, 16) +#define WMT_PIN_VDOUT17		WMT_PIN(1, 17) +#define WMT_PIN_VDOUT18		WMT_PIN(1, 18) +#define WMT_PIN_VDOUT19		WMT_PIN(1, 19) +#define WMT_PIN_VDOUT20		WMT_PIN(1, 20) +#define WMT_PIN_VDOUT21		WMT_PIN(1, 21) +#define WMT_PIN_VDOUT22		WMT_PIN(1, 22) +#define WMT_PIN_VDOUT23		WMT_PIN(1, 23) +#define WMT_PIN_VDIN0		WMT_PIN(2, 0) +#define WMT_PIN_VDIN1		WMT_PIN(2, 1) +#define WMT_PIN_VDIN2		WMT_PIN(2, 2) +#define WMT_PIN_VDIN3		WMT_PIN(2, 3) +#define WMT_PIN_VDIN4		WMT_PIN(2, 4) +#define WMT_PIN_VDIN5		WMT_PIN(2, 5) +#define WMT_PIN_VDIN6		WMT_PIN(2, 6) +#define WMT_PIN_VDIN7		WMT_PIN(2, 7) +#define WMT_PIN_I2C1SCL		WMT_PIN(2, 12) +#define WMT_PIN_I2C1SDA		WMT_PIN(2, 13) +#define WMT_PIN_SPI0MOSI	WMT_PIN(2, 24) +#define WMT_PIN_SPI0MISO	WMT_PIN(2, 25) +#define WMT_PIN_SPI0SS0		WMT_PIN(2, 26) +#define WMT_PIN_SPI0CLK		WMT_PIN(2, 27) +#define WMT_PIN_SD0DATA0	WMT_PIN(3, 8) +#define WMT_PIN_SD0DATA1	WMT_PIN(3, 9) +#define WMT_PIN_SD0DATA2	WMT_PIN(3, 10) +#define WMT_PIN_SD0DATA3	WMT_PIN(3, 11) +#define WMT_PIN_SD0CLK		WMT_PIN(3, 12) +#define WMT_PIN_SD0WP		WMT_PIN(3, 13) +#define WMT_PIN_SD0CMD		WMT_PIN(3, 14) +#define WMT_PIN_SD1DATA0	WMT_PIN(3, 24) +#define WMT_PIN_SD1DATA1	WMT_PIN(3, 25) +#define WMT_PIN_SD1DATA2	WMT_PIN(3, 26) +#define WMT_PIN_SD1DATA3	WMT_PIN(3, 27) +#define WMT_PIN_SD1DATA4	WMT_PIN(3, 28) +#define WMT_PIN_SD1DATA5	WMT_PIN(3, 29) +#define WMT_PIN_SD1DATA6	WMT_PIN(3, 30) +#define WMT_PIN_SD1DATA7	WMT_PIN(3, 31) +#define WMT_PIN_I2C0SCL		WMT_PIN(5, 8) +#define WMT_PIN_I2C0SDA		WMT_PIN(5, 9) +#define WMT_PIN_UART0RTS	WMT_PIN(5, 16) +#define WMT_PIN_UART0TXD	WMT_PIN(5, 17) +#define WMT_PIN_UART0CTS	WMT_PIN(5, 18) +#define WMT_PIN_UART0RXD	WMT_PIN(5, 19) +#define WMT_PIN_UART1RTS	WMT_PIN(5, 20) +#define WMT_PIN_UART1TXD	WMT_PIN(5, 21) +#define WMT_PIN_UART1CTS	WMT_PIN(5, 22) +#define WMT_PIN_UART1RXD	WMT_PIN(5, 23) +#define WMT_PIN_UART2RTS	WMT_PIN(5, 24) +#define WMT_PIN_UART2TXD	WMT_PIN(5, 25) +#define WMT_PIN_UART2CTS	WMT_PIN(5, 26) +#define WMT_PIN_UART2RXD	WMT_PIN(5, 27) +#define WMT_PIN_UART3RTS	WMT_PIN(5, 28) +#define WMT_PIN_UART3TXD	WMT_PIN(5, 29) +#define WMT_PIN_UART3CTS	WMT_PIN(5, 30) +#define WMT_PIN_UART3RXD	WMT_PIN(5, 31) +#define WMT_PIN_KPADROW0	WMT_PIN(6, 16) +#define WMT_PIN_KPADROW1	WMT_PIN(6, 17) +#define WMT_PIN_KPADCOL0	WMT_PIN(6, 18) +#define WMT_PIN_KPADCOL1	WMT_PIN(6, 19) +#define WMT_PIN_SD1CLK		WMT_PIN(7, 0) +#define WMT_PIN_SD1CMD		WMT_PIN(7, 1) +#define WMT_PIN_SD1WP		WMT_PIN(7, 13) + +static const struct pinctrl_pin_desc wm8650_pins[] = { +	PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), +	PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), +	PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), +	PINCTRL_PIN(WMT_PIN_SUSGPIO0, "susgpio0"), +	PINCTRL_PIN(WMT_PIN_SD0CD, "sd0_cd"), +	PINCTRL_PIN(WMT_PIN_SD1CD, "sd1_cd"), +	PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), +	PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), +	PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), +	PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), +	PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), +	PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), +	PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), +	PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), +	PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), +	PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), +	PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), +	PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), +	PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), +	PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), +	PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), +	PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), +	PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), +	PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), +	PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), +	PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), +	PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), +	PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), +	PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), +	PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), +	PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), +	PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), +	PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), +	PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), +	PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), +	PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), +	PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), +	PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), +	PINCTRL_PIN(WMT_PIN_I2C1SCL, "i2c1_scl"), +	PINCTRL_PIN(WMT_PIN_I2C1SDA, "i2c1_sda"), +	PINCTRL_PIN(WMT_PIN_SPI0MOSI, "spi0_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI0MISO, "spi0_miso"), +	PINCTRL_PIN(WMT_PIN_SPI0SS0, "spi0_ss0"), +	PINCTRL_PIN(WMT_PIN_SPI0CLK, "spi0_clk"), +	PINCTRL_PIN(WMT_PIN_SD0DATA0, "sd0_data0"), +	PINCTRL_PIN(WMT_PIN_SD0DATA1, "sd0_data1"), +	PINCTRL_PIN(WMT_PIN_SD0DATA2, "sd0_data2"), +	PINCTRL_PIN(WMT_PIN_SD0DATA3, "sd0_data3"), +	PINCTRL_PIN(WMT_PIN_SD0CLK, "sd0_clk"), +	PINCTRL_PIN(WMT_PIN_SD0WP, "sd0_wp"), +	PINCTRL_PIN(WMT_PIN_SD0CMD, "sd0_cmd"), +	PINCTRL_PIN(WMT_PIN_SD1DATA0, "sd1_data0"), +	PINCTRL_PIN(WMT_PIN_SD1DATA1, "sd1_data1"), +	PINCTRL_PIN(WMT_PIN_SD1DATA2, "sd1_data2"), +	PINCTRL_PIN(WMT_PIN_SD1DATA3, "sd1_data3"), +	PINCTRL_PIN(WMT_PIN_SD1DATA4, "sd1_data4"), +	PINCTRL_PIN(WMT_PIN_SD1DATA5, "sd1_data5"), +	PINCTRL_PIN(WMT_PIN_SD1DATA6, "sd1_data6"), +	PINCTRL_PIN(WMT_PIN_SD1DATA7, "sd1_data7"), +	PINCTRL_PIN(WMT_PIN_I2C0SCL, "i2c0_scl"), +	PINCTRL_PIN(WMT_PIN_I2C0SDA, "i2c0_sda"), +	PINCTRL_PIN(WMT_PIN_UART0RTS, "uart0_rts"), +	PINCTRL_PIN(WMT_PIN_UART0TXD, "uart0_txd"), +	PINCTRL_PIN(WMT_PIN_UART0CTS, "uart0_cts"), +	PINCTRL_PIN(WMT_PIN_UART0RXD, "uart0_rxd"), +	PINCTRL_PIN(WMT_PIN_UART1RTS, "uart1_rts"), +	PINCTRL_PIN(WMT_PIN_UART1TXD, "uart1_txd"), +	PINCTRL_PIN(WMT_PIN_UART1CTS, "uart1_cts"), +	PINCTRL_PIN(WMT_PIN_UART1RXD, "uart1_rxd"), +	PINCTRL_PIN(WMT_PIN_UART2RTS, "uart2_rts"), +	PINCTRL_PIN(WMT_PIN_UART2TXD, "uart2_txd"), +	PINCTRL_PIN(WMT_PIN_UART2CTS, "uart2_cts"), +	PINCTRL_PIN(WMT_PIN_UART2RXD, "uart2_rxd"), +	PINCTRL_PIN(WMT_PIN_UART3RTS, "uart3_rts"), +	PINCTRL_PIN(WMT_PIN_UART3TXD, "uart3_txd"), +	PINCTRL_PIN(WMT_PIN_UART3CTS, "uart3_cts"), +	PINCTRL_PIN(WMT_PIN_UART3RXD, "uart3_rxd"), +	PINCTRL_PIN(WMT_PIN_KPADROW0, "kpadrow0"), +	PINCTRL_PIN(WMT_PIN_KPADROW1, "kpadrow1"), +	PINCTRL_PIN(WMT_PIN_KPADCOL0, "kpadcol0"), +	PINCTRL_PIN(WMT_PIN_KPADCOL1, "kpadcol1"), +	PINCTRL_PIN(WMT_PIN_SD1CLK, "sd1_clk"), +	PINCTRL_PIN(WMT_PIN_SD1CMD, "sd1_cmd"), +	PINCTRL_PIN(WMT_PIN_SD1WP, "sd1_wp"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8650_groups[] = { +	"extgpio0", +	"extgpio1", +	"extgpio2", +	"extgpio3", +	"extgpio4", +	"extgpio5", +	"extgpio6", +	"extgpio7", +	"wakeup0", +	"wakeup1", +	"susgpio0", +	"sd0_cd", +	"sd1_cd", +	"vdout0", +	"vdout1", +	"vdout2", +	"vdout3", +	"vdout4", +	"vdout5", +	"vdout6", +	"vdout7", +	"vdout8", +	"vdout9", +	"vdout10", +	"vdout11", +	"vdout12", +	"vdout13", +	"vdout14", +	"vdout15", +	"vdout16", +	"vdout17", +	"vdout18", +	"vdout19", +	"vdout20", +	"vdout21", +	"vdout22", +	"vdout23", +	"vdin0", +	"vdin1", +	"vdin2", +	"vdin3", +	"vdin4", +	"vdin5", +	"vdin6", +	"vdin7", +	"i2c1_scl", +	"i2c1_sda", +	"spi0_mosi", +	"spi0_miso", +	"spi0_ss0", +	"spi0_clk", +	"sd0_data0", +	"sd0_data1", +	"sd0_data2", +	"sd0_data3", +	"sd0_clk", +	"sd0_wp", +	"sd0_cmd", +	"sd1_data0", +	"sd1_data1", +	"sd1_data2", +	"sd1_data3", +	"sd1_data4", +	"sd1_data5", +	"sd1_data6", +	"sd1_data7", +	"i2c0_scl", +	"i2c0_sda", +	"uart0_rts", +	"uart0_txd", +	"uart0_cts", +	"uart0_rxd", +	"uart1_rts", +	"uart1_txd", +	"uart1_cts", +	"uart1_rxd", +	"uart2_rts", +	"uart2_txd", +	"uart2_cts", +	"uart2_rxd", +	"uart3_rts", +	"uart3_txd", +	"uart3_cts", +	"uart3_rxd", +	"kpadrow0", +	"kpadrow1", +	"kpadcol0", +	"kpadcol1", +	"sd1_clk", +	"sd1_cmd", +	"sd1_wp", +}; + +static int wm8650_pinctrl_probe(struct platform_device *pdev) +{ +	struct wmt_pinctrl_data *data; + +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); +	if (!data) { +		dev_err(&pdev->dev, "failed to allocate data\n"); +		return -ENOMEM; +	} + +	data->banks = wm8650_banks; +	data->nbanks = ARRAY_SIZE(wm8650_banks); +	data->pins = wm8650_pins; +	data->npins = ARRAY_SIZE(wm8650_pins); +	data->groups = wm8650_groups; +	data->ngroups = ARRAY_SIZE(wm8650_groups); + +	return wmt_pinctrl_probe(pdev, data); +} + +static int wm8650_pinctrl_remove(struct platform_device *pdev) +{ +	return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { +	{ .compatible = "wm,wm8650-pinctrl" }, +	{ /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { +	.probe	= wm8650_pinctrl_probe, +	.remove	= wm8650_pinctrl_remove, +	.driver = { +		.name	= "pinctrl-wm8650", +		.owner	= THIS_MODULE, +		.of_match_table	= wmt_pinctrl_of_match, +	}, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_DESCRIPTION("Wondermedia WM8650 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8750.c b/drivers/pinctrl/vt8500/pinctrl-wm8750.c new file mode 100644 index 00000000000..b964cc55056 --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8750.c @@ -0,0 +1,409 @@ +/* + * Pinctrl data for Wondermedia WM8750 SoC + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8750_banks[] = { +	WMT_PINCTRL_BANK(0x40, 0x80, 0xC0, 0x00, 0x480, 0x4C0),	/* 0 */ +	WMT_PINCTRL_BANK(0x44, 0x84, 0xC4, 0x04, 0x484, 0x4C4),	/* 1 */ +	WMT_PINCTRL_BANK(0x48, 0x88, 0xC8, 0x08, 0x488, 0x4C8),	/* 2 */ +	WMT_PINCTRL_BANK(0x4C, 0x8C, 0xCC, 0x0C, 0x48C, 0x4CC),	/* 3 */ +	WMT_PINCTRL_BANK(0x50, 0x90, 0xD0, 0x10, 0x490, 0x4D0),	/* 4 */ +	WMT_PINCTRL_BANK(0x54, 0x94, 0xD4, 0x14, 0x494, 0x4D4),	/* 5 */ +	WMT_PINCTRL_BANK(0x58, 0x98, 0xD8, 0x18, 0x498, 0x4D8),	/* 6 */ +	WMT_PINCTRL_BANK(0x5C, 0x9C, 0xDC, 0x1C, 0x49C, 0x4DC),	/* 7 */ +	WMT_PINCTRL_BANK(0x60, 0xA0, 0xE0, 0x20, 0x4A0, 0x4E0),	/* 8 */ +	WMT_PINCTRL_BANK(0x70, 0xB0, 0xF0, 0x30, 0x4B0, 0x4F0),	/* 9 */ +	WMT_PINCTRL_BANK(0x7C, 0xBC, 0xDC, 0x3C, 0x4BC, 0x4FC),	/* 10 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0	WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1	WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2	WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3	WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4	WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5	WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6	WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7	WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0		WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1		WMT_PIN(0, 16) +#define WMT_PIN_SD0CD		WMT_PIN(0, 28) +#define WMT_PIN_VDOUT0		WMT_PIN(1, 0) +#define WMT_PIN_VDOUT1		WMT_PIN(1, 1) +#define WMT_PIN_VDOUT2		WMT_PIN(1, 2) +#define WMT_PIN_VDOUT3		WMT_PIN(1, 3) +#define WMT_PIN_VDOUT4		WMT_PIN(1, 4) +#define WMT_PIN_VDOUT5		WMT_PIN(1, 5) +#define WMT_PIN_VDOUT6		WMT_PIN(1, 6) +#define WMT_PIN_VDOUT7		WMT_PIN(1, 7) +#define WMT_PIN_VDOUT8		WMT_PIN(1, 8) +#define WMT_PIN_VDOUT9		WMT_PIN(1, 9) +#define WMT_PIN_VDOUT10		WMT_PIN(1, 10) +#define WMT_PIN_VDOUT11		WMT_PIN(1, 11) +#define WMT_PIN_VDOUT12		WMT_PIN(1, 12) +#define WMT_PIN_VDOUT13		WMT_PIN(1, 13) +#define WMT_PIN_VDOUT14		WMT_PIN(1, 14) +#define WMT_PIN_VDOUT15		WMT_PIN(1, 15) +#define WMT_PIN_VDOUT16		WMT_PIN(1, 16) +#define WMT_PIN_VDOUT17		WMT_PIN(1, 17) +#define WMT_PIN_VDOUT18		WMT_PIN(1, 18) +#define WMT_PIN_VDOUT19		WMT_PIN(1, 19) +#define WMT_PIN_VDOUT20		WMT_PIN(1, 20) +#define WMT_PIN_VDOUT21		WMT_PIN(1, 21) +#define WMT_PIN_VDOUT22		WMT_PIN(1, 22) +#define WMT_PIN_VDOUT23		WMT_PIN(1, 23) +#define WMT_PIN_VDIN0		WMT_PIN(2, 0) +#define WMT_PIN_VDIN1		WMT_PIN(2, 1) +#define WMT_PIN_VDIN2		WMT_PIN(2, 2) +#define WMT_PIN_VDIN3		WMT_PIN(2, 3) +#define WMT_PIN_VDIN4		WMT_PIN(2, 4) +#define WMT_PIN_VDIN5		WMT_PIN(2, 5) +#define WMT_PIN_VDIN6		WMT_PIN(2, 6) +#define WMT_PIN_VDIN7		WMT_PIN(2, 7) +#define WMT_PIN_SPI0_MOSI	WMT_PIN(2, 24) +#define WMT_PIN_SPI0_MISO	WMT_PIN(2, 25) +#define WMT_PIN_SPI0_SS		WMT_PIN(2, 26) +#define WMT_PIN_SPI0_CLK	WMT_PIN(2, 27) +#define WMT_PIN_SPI0_SSB	WMT_PIN(2, 28) +#define WMT_PIN_SD0CLK		WMT_PIN(3, 17) +#define WMT_PIN_SD0CMD		WMT_PIN(3, 18) +#define WMT_PIN_SD0WP		WMT_PIN(3, 19) +#define WMT_PIN_SD0DATA0	WMT_PIN(3, 20) +#define WMT_PIN_SD0DATA1	WMT_PIN(3, 21) +#define WMT_PIN_SD0DATA2	WMT_PIN(3, 22) +#define WMT_PIN_SD0DATA3	WMT_PIN(3, 23) +#define WMT_PIN_SD1DATA0	WMT_PIN(3, 24) +#define WMT_PIN_SD1DATA1	WMT_PIN(3, 25) +#define WMT_PIN_SD1DATA2	WMT_PIN(3, 26) +#define WMT_PIN_SD1DATA3	WMT_PIN(3, 27) +#define WMT_PIN_SD1DATA4	WMT_PIN(3, 28) +#define WMT_PIN_SD1DATA5	WMT_PIN(3, 29) +#define WMT_PIN_SD1DATA6	WMT_PIN(3, 30) +#define WMT_PIN_SD1DATA7	WMT_PIN(3, 31) +#define WMT_PIN_I2C0_SCL	WMT_PIN(5, 8) +#define WMT_PIN_I2C0_SDA	WMT_PIN(5, 9) +#define WMT_PIN_I2C1_SCL	WMT_PIN(5, 10) +#define WMT_PIN_I2C1_SDA	WMT_PIN(5, 11) +#define WMT_PIN_I2C2_SCL	WMT_PIN(5, 12) +#define WMT_PIN_I2C2_SDA	WMT_PIN(5, 13) +#define WMT_PIN_UART0_RTS	WMT_PIN(5, 16) +#define WMT_PIN_UART0_TXD	WMT_PIN(5, 17) +#define WMT_PIN_UART0_CTS	WMT_PIN(5, 18) +#define WMT_PIN_UART0_RXD	WMT_PIN(5, 19) +#define WMT_PIN_UART1_RTS	WMT_PIN(5, 20) +#define WMT_PIN_UART1_TXD	WMT_PIN(5, 21) +#define WMT_PIN_UART1_CTS	WMT_PIN(5, 22) +#define WMT_PIN_UART1_RXD	WMT_PIN(5, 23) +#define WMT_PIN_UART2_RTS	WMT_PIN(5, 24) +#define WMT_PIN_UART2_TXD	WMT_PIN(5, 25) +#define WMT_PIN_UART2_CTS	WMT_PIN(5, 26) +#define WMT_PIN_UART2_RXD	WMT_PIN(5, 27) +#define WMT_PIN_UART3_RTS	WMT_PIN(5, 28) +#define WMT_PIN_UART3_TXD	WMT_PIN(5, 29) +#define WMT_PIN_UART3_CTS	WMT_PIN(5, 30) +#define WMT_PIN_UART3_RXD	WMT_PIN(5, 31) +#define WMT_PIN_SD2CD		WMT_PIN(6, 0) +#define WMT_PIN_SD2DATA3	WMT_PIN(6, 1) +#define WMT_PIN_SD2DATA0	WMT_PIN(6, 2) +#define WMT_PIN_SD2WP		WMT_PIN(6, 3) +#define WMT_PIN_SD2DATA1	WMT_PIN(6, 4) +#define WMT_PIN_SD2DATA2	WMT_PIN(6, 5) +#define WMT_PIN_SD2CMD		WMT_PIN(6, 6) +#define WMT_PIN_SD2CLK		WMT_PIN(6, 7) +#define WMT_PIN_SD2PWR		WMT_PIN(6, 9) +#define WMT_PIN_SD1CLK		WMT_PIN(7, 0) +#define WMT_PIN_SD1CMD		WMT_PIN(7, 1) +#define WMT_PIN_SD1PWR		WMT_PIN(7, 10) +#define WMT_PIN_SD1WP		WMT_PIN(7, 11) +#define WMT_PIN_SD1CD		WMT_PIN(7, 12) +#define WMT_PIN_SPI0SS3		WMT_PIN(7, 24) +#define WMT_PIN_SPI0SS2		WMT_PIN(7, 25) +#define WMT_PIN_PWMOUT1		WMT_PIN(7, 26) +#define WMT_PIN_PWMOUT0		WMT_PIN(7, 27) + +static const struct pinctrl_pin_desc wm8750_pins[] = { +	PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), +	PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), +	PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), +	PINCTRL_PIN(WMT_PIN_SD0CD, "sd0_cd"), +	PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), +	PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), +	PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), +	PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), +	PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), +	PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), +	PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), +	PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), +	PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), +	PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), +	PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), +	PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), +	PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), +	PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), +	PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), +	PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), +	PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), +	PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), +	PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), +	PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), +	PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), +	PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), +	PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), +	PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), +	PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), +	PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), +	PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), +	PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), +	PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), +	PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), +	PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), +	PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), +	PINCTRL_PIN(WMT_PIN_SPI0_MOSI, "spi0_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI0_MISO, "spi0_miso"), +	PINCTRL_PIN(WMT_PIN_SPI0_SS, "spi0_ss"), +	PINCTRL_PIN(WMT_PIN_SPI0_CLK, "spi0_clk"), +	PINCTRL_PIN(WMT_PIN_SPI0_SSB, "spi0_ssb"), +	PINCTRL_PIN(WMT_PIN_SD0CLK, "sd0_clk"), +	PINCTRL_PIN(WMT_PIN_SD0CMD, "sd0_cmd"), +	PINCTRL_PIN(WMT_PIN_SD0WP, "sd0_wp"), +	PINCTRL_PIN(WMT_PIN_SD0DATA0, "sd0_data0"), +	PINCTRL_PIN(WMT_PIN_SD0DATA1, "sd0_data1"), +	PINCTRL_PIN(WMT_PIN_SD0DATA2, "sd0_data2"), +	PINCTRL_PIN(WMT_PIN_SD0DATA3, "sd0_data3"), +	PINCTRL_PIN(WMT_PIN_SD1DATA0, "sd1_data0"), +	PINCTRL_PIN(WMT_PIN_SD1DATA1, "sd1_data1"), +	PINCTRL_PIN(WMT_PIN_SD1DATA2, "sd1_data2"), +	PINCTRL_PIN(WMT_PIN_SD1DATA3, "sd1_data3"), +	PINCTRL_PIN(WMT_PIN_SD1DATA4, "sd1_data4"), +	PINCTRL_PIN(WMT_PIN_SD1DATA5, "sd1_data5"), +	PINCTRL_PIN(WMT_PIN_SD1DATA6, "sd1_data6"), +	PINCTRL_PIN(WMT_PIN_SD1DATA7, "sd1_data7"), +	PINCTRL_PIN(WMT_PIN_I2C0_SCL, "i2c0_scl"), +	PINCTRL_PIN(WMT_PIN_I2C0_SDA, "i2c0_sda"), +	PINCTRL_PIN(WMT_PIN_I2C1_SCL, "i2c1_scl"), +	PINCTRL_PIN(WMT_PIN_I2C1_SDA, "i2c1_sda"), +	PINCTRL_PIN(WMT_PIN_I2C2_SCL, "i2c2_scl"), +	PINCTRL_PIN(WMT_PIN_I2C2_SDA, "i2c2_sda"), +	PINCTRL_PIN(WMT_PIN_UART0_RTS, "uart0_rts"), +	PINCTRL_PIN(WMT_PIN_UART0_TXD, "uart0_txd"), +	PINCTRL_PIN(WMT_PIN_UART0_CTS, "uart0_cts"), +	PINCTRL_PIN(WMT_PIN_UART0_RXD, "uart0_rxd"), +	PINCTRL_PIN(WMT_PIN_UART1_RTS, "uart1_rts"), +	PINCTRL_PIN(WMT_PIN_UART1_TXD, "uart1_txd"), +	PINCTRL_PIN(WMT_PIN_UART1_CTS, "uart1_cts"), +	PINCTRL_PIN(WMT_PIN_UART1_RXD, "uart1_rxd"), +	PINCTRL_PIN(WMT_PIN_UART2_RTS, "uart2_rts"), +	PINCTRL_PIN(WMT_PIN_UART2_TXD, "uart2_txd"), +	PINCTRL_PIN(WMT_PIN_UART2_CTS, "uart2_cts"), +	PINCTRL_PIN(WMT_PIN_UART2_RXD, "uart2_rxd"), +	PINCTRL_PIN(WMT_PIN_UART3_RTS, "uart3_rts"), +	PINCTRL_PIN(WMT_PIN_UART3_TXD, "uart3_txd"), +	PINCTRL_PIN(WMT_PIN_UART3_CTS, "uart3_cts"), +	PINCTRL_PIN(WMT_PIN_UART3_RXD, "uart3_rxd"), +	PINCTRL_PIN(WMT_PIN_SD2CD, "sd2_cd"), +	PINCTRL_PIN(WMT_PIN_SD2DATA3, "sd2_data3"), +	PINCTRL_PIN(WMT_PIN_SD2DATA0, "sd2_data0"), +	PINCTRL_PIN(WMT_PIN_SD2WP, "sd2_wp"), +	PINCTRL_PIN(WMT_PIN_SD2DATA1, "sd2_data1"), +	PINCTRL_PIN(WMT_PIN_SD2DATA2, "sd2_data2"), +	PINCTRL_PIN(WMT_PIN_SD2CMD, "sd2_cmd"), +	PINCTRL_PIN(WMT_PIN_SD2CLK, "sd2_clk"), +	PINCTRL_PIN(WMT_PIN_SD2PWR, "sd2_pwr"), +	PINCTRL_PIN(WMT_PIN_SD1CLK, "sd1_clk"), +	PINCTRL_PIN(WMT_PIN_SD1CMD, "sd1_cmd"), +	PINCTRL_PIN(WMT_PIN_SD1PWR, "sd1_pwr"), +	PINCTRL_PIN(WMT_PIN_SD1WP, "sd1_wp"), +	PINCTRL_PIN(WMT_PIN_SD1CD, "sd1_cd"), +	PINCTRL_PIN(WMT_PIN_SPI0SS3, "spi0_ss3"), +	PINCTRL_PIN(WMT_PIN_SPI0SS2, "spi0_ss2"), +	PINCTRL_PIN(WMT_PIN_PWMOUT1, "pwmout1"), +	PINCTRL_PIN(WMT_PIN_PWMOUT0, "pwmout0"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8750_groups[] = { +	"extgpio0", +	"extgpio1", +	"extgpio2", +	"extgpio3", +	"extgpio4", +	"extgpio5", +	"extgpio6", +	"extgpio7", +	"wakeup0", +	"wakeup1", +	"sd0_cd", +	"vdout0", +	"vdout1", +	"vdout2", +	"vdout3", +	"vdout4", +	"vdout5", +	"vdout6", +	"vdout7", +	"vdout8", +	"vdout9", +	"vdout10", +	"vdout11", +	"vdout12", +	"vdout13", +	"vdout14", +	"vdout15", +	"vdout16", +	"vdout17", +	"vdout18", +	"vdout19", +	"vdout20", +	"vdout21", +	"vdout22", +	"vdout23", +	"vdin0", +	"vdin1", +	"vdin2", +	"vdin3", +	"vdin4", +	"vdin5", +	"vdin6", +	"vdin7", +	"spi0_mosi", +	"spi0_miso", +	"spi0_ss", +	"spi0_clk", +	"spi0_ssb", +	"sd0_clk", +	"sd0_cmd", +	"sd0_wp", +	"sd0_data0", +	"sd0_data1", +	"sd0_data2", +	"sd0_data3", +	"sd1_data0", +	"sd1_data1", +	"sd1_data2", +	"sd1_data3", +	"sd1_data4", +	"sd1_data5", +	"sd1_data6", +	"sd1_data7", +	"i2c0_scl", +	"i2c0_sda", +	"i2c1_scl", +	"i2c1_sda", +	"i2c2_scl", +	"i2c2_sda", +	"uart0_rts", +	"uart0_txd", +	"uart0_cts", +	"uart0_rxd", +	"uart1_rts", +	"uart1_txd", +	"uart1_cts", +	"uart1_rxd", +	"uart2_rts", +	"uart2_txd", +	"uart2_cts", +	"uart2_rxd", +	"uart3_rts", +	"uart3_txd", +	"uart3_cts", +	"uart3_rxd", +	"sd2_cd", +	"sd2_data3", +	"sd2_data0", +	"sd2_wp", +	"sd2_data1", +	"sd2_data2", +	"sd2_cmd", +	"sd2_clk", +	"sd2_pwr", +	"sd1_clk", +	"sd1_cmd", +	"sd1_pwr", +	"sd1_wp", +	"sd1_cd", +	"spi0_ss3", +	"spi0_ss2", +	"pwmout1", +	"pwmout0", +}; + +static int wm8750_pinctrl_probe(struct platform_device *pdev) +{ +	struct wmt_pinctrl_data *data; + +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); +	if (!data) { +		dev_err(&pdev->dev, "failed to allocate data\n"); +		return -ENOMEM; +	} + +	data->banks = wm8750_banks; +	data->nbanks = ARRAY_SIZE(wm8750_banks); +	data->pins = wm8750_pins; +	data->npins = ARRAY_SIZE(wm8750_pins); +	data->groups = wm8750_groups; +	data->ngroups = ARRAY_SIZE(wm8750_groups); + +	return wmt_pinctrl_probe(pdev, data); +} + +static int wm8750_pinctrl_remove(struct platform_device *pdev) +{ +	return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { +	{ .compatible = "wm,wm8750-pinctrl" }, +	{ /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { +	.probe	= wm8750_pinctrl_probe, +	.remove	= wm8750_pinctrl_remove, +	.driver = { +		.name	= "pinctrl-wm8750", +		.owner	= THIS_MODULE, +		.of_match_table	= wmt_pinctrl_of_match, +	}, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_DESCRIPTION("Wondermedia WM8750 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8850.c b/drivers/pinctrl/vt8500/pinctrl-wm8850.c new file mode 100644 index 00000000000..ecadce9c91d --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wm8850.c @@ -0,0 +1,388 @@ +/* + * Pinctrl data for Wondermedia WM8850 SoC + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "pinctrl-wmt.h" + +/* + * Describe the register offsets within the GPIO memory space + * The dedicated external GPIO's should always be listed in bank 0 + * so they are exported in the 0..31 range which is what users + * expect. + * + * Do not reorder these banks as it will change the pin numbering + */ +static const struct wmt_pinctrl_bank_registers wm8850_banks[] = { +	WMT_PINCTRL_BANK(0x40, 0x80, 0xC0, 0x00, 0x480, 0x4C0),		/* 0 */ +	WMT_PINCTRL_BANK(0x44, 0x84, 0xC4, 0x04, 0x484, 0x4C4),		/* 1 */ +	WMT_PINCTRL_BANK(0x48, 0x88, 0xC8, 0x08, 0x488, 0x4C8),		/* 2 */ +	WMT_PINCTRL_BANK(0x4C, 0x8C, 0xCC, 0x0C, 0x48C, 0x4CC),		/* 3 */ +	WMT_PINCTRL_BANK(0x50, 0x90, 0xD0, 0x10, 0x490, 0x4D0),		/* 4 */ +	WMT_PINCTRL_BANK(0x54, 0x94, 0xD4, 0x14, 0x494, 0x4D4),		/* 5 */ +	WMT_PINCTRL_BANK(0x58, 0x98, 0xD8, 0x18, 0x498, 0x4D8),		/* 6 */ +	WMT_PINCTRL_BANK(0x5C, 0x9C, 0xDC, 0x1C, 0x49C, 0x4DC),		/* 7 */ +	WMT_PINCTRL_BANK(0x60, 0xA0, 0xE0, 0x20, 0x4A0, 0x4E0),		/* 8 */ +	WMT_PINCTRL_BANK(0x70, 0xB0, 0xF0, 0x30, 0x4B0, 0x4F0),		/* 9 */ +	WMT_PINCTRL_BANK(0x7C, 0xBC, 0xDC, 0x3C, 0x4BC, 0x4FC),		/* 10 */ +}; + +/* Please keep sorted by bank/bit */ +#define WMT_PIN_EXTGPIO0	WMT_PIN(0, 0) +#define WMT_PIN_EXTGPIO1	WMT_PIN(0, 1) +#define WMT_PIN_EXTGPIO2	WMT_PIN(0, 2) +#define WMT_PIN_EXTGPIO3	WMT_PIN(0, 3) +#define WMT_PIN_EXTGPIO4	WMT_PIN(0, 4) +#define WMT_PIN_EXTGPIO5	WMT_PIN(0, 5) +#define WMT_PIN_EXTGPIO6	WMT_PIN(0, 6) +#define WMT_PIN_EXTGPIO7	WMT_PIN(0, 7) +#define WMT_PIN_WAKEUP0		WMT_PIN(0, 16) +#define WMT_PIN_WAKEUP1		WMT_PIN(0, 17) +#define WMT_PIN_WAKEUP2		WMT_PIN(0, 18) +#define WMT_PIN_WAKEUP3		WMT_PIN(0, 19) +#define WMT_PIN_SUSGPIO0	WMT_PIN(0, 21) +#define WMT_PIN_SUSGPIO1	WMT_PIN(0, 22) +#define WMT_PIN_SD0CD		WMT_PIN(0, 28) +#define WMT_PIN_VDOUT0		WMT_PIN(1, 0) +#define WMT_PIN_VDOUT1		WMT_PIN(1, 1) +#define WMT_PIN_VDOUT2		WMT_PIN(1, 2) +#define WMT_PIN_VDOUT3		WMT_PIN(1, 3) +#define WMT_PIN_VDOUT4		WMT_PIN(1, 4) +#define WMT_PIN_VDOUT5		WMT_PIN(1, 5) +#define WMT_PIN_VDOUT6		WMT_PIN(1, 6) +#define WMT_PIN_VDOUT7		WMT_PIN(1, 7) +#define WMT_PIN_VDOUT8		WMT_PIN(1, 8) +#define WMT_PIN_VDOUT9		WMT_PIN(1, 9) +#define WMT_PIN_VDOUT10		WMT_PIN(1, 10) +#define WMT_PIN_VDOUT11		WMT_PIN(1, 11) +#define WMT_PIN_VDOUT12		WMT_PIN(1, 12) +#define WMT_PIN_VDOUT13		WMT_PIN(1, 13) +#define WMT_PIN_VDOUT14		WMT_PIN(1, 14) +#define WMT_PIN_VDOUT15		WMT_PIN(1, 15) +#define WMT_PIN_VDOUT16		WMT_PIN(1, 16) +#define WMT_PIN_VDOUT17		WMT_PIN(1, 17) +#define WMT_PIN_VDOUT18		WMT_PIN(1, 18) +#define WMT_PIN_VDOUT19		WMT_PIN(1, 19) +#define WMT_PIN_VDOUT20		WMT_PIN(1, 20) +#define WMT_PIN_VDOUT21		WMT_PIN(1, 21) +#define WMT_PIN_VDOUT22		WMT_PIN(1, 22) +#define WMT_PIN_VDOUT23		WMT_PIN(1, 23) +#define WMT_PIN_VDIN0		WMT_PIN(2, 0) +#define WMT_PIN_VDIN1		WMT_PIN(2, 1) +#define WMT_PIN_VDIN2		WMT_PIN(2, 2) +#define WMT_PIN_VDIN3		WMT_PIN(2, 3) +#define WMT_PIN_VDIN4		WMT_PIN(2, 4) +#define WMT_PIN_VDIN5		WMT_PIN(2, 5) +#define WMT_PIN_VDIN6		WMT_PIN(2, 6) +#define WMT_PIN_VDIN7		WMT_PIN(2, 7) +#define WMT_PIN_SPI0_MOSI	WMT_PIN(2, 24) +#define WMT_PIN_SPI0_MISO	WMT_PIN(2, 25) +#define WMT_PIN_SPI0_SS		WMT_PIN(2, 26) +#define WMT_PIN_SPI0_CLK	WMT_PIN(2, 27) +#define WMT_PIN_SPI0_SSB	WMT_PIN(2, 28) +#define WMT_PIN_SD0CLK		WMT_PIN(3, 17) +#define WMT_PIN_SD0CMD		WMT_PIN(3, 18) +#define WMT_PIN_SD0WP		WMT_PIN(3, 19) +#define WMT_PIN_SD0DATA0	WMT_PIN(3, 20) +#define WMT_PIN_SD0DATA1	WMT_PIN(3, 21) +#define WMT_PIN_SD0DATA2	WMT_PIN(3, 22) +#define WMT_PIN_SD0DATA3	WMT_PIN(3, 23) +#define WMT_PIN_SD1DATA0	WMT_PIN(3, 24) +#define WMT_PIN_SD1DATA1	WMT_PIN(3, 25) +#define WMT_PIN_SD1DATA2	WMT_PIN(3, 26) +#define WMT_PIN_SD1DATA3	WMT_PIN(3, 27) +#define WMT_PIN_SD1DATA4	WMT_PIN(3, 28) +#define WMT_PIN_SD1DATA5	WMT_PIN(3, 29) +#define WMT_PIN_SD1DATA6	WMT_PIN(3, 30) +#define WMT_PIN_SD1DATA7	WMT_PIN(3, 31) +#define WMT_PIN_I2C0_SCL	WMT_PIN(5, 8) +#define WMT_PIN_I2C0_SDA	WMT_PIN(5, 9) +#define WMT_PIN_I2C1_SCL	WMT_PIN(5, 10) +#define WMT_PIN_I2C1_SDA	WMT_PIN(5, 11) +#define WMT_PIN_I2C2_SCL	WMT_PIN(5, 12) +#define WMT_PIN_I2C2_SDA	WMT_PIN(5, 13) +#define WMT_PIN_UART0_RTS	WMT_PIN(5, 16) +#define WMT_PIN_UART0_TXD	WMT_PIN(5, 17) +#define WMT_PIN_UART0_CTS	WMT_PIN(5, 18) +#define WMT_PIN_UART0_RXD	WMT_PIN(5, 19) +#define WMT_PIN_UART1_RTS	WMT_PIN(5, 20) +#define WMT_PIN_UART1_TXD	WMT_PIN(5, 21) +#define WMT_PIN_UART1_CTS	WMT_PIN(5, 22) +#define WMT_PIN_UART1_RXD	WMT_PIN(5, 23) +#define WMT_PIN_UART2_RTS	WMT_PIN(5, 24) +#define WMT_PIN_UART2_TXD	WMT_PIN(5, 25) +#define WMT_PIN_UART2_CTS	WMT_PIN(5, 26) +#define WMT_PIN_UART2_RXD	WMT_PIN(5, 27) +#define WMT_PIN_SD2WP		WMT_PIN(6, 3) +#define WMT_PIN_SD2CMD		WMT_PIN(6, 6) +#define WMT_PIN_SD2CLK		WMT_PIN(6, 7) +#define WMT_PIN_SD2PWR		WMT_PIN(6, 9) +#define WMT_PIN_SD1CLK		WMT_PIN(7, 0) +#define WMT_PIN_SD1CMD		WMT_PIN(7, 1) +#define WMT_PIN_SD1PWR		WMT_PIN(7, 10) +#define WMT_PIN_SD1WP		WMT_PIN(7, 11) +#define WMT_PIN_SD1CD		WMT_PIN(7, 12) +#define WMT_PIN_PWMOUT1		WMT_PIN(7, 26) +#define WMT_PIN_PWMOUT0		WMT_PIN(7, 27) + +static const struct pinctrl_pin_desc wm8850_pins[] = { +	PINCTRL_PIN(WMT_PIN_EXTGPIO0, "extgpio0"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO1, "extgpio1"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO2, "extgpio2"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO3, "extgpio3"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO4, "extgpio4"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO5, "extgpio5"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO6, "extgpio6"), +	PINCTRL_PIN(WMT_PIN_EXTGPIO7, "extgpio7"), +	PINCTRL_PIN(WMT_PIN_WAKEUP0, "wakeup0"), +	PINCTRL_PIN(WMT_PIN_WAKEUP1, "wakeup1"), +	PINCTRL_PIN(WMT_PIN_WAKEUP2, "wakeup2"), +	PINCTRL_PIN(WMT_PIN_WAKEUP3, "wakeup3"), +	PINCTRL_PIN(WMT_PIN_SUSGPIO0, "susgpio0"), +	PINCTRL_PIN(WMT_PIN_SUSGPIO1, "susgpio1"), +	PINCTRL_PIN(WMT_PIN_SD0CD, "sd0_cd"), +	PINCTRL_PIN(WMT_PIN_VDOUT0, "vdout0"), +	PINCTRL_PIN(WMT_PIN_VDOUT1, "vdout1"), +	PINCTRL_PIN(WMT_PIN_VDOUT2, "vdout2"), +	PINCTRL_PIN(WMT_PIN_VDOUT3, "vdout3"), +	PINCTRL_PIN(WMT_PIN_VDOUT4, "vdout4"), +	PINCTRL_PIN(WMT_PIN_VDOUT5, "vdout5"), +	PINCTRL_PIN(WMT_PIN_VDOUT6, "vdout6"), +	PINCTRL_PIN(WMT_PIN_VDOUT7, "vdout7"), +	PINCTRL_PIN(WMT_PIN_VDOUT8, "vdout8"), +	PINCTRL_PIN(WMT_PIN_VDOUT9, "vdout9"), +	PINCTRL_PIN(WMT_PIN_VDOUT10, "vdout10"), +	PINCTRL_PIN(WMT_PIN_VDOUT11, "vdout11"), +	PINCTRL_PIN(WMT_PIN_VDOUT12, "vdout12"), +	PINCTRL_PIN(WMT_PIN_VDOUT13, "vdout13"), +	PINCTRL_PIN(WMT_PIN_VDOUT14, "vdout14"), +	PINCTRL_PIN(WMT_PIN_VDOUT15, "vdout15"), +	PINCTRL_PIN(WMT_PIN_VDOUT16, "vdout16"), +	PINCTRL_PIN(WMT_PIN_VDOUT17, "vdout17"), +	PINCTRL_PIN(WMT_PIN_VDOUT18, "vdout18"), +	PINCTRL_PIN(WMT_PIN_VDOUT19, "vdout19"), +	PINCTRL_PIN(WMT_PIN_VDOUT20, "vdout20"), +	PINCTRL_PIN(WMT_PIN_VDOUT21, "vdout21"), +	PINCTRL_PIN(WMT_PIN_VDOUT22, "vdout22"), +	PINCTRL_PIN(WMT_PIN_VDOUT23, "vdout23"), +	PINCTRL_PIN(WMT_PIN_VDIN0, "vdin0"), +	PINCTRL_PIN(WMT_PIN_VDIN1, "vdin1"), +	PINCTRL_PIN(WMT_PIN_VDIN2, "vdin2"), +	PINCTRL_PIN(WMT_PIN_VDIN3, "vdin3"), +	PINCTRL_PIN(WMT_PIN_VDIN4, "vdin4"), +	PINCTRL_PIN(WMT_PIN_VDIN5, "vdin5"), +	PINCTRL_PIN(WMT_PIN_VDIN6, "vdin6"), +	PINCTRL_PIN(WMT_PIN_VDIN7, "vdin7"), +	PINCTRL_PIN(WMT_PIN_SPI0_MOSI, "spi0_mosi"), +	PINCTRL_PIN(WMT_PIN_SPI0_MISO, "spi0_miso"), +	PINCTRL_PIN(WMT_PIN_SPI0_SS, "spi0_ss"), +	PINCTRL_PIN(WMT_PIN_SPI0_CLK, "spi0_clk"), +	PINCTRL_PIN(WMT_PIN_SPI0_SSB, "spi0_ssb"), +	PINCTRL_PIN(WMT_PIN_SD0CLK, "sd0_clk"), +	PINCTRL_PIN(WMT_PIN_SD0CMD, "sd0_cmd"), +	PINCTRL_PIN(WMT_PIN_SD0WP, "sd0_wp"), +	PINCTRL_PIN(WMT_PIN_SD0DATA0, "sd0_data0"), +	PINCTRL_PIN(WMT_PIN_SD0DATA1, "sd0_data1"), +	PINCTRL_PIN(WMT_PIN_SD0DATA2, "sd0_data2"), +	PINCTRL_PIN(WMT_PIN_SD0DATA3, "sd0_data3"), +	PINCTRL_PIN(WMT_PIN_SD1DATA0, "sd1_data0"), +	PINCTRL_PIN(WMT_PIN_SD1DATA1, "sd1_data1"), +	PINCTRL_PIN(WMT_PIN_SD1DATA2, "sd1_data2"), +	PINCTRL_PIN(WMT_PIN_SD1DATA3, "sd1_data3"), +	PINCTRL_PIN(WMT_PIN_SD1DATA4, "sd1_data4"), +	PINCTRL_PIN(WMT_PIN_SD1DATA5, "sd1_data5"), +	PINCTRL_PIN(WMT_PIN_SD1DATA6, "sd1_data6"), +	PINCTRL_PIN(WMT_PIN_SD1DATA7, "sd1_data7"), +	PINCTRL_PIN(WMT_PIN_I2C0_SCL, "i2c0_scl"), +	PINCTRL_PIN(WMT_PIN_I2C0_SDA, "i2c0_sda"), +	PINCTRL_PIN(WMT_PIN_I2C1_SCL, "i2c1_scl"), +	PINCTRL_PIN(WMT_PIN_I2C1_SDA, "i2c1_sda"), +	PINCTRL_PIN(WMT_PIN_I2C2_SCL, "i2c2_scl"), +	PINCTRL_PIN(WMT_PIN_I2C2_SDA, "i2c2_sda"), +	PINCTRL_PIN(WMT_PIN_UART0_RTS, "uart0_rts"), +	PINCTRL_PIN(WMT_PIN_UART0_TXD, "uart0_txd"), +	PINCTRL_PIN(WMT_PIN_UART0_CTS, "uart0_cts"), +	PINCTRL_PIN(WMT_PIN_UART0_RXD, "uart0_rxd"), +	PINCTRL_PIN(WMT_PIN_UART1_RTS, "uart1_rts"), +	PINCTRL_PIN(WMT_PIN_UART1_TXD, "uart1_txd"), +	PINCTRL_PIN(WMT_PIN_UART1_CTS, "uart1_cts"), +	PINCTRL_PIN(WMT_PIN_UART1_RXD, "uart1_rxd"), +	PINCTRL_PIN(WMT_PIN_UART2_RTS, "uart2_rts"), +	PINCTRL_PIN(WMT_PIN_UART2_TXD, "uart2_txd"), +	PINCTRL_PIN(WMT_PIN_UART2_CTS, "uart2_cts"), +	PINCTRL_PIN(WMT_PIN_UART2_RXD, "uart2_rxd"), +	PINCTRL_PIN(WMT_PIN_SD2WP, "sd2_wp"), +	PINCTRL_PIN(WMT_PIN_SD2CMD, "sd2_cmd"), +	PINCTRL_PIN(WMT_PIN_SD2CLK, "sd2_clk"), +	PINCTRL_PIN(WMT_PIN_SD2PWR, "sd2_pwr"), +	PINCTRL_PIN(WMT_PIN_SD1CLK, "sd1_clk"), +	PINCTRL_PIN(WMT_PIN_SD1CMD, "sd1_cmd"), +	PINCTRL_PIN(WMT_PIN_SD1PWR, "sd1_pwr"), +	PINCTRL_PIN(WMT_PIN_SD1WP, "sd1_wp"), +	PINCTRL_PIN(WMT_PIN_SD1CD, "sd1_cd"), +	PINCTRL_PIN(WMT_PIN_PWMOUT1, "pwmout1"), +	PINCTRL_PIN(WMT_PIN_PWMOUT0, "pwmout0"), +}; + +/* Order of these names must match the above list */ +static const char * const wm8850_groups[] = { +	"extgpio0", +	"extgpio1", +	"extgpio2", +	"extgpio3", +	"extgpio4", +	"extgpio5", +	"extgpio6", +	"extgpio7", +	"wakeup0", +	"wakeup1", +	"wakeup2", +	"wakeup3", +	"susgpio0", +	"susgpio1", +	"sd0_cd", +	"vdout0", +	"vdout1", +	"vdout2", +	"vdout3", +	"vdout4", +	"vdout5", +	"vdout6", +	"vdout7", +	"vdout8", +	"vdout9", +	"vdout10", +	"vdout11", +	"vdout12", +	"vdout13", +	"vdout14", +	"vdout15", +	"vdout16", +	"vdout17", +	"vdout18", +	"vdout19", +	"vdout20", +	"vdout21", +	"vdout22", +	"vdout23", +	"vdin0", +	"vdin1", +	"vdin2", +	"vdin3", +	"vdin4", +	"vdin5", +	"vdin6", +	"vdin7", +	"spi0_mosi", +	"spi0_miso", +	"spi0_ss", +	"spi0_clk", +	"spi0_ssb", +	"sd0_clk", +	"sd0_cmd", +	"sd0_wp", +	"sd0_data0", +	"sd0_data1", +	"sd0_data2", +	"sd0_data3", +	"sd1_data0", +	"sd1_data1", +	"sd1_data2", +	"sd1_data3", +	"sd1_data4", +	"sd1_data5", +	"sd1_data6", +	"sd1_data7", +	"i2c0_scl", +	"i2c0_sda", +	"i2c1_scl", +	"i2c1_sda", +	"i2c2_scl", +	"i2c2_sda", +	"uart0_rts", +	"uart0_txd", +	"uart0_cts", +	"uart0_rxd", +	"uart1_rts", +	"uart1_txd", +	"uart1_cts", +	"uart1_rxd", +	"uart2_rts", +	"uart2_txd", +	"uart2_cts", +	"uart2_rxd", +	"sd2_wp", +	"sd2_cmd", +	"sd2_clk", +	"sd2_pwr", +	"sd1_clk", +	"sd1_cmd", +	"sd1_pwr", +	"sd1_wp", +	"sd1_cd", +	"pwmout1", +	"pwmout0", +}; + +static int wm8850_pinctrl_probe(struct platform_device *pdev) +{ +	struct wmt_pinctrl_data *data; + +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); +	if (!data) { +		dev_err(&pdev->dev, "failed to allocate data\n"); +		return -ENOMEM; +	} + +	data->banks = wm8850_banks; +	data->nbanks = ARRAY_SIZE(wm8850_banks); +	data->pins = wm8850_pins; +	data->npins = ARRAY_SIZE(wm8850_pins); +	data->groups = wm8850_groups; +	data->ngroups = ARRAY_SIZE(wm8850_groups); + +	return wmt_pinctrl_probe(pdev, data); +} + +static int wm8850_pinctrl_remove(struct platform_device *pdev) +{ +	return wmt_pinctrl_remove(pdev); +} + +static struct of_device_id wmt_pinctrl_of_match[] = { +	{ .compatible = "wm,wm8850-pinctrl" }, +	{ /* sentinel */ }, +}; + +static struct platform_driver wmt_pinctrl_driver = { +	.probe	= wm8850_pinctrl_probe, +	.remove	= wm8850_pinctrl_remove, +	.driver = { +		.name	= "pinctrl-wm8850", +		.owner	= THIS_MODULE, +		.of_match_table	= wmt_pinctrl_of_match, +	}, +}; + +module_platform_driver(wmt_pinctrl_driver); + +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_DESCRIPTION("Wondermedia WM8850 Pincontrol driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_pinctrl_of_match); diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c new file mode 100644 index 00000000000..14400a7974b --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -0,0 +1,632 @@ +/* + * Pinctrl driver for the Wondermedia SoC's + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "pinctrl-wmt.h" + +static inline void wmt_setbits(struct wmt_pinctrl_data *data, u32 reg, +				 u32 mask) +{ +	u32 val; + +	val = readl_relaxed(data->base + reg); +	val |= mask; +	writel_relaxed(val, data->base + reg); +} + +static inline void wmt_clearbits(struct wmt_pinctrl_data *data, u32 reg, +				   u32 mask) +{ +	u32 val; + +	val = readl_relaxed(data->base + reg); +	val &= ~mask; +	writel_relaxed(val, data->base + reg); +} + +enum wmt_func_sel { +	WMT_FSEL_GPIO_IN = 0, +	WMT_FSEL_GPIO_OUT = 1, +	WMT_FSEL_ALT = 2, +	WMT_FSEL_COUNT = 3, +}; + +static const char * const wmt_functions[WMT_FSEL_COUNT] = { +	[WMT_FSEL_GPIO_IN] = "gpio_in", +	[WMT_FSEL_GPIO_OUT] = "gpio_out", +	[WMT_FSEL_ALT] = "alt", +}; + +static int wmt_pmx_get_functions_count(struct pinctrl_dev *pctldev) +{ +	return WMT_FSEL_COUNT; +} + +static const char *wmt_pmx_get_function_name(struct pinctrl_dev *pctldev, +					     unsigned selector) +{ +	return wmt_functions[selector]; +} + +static int wmt_pmx_get_function_groups(struct pinctrl_dev *pctldev, +				       unsigned selector, +				       const char * const **groups, +				       unsigned * const num_groups) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	/* every pin does every function */ +	*groups = data->groups; +	*num_groups = data->ngroups; + +	return 0; +} + +static int wmt_set_pinmux(struct wmt_pinctrl_data *data, unsigned func, +			  unsigned pin) +{ +	u32 bank = WMT_BANK_FROM_PIN(pin); +	u32 bit = WMT_BIT_FROM_PIN(pin); +	u32 reg_en = data->banks[bank].reg_en; +	u32 reg_dir = data->banks[bank].reg_dir; + +	if (reg_dir == NO_REG) { +		dev_err(data->dev, "pin:%d no direction register defined\n", +			pin); +		return -EINVAL; +	} + +	/* +	 * If reg_en == NO_REG, we assume it is a dedicated GPIO and cannot be +	 * disabled (as on VT8500) and that no alternate function is available. +	 */ +	switch (func) { +	case WMT_FSEL_GPIO_IN: +		if (reg_en != NO_REG) +			wmt_setbits(data, reg_en, BIT(bit)); +		wmt_clearbits(data, reg_dir, BIT(bit)); +		break; +	case WMT_FSEL_GPIO_OUT: +		if (reg_en != NO_REG) +			wmt_setbits(data, reg_en, BIT(bit)); +		wmt_setbits(data, reg_dir, BIT(bit)); +		break; +	case WMT_FSEL_ALT: +		if (reg_en == NO_REG) { +			dev_err(data->dev, "pin:%d no alt function available\n", +				pin); +			return -EINVAL; +		} +		wmt_clearbits(data, reg_en, BIT(bit)); +	} + +	return 0; +} + +static int wmt_pmx_enable(struct pinctrl_dev *pctldev, +			  unsigned func_selector, +			  unsigned group_selector) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); +	u32 pinnum = data->pins[group_selector].number; + +	return wmt_set_pinmux(data, func_selector, pinnum); +} + +static void wmt_pmx_disable(struct pinctrl_dev *pctldev, +			    unsigned func_selector, +			    unsigned group_selector) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); +	u32 pinnum = data->pins[group_selector].number; + +	/* disable by setting GPIO_IN */ +	wmt_set_pinmux(data, WMT_FSEL_GPIO_IN, pinnum); +} + +static void wmt_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, +				      struct pinctrl_gpio_range *range, +				      unsigned offset) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	/* disable by setting GPIO_IN */ +	wmt_set_pinmux(data, WMT_FSEL_GPIO_IN, offset); +} + +static int wmt_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, +				      struct pinctrl_gpio_range *range, +				      unsigned offset, +				      bool input) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	wmt_set_pinmux(data, (input ? WMT_FSEL_GPIO_IN : WMT_FSEL_GPIO_OUT), +		       offset); + +	return 0; +} + +static struct pinmux_ops wmt_pinmux_ops = { +	.get_functions_count = wmt_pmx_get_functions_count, +	.get_function_name = wmt_pmx_get_function_name, +	.get_function_groups = wmt_pmx_get_function_groups, +	.enable = wmt_pmx_enable, +	.disable = wmt_pmx_disable, +	.gpio_disable_free = wmt_pmx_gpio_disable_free, +	.gpio_set_direction = wmt_pmx_gpio_set_direction, +}; + +static int wmt_get_groups_count(struct pinctrl_dev *pctldev) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	return data->ngroups; +} + +static const char *wmt_get_group_name(struct pinctrl_dev *pctldev, +				      unsigned selector) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	return data->groups[selector]; +} + +static int wmt_get_group_pins(struct pinctrl_dev *pctldev, +			      unsigned selector, +			      const unsigned **pins, +			      unsigned *num_pins) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	*pins = &data->pins[selector].number; +	*num_pins = 1; + +	return 0; +} + +static int wmt_pctl_find_group_by_pin(struct wmt_pinctrl_data *data, u32 pin) +{ +	int i; + +	for (i = 0; i < data->npins; i++) { +		if (data->pins[i].number == pin) +			return i; +	} + +	return -EINVAL; +} + +static int wmt_pctl_dt_node_to_map_func(struct wmt_pinctrl_data *data, +					struct device_node *np, +					u32 pin, u32 fnum, +					struct pinctrl_map **maps) +{ +	int group; +	struct pinctrl_map *map = *maps; + +	if (fnum >= ARRAY_SIZE(wmt_functions)) { +		dev_err(data->dev, "invalid wm,function %d\n", fnum); +		return -EINVAL; +	} + +	group = wmt_pctl_find_group_by_pin(data, pin); +	if (group < 0) { +		dev_err(data->dev, "unable to match pin %d to group\n", pin); +		return group; +	} + +	map->type = PIN_MAP_TYPE_MUX_GROUP; +	map->data.mux.group = data->groups[group]; +	map->data.mux.function = wmt_functions[fnum]; +	(*maps)++; + +	return 0; +} + +static int wmt_pctl_dt_node_to_map_pull(struct wmt_pinctrl_data *data, +					struct device_node *np, +					u32 pin, u32 pull, +					struct pinctrl_map **maps) +{ +	int group; +	unsigned long *configs; +	struct pinctrl_map *map = *maps; + +	if (pull > 2) { +		dev_err(data->dev, "invalid wm,pull %d\n", pull); +		return -EINVAL; +	} + +	group = wmt_pctl_find_group_by_pin(data, pin); +	if (group < 0) { +		dev_err(data->dev, "unable to match pin %d to group\n", pin); +		return group; +	} + +	configs = kzalloc(sizeof(*configs), GFP_KERNEL); +	if (!configs) +		return -ENOMEM; + +	configs[0] = pull; + +	map->type = PIN_MAP_TYPE_CONFIGS_PIN; +	map->data.configs.group_or_pin = data->groups[group]; +	map->data.configs.configs = configs; +	map->data.configs.num_configs = 1; +	(*maps)++; + +	return 0; +} + +static void wmt_pctl_dt_free_map(struct pinctrl_dev *pctldev, +				 struct pinctrl_map *maps, +				 unsigned num_maps) +{ +	int i; + +	for (i = 0; i < num_maps; i++) +		if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) +			kfree(maps[i].data.configs.configs); + +	kfree(maps); +} + +static int wmt_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, +				   struct device_node *np, +				   struct pinctrl_map **map, +				   unsigned *num_maps) +{ +	struct pinctrl_map *maps, *cur_map; +	struct property *pins, *funcs, *pulls; +	u32 pin, func, pull; +	int num_pins, num_funcs, num_pulls, maps_per_pin; +	int i, err; +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); + +	pins = of_find_property(np, "wm,pins", NULL); +	if (!pins) { +		dev_err(data->dev, "missing wmt,pins property\n"); +		return -EINVAL; +	} + +	funcs = of_find_property(np, "wm,function", NULL); +	pulls = of_find_property(np, "wm,pull", NULL); + +	if (!funcs && !pulls) { +		dev_err(data->dev, "neither wm,function nor wm,pull specified\n"); +		return -EINVAL; +	} + +	/* +	 * The following lines calculate how many values are defined for each +	 * of the properties. +	 */ +	num_pins = pins->length / sizeof(u32); +	num_funcs = funcs ? (funcs->length / sizeof(u32)) : 0; +	num_pulls = pulls ? (pulls->length / sizeof(u32)) : 0; + +	if (num_funcs > 1 && num_funcs != num_pins) { +		dev_err(data->dev, "wm,function must have 1 or %d entries\n", +			num_pins); +		return -EINVAL; +	} + +	if (num_pulls > 1 && num_pulls != num_pins) { +		dev_err(data->dev, "wm,pull must have 1 or %d entries\n", +			num_pins); +		return -EINVAL; +	} + +	maps_per_pin = 0; +	if (num_funcs) +		maps_per_pin++; +	if (num_pulls) +		maps_per_pin++; + +	cur_map = maps = kzalloc(num_pins * maps_per_pin * sizeof(*maps), +				 GFP_KERNEL); +	if (!maps) +		return -ENOMEM; + +	for (i = 0; i < num_pins; i++) { +		err = of_property_read_u32_index(np, "wm,pins", i, &pin); +		if (err) +			goto fail; + +		if (pin >= (data->nbanks * 32)) { +			dev_err(data->dev, "invalid wm,pins value\n"); +			err = -EINVAL; +			goto fail; +		} + +		if (num_funcs) { +			err = of_property_read_u32_index(np, "wm,function", +						(num_funcs > 1 ? i : 0), &func); +			if (err) +				goto fail; + +			err = wmt_pctl_dt_node_to_map_func(data, np, pin, func, +							   &cur_map); +			if (err) +				goto fail; +		} + +		if (num_pulls) { +			err = of_property_read_u32_index(np, "wm,pull", +						(num_pulls > 1 ? i : 0), &pull); +			if (err) +				goto fail; + +			err = wmt_pctl_dt_node_to_map_pull(data, np, pin, pull, +							   &cur_map); +			if (err) +				goto fail; +		} +	} +	*map = maps; +	*num_maps = num_pins * maps_per_pin; +	return 0; + +/* + * The fail path removes any maps that have been allocated. The fail path is + * only called from code after maps has been kzalloc'd. It is also safe to + * pass 'num_pins * maps_per_pin' as the map count even though we probably + * failed before all the mappings were read as all maps are allocated at once, + * and configs are only allocated for .type = PIN_MAP_TYPE_CONFIGS_PIN - there + * is no failpath where a config can be allocated without .type being set. + */ +fail: +	wmt_pctl_dt_free_map(pctldev, maps, num_pins * maps_per_pin); +	return err; +} + +static struct pinctrl_ops wmt_pctl_ops = { +	.get_groups_count = wmt_get_groups_count, +	.get_group_name	= wmt_get_group_name, +	.get_group_pins	= wmt_get_group_pins, +	.dt_node_to_map = wmt_pctl_dt_node_to_map, +	.dt_free_map = wmt_pctl_dt_free_map, +}; + +static int wmt_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, +			   unsigned long *config) +{ +	return -ENOTSUPP; +} + +static int wmt_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, +			   unsigned long config) +{ +	struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); +	enum pin_config_param param = pinconf_to_config_param(config); +	u16 arg = pinconf_to_config_argument(config); +	u32 bank = WMT_BANK_FROM_PIN(pin); +	u32 bit = WMT_BIT_FROM_PIN(pin); +	u32 reg_pull_en = data->banks[bank].reg_pull_en; +	u32 reg_pull_cfg = data->banks[bank].reg_pull_cfg; + +	if ((reg_pull_en == NO_REG) || (reg_pull_cfg == NO_REG)) { +		dev_err(data->dev, "bias functions not supported on pin %d\n", +			pin); +		return -EINVAL; +	} + +	if ((param == PIN_CONFIG_BIAS_PULL_DOWN) || +	    (param == PIN_CONFIG_BIAS_PULL_UP)) { +		if (arg == 0) +			param = PIN_CONFIG_BIAS_DISABLE; +	} + +	switch (param) { +	case PIN_CONFIG_BIAS_DISABLE: +		wmt_clearbits(data, reg_pull_en, BIT(bit)); +		break; +	case PIN_CONFIG_BIAS_PULL_DOWN: +		wmt_clearbits(data, reg_pull_cfg, BIT(bit)); +		wmt_setbits(data, reg_pull_en, BIT(bit)); +		break; +	case PIN_CONFIG_BIAS_PULL_UP: +		wmt_setbits(data, reg_pull_cfg, BIT(bit)); +		wmt_setbits(data, reg_pull_en, BIT(bit)); +		break; +	default: +		dev_err(data->dev, "unknown pinconf param\n"); +		return -EINVAL; +	} + +	return 0; +} + +static struct pinconf_ops wmt_pinconf_ops = { +	.pin_config_get = wmt_pinconf_get, +	.pin_config_set = wmt_pinconf_set, +}; + +static struct pinctrl_desc wmt_desc = { +	.owner = THIS_MODULE, +	.name = "pinctrl-wmt", +	.pctlops = &wmt_pctl_ops, +	.pmxops = &wmt_pinmux_ops, +	.confops = &wmt_pinconf_ops, +}; + +static int wmt_gpio_request(struct gpio_chip *chip, unsigned offset) +{ +	return pinctrl_request_gpio(chip->base + offset); +} + +static void wmt_gpio_free(struct gpio_chip *chip, unsigned offset) +{ +	pinctrl_free_gpio(chip->base + offset); +} + +static int wmt_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ +	struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev); +	u32 bank = WMT_BANK_FROM_PIN(offset); +	u32 bit = WMT_BIT_FROM_PIN(offset); +	u32 reg_dir = data->banks[bank].reg_dir; +	u32 val; + +	val = readl_relaxed(data->base + reg_dir); +	if (val & BIT(bit)) +		return GPIOF_DIR_OUT; +	else +		return GPIOF_DIR_IN; +} + +static int wmt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ +	return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int wmt_gpio_direction_output(struct gpio_chip *chip, unsigned offset, +				     int value) +{ +	return pinctrl_gpio_direction_output(chip->base + offset); +} + +static int wmt_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ +	struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev); +	u32 bank = WMT_BANK_FROM_PIN(offset); +	u32 bit = WMT_BIT_FROM_PIN(offset); +	u32 reg_data_in = data->banks[bank].reg_data_in; + +	if (reg_data_in == NO_REG) { +		dev_err(data->dev, "no data in register defined\n"); +		return -EINVAL; +	} + +	return !!(readl_relaxed(data->base + reg_data_in) & BIT(bit)); +} + +static void wmt_gpio_set_value(struct gpio_chip *chip, unsigned offset, +			       int val) +{ +	struct wmt_pinctrl_data *data = dev_get_drvdata(chip->dev); +	u32 bank = WMT_BANK_FROM_PIN(offset); +	u32 bit = WMT_BIT_FROM_PIN(offset); +	u32 reg_data_out = data->banks[bank].reg_data_out; + +	if (reg_data_out == NO_REG) { +		dev_err(data->dev, "no data out register defined\n"); +		return; +	} + +	if (val) +		wmt_setbits(data, reg_data_out, BIT(bit)); +	else +		wmt_clearbits(data, reg_data_out, BIT(bit)); +} + +static struct gpio_chip wmt_gpio_chip = { +	.label = "gpio-wmt", +	.owner = THIS_MODULE, +	.request = wmt_gpio_request, +	.free = wmt_gpio_free, +	.get_direction = wmt_gpio_get_direction, +	.direction_input = wmt_gpio_direction_input, +	.direction_output = wmt_gpio_direction_output, +	.get = wmt_gpio_get_value, +	.set = wmt_gpio_set_value, +	.can_sleep = 0, +}; + +int wmt_pinctrl_probe(struct platform_device *pdev, +		      struct wmt_pinctrl_data *data) +{ +	int err; +	struct resource *res; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	data->base = devm_request_and_ioremap(&pdev->dev, res); +	if (!data->base) { +		dev_err(&pdev->dev, "failed to map memory resource\n"); +		return -EBUSY; +	} + +	wmt_desc.pins = data->pins; +	wmt_desc.npins = data->npins; + +	data->gpio_chip = wmt_gpio_chip; +	data->gpio_chip.dev = &pdev->dev; +	data->gpio_chip.of_node = pdev->dev.of_node; +	data->gpio_chip.ngpio = data->nbanks * 32; + +	platform_set_drvdata(pdev, data); + +	data->dev = &pdev->dev; + +	data->pctl_dev = pinctrl_register(&wmt_desc, &pdev->dev, data); +	if (IS_ERR(data->pctl_dev)) { +		dev_err(&pdev->dev, "Failed to register pinctrl\n"); +		return -EINVAL; +	} + +	err = gpiochip_add(&data->gpio_chip); +	if (err) { +		dev_err(&pdev->dev, "could not add GPIO chip\n"); +		goto fail_gpio; +	} + +	err = gpiochip_add_pin_range(&data->gpio_chip, dev_name(data->dev), +				     0, 0, data->nbanks * 32); +	if (err) +		goto fail_range; + +	dev_info(&pdev->dev, "Pin controller initialized\n"); + +	return 0; + +fail_range: +	err = gpiochip_remove(&data->gpio_chip); +	if (err) +		dev_err(&pdev->dev, "failed to remove gpio chip\n"); +fail_gpio: +	pinctrl_unregister(data->pctl_dev); +	return err; +} + +int wmt_pinctrl_remove(struct platform_device *pdev) +{ +	struct wmt_pinctrl_data *data = platform_get_drvdata(pdev); +	int err; + +	err = gpiochip_remove(&data->gpio_chip); +	if (err) +		dev_err(&pdev->dev, "failed to remove gpio chip\n"); + +	pinctrl_unregister(data->pctl_dev); + +	return 0; +} diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.h b/drivers/pinctrl/vt8500/pinctrl-wmt.h new file mode 100644 index 00000000000..41f5f2deb5d --- /dev/null +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.h @@ -0,0 +1,79 @@ +/* + * Pinctrl driver for the Wondermedia SoC's + * + * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +#include <linux/gpio.h> + +/* VT8500 has no enable register in the extgpio bank. */ +#define NO_REG	0xFFFF + +#define WMT_PINCTRL_BANK(__en, __dir, __dout, __din, __pen, __pcfg)	\ +{									\ +	.reg_en		= __en,						\ +	.reg_dir	= __dir,					\ +	.reg_data_out	= __dout,					\ +	.reg_data_in	= __din,					\ +	.reg_pull_en	= __pen,					\ +	.reg_pull_cfg	= __pcfg,					\ +} + +/* Encode/decode the bank/bit pairs into a pin value */ +#define WMT_PIN(__bank, __offset)	((__bank << 5) | __offset) +#define WMT_BANK_FROM_PIN(__pin)	(__pin >> 5) +#define WMT_BIT_FROM_PIN(__pin)		(__pin & 0x1f) + +#define WMT_GROUP(__name, __data)		\ +{						\ +	.name = __name,				\ +	.pins = __data,				\ +	.npins = ARRAY_SIZE(__data),		\ +} + +struct wmt_pinctrl_bank_registers { +	u32	reg_en; +	u32	reg_dir; +	u32	reg_data_out; +	u32	reg_data_in; + +	u32	reg_pull_en; +	u32	reg_pull_cfg; +}; + +struct wmt_pinctrl_group { +	const char *name; +	const unsigned int *pins; +	const unsigned npins; +}; + +struct wmt_pinctrl_data { +	struct device *dev; +	struct pinctrl_dev *pctl_dev; + +	/* must be initialized before calling wmt_pinctrl_probe */ +	void __iomem *base; +	const struct wmt_pinctrl_bank_registers *banks; +	const struct pinctrl_pin_desc *pins; +	const char * const *groups; + +	u32 nbanks; +	u32 npins; +	u32 ngroups; + +	struct gpio_chip gpio_chip; +	struct pinctrl_gpio_range gpio_range; +}; + +int wmt_pinctrl_probe(struct platform_device *pdev, +		      struct wmt_pinctrl_data *data); +int wmt_pinctrl_remove(struct platform_device *pdev); diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 434ebc3a99d..0a9f27e094e 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -44,6 +44,7 @@ static DECLARE_COMPLETION(at91_rtc_updated);  static unsigned int at91_alarm_year = AT91_RTC_EPOCH;  static void __iomem *at91_rtc_regs;  static int irq; +static u32 at91_rtc_imr;  /*   * Decode time/date into rtc_time structure @@ -108,9 +109,11 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)  	cr = at91_rtc_read(AT91_RTC_CR);  	at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); +	at91_rtc_imr |= AT91_RTC_ACKUPD;  	at91_rtc_write(AT91_RTC_IER, AT91_RTC_ACKUPD);  	wait_for_completion(&at91_rtc_updated);	/* wait for ACKUPD interrupt */  	at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD); +	at91_rtc_imr &= ~AT91_RTC_ACKUPD;  	at91_rtc_write(AT91_RTC_TIMR,  			  bin2bcd(tm->tm_sec) << 0 @@ -142,7 +145,7 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)  	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);  	tm->tm_year = at91_alarm_year - 1900; -	alrm->enabled = (at91_rtc_read(AT91_RTC_IMR) & AT91_RTC_ALARM) +	alrm->enabled = (at91_rtc_imr & AT91_RTC_ALARM)  			? 1 : 0;  	dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, @@ -168,6 +171,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)  	tm.tm_sec = alrm->time.tm_sec;  	at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); +	at91_rtc_imr &= ~AT91_RTC_ALARM;  	at91_rtc_write(AT91_RTC_TIMALR,  		  bin2bcd(tm.tm_sec) << 0  		| bin2bcd(tm.tm_min) << 8 @@ -180,6 +184,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)  	if (alrm->enabled) {  		at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); +		at91_rtc_imr |= AT91_RTC_ALARM;  		at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM);  	} @@ -196,9 +201,12 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)  	if (enabled) {  		at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); +		at91_rtc_imr |= AT91_RTC_ALARM;  		at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); -	} else +	} else {  		at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ALARM); +		at91_rtc_imr &= ~AT91_RTC_ALARM; +	}  	return 0;  } @@ -207,12 +215,10 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)   */  static int at91_rtc_proc(struct device *dev, struct seq_file *seq)  { -	unsigned long imr = at91_rtc_read(AT91_RTC_IMR); -  	seq_printf(seq, "update_IRQ\t: %s\n", -			(imr & AT91_RTC_ACKUPD) ? "yes" : "no"); +			(at91_rtc_imr & AT91_RTC_ACKUPD) ? "yes" : "no");  	seq_printf(seq, "periodic_IRQ\t: %s\n", -			(imr & AT91_RTC_SECEV) ? "yes" : "no"); +			(at91_rtc_imr & AT91_RTC_SECEV) ? "yes" : "no");  	return 0;  } @@ -227,7 +233,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)  	unsigned int rtsr;  	unsigned long events = 0; -	rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read(AT91_RTC_IMR); +	rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_imr;  	if (rtsr) {		/* this interrupt is shared!  Is it ours? */  		if (rtsr & AT91_RTC_ALARM)  			events |= (RTC_AF | RTC_IRQF); @@ -291,6 +297,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev)  	at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |  					AT91_RTC_SECEV | AT91_RTC_TIMEV |  					AT91_RTC_CALEV); +	at91_rtc_imr = 0;  	ret = request_irq(irq, at91_rtc_interrupt,  				IRQF_SHARED, @@ -329,6 +336,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)  	at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |  					AT91_RTC_SECEV | AT91_RTC_TIMEV |  					AT91_RTC_CALEV); +	at91_rtc_imr = 0;  	free_irq(irq, pdev);  	rtc_device_unregister(rtc); @@ -341,31 +349,35 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)  /* AT91RM9200 RTC Power management control */ -static u32 at91_rtc_imr; +static u32 at91_rtc_bkpimr; +  static int at91_rtc_suspend(struct device *dev)  {  	/* this IRQ is shared with DBGU and other hardware which isn't  	 * necessarily doing PM like we are...  	 */ -	at91_rtc_imr = at91_rtc_read(AT91_RTC_IMR) -			& (AT91_RTC_ALARM|AT91_RTC_SECEV); -	if (at91_rtc_imr) { -		if (device_may_wakeup(dev)) +	at91_rtc_bkpimr = at91_rtc_imr & (AT91_RTC_ALARM|AT91_RTC_SECEV); +	if (at91_rtc_bkpimr) { +		if (device_may_wakeup(dev)) {  			enable_irq_wake(irq); -		else -			at91_rtc_write(AT91_RTC_IDR, at91_rtc_imr); -	} +		} else { +			at91_rtc_write(AT91_RTC_IDR, at91_rtc_bkpimr); +			at91_rtc_imr &= ~at91_rtc_bkpimr; +		} +}  	return 0;  }  static int at91_rtc_resume(struct device *dev)  { -	if (at91_rtc_imr) { -		if (device_may_wakeup(dev)) +	if (at91_rtc_bkpimr) { +		if (device_may_wakeup(dev)) {  			disable_irq_wake(irq); -		else -			at91_rtc_write(AT91_RTC_IER, at91_rtc_imr); +		} else { +			at91_rtc_imr |= at91_rtc_bkpimr; +			at91_rtc_write(AT91_RTC_IER, at91_rtc_bkpimr); +		}  	}  	return 0;  } diff --git a/drivers/rtc/rtc-at91rm9200.h b/drivers/rtc/rtc-at91rm9200.h index da1945e5f71..5f940b6844c 100644 --- a/drivers/rtc/rtc-at91rm9200.h +++ b/drivers/rtc/rtc-at91rm9200.h @@ -64,7 +64,6 @@  #define	AT91_RTC_SCCR		0x1c			/* Status Clear Command Register */  #define	AT91_RTC_IER		0x20			/* Interrupt Enable Register */  #define	AT91_RTC_IDR		0x24			/* Interrupt Disable Register */ -#define	AT91_RTC_IMR		0x28			/* Interrupt Mask Register */  #define	AT91_RTC_VER		0x2c			/* Valid Entry Register */  #define		AT91_RTC_NVTIM		(1 <<  0)		/* Non valid Time */ diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index 0dde688ca09..969abbad7fe 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -239,11 +239,9 @@ static int da9052_rtc_probe(struct platform_device *pdev)  	rtc->da9052 = dev_get_drvdata(pdev->dev.parent);  	platform_set_drvdata(pdev, rtc); -	rtc->irq = platform_get_irq_byname(pdev, "ALM"); -	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, -				da9052_rtc_irq, -				IRQF_TRIGGER_LOW | IRQF_ONESHOT, -				"ALM", rtc); +	rtc->irq =  DA9052_IRQ_ALARM; +	ret = da9052_request_irq(rtc->da9052, rtc->irq, "ALM", +				da9052_rtc_irq, rtc);  	if (ret != 0) {  		rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);  		return ret; diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index 57233c88599..8f87fec27ce 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -14,6 +14,7 @@  #include <linux/platform_device.h>  #include <linux/of.h>  #include <linux/delay.h> +#include <linux/clk.h>  #include <linux/gfp.h>  #include <linux/module.h> @@ -41,6 +42,7 @@ struct rtc_plat_data {  	struct rtc_device *rtc;  	void __iomem *ioaddr;  	int		irq; +	struct clk	*clk;  };  static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -221,6 +223,7 @@ static int mv_rtc_probe(struct platform_device *pdev)  	struct rtc_plat_data *pdata;  	resource_size_t size;  	u32 rtc_time; +	int ret = 0;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) @@ -239,11 +242,17 @@ static int mv_rtc_probe(struct platform_device *pdev)  	if (!pdata->ioaddr)  		return -ENOMEM; +	pdata->clk = devm_clk_get(&pdev->dev, NULL); +	/* Not all SoCs require a clock.*/ +	if (!IS_ERR(pdata->clk)) +		clk_prepare_enable(pdata->clk); +  	/* make sure the 24 hours mode is enabled */  	rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);  	if (rtc_time & RTC_HOURS_12H_MODE) {  		dev_err(&pdev->dev, "24 Hours mode not supported.\n"); -		return -EINVAL; +		ret = -EINVAL; +		goto out;  	}  	/* make sure it is actually functional */ @@ -252,7 +261,8 @@ static int mv_rtc_probe(struct platform_device *pdev)  		rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);  		if (rtc_time == 0x01000000) {  			dev_err(&pdev->dev, "internal RTC not ticking\n"); -			return -ENODEV; +			ret = -ENODEV; +			goto out;  		}  	} @@ -268,8 +278,10 @@ static int mv_rtc_probe(struct platform_device *pdev)  	} else  		pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,  						 &mv_rtc_ops, THIS_MODULE); -	if (IS_ERR(pdata->rtc)) -		return PTR_ERR(pdata->rtc); +	if (IS_ERR(pdata->rtc)) { +		ret = PTR_ERR(pdata->rtc); +		goto out; +	}  	if (pdata->irq >= 0) {  		writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); @@ -282,6 +294,11 @@ static int mv_rtc_probe(struct platform_device *pdev)  	}  	return 0; +out: +	if (!IS_ERR(pdata->clk)) +		clk_disable_unprepare(pdata->clk); + +	return ret;  }  static int __exit mv_rtc_remove(struct platform_device *pdev) @@ -292,6 +309,9 @@ static int __exit mv_rtc_remove(struct platform_device *pdev)  		device_init_wakeup(&pdev->dev, 0);  	rtc_device_unregister(pdata->rtc); +	if (!IS_ERR(pdata->clk)) +		clk_disable_unprepare(pdata->clk); +  	return 0;  } diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index 9978ad4433c..5ac9c935c15 100644 --- a/drivers/s390/block/scm_blk.c +++ b/drivers/s390/block/scm_blk.c @@ -135,6 +135,11 @@ static const struct block_device_operations scm_blk_devops = {  	.release = scm_release,  }; +static bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req) +{ +	return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT; +} +  static void scm_request_prepare(struct scm_request *scmrq)  {  	struct scm_blk_dev *bdev = scmrq->bdev; @@ -195,14 +200,18 @@ void scm_request_requeue(struct scm_request *scmrq)  	scm_release_cluster(scmrq);  	blk_requeue_request(bdev->rq, scmrq->request); +	atomic_dec(&bdev->queued_reqs);  	scm_request_done(scmrq);  	scm_ensure_queue_restart(bdev);  }  void scm_request_finish(struct scm_request *scmrq)  { +	struct scm_blk_dev *bdev = scmrq->bdev; +  	scm_release_cluster(scmrq);  	blk_end_request_all(scmrq->request, scmrq->error); +	atomic_dec(&bdev->queued_reqs);  	scm_request_done(scmrq);  } @@ -218,6 +227,10 @@ static void scm_blk_request(struct request_queue *rq)  		if (req->cmd_type != REQ_TYPE_FS)  			continue; +		if (!scm_permit_request(bdev, req)) { +			scm_ensure_queue_restart(bdev); +			return; +		}  		scmrq = scm_request_fetch();  		if (!scmrq) {  			SCM_LOG(5, "no request"); @@ -231,11 +244,13 @@ static void scm_blk_request(struct request_queue *rq)  			return;  		}  		if (scm_need_cluster_request(scmrq)) { +			atomic_inc(&bdev->queued_reqs);  			blk_start_request(req);  			scm_initiate_cluster_request(scmrq);  			return;  		}  		scm_request_prepare(scmrq); +		atomic_inc(&bdev->queued_reqs);  		blk_start_request(req);  		ret = scm_start_aob(scmrq->aob); @@ -244,7 +259,6 @@ static void scm_blk_request(struct request_queue *rq)  			scm_request_requeue(scmrq);  			return;  		} -		atomic_inc(&bdev->queued_reqs);  	}  } @@ -280,6 +294,38 @@ void scm_blk_irq(struct scm_device *scmdev, void *data, int error)  	tasklet_hi_schedule(&bdev->tasklet);  } +static void scm_blk_handle_error(struct scm_request *scmrq) +{ +	struct scm_blk_dev *bdev = scmrq->bdev; +	unsigned long flags; + +	if (scmrq->error != -EIO) +		goto restart; + +	/* For -EIO the response block is valid. */ +	switch (scmrq->aob->response.eqc) { +	case EQC_WR_PROHIBIT: +		spin_lock_irqsave(&bdev->lock, flags); +		if (bdev->state != SCM_WR_PROHIBIT) +			pr_info("%lu: Write access to the SCM increment is suspended\n", +				(unsigned long) bdev->scmdev->address); +		bdev->state = SCM_WR_PROHIBIT; +		spin_unlock_irqrestore(&bdev->lock, flags); +		goto requeue; +	default: +		break; +	} + +restart: +	if (!scm_start_aob(scmrq->aob)) +		return; + +requeue: +	spin_lock_irqsave(&bdev->rq_lock, flags); +	scm_request_requeue(scmrq); +	spin_unlock_irqrestore(&bdev->rq_lock, flags); +} +  static void scm_blk_tasklet(struct scm_blk_dev *bdev)  {  	struct scm_request *scmrq; @@ -293,11 +339,8 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)  		spin_unlock_irqrestore(&bdev->lock, flags);  		if (scmrq->error && scmrq->retries-- > 0) { -			if (scm_start_aob(scmrq->aob)) { -				spin_lock_irqsave(&bdev->rq_lock, flags); -				scm_request_requeue(scmrq); -				spin_unlock_irqrestore(&bdev->rq_lock, flags); -			} +			scm_blk_handle_error(scmrq); +  			/* Request restarted or requeued, handle next. */  			spin_lock_irqsave(&bdev->lock, flags);  			continue; @@ -310,7 +353,6 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)  		}  		scm_request_finish(scmrq); -		atomic_dec(&bdev->queued_reqs);  		spin_lock_irqsave(&bdev->lock, flags);  	}  	spin_unlock_irqrestore(&bdev->lock, flags); @@ -332,6 +374,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)  	}  	bdev->scmdev = scmdev; +	bdev->state = SCM_OPER;  	spin_lock_init(&bdev->rq_lock);  	spin_lock_init(&bdev->lock);  	INIT_LIST_HEAD(&bdev->finished_requests); @@ -396,6 +439,18 @@ void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)  	put_disk(bdev->gendisk);  } +void scm_blk_set_available(struct scm_blk_dev *bdev) +{ +	unsigned long flags; + +	spin_lock_irqsave(&bdev->lock, flags); +	if (bdev->state == SCM_WR_PROHIBIT) +		pr_info("%lu: Write access to the SCM increment is restored\n", +			(unsigned long) bdev->scmdev->address); +	bdev->state = SCM_OPER; +	spin_unlock_irqrestore(&bdev->lock, flags); +} +  static int __init scm_blk_init(void)  {  	int ret = -EINVAL; diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h index 3c1ccf49464..8b387b32fd6 100644 --- a/drivers/s390/block/scm_blk.h +++ b/drivers/s390/block/scm_blk.h @@ -21,6 +21,7 @@ struct scm_blk_dev {  	spinlock_t rq_lock;	/* guard the request queue */  	spinlock_t lock;	/* guard the rest of the blockdev */  	atomic_t queued_reqs; +	enum {SCM_OPER, SCM_WR_PROHIBIT} state;  	struct list_head finished_requests;  #ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE  	struct list_head cluster_list; @@ -48,6 +49,7 @@ struct scm_request {  int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);  void scm_blk_dev_cleanup(struct scm_blk_dev *); +void scm_blk_set_available(struct scm_blk_dev *);  void scm_blk_irq(struct scm_device *, void *, int);  void scm_request_finish(struct scm_request *); diff --git a/drivers/s390/block/scm_drv.c b/drivers/s390/block/scm_drv.c index 9fa0a908607..5f6180d6ff0 100644 --- a/drivers/s390/block/scm_drv.c +++ b/drivers/s390/block/scm_drv.c @@ -13,12 +13,23 @@  #include <asm/eadm.h>  #include "scm_blk.h" -static void notify(struct scm_device *scmdev) +static void scm_notify(struct scm_device *scmdev, enum scm_event event)  { -	pr_info("%lu: The capabilities of the SCM increment changed\n", -		(unsigned long) scmdev->address); -	SCM_LOG(2, "State changed"); -	SCM_LOG_STATE(2, scmdev); +	struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev); + +	switch (event) { +	case SCM_CHANGE: +		pr_info("%lu: The capabilities of the SCM increment changed\n", +			(unsigned long) scmdev->address); +		SCM_LOG(2, "State changed"); +		SCM_LOG_STATE(2, scmdev); +		break; +	case SCM_AVAIL: +		SCM_LOG(2, "Increment available"); +		SCM_LOG_STATE(2, scmdev); +		scm_blk_set_available(bdev); +		break; +	}  }  static int scm_probe(struct scm_device *scmdev) @@ -64,7 +75,7 @@ static struct scm_driver scm_drv = {  		.name = "scm_block",  		.owner = THIS_MODULE,  	}, -	.notify = notify, +	.notify = scm_notify,  	.probe = scm_probe,  	.remove = scm_remove,  	.handler = scm_blk_irq, diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 30a2255389e..cd798386b62 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -627,6 +627,8 @@ static int __init sclp_detect_standby_memory(void)  	struct read_storage_sccb *sccb;  	int i, id, assigned, rc; +	if (OLDMEM_BASE) /* No standby memory in kdump mode */ +		return 0;  	if (!early_read_info_sccb_valid)  		return 0;  	if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL) diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 31ceef1beb8..e16c553f655 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -433,6 +433,20 @@ static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area)  			      " failed (rc=%d).\n", ret);  } +static void chsc_process_sei_scm_avail(struct chsc_sei_nt0_area *sei_area) +{ +	int ret; + +	CIO_CRW_EVENT(4, "chsc: scm available information\n"); +	if (sei_area->rs != 7) +		return; + +	ret = scm_process_availability_information(); +	if (ret) +		CIO_CRW_EVENT(0, "chsc: process availability information" +			      " failed (rc=%d).\n", ret); +} +  static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)  {  	switch (sei_area->cc) { @@ -468,6 +482,9 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area)  	case 12: /* scm change notification */  		chsc_process_sei_scm_change(sei_area);  		break; +	case 14: /* scm available notification */ +		chsc_process_sei_scm_avail(sei_area); +		break;  	default: /* other stuff */  		CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n",  			      sei_area->cc); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 227e05f674b..349d5fc4719 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -156,8 +156,10 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);  #ifdef CONFIG_SCM_BUS  int scm_update_information(void); +int scm_process_availability_information(void);  #else /* CONFIG_SCM_BUS */  static inline int scm_update_information(void) { return 0; } +static inline int scm_process_availability_information(void) { return 0; }  #endif /* CONFIG_SCM_BUS */ diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c index bcf20f3aa51..46ec25632e8 100644 --- a/drivers/s390/cio/scm.c +++ b/drivers/s390/cio/scm.c @@ -211,7 +211,7 @@ static void scmdev_update(struct scm_device *scmdev, struct sale *sale)  		goto out;  	scmdrv = to_scm_drv(scmdev->dev.driver);  	if (changed && scmdrv->notify) -		scmdrv->notify(scmdev); +		scmdrv->notify(scmdev, SCM_CHANGE);  out:  	device_unlock(&scmdev->dev);  	if (changed) @@ -297,6 +297,22 @@ int scm_update_information(void)  	return ret;  } +static int scm_dev_avail(struct device *dev, void *unused) +{ +	struct scm_driver *scmdrv = to_scm_drv(dev->driver); +	struct scm_device *scmdev = to_scm_dev(dev); + +	if (dev->driver && scmdrv->notify) +		scmdrv->notify(scmdev, SCM_AVAIL); + +	return 0; +} + +int scm_process_availability_information(void) +{ +	return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail); +} +  static int __init scm_init(void)  {  	int ret; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d87961d4c0d..8c0622399fc 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -916,6 +916,7 @@ int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,  	void *reply_param);  int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);  int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int); +int qeth_get_elements_for_frags(struct sk_buff *);  int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,  			struct sk_buff *, struct qeth_hdr *, int, int, int);  int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 0d8cdff8181..0d73a999983 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3679,6 +3679,25 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,  }  EXPORT_SYMBOL_GPL(qeth_get_priority_queue); +int qeth_get_elements_for_frags(struct sk_buff *skb) +{ +	int cnt, length, e, elements = 0; +	struct skb_frag_struct *frag; +	char *data; + +	for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { +		frag = &skb_shinfo(skb)->frags[cnt]; +		data = (char *)page_to_phys(skb_frag_page(frag)) + +			frag->page_offset; +		length = frag->size; +		e = PFN_UP((unsigned long)data + length - 1) - +			PFN_DOWN((unsigned long)data); +		elements += e; +	} +	return elements; +} +EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); +  int qeth_get_elements_no(struct qeth_card *card, void *hdr,  		     struct sk_buff *skb, int elems)  { @@ -3686,7 +3705,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,  	int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) -  		PFN_DOWN((unsigned long)skb->data); -	elements_needed += skb_shinfo(skb)->nr_frags; +	elements_needed += qeth_get_elements_for_frags(skb); +  	if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {  		QETH_DBF_MESSAGE(2, "Invalid size of IP packet "  			"(Number=%d / Length=%d). Discarded.\n", @@ -3771,12 +3791,23 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,  	for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) {  		frag = &skb_shinfo(skb)->frags[cnt]; -		buffer->element[element].addr = (char *) -			page_to_phys(skb_frag_page(frag)) -			+ frag->page_offset; -		buffer->element[element].length = frag->size; -		buffer->element[element].eflags = SBAL_EFLAGS_MIDDLE_FRAG; -		element++; +		data = (char *)page_to_phys(skb_frag_page(frag)) + +			frag->page_offset; +		length = frag->size; +		while (length > 0) { +			length_here = PAGE_SIZE - +				((unsigned long) data % PAGE_SIZE); +			if (length < length_here) +				length_here = length; + +			buffer->element[element].addr = data; +			buffer->element[element].length = length_here; +			buffer->element[element].eflags = +				SBAL_EFLAGS_MIDDLE_FRAG; +			length -= length_here; +			data += length_here; +			element++; +		}  	}  	if (buffer->element[element - 1].eflags) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 091ca0efa1c..8710337dab3 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -623,7 +623,7 @@ static int qeth_l3_send_setrouting(struct qeth_card *card,  	return rc;  } -static void qeth_l3_correct_routing_type(struct qeth_card *card, +static int qeth_l3_correct_routing_type(struct qeth_card *card,  		enum qeth_routing_types *type, enum qeth_prot_versions prot)  {  	if (card->info.type == QETH_CARD_TYPE_IQD) { @@ -632,7 +632,7 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,  		case PRIMARY_CONNECTOR:  		case SECONDARY_CONNECTOR:  		case MULTICAST_ROUTER: -			return; +			return 0;  		default:  			goto out_inval;  		} @@ -641,17 +641,18 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,  		case NO_ROUTER:  		case PRIMARY_ROUTER:  		case SECONDARY_ROUTER: -			return; +			return 0;  		case MULTICAST_ROUTER:  			if (qeth_is_ipafunc_supported(card, prot,  						      IPA_OSA_MC_ROUTER)) -				return; +				return 0;  		default:  			goto out_inval;  		}  	}  out_inval:  	*type = NO_ROUTER; +	return -EINVAL;  }  int qeth_l3_setrouting_v4(struct qeth_card *card) @@ -660,8 +661,10 @@ int qeth_l3_setrouting_v4(struct qeth_card *card)  	QETH_CARD_TEXT(card, 3, "setrtg4"); -	qeth_l3_correct_routing_type(card, &card->options.route4.type, +	rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,  				  QETH_PROT_IPV4); +	if (rc) +		return rc;  	rc = qeth_l3_send_setrouting(card, card->options.route4.type,  				  QETH_PROT_IPV4); @@ -683,8 +686,10 @@ int qeth_l3_setrouting_v6(struct qeth_card *card)  	if (!qeth_is_supported(card, IPA_IPV6))  		return 0; -	qeth_l3_correct_routing_type(card, &card->options.route6.type, +	rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,  				  QETH_PROT_IPV6); +	if (rc) +		return rc;  	rc = qeth_l3_send_setrouting(card, card->options.route6.type,  				  QETH_PROT_IPV6); @@ -2898,7 +2903,9 @@ static inline int qeth_l3_tso_elements(struct sk_buff *skb)  		tcp_hdr(skb)->doff * 4;  	int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);  	int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd); -	elements += skb_shinfo(skb)->nr_frags; + +	elements += qeth_get_elements_for_frags(skb); +  	return elements;  } @@ -3348,7 +3355,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)  		rc = -ENODEV;  		goto out_remove;  	} -	qeth_trace_features(card);  	if (!card->dev && qeth_l3_setup_netdev(card)) {  		rc = -ENODEV; @@ -3425,6 +3431,7 @@ contin:  		qeth_l3_set_multicast_list(card->dev);  		rtnl_unlock();  	} +	qeth_trace_features(card);  	/* let user_space know that device is online */  	kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);  	mutex_unlock(&card->conf_mutex); diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index ebc37948626..e70af2406ff 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -87,6 +87,8 @@ static ssize_t qeth_l3_dev_route_store(struct qeth_card *card,  			rc = qeth_l3_setrouting_v6(card);  	}  out: +	if (rc) +		route->type = old_route_type;  	mutex_unlock(&card->conf_mutex);  	return rc ? rc : count;  } diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c index 192cf088f83..57b45190479 100644 --- a/drivers/staging/comedi/drivers/dt9812.c +++ b/drivers/staging/comedi/drivers/dt9812.c @@ -947,12 +947,13 @@ static int dt9812_di_rinsn(struct comedi_device *dev,  			   unsigned int *data)  {  	struct comedi_dt9812 *devpriv = dev->private; +	unsigned int channel = CR_CHAN(insn->chanspec);  	int n;  	u8 bits = 0;  	dt9812_digital_in(devpriv->slot, &bits);  	for (n = 0; n < insn->n; n++) -		data[n] = ((1 << insn->chanspec) & bits) != 0; +		data[n] = ((1 << channel) & bits) != 0;  	return n;  } @@ -961,12 +962,13 @@ static int dt9812_do_winsn(struct comedi_device *dev,  			   unsigned int *data)  {  	struct comedi_dt9812 *devpriv = dev->private; +	unsigned int channel = CR_CHAN(insn->chanspec);  	int n;  	u8 bits = 0;  	dt9812_digital_out_shadow(devpriv->slot, &bits);  	for (n = 0; n < insn->n; n++) { -		u8 mask = 1 << insn->chanspec; +		u8 mask = 1 << channel;  		bits &= ~mask;  		if (data[n]) @@ -981,13 +983,13 @@ static int dt9812_ai_rinsn(struct comedi_device *dev,  			   unsigned int *data)  {  	struct comedi_dt9812 *devpriv = dev->private; +	unsigned int channel = CR_CHAN(insn->chanspec);  	int n;  	for (n = 0; n < insn->n; n++) {  		u16 value = 0; -		dt9812_analog_in(devpriv->slot, insn->chanspec, &value, -				 DT9812_GAIN_1); +		dt9812_analog_in(devpriv->slot, channel, &value, DT9812_GAIN_1);  		data[n] = value;  	}  	return n; @@ -998,12 +1000,13 @@ static int dt9812_ao_rinsn(struct comedi_device *dev,  			   unsigned int *data)  {  	struct comedi_dt9812 *devpriv = dev->private; +	unsigned int channel = CR_CHAN(insn->chanspec);  	int n;  	u16 value;  	for (n = 0; n < insn->n; n++) {  		value = 0; -		dt9812_analog_out_shadow(devpriv->slot, insn->chanspec, &value); +		dt9812_analog_out_shadow(devpriv->slot, channel, &value);  		data[n] = value;  	}  	return n; @@ -1014,10 +1017,11 @@ static int dt9812_ao_winsn(struct comedi_device *dev,  			   unsigned int *data)  {  	struct comedi_dt9812 *devpriv = dev->private; +	unsigned int channel = CR_CHAN(insn->chanspec);  	int n;  	for (n = 0; n < insn->n; n++) -		dt9812_analog_out(devpriv->slot, insn->chanspec, data[n]); +		dt9812_analog_out(devpriv->slot, channel, data[n]);  	return n;  } diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c index 81a1fe66157..71a73ec5af8 100644 --- a/drivers/staging/comedi/drivers/s626.c +++ b/drivers/staging/comedi/drivers/s626.c @@ -1483,7 +1483,7 @@ static int s626_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)  	case TRIG_NONE:  		/*  continous acquisition */  		devpriv->ai_continous = 1; -		devpriv->ai_sample_count = 0; +		devpriv->ai_sample_count = 1;  		break;  	} diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c index 1a0062a0445..6aac1f60bc4 100644 --- a/drivers/staging/comedi/drivers/usbdux.c +++ b/drivers/staging/comedi/drivers/usbdux.c @@ -730,10 +730,14 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)  static int usbduxsub_start(struct usbduxsub *usbduxsub)  {  	int errcode = 0; -	uint8_t local_transfer_buffer[16]; +	uint8_t *local_transfer_buffer; + +	local_transfer_buffer = kmalloc(1, GFP_KERNEL); +	if (!local_transfer_buffer) +		return -ENOMEM;  	/* 7f92 to zero */ -	local_transfer_buffer[0] = 0; +	*local_transfer_buffer = 0;  	errcode = usb_control_msg(usbduxsub->usbdev,  				  /* create a pipe for a control transfer */  				  usb_sndctrlpipe(usbduxsub->usbdev, 0), @@ -751,22 +755,25 @@ static int usbduxsub_start(struct usbduxsub *usbduxsub)  				  1,  				  /* Timeout */  				  BULK_TIMEOUT); -	if (errcode < 0) { +	if (errcode < 0)  		dev_err(&usbduxsub->interface->dev,  			"comedi_: control msg failed (start)\n"); -		return errcode; -	} -	return 0; + +	kfree(local_transfer_buffer); +	return errcode;  }  static int usbduxsub_stop(struct usbduxsub *usbduxsub)  {  	int errcode = 0; +	uint8_t *local_transfer_buffer; -	uint8_t local_transfer_buffer[16]; +	local_transfer_buffer = kmalloc(1, GFP_KERNEL); +	if (!local_transfer_buffer) +		return -ENOMEM;  	/* 7f92 to one */ -	local_transfer_buffer[0] = 1; +	*local_transfer_buffer = 1;  	errcode = usb_control_msg(usbduxsub->usbdev,  				  usb_sndctrlpipe(usbduxsub->usbdev, 0),  				  /* bRequest, "Firmware" */ @@ -781,12 +788,12 @@ static int usbduxsub_stop(struct usbduxsub *usbduxsub)  				  1,  				  /* Timeout */  				  BULK_TIMEOUT); -	if (errcode < 0) { +	if (errcode < 0)  		dev_err(&usbduxsub->interface->dev,  			"comedi_: control msg failed (stop)\n"); -		return errcode; -	} -	return 0; + +	kfree(local_transfer_buffer); +	return errcode;  }  static int usbduxsub_upload(struct usbduxsub *usbduxsub, diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c index 4bf5dd094dc..1ba0e3df492 100644 --- a/drivers/staging/comedi/drivers/usbduxfast.c +++ b/drivers/staging/comedi/drivers/usbduxfast.c @@ -436,10 +436,14 @@ static void usbduxfastsub_ai_Irq(struct urb *urb)  static int usbduxfastsub_start(struct usbduxfastsub_s *udfs)  {  	int ret; -	unsigned char local_transfer_buffer[16]; +	unsigned char *local_transfer_buffer; + +	local_transfer_buffer = kmalloc(1, GFP_KERNEL); +	if (!local_transfer_buffer) +		return -ENOMEM;  	/* 7f92 to zero */ -	local_transfer_buffer[0] = 0; +	*local_transfer_buffer = 0;  	/* bRequest, "Firmware" */  	ret = usb_control_msg(udfs->usbdev, usb_sndctrlpipe(udfs->usbdev, 0),  			      USBDUXFASTSUB_FIRMWARE, @@ -450,22 +454,25 @@ static int usbduxfastsub_start(struct usbduxfastsub_s *udfs)  			      local_transfer_buffer,  			      1,      /* Length */  			      EZTIMEOUT);    /* Timeout */ -	if (ret < 0) { +	if (ret < 0)  		dev_err(&udfs->interface->dev,  			"control msg failed (start)\n"); -		return ret; -	} -	return 0; +	kfree(local_transfer_buffer); +	return ret;  }  static int usbduxfastsub_stop(struct usbduxfastsub_s *udfs)  {  	int ret; -	unsigned char local_transfer_buffer[16]; +	unsigned char *local_transfer_buffer; + +	local_transfer_buffer = kmalloc(1, GFP_KERNEL); +	if (!local_transfer_buffer) +		return -ENOMEM;  	/* 7f92 to one */ -	local_transfer_buffer[0] = 1; +	*local_transfer_buffer = 1;  	/* bRequest, "Firmware" */  	ret = usb_control_msg(udfs->usbdev, usb_sndctrlpipe(udfs->usbdev, 0),  			      USBDUXFASTSUB_FIRMWARE, @@ -474,13 +481,12 @@ static int usbduxfastsub_stop(struct usbduxfastsub_s *udfs)  			      0x0000,	/* Index */  			      local_transfer_buffer, 1,	/* Length */  			      EZTIMEOUT);	/* Timeout */ -	if (ret < 0) { +	if (ret < 0)  		dev_err(&udfs->interface->dev,  			"control msg failed (stop)\n"); -		return ret; -	} -	return 0; +	kfree(local_transfer_buffer); +	return ret;  }  static int usbduxfastsub_upload(struct usbduxfastsub_s *udfs, diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c index d066351a71b..a728c8fc32a 100644 --- a/drivers/staging/comedi/drivers/usbduxsigma.c +++ b/drivers/staging/comedi/drivers/usbduxsigma.c @@ -681,7 +681,11 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)  static int usbduxsub_start(struct usbduxsub *usbduxsub)  {  	int errcode = 0; -	uint8_t local_transfer_buffer[16]; +	uint8_t *local_transfer_buffer; + +	local_transfer_buffer = kmalloc(16, GFP_KERNEL); +	if (!local_transfer_buffer) +		return -ENOMEM;  	/* 7f92 to zero */  	local_transfer_buffer[0] = 0; @@ -702,19 +706,22 @@ static int usbduxsub_start(struct usbduxsub *usbduxsub)  				  1,  				  /* Timeout */  				  BULK_TIMEOUT); -	if (errcode < 0) { +	if (errcode < 0)  		dev_err(&usbduxsub->interface->dev,  			"comedi_: control msg failed (start)\n"); -		return errcode; -	} -	return 0; + +	kfree(local_transfer_buffer); +	return errcode;  }  static int usbduxsub_stop(struct usbduxsub *usbduxsub)  {  	int errcode = 0; +	uint8_t *local_transfer_buffer; -	uint8_t local_transfer_buffer[16]; +	local_transfer_buffer = kmalloc(16, GFP_KERNEL); +	if (!local_transfer_buffer) +		return -ENOMEM;  	/* 7f92 to one */  	local_transfer_buffer[0] = 1; @@ -732,12 +739,12 @@ static int usbduxsub_stop(struct usbduxsub *usbduxsub)  				  1,  				  /* Timeout */  				  BULK_TIMEOUT); -	if (errcode < 0) { +	if (errcode < 0)  		dev_err(&usbduxsub->interface->dev,  			"comedi_: control msg failed (stop)\n"); -		return errcode; -	} -	return 0; + +	kfree(local_transfer_buffer); +	return errcode;  }  static int usbduxsub_upload(struct usbduxsub *usbduxsub, diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 4b3a019409b..b028b0d1317 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -483,17 +483,6 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,  		goto err_out;  	} -	ipu_crtc->irq = ipu_idmac_channel_irq(ipu, ipu_crtc->ipu_ch, -			IPU_IRQ_EOF); -	ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, -			"imx_drm", ipu_crtc); -	if (ret < 0) { -		dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); -		goto err_out; -	} - -	disable_irq(ipu_crtc->irq); -  	return 0;  err_out:  	ipu_put_resources(ipu_crtc); @@ -504,6 +493,7 @@ err_out:  static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,  		struct ipu_client_platformdata *pdata)  { +	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);  	int ret;  	ret = ipu_get_resources(ipu_crtc, pdata); @@ -522,6 +512,17 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,  		goto err_put_resources;  	} +	ipu_crtc->irq = ipu_idmac_channel_irq(ipu, ipu_crtc->ipu_ch, +			IPU_IRQ_EOF); +	ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, +			"imx_drm", ipu_crtc); +	if (ret < 0) { +		dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); +		goto err_put_resources; +	} + +	disable_irq(ipu_crtc->irq); +  	return 0;  err_put_resources: diff --git a/drivers/staging/tidspbridge/rmgr/drv.c b/drivers/staging/tidspbridge/rmgr/drv.c index db1da28cecb..be26917a689 100644 --- a/drivers/staging/tidspbridge/rmgr/drv.c +++ b/drivers/staging/tidspbridge/rmgr/drv.c @@ -76,37 +76,28 @@ int drv_insert_node_res_element(void *hnode, void *node_resource,  	struct node_res_object **node_res_obj =  	    (struct node_res_object **)node_resource;  	struct process_context *ctxt = (struct process_context *)process_ctxt; -	int status = 0;  	int retval;  	*node_res_obj = kzalloc(sizeof(struct node_res_object), GFP_KERNEL); -	if (!*node_res_obj) { -		status = -ENOMEM; -		goto func_end; -	} +	if (!*node_res_obj) +		return -ENOMEM;  	(*node_res_obj)->node = hnode; -	retval = idr_get_new(ctxt->node_id, *node_res_obj, -						&(*node_res_obj)->id); -	if (retval == -EAGAIN) { -		if (!idr_pre_get(ctxt->node_id, GFP_KERNEL)) { -			pr_err("%s: OUT OF MEMORY\n", __func__); -			status = -ENOMEM; -			goto func_end; -		} - -		retval = idr_get_new(ctxt->node_id, *node_res_obj, -						&(*node_res_obj)->id); +	retval = idr_alloc(ctxt->node_id, *node_res_obj, 0, 0, GFP_KERNEL); +	if (retval >= 0) { +		(*node_res_obj)->id = retval; +		return 0;  	} -	if (retval) { + +	kfree(*node_res_obj); + +	if (retval == -ENOSPC) {  		pr_err("%s: FAILED, IDR is FULL\n", __func__); -		status = -EFAULT; +		return -EFAULT; +	} else { +		pr_err("%s: OUT OF MEMORY\n", __func__); +		return -ENOMEM;  	} -func_end: -	if (status) -		kfree(*node_res_obj); - -	return status;  }  /* Release all Node resources and its context @@ -201,35 +192,26 @@ int drv_proc_insert_strm_res_element(void *stream_obj,  	struct strm_res_object **pstrm_res =  	    (struct strm_res_object **)strm_res;  	struct process_context *ctxt = (struct process_context *)process_ctxt; -	int status = 0;  	int retval;  	*pstrm_res = kzalloc(sizeof(struct strm_res_object), GFP_KERNEL); -	if (*pstrm_res == NULL) { -		status = -EFAULT; -		goto func_end; -	} +	if (*pstrm_res == NULL) +		return -EFAULT;  	(*pstrm_res)->stream = stream_obj; -	retval = idr_get_new(ctxt->stream_id, *pstrm_res, -						&(*pstrm_res)->id); -	if (retval == -EAGAIN) { -		if (!idr_pre_get(ctxt->stream_id, GFP_KERNEL)) { -			pr_err("%s: OUT OF MEMORY\n", __func__); -			status = -ENOMEM; -			goto func_end; -		} - -		retval = idr_get_new(ctxt->stream_id, *pstrm_res, -						&(*pstrm_res)->id); +	retval = idr_alloc(ctxt->stream_id, *pstrm_res, 0, 0, GFP_KERNEL); +	if (retval >= 0) { +		(*pstrm_res)->id = retval; +		return 0;  	} -	if (retval) { + +	if (retval == -ENOSPC) {  		pr_err("%s: FAILED, IDR is FULL\n", __func__); -		status = -EPERM; +		return -EPERM; +	} else { +		pr_err("%s: OUT OF MEMORY\n", __func__); +		return -ENOMEM;  	} - -func_end: -	return status;  }  static int drv_proc_free_strm_res(int id, void *p, void *process_ctxt) diff --git a/drivers/staging/vt6656/card.c b/drivers/staging/vt6656/card.c index 22918a106d7..d2479b76645 100644 --- a/drivers/staging/vt6656/card.c +++ b/drivers/staging/vt6656/card.c @@ -790,7 +790,7 @@ u64 CARDqGetNextTBTT(u64 qwTSF, WORD wBeaconInterval)  	if ((~uLowNextTBTT) < uLowRemain)  		qwTSF = ((qwTSF >> 32) + 1) << 32; -	qwTSF = (qwTSF & 0xffffffff00000000UL) | +	qwTSF = (qwTSF & 0xffffffff00000000ULL) |  		(u64)(uLowNextTBTT + uLowRemain);      return (qwTSF); diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index d5f53e1a74a..a5063a6f64d 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -669,8 +669,6 @@ static int vt6656_suspend(struct usb_interface *intf, pm_message_t message)  	if (device->flags & DEVICE_FLAGS_OPENED)  		device_close(device->dev); -	usb_put_dev(interface_to_usbdev(intf)); -  	return 0;  } @@ -681,8 +679,6 @@ static int vt6656_resume(struct usb_interface *intf)  	if (!device || !device->dev)  		return -ENODEV; -	usb_get_dev(interface_to_usbdev(intf)); -  	if (!(device->flags & DEVICE_FLAGS_OPENED))  		device_open(device->dev); diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 73582705e8c..5c371453096 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -15,7 +15,7 @@ config RAMSTER  	depends on CONFIGFS_FS=y && SYSFS=y && !HIGHMEM && ZCACHE=y  	depends on NET  	# must ensure struct page is 8-byte aligned -	select HAVE_ALIGNED_STRUCT_PAGE if !64_BIT +	select HAVE_ALIGNED_STRUCT_PAGE if !64BIT  	default n  	help  	  RAMster allows RAM on other machines in a cluster to be utilized diff --git a/drivers/staging/zcache/ramster/tcp.c b/drivers/staging/zcache/ramster/tcp.c index aa2a1a763aa..f6e1e5209d8 100644 --- a/drivers/staging/zcache/ramster/tcp.c +++ b/drivers/staging/zcache/ramster/tcp.c @@ -300,27 +300,22 @@ static u8 r2net_num_from_nn(struct r2net_node *nn)  static int r2net_prep_nsw(struct r2net_node *nn, struct r2net_status_wait *nsw)  { -	int ret = 0; +	int ret; -	do { -		if (!idr_pre_get(&nn->nn_status_idr, GFP_ATOMIC)) { -			ret = -EAGAIN; -			break; -		} -		spin_lock(&nn->nn_lock); -		ret = idr_get_new(&nn->nn_status_idr, nsw, &nsw->ns_id); -		if (ret == 0) -			list_add_tail(&nsw->ns_node_item, -				      &nn->nn_status_list); -		spin_unlock(&nn->nn_lock); -	} while (ret == -EAGAIN); +	spin_lock(&nn->nn_lock); +	ret = idr_alloc(&nn->nn_status_idr, nsw, 0, 0, GFP_ATOMIC); +	if (ret >= 0) { +		nsw->ns_id = ret; +		list_add_tail(&nsw->ns_node_item, &nn->nn_status_list); +	} +	spin_unlock(&nn->nn_lock); -	if (ret == 0)  { +	if (ret >= 0) {  		init_waitqueue_head(&nsw->ns_wq);  		nsw->ns_sys_status = R2NET_ERR_NONE;  		nsw->ns_status = 0; +		return 0;  	} -  	return ret;  } diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index db0cf7c8add..a0fc7b9eea6 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -166,6 +166,7 @@ static int chap_server_compute_md5(  {  	char *endptr;  	unsigned long id; +	unsigned char id_as_uchar;  	unsigned char digest[MD5_SIGNATURE_SIZE];  	unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2];  	unsigned char identifier[10], *challenge = NULL; @@ -355,7 +356,9 @@ static int chap_server_compute_md5(  		goto out;  	} -	sg_init_one(&sg, &id, 1); +	/* To handle both endiannesses */ +	id_as_uchar = id; +	sg_init_one(&sg, &id_as_uchar, 1);  	ret = crypto_hash_update(&desc, &sg, 1);  	if (ret < 0) {  		pr_err("crypto_hash_update() failed for id\n"); diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h index bc02b018ae4..37ffc5bd239 100644 --- a/drivers/target/target_core_file.h +++ b/drivers/target/target_core_file.h @@ -7,7 +7,7 @@  #define FD_DEVICE_QUEUE_DEPTH	32  #define FD_MAX_DEVICE_QUEUE_DEPTH 128  #define FD_BLOCKSIZE		512 -#define FD_MAX_SECTORS		1024 +#define FD_MAX_SECTORS		2048  #define RRF_EMULATE_CDB		0x01  #define RRF_GOT_LBA		0x02 diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 82e78d72fdb..e992b27aa09 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -883,7 +883,14 @@ pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,  		pr_debug("PSCSI: i: %d page: %p len: %d off: %d\n", i,  			page, len, off); -		while (len > 0 && data_len > 0) { +		/* +		 * We only have one page of data in each sg element, +		 * we can not cross a page boundary. +		 */ +		if (off + len > PAGE_SIZE) +			goto fail; + +		if (len > 0 && data_len > 0) {  			bytes = min_t(unsigned int, len, PAGE_SIZE - off);  			bytes = min(bytes, data_len); @@ -940,9 +947,7 @@ pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,  				bio = NULL;  			} -			len -= bytes;  			data_len -= bytes; -			off = 0;  		}  	} diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 290230de2c5..60d4b5185f3 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -464,8 +464,11 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)  		break;  	case SYNCHRONIZE_CACHE:  	case SYNCHRONIZE_CACHE_16: -		if (!ops->execute_sync_cache) -			return TCM_UNSUPPORTED_SCSI_OPCODE; +		if (!ops->execute_sync_cache) { +			size = 0; +			cmd->execute_cmd = sbc_emulate_noop; +			break; +		}  		/*  		 * Extract LBA and range to be flushed for emulated SYNCHRONIZE_CACHE diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 9169d6a5d7e..aac9d2727e3 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -711,7 +711,8 @@ int core_tpg_register(  	if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) {  		if (core_tpg_setup_virtual_lun0(se_tpg) < 0) { -			kfree(se_tpg); +			array_free(se_tpg->tpg_lun_list, +				   TRANSPORT_MAX_LUNS_PER_TPG);  			return -ENOMEM;  		}  	} diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2030b608136..3243ea790ea 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1139,8 +1139,10 @@ target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb)  		return ret;  	ret = target_check_reservation(cmd); -	if (ret) +	if (ret) { +		cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT;  		return ret; +	}  	ret = dev->transport->parse_cdb(cmd);  	if (ret) diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index 7b0bfa0e7a9..3078c403b42 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -143,22 +143,18 @@ static int dove_thermal_probe(struct platform_device *pdev)  	if (!priv)  		return -ENOMEM; -	priv->sensor = devm_request_and_ioremap(&pdev->dev, res); -	if (!priv->sensor) { -		dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); -		return -EADDRNOTAVAIL; -	} +	priv->sensor = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(priv->sensor)) +		return PTR_ERR(priv->sensor);  	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);  	if (!res) {  		dev_err(&pdev->dev, "Failed to get platform resource\n");  		return -ENODEV;  	} -	priv->control = devm_request_and_ioremap(&pdev->dev, res); -	if (!priv->control) { -		dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); -		return -EADDRNOTAVAIL; -	} +	priv->control = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(priv->control)) +		return PTR_ERR(priv->control);  	ret = dove_init_sensor(priv);  	if (ret) { diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index e04ebd8671a..46568c078de 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -476,7 +476,7 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)  	if (IS_ERR(th_zone->therm_dev)) {  		pr_err("Failed to register thermal zone device\n"); -		ret = -EINVAL; +		ret = PTR_ERR(th_zone->therm_dev);  		goto err_unregister;  	}  	th_zone->mode = THERMAL_DEVICE_ENABLED; diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index 65cb4f09e8f..e5500edb528 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -85,11 +85,9 @@ static int kirkwood_thermal_probe(struct platform_device *pdev)  	if (!priv)  		return -ENOMEM; -	priv->sensor = devm_request_and_ioremap(&pdev->dev, res); -	if (!priv->sensor) { -		dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); -		return -EADDRNOTAVAIL; -	} +	priv->sensor = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(priv->sensor)) +		return PTR_ERR(priv->sensor);  	thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0,  					       priv, &ops, NULL, 0, 0); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 28f09199401..2cc5b6115e3 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -145,6 +145,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)  	struct device *dev = rcar_priv_to_dev(priv);  	int i;  	int ctemp, old, new; +	int ret = -EINVAL;  	mutex_lock(&priv->lock); @@ -174,7 +175,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)  	if (!ctemp) {  		dev_err(dev, "thermal sensor was broken\n"); -		return -EINVAL; +		goto err_out_unlock;  	}  	/* @@ -192,10 +193,10 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)  	dev_dbg(dev, "thermal%d  %d -> %d\n", priv->id, priv->ctemp, ctemp);  	priv->ctemp = ctemp; - +	ret = 0; +err_out_unlock:  	mutex_unlock(&priv->lock); - -	return 0; +	return ret;  }  static int rcar_thermal_get_temp(struct thermal_zone_device *zone, @@ -363,6 +364,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)  	struct resource *res, *irq;  	int mres = 0;  	int i; +	int ret = -ENODEV;  	int idle = IDLE_INTERVAL;  	common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); @@ -399,11 +401,9 @@ static int rcar_thermal_probe(struct platform_device *pdev)  		/*  		 * rcar_has_irq_support() will be enabled  		 */ -		common->base = devm_request_and_ioremap(dev, res); -		if (!common->base) { -			dev_err(dev, "Unable to ioremap thermal register\n"); -			return -ENOMEM; -		} +		common->base = devm_ioremap_resource(dev, res); +		if (IS_ERR(common->base)) +			return PTR_ERR(common->base);  		/* enable temperature comparation */  		rcar_thermal_common_write(common, ENR, 0x00030303); @@ -422,11 +422,9 @@ static int rcar_thermal_probe(struct platform_device *pdev)  			return -ENOMEM;  		} -		priv->base = devm_request_and_ioremap(dev, res); -		if (!priv->base) { -			dev_err(dev, "Unable to ioremap priv register\n"); -			return -ENOMEM; -		} +		priv->base = devm_ioremap_resource(dev, res); +		if (IS_ERR(priv->base)) +			return PTR_ERR(priv->base);  		priv->common = common;  		priv->id = i; @@ -441,6 +439,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)  						idle);  		if (IS_ERR(priv->zone)) {  			dev_err(dev, "can't register thermal zone\n"); +			ret = PTR_ERR(priv->zone);  			goto error_unregister;  		} @@ -460,7 +459,7 @@ error_unregister:  	rcar_thermal_for_each_priv(priv, common)  		thermal_zone_device_unregister(priv->zone); -	return -ENODEV; +	return ret;  }  static int rcar_thermal_remove(struct platform_device *pdev) diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250_core.c index 0efc815a496..35f9c96aada 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -301,7 +301,28 @@ static const struct serial8250_config uart_config[] = {  	},  	[PORT_8250_CIR] = {  		.name		= "CIR port" -	} +	}, +	[PORT_ALTR_16550_F32] = { +		.name		= "Altera 16550 FIFO32", +		.fifo_size	= 32, +		.tx_loadsz	= 32, +		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, +		.flags		= UART_CAP_FIFO | UART_CAP_AFE, +	}, +	[PORT_ALTR_16550_F64] = { +		.name		= "Altera 16550 FIFO64", +		.fifo_size	= 64, +		.tx_loadsz	= 64, +		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, +		.flags		= UART_CAP_FIFO | UART_CAP_AFE, +	}, +	[PORT_ALTR_16550_F128] = { +		.name		= "Altera 16550 FIFO128", +		.fifo_size	= 128, +		.tx_loadsz	= 128, +		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, +		.flags		= UART_CAP_FIFO | UART_CAP_AFE, +	},  };  /* Uart divisor latch read */ @@ -3396,3 +3417,34 @@ module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444);  MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");  #endif  MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); + +#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS +#ifndef MODULE +/* This module was renamed to 8250_core in 3.7.  Keep the old "8250" name + * working as well for the module options so we don't break people.  We + * need to keep the names identical and the convenient macros will happily + * refuse to let us do that by failing the build with redefinition errors + * of global variables.  So we stick them inside a dummy function to avoid + * those conflicts.  The options still get parsed, and the redefined + * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive. + * + * This is hacky.  I'm sorry. + */ +static void __used s8250_options(void) +{ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "8250_core." + +	module_param_cb(share_irqs, ¶m_ops_uint, &share_irqs, 0644); +	module_param_cb(nr_uarts, ¶m_ops_uint, &nr_uarts, 0644); +	module_param_cb(skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644); +#ifdef CONFIG_SERIAL_8250_RSA +	__module_param_call(MODULE_PARAM_PREFIX, probe_rsa, +		¶m_array_ops, .arr = &__param_arr_probe_rsa, +		0444, -1); +#endif +} +#else +MODULE_ALIAS("8250_core"); +#endif +#endif diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 791c5a77ec6..26e3a97ab15 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1554,6 +1554,7 @@ pci_wch_ch353_setup(struct serial_private *priv,  #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA	0xc001  #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d  #define PCI_VENDOR_ID_WCH		0x4348 +#define PCI_DEVICE_ID_WCH_CH352_2S	0x3253  #define PCI_DEVICE_ID_WCH_CH353_4S	0x3453  #define PCI_DEVICE_ID_WCH_CH353_2S1PF	0x5046  #define PCI_DEVICE_ID_WCH_CH353_2S1P	0x7053 @@ -1571,6 +1572,7 @@ pci_wch_ch353_setup(struct serial_private *priv,  /* Unknown vendors/cards - this should not be in linux/pci_ids.h */  #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584 +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588	0x1588  /*   * Master list of serial port init/setup/exit quirks. @@ -1852,15 +1854,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {  	},  	{  		.vendor		= PCI_VENDOR_ID_PLX, -		.device		= PCI_DEVICE_ID_PLX_9050, -		.subvendor	= PCI_VENDOR_ID_PLX, -		.subdevice	= PCI_SUBDEVICE_ID_UNKNOWN_0x1584, -		.init		= pci_plx9050_init, -		.setup		= pci_default_setup, -		.exit		= pci_plx9050_exit, -	}, -	{ -		.vendor		= PCI_VENDOR_ID_PLX,  		.device		= PCI_DEVICE_ID_PLX_ROMULUS,  		.subvendor	= PCI_VENDOR_ID_PLX,  		.subdevice	= PCI_DEVICE_ID_PLX_ROMULUS, @@ -2180,6 +2173,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {  		.subdevice      = PCI_ANY_ID,  		.setup          = pci_wch_ch353_setup,  	}, +	/* WCH CH352 2S card (16550 clone) */ +	{ +		.vendor		= PCI_VENDOR_ID_WCH, +		.device		= PCI_DEVICE_ID_WCH_CH352_2S, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.setup		= pci_wch_ch353_setup, +	},  	/*  	 * ASIX devices with FIFO bug  	 */ @@ -3733,7 +3734,12 @@ static struct pci_device_id serial_pci_tbl[] = {  	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,  		PCI_VENDOR_ID_PLX,  		PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, -		pbn_b0_4_115200 }, +		pbn_b2_4_115200 }, +	/* Unknown card - subdevice 0x1588 */ +	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, +		PCI_VENDOR_ID_PLX, +		PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0, +		pbn_b2_8_115200 },  	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,  		PCI_SUBVENDOR_ID_KEYSPAN,  		PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, @@ -4791,6 +4797,10 @@ static struct pci_device_id serial_pci_tbl[] = {  		PCI_VENDOR_ID_IBM, 0x0299,  		0, 0, pbn_b0_bt_2_115200 }, +	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, +		0x1000, 0x0012, +		0, 0, pbn_b0_bt_2_115200 }, +  	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,  		0xA000, 0x1000,  		0, 0, pbn_b0_1_115200 }, @@ -4869,6 +4879,10 @@ static struct pci_device_id serial_pci_tbl[] = {  		PCI_ANY_ID, PCI_ANY_ID,  		0, 0, pbn_b0_bt_2_115200 }, +	{	PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH352_2S, +		PCI_ANY_ID, PCI_ANY_ID, +		0, 0, pbn_b0_bt_2_115200 }, +  	/*  	 * Commtech, Inc. Fastcom adapters  	 */ diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 35d9ab95c5c..b3455a970a1 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -429,6 +429,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)  {  	struct uart_8250_port uart;  	int ret, line, flags = dev_id->driver_data; +	struct resource *res = NULL;  	if (flags & UNKNOWN_DEV) {  		ret = serial_pnp_guess_board(dev); @@ -439,11 +440,12 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)  	memset(&uart, 0, sizeof(uart));  	if (pnp_irq_valid(dev, 0))  		uart.port.irq = pnp_irq(dev, 0); -	if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) { -		uart.port.iobase = pnp_port_start(dev, 2); -		uart.port.iotype = UPIO_PORT; -	} else if (pnp_port_valid(dev, 0)) { -		uart.port.iobase = pnp_port_start(dev, 0); +	if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) +		res = pnp_get_resource(dev, IORESOURCE_IO, 2); +	else if (pnp_port_valid(dev, 0)) +		res = pnp_get_resource(dev, IORESOURCE_IO, 0); +	if (pnp_resource_enabled(res)) { +		uart.port.iobase = res->start;  		uart.port.iotype = UPIO_PORT;  	} else if (pnp_mem_valid(dev, 0)) {  		uart.port.mapbase = pnp_mem_start(dev, 0); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 2ef9537bcb2..80fe91e64a5 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -33,6 +33,23 @@ config SERIAL_8250  	  Most people will say Y or M here, so that they can use serial mice,  	  modems and similar devices connecting to the standard serial ports. +config SERIAL_8250_DEPRECATED_OPTIONS +	bool "Support 8250_core.* kernel options (DEPRECATED)" +	depends on SERIAL_8250 +	default y +	---help--- +	  In 3.7 we renamed 8250 to 8250_core by mistake, so now we have to +	  accept kernel parameters in both forms like 8250_core.nr_uarts=4 and +	  8250.nr_uarts=4. We now renamed the module back to 8250, but if +	  anybody noticed in 3.7 and changed their userspace we still have to +	  keep the 8350_core.* options around until they revert the changes +	  they already did. + +	  If 8250 is built as a module, this adds 8250_core alias instead.  + +	  If you did not notice yet and/or you have userspace from pre-3.7, it +	  is safe (and recommended) to say N here. +  config SERIAL_8250_PNP  	bool "8250/16550 PNP device support" if EXPERT  	depends on SERIAL_8250 && PNP diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index a23838a4d53..36d68d05430 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -2,10 +2,10 @@  # Makefile for the 8250 serial device drivers.  # -obj-$(CONFIG_SERIAL_8250)		+= 8250_core.o -8250_core-y				:= 8250.o -8250_core-$(CONFIG_SERIAL_8250_PNP)	+= 8250_pnp.o -8250_core-$(CONFIG_SERIAL_8250_DMA)	+= 8250_dma.o +obj-$(CONFIG_SERIAL_8250)		+= 8250.o +8250-y					:= 8250_core.o +8250-$(CONFIG_SERIAL_8250_PNP)		+= 8250_pnp.o +8250-$(CONFIG_SERIAL_8250_DMA)		+= 8250_dma.o  obj-$(CONFIG_SERIAL_8250_GSC)		+= 8250_gsc.o  obj-$(CONFIG_SERIAL_8250_PCI)		+= 8250_pci.o  obj-$(CONFIG_SERIAL_8250_HP300)		+= 8250_hp300.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index cf9210db9fa..7e7006fd404 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -211,14 +211,14 @@ config SERIAL_SAMSUNG  config SERIAL_SAMSUNG_UARTS_4  	bool  	depends on PLAT_SAMSUNG -	default y if !(CPU_S3C2410 || SERIAL_S3C2412 || CPU_S3C2440 || CPU_S3C2442) +	default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442)  	help  	  Internal node for the common case of 4 Samsung compatible UARTs  config SERIAL_SAMSUNG_UARTS  	int  	depends on PLAT_SAMSUNG -	default 6 if ARCH_S5P6450 +	default 6 if CPU_S5P6450  	default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416  	default 3  	help diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index d4a7c241b75..3467462869c 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -158,7 +158,7 @@ struct atmel_uart_port {  };  static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; -static unsigned long atmel_ports_in_use; +static DECLARE_BITMAP(atmel_ports_in_use, ATMEL_MAX_UART);  #ifdef SUPPORT_SYSRQ  static struct console atmel_console; @@ -1769,15 +1769,14 @@ static int atmel_serial_probe(struct platform_device *pdev)  	if (ret < 0)  		/* port id not found in platform data nor device-tree aliases:  		 * auto-enumerate it */ -		ret = find_first_zero_bit(&atmel_ports_in_use, -				sizeof(atmel_ports_in_use)); +		ret = find_first_zero_bit(atmel_ports_in_use, ATMEL_MAX_UART); -	if (ret > ATMEL_MAX_UART) { +	if (ret >= ATMEL_MAX_UART) {  		ret = -ENODEV;  		goto err;  	} -	if (test_and_set_bit(ret, &atmel_ports_in_use)) { +	if (test_and_set_bit(ret, atmel_ports_in_use)) {  		/* port already in use */  		ret = -EBUSY;  		goto err; @@ -1857,7 +1856,7 @@ static int atmel_serial_remove(struct platform_device *pdev)  	/* "port" is allocated statically, so we shouldn't free it */ -	clear_bit(port->line, &atmel_ports_in_use); +	clear_bit(port->line, atmel_ports_in_use);  	clk_put(atmel_port->clk); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 719594e5fc2..52a3ecd4042 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -235,7 +235,7 @@ static const char *bcm_uart_type(struct uart_port *port)   */  static void bcm_uart_do_rx(struct uart_port *port)  { -	struct tty_port *port = &port->state->port; +	struct tty_port *tty_port = &port->state->port;  	unsigned int max_count;  	/* limit number of char read in interrupt, should not be @@ -260,7 +260,7 @@ static void bcm_uart_do_rx(struct uart_port *port)  			bcm_uart_writel(port, val, UART_CTL_REG);  			port->icount.overrun++; -			tty_insert_flip_char(port, 0, TTY_OVERRUN); +			tty_insert_flip_char(tty_port, 0, TTY_OVERRUN);  		}  		if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) @@ -299,11 +299,11 @@ static void bcm_uart_do_rx(struct uart_port *port)  		if ((cstat & port->ignore_status_mask) == 0) -			tty_insert_flip_char(port, c, flag); +			tty_insert_flip_char(tty_port, c, flag);  	} while (--max_count); -	tty_flip_buffer_push(port); +	tty_flip_buffer_push(tty_port);  }  /* diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index c0e1fad51be..018bad92255 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -550,7 +550,7 @@ static int mpc512x_psc_clock(struct uart_port *port, int enable)  		return 0;  	psc_num = (port->mapbase & 0xf00) >> 8; -	snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num); +	snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num);  	psc_clk = clk_get(port->dev, clk_name);  	if (IS_ERR(psc_clk)) {  		dev_err(port->dev, "Failed to get PSC clock entry!\n"); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index d5874605682..b025d543827 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -241,6 +241,12 @@ static struct of_device_id of_platform_serial_table[] = {  	{ .compatible = "ns16850",  .data = (void *)PORT_16850, },  	{ .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, },  	{ .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, +	{ .compatible = "altr,16550-FIFO32", +		.data = (void *)PORT_ALTR_16550_F32, }, +	{ .compatible = "altr,16550-FIFO64", +		.data = (void *)PORT_ALTR_16550_F64, }, +	{ .compatible = "altr,16550-FIFO128", +		.data = (void *)PORT_ALTR_16550_F128, },  #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL  	{ .compatible = "ibm,qpace-nwp-serial",  		.data = (void *)PORT_NWPSERIAL, }, diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index e343d667085..451687cb968 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -968,6 +968,7 @@ static struct uart_ops sunsu_pops = {  #define UART_NR	4  static struct uart_sunsu_port sunsu_ports[UART_NR]; +static int nr_inst; /* Number of already registered ports */  #ifdef CONFIG_SERIO @@ -1337,13 +1338,8 @@ static int __init sunsu_console_setup(struct console *co, char *options)  	printk("Console: ttyS%d (SU)\n",  	       (sunsu_reg.minor - 64) + co->index); -	/* -	 * Check whether an invalid uart number has been specified, and -	 * if so, search for the first available port that does have -	 * console support. -	 */ -	if (co->index >= UART_NR) -		co->index = 0; +	if (co->index > nr_inst) +		return -ENODEV;  	port = &sunsu_ports[co->index].port;  	/* @@ -1408,7 +1404,6 @@ static enum su_type su_get_type(struct device_node *dp)  static int su_probe(struct platform_device *op)  { -	static int inst;  	struct device_node *dp = op->dev.of_node;  	struct uart_sunsu_port *up;  	struct resource *rp; @@ -1418,16 +1413,16 @@ static int su_probe(struct platform_device *op)  	type = su_get_type(dp);  	if (type == SU_PORT_PORT) { -		if (inst >= UART_NR) +		if (nr_inst >= UART_NR)  			return -EINVAL; -		up = &sunsu_ports[inst]; +		up = &sunsu_ports[nr_inst];  	} else {  		up = kzalloc(sizeof(*up), GFP_KERNEL);  		if (!up)  			return -ENOMEM;  	} -	up->port.line = inst; +	up->port.line = nr_inst;  	spin_lock_init(&up->port.lock); @@ -1461,6 +1456,8 @@ static int su_probe(struct platform_device *op)  		}  		dev_set_drvdata(&op->dev, up); +		nr_inst++; +  		return 0;  	} @@ -1488,7 +1485,7 @@ static int su_probe(struct platform_device *op)  	dev_set_drvdata(&op->dev, up); -	inst++; +	nr_inst++;  	return 0; diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index a3f9dd5c9df..705240e6c4e 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -611,14 +611,7 @@ static int vt8500_serial_probe(struct platform_device *pdev)  	vt8500_port->uart.dev = &pdev->dev;  	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; -	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); -	if (!IS_ERR(vt8500_port->clk)) { -		vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); -	} else { -		/* use the default of 24Mhz if not specified and warn */ -		pr_warn("%s: serial clock source not specified\n", __func__); -		vt8500_port->uart.uartclk = 24000000; -	} +	vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);  	snprintf(vt8500_port->name, sizeof(vt8500_port->name),  		 "VT8500 UART%d", pdev->id); diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index ba451c7209f..f36bbba1ac8 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -578,6 +578,8 @@ static int xuartps_startup(struct uart_port *port)  	/* Receive Timeout register is enabled with value of 10 */  	xuartps_writel(10, XUARTPS_RXTOUT_OFFSET); +	/* Clear out any pending interrupts before enabling them */ +	xuartps_writel(xuartps_readl(XUARTPS_ISR_OFFSET), XUARTPS_ISR_OFFSET);  	/* Set the Interrupt Registers with desired interrupts */  	xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index bb119934e76..578aa7594b1 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -425,7 +425,7 @@ static void flush_to_ldisc(struct work_struct *work)  	struct tty_ldisc *disc;  	tty = port->itty; -	if (WARN_RATELIMIT(tty == NULL, "tty is NULL\n")) +	if (tty == NULL)  		return;  	disc = tty_ldisc_ref(tty); diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index e4ca345873c..d7799deacb2 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -93,7 +93,7 @@ vcs_poll_data_free(struct vcs_poll_data *poll)  static struct vcs_poll_data *  vcs_poll_data_get(struct file *file)  { -	struct vcs_poll_data *poll = file->private_data; +	struct vcs_poll_data *poll = file->private_data, *kill = NULL;  	if (poll)  		return poll; @@ -122,10 +122,12 @@ vcs_poll_data_get(struct file *file)  		file->private_data = poll;  	} else {  		/* someone else raced ahead of us */ -		vcs_poll_data_free(poll); +		kill = poll;  		poll = file->private_data;  	}  	spin_unlock(&file->f_lock); +	if (kill) +		vcs_poll_data_free(kill);  	return poll;  } diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index f5ed3d75fa5..8f5ebced5df 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_USB_MICROTEK)	+= image/  obj-$(CONFIG_USB_SERIAL)	+= serial/  obj-$(CONFIG_USB)		+= misc/ -obj-$(CONFIG_USB_COMMON)	+= phy/ +obj-$(CONFIG_USB_OTG_UTILS)	+= phy/  obj-$(CONFIG_EARLY_PRINTK_DBGP)	+= early/  obj-$(CONFIG_USB_ATM)		+= atm/ diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c index a03fbc15fa9..aa491627a45 100644 --- a/drivers/usb/c67x00/c67x00-sched.c +++ b/drivers/usb/c67x00/c67x00-sched.c @@ -100,7 +100,7 @@ struct c67x00_urb_priv {  #define TD_PIDEP_OFFSET		0x04  #define TD_PIDEPMASK_PID	0xF0  #define TD_PIDEPMASK_EP		0x0F -#define TD_PORTLENMASK_DL	0x02FF +#define TD_PORTLENMASK_DL	0x03FF  #define TD_PORTLENMASK_PN	0xC000  #define TD_STATUS_OFFSET	0x07 @@ -590,7 +590,7 @@ static int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb,  {  	struct c67x00_td *td;  	struct c67x00_urb_priv *urbp = urb->hcpriv; -	const __u8 active_flag = 1, retry_cnt = 1; +	const __u8 active_flag = 1, retry_cnt = 3;  	__u8 cmd = 0;  	int tt = 0; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 2f45bba8561..f64fbea1cf2 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1767,7 +1767,7 @@ static int udc_start(struct ci13xxx *ci)  		goto put_transceiver;  	} -	retval = dbg_create_files(&ci->gadget.dev); +	retval = dbg_create_files(ci->dev);  	if (retval)  		goto unreg_device; @@ -1796,7 +1796,7 @@ remove_trans:  	dev_err(dev, "error = %i\n", retval);  remove_dbg: -	dbg_remove_files(&ci->gadget.dev); +	dbg_remove_files(ci->dev);  unreg_device:  	device_unregister(&ci->gadget.dev);  put_transceiver: @@ -1836,7 +1836,7 @@ static void udc_stop(struct ci13xxx *ci)  		if (ci->global_phy)  			usb_put_phy(ci->transceiver);  	} -	dbg_remove_files(&ci->gadget.dev); +	dbg_remove_files(ci->dev);  	device_unregister(&ci->gadget.dev);  	/* my kobject is dynamic, I swear! */  	memset(&ci->gadget, 0, sizeof(ci->gadget)); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8ac25adf31b..387dc6c8ad2 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -593,7 +593,6 @@ static void acm_port_destruct(struct tty_port *port)  	dev_dbg(&acm->control->dev, "%s\n", __func__); -	tty_unregister_device(acm_tty_driver, acm->minor);  	acm_release_minor(acm);  	usb_put_intf(acm->control);  	kfree(acm->country_codes); @@ -977,6 +976,8 @@ static int acm_probe(struct usb_interface *intf,  	int num_rx_buf;  	int i;  	int combined_interfaces = 0; +	struct device *tty_dev; +	int rv = -ENOMEM;  	/* normal quirks */  	quirks = (unsigned long)id->driver_info; @@ -1339,11 +1340,24 @@ skip_countries:  	usb_set_intfdata(data_interface, acm);  	usb_get_intf(control_interface); -	tty_port_register_device(&acm->port, acm_tty_driver, minor, +	tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor,  			&control_interface->dev); +	if (IS_ERR(tty_dev)) { +		rv = PTR_ERR(tty_dev); +		goto alloc_fail8; +	}  	return 0; +alloc_fail8: +	if (acm->country_codes) { +		device_remove_file(&acm->control->dev, +				&dev_attr_wCountryCodes); +		device_remove_file(&acm->control->dev, +				&dev_attr_iCountryCodeRelDate); +	} +	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);  alloc_fail7: +	usb_set_intfdata(intf, NULL);  	for (i = 0; i < ACM_NW; i++)  		usb_free_urb(acm->wb[i].urb);  alloc_fail6: @@ -1359,7 +1373,7 @@ alloc_fail2:  	acm_release_minor(acm);  	kfree(acm);  alloc_fail: -	return -ENOMEM; +	return rv;  }  static void stop_data_traffic(struct acm *acm) @@ -1411,6 +1425,8 @@ static void acm_disconnect(struct usb_interface *intf)  	stop_data_traffic(acm); +	tty_unregister_device(acm_tty_driver, acm->minor); +  	usb_free_urb(acm->ctrlurb);  	for (i = 0; i < ACM_NW; i++)  		usb_free_urb(acm->wb[i].urb); diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 5f0cb417b73..122d056d96d 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -56,6 +56,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);  #define WDM_RESPONDING		7  #define WDM_SUSPENDING		8  #define WDM_RESETTING		9 +#define WDM_OVERFLOW		10  #define WDM_MAX			16 @@ -155,6 +156,7 @@ static void wdm_in_callback(struct urb *urb)  {  	struct wdm_device *desc = urb->context;  	int status = urb->status; +	int length = urb->actual_length;  	spin_lock(&desc->iuspin);  	clear_bit(WDM_RESPONDING, &desc->flags); @@ -185,9 +187,17 @@ static void wdm_in_callback(struct urb *urb)  	}  	desc->rerr = status; -	desc->reslength = urb->actual_length; -	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); -	desc->length += desc->reslength; +	if (length + desc->length > desc->wMaxCommand) { +		/* The buffer would overflow */ +		set_bit(WDM_OVERFLOW, &desc->flags); +	} else { +		/* we may already be in overflow */ +		if (!test_bit(WDM_OVERFLOW, &desc->flags)) { +			memmove(desc->ubuf + desc->length, desc->inbuf, length); +			desc->length += length; +			desc->reslength = length; +		} +	}  skip_error:  	wake_up(&desc->wait); @@ -435,6 +445,11 @@ retry:  			rv = -ENODEV;  			goto err;  		} +		if (test_bit(WDM_OVERFLOW, &desc->flags)) { +			clear_bit(WDM_OVERFLOW, &desc->flags); +			rv = -ENOBUFS; +			goto err; +		}  		i++;  		if (file->f_flags & O_NONBLOCK) {  			if (!test_bit(WDM_READ, &desc->flags)) { @@ -478,6 +493,7 @@ retry:  			spin_unlock_irq(&desc->iuspin);  			goto retry;  		} +  		if (!desc->reslength) { /* zero length read */  			dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);  			clear_bit(WDM_READ, &desc->flags); @@ -1004,6 +1020,7 @@ static int wdm_post_reset(struct usb_interface *intf)  	struct wdm_device *desc = wdm_find_device(intf);  	int rv; +	clear_bit(WDM_OVERFLOW, &desc->flags);  	clear_bit(WDM_RESETTING, &desc->flags);  	rv = recover_from_urb_loss(desc);  	mutex_unlock(&desc->wlock); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 622b4a48e73..2b487d4797b 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -173,6 +173,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)  	struct hc_driver	*driver;  	struct usb_hcd		*hcd;  	int			retval; +	int			hcd_irq = 0;  	if (usb_disabled())  		return -ENODEV; @@ -187,15 +188,19 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)  		return -ENODEV;  	dev->current_state = PCI_D0; -	/* The xHCI driver supports MSI and MSI-X, -	 * so don't fail if the BIOS doesn't provide a legacy IRQ. +	/* +	 * The xHCI driver has its own irq management +	 * make sure irq setup is not touched for xhci in generic hcd code  	 */ -	if (!dev->irq && (driver->flags & HCD_MASK) != HCD_USB3) { -		dev_err(&dev->dev, -			"Found HC with no IRQ.  Check BIOS/PCI %s setup!\n", -			pci_name(dev)); -		retval = -ENODEV; -		goto disable_pci; +	if ((driver->flags & HCD_MASK) != HCD_USB3) { +		if (!dev->irq) { +			dev_err(&dev->dev, +			"Found HC with no IRQ. Check BIOS/PCI %s setup!\n", +				pci_name(dev)); +			retval = -ENODEV; +			goto disable_pci; +		} +		hcd_irq = dev->irq;  	}  	hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); @@ -245,7 +250,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)  	pci_set_master(dev); -	retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); +	retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);  	if (retval != 0)  		goto unmap_registers;  	set_hs_companion(dev, hcd); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 99b34a30354..f9ec44cbb82 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2412,6 +2412,14 @@ int usb_hcd_is_primary_hcd(struct usb_hcd *hcd)  }  EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd); +int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1) +{ +	if (!hcd->driver->find_raw_port_number) +		return port1; + +	return hcd->driver->find_raw_port_number(hcd, port1); +} +  static int usb_hcd_request_irqs(struct usb_hcd *hcd,  		unsigned int irqnum, unsigned long irqflags)  { diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index b6f4bad3f75..255c14464bf 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -15,6 +15,7 @@  #include <linux/kernel.h>  #include <linux/acpi.h>  #include <linux/pci.h> +#include <linux/usb/hcd.h>  #include <acpi/acpi_bus.h>  #include "usb.h" @@ -188,8 +189,13 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)  		 * connected to.  		 */  		if (!udev->parent) { -			*handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev), +			struct usb_hcd *hcd = bus_to_hcd(udev->bus); +			int raw_port_num; + +			raw_port_num = usb_hcd_find_raw_port_number(hcd,  				port_num); +			*handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev), +				raw_port_num);  			if (!*handle)  				return -ENODEV;  		} else { diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 999909451e3..ffa6b004a84 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -583,6 +583,7 @@ static int dwc3_remove(struct platform_device *pdev)  		break;  	} +	dwc3_free_event_buffers(dwc);  	dwc3_core_exit(dwc);  	return 0; diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index b50da53e9a5..b082bec7343 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -23,8 +23,6 @@  #include <linux/usb/nop-usb-xceiv.h>  #include <linux/of.h> -#include "core.h" -  struct dwc3_exynos {  	struct platform_device	*dwc3;  	struct platform_device	*usb2_phy; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 22f337f5721..afa05e3c9cf 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -54,8 +54,6 @@  #include <linux/usb/otg.h>  #include <linux/usb/nop-usb-xceiv.h> -#include "core.h" -  /*   * All these registers belong to OMAP's Wrapper around the   * DesignWare USB3 Core. @@ -465,20 +463,20 @@ static int dwc3_omap_remove(struct platform_device *pdev)  	return 0;  } -static const struct of_device_id of_dwc3_matach[] = { +static const struct of_device_id of_dwc3_match[] = {  	{  		"ti,dwc3",  	},  	{ },  }; -MODULE_DEVICE_TABLE(of, of_dwc3_matach); +MODULE_DEVICE_TABLE(of, of_dwc3_match);  static struct platform_driver dwc3_omap_driver = {  	.probe		= dwc3_omap_probe,  	.remove		= dwc3_omap_remove,  	.driver		= {  		.name	= "omap-dwc3", -		.of_match_table	= of_dwc3_matach, +		.of_match_table	= of_dwc3_match,  	},  }; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 7d70f44567d..e8d77689a32 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -45,8 +45,6 @@  #include <linux/usb/otg.h>  #include <linux/usb/nop-usb-xceiv.h> -#include "core.h" -  /* FIXME define these in <linux/pci_ids.h> */  #define PCI_VENDOR_ID_SYNOPSYS		0x16c3  #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3	0xabcd diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index d7da073a23f..1d139ca05ef 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -891,7 +891,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,  				DWC3_TRBCTL_CONTROL_DATA);  	} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)  			&& (dep->number == 0)) { -		u32		transfer_size; +		u32	transfer_size; +		u32	maxpacket;  		ret = usb_gadget_map_request(&dwc->gadget, &req->request,  				dep->number); @@ -902,8 +903,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,  		WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); -		transfer_size = roundup(req->request.length, -				(u32) dep->endpoint.maxpacket); +		maxpacket = dep->endpoint.maxpacket; +		transfer_size = roundup(req->request.length, maxpacket);  		dwc->ep0_bounced = true; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a04342f6cbf..82e160e96fc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2159,7 +2159,6 @@ static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)  static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)  { -	struct dwc3_gadget_ep_cmd_params params;  	struct dwc3_ep		*dep;  	int			ret;  	u32			reg; @@ -2167,8 +2166,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)  	dev_vdbg(dwc->dev, "%s\n", __func__); -	memset(¶ms, 0x00, sizeof(params)); -  	reg = dwc3_readl(dwc->regs, DWC3_DSTS);  	speed = reg & DWC3_DSTS_CONNECTSPD;  	dwc->speed = speed; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5a0c541daf8..c7525b1cad7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -145,6 +145,7 @@ config USB_LPC32XX  	tristate "LPC32XX USB Peripheral Controller"  	depends on ARCH_LPC32XX  	select USB_ISP1301 +	select USB_OTG_UTILS  	help  	   This option selects the USB device controller in the LPC32xx SoC. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 97a13c349cc..82fb2251135 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,6 +35,12 @@ mv_udc-y			:= mv_udc_core.o  obj-$(CONFIG_USB_FUSB300)	+= fusb300_udc.o  obj-$(CONFIG_USB_MV_U3D)	+= mv_u3d_core.o +# USB Functions +obj-$(CONFIG_USB_F_ACM)		+= f_acm.o +f_ss_lb-y			:= f_loopback.o f_sourcesink.o +obj-$(CONFIG_USB_F_SS_LB)	+= f_ss_lb.o +obj-$(CONFIG_USB_U_SERIAL)	+= u_serial.o +  #  # USB gadget drivers  # @@ -74,9 +80,3 @@ obj-$(CONFIG_USB_G_WEBCAM)	+= g_webcam.o  obj-$(CONFIG_USB_G_NCM)		+= g_ncm.o  obj-$(CONFIG_USB_G_ACM_MS)	+= g_acm_ms.o  obj-$(CONFIG_USB_GADGET_TARGET)	+= tcm_usb_gadget.o - -# USB Functions -obj-$(CONFIG_USB_F_ACM)		+= f_acm.o -f_ss_lb-y			:= f_loopback.o f_sourcesink.o -obj-$(CONFIG_USB_F_SS_LB)	+= f_ss_lb.o -obj-$(CONFIG_USB_U_SERIAL)	+= u_serial.o diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 7c821de8ce3..c0d62b27861 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1757,10 +1757,7 @@ static const struct usb_gadget_driver composite_driver_template = {  /**   * usb_composite_probe() - register a composite driver   * @driver: the driver to register - * @bind: the callback used to allocate resources that are shared across the - *	whole device, such as string IDs, and add its configurations using - *	@usb_add_config().  This may fail by returning a negative errno - *	value; it should return zero on successful initialization. + *   * Context: single threaded during gadget setup   *   * This function is used to register drivers using the composite driver diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 71beeb83355..cc9c49c57c8 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -447,14 +447,13 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)  static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)  {  	struct f_rndis			*rndis = req->context; -	struct usb_composite_dev	*cdev = rndis->port.func.config->cdev;  	int				status;  	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */  //	spin_lock(&dev->lock);  	status = rndis_msg_parser(rndis->config, (u8 *) req->buf);  	if (status < 0) -		ERROR(cdev, "RNDIS command error %d, %d/%d\n", +		pr_err("RNDIS command error %d, %d/%d\n",  			status, req->actual, req->length);  //	spin_unlock(&dev->lock);  } diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c index f570e667a64..fa8ea4ea00c 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/f_uac1.c @@ -418,6 +418,7 @@ static int audio_get_intf_req(struct usb_function *f,  	req->context = audio;  	req->complete = f_audio_complete; +	len = min_t(size_t, sizeof(value), len);  	memcpy(req->buf, &value, len);  	return len; diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 3953dd4d718..3b343b23e4b 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -357,7 +357,7 @@ static int gfs_bind(struct usb_composite_dev *cdev)  		goto error;  	gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; -	for (i = func_num; --i; ) { +	for (i = func_num; i--; ) {  		ret = functionfs_bind(ffs_tab[i].ffs_data, cdev);  		if (unlikely(ret < 0)) {  			while (++i < func_num) @@ -413,7 +413,7 @@ static int gfs_unbind(struct usb_composite_dev *cdev)  		gether_cleanup();  	gfs_ether_setup = false; -	for (i = func_num; --i; ) +	for (i = func_num; i--; )  		if (ffs_tab[i].ffs_data)  			functionfs_unbind(ffs_tab[i].ffs_data); diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 8efd7555fa2..5bd930d779b 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1334,27 +1334,18 @@ static int imx_udc_start(struct usb_gadget *gadget,  		struct usb_gadget_driver *driver)  {  	struct imx_udc_struct *imx_usb; -	int retval;  	imx_usb = container_of(gadget, struct imx_udc_struct, gadget);  	/* first hook up the driver ... */  	imx_usb->driver = driver;  	imx_usb->gadget.dev.driver = &driver->driver; -	retval = device_add(&imx_usb->gadget.dev); -	if (retval) -		goto fail; -  	D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n",  		__func__, driver->driver.name);  	imx_udc_enable(imx_usb);  	return 0; -fail: -	imx_usb->driver = NULL; -	imx_usb->gadget.dev.driver = NULL; -	return retval;  }  static int imx_udc_stop(struct usb_gadget *gadget, @@ -1370,8 +1361,6 @@ static int imx_udc_stop(struct usb_gadget *gadget,  	imx_usb->gadget.dev.driver = NULL;  	imx_usb->driver = NULL; -	device_del(&imx_usb->gadget.dev); -  	D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n",  		__func__, driver->driver.name); @@ -1477,6 +1466,10 @@ static int __init imx_udc_probe(struct platform_device *pdev)  	imx_usb->gadget.dev.parent = &pdev->dev;  	imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; +	ret = device_add(&imx_usb->gadget.dev); +	if (retval) +		goto fail4; +  	platform_set_drvdata(pdev, imx_usb);  	usb_init_data(imx_usb); @@ -1488,9 +1481,11 @@ static int __init imx_udc_probe(struct platform_device *pdev)  	ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget);  	if (ret) -		goto fail4; +		goto fail5;  	return 0; +fail5: +	device_unregister(&imx_usb->gadget.dev);  fail4:  	for (i = 0; i < IMX_USB_NB_EP + 1; i++)  		free_irq(imx_usb->usbd_int[i], imx_usb); @@ -1514,6 +1509,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev)  	int i;  	usb_del_gadget_udc(&imx_usb->gadget); +	device_unregister(&imx_usb->gadget.dev);  	imx_udc_disable(imx_usb);  	del_timer(&imx_usb->timer); diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index d226058e3b8..32524b63195 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -59,7 +59,7 @@ static const char * const ep_name[] = {  };  #define DMA_ADDR_INVALID	(~(dma_addr_t)0) -#ifdef CONFIG_USB_GADGET_NET2272_DMA +#ifdef CONFIG_USB_NET2272_DMA  /*   * use_dma: the NET2272 can use an external DMA controller.   * Note that since there is no generic DMA api, some functions, @@ -1495,6 +1495,13 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)  	for (i = 0; i < 4; ++i)  		net2272_dequeue_all(&dev->ep[i]); +	/* report disconnect; the driver is already quiesced */ +	if (driver) { +		spin_unlock(&dev->lock); +		driver->disconnect(&dev->gadget); +		spin_lock(&dev->lock); +	} +  	net2272_usb_reinit(dev);  } diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index a1b650e1133..3bd0f992fb4 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1924,7 +1924,6 @@ static int net2280_start(struct usb_gadget *_gadget,  err_func:  	device_remove_file (&dev->pdev->dev, &dev_attr_function);  err_unbind: -	driver->unbind (&dev->gadget);  	dev->gadget.dev.driver = NULL;  	dev->driver = NULL;  	return retval; @@ -1946,6 +1945,13 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)  	for (i = 0; i < 7; i++)  		nuke (&dev->ep [i]); +	/* report disconnect; the driver is already quiesced */ +	if (driver) { +		spin_unlock(&dev->lock); +		driver->disconnect(&dev->gadget); +		spin_lock(&dev->lock); +	} +  	usb_reinit (dev);  } diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 06be85c2b23..f8445653577 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -62,6 +62,7 @@  #define	DRIVER_VERSION	"4 October 2004"  #define OMAP_DMA_USB_W2FC_TX0		29 +#define OMAP_DMA_USB_W2FC_RX0		26  /*   * The OMAP UDC needs _very_ early endpoint setup:  before enabling the @@ -1310,7 +1311,7 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)  }  static int omap_udc_start(struct usb_gadget *g, -		struct usb_gadget_driver *driver) +		struct usb_gadget_driver *driver);  static int omap_udc_stop(struct usb_gadget *g,  		struct usb_gadget_driver *driver); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 2bbcdce942d..d0f37484b6b 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1266,13 +1266,6 @@ static int pxa25x_udc_start(struct usb_gadget *g,  	dev->gadget.dev.driver = &driver->driver;  	dev->pullup = 1; -	retval = device_add (&dev->gadget.dev); -	if (retval) { -		dev->driver = NULL; -		dev->gadget.dev.driver = NULL; -		return retval; -	} -  	/* ... then enable host detection and ep0; and we're ready  	 * for set_configuration as well as eventual disconnect.  	 */ @@ -1310,6 +1303,10 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)  	}  	del_timer_sync(&dev->timer); +	/* report disconnect; the driver is already quiesced */ +	if (driver) +		driver->disconnect(&dev->gadget); +  	/* re-init driver-visible data structures */  	udc_reinit(dev);  } @@ -1331,7 +1328,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g,  	dev->gadget.dev.driver = NULL;  	dev->driver = NULL; -	device_del (&dev->gadget.dev);  	dump_state(dev);  	return 0; @@ -2146,6 +2142,13 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)  	dev->gadget.dev.parent = &pdev->dev;  	dev->gadget.dev.dma_mask = pdev->dev.dma_mask; +	retval = device_add(&dev->gadget.dev); +	if (retval) { +		dev->driver = NULL; +		dev->gadget.dev.driver = NULL; +		goto err_device_add; +	} +  	the_controller = dev;  	platform_set_drvdata(pdev, dev); @@ -2196,6 +2199,8 @@ lubbock_fail0:  	free_irq(irq, dev);  #endif   err_irq1: +	device_unregister(&dev->gadget.dev); + err_device_add:  	if (gpio_is_valid(dev->mach->gpio_pullup))  		gpio_free(dev->mach->gpio_pullup);   err_gpio_pullup: @@ -2217,10 +2222,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)  {  	struct pxa25x_udc *dev = platform_get_drvdata(pdev); -	usb_del_gadget_udc(&dev->gadget);  	if (dev->driver)  		return -EBUSY; +	usb_del_gadget_udc(&dev->gadget); +	device_unregister(&dev->gadget.dev);  	dev->pullup = 0;  	pullup(dev); diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index f7d25795821..2fc867652ef 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1814,11 +1814,6 @@ static int pxa27x_udc_start(struct usb_gadget *g,  	udc->gadget.dev.driver = &driver->driver;  	dplus_pullup(udc, 1); -	retval = device_add(&udc->gadget.dev); -	if (retval) { -		dev_err(udc->dev, "device_add error %d\n", retval); -		goto fail; -	}  	if (!IS_ERR_OR_NULL(udc->transceiver)) {  		retval = otg_set_peripheral(udc->transceiver->otg,  						&udc->gadget); @@ -1876,7 +1871,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g,  	udc->driver = NULL; -	device_del(&udc->gadget.dev);  	if (!IS_ERR_OR_NULL(udc->transceiver))  		return otg_set_peripheral(udc->transceiver->otg, NULL); @@ -2480,13 +2474,24 @@ static int __init pxa_udc_probe(struct platform_device *pdev)  			driver_name, udc->irq, retval);  		goto err_irq;  	} + +	retval = device_add(&udc->gadget.dev); +	if (retval) { +		dev_err(udc->dev, "device_add error %d\n", retval); +		goto err_dev_add; +	} +  	retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);  	if (retval)  		goto err_add_udc;  	pxa_init_debugfs(udc); +  	return 0; +  err_add_udc: +	device_unregister(&udc->gadget.dev); +err_dev_add:  	free_irq(udc->irq, udc);  err_irq:  	iounmap(udc->regs); @@ -2507,6 +2512,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev)  	int gpio = udc->mach->gpio_pullup;  	usb_del_gadget_udc(&udc->gadget); +	device_del(&udc->gadget.dev);  	usb_gadget_unregister_driver(udc->driver);  	free_irq(udc->irq, udc);  	pxa_cleanup_debugfs(udc); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index fc07b438128..08f89652533 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1668,8 +1668,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)  static int s3c2410_udc_start(struct usb_gadget *g,  		struct usb_gadget_driver *driver)  { -	struct s3c2410_udc *udc = to_s3c2410(g) -	int		retval; +	struct s3c2410_udc *udc = to_s3c2410(g);  	dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name); @@ -1677,22 +1676,10 @@ static int s3c2410_udc_start(struct usb_gadget *g,  	udc->driver = driver;  	udc->gadget.dev.driver = &driver->driver; -	/* Bind the driver */ -	retval = device_add(&udc->gadget.dev); -	if (retval) { -		dev_err(&udc->gadget.dev, "Error in device_add() : %d\n", retval); -		goto register_error; -	} -  	/* Enable udc */  	s3c2410_udc_enable(udc);  	return 0; - -register_error: -	udc->driver = NULL; -	udc->gadget.dev.driver = NULL; -	return retval;  }  static int s3c2410_udc_stop(struct usb_gadget *g, @@ -1700,7 +1687,6 @@ static int s3c2410_udc_stop(struct usb_gadget *g,  {  	struct s3c2410_udc *udc = to_s3c2410(g); -	device_del(&udc->gadget.dev);  	udc->driver = NULL;  	/* Disable udc */ @@ -1842,6 +1828,13 @@ static int s3c2410_udc_probe(struct platform_device *pdev)  	udc->gadget.dev.parent = &pdev->dev;  	udc->gadget.dev.dma_mask = pdev->dev.dma_mask; +	/* Bind the driver */ +	retval = device_add(&udc->gadget.dev); +	if (retval) { +		dev_err(&udc->gadget.dev, "Error in device_add() : %d\n", retval); +		goto err_device_add; +	} +  	the_controller = udc;  	platform_set_drvdata(pdev, udc); @@ -1930,6 +1923,8 @@ err_gpio_claim:  err_int:  	free_irq(IRQ_USBD, udc);  err_map: +	device_unregister(&udc->gadget.dev); +err_device_add:  	iounmap(base_addr);  err_mem:  	release_mem_region(rsrc_start, rsrc_len); @@ -1947,10 +1942,11 @@ static int s3c2410_udc_remove(struct platform_device *pdev)  	dev_dbg(&pdev->dev, "%s()\n", __func__); -	usb_del_gadget_udc(&udc->gadget);  	if (udc->driver)  		return -EBUSY; +	usb_del_gadget_udc(&udc->gadget); +	device_unregister(&udc->gadget.dev);  	debugfs_remove(udc->regs_info);  	if (udc_info && !udc_info->udc_command && diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index c5034d9c946..b369292d4b9 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -136,7 +136,7 @@ static struct portmaster {  	pr_debug(fmt, ##arg)  #endif /* pr_vdebug */  #else -#ifndef pr_vdebig +#ifndef pr_vdebug  #define pr_vdebug(fmt, arg...) \  	({ if (0) pr_debug(fmt, ##arg); })  #endif /* pr_vdebug */ diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c index e0c5e88e03e..c7d460f4339 100644 --- a/drivers/usb/gadget/u_uac1.c +++ b/drivers/usb/gadget/u_uac1.c @@ -240,8 +240,11 @@ static int gaudio_open_snd_dev(struct gaudio *card)  	snd = &card->playback;  	snd->filp = filp_open(fn_play, O_WRONLY, 0);  	if (IS_ERR(snd->filp)) { +		int ret = PTR_ERR(snd->filp); +  		ERROR(card, "No such PCM playback device: %s\n", fn_play);  		snd->filp = NULL; +		return ret;  	}  	pcm_file = snd->filp->private_data;  	snd->substream = pcm_file->substream; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 2a9cd369f71..f8f62c3ed65 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -216,7 +216,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)  	usb_gadget_disconnect(udc->gadget);  	udc->driver->disconnect(udc->gadget);  	udc->driver->unbind(udc->gadget); -	usb_gadget_udc_stop(udc->gadget, udc->driver); +	usb_gadget_udc_stop(udc->gadget, NULL);  	udc->driver = NULL;  	udc->dev.driver = NULL; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b416a3fc995..416a6dce5e1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -302,6 +302,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)  static void end_unlink_async(struct ehci_hcd *ehci);  static void unlink_empty_async(struct ehci_hcd *ehci); +static void unlink_empty_async_suspended(struct ehci_hcd *ehci);  static void ehci_work(struct ehci_hcd *ehci);  static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);  static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); @@ -748,11 +749,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)  		/* guard against (alleged) silicon errata */  		if (cmd & CMD_IAAD)  			ehci_dbg(ehci, "IAA with IAAD still set?\n"); -		if (ehci->async_iaa) { +		if (ehci->async_iaa)  			COUNT(ehci->stats.iaa); -			end_unlink_async(ehci); -		} else -			ehci_dbg(ehci, "IAA with nothing unlinked?\n"); +		end_unlink_async(ehci);  	}  	/* remote wakeup [4.3.1] */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4d3b294f203..7d06e77f6c4 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -328,7 +328,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  	ehci->rh_state = EHCI_RH_SUSPENDED;  	end_unlink_async(ehci); -	unlink_empty_async(ehci); +	unlink_empty_async_suspended(ehci);  	ehci_handle_intr_unlinks(ehci);  	end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index fd252f0cfb3..23d13690428 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -135,7 +135,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)  		 * qtd is updated in qh_completions(). Update the QH  		 * overlay here.  		 */ -		if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) { +		if (qh->hw->hw_token & ACTIVE_BIT(ehci)) {  			qh->hw->hw_qtd_next = qtd->hw_next;  			qtd = NULL;  		} @@ -449,11 +449,19 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)  			else if (last_status == -EINPROGRESS && !urb->unlinked)  				continue; -			/* qh unlinked; token in overlay may be most current */ -			if (state == QH_STATE_IDLE -					&& cpu_to_hc32(ehci, qtd->qtd_dma) -						== hw->hw_current) { +			/* +			 * If this was the active qtd when the qh was unlinked +			 * and the overlay's token is active, then the overlay +			 * hasn't been written back to the qtd yet so use its +			 * token instead of the qtd's.  After the qtd is +			 * processed and removed, the overlay won't be valid +			 * any more. +			 */ +			if (state == QH_STATE_IDLE && +					qh->qtd_list.next == &qtd->qtd_list && +					(hw->hw_token & ACTIVE_BIT(ehci))) {  				token = hc32_to_cpu(ehci, hw->hw_token); +				hw->hw_token &= ~ACTIVE_BIT(ehci);  				/* An unlink may leave an incomplete  				 * async transaction in the TT buffer. @@ -1170,7 +1178,7 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)  	struct ehci_qh		*prev;  	/* Add to the end of the list of QHs waiting for the next IAAD */ -	qh->qh_state = QH_STATE_UNLINK; +	qh->qh_state = QH_STATE_UNLINK_WAIT;  	if (ehci->async_unlink)  		ehci->async_unlink_last->unlink_next = qh;  	else @@ -1213,9 +1221,19 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)  		/* Do only the first waiting QH (nVidia bug?) */  		qh = ehci->async_unlink; -		ehci->async_iaa = qh; -		ehci->async_unlink = qh->unlink_next; -		qh->unlink_next = NULL; + +		/* +		 * Intel (?) bug: The HC can write back the overlay region +		 * even after the IAA interrupt occurs.  In self-defense, +		 * always go through two IAA cycles for each QH. +		 */ +		if (qh->qh_state == QH_STATE_UNLINK_WAIT) { +			qh->qh_state = QH_STATE_UNLINK; +		} else { +			ehci->async_iaa = qh; +			ehci->async_unlink = qh->unlink_next; +			qh->unlink_next = NULL; +		}  		/* Make sure the unlinks are all visible to the hardware */  		wmb(); @@ -1298,6 +1316,19 @@ static void unlink_empty_async(struct ehci_hcd *ehci)  	}  } +/* The root hub is suspended; unlink all the async QHs */ +static void unlink_empty_async_suspended(struct ehci_hcd *ehci) +{ +	struct ehci_qh		*qh; + +	while (ehci->async->qh_next.qh) { +		qh = ehci->async->qh_next.qh; +		WARN_ON(!list_empty(&qh->qtd_list)); +		single_unlink_async(ehci, qh); +	} +	start_iaa_cycle(ehci, false); +} +  /* makes sure the async qh will become idle */  /* caller must own ehci->lock */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index b476daf49f6..010f686d888 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1214,6 +1214,7 @@ itd_urb_transaction (  		memset (itd, 0, sizeof *itd);  		itd->itd_dma = itd_dma; +		itd->frame = 9999;		/* an invalid value */  		list_add (&itd->itd_list, &sched->td_list);  	}  	spin_unlock_irqrestore (&ehci->lock, flags); @@ -1915,6 +1916,7 @@ sitd_urb_transaction (  		memset (sitd, 0, sizeof *sitd);  		sitd->sitd_dma = sitd_dma; +		sitd->frame = 9999;		/* an invalid value */  		list_add (&sitd->sitd_list, &iso_sched->td_list);  	} diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 20dbdcbe9b0..c3fa1305f83 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -304,7 +304,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci)  	 * (a) SMP races against real IAA firing and retriggering, and  	 * (b) clean HC shutdown, when IAA watchdog was pending.  	 */ -	if (ehci->async_iaa) { +	if (1) {  		u32 cmd, status;  		/* If we get here, IAA is *REALLY* late.  It's barely diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 35616ffbe3a..6dc238c592b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1022,44 +1022,24 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,   * is attached to (or the roothub port its ancestor hub is attached to).  All we   * know is the index of that port under either the USB 2.0 or the USB 3.0   * roothub, but that doesn't give us the real index into the HW port status - * registers.  Scan through the xHCI roothub port array, looking for the Nth - * entry of the correct port speed.  Return the port number of that entry. + * registers. Call xhci_find_raw_port_number() to get real index.   */  static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,  		struct usb_device *udev)  {  	struct usb_device *top_dev; -	unsigned int num_similar_speed_ports; -	unsigned int faked_port_num; -	int i; +	struct usb_hcd *hcd; + +	if (udev->speed == USB_SPEED_SUPER) +		hcd = xhci->shared_hcd; +	else +		hcd = xhci->main_hcd;  	for (top_dev = udev; top_dev->parent && top_dev->parent->parent;  			top_dev = top_dev->parent)  		/* Found device below root hub */; -	faked_port_num = top_dev->portnum; -	for (i = 0, num_similar_speed_ports = 0; -			i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { -		u8 port_speed = xhci->port_array[i]; - -		/* -		 * Skip ports that don't have known speeds, or have duplicate -		 * Extended Capabilities port speed entries. -		 */ -		if (port_speed == 0 || port_speed == DUPLICATE_ENTRY) -			continue; -		/* -		 * USB 3.0 ports are always under a USB 3.0 hub.  USB 2.0 and -		 * 1.1 ports are under the USB 2.0 hub.  If the port speed -		 * matches the device speed, it's a similar speed port. -		 */ -		if ((port_speed == 0x03) == (udev->speed == USB_SPEED_SUPER)) -			num_similar_speed_ports++; -		if (num_similar_speed_ports == faked_port_num) -			/* Roothub ports are numbered from 1 to N */ -			return i+1; -	} -	return 0; +	return	xhci_find_raw_port_number(hcd, top_dev->portnum);  }  /* Setup an xHCI virtual device for a Set Address command */ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index af259e0ec17..1a30c380043 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -313,6 +313,7 @@ static const struct hc_driver xhci_pci_hc_driver = {  	.set_usb2_hw_lpm =	xhci_set_usb2_hardware_lpm,  	.enable_usb3_lpm_timeout =	xhci_enable_usb3_lpm_timeout,  	.disable_usb3_lpm_timeout =	xhci_disable_usb3_lpm_timeout, +	.find_raw_port_number =	xhci_find_raw_port_number,  };  /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 88287546530..1969c001b3f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1599,14 +1599,20 @@ static void handle_port_status(struct xhci_hcd *xhci,  	max_ports = HCS_MAX_PORTS(xhci->hcs_params1);  	if ((port_id <= 0) || (port_id > max_ports)) {  		xhci_warn(xhci, "Invalid port id %d\n", port_id); -		bogus_port_status = true; -		goto cleanup; +		inc_deq(xhci, xhci->event_ring); +		return;  	}  	/* Figure out which usb_hcd this port is attached to:  	 * is it a USB 3.0 port or a USB 2.0/1.1 port?  	 */  	major_revision = xhci->port_array[port_id - 1]; + +	/* Find the right roothub. */ +	hcd = xhci_to_hcd(xhci); +	if ((major_revision == 0x03) != (hcd->speed == HCD_USB3)) +		hcd = xhci->shared_hcd; +  	if (major_revision == 0) {  		xhci_warn(xhci, "Event for port %u not in "  				"Extended Capabilities, ignoring.\n", @@ -1629,10 +1635,6 @@ static void handle_port_status(struct xhci_hcd *xhci,  	 * into the index into the ports on the correct split roothub, and the  	 * correct bus_state structure.  	 */ -	/* Find the right roothub. */ -	hcd = xhci_to_hcd(xhci); -	if ((major_revision == 0x03) != (hcd->speed == HCD_USB3)) -		hcd = xhci->shared_hcd;  	bus_state = &xhci->bus_state[hcd_index(hcd)];  	if (hcd->speed == HCD_USB3)  		port_array = xhci->usb3_ports; @@ -2027,8 +2029,8 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,  		if (event_trb != ep_ring->dequeue &&  				event_trb != td->last_trb)  			td->urb->actual_length = -				td->urb->transfer_buffer_length -				- TRB_LEN(le32_to_cpu(event->transfer_len)); +				td->urb->transfer_buffer_length - +				EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));  		else  			td->urb->actual_length = 0; @@ -2060,7 +2062,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,  		/* Maybe the event was for the data stage? */  			td->urb->actual_length =  				td->urb->transfer_buffer_length - -				TRB_LEN(le32_to_cpu(event->transfer_len)); +				EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));  			xhci_dbg(xhci, "Waiting for status "  					"stage event\n");  			return 0; @@ -2096,7 +2098,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,  	/* handle completion code */  	switch (trb_comp_code) {  	case COMP_SUCCESS: -		if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) { +		if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {  			frame->status = 0;  			break;  		} @@ -2141,7 +2143,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,  				len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));  		}  		len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - -			TRB_LEN(le32_to_cpu(event->transfer_len)); +			EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));  		if (trb_comp_code != COMP_STOP_INVAL) {  			frame->actual_length = len; @@ -2199,7 +2201,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,  	case COMP_SUCCESS:  		/* Double check that the HW transferred everything. */  		if (event_trb != td->last_trb || -				TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { +		    EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {  			xhci_warn(xhci, "WARN Successful completion "  					"on short TX\n");  			if (td->urb->transfer_flags & URB_SHORT_NOT_OK) @@ -2227,18 +2229,18 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,  				"%d bytes untransferred\n",  				td->urb->ep->desc.bEndpointAddress,  				td->urb->transfer_buffer_length, -				TRB_LEN(le32_to_cpu(event->transfer_len))); +				EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));  	/* Fast path - was this the last TRB in the TD for this URB? */  	if (event_trb == td->last_trb) { -		if (TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { +		if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {  			td->urb->actual_length =  				td->urb->transfer_buffer_length - -				TRB_LEN(le32_to_cpu(event->transfer_len)); +				EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));  			if (td->urb->transfer_buffer_length <  					td->urb->actual_length) {  				xhci_warn(xhci, "HC gave bad length "  						"of %d bytes left\n", -					  TRB_LEN(le32_to_cpu(event->transfer_len))); +					  EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));  				td->urb->actual_length = 0;  				if (td->urb->transfer_flags & URB_SHORT_NOT_OK)  					*status = -EREMOTEIO; @@ -2280,7 +2282,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,  		if (trb_comp_code != COMP_STOP_INVAL)  			td->urb->actual_length +=  				TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - -				TRB_LEN(le32_to_cpu(event->transfer_len)); +				EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));  	}  	return finish_td(xhci, td, event_trb, event, ep, status, false); @@ -2368,7 +2370,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,  	 * transfer type  	 */  	case COMP_SUCCESS: -		if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) +		if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)  			break;  		if (xhci->quirks & XHCI_TRUST_TX_LENGTH)  			trb_comp_code = COMP_SHORT_TX; @@ -2461,14 +2463,21 @@ static int handle_tx_event(struct xhci_hcd *xhci,  		 * TD list.  		 */  		if (list_empty(&ep_ring->td_list)) { -			xhci_warn(xhci, "WARN Event TRB for slot %d ep %d " -					"with no TDs queued?\n", -				  TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), -				  ep_index); -			xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", -				 (le32_to_cpu(event->flags) & -				  TRB_TYPE_BITMASK)>>10); -			xhci_print_trb_offsets(xhci, (union xhci_trb *) event); +			/* +			 * A stopped endpoint may generate an extra completion +			 * event if the device was suspended.  Don't print +			 * warnings. +			 */ +			if (!(trb_comp_code == COMP_STOP || +						trb_comp_code == COMP_STOP_INVAL)) { +				xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", +						TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), +						ep_index); +				xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", +						(le32_to_cpu(event->flags) & +						 TRB_TYPE_BITMASK)>>10); +				xhci_print_trb_offsets(xhci, (union xhci_trb *) event); +			}  			if (ep->skip) {  				ep->skip = false;  				xhci_dbg(xhci, "td_list is empty while skip " diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f1f01a834ba..53b8f89a0b1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -350,7 +350,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)  	 * generate interrupts.  Don't even try to enable MSI.  	 */  	if (xhci->quirks & XHCI_BROKEN_MSI) -		return 0; +		goto legacy_irq;  	/* unregister the legacy interrupt */  	if (hcd->irq) @@ -371,6 +371,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)  		return -EINVAL;  	} + legacy_irq:  	/* fall back to legacy interrupt*/  	ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,  			hcd->irq_descr, hcd); @@ -3778,6 +3779,28 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)  	return 0;  } +/* + * Transfer the port index into real index in the HW port status + * registers. Caculate offset between the port's PORTSC register + * and port status base. Divide the number of per port register + * to get the real index. The raw port number bases 1. + */ +int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1) +{ +	struct xhci_hcd *xhci = hcd_to_xhci(hcd); +	__le32 __iomem *base_addr = &xhci->op_regs->port_status_base; +	__le32 __iomem *addr; +	int raw_port; + +	if (hcd->speed != HCD_USB3) +		addr = xhci->usb2_ports[port1 - 1]; +	else +		addr = xhci->usb3_ports[port1 - 1]; + +	raw_port = (addr - base_addr)/NUM_PORT_REGS + 1; +	return raw_port; +} +  #ifdef CONFIG_USB_SUSPEND  /* BESL to HIRD Encoding array for USB2 LPM */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f791bd0aee6..63582719e0f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -206,8 +206,8 @@ struct xhci_op_regs {  /* bits 12:31 are reserved (and should be preserved on writes). */  /* IMAN - Interrupt Management Register */ -#define IMAN_IP		(1 << 1) -#define IMAN_IE		(1 << 0) +#define IMAN_IE		(1 << 1) +#define IMAN_IP		(1 << 0)  /* USBSTS - USB status - status bitmasks */  /* HC not running - set to 1 when run/stop bit is cleared. */ @@ -972,6 +972,10 @@ struct xhci_transfer_event {  	__le32	flags;  }; +/* Transfer event TRB length bit mask */ +/* bits 0:23 */ +#define	EVENT_TRB_LEN(p)		((p) & 0xffffff) +  /** Transfer Event bit fields **/  #define	TRB_TO_EP_ID(p)	(((p) >> 16) & 0x1f) @@ -1829,6 +1833,7 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,  int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,  		char *buf, u16 wLength);  int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); +int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);  #ifdef CONFIG_PM  int xhci_bus_suspend(struct usb_hcd *hcd); diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 45b19e2c60b..05e51432dd2 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -7,11 +7,6 @@  config USB_MUSB_HDRC  	tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'  	depends on USB && USB_GADGET -	select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN) -	select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX) -	select TWL4030_USB if MACH_OMAP_3430SDP -	select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA -	select OMAP_CONTROL_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA  	select USB_OTG_UTILS  	help  	  Say Y here if your system has a dual role high speed USB diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 7c71769d71f..41613a2b35e 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -327,7 +327,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)  		u8 devctl = musb_readb(mregs, MUSB_DEVCTL);  		int err; -		err = musb->int_usb & USB_INTR_VBUSERROR; +		err = musb->int_usb & MUSB_INTR_VBUSERROR;  		if (err) {  			/*  			 * The Mentor core doesn't debounce VBUS as needed diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 60b41cc28da..daec6e0f7e3 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1624,8 +1624,6 @@ EXPORT_SYMBOL_GPL(musb_dma_completion);  /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_SYSFS -  static ssize_t  musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)  { @@ -1742,8 +1740,6 @@ static const struct attribute_group musb_attr_group = {  	.attrs = musb_attributes,  }; -#endif	/* sysfs */ -  /* Only used to provide driver mode change events */  static void musb_irq_work(struct work_struct *data)  { @@ -1968,11 +1964,9 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)  	if (status < 0)  		goto fail4; -#ifdef CONFIG_SYSFS  	status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group);  	if (status)  		goto fail5; -#endif  	pm_runtime_put(musb->controller); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index be18537c5f1..83eddedcd9b 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -141,7 +141,9 @@ static inline void map_dma_buffer(struct musb_request *request,  static inline void unmap_dma_buffer(struct musb_request *request,  				struct musb *musb)  { -	if (!is_buffer_mapped(request)) +	struct musb_ep *musb_ep = request->ep; + +	if (!is_buffer_mapped(request) || !musb_ep->dma)  		return;  	if (request->request.dma == DMA_ADDR_INVALID) { @@ -195,7 +197,10 @@ __acquires(ep->musb->lock)  	ep->busy = 1;  	spin_unlock(&musb->lock); -	unmap_dma_buffer(req, musb); + +	if (!dma_mapping_error(&musb->g.dev, request->dma)) +		unmap_dma_buffer(req, musb); +  	if (request->status == 0)  		dev_dbg(musb->controller, "%s done request %p,  %d/%d\n",  				ep->end_point.name, request, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 1762354fe79..1a42a458f2c 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -51,7 +51,7 @@ struct omap2430_glue {  };  #define glue_to_musb(g)		platform_get_drvdata(g->musb) -struct omap2430_glue		*_glue; +static struct omap2430_glue	*_glue;  static struct timer_list musb_idle_timer; @@ -237,9 +237,13 @@ void omap_musb_mailbox(enum omap_musb_vbus_id_status status)  {  	struct omap2430_glue	*glue = _glue; -	if (glue && glue_to_musb(glue)) { -		glue->status = status; -	} else { +	if (!glue) { +		pr_err("%s: musb core is not yet initialized\n", __func__); +		return; +	} +	glue->status = status; + +	if (!glue_to_musb(glue)) {  		pr_err("%s: musb core is not yet ready\n", __func__);  		return;  	} diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index e1814397ca3..2bd03d261a5 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -130,7 +130,7 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type)  	spin_lock_irqsave(&phy_lock, flags);  	phy = __usb_find_phy(&phy_list, type); -	if (IS_ERR(phy)) { +	if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {  		pr_err("unable to find transceiver of type %s\n",  			usb_phy_type_string(type));  		goto err0; @@ -228,7 +228,7 @@ struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index)  	spin_lock_irqsave(&phy_lock, flags);  	phy = __usb_find_phy_dev(dev, &phy_bind_list, index); -	if (IS_ERR(phy)) { +	if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {  		pr_err("unable to find transceiver\n");  		goto err0;  	} @@ -301,8 +301,12 @@ EXPORT_SYMBOL(devm_usb_put_phy);   */  void usb_put_phy(struct usb_phy *x)  { -	if (x) +	if (x) { +		struct module *owner = x->dev->driver->owner; +  		put_device(x->dev); +		module_put(owner); +	}  }  EXPORT_SYMBOL(usb_put_phy); diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 65217a59006..90549382eba 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -38,6 +38,7 @@ config USB_ISP1301  	tristate "NXP ISP1301 USB transceiver support"  	depends on USB || USB_GADGET  	depends on I2C +	select USB_OTG_UTILS  	help  	  Say Y here to add support for the NXP ISP1301 USB transceiver driver.  	  This chip is typically used as USB transceiver for USB host, gadget diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c index 5323b71c352..1419ceda975 100644 --- a/drivers/usb/phy/omap-control-usb.c +++ b/drivers/usb/phy/omap-control-usb.c @@ -219,32 +219,26 @@ static int omap_control_usb_probe(struct platform_device *pdev)  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  		"control_dev_conf"); -	control_usb->dev_conf = devm_request_and_ioremap(&pdev->dev, res); -	if (!control_usb->dev_conf) { -		dev_err(&pdev->dev, "Failed to obtain io memory\n"); -		return -EADDRNOTAVAIL; -	} +	control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(control_usb->dev_conf)) +		return PTR_ERR(control_usb->dev_conf);  	if (control_usb->type == OMAP_CTRL_DEV_TYPE1) {  		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  			"otghs_control"); -		control_usb->otghs_control = devm_request_and_ioremap( +		control_usb->otghs_control = devm_ioremap_resource(  			&pdev->dev, res); -		if (!control_usb->otghs_control) { -			dev_err(&pdev->dev, "Failed to obtain io memory\n"); -			return -EADDRNOTAVAIL; -		} +		if (IS_ERR(control_usb->otghs_control)) +			return PTR_ERR(control_usb->otghs_control);  	}  	if (control_usb->type == OMAP_CTRL_DEV_TYPE2) {  		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  			"phy_power_usb"); -		control_usb->phy_power = devm_request_and_ioremap( +		control_usb->phy_power = devm_ioremap_resource(  			&pdev->dev, res); -		if (!control_usb->phy_power) { -			dev_dbg(&pdev->dev, "Failed to obtain io memory\n"); -			return -EADDRNOTAVAIL; -		} +		if (IS_ERR(control_usb->phy_power)) +			return PTR_ERR(control_usb->phy_power);  		control_usb->sys_clk = devm_clk_get(control_usb->dev,  			"sys_clkin"); diff --git a/drivers/usb/phy/omap-usb3.c b/drivers/usb/phy/omap-usb3.c index fadc0c2b65b..a6e60b1e102 100644 --- a/drivers/usb/phy/omap-usb3.c +++ b/drivers/usb/phy/omap-usb3.c @@ -212,11 +212,9 @@ static int omap_usb3_probe(struct platform_device *pdev)  	}  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); -	phy->pll_ctrl_base = devm_request_and_ioremap(&pdev->dev, res); -	if (!phy->pll_ctrl_base) { -		dev_err(&pdev->dev, "ioremap of pll_ctrl failed\n"); -		return -ENOMEM; -	} +	phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(phy->pll_ctrl_base)) +		return PTR_ERR(phy->pll_ctrl_base);  	phy->dev		= &pdev->dev; diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 6ea55373383..967101ec15f 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -787,11 +787,9 @@ static int samsung_usbphy_probe(struct platform_device *pdev)  		return -ENODEV;  	} -	phy_base = devm_request_and_ioremap(dev, phy_mem); -	if (!phy_base) { -		dev_err(dev, "%s: register mapping failed\n", __func__); -		return -ENXIO; -	} +	phy_base = devm_ioremap_resource(dev, phy_mem); +	if (IS_ERR(phy_base)) +		return PTR_ERR(phy_base);  	sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);  	if (!sphy) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index cbd904b8fba..4775f8209e5 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -62,7 +62,6 @@ static int is_irda(struct usb_serial *serial)  }  struct ark3116_private { -	wait_queue_head_t       delta_msr_wait;  	struct async_icount	icount;  	int			irda;	/* 1 for irda device */ @@ -146,7 +145,6 @@ static int ark3116_port_probe(struct usb_serial_port *port)  	if (!priv)  		return -ENOMEM; -	init_waitqueue_head(&priv->delta_msr_wait);  	mutex_init(&priv->hw_lock);  	spin_lock_init(&priv->status_lock); @@ -456,10 +454,14 @@ static int ark3116_ioctl(struct tty_struct *tty,  	case TIOCMIWAIT:  		for (;;) {  			struct async_icount prev = priv->icount; -			interruptible_sleep_on(&priv->delta_msr_wait); +			interruptible_sleep_on(&port->delta_msr_wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			if ((prev.rng == priv->icount.rng) &&  			    (prev.dsr == priv->icount.dsr) &&  			    (prev.dcd == priv->icount.dcd) && @@ -580,7 +582,7 @@ static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr)  			priv->icount.dcd++;  		if (msr & UART_MSR_TERI)  			priv->icount.rng++; -		wake_up_interruptible(&priv->delta_msr_wait); +		wake_up_interruptible(&port->delta_msr_wait);  	}  } diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index d255f66e708..07d4650a32a 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -80,7 +80,6 @@ MODULE_DEVICE_TABLE(usb, id_table);  struct ch341_private {  	spinlock_t lock; /* access lock */ -	wait_queue_head_t delta_msr_wait; /* wait queue for modem status */  	unsigned baud_rate; /* set baud rate */  	u8 line_control; /* set line control value RTS/DTR */  	u8 line_status; /* active status of modem control inputs */ @@ -252,7 +251,6 @@ static int ch341_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->lock); -	init_waitqueue_head(&priv->delta_msr_wait);  	priv->baud_rate = DEFAULT_BAUD_RATE;  	priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; @@ -298,7 +296,7 @@ static void ch341_dtr_rts(struct usb_serial_port *port, int on)  		priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);  	spin_unlock_irqrestore(&priv->lock, flags);  	ch341_set_handshake(port->serial->dev, priv->line_control); -	wake_up_interruptible(&priv->delta_msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  }  static void ch341_close(struct usb_serial_port *port) @@ -491,7 +489,7 @@ static void ch341_read_int_callback(struct urb *urb)  			tty_kref_put(tty);  		} -		wake_up_interruptible(&priv->delta_msr_wait); +		wake_up_interruptible(&port->delta_msr_wait);  	}  exit: @@ -517,11 +515,14 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  	spin_unlock_irqrestore(&priv->lock, flags);  	while (!multi_change) { -		interruptible_sleep_on(&priv->delta_msr_wait); +		interruptible_sleep_on(&port->delta_msr_wait);  		/* see if a signal did it */  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->lock, flags);  		status = priv->line_status;  		multi_change = priv->multi_status_change; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index edc0f0dcad8..4747d1c328f 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -85,6 +85,7 @@ static const struct usb_device_id id_table[] = {  	{ USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */  	{ USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */  	{ USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ +	{ USB_DEVICE(0x2405, 0x0003) }, /* West Mountain Radio RIGblaster Advantage */  	{ USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */  	{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */  	{ USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */ @@ -150,6 +151,25 @@ static const struct usb_device_id id_table[] = {  	{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */  	{ USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */  	{ USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */ +	{ USB_DEVICE(0x1FB9, 0x0100) }, /* Lake Shore Model 121 Current Source */ +	{ USB_DEVICE(0x1FB9, 0x0200) }, /* Lake Shore Model 218A Temperature Monitor */ +	{ USB_DEVICE(0x1FB9, 0x0201) }, /* Lake Shore Model 219 Temperature Monitor */ +	{ USB_DEVICE(0x1FB9, 0x0202) }, /* Lake Shore Model 233 Temperature Transmitter */ +	{ USB_DEVICE(0x1FB9, 0x0203) }, /* Lake Shore Model 235 Temperature Transmitter */ +	{ USB_DEVICE(0x1FB9, 0x0300) }, /* Lake Shore Model 335 Temperature Controller */ +	{ USB_DEVICE(0x1FB9, 0x0301) }, /* Lake Shore Model 336 Temperature Controller */ +	{ USB_DEVICE(0x1FB9, 0x0302) }, /* Lake Shore Model 350 Temperature Controller */ +	{ USB_DEVICE(0x1FB9, 0x0303) }, /* Lake Shore Model 371 AC Bridge */ +	{ USB_DEVICE(0x1FB9, 0x0400) }, /* Lake Shore Model 411 Handheld Gaussmeter */ +	{ USB_DEVICE(0x1FB9, 0x0401) }, /* Lake Shore Model 425 Gaussmeter */ +	{ USB_DEVICE(0x1FB9, 0x0402) }, /* Lake Shore Model 455A Gaussmeter */ +	{ USB_DEVICE(0x1FB9, 0x0403) }, /* Lake Shore Model 475A Gaussmeter */ +	{ USB_DEVICE(0x1FB9, 0x0404) }, /* Lake Shore Model 465 Three Axis Gaussmeter */ +	{ USB_DEVICE(0x1FB9, 0x0600) }, /* Lake Shore Model 625A Superconducting MPS */ +	{ USB_DEVICE(0x1FB9, 0x0601) }, /* Lake Shore Model 642A Magnet Power Supply */ +	{ USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */ +	{ USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */ +	{ USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */  	{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */  	{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */  	{ USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 8efa19d0e9f..ba7352e4187 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -111,7 +111,6 @@ struct cypress_private {  	int baud_rate;			   /* stores current baud rate in  					      integer form */  	int isthrottled;		   /* if throttled, discard reads */ -	wait_queue_head_t delta_msr_wait;  /* used for TIOCMIWAIT */  	char prev_status, diff_status;	   /* used for TIOCMIWAIT */  	/* we pass a pointer to this as the argument sent to  	   cypress_set_termios old_termios */ @@ -449,7 +448,6 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)  		kfree(priv);  		return -ENOMEM;  	} -	init_waitqueue_head(&priv->delta_msr_wait);  	usb_reset_configuration(serial->dev); @@ -868,12 +866,16 @@ static int cypress_ioctl(struct tty_struct *tty,  	switch (cmd) {  	/* This code comes from drivers/char/serial.c and ftdi_sio.c */  	case TIOCMIWAIT: -		while (priv != NULL) { -			interruptible_sleep_on(&priv->delta_msr_wait); +		for (;;) { +			interruptible_sleep_on(&port->delta_msr_wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; -			else { + +			if (port->serial->disconnected) +				return -EIO; + +			{  				char diff = priv->diff_status;  				if (diff == 0)  					return -EIO; /* no change => error */ @@ -1187,7 +1189,7 @@ static void cypress_read_int_callback(struct urb *urb)  	if (priv->current_status != priv->prev_status) {  		priv->diff_status |= priv->current_status ^  			priv->prev_status; -		wake_up_interruptible(&priv->delta_msr_wait); +		wake_up_interruptible(&port->delta_msr_wait);  		priv->prev_status = priv->current_status;  	}  	spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index b1b2dc64b50..a172ad5c5ce 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -47,7 +47,6 @@ MODULE_DEVICE_TABLE(usb, id_table);  struct f81232_private {  	spinlock_t lock; -	wait_queue_head_t delta_msr_wait;  	u8 line_control;  	u8 line_status;  }; @@ -111,7 +110,7 @@ static void f81232_process_read_urb(struct urb *urb)  	line_status = priv->line_status;  	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;  	spin_unlock_irqrestore(&priv->lock, flags); -	wake_up_interruptible(&priv->delta_msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  	if (!urb->actual_length)  		return; @@ -256,11 +255,14 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  	spin_unlock_irqrestore(&priv->lock, flags);  	while (1) { -		interruptible_sleep_on(&priv->delta_msr_wait); +		interruptible_sleep_on(&port->delta_msr_wait);  		/* see if a signal did it */  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->lock, flags);  		status = priv->line_status;  		spin_unlock_irqrestore(&priv->lock, flags); @@ -322,7 +324,6 @@ static int f81232_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->lock); -	init_waitqueue_head(&priv->delta_msr_wait);  	usb_set_serial_port_data(port, priv); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index edd162df49c..9886180e45f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -69,9 +69,7 @@ struct ftdi_private {  	int flags;		/* some ASYNC_xxxx flags are supported */  	unsigned long last_dtr_rts;	/* saved modem control outputs */  	struct async_icount	icount; -	wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */  	char prev_status;        /* Used for TIOCMIWAIT */ -	bool dev_gone;        /* Used to abort TIOCMIWAIT */  	char transmit_empty;	/* If transmitter is empty or not */  	__u16 interface;	/* FT2232C, FT2232H or FT4232H port interface  				   (0 for FT232/245) */ @@ -642,6 +640,7 @@ static struct usb_device_id id_table_combined [] = {  	{ USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },  	{ USB_DEVICE(ACTON_VID, ACTON_SPECTRAPRO_PID) },  	{ USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) }, +	{ USB_DEVICE(MITSUBISHI_VID, MITSUBISHI_FXUSB_PID) },  	{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },  	{ USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },  	{ USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, @@ -1691,10 +1690,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)  	kref_init(&priv->kref);  	mutex_init(&priv->cfg_lock); -	init_waitqueue_head(&priv->delta_msr_wait);  	priv->flags = ASYNC_LOW_LATENCY; -	priv->dev_gone = false;  	if (quirk && quirk->port_probe)  		quirk->port_probe(priv); @@ -1840,8 +1837,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)  {  	struct ftdi_private *priv = usb_get_serial_port_data(port); -	priv->dev_gone = true; -	wake_up_interruptible_all(&priv->delta_msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  	remove_sysfs_attrs(port); @@ -1989,7 +1985,7 @@ static int ftdi_process_packet(struct usb_serial_port *port,  		if (diff_status & FTDI_RS0_RLSD)  			priv->icount.dcd++; -		wake_up_interruptible_all(&priv->delta_msr_wait); +		wake_up_interruptible(&port->delta_msr_wait);  		priv->prev_status = status;  	} @@ -2440,11 +2436,15 @@ static int ftdi_ioctl(struct tty_struct *tty,  	 */  	case TIOCMIWAIT:  		cprev = priv->icount; -		while (!priv->dev_gone) { -			interruptible_sleep_on(&priv->delta_msr_wait); +		for (;;) { +			interruptible_sleep_on(&port->delta_msr_wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			cnow = priv->icount;  			if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||  			    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || @@ -2454,8 +2454,6 @@ static int ftdi_ioctl(struct tty_struct *tty,  			}  			cprev = cnow;  		} -		return -EIO; -		break;  	case TIOCSERGETLSR:  		return get_lsr_info(port, (struct serial_struct __user *)arg);  		break; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 9d359e189a6..e79861eeed4 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -584,6 +584,13 @@  #define CONTEC_COM1USBH_PID	0x8311	/* COM-1(USB)H */  /* + * Mitsubishi Electric Corp. (http://www.meau.com) + * Submitted by Konstantin Holoborodko + */ +#define MITSUBISHI_VID		0x06D3 +#define MITSUBISHI_FXUSB_PID	0x0284 /* USB/RS422 converters: FX-USB-AW/-BD */ + +/*   * Definitions for B&B Electronics products.   */  #define BANDB_VID		0x0856	/* B&B Electronics Vendor ID */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 1a07b12ef34..81caf5623ee 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -956,10 +956,7 @@ static void garmin_close(struct usb_serial_port *port)  	if (!serial)  		return; -	mutex_lock(&port->serial->disc_mutex); - -	if (!port->serial->disconnected) -		garmin_clear(garmin_data_p); +	garmin_clear(garmin_data_p);  	/* shutdown our urbs */  	usb_kill_urb(port->read_urb); @@ -968,8 +965,6 @@ static void garmin_close(struct usb_serial_port *port)  	/* keep reset state so we know that we must start a new session */  	if (garmin_data_p->state != STATE_RESET)  		garmin_data_p->state = STATE_DISCONNECTED; - -	mutex_unlock(&port->serial->disc_mutex);  } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index b00e5cbf741..efd8b978128 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -110,7 +110,6 @@ struct edgeport_port {  	wait_queue_head_t	wait_chase;		/* for handling sleeping while waiting for chase to finish */  	wait_queue_head_t	wait_open;		/* for handling sleeping while waiting for open to finish */  	wait_queue_head_t	wait_command;		/* for handling sleeping while waiting for command to finish */ -	wait_queue_head_t	delta_msr_wait;		/* for handling sleeping while waiting for msr change to happen */  	struct async_icount	icount;  	struct usb_serial_port	*port;			/* loop back to the owner of this object */ @@ -884,7 +883,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)  	/* initialize our wait queues */  	init_waitqueue_head(&edge_port->wait_open);  	init_waitqueue_head(&edge_port->wait_chase); -	init_waitqueue_head(&edge_port->delta_msr_wait);  	init_waitqueue_head(&edge_port->wait_command);  	/* initialize our icount structure */ @@ -1669,13 +1667,17 @@ static int edge_ioctl(struct tty_struct *tty,  		dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__,  port->number);  		cprev = edge_port->icount;  		while (1) { -			prepare_to_wait(&edge_port->delta_msr_wait, +			prepare_to_wait(&port->delta_msr_wait,  						&wait, TASK_INTERRUPTIBLE);  			schedule(); -			finish_wait(&edge_port->delta_msr_wait, &wait); +			finish_wait(&port->delta_msr_wait, &wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			cnow = edge_port->icount;  			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&  			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) @@ -2051,7 +2053,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr)  			icount->dcd++;  		if (newMsr & EDGEPORT_MSR_DELTA_RI)  			icount->rng++; -		wake_up_interruptible(&edge_port->delta_msr_wait); +		wake_up_interruptible(&edge_port->port->delta_msr_wait);  	}  	/* Save the new modem status */ diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index c23776679f7..7777172206d 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -87,9 +87,6 @@ struct edgeport_port {  	int close_pending;  	int lsr_event;  	struct async_icount	icount; -	wait_queue_head_t	delta_msr_wait;	/* for handling sleeping while -						   waiting for msr change to -						   happen */  	struct edgeport_serial	*edge_serial;  	struct usb_serial_port	*port;  	__u8 bUartMode;		/* Port type, 0: RS232, etc. */ @@ -1459,7 +1456,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr)  			icount->dcd++;  		if (msr & EDGEPORT_MSR_DELTA_RI)  			icount->rng++; -		wake_up_interruptible(&edge_port->delta_msr_wait); +		wake_up_interruptible(&edge_port->port->delta_msr_wait);  	}  	/* Save the new modem status */ @@ -1754,7 +1751,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)  	dev = port->serial->dev;  	memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount)); -	init_waitqueue_head(&edge_port->delta_msr_wait);  	/* turn off loopback */  	status = ti_do_config(edge_port, UMPC_SET_CLR_LOOPBACK, 0); @@ -2434,10 +2430,14 @@ static int edge_ioctl(struct tty_struct *tty,  		dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__);  		cprev = edge_port->icount;  		while (1) { -			interruptible_sleep_on(&edge_port->delta_msr_wait); +			interruptible_sleep_on(&port->delta_msr_wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			cnow = edge_port->icount;  			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&  			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) @@ -2649,6 +2649,7 @@ static struct usb_serial_driver edgeport_2port_device = {  	.set_termios		= edge_set_termios,  	.tiocmget		= edge_tiocmget,  	.tiocmset		= edge_tiocmset, +	.get_icount		= edge_get_icount,  	.write			= edge_write,  	.write_room		= edge_write_room,  	.chars_in_buffer	= edge_chars_in_buffer, diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index a64d420f687..06d5a60be2c 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -114,8 +114,6 @@ struct mct_u232_private {  	unsigned char	     last_msr;      /* Modem Status Register */  	unsigned int	     rx_flags;      /* Throttling flags */  	struct async_icount  icount; -	wait_queue_head_t    msr_wait;	/* for handling sleeping while waiting -						for msr change to happen */  };  #define THROTTLED		0x01 @@ -409,7 +407,6 @@ static int mct_u232_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->lock); -	init_waitqueue_head(&priv->msr_wait);  	usb_set_serial_port_data(port, priv); @@ -601,7 +598,7 @@ static void mct_u232_read_int_callback(struct urb *urb)  		tty_kref_put(tty);  	}  #endif -	wake_up_interruptible(&priv->msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  	spin_unlock_irqrestore(&priv->lock, flags);  exit:  	retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -810,13 +807,17 @@ static int  mct_u232_ioctl(struct tty_struct *tty,  		cprev = mct_u232_port->icount;  		spin_unlock_irqrestore(&mct_u232_port->lock, flags);  		for ( ; ; ) { -			prepare_to_wait(&mct_u232_port->msr_wait, +			prepare_to_wait(&port->delta_msr_wait,  					&wait, TASK_INTERRUPTIBLE);  			schedule(); -			finish_wait(&mct_u232_port->msr_wait, &wait); +			finish_wait(&port->delta_msr_wait, &wait);  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			spin_lock_irqsave(&mct_u232_port->lock, flags);  			cnow = mct_u232_port->icount;  			spin_unlock_irqrestore(&mct_u232_port->lock, flags); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 809fb329eca..b8051fa6191 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -219,7 +219,6 @@ struct moschip_port {  	char open;  	char open_ports;  	wait_queue_head_t wait_chase;	/* for handling sleeping while waiting for chase to finish */ -	wait_queue_head_t delta_msr_wait;	/* for handling sleeping while waiting for msr change to happen */  	int delta_msr_cond;  	struct async_icount icount;  	struct usb_serial_port *port;	/* loop back to the owner of this object */ @@ -423,6 +422,9 @@ static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)  			icount->rng++;  			smp_wmb();  		} + +		mos7840_port->delta_msr_cond = 1; +		wake_up_interruptible(&port->port->delta_msr_wait);  	}  } @@ -1127,7 +1129,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)  	/* initialize our wait queues */  	init_waitqueue_head(&mos7840_port->wait_chase); -	init_waitqueue_head(&mos7840_port->delta_msr_wait);  	/* initialize our icount structure */  	memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount)); @@ -2017,8 +2018,6 @@ static void mos7840_change_port_settings(struct tty_struct *tty,  			mos7840_port->read_urb_busy = false;  		}  	} -	wake_up(&mos7840_port->delta_msr_wait); -	mos7840_port->delta_msr_cond = 1;  	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__,  		mos7840_port->shadowLCR);  } @@ -2219,13 +2218,18 @@ static int mos7840_ioctl(struct tty_struct *tty,  		while (1) {  			/* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */  			mos7840_port->delta_msr_cond = 0; -			wait_event_interruptible(mos7840_port->delta_msr_wait, -						 (mos7840_port-> +			wait_event_interruptible(port->delta_msr_wait, +						 (port->serial->disconnected || +						  mos7840_port->  						  delta_msr_cond == 1));  			/* see if a signal did it */  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			cnow = mos7840_port->icount;  			smp_rmb();  			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index f7d339d8187..558adfc0500 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -341,6 +341,8 @@ static void option_instat_callback(struct urb *urb);  #define CINTERION_PRODUCT_EU3_E			0x0051  #define CINTERION_PRODUCT_EU3_P			0x0052  #define CINTERION_PRODUCT_PH8			0x0053 +#define CINTERION_PRODUCT_AH6			0x0055 +#define CINTERION_PRODUCT_PLS8			0x0060  /* Olivetti products */  #define OLIVETTI_VENDOR_ID			0x0b3c @@ -579,6 +581,7 @@ static const struct usb_device_id option_ids[] = {  	{ USB_DEVICE(QUANTA_VENDOR_ID, 0xea42),  		.driver_info = (kernel_ulong_t)&net_intf4_blacklist },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff),  		.driver_info = (kernel_ulong_t) &net_intf1_blacklist }, @@ -1260,6 +1263,8 @@ static const struct usb_device_id option_ids[] = {  	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_E) },  	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_P) },  	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8) }, +	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AH6) }, +	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLS8) },  	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },   	{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },  	{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) }, diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a958fd41b5b..87c71ccfee8 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -188,7 +188,6 @@ struct oti6858_private {  	u8 setup_done;  	struct delayed_work delayed_setup_work; -	wait_queue_head_t intr_wait;  	struct usb_serial_port *port;   /* USB port with which associated */  }; @@ -339,7 +338,6 @@ static int oti6858_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->lock); -	init_waitqueue_head(&priv->intr_wait);  	priv->port = port;  	INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line);  	INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); @@ -664,11 +662,15 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  	spin_unlock_irqrestore(&priv->lock, flags);  	while (1) { -		wait_event_interruptible(priv->intr_wait, +		wait_event_interruptible(port->delta_msr_wait, +					port->serial->disconnected ||  					priv->status.pin_state != prev);  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->lock, flags);  		status = priv->status.pin_state & PIN_MASK;  		spin_unlock_irqrestore(&priv->lock, flags); @@ -763,7 +765,7 @@ static void oti6858_read_int_callback(struct urb *urb)  		if (!priv->transient) {  			if (xs->pin_state != priv->status.pin_state) -				wake_up_interruptible(&priv->intr_wait); +				wake_up_interruptible(&port->delta_msr_wait);  			memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE);  		} diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 54adc9125e5..3b10018d89a 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -139,7 +139,6 @@ struct pl2303_serial_private {  struct pl2303_private {  	spinlock_t lock; -	wait_queue_head_t delta_msr_wait;  	u8 line_control;  	u8 line_status;  }; @@ -233,7 +232,6 @@ static int pl2303_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->lock); -	init_waitqueue_head(&priv->delta_msr_wait);  	usb_set_serial_port_data(port, priv); @@ -607,11 +605,14 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  	spin_unlock_irqrestore(&priv->lock, flags);  	while (1) { -		interruptible_sleep_on(&priv->delta_msr_wait); +		interruptible_sleep_on(&port->delta_msr_wait);  		/* see if a signal did it */  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->lock, flags);  		status = priv->line_status;  		spin_unlock_irqrestore(&priv->lock, flags); @@ -719,7 +720,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port,  	spin_unlock_irqrestore(&priv->lock, flags);  	if (priv->line_status & UART_BREAK_ERROR)  		usb_serial_handle_break(port); -	wake_up_interruptible(&priv->delta_msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  	tty = tty_port_tty_get(&port->port);  	if (!tty) @@ -783,7 +784,7 @@ static void pl2303_process_read_urb(struct urb *urb)  	line_status = priv->line_status;  	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;  	spin_unlock_irqrestore(&priv->lock, flags); -	wake_up_interruptible(&priv->delta_msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  	if (!urb->actual_length)  		return; diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 9b1b96f2d09..31f81c3c15e 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -69,6 +69,7 @@ static struct usb_device_id id_table[] = {  	{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfd, 0xff) },  /* NMEA */  	{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfe, 0xff) },  /* WMC */  	{ USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xff, 0xff) },  /* DIAG */ +	{ USB_DEVICE_AND_INTERFACE_INFO(0x1fac, 0x0151, 0xff, 0xff, 0xff) },  	{ },  };  MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 24662547dc5..59b32b78212 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -197,12 +197,15 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)  	if (is_gobi1k) {  		/* Gobi 1K USB layout: -		 * 0: serial port (doesn't respond) +		 * 0: DM/DIAG (use libqcdm from ModemManager for communication)  		 * 1: serial port (doesn't respond)  		 * 2: AT-capable modem port  		 * 3: QMI/net  		 */ -		if (ifnum == 2) +		if (ifnum == 0) { +			dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n"); +			altsetting = 1; +		} else if (ifnum == 2)  			dev_dbg(dev, "Modem port found\n");  		else  			altsetting = -1; diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 00e6c9bac8a..75f125ddb0c 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -128,7 +128,6 @@ struct qt2_port_private {  	u8          shadowLSR;  	u8          shadowMSR; -	wait_queue_head_t   delta_msr_wait; /* Used for TIOCMIWAIT */  	struct async_icount icount;  	struct usb_serial_port *port; @@ -506,8 +505,9 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  	spin_unlock_irqrestore(&priv->lock, flags);  	while (1) { -		wait_event_interruptible(priv->delta_msr_wait, -					 ((priv->icount.rng != prev.rng) || +		wait_event_interruptible(port->delta_msr_wait, +					 (port->serial->disconnected || +					  (priv->icount.rng != prev.rng) ||  					  (priv->icount.dsr != prev.dsr) ||  					  (priv->icount.dcd != prev.dcd) ||  					  (priv->icount.cts != prev.cts))); @@ -515,6 +515,9 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->lock, flags);  		cur = priv->icount;  		spin_unlock_irqrestore(&priv->lock, flags); @@ -661,7 +664,9 @@ void qt2_process_read_urb(struct urb *urb)  						 __func__);  					break;  				} -				tty_flip_buffer_push(&port->port); + +				if (port_priv->is_open) +					tty_flip_buffer_push(&port->port);  				newport = *(ch + 3); @@ -704,7 +709,8 @@ void qt2_process_read_urb(struct urb *urb)  		tty_insert_flip_string(&port->port, ch, 1);  	} -	tty_flip_buffer_push(&port->port); +	if (port_priv->is_open) +		tty_flip_buffer_push(&port->port);  }  static void qt2_write_bulk_callback(struct urb *urb) @@ -824,7 +830,6 @@ static int qt2_port_probe(struct usb_serial_port *port)  	spin_lock_init(&port_priv->lock);  	spin_lock_init(&port_priv->urb_lock); -	init_waitqueue_head(&port_priv->delta_msr_wait);  	port_priv->port = port;  	port_priv->write_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -967,7 +972,7 @@ static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch)  		if (newMSR & UART_MSR_TERI)  			port_priv->icount.rng++; -		wake_up_interruptible(&port_priv->delta_msr_wait); +		wake_up_interruptible(&port->delta_msr_wait);  	}  } diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 91ff8e3bddb..549ef68ff5f 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -149,7 +149,6 @@ enum spcp8x5_type {  struct spcp8x5_private {  	spinlock_t 	lock;  	enum spcp8x5_type	type; -	wait_queue_head_t	delta_msr_wait;  	u8 			line_control;  	u8 			line_status;  }; @@ -179,7 +178,6 @@ static int spcp8x5_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->lock); -	init_waitqueue_head(&priv->delta_msr_wait);  	priv->type = type;  	usb_set_serial_port_data(port , priv); @@ -475,7 +473,7 @@ static void spcp8x5_process_read_urb(struct urb *urb)  	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;  	spin_unlock_irqrestore(&priv->lock, flags);  	/* wake up the wait for termios */ -	wake_up_interruptible(&priv->delta_msr_wait); +	wake_up_interruptible(&port->delta_msr_wait);  	if (!urb->actual_length)  		return; @@ -526,12 +524,15 @@ static int spcp8x5_wait_modem_info(struct usb_serial_port *port,  	while (1) {  		/* wake up in bulk read */ -		interruptible_sleep_on(&priv->delta_msr_wait); +		interruptible_sleep_on(&port->delta_msr_wait);  		/* see if a signal did it */  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->lock, flags);  		status = priv->line_status;  		spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index b57cf841c5b..4b2a19757b4 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -61,7 +61,6 @@ struct ssu100_port_private {  	spinlock_t status_lock;  	u8 shadowLSR;  	u8 shadowMSR; -	wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */  	struct async_icount icount;  }; @@ -355,8 +354,9 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  	spin_unlock_irqrestore(&priv->status_lock, flags);  	while (1) { -		wait_event_interruptible(priv->delta_msr_wait, -					 ((priv->icount.rng != prev.rng) || +		wait_event_interruptible(port->delta_msr_wait, +					 (port->serial->disconnected || +					  (priv->icount.rng != prev.rng) ||  					  (priv->icount.dsr != prev.dsr) ||  					  (priv->icount.dcd != prev.dcd) ||  					  (priv->icount.cts != prev.cts))); @@ -364,6 +364,9 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)  		if (signal_pending(current))  			return -ERESTARTSYS; +		if (port->serial->disconnected) +			return -EIO; +  		spin_lock_irqsave(&priv->status_lock, flags);  		cur = priv->icount;  		spin_unlock_irqrestore(&priv->status_lock, flags); @@ -445,7 +448,6 @@ static int ssu100_port_probe(struct usb_serial_port *port)  		return -ENOMEM;  	spin_lock_init(&priv->status_lock); -	init_waitqueue_head(&priv->delta_msr_wait);  	usb_set_serial_port_data(port, priv); @@ -537,7 +539,7 @@ static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)  			priv->icount.dcd++;  		if (msr & UART_MSR_TERI)  			priv->icount.rng++; -		wake_up_interruptible(&priv->delta_msr_wait); +		wake_up_interruptible(&port->delta_msr_wait);  	}  } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 39cb9b807c3..73deb029fc0 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -74,7 +74,6 @@ struct ti_port {  	int			tp_flags;  	int			tp_closing_wait;/* in .01 secs */  	struct async_icount	tp_icount; -	wait_queue_head_t	tp_msr_wait;	/* wait for msr change */  	wait_queue_head_t	tp_write_wait;  	struct ti_device	*tp_tdev;  	struct usb_serial_port	*tp_port; @@ -432,7 +431,6 @@ static int ti_port_probe(struct usb_serial_port *port)  	else  		tport->tp_uart_base_addr = TI_UART2_BASE_ADDR;  	tport->tp_closing_wait = closing_wait; -	init_waitqueue_head(&tport->tp_msr_wait);  	init_waitqueue_head(&tport->tp_write_wait);  	if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, GFP_KERNEL)) {  		kfree(tport); @@ -784,9 +782,13 @@ static int ti_ioctl(struct tty_struct *tty,  		dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__);  		cprev = tport->tp_icount;  		while (1) { -			interruptible_sleep_on(&tport->tp_msr_wait); +			interruptible_sleep_on(&port->delta_msr_wait);  			if (signal_pending(current))  				return -ERESTARTSYS; + +			if (port->serial->disconnected) +				return -EIO; +  			cnow = tport->tp_icount;  			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&  			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) @@ -1392,7 +1394,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr)  			icount->dcd++;  		if (msr & TI_MSR_DELTA_RI)  			icount->rng++; -		wake_up_interruptible(&tport->tp_msr_wait); +		wake_up_interruptible(&tport->tp_port->delta_msr_wait);  		spin_unlock_irqrestore(&tport->tp_lock, flags);  	} diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a19ed74d770..5d9b178484f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -151,6 +151,7 @@ static void destroy_serial(struct kref *kref)  		}  	} +	usb_put_intf(serial->interface);  	usb_put_dev(serial->dev);  	kfree(serial);  } @@ -620,7 +621,7 @@ static struct usb_serial *create_serial(struct usb_device *dev,  	}  	serial->dev = usb_get_dev(dev);  	serial->type = driver; -	serial->interface = interface; +	serial->interface = usb_get_intf(interface);  	kref_init(&serial->kref);  	mutex_init(&serial->disc_mutex);  	serial->minor = SERIAL_TTY_NO_MINOR; @@ -902,6 +903,7 @@ static int usb_serial_probe(struct usb_interface *interface,  		port->port.ops = &serial_port_ops;  		port->serial = serial;  		spin_lock_init(&port->lock); +		init_waitqueue_head(&port->delta_msr_wait);  		/* Keep this for private driver use for the moment but  		   should probably go away */  		INIT_WORK(&port->work, usb_serial_port_work); diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 7ab9046ae0e..105d900150c 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -92,8 +92,8 @@ int usb_stor_ucr61s2b_init(struct us_data *us)  	return 0;  } -/* This places the HUAWEI usb dongles in multi-port mode */ -static int usb_stor_huawei_feature_init(struct us_data *us) +/* This places the HUAWEI E220 devices in multi-port mode */ +int usb_stor_huawei_e220_init(struct us_data *us)  {  	int result; @@ -104,75 +104,3 @@ static int usb_stor_huawei_feature_init(struct us_data *us)  	US_DEBUGP("Huawei mode set result is %d\n", result);  	return 0;  } - -/* - * It will send a scsi switch command called rewind' to huawei dongle. - * When the dongle receives this command at the first time, - * it will reboot immediately. After rebooted, it will ignore this command. - * So it is  unnecessary to read its response. - */ -static int usb_stor_huawei_scsi_init(struct us_data *us) -{ -	int result = 0; -	int act_len = 0; -	struct bulk_cb_wrap *bcbw = (struct bulk_cb_wrap *) us->iobuf; -	char rewind_cmd[] = {0x11, 0x06, 0x20, 0x00, 0x00, 0x01, 0x01, 0x00, -			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -	bcbw->Signature = cpu_to_le32(US_BULK_CB_SIGN); -	bcbw->Tag = 0; -	bcbw->DataTransferLength = 0; -	bcbw->Flags = bcbw->Lun = 0; -	bcbw->Length = sizeof(rewind_cmd); -	memset(bcbw->CDB, 0, sizeof(bcbw->CDB)); -	memcpy(bcbw->CDB, rewind_cmd, sizeof(rewind_cmd)); - -	result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcbw, -					US_BULK_CB_WRAP_LEN, &act_len); -	US_DEBUGP("transfer actual length=%d, result=%d\n", act_len, result); -	return result; -} - -/* - * It tries to find the supported Huawei USB dongles. - * In Huawei, they assign the following product IDs - * for all of their mobile broadband dongles, - * including the new dongles in the future. - * So if the product ID is not included in this list, - * it means it is not Huawei's mobile broadband dongles. - */ -static int usb_stor_huawei_dongles_pid(struct us_data *us) -{ -	struct usb_interface_descriptor *idesc; -	int idProduct; - -	idesc = &us->pusb_intf->cur_altsetting->desc; -	idProduct = le16_to_cpu(us->pusb_dev->descriptor.idProduct); -	/* The first port is CDROM, -	 * means the dongle in the single port mode, -	 * and a switch command is required to be sent. */ -	if (idesc && idesc->bInterfaceNumber == 0) { -		if ((idProduct == 0x1001) -			|| (idProduct == 0x1003) -			|| (idProduct == 0x1004) -			|| (idProduct >= 0x1401 && idProduct <= 0x1500) -			|| (idProduct >= 0x1505 && idProduct <= 0x1600) -			|| (idProduct >= 0x1c02 && idProduct <= 0x2202)) { -			return 1; -		} -	} -	return 0; -} - -int usb_stor_huawei_init(struct us_data *us) -{ -	int result = 0; - -	if (usb_stor_huawei_dongles_pid(us)) { -		if (le16_to_cpu(us->pusb_dev->descriptor.idProduct) >= 0x1446) -			result = usb_stor_huawei_scsi_init(us); -		else -			result = usb_stor_huawei_feature_init(us); -	} -	return result; -} diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 5376d4fc76f..529327fbb06 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -46,5 +46,5 @@ int usb_stor_euscsi_init(struct us_data *us);   * flash reader */  int usb_stor_ucr61s2b_init(struct us_data *us); -/* This places the HUAWEI usb dongles in multi-port mode */ -int usb_stor_huawei_init(struct us_data *us); +/* This places the HUAWEI E220 devices in multi-port mode */ +int usb_stor_huawei_e220_init(struct us_data *us); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 72923b56bbf..1799335288b 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -53,6 +53,14 @@   * as opposed to devices that do something strangely or wrongly.   */ +/* In-kernel mode switching is deprecated.  Do not add new devices to + * this list for the sole purpose of switching them to a different + * mode.  Existing userspace solutions are superior. + * + * New mode switching devices should instead be added to the database + * maintained at http://www.draisberghof.de/usb_modeswitch/ + */ +  #if !defined(CONFIG_USB_STORAGE_SDDR09) && \  		!defined(CONFIG_USB_STORAGE_SDDR09_MODULE)  #define NO_SDDR09 @@ -488,6 +496,13 @@ UNUSUAL_DEV(  0x04e8, 0x5122, 0x0000, 0x9999,  		USB_SC_DEVICE, USB_PR_DEVICE, NULL,  		US_FL_MAX_SECTORS_64 | US_FL_BULK_IGNORE_TAG), +/* Added by Dmitry Artamonow <mad_soft@inbox.ru> */ +UNUSUAL_DEV(  0x04e8, 0x5136, 0x0000, 0x9999, +		"Samsung", +		"YP-Z3", +		USB_SC_DEVICE, USB_PR_DEVICE, NULL, +		US_FL_MAX_SECTORS_64), +  /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>.   * Device uses standards-violating 32-byte Bulk Command Block Wrappers and   * reports itself as "Proprietary SCSI Bulk." Cf. device entry 0x084d:0x0011. @@ -1527,10 +1542,335 @@ UNUSUAL_DEV(  0x1210, 0x0003, 0x0100, 0x0100,  /* Reported by fangxiaozhi <huananhu@huawei.com>   * This brings the HUAWEI data card devices into multi-port mode   */ -UNUSUAL_VENDOR_INTF(0x12d1, 0x08, 0x06, 0x50, +UNUSUAL_DEV(  0x12d1, 0x1001, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1003, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1004, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1401, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1402, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1403, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1404, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1405, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1406, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1407, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1408, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1409, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140E, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140F, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1410, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1411, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1412, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1413, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1414, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1415, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1416, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1417, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1418, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1419, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141E, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141F, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1420, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1421, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1422, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1423, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1424, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1425, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1426, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1427, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1428, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1429, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x142A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x142B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x142C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x142D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x142E, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x142F, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1430, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1431, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1432, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1433, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1434, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1435, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1436, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1437, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1438, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1439, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143E, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143F, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage", -		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_init, +		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init,  		0),  /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */ diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 964ff22bf28..aeb00fc2d3b 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -27,6 +27,7 @@  #include <linux/pci.h>  #include <linux/uaccess.h>  #include <linux/vfio.h> +#include <linux/slab.h>  #include "vfio_pci_private.h" diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 3639371fa69..a96509187de 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -22,6 +22,7 @@  #include <linux/vfio.h>  #include <linux/wait.h>  #include <linux/workqueue.h> +#include <linux/slab.h>  #include "vfio_pci_private.h" diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 959b1cd89e6..ec6fb3fa59b 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -339,7 +339,8 @@ static void handle_tx(struct vhost_net *net)  				msg.msg_controllen = 0;  				ubufs = NULL;  			} else { -				struct ubuf_info *ubuf = &vq->ubuf_info[head]; +				struct ubuf_info *ubuf; +				ubuf = vq->ubuf_info + vq->upend_idx;  				vq->heads[vq->upend_idx].len =  					VHOST_DMA_IN_PROGRESS; diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 9951297b242..2968b493465 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -60,6 +60,15 @@ enum {  	VHOST_SCSI_VQ_IO = 2,  }; +/* + * VIRTIO_RING_F_EVENT_IDX seems broken. Not sure the bug is in + * kernel but disabling it helps. + * TODO: debug and remove the workaround. + */ +enum { +	VHOST_SCSI_FEATURES = VHOST_FEATURES & (~VIRTIO_RING_F_EVENT_IDX) +}; +  #define VHOST_SCSI_MAX_TARGET	256  #define VHOST_SCSI_MAX_VQ	128 @@ -850,7 +859,7 @@ static int vhost_scsi_clear_endpoint(  	for (index = 0; index < vs->dev.nvqs; ++index) {  		if (!vhost_vq_access_ok(&vs->vqs[index])) {  			ret = -EFAULT; -			goto err; +			goto err_dev;  		}  	}  	for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { @@ -860,10 +869,11 @@ static int vhost_scsi_clear_endpoint(  		if (!tv_tpg)  			continue; +		mutex_lock(&tv_tpg->tv_tpg_mutex);  		tv_tport = tv_tpg->tport;  		if (!tv_tport) {  			ret = -ENODEV; -			goto err; +			goto err_tpg;  		}  		if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) { @@ -872,16 +882,19 @@ static int vhost_scsi_clear_endpoint(  				tv_tport->tport_name, tv_tpg->tport_tpgt,  				t->vhost_wwpn, t->vhost_tpgt);  			ret = -EINVAL; -			goto err; +			goto err_tpg;  		}  		tv_tpg->tv_tpg_vhost_count--;  		vs->vs_tpg[target] = NULL;  		vs->vs_endpoint = false; +		mutex_unlock(&tv_tpg->tv_tpg_mutex);  	}  	mutex_unlock(&vs->dev.mutex);  	return 0; -err: +err_tpg: +	mutex_unlock(&tv_tpg->tv_tpg_mutex); +err_dev:  	mutex_unlock(&vs->dev.mutex);  	return ret;  } @@ -937,11 +950,12 @@ static void vhost_scsi_flush(struct vhost_scsi *vs)  	for (i = 0; i < VHOST_SCSI_MAX_VQ; i++)  		vhost_scsi_flush_vq(vs, i); +	vhost_work_flush(&vs->dev, &vs->vs_completion_work);  }  static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)  { -	if (features & ~VHOST_FEATURES) +	if (features & ~VHOST_SCSI_FEATURES)  		return -EOPNOTSUPP;  	mutex_lock(&vs->dev.mutex); @@ -987,7 +1001,7 @@ static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,  			return -EFAULT;  		return 0;  	case VHOST_GET_FEATURES: -		features = VHOST_FEATURES; +		features = VHOST_SCSI_FEATURES;  		if (copy_to_user(featurep, &features, sizeof features))  			return -EFAULT;  		return 0; diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 12cf5f31ee8..c1a2914447e 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -34,6 +34,77 @@  #define ATMEL_LCDC_DMA_BURST_LEN	8	/* words */  #define ATMEL_LCDC_FIFO_SIZE		512	/* words */ +struct atmel_lcdfb_config { +	bool have_alt_pixclock; +	bool have_hozval; +	bool have_intensity_bit; +}; + +static struct atmel_lcdfb_config at91sam9261_config = { +	.have_hozval		= true, +	.have_intensity_bit	= true, +}; + +static struct atmel_lcdfb_config at91sam9263_config = { +	.have_intensity_bit	= true, +}; + +static struct atmel_lcdfb_config at91sam9g10_config = { +	.have_hozval		= true, +}; + +static struct atmel_lcdfb_config at91sam9g45_config = { +	.have_alt_pixclock	= true, +}; + +static struct atmel_lcdfb_config at91sam9g45es_config = { +}; + +static struct atmel_lcdfb_config at91sam9rl_config = { +	.have_intensity_bit	= true, +}; + +static struct atmel_lcdfb_config at32ap_config = { +	.have_hozval		= true, +}; + +static const struct platform_device_id atmel_lcdfb_devtypes[] = { +	{ +		.name = "at91sam9261-lcdfb", +		.driver_data = (unsigned long)&at91sam9261_config, +	}, { +		.name = "at91sam9263-lcdfb", +		.driver_data = (unsigned long)&at91sam9263_config, +	}, { +		.name = "at91sam9g10-lcdfb", +		.driver_data = (unsigned long)&at91sam9g10_config, +	}, { +		.name = "at91sam9g45-lcdfb", +		.driver_data = (unsigned long)&at91sam9g45_config, +	}, { +		.name = "at91sam9g45es-lcdfb", +		.driver_data = (unsigned long)&at91sam9g45es_config, +	}, { +		.name = "at91sam9rl-lcdfb", +		.driver_data = (unsigned long)&at91sam9rl_config, +	}, { +		.name = "at32ap-lcdfb", +		.driver_data = (unsigned long)&at32ap_config, +	}, { +		/* terminator */ +	} +}; + +static struct atmel_lcdfb_config * +atmel_lcdfb_get_config(struct platform_device *pdev) +{ +	unsigned long data; + +	data = platform_get_device_id(pdev)->driver_data; + +	return (struct atmel_lcdfb_config *)data; +} +  #if defined(CONFIG_ARCH_AT91)  #define	ATMEL_LCDFB_FBINFO_DEFAULT	(FBINFO_DEFAULT \  					 | FBINFO_PARTIAL_PAN_OK \ @@ -193,14 +264,16 @@ static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = {  	.accel		= FB_ACCEL_NONE,  }; -static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2) +static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo, +							unsigned long xres)  { +	unsigned long lcdcon2;  	unsigned long value; -	if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10() -		|| cpu_is_at32ap7000())) +	if (!sinfo->config->have_hozval)  		return xres; +	lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2);  	value = xres;  	if ((lcdcon2 & ATMEL_LCDC_DISTYPE) != ATMEL_LCDC_DISTYPE_TFT) {  		/* STN display */ @@ -422,17 +495,22 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,  			= var->bits_per_pixel;  		break;  	case 16: +		/* Older SOCs use IBGR:555 rather than BGR:565. */ +		if (sinfo->config->have_intensity_bit) +			var->green.length = 5; +		else +			var->green.length = 6; +  		if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { -			/* RGB:565 mode */ -			var->red.offset = 11; +			/* RGB:5X5 mode */ +			var->red.offset = var->green.length + 5;  			var->blue.offset = 0;  		} else { -			/* BGR:565 mode */ +			/* BGR:5X5 mode */  			var->red.offset = 0; -			var->blue.offset = 11; +			var->blue.offset = var->green.length + 5;  		}  		var->green.offset = 5; -		var->green.length = 6;  		var->red.length = var->blue.length = 5;  		break;  	case 32: @@ -526,7 +604,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)  	/* Now, the LCDC core... */  	/* Set pixel clock */ -	if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es()) +	if (sinfo->config->have_alt_pixclock)  		pix_factor = 1;  	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; @@ -586,8 +664,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)  	lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value);  	/* Horizontal value (aka line size) */ -	hozval_linesz = compute_hozval(info->var.xres, -					lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2)); +	hozval_linesz = compute_hozval(sinfo, info->var.xres);  	/* Display size */  	value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET; @@ -679,8 +756,7 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red,  	case FB_VISUAL_PSEUDOCOLOR:  		if (regno < 256) { -			if (cpu_is_at91sam9261() || cpu_is_at91sam9263() -			    || cpu_is_at91sam9rl()) { +			if (sinfo->config->have_intensity_bit) {  				/* old style I+BGR:555 */  				val  = ((red   >> 11) & 0x001f);  				val |= ((green >>  6) & 0x03e0); @@ -817,15 +893,13 @@ static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo)  static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo)  { -	if (sinfo->bus_clk) -		clk_enable(sinfo->bus_clk); +	clk_enable(sinfo->bus_clk);  	clk_enable(sinfo->lcdc_clk);  }  static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo)  { -	if (sinfo->bus_clk) -		clk_disable(sinfo->bus_clk); +	clk_disable(sinfo->bus_clk);  	clk_disable(sinfo->lcdc_clk);  } @@ -870,6 +944,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)  	}  	sinfo->info = info;  	sinfo->pdev = pdev; +	sinfo->config = atmel_lcdfb_get_config(pdev); +	if (!sinfo->config) +		goto free_info;  	strcpy(info->fix.id, sinfo->pdev->name);  	info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; @@ -880,13 +957,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)  	info->fix = atmel_lcdfb_fix;  	/* Enable LCDC Clocks */ -	if (cpu_is_at91sam9261() || cpu_is_at91sam9g10() -	 || cpu_is_at32ap7000()) { -		sinfo->bus_clk = clk_get(dev, "hck1"); -		if (IS_ERR(sinfo->bus_clk)) { -			ret = PTR_ERR(sinfo->bus_clk); -			goto free_info; -		} +	sinfo->bus_clk = clk_get(dev, "hclk"); +	if (IS_ERR(sinfo->bus_clk)) { +		ret = PTR_ERR(sinfo->bus_clk); +		goto free_info;  	}  	sinfo->lcdc_clk = clk_get(dev, "lcdc_clk");  	if (IS_ERR(sinfo->lcdc_clk)) { @@ -1047,8 +1121,7 @@ stop_clk:  	atmel_lcdfb_stop_clock(sinfo);  	clk_put(sinfo->lcdc_clk);  put_bus_clk: -	if (sinfo->bus_clk) -		clk_put(sinfo->bus_clk); +	clk_put(sinfo->bus_clk);  free_info:  	framebuffer_release(info);  out: @@ -1073,8 +1146,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev)  	unregister_framebuffer(info);  	atmel_lcdfb_stop_clock(sinfo);  	clk_put(sinfo->lcdc_clk); -	if (sinfo->bus_clk) -		clk_put(sinfo->bus_clk); +	clk_put(sinfo->bus_clk);  	fb_dealloc_cmap(&info->cmap);  	free_irq(sinfo->irq_base, info);  	iounmap(sinfo->mmio); @@ -1143,7 +1215,7 @@ static struct platform_driver atmel_lcdfb_driver = {  	.remove		= __exit_p(atmel_lcdfb_remove),  	.suspend	= atmel_lcdfb_suspend,  	.resume		= atmel_lcdfb_resume, - +	.id_table	= atmel_lcdfb_devtypes,  	.driver		= {  		.name	= "atmel_lcdfb",  		.owner	= THIS_MODULE, diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index 3f2519d3071..e06cd5d90c9 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -23,6 +23,7 @@  #include <linux/slab.h>  #include <linux/clk.h>  #include <linux/fb.h> +#include <linux/io.h>  #include <linux/platform_data/video-ep93xx.h> diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 755556ca5b2..45169cbaba6 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -169,6 +169,7 @@ struct mxsfb_info {  	unsigned dotclk_delay;  	const struct mxsfb_devdata *devdata;  	int mapped; +	u32 sync;  };  #define mxsfb_is_v3(host) (host->devdata->ipversion == 3) @@ -456,9 +457,9 @@ static int mxsfb_set_par(struct fb_info *fb_info)  		vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;  	if (fb_info->var.sync & FB_SYNC_VERT_HIGH_ACT)  		vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH; -	if (fb_info->var.sync & FB_SYNC_DATA_ENABLE_HIGH_ACT) +	if (host->sync & MXSFB_SYNC_DATA_ENABLE_HIGH_ACT)  		vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH; -	if (fb_info->var.sync & FB_SYNC_DOTCLK_FAILING_ACT) +	if (host->sync & MXSFB_SYNC_DOTCLK_FAILING_ACT)  		vdctrl0 |= VDCTRL0_DOTCLK_ACT_FAILING;  	writel(vdctrl0, host->base + LCDC_VDCTRL0); @@ -861,6 +862,8 @@ static int mxsfb_probe(struct platform_device *pdev)  	INIT_LIST_HEAD(&fb_info->modelist); +	host->sync = pdata->sync; +  	ret = mxsfb_init_fbinfo(host);  	if (ret != 0)  		goto error_init_fb; diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c index ed4cad87fbc..4a5f2cd3d3b 100644 --- a/drivers/video/omap/lcd_ams_delta.c +++ b/drivers/video/omap/lcd_ams_delta.c @@ -27,6 +27,7 @@  #include <linux/lcd.h>  #include <linux/gpio.h> +#include <mach/hardware.h>  #include <mach/board-ams-delta.h>  #include "omapfb.h" diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c index 3aa62da8919..7fbe04bce0e 100644 --- a/drivers/video/omap/lcd_osk.c +++ b/drivers/video/omap/lcd_osk.c @@ -24,7 +24,10 @@  #include <linux/platform_device.h>  #include <asm/gpio.h> + +#include <mach/hardware.h>  #include <mach/mux.h> +  #include "omapfb.h"  static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index e31f5b33b50..d40612c31a9 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -32,6 +32,8 @@  #include <linux/omap-dma.h> +#include <mach/hardware.h> +  #include "omapfb.h"  #include "lcdc.h" diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 6b6643911d2..048c98381ef 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -63,6 +63,9 @@ struct tpo_td043_device {  	u32 power_on_resume:1;  }; +/* used to pass spi_device from SPI to DSS portion of the driver */ +static struct tpo_td043_device *g_tpo_td043; +  static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)  {  	struct spi_message	m; @@ -403,7 +406,7 @@ static void tpo_td043_disable(struct omap_dss_device *dssdev)  static int tpo_td043_probe(struct omap_dss_device *dssdev)  { -	struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); +	struct tpo_td043_device *tpo_td043 = g_tpo_td043;  	int nreset_gpio = dssdev->reset_gpio;  	int ret = 0; @@ -440,6 +443,8 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)  	if (ret)  		dev_warn(&dssdev->dev, "failed to create sysfs files\n"); +	dev_set_drvdata(&dssdev->dev, tpo_td043); +  	return 0;  fail_gpio_req: @@ -505,6 +510,9 @@ static int tpo_td043_spi_probe(struct spi_device *spi)  		return -ENODEV;  	} +	if (g_tpo_td043 != NULL) +		return -EBUSY; +  	spi->bits_per_word = 16;  	spi->mode = SPI_MODE_0; @@ -521,7 +529,7 @@ static int tpo_td043_spi_probe(struct spi_device *spi)  	tpo_td043->spi = spi;  	tpo_td043->nreset_gpio = dssdev->reset_gpio;  	dev_set_drvdata(&spi->dev, tpo_td043); -	dev_set_drvdata(&dssdev->dev, tpo_td043); +	g_tpo_td043 = tpo_td043;  	omap_dss_register_driver(&tpo_td043_driver); @@ -534,6 +542,7 @@ static int tpo_td043_spi_remove(struct spi_device *spi)  	omap_dss_unregister_driver(&tpo_td043_driver);  	kfree(tpo_td043); +	g_tpo_td043 = NULL;  	return 0;  } diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index d7d66ef5cb5..7f791aeda4d 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -202,12 +202,10 @@ static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = {  static const enum omap_dss_output_id omap4_dss_supported_outputs[] = {  	/* OMAP_DSS_CHANNEL_LCD */ -	OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | -	OMAP_DSS_OUTPUT_DSI1, +	OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1,  	/* OMAP_DSS_CHANNEL_DIGIT */ -	OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI | -	OMAP_DSS_OUTPUT_DPI, +	OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI,  	/* OMAP_DSS_CHANNEL_LCD2 */  	OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index d39dfa4cc23..46d97014342 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -47,11 +47,13 @@ static u8 w1_gpio_read_bit(void *data)  	return gpio_get_value(pdata->pin) ? 1 : 0;  } +#if defined(CONFIG_OF)  static struct of_device_id w1_gpio_dt_ids[] = {  	{ .compatible = "w1-gpio" },  	{}  };  MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); +#endif  static int w1_gpio_probe_dt(struct platform_device *pdev)  { @@ -158,7 +160,7 @@ static int w1_gpio_probe(struct platform_device *pdev)  	return err;  } -static int __exit w1_gpio_remove(struct platform_device *pdev) +static int w1_gpio_remove(struct platform_device *pdev)  {  	struct w1_bus_master *master = platform_get_drvdata(pdev);  	struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; @@ -210,7 +212,7 @@ static struct platform_driver w1_gpio_driver = {  		.of_match_table = of_match_ptr(w1_gpio_dt_ids),  	},  	.probe = w1_gpio_probe, -	.remove	= __exit_p(w1_gpio_remove), +	.remove	= w1_gpio_remove,  	.suspend = w1_gpio_suspend,  	.resume = w1_gpio_resume,  }; diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 7994d933f04..7ce277d2bb6 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -924,7 +924,8 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb  			tmp64 = (triplet_ret >> 2);  			rn |= (tmp64 << i); -			if (kthread_should_stop()) { +			/* ensure we're called from kthread and not by netlink callback */ +			if (!dev->priv && kthread_should_stop()) {  				mutex_unlock(&dev->bus_mutex);  				dev_dbg(&dev->dev, "Abort w1_search\n");  				return; diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index e3b8f757d2d..0e9d8c479c3 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -40,13 +40,12 @@  #include "sp5100_tco.h"  /* Module and version information */ -#define TCO_VERSION "0.03" +#define TCO_VERSION "0.05"  #define TCO_MODULE_NAME "SP5100 TCO timer"  #define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION  /* internal variables */  static u32 tcobase_phys; -static u32 resbase_phys;  static u32 tco_wdt_fired;  static void __iomem *tcobase;  static unsigned int pm_iobase; @@ -54,10 +53,6 @@ static DEFINE_SPINLOCK(tco_lock);	/* Guards the hardware */  static unsigned long timer_alive;  static char tco_expect_close;  static struct pci_dev *sp5100_tco_pci; -static struct resource wdt_res = { -	.name = "Watchdog Timer", -	.flags = IORESOURCE_MEM, -};  /* the watchdog platform device */  static struct platform_device *sp5100_tco_platform_device; @@ -75,12 +70,6 @@ module_param(nowayout, bool, 0);  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."  		" (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static unsigned int force_addr; -module_param(force_addr, uint, 0); -MODULE_PARM_DESC(force_addr, "Force the use of specified MMIO address." -		" ONLY USE THIS PARAMETER IF YOU REALLY KNOW" -		" WHAT YOU ARE DOING (default=none)"); -  /*   * Some TCO specific functions   */ @@ -176,39 +165,6 @@ static void tco_timer_enable(void)  	}  } -static void tco_timer_disable(void) -{ -	int val; - -	if (sp5100_tco_pci->revision >= 0x40) { -		/* For SB800 or later */ -		/* Enable watchdog decode bit and Disable watchdog timer */ -		outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG); -		val = inb(SB800_IO_PM_DATA_REG); -		val |= SB800_PCI_WATCHDOG_DECODE_EN; -		val |= SB800_PM_WATCHDOG_DISABLE; -		outb(val, SB800_IO_PM_DATA_REG); -	} else { -		/* For SP5100 or SB7x0 */ -		/* Enable watchdog decode bit */ -		pci_read_config_dword(sp5100_tco_pci, -				      SP5100_PCI_WATCHDOG_MISC_REG, -				      &val); - -		val |= SP5100_PCI_WATCHDOG_DECODE_EN; - -		pci_write_config_dword(sp5100_tco_pci, -				       SP5100_PCI_WATCHDOG_MISC_REG, -				       val); - -		/* Disable Watchdog timer */ -		outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); -		val = inb(SP5100_IO_PM_DATA_REG); -		val |= SP5100_PM_WATCHDOG_DISABLE; -		outb(val, SP5100_IO_PM_DATA_REG); -	} -} -  /*   *	/dev/watchdog handling   */ @@ -361,7 +317,7 @@ static unsigned char sp5100_tco_setupdevice(void)  {  	struct pci_dev *dev = NULL;  	const char *dev_name = NULL; -	u32 val, tmp_val; +	u32 val;  	u32 index_reg, data_reg, base_addr;  	/* Match the PCI device */ @@ -459,63 +415,8 @@ static unsigned char sp5100_tco_setupdevice(void)  	} else  		pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val); -	/* -	 * Lastly re-programming the watchdog timer MMIO address, -	 * This method is a last resort... -	 * -	 * Before re-programming, to ensure that the watchdog timer -	 * is disabled, disable the watchdog timer. -	 */ -	tco_timer_disable(); - -	if (force_addr) { -		/* -		 * Force the use of watchdog timer MMIO address, and aligned to -		 * 8byte boundary. -		 */ -		force_addr &= ~0x7; -		val = force_addr; - -		pr_info("Force the use of 0x%04x as MMIO address\n", val); -	} else { -		/* -		 * Get empty slot into the resource tree for watchdog timer. -		 */ -		if (allocate_resource(&iomem_resource, -				      &wdt_res, -				      SP5100_WDT_MEM_MAP_SIZE, -				      0xf0000000, -				      0xfffffff8, -				      0x8, -				      NULL, -				      NULL)) { -			pr_err("MMIO allocation failed\n"); -			goto unreg_region; -		} - -		val = resbase_phys = wdt_res.start; -		pr_debug("Got 0x%04x from resource tree\n", val); -	} - -	/* Restore to the low three bits */ -	outb(base_addr+0, index_reg); -	tmp_val = val | (inb(data_reg) & 0x7); - -	/* Re-programming the watchdog timer base address */ -	outb(base_addr+0, index_reg); -	outb((tmp_val >>  0) & 0xff, data_reg); -	outb(base_addr+1, index_reg); -	outb((tmp_val >>  8) & 0xff, data_reg); -	outb(base_addr+2, index_reg); -	outb((tmp_val >> 16) & 0xff, data_reg); -	outb(base_addr+3, index_reg); -	outb((tmp_val >> 24) & 0xff, data_reg); - -	if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, -								   dev_name)) { -		pr_err("MMIO address 0x%04x already in use\n", val); -		goto unreg_resource; -	} +	pr_notice("failed to find MMIO address, giving up.\n"); +	goto  unreg_region;  setup_wdt:  	tcobase_phys = val; @@ -555,9 +456,6 @@ setup_wdt:  unreg_mem_region:  	release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); -unreg_resource: -	if (resbase_phys) -		release_resource(&wdt_res);  unreg_region:  	release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);  exit: @@ -567,7 +465,6 @@ exit:  static int sp5100_tco_init(struct platform_device *dev)  {  	int ret; -	char addr_str[16];  	/*  	 * Check whether or not the hardware watchdog is there. If found, then @@ -599,23 +496,14 @@ static int sp5100_tco_init(struct platform_device *dev)  	clear_bit(0, &timer_alive);  	/* Show module parameters */ -	if (force_addr == tcobase_phys) -		/* The force_addr is vaild */ -		sprintf(addr_str, "0x%04x", force_addr); -	else -		strcpy(addr_str, "none"); - -	pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d, " -		"force_addr=%s)\n", -		tcobase, heartbeat, nowayout, addr_str); +	pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", +		tcobase, heartbeat, nowayout);  	return 0;  exit:  	iounmap(tcobase);  	release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); -	if (resbase_phys) -		release_resource(&wdt_res);  	release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);  	return ret;  } @@ -630,8 +518,6 @@ static void sp5100_tco_cleanup(void)  	misc_deregister(&sp5100_tco_miscdev);  	iounmap(tcobase);  	release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); -	if (resbase_phys) -		release_resource(&wdt_res);  	release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);  } diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index 71594a0c14b..2b28c00da0d 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -57,7 +57,7 @@  #define SB800_PM_WATCHDOG_DISABLE	(1 << 2)  #define SB800_PM_WATCHDOG_SECOND_RES	(3 << 0)  #define SB800_ACPI_MMIO_DECODE_EN	(1 << 0) -#define SB800_ACPI_MMIO_SEL		(1 << 2) +#define SB800_ACPI_MMIO_SEL		(1 << 1)  #define SB800_PM_WDT_MMIO_OFFSET	0xB00 diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 5a32232cf7c..67af155cf60 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -182,7 +182,7 @@ config XEN_PRIVCMD  config XEN_STUB  	bool "Xen stub drivers" -	depends on XEN && X86_64 +	depends on XEN && X86_64 && BROKEN  	default n  	help  	  Allow kernel to install stub drivers, to reserve space for Xen drivers, diff --git a/drivers/xen/events.c b/drivers/xen/events.c index d17aa41a904..aa85881d17b 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -403,11 +403,23 @@ static void unmask_evtchn(int port)  	if (unlikely((cpu != cpu_from_evtchn(port))))  		do_hypercall = 1; -	else +	else { +		/* +		 * Need to clear the mask before checking pending to +		 * avoid a race with an event becoming pending. +		 * +		 * EVTCHNOP_unmask will only trigger an upcall if the +		 * mask bit was set, so if a hypercall is needed +		 * remask the event. +		 */ +		sync_clear_bit(port, BM(&s->evtchn_mask[0]));  		evtchn_pending = sync_test_bit(port, BM(&s->evtchn_pending[0])); -	if (unlikely(evtchn_pending && xen_hvm_domain())) -		do_hypercall = 1; +		if (unlikely(evtchn_pending && xen_hvm_domain())) { +			sync_set_bit(port, BM(&s->evtchn_mask[0])); +			do_hypercall = 1; +		} +	}  	/* Slow path (hypercall) if this is a non-local port or if this is  	 * an hvm domain and an event is pending (hvm domains don't have @@ -418,8 +430,6 @@ static void unmask_evtchn(int port)  	} else {  		struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu); -		sync_clear_bit(port, BM(&s->evtchn_mask[0])); -  		/*  		 * The following is basically the equivalent of  		 * 'hw_resend_irq'. Just like a real IO-APIC we 'lose diff --git a/drivers/xen/fallback.c b/drivers/xen/fallback.c index 0ef7c4d40f8..b04fb64c5a9 100644 --- a/drivers/xen/fallback.c +++ b/drivers/xen/fallback.c @@ -44,7 +44,7 @@ int xen_event_channel_op_compat(int cmd, void *arg)  }  EXPORT_SYMBOL_GPL(xen_event_channel_op_compat); -int HYPERVISOR_physdev_op_compat(int cmd, void *arg) +int xen_physdev_op_compat(int cmd, void *arg)  {  	struct physdev_op op;  	int rc; @@ -78,3 +78,4 @@ int HYPERVISOR_physdev_op_compat(int cmd, void *arg)  	return rc;  } +EXPORT_SYMBOL_GPL(xen_physdev_op_compat); diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 316df65163c..90e34ac7e52 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -500,16 +500,19 @@ static int __init xen_acpi_processor_init(void)  	(void)acpi_processor_preregister_performance(acpi_perf_data);  	for_each_possible_cpu(i) { +		struct acpi_processor *pr;  		struct acpi_processor_performance *perf; +		pr = per_cpu(processors, i);  		perf = per_cpu_ptr(acpi_perf_data, i); -		rc = acpi_processor_register_performance(perf, i); +		if (!pr) +			continue; + +		pr->performance = perf; +		rc = acpi_processor_get_performance_info(pr);  		if (rc)  			goto err_out;  	} -	rc = acpi_processor_notify_smm(THIS_MODULE); -	if (rc) -		goto err_unregister;  	for_each_possible_cpu(i) {  		struct acpi_processor *_pr; diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index 9204126f156..a2278ba7fb2 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -17,6 +17,7 @@  #include <xen/events.h>  #include <asm/xen/pci.h>  #include <asm/xen/hypervisor.h> +#include <xen/interface/physdev.h>  #include "pciback.h"  #include "conf_space.h"  #include "conf_space_quirks.h" @@ -85,37 +86,52 @@ static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)  static void pcistub_device_release(struct kref *kref)  {  	struct pcistub_device *psdev; +	struct pci_dev *dev;  	struct xen_pcibk_dev_data *dev_data;  	psdev = container_of(kref, struct pcistub_device, kref); -	dev_data = pci_get_drvdata(psdev->dev); +	dev = psdev->dev; +	dev_data = pci_get_drvdata(dev); -	dev_dbg(&psdev->dev->dev, "pcistub_device_release\n"); +	dev_dbg(&dev->dev, "pcistub_device_release\n"); -	xen_unregister_device_domain_owner(psdev->dev); +	xen_unregister_device_domain_owner(dev);  	/* Call the reset function which does not take lock as this  	 * is called from "unbind" which takes a device_lock mutex.  	 */ -	__pci_reset_function_locked(psdev->dev); -	if (pci_load_and_free_saved_state(psdev->dev, -					  &dev_data->pci_saved_state)) { -		dev_dbg(&psdev->dev->dev, "Could not reload PCI state\n"); -	} else -		pci_restore_state(psdev->dev); +	__pci_reset_function_locked(dev); +	if (pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state)) +		dev_dbg(&dev->dev, "Could not reload PCI state\n"); +	else +		pci_restore_state(dev); + +	if (pci_find_capability(dev, PCI_CAP_ID_MSIX)) { +		struct physdev_pci_device ppdev = { +			.seg = pci_domain_nr(dev->bus), +			.bus = dev->bus->number, +			.devfn = dev->devfn +		}; +		int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix, +						&ppdev); + +		if (err) +			dev_warn(&dev->dev, "MSI-X release failed (%d)\n", +				 err); +	}  	/* Disable the device */ -	xen_pcibk_reset_device(psdev->dev); +	xen_pcibk_reset_device(dev);  	kfree(dev_data); -	pci_set_drvdata(psdev->dev, NULL); +	pci_set_drvdata(dev, NULL);  	/* Clean-up the device */ -	xen_pcibk_config_free_dyn_fields(psdev->dev); -	xen_pcibk_config_free_dev(psdev->dev); +	xen_pcibk_config_free_dyn_fields(dev); +	xen_pcibk_config_free_dev(dev); -	psdev->dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED; -	pci_dev_put(psdev->dev); +	dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED; +	pci_dev_put(dev);  	kfree(psdev);  } @@ -355,6 +371,19 @@ static int pcistub_init_device(struct pci_dev *dev)  	if (err)  		goto config_release; +	if (pci_find_capability(dev, PCI_CAP_ID_MSIX)) { +		struct physdev_pci_device ppdev = { +			.seg = pci_domain_nr(dev->bus), +			.bus = dev->bus->number, +			.devfn = dev->devfn +		}; + +		err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev); +		if (err) +			dev_err(&dev->dev, "MSI-X preparation failed (%d)\n", +				err); +	} +  	/* We need the device active to save the state. */  	dev_dbg(&dev->dev, "save state of device\n");  	pci_save_state(dev); diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index 37c1f825f51..b98cf0c3572 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -113,7 +113,8 @@ void xen_pcibk_reset_device(struct pci_dev *dev)  		if (dev->msi_enabled)  			pci_disable_msi(dev);  #endif -		pci_disable_device(dev); +		if (pci_is_enabled(dev)) +			pci_disable_device(dev);  		pci_write_config_word(dev, PCI_COMMAND, 0); diff --git a/drivers/xen/xen-stub.c b/drivers/xen/xen-stub.c index d85e411cbf8..bbef194c5b0 100644 --- a/drivers/xen/xen-stub.c +++ b/drivers/xen/xen-stub.c @@ -25,7 +25,6 @@  #include <linux/export.h>  #include <linux/types.h>  #include <linux/acpi.h> -#include <acpi/acpi_drivers.h>  #include <xen/acpi.h>  #ifdef CONFIG_ACPI  |