1
0
Fork 0

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.
  ...
hifive-unleashed-5.1
Linus Torvalds 2016-10-03 19:57:49 -07:00
commit 7a53eea1f7
129 changed files with 4040 additions and 2805 deletions

View File

@ -1944,6 +1944,11 @@ E: kraxel@bytesex.org
E: kraxel@suse.de
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

View File

@ -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

View File

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

View File

@ -1,11 +1,20 @@
= Rockchip eFuse device tree bindings =
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>;

View File

@ -8,13 +8,14 @@ As with other subsystems within the Linux kernel, VME device drivers register
with the VME subsystem, typically called from the devices init routine. This is
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

View File

@ -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

View File

@ -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);

View File

@ -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");

View File

@ -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);

View File

@ -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/

View File

@ -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");

View File

@ -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");

View File

@ -381,6 +381,9 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -493,7 +493,7 @@ exit_smapi_request_error:
}
int smapi_set_DSP_power_state(BOOLEAN bOn)
int smapi_set_DSP_power_state(bool bOn)
{
int bRC = -EIO;
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();

View File

@ -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

View File

@ -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",

View File

@ -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)

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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");

View File

@ -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 @@ cleanup:
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);

View File

@ -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 @@ err_deq_chan:
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;
}

View File

@ -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;
}

View File

@ -278,7 +278,7 @@ cleanup:
*
* 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

View File

@ -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 @@ probe_error0:
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;
}

View File

@ -83,6 +83,12 @@ static void fcopy_timeout_func(struct work_struct *dummy)
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
}
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)) {

View File

@ -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)) {

View File

@ -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);
}

View File

@ -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");

View File

@ -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;

View File

@ -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 */

View File

@ -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,

View File

@ -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;
}

View File

@ -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 @@ err_alloc:
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);

View File

@ -184,8 +184,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata)
if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) {
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);

View File

@ -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)

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -311,9 +311,10 @@ void etm_config_trace_mode(struct etm_config *config)
#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
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 @@ err:
}
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,

View File

@ -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, &reg, 1);
return reg.data;
}
#define coresight_etm4x_simple_func(name, offset) \
coresight_simple_func(struct etmv4_drvdata, NULL, name, offset)
#define coresight_etm4x_cross_read(name, offset) \
coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \
name, offset)
coresight_etm4x_simple_func(trcoslsr, TRCOSLSR);
coresight_etm4x_simple_func(trcpdcr, TRCPDCR);
coresight_etm4x_simple_func(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,

View File

@ -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 @@ err:
}
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 @@ err_arch_supported:
}
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",

View File

@ -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

View File

@ -176,7 +176,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct 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);

View File

@ -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,

View File

@ -102,7 +102,7 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
struct resource *res = &adev->res;
struct 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);

View File

@ -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;

View File

@ -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;

View File

@ -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 @@ out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free memory outside the spinlock if need be */
if (!used && buf)
if (!used)
kfree(buf);
if (!ret)

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -119,7 +119,7 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct 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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -233,6 +233,7 @@ int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
dev->dev.bus = &mcb_bus_type;
dev->dev.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);

View File

@ -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);

View File

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

View File

@ -26,19 +26,20 @@ static inline uint32_t get_next_dtype(void __iomem *p)
}
static int chameleon_parse_bdd(struct mcb_bus *bus,
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 @@ err:
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);

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

@ -121,9 +121,8 @@ static int at25_ee_read(void *priv, unsigned int offset,
* this chip is clocked very slowly
*/
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)" : "",

View File

@ -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;

View File

@ -1048,8 +1048,6 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
"[%s] **err: could not allocate DDCB **\n", __func__);
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) {

View File

@ -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,

View File

@ -688,7 +688,8 @@ static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
{
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 @@ ram_free:
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>");

View File

@ -47,7 +47,6 @@ const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
void mei_amthif_reset_params(struct mei_device *dev)
{
/* 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;
}

View File

@ -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);
}

View File

@ -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 @@ err:
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 @@ out:
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;

View File

@ -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);
/*

View File

@ -161,6 +161,7 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
* @dev: the device structure
* @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 @@ out:
* 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:

View File

@ -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
*/

View File

@ -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.

View File

@ -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
*/

View File

@ -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);

View File

@ -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);
}

View File

@ -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:

View File

@ -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);

View File

@ -91,6 +91,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_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;
}

View File

@ -347,6 +347,10 @@ static int mei_txe_pm_runtime_suspend(struct device *device)
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
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;
}

View File

@ -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);
}

View File

@ -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