提交 1fc14993 编写于 作者: L Linus Torvalds

Merge tag 'char-misc-4.1-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/misc driver patchset for 4.1-rc1.

  Lots of different driver subsystem updates here, nothing major, full
  details are in the shortlog.

  All of this has been in linux-next for a while"

* tag 'char-misc-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (133 commits)
  mei: trace: remove unused TRACE_SYSTEM_STRING
  DTS: ARM: OMAP3-N900: Add lis3lv02d support
  Documentation: DT: lis302: update wakeup binding
  lis3lv02d: DT: add wakeup unit 2 and wakeup threshold
  lis3lv02d: DT: use s32 to support negative values
  Drivers: hv: hv_balloon: correctly handle num_pages>INT_MAX case
  Drivers: hv: hv_balloon: correctly handle val.freeram<num_pages case
  mei: replace check for connection instead of transitioning
  mei: use mei_cl_is_connected consistently
  mei: fix mei_poll operation
  hv_vmbus: Add gradually increased delay for retries in vmbus_post_msg()
  Drivers: hv: hv_balloon: survive ballooning request with num_pages=0
  Drivers: hv: hv_balloon: eliminate jumps in piecewiese linear floor function
  Drivers: hv: hv_balloon: do not online pages in offline blocks
  hv: remove the per-channel workqueue
  hv: don't schedule new works in vmbus_onoffer()/vmbus_onoffer_rescind()
  hv: run non-blocking message handlers in the dispatch tasklet
  coresight: moving to new "hwtracing" directory
  coresight-tmc: Adding a status interface to sysfs
  coresight: remove the unnecessary configuration coresight-default-sink
  ...
......@@ -61,7 +61,6 @@ Example:
compatible = "arm,coresight-etb10", "arm,primecell";
reg = <0 0x20010000 0 0x1000>;
coresight-default-sink;
clocks = <&oscclk6a>;
clock-names = "apb_pclk";
port {
......
USB GPIO Extcon device
This is a virtual device used to generate USB cable states from the USB ID pin
connected to a GPIO pin.
Required properties:
- compatible: Should be "linux,extcon-usb-gpio"
- id-gpio: gpio for USB ID pin. See gpio binding.
Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below:
extcon_usb1 {
compatible = "linux,extcon-usb-gpio";
id-gpio = <&gpio6 1 GPIO_ACTIVE_HIGH>;
}
&omap_dwc3_1 {
extcon = <&extcon_usb1>;
};
* Ingenic JZ4780 NAND/external memory controller (NEMC)
This file documents the device tree bindings for the NEMC external memory
controller in Ingenic JZ4780
Required properties:
- compatible: Should be set to one of:
"ingenic,jz4780-nemc" (JZ4780)
- reg: Should specify the NEMC controller registers location and length.
- clocks: Clock for the NEMC controller.
- #address-cells: Must be set to 2.
- #size-cells: Must be set to 1.
- ranges: A set of ranges for each bank describing the physical memory layout.
Each should specify the following 4 integer values:
<cs number> 0 <physical address of mapping> <size of mapping>
Each child of the NEMC node describes a device connected to the NEMC.
Required child node properties:
- reg: Should contain at least one register specifier, given in the following
format:
<cs number> <offset> <size>
Multiple registers can be specified across multiple banks. This is needed,
for example, for packaged NAND devices with multiple dies. Such devices
should be grouped into a single node.
Optional child node properties:
- ingenic,nemc-bus-width: Specifies the bus width in bits. Defaults to 8 bits.
- ingenic,nemc-tAS: Address setup time in nanoseconds.
- ingenic,nemc-tAH: Address hold time in nanoseconds.
- ingenic,nemc-tBP: Burst pitch time in nanoseconds.
- ingenic,nemc-tAW: Access wait time in nanoseconds.
- ingenic,nemc-tSTRV: Static memory recovery time in nanoseconds.
If a child node references multiple banks in its "reg" property, the same value
for all optional parameters will be configured for all banks. If any optional
parameters are omitted, they will be left unchanged from whatever they are
configured to when the NEMC device is probed (which may be the reset value as
given in the hardware reference manual, or a value configured by the boot
loader).
Example (NEMC node with a NAND child device attached at CS1):
nemc: nemc@13410000 {
compatible = "ingenic,jz4780-nemc";
reg = <0x13410000 0x10000>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <1 0 0x1b000000 0x1000000
2 0 0x1a000000 0x1000000
3 0 0x19000000 0x1000000
4 0 0x18000000 0x1000000
5 0 0x17000000 0x1000000
6 0 0x16000000 0x1000000>;
clocks = <&cgu JZ4780_CLK_NEMC>;
nand: nand@1 {
compatible = "ingenic,jz4780-nand";
reg = <1 0 0x1000000>;
ingenic,nemc-tAS = <10>;
ingenic,nemc-tAH = <5>;
ingenic,nemc-tBP = <10>;
ingenic,nemc-tAW = <15>;
ingenic,nemc-tSTRV = <100>;
...
};
};
......@@ -46,11 +46,18 @@ Optional properties for all bus drivers:
interrupt 2
- st,wakeup-{x,y,z}-{lo,hi}: set wakeup condition on x/y/z axis for
upper/lower limit
- st,wakeup-threshold: set wakeup threshold
- st,wakeup2-{x,y,z}-{lo,hi}: set wakeup condition on x/y/z axis for
upper/lower limit for second wakeup
engine.
- st,wakeup2-threshold: set wakeup threshold for second wakeup
engine.
- st,highpass-cutoff-hz=: 1, 2, 4 or 8 for 1Hz, 2Hz, 4Hz or 8Hz of
highpass cut-off frequency
- st,hipass{1,2}-disable: disable highpass 1/2.
- st,default-rate=: set the default rate
- st,axis-{x,y,z}=: set the axis to map to the three coordinates
- st,axis-{x,y,z}=: set the axis to map to the three coordinates.
Negative values can be used for inverted axis.
- st,{min,max}-limit-{x,y,z} set the min/max limits for x/y/z axis
(used by self-test)
......
Qualcomm SPMI Controller (PMIC Arbiter)
The SPMI PMIC Arbiter is found on the Snapdragon 800 Series. It is an SPMI
The SPMI PMIC Arbiter is found on Snapdragon chipsets. It is an SPMI
controller with wrapping arbitration logic to allow for multiple on-chip
devices to control a single SPMI master.
......@@ -19,6 +19,10 @@ Required properties:
"core" - core registers
"intr" - interrupt controller registers
"cnfg" - configuration registers
Registers used only for V2 PMIC Arbiter:
"chnls" - tx-channel per virtual slave registers.
"obsrvr" - rx-channel (called observer) per virtual slave registers.
- reg : address + size pairs describing the PMIC arb register sets; order must
correspond with the order of entries in reg-names
- #address-cells : must be set to 2
......
......@@ -276,6 +276,7 @@ IOMAP
devm_ioport_unmap()
devm_ioremap()
devm_ioremap_nocache()
devm_ioremap_wc()
devm_ioremap_resource() : checks resource, requests memory region, ioremaps
devm_iounmap()
pcim_iomap()
......
......@@ -14,7 +14,7 @@ document is concerned with the latter.
HW assisted tracing is becoming increasingly useful when dealing with systems
that have many SoCs and other components like GPU and DMA engines. ARM has
developed a HW assisted tracing solution by means of different components, each
being added to a design at systhesis time to cater to specific tracing needs.
being added to a design at synthesis time to cater to specific tracing needs.
Compoments are generally categorised as source, link and sinks and are
(usually) discovered using the AMBA bus.
......
......@@ -958,7 +958,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS
M: Mathieu Poirier <mathieu.poirier@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: drivers/coresight/*
F: drivers/hwtracing/coresight/*
F: Documentation/trace/coresight.txt
F: Documentation/devicetree/bindings/arm/coresight.txt
F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
......@@ -1828,7 +1828,7 @@ S: Supported
F: drivers/spi/spi-atmel.*
ATMEL SSC DRIVER
M: Bo Shen <voice.shen@atmel.com>
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Supported
F: drivers/misc/atmel-ssc.c
......
......@@ -1610,59 +1610,6 @@ config DEBUG_SET_MODULE_RONX
against certain classes of kernel exploits.
If in doubt, say "N".
menuconfig CORESIGHT
bool "CoreSight Tracing Support"
select ARM_AMBA
help
This framework provides a kernel interface for the CoreSight debug
and trace drivers to register themselves with. It's intended to build
a topological view of the CoreSight components based on a DT
specification and configure the right serie of components when a
trace source gets enabled.
if CORESIGHT
config CORESIGHT_LINKS_AND_SINKS
bool "CoreSight Link and Sink drivers"
help
This enables support for CoreSight link and sink drivers that are
responsible for transporting and collecting the trace data
respectively. Link and sinks are dynamically aggregated with a trace
entity at run time to form a complete trace path.
config CORESIGHT_LINK_AND_SINK_TMC
bool "Coresight generic TMC driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Memory Controller driver. Depending
on its configuration the device can act as a link (embedded trace router
- ETR) or sink (embedded trace FIFO). The driver complies with the
generic implementation of the component without special enhancement or
added features.
config CORESIGHT_SINK_TPIU
bool "Coresight generic TPIU driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Port Interface Unit driver, responsible
for bridging the gap between the on-chip coresight components and a trace
port collection engine, typically connected to an external host for use
case capturing more traces than the on-board coresight memory can handle.
config CORESIGHT_SINK_ETBV10
bool "Coresight ETBv1.0 driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Embedded Trace Buffer version 1.0 driver
that complies with the generic implementation of the component without
special enhancement or added features.
source "drivers/hwtracing/coresight/Kconfig"
config CORESIGHT_SOURCE_ETM3X
bool "CoreSight Embedded Trace Macrocell 3.x driver"
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for processor ETM3.x and PTM1.x modules,
which allows tracing the instructions that a processor is executing
This is primarily useful for instruction level tracing. Depending
the ETM version data tracing may also be available.
endif
endmenu
......@@ -275,7 +275,6 @@
compatible = "arm,coresight-etb10", "arm,primecell";
reg = <0 0xe3c42000 0 0x1000>;
coresight-default-sink;
clocks = <&clk_375m>;
clock-names = "apb_pclk";
port {
......
......@@ -150,7 +150,6 @@
compatible = "arm,coresight-etb10", "arm,primecell";
reg = <0x5401b000 0x1000>;
coresight-default-sink;
clocks = <&emu_src_ck>;
clock-names = "apb_pclk";
port {
......
......@@ -145,7 +145,6 @@
compatible = "arm,coresight-etb10", "arm,primecell";
reg = <0x5401b000 0x1000>;
coresight-default-sink;
clocks = <&emu_src_ck>;
clock-names = "apb_pclk";
port {
......
......@@ -609,6 +609,58 @@
pinctrl-0 = <&i2c3_pins>;
clock-frequency = <400000>;
lis302dl: lis3lv02d@1d {
compatible = "st,lis3lv02d";
reg = <0x1d>;
Vdd-supply = <&vaux1>;
Vdd_IO-supply = <&vio>;
interrupt-parent = <&gpio6>;
interrupts = <21 20>; /* 181 and 180 */
/* click flags */
st,click-single-x;
st,click-single-y;
st,click-single-z;
/* Limits are 0.5g * value */
st,click-threshold-x = <8>;
st,click-threshold-y = <8>;
st,click-threshold-z = <10>;
/* Click must be longer than time limit */
st,click-time-limit = <9>;
/* Kind of debounce filter */
st,click-latency = <50>;
/* Interrupt line 2 for click detection */
st,irq2-click;
st,wakeup-x-hi;
st,wakeup-y-hi;
st,wakeup-threshold = <(800/18)>; /* millig-value / 18 to get HW values */
st,wakeup2-z-hi;
st,wakeup2-threshold = <(900/18)>; /* millig-value / 18 to get HW values */
st,hipass1-disable;
st,hipass2-disable;
st,axis-x = <1>; /* LIS3_DEV_X */
st,axis-y = <(-2)>; /* LIS3_INV_DEV_Y */
st,axis-z = <(-3)>; /* LIS3_INV_DEV_Z */
st,min-limit-x = <(-32)>;
st,min-limit-y = <3>;
st,min-limit-z = <3>;
st,max-limit-x = <(-3)>;
st,max-limit-y = <32>;
st,max-limit-z = <32>;
};
};
&mmc1 {
......
......@@ -362,7 +362,6 @@
compatible = "arm,coresight-etb10", "arm,primecell";
reg = <0 0x20010000 0 0x1000>;
coresight-default-sink;
clocks = <&oscclk6a>;
clock-names = "apb_pclk";
port {
......
......@@ -89,4 +89,6 @@ config DEBUG_ALIGN_RODATA
If in doubt, say N
source "drivers/hwtracing/coresight/Kconfig"
endmenu
......@@ -67,6 +67,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
extern void iounmap(volatile void __iomem *addr);
#define ioremap_nocache(off,size) ioremap(off,size)
#define ioremap_wc ioremap_nocache
/*
* IO bus memory addresses are also 1:1 with the physical address
......
......@@ -225,6 +225,8 @@
#define HV_STATUS_INVALID_HYPERCALL_CODE 2
#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
#define HV_STATUS_INVALID_ALIGNMENT 4
#define HV_STATUS_INSUFFICIENT_MEMORY 11
#define HV_STATUS_INVALID_CONNECTION_ID 18
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
typedef struct _HV_REFERENCE_TSC_PAGE {
......
......@@ -163,5 +163,5 @@ obj-$(CONFIG_POWERCAP) += powercap/
obj-$(CONFIG_MCB) += mcb/
obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += coresight/
obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
obj-$(CONFIG_ANDROID) += android/
......@@ -300,11 +300,14 @@ static const struct file_operations rng_chrdev_ops = {
.llseek = noop_llseek,
};
static const struct attribute_group *rng_dev_groups[];
static struct miscdevice rng_miscdev = {
.minor = RNG_MISCDEV_MINOR,
.name = RNG_MODULE_NAME,
.nodename = "hwrng",
.fops = &rng_chrdev_ops,
.groups = rng_dev_groups,
};
......@@ -377,37 +380,22 @@ static DEVICE_ATTR(rng_available, S_IRUGO,
hwrng_attr_available_show,
NULL);
static struct attribute *rng_dev_attrs[] = {
&dev_attr_rng_current.attr,
&dev_attr_rng_available.attr,
NULL
};
ATTRIBUTE_GROUPS(rng_dev);
static void __exit unregister_miscdev(void)
{
device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
misc_deregister(&rng_miscdev);
}
static int __init register_miscdev(void)
{
int err;
err = misc_register(&rng_miscdev);
if (err)
goto out;
err = device_create_file(rng_miscdev.this_device,
&dev_attr_rng_current);
if (err)
goto err_misc_dereg;
err = device_create_file(rng_miscdev.this_device,
&dev_attr_rng_available);
if (err)
goto err_remove_current;
out:
return err;
err_remove_current:
device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
err_misc_dereg:
misc_deregister(&rng_miscdev);
goto out;
return misc_register(&rng_miscdev);
}
static int hwrng_fillfn(void *unused)
......
......@@ -133,7 +133,7 @@ static int rng_remove(struct platform_device *dev)
return 0;
}
static struct of_device_id rng_match[] = {
static const struct of_device_id rng_match[] = {
{ .compatible = "1682m-rng", },
{ .compatible = "pasemi,pwrficient-rng", },
{ },
......
......@@ -61,7 +61,7 @@ static int powernv_rng_probe(struct platform_device *pdev)
return 0;
}
static struct of_device_id powernv_rng_match[] = {
static const struct of_device_id powernv_rng_match[] = {
{ .compatible = "ibm,power-rng",},
{},
};
......
......@@ -123,7 +123,7 @@ static int ppc4xx_rng_remove(struct platform_device *dev)
return 0;
}
static struct of_device_id ppc4xx_rng_match[] = {
static const struct of_device_id ppc4xx_rng_match[] = {
{ .compatible = "ppc4xx-rng", },
{ .compatible = "amcc,ppc460ex-rng", },
{ .compatible = "amcc,ppc440epx-rng", },
......
......@@ -510,13 +510,15 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
* 9) AC power
* 10) Fn Key status
*/
return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
I8K_PROC_FMT,
bios_version,
i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
cpu_temp,
left_fan, right_fan, left_speed, right_speed,
ac_power, fn_key);
seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
I8K_PROC_FMT,
bios_version,
i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
cpu_temp,
left_fan, right_fan, left_speed, right_speed,
ac_power, fn_key);
return 0;
}
static int i8k_open_fs(struct inode *inode, struct file *file)
......
......@@ -2667,7 +2667,7 @@ static struct pci_driver ipmi_pci_driver = {
};
#endif /* CONFIG_PCI */
static struct of_device_id ipmi_match[];
static const struct of_device_id ipmi_match[];
static int ipmi_probe(struct platform_device *dev)
{
#ifdef CONFIG_OF
......@@ -2764,7 +2764,7 @@ static int ipmi_remove(struct platform_device *dev)
return 0;
}
static struct of_device_id ipmi_match[] =
static const struct of_device_id ipmi_match[] =
{
{ .type = "ipmi", .compatible = "ipmi-kcs",
.data = (void *)(unsigned long) SI_KCS },
......
......@@ -140,12 +140,17 @@ static int misc_open(struct inode * inode, struct file * file)
goto fail;
}
/*
* Place the miscdevice in the file's
* private_data so it can be used by the
* file operations, including f_op->open below
*/
file->private_data = c;
err = 0;
replace_fops(file, new_fops);
if (file->f_op->open) {
file->private_data = c;
if (file->f_op->open)
err = file->f_op->open(inode,file);
}
fail:
mutex_unlock(&misc_mtx);
return err;
......@@ -169,7 +174,9 @@ static const struct file_operations misc_fops = {
* the minor number requested is used.
*
* The structure passed is linked into the kernel and may not be
* destroyed until it has been unregistered.
* destroyed until it has been unregistered. By default, an open()
* syscall to the device sets file->private_data to point to the
* structure. Drivers don't need open in fops for this.
*
* A zero is returned on success and a negative errno code for
* failure.
......@@ -205,8 +212,9 @@ int misc_register(struct miscdevice * misc)
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
misc->this_device =
device_create_with_groups(misc_class, misc->parent, dev,
misc, misc->groups, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
......
......@@ -355,7 +355,7 @@ static inline bool use_multiport(struct ports_device *portdev)
* early_init
*/
if (!portdev->vdev)
return 0;
return false;
return __virtio_test_bit(portdev->vdev, VIRTIO_CONSOLE_F_MULTIPORT);
}
......
......@@ -1237,6 +1237,8 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf,
unsigned char *tail;
int i;
howmany = 0;
end_offset_plus1 = bufpos >>
channel->log2_element_size;
......
......@@ -31,7 +31,7 @@ MODULE_LICENSE("GPL v2");
static const char xillyname[] = "xillybus_of";
/* Match table for of_platform binding */
static struct of_device_id xillybus_of_match[] = {
static const struct of_device_id xillybus_of_match[] = {
{ .compatible = "xillybus,xillybus-1.00.a", },
{ .compatible = "xlnx,xillybus-1.00.a", }, /* Deprecated */
{}
......
......@@ -55,6 +55,16 @@ config EXTCON_MAX77693
Maxim MAX77693 PMIC. The MAX77693 MUIC is a USB port accessory
detector and switch.
config EXTCON_MAX77843
tristate "MAX77843 EXTCON Support"
depends on MFD_MAX77843
select IRQ_DOMAIN
select REGMAP_I2C
help
If you say yes here you get support for the MUIC device of
Maxim MAX77843. The MAX77843 MUIC is a USB port accessory
detector add switch.
config EXTCON_MAX8997
tristate "MAX8997 EXTCON Support"
depends on MFD_MAX8997 && IRQ_DOMAIN
......@@ -93,4 +103,11 @@ config EXTCON_SM5502
Silicon Mitus SM5502. The SM5502 is a USB port accessory
detector and switch.
config EXTCON_USB_GPIO
tristate "USB GPIO extcon support"
depends on GPIOLIB
help
Say Y here to enable GPIO based USB cable detection extcon support.
Used typically if GPIO is used for USB ID pin detection.
endif # MULTISTATE_SWITCH
......@@ -2,13 +2,15 @@
# Makefile for external connector class (extcon) devices
#
obj-$(CONFIG_EXTCON) += extcon-class.o
obj-$(CONFIG_EXTCON) += extcon.o
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o
obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o
obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o
obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o
......@@ -136,18 +136,35 @@ static const char *arizona_cable[] = {
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
unsigned int magic)
static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
bool clamp)
{
struct arizona *arizona = info->arizona;
unsigned int mask = 0, val = 0;
int ret;
switch (arizona->type) {
case WM5110:
mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
ARIZONA_HP1L_SHRTI;
if (clamp)
val = ARIZONA_HP1L_SHRTO;
else
val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
break;
default:
mask = ARIZONA_RMV_SHRT_HP1L;
if (clamp)
val = ARIZONA_RMV_SHRT_HP1L;
break;
};
mutex_lock(&arizona->dapm->card->dapm_mutex);
arizona->hpdet_magic = magic;
arizona->hpdet_clamp = clamp;
/* Keep the HP output stages disabled while doing the magic */
if (magic) {
/* Keep the HP output stages disabled while doing the clamp */
if (clamp) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT1L_ENA |
......@@ -158,20 +175,20 @@ static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
ret);
}
ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000,
magic);
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
mask, val);
if (ret != 0)
dev_warn(arizona->dev, "Failed to do magic: %d\n",
dev_warn(arizona->dev, "Failed to do clamp: %d\n",
ret);
ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000,
magic);
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
mask, val);
if (ret != 0)
dev_warn(arizona->dev, "Failed to do magic: %d\n",
dev_warn(arizona->dev, "Failed to do clamp: %d\n",
ret);
/* Restore the desired state while not doing the magic */
if (!magic) {
/* Restore the desired state while not doing the clamp */
if (!clamp) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT1L_ENA |
......@@ -603,7 +620,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
0);
arizona_extcon_do_magic(info, 0);
arizona_extcon_hp_clamp(info, false);
if (id_gpio)
gpio_set_value_cansleep(id_gpio, 0);
......@@ -648,7 +665,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
if (info->mic)
arizona_stop_mic(info);
arizona_extcon_do_magic(info, 0x4000);
arizona_extcon_hp_clamp(info, true);
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
......@@ -699,7 +716,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
info->hpdet_active = true;
arizona_extcon_do_magic(info, 0x4000);
arizona_extcon_hp_clamp(info, true);
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
......
......@@ -539,8 +539,6 @@ static void max14577_muic_irq_work(struct work_struct *work)
dev_err(info->dev, "failed to handle MUIC interrupt\n");
mutex_unlock(&info->mutex);
return;
}
/*
......@@ -730,8 +728,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
" error :%d)\n",
"failed: irq request (IRQ: %d, error :%d)\n",
muic_irq->irq, ret);
return ret;
}
......
......@@ -190,8 +190,8 @@ enum max77693_muic_acc_type {
/* The below accessories have same ADC value so ADCLow and
ADC1K bit is used to separate specific accessory */
/* ADC|VBVolot|ADCLow|ADC1K| */
MAX77693_MUIC_GND_USB_OTG = 0x100, /* 0x0| 0| 0| 0| */
MAX77693_MUIC_GND_USB_OTG_VB = 0x104, /* 0x0| 1| 0| 0| */
MAX77693_MUIC_GND_USB_HOST = 0x100, /* 0x0| 0| 0| 0| */
MAX77693_MUIC_GND_USB_HOST_VB = 0x104, /* 0x0| 1| 0| 0| */
MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* 0x0| 0| 1| 0| */
MAX77693_MUIC_GND_MHL = 0x103, /* 0x0| 0| 1| 1| */
MAX77693_MUIC_GND_MHL_VB = 0x107, /* 0x0| 1| 1| 1| */
......@@ -228,7 +228,7 @@ static const char *max77693_extcon_cable[] = {
[EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger",
[EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream",
[EXTCON_CABLE_MHL] = "MHL",
[EXTCON_CABLE_MHL_TA] = "MHL_TA",
[EXTCON_CABLE_MHL_TA] = "MHL-TA",
[EXTCON_CABLE_JIG_USB_ON] = "JIG-USB-ON",
[EXTCON_CABLE_JIG_USB_OFF] = "JIG-USB-OFF",
[EXTCON_CABLE_JIG_UART_OFF] = "JIG-UART-OFF",
......@@ -403,8 +403,8 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info,
/**
* [0x1|VBVolt|ADCLow|ADC1K]
* [0x1| 0| 0| 0] USB_OTG
* [0x1| 1| 0| 0] USB_OTG_VB
* [0x1| 0| 0| 0] USB_HOST
* [0x1| 1| 0| 0] USB_HSOT_VB
* [0x1| 0| 1| 0] Audio Video cable with load
* [0x1| 0| 1| 1] MHL without charging cable
* [0x1| 1| 1| 1] MHL with charging cable
......@@ -523,7 +523,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
* - Support charging and data connection through micro-usb port
* if USB cable is connected between target and host
* device.
* - Support OTG device (Mouse/Keyboard)
* - Support OTG(On-The-Go) device (Ex: Mouse/Keyboard)
*/
ret = max77693_muic_set_path(info, info->path_usb, attached);
if (ret < 0)
......@@ -609,9 +609,9 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
MAX77693_CABLE_GROUP_ADC_GND, &attached);
switch (cable_type_gnd) {
case MAX77693_MUIC_GND_USB_OTG:
case MAX77693_MUIC_GND_USB_OTG_VB:
/* USB_OTG, PATH: AP_USB */
case MAX77693_MUIC_GND_USB_HOST:
case MAX77693_MUIC_GND_USB_HOST_VB:
/* USB_HOST, PATH: AP_USB */
ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
if (ret < 0)
return ret;
......@@ -704,7 +704,7 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
switch (cable_type) {
case MAX77693_MUIC_ADC_GROUND:
/* USB_OTG/MHL/Audio */
/* USB_HOST/MHL/Audio */
max77693_muic_adc_ground_handler(info);
break;
case MAX77693_MUIC_ADC_FACTORY_MODE_USB_OFF:
......@@ -823,19 +823,19 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
case MAX77693_MUIC_GND_MHL:
case MAX77693_MUIC_GND_MHL_VB:
/*
* MHL cable with MHL_TA(USB/TA) cable
* MHL cable with MHL-TA(USB/TA) cable
* - MHL cable include two port(HDMI line and separate
* micro-usb port. When the target connect MHL cable,
* extcon driver check whether MHL_TA(USB/TA) cable is
* connected. If MHL_TA cable is connected, extcon
* extcon driver check whether MHL-TA(USB/TA) cable is
* connected. If MHL-TA cable is connected, extcon
* driver notify state to notifiee for charging battery.
*
* Features of 'MHL_TA(USB/TA) with MHL cable'
* Features of 'MHL-TA(USB/TA) with MHL cable'
* - Support MHL
* - Support charging through micro-usb port without
* data connection
*/
extcon_set_cable_state(info->edev, "MHL_TA", attached);
extcon_set_cable_state(info->edev, "MHL-TA", attached);
if (!cable_attached)
extcon_set_cable_state(info->edev,
"MHL", cable_attached);
......@@ -886,7 +886,7 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
* - Support charging and data connection through micro-
* usb port if USB cable is connected between target
* and host device
* - Support OTG device (Mouse/Keyboard)
* - Support OTG(On-The-Go) device (Ex: Mouse/Keyboard)
*/
ret = max77693_muic_set_path(info, info->path_usb,
attached);
......@@ -1019,8 +1019,6 @@ static void max77693_muic_irq_work(struct work_struct *work)
dev_err(info->dev, "failed to handle MUIC interrupt\n");
mutex_unlock(&info->mutex);
return;
}
static irqreturn_t max77693_muic_irq_handler(int irq, void *data)
......@@ -1171,8 +1169,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
" error :%d)\n",
"failed: irq request (IRQ: %d, error :%d)\n",
muic_irq->irq, ret);
return ret;
}
......
此差异已折叠。
......@@ -579,8 +579,6 @@ static void max8997_muic_irq_work(struct work_struct *work)
dev_err(info->dev, "failed to handle MUIC interrupt\n");
mutex_unlock(&info->mutex);
return;
}
static irqreturn_t max8997_muic_irq_handler(int irq, void *data)
......@@ -689,8 +687,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
" error :%d)\n",
"failed: irq request (IRQ: %d, error :%d)\n",
muic_irq->irq, ret);
goto err_irq;
}
......
......@@ -582,10 +582,8 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c,
return -EINVAL;
info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&i2c->dev, "failed to allocate memory\n");
if (!info)
return -ENOMEM;
}
i2c_set_clientdata(i2c, info);
info->dev = &i2c->dev;
......@@ -681,7 +679,7 @@ static int rt8973a_muic_i2c_remove(struct i2c_client *i2c)
return 0;
}
static struct of_device_id rt8973a_dt_match[] = {
static const struct of_device_id rt8973a_dt_match[] = {
{ .compatible = "richtek,rt8973a-muic" },
{ },
};
......
......@@ -359,8 +359,8 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
break;
default:
dev_dbg(info->dev,
"cannot identify the cable type: adc(0x%x) "
"dev_type1(0x%x)\n", adc, dev_type1);
"cannot identify the cable type: adc(0x%x)\n",
adc);
return -EINVAL;
};
break;
......@@ -659,7 +659,7 @@ static int sm5502_muic_i2c_remove(struct i2c_client *i2c)
return 0;
}
static struct of_device_id sm5502_dt_match[] = {
static const struct of_device_id sm5502_dt_match[] = {
{ .compatible = "siliconmitus,sm5502-muic" },
{ },
};
......
/**
* drivers/extcon/extcon-usb-gpio.c - USB GPIO extcon driver
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
* Author: Roger Quadros <rogerq@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <linux/extcon.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
struct usb_extcon_info {
struct device *dev;
struct extcon_dev *edev;
struct gpio_desc *id_gpiod;
int id_irq;
unsigned long debounce_jiffies;
struct delayed_work wq_detcable;
};
/* List of detectable cables */
enum {
EXTCON_CABLE_USB = 0,
EXTCON_CABLE_USB_HOST,
EXTCON_CABLE_END,
};
static const char *usb_extcon_cable[] = {
[EXTCON_CABLE_USB] = "USB",
[EXTCON_CABLE_USB_HOST] = "USB-HOST",
NULL,
};
static void usb_extcon_detect_cable(struct work_struct *work)
{
int id;
struct usb_extcon_info *info = container_of(to_delayed_work(work),
struct usb_extcon_info,
wq_detcable);
/* check ID and update cable state */
id = gpiod_get_value_cansleep(info->id_gpiod);
if (id) {
/*
* ID = 1 means USB HOST cable detached.
* As we don't have event for USB peripheral cable attached,
* we simulate USB peripheral attach here.
*/
extcon_set_cable_state(info->edev,
usb_extcon_cable[EXTCON_CABLE_USB_HOST],
false);
extcon_set_cable_state(info->edev,
usb_extcon_cable[EXTCON_CABLE_USB],
true);
} else {
/*
* ID = 0 means USB HOST cable attached.
* As we don't have event for USB peripheral cable detached,
* we simulate USB peripheral detach here.
*/
extcon_set_cable_state(info->edev,
usb_extcon_cable[EXTCON_CABLE_USB],
false);
extcon_set_cable_state(info->edev,
usb_extcon_cable[EXTCON_CABLE_USB_HOST],
true);
}
}
static irqreturn_t usb_irq_handler(int irq, void *dev_id)
{
struct usb_extcon_info *info = dev_id;
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
info->debounce_jiffies);
return IRQ_HANDLED;
}
static int usb_extcon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct usb_extcon_info *info;
int ret;
if (!np)
return -EINVAL;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->dev = dev;
info->id_gpiod = devm_gpiod_get(&pdev->dev, "id");
if (IS_ERR(info->id_gpiod)) {
dev_err(dev, "failed to get ID GPIO\n");
return PTR_ERR(info->id_gpiod);
}
ret = gpiod_set_debounce(info->id_gpiod,
USB_GPIO_DEBOUNCE_MS * 1000);
if (ret < 0)
info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS);
INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
info->id_irq = gpiod_to_irq(info->id_gpiod);
if (info->id_irq < 0) {
dev_err(dev, "failed to get ID IRQ\n");
return info->id_irq;
}
ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
usb_irq_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
pdev->name, info);
if (ret < 0) {
dev_err(dev, "failed to request handler for ID IRQ\n");
return ret;
}
info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
ret = devm_extcon_dev_register(dev, info->edev);
if (ret < 0) {
dev_err(dev, "failed to register extcon device\n");
return ret;
}
platform_set_drvdata(pdev, info);
device_init_wakeup(dev, 1);
/* Perform initial detection */
usb_extcon_detect_cable(&info->wq_detcable.work);
return 0;
}
static int usb_extcon_remove(struct platform_device *pdev)
{
struct usb_extcon_info *info = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&info->wq_detcable);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int usb_extcon_suspend(struct device *dev)
{
struct usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
if (device_may_wakeup(dev)) {
ret = enable_irq_wake(info->id_irq);
if (ret)
return ret;
}
/*
* We don't want to process any IRQs after this point
* as GPIOs used behind I2C subsystem might not be
* accessible until resume completes. So disable IRQ.
*/
disable_irq(info->id_irq);
return ret;
}
static int usb_extcon_resume(struct device *dev)
{
struct usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
if (device_may_wakeup(dev)) {
ret = disable_irq_wake(info->id_irq);
if (ret)
return ret;
}
enable_irq(info->id_irq);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(usb_extcon_pm_ops,
usb_extcon_suspend, usb_extcon_resume);
static const struct of_device_id usb_extcon_dt_match[] = {
{ .compatible = "linux,extcon-usb-gpio", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, usb_extcon_dt_match);
static struct platform_driver usb_extcon_driver = {
.probe = usb_extcon_probe,
.remove = usb_extcon_remove,
.driver = {
.name = "extcon-usb-gpio",
.pm = &usb_extcon_pm_ops,
.of_match_table = usb_extcon_dt_match,
},
};
module_platform_driver(usb_extcon_driver);
MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
MODULE_DESCRIPTION("USB GPIO extcon driver");
MODULE_LICENSE("GPL v2");
......@@ -158,6 +158,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
/* Optional callback given by the user */
if (edev->print_name) {
int ret = edev->print_name(edev, buf);
if (ret >= 0)
return ret;
}
......@@ -444,6 +445,9 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
const char *extcon_name, const char *cable_name,
struct notifier_block *nb)
{
unsigned long flags;
int ret;
if (!obj || !cable_name || !nb)
return -EINVAL;
......@@ -461,8 +465,11 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
obj->internal_nb.notifier_call = _call_per_cable;
return raw_notifier_chain_register(&obj->edev->nh,
spin_lock_irqsave(&obj->edev->lock, flags);
ret = raw_notifier_chain_register(&obj->edev->nh,
&obj->internal_nb);
spin_unlock_irqrestore(&obj->edev->lock, flags);
return ret;
} else {
struct class_dev_iter iter;
struct extcon_dev *extd;
......@@ -495,10 +502,17 @@ EXPORT_SYMBOL_GPL(extcon_register_interest);
*/
int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
{
unsigned long flags;
int ret;
if (!obj)
return -EINVAL;
return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
spin_lock_irqsave(&obj->edev->lock, flags);
ret = raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
spin_unlock_irqrestore(&obj->edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_unregister_interest);
......@@ -515,7 +529,14 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
int extcon_register_notifier(struct extcon_dev *edev,
struct notifier_block *nb)
{
return raw_notifier_chain_register(&edev->nh, nb);
unsigned long flags;
int ret;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_register(&edev->nh, nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_register_notifier);
......@@ -527,7 +548,14 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier);
int extcon_unregister_notifier(struct extcon_dev *edev,
struct notifier_block *nb)
{
return raw_notifier_chain_unregister(&edev->nh, nb);
unsigned long flags;
int ret;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_unregister(&edev->nh, nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
......
......@@ -71,7 +71,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
struct vmbus_channel_msginfo *open_info = NULL;
void *in, *out;
unsigned long flags;
int ret, t, err = 0;
int ret, err = 0;
unsigned long t;
spin_lock_irqsave(&newchannel->lock, flags);
if (newchannel->state == CHANNEL_OPEN_STATE) {
......@@ -89,9 +90,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
get_order(send_ringbuffer_size + recv_ringbuffer_size));
if (!out)
return -ENOMEM;
if (!out) {
err = -ENOMEM;
goto error0;
}
in = (void *)((unsigned long)out + send_ringbuffer_size);
......@@ -135,7 +137,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
GFP_KERNEL);
if (!open_info) {
err = -ENOMEM;
goto error0;
goto error_gpadl;
}
init_completion(&open_info->waitevent);
......@@ -151,7 +153,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (userdatalen > MAX_USER_DEFINED_BYTES) {
err = -EINVAL;
goto error0;
goto error_gpadl;
}
if (userdatalen)
......@@ -195,10 +197,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
error_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);
newchannel->state = CHANNEL_OPEN_STATE;
return err;
}
EXPORT_SYMBOL_GPL(vmbus_open);
......@@ -534,6 +540,12 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
free_pages((unsigned long)channel->ringbuffer_pages,
get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
/*
* If the channel has been rescinded; process device removal.
*/
if (channel->rescind)
hv_process_channel_removal(channel,
channel->offermsg.child_relid);
return ret;
}
......@@ -569,23 +581,9 @@ void vmbus_close(struct vmbus_channel *channel)
}
EXPORT_SYMBOL_GPL(vmbus_close);
/**
* vmbus_sendpacket() - Send the specified buffer on the given channel
* @channel: Pointer to vmbus_channel structure.
* @buffer: Pointer to the buffer you want to receive the data into.
* @bufferlen: Maximum size of what the the buffer will hold
* @requestid: Identifier of the request
* @type: Type of packet that is being send e.g. negotiate, time
* packet etc.
*
* Sends data in @buffer directly to hyper-v via the vmbus
* This will send the data unparsed to hyper-v.
*
* Mainly used by Hyper-V drivers.
*/
int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u64 requestid,
enum vmbus_packet_type type, u32 flags)
enum vmbus_packet_type type, u32 flags, bool kick_q)
{
struct vmpacket_descriptor desc;
u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
......@@ -613,21 +611,61 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
if (ret == 0 && signal)
/*
* Signalling the host is conditional on many factors:
* 1. The ring state changed from being empty to non-empty.
* This is tracked by the variable "signal".
* 2. The variable kick_q tracks if more data will be placed
* on the ring. We will not signal if more data is
* to be placed.
*
* If we cannot write to the ring-buffer; signal the host
* even if we may not have written anything. This is a rare
* enough condition that it should not matter.
*/
if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel);
return ret;
}
EXPORT_SYMBOL(vmbus_sendpacket_ctl);
/**
* vmbus_sendpacket() - Send the specified buffer on the given channel
* @channel: Pointer to vmbus_channel structure.
* @buffer: Pointer to the buffer you want to receive the data into.
* @bufferlen: Maximum size of what the the buffer will hold
* @requestid: Identifier of the request
* @type: Type of packet that is being send e.g. negotiate, time
* packet etc.
*
* Sends data in @buffer directly to hyper-v via the vmbus
* This will send the data unparsed to hyper-v.
*
* Mainly used by Hyper-V drivers.
*/
int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u64 requestid,
enum vmbus_packet_type type, u32 flags)
{
return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid,
type, flags, true);
}
EXPORT_SYMBOL(vmbus_sendpacket);
/*
* vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
* packets using a GPADL Direct packet type.
* vmbus_sendpacket_pagebuffer_ctl - Send a range of single-page buffer
* packets using a GPADL Direct packet type. This interface allows you
* to control notifying the host. This will be useful for sending
* batched data. Also the sender can control the send flags
* explicitly.
*/
int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
struct hv_page_buffer pagebuffers[],
u32 pagecount, void *buffer, u32 bufferlen,
u64 requestid)
u64 requestid,
u32 flags,
bool kick_q)
{
int ret;
int i;
......@@ -655,7 +693,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
/* Setup the descriptor */
desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
desc.flags = flags;
desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
desc.length8 = (u16)(packetlen_aligned >> 3);
desc.transactionid = requestid;
......@@ -676,11 +714,40 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
if (ret == 0 && signal)
/*
* Signalling the host is conditional on many factors:
* 1. The ring state changed from being empty to non-empty.
* This is tracked by the variable "signal".
* 2. The variable kick_q tracks if more data will be placed
* on the ring. We will not signal if more data is
* to be placed.
*
* If we cannot write to the ring-buffer; signal the host
* even if we may not have written anything. This is a rare
* enough condition that it should not matter.
*/
if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel);
return ret;
}
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl);
/*
* vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
* packets using a GPADL Direct packet type.
*/
int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
struct hv_page_buffer pagebuffers[],
u32 pagecount, void *buffer, u32 bufferlen,
u64 requestid)
{
u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount,
buffer, bufferlen, requestid,
flags, true);
}
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
/*
......
......@@ -32,12 +32,6 @@
#include "hyperv_vmbus.h"
struct vmbus_channel_message_table_entry {
enum vmbus_channel_message_type message_type;
void (*message_handler)(struct vmbus_channel_message_header *msg);
};
/**
* vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
* @icmsghdrp: Pointer to msg header structure
......@@ -139,54 +133,29 @@ 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);
spin_lock_init(&channel->inbound_lock);
spin_lock_init(&channel->lock);
INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list);
channel->controlwq = create_workqueue("hv_vmbus_ctl");
if (!channel->controlwq) {
kfree(channel);
return NULL;
}
return channel;
}
/*
* release_hannel - Release the vmbus channel object itself
*/
static void release_channel(struct work_struct *work)
{
struct vmbus_channel *channel = container_of(work,
struct vmbus_channel,
work);
destroy_workqueue(channel->controlwq);
kfree(channel);
}
/*
* free_channel - Release the resources used by the vmbus channel object
*/
static void free_channel(struct vmbus_channel *channel)
{
/*
* We have to release the channel's workqueue/thread in the vmbus's
* workqueue/thread context
* ie we can't destroy ourselves.
*/
INIT_WORK(&channel->work, release_channel);
queue_work(vmbus_connection.work_queue, &channel->work);
kfree(channel);
}
static void percpu_channel_enq(void *arg)
......@@ -204,33 +173,21 @@ static void percpu_channel_deq(void *arg)
list_del(&channel->percpu_list);
}
/*
* vmbus_process_rescind_offer -
* Rescind the offer by initiating a device removal
*/
static void vmbus_process_rescind_offer(struct work_struct *work)
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
{
struct vmbus_channel *channel = container_of(work,
struct vmbus_channel,
work);
struct vmbus_channel_relid_released msg;
unsigned long flags;
struct vmbus_channel *primary_channel;
struct vmbus_channel_relid_released msg;
struct device *dev;
if (channel->device_obj) {
dev = get_device(&channel->device_obj->device);
if (dev) {
vmbus_device_unregister(channel->device_obj);
put_device(dev);
}
}
memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
msg.child_relid = channel->offermsg.child_relid;
msg.child_relid = relid;
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
if (channel == NULL)
return;
if (channel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(channel->target_cpu,
......@@ -259,7 +216,6 @@ void vmbus_free_channels(void)
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
vmbus_device_unregister(channel->device_obj);
kfree(channel->device_obj);
free_channel(channel);
}
}
......@@ -268,15 +224,11 @@ void vmbus_free_channels(void)
* vmbus_process_offer - Process the offer by creating a channel/device
* associated with this offer
*/
static void vmbus_process_offer(struct work_struct *work)
static void vmbus_process_offer(struct vmbus_channel *newchannel)
{
struct vmbus_channel *newchannel = container_of(work,
struct vmbus_channel,
work);
struct vmbus_channel *channel;
bool fnew = true;
bool enq = false;
int ret;
unsigned long flags;
/* Make sure this is a new offer */
......@@ -335,10 +287,11 @@ static void vmbus_process_offer(struct work_struct *work)
}
newchannel->state = CHANNEL_OPEN_STATE;
channel->num_sc++;
if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel);
goto done_init_rescind;
return;
}
goto err_free_chan;
......@@ -361,33 +314,35 @@ static void vmbus_process_offer(struct work_struct *work)
&newchannel->offermsg.offer.if_instance,
newchannel);
if (!newchannel->device_obj)
goto err_free_chan;
goto err_deq_chan;
/*
* Add the new device to the bus. This will kick off device-driver
* binding which eventually invokes the device driver's AddDevice()
* method.
*/
ret = vmbus_device_register(newchannel->device_obj);
if (ret != 0) {
if (vmbus_device_register(newchannel->device_obj) != 0) {
pr_err("unable to add child device object (relid %d)\n",
newchannel->offermsg.child_relid);
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_del(&newchannel->listentry);
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
newchannel->offermsg.child_relid);
kfree(newchannel->device_obj);
goto err_free_chan;
goto err_deq_chan;
}
done_init_rescind:
spin_lock_irqsave(&newchannel->lock, flags);
/* The next possible work is rescind handling */
INIT_WORK(&newchannel->work, vmbus_process_rescind_offer);
/* Check if rescind offer was already received */
if (newchannel->rescind)
queue_work(newchannel->controlwq, &newchannel->work);
spin_unlock_irqrestore(&newchannel->lock, flags);
return;
err_deq_chan:
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_del(&newchannel->listentry);
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
if (newchannel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(newchannel->target_cpu,
percpu_channel_deq, newchannel, true);
} else {
percpu_channel_deq(newchannel);
put_cpu();
}
err_free_chan:
free_channel(newchannel);
}
......@@ -411,6 +366,8 @@ static const struct hv_vmbus_device_id hp_devs[] = {
{ HV_SCSI_GUID, },
/* Network */
{ HV_NIC_GUID, },
/* NetworkDirect Guest RDMA */
{ HV_ND_GUID, },
};
......@@ -511,8 +468,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
newchannel->monitor_grp = (u8)offer->monitorid / 32;
newchannel->monitor_bit = (u8)offer->monitorid % 32;
INIT_WORK(&newchannel->work, vmbus_process_offer);
queue_work(newchannel->controlwq, &newchannel->work);
vmbus_process_offer(newchannel);
}
/*
......@@ -525,28 +481,34 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
struct vmbus_channel_rescind_offer *rescind;
struct vmbus_channel *channel;
unsigned long flags;
struct device *dev;
rescind = (struct vmbus_channel_rescind_offer *)hdr;
channel = relid2channel(rescind->child_relid);
if (channel == NULL)
/* Just return here, no channel found */
if (channel == NULL) {
hv_process_channel_removal(NULL, rescind->child_relid);
return;
}
spin_lock_irqsave(&channel->lock, flags);
channel->rescind = true;
/*
* channel->work.func != vmbus_process_rescind_offer means we are still
* processing offer request and the rescind offer processing should be
* postponed. It will be done at the very end of vmbus_process_offer()
* as rescind flag is being checked there.
*/
if (channel->work.func == vmbus_process_rescind_offer)
/* work is initialized for vmbus_process_rescind_offer() from
* vmbus_process_offer() where the channel got created */
queue_work(channel->controlwq, &channel->work);
spin_unlock_irqrestore(&channel->lock, flags);
if (channel->device_obj) {
/*
* We will have to unregister this device from the
* driver core.
*/
dev = get_device(&channel->device_obj->device);
if (dev) {
vmbus_device_unregister(channel->device_obj);
put_device(dev);
}
} else {
hv_process_channel_removal(channel,
channel->offermsg.child_relid);
}
}
/*
......@@ -731,25 +693,25 @@ static void vmbus_onversion_response(
}
/* Channel message dispatch table */
static struct vmbus_channel_message_table_entry
struct vmbus_channel_message_table_entry
channel_message_table[CHANNELMSG_COUNT] = {
{CHANNELMSG_INVALID, NULL},
{CHANNELMSG_OFFERCHANNEL, vmbus_onoffer},
{CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind},
{CHANNELMSG_REQUESTOFFERS, NULL},
{CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered},
{CHANNELMSG_OPENCHANNEL, NULL},
{CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result},
{CHANNELMSG_CLOSECHANNEL, NULL},
{CHANNELMSG_GPADL_HEADER, NULL},
{CHANNELMSG_GPADL_BODY, NULL},
{CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created},
{CHANNELMSG_GPADL_TEARDOWN, NULL},
{CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown},
{CHANNELMSG_RELID_RELEASED, NULL},
{CHANNELMSG_INITIATE_CONTACT, NULL},
{CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response},
{CHANNELMSG_UNLOAD, NULL},
{CHANNELMSG_INVALID, 0, NULL},
{CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer},
{CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind},
{CHANNELMSG_REQUESTOFFERS, 0, NULL},
{CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered},
{CHANNELMSG_OPENCHANNEL, 0, NULL},
{CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result},
{CHANNELMSG_CLOSECHANNEL, 0, NULL},
{CHANNELMSG_GPADL_HEADER, 0, NULL},
{CHANNELMSG_GPADL_BODY, 0, NULL},
{CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created},
{CHANNELMSG_GPADL_TEARDOWN, 0, NULL},
{CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown},
{CHANNELMSG_RELID_RELEASED, 0, NULL},
{CHANNELMSG_INITIATE_CONTACT, 0, NULL},
{CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response},
{CHANNELMSG_UNLOAD, 0, NULL},
};
/*
......@@ -787,7 +749,7 @@ int vmbus_request_offers(void)
{
struct vmbus_channel_message_header *msg;
struct vmbus_channel_msginfo *msginfo;
int ret, t;
int ret;
msginfo = kmalloc(sizeof(*msginfo) +
sizeof(struct vmbus_channel_message_header),
......@@ -795,8 +757,6 @@ int vmbus_request_offers(void)
if (!msginfo)
return -ENOMEM;
init_completion(&msginfo->waitevent);
msg = (struct vmbus_channel_message_header *)msginfo->msg;
msg->msgtype = CHANNELMSG_REQUESTOFFERS;
......@@ -810,14 +770,6 @@ int vmbus_request_offers(void)
goto cleanup;
}
t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
if (t == 0) {
ret = -ETIMEDOUT;
goto cleanup;
}
cleanup:
kfree(msginfo);
......@@ -826,9 +778,8 @@ int vmbus_request_offers(void)
/*
* Retrieve the (sub) channel on which to send an outgoing request.
* When a primary channel has multiple sub-channels, we choose a
* channel whose VCPU binding is closest to the VCPU on which
* this call is being made.
* When a primary channel has multiple sub-channels, we try to
* distribute the load equally amongst all available channels.
*/
struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
{
......@@ -836,11 +787,19 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
int cur_cpu;
struct vmbus_channel *cur_channel;
struct vmbus_channel *outgoing_channel = primary;
int cpu_distance, new_cpu_distance;
int next_channel;
int i = 1;
if (list_empty(&primary->sc_list))
return outgoing_channel;
next_channel = primary->next_oc++;
if (next_channel > (primary->num_sc)) {
primary->next_oc = 0;
return outgoing_channel;
}
cur_cpu = hv_context.vp_index[get_cpu()];
put_cpu();
list_for_each_safe(cur, tmp, &primary->sc_list) {
......@@ -851,18 +810,10 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
if (cur_channel->target_vp == cur_cpu)
return cur_channel;
cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ?
(outgoing_channel->target_vp - cur_cpu) :
(cur_cpu - outgoing_channel->target_vp));
new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ?
(cur_channel->target_vp - cur_cpu) :
(cur_cpu - cur_channel->target_vp));
if (cpu_distance < new_cpu_distance)
continue;
if (i == next_channel)
return cur_channel;
outgoing_channel = cur_channel;
i++;
}
return outgoing_channel;
......
......@@ -216,10 +216,21 @@ int vmbus_connect(void)
cleanup:
pr_err("Unable to connect to host\n");
vmbus_connection.conn_state = DISCONNECTED;
vmbus_disconnect();
kfree(msginfo);
if (vmbus_connection.work_queue)
return ret;
}
void vmbus_disconnect(void)
{
if (vmbus_connection.work_queue) {
drain_workqueue(vmbus_connection.work_queue);
destroy_workqueue(vmbus_connection.work_queue);
}
if (vmbus_connection.int_page) {
free_pages((unsigned long)vmbus_connection.int_page, 0);
......@@ -230,10 +241,6 @@ int vmbus_connect(void)
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
vmbus_connection.monitor_pages[0] = NULL;
vmbus_connection.monitor_pages[1] = NULL;
kfree(msginfo);
return ret;
}
/*
......@@ -311,10 +318,8 @@ static void process_chn_event(u32 relid)
*/
channel = pcpu_relid2channel(relid);
if (!channel) {
pr_err("channel not found for relid - %u\n", relid);
if (!channel)
return;
}
/*
* A channel once created is persistent even when there
......@@ -349,10 +354,7 @@ static void process_chn_event(u32 relid)
else
bytes_to_read = 0;
} while (read_state && (bytes_to_read != 0));
} else {
pr_err("no channel callback for relid - %u\n", relid);
}
}
/*
......@@ -420,6 +422,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;
conn_id.asu32 = 0;
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
......@@ -429,13 +432,20 @@ int vmbus_post_msg(void *buffer, size_t buflen)
* insufficient resources. Retry the operation a couple of
* times before giving up.
*/
while (retries < 10) {
while (retries < 20) {
ret = hv_post_message(conn_id, 1, buffer, buflen);
switch (ret) {
case HV_STATUS_INVALID_CONNECTION_ID:
/*
* We could get this if we send messages too
* frequently.
*/
ret = -EAGAIN;
break;
case HV_STATUS_INSUFFICIENT_MEMORY:
case HV_STATUS_INSUFFICIENT_BUFFERS:
ret = -ENOMEM;
case -ENOMEM:
break;
case HV_STATUS_SUCCESS:
return ret;
......@@ -445,7 +455,9 @@ int vmbus_post_msg(void *buffer, size_t buflen)
}
retries++;
msleep(100);
msleep(msec);
if (msec < 2048)
msec *= 2;
}
return ret;
}
......
......@@ -312,7 +312,11 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
dev->features = CLOCK_EVT_FEAT_ONESHOT;
dev->cpumask = cpumask_of(cpu);
dev->rating = 1000;
dev->owner = THIS_MODULE;
/*
* Avoid settint dev->owner = THIS_MODULE deliberately as doing so will
* result in clockevents_config_and_register() taking additional
* references to the hv_vmbus module making it impossible to unload.
*/
dev->set_mode = hv_ce_setmode;
dev->set_next_event = hv_ce_set_next_event;
......@@ -469,6 +473,20 @@ void hv_synic_init(void *arg)
return;
}
/*
* hv_synic_clockevents_cleanup - Cleanup clockevent devices
*/
void hv_synic_clockevents_cleanup(void)
{
int cpu;
if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
return;
for_each_online_cpu(cpu)
clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
}
/*
* hv_synic_cleanup - Cleanup routine for hv_synic_init().
*/
......@@ -477,11 +495,17 @@ void hv_synic_cleanup(void *arg)
union hv_synic_sint shared_sint;
union hv_synic_simp simp;
union hv_synic_siefp siefp;
union hv_synic_scontrol sctrl;
int cpu = smp_processor_id();
if (!hv_context.synic_initialized)
return;
/* Turn off clockevent device */
if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)
hv_ce_setmode(CLOCK_EVT_MODE_SHUTDOWN,
hv_context.clk_evt[cpu]);
rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
shared_sint.masked = 1;
......@@ -502,6 +526,10 @@ void hv_synic_cleanup(void *arg)
wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
free_page((unsigned long)hv_context.synic_message_page[cpu]);
free_page((unsigned long)hv_context.synic_event_page[cpu]);
/* Disable the global synic bit */
rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
sctrl.enable = 0;
wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
hv_synic_free_cpu(cpu);
}
......@@ -428,14 +428,13 @@ 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_start_pfn : covered_end_pfn defines the pages that can
* 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;
......@@ -503,6 +502,8 @@ struct hv_dynmem_device {
* Number of pages we have currently ballooned out.
*/
unsigned int num_pages_ballooned;
unsigned int num_pages_onlined;
unsigned int num_pages_added;
/*
* State to manage the ballooning (up) operation.
......@@ -534,7 +535,6 @@ struct hv_dynmem_device {
struct task_struct *thread;
struct mutex ha_region_mutex;
struct completion waiter_event;
/*
* A list of hot-add regions.
......@@ -554,46 +554,32 @@ static struct hv_dynmem_device dm_device;
static void post_status(struct hv_dynmem_device *dm);
#ifdef CONFIG_MEMORY_HOTPLUG
static void acquire_region_mutex(bool trylock)
{
if (trylock) {
reinit_completion(&dm_device.waiter_event);
while (!mutex_trylock(&dm_device.ha_region_mutex))
wait_for_completion(&dm_device.waiter_event);
} else {
mutex_lock(&dm_device.ha_region_mutex);
}
}
static void release_region_mutex(bool trylock)
{
if (trylock) {
mutex_unlock(&dm_device.ha_region_mutex);
} else {
mutex_unlock(&dm_device.ha_region_mutex);
complete(&dm_device.waiter_event);
}
}
static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
void *v)
{
struct memory_notify *mem = (struct memory_notify *)v;
switch (val) {
case MEM_GOING_ONLINE:
acquire_region_mutex(true);
mutex_lock(&dm_device.ha_region_mutex);
break;
case MEM_ONLINE:
dm_device.num_pages_onlined += mem->nr_pages;
case MEM_CANCEL_ONLINE:
release_region_mutex(true);
mutex_unlock(&dm_device.ha_region_mutex);
if (dm_device.ha_waiting) {
dm_device.ha_waiting = false;
complete(&dm_device.ol_waitevent);
}
break;
case MEM_GOING_OFFLINE:
case MEM_OFFLINE:
mutex_lock(&dm_device.ha_region_mutex);
dm_device.num_pages_onlined -= mem->nr_pages;
mutex_unlock(&dm_device.ha_region_mutex);
break;
case MEM_GOING_OFFLINE:
case MEM_CANCEL_OFFLINE:
break;
}
......@@ -646,7 +632,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
init_completion(&dm_device.ol_waitevent);
dm_device.ha_waiting = true;
release_region_mutex(false);
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));
......@@ -665,6 +651,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
}
has->ha_end_pfn -= HA_CHUNK;
has->covered_end_pfn -= processed_pfn;
mutex_lock(&dm_device.ha_region_mutex);
break;
}
......@@ -675,7 +662,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
* have not been "onlined" within the allowed time.
*/
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
acquire_region_mutex(false);
mutex_lock(&dm_device.ha_region_mutex);
post_status(&dm_device);
}
......@@ -691,8 +678,7 @@ static void hv_online_page(struct page *pg)
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->covered_start_pfn);
cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn);
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
if (((unsigned long)pg >= cur_start_pgp) &&
......@@ -704,7 +690,6 @@ static void hv_online_page(struct page *pg)
__online_page_set_limits(pg);
__online_page_increment_counters(pg);
__online_page_free(pg);
has->covered_start_pfn++;
}
}
}
......@@ -748,10 +733,9 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
* is, update it.
*/
if (has->covered_end_pfn != start_pfn) {
if (has->covered_end_pfn != start_pfn)
has->covered_end_pfn = start_pfn;
has->covered_start_pfn = start_pfn;
}
return true;
}
......@@ -794,9 +778,18 @@ static unsigned long handle_pg_range(unsigned long pg_start,
pgs_ol = has->ha_end_pfn - start_pfn;
if (pgs_ol > pfn_cnt)
pgs_ol = pfn_cnt;
hv_bring_pgs_online(start_pfn, pgs_ol);
/*
* Check if the corresponding memory block is already
* online by checking its last previously backed page.
* In case it is we need to bring rest (which was not
* backed previously) online too.
*/
if (start_pfn > has->start_pfn &&
!PageReserved(pfn_to_page(start_pfn - 1)))
hv_bring_pgs_online(start_pfn, pgs_ol);
has->covered_end_pfn += pgs_ol;
has->covered_start_pfn += pgs_ol;
pfn_cnt -= pgs_ol;
}
......@@ -857,7 +850,6 @@ static unsigned long process_hot_add(unsigned long pg_start,
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;
}
......@@ -886,7 +878,7 @@ static void hot_add_req(struct work_struct *dummy)
resp.hdr.size = sizeof(struct dm_hot_add_response);
#ifdef CONFIG_MEMORY_HOTPLUG
acquire_region_mutex(false);
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;
......@@ -918,7 +910,9 @@ static void hot_add_req(struct work_struct *dummy)
if (do_hot_add)
resp.page_count = process_hot_add(pg_start, pfn_cnt,
rg_start, rg_sz);
release_region_mutex(false);
dm->num_pages_added += resp.page_count;
mutex_unlock(&dm_device.ha_region_mutex);
#endif
/*
* The result field of the response structure has the
......@@ -982,8 +976,8 @@ static unsigned long compute_balloon_floor(void)
* 128 72 (1/2)
* 512 168 (1/4)
* 2048 360 (1/8)
* 8192 768 (1/16)
* 32768 1536 (1/32)
* 8192 744 (1/16)
* 32768 1512 (1/32)
*/
if (totalram_pages < MB2PAGES(128))
min_pages = MB2PAGES(8) + (totalram_pages >> 1);
......@@ -992,9 +986,9 @@ static unsigned long compute_balloon_floor(void)
else if (totalram_pages < MB2PAGES(2048))
min_pages = MB2PAGES(104) + (totalram_pages >> 3);
else if (totalram_pages < MB2PAGES(8192))
min_pages = MB2PAGES(256) + (totalram_pages >> 4);
min_pages = MB2PAGES(232) + (totalram_pages >> 4);
else
min_pages = MB2PAGES(512) + (totalram_pages >> 5);
min_pages = MB2PAGES(488) + (totalram_pages >> 5);
#undef MB2PAGES
return min_pages;
}
......@@ -1031,17 +1025,21 @@ static void post_status(struct hv_dynmem_device *dm)
status.hdr.trans_id = atomic_inc_return(&trans_id);
/*
* The host expects the guest to report free memory.
* Further, the host expects the pressure information to
* include the ballooned out pages.
* For a given amount of memory that we are managing, we
* need to compute a floor below which we should not balloon.
* Compute this and add it to the pressure report.
* The host expects the guest to report free and committed memory.
* Furthermore, the host expects the pressure information to include
* the ballooned out pages. For a given amount of memory that we are
* managing we need to compute a floor below which we should not
* balloon. Compute this and add it to the pressure report.
* We also need to report all offline pages (num_pages_added -
* 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_committed = vm_memory_committed() +
dm->num_pages_ballooned +
compute_balloon_floor();
dm->num_pages_ballooned +
(dm->num_pages_added > dm->num_pages_onlined ?
dm->num_pages_added - dm->num_pages_onlined : 0) +
compute_balloon_floor();
/*
* If our transaction ID is no longer current, just don't
......@@ -1083,11 +1081,12 @@ static void free_balloon_pages(struct hv_dynmem_device *dm,
static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
struct dm_balloon_response *bl_resp, int alloc_unit,
bool *alloc_error)
static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm,
unsigned int num_pages,
struct dm_balloon_response *bl_resp,
int alloc_unit)
{
int i = 0;
unsigned int i = 0;
struct page *pg;
if (num_pages < alloc_unit)
......@@ -1106,11 +1105,8 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
__GFP_NOMEMALLOC | __GFP_NOWARN,
get_order(alloc_unit << PAGE_SHIFT));
if (!pg) {
*alloc_error = true;
if (!pg)
return i * alloc_unit;
}
dm->num_pages_ballooned += alloc_unit;
......@@ -1137,14 +1133,15 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
static void balloon_up(struct work_struct *dummy)
{
int num_pages = dm_device.balloon_wrk.num_pages;
int num_ballooned = 0;
unsigned int num_pages = dm_device.balloon_wrk.num_pages;
unsigned int num_ballooned = 0;
struct dm_balloon_response *bl_resp;
int alloc_unit;
int ret;
bool alloc_error;
bool done = false;
int i;
struct sysinfo val;
unsigned long floor;
/* The host balloons pages in 2M granularity. */
WARN_ON_ONCE(num_pages % PAGES_IN_2M != 0);
......@@ -1155,6 +1152,15 @@ static void balloon_up(struct work_struct *dummy)
*/
alloc_unit = 512;
si_meminfo(&val);
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;
num_pages -= num_pages % PAGES_IN_2M;
}
while (!done) {
bl_resp = (struct dm_balloon_response *)send_buffer;
memset(send_buffer, 0, PAGE_SIZE);
......@@ -1164,18 +1170,15 @@ static void balloon_up(struct work_struct *dummy)
num_pages -= num_ballooned;
alloc_error = false;
num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
bl_resp, alloc_unit,
&alloc_error);
bl_resp, alloc_unit);
if (alloc_unit != 1 && num_ballooned == 0) {
alloc_unit = 1;
continue;
}
if ((alloc_unit == 1 && alloc_error) ||
(num_ballooned == num_pages)) {
if (num_ballooned == 0 || num_ballooned == num_pages) {
bl_resp->more_pages = 0;
done = true;
dm_device.state = DM_INITIALIZED;
......@@ -1414,7 +1417,8 @@ static void balloon_onchannelcallback(void *context)
static int balloon_probe(struct hv_device *dev,
const struct hv_vmbus_device_id *dev_id)
{
int ret, t;
int ret;
unsigned long t;
struct dm_version_request version_req;
struct dm_capabilities cap_msg;
......@@ -1439,7 +1443,6 @@ static int balloon_probe(struct hv_device *dev,
dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
init_completion(&dm_device.host_event);
init_completion(&dm_device.config_event);
init_completion(&dm_device.waiter_event);
INIT_LIST_HEAD(&dm_device.ha_region_list);
mutex_init(&dm_device.ha_region_mutex);
INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
......
......@@ -340,12 +340,8 @@ static int util_probe(struct hv_device *dev,
set_channel_read_state(dev->channel, false);
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
goto error;
hv_set_drvdata(dev, srv);
/*
* Based on the host; initialize the framework and
* service version numbers we will negotiate.
......@@ -365,6 +361,11 @@ static int util_probe(struct hv_device *dev,
hb_srv_version = HB_VERSION;
}
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
goto error;
return 0;
error:
......@@ -379,9 +380,9 @@ static int util_remove(struct hv_device *dev)
{
struct hv_util_service *srv = hv_get_drvdata(dev);
vmbus_close(dev->channel);
if (srv->util_deinit)
srv->util_deinit();
vmbus_close(dev->channel);
kfree(srv->recv_buffer);
return 0;
......
......@@ -49,6 +49,17 @@ enum hv_cpuid_function {
HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005,
};
#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE 0x400
#define HV_X64_MSR_CRASH_P0 0x40000100
#define HV_X64_MSR_CRASH_P1 0x40000101
#define HV_X64_MSR_CRASH_P2 0x40000102
#define HV_X64_MSR_CRASH_P3 0x40000103
#define HV_X64_MSR_CRASH_P4 0x40000104
#define HV_X64_MSR_CRASH_CTL 0x40000105
#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63)
/* Define version of the synthetic interrupt controller. */
#define HV_SYNIC_VERSION (1)
......@@ -572,6 +583,8 @@ extern void hv_synic_init(void *irqarg);
extern void hv_synic_cleanup(void *arg);
extern void hv_synic_clockevents_cleanup(void);
/*
* Host version information.
*/
......@@ -672,6 +685,23 @@ struct vmbus_msginfo {
extern struct vmbus_connection vmbus_connection;
enum vmbus_message_handler_type {
/* The related handler can sleep. */
VMHT_BLOCKING = 0,
/* The related handler must NOT sleep. */
VMHT_NON_BLOCKING = 1,
};
struct vmbus_channel_message_table_entry {
enum vmbus_channel_message_type message_type;
enum vmbus_message_handler_type handler_type;
void (*message_handler)(struct vmbus_channel_message_header *msg);
};
extern struct vmbus_channel_message_table_entry
channel_message_table[CHANNELMSG_COUNT];
/* General vmbus interface */
struct hv_device *vmbus_device_create(const uuid_le *type,
......@@ -692,6 +722,7 @@ void vmbus_free_channels(void);
/* Connection interface */
int vmbus_connect(void);
void vmbus_disconnect(void);
int vmbus_post_msg(void *buffer, size_t buflen);
......
......@@ -33,9 +33,12 @@
#include <linux/hyperv.h>
#include <linux/kernel_stat.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <asm/hyperv.h>
#include <asm/hypervisor.h>
#include <asm/mshyperv.h>
#include <linux/notifier.h>
#include <linux/ptrace.h>
#include "hyperv_vmbus.h"
static struct acpi_device *hv_acpi_dev;
......@@ -44,6 +47,31 @@ static struct tasklet_struct msg_dpc;
static struct completion probe_event;
static int irq;
static int hyperv_panic_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct pt_regs *regs;
regs = current_pt_regs();
wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip);
wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax);
wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx);
wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx);
wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx);
/*
* Let Hyper-V know there is crash data available
*/
wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
return NOTIFY_DONE;
}
static struct notifier_block hyperv_panic_block = {
.notifier_call = hyperv_panic_event,
};
struct resource hyperv_mmio = {
.name = "hyperv mmio",
.flags = IORESOURCE_MEM,
......@@ -507,14 +535,26 @@ static int vmbus_probe(struct device *child_device)
*/
static int vmbus_remove(struct device *child_device)
{
struct hv_driver *drv = drv_to_hv_drv(child_device->driver);
struct hv_driver *drv;
struct hv_device *dev = device_to_hv_device(child_device);
if (drv->remove)
drv->remove(dev);
else
pr_err("remove not set for driver %s\n",
dev_name(child_device));
u32 relid = dev->channel->offermsg.child_relid;
if (child_device->driver) {
drv = drv_to_hv_drv(child_device->driver);
if (drv->remove)
drv->remove(dev);
else {
hv_process_channel_removal(dev->channel, relid);
pr_err("remove not set for driver %s\n",
dev_name(child_device));
}
} else {
/*
* We don't have a driver for this device; deal with the
* rescind message by removing the channel.
*/
hv_process_channel_removal(dev->channel, relid);
}
return 0;
}
......@@ -573,6 +613,10 @@ static void vmbus_onmessage_work(struct work_struct *work)
{
struct onmessage_work_context *ctx;
/* Do not process messages if we're in DISCONNECTED state */
if (vmbus_connection.conn_state == DISCONNECTED)
return;
ctx = container_of(work, struct onmessage_work_context,
work);
vmbus_onmessage(&ctx->msg);
......@@ -613,21 +657,36 @@ static void vmbus_on_msg_dpc(unsigned long data)
void *page_addr = hv_context.synic_message_page[cpu];
struct hv_message *msg = (struct hv_message *)page_addr +
VMBUS_MESSAGE_SINT;
struct vmbus_channel_message_header *hdr;
struct vmbus_channel_message_table_entry *entry;
struct onmessage_work_context *ctx;
while (1) {
if (msg->header.message_type == HVMSG_NONE) {
if (msg->header.message_type == HVMSG_NONE)
/* no msg */
break;
} else {
hdr = (struct vmbus_channel_message_header *)msg->u.payload;
if (hdr->msgtype >= CHANNELMSG_COUNT) {
WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype);
goto msg_handled;
}
entry = &channel_message_table[hdr->msgtype];
if (entry->handler_type == VMHT_BLOCKING) {
ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
if (ctx == NULL)
continue;
INIT_WORK(&ctx->work, vmbus_onmessage_work);
memcpy(&ctx->msg, msg, sizeof(*msg));
queue_work(vmbus_connection.work_queue, &ctx->work);
}
} else
entry->message_handler(hdr);
msg_handled:
msg->header.message_type = HVMSG_NONE;
/*
......@@ -704,6 +763,39 @@ static void vmbus_isr(void)
}
}
#ifdef CONFIG_HOTPLUG_CPU
static int hyperv_cpu_disable(void)
{
return -ENOSYS;
}
static void hv_cpu_hotplug_quirk(bool vmbus_loaded)
{
static void *previous_cpu_disable;
/*
* Offlining a CPU when running on newer hypervisors (WS2012R2, Win8,
* ...) is not supported at this moment as channel interrupts are
* distributed across all of them.
*/
if ((vmbus_proto_version == VERSION_WS2008) ||
(vmbus_proto_version == VERSION_WIN7))
return;
if (vmbus_loaded) {
previous_cpu_disable = smp_ops.cpu_disable;
smp_ops.cpu_disable = hyperv_cpu_disable;
pr_notice("CPU offlining is not supported by hypervisor\n");
} else if (previous_cpu_disable)
smp_ops.cpu_disable = previous_cpu_disable;
}
#else
static void hv_cpu_hotplug_quirk(bool vmbus_loaded)
{
}
#endif
/*
* vmbus_bus_init -Main vmbus driver initialization routine.
*
......@@ -744,6 +836,16 @@ static int vmbus_bus_init(int irq)
if (ret)
goto err_alloc;
hv_cpu_hotplug_quirk(true);
/*
* Only register if the crash MSRs are available
*/
if (ms_hyperv.features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
atomic_notifier_chain_register(&panic_notifier_list,
&hyperv_panic_block);
}
vmbus_request_offers();
return 0;
......@@ -840,10 +942,8 @@ int vmbus_device_register(struct hv_device *child_device_obj)
{
int ret = 0;
static atomic_t device_num = ATOMIC_INIT(0);
dev_set_name(&child_device_obj->device, "vmbus_0_%d",
atomic_inc_return(&device_num));
dev_set_name(&child_device_obj->device, "vmbus_%d",
child_device_obj->channel->id);
child_device_obj->device.bus = &hv_bus;
child_device_obj->device.parent = &hv_acpi_dev->dev;
......@@ -992,11 +1092,19 @@ static int __init hv_acpi_init(void)
static void __exit vmbus_exit(void)
{
int cpu;
vmbus_connection.conn_state = DISCONNECTED;
hv_synic_clockevents_cleanup();
hv_remove_vmbus_irq();
vmbus_free_channels();
bus_unregister(&hv_bus);
hv_cleanup();
for_each_online_cpu(cpu)
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
acpi_bus_unregister_driver(&vmbus_acpi_driver);
hv_cpu_hotplug_quirk(false);
vmbus_disconnect();
}
......
#
# Coresight configuration
#
menuconfig CORESIGHT
bool "CoreSight Tracing Support"
select ARM_AMBA
help
This framework provides a kernel interface for the CoreSight debug
and trace drivers to register themselves with. It's intended to build
a topological view of the CoreSight components based on a DT
specification and configure the right serie of components when a
trace source gets enabled.
if CORESIGHT
config CORESIGHT_LINKS_AND_SINKS
bool "CoreSight Link and Sink drivers"
help
This enables support for CoreSight link and sink drivers that are
responsible for transporting and collecting the trace data
respectively. Link and sinks are dynamically aggregated with a trace
entity at run time to form a complete trace path.
config CORESIGHT_LINK_AND_SINK_TMC
bool "Coresight generic TMC driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Memory Controller driver.
Depending on its configuration the device can act as a link (embedded
trace router - ETR) or sink (embedded trace FIFO). The driver
complies with the generic implementation of the component without
special enhancement or added features.
config CORESIGHT_SINK_TPIU
bool "Coresight generic TPIU driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Port Interface Unit driver,
responsible for bridging the gap between the on-chip coresight
components and a trace for bridging the gap between the on-chip
coresight components and a trace port collection engine, typically
connected to an external host for use case capturing more traces than
the on-board coresight memory can handle.
config CORESIGHT_SINK_ETBV10
bool "Coresight ETBv1.0 driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Embedded Trace Buffer version 1.0 driver
that complies with the generic implementation of the component without
special enhancement or added features.
config CORESIGHT_SOURCE_ETM3X
bool "CoreSight Embedded Trace Macrocell 3.x driver"
depends on !ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for processor ETM3.x and PTM1.x modules,
which allows tracing the instructions that a processor is executing
This is primarily useful for instruction level tracing. Depending
the ETM version data tracing may also be available.
endif
......@@ -313,8 +313,8 @@ static ssize_t etb_read(struct file *file, char __user *data,
*ppos += len;
dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
__func__, len, (int) (depth * 4 - *ppos));
dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
__func__, len, (int)(depth * 4 - *ppos));
return len;
}
......
......@@ -107,7 +107,7 @@ static int replicator_remove(struct platform_device *pdev)
return 0;
}
static struct of_device_id replicator_match[] = {
static const struct of_device_id replicator_match[] = {
{.compatible = "arm,coresight-replicator"},
{}
};
......
......@@ -533,8 +533,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
*ppos += len;
dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
__func__, len, (int) (drvdata->size - *ppos));
dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
__func__, len, (int)(drvdata->size - *ppos));
return len;
}
......@@ -565,6 +565,59 @@ static const struct file_operations tmc_fops = {
.llseek = no_llseek,
};
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
unsigned long flags;
u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg;
u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr;
u32 devid;
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = clk_prepare_enable(drvdata->clk);
if (ret)
goto out;
spin_lock_irqsave(&drvdata->spinlock, flags);
CS_UNLOCK(drvdata->base);
tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ);
tmc_sts = readl_relaxed(drvdata->base + TMC_STS);
tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP);
tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP);
tmc_trg = readl_relaxed(drvdata->base + TMC_TRG);
tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL);
tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR);
tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
tmc_mode = readl_relaxed(drvdata->base + TMC_MODE);
tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR);
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
clk_disable_unprepare(drvdata->clk);
return sprintf(buf,
"Depth:\t\t0x%x\n"
"Status:\t\t0x%x\n"
"RAM read ptr:\t0x%x\n"
"RAM wrt ptr:\t0x%x\n"
"Trigger cnt:\t0x%x\n"
"Control:\t0x%x\n"
"Flush status:\t0x%x\n"
"Flush ctrl:\t0x%x\n"
"Mode:\t\t0x%x\n"
"PSRC:\t\t0x%x\n"
"DEVID:\t\t0x%x\n",
tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg,
tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid);
out:
return -EINVAL;
}
static DEVICE_ATTR_RO(status);
static ssize_t trigger_cntr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
......@@ -593,18 +646,21 @@ static DEVICE_ATTR_RW(trigger_cntr);
static struct attribute *coresight_etb_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etb);
static struct attribute *coresight_etr_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etr);
static struct attribute *coresight_etf_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etf);
......
......@@ -305,7 +305,9 @@ static int coresight_build_paths(struct coresight_device *csdev,
list_add(&csdev->path_link, path);
if (csdev->type == CORESIGHT_DEV_TYPE_SINK && csdev->activated) {
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->activated) {
if (enable)
ret = coresight_enable_path(path);
else
......
......@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
#include <linux/coresight.h>
#include <linux/cpumask.h>
#include <asm/smp_plat.h>
......@@ -104,7 +105,7 @@ static int of_coresight_alloc_memory(struct device *dev,
struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node)
{
int i = 0, ret = 0;
int i = 0, ret = 0, cpu;
struct coresight_platform_data *pdata;
struct of_endpoint endpoint, rendpoint;
struct device *rdev;
......@@ -178,17 +179,10 @@ struct coresight_platform_data *of_get_coresight_platform_data(
/* Affinity defaults to CPU0 */
pdata->cpu = 0;
dn = of_parse_phandle(node, "cpu", 0);
if (dn) {
const u32 *cell;
int len, index;
u64 hwid;
cell = of_get_property(dn, "reg", &len);
if (cell) {
hwid = of_read_number(cell, of_n_addr_cells(dn));
index = get_logical_index(hwid);
if (index != -EINVAL)
pdata->cpu = index;
for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) {
if (dn == of_get_cpu_node(cpu, NULL)) {
pdata->cpu = cpu;
break;
}
}
......
......@@ -56,9 +56,9 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE,
KBUILD_MODNAME);
if (IS_ERR(res)) {
if (!res) {
dev_err(&pdev->dev, "Failed to request PCI memory\n");
ret = PTR_ERR(res);
ret = -EBUSY;
goto out_disable;
}
......
......@@ -83,6 +83,15 @@ config FSL_IFC
bool
depends on FSL_SOC
config JZ4780_NEMC
bool "Ingenic JZ4780 SoC NEMC driver"
default y
depends on MACH_JZ4780
help
This driver is for the NAND/External Memory Controller (NEMC) in
the Ingenic JZ4780. This controller is used to handle external
memory devices such as NAND and SRAM.
source "drivers/memory/tegra/Kconfig"
endif
......@@ -13,5 +13,6 @@ obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o
obj-$(CONFIG_TEGRA_MC) += tegra/
/*
* JZ4780 NAND/external memory controller (NEMC)
*
* Copyright (c) 2015 Imagination Technologies
* Author: Alex Smith <alex@alex-smith.me.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/math64.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jz4780-nemc.h>
#define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4))
#define NEMC_NFCSR 0x50
#define NEMC_SMCR_SMT BIT(0)
#define NEMC_SMCR_BW_SHIFT 6
#define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT)
#define NEMC_SMCR_BW_8 (0 << 6)
#define NEMC_SMCR_TAS_SHIFT 8
#define NEMC_SMCR_TAS_MASK (0xf << NEMC_SMCR_TAS_SHIFT)
#define NEMC_SMCR_TAH_SHIFT 12
#define NEMC_SMCR_TAH_MASK (0xf << NEMC_SMCR_TAH_SHIFT)
#define NEMC_SMCR_TBP_SHIFT 16
#define NEMC_SMCR_TBP_MASK (0xf << NEMC_SMCR_TBP_SHIFT)
#define NEMC_SMCR_TAW_SHIFT 20
#define NEMC_SMCR_TAW_MASK (0xf << NEMC_SMCR_TAW_SHIFT)
#define NEMC_SMCR_TSTRV_SHIFT 24
#define NEMC_SMCR_TSTRV_MASK (0x3f << NEMC_SMCR_TSTRV_SHIFT)
#define NEMC_NFCSR_NFEn(n) BIT(((n) - 1) << 1)
#define NEMC_NFCSR_NFCEn(n) BIT((((n) - 1) << 1) + 1)
#define NEMC_NFCSR_TNFEn(n) BIT(16 + (n) - 1)
struct jz4780_nemc {
spinlock_t lock;
struct device *dev;
void __iomem *base;
struct clk *clk;
uint32_t clk_period;
unsigned long banks_present;
};
/**
* jz4780_nemc_num_banks() - count the number of banks referenced by a device
* @dev: device to count banks for, must be a child of the NEMC.
*
* Return: The number of unique NEMC banks referred to by the specified NEMC
* child device. Unique here means that a device that references the same bank
* multiple times in the its "reg" property will only count once.
*/
unsigned int jz4780_nemc_num_banks(struct device *dev)
{
const __be32 *prop;
unsigned int bank, count = 0;
unsigned long referenced = 0;
int i = 0;
while ((prop = of_get_address(dev->of_node, i++, NULL, NULL))) {
bank = of_read_number(prop, 1);
if (!(referenced & BIT(bank))) {
referenced |= BIT(bank);
count++;
}
}
return count;
}
EXPORT_SYMBOL(jz4780_nemc_num_banks);
/**
* jz4780_nemc_set_type() - set the type of device connected to a bank
* @dev: child device of the NEMC.
* @bank: bank number to configure.
* @type: type of device connected to the bank.
*/
void jz4780_nemc_set_type(struct device *dev, unsigned int bank,
enum jz4780_nemc_bank_type type)
{
struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent);
uint32_t nfcsr;
nfcsr = readl(nemc->base + NEMC_NFCSR);
/* TODO: Support toggle NAND devices. */
switch (type) {
case JZ4780_NEMC_BANK_SRAM:
nfcsr &= ~(NEMC_NFCSR_TNFEn(bank) | NEMC_NFCSR_NFEn(bank));
break;
case JZ4780_NEMC_BANK_NAND:
nfcsr &= ~NEMC_NFCSR_TNFEn(bank);
nfcsr |= NEMC_NFCSR_NFEn(bank);
break;
}
writel(nfcsr, nemc->base + NEMC_NFCSR);
}
EXPORT_SYMBOL(jz4780_nemc_set_type);
/**
* jz4780_nemc_assert() - (de-)assert a NAND device's chip enable pin
* @dev: child device of the NEMC.
* @bank: bank number of device.
* @assert: whether the chip enable pin should be asserted.
*
* (De-)asserts the chip enable pin for the NAND device connected to the
* specified bank.
*/
void jz4780_nemc_assert(struct device *dev, unsigned int bank, bool assert)
{
struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent);
uint32_t nfcsr;
nfcsr = readl(nemc->base + NEMC_NFCSR);
if (assert)
nfcsr |= NEMC_NFCSR_NFCEn(bank);
else
nfcsr &= ~NEMC_NFCSR_NFCEn(bank);
writel(nfcsr, nemc->base + NEMC_NFCSR);
}
EXPORT_SYMBOL(jz4780_nemc_assert);
static uint32_t jz4780_nemc_clk_period(struct jz4780_nemc *nemc)
{
unsigned long rate;
rate = clk_get_rate(nemc->clk);
if (!rate)
return 0;
/* Return in picoseconds. */
return div64_ul(1000000000000ull, rate);
}
static uint32_t jz4780_nemc_ns_to_cycles(struct jz4780_nemc *nemc, uint32_t ns)
{
return ((ns * 1000) + nemc->clk_period - 1) / nemc->clk_period;
}
static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc,
unsigned int bank,
struct device_node *node)
{
uint32_t smcr, val, cycles;
/*
* Conversion of tBP and tAW cycle counts to values supported by the
* hardware (round up to the next supported value).
*/
static const uint32_t convert_tBP_tAW[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
/* 11 - 12 -> 12 cycles */
11, 11,
/* 13 - 15 -> 15 cycles */
12, 12, 12,
/* 16 - 20 -> 20 cycles */
13, 13, 13, 13, 13,
/* 21 - 25 -> 25 cycles */
14, 14, 14, 14, 14,
/* 26 - 31 -> 31 cycles */
15, 15, 15, 15, 15, 15
};
smcr = readl(nemc->base + NEMC_SMCRn(bank));
smcr &= ~NEMC_SMCR_SMT;
if (!of_property_read_u32(node, "ingenic,nemc-bus-width", &val)) {
smcr &= ~NEMC_SMCR_BW_MASK;
switch (val) {
case 8:
smcr |= NEMC_SMCR_BW_8;
break;
default:
/*
* Earlier SoCs support a 16 bit bus width (the 4780
* does not), until those are properly supported, error.
*/
dev_err(nemc->dev, "unsupported bus width: %u\n", val);
return false;
}
}
if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) {
smcr &= ~NEMC_SMCR_TAS_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 15) {
dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= cycles << NEMC_SMCR_TAS_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) {
smcr &= ~NEMC_SMCR_TAH_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 15) {
dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= cycles << NEMC_SMCR_TAH_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tBP", &val) == 0) {
smcr &= ~NEMC_SMCR_TBP_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 31) {
dev_err(nemc->dev, "tBP %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TBP_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tAW", &val) == 0) {
smcr &= ~NEMC_SMCR_TAW_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 31) {
dev_err(nemc->dev, "tAW %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TAW_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tSTRV", &val) == 0) {
smcr &= ~NEMC_SMCR_TSTRV_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 63) {
dev_err(nemc->dev, "tSTRV %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= cycles << NEMC_SMCR_TSTRV_SHIFT;
}
writel(smcr, nemc->base + NEMC_SMCRn(bank));
return true;
}
static int jz4780_nemc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct jz4780_nemc *nemc;
struct resource *res;
struct device_node *child;
const __be32 *prop;
unsigned int bank;
unsigned long referenced;
int i, ret;
nemc = devm_kzalloc(dev, sizeof(*nemc), GFP_KERNEL);
if (!nemc)
return -ENOMEM;
spin_lock_init(&nemc->lock);
nemc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nemc->base = devm_ioremap_resource(dev, res);
if (IS_ERR(nemc->base)) {
dev_err(dev, "failed to get I/O memory\n");
return PTR_ERR(nemc->base);
}
writel(0, nemc->base + NEMC_NFCSR);
nemc->clk = devm_clk_get(dev, NULL);
if (IS_ERR(nemc->clk)) {
dev_err(dev, "failed to get clock\n");
return PTR_ERR(nemc->clk);
}
ret = clk_prepare_enable(nemc->clk);
if (ret) {
dev_err(dev, "failed to enable clock: %d\n", ret);
return ret;
}
nemc->clk_period = jz4780_nemc_clk_period(nemc);
if (!nemc->clk_period) {
dev_err(dev, "failed to calculate clock period\n");
clk_disable_unprepare(nemc->clk);
return -EINVAL;
}
/*
* Iterate over child devices, check that they do not conflict with
* each other, and register child devices for them. If a child device
* has invalid properties, it is ignored and no platform device is
* registered for it.
*/
for_each_child_of_node(nemc->dev->of_node, child) {
referenced = 0;
i = 0;
while ((prop = of_get_address(child, i++, NULL, NULL))) {
bank = of_read_number(prop, 1);
if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) {
dev_err(nemc->dev,
"%s requests invalid bank %u\n",
child->full_name, bank);
/* Will continue the outer loop below. */
referenced = 0;
break;
}
referenced |= BIT(bank);
}
if (!referenced) {
dev_err(nemc->dev, "%s has no addresses\n",
child->full_name);
continue;
} else if (nemc->banks_present & referenced) {
dev_err(nemc->dev, "%s conflicts with another node\n",
child->full_name);
continue;
}
/* Configure bank parameters. */
for_each_set_bit(bank, &referenced, JZ4780_NEMC_NUM_BANKS) {
if (!jz4780_nemc_configure_bank(nemc, bank, child)) {
referenced = 0;
break;
}
}
if (referenced) {
if (of_platform_device_create(child, NULL, nemc->dev))
nemc->banks_present |= referenced;
}
}
platform_set_drvdata(pdev, nemc);
dev_info(dev, "JZ4780 NEMC initialised\n");
return 0;
}
static int jz4780_nemc_remove(struct platform_device *pdev)
{
struct jz4780_nemc *nemc = platform_get_drvdata(pdev);
clk_disable_unprepare(nemc->clk);
return 0;
}
static const struct of_device_id jz4780_nemc_dt_match[] = {
{ .compatible = "ingenic,jz4780-nemc" },
{},
};
static struct platform_driver jz4780_nemc_driver = {
.probe = jz4780_nemc_probe,
.remove = jz4780_nemc_remove,
.driver = {
.name = "jz4780-nemc",
.of_match_table = of_match_ptr(jz4780_nemc_dt_match),
},
};
static int __init jz4780_nemc_init(void)
{
return platform_driver_register(&jz4780_nemc_driver);
}
subsys_initcall(jz4780_nemc_init);
......@@ -230,6 +230,8 @@ static const struct i2c_device_id bh1780_id[] = {
{ },
};
MODULE_DEVICE_TABLE(i2c, bh1780_id);
#ifdef CONFIG_OF
static const struct of_device_id of_bh1780_match[] = {
{ .compatible = "rohm,bh1780gli", },
......
......@@ -479,6 +479,7 @@ static int fpga_program_block(struct fpga_dev *priv, void *buf, size_t count)
static noinline int fpga_program_cpu(struct fpga_dev *priv)
{
int ret;
unsigned long timeout;
/* Disable the programmer */
fpga_programmer_disable(priv);
......@@ -497,8 +498,8 @@ static noinline int fpga_program_cpu(struct fpga_dev *priv)
goto out_disable_controller;
/* Wait for the interrupt handler to signal that programming finished */
ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
if (!ret) {
timeout = wait_for_completion_timeout(&priv->completion, 2 * HZ);
if (!timeout) {
dev_err(priv->dev, "Timed out waiting for completion\n");
ret = -ETIMEDOUT;
goto out_disable_controller;
......@@ -536,6 +537,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
struct sg_table table;
dma_cookie_t cookie;
int ret, i;
unsigned long timeout;
/* Disable the programmer */
fpga_programmer_disable(priv);
......@@ -623,8 +625,8 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
dev_dbg(priv->dev, "enabled the controller\n");
/* Wait for the interrupt handler to signal that programming finished */
ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
if (!ret) {
timeout = wait_for_completion_timeout(&priv->completion, 2 * HZ);
if (!timeout) {
dev_err(priv->dev, "Timed out waiting for completion\n");
ret = -ETIMEDOUT;
goto out_disable_controller;
......@@ -1142,7 +1144,7 @@ static int fpga_of_probe(struct platform_device *op)
return ret;
}
static struct of_device_id fpga_of_match[] = {
static const struct of_device_id fpga_of_match[] = {
{ .compatible = "carma,fpga-programmer", },
{},
};
......
......@@ -1486,7 +1486,7 @@ static int data_of_remove(struct platform_device *op)
return 0;
}
static struct of_device_id data_of_match[] = {
static const struct of_device_id data_of_match[] = {
{ .compatible = "carma,carma-fpga", },
{},
};
......
......@@ -950,6 +950,7 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3)
struct lis3lv02d_platform_data *pdata;
struct device_node *np = lis3->of_node;
u32 val;
s32 sval;
if (!lis3->of_node)
return 0;
......@@ -1031,6 +1032,23 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3)
pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO;
if (of_get_property(np, "st,wakeup-z-hi", NULL))
pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI;
if (of_get_property(np, "st,wakeup-threshold", &val))
pdata->wakeup_thresh = val;
if (of_get_property(np, "st,wakeup2-x-lo", NULL))
pdata->wakeup_flags2 |= LIS3_WAKEUP_X_LO;
if (of_get_property(np, "st,wakeup2-x-hi", NULL))
pdata->wakeup_flags2 |= LIS3_WAKEUP_X_HI;
if (of_get_property(np, "st,wakeup2-y-lo", NULL))
pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_LO;
if (of_get_property(np, "st,wakeup2-y-hi", NULL))
pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_HI;
if (of_get_property(np, "st,wakeup2-z-lo", NULL))
pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_LO;
if (of_get_property(np, "st,wakeup2-z-hi", NULL))
pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_HI;
if (of_get_property(np, "st,wakeup2-threshold", &val))
pdata->wakeup_thresh2 = val;
if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) {
switch (val) {
......@@ -1054,29 +1072,29 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3)
if (of_get_property(np, "st,hipass2-disable", NULL))
pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE;
if (of_get_property(np, "st,axis-x", &val))
pdata->axis_x = val;
if (of_get_property(np, "st,axis-y", &val))
pdata->axis_y = val;
if (of_get_property(np, "st,axis-z", &val))
pdata->axis_z = val;
if (of_property_read_s32(np, "st,axis-x", &sval) == 0)
pdata->axis_x = sval;
if (of_property_read_s32(np, "st,axis-y", &sval) == 0)
pdata->axis_y = sval;
if (of_property_read_s32(np, "st,axis-z", &sval) == 0)
pdata->axis_z = sval;
if (of_get_property(np, "st,default-rate", NULL))
pdata->default_rate = val;
if (of_get_property(np, "st,min-limit-x", &val))
pdata->st_min_limits[0] = val;
if (of_get_property(np, "st,min-limit-y", &val))
pdata->st_min_limits[1] = val;
if (of_get_property(np, "st,min-limit-z", &val))
pdata->st_min_limits[2] = val;
if (of_get_property(np, "st,max-limit-x", &val))
pdata->st_max_limits[0] = val;
if (of_get_property(np, "st,max-limit-y", &val))
pdata->st_max_limits[1] = val;
if (of_get_property(np, "st,max-limit-z", &val))
pdata->st_max_limits[2] = val;
if (of_property_read_s32(np, "st,min-limit-x", &sval) == 0)
pdata->st_min_limits[0] = sval;
if (of_property_read_s32(np, "st,min-limit-y", &sval) == 0)
pdata->st_min_limits[1] = sval;
if (of_property_read_s32(np, "st,min-limit-z", &sval) == 0)
pdata->st_min_limits[2] = sval;
if (of_property_read_s32(np, "st,max-limit-x", &sval) == 0)
pdata->st_max_limits[0] = sval;
if (of_property_read_s32(np, "st,max-limit-y", &sval) == 0)
pdata->st_max_limits[1] = sval;
if (of_property_read_s32(np, "st,max-limit-z", &sval) == 0)
pdata->st_max_limits[2] = sval;
lis3->pdata = pdata;
......
......@@ -106,7 +106,7 @@ static union axis_conversion lis3lv02d_axis_map =
{ .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
#ifdef CONFIG_OF
static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
static const struct of_device_id lis3lv02d_i2c_dt_ids[] = {
{ .compatible = "st,lis3lv02d" },
{}
};
......
......@@ -61,7 +61,7 @@ static union axis_conversion lis3lv02d_axis_normal =
{ .as_array = { 1, 2, 3 } };
#ifdef CONFIG_OF
static struct of_device_id lis302dl_spi_dt_ids[] = {
static const struct of_device_id lis302dl_spi_dt_ids[] = {
{ .compatible = "st,lis302dl-spi" },
{}
};
......
......@@ -21,3 +21,6 @@ mei-me-objs += hw-me.o
obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o
mei-txe-objs := pci-txe.o
mei-txe-objs += hw-txe.o
mei-$(CONFIG_EVENT_TRACING) += mei-trace.o
CFLAGS_mei-trace.o = -I$(src)
......@@ -48,10 +48,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
{
/* reset iamthif parameters. */
dev->iamthif_current_cb = NULL;
dev->iamthif_msg_buf_size = 0;
dev->iamthif_msg_buf_index = 0;
dev->iamthif_canceled = false;
dev->iamthif_ioctl = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->iamthif_timer = 0;
dev->iamthif_stall_timer = 0;
......@@ -69,7 +66,6 @@ int mei_amthif_host_init(struct mei_device *dev)
{
struct mei_cl *cl = &dev->iamthif_cl;
struct mei_me_client *me_cl;
unsigned char *msg_buf;
int ret;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
......@@ -90,18 +86,6 @@ int mei_amthif_host_init(struct mei_device *dev)
dev->iamthif_mtu = me_cl->props.max_msg_length;
dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu);
kfree(dev->iamthif_msg_buf);
dev->iamthif_msg_buf = NULL;
/* allocate storage for ME message buffer */
msg_buf = kcalloc(dev->iamthif_mtu,
sizeof(unsigned char), GFP_KERNEL);
if (!msg_buf) {
ret = -ENOMEM;
goto out;
}
dev->iamthif_msg_buf = msg_buf;
ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
if (ret < 0) {
......@@ -194,30 +178,33 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
dev_dbg(dev->dev, "woke up from sleep\n");
}
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");
dev->iamthif_timer = 0;
if (cb) {
timeout = cb->read_time +
mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
dev_dbg(dev->dev, "amthif timeout = %lud\n",
timeout);
if (time_after(jiffies, timeout)) {
dev_dbg(dev->dev, "amthif Time out\n");
/* 15 sec for the message has expired */
list_del(&cb->list);
rets = -ETIME;
goto free;
}
timeout = cb->read_time +
mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
dev_dbg(dev->dev, "amthif timeout = %lud\n",
timeout);
if (time_after(jiffies, timeout)) {
dev_dbg(dev->dev, "amthif Time out\n");
/* 15 sec for the message has expired */
list_del_init(&cb->list);
rets = -ETIME;
goto free;
}
/* if the whole message will fit remove it from the list */
if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
list_del(&cb->list);
list_del_init(&cb->list);
else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
/* end of the message has been reached */
list_del(&cb->list);
list_del_init(&cb->list);
rets = 0;
goto free;
}
......@@ -225,15 +212,15 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
* remove message from deletion list
*/
dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n",
cb->response_buffer.size);
dev_dbg(dev->dev, "amthif cb->buf size - %d\n",
cb->buf.size);
dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", 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->response_buffer.data + *offset, length)) {
if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
dev_dbg(dev->dev, "failed to copy data to userland\n");
rets = -EFAULT;
} else {
......@@ -252,126 +239,88 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
}
/**
* mei_amthif_send_cmd - send amthif command to the ME
* mei_amthif_read_start - queue message for sending read credential
*
* @dev: the device structure
* @cb: mei call back struct
* @cl: host client
* @file: file pointer of message recipient
*
* Return: 0 on success, <0 on failure.
*
*/
static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
{
struct mei_msg_hdr mei_hdr;
struct mei_cl *cl;
int ret;
if (!dev || !cb)
return -ENODEV;
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
size_t length = dev->iamthif_mtu;
int rets;
dev_dbg(dev->dev, "write data to amthif client.\n");
cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
if (!cb) {
rets = -ENOMEM;
goto err;
}
dev->iamthif_state = MEI_IAMTHIF_WRITING;
dev->iamthif_current_cb = cb;
dev->iamthif_file_object = cb->file_object;
dev->iamthif_canceled = false;
dev->iamthif_ioctl = true;
dev->iamthif_msg_buf_size = cb->request_buffer.size;
memcpy(dev->iamthif_msg_buf, cb->request_buffer.data,
cb->request_buffer.size);
cl = &dev->iamthif_cl;
rets = mei_io_cb_alloc_buf(cb, length);
if (rets)
goto err;
ret = mei_cl_flow_ctrl_creds(cl);
if (ret < 0)
return ret;
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
if (ret && mei_hbuf_acquire(dev)) {
ret = 0;
if (cb->request_buffer.size > mei_hbuf_max_len(dev)) {
mei_hdr.length = mei_hbuf_max_len(dev);
mei_hdr.msg_complete = 0;
} else {
mei_hdr.length = cb->request_buffer.size;
mei_hdr.msg_complete = 1;
}
dev->iamthif_state = MEI_IAMTHIF_READING;
dev->iamthif_file_object = cb->file_object;
dev->iamthif_current_cb = cb;
mei_hdr.host_addr = cl->host_client_id;
mei_hdr.me_addr = cl->me_client_id;
mei_hdr.reserved = 0;
mei_hdr.internal = 0;
dev->iamthif_msg_buf_index += mei_hdr.length;
ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf);
if (ret)
return ret;
if (mei_hdr.msg_complete) {
if (mei_cl_flow_ctrl_reduce(cl))
return -EIO;
dev->iamthif_flow_control_pending = true;
dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
dev_dbg(dev->dev, "add amthif cb to write waiting list\n");
dev->iamthif_current_cb = cb;
dev->iamthif_file_object = cb->file_object;
list_add_tail(&cb->list, &dev->write_waiting_list.list);
} else {
dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n");
list_add_tail(&cb->list, &dev->write_list.list);
}
} else {
list_add_tail(&cb->list, &dev->write_list.list);
}
return 0;
err:
mei_io_cb_free(cb);
return rets;
}
/**
* mei_amthif_write - write amthif data to amthif client
* mei_amthif_send_cmd - send amthif command to the ME
*
* @dev: the device structure
* @cl: the host client
* @cb: mei call back struct
*
* Return: 0 on success, <0 on failure.
*
*/
int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev;
int ret;
if (!dev || !cb)
if (!cl->dev || !cb)
return -ENODEV;
ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu);
if (ret)
dev = cl->dev;
dev->iamthif_state = MEI_IAMTHIF_WRITING;
dev->iamthif_current_cb = cb;
dev->iamthif_file_object = cb->file_object;
dev->iamthif_canceled = false;
ret = mei_cl_write(cl, cb, false);
if (ret < 0)
return ret;
cb->fop_type = MEI_FOP_WRITE;
if (cb->completed)
cb->status = mei_amthif_read_start(cl, cb->file_object);
if (!list_empty(&dev->amthif_cmd_list.list) ||
dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev_dbg(dev->dev,
"amthif state = %d\n", dev->iamthif_state);
dev_dbg(dev->dev, "AMTHIF: add cb to the wait list\n");
list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
return 0;
}
return mei_amthif_send_cmd(dev, cb);
return 0;
}
/**
* mei_amthif_run_next_cmd - send next amt command from queue
*
* @dev: the device structure
*
* Return: 0 on success, <0 on failure.
*/
void mei_amthif_run_next_cmd(struct mei_device *dev)
int mei_amthif_run_next_cmd(struct mei_device *dev)
{
struct mei_cl *cl = &dev->iamthif_cl;
struct mei_cl_cb *cb;
int ret;
if (!dev)
return;
dev->iamthif_msg_buf_size = 0;
dev->iamthif_msg_buf_index = 0;
dev->iamthif_canceled = false;
dev->iamthif_ioctl = true;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->iamthif_timer = 0;
dev->iamthif_file_object = NULL;
......@@ -381,13 +330,48 @@ void mei_amthif_run_next_cmd(struct mei_device *dev)
cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
typeof(*cb), list);
if (!cb)
return;
list_del(&cb->list);
ret = mei_amthif_send_cmd(dev, cb);
if (ret)
dev_warn(dev->dev, "amthif write failed status = %d\n", ret);
return 0;
list_del_init(&cb->list);
return mei_amthif_send_cmd(cl, cb);
}
/**
* mei_amthif_write - write amthif data to amthif client
*
* @cl: host client
* @cb: mei call back struct
*
* Return: 0 on success, <0 on failure.
*/
int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev;
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
if (WARN_ON(!cb))
return -EINVAL;
dev = cl->dev;
list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
return mei_amthif_run_next_cmd(dev);
}
/**
* mei_amthif_poll - the amthif poll function
*
* @dev: the device structure
* @file: pointer to file structure
* @wait: pointer to poll_table structure
*
* Return: poll mask
*
* Locking: called under "dev->device_lock" lock
*/
unsigned int mei_amthif_poll(struct mei_device *dev,
struct file *file, poll_table *wait)
......@@ -396,19 +380,12 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
poll_wait(file, &dev->iamthif_cl.wait, wait);
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(&dev->iamthif_cl)) {
mask = POLLERR;
} else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
dev->iamthif_file_object == file) {
if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
dev->iamthif_file_object == file) {
mask |= (POLLIN | POLLRDNORM);
dev_dbg(dev->dev, "run next amthif cb\n");
mask |= POLLIN | POLLRDNORM;
mei_amthif_run_next_cmd(dev);
}
mutex_unlock(&dev->device_lock);
return mask;
}
......@@ -427,71 +404,14 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
{
struct mei_device *dev = cl->dev;
struct mei_msg_hdr mei_hdr;
size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
u32 msg_slots = mei_data2slots(len);
int slots;
int rets;
rets = mei_cl_flow_ctrl_creds(cl);
if (rets < 0)
return rets;
if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
return 0;
}
mei_hdr.host_addr = cl->host_client_id;
mei_hdr.me_addr = cl->me_client_id;
mei_hdr.reserved = 0;
mei_hdr.internal = 0;
slots = mei_hbuf_empty_slots(dev);
if (slots >= msg_slots) {
mei_hdr.length = len;
mei_hdr.msg_complete = 1;
/* Split the message only if we can write the whole host buffer */
} else if (slots == dev->hbuf_depth) {
msg_slots = slots;
len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
mei_hdr.length = len;
mei_hdr.msg_complete = 0;
} else {
/* wait for next time the host buffer is empty */
return 0;
}
dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
rets = mei_write_message(dev, &mei_hdr,
dev->iamthif_msg_buf + dev->iamthif_msg_buf_index);
if (rets) {
dev->iamthif_state = MEI_IAMTHIF_IDLE;
cl->status = rets;
list_del(&cb->list);
return rets;
}
if (mei_cl_flow_ctrl_reduce(cl))
return -EIO;
dev->iamthif_msg_buf_index += mei_hdr.length;
cl->status = 0;
if (mei_hdr.msg_complete) {
dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
dev->iamthif_flow_control_pending = true;
/* save iamthif cb sent to amthif client */
cb->buf_idx = dev->iamthif_msg_buf_index;
dev->iamthif_current_cb = cb;
int ret;
list_move_tail(&cb->list, &dev->write_waiting_list.list);
}
ret = mei_cl_irq_write(cl, cb, cmpl_list);
if (ret)
return ret;
if (cb->completed)
cb->status = mei_amthif_read_start(cl, cb->file_object);
return 0;
}
......@@ -500,83 +420,35 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
* mei_amthif_irq_read_msg - read routine after ISR to
* handle the read amthif message
*
* @dev: the device structure
* @cl: mei client
* @mei_hdr: header of amthif message
* @complete_list: An instance of our list structure
* @cmpl_list: completed callbacks list
*
* Return: 0 on success, <0 on failure.
* Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status
*/
int mei_amthif_irq_read_msg(struct mei_device *dev,
int mei_amthif_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
struct mei_cl_cb *complete_list)
struct mei_cl_cb *cmpl_list)
{
struct mei_cl_cb *cb;
unsigned char *buffer;
BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id);
BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
struct mei_device *dev;
int ret;
buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index;
BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
dev = cl->dev;
mei_read_slots(dev, buffer, mei_hdr->length);
if (dev->iamthif_state != MEI_IAMTHIF_READING)
return 0;
dev->iamthif_msg_buf_index += mei_hdr->length;
ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
if (ret)
return ret;
if (!mei_hdr->msg_complete)
return 0;
dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n",
mei_hdr->length);
dev_dbg(dev->dev, "completed amthif read.\n ");
if (!dev->iamthif_current_cb)
return -ENODEV;
cb = dev->iamthif_current_cb;
dev->iamthif_current_cb = NULL;
dev->iamthif_stall_timer = 0;
cb->buf_idx = dev->iamthif_msg_buf_index;
cb->read_time = jiffies;
if (dev->iamthif_ioctl) {
/* found the iamthif cb */
dev_dbg(dev->dev, "complete the amthif read cb.\n ");
dev_dbg(dev->dev, "add the amthif read cb to complete.\n ");
list_add_tail(&cb->list, &complete_list->list);
}
return 0;
}
/**
* mei_amthif_irq_read - prepares to read amthif data.
*
* @dev: the device structure.
* @slots: free slots.
*
* Return: 0, OK; otherwise, error.
*/
int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
{
u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
if (*slots < msg_slots)
return -EMSGSIZE;
*slots -= msg_slots;
if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) {
dev_dbg(dev->dev, "iamthif flow control failed\n");
return -EIO;
}
dev_dbg(dev->dev, "iamthif flow control success\n");
dev->iamthif_state = MEI_IAMTHIF_READING;
dev->iamthif_flow_control_pending = false;
dev->iamthif_msg_buf_index = 0;
dev->iamthif_msg_buf_size = 0;
dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
return 0;
}
......@@ -588,17 +460,30 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
*/
void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
{
if (cb->fop_type == MEI_FOP_WRITE) {
if (!cb->status) {
dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
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, &dev->amthif_rd_complete_list.list);
wake_up_interruptible(&dev->iamthif_cl.wait);
return;
}
if (dev->iamthif_canceled != 1) {
dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
dev->iamthif_stall_timer = 0;
memcpy(cb->response_buffer.data,
dev->iamthif_msg_buf,
dev->iamthif_msg_buf_index);
list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
dev_dbg(dev->dev, "amthif read completed\n");
dev->iamthif_timer = jiffies;
dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n",
dev->iamthif_timer);
dev->iamthif_timer);
} else {
mei_amthif_run_next_cmd(dev);
}
......@@ -623,26 +508,22 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
static bool mei_clear_list(struct mei_device *dev,
const struct file *file, struct list_head *mei_cb_list)
{
struct mei_cl_cb *cb_pos = NULL;
struct mei_cl_cb *cb_next = NULL;
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_pos, cb_next, mei_cb_list, list) {
list_for_each_entry_safe(cb, next, mei_cb_list, list) {
/* check if list member associated with a file */
if (file == cb_pos->file_object) {
/* remove member from the list */
list_del(&cb_pos->list);
if (file == cb->file_object) {
/* check if cb equal to current iamthif cb */
if (dev->iamthif_current_cb == cb_pos) {
if (dev->iamthif_current_cb == cb) {
dev->iamthif_current_cb = NULL;
/* send flow control to iamthif client */
mei_hbm_cl_flow_control_req(dev,
&dev->iamthif_cl);
mei_hbm_cl_flow_control_req(dev, cl);
}
/* free all allocated buffers */
mei_io_cb_free(cb_pos);
cb_pos = NULL;
mei_io_cb_free(cb);
removed = true;
}
}
......
......@@ -238,7 +238,7 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
dev = cl->dev;
mutex_lock(&dev->device_lock);
if (cl->state != MEI_FILE_CONNECTED) {
if (!mei_cl_is_connected(cl)) {
rets = -ENODEV;
goto out;
}
......@@ -255,17 +255,13 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
goto out;
}
cb = mei_io_cb_init(cl, NULL);
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
if (!cb) {
rets = -ENOMEM;
goto out;
}
rets = mei_io_cb_alloc_req_buf(cb, length);
if (rets < 0)
goto out;
memcpy(cb->request_buffer.data, buf, length);
memcpy(cb->buf.data, buf, length);
rets = mei_cl_write(cl, cb, blocking);
......@@ -292,20 +288,21 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
mutex_lock(&dev->device_lock);
if (!cl->read_cb) {
rets = mei_cl_read_start(cl, length);
if (rets < 0)
goto out;
}
cb = mei_cl_read_cb(cl, NULL);
if (cb)
goto copy;
if (cl->reading_state != MEI_READ_COMPLETE &&
!waitqueue_active(&cl->rx_wait)) {
rets = mei_cl_read_start(cl, length, NULL);
if (rets && rets != -EBUSY)
goto out;
if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
mutex_unlock(&dev->device_lock);
if (wait_event_interruptible(cl->rx_wait,
cl->reading_state == MEI_READ_COMPLETE ||
mei_cl_is_transitioning(cl))) {
(!list_empty(&cl->rd_completed)) ||
(!mei_cl_is_connected(cl)))) {
if (signal_pending(current))
return -EINTR;
......@@ -313,23 +310,31 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
}
mutex_lock(&dev->device_lock);
}
cb = cl->read_cb;
if (!mei_cl_is_connected(cl)) {
rets = -EBUSY;
goto out;
}
}
if (cl->reading_state != MEI_READ_COMPLETE) {
cb = mei_cl_read_cb(cl, NULL);
if (!cb) {
rets = 0;
goto out;
}
copy:
if (cb->status) {
rets = cb->status;
goto free;
}
r_length = min_t(size_t, length, cb->buf_idx);
memcpy(buf, cb->response_buffer.data, r_length);
memcpy(buf, cb->buf.data, r_length);
rets = r_length;
free:
mei_io_cb_free(cb);
cl->reading_state = MEI_IDLE;
cl->read_cb = NULL;
out:
mutex_unlock(&dev->device_lock);
......@@ -386,7 +391,7 @@ static void mei_bus_event_work(struct work_struct *work)
device->events = 0;
/* Prepare for the next read */
mei_cl_read_start(device->cl, 0);
mei_cl_read_start(device->cl, 0, NULL);
}
int mei_cl_register_event_cb(struct mei_cl_device *device,
......@@ -400,7 +405,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *device,
device->event_context = context;
INIT_WORK(&device->event_work, mei_bus_event_work);
mei_cl_read_start(device->cl, 0);
mei_cl_read_start(device->cl, 0, NULL);
return 0;
}
......@@ -441,8 +446,8 @@ int mei_cl_enable_device(struct mei_cl_device *device)
mutex_unlock(&dev->device_lock);
if (device->event_cb && !cl->read_cb)
mei_cl_read_start(device->cl, 0);
if (device->event_cb)
mei_cl_read_start(device->cl, 0, NULL);
if (!device->ops || !device->ops->enable)
return 0;
......@@ -462,54 +467,34 @@ int mei_cl_disable_device(struct mei_cl_device *device)
dev = cl->dev;
if (device->ops && device->ops->disable)
device->ops->disable(device);
device->event_cb = NULL;
mutex_lock(&dev->device_lock);
if (cl->state != MEI_FILE_CONNECTED) {
mutex_unlock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
dev_err(dev->dev, "Already disconnected");
return 0;
err = 0;
goto out;
}
cl->state = MEI_FILE_DISCONNECTING;
err = mei_cl_disconnect(cl);
if (err < 0) {
mutex_unlock(&dev->device_lock);
dev_err(dev->dev,
"Could not disconnect from the ME client");
return err;
dev_err(dev->dev, "Could not disconnect from the ME client");
goto out;
}
/* Flush queues and remove any pending read */
mei_cl_flush_queues(cl);
if (cl->read_cb) {
struct mei_cl_cb *cb = NULL;
cb = mei_cl_find_read_cb(cl);
/* Remove entry from read list */
if (cb)
list_del(&cb->list);
cb = cl->read_cb;
cl->read_cb = NULL;
if (cb) {
mei_io_cb_free(cb);
cb = NULL;
}
}
device->event_cb = NULL;
mei_cl_flush_queues(cl, NULL);
out:
mutex_unlock(&dev->device_lock);
return err;
if (!device->ops || !device->ops->disable)
return 0;
return device->ops->disable(device);
}
EXPORT_SYMBOL_GPL(mei_cl_disable_device);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -71,8 +71,8 @@ extern const struct mei_cfg mei_me_pch8_sps_cfg;
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg);
int mei_me_pg_set_sync(struct mei_device *dev);
int mei_me_pg_unset_sync(struct mei_device *dev);
int mei_me_pg_enter_sync(struct mei_device *dev);
int mei_me_pg_exit_sync(struct mei_device *dev);
irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
......
......@@ -412,7 +412,7 @@ static void mei_txe_intr_disable(struct mei_device *dev)
mei_txe_br_reg_write(hw, HIER_REG, 0);
}
/**
* mei_txe_intr_disable - enable all interrupts
* mei_txe_intr_enable - enable all interrupts
*
* @dev: the device structure
*/
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -363,8 +363,6 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev)
{
int rc;
pci_msi_off(pdev);
/* Enable intx */
pci_intx(pdev, 1);
rc = mic_setup_callbacks(mdev);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册