Char/Misc driver update for 4.9-rc1

Here's the "big" char and misc driver update for 4.9-rc1.
 
 Lots of little things here, all over the driver tree for subsystems that
 flow through me.  Nothing major that I can discern, full details are in
 the shortlog.
 
 All have been in the linux-next tree with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iFUEABECABYFAlfyOIQPHGdyZWdAa3JvYWguY29tAAoJEDFH1A3bLfsp9OQAlRy3
 gSKfQUlXjTs96Bx/I5PtWysAn0r8nyKZoP1oSgsTddOCEeXngTXc
 =4uPs
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here's the "big" char and misc driver update for 4.9-rc1.

  Lots of little things here, all over the driver tree for subsystems
  that flow through me. Nothing major that I can discern, full details
  are in the shortlog.

  All have been in the linux-next tree with no reported issues"

* tag 'char-misc-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (144 commits)
  drivers/misc/hpilo: Changes to support new security states in iLO5 FW
  at25: fix debug and error messaging
  misc/genwqe: ensure zero initialization
  vme: fake: remove unexpected unlock in fake_master_set()
  vme: fake: mark symbols static where possible
  spmi: pmic-arb: Return an error code if sanity check fails
  Drivers: hv: get rid of id in struct vmbus_channel
  Drivers: hv: make VMBus bus ids persistent
  mcb: Add a dma_device to mcb_device
  mcb: Enable PCI bus mastering by default
  mei: stop the stall timer worker if not needed
  clk: probe common clock drivers earlier
  vme: fake: fix build for 64-bit dma_addr_t
  ttyprintk: Neaten and simplify printing
  mei: me: add kaby point device ids
  coresight: tmc: mark symbols static where possible
  coresight: perf: deal with error condition properly
  Drivers: hv: hv_util: Avoid dynamic allocation in time synch
  fpga manager: Add hardware dependency to Zynq driver
  Drivers: hv: utils: Support TimeSync version 4.0 protocol samples.
  ...
This commit is contained in:
Linus Torvalds 2016-10-03 19:57:49 -07:00
commit 7a53eea1f7
129 changed files with 4040 additions and 2805 deletions

View File

@ -1944,6 +1944,11 @@ E: kraxel@bytesex.org
E: kraxel@suse.de E: kraxel@suse.de
D: video4linux, bttv, vesafb, some scsi, misc fixes D: video4linux, bttv, vesafb, some scsi, misc fixes
N: Hans J. Koch
D: USERSPACE I/O, MAX6650
D: Hans passed away in June 2016, and will be greatly missed.
W: https://lwn.net/Articles/691000/
N: Harald Koenig N: Harald Koenig
E: koenig@tat.physik.uni-tuebingen.de E: koenig@tat.physik.uni-tuebingen.de
D: XFree86 (S3), DCF77, some kernel hacks and fixes D: XFree86 (S3), DCF77, some kernel hacks and fixes

View File

@ -153,7 +153,7 @@ Description:
What: /sys/class/mic/mic(x)/heartbeat_enable What: /sys/class/mic/mic(x)/heartbeat_enable
Date: March 2015 Date: March 2015
KernelVersion: 3.20 KernelVersion: 4.4
Contact: Ashutosh Dixit <ashutosh.dixit@intel.com> Contact: Ashutosh Dixit <ashutosh.dixit@intel.com>
Description: Description:
The MIC drivers detect and inform user space about card crashes The MIC drivers detect and inform user space about card crashes

View File

@ -1,31 +0,0 @@
What: /sys/bus/i2c/devices/<busnum>-<devaddr>/pressure0_input
Date: June 2010
Contact: Christoph Mair <christoph.mair@gmail.com>
Description: Start a pressure measurement and read the result. Values
represent the ambient air pressure in pascal (0.01 millibar).
Reading: returns the current air pressure.
What: /sys/bus/i2c/devices/<busnum>-<devaddr>/temp0_input
Date: June 2010
Contact: Christoph Mair <christoph.mair@gmail.com>
Description: Measure the ambient temperature. The returned value represents
the ambient temperature in units of 0.1 degree celsius.
Reading: returns the current temperature.
What: /sys/bus/i2c/devices/<busnum>-<devaddr>/oversampling
Date: June 2010
Contact: Christoph Mair <christoph.mair@gmail.com>
Description: Tell the bmp085 to use more samples to calculate a pressure
value. When writing to this file the chip will use 2^x samples
to calculate the next pressure value with x being the value
written. Using this feature will decrease RMS noise and
increase the measurement time.
Reading: returns the current oversampling setting.
Writing: sets a new oversampling setting.
Accepted values: 0..3.

View File

@ -1,11 +1,20 @@
= Rockchip eFuse device tree bindings = = Rockchip eFuse device tree bindings =
Required properties: Required properties:
- compatible: Should be "rockchip,rockchip-efuse" - compatible: Should be one of the following.
- "rockchip,rk3066a-efuse" - for RK3066a SoCs.
- "rockchip,rk3188-efuse" - for RK3188 SoCs.
- "rockchip,rk3288-efuse" - for RK3288 SoCs.
- "rockchip,rk3399-efuse" - for RK3399 SoCs.
- reg: Should contain the registers location and exact eFuse size - reg: Should contain the registers location and exact eFuse size
- clocks: Should be the clock id of eFuse - clocks: Should be the clock id of eFuse
- clock-names: Should be "pclk_efuse" - clock-names: Should be "pclk_efuse"
Deprecated properties:
- compatible: "rockchip,rockchip-efuse"
Old efuse compatible value compatible to rk3066a, rk3188 and rk3288
efuses
= Data cells = = Data cells =
Are child nodes of eFuse, bindings of which as described in Are child nodes of eFuse, bindings of which as described in
bindings/nvmem/nvmem.txt bindings/nvmem/nvmem.txt
@ -13,7 +22,7 @@ bindings/nvmem/nvmem.txt
Example: Example:
efuse: efuse@ffb40000 { efuse: efuse@ffb40000 {
compatible = "rockchip,rockchip-efuse"; compatible = "rockchip,rk3288-efuse";
reg = <0xffb40000 0x20>; reg = <0xffb40000 0x20>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;

View File

@ -8,13 +8,14 @@ As with other subsystems within the Linux kernel, VME device drivers register
with the VME subsystem, typically called from the devices init routine. This is with the VME subsystem, typically called from the devices init routine. This is
achieved via a call to the following function: achieved via a call to the following function:
int vme_register_driver (struct vme_driver *driver); int vme_register_driver (struct vme_driver *driver, unsigned int ndevs);
If driver registration is successful this function returns zero, if an error If driver registration is successful this function returns zero, if an error
occurred a negative error code will be returned. occurred a negative error code will be returned.
A pointer to a structure of type 'vme_driver' must be provided to the A pointer to a structure of type 'vme_driver' must be provided to the
registration function. The structure is as follows: registration function. Along with ndevs, which is the number of devices your
driver is able to support. The structure is as follows:
struct vme_driver { struct vme_driver {
struct list_head node; struct list_head node;
@ -32,8 +33,8 @@ At the minimum, the '.name', '.match' and '.probe' elements of this structure
should be correctly set. The '.name' element is a pointer to a string holding should be correctly set. The '.name' element is a pointer to a string holding
the device driver's name. the device driver's name.
The '.match' function allows controlling the number of devices that need to The '.match' function allows control over which VME devices should be registered
be registered. The match function should return 1 if a device should be with the driver. The match function should return 1 if a device should be
probed and 0 otherwise. This example match function (from vme_user.c) limits probed and 0 otherwise. This example match function (from vme_user.c) limits
the number of devices probed to one: the number of devices probed to one:
@ -385,13 +386,13 @@ location monitor location. Each location monitor can monitor a number of
adjacent locations: adjacent locations:
int vme_lm_attach(struct vme_resource *res, int num, int vme_lm_attach(struct vme_resource *res, int num,
void (*callback)(int)); void (*callback)(void *));
int vme_lm_detach(struct vme_resource *res, int num); int vme_lm_detach(struct vme_resource *res, int num);
The callback function is declared as follows. The callback function is declared as follows.
void callback(int num); void callback(void *data);
Slot Detection Slot Detection

View File

@ -7458,9 +7458,8 @@ F: Documentation/hwmon/max20751
F: drivers/hwmon/max20751.c F: drivers/hwmon/max20751.c
MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
M: "Hans J. Koch" <hjk@hansjkoch.de>
L: linux-hwmon@vger.kernel.org L: linux-hwmon@vger.kernel.org
S: Maintained S: Orphan
F: Documentation/hwmon/max6650 F: Documentation/hwmon/max6650
F: drivers/hwmon/max6650.c F: drivers/hwmon/max6650.c
@ -12418,7 +12417,6 @@ F: fs/hostfs/
F: fs/hppfs/ F: fs/hppfs/
USERSPACE I/O (UIO) USERSPACE I/O (UIO)
M: "Hans J. Koch" <hjk@hansjkoch.de>
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git

View File

@ -56,16 +56,4 @@ static struct miscdevice bL_switcher_device = {
"b.L_switcher", "b.L_switcher",
&bL_switcher_fops &bL_switcher_fops
}; };
module_misc_device(bL_switcher_device);
static int __init bL_switcher_dummy_if_init(void)
{
return misc_register(&bL_switcher_device);
}
static void __exit bL_switcher_dummy_if_exit(void)
{
misc_deregister(&bL_switcher_device);
}
module_init(bL_switcher_dummy_if_init);
module_exit(bL_switcher_dummy_if_exit);

View File

@ -59,18 +59,7 @@ static struct miscdevice coreb_dev = {
.name = "coreb", .name = "coreb",
.fops = &coreb_fops, .fops = &coreb_fops,
}; };
module_misc_device(coreb_dev);
static int __init bf561_coreb_init(void)
{
return misc_register(&coreb_dev);
}
module_init(bf561_coreb_init);
static void __exit bf561_coreb_exit(void)
{
misc_deregister(&coreb_dev);
}
module_exit(bf561_coreb_exit);
MODULE_AUTHOR("Bas Vermeulen <bvermeul@blackstar.xs4all.nl>"); MODULE_AUTHOR("Bas Vermeulen <bvermeul@blackstar.xs4all.nl>");
MODULE_DESCRIPTION("BF561 Core B Support"); MODULE_DESCRIPTION("BF561 Core B Support");

View File

@ -175,27 +175,4 @@ static struct miscdevice harddog_miscdev = {
.name = "watchdog", .name = "watchdog",
.fops = &harddog_fops, .fops = &harddog_fops,
}; };
module_misc_device(harddog_miscdev);
static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n";
static int __init harddog_init(void)
{
int ret;
ret = misc_register(&harddog_miscdev);
if (ret)
return ret;
printk(banner);
return 0;
}
static void __exit harddog_exit(void)
{
misc_deregister(&harddog_miscdev);
}
module_init(harddog_init);
module_exit(harddog_exit);

View File

@ -29,6 +29,8 @@ obj-$(CONFIG_SFI) += sfi/
# was used and do nothing if so # was used and do nothing if so
obj-$(CONFIG_PNP) += pnp/ obj-$(CONFIG_PNP) += pnp/
obj-y += amba/ obj-y += amba/
obj-y += clk/
# Many drivers will want to use DMA so this has to be made available # Many drivers will want to use DMA so this has to be made available
# really early. # really early.
obj-$(CONFIG_DMADEVICES) += dma/ obj-$(CONFIG_DMADEVICES) += dma/
@ -142,8 +144,6 @@ obj-$(CONFIG_VHOST) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_STAGING) += staging/ obj-$(CONFIG_STAGING) += staging/
obj-y += platform/ obj-y += platform/
#common clk code
obj-y += clk/
obj-$(CONFIG_MAILBOX) += mailbox/ obj-$(CONFIG_MAILBOX) += mailbox/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/

View File

@ -377,21 +377,7 @@ static struct miscdevice vhci_miscdev = {
.fops = &vhci_fops, .fops = &vhci_fops,
.minor = VHCI_MINOR, .minor = VHCI_MINOR,
}; };
module_misc_device(vhci_miscdev);
static int __init vhci_init(void)
{
BT_INFO("Virtual HCI driver ver %s", VERSION);
return misc_register(&vhci_miscdev);
}
static void __exit vhci_exit(void)
{
misc_deregister(&vhci_miscdev);
}
module_init(vhci_init);
module_exit(vhci_exit);
module_param(amp, bool, 0644); module_param(amp, bool, 0644);
MODULE_PARM_DESC(amp, "Create AMP controller device"); MODULE_PARM_DESC(amp, "Create AMP controller device");

View File

@ -230,45 +230,7 @@ static struct miscdevice bfin_otp_misc_device = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.fops = &bfin_otp_fops, .fops = &bfin_otp_fops,
}; };
module_misc_device(bfin_otp_misc_device);
/**
* bfin_otp_init - Initialize module
*
* Registers the device and notifier handler. Actual device
* initialization is handled by bfin_otp_open().
*/
static int __init bfin_otp_init(void)
{
int ret;
stampit();
ret = misc_register(&bfin_otp_misc_device);
if (ret) {
pr_init(KERN_ERR PFX "unable to register a misc device\n");
return ret;
}
pr_init(KERN_INFO PFX "initialized\n");
return 0;
}
/**
* bfin_otp_exit - Deinitialize module
*
* Unregisters the device and notifier handler. Actual device
* deinitialization is handled by bfin_otp_close().
*/
static void __exit bfin_otp_exit(void)
{
stampit();
misc_deregister(&bfin_otp_misc_device);
}
module_init(bfin_otp_init);
module_exit(bfin_otp_exit);
MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
MODULE_DESCRIPTION("Blackfin OTP Memory Interface"); MODULE_DESCRIPTION("Blackfin OTP Memory Interface");

View File

@ -381,6 +381,9 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
int err = 0; int err = 0;
if (!pfn_valid(PFN_DOWN(p)))
return -EIO;
read = 0; read = 0;
if (p < (unsigned long) high_memory) { if (p < (unsigned long) high_memory) {
low_count = count; low_count = count;
@ -509,6 +512,9 @@ static ssize_t write_kmem(struct file *file, const char __user *buf,
char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
int err = 0; int err = 0;
if (!pfn_valid(PFN_DOWN(p)))
return -EIO;
if (p < (unsigned long) high_memory) { if (p < (unsigned long) high_memory) {
unsigned long to_write = min_t(unsigned long, count, unsigned long to_write = min_t(unsigned long, count,
(unsigned long)high_memory - p); (unsigned long)high_memory - p);

View File

@ -124,7 +124,7 @@ static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex,
MKBYTE(rSlaveControl)); MKBYTE(rSlaveControl));
rSlaveControl_Save = rSlaveControl; rSlaveControl_Save = rSlaveControl;
rSlaveControl.ConfigMode = TRUE; rSlaveControl.ConfigMode = true;
PRINTK_2(TRACE_3780I, PRINTK_2(TRACE_3780I,
"3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n", "3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n",
@ -155,7 +155,7 @@ unsigned char dsp3780I_ReadGenCfg(unsigned short usDspBaseIO,
MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl); MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl);
rSlaveControl_Save = rSlaveControl; rSlaveControl_Save = rSlaveControl;
rSlaveControl.ConfigMode = TRUE; rSlaveControl.ConfigMode = true;
OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl)); OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl));
OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex); OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex);
ucValue = InByteDsp(DSP_ConfigData); ucValue = InByteDsp(DSP_ConfigData);
@ -230,7 +230,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
rUartCfg1.BaseIO = 3; rUartCfg1.BaseIO = 3;
break; break;
} }
rUartCfg2.Enable = TRUE; rUartCfg2.Enable = true;
} }
rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0; rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0;
@ -238,7 +238,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse; rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse;
rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq]; rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq];
rHBridgeCfg1.AccessMode = 1; rHBridgeCfg1.AccessMode = 1;
rHBridgeCfg2.Enable = TRUE; rHBridgeCfg2.Enable = true;
rBusmasterCfg2.Reserved = 0; rBusmasterCfg2.Reserved = 0;
@ -278,8 +278,8 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
* soft-reset active for 10ms. * soft-reset active for 10ms.
*/ */
rSlaveControl.ClockControl = 0; rSlaveControl.ClockControl = 0;
rSlaveControl.SoftReset = TRUE; rSlaveControl.SoftReset = true;
rSlaveControl.ConfigMode = FALSE; rSlaveControl.ConfigMode = false;
rSlaveControl.Reserved = 0; rSlaveControl.Reserved = 0;
PRINTK_4(TRACE_3780I, PRINTK_4(TRACE_3780I,
@ -302,7 +302,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
for (i = 0; i < 11; i++) for (i = 0; i < 11; i++)
udelay(2000); udelay(2000);
rSlaveControl.SoftReset = FALSE; rSlaveControl.SoftReset = false;
OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl); MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
@ -326,10 +326,10 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
} }
rHBridgeControl.EnableDspInt = FALSE; rHBridgeControl.EnableDspInt = false;
rHBridgeControl.MemAutoInc = TRUE; rHBridgeControl.MemAutoInc = true;
rHBridgeControl.IoAutoInc = FALSE; rHBridgeControl.IoAutoInc = false;
rHBridgeControl.DiagnosticMode = FALSE; rHBridgeControl.DiagnosticMode = false;
PRINTK_3(TRACE_3780I, PRINTK_3(TRACE_3780I,
"3780i::dsp3780i_EnableDSP DSP_HBridgeControl %x rHBridgeControl %x\n", "3780i::dsp3780i_EnableDSP DSP_HBridgeControl %x rHBridgeControl %x\n",
@ -345,7 +345,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
ChipID = ReadMsaCfg(DSP_ChipID); ChipID = ReadMsaCfg(DSP_ChipID);
PRINTK_2(TRACE_3780I, PRINTK_2(TRACE_3780I,
"3780i::dsp3780I_EnableDSP exiting bRC=TRUE, ChipID %x\n", "3780i::dsp3780I_EnableDSP exiting bRC=true, ChipID %x\n",
ChipID); ChipID);
return 0; return 0;
@ -361,8 +361,8 @@ int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings)
PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n"); PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n");
rSlaveControl.ClockControl = 0; rSlaveControl.ClockControl = 0;
rSlaveControl.SoftReset = TRUE; rSlaveControl.SoftReset = true;
rSlaveControl.ConfigMode = FALSE; rSlaveControl.ConfigMode = false;
rSlaveControl.Reserved = 0; rSlaveControl.Reserved = 0;
spin_lock_irqsave(&dsp_lock, flags); spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
@ -398,14 +398,14 @@ int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings)
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rHBridgeControl %x\n", PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rHBridgeControl %x\n",
MKWORD(rHBridgeControl)); MKWORD(rHBridgeControl));
rHBridgeControl.EnableDspInt = FALSE; rHBridgeControl.EnableDspInt = false;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags); spin_unlock_irqrestore(&dsp_lock, flags);
/* Reset the core via the boot domain register */ /* Reset the core via the boot domain register */
rBootDomain.ResetCore = TRUE; rBootDomain.ResetCore = true;
rBootDomain.Halt = TRUE; rBootDomain.Halt = true;
rBootDomain.NMI = TRUE; rBootDomain.NMI = true;
rBootDomain.Reserved = 0; rBootDomain.Reserved = 0;
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rBootDomain %x\n", PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rBootDomain %x\n",
@ -438,26 +438,26 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
/* Transition the core to a running state */ /* Transition the core to a running state */
rBootDomain.ResetCore = TRUE; rBootDomain.ResetCore = true;
rBootDomain.Halt = FALSE; rBootDomain.Halt = false;
rBootDomain.NMI = TRUE; rBootDomain.NMI = true;
rBootDomain.Reserved = 0; rBootDomain.Reserved = 0;
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
udelay(5); udelay(5);
rBootDomain.ResetCore = FALSE; rBootDomain.ResetCore = false;
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
udelay(5); udelay(5);
rBootDomain.NMI = FALSE; rBootDomain.NMI = false;
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
udelay(5); udelay(5);
/* Enable DSP to PC interrupt */ /* Enable DSP to PC interrupt */
spin_lock_irqsave(&dsp_lock, flags); spin_lock_irqsave(&dsp_lock, flags);
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
rHBridgeControl.EnableDspInt = TRUE; rHBridgeControl.EnableDspInt = true;
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n", PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n",
MKWORD(rHBridgeControl)); MKWORD(rHBridgeControl));
@ -466,7 +466,7 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
spin_unlock_irqrestore(&dsp_lock, flags); spin_unlock_irqrestore(&dsp_lock, flags);
PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=TRUE\n"); PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=true\n");
return 0; return 0;
} }
@ -508,7 +508,7 @@ int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
PRINTK_1(TRACE_3780I, PRINTK_1(TRACE_3780I,
"3780I::dsp3780I_ReadDStore exit bRC=TRUE\n"); "3780I::dsp3780I_ReadDStore exit bRC=true\n");
return 0; return 0;
} }
@ -550,7 +550,7 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
PRINTK_1(TRACE_3780I, PRINTK_1(TRACE_3780I,
"3780I::dsp3780I_ReadAndClearDStore exit bRC=TRUE\n"); "3780I::dsp3780I_ReadAndClearDStore exit bRC=true\n");
return 0; return 0;
} }
@ -592,7 +592,7 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
PRINTK_1(TRACE_3780I, PRINTK_1(TRACE_3780I,
"3780I::dsp3780D_WriteDStore exit bRC=TRUE\n"); "3780I::dsp3780D_WriteDStore exit bRC=true\n");
return 0; return 0;
} }
@ -640,7 +640,7 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
} }
PRINTK_1(TRACE_3780I, PRINTK_1(TRACE_3780I,
"3780I::dsp3780I_ReadIStore exit bRC=TRUE\n"); "3780I::dsp3780I_ReadIStore exit bRC=true\n");
return 0; return 0;
} }
@ -689,7 +689,7 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
} }
PRINTK_1(TRACE_3780I, PRINTK_1(TRACE_3780I,
"3780I::dsp3780I_WriteIStore exit bRC=TRUE\n"); "3780I::dsp3780I_WriteIStore exit bRC=true\n");
return 0; return 0;
} }
@ -713,7 +713,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
*/ */
spin_lock_irqsave(&dsp_lock, flags); spin_lock_irqsave(&dsp_lock, flags);
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
rHBridgeControl.EnableDspInt = FALSE; rHBridgeControl.EnableDspInt = false;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
*pusIPCSource = InWordDsp(DSP_Interrupt); *pusIPCSource = InWordDsp(DSP_Interrupt);
@ -725,7 +725,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource)); OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource));
rHBridgeControl.EnableDspInt = TRUE; rHBridgeControl.EnableDspInt = true;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags); spin_unlock_irqrestore(&dsp_lock, flags);

View File

@ -101,7 +101,7 @@ typedef struct {
} DSP_UART_CFG_1; } DSP_UART_CFG_1;
typedef struct { typedef struct {
unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=FALSE, 1=TRUE */ unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */ unsigned char Reserved:7; /* 0: Reserved */
} DSP_UART_CFG_2; } DSP_UART_CFG_2;
@ -114,7 +114,7 @@ typedef struct {
} DSP_HBRIDGE_CFG_1; } DSP_HBRIDGE_CFG_1;
typedef struct { typedef struct {
unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=FALSE, 1=TRUE */ unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */ unsigned char Reserved:7; /* 0: Reserved */
} DSP_HBRIDGE_CFG_2; } DSP_HBRIDGE_CFG_2;
@ -133,12 +133,12 @@ typedef struct {
typedef struct { typedef struct {
unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=FALSE, 1=TRUE */ unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */ unsigned char Reserved:7; /* 0: Reserved */
} DSP_ISA_PROT_CFG; } DSP_ISA_PROT_CFG;
typedef struct { typedef struct {
unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=FALSE, 1=TRUE */ unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */ unsigned char Reserved:7; /* 0: Reserved */
} DSP_POWER_MGMT_CFG; } DSP_POWER_MGMT_CFG;

View File

@ -296,8 +296,8 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
pDrvData->IPCs[ipcnum].usIntCount); pDrvData->IPCs[ipcnum].usIntCount);
mutex_lock(&mwave_mutex); mutex_lock(&mwave_mutex);
pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsHere = false;
pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; pDrvData->IPCs[ipcnum].bIsEnabled = true;
mutex_unlock(&mwave_mutex); mutex_unlock(&mwave_mutex);
PRINTK_2(TRACE_MWAVE, PRINTK_2(TRACE_MWAVE,
@ -324,7 +324,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
pDrvData->IPCs[ipcnum].usIntCount); pDrvData->IPCs[ipcnum].usIntCount);
mutex_lock(&mwave_mutex); mutex_lock(&mwave_mutex);
if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
PRINTK_2(TRACE_MWAVE, PRINTK_2(TRACE_MWAVE,
@ -332,7 +332,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
" ipc %x going to sleep\n", " ipc %x going to sleep\n",
ipcnum); ipcnum);
add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
pDrvData->IPCs[ipcnum].bIsHere = TRUE; pDrvData->IPCs[ipcnum].bIsHere = true;
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
/* check whether an event was signalled by */ /* check whether an event was signalled by */
/* the interrupt handler while we were gone */ /* the interrupt handler while we were gone */
@ -355,7 +355,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
" application\n", " application\n",
ipcnum); ipcnum);
} }
pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsHere = false;
remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
PRINTK_2(TRACE_MWAVE, PRINTK_2(TRACE_MWAVE,
@ -384,9 +384,9 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
return -EINVAL; return -EINVAL;
} }
mutex_lock(&mwave_mutex); mutex_lock(&mwave_mutex);
if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
pDrvData->IPCs[ipcnum].bIsEnabled = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = false;
if (pDrvData->IPCs[ipcnum].bIsHere == TRUE) { if (pDrvData->IPCs[ipcnum].bIsHere == true) {
wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue); wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
} }
} }
@ -541,7 +541,7 @@ static void mwave_exit(void)
if (pDrvData->device_registered) { if (pDrvData->device_registered) {
device_unregister(&mwave_device); device_unregister(&mwave_device);
pDrvData->device_registered = FALSE; pDrvData->device_registered = false;
} }
#endif #endif
@ -576,16 +576,16 @@ static int __init mwave_init(void)
memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA)); memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA));
pDrvData->bBDInitialized = FALSE; pDrvData->bBDInitialized = false;
pDrvData->bResourcesClaimed = FALSE; pDrvData->bResourcesClaimed = false;
pDrvData->bDSPEnabled = FALSE; pDrvData->bDSPEnabled = false;
pDrvData->bDSPReset = FALSE; pDrvData->bDSPReset = false;
pDrvData->bMwaveDevRegistered = FALSE; pDrvData->bMwaveDevRegistered = false;
pDrvData->sLine = -1; pDrvData->sLine = -1;
for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) { for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
pDrvData->IPCs[i].bIsEnabled = FALSE; pDrvData->IPCs[i].bIsEnabled = false;
pDrvData->IPCs[i].bIsHere = FALSE; pDrvData->IPCs[i].bIsHere = false;
pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */ pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */
init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue); init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue);
} }
@ -601,7 +601,7 @@ static int __init mwave_init(void)
" Failed to initialize board data\n"); " Failed to initialize board data\n");
goto cleanup_error; goto cleanup_error;
} }
pDrvData->bBDInitialized = TRUE; pDrvData->bBDInitialized = true;
retval = tp3780I_CalcResources(&pDrvData->rBDData); retval = tp3780I_CalcResources(&pDrvData->rBDData);
PRINTK_2(TRACE_MWAVE, PRINTK_2(TRACE_MWAVE,
@ -626,7 +626,7 @@ static int __init mwave_init(void)
" Failed to claim resources\n"); " Failed to claim resources\n");
goto cleanup_error; goto cleanup_error;
} }
pDrvData->bResourcesClaimed = TRUE; pDrvData->bResourcesClaimed = true;
retval = tp3780I_EnableDSP(&pDrvData->rBDData); retval = tp3780I_EnableDSP(&pDrvData->rBDData);
PRINTK_2(TRACE_MWAVE, PRINTK_2(TRACE_MWAVE,
@ -639,7 +639,7 @@ static int __init mwave_init(void)
" Failed to enable DSP\n"); " Failed to enable DSP\n");
goto cleanup_error; goto cleanup_error;
} }
pDrvData->bDSPEnabled = TRUE; pDrvData->bDSPEnabled = true;
if (misc_register(&mwave_misc_dev) < 0) { if (misc_register(&mwave_misc_dev) < 0) {
PRINTK_ERROR(KERN_ERR_MWAVE PRINTK_ERROR(KERN_ERR_MWAVE
@ -647,7 +647,7 @@ static int __init mwave_init(void)
" Failed to register misc device\n"); " Failed to register misc device\n");
goto cleanup_error; goto cleanup_error;
} }
pDrvData->bMwaveDevRegistered = TRUE; pDrvData->bMwaveDevRegistered = true;
pDrvData->sLine = register_serial_portandirq( pDrvData->sLine = register_serial_portandirq(
pDrvData->rBDData.rDspSettings.usUartBaseIO, pDrvData->rBDData.rDspSettings.usUartBaseIO,
@ -668,7 +668,7 @@ static int __init mwave_init(void)
if (device_register(&mwave_device)) if (device_register(&mwave_device))
goto cleanup_error; goto cleanup_error;
pDrvData->device_registered = TRUE; pDrvData->device_registered = true;
for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) { for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) {
if(device_create_file(&mwave_device, mwave_dev_attrs[i])) { if(device_create_file(&mwave_device, mwave_dev_attrs[i])) {
PRINTK_ERROR(KERN_ERR_MWAVE PRINTK_ERROR(KERN_ERR_MWAVE

View File

@ -125,8 +125,8 @@ extern int mwave_uart_io;
typedef struct _MWAVE_IPC { typedef struct _MWAVE_IPC {
unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */ unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */
BOOLEAN bIsEnabled; bool bIsEnabled;
BOOLEAN bIsHere; bool bIsHere;
/* entry spin lock */ /* entry spin lock */
wait_queue_head_t ipc_wait_queue; wait_queue_head_t ipc_wait_queue;
} MWAVE_IPC; } MWAVE_IPC;
@ -135,12 +135,12 @@ typedef struct _MWAVE_DEVICE_DATA {
THINKPAD_BD_DATA rBDData; /* board driver's data area */ THINKPAD_BD_DATA rBDData; /* board driver's data area */
unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */ unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */
unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */ unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */
BOOLEAN bBDInitialized; bool bBDInitialized;
BOOLEAN bResourcesClaimed; bool bResourcesClaimed;
BOOLEAN bDSPEnabled; bool bDSPEnabled;
BOOLEAN bDSPReset; bool bDSPReset;
MWAVE_IPC IPCs[16]; MWAVE_IPC IPCs[16];
BOOLEAN bMwaveDevRegistered; bool bMwaveDevRegistered;
short sLine; short sLine;
int nr_registered_attrs; int nr_registered_attrs;
int device_registered; int device_registered;

View File

@ -493,7 +493,7 @@ exit_smapi_request_error:
} }
int smapi_set_DSP_power_state(BOOLEAN bOn) int smapi_set_DSP_power_state(bool bOn)
{ {
int bRC = -EIO; int bRC = -EIO;
unsigned short usAX, usBX, usCX, usDX, usDI, usSI; unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
@ -556,7 +556,7 @@ int smapi_init(void)
PRINTK_ERROR("smapi::smapi_init, ERROR unable to read from SMAPI port\n"); PRINTK_ERROR("smapi::smapi_init, ERROR unable to read from SMAPI port\n");
} else { } else {
PRINTK_2(TRACE_SMAPI, PRINTK_2(TRACE_SMAPI,
"smapi::smapi_init, exit TRUE g_usSmapiPort %x\n", "smapi::smapi_init, exit true g_usSmapiPort %x\n",
g_usSmapiPort); g_usSmapiPort);
retval = 0; retval = 0;
//SmapiQuerySystemID(); //SmapiQuerySystemID();

View File

@ -49,10 +49,6 @@
#ifndef _LINUX_SMAPI_H #ifndef _LINUX_SMAPI_H
#define _LINUX_SMAPI_H #define _LINUX_SMAPI_H
#define TRUE 1
#define FALSE 0
#define BOOLEAN int
typedef struct { typedef struct {
int bDSPPresent; int bDSPPresent;
int bDSPEnabled; int bDSPEnabled;
@ -74,7 +70,7 @@ typedef struct {
int smapi_init(void); int smapi_init(void);
int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings); int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings);
int smapi_set_DSP_cfg(void); int smapi_set_DSP_cfg(void);
int smapi_set_DSP_power_state(BOOLEAN bOn); int smapi_set_DSP_power_state(bool bOn);
#endif #endif

View File

@ -80,13 +80,13 @@ static void EnableSRAM(THINKPAD_BD_DATA * pBDData)
WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode)); WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode));
MKWORD(rGpioDriverEnable) = 0; MKWORD(rGpioDriverEnable) = 0;
rGpioDriverEnable.Enable10 = TRUE; rGpioDriverEnable.Enable10 = true;
rGpioDriverEnable.Mask10 = TRUE; rGpioDriverEnable.Mask10 = true;
WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable)); WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable));
MKWORD(rGpioOutputData) = 0; MKWORD(rGpioOutputData) = 0;
rGpioOutputData.Latch10 = 0; rGpioOutputData.Latch10 = 0;
rGpioOutputData.Mask10 = TRUE; rGpioOutputData.Mask10 = true;
WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData)); WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData));
PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM exit\n"); PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM exit\n");
@ -127,7 +127,7 @@ static irqreturn_t DspInterrupt(int irq, void *dev_id)
PRINTK_2(TRACE_TP3780I, PRINTK_2(TRACE_TP3780I,
"tp3780i::DspInterrupt usIntCount %x\n", "tp3780i::DspInterrupt usIntCount %x\n",
pDrvData->IPCs[usPCNum - 1].usIntCount); pDrvData->IPCs[usPCNum - 1].usIntCount);
if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == TRUE) { if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) {
PRINTK_2(TRACE_TP3780I, PRINTK_2(TRACE_TP3780I,
"tp3780i::DspInterrupt, waking up usPCNum %x\n", "tp3780i::DspInterrupt, waking up usPCNum %x\n",
usPCNum - 1); usPCNum - 1);
@ -160,8 +160,8 @@ int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData)
PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData entry pBDData %p\n", pBDData); PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData entry pBDData %p\n", pBDData);
pBDData->bDSPEnabled = FALSE; pBDData->bDSPEnabled = false;
pSettings->bInterruptClaimed = FALSE; pSettings->bInterruptClaimed = false;
retval = smapi_init(); retval = smapi_init();
if (retval) { if (retval) {
@ -269,7 +269,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
if (pSettings->bInterruptClaimed) { if (pSettings->bInterruptClaimed) {
free_irq(pSettings->usDspIrq, NULL); free_irq(pSettings->usDspIrq, NULL);
pSettings->bInterruptClaimed = FALSE; pSettings->bInterruptClaimed = false;
} }
PRINTK_2(TRACE_TP3780I, PRINTK_2(TRACE_TP3780I,
@ -283,7 +283,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData) int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
{ {
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
BOOLEAN bDSPPoweredUp = FALSE, bInterruptAllocated = FALSE; bool bDSPPoweredUp = false, bInterruptAllocated = false;
PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP entry pBDData %p\n", pBDData); PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP entry pBDData %p\n", pBDData);
@ -336,14 +336,14 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
} }
} }
pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = TRUE; pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true;
pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = TRUE; pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true;
if (pBDData->bShareDspIrq) { if (pBDData->bShareDspIrq) {
pSettings->bDspIrqActiveLow = FALSE; pSettings->bDspIrqActiveLow = false;
} }
if (pBDData->bShareUartIrq) { if (pBDData->bShareUartIrq) {
pSettings->bUartIrqActiveLow = FALSE; pSettings->bUartIrqActiveLow = false;
} }
pSettings->usNumTransfers = TP_CFG_NumTransfers; pSettings->usNumTransfers = TP_CFG_NumTransfers;
@ -373,16 +373,16 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
PRINTK_3(TRACE_TP3780I, PRINTK_3(TRACE_TP3780I,
"tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n", "tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n",
pSettings->usDspIrq, pBDData->bShareDspIrq); pSettings->usDspIrq, pBDData->bShareDspIrq);
bInterruptAllocated = TRUE; bInterruptAllocated = true;
pSettings->bInterruptClaimed = TRUE; pSettings->bInterruptClaimed = true;
} }
smapi_set_DSP_power_state(FALSE); smapi_set_DSP_power_state(false);
if (smapi_set_DSP_power_state(TRUE)) { if (smapi_set_DSP_power_state(true)) {
PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(TRUE) failed\n"); PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(true) failed\n");
goto exit_cleanup; goto exit_cleanup;
} else { } else {
bDSPPoweredUp = TRUE; bDSPPoweredUp = true;
} }
if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) { if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) {
@ -392,7 +392,7 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
EnableSRAM(pBDData); EnableSRAM(pBDData);
pBDData->bDSPEnabled = TRUE; pBDData->bDSPEnabled = true;
PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n"); PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n");
@ -401,10 +401,10 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
exit_cleanup: exit_cleanup:
PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n"); PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n");
if (bDSPPoweredUp) if (bDSPPoweredUp)
smapi_set_DSP_power_state(FALSE); smapi_set_DSP_power_state(false);
if (bInterruptAllocated) { if (bInterruptAllocated) {
free_irq(pSettings->usDspIrq, NULL); free_irq(pSettings->usDspIrq, NULL);
pSettings->bInterruptClaimed = FALSE; pSettings->bInterruptClaimed = false;
} }
return -EIO; return -EIO;
} }
@ -421,10 +421,10 @@ int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData)
dsp3780I_DisableDSP(&pBDData->rDspSettings); dsp3780I_DisableDSP(&pBDData->rDspSettings);
if (pSettings->bInterruptClaimed) { if (pSettings->bInterruptClaimed) {
free_irq(pSettings->usDspIrq, NULL); free_irq(pSettings->usDspIrq, NULL);
pSettings->bInterruptClaimed = FALSE; pSettings->bInterruptClaimed = false;
} }
smapi_set_DSP_power_state(FALSE); smapi_set_DSP_power_state(false);
pBDData->bDSPEnabled = FALSE; pBDData->bDSPEnabled = false;
} }
PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP exit retval %x\n", retval); PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP exit retval %x\n", retval);
@ -516,7 +516,7 @@ int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
int retval = 0; int retval = 0;
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO; unsigned short usDspBaseIO = pSettings->usDspBaseIO;
BOOLEAN bRC = 0; bool bRC = 0;
PRINTK_6(TRACE_TP3780I, PRINTK_6(TRACE_TP3780I,
"tp3780i::tp3780I_ReadWriteDspDStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n", "tp3780i::tp3780I_ReadWriteDspDStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",
@ -552,7 +552,7 @@ int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
int retval = 0; int retval = 0;
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings; DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO; unsigned short usDspBaseIO = pSettings->usDspBaseIO;
BOOLEAN bRC = 0; bool bRC = 0;
PRINTK_6(TRACE_TP3780I, PRINTK_6(TRACE_TP3780I,
"tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n", "tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",

View File

@ -286,7 +286,7 @@ static int register_device(int minor, struct pp_struct *pp)
struct parport *port; struct parport *port;
struct pardevice *pdev = NULL; struct pardevice *pdev = NULL;
char *name; char *name;
int fl; struct pardev_cb ppdev_cb;
name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor); name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
if (name == NULL) if (name == NULL)
@ -299,9 +299,11 @@ static int register_device(int minor, struct pp_struct *pp)
return -ENXIO; return -ENXIO;
} }
fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; memset(&ppdev_cb, 0, sizeof(ppdev_cb));
pdev = parport_register_device(port, name, NULL, ppdev_cb.irq_func = pp_irq;
NULL, pp_irq, fl, pp); ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
ppdev_cb.private = pp;
pdev = parport_register_dev_model(port, name, &ppdev_cb, minor);
parport_put_port(port); parport_put_port(port);
if (!pdev) { if (!pdev) {
@ -799,10 +801,23 @@ static void pp_detach(struct parport *port)
device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number)); device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
} }
static int pp_probe(struct pardevice *par_dev)
{
struct device_driver *drv = par_dev->dev.driver;
int len = strlen(drv->name);
if (strncmp(par_dev->name, drv->name, len))
return -ENODEV;
return 0;
}
static struct parport_driver pp_driver = { static struct parport_driver pp_driver = {
.name = CHRDEV, .name = CHRDEV,
.attach = pp_attach, .probe = pp_probe,
.match_port = pp_attach,
.detach = pp_detach, .detach = pp_detach,
.devmodel = true,
}; };
static int __init ppdev_init(void) static int __init ppdev_init(void)

View File

@ -385,13 +385,18 @@ scdrv_init(void)
event_nasid = ia64_sn_get_console_nasid(); event_nasid = ia64_sn_get_console_nasid();
snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME);
if (IS_ERR(snsc_class)) {
printk("%s: failed to allocate class\n", __func__);
return PTR_ERR(snsc_class);
}
if (alloc_chrdev_region(&first_dev, 0, num_cnodes, if (alloc_chrdev_region(&first_dev, 0, num_cnodes,
SYSCTL_BASENAME) < 0) { SYSCTL_BASENAME) < 0) {
printk("%s: failed to register SN system controller device\n", printk("%s: failed to register SN system controller device\n",
__func__); __func__);
return -ENODEV; return -ENODEV;
} }
snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME);
for (cnode = 0; cnode < num_cnodes; cnode++) { for (cnode = 0; cnode < num_cnodes; cnode++) {
geoid = cnodeid_get_geoid(cnode); geoid = cnodeid_get_geoid(cnode);

View File

@ -331,13 +331,11 @@ static const struct file_operations srom_fops = {
/** /**
* srom_setup_minor() - Initialize per-minor information. * srom_setup_minor() - Initialize per-minor information.
* @srom: Per-device SROM state. * @srom: Per-device SROM state.
* @index: Device to set up. * @devhdl: Partition device handle.
*/ */
static int srom_setup_minor(struct srom_dev *srom, int index) static int srom_setup_minor(struct srom_dev *srom, int devhdl)
{ {
struct device *dev; srom->hv_devhdl = devhdl;
int devhdl = srom->hv_devhdl;
mutex_init(&srom->lock); mutex_init(&srom->lock);
if (_srom_read(devhdl, &srom->total_size, if (_srom_read(devhdl, &srom->total_size,
@ -350,9 +348,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index)
SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0) SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
return -EIO; return -EIO;
dev = device_create(srom_class, &srom_parent->dev, return 0;
MKDEV(srom_major, index), srom, "%d", index);
return PTR_ERR_OR_ZERO(dev);
} }
/** srom_init() - Initialize the driver's module. */ /** srom_init() - Initialize the driver's module. */
@ -365,7 +361,7 @@ static int srom_init(void)
* Start with a plausible number of partitions; the krealloc() call * Start with a plausible number of partitions; the krealloc() call
* below will yield about log(srom_devs) additional allocations. * below will yield about log(srom_devs) additional allocations.
*/ */
srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL); srom_devices = kmalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
/* Discover the number of srom partitions. */ /* Discover the number of srom partitions. */
for (i = 0; ; i++) { for (i = 0; ; i++) {
@ -373,7 +369,7 @@ static int srom_init(void)
char buf[20]; char buf[20];
struct srom_dev *new_srom_devices = struct srom_dev *new_srom_devices =
krealloc(srom_devices, (i+1) * sizeof(struct srom_dev), krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
GFP_KERNEL | __GFP_ZERO); GFP_KERNEL);
if (!new_srom_devices) { if (!new_srom_devices) {
result = -ENOMEM; result = -ENOMEM;
goto fail_mem; goto fail_mem;
@ -387,7 +383,9 @@ static int srom_init(void)
i, devhdl); i, devhdl);
break; break;
} }
srom_devices[i].hv_devhdl = devhdl; result = srom_setup_minor(&srom_devices[i], devhdl);
if (result != 0)
goto fail_mem;
} }
srom_devs = i; srom_devs = i;
@ -431,9 +429,13 @@ static int srom_init(void)
srom_class->dev_groups = srom_dev_groups; srom_class->dev_groups = srom_dev_groups;
srom_class->devnode = srom_devnode; srom_class->devnode = srom_devnode;
/* Do per-partition initialization */ /* Create per-partition devices */
for (i = 0; i < srom_devs; i++) { for (i = 0; i < srom_devs; i++) {
result = srom_setup_minor(srom_devices + i, i); struct device *dev =
device_create(srom_class, &srom_parent->dev,
MKDEV(srom_major, i), srom_devices + i,
"%d", i);
result = PTR_ERR_OR_ZERO(dev);
if (result < 0) if (result < 0)
goto fail_class; goto fail_class;
} }

View File

@ -31,60 +31,53 @@ static struct ttyprintk_port tpk_port;
* printk messages (also suitable for logging service): * printk messages (also suitable for logging service):
* - any cr is replaced by nl * - any cr is replaced by nl
* - adds a ttyprintk source tag in front of each line * - adds a ttyprintk source tag in front of each line
* - too long message is fragmeted, with '\'nl between fragments * - too long message is fragmented, with '\'nl between fragments
* - TPK_STR_SIZE isn't really the write_room limiting factor, bcause * - TPK_STR_SIZE isn't really the write_room limiting factor, because
* it is emptied on the fly during preformatting. * it is emptied on the fly during preformatting.
*/ */
#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */ #define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
#define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */ #define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
static const char *tpk_tag = "[U] "; /* U for User */
static int tpk_curr; static int tpk_curr;
static char tpk_buffer[TPK_STR_SIZE + 4];
static void tpk_flush(void)
{
if (tpk_curr > 0) {
tpk_buffer[tpk_curr] = '\0';
pr_info("[U] %s\n", tpk_buffer);
tpk_curr = 0;
}
}
static int tpk_printk(const unsigned char *buf, int count) static int tpk_printk(const unsigned char *buf, int count)
{ {
static char tmp[TPK_STR_SIZE + 4];
int i = tpk_curr; int i = tpk_curr;
if (buf == NULL) { if (buf == NULL) {
/* flush tmp[] */ tpk_flush();
if (tpk_curr > 0) {
/* non nl or cr terminated message - add nl */
tmp[tpk_curr + 0] = '\n';
tmp[tpk_curr + 1] = '\0';
printk(KERN_INFO "%s%s", tpk_tag, tmp);
tpk_curr = 0;
}
return i; return i;
} }
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
tmp[tpk_curr] = buf[i]; if (tpk_curr >= TPK_STR_SIZE) {
if (tpk_curr < TPK_STR_SIZE) {
switch (buf[i]) {
case '\r':
/* replace cr with nl */
tmp[tpk_curr + 0] = '\n';
tmp[tpk_curr + 1] = '\0';
printk(KERN_INFO "%s%s", tpk_tag, tmp);
tpk_curr = 0;
if ((i + 1) < count && buf[i + 1] == '\n')
i++;
break;
case '\n':
tmp[tpk_curr + 1] = '\0';
printk(KERN_INFO "%s%s", tpk_tag, tmp);
tpk_curr = 0;
break;
default:
tpk_curr++;
}
} else {
/* end of tmp buffer reached: cut the message in two */ /* end of tmp buffer reached: cut the message in two */
tmp[tpk_curr + 1] = '\\'; tpk_buffer[tpk_curr++] = '\\';
tmp[tpk_curr + 2] = '\n'; tpk_flush();
tmp[tpk_curr + 3] = '\0'; }
printk(KERN_INFO "%s%s", tpk_tag, tmp);
tpk_curr = 0; switch (buf[i]) {
case '\r':
tpk_flush();
if ((i + 1) < count && buf[i + 1] == '\n')
i++;
break;
case '\n':
tpk_flush();
break;
default:
tpk_buffer[tpk_curr++] = buf[i];
break;
} }
} }

View File

@ -655,10 +655,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
version = channel->wr_buffers[0]->addr; version = channel->wr_buffers[0]->addr;
/* Check version number. Accept anything below 0x82 for now. */ /* Check version number. Reject anything above 0x82. */
if (*version > 0x82) { if (*version > 0x82) {
dev_err(endpoint->dev, dev_err(endpoint->dev,
"No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgarde. Aborting.\n", "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgrade. Aborting.\n",
*version); *version);
return -ENODEV; return -ENODEV;
} }

View File

@ -21,6 +21,7 @@ config FPGA_MGR_SOCFPGA
config FPGA_MGR_ZYNQ_FPGA config FPGA_MGR_ZYNQ_FPGA
tristate "Xilinx Zynq FPGA" tristate "Xilinx Zynq FPGA"
depends on ARCH_ZYNQ || COMPILE_TEST
depends on HAS_DMA depends on HAS_DMA
help help
FPGA manager driver support for Xilinx Zynq FPGAs. FPGA manager driver support for Xilinx Zynq FPGAs.

View File

@ -779,19 +779,8 @@ static struct miscdevice uhid_misc = {
.minor = UHID_MINOR, .minor = UHID_MINOR,
.name = UHID_NAME, .name = UHID_NAME,
}; };
module_misc_device(uhid_misc);
static int __init uhid_init(void)
{
return misc_register(&uhid_misc);
}
static void __exit uhid_exit(void)
{
misc_deregister(&uhid_misc);
}
module_init(uhid_init);
module_exit(uhid_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");

View File

@ -43,7 +43,12 @@ static void vmbus_setevent(struct vmbus_channel *channel)
{ {
struct hv_monitor_page *monitorpage; struct hv_monitor_page *monitorpage;
if (channel->offermsg.monitor_allocated) { /*
* For channels marked as in "low latency" mode
* bypass the monitor page mechanism.
*/
if ((channel->offermsg.monitor_allocated) &&
(!channel->low_latency)) {
/* Each u32 represents 32 channels */ /* Each u32 represents 32 channels */
sync_set_bit(channel->offermsg.child_relid & 31, sync_set_bit(channel->offermsg.child_relid & 31,
(unsigned long *) vmbus_connection.send_int_page + (unsigned long *) vmbus_connection.send_int_page +
@ -70,12 +75,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
{ {
struct vmbus_channel_open_channel *open_msg; struct vmbus_channel_open_channel *open_msg;
struct vmbus_channel_msginfo *open_info = NULL; struct vmbus_channel_msginfo *open_info = NULL;
void *in, *out;
unsigned long flags; unsigned long flags;
int ret, err = 0; int ret, err = 0;
unsigned long t;
struct page *page; struct page *page;
if (send_ringbuffer_size % PAGE_SIZE ||
recv_ringbuffer_size % PAGE_SIZE)
return -EINVAL;
spin_lock_irqsave(&newchannel->lock, flags); spin_lock_irqsave(&newchannel->lock, flags);
if (newchannel->state == CHANNEL_OPEN_STATE) { if (newchannel->state == CHANNEL_OPEN_STATE) {
newchannel->state = CHANNEL_OPENING_STATE; newchannel->state = CHANNEL_OPENING_STATE;
@ -95,36 +102,33 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
recv_ringbuffer_size)); recv_ringbuffer_size));
if (!page) if (!page)
out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, page = alloc_pages(GFP_KERNEL|__GFP_ZERO,
get_order(send_ringbuffer_size + get_order(send_ringbuffer_size +
recv_ringbuffer_size)); recv_ringbuffer_size));
else
out = (void *)page_address(page);
if (!out) { if (!page) {
err = -ENOMEM; err = -ENOMEM;
goto error0; goto error_set_chnstate;
} }
in = (void *)((unsigned long)out + send_ringbuffer_size); newchannel->ringbuffer_pages = page_address(page);
newchannel->ringbuffer_pages = out;
newchannel->ringbuffer_pagecount = (send_ringbuffer_size + newchannel->ringbuffer_pagecount = (send_ringbuffer_size +
recv_ringbuffer_size) >> PAGE_SHIFT; recv_ringbuffer_size) >> PAGE_SHIFT;
ret = hv_ringbuffer_init( ret = hv_ringbuffer_init(&newchannel->outbound, page,
&newchannel->outbound, out, send_ringbuffer_size); send_ringbuffer_size >> PAGE_SHIFT);
if (ret != 0) { if (ret != 0) {
err = ret; err = ret;
goto error0; goto error_free_pages;
} }
ret = hv_ringbuffer_init( ret = hv_ringbuffer_init(&newchannel->inbound,
&newchannel->inbound, in, recv_ringbuffer_size); &page[send_ringbuffer_size >> PAGE_SHIFT],
recv_ringbuffer_size >> PAGE_SHIFT);
if (ret != 0) { if (ret != 0) {
err = ret; err = ret;
goto error0; goto error_free_pages;
} }
@ -132,14 +136,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
newchannel->ringbuffer_gpadlhandle = 0; newchannel->ringbuffer_gpadlhandle = 0;
ret = vmbus_establish_gpadl(newchannel, ret = vmbus_establish_gpadl(newchannel,
newchannel->outbound.ring_buffer, page_address(page),
send_ringbuffer_size + send_ringbuffer_size +
recv_ringbuffer_size, recv_ringbuffer_size,
&newchannel->ringbuffer_gpadlhandle); &newchannel->ringbuffer_gpadlhandle);
if (ret != 0) { if (ret != 0) {
err = ret; err = ret;
goto error0; goto error_free_pages;
} }
/* Create and init the channel open message */ /* Create and init the channel open message */
@ -148,7 +152,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
GFP_KERNEL); GFP_KERNEL);
if (!open_info) { if (!open_info) {
err = -ENOMEM; err = -ENOMEM;
goto error_gpadl; goto error_free_gpadl;
} }
init_completion(&open_info->waitevent); init_completion(&open_info->waitevent);
@ -164,7 +168,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (userdatalen > MAX_USER_DEFINED_BYTES) { if (userdatalen > MAX_USER_DEFINED_BYTES) {
err = -EINVAL; err = -EINVAL;
goto error_gpadl; goto error_free_gpadl;
} }
if (userdatalen) if (userdatalen)
@ -180,14 +184,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (ret != 0) { if (ret != 0) {
err = ret; err = ret;
goto error1; goto error_clean_msglist;
} }
t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); wait_for_completion(&open_info->waitevent);
if (t == 0) {
err = -ETIMEDOUT;
goto error1;
}
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&open_info->msglistentry); list_del(&open_info->msglistentry);
@ -195,25 +195,27 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (open_info->response.open_result.status) { if (open_info->response.open_result.status) {
err = -EAGAIN; err = -EAGAIN;
goto error_gpadl; goto error_free_gpadl;
} }
newchannel->state = CHANNEL_OPENED_STATE; newchannel->state = CHANNEL_OPENED_STATE;
kfree(open_info); kfree(open_info);
return 0; return 0;
error1: error_clean_msglist:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&open_info->msglistentry); list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
error_gpadl: error_free_gpadl:
vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle); vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
error0:
free_pages((unsigned long)out,
get_order(send_ringbuffer_size + recv_ringbuffer_size));
kfree(open_info); kfree(open_info);
error_free_pages:
hv_ringbuffer_cleanup(&newchannel->outbound);
hv_ringbuffer_cleanup(&newchannel->inbound);
__free_pages(page,
get_order(send_ringbuffer_size + recv_ringbuffer_size));
error_set_chnstate:
newchannel->state = CHANNEL_OPEN_STATE; newchannel->state = CHANNEL_OPEN_STATE;
return err; return err;
} }
@ -238,8 +240,7 @@ EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
* create_gpadl_header - Creates a gpadl for the specified buffer * create_gpadl_header - Creates a gpadl for the specified buffer
*/ */
static int create_gpadl_header(void *kbuffer, u32 size, static int create_gpadl_header(void *kbuffer, u32 size,
struct vmbus_channel_msginfo **msginfo, struct vmbus_channel_msginfo **msginfo)
u32 *messagecount)
{ {
int i; int i;
int pagecount; int pagecount;
@ -283,7 +284,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys( gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys(
kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT; kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
*msginfo = msgheader; *msginfo = msgheader;
*messagecount = 1;
pfnsum = pfncount; pfnsum = pfncount;
pfnleft = pagecount - pfncount; pfnleft = pagecount - pfncount;
@ -323,7 +323,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
} }
msgbody->msgsize = msgsize; msgbody->msgsize = msgsize;
(*messagecount)++;
gpadl_body = gpadl_body =
(struct vmbus_channel_gpadl_body *)msgbody->msg; (struct vmbus_channel_gpadl_body *)msgbody->msg;
@ -352,6 +351,8 @@ static int create_gpadl_header(void *kbuffer, u32 size,
msgheader = kzalloc(msgsize, GFP_KERNEL); msgheader = kzalloc(msgsize, GFP_KERNEL);
if (msgheader == NULL) if (msgheader == NULL)
goto nomem; goto nomem;
INIT_LIST_HEAD(&msgheader->submsglist);
msgheader->msgsize = msgsize; msgheader->msgsize = msgsize;
gpadl_header = (struct vmbus_channel_gpadl_header *) gpadl_header = (struct vmbus_channel_gpadl_header *)
@ -366,7 +367,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT; kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
*msginfo = msgheader; *msginfo = msgheader;
*messagecount = 1;
} }
return 0; return 0;
@ -390,8 +390,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
struct vmbus_channel_gpadl_header *gpadlmsg; struct vmbus_channel_gpadl_header *gpadlmsg;
struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_gpadl_body *gpadl_body;
struct vmbus_channel_msginfo *msginfo = NULL; struct vmbus_channel_msginfo *msginfo = NULL;
struct vmbus_channel_msginfo *submsginfo; struct vmbus_channel_msginfo *submsginfo, *tmp;
u32 msgcount;
struct list_head *curr; struct list_head *curr;
u32 next_gpadl_handle; u32 next_gpadl_handle;
unsigned long flags; unsigned long flags;
@ -400,7 +399,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
next_gpadl_handle = next_gpadl_handle =
(atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1); (atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); ret = create_gpadl_header(kbuffer, size, &msginfo);
if (ret) if (ret)
return ret; return ret;
@ -423,24 +422,21 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
if (ret != 0) if (ret != 0)
goto cleanup; goto cleanup;
if (msgcount > 1) { list_for_each(curr, &msginfo->submsglist) {
list_for_each(curr, &msginfo->submsglist) { submsginfo = (struct vmbus_channel_msginfo *)curr;
gpadl_body =
(struct vmbus_channel_gpadl_body *)submsginfo->msg;
submsginfo = (struct vmbus_channel_msginfo *)curr; gpadl_body->header.msgtype =
gpadl_body = CHANNELMSG_GPADL_BODY;
(struct vmbus_channel_gpadl_body *)submsginfo->msg; gpadl_body->gpadl = next_gpadl_handle;
gpadl_body->header.msgtype = ret = vmbus_post_msg(gpadl_body,
CHANNELMSG_GPADL_BODY; submsginfo->msgsize -
gpadl_body->gpadl = next_gpadl_handle; sizeof(*submsginfo));
if (ret != 0)
goto cleanup;
ret = vmbus_post_msg(gpadl_body,
submsginfo->msgsize -
sizeof(*submsginfo));
if (ret != 0)
goto cleanup;
}
} }
wait_for_completion(&msginfo->waitevent); wait_for_completion(&msginfo->waitevent);
@ -451,6 +447,10 @@ cleanup:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry); list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist,
msglistentry) {
kfree(submsginfo);
}
kfree(msginfo); kfree(msginfo);
return ret; return ret;
@ -512,7 +512,6 @@ static void reset_channel_cb(void *arg)
static int vmbus_close_internal(struct vmbus_channel *channel) static int vmbus_close_internal(struct vmbus_channel *channel)
{ {
struct vmbus_channel_close_channel *msg; struct vmbus_channel_close_channel *msg;
struct tasklet_struct *tasklet;
int ret; int ret;
/* /*
@ -524,8 +523,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
* To resolve the race, we can serialize them by disabling the * To resolve the race, we can serialize them by disabling the
* tasklet when the latter is running here. * tasklet when the latter is running here.
*/ */
tasklet = hv_context.event_dpc[channel->target_cpu]; hv_event_tasklet_disable(channel);
tasklet_disable(tasklet);
/* /*
* In case a device driver's probe() fails (e.g., * In case a device driver's probe() fails (e.g.,
@ -591,7 +589,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
out: out:
tasklet_enable(tasklet); hv_event_tasklet_enable(channel);
return ret; return ret;
} }
@ -659,7 +657,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs,
&signal, lock); &signal, lock, channel->signal_policy);
/* /*
* Signalling the host is conditional on many factors: * Signalling the host is conditional on many factors:
@ -680,11 +678,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
* mechanism which can hurt the performance otherwise. * mechanism which can hurt the performance otherwise.
*/ */
if (channel->signal_policy)
signal = true;
else
kick_q = true;
if (((ret == 0) && kick_q && signal) || if (((ret == 0) && kick_q && signal) ||
(ret && !is_hvsock_channel(channel))) (ret && !is_hvsock_channel(channel)))
vmbus_setevent(channel); vmbus_setevent(channel);
@ -777,7 +770,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
&signal, lock); &signal, lock, channel->signal_policy);
/* /*
* Signalling the host is conditional on many factors: * Signalling the host is conditional on many factors:
@ -795,11 +788,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
* enough condition that it should not matter. * enough condition that it should not matter.
*/ */
if (channel->signal_policy)
signal = true;
else
kick_q = true;
if (((ret == 0) && kick_q && signal) || (ret)) if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel); vmbus_setevent(channel);
@ -861,7 +849,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
&signal, lock); &signal, lock, channel->signal_policy);
if (ret == 0 && signal) if (ret == 0 && signal)
vmbus_setevent(channel); vmbus_setevent(channel);
@ -926,7 +914,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
&signal, lock); &signal, lock, channel->signal_policy);
if (ret == 0 && signal) if (ret == 0 && signal)
vmbus_setevent(channel); vmbus_setevent(channel);

View File

@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/mm.h> #include <linux/mm.h>
@ -138,10 +139,32 @@ static const struct vmbus_device vmbus_devs[] = {
}, },
}; };
static u16 hv_get_dev_type(const uuid_le *guid) static const struct {
uuid_le guid;
} vmbus_unsupported_devs[] = {
{ HV_AVMA1_GUID },
{ HV_AVMA2_GUID },
{ HV_RDV_GUID },
};
static bool is_unsupported_vmbus_devs(const uuid_le *guid)
{ {
int i;
for (i = 0; i < ARRAY_SIZE(vmbus_unsupported_devs); i++)
if (!uuid_le_cmp(*guid, vmbus_unsupported_devs[i].guid))
return true;
return false;
}
static u16 hv_get_dev_type(const struct vmbus_channel *channel)
{
const uuid_le *guid = &channel->offermsg.offer.if_type;
u16 i; u16 i;
if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid))
return HV_UNKOWN;
for (i = HV_IDE; i < HV_UNKOWN; i++) { for (i = HV_IDE; i < HV_UNKOWN; i++) {
if (!uuid_le_cmp(*guid, vmbus_devs[i].guid)) if (!uuid_le_cmp(*guid, vmbus_devs[i].guid))
return i; return i;
@ -251,14 +274,12 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
*/ */
static struct vmbus_channel *alloc_channel(void) static struct vmbus_channel *alloc_channel(void)
{ {
static atomic_t chan_num = ATOMIC_INIT(0);
struct vmbus_channel *channel; struct vmbus_channel *channel;
channel = kzalloc(sizeof(*channel), GFP_ATOMIC); channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
if (!channel) if (!channel)
return NULL; return NULL;
channel->id = atomic_inc_return(&chan_num);
channel->acquire_ring_lock = true; channel->acquire_ring_lock = true;
spin_lock_init(&channel->inbound_lock); spin_lock_init(&channel->inbound_lock);
spin_lock_init(&channel->lock); spin_lock_init(&channel->lock);
@ -303,16 +324,32 @@ static void vmbus_release_relid(u32 relid)
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
} }
void hv_event_tasklet_disable(struct vmbus_channel *channel)
{
struct tasklet_struct *tasklet;
tasklet = hv_context.event_dpc[channel->target_cpu];
tasklet_disable(tasklet);
}
void hv_event_tasklet_enable(struct vmbus_channel *channel)
{
struct tasklet_struct *tasklet;
tasklet = hv_context.event_dpc[channel->target_cpu];
tasklet_enable(tasklet);
/* In case there is any pending event */
tasklet_schedule(tasklet);
}
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
{ {
unsigned long flags; unsigned long flags;
struct vmbus_channel *primary_channel; struct vmbus_channel *primary_channel;
vmbus_release_relid(relid);
BUG_ON(!channel->rescind); BUG_ON(!channel->rescind);
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
hv_event_tasklet_disable(channel);
if (channel->target_cpu != get_cpu()) { if (channel->target_cpu != get_cpu()) {
put_cpu(); put_cpu();
smp_call_function_single(channel->target_cpu, smp_call_function_single(channel->target_cpu,
@ -321,6 +358,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
percpu_channel_deq(channel); percpu_channel_deq(channel);
put_cpu(); put_cpu();
} }
hv_event_tasklet_enable(channel);
if (channel->primary_channel == NULL) { if (channel->primary_channel == NULL) {
list_del(&channel->listentry); list_del(&channel->listentry);
@ -338,8 +376,11 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
* We need to free the bit for init_vp_index() to work in the case * We need to free the bit for init_vp_index() to work in the case
* of sub-channel, when we reload drivers like hv_netvsc. * of sub-channel, when we reload drivers like hv_netvsc.
*/ */
cpumask_clear_cpu(channel->target_cpu, if (channel->affinity_policy == HV_LOCALIZED)
&primary_channel->alloced_cpus_in_node); cpumask_clear_cpu(channel->target_cpu,
&primary_channel->alloced_cpus_in_node);
vmbus_release_relid(relid);
free_channel(channel); free_channel(channel);
} }
@ -405,10 +446,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
goto err_free_chan; goto err_free_chan;
} }
dev_type = hv_get_dev_type(&newchannel->offermsg.offer.if_type); dev_type = hv_get_dev_type(newchannel);
if (dev_type == HV_NIC)
set_channel_signal_state(newchannel, HV_SIGNAL_POLICY_EXPLICIT);
init_vp_index(newchannel, dev_type); init_vp_index(newchannel, dev_type);
hv_event_tasklet_disable(newchannel);
if (newchannel->target_cpu != get_cpu()) { if (newchannel->target_cpu != get_cpu()) {
put_cpu(); put_cpu();
smp_call_function_single(newchannel->target_cpu, smp_call_function_single(newchannel->target_cpu,
@ -418,6 +462,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
percpu_channel_enq(newchannel); percpu_channel_enq(newchannel);
put_cpu(); put_cpu();
} }
hv_event_tasklet_enable(newchannel);
/* /*
* This state is used to indicate a successful open * This state is used to indicate a successful open
@ -463,12 +508,11 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
return; return;
err_deq_chan: err_deq_chan:
vmbus_release_relid(newchannel->offermsg.child_relid);
mutex_lock(&vmbus_connection.channel_mutex); mutex_lock(&vmbus_connection.channel_mutex);
list_del(&newchannel->listentry); list_del(&newchannel->listentry);
mutex_unlock(&vmbus_connection.channel_mutex); mutex_unlock(&vmbus_connection.channel_mutex);
hv_event_tasklet_disable(newchannel);
if (newchannel->target_cpu != get_cpu()) { if (newchannel->target_cpu != get_cpu()) {
put_cpu(); put_cpu();
smp_call_function_single(newchannel->target_cpu, smp_call_function_single(newchannel->target_cpu,
@ -477,6 +521,9 @@ err_deq_chan:
percpu_channel_deq(newchannel); percpu_channel_deq(newchannel);
put_cpu(); put_cpu();
} }
hv_event_tasklet_enable(newchannel);
vmbus_release_relid(newchannel->offermsg.child_relid);
err_free_chan: err_free_chan:
free_channel(newchannel); free_channel(newchannel);
@ -522,17 +569,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
} }
/* /*
* We distribute primary channels evenly across all the available * Based on the channel affinity policy, we will assign the NUMA
* NUMA nodes and within the assigned NUMA node we will assign the * nodes.
* first available CPU to the primary channel.
* The sub-channels will be assigned to the CPUs available in the
* NUMA node evenly.
*/ */
if (!primary) {
if ((channel->affinity_policy == HV_BALANCED) || (!primary)) {
while (true) { while (true) {
next_node = next_numa_node_id++; next_node = next_numa_node_id++;
if (next_node == nr_node_ids) if (next_node == nr_node_ids) {
next_node = next_numa_node_id = 0; next_node = next_numa_node_id = 0;
continue;
}
if (cpumask_empty(cpumask_of_node(next_node))) if (cpumask_empty(cpumask_of_node(next_node)))
continue; continue;
break; break;
@ -556,15 +603,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
cur_cpu = -1; cur_cpu = -1;
/* if (primary->affinity_policy == HV_LOCALIZED) {
* Normally Hyper-V host doesn't create more subchannels than there /*
* are VCPUs on the node but it is possible when not all present VCPUs * Normally Hyper-V host doesn't create more subchannels
* on the node are initialized by guest. Clear the alloced_cpus_in_node * than there are VCPUs on the node but it is possible when not
* to start over. * all present VCPUs on the node are initialized by guest.
*/ * Clear the alloced_cpus_in_node to start over.
if (cpumask_equal(&primary->alloced_cpus_in_node, */
cpumask_of_node(primary->numa_node))) if (cpumask_equal(&primary->alloced_cpus_in_node,
cpumask_clear(&primary->alloced_cpus_in_node); cpumask_of_node(primary->numa_node)))
cpumask_clear(&primary->alloced_cpus_in_node);
}
while (true) { while (true) {
cur_cpu = cpumask_next(cur_cpu, &available_mask); cur_cpu = cpumask_next(cur_cpu, &available_mask);
@ -575,17 +624,24 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
continue; continue;
} }
/* if (primary->affinity_policy == HV_LOCALIZED) {
* NOTE: in the case of sub-channel, we clear the sub-channel /*
* related bit(s) in primary->alloced_cpus_in_node in * NOTE: in the case of sub-channel, we clear the
* hv_process_channel_removal(), so when we reload drivers * sub-channel related bit(s) in
* like hv_netvsc in SMP guest, here we're able to re-allocate * primary->alloced_cpus_in_node in
* bit from primary->alloced_cpus_in_node. * hv_process_channel_removal(), so when we
*/ * reload drivers like hv_netvsc in SMP guest, here
if (!cpumask_test_cpu(cur_cpu, * we're able to re-allocate
&primary->alloced_cpus_in_node)) { * bit from primary->alloced_cpus_in_node.
cpumask_set_cpu(cur_cpu, */
&primary->alloced_cpus_in_node); if (!cpumask_test_cpu(cur_cpu,
&primary->alloced_cpus_in_node)) {
cpumask_set_cpu(cur_cpu,
&primary->alloced_cpus_in_node);
cpumask_set_cpu(cur_cpu, alloced_mask);
break;
}
} else {
cpumask_set_cpu(cur_cpu, alloced_mask); cpumask_set_cpu(cur_cpu, alloced_mask);
break; break;
} }

View File

@ -439,7 +439,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
union hv_connection_id conn_id; union hv_connection_id conn_id;
int ret = 0; int ret = 0;
int retries = 0; int retries = 0;
u32 msec = 1; u32 usec = 1;
conn_id.asu32 = 0; conn_id.asu32 = 0;
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
@ -472,9 +472,9 @@ int vmbus_post_msg(void *buffer, size_t buflen)
} }
retries++; retries++;
msleep(msec); udelay(usec);
if (msec < 2048) if (usec < 2048)
msec *= 2; usec *= 2;
} }
return ret; return ret;
} }

View File

@ -278,7 +278,7 @@ cleanup:
* *
* This routine is called normally during driver unloading or exiting. * This routine is called normally during driver unloading or exiting.
*/ */
void hv_cleanup(void) void hv_cleanup(bool crash)
{ {
union hv_x64_msr_hypercall_contents hypercall_msr; union hv_x64_msr_hypercall_contents hypercall_msr;
@ -288,7 +288,8 @@ void hv_cleanup(void)
if (hv_context.hypercall_page) { if (hv_context.hypercall_page) {
hypercall_msr.as_uint64 = 0; hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
vfree(hv_context.hypercall_page); if (!crash)
vfree(hv_context.hypercall_page);
hv_context.hypercall_page = NULL; hv_context.hypercall_page = NULL;
} }
@ -308,7 +309,8 @@ void hv_cleanup(void)
hypercall_msr.as_uint64 = 0; hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
vfree(hv_context.tsc_page); if (!crash)
vfree(hv_context.tsc_page);
hv_context.tsc_page = NULL; hv_context.tsc_page = NULL;
} }
#endif #endif

View File

@ -430,16 +430,27 @@ struct dm_info_msg {
* currently hot added. We hot add in multiples of 128M * currently hot added. We hot add in multiples of 128M
* chunks; it is possible that we may not be able to bring * chunks; it is possible that we may not be able to bring
* online all the pages in the region. The range * online all the pages in the region. The range
* covered_end_pfn defines the pages that can * covered_start_pfn:covered_end_pfn defines the pages that can
* be brough online. * be brough online.
*/ */
struct hv_hotadd_state { struct hv_hotadd_state {
struct list_head list; struct list_head list;
unsigned long start_pfn; unsigned long start_pfn;
unsigned long covered_start_pfn;
unsigned long covered_end_pfn; unsigned long covered_end_pfn;
unsigned long ha_end_pfn; unsigned long ha_end_pfn;
unsigned long end_pfn; unsigned long end_pfn;
/*
* A list of gaps.
*/
struct list_head gap_list;
};
struct hv_hotadd_gap {
struct list_head list;
unsigned long start_pfn;
unsigned long end_pfn;
}; };
struct balloon_state { struct balloon_state {
@ -536,7 +547,11 @@ struct hv_dynmem_device {
*/ */
struct task_struct *thread; struct task_struct *thread;
struct mutex ha_region_mutex; /*
* Protects ha_region_list, num_pages_onlined counter and individual
* regions from ha_region_list.
*/
spinlock_t ha_lock;
/* /*
* A list of hot-add regions. * A list of hot-add regions.
@ -560,18 +575,14 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
void *v) void *v)
{ {
struct memory_notify *mem = (struct memory_notify *)v; struct memory_notify *mem = (struct memory_notify *)v;
unsigned long flags;
switch (val) { switch (val) {
case MEM_GOING_ONLINE:
mutex_lock(&dm_device.ha_region_mutex);
break;
case MEM_ONLINE: case MEM_ONLINE:
spin_lock_irqsave(&dm_device.ha_lock, flags);
dm_device.num_pages_onlined += mem->nr_pages; dm_device.num_pages_onlined += mem->nr_pages;
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
case MEM_CANCEL_ONLINE: case MEM_CANCEL_ONLINE:
if (val == MEM_ONLINE ||
mutex_is_locked(&dm_device.ha_region_mutex))
mutex_unlock(&dm_device.ha_region_mutex);
if (dm_device.ha_waiting) { if (dm_device.ha_waiting) {
dm_device.ha_waiting = false; dm_device.ha_waiting = false;
complete(&dm_device.ol_waitevent); complete(&dm_device.ol_waitevent);
@ -579,10 +590,11 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
break; break;
case MEM_OFFLINE: case MEM_OFFLINE:
mutex_lock(&dm_device.ha_region_mutex); spin_lock_irqsave(&dm_device.ha_lock, flags);
dm_device.num_pages_onlined -= mem->nr_pages; dm_device.num_pages_onlined -= mem->nr_pages;
mutex_unlock(&dm_device.ha_region_mutex); spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break; break;
case MEM_GOING_ONLINE:
case MEM_GOING_OFFLINE: case MEM_GOING_OFFLINE:
case MEM_CANCEL_OFFLINE: case MEM_CANCEL_OFFLINE:
break; break;
@ -595,18 +607,46 @@ static struct notifier_block hv_memory_nb = {
.priority = 0 .priority = 0
}; };
/* Check if the particular page is backed and can be onlined and online it. */
static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
{
unsigned long cur_start_pgp;
unsigned long cur_end_pgp;
struct hv_hotadd_gap *gap;
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size) cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
/* The page is not backed. */
if (((unsigned long)pg < cur_start_pgp) ||
((unsigned long)pg >= cur_end_pgp))
return;
/* Check for gaps. */
list_for_each_entry(gap, &has->gap_list, list) {
cur_start_pgp = (unsigned long)
pfn_to_page(gap->start_pfn);
cur_end_pgp = (unsigned long)
pfn_to_page(gap->end_pfn);
if (((unsigned long)pg >= cur_start_pgp) &&
((unsigned long)pg < cur_end_pgp)) {
return;
}
}
/* This frame is currently backed; online the page. */
__online_page_set_limits(pg);
__online_page_increment_counters(pg);
__online_page_free(pg);
}
static void hv_bring_pgs_online(struct hv_hotadd_state *has,
unsigned long start_pfn, unsigned long size)
{ {
int i; int i;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++)
struct page *pg; hv_page_online_one(has, pfn_to_page(start_pfn + i));
pg = pfn_to_page(start_pfn + i);
__online_page_set_limits(pg);
__online_page_increment_counters(pg);
__online_page_free(pg);
}
} }
static void hv_mem_hot_add(unsigned long start, unsigned long size, static void hv_mem_hot_add(unsigned long start, unsigned long size,
@ -618,9 +658,12 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
unsigned long start_pfn; unsigned long start_pfn;
unsigned long processed_pfn; unsigned long processed_pfn;
unsigned long total_pfn = pfn_count; unsigned long total_pfn = pfn_count;
unsigned long flags;
for (i = 0; i < (size/HA_CHUNK); i++) { for (i = 0; i < (size/HA_CHUNK); i++) {
start_pfn = start + (i * HA_CHUNK); start_pfn = start + (i * HA_CHUNK);
spin_lock_irqsave(&dm_device.ha_lock, flags);
has->ha_end_pfn += HA_CHUNK; has->ha_end_pfn += HA_CHUNK;
if (total_pfn > HA_CHUNK) { if (total_pfn > HA_CHUNK) {
@ -632,11 +675,11 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
} }
has->covered_end_pfn += processed_pfn; has->covered_end_pfn += processed_pfn;
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
init_completion(&dm_device.ol_waitevent); init_completion(&dm_device.ol_waitevent);
dm_device.ha_waiting = true; dm_device.ha_waiting = !memhp_auto_online;
mutex_unlock(&dm_device.ha_region_mutex);
nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
ret = add_memory(nid, PFN_PHYS((start_pfn)), ret = add_memory(nid, PFN_PHYS((start_pfn)),
(HA_CHUNK << PAGE_SHIFT)); (HA_CHUNK << PAGE_SHIFT));
@ -653,20 +696,23 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
*/ */
do_hot_add = false; do_hot_add = false;
} }
spin_lock_irqsave(&dm_device.ha_lock, flags);
has->ha_end_pfn -= HA_CHUNK; has->ha_end_pfn -= HA_CHUNK;
has->covered_end_pfn -= processed_pfn; has->covered_end_pfn -= processed_pfn;
mutex_lock(&dm_device.ha_region_mutex); spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break; break;
} }
/* /*
* Wait for the memory block to be onlined. * Wait for the memory block to be onlined when memory onlining
* Since the hot add has succeeded, it is ok to * is done outside of kernel (memhp_auto_online). Since the hot
* proceed even if the pages in the hot added region * add has succeeded, it is ok to proceed even if the pages in
* have not been "onlined" within the allowed time. * the hot added region have not been "onlined" within the
* allowed time.
*/ */
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ); if (dm_device.ha_waiting)
mutex_lock(&dm_device.ha_region_mutex); wait_for_completion_timeout(&dm_device.ol_waitevent,
5*HZ);
post_status(&dm_device); post_status(&dm_device);
} }
@ -675,47 +721,64 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
static void hv_online_page(struct page *pg) static void hv_online_page(struct page *pg)
{ {
struct list_head *cur;
struct hv_hotadd_state *has; struct hv_hotadd_state *has;
unsigned long cur_start_pgp; unsigned long cur_start_pgp;
unsigned long cur_end_pgp; unsigned long cur_end_pgp;
unsigned long flags;
list_for_each(cur, &dm_device.ha_region_list) { spin_lock_irqsave(&dm_device.ha_lock, flags);
has = list_entry(cur, struct hv_hotadd_state, list); list_for_each_entry(has, &dm_device.ha_region_list, list) {
cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn); cur_start_pgp = (unsigned long)
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); pfn_to_page(has->start_pfn);
cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
if (((unsigned long)pg >= cur_start_pgp) && /* The page belongs to a different HAS. */
((unsigned long)pg < cur_end_pgp)) { if (((unsigned long)pg < cur_start_pgp) ||
/* ((unsigned long)pg >= cur_end_pgp))
* This frame is currently backed; online the continue;
* page.
*/ hv_page_online_one(has, pg);
__online_page_set_limits(pg); break;
__online_page_increment_counters(pg);
__online_page_free(pg);
}
} }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
} }
static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
{ {
struct list_head *cur;
struct hv_hotadd_state *has; struct hv_hotadd_state *has;
struct hv_hotadd_gap *gap;
unsigned long residual, new_inc; unsigned long residual, new_inc;
int ret = 0;
unsigned long flags;
if (list_empty(&dm_device.ha_region_list)) spin_lock_irqsave(&dm_device.ha_lock, flags);
return false; list_for_each_entry(has, &dm_device.ha_region_list, list) {
list_for_each(cur, &dm_device.ha_region_list) {
has = list_entry(cur, struct hv_hotadd_state, list);
/* /*
* If the pfn range we are dealing with is not in the current * If the pfn range we are dealing with is not in the current
* "hot add block", move on. * "hot add block", move on.
*/ */
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue; continue;
/*
* If the current start pfn is not where the covered_end
* is, create a gap and update covered_end_pfn.
*/
if (has->covered_end_pfn != start_pfn) {
gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC);
if (!gap) {
ret = -ENOMEM;
break;
}
INIT_LIST_HEAD(&gap->list);
gap->start_pfn = has->covered_end_pfn;
gap->end_pfn = start_pfn;
list_add_tail(&gap->list, &has->gap_list);
has->covered_end_pfn = start_pfn;
}
/* /*
* If the current hot add-request extends beyond * If the current hot add-request extends beyond
* our current limit; extend it. * our current limit; extend it.
@ -732,19 +795,12 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
has->end_pfn += new_inc; has->end_pfn += new_inc;
} }
/* ret = 1;
* If the current start pfn is not where the covered_end break;
* is, update it.
*/
if (has->covered_end_pfn != start_pfn)
has->covered_end_pfn = start_pfn;
return true;
} }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
return false; return ret;
} }
static unsigned long handle_pg_range(unsigned long pg_start, static unsigned long handle_pg_range(unsigned long pg_start,
@ -753,17 +809,13 @@ static unsigned long handle_pg_range(unsigned long pg_start,
unsigned long start_pfn = pg_start; unsigned long start_pfn = pg_start;
unsigned long pfn_cnt = pg_count; unsigned long pfn_cnt = pg_count;
unsigned long size; unsigned long size;
struct list_head *cur;
struct hv_hotadd_state *has; struct hv_hotadd_state *has;
unsigned long pgs_ol = 0; unsigned long pgs_ol = 0;
unsigned long old_covered_state; unsigned long old_covered_state;
unsigned long res = 0, flags;
if (list_empty(&dm_device.ha_region_list)) spin_lock_irqsave(&dm_device.ha_lock, flags);
return 0; list_for_each_entry(has, &dm_device.ha_region_list, list) {
list_for_each(cur, &dm_device.ha_region_list) {
has = list_entry(cur, struct hv_hotadd_state, list);
/* /*
* If the pfn range we are dealing with is not in the current * If the pfn range we are dealing with is not in the current
* "hot add block", move on. * "hot add block", move on.
@ -783,6 +835,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
if (pgs_ol > pfn_cnt) if (pgs_ol > pfn_cnt)
pgs_ol = pfn_cnt; pgs_ol = pfn_cnt;
has->covered_end_pfn += pgs_ol;
pfn_cnt -= pgs_ol;
/* /*
* Check if the corresponding memory block is already * Check if the corresponding memory block is already
* online by checking its last previously backed page. * online by checking its last previously backed page.
@ -791,10 +845,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
*/ */
if (start_pfn > has->start_pfn && if (start_pfn > has->start_pfn &&
!PageReserved(pfn_to_page(start_pfn - 1))) !PageReserved(pfn_to_page(start_pfn - 1)))
hv_bring_pgs_online(start_pfn, pgs_ol); hv_bring_pgs_online(has, start_pfn, pgs_ol);
has->covered_end_pfn += pgs_ol;
pfn_cnt -= pgs_ol;
} }
if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) { if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) {
@ -813,17 +865,20 @@ static unsigned long handle_pg_range(unsigned long pg_start,
} else { } else {
pfn_cnt = size; pfn_cnt = size;
} }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has); hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has);
spin_lock_irqsave(&dm_device.ha_lock, flags);
} }
/* /*
* If we managed to online any pages that were given to us, * If we managed to online any pages that were given to us,
* we declare success. * we declare success.
*/ */
return has->covered_end_pfn - old_covered_state; res = has->covered_end_pfn - old_covered_state;
break;
} }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
return 0; return res;
} }
static unsigned long process_hot_add(unsigned long pg_start, static unsigned long process_hot_add(unsigned long pg_start,
@ -832,13 +887,20 @@ static unsigned long process_hot_add(unsigned long pg_start,
unsigned long rg_size) unsigned long rg_size)
{ {
struct hv_hotadd_state *ha_region = NULL; struct hv_hotadd_state *ha_region = NULL;
int covered;
unsigned long flags;
if (pfn_cnt == 0) if (pfn_cnt == 0)
return 0; return 0;
if (!dm_device.host_specified_ha_region) if (!dm_device.host_specified_ha_region) {
if (pfn_covered(pg_start, pfn_cnt)) covered = pfn_covered(pg_start, pfn_cnt);
if (covered < 0)
return 0;
if (covered)
goto do_pg_range; goto do_pg_range;
}
/* /*
* If the host has specified a hot-add range; deal with it first. * If the host has specified a hot-add range; deal with it first.
@ -850,12 +912,17 @@ static unsigned long process_hot_add(unsigned long pg_start,
return 0; return 0;
INIT_LIST_HEAD(&ha_region->list); INIT_LIST_HEAD(&ha_region->list);
INIT_LIST_HEAD(&ha_region->gap_list);
list_add_tail(&ha_region->list, &dm_device.ha_region_list);
ha_region->start_pfn = rg_start; ha_region->start_pfn = rg_start;
ha_region->ha_end_pfn = rg_start; ha_region->ha_end_pfn = rg_start;
ha_region->covered_start_pfn = pg_start;
ha_region->covered_end_pfn = pg_start; ha_region->covered_end_pfn = pg_start;
ha_region->end_pfn = rg_start + rg_size; ha_region->end_pfn = rg_start + rg_size;
spin_lock_irqsave(&dm_device.ha_lock, flags);
list_add_tail(&ha_region->list, &dm_device.ha_region_list);
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
} }
do_pg_range: do_pg_range:
@ -882,7 +949,6 @@ static void hot_add_req(struct work_struct *dummy)
resp.hdr.size = sizeof(struct dm_hot_add_response); resp.hdr.size = sizeof(struct dm_hot_add_response);
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
mutex_lock(&dm_device.ha_region_mutex);
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page; pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt; pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
@ -916,7 +982,6 @@ static void hot_add_req(struct work_struct *dummy)
rg_start, rg_sz); rg_start, rg_sz);
dm->num_pages_added += resp.page_count; dm->num_pages_added += resp.page_count;
mutex_unlock(&dm_device.ha_region_mutex);
#endif #endif
/* /*
* The result field of the response structure has the * The result field of the response structure has the
@ -1010,7 +1075,6 @@ static unsigned long compute_balloon_floor(void)
static void post_status(struct hv_dynmem_device *dm) static void post_status(struct hv_dynmem_device *dm)
{ {
struct dm_status status; struct dm_status status;
struct sysinfo val;
unsigned long now = jiffies; unsigned long now = jiffies;
unsigned long last_post = last_post_time; unsigned long last_post = last_post_time;
@ -1022,7 +1086,6 @@ static void post_status(struct hv_dynmem_device *dm)
if (!time_after(now, (last_post_time + HZ))) if (!time_after(now, (last_post_time + HZ)))
return; return;
si_meminfo(&val);
memset(&status, 0, sizeof(struct dm_status)); memset(&status, 0, sizeof(struct dm_status));
status.hdr.type = DM_STATUS_REPORT; status.hdr.type = DM_STATUS_REPORT;
status.hdr.size = sizeof(struct dm_status); status.hdr.size = sizeof(struct dm_status);
@ -1038,7 +1101,7 @@ static void post_status(struct hv_dynmem_device *dm)
* num_pages_onlined) as committed to the host, otherwise it can try * num_pages_onlined) as committed to the host, otherwise it can try
* asking us to balloon them out. * asking us to balloon them out.
*/ */
status.num_avail = val.freeram; status.num_avail = si_mem_available();
status.num_committed = vm_memory_committed() + status.num_committed = vm_memory_committed() +
dm->num_pages_ballooned + dm->num_pages_ballooned +
(dm->num_pages_added > dm->num_pages_onlined ? (dm->num_pages_added > dm->num_pages_onlined ?
@ -1144,7 +1207,7 @@ static void balloon_up(struct work_struct *dummy)
int ret; int ret;
bool done = false; bool done = false;
int i; int i;
struct sysinfo val; long avail_pages;
unsigned long floor; unsigned long floor;
/* The host balloons pages in 2M granularity. */ /* The host balloons pages in 2M granularity. */
@ -1156,12 +1219,12 @@ static void balloon_up(struct work_struct *dummy)
*/ */
alloc_unit = 512; alloc_unit = 512;
si_meminfo(&val); avail_pages = si_mem_available();
floor = compute_balloon_floor(); floor = compute_balloon_floor();
/* Refuse to balloon below the floor, keep the 2M granularity. */ /* Refuse to balloon below the floor, keep the 2M granularity. */
if (val.freeram < num_pages || val.freeram - num_pages < floor) { if (avail_pages < num_pages || avail_pages - num_pages < floor) {
num_pages = val.freeram > floor ? (val.freeram - floor) : 0; num_pages = avail_pages > floor ? (avail_pages - floor) : 0;
num_pages -= num_pages % PAGES_IN_2M; num_pages -= num_pages % PAGES_IN_2M;
} }
@ -1172,7 +1235,6 @@ static void balloon_up(struct work_struct *dummy)
bl_resp->hdr.size = sizeof(struct dm_balloon_response); bl_resp->hdr.size = sizeof(struct dm_balloon_response);
bl_resp->more_pages = 1; bl_resp->more_pages = 1;
num_pages -= num_ballooned; num_pages -= num_ballooned;
num_ballooned = alloc_balloon_pages(&dm_device, num_pages, num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
bl_resp, alloc_unit); bl_resp, alloc_unit);
@ -1461,7 +1523,7 @@ static int balloon_probe(struct hv_device *dev,
init_completion(&dm_device.host_event); init_completion(&dm_device.host_event);
init_completion(&dm_device.config_event); init_completion(&dm_device.config_event);
INIT_LIST_HEAD(&dm_device.ha_region_list); INIT_LIST_HEAD(&dm_device.ha_region_list);
mutex_init(&dm_device.ha_region_mutex); spin_lock_init(&dm_device.ha_lock);
INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up); INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req); INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req);
dm_device.host_specified_ha_region = false; dm_device.host_specified_ha_region = false;
@ -1580,8 +1642,9 @@ probe_error0:
static int balloon_remove(struct hv_device *dev) static int balloon_remove(struct hv_device *dev)
{ {
struct hv_dynmem_device *dm = hv_get_drvdata(dev); struct hv_dynmem_device *dm = hv_get_drvdata(dev);
struct list_head *cur, *tmp; struct hv_hotadd_state *has, *tmp;
struct hv_hotadd_state *has; struct hv_hotadd_gap *gap, *tmp_gap;
unsigned long flags;
if (dm->num_pages_ballooned != 0) if (dm->num_pages_ballooned != 0)
pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
@ -1596,11 +1659,16 @@ static int balloon_remove(struct hv_device *dev)
restore_online_page_callback(&hv_online_page); restore_online_page_callback(&hv_online_page);
unregister_memory_notifier(&hv_memory_nb); unregister_memory_notifier(&hv_memory_nb);
#endif #endif
list_for_each_safe(cur, tmp, &dm->ha_region_list) { spin_lock_irqsave(&dm_device.ha_lock, flags);
has = list_entry(cur, struct hv_hotadd_state, list); list_for_each_entry_safe(has, tmp, &dm->ha_region_list, list) {
list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) {
list_del(&gap->list);
kfree(gap);
}
list_del(&has->list); list_del(&has->list);
kfree(has); kfree(has);
} }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
return 0; return 0;
} }

View File

@ -83,6 +83,12 @@ static void fcopy_timeout_func(struct work_struct *dummy)
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
} }
static void fcopy_register_done(void)
{
pr_debug("FCP: userspace daemon registered\n");
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
}
static int fcopy_handle_handshake(u32 version) static int fcopy_handle_handshake(u32 version)
{ {
u32 our_ver = FCOPY_CURRENT_VERSION; u32 our_ver = FCOPY_CURRENT_VERSION;
@ -94,7 +100,8 @@ static int fcopy_handle_handshake(u32 version)
break; break;
case FCOPY_VERSION_1: case FCOPY_VERSION_1:
/* Daemon expects us to reply with our own version */ /* Daemon expects us to reply with our own version */
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver))) if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
fcopy_register_done))
return -EFAULT; return -EFAULT;
dm_reg_value = version; dm_reg_value = version;
break; break;
@ -107,8 +114,7 @@ static int fcopy_handle_handshake(u32 version)
*/ */
return -EINVAL; return -EINVAL;
} }
pr_debug("FCP: userspace daemon ver. %d registered\n", version); pr_debug("FCP: userspace daemon ver. %d connected\n", version);
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
return 0; return 0;
} }
@ -161,7 +167,7 @@ static void fcopy_send_data(struct work_struct *dummy)
} }
fcopy_transaction.state = HVUTIL_USERSPACE_REQ; fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
rc = hvutil_transport_send(hvt, out_src, out_len); rc = hvutil_transport_send(hvt, out_src, out_len, NULL);
if (rc) { if (rc) {
pr_debug("FCP: failed to communicate to the daemon: %d\n", rc); pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
if (cancel_delayed_work_sync(&fcopy_timeout_work)) { if (cancel_delayed_work_sync(&fcopy_timeout_work)) {

View File

@ -102,6 +102,17 @@ static void kvp_poll_wrapper(void *channel)
hv_kvp_onchannelcallback(channel); hv_kvp_onchannelcallback(channel);
} }
static void kvp_register_done(void)
{
/*
* If we're still negotiating with the host cancel the timeout
* work to not poll the channel twice.
*/
pr_debug("KVP: userspace daemon registered\n");
cancel_delayed_work_sync(&kvp_host_handshake_work);
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
}
static void static void
kvp_register(int reg_value) kvp_register(int reg_value)
{ {
@ -116,7 +127,8 @@ kvp_register(int reg_value)
kvp_msg->kvp_hdr.operation = reg_value; kvp_msg->kvp_hdr.operation = reg_value;
strcpy(version, HV_DRV_VERSION); strcpy(version, HV_DRV_VERSION);
hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg)); hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg),
kvp_register_done);
kfree(kvp_msg); kfree(kvp_msg);
} }
} }
@ -158,17 +170,10 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
/* /*
* We have a compatible daemon; complete the handshake. * We have a compatible daemon; complete the handshake.
*/ */
pr_debug("KVP: userspace daemon ver. %d registered\n", pr_debug("KVP: userspace daemon ver. %d connected\n",
KVP_OP_REGISTER); msg->kvp_hdr.operation);
kvp_register(dm_reg_value); kvp_register(dm_reg_value);
/*
* If we're still negotiating with the host cancel the timeout
* work to not poll the channel twice.
*/
cancel_delayed_work_sync(&kvp_host_handshake_work);
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
return 0; return 0;
} }
@ -455,7 +460,7 @@ kvp_send_key(struct work_struct *dummy)
} }
kvp_transaction.state = HVUTIL_USERSPACE_REQ; kvp_transaction.state = HVUTIL_USERSPACE_REQ;
rc = hvutil_transport_send(hvt, message, sizeof(*message)); rc = hvutil_transport_send(hvt, message, sizeof(*message), NULL);
if (rc) { if (rc) {
pr_debug("KVP: failed to communicate to the daemon: %d\n", rc); pr_debug("KVP: failed to communicate to the daemon: %d\n", rc);
if (cancel_delayed_work_sync(&kvp_timeout_work)) { if (cancel_delayed_work_sync(&kvp_timeout_work)) {

View File

@ -67,11 +67,11 @@ static const char vss_devname[] = "vmbus/hv_vss";
static __u8 *recv_buffer; static __u8 *recv_buffer;
static struct hvutil_transport *hvt; static struct hvutil_transport *hvt;
static void vss_send_op(struct work_struct *dummy);
static void vss_timeout_func(struct work_struct *dummy); static void vss_timeout_func(struct work_struct *dummy);
static void vss_handle_request(struct work_struct *dummy);
static DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func); static DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func);
static DECLARE_WORK(vss_send_op_work, vss_send_op); static DECLARE_WORK(vss_handle_request_work, vss_handle_request);
static void vss_poll_wrapper(void *channel) static void vss_poll_wrapper(void *channel)
{ {
@ -95,6 +95,12 @@ static void vss_timeout_func(struct work_struct *dummy)
hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
} }
static void vss_register_done(void)
{
hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
pr_debug("VSS: userspace daemon registered\n");
}
static int vss_handle_handshake(struct hv_vss_msg *vss_msg) static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
{ {
u32 our_ver = VSS_OP_REGISTER1; u32 our_ver = VSS_OP_REGISTER1;
@ -105,16 +111,16 @@ static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
dm_reg_value = VSS_OP_REGISTER; dm_reg_value = VSS_OP_REGISTER;
break; break;
case VSS_OP_REGISTER1: case VSS_OP_REGISTER1:
/* Daemon expects us to reply with our own version*/ /* Daemon expects us to reply with our own version */
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver))) if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
vss_register_done))
return -EFAULT; return -EFAULT;
dm_reg_value = VSS_OP_REGISTER1; dm_reg_value = VSS_OP_REGISTER1;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper); pr_debug("VSS: userspace daemon ver. %d connected\n", dm_reg_value);
pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value);
return 0; return 0;
} }
@ -136,6 +142,11 @@ static int vss_on_msg(void *msg, int len)
return vss_handle_handshake(vss_msg); return vss_handle_handshake(vss_msg);
} else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) { } else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) {
vss_transaction.state = HVUTIL_USERSPACE_RECV; vss_transaction.state = HVUTIL_USERSPACE_RECV;
if (vss_msg->vss_hdr.operation == VSS_OP_HOT_BACKUP)
vss_transaction.msg->vss_cf.flags =
VSS_HBU_NO_AUTO_RECOVERY;
if (cancel_delayed_work_sync(&vss_timeout_work)) { if (cancel_delayed_work_sync(&vss_timeout_work)) {
vss_respond_to_host(vss_msg->error); vss_respond_to_host(vss_msg->error);
/* Transaction is finished, reset the state. */ /* Transaction is finished, reset the state. */
@ -150,8 +161,7 @@ static int vss_on_msg(void *msg, int len)
return 0; return 0;
} }
static void vss_send_op(void)
static void vss_send_op(struct work_struct *dummy)
{ {
int op = vss_transaction.msg->vss_hdr.operation; int op = vss_transaction.msg->vss_hdr.operation;
int rc; int rc;
@ -168,7 +178,10 @@ static void vss_send_op(struct work_struct *dummy)
vss_msg->vss_hdr.operation = op; vss_msg->vss_hdr.operation = op;
vss_transaction.state = HVUTIL_USERSPACE_REQ; vss_transaction.state = HVUTIL_USERSPACE_REQ;
rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg));
schedule_delayed_work(&vss_timeout_work, VSS_USERSPACE_TIMEOUT);
rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
if (rc) { if (rc) {
pr_warn("VSS: failed to communicate to the daemon: %d\n", rc); pr_warn("VSS: failed to communicate to the daemon: %d\n", rc);
if (cancel_delayed_work_sync(&vss_timeout_work)) { if (cancel_delayed_work_sync(&vss_timeout_work)) {
@ -182,6 +195,38 @@ static void vss_send_op(struct work_struct *dummy)
return; return;
} }
static void vss_handle_request(struct work_struct *dummy)
{
switch (vss_transaction.msg->vss_hdr.operation) {
/*
* Initiate a "freeze/thaw" operation in the guest.
* We respond to the host once the operation is complete.
*
* We send the message to the user space daemon and the operation is
* performed in the daemon.
*/
case VSS_OP_THAW:
case VSS_OP_FREEZE:
case VSS_OP_HOT_BACKUP:
if (vss_transaction.state < HVUTIL_READY) {
/* Userspace is not registered yet */
vss_respond_to_host(HV_E_FAIL);
return;
}
vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
vss_send_op();
return;
case VSS_OP_GET_DM_INFO:
vss_transaction.msg->dm_info.flags = 0;
break;
default:
break;
}
vss_respond_to_host(0);
hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
}
/* /*
* Send a response back to the host. * Send a response back to the host.
*/ */
@ -266,48 +311,8 @@ void hv_vss_onchannelcallback(void *context)
vss_transaction.recv_req_id = requestid; vss_transaction.recv_req_id = requestid;
vss_transaction.msg = (struct hv_vss_msg *)vss_msg; vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
switch (vss_msg->vss_hdr.operation) { schedule_work(&vss_handle_request_work);
/* return;
* Initiate a "freeze/thaw"
* operation in the guest.
* We respond to the host once
* the operation is complete.
*
* We send the message to the
* user space daemon and the
* operation is performed in
* the daemon.
*/
case VSS_OP_FREEZE:
case VSS_OP_THAW:
if (vss_transaction.state < HVUTIL_READY) {
/* Userspace is not registered yet */
vss_respond_to_host(HV_E_FAIL);
return;
}
vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
schedule_work(&vss_send_op_work);
schedule_delayed_work(&vss_timeout_work,
VSS_USERSPACE_TIMEOUT);
return;
case VSS_OP_HOT_BACKUP:
vss_msg->vss_cf.flags =
VSS_HBU_NO_AUTO_RECOVERY;
vss_respond_to_host(0);
return;
case VSS_OP_GET_DM_INFO:
vss_msg->dm_info.flags = 0;
vss_respond_to_host(0);
return;
default:
vss_respond_to_host(0);
return;
}
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
@ -358,6 +363,6 @@ void hv_vss_deinit(void)
{ {
vss_transaction.state = HVUTIL_DEVICE_DYING; vss_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&vss_timeout_work); cancel_delayed_work_sync(&vss_timeout_work);
cancel_work_sync(&vss_send_op_work); cancel_work_sync(&vss_handle_request_work);
hvutil_transport_destroy(hvt); hvutil_transport_destroy(hvt);
} }

View File

@ -34,22 +34,25 @@
#define SD_MINOR 0 #define SD_MINOR 0
#define SD_VERSION (SD_MAJOR << 16 | SD_MINOR) #define SD_VERSION (SD_MAJOR << 16 | SD_MINOR)
#define SD_WS2008_MAJOR 1 #define SD_MAJOR_1 1
#define SD_WS2008_VERSION (SD_WS2008_MAJOR << 16 | SD_MINOR) #define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR)
#define TS_MAJOR 3 #define TS_MAJOR 4
#define TS_MINOR 0 #define TS_MINOR 0
#define TS_VERSION (TS_MAJOR << 16 | TS_MINOR) #define TS_VERSION (TS_MAJOR << 16 | TS_MINOR)
#define TS_WS2008_MAJOR 1 #define TS_MAJOR_1 1
#define TS_WS2008_VERSION (TS_WS2008_MAJOR << 16 | TS_MINOR) #define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR)
#define TS_MAJOR_3 3
#define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR)
#define HB_MAJOR 3 #define HB_MAJOR 3
#define HB_MINOR 0 #define HB_MINOR 0
#define HB_VERSION (HB_MAJOR << 16 | HB_MINOR) #define HB_VERSION (HB_MAJOR << 16 | HB_MINOR)
#define HB_WS2008_MAJOR 1 #define HB_MAJOR_1 1
#define HB_WS2008_VERSION (HB_WS2008_MAJOR << 16 | HB_MINOR) #define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR)
static int sd_srv_version; static int sd_srv_version;
static int ts_srv_version; static int ts_srv_version;
@ -61,9 +64,14 @@ static struct hv_util_service util_shutdown = {
.util_cb = shutdown_onchannelcallback, .util_cb = shutdown_onchannelcallback,
}; };
static int hv_timesync_init(struct hv_util_service *srv);
static void hv_timesync_deinit(void);
static void timesync_onchannelcallback(void *context); static void timesync_onchannelcallback(void *context);
static struct hv_util_service util_timesynch = { static struct hv_util_service util_timesynch = {
.util_cb = timesync_onchannelcallback, .util_cb = timesync_onchannelcallback,
.util_init = hv_timesync_init,
.util_deinit = hv_timesync_deinit,
}; };
static void heartbeat_onchannelcallback(void *context); static void heartbeat_onchannelcallback(void *context);
@ -160,20 +168,6 @@ static void shutdown_onchannelcallback(void *context)
schedule_work(&shutdown_work); schedule_work(&shutdown_work);
} }
/*
* Set guest time to host UTC time.
*/
static inline void do_adj_guesttime(u64 hosttime)
{
s64 host_tns;
struct timespec host_ts;
host_tns = (hosttime - WLTIMEDELTA) * 100;
host_ts = ns_to_timespec(host_tns);
do_settimeofday(&host_ts);
}
/* /*
* Set the host time in a process context. * Set the host time in a process context.
*/ */
@ -181,15 +175,37 @@ static inline void do_adj_guesttime(u64 hosttime)
struct adj_time_work { struct adj_time_work {
struct work_struct work; struct work_struct work;
u64 host_time; u64 host_time;
u64 ref_time;
u8 flags;
}; };
static void hv_set_host_time(struct work_struct *work) static void hv_set_host_time(struct work_struct *work)
{ {
struct adj_time_work *wrk; struct adj_time_work *wrk;
s64 host_tns;
u64 newtime;
struct timespec host_ts;
wrk = container_of(work, struct adj_time_work, work); wrk = container_of(work, struct adj_time_work, work);
do_adj_guesttime(wrk->host_time);
kfree(wrk); newtime = wrk->host_time;
if (ts_srv_version > TS_VERSION_3) {
/*
* Some latency has been introduced since Hyper-V generated
* its time sample. Take that latency into account before
* using TSC reference time sample from Hyper-V.
*
* This sample is given by TimeSync v4 and above hosts.
*/
u64 current_tick;
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
newtime += (current_tick - wrk->ref_time);
}
host_tns = (newtime - WLTIMEDELTA) * 100;
host_ts = ns_to_timespec(host_tns);
do_settimeofday(&host_ts);
} }
/* /*
@ -198,33 +214,31 @@ static void hv_set_host_time(struct work_struct *work)
* ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
* After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
* message after the timesync channel is opened. Since the hv_utils module is * message after the timesync channel is opened. Since the hv_utils module is
* loaded after hv_vmbus, the first message is usually missed. The other * loaded after hv_vmbus, the first message is usually missed. This bit is
* thing is, systime is automatically set to emulated hardware clock which may * considered a hard request to discipline the clock.
* not be UTC time or in the same time zone. So, to override these effects, we *
* use the first 50 time samples for initial system time setting. * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is
* typically used as a hint to the guest. The guest is under no obligation
* to discipline the clock.
*/ */
static inline void adj_guesttime(u64 hosttime, u8 flags) static struct adj_time_work wrk;
static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags)
{ {
struct adj_time_work *wrk;
static s32 scnt = 50;
wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC); /*
if (wrk == NULL) * This check is safe since we are executing in the
* interrupt context and time synch messages arre always
* delivered on the same CPU.
*/
if (work_pending(&wrk.work))
return; return;
wrk->host_time = hosttime; wrk.host_time = hosttime;
if ((flags & ICTIMESYNCFLAG_SYNC) != 0) { wrk.ref_time = reftime;
INIT_WORK(&wrk->work, hv_set_host_time); wrk.flags = flags;
schedule_work(&wrk->work); if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) {
return; schedule_work(&wrk.work);
} }
if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
scnt--;
INIT_WORK(&wrk->work, hv_set_host_time);
schedule_work(&wrk->work);
} else
kfree(wrk);
} }
/* /*
@ -237,6 +251,7 @@ static void timesync_onchannelcallback(void *context)
u64 requestid; u64 requestid;
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
struct ictimesync_data *timedatap; struct ictimesync_data *timedatap;
struct ictimesync_ref_data *refdata;
u8 *time_txf_buf = util_timesynch.recv_buffer; u8 *time_txf_buf = util_timesynch.recv_buffer;
struct icmsg_negotiate *negop = NULL; struct icmsg_negotiate *negop = NULL;
@ -252,11 +267,27 @@ static void timesync_onchannelcallback(void *context)
time_txf_buf, time_txf_buf,
util_fw_version, util_fw_version,
ts_srv_version); ts_srv_version);
pr_info("Using TimeSync version %d.%d\n",
ts_srv_version >> 16, ts_srv_version & 0xFFFF);
} else { } else {
timedatap = (struct ictimesync_data *)&time_txf_buf[ if (ts_srv_version > TS_VERSION_3) {
sizeof(struct vmbuspipe_hdr) + refdata = (struct ictimesync_ref_data *)
sizeof(struct icmsg_hdr)]; &time_txf_buf[
adj_guesttime(timedatap->parenttime, timedatap->flags); sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
adj_guesttime(refdata->parenttime,
refdata->vmreferencetime,
refdata->flags);
} else {
timedatap = (struct ictimesync_data *)
&time_txf_buf[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
adj_guesttime(timedatap->parenttime,
0,
timedatap->flags);
}
} }
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
@ -350,16 +381,21 @@ static int util_probe(struct hv_device *dev,
switch (vmbus_proto_version) { switch (vmbus_proto_version) {
case (VERSION_WS2008): case (VERSION_WS2008):
util_fw_version = UTIL_WS2K8_FW_VERSION; util_fw_version = UTIL_WS2K8_FW_VERSION;
sd_srv_version = SD_WS2008_VERSION; sd_srv_version = SD_VERSION_1;
ts_srv_version = TS_WS2008_VERSION; ts_srv_version = TS_VERSION_1;
hb_srv_version = HB_WS2008_VERSION; hb_srv_version = HB_VERSION_1;
break; break;
case(VERSION_WIN10):
default:
util_fw_version = UTIL_FW_VERSION; util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION; sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION; ts_srv_version = TS_VERSION;
hb_srv_version = HB_VERSION; hb_srv_version = HB_VERSION;
break;
default:
util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION_3;
hb_srv_version = HB_VERSION;
} }
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
@ -427,6 +463,17 @@ static struct hv_driver util_drv = {
.remove = util_remove, .remove = util_remove,
}; };
static int hv_timesync_init(struct hv_util_service *srv)
{
INIT_WORK(&wrk.work, hv_set_host_time);
return 0;
}
static void hv_timesync_deinit(void)
{
cancel_work_sync(&wrk.work);
}
static int __init init_hyperv_utils(void) static int __init init_hyperv_utils(void)
{ {
pr_info("Registering HyperV Utility Driver\n"); pr_info("Registering HyperV Utility Driver\n");

View File

@ -72,6 +72,10 @@ static ssize_t hvt_op_read(struct file *file, char __user *buf,
hvt->outmsg = NULL; hvt->outmsg = NULL;
hvt->outmsg_len = 0; hvt->outmsg_len = 0;
if (hvt->on_read)
hvt->on_read();
hvt->on_read = NULL;
out_unlock: out_unlock:
mutex_unlock(&hvt->lock); mutex_unlock(&hvt->lock);
return ret; return ret;
@ -219,7 +223,8 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
mutex_unlock(&hvt->lock); mutex_unlock(&hvt->lock);
} }
int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len,
void (*on_read_cb)(void))
{ {
struct cn_msg *cn_msg; struct cn_msg *cn_msg;
int ret = 0; int ret = 0;
@ -237,6 +242,13 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
memcpy(cn_msg->data, msg, len); memcpy(cn_msg->data, msg, len);
ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC); ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC);
kfree(cn_msg); kfree(cn_msg);
/*
* We don't know when netlink messages are delivered but unlike
* in CHARDEV mode we're not blocked and we can send next
* messages right away.
*/
if (on_read_cb)
on_read_cb();
return ret; return ret;
} }
/* HVUTIL_TRANSPORT_CHARDEV */ /* HVUTIL_TRANSPORT_CHARDEV */
@ -255,6 +267,7 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
if (hvt->outmsg) { if (hvt->outmsg) {
memcpy(hvt->outmsg, msg, len); memcpy(hvt->outmsg, msg, len);
hvt->outmsg_len = len; hvt->outmsg_len = len;
hvt->on_read = on_read_cb;
wake_up_interruptible(&hvt->outmsg_q); wake_up_interruptible(&hvt->outmsg_q);
} else } else
ret = -ENOMEM; ret = -ENOMEM;

View File

@ -36,6 +36,7 @@ struct hvutil_transport {
struct list_head list; /* hvt_list */ struct list_head list; /* hvt_list */
int (*on_msg)(void *, int); /* callback on new user message */ int (*on_msg)(void *, int); /* callback on new user message */
void (*on_reset)(void); /* callback when userspace drops */ void (*on_reset)(void); /* callback when userspace drops */
void (*on_read)(void); /* callback on message read */
u8 *outmsg; /* message to the userspace */ u8 *outmsg; /* message to the userspace */
int outmsg_len; /* its length */ int outmsg_len; /* its length */
wait_queue_head_t outmsg_q; /* poll/read wait queue */ wait_queue_head_t outmsg_q; /* poll/read wait queue */
@ -46,7 +47,8 @@ struct hvutil_transport *hvutil_transport_init(const char *name,
u32 cn_idx, u32 cn_val, u32 cn_idx, u32 cn_val,
int (*on_msg)(void *, int), int (*on_msg)(void *, int),
void (*on_reset)(void)); void (*on_reset)(void));
int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len); int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len,
void (*on_read_cb)(void));
void hvutil_transport_destroy(struct hvutil_transport *hvt); void hvutil_transport_destroy(struct hvutil_transport *hvt);
#endif /* _HV_UTILS_TRANSPORT_H */ #endif /* _HV_UTILS_TRANSPORT_H */

View File

@ -495,7 +495,7 @@ struct hv_ring_buffer_debug_info {
extern int hv_init(void); extern int hv_init(void);
extern void hv_cleanup(void); extern void hv_cleanup(bool crash);
extern int hv_post_message(union hv_connection_id connection_id, extern int hv_post_message(union hv_connection_id connection_id,
enum hv_message_type message_type, enum hv_message_type message_type,
@ -522,14 +522,15 @@ extern unsigned int host_info_edx;
/* Interface */ /* Interface */
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
u32 buflen); struct page *pages, u32 pagecnt);
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
struct kvec *kv_list, struct kvec *kv_list,
u32 kv_count, bool *signal, bool lock); u32 kv_count, bool *signal, bool lock,
enum hv_signal_policy policy);
int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
void *buffer, u32 buflen, u32 *buffer_actual_len, void *buffer, u32 buflen, u32 *buffer_actual_len,

View File

@ -27,6 +27,8 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include "hyperv_vmbus.h" #include "hyperv_vmbus.h"
@ -66,12 +68,20 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
* arrived. * arrived.
*/ */
static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi,
enum hv_signal_policy policy)
{ {
virt_mb(); virt_mb();
if (READ_ONCE(rbi->ring_buffer->interrupt_mask)) if (READ_ONCE(rbi->ring_buffer->interrupt_mask))
return false; return false;
/*
* When the client wants to control signaling,
* we only honour the host interrupt mask.
*/
if (policy == HV_SIGNAL_POLICY_EXPLICIT)
return true;
/* check interrupt_mask before read_index */ /* check interrupt_mask before read_index */
virt_rmb(); virt_rmb();
/* /*
@ -162,18 +172,7 @@ static u32 hv_copyfrom_ringbuffer(
void *ring_buffer = hv_get_ring_buffer(ring_info); void *ring_buffer = hv_get_ring_buffer(ring_info);
u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); u32 ring_buffer_size = hv_get_ring_buffersize(ring_info);
u32 frag_len; memcpy(dest, ring_buffer + start_read_offset, destlen);
/* wrap-around detected at the src */
if (destlen > ring_buffer_size - start_read_offset) {
frag_len = ring_buffer_size - start_read_offset;
memcpy(dest, ring_buffer + start_read_offset, frag_len);
memcpy(dest + frag_len, ring_buffer, destlen - frag_len);
} else
memcpy(dest, ring_buffer + start_read_offset, destlen);
start_read_offset += destlen; start_read_offset += destlen;
start_read_offset %= ring_buffer_size; start_read_offset %= ring_buffer_size;
@ -194,15 +193,8 @@ static u32 hv_copyto_ringbuffer(
{ {
void *ring_buffer = hv_get_ring_buffer(ring_info); void *ring_buffer = hv_get_ring_buffer(ring_info);
u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); u32 ring_buffer_size = hv_get_ring_buffersize(ring_info);
u32 frag_len;
/* wrap-around detected! */ memcpy(ring_buffer + start_write_offset, src, srclen);
if (srclen > ring_buffer_size - start_write_offset) {
frag_len = ring_buffer_size - start_write_offset;
memcpy(ring_buffer + start_write_offset, src, frag_len);
memcpy(ring_buffer, src + frag_len, srclen - frag_len);
} else
memcpy(ring_buffer + start_write_offset, src, srclen);
start_write_offset += srclen; start_write_offset += srclen;
start_write_offset %= ring_buffer_size; start_write_offset %= ring_buffer_size;
@ -235,22 +227,46 @@ void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
/* Initialize the ring buffer. */ /* Initialize the ring buffer. */
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
void *buffer, u32 buflen) struct page *pages, u32 page_cnt)
{ {
if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) int i;
return -EINVAL; struct page **pages_wraparound;
BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE));
memset(ring_info, 0, sizeof(struct hv_ring_buffer_info)); memset(ring_info, 0, sizeof(struct hv_ring_buffer_info));
ring_info->ring_buffer = (struct hv_ring_buffer *)buffer; /*
* First page holds struct hv_ring_buffer, do wraparound mapping for
* the rest.
*/
pages_wraparound = kzalloc(sizeof(struct page *) * (page_cnt * 2 - 1),
GFP_KERNEL);
if (!pages_wraparound)
return -ENOMEM;
pages_wraparound[0] = pages;
for (i = 0; i < 2 * (page_cnt - 1); i++)
pages_wraparound[i + 1] = &pages[i % (page_cnt - 1) + 1];
ring_info->ring_buffer = (struct hv_ring_buffer *)
vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP, PAGE_KERNEL);
kfree(pages_wraparound);
if (!ring_info->ring_buffer)
return -ENOMEM;
ring_info->ring_buffer->read_index = ring_info->ring_buffer->read_index =
ring_info->ring_buffer->write_index = 0; ring_info->ring_buffer->write_index = 0;
/* Set the feature bit for enabling flow control. */ /* Set the feature bit for enabling flow control. */
ring_info->ring_buffer->feature_bits.value = 1; ring_info->ring_buffer->feature_bits.value = 1;
ring_info->ring_size = buflen; ring_info->ring_size = page_cnt << PAGE_SHIFT;
ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); ring_info->ring_datasize = ring_info->ring_size -
sizeof(struct hv_ring_buffer);
spin_lock_init(&ring_info->ring_lock); spin_lock_init(&ring_info->ring_lock);
@ -260,11 +276,13 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
/* Cleanup the ring buffer. */ /* Cleanup the ring buffer. */
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
{ {
vunmap(ring_info->ring_buffer);
} }
/* Write to the ring buffer. */ /* Write to the ring buffer. */
int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
struct kvec *kv_list, u32 kv_count, bool *signal, bool lock) struct kvec *kv_list, u32 kv_count, bool *signal, bool lock,
enum hv_signal_policy policy)
{ {
int i = 0; int i = 0;
u32 bytes_avail_towrite; u32 bytes_avail_towrite;
@ -326,7 +344,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
if (lock) if (lock)
spin_unlock_irqrestore(&outring_info->ring_lock, flags); spin_unlock_irqrestore(&outring_info->ring_lock, flags);
*signal = hv_need_to_signal(old_write, outring_info); *signal = hv_need_to_signal(old_write, outring_info, policy);
return 0; return 0;
} }

View File

@ -105,8 +105,8 @@ static struct notifier_block hyperv_panic_block = {
static const char *fb_mmio_name = "fb_range"; static const char *fb_mmio_name = "fb_range";
static struct resource *fb_mmio; static struct resource *fb_mmio;
struct resource *hyperv_mmio; static struct resource *hyperv_mmio;
DEFINE_SEMAPHORE(hyperv_mmio_lock); static DEFINE_SEMAPHORE(hyperv_mmio_lock);
static int vmbus_exists(void) static int vmbus_exists(void)
{ {
@ -874,7 +874,7 @@ err_alloc:
bus_unregister(&hv_bus); bus_unregister(&hv_bus);
err_cleanup: err_cleanup:
hv_cleanup(); hv_cleanup(false);
return ret; return ret;
} }
@ -961,8 +961,8 @@ int vmbus_device_register(struct hv_device *child_device_obj)
{ {
int ret = 0; int ret = 0;
dev_set_name(&child_device_obj->device, "vmbus_%d", dev_set_name(&child_device_obj->device, "vmbus-%pUl",
child_device_obj->channel->id); child_device_obj->channel->offermsg.offer.if_instance.b);
child_device_obj->device.bus = &hv_bus; child_device_obj->device.bus = &hv_bus;
child_device_obj->device.parent = &hv_acpi_dev->dev; child_device_obj->device.parent = &hv_acpi_dev->dev;
@ -1326,7 +1326,7 @@ static void hv_kexec_handler(void)
vmbus_initiate_unload(false); vmbus_initiate_unload(false);
for_each_online_cpu(cpu) for_each_online_cpu(cpu)
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
hv_cleanup(); hv_cleanup(false);
}; };
static void hv_crash_handler(struct pt_regs *regs) static void hv_crash_handler(struct pt_regs *regs)
@ -1338,7 +1338,7 @@ static void hv_crash_handler(struct pt_regs *regs)
* for kdump. * for kdump.
*/ */
hv_synic_cleanup(NULL); hv_synic_cleanup(NULL);
hv_cleanup(); hv_cleanup(true);
}; };
static int __init hv_acpi_init(void) static int __init hv_acpi_init(void)
@ -1398,7 +1398,7 @@ static void __exit vmbus_exit(void)
&hyperv_panic_block); &hyperv_panic_block);
} }
bus_unregister(&hv_bus); bus_unregister(&hv_bus);
hv_cleanup(); hv_cleanup(false);
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
tasklet_kill(hv_context.event_dpc[cpu]); tasklet_kill(hv_context.event_dpc[cpu]);
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);

View File

@ -184,8 +184,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata)
if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) { if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) {
dev_err(drvdata->dev, dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", "timeout while waiting for completion of Manual Flush\n");
ETB_FFCR);
} }
/* disable trace capture */ /* disable trace capture */
@ -193,8 +192,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata)
if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) { if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) {
dev_err(drvdata->dev, dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", "timeout while waiting for Formatter to Stop\n");
ETB_FFCR);
} }
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
@ -561,7 +559,7 @@ static const struct file_operations etb_fops = {
}; };
#define coresight_etb10_simple_func(name, offset) \ #define coresight_etb10_simple_func(name, offset) \
coresight_simple_func(struct etb_drvdata, name, offset) coresight_simple_func(struct etb_drvdata, NULL, name, offset)
coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG);
coresight_etb10_simple_func(sts, ETB_STATUS_REG); coresight_etb10_simple_func(sts, ETB_STATUS_REG);
@ -638,7 +636,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct etb_drvdata *drvdata; struct etb_drvdata *drvdata;
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
if (np) { if (np) {
@ -684,17 +682,13 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
return -ENOMEM; return -ENOMEM;
} }
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.type = CORESIGHT_DEV_TYPE_SINK;
if (!desc) desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
return -ENOMEM; desc.ops = &etb_cs_ops;
desc.pdata = pdata;
desc->type = CORESIGHT_DEV_TYPE_SINK; desc.dev = dev;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.groups = coresight_etb_groups;
desc->ops = &etb_cs_ops; drvdata->csdev = coresight_register(&desc);
desc->pdata = pdata;
desc->dev = dev;
desc->groups = coresight_etb_groups;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev); return PTR_ERR(drvdata->csdev);

View File

@ -27,6 +27,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "coresight-etm-perf.h"
#include "coresight-priv.h" #include "coresight-priv.h"
static struct pmu etm_pmu; static struct pmu etm_pmu;
@ -71,14 +72,48 @@ static const struct attribute_group *etm_pmu_attr_groups[] = {
static void etm_event_read(struct perf_event *event) {} static void etm_event_read(struct perf_event *event) {}
static int etm_event_init(struct perf_event *event) static int etm_addr_filters_alloc(struct perf_event *event)
{ {
if (event->attr.type != etm_pmu.type) struct etm_filters *filters;
return -ENOENT; int node = event->cpu == -1 ? -1 : cpu_to_node(event->cpu);
filters = kzalloc_node(sizeof(struct etm_filters), GFP_KERNEL, node);
if (!filters)
return -ENOMEM;
if (event->parent)
memcpy(filters, event->parent->hw.addr_filters,
sizeof(*filters));
event->hw.addr_filters = filters;
return 0; return 0;
} }
static void etm_event_destroy(struct perf_event *event)
{
kfree(event->hw.addr_filters);
event->hw.addr_filters = NULL;
}
static int etm_event_init(struct perf_event *event)
{
int ret = 0;
if (event->attr.type != etm_pmu.type) {
ret = -ENOENT;
goto out;
}
ret = etm_addr_filters_alloc(event);
if (ret)
goto out;
event->destroy = etm_event_destroy;
out:
return ret;
}
static void free_event_data(struct work_struct *work) static void free_event_data(struct work_struct *work)
{ {
int cpu; int cpu;
@ -100,7 +135,7 @@ static void free_event_data(struct work_struct *work)
} }
for_each_cpu(cpu, mask) { for_each_cpu(cpu, mask) {
if (event_data->path[cpu]) if (!(IS_ERR_OR_NULL(event_data->path[cpu])))
coresight_release_path(event_data->path[cpu]); coresight_release_path(event_data->path[cpu]);
} }
@ -185,7 +220,7 @@ static void *etm_setup_aux(int event_cpu, void **pages,
* referenced later when the path is actually needed. * referenced later when the path is actually needed.
*/ */
event_data->path[cpu] = coresight_build_path(csdev); event_data->path[cpu] = coresight_build_path(csdev);
if (!event_data->path[cpu]) if (IS_ERR(event_data->path[cpu]))
goto err; goto err;
} }
@ -258,7 +293,7 @@ static void etm_event_start(struct perf_event *event, int flags)
event->hw.state = 0; event->hw.state = 0;
/* Finally enable the tracer */ /* Finally enable the tracer */
if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF)) if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
goto fail_end_stop; goto fail_end_stop;
out: out:
@ -291,7 +326,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
return; return;
/* stop tracer */ /* stop tracer */
source_ops(csdev)->disable(csdev); source_ops(csdev)->disable(csdev, event);
/* tell the core */ /* tell the core */
event->hw.state = PERF_HES_STOPPED; event->hw.state = PERF_HES_STOPPED;
@ -342,6 +377,87 @@ static void etm_event_del(struct perf_event *event, int mode)
etm_event_stop(event, PERF_EF_UPDATE); etm_event_stop(event, PERF_EF_UPDATE);
} }
static int etm_addr_filters_validate(struct list_head *filters)
{
bool range = false, address = false;
int index = 0;
struct perf_addr_filter *filter;
list_for_each_entry(filter, filters, entry) {
/*
* No need to go further if there's no more
* room for filters.
*/
if (++index > ETM_ADDR_CMP_MAX)
return -EOPNOTSUPP;
/*
* As taken from the struct perf_addr_filter documentation:
* @range: 1: range, 0: address
*
* At this time we don't allow range and start/stop filtering
* to cohabitate, they have to be mutually exclusive.
*/
if ((filter->range == 1) && address)
return -EOPNOTSUPP;
if ((filter->range == 0) && range)
return -EOPNOTSUPP;
/*
* For range filtering, the second address in the address
* range comparator needs to be higher than the first.
* Invalid otherwise.
*/
if (filter->range && filter->size == 0)
return -EINVAL;
/*
* Everything checks out with this filter, record what we've
* received before moving on to the next one.
*/
if (filter->range)
range = true;
else
address = true;
}
return 0;
}
static void etm_addr_filters_sync(struct perf_event *event)
{
struct perf_addr_filters_head *head = perf_event_addr_filters(event);
unsigned long start, stop, *offs = event->addr_filters_offs;
struct etm_filters *filters = event->hw.addr_filters;
struct etm_filter *etm_filter;
struct perf_addr_filter *filter;
int i = 0;
list_for_each_entry(filter, &head->list, entry) {
start = filter->offset + offs[i];
stop = start + filter->size;
etm_filter = &filters->etm_filter[i];
if (filter->range == 1) {
etm_filter->start_addr = start;
etm_filter->stop_addr = stop;
etm_filter->type = ETM_ADDR_TYPE_RANGE;
} else {
if (filter->filter == 1) {
etm_filter->start_addr = start;
etm_filter->type = ETM_ADDR_TYPE_START;
} else {
etm_filter->stop_addr = stop;
etm_filter->type = ETM_ADDR_TYPE_STOP;
}
}
i++;
}
filters->nr_filters = i;
}
int etm_perf_symlink(struct coresight_device *csdev, bool link) int etm_perf_symlink(struct coresight_device *csdev, bool link)
{ {
char entry[sizeof("cpu9999999")]; char entry[sizeof("cpu9999999")];
@ -371,18 +487,21 @@ static int __init etm_perf_init(void)
{ {
int ret; int ret;
etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE; etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE;
etm_pmu.attr_groups = etm_pmu_attr_groups; etm_pmu.attr_groups = etm_pmu_attr_groups;
etm_pmu.task_ctx_nr = perf_sw_context; etm_pmu.task_ctx_nr = perf_sw_context;
etm_pmu.read = etm_event_read; etm_pmu.read = etm_event_read;
etm_pmu.event_init = etm_event_init; etm_pmu.event_init = etm_event_init;
etm_pmu.setup_aux = etm_setup_aux; etm_pmu.setup_aux = etm_setup_aux;
etm_pmu.free_aux = etm_free_aux; etm_pmu.free_aux = etm_free_aux;
etm_pmu.start = etm_event_start; etm_pmu.start = etm_event_start;
etm_pmu.stop = etm_event_stop; etm_pmu.stop = etm_event_stop;
etm_pmu.add = etm_event_add; etm_pmu.add = etm_event_add;
etm_pmu.del = etm_event_del; etm_pmu.del = etm_event_del;
etm_pmu.addr_filters_sync = etm_addr_filters_sync;
etm_pmu.addr_filters_validate = etm_addr_filters_validate;
etm_pmu.nr_addr_filters = ETM_ADDR_CMP_MAX;
ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1); ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
if (ret == 0) if (ret == 0)

View File

@ -18,8 +18,42 @@
#ifndef _CORESIGHT_ETM_PERF_H #ifndef _CORESIGHT_ETM_PERF_H
#define _CORESIGHT_ETM_PERF_H #define _CORESIGHT_ETM_PERF_H
#include "coresight-priv.h"
struct coresight_device; struct coresight_device;
/*
* In both ETMv3 and v4 the maximum number of address comparator implentable
* is 8. The actual number is implementation specific and will be checked
* when filters are applied.
*/
#define ETM_ADDR_CMP_MAX 8
/**
* struct etm_filter - single instruction range or start/stop configuration.
* @start_addr: The address to start tracing on.
* @stop_addr: The address to stop tracing on.
* @type: Is this a range or start/stop filter.
*/
struct etm_filter {
unsigned long start_addr;
unsigned long stop_addr;
enum etm_addr_type type;
};
/**
* struct etm_filters - set of filters for a session
* @etm_filter: All the filters for this session.
* @nr_filters: Number of filters
* @ssstatus: Status of the start/stop logic.
*/
struct etm_filters {
struct etm_filter etm_filter[ETM_ADDR_CMP_MAX];
unsigned int nr_filters;
bool ssstatus;
};
#ifdef CONFIG_CORESIGHT #ifdef CONFIG_CORESIGHT
int etm_perf_symlink(struct coresight_device *csdev, bool link); int etm_perf_symlink(struct coresight_device *csdev, bool link);

View File

@ -259,14 +259,6 @@ struct etm_drvdata {
struct etm_config config; struct etm_config config;
}; };
enum etm_addr_type {
ETM_ADDR_TYPE_NONE,
ETM_ADDR_TYPE_SINGLE,
ETM_ADDR_TYPE_RANGE,
ETM_ADDR_TYPE_START,
ETM_ADDR_TYPE_STOP,
};
static inline void etm_writel(struct etm_drvdata *drvdata, static inline void etm_writel(struct etm_drvdata *drvdata,
u32 val, u32 off) u32 val, u32 off)
{ {

View File

@ -18,6 +18,7 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include "coresight-etm.h" #include "coresight-etm.h"
#include "coresight-priv.h"
static ssize_t nr_addr_cmp_show(struct device *dev, static ssize_t nr_addr_cmp_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
@ -1222,7 +1223,7 @@ static struct attribute *coresight_etm_attrs[] = {
}; };
#define coresight_etm3x_simple_func(name, offset) \ #define coresight_etm3x_simple_func(name, offset) \
coresight_simple_func(struct etm_drvdata, name, offset) coresight_simple_func(struct etm_drvdata, NULL, name, offset)
coresight_etm3x_simple_func(etmccr, ETMCCR); coresight_etm3x_simple_func(etmccr, ETMCCR);
coresight_etm3x_simple_func(etmccer, ETMCCER); coresight_etm3x_simple_func(etmccer, ETMCCER);

View File

@ -311,9 +311,10 @@ void etm_config_trace_mode(struct etm_config *config)
#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) #define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
static int etm_parse_event_config(struct etm_drvdata *drvdata, static int etm_parse_event_config(struct etm_drvdata *drvdata,
struct perf_event_attr *attr) struct perf_event *event)
{ {
struct etm_config *config = &drvdata->config; struct etm_config *config = &drvdata->config;
struct perf_event_attr *attr = &event->attr;
if (!attr) if (!attr)
return -EINVAL; return -EINVAL;
@ -459,7 +460,7 @@ static int etm_trace_id(struct coresight_device *csdev)
} }
static int etm_enable_perf(struct coresight_device *csdev, static int etm_enable_perf(struct coresight_device *csdev,
struct perf_event_attr *attr) struct perf_event *event)
{ {
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -467,7 +468,7 @@ static int etm_enable_perf(struct coresight_device *csdev,
return -EINVAL; return -EINVAL;
/* Configure the tracer based on the session's specifics */ /* Configure the tracer based on the session's specifics */
etm_parse_event_config(drvdata, attr); etm_parse_event_config(drvdata, event);
/* And enable it */ /* And enable it */
etm_enable_hw(drvdata); etm_enable_hw(drvdata);
@ -504,7 +505,7 @@ err:
} }
static int etm_enable(struct coresight_device *csdev, static int etm_enable(struct coresight_device *csdev,
struct perf_event_attr *attr, u32 mode) struct perf_event *event, u32 mode)
{ {
int ret; int ret;
u32 val; u32 val;
@ -521,7 +522,7 @@ static int etm_enable(struct coresight_device *csdev,
ret = etm_enable_sysfs(csdev); ret = etm_enable_sysfs(csdev);
break; break;
case CS_MODE_PERF: case CS_MODE_PERF:
ret = etm_enable_perf(csdev, attr); ret = etm_enable_perf(csdev, event);
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
@ -601,7 +602,8 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
dev_info(drvdata->dev, "ETM tracing disabled\n"); dev_info(drvdata->dev, "ETM tracing disabled\n");
} }
static void etm_disable(struct coresight_device *csdev) static void etm_disable(struct coresight_device *csdev,
struct perf_event *event)
{ {
u32 mode; u32 mode;
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -756,13 +758,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct etm_drvdata *drvdata; struct etm_drvdata *drvdata;
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) if (!drvdata)
return -ENOMEM; return -ENOMEM;
@ -825,13 +823,13 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
etm_init_trace_id(drvdata); etm_init_trace_id(drvdata);
etm_set_default(&drvdata->config); etm_set_default(&drvdata->config);
desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
desc->ops = &etm_cs_ops; desc.ops = &etm_cs_ops;
desc->pdata = pdata; desc.pdata = pdata;
desc->dev = dev; desc.dev = dev;
desc->groups = coresight_etm_groups; desc.groups = coresight_etm_groups;
drvdata->csdev = coresight_register(desc); drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) { if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev); ret = PTR_ERR(drvdata->csdev);
goto err_arch_supported; goto err_arch_supported;
@ -893,6 +891,11 @@ static struct amba_id etm_ids[] = {
.mask = 0x0003ffff, .mask = 0x0003ffff,
.data = "ETM 3.3", .data = "ETM 3.3",
}, },
{ /* ETM 3.5 - Cortex-A5 */
.id = 0x0003b955,
.mask = 0x0003ffff,
.data = "ETM 3.5",
},
{ /* ETM 3.5 */ { /* ETM 3.5 */
.id = 0x0003b956, .id = 0x0003b956,
.mask = 0x0003ffff, .mask = 0x0003ffff,

View File

@ -18,6 +18,7 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include "coresight-etm4x.h" #include "coresight-etm4x.h"
#include "coresight-priv.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude)
{ {
@ -2039,15 +2040,42 @@ static struct attribute *coresight_etmv4_attrs[] = {
NULL, NULL,
}; };
#define coresight_etm4x_simple_func(name, offset) \ struct etmv4_reg {
coresight_simple_func(struct etmv4_drvdata, name, offset) void __iomem *addr;
u32 data;
};
static void do_smp_cross_read(void *data)
{
struct etmv4_reg *reg = data;
reg->data = readl_relaxed(reg->addr);
}
static u32 etmv4_cross_read(const struct device *dev, u32 offset)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct etmv4_reg reg;
reg.addr = drvdata->base + offset;
/*
* smp cross call ensures the CPU will be powered up before
* accessing the ETMv4 trace core registers
*/
smp_call_function_single(drvdata->cpu, do_smp_cross_read, &reg, 1);
return reg.data;
}
#define coresight_etm4x_simple_func(name, offset) \
coresight_simple_func(struct etmv4_drvdata, NULL, name, offset)
#define coresight_etm4x_cross_read(name, offset) \
coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \
name, offset)
coresight_etm4x_simple_func(trcoslsr, TRCOSLSR);
coresight_etm4x_simple_func(trcpdcr, TRCPDCR); coresight_etm4x_simple_func(trcpdcr, TRCPDCR);
coresight_etm4x_simple_func(trcpdsr, TRCPDSR); coresight_etm4x_simple_func(trcpdsr, TRCPDSR);
coresight_etm4x_simple_func(trclsr, TRCLSR); coresight_etm4x_simple_func(trclsr, TRCLSR);
coresight_etm4x_simple_func(trcconfig, TRCCONFIGR);
coresight_etm4x_simple_func(trctraceid, TRCTRACEIDR);
coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS);
coresight_etm4x_simple_func(trcdevid, TRCDEVID); coresight_etm4x_simple_func(trcdevid, TRCDEVID);
coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE);
@ -2055,6 +2083,9 @@ coresight_etm4x_simple_func(trcpidr0, TRCPIDR0);
coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); coresight_etm4x_simple_func(trcpidr1, TRCPIDR1);
coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); coresight_etm4x_simple_func(trcpidr2, TRCPIDR2);
coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); coresight_etm4x_simple_func(trcpidr3, TRCPIDR3);
coresight_etm4x_cross_read(trcoslsr, TRCOSLSR);
coresight_etm4x_cross_read(trcconfig, TRCCONFIGR);
coresight_etm4x_cross_read(trctraceid, TRCTRACEIDR);
static struct attribute *coresight_etmv4_mgmt_attrs[] = { static struct attribute *coresight_etmv4_mgmt_attrs[] = {
&dev_attr_trcoslsr.attr, &dev_attr_trcoslsr.attr,
@ -2073,19 +2104,19 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = {
NULL, NULL,
}; };
coresight_etm4x_simple_func(trcidr0, TRCIDR0); coresight_etm4x_cross_read(trcidr0, TRCIDR0);
coresight_etm4x_simple_func(trcidr1, TRCIDR1); coresight_etm4x_cross_read(trcidr1, TRCIDR1);
coresight_etm4x_simple_func(trcidr2, TRCIDR2); coresight_etm4x_cross_read(trcidr2, TRCIDR2);
coresight_etm4x_simple_func(trcidr3, TRCIDR3); coresight_etm4x_cross_read(trcidr3, TRCIDR3);
coresight_etm4x_simple_func(trcidr4, TRCIDR4); coresight_etm4x_cross_read(trcidr4, TRCIDR4);
coresight_etm4x_simple_func(trcidr5, TRCIDR5); coresight_etm4x_cross_read(trcidr5, TRCIDR5);
/* trcidr[6,7] are reserved */ /* trcidr[6,7] are reserved */
coresight_etm4x_simple_func(trcidr8, TRCIDR8); coresight_etm4x_cross_read(trcidr8, TRCIDR8);
coresight_etm4x_simple_func(trcidr9, TRCIDR9); coresight_etm4x_cross_read(trcidr9, TRCIDR9);
coresight_etm4x_simple_func(trcidr10, TRCIDR10); coresight_etm4x_cross_read(trcidr10, TRCIDR10);
coresight_etm4x_simple_func(trcidr11, TRCIDR11); coresight_etm4x_cross_read(trcidr11, TRCIDR11);
coresight_etm4x_simple_func(trcidr12, TRCIDR12); coresight_etm4x_cross_read(trcidr12, TRCIDR12);
coresight_etm4x_simple_func(trcidr13, TRCIDR13); coresight_etm4x_cross_read(trcidr13, TRCIDR13);
static struct attribute *coresight_etmv4_trcidr_attrs[] = { static struct attribute *coresight_etmv4_trcidr_attrs[] = {
&dev_attr_trcidr0.attr, &dev_attr_trcidr0.attr,

View File

@ -33,7 +33,6 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/perf_event.h>
#include <asm/sections.h> #include <asm/sections.h>
#include <asm/local.h> #include <asm/local.h>
@ -46,7 +45,9 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
/* The number of ETMv4 currently registered */ /* The number of ETMv4 currently registered */
static int etm4_count; static int etm4_count;
static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
static void etm4_set_default(struct etmv4_config *config); static void etm4_set_default_config(struct etmv4_config *config);
static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
struct perf_event *event);
static enum cpuhp_state hp_online; static enum cpuhp_state hp_online;
@ -79,22 +80,8 @@ static int etm4_cpu_id(struct coresight_device *csdev)
static int etm4_trace_id(struct coresight_device *csdev) static int etm4_trace_id(struct coresight_device *csdev)
{ {
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
unsigned long flags;
int trace_id = -1;
if (!local_read(&drvdata->mode)) return drvdata->trcid;
return drvdata->trcid;
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR);
trace_id &= ETM_TRACEID_MASK;
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return trace_id;
} }
static void etm4_enable_hw(void *info) static void etm4_enable_hw(void *info)
@ -113,8 +100,7 @@ static void etm4_enable_hw(void *info)
/* wait for TRCSTATR.IDLE to go up */ /* wait for TRCSTATR.IDLE to go up */
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1))
dev_err(drvdata->dev, dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", "timeout while waiting for Idle Trace Status\n");
TRCSTATR);
writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR); writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR);
writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR); writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR);
@ -180,14 +166,20 @@ static void etm4_enable_hw(void *info)
writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0);
writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1);
/*
* Request to keep the trace unit powered and also
* emulation of powerdown
*/
writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | TRCPDCR_PU,
drvdata->base + TRCPDCR);
/* Enable the trace unit */ /* Enable the trace unit */
writel_relaxed(1, drvdata->base + TRCPRGCTLR); writel_relaxed(1, drvdata->base + TRCPRGCTLR);
/* wait for TRCSTATR.IDLE to go back down to '0' */ /* wait for TRCSTATR.IDLE to go back down to '0' */
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0)) if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0))
dev_err(drvdata->dev, dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", "timeout while waiting for Idle Trace Status\n");
TRCSTATR);
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
@ -195,12 +187,16 @@ static void etm4_enable_hw(void *info)
} }
static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
struct perf_event_attr *attr) struct perf_event *event)
{ {
int ret = 0;
struct etmv4_config *config = &drvdata->config; struct etmv4_config *config = &drvdata->config;
struct perf_event_attr *attr = &event->attr;
if (!attr) if (!attr) {
return -EINVAL; ret = -EINVAL;
goto out;
}
/* Clear configuration from previous run */ /* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config)); memset(config, 0, sizeof(struct etmv4_config));
@ -212,14 +208,12 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
config->mode = ETM_MODE_EXCL_USER; config->mode = ETM_MODE_EXCL_USER;
/* Always start from the default config */ /* Always start from the default config */
etm4_set_default(config); etm4_set_default_config(config);
/* /* Configure filters specified on the perf cmd line, if any. */
* By default the tracers are configured to trace the whole address ret = etm4_set_event_filters(drvdata, event);
* range. Narrow the field only if requested by user space. if (ret)
*/ goto out;
if (config->mode)
etm4_config_trace_mode(config);
/* Go from generic option to ETMv4 specifics */ /* Go from generic option to ETMv4 specifics */
if (attr->config & BIT(ETM_OPT_CYCACC)) if (attr->config & BIT(ETM_OPT_CYCACC))
@ -227,23 +221,30 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
if (attr->config & BIT(ETM_OPT_TS)) if (attr->config & BIT(ETM_OPT_TS))
config->cfg |= ETMv4_MODE_TIMESTAMP; config->cfg |= ETMv4_MODE_TIMESTAMP;
return 0; out:
return ret;
} }
static int etm4_enable_perf(struct coresight_device *csdev, static int etm4_enable_perf(struct coresight_device *csdev,
struct perf_event_attr *attr) struct perf_event *event)
{ {
int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
return -EINVAL; ret = -EINVAL;
goto out;
}
/* Configure the tracer based on the session's specifics */ /* Configure the tracer based on the session's specifics */
etm4_parse_event_config(drvdata, attr); ret = etm4_parse_event_config(drvdata, event);
if (ret)
goto out;
/* And enable it */ /* And enable it */
etm4_enable_hw(drvdata); etm4_enable_hw(drvdata);
return 0; out:
return ret;
} }
static int etm4_enable_sysfs(struct coresight_device *csdev) static int etm4_enable_sysfs(struct coresight_device *csdev)
@ -274,7 +275,7 @@ err:
} }
static int etm4_enable(struct coresight_device *csdev, static int etm4_enable(struct coresight_device *csdev,
struct perf_event_attr *attr, u32 mode) struct perf_event *event, u32 mode)
{ {
int ret; int ret;
u32 val; u32 val;
@ -291,7 +292,7 @@ static int etm4_enable(struct coresight_device *csdev,
ret = etm4_enable_sysfs(csdev); ret = etm4_enable_sysfs(csdev);
break; break;
case CS_MODE_PERF: case CS_MODE_PERF:
ret = etm4_enable_perf(csdev, attr); ret = etm4_enable_perf(csdev, event);
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
@ -311,6 +312,11 @@ static void etm4_disable_hw(void *info)
CS_UNLOCK(drvdata->base); CS_UNLOCK(drvdata->base);
/* power can be removed from the trace unit now */
control = readl_relaxed(drvdata->base + TRCPDCR);
control &= ~TRCPDCR_PU;
writel_relaxed(control, drvdata->base + TRCPDCR);
control = readl_relaxed(drvdata->base + TRCPRGCTLR); control = readl_relaxed(drvdata->base + TRCPRGCTLR);
/* EN, bit[0] Trace unit enable bit */ /* EN, bit[0] Trace unit enable bit */
@ -326,14 +332,28 @@ static void etm4_disable_hw(void *info)
dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
} }
static int etm4_disable_perf(struct coresight_device *csdev) static int etm4_disable_perf(struct coresight_device *csdev,
struct perf_event *event)
{ {
u32 control;
struct etm_filters *filters = event->hw.addr_filters;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
return -EINVAL; return -EINVAL;
etm4_disable_hw(drvdata); etm4_disable_hw(drvdata);
/*
* Check if the start/stop logic was active when the unit was stopped.
* That way we can re-enable the start/stop logic when the process is
* scheduled again. Configuration of the start/stop logic happens in
* function etm4_set_event_filters().
*/
control = readl_relaxed(drvdata->base + TRCVICTLR);
/* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
return 0; return 0;
} }
@ -362,7 +382,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
dev_info(drvdata->dev, "ETM tracing disabled\n"); dev_info(drvdata->dev, "ETM tracing disabled\n");
} }
static void etm4_disable(struct coresight_device *csdev) static void etm4_disable(struct coresight_device *csdev,
struct perf_event *event)
{ {
u32 mode; u32 mode;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -381,7 +402,7 @@ static void etm4_disable(struct coresight_device *csdev)
etm4_disable_sysfs(csdev); etm4_disable_sysfs(csdev);
break; break;
case CS_MODE_PERF: case CS_MODE_PERF:
etm4_disable_perf(csdev); etm4_disable_perf(csdev, event);
break; break;
} }
@ -564,21 +585,8 @@ static void etm4_init_arch_data(void *info)
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
} }
static void etm4_set_default(struct etmv4_config *config) static void etm4_set_default_config(struct etmv4_config *config)
{ {
if (WARN_ON_ONCE(!config))
return;
/*
* Make default initialisation trace everything
*
* Select the "always true" resource selector on the
* "Enablign Event" line and configure address range comparator
* '0' to trace all the possible address range. From there
* configure the "include/exclude" engine to include address
* range comparator '0'.
*/
/* disable all events tracing */ /* disable all events tracing */
config->eventctrl0 = 0x0; config->eventctrl0 = 0x0;
config->eventctrl1 = 0x0; config->eventctrl1 = 0x0;
@ -594,6 +602,108 @@ static void etm4_set_default(struct etmv4_config *config)
/* TRCVICTLR::EVENT = 0x01, select the always on logic */ /* TRCVICTLR::EVENT = 0x01, select the always on logic */
config->vinst_ctrl |= BIT(0); config->vinst_ctrl |= BIT(0);
}
static u64 etm4_get_access_type(struct etmv4_config *config)
{
u64 access_type = 0;
/*
* EXLEVEL_NS, bits[15:12]
* The Exception levels are:
* Bit[12] Exception level 0 - Application
* Bit[13] Exception level 1 - OS
* Bit[14] Exception level 2 - Hypervisor
* Bit[15] Never implemented
*
* Always stay away from hypervisor mode.
*/
access_type = ETM_EXLEVEL_NS_HYP;
if (config->mode & ETM_MODE_EXCL_KERN)
access_type |= ETM_EXLEVEL_NS_OS;
if (config->mode & ETM_MODE_EXCL_USER)
access_type |= ETM_EXLEVEL_NS_APP;
/*
* EXLEVEL_S, bits[11:8], don't trace anything happening
* in secure state.
*/
access_type |= (ETM_EXLEVEL_S_APP |
ETM_EXLEVEL_S_OS |
ETM_EXLEVEL_S_HYP);
return access_type;
}
static void etm4_set_comparator_filter(struct etmv4_config *config,
u64 start, u64 stop, int comparator)
{
u64 access_type = etm4_get_access_type(config);
/* First half of default address comparator */
config->addr_val[comparator] = start;
config->addr_acc[comparator] = access_type;
config->addr_type[comparator] = ETM_ADDR_TYPE_RANGE;
/* Second half of default address comparator */
config->addr_val[comparator + 1] = stop;
config->addr_acc[comparator + 1] = access_type;
config->addr_type[comparator + 1] = ETM_ADDR_TYPE_RANGE;
/*
* Configure the ViewInst function to include this address range
* comparator.
*
* @comparator is divided by two since it is the index in the
* etmv4_config::addr_val array but register TRCVIIECTLR deals with
* address range comparator _pairs_.
*
* Therefore:
* index 0 -> compatator pair 0
* index 2 -> comparator pair 1
* index 4 -> comparator pair 2
* ...
* index 14 -> comparator pair 7
*/
config->viiectlr |= BIT(comparator / 2);
}
static void etm4_set_start_stop_filter(struct etmv4_config *config,
u64 address, int comparator,
enum etm_addr_type type)
{
int shift;
u64 access_type = etm4_get_access_type(config);
/* Configure the comparator */
config->addr_val[comparator] = address;
config->addr_acc[comparator] = access_type;
config->addr_type[comparator] = type;
/*
* Configure ViewInst Start-Stop control register.
* Addresses configured to start tracing go from bit 0 to n-1,
* while those configured to stop tracing from 16 to 16 + n-1.
*/
shift = (type == ETM_ADDR_TYPE_START ? 0 : 16);
config->vissctlr |= BIT(shift + comparator);
}
static void etm4_set_default_filter(struct etmv4_config *config)
{
u64 start, stop;
/*
* Configure address range comparator '0' to encompass all
* possible addresses.
*/
start = 0x0;
stop = ~0x0;
etm4_set_comparator_filter(config, start, stop,
ETM_DEFAULT_ADDR_COMP);
/* /*
* TRCVICTLR::SSSTATUS == 1, the start-stop logic is * TRCVICTLR::SSSTATUS == 1, the start-stop logic is
@ -601,45 +711,158 @@ static void etm4_set_default(struct etmv4_config *config)
*/ */
config->vinst_ctrl |= BIT(9); config->vinst_ctrl |= BIT(9);
/* /* No start-stop filtering for ViewInst */
* Configure address range comparator '0' to encompass all
* possible addresses.
*/
/* First half of default address comparator: start at address 0 */
config->addr_val[ETM_DEFAULT_ADDR_COMP] = 0x0;
/* trace instruction addresses */
config->addr_acc[ETM_DEFAULT_ADDR_COMP] &= ~(BIT(0) | BIT(1));
/* EXLEVEL_NS, bits[12:15], only trace application and kernel space */
config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= ETM_EXLEVEL_NS_HYP;
/* EXLEVEL_S, bits[11:8], don't trace anything in secure state */
config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= (ETM_EXLEVEL_S_APP |
ETM_EXLEVEL_S_OS |
ETM_EXLEVEL_S_HYP);
config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE;
/*
* Second half of default address comparator: go all
* the way to the top.
*/
config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = ~0x0;
/* trace instruction addresses */
config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] &= ~(BIT(0) | BIT(1));
/* Address comparator type must be equal for both halves */
config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] =
config->addr_acc[ETM_DEFAULT_ADDR_COMP];
config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE;
/*
* Configure the ViewInst function to filter on address range
* comparator '0'.
*/
config->viiectlr = BIT(0);
/* no start-stop filtering for ViewInst */
config->vissctlr = 0x0; config->vissctlr = 0x0;
} }
static void etm4_set_default(struct etmv4_config *config)
{
if (WARN_ON_ONCE(!config))
return;
/*
* Make default initialisation trace everything
*
* Select the "always true" resource selector on the
* "Enablign Event" line and configure address range comparator
* '0' to trace all the possible address range. From there
* configure the "include/exclude" engine to include address
* range comparator '0'.
*/
etm4_set_default_config(config);
etm4_set_default_filter(config);
}
static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type)
{
int nr_comparator, index = 0;
struct etmv4_config *config = &drvdata->config;
/*
* nr_addr_cmp holds the number of comparator _pair_, so time 2
* for the total number of comparators.
*/
nr_comparator = drvdata->nr_addr_cmp * 2;
/* Go through the tally of comparators looking for a free one. */
while (index < nr_comparator) {
switch (type) {
case ETM_ADDR_TYPE_RANGE:
if (config->addr_type[index] == ETM_ADDR_TYPE_NONE &&
config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE)
return index;
/* Address range comparators go in pairs */
index += 2;
break;
case ETM_ADDR_TYPE_START:
case ETM_ADDR_TYPE_STOP:
if (config->addr_type[index] == ETM_ADDR_TYPE_NONE)
return index;
/* Start/stop address can have odd indexes */
index += 1;
break;
default:
return -EINVAL;
}
}
/* If we are here all the comparators have been used. */
return -ENOSPC;
}
static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
struct perf_event *event)
{
int i, comparator, ret = 0;
u64 address;
struct etmv4_config *config = &drvdata->config;
struct etm_filters *filters = event->hw.addr_filters;
if (!filters)
goto default_filter;
/* Sync events with what Perf got */
perf_event_addr_filters_sync(event);
/*
* If there are no filters to deal with simply go ahead with
* the default filter, i.e the entire address range.
*/
if (!filters->nr_filters)
goto default_filter;
for (i = 0; i < filters->nr_filters; i++) {
struct etm_filter *filter = &filters->etm_filter[i];
enum etm_addr_type type = filter->type;
/* See if a comparator is free. */
comparator = etm4_get_next_comparator(drvdata, type);
if (comparator < 0) {
ret = comparator;
goto out;
}
switch (type) {
case ETM_ADDR_TYPE_RANGE:
etm4_set_comparator_filter(config,
filter->start_addr,
filter->stop_addr,
comparator);
/*
* TRCVICTLR::SSSTATUS == 1, the start-stop logic is
* in the started state
*/
config->vinst_ctrl |= BIT(9);
/* No start-stop filtering for ViewInst */
config->vissctlr = 0x0;
break;
case ETM_ADDR_TYPE_START:
case ETM_ADDR_TYPE_STOP:
/* Get the right start or stop address */
address = (type == ETM_ADDR_TYPE_START ?
filter->start_addr :
filter->stop_addr);
/* Configure comparator */
etm4_set_start_stop_filter(config, address,
comparator, type);
/*
* If filters::ssstatus == 1, trace acquisition was
* started but the process was yanked away before the
* the stop address was hit. As such the start/stop
* logic needs to be re-started so that tracing can
* resume where it left.
*
* The start/stop logic status when a process is
* scheduled out is checked in function
* etm4_disable_perf().
*/
if (filters->ssstatus)
config->vinst_ctrl |= BIT(9);
/* No include/exclude filtering for ViewInst */
config->viiectlr = 0x0;
break;
default:
ret = -EINVAL;
goto out;
}
}
goto out;
default_filter:
etm4_set_default_filter(config);
out:
return ret;
}
void etm4_config_trace_mode(struct etmv4_config *config) void etm4_config_trace_mode(struct etmv4_config *config)
{ {
u32 addr_acc, mode; u32 addr_acc, mode;
@ -727,13 +950,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct etmv4_drvdata *drvdata; struct etmv4_drvdata *drvdata;
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) if (!drvdata)
return -ENOMEM; return -ENOMEM;
@ -788,13 +1007,13 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
etm4_init_trace_id(drvdata); etm4_init_trace_id(drvdata);
etm4_set_default(&drvdata->config); etm4_set_default(&drvdata->config);
desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
desc->ops = &etm4_cs_ops; desc.ops = &etm4_cs_ops;
desc->pdata = pdata; desc.pdata = pdata;
desc->dev = dev; desc.dev = dev;
desc->groups = coresight_etmv4_groups; desc.groups = coresight_etmv4_groups;
drvdata->csdev = coresight_register(desc); drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) { if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev); ret = PTR_ERR(drvdata->csdev);
goto err_arch_supported; goto err_arch_supported;
@ -826,12 +1045,12 @@ err_arch_supported:
} }
static struct amba_id etm4_ids[] = { static struct amba_id etm4_ids[] = {
{ /* ETM 4.0 - Qualcomm */ { /* ETM 4.0 - Cortex-A53 */
.id = 0x0003b95d, .id = 0x000bb95d,
.mask = 0x0003ffff, .mask = 0x000fffff,
.data = "ETM 4.0", .data = "ETM 4.0",
}, },
{ /* ETM 4.0 - Juno board */ { /* ETM 4.0 - Cortex-A57 */
.id = 0x000bb95e, .id = 0x000bb95e,
.mask = 0x000fffff, .mask = 0x000fffff,
.data = "ETM 4.0", .data = "ETM 4.0",

View File

@ -183,6 +183,9 @@
#define TRCSTATR_IDLE_BIT 0 #define TRCSTATR_IDLE_BIT 0
#define ETM_DEFAULT_ADDR_COMP 0 #define ETM_DEFAULT_ADDR_COMP 0
/* PowerDown Control Register bits */
#define TRCPDCR_PU BIT(3)
/* secure state access levels */ /* secure state access levels */
#define ETM_EXLEVEL_S_APP BIT(8) #define ETM_EXLEVEL_S_APP BIT(8)
#define ETM_EXLEVEL_S_OS BIT(9) #define ETM_EXLEVEL_S_OS BIT(9)
@ -407,14 +410,6 @@ enum etm_addr_ctxtype {
ETM_CTX_CTXID_VMID, ETM_CTX_CTXID_VMID,
}; };
enum etm_addr_type {
ETM_ADDR_TYPE_NONE,
ETM_ADDR_TYPE_SINGLE,
ETM_ADDR_TYPE_RANGE,
ETM_ADDR_TYPE_START,
ETM_ADDR_TYPE_STOP,
};
extern const struct attribute_group *coresight_etmv4_groups[]; extern const struct attribute_group *coresight_etmv4_groups[];
void etm4_config_trace_mode(struct etmv4_config *config); void etm4_config_trace_mode(struct etmv4_config *config);
#endif #endif

View File

@ -176,7 +176,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct funnel_drvdata *drvdata; struct funnel_drvdata *drvdata;
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
if (np) { if (np) {
@ -207,17 +207,13 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->base = base; drvdata->base = base;
pm_runtime_put(&adev->dev); pm_runtime_put(&adev->dev);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.type = CORESIGHT_DEV_TYPE_LINK;
if (!desc) desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
return -ENOMEM; desc.ops = &funnel_cs_ops;
desc.pdata = pdata;
desc->type = CORESIGHT_DEV_TYPE_LINK; desc.dev = dev;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; desc.groups = coresight_funnel_groups;
desc->ops = &funnel_cs_ops; drvdata->csdev = coresight_register(&desc);
desc->pdata = pdata;
desc->dev = dev;
desc->groups = coresight_funnel_groups;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev); return PTR_ERR(drvdata->csdev);

View File

@ -16,6 +16,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/coresight.h> #include <linux/coresight.h>
#include <linux/pm_runtime.h>
/* /*
* Coresight management registers (0xf00-0xfcc) * Coresight management registers (0xf00-0xfcc)
@ -37,16 +38,32 @@
#define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_KERN BIT(30)
#define ETM_MODE_EXCL_USER BIT(31) #define ETM_MODE_EXCL_USER BIT(31)
#define coresight_simple_func(type, name, offset) \ typedef u32 (*coresight_read_fn)(const struct device *, u32 offset);
#define coresight_simple_func(type, func, name, offset) \
static ssize_t name##_show(struct device *_dev, \ static ssize_t name##_show(struct device *_dev, \
struct device_attribute *attr, char *buf) \ struct device_attribute *attr, char *buf) \
{ \ { \
type *drvdata = dev_get_drvdata(_dev->parent); \ type *drvdata = dev_get_drvdata(_dev->parent); \
return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ coresight_read_fn fn = func; \
readl_relaxed(drvdata->base + offset)); \ u32 val; \
pm_runtime_get_sync(_dev->parent); \
if (fn) \
val = fn(_dev->parent, offset); \
else \
val = readl_relaxed(drvdata->base + offset); \
pm_runtime_put_sync(_dev->parent); \
return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \
} \ } \
static DEVICE_ATTR_RO(name) static DEVICE_ATTR_RO(name)
enum etm_addr_type {
ETM_ADDR_TYPE_NONE,
ETM_ADDR_TYPE_SINGLE,
ETM_ADDR_TYPE_RANGE,
ETM_ADDR_TYPE_START,
ETM_ADDR_TYPE_STOP,
};
enum cs_mode { enum cs_mode {
CS_MODE_DISABLED, CS_MODE_DISABLED,
CS_MODE_SYSFS, CS_MODE_SYSFS,

View File

@ -102,7 +102,7 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct replicator_state *drvdata; struct replicator_state *drvdata;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
void __iomem *base; void __iomem *base;
@ -134,16 +134,12 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
dev_set_drvdata(dev, drvdata); dev_set_drvdata(dev, drvdata);
pm_runtime_put(&adev->dev); pm_runtime_put(&adev->dev);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.type = CORESIGHT_DEV_TYPE_LINK;
if (!desc) desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
return -ENOMEM; desc.ops = &replicator_cs_ops;
desc.pdata = adev->dev.platform_data;
desc->type = CORESIGHT_DEV_TYPE_LINK; desc.dev = &adev->dev;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; drvdata->csdev = coresight_register(&desc);
desc->ops = &replicator_cs_ops;
desc->pdata = adev->dev.platform_data;
desc->dev = &adev->dev;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev); return PTR_ERR(drvdata->csdev);

View File

@ -69,7 +69,7 @@ static int replicator_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct replicator_drvdata *drvdata; struct replicator_drvdata *drvdata;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
if (np) { if (np) {
@ -95,18 +95,12 @@ static int replicator_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
platform_set_drvdata(pdev, drvdata); platform_set_drvdata(pdev, drvdata);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.type = CORESIGHT_DEV_TYPE_LINK;
if (!desc) { desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
ret = -ENOMEM; desc.ops = &replicator_cs_ops;
goto out_disable_pm; desc.pdata = pdev->dev.platform_data;
} desc.dev = &pdev->dev;
drvdata->csdev = coresight_register(&desc);
desc->type = CORESIGHT_DEV_TYPE_LINK;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
desc->ops = &replicator_cs_ops;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) { if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev); ret = PTR_ERR(drvdata->csdev);
goto out_disable_pm; goto out_disable_pm;

View File

@ -105,10 +105,12 @@ module_param_named(
/** /**
* struct channel_space - central management entity for extended ports * struct channel_space - central management entity for extended ports
* @base: memory mapped base address where channels start. * @base: memory mapped base address where channels start.
* @phys: physical base address of channel region.
* @guaraneed: is the channel delivery guaranteed. * @guaraneed: is the channel delivery guaranteed.
*/ */
struct channel_space { struct channel_space {
void __iomem *base; void __iomem *base;
phys_addr_t phys;
unsigned long *guaranteed; unsigned long *guaranteed;
}; };
@ -196,7 +198,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
} }
static int stm_enable(struct coresight_device *csdev, static int stm_enable(struct coresight_device *csdev,
struct perf_event_attr *attr, u32 mode) struct perf_event *event, u32 mode)
{ {
u32 val; u32 val;
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -258,7 +260,8 @@ static void stm_disable_hw(struct stm_drvdata *drvdata)
stm_hwevent_disable_hw(drvdata); stm_hwevent_disable_hw(drvdata);
} }
static void stm_disable(struct coresight_device *csdev) static void stm_disable(struct coresight_device *csdev,
struct perf_event *event)
{ {
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -353,7 +356,24 @@ static void stm_generic_unlink(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev) if (!drvdata || !drvdata->csdev)
return; return;
stm_disable(drvdata->csdev); stm_disable(drvdata->csdev, NULL);
}
static phys_addr_t
stm_mmio_addr(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int nr_chans)
{
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
phys_addr_t addr;
addr = drvdata->chs.phys + channel * BYTES_PER_CHANNEL;
if (offset_in_page(addr) ||
offset_in_page(nr_chans * BYTES_PER_CHANNEL))
return 0;
return addr;
} }
static long stm_generic_set_options(struct stm_data *stm_data, static long stm_generic_set_options(struct stm_data *stm_data,
@ -616,7 +636,7 @@ static ssize_t traceid_store(struct device *dev,
static DEVICE_ATTR_RW(traceid); static DEVICE_ATTR_RW(traceid);
#define coresight_stm_simple_func(name, offset) \ #define coresight_stm_simple_func(name, offset) \
coresight_simple_func(struct stm_drvdata, name, offset) coresight_simple_func(struct stm_drvdata, NULL, name, offset)
coresight_stm_simple_func(tcsr, STMTCSR); coresight_stm_simple_func(tcsr, STMTCSR);
coresight_stm_simple_func(tsfreqr, STMTSFREQR); coresight_stm_simple_func(tsfreqr, STMTSFREQR);
@ -761,7 +781,9 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata)
drvdata->stm.sw_end = 1; drvdata->stm.sw_end = 1;
drvdata->stm.hw_override = true; drvdata->stm.hw_override = true;
drvdata->stm.sw_nchannels = drvdata->numsp; drvdata->stm.sw_nchannels = drvdata->numsp;
drvdata->stm.sw_mmiosz = BYTES_PER_CHANNEL;
drvdata->stm.packet = stm_generic_packet; drvdata->stm.packet = stm_generic_packet;
drvdata->stm.mmio_addr = stm_mmio_addr;
drvdata->stm.link = stm_generic_link; drvdata->stm.link = stm_generic_link;
drvdata->stm.unlink = stm_generic_unlink; drvdata->stm.unlink = stm_generic_unlink;
drvdata->stm.set_options = stm_generic_set_options; drvdata->stm.set_options = stm_generic_set_options;
@ -778,7 +800,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct resource ch_res; struct resource ch_res;
size_t res_size, bitmap_size; size_t res_size, bitmap_size;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
if (np) { if (np) {
@ -808,6 +830,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res);
if (ret) if (ret)
return ret; return ret;
drvdata->chs.phys = ch_res.start;
base = devm_ioremap_resource(dev, &ch_res); base = devm_ioremap_resource(dev, &ch_res);
if (IS_ERR(base)) if (IS_ERR(base))
@ -843,19 +866,13 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
return -EPROBE_DEFER; return -EPROBE_DEFER;
} }
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.type = CORESIGHT_DEV_TYPE_SOURCE;
if (!desc) { desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
ret = -ENOMEM; desc.ops = &stm_cs_ops;
goto stm_unregister; desc.pdata = pdata;
} desc.dev = dev;
desc.groups = coresight_stm_groups;
desc->type = CORESIGHT_DEV_TYPE_SOURCE; drvdata->csdev = coresight_register(&desc);
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
desc->ops = &stm_cs_ops;
desc->pdata = pdata;
desc->dev = dev;
desc->groups = coresight_stm_groups;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) { if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev); ret = PTR_ERR(drvdata->csdev);
goto stm_unregister; goto stm_unregister;

View File

@ -22,7 +22,7 @@
#include "coresight-priv.h" #include "coresight-priv.h"
#include "coresight-tmc.h" #include "coresight-tmc.h"
void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
{ {
CS_UNLOCK(drvdata->base); CS_UNLOCK(drvdata->base);
@ -48,6 +48,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
int i; int i;
bufp = drvdata->buf; bufp = drvdata->buf;
drvdata->len = 0;
while (1) { while (1) {
for (i = 0; i < drvdata->memwidth; i++) { for (i = 0; i < drvdata->memwidth; i++) {
read_data = readl_relaxed(drvdata->base + TMC_RRD); read_data = readl_relaxed(drvdata->base + TMC_RRD);
@ -55,6 +56,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
return; return;
memcpy(bufp, &read_data, 4); memcpy(bufp, &read_data, 4);
bufp += 4; bufp += 4;
drvdata->len += 4;
} }
} }
} }
@ -166,7 +168,7 @@ out:
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free memory outside the spinlock if need be */ /* Free memory outside the spinlock if need be */
if (!used && buf) if (!used)
kfree(buf); kfree(buf);
if (!ret) if (!ret)

View File

@ -20,7 +20,7 @@
#include "coresight-priv.h" #include "coresight-priv.h"
#include "coresight-tmc.h" #include "coresight-tmc.h"
void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{ {
u32 axictl; u32 axictl;
@ -64,11 +64,17 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
rwp = readl_relaxed(drvdata->base + TMC_RWP); rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS); val = readl_relaxed(drvdata->base + TMC_STS);
/* How much memory do we still have */ /*
if (val & BIT(0)) * Adjust the buffer to point to the beginning of the trace data
* and update the available trace data.
*/
if (val & TMC_STS_FULL) {
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
else drvdata->len = drvdata->size;
} else {
drvdata->buf = drvdata->vaddr; drvdata->buf = drvdata->vaddr;
drvdata->len = rwp - drvdata->paddr;
}
} }
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)

View File

@ -38,8 +38,7 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
if (coresight_timeout(drvdata->base, if (coresight_timeout(drvdata->base,
TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
dev_err(drvdata->dev, dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", "timeout while waiting for TMC to be Ready\n");
TMC_STS);
} }
} }
@ -56,8 +55,7 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
if (coresight_timeout(drvdata->base, if (coresight_timeout(drvdata->base,
TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) { TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) {
dev_err(drvdata->dev, dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", "timeout while waiting for completion of Manual Flush\n");
TMC_FFCR);
} }
tmc_wait_for_tmcready(drvdata); tmc_wait_for_tmcready(drvdata);
@ -140,8 +138,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
struct tmc_drvdata, miscdev); struct tmc_drvdata, miscdev);
char *bufp = drvdata->buf + *ppos; char *bufp = drvdata->buf + *ppos;
if (*ppos + len > drvdata->size) if (*ppos + len > drvdata->len)
len = drvdata->size - *ppos; len = drvdata->len - *ppos;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (bufp == (char *)(drvdata->vaddr + drvdata->size)) if (bufp == (char *)(drvdata->vaddr + drvdata->size))
@ -160,7 +158,7 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
*ppos += len; *ppos += len;
dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
__func__, len, (int)(drvdata->size - *ppos)); __func__, len, (int)(drvdata->len - *ppos));
return len; return len;
} }
@ -220,7 +218,7 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid)
} }
#define coresight_tmc_simple_func(name, offset) \ #define coresight_tmc_simple_func(name, offset) \
coresight_simple_func(struct tmc_drvdata, name, offset) coresight_simple_func(struct tmc_drvdata, NULL, name, offset)
coresight_tmc_simple_func(rsz, TMC_RSZ); coresight_tmc_simple_func(rsz, TMC_RSZ);
coresight_tmc_simple_func(sts, TMC_STS); coresight_tmc_simple_func(sts, TMC_STS);
@ -249,8 +247,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = {
NULL, NULL,
}; };
ssize_t trigger_cntr_show(struct device *dev, static ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->trigger_cntr; unsigned long val = drvdata->trigger_cntr;
@ -304,27 +302,32 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct tmc_drvdata *drvdata; struct tmc_drvdata *drvdata;
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
if (np) { if (np) {
pdata = of_get_coresight_platform_data(dev, np); pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata)) if (IS_ERR(pdata)) {
return PTR_ERR(pdata); ret = PTR_ERR(pdata);
goto out;
}
adev->dev.platform_data = pdata; adev->dev.platform_data = pdata;
} }
ret = -ENOMEM;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) if (!drvdata)
return -ENOMEM; goto out;
drvdata->dev = &adev->dev; drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata); dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */ /* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res); base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) if (IS_ERR(base)) {
return PTR_ERR(base); ret = PTR_ERR(base);
goto out;
}
drvdata->base = base; drvdata->base = base;
@ -347,33 +350,28 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_put(&adev->dev); pm_runtime_put(&adev->dev);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.pdata = pdata;
if (!desc) { desc.dev = dev;
ret = -ENOMEM; desc.groups = coresight_tmc_groups;
goto err_devm_kzalloc;
}
desc->pdata = pdata;
desc->dev = dev;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc->groups = coresight_tmc_groups;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
desc->type = CORESIGHT_DEV_TYPE_SINK; desc.type = CORESIGHT_DEV_TYPE_SINK;
desc->ops = &tmc_etb_cs_ops; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etb_cs_ops;
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
desc->type = CORESIGHT_DEV_TYPE_SINK; desc.type = CORESIGHT_DEV_TYPE_SINK;
desc->ops = &tmc_etr_cs_ops; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etr_cs_ops;
} else { } else {
desc->type = CORESIGHT_DEV_TYPE_LINKSINK; desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
desc->ops = &tmc_etf_cs_ops; desc.ops = &tmc_etf_cs_ops;
} }
drvdata->csdev = coresight_register(desc); drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) { if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev); ret = PTR_ERR(drvdata->csdev);
goto err_devm_kzalloc; goto out;
} }
drvdata->miscdev.name = pdata->name; drvdata->miscdev.name = pdata->name;
@ -381,16 +379,8 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->miscdev.fops = &tmc_fops; drvdata->miscdev.fops = &tmc_fops;
ret = misc_register(&drvdata->miscdev); ret = misc_register(&drvdata->miscdev);
if (ret) if (ret)
goto err_misc_register; coresight_unregister(drvdata->csdev);
out:
return 0;
err_misc_register:
coresight_unregister(drvdata->csdev);
err_devm_kzalloc:
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
dma_free_coherent(dev, drvdata->size,
drvdata->vaddr, drvdata->paddr);
return ret; return ret;
} }

View File

@ -98,7 +98,8 @@ enum tmc_mem_intf_width {
* @buf: area of memory where trace data get sent. * @buf: area of memory where trace data get sent.
* @paddr: DMA start location in RAM. * @paddr: DMA start location in RAM.
* @vaddr: virtual representation of @paddr. * @vaddr: virtual representation of @paddr.
* @size: @buf size. * @size: trace buffer size.
* @len: size of the available trace.
* @mode: how this TMC is being used. * @mode: how this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type. * @config_type: TMC variant, must be of type @tmc_config_type.
* @memwidth: width of the memory interface databus, in bytes. * @memwidth: width of the memory interface databus, in bytes.
@ -115,6 +116,7 @@ struct tmc_drvdata {
dma_addr_t paddr; dma_addr_t paddr;
void __iomem *vaddr; void __iomem *vaddr;
u32 size; u32 size;
u32 len;
local_t mode; local_t mode;
enum tmc_config_type config_type; enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth; enum tmc_mem_intf_width memwidth;

View File

@ -119,7 +119,7 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL; struct coresight_platform_data *pdata = NULL;
struct tpiu_drvdata *drvdata; struct tpiu_drvdata *drvdata;
struct resource *res = &adev->res; struct resource *res = &adev->res;
struct coresight_desc *desc; struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node; struct device_node *np = adev->dev.of_node;
if (np) { if (np) {
@ -154,16 +154,12 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_put(&adev->dev); pm_runtime_put(&adev->dev);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); desc.type = CORESIGHT_DEV_TYPE_SINK;
if (!desc) desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
return -ENOMEM; desc.ops = &tpiu_cs_ops;
desc.pdata = pdata;
desc->type = CORESIGHT_DEV_TYPE_SINK; desc.dev = dev;
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT; drvdata->csdev = coresight_register(&desc);
desc->ops = &tpiu_cs_ops;
desc->pdata = pdata;
desc->dev = dev;
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev)) if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev); return PTR_ERR(drvdata->csdev);

View File

@ -257,7 +257,7 @@ static void coresight_disable_source(struct coresight_device *csdev)
{ {
if (atomic_dec_return(csdev->refcnt) == 0) { if (atomic_dec_return(csdev->refcnt) == 0) {
if (source_ops(csdev)->disable) { if (source_ops(csdev)->disable) {
source_ops(csdev)->disable(csdev); source_ops(csdev)->disable(csdev, NULL);
csdev->enable = false; csdev->enable = false;
} }
} }
@ -429,7 +429,7 @@ struct list_head *coresight_build_path(struct coresight_device *csdev)
path = kzalloc(sizeof(struct list_head), GFP_KERNEL); path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!path) if (!path)
return NULL; return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(path); INIT_LIST_HEAD(path);
@ -725,7 +725,8 @@ static int coresight_orphan_match(struct device *dev, void *data)
/* We have found at least one orphan connection */ /* We have found at least one orphan connection */
if (conn->child_dev == NULL) { if (conn->child_dev == NULL) {
/* Does it match this newly added device? */ /* Does it match this newly added device? */
if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { if (conn->child_name &&
!strcmp(dev_name(&csdev->dev), conn->child_name)) {
conn->child_dev = csdev; conn->child_dev = csdev;
} else { } else {
/* This component still has an orphan */ /* This component still has an orphan */
@ -893,7 +894,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
int nr_refcnts = 1; int nr_refcnts = 1;
atomic_t *refcnts = NULL; atomic_t *refcnts = NULL;
struct coresight_device *csdev; struct coresight_device *csdev;
struct coresight_connection *conns; struct coresight_connection *conns = NULL;
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL); csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
if (!csdev) { if (!csdev) {
@ -921,16 +922,20 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
csdev->nr_inport = desc->pdata->nr_inport; csdev->nr_inport = desc->pdata->nr_inport;
csdev->nr_outport = desc->pdata->nr_outport; csdev->nr_outport = desc->pdata->nr_outport;
conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL);
if (!conns) {
ret = -ENOMEM;
goto err_kzalloc_conns;
}
for (i = 0; i < csdev->nr_outport; i++) { /* Initialise connections if there is at least one outport */
conns[i].outport = desc->pdata->outports[i]; if (csdev->nr_outport) {
conns[i].child_name = desc->pdata->child_names[i]; conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL);
conns[i].child_port = desc->pdata->child_ports[i]; if (!conns) {
ret = -ENOMEM;
goto err_kzalloc_conns;
}
for (i = 0; i < csdev->nr_outport; i++) {
conns[i].outport = desc->pdata->outports[i];
conns[i].child_name = desc->pdata->child_names[i];
conns[i].child_port = desc->pdata->child_ports[i];
}
} }
csdev->conns = conns; csdev->conns = conns;

View File

@ -166,7 +166,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
rdev = of_coresight_get_endpoint_device(rparent); rdev = of_coresight_get_endpoint_device(rparent);
if (!rdev) if (!rdev)
continue; return ERR_PTR(-EPROBE_DEFER);
pdata->child_names[i] = dev_name(rdev); pdata->child_names[i] = dev_name(rdev);
pdata->child_ports[i] = rendpoint.id; pdata->child_ports[i] = rendpoint.id;
@ -184,6 +184,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
break; break;
} }
} }
of_node_put(dn);
return pdata; return pdata;
} }

View File

@ -8,8 +8,6 @@ menu "Pressure sensors"
config BMP280 config BMP280
tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver" tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
depends on (I2C || SPI_MASTER) depends on (I2C || SPI_MASTER)
depends on !(BMP085_I2C=y || BMP085_I2C=m)
depends on !(BMP085_SPI=y || BMP085_SPI=m)
select REGMAP select REGMAP
select BMP280_I2C if (I2C) select BMP280_I2C if (I2C)
select BMP280_SPI if (SPI_MASTER) select BMP280_SPI if (SPI_MASTER)

View File

@ -1013,23 +1013,12 @@ static struct miscdevice uinput_misc = {
.minor = UINPUT_MINOR, .minor = UINPUT_MINOR,
.name = UINPUT_NAME, .name = UINPUT_NAME,
}; };
module_misc_device(uinput_misc);
MODULE_ALIAS_MISCDEV(UINPUT_MINOR); MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
MODULE_ALIAS("devname:" UINPUT_NAME); MODULE_ALIAS("devname:" UINPUT_NAME);
static int __init uinput_init(void)
{
return misc_register(&uinput_misc);
}
static void __exit uinput_exit(void)
{
misc_deregister(&uinput_misc);
}
MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
MODULE_DESCRIPTION("User level driver support for input subsystem"); MODULE_DESCRIPTION("User level driver support for input subsystem");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION("0.3"); MODULE_VERSION("0.3");
module_init(uinput_init);
module_exit(uinput_exit);

View File

@ -1171,27 +1171,10 @@ static struct miscdevice _nvm_misc = {
.nodename = "lightnvm/control", .nodename = "lightnvm/control",
.fops = &_ctl_fops, .fops = &_ctl_fops,
}; };
module_misc_device(_nvm_misc);
MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR); MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
static int __init nvm_mod_init(void)
{
int ret;
ret = misc_register(&_nvm_misc);
if (ret)
pr_err("nvm: misc_register failed for control device");
return ret;
}
static void __exit nvm_mod_exit(void)
{
misc_deregister(&_nvm_misc);
}
MODULE_AUTHOR("Matias Bjorling <m@bjorling.me>"); MODULE_AUTHOR("Matias Bjorling <m@bjorling.me>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.1"); MODULE_VERSION("0.1");
module_init(nvm_mod_init);
module_exit(nvm_mod_exit);

View File

@ -28,4 +28,13 @@ config MCB_PCI
If build as a module, the module is called mcb-pci.ko If build as a module, the module is called mcb-pci.ko
config MCB_LPC
tristate "LPC (non PCI) based MCB carrier"
default n
help
This is a MCB carrier on a LPC or non PCI device.
If build as a module, the module is called mcb-lpc.ko
endif # MCB endif # MCB

View File

@ -5,3 +5,4 @@ mcb-y += mcb-core.o
mcb-y += mcb-parse.o mcb-y += mcb-parse.o
obj-$(CONFIG_MCB_PCI) += mcb-pci.o obj-$(CONFIG_MCB_PCI) += mcb-pci.o
obj-$(CONFIG_MCB_LPC) += mcb-lpc.o

View File

@ -233,6 +233,7 @@ int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
dev->dev.bus = &mcb_bus_type; dev->dev.bus = &mcb_bus_type;
dev->dev.parent = bus->dev.parent; dev->dev.parent = bus->dev.parent;
dev->dev.release = mcb_release_dev; dev->dev.release = mcb_release_dev;
dev->dma_dev = bus->carrier;
device_id = dev->id; device_id = dev->id;
dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d", dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d",
@ -369,7 +370,6 @@ struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
if (!dev) if (!dev)
return NULL; return NULL;
INIT_LIST_HEAD(&dev->bus_list);
dev->bus = bus; dev->bus = bus;
return dev; return dev;
@ -405,20 +405,6 @@ static int __mcb_bus_add_devices(struct device *dev, void *data)
return 0; return 0;
} }
static int __mcb_bus_add_child(struct device *dev, void *data)
{
struct mcb_device *mdev = to_mcb_device(dev);
struct mcb_bus *child;
BUG_ON(!mdev->is_added);
child = mdev->subordinate;
if (child)
mcb_bus_add_devices(child);
return 0;
}
/** /**
* mcb_bus_add_devices() - Add devices in the bus' internal device list * mcb_bus_add_devices() - Add devices in the bus' internal device list
* @bus: The @mcb_bus we add the devices * @bus: The @mcb_bus we add the devices
@ -428,8 +414,6 @@ static int __mcb_bus_add_child(struct device *dev, void *data)
void mcb_bus_add_devices(const struct mcb_bus *bus) void mcb_bus_add_devices(const struct mcb_bus *bus)
{ {
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices); bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_child);
} }
EXPORT_SYMBOL_GPL(mcb_bus_add_devices); EXPORT_SYMBOL_GPL(mcb_bus_add_devices);

View File

@ -112,6 +112,15 @@ struct chameleon_bdd {
u32 size; u32 size;
} __packed; } __packed;
struct chameleon_bar {
u32 addr;
u32 size;
};
#define BAR_CNT(x) ((x) & 0x07)
#define CHAMELEON_BAR_MAX 6
#define BAR_DESC_SIZE(x) ((x) * sizeof(struct chameleon_bar) + sizeof(__le32))
int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
void __iomem *base); void __iomem *base);

158
drivers/mcb/mcb-lpc.c Normal file
View File

@ -0,0 +1,158 @@
/*
* MEN Chameleon Bus.
*
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
* Author: Andreas Werner <andreas.werner@men.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; version 2 of the License.
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/mcb.h>
#include <linux/io.h>
#include "mcb-internal.h"
struct priv {
struct mcb_bus *bus;
struct resource *mem;
void __iomem *base;
};
static int mcb_lpc_probe(struct platform_device *pdev)
{
struct resource *res;
struct priv *priv;
int ret = 0;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!priv->mem) {
dev_err(&pdev->dev, "No Memory resource\n");
return -ENODEV;
}
res = devm_request_mem_region(&pdev->dev, priv->mem->start,
resource_size(priv->mem),
KBUILD_MODNAME);
if (!res) {
dev_err(&pdev->dev, "Failed to request IO memory\n");
return -EBUSY;
}
priv->base = devm_ioremap(&pdev->dev, priv->mem->start,
resource_size(priv->mem));
if (!priv->base) {
dev_err(&pdev->dev, "Cannot ioremap\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, priv);
priv->bus = mcb_alloc_bus(&pdev->dev);
if (IS_ERR(priv->bus))
return PTR_ERR(priv->bus);
ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base);
if (ret < 0) {
mcb_release_bus(priv->bus);
return ret;
}
dev_dbg(&pdev->dev, "Found %d cells\n", ret);
mcb_bus_add_devices(priv->bus);
return 0;
}
static int mcb_lpc_remove(struct platform_device *pdev)
{
struct priv *priv = platform_get_drvdata(pdev);
mcb_release_bus(priv->bus);
return 0;
}
static struct platform_device *mcb_lpc_pdev;
static int mcb_lpc_create_platform_device(const struct dmi_system_id *id)
{
struct resource *res = id->driver_data;
int ret;
mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1);
if (!mcb_lpc_pdev)
return -ENOMEM;
ret = platform_device_add_resources(mcb_lpc_pdev, res, 1);
if (ret)
goto out_put;
ret = platform_device_add(mcb_lpc_pdev);
if (ret)
goto out_put;
return 0;
out_put:
platform_device_put(mcb_lpc_pdev);
return ret;
}
static struct resource sc24_fpga_resource = {
.start = 0xe000e000,
.end = 0xe000e000 + CHAM_HEADER_SIZE,
.flags = IORESOURCE_MEM,
};
static struct platform_driver mcb_lpc_driver = {
.driver = {
.name = "mcb-lpc",
},
.probe = mcb_lpc_probe,
.remove = mcb_lpc_remove,
};
static const struct dmi_system_id mcb_lpc_dmi_table[] = {
{
.ident = "SC24",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"),
},
.driver_data = (void *)&sc24_fpga_resource,
.callback = mcb_lpc_create_platform_device,
},
{}
};
MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);
static int __init mcb_lpc_init(void)
{
if (!dmi_check_system(mcb_lpc_dmi_table))
return -ENODEV;
return platform_driver_register(&mcb_lpc_driver);
}
static void __exit mcb_lpc_exit(void)
{
platform_device_unregister(mcb_lpc_pdev);
platform_driver_unregister(&mcb_lpc_driver);
}
module_init(mcb_lpc_init);
module_exit(mcb_lpc_exit);
MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MCB over LPC support");

View File

@ -26,19 +26,20 @@ static inline uint32_t get_next_dtype(void __iomem *p)
} }
static int chameleon_parse_bdd(struct mcb_bus *bus, static int chameleon_parse_bdd(struct mcb_bus *bus,
phys_addr_t mapbase, struct chameleon_bar *cb,
void __iomem *base) void __iomem *base)
{ {
return 0; return 0;
} }
static int chameleon_parse_gdd(struct mcb_bus *bus, static int chameleon_parse_gdd(struct mcb_bus *bus,
phys_addr_t mapbase, struct chameleon_bar *cb,
void __iomem *base) void __iomem *base, int bar_count)
{ {
struct chameleon_gdd __iomem *gdd = struct chameleon_gdd __iomem *gdd =
(struct chameleon_gdd __iomem *) base; (struct chameleon_gdd __iomem *) base;
struct mcb_device *mdev; struct mcb_device *mdev;
u32 dev_mapbase;
u32 offset; u32 offset;
u32 size; u32 size;
int ret; int ret;
@ -61,13 +62,39 @@ static int chameleon_parse_gdd(struct mcb_bus *bus,
mdev->group = GDD_GRP(reg2); mdev->group = GDD_GRP(reg2);
mdev->inst = GDD_INS(reg2); mdev->inst = GDD_INS(reg2);
/*
* If the BAR is missing, dev_mapbase is zero, or if the
* device is IO mapped we just print a warning and go on with the
* next device, instead of completely stop the gdd parser
*/
if (mdev->bar > bar_count - 1) {
pr_info("No BAR for 16z%03d\n", mdev->id);
ret = 0;
goto err;
}
dev_mapbase = cb[mdev->bar].addr;
if (!dev_mapbase) {
pr_info("BAR not assigned for 16z%03d\n", mdev->id);
ret = 0;
goto err;
}
if (dev_mapbase & 0x01) {
pr_info("IO mapped Device (16z%03d) not yet supported\n",
mdev->id);
ret = 0;
goto err;
}
pr_debug("Found a 16z%03d\n", mdev->id); pr_debug("Found a 16z%03d\n", mdev->id);
mdev->irq.start = GDD_IRQ(reg1); mdev->irq.start = GDD_IRQ(reg1);
mdev->irq.end = GDD_IRQ(reg1); mdev->irq.end = GDD_IRQ(reg1);
mdev->irq.flags = IORESOURCE_IRQ; mdev->irq.flags = IORESOURCE_IRQ;
mdev->mem.start = mapbase + offset; mdev->mem.start = dev_mapbase + offset;
mdev->mem.end = mdev->mem.start + size - 1; mdev->mem.end = mdev->mem.start + size - 1;
mdev->mem.flags = IORESOURCE_MEM; mdev->mem.flags = IORESOURCE_MEM;
@ -85,13 +112,76 @@ err:
return ret; return ret;
} }
static void chameleon_parse_bar(void __iomem *base,
struct chameleon_bar *cb, int bar_count)
{
char __iomem *p = base;
int i;
/* skip reg1 */
p += sizeof(__le32);
for (i = 0; i < bar_count; i++) {
cb[i].addr = readl(p);
cb[i].size = readl(p + 4);
p += sizeof(struct chameleon_bar);
}
}
static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase,
struct chameleon_bar **cb)
{
struct chameleon_bar *c;
int bar_count;
__le32 reg;
u32 dtype;
/*
* For those devices which are not connected
* to the PCI Bus (e.g. LPC) there is a bar
* descriptor located directly after the
* chameleon header. This header is comparable
* to a PCI header.
*/
dtype = get_next_dtype(*base);
if (dtype == CHAMELEON_DTYPE_BAR) {
reg = readl(*base);
bar_count = BAR_CNT(reg);
if (bar_count <= 0 && bar_count > CHAMELEON_BAR_MAX)
return -ENODEV;
c = kcalloc(bar_count, sizeof(struct chameleon_bar),
GFP_KERNEL);
if (!c)
return -ENOMEM;
chameleon_parse_bar(*base, c, bar_count);
*base += BAR_DESC_SIZE(bar_count);
} else {
c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL);
if (!c)
return -ENOMEM;
bar_count = 1;
c->addr = mapbase;
}
*cb = c;
return bar_count;
}
int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
void __iomem *base) void __iomem *base)
{ {
char __iomem *p = base;
struct chameleon_fpga_header *header; struct chameleon_fpga_header *header;
uint32_t dtype; struct chameleon_bar *cb;
char __iomem *p = base;
int num_cells = 0; int num_cells = 0;
uint32_t dtype;
int bar_count;
int ret = 0; int ret = 0;
u32 hsize; u32 hsize;
@ -108,8 +198,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
if (header->magic != CHAMELEONV2_MAGIC) { if (header->magic != CHAMELEONV2_MAGIC) {
pr_err("Unsupported chameleon version 0x%x\n", pr_err("Unsupported chameleon version 0x%x\n",
header->magic); header->magic);
kfree(header); ret = -ENODEV;
return -ENODEV; goto free_header;
} }
p += hsize; p += hsize;
@ -119,16 +209,20 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s", snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s",
header->filename); header->filename);
bar_count = chameleon_get_bar(&p, mapbase, &cb);
if (bar_count < 0)
goto free_header;
for_each_chameleon_cell(dtype, p) { for_each_chameleon_cell(dtype, p) {
switch (dtype) { switch (dtype) {
case CHAMELEON_DTYPE_GENERAL: case CHAMELEON_DTYPE_GENERAL:
ret = chameleon_parse_gdd(bus, mapbase, p); ret = chameleon_parse_gdd(bus, cb, p, bar_count);
if (ret < 0) if (ret < 0)
goto out; goto free_bar;
p += sizeof(struct chameleon_gdd); p += sizeof(struct chameleon_gdd);
break; break;
case CHAMELEON_DTYPE_BRIDGE: case CHAMELEON_DTYPE_BRIDGE:
chameleon_parse_bdd(bus, mapbase, p); chameleon_parse_bdd(bus, cb, p);
p += sizeof(struct chameleon_bdd); p += sizeof(struct chameleon_bdd);
break; break;
case CHAMELEON_DTYPE_END: case CHAMELEON_DTYPE_END:
@ -136,8 +230,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
default: default:
pr_err("Invalid chameleon descriptor type 0x%x\n", pr_err("Invalid chameleon descriptor type 0x%x\n",
dtype); dtype);
kfree(header); ret = -EINVAL;
return -EINVAL; goto free_bar;
} }
num_cells++; num_cells++;
} }
@ -145,11 +239,15 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
if (num_cells == 0) if (num_cells == 0)
num_cells = -EINVAL; num_cells = -EINVAL;
kfree(cb);
kfree(header); kfree(header);
return num_cells; return num_cells;
out: free_bar:
kfree(cb);
free_header:
kfree(header); kfree(header);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(chameleon_parse_cells); EXPORT_SYMBOL_GPL(chameleon_parse_cells);

View File

@ -46,6 +46,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_err(&pdev->dev, "Failed to enable PCI device\n"); dev_err(&pdev->dev, "Failed to enable PCI device\n");
return -ENODEV; return -ENODEV;
} }
pci_set_master(pdev);
priv->mapbase = pci_resource_start(pdev, 0); priv->mapbase = pci_resource_start(pdev, 0);
if (!priv->mapbase) { if (!priv->mapbase) {

View File

@ -16,6 +16,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <memory/jedec_ddr.h> #include <memory/jedec_ddr.h>
#include <linux/export.h> #include <linux/export.h>
#include "of_memory.h"
/** /**
* of_get_min_tck() - extract min timing values for ddr * of_get_min_tck() - extract min timing values for ddr

View File

@ -429,34 +429,6 @@ config ARM_CHARLCD
line and the Linux version on the second line, but that's line and the Linux version on the second line, but that's
still useful. still useful.
config BMP085
tristate
depends on SYSFS
config BMP085_I2C
tristate "BMP085 digital pressure sensor on I2C"
select BMP085
select REGMAP_I2C
depends on I2C && SYSFS
help
Say Y here if you want to support Bosch Sensortec's digital pressure
sensor hooked to an I2C bus.
To compile this driver as a module, choose M here: the
module will be called bmp085-i2c.
config BMP085_SPI
tristate "BMP085 digital pressure sensor on SPI"
select BMP085
select REGMAP_SPI
depends on SPI_MASTER && SYSFS
help
Say Y here if you want to support Bosch Sensortec's digital pressure
sensor hooked to an SPI bus.
To compile this driver as a module, choose M here: the
module will be called bmp085-spi.
config PCH_PHUB config PCH_PHUB
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
select GENERIC_NET_UTILS select GENERIC_NET_UTILS

View File

@ -9,9 +9,6 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
obj-$(CONFIG_INTEL_MID_PTI) += pti.o obj-$(CONFIG_INTEL_MID_PTI) += pti.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o 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_DUMMY_IRQ) += dummy-irq.o
obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_LKDTM) += lkdtm.o

View File

@ -1,83 +0,0 @@
/*
* Copyright (c) 2012 Bosch Sensortec GmbH
* Copyright (c) 2012 Unixphere AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include "bmp085.h"
#define BMP085_I2C_ADDRESS 0x77
static const unsigned short normal_i2c[] = { BMP085_I2C_ADDRESS,
I2C_CLIENT_END };
static int bmp085_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
if (client->addr != BMP085_I2C_ADDRESS)
return -ENODEV;
return bmp085_detect(&client->dev);
}
static int bmp085_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct regmap *regmap = devm_regmap_init_i2c(client,
&bmp085_regmap_config);
if (IS_ERR(regmap)) {
err = PTR_ERR(regmap);
dev_err(&client->dev, "Failed to init regmap: %d\n", err);
return err;
}
return bmp085_probe(&client->dev, regmap, client->irq);
}
static int bmp085_i2c_remove(struct i2c_client *client)
{
return bmp085_remove(&client->dev);
}
static const struct i2c_device_id bmp085_id[] = {
{ BMP085_NAME, 0 },
{ "bmp180", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bmp085_id);
static struct i2c_driver bmp085_i2c_driver = {
.driver = {
.name = BMP085_NAME,
},
.id_table = bmp085_id,
.probe = bmp085_i2c_probe,
.remove = bmp085_i2c_remove,
.detect = bmp085_i2c_detect,
.address_list = normal_i2c
};
module_i2c_driver(bmp085_i2c_driver);
MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
MODULE_DESCRIPTION("BMP085 I2C bus driver");
MODULE_LICENSE("GPL");

View File

@ -1,79 +0,0 @@
/*
* Copyright (c) 2012 Bosch Sensortec GmbH
* Copyright (c) 2012 Unixphere AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include "bmp085.h"
static int bmp085_spi_probe(struct spi_device *client)
{
int err;
struct regmap *regmap;
client->bits_per_word = 8;
err = spi_setup(client);
if (err < 0) {
dev_err(&client->dev, "spi_setup failed!\n");
return err;
}
regmap = devm_regmap_init_spi(client, &bmp085_regmap_config);
if (IS_ERR(regmap)) {
err = PTR_ERR(regmap);
dev_err(&client->dev, "Failed to init regmap: %d\n", err);
return err;
}
return bmp085_probe(&client->dev, regmap, client->irq);
}
static int bmp085_spi_remove(struct spi_device *client)
{
return bmp085_remove(&client->dev);
}
static const struct of_device_id bmp085_of_match[] = {
{ .compatible = "bosch,bmp085", },
{ },
};
MODULE_DEVICE_TABLE(of, bmp085_of_match);
static const struct spi_device_id bmp085_id[] = {
{ "bmp180", 0 },
{ "bmp181", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, bmp085_id);
static struct spi_driver bmp085_spi_driver = {
.driver = {
.name = BMP085_NAME,
.of_match_table = bmp085_of_match
},
.id_table = bmp085_id,
.probe = bmp085_spi_probe,
.remove = bmp085_spi_remove
};
module_spi_driver(bmp085_spi_driver);
MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
MODULE_DESCRIPTION("BMP085 SPI bus driver");
MODULE_LICENSE("GPL");

View File

@ -1,506 +0,0 @@
/* Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
* Copyright (c) 2012 Bosch Sensortec GmbH
* Copyright (c) 2012 Unixphere AB
*
* This driver supports the bmp085 and bmp18x digital barometric pressure
* and temperature sensors from Bosch Sensortec. The datasheets
* are available from their website:
* http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf
* http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP180-DS000-07.pdf
*
* A pressure measurement is issued by reading from pressure0_input.
* The return value ranges from 30000 to 110000 pascal with a resulution
* of 1 pascal (0.01 millibar) which enables measurements from 9000m above
* to 500m below sea level.
*
* The temperature can be read from temp0_input. Values range from
* -400 to 850 representing the ambient temperature in degree celsius
* multiplied by 10.The resolution is 0.1 celsius.
*
* Because ambient pressure is temperature dependent, a temperature
* measurement will be executed automatically even if the user is reading
* from pressure0_input. This happens if the last temperature measurement
* has been executed more then one second ago.
*
* To decrease RMS noise from pressure measurements, the bmp085 can
* autonomously calculate the average of up to eight samples. This is
* set up by writing to the oversampling sysfs file. Accepted values
* are 0, 1, 2 and 3. 2^x when x is the value written to this file
* specifies the number of samples used to calculate the ambient pressure.
* RMS noise is specified with six pascal (without averaging) and decreases
* down to 3 pascal when using an oversampling setting of 3.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include "bmp085.h"
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/gpio.h>
#define BMP085_CHIP_ID 0x55
#define BMP085_CALIBRATION_DATA_START 0xAA
#define BMP085_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */
#define BMP085_CHIP_ID_REG 0xD0
#define BMP085_CTRL_REG 0xF4
#define BMP085_TEMP_MEASUREMENT 0x2E
#define BMP085_PRESSURE_MEASUREMENT 0x34
#define BMP085_CONVERSION_REGISTER_MSB 0xF6
#define BMP085_CONVERSION_REGISTER_LSB 0xF7
#define BMP085_CONVERSION_REGISTER_XLSB 0xF8
#define BMP085_TEMP_CONVERSION_TIME 5
struct bmp085_calibration_data {
s16 AC1, AC2, AC3;
u16 AC4, AC5, AC6;
s16 B1, B2;
s16 MB, MC, MD;
};
struct bmp085_data {
struct device *dev;
struct regmap *regmap;
struct mutex lock;
struct bmp085_calibration_data calibration;
u8 oversampling_setting;
u32 raw_temperature;
u32 raw_pressure;
u32 temp_measurement_period;
unsigned long last_temp_measurement;
u8 chip_id;
s32 b6; /* calculated temperature correction coefficient */
int irq;
struct completion done;
};
static irqreturn_t bmp085_eoc_isr(int irq, void *devid)
{
struct bmp085_data *data = devid;
complete(&data->done);
return IRQ_HANDLED;
}
static s32 bmp085_read_calibration_data(struct bmp085_data *data)
{
u16 tmp[BMP085_CALIBRATION_DATA_LENGTH];
struct bmp085_calibration_data *cali = &(data->calibration);
s32 status = regmap_bulk_read(data->regmap,
BMP085_CALIBRATION_DATA_START, (u8 *)tmp,
(BMP085_CALIBRATION_DATA_LENGTH << 1));
if (status < 0)
return status;
cali->AC1 = be16_to_cpu(tmp[0]);
cali->AC2 = be16_to_cpu(tmp[1]);
cali->AC3 = be16_to_cpu(tmp[2]);
cali->AC4 = be16_to_cpu(tmp[3]);
cali->AC5 = be16_to_cpu(tmp[4]);
cali->AC6 = be16_to_cpu(tmp[5]);
cali->B1 = be16_to_cpu(tmp[6]);
cali->B2 = be16_to_cpu(tmp[7]);
cali->MB = be16_to_cpu(tmp[8]);
cali->MC = be16_to_cpu(tmp[9]);
cali->MD = be16_to_cpu(tmp[10]);
return 0;
}
static s32 bmp085_update_raw_temperature(struct bmp085_data *data)
{
u16 tmp;
s32 status;
mutex_lock(&data->lock);
init_completion(&data->done);
status = regmap_write(data->regmap, BMP085_CTRL_REG,
BMP085_TEMP_MEASUREMENT);
if (status < 0) {
dev_err(data->dev,
"Error while requesting temperature measurement.\n");
goto exit;
}
wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies(
BMP085_TEMP_CONVERSION_TIME));
status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB,
&tmp, sizeof(tmp));
if (status < 0) {
dev_err(data->dev,
"Error while reading temperature measurement result\n");
goto exit;
}
data->raw_temperature = be16_to_cpu(tmp);
data->last_temp_measurement = jiffies;
status = 0; /* everything ok, return 0 */
exit:
mutex_unlock(&data->lock);
return status;
}
static s32 bmp085_update_raw_pressure(struct bmp085_data *data)
{
u32 tmp = 0;
s32 status;
mutex_lock(&data->lock);
init_completion(&data->done);
status = regmap_write(data->regmap, BMP085_CTRL_REG,
BMP085_PRESSURE_MEASUREMENT +
(data->oversampling_setting << 6));
if (status < 0) {
dev_err(data->dev,
"Error while requesting pressure measurement.\n");
goto exit;
}
/* wait for the end of conversion */
wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies(
2+(3 << data->oversampling_setting)));
/* copy data into a u32 (4 bytes), but skip the first byte. */
status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB,
((u8 *)&tmp)+1, 3);
if (status < 0) {
dev_err(data->dev,
"Error while reading pressure measurement results\n");
goto exit;
}
data->raw_pressure = be32_to_cpu((tmp));
data->raw_pressure >>= (8-data->oversampling_setting);
status = 0; /* everything ok, return 0 */
exit:
mutex_unlock(&data->lock);
return status;
}
/*
* This function starts the temperature measurement and returns the value
* in tenth of a degree celsius.
*/
static s32 bmp085_get_temperature(struct bmp085_data *data, int *temperature)
{
struct bmp085_calibration_data *cali = &data->calibration;
long x1, x2;
int status;
status = bmp085_update_raw_temperature(data);
if (status < 0)
goto exit;
x1 = ((data->raw_temperature - cali->AC6) * cali->AC5) >> 15;
x2 = (cali->MC << 11) / (x1 + cali->MD);
data->b6 = x1 + x2 - 4000;
/* if NULL just update b6. Used for pressure only measurements */
if (temperature != NULL)
*temperature = (x1+x2+8) >> 4;
exit:
return status;
}
/*
* This function starts the pressure measurement and returns the value
* in millibar. Since the pressure depends on the ambient temperature,
* a temperature measurement is executed according to the given temperature
* measurement period (default is 1 sec boundary). This period could vary
* and needs to be adjusted according to the sensor environment, i.e. if big
* temperature variations then the temperature needs to be read out often.
*/
static s32 bmp085_get_pressure(struct bmp085_data *data, int *pressure)
{
struct bmp085_calibration_data *cali = &data->calibration;
s32 x1, x2, x3, b3;
u32 b4, b7;
s32 p;
int status;
/* alt least every second force an update of the ambient temperature */
if ((data->last_temp_measurement == 0) ||
time_is_before_jiffies(data->last_temp_measurement + 1*HZ)) {
status = bmp085_get_temperature(data, NULL);
if (status < 0)
return status;
}
status = bmp085_update_raw_pressure(data);
if (status < 0)
return status;
x1 = (data->b6 * data->b6) >> 12;
x1 *= cali->B2;
x1 >>= 11;
x2 = cali->AC2 * data->b6;
x2 >>= 11;
x3 = x1 + x2;
b3 = (((((s32)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2);
b3 >>= 2;
x1 = (cali->AC3 * data->b6) >> 13;
x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16;
x3 = (x1 + x2 + 2) >> 2;
b4 = (cali->AC4 * (u32)(x3 + 32768)) >> 15;
b7 = ((u32)data->raw_pressure - b3) *
(50000 >> data->oversampling_setting);
p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2));
x1 = p >> 8;
x1 *= x1;
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
p += (x1 + x2 + 3791) >> 4;
*pressure = p;
return 0;
}
/*
* This function sets the chip-internal oversampling. Valid values are 0..3.
* The chip will use 2^oversampling samples for internal averaging.
* This influences the measurement time and the accuracy; larger values
* increase both. The datasheet gives an overview on how measurement time,
* accuracy and noise correlate.
*/
static void bmp085_set_oversampling(struct bmp085_data *data,
unsigned char oversampling)
{
if (oversampling > 3)
oversampling = 3;
data->oversampling_setting = oversampling;
}
/*
* Returns the currently selected oversampling. Range: 0..3
*/
static unsigned char bmp085_get_oversampling(struct bmp085_data *data)
{
return data->oversampling_setting;
}
/* sysfs callbacks */
static ssize_t set_oversampling(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct bmp085_data *data = dev_get_drvdata(dev);
unsigned long oversampling;
int err = kstrtoul(buf, 10, &oversampling);
if (err == 0) {
mutex_lock(&data->lock);
bmp085_set_oversampling(data, oversampling);
mutex_unlock(&data->lock);
return count;
}
return err;
}
static ssize_t show_oversampling(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bmp085_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", bmp085_get_oversampling(data));
}
static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO,
show_oversampling, set_oversampling);
static ssize_t show_temperature(struct device *dev,
struct device_attribute *attr, char *buf)
{
int temperature;
int status;
struct bmp085_data *data = dev_get_drvdata(dev);
status = bmp085_get_temperature(data, &temperature);
if (status < 0)
return status;
else
return sprintf(buf, "%d\n", temperature);
}
static DEVICE_ATTR(temp0_input, S_IRUGO, show_temperature, NULL);
static ssize_t show_pressure(struct device *dev,
struct device_attribute *attr, char *buf)
{
int pressure;
int status;
struct bmp085_data *data = dev_get_drvdata(dev);
status = bmp085_get_pressure(data, &pressure);
if (status < 0)
return status;
else
return sprintf(buf, "%d\n", pressure);
}
static DEVICE_ATTR(pressure0_input, S_IRUGO, show_pressure, NULL);
static struct attribute *bmp085_attributes[] = {
&dev_attr_temp0_input.attr,
&dev_attr_pressure0_input.attr,
&dev_attr_oversampling.attr,
NULL
};
static const struct attribute_group bmp085_attr_group = {
.attrs = bmp085_attributes,
};
int bmp085_detect(struct device *dev)
{
struct bmp085_data *data = dev_get_drvdata(dev);
unsigned int id;
int ret;
ret = regmap_read(data->regmap, BMP085_CHIP_ID_REG, &id);
if (ret < 0)
return ret;
if (id != data->chip_id)
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_GPL(bmp085_detect);
static void bmp085_get_of_properties(struct bmp085_data *data)
{
#ifdef CONFIG_OF
struct device_node *np = data->dev->of_node;
u32 prop;
if (!np)
return;
if (!of_property_read_u32(np, "chip-id", &prop))
data->chip_id = prop & 0xff;
if (!of_property_read_u32(np, "temp-measurement-period", &prop))
data->temp_measurement_period = (prop/100)*HZ;
if (!of_property_read_u32(np, "default-oversampling", &prop))
data->oversampling_setting = prop & 0xff;
#endif
}
static int bmp085_init_client(struct bmp085_data *data)
{
int status = bmp085_read_calibration_data(data);
if (status < 0)
return status;
/* default settings */
data->chip_id = BMP085_CHIP_ID;
data->last_temp_measurement = 0;
data->temp_measurement_period = 1*HZ;
data->oversampling_setting = 3;
bmp085_get_of_properties(data);
mutex_init(&data->lock);
return 0;
}
struct regmap_config bmp085_regmap_config = {
.reg_bits = 8,
.val_bits = 8
};
EXPORT_SYMBOL_GPL(bmp085_regmap_config);
int bmp085_probe(struct device *dev, struct regmap *regmap, int irq)
{
struct bmp085_data *data;
int err = 0;
data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit;
}
dev_set_drvdata(dev, data);
data->dev = dev;
data->regmap = regmap;
data->irq = irq;
if (data->irq > 0) {
err = devm_request_irq(dev, data->irq, bmp085_eoc_isr,
IRQF_TRIGGER_RISING, "bmp085",
data);
if (err < 0)
goto exit_free;
}
/* Initialize the BMP085 chip */
err = bmp085_init_client(data);
if (err < 0)
goto exit_free;
err = bmp085_detect(dev);
if (err < 0) {
dev_err(dev, "%s: chip_id failed!\n", BMP085_NAME);
goto exit_free;
}
/* Register sysfs hooks */
err = sysfs_create_group(&dev->kobj, &bmp085_attr_group);
if (err)
goto exit_free;
dev_info(dev, "Successfully initialized %s!\n", BMP085_NAME);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
EXPORT_SYMBOL_GPL(bmp085_probe);
int bmp085_remove(struct device *dev)
{
struct bmp085_data *data = dev_get_drvdata(dev);
sysfs_remove_group(&data->dev->kobj, &bmp085_attr_group);
kfree(data);
return 0;
}
EXPORT_SYMBOL_GPL(bmp085_remove);
MODULE_AUTHOR("Christoph Mair <christoph.mair@gmail.com>");
MODULE_DESCRIPTION("BMP085 driver");
MODULE_LICENSE("GPL");

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2012 Bosch Sensortec GmbH
* Copyright (c) 2012 Unixphere AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _BMP085_H
#define _BMP085_H
#include <linux/regmap.h>
#define BMP085_NAME "bmp085"
extern struct regmap_config bmp085_regmap_config;
int bmp085_probe(struct device *dev, struct regmap *regmap, int irq);
int bmp085_remove(struct device *dev);
int bmp085_detect(struct device *dev);
#endif

View File

@ -121,9 +121,8 @@ static int at25_ee_read(void *priv, unsigned int offset,
* this chip is clocked very slowly * this chip is clocked very slowly
*/ */
status = spi_sync(at25->spi, &m); status = spi_sync(at25->spi, &m);
dev_dbg(&at25->spi->dev, dev_dbg(&at25->spi->dev, "read %zu bytes at %d --> %zd\n",
"read %Zd bytes at %d --> %d\n", count, offset, status);
count, offset, (int) status);
mutex_unlock(&at25->lock); mutex_unlock(&at25->lock);
return status; return status;
@ -167,8 +166,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
*cp = AT25_WREN; *cp = AT25_WREN;
status = spi_write(at25->spi, cp, 1); status = spi_write(at25->spi, cp, 1);
if (status < 0) { if (status < 0) {
dev_dbg(&at25->spi->dev, "WREN --> %d\n", dev_dbg(&at25->spi->dev, "WREN --> %d\n", status);
(int) status);
break; break;
} }
@ -196,9 +194,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
memcpy(cp, buf, segment); memcpy(cp, buf, segment);
status = spi_write(at25->spi, bounce, status = spi_write(at25->spi, bounce,
segment + at25->addrlen + 1); segment + at25->addrlen + 1);
dev_dbg(&at25->spi->dev, dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n",
"write %u bytes at %u --> %d\n", segment, offset, status);
segment, offset, (int) status);
if (status < 0) if (status < 0)
break; break;
@ -225,8 +222,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
if ((sr < 0) || (sr & AT25_SR_nRDY)) { if ((sr < 0) || (sr & AT25_SR_nRDY)) {
dev_err(&at25->spi->dev, dev_err(&at25->spi->dev,
"write %d bytes offset %d, " "write %u bytes offset %u, timeout after %u msecs\n",
"timeout after %u msecs\n",
segment, offset, segment, offset,
jiffies_to_msecs(jiffies - jiffies_to_msecs(jiffies -
(timeout - EE_TIMEOUT))); (timeout - EE_TIMEOUT)));
@ -368,9 +364,7 @@ static int at25_probe(struct spi_device *spi)
return PTR_ERR(at25->nvmem); return PTR_ERR(at25->nvmem);
dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n", dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n",
(chip.byte_len < 1024) (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024),
? chip.byte_len
: (chip.byte_len / 1024),
(chip.byte_len < 1024) ? "Byte" : "KByte", (chip.byte_len < 1024) ? "Byte" : "KByte",
at25->chip.name, at25->chip.name,
(chip.flags & EE_READONLY) ? " (readonly)" : "", (chip.flags & EE_READONLY) ? " (readonly)" : "",

View File

@ -1350,6 +1350,19 @@ static struct pci_driver genwqe_driver = {
.err_handler = &genwqe_err_handler, .err_handler = &genwqe_err_handler,
}; };
/**
* genwqe_devnode() - Set default access mode for genwqe devices.
*
* Default mode should be rw for everybody. Do not change default
* device name.
*/
static char *genwqe_devnode(struct device *dev, umode_t *mode)
{
if (mode)
*mode = 0666;
return NULL;
}
/** /**
* genwqe_init_module() - Driver registration and initialization * genwqe_init_module() - Driver registration and initialization
*/ */
@ -1363,6 +1376,8 @@ static int __init genwqe_init_module(void)
return -ENOMEM; return -ENOMEM;
} }
class_genwqe->devnode = genwqe_devnode;
debugfs_genwqe = debugfs_create_dir(GENWQE_DEVNAME, NULL); debugfs_genwqe = debugfs_create_dir(GENWQE_DEVNAME, NULL);
if (!debugfs_genwqe) { if (!debugfs_genwqe) {
rc = -ENOMEM; rc = -ENOMEM;

View File

@ -1048,8 +1048,6 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
"[%s] **err: could not allocate DDCB **\n", __func__); "[%s] **err: could not allocate DDCB **\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
memset(queue->ddcb_vaddr, 0, queue_size);
queue->ddcb_req = kzalloc(sizeof(struct ddcb_requ *) * queue->ddcb_req = kzalloc(sizeof(struct ddcb_requ *) *
queue->ddcb_max, GFP_KERNEL); queue->ddcb_max, GFP_KERNEL);
if (!queue->ddcb_req) { if (!queue->ddcb_req) {

View File

@ -220,8 +220,8 @@ void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
if (get_order(size) > MAX_ORDER) if (get_order(size) > MAX_ORDER)
return NULL; return NULL;
return dma_alloc_coherent(&cd->pci_dev->dev, size, dma_handle, return dma_zalloc_coherent(&cd->pci_dev->dev, size, dma_handle,
GFP_KERNEL); GFP_KERNEL);
} }
void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size, void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,

View File

@ -688,7 +688,8 @@ static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
{ {
int error = -ENOMEM; int bar;
unsigned long off;
/* map the memory mapped i/o registers */ /* map the memory mapped i/o registers */
hw->mmio_vaddr = pci_iomap(pdev, 1, 0); hw->mmio_vaddr = pci_iomap(pdev, 1, 0);
@ -698,7 +699,15 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
} }
/* map the adapter shared memory region */ /* map the adapter shared memory region */
hw->ram_vaddr = pci_iomap(pdev, 2, max_ccb * ILOHW_CCB_SZ); if (pdev->subsystem_device == 0x00E4) {
bar = 5;
/* Last 8k is reserved for CCBs */
off = pci_resource_len(pdev, bar) - 0x2000;
} else {
bar = 2;
off = 0;
}
hw->ram_vaddr = pci_iomap_range(pdev, bar, off, max_ccb * ILOHW_CCB_SZ);
if (hw->ram_vaddr == NULL) { if (hw->ram_vaddr == NULL) {
dev_err(&pdev->dev, "Error mapping shared mem\n"); dev_err(&pdev->dev, "Error mapping shared mem\n");
goto mmio_free; goto mmio_free;
@ -717,7 +726,7 @@ ram_free:
mmio_free: mmio_free:
pci_iounmap(pdev, hw->mmio_vaddr); pci_iounmap(pdev, hw->mmio_vaddr);
out: out:
return error; return -ENOMEM;
} }
static void ilo_remove(struct pci_dev *pdev) static void ilo_remove(struct pci_dev *pdev)
@ -899,7 +908,7 @@ static void __exit ilo_exit(void)
class_destroy(ilo_class); class_destroy(ilo_class);
} }
MODULE_VERSION("1.4.1"); MODULE_VERSION("1.5.0");
MODULE_ALIAS(ILO_NAME); MODULE_ALIAS(ILO_NAME);
MODULE_DESCRIPTION(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME);
MODULE_AUTHOR("David Altobelli <david.altobelli@hpe.com>"); MODULE_AUTHOR("David Altobelli <david.altobelli@hpe.com>");

View File

@ -47,7 +47,6 @@ const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
void mei_amthif_reset_params(struct mei_device *dev) void mei_amthif_reset_params(struct mei_device *dev)
{ {
/* reset iamthif parameters. */ /* reset iamthif parameters. */
dev->iamthif_current_cb = NULL;
dev->iamthif_canceled = false; dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->iamthif_stall_timer = 0; dev->iamthif_stall_timer = 0;
@ -67,8 +66,12 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
struct mei_cl *cl = &dev->iamthif_cl; struct mei_cl *cl = &dev->iamthif_cl;
int ret; int ret;
if (mei_cl_is_connected(cl)) mutex_lock(&dev->device_lock);
return 0;
if (mei_cl_is_connected(cl)) {
ret = 0;
goto out;
}
dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_state = MEI_IAMTHIF_IDLE;
@ -77,179 +80,37 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
ret = mei_cl_link(cl); ret = mei_cl_link(cl);
if (ret < 0) { if (ret < 0) {
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
return ret; goto out;
} }
ret = mei_cl_connect(cl, me_cl, NULL); ret = mei_cl_connect(cl, me_cl, NULL);
return ret;
}
/**
* mei_amthif_read - read data from AMTHIF client
*
* @dev: the device structure
* @file: pointer to file object
* @ubuf: pointer to user data in user space
* @length: data length to read
* @offset: data read offset
*
* Locking: called under "dev->device_lock" lock
*
* Return:
* returned data length on success,
* zero if no data to read,
* negative on failure.
*/
int mei_amthif_read(struct mei_device *dev, struct file *file,
char __user *ubuf, size_t length, loff_t *offset)
{
struct mei_cl *cl = file->private_data;
struct mei_cl_cb *cb;
int rets;
int wait_ret;
dev_dbg(dev->dev, "checking amthif data\n");
cb = mei_cl_read_cb(cl, file);
/* Check for if we can block or not*/
if (cb == NULL && file->f_flags & O_NONBLOCK)
return -EAGAIN;
dev_dbg(dev->dev, "waiting for amthif data\n");
while (cb == NULL) {
/* unlock the Mutex */
mutex_unlock(&dev->device_lock);
wait_ret = wait_event_interruptible(cl->rx_wait,
!list_empty(&cl->rd_completed) ||
!mei_cl_is_connected(cl));
/* Locking again the Mutex */
mutex_lock(&dev->device_lock);
if (wait_ret)
return -ERESTARTSYS;
if (!mei_cl_is_connected(cl)) {
rets = -EBUSY;
goto out;
}
cb = mei_cl_read_cb(cl, file);
}
if (cb->status) {
rets = cb->status;
dev_dbg(dev->dev, "read operation failed %d\n", rets);
goto free;
}
dev_dbg(dev->dev, "Got amthif data\n");
/* if the whole message will fit remove it from the list */
if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
list_del_init(&cb->list);
else if (cb->buf_idx <= *offset) {
/* end of the message has been reached */
list_del_init(&cb->list);
rets = 0;
goto free;
}
/* else means that not full buffer will be read and do not
* remove message from deletion list
*/
dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n",
cb->buf.size, cb->buf_idx);
/* length is being truncated to PAGE_SIZE, however,
* the buf_idx may point beyond */
length = min_t(size_t, length, (cb->buf_idx - *offset));
if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
dev_dbg(dev->dev, "failed to copy data to userland\n");
rets = -EFAULT;
} else {
rets = length;
if ((*offset + length) < cb->buf_idx) {
*offset += length;
goto out;
}
}
free:
dev_dbg(dev->dev, "free amthif cb memory.\n");
*offset = 0;
mei_io_cb_free(cb);
out: out:
return rets; mutex_unlock(&dev->device_lock);
return ret;
} }
/** /**
* mei_amthif_read_start - queue message for sending read credential * mei_amthif_read_start - queue message for sending read credential
* *
* @cl: host client * @cl: host client
* @file: file pointer of message recipient * @fp: file pointer of message recipient
* *
* Return: 0 on success, <0 on failure. * Return: 0 on success, <0 on failure.
*/ */
static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file) static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp)
{ {
struct mei_device *dev = cl->dev; struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
int rets;
cb = mei_io_cb_init(cl, MEI_FOP_READ, file); cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp);
if (!cb) { if (!cb)
rets = -ENOMEM; return -ENOMEM;
goto err;
}
rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl)); cl->rx_flow_ctrl_creds++;
if (rets)
goto err;
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
dev->iamthif_state = MEI_IAMTHIF_READING; dev->iamthif_state = MEI_IAMTHIF_READING;
dev->iamthif_fp = cb->fp; cl->fp = cb->fp;
dev->iamthif_current_cb = cb;
return 0;
err:
mei_io_cb_free(cb);
return rets;
}
/**
* mei_amthif_send_cmd - send amthif command to the ME
*
* @cl: the host client
* @cb: mei call back struct
*
* Return: 0 on success, <0 on failure.
*/
static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev;
int ret;
if (!cl->dev || !cb)
return -ENODEV;
dev = cl->dev;
dev->iamthif_state = MEI_IAMTHIF_WRITING;
dev->iamthif_current_cb = cb;
dev->iamthif_fp = cb->fp;
dev->iamthif_canceled = false;
ret = mei_cl_write(cl, cb, false);
if (ret < 0)
return ret;
if (cb->completed)
cb->status = mei_amthif_read_start(cl, cb->fp);
return 0; return 0;
} }
@ -265,20 +126,32 @@ int mei_amthif_run_next_cmd(struct mei_device *dev)
{ {
struct mei_cl *cl = &dev->iamthif_cl; struct mei_cl *cl = &dev->iamthif_cl;
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
int ret;
dev->iamthif_canceled = false; dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->iamthif_fp = NULL;
dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
typeof(*cb), list); typeof(*cb), list);
if (!cb) if (!cb) {
dev->iamthif_state = MEI_IAMTHIF_IDLE;
cl->fp = NULL;
return 0; return 0;
}
list_del_init(&cb->list); list_del_init(&cb->list);
return mei_amthif_send_cmd(cl, cb); dev->iamthif_state = MEI_IAMTHIF_WRITING;
cl->fp = cb->fp;
ret = mei_cl_write(cl, cb, false);
if (ret < 0)
return ret;
if (cb->completed)
cb->status = mei_amthif_read_start(cl, cb->fp);
return 0;
} }
/** /**
@ -299,8 +172,7 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
/* /*
* The previous request is still in processing, queue this one. * The previous request is still in processing, queue this one.
*/ */
if (dev->iamthif_state > MEI_IAMTHIF_IDLE && if (dev->iamthif_state != MEI_IAMTHIF_IDLE)
dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE)
return 0; return 0;
return mei_amthif_run_next_cmd(dev); return mei_amthif_run_next_cmd(dev);
@ -309,7 +181,6 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
/** /**
* mei_amthif_poll - the amthif poll function * mei_amthif_poll - the amthif poll function
* *
* @dev: the device structure
* @file: pointer to file structure * @file: pointer to file structure
* @wait: pointer to poll_table structure * @wait: pointer to poll_table structure
* *
@ -317,26 +188,19 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
* *
* Locking: called under "dev->device_lock" lock * Locking: called under "dev->device_lock" lock
*/ */
unsigned int mei_amthif_poll(struct file *file, poll_table *wait)
unsigned int mei_amthif_poll(struct mei_device *dev,
struct file *file, poll_table *wait)
{ {
struct mei_cl *cl = file->private_data;
struct mei_cl_cb *cb = mei_cl_read_cb(cl, file);
unsigned int mask = 0; unsigned int mask = 0;
poll_wait(file, &dev->iamthif_cl.rx_wait, wait); poll_wait(file, &cl->rx_wait, wait);
if (cb)
if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
dev->iamthif_fp == file) {
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
mei_amthif_run_next_cmd(dev);
}
return mask; return mask;
} }
/** /**
* mei_amthif_irq_write - write iamthif command in irq thread context. * mei_amthif_irq_write - write iamthif command in irq thread context.
* *
@ -393,7 +257,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl,
return 0; return 0;
dev_dbg(dev->dev, "completed amthif read.\n "); dev_dbg(dev->dev, "completed amthif read.\n ");
dev->iamthif_current_cb = NULL;
dev->iamthif_stall_timer = 0; dev->iamthif_stall_timer = 0;
return 0; return 0;
@ -409,115 +272,63 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
{ {
struct mei_device *dev = cl->dev; struct mei_device *dev = cl->dev;
if (cb->fop_type == MEI_FOP_WRITE) { dev_dbg(dev->dev, "completing amthif call back.\n");
switch (cb->fop_type) {
case MEI_FOP_WRITE:
if (!cb->status) { if (!cb->status) {
dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
mei_schedule_stall_timer(dev);
mei_io_cb_free(cb); mei_io_cb_free(cb);
return; return;
} }
/* dev->iamthif_state = MEI_IAMTHIF_IDLE;
* in case of error enqueue the write cb to complete read list cl->fp = NULL;
* so it can be propagated to the reader if (!dev->iamthif_canceled) {
*/ /*
list_add_tail(&cb->list, &cl->rd_completed); * in case of error enqueue the write cb to complete
wake_up_interruptible(&cl->rx_wait); * read list so it can be propagated to the reader
return; */
} list_add_tail(&cb->list, &cl->rd_completed);
wake_up_interruptible(&cl->rx_wait);
} else {
mei_io_cb_free(cb);
}
break;
case MEI_FOP_READ:
if (!dev->iamthif_canceled) {
list_add_tail(&cb->list, &cl->rd_completed);
dev_dbg(dev->dev, "amthif read completed\n");
wake_up_interruptible(&cl->rx_wait);
} else {
mei_io_cb_free(cb);
}
if (!dev->iamthif_canceled) {
dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
dev->iamthif_stall_timer = 0; dev->iamthif_stall_timer = 0;
list_add_tail(&cb->list, &cl->rd_completed);
dev_dbg(dev->dev, "amthif read completed\n");
} else {
mei_amthif_run_next_cmd(dev); mei_amthif_run_next_cmd(dev);
break;
default:
WARN_ON(1);
} }
dev_dbg(dev->dev, "completing amthif call back.\n");
wake_up_interruptible(&cl->rx_wait);
} }
/** /**
* mei_clear_list - removes all callbacks associated with file * mei_clear_list - removes all callbacks associated with file
* from mei_cb_list * from mei_cb_list
* *
* @dev: device structure.
* @file: file structure * @file: file structure
* @mei_cb_list: callbacks list * @mei_cb_list: callbacks list
* *
* mei_clear_list is called to clear resources associated with file * mei_clear_list is called to clear resources associated with file
* when application calls close function or Ctrl-C was pressed * when application calls close function or Ctrl-C was pressed
*
* Return: true if callback removed from the list, false otherwise
*/ */
static bool mei_clear_list(struct mei_device *dev, static void mei_clear_list(const struct file *file,
const struct file *file, struct list_head *mei_cb_list) struct list_head *mei_cb_list)
{ {
struct mei_cl *cl = &dev->iamthif_cl;
struct mei_cl_cb *cb, *next; struct mei_cl_cb *cb, *next;
bool removed = false;
/* list all list member */ list_for_each_entry_safe(cb, next, mei_cb_list, list)
list_for_each_entry_safe(cb, next, mei_cb_list, list) { if (file == cb->fp)
/* check if list member associated with a file */
if (file == cb->fp) {
/* check if cb equal to current iamthif cb */
if (dev->iamthif_current_cb == cb) {
dev->iamthif_current_cb = NULL;
/* send flow control to iamthif client */
mei_hbm_cl_flow_control_req(dev, cl);
}
/* free all allocated buffers */
mei_io_cb_free(cb); mei_io_cb_free(cb);
removed = true;
}
}
return removed;
}
/**
* mei_clear_lists - removes all callbacks associated with file
*
* @dev: device structure
* @file: file structure
*
* mei_clear_lists is called to clear resources associated with file
* when application calls close function or Ctrl-C was pressed
*
* Return: true if callback removed from the list, false otherwise
*/
static bool mei_clear_lists(struct mei_device *dev, const struct file *file)
{
bool removed = false;
struct mei_cl *cl = &dev->iamthif_cl;
/* remove callbacks associated with a file */
mei_clear_list(dev, file, &dev->amthif_cmd_list.list);
if (mei_clear_list(dev, file, &cl->rd_completed))
removed = true;
mei_clear_list(dev, file, &dev->ctrl_rd_list.list);
if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list))
removed = true;
if (mei_clear_list(dev, file, &dev->write_waiting_list.list))
removed = true;
if (mei_clear_list(dev, file, &dev->write_list.list))
removed = true;
/* check if iamthif_current_cb not NULL */
if (dev->iamthif_current_cb && !removed) {
/* check file and iamthif current cb association */
if (dev->iamthif_current_cb->fp == file) {
/* remove cb */
mei_io_cb_free(dev->iamthif_current_cb);
dev->iamthif_current_cb = NULL;
removed = true;
}
}
return removed;
} }
/** /**
@ -530,23 +341,21 @@ static bool mei_clear_lists(struct mei_device *dev, const struct file *file)
*/ */
int mei_amthif_release(struct mei_device *dev, struct file *file) int mei_amthif_release(struct mei_device *dev, struct file *file)
{ {
struct mei_cl *cl = file->private_data;
if (dev->iamthif_open_count > 0) if (dev->iamthif_open_count > 0)
dev->iamthif_open_count--; dev->iamthif_open_count--;
if (dev->iamthif_fp == file && if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
dev->iamthif_state); dev->iamthif_state);
dev->iamthif_canceled = true; dev->iamthif_canceled = true;
if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
dev_dbg(dev->dev, "run next amthif iamthif cb\n");
mei_amthif_run_next_cmd(dev);
}
} }
if (mei_clear_lists(dev, file)) mei_clear_list(file, &dev->amthif_cmd_list.list);
dev->iamthif_state = MEI_IAMTHIF_IDLE; mei_clear_list(file, &cl->rd_completed);
mei_clear_list(file, &dev->ctrl_rd_list.list);
return 0; return 0;
} }

View File

@ -126,7 +126,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
goto out; goto out;
/* wait on event only if there is no other waiter */ /* wait on event only if there is no other waiter */
if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { /* synchronized under device mutex */
if (!waitqueue_active(&cl->rx_wait)) {
mutex_unlock(&bus->device_lock); mutex_unlock(&bus->device_lock);
@ -142,7 +143,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
mutex_lock(&bus->device_lock); mutex_lock(&bus->device_lock);
if (!mei_cl_is_connected(cl)) { if (!mei_cl_is_connected(cl)) {
rets = -EBUSY; rets = -ENODEV;
goto out; goto out;
} }
} }
@ -234,7 +235,7 @@ static void mei_cl_bus_event_work(struct work_struct *work)
/* Prepare for the next read */ /* Prepare for the next read */
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
mutex_lock(&bus->device_lock); mutex_lock(&bus->device_lock);
mei_cl_read_start(cldev->cl, 0, NULL); mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
mutex_unlock(&bus->device_lock); mutex_unlock(&bus->device_lock);
} }
} }
@ -324,7 +325,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
mutex_lock(&bus->device_lock); mutex_lock(&bus->device_lock);
ret = mei_cl_read_start(cldev->cl, 0, NULL); ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
mutex_unlock(&bus->device_lock); mutex_unlock(&bus->device_lock);
if (ret && ret != -EBUSY) if (ret && ret != -EBUSY)
return ret; return ret;
@ -983,12 +984,10 @@ void mei_cl_bus_rescan_work(struct work_struct *work)
container_of(work, struct mei_device, bus_rescan_work); container_of(work, struct mei_device, bus_rescan_work);
struct mei_me_client *me_cl; struct mei_me_client *me_cl;
mutex_lock(&bus->device_lock);
me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid);
if (me_cl) if (me_cl)
mei_amthif_host_init(bus, me_cl); mei_amthif_host_init(bus, me_cl);
mei_me_cl_put(me_cl); mei_me_cl_put(me_cl);
mutex_unlock(&bus->device_lock);
mei_cl_bus_rescan(bus); mei_cl_bus_rescan(bus);
} }

View File

@ -358,8 +358,9 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
* *
* Return: mei_cl_cb pointer or NULL; * Return: mei_cl_cb pointer or NULL;
*/ */
struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
const struct file *fp) enum mei_cb_file_ops type,
const struct file *fp)
{ {
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
@ -419,31 +420,6 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
__mei_io_list_flush(list, cl, true); __mei_io_list_flush(list, cl, true);
} }
/**
* mei_io_cb_alloc_buf - allocate callback buffer
*
* @cb: io callback structure
* @length: size of the buffer
*
* Return: 0 on success
* -EINVAL if cb is NULL
* -ENOMEM if allocation failed
*/
int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
{
if (!cb)
return -EINVAL;
if (length == 0)
return 0;
cb->buf.data = kmalloc(length, GFP_KERNEL);
if (!cb->buf.data)
return -ENOMEM;
cb->buf.size = length;
return 0;
}
/** /**
* mei_cl_alloc_cb - a convenient wrapper for allocating read cb * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
* *
@ -455,23 +431,58 @@ int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
* Return: cb on success and NULL on failure * Return: cb on success and NULL on failure
*/ */
struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
enum mei_cb_file_ops type, enum mei_cb_file_ops fop_type,
const struct file *fp) const struct file *fp)
{ {
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
cb = mei_io_cb_init(cl, type, fp); cb = mei_io_cb_init(cl, fop_type, fp);
if (!cb) if (!cb)
return NULL; return NULL;
if (mei_io_cb_alloc_buf(cb, length)) { if (length == 0)
return cb;
cb->buf.data = kmalloc(length, GFP_KERNEL);
if (!cb->buf.data) {
mei_io_cb_free(cb); mei_io_cb_free(cb);
return NULL; return NULL;
} }
cb->buf.size = length;
return cb; return cb;
} }
/**
* mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating
* and enqueuing of the control commands cb
*
* @cl: host client
* @length: size of the buffer
* @type: operation type
* @fp: associated file pointer (might be NULL)
*
* Return: cb on success and NULL on failure
* Locking: called under "dev->device_lock" lock
*/
struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
enum mei_cb_file_ops fop_type,
const struct file *fp)
{
struct mei_cl_cb *cb;
/* for RX always allocate at least client's mtu */
if (length)
length = max_t(size_t, length, mei_cl_mtu(cl));
cb = mei_cl_alloc_cb(cl, length, fop_type, fp);
if (!cb)
return NULL;
list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list);
return cb;
}
/** /**
* mei_cl_read_cb - find this cl's callback in the read list * mei_cl_read_cb - find this cl's callback in the read list
* for a specific file * for a specific file
@ -754,7 +765,8 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl);
mei_cl_wake_all(cl); mei_cl_wake_all(cl);
cl->mei_flow_ctrl_creds = 0; cl->rx_flow_ctrl_creds = 0;
cl->tx_flow_ctrl_creds = 0;
cl->timer_count = 0; cl->timer_count = 0;
if (!cl->me_cl) if (!cl->me_cl)
@ -764,7 +776,7 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
cl->me_cl->connect_count--; cl->me_cl->connect_count--;
if (cl->me_cl->connect_count == 0) if (cl->me_cl->connect_count == 0)
cl->me_cl->mei_flow_ctrl_creds = 0; cl->me_cl->tx_flow_ctrl_creds = 0;
mei_me_cl_put(cl->me_cl); mei_me_cl_put(cl->me_cl);
cl->me_cl = NULL; cl->me_cl = NULL;
@ -814,6 +826,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
list_move_tail(&cb->list, &dev->ctrl_rd_list.list); list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
cl->timer_count = MEI_CONNECT_TIMEOUT; cl->timer_count = MEI_CONNECT_TIMEOUT;
mei_schedule_stall_timer(dev);
return 0; return 0;
} }
@ -867,13 +880,11 @@ static int __mei_cl_disconnect(struct mei_cl *cl)
cl->state = MEI_FILE_DISCONNECTING; cl->state = MEI_FILE_DISCONNECTING;
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL);
rets = cb ? 0 : -ENOMEM; if (!cb) {
if (rets) rets = -ENOMEM;
goto out; goto out;
}
cl_dbg(dev, cl, "add disconnect cb to control write list\n");
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
if (mei_hbuf_acquire(dev)) { if (mei_hbuf_acquire(dev)) {
rets = mei_cl_send_disconnect(cl, cb); rets = mei_cl_send_disconnect(cl, cb);
@ -1001,6 +1012,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
list_move_tail(&cb->list, &dev->ctrl_rd_list.list); list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
cl->timer_count = MEI_CONNECT_TIMEOUT; cl->timer_count = MEI_CONNECT_TIMEOUT;
mei_schedule_stall_timer(dev);
return 0; return 0;
} }
@ -1042,14 +1054,14 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
* *
* @cl: host client * @cl: host client
* @me_cl: me client * @me_cl: me client
* @file: pointer to file structure * @fp: pointer to file structure
* *
* Locking: called under "dev->device_lock" lock * Locking: called under "dev->device_lock" lock
* *
* Return: 0 on success, <0 on failure. * Return: 0 on success, <0 on failure.
*/ */
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
const struct file *file) const struct file *fp)
{ {
struct mei_device *dev; struct mei_device *dev;
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
@ -1076,12 +1088,11 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
goto nortpm; goto nortpm;
} }
cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file); cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp);
rets = cb ? 0 : -ENOMEM; if (!cb) {
if (rets) rets = -ENOMEM;
goto out; goto out;
}
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
/* run hbuf acquire last so we don't have to undo */ /* run hbuf acquire last so we don't have to undo */
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
@ -1159,50 +1170,42 @@ err:
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/** /**
* mei_cl_flow_ctrl_creds - checks flow_control credits for cl. * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl.
* *
* @cl: host client * @cl: host client
* @fp: the file pointer associated with the pointer
* *
* Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise.
*/ */
static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp) static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl)
{ {
int rets;
if (WARN_ON(!cl || !cl->me_cl)) if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL; return -EINVAL;
if (cl->mei_flow_ctrl_creds > 0) if (cl->tx_flow_ctrl_creds > 0)
return 1; return 1;
if (mei_cl_is_fixed_address(cl)) { if (mei_cl_is_fixed_address(cl))
rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp);
if (rets && rets != -EBUSY)
return rets;
return 1; return 1;
}
if (mei_cl_is_single_recv_buf(cl)) { if (mei_cl_is_single_recv_buf(cl)) {
if (cl->me_cl->mei_flow_ctrl_creds > 0) if (cl->me_cl->tx_flow_ctrl_creds > 0)
return 1; return 1;
} }
return 0; return 0;
} }
/** /**
* mei_cl_flow_ctrl_reduce - reduces flow_control. * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits
* for a client
* *
* @cl: private data of the file object * @cl: host client
* *
* Return: * Return:
* 0 on success * 0 on success
* -EINVAL when ctrl credits are <= 0 * -EINVAL when ctrl credits are <= 0
*/ */
static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
{ {
if (WARN_ON(!cl || !cl->me_cl)) if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL; return -EINVAL;
@ -1211,13 +1214,13 @@ static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
return 0; return 0;
if (mei_cl_is_single_recv_buf(cl)) { if (mei_cl_is_single_recv_buf(cl)) {
if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0)) if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0))
return -EINVAL; return -EINVAL;
cl->me_cl->mei_flow_ctrl_creds--; cl->me_cl->tx_flow_ctrl_creds--;
} else { } else {
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) if (WARN_ON(cl->tx_flow_ctrl_creds <= 0))
return -EINVAL; return -EINVAL;
cl->mei_flow_ctrl_creds--; cl->tx_flow_ctrl_creds--;
} }
return 0; return 0;
} }
@ -1292,7 +1295,7 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
* mei_cl_notify_request - send notification stop/start request * mei_cl_notify_request - send notification stop/start request
* *
* @cl: host client * @cl: host client
* @file: associate request with file * @fp: associate request with file
* @request: 1 for start or 0 for stop * @request: 1 for start or 0 for stop
* *
* Locking: called under "dev->device_lock" lock * Locking: called under "dev->device_lock" lock
@ -1300,7 +1303,7 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
* Return: 0 on such and error otherwise. * Return: 0 on such and error otherwise.
*/ */
int mei_cl_notify_request(struct mei_cl *cl, int mei_cl_notify_request(struct mei_cl *cl,
const struct file *file, u8 request) const struct file *fp, u8 request)
{ {
struct mei_device *dev; struct mei_device *dev;
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
@ -1325,7 +1328,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
} }
fop_type = mei_cl_notify_req2fop(request); fop_type = mei_cl_notify_req2fop(request);
cb = mei_io_cb_init(cl, fop_type, file); cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp);
if (!cb) { if (!cb) {
rets = -ENOMEM; rets = -ENOMEM;
goto out; goto out;
@ -1336,9 +1339,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
rets = -ENODEV; rets = -ENODEV;
goto out; goto out;
} }
list_add_tail(&cb->list, &dev->ctrl_rd_list.list); list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
} else {
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
} }
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
@ -1435,25 +1436,6 @@ out:
return 0; return 0;
} }
/**
* mei_cl_is_read_fc_cb - check if read cb is waiting for flow control
* for given host client
*
* @cl: host client
*
* Return: true, if found at least one cb.
*/
static bool mei_cl_is_read_fc_cb(struct mei_cl *cl)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
list_for_each_entry(cb, &dev->ctrl_wr_list.list, list)
if (cb->fop_type == MEI_FOP_READ && cb->cl == cl)
return true;
return false;
}
/** /**
* mei_cl_read_start - the start read client message function. * mei_cl_read_start - the start read client message function.
* *
@ -1477,26 +1459,22 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
if (!mei_cl_is_connected(cl)) if (!mei_cl_is_connected(cl))
return -ENODEV; return -ENODEV;
/* HW currently supports only one pending read */
if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl))
return -EBUSY;
if (!mei_me_cl_is_active(cl->me_cl)) { if (!mei_me_cl_is_active(cl->me_cl)) {
cl_err(dev, cl, "no such me client\n"); cl_err(dev, cl, "no such me client\n");
return -ENOTTY; return -ENOTTY;
} }
/* always allocate at least client max message */ if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl)
length = max_t(size_t, length, mei_cl_mtu(cl)); return 0;
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
/* HW currently supports only one pending read */
if (cl->rx_flow_ctrl_creds)
return -EBUSY;
cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp);
if (!cb) if (!cb)
return -ENOMEM; return -ENOMEM;
if (mei_cl_is_fixed_address(cl)) {
list_add_tail(&cb->list, &cl->rd_pending);
return 0;
}
rets = pm_runtime_get(dev->dev); rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) { if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev); pm_runtime_put_noidle(dev->dev);
@ -1504,16 +1482,15 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
goto nortpm; goto nortpm;
} }
rets = 0;
if (mei_hbuf_acquire(dev)) { if (mei_hbuf_acquire(dev)) {
rets = mei_hbm_cl_flow_control_req(dev, cl); rets = mei_hbm_cl_flow_control_req(dev, cl);
if (rets < 0) if (rets < 0)
goto out; goto out;
list_add_tail(&cb->list, &cl->rd_pending); list_move_tail(&cb->list, &cl->rd_pending);
} else {
rets = 0;
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
} }
cl->rx_flow_ctrl_creds++;
out: out:
cl_dbg(dev, cl, "rpm: autosuspend\n"); cl_dbg(dev, cl, "rpm: autosuspend\n");
@ -1557,7 +1534,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
first_chunk = cb->buf_idx == 0; first_chunk = cb->buf_idx == 0;
rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1; rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1;
if (rets < 0) if (rets < 0)
return rets; return rets;
@ -1605,7 +1582,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
cb->completed = mei_hdr.msg_complete == 1; cb->completed = mei_hdr.msg_complete == 1;
if (first_chunk) { if (first_chunk) {
if (mei_cl_flow_ctrl_reduce(cl)) if (mei_cl_tx_flow_ctrl_creds_reduce(cl))
return -EIO; return -EIO;
} }
@ -1663,7 +1640,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
mei_hdr.msg_complete = 0; mei_hdr.msg_complete = 0;
mei_hdr.internal = cb->internal; mei_hdr.internal = cb->internal;
rets = mei_cl_flow_ctrl_creds(cl, cb->fp); rets = mei_cl_tx_flow_ctrl_creds(cl);
if (rets < 0) if (rets < 0)
goto err; goto err;
@ -1691,7 +1668,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
if (rets) if (rets)
goto err; goto err;
rets = mei_cl_flow_ctrl_reduce(cl); rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
if (rets) if (rets)
goto err; goto err;
@ -1761,6 +1738,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
case MEI_FOP_READ: case MEI_FOP_READ:
list_add_tail(&cb->list, &cl->rd_completed); list_add_tail(&cb->list, &cl->rd_completed);
if (!mei_cl_is_fixed_address(cl) &&
!WARN_ON(!cl->rx_flow_ctrl_creds))
cl->rx_flow_ctrl_creds--;
if (!mei_cl_bus_rx_event(cl)) if (!mei_cl_bus_rx_event(cl))
wake_up_interruptible(&cl->rx_wait); wake_up_interruptible(&cl->rx_wait);
break; break;

View File

@ -82,11 +82,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
/* /*
* MEI IO Functions * MEI IO Functions
*/ */
struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
const struct file *fp);
void mei_io_cb_free(struct mei_cl_cb *priv_cb); void mei_io_cb_free(struct mei_cl_cb *priv_cb);
int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length);
/** /**
* mei_io_list_init - Sets up a queue list. * mei_io_list_init - Sets up a queue list.
@ -118,6 +114,9 @@ void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp);
struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
enum mei_cb_file_ops type, enum mei_cb_file_ops type,
const struct file *fp); const struct file *fp);
struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
enum mei_cb_file_ops type,
const struct file *fp);
int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp);
/* /*

View File

@ -161,6 +161,7 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
* @dev: the device structure * @dev: the device structure
* @cl: client * @cl: client
* @hbm_cmd: host bus message command * @hbm_cmd: host bus message command
* @buf: message buffer
* @len: buffer length * @len: buffer length
* *
* Return: 0 on success, <0 on failure. * Return: 0 on success, <0 on failure.
@ -276,6 +277,7 @@ int mei_hbm_start_req(struct mei_device *dev)
dev->hbm_state = MEI_HBM_STARTING; dev->hbm_state = MEI_HBM_STARTING;
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
mei_schedule_stall_timer(dev);
return 0; return 0;
} }
@ -311,6 +313,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
} }
dev->hbm_state = MEI_HBM_ENUM_CLIENTS; dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
mei_schedule_stall_timer(dev);
return 0; return 0;
} }
@ -339,7 +342,7 @@ static int mei_hbm_me_cl_add(struct mei_device *dev,
me_cl->props = res->client_properties; me_cl->props = res->client_properties;
me_cl->client_id = res->me_addr; me_cl->client_id = res->me_addr;
me_cl->mei_flow_ctrl_creds = 0; me_cl->tx_flow_ctrl_creds = 0;
mei_me_cl_add(dev, me_cl); mei_me_cl_add(dev, me_cl);
@ -561,6 +564,7 @@ static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx)
} }
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
mei_schedule_stall_timer(dev);
return 0; return 0;
} }
@ -636,23 +640,22 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
} }
/** /**
* mei_hbm_add_single_flow_creds - adds single buffer credentials. * mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials.
* *
* @dev: the device structure * @dev: the device structure
* @flow: flow control. * @fctrl: flow control response bus message
* *
* Return: 0 on success, < 0 otherwise * Return: 0 on success, < 0 otherwise
*/ */
static int mei_hbm_add_single_flow_creds(struct mei_device *dev, static int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev,
struct hbm_flow_control *flow) struct hbm_flow_control *fctrl)
{ {
struct mei_me_client *me_cl; struct mei_me_client *me_cl;
int rets; int rets;
me_cl = mei_me_cl_by_id(dev, flow->me_addr); me_cl = mei_me_cl_by_id(dev, fctrl->me_addr);
if (!me_cl) { if (!me_cl) {
dev_err(dev->dev, "no such me client %d\n", dev_err(dev->dev, "no such me client %d\n", fctrl->me_addr);
flow->me_addr);
return -ENOENT; return -ENOENT;
} }
@ -661,9 +664,9 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
goto out; goto out;
} }
me_cl->mei_flow_ctrl_creds++; me_cl->tx_flow_ctrl_creds++;
dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n", dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
flow->me_addr, me_cl->mei_flow_ctrl_creds); fctrl->me_addr, me_cl->tx_flow_ctrl_creds);
rets = 0; rets = 0;
out: out:
@ -675,24 +678,24 @@ out:
* mei_hbm_cl_flow_control_res - flow control response from me * mei_hbm_cl_flow_control_res - flow control response from me
* *
* @dev: the device structure * @dev: the device structure
* @flow_control: flow control response bus message * @fctrl: flow control response bus message
*/ */
static void mei_hbm_cl_flow_control_res(struct mei_device *dev, static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev,
struct hbm_flow_control *flow_control) struct hbm_flow_control *fctrl)
{ {
struct mei_cl *cl; struct mei_cl *cl;
if (!flow_control->host_addr) { if (!fctrl->host_addr) {
/* single receive buffer */ /* single receive buffer */
mei_hbm_add_single_flow_creds(dev, flow_control); mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl);
return; return;
} }
cl = mei_hbm_cl_find_by_cmd(dev, flow_control); cl = mei_hbm_cl_find_by_cmd(dev, fctrl);
if (cl) { if (cl) {
cl->mei_flow_ctrl_creds++; cl->tx_flow_ctrl_creds++;
cl_dbg(dev, cl, "flow control creds = %d.\n", cl_dbg(dev, cl, "flow control creds = %d.\n",
cl->mei_flow_ctrl_creds); cl->tx_flow_ctrl_creds);
} }
} }
@ -871,10 +874,10 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
cl->state = MEI_FILE_DISCONNECTING; cl->state = MEI_FILE_DISCONNECTING;
cl->timer_count = 0; cl->timer_count = 0;
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL); cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT_RSP,
NULL);
if (!cb) if (!cb)
return -ENOMEM; return -ENOMEM;
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
} }
return 0; return 0;
} }
@ -1022,7 +1025,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
struct mei_hbm_cl_cmd *cl_cmd; struct mei_hbm_cl_cmd *cl_cmd;
struct hbm_client_connect_request *disconnect_req; struct hbm_client_connect_request *disconnect_req;
struct hbm_flow_control *flow_control; struct hbm_flow_control *fctrl;
/* read the message to our buffer */ /* read the message to our buffer */
BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf)); BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
@ -1102,8 +1105,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
case MEI_FLOW_CONTROL_CMD: case MEI_FLOW_CONTROL_CMD:
dev_dbg(dev->dev, "hbm: client flow control response: message received.\n"); dev_dbg(dev->dev, "hbm: client flow control response: message received.\n");
flow_control = (struct hbm_flow_control *) mei_msg; fctrl = (struct hbm_flow_control *)mei_msg;
mei_hbm_cl_flow_control_res(dev, flow_control); mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl);
break; break;
case MEI_PG_ISOLATION_ENTRY_RES_CMD: case MEI_PG_ISOLATION_ENTRY_RES_CMD:

View File

@ -125,6 +125,9 @@
#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */ #define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */ #define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */
#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */
#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */
/* /*
* MEI HW Section * MEI HW Section
*/ */

View File

@ -18,6 +18,7 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include "mei_dev.h" #include "mei_dev.h"
#include "hbm.h" #include "hbm.h"
@ -1063,6 +1064,8 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
} }
} }
pm_runtime_set_active(dev->dev);
hcsr = mei_hcsr_read(dev); hcsr = mei_hcsr_read(dev);
/* H_RST may be found lit before reset is started, /* H_RST may be found lit before reset is started,
* for example if preceding reset flow hasn't completed. * for example if preceding reset flow hasn't completed.

View File

@ -20,6 +20,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h> #include <linux/mei.h>
@ -935,6 +936,8 @@ static int mei_txe_hw_start(struct mei_device *dev)
return ret; return ret;
} }
pm_runtime_set_active(dev->dev);
/* enable input ready interrupts: /* enable input ready interrupts:
* SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK * SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK
*/ */

View File

@ -94,7 +94,7 @@ void mei_cancel_work(struct mei_device *dev)
cancel_work_sync(&dev->reset_work); cancel_work_sync(&dev->reset_work);
cancel_work_sync(&dev->bus_rescan_work); cancel_work_sync(&dev->bus_rescan_work);
cancel_delayed_work(&dev->timer_work); cancel_delayed_work_sync(&dev->timer_work);
} }
EXPORT_SYMBOL_GPL(mei_cancel_work); EXPORT_SYMBOL_GPL(mei_cancel_work);

View File

@ -102,26 +102,25 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
{ {
struct mei_device *dev = cl->dev; struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb; struct mei_cl_cb *cb;
unsigned char *buffer = NULL;
size_t buf_sz; size_t buf_sz;
cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
if (!cb) { if (!cb) {
cl_err(dev, cl, "pending read cb not found\n"); if (!mei_cl_is_fixed_address(cl)) {
goto out; cl_err(dev, cl, "pending read cb not found\n");
goto discard;
}
cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp);
if (!cb)
goto discard;
list_add_tail(&cb->list, &cl->rd_pending);
} }
if (!mei_cl_is_connected(cl)) { if (!mei_cl_is_connected(cl)) {
cl_dbg(dev, cl, "not connected\n"); cl_dbg(dev, cl, "not connected\n");
cb->status = -ENODEV;
goto out;
}
if (cb->buf.size == 0 || cb->buf.data == NULL) {
cl_err(dev, cl, "response buffer is not allocated.\n");
list_move_tail(&cb->list, &complete_list->list); list_move_tail(&cb->list, &complete_list->list);
cb->status = -ENOMEM; cb->status = -ENODEV;
goto out; goto discard;
} }
buf_sz = mei_hdr->length + cb->buf_idx; buf_sz = mei_hdr->length + cb->buf_idx;
@ -132,25 +131,19 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
list_move_tail(&cb->list, &complete_list->list); list_move_tail(&cb->list, &complete_list->list);
cb->status = -EMSGSIZE; cb->status = -EMSGSIZE;
goto out; goto discard;
} }
if (cb->buf.size < buf_sz) { if (cb->buf.size < buf_sz) {
cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
cb->buf.size, mei_hdr->length, cb->buf_idx); cb->buf.size, mei_hdr->length, cb->buf_idx);
buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL);
if (!buffer) { list_move_tail(&cb->list, &complete_list->list);
cb->status = -ENOMEM; cb->status = -EMSGSIZE;
list_move_tail(&cb->list, &complete_list->list); goto discard;
goto out;
}
cb->buf.data = buffer;
cb->buf.size = buf_sz;
} }
buffer = cb->buf.data + cb->buf_idx; mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length);
mei_read_slots(dev, buffer, mei_hdr->length);
cb->buf_idx += mei_hdr->length; cb->buf_idx += mei_hdr->length;
@ -162,10 +155,10 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
pm_request_autosuspend(dev->dev); pm_request_autosuspend(dev->dev);
} }
out: return 0;
if (!buffer)
mei_irq_discard_msg(dev, mei_hdr);
discard:
mei_irq_discard_msg(dev, mei_hdr);
return 0; return 0;
} }
@ -216,6 +209,9 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
int slots; int slots;
int ret; int ret;
if (!list_empty(&cl->rd_pending))
return 0;
msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
slots = mei_hbuf_empty_slots(dev); slots = mei_hbuf_empty_slots(dev);
@ -463,6 +459,19 @@ static void mei_connect_timeout(struct mei_cl *cl)
mei_reset(dev); mei_reset(dev);
} }
#define MEI_STALL_TIMER_FREQ (2 * HZ)
/**
* mei_schedule_stall_timer - re-arm stall_timer work
*
* Schedule stall timer
*
* @dev: the device structure
*/
void mei_schedule_stall_timer(struct mei_device *dev)
{
schedule_delayed_work(&dev->timer_work, MEI_STALL_TIMER_FREQ);
}
/** /**
* mei_timer - timer function. * mei_timer - timer function.
* *
@ -472,10 +481,9 @@ static void mei_connect_timeout(struct mei_cl *cl)
void mei_timer(struct work_struct *work) void mei_timer(struct work_struct *work)
{ {
struct mei_cl *cl; struct mei_cl *cl;
struct mei_device *dev = container_of(work, struct mei_device *dev = container_of(work,
struct mei_device, timer_work.work); struct mei_device, timer_work.work);
bool reschedule_timer = false;
mutex_lock(&dev->device_lock); mutex_lock(&dev->device_lock);
@ -490,6 +498,7 @@ void mei_timer(struct work_struct *work)
mei_reset(dev); mei_reset(dev);
goto out; goto out;
} }
reschedule_timer = true;
} }
} }
@ -504,6 +513,7 @@ void mei_timer(struct work_struct *work)
mei_connect_timeout(cl); mei_connect_timeout(cl);
goto out; goto out;
} }
reschedule_timer = true;
} }
} }
@ -514,19 +524,16 @@ void mei_timer(struct work_struct *work)
if (--dev->iamthif_stall_timer == 0) { if (--dev->iamthif_stall_timer == 0) {
dev_err(dev->dev, "timer: amthif hanged.\n"); dev_err(dev->dev, "timer: amthif hanged.\n");
mei_reset(dev); mei_reset(dev);
dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
mei_io_cb_free(dev->iamthif_current_cb);
dev->iamthif_current_cb = NULL;
dev->iamthif_fp = NULL;
mei_amthif_run_next_cmd(dev); mei_amthif_run_next_cmd(dev);
goto out;
} }
reschedule_timer = true;
} }
out: out:
if (dev->dev_state != MEI_DEV_DISABLED) if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer)
schedule_delayed_work(&dev->timer_work, 2 * HZ); mei_schedule_stall_timer(dev);
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
} }

View File

@ -71,6 +71,7 @@ static int mei_open(struct inode *inode, struct file *file)
goto err_unlock; goto err_unlock;
} }
cl->fp = file;
file->private_data = cl; file->private_data = cl;
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
@ -138,9 +139,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
struct mei_cl *cl = file->private_data; struct mei_cl *cl = file->private_data;
struct mei_device *dev; struct mei_device *dev;
struct mei_cl_cb *cb = NULL; struct mei_cl_cb *cb = NULL;
bool nonblock = !!(file->f_flags & O_NONBLOCK);
int rets; int rets;
int err;
if (WARN_ON(!cl || !cl->dev)) if (WARN_ON(!cl || !cl->dev))
return -ENODEV; return -ENODEV;
@ -164,11 +164,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
goto out; goto out;
} }
if (cl == &dev->iamthif_cl) {
rets = mei_amthif_read(dev, file, ubuf, length, offset);
goto out;
}
cb = mei_cl_read_cb(cl, file); cb = mei_cl_read_cb(cl, file);
if (cb) if (cb)
goto copy_buffer; goto copy_buffer;
@ -176,24 +171,29 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
if (*offset > 0) if (*offset > 0)
*offset = 0; *offset = 0;
err = mei_cl_read_start(cl, length, file); rets = mei_cl_read_start(cl, length, file);
if (err && err != -EBUSY) { if (rets && rets != -EBUSY) {
cl_dbg(dev, cl, "mei start read failure status = %d\n", err); cl_dbg(dev, cl, "mei start read failure status = %d\n", rets);
rets = err;
goto out; goto out;
} }
if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { if (nonblock) {
if (file->f_flags & O_NONBLOCK) { rets = -EAGAIN;
rets = -EAGAIN; goto out;
goto out; }
}
if (rets == -EBUSY &&
!mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) {
rets = -ENOMEM;
goto out;
}
do {
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
if (wait_event_interruptible(cl->rx_wait, if (wait_event_interruptible(cl->rx_wait,
(!list_empty(&cl->rd_completed)) || (!list_empty(&cl->rd_completed)) ||
(!mei_cl_is_connected(cl)))) { (!mei_cl_is_connected(cl)))) {
if (signal_pending(current)) if (signal_pending(current))
return -EINTR; return -EINTR;
@ -202,16 +202,12 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
mutex_lock(&dev->device_lock); mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) { if (!mei_cl_is_connected(cl)) {
rets = -EBUSY; rets = -ENODEV;
goto out; goto out;
} }
}
cb = mei_cl_read_cb(cl, file); cb = mei_cl_read_cb(cl, file);
if (!cb) { } while (!cb);
rets = 0;
goto out;
}
copy_buffer: copy_buffer:
/* now copy the data to user space */ /* now copy the data to user space */
@ -609,24 +605,24 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
goto out; goto out;
} }
if (cl == &dev->iamthif_cl) {
mask = mei_amthif_poll(dev, file, wait);
goto out;
}
if (notify_en) { if (notify_en) {
poll_wait(file, &cl->ev_wait, wait); poll_wait(file, &cl->ev_wait, wait);
if (cl->notify_ev) if (cl->notify_ev)
mask |= POLLPRI; mask |= POLLPRI;
} }
if (cl == &dev->iamthif_cl) {
mask |= mei_amthif_poll(file, wait);
goto out;
}
if (req_events & (POLLIN | POLLRDNORM)) { if (req_events & (POLLIN | POLLRDNORM)) {
poll_wait(file, &cl->rx_wait, wait); poll_wait(file, &cl->rx_wait, wait);
if (!list_empty(&cl->rd_completed)) if (!list_empty(&cl->rd_completed))
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
else else
mei_cl_read_start(cl, 0, file); mei_cl_read_start(cl, mei_cl_mtu(cl), file);
} }
out: out:

View File

@ -80,18 +80,13 @@ const char *mei_dev_state_str(int state);
enum iamthif_states { enum iamthif_states {
MEI_IAMTHIF_IDLE, MEI_IAMTHIF_IDLE,
MEI_IAMTHIF_WRITING, MEI_IAMTHIF_WRITING,
MEI_IAMTHIF_FLOW_CONTROL,
MEI_IAMTHIF_READING, MEI_IAMTHIF_READING,
MEI_IAMTHIF_READ_COMPLETE
}; };
enum mei_file_transaction_states { enum mei_file_transaction_states {
MEI_IDLE, MEI_IDLE,
MEI_WRITING, MEI_WRITING,
MEI_WRITE_COMPLETE, MEI_WRITE_COMPLETE,
MEI_FLOW_CONTROL,
MEI_READING,
MEI_READ_COMPLETE
}; };
/** /**
@ -146,7 +141,7 @@ struct mei_fw_status {
* @refcnt: struct reference count * @refcnt: struct reference count
* @props: client properties * @props: client properties
* @client_id: me client id * @client_id: me client id
* @mei_flow_ctrl_creds: flow control credits * @tx_flow_ctrl_creds: flow control credits
* @connect_count: number connections to this client * @connect_count: number connections to this client
* @bus_added: added to bus * @bus_added: added to bus
*/ */
@ -155,7 +150,7 @@ struct mei_me_client {
struct kref refcnt; struct kref refcnt;
struct mei_client_properties props; struct mei_client_properties props;
u8 client_id; u8 client_id;
u8 mei_flow_ctrl_creds; u8 tx_flow_ctrl_creds;
u8 connect_count; u8 connect_count;
u8 bus_added; u8 bus_added;
}; };
@ -202,10 +197,11 @@ struct mei_cl_cb {
* @ev_async: event async notification * @ev_async: event async notification
* @status: connection status * @status: connection status
* @me_cl: fw client connected * @me_cl: fw client connected
* @fp: file associated with client
* @host_client_id: host id * @host_client_id: host id
* @mei_flow_ctrl_creds: transmit flow credentials * @tx_flow_ctrl_creds: transmit flow credentials
* @rx_flow_ctrl_creds: receive flow credentials
* @timer_count: watchdog timer for operation completion * @timer_count: watchdog timer for operation completion
* @reserved: reserved for alignment
* @notify_en: notification - enabled/disabled * @notify_en: notification - enabled/disabled
* @notify_ev: pending notification event * @notify_ev: pending notification event
* @writing_state: state of the tx * @writing_state: state of the tx
@ -225,10 +221,11 @@ struct mei_cl {
struct fasync_struct *ev_async; struct fasync_struct *ev_async;
int status; int status;
struct mei_me_client *me_cl; struct mei_me_client *me_cl;
const struct file *fp;
u8 host_client_id; u8 host_client_id;
u8 mei_flow_ctrl_creds; u8 tx_flow_ctrl_creds;
u8 rx_flow_ctrl_creds;
u8 timer_count; u8 timer_count;
u8 reserved;
u8 notify_en; u8 notify_en;
u8 notify_ev; u8 notify_ev;
enum mei_file_transaction_states writing_state; enum mei_file_transaction_states writing_state;
@ -400,9 +397,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @override_fixed_address: force allow fixed address behavior * @override_fixed_address: force allow fixed address behavior
* *
* @amthif_cmd_list : amthif list for cmd waiting * @amthif_cmd_list : amthif list for cmd waiting
* @iamthif_fp : file for current amthif operation
* @iamthif_cl : amthif host client * @iamthif_cl : amthif host client
* @iamthif_current_cb : amthif current operation callback
* @iamthif_open_count : number of opened amthif connections * @iamthif_open_count : number of opened amthif connections
* @iamthif_stall_timer : timer to detect amthif hang * @iamthif_stall_timer : timer to detect amthif hang
* @iamthif_state : amthif processor state * @iamthif_state : amthif processor state
@ -484,10 +479,7 @@ struct mei_device {
/* amthif list for cmd waiting */ /* amthif list for cmd waiting */
struct mei_cl_cb amthif_cmd_list; struct mei_cl_cb amthif_cmd_list;
/* driver managed amthif list for reading completed amthif cmd data */
const struct file *iamthif_fp;
struct mei_cl iamthif_cl; struct mei_cl iamthif_cl;
struct mei_cl_cb *iamthif_current_cb;
long iamthif_open_count; long iamthif_open_count;
u32 iamthif_stall_timer; u32 iamthif_stall_timer;
enum iamthif_states iamthif_state; enum iamthif_states iamthif_state;
@ -556,6 +548,7 @@ void mei_cancel_work(struct mei_device *dev);
*/ */
void mei_timer(struct work_struct *work); void mei_timer(struct work_struct *work);
void mei_schedule_stall_timer(struct mei_device *dev);
int mei_irq_read_handler(struct mei_device *dev, int mei_irq_read_handler(struct mei_device *dev,
struct mei_cl_cb *cmpl_list, s32 *slots); struct mei_cl_cb *cmpl_list, s32 *slots);
@ -569,11 +562,7 @@ void mei_amthif_reset_params(struct mei_device *dev);
int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl); int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
int mei_amthif_read(struct mei_device *dev, struct file *file, unsigned int mei_amthif_poll(struct file *file, poll_table *wait);
char __user *ubuf, size_t length, loff_t *offset);
unsigned int mei_amthif_poll(struct mei_device *dev,
struct file *file, poll_table *wait);
int mei_amthif_release(struct mei_device *dev, struct file *file); int mei_amthif_release(struct mei_device *dev, struct file *file);

View File

@ -91,6 +91,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)},
/* required last entry */ /* required last entry */
{0, } {0, }
}; };
@ -217,8 +220,6 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, dev); pci_set_drvdata(pdev, dev);
schedule_delayed_work(&dev->timer_work, HZ);
/* /*
* For not wake-able HW runtime pm framework * For not wake-able HW runtime pm framework
* can't be used on pci device level. * can't be used on pci device level.
@ -400,6 +401,9 @@ static int mei_me_pm_runtime_suspend(struct device *device)
dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret); dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret);
if (ret && ret != -EAGAIN)
schedule_work(&dev->reset_work);
return ret; return ret;
} }
@ -423,6 +427,9 @@ static int mei_me_pm_runtime_resume(struct device *device)
dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret); dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret);
if (ret)
schedule_work(&dev->reset_work);
return ret; return ret;
} }

View File

@ -347,6 +347,10 @@ static int mei_txe_pm_runtime_suspend(struct device *device)
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret); dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
if (ret && ret != -EAGAIN)
schedule_work(&dev->reset_work);
return ret; return ret;
} }
@ -372,6 +376,9 @@ static int mei_txe_pm_runtime_resume(struct device *device)
dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret); dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret);
if (ret)
schedule_work(&dev->reset_work);
return ret; return ret;
} }

View File

@ -115,7 +115,6 @@ int scif_reserve_dma_chan(struct scif_endpt *ep)
*/ */
static static
void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn,
struct scif_endpt *ep,
u64 start, u64 len) u64 start, u64 len)
{ {
struct list_head *item, *tmp; struct list_head *item, *tmp;
@ -128,7 +127,6 @@ void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn,
list_for_each_safe(item, tmp, &mmn->tc_reg_list) { list_for_each_safe(item, tmp, &mmn->tc_reg_list) {
window = list_entry(item, struct scif_window, list); window = list_entry(item, struct scif_window, list);
ep = (struct scif_endpt *)window->ep;
if (!len) if (!len)
break; break;
start_va = window->va_for_temp; start_va = window->va_for_temp;
@ -146,7 +144,7 @@ static void scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, u64 start, u64 len)
struct scif_endpt *ep = mmn->ep; struct scif_endpt *ep = mmn->ep;
spin_lock(&ep->rma_info.tc_lock); spin_lock(&ep->rma_info.tc_lock);
__scif_rma_destroy_tcw(mmn, ep, start, len); __scif_rma_destroy_tcw(mmn, start, len);
spin_unlock(&ep->rma_info.tc_lock); spin_unlock(&ep->rma_info.tc_lock);
} }
@ -169,7 +167,7 @@ static void __scif_rma_destroy_tcw_ep(struct scif_endpt *ep)
spin_lock(&ep->rma_info.tc_lock); spin_lock(&ep->rma_info.tc_lock);
list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) { list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) {
mmn = list_entry(item, struct scif_mmu_notif, list); mmn = list_entry(item, struct scif_mmu_notif, list);
__scif_rma_destroy_tcw(mmn, ep, 0, ULONG_MAX); __scif_rma_destroy_tcw(mmn, 0, ULONG_MAX);
} }
spin_unlock(&ep->rma_info.tc_lock); spin_unlock(&ep->rma_info.tc_lock);
} }

View File

@ -552,7 +552,7 @@ static void scif_munmap(struct vm_area_struct *vma)
{ {
struct scif_endpt *ep; struct scif_endpt *ep;
struct vma_pvt *vmapvt = vma->vm_private_data; struct vma_pvt *vmapvt = vma->vm_private_data;
int nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; int nr_pages = vma_pages(vma);
s64 offset; s64 offset;
struct scif_rma_req req; struct scif_rma_req req;
struct scif_window *window = NULL; struct scif_window *window = NULL;
@ -614,7 +614,7 @@ int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd)
struct scif_window *window = NULL; struct scif_window *window = NULL;
struct scif_endpt *ep = (struct scif_endpt *)epd; struct scif_endpt *ep = (struct scif_endpt *)epd;
s64 start_offset = vma->vm_pgoff << PAGE_SHIFT; s64 start_offset = vma->vm_pgoff << PAGE_SHIFT;
int nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; int nr_pages = vma_pages(vma);
int err; int err;
struct vma_pvt *vmapvt; struct vma_pvt *vmapvt;

Some files were not shown because too many files have changed in this diff Show More