diff options
Diffstat (limited to 'drivers/misc')
38 files changed, 2269 insertions, 565 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e83fdfe0c8c..c002d8660e3 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -93,6 +93,14 @@ config ATMEL_TCB_CLKSRC_BLOCK  	  TC can be used for other purposes, such as PWM generation and  	  interval timing. +config DUMMY_IRQ +	tristate "Dummy IRQ handler" +	default n +	---help--- +	  This module accepts a single 'irq' parameter, which it should register for. +	  The sole purpose of this module is to help with debugging of systems on +	  which spurious IRQs would happen on disabled IRQ vector. +  config IBM_ASM  	tristate "Device driver for IBM RSA service processor"  	depends on X86 && PCI && INPUT @@ -398,7 +406,7 @@ config DS1682  config SPEAR13XX_PCIE_GADGET  	bool "PCIe gadget support for SPEAr13XX platform" -	depends on ARCH_SPEAR13XX +	depends on ARCH_SPEAR13XX && BROKEN  	default n  	help  	 This option enables gadget support for PCIe controller. If @@ -418,7 +426,7 @@ config TI_DAC7512  config VMWARE_BALLOON  	tristate "VMware Balloon Driver" -	depends on X86 +	depends on X86 && HYPERVISOR_GUEST  	help  	  This is VMware physical memory management driver which acts  	  like a "balloon" that can be inflated to reclaim physical pages @@ -510,6 +518,15 @@ config LATTICE_ECP3_CONFIG  	  If unsure, say N. +config SRAM +	bool "Generic on-chip SRAM driver" +	depends on HAS_IOMEM +	select GENERIC_ALLOCATOR +	help +	  This driver allows you to declare a memory region to be managed by +	  the genalloc API. It is supposed to be used for small on-chip SRAM +	  areas found on many SoCs. +  source "drivers/misc/c2port/Kconfig"  source "drivers/misc/eeprom/Kconfig"  source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 35a1463c72d..c235d5b6831 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o  obj-$(CONFIG_BMP085)		+= bmp085.o  obj-$(CONFIG_BMP085_I2C)	+= bmp085-i2c.o  obj-$(CONFIG_BMP085_SPI)	+= bmp085-spi.o +obj-$(CONFIG_DUMMY_IRQ)		+= dummy-irq.o  obj-$(CONFIG_ICS932S401)	+= ics932s401.o  obj-$(CONFIG_LKDTM)		+= lkdtm.o  obj-$(CONFIG_TIFM_CORE)       	+= tifm_core.o @@ -49,6 +50,6 @@ obj-y				+= carma/  obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o  obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/  obj-$(CONFIG_INTEL_MEI)		+= mei/ -obj-$(CONFIG_MAX8997_MUIC)	+= max8997-muic.o  obj-$(CONFIG_VMWARE_VMCI)	+= vmw_vmci/  obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o +obj-$(CONFIG_SRAM)		+= sram.o diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index d648b089302..5b5fd8416b3 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -272,19 +272,8 @@ static int apds9802als_remove(struct i2c_client *client)  }  #ifdef CONFIG_PM -static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) -{ -	als_set_power_state(client, false); -	return 0; -} - -static int apds9802als_resume(struct i2c_client *client) -{ -	als_set_default_config(client); -	return 0; -} -static int apds9802als_runtime_suspend(struct device *dev) +static int apds9802als_suspend(struct device *dev)  {  	struct i2c_client *client = to_i2c_client(dev); @@ -292,7 +281,7 @@ static int apds9802als_runtime_suspend(struct device *dev)  	return 0;  } -static int apds9802als_runtime_resume(struct device *dev) +static int apds9802als_resume(struct device *dev)  {  	struct i2c_client *client = to_i2c_client(dev); @@ -300,16 +289,12 @@ static int apds9802als_runtime_resume(struct device *dev)  	return 0;  } -static const struct dev_pm_ops apds9802als_pm_ops = { -	.runtime_suspend = apds9802als_runtime_suspend, -	.runtime_resume = apds9802als_runtime_resume, -}; +static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, +	apds9802als_resume, NULL);  #define APDS9802ALS_PM_OPS (&apds9802als_pm_ops)  #else	/* CONFIG_PM */ -#define apds9802als_suspend NULL -#define apds9802als_resume NULL  #define APDS9802ALS_PM_OPS NULL  #endif	/* CONFIG_PM */ @@ -327,8 +312,6 @@ static struct i2c_driver apds9802als_driver = {  	},  	.probe = apds9802als_probe,  	.remove = apds9802als_remove, -	.suspend = apds9802als_suspend, -	.resume = apds9802als_resume,  	.id_table = apds9802als_id,  }; diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 0e67f8263cd..98f9bb26492 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -700,9 +700,6 @@ static ssize_t apds990x_lux_calib_store(struct device *dev,  	if (strict_strtoul(buf, 0, &value))  		return -EINVAL; -	if (chip->lux_calib > APDS_RANGE) -		return -EINVAL; -  	chip->lux_calib = value;  	return len; @@ -1204,7 +1201,7 @@ static int apds990x_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int apds990x_suspend(struct device *dev)  {  	struct i2c_client *client = container_of(dev, struct i2c_client, dev); @@ -1227,10 +1224,6 @@ static int apds990x_resume(struct device *dev)  	return 0;  } -#else -#define apds990x_suspend  NULL -#define apds990x_resume	  NULL -#define apds990x_shutdown NULL  #endif  #ifdef CONFIG_PM_RUNTIME diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index fe8616a8d28..48651ef0028 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -378,18 +378,7 @@ static struct platform_driver charlcd_driver = {  	.remove = __exit_p(charlcd_remove),  }; -static int __init charlcd_init(void) -{ -	return platform_driver_probe(&charlcd_driver, charlcd_probe); -} - -static void __exit charlcd_exit(void) -{ -	platform_driver_unregister(&charlcd_driver); -} - -module_init(charlcd_init); -module_exit(charlcd_exit); +module_platform_driver_probe(charlcd_driver, charlcd_probe);  MODULE_AUTHOR("Linus Walleij <triad@df.lth.se>");  MODULE_DESCRIPTION("ARM Character LCD Driver"); diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 28f5aaa19d4..494d0500bda 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -393,17 +393,7 @@ static struct platform_driver atmel_pwm_driver = {  	 */  }; -static int __init pwm_init(void) -{ -	return platform_driver_probe(&atmel_pwm_driver, pwm_probe); -} -module_init(pwm_init); - -static void __exit pwm_exit(void) -{ -	platform_driver_unregister(&atmel_pwm_driver); -} -module_exit(pwm_exit); +module_platform_driver_probe(atmel_pwm_driver, pwm_probe);  MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module");  MODULE_LICENSE("GPL"); diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 2ed8fc3be7e..f4975f7d0d5 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1310,7 +1310,7 @@ static int bh1770_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int bh1770_suspend(struct device *dev)  {  	struct i2c_client *client = container_of(dev, struct i2c_client, dev); @@ -1346,11 +1346,6 @@ static int bh1770_resume(struct device *dev)  	}  	return ret;  } - -#else -#define bh1770_suspend	NULL -#define bh1770_shutdown NULL -#define bh1770_resume	NULL  #endif  #ifdef CONFIG_PM_RUNTIME diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index cf03d0abf33..818f3a0e62b 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c @@ -196,7 +196,7 @@ static int bh1780_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int bh1780_suspend(struct device *dev)  {  	struct bh1780_data *ddata; @@ -235,11 +235,9 @@ static int bh1780_resume(struct device *dev)  	return 0;  } +#endif /* CONFIG_PM_SLEEP */ +  static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume); -#define BH1780_PMOPS (&bh1780_pm) -#else -#define BH1780_PMOPS NULL -#endif /* CONFIG_PM */  static const struct i2c_device_id bh1780_id[] = {  	{ "bh1780", 0 }, @@ -252,7 +250,7 @@ static struct i2c_driver bh1780_driver = {  	.id_table	= bh1780_id,  	.driver = {  		.name = "bh1780", -		.pm	= BH1780_PMOPS, +		.pm	= &bh1780_pm,  	},  }; diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index 9858f36dad8..effd8c6b2b9 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -24,8 +24,11 @@  static int mfgpt_reset_timers;  module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); -MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " -		"required by some broken BIOSes (ie, TinyBIOS < 0.99)."); +MODULE_PARM_DESC(mfgptfix, "Try to reset the MFGPT timers during init; " +		"required by some broken BIOSes (ie, TinyBIOS < 0.99) or kexec " +		"(1 = reset the MFGPT using an undocumented bit, " +		"2 = perform a soft reset by unconfiguring all timers); " +		"use what works best for you.");  struct cs5535_mfgpt_timer {  	struct cs5535_mfgpt_chip *chip; @@ -256,6 +259,28 @@ static void reset_all_timers(void)  }  /* + * This is another sledgehammer to reset all MFGPT timers. + * Instead of using the undocumented bit method it clears + * IRQ, NMI and RESET settings. + */ +static void soft_reset(void) +{ +	int i; +	struct cs5535_mfgpt_timer t; + +	for (i = 0; i < MFGPT_MAX_TIMERS; i++) { +		t.nr = i; + +		cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_RESET, 0); +		cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_RESET, 0); +		cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_NMI, 0); +		cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_NMI, 0); +		cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_IRQ, 0); +		cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_IRQ, 0); +	} +} + +/*   * Check whether any MFGPTs are available for the kernel to use.  In most   * cases, firmware that uses AMD's VSA code will claim all timers during   * bootup; we certainly don't want to take them if they're already in use. @@ -271,15 +296,17 @@ static int scan_timers(struct cs5535_mfgpt_chip *mfgpt)  	int i;  	/* bios workaround */ -	if (mfgpt_reset_timers) +	if (mfgpt_reset_timers == 1)  		reset_all_timers(); +	else if (mfgpt_reset_timers == 2) +		soft_reset();  	/* just to be safe, protect this section w/ lock */  	spin_lock_irqsave(&mfgpt->lock, flags);  	for (i = 0; i < MFGPT_MAX_TIMERS; i++) {  		timer.nr = i;  		val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); -		if (!(val & MFGPT_SETUP_SETUP)) { +		if (!(val & MFGPT_SETUP_SETUP) || mfgpt_reset_timers == 2) {  			__set_bit(i, mfgpt->avail);  			timers++;  		} @@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev)  	struct resource *res;  	int err = -EIO, t; +	if (mfgpt_reset_timers < 0 || mfgpt_reset_timers > 2) { +		dev_err(&pdev->dev, "Bad mfgpt_reset_timers value: %i\n", +			mfgpt_reset_timers); +		goto done; +	} +  	/* There are two ways to get the MFGPT base address; one is by  	 * fetching it from MSR_LBAR_MFGPT, the other is by reading the  	 * PCI BAR info.  The latter method is easier (especially across diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c new file mode 100644 index 00000000000..7014167e2c6 --- /dev/null +++ b/drivers/misc/dummy-irq.c @@ -0,0 +1,59 @@ +/* + * Dummy IRQ handler driver. + * + * This module only registers itself as a handler that is specified to it + * by the 'irq' parameter. + * + * The sole purpose of this module is to help with debugging of systems on + * which spurious IRQs would happen on disabled IRQ vector. + * + * Copyright (C) 2013 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +static int irq; + +static irqreturn_t dummy_interrupt(int irq, void *dev_id) +{ +	static int count = 0; + +	if (count == 0) { +		printk(KERN_INFO "dummy-irq: interrupt occured on IRQ %d\n", +				irq); +		count++; +	} + +	return IRQ_NONE; +} + +static int __init dummy_irq_init(void) +{ +	if (request_irq(irq, &dummy_interrupt, IRQF_SHARED, "dummy_irq", &irq)) { +		printk(KERN_ERR "dummy-irq: cannot register IRQ %d\n", irq); +		return -EIO; +	} +	printk(KERN_INFO "dummy-irq: registered for IRQ %d\n", irq); +	return 0; +} + +static void __exit dummy_irq_exit(void) +{ +	printk(KERN_INFO "dummy-irq unloaded\n"); +	free_irq(irq, &irq); +} + +module_init(dummy_irq_init); +module_exit(dummy_irq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jiri Kosina"); +module_param(irq, uint, 0444); +MODULE_PARM_DESC(irq, "The IRQ to register for"); diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b08cf8a0878..ad8fd8e6493 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -412,7 +412,7 @@ static int at25_probe(struct spi_device *spi)  	mutex_init(&at25->lock);  	at25->chip = chip;  	at25->spi = spi_dev_get(spi); -	dev_set_drvdata(&spi->dev, at25); +	spi_set_drvdata(spi, at25);  	at25->addrlen = addrlen;  	/* Export the EEPROM bytes through sysfs, since that's convenient. @@ -463,7 +463,7 @@ static int at25_remove(struct spi_device *spi)  {  	struct at25_data	*at25; -	at25 = dev_get_drvdata(&spi->dev); +	at25 = spi_get_drvdata(spi);  	sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);  	kfree(at25);  	return 0; diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index a6b5d5e7348..94cfc121257 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -363,7 +363,7 @@ static int eeprom_93xx46_probe(struct spi_device *spi)  			dev_err(&spi->dev, "can't create erase interface\n");  	} -	dev_set_drvdata(&spi->dev, edev); +	spi_set_drvdata(spi, edev);  	return 0;  fail:  	kfree(edev); @@ -372,13 +372,13 @@ fail:  static int eeprom_93xx46_remove(struct spi_device *spi)  { -	struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev); +	struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi);  	if (!(edev->pdata->flags & EE_READONLY))  		device_remove_file(&spi->dev, &dev_attr_erase);  	sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin); -	dev_set_drvdata(&spi->dev, NULL); +	spi_set_drvdata(spi, NULL);  	kfree(edev);  	return 0;  } diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 16d7179e2f9..96787ec15ca 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -365,18 +365,7 @@ static struct platform_driver ep93xx_pwm_driver = {  	.remove		= __exit_p(ep93xx_pwm_remove),  }; -static int __init ep93xx_pwm_init(void) -{ -	return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe); -} - -static void __exit ep93xx_pwm_exit(void) -{ -	platform_driver_unregister(&ep93xx_pwm_driver); -} - -module_init(ep93xx_pwm_init); -module_exit(ep93xx_pwm_exit); +module_platform_driver_probe(ep93xx_pwm_driver, ep93xx_pwm_probe);  MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "  	      "H Hartley Sweeten <hsweeten@visionengravers.com>"); diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c index e8cbb1c59f4..a725c79c35f 100644 --- a/drivers/misc/fsa9480.c +++ b/drivers/misc/fsa9480.c @@ -474,10 +474,11 @@ static int fsa9480_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int fsa9480_suspend(struct i2c_client *client, pm_message_t state) +static int fsa9480_suspend(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);  	struct fsa9480_platform_data *pdata = usbsw->pdata; @@ -490,8 +491,9 @@ static int fsa9480_suspend(struct i2c_client *client, pm_message_t state)  	return 0;  } -static int fsa9480_resume(struct i2c_client *client) +static int fsa9480_resume(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);  	int dev1, dev2; @@ -515,12 +517,14 @@ static int fsa9480_resume(struct i2c_client *client)  	return 0;  } +static SIMPLE_DEV_PM_OPS(fsa9480_pm_ops, fsa9480_suspend, fsa9480_resume); +#define FSA9480_PM_OPS (&fsa9480_pm_ops) +  #else -#define fsa9480_suspend NULL -#define fsa9480_resume NULL +#define FSA9480_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */  static const struct i2c_device_id fsa9480_id[] = {  	{"fsa9480", 0}, @@ -531,11 +535,10 @@ MODULE_DEVICE_TABLE(i2c, fsa9480_id);  static struct i2c_driver fsa9480_i2c_driver = {  	.driver = {  		.name = "fsa9480", +		.pm = FSA9480_PM_OPS,  	},  	.probe = fsa9480_probe,  	.remove = fsa9480_remove, -	.resume = fsa9480_resume, -	.suspend = fsa9480_suspend,  	.id_table = fsa9480_id,  }; diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 29b306c6bdb..c5145b3fcce 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -409,18 +409,20 @@ static int isl29003_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM -static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int isl29003_suspend(struct device *dev)  { +	struct i2c_client *client = to_i2c_client(dev);  	struct isl29003_data *data = i2c_get_clientdata(client);  	data->power_state_before_suspend = isl29003_get_power_state(client);  	return isl29003_set_power_state(client, 0);  } -static int isl29003_resume(struct i2c_client *client) +static int isl29003_resume(struct device *dev)  {  	int i; +	struct i2c_client *client = to_i2c_client(dev);  	struct isl29003_data *data = i2c_get_clientdata(client);  	/* restore registers from cache */ @@ -432,10 +434,12 @@ static int isl29003_resume(struct i2c_client *client)  		data->power_state_before_suspend);  } +static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); +#define ISL29003_PM_OPS (&isl29003_pm_ops) +  #else -#define isl29003_suspend	NULL -#define isl29003_resume		NULL -#endif /* CONFIG_PM */ +#define ISL29003_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */  static const struct i2c_device_id isl29003_id[] = {  	{ "isl29003", 0 }, @@ -447,9 +451,8 @@ static struct i2c_driver isl29003_driver = {  	.driver = {  		.name	= ISL29003_DRV_NAME,  		.owner	= THIS_MODULE, +		.pm	= ISL29003_PM_OPS,  	}, -	.suspend = isl29003_suspend, -	.resume	= isl29003_resume,  	.probe	= isl29003_probe,  	.remove	= isl29003_remove,  	.id_table = isl29003_id, diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 155700bfd2b..bb26f086bd8 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -69,7 +69,7 @@ static const struct ecp3_dev ecp3_dev[] = {  static void firmware_load(const struct firmware *fw, void *context)  {  	struct spi_device *spi = (struct spi_device *)context; -	struct fpga_data *data = dev_get_drvdata(&spi->dev); +	struct fpga_data *data = spi_get_drvdata(spi);  	u8 *buffer;  	int ret;  	u8 txbuf[8]; diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d21b4d006a5..c76fa31e9bf 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -10,10 +10,9 @@ config INTEL_MEI  	  <http://software.intel.com/en-us/manageability/>  config INTEL_MEI_ME -	bool "ME Enabled Intel Chipsets" -	depends on INTEL_MEI +	tristate "ME Enabled Intel Chipsets" +	select INTEL_MEI  	depends on X86 && PCI && WATCHDOG_CORE -	default y  	help  	  MEI support for ME Enabled Intel chipsets. diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 040af6c7b14..08698a46626 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -10,5 +10,10 @@ mei-objs += client.o  mei-objs += main.o  mei-objs += amthif.o  mei-objs += wd.o -mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o -mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o +mei-objs += bus.o +mei-objs += nfc.o +mei-$(CONFIG_DEBUG_FS) += debugfs.o + +obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o +mei-me-objs := pci-me.o +mei-me-objs += hw-me.o diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c86d7e3839a..b3e50984d2c 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -60,7 +60,7 @@ void mei_amthif_reset_params(struct mei_device *dev)  }  /** - * mei_amthif_host_init_ - mei initialization amthif client. + * mei_amthif_host_init - mei initialization amthif client.   *   * @dev: the device structure   * @@ -433,7 +433,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev,  /** - * mei_amthif_irq_process_completed - processes completed iamthif operation. + * mei_amthif_irq_write_completed - processes completed iamthif operation.   *   * @dev: the device structure.   * @slots: free slots. @@ -449,7 +449,7 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,  	struct mei_msg_hdr mei_hdr;  	struct mei_cl *cl = cb->cl;  	size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; -	size_t msg_slots = mei_data2slots(len); +	u32 msg_slots = mei_data2slots(len);  	mei_hdr.host_addr = cl->host_client_id;  	mei_hdr.me_addr = cl->me_client_id; @@ -505,14 +505,15 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,   * mei_amthif_irq_read_message - read routine after ISR to   *			handle the read amthif message   * - * @complete_list: An instance of our list structure   * @dev: the device structure   * @mei_hdr: header of amthif message + * @complete_list: An instance of our list structure   *   * returns 0 on success, <0 on failure.   */ -int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, -		struct mei_device *dev, struct mei_msg_hdr *mei_hdr) +int mei_amthif_irq_read_msg(struct mei_device *dev, +			    struct mei_msg_hdr *mei_hdr, +			    struct mei_cl_cb *complete_list)  {  	struct mei_cl_cb *cb;  	unsigned char *buffer; @@ -530,8 +531,7 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,  	if (!mei_hdr->msg_complete)  		return 0; -	dev_dbg(&dev->pdev->dev, -			"amthif_message_buffer_index =%d\n", +	dev_dbg(&dev->pdev->dev, "amthif_message_buffer_index =%d\n",  			mei_hdr->length);  	dev_dbg(&dev->pdev->dev, "completed amthif read.\n "); @@ -566,12 +566,13 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,   */  int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)  { +	u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); -	if (((*slots) * sizeof(u32)) < (sizeof(struct mei_msg_hdr) -			+ sizeof(struct hbm_flow_control))) { +	if (*slots < msg_slots)  		return -EMSGSIZE; -	} -	*slots -= mei_data2slots(sizeof(struct hbm_flow_control)); + +	*slots -= msg_slots; +  	if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) {  		dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");  		return -EIO; @@ -703,7 +704,7 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file)  /**  * mei_amthif_release - the release function  * -*  @inode: pointer to inode structure +*  @dev: device structure  *  @file: pointer to file structure  *  *  returns 0 on success, <0 on error diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c new file mode 100644 index 00000000000..1e935eacaa7 --- /dev/null +++ b/drivers/misc/mei/bus.c @@ -0,0 +1,528 @@ +/* + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/mei_cl_bus.h> + +#include "mei_dev.h" +#include "hw-me.h" +#include "client.h" + +#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) +#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) + +static int mei_cl_device_match(struct device *dev, struct device_driver *drv) +{ +	struct mei_cl_device *device = to_mei_cl_device(dev); +	struct mei_cl_driver *driver = to_mei_cl_driver(drv); +	const struct mei_cl_device_id *id; + +	if (!device) +		return 0; + +	if (!driver || !driver->id_table) +		return 0; + +	id = driver->id_table; + +	while (id->name[0]) { +		if (!strcmp(dev_name(dev), id->name)) +			return 1; + +		id++; +	} + +	return 0; +} + +static int mei_cl_device_probe(struct device *dev) +{ +	struct mei_cl_device *device = to_mei_cl_device(dev); +	struct mei_cl_driver *driver; +	struct mei_cl_device_id id; + +	if (!device) +		return 0; + +	driver = to_mei_cl_driver(dev->driver); +	if (!driver || !driver->probe) +		return -ENODEV; + +	dev_dbg(dev, "Device probe\n"); + +	strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); + +	return driver->probe(device, &id); +} + +static int mei_cl_device_remove(struct device *dev) +{ +	struct mei_cl_device *device = to_mei_cl_device(dev); +	struct mei_cl_driver *driver; + +	if (!device || !dev->driver) +		return 0; + +	if (device->event_cb) { +		device->event_cb = NULL; +		cancel_work_sync(&device->event_work); +	} + +	driver = to_mei_cl_driver(dev->driver); +	if (!driver->remove) { +		dev->driver = NULL; + +		return 0; +	} + +	return driver->remove(device); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, +			     char *buf) +{ +	int len; + +	len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev)); + +	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute mei_cl_dev_attrs[] = { +	__ATTR_RO(modalias), +	__ATTR_NULL, +}; + +static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) +{ +	if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) +		return -ENOMEM; + +	return 0; +} + +static struct bus_type mei_cl_bus_type = { +	.name		= "mei", +	.dev_attrs	= mei_cl_dev_attrs, +	.match		= mei_cl_device_match, +	.probe		= mei_cl_device_probe, +	.remove		= mei_cl_device_remove, +	.uevent		= mei_cl_uevent, +}; + +static void mei_cl_dev_release(struct device *dev) +{ +	kfree(to_mei_cl_device(dev)); +} + +static struct device_type mei_cl_device_type = { +	.release	= mei_cl_dev_release, +}; + +static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, +						uuid_le uuid) +{ +	struct mei_cl *cl, *next; + +	list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { +		if (!uuid_le_cmp(uuid, cl->device_uuid)) +			return cl; +	} + +	return NULL; +} +struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, +					uuid_le uuid, char *name, +					struct mei_cl_ops *ops) +{ +	struct mei_cl_device *device; +	struct mei_cl *cl; +	int status; + +	cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); +	if (cl == NULL) +		return NULL; + +	device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); +	if (!device) +		return NULL; + +	device->cl = cl; +	device->ops = ops; + +	device->dev.parent = &dev->pdev->dev; +	device->dev.bus = &mei_cl_bus_type; +	device->dev.type = &mei_cl_device_type; + +	dev_set_name(&device->dev, "%s", name); + +	status = device_register(&device->dev); +	if (status) { +		dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); +		kfree(device); +		return NULL; +	} + +	cl->device = device; + +	dev_dbg(&device->dev, "client %s registered\n", name); + +	return device; +} +EXPORT_SYMBOL_GPL(mei_cl_add_device); + +void mei_cl_remove_device(struct mei_cl_device *device) +{ +	device_unregister(&device->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_remove_device); + +int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) +{ +	int err; + +	driver->driver.name = driver->name; +	driver->driver.owner = owner; +	driver->driver.bus = &mei_cl_bus_type; + +	err = driver_register(&driver->driver); +	if (err) +		return err; + +	pr_debug("mei: driver [%s] registered\n", driver->driver.name); + +	return 0; +} +EXPORT_SYMBOL_GPL(__mei_cl_driver_register); + +void mei_cl_driver_unregister(struct mei_cl_driver *driver) +{ +	driver_unregister(&driver->driver); + +	pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); + +static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, +			bool blocking) +{ +	struct mei_device *dev; +	struct mei_cl_cb *cb; +	int id; +	int rets; + +	if (WARN_ON(!cl || !cl->dev)) +		return -ENODEV; + +	dev = cl->dev; + +	if (cl->state != MEI_FILE_CONNECTED) +		return -ENODEV; + +	/* Check if we have an ME client device */ +	id = mei_me_cl_by_id(dev, cl->me_client_id); +	if (id < 0) +		return -ENODEV; + +	if (length > dev->me_clients[id].props.max_msg_length) +		return -EINVAL; + +	cb = mei_io_cb_init(cl, NULL); +	if (!cb) +		return -ENOMEM; + +	rets = mei_io_cb_alloc_req_buf(cb, length); +	if (rets < 0) { +		mei_io_cb_free(cb); +		return rets; +	} + +	memcpy(cb->request_buffer.data, buf, length); + +	mutex_lock(&dev->device_lock); + +	rets = mei_cl_write(cl, cb, blocking); + +	mutex_unlock(&dev->device_lock); +	if (rets < 0) +		mei_io_cb_free(cb); + +	return rets; +} + +int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) +{ +	struct mei_device *dev; +	struct mei_cl_cb *cb; +	size_t r_length; +	int err; + +	if (WARN_ON(!cl || !cl->dev)) +		return -ENODEV; + +	dev = cl->dev; + +	mutex_lock(&dev->device_lock); + +	if (!cl->read_cb) { +		err = mei_cl_read_start(cl, length); +		if (err < 0) { +			mutex_unlock(&dev->device_lock); +			return err; +		} +	} + +	if (cl->reading_state != MEI_READ_COMPLETE && +	    !waitqueue_active(&cl->rx_wait)) { +		mutex_unlock(&dev->device_lock); + +		if (wait_event_interruptible(cl->rx_wait, +				(MEI_READ_COMPLETE == cl->reading_state))) { +			if (signal_pending(current)) +				return -EINTR; +			return -ERESTARTSYS; +		} + +		mutex_lock(&dev->device_lock); +	} + +	cb = cl->read_cb; + +	if (cl->reading_state != MEI_READ_COMPLETE) { +		r_length = 0; +		goto out; +	} + +	r_length = min_t(size_t, length, cb->buf_idx); + +	memcpy(buf, cb->response_buffer.data, r_length); + +	mei_io_cb_free(cb); +	cl->reading_state = MEI_IDLE; +	cl->read_cb = NULL; + +out: +	mutex_unlock(&dev->device_lock); + +	return r_length; +} + +inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) +{ +	return ___mei_cl_send(cl, buf, length, 0); +} + +inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) +{ +	return ___mei_cl_send(cl, buf, length, 1); +} + +int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) +{ +	struct mei_cl *cl = device->cl; + +	if (cl == NULL) +		return -ENODEV; + +	if (device->ops && device->ops->send) +		return device->ops->send(device, buf, length); + +	return __mei_cl_send(cl, buf, length); +} +EXPORT_SYMBOL_GPL(mei_cl_send); + +int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) +{ +	struct mei_cl *cl =  device->cl; + +	if (cl == NULL) +		return -ENODEV; + +	if (device->ops && device->ops->recv) +		return device->ops->recv(device, buf, length); + +	return __mei_cl_recv(cl, buf, length); +} +EXPORT_SYMBOL_GPL(mei_cl_recv); + +static void mei_bus_event_work(struct work_struct *work) +{ +	struct mei_cl_device *device; + +	device = container_of(work, struct mei_cl_device, event_work); + +	if (device->event_cb) +		device->event_cb(device, device->events, device->event_context); + +	device->events = 0; + +	/* Prepare for the next read */ +	mei_cl_read_start(device->cl, 0); +} + +int mei_cl_register_event_cb(struct mei_cl_device *device, +			  mei_cl_event_cb_t event_cb, void *context) +{ +	if (device->event_cb) +		return -EALREADY; + +	device->events = 0; +	device->event_cb = event_cb; +	device->event_context = context; +	INIT_WORK(&device->event_work, mei_bus_event_work); + +	mei_cl_read_start(device->cl, 0); + +	return 0; +} +EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); + +void *mei_cl_get_drvdata(const struct mei_cl_device *device) +{ +	return dev_get_drvdata(&device->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); + +void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) +{ +	dev_set_drvdata(&device->dev, data); +} +EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); + +int mei_cl_enable_device(struct mei_cl_device *device) +{ +	int err; +	struct mei_device *dev; +	struct mei_cl *cl = device->cl; + +	if (cl == NULL) +		return -ENODEV; + +	dev = cl->dev; + +	mutex_lock(&dev->device_lock); + +	cl->state = MEI_FILE_CONNECTING; + +	err = mei_cl_connect(cl, NULL); +	if (err < 0) { +		mutex_unlock(&dev->device_lock); +		dev_err(&dev->pdev->dev, "Could not connect to the ME client"); + +		return err; +	} + +	mutex_unlock(&dev->device_lock); + +	if (device->event_cb && !cl->read_cb) +		mei_cl_read_start(device->cl, 0); + +	if (!device->ops || !device->ops->enable) +		return 0; + +	return device->ops->enable(device); +} +EXPORT_SYMBOL_GPL(mei_cl_enable_device); + +int mei_cl_disable_device(struct mei_cl_device *device) +{ +	int err; +	struct mei_device *dev; +	struct mei_cl *cl = device->cl; + +	if (cl == NULL) +		return -ENODEV; + +	dev = cl->dev; + +	mutex_lock(&dev->device_lock); + +	if (cl->state != MEI_FILE_CONNECTED) { +		mutex_unlock(&dev->device_lock); +		dev_err(&dev->pdev->dev, "Already disconnected"); + +		return 0; +	} + +	cl->state = MEI_FILE_DISCONNECTING; + +	err = mei_cl_disconnect(cl); +	if (err < 0) { +		mutex_unlock(&dev->device_lock); +		dev_err(&dev->pdev->dev, +			"Could not disconnect from the ME client"); + +		return err; +	} + +	/* Flush queues and remove any pending read */ +	mei_cl_flush_queues(cl); + +	if (cl->read_cb) { +		struct mei_cl_cb *cb = NULL; + +		cb = mei_cl_find_read_cb(cl); +		/* Remove entry from read list */ +		if (cb) +			list_del(&cb->list); + +		cb = cl->read_cb; +		cl->read_cb = NULL; + +		if (cb) { +			mei_io_cb_free(cb); +			cb = NULL; +		} +	} + +	mutex_unlock(&dev->device_lock); + +	if (!device->ops || !device->ops->disable) +		return 0; + +	return device->ops->disable(device); +} +EXPORT_SYMBOL_GPL(mei_cl_disable_device); + +void mei_cl_bus_rx_event(struct mei_cl *cl) +{ +	struct mei_cl_device *device = cl->device; + +	if (!device || !device->event_cb) +		return; + +	set_bit(MEI_CL_EVENT_RX, &device->events); + +	schedule_work(&device->event_work); +} + +int __init mei_cl_bus_init(void) +{ +	return bus_register(&mei_cl_bus_type); +} + +void __exit mei_cl_bus_exit(void) +{ +	bus_unregister(&mei_cl_bus_type); +} diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1569afe935d..e310ca6ed1a 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -109,7 +109,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb)   * mei_io_cb_init - allocate and initialize io callback   *   * @cl - mei client - * @file: pointer to file structure + * @fp: pointer to file structure   *   * returns mei_cl_cb pointer or NULL;   */ @@ -132,8 +132,8 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)  /**   * mei_io_cb_alloc_req_buf - allocate request buffer   * - * @cb -  io callback structure - * @size: size of the buffer + * @cb: io callback structure + * @length: size of the buffer   *   * returns 0 on success   *         -EINVAL if cb is NULL @@ -154,10 +154,10 @@ int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)  	return 0;  }  /** - * mei_io_cb_alloc_req_buf - allocate respose buffer + * mei_io_cb_alloc_resp_buf - allocate respose buffer   * - * @cb -  io callback structure - * @size: size of the buffer + * @cb: io callback structure + * @length: size of the buffer   *   * returns 0 on success   *         -EINVAL if cb is NULL @@ -183,7 +183,6 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)  /**   * mei_cl_flush_queues - flushes queue lists belonging to cl.   * - * @dev: the device structure   * @cl: host client   */  int mei_cl_flush_queues(struct mei_cl *cl) @@ -216,6 +215,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)  	init_waitqueue_head(&cl->rx_wait);  	init_waitqueue_head(&cl->tx_wait);  	INIT_LIST_HEAD(&cl->link); +	INIT_LIST_HEAD(&cl->device_link);  	cl->reading_state = MEI_IDLE;  	cl->writing_state = MEI_IDLE;  	cl->dev = dev; @@ -243,7 +243,8 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)  /**   * mei_cl_find_read_cb - find this cl's callback in the read list   * - * @dev: device structure + * @cl: host client + *   * returns cb on success, NULL on error   */  struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) @@ -262,6 +263,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)   *   * @cl - host client   * @id - fixed host id or -1 for genereting one + *   * returns 0 on success   *	-EINVAL on incorrect values   *	-ENONET if client not found @@ -301,7 +303,7 @@ int mei_cl_link(struct mei_cl *cl, int id)  /**   * mei_cl_unlink - remove me_cl from the list   * - * @dev: the device structure + * @cl: host client   */  int mei_cl_unlink(struct mei_cl *cl)  { @@ -357,6 +359,9 @@ void mei_host_client_init(struct work_struct *work)  			mei_amthif_host_init(dev);  		else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))  			mei_wd_host_init(dev); +		else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) +			mei_nfc_host_init(dev); +  	}  	dev->dev_state = MEI_DEV_ENABLED; @@ -534,7 +539,6 @@ out:  /**   * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.   * - * @dev: the device structure   * @cl: private data of the file object   *   * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. @@ -575,8 +579,8 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)  /**   * mei_cl_flow_ctrl_reduce - reduces flow_control.   * - * @dev: the device structure   * @cl: private data of the file object + *   * @returns   *	0 on success   *	-ENOENT when me client is not found @@ -614,13 +618,13 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)  }  /** - * mei_cl_start_read - the start read client message function. + * mei_cl_read_start - the start read client message function.   *   * @cl: host client   *   * returns 0 on success, <0 on failure.   */ -int mei_cl_read_start(struct mei_cl *cl) +int mei_cl_read_start(struct mei_cl *cl, size_t length)  {  	struct mei_device *dev;  	struct mei_cl_cb *cb; @@ -653,8 +657,9 @@ int mei_cl_read_start(struct mei_cl *cl)  	if (!cb)  		return -ENOMEM; -	rets = mei_io_cb_alloc_resp_buf(cb, -			dev->me_clients[i].props.max_msg_length); +	/* always allocate at least client max message */ +	length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); +	rets = mei_io_cb_alloc_resp_buf(cb, length);  	if (rets)  		goto err; @@ -677,6 +682,111 @@ err:  }  /** + * mei_cl_write - submit a write cb to mei device +	assumes device_lock is locked + * + * @cl: host client + * @cl: write callback with filled data + * + * returns numbe of bytes sent on success, <0 on failure. + */ +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) +{ +	struct mei_device *dev; +	struct mei_msg_data *buf; +	struct mei_msg_hdr mei_hdr; +	int rets; + + +	if (WARN_ON(!cl || !cl->dev)) +		return -ENODEV; + +	if (WARN_ON(!cb)) +		return -EINVAL; + +	dev = cl->dev; + + +	buf = &cb->request_buffer; + +	dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size); + + +	cb->fop_type = MEI_FOP_WRITE; + +	rets = mei_cl_flow_ctrl_creds(cl); +	if (rets < 0) +		goto err; + +	/* Host buffer is not ready, we queue the request */ +	if (rets == 0 || !dev->hbuf_is_ready) { +		cb->buf_idx = 0; +		/* unseting complete will enqueue the cb for write */ +		mei_hdr.msg_complete = 0; +		cl->writing_state = MEI_WRITING; +		rets = buf->size; +		goto out; +	} + +	dev->hbuf_is_ready = false; + +	/* Check for a maximum length */ +	if (buf->size > mei_hbuf_max_len(dev)) { +		mei_hdr.length = mei_hbuf_max_len(dev); +		mei_hdr.msg_complete = 0; +	} else { +		mei_hdr.length = buf->size; +		mei_hdr.msg_complete = 1; +	} + +	mei_hdr.host_addr = cl->host_client_id; +	mei_hdr.me_addr = cl->me_client_id; +	mei_hdr.reserved = 0; + +	dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", +		MEI_HDR_PRM(&mei_hdr)); + + +	if (mei_write_message(dev, &mei_hdr, buf->data)) { +		rets = -EIO; +		goto err; +	} + +	cl->writing_state = MEI_WRITING; +	cb->buf_idx = mei_hdr.length; + +	rets = buf->size; +out: +	if (mei_hdr.msg_complete) { +		if (mei_cl_flow_ctrl_reduce(cl)) { +			rets = -ENODEV; +			goto err; +		} +		list_add_tail(&cb->list, &dev->write_waiting_list.list); +	} else { +		list_add_tail(&cb->list, &dev->write_list.list); +	} + + +	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { + +		mutex_unlock(&dev->device_lock); +		if (wait_event_interruptible(cl->tx_wait, +			cl->writing_state == MEI_WRITE_COMPLETE)) { +				if (signal_pending(current)) +					rets = -EINTR; +				else +					rets = -ERESTARTSYS; +		} +		mutex_lock(&dev->device_lock); +	} +err: +	return rets; +} + + + +/**   * mei_cl_all_disconnect - disconnect forcefully all connected clients   *   * @dev - mei device diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 214b2397ec3..cfdb144526a 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -86,17 +86,16 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);   */  bool mei_cl_is_other_connecting(struct mei_cl *cl);  int mei_cl_disconnect(struct mei_cl *cl); - -int mei_cl_read_start(struct mei_cl *cl); -  int mei_cl_connect(struct mei_cl *cl, struct file *file); +int mei_cl_read_start(struct mei_cl *cl, size_t length); +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking);  void mei_host_client_init(struct work_struct *work); +  void mei_cl_all_disconnect(struct mei_device *dev);  void mei_cl_all_read_wakeup(struct mei_device *dev);  void mei_cl_all_write_clear(struct mei_device *dev); -  #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c new file mode 100644 index 00000000000..e3870f22d23 --- /dev/null +++ b/drivers/misc/mei/debugfs.c @@ -0,0 +1,143 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/pci.h> + +#include <linux/mei.h> + +#include "mei_dev.h" +#include "hw.h" + +static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, +					size_t cnt, loff_t *ppos) +{ +	struct mei_device *dev = fp->private_data; +	struct mei_me_client *cl; +	const size_t bufsz = 1024; +	char *buf = kzalloc(bufsz, GFP_KERNEL); +	int i; +	int pos = 0; +	int ret; + +	if  (!buf) +		return -ENOMEM; + +	pos += scnprintf(buf + pos, bufsz - pos, +			"  |id|addr|         UUID                       |con|msg len|\n"); + +	mutex_lock(&dev->device_lock); + +	/*  if the driver is not enabled the list won't b consitent */ +	if (dev->dev_state != MEI_DEV_ENABLED) +		goto out; + +	for (i = 0; i < dev->me_clients_num; i++) { +		cl = &dev->me_clients[i]; + +		/* skip me clients that cannot be connected */ +		if (cl->props.max_number_of_connections == 0) +			continue; + +		pos += scnprintf(buf + pos, bufsz - pos, +			"%2d|%2d|%4d|%pUl|%3d|%7d|\n", +			i, cl->client_id, +			cl->props.fixed_address, +			&cl->props.protocol_name, +			cl->props.max_number_of_connections, +			cl->props.max_msg_length); +	} +out: +	mutex_unlock(&dev->device_lock); +	ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); +	kfree(buf); +	return ret; +} + +static const struct file_operations mei_dbgfs_fops_meclients = { +	.open = simple_open, +	.read = mei_dbgfs_read_meclients, +	.llseek = generic_file_llseek, +}; + +static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, +					size_t cnt, loff_t *ppos) +{ +	struct mei_device *dev = fp->private_data; +	const size_t bufsz = 1024; +	char *buf = kzalloc(bufsz, GFP_KERNEL); +	int pos = 0; +	int ret; + +	if  (!buf) +		return -ENOMEM; + +	pos += scnprintf(buf + pos, bufsz - pos, "%s\n", +			mei_dev_state_str(dev->dev_state)); +	ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); +	kfree(buf); +	return ret; +} +static const struct file_operations mei_dbgfs_fops_devstate = { +	.open = simple_open, +	.read = mei_dbgfs_read_devstate, +	.llseek = generic_file_llseek, +}; + +/** + * mei_dbgfs_deregister - Remove the debugfs files and directories + * @mei - pointer to mei device private dat + */ +void mei_dbgfs_deregister(struct mei_device *dev) +{ +	if (!dev->dbgfs_dir) +		return; +	debugfs_remove_recursive(dev->dbgfs_dir); +	dev->dbgfs_dir = NULL; +} + +/** + * Add the debugfs files + * + */ +int mei_dbgfs_register(struct mei_device *dev, const char *name) +{ +	struct dentry *dir, *f; +	dir = debugfs_create_dir(name, NULL); +	if (!dir) +		return -ENOMEM; + +	f = debugfs_create_file("meclients", S_IRUSR, dir, +				dev, &mei_dbgfs_fops_meclients); +	if (!f) { +		dev_err(&dev->pdev->dev, "meclients: registration failed\n"); +		goto err; +	} +	f = debugfs_create_file("devstate", S_IRUSR, dir, +				dev, &mei_dbgfs_fops_devstate); +	if (!f) { +		dev_err(&dev->pdev->dev, "devstate: registration failed\n"); +		goto err; +	} +	dev->dbgfs_dir = dir; +	return 0; +err: +	mei_dbgfs_deregister(dev); +	return -ENODEV; +} + diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index fb9e63ba3bb..6916045166e 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -52,7 +52,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)  			sizeof(struct mei_me_client), GFP_KERNEL);  	if (!clients) {  		dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); -		dev->dev_state = MEI_DEV_RESETING; +		dev->dev_state = MEI_DEV_RESETTING;  		mei_reset(dev, 1);  		return;  	} @@ -62,6 +62,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)  /**   * mei_hbm_cl_hdr - construct client hbm header + *   * @cl: - client   * @hbm_cmd: host bus message command   * @buf: buffer for cl header @@ -123,12 +124,33 @@ static bool is_treat_specially_client(struct mei_cl *cl,  	return false;  } +int mei_hbm_start_wait(struct mei_device *dev) +{ +	int ret; +	if (dev->hbm_state > MEI_HBM_START) +		return 0; + +	mutex_unlock(&dev->device_lock); +	ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, +			dev->hbm_state == MEI_HBM_IDLE || +			dev->hbm_state > MEI_HBM_START, +			mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); +	mutex_lock(&dev->device_lock); + +	if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { +		dev->hbm_state = MEI_HBM_IDLE; +		dev_err(&dev->pdev->dev, "wating for mei start failed\n"); +		return -ETIMEDOUT; +	} +	return 0; +} +  /**   * mei_hbm_start_req - sends start request message.   *   * @dev: the device structure   */ -void mei_hbm_start_req(struct mei_device *dev) +int mei_hbm_start_req(struct mei_device *dev)  {  	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;  	struct hbm_host_version_request *start_req; @@ -143,18 +165,19 @@ void mei_hbm_start_req(struct mei_device *dev)  	start_req->host_version.major_version = HBM_MAJOR_VERSION;  	start_req->host_version.minor_version = HBM_MINOR_VERSION; -	dev->recvd_msg = false; +	dev->hbm_state = MEI_HBM_IDLE;  	if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) { -		dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); -		dev->dev_state = MEI_DEV_RESETING; +		dev_err(&dev->pdev->dev, "version message writet failed\n"); +		dev->dev_state = MEI_DEV_RESETTING;  		mei_reset(dev, 1); +		return -ENODEV;  	} -	dev->init_clients_state = MEI_START_MESSAGE; +	dev->hbm_state = MEI_HBM_START;  	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; -	return ; +	return 0;  } -/** +/*   * mei_hbm_enum_clients_req - sends enumeration client request message.   *   * @dev: the device structure @@ -174,17 +197,17 @@ static void mei_hbm_enum_clients_req(struct mei_device *dev)  	enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;  	if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) { -		dev->dev_state = MEI_DEV_RESETING; -		dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); +		dev->dev_state = MEI_DEV_RESETTING; +		dev_err(&dev->pdev->dev, "enumeration request write failed.\n");  		mei_reset(dev, 1);  	} -	dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE; +	dev->hbm_state = MEI_HBM_ENUM_CLIENTS;  	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;  	return;  }  /** - * mei_hbm_prop_requsest - request property for a single client + * mei_hbm_prop_req - request property for a single client   *   * @dev: the device structure   * @@ -208,6 +231,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)  	/* We got all client properties */  	if (next_client_index == MEI_CLIENTS_MAX) { +		dev->hbm_state = MEI_HBM_STARTED;  		schedule_work(&dev->init_work);  		return 0; @@ -226,8 +250,8 @@ static int mei_hbm_prop_req(struct mei_device *dev)  	prop_req->address = next_client_index;  	if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) { -		dev->dev_state = MEI_DEV_RESETING; -		dev_err(&dev->pdev->dev, "Properties request command failed\n"); +		dev->dev_state = MEI_DEV_RESETTING; +		dev_err(&dev->pdev->dev, "properties request write failed\n");  		mei_reset(dev, 1);  		return -EIO; @@ -283,9 +307,9 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)  }  /** - * add_single_flow_creds - adds single buffer credentials. + * mei_hbm_add_single_flow_creds - adds single buffer credentials.   * - * @file: private data ot the file object. + * @dev: the device structure   * @flow: flow control.   */  static void mei_hbm_add_single_flow_creds(struct mei_device *dev, @@ -477,7 +501,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,  /** - * mei_client_disconnect_request - disconnect request initiated by me + * mei_hbm_fw_disconnect_req - disconnect request initiated by me   *  host sends disoconnect response   *   * @dev: the device structure. @@ -542,27 +566,28 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)  			dev->version = version_res->me_max_version;  			dev_dbg(&dev->pdev->dev, "version mismatch.\n"); +			dev->hbm_state = MEI_HBM_STOP;  			mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,  						dev->wr_msg.data);  			mei_write_message(dev, &dev->wr_msg.hdr,  					dev->wr_msg.data); +  			return;  		}  		dev->version.major_version = HBM_MAJOR_VERSION;  		dev->version.minor_version = HBM_MINOR_VERSION;  		if (dev->dev_state == MEI_DEV_INIT_CLIENTS && -		    dev->init_clients_state == MEI_START_MESSAGE) { +		    dev->hbm_state == MEI_HBM_START) {  			dev->init_clients_timer = 0;  			mei_hbm_enum_clients_req(dev);  		} else { -			dev->recvd_msg = false; -			dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n"); +			dev_err(&dev->pdev->dev, "reset: wrong host start response\n");  			mei_reset(dev, 1);  			return;  		} -		dev->recvd_msg = true; +		wake_up_interruptible(&dev->wait_recvd_msg);  		dev_dbg(&dev->pdev->dev, "host start response message received.\n");  		break; @@ -591,23 +616,20 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)  		me_client = &dev->me_clients[dev->me_client_presentation_num];  		if (props_res->status || !dev->me_clients) { -			dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n"); +			dev_err(&dev->pdev->dev, "reset: properties response hbm wrong status.\n");  			mei_reset(dev, 1);  			return;  		}  		if (me_client->client_id != props_res->address) { -			dev_err(&dev->pdev->dev, -				"Host client properties reply mismatch\n"); +			dev_err(&dev->pdev->dev, "reset: host properties response address mismatch\n");  			mei_reset(dev, 1); -  			return;  		}  		if (dev->dev_state != MEI_DEV_INIT_CLIENTS || -		    dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) { -			dev_err(&dev->pdev->dev, -				"Unexpected client properties reply\n"); +		    dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { +			dev_err(&dev->pdev->dev, "reset: unexpected properties response\n");  			mei_reset(dev, 1);  			return; @@ -626,26 +648,28 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)  		enum_res = (struct hbm_host_enum_response *) mei_msg;  		memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);  		if (dev->dev_state == MEI_DEV_INIT_CLIENTS && -		    dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) { +		    dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {  				dev->init_clients_timer = 0;  				dev->me_client_presentation_num = 0;  				dev->me_client_index = 0;  				mei_hbm_me_cl_allocate(dev); -				dev->init_clients_state = -					MEI_CLIENT_PROPERTIES_MESSAGE; +				dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;  				/* first property reqeust */  				mei_hbm_prop_req(dev);  		} else { -			dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n"); +			dev_err(&dev->pdev->dev, "reset: unexpected enumeration response hbm.\n");  			mei_reset(dev, 1);  			return;  		}  		break;  	case HOST_STOP_RES_CMD: + +		if (dev->hbm_state != MEI_HBM_STOP) +			dev_err(&dev->pdev->dev, "unexpected stop response hbm.\n");  		dev->dev_state = MEI_DEV_DISABLED; -		dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n"); +		dev_info(&dev->pdev->dev, "reset: FW stop response.\n");  		mei_reset(dev, 1);  		break; @@ -657,6 +681,7 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)  	case ME_STOP_REQ_CMD: +		dev->hbm_state = MEI_HBM_STOP;  		mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,  					dev->wr_ext_msg.data);  		break; diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index b552afbaf85..e80dc24ef3e 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -17,6 +17,27 @@  #ifndef _MEI_HBM_H_  #define _MEI_HBM_H_ +struct mei_device; +struct mei_msg_hdr; +struct mei_cl; + +/** + * enum mei_hbm_state - host bus message protocol state + * + * @MEI_HBM_IDLE : protocol not started + * @MEI_HBM_START : start request message was sent + * @MEI_HBM_ENUM_CLIENTS : enumeration request was sent + * @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties + */ +enum mei_hbm_state { +	MEI_HBM_IDLE = 0, +	MEI_HBM_START, +	MEI_HBM_ENUM_CLIENTS, +	MEI_HBM_CLIENT_PROPERTIES, +	MEI_HBM_STARTED, +	MEI_HBM_STOP, +}; +  void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr);  static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) @@ -28,8 +49,8 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)  	hdr->reserved = 0;  } -void mei_hbm_start_req(struct mei_device *dev); - +int mei_hbm_start_req(struct mei_device *dev); +int mei_hbm_start_wait(struct mei_device *dev);  int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);  int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);  int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 45ea7185c00..822170f0034 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -26,14 +26,14 @@  /** - * mei_reg_read - Reads 32bit data from the mei device + * mei_me_reg_read - Reads 32bit data from the mei device   *   * @dev: the device structure   * @offset: offset from which to read the data   *   * returns register value (u32)   */ -static inline u32 mei_reg_read(const struct mei_me_hw *hw, +static inline u32 mei_me_reg_read(const struct mei_me_hw *hw,  			       unsigned long offset)  {  	return ioread32(hw->mem_addr + offset); @@ -41,20 +41,20 @@ static inline u32 mei_reg_read(const struct mei_me_hw *hw,  /** - * mei_reg_write - Writes 32bit data to the mei device + * mei_me_reg_write - Writes 32bit data to the mei device   *   * @dev: the device structure   * @offset: offset from which to write the data   * @value: register value to write (u32)   */ -static inline void mei_reg_write(const struct mei_me_hw *hw, +static inline void mei_me_reg_write(const struct mei_me_hw *hw,  				 unsigned long offset, u32 value)  {  	iowrite32(value, hw->mem_addr + offset);  }  /** - * mei_mecbrw_read - Reads 32bit data from ME circular buffer + * mei_me_mecbrw_read - Reads 32bit data from ME circular buffer   *  read window register   *   * @dev: the device structure @@ -63,18 +63,18 @@ static inline void mei_reg_write(const struct mei_me_hw *hw,   */  static u32 mei_me_mecbrw_read(const struct mei_device *dev)  { -	return mei_reg_read(to_me_hw(dev), ME_CB_RW); +	return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);  }  /** - * mei_mecsr_read - Reads 32bit data from the ME CSR + * mei_me_mecsr_read - Reads 32bit data from the ME CSR   *   * @dev: the device structure   *   * returns ME_CSR_HA register value (u32)   */ -static inline u32 mei_mecsr_read(const struct mei_me_hw *hw) +static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw)  { -	return mei_reg_read(hw, ME_CSR_HA); +	return mei_me_reg_read(hw, ME_CSR_HA);  }  /** @@ -86,7 +86,7 @@ static inline u32 mei_mecsr_read(const struct mei_me_hw *hw)   */  static inline u32 mei_hcsr_read(const struct mei_me_hw *hw)  { -	return mei_reg_read(hw, H_CSR); +	return mei_me_reg_read(hw, H_CSR);  }  /** @@ -98,12 +98,12 @@ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw)  static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr)  {  	hcsr &= ~H_IS; -	mei_reg_write(hw, H_CSR, hcsr); +	mei_me_reg_write(hw, H_CSR, hcsr);  }  /** - * me_hw_config - configure hw dependent settings + * mei_me_hw_config - configure hw dependent settings   *   * @dev: mei device   */ @@ -123,7 +123,7 @@ static void mei_me_intr_clear(struct mei_device *dev)  	struct mei_me_hw *hw = to_me_hw(dev);  	u32 hcsr = mei_hcsr_read(hw);  	if ((hcsr & H_IS) == H_IS) -		mei_reg_write(hw, H_CSR, hcsr); +		mei_me_reg_write(hw, H_CSR, hcsr);  }  /**   * mei_me_intr_enable - enables mei device interrupts @@ -152,10 +152,24 @@ static void mei_me_intr_disable(struct mei_device *dev)  }  /** + * mei_me_hw_reset_release - release device from the reset + * + * @dev: the device structure + */ +static void mei_me_hw_reset_release(struct mei_device *dev) +{ +	struct mei_me_hw *hw = to_me_hw(dev); +	u32 hcsr = mei_hcsr_read(hw); + +	hcsr |= H_IG; +	hcsr &= ~H_RST; +	mei_hcsr_set(hw, hcsr); +} +/**   * mei_me_hw_reset - resets fw via mei csr register.   *   * @dev: the device structure - * @interrupts_enabled: if interrupt should be enabled after reset. + * @intr_enable: if interrupt should be enabled after reset.   */  static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable)  { @@ -169,18 +183,14 @@ static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable)  	if (intr_enable)  		hcsr |= H_IE;  	else -		hcsr &= ~H_IE; - -	mei_hcsr_set(hw, hcsr); - -	hcsr = mei_hcsr_read(hw) | H_IG; -	hcsr &= ~H_RST; +		hcsr |= ~H_IE;  	mei_hcsr_set(hw, hcsr); -	hcsr = mei_hcsr_read(hw); +	if (dev->dev_state == MEI_DEV_POWER_DOWN) +		mei_me_hw_reset_release(dev); -	dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", hcsr); +	dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", mei_hcsr_read(hw));  }  /** @@ -218,10 +228,42 @@ static bool mei_me_host_is_ready(struct mei_device *dev)  static bool mei_me_hw_is_ready(struct mei_device *dev)  {  	struct mei_me_hw *hw = to_me_hw(dev); -	hw->me_hw_state = mei_mecsr_read(hw); +	hw->me_hw_state = mei_me_mecsr_read(hw);  	return (hw->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA;  } +static int mei_me_hw_ready_wait(struct mei_device *dev) +{ +	int err; +	if (mei_me_hw_is_ready(dev)) +		return 0; + +	mutex_unlock(&dev->device_lock); +	err = wait_event_interruptible_timeout(dev->wait_hw_ready, +			dev->recvd_hw_ready, MEI_INTEROP_TIMEOUT); +	mutex_lock(&dev->device_lock); +	if (!err && !dev->recvd_hw_ready) { +		dev_err(&dev->pdev->dev, +			"wait hw ready failed. status = 0x%x\n", err); +		return -ETIMEDOUT; +	} + +	dev->recvd_hw_ready = false; +	return 0; +} + +static int mei_me_hw_start(struct mei_device *dev) +{ +	int ret = mei_me_hw_ready_wait(dev); +	if (ret) +		return ret; +	dev_dbg(&dev->pdev->dev, "hw is ready\n"); + +	mei_me_host_set_ready(dev); +	return ret; +} + +  /**   * mei_hbuf_filled_slots - gets number of device filled buffer slots   * @@ -243,7 +285,7 @@ static unsigned char mei_hbuf_filled_slots(struct mei_device *dev)  }  /** - * mei_hbuf_is_empty - checks if host buffer is empty. + * mei_me_hbuf_is_empty - checks if host buffer is empty.   *   * @dev: the device structure   * @@ -295,10 +337,11 @@ static int mei_me_write_message(struct mei_device *dev,  			unsigned char *buf)  {  	struct mei_me_hw *hw = to_me_hw(dev); -	unsigned long rem, dw_cnt; +	unsigned long rem;  	unsigned long length = header->length;  	u32 *reg_buf = (u32 *)buf;  	u32 hcsr; +	u32 dw_cnt;  	int i;  	int empty_slots; @@ -311,16 +354,16 @@ static int mei_me_write_message(struct mei_device *dev,  	if (empty_slots < 0 || dw_cnt > empty_slots)  		return -EIO; -	mei_reg_write(hw, H_CB_WW, *((u32 *) header)); +	mei_me_reg_write(hw, H_CB_WW, *((u32 *) header));  	for (i = 0; i < length / 4; i++) -		mei_reg_write(hw, H_CB_WW, reg_buf[i]); +		mei_me_reg_write(hw, H_CB_WW, reg_buf[i]);  	rem = length & 0x3;  	if (rem > 0) {  		u32 reg = 0;  		memcpy(®, &buf[length - rem], rem); -		mei_reg_write(hw, H_CB_WW, reg); +		mei_me_reg_write(hw, H_CB_WW, reg);  	}  	hcsr = mei_hcsr_read(hw) | H_IG; @@ -344,7 +387,7 @@ static int mei_me_count_full_read_slots(struct mei_device *dev)  	char read_ptr, write_ptr;  	unsigned char buffer_depth, filled_slots; -	hw->me_hw_state = mei_mecsr_read(hw); +	hw->me_hw_state = mei_me_mecsr_read(hw);  	buffer_depth = (unsigned char)((hw->me_hw_state & ME_CBD_HRA) >> 24);  	read_ptr = (char) ((hw->me_hw_state & ME_CBRP_HRA) >> 8);  	write_ptr = (char) ((hw->me_hw_state & ME_CBWP_HRA) >> 16); @@ -404,7 +447,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)  		return IRQ_NONE;  	/* clear H_IS bit in H_CSR */ -	mei_reg_write(hw, H_CSR, csr_reg); +	mei_me_reg_write(hw, H_CSR, csr_reg);  	return IRQ_WAKE_THREAD;  } @@ -423,12 +466,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)  {  	struct mei_device *dev = (struct mei_device *) dev_id;  	struct mei_cl_cb complete_list; -	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; -	struct mei_cl *cl;  	s32 slots;  	int rets; -	bool  bus_message_received; -  	dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");  	/* initialize our complete list */ @@ -442,7 +481,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)  	/* check if ME wants a reset */  	if (!mei_hw_is_ready(dev) && -	    dev->dev_state != MEI_DEV_RESETING && +	    dev->dev_state != MEI_DEV_RESETTING &&  	    dev->dev_state != MEI_DEV_INITIALIZING) {  		dev_dbg(&dev->pdev->dev, "FW not ready.\n");  		mei_reset(dev, 1); @@ -455,18 +494,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)  		if (mei_hw_is_ready(dev)) {  			dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); -			mei_host_set_ready(dev); - -			dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); -			/* link is established * start sending messages.  */ - -			dev->dev_state = MEI_DEV_INIT_CLIENTS; +			dev->recvd_hw_ready = true; +			wake_up_interruptible(&dev->wait_hw_ready); -			mei_hbm_start_req(dev);  			mutex_unlock(&dev->device_lock);  			return IRQ_HANDLED;  		} else { -			dev_dbg(&dev->pdev->dev, "FW not ready.\n"); +			dev_dbg(&dev->pdev->dev, "Reset Completed.\n"); +			mei_me_hw_reset_release(dev);  			mutex_unlock(&dev->device_lock);  			return IRQ_HANDLED;  		} @@ -488,44 +523,20 @@ end:  	dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");  	dev->hbuf_is_ready = mei_hbuf_is_ready(dev); -	bus_message_received = false; -	if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { -		dev_dbg(&dev->pdev->dev, "received waiting bus message\n"); -		bus_message_received = true; -	}  	mutex_unlock(&dev->device_lock); -	if (bus_message_received) { -		dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n"); -		wake_up_interruptible(&dev->wait_recvd_msg); -		bus_message_received = false; -	} -	if (list_empty(&complete_list.list)) -		return IRQ_HANDLED; +	mei_irq_compl_handler(dev, &complete_list); -	list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) { -		cl = cb_pos->cl; -		list_del(&cb_pos->list); -		if (cl) { -			if (cl != &dev->iamthif_cl) { -				dev_dbg(&dev->pdev->dev, "completing call back.\n"); -				mei_irq_complete_handler(cl, cb_pos); -				cb_pos = NULL; -			} else if (cl == &dev->iamthif_cl) { -				mei_amthif_complete(dev, cb_pos); -			} -		} -	}  	return IRQ_HANDLED;  }  static const struct mei_hw_ops mei_me_hw_ops = { -	.host_set_ready = mei_me_host_set_ready,  	.host_is_ready = mei_me_host_is_ready,  	.hw_is_ready = mei_me_hw_is_ready,  	.hw_reset = mei_me_hw_reset, -	.hw_config  = mei_me_hw_config, +	.hw_config = mei_me_hw_config, +	.hw_start = mei_me_hw_start,  	.intr_clear = mei_me_intr_clear,  	.intr_enable = mei_me_intr_enable, @@ -543,7 +554,7 @@ static const struct mei_hw_ops mei_me_hw_ops = {  };  /** - * init_mei_device - allocates and initializes the mei device structure + * mei_me_dev_init - allocates and initializes the mei device structure   *   * @pdev: The pci device structure   * @@ -560,14 +571,6 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev)  	mei_device_init(dev); -	INIT_LIST_HEAD(&dev->wd_cl.link); -	INIT_LIST_HEAD(&dev->iamthif_cl.link); -	mei_io_list_init(&dev->amthif_cmd_list); -	mei_io_list_init(&dev->amthif_rd_complete_list); - -	INIT_DELAYED_WORK(&dev->timer_work, mei_timer); -	INIT_WORK(&dev->init_work, mei_host_client_init); -  	dev->ops = &mei_me_hw_ops;  	dev->pdev = pdev; diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 8518d3eeb83..80bd829fbd9 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -36,12 +36,6 @@ struct mei_me_hw {  struct mei_device *mei_me_dev_init(struct pci_dev *pdev); -/* get slots (dwords) from a message length + header (bytes) */ -static inline unsigned char mei_data2slots(size_t length) -{ -	return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); -} -  irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);  irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6ec530168af..713d89fedc4 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -14,6 +14,7 @@   *   */ +#include <linux/export.h>  #include <linux/pci.h>  #include <linux/sched.h>  #include <linux/wait.h> @@ -22,6 +23,7 @@  #include <linux/mei.h>  #include "mei_dev.h" +#include "hbm.h"  #include "client.h"  const char *mei_dev_state_str(int state) @@ -31,9 +33,8 @@ const char *mei_dev_state_str(int state)  	MEI_DEV_STATE(INITIALIZING);  	MEI_DEV_STATE(INIT_CLIENTS);  	MEI_DEV_STATE(ENABLED); -	MEI_DEV_STATE(RESETING); +	MEI_DEV_STATE(RESETTING);  	MEI_DEV_STATE(DISABLED); -	MEI_DEV_STATE(RECOVERING_FROM_RESET);  	MEI_DEV_STATE(POWER_DOWN);  	MEI_DEV_STATE(POWER_UP);  	default: @@ -46,7 +47,9 @@ void mei_device_init(struct mei_device *dev)  {  	/* setup our list array */  	INIT_LIST_HEAD(&dev->file_list); +	INIT_LIST_HEAD(&dev->device_list);  	mutex_init(&dev->device_lock); +	init_waitqueue_head(&dev->wait_hw_ready);  	init_waitqueue_head(&dev->wait_recvd_msg);  	init_waitqueue_head(&dev->wait_stop_wd);  	dev->dev_state = MEI_DEV_INITIALIZING; @@ -56,19 +59,27 @@ void mei_device_init(struct mei_device *dev)  	mei_io_list_init(&dev->write_waiting_list);  	mei_io_list_init(&dev->ctrl_wr_list);  	mei_io_list_init(&dev->ctrl_rd_list); + +	INIT_DELAYED_WORK(&dev->timer_work, mei_timer); +	INIT_WORK(&dev->init_work, mei_host_client_init); + +	INIT_LIST_HEAD(&dev->wd_cl.link); +	INIT_LIST_HEAD(&dev->iamthif_cl.link); +	mei_io_list_init(&dev->amthif_cmd_list); +	mei_io_list_init(&dev->amthif_rd_complete_list); +  } +EXPORT_SYMBOL_GPL(mei_device_init);  /** - * mei_hw_init - initializes host and fw to start work. + * mei_start - initializes host and fw to start work.   *   * @dev: the device structure   *   * returns 0 on success, <0 on failure.   */ -int mei_hw_init(struct mei_device *dev) +int mei_start(struct mei_device *dev)  { -	int ret = 0; -  	mutex_lock(&dev->device_lock);  	/* acknowledge interrupt and stop interupts */ @@ -76,29 +87,15 @@ int mei_hw_init(struct mei_device *dev)  	mei_hw_config(dev); -	dev->recvd_msg = false;  	dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");  	mei_reset(dev, 1); -	/* wait for ME to turn on ME_RDY */ -	if (!dev->recvd_msg) { -		mutex_unlock(&dev->device_lock); -		ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, -			dev->recvd_msg, -			mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); -		mutex_lock(&dev->device_lock); -	} - -	if (ret <= 0 && !dev->recvd_msg) { -		dev->dev_state = MEI_DEV_DISABLED; -		dev_dbg(&dev->pdev->dev, -			"wait_event_interruptible_timeout failed" -			"on wait for ME to turn on ME_RDY.\n"); +	if (mei_hbm_start_wait(dev)) { +		dev_err(&dev->pdev->dev, "HBM haven't started");  		goto err;  	} -  	if (!mei_host_is_ready(dev)) {  		dev_err(&dev->pdev->dev, "host is not ready.\n");  		goto err; @@ -115,7 +112,6 @@ int mei_hw_init(struct mei_device *dev)  		goto err;  	} -	dev->recvd_msg = false;  	dev_dbg(&dev->pdev->dev, "link layer has been established.\n");  	mutex_unlock(&dev->device_lock); @@ -126,6 +122,7 @@ err:  	mutex_unlock(&dev->device_lock);  	return -ENODEV;  } +EXPORT_SYMBOL_GPL(mei_start);  /**   * mei_reset - resets host and fw. @@ -137,9 +134,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)  {  	bool unexpected; -	if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) -		return; -  	unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&  			dev->dev_state != MEI_DEV_DISABLED &&  			dev->dev_state != MEI_DEV_POWER_DOWN && @@ -147,11 +141,12 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)  	mei_hw_reset(dev, interrupts_enabled); +	dev->hbm_state = MEI_HBM_IDLE;  	if (dev->dev_state != MEI_DEV_INITIALIZING) {  		if (dev->dev_state != MEI_DEV_DISABLED &&  		    dev->dev_state != MEI_DEV_POWER_DOWN) -			dev->dev_state = MEI_DEV_RESETING; +			dev->dev_state = MEI_DEV_RESETTING;  		mei_cl_all_disconnect(dev); @@ -176,13 +171,50 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)  		dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",  			 mei_dev_state_str(dev->dev_state)); +	if (!interrupts_enabled) { +		dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); +		return; +	} + +	mei_hw_start(dev); + +	dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); +	/* link is established * start sending messages.  */ + +	dev->dev_state = MEI_DEV_INIT_CLIENTS; + +	mei_hbm_start_req(dev); +  	/* wake up all readings so they can be interrupted */  	mei_cl_all_read_wakeup(dev);  	/* remove all waiting requests */  	mei_cl_all_write_clear(dev);  } +EXPORT_SYMBOL_GPL(mei_reset); + +void mei_stop(struct mei_device *dev) +{ +	dev_dbg(&dev->pdev->dev, "stopping the device.\n"); + +	mutex_lock(&dev->device_lock); + +	cancel_delayed_work(&dev->timer_work); +	mei_wd_stop(dev); + +	mei_nfc_host_exit(); + +	dev->dev_state = MEI_DEV_POWER_DOWN; +	mei_reset(dev, 0); + +	mutex_unlock(&dev->device_lock); + +	flush_scheduled_work(); + +	mei_watchdog_unregister(dev); +} +EXPORT_SYMBOL_GPL(mei_stop); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3535b2676c9..2ad73698941 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -15,6 +15,7 @@   */ +#include <linux/export.h>  #include <linux/pci.h>  #include <linux/kthread.h>  #include <linux/interrupt.h> @@ -30,103 +31,153 @@  /** - * mei_complete_handler - processes completed operation. + * mei_cl_complete_handler - processes completed operation for a client   *   * @cl: private data of the file object. - * @cb_pos: callback block. + * @cb: callback block.   */ -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) +static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb)  { -	if (cb_pos->fop_type == MEI_FOP_WRITE) { -		mei_io_cb_free(cb_pos); -		cb_pos = NULL; +	if (cb->fop_type == MEI_FOP_WRITE) { +		mei_io_cb_free(cb); +		cb = NULL;  		cl->writing_state = MEI_WRITE_COMPLETE;  		if (waitqueue_active(&cl->tx_wait))  			wake_up_interruptible(&cl->tx_wait); -	} else if (cb_pos->fop_type == MEI_FOP_READ && +	} else if (cb->fop_type == MEI_FOP_READ &&  			MEI_READING == cl->reading_state) {  		cl->reading_state = MEI_READ_COMPLETE;  		if (waitqueue_active(&cl->rx_wait))  			wake_up_interruptible(&cl->rx_wait); +		else +			mei_cl_bus_rx_event(cl);  	}  }  /** - * _mei_irq_thread_state_ok - checks if mei header matches file private data + * mei_irq_compl_handler - dispatch complete handelers + *	for the completed callbacks   * - * @cl: private data of the file object + * @dev - mei device + * @compl_list - list of completed cbs + */ +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +{ +	struct mei_cl_cb *cb, *next; +	struct mei_cl *cl; + +	list_for_each_entry_safe(cb, next, &compl_list->list, list) { +		cl = cb->cl; +		list_del(&cb->list); +		if (!cl) +			continue; + +		dev_dbg(&dev->pdev->dev, "completing call back.\n"); +		if (cl == &dev->iamthif_cl) +			mei_amthif_complete(dev, cb); +		else +			mei_cl_complete_handler(cl, cb); +	} +} +EXPORT_SYMBOL_GPL(mei_irq_compl_handler); + +/** + * mei_cl_hbm_equal - check if hbm is addressed to the client + * + * @cl: host client   * @mei_hdr: header of mei client message   * - * returns !=0 if matches, 0 if no match. + * returns true if matches, false otherwise + */ +static inline int mei_cl_hbm_equal(struct mei_cl *cl, +			struct mei_msg_hdr *mei_hdr) +{ +	return cl->host_client_id == mei_hdr->host_addr && +		cl->me_client_id == mei_hdr->me_addr; +} +/** + * mei_cl_is_reading - checks if the client +		is the one to read this message + * + * @cl: mei client + * @mei_hdr: header of mei message + * + * returns true on match and false otherwise   */ -static int _mei_irq_thread_state_ok(struct mei_cl *cl, -				struct mei_msg_hdr *mei_hdr) +static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr)  { -	return (cl->host_client_id == mei_hdr->host_addr && -		cl->me_client_id == mei_hdr->me_addr && +	return mei_cl_hbm_equal(cl, mei_hdr) &&  		cl->state == MEI_FILE_CONNECTED && -		MEI_READ_COMPLETE != cl->reading_state); +		cl->reading_state != MEI_READ_COMPLETE;  }  /** - * mei_irq_thread_read_client_message - bottom half read routine after ISR to - * handle the read mei client message data processing. + * mei_irq_read_client_message - process client message   * - * @complete_list: An instance of our list structure   * @dev: the device structure   * @mei_hdr: header of mei client message + * @complete_list: An instance of our list structure   *   * returns 0 on success, <0 on failure.   */ -static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list, -		struct mei_device *dev, -		struct mei_msg_hdr *mei_hdr) +static int mei_cl_irq_read_msg(struct mei_device *dev, +			       struct mei_msg_hdr *mei_hdr, +			       struct mei_cl_cb *complete_list)  {  	struct mei_cl *cl; -	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; +	struct mei_cl_cb *cb, *next;  	unsigned char *buffer = NULL; -	dev_dbg(&dev->pdev->dev, "start client msg\n"); -	if (list_empty(&dev->read_list.list)) -		goto quit; +	list_for_each_entry_safe(cb, next, &dev->read_list.list, list) { +		cl = cb->cl; +		if (!cl || !mei_cl_is_reading(cl, mei_hdr)) +			continue; -	list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) { -		cl = cb_pos->cl; -		if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { -			cl->reading_state = MEI_READING; -			buffer = cb_pos->response_buffer.data + cb_pos->buf_idx; +		cl->reading_state = MEI_READING; -			if (cb_pos->response_buffer.size < -					mei_hdr->length + cb_pos->buf_idx) { -				dev_dbg(&dev->pdev->dev, "message overflow.\n"); -				list_del(&cb_pos->list); -				return -ENOMEM; -			} -			if (buffer) -				mei_read_slots(dev, buffer, mei_hdr->length); +		if (cb->response_buffer.size == 0 || +		    cb->response_buffer.data == NULL) { +			dev_err(&dev->pdev->dev, "response buffer is not allocated.\n"); +			list_del(&cb->list); +			return -ENOMEM; +		} -			cb_pos->buf_idx += mei_hdr->length; -			if (mei_hdr->msg_complete) { -				cl->status = 0; -				list_del(&cb_pos->list); -				dev_dbg(&dev->pdev->dev, -					"completed read H cl = %d, ME cl = %d, length = %lu\n", -					cl->host_client_id, -					cl->me_client_id, -					cb_pos->buf_idx); +		if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { +			dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n", +				cb->response_buffer.size, +				mei_hdr->length, cb->buf_idx); +			buffer = krealloc(cb->response_buffer.data, +					  mei_hdr->length + cb->buf_idx, +					  GFP_KERNEL); -				list_add_tail(&cb_pos->list, -						&complete_list->list); +			if (!buffer) { +				dev_err(&dev->pdev->dev, "allocation failed.\n"); +				list_del(&cb->list); +				return -ENOMEM;  			} - -			break; +			cb->response_buffer.data = buffer; +			cb->response_buffer.size = +				mei_hdr->length + cb->buf_idx;  		} +		buffer = cb->response_buffer.data + cb->buf_idx; +		mei_read_slots(dev, buffer, mei_hdr->length); + +		cb->buf_idx += mei_hdr->length; +		if (mei_hdr->msg_complete) { +			cl->status = 0; +			list_del(&cb->list); +			dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n", +				cl->host_client_id, +				cl->me_client_id, +				cb->buf_idx); +			list_add_tail(&cb->list, &complete_list->list); +		} +		break;  	} -quit:  	dev_dbg(&dev->pdev->dev, "message read\n");  	if (!buffer) {  		mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); @@ -153,31 +204,33 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,  				struct mei_cl *cl,  				struct mei_cl_cb *cmpl_list)  { -	if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + -			sizeof(struct hbm_client_connect_request))) -		return -EBADMSG; +	u32 msg_slots = +		mei_data2slots(sizeof(struct hbm_client_connect_request)); -	*slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); +	if (*slots < msg_slots) +		return -EMSGSIZE; + +	*slots -= msg_slots;  	if (mei_hbm_cl_disconnect_req(dev, cl)) {  		cl->status = 0;  		cb_pos->buf_idx = 0;  		list_move_tail(&cb_pos->list, &cmpl_list->list); -		return -EMSGSIZE; -	} else { -		cl->state = MEI_FILE_DISCONNECTING; -		cl->status = 0; -		cb_pos->buf_idx = 0; -		list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); -		cl->timer_count = MEI_CONNECT_TIMEOUT; +		return -EIO;  	} +	cl->state = MEI_FILE_DISCONNECTING; +	cl->status = 0; +	cb_pos->buf_idx = 0; +	list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); +	cl->timer_count = MEI_CONNECT_TIMEOUT; +  	return 0;  }  /** - * _mei_hb_read - processes read related operation. + * _mei_irq_thread_read - processes read related operation.   *   * @dev: the device structure.   * @slots: free slots. @@ -192,14 +245,15 @@ static int _mei_irq_thread_read(struct mei_device *dev,	s32 *slots,  			struct mei_cl *cl,  			struct mei_cl_cb *cmpl_list)  { -	if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + -			sizeof(struct hbm_flow_control))) { +	u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); + +	if (*slots < msg_slots) {  		/* return the cancel routine */  		list_del(&cb_pos->list); -		return -EBADMSG; +		return -EMSGSIZE;  	} -	*slots -= mei_data2slots(sizeof(struct hbm_flow_control)); +	*slots -= msg_slots;  	if (mei_hbm_cl_flow_control_req(dev, cl)) {  		cl->status = -ENODEV; @@ -229,15 +283,19 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,  			struct mei_cl *cl,  			struct mei_cl_cb *cmpl_list)  { -	if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + -			sizeof(struct hbm_client_connect_request))) { +	u32 msg_slots = +		mei_data2slots(sizeof(struct hbm_client_connect_request)); + +	if (*slots < msg_slots) {  		/* return the cancel routine */  		list_del(&cb_pos->list); -		return -EBADMSG; +		return -EMSGSIZE;  	} +	*slots -=  msg_slots; +  	cl->state = MEI_FILE_CONNECTING; -	*slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); +  	if (mei_hbm_cl_connect_req(dev, cl)) {  		cl->status = -ENODEV;  		cb_pos->buf_idx = 0; @@ -266,7 +324,7 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,  	struct mei_msg_hdr mei_hdr;  	struct mei_cl *cl = cb->cl;  	size_t len = cb->request_buffer.size - cb->buf_idx; -	size_t msg_slots = mei_data2slots(len); +	u32 msg_slots = mei_data2slots(len);  	mei_hdr.host_addr = cl->host_client_id;  	mei_hdr.me_addr = cl->me_client_id; @@ -298,19 +356,20 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,  		return -ENODEV;  	} -	if (mei_cl_flow_ctrl_reduce(cl)) -		return -ENODEV;  	cl->status = 0;  	cb->buf_idx += mei_hdr.length; -	if (mei_hdr.msg_complete) +	if (mei_hdr.msg_complete) { +		if (mei_cl_flow_ctrl_reduce(cl)) +			return -ENODEV;  		list_move_tail(&cb->list, &dev->write_waiting_list.list); +	}  	return 0;  }  /** - * mei_irq_thread_read_handler - bottom half read routine after ISR to + * mei_irq_read_handler - bottom half read routine after ISR to   * handle the read processing.   *   * @dev: the device structure @@ -350,8 +409,7 @@ int mei_irq_read_handler(struct mei_device *dev,  					" client = %d, ME client = %d\n",  					cl_pos->host_client_id,  					cl_pos->me_client_id); -			if (cl_pos->host_client_id == mei_hdr->host_addr && -			    cl_pos->me_client_id == mei_hdr->me_addr) +			if (mei_cl_hbm_equal(cl_pos, mei_hdr))  				break;  		} @@ -362,7 +420,7 @@ int mei_irq_read_handler(struct mei_device *dev,  		}  	}  	if (((*slots) * sizeof(u32)) < mei_hdr->length) { -		dev_dbg(&dev->pdev->dev, +		dev_err(&dev->pdev->dev,  				"we can't read the message slots =%08x.\n",  				*slots);  		/* we can't read the message */ @@ -378,20 +436,19 @@ int mei_irq_read_handler(struct mei_device *dev,  	} else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&  		   (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&  		   (dev->iamthif_state == MEI_IAMTHIF_READING)) { -		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); +		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");  		dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); -		ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr); +		ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);  		if (ret)  			goto end;  	} else { -		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n"); -		ret = mei_irq_thread_read_client_message(cmpl_list, -							 dev, mei_hdr); +		dev_dbg(&dev->pdev->dev, "call mei_cl_irq_read_msg.\n"); +		dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); +		ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list);  		if (ret)  			goto end; -  	}  	/* reset the number of slots and header */ @@ -400,7 +457,7 @@ int mei_irq_read_handler(struct mei_device *dev,  	if (*slots == -EOVERFLOW) {  		/* overflow - reset */ -		dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n"); +		dev_err(&dev->pdev->dev, "resetting due to slots overflow.\n");  		/* set the event since message has been read */  		ret = -ERANGE;  		goto end; @@ -408,6 +465,7 @@ int mei_irq_read_handler(struct mei_device *dev,  end:  	return ret;  } +EXPORT_SYMBOL_GPL(mei_irq_read_handler);  /** @@ -419,8 +477,7 @@ end:   *   * returns 0 on success, <0 on failure.   */ -int mei_irq_write_handler(struct mei_device *dev, -				struct mei_cl_cb *cmpl_list) +int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)  {  	struct mei_cl *cl; @@ -559,6 +616,7 @@ int mei_irq_write_handler(struct mei_device *dev,  	}  	return 0;  } +EXPORT_SYMBOL_GPL(mei_irq_write_handler); @@ -586,8 +644,8 @@ void mei_timer(struct work_struct *work)  		if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {  			if (dev->init_clients_timer) {  				if (--dev->init_clients_timer == 0) { -					dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n", -						dev->init_clients_state); +					dev_err(&dev->pdev->dev, "reset: init clients timeout hbm_state = %d.\n", +						dev->hbm_state);  					mei_reset(dev, 1);  				}  			} @@ -598,7 +656,7 @@ void mei_timer(struct work_struct *work)  	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {  		if (cl_pos->timer_count) {  			if (--cl_pos->timer_count == 0) { -				dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n"); +				dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n");  				mei_reset(dev, 1);  				goto out;  			} @@ -607,7 +665,7 @@ void mei_timer(struct work_struct *work)  	if (dev->iamthif_stall_timer) {  		if (--dev->iamthif_stall_timer == 0) { -			dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n"); +			dev_err(&dev->pdev->dev, "reset: amthif  hanged.\n");  			mei_reset(dev, 1);  			dev->iamthif_msg_buf_size = 0;  			dev->iamthif_msg_buf_index = 0; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 903f809b21f..7c44c8dbae4 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -48,7 +48,7 @@   *   * @inode: pointer to inode structure   * @file: pointer to file structure - * + e   * returns 0 on success, <0 on error   */  static int mei_open(struct inode *inode, struct file *file) @@ -244,7 +244,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,  		goto out;  	} -	err = mei_cl_read_start(cl); +	err = mei_cl_read_start(cl, length);  	if (err && err != -EBUSY) {  		dev_dbg(&dev->pdev->dev,  			"mei start read failure with status = %d\n", err); @@ -292,9 +292,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,  	}  	/* now copy the data to user space */  copy_buffer: -	dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n", -	    cb->response_buffer.size); -	dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx); +	dev_dbg(&dev->pdev->dev, "buf.size = %d buf.idx= %ld\n", +	    cb->response_buffer.size, cb->buf_idx);  	if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {  		rets = -EMSGSIZE;  		goto free; @@ -342,11 +341,10 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,  {  	struct mei_cl *cl = file->private_data;  	struct mei_cl_cb *write_cb = NULL; -	struct mei_msg_hdr mei_hdr;  	struct mei_device *dev;  	unsigned long timeout = 0;  	int rets; -	int i; +	int id;  	if (WARN_ON(!cl || !cl->dev))  		return -ENODEV; @@ -357,24 +355,24 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,  	if (dev->dev_state != MEI_DEV_ENABLED) {  		rets = -ENODEV; -		goto err; +		goto out;  	} -	i = mei_me_cl_by_id(dev, cl->me_client_id); -	if (i < 0) { +	id = mei_me_cl_by_id(dev, cl->me_client_id); +	if (id < 0) {  		rets = -ENODEV; -		goto err; +		goto out;  	} -	if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { +	if (length > dev->me_clients[id].props.max_msg_length || length <= 0) {  		rets = -EMSGSIZE; -		goto err; +		goto out;  	}  	if (cl->state != MEI_FILE_CONNECTED) { -		rets = -ENODEV;  		dev_err(&dev->pdev->dev, "host client = %d,  is not connected to ME client = %d",  			cl->host_client_id, cl->me_client_id); -		goto err; +		rets = -ENODEV; +		goto out;  	}  	if (cl == &dev->iamthif_cl) {  		write_cb = mei_amthif_find_read_list_entry(dev, file); @@ -412,17 +410,15 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,  	if (!write_cb) {  		dev_err(&dev->pdev->dev, "write cb allocation failed\n");  		rets = -ENOMEM; -		goto err; +		goto out;  	}  	rets = mei_io_cb_alloc_req_buf(write_cb, length);  	if (rets) -		goto err; - -	dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); +		goto out;  	rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);  	if (rets) -		goto err; +		goto out;  	cl->sm_state = 0;  	if (length == 4 && @@ -440,65 +436,17 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,  		if (rets) {  			dev_err(&dev->pdev->dev,  				"amthif write failed with status = %d\n", rets); -			goto err; +			goto out;  		}  		mutex_unlock(&dev->device_lock);  		return length;  	} -	write_cb->fop_type = MEI_FOP_WRITE; - -	dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n", -	    cl->host_client_id, cl->me_client_id); -	rets = mei_cl_flow_ctrl_creds(cl); -	if (rets < 0) -		goto err; - -	if (rets == 0 || !dev->hbuf_is_ready) { -		write_cb->buf_idx = 0; -		mei_hdr.msg_complete = 0; -		cl->writing_state = MEI_WRITING; -		goto out; -	} - -	dev->hbuf_is_ready = false; -	if (length >  mei_hbuf_max_len(dev)) { -		mei_hdr.length = mei_hbuf_max_len(dev); -		mei_hdr.msg_complete = 0; -	} else { -		mei_hdr.length = length; -		mei_hdr.msg_complete = 1; -	} -	mei_hdr.host_addr = cl->host_client_id; -	mei_hdr.me_addr = cl->me_client_id; -	mei_hdr.reserved = 0; - -	dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", -		MEI_HDR_PRM(&mei_hdr)); -	if (mei_write_message(dev, &mei_hdr, write_cb->request_buffer.data)) { -		rets = -ENODEV; -		goto err; -	} -	cl->writing_state = MEI_WRITING; -	write_cb->buf_idx = mei_hdr.length; - +	rets = mei_cl_write(cl, write_cb, false);  out: -	if (mei_hdr.msg_complete) { -		if (mei_cl_flow_ctrl_reduce(cl)) { -			rets = -ENODEV; -			goto err; -		} -		list_add_tail(&write_cb->list, &dev->write_waiting_list.list); -	} else { -		list_add_tail(&write_cb->list, &dev->write_list.list); -	} -  	mutex_unlock(&dev->device_lock); -	return length; - -err: -	mutex_unlock(&dev->device_lock); -	mei_io_cb_free(write_cb); +	if (rets < 0) +		mei_io_cb_free(write_cb);  	return rets;  } @@ -753,17 +701,44 @@ static struct miscdevice  mei_misc_device = {  		.minor = MISC_DYNAMIC_MINOR,  }; -int mei_register(struct device *dev) + +int mei_register(struct mei_device *dev)  { -	mei_misc_device.parent = dev; -	return misc_register(&mei_misc_device); +	int ret; +	mei_misc_device.parent = &dev->pdev->dev; +	ret = misc_register(&mei_misc_device); +	if (ret) +		return ret; + +	if (mei_dbgfs_register(dev, mei_misc_device.name)) +		dev_err(&dev->pdev->dev, "cannot register debugfs\n"); + +	return 0;  } +EXPORT_SYMBOL_GPL(mei_register); -void mei_deregister(void) +void mei_deregister(struct mei_device *dev)  { +	mei_dbgfs_deregister(dev);  	misc_deregister(&mei_misc_device);  	mei_misc_device.parent = NULL;  } +EXPORT_SYMBOL_GPL(mei_deregister); + +static int __init mei_init(void) +{ +	return mei_cl_bus_init(); +} + +static void __exit mei_exit(void) +{ +	mei_cl_bus_exit(); +} + +module_init(mei_init); +module_exit(mei_exit); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) Management Engine Interface");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index cb80166161f..4de5140e737 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -21,9 +21,11 @@  #include <linux/watchdog.h>  #include <linux/poll.h>  #include <linux/mei.h> +#include <linux/mei_cl_bus.h>  #include "hw.h"  #include "hw-me-regs.h" +#include "hbm.h"  /*   * watch dog definition @@ -95,22 +97,14 @@ enum mei_dev_state {  	MEI_DEV_INITIALIZING = 0,  	MEI_DEV_INIT_CLIENTS,  	MEI_DEV_ENABLED, -	MEI_DEV_RESETING, +	MEI_DEV_RESETTING,  	MEI_DEV_DISABLED, -	MEI_DEV_RECOVERING_FROM_RESET,  	MEI_DEV_POWER_DOWN,  	MEI_DEV_POWER_UP  };  const char *mei_dev_state_str(int state); -/* init clients states*/ -enum mei_init_clients_states { -	MEI_START_MESSAGE = 0, -	MEI_ENUM_CLIENTS_MESSAGE, -	MEI_CLIENT_PROPERTIES_MESSAGE -}; -  enum iamthif_states {  	MEI_IAMTHIF_IDLE,  	MEI_IAMTHIF_WRITING, @@ -153,7 +147,7 @@ enum mei_cb_file_ops {  /*   * Intel MEI message data struct   */ -struct mei_message_data { +struct mei_msg_data {  	u32 size;  	unsigned char *data;  }; @@ -184,8 +178,8 @@ struct mei_cl_cb {  	struct list_head list;  	struct mei_cl *cl;  	enum mei_cb_file_ops fop_type; -	struct mei_message_data request_buffer; -	struct mei_message_data response_buffer; +	struct mei_msg_data request_buffer; +	struct mei_msg_data response_buffer;  	unsigned long buf_idx;  	unsigned long read_time;  	struct file *file_object; @@ -209,15 +203,20 @@ struct mei_cl {  	enum mei_file_transaction_states writing_state;  	int sm_state;  	struct mei_cl_cb *read_cb; + +	/* MEI CL bus data */ +	struct mei_cl_device *device; +	struct list_head device_link; +	uuid_le device_uuid;  };  /** struct mei_hw_ops   * - * @host_set_ready   - notify FW that host side is ready   * @host_is_ready    - query for host readiness   * @hw_is_ready      - query if hw is ready   * @hw_reset         - reset hw + * @hw_start         - start hw after reset   * @hw_config        - configure hw   * @intr_clear       - clear pending interrupts @@ -237,11 +236,11 @@ struct mei_cl {   */  struct mei_hw_ops { -	void (*host_set_ready) (struct mei_device *dev);  	bool (*host_is_ready) (struct mei_device *dev);  	bool (*hw_is_ready) (struct mei_device *dev);  	void (*hw_reset) (struct mei_device *dev, bool enable); +	int  (*hw_start) (struct mei_device *dev);  	void (*hw_config) (struct mei_device *dev);  	void (*intr_clear) (struct mei_device *dev); @@ -263,9 +262,77 @@ struct mei_hw_ops {  		     unsigned char *buf, unsigned long len);  }; +/* MEI bus API*/ + +/** + * struct mei_cl_ops - MEI CL device ops + * This structure allows ME host clients to implement technology + * specific operations. + * + * @enable: Enable an MEI CL device. Some devices require specific + *	HECI commands to initialize completely. + * @disable: Disable an MEI CL device. + * @send: Tx hook for the device. This allows ME host clients to trap + *	the device driver buffers before actually physically + *	pushing it to the ME. + * @recv: Rx hook for the device. This allows ME host clients to trap the + *	ME buffers before forwarding them to the device driver. + */ +struct mei_cl_ops { +	int (*enable)(struct mei_cl_device *device); +	int (*disable)(struct mei_cl_device *device); +	int (*send)(struct mei_cl_device *device, u8 *buf, size_t length); +	int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length); +}; + +struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, +					uuid_le uuid, char *name, +					struct mei_cl_ops *ops); +void mei_cl_remove_device(struct mei_cl_device *device); + +int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length); +int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length); +int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); +void mei_cl_bus_rx_event(struct mei_cl *cl); +int mei_cl_bus_init(void); +void mei_cl_bus_exit(void); + + +/** + * struct mei_cl_device - MEI device handle + * An mei_cl_device pointer is returned from mei_add_device() + * and links MEI bus clients to their actual ME host client pointer. + * Drivers for MEI devices will get an mei_cl_device pointer + * when being probed and shall use it for doing ME bus I/O. + * + * @dev: linux driver model device pointer + * @uuid: me client uuid + * @cl: mei client + * @ops: ME transport ops + * @event_cb: Drivers register this callback to get asynchronous ME + *	events (e.g. Rx buffer pending) notifications. + * @events: Events bitmask sent to the driver. + * @priv_data: client private data + */ +struct mei_cl_device { +	struct device dev; + +	struct mei_cl *cl; + +	const struct mei_cl_ops *ops; + +	struct work_struct event_work; +	mei_cl_event_cb_t event_cb; +	void *event_context; +	unsigned long events; + +	void *priv_data; +}; +  /**   * struct mei_device -  MEI private device struct + * @hbm_state - state of host bus message protocol   * @mem_addr - mem mapped base register address   * @hbuf_depth - depth of hardware host/write buffer is slots @@ -296,11 +363,12 @@ struct mei_device {  	 */  	struct mutex device_lock; /* device lock */  	struct delayed_work timer_work;	/* MEI timer delayed work (timeouts) */ -	bool recvd_msg; +	bool recvd_hw_ready;  	/*  	 * waiting queue for receive message from FW  	 */ +	wait_queue_head_t wait_hw_ready;  	wait_queue_head_t wait_recvd_msg;  	wait_queue_head_t wait_stop_wd; @@ -308,7 +376,7 @@ struct mei_device {  	 * mei device  states  	 */  	enum mei_dev_state dev_state; -	enum mei_init_clients_states init_clients_state; +	enum mei_hbm_state hbm_state;  	u16 init_clients_timer;  	unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE];	/* control messages */ @@ -365,6 +433,14 @@ struct mei_device {  	struct work_struct init_work; +	/* List of bus devices */ +	struct list_head device_list; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +	struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ + +  	const struct mei_hw_ops *ops;  	char hw[0] __aligned(sizeof(void *));  }; @@ -374,13 +450,24 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec)  	return msecs_to_jiffies(sec * MSEC_PER_SEC);  } +/** + * mei_data2slots - get slots - number of (dwords) from a message length + *	+ size of the mei header + * @length - size of the messages in bytes + * returns  - number of slots + */ +static inline u32 mei_data2slots(size_t length) +{ +	return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); +}  /*   * mei init function prototypes   */  void mei_device_init(struct mei_device *dev);  void mei_reset(struct mei_device *dev, int interrupts); -int mei_hw_init(struct mei_device *dev); +int mei_start(struct mei_device *dev); +void mei_stop(struct mei_device *dev);  /*   *  MEI interrupt functions prototype @@ -391,8 +478,7 @@ int mei_irq_read_handler(struct mei_device *dev,  		struct mei_cl_cb *cmpl_list, s32 *slots);  int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); - -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos); +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);  /*   * AMTHIF - AMT Host Interface Functions @@ -416,6 +502,25 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,  void mei_amthif_run_next_cmd(struct mei_device *dev); +int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, +			struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); + +void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); +int mei_amthif_irq_read_msg(struct mei_device *dev, +			    struct mei_msg_hdr *mei_hdr, +			    struct mei_cl_cb *complete_list); +int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); + +/* + * NFC functions + */ +int mei_nfc_host_init(struct mei_device *dev); +void mei_nfc_host_exit(void); + +/* + * NFC Client UUID + */ +extern const uuid_le mei_nfc_guid;  int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,  			struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); @@ -454,6 +559,11 @@ static inline void mei_hw_reset(struct mei_device *dev, bool enable)  	dev->ops->hw_reset(dev, enable);  } +static inline void mei_hw_start(struct mei_device *dev) +{ +	dev->ops->hw_start(dev); +} +  static inline void mei_clear_interrupts(struct mei_device *dev)  {  	dev->ops->intr_clear(dev); @@ -469,10 +579,6 @@ static inline void mei_disable_interrupts(struct mei_device *dev)  	dev->ops->intr_disable(dev);  } -static inline void mei_host_set_ready(struct mei_device *dev) -{ -	dev->ops->host_set_ready(dev); -}  static inline bool mei_host_is_ready(struct mei_device *dev)  {  	return dev->ops->host_is_ready(dev); @@ -520,8 +626,19 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)  	return dev->ops->rdbuf_full_slots(dev);  } -int mei_register(struct device *dev); -void mei_deregister(void); +#if IS_ENABLED(CONFIG_DEBUG_FS) +int mei_dbgfs_register(struct mei_device *dev, const char *name); +void mei_dbgfs_deregister(struct mei_device *dev); +#else +static inline int mei_dbgfs_register(struct mei_device *dev, const char *name) +{ +	return 0; +} +static inline void mei_dbgfs_deregister(struct mei_device *dev) {} +#endif /* CONFIG_DEBUG_FS */ + +int mei_register(struct mei_device *dev); +void mei_deregister(struct mei_device *dev);  #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d comp=%1d"  #define MEI_HDR_PRM(hdr)                  \ diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c new file mode 100644 index 00000000000..3adf8a70f26 --- /dev/null +++ b/drivers/misc/mei/nfc.c @@ -0,0 +1,554 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/mei_cl_bus.h> + +#include "mei_dev.h" +#include "client.h" + +struct mei_nfc_cmd { +	u8 command; +	u8 status; +	u16 req_id; +	u32 reserved; +	u16 data_size; +	u8 sub_command; +	u8 data[]; +} __packed; + +struct mei_nfc_reply { +	u8 command; +	u8 status; +	u16 req_id; +	u32 reserved; +	u16 data_size; +	u8 sub_command; +	u8 reply_status; +	u8 data[]; +} __packed; + +struct mei_nfc_if_version { +	u8 radio_version_sw[3]; +	u8 reserved[3]; +	u8 radio_version_hw[3]; +	u8 i2c_addr; +	u8 fw_ivn; +	u8 vendor_id; +	u8 radio_type; +} __packed; + +struct mei_nfc_connect { +	u8 fw_ivn; +	u8 vendor_id; +} __packed; + +struct mei_nfc_connect_resp { +	u8 fw_ivn; +	u8 vendor_id; +	u16 me_major; +	u16 me_minor; +	u16 me_hotfix; +	u16 me_build; +} __packed; + +struct mei_nfc_hci_hdr { +	u8 cmd; +	u8 status; +	u16 req_id; +	u32 reserved; +	u16 data_size; +} __packed; + +#define MEI_NFC_CMD_MAINTENANCE 0x00 +#define MEI_NFC_CMD_HCI_SEND 0x01 +#define MEI_NFC_CMD_HCI_RECV 0x02 + +#define MEI_NFC_SUBCMD_CONNECT    0x00 +#define MEI_NFC_SUBCMD_IF_VERSION 0x01 + +#define MEI_NFC_HEADER_SIZE 10 + +/** mei_nfc_dev - NFC mei device + * + * @cl: NFC host client + * @cl_info: NFC info host client + * @init_work: perform connection to the info client + * @fw_ivn: NFC Intervace Version Number + * @vendor_id: NFC manufacturer ID + * @radio_type: NFC radio type + */ +struct mei_nfc_dev { +	struct mei_cl *cl; +	struct mei_cl *cl_info; +	struct work_struct init_work; +	wait_queue_head_t send_wq; +	u8 fw_ivn; +	u8 vendor_id; +	u8 radio_type; +	char *bus_name; + +	u16 req_id; +	u16 recv_req_id; +}; + +static struct mei_nfc_dev nfc_dev; + +/* UUIDs for NFC F/W clients */ +const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, +				     0x94, 0xd4, 0x50, 0x26, +				     0x67, 0x23, 0x77, 0x5c); + +static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, +					0x48, 0xa4, 0xef, 0xab, +					0xba, 0x8a, 0x12, 0x06); + +/* Vendors */ +#define MEI_NFC_VENDOR_INSIDE 0x00 +#define MEI_NFC_VENDOR_NXP    0x01 + +/* Radio types */ +#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 +#define MEI_NFC_VENDOR_NXP_PN544    0x01 + +static void mei_nfc_free(struct mei_nfc_dev *ndev) +{ +	if (ndev->cl) { +		list_del(&ndev->cl->device_link); +		mei_cl_unlink(ndev->cl); +		kfree(ndev->cl); +	} + +	if (ndev->cl_info) { +		list_del(&ndev->cl_info->device_link); +		mei_cl_unlink(ndev->cl_info); +		kfree(ndev->cl_info); +	} +} + +static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) +{ +	struct mei_device *dev; + +	if (!ndev->cl) +		return -ENODEV; + +	dev = ndev->cl->dev; + +	switch (ndev->vendor_id) { +	case MEI_NFC_VENDOR_INSIDE: +		switch (ndev->radio_type) { +		case MEI_NFC_VENDOR_INSIDE_UREAD: +			ndev->bus_name = "microread"; +			return 0; + +		default: +			dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", +				ndev->radio_type); + +			return -EINVAL; +		} + +	case MEI_NFC_VENDOR_NXP: +		switch (ndev->radio_type) { +		case MEI_NFC_VENDOR_NXP_PN544: +			ndev->bus_name = "pn544"; +			return 0; +		default: +			dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", +				ndev->radio_type); + +			return -EINVAL; +		} + +	default: +		dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n", +			ndev->vendor_id); + +		return -EINVAL; +	} + +	return 0; +} + +static int mei_nfc_connect(struct mei_nfc_dev *ndev) +{ +	struct mei_device *dev; +	struct mei_cl *cl; +	struct mei_nfc_cmd *cmd, *reply; +	struct mei_nfc_connect *connect; +	struct mei_nfc_connect_resp *connect_resp; +	size_t connect_length, connect_resp_length; +	int bytes_recv, ret; + +	cl = ndev->cl; +	dev = cl->dev; + +	connect_length = sizeof(struct mei_nfc_cmd) + +			sizeof(struct mei_nfc_connect); + +	connect_resp_length = sizeof(struct mei_nfc_cmd) + +			sizeof(struct mei_nfc_connect_resp); + +	cmd = kzalloc(connect_length, GFP_KERNEL); +	if (!cmd) +		return -ENOMEM; +	connect = (struct mei_nfc_connect *)cmd->data; + +	reply = kzalloc(connect_resp_length, GFP_KERNEL); +	if (!reply) { +		kfree(cmd); +		return -ENOMEM; +	} + +	connect_resp = (struct mei_nfc_connect_resp *)reply->data; + +	cmd->command = MEI_NFC_CMD_MAINTENANCE; +	cmd->data_size = 3; +	cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; +	connect->fw_ivn = ndev->fw_ivn; +	connect->vendor_id = ndev->vendor_id; + +	ret = __mei_cl_send(cl, (u8 *)cmd, connect_length); +	if (ret < 0) { +		dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); +		goto err; +	} + +	bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length); +	if (bytes_recv < 0) { +		dev_err(&dev->pdev->dev, "Could not read connect response\n"); +		ret = bytes_recv; +		goto err; +	} + +	dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", +		 connect_resp->fw_ivn, connect_resp->vendor_id); + +	dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", +		connect_resp->me_major, connect_resp->me_minor, +		connect_resp->me_hotfix, connect_resp->me_build); + +	ret = 0; + +err: +	kfree(reply); +	kfree(cmd); + +	return ret; +} + +static int mei_nfc_if_version(struct mei_nfc_dev *ndev) +{ +	struct mei_device *dev; +	struct mei_cl *cl; + +	struct mei_nfc_cmd cmd; +	struct mei_nfc_reply *reply = NULL; +	struct mei_nfc_if_version *version; +	size_t if_version_length; +	int bytes_recv, ret; + +	cl = ndev->cl_info; +	dev = cl->dev; + +	memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); +	cmd.command = MEI_NFC_CMD_MAINTENANCE; +	cmd.data_size = 1; +	cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; + +	ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); +	if (ret < 0) { +		dev_err(&dev->pdev->dev, "Could not send IF version cmd\n"); +		return ret; +	} + +	/* to be sure on the stack we alloc memory */ +	if_version_length = sizeof(struct mei_nfc_reply) + +		sizeof(struct mei_nfc_if_version); + +	reply = kzalloc(if_version_length, GFP_KERNEL); +	if (!reply) +		return -ENOMEM; + +	bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); +	if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { +		dev_err(&dev->pdev->dev, "Could not read IF version\n"); +		ret = -EIO; +		goto err; +	} + +	version = (struct mei_nfc_if_version *)reply->data; + +	ndev->fw_ivn = version->fw_ivn; +	ndev->vendor_id = version->vendor_id; +	ndev->radio_type = version->radio_type; + +err: +	kfree(reply); +	return ret; +} + +static int mei_nfc_enable(struct mei_cl_device *cldev) +{ +	struct mei_device *dev; +	struct mei_nfc_dev *ndev = &nfc_dev; +	int ret; + +	dev = ndev->cl->dev; + +	ret = mei_nfc_connect(ndev); +	if (ret < 0) { +		dev_err(&dev->pdev->dev, "Could not connect to NFC"); +		return ret; +	} + +	return 0; +} + +static int mei_nfc_disable(struct mei_cl_device *cldev) +{ +	return 0; +} + +static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) +{ +	struct mei_device *dev; +	struct mei_nfc_dev *ndev; +	struct mei_nfc_hci_hdr *hdr; +	u8 *mei_buf; +	int err; + +	ndev = (struct mei_nfc_dev *) cldev->priv_data; +	dev = ndev->cl->dev; + +	mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); +	if (!mei_buf) +		return -ENOMEM; + +	hdr = (struct mei_nfc_hci_hdr *) mei_buf; +	hdr->cmd = MEI_NFC_CMD_HCI_SEND; +	hdr->status = 0; +	hdr->req_id = ndev->req_id; +	hdr->reserved = 0; +	hdr->data_size = length; + +	memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); + +	err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); +	if (err < 0) +		return err; + +	kfree(mei_buf); + +	if (!wait_event_interruptible_timeout(ndev->send_wq, +				ndev->recv_req_id == ndev->req_id, HZ)) { +		dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); +		err = -ETIMEDOUT; +	} else { +		ndev->req_id++; +	} + +	return err; +} + +static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) +{ +	struct mei_nfc_dev *ndev; +	struct mei_nfc_hci_hdr *hci_hdr; +	int received_length; + +	ndev = (struct mei_nfc_dev *)cldev->priv_data; + +	received_length = __mei_cl_recv(ndev->cl, buf, length); +	if (received_length < 0) +		return received_length; + +	hci_hdr = (struct mei_nfc_hci_hdr *) buf; + +	if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) { +		ndev->recv_req_id = hci_hdr->req_id; +		wake_up(&ndev->send_wq); + +		return 0; +	} + +	return received_length; +} + +static struct mei_cl_ops nfc_ops = { +	.enable = mei_nfc_enable, +	.disable = mei_nfc_disable, +	.send = mei_nfc_send, +	.recv = mei_nfc_recv, +}; + +static void mei_nfc_init(struct work_struct *work) +{ +	struct mei_device *dev; +	struct mei_cl_device *cldev; +	struct mei_nfc_dev *ndev; +	struct mei_cl *cl_info; + +	ndev = container_of(work, struct mei_nfc_dev, init_work); + +	cl_info = ndev->cl_info; +	dev = cl_info->dev; + +	mutex_lock(&dev->device_lock); + +	if (mei_cl_connect(cl_info, NULL) < 0) { +		mutex_unlock(&dev->device_lock); +		dev_err(&dev->pdev->dev, +			"Could not connect to the NFC INFO ME client"); + +		goto err; +	} + +	mutex_unlock(&dev->device_lock); + +	if (mei_nfc_if_version(ndev) < 0) { +		dev_err(&dev->pdev->dev, "Could not get the NFC interfave version"); + +		goto err; +	} + +	dev_info(&dev->pdev->dev, +		"NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", +		ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); + +	mutex_lock(&dev->device_lock); + +	if (mei_cl_disconnect(cl_info) < 0) { +		mutex_unlock(&dev->device_lock); +		dev_err(&dev->pdev->dev, +			"Could not disconnect the NFC INFO ME client"); + +		goto err; +	} + +	mutex_unlock(&dev->device_lock); + +	if (mei_nfc_build_bus_name(ndev) < 0) { +		dev_err(&dev->pdev->dev, +			"Could not build the bus ID name\n"); +		return; +	} + +	cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops); +	if (!cldev) { +		dev_err(&dev->pdev->dev, +			"Could not add the NFC device to the MEI bus\n"); + +		goto err; +	} + +	cldev->priv_data = ndev; + + +	return; + +err: +	mei_nfc_free(ndev); + +	return; +} + + +int mei_nfc_host_init(struct mei_device *dev) +{ +	struct mei_nfc_dev *ndev = &nfc_dev; +	struct mei_cl *cl_info, *cl = NULL; +	int i, ret; + +	/* already initialzed */ +	if (ndev->cl_info) +		return 0; + +	cl_info = mei_cl_allocate(dev); +	cl = mei_cl_allocate(dev); + +	if (!cl || !cl_info) { +		ret = -ENOMEM; +		goto err; +	} + +	/* check for valid client id */ +	i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); +	if (i < 0) { +		dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); +		ret = -ENOENT; +		goto err; +	} + +	cl_info->me_client_id = dev->me_clients[i].client_id; + +	ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY); +	if (ret) +		goto err; + +	cl_info->device_uuid = mei_nfc_info_guid; + +	list_add_tail(&cl_info->device_link, &dev->device_list); + +	/* check for valid client id */ +	i = mei_me_cl_by_uuid(dev, &mei_nfc_guid); +	if (i < 0) { +		dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); +		ret = -ENOENT; +		goto err; +	} + +	cl->me_client_id = dev->me_clients[i].client_id; + +	ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); +	if (ret) +		goto err; + +	cl->device_uuid = mei_nfc_guid; + +	list_add_tail(&cl->device_link, &dev->device_list); + +	ndev->cl_info = cl_info; +	ndev->cl = cl; +	ndev->req_id = 1; + +	INIT_WORK(&ndev->init_work, mei_nfc_init); +	init_waitqueue_head(&ndev->send_wq); +	schedule_work(&ndev->init_work); + +	return 0; + +err: +	mei_nfc_free(ndev); + +	return ret; +} + +void mei_nfc_host_exit(void) +{ +	struct mei_nfc_dev *ndev = &nfc_dev; + +	if (ndev->cl && ndev->cl->device) +		mei_cl_remove_device(ndev->cl->device); + +	mei_nfc_free(ndev); +} diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b40ec0601ab..a727464e9c3 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -47,7 +47,7 @@  static struct pci_dev *mei_pdev;  /* mei_pci_tbl - PCI Device ID Table */ -static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { +static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, @@ -86,18 +86,19 @@ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {  	{0, }  }; -MODULE_DEVICE_TABLE(pci, mei_pci_tbl); +MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);  static DEFINE_MUTEX(mei_mutex);  /**   * mei_quirk_probe - probe for devices that doesn't valid ME interface + *   * @pdev: PCI device structure   * @ent: entry into pci_device_table   *   * returns true if ME Interface is valid, false otherwise   */ -static bool mei_quirk_probe(struct pci_dev *pdev, +static bool mei_me_quirk_probe(struct pci_dev *pdev,  				const struct pci_device_id *ent)  {  	u32 reg; @@ -119,7 +120,7 @@ static bool mei_quirk_probe(struct pci_dev *pdev,   *   * returns 0 on success, <0 on failure.   */ -static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  {  	struct mei_device *dev;  	struct mei_me_hw *hw; @@ -127,7 +128,7 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	mutex_lock(&mei_mutex); -	if (!mei_quirk_probe(pdev, ent)) { +	if (!mei_me_quirk_probe(pdev, ent)) {  		err = -ENODEV;  		goto end;  	} @@ -184,20 +185,19 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  		goto disable_msi;  	} -	if (mei_hw_init(dev)) { +	if (mei_start(dev)) {  		dev_err(&pdev->dev, "init hw failure.\n");  		err = -ENODEV;  		goto release_irq;  	} -	err = mei_register(&pdev->dev); +	err = mei_register(dev);  	if (err)  		goto release_irq;  	mei_pdev = pdev;  	pci_set_drvdata(pdev, dev); -  	schedule_delayed_work(&dev->timer_work, HZ);  	mutex_unlock(&mei_mutex); @@ -233,7 +233,7 @@ end:   * mei_remove is called by the PCI subsystem to alert the driver   * that it should release a PCI device.   */ -static void mei_remove(struct pci_dev *pdev) +static void mei_me_remove(struct pci_dev *pdev)  {  	struct mei_device *dev;  	struct mei_me_hw *hw; @@ -247,44 +247,12 @@ static void mei_remove(struct pci_dev *pdev)  	hw = to_me_hw(dev); -	mutex_lock(&dev->device_lock); - -	cancel_delayed_work(&dev->timer_work); -	mei_wd_stop(dev); +	dev_err(&pdev->dev, "stop\n"); +	mei_stop(dev);  	mei_pdev = NULL; -	if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) { -		dev->iamthif_cl.state = MEI_FILE_DISCONNECTING; -		mei_cl_disconnect(&dev->iamthif_cl); -	} -	if (dev->wd_cl.state == MEI_FILE_CONNECTED) { -		dev->wd_cl.state = MEI_FILE_DISCONNECTING; -		mei_cl_disconnect(&dev->wd_cl); -	} - -	/* Unregistering watchdog device */ -	mei_watchdog_unregister(dev); - -	/* remove entry if already in list */ -	dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); - -	if (dev->open_handle_count > 0) -		dev->open_handle_count--; -	mei_cl_unlink(&dev->wd_cl); - -	if (dev->open_handle_count > 0) -		dev->open_handle_count--; -	mei_cl_unlink(&dev->iamthif_cl); - -	dev->iamthif_current_cb = NULL; -	dev->me_clients_num = 0; - -	mutex_unlock(&dev->device_lock); - -	flush_scheduled_work(); -  	/* disable interrupts */  	mei_disable_interrupts(dev); @@ -295,44 +263,37 @@ static void mei_remove(struct pci_dev *pdev)  	if (hw->mem_addr)  		pci_iounmap(pdev, hw->mem_addr); +	mei_deregister(dev); +  	kfree(dev);  	pci_release_regions(pdev);  	pci_disable_device(pdev); -	mei_deregister();  }  #ifdef CONFIG_PM -static int mei_pci_suspend(struct device *device) +static int mei_me_pci_suspend(struct device *device)  {  	struct pci_dev *pdev = to_pci_dev(device);  	struct mei_device *dev = pci_get_drvdata(pdev); -	int err;  	if (!dev)  		return -ENODEV; -	mutex_lock(&dev->device_lock); -	cancel_delayed_work(&dev->timer_work); +	dev_err(&pdev->dev, "suspend\n"); -	/* Stop watchdog if exists */ -	err = mei_wd_stop(dev); -	/* Set new mei state */ -	if (dev->dev_state == MEI_DEV_ENABLED || -	    dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { -		dev->dev_state = MEI_DEV_POWER_DOWN; -		mei_reset(dev, 0); -	} -	mutex_unlock(&dev->device_lock); +	mei_stop(dev); + +	mei_disable_interrupts(dev);  	free_irq(pdev->irq, dev);  	pci_disable_msi(pdev); -	return err; +	return 0;  } -static int mei_pci_resume(struct device *device) +static int mei_me_pci_resume(struct device *device)  {  	struct pci_dev *pdev = to_pci_dev(device);  	struct mei_device *dev; @@ -372,24 +333,24 @@ static int mei_pci_resume(struct device *device)  	return err;  } -static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume); -#define MEI_PM_OPS	(&mei_pm_ops) +static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume); +#define MEI_ME_PM_OPS	(&mei_me_pm_ops)  #else -#define MEI_PM_OPS	NULL +#define MEI_ME_PM_OPS	NULL  #endif /* CONFIG_PM */  /*   *  PCI driver structure   */ -static struct pci_driver mei_driver = { +static struct pci_driver mei_me_driver = {  	.name = KBUILD_MODNAME, -	.id_table = mei_pci_tbl, -	.probe = mei_probe, -	.remove = mei_remove, -	.shutdown = mei_remove, -	.driver.pm = MEI_PM_OPS, +	.id_table = mei_me_pci_tbl, +	.probe = mei_me_probe, +	.remove = mei_me_remove, +	.shutdown = mei_me_remove, +	.driver.pm = MEI_ME_PM_OPS,  }; -module_pci_driver(mei_driver); +module_pci_driver(mei_me_driver);  MODULE_AUTHOR("Intel Corporation");  MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 2413247fc39..6251a4ee706 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -58,6 +58,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)   * mei_wd_host_init - connect to the watchdog client   *   * @dev: the device structure + *   * returns -ENENT if wd client cannot be found   *         -EIO if write has failed   *         0 on success @@ -317,7 +318,8 @@ end:   *   * returns 0 if success, negative errno code for failure   */ -static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout) +static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, +		unsigned int timeout)  {  	struct mei_device *dev; diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c new file mode 100644 index 00000000000..437192e4300 --- /dev/null +++ b/drivers/misc/sram.c @@ -0,0 +1,121 @@ +/* + * Generic on-chip SRAM allocation driver + * + * Copyright (C) 2012 Philipp Zabel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/genalloc.h> + +#define SRAM_GRANULARITY	32 + +struct sram_dev { +	struct gen_pool *pool; +	struct clk *clk; +}; + +static int sram_probe(struct platform_device *pdev) +{ +	void __iomem *virt_base; +	struct sram_dev *sram; +	struct resource *res; +	unsigned long size; +	int ret; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -EINVAL; + +	size = resource_size(res); + +	virt_base = devm_request_and_ioremap(&pdev->dev, res); +	if (!virt_base) +		return -EADDRNOTAVAIL; + +	sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL); +	if (!sram) +		return -ENOMEM; + +	sram->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(sram->clk)) +		sram->clk = NULL; +	else +		clk_prepare_enable(sram->clk); + +	sram->pool = devm_gen_pool_create(&pdev->dev, ilog2(SRAM_GRANULARITY), -1); +	if (!sram->pool) +		return -ENOMEM; + +	ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base, +				res->start, size, -1); +	if (ret < 0) { +		gen_pool_destroy(sram->pool); +		return ret; +	} + +	platform_set_drvdata(pdev, sram); + +	dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base); + +	return 0; +} + +static int sram_remove(struct platform_device *pdev) +{ +	struct sram_dev *sram = platform_get_drvdata(pdev); + +	if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) +		dev_dbg(&pdev->dev, "removed while SRAM allocated\n"); + +	gen_pool_destroy(sram->pool); + +	if (sram->clk) +		clk_disable_unprepare(sram->clk); + +	return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id sram_dt_ids[] = { +	{ .compatible = "mmio-sram" }, +	{} +}; +#endif + +static struct platform_driver sram_driver = { +	.driver = { +		.name = "sram", +		.of_match_table = of_match_ptr(sram_dt_ids), +	}, +	.probe = sram_probe, +	.remove = sram_remove, +}; + +static int __init sram_init(void) +{ +	return platform_driver_register(&sram_driver); +} + +postcore_initcall(sram_init); diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 1e7bc0eb081..1dfde4d543d 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -417,24 +417,26 @@ static int tsl2550_remove(struct i2c_client *client)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg) +static int tsl2550_suspend(struct device *dev)  { -	return tsl2550_set_power_state(client, 0); +	return tsl2550_set_power_state(to_i2c_client(dev), 0);  } -static int tsl2550_resume(struct i2c_client *client) +static int tsl2550_resume(struct device *dev)  { -	return tsl2550_set_power_state(client, 1); +	return tsl2550_set_power_state(to_i2c_client(dev), 1);  } +static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume); +#define TSL2550_PM_OPS (&tsl2550_pm_ops) +  #else -#define tsl2550_suspend		NULL -#define tsl2550_resume		NULL +#define TSL2550_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */  static const struct i2c_device_id tsl2550_id[] = {  	{ "tsl2550", 0 }, @@ -446,9 +448,8 @@ static struct i2c_driver tsl2550_driver = {  	.driver = {  		.name	= TSL2550_DRV_NAME,  		.owner	= THIS_MODULE, +		.pm	= TSL2550_PM_OPS,  	}, -	.suspend = tsl2550_suspend, -	.resume	= tsl2550_resume,  	.probe	= tsl2550_probe,  	.remove	= tsl2550_remove,  	.id_table = tsl2550_id, diff --git a/drivers/misc/vmw_vmci/Kconfig b/drivers/misc/vmw_vmci/Kconfig index 39c2ecadb27..ea98f7e9ccd 100644 --- a/drivers/misc/vmw_vmci/Kconfig +++ b/drivers/misc/vmw_vmci/Kconfig @@ -4,7 +4,7 @@  config VMWARE_VMCI  	tristate "VMware VMCI Driver" -	depends on X86 && PCI +	depends on X86 && PCI && NET  	help  	  This is VMware's Virtual Machine Communication Interface.  It enables  	  high-speed communication between host and guest in a virtual diff --git a/drivers/misc/vmw_vmci/vmci_datagram.c b/drivers/misc/vmw_vmci/vmci_datagram.c index ed5c433cd49..f3cdd904fe4 100644 --- a/drivers/misc/vmw_vmci/vmci_datagram.c +++ b/drivers/misc/vmw_vmci/vmci_datagram.c @@ -42,9 +42,11 @@ struct datagram_entry {  struct delayed_datagram_info {  	struct datagram_entry *entry; -	struct vmci_datagram msg;  	struct work_struct work;  	bool in_dg_host_queue; +	/* msg and msg_payload must be together. */ +	struct vmci_datagram msg; +	u8 msg_payload[];  };  /* Number of in-flight host->host datagrams */  |