mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
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:
commit
7a53eea1f7
5
CREDITS
5
CREDITS
@ -1944,6 +1944,11 @@ E: kraxel@bytesex.org
|
||||
E: kraxel@suse.de
|
||||
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
|
||||
E: koenig@tat.physik.uni-tuebingen.de
|
||||
D: XFree86 (S3), DCF77, some kernel hacks and fixes
|
||||
|
@ -153,7 +153,7 @@ Description:
|
||||
|
||||
What: /sys/class/mic/mic(x)/heartbeat_enable
|
||||
Date: March 2015
|
||||
KernelVersion: 3.20
|
||||
KernelVersion: 4.4
|
||||
Contact: Ashutosh Dixit <ashutosh.dixit@intel.com>
|
||||
Description:
|
||||
The MIC drivers detect and inform user space about card crashes
|
||||
|
@ -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.
|
@ -1,11 +1,20 @@
|
||||
= Rockchip eFuse device tree bindings =
|
||||
|
||||
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
|
||||
- clocks: Should be the clock id of 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 =
|
||||
Are child nodes of eFuse, bindings of which as described in
|
||||
bindings/nvmem/nvmem.txt
|
||||
@ -13,7 +22,7 @@ bindings/nvmem/nvmem.txt
|
||||
Example:
|
||||
|
||||
efuse: efuse@ffb40000 {
|
||||
compatible = "rockchip,rockchip-efuse";
|
||||
compatible = "rockchip,rk3288-efuse";
|
||||
reg = <0xffb40000 0x20>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
@ -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
|
||||
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
|
||||
occurred a negative error code will be returned.
|
||||
|
||||
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 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
|
||||
the device driver's name.
|
||||
|
||||
The '.match' function allows controlling the number of devices that need to
|
||||
be registered. The match function should return 1 if a device should be
|
||||
The '.match' function allows control over which VME devices should be registered
|
||||
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
|
||||
the number of devices probed to one:
|
||||
|
||||
@ -385,13 +386,13 @@ location monitor location. Each location monitor can monitor a number of
|
||||
adjacent locations:
|
||||
|
||||
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);
|
||||
|
||||
The callback function is declared as follows.
|
||||
|
||||
void callback(int num);
|
||||
void callback(void *data);
|
||||
|
||||
|
||||
Slot Detection
|
||||
|
@ -7458,9 +7458,8 @@ F: Documentation/hwmon/max20751
|
||||
F: drivers/hwmon/max20751.c
|
||||
|
||||
MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
|
||||
M: "Hans J. Koch" <hjk@hansjkoch.de>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: Documentation/hwmon/max6650
|
||||
F: drivers/hwmon/max6650.c
|
||||
|
||||
@ -12418,7 +12417,6 @@ F: fs/hostfs/
|
||||
F: fs/hppfs/
|
||||
|
||||
USERSPACE I/O (UIO)
|
||||
M: "Hans J. Koch" <hjk@hansjkoch.de>
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
|
||||
|
@ -56,16 +56,4 @@ static struct miscdevice bL_switcher_device = {
|
||||
"b.L_switcher",
|
||||
&bL_switcher_fops
|
||||
};
|
||||
|
||||
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);
|
||||
module_misc_device(bL_switcher_device);
|
||||
|
@ -59,18 +59,7 @@ static struct miscdevice coreb_dev = {
|
||||
.name = "coreb",
|
||||
.fops = &coreb_fops,
|
||||
};
|
||||
|
||||
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_misc_device(coreb_dev);
|
||||
|
||||
MODULE_AUTHOR("Bas Vermeulen <bvermeul@blackstar.xs4all.nl>");
|
||||
MODULE_DESCRIPTION("BF561 Core B Support");
|
||||
|
@ -175,27 +175,4 @@ static struct miscdevice harddog_miscdev = {
|
||||
.name = "watchdog",
|
||||
.fops = &harddog_fops,
|
||||
};
|
||||
|
||||
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);
|
||||
module_misc_device(harddog_miscdev);
|
||||
|
@ -29,6 +29,8 @@ obj-$(CONFIG_SFI) += sfi/
|
||||
# was used and do nothing if so
|
||||
obj-$(CONFIG_PNP) += pnp/
|
||||
obj-y += amba/
|
||||
|
||||
obj-y += clk/
|
||||
# Many drivers will want to use DMA so this has to be made available
|
||||
# really early.
|
||||
obj-$(CONFIG_DMADEVICES) += dma/
|
||||
@ -142,8 +144,6 @@ obj-$(CONFIG_VHOST) += vhost/
|
||||
obj-$(CONFIG_VLYNQ) += vlynq/
|
||||
obj-$(CONFIG_STAGING) += staging/
|
||||
obj-y += platform/
|
||||
#common clk code
|
||||
obj-y += clk/
|
||||
|
||||
obj-$(CONFIG_MAILBOX) += mailbox/
|
||||
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
|
||||
|
@ -377,21 +377,7 @@ static struct miscdevice vhci_miscdev = {
|
||||
.fops = &vhci_fops,
|
||||
.minor = VHCI_MINOR,
|
||||
};
|
||||
|
||||
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_misc_device(vhci_miscdev);
|
||||
|
||||
module_param(amp, bool, 0644);
|
||||
MODULE_PARM_DESC(amp, "Create AMP controller device");
|
||||
|
@ -230,45 +230,7 @@ static struct miscdevice bfin_otp_misc_device = {
|
||||
.name = DRIVER_NAME,
|
||||
.fops = &bfin_otp_fops,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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_misc_device(bfin_otp_misc_device);
|
||||
|
||||
MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
|
||||
MODULE_DESCRIPTION("Blackfin OTP Memory Interface");
|
||||
|
@ -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 */
|
||||
int err = 0;
|
||||
|
||||
if (!pfn_valid(PFN_DOWN(p)))
|
||||
return -EIO;
|
||||
|
||||
read = 0;
|
||||
if (p < (unsigned long) high_memory) {
|
||||
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 */
|
||||
int err = 0;
|
||||
|
||||
if (!pfn_valid(PFN_DOWN(p)))
|
||||
return -EIO;
|
||||
|
||||
if (p < (unsigned long) high_memory) {
|
||||
unsigned long to_write = min_t(unsigned long, count,
|
||||
(unsigned long)high_memory - p);
|
||||
|
@ -124,7 +124,7 @@ static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex,
|
||||
MKBYTE(rSlaveControl));
|
||||
|
||||
rSlaveControl_Save = rSlaveControl;
|
||||
rSlaveControl.ConfigMode = TRUE;
|
||||
rSlaveControl.ConfigMode = true;
|
||||
|
||||
PRINTK_2(TRACE_3780I,
|
||||
"3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n",
|
||||
@ -155,7 +155,7 @@ unsigned char dsp3780I_ReadGenCfg(unsigned short usDspBaseIO,
|
||||
|
||||
MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl);
|
||||
rSlaveControl_Save = rSlaveControl;
|
||||
rSlaveControl.ConfigMode = TRUE;
|
||||
rSlaveControl.ConfigMode = true;
|
||||
OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl));
|
||||
OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex);
|
||||
ucValue = InByteDsp(DSP_ConfigData);
|
||||
@ -230,7 +230,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
|
||||
rUartCfg1.BaseIO = 3;
|
||||
break;
|
||||
}
|
||||
rUartCfg2.Enable = TRUE;
|
||||
rUartCfg2.Enable = true;
|
||||
}
|
||||
|
||||
rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0;
|
||||
@ -238,7 +238,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
|
||||
rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse;
|
||||
rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq];
|
||||
rHBridgeCfg1.AccessMode = 1;
|
||||
rHBridgeCfg2.Enable = TRUE;
|
||||
rHBridgeCfg2.Enable = true;
|
||||
|
||||
|
||||
rBusmasterCfg2.Reserved = 0;
|
||||
@ -278,8 +278,8 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
|
||||
* soft-reset active for 10ms.
|
||||
*/
|
||||
rSlaveControl.ClockControl = 0;
|
||||
rSlaveControl.SoftReset = TRUE;
|
||||
rSlaveControl.ConfigMode = FALSE;
|
||||
rSlaveControl.SoftReset = true;
|
||||
rSlaveControl.ConfigMode = false;
|
||||
rSlaveControl.Reserved = 0;
|
||||
|
||||
PRINTK_4(TRACE_3780I,
|
||||
@ -302,7 +302,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
|
||||
for (i = 0; i < 11; i++)
|
||||
udelay(2000);
|
||||
|
||||
rSlaveControl.SoftReset = FALSE;
|
||||
rSlaveControl.SoftReset = false;
|
||||
OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
|
||||
|
||||
MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
|
||||
@ -326,10 +326,10 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
|
||||
}
|
||||
|
||||
|
||||
rHBridgeControl.EnableDspInt = FALSE;
|
||||
rHBridgeControl.MemAutoInc = TRUE;
|
||||
rHBridgeControl.IoAutoInc = FALSE;
|
||||
rHBridgeControl.DiagnosticMode = FALSE;
|
||||
rHBridgeControl.EnableDspInt = false;
|
||||
rHBridgeControl.MemAutoInc = true;
|
||||
rHBridgeControl.IoAutoInc = false;
|
||||
rHBridgeControl.DiagnosticMode = false;
|
||||
|
||||
PRINTK_3(TRACE_3780I,
|
||||
"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);
|
||||
|
||||
PRINTK_2(TRACE_3780I,
|
||||
"3780i::dsp3780I_EnableDSP exiting bRC=TRUE, ChipID %x\n",
|
||||
"3780i::dsp3780I_EnableDSP exiting bRC=true, ChipID %x\n",
|
||||
ChipID);
|
||||
|
||||
return 0;
|
||||
@ -361,8 +361,8 @@ int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings)
|
||||
PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n");
|
||||
|
||||
rSlaveControl.ClockControl = 0;
|
||||
rSlaveControl.SoftReset = TRUE;
|
||||
rSlaveControl.ConfigMode = FALSE;
|
||||
rSlaveControl.SoftReset = true;
|
||||
rSlaveControl.ConfigMode = false;
|
||||
rSlaveControl.Reserved = 0;
|
||||
spin_lock_irqsave(&dsp_lock, flags);
|
||||
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",
|
||||
MKWORD(rHBridgeControl));
|
||||
|
||||
rHBridgeControl.EnableDspInt = FALSE;
|
||||
rHBridgeControl.EnableDspInt = false;
|
||||
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
|
||||
spin_unlock_irqrestore(&dsp_lock, flags);
|
||||
|
||||
/* Reset the core via the boot domain register */
|
||||
rBootDomain.ResetCore = TRUE;
|
||||
rBootDomain.Halt = TRUE;
|
||||
rBootDomain.NMI = TRUE;
|
||||
rBootDomain.ResetCore = true;
|
||||
rBootDomain.Halt = true;
|
||||
rBootDomain.NMI = true;
|
||||
rBootDomain.Reserved = 0;
|
||||
|
||||
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 */
|
||||
rBootDomain.ResetCore = TRUE;
|
||||
rBootDomain.Halt = FALSE;
|
||||
rBootDomain.NMI = TRUE;
|
||||
rBootDomain.ResetCore = true;
|
||||
rBootDomain.Halt = false;
|
||||
rBootDomain.NMI = true;
|
||||
rBootDomain.Reserved = 0;
|
||||
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
|
||||
|
||||
udelay(5);
|
||||
|
||||
rBootDomain.ResetCore = FALSE;
|
||||
rBootDomain.ResetCore = false;
|
||||
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
|
||||
udelay(5);
|
||||
|
||||
rBootDomain.NMI = FALSE;
|
||||
rBootDomain.NMI = false;
|
||||
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
|
||||
udelay(5);
|
||||
|
||||
/* Enable DSP to PC interrupt */
|
||||
spin_lock_irqsave(&dsp_lock, flags);
|
||||
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
|
||||
rHBridgeControl.EnableDspInt = TRUE;
|
||||
rHBridgeControl.EnableDspInt = true;
|
||||
|
||||
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n",
|
||||
MKWORD(rHBridgeControl));
|
||||
@ -466,7 +466,7 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
|
||||
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;
|
||||
}
|
||||
@ -508,7 +508,7 @@ int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
|
||||
|
||||
|
||||
PRINTK_1(TRACE_3780I,
|
||||
"3780I::dsp3780I_ReadDStore exit bRC=TRUE\n");
|
||||
"3780I::dsp3780I_ReadDStore exit bRC=true\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -550,7 +550,7 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
|
||||
|
||||
|
||||
PRINTK_1(TRACE_3780I,
|
||||
"3780I::dsp3780I_ReadAndClearDStore exit bRC=TRUE\n");
|
||||
"3780I::dsp3780I_ReadAndClearDStore exit bRC=true\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -592,7 +592,7 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
|
||||
|
||||
|
||||
PRINTK_1(TRACE_3780I,
|
||||
"3780I::dsp3780D_WriteDStore exit bRC=TRUE\n");
|
||||
"3780I::dsp3780D_WriteDStore exit bRC=true\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -640,7 +640,7 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
|
||||
}
|
||||
|
||||
PRINTK_1(TRACE_3780I,
|
||||
"3780I::dsp3780I_ReadIStore exit bRC=TRUE\n");
|
||||
"3780I::dsp3780I_ReadIStore exit bRC=true\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -689,7 +689,7 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
|
||||
}
|
||||
|
||||
PRINTK_1(TRACE_3780I,
|
||||
"3780I::dsp3780I_WriteIStore exit bRC=TRUE\n");
|
||||
"3780I::dsp3780I_WriteIStore exit bRC=true\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -713,7 +713,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
|
||||
*/
|
||||
spin_lock_irqsave(&dsp_lock, flags);
|
||||
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
|
||||
rHBridgeControl.EnableDspInt = FALSE;
|
||||
rHBridgeControl.EnableDspInt = false;
|
||||
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
|
||||
|
||||
*pusIPCSource = InWordDsp(DSP_Interrupt);
|
||||
@ -725,7 +725,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
|
||||
|
||||
OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource));
|
||||
|
||||
rHBridgeControl.EnableDspInt = TRUE;
|
||||
rHBridgeControl.EnableDspInt = true;
|
||||
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
|
||||
spin_unlock_irqrestore(&dsp_lock, flags);
|
||||
|
||||
|
@ -101,7 +101,7 @@ typedef struct {
|
||||
} DSP_UART_CFG_1;
|
||||
|
||||
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 */
|
||||
} DSP_UART_CFG_2;
|
||||
|
||||
@ -114,7 +114,7 @@ typedef struct {
|
||||
} DSP_HBRIDGE_CFG_1;
|
||||
|
||||
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 */
|
||||
} DSP_HBRIDGE_CFG_2;
|
||||
|
||||
@ -133,12 +133,12 @@ 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 */
|
||||
} DSP_ISA_PROT_CFG;
|
||||
|
||||
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 */
|
||||
} DSP_POWER_MGMT_CFG;
|
||||
|
||||
|
@ -296,8 +296,8 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
|
||||
pDrvData->IPCs[ipcnum].usIntCount);
|
||||
|
||||
mutex_lock(&mwave_mutex);
|
||||
pDrvData->IPCs[ipcnum].bIsHere = FALSE;
|
||||
pDrvData->IPCs[ipcnum].bIsEnabled = TRUE;
|
||||
pDrvData->IPCs[ipcnum].bIsHere = false;
|
||||
pDrvData->IPCs[ipcnum].bIsEnabled = true;
|
||||
mutex_unlock(&mwave_mutex);
|
||||
|
||||
PRINTK_2(TRACE_MWAVE,
|
||||
@ -324,7 +324,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
|
||||
pDrvData->IPCs[ipcnum].usIntCount);
|
||||
|
||||
mutex_lock(&mwave_mutex);
|
||||
if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) {
|
||||
if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
PRINTK_2(TRACE_MWAVE,
|
||||
@ -332,7 +332,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
|
||||
" ipc %x going to sleep\n",
|
||||
ipcnum);
|
||||
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);
|
||||
/* check whether an event was signalled by */
|
||||
/* the interrupt handler while we were gone */
|
||||
@ -355,7 +355,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
|
||||
" application\n",
|
||||
ipcnum);
|
||||
}
|
||||
pDrvData->IPCs[ipcnum].bIsHere = FALSE;
|
||||
pDrvData->IPCs[ipcnum].bIsHere = false;
|
||||
remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
|
||||
set_current_state(TASK_RUNNING);
|
||||
PRINTK_2(TRACE_MWAVE,
|
||||
@ -384,9 +384,9 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&mwave_mutex);
|
||||
if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) {
|
||||
pDrvData->IPCs[ipcnum].bIsEnabled = FALSE;
|
||||
if (pDrvData->IPCs[ipcnum].bIsHere == TRUE) {
|
||||
if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
|
||||
pDrvData->IPCs[ipcnum].bIsEnabled = false;
|
||||
if (pDrvData->IPCs[ipcnum].bIsHere == true) {
|
||||
wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
|
||||
}
|
||||
}
|
||||
@ -541,7 +541,7 @@ static void mwave_exit(void)
|
||||
|
||||
if (pDrvData->device_registered) {
|
||||
device_unregister(&mwave_device);
|
||||
pDrvData->device_registered = FALSE;
|
||||
pDrvData->device_registered = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -576,16 +576,16 @@ static int __init mwave_init(void)
|
||||
|
||||
memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA));
|
||||
|
||||
pDrvData->bBDInitialized = FALSE;
|
||||
pDrvData->bResourcesClaimed = FALSE;
|
||||
pDrvData->bDSPEnabled = FALSE;
|
||||
pDrvData->bDSPReset = FALSE;
|
||||
pDrvData->bMwaveDevRegistered = FALSE;
|
||||
pDrvData->bBDInitialized = false;
|
||||
pDrvData->bResourcesClaimed = false;
|
||||
pDrvData->bDSPEnabled = false;
|
||||
pDrvData->bDSPReset = false;
|
||||
pDrvData->bMwaveDevRegistered = false;
|
||||
pDrvData->sLine = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
|
||||
pDrvData->IPCs[i].bIsEnabled = FALSE;
|
||||
pDrvData->IPCs[i].bIsHere = FALSE;
|
||||
pDrvData->IPCs[i].bIsEnabled = false;
|
||||
pDrvData->IPCs[i].bIsHere = false;
|
||||
pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */
|
||||
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");
|
||||
goto cleanup_error;
|
||||
}
|
||||
pDrvData->bBDInitialized = TRUE;
|
||||
pDrvData->bBDInitialized = true;
|
||||
|
||||
retval = tp3780I_CalcResources(&pDrvData->rBDData);
|
||||
PRINTK_2(TRACE_MWAVE,
|
||||
@ -626,7 +626,7 @@ static int __init mwave_init(void)
|
||||
" Failed to claim resources\n");
|
||||
goto cleanup_error;
|
||||
}
|
||||
pDrvData->bResourcesClaimed = TRUE;
|
||||
pDrvData->bResourcesClaimed = true;
|
||||
|
||||
retval = tp3780I_EnableDSP(&pDrvData->rBDData);
|
||||
PRINTK_2(TRACE_MWAVE,
|
||||
@ -639,7 +639,7 @@ static int __init mwave_init(void)
|
||||
" Failed to enable DSP\n");
|
||||
goto cleanup_error;
|
||||
}
|
||||
pDrvData->bDSPEnabled = TRUE;
|
||||
pDrvData->bDSPEnabled = true;
|
||||
|
||||
if (misc_register(&mwave_misc_dev) < 0) {
|
||||
PRINTK_ERROR(KERN_ERR_MWAVE
|
||||
@ -647,7 +647,7 @@ static int __init mwave_init(void)
|
||||
" Failed to register misc device\n");
|
||||
goto cleanup_error;
|
||||
}
|
||||
pDrvData->bMwaveDevRegistered = TRUE;
|
||||
pDrvData->bMwaveDevRegistered = true;
|
||||
|
||||
pDrvData->sLine = register_serial_portandirq(
|
||||
pDrvData->rBDData.rDspSettings.usUartBaseIO,
|
||||
@ -668,7 +668,7 @@ static int __init mwave_init(void)
|
||||
|
||||
if (device_register(&mwave_device))
|
||||
goto cleanup_error;
|
||||
pDrvData->device_registered = TRUE;
|
||||
pDrvData->device_registered = true;
|
||||
for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) {
|
||||
if(device_create_file(&mwave_device, mwave_dev_attrs[i])) {
|
||||
PRINTK_ERROR(KERN_ERR_MWAVE
|
||||
|
@ -125,8 +125,8 @@ extern int mwave_uart_io;
|
||||
|
||||
typedef struct _MWAVE_IPC {
|
||||
unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */
|
||||
BOOLEAN bIsEnabled;
|
||||
BOOLEAN bIsHere;
|
||||
bool bIsEnabled;
|
||||
bool bIsHere;
|
||||
/* entry spin lock */
|
||||
wait_queue_head_t ipc_wait_queue;
|
||||
} MWAVE_IPC;
|
||||
@ -135,12 +135,12 @@ typedef struct _MWAVE_DEVICE_DATA {
|
||||
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_DPC; /* IPC source bits for recently processed intr, set during DPC processing */
|
||||
BOOLEAN bBDInitialized;
|
||||
BOOLEAN bResourcesClaimed;
|
||||
BOOLEAN bDSPEnabled;
|
||||
BOOLEAN bDSPReset;
|
||||
bool bBDInitialized;
|
||||
bool bResourcesClaimed;
|
||||
bool bDSPEnabled;
|
||||
bool bDSPReset;
|
||||
MWAVE_IPC IPCs[16];
|
||||
BOOLEAN bMwaveDevRegistered;
|
||||
bool bMwaveDevRegistered;
|
||||
short sLine;
|
||||
int nr_registered_attrs;
|
||||
int device_registered;
|
||||
|
@ -493,7 +493,7 @@ int smapi_set_DSP_cfg(void)
|
||||
}
|
||||
|
||||
|
||||
int smapi_set_DSP_power_state(BOOLEAN bOn)
|
||||
int smapi_set_DSP_power_state(bool bOn)
|
||||
{
|
||||
int bRC = -EIO;
|
||||
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");
|
||||
} else {
|
||||
PRINTK_2(TRACE_SMAPI,
|
||||
"smapi::smapi_init, exit TRUE g_usSmapiPort %x\n",
|
||||
"smapi::smapi_init, exit true g_usSmapiPort %x\n",
|
||||
g_usSmapiPort);
|
||||
retval = 0;
|
||||
//SmapiQuerySystemID();
|
||||
|
@ -49,10 +49,6 @@
|
||||
#ifndef _LINUX_SMAPI_H
|
||||
#define _LINUX_SMAPI_H
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define BOOLEAN int
|
||||
|
||||
typedef struct {
|
||||
int bDSPPresent;
|
||||
int bDSPEnabled;
|
||||
@ -74,7 +70,7 @@ typedef struct {
|
||||
int smapi_init(void);
|
||||
int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings);
|
||||
int smapi_set_DSP_cfg(void);
|
||||
int smapi_set_DSP_power_state(BOOLEAN bOn);
|
||||
int smapi_set_DSP_power_state(bool bOn);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -80,13 +80,13 @@ static void EnableSRAM(THINKPAD_BD_DATA * pBDData)
|
||||
WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode));
|
||||
|
||||
MKWORD(rGpioDriverEnable) = 0;
|
||||
rGpioDriverEnable.Enable10 = TRUE;
|
||||
rGpioDriverEnable.Mask10 = TRUE;
|
||||
rGpioDriverEnable.Enable10 = true;
|
||||
rGpioDriverEnable.Mask10 = true;
|
||||
WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable));
|
||||
|
||||
MKWORD(rGpioOutputData) = 0;
|
||||
rGpioOutputData.Latch10 = 0;
|
||||
rGpioOutputData.Mask10 = TRUE;
|
||||
rGpioOutputData.Mask10 = true;
|
||||
WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData));
|
||||
|
||||
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,
|
||||
"tp3780i::DspInterrupt usIntCount %x\n",
|
||||
pDrvData->IPCs[usPCNum - 1].usIntCount);
|
||||
if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == TRUE) {
|
||||
if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) {
|
||||
PRINTK_2(TRACE_TP3780I,
|
||||
"tp3780i::DspInterrupt, waking up usPCNum %x\n",
|
||||
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);
|
||||
|
||||
pBDData->bDSPEnabled = FALSE;
|
||||
pSettings->bInterruptClaimed = FALSE;
|
||||
pBDData->bDSPEnabled = false;
|
||||
pSettings->bInterruptClaimed = false;
|
||||
|
||||
retval = smapi_init();
|
||||
if (retval) {
|
||||
@ -269,7 +269,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
|
||||
|
||||
if (pSettings->bInterruptClaimed) {
|
||||
free_irq(pSettings->usDspIrq, NULL);
|
||||
pSettings->bInterruptClaimed = FALSE;
|
||||
pSettings->bInterruptClaimed = false;
|
||||
}
|
||||
|
||||
PRINTK_2(TRACE_TP3780I,
|
||||
@ -283,7 +283,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
|
||||
int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -336,14 +336,14 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
|
||||
}
|
||||
}
|
||||
|
||||
pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = TRUE;
|
||||
pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = TRUE;
|
||||
pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true;
|
||||
pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true;
|
||||
|
||||
if (pBDData->bShareDspIrq) {
|
||||
pSettings->bDspIrqActiveLow = FALSE;
|
||||
pSettings->bDspIrqActiveLow = false;
|
||||
}
|
||||
if (pBDData->bShareUartIrq) {
|
||||
pSettings->bUartIrqActiveLow = FALSE;
|
||||
pSettings->bUartIrqActiveLow = false;
|
||||
}
|
||||
|
||||
pSettings->usNumTransfers = TP_CFG_NumTransfers;
|
||||
@ -373,16 +373,16 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
|
||||
PRINTK_3(TRACE_TP3780I,
|
||||
"tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n",
|
||||
pSettings->usDspIrq, pBDData->bShareDspIrq);
|
||||
bInterruptAllocated = TRUE;
|
||||
pSettings->bInterruptClaimed = TRUE;
|
||||
bInterruptAllocated = true;
|
||||
pSettings->bInterruptClaimed = true;
|
||||
}
|
||||
|
||||
smapi_set_DSP_power_state(FALSE);
|
||||
if (smapi_set_DSP_power_state(TRUE)) {
|
||||
PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(TRUE) failed\n");
|
||||
smapi_set_DSP_power_state(false);
|
||||
if (smapi_set_DSP_power_state(true)) {
|
||||
PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(true) failed\n");
|
||||
goto exit_cleanup;
|
||||
} else {
|
||||
bDSPPoweredUp = TRUE;
|
||||
bDSPPoweredUp = true;
|
||||
}
|
||||
|
||||
if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) {
|
||||
@ -392,7 +392,7 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
|
||||
|
||||
EnableSRAM(pBDData);
|
||||
|
||||
pBDData->bDSPEnabled = TRUE;
|
||||
pBDData->bDSPEnabled = true;
|
||||
|
||||
PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n");
|
||||
|
||||
@ -401,10 +401,10 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
|
||||
exit_cleanup:
|
||||
PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n");
|
||||
if (bDSPPoweredUp)
|
||||
smapi_set_DSP_power_state(FALSE);
|
||||
smapi_set_DSP_power_state(false);
|
||||
if (bInterruptAllocated) {
|
||||
free_irq(pSettings->usDspIrq, NULL);
|
||||
pSettings->bInterruptClaimed = FALSE;
|
||||
pSettings->bInterruptClaimed = false;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
@ -421,10 +421,10 @@ int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData)
|
||||
dsp3780I_DisableDSP(&pBDData->rDspSettings);
|
||||
if (pSettings->bInterruptClaimed) {
|
||||
free_irq(pSettings->usDspIrq, NULL);
|
||||
pSettings->bInterruptClaimed = FALSE;
|
||||
pSettings->bInterruptClaimed = false;
|
||||
}
|
||||
smapi_set_DSP_power_state(FALSE);
|
||||
pBDData->bDSPEnabled = FALSE;
|
||||
smapi_set_DSP_power_state(false);
|
||||
pBDData->bDSPEnabled = false;
|
||||
}
|
||||
|
||||
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;
|
||||
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
|
||||
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
|
||||
BOOLEAN bRC = 0;
|
||||
bool bRC = 0;
|
||||
|
||||
PRINTK_6(TRACE_TP3780I,
|
||||
"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;
|
||||
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
|
||||
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
|
||||
BOOLEAN bRC = 0;
|
||||
bool bRC = 0;
|
||||
|
||||
PRINTK_6(TRACE_TP3780I,
|
||||
"tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",
|
||||
|
@ -286,7 +286,7 @@ static int register_device(int minor, struct pp_struct *pp)
|
||||
struct parport *port;
|
||||
struct pardevice *pdev = NULL;
|
||||
char *name;
|
||||
int fl;
|
||||
struct pardev_cb ppdev_cb;
|
||||
|
||||
name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
|
||||
if (name == NULL)
|
||||
@ -299,9 +299,11 @@ static int register_device(int minor, struct pp_struct *pp)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
|
||||
pdev = parport_register_device(port, name, NULL,
|
||||
NULL, pp_irq, fl, pp);
|
||||
memset(&ppdev_cb, 0, sizeof(ppdev_cb));
|
||||
ppdev_cb.irq_func = pp_irq;
|
||||
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);
|
||||
|
||||
if (!pdev) {
|
||||
@ -799,10 +801,23 @@ static void pp_detach(struct parport *port)
|
||||
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 = {
|
||||
.name = CHRDEV,
|
||||
.attach = pp_attach,
|
||||
.probe = pp_probe,
|
||||
.match_port = pp_attach,
|
||||
.detach = pp_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
|
||||
static int __init ppdev_init(void)
|
||||
|
@ -385,13 +385,18 @@ scdrv_init(void)
|
||||
|
||||
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,
|
||||
SYSCTL_BASENAME) < 0) {
|
||||
printk("%s: failed to register SN system controller device\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME);
|
||||
|
||||
for (cnode = 0; cnode < num_cnodes; cnode++) {
|
||||
geoid = cnodeid_get_geoid(cnode);
|
||||
|
@ -331,13 +331,11 @@ static const struct file_operations srom_fops = {
|
||||
/**
|
||||
* srom_setup_minor() - Initialize per-minor information.
|
||||
* @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;
|
||||
int devhdl = srom->hv_devhdl;
|
||||
|
||||
srom->hv_devhdl = devhdl;
|
||||
mutex_init(&srom->lock);
|
||||
|
||||
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)
|
||||
return -EIO;
|
||||
|
||||
dev = device_create(srom_class, &srom_parent->dev,
|
||||
MKDEV(srom_major, index), srom, "%d", index);
|
||||
return PTR_ERR_OR_ZERO(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** 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
|
||||
* 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. */
|
||||
for (i = 0; ; i++) {
|
||||
@ -373,7 +369,7 @@ static int srom_init(void)
|
||||
char buf[20];
|
||||
struct srom_dev *new_srom_devices =
|
||||
krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
GFP_KERNEL);
|
||||
if (!new_srom_devices) {
|
||||
result = -ENOMEM;
|
||||
goto fail_mem;
|
||||
@ -387,7 +383,9 @@ static int srom_init(void)
|
||||
i, devhdl);
|
||||
break;
|
||||
}
|
||||
srom_devices[i].hv_devhdl = devhdl;
|
||||
result = srom_setup_minor(&srom_devices[i], devhdl);
|
||||
if (result != 0)
|
||||
goto fail_mem;
|
||||
}
|
||||
srom_devs = i;
|
||||
|
||||
@ -431,9 +429,13 @@ static int srom_init(void)
|
||||
srom_class->dev_groups = srom_dev_groups;
|
||||
srom_class->devnode = srom_devnode;
|
||||
|
||||
/* Do per-partition initialization */
|
||||
/* Create per-partition devices */
|
||||
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)
|
||||
goto fail_class;
|
||||
}
|
||||
|
@ -31,60 +31,53 @@ static struct ttyprintk_port tpk_port;
|
||||
* printk messages (also suitable for logging service):
|
||||
* - any cr is replaced by nl
|
||||
* - adds a ttyprintk source tag in front of each line
|
||||
* - too long message is fragmeted, with '\'nl between fragments
|
||||
* - TPK_STR_SIZE isn't really the write_room limiting factor, bcause
|
||||
* - too long message is fragmented, with '\'nl between fragments
|
||||
* - TPK_STR_SIZE isn't really the write_room limiting factor, because
|
||||
* it is emptied on the fly during preformatting.
|
||||
*/
|
||||
#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
|
||||
#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 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 char tmp[TPK_STR_SIZE + 4];
|
||||
int i = tpk_curr;
|
||||
|
||||
if (buf == NULL) {
|
||||
/* flush tmp[] */
|
||||
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;
|
||||
}
|
||||
tpk_flush();
|
||||
return i;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
tmp[tpk_curr] = buf[i];
|
||||
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 {
|
||||
if (tpk_curr >= TPK_STR_SIZE) {
|
||||
/* end of tmp buffer reached: cut the message in two */
|
||||
tmp[tpk_curr + 1] = '\\';
|
||||
tmp[tpk_curr + 2] = '\n';
|
||||
tmp[tpk_curr + 3] = '\0';
|
||||
printk(KERN_INFO "%s%s", tpk_tag, tmp);
|
||||
tpk_curr = 0;
|
||||
tpk_buffer[tpk_curr++] = '\\';
|
||||
tpk_flush();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -655,10 +655,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
|
||||
|
||||
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) {
|
||||
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);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ config FPGA_MGR_SOCFPGA
|
||||
|
||||
config FPGA_MGR_ZYNQ_FPGA
|
||||
tristate "Xilinx Zynq FPGA"
|
||||
depends on ARCH_ZYNQ || COMPILE_TEST
|
||||
depends on HAS_DMA
|
||||
help
|
||||
FPGA manager driver support for Xilinx Zynq FPGAs.
|
||||
|
@ -779,19 +779,8 @@ static struct miscdevice uhid_misc = {
|
||||
.minor = UHID_MINOR,
|
||||
.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_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
|
||||
MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
|
||||
|
@ -43,7 +43,12 @@ static void vmbus_setevent(struct vmbus_channel *channel)
|
||||
{
|
||||
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 */
|
||||
sync_set_bit(channel->offermsg.child_relid & 31,
|
||||
(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_msginfo *open_info = NULL;
|
||||
void *in, *out;
|
||||
unsigned long flags;
|
||||
int ret, err = 0;
|
||||
unsigned long t;
|
||||
struct page *page;
|
||||
|
||||
if (send_ringbuffer_size % PAGE_SIZE ||
|
||||
recv_ringbuffer_size % PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&newchannel->lock, flags);
|
||||
if (newchannel->state == CHANNEL_OPEN_STATE) {
|
||||
newchannel->state = CHANNEL_OPENING_STATE;
|
||||
@ -95,36 +102,33 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
recv_ringbuffer_size));
|
||||
|
||||
if (!page)
|
||||
out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
|
||||
get_order(send_ringbuffer_size +
|
||||
recv_ringbuffer_size));
|
||||
else
|
||||
out = (void *)page_address(page);
|
||||
page = alloc_pages(GFP_KERNEL|__GFP_ZERO,
|
||||
get_order(send_ringbuffer_size +
|
||||
recv_ringbuffer_size));
|
||||
|
||||
if (!out) {
|
||||
if (!page) {
|
||||
err = -ENOMEM;
|
||||
goto error0;
|
||||
goto error_set_chnstate;
|
||||
}
|
||||
|
||||
in = (void *)((unsigned long)out + send_ringbuffer_size);
|
||||
|
||||
newchannel->ringbuffer_pages = out;
|
||||
newchannel->ringbuffer_pages = page_address(page);
|
||||
newchannel->ringbuffer_pagecount = (send_ringbuffer_size +
|
||||
recv_ringbuffer_size) >> PAGE_SHIFT;
|
||||
|
||||
ret = hv_ringbuffer_init(
|
||||
&newchannel->outbound, out, send_ringbuffer_size);
|
||||
ret = hv_ringbuffer_init(&newchannel->outbound, page,
|
||||
send_ringbuffer_size >> PAGE_SHIFT);
|
||||
|
||||
if (ret != 0) {
|
||||
err = ret;
|
||||
goto error0;
|
||||
goto error_free_pages;
|
||||
}
|
||||
|
||||
ret = hv_ringbuffer_init(
|
||||
&newchannel->inbound, in, recv_ringbuffer_size);
|
||||
ret = hv_ringbuffer_init(&newchannel->inbound,
|
||||
&page[send_ringbuffer_size >> PAGE_SHIFT],
|
||||
recv_ringbuffer_size >> PAGE_SHIFT);
|
||||
if (ret != 0) {
|
||||
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;
|
||||
|
||||
ret = vmbus_establish_gpadl(newchannel,
|
||||
newchannel->outbound.ring_buffer,
|
||||
send_ringbuffer_size +
|
||||
recv_ringbuffer_size,
|
||||
&newchannel->ringbuffer_gpadlhandle);
|
||||
page_address(page),
|
||||
send_ringbuffer_size +
|
||||
recv_ringbuffer_size,
|
||||
&newchannel->ringbuffer_gpadlhandle);
|
||||
|
||||
if (ret != 0) {
|
||||
err = ret;
|
||||
goto error0;
|
||||
goto error_free_pages;
|
||||
}
|
||||
|
||||
/* Create and init the channel open message */
|
||||
@ -148,7 +152,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
GFP_KERNEL);
|
||||
if (!open_info) {
|
||||
err = -ENOMEM;
|
||||
goto error_gpadl;
|
||||
goto error_free_gpadl;
|
||||
}
|
||||
|
||||
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) {
|
||||
err = -EINVAL;
|
||||
goto error_gpadl;
|
||||
goto error_free_gpadl;
|
||||
}
|
||||
|
||||
if (userdatalen)
|
||||
@ -180,14 +184,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
|
||||
|
||||
if (ret != 0) {
|
||||
err = ret;
|
||||
goto error1;
|
||||
goto error_clean_msglist;
|
||||
}
|
||||
|
||||
t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ);
|
||||
if (t == 0) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error1;
|
||||
}
|
||||
wait_for_completion(&open_info->waitevent);
|
||||
|
||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||
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) {
|
||||
err = -EAGAIN;
|
||||
goto error_gpadl;
|
||||
goto error_free_gpadl;
|
||||
}
|
||||
|
||||
newchannel->state = CHANNEL_OPENED_STATE;
|
||||
kfree(open_info);
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
error_clean_msglist:
|
||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||
list_del(&open_info->msglistentry);
|
||||
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
|
||||
|
||||
error_gpadl:
|
||||
error_free_gpadl:
|
||||
vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
|
||||
|
||||
error0:
|
||||
free_pages((unsigned long)out,
|
||||
get_order(send_ringbuffer_size + recv_ringbuffer_size));
|
||||
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;
|
||||
return err;
|
||||
}
|
||||
@ -238,8 +240,7 @@ EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
|
||||
* create_gpadl_header - Creates a gpadl for the specified buffer
|
||||
*/
|
||||
static int create_gpadl_header(void *kbuffer, u32 size,
|
||||
struct vmbus_channel_msginfo **msginfo,
|
||||
u32 *messagecount)
|
||||
struct vmbus_channel_msginfo **msginfo)
|
||||
{
|
||||
int i;
|
||||
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(
|
||||
kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
|
||||
*msginfo = msgheader;
|
||||
*messagecount = 1;
|
||||
|
||||
pfnsum = pfncount;
|
||||
pfnleft = pagecount - pfncount;
|
||||
@ -323,7 +323,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
|
||||
}
|
||||
|
||||
msgbody->msgsize = msgsize;
|
||||
(*messagecount)++;
|
||||
gpadl_body =
|
||||
(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);
|
||||
if (msgheader == NULL)
|
||||
goto nomem;
|
||||
|
||||
INIT_LIST_HEAD(&msgheader->submsglist);
|
||||
msgheader->msgsize = msgsize;
|
||||
|
||||
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;
|
||||
|
||||
*msginfo = msgheader;
|
||||
*messagecount = 1;
|
||||
}
|
||||
|
||||
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_body *gpadl_body;
|
||||
struct vmbus_channel_msginfo *msginfo = NULL;
|
||||
struct vmbus_channel_msginfo *submsginfo;
|
||||
u32 msgcount;
|
||||
struct vmbus_channel_msginfo *submsginfo, *tmp;
|
||||
struct list_head *curr;
|
||||
u32 next_gpadl_handle;
|
||||
unsigned long flags;
|
||||
@ -400,7 +399,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
|
||||
next_gpadl_handle =
|
||||
(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)
|
||||
return ret;
|
||||
|
||||
@ -423,24 +422,21 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
|
||||
if (ret != 0)
|
||||
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 =
|
||||
(struct vmbus_channel_gpadl_body *)submsginfo->msg;
|
||||
gpadl_body->header.msgtype =
|
||||
CHANNELMSG_GPADL_BODY;
|
||||
gpadl_body->gpadl = next_gpadl_handle;
|
||||
|
||||
gpadl_body->header.msgtype =
|
||||
CHANNELMSG_GPADL_BODY;
|
||||
gpadl_body->gpadl = next_gpadl_handle;
|
||||
ret = vmbus_post_msg(gpadl_body,
|
||||
submsginfo->msgsize -
|
||||
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);
|
||||
|
||||
@ -451,6 +447,10 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
|
||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||
list_del(&msginfo->msglistentry);
|
||||
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
|
||||
list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist,
|
||||
msglistentry) {
|
||||
kfree(submsginfo);
|
||||
}
|
||||
|
||||
kfree(msginfo);
|
||||
return ret;
|
||||
@ -512,7 +512,6 @@ static void reset_channel_cb(void *arg)
|
||||
static int vmbus_close_internal(struct vmbus_channel *channel)
|
||||
{
|
||||
struct vmbus_channel_close_channel *msg;
|
||||
struct tasklet_struct *tasklet;
|
||||
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
|
||||
* tasklet when the latter is running here.
|
||||
*/
|
||||
tasklet = hv_context.event_dpc[channel->target_cpu];
|
||||
tasklet_disable(tasklet);
|
||||
hv_event_tasklet_disable(channel);
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
out:
|
||||
tasklet_enable(tasklet);
|
||||
hv_event_tasklet_enable(channel);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -659,7 +657,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs,
|
||||
&signal, lock);
|
||||
&signal, lock, channel->signal_policy);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (channel->signal_policy)
|
||||
signal = true;
|
||||
else
|
||||
kick_q = true;
|
||||
|
||||
if (((ret == 0) && kick_q && signal) ||
|
||||
(ret && !is_hvsock_channel(channel)))
|
||||
vmbus_setevent(channel);
|
||||
@ -777,7 +770,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
|
||||
&signal, lock);
|
||||
&signal, lock, channel->signal_policy);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (channel->signal_policy)
|
||||
signal = true;
|
||||
else
|
||||
kick_q = true;
|
||||
|
||||
if (((ret == 0) && kick_q && signal) || (ret))
|
||||
vmbus_setevent(channel);
|
||||
|
||||
@ -861,7 +849,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
|
||||
&signal, lock);
|
||||
&signal, lock, channel->signal_policy);
|
||||
|
||||
if (ret == 0 && signal)
|
||||
vmbus_setevent(channel);
|
||||
@ -926,7 +914,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
|
||||
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
|
||||
|
||||
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
|
||||
&signal, lock);
|
||||
&signal, lock, channel->signal_policy);
|
||||
|
||||
if (ret == 0 && signal)
|
||||
vmbus_setevent(channel);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.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;
|
||||
|
||||
if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid))
|
||||
return HV_UNKOWN;
|
||||
|
||||
for (i = HV_IDE; i < HV_UNKOWN; i++) {
|
||||
if (!uuid_le_cmp(*guid, vmbus_devs[i].guid))
|
||||
return i;
|
||||
@ -251,14 +274,12 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
|
||||
*/
|
||||
static struct vmbus_channel *alloc_channel(void)
|
||||
{
|
||||
static atomic_t chan_num = ATOMIC_INIT(0);
|
||||
struct vmbus_channel *channel;
|
||||
|
||||
channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
|
||||
if (!channel)
|
||||
return NULL;
|
||||
|
||||
channel->id = atomic_inc_return(&chan_num);
|
||||
channel->acquire_ring_lock = true;
|
||||
spin_lock_init(&channel->inbound_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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct vmbus_channel *primary_channel;
|
||||
|
||||
vmbus_release_relid(relid);
|
||||
|
||||
BUG_ON(!channel->rescind);
|
||||
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
|
||||
|
||||
hv_event_tasklet_disable(channel);
|
||||
if (channel->target_cpu != get_cpu()) {
|
||||
put_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);
|
||||
put_cpu();
|
||||
}
|
||||
hv_event_tasklet_enable(channel);
|
||||
|
||||
if (channel->primary_channel == NULL) {
|
||||
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
|
||||
* of sub-channel, when we reload drivers like hv_netvsc.
|
||||
*/
|
||||
cpumask_clear_cpu(channel->target_cpu,
|
||||
&primary_channel->alloced_cpus_in_node);
|
||||
if (channel->affinity_policy == HV_LOCALIZED)
|
||||
cpumask_clear_cpu(channel->target_cpu,
|
||||
&primary_channel->alloced_cpus_in_node);
|
||||
|
||||
vmbus_release_relid(relid);
|
||||
|
||||
free_channel(channel);
|
||||
}
|
||||
@ -405,10 +446,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
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);
|
||||
|
||||
hv_event_tasklet_disable(newchannel);
|
||||
if (newchannel->target_cpu != get_cpu()) {
|
||||
put_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);
|
||||
put_cpu();
|
||||
}
|
||||
hv_event_tasklet_enable(newchannel);
|
||||
|
||||
/*
|
||||
* This state is used to indicate a successful open
|
||||
@ -463,12 +508,11 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
return;
|
||||
|
||||
err_deq_chan:
|
||||
vmbus_release_relid(newchannel->offermsg.child_relid);
|
||||
|
||||
mutex_lock(&vmbus_connection.channel_mutex);
|
||||
list_del(&newchannel->listentry);
|
||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||
|
||||
hv_event_tasklet_disable(newchannel);
|
||||
if (newchannel->target_cpu != get_cpu()) {
|
||||
put_cpu();
|
||||
smp_call_function_single(newchannel->target_cpu,
|
||||
@ -477,6 +521,9 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||
percpu_channel_deq(newchannel);
|
||||
put_cpu();
|
||||
}
|
||||
hv_event_tasklet_enable(newchannel);
|
||||
|
||||
vmbus_release_relid(newchannel->offermsg.child_relid);
|
||||
|
||||
err_free_chan:
|
||||
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
|
||||
* NUMA nodes and within the assigned NUMA node we will assign the
|
||||
* first available CPU to the primary channel.
|
||||
* The sub-channels will be assigned to the CPUs available in the
|
||||
* NUMA node evenly.
|
||||
* Based on the channel affinity policy, we will assign the NUMA
|
||||
* nodes.
|
||||
*/
|
||||
if (!primary) {
|
||||
|
||||
if ((channel->affinity_policy == HV_BALANCED) || (!primary)) {
|
||||
while (true) {
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
if (cpumask_empty(cpumask_of_node(next_node)))
|
||||
continue;
|
||||
break;
|
||||
@ -556,15 +603,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
|
||||
|
||||
cur_cpu = -1;
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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)))
|
||||
cpumask_clear(&primary->alloced_cpus_in_node);
|
||||
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 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)))
|
||||
cpumask_clear(&primary->alloced_cpus_in_node);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: in the case of sub-channel, we clear the sub-channel
|
||||
* related bit(s) in primary->alloced_cpus_in_node in
|
||||
* hv_process_channel_removal(), so when we reload drivers
|
||||
* like hv_netvsc in SMP guest, here we're able to re-allocate
|
||||
* bit from 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);
|
||||
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
|
||||
* hv_process_channel_removal(), so when we
|
||||
* reload drivers like hv_netvsc in SMP guest, here
|
||||
* we're able to re-allocate
|
||||
* bit from 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);
|
||||
break;
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
|
||||
union hv_connection_id conn_id;
|
||||
int ret = 0;
|
||||
int retries = 0;
|
||||
u32 msec = 1;
|
||||
u32 usec = 1;
|
||||
|
||||
conn_id.asu32 = 0;
|
||||
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
|
||||
@ -472,9 +472,9 @@ int vmbus_post_msg(void *buffer, size_t buflen)
|
||||
}
|
||||
|
||||
retries++;
|
||||
msleep(msec);
|
||||
if (msec < 2048)
|
||||
msec *= 2;
|
||||
udelay(usec);
|
||||
if (usec < 2048)
|
||||
usec *= 2;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ int hv_init(void)
|
||||
*
|
||||
* 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;
|
||||
|
||||
@ -288,7 +288,8 @@ void hv_cleanup(void)
|
||||
if (hv_context.hypercall_page) {
|
||||
hypercall_msr.as_uint64 = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -308,7 +309,8 @@ void hv_cleanup(void)
|
||||
|
||||
hypercall_msr.as_uint64 = 0;
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
@ -430,16 +430,27 @@ struct dm_info_msg {
|
||||
* currently hot added. We hot add in multiples of 128M
|
||||
* chunks; it is possible that we may not be able to bring
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct hv_hotadd_state {
|
||||
struct list_head list;
|
||||
unsigned long start_pfn;
|
||||
unsigned long covered_start_pfn;
|
||||
unsigned long covered_end_pfn;
|
||||
unsigned long ha_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 {
|
||||
@ -536,7 +547,11 @@ struct hv_dynmem_device {
|
||||
*/
|
||||
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.
|
||||
@ -560,18 +575,14 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
struct memory_notify *mem = (struct memory_notify *)v;
|
||||
unsigned long flags;
|
||||
|
||||
switch (val) {
|
||||
case MEM_GOING_ONLINE:
|
||||
mutex_lock(&dm_device.ha_region_mutex);
|
||||
break;
|
||||
|
||||
case MEM_ONLINE:
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
dm_device.num_pages_onlined += mem->nr_pages;
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
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) {
|
||||
dm_device.ha_waiting = false;
|
||||
complete(&dm_device.ol_waitevent);
|
||||
@ -579,10 +590,11 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
|
||||
break;
|
||||
|
||||
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;
|
||||
mutex_unlock(&dm_device.ha_region_mutex);
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
break;
|
||||
case MEM_GOING_ONLINE:
|
||||
case MEM_GOING_OFFLINE:
|
||||
case MEM_CANCEL_OFFLINE:
|
||||
break;
|
||||
@ -595,18 +607,46 @@ static struct notifier_block hv_memory_nb = {
|
||||
.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;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
struct page *pg;
|
||||
pg = pfn_to_page(start_pfn + i);
|
||||
__online_page_set_limits(pg);
|
||||
__online_page_increment_counters(pg);
|
||||
__online_page_free(pg);
|
||||
}
|
||||
for (i = 0; i < size; i++)
|
||||
hv_page_online_one(has, pfn_to_page(start_pfn + i));
|
||||
}
|
||||
|
||||
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 processed_pfn;
|
||||
unsigned long total_pfn = pfn_count;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < (size/HA_CHUNK); i++) {
|
||||
start_pfn = start + (i * HA_CHUNK);
|
||||
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
has->ha_end_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;
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
|
||||
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));
|
||||
ret = add_memory(nid, PFN_PHYS((start_pfn)),
|
||||
(HA_CHUNK << PAGE_SHIFT));
|
||||
@ -653,20 +696,23 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
||||
*/
|
||||
do_hot_add = false;
|
||||
}
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
has->ha_end_pfn -= HA_CHUNK;
|
||||
has->covered_end_pfn -= processed_pfn;
|
||||
mutex_lock(&dm_device.ha_region_mutex);
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the memory block to be onlined.
|
||||
* Since the hot add has succeeded, it is ok to
|
||||
* proceed even if the pages in the hot added region
|
||||
* have not been "onlined" within the allowed time.
|
||||
* Wait for the memory block to be onlined when memory onlining
|
||||
* is done outside of kernel (memhp_auto_online). Since the hot
|
||||
* add has succeeded, it is ok to proceed even if the pages in
|
||||
* the hot added region have not been "onlined" within the
|
||||
* allowed time.
|
||||
*/
|
||||
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
||||
mutex_lock(&dm_device.ha_region_mutex);
|
||||
if (dm_device.ha_waiting)
|
||||
wait_for_completion_timeout(&dm_device.ol_waitevent,
|
||||
5*HZ);
|
||||
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)
|
||||
{
|
||||
struct list_head *cur;
|
||||
struct hv_hotadd_state *has;
|
||||
unsigned long cur_start_pgp;
|
||||
unsigned long cur_end_pgp;
|
||||
unsigned long flags;
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn);
|
||||
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
list_for_each_entry(has, &dm_device.ha_region_list, list) {
|
||||
cur_start_pgp = (unsigned long)
|
||||
pfn_to_page(has->start_pfn);
|
||||
cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
|
||||
|
||||
if (((unsigned long)pg >= cur_start_pgp) &&
|
||||
((unsigned long)pg < cur_end_pgp)) {
|
||||
/*
|
||||
* This frame is currently backed; online the
|
||||
* page.
|
||||
*/
|
||||
__online_page_set_limits(pg);
|
||||
__online_page_increment_counters(pg);
|
||||
__online_page_free(pg);
|
||||
}
|
||||
/* The page belongs to a different HAS. */
|
||||
if (((unsigned long)pg < cur_start_pgp) ||
|
||||
((unsigned long)pg >= cur_end_pgp))
|
||||
continue;
|
||||
|
||||
hv_page_online_one(has, pg);
|
||||
break;
|
||||
}
|
||||
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_gap *gap;
|
||||
unsigned long residual, new_inc;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (list_empty(&dm_device.ha_region_list))
|
||||
return false;
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
list_for_each_entry(has, &dm_device.ha_region_list, list) {
|
||||
/*
|
||||
* If the pfn range we are dealing with is not in the current
|
||||
* "hot add block", move on.
|
||||
*/
|
||||
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the current start pfn is not where the covered_end
|
||||
* is, update it.
|
||||
*/
|
||||
|
||||
if (has->covered_end_pfn != start_pfn)
|
||||
has->covered_end_pfn = start_pfn;
|
||||
|
||||
return true;
|
||||
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 pfn_cnt = pg_count;
|
||||
unsigned long size;
|
||||
struct list_head *cur;
|
||||
struct hv_hotadd_state *has;
|
||||
unsigned long pgs_ol = 0;
|
||||
unsigned long old_covered_state;
|
||||
unsigned long res = 0, flags;
|
||||
|
||||
if (list_empty(&dm_device.ha_region_list))
|
||||
return 0;
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
list_for_each_entry(has, &dm_device.ha_region_list, list) {
|
||||
/*
|
||||
* If the pfn range we are dealing with is not in the current
|
||||
* "hot add block", move on.
|
||||
@ -783,6 +835,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
|
||||
if (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
|
||||
* 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 &&
|
||||
!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)) {
|
||||
@ -813,17 +865,20 @@ static unsigned long handle_pg_range(unsigned long pg_start,
|
||||
} else {
|
||||
pfn_cnt = size;
|
||||
}
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
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,
|
||||
* 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,
|
||||
@ -832,13 +887,20 @@ static unsigned long process_hot_add(unsigned long pg_start,
|
||||
unsigned long rg_size)
|
||||
{
|
||||
struct hv_hotadd_state *ha_region = NULL;
|
||||
int covered;
|
||||
unsigned long flags;
|
||||
|
||||
if (pfn_cnt == 0)
|
||||
return 0;
|
||||
|
||||
if (!dm_device.host_specified_ha_region)
|
||||
if (pfn_covered(pg_start, pfn_cnt))
|
||||
if (!dm_device.host_specified_ha_region) {
|
||||
covered = pfn_covered(pg_start, pfn_cnt);
|
||||
if (covered < 0)
|
||||
return 0;
|
||||
|
||||
if (covered)
|
||||
goto do_pg_range;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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->ha_end_pfn = rg_start;
|
||||
ha_region->covered_start_pfn = pg_start;
|
||||
ha_region->covered_end_pfn = pg_start;
|
||||
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:
|
||||
@ -882,7 +949,6 @@ static void hot_add_req(struct work_struct *dummy)
|
||||
resp.hdr.size = sizeof(struct dm_hot_add_response);
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
mutex_lock(&dm_device.ha_region_mutex);
|
||||
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
|
||||
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);
|
||||
|
||||
dm->num_pages_added += resp.page_count;
|
||||
mutex_unlock(&dm_device.ha_region_mutex);
|
||||
#endif
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct dm_status status;
|
||||
struct sysinfo val;
|
||||
unsigned long now = jiffies;
|
||||
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)))
|
||||
return;
|
||||
|
||||
si_meminfo(&val);
|
||||
memset(&status, 0, sizeof(struct dm_status));
|
||||
status.hdr.type = DM_STATUS_REPORT;
|
||||
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
|
||||
* asking us to balloon them out.
|
||||
*/
|
||||
status.num_avail = val.freeram;
|
||||
status.num_avail = si_mem_available();
|
||||
status.num_committed = vm_memory_committed() +
|
||||
dm->num_pages_ballooned +
|
||||
(dm->num_pages_added > dm->num_pages_onlined ?
|
||||
@ -1144,7 +1207,7 @@ static void balloon_up(struct work_struct *dummy)
|
||||
int ret;
|
||||
bool done = false;
|
||||
int i;
|
||||
struct sysinfo val;
|
||||
long avail_pages;
|
||||
unsigned long floor;
|
||||
|
||||
/* The host balloons pages in 2M granularity. */
|
||||
@ -1156,12 +1219,12 @@ static void balloon_up(struct work_struct *dummy)
|
||||
*/
|
||||
alloc_unit = 512;
|
||||
|
||||
si_meminfo(&val);
|
||||
avail_pages = si_mem_available();
|
||||
floor = compute_balloon_floor();
|
||||
|
||||
/* Refuse to balloon below the floor, keep the 2M granularity. */
|
||||
if (val.freeram < num_pages || val.freeram - num_pages < floor) {
|
||||
num_pages = val.freeram > floor ? (val.freeram - floor) : 0;
|
||||
if (avail_pages < num_pages || avail_pages - num_pages < floor) {
|
||||
num_pages = avail_pages > floor ? (avail_pages - floor) : 0;
|
||||
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->more_pages = 1;
|
||||
|
||||
|
||||
num_pages -= num_ballooned;
|
||||
num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
|
||||
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.config_event);
|
||||
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.ha_wrk.wrk, hot_add_req);
|
||||
dm_device.host_specified_ha_region = false;
|
||||
@ -1580,8 +1642,9 @@ static int balloon_probe(struct hv_device *dev,
|
||||
static int balloon_remove(struct hv_device *dev)
|
||||
{
|
||||
struct hv_dynmem_device *dm = hv_get_drvdata(dev);
|
||||
struct list_head *cur, *tmp;
|
||||
struct hv_hotadd_state *has;
|
||||
struct hv_hotadd_state *has, *tmp;
|
||||
struct hv_hotadd_gap *gap, *tmp_gap;
|
||||
unsigned long flags;
|
||||
|
||||
if (dm->num_pages_ballooned != 0)
|
||||
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);
|
||||
unregister_memory_notifier(&hv_memory_nb);
|
||||
#endif
|
||||
list_for_each_safe(cur, tmp, &dm->ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
spin_lock_irqsave(&dm_device.ha_lock, flags);
|
||||
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);
|
||||
kfree(has);
|
||||
}
|
||||
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -83,6 +83,12 @@ static void fcopy_timeout_func(struct work_struct *dummy)
|
||||
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)
|
||||
{
|
||||
u32 our_ver = FCOPY_CURRENT_VERSION;
|
||||
@ -94,7 +100,8 @@ static int fcopy_handle_handshake(u32 version)
|
||||
break;
|
||||
case FCOPY_VERSION_1:
|
||||
/* 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;
|
||||
dm_reg_value = version;
|
||||
break;
|
||||
@ -107,8 +114,7 @@ static int fcopy_handle_handshake(u32 version)
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_debug("FCP: userspace daemon ver. %d registered\n", version);
|
||||
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
|
||||
pr_debug("FCP: userspace daemon ver. %d connected\n", version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -161,7 +167,7 @@ static void fcopy_send_data(struct work_struct *dummy)
|
||||
}
|
||||
|
||||
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) {
|
||||
pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
|
||||
|
@ -102,6 +102,17 @@ static void kvp_poll_wrapper(void *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
|
||||
kvp_register(int reg_value)
|
||||
{
|
||||
@ -116,7 +127,8 @@ kvp_register(int reg_value)
|
||||
kvp_msg->kvp_hdr.operation = reg_value;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -158,17 +170,10 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
|
||||
/*
|
||||
* We have a compatible daemon; complete the handshake.
|
||||
*/
|
||||
pr_debug("KVP: userspace daemon ver. %d registered\n",
|
||||
KVP_OP_REGISTER);
|
||||
pr_debug("KVP: userspace daemon ver. %d connected\n",
|
||||
msg->kvp_hdr.operation);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -455,7 +460,7 @@ kvp_send_key(struct work_struct *dummy)
|
||||
}
|
||||
|
||||
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) {
|
||||
pr_debug("KVP: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&kvp_timeout_work)) {
|
||||
|
@ -67,11 +67,11 @@ static const char vss_devname[] = "vmbus/hv_vss";
|
||||
static __u8 *recv_buffer;
|
||||
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_handle_request(struct work_struct *dummy);
|
||||
|
||||
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)
|
||||
{
|
||||
@ -95,6 +95,12 @@ static void vss_timeout_func(struct work_struct *dummy)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
break;
|
||||
case VSS_OP_REGISTER1:
|
||||
/* Daemon expects us to reply with our own version*/
|
||||
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver)))
|
||||
/* Daemon expects us to reply with our own version */
|
||||
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
|
||||
vss_register_done))
|
||||
return -EFAULT;
|
||||
dm_reg_value = VSS_OP_REGISTER1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
|
||||
pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value);
|
||||
pr_debug("VSS: userspace daemon ver. %d connected\n", dm_reg_value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -136,6 +142,11 @@ static int vss_on_msg(void *msg, int len)
|
||||
return vss_handle_handshake(vss_msg);
|
||||
} else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) {
|
||||
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)) {
|
||||
vss_respond_to_host(vss_msg->error);
|
||||
/* Transaction is finished, reset the state. */
|
||||
@ -150,8 +161,7 @@ static int vss_on_msg(void *msg, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void vss_send_op(struct work_struct *dummy)
|
||||
static void vss_send_op(void)
|
||||
{
|
||||
int op = vss_transaction.msg->vss_hdr.operation;
|
||||
int rc;
|
||||
@ -168,7 +178,10 @@ static void vss_send_op(struct work_struct *dummy)
|
||||
vss_msg->vss_hdr.operation = op;
|
||||
|
||||
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) {
|
||||
pr_warn("VSS: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&vss_timeout_work)) {
|
||||
@ -182,6 +195,38 @@ static void vss_send_op(struct work_struct *dummy)
|
||||
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.
|
||||
*/
|
||||
@ -266,48 +311,8 @@ void hv_vss_onchannelcallback(void *context)
|
||||
vss_transaction.recv_req_id = requestid;
|
||||
vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
|
||||
|
||||
switch (vss_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_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;
|
||||
|
||||
}
|
||||
|
||||
schedule_work(&vss_handle_request_work);
|
||||
return;
|
||||
}
|
||||
|
||||
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
|
||||
@ -358,6 +363,6 @@ void hv_vss_deinit(void)
|
||||
{
|
||||
vss_transaction.state = HVUTIL_DEVICE_DYING;
|
||||
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);
|
||||
}
|
||||
|
@ -34,22 +34,25 @@
|
||||
#define SD_MINOR 0
|
||||
#define SD_VERSION (SD_MAJOR << 16 | SD_MINOR)
|
||||
|
||||
#define SD_WS2008_MAJOR 1
|
||||
#define SD_WS2008_VERSION (SD_WS2008_MAJOR << 16 | SD_MINOR)
|
||||
#define SD_MAJOR_1 1
|
||||
#define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR)
|
||||
|
||||
#define TS_MAJOR 3
|
||||
#define TS_MAJOR 4
|
||||
#define TS_MINOR 0
|
||||
#define TS_VERSION (TS_MAJOR << 16 | TS_MINOR)
|
||||
|
||||
#define TS_WS2008_MAJOR 1
|
||||
#define TS_WS2008_VERSION (TS_WS2008_MAJOR << 16 | TS_MINOR)
|
||||
#define TS_MAJOR_1 1
|
||||
#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_MINOR 0
|
||||
#define HB_MINOR 0
|
||||
#define HB_VERSION (HB_MAJOR << 16 | HB_MINOR)
|
||||
|
||||
#define HB_WS2008_MAJOR 1
|
||||
#define HB_WS2008_VERSION (HB_WS2008_MAJOR << 16 | HB_MINOR)
|
||||
#define HB_MAJOR_1 1
|
||||
#define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR)
|
||||
|
||||
static int sd_srv_version;
|
||||
static int ts_srv_version;
|
||||
@ -61,9 +64,14 @@ static struct hv_util_service util_shutdown = {
|
||||
.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 struct hv_util_service util_timesynch = {
|
||||
.util_cb = timesync_onchannelcallback,
|
||||
.util_init = hv_timesync_init,
|
||||
.util_deinit = hv_timesync_deinit,
|
||||
};
|
||||
|
||||
static void heartbeat_onchannelcallback(void *context);
|
||||
@ -160,20 +168,6 @@ static void shutdown_onchannelcallback(void *context)
|
||||
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.
|
||||
*/
|
||||
@ -181,15 +175,37 @@ static inline void do_adj_guesttime(u64 hosttime)
|
||||
struct adj_time_work {
|
||||
struct work_struct work;
|
||||
u64 host_time;
|
||||
u64 ref_time;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
static void hv_set_host_time(struct work_struct *work)
|
||||
{
|
||||
struct adj_time_work *wrk;
|
||||
s64 host_tns;
|
||||
u64 newtime;
|
||||
struct timespec host_ts;
|
||||
|
||||
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.
|
||||
* 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
|
||||
* loaded after hv_vmbus, the first message is usually missed. The other
|
||||
* thing is, systime is automatically set to emulated hardware clock which may
|
||||
* 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.
|
||||
* loaded after hv_vmbus, the first message is usually missed. This bit is
|
||||
* considered a hard request to discipline the clock.
|
||||
*
|
||||
* 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;
|
||||
|
||||
wrk->host_time = hosttime;
|
||||
if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
|
||||
INIT_WORK(&wrk->work, hv_set_host_time);
|
||||
schedule_work(&wrk->work);
|
||||
return;
|
||||
wrk.host_time = hosttime;
|
||||
wrk.ref_time = reftime;
|
||||
wrk.flags = flags;
|
||||
if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) {
|
||||
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;
|
||||
struct icmsg_hdr *icmsghdrp;
|
||||
struct ictimesync_data *timedatap;
|
||||
struct ictimesync_ref_data *refdata;
|
||||
u8 *time_txf_buf = util_timesynch.recv_buffer;
|
||||
struct icmsg_negotiate *negop = NULL;
|
||||
|
||||
@ -252,11 +267,27 @@ static void timesync_onchannelcallback(void *context)
|
||||
time_txf_buf,
|
||||
util_fw_version,
|
||||
ts_srv_version);
|
||||
pr_info("Using TimeSync version %d.%d\n",
|
||||
ts_srv_version >> 16, ts_srv_version & 0xFFFF);
|
||||
} else {
|
||||
timedatap = (struct ictimesync_data *)&time_txf_buf[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
adj_guesttime(timedatap->parenttime, timedatap->flags);
|
||||
if (ts_srv_version > TS_VERSION_3) {
|
||||
refdata = (struct ictimesync_ref_data *)
|
||||
&time_txf_buf[
|
||||
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
|
||||
@ -350,16 +381,21 @@ static int util_probe(struct hv_device *dev,
|
||||
switch (vmbus_proto_version) {
|
||||
case (VERSION_WS2008):
|
||||
util_fw_version = UTIL_WS2K8_FW_VERSION;
|
||||
sd_srv_version = SD_WS2008_VERSION;
|
||||
ts_srv_version = TS_WS2008_VERSION;
|
||||
hb_srv_version = HB_WS2008_VERSION;
|
||||
sd_srv_version = SD_VERSION_1;
|
||||
ts_srv_version = TS_VERSION_1;
|
||||
hb_srv_version = HB_VERSION_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
case(VERSION_WIN10):
|
||||
util_fw_version = UTIL_FW_VERSION;
|
||||
sd_srv_version = SD_VERSION;
|
||||
ts_srv_version = TS_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,
|
||||
@ -427,6 +463,17 @@ static struct hv_driver util_drv = {
|
||||
.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)
|
||||
{
|
||||
pr_info("Registering HyperV Utility Driver\n");
|
||||
|
@ -72,6 +72,10 @@ static ssize_t hvt_op_read(struct file *file, char __user *buf,
|
||||
hvt->outmsg = NULL;
|
||||
hvt->outmsg_len = 0;
|
||||
|
||||
if (hvt->on_read)
|
||||
hvt->on_read();
|
||||
hvt->on_read = NULL;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&hvt->lock);
|
||||
return ret;
|
||||
@ -219,7 +223,8 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
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;
|
||||
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);
|
||||
ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC);
|
||||
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;
|
||||
}
|
||||
/* HVUTIL_TRANSPORT_CHARDEV */
|
||||
@ -255,6 +267,7 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
|
||||
if (hvt->outmsg) {
|
||||
memcpy(hvt->outmsg, msg, len);
|
||||
hvt->outmsg_len = len;
|
||||
hvt->on_read = on_read_cb;
|
||||
wake_up_interruptible(&hvt->outmsg_q);
|
||||
} else
|
||||
ret = -ENOMEM;
|
||||
|
@ -36,6 +36,7 @@ struct hvutil_transport {
|
||||
struct list_head list; /* hvt_list */
|
||||
int (*on_msg)(void *, int); /* callback on new user message */
|
||||
void (*on_reset)(void); /* callback when userspace drops */
|
||||
void (*on_read)(void); /* callback on message read */
|
||||
u8 *outmsg; /* message to the userspace */
|
||||
int outmsg_len; /* its length */
|
||||
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,
|
||||
int (*on_msg)(void *, int),
|
||||
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);
|
||||
|
||||
#endif /* _HV_UTILS_TRANSPORT_H */
|
||||
|
@ -495,7 +495,7 @@ struct hv_ring_buffer_debug_info {
|
||||
|
||||
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,
|
||||
enum hv_message_type message_type,
|
||||
@ -522,14 +522,15 @@ extern unsigned int host_info_edx;
|
||||
/* Interface */
|
||||
|
||||
|
||||
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer,
|
||||
u32 buflen);
|
||||
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
|
||||
struct page *pages, u32 pagecnt);
|
||||
|
||||
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
|
||||
|
||||
int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
|
||||
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,
|
||||
void *buffer, u32 buflen, u32 *buffer_actual_len,
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
@ -66,12 +68,20 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
|
||||
* 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();
|
||||
if (READ_ONCE(rbi->ring_buffer->interrupt_mask))
|
||||
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 */
|
||||
virt_rmb();
|
||||
/*
|
||||
@ -162,18 +172,7 @@ static u32 hv_copyfrom_ringbuffer(
|
||||
void *ring_buffer = hv_get_ring_buffer(ring_info);
|
||||
u32 ring_buffer_size = hv_get_ring_buffersize(ring_info);
|
||||
|
||||
u32 frag_len;
|
||||
|
||||
/* 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);
|
||||
|
||||
memcpy(dest, ring_buffer + start_read_offset, destlen);
|
||||
|
||||
start_read_offset += destlen;
|
||||
start_read_offset %= ring_buffer_size;
|
||||
@ -194,15 +193,8 @@ static u32 hv_copyto_ringbuffer(
|
||||
{
|
||||
void *ring_buffer = hv_get_ring_buffer(ring_info);
|
||||
u32 ring_buffer_size = hv_get_ring_buffersize(ring_info);
|
||||
u32 frag_len;
|
||||
|
||||
/* wrap-around detected! */
|
||||
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);
|
||||
memcpy(ring_buffer + start_write_offset, src, srclen);
|
||||
|
||||
start_write_offset += srclen;
|
||||
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. */
|
||||
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)
|
||||
return -EINVAL;
|
||||
int i;
|
||||
struct page **pages_wraparound;
|
||||
|
||||
BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE));
|
||||
|
||||
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->write_index = 0;
|
||||
|
||||
/* Set the feature bit for enabling flow control. */
|
||||
ring_info->ring_buffer->feature_bits.value = 1;
|
||||
|
||||
ring_info->ring_size = buflen;
|
||||
ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer);
|
||||
ring_info->ring_size = page_cnt << PAGE_SHIFT;
|
||||
ring_info->ring_datasize = ring_info->ring_size -
|
||||
sizeof(struct hv_ring_buffer);
|
||||
|
||||
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. */
|
||||
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
|
||||
{
|
||||
vunmap(ring_info->ring_buffer);
|
||||
}
|
||||
|
||||
/* Write to the ring buffer. */
|
||||
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;
|
||||
u32 bytes_avail_towrite;
|
||||
@ -326,7 +344,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
|
||||
if (lock)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -105,8 +105,8 @@ static struct notifier_block hyperv_panic_block = {
|
||||
|
||||
static const char *fb_mmio_name = "fb_range";
|
||||
static struct resource *fb_mmio;
|
||||
struct resource *hyperv_mmio;
|
||||
DEFINE_SEMAPHORE(hyperv_mmio_lock);
|
||||
static struct resource *hyperv_mmio;
|
||||
static DEFINE_SEMAPHORE(hyperv_mmio_lock);
|
||||
|
||||
static int vmbus_exists(void)
|
||||
{
|
||||
@ -874,7 +874,7 @@ static int vmbus_bus_init(void)
|
||||
bus_unregister(&hv_bus);
|
||||
|
||||
err_cleanup:
|
||||
hv_cleanup();
|
||||
hv_cleanup(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -961,8 +961,8 @@ int vmbus_device_register(struct hv_device *child_device_obj)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
dev_set_name(&child_device_obj->device, "vmbus_%d",
|
||||
child_device_obj->channel->id);
|
||||
dev_set_name(&child_device_obj->device, "vmbus-%pUl",
|
||||
child_device_obj->channel->offermsg.offer.if_instance.b);
|
||||
|
||||
child_device_obj->device.bus = &hv_bus;
|
||||
child_device_obj->device.parent = &hv_acpi_dev->dev;
|
||||
@ -1326,7 +1326,7 @@ static void hv_kexec_handler(void)
|
||||
vmbus_initiate_unload(false);
|
||||
for_each_online_cpu(cpu)
|
||||
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
|
||||
hv_cleanup();
|
||||
hv_cleanup(false);
|
||||
};
|
||||
|
||||
static void hv_crash_handler(struct pt_regs *regs)
|
||||
@ -1338,7 +1338,7 @@ static void hv_crash_handler(struct pt_regs *regs)
|
||||
* for kdump.
|
||||
*/
|
||||
hv_synic_cleanup(NULL);
|
||||
hv_cleanup();
|
||||
hv_cleanup(true);
|
||||
};
|
||||
|
||||
static int __init hv_acpi_init(void)
|
||||
@ -1398,7 +1398,7 @@ static void __exit vmbus_exit(void)
|
||||
&hyperv_panic_block);
|
||||
}
|
||||
bus_unregister(&hv_bus);
|
||||
hv_cleanup();
|
||||
hv_cleanup(false);
|
||||
for_each_online_cpu(cpu) {
|
||||
tasklet_kill(hv_context.event_dpc[cpu]);
|
||||
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
|
||||
|
@ -184,8 +184,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata)
|
||||
|
||||
if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
ETB_FFCR);
|
||||
"timeout while waiting for completion of Manual Flush\n");
|
||||
}
|
||||
|
||||
/* 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)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
ETB_FFCR);
|
||||
"timeout while waiting for Formatter to Stop\n");
|
||||
}
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
@ -561,7 +559,7 @@ static const struct file_operations etb_fops = {
|
||||
};
|
||||
|
||||
#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(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 etb_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
@ -684,17 +682,13 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
desc->ops = &etb_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->groups = coresight_etb_groups;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
desc.type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
desc.ops = &etb_cs_ops;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
desc.groups = coresight_etb_groups;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "coresight-etm-perf.h"
|
||||
#include "coresight-priv.h"
|
||||
|
||||
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 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)
|
||||
return -ENOENT;
|
||||
struct etm_filters *filters;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int cpu;
|
||||
@ -100,7 +135,7 @@ static void free_event_data(struct work_struct *work)
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
@ -185,7 +220,7 @@ static void *etm_setup_aux(int event_cpu, void **pages,
|
||||
* referenced later when the path is actually needed.
|
||||
*/
|
||||
event_data->path[cpu] = coresight_build_path(csdev);
|
||||
if (!event_data->path[cpu])
|
||||
if (IS_ERR(event_data->path[cpu]))
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -258,7 +293,7 @@ static void etm_event_start(struct perf_event *event, int flags)
|
||||
event->hw.state = 0;
|
||||
|
||||
/* 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;
|
||||
|
||||
out:
|
||||
@ -291,7 +326,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
|
||||
return;
|
||||
|
||||
/* stop tracer */
|
||||
source_ops(csdev)->disable(csdev);
|
||||
source_ops(csdev)->disable(csdev, event);
|
||||
|
||||
/* tell the core */
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char entry[sizeof("cpu9999999")];
|
||||
@ -371,18 +487,21 @@ static int __init etm_perf_init(void)
|
||||
{
|
||||
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.task_ctx_nr = perf_sw_context;
|
||||
etm_pmu.read = etm_event_read;
|
||||
etm_pmu.event_init = etm_event_init;
|
||||
etm_pmu.setup_aux = etm_setup_aux;
|
||||
etm_pmu.free_aux = etm_free_aux;
|
||||
etm_pmu.start = etm_event_start;
|
||||
etm_pmu.stop = etm_event_stop;
|
||||
etm_pmu.add = etm_event_add;
|
||||
etm_pmu.del = etm_event_del;
|
||||
etm_pmu.attr_groups = etm_pmu_attr_groups;
|
||||
etm_pmu.task_ctx_nr = perf_sw_context;
|
||||
etm_pmu.read = etm_event_read;
|
||||
etm_pmu.event_init = etm_event_init;
|
||||
etm_pmu.setup_aux = etm_setup_aux;
|
||||
etm_pmu.free_aux = etm_free_aux;
|
||||
etm_pmu.start = etm_event_start;
|
||||
etm_pmu.stop = etm_event_stop;
|
||||
etm_pmu.add = etm_event_add;
|
||||
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);
|
||||
if (ret == 0)
|
||||
|
@ -18,8 +18,42 @@
|
||||
#ifndef _CORESIGHT_ETM_PERF_H
|
||||
#define _CORESIGHT_ETM_PERF_H
|
||||
|
||||
#include "coresight-priv.h"
|
||||
|
||||
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
|
||||
int etm_perf_symlink(struct coresight_device *csdev, bool link);
|
||||
|
||||
|
@ -259,14 +259,6 @@ struct etm_drvdata {
|
||||
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,
|
||||
u32 val, u32 off)
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include "coresight-etm.h"
|
||||
#include "coresight-priv.h"
|
||||
|
||||
static ssize_t nr_addr_cmp_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -1222,7 +1223,7 @@ static struct attribute *coresight_etm_attrs[] = {
|
||||
};
|
||||
|
||||
#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(etmccer, ETMCCER);
|
||||
|
@ -311,9 +311,10 @@ void etm_config_trace_mode(struct etm_config *config)
|
||||
#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
|
||||
|
||||
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 perf_event_attr *attr = &event->attr;
|
||||
|
||||
if (!attr)
|
||||
return -EINVAL;
|
||||
@ -459,7 +460,7 @@ static int etm_trace_id(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);
|
||||
|
||||
@ -467,7 +468,7 @@ static int etm_enable_perf(struct coresight_device *csdev,
|
||||
return -EINVAL;
|
||||
|
||||
/* Configure the tracer based on the session's specifics */
|
||||
etm_parse_event_config(drvdata, attr);
|
||||
etm_parse_event_config(drvdata, event);
|
||||
/* And enable it */
|
||||
etm_enable_hw(drvdata);
|
||||
|
||||
@ -504,7 +505,7 @@ static int etm_enable_sysfs(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;
|
||||
u32 val;
|
||||
@ -521,7 +522,7 @@ static int etm_enable(struct coresight_device *csdev,
|
||||
ret = etm_enable_sysfs(csdev);
|
||||
break;
|
||||
case CS_MODE_PERF:
|
||||
ret = etm_enable_perf(csdev, attr);
|
||||
ret = etm_enable_perf(csdev, event);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@ -601,7 +602,8 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
|
||||
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;
|
||||
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 etm_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
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);
|
||||
if (!drvdata)
|
||||
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_set_default(&drvdata->config);
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
|
||||
desc->ops = &etm_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->groups = coresight_etm_groups;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
|
||||
desc.ops = &etm_cs_ops;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
desc.groups = coresight_etm_groups;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto err_arch_supported;
|
||||
@ -893,6 +891,11 @@ static struct amba_id etm_ids[] = {
|
||||
.mask = 0x0003ffff,
|
||||
.data = "ETM 3.3",
|
||||
},
|
||||
{ /* ETM 3.5 - Cortex-A5 */
|
||||
.id = 0x0003b955,
|
||||
.mask = 0x0003ffff,
|
||||
.data = "ETM 3.5",
|
||||
},
|
||||
{ /* ETM 3.5 */
|
||||
.id = 0x0003b956,
|
||||
.mask = 0x0003ffff,
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include "coresight-etm4x.h"
|
||||
#include "coresight-priv.h"
|
||||
|
||||
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude)
|
||||
{
|
||||
@ -2039,15 +2040,42 @@ static struct attribute *coresight_etmv4_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define coresight_etm4x_simple_func(name, offset) \
|
||||
coresight_simple_func(struct etmv4_drvdata, name, offset)
|
||||
struct etmv4_reg {
|
||||
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, ®, 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(trcpdsr, TRCPDSR);
|
||||
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(trcdevid, TRCDEVID);
|
||||
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(trcpidr2, TRCPIDR2);
|
||||
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[] = {
|
||||
&dev_attr_trcoslsr.attr,
|
||||
@ -2073,19 +2104,19 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
coresight_etm4x_simple_func(trcidr0, TRCIDR0);
|
||||
coresight_etm4x_simple_func(trcidr1, TRCIDR1);
|
||||
coresight_etm4x_simple_func(trcidr2, TRCIDR2);
|
||||
coresight_etm4x_simple_func(trcidr3, TRCIDR3);
|
||||
coresight_etm4x_simple_func(trcidr4, TRCIDR4);
|
||||
coresight_etm4x_simple_func(trcidr5, TRCIDR5);
|
||||
coresight_etm4x_cross_read(trcidr0, TRCIDR0);
|
||||
coresight_etm4x_cross_read(trcidr1, TRCIDR1);
|
||||
coresight_etm4x_cross_read(trcidr2, TRCIDR2);
|
||||
coresight_etm4x_cross_read(trcidr3, TRCIDR3);
|
||||
coresight_etm4x_cross_read(trcidr4, TRCIDR4);
|
||||
coresight_etm4x_cross_read(trcidr5, TRCIDR5);
|
||||
/* trcidr[6,7] are reserved */
|
||||
coresight_etm4x_simple_func(trcidr8, TRCIDR8);
|
||||
coresight_etm4x_simple_func(trcidr9, TRCIDR9);
|
||||
coresight_etm4x_simple_func(trcidr10, TRCIDR10);
|
||||
coresight_etm4x_simple_func(trcidr11, TRCIDR11);
|
||||
coresight_etm4x_simple_func(trcidr12, TRCIDR12);
|
||||
coresight_etm4x_simple_func(trcidr13, TRCIDR13);
|
||||
coresight_etm4x_cross_read(trcidr8, TRCIDR8);
|
||||
coresight_etm4x_cross_read(trcidr9, TRCIDR9);
|
||||
coresight_etm4x_cross_read(trcidr10, TRCIDR10);
|
||||
coresight_etm4x_cross_read(trcidr11, TRCIDR11);
|
||||
coresight_etm4x_cross_read(trcidr12, TRCIDR12);
|
||||
coresight_etm4x_cross_read(trcidr13, TRCIDR13);
|
||||
|
||||
static struct attribute *coresight_etmv4_trcidr_attrs[] = {
|
||||
&dev_attr_trcidr0.attr,
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <asm/sections.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 */
|
||||
static int etm4_count;
|
||||
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;
|
||||
|
||||
@ -79,22 +80,8 @@ static int etm4_cpu_id(struct coresight_device *csdev)
|
||||
static int etm4_trace_id(struct coresight_device *csdev)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
return drvdata->trcid;
|
||||
}
|
||||
|
||||
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 */
|
||||
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1))
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
TRCSTATR);
|
||||
"timeout while waiting for Idle Trace Status\n");
|
||||
|
||||
writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR);
|
||||
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_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 */
|
||||
writel_relaxed(1, drvdata->base + TRCPRGCTLR);
|
||||
|
||||
/* wait for TRCSTATR.IDLE to go back down to '0' */
|
||||
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0))
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
TRCSTATR);
|
||||
"timeout while waiting for Idle Trace Status\n");
|
||||
|
||||
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,
|
||||
struct perf_event_attr *attr)
|
||||
struct perf_event *event)
|
||||
{
|
||||
int ret = 0;
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
struct perf_event_attr *attr = &event->attr;
|
||||
|
||||
if (!attr)
|
||||
return -EINVAL;
|
||||
if (!attr) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Clear configuration from previous run */
|
||||
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;
|
||||
|
||||
/* Always start from the default config */
|
||||
etm4_set_default(config);
|
||||
etm4_set_default_config(config);
|
||||
|
||||
/*
|
||||
* By default the tracers are configured to trace the whole address
|
||||
* range. Narrow the field only if requested by user space.
|
||||
*/
|
||||
if (config->mode)
|
||||
etm4_config_trace_mode(config);
|
||||
/* Configure filters specified on the perf cmd line, if any. */
|
||||
ret = etm4_set_event_filters(drvdata, event);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Go from generic option to ETMv4 specifics */
|
||||
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))
|
||||
config->cfg |= ETMv4_MODE_TIMESTAMP;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
etm4_enable_hw(drvdata);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int etm4_enable_sysfs(struct coresight_device *csdev)
|
||||
@ -274,7 +275,7 @@ static int etm4_enable_sysfs(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;
|
||||
u32 val;
|
||||
@ -291,7 +292,7 @@ static int etm4_enable(struct coresight_device *csdev,
|
||||
ret = etm4_enable_sysfs(csdev);
|
||||
break;
|
||||
case CS_MODE_PERF:
|
||||
ret = etm4_enable_perf(csdev, attr);
|
||||
ret = etm4_enable_perf(csdev, event);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@ -311,6 +312,11 @@ static void etm4_disable_hw(void *info)
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -362,7 +382,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
|
||||
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;
|
||||
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);
|
||||
break;
|
||||
case CS_MODE_PERF:
|
||||
etm4_disable_perf(csdev);
|
||||
etm4_disable_perf(csdev, event);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -564,21 +585,8 @@ static void etm4_init_arch_data(void *info)
|
||||
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 */
|
||||
config->eventctrl0 = 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 */
|
||||
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
|
||||
@ -601,45 +711,158 @@ static void etm4_set_default(struct etmv4_config *config)
|
||||
*/
|
||||
config->vinst_ctrl |= BIT(9);
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
/* No start-stop filtering for ViewInst */
|
||||
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)
|
||||
{
|
||||
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 etmv4_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
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);
|
||||
if (!drvdata)
|
||||
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_set_default(&drvdata->config);
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
|
||||
desc->ops = &etm4_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->groups = coresight_etmv4_groups;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
|
||||
desc.ops = &etm4_cs_ops;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
desc.groups = coresight_etmv4_groups;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto err_arch_supported;
|
||||
@ -826,12 +1045,12 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
}
|
||||
|
||||
static struct amba_id etm4_ids[] = {
|
||||
{ /* ETM 4.0 - Qualcomm */
|
||||
.id = 0x0003b95d,
|
||||
.mask = 0x0003ffff,
|
||||
{ /* ETM 4.0 - Cortex-A53 */
|
||||
.id = 0x000bb95d,
|
||||
.mask = 0x000fffff,
|
||||
.data = "ETM 4.0",
|
||||
},
|
||||
{ /* ETM 4.0 - Juno board */
|
||||
{ /* ETM 4.0 - Cortex-A57 */
|
||||
.id = 0x000bb95e,
|
||||
.mask = 0x000fffff,
|
||||
.data = "ETM 4.0",
|
||||
|
@ -183,6 +183,9 @@
|
||||
#define TRCSTATR_IDLE_BIT 0
|
||||
#define ETM_DEFAULT_ADDR_COMP 0
|
||||
|
||||
/* PowerDown Control Register bits */
|
||||
#define TRCPDCR_PU BIT(3)
|
||||
|
||||
/* secure state access levels */
|
||||
#define ETM_EXLEVEL_S_APP BIT(8)
|
||||
#define ETM_EXLEVEL_S_OS BIT(9)
|
||||
@ -407,14 +410,6 @@ enum etm_addr_ctxtype {
|
||||
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[];
|
||||
void etm4_config_trace_mode(struct etmv4_config *config);
|
||||
#endif
|
||||
|
@ -176,7 +176,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct funnel_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
@ -207,17 +207,13 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
drvdata->base = base;
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
|
||||
desc->ops = &funnel_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->groups = coresight_funnel_groups;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
desc.type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
|
||||
desc.ops = &funnel_cs_ops;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
desc.groups = coresight_funnel_groups;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/*
|
||||
* Coresight management registers (0xf00-0xfcc)
|
||||
@ -37,16 +38,32 @@
|
||||
#define ETM_MODE_EXCL_KERN BIT(30)
|
||||
#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, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
type *drvdata = dev_get_drvdata(_dev->parent); \
|
||||
return scnprintf(buf, PAGE_SIZE, "0x%x\n", \
|
||||
readl_relaxed(drvdata->base + offset)); \
|
||||
coresight_read_fn fn = func; \
|
||||
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)
|
||||
|
||||
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 {
|
||||
CS_MODE_DISABLED,
|
||||
CS_MODE_SYSFS,
|
||||
|
@ -102,7 +102,7 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct replicator_state *drvdata;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
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);
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
desc->ops = &replicator_cs_ops;
|
||||
desc->pdata = adev->dev.platform_data;
|
||||
desc->dev = &adev->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 = adev->dev.platform_data;
|
||||
desc.dev = &adev->dev;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
|
@ -69,7 +69,7 @@ static int replicator_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct replicator_drvdata *drvdata;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
@ -95,18 +95,12 @@ static int replicator_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc) {
|
||||
ret = -ENOMEM;
|
||||
goto out_disable_pm;
|
||||
}
|
||||
|
||||
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);
|
||||
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)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto out_disable_pm;
|
||||
|
@ -105,10 +105,12 @@ module_param_named(
|
||||
/**
|
||||
* struct channel_space - central management entity for extended ports
|
||||
* @base: memory mapped base address where channels start.
|
||||
* @phys: physical base address of channel region.
|
||||
* @guaraneed: is the channel delivery guaranteed.
|
||||
*/
|
||||
struct channel_space {
|
||||
void __iomem *base;
|
||||
phys_addr_t phys;
|
||||
unsigned long *guaranteed;
|
||||
};
|
||||
|
||||
@ -196,7 +198,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
|
||||
}
|
||||
|
||||
static int stm_enable(struct coresight_device *csdev,
|
||||
struct perf_event_attr *attr, u32 mode)
|
||||
struct perf_event *event, u32 mode)
|
||||
{
|
||||
u32 val;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -353,7 +356,24 @@ static void stm_generic_unlink(struct stm_data *stm_data,
|
||||
if (!drvdata || !drvdata->csdev)
|
||||
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,
|
||||
@ -616,7 +636,7 @@ static ssize_t traceid_store(struct device *dev,
|
||||
static DEVICE_ATTR_RW(traceid);
|
||||
|
||||
#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(tsfreqr, STMTSFREQR);
|
||||
@ -761,7 +781,9 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata)
|
||||
drvdata->stm.sw_end = 1;
|
||||
drvdata->stm.hw_override = true;
|
||||
drvdata->stm.sw_nchannels = drvdata->numsp;
|
||||
drvdata->stm.sw_mmiosz = BYTES_PER_CHANNEL;
|
||||
drvdata->stm.packet = stm_generic_packet;
|
||||
drvdata->stm.mmio_addr = stm_mmio_addr;
|
||||
drvdata->stm.link = stm_generic_link;
|
||||
drvdata->stm.unlink = stm_generic_unlink;
|
||||
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 ch_res;
|
||||
size_t res_size, bitmap_size;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
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);
|
||||
if (ret)
|
||||
return ret;
|
||||
drvdata->chs.phys = ch_res.start;
|
||||
|
||||
base = devm_ioremap_resource(dev, &ch_res);
|
||||
if (IS_ERR(base))
|
||||
@ -843,19 +866,13 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc) {
|
||||
ret = -ENOMEM;
|
||||
goto stm_unregister;
|
||||
}
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
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);
|
||||
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
|
||||
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)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto stm_unregister;
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "coresight-priv.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);
|
||||
|
||||
@ -48,6 +48,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
|
||||
int i;
|
||||
|
||||
bufp = drvdata->buf;
|
||||
drvdata->len = 0;
|
||||
while (1) {
|
||||
for (i = 0; i < drvdata->memwidth; i++) {
|
||||
read_data = readl_relaxed(drvdata->base + TMC_RRD);
|
||||
@ -55,6 +56,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
|
||||
return;
|
||||
memcpy(bufp, &read_data, 4);
|
||||
bufp += 4;
|
||||
drvdata->len += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,7 +168,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Free memory outside the spinlock if need be */
|
||||
if (!used && buf)
|
||||
if (!used)
|
||||
kfree(buf);
|
||||
|
||||
if (!ret)
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "coresight-priv.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;
|
||||
|
||||
@ -64,11 +64,17 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
|
||||
rwp = readl_relaxed(drvdata->base + TMC_RWP);
|
||||
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;
|
||||
else
|
||||
drvdata->len = drvdata->size;
|
||||
} else {
|
||||
drvdata->buf = drvdata->vaddr;
|
||||
drvdata->len = rwp - drvdata->paddr;
|
||||
}
|
||||
}
|
||||
|
||||
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
|
||||
|
@ -38,8 +38,7 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
|
||||
if (coresight_timeout(drvdata->base,
|
||||
TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
TMC_STS);
|
||||
"timeout while waiting for TMC to be Ready\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,8 +55,7 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
|
||||
if (coresight_timeout(drvdata->base,
|
||||
TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) {
|
||||
dev_err(drvdata->dev,
|
||||
"timeout observed when probing at offset %#x\n",
|
||||
TMC_FFCR);
|
||||
"timeout while waiting for completion of Manual Flush\n");
|
||||
}
|
||||
|
||||
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);
|
||||
char *bufp = drvdata->buf + *ppos;
|
||||
|
||||
if (*ppos + len > drvdata->size)
|
||||
len = drvdata->size - *ppos;
|
||||
if (*ppos + len > drvdata->len)
|
||||
len = drvdata->len - *ppos;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -220,7 +218,7 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid)
|
||||
}
|
||||
|
||||
#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(sts, TMC_STS);
|
||||
@ -249,8 +247,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
ssize_t trigger_cntr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t trigger_cntr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
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 tmc_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
pdata = of_get_coresight_platform_data(dev, np);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
if (IS_ERR(pdata)) {
|
||||
ret = PTR_ERR(pdata);
|
||||
goto out;
|
||||
}
|
||||
adev->dev.platform_data = pdata;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
goto out;
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_devm_kzalloc;
|
||||
}
|
||||
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
desc->groups = coresight_tmc_groups;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
desc.groups = coresight_tmc_groups;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->ops = &tmc_etb_cs_ops;
|
||||
desc.type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
desc.ops = &tmc_etb_cs_ops;
|
||||
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->ops = &tmc_etr_cs_ops;
|
||||
desc.type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
|
||||
desc.ops = &tmc_etr_cs_ops;
|
||||
} else {
|
||||
desc->type = CORESIGHT_DEV_TYPE_LINKSINK;
|
||||
desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
|
||||
desc->ops = &tmc_etf_cs_ops;
|
||||
desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
|
||||
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
|
||||
desc.ops = &tmc_etf_cs_ops;
|
||||
}
|
||||
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev)) {
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
goto err_devm_kzalloc;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
ret = misc_register(&drvdata->miscdev);
|
||||
if (ret)
|
||||
goto err_misc_register;
|
||||
|
||||
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);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,8 @@ enum tmc_mem_intf_width {
|
||||
* @buf: area of memory where trace data get sent.
|
||||
* @paddr: DMA start location in RAM.
|
||||
* @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.
|
||||
* @config_type: TMC variant, must be of type @tmc_config_type.
|
||||
* @memwidth: width of the memory interface databus, in bytes.
|
||||
@ -115,6 +116,7 @@ struct tmc_drvdata {
|
||||
dma_addr_t paddr;
|
||||
void __iomem *vaddr;
|
||||
u32 size;
|
||||
u32 len;
|
||||
local_t mode;
|
||||
enum tmc_config_type config_type;
|
||||
enum tmc_mem_intf_width memwidth;
|
||||
|
@ -119,7 +119,7 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct tpiu_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
struct coresight_desc *desc;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
if (np) {
|
||||
@ -154,16 +154,12 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
desc->type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
|
||||
desc->ops = &tpiu_cs_ops;
|
||||
desc->pdata = pdata;
|
||||
desc->dev = dev;
|
||||
drvdata->csdev = coresight_register(desc);
|
||||
desc.type = CORESIGHT_DEV_TYPE_SINK;
|
||||
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
|
||||
desc.ops = &tpiu_cs_ops;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
|
||||
|
@ -257,7 +257,7 @@ static void coresight_disable_source(struct coresight_device *csdev)
|
||||
{
|
||||
if (atomic_dec_return(csdev->refcnt) == 0) {
|
||||
if (source_ops(csdev)->disable) {
|
||||
source_ops(csdev)->disable(csdev);
|
||||
source_ops(csdev)->disable(csdev, NULL);
|
||||
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);
|
||||
if (!path)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
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 */
|
||||
if (conn->child_dev == NULL) {
|
||||
/* 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;
|
||||
} else {
|
||||
/* This component still has an orphan */
|
||||
@ -893,7 +894,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
||||
int nr_refcnts = 1;
|
||||
atomic_t *refcnts = NULL;
|
||||
struct coresight_device *csdev;
|
||||
struct coresight_connection *conns;
|
||||
struct coresight_connection *conns = NULL;
|
||||
|
||||
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
|
||||
if (!csdev) {
|
||||
@ -921,16 +922,20 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
||||
|
||||
csdev->nr_inport = desc->pdata->nr_inport;
|
||||
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++) {
|
||||
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];
|
||||
/* Initialise connections if there is at least one outport */
|
||||
if (csdev->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++) {
|
||||
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;
|
||||
|
@ -166,7 +166,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
|
||||
rdev = of_coresight_get_endpoint_device(rparent);
|
||||
if (!rdev)
|
||||
continue;
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
pdata->child_names[i] = dev_name(rdev);
|
||||
pdata->child_ports[i] = rendpoint.id;
|
||||
@ -184,6 +184,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
|
||||
break;
|
||||
}
|
||||
}
|
||||
of_node_put(dn);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ menu "Pressure sensors"
|
||||
config BMP280
|
||||
tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
|
||||
depends on (I2C || SPI_MASTER)
|
||||
depends on !(BMP085_I2C=y || BMP085_I2C=m)
|
||||
depends on !(BMP085_SPI=y || BMP085_SPI=m)
|
||||
select REGMAP
|
||||
select BMP280_I2C if (I2C)
|
||||
select BMP280_SPI if (SPI_MASTER)
|
||||
|
@ -1013,23 +1013,12 @@ static struct miscdevice uinput_misc = {
|
||||
.minor = UINPUT_MINOR,
|
||||
.name = UINPUT_NAME,
|
||||
};
|
||||
module_misc_device(uinput_misc);
|
||||
|
||||
MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
|
||||
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_DESCRIPTION("User level driver support for input subsystem");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.3");
|
||||
|
||||
module_init(uinput_init);
|
||||
module_exit(uinput_exit);
|
||||
|
@ -1171,27 +1171,10 @@ static struct miscdevice _nvm_misc = {
|
||||
.nodename = "lightnvm/control",
|
||||
.fops = &_ctl_fops,
|
||||
};
|
||||
module_misc_device(_nvm_misc);
|
||||
|
||||
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_LICENSE("GPL v2");
|
||||
MODULE_VERSION("0.1");
|
||||
module_init(nvm_mod_init);
|
||||
module_exit(nvm_mod_exit);
|
||||
|
@ -28,4 +28,13 @@ config MCB_PCI
|
||||
|
||||
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
|
||||
|
@ -5,3 +5,4 @@ mcb-y += mcb-core.o
|
||||
mcb-y += mcb-parse.o
|
||||
|
||||
obj-$(CONFIG_MCB_PCI) += mcb-pci.o
|
||||
obj-$(CONFIG_MCB_LPC) += mcb-lpc.o
|
||||
|
@ -233,6 +233,7 @@ int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
|
||||
dev->dev.bus = &mcb_bus_type;
|
||||
dev->dev.parent = bus->dev.parent;
|
||||
dev->dev.release = mcb_release_dev;
|
||||
dev->dma_dev = bus->carrier;
|
||||
|
||||
device_id = dev->id;
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&dev->bus_list);
|
||||
dev->bus = bus;
|
||||
|
||||
return dev;
|
||||
@ -405,20 +405,6 @@ static int __mcb_bus_add_devices(struct device *dev, void *data)
|
||||
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
|
||||
* @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)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -112,6 +112,15 @@ struct chameleon_bdd {
|
||||
u32 size;
|
||||
} __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,
|
||||
void __iomem *base);
|
||||
|
||||
|
158
drivers/mcb/mcb-lpc.c
Normal file
158
drivers/mcb/mcb-lpc.c
Normal 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");
|
@ -26,19 +26,20 @@ static inline uint32_t get_next_dtype(void __iomem *p)
|
||||
}
|
||||
|
||||
static int chameleon_parse_bdd(struct mcb_bus *bus,
|
||||
phys_addr_t mapbase,
|
||||
struct chameleon_bar *cb,
|
||||
void __iomem *base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chameleon_parse_gdd(struct mcb_bus *bus,
|
||||
phys_addr_t mapbase,
|
||||
void __iomem *base)
|
||||
struct chameleon_bar *cb,
|
||||
void __iomem *base, int bar_count)
|
||||
{
|
||||
struct chameleon_gdd __iomem *gdd =
|
||||
(struct chameleon_gdd __iomem *) base;
|
||||
struct mcb_device *mdev;
|
||||
u32 dev_mapbase;
|
||||
u32 offset;
|
||||
u32 size;
|
||||
int ret;
|
||||
@ -61,13 +62,39 @@ static int chameleon_parse_gdd(struct mcb_bus *bus,
|
||||
mdev->group = GDD_GRP(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);
|
||||
|
||||
mdev->irq.start = GDD_IRQ(reg1);
|
||||
mdev->irq.end = GDD_IRQ(reg1);
|
||||
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.flags = IORESOURCE_MEM;
|
||||
|
||||
@ -85,13 +112,76 @@ static int chameleon_parse_gdd(struct mcb_bus *bus,
|
||||
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,
|
||||
void __iomem *base)
|
||||
{
|
||||
char __iomem *p = base;
|
||||
struct chameleon_fpga_header *header;
|
||||
uint32_t dtype;
|
||||
struct chameleon_bar *cb;
|
||||
char __iomem *p = base;
|
||||
int num_cells = 0;
|
||||
uint32_t dtype;
|
||||
int bar_count;
|
||||
int ret = 0;
|
||||
u32 hsize;
|
||||
|
||||
@ -108,8 +198,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
|
||||
if (header->magic != CHAMELEONV2_MAGIC) {
|
||||
pr_err("Unsupported chameleon version 0x%x\n",
|
||||
header->magic);
|
||||
kfree(header);
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto free_header;
|
||||
}
|
||||
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",
|
||||
header->filename);
|
||||
|
||||
bar_count = chameleon_get_bar(&p, mapbase, &cb);
|
||||
if (bar_count < 0)
|
||||
goto free_header;
|
||||
|
||||
for_each_chameleon_cell(dtype, p) {
|
||||
switch (dtype) {
|
||||
case CHAMELEON_DTYPE_GENERAL:
|
||||
ret = chameleon_parse_gdd(bus, mapbase, p);
|
||||
ret = chameleon_parse_gdd(bus, cb, p, bar_count);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
goto free_bar;
|
||||
p += sizeof(struct chameleon_gdd);
|
||||
break;
|
||||
case CHAMELEON_DTYPE_BRIDGE:
|
||||
chameleon_parse_bdd(bus, mapbase, p);
|
||||
chameleon_parse_bdd(bus, cb, p);
|
||||
p += sizeof(struct chameleon_bdd);
|
||||
break;
|
||||
case CHAMELEON_DTYPE_END:
|
||||
@ -136,8 +230,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
|
||||
default:
|
||||
pr_err("Invalid chameleon descriptor type 0x%x\n",
|
||||
dtype);
|
||||
kfree(header);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto free_bar;
|
||||
}
|
||||
num_cells++;
|
||||
}
|
||||
@ -145,11 +239,15 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
|
||||
if (num_cells == 0)
|
||||
num_cells = -EINVAL;
|
||||
|
||||
kfree(cb);
|
||||
kfree(header);
|
||||
return num_cells;
|
||||
|
||||
out:
|
||||
free_bar:
|
||||
kfree(cb);
|
||||
free_header:
|
||||
kfree(header);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(chameleon_parse_cells);
|
||||
|
@ -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");
|
||||
return -ENODEV;
|
||||
}
|
||||
pci_set_master(pdev);
|
||||
|
||||
priv->mapbase = pci_resource_start(pdev, 0);
|
||||
if (!priv->mapbase) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/gfp.h>
|
||||
#include <memory/jedec_ddr.h>
|
||||
#include <linux/export.h>
|
||||
#include "of_memory.h"
|
||||
|
||||
/**
|
||||
* of_get_min_tck() - extract min timing values for ddr
|
||||
|
@ -429,34 +429,6 @@ config ARM_CHARLCD
|
||||
line and the Linux version on the second line, but that's
|
||||
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
|
||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
|
||||
select GENERIC_NET_UTILS
|
||||
|
@ -9,9 +9,6 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
|
||||
obj-$(CONFIG_INTEL_MID_PTI) += pti.o
|
||||
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.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_ICS932S401) += ics932s401.o
|
||||
obj-$(CONFIG_LKDTM) += lkdtm.o
|
||||
|
@ -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");
|
@ -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");
|
@ -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");
|
@ -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
|
@ -121,9 +121,8 @@ static int at25_ee_read(void *priv, unsigned int offset,
|
||||
* this chip is clocked very slowly
|
||||
*/
|
||||
status = spi_sync(at25->spi, &m);
|
||||
dev_dbg(&at25->spi->dev,
|
||||
"read %Zd bytes at %d --> %d\n",
|
||||
count, offset, (int) status);
|
||||
dev_dbg(&at25->spi->dev, "read %zu bytes at %d --> %zd\n",
|
||||
count, offset, status);
|
||||
|
||||
mutex_unlock(&at25->lock);
|
||||
return status;
|
||||
@ -167,8 +166,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
|
||||
*cp = AT25_WREN;
|
||||
status = spi_write(at25->spi, cp, 1);
|
||||
if (status < 0) {
|
||||
dev_dbg(&at25->spi->dev, "WREN --> %d\n",
|
||||
(int) status);
|
||||
dev_dbg(&at25->spi->dev, "WREN --> %d\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -196,9 +194,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
|
||||
memcpy(cp, buf, segment);
|
||||
status = spi_write(at25->spi, bounce,
|
||||
segment + at25->addrlen + 1);
|
||||
dev_dbg(&at25->spi->dev,
|
||||
"write %u bytes at %u --> %d\n",
|
||||
segment, offset, (int) status);
|
||||
dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n",
|
||||
segment, offset, status);
|
||||
if (status < 0)
|
||||
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)) {
|
||||
dev_err(&at25->spi->dev,
|
||||
"write %d bytes offset %d, "
|
||||
"timeout after %u msecs\n",
|
||||
"write %u bytes offset %u, timeout after %u msecs\n",
|
||||
segment, offset,
|
||||
jiffies_to_msecs(jiffies -
|
||||
(timeout - EE_TIMEOUT)));
|
||||
@ -368,9 +364,7 @@ static int at25_probe(struct spi_device *spi)
|
||||
return PTR_ERR(at25->nvmem);
|
||||
|
||||
dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n",
|
||||
(chip.byte_len < 1024)
|
||||
? chip.byte_len
|
||||
: (chip.byte_len / 1024),
|
||||
(chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024),
|
||||
(chip.byte_len < 1024) ? "Byte" : "KByte",
|
||||
at25->chip.name,
|
||||
(chip.flags & EE_READONLY) ? " (readonly)" : "",
|
||||
|
@ -1350,6 +1350,19 @@ static struct pci_driver genwqe_driver = {
|
||||
.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
|
||||
*/
|
||||
@ -1363,6 +1376,8 @@ static int __init genwqe_init_module(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
class_genwqe->devnode = genwqe_devnode;
|
||||
|
||||
debugfs_genwqe = debugfs_create_dir(GENWQE_DEVNAME, NULL);
|
||||
if (!debugfs_genwqe) {
|
||||
rc = -ENOMEM;
|
||||
|
@ -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__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(queue->ddcb_vaddr, 0, queue_size);
|
||||
|
||||
queue->ddcb_req = kzalloc(sizeof(struct ddcb_requ *) *
|
||||
queue->ddcb_max, GFP_KERNEL);
|
||||
if (!queue->ddcb_req) {
|
||||
|
@ -220,8 +220,8 @@ void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
|
||||
if (get_order(size) > MAX_ORDER)
|
||||
return NULL;
|
||||
|
||||
return dma_alloc_coherent(&cd->pci_dev->dev, size, dma_handle,
|
||||
GFP_KERNEL);
|
||||
return dma_zalloc_coherent(&cd->pci_dev->dev, size, dma_handle,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,
|
||||
|
@ -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)
|
||||
{
|
||||
int error = -ENOMEM;
|
||||
int bar;
|
||||
unsigned long off;
|
||||
|
||||
/* map the memory mapped i/o registers */
|
||||
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 */
|
||||
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) {
|
||||
dev_err(&pdev->dev, "Error mapping shared mem\n");
|
||||
goto mmio_free;
|
||||
@ -717,7 +726,7 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
|
||||
mmio_free:
|
||||
pci_iounmap(pdev, hw->mmio_vaddr);
|
||||
out:
|
||||
return error;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void ilo_remove(struct pci_dev *pdev)
|
||||
@ -899,7 +908,7 @@ static void __exit ilo_exit(void)
|
||||
class_destroy(ilo_class);
|
||||
}
|
||||
|
||||
MODULE_VERSION("1.4.1");
|
||||
MODULE_VERSION("1.5.0");
|
||||
MODULE_ALIAS(ILO_NAME);
|
||||
MODULE_DESCRIPTION(ILO_NAME);
|
||||
MODULE_AUTHOR("David Altobelli <david.altobelli@hpe.com>");
|
||||
|
@ -47,7 +47,6 @@ const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
|
||||
void mei_amthif_reset_params(struct mei_device *dev)
|
||||
{
|
||||
/* reset iamthif parameters. */
|
||||
dev->iamthif_current_cb = NULL;
|
||||
dev->iamthif_canceled = false;
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
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;
|
||||
int ret;
|
||||
|
||||
if (mei_cl_is_connected(cl))
|
||||
return 0;
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (mei_cl_is_connected(cl)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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:
|
||||
return rets;
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_amthif_read_start - queue message for sending read credential
|
||||
*
|
||||
* @cl: host client
|
||||
* @file: file pointer of message recipient
|
||||
* @fp: file pointer of message recipient
|
||||
*
|
||||
* 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_cl_cb *cb;
|
||||
int rets;
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
|
||||
if (!cb) {
|
||||
rets = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
|
||||
rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl));
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
cl->rx_flow_ctrl_creds++;
|
||||
|
||||
dev->iamthif_state = MEI_IAMTHIF_READING;
|
||||
dev->iamthif_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);
|
||||
cl->fp = cb->fp;
|
||||
|
||||
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_cb *cb;
|
||||
int ret;
|
||||
|
||||
dev->iamthif_canceled = false;
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
dev->iamthif_fp = NULL;
|
||||
|
||||
dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
|
||||
|
||||
cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
|
||||
typeof(*cb), list);
|
||||
if (!cb)
|
||||
if (!cb) {
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
cl->fp = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
if (dev->iamthif_state > MEI_IAMTHIF_IDLE &&
|
||||
dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE)
|
||||
if (dev->iamthif_state != MEI_IAMTHIF_IDLE)
|
||||
return 0;
|
||||
|
||||
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
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @file: pointer to file 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
|
||||
*/
|
||||
|
||||
unsigned int mei_amthif_poll(struct mei_device *dev,
|
||||
struct file *file, poll_table *wait)
|
||||
unsigned int mei_amthif_poll(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;
|
||||
|
||||
poll_wait(file, &dev->iamthif_cl.rx_wait, wait);
|
||||
|
||||
if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
|
||||
dev->iamthif_fp == file) {
|
||||
|
||||
poll_wait(file, &cl->rx_wait, wait);
|
||||
if (cb)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
mei_amthif_run_next_cmd(dev);
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
dev_dbg(dev->dev, "completed amthif read.\n ");
|
||||
dev->iamthif_current_cb = NULL;
|
||||
dev->iamthif_stall_timer = 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;
|
||||
|
||||
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) {
|
||||
dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
|
||||
mei_schedule_stall_timer(dev);
|
||||
mei_io_cb_free(cb);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* in case of error enqueue the write cb to complete read list
|
||||
* so it can be propagated to the reader
|
||||
*/
|
||||
list_add_tail(&cb->list, &cl->rd_completed);
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
return;
|
||||
}
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
cl->fp = NULL;
|
||||
if (!dev->iamthif_canceled) {
|
||||
/*
|
||||
* in case of error enqueue the write cb to complete
|
||||
* read list so it can be propagated to the reader
|
||||
*/
|
||||
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;
|
||||
list_add_tail(&cb->list, &cl->rd_completed);
|
||||
dev_dbg(dev->dev, "amthif read completed\n");
|
||||
} else {
|
||||
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
|
||||
* from mei_cb_list
|
||||
*
|
||||
* @dev: device structure.
|
||||
* @file: file structure
|
||||
* @mei_cb_list: callbacks list
|
||||
*
|
||||
* mei_clear_list 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_list(struct mei_device *dev,
|
||||
const struct file *file, struct list_head *mei_cb_list)
|
||||
static void mei_clear_list(const struct file *file,
|
||||
struct list_head *mei_cb_list)
|
||||
{
|
||||
struct mei_cl *cl = &dev->iamthif_cl;
|
||||
struct mei_cl_cb *cb, *next;
|
||||
bool removed = false;
|
||||
|
||||
/* list all list member */
|
||||
list_for_each_entry_safe(cb, next, mei_cb_list, list) {
|
||||
/* 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 */
|
||||
list_for_each_entry_safe(cb, next, mei_cb_list, list)
|
||||
if (file == cb->fp)
|
||||
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)
|
||||
{
|
||||
struct mei_cl *cl = file->private_data;
|
||||
|
||||
if (dev->iamthif_open_count > 0)
|
||||
dev->iamthif_open_count--;
|
||||
|
||||
if (dev->iamthif_fp == file &&
|
||||
dev->iamthif_state != MEI_IAMTHIF_IDLE) {
|
||||
if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
|
||||
|
||||
dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
|
||||
dev->iamthif_state);
|
||||
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))
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
mei_clear_list(file, &dev->amthif_cmd_list.list);
|
||||
mei_clear_list(file, &cl->rd_completed);
|
||||
mei_clear_list(file, &dev->ctrl_rd_list.list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -126,7 +126,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
goto out;
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -142,7 +143,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
mutex_lock(&bus->device_lock);
|
||||
|
||||
if (!mei_cl_is_connected(cl)) {
|
||||
rets = -EBUSY;
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -234,7 +235,7 @@ static void mei_cl_bus_event_work(struct work_struct *work)
|
||||
/* Prepare for the next read */
|
||||
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -324,7 +325,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
|
||||
|
||||
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
|
||||
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);
|
||||
if (ret && ret != -EBUSY)
|
||||
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);
|
||||
struct mei_me_client *me_cl;
|
||||
|
||||
mutex_lock(&bus->device_lock);
|
||||
me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid);
|
||||
if (me_cl)
|
||||
mei_amthif_host_init(bus, me_cl);
|
||||
mei_me_cl_put(me_cl);
|
||||
mutex_unlock(&bus->device_lock);
|
||||
|
||||
mei_cl_bus_rescan(bus);
|
||||
}
|
||||
|
@ -358,8 +358,9 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
|
||||
*
|
||||
* 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,
|
||||
const struct file *fp)
|
||||
static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
|
||||
enum mei_cb_file_ops type,
|
||||
const struct file *fp)
|
||||
{
|
||||
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_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
|
||||
*
|
||||
@ -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
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
cb = mei_io_cb_init(cl, type, fp);
|
||||
cb = mei_io_cb_init(cl, fop_type, fp);
|
||||
if (!cb)
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
cb->buf.size = length;
|
||||
|
||||
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
|
||||
* 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_wr_list, 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;
|
||||
|
||||
if (!cl->me_cl)
|
||||
@ -764,7 +776,7 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
|
||||
cl->me_cl->connect_count--;
|
||||
|
||||
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);
|
||||
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);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
mei_schedule_stall_timer(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -867,13 +880,11 @@ static int __mei_cl_disconnect(struct mei_cl *cl)
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
|
||||
rets = cb ? 0 : -ENOMEM;
|
||||
if (rets)
|
||||
cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL);
|
||||
if (!cb) {
|
||||
rets = -ENOMEM;
|
||||
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)) {
|
||||
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);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
mei_schedule_stall_timer(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1042,14 +1054,14 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
*
|
||||
* @cl: host client
|
||||
* @me_cl: me client
|
||||
* @file: pointer to file structure
|
||||
* @fp: pointer to file structure
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* Return: 0 on success, <0 on failure.
|
||||
*/
|
||||
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_cl_cb *cb;
|
||||
@ -1076,12 +1088,11 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
|
||||
goto nortpm;
|
||||
}
|
||||
|
||||
cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
|
||||
rets = cb ? 0 : -ENOMEM;
|
||||
if (rets)
|
||||
cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp);
|
||||
if (!cb) {
|
||||
rets = -ENOMEM;
|
||||
goto out;
|
||||
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
}
|
||||
|
||||
/* run hbuf acquire last so we don't have to undo */
|
||||
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
||||
@ -1159,50 +1170,42 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev)
|
||||
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
|
||||
* @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))
|
||||
return -EINVAL;
|
||||
|
||||
if (cl->mei_flow_ctrl_creds > 0)
|
||||
if (cl->tx_flow_ctrl_creds > 0)
|
||||
return 1;
|
||||
|
||||
if (mei_cl_is_fixed_address(cl)) {
|
||||
rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp);
|
||||
if (rets && rets != -EBUSY)
|
||||
return rets;
|
||||
if (mei_cl_is_fixed_address(cl))
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 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:
|
||||
* 0 on success
|
||||
* -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))
|
||||
return -EINVAL;
|
||||
@ -1211,13 +1214,13 @@ static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
cl->me_cl->mei_flow_ctrl_creds--;
|
||||
cl->me_cl->tx_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
if (WARN_ON(cl->tx_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
cl->tx_flow_ctrl_creds--;
|
||||
}
|
||||
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
|
||||
*
|
||||
* @cl: host client
|
||||
* @file: associate request with file
|
||||
* @fp: associate request with file
|
||||
* @request: 1 for start or 0 for stop
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
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_cl_cb *cb;
|
||||
@ -1325,7 +1328,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
|
||||
}
|
||||
|
||||
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) {
|
||||
rets = -ENOMEM;
|
||||
goto out;
|
||||
@ -1336,9 +1339,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
} else {
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
@ -1435,25 +1436,6 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
|
||||
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.
|
||||
*
|
||||
@ -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))
|
||||
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)) {
|
||||
cl_err(dev, cl, "no such me client\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/* always allocate at least client max message */
|
||||
length = max_t(size_t, length, mei_cl_mtu(cl));
|
||||
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
|
||||
if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl)
|
||||
return 0;
|
||||
|
||||
/* 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)
|
||||
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);
|
||||
if (rets < 0 && rets != -EINPROGRESS) {
|
||||
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;
|
||||
}
|
||||
|
||||
rets = 0;
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
rets = mei_hbm_cl_flow_control_req(dev, cl);
|
||||
if (rets < 0)
|
||||
goto out;
|
||||
|
||||
list_add_tail(&cb->list, &cl->rd_pending);
|
||||
} else {
|
||||
rets = 0;
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
list_move_tail(&cb->list, &cl->rd_pending);
|
||||
}
|
||||
cl->rx_flow_ctrl_creds++;
|
||||
|
||||
out:
|
||||
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;
|
||||
|
||||
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)
|
||||
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;
|
||||
|
||||
if (first_chunk) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl))
|
||||
if (mei_cl_tx_flow_ctrl_creds_reduce(cl))
|
||||
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.internal = cb->internal;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl, cb->fp);
|
||||
rets = mei_cl_tx_flow_ctrl_creds(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
|
||||
@ -1691,7 +1668,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
rets = mei_cl_flow_ctrl_reduce(cl);
|
||||
rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
@ -1761,6 +1738,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
|
||||
case MEI_FOP_READ:
|
||||
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))
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
break;
|
||||
|
@ -82,11 +82,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
|
||||
/*
|
||||
* 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);
|
||||
int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length);
|
||||
|
||||
|
||||
/**
|
||||
* 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,
|
||||
enum mei_cb_file_ops type,
|
||||
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);
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
* @cl: client
|
||||
* @hbm_cmd: host bus message command
|
||||
* @buf: message buffer
|
||||
* @len: buffer length
|
||||
*
|
||||
* 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->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
|
||||
mei_schedule_stall_timer(dev);
|
||||
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->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
|
||||
mei_schedule_stall_timer(dev);
|
||||
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->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);
|
||||
|
||||
@ -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;
|
||||
mei_schedule_stall_timer(dev);
|
||||
|
||||
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
|
||||
* @flow: flow control.
|
||||
* @fctrl: flow control response bus message
|
||||
*
|
||||
* Return: 0 on success, < 0 otherwise
|
||||
*/
|
||||
static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
struct hbm_flow_control *flow)
|
||||
static int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev,
|
||||
struct hbm_flow_control *fctrl)
|
||||
{
|
||||
struct mei_me_client *me_cl;
|
||||
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) {
|
||||
dev_err(dev->dev, "no such me client %d\n",
|
||||
flow->me_addr);
|
||||
dev_err(dev->dev, "no such me client %d\n", fctrl->me_addr);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
@ -661,9 +664,9 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
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",
|
||||
flow->me_addr, me_cl->mei_flow_ctrl_creds);
|
||||
fctrl->me_addr, me_cl->tx_flow_ctrl_creds);
|
||||
|
||||
rets = 0;
|
||||
out:
|
||||
@ -675,24 +678,24 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
* mei_hbm_cl_flow_control_res - flow control response from me
|
||||
*
|
||||
* @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,
|
||||
struct hbm_flow_control *flow_control)
|
||||
static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev,
|
||||
struct hbm_flow_control *fctrl)
|
||||
{
|
||||
struct mei_cl *cl;
|
||||
|
||||
if (!flow_control->host_addr) {
|
||||
if (!fctrl->host_addr) {
|
||||
/* single receive buffer */
|
||||
mei_hbm_add_single_flow_creds(dev, flow_control);
|
||||
mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl);
|
||||
return;
|
||||
}
|
||||
|
||||
cl = mei_hbm_cl_find_by_cmd(dev, flow_control);
|
||||
cl = mei_hbm_cl_find_by_cmd(dev, fctrl);
|
||||
if (cl) {
|
||||
cl->mei_flow_ctrl_creds++;
|
||||
cl->tx_flow_ctrl_creds++;
|
||||
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->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)
|
||||
return -ENOMEM;
|
||||
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
|
||||
}
|
||||
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 hbm_client_connect_request *disconnect_req;
|
||||
struct hbm_flow_control *flow_control;
|
||||
struct hbm_flow_control *fctrl;
|
||||
|
||||
/* read the message to our buffer */
|
||||
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:
|
||||
dev_dbg(dev->dev, "hbm: client flow control response: message received.\n");
|
||||
|
||||
flow_control = (struct hbm_flow_control *) mei_msg;
|
||||
mei_hbm_cl_flow_control_res(dev, flow_control);
|
||||
fctrl = (struct hbm_flow_control *)mei_msg;
|
||||
mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl);
|
||||
break;
|
||||
|
||||
case MEI_PG_ISOLATION_ENTRY_RES_CMD:
|
||||
|
@ -125,6 +125,9 @@
|
||||
#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
|
||||
#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
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "mei_dev.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);
|
||||
/* H_RST may be found lit before reset is started,
|
||||
* for example if preceding reset flow hasn't completed.
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mei.h>
|
||||
|
||||
@ -935,6 +936,8 @@ static int mei_txe_hw_start(struct mei_device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev->dev);
|
||||
|
||||
/* enable input ready interrupts:
|
||||
* SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK
|
||||
*/
|
||||
|
@ -94,7 +94,7 @@ void mei_cancel_work(struct mei_device *dev)
|
||||
cancel_work_sync(&dev->reset_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);
|
||||
|
||||
|
@ -102,26 +102,25 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_cl_cb *cb;
|
||||
unsigned char *buffer = NULL;
|
||||
size_t buf_sz;
|
||||
|
||||
cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
|
||||
if (!cb) {
|
||||
cl_err(dev, cl, "pending read cb not found\n");
|
||||
goto out;
|
||||
if (!mei_cl_is_fixed_address(cl)) {
|
||||
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)) {
|
||||
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);
|
||||
cb->status = -ENOMEM;
|
||||
goto out;
|
||||
cb->status = -ENODEV;
|
||||
goto discard;
|
||||
}
|
||||
|
||||
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);
|
||||
cb->status = -EMSGSIZE;
|
||||
goto out;
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if (cb->buf.size < buf_sz) {
|
||||
cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
|
||||
cb->buf.size, mei_hdr->length, cb->buf_idx);
|
||||
buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL);
|
||||
|
||||
if (!buffer) {
|
||||
cb->status = -ENOMEM;
|
||||
list_move_tail(&cb->list, &complete_list->list);
|
||||
goto out;
|
||||
}
|
||||
cb->buf.data = buffer;
|
||||
cb->buf.size = buf_sz;
|
||||
list_move_tail(&cb->list, &complete_list->list);
|
||||
cb->status = -EMSGSIZE;
|
||||
goto discard;
|
||||
}
|
||||
|
||||
buffer = cb->buf.data + cb->buf_idx;
|
||||
mei_read_slots(dev, buffer, mei_hdr->length);
|
||||
mei_read_slots(dev, cb->buf.data + 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);
|
||||
}
|
||||
|
||||
out:
|
||||
if (!buffer)
|
||||
mei_irq_discard_msg(dev, mei_hdr);
|
||||
return 0;
|
||||
|
||||
discard:
|
||||
mei_irq_discard_msg(dev, mei_hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -216,6 +209,9 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
int slots;
|
||||
int ret;
|
||||
|
||||
if (!list_empty(&cl->rd_pending))
|
||||
return 0;
|
||||
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
@ -463,6 +459,19 @@ static void mei_connect_timeout(struct mei_cl *cl)
|
||||
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.
|
||||
*
|
||||
@ -472,10 +481,9 @@ static void mei_connect_timeout(struct mei_cl *cl)
|
||||
void mei_timer(struct work_struct *work)
|
||||
{
|
||||
struct mei_cl *cl;
|
||||
|
||||
struct mei_device *dev = container_of(work,
|
||||
struct mei_device, timer_work.work);
|
||||
|
||||
bool reschedule_timer = false;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
@ -490,6 +498,7 @@ void mei_timer(struct work_struct *work)
|
||||
mei_reset(dev);
|
||||
goto out;
|
||||
}
|
||||
reschedule_timer = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -504,6 +513,7 @@ void mei_timer(struct work_struct *work)
|
||||
mei_connect_timeout(cl);
|
||||
goto out;
|
||||
}
|
||||
reschedule_timer = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,19 +524,16 @@ void mei_timer(struct work_struct *work)
|
||||
if (--dev->iamthif_stall_timer == 0) {
|
||||
dev_err(dev->dev, "timer: amthif hanged.\n");
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
reschedule_timer = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (dev->dev_state != MEI_DEV_DISABLED)
|
||||
schedule_delayed_work(&dev->timer_work, 2 * HZ);
|
||||
if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer)
|
||||
mei_schedule_stall_timer(dev);
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ static int mei_open(struct inode *inode, struct file *file)
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
cl->fp = file;
|
||||
file->private_data = cl;
|
||||
|
||||
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_device *dev;
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
bool nonblock = !!(file->f_flags & O_NONBLOCK);
|
||||
int rets;
|
||||
int err;
|
||||
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
@ -164,11 +164,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
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);
|
||||
if (cb)
|
||||
goto copy_buffer;
|
||||
@ -176,24 +171,29 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
if (*offset > 0)
|
||||
*offset = 0;
|
||||
|
||||
err = mei_cl_read_start(cl, length, file);
|
||||
if (err && err != -EBUSY) {
|
||||
cl_dbg(dev, cl, "mei start read failure status = %d\n", err);
|
||||
rets = err;
|
||||
rets = mei_cl_read_start(cl, length, file);
|
||||
if (rets && rets != -EBUSY) {
|
||||
cl_dbg(dev, cl, "mei start read failure status = %d\n", rets);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
rets = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
if (nonblock) {
|
||||
rets = -EAGAIN;
|
||||
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);
|
||||
|
||||
if (wait_event_interruptible(cl->rx_wait,
|
||||
(!list_empty(&cl->rd_completed)) ||
|
||||
(!mei_cl_is_connected(cl)))) {
|
||||
(!list_empty(&cl->rd_completed)) ||
|
||||
(!mei_cl_is_connected(cl)))) {
|
||||
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
@ -202,16 +202,12 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (!mei_cl_is_connected(cl)) {
|
||||
rets = -EBUSY;
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cb = mei_cl_read_cb(cl, file);
|
||||
if (!cb) {
|
||||
rets = 0;
|
||||
goto out;
|
||||
}
|
||||
cb = mei_cl_read_cb(cl, file);
|
||||
} while (!cb);
|
||||
|
||||
copy_buffer:
|
||||
/* now copy the data to user space */
|
||||
@ -609,24 +605,24 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
mask = mei_amthif_poll(dev, file, wait);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (notify_en) {
|
||||
poll_wait(file, &cl->ev_wait, wait);
|
||||
if (cl->notify_ev)
|
||||
mask |= POLLPRI;
|
||||
}
|
||||
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
mask |= mei_amthif_poll(file, wait);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (req_events & (POLLIN | POLLRDNORM)) {
|
||||
poll_wait(file, &cl->rx_wait, wait);
|
||||
|
||||
if (!list_empty(&cl->rd_completed))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
else
|
||||
mei_cl_read_start(cl, 0, file);
|
||||
mei_cl_read_start(cl, mei_cl_mtu(cl), file);
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -80,18 +80,13 @@ const char *mei_dev_state_str(int state);
|
||||
enum iamthif_states {
|
||||
MEI_IAMTHIF_IDLE,
|
||||
MEI_IAMTHIF_WRITING,
|
||||
MEI_IAMTHIF_FLOW_CONTROL,
|
||||
MEI_IAMTHIF_READING,
|
||||
MEI_IAMTHIF_READ_COMPLETE
|
||||
};
|
||||
|
||||
enum mei_file_transaction_states {
|
||||
MEI_IDLE,
|
||||
MEI_WRITING,
|
||||
MEI_WRITE_COMPLETE,
|
||||
MEI_FLOW_CONTROL,
|
||||
MEI_READING,
|
||||
MEI_READ_COMPLETE
|
||||
};
|
||||
|
||||
/**
|
||||
@ -146,7 +141,7 @@ struct mei_fw_status {
|
||||
* @refcnt: struct reference count
|
||||
* @props: client properties
|
||||
* @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
|
||||
* @bus_added: added to bus
|
||||
*/
|
||||
@ -155,7 +150,7 @@ struct mei_me_client {
|
||||
struct kref refcnt;
|
||||
struct mei_client_properties props;
|
||||
u8 client_id;
|
||||
u8 mei_flow_ctrl_creds;
|
||||
u8 tx_flow_ctrl_creds;
|
||||
u8 connect_count;
|
||||
u8 bus_added;
|
||||
};
|
||||
@ -202,10 +197,11 @@ struct mei_cl_cb {
|
||||
* @ev_async: event async notification
|
||||
* @status: connection status
|
||||
* @me_cl: fw client connected
|
||||
* @fp: file associated with client
|
||||
* @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
|
||||
* @reserved: reserved for alignment
|
||||
* @notify_en: notification - enabled/disabled
|
||||
* @notify_ev: pending notification event
|
||||
* @writing_state: state of the tx
|
||||
@ -225,10 +221,11 @@ struct mei_cl {
|
||||
struct fasync_struct *ev_async;
|
||||
int status;
|
||||
struct mei_me_client *me_cl;
|
||||
const struct file *fp;
|
||||
u8 host_client_id;
|
||||
u8 mei_flow_ctrl_creds;
|
||||
u8 tx_flow_ctrl_creds;
|
||||
u8 rx_flow_ctrl_creds;
|
||||
u8 timer_count;
|
||||
u8 reserved;
|
||||
u8 notify_en;
|
||||
u8 notify_ev;
|
||||
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
|
||||
*
|
||||
* @amthif_cmd_list : amthif list for cmd waiting
|
||||
* @iamthif_fp : file for current amthif operation
|
||||
* @iamthif_cl : amthif host client
|
||||
* @iamthif_current_cb : amthif current operation callback
|
||||
* @iamthif_open_count : number of opened amthif connections
|
||||
* @iamthif_stall_timer : timer to detect amthif hang
|
||||
* @iamthif_state : amthif processor state
|
||||
@ -484,10 +479,7 @@ struct mei_device {
|
||||
|
||||
/* amthif list for cmd waiting */
|
||||
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_cb *iamthif_current_cb;
|
||||
long iamthif_open_count;
|
||||
u32 iamthif_stall_timer;
|
||||
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_schedule_stall_timer(struct mei_device *dev);
|
||||
int mei_irq_read_handler(struct mei_device *dev,
|
||||
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_read(struct mei_device *dev, struct file *file,
|
||||
char __user *ubuf, size_t length, loff_t *offset);
|
||||
|
||||
unsigned int mei_amthif_poll(struct mei_device *dev,
|
||||
struct file *file, poll_table *wait);
|
||||
unsigned int mei_amthif_poll(struct file *file, poll_table *wait);
|
||||
|
||||
int mei_amthif_release(struct mei_device *dev, struct file *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_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 */
|
||||
{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);
|
||||
|
||||
schedule_delayed_work(&dev->timer_work, HZ);
|
||||
|
||||
/*
|
||||
* For not wake-able HW runtime pm framework
|
||||
* 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);
|
||||
|
||||
if (ret && ret != -EAGAIN)
|
||||
schedule_work(&dev->reset_work);
|
||||
|
||||
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);
|
||||
|
||||
if (ret)
|
||||
schedule_work(&dev->reset_work);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (ret && ret != -EAGAIN)
|
||||
schedule_work(&dev->reset_work);
|
||||
|
||||
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);
|
||||
|
||||
if (ret)
|
||||
schedule_work(&dev->reset_work);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,6 @@ int scif_reserve_dma_chan(struct scif_endpt *ep)
|
||||
*/
|
||||
static
|
||||
void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn,
|
||||
struct scif_endpt *ep,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
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) {
|
||||
window = list_entry(item, struct scif_window, list);
|
||||
ep = (struct scif_endpt *)window->ep;
|
||||
if (!len)
|
||||
break;
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -169,7 +167,7 @@ static void __scif_rma_destroy_tcw_ep(struct scif_endpt *ep)
|
||||
spin_lock(&ep->rma_info.tc_lock);
|
||||
list_for_each_safe(item, tmp, &ep->rma_info.mmn_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);
|
||||
}
|
||||
|
@ -552,7 +552,7 @@ static void scif_munmap(struct vm_area_struct *vma)
|
||||
{
|
||||
struct scif_endpt *ep;
|
||||
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;
|
||||
struct scif_rma_req req;
|
||||
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_endpt *ep = (struct scif_endpt *)epd;
|
||||
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;
|
||||
struct vma_pvt *vmapvt;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user