diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-03 11:36:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-03 11:36:27 -0700 |
commit | 1d1fdd95df681f0c065d90ffaafa215a0e8825e2 (patch) | |
tree | 19016e131bb5c7eb280a4cc8dff864ba36e53be4 /drivers | |
parent | b3b49114c80e799af8b08c0c6d1ff886ea843f03 (diff) | |
parent | 3cc1f95283a125cf54ccf1e25065321d4385133b (diff) | |
download | linux-1d1fdd95df681f0c065d90ffaafa215a0e8825e2.tar.bz2 |
Merge tag 'char-misc-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc patches from Greg KH:
"Here is the big char/misc driver pull request for 3.12-rc1
Lots of driver updates all over the char/misc tree, full details in
the shortlog"
* tag 'char-misc-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (62 commits)
drivers: uio: Kconfig: add MMU dependancy for UIO
drivers: uio: Add driver for Humusoft MF624 DAQ PCI card
drivers: uio_pdrv_genirq: use dev_get_platdata()
drivers: uio_pruss: use dev_get_platdata()
drivers: uio_dmem_genirq: use dev_get_platdata()
drivers: parport: Kconfig: exclude h8300 for PARPORT_PC
drivers: misc: ti-st: fix potential race if st_kim_start fails
Drivers: hv: vmbus: Do not attempt to negoatiate a new version prematurely
misc: vmw_balloon: Remove braces to fix build for clang.
Drivers: hv: vmbus: Fix a bug in the handling of channel offers
vme: vme_ca91cx42.c: fix to pass correct device identity to free_irq()
VMCI: Add support for virtual IOMMU
VMCI: Remove non-blocking/pinned queuepair support
uio: uio_pruss: remove unnecessary platform_set_drvdata()
parport: amiga: remove unnecessary platform_set_drvdata()
vme: vme_vmivme7805.c: add missing __iomem annotation
vme: vme_ca91cx42.c: add missing __iomem annotation
vme: vme_tsi148.c: add missing __iomem annotation
drivers/misc/hpilo: Correct panic when an AUX iLO is detected
uio: drop unused vma_count member in uio_device struct
...
Diffstat (limited to 'drivers')
55 files changed, 976 insertions, 576 deletions
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 5c5cc00ebb07..d39cca659a3f 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -1182,14 +1182,14 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id) } count++; - if (gis & (BIT1 + BIT0)) { + if (gis & (BIT1 | BIT0)) { isr = read_reg16(info, CHB + ISR); if (isr & IRQ_DCD) dcd_change(info, tty); if (isr & IRQ_CTS) cts_change(info, tty); } - if (gis & (BIT3 + BIT2)) + if (gis & (BIT3 | BIT2)) { isr = read_reg16(info, CHA + ISR); if (isr & IRQ_TIMER) { @@ -1210,7 +1210,7 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id) if (isr & IRQ_RXTIME) { issue_command(info, CHA, CMD_RXFIFO_READ); } - if (isr & (IRQ_RXEOM + IRQ_RXFIFO)) { + if (isr & (IRQ_RXEOM | IRQ_RXFIFO)) { if (info->params.mode == MGSL_MODE_HDLC) rx_ready_hdlc(info, isr & IRQ_RXEOM); else @@ -3031,11 +3031,11 @@ static void loopback_enable(MGSLPC_INFO *info) unsigned char val; /* CCR1:02..00 CM[2..0] Clock Mode = 111 (clock mode 7) */ - val = read_reg(info, CHA + CCR1) | (BIT2 + BIT1 + BIT0); + val = read_reg(info, CHA + CCR1) | (BIT2 | BIT1 | BIT0); write_reg(info, CHA + CCR1, val); /* CCR2:04 SSEL Clock source select, 1=submode b */ - val = read_reg(info, CHA + CCR2) | (BIT4 + BIT5); + val = read_reg(info, CHA + CCR2) | (BIT4 | BIT5); write_reg(info, CHA + CCR2, val); /* set LinkSpeed if available, otherwise default to 2Mbps */ @@ -3125,10 +3125,10 @@ static void hdlc_mode(MGSLPC_INFO *info) val |= BIT4; break; // FM0 case HDLC_ENCODING_BIPHASE_MARK: - val |= BIT4 + BIT2; + val |= BIT4 | BIT2; break; // FM1 case HDLC_ENCODING_BIPHASE_LEVEL: - val |= BIT4 + BIT3; + val |= BIT4 | BIT3; break; // Manchester } write_reg(info, CHA + CCR0, val); @@ -3185,7 +3185,7 @@ static void hdlc_mode(MGSLPC_INFO *info) */ val = 0x00; if (info->params.crc_type == HDLC_CRC_NONE) - val |= BIT2 + BIT1; + val |= BIT2 | BIT1; if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE) val |= BIT5; switch (info->params.preamble_length) @@ -3197,7 +3197,7 @@ static void hdlc_mode(MGSLPC_INFO *info) val |= BIT6; break; case HDLC_PREAMBLE_LENGTH_64BITS: - val |= BIT7 + BIT6; + val |= BIT7 | BIT6; break; } write_reg(info, CHA + CCR3, val); @@ -3264,8 +3264,8 @@ static void hdlc_mode(MGSLPC_INFO *info) clear_reg_bits(info, CHA + PVR, BIT3); irq_enable(info, CHA, - IRQ_RXEOM + IRQ_RXFIFO + IRQ_ALLSENT + - IRQ_UNDERRUN + IRQ_TXFIFO); + IRQ_RXEOM | IRQ_RXFIFO | IRQ_ALLSENT | + IRQ_UNDERRUN | IRQ_TXFIFO); issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET); wait_command_complete(info, CHA); read_reg16(info, CHA + ISR); /* clear pending IRQs */ @@ -3582,8 +3582,8 @@ static void async_mode(MGSLPC_INFO *info) } else clear_reg_bits(info, CHA + PVR, BIT3); irq_enable(info, CHA, - IRQ_RXEOM + IRQ_RXFIFO + IRQ_BREAK_ON + IRQ_RXTIME + - IRQ_ALLSENT + IRQ_TXFIFO); + IRQ_RXEOM | IRQ_RXFIFO | IRQ_BREAK_ON | IRQ_RXTIME | + IRQ_ALLSENT | IRQ_TXFIFO); issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET); wait_command_complete(info, CHA); read_reg16(info, CHA + ISR); /* clear pending IRQs */ diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 63f454e20576..f1d54a3985bd 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -14,6 +14,10 @@ if EXTCON comment "Extcon Device Drivers" +config OF_EXTCON + def_tristate y + depends on OF + config EXTCON_GPIO tristate "GPIO extcon support" depends on GPIOLIB diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 540e2c3a4431..759fdae46f95 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -2,6 +2,8 @@ # Makefile for external connector class (extcon) devices # +obj-$(CONFIG_OF_EXTCON) += of_extcon.o + obj-$(CONFIG_EXTCON) += extcon-class.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index d0233cd18ffa..5985807e52c9 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -87,7 +87,8 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data) { struct adc_jack_data *data = _data; - schedule_delayed_work(&data->handler, data->handling_delay); + queue_delayed_work(system_power_efficient_wq, + &data->handler, data->handling_delay); return IRQ_HANDLED; } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 7a1b4a7791ba..e55713083c78 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -890,8 +890,9 @@ static void arizona_micd_detect(struct work_struct *work) handled: if (info->detecting) - schedule_delayed_work(&info->micd_timeout_work, - msecs_to_jiffies(info->micd_timeout)); + queue_delayed_work(system_power_efficient_wq, + &info->micd_timeout_work, + msecs_to_jiffies(info->micd_timeout)); pm_runtime_mark_last_busy(info->dev); mutex_unlock(&info->lock); @@ -912,8 +913,9 @@ static irqreturn_t arizona_micdet(int irq, void *data) mutex_unlock(&info->lock); if (debounce) - schedule_delayed_work(&info->micd_detect_work, - msecs_to_jiffies(debounce)); + queue_delayed_work(system_power_efficient_wq, + &info->micd_detect_work, + msecs_to_jiffies(debounce)); else arizona_micd_detect(&info->micd_detect_work.work); @@ -967,12 +969,14 @@ static irqreturn_t arizona_jackdet(int irq, void *data) if (val == info->last_jackdet) { dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n"); if (cancelled_hp) - schedule_delayed_work(&info->hpdet_work, - msecs_to_jiffies(HPDET_DEBOUNCE)); + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); if (cancelled_mic) - schedule_delayed_work(&info->micd_timeout_work, - msecs_to_jiffies(info->micd_timeout)); + queue_delayed_work(system_power_efficient_wq, + &info->micd_timeout_work, + msecs_to_jiffies(info->micd_timeout)); goto out; } @@ -994,8 +998,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) arizona_start_mic(info); } else { - schedule_delayed_work(&info->hpdet_work, - msecs_to_jiffies(HPDET_DEBOUNCE)); + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); } regmap_update_bits(arizona->regmap, diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 18ccadef43fd..1446152bf2e9 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -602,7 +602,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) edev->dev->class = extcon_class; edev->dev->release = extcon_dev_release; - dev_set_name(edev->dev, "%s", edev->name ? edev->name : dev_name(dev)); + edev->name = edev->name ? edev->name : dev_name(dev); + dev_set_name(edev->dev, "%s", edev->name); if (edev->max_supported) { char buf[10]; diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 02bec32adde4..f874c30ddbff 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -56,7 +56,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) { struct gpio_extcon_data *extcon_data = dev_id; - schedule_delayed_work(&extcon_data->work, + queue_delayed_work(system_power_efficient_wq, &extcon_data->work, extcon_data->debounce_jiffies); return IRQ_HANDLED; } diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index b752a0ad7b63..89fdd05c5fd6 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -57,6 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; extcon_set_cable_state(&palmas_usb->edev, "USB", true); + dev_info(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious connect event detected\n"); @@ -65,6 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_cable_state(&palmas_usb->edev, "USB", false); + dev_info(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, "Spurious disconnect event detected\n"); @@ -84,28 +86,23 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) if (set & PALMAS_USB_ID_INT_SRC_ID_GND) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_CLR, - PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); + dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_GND); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_CLR, - PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && + (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { + palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; + extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } return IRQ_HANDLED; @@ -122,13 +119,17 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb) palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_GND); + PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | + PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); - palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); + if (palmas_usb->enable_vbus_detection) + palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); /* cold plug for host mode needs this delay */ - msleep(30); - palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); + if (palmas_usb->enable_id_detection) { + msleep(30); + palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); + } } static int palmas_usb_probe(struct platform_device *pdev) @@ -139,21 +140,25 @@ static int palmas_usb_probe(struct platform_device *pdev) struct palmas_usb *palmas_usb; int status; - if (node && !pdata) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - - if (!pdata) - return -ENOMEM; - - pdata->wakeup = of_property_read_bool(node, "ti,wakeup"); - } else if (!pdata) { - return -EINVAL; - } - palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); if (!palmas_usb) return -ENOMEM; + if (node && !pdata) { + palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup"); + palmas_usb->enable_id_detection = of_property_read_bool(node, + "ti,enable-id-detection"); + palmas_usb->enable_vbus_detection = of_property_read_bool(node, + "ti,enable-vbus-detection"); + } else { + palmas_usb->wakeup = true; + palmas_usb->enable_id_detection = true; + palmas_usb->enable_vbus_detection = true; + + if (pdata) + palmas_usb->wakeup = pdata->wakeup; + } + palmas->usb = palmas_usb; palmas_usb->palmas = palmas; @@ -168,11 +173,10 @@ static int palmas_usb_probe(struct platform_device *pdev) palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, PALMAS_VBUS_IRQ); - palmas_usb_wakeup(palmas, pdata->wakeup); + palmas_usb_wakeup(palmas, palmas_usb->wakeup); platform_set_drvdata(pdev, palmas_usb); - palmas_usb->edev.name = "palmas-usb"; palmas_usb->edev.supported_cable = palmas_extcon_cable; palmas_usb->edev.mutually_exclusive = mutually_exclusive; @@ -182,28 +186,36 @@ static int palmas_usb_probe(struct platform_device *pdev) return status; } - status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq, - NULL, palmas_id_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "palmas_usb_id", palmas_usb); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + if (palmas_usb->enable_id_detection) { + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->id_irq, + NULL, palmas_id_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas_usb_id", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->id_irq, status); - goto fail_extcon; + goto fail_extcon; + } } - status = devm_request_threaded_irq(palmas_usb->dev, - palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "palmas_usb_vbus", palmas_usb); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + if (palmas_usb->enable_vbus_detection) { + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->vbus_irq, NULL, + palmas_vbus_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas_usb_vbus", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->vbus_irq, status); - goto fail_extcon; + goto fail_extcon; + } } palmas_enable_irq(palmas_usb); - + device_set_wakeup_capable(&pdev->dev, true); return 0; fail_extcon: @@ -221,6 +233,39 @@ static int palmas_usb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int palmas_usb_suspend(struct device *dev) +{ + struct palmas_usb *palmas_usb = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + if (palmas_usb->enable_vbus_detection) + enable_irq_wake(palmas_usb->vbus_irq); + if (palmas_usb->enable_id_detection) + enable_irq_wake(palmas_usb->id_irq); + } + return 0; +} + +static int palmas_usb_resume(struct device *dev) +{ + struct palmas_usb *palmas_usb = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + if (palmas_usb->enable_vbus_detection) + disable_irq_wake(palmas_usb->vbus_irq); + if (palmas_usb->enable_id_detection) + disable_irq_wake(palmas_usb->id_irq); + } + return 0; +}; +#endif + +static const struct dev_pm_ops palmas_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_usb_suspend, + palmas_usb_resume) +}; + static struct of_device_id of_palmas_match_tbl[] = { { .compatible = "ti,palmas-usb", }, { .compatible = "ti,twl6035-usb", }, @@ -234,6 +279,7 @@ static struct platform_driver palmas_usb_driver = { .name = "palmas-usb", .of_match_table = of_palmas_match_tbl, .owner = THIS_MODULE, + .pm = &palmas_pm_ops, }, }; diff --git a/drivers/extcon/of_extcon.c b/drivers/extcon/of_extcon.c new file mode 100644 index 000000000000..72173ecbb311 --- /dev/null +++ b/drivers/extcon/of_extcon.c @@ -0,0 +1,64 @@ +/* + * OF helpers for External connector (extcon) framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + * Kishon Vijay Abraham I <kishon@ti.com> + * + * Copyright (C) 2013 Samsung Electronics + * Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/extcon.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/extcon/of_extcon.h> + +/* + * of_extcon_get_extcon_dev - Get the name of extcon device from devicetree + * @dev - instance to the given device + * @index - index into list of extcon_dev + * + * return the instance of extcon device + */ +struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index) +{ + struct device_node *node; + struct extcon_dev *edev; + struct platform_device *extcon_parent_dev; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, "extcon", index); + if (!node) { + dev_dbg(dev, "failed to get phandle in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + extcon_parent_dev = of_find_device_by_node(node); + if (!extcon_parent_dev) { + dev_dbg(dev, "unable to find device by node\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev)); + if (!edev) { + dev_dbg(dev, "unable to get extcon device : %s\n", + dev_name(&extcon_parent_dev->dev)); + return ERR_PTR(-ENODEV); + } + + return edev; +} +EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev); diff --git a/drivers/fmc/fmc-chardev.c b/drivers/fmc/fmc-chardev.c index cc031db2d2a3..ace6ef24d15e 100644 --- a/drivers/fmc/fmc-chardev.c +++ b/drivers/fmc/fmc-chardev.c @@ -143,18 +143,17 @@ static int fc_probe(struct fmc_device *fmc) fc->misc.fops = &fc_fops; fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL); - spin_lock(&fc_lock); ret = misc_register(&fc->misc); if (ret < 0) - goto err_unlock; + goto out; + spin_lock(&fc_lock); list_add(&fc->list, &fc_devices); spin_unlock(&fc_lock); dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n", fc->misc.name); return 0; -err_unlock: - spin_unlock(&fc_lock); +out: kfree(fc->misc.name); kfree(fc); return ret; @@ -174,10 +173,10 @@ static int fc_remove(struct fmc_device *fmc) spin_lock(&fc_lock); list_del(&fc->list); + spin_unlock(&fc_lock); misc_deregister(&fc->misc); kfree(fc->misc.name); kfree(fc); - spin_unlock(&fc_lock); return 0; } diff --git a/drivers/fmc/fmc-write-eeprom.c b/drivers/fmc/fmc-write-eeprom.c index 2cc680dd604d..ee5b47904130 100644 --- a/drivers/fmc/fmc-write-eeprom.c +++ b/drivers/fmc/fmc-write-eeprom.c @@ -103,7 +103,7 @@ static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s) * difficult to know in advance when probing the first card if others * are there. */ -int fwe_probe(struct fmc_device *fmc) +static int fwe_probe(struct fmc_device *fmc) { int err, index = 0; const struct firmware *fw; @@ -144,7 +144,7 @@ int fwe_probe(struct fmc_device *fmc) return 0; } -int fwe_remove(struct fmc_device *fmc) +static int fwe_remove(struct fmc_device *fmc) { return 0; } diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 713217380b44..8fae6d1414cc 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -590,6 +590,5 @@ static void __exit mousevsc_exit(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); module_init(mousevsc_init); module_exit(mousevsc_exit); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0df75908200e..bbff5f200bef 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -48,30 +48,39 @@ struct vmbus_channel_message_table_entry { * @negop is of type &struct icmsg_negotiate. * Set up and fill in default negotiate response message. * - * The max_fw_version specifies the maximum framework version that - * we can support and max _srv_version specifies the maximum service - * version we can support. A special value MAX_SRV_VER can be - * specified to indicate that we can handle the maximum version - * exposed by the host. + * The fw_version specifies the framework version that + * we can support and srv_version specifies the service + * version we can support. * * Mainly used by Hyper-V drivers. */ -void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, +bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, struct icmsg_negotiate *negop, u8 *buf, - int max_fw_version, int max_srv_version) + int fw_version, int srv_version) { - int icframe_vercnt; - int icmsg_vercnt; + int icframe_major, icframe_minor; + int icmsg_major, icmsg_minor; + int fw_major, fw_minor; + int srv_major, srv_minor; int i; + bool found_match = false; icmsghdrp->icmsgsize = 0x10; + fw_major = (fw_version >> 16); + fw_minor = (fw_version & 0xFFFF); + + srv_major = (srv_version >> 16); + srv_minor = (srv_version & 0xFFFF); negop = (struct icmsg_negotiate *)&buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - icframe_vercnt = negop->icframe_vercnt; - icmsg_vercnt = negop->icmsg_vercnt; + icframe_major = negop->icframe_vercnt; + icframe_minor = 0; + + icmsg_major = negop->icmsg_vercnt; + icmsg_minor = 0; /* * Select the framework version number we will @@ -79,26 +88,48 @@ void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, */ for (i = 0; i < negop->icframe_vercnt; i++) { - if (negop->icversion_data[i].major <= max_fw_version) - icframe_vercnt = negop->icversion_data[i].major; + if ((negop->icversion_data[i].major == fw_major) && + (negop->icversion_data[i].minor == fw_minor)) { + icframe_major = negop->icversion_data[i].major; + icframe_minor = negop->icversion_data[i].minor; + found_match = true; + } } + if (!found_match) + goto fw_error; + + found_match = false; + for (i = negop->icframe_vercnt; (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) { - if (negop->icversion_data[i].major <= max_srv_version) - icmsg_vercnt = negop->icversion_data[i].major; + if ((negop->icversion_data[i].major == srv_major) && + (negop->icversion_data[i].minor == srv_minor)) { + icmsg_major = negop->icversion_data[i].major; + icmsg_minor = negop->icversion_data[i].minor; + found_match = true; + } } /* - * Respond with the maximum framework and service + * Respond with the framework and service * version numbers we can support. */ - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - negop->icversion_data[0].major = icframe_vercnt; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = icmsg_vercnt; - negop->icversion_data[1].minor = 0; + +fw_error: + if (!found_match) { + negop->icframe_vercnt = 0; + negop->icmsg_vercnt = 0; + } else { + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + } + + negop->icversion_data[0].major = icframe_major; + negop->icversion_data[0].minor = icframe_minor; + negop->icversion_data[1].major = icmsg_major; + negop->icversion_data[1].minor = icmsg_minor; + return found_match; } EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); @@ -262,6 +293,13 @@ static void vmbus_process_offer(struct work_struct *work) } /* + * This state is used to indicate a successful open + * so that when we do close the channel normally, we + * can cleanup properly + */ + newchannel->state = CHANNEL_OPEN_STATE; + + /* * Start the process of binding this offer to the driver * We need to set the DeviceObject field before calling * vmbus_child_dev_add() @@ -287,13 +325,6 @@ static void vmbus_process_offer(struct work_struct *work) kfree(newchannel->device_obj); free_channel(newchannel); - } else { - /* - * This state is used to indicate a successful open - * so that when we do close the channel normally, we - * can cleanup properly - */ - newchannel->state = CHANNEL_OPEN_STATE; } } diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index ec3b8cdf1e04..8f4743ab5fb2 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -195,7 +195,10 @@ int vmbus_connect(void) do { ret = vmbus_negotiate_version(msginfo, version); - if (ret == 0) + if (ret) + goto cleanup; + + if (vmbus_connection.conn_state == CONNECTED) break; version = vmbus_get_next_version(version); diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index deb5c25305af..7e17a5495e02 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -825,7 +825,6 @@ static void hot_add_req(struct work_struct *dummy) memset(&resp, 0, sizeof(struct dm_hot_add_response)); resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE; resp.hdr.size = sizeof(struct dm_hot_add_response); - resp.hdr.trans_id = atomic_inc_return(&trans_id); #ifdef CONFIG_MEMORY_HOTPLUG pg_start = dm->ha_wrk.ha_page_range.finfo.start_page; @@ -887,6 +886,7 @@ static void hot_add_req(struct work_struct *dummy) pr_info("Memory hot add failed\n"); dm->state = DM_INITIALIZED; + resp.hdr.trans_id = atomic_inc_return(&trans_id); vmbus_sendpacket(dm->dev->channel, &resp, sizeof(struct dm_hot_add_response), (unsigned long)NULL, @@ -1081,7 +1081,6 @@ static void balloon_up(struct work_struct *dummy) bl_resp = (struct dm_balloon_response *)send_buffer; memset(send_buffer, 0, PAGE_SIZE); bl_resp->hdr.type = DM_BALLOON_RESPONSE; - bl_resp->hdr.trans_id = atomic_inc_return(&trans_id); bl_resp->hdr.size = sizeof(struct dm_balloon_response); bl_resp->more_pages = 1; @@ -1109,6 +1108,7 @@ static void balloon_up(struct work_struct *dummy) */ do { + bl_resp->hdr.trans_id = atomic_inc_return(&trans_id); ret = vmbus_sendpacket(dm_device.dev->channel, bl_resp, bl_resp->hdr.size, @@ -1526,5 +1526,4 @@ static int __init init_balloon_drv(void) module_init(init_balloon_drv); MODULE_DESCRIPTION("Hyper-V Balloon"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index ed50e9e83c61..28b03325b872 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -29,6 +29,16 @@ #include <linux/hyperv.h> +/* + * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7) + */ +#define WIN7_SRV_MAJOR 3 +#define WIN7_SRV_MINOR 0 +#define WIN7_SRV_MAJOR_MINOR (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR) + +#define WIN8_SRV_MAJOR 4 +#define WIN8_SRV_MINOR 0 +#define WIN8_SRV_MAJOR_MINOR (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) /* * Global state maintained for transaction that is being processed. @@ -76,7 +86,9 @@ static u8 *recv_buffer; /* * Register the kernel component with the user-level daemon. * As part of this registration, pass the LIC version number. + * This number has no meaning, it satisfies the registration protocol. */ +#define HV_DRV_VERSION "3.1" static void kvp_register(int reg_value) @@ -593,8 +605,19 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + /* + * We start with win8 version and if the host cannot + * support that we use the previous version. + */ + if (vmbus_prep_negotiate_resp(icmsghdrp, negop, + recv_buffer, UTIL_FW_MAJOR_MINOR, + WIN8_SRV_MAJOR_MINOR)) + goto done; + vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, MAX_SRV_VER, MAX_SRV_VER); + recv_buffer, UTIL_FW_MAJOR_MINOR, + WIN7_SRV_MAJOR_MINOR); + } else { kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + @@ -626,6 +649,7 @@ void hv_kvp_onchannelcallback(void *context) return; } +done: icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 8ad5653ce447..e4572f3f2834 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -24,6 +24,10 @@ #include <linux/workqueue.h> #include <linux/hyperv.h> +#define VSS_MAJOR 5 +#define VSS_MINOR 0 +#define VSS_MAJOR_MINOR (VSS_MAJOR << 16 | VSS_MINOR) + /* @@ -186,18 +190,8 @@ void hv_vss_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, MAX_SRV_VER, MAX_SRV_VER); - /* - * We currently negotiate the highest number the - * host has presented. If this version is not - * atleast 5.0, reject. - */ - negop = (struct icmsg_negotiate *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - if (negop->icversion_data[1].major < 5) - negop->icframe_vercnt = 0; + recv_buffer, UTIL_FW_MAJOR_MINOR, + VSS_MAJOR_MINOR); } else { vss_msg = (struct hv_vss_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 2f561c5dfe24..cb82233541b1 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -28,6 +28,18 @@ #include <linux/reboot.h> #include <linux/hyperv.h> +#define SHUTDOWN_MAJOR 3 +#define SHUTDOWN_MINOR 0 +#define SHUTDOWN_MAJOR_MINOR (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR) + +#define TIMESYNCH_MAJOR 3 +#define TIMESYNCH_MINOR 0 +#define TIMESYNCH_MAJOR_MINOR (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR) + +#define HEARTBEAT_MAJOR 3 +#define HEARTBEAT_MINOR 0 +#define HEARTBEAT_MAJOR_MINOR (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR) + static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, @@ -87,7 +99,8 @@ static void shutdown_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, negop, - shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER); + shut_txf_buf, UTIL_FW_MAJOR_MINOR, + SHUTDOWN_MAJOR_MINOR); } else { shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ @@ -213,7 +226,8 @@ static void timesync_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf, - MAX_SRV_VER, MAX_SRV_VER); + UTIL_FW_MAJOR_MINOR, + TIMESYNCH_MAJOR_MINOR); } else { timedatap = (struct ictimesync_data *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr) + @@ -253,7 +267,8 @@ static void heartbeat_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, NULL, - hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER); + hbeat_txf_buf, UTIL_FW_MAJOR_MINOR, + HEARTBEAT_MAJOR_MINOR); } else { heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ @@ -380,5 +395,4 @@ module_init(init_hyperv_utils); module_exit(exit_hyperv_utils); MODULE_DESCRIPTION("Hyper-V Utilities"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e8e071fc1d6d..f9fe46f52cfa 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -816,7 +816,6 @@ static void __exit vmbus_exit(void) MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); subsys_initcall(hv_acpi_init); module_exit(vmbus_exit); diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index e068a76a5f6f..5be808406edc 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/of.h> -#include <linux/pinctrl/consumer.h> /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); @@ -137,13 +136,6 @@ static int ssc_probe(struct platform_device *pdev) struct resource *regs; struct ssc_device *ssc; const struct atmel_ssc_platform_data *plat_dat; - struct pinctrl *pinctrl; - - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(&pdev->dev, "Failed to request pinctrl\n"); - return PTR_ERR(pinctrl); - } ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL); if (!ssc) { diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 621c7a373390..b83e3ca12a41 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -759,7 +759,7 @@ static int ilo_probe(struct pci_dev *pdev, /* Ignore subsystem_device = 0x1979 (set by BIOS) */ if (pdev->subsystem_device == 0x1979) - goto out; + return 0; if (max_ccb > MAX_CCB) max_ccb = MAX_CCB; @@ -899,7 +899,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("1.4"); +MODULE_VERSION("1.4.1"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>"); diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 08aad69c8da4..2fc0586ce3bb 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -43,6 +43,7 @@ #include <linux/slab.h> #include <scsi/scsi_cmnd.h> #include <linux/debugfs.h> +#include <linux/vmalloc.h> #ifdef CONFIG_IDE #include <linux/ide.h> @@ -50,6 +51,7 @@ #define DEFAULT_COUNT 10 #define REC_NUM_DEFAULT 10 +#define EXEC_SIZE 64 enum cname { CN_INVALID, @@ -68,6 +70,7 @@ enum ctype { CT_NONE, CT_PANIC, CT_BUG, + CT_WARNING, CT_EXCEPTION, CT_LOOP, CT_OVERFLOW, @@ -77,7 +80,12 @@ enum ctype { CT_WRITE_AFTER_FREE, CT_SOFTLOCKUP, CT_HARDLOCKUP, + CT_SPINLOCKUP, CT_HUNG_TASK, + CT_EXEC_DATA, + CT_EXEC_STACK, + CT_EXEC_KMALLOC, + CT_EXEC_VMALLOC, }; static char* cp_name[] = { @@ -95,6 +103,7 @@ static char* cp_name[] = { static char* cp_type[] = { "PANIC", "BUG", + "WARNING", "EXCEPTION", "LOOP", "OVERFLOW", @@ -104,7 +113,12 @@ static char* cp_type[] = { "WRITE_AFTER_FREE", "SOFTLOCKUP", "HARDLOCKUP", + "SPINLOCKUP", "HUNG_TASK", + "EXEC_DATA", + "EXEC_STACK", + "EXEC_KMALLOC", + "EXEC_VMALLOC", }; static struct jprobe lkdtm; @@ -121,6 +135,9 @@ static enum cname cpoint = CN_INVALID; static enum ctype cptype = CT_NONE; static int count = DEFAULT_COUNT; static DEFINE_SPINLOCK(count_lock); +static DEFINE_SPINLOCK(lock_me_up); + +static u8 data_area[EXEC_SIZE]; module_param(recur_count, int, 0644); MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ @@ -275,6 +292,19 @@ static int recursive_loop(int a) return recursive_loop(a); } +static void do_nothing(void) +{ + return; +} + +static void execute_location(void *dst) +{ + void (*func)(void) = dst; + + memcpy(dst, do_nothing, EXEC_SIZE); + func(); +} + static void lkdtm_do_action(enum ctype which) { switch (which) { @@ -284,6 +314,9 @@ static void lkdtm_do_action(enum ctype which) case CT_BUG: BUG(); break; + case CT_WARNING: + WARN_ON(1); + break; case CT_EXCEPTION: *((int *) 0) = 0; break; @@ -295,10 +328,10 @@ static void lkdtm_do_action(enum ctype which) (void) recursive_loop(0); break; case CT_CORRUPT_STACK: { - volatile u32 data[8]; - volatile u32 *p = data; + /* Make sure the compiler creates and uses an 8 char array. */ + volatile char data[8]; - p[12] = 0x12345678; + memset((void *)data, 0, 64); break; } case CT_UNALIGNED_LOAD_STORE_WRITE: { @@ -340,10 +373,34 @@ static void lkdtm_do_action(enum ctype which) for (;;) cpu_relax(); break; + case CT_SPINLOCKUP: + /* Must be called twice to trigger. */ + spin_lock(&lock_me_up); + break; case CT_HUNG_TASK: set_current_state(TASK_UNINTERRUPTIBLE); schedule(); break; + case CT_EXEC_DATA: + execute_location(data_area); + break; + case CT_EXEC_STACK: { + u8 stack_area[EXEC_SIZE]; + execute_location(stack_area); + break; + } + case CT_EXEC_KMALLOC: { + u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL); + execute_location(kmalloc_area); + kfree(kmalloc_area); + break; + } + case CT_EXEC_VMALLOC: { + u32 *vmalloc_area = vmalloc(EXEC_SIZE); + execute_location(vmalloc_area); + vfree(vmalloc_area); + break; + } case CT_NONE: default: break; diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 749452f8e2f6..d0fdc134068a 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -418,15 +418,23 @@ unsigned int mei_amthif_poll(struct mei_device *dev, struct file *file, poll_table *wait) { unsigned int mask = 0; - mutex_unlock(&dev->device_lock); + poll_wait(file, &dev->iamthif_cl.wait, wait); + mutex_lock(&dev->device_lock); - if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && - dev->iamthif_file_object == file) { + if (!mei_cl_is_connected(&dev->iamthif_cl)) { + + mask = POLLERR; + + } else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && + dev->iamthif_file_object == file) { + mask |= (POLLIN | POLLRDNORM); dev_dbg(&dev->pdev->dev, "run next amthif cb\n"); mei_amthif_run_next_cmd(dev); } + mutex_unlock(&dev->device_lock); + return mask; } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 9ecd49a7be1b..a150a42ed4af 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -47,7 +47,7 @@ static int mei_cl_device_match(struct device *dev, struct device_driver *drv) id = driver->id_table; while (id->name[0]) { - if (!strcmp(dev_name(dev), id->name)) + if (!strncmp(dev_name(dev), id->name, sizeof(id->name))) return 1; id++; @@ -71,7 +71,7 @@ static int mei_cl_device_probe(struct device *dev) dev_dbg(dev, "Device probe\n"); - strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); + strncpy(id.name, dev_name(dev), sizeof(id.name)); return driver->probe(device, &id); } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 21d3f5aa8353..e0684b4d9a08 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -635,10 +635,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) dev = cl->dev; - if (cl->state != MEI_FILE_CONNECTED) - return -ENODEV; - - if (dev->dev_state != MEI_DEV_ENABLED) + if (!mei_cl_is_connected(cl)) return -ENODEV; if (cl->read_cb) { @@ -892,18 +889,22 @@ void mei_cl_all_disconnect(struct mei_device *dev) /** - * mei_cl_all_read_wakeup - wake up all readings so they can be interrupted + * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted * * @dev - mei device */ -void mei_cl_all_read_wakeup(struct mei_device *dev) +void mei_cl_all_wakeup(struct mei_device *dev) { struct mei_cl *cl, *next; list_for_each_entry_safe(cl, next, &dev->file_list, link) { if (waitqueue_active(&cl->rx_wait)) { - dev_dbg(&dev->pdev->dev, "Waking up client!\n"); + dev_dbg(&dev->pdev->dev, "Waking up reading client!\n"); wake_up_interruptible(&cl->rx_wait); } + if (waitqueue_active(&cl->tx_wait)) { + dev_dbg(&dev->pdev->dev, "Waking up writing client!\n"); + wake_up_interruptible(&cl->tx_wait); + } } } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 26b157d8bad5..9eb031e92070 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -84,6 +84,13 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); /* * MEI input output function prototype */ +static inline bool mei_cl_is_connected(struct mei_cl *cl) +{ + return (cl->dev && + cl->dev->dev_state == MEI_DEV_ENABLED && + cl->state == MEI_FILE_CONNECTED); +} + bool mei_cl_is_other_connecting(struct mei_cl *cl); int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_connect(struct mei_cl *cl, struct file *file); @@ -99,7 +106,7 @@ void mei_host_client_init(struct work_struct *work); void mei_cl_all_disconnect(struct mei_device *dev); -void mei_cl_all_read_wakeup(struct mei_device *dev); +void mei_cl_all_wakeup(struct mei_device *dev); void mei_cl_all_write_clear(struct mei_device *dev); #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index b22c7e247225..3412adcdaeb0 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -176,21 +176,18 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); - dev_dbg(&dev->pdev->dev, "before reset HCSR = 0x%08x.\n", hcsr); - - hcsr |= (H_RST | H_IG); + hcsr |= H_RST | H_IG | H_IS; if (intr_enable) hcsr |= H_IE; else - hcsr |= ~H_IE; + hcsr &= ~H_IE; - mei_hcsr_set(hw, hcsr); + mei_me_reg_write(hw, H_CSR, hcsr); if (dev->dev_state == MEI_DEV_POWER_DOWN) mei_me_hw_reset_release(dev); - dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", mei_hcsr_read(hw)); return 0; } diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index e6f16f83ecde..92c73118b13c 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -154,8 +154,14 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETTING; + /* remove all waiting requests */ + mei_cl_all_write_clear(dev); + mei_cl_all_disconnect(dev); + /* wake up all readings so they can be interrupted */ + mei_cl_all_wakeup(dev); + /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); @@ -196,11 +202,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) mei_hbm_start_req(dev); - /* wake up all readings so they can be interrupted */ - mei_cl_all_read_wakeup(dev); - - /* remove all waiting requests */ - mei_cl_all_write_clear(dev); } EXPORT_SYMBOL_GPL(mei_reset); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5e11b5b9b65d..173ff095be0d 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -625,24 +625,32 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) unsigned int mask = 0; if (WARN_ON(!cl || !cl->dev)) - return mask; + return POLLERR; dev = cl->dev; mutex_lock(&dev->device_lock); - if (dev->dev_state != MEI_DEV_ENABLED) - goto out; - - - if (cl == &dev->iamthif_cl) { - mask = mei_amthif_poll(dev, file, wait); + if (!mei_cl_is_connected(cl)) { + mask = POLLERR; goto out; } mutex_unlock(&dev->device_lock); + + + if (cl == &dev->iamthif_cl) + return mei_amthif_poll(dev, file, wait); + poll_wait(file, &cl->tx_wait, wait); + mutex_lock(&dev->device_lock); + + if (!mei_cl_is_connected(cl)) { + mask = POLLERR; + goto out; + } + if (MEI_WRITE_COMPLETE == cl->writing_state) mask |= (POLLIN | POLLRDNORM); diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index d87cc91bc016..afe66571ce0b 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -68,7 +68,8 @@ static int sram_probe(struct platform_device *pdev) ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base, res->start, size, -1); if (ret < 0) { - gen_pool_destroy(sram->pool); + if (sram->clk) + clk_disable_unprepare(sram->clk); return ret; } diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 0a1428016350..8d64b681dd93 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -562,7 +562,9 @@ long st_register(struct st_proto_s *new_proto) if ((st_gdata->protos_registered != ST_EMPTY) && (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { pr_err(" KIM failure complete callback "); + spin_lock_irqsave(&st_gdata->lock, flags); st_reg_complete(st_gdata, err); + spin_unlock_irqrestore(&st_gdata->lock, flags); clear_bit(ST_REG_PENDING, &st_gdata->st_state); } return -EINVAL; diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index cb56e270da11..2421835d5daf 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -133,7 +133,7 @@ MODULE_LICENSE("GPL"); #define VMWARE_BALLOON_CMD(cmd, data, result) \ ({ \ unsigned long __stat, __dummy1, __dummy2; \ - __asm__ __volatile__ ("inl (%%dx)" : \ + __asm__ __volatile__ ("inl %%dx" : \ "=a"(__stat), \ "=c"(__dummy1), \ "=d"(__dummy2), \ diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index 7b3fce2da6c3..3dee7ae123e7 100644 --- a/drivers/misc/vmw_vmci/vmci_driver.c +++ b/drivers/misc/vmw_vmci/vmci_driver.c @@ -113,5 +113,5 @@ module_exit(vmci_drv_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface."); -MODULE_VERSION("1.0.0.0-k"); +MODULE_VERSION("1.1.0.0-k"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/vmw_vmci/vmci_driver.h b/drivers/misc/vmw_vmci/vmci_driver.h index f69156a1f30c..cee9e977d318 100644 --- a/drivers/misc/vmw_vmci/vmci_driver.h +++ b/drivers/misc/vmw_vmci/vmci_driver.h @@ -35,6 +35,13 @@ struct vmci_obj { enum vmci_obj_type type; }; +/* + * Needed by other components of this module. It's okay to have one global + * instance of this because there can only ever be one VMCI device. Our + * virtual hardware enforces this. + */ +extern struct pci_dev *vmci_pdev; + u32 vmci_get_context_id(void); int vmci_send_datagram(struct vmci_datagram *dg); diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 60c01999f489..b3a2b763ecf2 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -65,9 +65,11 @@ struct vmci_guest_device { void *data_buffer; void *notification_bitmap; + dma_addr_t notification_base; }; /* vmci_dev singleton device and supporting data*/ +struct pci_dev *vmci_pdev; static struct vmci_guest_device *vmci_dev_g; static DEFINE_SPINLOCK(vmci_dev_spinlock); @@ -528,7 +530,9 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * well. */ if (capabilities & VMCI_CAPS_NOTIFICATIONS) { - vmci_dev->notification_bitmap = vmalloc(PAGE_SIZE); + vmci_dev->notification_bitmap = dma_alloc_coherent( + &pdev->dev, PAGE_SIZE, &vmci_dev->notification_base, + GFP_KERNEL); if (!vmci_dev->notification_bitmap) { dev_warn(&pdev->dev, "Unable to allocate notification bitmap\n"); @@ -546,6 +550,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, /* Set up global device so that we can start sending datagrams */ spin_lock_irq(&vmci_dev_spinlock); vmci_dev_g = vmci_dev; + vmci_pdev = pdev; spin_unlock_irq(&vmci_dev_spinlock); /* @@ -553,9 +558,8 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * used. */ if (capabilities & VMCI_CAPS_NOTIFICATIONS) { - struct page *page = - vmalloc_to_page(vmci_dev->notification_bitmap); - unsigned long bitmap_ppn = page_to_pfn(page); + unsigned long bitmap_ppn = + vmci_dev->notification_base >> PAGE_SHIFT; if (!vmci_dbell_register_notification_bitmap(bitmap_ppn)) { dev_warn(&pdev->dev, "VMCI device unable to register notification bitmap with PPN 0x%x\n", @@ -665,11 +669,14 @@ err_remove_bitmap: if (vmci_dev->notification_bitmap) { iowrite32(VMCI_CONTROL_RESET, vmci_dev->iobase + VMCI_CONTROL_ADDR); - vfree(vmci_dev->notification_bitmap); + dma_free_coherent(&pdev->dev, PAGE_SIZE, + vmci_dev->notification_bitmap, + vmci_dev->notification_base); } err_remove_vmci_dev_g: spin_lock_irq(&vmci_dev_spinlock); + vmci_pdev = NULL; vmci_dev_g = NULL; spin_unlock_irq(&vmci_dev_spinlock); @@ -699,6 +706,7 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) spin_lock_irq(&vmci_dev_spinlock); vmci_dev_g = NULL; + vmci_pdev = NULL; spin_unlock_irq(&vmci_dev_spinlock); dev_dbg(&pdev->dev, "Resetting vmci device\n"); @@ -727,7 +735,9 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) * device, so we can safely free it here. */ - vfree(vmci_dev->notification_bitmap); + dma_free_coherent(&pdev->dev, PAGE_SIZE, + vmci_dev->notification_bitmap, + vmci_dev->notification_base); } vfree(vmci_dev->data_buffer); diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 8ff2e5ee8fb8..a0515a6d6ebd 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/pagemap.h> +#include <linux/pci.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/uio.h> @@ -146,14 +147,20 @@ typedef int vmci_memcpy_from_queue_func(void *dest, size_t dest_offset, /* The Kernel specific component of the struct vmci_queue structure. */ struct vmci_queue_kern_if { - struct page **page; - struct page **header_page; - void *va; struct mutex __mutex; /* Protects the queue. */ struct mutex *mutex; /* Shared by producer and consumer queues. */ - bool host; - size_t num_pages; - bool mapped; + size_t num_pages; /* Number of pages incl. header. */ + bool host; /* Host or guest? */ + union { + struct { + dma_addr_t *pas; + void **vas; + } g; /* Used by the guest. */ + struct { + struct page **page; + struct page **header_page; + } h; /* Used by the host. */ + } u; }; /* @@ -265,76 +272,65 @@ static void qp_free_queue(void *q, u64 size) struct vmci_queue *queue = q; if (queue) { - u64 i = DIV_ROUND_UP(size, PAGE_SIZE); + u64 i; - if (queue->kernel_if->mapped) { - vunmap(queue->kernel_if->va); - queue->kernel_if->va = NULL; + /* Given size does not include header, so add in a page here. */ + for (i = 0; i < DIV_ROUND_UP(size, PAGE_SIZE) + 1; i++) { + dma_free_coherent(&vmci_pdev->dev, PAGE_SIZE, + queue->kernel_if->u.g.vas[i], + queue->kernel_if->u.g.pas[i]); } - while (i) - __free_page(queue->kernel_if->page[--i]); - - vfree(queue->q_header); + vfree(queue); } } /* - * Allocates kernel VA space of specified size, plus space for the - * queue structure/kernel interface and the queue header. Allocates - * physical pages for the queue data pages. - * - * PAGE m: struct vmci_queue_header (struct vmci_queue->q_header) - * PAGE m+1: struct vmci_queue - * PAGE m+1+q: struct vmci_queue_kern_if (struct vmci_queue->kernel_if) - * PAGE n-size: Data pages (struct vmci_queue->kernel_if->page[]) + * Allocates kernel queue pages of specified size with IOMMU mappings, + * plus space for the queue structure/kernel interface and the queue + * header. */ static void *qp_alloc_queue(u64 size, u32 flags) { u64 i; struct vmci_queue *queue; - struct vmci_queue_header *q_header; - const u64 num_data_pages = DIV_ROUND_UP(size, PAGE_SIZE); - const uint queue_size = - PAGE_SIZE + - sizeof(*queue) + sizeof(*(queue->kernel_if)) + - num_data_pages * sizeof(*(queue->kernel_if->page)); - - q_header = vmalloc(queue_size); - if (!q_header) + const size_t num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; + const size_t pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas); + const size_t vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas); + const size_t queue_size = + sizeof(*queue) + sizeof(*queue->kernel_if) + + pas_size + vas_size; + + queue = vmalloc(queue_size); + if (!queue) return NULL; - queue = (void *)q_header + PAGE_SIZE; - queue->q_header = q_header; + queue->q_header = NULL; queue->saved_header = NULL; queue->kernel_if = (struct vmci_queue_kern_if *)(queue + 1); - queue->kernel_if->header_page = NULL; /* Unused in guest. */ - queue->kernel_if->page = (struct page **)(queue->kernel_if + 1); + queue->kernel_if->mutex = NULL; + queue->kernel_if->num_pages = num_pages; + queue->kernel_if->u.g.pas = (dma_addr_t *)(queue->kernel_if + 1); + queue->kernel_if->u.g.vas = + (void **)((u8 *)queue->kernel_if->u.g.pas + pas_size); queue->kernel_if->host = false; - queue->kernel_if->va = NULL; - queue->kernel_if->mapped = false; - - for (i = 0; i < num_data_pages; i++) { - queue->kernel_if->page[i] = alloc_pages(GFP_KERNEL, 0); - if (!queue->kernel_if->page[i]) - goto fail; - } - if (vmci_qp_pinned(flags)) { - queue->kernel_if->va = - vmap(queue->kernel_if->page, num_data_pages, VM_MAP, - PAGE_KERNEL); - if (!queue->kernel_if->va) - goto fail; - - queue->kernel_if->mapped = true; + for (i = 0; i < num_pages; i++) { + queue->kernel_if->u.g.vas[i] = + dma_alloc_coherent(&vmci_pdev->dev, PAGE_SIZE, + &queue->kernel_if->u.g.pas[i], + GFP_KERNEL); + if (!queue->kernel_if->u.g.vas[i]) { + /* Size excl. the header. */ + qp_free_queue(queue, i * PAGE_SIZE); + return NULL; + } } - return (void *)queue; + /* Queue header is the first page. */ + queue->q_header = queue->kernel_if->u.g.vas[0]; - fail: - qp_free_queue(queue, i * PAGE_SIZE); - return NULL; + return queue; } /* @@ -353,17 +349,18 @@ static int __qp_memcpy_to_queue(struct vmci_queue *queue, size_t bytes_copied = 0; while (bytes_copied < size) { - u64 page_index = (queue_offset + bytes_copied) / PAGE_SIZE; - size_t page_offset = + const u64 page_index = + (queue_offset + bytes_copied) / PAGE_SIZE; + const size_t page_offset = (queue_offset + bytes_copied) & (PAGE_SIZE - 1); void *va; size_t to_copy; - if (!kernel_if->mapped) - va = kmap(kernel_if->page[page_index]); + if (kernel_if->host) + va = kmap(kernel_if->u.h.page[page_index]); else - va = (void *)((u8 *)kernel_if->va + - (page_index * PAGE_SIZE)); + va = kernel_if->u.g.vas[page_index + 1]; + /* Skip header. */ if (size - bytes_copied > PAGE_SIZE - page_offset) /* Enough payload to fill up from this page. */ @@ -379,7 +376,8 @@ static int __qp_memcpy_to_queue(struct vmci_queue *queue, err = memcpy_fromiovec((u8 *)va + page_offset, iov, to_copy); if (err != 0) { - kunmap(kernel_if->page[page_index]); + if (kernel_if->host) + kunmap(kernel_if->u.h.page[page_index]); return VMCI_ERROR_INVALID_ARGS; } } else { @@ -388,8 +386,8 @@ static int __qp_memcpy_to_queue(struct vmci_queue *queue, } bytes_copied += to_copy; - if (!kernel_if->mapped) - kunmap(kernel_if->page[page_index]); + if (kernel_if->host) + kunmap(kernel_if->u.h.page[page_index]); } return VMCI_SUCCESS; @@ -411,17 +409,18 @@ static int __qp_memcpy_from_queue(void *dest, size_t bytes_copied = 0; while (bytes_copied < size) { - u64 page_index = (queue_offset + bytes_copied) / PAGE_SIZE; - size_t page_offset = + const u64 page_index = + (queue_offset + bytes_copied) / PAGE_SIZE; + const size_t page_offset = (queue_offset + bytes_copied) & (PAGE_SIZE - 1); void *va; size_t to_copy; - if (!kernel_if->mapped) - va = kmap(kernel_if->page[page_index]); + if (kernel_if->host) + va = kmap(kernel_if->u.h.page[page_index]); else - va = (void *)((u8 *)kernel_if->va + - (page_index * PAGE_SIZE)); + va = kernel_if->u.g.vas[page_index + 1]; + /* Skip header. */ if (size - bytes_copied > PAGE_SIZE - page_offset) /* Enough payload to fill up this page. */ @@ -437,7 +436,8 @@ static int __qp_memcpy_from_queue(void *dest, err = memcpy_toiovec(iov, (u8 *)va + page_offset, to_copy); if (err != 0) { - kunmap(kernel_if->page[page_index]); + if (kernel_if->host) + kunmap(kernel_if->u.h.page[page_index]); return VMCI_ERROR_INVALID_ARGS; } } else { @@ -446,8 +446,8 @@ static int __qp_memcpy_from_queue(void *dest, } bytes_copied += to_copy; - if (!kernel_if->mapped) - kunmap(kernel_if->page[page_index]); + if (kernel_if->host) + kunmap(kernel_if->u.h.page[page_index]); } return VMCI_SUCCESS; @@ -489,12 +489,11 @@ static int qp_alloc_ppn_set(void *prod_q, return VMCI_ERROR_NO_MEM; } - produce_ppns[0] = page_to_pfn(vmalloc_to_page(produce_q->q_header)); - for (i = 1; i < num_produce_pages; i++) { + for (i = 0; i < num_produce_pages; i++) { unsigned long pfn; produce_ppns[i] = - page_to_pfn(produce_q->kernel_if->page[i - 1]); + produce_q->kernel_if->u.g.pas[i] >> PAGE_SHIFT; pfn = produce_ppns[i]; /* Fail allocation if PFN isn't supported by hypervisor. */ @@ -503,12 +502,11 @@ static int qp_alloc_ppn_set(void *prod_q, goto ppn_error; } - consume_ppns[0] = page_to_pfn(vmalloc_to_page(consume_q->q_header)); - for (i = 1; i < num_consume_pages; i++) { + for (i = 0; i < num_consume_pages; i++) { unsigned long pfn; consume_ppns[i] = - page_to_pfn(consume_q->kernel_if->page[i - 1]); + consume_q->kernel_if->u.g.pas[i] >> PAGE_SHIFT; pfn = consume_ppns[i]; /* Fail allocation if PFN isn't supported by hypervisor. */ @@ -619,23 +617,20 @@ static struct vmci_queue *qp_host_alloc_queue(u64 size) const size_t num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if)); const size_t queue_page_size = - num_pages * sizeof(*queue->kernel_if->page); + num_pages * sizeof(*queue->kernel_if->u.h.page); queue = kzalloc(queue_size + queue_page_size, GFP_KERNEL); if (queue) { queue->q_header = NULL; queue->saved_header = NULL; - queue->kernel_if = - (struct vmci_queue_kern_if *)((u8 *)queue + - sizeof(*queue)); + queue->kernel_if = (struct vmci_queue_kern_if *)(queue + 1); queue->kernel_if->host = true; queue->kernel_if->mutex = NULL; queue->kernel_if->num_pages = num_pages; - queue->kernel_if->header_page = + queue->kernel_if->u.h.header_page = (struct page **)((u8 *)queue + queue_size); - queue->kernel_if->page = &queue->kernel_if->header_page[1]; - queue->kernel_if->va = NULL; - queue->kernel_if->mapped = false; + queue->kernel_if->u.h.page = + &queue->kernel_if->u.h.header_page[1]; } return queue; @@ -742,11 +737,12 @@ static int qp_host_get_user_memory(u64 produce_uva, current->mm, (uintptr_t) produce_uva, produce_q->kernel_if->num_pages, - 1, 0, produce_q->kernel_if->header_page, NULL); + 1, 0, + produce_q->kernel_if->u.h.header_page, NULL); if (retval < produce_q->kernel_if->num_pages) { pr_warn("get_user_pages(produce) failed (retval=%d)", retval); - qp_release_pages(produce_q->kernel_if->header_page, retval, - false); + qp_release_pages(produce_q->kernel_if->u.h.header_page, + retval, false); err = VMCI_ERROR_NO_MEM; goto out; } @@ -755,12 +751,13 @@ static int qp_host_get_user_memory(u64 produce_uva, current->mm, (uintptr_t) consume_uva, consume_q->kernel_if->num_pages, - 1, 0, consume_q->kernel_if->header_page, NULL); + 1, 0, + consume_q->kernel_if->u.h.header_page, NULL); if (retval < consume_q->kernel_if->num_pages) { pr_warn("get_user_pages(consume) failed (retval=%d)", retval); - qp_release_pages(consume_q->kernel_if->header_page, retval, - false); - qp_release_pages(produce_q->kernel_if->header_page, + qp_release_pages(consume_q->kernel_if->u.h.header_page, + retval, false); + qp_release_pages(produce_q->kernel_if->u.h.header_page, produce_q->kernel_if->num_pages, false); err = VMCI_ERROR_NO_MEM; } @@ -803,15 +800,15 @@ static int qp_host_register_user_memory(struct vmci_qp_page_store *page_store, static void qp_host_unregister_user_memory(struct vmci_queue *produce_q, struct vmci_queue *consume_q) { - qp_release_pages(produce_q->kernel_if->header_page, + qp_release_pages(produce_q->kernel_if->u.h.header_page, produce_q->kernel_if->num_pages, true); - memset(produce_q->kernel_if->header_page, 0, - sizeof(*produce_q->kernel_if->header_page) * + memset(produce_q->kernel_if->u.h.header_page, 0, + sizeof(*produce_q->kernel_if->u.h.header_page) * produce_q->kernel_if->num_pages); - qp_release_pages(consume_q->kernel_if->header_page, + qp_release_pages(consume_q->kernel_if->u.h.header_page, consume_q->kernel_if->num_pages, true); - memset(consume_q->kernel_if->header_page, 0, - sizeof(*consume_q->kernel_if->header_page) * + memset(consume_q->kernel_if->u.h.header_page, 0, + sizeof(*consume_q->kernel_if->u.h.header_page) * consume_q->kernel_if->num_pages); } @@ -834,12 +831,12 @@ static int qp_host_map_queues(struct vmci_queue *produce_q, if (produce_q->q_header != consume_q->q_header) return VMCI_ERROR_QUEUEPAIR_MISMATCH; - if (produce_q->kernel_if->header_page == NULL || - *produce_q->kernel_if->header_page == NULL) + if (produce_q->kernel_if->u.h.header_page == NULL || + *produce_q->kernel_if->u.h.header_page == NULL) return VMCI_ERROR_UNAVAILABLE; - headers[0] = *produce_q->kernel_if->header_page; - headers[1] = *consume_q->kernel_if->header_page; + headers[0] = *produce_q->kernel_if->u.h.header_page; + headers[1] = *consume_q->kernel_if->u.h.header_page; produce_q->q_header = vmap(headers, 2, VM_MAP, PAGE_KERNEL); if (produce_q->q_header != NULL) { @@ -1720,21 +1717,6 @@ static int qp_broker_attach(struct qp_broker_entry *entry, if (result < VMCI_SUCCESS) return result; - /* - * Preemptively load in the headers if non-blocking to - * prevent blocking later. - */ - if (entry->qp.flags & VMCI_QPFLAG_NONBLOCK) { - result = qp_host_map_queues(entry->produce_q, - entry->consume_q); - if (result < VMCI_SUCCESS) { - qp_host_unregister_user_memory( - entry->produce_q, - entry->consume_q); - return result; - } - } - entry->state = VMCIQPB_ATTACHED_MEM; } else { entry->state = VMCIQPB_ATTACHED_NO_MEM; @@ -1749,24 +1731,6 @@ static int qp_broker_attach(struct qp_broker_entry *entry, return VMCI_ERROR_UNAVAILABLE; } else { - /* - * For non-blocking queue pairs, we cannot rely on - * enqueue/dequeue to map in the pages on the - * host-side, since it may block, so we make an - * attempt here. - */ - - if (flags & VMCI_QPFLAG_NONBLOCK) { - result = - qp_host_map_queues(entry->produce_q, - entry->consume_q); - if (result < VMCI_SUCCESS) - return result; - - entry->qp.flags |= flags & - (VMCI_QPFLAG_NONBLOCK | VMCI_QPFLAG_PINNED); - } - /* The host side has successfully attached to a queue pair. */ entry->state = VMCIQPB_ATTACHED_MEM; } @@ -2543,24 +2507,19 @@ void vmci_qp_guest_endpoints_exit(void) * Since non-blocking isn't yet implemented on the host personality we * have no reason to acquire a spin lock. So to avoid the use of an * unnecessary lock only acquire the mutex if we can block. - * Note: It is assumed that QPFLAG_PINNED implies QPFLAG_NONBLOCK. Therefore - * we can use the same locking function for access to both the queue - * and the queue headers as it is the same logic. Assert this behvior. */ static void qp_lock(const struct vmci_qp *qpair) { - if (vmci_can_block(qpair->flags)) - qp_acquire_queue_mutex(qpair->produce_q); + qp_acquire_queue_mutex(qpair->produce_q); } /* * Helper routine that unlocks the queue pair after calling - * qp_lock. Respects non-blocking and pinning flags. + * qp_lock. */ static void qp_unlock(const struct vmci_qp *qpair) { - if (vmci_can_block(qpair->flags)) - qp_release_queue_mutex(qpair->produce_q); + qp_release_queue_mutex(qpair->produce_q); } /* @@ -2568,17 +2527,12 @@ static void qp_unlock(const struct vmci_qp *qpair) * currently not mapped, it will be attempted to do so. */ static int qp_map_queue_headers(struct vmci_queue *produce_q, - struct vmci_queue *consume_q, - bool can_block) + struct vmci_queue *consume_q) { int result; if (NULL == produce_q->q_header || NULL == consume_q->q_header) { - if (can_block) - result = qp_host_map_queues(produce_q, consume_q); - else - result = VMCI_ERROR_QUEUEPAIR_NOT_READY; - + result = qp_host_map_queues(produce_q, consume_q); if (result < VMCI_SUCCESS) return (produce_q->saved_header && consume_q->saved_header) ? @@ -2601,8 +2555,7 @@ static int qp_get_queue_headers(const struct vmci_qp *qpair, { int result; - result = qp_map_queue_headers(qpair->produce_q, qpair->consume_q, - vmci_can_block(qpair->flags)); + result = qp_map_queue_headers(qpair->produce_q, qpair->consume_q); if (result == VMCI_SUCCESS) { *produce_q_header = qpair->produce_q->q_header; *consume_q_header = qpair->consume_q->q_header; @@ -2645,9 +2598,6 @@ static bool qp_wait_for_ready_queue(struct vmci_qp *qpair) { unsigned int generation; - if (qpair->flags & VMCI_QPFLAG_NONBLOCK) - return false; - qpair->blocked++; generation = qpair->generation; qp_unlock(qpair); @@ -2674,15 +2624,14 @@ static ssize_t qp_enqueue_locked(struct vmci_queue *produce_q, const u64 produce_q_size, const void *buf, size_t buf_size, - vmci_memcpy_to_queue_func memcpy_to_queue, - bool can_block) + vmci_memcpy_to_queue_func memcpy_to_queue) { s64 free_space; u64 tail; size_t written; ssize_t result; - result = qp_map_queue_headers(produce_q, consume_q, can_block); + result = qp_map_queue_headers(produce_q, consume_q); if (unlikely(result != VMCI_SUCCESS)) return result; @@ -2737,15 +2686,14 @@ static ssize_t qp_dequeue_locked(struct vmci_queue *produce_q, void *buf, size_t buf_size, vmci_memcpy_from_queue_func memcpy_from_queue, - bool update_consumer, - bool can_block) + bool update_consumer) { s64 buf_ready; u64 head; size_t read; ssize_t result; - result = qp_map_queue_headers(produce_q, consume_q, can_block); + result = qp_map_queue_headers(produce_q, consume_q); if (unlikely(result != VMCI_SUCCESS)) return result; @@ -2842,32 +2790,11 @@ int vmci_qpair_alloc(struct vmci_qp **qpair, route = vmci_guest_code_active() ? VMCI_ROUTE_AS_GUEST : VMCI_ROUTE_AS_HOST; - /* If NONBLOCK or PINNED is set, we better be the guest personality. */ - if ((!vmci_can_block(flags) || vmci_qp_pinned(flags)) && - VMCI_ROUTE_AS_GUEST != route) { - pr_devel("Not guest personality w/ NONBLOCK OR PINNED set"); + if (flags & (VMCI_QPFLAG_NONBLOCK | VMCI_QPFLAG_PINNED)) { + pr_devel("NONBLOCK OR PINNED set"); return VMCI_ERROR_INVALID_ARGS; } - /* - * Limit the size of pinned QPs and check sanity. - * - * Pinned pages implies non-blocking mode. Mutexes aren't acquired - * when the NONBLOCK flag is set in qpair code; and also should not be - * acquired when the PINNED flagged is set. Since pinning pages - * implies we want speed, it makes no sense not to have NONBLOCK - * set if PINNED is set. Hence enforce this implication. - */ - if (vmci_qp_pinned(flags)) { - if (vmci_can_block(flags)) { - pr_err("Attempted to enable pinning w/o non-blocking"); - return VMCI_ERROR_INVALID_ARGS; - } - - if (produce_qsize + consume_qsize > VMCI_MAX_PINNED_QP_MEMORY) - return VMCI_ERROR_NO_RESOURCES; - } - my_qpair = kzalloc(sizeof(*my_qpair), GFP_KERNEL); if (!my_qpair) return VMCI_ERROR_NO_MEM; @@ -3195,8 +3122,7 @@ ssize_t vmci_qpair_enqueue(struct vmci_qp *qpair, qpair->consume_q, qpair->produce_q_size, buf, buf_size, - qp_memcpy_to_queue, - vmci_can_block(qpair->flags)); + qp_memcpy_to_queue); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && !qp_wait_for_ready_queue(qpair)) @@ -3237,8 +3163,7 @@ ssize_t vmci_qpair_dequeue(struct vmci_qp *qpair, qpair->consume_q, qpair->consume_q_size, buf, buf_size, - qp_memcpy_from_queue, true, - vmci_can_block(qpair->flags)); + qp_memcpy_from_queue, true); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && !qp_wait_for_ready_queue(qpair)) @@ -3280,8 +3205,7 @@ ssize_t vmci_qpair_peek(struct vmci_qp *qpair, qpair->consume_q, qpair->consume_q_size, buf, buf_size, - qp_memcpy_from_queue, false, - vmci_can_block(qpair->flags)); + qp_memcpy_from_queue, false); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && !qp_wait_for_ready_queue(qpair)) @@ -3323,8 +3247,7 @@ ssize_t vmci_qpair_enquev(struct vmci_qp *qpair, qpair->consume_q, qpair->produce_q_size, iov, iov_size, - qp_memcpy_to_queue_iov, - vmci_can_block(qpair->flags)); + qp_memcpy_to_queue_iov); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && !qp_wait_for_ready_queue(qpair)) @@ -3367,7 +3290,7 @@ ssize_t vmci_qpair_dequev(struct vmci_qp *qpair, qpair->consume_q_size, iov, iov_size, qp_memcpy_from_queue_iov, - true, vmci_can_block(qpair->flags)); + true); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && !qp_wait_for_ready_queue(qpair)) @@ -3411,7 +3334,7 @@ ssize_t vmci_qpair_peekv(struct vmci_qp *qpair, qpair->consume_q_size, iov, iov_size, qp_memcpy_from_queue_iov, - false, vmci_can_block(qpair->flags)); + false); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && !qp_wait_for_ready_queue(qpair)) diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.h b/drivers/misc/vmw_vmci/vmci_queue_pair.h index 58c6959f6b6d..ed177f04ef24 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.h +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.h @@ -146,24 +146,6 @@ VMCI_QP_PAGESTORE_IS_WELLFORMED(struct vmci_qp_page_store *page_store) return page_store->len >= 2; } -/* - * Helper function to check if the non-blocking flag - * is set for a given queue pair. - */ -static inline bool vmci_can_block(u32 flags) -{ - return !(flags & VMCI_QPFLAG_NONBLOCK); -} - -/* - * Helper function to check if the queue pair is pinned - * into memory. - */ -static inline bool vmci_qp_pinned(u32 flags) -{ - return flags & VMCI_QPFLAG_PINNED; -} - void vmci_qp_broker_exit(void); int vmci_qp_broker_alloc(struct vmci_handle handle, u32 peer, u32 flags, u32 priv_flags, diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 23a0fff0df52..524f713f6017 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -306,7 +306,6 @@ static void netvsc_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, HV_DRV_VERSION, sizeof(info->version)); strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); } @@ -529,7 +528,6 @@ static int __init netvsc_drv_init(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); module_init(netvsc_drv_init); diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index dc82ef096f3b..70694ce38be2 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -37,7 +37,7 @@ config PARPORT_PC tristate "PC-style hardware" depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && !S390 && \ (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && \ - !XTENSA && !CRIS + !XTENSA && !CRIS && !H8300 ---help--- You should say Y here if you have a PC-style parallel port. All diff --git a/drivers/parport/parport_amiga.c b/drivers/parport/parport_amiga.c index 09503b8d12e6..26ecdea84fb5 100644 --- a/drivers/parport/parport_amiga.c +++ b/drivers/parport/parport_amiga.c @@ -232,7 +232,6 @@ static int __exit amiga_parallel_remove(struct platform_device *pdev) if (port->irq != PARPORT_IRQ_NONE) free_irq(IRQ_AMIGA_CIAA_FLG, port); parport_put_port(port); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 83ec1aa85964..1a28f5632797 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1879,7 +1879,6 @@ static void __exit storvsc_drv_exit(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver"); module_init(storvsc_drv_init); module_exit(storvsc_drv_exit); diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 5295be0342c1..5a90914d856d 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -1,5 +1,6 @@ menuconfig UIO tristate "Userspace I/O drivers" + depends on MMU help Enable this to allow the userspace driver core code to be built. This code allows userspace programs easy access to @@ -23,13 +24,6 @@ config UIO_CIF To compile this driver as a module, choose M here: the module will be called uio_cif. -config UIO_PDRV - tristate "Userspace I/O platform driver" - help - Generic platform driver for Userspace I/O devices. - - If you don't know what to do here, say N. - config UIO_PDRV_GENIRQ tristate "Userspace I/O platform driver with generic IRQ handling" help @@ -128,4 +122,17 @@ config UIO_PRUSS To compile this driver as a module, choose M here: the module will be called uio_pruss. +config UIO_MF624 + tristate "Humusoft MF624 DAQ PCI card driver" + depends on PCI + help + Userspace I/O interface for the Humusoft MF624 PCI card. + A sample userspace application using this driver is available + (among other MF624 related information and software components) + for download in a git repository: + + git clone git://rtime.felk.cvut.cz/mf6xx.git + + If you compile this as a module, it will be called uio_mf624. + endif diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index b354c539507a..d3218bde3aeb 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_UIO) += uio.o obj-$(CONFIG_UIO_CIF) += uio_cif.o -obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o obj-$(CONFIG_UIO_DMEM_GENIRQ) += uio_dmem_genirq.o obj-$(CONFIG_UIO_AEC) += uio_aec.o @@ -8,3 +7,4 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o obj-$(CONFIG_UIO_NETX) += uio_netx.o obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o +obj-$(CONFIG_UIO_MF624) += uio_mf624.o diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 3b96f18593b3..8abe78c0b16f 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -35,7 +35,6 @@ struct uio_device { atomic_t event; struct fasync_struct *async_queue; wait_queue_head_t wait; - int vma_count; struct uio_info *info; struct kobject *map_dir; struct kobject *portio_dir; @@ -593,18 +592,6 @@ static int uio_find_mem_index(struct vm_area_struct *vma) return -1; } -static void uio_vma_open(struct vm_area_struct *vma) -{ - struct uio_device *idev = vma->vm_private_data; - idev->vma_count++; -} - -static void uio_vma_close(struct vm_area_struct *vma) -{ - struct uio_device *idev = vma->vm_private_data; - idev->vma_count--; -} - static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct uio_device *idev = vma->vm_private_data; @@ -630,12 +617,23 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static const struct vm_operations_struct uio_vm_ops = { - .open = uio_vma_open, - .close = uio_vma_close, +static const struct vm_operations_struct uio_logical_vm_ops = { .fault = uio_vma_fault, }; +static int uio_mmap_logical(struct vm_area_struct *vma) +{ + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_ops = &uio_logical_vm_ops; + return 0; +} + +static const struct vm_operations_struct uio_physical_vm_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys, +#endif +}; + static int uio_mmap_physical(struct vm_area_struct *vma) { struct uio_device *idev = vma->vm_private_data; @@ -643,6 +641,8 @@ static int uio_mmap_physical(struct vm_area_struct *vma) if (mi < 0) return -EINVAL; + vma->vm_ops = &uio_physical_vm_ops; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); return remap_pfn_range(vma, @@ -652,14 +652,6 @@ static int uio_mmap_physical(struct vm_area_struct *vma) vma->vm_page_prot); } -static int uio_mmap_logical(struct vm_area_struct *vma) -{ - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_ops = &uio_vm_ops; - uio_vma_open(vma); - return 0; -} - static int uio_mmap(struct file *filep, struct vm_area_struct *vma) { struct uio_listener *listener = filep->private_data; diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 125d0e5a6887..1270f3b26139 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -146,7 +146,7 @@ static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on) static int uio_dmem_genirq_probe(struct platform_device *pdev) { - struct uio_dmem_genirq_pdata *pdata = pdev->dev.platform_data; + struct uio_dmem_genirq_pdata *pdata = dev_get_platdata(&pdev->dev); struct uio_info *uioinfo = &pdata->uioinfo; struct uio_dmem_genirq_platdata *priv; struct uio_mem *uiomem; diff --git a/drivers/uio/uio_mf624.c b/drivers/uio/uio_mf624.c new file mode 100644 index 000000000000..a1768b2f4493 --- /dev/null +++ b/drivers/uio/uio_mf624.c @@ -0,0 +1,247 @@ +/* + * UIO driver fo Humusoft MF624 DAQ card. + * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>, + * Czech Technical University in Prague + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/uio_driver.h> + +#define PCI_VENDOR_ID_HUMUSOFT 0x186c +#define PCI_DEVICE_ID_MF624 0x0624 +#define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c +#define PCI_SUBDEVICE_DEVICE 0x0624 + +/* BAR0 Interrupt control/status register */ +#define INTCSR 0x4C +#define INTCSR_ADINT_ENABLE (1 << 0) +#define INTCSR_CTR4INT_ENABLE (1 << 3) +#define INTCSR_PCIINT_ENABLE (1 << 6) +#define INTCSR_ADINT_STATUS (1 << 2) +#define INTCSR_CTR4INT_STATUS (1 << 5) + +enum mf624_interrupt_source {ADC, CTR4, ALL}; + +void mf624_disable_interrupt(enum mf624_interrupt_source source, + struct uio_info *info) +{ + void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; + + switch (source) { + case ADC: + iowrite32(ioread32(INTCSR_reg) + & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE), + INTCSR_reg); + break; + + case CTR4: + iowrite32(ioread32(INTCSR_reg) + & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE), + INTCSR_reg); + break; + + case ALL: + default: + iowrite32(ioread32(INTCSR_reg) + & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE + | INTCSR_PCIINT_ENABLE), + INTCSR_reg); + break; + } +} + +void mf624_enable_interrupt(enum mf624_interrupt_source source, + struct uio_info *info) +{ + void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; + + switch (source) { + case ADC: + iowrite32(ioread32(INTCSR_reg) + | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE, + INTCSR_reg); + break; + + case CTR4: + iowrite32(ioread32(INTCSR_reg) + | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE, + INTCSR_reg); + break; + + case ALL: + default: + iowrite32(ioread32(INTCSR_reg) + | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE + | INTCSR_PCIINT_ENABLE, + INTCSR_reg); + break; + } +} + +static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info) +{ + void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; + + if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE) + && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) { + mf624_disable_interrupt(ADC, info); + return IRQ_HANDLED; + } + + if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE) + && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) { + mf624_disable_interrupt(CTR4, info); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mf624_irqcontrol(struct uio_info *info, s32 irq_on) +{ + if (irq_on == 0) + mf624_disable_interrupt(ALL, info); + else if (irq_on == 1) + mf624_enable_interrupt(ALL, info); + + return 0; +} + +static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct uio_info *info; + + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (pci_enable_device(dev)) + goto out_free; + + if (pci_request_regions(dev, "mf624")) + goto out_disable; + + info->name = "mf624"; + info->version = "0.0.1"; + + /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */ + + /* BAR0 */ + info->mem[0].name = "PCI chipset, interrupts, status " + "bits, special functions"; + info->mem[0].addr = pci_resource_start(dev, 0); + if (!info->mem[0].addr) + goto out_release; + info->mem[0].size = pci_resource_len(dev, 0); + info->mem[0].memtype = UIO_MEM_PHYS; + info->mem[0].internal_addr = pci_ioremap_bar(dev, 0); + if (!info->mem[0].internal_addr) + goto out_release; + + /* BAR2 */ + info->mem[1].name = "ADC, DAC, DIO"; + info->mem[1].addr = pci_resource_start(dev, 2); + if (!info->mem[1].addr) + goto out_unmap0; + info->mem[1].size = pci_resource_len(dev, 2); + info->mem[1].memtype = UIO_MEM_PHYS; + info->mem[1].internal_addr = pci_ioremap_bar(dev, 2); + if (!info->mem[1].internal_addr) + goto out_unmap0; + + /* BAR4 */ + info->mem[2].name = "Counter/timer chip"; + info->mem[2].addr = pci_resource_start(dev, 4); + if (!info->mem[2].addr) + goto out_unmap1; + info->mem[2].size = pci_resource_len(dev, 4); + info->mem[2].memtype = UIO_MEM_PHYS; + info->mem[2].internal_addr = pci_ioremap_bar(dev, 4); + if (!info->mem[2].internal_addr) + goto out_unmap1; + + info->irq = dev->irq; + info->irq_flags = IRQF_SHARED; + info->handler = mf624_irq_handler; + + info->irqcontrol = mf624_irqcontrol; + + if (uio_register_device(&dev->dev, info)) + goto out_unmap2; + + pci_set_drvdata(dev, info); + + return 0; + +out_unmap2: + iounmap(info->mem[2].internal_addr); +out_unmap1: + iounmap(info->mem[1].internal_addr); +out_unmap0: + iounmap(info->mem[0].internal_addr); + +out_release: + pci_release_regions(dev); + +out_disable: + pci_disable_device(dev); + +out_free: + kfree(info); + return -ENODEV; +} + +static void mf624_pci_remove(struct pci_dev *dev) +{ + struct uio_info *info = pci_get_drvdata(dev); + + mf624_disable_interrupt(ALL, info); + + uio_unregister_device(info); + pci_release_regions(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + + iounmap(info->mem[0].internal_addr); + iounmap(info->mem[1].internal_addr); + iounmap(info->mem[2].internal_addr); + + kfree(info); +} + +static DEFINE_PCI_DEVICE_TABLE(mf624_pci_id) = { + { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) }, + { 0, } +}; + +static struct pci_driver mf624_pci_driver = { + .name = "mf624", + .id_table = mf624_pci_id, + .probe = mf624_pci_probe, + .remove = mf624_pci_remove, +}; +MODULE_DEVICE_TABLE(pci, mf624_pci_id); + +module_pci_driver(mf624_pci_driver); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>"); diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c deleted file mode 100644 index 39be9e061700..000000000000 --- a/drivers/uio/uio_pdrv.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * drivers/uio/uio_pdrv.c - * - * Copyright (C) 2008 by Digi International Inc. - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ -#include <linux/platform_device.h> -#include <linux/uio_driver.h> -#include <linux/stringify.h> -#include <linux/module.h> -#include <linux/slab.h> - -#define DRIVER_NAME "uio_pdrv" - -struct uio_platdata { - struct uio_info *uioinfo; -}; - -static int uio_pdrv_probe(struct platform_device *pdev) -{ - struct uio_info *uioinfo = pdev->dev.platform_data; - struct uio_platdata *pdata; - struct uio_mem *uiomem; - int ret = -ENODEV; - int i; - - if (!uioinfo || !uioinfo->name || !uioinfo->version) { - dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__); - goto err_uioinfo; - } - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__); - goto err_alloc_pdata; - } - - pdata->uioinfo = uioinfo; - - uiomem = &uioinfo->mem[0]; - - for (i = 0; i < pdev->num_resources; ++i) { - struct resource *r = &pdev->resource[i]; - - if (r->flags != IORESOURCE_MEM) - continue; - - if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) { - dev_warn(&pdev->dev, "device has more than " - __stringify(MAX_UIO_MAPS) - " I/O memory resources.\n"); - break; - } - - uiomem->memtype = UIO_MEM_PHYS; - uiomem->addr = r->start; - uiomem->size = resource_size(r); - uiomem->name = r->name; - ++uiomem; - } - - while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) { - uiomem->size = 0; - ++uiomem; - } - - pdata->uioinfo->priv = pdata; - - ret = uio_register_device(&pdev->dev, pdata->uioinfo); - - if (ret) { - kfree(pdata); -err_alloc_pdata: -err_uioinfo: - return ret; - } - - platform_set_drvdata(pdev, pdata); - - return 0; -} - -static int uio_pdrv_remove(struct platform_device *pdev) -{ - struct uio_platdata *pdata = platform_get_drvdata(pdev); - - uio_unregister_device(pdata->uioinfo); - - kfree(pdata); - - return 0; -} - -static struct platform_driver uio_pdrv = { - .probe = uio_pdrv_probe, - .remove = uio_pdrv_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(uio_pdrv); - -MODULE_AUTHOR("Uwe Kleine-Koenig"); -MODULE_DESCRIPTION("Userspace I/O platform driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 4eb8eaf71be8..90ff17a0202f 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -104,7 +104,7 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on) static int uio_pdrv_genirq_probe(struct platform_device *pdev) { - struct uio_info *uioinfo = pdev->dev.platform_data; + struct uio_info *uioinfo = dev_get_platdata(&pdev->dev); struct uio_pdrv_genirq_platdata *priv; struct uio_mem *uiomem; int ret = -EINVAL; diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 21f7a72301e4..f519da9034b7 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -121,7 +121,7 @@ static int pruss_probe(struct platform_device *dev) struct uio_pruss_dev *gdev; struct resource *regs_prussio; int ret = -ENODEV, cnt = 0, len; - struct uio_pruss_pdata *pdata = dev->dev.platform_data; + struct uio_pruss_pdata *pdata = dev_get_platdata(&dev->dev); gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL); if (!gdev) @@ -224,7 +224,6 @@ static int pruss_remove(struct platform_device *dev) struct uio_pruss_dev *gdev = platform_get_drvdata(dev); pruss_cleanup(dev, gdev); - platform_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 3e225d5846f6..f969ea266acb 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -1,6 +1,7 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on (USB || USB_GADGET) && GENERIC_HARDIRQS && HAS_DMA + depends on EXTCON select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD help Say Y or M here if your system has a Dual Role SuperSpeed diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ecd99451ee90..7f7ea62e961b 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -23,13 +23,15 @@ #include <linux/spinlock.h> #include <linux/platform_device.h> #include <linux/platform_data/dwc3-omap.h> -#include <linux/usb/dwc3-omap.h> #include <linux/pm_runtime.h> #include <linux/dma-mapping.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/extcon.h> +#include <linux/extcon/of_extcon.h> +#include <linux/regulator/consumer.h> #include <linux/usb/otg.h> @@ -135,9 +137,21 @@ struct dwc3_omap { u32 revision; u32 dma_status:1; + + struct extcon_specific_cable_nb extcon_vbus_dev; + struct extcon_specific_cable_nb extcon_id_dev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; + + struct regulator *vbus_reg; }; -static struct dwc3_omap *_omap; +enum omap_dwc3_vbus_id_status { + OMAP_DWC3_ID_FLOAT, + OMAP_DWC3_ID_GROUND, + OMAP_DWC3_VBUS_OFF, + OMAP_DWC3_VBUS_VALID, +}; static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset) { @@ -201,18 +215,24 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value) omap->irq0_offset, value); } -int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, + enum omap_dwc3_vbus_id_status status) { - u32 val; - struct dwc3_omap *omap = _omap; - - if (!omap) - return -EPROBE_DEFER; + int ret; + u32 val; switch (status) { case OMAP_DWC3_ID_GROUND: dev_dbg(omap->dev, "ID GND\n"); + if (omap->vbus_reg) { + ret = regulator_enable(omap->vbus_reg); + if (ret) { + dev_dbg(omap->dev, "regulator enable failed\n"); + return; + } + } + val = dwc3_omap_read_utmi_status(omap); val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID @@ -235,6 +255,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) break; case OMAP_DWC3_ID_FLOAT: + if (omap->vbus_reg) + regulator_disable(omap->vbus_reg); + case OMAP_DWC3_VBUS_OFF: dev_dbg(omap->dev, "VBUS Disconnect\n"); @@ -248,12 +271,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) break; default: - dev_dbg(omap->dev, "ID float\n"); + dev_dbg(omap->dev, "invalid state\n"); } - - return 0; } -EXPORT_SYMBOL_GPL(dwc3_omap_mailbox); static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) { @@ -346,6 +366,32 @@ static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32); +static int dwc3_omap_id_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, id_nb); + + if (event) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + else + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_FLOAT); + + return NOTIFY_DONE; +} + +static int dwc3_omap_vbus_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, vbus_nb); + + if (event) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + else + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_OFF); + + return NOTIFY_DONE; +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -353,6 +399,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct resource *res; struct device *dev = &pdev->dev; + struct extcon_dev *edev; + struct regulator *vbus_reg = NULL; int ret = -ENOMEM; int irq; @@ -393,19 +441,22 @@ static int dwc3_omap_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + if (of_property_read_bool(node, "vbus-supply")) { + vbus_reg = devm_regulator_get(dev, "vbus"); + if (IS_ERR(vbus_reg)) { + dev_err(dev, "vbus init failed\n"); + return PTR_ERR(vbus_reg); + } + } + spin_lock_init(&omap->lock); omap->dev = dev; omap->irq = irq; omap->base = base; + omap->vbus_reg = vbus_reg; dev->dma_mask = &dwc3_omap_dma_mask; - /* - * REVISIT if we ever have two instances of the wrapper, we will be - * in big trouble - */ - _omap = omap; - pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { @@ -480,14 +531,46 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); + if (of_property_read_bool(node, "extcon")) { + edev = of_extcon_get_extcon_dev(dev, 0); + if (IS_ERR(edev)) { + dev_vdbg(dev, "couldn't get extcon device\n"); + ret = PTR_ERR(edev); + goto err2; + } + + omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; + ret = extcon_register_interest(&omap->extcon_vbus_dev, + edev->name, "USB", &omap->vbus_nb); + if (ret < 0) + dev_vdbg(dev, "failed to register notifier for USB\n"); + omap->id_nb.notifier_call = dwc3_omap_id_notifier; + ret = extcon_register_interest(&omap->extcon_id_dev, edev->name, + "USB-HOST", &omap->id_nb); + if (ret < 0) + dev_vdbg(dev, + "failed to register notifier for USB-HOST\n"); + + if (extcon_get_cable_state(edev, "USB") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + if (extcon_get_cable_state(edev, "USB-HOST") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + } + ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { dev_err(&pdev->dev, "failed to create dwc3 core\n"); - goto err2; + goto err3; } return 0; +err3: + if (omap->extcon_vbus_dev.edev) + extcon_unregister_interest(&omap->extcon_vbus_dev); + if (omap->extcon_id_dev.edev) + extcon_unregister_interest(&omap->extcon_id_dev); + err2: dwc3_omap_disable_irqs(omap); @@ -504,6 +587,10 @@ static int dwc3_omap_remove(struct platform_device *pdev) { struct dwc3_omap *omap = platform_get_drvdata(pdev); + if (omap->extcon_vbus_dev.edev) + extcon_unregister_interest(&omap->extcon_vbus_dev); + if (omap->extcon_id_dev.edev) + extcon_unregister_interest(&omap->extcon_id_dev); dwc3_omap_disable_irqs(omap); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c index d4d2c5fe2488..8ac99b87c07e 100644 --- a/drivers/video/hyperv_fb.c +++ b/drivers/video/hyperv_fb.c @@ -825,5 +825,4 @@ module_init(hvfb_drv_init); module_exit(hvfb_drv_exit); MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver"); diff --git a/drivers/vme/boards/vme_vmivme7805.c b/drivers/vme/boards/vme_vmivme7805.c index dd22b5072e21..cf74aee2cef0 100644 --- a/drivers/vme/boards/vme_vmivme7805.c +++ b/drivers/vme/boards/vme_vmivme7805.c @@ -23,7 +23,7 @@ static int vmic_probe(struct pci_dev *, const struct pci_device_id *); static void vmic_remove(struct pci_dev *); /** Base address to access FPGA register */ -static void *vmic_base; +static void __iomem *vmic_base; static const char driver_name[] = "vmivme_7805"; diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index 64bfea314429..f8448573d030 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -243,6 +243,8 @@ static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge) static void ca91cx42_irq_exit(struct ca91cx42_driver *bridge, struct pci_dev *pdev) { + struct vme_bridge *ca91cx42_bridge; + /* Disable interrupts from PCI to VME */ iowrite32(0, bridge->base + VINT_EN); @@ -251,7 +253,9 @@ static void ca91cx42_irq_exit(struct ca91cx42_driver *bridge, /* Clear Any Pending PCI Interrupts */ iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); - free_irq(pdev->irq, pdev); + ca91cx42_bridge = container_of((void *)bridge, struct vme_bridge, + driver_priv); + free_irq(pdev->irq, ca91cx42_bridge); } static int ca91cx42_iack_received(struct ca91cx42_driver *bridge, int level) @@ -856,7 +860,7 @@ static ssize_t ca91cx42_master_read(struct vme_master_resource *image, void *buf, size_t count, loff_t offset) { ssize_t retval; - void *addr = image->kern_base + offset; + void __iomem *addr = image->kern_base + offset; unsigned int done = 0; unsigned int count32; @@ -916,7 +920,7 @@ static ssize_t ca91cx42_master_write(struct vme_master_resource *image, void *buf, size_t count, loff_t offset) { ssize_t retval; - void *addr = image->kern_base + offset; + void __iomem *addr = image->kern_base + offset; unsigned int done = 0; unsigned int count32; diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 94c892f27be4..9cf88337e4e9 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -1267,7 +1267,7 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, u32 aspace, cycle, dwidth; struct vme_bus_error *vme_err = NULL; struct vme_bridge *tsi148_bridge; - void *addr = image->kern_base + offset; + void __iomem *addr = image->kern_base + offset; unsigned int done = 0; unsigned int count32; @@ -1348,7 +1348,7 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf, int retval = 0, enabled; unsigned long long vme_base, size; u32 aspace, cycle, dwidth; - void *addr = image->kern_base + offset; + void __iomem *addr = image->kern_base + offset; unsigned int done = 0; unsigned int count32; |