diff options
Diffstat (limited to 'drivers/misc')
63 files changed, 7299 insertions, 212 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8dacd4c9ee87..e760715bd9cb 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -537,4 +537,5 @@ source "drivers/misc/carma/Kconfig" | |||
| 537 | source "drivers/misc/altera-stapl/Kconfig" | 537 | source "drivers/misc/altera-stapl/Kconfig" |
| 538 | source "drivers/misc/mei/Kconfig" | 538 | source "drivers/misc/mei/Kconfig" |
| 539 | source "drivers/misc/vmw_vmci/Kconfig" | 539 | source "drivers/misc/vmw_vmci/Kconfig" |
| 540 | source "drivers/misc/mic/Kconfig" | ||
| 540 | endmenu | 541 | endmenu |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c235d5b68311..0b7ea3ea8bb8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
| @@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI) += mei/ | |||
| 53 | obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ | 53 | obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ |
| 54 | obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o | 54 | obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o |
| 55 | obj-$(CONFIG_SRAM) += sram.o | 55 | obj-$(CONFIG_SRAM) += sram.o |
| 56 | obj-y += mic/ | ||
diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index 1256a4bf1c04..b7ebf8021d99 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c | |||
| @@ -297,7 +297,7 @@ static int __init charlcd_probe(struct platform_device *pdev) | |||
| 297 | lcd->irq = platform_get_irq(pdev, 0); | 297 | lcd->irq = platform_get_irq(pdev, 0); |
| 298 | /* If no IRQ is supplied, we'll survive without it */ | 298 | /* If no IRQ is supplied, we'll survive without it */ |
| 299 | if (lcd->irq >= 0) { | 299 | if (lcd->irq >= 0) { |
| 300 | if (request_irq(lcd->irq, charlcd_interrupt, IRQF_DISABLED, | 300 | if (request_irq(lcd->irq, charlcd_interrupt, 0, |
| 301 | DRIVERNAME, lcd)) { | 301 | DRIVERNAME, lcd)) { |
| 302 | ret = -EIO; | 302 | ret = -EIO; |
| 303 | goto out_no_irq; | 303 | goto out_no_irq; |
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 494d0500bda6..a6dc56e1bc58 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c | |||
| @@ -90,8 +90,10 @@ int pwm_channel_alloc(int index, struct pwm_channel *ch) | |||
| 90 | unsigned long flags; | 90 | unsigned long flags; |
| 91 | int status = 0; | 91 | int status = 0; |
| 92 | 92 | ||
| 93 | /* insist on PWM init, with this signal pinned out */ | 93 | if (!pwm) |
| 94 | if (!pwm || !(pwm->mask & 1 << index)) | 94 | return -EPROBE_DEFER; |
| 95 | |||
| 96 | if (!(pwm->mask & 1 << index)) | ||
| 95 | return -ENODEV; | 97 | return -ENODEV; |
| 96 | 98 | ||
| 97 | if (index < 0 || index >= PWM_NCHAN || !ch) | 99 | if (index < 0 || index >= PWM_NCHAN || !ch) |
diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index 057580e026c0..48ea33d15a79 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
| 24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
| 25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
| 26 | #include <linux/of.h> | ||
| 26 | 27 | ||
| 27 | #define BH1780_REG_CONTROL 0x80 | 28 | #define BH1780_REG_CONTROL 0x80 |
| 28 | #define BH1780_REG_PARTID 0x8A | 29 | #define BH1780_REG_PARTID 0x8A |
| @@ -244,6 +245,15 @@ static const struct i2c_device_id bh1780_id[] = { | |||
| 244 | { }, | 245 | { }, |
| 245 | }; | 246 | }; |
| 246 | 247 | ||
| 248 | #ifdef CONFIG_OF | ||
| 249 | static const struct of_device_id of_bh1780_match[] = { | ||
| 250 | { .compatible = "rohm,bh1780gli", }, | ||
| 251 | {}, | ||
| 252 | }; | ||
| 253 | |||
| 254 | MODULE_DEVICE_TABLE(of, of_bh1780_match); | ||
| 255 | #endif | ||
| 256 | |||
| 247 | static struct i2c_driver bh1780_driver = { | 257 | static struct i2c_driver bh1780_driver = { |
| 248 | .probe = bh1780_probe, | 258 | .probe = bh1780_probe, |
| 249 | .remove = bh1780_remove, | 259 | .remove = bh1780_remove, |
| @@ -251,6 +261,7 @@ static struct i2c_driver bh1780_driver = { | |||
| 251 | .driver = { | 261 | .driver = { |
| 252 | .name = "bh1780", | 262 | .name = "bh1780", |
| 253 | .pm = &bh1780_pm, | 263 | .pm = &bh1780_pm, |
| 264 | .of_match_table = of_match_ptr(of_bh1780_match), | ||
| 254 | }, | 265 | }, |
| 255 | }; | 266 | }; |
| 256 | 267 | ||
diff --git a/drivers/misc/bmp085.c b/drivers/misc/bmp085.c index 849e2fed4da2..2704d885a9b3 100644 --- a/drivers/misc/bmp085.c +++ b/drivers/misc/bmp085.c | |||
| @@ -374,7 +374,7 @@ int bmp085_detect(struct device *dev) | |||
| 374 | } | 374 | } |
| 375 | EXPORT_SYMBOL_GPL(bmp085_detect); | 375 | EXPORT_SYMBOL_GPL(bmp085_detect); |
| 376 | 376 | ||
| 377 | static void __init bmp085_get_of_properties(struct bmp085_data *data) | 377 | static void bmp085_get_of_properties(struct bmp085_data *data) |
| 378 | { | 378 | { |
| 379 | #ifdef CONFIG_OF | 379 | #ifdef CONFIG_OF |
| 380 | struct device_node *np = data->dev->of_node; | 380 | struct device_node *np = data->dev->of_node; |
diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 2e50f811ff59..fb397e7d1cce 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c | |||
| @@ -176,7 +176,7 @@ static int cb710_suspend(struct pci_dev *pdev, pm_message_t state) | |||
| 176 | { | 176 | { |
| 177 | struct cb710_chip *chip = pci_get_drvdata(pdev); | 177 | struct cb710_chip *chip = pci_get_drvdata(pdev); |
| 178 | 178 | ||
| 179 | free_irq(pdev->irq, chip); | 179 | devm_free_irq(&pdev->dev, pdev->irq, chip); |
| 180 | pci_save_state(pdev); | 180 | pci_save_state(pdev); |
| 181 | pci_disable_device(pdev); | 181 | pci_disable_device(pdev); |
| 182 | if (state.event & PM_EVENT_SLEEP) | 182 | if (state.event & PM_EVENT_SLEEP) |
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 04f2e1fa9dd1..9536852fd4c6 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig | |||
| @@ -96,4 +96,17 @@ config EEPROM_DIGSY_MTC_CFG | |||
| 96 | 96 | ||
| 97 | If unsure, say N. | 97 | If unsure, say N. |
| 98 | 98 | ||
| 99 | config EEPROM_SUNXI_SID | ||
| 100 | tristate "Allwinner sunxi security ID support" | ||
| 101 | depends on ARCH_SUNXI && SYSFS | ||
| 102 | help | ||
| 103 | This is a driver for the 'security ID' available on various Allwinner | ||
| 104 | devices. | ||
| 105 | |||
| 106 | Due to the potential risks involved with changing e-fuses, | ||
| 107 | this driver is read-only. | ||
| 108 | |||
| 109 | This driver can also be built as a module. If so, the module | ||
| 110 | will be called sunxi_sid. | ||
| 111 | |||
| 99 | endmenu | 112 | endmenu |
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index fc1e81d29267..9507aec95e94 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile | |||
| @@ -4,4 +4,5 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o | |||
| 4 | obj-$(CONFIG_EEPROM_MAX6875) += max6875.o | 4 | obj-$(CONFIG_EEPROM_MAX6875) += max6875.o |
| 5 | obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o | 5 | obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o |
| 6 | obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o | 6 | obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o |
| 7 | obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o | ||
| 7 | obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o | 8 | obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o |
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 5d4fd69d04ca..94b8a3324319 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c | |||
| @@ -428,6 +428,9 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj, | |||
| 428 | { | 428 | { |
| 429 | struct at24_data *at24; | 429 | struct at24_data *at24; |
| 430 | 430 | ||
| 431 | if (unlikely(off >= attr->size)) | ||
| 432 | return -EFBIG; | ||
| 433 | |||
| 431 | at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); | 434 | at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); |
| 432 | return at24_write(at24, buf, off, count); | 435 | return at24_write(at24, buf, off, count); |
| 433 | } | 436 | } |
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 840b3594a5ae..4f3bca1003a1 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c | |||
| @@ -462,10 +462,17 @@ static int at25_remove(struct spi_device *spi) | |||
| 462 | 462 | ||
| 463 | /*-------------------------------------------------------------------------*/ | 463 | /*-------------------------------------------------------------------------*/ |
| 464 | 464 | ||
| 465 | static const struct of_device_id at25_of_match[] = { | ||
| 466 | { .compatible = "atmel,at25", }, | ||
| 467 | { } | ||
| 468 | }; | ||
| 469 | MODULE_DEVICE_TABLE(of, at25_of_match); | ||
| 470 | |||
| 465 | static struct spi_driver at25_driver = { | 471 | static struct spi_driver at25_driver = { |
| 466 | .driver = { | 472 | .driver = { |
| 467 | .name = "at25", | 473 | .name = "at25", |
| 468 | .owner = THIS_MODULE, | 474 | .owner = THIS_MODULE, |
| 475 | .of_match_table = at25_of_match, | ||
| 469 | }, | 476 | }, |
| 470 | .probe = at25_probe, | 477 | .probe = at25_probe, |
| 471 | .remove = at25_remove, | 478 | .remove = at25_remove, |
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 94cfc1212577..3a015abb444a 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c | |||
| @@ -202,7 +202,7 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj, | |||
| 202 | edev = dev_get_drvdata(dev); | 202 | edev = dev_get_drvdata(dev); |
| 203 | 203 | ||
| 204 | if (unlikely(off >= edev->bin.size)) | 204 | if (unlikely(off >= edev->bin.size)) |
| 205 | return 0; | 205 | return -EFBIG; |
| 206 | if ((off + count) > edev->bin.size) | 206 | if ((off + count) > edev->bin.size) |
| 207 | count = edev->bin.size - off; | 207 | count = edev->bin.size - off; |
| 208 | if (unlikely(!count)) | 208 | if (unlikely(!count)) |
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c new file mode 100644 index 000000000000..9c34e5704304 --- /dev/null +++ b/drivers/misc/eeprom/sunxi_sid.c | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> | ||
| 3 | * http://www.linux-sunxi.org | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation; either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * This driver exposes the Allwinner security ID, efuses exported in byte- | ||
| 16 | * sized chunks. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/compiler.h> | ||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/err.h> | ||
| 22 | #include <linux/export.h> | ||
| 23 | #include <linux/fs.h> | ||
| 24 | #include <linux/init.h> | ||
| 25 | #include <linux/io.h> | ||
| 26 | #include <linux/kernel.h> | ||
| 27 | #include <linux/kobject.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/of_device.h> | ||
| 30 | #include <linux/platform_device.h> | ||
| 31 | #include <linux/random.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | #include <linux/stat.h> | ||
| 34 | #include <linux/sysfs.h> | ||
| 35 | #include <linux/types.h> | ||
| 36 | |||
| 37 | #define DRV_NAME "sunxi-sid" | ||
| 38 | |||
| 39 | struct sunxi_sid_data { | ||
| 40 | void __iomem *reg_base; | ||
| 41 | unsigned int keysize; | ||
| 42 | }; | ||
| 43 | |||
| 44 | /* We read the entire key, due to a 32 bit read alignment requirement. Since we | ||
| 45 | * want to return the requested byte, this results in somewhat slower code and | ||
| 46 | * uses 4 times more reads as needed but keeps code simpler. Since the SID is | ||
| 47 | * only very rarely probed, this is not really an issue. | ||
| 48 | */ | ||
| 49 | static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data, | ||
| 50 | const unsigned int offset) | ||
| 51 | { | ||
| 52 | u32 sid_key; | ||
| 53 | |||
| 54 | if (offset >= sid_data->keysize) | ||
| 55 | return 0; | ||
| 56 | |||
| 57 | sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4)); | ||
| 58 | sid_key >>= (offset % 4) * 8; | ||
| 59 | |||
| 60 | return sid_key; /* Only return the last byte */ | ||
| 61 | } | ||
| 62 | |||
| 63 | static ssize_t sid_read(struct file *fd, struct kobject *kobj, | ||
| 64 | struct bin_attribute *attr, char *buf, | ||
| 65 | loff_t pos, size_t size) | ||
| 66 | { | ||
| 67 | struct platform_device *pdev; | ||
| 68 | struct sunxi_sid_data *sid_data; | ||
| 69 | int i; | ||
| 70 | |||
| 71 | pdev = to_platform_device(kobj_to_dev(kobj)); | ||
| 72 | sid_data = platform_get_drvdata(pdev); | ||
| 73 | |||
| 74 | if (pos < 0 || pos >= sid_data->keysize) | ||
| 75 | return 0; | ||
| 76 | if (size > sid_data->keysize - pos) | ||
| 77 | size = sid_data->keysize - pos; | ||
| 78 | |||
| 79 | for (i = 0; i < size; i++) | ||
| 80 | buf[i] = sunxi_sid_read_byte(sid_data, pos + i); | ||
| 81 | |||
| 82 | return i; | ||
| 83 | } | ||
| 84 | |||
| 85 | static struct bin_attribute sid_bin_attr = { | ||
| 86 | .attr = { .name = "eeprom", .mode = S_IRUGO, }, | ||
| 87 | .read = sid_read, | ||
| 88 | }; | ||
| 89 | |||
| 90 | static int sunxi_sid_remove(struct platform_device *pdev) | ||
| 91 | { | ||
| 92 | device_remove_bin_file(&pdev->dev, &sid_bin_attr); | ||
| 93 | dev_dbg(&pdev->dev, "driver unloaded\n"); | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | static const struct of_device_id sunxi_sid_of_match[] = { | ||
| 99 | { .compatible = "allwinner,sun4i-sid", .data = (void *)16}, | ||
| 100 | { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512}, | ||
| 101 | {/* sentinel */}, | ||
| 102 | }; | ||
| 103 | MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); | ||
| 104 | |||
| 105 | static int sunxi_sid_probe(struct platform_device *pdev) | ||
| 106 | { | ||
| 107 | struct sunxi_sid_data *sid_data; | ||
| 108 | struct resource *res; | ||
| 109 | const struct of_device_id *of_dev_id; | ||
| 110 | u8 *entropy; | ||
| 111 | unsigned int i; | ||
| 112 | |||
| 113 | sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data), | ||
| 114 | GFP_KERNEL); | ||
| 115 | if (!sid_data) | ||
| 116 | return -ENOMEM; | ||
| 117 | |||
| 118 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 119 | sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res); | ||
| 120 | if (IS_ERR(sid_data->reg_base)) | ||
| 121 | return PTR_ERR(sid_data->reg_base); | ||
| 122 | |||
| 123 | of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev); | ||
| 124 | if (!of_dev_id) | ||
| 125 | return -ENODEV; | ||
| 126 | sid_data->keysize = (int)of_dev_id->data; | ||
| 127 | |||
| 128 | platform_set_drvdata(pdev, sid_data); | ||
| 129 | |||
| 130 | sid_bin_attr.size = sid_data->keysize; | ||
| 131 | if (device_create_bin_file(&pdev->dev, &sid_bin_attr)) | ||
| 132 | return -ENODEV; | ||
| 133 | |||
| 134 | entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL); | ||
| 135 | for (i = 0; i < sid_data->keysize; i++) | ||
| 136 | entropy[i] = sunxi_sid_read_byte(sid_data, i); | ||
| 137 | add_device_randomness(entropy, sid_data->keysize); | ||
| 138 | kfree(entropy); | ||
| 139 | |||
| 140 | dev_dbg(&pdev->dev, "loaded\n"); | ||
| 141 | |||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | static struct platform_driver sunxi_sid_driver = { | ||
| 146 | .probe = sunxi_sid_probe, | ||
| 147 | .remove = sunxi_sid_remove, | ||
| 148 | .driver = { | ||
| 149 | .name = DRV_NAME, | ||
| 150 | .owner = THIS_MODULE, | ||
| 151 | .of_match_table = sunxi_sid_of_match, | ||
| 152 | }, | ||
| 153 | }; | ||
| 154 | module_platform_driver(sunxi_sid_driver); | ||
| 155 | |||
| 156 | MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); | ||
| 157 | MODULE_DESCRIPTION("Allwinner sunxi security id driver"); | ||
| 158 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 0346d87c5fed..6b3bf9ab051d 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c | |||
| @@ -153,7 +153,6 @@ error_ioremap: | |||
| 153 | error_heartbeat: | 153 | error_heartbeat: |
| 154 | ibmasm_event_buffer_exit(sp); | 154 | ibmasm_event_buffer_exit(sp); |
| 155 | error_eventbuffer: | 155 | error_eventbuffer: |
| 156 | pci_set_drvdata(pdev, NULL); | ||
| 157 | kfree(sp); | 156 | kfree(sp); |
| 158 | error_kmalloc: | 157 | error_kmalloc: |
| 159 | pci_release_regions(pdev); | 158 | pci_release_regions(pdev); |
| @@ -165,7 +164,7 @@ error_resources: | |||
| 165 | 164 | ||
| 166 | static void ibmasm_remove_one(struct pci_dev *pdev) | 165 | static void ibmasm_remove_one(struct pci_dev *pdev) |
| 167 | { | 166 | { |
| 168 | struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev); | 167 | struct service_processor *sp = pci_get_drvdata(pdev); |
| 169 | 168 | ||
| 170 | dbg("Unregistering UART\n"); | 169 | dbg("Unregistering UART\n"); |
| 171 | ibmasm_unregister_uart(sp); | 170 | ibmasm_unregister_uart(sp); |
| @@ -182,7 +181,6 @@ static void ibmasm_remove_one(struct pci_dev *pdev) | |||
| 182 | ibmasm_free_remote_input_dev(sp); | 181 | ibmasm_free_remote_input_dev(sp); |
| 183 | iounmap(sp->base_address); | 182 | iounmap(sp->base_address); |
| 184 | ibmasm_event_buffer_exit(sp); | 183 | ibmasm_event_buffer_exit(sp); |
| 185 | pci_set_drvdata(pdev, NULL); | ||
| 186 | kfree(sp); | 184 | kfree(sp); |
| 187 | pci_release_regions(pdev); | 185 | pci_release_regions(pdev); |
| 188 | pci_disable_device(pdev); | 186 | pci_disable_device(pdev); |
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 2fc0586ce3bb..a2edb2ee0921 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c | |||
| @@ -44,13 +44,25 @@ | |||
| 44 | #include <scsi/scsi_cmnd.h> | 44 | #include <scsi/scsi_cmnd.h> |
| 45 | #include <linux/debugfs.h> | 45 | #include <linux/debugfs.h> |
| 46 | #include <linux/vmalloc.h> | 46 | #include <linux/vmalloc.h> |
| 47 | #include <linux/mman.h> | ||
| 47 | 48 | ||
| 48 | #ifdef CONFIG_IDE | 49 | #ifdef CONFIG_IDE |
| 49 | #include <linux/ide.h> | 50 | #include <linux/ide.h> |
| 50 | #endif | 51 | #endif |
| 51 | 52 | ||
| 53 | /* | ||
| 54 | * Make sure our attempts to over run the kernel stack doesn't trigger | ||
| 55 | * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we | ||
| 56 | * recurse past the end of THREAD_SIZE by default. | ||
| 57 | */ | ||
| 58 | #if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0) | ||
| 59 | #define REC_STACK_SIZE (CONFIG_FRAME_WARN / 2) | ||
| 60 | #else | ||
| 61 | #define REC_STACK_SIZE (THREAD_SIZE / 8) | ||
| 62 | #endif | ||
| 63 | #define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2) | ||
| 64 | |||
| 52 | #define DEFAULT_COUNT 10 | 65 | #define DEFAULT_COUNT 10 |
| 53 | #define REC_NUM_DEFAULT 10 | ||
| 54 | #define EXEC_SIZE 64 | 66 | #define EXEC_SIZE 64 |
| 55 | 67 | ||
| 56 | enum cname { | 68 | enum cname { |
| @@ -86,6 +98,9 @@ enum ctype { | |||
| 86 | CT_EXEC_STACK, | 98 | CT_EXEC_STACK, |
| 87 | CT_EXEC_KMALLOC, | 99 | CT_EXEC_KMALLOC, |
| 88 | CT_EXEC_VMALLOC, | 100 | CT_EXEC_VMALLOC, |
| 101 | CT_EXEC_USERSPACE, | ||
| 102 | CT_ACCESS_USERSPACE, | ||
| 103 | CT_WRITE_RO, | ||
| 89 | }; | 104 | }; |
| 90 | 105 | ||
| 91 | static char* cp_name[] = { | 106 | static char* cp_name[] = { |
| @@ -119,6 +134,9 @@ static char* cp_type[] = { | |||
| 119 | "EXEC_STACK", | 134 | "EXEC_STACK", |
| 120 | "EXEC_KMALLOC", | 135 | "EXEC_KMALLOC", |
| 121 | "EXEC_VMALLOC", | 136 | "EXEC_VMALLOC", |
| 137 | "EXEC_USERSPACE", | ||
| 138 | "ACCESS_USERSPACE", | ||
| 139 | "WRITE_RO", | ||
| 122 | }; | 140 | }; |
| 123 | 141 | ||
| 124 | static struct jprobe lkdtm; | 142 | static struct jprobe lkdtm; |
| @@ -139,9 +157,10 @@ static DEFINE_SPINLOCK(lock_me_up); | |||
| 139 | 157 | ||
| 140 | static u8 data_area[EXEC_SIZE]; | 158 | static u8 data_area[EXEC_SIZE]; |
| 141 | 159 | ||
| 160 | static const unsigned long rodata = 0xAA55AA55; | ||
| 161 | |||
| 142 | module_param(recur_count, int, 0644); | 162 | module_param(recur_count, int, 0644); |
| 143 | MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ | 163 | MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test"); |
| 144 | "default is 10"); | ||
| 145 | module_param(cpoint_name, charp, 0444); | 164 | module_param(cpoint_name, charp, 0444); |
| 146 | MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); | 165 | MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); |
| 147 | module_param(cpoint_type, charp, 0444); | 166 | module_param(cpoint_type, charp, 0444); |
| @@ -280,16 +299,16 @@ static int lkdtm_parse_commandline(void) | |||
| 280 | return -EINVAL; | 299 | return -EINVAL; |
| 281 | } | 300 | } |
| 282 | 301 | ||
| 283 | static int recursive_loop(int a) | 302 | static int recursive_loop(int remaining) |
| 284 | { | 303 | { |
| 285 | char buf[1024]; | 304 | char buf[REC_STACK_SIZE]; |
| 286 | 305 | ||
| 287 | memset(buf,0xFF,1024); | 306 | /* Make sure compiler does not optimize this away. */ |
| 288 | recur_count--; | 307 | memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE); |
| 289 | if (!recur_count) | 308 | if (!remaining) |
| 290 | return 0; | 309 | return 0; |
| 291 | else | 310 | else |
| 292 | return recursive_loop(a); | 311 | return recursive_loop(remaining - 1); |
| 293 | } | 312 | } |
| 294 | 313 | ||
| 295 | static void do_nothing(void) | 314 | static void do_nothing(void) |
| @@ -297,6 +316,14 @@ static void do_nothing(void) | |||
| 297 | return; | 316 | return; |
| 298 | } | 317 | } |
| 299 | 318 | ||
| 319 | static noinline void corrupt_stack(void) | ||
| 320 | { | ||
| 321 | /* Use default char array length that triggers stack protection. */ | ||
| 322 | char data[8]; | ||
| 323 | |||
| 324 | memset((void *)data, 0, 64); | ||
| 325 | } | ||
| 326 | |||
| 300 | static void execute_location(void *dst) | 327 | static void execute_location(void *dst) |
| 301 | { | 328 | { |
| 302 | void (*func)(void) = dst; | 329 | void (*func)(void) = dst; |
| @@ -305,6 +332,15 @@ static void execute_location(void *dst) | |||
| 305 | func(); | 332 | func(); |
| 306 | } | 333 | } |
| 307 | 334 | ||
| 335 | static void execute_user_location(void *dst) | ||
| 336 | { | ||
| 337 | void (*func)(void) = dst; | ||
| 338 | |||
| 339 | if (copy_to_user(dst, do_nothing, EXEC_SIZE)) | ||
| 340 | return; | ||
| 341 | func(); | ||
| 342 | } | ||
| 343 | |||
| 308 | static void lkdtm_do_action(enum ctype which) | 344 | static void lkdtm_do_action(enum ctype which) |
| 309 | { | 345 | { |
| 310 | switch (which) { | 346 | switch (which) { |
| @@ -325,15 +361,11 @@ static void lkdtm_do_action(enum ctype which) | |||
| 325 | ; | 361 | ; |
| 326 | break; | 362 | break; |
| 327 | case CT_OVERFLOW: | 363 | case CT_OVERFLOW: |
| 328 | (void) recursive_loop(0); | 364 | (void) recursive_loop(recur_count); |
| 329 | break; | 365 | break; |
| 330 | case CT_CORRUPT_STACK: { | 366 | case CT_CORRUPT_STACK: |
| 331 | /* Make sure the compiler creates and uses an 8 char array. */ | 367 | corrupt_stack(); |
| 332 | volatile char data[8]; | ||
| 333 | |||
| 334 | memset((void *)data, 0, 64); | ||
| 335 | break; | 368 | break; |
| 336 | } | ||
| 337 | case CT_UNALIGNED_LOAD_STORE_WRITE: { | 369 | case CT_UNALIGNED_LOAD_STORE_WRITE: { |
| 338 | static u8 data[5] __attribute__((aligned(4))) = {1, 2, | 370 | static u8 data[5] __attribute__((aligned(4))) = {1, 2, |
| 339 | 3, 4, 5}; | 371 | 3, 4, 5}; |
| @@ -401,6 +433,49 @@ static void lkdtm_do_action(enum ctype which) | |||
| 401 | vfree(vmalloc_area); | 433 | vfree(vmalloc_area); |
| 402 | break; | 434 | break; |
| 403 | } | 435 | } |
| 436 | case CT_EXEC_USERSPACE: { | ||
| 437 | unsigned long user_addr; | ||
| 438 | |||
| 439 | user_addr = vm_mmap(NULL, 0, PAGE_SIZE, | ||
| 440 | PROT_READ | PROT_WRITE | PROT_EXEC, | ||
| 441 | MAP_ANONYMOUS | MAP_PRIVATE, 0); | ||
| 442 | if (user_addr >= TASK_SIZE) { | ||
| 443 | pr_warn("Failed to allocate user memory\n"); | ||
| 444 | return; | ||
| 445 | } | ||
| 446 | execute_user_location((void *)user_addr); | ||
| 447 | vm_munmap(user_addr, PAGE_SIZE); | ||
| 448 | break; | ||
| 449 | } | ||
| 450 | case CT_ACCESS_USERSPACE: { | ||
| 451 | unsigned long user_addr, tmp; | ||
| 452 | unsigned long *ptr; | ||
| 453 | |||
| 454 | user_addr = vm_mmap(NULL, 0, PAGE_SIZE, | ||
| 455 | PROT_READ | PROT_WRITE | PROT_EXEC, | ||
| 456 | MAP_ANONYMOUS | MAP_PRIVATE, 0); | ||
| 457 | if (user_addr >= TASK_SIZE) { | ||
| 458 | pr_warn("Failed to allocate user memory\n"); | ||
| 459 | return; | ||
| 460 | } | ||
| 461 | |||
| 462 | ptr = (unsigned long *)user_addr; | ||
| 463 | tmp = *ptr; | ||
| 464 | tmp += 0xc0dec0de; | ||
| 465 | *ptr = tmp; | ||
| 466 | |||
| 467 | vm_munmap(user_addr, PAGE_SIZE); | ||
| 468 | |||
| 469 | break; | ||
| 470 | } | ||
| 471 | case CT_WRITE_RO: { | ||
| 472 | unsigned long *ptr; | ||
| 473 | |||
| 474 | ptr = (unsigned long *)&rodata; | ||
| 475 | *ptr ^= 0xabcd1234; | ||
| 476 | |||
| 477 | break; | ||
| 478 | } | ||
| 404 | case CT_NONE: | 479 | case CT_NONE: |
| 405 | default: | 480 | default: |
| 406 | break; | 481 | break; |
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index f6ff711aa5bb..d22c6864508b 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c | |||
| @@ -58,6 +58,7 @@ void mei_amthif_reset_params(struct mei_device *dev) | |||
| 58 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | 58 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
| 59 | dev->iamthif_timer = 0; | 59 | dev->iamthif_timer = 0; |
| 60 | dev->iamthif_stall_timer = 0; | 60 | dev->iamthif_stall_timer = 0; |
| 61 | dev->iamthif_open_count = 0; | ||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | /** | 64 | /** |
| @@ -78,8 +79,10 @@ int mei_amthif_host_init(struct mei_device *dev) | |||
| 78 | 79 | ||
| 79 | i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); | 80 | i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); |
| 80 | if (i < 0) { | 81 | if (i < 0) { |
| 81 | dev_info(&dev->pdev->dev, "amthif: failed to find the client\n"); | 82 | ret = i; |
| 82 | return -ENOENT; | 83 | dev_info(&dev->pdev->dev, |
| 84 | "amthif: failed to find the client %d\n", ret); | ||
| 85 | return ret; | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | cl->me_client_id = dev->me_clients[i].client_id; | 88 | cl->me_client_id = dev->me_clients[i].client_id; |
| @@ -106,8 +109,9 @@ int mei_amthif_host_init(struct mei_device *dev) | |||
| 106 | ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); | 109 | ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); |
| 107 | 110 | ||
| 108 | if (ret < 0) { | 111 | if (ret < 0) { |
| 109 | dev_err(&dev->pdev->dev, "amthif: failed link client\n"); | 112 | dev_err(&dev->pdev->dev, |
| 110 | return -ENOENT; | 113 | "amthif: failed link client %d\n", ret); |
| 114 | return ret; | ||
| 111 | } | 115 | } |
| 112 | 116 | ||
| 113 | cl->state = MEI_FILE_CONNECTING; | 117 | cl->state = MEI_FILE_CONNECTING; |
| @@ -313,13 +317,13 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) | |||
| 313 | mei_hdr.me_addr = dev->iamthif_cl.me_client_id; | 317 | mei_hdr.me_addr = dev->iamthif_cl.me_client_id; |
| 314 | mei_hdr.reserved = 0; | 318 | mei_hdr.reserved = 0; |
| 315 | dev->iamthif_msg_buf_index += mei_hdr.length; | 319 | dev->iamthif_msg_buf_index += mei_hdr.length; |
| 316 | if (mei_write_message(dev, &mei_hdr, | 320 | ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf); |
| 317 | (unsigned char *)dev->iamthif_msg_buf)) | 321 | if (ret) |
| 318 | return -ENODEV; | 322 | return ret; |
| 319 | 323 | ||
| 320 | if (mei_hdr.msg_complete) { | 324 | if (mei_hdr.msg_complete) { |
| 321 | if (mei_cl_flow_ctrl_reduce(&dev->iamthif_cl)) | 325 | if (mei_cl_flow_ctrl_reduce(&dev->iamthif_cl)) |
| 322 | return -ENODEV; | 326 | return -EIO; |
| 323 | dev->iamthif_flow_control_pending = true; | 327 | dev->iamthif_flow_control_pending = true; |
| 324 | dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; | 328 | dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; |
| 325 | dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n"); | 329 | dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n"); |
| @@ -459,6 +463,16 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 459 | struct mei_msg_hdr mei_hdr; | 463 | struct mei_msg_hdr mei_hdr; |
| 460 | size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; | 464 | size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; |
| 461 | u32 msg_slots = mei_data2slots(len); | 465 | u32 msg_slots = mei_data2slots(len); |
| 466 | int rets; | ||
| 467 | |||
| 468 | rets = mei_cl_flow_ctrl_creds(cl); | ||
| 469 | if (rets < 0) | ||
| 470 | return rets; | ||
| 471 | |||
| 472 | if (rets == 0) { | ||
| 473 | cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); | ||
| 474 | return 0; | ||
| 475 | } | ||
| 462 | 476 | ||
| 463 | mei_hdr.host_addr = cl->host_client_id; | 477 | mei_hdr.host_addr = cl->host_client_id; |
| 464 | mei_hdr.me_addr = cl->me_client_id; | 478 | mei_hdr.me_addr = cl->me_client_id; |
| @@ -481,16 +495,17 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 481 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); | 495 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); |
| 482 | 496 | ||
| 483 | *slots -= msg_slots; | 497 | *slots -= msg_slots; |
| 484 | if (mei_write_message(dev, &mei_hdr, | 498 | rets = mei_write_message(dev, &mei_hdr, |
| 485 | dev->iamthif_msg_buf + dev->iamthif_msg_buf_index)) { | 499 | dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); |
| 486 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | 500 | if (rets) { |
| 487 | cl->status = -ENODEV; | 501 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
| 488 | list_del(&cb->list); | 502 | cl->status = rets; |
| 489 | return -ENODEV; | 503 | list_del(&cb->list); |
| 504 | return rets; | ||
| 490 | } | 505 | } |
| 491 | 506 | ||
| 492 | if (mei_cl_flow_ctrl_reduce(cl)) | 507 | if (mei_cl_flow_ctrl_reduce(cl)) |
| 493 | return -ENODEV; | 508 | return -EIO; |
| 494 | 509 | ||
| 495 | dev->iamthif_msg_buf_index += mei_hdr.length; | 510 | dev->iamthif_msg_buf_index += mei_hdr.length; |
| 496 | cl->status = 0; | 511 | cl->status = 0; |
| @@ -720,8 +735,8 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file) | |||
| 720 | */ | 735 | */ |
| 721 | int mei_amthif_release(struct mei_device *dev, struct file *file) | 736 | int mei_amthif_release(struct mei_device *dev, struct file *file) |
| 722 | { | 737 | { |
| 723 | if (dev->open_handle_count > 0) | 738 | if (dev->iamthif_open_count > 0) |
| 724 | dev->open_handle_count--; | 739 | dev->iamthif_open_count--; |
| 725 | 740 | ||
| 726 | if (dev->iamthif_file_object == file && | 741 | if (dev->iamthif_file_object == file && |
| 727 | dev->iamthif_state != MEI_IAMTHIF_IDLE) { | 742 | dev->iamthif_state != MEI_IAMTHIF_IDLE) { |
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index cd2033cd7120..4bc7d620d695 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c | |||
| @@ -245,7 +245,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, | |||
| 245 | /* Check if we have an ME client device */ | 245 | /* Check if we have an ME client device */ |
| 246 | id = mei_me_cl_by_id(dev, cl->me_client_id); | 246 | id = mei_me_cl_by_id(dev, cl->me_client_id); |
| 247 | if (id < 0) | 247 | if (id < 0) |
| 248 | return -ENODEV; | 248 | return id; |
| 249 | 249 | ||
| 250 | if (length > dev->me_clients[id].props.max_msg_length) | 250 | if (length > dev->me_clients[id].props.max_msg_length) |
| 251 | return -EINVAL; | 251 | return -EINVAL; |
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e0684b4d9a08..87c96e4669e2 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c | |||
| @@ -187,10 +187,14 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) | |||
| 187 | */ | 187 | */ |
| 188 | int mei_cl_flush_queues(struct mei_cl *cl) | 188 | int mei_cl_flush_queues(struct mei_cl *cl) |
| 189 | { | 189 | { |
| 190 | struct mei_device *dev; | ||
| 191 | |||
| 190 | if (WARN_ON(!cl || !cl->dev)) | 192 | if (WARN_ON(!cl || !cl->dev)) |
| 191 | return -EINVAL; | 193 | return -EINVAL; |
| 192 | 194 | ||
| 193 | dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); | 195 | dev = cl->dev; |
| 196 | |||
| 197 | cl_dbg(dev, cl, "remove list entry belonging to cl\n"); | ||
| 194 | mei_io_list_flush(&cl->dev->read_list, cl); | 198 | mei_io_list_flush(&cl->dev->read_list, cl); |
| 195 | mei_io_list_flush(&cl->dev->write_list, cl); | 199 | mei_io_list_flush(&cl->dev->write_list, cl); |
| 196 | mei_io_list_flush(&cl->dev->write_waiting_list, cl); | 200 | mei_io_list_flush(&cl->dev->write_waiting_list, cl); |
| @@ -271,6 +275,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) | |||
| 271 | int mei_cl_link(struct mei_cl *cl, int id) | 275 | int mei_cl_link(struct mei_cl *cl, int id) |
| 272 | { | 276 | { |
| 273 | struct mei_device *dev; | 277 | struct mei_device *dev; |
| 278 | long open_handle_count; | ||
| 274 | 279 | ||
| 275 | if (WARN_ON(!cl || !cl->dev)) | 280 | if (WARN_ON(!cl || !cl->dev)) |
| 276 | return -EINVAL; | 281 | return -EINVAL; |
| @@ -284,7 +289,14 @@ int mei_cl_link(struct mei_cl *cl, int id) | |||
| 284 | 289 | ||
| 285 | if (id >= MEI_CLIENTS_MAX) { | 290 | if (id >= MEI_CLIENTS_MAX) { |
| 286 | dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ; | 291 | dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ; |
| 287 | return -ENOENT; | 292 | return -EMFILE; |
| 293 | } | ||
| 294 | |||
| 295 | open_handle_count = dev->open_handle_count + dev->iamthif_open_count; | ||
| 296 | if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { | ||
| 297 | dev_err(&dev->pdev->dev, "open_handle_count exceded %d", | ||
| 298 | MEI_MAX_OPEN_HANDLE_COUNT); | ||
| 299 | return -EMFILE; | ||
| 288 | } | 300 | } |
| 289 | 301 | ||
| 290 | dev->open_handle_count++; | 302 | dev->open_handle_count++; |
| @@ -296,7 +308,7 @@ int mei_cl_link(struct mei_cl *cl, int id) | |||
| 296 | 308 | ||
| 297 | cl->state = MEI_FILE_INITIALIZING; | 309 | cl->state = MEI_FILE_INITIALIZING; |
| 298 | 310 | ||
| 299 | dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id); | 311 | cl_dbg(dev, cl, "link cl\n"); |
| 300 | return 0; | 312 | return 0; |
| 301 | } | 313 | } |
| 302 | 314 | ||
| @@ -308,7 +320,6 @@ int mei_cl_link(struct mei_cl *cl, int id) | |||
| 308 | int mei_cl_unlink(struct mei_cl *cl) | 320 | int mei_cl_unlink(struct mei_cl *cl) |
| 309 | { | 321 | { |
| 310 | struct mei_device *dev; | 322 | struct mei_device *dev; |
| 311 | struct mei_cl *pos, *next; | ||
| 312 | 323 | ||
| 313 | /* don't shout on error exit path */ | 324 | /* don't shout on error exit path */ |
| 314 | if (!cl) | 325 | if (!cl) |
| @@ -320,14 +331,21 @@ int mei_cl_unlink(struct mei_cl *cl) | |||
| 320 | 331 | ||
| 321 | dev = cl->dev; | 332 | dev = cl->dev; |
| 322 | 333 | ||
| 323 | list_for_each_entry_safe(pos, next, &dev->file_list, link) { | 334 | cl_dbg(dev, cl, "unlink client"); |
| 324 | if (cl->host_client_id == pos->host_client_id) { | 335 | |
| 325 | dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", | 336 | if (dev->open_handle_count > 0) |
| 326 | pos->host_client_id, pos->me_client_id); | 337 | dev->open_handle_count--; |
| 327 | list_del_init(&pos->link); | 338 | |
| 328 | break; | 339 | /* never clear the 0 bit */ |
| 329 | } | 340 | if (cl->host_client_id) |
| 330 | } | 341 | clear_bit(cl->host_client_id, dev->host_clients_map); |
| 342 | |||
| 343 | list_del_init(&cl->link); | ||
| 344 | |||
| 345 | cl->state = MEI_FILE_INITIALIZING; | ||
| 346 | |||
| 347 | list_del_init(&cl->link); | ||
| 348 | |||
| 331 | return 0; | 349 | return 0; |
| 332 | } | 350 | } |
| 333 | 351 | ||
| @@ -341,17 +359,6 @@ void mei_host_client_init(struct work_struct *work) | |||
| 341 | 359 | ||
| 342 | mutex_lock(&dev->device_lock); | 360 | mutex_lock(&dev->device_lock); |
| 343 | 361 | ||
| 344 | bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); | ||
| 345 | dev->open_handle_count = 0; | ||
| 346 | |||
| 347 | /* | ||
| 348 | * Reserving the first three client IDs | ||
| 349 | * 0: Reserved for MEI Bus Message communications | ||
| 350 | * 1: Reserved for Watchdog | ||
| 351 | * 2: Reserved for AMTHI | ||
| 352 | */ | ||
| 353 | bitmap_set(dev->host_clients_map, 0, 3); | ||
| 354 | |||
| 355 | for (i = 0; i < dev->me_clients_num; i++) { | 362 | for (i = 0; i < dev->me_clients_num; i++) { |
| 356 | client_props = &dev->me_clients[i].props; | 363 | client_props = &dev->me_clients[i].props; |
| 357 | 364 | ||
| @@ -390,6 +397,8 @@ int mei_cl_disconnect(struct mei_cl *cl) | |||
| 390 | 397 | ||
| 391 | dev = cl->dev; | 398 | dev = cl->dev; |
| 392 | 399 | ||
| 400 | cl_dbg(dev, cl, "disconnecting"); | ||
| 401 | |||
| 393 | if (cl->state != MEI_FILE_DISCONNECTING) | 402 | if (cl->state != MEI_FILE_DISCONNECTING) |
| 394 | return 0; | 403 | return 0; |
| 395 | 404 | ||
| @@ -402,13 +411,13 @@ int mei_cl_disconnect(struct mei_cl *cl) | |||
| 402 | dev->hbuf_is_ready = false; | 411 | dev->hbuf_is_ready = false; |
| 403 | if (mei_hbm_cl_disconnect_req(dev, cl)) { | 412 | if (mei_hbm_cl_disconnect_req(dev, cl)) { |
| 404 | rets = -ENODEV; | 413 | rets = -ENODEV; |
| 405 | dev_err(&dev->pdev->dev, "failed to disconnect.\n"); | 414 | cl_err(dev, cl, "failed to disconnect.\n"); |
| 406 | goto free; | 415 | goto free; |
| 407 | } | 416 | } |
| 408 | mdelay(10); /* Wait for hardware disconnection ready */ | 417 | mdelay(10); /* Wait for hardware disconnection ready */ |
| 409 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); | 418 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); |
| 410 | } else { | 419 | } else { |
| 411 | dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); | 420 | cl_dbg(dev, cl, "add disconnect cb to control write list\n"); |
| 412 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); | 421 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); |
| 413 | 422 | ||
| 414 | } | 423 | } |
| @@ -421,18 +430,17 @@ int mei_cl_disconnect(struct mei_cl *cl) | |||
| 421 | mutex_lock(&dev->device_lock); | 430 | mutex_lock(&dev->device_lock); |
| 422 | if (MEI_FILE_DISCONNECTED == cl->state) { | 431 | if (MEI_FILE_DISCONNECTED == cl->state) { |
| 423 | rets = 0; | 432 | rets = 0; |
| 424 | dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); | 433 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); |
| 425 | } else { | 434 | } else { |
| 426 | rets = -ENODEV; | 435 | rets = -ENODEV; |
| 427 | if (MEI_FILE_DISCONNECTED != cl->state) | 436 | if (MEI_FILE_DISCONNECTED != cl->state) |
| 428 | dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); | 437 | cl_err(dev, cl, "wrong status client disconnect.\n"); |
| 429 | 438 | ||
| 430 | if (err) | 439 | if (err) |
| 431 | dev_dbg(&dev->pdev->dev, | 440 | cl_dbg(dev, cl, "wait failed disconnect err=%08x\n", |
| 432 | "wait failed disconnect err=%08x\n", | ||
| 433 | err); | 441 | err); |
| 434 | 442 | ||
| 435 | dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); | 443 | cl_err(dev, cl, "failed to disconnect from FW client.\n"); |
| 436 | } | 444 | } |
| 437 | 445 | ||
| 438 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | 446 | mei_io_list_flush(&dev->ctrl_rd_list, cl); |
| @@ -639,13 +647,12 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) | |||
| 639 | return -ENODEV; | 647 | return -ENODEV; |
| 640 | 648 | ||
| 641 | if (cl->read_cb) { | 649 | if (cl->read_cb) { |
| 642 | dev_dbg(&dev->pdev->dev, "read is pending.\n"); | 650 | cl_dbg(dev, cl, "read is pending.\n"); |
| 643 | return -EBUSY; | 651 | return -EBUSY; |
| 644 | } | 652 | } |
| 645 | i = mei_me_cl_by_id(dev, cl->me_client_id); | 653 | i = mei_me_cl_by_id(dev, cl->me_client_id); |
| 646 | if (i < 0) { | 654 | if (i < 0) { |
| 647 | dev_err(&dev->pdev->dev, "no such me client %d\n", | 655 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); |
| 648 | cl->me_client_id); | ||
| 649 | return -ENODEV; | 656 | return -ENODEV; |
| 650 | } | 657 | } |
| 651 | 658 | ||
| @@ -664,6 +671,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) | |||
| 664 | if (dev->hbuf_is_ready) { | 671 | if (dev->hbuf_is_ready) { |
| 665 | dev->hbuf_is_ready = false; | 672 | dev->hbuf_is_ready = false; |
| 666 | if (mei_hbm_cl_flow_control_req(dev, cl)) { | 673 | if (mei_hbm_cl_flow_control_req(dev, cl)) { |
| 674 | cl_err(dev, cl, "flow control send failed\n"); | ||
| 667 | rets = -ENODEV; | 675 | rets = -ENODEV; |
| 668 | goto err; | 676 | goto err; |
| 669 | } | 677 | } |
| @@ -691,10 +699,32 @@ err: | |||
| 691 | int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, | 699 | int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, |
| 692 | s32 *slots, struct mei_cl_cb *cmpl_list) | 700 | s32 *slots, struct mei_cl_cb *cmpl_list) |
| 693 | { | 701 | { |
| 694 | struct mei_device *dev = cl->dev; | 702 | struct mei_device *dev; |
| 703 | struct mei_msg_data *buf; | ||
| 695 | struct mei_msg_hdr mei_hdr; | 704 | struct mei_msg_hdr mei_hdr; |
| 696 | size_t len = cb->request_buffer.size - cb->buf_idx; | 705 | size_t len; |
| 697 | u32 msg_slots = mei_data2slots(len); | 706 | u32 msg_slots; |
| 707 | int rets; | ||
| 708 | |||
| 709 | |||
| 710 | if (WARN_ON(!cl || !cl->dev)) | ||
| 711 | return -ENODEV; | ||
| 712 | |||
| 713 | dev = cl->dev; | ||
| 714 | |||
| 715 | buf = &cb->request_buffer; | ||
| 716 | |||
| 717 | rets = mei_cl_flow_ctrl_creds(cl); | ||
| 718 | if (rets < 0) | ||
| 719 | return rets; | ||
| 720 | |||
| 721 | if (rets == 0) { | ||
| 722 | cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); | ||
| 723 | return 0; | ||
| 724 | } | ||
| 725 | |||
| 726 | len = buf->size - cb->buf_idx; | ||
| 727 | msg_slots = mei_data2slots(len); | ||
| 698 | 728 | ||
| 699 | mei_hdr.host_addr = cl->host_client_id; | 729 | mei_hdr.host_addr = cl->host_client_id; |
| 700 | mei_hdr.me_addr = cl->me_client_id; | 730 | mei_hdr.me_addr = cl->me_client_id; |
| @@ -714,16 +744,15 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 714 | return 0; | 744 | return 0; |
| 715 | } | 745 | } |
| 716 | 746 | ||
| 717 | dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n", | 747 | cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", |
| 718 | cb->request_buffer.size, cb->buf_idx); | 748 | cb->request_buffer.size, cb->buf_idx); |
| 719 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); | ||
| 720 | 749 | ||
| 721 | *slots -= msg_slots; | 750 | *slots -= msg_slots; |
| 722 | if (mei_write_message(dev, &mei_hdr, | 751 | rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); |
| 723 | cb->request_buffer.data + cb->buf_idx)) { | 752 | if (rets) { |
| 724 | cl->status = -ENODEV; | 753 | cl->status = rets; |
| 725 | list_move_tail(&cb->list, &cmpl_list->list); | 754 | list_move_tail(&cb->list, &cmpl_list->list); |
| 726 | return -ENODEV; | 755 | return rets; |
| 727 | } | 756 | } |
| 728 | 757 | ||
| 729 | cl->status = 0; | 758 | cl->status = 0; |
| @@ -732,7 +761,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 732 | 761 | ||
| 733 | if (mei_hdr.msg_complete) { | 762 | if (mei_hdr.msg_complete) { |
| 734 | if (mei_cl_flow_ctrl_reduce(cl)) | 763 | if (mei_cl_flow_ctrl_reduce(cl)) |
| 735 | return -ENODEV; | 764 | return -EIO; |
| 736 | list_move_tail(&cb->list, &dev->write_waiting_list.list); | 765 | list_move_tail(&cb->list, &dev->write_waiting_list.list); |
| 737 | } | 766 | } |
| 738 | 767 | ||
| @@ -767,7 +796,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) | |||
| 767 | 796 | ||
| 768 | buf = &cb->request_buffer; | 797 | buf = &cb->request_buffer; |
| 769 | 798 | ||
| 770 | dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size); | 799 | cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); |
| 771 | 800 | ||
| 772 | 801 | ||
| 773 | cb->fop_type = MEI_FOP_WRITE; | 802 | cb->fop_type = MEI_FOP_WRITE; |
| @@ -800,14 +829,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) | |||
| 800 | mei_hdr.me_addr = cl->me_client_id; | 829 | mei_hdr.me_addr = cl->me_client_id; |
| 801 | mei_hdr.reserved = 0; | 830 | mei_hdr.reserved = 0; |
| 802 | 831 | ||
| 803 | dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", | ||
| 804 | MEI_HDR_PRM(&mei_hdr)); | ||
| 805 | |||
| 806 | 832 | ||
| 807 | if (mei_write_message(dev, &mei_hdr, buf->data)) { | 833 | rets = mei_write_message(dev, &mei_hdr, buf->data); |
| 808 | rets = -EIO; | 834 | if (rets) |
| 809 | goto err; | 835 | goto err; |
| 810 | } | ||
| 811 | 836 | ||
| 812 | cl->writing_state = MEI_WRITING; | 837 | cl->writing_state = MEI_WRITING; |
| 813 | cb->buf_idx = mei_hdr.length; | 838 | cb->buf_idx = mei_hdr.length; |
| @@ -898,11 +923,11 @@ void mei_cl_all_wakeup(struct mei_device *dev) | |||
| 898 | struct mei_cl *cl, *next; | 923 | struct mei_cl *cl, *next; |
| 899 | list_for_each_entry_safe(cl, next, &dev->file_list, link) { | 924 | list_for_each_entry_safe(cl, next, &dev->file_list, link) { |
| 900 | if (waitqueue_active(&cl->rx_wait)) { | 925 | if (waitqueue_active(&cl->rx_wait)) { |
| 901 | dev_dbg(&dev->pdev->dev, "Waking up reading client!\n"); | 926 | cl_dbg(dev, cl, "Waking up reading client!\n"); |
| 902 | wake_up_interruptible(&cl->rx_wait); | 927 | wake_up_interruptible(&cl->rx_wait); |
| 903 | } | 928 | } |
| 904 | if (waitqueue_active(&cl->tx_wait)) { | 929 | if (waitqueue_active(&cl->tx_wait)) { |
| 905 | dev_dbg(&dev->pdev->dev, "Waking up writing client!\n"); | 930 | cl_dbg(dev, cl, "Waking up writing client!\n"); |
| 906 | wake_up_interruptible(&cl->tx_wait); | 931 | wake_up_interruptible(&cl->tx_wait); |
| 907 | } | 932 | } |
| 908 | } | 933 | } |
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 892cc4207fa2..c8396e582f1c 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h | |||
| @@ -115,4 +115,13 @@ void mei_cl_all_disconnect(struct mei_device *dev); | |||
| 115 | void mei_cl_all_wakeup(struct mei_device *dev); | 115 | void mei_cl_all_wakeup(struct mei_device *dev); |
| 116 | void mei_cl_all_write_clear(struct mei_device *dev); | 116 | void mei_cl_all_write_clear(struct mei_device *dev); |
| 117 | 117 | ||
| 118 | #define MEI_CL_FMT "cl:host=%02d me=%02d " | ||
| 119 | #define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id | ||
| 120 | |||
| 121 | #define cl_dbg(dev, cl, format, arg...) \ | ||
| 122 | dev_dbg(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) | ||
| 123 | |||
| 124 | #define cl_err(dev, cl, format, arg...) \ | ||
| 125 | dev_err(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) | ||
| 126 | |||
| 118 | #endif /* _MEI_CLIENT_H_ */ | 127 | #endif /* _MEI_CLIENT_H_ */ |
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 0a0448326e9d..9b3a0fb7f265 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c | |||
| @@ -49,7 +49,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev) | |||
| 49 | kfree(dev->me_clients); | 49 | kfree(dev->me_clients); |
| 50 | dev->me_clients = NULL; | 50 | dev->me_clients = NULL; |
| 51 | 51 | ||
| 52 | dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n", | 52 | dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n", |
| 53 | dev->me_clients_num * sizeof(struct mei_me_client)); | 53 | dev->me_clients_num * sizeof(struct mei_me_client)); |
| 54 | /* allocate storage for ME clients representation */ | 54 | /* allocate storage for ME clients representation */ |
| 55 | clients = kcalloc(dev->me_clients_num, | 55 | clients = kcalloc(dev->me_clients_num, |
| @@ -174,7 +174,7 @@ int mei_hbm_start_req(struct mei_device *dev) | |||
| 174 | dev_err(&dev->pdev->dev, "version message write failed\n"); | 174 | dev_err(&dev->pdev->dev, "version message write failed\n"); |
| 175 | dev->dev_state = MEI_DEV_RESETTING; | 175 | dev->dev_state = MEI_DEV_RESETTING; |
| 176 | mei_reset(dev, 1); | 176 | mei_reset(dev, 1); |
| 177 | return -ENODEV; | 177 | return -EIO; |
| 178 | } | 178 | } |
| 179 | dev->hbm_state = MEI_HBM_START; | 179 | dev->hbm_state = MEI_HBM_START; |
| 180 | dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; | 180 | dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; |
| @@ -677,7 +677,10 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) | |||
| 677 | 677 | ||
| 678 | case HOST_ENUM_RES_CMD: | 678 | case HOST_ENUM_RES_CMD: |
| 679 | enum_res = (struct hbm_host_enum_response *) mei_msg; | 679 | enum_res = (struct hbm_host_enum_response *) mei_msg; |
| 680 | memcpy(dev->me_clients_map, enum_res->valid_addresses, 32); | 680 | BUILD_BUG_ON(sizeof(dev->me_clients_map) |
| 681 | < sizeof(enum_res->valid_addresses)); | ||
| 682 | memcpy(dev->me_clients_map, enum_res->valid_addresses, | ||
| 683 | sizeof(enum_res->valid_addresses)); | ||
| 681 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && | 684 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && |
| 682 | dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { | 685 | dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { |
| 683 | dev->init_clients_timer = 0; | 686 | dev->init_clients_timer = 0; |
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 6a203b6e8346..6c0fde55270d 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h | |||
| @@ -110,6 +110,7 @@ | |||
| 110 | #define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ | 110 | #define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ |
| 111 | 111 | ||
| 112 | #define MEI_DEV_ID_LPT 0x8C3A /* Lynx Point */ | 112 | #define MEI_DEV_ID_LPT 0x8C3A /* Lynx Point */ |
| 113 | #define MEI_DEV_ID_LPT_W 0x8D3A /* Lynx Point - Wellsburg */ | ||
| 113 | #define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ | 114 | #define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ |
| 114 | /* | 115 | /* |
| 115 | * MEI HW Section | 116 | * MEI HW Section |
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6197018e2f16..f7f3abbe12b6 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c | |||
| @@ -68,6 +68,14 @@ void mei_device_init(struct mei_device *dev) | |||
| 68 | mei_io_list_init(&dev->amthif_cmd_list); | 68 | mei_io_list_init(&dev->amthif_cmd_list); |
| 69 | mei_io_list_init(&dev->amthif_rd_complete_list); | 69 | mei_io_list_init(&dev->amthif_rd_complete_list); |
| 70 | 70 | ||
| 71 | bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); | ||
| 72 | dev->open_handle_count = 0; | ||
| 73 | |||
| 74 | /* | ||
| 75 | * Reserving the first client ID | ||
| 76 | * 0: Reserved for MEI Bus Message communications | ||
| 77 | */ | ||
| 78 | bitmap_set(dev->host_clients_map, 0, 1); | ||
| 71 | } | 79 | } |
| 72 | EXPORT_SYMBOL_GPL(mei_device_init); | 80 | EXPORT_SYMBOL_GPL(mei_device_init); |
| 73 | 81 | ||
| @@ -139,6 +147,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) | |||
| 139 | dev->dev_state != MEI_DEV_POWER_DOWN && | 147 | dev->dev_state != MEI_DEV_POWER_DOWN && |
| 140 | dev->dev_state != MEI_DEV_POWER_UP); | 148 | dev->dev_state != MEI_DEV_POWER_UP); |
| 141 | 149 | ||
| 150 | if (unexpected) | ||
| 151 | dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", | ||
| 152 | mei_dev_state_str(dev->dev_state)); | ||
| 153 | |||
| 142 | ret = mei_hw_reset(dev, interrupts_enabled); | 154 | ret = mei_hw_reset(dev, interrupts_enabled); |
| 143 | if (ret) { | 155 | if (ret) { |
| 144 | dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); | 156 | dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); |
| @@ -165,12 +177,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) | |||
| 165 | /* remove entry if already in list */ | 177 | /* remove entry if already in list */ |
| 166 | dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); | 178 | dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); |
| 167 | mei_cl_unlink(&dev->wd_cl); | 179 | mei_cl_unlink(&dev->wd_cl); |
| 168 | if (dev->open_handle_count > 0) | ||
| 169 | dev->open_handle_count--; | ||
| 170 | mei_cl_unlink(&dev->iamthif_cl); | 180 | mei_cl_unlink(&dev->iamthif_cl); |
| 171 | if (dev->open_handle_count > 0) | ||
| 172 | dev->open_handle_count--; | ||
| 173 | |||
| 174 | mei_amthif_reset_params(dev); | 181 | mei_amthif_reset_params(dev); |
| 175 | memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); | 182 | memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); |
| 176 | } | 183 | } |
| @@ -182,10 +189,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) | |||
| 182 | dev->rd_msg_hdr = 0; | 189 | dev->rd_msg_hdr = 0; |
| 183 | dev->wd_pending = false; | 190 | dev->wd_pending = false; |
| 184 | 191 | ||
| 185 | if (unexpected) | ||
| 186 | dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", | ||
| 187 | mei_dev_state_str(dev->dev_state)); | ||
| 188 | |||
| 189 | if (!interrupts_enabled) { | 192 | if (!interrupts_enabled) { |
| 190 | dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); | 193 | dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); |
| 191 | return; | 194 | return; |
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 4b59cb742dee..7a95c07e59a6 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c | |||
| @@ -113,13 +113,13 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, | |||
| 113 | 113 | ||
| 114 | if (cb->response_buffer.size == 0 || | 114 | if (cb->response_buffer.size == 0 || |
| 115 | cb->response_buffer.data == NULL) { | 115 | cb->response_buffer.data == NULL) { |
| 116 | dev_err(&dev->pdev->dev, "response buffer is not allocated.\n"); | 116 | cl_err(dev, cl, "response buffer is not allocated.\n"); |
| 117 | list_del(&cb->list); | 117 | list_del(&cb->list); |
| 118 | return -ENOMEM; | 118 | return -ENOMEM; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { | 121 | if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { |
| 122 | dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n", | 122 | cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", |
| 123 | cb->response_buffer.size, | 123 | cb->response_buffer.size, |
| 124 | mei_hdr->length, cb->buf_idx); | 124 | mei_hdr->length, cb->buf_idx); |
| 125 | buffer = krealloc(cb->response_buffer.data, | 125 | buffer = krealloc(cb->response_buffer.data, |
| @@ -127,7 +127,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, | |||
| 127 | GFP_KERNEL); | 127 | GFP_KERNEL); |
| 128 | 128 | ||
| 129 | if (!buffer) { | 129 | if (!buffer) { |
| 130 | dev_err(&dev->pdev->dev, "allocation failed.\n"); | 130 | cl_err(dev, cl, "allocation failed.\n"); |
| 131 | list_del(&cb->list); | 131 | list_del(&cb->list); |
| 132 | return -ENOMEM; | 132 | return -ENOMEM; |
| 133 | } | 133 | } |
| @@ -143,9 +143,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, | |||
| 143 | if (mei_hdr->msg_complete) { | 143 | if (mei_hdr->msg_complete) { |
| 144 | cl->status = 0; | 144 | cl->status = 0; |
| 145 | list_del(&cb->list); | 145 | list_del(&cb->list); |
| 146 | dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n", | 146 | cl_dbg(dev, cl, "completed read length = %lu\n", |
| 147 | cl->host_client_id, | ||
| 148 | cl->me_client_id, | ||
| 149 | cb->buf_idx); | 147 | cb->buf_idx); |
| 150 | list_add_tail(&cb->list, &complete_list->list); | 148 | list_add_tail(&cb->list, &complete_list->list); |
| 151 | } | 149 | } |
| @@ -218,9 +216,11 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 218 | s32 *slots, struct mei_cl_cb *cmpl_list) | 216 | s32 *slots, struct mei_cl_cb *cmpl_list) |
| 219 | { | 217 | { |
| 220 | struct mei_device *dev = cl->dev; | 218 | struct mei_device *dev = cl->dev; |
| 221 | |||
| 222 | u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); | 219 | u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); |
| 223 | 220 | ||
| 221 | int ret; | ||
| 222 | |||
| 223 | |||
| 224 | if (*slots < msg_slots) { | 224 | if (*slots < msg_slots) { |
| 225 | /* return the cancel routine */ | 225 | /* return the cancel routine */ |
| 226 | list_del(&cb->list); | 226 | list_del(&cb->list); |
| @@ -229,12 +229,14 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 229 | 229 | ||
| 230 | *slots -= msg_slots; | 230 | *slots -= msg_slots; |
| 231 | 231 | ||
| 232 | if (mei_hbm_cl_flow_control_req(dev, cl)) { | 232 | ret = mei_hbm_cl_flow_control_req(dev, cl); |
| 233 | cl->status = -ENODEV; | 233 | if (ret) { |
| 234 | cl->status = ret; | ||
| 234 | cb->buf_idx = 0; | 235 | cb->buf_idx = 0; |
| 235 | list_move_tail(&cb->list, &cmpl_list->list); | 236 | list_move_tail(&cb->list, &cmpl_list->list); |
| 236 | return -ENODEV; | 237 | return ret; |
| 237 | } | 238 | } |
| 239 | |||
| 238 | list_move_tail(&cb->list, &dev->read_list.list); | 240 | list_move_tail(&cb->list, &dev->read_list.list); |
| 239 | 241 | ||
| 240 | return 0; | 242 | return 0; |
| @@ -256,6 +258,7 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 256 | s32 *slots, struct mei_cl_cb *cmpl_list) | 258 | s32 *slots, struct mei_cl_cb *cmpl_list) |
| 257 | { | 259 | { |
| 258 | struct mei_device *dev = cl->dev; | 260 | struct mei_device *dev = cl->dev; |
| 261 | int ret; | ||
| 259 | 262 | ||
| 260 | u32 msg_slots = | 263 | u32 msg_slots = |
| 261 | mei_data2slots(sizeof(struct hbm_client_connect_request)); | 264 | mei_data2slots(sizeof(struct hbm_client_connect_request)); |
| @@ -270,11 +273,12 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, | |||
| 270 | 273 | ||
| 271 | cl->state = MEI_FILE_CONNECTING; | 274 | cl->state = MEI_FILE_CONNECTING; |
| 272 | 275 | ||
| 273 | if (mei_hbm_cl_connect_req(dev, cl)) { | 276 | ret = mei_hbm_cl_connect_req(dev, cl); |
| 274 | cl->status = -ENODEV; | 277 | if (ret) { |
| 278 | cl->status = ret; | ||
| 275 | cb->buf_idx = 0; | 279 | cb->buf_idx = 0; |
| 276 | list_del(&cb->list); | 280 | list_del(&cb->list); |
| 277 | return -ENODEV; | 281 | return ret; |
| 278 | } | 282 | } |
| 279 | 283 | ||
| 280 | list_move_tail(&cb->list, &dev->ctrl_rd_list.list); | 284 | list_move_tail(&cb->list, &dev->ctrl_rd_list.list); |
| @@ -345,14 +349,14 @@ int mei_irq_read_handler(struct mei_device *dev, | |||
| 345 | 349 | ||
| 346 | /* decide where to read the message too */ | 350 | /* decide where to read the message too */ |
| 347 | if (!mei_hdr->host_addr) { | 351 | if (!mei_hdr->host_addr) { |
| 348 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n"); | 352 | dev_dbg(&dev->pdev->dev, "call mei_hbm_dispatch.\n"); |
| 349 | mei_hbm_dispatch(dev, mei_hdr); | 353 | mei_hbm_dispatch(dev, mei_hdr); |
| 350 | dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n"); | 354 | dev_dbg(&dev->pdev->dev, "end mei_hbm_dispatch.\n"); |
| 351 | } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && | 355 | } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && |
| 352 | (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && | 356 | (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && |
| 353 | (dev->iamthif_state == MEI_IAMTHIF_READING)) { | 357 | (dev->iamthif_state == MEI_IAMTHIF_READING)) { |
| 354 | 358 | ||
| 355 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); | 359 | dev_dbg(&dev->pdev->dev, "call mei_amthif_irq_read_msg.\n"); |
| 356 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); | 360 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
| 357 | 361 | ||
| 358 | ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); | 362 | ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); |
| @@ -423,12 +427,12 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) | |||
| 423 | if (MEI_WRITING == cl->writing_state && | 427 | if (MEI_WRITING == cl->writing_state && |
| 424 | cb->fop_type == MEI_FOP_WRITE && | 428 | cb->fop_type == MEI_FOP_WRITE && |
| 425 | cl != &dev->iamthif_cl) { | 429 | cl != &dev->iamthif_cl) { |
| 426 | dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n"); | 430 | cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); |
| 427 | cl->writing_state = MEI_WRITE_COMPLETE; | 431 | cl->writing_state = MEI_WRITE_COMPLETE; |
| 428 | list_add_tail(&cb->list, &cmpl_list->list); | 432 | list_add_tail(&cb->list, &cmpl_list->list); |
| 429 | } | 433 | } |
| 430 | if (cl == &dev->iamthif_cl) { | 434 | if (cl == &dev->iamthif_cl) { |
| 431 | dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); | 435 | cl_dbg(dev, cl, "check iamthif flow control.\n"); |
| 432 | if (dev->iamthif_flow_control_pending) { | 436 | if (dev->iamthif_flow_control_pending) { |
| 433 | ret = mei_amthif_irq_read(dev, &slots); | 437 | ret = mei_amthif_irq_read(dev, &slots); |
| 434 | if (ret) | 438 | if (ret) |
| @@ -509,13 +513,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) | |||
| 509 | cl = cb->cl; | 513 | cl = cb->cl; |
| 510 | if (cl == NULL) | 514 | if (cl == NULL) |
| 511 | continue; | 515 | continue; |
| 512 | if (mei_cl_flow_ctrl_creds(cl) <= 0) { | ||
| 513 | dev_dbg(&dev->pdev->dev, | ||
| 514 | "No flow control credentials for client %d, not sending.\n", | ||
| 515 | cl->host_client_id); | ||
| 516 | continue; | ||
| 517 | } | ||
| 518 | |||
| 519 | if (cl == &dev->iamthif_cl) | 516 | if (cl == &dev->iamthif_cl) |
| 520 | ret = mei_amthif_irq_write_complete(cl, cb, | 517 | ret = mei_amthif_irq_write_complete(cl, cb, |
| 521 | &slots, cmpl_list); | 518 | &slots, cmpl_list); |
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index cabeddd66c1f..9661a812f550 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c | |||
| @@ -60,48 +60,45 @@ static int mei_open(struct inode *inode, struct file *file) | |||
| 60 | 60 | ||
| 61 | int err; | 61 | int err; |
| 62 | 62 | ||
| 63 | err = -ENODEV; | ||
| 64 | if (!misc->parent) | 63 | if (!misc->parent) |
| 65 | goto out; | 64 | return -ENODEV; |
| 66 | 65 | ||
| 67 | pdev = container_of(misc->parent, struct pci_dev, dev); | 66 | pdev = container_of(misc->parent, struct pci_dev, dev); |
| 68 | 67 | ||
| 69 | dev = pci_get_drvdata(pdev); | 68 | dev = pci_get_drvdata(pdev); |
| 70 | if (!dev) | 69 | if (!dev) |
| 71 | goto out; | 70 | return -ENODEV; |
| 72 | 71 | ||
| 73 | mutex_lock(&dev->device_lock); | 72 | mutex_lock(&dev->device_lock); |
| 74 | err = -ENOMEM; | 73 | |
| 75 | cl = mei_cl_allocate(dev); | 74 | cl = NULL; |
| 76 | if (!cl) | ||
| 77 | goto out_unlock; | ||
| 78 | 75 | ||
| 79 | err = -ENODEV; | 76 | err = -ENODEV; |
| 80 | if (dev->dev_state != MEI_DEV_ENABLED) { | 77 | if (dev->dev_state != MEI_DEV_ENABLED) { |
| 81 | dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", | 78 | dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", |
| 82 | mei_dev_state_str(dev->dev_state)); | 79 | mei_dev_state_str(dev->dev_state)); |
| 83 | goto out_unlock; | 80 | goto err_unlock; |
| 84 | } | ||
| 85 | err = -EMFILE; | ||
| 86 | if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { | ||
| 87 | dev_err(&dev->pdev->dev, "open_handle_count exceded %d", | ||
| 88 | MEI_MAX_OPEN_HANDLE_COUNT); | ||
| 89 | goto out_unlock; | ||
| 90 | } | 81 | } |
| 91 | 82 | ||
| 83 | err = -ENOMEM; | ||
| 84 | cl = mei_cl_allocate(dev); | ||
| 85 | if (!cl) | ||
| 86 | goto err_unlock; | ||
| 87 | |||
| 88 | /* open_handle_count check is handled in the mei_cl_link */ | ||
| 92 | err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); | 89 | err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); |
| 93 | if (err) | 90 | if (err) |
| 94 | goto out_unlock; | 91 | goto err_unlock; |
| 95 | 92 | ||
| 96 | file->private_data = cl; | 93 | file->private_data = cl; |
| 94 | |||
| 97 | mutex_unlock(&dev->device_lock); | 95 | mutex_unlock(&dev->device_lock); |
| 98 | 96 | ||
| 99 | return nonseekable_open(inode, file); | 97 | return nonseekable_open(inode, file); |
| 100 | 98 | ||
| 101 | out_unlock: | 99 | err_unlock: |
| 102 | mutex_unlock(&dev->device_lock); | 100 | mutex_unlock(&dev->device_lock); |
| 103 | kfree(cl); | 101 | kfree(cl); |
| 104 | out: | ||
| 105 | return err; | 102 | return err; |
| 106 | } | 103 | } |
| 107 | 104 | ||
| @@ -144,10 +141,6 @@ static int mei_release(struct inode *inode, struct file *file) | |||
| 144 | cl->host_client_id, | 141 | cl->host_client_id, |
| 145 | cl->me_client_id); | 142 | cl->me_client_id); |
| 146 | 143 | ||
| 147 | if (dev->open_handle_count > 0) { | ||
| 148 | clear_bit(cl->host_client_id, dev->host_clients_map); | ||
| 149 | dev->open_handle_count--; | ||
| 150 | } | ||
| 151 | mei_cl_unlink(cl); | 144 | mei_cl_unlink(cl); |
| 152 | 145 | ||
| 153 | 146 | ||
| @@ -165,10 +158,7 @@ static int mei_release(struct inode *inode, struct file *file) | |||
| 165 | 158 | ||
| 166 | file->private_data = NULL; | 159 | file->private_data = NULL; |
| 167 | 160 | ||
| 168 | if (cb) { | 161 | mei_io_cb_free(cb); |
| 169 | mei_io_cb_free(cb); | ||
| 170 | cb = NULL; | ||
| 171 | } | ||
| 172 | 162 | ||
| 173 | kfree(cl); | 163 | kfree(cl); |
| 174 | out: | 164 | out: |
| @@ -203,12 +193,18 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, | |||
| 203 | 193 | ||
| 204 | dev = cl->dev; | 194 | dev = cl->dev; |
| 205 | 195 | ||
| 196 | |||
| 206 | mutex_lock(&dev->device_lock); | 197 | mutex_lock(&dev->device_lock); |
| 207 | if (dev->dev_state != MEI_DEV_ENABLED) { | 198 | if (dev->dev_state != MEI_DEV_ENABLED) { |
| 208 | rets = -ENODEV; | 199 | rets = -ENODEV; |
| 209 | goto out; | 200 | goto out; |
| 210 | } | 201 | } |
| 211 | 202 | ||
| 203 | if (length == 0) { | ||
| 204 | rets = 0; | ||
| 205 | goto out; | ||
| 206 | } | ||
| 207 | |||
| 212 | if (cl == &dev->iamthif_cl) { | 208 | if (cl == &dev->iamthif_cl) { |
| 213 | rets = mei_amthif_read(dev, file, ubuf, length, offset); | 209 | rets = mei_amthif_read(dev, file, ubuf, length, offset); |
| 214 | goto out; | 210 | goto out; |
| @@ -347,8 +343,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, | |||
| 347 | rets = -ENODEV; | 343 | rets = -ENODEV; |
| 348 | goto out; | 344 | goto out; |
| 349 | } | 345 | } |
| 350 | if (length > dev->me_clients[id].props.max_msg_length || length <= 0) { | 346 | |
| 351 | rets = -EMSGSIZE; | 347 | if (length == 0) { |
| 348 | rets = 0; | ||
| 349 | goto out; | ||
| 350 | } | ||
| 351 | |||
| 352 | if (length > dev->me_clients[id].props.max_msg_length) { | ||
| 353 | rets = -EFBIG; | ||
| 352 | goto out; | 354 | goto out; |
| 353 | } | 355 | } |
| 354 | 356 | ||
| @@ -401,8 +403,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, | |||
| 401 | goto out; | 403 | goto out; |
| 402 | 404 | ||
| 403 | rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); | 405 | rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); |
| 404 | if (rets) | 406 | if (rets) { |
| 407 | dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); | ||
| 408 | rets = -EFAULT; | ||
| 405 | goto out; | 409 | goto out; |
| 410 | } | ||
| 406 | 411 | ||
| 407 | if (cl == &dev->iamthif_cl) { | 412 | if (cl == &dev->iamthif_cl) { |
| 408 | rets = mei_amthif_write(dev, write_cb); | 413 | rets = mei_amthif_write(dev, write_cb); |
| @@ -489,11 +494,11 @@ static int mei_ioctl_connect_client(struct file *file, | |||
| 489 | rets = -ENODEV; | 494 | rets = -ENODEV; |
| 490 | goto end; | 495 | goto end; |
| 491 | } | 496 | } |
| 492 | clear_bit(cl->host_client_id, dev->host_clients_map); | ||
| 493 | mei_cl_unlink(cl); | 497 | mei_cl_unlink(cl); |
| 494 | 498 | ||
| 495 | kfree(cl); | 499 | kfree(cl); |
| 496 | cl = NULL; | 500 | cl = NULL; |
| 501 | dev->iamthif_open_count++; | ||
| 497 | file->private_data = &dev->iamthif_cl; | 502 | file->private_data = &dev->iamthif_cl; |
| 498 | 503 | ||
| 499 | client = &data->out_client_properties; | 504 | client = &data->out_client_properties; |
| @@ -564,7 +569,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) | |||
| 564 | dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); | 569 | dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); |
| 565 | if (copy_from_user(connect_data, (char __user *)data, | 570 | if (copy_from_user(connect_data, (char __user *)data, |
| 566 | sizeof(struct mei_connect_client_data))) { | 571 | sizeof(struct mei_connect_client_data))) { |
| 567 | dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); | 572 | dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); |
| 568 | rets = -EFAULT; | 573 | rets = -EFAULT; |
| 569 | goto out; | 574 | goto out; |
| 570 | } | 575 | } |
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 456b322013e2..406f68e05b4e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h | |||
| @@ -414,6 +414,7 @@ struct mei_device { | |||
| 414 | struct file *iamthif_file_object; | 414 | struct file *iamthif_file_object; |
| 415 | struct mei_cl iamthif_cl; | 415 | struct mei_cl iamthif_cl; |
| 416 | struct mei_cl_cb *iamthif_current_cb; | 416 | struct mei_cl_cb *iamthif_current_cb; |
| 417 | long iamthif_open_count; | ||
| 417 | int iamthif_mtu; | 418 | int iamthif_mtu; |
| 418 | unsigned long iamthif_timer; | 419 | unsigned long iamthif_timer; |
| 419 | u32 iamthif_stall_timer; | 420 | u32 iamthif_stall_timer; |
diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index d0c6907dfd92..994ca4aff1a3 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c | |||
| @@ -485,8 +485,11 @@ int mei_nfc_host_init(struct mei_device *dev) | |||
| 485 | if (ndev->cl_info) | 485 | if (ndev->cl_info) |
| 486 | return 0; | 486 | return 0; |
| 487 | 487 | ||
| 488 | cl_info = mei_cl_allocate(dev); | 488 | ndev->cl_info = mei_cl_allocate(dev); |
| 489 | cl = mei_cl_allocate(dev); | 489 | ndev->cl = mei_cl_allocate(dev); |
| 490 | |||
| 491 | cl = ndev->cl; | ||
| 492 | cl_info = ndev->cl_info; | ||
| 490 | 493 | ||
| 491 | if (!cl || !cl_info) { | 494 | if (!cl || !cl_info) { |
| 492 | ret = -ENOMEM; | 495 | ret = -ENOMEM; |
| @@ -527,10 +530,9 @@ int mei_nfc_host_init(struct mei_device *dev) | |||
| 527 | 530 | ||
| 528 | cl->device_uuid = mei_nfc_guid; | 531 | cl->device_uuid = mei_nfc_guid; |
| 529 | 532 | ||
| 533 | |||
| 530 | list_add_tail(&cl->device_link, &dev->device_list); | 534 | list_add_tail(&cl->device_link, &dev->device_list); |
| 531 | 535 | ||
| 532 | ndev->cl_info = cl_info; | ||
| 533 | ndev->cl = cl; | ||
| 534 | ndev->req_id = 1; | 536 | ndev->req_id = 1; |
| 535 | 537 | ||
| 536 | INIT_WORK(&ndev->init_work, mei_nfc_init); | 538 | INIT_WORK(&ndev->init_work, mei_nfc_init); |
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1b3844e82379..b96205aece0c 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c | |||
| @@ -77,6 +77,7 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { | |||
| 77 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, | 77 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, |
| 78 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, | 78 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, |
| 79 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, | 79 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, |
| 80 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)}, | ||
| 80 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, | 81 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, |
| 81 | 82 | ||
| 82 | /* required last entry */ | 83 | /* required last entry */ |
| @@ -189,7 +190,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 189 | 190 | ||
| 190 | schedule_delayed_work(&dev->timer_work, HZ); | 191 | schedule_delayed_work(&dev->timer_work, HZ); |
| 191 | 192 | ||
| 192 | pr_debug("initialization successful.\n"); | 193 | dev_dbg(&pdev->dev, "initialization successful.\n"); |
| 193 | 194 | ||
| 194 | return 0; | 195 | return 0; |
| 195 | 196 | ||
| @@ -231,7 +232,7 @@ static void mei_me_remove(struct pci_dev *pdev) | |||
| 231 | hw = to_me_hw(dev); | 232 | hw = to_me_hw(dev); |
| 232 | 233 | ||
| 233 | 234 | ||
| 234 | dev_err(&pdev->dev, "stop\n"); | 235 | dev_dbg(&pdev->dev, "stop\n"); |
| 235 | mei_stop(dev); | 236 | mei_stop(dev); |
| 236 | 237 | ||
| 237 | /* disable interrupts */ | 238 | /* disable interrupts */ |
| @@ -239,7 +240,6 @@ static void mei_me_remove(struct pci_dev *pdev) | |||
| 239 | 240 | ||
| 240 | free_irq(pdev->irq, dev); | 241 | free_irq(pdev->irq, dev); |
| 241 | pci_disable_msi(pdev); | 242 | pci_disable_msi(pdev); |
| 242 | pci_set_drvdata(pdev, NULL); | ||
| 243 | 243 | ||
| 244 | if (hw->mem_addr) | 244 | if (hw->mem_addr) |
| 245 | pci_iounmap(pdev, hw->mem_addr); | 245 | pci_iounmap(pdev, hw->mem_addr); |
| @@ -262,7 +262,7 @@ static int mei_me_pci_suspend(struct device *device) | |||
| 262 | if (!dev) | 262 | if (!dev) |
| 263 | return -ENODEV; | 263 | return -ENODEV; |
| 264 | 264 | ||
| 265 | dev_err(&pdev->dev, "suspend\n"); | 265 | dev_dbg(&pdev->dev, "suspend\n"); |
| 266 | 266 | ||
| 267 | mei_stop(dev); | 267 | mei_stop(dev); |
| 268 | 268 | ||
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index b8921432e89d..9e354216c163 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c | |||
| @@ -60,7 +60,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) | |||
| 60 | int mei_wd_host_init(struct mei_device *dev) | 60 | int mei_wd_host_init(struct mei_device *dev) |
| 61 | { | 61 | { |
| 62 | struct mei_cl *cl = &dev->wd_cl; | 62 | struct mei_cl *cl = &dev->wd_cl; |
| 63 | int i; | 63 | int id; |
| 64 | int ret; | 64 | int ret; |
| 65 | 65 | ||
| 66 | mei_cl_init(cl, dev); | 66 | mei_cl_init(cl, dev); |
| @@ -70,19 +70,19 @@ int mei_wd_host_init(struct mei_device *dev) | |||
| 70 | 70 | ||
| 71 | 71 | ||
| 72 | /* check for valid client id */ | 72 | /* check for valid client id */ |
| 73 | i = mei_me_cl_by_uuid(dev, &mei_wd_guid); | 73 | id = mei_me_cl_by_uuid(dev, &mei_wd_guid); |
| 74 | if (i < 0) { | 74 | if (id < 0) { |
| 75 | dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); | 75 | dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); |
| 76 | return -ENOENT; | 76 | return id; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | cl->me_client_id = dev->me_clients[i].client_id; | 79 | cl->me_client_id = dev->me_clients[id].client_id; |
| 80 | 80 | ||
| 81 | ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); | 81 | ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); |
| 82 | 82 | ||
| 83 | if (ret < 0) { | 83 | if (ret < 0) { |
| 84 | dev_info(&dev->pdev->dev, "wd: failed link client\n"); | 84 | dev_info(&dev->pdev->dev, "wd: failed link client\n"); |
| 85 | return -ENOENT; | 85 | return ret; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | cl->state = MEI_FILE_CONNECTING; | 88 | cl->state = MEI_FILE_CONNECTING; |
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig new file mode 100644 index 000000000000..e42b331edbc6 --- /dev/null +++ b/drivers/misc/mic/Kconfig | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | comment "Intel MIC Host Driver" | ||
| 2 | |||
| 3 | config INTEL_MIC_HOST | ||
| 4 | tristate "Intel MIC Host Driver" | ||
| 5 | depends on 64BIT && PCI && X86 | ||
| 6 | select VHOST_RING | ||
| 7 | default N | ||
| 8 | help | ||
| 9 | This enables Host Driver support for the Intel Many Integrated | ||
| 10 | Core (MIC) family of PCIe form factor coprocessor devices that | ||
| 11 | run a 64 bit Linux OS. The driver manages card OS state and | ||
| 12 | enables communication between host and card. Intel MIC X100 | ||
| 13 | devices are currently supported. | ||
| 14 | |||
| 15 | If you are building a host kernel with an Intel MIC device then | ||
| 16 | say M (recommended) or Y, else say N. If unsure say N. | ||
| 17 | |||
| 18 | More information about the Intel MIC family as well as the Linux | ||
| 19 | OS and tools for MIC to use with this driver are available from | ||
| 20 | <http://software.intel.com/en-us/mic-developer>. | ||
| 21 | |||
| 22 | comment "Intel MIC Card Driver" | ||
| 23 | |||
| 24 | config INTEL_MIC_CARD | ||
| 25 | tristate "Intel MIC Card Driver" | ||
| 26 | depends on 64BIT && X86 | ||
| 27 | select VIRTIO | ||
| 28 | default N | ||
| 29 | help | ||
| 30 | This enables card driver support for the Intel Many Integrated | ||
| 31 | Core (MIC) device family. The card driver communicates shutdown/ | ||
| 32 | crash events to the host and allows registration/configuration of | ||
| 33 | virtio devices. Intel MIC X100 devices are currently supported. | ||
| 34 | |||
| 35 | If you are building a card kernel for an Intel MIC device then | ||
| 36 | say M (recommended) or Y, else say N. If unsure say N. | ||
| 37 | |||
| 38 | For more information see | ||
| 39 | <http://software.intel.com/en-us/mic-developer>. | ||
diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile new file mode 100644 index 000000000000..05b34d683a58 --- /dev/null +++ b/drivers/misc/mic/Makefile | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # | ||
| 2 | # Makefile - Intel MIC Linux driver. | ||
| 3 | # Copyright(c) 2013, Intel Corporation. | ||
| 4 | # | ||
| 5 | obj-$(CONFIG_INTEL_MIC_HOST) += host/ | ||
| 6 | obj-$(CONFIG_INTEL_MIC_CARD) += card/ | ||
diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile new file mode 100644 index 000000000000..69d58bef92ce --- /dev/null +++ b/drivers/misc/mic/card/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # | ||
| 2 | # Makefile - Intel MIC Linux driver. | ||
| 3 | # Copyright(c) 2013, Intel Corporation. | ||
| 4 | # | ||
| 5 | ccflags-y += -DINTEL_MIC_CARD | ||
| 6 | |||
| 7 | obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o | ||
| 8 | mic_card-y += mic_x100.o | ||
| 9 | mic_card-y += mic_device.o | ||
| 10 | mic_card-y += mic_debugfs.o | ||
| 11 | mic_card-y += mic_virtio.o | ||
diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c new file mode 100644 index 000000000000..421b3d7911df --- /dev/null +++ b/drivers/misc/mic/card/mic_debugfs.c | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Intel MIC Card driver. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | #include <linux/debugfs.h> | ||
| 28 | #include <linux/delay.h> | ||
| 29 | #include <linux/seq_file.h> | ||
| 30 | #include <linux/interrupt.h> | ||
| 31 | #include <linux/device.h> | ||
| 32 | |||
| 33 | #include "../common/mic_dev.h" | ||
| 34 | #include "mic_device.h" | ||
| 35 | |||
| 36 | /* Debugfs parent dir */ | ||
| 37 | static struct dentry *mic_dbg; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * mic_intr_test - Send interrupts to host. | ||
| 41 | */ | ||
| 42 | static int mic_intr_test(struct seq_file *s, void *unused) | ||
| 43 | { | ||
| 44 | struct mic_driver *mdrv = s->private; | ||
| 45 | struct mic_device *mdev = &mdrv->mdev; | ||
| 46 | |||
| 47 | mic_send_intr(mdev, 0); | ||
| 48 | msleep(1000); | ||
| 49 | mic_send_intr(mdev, 1); | ||
| 50 | msleep(1000); | ||
| 51 | mic_send_intr(mdev, 2); | ||
| 52 | msleep(1000); | ||
| 53 | mic_send_intr(mdev, 3); | ||
| 54 | msleep(1000); | ||
| 55 | |||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | static int mic_intr_test_open(struct inode *inode, struct file *file) | ||
| 60 | { | ||
| 61 | return single_open(file, mic_intr_test, inode->i_private); | ||
| 62 | } | ||
| 63 | |||
| 64 | static int mic_intr_test_release(struct inode *inode, struct file *file) | ||
| 65 | { | ||
| 66 | return single_release(inode, file); | ||
| 67 | } | ||
| 68 | |||
| 69 | static const struct file_operations intr_test_ops = { | ||
| 70 | .owner = THIS_MODULE, | ||
| 71 | .open = mic_intr_test_open, | ||
| 72 | .read = seq_read, | ||
| 73 | .llseek = seq_lseek, | ||
| 74 | .release = mic_intr_test_release | ||
| 75 | }; | ||
| 76 | |||
| 77 | /** | ||
| 78 | * mic_create_card_debug_dir - Initialize MIC debugfs entries. | ||
| 79 | */ | ||
| 80 | void __init mic_create_card_debug_dir(struct mic_driver *mdrv) | ||
| 81 | { | ||
| 82 | struct dentry *d; | ||
| 83 | |||
| 84 | if (!mic_dbg) | ||
| 85 | return; | ||
| 86 | |||
| 87 | mdrv->dbg_dir = debugfs_create_dir(mdrv->name, mic_dbg); | ||
| 88 | if (!mdrv->dbg_dir) { | ||
| 89 | dev_err(mdrv->dev, "Cant create dbg_dir %s\n", mdrv->name); | ||
| 90 | return; | ||
| 91 | } | ||
| 92 | |||
| 93 | d = debugfs_create_file("intr_test", 0444, mdrv->dbg_dir, | ||
| 94 | mdrv, &intr_test_ops); | ||
| 95 | |||
| 96 | if (!d) { | ||
| 97 | dev_err(mdrv->dev, | ||
| 98 | "Cant create dbg intr_test %s\n", mdrv->name); | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries. | ||
| 105 | */ | ||
| 106 | void mic_delete_card_debug_dir(struct mic_driver *mdrv) | ||
| 107 | { | ||
| 108 | if (!mdrv->dbg_dir) | ||
| 109 | return; | ||
| 110 | |||
| 111 | debugfs_remove_recursive(mdrv->dbg_dir); | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * mic_init_card_debugfs - Initialize global debugfs entry. | ||
| 116 | */ | ||
| 117 | void __init mic_init_card_debugfs(void) | ||
| 118 | { | ||
| 119 | mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
| 120 | if (!mic_dbg) | ||
| 121 | pr_err("can't create debugfs dir\n"); | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * mic_exit_card_debugfs - Uninitialize global debugfs entry | ||
| 126 | */ | ||
| 127 | void mic_exit_card_debugfs(void) | ||
| 128 | { | ||
| 129 | debugfs_remove(mic_dbg); | ||
| 130 | } | ||
diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c new file mode 100644 index 000000000000..d0980ff96833 --- /dev/null +++ b/drivers/misc/mic/card/mic_device.c | |||
| @@ -0,0 +1,305 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Intel MIC Card driver. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/pci.h> | ||
| 29 | #include <linux/interrupt.h> | ||
| 30 | #include <linux/reboot.h> | ||
| 31 | |||
| 32 | #include <linux/mic_common.h> | ||
| 33 | #include "../common/mic_dev.h" | ||
| 34 | #include "mic_device.h" | ||
| 35 | #include "mic_virtio.h" | ||
| 36 | |||
| 37 | static struct mic_driver *g_drv; | ||
| 38 | static struct mic_irq *shutdown_cookie; | ||
| 39 | |||
| 40 | static void mic_notify_host(u8 state) | ||
| 41 | { | ||
| 42 | struct mic_driver *mdrv = g_drv; | ||
| 43 | struct mic_bootparam __iomem *bootparam = mdrv->dp; | ||
| 44 | |||
| 45 | iowrite8(state, &bootparam->shutdown_status); | ||
| 46 | dev_dbg(mdrv->dev, "%s %d system_state %d\n", | ||
| 47 | __func__, __LINE__, state); | ||
| 48 | mic_send_intr(&mdrv->mdev, ioread8(&bootparam->c2h_shutdown_db)); | ||
| 49 | } | ||
| 50 | |||
| 51 | static int mic_panic_event(struct notifier_block *this, unsigned long event, | ||
| 52 | void *ptr) | ||
| 53 | { | ||
| 54 | struct mic_driver *mdrv = g_drv; | ||
| 55 | struct mic_bootparam __iomem *bootparam = mdrv->dp; | ||
| 56 | |||
| 57 | iowrite8(-1, &bootparam->h2c_config_db); | ||
| 58 | iowrite8(-1, &bootparam->h2c_shutdown_db); | ||
| 59 | mic_notify_host(MIC_CRASHED); | ||
| 60 | return NOTIFY_DONE; | ||
| 61 | } | ||
| 62 | |||
| 63 | static struct notifier_block mic_panic = { | ||
| 64 | .notifier_call = mic_panic_event, | ||
| 65 | }; | ||
| 66 | |||
| 67 | static irqreturn_t mic_shutdown_isr(int irq, void *data) | ||
| 68 | { | ||
| 69 | struct mic_driver *mdrv = g_drv; | ||
| 70 | struct mic_bootparam __iomem *bootparam = mdrv->dp; | ||
| 71 | |||
| 72 | mic_ack_interrupt(&g_drv->mdev); | ||
| 73 | if (ioread8(&bootparam->shutdown_card)) | ||
| 74 | orderly_poweroff(true); | ||
| 75 | return IRQ_HANDLED; | ||
| 76 | } | ||
| 77 | |||
| 78 | static int mic_shutdown_init(void) | ||
| 79 | { | ||
| 80 | int rc = 0; | ||
| 81 | struct mic_driver *mdrv = g_drv; | ||
| 82 | struct mic_bootparam __iomem *bootparam = mdrv->dp; | ||
| 83 | int shutdown_db; | ||
| 84 | |||
| 85 | shutdown_db = mic_next_card_db(); | ||
| 86 | shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, | ||
| 87 | "Shutdown", mdrv, shutdown_db); | ||
| 88 | if (IS_ERR(shutdown_cookie)) | ||
| 89 | rc = PTR_ERR(shutdown_cookie); | ||
| 90 | else | ||
| 91 | iowrite8(shutdown_db, &bootparam->h2c_shutdown_db); | ||
| 92 | return rc; | ||
| 93 | } | ||
| 94 | |||
| 95 | static void mic_shutdown_uninit(void) | ||
| 96 | { | ||
| 97 | struct mic_driver *mdrv = g_drv; | ||
| 98 | struct mic_bootparam __iomem *bootparam = mdrv->dp; | ||
| 99 | |||
| 100 | iowrite8(-1, &bootparam->h2c_shutdown_db); | ||
| 101 | mic_free_card_irq(shutdown_cookie, mdrv); | ||
| 102 | } | ||
| 103 | |||
| 104 | static int __init mic_dp_init(void) | ||
| 105 | { | ||
| 106 | struct mic_driver *mdrv = g_drv; | ||
| 107 | struct mic_device *mdev = &mdrv->mdev; | ||
| 108 | struct mic_bootparam __iomem *bootparam; | ||
| 109 | u64 lo, hi, dp_dma_addr; | ||
| 110 | u32 magic; | ||
| 111 | |||
| 112 | lo = mic_read_spad(&mdrv->mdev, MIC_DPLO_SPAD); | ||
| 113 | hi = mic_read_spad(&mdrv->mdev, MIC_DPHI_SPAD); | ||
| 114 | |||
| 115 | dp_dma_addr = lo | (hi << 32); | ||
| 116 | mdrv->dp = mic_card_map(mdev, dp_dma_addr, MIC_DP_SIZE); | ||
| 117 | if (!mdrv->dp) { | ||
| 118 | dev_err(mdrv->dev, "Cannot remap Aperture BAR\n"); | ||
| 119 | return -ENOMEM; | ||
| 120 | } | ||
| 121 | bootparam = mdrv->dp; | ||
| 122 | magic = ioread32(&bootparam->magic); | ||
| 123 | if (MIC_MAGIC != magic) { | ||
| 124 | dev_err(mdrv->dev, "bootparam magic mismatch 0x%x\n", magic); | ||
| 125 | return -EIO; | ||
| 126 | } | ||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* Uninitialize the device page */ | ||
| 131 | static void mic_dp_uninit(void) | ||
| 132 | { | ||
| 133 | mic_card_unmap(&g_drv->mdev, g_drv->dp); | ||
| 134 | } | ||
| 135 | |||
| 136 | /** | ||
| 137 | * mic_request_card_irq - request an irq. | ||
| 138 | * | ||
| 139 | * @func: The callback function that handles the interrupt. | ||
| 140 | * @name: The ASCII name of the callee requesting the irq. | ||
| 141 | * @data: private data that is returned back when calling the | ||
| 142 | * function handler. | ||
| 143 | * @index: The doorbell index of the requester. | ||
| 144 | * | ||
| 145 | * returns: The cookie that is transparent to the caller. Passed | ||
| 146 | * back when calling mic_free_irq. An appropriate error code | ||
| 147 | * is returned on failure. Caller needs to use IS_ERR(return_val) | ||
| 148 | * to check for failure and PTR_ERR(return_val) to obtained the | ||
| 149 | * error code. | ||
| 150 | * | ||
| 151 | */ | ||
| 152 | struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), | ||
| 153 | const char *name, void *data, int index) | ||
| 154 | { | ||
| 155 | int rc = 0; | ||
| 156 | unsigned long cookie; | ||
| 157 | struct mic_driver *mdrv = g_drv; | ||
| 158 | |||
| 159 | rc = request_irq(mic_db_to_irq(mdrv, index), func, | ||
| 160 | 0, name, data); | ||
| 161 | if (rc) { | ||
| 162 | dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc); | ||
| 163 | goto err; | ||
| 164 | } | ||
| 165 | mdrv->irq_info.irq_usage_count[index]++; | ||
| 166 | cookie = index; | ||
| 167 | return (struct mic_irq *)cookie; | ||
| 168 | err: | ||
| 169 | return ERR_PTR(rc); | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * mic_free_card_irq - free irq. | ||
| 174 | * | ||
| 175 | * @cookie: cookie obtained during a successful call to mic_request_irq | ||
| 176 | * @data: private data specified by the calling function during the | ||
| 177 | * mic_request_irq | ||
| 178 | * | ||
| 179 | * returns: none. | ||
| 180 | */ | ||
| 181 | void mic_free_card_irq(struct mic_irq *cookie, void *data) | ||
| 182 | { | ||
| 183 | int index; | ||
| 184 | struct mic_driver *mdrv = g_drv; | ||
| 185 | |||
| 186 | index = (unsigned long)cookie & 0xFFFFU; | ||
| 187 | free_irq(mic_db_to_irq(mdrv, index), data); | ||
| 188 | mdrv->irq_info.irq_usage_count[index]--; | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * mic_next_card_db - Get the doorbell with minimum usage count. | ||
| 193 | * | ||
| 194 | * Returns the irq index. | ||
| 195 | */ | ||
| 196 | int mic_next_card_db(void) | ||
| 197 | { | ||
| 198 | int i; | ||
| 199 | int index = 0; | ||
| 200 | struct mic_driver *mdrv = g_drv; | ||
| 201 | |||
| 202 | for (i = 0; i < mdrv->intr_info.num_intr; i++) { | ||
| 203 | if (mdrv->irq_info.irq_usage_count[i] < | ||
| 204 | mdrv->irq_info.irq_usage_count[index]) | ||
| 205 | index = i; | ||
| 206 | } | ||
| 207 | |||
| 208 | return index; | ||
| 209 | } | ||
| 210 | |||
| 211 | /** | ||
| 212 | * mic_init_irq - Initialize irq information. | ||
| 213 | * | ||
| 214 | * Returns 0 in success. Appropriate error code on failure. | ||
| 215 | */ | ||
| 216 | static int mic_init_irq(void) | ||
| 217 | { | ||
| 218 | struct mic_driver *mdrv = g_drv; | ||
| 219 | |||
| 220 | mdrv->irq_info.irq_usage_count = kzalloc((sizeof(u32) * | ||
| 221 | mdrv->intr_info.num_intr), | ||
| 222 | GFP_KERNEL); | ||
| 223 | if (!mdrv->irq_info.irq_usage_count) | ||
| 224 | return -ENOMEM; | ||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * mic_uninit_irq - Uninitialize irq information. | ||
| 230 | * | ||
| 231 | * None. | ||
| 232 | */ | ||
| 233 | static void mic_uninit_irq(void) | ||
| 234 | { | ||
| 235 | struct mic_driver *mdrv = g_drv; | ||
| 236 | |||
| 237 | kfree(mdrv->irq_info.irq_usage_count); | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * mic_driver_init - MIC driver initialization tasks. | ||
| 242 | * | ||
| 243 | * Returns 0 in success. Appropriate error code on failure. | ||
| 244 | */ | ||
| 245 | int __init mic_driver_init(struct mic_driver *mdrv) | ||
| 246 | { | ||
| 247 | int rc; | ||
| 248 | |||
| 249 | g_drv = mdrv; | ||
| 250 | /* | ||
| 251 | * Unloading the card module is not supported. The MIC card module | ||
| 252 | * handles fundamental operations like host/card initiated shutdowns | ||
| 253 | * and informing the host about card crashes and cannot be unloaded. | ||
| 254 | */ | ||
| 255 | if (!try_module_get(mdrv->dev->driver->owner)) { | ||
| 256 | rc = -ENODEV; | ||
| 257 | goto done; | ||
| 258 | } | ||
| 259 | rc = mic_dp_init(); | ||
| 260 | if (rc) | ||
| 261 | goto put; | ||
| 262 | rc = mic_init_irq(); | ||
| 263 | if (rc) | ||
| 264 | goto dp_uninit; | ||
| 265 | rc = mic_shutdown_init(); | ||
| 266 | if (rc) | ||
| 267 | goto irq_uninit; | ||
| 268 | rc = mic_devices_init(mdrv); | ||
| 269 | if (rc) | ||
| 270 | goto shutdown_uninit; | ||
| 271 | mic_create_card_debug_dir(mdrv); | ||
| 272 | atomic_notifier_chain_register(&panic_notifier_list, &mic_panic); | ||
| 273 | done: | ||
| 274 | return rc; | ||
| 275 | shutdown_uninit: | ||
| 276 | mic_shutdown_uninit(); | ||
| 277 | irq_uninit: | ||
| 278 | mic_uninit_irq(); | ||
| 279 | dp_uninit: | ||
| 280 | mic_dp_uninit(); | ||
| 281 | put: | ||
| 282 | module_put(mdrv->dev->driver->owner); | ||
| 283 | return rc; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* | ||
| 287 | * mic_driver_uninit - MIC driver uninitialization tasks. | ||
| 288 | * | ||
| 289 | * Returns None | ||
| 290 | */ | ||
| 291 | void mic_driver_uninit(struct mic_driver *mdrv) | ||
| 292 | { | ||
| 293 | mic_delete_card_debug_dir(mdrv); | ||
| 294 | mic_devices_uninit(mdrv); | ||
| 295 | /* | ||
| 296 | * Inform the host about the shutdown status i.e. poweroff/restart etc. | ||
| 297 | * The module cannot be unloaded so the only code path to call | ||
| 298 | * mic_devices_uninit(..) is the shutdown callback. | ||
| 299 | */ | ||
| 300 | mic_notify_host(system_state); | ||
| 301 | mic_shutdown_uninit(); | ||
| 302 | mic_uninit_irq(); | ||
| 303 | mic_dp_uninit(); | ||
| 304 | module_put(mdrv->dev->driver->owner); | ||
| 305 | } | ||
diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h new file mode 100644 index 000000000000..347b9b3b7916 --- /dev/null +++ b/drivers/misc/mic/card/mic_device.h | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Intel MIC Card driver. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | #ifndef _MIC_CARD_DEVICE_H_ | ||
| 28 | #define _MIC_CARD_DEVICE_H_ | ||
| 29 | |||
| 30 | #include <linux/workqueue.h> | ||
| 31 | #include <linux/io.h> | ||
| 32 | |||
| 33 | /** | ||
| 34 | * struct mic_intr_info - Contains h/w specific interrupt sources info | ||
| 35 | * | ||
| 36 | * @num_intr: The number of irqs available | ||
| 37 | */ | ||
| 38 | struct mic_intr_info { | ||
| 39 | u32 num_intr; | ||
| 40 | }; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct mic_irq_info - OS specific irq information | ||
| 44 | * | ||
| 45 | * @irq_usage_count: usage count array tracking the number of sources | ||
| 46 | * assigned for each irq. | ||
| 47 | */ | ||
| 48 | struct mic_irq_info { | ||
| 49 | int *irq_usage_count; | ||
| 50 | }; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * struct mic_device - MIC device information. | ||
| 54 | * | ||
| 55 | * @mmio: MMIO bar information. | ||
| 56 | */ | ||
| 57 | struct mic_device { | ||
| 58 | struct mic_mw mmio; | ||
| 59 | }; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * struct mic_driver - MIC card driver information. | ||
| 63 | * | ||
| 64 | * @name: Name for MIC driver. | ||
| 65 | * @dbg_dir: debugfs directory of this MIC device. | ||
| 66 | * @dev: The device backing this MIC. | ||
| 67 | * @dp: The pointer to the virtio device page. | ||
| 68 | * @mdev: MIC device information for the host. | ||
| 69 | * @hotplug_work: Hot plug work for adding/removing virtio devices. | ||
| 70 | * @irq_info: The OS specific irq information | ||
| 71 | * @intr_info: H/W specific interrupt information. | ||
| 72 | */ | ||
| 73 | struct mic_driver { | ||
| 74 | char name[20]; | ||
| 75 | struct dentry *dbg_dir; | ||
| 76 | struct device *dev; | ||
| 77 | void __iomem *dp; | ||
| 78 | struct mic_device mdev; | ||
| 79 | struct work_struct hotplug_work; | ||
| 80 | struct mic_irq_info irq_info; | ||
| 81 | struct mic_intr_info intr_info; | ||
| 82 | }; | ||
| 83 | |||
| 84 | /** | ||
| 85 | * struct mic_irq - opaque pointer used as cookie | ||
| 86 | */ | ||
| 87 | struct mic_irq; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * mic_mmio_read - read from an MMIO register. | ||
| 91 | * @mw: MMIO register base virtual address. | ||
| 92 | * @offset: register offset. | ||
| 93 | * | ||
| 94 | * RETURNS: register value. | ||
| 95 | */ | ||
| 96 | static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) | ||
| 97 | { | ||
| 98 | return ioread32(mw->va + offset); | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * mic_mmio_write - write to an MMIO register. | ||
| 103 | * @mw: MMIO register base virtual address. | ||
| 104 | * @val: the data value to put into the register | ||
| 105 | * @offset: register offset. | ||
| 106 | * | ||
| 107 | * RETURNS: none. | ||
| 108 | */ | ||
| 109 | static inline void | ||
| 110 | mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) | ||
| 111 | { | ||
| 112 | iowrite32(val, mw->va + offset); | ||
| 113 | } | ||
| 114 | |||
| 115 | int mic_driver_init(struct mic_driver *mdrv); | ||
| 116 | void mic_driver_uninit(struct mic_driver *mdrv); | ||
| 117 | int mic_next_card_db(void); | ||
| 118 | struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), | ||
| 119 | const char *name, void *data, int intr_src); | ||
| 120 | void mic_free_card_irq(struct mic_irq *cookie, void *data); | ||
| 121 | u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); | ||
| 122 | void mic_send_intr(struct mic_device *mdev, int doorbell); | ||
| 123 | int mic_db_to_irq(struct mic_driver *mdrv, int db); | ||
| 124 | u32 mic_ack_interrupt(struct mic_device *mdev); | ||
| 125 | void mic_hw_intr_init(struct mic_driver *mdrv); | ||
| 126 | void __iomem * | ||
| 127 | mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size); | ||
| 128 | void mic_card_unmap(struct mic_device *mdev, void __iomem *addr); | ||
| 129 | void __init mic_create_card_debug_dir(struct mic_driver *mdrv); | ||
| 130 | void mic_delete_card_debug_dir(struct mic_driver *mdrv); | ||
| 131 | void __init mic_init_card_debugfs(void); | ||
| 132 | void mic_exit_card_debugfs(void); | ||
| 133 | #endif | ||
diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c new file mode 100644 index 000000000000..914cc9b2caad --- /dev/null +++ b/drivers/misc/mic/card/mic_virtio.c | |||
| @@ -0,0 +1,630 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Adapted from: | ||
| 25 | * | ||
| 26 | * virtio for kvm on s390 | ||
| 27 | * | ||
| 28 | * Copyright IBM Corp. 2008 | ||
| 29 | * | ||
| 30 | * This program is free software; you can redistribute it and/or modify | ||
| 31 | * it under the terms of the GNU General Public License (version 2 only) | ||
| 32 | * as published by the Free Software Foundation. | ||
| 33 | * | ||
| 34 | * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> | ||
| 35 | * | ||
| 36 | * Intel MIC Card driver. | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | #include <linux/delay.h> | ||
| 40 | #include <linux/slab.h> | ||
| 41 | #include <linux/virtio_config.h> | ||
| 42 | |||
| 43 | #include "../common/mic_dev.h" | ||
| 44 | #include "mic_virtio.h" | ||
| 45 | |||
| 46 | #define VIRTIO_SUBCODE_64 0x0D00 | ||
| 47 | |||
| 48 | #define MIC_MAX_VRINGS 4 | ||
| 49 | struct mic_vdev { | ||
| 50 | struct virtio_device vdev; | ||
| 51 | struct mic_device_desc __iomem *desc; | ||
| 52 | struct mic_device_ctrl __iomem *dc; | ||
| 53 | struct mic_device *mdev; | ||
| 54 | void __iomem *vr[MIC_MAX_VRINGS]; | ||
| 55 | int used_size[MIC_MAX_VRINGS]; | ||
| 56 | struct completion reset_done; | ||
| 57 | struct mic_irq *virtio_cookie; | ||
| 58 | int c2h_vdev_db; | ||
| 59 | }; | ||
| 60 | |||
| 61 | static struct mic_irq *virtio_config_cookie; | ||
| 62 | #define to_micvdev(vd) container_of(vd, struct mic_vdev, vdev) | ||
| 63 | |||
| 64 | /* Helper API to obtain the parent of the virtio device */ | ||
| 65 | static inline struct device *mic_dev(struct mic_vdev *mvdev) | ||
| 66 | { | ||
| 67 | return mvdev->vdev.dev.parent; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* This gets the device's feature bits. */ | ||
| 71 | static u32 mic_get_features(struct virtio_device *vdev) | ||
| 72 | { | ||
| 73 | unsigned int i, bits; | ||
| 74 | u32 features = 0; | ||
| 75 | struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; | ||
| 76 | u8 __iomem *in_features = mic_vq_features(desc); | ||
| 77 | int feature_len = ioread8(&desc->feature_len); | ||
| 78 | |||
| 79 | bits = min_t(unsigned, feature_len, | ||
| 80 | sizeof(vdev->features)) * 8; | ||
| 81 | for (i = 0; i < bits; i++) | ||
| 82 | if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) | ||
| 83 | features |= BIT(i); | ||
| 84 | |||
| 85 | return features; | ||
| 86 | } | ||
| 87 | |||
| 88 | static void mic_finalize_features(struct virtio_device *vdev) | ||
| 89 | { | ||
| 90 | unsigned int i, bits; | ||
| 91 | struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; | ||
| 92 | u8 feature_len = ioread8(&desc->feature_len); | ||
| 93 | /* Second half of bitmap is features we accept. */ | ||
| 94 | u8 __iomem *out_features = | ||
| 95 | mic_vq_features(desc) + feature_len; | ||
| 96 | |||
| 97 | /* Give virtio_ring a chance to accept features. */ | ||
| 98 | vring_transport_features(vdev); | ||
| 99 | |||
| 100 | memset_io(out_features, 0, feature_len); | ||
| 101 | bits = min_t(unsigned, feature_len, | ||
| 102 | sizeof(vdev->features)) * 8; | ||
| 103 | for (i = 0; i < bits; i++) { | ||
| 104 | if (test_bit(i, vdev->features)) | ||
| 105 | iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), | ||
| 106 | &out_features[i / 8]); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | /* | ||
| 111 | * Reading and writing elements in config space | ||
| 112 | */ | ||
| 113 | static void mic_get(struct virtio_device *vdev, unsigned int offset, | ||
| 114 | void *buf, unsigned len) | ||
| 115 | { | ||
| 116 | struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; | ||
| 117 | |||
| 118 | if (offset + len > ioread8(&desc->config_len)) | ||
| 119 | return; | ||
| 120 | memcpy_fromio(buf, mic_vq_configspace(desc) + offset, len); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void mic_set(struct virtio_device *vdev, unsigned int offset, | ||
| 124 | const void *buf, unsigned len) | ||
| 125 | { | ||
| 126 | struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; | ||
| 127 | |||
| 128 | if (offset + len > ioread8(&desc->config_len)) | ||
| 129 | return; | ||
| 130 | memcpy_toio(mic_vq_configspace(desc) + offset, buf, len); | ||
| 131 | } | ||
| 132 | |||
| 133 | /* | ||
| 134 | * The operations to get and set the status word just access the status | ||
| 135 | * field of the device descriptor. set_status also interrupts the host | ||
| 136 | * to tell about status changes. | ||
| 137 | */ | ||
| 138 | static u8 mic_get_status(struct virtio_device *vdev) | ||
| 139 | { | ||
| 140 | return ioread8(&to_micvdev(vdev)->desc->status); | ||
| 141 | } | ||
| 142 | |||
| 143 | static void mic_set_status(struct virtio_device *vdev, u8 status) | ||
| 144 | { | ||
| 145 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 146 | if (!status) | ||
| 147 | return; | ||
| 148 | iowrite8(status, &mvdev->desc->status); | ||
| 149 | mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); | ||
| 150 | } | ||
| 151 | |||
| 152 | /* Inform host on a virtio device reset and wait for ack from host */ | ||
| 153 | static void mic_reset_inform_host(struct virtio_device *vdev) | ||
| 154 | { | ||
| 155 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 156 | struct mic_device_ctrl __iomem *dc = mvdev->dc; | ||
| 157 | int retry = 100, i; | ||
| 158 | |||
| 159 | iowrite8(0, &dc->host_ack); | ||
| 160 | iowrite8(1, &dc->vdev_reset); | ||
| 161 | mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); | ||
| 162 | |||
| 163 | /* Wait till host completes all card accesses and acks the reset */ | ||
| 164 | for (i = retry; i--;) { | ||
| 165 | if (ioread8(&dc->host_ack)) | ||
| 166 | break; | ||
| 167 | msleep(100); | ||
| 168 | }; | ||
| 169 | |||
| 170 | dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); | ||
| 171 | |||
| 172 | /* Reset status to 0 in case we timed out */ | ||
| 173 | iowrite8(0, &mvdev->desc->status); | ||
| 174 | } | ||
| 175 | |||
| 176 | static void mic_reset(struct virtio_device *vdev) | ||
| 177 | { | ||
| 178 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 179 | |||
| 180 | dev_dbg(mic_dev(mvdev), "%s: virtio id %d\n", | ||
| 181 | __func__, vdev->id.device); | ||
| 182 | |||
| 183 | mic_reset_inform_host(vdev); | ||
| 184 | complete_all(&mvdev->reset_done); | ||
| 185 | } | ||
| 186 | |||
| 187 | /* | ||
| 188 | * The virtio_ring code calls this API when it wants to notify the Host. | ||
| 189 | */ | ||
| 190 | static void mic_notify(struct virtqueue *vq) | ||
| 191 | { | ||
| 192 | struct mic_vdev *mvdev = vq->priv; | ||
| 193 | |||
| 194 | mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); | ||
| 195 | } | ||
| 196 | |||
| 197 | static void mic_del_vq(struct virtqueue *vq, int n) | ||
| 198 | { | ||
| 199 | struct mic_vdev *mvdev = to_micvdev(vq->vdev); | ||
| 200 | struct vring *vr = (struct vring *)(vq + 1); | ||
| 201 | |||
| 202 | free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n])); | ||
| 203 | vring_del_virtqueue(vq); | ||
| 204 | mic_card_unmap(mvdev->mdev, mvdev->vr[n]); | ||
| 205 | mvdev->vr[n] = NULL; | ||
| 206 | } | ||
| 207 | |||
| 208 | static void mic_del_vqs(struct virtio_device *vdev) | ||
| 209 | { | ||
| 210 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 211 | struct virtqueue *vq, *n; | ||
| 212 | int idx = 0; | ||
| 213 | |||
| 214 | dev_dbg(mic_dev(mvdev), "%s\n", __func__); | ||
| 215 | |||
| 216 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) | ||
| 217 | mic_del_vq(vq, idx++); | ||
| 218 | } | ||
| 219 | |||
| 220 | /* | ||
| 221 | * This routine will assign vring's allocated in host/io memory. Code in | ||
| 222 | * virtio_ring.c however continues to access this io memory as if it were local | ||
| 223 | * memory without io accessors. | ||
| 224 | */ | ||
| 225 | static struct virtqueue *mic_find_vq(struct virtio_device *vdev, | ||
| 226 | unsigned index, | ||
| 227 | void (*callback)(struct virtqueue *vq), | ||
| 228 | const char *name) | ||
| 229 | { | ||
| 230 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 231 | struct mic_vqconfig __iomem *vqconfig; | ||
| 232 | struct mic_vqconfig config; | ||
| 233 | struct virtqueue *vq; | ||
| 234 | void __iomem *va; | ||
| 235 | struct _mic_vring_info __iomem *info; | ||
| 236 | void *used; | ||
| 237 | int vr_size, _vr_size, err, magic; | ||
| 238 | struct vring *vr; | ||
| 239 | u8 type = ioread8(&mvdev->desc->type); | ||
| 240 | |||
| 241 | if (index >= ioread8(&mvdev->desc->num_vq)) | ||
| 242 | return ERR_PTR(-ENOENT); | ||
| 243 | |||
| 244 | if (!name) | ||
| 245 | return ERR_PTR(-ENOENT); | ||
| 246 | |||
| 247 | /* First assign the vring's allocated in host memory */ | ||
| 248 | vqconfig = mic_vq_config(mvdev->desc) + index; | ||
| 249 | memcpy_fromio(&config, vqconfig, sizeof(config)); | ||
| 250 | _vr_size = vring_size(config.num, MIC_VIRTIO_RING_ALIGN); | ||
| 251 | vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); | ||
| 252 | va = mic_card_map(mvdev->mdev, config.address, vr_size); | ||
| 253 | if (!va) | ||
| 254 | return ERR_PTR(-ENOMEM); | ||
| 255 | mvdev->vr[index] = va; | ||
| 256 | memset_io(va, 0x0, _vr_size); | ||
| 257 | vq = vring_new_virtqueue(index, | ||
| 258 | config.num, MIC_VIRTIO_RING_ALIGN, vdev, | ||
| 259 | false, | ||
| 260 | va, mic_notify, callback, name); | ||
| 261 | if (!vq) { | ||
| 262 | err = -ENOMEM; | ||
| 263 | goto unmap; | ||
| 264 | } | ||
| 265 | info = va + _vr_size; | ||
| 266 | magic = ioread32(&info->magic); | ||
| 267 | |||
| 268 | if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { | ||
| 269 | err = -EIO; | ||
| 270 | goto unmap; | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Allocate and reassign used ring now */ | ||
| 274 | mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + | ||
| 275 | sizeof(struct vring_used_elem) * config.num); | ||
| 276 | used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, | ||
| 277 | get_order(mvdev->used_size[index])); | ||
| 278 | if (!used) { | ||
| 279 | err = -ENOMEM; | ||
| 280 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 281 | __func__, __LINE__, err); | ||
| 282 | goto del_vq; | ||
| 283 | } | ||
| 284 | iowrite64(virt_to_phys(used), &vqconfig->used_address); | ||
| 285 | |||
| 286 | /* | ||
| 287 | * To reassign the used ring here we are directly accessing | ||
| 288 | * struct vring_virtqueue which is a private data structure | ||
| 289 | * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in | ||
| 290 | * vring_new_virtqueue() would ensure that | ||
| 291 | * (&vq->vring == (struct vring *) (&vq->vq + 1)); | ||
| 292 | */ | ||
| 293 | vr = (struct vring *)(vq + 1); | ||
| 294 | vr->used = used; | ||
| 295 | |||
| 296 | vq->priv = mvdev; | ||
| 297 | return vq; | ||
| 298 | del_vq: | ||
| 299 | vring_del_virtqueue(vq); | ||
| 300 | unmap: | ||
| 301 | mic_card_unmap(mvdev->mdev, mvdev->vr[index]); | ||
| 302 | return ERR_PTR(err); | ||
| 303 | } | ||
| 304 | |||
| 305 | static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs, | ||
| 306 | struct virtqueue *vqs[], | ||
| 307 | vq_callback_t *callbacks[], | ||
| 308 | const char *names[]) | ||
| 309 | { | ||
| 310 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 311 | struct mic_device_ctrl __iomem *dc = mvdev->dc; | ||
| 312 | int i, err, retry = 100; | ||
| 313 | |||
| 314 | /* We must have this many virtqueues. */ | ||
| 315 | if (nvqs > ioread8(&mvdev->desc->num_vq)) | ||
| 316 | return -ENOENT; | ||
| 317 | |||
| 318 | for (i = 0; i < nvqs; ++i) { | ||
| 319 | dev_dbg(mic_dev(mvdev), "%s: %d: %s\n", | ||
| 320 | __func__, i, names[i]); | ||
| 321 | vqs[i] = mic_find_vq(vdev, i, callbacks[i], names[i]); | ||
| 322 | if (IS_ERR(vqs[i])) { | ||
| 323 | err = PTR_ERR(vqs[i]); | ||
| 324 | goto error; | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | iowrite8(1, &dc->used_address_updated); | ||
| 329 | /* | ||
| 330 | * Send an interrupt to the host to inform it that used | ||
| 331 | * rings have been re-assigned. | ||
| 332 | */ | ||
| 333 | mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); | ||
| 334 | for (i = retry; i--;) { | ||
| 335 | if (!ioread8(&dc->used_address_updated)) | ||
| 336 | break; | ||
| 337 | msleep(100); | ||
| 338 | }; | ||
| 339 | |||
| 340 | dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); | ||
| 341 | if (!retry) { | ||
| 342 | err = -ENODEV; | ||
| 343 | goto error; | ||
| 344 | } | ||
| 345 | |||
| 346 | return 0; | ||
| 347 | error: | ||
| 348 | mic_del_vqs(vdev); | ||
| 349 | return err; | ||
| 350 | } | ||
| 351 | |||
| 352 | /* | ||
| 353 | * The config ops structure as defined by virtio config | ||
| 354 | */ | ||
| 355 | static struct virtio_config_ops mic_vq_config_ops = { | ||
| 356 | .get_features = mic_get_features, | ||
| 357 | .finalize_features = mic_finalize_features, | ||
| 358 | .get = mic_get, | ||
| 359 | .set = mic_set, | ||
| 360 | .get_status = mic_get_status, | ||
| 361 | .set_status = mic_set_status, | ||
| 362 | .reset = mic_reset, | ||
| 363 | .find_vqs = mic_find_vqs, | ||
| 364 | .del_vqs = mic_del_vqs, | ||
| 365 | }; | ||
| 366 | |||
| 367 | static irqreturn_t | ||
| 368 | mic_virtio_intr_handler(int irq, void *data) | ||
| 369 | { | ||
| 370 | struct mic_vdev *mvdev = data; | ||
| 371 | struct virtqueue *vq; | ||
| 372 | |||
| 373 | mic_ack_interrupt(mvdev->mdev); | ||
| 374 | list_for_each_entry(vq, &mvdev->vdev.vqs, list) | ||
| 375 | vring_interrupt(0, vq); | ||
| 376 | |||
| 377 | return IRQ_HANDLED; | ||
| 378 | } | ||
| 379 | |||
| 380 | static void mic_virtio_release_dev(struct device *_d) | ||
| 381 | { | ||
| 382 | /* | ||
| 383 | * No need for a release method similar to virtio PCI. | ||
| 384 | * Provide an empty one to avoid getting a warning from core. | ||
| 385 | */ | ||
| 386 | } | ||
| 387 | |||
| 388 | /* | ||
| 389 | * adds a new device and register it with virtio | ||
| 390 | * appropriate drivers are loaded by the device model | ||
| 391 | */ | ||
| 392 | static int mic_add_device(struct mic_device_desc __iomem *d, | ||
| 393 | unsigned int offset, struct mic_driver *mdrv) | ||
| 394 | { | ||
| 395 | struct mic_vdev *mvdev; | ||
| 396 | int ret; | ||
| 397 | int virtio_db; | ||
| 398 | u8 type = ioread8(&d->type); | ||
| 399 | |||
| 400 | mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); | ||
| 401 | if (!mvdev) { | ||
| 402 | dev_err(mdrv->dev, "Cannot allocate mic dev %u type %u\n", | ||
| 403 | offset, type); | ||
| 404 | return -ENOMEM; | ||
| 405 | } | ||
| 406 | |||
| 407 | mvdev->mdev = &mdrv->mdev; | ||
| 408 | mvdev->vdev.dev.parent = mdrv->dev; | ||
| 409 | mvdev->vdev.dev.release = mic_virtio_release_dev; | ||
| 410 | mvdev->vdev.id.device = type; | ||
| 411 | mvdev->vdev.config = &mic_vq_config_ops; | ||
| 412 | mvdev->desc = d; | ||
| 413 | mvdev->dc = (void __iomem *)d + mic_aligned_desc_size(d); | ||
| 414 | init_completion(&mvdev->reset_done); | ||
| 415 | |||
| 416 | virtio_db = mic_next_card_db(); | ||
| 417 | mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, | ||
| 418 | "virtio intr", mvdev, virtio_db); | ||
| 419 | if (IS_ERR(mvdev->virtio_cookie)) { | ||
| 420 | ret = PTR_ERR(mvdev->virtio_cookie); | ||
| 421 | goto kfree; | ||
| 422 | } | ||
| 423 | iowrite8((u8)virtio_db, &mvdev->dc->h2c_vdev_db); | ||
| 424 | mvdev->c2h_vdev_db = ioread8(&mvdev->dc->c2h_vdev_db); | ||
| 425 | |||
| 426 | ret = register_virtio_device(&mvdev->vdev); | ||
| 427 | if (ret) { | ||
| 428 | dev_err(mic_dev(mvdev), | ||
| 429 | "Failed to register mic device %u type %u\n", | ||
| 430 | offset, type); | ||
| 431 | goto free_irq; | ||
| 432 | } | ||
| 433 | iowrite64((u64)mvdev, &mvdev->dc->vdev); | ||
| 434 | dev_dbg(mic_dev(mvdev), "%s: registered mic device %u type %u mvdev %p\n", | ||
| 435 | __func__, offset, type, mvdev); | ||
| 436 | |||
| 437 | return 0; | ||
| 438 | |||
| 439 | free_irq: | ||
| 440 | mic_free_card_irq(mvdev->virtio_cookie, mvdev); | ||
| 441 | kfree: | ||
| 442 | kfree(mvdev); | ||
| 443 | return ret; | ||
| 444 | } | ||
| 445 | |||
| 446 | /* | ||
| 447 | * match for a mic device with a specific desc pointer | ||
| 448 | */ | ||
| 449 | static int mic_match_desc(struct device *dev, void *data) | ||
| 450 | { | ||
| 451 | struct virtio_device *vdev = dev_to_virtio(dev); | ||
| 452 | struct mic_vdev *mvdev = to_micvdev(vdev); | ||
| 453 | |||
| 454 | return mvdev->desc == (void __iomem *)data; | ||
| 455 | } | ||
| 456 | |||
| 457 | static void mic_handle_config_change(struct mic_device_desc __iomem *d, | ||
| 458 | unsigned int offset, struct mic_driver *mdrv) | ||
| 459 | { | ||
| 460 | struct mic_device_ctrl __iomem *dc | ||
| 461 | = (void __iomem *)d + mic_aligned_desc_size(d); | ||
| 462 | struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); | ||
| 463 | struct virtio_driver *drv; | ||
| 464 | |||
| 465 | if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) | ||
| 466 | return; | ||
| 467 | |||
| 468 | dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__); | ||
| 469 | drv = container_of(mvdev->vdev.dev.driver, | ||
| 470 | struct virtio_driver, driver); | ||
| 471 | if (drv->config_changed) | ||
| 472 | drv->config_changed(&mvdev->vdev); | ||
| 473 | iowrite8(1, &dc->guest_ack); | ||
| 474 | } | ||
| 475 | |||
| 476 | /* | ||
| 477 | * removes a virtio device if a hot remove event has been | ||
| 478 | * requested by the host. | ||
| 479 | */ | ||
| 480 | static int mic_remove_device(struct mic_device_desc __iomem *d, | ||
| 481 | unsigned int offset, struct mic_driver *mdrv) | ||
| 482 | { | ||
| 483 | struct mic_device_ctrl __iomem *dc | ||
| 484 | = (void __iomem *)d + mic_aligned_desc_size(d); | ||
| 485 | struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); | ||
| 486 | u8 status; | ||
| 487 | int ret = -1; | ||
| 488 | |||
| 489 | if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { | ||
| 490 | dev_dbg(mdrv->dev, | ||
| 491 | "%s %d config_change %d type %d mvdev %p\n", | ||
| 492 | __func__, __LINE__, | ||
| 493 | ioread8(&dc->config_change), ioread8(&d->type), mvdev); | ||
| 494 | |||
| 495 | status = ioread8(&d->status); | ||
| 496 | INIT_COMPLETION(mvdev->reset_done); | ||
| 497 | unregister_virtio_device(&mvdev->vdev); | ||
| 498 | mic_free_card_irq(mvdev->virtio_cookie, mvdev); | ||
| 499 | if (status & VIRTIO_CONFIG_S_DRIVER_OK) | ||
| 500 | wait_for_completion(&mvdev->reset_done); | ||
| 501 | kfree(mvdev); | ||
| 502 | iowrite8(1, &dc->guest_ack); | ||
| 503 | dev_dbg(mdrv->dev, "%s %d guest_ack %d\n", | ||
| 504 | __func__, __LINE__, ioread8(&dc->guest_ack)); | ||
| 505 | ret = 0; | ||
| 506 | } | ||
| 507 | |||
| 508 | return ret; | ||
| 509 | } | ||
| 510 | |||
| 511 | #define REMOVE_DEVICES true | ||
| 512 | |||
| 513 | static void mic_scan_devices(struct mic_driver *mdrv, bool remove) | ||
| 514 | { | ||
| 515 | s8 type; | ||
| 516 | unsigned int i; | ||
| 517 | struct mic_device_desc __iomem *d; | ||
| 518 | struct mic_device_ctrl __iomem *dc; | ||
| 519 | struct device *dev; | ||
| 520 | int ret; | ||
| 521 | |||
| 522 | for (i = mic_aligned_size(struct mic_bootparam); | ||
| 523 | i < MIC_DP_SIZE; i += mic_total_desc_size(d)) { | ||
| 524 | d = mdrv->dp + i; | ||
| 525 | dc = (void __iomem *)d + mic_aligned_desc_size(d); | ||
| 526 | /* | ||
| 527 | * This read barrier is paired with the corresponding write | ||
| 528 | * barrier on the host which is inserted before adding or | ||
| 529 | * removing a virtio device descriptor, by updating the type. | ||
| 530 | */ | ||
| 531 | rmb(); | ||
| 532 | type = ioread8(&d->type); | ||
| 533 | |||
| 534 | /* end of list */ | ||
| 535 | if (type == 0) | ||
| 536 | break; | ||
| 537 | |||
| 538 | if (type == -1) | ||
| 539 | continue; | ||
| 540 | |||
| 541 | /* device already exists */ | ||
| 542 | dev = device_find_child(mdrv->dev, d, mic_match_desc); | ||
| 543 | if (dev) { | ||
| 544 | if (remove) | ||
| 545 | iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, | ||
| 546 | &dc->config_change); | ||
| 547 | put_device(dev); | ||
| 548 | mic_handle_config_change(d, i, mdrv); | ||
| 549 | ret = mic_remove_device(d, i, mdrv); | ||
| 550 | if (!ret && !remove) | ||
| 551 | iowrite8(-1, &d->type); | ||
| 552 | if (remove) { | ||
| 553 | iowrite8(0, &dc->config_change); | ||
| 554 | iowrite8(0, &dc->guest_ack); | ||
| 555 | } | ||
| 556 | continue; | ||
| 557 | } | ||
| 558 | |||
| 559 | /* new device */ | ||
| 560 | dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n", | ||
| 561 | __func__, __LINE__, d); | ||
| 562 | if (!remove) | ||
| 563 | mic_add_device(d, i, mdrv); | ||
| 564 | } | ||
| 565 | } | ||
| 566 | |||
| 567 | /* | ||
| 568 | * mic_hotplug_device tries to find changes in the device page. | ||
| 569 | */ | ||
| 570 | static void mic_hotplug_devices(struct work_struct *work) | ||
| 571 | { | ||
| 572 | struct mic_driver *mdrv = container_of(work, | ||
| 573 | struct mic_driver, hotplug_work); | ||
| 574 | |||
| 575 | mic_scan_devices(mdrv, !REMOVE_DEVICES); | ||
| 576 | } | ||
| 577 | |||
| 578 | /* | ||
| 579 | * Interrupt handler for hot plug/config changes etc. | ||
| 580 | */ | ||
| 581 | static irqreturn_t | ||
| 582 | mic_extint_handler(int irq, void *data) | ||
| 583 | { | ||
| 584 | struct mic_driver *mdrv = (struct mic_driver *)data; | ||
| 585 | |||
| 586 | dev_dbg(mdrv->dev, "%s %d hotplug work\n", | ||
| 587 | __func__, __LINE__); | ||
| 588 | mic_ack_interrupt(&mdrv->mdev); | ||
| 589 | schedule_work(&mdrv->hotplug_work); | ||
| 590 | return IRQ_HANDLED; | ||
| 591 | } | ||
| 592 | |||
| 593 | /* | ||
| 594 | * Init function for virtio | ||
| 595 | */ | ||
| 596 | int mic_devices_init(struct mic_driver *mdrv) | ||
| 597 | { | ||
| 598 | int rc; | ||
| 599 | struct mic_bootparam __iomem *bootparam; | ||
| 600 | int config_db; | ||
| 601 | |||
| 602 | INIT_WORK(&mdrv->hotplug_work, mic_hotplug_devices); | ||
| 603 | mic_scan_devices(mdrv, !REMOVE_DEVICES); | ||
| 604 | |||
| 605 | config_db = mic_next_card_db(); | ||
| 606 | virtio_config_cookie = mic_request_card_irq(mic_extint_handler, | ||
| 607 | "virtio_config_intr", mdrv, config_db); | ||
| 608 | if (IS_ERR(virtio_config_cookie)) { | ||
| 609 | rc = PTR_ERR(virtio_config_cookie); | ||
| 610 | goto exit; | ||
| 611 | } | ||
| 612 | |||
| 613 | bootparam = mdrv->dp; | ||
| 614 | iowrite8(config_db, &bootparam->h2c_config_db); | ||
| 615 | return 0; | ||
| 616 | exit: | ||
| 617 | return rc; | ||
| 618 | } | ||
| 619 | |||
| 620 | /* | ||
| 621 | * Uninit function for virtio | ||
| 622 | */ | ||
| 623 | void mic_devices_uninit(struct mic_driver *mdrv) | ||
| 624 | { | ||
| 625 | struct mic_bootparam __iomem *bootparam = mdrv->dp; | ||
| 626 | iowrite8(-1, &bootparam->h2c_config_db); | ||
| 627 | mic_free_card_irq(virtio_config_cookie, mdrv); | ||
| 628 | flush_work(&mdrv->hotplug_work); | ||
| 629 | mic_scan_devices(mdrv, REMOVE_DEVICES); | ||
| 630 | } | ||
diff --git a/drivers/misc/mic/card/mic_virtio.h b/drivers/misc/mic/card/mic_virtio.h new file mode 100644 index 000000000000..2c5c22c93ba8 --- /dev/null +++ b/drivers/misc/mic/card/mic_virtio.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Intel MIC Card driver. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | #ifndef __MIC_CARD_VIRTIO_H | ||
| 28 | #define __MIC_CARD_VIRTIO_H | ||
| 29 | |||
| 30 | #include <linux/mic_common.h> | ||
| 31 | #include "mic_device.h" | ||
| 32 | |||
| 33 | /* | ||
| 34 | * 64 bit I/O access | ||
| 35 | */ | ||
| 36 | #ifndef ioread64 | ||
| 37 | #define ioread64 readq | ||
| 38 | #endif | ||
| 39 | #ifndef iowrite64 | ||
| 40 | #define iowrite64 writeq | ||
| 41 | #endif | ||
| 42 | |||
| 43 | static inline unsigned mic_desc_size(struct mic_device_desc __iomem *desc) | ||
| 44 | { | ||
| 45 | return mic_aligned_size(*desc) | ||
| 46 | + ioread8(&desc->num_vq) * mic_aligned_size(struct mic_vqconfig) | ||
| 47 | + ioread8(&desc->feature_len) * 2 | ||
| 48 | + ioread8(&desc->config_len); | ||
| 49 | } | ||
| 50 | |||
| 51 | static inline struct mic_vqconfig __iomem * | ||
| 52 | mic_vq_config(struct mic_device_desc __iomem *desc) | ||
| 53 | { | ||
| 54 | return (struct mic_vqconfig __iomem *)(desc + 1); | ||
| 55 | } | ||
| 56 | |||
| 57 | static inline __u8 __iomem * | ||
| 58 | mic_vq_features(struct mic_device_desc __iomem *desc) | ||
| 59 | { | ||
| 60 | return (__u8 __iomem *)(mic_vq_config(desc) + ioread8(&desc->num_vq)); | ||
| 61 | } | ||
| 62 | |||
| 63 | static inline __u8 __iomem * | ||
| 64 | mic_vq_configspace(struct mic_device_desc __iomem *desc) | ||
| 65 | { | ||
| 66 | return mic_vq_features(desc) + ioread8(&desc->feature_len) * 2; | ||
| 67 | } | ||
| 68 | static inline unsigned mic_total_desc_size(struct mic_device_desc __iomem *desc) | ||
| 69 | { | ||
| 70 | return mic_aligned_desc_size(desc) + | ||
| 71 | mic_aligned_size(struct mic_device_ctrl); | ||
| 72 | } | ||
| 73 | |||
| 74 | int mic_devices_init(struct mic_driver *mdrv); | ||
| 75 | void mic_devices_uninit(struct mic_driver *mdrv); | ||
| 76 | |||
| 77 | #endif | ||
diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c new file mode 100644 index 000000000000..2868945c9a4d --- /dev/null +++ b/drivers/misc/mic/card/mic_x100.c | |||
| @@ -0,0 +1,256 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Intel MIC Card driver. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/pci.h> | ||
| 29 | #include <linux/platform_device.h> | ||
| 30 | |||
| 31 | #include "../common/mic_dev.h" | ||
| 32 | #include "mic_device.h" | ||
| 33 | #include "mic_x100.h" | ||
| 34 | |||
| 35 | static const char mic_driver_name[] = "mic"; | ||
| 36 | |||
| 37 | static struct mic_driver g_drv; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * mic_read_spad - read from the scratchpad register | ||
| 41 | * @mdev: pointer to mic_device instance | ||
| 42 | * @idx: index to scratchpad register, 0 based | ||
| 43 | * | ||
| 44 | * This function allows reading of the 32bit scratchpad register. | ||
| 45 | * | ||
| 46 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 47 | */ | ||
| 48 | u32 mic_read_spad(struct mic_device *mdev, unsigned int idx) | ||
| 49 | { | ||
| 50 | return mic_mmio_read(&mdev->mmio, | ||
| 51 | MIC_X100_SBOX_BASE_ADDRESS + | ||
| 52 | MIC_X100_SBOX_SPAD0 + idx * 4); | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * __mic_send_intr - Send interrupt to Host. | ||
| 57 | * @mdev: pointer to mic_device instance | ||
| 58 | * @doorbell: Doorbell number. | ||
| 59 | */ | ||
| 60 | void mic_send_intr(struct mic_device *mdev, int doorbell) | ||
| 61 | { | ||
| 62 | struct mic_mw *mw = &mdev->mmio; | ||
| 63 | |||
| 64 | if (doorbell > MIC_X100_MAX_DOORBELL_IDX) | ||
| 65 | return; | ||
| 66 | /* Ensure that the interrupt is ordered w.r.t previous stores. */ | ||
| 67 | wmb(); | ||
| 68 | mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, | ||
| 69 | MIC_X100_SBOX_BASE_ADDRESS + | ||
| 70 | (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * mic_ack_interrupt - Device specific interrupt handling. | ||
| 75 | * @mdev: pointer to mic_device instance | ||
| 76 | * | ||
| 77 | * Returns: bitmask of doorbell events triggered. | ||
| 78 | */ | ||
| 79 | u32 mic_ack_interrupt(struct mic_device *mdev) | ||
| 80 | { | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline int mic_get_sbox_irq(int db) | ||
| 85 | { | ||
| 86 | return MIC_X100_IRQ_BASE + db; | ||
| 87 | } | ||
| 88 | |||
| 89 | static inline int mic_get_rdmasr_irq(int index) | ||
| 90 | { | ||
| 91 | return MIC_X100_RDMASR_IRQ_BASE + index; | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * mic_hw_intr_init - Initialize h/w specific interrupt | ||
| 96 | * information. | ||
| 97 | * @mdrv: pointer to mic_driver | ||
| 98 | */ | ||
| 99 | void mic_hw_intr_init(struct mic_driver *mdrv) | ||
| 100 | { | ||
| 101 | mdrv->intr_info.num_intr = MIC_X100_NUM_SBOX_IRQ + | ||
| 102 | MIC_X100_NUM_RDMASR_IRQ; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * mic_db_to_irq - Retrieve irq number corresponding to a doorbell. | ||
| 107 | * @mdrv: pointer to mic_driver | ||
| 108 | * @db: The doorbell obtained for which the irq is needed. Doorbell | ||
| 109 | * may correspond to an sbox doorbell or an rdmasr index. | ||
| 110 | * | ||
| 111 | * Returns the irq corresponding to the doorbell. | ||
| 112 | */ | ||
| 113 | int mic_db_to_irq(struct mic_driver *mdrv, int db) | ||
| 114 | { | ||
| 115 | int rdmasr_index; | ||
| 116 | if (db < MIC_X100_NUM_SBOX_IRQ) { | ||
| 117 | return mic_get_sbox_irq(db); | ||
| 118 | } else { | ||
| 119 | rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ + | ||
| 120 | MIC_X100_RDMASR_IRQ_BASE; | ||
| 121 | return mic_get_rdmasr_irq(rdmasr_index); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | /* | ||
| 126 | * mic_card_map - Allocate virtual address for a remote memory region. | ||
| 127 | * @mdev: pointer to mic_device instance. | ||
| 128 | * @addr: Remote DMA address. | ||
| 129 | * @size: Size of the region. | ||
| 130 | * | ||
| 131 | * Returns: Virtual address backing the remote memory region. | ||
| 132 | */ | ||
| 133 | void __iomem * | ||
| 134 | mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size) | ||
| 135 | { | ||
| 136 | return ioremap(addr, size); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* | ||
| 140 | * mic_card_unmap - Unmap the virtual address for a remote memory region. | ||
| 141 | * @mdev: pointer to mic_device instance. | ||
| 142 | * @addr: Virtual address for remote memory region. | ||
| 143 | * | ||
| 144 | * Returns: None. | ||
| 145 | */ | ||
| 146 | void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) | ||
| 147 | { | ||
| 148 | iounmap(addr); | ||
| 149 | } | ||
| 150 | |||
| 151 | static int __init mic_probe(struct platform_device *pdev) | ||
| 152 | { | ||
| 153 | struct mic_driver *mdrv = &g_drv; | ||
| 154 | struct mic_device *mdev = &mdrv->mdev; | ||
| 155 | int rc = 0; | ||
| 156 | |||
| 157 | mdrv->dev = &pdev->dev; | ||
| 158 | snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name); | ||
| 159 | |||
| 160 | mdev->mmio.pa = MIC_X100_MMIO_BASE; | ||
| 161 | mdev->mmio.len = MIC_X100_MMIO_LEN; | ||
| 162 | mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN); | ||
| 163 | if (!mdev->mmio.va) { | ||
| 164 | dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); | ||
| 165 | rc = -EIO; | ||
| 166 | goto done; | ||
| 167 | } | ||
| 168 | mic_hw_intr_init(mdrv); | ||
| 169 | rc = mic_driver_init(mdrv); | ||
| 170 | if (rc) { | ||
| 171 | dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); | ||
| 172 | goto iounmap; | ||
| 173 | } | ||
| 174 | done: | ||
| 175 | return rc; | ||
| 176 | iounmap: | ||
| 177 | iounmap(mdev->mmio.va); | ||
| 178 | return rc; | ||
| 179 | } | ||
| 180 | |||
| 181 | static int mic_remove(struct platform_device *pdev) | ||
| 182 | { | ||
| 183 | struct mic_driver *mdrv = &g_drv; | ||
| 184 | struct mic_device *mdev = &mdrv->mdev; | ||
| 185 | |||
| 186 | mic_driver_uninit(mdrv); | ||
| 187 | iounmap(mdev->mmio.va); | ||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | static void mic_platform_shutdown(struct platform_device *pdev) | ||
| 192 | { | ||
| 193 | mic_remove(pdev); | ||
| 194 | } | ||
| 195 | |||
| 196 | static struct platform_device mic_platform_dev = { | ||
| 197 | .name = mic_driver_name, | ||
| 198 | .id = 0, | ||
| 199 | .num_resources = 0, | ||
| 200 | }; | ||
| 201 | |||
| 202 | static struct platform_driver __refdata mic_platform_driver = { | ||
| 203 | .probe = mic_probe, | ||
| 204 | .remove = mic_remove, | ||
| 205 | .shutdown = mic_platform_shutdown, | ||
| 206 | .driver = { | ||
| 207 | .name = mic_driver_name, | ||
| 208 | .owner = THIS_MODULE, | ||
| 209 | }, | ||
| 210 | }; | ||
| 211 | |||
| 212 | static int __init mic_init(void) | ||
| 213 | { | ||
| 214 | int ret; | ||
| 215 | struct cpuinfo_x86 *c = &cpu_data(0); | ||
| 216 | |||
| 217 | if (!(c->x86 == 11 && c->x86_model == 1)) { | ||
| 218 | ret = -ENODEV; | ||
| 219 | pr_err("%s not running on X100 ret %d\n", __func__, ret); | ||
| 220 | goto done; | ||
| 221 | } | ||
| 222 | |||
| 223 | mic_init_card_debugfs(); | ||
| 224 | ret = platform_device_register(&mic_platform_dev); | ||
| 225 | if (ret) { | ||
| 226 | pr_err("platform_device_register ret %d\n", ret); | ||
| 227 | goto cleanup_debugfs; | ||
| 228 | } | ||
| 229 | ret = platform_driver_register(&mic_platform_driver); | ||
| 230 | if (ret) { | ||
| 231 | pr_err("platform_driver_register ret %d\n", ret); | ||
| 232 | goto device_unregister; | ||
| 233 | } | ||
| 234 | return ret; | ||
| 235 | |||
| 236 | device_unregister: | ||
| 237 | platform_device_unregister(&mic_platform_dev); | ||
| 238 | cleanup_debugfs: | ||
| 239 | mic_exit_card_debugfs(); | ||
| 240 | done: | ||
| 241 | return ret; | ||
| 242 | } | ||
| 243 | |||
| 244 | static void __exit mic_exit(void) | ||
| 245 | { | ||
| 246 | platform_driver_unregister(&mic_platform_driver); | ||
| 247 | platform_device_unregister(&mic_platform_dev); | ||
| 248 | mic_exit_card_debugfs(); | ||
| 249 | } | ||
| 250 | |||
| 251 | module_init(mic_init); | ||
| 252 | module_exit(mic_exit); | ||
| 253 | |||
| 254 | MODULE_AUTHOR("Intel Corporation"); | ||
| 255 | MODULE_DESCRIPTION("Intel(R) MIC X100 Card driver"); | ||
| 256 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/mic/card/mic_x100.h b/drivers/misc/mic/card/mic_x100.h new file mode 100644 index 000000000000..d66ea55639c3 --- /dev/null +++ b/drivers/misc/mic/card/mic_x100.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Disclaimer: The codes contained in these modules may be specific to | ||
| 19 | * the Intel Software Development Platform codenamed: Knights Ferry, and | ||
| 20 | * the Intel product codenamed: Knights Corner, and are not backward | ||
| 21 | * compatible with other Intel products. Additionally, Intel will NOT | ||
| 22 | * support the codes or instruction set in future products. | ||
| 23 | * | ||
| 24 | * Intel MIC Card driver. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | #ifndef _MIC_X100_CARD_H_ | ||
| 28 | #define _MIC_X100_CARD_H_ | ||
| 29 | |||
| 30 | #define MIC_X100_MMIO_BASE 0x08007C0000ULL | ||
| 31 | #define MIC_X100_MMIO_LEN 0x00020000ULL | ||
| 32 | #define MIC_X100_SBOX_BASE_ADDRESS 0x00010000ULL | ||
| 33 | |||
| 34 | #define MIC_X100_SBOX_SPAD0 0x0000AB20 | ||
| 35 | #define MIC_X100_SBOX_SDBIC0 0x0000CC90 | ||
| 36 | #define MIC_X100_SBOX_SDBIC0_DBREQ_BIT 0x80000000 | ||
| 37 | #define MIC_X100_SBOX_RDMASR0 0x0000B180 | ||
| 38 | |||
| 39 | #define MIC_X100_MAX_DOORBELL_IDX 8 | ||
| 40 | |||
| 41 | #define MIC_X100_NUM_SBOX_IRQ 8 | ||
| 42 | #define MIC_X100_NUM_RDMASR_IRQ 8 | ||
| 43 | #define MIC_X100_SBOX_IRQ_BASE 0 | ||
| 44 | #define MIC_X100_RDMASR_IRQ_BASE 17 | ||
| 45 | |||
| 46 | #define MIC_X100_IRQ_BASE 26 | ||
| 47 | |||
| 48 | #endif | ||
diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h new file mode 100644 index 000000000000..92999c2bbf82 --- /dev/null +++ b/drivers/misc/mic/common/mic_dev.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef __MIC_DEV_H__ | ||
| 22 | #define __MIC_DEV_H__ | ||
| 23 | |||
| 24 | /** | ||
| 25 | * struct mic_mw - MIC memory window | ||
| 26 | * | ||
| 27 | * @pa: Base physical address. | ||
| 28 | * @va: Base ioremap'd virtual address. | ||
| 29 | * @len: Size of the memory window. | ||
| 30 | */ | ||
| 31 | struct mic_mw { | ||
| 32 | phys_addr_t pa; | ||
| 33 | void __iomem *va; | ||
| 34 | resource_size_t len; | ||
| 35 | }; | ||
| 36 | |||
| 37 | /* | ||
| 38 | * Scratch pad register offsets used by the host to communicate | ||
| 39 | * device page DMA address to the card. | ||
| 40 | */ | ||
| 41 | #define MIC_DPLO_SPAD 14 | ||
| 42 | #define MIC_DPHI_SPAD 15 | ||
| 43 | |||
| 44 | /* | ||
| 45 | * These values are supposed to be in the config_change field of the | ||
| 46 | * device page when the host sends a config change interrupt to the card. | ||
| 47 | */ | ||
| 48 | #define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 | ||
| 49 | #define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 | ||
| 50 | |||
| 51 | #endif | ||
diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile new file mode 100644 index 000000000000..c2197f999394 --- /dev/null +++ b/drivers/misc/mic/host/Makefile | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # | ||
| 2 | # Makefile - Intel MIC Linux driver. | ||
| 3 | # Copyright(c) 2013, Intel Corporation. | ||
| 4 | # | ||
| 5 | obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o | ||
| 6 | mic_host-objs := mic_main.o | ||
| 7 | mic_host-objs += mic_x100.o | ||
| 8 | mic_host-objs += mic_sysfs.o | ||
| 9 | mic_host-objs += mic_smpt.o | ||
| 10 | mic_host-objs += mic_intr.o | ||
| 11 | mic_host-objs += mic_boot.o | ||
| 12 | mic_host-objs += mic_debugfs.o | ||
| 13 | mic_host-objs += mic_fops.o | ||
| 14 | mic_host-objs += mic_virtio.o | ||
diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c new file mode 100644 index 000000000000..b079c65eed6d --- /dev/null +++ b/drivers/misc/mic/host/mic_boot.c | |||
| @@ -0,0 +1,300 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/delay.h> | ||
| 22 | #include <linux/firmware.h> | ||
| 23 | #include <linux/pci.h> | ||
| 24 | |||
| 25 | #include <linux/mic_common.h> | ||
| 26 | #include "../common/mic_dev.h" | ||
| 27 | #include "mic_device.h" | ||
| 28 | #include "mic_smpt.h" | ||
| 29 | #include "mic_virtio.h" | ||
| 30 | |||
| 31 | /** | ||
| 32 | * mic_reset - Reset the MIC device. | ||
| 33 | * @mdev: pointer to mic_device instance | ||
| 34 | */ | ||
| 35 | static void mic_reset(struct mic_device *mdev) | ||
| 36 | { | ||
| 37 | int i; | ||
| 38 | |||
| 39 | #define MIC_RESET_TO (45) | ||
| 40 | |||
| 41 | INIT_COMPLETION(mdev->reset_wait); | ||
| 42 | mdev->ops->reset_fw_ready(mdev); | ||
| 43 | mdev->ops->reset(mdev); | ||
| 44 | |||
| 45 | for (i = 0; i < MIC_RESET_TO; i++) { | ||
| 46 | if (mdev->ops->is_fw_ready(mdev)) | ||
| 47 | goto done; | ||
| 48 | /* | ||
| 49 | * Resets typically take 10s of seconds to complete. | ||
| 50 | * Since an MMIO read is required to check if the | ||
| 51 | * firmware is ready or not, a 1 second delay works nicely. | ||
| 52 | */ | ||
| 53 | msleep(1000); | ||
| 54 | } | ||
| 55 | mic_set_state(mdev, MIC_RESET_FAILED); | ||
| 56 | done: | ||
| 57 | complete_all(&mdev->reset_wait); | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Initialize the MIC bootparams */ | ||
| 61 | void mic_bootparam_init(struct mic_device *mdev) | ||
| 62 | { | ||
| 63 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 64 | |||
| 65 | bootparam->magic = MIC_MAGIC; | ||
| 66 | bootparam->c2h_shutdown_db = mdev->shutdown_db; | ||
| 67 | bootparam->h2c_shutdown_db = -1; | ||
| 68 | bootparam->h2c_config_db = -1; | ||
| 69 | bootparam->shutdown_status = 0; | ||
| 70 | bootparam->shutdown_card = 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * mic_start - Start the MIC. | ||
| 75 | * @mdev: pointer to mic_device instance | ||
| 76 | * @buf: buffer containing boot string including firmware/ramdisk path. | ||
| 77 | * | ||
| 78 | * This function prepares an MIC for boot and initiates boot. | ||
| 79 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 80 | */ | ||
| 81 | int mic_start(struct mic_device *mdev, const char *buf) | ||
| 82 | { | ||
| 83 | int rc; | ||
| 84 | mutex_lock(&mdev->mic_mutex); | ||
| 85 | retry: | ||
| 86 | if (MIC_OFFLINE != mdev->state) { | ||
| 87 | rc = -EINVAL; | ||
| 88 | goto unlock_ret; | ||
| 89 | } | ||
| 90 | if (!mdev->ops->is_fw_ready(mdev)) { | ||
| 91 | mic_reset(mdev); | ||
| 92 | /* | ||
| 93 | * The state will either be MIC_OFFLINE if the reset succeeded | ||
| 94 | * or MIC_RESET_FAILED if the firmware reset failed. | ||
| 95 | */ | ||
| 96 | goto retry; | ||
| 97 | } | ||
| 98 | rc = mdev->ops->load_mic_fw(mdev, buf); | ||
| 99 | if (rc) | ||
| 100 | goto unlock_ret; | ||
| 101 | mic_smpt_restore(mdev); | ||
| 102 | mic_intr_restore(mdev); | ||
| 103 | mdev->intr_ops->enable_interrupts(mdev); | ||
| 104 | mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); | ||
| 105 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); | ||
| 106 | mdev->ops->send_firmware_intr(mdev); | ||
| 107 | mic_set_state(mdev, MIC_ONLINE); | ||
| 108 | unlock_ret: | ||
| 109 | mutex_unlock(&mdev->mic_mutex); | ||
| 110 | return rc; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * mic_stop - Prepare the MIC for reset and trigger reset. | ||
| 115 | * @mdev: pointer to mic_device instance | ||
| 116 | * @force: force a MIC to reset even if it is already offline. | ||
| 117 | * | ||
| 118 | * RETURNS: None. | ||
| 119 | */ | ||
| 120 | void mic_stop(struct mic_device *mdev, bool force) | ||
| 121 | { | ||
| 122 | mutex_lock(&mdev->mic_mutex); | ||
| 123 | if (MIC_OFFLINE != mdev->state || force) { | ||
| 124 | mic_virtio_reset_devices(mdev); | ||
| 125 | mic_bootparam_init(mdev); | ||
| 126 | mic_reset(mdev); | ||
| 127 | if (MIC_RESET_FAILED == mdev->state) | ||
| 128 | goto unlock; | ||
| 129 | mic_set_shutdown_status(mdev, MIC_NOP); | ||
| 130 | if (MIC_SUSPENDED != mdev->state) | ||
| 131 | mic_set_state(mdev, MIC_OFFLINE); | ||
| 132 | } | ||
| 133 | unlock: | ||
| 134 | mutex_unlock(&mdev->mic_mutex); | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * mic_shutdown - Initiate MIC shutdown. | ||
| 139 | * @mdev: pointer to mic_device instance | ||
| 140 | * | ||
| 141 | * RETURNS: None. | ||
| 142 | */ | ||
| 143 | void mic_shutdown(struct mic_device *mdev) | ||
| 144 | { | ||
| 145 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 146 | s8 db = bootparam->h2c_shutdown_db; | ||
| 147 | |||
| 148 | mutex_lock(&mdev->mic_mutex); | ||
| 149 | if (MIC_ONLINE == mdev->state && db != -1) { | ||
| 150 | bootparam->shutdown_card = 1; | ||
| 151 | mdev->ops->send_intr(mdev, db); | ||
| 152 | mic_set_state(mdev, MIC_SHUTTING_DOWN); | ||
| 153 | } | ||
| 154 | mutex_unlock(&mdev->mic_mutex); | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 158 | * mic_shutdown_work - Handle shutdown interrupt from MIC. | ||
| 159 | * @work: The work structure. | ||
| 160 | * | ||
| 161 | * This work is scheduled whenever the host has received a shutdown | ||
| 162 | * interrupt from the MIC. | ||
| 163 | */ | ||
| 164 | void mic_shutdown_work(struct work_struct *work) | ||
| 165 | { | ||
| 166 | struct mic_device *mdev = container_of(work, struct mic_device, | ||
| 167 | shutdown_work); | ||
| 168 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 169 | |||
| 170 | mutex_lock(&mdev->mic_mutex); | ||
| 171 | mic_set_shutdown_status(mdev, bootparam->shutdown_status); | ||
| 172 | bootparam->shutdown_status = 0; | ||
| 173 | |||
| 174 | /* | ||
| 175 | * if state is MIC_SUSPENDED, OSPM suspend is in progress. We do not | ||
| 176 | * change the state here so as to prevent users from booting the card | ||
| 177 | * during and after the suspend operation. | ||
| 178 | */ | ||
| 179 | if (MIC_SHUTTING_DOWN != mdev->state && | ||
| 180 | MIC_SUSPENDED != mdev->state) | ||
| 181 | mic_set_state(mdev, MIC_SHUTTING_DOWN); | ||
| 182 | mutex_unlock(&mdev->mic_mutex); | ||
| 183 | } | ||
| 184 | |||
| 185 | /** | ||
| 186 | * mic_reset_trigger_work - Trigger MIC reset. | ||
| 187 | * @work: The work structure. | ||
| 188 | * | ||
| 189 | * This work is scheduled whenever the host wants to reset the MIC. | ||
| 190 | */ | ||
| 191 | void mic_reset_trigger_work(struct work_struct *work) | ||
| 192 | { | ||
| 193 | struct mic_device *mdev = container_of(work, struct mic_device, | ||
| 194 | reset_trigger_work); | ||
| 195 | |||
| 196 | mic_stop(mdev, false); | ||
| 197 | } | ||
| 198 | |||
| 199 | /** | ||
| 200 | * mic_complete_resume - Complete MIC Resume after an OSPM suspend/hibernate | ||
| 201 | * event. | ||
| 202 | * @mdev: pointer to mic_device instance | ||
| 203 | * | ||
| 204 | * RETURNS: None. | ||
| 205 | */ | ||
| 206 | void mic_complete_resume(struct mic_device *mdev) | ||
| 207 | { | ||
| 208 | if (mdev->state != MIC_SUSPENDED) { | ||
| 209 | dev_warn(mdev->sdev->parent, "state %d should be %d\n", | ||
| 210 | mdev->state, MIC_SUSPENDED); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | /* Make sure firmware is ready */ | ||
| 215 | if (!mdev->ops->is_fw_ready(mdev)) | ||
| 216 | mic_stop(mdev, true); | ||
| 217 | |||
| 218 | mutex_lock(&mdev->mic_mutex); | ||
| 219 | mic_set_state(mdev, MIC_OFFLINE); | ||
| 220 | mutex_unlock(&mdev->mic_mutex); | ||
| 221 | } | ||
| 222 | |||
| 223 | /** | ||
| 224 | * mic_prepare_suspend - Handle suspend notification for the MIC device. | ||
| 225 | * @mdev: pointer to mic_device instance | ||
| 226 | * | ||
| 227 | * RETURNS: None. | ||
| 228 | */ | ||
| 229 | void mic_prepare_suspend(struct mic_device *mdev) | ||
| 230 | { | ||
| 231 | int rc; | ||
| 232 | |||
| 233 | #define MIC_SUSPEND_TIMEOUT (60 * HZ) | ||
| 234 | |||
| 235 | mutex_lock(&mdev->mic_mutex); | ||
| 236 | switch (mdev->state) { | ||
| 237 | case MIC_OFFLINE: | ||
| 238 | /* | ||
| 239 | * Card is already offline. Set state to MIC_SUSPENDED | ||
| 240 | * to prevent users from booting the card. | ||
| 241 | */ | ||
| 242 | mic_set_state(mdev, MIC_SUSPENDED); | ||
| 243 | mutex_unlock(&mdev->mic_mutex); | ||
| 244 | break; | ||
| 245 | case MIC_ONLINE: | ||
| 246 | /* | ||
| 247 | * Card is online. Set state to MIC_SUSPENDING and notify | ||
| 248 | * MIC user space daemon which will issue card | ||
| 249 | * shutdown and reset. | ||
| 250 | */ | ||
| 251 | mic_set_state(mdev, MIC_SUSPENDING); | ||
| 252 | mutex_unlock(&mdev->mic_mutex); | ||
| 253 | rc = wait_for_completion_timeout(&mdev->reset_wait, | ||
| 254 | MIC_SUSPEND_TIMEOUT); | ||
| 255 | /* Force reset the card if the shutdown completion timed out */ | ||
| 256 | if (!rc) { | ||
| 257 | mutex_lock(&mdev->mic_mutex); | ||
| 258 | mic_set_state(mdev, MIC_SUSPENDED); | ||
| 259 | mutex_unlock(&mdev->mic_mutex); | ||
| 260 | mic_stop(mdev, true); | ||
| 261 | } | ||
| 262 | break; | ||
| 263 | case MIC_SHUTTING_DOWN: | ||
| 264 | /* | ||
| 265 | * Card is shutting down. Set state to MIC_SUSPENDED | ||
| 266 | * to prevent further boot of the card. | ||
| 267 | */ | ||
| 268 | mic_set_state(mdev, MIC_SUSPENDED); | ||
| 269 | mutex_unlock(&mdev->mic_mutex); | ||
| 270 | rc = wait_for_completion_timeout(&mdev->reset_wait, | ||
| 271 | MIC_SUSPEND_TIMEOUT); | ||
| 272 | /* Force reset the card if the shutdown completion timed out */ | ||
| 273 | if (!rc) | ||
| 274 | mic_stop(mdev, true); | ||
| 275 | break; | ||
| 276 | default: | ||
| 277 | mutex_unlock(&mdev->mic_mutex); | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | /** | ||
| 283 | * mic_suspend - Initiate MIC suspend. Suspend merely issues card shutdown. | ||
| 284 | * @mdev: pointer to mic_device instance | ||
| 285 | * | ||
| 286 | * RETURNS: None. | ||
| 287 | */ | ||
| 288 | void mic_suspend(struct mic_device *mdev) | ||
| 289 | { | ||
| 290 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 291 | s8 db = bootparam->h2c_shutdown_db; | ||
| 292 | |||
| 293 | mutex_lock(&mdev->mic_mutex); | ||
| 294 | if (MIC_SUSPENDING == mdev->state && db != -1) { | ||
| 295 | bootparam->shutdown_card = 1; | ||
| 296 | mdev->ops->send_intr(mdev, db); | ||
| 297 | mic_set_state(mdev, MIC_SUSPENDED); | ||
| 298 | } | ||
| 299 | mutex_unlock(&mdev->mic_mutex); | ||
| 300 | } | ||
diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c new file mode 100644 index 000000000000..028ba5d6fd1c --- /dev/null +++ b/drivers/misc/mic/host/mic_debugfs.c | |||
| @@ -0,0 +1,491 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/debugfs.h> | ||
| 22 | #include <linux/pci.h> | ||
| 23 | #include <linux/seq_file.h> | ||
| 24 | |||
| 25 | #include <linux/mic_common.h> | ||
| 26 | #include "../common/mic_dev.h" | ||
| 27 | #include "mic_device.h" | ||
| 28 | #include "mic_smpt.h" | ||
| 29 | #include "mic_virtio.h" | ||
| 30 | |||
| 31 | /* Debugfs parent dir */ | ||
| 32 | static struct dentry *mic_dbg; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * mic_log_buf_show - Display MIC kernel log buffer. | ||
| 36 | * | ||
| 37 | * log_buf addr/len is read from System.map by user space | ||
| 38 | * and populated in sysfs entries. | ||
| 39 | */ | ||
| 40 | static int mic_log_buf_show(struct seq_file *s, void *unused) | ||
| 41 | { | ||
| 42 | void __iomem *log_buf_va; | ||
| 43 | int __iomem *log_buf_len_va; | ||
| 44 | struct mic_device *mdev = s->private; | ||
| 45 | void *kva; | ||
| 46 | int size; | ||
| 47 | unsigned long aper_offset; | ||
| 48 | |||
| 49 | if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len) | ||
| 50 | goto done; | ||
| 51 | /* | ||
| 52 | * Card kernel will never be relocated and any kernel text/data mapping | ||
| 53 | * can be translated to phys address by subtracting __START_KERNEL_map. | ||
| 54 | */ | ||
| 55 | aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map; | ||
| 56 | log_buf_len_va = mdev->aper.va + aper_offset; | ||
| 57 | aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map; | ||
| 58 | log_buf_va = mdev->aper.va + aper_offset; | ||
| 59 | size = ioread32(log_buf_len_va); | ||
| 60 | |||
| 61 | kva = kmalloc(size, GFP_KERNEL); | ||
| 62 | if (!kva) | ||
| 63 | goto done; | ||
| 64 | mutex_lock(&mdev->mic_mutex); | ||
| 65 | memcpy_fromio(kva, log_buf_va, size); | ||
| 66 | switch (mdev->state) { | ||
| 67 | case MIC_ONLINE: | ||
| 68 | /* Fall through */ | ||
| 69 | case MIC_SHUTTING_DOWN: | ||
| 70 | seq_write(s, kva, size); | ||
| 71 | break; | ||
| 72 | default: | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | mutex_unlock(&mdev->mic_mutex); | ||
| 76 | kfree(kva); | ||
| 77 | done: | ||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | |||
| 81 | static int mic_log_buf_open(struct inode *inode, struct file *file) | ||
| 82 | { | ||
| 83 | return single_open(file, mic_log_buf_show, inode->i_private); | ||
| 84 | } | ||
| 85 | |||
| 86 | static int mic_log_buf_release(struct inode *inode, struct file *file) | ||
| 87 | { | ||
| 88 | return single_release(inode, file); | ||
| 89 | } | ||
| 90 | |||
| 91 | static const struct file_operations log_buf_ops = { | ||
| 92 | .owner = THIS_MODULE, | ||
| 93 | .open = mic_log_buf_open, | ||
| 94 | .read = seq_read, | ||
| 95 | .llseek = seq_lseek, | ||
| 96 | .release = mic_log_buf_release | ||
| 97 | }; | ||
| 98 | |||
| 99 | static int mic_smpt_show(struct seq_file *s, void *pos) | ||
| 100 | { | ||
| 101 | int i; | ||
| 102 | struct mic_device *mdev = s->private; | ||
| 103 | unsigned long flags; | ||
| 104 | |||
| 105 | seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", | ||
| 106 | mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); | ||
| 107 | seq_puts(s, "====================================================\n"); | ||
| 108 | |||
| 109 | if (mdev->smpt) { | ||
| 110 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
| 111 | spin_lock_irqsave(&smpt_info->smpt_lock, flags); | ||
| 112 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
| 113 | seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", | ||
| 114 | " ", i, smpt_info->entry[i].dma_addr, | ||
| 115 | smpt_info->entry[i].ref_count); | ||
| 116 | } | ||
| 117 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
| 118 | } | ||
| 119 | seq_puts(s, "====================================================\n"); | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 123 | static int mic_smpt_debug_open(struct inode *inode, struct file *file) | ||
| 124 | { | ||
| 125 | return single_open(file, mic_smpt_show, inode->i_private); | ||
| 126 | } | ||
| 127 | |||
| 128 | static int mic_smpt_debug_release(struct inode *inode, struct file *file) | ||
| 129 | { | ||
| 130 | return single_release(inode, file); | ||
| 131 | } | ||
| 132 | |||
| 133 | static const struct file_operations smpt_file_ops = { | ||
| 134 | .owner = THIS_MODULE, | ||
| 135 | .open = mic_smpt_debug_open, | ||
| 136 | .read = seq_read, | ||
| 137 | .llseek = seq_lseek, | ||
| 138 | .release = mic_smpt_debug_release | ||
| 139 | }; | ||
| 140 | |||
| 141 | static int mic_soft_reset_show(struct seq_file *s, void *pos) | ||
| 142 | { | ||
| 143 | struct mic_device *mdev = s->private; | ||
| 144 | |||
| 145 | mic_stop(mdev, true); | ||
| 146 | return 0; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int mic_soft_reset_debug_open(struct inode *inode, struct file *file) | ||
| 150 | { | ||
| 151 | return single_open(file, mic_soft_reset_show, inode->i_private); | ||
| 152 | } | ||
| 153 | |||
| 154 | static int mic_soft_reset_debug_release(struct inode *inode, struct file *file) | ||
| 155 | { | ||
| 156 | return single_release(inode, file); | ||
| 157 | } | ||
| 158 | |||
| 159 | static const struct file_operations soft_reset_ops = { | ||
| 160 | .owner = THIS_MODULE, | ||
| 161 | .open = mic_soft_reset_debug_open, | ||
| 162 | .read = seq_read, | ||
| 163 | .llseek = seq_lseek, | ||
| 164 | .release = mic_soft_reset_debug_release | ||
| 165 | }; | ||
| 166 | |||
| 167 | static int mic_post_code_show(struct seq_file *s, void *pos) | ||
| 168 | { | ||
| 169 | struct mic_device *mdev = s->private; | ||
| 170 | u32 reg = mdev->ops->get_postcode(mdev); | ||
| 171 | |||
| 172 | seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff); | ||
| 173 | return 0; | ||
| 174 | } | ||
| 175 | |||
| 176 | static int mic_post_code_debug_open(struct inode *inode, struct file *file) | ||
| 177 | { | ||
| 178 | return single_open(file, mic_post_code_show, inode->i_private); | ||
| 179 | } | ||
| 180 | |||
| 181 | static int mic_post_code_debug_release(struct inode *inode, struct file *file) | ||
| 182 | { | ||
| 183 | return single_release(inode, file); | ||
| 184 | } | ||
| 185 | |||
| 186 | static const struct file_operations post_code_ops = { | ||
| 187 | .owner = THIS_MODULE, | ||
| 188 | .open = mic_post_code_debug_open, | ||
| 189 | .read = seq_read, | ||
| 190 | .llseek = seq_lseek, | ||
| 191 | .release = mic_post_code_debug_release | ||
| 192 | }; | ||
| 193 | |||
| 194 | static int mic_dp_show(struct seq_file *s, void *pos) | ||
| 195 | { | ||
| 196 | struct mic_device *mdev = s->private; | ||
| 197 | struct mic_device_desc *d; | ||
| 198 | struct mic_device_ctrl *dc; | ||
| 199 | struct mic_vqconfig *vqconfig; | ||
| 200 | __u32 *features; | ||
| 201 | __u8 *config; | ||
| 202 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 203 | int i, j; | ||
| 204 | |||
| 205 | seq_printf(s, "Bootparam: magic 0x%x\n", | ||
| 206 | bootparam->magic); | ||
| 207 | seq_printf(s, "Bootparam: h2c_shutdown_db %d\n", | ||
| 208 | bootparam->h2c_shutdown_db); | ||
| 209 | seq_printf(s, "Bootparam: h2c_config_db %d\n", | ||
| 210 | bootparam->h2c_config_db); | ||
| 211 | seq_printf(s, "Bootparam: c2h_shutdown_db %d\n", | ||
| 212 | bootparam->c2h_shutdown_db); | ||
| 213 | seq_printf(s, "Bootparam: shutdown_status %d\n", | ||
| 214 | bootparam->shutdown_status); | ||
| 215 | seq_printf(s, "Bootparam: shutdown_card %d\n", | ||
| 216 | bootparam->shutdown_card); | ||
| 217 | |||
| 218 | for (i = sizeof(*bootparam); i < MIC_DP_SIZE; | ||
| 219 | i += mic_total_desc_size(d)) { | ||
| 220 | d = mdev->dp + i; | ||
| 221 | dc = (void *)d + mic_aligned_desc_size(d); | ||
| 222 | |||
| 223 | /* end of list */ | ||
| 224 | if (d->type == 0) | ||
| 225 | break; | ||
| 226 | |||
| 227 | if (d->type == -1) | ||
| 228 | continue; | ||
| 229 | |||
| 230 | seq_printf(s, "Type %d ", d->type); | ||
| 231 | seq_printf(s, "Num VQ %d ", d->num_vq); | ||
| 232 | seq_printf(s, "Feature Len %d\n", d->feature_len); | ||
| 233 | seq_printf(s, "Config Len %d ", d->config_len); | ||
| 234 | seq_printf(s, "Shutdown Status %d\n", d->status); | ||
| 235 | |||
| 236 | for (j = 0; j < d->num_vq; j++) { | ||
| 237 | vqconfig = mic_vq_config(d) + j; | ||
| 238 | seq_printf(s, "vqconfig[%d]: ", j); | ||
| 239 | seq_printf(s, "address 0x%llx ", vqconfig->address); | ||
| 240 | seq_printf(s, "num %d ", vqconfig->num); | ||
| 241 | seq_printf(s, "used address 0x%llx\n", | ||
| 242 | vqconfig->used_address); | ||
| 243 | } | ||
| 244 | |||
| 245 | features = (__u32 *)mic_vq_features(d); | ||
| 246 | seq_printf(s, "Features: Host 0x%x ", features[0]); | ||
| 247 | seq_printf(s, "Guest 0x%x\n", features[1]); | ||
| 248 | |||
| 249 | config = mic_vq_configspace(d); | ||
| 250 | for (j = 0; j < d->config_len; j++) | ||
| 251 | seq_printf(s, "config[%d]=%d\n", j, config[j]); | ||
| 252 | |||
| 253 | seq_puts(s, "Device control:\n"); | ||
| 254 | seq_printf(s, "Config Change %d ", dc->config_change); | ||
| 255 | seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); | ||
| 256 | seq_printf(s, "Guest Ack %d ", dc->guest_ack); | ||
| 257 | seq_printf(s, "Host ack %d\n", dc->host_ack); | ||
| 258 | seq_printf(s, "Used address updated %d ", | ||
| 259 | dc->used_address_updated); | ||
| 260 | seq_printf(s, "Vdev 0x%llx\n", dc->vdev); | ||
| 261 | seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); | ||
| 262 | seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); | ||
| 263 | } | ||
| 264 | |||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | |||
| 268 | static int mic_dp_debug_open(struct inode *inode, struct file *file) | ||
| 269 | { | ||
| 270 | return single_open(file, mic_dp_show, inode->i_private); | ||
| 271 | } | ||
| 272 | |||
| 273 | static int mic_dp_debug_release(struct inode *inode, struct file *file) | ||
| 274 | { | ||
| 275 | return single_release(inode, file); | ||
| 276 | } | ||
| 277 | |||
| 278 | static const struct file_operations dp_ops = { | ||
| 279 | .owner = THIS_MODULE, | ||
| 280 | .open = mic_dp_debug_open, | ||
| 281 | .read = seq_read, | ||
| 282 | .llseek = seq_lseek, | ||
| 283 | .release = mic_dp_debug_release | ||
| 284 | }; | ||
| 285 | |||
| 286 | static int mic_vdev_info_show(struct seq_file *s, void *unused) | ||
| 287 | { | ||
| 288 | struct mic_device *mdev = s->private; | ||
| 289 | struct list_head *pos, *tmp; | ||
| 290 | struct mic_vdev *mvdev; | ||
| 291 | int i, j; | ||
| 292 | |||
| 293 | mutex_lock(&mdev->mic_mutex); | ||
| 294 | list_for_each_safe(pos, tmp, &mdev->vdev_list) { | ||
| 295 | mvdev = list_entry(pos, struct mic_vdev, list); | ||
| 296 | seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", | ||
| 297 | mvdev->virtio_id, | ||
| 298 | mic_vdevup(mvdev) ? "UP" : "DOWN", | ||
| 299 | mvdev->in_bytes, | ||
| 300 | mvdev->out_bytes); | ||
| 301 | for (i = 0; i < MIC_MAX_VRINGS; i++) { | ||
| 302 | struct vring_desc *desc; | ||
| 303 | struct vring_avail *avail; | ||
| 304 | struct vring_used *used; | ||
| 305 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
| 306 | struct vringh *vrh = &mvr->vrh; | ||
| 307 | int num = vrh->vring.num; | ||
| 308 | if (!num) | ||
| 309 | continue; | ||
| 310 | desc = vrh->vring.desc; | ||
| 311 | seq_printf(s, "vring i %d avail_idx %d", | ||
| 312 | i, mvr->vring.info->avail_idx & (num - 1)); | ||
| 313 | seq_printf(s, " vring i %d avail_idx %d\n", | ||
| 314 | i, mvr->vring.info->avail_idx); | ||
| 315 | seq_printf(s, "vrh i %d weak_barriers %d", | ||
| 316 | i, vrh->weak_barriers); | ||
| 317 | seq_printf(s, " last_avail_idx %d last_used_idx %d", | ||
| 318 | vrh->last_avail_idx, vrh->last_used_idx); | ||
| 319 | seq_printf(s, " completed %d\n", vrh->completed); | ||
| 320 | for (j = 0; j < num; j++) { | ||
| 321 | seq_printf(s, "desc[%d] addr 0x%llx len %d", | ||
| 322 | j, desc->addr, desc->len); | ||
| 323 | seq_printf(s, " flags 0x%x next %d\n", | ||
| 324 | desc->flags, desc->next); | ||
| 325 | desc++; | ||
| 326 | } | ||
| 327 | avail = vrh->vring.avail; | ||
| 328 | seq_printf(s, "avail flags 0x%x idx %d\n", | ||
| 329 | avail->flags, avail->idx & (num - 1)); | ||
| 330 | seq_printf(s, "avail flags 0x%x idx %d\n", | ||
| 331 | avail->flags, avail->idx); | ||
| 332 | for (j = 0; j < num; j++) | ||
| 333 | seq_printf(s, "avail ring[%d] %d\n", | ||
| 334 | j, avail->ring[j]); | ||
| 335 | used = vrh->vring.used; | ||
| 336 | seq_printf(s, "used flags 0x%x idx %d\n", | ||
| 337 | used->flags, used->idx & (num - 1)); | ||
| 338 | seq_printf(s, "used flags 0x%x idx %d\n", | ||
| 339 | used->flags, used->idx); | ||
| 340 | for (j = 0; j < num; j++) | ||
| 341 | seq_printf(s, "used ring[%d] id %d len %d\n", | ||
| 342 | j, used->ring[j].id, | ||
| 343 | used->ring[j].len); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | mutex_unlock(&mdev->mic_mutex); | ||
| 347 | |||
| 348 | return 0; | ||
| 349 | } | ||
| 350 | |||
| 351 | static int mic_vdev_info_debug_open(struct inode *inode, struct file *file) | ||
| 352 | { | ||
| 353 | return single_open(file, mic_vdev_info_show, inode->i_private); | ||
| 354 | } | ||
| 355 | |||
| 356 | static int mic_vdev_info_debug_release(struct inode *inode, struct file *file) | ||
| 357 | { | ||
| 358 | return single_release(inode, file); | ||
| 359 | } | ||
| 360 | |||
| 361 | static const struct file_operations vdev_info_ops = { | ||
| 362 | .owner = THIS_MODULE, | ||
| 363 | .open = mic_vdev_info_debug_open, | ||
| 364 | .read = seq_read, | ||
| 365 | .llseek = seq_lseek, | ||
| 366 | .release = mic_vdev_info_debug_release | ||
| 367 | }; | ||
| 368 | |||
| 369 | static int mic_msi_irq_info_show(struct seq_file *s, void *pos) | ||
| 370 | { | ||
| 371 | struct mic_device *mdev = s->private; | ||
| 372 | int reg; | ||
| 373 | int i, j; | ||
| 374 | u16 entry; | ||
| 375 | u16 vector; | ||
| 376 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 377 | struct pci_dev, dev); | ||
| 378 | |||
| 379 | if (pci_dev_msi_enabled(pdev)) { | ||
| 380 | for (i = 0; i < mdev->irq_info.num_vectors; i++) { | ||
| 381 | if (pdev->msix_enabled) { | ||
| 382 | entry = mdev->irq_info.msix_entries[i].entry; | ||
| 383 | vector = mdev->irq_info.msix_entries[i].vector; | ||
| 384 | } else { | ||
| 385 | entry = 0; | ||
| 386 | vector = pdev->irq; | ||
| 387 | } | ||
| 388 | |||
| 389 | reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); | ||
| 390 | |||
| 391 | seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", | ||
| 392 | "IRQ:", vector, "Entry:", entry, i, reg); | ||
| 393 | |||
| 394 | seq_printf(s, "%-10s", "offset:"); | ||
| 395 | for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) | ||
| 396 | seq_printf(s, "%4d ", j); | ||
| 397 | seq_puts(s, "\n"); | ||
| 398 | |||
| 399 | |||
| 400 | seq_printf(s, "%-10s", "count:"); | ||
| 401 | for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) | ||
| 402 | seq_printf(s, "%4d ", | ||
| 403 | (mdev->irq_info.mic_msi_map[i] & | ||
| 404 | BIT(j)) ? 1 : 0); | ||
| 405 | seq_puts(s, "\n\n"); | ||
| 406 | } | ||
| 407 | } else { | ||
| 408 | seq_puts(s, "MSI/MSIx interrupts not enabled\n"); | ||
| 409 | } | ||
| 410 | |||
| 411 | return 0; | ||
| 412 | } | ||
| 413 | |||
| 414 | static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file) | ||
| 415 | { | ||
| 416 | return single_open(file, mic_msi_irq_info_show, inode->i_private); | ||
| 417 | } | ||
| 418 | |||
| 419 | static int | ||
| 420 | mic_msi_irq_info_debug_release(struct inode *inode, struct file *file) | ||
| 421 | { | ||
| 422 | return single_release(inode, file); | ||
| 423 | } | ||
| 424 | |||
| 425 | static const struct file_operations msi_irq_info_ops = { | ||
| 426 | .owner = THIS_MODULE, | ||
| 427 | .open = mic_msi_irq_info_debug_open, | ||
| 428 | .read = seq_read, | ||
| 429 | .llseek = seq_lseek, | ||
| 430 | .release = mic_msi_irq_info_debug_release | ||
| 431 | }; | ||
| 432 | |||
| 433 | /** | ||
| 434 | * mic_create_debug_dir - Initialize MIC debugfs entries. | ||
| 435 | */ | ||
| 436 | void mic_create_debug_dir(struct mic_device *mdev) | ||
| 437 | { | ||
| 438 | if (!mic_dbg) | ||
| 439 | return; | ||
| 440 | |||
| 441 | mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg); | ||
| 442 | if (!mdev->dbg_dir) | ||
| 443 | return; | ||
| 444 | |||
| 445 | debugfs_create_file("log_buf", 0444, mdev->dbg_dir, mdev, &log_buf_ops); | ||
| 446 | |||
| 447 | debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops); | ||
| 448 | |||
| 449 | debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, mdev, | ||
| 450 | &soft_reset_ops); | ||
| 451 | |||
| 452 | debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, | ||
| 453 | &post_code_ops); | ||
| 454 | |||
| 455 | debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); | ||
| 456 | |||
| 457 | debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev, | ||
| 458 | &vdev_info_ops); | ||
| 459 | |||
| 460 | debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, | ||
| 461 | &msi_irq_info_ops); | ||
| 462 | } | ||
| 463 | |||
| 464 | /** | ||
| 465 | * mic_delete_debug_dir - Uninitialize MIC debugfs entries. | ||
| 466 | */ | ||
| 467 | void mic_delete_debug_dir(struct mic_device *mdev) | ||
| 468 | { | ||
| 469 | if (!mdev->dbg_dir) | ||
| 470 | return; | ||
| 471 | |||
| 472 | debugfs_remove_recursive(mdev->dbg_dir); | ||
| 473 | } | ||
| 474 | |||
| 475 | /** | ||
| 476 | * mic_init_debugfs - Initialize global debugfs entry. | ||
| 477 | */ | ||
| 478 | void __init mic_init_debugfs(void) | ||
| 479 | { | ||
| 480 | mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
| 481 | if (!mic_dbg) | ||
| 482 | pr_err("can't create debugfs dir\n"); | ||
| 483 | } | ||
| 484 | |||
| 485 | /** | ||
| 486 | * mic_exit_debugfs - Uninitialize global debugfs entry | ||
| 487 | */ | ||
| 488 | void mic_exit_debugfs(void) | ||
| 489 | { | ||
| 490 | debugfs_remove(mic_dbg); | ||
| 491 | } | ||
diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h new file mode 100644 index 000000000000..3574cc375bb9 --- /dev/null +++ b/drivers/misc/mic/host/mic_device.h | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef _MIC_DEVICE_H_ | ||
| 22 | #define _MIC_DEVICE_H_ | ||
| 23 | |||
| 24 | #include <linux/cdev.h> | ||
| 25 | #include <linux/idr.h> | ||
| 26 | #include <linux/notifier.h> | ||
| 27 | |||
| 28 | #include "mic_intr.h" | ||
| 29 | |||
| 30 | /* The maximum number of MIC devices supported in a single host system. */ | ||
| 31 | #define MIC_MAX_NUM_DEVS 256 | ||
| 32 | |||
| 33 | /** | ||
| 34 | * enum mic_hw_family - The hardware family to which a device belongs. | ||
| 35 | */ | ||
| 36 | enum mic_hw_family { | ||
| 37 | MIC_FAMILY_X100 = 0, | ||
| 38 | MIC_FAMILY_UNKNOWN | ||
| 39 | }; | ||
| 40 | |||
| 41 | /** | ||
| 42 | * enum mic_stepping - MIC stepping ids. | ||
| 43 | */ | ||
| 44 | enum mic_stepping { | ||
| 45 | MIC_A0_STEP = 0x0, | ||
| 46 | MIC_B0_STEP = 0x10, | ||
| 47 | MIC_B1_STEP = 0x11, | ||
| 48 | MIC_C0_STEP = 0x20, | ||
| 49 | }; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * struct mic_device - MIC device information for each card. | ||
| 53 | * | ||
| 54 | * @mmio: MMIO bar information. | ||
| 55 | * @aper: Aperture bar information. | ||
| 56 | * @family: The MIC family to which this device belongs. | ||
| 57 | * @ops: MIC HW specific operations. | ||
| 58 | * @id: The unique device id for this MIC device. | ||
| 59 | * @stepping: Stepping ID. | ||
| 60 | * @attr_group: Pointer to list of sysfs attribute groups. | ||
| 61 | * @sdev: Device for sysfs entries. | ||
| 62 | * @mic_mutex: Mutex for synchronizing access to mic_device. | ||
| 63 | * @intr_ops: HW specific interrupt operations. | ||
| 64 | * @smpt_ops: Hardware specific SMPT operations. | ||
| 65 | * @smpt: MIC SMPT information. | ||
| 66 | * @intr_info: H/W specific interrupt information. | ||
| 67 | * @irq_info: The OS specific irq information | ||
| 68 | * @dbg_dir: debugfs directory of this MIC device. | ||
| 69 | * @cmdline: Kernel command line. | ||
| 70 | * @firmware: Firmware file name. | ||
| 71 | * @ramdisk: Ramdisk file name. | ||
| 72 | * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates. | ||
| 73 | * @bootaddr: MIC boot address. | ||
| 74 | * @reset_trigger_work: Work for triggering reset requests. | ||
| 75 | * @shutdown_work: Work for handling shutdown interrupts. | ||
| 76 | * @state: MIC state. | ||
| 77 | * @shutdown_status: MIC status reported by card for shutdown/crashes. | ||
| 78 | * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. | ||
| 79 | * @reset_wait: Waitqueue for sleeping while reset completes. | ||
| 80 | * @log_buf_addr: Log buffer address for MIC. | ||
| 81 | * @log_buf_len: Log buffer length address for MIC. | ||
| 82 | * @dp: virtio device page | ||
| 83 | * @dp_dma_addr: virtio device page DMA address. | ||
| 84 | * @shutdown_db: shutdown doorbell. | ||
| 85 | * @shutdown_cookie: shutdown cookie. | ||
| 86 | * @cdev: Character device for MIC. | ||
| 87 | * @vdev_list: list of virtio devices. | ||
| 88 | * @pm_notifier: Handles PM notifications from the OS. | ||
| 89 | */ | ||
| 90 | struct mic_device { | ||
| 91 | struct mic_mw mmio; | ||
| 92 | struct mic_mw aper; | ||
| 93 | enum mic_hw_family family; | ||
| 94 | struct mic_hw_ops *ops; | ||
| 95 | int id; | ||
| 96 | enum mic_stepping stepping; | ||
| 97 | const struct attribute_group **attr_group; | ||
| 98 | struct device *sdev; | ||
| 99 | struct mutex mic_mutex; | ||
| 100 | struct mic_hw_intr_ops *intr_ops; | ||
| 101 | struct mic_smpt_ops *smpt_ops; | ||
| 102 | struct mic_smpt_info *smpt; | ||
| 103 | struct mic_intr_info *intr_info; | ||
| 104 | struct mic_irq_info irq_info; | ||
| 105 | struct dentry *dbg_dir; | ||
| 106 | char *cmdline; | ||
| 107 | char *firmware; | ||
| 108 | char *ramdisk; | ||
| 109 | char *bootmode; | ||
| 110 | u32 bootaddr; | ||
| 111 | struct work_struct reset_trigger_work; | ||
| 112 | struct work_struct shutdown_work; | ||
| 113 | u8 state; | ||
| 114 | u8 shutdown_status; | ||
| 115 | struct sysfs_dirent *state_sysfs; | ||
| 116 | struct completion reset_wait; | ||
| 117 | void *log_buf_addr; | ||
| 118 | int *log_buf_len; | ||
| 119 | void *dp; | ||
| 120 | dma_addr_t dp_dma_addr; | ||
| 121 | int shutdown_db; | ||
| 122 | struct mic_irq *shutdown_cookie; | ||
| 123 | struct cdev cdev; | ||
| 124 | struct list_head vdev_list; | ||
| 125 | struct notifier_block pm_notifier; | ||
| 126 | }; | ||
| 127 | |||
| 128 | /** | ||
| 129 | * struct mic_hw_ops - MIC HW specific operations. | ||
| 130 | * @aper_bar: Aperture bar resource number. | ||
| 131 | * @mmio_bar: MMIO bar resource number. | ||
| 132 | * @read_spad: Read from scratch pad register. | ||
| 133 | * @write_spad: Write to scratch pad register. | ||
| 134 | * @send_intr: Send an interrupt for a particular doorbell on the card. | ||
| 135 | * @ack_interrupt: Hardware specific operations to ack the h/w on | ||
| 136 | * receipt of an interrupt. | ||
| 137 | * @reset: Reset the remote processor. | ||
| 138 | * @reset_fw_ready: Reset firmware ready field. | ||
| 139 | * @is_fw_ready: Check if firmware is ready for OS download. | ||
| 140 | * @send_firmware_intr: Send an interrupt to the card firmware. | ||
| 141 | * @load_mic_fw: Load firmware segments required to boot the card | ||
| 142 | * into card memory. This includes the kernel, command line, ramdisk etc. | ||
| 143 | * @get_postcode: Get post code status from firmware. | ||
| 144 | */ | ||
| 145 | struct mic_hw_ops { | ||
| 146 | u8 aper_bar; | ||
| 147 | u8 mmio_bar; | ||
| 148 | u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); | ||
| 149 | void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); | ||
| 150 | void (*send_intr)(struct mic_device *mdev, int doorbell); | ||
| 151 | u32 (*ack_interrupt)(struct mic_device *mdev); | ||
| 152 | void (*reset)(struct mic_device *mdev); | ||
| 153 | void (*reset_fw_ready)(struct mic_device *mdev); | ||
| 154 | bool (*is_fw_ready)(struct mic_device *mdev); | ||
| 155 | void (*send_firmware_intr)(struct mic_device *mdev); | ||
| 156 | int (*load_mic_fw)(struct mic_device *mdev, const char *buf); | ||
| 157 | u32 (*get_postcode)(struct mic_device *mdev); | ||
| 158 | }; | ||
| 159 | |||
| 160 | /** | ||
| 161 | * mic_mmio_read - read from an MMIO register. | ||
| 162 | * @mw: MMIO register base virtual address. | ||
| 163 | * @offset: register offset. | ||
| 164 | * | ||
| 165 | * RETURNS: register value. | ||
| 166 | */ | ||
| 167 | static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) | ||
| 168 | { | ||
| 169 | return ioread32(mw->va + offset); | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * mic_mmio_write - write to an MMIO register. | ||
| 174 | * @mw: MMIO register base virtual address. | ||
| 175 | * @val: the data value to put into the register | ||
| 176 | * @offset: register offset. | ||
| 177 | * | ||
| 178 | * RETURNS: none. | ||
| 179 | */ | ||
| 180 | static inline void | ||
| 181 | mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) | ||
| 182 | { | ||
| 183 | iowrite32(val, mw->va + offset); | ||
| 184 | } | ||
| 185 | |||
| 186 | void mic_sysfs_init(struct mic_device *mdev); | ||
| 187 | int mic_start(struct mic_device *mdev, const char *buf); | ||
| 188 | void mic_stop(struct mic_device *mdev, bool force); | ||
| 189 | void mic_shutdown(struct mic_device *mdev); | ||
| 190 | void mic_reset_delayed_work(struct work_struct *work); | ||
| 191 | void mic_reset_trigger_work(struct work_struct *work); | ||
| 192 | void mic_shutdown_work(struct work_struct *work); | ||
| 193 | void mic_bootparam_init(struct mic_device *mdev); | ||
| 194 | void mic_set_state(struct mic_device *mdev, u8 state); | ||
| 195 | void mic_set_shutdown_status(struct mic_device *mdev, u8 status); | ||
| 196 | void mic_create_debug_dir(struct mic_device *dev); | ||
| 197 | void mic_delete_debug_dir(struct mic_device *dev); | ||
| 198 | void __init mic_init_debugfs(void); | ||
| 199 | void mic_exit_debugfs(void); | ||
| 200 | void mic_prepare_suspend(struct mic_device *mdev); | ||
| 201 | void mic_complete_resume(struct mic_device *mdev); | ||
| 202 | void mic_suspend(struct mic_device *mdev); | ||
| 203 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c new file mode 100644 index 000000000000..85776d7327f3 --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.c | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/poll.h> | ||
| 22 | #include <linux/pci.h> | ||
| 23 | |||
| 24 | #include <linux/mic_common.h> | ||
| 25 | #include "../common/mic_dev.h" | ||
| 26 | #include "mic_device.h" | ||
| 27 | #include "mic_fops.h" | ||
| 28 | #include "mic_virtio.h" | ||
| 29 | |||
| 30 | int mic_open(struct inode *inode, struct file *f) | ||
| 31 | { | ||
| 32 | struct mic_vdev *mvdev; | ||
| 33 | struct mic_device *mdev = container_of(inode->i_cdev, | ||
| 34 | struct mic_device, cdev); | ||
| 35 | |||
| 36 | mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); | ||
| 37 | if (!mvdev) | ||
| 38 | return -ENOMEM; | ||
| 39 | |||
| 40 | init_waitqueue_head(&mvdev->waitq); | ||
| 41 | INIT_LIST_HEAD(&mvdev->list); | ||
| 42 | mvdev->mdev = mdev; | ||
| 43 | mvdev->virtio_id = -1; | ||
| 44 | |||
| 45 | f->private_data = mvdev; | ||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | int mic_release(struct inode *inode, struct file *f) | ||
| 50 | { | ||
| 51 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
| 52 | |||
| 53 | if (-1 != mvdev->virtio_id) | ||
| 54 | mic_virtio_del_device(mvdev); | ||
| 55 | f->private_data = NULL; | ||
| 56 | kfree(mvdev); | ||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg) | ||
| 61 | { | ||
| 62 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
| 63 | void __user *argp = (void __user *)arg; | ||
| 64 | int ret; | ||
| 65 | |||
| 66 | switch (cmd) { | ||
| 67 | case MIC_VIRTIO_ADD_DEVICE: | ||
| 68 | { | ||
| 69 | ret = mic_virtio_add_device(mvdev, argp); | ||
| 70 | if (ret < 0) { | ||
| 71 | dev_err(mic_dev(mvdev), | ||
| 72 | "%s %d errno ret %d\n", | ||
| 73 | __func__, __LINE__, ret); | ||
| 74 | return ret; | ||
| 75 | } | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | case MIC_VIRTIO_COPY_DESC: | ||
| 79 | { | ||
| 80 | struct mic_copy_desc copy; | ||
| 81 | |||
| 82 | ret = mic_vdev_inited(mvdev); | ||
| 83 | if (ret) | ||
| 84 | return ret; | ||
| 85 | |||
| 86 | if (copy_from_user(©, argp, sizeof(copy))) | ||
| 87 | return -EFAULT; | ||
| 88 | |||
| 89 | dev_dbg(mic_dev(mvdev), | ||
| 90 | "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n", | ||
| 91 | __func__, __LINE__, copy.iovcnt, copy.vr_idx, | ||
| 92 | copy.update_used); | ||
| 93 | |||
| 94 | ret = mic_virtio_copy_desc(mvdev, ©); | ||
| 95 | if (ret < 0) { | ||
| 96 | dev_err(mic_dev(mvdev), | ||
| 97 | "%s %d errno ret %d\n", | ||
| 98 | __func__, __LINE__, ret); | ||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | if (copy_to_user( | ||
| 102 | &((struct mic_copy_desc __user *)argp)->out_len, | ||
| 103 | ©.out_len, sizeof(copy.out_len))) { | ||
| 104 | dev_err(mic_dev(mvdev), "%s %d errno ret %d\n", | ||
| 105 | __func__, __LINE__, -EFAULT); | ||
| 106 | return -EFAULT; | ||
| 107 | } | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | case MIC_VIRTIO_CONFIG_CHANGE: | ||
| 111 | { | ||
| 112 | ret = mic_vdev_inited(mvdev); | ||
| 113 | if (ret) | ||
| 114 | return ret; | ||
| 115 | |||
| 116 | ret = mic_virtio_config_change(mvdev, argp); | ||
| 117 | if (ret < 0) { | ||
| 118 | dev_err(mic_dev(mvdev), | ||
| 119 | "%s %d errno ret %d\n", | ||
| 120 | __func__, __LINE__, ret); | ||
| 121 | return ret; | ||
| 122 | } | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | default: | ||
| 126 | return -ENOIOCTLCMD; | ||
| 127 | }; | ||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* | ||
| 132 | * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and | ||
| 133 | * not when previously enqueued buffers may be available. This means that | ||
| 134 | * in the card->host (TX) path, when userspace is unblocked by poll it | ||
| 135 | * must drain all available descriptors or it can stall. | ||
| 136 | */ | ||
| 137 | unsigned int mic_poll(struct file *f, poll_table *wait) | ||
| 138 | { | ||
| 139 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
| 140 | int mask = 0; | ||
| 141 | |||
| 142 | poll_wait(f, &mvdev->waitq, wait); | ||
| 143 | |||
| 144 | if (mic_vdev_inited(mvdev)) { | ||
| 145 | mask = POLLERR; | ||
| 146 | } else if (mvdev->poll_wake) { | ||
| 147 | mvdev->poll_wake = 0; | ||
| 148 | mask = POLLIN | POLLOUT; | ||
| 149 | } | ||
| 150 | |||
| 151 | return mask; | ||
| 152 | } | ||
| 153 | |||
| 154 | static inline int | ||
| 155 | mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, | ||
| 156 | unsigned long *size, unsigned long *pa) | ||
| 157 | { | ||
| 158 | struct mic_device *mdev = mvdev->mdev; | ||
| 159 | unsigned long start = MIC_DP_SIZE; | ||
| 160 | int i; | ||
| 161 | |||
| 162 | /* | ||
| 163 | * MMAP interface is as follows: | ||
| 164 | * offset region | ||
| 165 | * 0x0 virtio device_page | ||
| 166 | * 0x1000 first vring | ||
| 167 | * 0x1000 + size of 1st vring second vring | ||
| 168 | * .... | ||
| 169 | */ | ||
| 170 | if (!offset) { | ||
| 171 | *pa = virt_to_phys(mdev->dp); | ||
| 172 | *size = MIC_DP_SIZE; | ||
| 173 | return 0; | ||
| 174 | } | ||
| 175 | |||
| 176 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
| 177 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
| 178 | if (offset == start) { | ||
| 179 | *pa = virt_to_phys(mvr->vring.va); | ||
| 180 | *size = mvr->vring.len; | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | start += mvr->vring.len; | ||
| 184 | } | ||
| 185 | return -1; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* | ||
| 189 | * Maps the device page and virtio rings to user space for readonly access. | ||
| 190 | */ | ||
| 191 | int | ||
| 192 | mic_mmap(struct file *f, struct vm_area_struct *vma) | ||
| 193 | { | ||
| 194 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
| 195 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | ||
| 196 | unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; | ||
| 197 | int i, err; | ||
| 198 | |||
| 199 | err = mic_vdev_inited(mvdev); | ||
| 200 | if (err) | ||
| 201 | return err; | ||
| 202 | |||
| 203 | if (vma->vm_flags & VM_WRITE) | ||
| 204 | return -EACCES; | ||
| 205 | |||
| 206 | while (size_rem) { | ||
| 207 | i = mic_query_offset(mvdev, offset, &size, &pa); | ||
| 208 | if (i < 0) | ||
| 209 | return -EINVAL; | ||
| 210 | err = remap_pfn_range(vma, vma->vm_start + offset, | ||
| 211 | pa >> PAGE_SHIFT, size, vma->vm_page_prot); | ||
| 212 | if (err) | ||
| 213 | return err; | ||
| 214 | dev_dbg(mic_dev(mvdev), | ||
| 215 | "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n", | ||
| 216 | __func__, __LINE__, mvdev->virtio_id, size, offset, | ||
| 217 | pa, vma->vm_start + offset); | ||
| 218 | size_rem -= size; | ||
| 219 | offset += size; | ||
| 220 | } | ||
| 221 | return 0; | ||
| 222 | } | ||
diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h new file mode 100644 index 000000000000..dc3893dff667 --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef _MIC_FOPS_H_ | ||
| 22 | #define _MIC_FOPS_H_ | ||
| 23 | |||
| 24 | int mic_open(struct inode *inode, struct file *filp); | ||
| 25 | int mic_release(struct inode *inode, struct file *filp); | ||
| 26 | ssize_t mic_read(struct file *filp, char __user *buf, | ||
| 27 | size_t count, loff_t *pos); | ||
| 28 | long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); | ||
| 29 | int mic_mmap(struct file *f, struct vm_area_struct *vma); | ||
| 30 | unsigned int mic_poll(struct file *f, poll_table *wait); | ||
| 31 | |||
| 32 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c new file mode 100644 index 000000000000..f9c29bc918bc --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.c | |||
| @@ -0,0 +1,630 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/pci.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | |||
| 24 | #include "../common/mic_dev.h" | ||
| 25 | #include "mic_device.h" | ||
| 26 | |||
| 27 | /* | ||
| 28 | * mic_invoke_callback - Invoke callback functions registered for | ||
| 29 | * the corresponding source id. | ||
| 30 | * | ||
| 31 | * @mdev: pointer to the mic_device instance | ||
| 32 | * @idx: The interrupt source id. | ||
| 33 | * | ||
| 34 | * Returns none. | ||
| 35 | */ | ||
| 36 | static inline void mic_invoke_callback(struct mic_device *mdev, int idx) | ||
| 37 | { | ||
| 38 | struct mic_intr_cb *intr_cb; | ||
| 39 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 40 | struct pci_dev, dev); | ||
| 41 | |||
| 42 | spin_lock(&mdev->irq_info.mic_intr_lock); | ||
| 43 | list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list) | ||
| 44 | if (intr_cb->func) | ||
| 45 | intr_cb->func(pdev->irq, intr_cb->data); | ||
| 46 | spin_unlock(&mdev->irq_info.mic_intr_lock); | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * mic_interrupt - Generic interrupt handler for | ||
| 51 | * MSI and INTx based interrupts. | ||
| 52 | */ | ||
| 53 | static irqreturn_t mic_interrupt(int irq, void *dev) | ||
| 54 | { | ||
| 55 | struct mic_device *mdev = dev; | ||
| 56 | struct mic_intr_info *info = mdev->intr_info; | ||
| 57 | u32 mask; | ||
| 58 | int i; | ||
| 59 | |||
| 60 | mask = mdev->ops->ack_interrupt(mdev); | ||
| 61 | if (!mask) | ||
| 62 | return IRQ_NONE; | ||
| 63 | |||
| 64 | for (i = info->intr_start_idx[MIC_INTR_DB]; | ||
| 65 | i < info->intr_len[MIC_INTR_DB]; i++) | ||
| 66 | if (mask & BIT(i)) | ||
| 67 | mic_invoke_callback(mdev, i); | ||
| 68 | |||
| 69 | return IRQ_HANDLED; | ||
| 70 | } | ||
| 71 | |||
| 72 | /* Return the interrupt offset from the index. Index is 0 based. */ | ||
| 73 | static u16 mic_map_src_to_offset(struct mic_device *mdev, | ||
| 74 | int intr_src, enum mic_intr_type type) | ||
| 75 | { | ||
| 76 | if (type >= MIC_NUM_INTR_TYPES) | ||
| 77 | return MIC_NUM_OFFSETS; | ||
| 78 | if (intr_src >= mdev->intr_info->intr_len[type]) | ||
| 79 | return MIC_NUM_OFFSETS; | ||
| 80 | |||
| 81 | return mdev->intr_info->intr_start_idx[type] + intr_src; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* Return next available msix_entry. */ | ||
| 85 | static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) | ||
| 86 | { | ||
| 87 | int i; | ||
| 88 | struct mic_irq_info *info = &mdev->irq_info; | ||
| 89 | |||
| 90 | for (i = 0; i < info->num_vectors; i++) | ||
| 91 | if (!info->mic_msi_map[i]) | ||
| 92 | return &info->msix_entries[i]; | ||
| 93 | return NULL; | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * mic_register_intr_callback - Register a callback handler for the | ||
| 98 | * given source id. | ||
| 99 | * | ||
| 100 | * @mdev: pointer to the mic_device instance | ||
| 101 | * @idx: The source id to be registered. | ||
| 102 | * @func: The function to be called when the source id receives | ||
| 103 | * the interrupt. | ||
| 104 | * @data: Private data of the requester. | ||
| 105 | * Return the callback structure that was registered or an | ||
| 106 | * appropriate error on failure. | ||
| 107 | */ | ||
| 108 | static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, | ||
| 109 | u8 idx, irqreturn_t (*func) (int irq, void *dev), | ||
| 110 | void *data) | ||
| 111 | { | ||
| 112 | struct mic_intr_cb *intr_cb; | ||
| 113 | unsigned long flags; | ||
| 114 | int rc; | ||
| 115 | intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL); | ||
| 116 | |||
| 117 | if (!intr_cb) | ||
| 118 | return ERR_PTR(-ENOMEM); | ||
| 119 | |||
| 120 | intr_cb->func = func; | ||
| 121 | intr_cb->data = data; | ||
| 122 | intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, | ||
| 123 | 0, 0, GFP_KERNEL); | ||
| 124 | if (intr_cb->cb_id < 0) { | ||
| 125 | rc = intr_cb->cb_id; | ||
| 126 | goto ida_fail; | ||
| 127 | } | ||
| 128 | |||
| 129 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 130 | list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); | ||
| 131 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 132 | |||
| 133 | return intr_cb; | ||
| 134 | ida_fail: | ||
| 135 | kfree(intr_cb); | ||
| 136 | return ERR_PTR(rc); | ||
| 137 | } | ||
| 138 | |||
| 139 | /** | ||
| 140 | * mic_unregister_intr_callback - Unregister the callback handler | ||
| 141 | * identified by its callback id. | ||
| 142 | * | ||
| 143 | * @mdev: pointer to the mic_device instance | ||
| 144 | * @idx: The callback structure id to be unregistered. | ||
| 145 | * Return the source id that was unregistered or MIC_NUM_OFFSETS if no | ||
| 146 | * such callback handler was found. | ||
| 147 | */ | ||
| 148 | static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) | ||
| 149 | { | ||
| 150 | struct list_head *pos, *tmp; | ||
| 151 | struct mic_intr_cb *intr_cb; | ||
| 152 | unsigned long flags; | ||
| 153 | int i; | ||
| 154 | |||
| 155 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { | ||
| 156 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 157 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { | ||
| 158 | intr_cb = list_entry(pos, struct mic_intr_cb, list); | ||
| 159 | if (intr_cb->cb_id == idx) { | ||
| 160 | list_del(pos); | ||
| 161 | ida_simple_remove(&mdev->irq_info.cb_ida, | ||
| 162 | intr_cb->cb_id); | ||
| 163 | kfree(intr_cb); | ||
| 164 | spin_unlock_irqrestore( | ||
| 165 | &mdev->irq_info.mic_intr_lock, flags); | ||
| 166 | return i; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 170 | } | ||
| 171 | return MIC_NUM_OFFSETS; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * mic_setup_msix - Initializes MSIx interrupts. | ||
| 176 | * | ||
| 177 | * @mdev: pointer to mic_device instance | ||
| 178 | * | ||
| 179 | * | ||
| 180 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 181 | */ | ||
| 182 | static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) | ||
| 183 | { | ||
| 184 | int rc, i; | ||
| 185 | int entry_size = sizeof(*mdev->irq_info.msix_entries); | ||
| 186 | |||
| 187 | mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX, | ||
| 188 | entry_size, GFP_KERNEL); | ||
| 189 | if (!mdev->irq_info.msix_entries) { | ||
| 190 | rc = -ENOMEM; | ||
| 191 | goto err_nomem1; | ||
| 192 | } | ||
| 193 | |||
| 194 | for (i = 0; i < MIC_MIN_MSIX; i++) | ||
| 195 | mdev->irq_info.msix_entries[i].entry = i; | ||
| 196 | |||
| 197 | rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries, | ||
| 198 | MIC_MIN_MSIX); | ||
| 199 | if (rc) { | ||
| 200 | dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); | ||
| 201 | goto err_enable_msix; | ||
| 202 | } | ||
| 203 | |||
| 204 | mdev->irq_info.num_vectors = MIC_MIN_MSIX; | ||
| 205 | mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * | ||
| 206 | mdev->irq_info.num_vectors), GFP_KERNEL); | ||
| 207 | |||
| 208 | if (!mdev->irq_info.mic_msi_map) { | ||
| 209 | rc = -ENOMEM; | ||
| 210 | goto err_nomem2; | ||
| 211 | } | ||
| 212 | |||
| 213 | dev_dbg(mdev->sdev->parent, | ||
| 214 | "%d MSIx irqs setup\n", mdev->irq_info.num_vectors); | ||
| 215 | return 0; | ||
| 216 | err_nomem2: | ||
| 217 | pci_disable_msix(pdev); | ||
| 218 | err_enable_msix: | ||
| 219 | kfree(mdev->irq_info.msix_entries); | ||
| 220 | err_nomem1: | ||
| 221 | mdev->irq_info.num_vectors = 0; | ||
| 222 | return rc; | ||
| 223 | } | ||
| 224 | |||
| 225 | /** | ||
| 226 | * mic_setup_callbacks - Initialize data structures needed | ||
| 227 | * to handle callbacks. | ||
| 228 | * | ||
| 229 | * @mdev: pointer to mic_device instance | ||
| 230 | */ | ||
| 231 | static int mic_setup_callbacks(struct mic_device *mdev) | ||
| 232 | { | ||
| 233 | int i; | ||
| 234 | |||
| 235 | mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS, | ||
| 236 | sizeof(*mdev->irq_info.cb_list), | ||
| 237 | GFP_KERNEL); | ||
| 238 | if (!mdev->irq_info.cb_list) | ||
| 239 | return -ENOMEM; | ||
| 240 | |||
| 241 | for (i = 0; i < MIC_NUM_OFFSETS; i++) | ||
| 242 | INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); | ||
| 243 | ida_init(&mdev->irq_info.cb_ida); | ||
| 244 | spin_lock_init(&mdev->irq_info.mic_intr_lock); | ||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | |||
| 248 | /** | ||
| 249 | * mic_release_callbacks - Uninitialize data structures needed | ||
| 250 | * to handle callbacks. | ||
| 251 | * | ||
| 252 | * @mdev: pointer to mic_device instance | ||
| 253 | */ | ||
| 254 | static void mic_release_callbacks(struct mic_device *mdev) | ||
| 255 | { | ||
| 256 | unsigned long flags; | ||
| 257 | struct list_head *pos, *tmp; | ||
| 258 | struct mic_intr_cb *intr_cb; | ||
| 259 | int i; | ||
| 260 | |||
| 261 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { | ||
| 262 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 263 | |||
| 264 | if (list_empty(&mdev->irq_info.cb_list[i])) { | ||
| 265 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, | ||
| 266 | flags); | ||
| 267 | break; | ||
| 268 | } | ||
| 269 | |||
| 270 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { | ||
| 271 | intr_cb = list_entry(pos, struct mic_intr_cb, list); | ||
| 272 | list_del(pos); | ||
| 273 | ida_simple_remove(&mdev->irq_info.cb_ida, | ||
| 274 | intr_cb->cb_id); | ||
| 275 | kfree(intr_cb); | ||
| 276 | } | ||
| 277 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 278 | } | ||
| 279 | ida_destroy(&mdev->irq_info.cb_ida); | ||
| 280 | kfree(mdev->irq_info.cb_list); | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * mic_setup_msi - Initializes MSI interrupts. | ||
| 285 | * | ||
| 286 | * @mdev: pointer to mic_device instance | ||
| 287 | * @pdev: PCI device structure | ||
| 288 | * | ||
| 289 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 290 | */ | ||
| 291 | static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) | ||
| 292 | { | ||
| 293 | int rc; | ||
| 294 | |||
| 295 | rc = pci_enable_msi(pdev); | ||
| 296 | if (rc) { | ||
| 297 | dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc); | ||
| 298 | return rc; | ||
| 299 | } | ||
| 300 | |||
| 301 | mdev->irq_info.num_vectors = 1; | ||
| 302 | mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * | ||
| 303 | mdev->irq_info.num_vectors), GFP_KERNEL); | ||
| 304 | |||
| 305 | if (!mdev->irq_info.mic_msi_map) { | ||
| 306 | rc = -ENOMEM; | ||
| 307 | goto err_nomem1; | ||
| 308 | } | ||
| 309 | |||
| 310 | rc = mic_setup_callbacks(mdev); | ||
| 311 | if (rc) { | ||
| 312 | dev_err(&pdev->dev, "Error setting up callbacks\n"); | ||
| 313 | goto err_nomem2; | ||
| 314 | } | ||
| 315 | |||
| 316 | rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev); | ||
| 317 | if (rc) { | ||
| 318 | dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); | ||
| 319 | goto err_irq_req_fail; | ||
| 320 | } | ||
| 321 | |||
| 322 | dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors); | ||
| 323 | return 0; | ||
| 324 | err_irq_req_fail: | ||
| 325 | mic_release_callbacks(mdev); | ||
| 326 | err_nomem2: | ||
| 327 | kfree(mdev->irq_info.mic_msi_map); | ||
| 328 | err_nomem1: | ||
| 329 | pci_disable_msi(pdev); | ||
| 330 | mdev->irq_info.num_vectors = 0; | ||
| 331 | return rc; | ||
| 332 | } | ||
| 333 | |||
| 334 | /** | ||
| 335 | * mic_setup_intx - Initializes legacy interrupts. | ||
| 336 | * | ||
| 337 | * @mdev: pointer to mic_device instance | ||
| 338 | * @pdev: PCI device structure | ||
| 339 | * | ||
| 340 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 341 | */ | ||
| 342 | static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) | ||
| 343 | { | ||
| 344 | int rc; | ||
| 345 | |||
| 346 | pci_msi_off(pdev); | ||
| 347 | |||
| 348 | /* Enable intx */ | ||
| 349 | pci_intx(pdev, 1); | ||
| 350 | rc = mic_setup_callbacks(mdev); | ||
| 351 | if (rc) { | ||
| 352 | dev_err(&pdev->dev, "Error setting up callbacks\n"); | ||
| 353 | goto err_nomem; | ||
| 354 | } | ||
| 355 | |||
| 356 | rc = request_irq(pdev->irq, mic_interrupt, | ||
| 357 | IRQF_SHARED, "mic-intx", mdev); | ||
| 358 | if (rc) | ||
| 359 | goto err; | ||
| 360 | |||
| 361 | dev_dbg(&pdev->dev, "intx irq setup\n"); | ||
| 362 | return 0; | ||
| 363 | err: | ||
| 364 | mic_release_callbacks(mdev); | ||
| 365 | err_nomem: | ||
| 366 | return rc; | ||
| 367 | } | ||
| 368 | |||
| 369 | /** | ||
| 370 | * mic_next_db - Retrieve the next doorbell interrupt source id. | ||
| 371 | * The id is picked sequentially from the available pool of | ||
| 372 | * doorlbell ids. | ||
| 373 | * | ||
| 374 | * @mdev: pointer to the mic_device instance. | ||
| 375 | * | ||
| 376 | * Returns the next doorbell interrupt source. | ||
| 377 | */ | ||
| 378 | int mic_next_db(struct mic_device *mdev) | ||
| 379 | { | ||
| 380 | int next_db; | ||
| 381 | |||
| 382 | next_db = mdev->irq_info.next_avail_src % | ||
| 383 | mdev->intr_info->intr_len[MIC_INTR_DB]; | ||
| 384 | mdev->irq_info.next_avail_src++; | ||
| 385 | return next_db; | ||
| 386 | } | ||
| 387 | |||
| 388 | #define COOKIE_ID_SHIFT 16 | ||
| 389 | #define GET_ENTRY(cookie) ((cookie) & 0xFFFF) | ||
| 390 | #define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT) | ||
| 391 | #define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) | ||
| 392 | |||
| 393 | /** | ||
| 394 | * mic_request_irq - request an irq. mic_mutex needs | ||
| 395 | * to be held before calling this function. | ||
| 396 | * | ||
| 397 | * @mdev: pointer to mic_device instance | ||
| 398 | * @func: The callback function that handles the interrupt. | ||
| 399 | * The function needs to call ack_interrupts | ||
| 400 | * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. | ||
| 401 | * @name: The ASCII name of the callee requesting the irq. | ||
| 402 | * @data: private data that is returned back when calling the | ||
| 403 | * function handler. | ||
| 404 | * @intr_src: The source id of the requester. Its the doorbell id | ||
| 405 | * for Doorbell interrupts and DMA channel id for DMA interrupts. | ||
| 406 | * @type: The type of interrupt. Values defined in mic_intr_type | ||
| 407 | * | ||
| 408 | * returns: The cookie that is transparent to the caller. Passed | ||
| 409 | * back when calling mic_free_irq. An appropriate error code | ||
| 410 | * is returned on failure. Caller needs to use IS_ERR(return_val) | ||
| 411 | * to check for failure and PTR_ERR(return_val) to obtained the | ||
| 412 | * error code. | ||
| 413 | * | ||
| 414 | */ | ||
| 415 | struct mic_irq *mic_request_irq(struct mic_device *mdev, | ||
| 416 | irqreturn_t (*func)(int irq, void *dev), | ||
| 417 | const char *name, void *data, int intr_src, | ||
| 418 | enum mic_intr_type type) | ||
| 419 | { | ||
| 420 | u16 offset; | ||
| 421 | int rc = 0; | ||
| 422 | struct msix_entry *msix = NULL; | ||
| 423 | unsigned long cookie = 0; | ||
| 424 | u16 entry; | ||
| 425 | struct mic_intr_cb *intr_cb; | ||
| 426 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 427 | struct pci_dev, dev); | ||
| 428 | |||
| 429 | offset = mic_map_src_to_offset(mdev, intr_src, type); | ||
| 430 | if (offset >= MIC_NUM_OFFSETS) { | ||
| 431 | dev_err(mdev->sdev->parent, | ||
| 432 | "Error mapping index %d to a valid source id.\n", | ||
| 433 | intr_src); | ||
| 434 | rc = -EINVAL; | ||
| 435 | goto err; | ||
| 436 | } | ||
| 437 | |||
| 438 | if (mdev->irq_info.num_vectors > 1) { | ||
| 439 | msix = mic_get_available_vector(mdev); | ||
| 440 | if (!msix) { | ||
| 441 | dev_err(mdev->sdev->parent, | ||
| 442 | "No MSIx vectors available for use.\n"); | ||
| 443 | rc = -ENOSPC; | ||
| 444 | goto err; | ||
| 445 | } | ||
| 446 | |||
| 447 | rc = request_irq(msix->vector, func, 0, name, data); | ||
| 448 | if (rc) { | ||
| 449 | dev_dbg(mdev->sdev->parent, | ||
| 450 | "request irq failed rc = %d\n", rc); | ||
| 451 | goto err; | ||
| 452 | } | ||
| 453 | entry = msix->entry; | ||
| 454 | mdev->irq_info.mic_msi_map[entry] |= BIT(offset); | ||
| 455 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
| 456 | entry, offset, true); | ||
| 457 | cookie = MK_COOKIE(entry, offset); | ||
| 458 | dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", | ||
| 459 | msix->vector, intr_src); | ||
| 460 | } else { | ||
| 461 | intr_cb = mic_register_intr_callback(mdev, | ||
| 462 | offset, func, data); | ||
| 463 | if (IS_ERR(intr_cb)) { | ||
| 464 | dev_err(mdev->sdev->parent, | ||
| 465 | "No available callback entries for use\n"); | ||
| 466 | rc = PTR_ERR(intr_cb); | ||
| 467 | goto err; | ||
| 468 | } | ||
| 469 | |||
| 470 | entry = 0; | ||
| 471 | if (pci_dev_msi_enabled(pdev)) { | ||
| 472 | mdev->irq_info.mic_msi_map[entry] |= (1 << offset); | ||
| 473 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
| 474 | entry, offset, true); | ||
| 475 | } | ||
| 476 | cookie = MK_COOKIE(entry, intr_cb->cb_id); | ||
| 477 | dev_dbg(mdev->sdev->parent, "callback %d registered for src: %d\n", | ||
| 478 | intr_cb->cb_id, intr_src); | ||
| 479 | } | ||
| 480 | return (struct mic_irq *)cookie; | ||
| 481 | err: | ||
| 482 | return ERR_PTR(rc); | ||
| 483 | } | ||
| 484 | |||
| 485 | /** | ||
| 486 | * mic_free_irq - free irq. mic_mutex | ||
| 487 | * needs to be held before calling this function. | ||
| 488 | * | ||
| 489 | * @mdev: pointer to mic_device instance | ||
| 490 | * @cookie: cookie obtained during a successful call to mic_request_irq | ||
| 491 | * @data: private data specified by the calling function during the | ||
| 492 | * mic_request_irq | ||
| 493 | * | ||
| 494 | * returns: none. | ||
| 495 | */ | ||
| 496 | void mic_free_irq(struct mic_device *mdev, | ||
| 497 | struct mic_irq *cookie, void *data) | ||
| 498 | { | ||
| 499 | u32 offset; | ||
| 500 | u32 entry; | ||
| 501 | u8 src_id; | ||
| 502 | unsigned int irq; | ||
| 503 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 504 | struct pci_dev, dev); | ||
| 505 | |||
| 506 | entry = GET_ENTRY((unsigned long)cookie); | ||
| 507 | offset = GET_OFFSET((unsigned long)cookie); | ||
| 508 | if (mdev->irq_info.num_vectors > 1) { | ||
| 509 | if (entry >= mdev->irq_info.num_vectors) { | ||
| 510 | dev_warn(mdev->sdev->parent, | ||
| 511 | "entry %d should be < num_irq %d\n", | ||
| 512 | entry, mdev->irq_info.num_vectors); | ||
| 513 | return; | ||
| 514 | } | ||
| 515 | irq = mdev->irq_info.msix_entries[entry].vector; | ||
| 516 | free_irq(irq, data); | ||
| 517 | mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset)); | ||
| 518 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
| 519 | entry, offset, false); | ||
| 520 | |||
| 521 | dev_dbg(mdev->sdev->parent, "irq: %d freed\n", irq); | ||
| 522 | } else { | ||
| 523 | irq = pdev->irq; | ||
| 524 | src_id = mic_unregister_intr_callback(mdev, offset); | ||
| 525 | if (src_id >= MIC_NUM_OFFSETS) { | ||
| 526 | dev_warn(mdev->sdev->parent, "Error unregistering callback\n"); | ||
| 527 | return; | ||
| 528 | } | ||
| 529 | if (pci_dev_msi_enabled(pdev)) { | ||
| 530 | mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id)); | ||
| 531 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
| 532 | entry, src_id, false); | ||
| 533 | } | ||
| 534 | dev_dbg(mdev->sdev->parent, "callback %d unregistered for src: %d\n", | ||
| 535 | offset, src_id); | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | /** | ||
| 540 | * mic_setup_interrupts - Initializes interrupts. | ||
| 541 | * | ||
| 542 | * @mdev: pointer to mic_device instance | ||
| 543 | * @pdev: PCI device structure | ||
| 544 | * | ||
| 545 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 546 | */ | ||
| 547 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev) | ||
| 548 | { | ||
| 549 | int rc; | ||
| 550 | |||
| 551 | rc = mic_setup_msix(mdev, pdev); | ||
| 552 | if (!rc) | ||
| 553 | goto done; | ||
| 554 | |||
| 555 | rc = mic_setup_msi(mdev, pdev); | ||
| 556 | if (!rc) | ||
| 557 | goto done; | ||
| 558 | |||
| 559 | rc = mic_setup_intx(mdev, pdev); | ||
| 560 | if (rc) { | ||
| 561 | dev_err(mdev->sdev->parent, "no usable interrupts\n"); | ||
| 562 | return rc; | ||
| 563 | } | ||
| 564 | done: | ||
| 565 | mdev->intr_ops->enable_interrupts(mdev); | ||
| 566 | return 0; | ||
| 567 | } | ||
| 568 | |||
| 569 | /** | ||
| 570 | * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts | ||
| 571 | * | ||
| 572 | * @mdev: pointer to mic_device instance | ||
| 573 | * @pdev: PCI device structure | ||
| 574 | * | ||
| 575 | * returns none. | ||
| 576 | */ | ||
| 577 | void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) | ||
| 578 | { | ||
| 579 | int i; | ||
| 580 | |||
| 581 | mdev->intr_ops->disable_interrupts(mdev); | ||
| 582 | if (mdev->irq_info.num_vectors > 1) { | ||
| 583 | for (i = 0; i < mdev->irq_info.num_vectors; i++) { | ||
| 584 | if (mdev->irq_info.mic_msi_map[i]) | ||
| 585 | dev_warn(&pdev->dev, "irq %d may still be in use.\n", | ||
| 586 | mdev->irq_info.msix_entries[i].vector); | ||
| 587 | } | ||
| 588 | kfree(mdev->irq_info.mic_msi_map); | ||
| 589 | kfree(mdev->irq_info.msix_entries); | ||
| 590 | pci_disable_msix(pdev); | ||
| 591 | } else { | ||
| 592 | if (pci_dev_msi_enabled(pdev)) { | ||
| 593 | free_irq(pdev->irq, mdev); | ||
| 594 | kfree(mdev->irq_info.mic_msi_map); | ||
| 595 | pci_disable_msi(pdev); | ||
| 596 | } else { | ||
| 597 | free_irq(pdev->irq, mdev); | ||
| 598 | } | ||
| 599 | mic_release_callbacks(mdev); | ||
| 600 | } | ||
| 601 | } | ||
| 602 | |||
| 603 | /** | ||
| 604 | * mic_intr_restore - Restore MIC interrupt registers. | ||
| 605 | * | ||
| 606 | * @mdev: pointer to mic_device instance. | ||
| 607 | * | ||
| 608 | * Restore the interrupt registers to values previously | ||
| 609 | * stored in the SW data structures. mic_mutex needs to | ||
| 610 | * be held before calling this function. | ||
| 611 | * | ||
| 612 | * returns None. | ||
| 613 | */ | ||
| 614 | void mic_intr_restore(struct mic_device *mdev) | ||
| 615 | { | ||
| 616 | int entry, offset; | ||
| 617 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 618 | struct pci_dev, dev); | ||
| 619 | |||
| 620 | if (!pci_dev_msi_enabled(pdev)) | ||
| 621 | return; | ||
| 622 | |||
| 623 | for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) { | ||
| 624 | for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) { | ||
| 625 | if (mdev->irq_info.mic_msi_map[entry] & BIT(offset)) | ||
| 626 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
| 627 | entry, offset, true); | ||
| 628 | } | ||
| 629 | } | ||
| 630 | } | ||
diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h new file mode 100644 index 000000000000..6091aa97e116 --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.h | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef _MIC_INTR_H_ | ||
| 22 | #define _MIC_INTR_H_ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * The minimum number of msix vectors required for normal operation. | ||
| 26 | * 3 for virtio network, console and block devices. | ||
| 27 | * 1 for card shutdown notifications. | ||
| 28 | */ | ||
| 29 | #define MIC_MIN_MSIX 4 | ||
| 30 | #define MIC_NUM_OFFSETS 32 | ||
| 31 | |||
| 32 | /** | ||
| 33 | * mic_intr_source - The type of source that will generate | ||
| 34 | * the interrupt.The number of types needs to be in sync with | ||
| 35 | * MIC_NUM_INTR_TYPES | ||
| 36 | * | ||
| 37 | * MIC_INTR_DB: The source is a doorbell | ||
| 38 | * MIC_INTR_DMA: The source is a DMA channel | ||
| 39 | * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR | ||
| 40 | * MIC_NUM_INTR_TYPES: Total number of interrupt sources. | ||
| 41 | */ | ||
| 42 | enum mic_intr_type { | ||
| 43 | MIC_INTR_DB = 0, | ||
| 44 | MIC_INTR_DMA, | ||
| 45 | MIC_INTR_ERR, | ||
| 46 | MIC_NUM_INTR_TYPES | ||
| 47 | }; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * struct mic_intr_info - Contains h/w specific interrupt sources | ||
| 51 | * information. | ||
| 52 | * | ||
| 53 | * @intr_start_idx: Contains the starting indexes of the | ||
| 54 | * interrupt types. | ||
| 55 | * @intr_len: Contains the length of the interrupt types. | ||
| 56 | */ | ||
| 57 | struct mic_intr_info { | ||
| 58 | u16 intr_start_idx[MIC_NUM_INTR_TYPES]; | ||
| 59 | u16 intr_len[MIC_NUM_INTR_TYPES]; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * struct mic_irq_info - OS specific irq information | ||
| 64 | * | ||
| 65 | * @next_avail_src: next available doorbell that can be assigned. | ||
| 66 | * @msix_entries: msix entries allocated while setting up MSI-x | ||
| 67 | * @mic_msi_map: The MSI/MSI-x mapping information. | ||
| 68 | * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. | ||
| 69 | * @cb_ida: callback ID allocator to track the callbacks registered. | ||
| 70 | * @mic_intr_lock: spinlock to protect the interrupt callback list. | ||
| 71 | * @cb_list: Array of callback lists one for each source. | ||
| 72 | */ | ||
| 73 | struct mic_irq_info { | ||
| 74 | int next_avail_src; | ||
| 75 | struct msix_entry *msix_entries; | ||
| 76 | u32 *mic_msi_map; | ||
| 77 | u16 num_vectors; | ||
| 78 | struct ida cb_ida; | ||
| 79 | spinlock_t mic_intr_lock; | ||
| 80 | struct list_head *cb_list; | ||
| 81 | }; | ||
| 82 | |||
| 83 | /** | ||
| 84 | * struct mic_intr_cb - Interrupt callback structure. | ||
| 85 | * | ||
| 86 | * @func: The callback function | ||
| 87 | * @data: Private data of the requester. | ||
| 88 | * @cb_id: The callback id. Identifies this callback. | ||
| 89 | * @list: list head pointing to the next callback structure. | ||
| 90 | */ | ||
| 91 | struct mic_intr_cb { | ||
| 92 | irqreturn_t (*func) (int irq, void *data); | ||
| 93 | void *data; | ||
| 94 | int cb_id; | ||
| 95 | struct list_head list; | ||
| 96 | }; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * struct mic_irq - opaque pointer used as cookie | ||
| 100 | */ | ||
| 101 | struct mic_irq; | ||
| 102 | |||
| 103 | /* Forward declaration */ | ||
| 104 | struct mic_device; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * struct mic_hw_intr_ops: MIC HW specific interrupt operations | ||
| 108 | * @intr_init: Initialize H/W specific interrupt information. | ||
| 109 | * @enable_interrupts: Enable interrupts from the hardware. | ||
| 110 | * @disable_interrupts: Disable interrupts from the hardware. | ||
| 111 | * @program_msi_to_src_map: Update MSI mapping registers with | ||
| 112 | * irq information. | ||
| 113 | * @read_msi_to_src_map: Read MSI mapping registers containing | ||
| 114 | * irq information. | ||
| 115 | */ | ||
| 116 | struct mic_hw_intr_ops { | ||
| 117 | void (*intr_init)(struct mic_device *mdev); | ||
| 118 | void (*enable_interrupts)(struct mic_device *mdev); | ||
| 119 | void (*disable_interrupts)(struct mic_device *mdev); | ||
| 120 | void (*program_msi_to_src_map) (struct mic_device *mdev, | ||
| 121 | int idx, int intr_src, bool set); | ||
| 122 | u32 (*read_msi_to_src_map) (struct mic_device *mdev, | ||
| 123 | int idx); | ||
| 124 | }; | ||
| 125 | |||
| 126 | int mic_next_db(struct mic_device *mdev); | ||
| 127 | struct mic_irq *mic_request_irq(struct mic_device *mdev, | ||
| 128 | irqreturn_t (*func)(int irq, void *data), | ||
| 129 | const char *name, void *data, int intr_src, | ||
| 130 | enum mic_intr_type type); | ||
| 131 | |||
| 132 | void mic_free_irq(struct mic_device *mdev, | ||
| 133 | struct mic_irq *cookie, void *data); | ||
| 134 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); | ||
| 135 | void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev); | ||
| 136 | void mic_intr_restore(struct mic_device *mdev); | ||
| 137 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c new file mode 100644 index 000000000000..ad838c7651c4 --- /dev/null +++ b/drivers/misc/mic/host/mic_main.c | |||
| @@ -0,0 +1,536 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | * Global TODO's across the driver to be added after initial base | ||
| 21 | * patches are accepted upstream: | ||
| 22 | * 1) Enable DMA support. | ||
| 23 | * 2) Enable per vring interrupt support. | ||
| 24 | */ | ||
| 25 | #include <linux/fs.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/pci.h> | ||
| 28 | #include <linux/poll.h> | ||
| 29 | #include <linux/suspend.h> | ||
| 30 | |||
| 31 | #include <linux/mic_common.h> | ||
| 32 | #include "../common/mic_dev.h" | ||
| 33 | #include "mic_device.h" | ||
| 34 | #include "mic_x100.h" | ||
| 35 | #include "mic_smpt.h" | ||
| 36 | #include "mic_fops.h" | ||
| 37 | #include "mic_virtio.h" | ||
| 38 | |||
| 39 | static const char mic_driver_name[] = "mic"; | ||
| 40 | |||
| 41 | static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = { | ||
| 42 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, | ||
| 43 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, | ||
| 44 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, | ||
| 45 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)}, | ||
| 46 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)}, | ||
| 47 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)}, | ||
| 48 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)}, | ||
| 49 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)}, | ||
| 50 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)}, | ||
| 51 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)}, | ||
| 52 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)}, | ||
| 53 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)}, | ||
| 54 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)}, | ||
| 55 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)}, | ||
| 56 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)}, | ||
| 57 | |||
| 58 | /* required last entry */ | ||
| 59 | { 0, } | ||
| 60 | }; | ||
| 61 | |||
| 62 | MODULE_DEVICE_TABLE(pci, mic_pci_tbl); | ||
| 63 | |||
| 64 | /* ID allocator for MIC devices */ | ||
| 65 | static struct ida g_mic_ida; | ||
| 66 | /* Class of MIC devices for sysfs accessibility. */ | ||
| 67 | static struct class *g_mic_class; | ||
| 68 | /* Base device node number for MIC devices */ | ||
| 69 | static dev_t g_mic_devno; | ||
| 70 | |||
| 71 | static const struct file_operations mic_fops = { | ||
| 72 | .open = mic_open, | ||
| 73 | .release = mic_release, | ||
| 74 | .unlocked_ioctl = mic_ioctl, | ||
| 75 | .poll = mic_poll, | ||
| 76 | .mmap = mic_mmap, | ||
| 77 | .owner = THIS_MODULE, | ||
| 78 | }; | ||
| 79 | |||
| 80 | /* Initialize the device page */ | ||
| 81 | static int mic_dp_init(struct mic_device *mdev) | ||
| 82 | { | ||
| 83 | mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL); | ||
| 84 | if (!mdev->dp) { | ||
| 85 | dev_err(mdev->sdev->parent, "%s %d err %d\n", | ||
| 86 | __func__, __LINE__, -ENOMEM); | ||
| 87 | return -ENOMEM; | ||
| 88 | } | ||
| 89 | |||
| 90 | mdev->dp_dma_addr = mic_map_single(mdev, | ||
| 91 | mdev->dp, MIC_DP_SIZE); | ||
| 92 | if (mic_map_error(mdev->dp_dma_addr)) { | ||
| 93 | kfree(mdev->dp); | ||
| 94 | dev_err(mdev->sdev->parent, "%s %d err %d\n", | ||
| 95 | __func__, __LINE__, -ENOMEM); | ||
| 96 | return -ENOMEM; | ||
| 97 | } | ||
| 98 | mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); | ||
| 99 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); | ||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Uninitialize the device page */ | ||
| 104 | static void mic_dp_uninit(struct mic_device *mdev) | ||
| 105 | { | ||
| 106 | mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE); | ||
| 107 | kfree(mdev->dp); | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * mic_shutdown_db - Shutdown doorbell interrupt handler. | ||
| 112 | */ | ||
| 113 | static irqreturn_t mic_shutdown_db(int irq, void *data) | ||
| 114 | { | ||
| 115 | struct mic_device *mdev = data; | ||
| 116 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 117 | |||
| 118 | mdev->ops->ack_interrupt(mdev); | ||
| 119 | |||
| 120 | switch (bootparam->shutdown_status) { | ||
| 121 | case MIC_HALTED: | ||
| 122 | case MIC_POWER_OFF: | ||
| 123 | case MIC_RESTART: | ||
| 124 | /* Fall through */ | ||
| 125 | case MIC_CRASHED: | ||
| 126 | schedule_work(&mdev->shutdown_work); | ||
| 127 | break; | ||
| 128 | default: | ||
| 129 | break; | ||
| 130 | }; | ||
| 131 | return IRQ_HANDLED; | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * mic_ops_init: Initialize HW specific operation tables. | ||
| 136 | * | ||
| 137 | * @mdev: pointer to mic_device instance | ||
| 138 | * | ||
| 139 | * returns none. | ||
| 140 | */ | ||
| 141 | static void mic_ops_init(struct mic_device *mdev) | ||
| 142 | { | ||
| 143 | switch (mdev->family) { | ||
| 144 | case MIC_FAMILY_X100: | ||
| 145 | mdev->ops = &mic_x100_ops; | ||
| 146 | mdev->intr_ops = &mic_x100_intr_ops; | ||
| 147 | mdev->smpt_ops = &mic_x100_smpt_ops; | ||
| 148 | break; | ||
| 149 | default: | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * mic_get_family - Determine hardware family to which this MIC belongs. | ||
| 156 | * | ||
| 157 | * @pdev: The pci device structure | ||
| 158 | * | ||
| 159 | * returns family. | ||
| 160 | */ | ||
| 161 | static enum mic_hw_family mic_get_family(struct pci_dev *pdev) | ||
| 162 | { | ||
| 163 | enum mic_hw_family family; | ||
| 164 | |||
| 165 | switch (pdev->device) { | ||
| 166 | case MIC_X100_PCI_DEVICE_2250: | ||
| 167 | case MIC_X100_PCI_DEVICE_2251: | ||
| 168 | case MIC_X100_PCI_DEVICE_2252: | ||
| 169 | case MIC_X100_PCI_DEVICE_2253: | ||
| 170 | case MIC_X100_PCI_DEVICE_2254: | ||
| 171 | case MIC_X100_PCI_DEVICE_2255: | ||
| 172 | case MIC_X100_PCI_DEVICE_2256: | ||
| 173 | case MIC_X100_PCI_DEVICE_2257: | ||
| 174 | case MIC_X100_PCI_DEVICE_2258: | ||
| 175 | case MIC_X100_PCI_DEVICE_2259: | ||
| 176 | case MIC_X100_PCI_DEVICE_225a: | ||
| 177 | case MIC_X100_PCI_DEVICE_225b: | ||
| 178 | case MIC_X100_PCI_DEVICE_225c: | ||
| 179 | case MIC_X100_PCI_DEVICE_225d: | ||
| 180 | case MIC_X100_PCI_DEVICE_225e: | ||
| 181 | family = MIC_FAMILY_X100; | ||
| 182 | break; | ||
| 183 | default: | ||
| 184 | family = MIC_FAMILY_UNKNOWN; | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | return family; | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 191 | * mic_pm_notifier: Notifier callback function that handles | ||
| 192 | * PM notifications. | ||
| 193 | * | ||
| 194 | * @notifier_block: The notifier structure. | ||
| 195 | * @pm_event: The event for which the driver was notified. | ||
| 196 | * @unused: Meaningless. Always NULL. | ||
| 197 | * | ||
| 198 | * returns NOTIFY_DONE | ||
| 199 | */ | ||
| 200 | static int mic_pm_notifier(struct notifier_block *notifier, | ||
| 201 | unsigned long pm_event, void *unused) | ||
| 202 | { | ||
| 203 | struct mic_device *mdev = container_of(notifier, | ||
| 204 | struct mic_device, pm_notifier); | ||
| 205 | |||
| 206 | switch (pm_event) { | ||
| 207 | case PM_HIBERNATION_PREPARE: | ||
| 208 | /* Fall through */ | ||
| 209 | case PM_SUSPEND_PREPARE: | ||
| 210 | mic_prepare_suspend(mdev); | ||
| 211 | break; | ||
| 212 | case PM_POST_HIBERNATION: | ||
| 213 | /* Fall through */ | ||
| 214 | case PM_POST_SUSPEND: | ||
| 215 | /* Fall through */ | ||
| 216 | case PM_POST_RESTORE: | ||
| 217 | mic_complete_resume(mdev); | ||
| 218 | break; | ||
| 219 | case PM_RESTORE_PREPARE: | ||
| 220 | break; | ||
| 221 | default: | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | return NOTIFY_DONE; | ||
| 225 | } | ||
| 226 | |||
| 227 | /** | ||
| 228 | * mic_device_init - Allocates and initializes the MIC device structure | ||
| 229 | * | ||
| 230 | * @mdev: pointer to mic_device instance | ||
| 231 | * @pdev: The pci device structure | ||
| 232 | * | ||
| 233 | * returns none. | ||
| 234 | */ | ||
| 235 | static int | ||
| 236 | mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) | ||
| 237 | { | ||
| 238 | int rc; | ||
| 239 | |||
| 240 | mdev->family = mic_get_family(pdev); | ||
| 241 | mdev->stepping = pdev->revision; | ||
| 242 | mic_ops_init(mdev); | ||
| 243 | mic_sysfs_init(mdev); | ||
| 244 | mutex_init(&mdev->mic_mutex); | ||
| 245 | mdev->irq_info.next_avail_src = 0; | ||
| 246 | INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); | ||
| 247 | INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); | ||
| 248 | init_completion(&mdev->reset_wait); | ||
| 249 | INIT_LIST_HEAD(&mdev->vdev_list); | ||
| 250 | mdev->pm_notifier.notifier_call = mic_pm_notifier; | ||
| 251 | rc = register_pm_notifier(&mdev->pm_notifier); | ||
| 252 | if (rc) { | ||
| 253 | dev_err(&pdev->dev, "register_pm_notifier failed rc %d\n", | ||
| 254 | rc); | ||
| 255 | goto register_pm_notifier_fail; | ||
| 256 | } | ||
| 257 | return 0; | ||
| 258 | register_pm_notifier_fail: | ||
| 259 | flush_work(&mdev->shutdown_work); | ||
| 260 | flush_work(&mdev->reset_trigger_work); | ||
| 261 | return rc; | ||
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * mic_device_uninit - Frees resources allocated during mic_device_init(..) | ||
| 266 | * | ||
| 267 | * @mdev: pointer to mic_device instance | ||
| 268 | * | ||
| 269 | * returns none | ||
| 270 | */ | ||
| 271 | static void mic_device_uninit(struct mic_device *mdev) | ||
| 272 | { | ||
| 273 | /* The cmdline sysfs entry might have allocated cmdline */ | ||
| 274 | kfree(mdev->cmdline); | ||
| 275 | kfree(mdev->firmware); | ||
| 276 | kfree(mdev->ramdisk); | ||
| 277 | kfree(mdev->bootmode); | ||
| 278 | flush_work(&mdev->reset_trigger_work); | ||
| 279 | flush_work(&mdev->shutdown_work); | ||
| 280 | unregister_pm_notifier(&mdev->pm_notifier); | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * mic_probe - Device Initialization Routine | ||
| 285 | * | ||
| 286 | * @pdev: PCI device structure | ||
| 287 | * @ent: entry in mic_pci_tbl | ||
| 288 | * | ||
| 289 | * returns 0 on success, < 0 on failure. | ||
| 290 | */ | ||
| 291 | static int mic_probe(struct pci_dev *pdev, | ||
| 292 | const struct pci_device_id *ent) | ||
| 293 | { | ||
| 294 | int rc; | ||
| 295 | struct mic_device *mdev; | ||
| 296 | |||
| 297 | mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); | ||
| 298 | if (!mdev) { | ||
| 299 | rc = -ENOMEM; | ||
| 300 | dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc); | ||
| 301 | goto mdev_alloc_fail; | ||
| 302 | } | ||
| 303 | mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); | ||
| 304 | if (mdev->id < 0) { | ||
| 305 | rc = mdev->id; | ||
| 306 | dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc); | ||
| 307 | goto ida_fail; | ||
| 308 | } | ||
| 309 | |||
| 310 | rc = mic_device_init(mdev, pdev); | ||
| 311 | if (rc) { | ||
| 312 | dev_err(&pdev->dev, "mic_device_init failed rc %d\n", rc); | ||
| 313 | goto device_init_fail; | ||
| 314 | } | ||
| 315 | |||
| 316 | rc = pci_enable_device(pdev); | ||
| 317 | if (rc) { | ||
| 318 | dev_err(&pdev->dev, "failed to enable pci device.\n"); | ||
| 319 | goto uninit_device; | ||
| 320 | } | ||
| 321 | |||
| 322 | pci_set_master(pdev); | ||
| 323 | |||
| 324 | rc = pci_request_regions(pdev, mic_driver_name); | ||
| 325 | if (rc) { | ||
| 326 | dev_err(&pdev->dev, "failed to get pci regions.\n"); | ||
| 327 | goto disable_device; | ||
| 328 | } | ||
| 329 | |||
| 330 | rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); | ||
| 331 | if (rc) { | ||
| 332 | dev_err(&pdev->dev, "Cannot set DMA mask\n"); | ||
| 333 | goto release_regions; | ||
| 334 | } | ||
| 335 | |||
| 336 | mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar); | ||
| 337 | mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar); | ||
| 338 | mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar); | ||
| 339 | if (!mdev->mmio.va) { | ||
| 340 | dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); | ||
| 341 | rc = -EIO; | ||
| 342 | goto release_regions; | ||
| 343 | } | ||
| 344 | |||
| 345 | mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar); | ||
| 346 | mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar); | ||
| 347 | mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len); | ||
| 348 | if (!mdev->aper.va) { | ||
| 349 | dev_err(&pdev->dev, "Cannot remap Aperture BAR\n"); | ||
| 350 | rc = -EIO; | ||
| 351 | goto unmap_mmio; | ||
| 352 | } | ||
| 353 | |||
| 354 | mdev->intr_ops->intr_init(mdev); | ||
| 355 | rc = mic_setup_interrupts(mdev, pdev); | ||
| 356 | if (rc) { | ||
| 357 | dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc); | ||
| 358 | goto unmap_aper; | ||
| 359 | } | ||
| 360 | rc = mic_smpt_init(mdev); | ||
| 361 | if (rc) { | ||
| 362 | dev_err(&pdev->dev, "smpt_init failed %d\n", rc); | ||
| 363 | goto free_interrupts; | ||
| 364 | } | ||
| 365 | |||
| 366 | pci_set_drvdata(pdev, mdev); | ||
| 367 | |||
| 368 | mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev, | ||
| 369 | MKDEV(MAJOR(g_mic_devno), mdev->id), NULL, | ||
| 370 | mdev->attr_group, "mic%d", mdev->id); | ||
| 371 | if (IS_ERR(mdev->sdev)) { | ||
| 372 | rc = PTR_ERR(mdev->sdev); | ||
| 373 | dev_err(&pdev->dev, | ||
| 374 | "device_create_with_groups failed rc %d\n", rc); | ||
| 375 | goto smpt_uninit; | ||
| 376 | } | ||
| 377 | mdev->state_sysfs = sysfs_get_dirent(mdev->sdev->kobj.sd, "state"); | ||
| 378 | if (!mdev->state_sysfs) { | ||
| 379 | rc = -ENODEV; | ||
| 380 | dev_err(&pdev->dev, "sysfs_get_dirent failed rc %d\n", rc); | ||
| 381 | goto destroy_device; | ||
| 382 | } | ||
| 383 | |||
| 384 | rc = mic_dp_init(mdev); | ||
| 385 | if (rc) { | ||
| 386 | dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc); | ||
| 387 | goto sysfs_put; | ||
| 388 | } | ||
| 389 | mutex_lock(&mdev->mic_mutex); | ||
| 390 | |||
| 391 | mdev->shutdown_db = mic_next_db(mdev); | ||
| 392 | mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db, | ||
| 393 | "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB); | ||
| 394 | if (IS_ERR(mdev->shutdown_cookie)) { | ||
| 395 | rc = PTR_ERR(mdev->shutdown_cookie); | ||
| 396 | mutex_unlock(&mdev->mic_mutex); | ||
| 397 | goto dp_uninit; | ||
| 398 | } | ||
| 399 | mutex_unlock(&mdev->mic_mutex); | ||
| 400 | mic_bootparam_init(mdev); | ||
| 401 | |||
| 402 | mic_create_debug_dir(mdev); | ||
| 403 | cdev_init(&mdev->cdev, &mic_fops); | ||
| 404 | mdev->cdev.owner = THIS_MODULE; | ||
| 405 | rc = cdev_add(&mdev->cdev, MKDEV(MAJOR(g_mic_devno), mdev->id), 1); | ||
| 406 | if (rc) { | ||
| 407 | dev_err(&pdev->dev, "cdev_add err id %d rc %d\n", mdev->id, rc); | ||
| 408 | goto cleanup_debug_dir; | ||
| 409 | } | ||
| 410 | return 0; | ||
| 411 | cleanup_debug_dir: | ||
| 412 | mic_delete_debug_dir(mdev); | ||
| 413 | mutex_lock(&mdev->mic_mutex); | ||
| 414 | mic_free_irq(mdev, mdev->shutdown_cookie, mdev); | ||
| 415 | mutex_unlock(&mdev->mic_mutex); | ||
| 416 | dp_uninit: | ||
| 417 | mic_dp_uninit(mdev); | ||
| 418 | sysfs_put: | ||
| 419 | sysfs_put(mdev->state_sysfs); | ||
| 420 | destroy_device: | ||
| 421 | device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); | ||
| 422 | smpt_uninit: | ||
| 423 | mic_smpt_uninit(mdev); | ||
| 424 | free_interrupts: | ||
| 425 | mic_free_interrupts(mdev, pdev); | ||
| 426 | unmap_aper: | ||
| 427 | iounmap(mdev->aper.va); | ||
| 428 | unmap_mmio: | ||
| 429 | iounmap(mdev->mmio.va); | ||
| 430 | release_regions: | ||
| 431 | pci_release_regions(pdev); | ||
| 432 | disable_device: | ||
| 433 | pci_disable_device(pdev); | ||
| 434 | uninit_device: | ||
| 435 | mic_device_uninit(mdev); | ||
| 436 | device_init_fail: | ||
| 437 | ida_simple_remove(&g_mic_ida, mdev->id); | ||
| 438 | ida_fail: | ||
| 439 | kfree(mdev); | ||
| 440 | mdev_alloc_fail: | ||
| 441 | dev_err(&pdev->dev, "Probe failed rc %d\n", rc); | ||
| 442 | return rc; | ||
| 443 | } | ||
| 444 | |||
| 445 | /** | ||
| 446 | * mic_remove - Device Removal Routine | ||
| 447 | * mic_remove is called by the PCI subsystem to alert the driver | ||
| 448 | * that it should release a PCI device. | ||
| 449 | * | ||
| 450 | * @pdev: PCI device structure | ||
| 451 | */ | ||
| 452 | static void mic_remove(struct pci_dev *pdev) | ||
| 453 | { | ||
| 454 | struct mic_device *mdev; | ||
| 455 | |||
| 456 | mdev = pci_get_drvdata(pdev); | ||
| 457 | if (!mdev) | ||
| 458 | return; | ||
| 459 | |||
| 460 | mic_stop(mdev, false); | ||
| 461 | cdev_del(&mdev->cdev); | ||
| 462 | mic_delete_debug_dir(mdev); | ||
| 463 | mutex_lock(&mdev->mic_mutex); | ||
| 464 | mic_free_irq(mdev, mdev->shutdown_cookie, mdev); | ||
| 465 | mutex_unlock(&mdev->mic_mutex); | ||
| 466 | flush_work(&mdev->shutdown_work); | ||
| 467 | mic_dp_uninit(mdev); | ||
| 468 | sysfs_put(mdev->state_sysfs); | ||
| 469 | device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); | ||
| 470 | mic_smpt_uninit(mdev); | ||
| 471 | mic_free_interrupts(mdev, pdev); | ||
| 472 | iounmap(mdev->mmio.va); | ||
| 473 | iounmap(mdev->aper.va); | ||
| 474 | mic_device_uninit(mdev); | ||
| 475 | pci_release_regions(pdev); | ||
| 476 | pci_disable_device(pdev); | ||
| 477 | ida_simple_remove(&g_mic_ida, mdev->id); | ||
| 478 | kfree(mdev); | ||
| 479 | } | ||
| 480 | static struct pci_driver mic_driver = { | ||
| 481 | .name = mic_driver_name, | ||
| 482 | .id_table = mic_pci_tbl, | ||
| 483 | .probe = mic_probe, | ||
| 484 | .remove = mic_remove | ||
| 485 | }; | ||
| 486 | |||
| 487 | static int __init mic_init(void) | ||
| 488 | { | ||
| 489 | int ret; | ||
| 490 | |||
| 491 | ret = alloc_chrdev_region(&g_mic_devno, 0, | ||
| 492 | MIC_MAX_NUM_DEVS, mic_driver_name); | ||
| 493 | if (ret) { | ||
| 494 | pr_err("alloc_chrdev_region failed ret %d\n", ret); | ||
| 495 | goto error; | ||
| 496 | } | ||
| 497 | |||
| 498 | g_mic_class = class_create(THIS_MODULE, mic_driver_name); | ||
| 499 | if (IS_ERR(g_mic_class)) { | ||
| 500 | ret = PTR_ERR(g_mic_class); | ||
| 501 | pr_err("class_create failed ret %d\n", ret); | ||
| 502 | goto cleanup_chrdev; | ||
| 503 | } | ||
| 504 | |||
| 505 | mic_init_debugfs(); | ||
| 506 | ida_init(&g_mic_ida); | ||
| 507 | ret = pci_register_driver(&mic_driver); | ||
| 508 | if (ret) { | ||
| 509 | pr_err("pci_register_driver failed ret %d\n", ret); | ||
| 510 | goto cleanup_debugfs; | ||
| 511 | } | ||
| 512 | return ret; | ||
| 513 | cleanup_debugfs: | ||
| 514 | mic_exit_debugfs(); | ||
| 515 | class_destroy(g_mic_class); | ||
| 516 | cleanup_chrdev: | ||
| 517 | unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); | ||
| 518 | error: | ||
| 519 | return ret; | ||
| 520 | } | ||
| 521 | |||
| 522 | static void __exit mic_exit(void) | ||
| 523 | { | ||
| 524 | pci_unregister_driver(&mic_driver); | ||
| 525 | ida_destroy(&g_mic_ida); | ||
| 526 | mic_exit_debugfs(); | ||
| 527 | class_destroy(g_mic_class); | ||
| 528 | unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); | ||
| 529 | } | ||
| 530 | |||
| 531 | module_init(mic_init); | ||
| 532 | module_exit(mic_exit); | ||
| 533 | |||
| 534 | MODULE_AUTHOR("Intel Corporation"); | ||
| 535 | MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver"); | ||
| 536 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c new file mode 100644 index 000000000000..fae474c4899e --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.c | |||
| @@ -0,0 +1,442 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/pci.h> | ||
| 22 | |||
| 23 | #include "../common/mic_dev.h" | ||
| 24 | #include "mic_device.h" | ||
| 25 | #include "mic_smpt.h" | ||
| 26 | |||
| 27 | static inline u64 mic_system_page_mask(struct mic_device *mdev) | ||
| 28 | { | ||
| 29 | return (1ULL << mdev->smpt->info.page_shift) - 1ULL; | ||
| 30 | } | ||
| 31 | |||
| 32 | static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa) | ||
| 33 | { | ||
| 34 | return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift; | ||
| 35 | } | ||
| 36 | |||
| 37 | static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index) | ||
| 38 | { | ||
| 39 | return mdev->smpt->info.base + (index * mdev->smpt->info.page_size); | ||
| 40 | } | ||
| 41 | |||
| 42 | static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa) | ||
| 43 | { | ||
| 44 | return pa & mic_system_page_mask(mdev); | ||
| 45 | } | ||
| 46 | |||
| 47 | static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa) | ||
| 48 | { | ||
| 49 | return ALIGN(pa - mic_system_page_mask(mdev), | ||
| 50 | mdev->smpt->info.page_size); | ||
| 51 | } | ||
| 52 | |||
| 53 | static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa) | ||
| 54 | { | ||
| 55 | return ALIGN(pa, mdev->smpt->info.page_size); | ||
| 56 | } | ||
| 57 | |||
| 58 | /* Total Cumulative system memory accessible by MIC across all SMPT entries */ | ||
| 59 | static inline u64 mic_max_system_memory(struct mic_device *mdev) | ||
| 60 | { | ||
| 61 | return mdev->smpt->info.num_reg * mdev->smpt->info.page_size; | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Maximum system memory address accessible by MIC */ | ||
| 65 | static inline u64 mic_max_system_addr(struct mic_device *mdev) | ||
| 66 | { | ||
| 67 | return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Check if the DMA address is a MIC system memory address */ | ||
| 71 | static inline bool | ||
| 72 | mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa) | ||
| 73 | { | ||
| 74 | return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Populate an SMPT entry and update the reference counts. */ | ||
| 78 | static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, | ||
| 79 | int entries, struct mic_device *mdev) | ||
| 80 | { | ||
| 81 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
| 82 | int i; | ||
| 83 | |||
| 84 | for (i = spt; i < spt + entries; i++, | ||
| 85 | addr += smpt_info->info.page_size) { | ||
| 86 | if (!smpt_info->entry[i].ref_count && | ||
| 87 | (smpt_info->entry[i].dma_addr != addr)) { | ||
| 88 | mdev->smpt_ops->set(mdev, addr, i); | ||
| 89 | smpt_info->entry[i].dma_addr = addr; | ||
| 90 | } | ||
| 91 | smpt_info->entry[i].ref_count += ref[i - spt]; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Find an available MIC address in MIC SMPT address space | ||
| 97 | * for a given DMA address and size. | ||
| 98 | */ | ||
| 99 | static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr, | ||
| 100 | int entries, s64 *ref, size_t size) | ||
| 101 | { | ||
| 102 | int spt; | ||
| 103 | int ae = 0; | ||
| 104 | int i; | ||
| 105 | unsigned long flags; | ||
| 106 | dma_addr_t mic_addr = 0; | ||
| 107 | dma_addr_t addr = dma_addr; | ||
| 108 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
| 109 | |||
| 110 | spin_lock_irqsave(&smpt_info->smpt_lock, flags); | ||
| 111 | |||
| 112 | /* find existing entries */ | ||
| 113 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
| 114 | if (smpt_info->entry[i].dma_addr == addr) { | ||
| 115 | ae++; | ||
| 116 | addr += smpt_info->info.page_size; | ||
| 117 | } else if (ae) /* cannot find contiguous entries */ | ||
| 118 | goto not_found; | ||
| 119 | |||
| 120 | if (ae == entries) | ||
| 121 | goto found; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* find free entry */ | ||
| 125 | for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) { | ||
| 126 | ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0; | ||
| 127 | if (ae == entries) | ||
| 128 | goto found; | ||
| 129 | } | ||
| 130 | |||
| 131 | not_found: | ||
| 132 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
| 133 | return mic_addr; | ||
| 134 | |||
| 135 | found: | ||
| 136 | spt = i - entries + 1; | ||
| 137 | mic_addr = mic_smpt_to_pa(mdev, spt); | ||
| 138 | mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev); | ||
| 139 | smpt_info->map_count++; | ||
| 140 | smpt_info->ref_count += (s64)size; | ||
| 141 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
| 142 | return mic_addr; | ||
| 143 | } | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Returns number of smpt entries needed for dma_addr to dma_addr + size | ||
| 147 | * also returns the reference count array for each of those entries | ||
| 148 | * and the starting smpt address | ||
| 149 | */ | ||
| 150 | static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr, | ||
| 151 | size_t size, s64 *ref, u64 *smpt_start) | ||
| 152 | { | ||
| 153 | u64 start = dma_addr; | ||
| 154 | u64 end = dma_addr + size; | ||
| 155 | int i = 0; | ||
| 156 | |||
| 157 | while (start < end) { | ||
| 158 | ref[i++] = min(mic_smpt_align_high(mdev, start + 1), | ||
| 159 | end) - start; | ||
| 160 | start = mic_smpt_align_high(mdev, start + 1); | ||
| 161 | } | ||
| 162 | |||
| 163 | if (smpt_start) | ||
| 164 | *smpt_start = mic_smpt_align_low(mdev, dma_addr); | ||
| 165 | |||
| 166 | return i; | ||
| 167 | } | ||
| 168 | |||
| 169 | /* | ||
| 170 | * mic_to_dma_addr - Converts a MIC address to a DMA address. | ||
| 171 | * | ||
| 172 | * @mdev: pointer to mic_device instance. | ||
| 173 | * @mic_addr: MIC address. | ||
| 174 | * | ||
| 175 | * returns a DMA address. | ||
| 176 | */ | ||
| 177 | static dma_addr_t | ||
| 178 | mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) | ||
| 179 | { | ||
| 180 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
| 181 | int spt; | ||
| 182 | dma_addr_t dma_addr; | ||
| 183 | |||
| 184 | if (!mic_is_system_addr(mdev, mic_addr)) { | ||
| 185 | dev_err(mdev->sdev->parent, | ||
| 186 | "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); | ||
| 187 | return -EINVAL; | ||
| 188 | } | ||
| 189 | spt = mic_sys_addr_to_smpt(mdev, mic_addr); | ||
| 190 | dma_addr = smpt_info->entry[spt].dma_addr + | ||
| 191 | mic_smpt_offset(mdev, mic_addr); | ||
| 192 | return dma_addr; | ||
| 193 | } | ||
| 194 | |||
| 195 | /** | ||
| 196 | * mic_map - Maps a DMA address to a MIC physical address. | ||
| 197 | * | ||
| 198 | * @mdev: pointer to mic_device instance. | ||
| 199 | * @dma_addr: DMA address. | ||
| 200 | * @size: Size of the region to be mapped. | ||
| 201 | * | ||
| 202 | * This API converts the DMA address provided to a DMA address understood | ||
| 203 | * by MIC. Caller should check for errors by calling mic_map_error(..). | ||
| 204 | * | ||
| 205 | * returns DMA address as required by MIC. | ||
| 206 | */ | ||
| 207 | dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size) | ||
| 208 | { | ||
| 209 | dma_addr_t mic_addr = 0; | ||
| 210 | int num_entries; | ||
| 211 | s64 *ref; | ||
| 212 | u64 smpt_start; | ||
| 213 | |||
| 214 | if (!size || size > mic_max_system_memory(mdev)) | ||
| 215 | return mic_addr; | ||
| 216 | |||
| 217 | ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); | ||
| 218 | if (!ref) | ||
| 219 | return mic_addr; | ||
| 220 | |||
| 221 | num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size, | ||
| 222 | ref, &smpt_start); | ||
| 223 | |||
| 224 | /* Set the smpt table appropriately and get 16G aligned mic address */ | ||
| 225 | mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size); | ||
| 226 | |||
| 227 | kfree(ref); | ||
| 228 | |||
| 229 | /* | ||
| 230 | * If mic_addr is zero then its an error case | ||
| 231 | * since mic_addr can never be zero. | ||
| 232 | * else generate mic_addr by adding the 16G offset in dma_addr | ||
| 233 | */ | ||
| 234 | if (!mic_addr && MIC_FAMILY_X100 == mdev->family) { | ||
| 235 | dev_err(mdev->sdev->parent, | ||
| 236 | "mic_map failed dma_addr 0x%llx size 0x%lx\n", | ||
| 237 | dma_addr, size); | ||
| 238 | return mic_addr; | ||
| 239 | } else { | ||
| 240 | return mic_addr + mic_smpt_offset(mdev, dma_addr); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | /** | ||
| 245 | * mic_unmap - Unmaps a MIC physical address. | ||
| 246 | * | ||
| 247 | * @mdev: pointer to mic_device instance. | ||
| 248 | * @mic_addr: MIC physical address. | ||
| 249 | * @size: Size of the region to be unmapped. | ||
| 250 | * | ||
| 251 | * This API unmaps the mappings created by mic_map(..). | ||
| 252 | * | ||
| 253 | * returns None. | ||
| 254 | */ | ||
| 255 | void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) | ||
| 256 | { | ||
| 257 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
| 258 | s64 *ref; | ||
| 259 | int num_smpt; | ||
| 260 | int spt; | ||
| 261 | int i; | ||
| 262 | unsigned long flags; | ||
| 263 | |||
| 264 | if (!size) | ||
| 265 | return; | ||
| 266 | |||
| 267 | if (!mic_is_system_addr(mdev, mic_addr)) { | ||
| 268 | dev_err(mdev->sdev->parent, | ||
| 269 | "invalid address: 0x%llx\n", mic_addr); | ||
| 270 | return; | ||
| 271 | } | ||
| 272 | |||
| 273 | spt = mic_sys_addr_to_smpt(mdev, mic_addr); | ||
| 274 | ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); | ||
| 275 | if (!ref) | ||
| 276 | return; | ||
| 277 | |||
| 278 | /* Get number of smpt entries to be mapped, ref count array */ | ||
| 279 | num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL); | ||
| 280 | |||
| 281 | spin_lock_irqsave(&smpt_info->smpt_lock, flags); | ||
| 282 | smpt_info->unmap_count++; | ||
| 283 | smpt_info->ref_count -= (s64)size; | ||
| 284 | |||
| 285 | for (i = spt; i < spt + num_smpt; i++) { | ||
| 286 | smpt_info->entry[i].ref_count -= ref[i - spt]; | ||
| 287 | if (smpt_info->entry[i].ref_count < 0) | ||
| 288 | dev_warn(mdev->sdev->parent, | ||
| 289 | "ref count for entry %d is negative\n", i); | ||
| 290 | } | ||
| 291 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
| 292 | kfree(ref); | ||
| 293 | } | ||
| 294 | |||
| 295 | /** | ||
| 296 | * mic_map_single - Maps a virtual address to a MIC physical address. | ||
| 297 | * | ||
| 298 | * @mdev: pointer to mic_device instance. | ||
| 299 | * @va: Kernel direct mapped virtual address. | ||
| 300 | * @size: Size of the region to be mapped. | ||
| 301 | * | ||
| 302 | * This API calls pci_map_single(..) for the direct mapped virtual address | ||
| 303 | * and then converts the DMA address provided to a DMA address understood | ||
| 304 | * by MIC. Caller should check for errors by calling mic_map_error(..). | ||
| 305 | * | ||
| 306 | * returns DMA address as required by MIC. | ||
| 307 | */ | ||
| 308 | dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) | ||
| 309 | { | ||
| 310 | dma_addr_t mic_addr = 0; | ||
| 311 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 312 | struct pci_dev, dev); | ||
| 313 | dma_addr_t dma_addr = | ||
| 314 | pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL); | ||
| 315 | |||
| 316 | if (!pci_dma_mapping_error(pdev, dma_addr)) { | ||
| 317 | mic_addr = mic_map(mdev, dma_addr, size); | ||
| 318 | if (!mic_addr) { | ||
| 319 | dev_err(mdev->sdev->parent, | ||
| 320 | "mic_map failed dma_addr 0x%llx size 0x%lx\n", | ||
| 321 | dma_addr, size); | ||
| 322 | pci_unmap_single(pdev, dma_addr, | ||
| 323 | size, PCI_DMA_BIDIRECTIONAL); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | return mic_addr; | ||
| 327 | } | ||
| 328 | |||
| 329 | /** | ||
| 330 | * mic_unmap_single - Unmaps a MIC physical address. | ||
| 331 | * | ||
| 332 | * @mdev: pointer to mic_device instance. | ||
| 333 | * @mic_addr: MIC physical address. | ||
| 334 | * @size: Size of the region to be unmapped. | ||
| 335 | * | ||
| 336 | * This API unmaps the mappings created by mic_map_single(..). | ||
| 337 | * | ||
| 338 | * returns None. | ||
| 339 | */ | ||
| 340 | void | ||
| 341 | mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) | ||
| 342 | { | ||
| 343 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 344 | struct pci_dev, dev); | ||
| 345 | dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr); | ||
| 346 | mic_unmap(mdev, mic_addr, size); | ||
| 347 | pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); | ||
| 348 | } | ||
| 349 | |||
| 350 | /** | ||
| 351 | * mic_smpt_init - Initialize MIC System Memory Page Tables. | ||
| 352 | * | ||
| 353 | * @mdev: pointer to mic_device instance. | ||
| 354 | * | ||
| 355 | * returns 0 for success and -errno for error. | ||
| 356 | */ | ||
| 357 | int mic_smpt_init(struct mic_device *mdev) | ||
| 358 | { | ||
| 359 | int i, err = 0; | ||
| 360 | dma_addr_t dma_addr; | ||
| 361 | struct mic_smpt_info *smpt_info; | ||
| 362 | |||
| 363 | mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL); | ||
| 364 | if (!mdev->smpt) | ||
| 365 | return -ENOMEM; | ||
| 366 | |||
| 367 | smpt_info = mdev->smpt; | ||
| 368 | mdev->smpt_ops->init(mdev); | ||
| 369 | smpt_info->entry = kmalloc_array(smpt_info->info.num_reg, | ||
| 370 | sizeof(*smpt_info->entry), GFP_KERNEL); | ||
| 371 | if (!smpt_info->entry) { | ||
| 372 | err = -ENOMEM; | ||
| 373 | goto free_smpt; | ||
| 374 | } | ||
| 375 | spin_lock_init(&smpt_info->smpt_lock); | ||
| 376 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
| 377 | dma_addr = i * smpt_info->info.page_size; | ||
| 378 | smpt_info->entry[i].dma_addr = dma_addr; | ||
| 379 | smpt_info->entry[i].ref_count = 0; | ||
| 380 | mdev->smpt_ops->set(mdev, dma_addr, i); | ||
| 381 | } | ||
| 382 | smpt_info->ref_count = 0; | ||
| 383 | smpt_info->map_count = 0; | ||
| 384 | smpt_info->unmap_count = 0; | ||
| 385 | return 0; | ||
| 386 | free_smpt: | ||
| 387 | kfree(smpt_info); | ||
| 388 | return err; | ||
| 389 | } | ||
| 390 | |||
| 391 | /** | ||
| 392 | * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables. | ||
| 393 | * | ||
| 394 | * @mdev: pointer to mic_device instance. | ||
| 395 | * | ||
| 396 | * returns None. | ||
| 397 | */ | ||
| 398 | void mic_smpt_uninit(struct mic_device *mdev) | ||
| 399 | { | ||
| 400 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
| 401 | int i; | ||
| 402 | |||
| 403 | dev_dbg(mdev->sdev->parent, | ||
| 404 | "nodeid %d SMPT ref count %lld map %lld unmap %lld\n", | ||
| 405 | mdev->id, smpt_info->ref_count, | ||
| 406 | smpt_info->map_count, smpt_info->unmap_count); | ||
| 407 | |||
| 408 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
| 409 | dev_dbg(mdev->sdev->parent, | ||
| 410 | "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n", | ||
| 411 | i, smpt_info->entry[i].dma_addr, | ||
| 412 | smpt_info->entry[i].ref_count); | ||
| 413 | if (smpt_info->entry[i].ref_count) | ||
| 414 | dev_warn(mdev->sdev->parent, | ||
| 415 | "ref count for entry %d is not zero\n", i); | ||
| 416 | } | ||
| 417 | kfree(smpt_info->entry); | ||
| 418 | kfree(smpt_info); | ||
| 419 | } | ||
| 420 | |||
| 421 | /** | ||
| 422 | * mic_smpt_restore - Restore MIC System Memory Page Tables. | ||
| 423 | * | ||
| 424 | * @mdev: pointer to mic_device instance. | ||
| 425 | * | ||
| 426 | * Restore the SMPT registers to values previously stored in the | ||
| 427 | * SW data structures. Some MIC steppings lose register state | ||
| 428 | * across resets and this API should be called for performing | ||
| 429 | * a restore operation if required. | ||
| 430 | * | ||
| 431 | * returns None. | ||
| 432 | */ | ||
| 433 | void mic_smpt_restore(struct mic_device *mdev) | ||
| 434 | { | ||
| 435 | int i; | ||
| 436 | dma_addr_t dma_addr; | ||
| 437 | |||
| 438 | for (i = 0; i < mdev->smpt->info.num_reg; i++) { | ||
| 439 | dma_addr = mdev->smpt->entry[i].dma_addr; | ||
| 440 | mdev->smpt_ops->set(mdev, dma_addr, i); | ||
| 441 | } | ||
| 442 | } | ||
diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h new file mode 100644 index 000000000000..51970abfe7df --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.h | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef MIC_SMPT_H | ||
| 22 | #define MIC_SMPT_H | ||
| 23 | /** | ||
| 24 | * struct mic_smpt_ops - MIC HW specific SMPT operations. | ||
| 25 | * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info. | ||
| 26 | * @set: Set the value for a particular SMPT entry. | ||
| 27 | */ | ||
| 28 | struct mic_smpt_ops { | ||
| 29 | void (*init)(struct mic_device *mdev); | ||
| 30 | void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index); | ||
| 31 | }; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * struct mic_smpt - MIC SMPT entry information. | ||
| 35 | * @dma_addr: Base DMA address for this SMPT entry. | ||
| 36 | * @ref_count: Number of active mappings for this SMPT entry in bytes. | ||
| 37 | */ | ||
| 38 | struct mic_smpt { | ||
| 39 | dma_addr_t dma_addr; | ||
| 40 | s64 ref_count; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * struct mic_smpt_hw_info - MIC SMPT hardware specific information. | ||
| 45 | * @num_reg: Number of SMPT registers. | ||
| 46 | * @page_shift: System memory page shift. | ||
| 47 | * @page_size: System memory page size. | ||
| 48 | * @base: System address base. | ||
| 49 | */ | ||
| 50 | struct mic_smpt_hw_info { | ||
| 51 | u8 num_reg; | ||
| 52 | u8 page_shift; | ||
| 53 | u64 page_size; | ||
| 54 | u64 base; | ||
| 55 | }; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * struct mic_smpt_info - MIC SMPT information. | ||
| 59 | * @entry: Array of SMPT entries. | ||
| 60 | * @smpt_lock: Spin lock protecting access to SMPT data structures. | ||
| 61 | * @info: Hardware specific SMPT information. | ||
| 62 | * @ref_count: Number of active SMPT mappings (for debug). | ||
| 63 | * @map_count: Number of SMPT mappings created (for debug). | ||
| 64 | * @unmap_count: Number of SMPT mappings destroyed (for debug). | ||
| 65 | */ | ||
| 66 | struct mic_smpt_info { | ||
| 67 | struct mic_smpt *entry; | ||
| 68 | spinlock_t smpt_lock; | ||
| 69 | struct mic_smpt_hw_info info; | ||
| 70 | s64 ref_count; | ||
| 71 | s64 map_count; | ||
| 72 | s64 unmap_count; | ||
| 73 | }; | ||
| 74 | |||
| 75 | dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size); | ||
| 76 | void mic_unmap_single(struct mic_device *mdev, | ||
| 77 | dma_addr_t mic_addr, size_t size); | ||
| 78 | dma_addr_t mic_map(struct mic_device *mdev, | ||
| 79 | dma_addr_t dma_addr, size_t size); | ||
| 80 | void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size); | ||
| 81 | |||
| 82 | /** | ||
| 83 | * mic_map_error - Check a MIC address for errors. | ||
| 84 | * | ||
| 85 | * @mdev: pointer to mic_device instance. | ||
| 86 | * | ||
| 87 | * returns Whether there was an error during mic_map..(..) APIs. | ||
| 88 | */ | ||
| 89 | static inline bool mic_map_error(dma_addr_t mic_addr) | ||
| 90 | { | ||
| 91 | return !mic_addr; | ||
| 92 | } | ||
| 93 | |||
| 94 | int mic_smpt_init(struct mic_device *mdev); | ||
| 95 | void mic_smpt_uninit(struct mic_device *mdev); | ||
| 96 | void mic_smpt_restore(struct mic_device *mdev); | ||
| 97 | |||
| 98 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c new file mode 100644 index 000000000000..6dd864e4a617 --- /dev/null +++ b/drivers/misc/mic/host/mic_sysfs.c | |||
| @@ -0,0 +1,459 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/pci.h> | ||
| 22 | |||
| 23 | #include <linux/mic_common.h> | ||
| 24 | #include "../common/mic_dev.h" | ||
| 25 | #include "mic_device.h" | ||
| 26 | |||
| 27 | /* | ||
| 28 | * A state-to-string lookup table, for exposing a human readable state | ||
| 29 | * via sysfs. Always keep in sync with enum mic_states | ||
| 30 | */ | ||
| 31 | static const char * const mic_state_string[] = { | ||
| 32 | [MIC_OFFLINE] = "offline", | ||
| 33 | [MIC_ONLINE] = "online", | ||
| 34 | [MIC_SHUTTING_DOWN] = "shutting_down", | ||
| 35 | [MIC_RESET_FAILED] = "reset_failed", | ||
| 36 | [MIC_SUSPENDING] = "suspending", | ||
| 37 | [MIC_SUSPENDED] = "suspended", | ||
| 38 | }; | ||
| 39 | |||
| 40 | /* | ||
| 41 | * A shutdown-status-to-string lookup table, for exposing a human | ||
| 42 | * readable state via sysfs. Always keep in sync with enum mic_shutdown_status | ||
| 43 | */ | ||
| 44 | static const char * const mic_shutdown_status_string[] = { | ||
| 45 | [MIC_NOP] = "nop", | ||
| 46 | [MIC_CRASHED] = "crashed", | ||
| 47 | [MIC_HALTED] = "halted", | ||
| 48 | [MIC_POWER_OFF] = "poweroff", | ||
| 49 | [MIC_RESTART] = "restart", | ||
| 50 | }; | ||
| 51 | |||
| 52 | void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status) | ||
| 53 | { | ||
| 54 | dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n", | ||
| 55 | mic_shutdown_status_string[mdev->shutdown_status], | ||
| 56 | mic_shutdown_status_string[shutdown_status]); | ||
| 57 | mdev->shutdown_status = shutdown_status; | ||
| 58 | } | ||
| 59 | |||
| 60 | void mic_set_state(struct mic_device *mdev, u8 state) | ||
| 61 | { | ||
| 62 | dev_dbg(mdev->sdev->parent, "State %s -> %s\n", | ||
| 63 | mic_state_string[mdev->state], | ||
| 64 | mic_state_string[state]); | ||
| 65 | mdev->state = state; | ||
| 66 | sysfs_notify_dirent(mdev->state_sysfs); | ||
| 67 | } | ||
| 68 | |||
| 69 | static ssize_t | ||
| 70 | family_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 71 | { | ||
| 72 | static const char x100[] = "x100"; | ||
| 73 | static const char unknown[] = "Unknown"; | ||
| 74 | const char *card = NULL; | ||
| 75 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 76 | |||
| 77 | if (!mdev) | ||
| 78 | return -EINVAL; | ||
| 79 | |||
| 80 | switch (mdev->family) { | ||
| 81 | case MIC_FAMILY_X100: | ||
| 82 | card = x100; | ||
| 83 | break; | ||
| 84 | default: | ||
| 85 | card = unknown; | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | return scnprintf(buf, PAGE_SIZE, "%s\n", card); | ||
| 89 | } | ||
| 90 | static DEVICE_ATTR_RO(family); | ||
| 91 | |||
| 92 | static ssize_t | ||
| 93 | stepping_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 94 | { | ||
| 95 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 96 | char *string = "??"; | ||
| 97 | |||
| 98 | if (!mdev) | ||
| 99 | return -EINVAL; | ||
| 100 | |||
| 101 | switch (mdev->stepping) { | ||
| 102 | case MIC_A0_STEP: | ||
| 103 | string = "A0"; | ||
| 104 | break; | ||
| 105 | case MIC_B0_STEP: | ||
| 106 | string = "B0"; | ||
| 107 | break; | ||
| 108 | case MIC_B1_STEP: | ||
| 109 | string = "B1"; | ||
| 110 | break; | ||
| 111 | case MIC_C0_STEP: | ||
| 112 | string = "C0"; | ||
| 113 | break; | ||
| 114 | default: | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | return scnprintf(buf, PAGE_SIZE, "%s\n", string); | ||
| 118 | } | ||
| 119 | static DEVICE_ATTR_RO(stepping); | ||
| 120 | |||
| 121 | static ssize_t | ||
| 122 | state_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 123 | { | ||
| 124 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 125 | |||
| 126 | if (!mdev || mdev->state >= MIC_LAST) | ||
| 127 | return -EINVAL; | ||
| 128 | |||
| 129 | return scnprintf(buf, PAGE_SIZE, "%s\n", | ||
| 130 | mic_state_string[mdev->state]); | ||
| 131 | } | ||
| 132 | |||
| 133 | static ssize_t | ||
| 134 | state_store(struct device *dev, struct device_attribute *attr, | ||
| 135 | const char *buf, size_t count) | ||
| 136 | { | ||
| 137 | int rc = 0; | ||
| 138 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 139 | if (!mdev) | ||
| 140 | return -EINVAL; | ||
| 141 | if (sysfs_streq(buf, "boot")) { | ||
| 142 | rc = mic_start(mdev, buf); | ||
| 143 | if (rc) { | ||
| 144 | dev_err(mdev->sdev->parent, | ||
| 145 | "mic_boot failed rc %d\n", rc); | ||
| 146 | count = rc; | ||
| 147 | } | ||
| 148 | goto done; | ||
| 149 | } | ||
| 150 | |||
| 151 | if (sysfs_streq(buf, "reset")) { | ||
| 152 | schedule_work(&mdev->reset_trigger_work); | ||
| 153 | goto done; | ||
| 154 | } | ||
| 155 | |||
| 156 | if (sysfs_streq(buf, "shutdown")) { | ||
| 157 | mic_shutdown(mdev); | ||
| 158 | goto done; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (sysfs_streq(buf, "suspend")) { | ||
| 162 | mic_suspend(mdev); | ||
| 163 | goto done; | ||
| 164 | } | ||
| 165 | |||
| 166 | count = -EINVAL; | ||
| 167 | done: | ||
| 168 | return count; | ||
| 169 | } | ||
| 170 | static DEVICE_ATTR_RW(state); | ||
| 171 | |||
| 172 | static ssize_t shutdown_status_show(struct device *dev, | ||
| 173 | struct device_attribute *attr, char *buf) | ||
| 174 | { | ||
| 175 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 176 | |||
| 177 | if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST) | ||
| 178 | return -EINVAL; | ||
| 179 | |||
| 180 | return scnprintf(buf, PAGE_SIZE, "%s\n", | ||
| 181 | mic_shutdown_status_string[mdev->shutdown_status]); | ||
| 182 | } | ||
| 183 | static DEVICE_ATTR_RO(shutdown_status); | ||
| 184 | |||
| 185 | static ssize_t | ||
| 186 | cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 187 | { | ||
| 188 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 189 | char *cmdline; | ||
| 190 | |||
| 191 | if (!mdev) | ||
| 192 | return -EINVAL; | ||
| 193 | |||
| 194 | cmdline = mdev->cmdline; | ||
| 195 | |||
| 196 | if (cmdline) | ||
| 197 | return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static ssize_t | ||
| 202 | cmdline_store(struct device *dev, struct device_attribute *attr, | ||
| 203 | const char *buf, size_t count) | ||
| 204 | { | ||
| 205 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 206 | |||
| 207 | if (!mdev) | ||
| 208 | return -EINVAL; | ||
| 209 | |||
| 210 | mutex_lock(&mdev->mic_mutex); | ||
| 211 | kfree(mdev->cmdline); | ||
| 212 | |||
| 213 | mdev->cmdline = kmalloc(count + 1, GFP_KERNEL); | ||
| 214 | if (!mdev->cmdline) { | ||
| 215 | count = -ENOMEM; | ||
| 216 | goto unlock; | ||
| 217 | } | ||
| 218 | |||
| 219 | strncpy(mdev->cmdline, buf, count); | ||
| 220 | |||
| 221 | if (mdev->cmdline[count - 1] == '\n') | ||
| 222 | mdev->cmdline[count - 1] = '\0'; | ||
| 223 | else | ||
| 224 | mdev->cmdline[count] = '\0'; | ||
| 225 | unlock: | ||
| 226 | mutex_unlock(&mdev->mic_mutex); | ||
| 227 | return count; | ||
| 228 | } | ||
| 229 | static DEVICE_ATTR_RW(cmdline); | ||
| 230 | |||
| 231 | static ssize_t | ||
| 232 | firmware_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 233 | { | ||
| 234 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 235 | char *firmware; | ||
| 236 | |||
| 237 | if (!mdev) | ||
| 238 | return -EINVAL; | ||
| 239 | |||
| 240 | firmware = mdev->firmware; | ||
| 241 | |||
| 242 | if (firmware) | ||
| 243 | return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); | ||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | |||
| 247 | static ssize_t | ||
| 248 | firmware_store(struct device *dev, struct device_attribute *attr, | ||
| 249 | const char *buf, size_t count) | ||
| 250 | { | ||
| 251 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 252 | |||
| 253 | if (!mdev) | ||
| 254 | return -EINVAL; | ||
| 255 | |||
| 256 | mutex_lock(&mdev->mic_mutex); | ||
| 257 | kfree(mdev->firmware); | ||
| 258 | |||
| 259 | mdev->firmware = kmalloc(count + 1, GFP_KERNEL); | ||
| 260 | if (!mdev->firmware) { | ||
| 261 | count = -ENOMEM; | ||
| 262 | goto unlock; | ||
| 263 | } | ||
| 264 | strncpy(mdev->firmware, buf, count); | ||
| 265 | |||
| 266 | if (mdev->firmware[count - 1] == '\n') | ||
| 267 | mdev->firmware[count - 1] = '\0'; | ||
| 268 | else | ||
| 269 | mdev->firmware[count] = '\0'; | ||
| 270 | unlock: | ||
| 271 | mutex_unlock(&mdev->mic_mutex); | ||
| 272 | return count; | ||
| 273 | } | ||
| 274 | static DEVICE_ATTR_RW(firmware); | ||
| 275 | |||
| 276 | static ssize_t | ||
| 277 | ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 278 | { | ||
| 279 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 280 | char *ramdisk; | ||
| 281 | |||
| 282 | if (!mdev) | ||
| 283 | return -EINVAL; | ||
| 284 | |||
| 285 | ramdisk = mdev->ramdisk; | ||
| 286 | |||
| 287 | if (ramdisk) | ||
| 288 | return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); | ||
| 289 | return 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | static ssize_t | ||
| 293 | ramdisk_store(struct device *dev, struct device_attribute *attr, | ||
| 294 | const char *buf, size_t count) | ||
| 295 | { | ||
| 296 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 297 | |||
| 298 | if (!mdev) | ||
| 299 | return -EINVAL; | ||
| 300 | |||
| 301 | mutex_lock(&mdev->mic_mutex); | ||
| 302 | kfree(mdev->ramdisk); | ||
| 303 | |||
| 304 | mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); | ||
| 305 | if (!mdev->ramdisk) { | ||
| 306 | count = -ENOMEM; | ||
| 307 | goto unlock; | ||
| 308 | } | ||
| 309 | |||
| 310 | strncpy(mdev->ramdisk, buf, count); | ||
| 311 | |||
| 312 | if (mdev->ramdisk[count - 1] == '\n') | ||
| 313 | mdev->ramdisk[count - 1] = '\0'; | ||
| 314 | else | ||
| 315 | mdev->ramdisk[count] = '\0'; | ||
| 316 | unlock: | ||
| 317 | mutex_unlock(&mdev->mic_mutex); | ||
| 318 | return count; | ||
| 319 | } | ||
| 320 | static DEVICE_ATTR_RW(ramdisk); | ||
| 321 | |||
| 322 | static ssize_t | ||
| 323 | bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 324 | { | ||
| 325 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 326 | char *bootmode; | ||
| 327 | |||
| 328 | if (!mdev) | ||
| 329 | return -EINVAL; | ||
| 330 | |||
| 331 | bootmode = mdev->bootmode; | ||
| 332 | |||
| 333 | if (bootmode) | ||
| 334 | return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); | ||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | static ssize_t | ||
| 339 | bootmode_store(struct device *dev, struct device_attribute *attr, | ||
| 340 | const char *buf, size_t count) | ||
| 341 | { | ||
| 342 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 343 | |||
| 344 | if (!mdev) | ||
| 345 | return -EINVAL; | ||
| 346 | |||
| 347 | if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf")) | ||
| 348 | return -EINVAL; | ||
| 349 | |||
| 350 | mutex_lock(&mdev->mic_mutex); | ||
| 351 | kfree(mdev->bootmode); | ||
| 352 | |||
| 353 | mdev->bootmode = kmalloc(count + 1, GFP_KERNEL); | ||
| 354 | if (!mdev->bootmode) { | ||
| 355 | count = -ENOMEM; | ||
| 356 | goto unlock; | ||
| 357 | } | ||
| 358 | |||
| 359 | strncpy(mdev->bootmode, buf, count); | ||
| 360 | |||
| 361 | if (mdev->bootmode[count - 1] == '\n') | ||
| 362 | mdev->bootmode[count - 1] = '\0'; | ||
| 363 | else | ||
| 364 | mdev->bootmode[count] = '\0'; | ||
| 365 | unlock: | ||
| 366 | mutex_unlock(&mdev->mic_mutex); | ||
| 367 | return count; | ||
| 368 | } | ||
| 369 | static DEVICE_ATTR_RW(bootmode); | ||
| 370 | |||
| 371 | static ssize_t | ||
| 372 | log_buf_addr_show(struct device *dev, struct device_attribute *attr, | ||
| 373 | char *buf) | ||
| 374 | { | ||
| 375 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 376 | |||
| 377 | if (!mdev) | ||
| 378 | return -EINVAL; | ||
| 379 | |||
| 380 | return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr); | ||
| 381 | } | ||
| 382 | |||
| 383 | static ssize_t | ||
| 384 | log_buf_addr_store(struct device *dev, struct device_attribute *attr, | ||
| 385 | const char *buf, size_t count) | ||
| 386 | { | ||
| 387 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 388 | int ret; | ||
| 389 | unsigned long addr; | ||
| 390 | |||
| 391 | if (!mdev) | ||
| 392 | return -EINVAL; | ||
| 393 | |||
| 394 | ret = kstrtoul(buf, 16, &addr); | ||
| 395 | if (ret) | ||
| 396 | goto exit; | ||
| 397 | |||
| 398 | mdev->log_buf_addr = (void *)addr; | ||
| 399 | ret = count; | ||
| 400 | exit: | ||
| 401 | return ret; | ||
| 402 | } | ||
| 403 | static DEVICE_ATTR_RW(log_buf_addr); | ||
| 404 | |||
| 405 | static ssize_t | ||
| 406 | log_buf_len_show(struct device *dev, struct device_attribute *attr, | ||
| 407 | char *buf) | ||
| 408 | { | ||
| 409 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 410 | |||
| 411 | if (!mdev) | ||
| 412 | return -EINVAL; | ||
| 413 | |||
| 414 | return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len); | ||
| 415 | } | ||
| 416 | |||
| 417 | static ssize_t | ||
| 418 | log_buf_len_store(struct device *dev, struct device_attribute *attr, | ||
| 419 | const char *buf, size_t count) | ||
| 420 | { | ||
| 421 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 422 | int ret; | ||
| 423 | unsigned long addr; | ||
| 424 | |||
| 425 | if (!mdev) | ||
| 426 | return -EINVAL; | ||
| 427 | |||
| 428 | ret = kstrtoul(buf, 16, &addr); | ||
| 429 | if (ret) | ||
| 430 | goto exit; | ||
| 431 | |||
| 432 | mdev->log_buf_len = (int *)addr; | ||
| 433 | ret = count; | ||
| 434 | exit: | ||
| 435 | return ret; | ||
| 436 | } | ||
| 437 | static DEVICE_ATTR_RW(log_buf_len); | ||
| 438 | |||
| 439 | static struct attribute *mic_default_attrs[] = { | ||
| 440 | &dev_attr_family.attr, | ||
| 441 | &dev_attr_stepping.attr, | ||
| 442 | &dev_attr_state.attr, | ||
| 443 | &dev_attr_shutdown_status.attr, | ||
| 444 | &dev_attr_cmdline.attr, | ||
| 445 | &dev_attr_firmware.attr, | ||
| 446 | &dev_attr_ramdisk.attr, | ||
| 447 | &dev_attr_bootmode.attr, | ||
| 448 | &dev_attr_log_buf_addr.attr, | ||
| 449 | &dev_attr_log_buf_len.attr, | ||
| 450 | |||
| 451 | NULL | ||
| 452 | }; | ||
| 453 | |||
| 454 | ATTRIBUTE_GROUPS(mic_default); | ||
| 455 | |||
| 456 | void mic_sysfs_init(struct mic_device *mdev) | ||
| 457 | { | ||
| 458 | mdev->attr_group = mic_default_groups; | ||
| 459 | } | ||
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c new file mode 100644 index 000000000000..5b8494bd1e00 --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.c | |||
| @@ -0,0 +1,700 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/pci.h> | ||
| 22 | #include <linux/sched.h> | ||
| 23 | #include <linux/uaccess.h> | ||
| 24 | |||
| 25 | #include <linux/mic_common.h> | ||
| 26 | #include "../common/mic_dev.h" | ||
| 27 | #include "mic_device.h" | ||
| 28 | #include "mic_smpt.h" | ||
| 29 | #include "mic_virtio.h" | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Initiates the copies across the PCIe bus from card memory to | ||
| 33 | * a user space buffer. | ||
| 34 | */ | ||
| 35 | static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, | ||
| 36 | void __user *ubuf, size_t len, u64 addr) | ||
| 37 | { | ||
| 38 | int err; | ||
| 39 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | ||
| 40 | /* | ||
| 41 | * We are copying from IO below an should ideally use something | ||
| 42 | * like copy_to_user_fromio(..) if it existed. | ||
| 43 | */ | ||
| 44 | if (copy_to_user(ubuf, dbuf, len)) { | ||
| 45 | err = -EFAULT; | ||
| 46 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 47 | __func__, __LINE__, err); | ||
| 48 | goto err; | ||
| 49 | } | ||
| 50 | mvdev->in_bytes += len; | ||
| 51 | err = 0; | ||
| 52 | err: | ||
| 53 | return err; | ||
| 54 | } | ||
| 55 | |||
| 56 | /* | ||
| 57 | * Initiates copies across the PCIe bus from a user space | ||
| 58 | * buffer to card memory. | ||
| 59 | */ | ||
| 60 | static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, | ||
| 61 | void __user *ubuf, size_t len, u64 addr) | ||
| 62 | { | ||
| 63 | int err; | ||
| 64 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | ||
| 65 | /* | ||
| 66 | * We are copying to IO below and should ideally use something | ||
| 67 | * like copy_from_user_toio(..) if it existed. | ||
| 68 | */ | ||
| 69 | if (copy_from_user(dbuf, ubuf, len)) { | ||
| 70 | err = -EFAULT; | ||
| 71 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 72 | __func__, __LINE__, err); | ||
| 73 | goto err; | ||
| 74 | } | ||
| 75 | mvdev->out_bytes += len; | ||
| 76 | err = 0; | ||
| 77 | err: | ||
| 78 | return err; | ||
| 79 | } | ||
| 80 | |||
| 81 | #define MIC_VRINGH_READ true | ||
| 82 | |||
| 83 | /* The function to call to notify the card about added buffers */ | ||
| 84 | static void mic_notify(struct vringh *vrh) | ||
| 85 | { | ||
| 86 | struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh); | ||
| 87 | struct mic_vdev *mvdev = mvrh->mvdev; | ||
| 88 | s8 db = mvdev->dc->h2c_vdev_db; | ||
| 89 | |||
| 90 | if (db != -1) | ||
| 91 | mvdev->mdev->ops->send_intr(mvdev->mdev, db); | ||
| 92 | } | ||
| 93 | |||
| 94 | /* Determine the total number of bytes consumed in a VRINGH KIOV */ | ||
| 95 | static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) | ||
| 96 | { | ||
| 97 | int i; | ||
| 98 | u32 total = iov->consumed; | ||
| 99 | |||
| 100 | for (i = 0; i < iov->i; i++) | ||
| 101 | total += iov->iov[i].iov_len; | ||
| 102 | return total; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* | ||
| 106 | * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. | ||
| 107 | * This API is heavily based on the vringh_iov_xfer(..) implementation | ||
| 108 | * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) | ||
| 109 | * and vringh_iov_push_kern(..) directly is because there is no | ||
| 110 | * way to override the VRINGH xfer(..) routines as of v3.10. | ||
| 111 | */ | ||
| 112 | static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, | ||
| 113 | void __user *ubuf, size_t len, bool read, size_t *out_len) | ||
| 114 | { | ||
| 115 | int ret = 0; | ||
| 116 | size_t partlen, tot_len = 0; | ||
| 117 | |||
| 118 | while (len && iov->i < iov->used) { | ||
| 119 | partlen = min(iov->iov[iov->i].iov_len, len); | ||
| 120 | if (read) | ||
| 121 | ret = mic_virtio_copy_to_user(mvdev, | ||
| 122 | ubuf, partlen, | ||
| 123 | (u64)iov->iov[iov->i].iov_base); | ||
| 124 | else | ||
| 125 | ret = mic_virtio_copy_from_user(mvdev, | ||
| 126 | ubuf, partlen, | ||
| 127 | (u64)iov->iov[iov->i].iov_base); | ||
| 128 | if (ret) { | ||
| 129 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 130 | __func__, __LINE__, ret); | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | len -= partlen; | ||
| 134 | ubuf += partlen; | ||
| 135 | tot_len += partlen; | ||
| 136 | iov->consumed += partlen; | ||
| 137 | iov->iov[iov->i].iov_len -= partlen; | ||
| 138 | iov->iov[iov->i].iov_base += partlen; | ||
| 139 | if (!iov->iov[iov->i].iov_len) { | ||
| 140 | /* Fix up old iov element then increment. */ | ||
| 141 | iov->iov[iov->i].iov_len = iov->consumed; | ||
| 142 | iov->iov[iov->i].iov_base -= iov->consumed; | ||
| 143 | |||
| 144 | iov->consumed = 0; | ||
| 145 | iov->i++; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | *out_len = tot_len; | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Use the standard VRINGH infrastructure in the kernel to fetch new | ||
| 154 | * descriptors, initiate the copies and update the used ring. | ||
| 155 | */ | ||
| 156 | static int _mic_virtio_copy(struct mic_vdev *mvdev, | ||
| 157 | struct mic_copy_desc *copy) | ||
| 158 | { | ||
| 159 | int ret = 0, iovcnt = copy->iovcnt; | ||
| 160 | struct iovec iov; | ||
| 161 | struct iovec __user *u_iov = copy->iov; | ||
| 162 | void __user *ubuf = NULL; | ||
| 163 | struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; | ||
| 164 | struct vringh_kiov *riov = &mvr->riov; | ||
| 165 | struct vringh_kiov *wiov = &mvr->wiov; | ||
| 166 | struct vringh *vrh = &mvr->vrh; | ||
| 167 | u16 *head = &mvr->head; | ||
| 168 | struct mic_vring *vr = &mvr->vring; | ||
| 169 | size_t len = 0, out_len; | ||
| 170 | |||
| 171 | copy->out_len = 0; | ||
| 172 | /* Fetch a new IOVEC if all previous elements have been processed */ | ||
| 173 | if (riov->i == riov->used && wiov->i == wiov->used) { | ||
| 174 | ret = vringh_getdesc_kern(vrh, riov, wiov, | ||
| 175 | head, GFP_KERNEL); | ||
| 176 | /* Check if there are available descriptors */ | ||
| 177 | if (ret <= 0) | ||
| 178 | return ret; | ||
| 179 | } | ||
| 180 | while (iovcnt) { | ||
| 181 | if (!len) { | ||
| 182 | /* Copy over a new iovec from user space. */ | ||
| 183 | ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); | ||
| 184 | if (ret) { | ||
| 185 | ret = -EINVAL; | ||
| 186 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 187 | __func__, __LINE__, ret); | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | len = iov.iov_len; | ||
| 191 | ubuf = iov.iov_base; | ||
| 192 | } | ||
| 193 | /* Issue all the read descriptors first */ | ||
| 194 | ret = mic_vringh_copy(mvdev, riov, ubuf, len, | ||
| 195 | MIC_VRINGH_READ, &out_len); | ||
| 196 | if (ret) { | ||
| 197 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 198 | __func__, __LINE__, ret); | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | len -= out_len; | ||
| 202 | ubuf += out_len; | ||
| 203 | copy->out_len += out_len; | ||
| 204 | /* Issue the write descriptors next */ | ||
| 205 | ret = mic_vringh_copy(mvdev, wiov, ubuf, len, | ||
| 206 | !MIC_VRINGH_READ, &out_len); | ||
| 207 | if (ret) { | ||
| 208 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 209 | __func__, __LINE__, ret); | ||
| 210 | break; | ||
| 211 | } | ||
| 212 | len -= out_len; | ||
| 213 | ubuf += out_len; | ||
| 214 | copy->out_len += out_len; | ||
| 215 | if (!len) { | ||
| 216 | /* One user space iovec is now completed */ | ||
| 217 | iovcnt--; | ||
| 218 | u_iov++; | ||
| 219 | } | ||
| 220 | /* Exit loop if all elements in KIOVs have been processed. */ | ||
| 221 | if (riov->i == riov->used && wiov->i == wiov->used) | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | /* | ||
| 225 | * Update the used ring if a descriptor was available and some data was | ||
| 226 | * copied in/out and the user asked for a used ring update. | ||
| 227 | */ | ||
| 228 | if (*head != USHRT_MAX && copy->out_len && copy->update_used) { | ||
| 229 | u32 total = 0; | ||
| 230 | |||
| 231 | /* Determine the total data consumed */ | ||
| 232 | total += mic_vringh_iov_consumed(riov); | ||
| 233 | total += mic_vringh_iov_consumed(wiov); | ||
| 234 | vringh_complete_kern(vrh, *head, total); | ||
| 235 | *head = USHRT_MAX; | ||
| 236 | if (vringh_need_notify_kern(vrh) > 0) | ||
| 237 | vringh_notify(vrh); | ||
| 238 | vringh_kiov_cleanup(riov); | ||
| 239 | vringh_kiov_cleanup(wiov); | ||
| 240 | /* Update avail idx for user space */ | ||
| 241 | vr->info->avail_idx = vrh->last_avail_idx; | ||
| 242 | } | ||
| 243 | return ret; | ||
| 244 | } | ||
| 245 | |||
| 246 | static inline int mic_verify_copy_args(struct mic_vdev *mvdev, | ||
| 247 | struct mic_copy_desc *copy) | ||
| 248 | { | ||
| 249 | if (copy->vr_idx >= mvdev->dd->num_vq) { | ||
| 250 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 251 | __func__, __LINE__, -EINVAL); | ||
| 252 | return -EINVAL; | ||
| 253 | } | ||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* Copy a specified number of virtio descriptors in a chain */ | ||
| 258 | int mic_virtio_copy_desc(struct mic_vdev *mvdev, | ||
| 259 | struct mic_copy_desc *copy) | ||
| 260 | { | ||
| 261 | int err; | ||
| 262 | struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; | ||
| 263 | |||
| 264 | err = mic_verify_copy_args(mvdev, copy); | ||
| 265 | if (err) | ||
| 266 | return err; | ||
| 267 | |||
| 268 | mutex_lock(&mvr->vr_mutex); | ||
| 269 | if (!mic_vdevup(mvdev)) { | ||
| 270 | err = -ENODEV; | ||
| 271 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 272 | __func__, __LINE__, err); | ||
| 273 | goto err; | ||
| 274 | } | ||
| 275 | err = _mic_virtio_copy(mvdev, copy); | ||
| 276 | if (err) { | ||
| 277 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 278 | __func__, __LINE__, err); | ||
| 279 | } | ||
| 280 | err: | ||
| 281 | mutex_unlock(&mvr->vr_mutex); | ||
| 282 | return err; | ||
| 283 | } | ||
| 284 | |||
| 285 | static void mic_virtio_init_post(struct mic_vdev *mvdev) | ||
| 286 | { | ||
| 287 | struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd); | ||
| 288 | int i; | ||
| 289 | |||
| 290 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
| 291 | if (!le64_to_cpu(vqconfig[i].used_address)) { | ||
| 292 | dev_warn(mic_dev(mvdev), "used_address zero??\n"); | ||
| 293 | continue; | ||
| 294 | } | ||
| 295 | mvdev->mvr[i].vrh.vring.used = | ||
| 296 | mvdev->mdev->aper.va + | ||
| 297 | le64_to_cpu(vqconfig[i].used_address); | ||
| 298 | } | ||
| 299 | |||
| 300 | mvdev->dc->used_address_updated = 0; | ||
| 301 | |||
| 302 | dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n", | ||
| 303 | __func__, mvdev->virtio_id); | ||
| 304 | } | ||
| 305 | |||
| 306 | static inline void mic_virtio_device_reset(struct mic_vdev *mvdev) | ||
| 307 | { | ||
| 308 | int i; | ||
| 309 | |||
| 310 | dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n", | ||
| 311 | __func__, mvdev->dd->status, mvdev->virtio_id); | ||
| 312 | |||
| 313 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
| 314 | /* | ||
| 315 | * Avoid lockdep false positive. The + 1 is for the mic | ||
| 316 | * mutex which is held in the reset devices code path. | ||
| 317 | */ | ||
| 318 | mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); | ||
| 319 | |||
| 320 | /* 0 status means "reset" */ | ||
| 321 | mvdev->dd->status = 0; | ||
| 322 | mvdev->dc->vdev_reset = 0; | ||
| 323 | mvdev->dc->host_ack = 1; | ||
| 324 | |||
| 325 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
| 326 | struct vringh *vrh = &mvdev->mvr[i].vrh; | ||
| 327 | mvdev->mvr[i].vring.info->avail_idx = 0; | ||
| 328 | vrh->completed = 0; | ||
| 329 | vrh->last_avail_idx = 0; | ||
| 330 | vrh->last_used_idx = 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
| 334 | mutex_unlock(&mvdev->mvr[i].vr_mutex); | ||
| 335 | } | ||
| 336 | |||
| 337 | void mic_virtio_reset_devices(struct mic_device *mdev) | ||
| 338 | { | ||
| 339 | struct list_head *pos, *tmp; | ||
| 340 | struct mic_vdev *mvdev; | ||
| 341 | |||
| 342 | dev_dbg(mdev->sdev->parent, "%s\n", __func__); | ||
| 343 | |||
| 344 | list_for_each_safe(pos, tmp, &mdev->vdev_list) { | ||
| 345 | mvdev = list_entry(pos, struct mic_vdev, list); | ||
| 346 | mic_virtio_device_reset(mvdev); | ||
| 347 | mvdev->poll_wake = 1; | ||
| 348 | wake_up(&mvdev->waitq); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | void mic_bh_handler(struct work_struct *work) | ||
| 353 | { | ||
| 354 | struct mic_vdev *mvdev = container_of(work, struct mic_vdev, | ||
| 355 | virtio_bh_work); | ||
| 356 | |||
| 357 | if (mvdev->dc->used_address_updated) | ||
| 358 | mic_virtio_init_post(mvdev); | ||
| 359 | |||
| 360 | if (mvdev->dc->vdev_reset) | ||
| 361 | mic_virtio_device_reset(mvdev); | ||
| 362 | |||
| 363 | mvdev->poll_wake = 1; | ||
| 364 | wake_up(&mvdev->waitq); | ||
| 365 | } | ||
| 366 | |||
| 367 | static irqreturn_t mic_virtio_intr_handler(int irq, void *data) | ||
| 368 | { | ||
| 369 | struct mic_vdev *mvdev = data; | ||
| 370 | struct mic_device *mdev = mvdev->mdev; | ||
| 371 | |||
| 372 | mdev->ops->ack_interrupt(mdev); | ||
| 373 | schedule_work(&mvdev->virtio_bh_work); | ||
| 374 | return IRQ_HANDLED; | ||
| 375 | } | ||
| 376 | |||
| 377 | int mic_virtio_config_change(struct mic_vdev *mvdev, | ||
| 378 | void __user *argp) | ||
| 379 | { | ||
| 380 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); | ||
| 381 | int ret = 0, retry = 100, i; | ||
| 382 | struct mic_bootparam *bootparam = mvdev->mdev->dp; | ||
| 383 | s8 db = bootparam->h2c_config_db; | ||
| 384 | |||
| 385 | mutex_lock(&mvdev->mdev->mic_mutex); | ||
| 386 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
| 387 | mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); | ||
| 388 | |||
| 389 | if (db == -1 || mvdev->dd->type == -1) { | ||
| 390 | ret = -EIO; | ||
| 391 | goto exit; | ||
| 392 | } | ||
| 393 | |||
| 394 | if (copy_from_user(mic_vq_configspace(mvdev->dd), | ||
| 395 | argp, mvdev->dd->config_len)) { | ||
| 396 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 397 | __func__, __LINE__, -EFAULT); | ||
| 398 | ret = -EFAULT; | ||
| 399 | goto exit; | ||
| 400 | } | ||
| 401 | mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; | ||
| 402 | mvdev->mdev->ops->send_intr(mvdev->mdev, db); | ||
| 403 | |||
| 404 | for (i = retry; i--;) { | ||
| 405 | ret = wait_event_timeout(wake, | ||
| 406 | mvdev->dc->guest_ack, msecs_to_jiffies(100)); | ||
| 407 | if (ret) | ||
| 408 | break; | ||
| 409 | } | ||
| 410 | |||
| 411 | dev_dbg(mic_dev(mvdev), | ||
| 412 | "%s %d retry: %d\n", __func__, __LINE__, retry); | ||
| 413 | mvdev->dc->config_change = 0; | ||
| 414 | mvdev->dc->guest_ack = 0; | ||
| 415 | exit: | ||
| 416 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
| 417 | mutex_unlock(&mvdev->mvr[i].vr_mutex); | ||
| 418 | mutex_unlock(&mvdev->mdev->mic_mutex); | ||
| 419 | return ret; | ||
| 420 | } | ||
| 421 | |||
| 422 | static int mic_copy_dp_entry(struct mic_vdev *mvdev, | ||
| 423 | void __user *argp, | ||
| 424 | __u8 *type, | ||
| 425 | struct mic_device_desc **devpage) | ||
| 426 | { | ||
| 427 | struct mic_device *mdev = mvdev->mdev; | ||
| 428 | struct mic_device_desc dd, *dd_config, *devp; | ||
| 429 | struct mic_vqconfig *vqconfig; | ||
| 430 | int ret = 0, i; | ||
| 431 | bool slot_found = false; | ||
| 432 | |||
| 433 | if (copy_from_user(&dd, argp, sizeof(dd))) { | ||
| 434 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 435 | __func__, __LINE__, -EFAULT); | ||
| 436 | return -EFAULT; | ||
| 437 | } | ||
| 438 | |||
| 439 | if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || | ||
| 440 | dd.num_vq > MIC_MAX_VRINGS) { | ||
| 441 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 442 | __func__, __LINE__, -EINVAL); | ||
| 443 | return -EINVAL; | ||
| 444 | } | ||
| 445 | |||
| 446 | dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL); | ||
| 447 | if (dd_config == NULL) { | ||
| 448 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 449 | __func__, __LINE__, -ENOMEM); | ||
| 450 | return -ENOMEM; | ||
| 451 | } | ||
| 452 | if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { | ||
| 453 | ret = -EFAULT; | ||
| 454 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 455 | __func__, __LINE__, ret); | ||
| 456 | goto exit; | ||
| 457 | } | ||
| 458 | |||
| 459 | vqconfig = mic_vq_config(dd_config); | ||
| 460 | for (i = 0; i < dd.num_vq; i++) { | ||
| 461 | if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { | ||
| 462 | ret = -EINVAL; | ||
| 463 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 464 | __func__, __LINE__, ret); | ||
| 465 | goto exit; | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | /* Find the first free device page entry */ | ||
| 470 | for (i = mic_aligned_size(struct mic_bootparam); | ||
| 471 | i < MIC_DP_SIZE - mic_total_desc_size(dd_config); | ||
| 472 | i += mic_total_desc_size(devp)) { | ||
| 473 | devp = mdev->dp + i; | ||
| 474 | if (devp->type == 0 || devp->type == -1) { | ||
| 475 | slot_found = true; | ||
| 476 | break; | ||
| 477 | } | ||
| 478 | } | ||
| 479 | if (!slot_found) { | ||
| 480 | ret = -EINVAL; | ||
| 481 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 482 | __func__, __LINE__, ret); | ||
| 483 | goto exit; | ||
| 484 | } | ||
| 485 | /* | ||
| 486 | * Save off the type before doing the memcpy. Type will be set in the | ||
| 487 | * end after completing all initialization for the new device. | ||
| 488 | */ | ||
| 489 | *type = dd_config->type; | ||
| 490 | dd_config->type = 0; | ||
| 491 | memcpy(devp, dd_config, mic_desc_size(dd_config)); | ||
| 492 | |||
| 493 | *devpage = devp; | ||
| 494 | exit: | ||
| 495 | kfree(dd_config); | ||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | |||
| 499 | static void mic_init_device_ctrl(struct mic_vdev *mvdev, | ||
| 500 | struct mic_device_desc *devpage) | ||
| 501 | { | ||
| 502 | struct mic_device_ctrl *dc; | ||
| 503 | |||
| 504 | dc = (void *)devpage + mic_aligned_desc_size(devpage); | ||
| 505 | |||
| 506 | dc->config_change = 0; | ||
| 507 | dc->guest_ack = 0; | ||
| 508 | dc->vdev_reset = 0; | ||
| 509 | dc->host_ack = 0; | ||
| 510 | dc->used_address_updated = 0; | ||
| 511 | dc->c2h_vdev_db = -1; | ||
| 512 | dc->h2c_vdev_db = -1; | ||
| 513 | mvdev->dc = dc; | ||
| 514 | } | ||
| 515 | |||
| 516 | int mic_virtio_add_device(struct mic_vdev *mvdev, | ||
| 517 | void __user *argp) | ||
| 518 | { | ||
| 519 | struct mic_device *mdev = mvdev->mdev; | ||
| 520 | struct mic_device_desc *dd = NULL; | ||
| 521 | struct mic_vqconfig *vqconfig; | ||
| 522 | int vr_size, i, j, ret; | ||
| 523 | u8 type = 0; | ||
| 524 | s8 db; | ||
| 525 | char irqname[10]; | ||
| 526 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 527 | u16 num; | ||
| 528 | |||
| 529 | mutex_lock(&mdev->mic_mutex); | ||
| 530 | |||
| 531 | ret = mic_copy_dp_entry(mvdev, argp, &type, &dd); | ||
| 532 | if (ret) { | ||
| 533 | mutex_unlock(&mdev->mic_mutex); | ||
| 534 | return ret; | ||
| 535 | } | ||
| 536 | |||
| 537 | mic_init_device_ctrl(mvdev, dd); | ||
| 538 | |||
| 539 | mvdev->dd = dd; | ||
| 540 | mvdev->virtio_id = type; | ||
| 541 | vqconfig = mic_vq_config(dd); | ||
| 542 | INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler); | ||
| 543 | |||
| 544 | for (i = 0; i < dd->num_vq; i++) { | ||
| 545 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
| 546 | struct mic_vring *vr = &mvdev->mvr[i].vring; | ||
| 547 | num = le16_to_cpu(vqconfig[i].num); | ||
| 548 | mutex_init(&mvr->vr_mutex); | ||
| 549 | vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + | ||
| 550 | sizeof(struct _mic_vring_info)); | ||
| 551 | vr->va = (void *) | ||
| 552 | __get_free_pages(GFP_KERNEL | __GFP_ZERO, | ||
| 553 | get_order(vr_size)); | ||
| 554 | if (!vr->va) { | ||
| 555 | ret = -ENOMEM; | ||
| 556 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 557 | __func__, __LINE__, ret); | ||
| 558 | goto err; | ||
| 559 | } | ||
| 560 | vr->len = vr_size; | ||
| 561 | vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); | ||
| 562 | vr->info->magic = MIC_MAGIC + mvdev->virtio_id + i; | ||
| 563 | vqconfig[i].address = mic_map_single(mdev, | ||
| 564 | vr->va, vr_size); | ||
| 565 | if (mic_map_error(vqconfig[i].address)) { | ||
| 566 | free_pages((unsigned long)vr->va, get_order(vr_size)); | ||
| 567 | ret = -ENOMEM; | ||
| 568 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 569 | __func__, __LINE__, ret); | ||
| 570 | goto err; | ||
| 571 | } | ||
| 572 | vqconfig[i].address = cpu_to_le64(vqconfig[i].address); | ||
| 573 | |||
| 574 | vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); | ||
| 575 | ret = vringh_init_kern(&mvr->vrh, | ||
| 576 | *(u32 *)mic_vq_features(mvdev->dd), num, false, | ||
| 577 | vr->vr.desc, vr->vr.avail, vr->vr.used); | ||
| 578 | if (ret) { | ||
| 579 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 580 | __func__, __LINE__, ret); | ||
| 581 | goto err; | ||
| 582 | } | ||
| 583 | vringh_kiov_init(&mvr->riov, NULL, 0); | ||
| 584 | vringh_kiov_init(&mvr->wiov, NULL, 0); | ||
| 585 | mvr->head = USHRT_MAX; | ||
| 586 | mvr->mvdev = mvdev; | ||
| 587 | mvr->vrh.notify = mic_notify; | ||
| 588 | dev_dbg(mdev->sdev->parent, | ||
| 589 | "%s %d index %d va %p info %p vr_size 0x%x\n", | ||
| 590 | __func__, __LINE__, i, vr->va, vr->info, vr_size); | ||
| 591 | } | ||
| 592 | |||
| 593 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, | ||
| 594 | mvdev->virtio_id); | ||
| 595 | mvdev->virtio_db = mic_next_db(mdev); | ||
| 596 | mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, | ||
| 597 | irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); | ||
| 598 | if (IS_ERR(mvdev->virtio_cookie)) { | ||
| 599 | ret = PTR_ERR(mvdev->virtio_cookie); | ||
| 600 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); | ||
| 601 | goto err; | ||
| 602 | } | ||
| 603 | |||
| 604 | mvdev->dc->c2h_vdev_db = mvdev->virtio_db; | ||
| 605 | |||
| 606 | list_add_tail(&mvdev->list, &mdev->vdev_list); | ||
| 607 | /* | ||
| 608 | * Order the type update with previous stores. This write barrier | ||
| 609 | * is paired with the corresponding read barrier before the uncached | ||
| 610 | * system memory read of the type, on the card while scanning the | ||
| 611 | * device page. | ||
| 612 | */ | ||
| 613 | smp_wmb(); | ||
| 614 | dd->type = type; | ||
| 615 | |||
| 616 | dev_dbg(mdev->sdev->parent, "Added virtio device id %d\n", dd->type); | ||
| 617 | |||
| 618 | db = bootparam->h2c_config_db; | ||
| 619 | if (db != -1) | ||
| 620 | mdev->ops->send_intr(mdev, db); | ||
| 621 | mutex_unlock(&mdev->mic_mutex); | ||
| 622 | return 0; | ||
| 623 | err: | ||
| 624 | vqconfig = mic_vq_config(dd); | ||
| 625 | for (j = 0; j < i; j++) { | ||
| 626 | struct mic_vringh *mvr = &mvdev->mvr[j]; | ||
| 627 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), | ||
| 628 | mvr->vring.len); | ||
| 629 | free_pages((unsigned long)mvr->vring.va, | ||
| 630 | get_order(mvr->vring.len)); | ||
| 631 | } | ||
| 632 | mutex_unlock(&mdev->mic_mutex); | ||
| 633 | return ret; | ||
| 634 | } | ||
| 635 | |||
| 636 | void mic_virtio_del_device(struct mic_vdev *mvdev) | ||
| 637 | { | ||
| 638 | struct list_head *pos, *tmp; | ||
| 639 | struct mic_vdev *tmp_mvdev; | ||
| 640 | struct mic_device *mdev = mvdev->mdev; | ||
| 641 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); | ||
| 642 | int i, ret, retry = 100; | ||
| 643 | struct mic_vqconfig *vqconfig; | ||
| 644 | struct mic_bootparam *bootparam = mdev->dp; | ||
| 645 | s8 db; | ||
| 646 | |||
| 647 | mutex_lock(&mdev->mic_mutex); | ||
| 648 | db = bootparam->h2c_config_db; | ||
| 649 | if (db == -1) | ||
| 650 | goto skip_hot_remove; | ||
| 651 | dev_dbg(mdev->sdev->parent, | ||
| 652 | "Requesting hot remove id %d\n", mvdev->virtio_id); | ||
| 653 | mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; | ||
| 654 | mdev->ops->send_intr(mdev, db); | ||
| 655 | for (i = retry; i--;) { | ||
| 656 | ret = wait_event_timeout(wake, | ||
| 657 | mvdev->dc->guest_ack, msecs_to_jiffies(100)); | ||
| 658 | if (ret) | ||
| 659 | break; | ||
| 660 | } | ||
| 661 | dev_dbg(mdev->sdev->parent, | ||
| 662 | "Device id %d config_change %d guest_ack %d\n", | ||
| 663 | mvdev->virtio_id, mvdev->dc->config_change, | ||
| 664 | mvdev->dc->guest_ack); | ||
| 665 | mvdev->dc->config_change = 0; | ||
| 666 | mvdev->dc->guest_ack = 0; | ||
| 667 | skip_hot_remove: | ||
| 668 | mic_free_irq(mdev, mvdev->virtio_cookie, mvdev); | ||
| 669 | flush_work(&mvdev->virtio_bh_work); | ||
| 670 | vqconfig = mic_vq_config(mvdev->dd); | ||
| 671 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
| 672 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
| 673 | vringh_kiov_cleanup(&mvr->riov); | ||
| 674 | vringh_kiov_cleanup(&mvr->wiov); | ||
| 675 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), | ||
| 676 | mvr->vring.len); | ||
| 677 | free_pages((unsigned long)mvr->vring.va, | ||
| 678 | get_order(mvr->vring.len)); | ||
| 679 | } | ||
| 680 | |||
| 681 | list_for_each_safe(pos, tmp, &mdev->vdev_list) { | ||
| 682 | tmp_mvdev = list_entry(pos, struct mic_vdev, list); | ||
| 683 | if (tmp_mvdev == mvdev) { | ||
| 684 | list_del(pos); | ||
| 685 | dev_dbg(mdev->sdev->parent, | ||
| 686 | "Removing virtio device id %d\n", | ||
| 687 | mvdev->virtio_id); | ||
| 688 | break; | ||
| 689 | } | ||
| 690 | } | ||
| 691 | /* | ||
| 692 | * Order the type update with previous stores. This write barrier | ||
| 693 | * is paired with the corresponding read barrier before the uncached | ||
| 694 | * system memory read of the type, on the card while scanning the | ||
| 695 | * device page. | ||
| 696 | */ | ||
| 697 | smp_wmb(); | ||
| 698 | mvdev->dd->type = -1; | ||
| 699 | mutex_unlock(&mdev->mic_mutex); | ||
| 700 | } | ||
diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h new file mode 100644 index 000000000000..184f3c84805b --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.h | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef MIC_VIRTIO_H | ||
| 22 | #define MIC_VIRTIO_H | ||
| 23 | |||
| 24 | #include <linux/virtio_config.h> | ||
| 25 | #include <linux/mic_ioctl.h> | ||
| 26 | |||
| 27 | /* | ||
| 28 | * Note on endianness. | ||
| 29 | * 1. Host can be both BE or LE | ||
| 30 | * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail | ||
| 31 | * rings and ioreadXX/iowriteXX to access used ring. | ||
| 32 | * 3. Device page exposed by host to guest contains LE values. Guest | ||
| 33 | * accesses these using ioreadXX/iowriteXX etc. This way in general we | ||
| 34 | * obey the virtio spec according to which guest works with native | ||
| 35 | * endianness and host is aware of guest endianness and does all | ||
| 36 | * required endianness conversion. | ||
| 37 | * 4. Data provided from user space to guest (in ADD_DEVICE and | ||
| 38 | * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be | ||
| 39 | * in guest endianness. | ||
| 40 | */ | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct mic_vringh - Virtio ring host information. | ||
| 44 | * | ||
| 45 | * @vring: The MIC vring used for setting up user space mappings. | ||
| 46 | * @vrh: The host VRINGH used for accessing the card vrings. | ||
| 47 | * @riov: The VRINGH read kernel IOV. | ||
| 48 | * @wiov: The VRINGH write kernel IOV. | ||
| 49 | * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). | ||
| 50 | * @vr_mutex: Mutex for synchronizing access to the VRING. | ||
| 51 | * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). | ||
| 52 | */ | ||
| 53 | struct mic_vringh { | ||
| 54 | struct mic_vring vring; | ||
| 55 | struct vringh vrh; | ||
| 56 | struct vringh_kiov riov; | ||
| 57 | struct vringh_kiov wiov; | ||
| 58 | u16 head; | ||
| 59 | struct mutex vr_mutex; | ||
| 60 | struct mic_vdev *mvdev; | ||
| 61 | }; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * struct mic_vdev - Host information for a card Virtio device. | ||
| 65 | * | ||
| 66 | * @virtio_id - Virtio device id. | ||
| 67 | * @waitq - Waitqueue to allow ring3 apps to poll. | ||
| 68 | * @mdev - Back pointer to host MIC device. | ||
| 69 | * @poll_wake - Used for waking up threads blocked in poll. | ||
| 70 | * @out_bytes - Debug stats for number of bytes copied from host to card. | ||
| 71 | * @in_bytes - Debug stats for number of bytes copied from card to host. | ||
| 72 | * @mvr - Store per VRING data structures. | ||
| 73 | * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. | ||
| 74 | * @dd - Virtio device descriptor. | ||
| 75 | * @dc - Virtio device control fields. | ||
| 76 | * @list - List of Virtio devices. | ||
| 77 | * @virtio_db - The doorbell used by the card to interrupt the host. | ||
| 78 | * @virtio_cookie - The cookie returned while requesting interrupts. | ||
| 79 | */ | ||
| 80 | struct mic_vdev { | ||
| 81 | int virtio_id; | ||
| 82 | wait_queue_head_t waitq; | ||
| 83 | struct mic_device *mdev; | ||
| 84 | int poll_wake; | ||
| 85 | unsigned long out_bytes; | ||
| 86 | unsigned long in_bytes; | ||
| 87 | struct mic_vringh mvr[MIC_MAX_VRINGS]; | ||
| 88 | struct work_struct virtio_bh_work; | ||
| 89 | struct mic_device_desc *dd; | ||
| 90 | struct mic_device_ctrl *dc; | ||
| 91 | struct list_head list; | ||
| 92 | int virtio_db; | ||
| 93 | struct mic_irq *virtio_cookie; | ||
| 94 | }; | ||
| 95 | |||
| 96 | void mic_virtio_uninit(struct mic_device *mdev); | ||
| 97 | int mic_virtio_add_device(struct mic_vdev *mvdev, | ||
| 98 | void __user *argp); | ||
| 99 | void mic_virtio_del_device(struct mic_vdev *mvdev); | ||
| 100 | int mic_virtio_config_change(struct mic_vdev *mvdev, | ||
| 101 | void __user *argp); | ||
| 102 | int mic_virtio_copy_desc(struct mic_vdev *mvdev, | ||
| 103 | struct mic_copy_desc *request); | ||
| 104 | void mic_virtio_reset_devices(struct mic_device *mdev); | ||
| 105 | void mic_bh_handler(struct work_struct *work); | ||
| 106 | |||
| 107 | /* Helper API to obtain the MIC PCIe device */ | ||
| 108 | static inline struct device *mic_dev(struct mic_vdev *mvdev) | ||
| 109 | { | ||
| 110 | return mvdev->mdev->sdev->parent; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Helper API to check if a virtio device is initialized */ | ||
| 114 | static inline int mic_vdev_inited(struct mic_vdev *mvdev) | ||
| 115 | { | ||
| 116 | /* Device has not been created yet */ | ||
| 117 | if (!mvdev->dd || !mvdev->dd->type) { | ||
| 118 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 119 | __func__, __LINE__, -EINVAL); | ||
| 120 | return -EINVAL; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Device has been removed/deleted */ | ||
| 124 | if (mvdev->dd->type == -1) { | ||
| 125 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 126 | __func__, __LINE__, -ENODEV); | ||
| 127 | return -ENODEV; | ||
| 128 | } | ||
| 129 | |||
| 130 | return 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | /* Helper API to check if a virtio device is running */ | ||
| 134 | static inline bool mic_vdevup(struct mic_vdev *mvdev) | ||
| 135 | { | ||
| 136 | return !!mvdev->dd->status; | ||
| 137 | } | ||
| 138 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c new file mode 100644 index 000000000000..81e9541b784c --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.c | |||
| @@ -0,0 +1,570 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #include <linux/fs.h> | ||
| 22 | #include <linux/pci.h> | ||
| 23 | #include <linux/sched.h> | ||
| 24 | #include <linux/firmware.h> | ||
| 25 | #include <linux/delay.h> | ||
| 26 | |||
| 27 | #include "../common/mic_dev.h" | ||
| 28 | #include "mic_device.h" | ||
| 29 | #include "mic_x100.h" | ||
| 30 | #include "mic_smpt.h" | ||
| 31 | |||
| 32 | /** | ||
| 33 | * mic_x100_write_spad - write to the scratchpad register | ||
| 34 | * @mdev: pointer to mic_device instance | ||
| 35 | * @idx: index to the scratchpad register, 0 based | ||
| 36 | * @val: the data value to put into the register | ||
| 37 | * | ||
| 38 | * This function allows writing of a 32bit value to the indexed scratchpad | ||
| 39 | * register. | ||
| 40 | * | ||
| 41 | * RETURNS: none. | ||
| 42 | */ | ||
| 43 | static void | ||
| 44 | mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) | ||
| 45 | { | ||
| 46 | dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", | ||
| 47 | val, idx); | ||
| 48 | mic_mmio_write(&mdev->mmio, val, | ||
| 49 | MIC_X100_SBOX_BASE_ADDRESS + | ||
| 50 | MIC_X100_SBOX_SPAD0 + idx * 4); | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * mic_x100_read_spad - read from the scratchpad register | ||
| 55 | * @mdev: pointer to mic_device instance | ||
| 56 | * @idx: index to scratchpad register, 0 based | ||
| 57 | * | ||
| 58 | * This function allows reading of the 32bit scratchpad register. | ||
| 59 | * | ||
| 60 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 61 | */ | ||
| 62 | static u32 | ||
| 63 | mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) | ||
| 64 | { | ||
| 65 | u32 val = mic_mmio_read(&mdev->mmio, | ||
| 66 | MIC_X100_SBOX_BASE_ADDRESS + | ||
| 67 | MIC_X100_SBOX_SPAD0 + idx * 4); | ||
| 68 | |||
| 69 | dev_dbg(mdev->sdev->parent, | ||
| 70 | "Reading 0x%x from scratch pad index %d\n", val, idx); | ||
| 71 | return val; | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * mic_x100_enable_interrupts - Enable interrupts. | ||
| 76 | * @mdev: pointer to mic_device instance | ||
| 77 | */ | ||
| 78 | static void mic_x100_enable_interrupts(struct mic_device *mdev) | ||
| 79 | { | ||
| 80 | u32 reg; | ||
| 81 | struct mic_mw *mw = &mdev->mmio; | ||
| 82 | u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; | ||
| 83 | u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; | ||
| 84 | |||
| 85 | reg = mic_mmio_read(mw, sice0); | ||
| 86 | reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); | ||
| 87 | mic_mmio_write(mw, reg, sice0); | ||
| 88 | |||
| 89 | /* | ||
| 90 | * Enable auto-clear when enabling interrupts. Applicable only for | ||
| 91 | * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. | ||
| 92 | */ | ||
| 93 | if (mdev->irq_info.num_vectors > 1) { | ||
| 94 | reg = mic_mmio_read(mw, siac0); | ||
| 95 | reg |= MIC_X100_SBOX_DBR_BITS(0xf) | | ||
| 96 | MIC_X100_SBOX_DMA_BITS(0xff); | ||
| 97 | mic_mmio_write(mw, reg, siac0); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * mic_x100_disable_interrupts - Disable interrupts. | ||
| 103 | * @mdev: pointer to mic_device instance | ||
| 104 | */ | ||
| 105 | static void mic_x100_disable_interrupts(struct mic_device *mdev) | ||
| 106 | { | ||
| 107 | u32 reg; | ||
| 108 | struct mic_mw *mw = &mdev->mmio; | ||
| 109 | u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; | ||
| 110 | u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; | ||
| 111 | u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; | ||
| 112 | |||
| 113 | reg = mic_mmio_read(mw, sice0); | ||
| 114 | mic_mmio_write(mw, reg, sicc0); | ||
| 115 | |||
| 116 | if (mdev->irq_info.num_vectors > 1) { | ||
| 117 | reg = mic_mmio_read(mw, siac0); | ||
| 118 | reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | | ||
| 119 | MIC_X100_SBOX_DMA_BITS(0xff)); | ||
| 120 | mic_mmio_write(mw, reg, siac0); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. | ||
| 126 | * @mdev: pointer to mic_device instance | ||
| 127 | */ | ||
| 128 | static void mic_x100_send_sbox_intr(struct mic_device *mdev, | ||
| 129 | int doorbell) | ||
| 130 | { | ||
| 131 | struct mic_mw *mw = &mdev->mmio; | ||
| 132 | u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; | ||
| 133 | u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + | ||
| 134 | apic_icr_offset); | ||
| 135 | |||
| 136 | /* for MIC we need to make sure we "hit" the send_icr bit (13) */ | ||
| 137 | apicicr_low = (apicicr_low | (1 << 13)); | ||
| 138 | |||
| 139 | /* Ensure that the interrupt is ordered w.r.t. previous stores. */ | ||
| 140 | wmb(); | ||
| 141 | mic_mmio_write(mw, apicicr_low, | ||
| 142 | MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); | ||
| 143 | } | ||
| 144 | |||
| 145 | /** | ||
| 146 | * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. | ||
| 147 | * @mdev: pointer to mic_device instance | ||
| 148 | */ | ||
| 149 | static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, | ||
| 150 | int doorbell) | ||
| 151 | { | ||
| 152 | int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); | ||
| 153 | /* Ensure that the interrupt is ordered w.r.t. previous stores. */ | ||
| 154 | wmb(); | ||
| 155 | mic_mmio_write(&mdev->mmio, 0, | ||
| 156 | MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); | ||
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 160 | * __mic_x100_send_intr - Send interrupt to MIC. | ||
| 161 | * @mdev: pointer to mic_device instance | ||
| 162 | * @doorbell: doorbell number. | ||
| 163 | */ | ||
| 164 | static void mic_x100_send_intr(struct mic_device *mdev, int doorbell) | ||
| 165 | { | ||
| 166 | int rdmasr_db; | ||
| 167 | if (doorbell < MIC_X100_NUM_SBOX_IRQ) { | ||
| 168 | mic_x100_send_sbox_intr(mdev, doorbell); | ||
| 169 | } else { | ||
| 170 | rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ + | ||
| 171 | MIC_X100_RDMASR_IRQ_BASE; | ||
| 172 | mic_x100_send_rdmasr_intr(mdev, rdmasr_db); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * mic_ack_interrupt - Device specific interrupt handling. | ||
| 178 | * @mdev: pointer to mic_device instance | ||
| 179 | * | ||
| 180 | * Returns: bitmask of doorbell events triggered. | ||
| 181 | */ | ||
| 182 | static u32 mic_x100_ack_interrupt(struct mic_device *mdev) | ||
| 183 | { | ||
| 184 | u32 reg = 0; | ||
| 185 | struct mic_mw *mw = &mdev->mmio; | ||
| 186 | u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; | ||
| 187 | |||
| 188 | /* Clear pending bit array. */ | ||
| 189 | if (MIC_A0_STEP == mdev->stepping) | ||
| 190 | mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + | ||
| 191 | MIC_X100_SBOX_MSIXPBACR); | ||
| 192 | |||
| 193 | if (mdev->irq_info.num_vectors <= 1) { | ||
| 194 | reg = mic_mmio_read(mw, sicr0); | ||
| 195 | |||
| 196 | if (unlikely(!reg)) | ||
| 197 | goto done; | ||
| 198 | |||
| 199 | mic_mmio_write(mw, reg, sicr0); | ||
| 200 | } | ||
| 201 | |||
| 202 | if (mdev->stepping >= MIC_B0_STEP) | ||
| 203 | mdev->intr_ops->enable_interrupts(mdev); | ||
| 204 | done: | ||
| 205 | return reg; | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * mic_x100_hw_intr_init - Initialize h/w specific interrupt | ||
| 210 | * information. | ||
| 211 | * @mdev: pointer to mic_device instance | ||
| 212 | */ | ||
| 213 | static void mic_x100_hw_intr_init(struct mic_device *mdev) | ||
| 214 | { | ||
| 215 | mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init; | ||
| 216 | } | ||
| 217 | |||
| 218 | /** | ||
| 219 | * mic_x100_read_msi_to_src_map - read from the MSI mapping registers | ||
| 220 | * @mdev: pointer to mic_device instance | ||
| 221 | * @idx: index to the mapping register, 0 based | ||
| 222 | * | ||
| 223 | * This function allows reading of the 32bit MSI mapping register. | ||
| 224 | * | ||
| 225 | * RETURNS: The value in the register. | ||
| 226 | */ | ||
| 227 | static u32 | ||
| 228 | mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) | ||
| 229 | { | ||
| 230 | return mic_mmio_read(&mdev->mmio, | ||
| 231 | MIC_X100_SBOX_BASE_ADDRESS + | ||
| 232 | MIC_X100_SBOX_MXAR0 + idx * 4); | ||
| 233 | } | ||
| 234 | |||
| 235 | /** | ||
| 236 | * mic_x100_program_msi_to_src_map - program the MSI mapping registers | ||
| 237 | * @mdev: pointer to mic_device instance | ||
| 238 | * @idx: index to the mapping register, 0 based | ||
| 239 | * @offset: The bit offset in the register that needs to be updated. | ||
| 240 | * @set: boolean specifying if the bit in the specified offset needs | ||
| 241 | * to be set or cleared. | ||
| 242 | * | ||
| 243 | * RETURNS: None. | ||
| 244 | */ | ||
| 245 | static void | ||
| 246 | mic_x100_program_msi_to_src_map(struct mic_device *mdev, | ||
| 247 | int idx, int offset, bool set) | ||
| 248 | { | ||
| 249 | unsigned long reg; | ||
| 250 | struct mic_mw *mw = &mdev->mmio; | ||
| 251 | u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + | ||
| 252 | MIC_X100_SBOX_MXAR0 + idx * 4; | ||
| 253 | |||
| 254 | reg = mic_mmio_read(mw, mxar); | ||
| 255 | if (set) | ||
| 256 | __set_bit(offset, ®); | ||
| 257 | else | ||
| 258 | __clear_bit(offset, ®); | ||
| 259 | mic_mmio_write(mw, reg, mxar); | ||
| 260 | } | ||
| 261 | |||
| 262 | /* | ||
| 263 | * mic_x100_reset_fw_ready - Reset Firmware ready status field. | ||
| 264 | * @mdev: pointer to mic_device instance | ||
| 265 | */ | ||
| 266 | static void mic_x100_reset_fw_ready(struct mic_device *mdev) | ||
| 267 | { | ||
| 268 | mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0); | ||
| 269 | } | ||
| 270 | |||
| 271 | /* | ||
| 272 | * mic_x100_is_fw_ready - Check if firmware is ready. | ||
| 273 | * @mdev: pointer to mic_device instance | ||
| 274 | */ | ||
| 275 | static bool mic_x100_is_fw_ready(struct mic_device *mdev) | ||
| 276 | { | ||
| 277 | u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); | ||
| 278 | return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false; | ||
| 279 | } | ||
| 280 | |||
| 281 | /** | ||
| 282 | * mic_x100_get_apic_id - Get bootstrap APIC ID. | ||
| 283 | * @mdev: pointer to mic_device instance | ||
| 284 | */ | ||
| 285 | static u32 mic_x100_get_apic_id(struct mic_device *mdev) | ||
| 286 | { | ||
| 287 | u32 scratch2 = 0; | ||
| 288 | |||
| 289 | scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); | ||
| 290 | return MIC_X100_SPAD2_APIC_ID(scratch2); | ||
| 291 | } | ||
| 292 | |||
| 293 | /** | ||
| 294 | * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. | ||
| 295 | * @mdev: pointer to mic_device instance | ||
| 296 | */ | ||
| 297 | static void mic_x100_send_firmware_intr(struct mic_device *mdev) | ||
| 298 | { | ||
| 299 | u32 apicicr_low; | ||
| 300 | u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; | ||
| 301 | int vector = MIC_X100_BSP_INTERRUPT_VECTOR; | ||
| 302 | struct mic_mw *mw = &mdev->mmio; | ||
| 303 | |||
| 304 | /* | ||
| 305 | * For MIC we need to make sure we "hit" | ||
| 306 | * the send_icr bit (13). | ||
| 307 | */ | ||
| 308 | apicicr_low = (vector | (1 << 13)); | ||
| 309 | |||
| 310 | mic_mmio_write(mw, mic_x100_get_apic_id(mdev), | ||
| 311 | MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); | ||
| 312 | |||
| 313 | /* Ensure that the interrupt is ordered w.r.t. previous stores. */ | ||
| 314 | wmb(); | ||
| 315 | mic_mmio_write(mw, apicicr_low, | ||
| 316 | MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * mic_x100_hw_reset - Reset the MIC device. | ||
| 321 | * @mdev: pointer to mic_device instance | ||
| 322 | */ | ||
| 323 | static void mic_x100_hw_reset(struct mic_device *mdev) | ||
| 324 | { | ||
| 325 | u32 reset_reg; | ||
| 326 | u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; | ||
| 327 | struct mic_mw *mw = &mdev->mmio; | ||
| 328 | |||
| 329 | /* Ensure that the reset is ordered w.r.t. previous loads and stores */ | ||
| 330 | mb(); | ||
| 331 | /* Trigger reset */ | ||
| 332 | reset_reg = mic_mmio_read(mw, rgcr); | ||
| 333 | reset_reg |= 0x1; | ||
| 334 | mic_mmio_write(mw, reset_reg, rgcr); | ||
| 335 | /* | ||
| 336 | * It seems we really want to delay at least 1 second | ||
| 337 | * after touching reset to prevent a lot of problems. | ||
| 338 | */ | ||
| 339 | msleep(1000); | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | * mic_x100_load_command_line - Load command line to MIC. | ||
| 344 | * @mdev: pointer to mic_device instance | ||
| 345 | * @fw: the firmware image | ||
| 346 | * | ||
| 347 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 348 | */ | ||
| 349 | static int | ||
| 350 | mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) | ||
| 351 | { | ||
| 352 | u32 len = 0; | ||
| 353 | u32 boot_mem; | ||
| 354 | char *buf; | ||
| 355 | void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size; | ||
| 356 | #define CMDLINE_SIZE 2048 | ||
| 357 | |||
| 358 | boot_mem = mdev->aper.len >> 20; | ||
| 359 | buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL); | ||
| 360 | if (!buf) { | ||
| 361 | dev_err(mdev->sdev->parent, | ||
| 362 | "%s %d allocation failed\n", __func__, __LINE__); | ||
| 363 | return -ENOMEM; | ||
| 364 | } | ||
| 365 | len += snprintf(buf, CMDLINE_SIZE - len, | ||
| 366 | " mem=%dM", boot_mem); | ||
| 367 | if (mdev->cmdline) | ||
| 368 | snprintf(buf + len, CMDLINE_SIZE - len, " %s", mdev->cmdline); | ||
| 369 | memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); | ||
| 370 | kfree(buf); | ||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | /** | ||
| 375 | * mic_x100_load_ramdisk - Load ramdisk to MIC. | ||
| 376 | * @mdev: pointer to mic_device instance | ||
| 377 | * | ||
| 378 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 379 | */ | ||
| 380 | static int | ||
| 381 | mic_x100_load_ramdisk(struct mic_device *mdev) | ||
| 382 | { | ||
| 383 | const struct firmware *fw; | ||
| 384 | int rc; | ||
| 385 | struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr; | ||
| 386 | |||
| 387 | rc = request_firmware(&fw, | ||
| 388 | mdev->ramdisk, mdev->sdev->parent); | ||
| 389 | if (rc < 0) { | ||
| 390 | dev_err(mdev->sdev->parent, | ||
| 391 | "ramdisk request_firmware failed: %d %s\n", | ||
| 392 | rc, mdev->ramdisk); | ||
| 393 | goto error; | ||
| 394 | } | ||
| 395 | /* | ||
| 396 | * Typically the bootaddr for card OS is 64M | ||
| 397 | * so copy over the ramdisk @ 128M. | ||
| 398 | */ | ||
| 399 | memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size); | ||
| 400 | iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image); | ||
| 401 | iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size); | ||
| 402 | release_firmware(fw); | ||
| 403 | error: | ||
| 404 | return rc; | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * mic_x100_get_boot_addr - Get MIC boot address. | ||
| 409 | * @mdev: pointer to mic_device instance | ||
| 410 | * | ||
| 411 | * This function is called during firmware load to determine | ||
| 412 | * the address at which the OS should be downloaded in card | ||
| 413 | * memory i.e. GDDR. | ||
| 414 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 415 | */ | ||
| 416 | static int | ||
| 417 | mic_x100_get_boot_addr(struct mic_device *mdev) | ||
| 418 | { | ||
| 419 | u32 scratch2, boot_addr; | ||
| 420 | int rc = 0; | ||
| 421 | |||
| 422 | scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); | ||
| 423 | boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2); | ||
| 424 | dev_dbg(mdev->sdev->parent, "%s %d boot_addr 0x%x\n", | ||
| 425 | __func__, __LINE__, boot_addr); | ||
| 426 | if (boot_addr > (1 << 31)) { | ||
| 427 | dev_err(mdev->sdev->parent, | ||
| 428 | "incorrect bootaddr 0x%x\n", | ||
| 429 | boot_addr); | ||
| 430 | rc = -EINVAL; | ||
| 431 | goto error; | ||
| 432 | } | ||
| 433 | mdev->bootaddr = boot_addr; | ||
| 434 | error: | ||
| 435 | return rc; | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * mic_x100_load_firmware - Load firmware to MIC. | ||
| 440 | * @mdev: pointer to mic_device instance | ||
| 441 | * @buf: buffer containing boot string including firmware/ramdisk path. | ||
| 442 | * | ||
| 443 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
| 444 | */ | ||
| 445 | static int | ||
| 446 | mic_x100_load_firmware(struct mic_device *mdev, const char *buf) | ||
| 447 | { | ||
| 448 | int rc; | ||
| 449 | const struct firmware *fw; | ||
| 450 | |||
| 451 | rc = mic_x100_get_boot_addr(mdev); | ||
| 452 | if (rc) | ||
| 453 | goto error; | ||
| 454 | /* load OS */ | ||
| 455 | rc = request_firmware(&fw, mdev->firmware, mdev->sdev->parent); | ||
| 456 | if (rc < 0) { | ||
| 457 | dev_err(mdev->sdev->parent, | ||
| 458 | "ramdisk request_firmware failed: %d %s\n", | ||
| 459 | rc, mdev->firmware); | ||
| 460 | goto error; | ||
| 461 | } | ||
| 462 | if (mdev->bootaddr > mdev->aper.len - fw->size) { | ||
| 463 | rc = -EINVAL; | ||
| 464 | dev_err(mdev->sdev->parent, "%s %d rc %d bootaddr 0x%x\n", | ||
| 465 | __func__, __LINE__, rc, mdev->bootaddr); | ||
| 466 | release_firmware(fw); | ||
| 467 | goto error; | ||
| 468 | } | ||
| 469 | memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); | ||
| 470 | mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); | ||
| 471 | if (!strcmp(mdev->bootmode, "elf")) | ||
| 472 | goto done; | ||
| 473 | /* load command line */ | ||
| 474 | rc = mic_x100_load_command_line(mdev, fw); | ||
| 475 | if (rc) { | ||
| 476 | dev_err(mdev->sdev->parent, "%s %d rc %d\n", | ||
| 477 | __func__, __LINE__, rc); | ||
| 478 | goto error; | ||
| 479 | } | ||
| 480 | release_firmware(fw); | ||
| 481 | /* load ramdisk */ | ||
| 482 | if (mdev->ramdisk) | ||
| 483 | rc = mic_x100_load_ramdisk(mdev); | ||
| 484 | error: | ||
| 485 | dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", __func__, __LINE__, rc); | ||
| 486 | done: | ||
| 487 | return rc; | ||
| 488 | } | ||
| 489 | |||
| 490 | /** | ||
| 491 | * mic_x100_get_postcode - Get postcode status from firmware. | ||
| 492 | * @mdev: pointer to mic_device instance | ||
| 493 | * | ||
| 494 | * RETURNS: postcode. | ||
| 495 | */ | ||
| 496 | static u32 mic_x100_get_postcode(struct mic_device *mdev) | ||
| 497 | { | ||
| 498 | return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE); | ||
| 499 | } | ||
| 500 | |||
| 501 | /** | ||
| 502 | * mic_x100_smpt_set - Update an SMPT entry with a DMA address. | ||
| 503 | * @mdev: pointer to mic_device instance | ||
| 504 | * | ||
| 505 | * RETURNS: none. | ||
| 506 | */ | ||
| 507 | static void | ||
| 508 | mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) | ||
| 509 | { | ||
| 510 | #define SNOOP_ON (0 << 0) | ||
| 511 | #define SNOOP_OFF (1 << 0) | ||
| 512 | /* | ||
| 513 | * Sbox Smpt Reg Bits: | ||
| 514 | * Bits 31:2 Host address | ||
| 515 | * Bits 1 RSVD | ||
| 516 | * Bits 0 No snoop | ||
| 517 | */ | ||
| 518 | #define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \ | ||
| 519 | (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01)) | ||
| 520 | |||
| 521 | uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, | ||
| 522 | dma_addr >> mdev->smpt->info.page_shift); | ||
| 523 | mic_mmio_write(&mdev->mmio, smpt_reg_val, | ||
| 524 | MIC_X100_SBOX_BASE_ADDRESS + | ||
| 525 | MIC_X100_SBOX_SMPT00 + (4 * index)); | ||
| 526 | } | ||
| 527 | |||
| 528 | /** | ||
| 529 | * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields. | ||
| 530 | * @mdev: pointer to mic_device instance | ||
| 531 | * | ||
| 532 | * RETURNS: none. | ||
| 533 | */ | ||
| 534 | static void mic_x100_smpt_hw_init(struct mic_device *mdev) | ||
| 535 | { | ||
| 536 | struct mic_smpt_hw_info *info = &mdev->smpt->info; | ||
| 537 | |||
| 538 | info->num_reg = 32; | ||
| 539 | info->page_shift = 34; | ||
| 540 | info->page_size = (1ULL << info->page_shift); | ||
| 541 | info->base = 0x8000000000ULL; | ||
| 542 | } | ||
| 543 | |||
| 544 | struct mic_smpt_ops mic_x100_smpt_ops = { | ||
| 545 | .init = mic_x100_smpt_hw_init, | ||
| 546 | .set = mic_x100_smpt_set, | ||
| 547 | }; | ||
| 548 | |||
| 549 | struct mic_hw_ops mic_x100_ops = { | ||
| 550 | .aper_bar = MIC_X100_APER_BAR, | ||
| 551 | .mmio_bar = MIC_X100_MMIO_BAR, | ||
| 552 | .read_spad = mic_x100_read_spad, | ||
| 553 | .write_spad = mic_x100_write_spad, | ||
| 554 | .send_intr = mic_x100_send_intr, | ||
| 555 | .ack_interrupt = mic_x100_ack_interrupt, | ||
| 556 | .reset = mic_x100_hw_reset, | ||
| 557 | .reset_fw_ready = mic_x100_reset_fw_ready, | ||
| 558 | .is_fw_ready = mic_x100_is_fw_ready, | ||
| 559 | .send_firmware_intr = mic_x100_send_firmware_intr, | ||
| 560 | .load_mic_fw = mic_x100_load_firmware, | ||
| 561 | .get_postcode = mic_x100_get_postcode, | ||
| 562 | }; | ||
| 563 | |||
| 564 | struct mic_hw_intr_ops mic_x100_intr_ops = { | ||
| 565 | .intr_init = mic_x100_hw_intr_init, | ||
| 566 | .enable_interrupts = mic_x100_enable_interrupts, | ||
| 567 | .disable_interrupts = mic_x100_disable_interrupts, | ||
| 568 | .program_msi_to_src_map = mic_x100_program_msi_to_src_map, | ||
| 569 | .read_msi_to_src_map = mic_x100_read_msi_to_src_map, | ||
| 570 | }; | ||
diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h new file mode 100644 index 000000000000..8b7daa182e54 --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.h | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2013 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Host driver. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | #ifndef _MIC_X100_HW_H_ | ||
| 22 | #define _MIC_X100_HW_H_ | ||
| 23 | |||
| 24 | #define MIC_X100_PCI_DEVICE_2250 0x2250 | ||
| 25 | #define MIC_X100_PCI_DEVICE_2251 0x2251 | ||
| 26 | #define MIC_X100_PCI_DEVICE_2252 0x2252 | ||
| 27 | #define MIC_X100_PCI_DEVICE_2253 0x2253 | ||
| 28 | #define MIC_X100_PCI_DEVICE_2254 0x2254 | ||
| 29 | #define MIC_X100_PCI_DEVICE_2255 0x2255 | ||
| 30 | #define MIC_X100_PCI_DEVICE_2256 0x2256 | ||
| 31 | #define MIC_X100_PCI_DEVICE_2257 0x2257 | ||
| 32 | #define MIC_X100_PCI_DEVICE_2258 0x2258 | ||
| 33 | #define MIC_X100_PCI_DEVICE_2259 0x2259 | ||
| 34 | #define MIC_X100_PCI_DEVICE_225a 0x225a | ||
| 35 | #define MIC_X100_PCI_DEVICE_225b 0x225b | ||
| 36 | #define MIC_X100_PCI_DEVICE_225c 0x225c | ||
| 37 | #define MIC_X100_PCI_DEVICE_225d 0x225d | ||
| 38 | #define MIC_X100_PCI_DEVICE_225e 0x225e | ||
| 39 | |||
| 40 | #define MIC_X100_APER_BAR 0 | ||
| 41 | #define MIC_X100_MMIO_BAR 4 | ||
| 42 | |||
| 43 | #define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 | ||
| 44 | #define MIC_X100_SBOX_SPAD0 0x0000AB20 | ||
| 45 | #define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf) | ||
| 46 | #define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff) | ||
| 47 | #define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf) | ||
| 48 | #define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf) | ||
| 49 | #define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff) | ||
| 50 | #define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8) | ||
| 51 | |||
| 52 | #define MIC_X100_SBOX_APICICR0 0x0000A9D0 | ||
| 53 | #define MIC_X100_SBOX_SICR0 0x00009004 | ||
| 54 | #define MIC_X100_SBOX_SICE0 0x0000900C | ||
| 55 | #define MIC_X100_SBOX_SICC0 0x00009010 | ||
| 56 | #define MIC_X100_SBOX_SIAC0 0x00009014 | ||
| 57 | #define MIC_X100_SBOX_MSIXPBACR 0x00009084 | ||
| 58 | #define MIC_X100_SBOX_MXAR0 0x00009044 | ||
| 59 | #define MIC_X100_SBOX_SMPT00 0x00003100 | ||
| 60 | #define MIC_X100_SBOX_RDMASR0 0x0000B180 | ||
| 61 | |||
| 62 | #define MIC_X100_DOORBELL_IDX_START 0 | ||
| 63 | #define MIC_X100_NUM_DOORBELL 4 | ||
| 64 | #define MIC_X100_DMA_IDX_START 8 | ||
| 65 | #define MIC_X100_NUM_DMA 8 | ||
| 66 | #define MIC_X100_ERR_IDX_START 30 | ||
| 67 | #define MIC_X100_NUM_ERR 1 | ||
| 68 | |||
| 69 | #define MIC_X100_NUM_SBOX_IRQ 8 | ||
| 70 | #define MIC_X100_NUM_RDMASR_IRQ 8 | ||
| 71 | #define MIC_X100_RDMASR_IRQ_BASE 17 | ||
| 72 | #define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1) | ||
| 73 | #define MIC_X100_SPAD2_APIC_ID(x) (((x) >> 1) & 0x1ff) | ||
| 74 | #define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000) | ||
| 75 | #define MIC_X100_SBOX_APICICR7 0x0000AA08 | ||
| 76 | #define MIC_X100_SBOX_RGCR 0x00004010 | ||
| 77 | #define MIC_X100_SBOX_SDBIC0 0x0000CC90 | ||
| 78 | #define MIC_X100_DOWNLOAD_INFO 2 | ||
| 79 | #define MIC_X100_FW_SIZE 5 | ||
| 80 | #define MIC_X100_POSTCODE 0x242c | ||
| 81 | |||
| 82 | static const u16 mic_x100_intr_init[] = { | ||
| 83 | MIC_X100_DOORBELL_IDX_START, | ||
| 84 | MIC_X100_DMA_IDX_START, | ||
| 85 | MIC_X100_ERR_IDX_START, | ||
| 86 | MIC_X100_NUM_DOORBELL, | ||
| 87 | MIC_X100_NUM_DMA, | ||
| 88 | MIC_X100_NUM_ERR, | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* Host->Card(bootstrap) Interrupt Vector */ | ||
| 92 | #define MIC_X100_BSP_INTERRUPT_VECTOR 229 | ||
| 93 | |||
| 94 | extern struct mic_hw_ops mic_x100_ops; | ||
| 95 | extern struct mic_smpt_ops mic_x100_smpt_ops; | ||
| 96 | extern struct mic_hw_intr_ops mic_x100_intr_ops; | ||
| 97 | |||
| 98 | #endif | ||
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 68b7c773d2cf..30754927fd80 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c | |||
| @@ -395,7 +395,7 @@ static int phantom_probe(struct pci_dev *pdev, | |||
| 395 | iowrite32(0, pht->caddr + PHN_IRQCTL); | 395 | iowrite32(0, pht->caddr + PHN_IRQCTL); |
| 396 | ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */ | 396 | ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */ |
| 397 | retval = request_irq(pdev->irq, phantom_isr, | 397 | retval = request_irq(pdev->irq, phantom_isr, |
| 398 | IRQF_SHARED | IRQF_DISABLED, "phantom", pht); | 398 | IRQF_SHARED, "phantom", pht); |
| 399 | if (retval) { | 399 | if (retval) { |
| 400 | dev_err(&pdev->dev, "can't establish ISR\n"); | 400 | dev_err(&pdev->dev, "can't establish ISR\n"); |
| 401 | goto err_unmo; | 401 | goto err_unmo; |
diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index f84ff0c06035..eda38cbe8530 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c | |||
| @@ -892,7 +892,6 @@ static void pti_pci_remove(struct pci_dev *pdev) | |||
| 892 | } | 892 | } |
| 893 | 893 | ||
| 894 | iounmap(drv_data->pti_ioaddr); | 894 | iounmap(drv_data->pti_ioaddr); |
| 895 | pci_set_drvdata(pdev, NULL); | ||
| 896 | kfree(drv_data); | 895 | kfree(drv_data); |
| 897 | pci_release_region(pdev, 1); | 896 | pci_release_region(pdev, 1); |
| 898 | pci_disable_device(pdev); | 897 | pci_disable_device(pdev); |
diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index 9b237221bc4e..83da711ce9f1 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c | |||
| @@ -22,9 +22,7 @@ | |||
| 22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
| 24 | #include <linux/spi/spi.h> | 24 | #include <linux/spi/spi.h> |
| 25 | 25 | #include <linux/of.h> | |
| 26 | #define DAC7512_DRV_NAME "dac7512" | ||
| 27 | #define DRIVER_VERSION "1.0" | ||
| 28 | 26 | ||
| 29 | static ssize_t dac7512_store_val(struct device *dev, | 27 | static ssize_t dac7512_store_val(struct device *dev, |
| 30 | struct device_attribute *attr, | 28 | struct device_attribute *attr, |
| @@ -75,13 +73,29 @@ static int dac7512_remove(struct spi_device *spi) | |||
| 75 | return 0; | 73 | return 0; |
| 76 | } | 74 | } |
| 77 | 75 | ||
| 76 | static const struct spi_device_id dac7512_id_table[] = { | ||
| 77 | { "dac7512", 0 }, | ||
| 78 | { } | ||
| 79 | }; | ||
| 80 | MODULE_DEVICE_TABLE(spi, dac7512_id_table); | ||
| 81 | |||
| 82 | #ifdef CONFIG_OF | ||
| 83 | static const struct of_device_id dac7512_of_match[] = { | ||
| 84 | { .compatible = "ti,dac7512", }, | ||
| 85 | { } | ||
| 86 | }; | ||
| 87 | MODULE_DEVICE_TABLE(of, dac7512_of_match); | ||
| 88 | #endif | ||
| 89 | |||
| 78 | static struct spi_driver dac7512_driver = { | 90 | static struct spi_driver dac7512_driver = { |
| 79 | .driver = { | 91 | .driver = { |
| 80 | .name = DAC7512_DRV_NAME, | 92 | .name = "dac7512", |
| 81 | .owner = THIS_MODULE, | 93 | .owner = THIS_MODULE, |
| 94 | .of_match_table = of_match_ptr(dac7512_of_match), | ||
| 82 | }, | 95 | }, |
| 83 | .probe = dac7512_probe, | 96 | .probe = dac7512_probe, |
| 84 | .remove = dac7512_remove, | 97 | .remove = dac7512_remove, |
| 98 | .id_table = dac7512_id_table, | ||
| 85 | }; | 99 | }; |
| 86 | 100 | ||
| 87 | module_spi_driver(dac7512_driver); | 101 | module_spi_driver(dac7512_driver); |
| @@ -89,4 +103,3 @@ module_spi_driver(dac7512_driver); | |||
| 89 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | 103 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); |
| 90 | MODULE_DESCRIPTION("DAC7512 16-bit DAC"); | 104 | MODULE_DESCRIPTION("DAC7512 16-bit DAC"); |
| 91 | MODULE_LICENSE("GPL v2"); | 105 | MODULE_LICENSE("GPL v2"); |
| 92 | MODULE_VERSION(DRIVER_VERSION); | ||
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index f8d6654391e5..a606c8901e18 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c | |||
| @@ -356,8 +356,10 @@ static int tifm_7xx1_probe(struct pci_dev *dev, | |||
| 356 | pci_set_drvdata(dev, fm); | 356 | pci_set_drvdata(dev, fm); |
| 357 | 357 | ||
| 358 | fm->addr = pci_ioremap_bar(dev, 0); | 358 | fm->addr = pci_ioremap_bar(dev, 0); |
| 359 | if (!fm->addr) | 359 | if (!fm->addr) { |
| 360 | rc = -ENODEV; | ||
| 360 | goto err_out_free; | 361 | goto err_out_free; |
| 362 | } | ||
| 361 | 363 | ||
| 362 | rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm); | 364 | rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm); |
| 363 | if (rc) | 365 | if (rc) |
| @@ -378,7 +380,6 @@ err_out_irq: | |||
| 378 | err_out_unmap: | 380 | err_out_unmap: |
| 379 | iounmap(fm->addr); | 381 | iounmap(fm->addr); |
| 380 | err_out_free: | 382 | err_out_free: |
| 381 | pci_set_drvdata(dev, NULL); | ||
| 382 | tifm_free_adapter(fm); | 383 | tifm_free_adapter(fm); |
| 383 | err_out_int: | 384 | err_out_int: |
| 384 | pci_intx(dev, 0); | 385 | pci_intx(dev, 0); |
| @@ -405,8 +406,6 @@ static void tifm_7xx1_remove(struct pci_dev *dev) | |||
| 405 | for (cnt = 0; cnt < fm->num_sockets; cnt++) | 406 | for (cnt = 0; cnt < fm->num_sockets; cnt++) |
| 406 | tifm_7xx1_sock_power_off(tifm_7xx1_sock_addr(fm->addr, cnt)); | 407 | tifm_7xx1_sock_power_off(tifm_7xx1_sock_addr(fm->addr, cnt)); |
| 407 | 408 | ||
| 408 | pci_set_drvdata(dev, NULL); | ||
| 409 | |||
| 410 | iounmap(fm->addr); | 409 | iounmap(fm->addr); |
| 411 | pci_intx(dev, 0); | 410 | pci_intx(dev, 0); |
| 412 | pci_release_regions(dev); | 411 | pci_release_regions(dev); |
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 0ab7c922212c..a511b2a713b3 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c | |||
| @@ -145,15 +145,17 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, | |||
| 145 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | 145 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
| 146 | return sprintf(buf, "%x", sock->type); | 146 | return sprintf(buf, "%x", sock->type); |
| 147 | } | 147 | } |
| 148 | static DEVICE_ATTR_RO(type); | ||
| 148 | 149 | ||
| 149 | static struct device_attribute tifm_dev_attrs[] = { | 150 | static struct attribute *tifm_dev_attrs[] = { |
| 150 | __ATTR(type, S_IRUGO, type_show, NULL), | 151 | &dev_attr_type.attr, |
| 151 | __ATTR_NULL | 152 | NULL, |
| 152 | }; | 153 | }; |
| 154 | ATTRIBUTE_GROUPS(tifm_dev); | ||
| 153 | 155 | ||
| 154 | static struct bus_type tifm_bus_type = { | 156 | static struct bus_type tifm_bus_type = { |
| 155 | .name = "tifm", | 157 | .name = "tifm", |
| 156 | .dev_attrs = tifm_dev_attrs, | 158 | .dev_groups = tifm_dev_groups, |
| 157 | .match = tifm_bus_match, | 159 | .match = tifm_bus_match, |
| 158 | .uevent = tifm_uevent, | 160 | .uevent = tifm_uevent, |
| 159 | .probe = tifm_device_probe, | 161 | .probe = tifm_device_probe, |
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index b3a2b763ecf2..c98b03b99353 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c | |||
| @@ -649,7 +649,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, | |||
| 649 | return 0; | 649 | return 0; |
| 650 | 650 | ||
| 651 | err_free_irq: | 651 | err_free_irq: |
| 652 | free_irq(vmci_dev->irq, &vmci_dev); | 652 | free_irq(vmci_dev->irq, vmci_dev); |
| 653 | tasklet_kill(&vmci_dev->datagram_tasklet); | 653 | tasklet_kill(&vmci_dev->datagram_tasklet); |
| 654 | tasklet_kill(&vmci_dev->bm_tasklet); | 654 | tasklet_kill(&vmci_dev->bm_tasklet); |
| 655 | 655 | ||
diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index d4722b3dc8ec..1723a6e4f2e8 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c | |||
| @@ -243,11 +243,7 @@ static int vmci_host_setup_notify(struct vmci_ctx *context, | |||
| 243 | /* | 243 | /* |
| 244 | * Lock physical page backing a given user VA. | 244 | * Lock physical page backing a given user VA. |
| 245 | */ | 245 | */ |
| 246 | down_read(¤t->mm->mmap_sem); | 246 | retval = get_user_pages_fast(PAGE_ALIGN(uva), 1, 1, &page); |
| 247 | retval = get_user_pages(current, current->mm, | ||
| 248 | PAGE_ALIGN(uva), | ||
| 249 | 1, 1, 0, &page, NULL); | ||
| 250 | up_read(¤t->mm->mmap_sem); | ||
| 251 | if (retval != 1) | 247 | if (retval != 1) |
| 252 | return VMCI_ERROR_GENERIC; | 248 | return VMCI_ERROR_GENERIC; |
| 253 | 249 | ||
diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index a0515a6d6ebd..1b7b303085d2 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c | |||
| @@ -732,13 +732,9 @@ static int qp_host_get_user_memory(u64 produce_uva, | |||
| 732 | int retval; | 732 | int retval; |
| 733 | int err = VMCI_SUCCESS; | 733 | int err = VMCI_SUCCESS; |
| 734 | 734 | ||
| 735 | down_write(¤t->mm->mmap_sem); | 735 | retval = get_user_pages_fast((uintptr_t) produce_uva, |
| 736 | retval = get_user_pages(current, | 736 | produce_q->kernel_if->num_pages, 1, |
| 737 | current->mm, | 737 | produce_q->kernel_if->u.h.header_page); |
| 738 | (uintptr_t) produce_uva, | ||
| 739 | produce_q->kernel_if->num_pages, | ||
| 740 | 1, 0, | ||
| 741 | produce_q->kernel_if->u.h.header_page, NULL); | ||
| 742 | if (retval < produce_q->kernel_if->num_pages) { | 738 | if (retval < produce_q->kernel_if->num_pages) { |
| 743 | pr_warn("get_user_pages(produce) failed (retval=%d)", retval); | 739 | pr_warn("get_user_pages(produce) failed (retval=%d)", retval); |
| 744 | qp_release_pages(produce_q->kernel_if->u.h.header_page, | 740 | qp_release_pages(produce_q->kernel_if->u.h.header_page, |
| @@ -747,12 +743,9 @@ static int qp_host_get_user_memory(u64 produce_uva, | |||
| 747 | goto out; | 743 | goto out; |
| 748 | } | 744 | } |
| 749 | 745 | ||
| 750 | retval = get_user_pages(current, | 746 | retval = get_user_pages_fast((uintptr_t) consume_uva, |
| 751 | current->mm, | 747 | consume_q->kernel_if->num_pages, 1, |
| 752 | (uintptr_t) consume_uva, | 748 | consume_q->kernel_if->u.h.header_page); |
| 753 | consume_q->kernel_if->num_pages, | ||
| 754 | 1, 0, | ||
| 755 | consume_q->kernel_if->u.h.header_page, NULL); | ||
| 756 | if (retval < consume_q->kernel_if->num_pages) { | 749 | if (retval < consume_q->kernel_if->num_pages) { |
| 757 | pr_warn("get_user_pages(consume) failed (retval=%d)", retval); | 750 | pr_warn("get_user_pages(consume) failed (retval=%d)", retval); |
| 758 | qp_release_pages(consume_q->kernel_if->u.h.header_page, | 751 | qp_release_pages(consume_q->kernel_if->u.h.header_page, |
| @@ -763,8 +756,6 @@ static int qp_host_get_user_memory(u64 produce_uva, | |||
| 763 | } | 756 | } |
| 764 | 757 | ||
| 765 | out: | 758 | out: |
| 766 | up_write(¤t->mm->mmap_sem); | ||
| 767 | |||
| 768 | return err; | 759 | return err; |
| 769 | } | 760 | } |
| 770 | 761 | ||
