提交 06dd3dfe 编写于 作者: L Linus Torvalds

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

Pull char/misc updates from Greg KH:
 "Here is the big set of char/misc driver patches for 4.17-rc1.

  There are a lot of little things in here, nothing huge, but all
  important to the different hardware types involved:

   -  thunderbolt driver updates

   -  parport updates (people still care...)

   -  nvmem driver updates

   -  mei updates (as always)

   -  hwtracing driver updates

   -  hyperv driver updates

   -  extcon driver updates

   -  ... and a handful of even smaller driver subsystem and individual
      driver updates

  All of these have been in linux-next with no reported issues"

* tag 'char-misc-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (149 commits)
  hwtracing: Add HW tracing support menu
  intel_th: Add ACPI glue layer
  intel_th: Allow forcing host mode through drvdata
  intel_th: Pick up irq number from resources
  intel_th: Don't touch switch routing in host mode
  intel_th: Use correct method of finding hub
  intel_th: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate
  stm class: Make dummy's master/channel ranges configurable
  stm class: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate
  MAINTAINERS: Bestow upon myself the care for drivers/hwtracing
  hv: add SPDX license id to Kconfig
  hv: add SPDX license to trace
  Drivers: hv: vmbus: do not mark HV_PCIE as perf_device
  Drivers: hv: vmbus: respect what we get from hv_get_synint_state()
  /dev/mem: Avoid overwriting "err" in read_mem()
  eeprom: at24: use SPDX identifier instead of GPL boiler-plate
  eeprom: at24: simplify the i2c functionality checking
  eeprom: at24: fix a line break
  eeprom: at24: tweak newlines
  eeprom: at24: refactor at24_probe()
  ...
......@@ -132,3 +132,10 @@ KernelVersion: 4.16
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Monitor bit associated with channel
Users: Debugging tools and userspace drivers
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/ring
Date: January. 2018
KernelVersion: 4.16
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Binary file created by uio_hv_generic for ring buffer
Users: Userspace drivers
What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
Date: Jun 2018
KernelVersion: 4.17
Contact: thunderbolt-software@lists.01.org
Description: Holds a comma separated list of device unique_ids that
are allowed to be connected automatically during system
startup (e.g boot devices). The list always contains
maximum supported number of unique_ids where unused
entries are empty. This allows the userspace software
to determine how many entries the controller supports.
If there are multiple controllers, each controller has
its own ACL list and size may be different between the
controllers.
System BIOS may have an option "Preboot ACL" or similar
that needs to be selected before this list is taken into
consideration.
Software always updates a full list in each write.
If a device is authorized automatically during boot its
boot attribute is set to 1.
What: /sys/bus/thunderbolt/devices/.../domainX/security
Date: Sep 2017
KernelVersion: 4.13
......@@ -12,6 +35,9 @@ Description: This attribute holds current Thunderbolt security level
minimum. User needs to authorize each device.
dponly: Automatically tunnel Display port (and USB). No
PCIe tunnels are created.
usbonly: Automatically tunnel USB controller of the
connected Thunderbolt dock (and Display Port). All
PCIe links downstream of the dock are removed.
What: /sys/bus/thunderbolt/devices/.../authorized
Date: Sep 2017
......@@ -38,6 +64,13 @@ Description: This attribute is used to authorize Thunderbolt devices
the device did not contain a key at all, and
EKEYREJECTED if the challenge response did not match.
What: /sys/bus/thunderbolt/devices/.../boot
Date: Jun 2018
KernelVersion: 4.17
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains 1 if Thunderbolt device was already
authorized on boot and 0 otherwise.
What: /sys/bus/thunderbolt/devices/.../key
Date: Sep 2017
KernelVersion: 4.13
......
......@@ -45,3 +45,12 @@ Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Display the driver HBM protocol version.
The HBM protocol version supported by the driver.
What: /sys/class/mei/meiN/tx_queue_limit
Date: Jan 2018
KernelVersion: 4.16
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Configure tx queue limit
Set maximal number of pending writes
per opened session.
What: /sys/bus/platform/devices/[..]/fsi-master-gpio/external_mode
Date: Feb 2018
KernelVersion: 4.17
Contact: jk@ozlabs.org
Description:
Controls access arbitration for GPIO-based FSI master. A
value of 0 (the default) sets normal mode, where the
driver performs FSI bus transactions, 1 sets external mode,
where the FSI bus is driven externally (for example, by
a debug device).
......@@ -21,11 +21,11 @@ vulnerable to DMA attacks.
Security levels and how to use them
-----------------------------------
Starting with Intel Falcon Ridge Thunderbolt controller there are 4
security levels available. The reason for these is the fact that the
connected devices can be DMA masters and thus read contents of the host
memory without CPU and OS knowing about it. There are ways to prevent
this by setting up an IOMMU but it is not always available for various
reasons.
security levels available. Intel Titan Ridge added one more security level
(usbonly). The reason for these is the fact that the connected devices can
be DMA masters and thus read contents of the host memory without CPU and OS
knowing about it. There are ways to prevent this by setting up an IOMMU but
it is not always available for various reasons.
The security levels are as follows:
......@@ -52,6 +52,11 @@ The security levels are as follows:
USB. No PCIe tunneling is done. In BIOS settings this is
typically called *Display Port Only*.
usbonly
The firmware automatically creates tunnels for the USB controller and
Display Port in a dock. All PCIe links downstream of the dock are
removed.
The current security level can be read from
``/sys/bus/thunderbolt/devices/domainX/security`` where ``domainX`` is
the Thunderbolt domain the host controller manages. There is typically
......
Samsung micro-USB 11-pin connector
==================================
Samsung micro-USB 11-pin connector is an extension of micro-USB connector.
It is present in multiple Samsung mobile devices.
It has additional pins to route MHL traffic simultanously with USB.
The bindings are superset of usb-connector bindings for micro-USB connector[1].
Required properties:
- compatible: must be: "samsung,usb-connector-11pin", "usb-b-connector",
- type: must be "micro".
Required nodes:
- any data bus to the connector should be modeled using the OF graph bindings
specified in bindings/graph.txt, unless the bus is between parent node and
the connector. Since single connector can have multpile data buses every bus
has assigned OF graph port number as follows:
0: High Speed (HS),
3: Mobile High-Definition Link (MHL), specific to 11-pin Samsung micro-USB.
[1]: bindings/connector/usb-connector.txt
Example
-------
Micro-USB connector with HS lines routed via controller (MUIC) and MHL lines
connected to HDMI-MHL bridge (sii8620):
muic-max77843@66 {
...
usb_con: connector {
compatible = "samsung,usb-connector-11pin", "usb-b-connector";
label = "micro-USB";
type = "micro";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@3 {
reg = <3>;
usb_con_mhl: endpoint {
remote-endpoint = <&sii8620_mhl>;
};
};
};
};
};
USB Connector
=============
USB connector node represents physical USB connector. It should be
a child of USB interface controller.
Required properties:
- compatible: describes type of the connector, must be one of:
"usb-a-connector",
"usb-b-connector",
"usb-c-connector".
Optional properties:
- label: symbolic name for the connector,
- type: size of the connector, should be specified in case of USB-A, USB-B
non-fullsize connectors: "mini", "micro".
Required nodes:
- any data bus to the connector should be modeled using the OF graph bindings
specified in bindings/graph.txt, unless the bus is between parent node and
the connector. Since single connector can have multpile data buses every bus
has assigned OF graph port number as follows:
0: High Speed (HS), present in all connectors,
1: Super Speed (SS), present in SS capable connectors,
2: Sideband use (SBU), present in USB-C.
Examples
--------
1. Micro-USB connector with HS lines routed via controller (MUIC):
muic-max77843@66 {
...
usb_con: connector {
compatible = "usb-b-connector";
label = "micro-USB";
type = "micro";
};
};
2. USB-C connector attached to CC controller (s2mm005), HS lines routed
to companion PMIC (max77865), SS lines to USB3 PHY and SBU to DisplayPort.
DisplayPort video lines are routed to the connector via SS mux in USB3 PHY.
ccic: s2mm005@33 {
...
usb_con: connector {
compatible = "usb-c-connector";
label = "USB-C";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
usb_con_hs: endpoint {
remote-endpoint = <&max77865_usbc_hs>;
};
};
port@1 {
reg = <1>;
usb_con_ss: endpoint {
remote-endpoint = <&usbdrd_phy_ss>;
};
};
port@2 {
reg = <2>;
usb_con_sbu: endpoint {
remote-endpoint = <&dp_aux>;
};
};
};
};
};
FSI bus & engine generic device tree bindings
=============================================
The FSI bus is probe-able, so the OS is able to enumerate FSI slaves, and
engines within those slaves. However, we have a facility to match devicetree
nodes to probed engines. This allows for fsi engines to expose non-probeable
busses, which are then exposed by the device tree. For example, an FSI engine
that is an I2C master - the I2C bus can be described by the device tree under
the engine's device tree node.
FSI masters may require their own DT nodes (to describe the master HW itself);
that requirement is defined by the master's implementation, and is described by
the fsi-master-* binding specifications.
Under the masters' nodes, we can describe the bus topology using nodes to
represent the FSI slaves and their slave engines. As a basic outline:
fsi-master {
/* top-level of FSI bus topology, bound to an FSI master driver and
* exposes an FSI bus */
fsi-slave@<link,id> {
/* this node defines the FSI slave device, and is handled
* entirely with FSI core code */
fsi-slave-engine@<addr> {
/* this node defines the engine endpoint & address range, which
* is bound to the relevant fsi device driver */
...
};
fsi-slave-engine@<addr> {
...
};
};
};
Note that since the bus is probe-able, some (or all) of the topology may
not be described; this binding only provides an optional facility for
adding subordinate device tree nodes as children of FSI engines.
FSI masters
-----------
FSI master nodes declare themselves as such with the "fsi-master" compatible
value. It's likely that an implementation-specific compatible value will
be needed as well, for example:
compatible = "fsi-master-gpio", "fsi-master";
Since the master nodes describe the top-level of the FSI topology, they also
need to declare the FSI-standard addressing scheme. This requires two cells for
addresses (link index and slave ID), and no size:
#address-cells = <2>;
#size-cells = <0>;
An optional boolean property can be added to indicate that a particular master
should not scan for connected devices at initialization time. This is
necessary in cases where a scan could cause arbitration issues with other
masters that may be present on the bus.
no-scan-on-init;
FSI slaves
----------
Slaves are identified by a (link-index, slave-id) pair, so require two cells
for an address identifier. Since these are not a range, no size cells are
required. For an example, a slave on link 1, with ID 2, could be represented
as:
cfam@1,2 {
reg = <1 2>;
[...];
}
Each slave provides an address-space, under which the engines are accessible.
That address space has a maximum of 23 bits, so we use one cell to represent
addresses and sizes in the slave address space:
#address-cells = <1>;
#size-cells = <1>;
FSI engines (devices)
---------------------
Engines are identified by their address under the slaves' address spaces. We
use a single cell for address and size. Engine nodes represent the endpoint
FSI device, and are passed to those FSI device drivers' ->probe() functions.
For example, for a slave using a single 0x400-byte page starting at address
0xc00:
engine@c00 {
reg = <0xc00 0x400>;
};
Full example
------------
Here's an example that illustrates:
- an FSI master
- connected to an FSI slave
- that contains an engine that is an I2C master
- connected to an I2C EEPROM
The FSI master may be connected to additional slaves, and slaves may have
additional engines, but they don't necessarily need to be describe in the
device tree if no extra platform information is required.
/* The GPIO-based FSI master node, describing the top level of the
* FSI bus
*/
gpio-fsi {
compatible = "fsi-master-gpio", "fsi-master";
#address-cells = <2>;
#size-cells = <0>;
/* A FSI slave (aka. CFAM) at link 0, ID 0. */
cfam@0,0 {
reg = <0 0>;
#address-cells = <1>;
#size-cells = <1>;
/* FSI engine at 0xc00, using a single page. In this example,
* it's an I2C master controller, so subnodes describe the
* I2C bus.
*/
i2c-controller@c00 {
reg = <0xc00 0x400>;
/* Engine-specific data. In this case, we're describing an
* I2C bus, so we're conforming to the generic I2C binding
*/
compatible = "some-vendor,fsi-i2c-controller";
#address-cells = <1>;
#size-cells = <1>;
/* I2C endpoint device: an Atmel EEPROM */
eeprom@50 {
compatible = "atmel,24c256";
reg = <0x50>;
pagesize = <64>;
};
};
};
};
......@@ -109,9 +109,50 @@ lpc: lpc@1e789000 {
};
};
BMC Node Children
==================
Host Node Children
==================
LPC Host Interface Controller
-------------------
The LPC Host Interface Controller manages functions exposed to the host such as
LPC firmware hub cycles, configuration of the LPC-to-AHB mapping, UART
management and bus snoop configuration.
Required properties:
- compatible: One of:
"aspeed,ast2400-lpc-ctrl";
"aspeed,ast2500-lpc-ctrl";
- reg: contains offset/length values of the host interface controller
memory regions
- clocks: contains a phandle to the syscon node describing the clocks.
There should then be one cell representing the clock to use
- memory-region: A phandle to a reserved_memory region to be used for the LPC
to AHB mapping
- flash: A phandle to the SPI flash controller containing the flash to
be exposed over the LPC to AHB mapping
Example:
lpc-host@80 {
lpc_ctrl: lpc-ctrl@0 {
compatible = "aspeed,ast2500-lpc-ctrl";
reg = <0x0 0x80>;
clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
memory-region = <&flash_memory>;
flash = <&spi>;
};
};
LPC Host Controller
-------------------
......
......@@ -11,17 +11,32 @@ Required properties:
"fsl,imx6ul-ocotp" (i.MX6UL),
"fsl,imx7d-ocotp" (i.MX7D/S),
followed by "syscon".
- #address-cells : Should be 1
- #size-cells : Should be 1
- reg: Should contain the register base and length.
- clocks: Should contain a phandle pointing to the gated peripheral clock.
Optional properties:
- read-only: disable write access
Example:
Optional Child nodes:
- Data cells of ocotp:
Detailed bindings are described in bindings/nvmem/nvmem.txt
Example:
ocotp: ocotp@21bc000 {
compatible = "fsl,imx6q-ocotp", "syscon";
#address-cells = <1>;
#size-cells = <1>;
compatible = "fsl,imx6sx-ocotp", "syscon";
reg = <0x021bc000 0x4000>;
clocks = <&clks IMX6QDL_CLK_IIM>;
read-only;
clocks = <&clks IMX6SX_CLK_OCOTP>;
tempmon_calib: calib@38 {
reg = <0x38 4>;
};
tempmon_temp_grade: temp-grade@20 {
reg = <0x20 4>;
};
};
Device tree bindings for Low Power General Purpose Register found in i.MX6Q/D
Secure Non-Volatile Storage.
and i.MX7 Secure Non-Volatile Storage.
This DT node should be represented as a sub-node of a "syscon",
"simple-mfd" node.
......@@ -8,6 +8,7 @@ Required properties:
- compatible: should be one of the fallowing variants:
"fsl,imx6q-snvs-lpgpr" for Freescale i.MX6Q/D/DL/S
"fsl,imx6ul-snvs-lpgpr" for Freescale i.MX6UL
"fsl,imx7d-snvs-lpgpr" for Freescale i.MX7D/S
Example:
snvs: snvs@020cc000 {
......
......@@ -709,6 +709,11 @@ The vmbus device regions are mapped into uio device resources:
3) Network receive buffer region
4) Network send buffer region
If a subchannel is created by a request to host, then the uio_hv_generic
device driver will create a sysfs binary file for the per-channel ring buffer.
For example:
/sys/bus/vmbus/devices/3811fe4d-0fa0-4b62-981a-74fc1084c757/channels/21/ring
Further information
===================
......
......@@ -6212,6 +6212,11 @@ F: Documentation/hw_random.txt
F: drivers/char/hw_random/
F: include/linux/hw_random.h
HARDWARE TRACING FACILITIES
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
S: Maintained
F: drivers/hwtracing/
HARDWARE SPINLOCK CORE
M: Ohad Ben-Cohen <ohad@wizery.com>
M: Bjorn Andersson <bjorn.andersson@linaro.org>
......@@ -8140,7 +8145,7 @@ F: drivers/*/*/*pasemi*
LINUX KERNEL DUMP TEST MODULE (LKDTM)
M: Kees Cook <keescook@chromium.org>
S: Maintained
F: drivers/misc/lkdtm*
F: drivers/misc/lkdtm/*
LINUX KERNEL MEMORY CONSISTENCY MODEL (LKMM)
M: Alan Stern <stern@rowland.harvard.edu>
......
......@@ -902,6 +902,9 @@ BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR,
hyperv_reenlightenment_intr)
BUILD_INTERRUPT3(hv_stimer0_callback_vector, HYPERV_STIMER0_VECTOR,
hv_stimer0_vector_handler)
#endif /* CONFIG_HYPERV */
ENTRY(page_fault)
......
......@@ -1140,6 +1140,9 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \
hyperv_reenlightenment_vector hyperv_reenlightenment_intr
apicinterrupt3 HYPERV_STIMER0_VECTOR \
hv_stimer0_callback_vector hv_stimer0_vector_handler
#endif /* CONFIG_HYPERV */
idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
......
......@@ -40,6 +40,7 @@ typedef struct {
#endif
#if IS_ENABLED(CONFIG_HYPERV)
unsigned int irq_hv_reenlightenment_count;
unsigned int hyperv_stimer0_count;
#endif
} ____cacheline_aligned irq_cpustat_t;
......
......@@ -106,9 +106,10 @@
#if IS_ENABLED(CONFIG_HYPERV)
#define HYPERV_REENLIGHTENMENT_VECTOR 0xee
#define HYPERV_STIMER0_VECTOR 0xed
#endif
#define LOCAL_TIMER_VECTOR 0xed
#define LOCAL_TIMER_VECTOR 0xec
#define NR_VECTORS 256
......
......@@ -173,6 +173,19 @@ void hv_remove_kexec_handler(void);
void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
void hv_remove_crash_handler(void);
/*
* Routines for stimer0 Direct Mode handling.
* On x86/x64, there are no percpu actions to take.
*/
void hv_stimer0_vector_handler(struct pt_regs *regs);
void hv_stimer0_callback_vector(void);
int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void));
void hv_remove_stimer0_irq(int irq);
static inline void hv_enable_stimer0_percpu_irq(int irq) {}
static inline void hv_disable_stimer0_percpu_irq(int irq) {}
#if IS_ENABLED(CONFIG_HYPERV)
extern struct clocksource *hyperv_cs;
extern void *hv_hypercall_pg;
......
......@@ -77,6 +77,9 @@
/* Crash MSR available */
#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10)
/* stimer Direct Mode is available */
#define HV_X64_STIMER_DIRECT_MODE_AVAILABLE (1 << 19)
/*
* Feature identification: EBX indicates which flags were specified at
* partition creation. The format is the same as the partition creation
......
......@@ -37,6 +37,7 @@ EXPORT_SYMBOL_GPL(ms_hyperv);
#if IS_ENABLED(CONFIG_HYPERV)
static void (*vmbus_handler)(void);
static void (*hv_stimer0_handler)(void);
static void (*hv_kexec_handler)(void);
static void (*hv_crash_handler)(struct pt_regs *regs);
......@@ -69,6 +70,41 @@ void hv_remove_vmbus_irq(void)
EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
/*
* Routines to do per-architecture handling of stimer0
* interrupts when in Direct Mode
*/
__visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
entering_irq();
inc_irq_stat(hyperv_stimer0_count);
if (hv_stimer0_handler)
hv_stimer0_handler();
ack_APIC_irq();
exiting_irq();
set_irq_regs(old_regs);
}
int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void))
{
*vector = HYPERV_STIMER0_VECTOR;
*irq = 0; /* Unused on x86/x64 */
hv_stimer0_handler = handler;
return 0;
}
EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
void hv_remove_stimer0_irq(int irq)
{
/* We have no way to deallocate the interrupt gate */
hv_stimer0_handler = NULL;
}
EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
void hv_setup_kexec_handler(void (*handler)(void))
{
hv_kexec_handler = handler;
......@@ -257,6 +293,10 @@ static void __init ms_hyperv_init_platform(void)
alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR,
hyperv_reenlightenment_vector);
/* Setup the IDT for stimer0 */
if (ms_hyperv.misc_features & HV_X64_STIMER_DIRECT_MODE_AVAILABLE)
alloc_intr_gate(HYPERV_STIMER0_VECTOR,
hv_stimer0_callback_vector);
#endif
}
......
......@@ -150,6 +150,13 @@ int arch_show_interrupts(struct seq_file *p, int prec)
irq_stats(j)->irq_hv_reenlightenment_count);
seq_puts(p, " Hyper-V reenlightenment interrupts\n");
}
if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) {
seq_printf(p, "%*s: ", prec, "HVS");
for_each_online_cpu(j)
seq_printf(p, "%10u ",
irq_stats(j)->hyperv_stimer0_count);
seq_puts(p, " Hyper-V stimer0 interrupts\n");
}
#endif
seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
#if defined(CONFIG_X86_IO_APIC)
......
......@@ -308,19 +308,22 @@ void blkdev_show(struct seq_file *seqf, off_t offset)
/**
* register_blkdev - register a new block device
*
* @major: the requested major device number [1..255]. If @major = 0, try to
* allocate any unused major number.
* @major: the requested major device number [1..BLKDEV_MAJOR_MAX-1]. If
* @major = 0, try to allocate any unused major number.
* @name: the name of the new block device as a zero terminated string
*
* The @name must be unique within the system.
*
* The return value depends on the @major input parameter:
*
* - if a major device number was requested in range [1..255] then the
* function returns zero on success, or a negative error code
* - if a major device number was requested in range [1..BLKDEV_MAJOR_MAX-1]
* then the function returns zero on success, or a negative error code
* - if any unused major number was requested with @major = 0 parameter
* then the return value is the allocated major number in range
* [1..255] or a negative error code otherwise
* [1..BLKDEV_MAJOR_MAX-1] or a negative error code otherwise
*
* See Documentation/admin-guide/devices.txt for the list of allocated
* major numbers.
*/
int register_blkdev(unsigned int major, const char *name)
{
......@@ -347,8 +350,8 @@ int register_blkdev(unsigned int major, const char *name)
}
if (major >= BLKDEV_MAJOR_MAX) {
pr_err("register_blkdev: major requested (%d) is greater than the maximum (%d) for %s\n",
major, BLKDEV_MAJOR_MAX, name);
pr_err("register_blkdev: major requested (%u) is greater than the maximum (%u) for %s\n",
major, BLKDEV_MAJOR_MAX-1, name);
ret = -EINVAL;
goto out;
......@@ -375,7 +378,7 @@ int register_blkdev(unsigned int major, const char *name)
ret = -EBUSY;
if (ret < 0) {
printk("register_blkdev: cannot get major %d for %s\n",
printk("register_blkdev: cannot get major %u for %s\n",
major, name);
kfree(p);
}
......
......@@ -199,9 +199,7 @@ source "drivers/dax/Kconfig"
source "drivers/nvmem/Kconfig"
source "drivers/hwtracing/stm/Kconfig"
source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/hwtracing/Kconfig"
source "drivers/fpga/Kconfig"
......
......@@ -236,7 +236,7 @@ source "drivers/char/hw_random/Kconfig"
config NVRAM
tristate "/dev/nvram support"
depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM
depends on ATARI || X86 || GENERIC_NVRAM
---help---
If you say Y here and create a character special file /dev/nvram
with major number 10 and minor number 144 using mknod ("man mknod"),
......
......@@ -137,7 +137,7 @@ static ssize_t read_mem(struct file *file, char __user *buf,
while (count > 0) {
unsigned long remaining;
int allowed;
int allowed, probe;
sz = size_inside_page(p, count);
......@@ -160,9 +160,9 @@ static ssize_t read_mem(struct file *file, char __user *buf,
if (!ptr)
goto failed;
err = probe_kernel_read(bounce, ptr, sz);
probe = probe_kernel_read(bounce, ptr, sz);
unxlate_dev_mem_ptr(p, ptr);
if (err)
if (probe)
goto failed;
remaining = copy_to_user(buf, bounce, sz);
......
......@@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2");
#define PCI_DEVICE_ID_XILLYBUS 0xebeb
#define PCI_VENDOR_ID_ALTERA 0x1172
#define PCI_VENDOR_ID_ACTEL 0x11aa
#define PCI_VENDOR_ID_LATTICE 0x1204
......
......@@ -18,8 +18,6 @@
*/
#include <linux/extcon-provider.h>
#include <linux/extcon/extcon-gpio.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
......@@ -29,14 +27,30 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
/**
* struct gpio_extcon_data - A simple GPIO-controlled extcon device state container.
* @edev: Extcon device.
* @irq: Interrupt line for the external connector.
* @work: Work fired by the interrupt.
* @debounce_jiffies: Number of jiffies to wait for the GPIO to stabilize, from the debounce
* value.
* @gpiod: GPIO descriptor for this external connector.
* @extcon_id: The unique id of specific external connector.
* @debounce: Debounce time for GPIO IRQ in ms.
* @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW).
* @check_on_resume: Boolean describing whether to check the state of gpio
* while resuming from sleep.
*/
struct gpio_extcon_data {
struct extcon_dev *edev;
int irq;
struct delayed_work work;
unsigned long debounce_jiffies;
struct gpio_desc *id_gpiod;
struct gpio_extcon_pdata *pdata;
struct gpio_desc *gpiod;
unsigned int extcon_id;
unsigned long debounce;
unsigned long irq_flags;
bool check_on_resume;
};
static void gpio_extcon_work(struct work_struct *work)
......@@ -46,11 +60,8 @@ static void gpio_extcon_work(struct work_struct *work)
container_of(to_delayed_work(work), struct gpio_extcon_data,
work);
state = gpiod_get_value_cansleep(data->id_gpiod);
if (data->pdata->gpio_active_low)
state = !state;
extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
state = gpiod_get_value_cansleep(data->gpiod);
extcon_set_state_sync(data->edev, data->extcon_id, state);
}
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
......@@ -62,65 +73,41 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data)
{
struct gpio_extcon_pdata *pdata = data->pdata;
int ret;
ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN,
dev_name(dev));
if (ret < 0)
return ret;
data->id_gpiod = gpio_to_desc(pdata->gpio);
if (!data->id_gpiod)
return -EINVAL;
if (pdata->debounce) {
ret = gpiod_set_debounce(data->id_gpiod,
pdata->debounce * 1000);
if (ret < 0)
data->debounce_jiffies =
msecs_to_jiffies(pdata->debounce);
}
data->irq = gpiod_to_irq(data->id_gpiod);
if (data->irq < 0)
return data->irq;
return 0;
}
static int gpio_extcon_probe(struct platform_device *pdev)
{
struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev);
struct gpio_extcon_data *data;
struct device *dev = &pdev->dev;
int ret;
if (!pdata)
return -EBUSY;
if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
return -EINVAL;
data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
GFP_KERNEL);
data = devm_kzalloc(dev, sizeof(struct gpio_extcon_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->pdata = pdata;
/* Initialize the gpio */
ret = gpio_extcon_init(&pdev->dev, data);
if (ret < 0)
return ret;
/*
* FIXME: extcon_id represents the unique identifier of external
* connectors such as EXTCON_USB, EXTCON_DISP_HDMI and so on. extcon_id
* is necessary to register the extcon device. But, it's not yet
* developed to get the extcon id from device-tree or others.
* On later, it have to be solved.
*/
if (!data->irq_flags || data->extcon_id > EXTCON_NONE)
return -EINVAL;
data->gpiod = devm_gpiod_get(dev, "extcon", GPIOD_IN);
if (IS_ERR(data->gpiod))
return PTR_ERR(data->gpiod);
data->irq = gpiod_to_irq(data->gpiod);
if (data->irq <= 0)
return data->irq;
/* Allocate the memory of extcon devie and register extcon device */
data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
data->edev = devm_extcon_dev_allocate(dev, &data->extcon_id);
if (IS_ERR(data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
dev_err(dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
ret = devm_extcon_dev_register(&pdev->dev, data->edev);
ret = devm_extcon_dev_register(dev, data->edev);
if (ret < 0)
return ret;
......@@ -130,8 +117,8 @@ static int gpio_extcon_probe(struct platform_device *pdev)
* Request the interrupt of gpio to detect whether external connector
* is attached or detached.
*/
ret = devm_request_any_context_irq(&pdev->dev, data->irq,
gpio_irq_handler, pdata->irq_flags,
ret = devm_request_any_context_irq(dev, data->irq,
gpio_irq_handler, data->irq_flags,
pdev->name, data);
if (ret < 0)
return ret;
......@@ -158,7 +145,7 @@ static int gpio_extcon_resume(struct device *dev)
struct gpio_extcon_data *data;
data = dev_get_drvdata(dev);
if (data->pdata->check_on_resume)
if (data->check_on_resume)
queue_delayed_work(system_power_efficient_wq,
&data->work, data->debounce_jiffies);
......
......@@ -66,6 +66,8 @@
#define CHT_WC_VBUS_GPIO_CTLO 0x6e2d
#define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0)
#define CHT_WC_VBUS_GPIO_CTLO_DRV_OD BIT(4)
#define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT BIT(5)
enum cht_wc_usb_id {
USB_ID_OTG,
......@@ -183,14 +185,15 @@ static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext,
{
int ret, val;
val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0;
/*
* The 5V boost converter is enabled through a gpio on the PMIC, since
* there currently is no gpio driver we access the gpio reg directly.
*/
ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO,
CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val);
val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT;
if (enable)
val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT;
ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val);
if (ret)
dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret);
}
......
......@@ -50,7 +50,11 @@ static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, fal
static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false };
static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = {
{ "id-gpios", &id_gpios, 1 },
/*
* Some platforms have a bug in ACPI GPIO description making IRQ
* GPIO to be output only. Ask the GPIO core to ignore this limit.
*/
{ "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
{ "vbus-gpios", &vbus_gpios, 1 },
{ "mux-gpios", &mux_gpios, 1 },
{ },
......@@ -112,9 +116,6 @@ static int int3496_probe(struct platform_device *pdev)
ret = PTR_ERR(data->gpio_usb_id);
dev_err(dev, "can't request USB ID GPIO: %d\n", ret);
return ret;
} else if (gpiod_get_direction(data->gpio_usb_id) != GPIOF_DIR_IN) {
dev_warn(dev, FW_BUG "USB ID GPIO not in input mode, fixing\n");
gpiod_direction_input(data->gpio_usb_id);
}
data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id);
......
......@@ -1336,6 +1336,28 @@ void extcon_dev_unregister(struct extcon_dev *edev)
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
#ifdef CONFIG_OF
/*
* extcon_find_edev_by_node - Find the extcon device from devicetree.
* @node : OF node identifying edev
*
* Return the pointer of extcon device if success or ERR_PTR(err) if fail.
*/
struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
{
struct extcon_dev *edev;
mutex_lock(&extcon_dev_list_lock);
list_for_each_entry(edev, &extcon_dev_list, entry)
if (edev->dev.parent && edev->dev.parent->of_node == node)
goto out;
edev = ERR_PTR(-EPROBE_DEFER);
out:
mutex_unlock(&extcon_dev_list_lock);
return edev;
}
/*
* extcon_get_edev_by_phandle - Get the extcon device from devicetree.
* @dev : the instance to the given device
......@@ -1363,25 +1385,27 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
return ERR_PTR(-ENODEV);
}
mutex_lock(&extcon_dev_list_lock);
list_for_each_entry(edev, &extcon_dev_list, entry) {
if (edev->dev.parent && edev->dev.parent->of_node == node) {
mutex_unlock(&extcon_dev_list_lock);
of_node_put(node);
return edev;
}
}
mutex_unlock(&extcon_dev_list_lock);
edev = extcon_find_edev_by_node(node);
of_node_put(node);
return ERR_PTR(-EPROBE_DEFER);
return edev;
}
#else
struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
{
return ERR_PTR(-ENOSYS);
}
struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
{
return ERR_PTR(-ENOSYS);
}
#endif /* CONFIG_OF */
EXPORT_SYMBOL_GPL(extcon_find_edev_by_node);
EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
/**
......
......@@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev,
const struct pci_device_id *dev_id);
static void altera_cvp_remove(struct pci_dev *pdev);
#define PCI_VENDOR_ID_ALTERA 0x1172
static struct pci_device_id altera_cvp_id_tbl[] = {
{ PCI_VDEVICE(ALTERA, PCI_ANY_ID) },
{ }
......
......@@ -4,6 +4,7 @@
menuconfig FSI
tristate "FSI support"
depends on OF
select CRC4
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
......
......@@ -18,6 +18,7 @@
#include <linux/fsi.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/bitops.h>
......@@ -142,6 +143,7 @@ static void fsi_device_release(struct device *_device)
{
struct fsi_device *device = to_fsi_dev(_device);
of_node_put(device->dev.of_node);
kfree(device);
}
......@@ -205,7 +207,7 @@ static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
if (rc)
return rc;
dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
be32_to_cpu(stat), be32_to_cpu(irq));
/* clear interrupts */
......@@ -334,6 +336,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave,
}
EXPORT_SYMBOL_GPL(fsi_slave_release_range);
static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
uint32_t addr, uint32_t size)
{
unsigned int len, na, ns;
const __be32 *prop;
uint32_t psize;
na = of_n_addr_cells(np);
ns = of_n_size_cells(np);
if (na != 1 || ns != 1)
return false;
prop = of_get_property(np, "reg", &len);
if (!prop || len != 8)
return false;
if (of_read_number(prop, 1) != addr)
return false;
psize = of_read_number(prop + 1, 1);
if (psize != size) {
dev_warn(dev,
"node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
of_node_full_name(np), psize, size);
}
return true;
}
/* Find a matching node for the slave engine at @address, using @size bytes
* of space. Returns NULL if not found, or a matching node with refcount
* already incremented.
*/
static struct device_node *fsi_device_find_of_node(struct fsi_device *dev)
{
struct device_node *parent, *np;
parent = dev_of_node(&dev->slave->dev);
if (!parent)
return NULL;
for_each_child_of_node(parent, np) {
if (fsi_device_node_matches(&dev->dev, np,
dev->addr, dev->size))
return np;
}
return NULL;
}
static int fsi_slave_scan(struct fsi_slave *slave)
{
uint32_t engine_addr;
......@@ -402,6 +455,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
slave->master->idx, slave->link,
slave->id, i - 2);
dev->dev.of_node = fsi_device_find_of_node(dev);
rc = device_register(&dev->dev);
if (rc) {
......@@ -558,9 +612,53 @@ static void fsi_slave_release(struct device *dev)
{
struct fsi_slave *slave = to_fsi_slave(dev);
of_node_put(dev->of_node);
kfree(slave);
}
static bool fsi_slave_node_matches(struct device_node *np,
int link, uint8_t id)
{
unsigned int len, na, ns;
const __be32 *prop;
na = of_n_addr_cells(np);
ns = of_n_size_cells(np);
/* Ensure we have the correct format for addresses and sizes in
* reg properties
*/
if (na != 2 || ns != 0)
return false;
prop = of_get_property(np, "reg", &len);
if (!prop || len != 8)
return false;
return (of_read_number(prop, 1) == link) &&
(of_read_number(prop + 1, 1) == id);
}
/* Find a matching node for the slave at (link, id). Returns NULL if none
* found, or a matching node with refcount already incremented.
*/
static struct device_node *fsi_slave_find_of_node(struct fsi_master *master,
int link, uint8_t id)
{
struct device_node *parent, *np;
parent = dev_of_node(&master->dev);
if (!parent)
return NULL;
for_each_child_of_node(parent, np) {
if (fsi_slave_node_matches(np, link, id))
return np;
}
return NULL;
}
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
uint32_t chip_id, llmode;
......@@ -589,7 +687,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
return -EIO;
}
dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
chip_id, master->idx, link, id);
rc = fsi_slave_set_smode(master, link, id);
......@@ -623,6 +721,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
slave->master = master;
slave->dev.parent = &master->dev;
slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
slave->dev.release = fsi_slave_release;
slave->link = link;
slave->id = id;
......@@ -656,10 +755,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
/* FSI master support */
static int fsi_check_access(uint32_t addr, size_t size)
{
if (size != 1 && size != 2 && size != 4)
return -EINVAL;
if ((addr & 0x3) != (size & 0x3))
if (size == 4) {
if (addr & 0x3)
return -EINVAL;
} else if (size == 2) {
if (addr & 0x1)
return -EINVAL;
} else if (size != 1)
return -EINVAL;
return 0;
......@@ -762,14 +864,20 @@ static void fsi_master_unscan(struct fsi_master *master)
device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
}
int fsi_master_rescan(struct fsi_master *master)
{
fsi_master_unscan(master);
return fsi_master_scan(master);
}
EXPORT_SYMBOL_GPL(fsi_master_rescan);
static ssize_t master_rescan_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fsi_master *master = to_fsi_master(dev);
int rc;
fsi_master_unscan(master);
rc = fsi_master_scan(master);
rc = fsi_master_rescan(master);
if (rc < 0)
return rc;
......@@ -793,6 +901,7 @@ static DEVICE_ATTR(break, 0200, NULL, master_break_store);
int fsi_master_register(struct fsi_master *master)
{
int rc;
struct device_node *np;
if (!master)
return -EINVAL;
......@@ -820,7 +929,9 @@ int fsi_master_register(struct fsi_master *master)
return rc;
}
fsi_master_scan(master);
np = dev_of_node(&master->dev);
if (!of_property_read_bool(np, "no-scan-on-init"))
fsi_master_scan(master);
return 0;
}
......
......@@ -9,6 +9,7 @@
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
......@@ -59,6 +60,7 @@ struct fsi_master_gpio {
struct gpio_desc *gpio_trans; /* Voltage translator */
struct gpio_desc *gpio_enable; /* FSI enable */
struct gpio_desc *gpio_mux; /* Mux control */
bool external_mode;
};
#define CREATE_TRACE_POINTS
......@@ -411,6 +413,12 @@ static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
int rc;
spin_lock_irqsave(&master->cmd_lock, flags);
if (master->external_mode) {
spin_unlock_irqrestore(&master->cmd_lock, flags);
return -EBUSY;
}
serial_out(master, cmd);
echo_delay(master);
rc = poll_for_response(master, slave, resp_len, resp);
......@@ -461,12 +469,18 @@ static int fsi_master_gpio_term(struct fsi_master *_master,
static int fsi_master_gpio_break(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
unsigned long flags;
if (link != 0)
return -ENODEV;
trace_fsi_master_gpio_break(master);
spin_lock_irqsave(&master->cmd_lock, flags);
if (master->external_mode) {
spin_unlock_irqrestore(&master->cmd_lock, flags);
return -EBUSY;
}
set_sda_output(master, 1);
sda_out(master, 1);
clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
......@@ -475,6 +489,7 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
echo_delay(master);
sda_out(master, 1);
clock_toggle(master, FSI_POST_BREAK_CLOCKS);
spin_unlock_irqrestore(&master->cmd_lock, flags);
/* Wait for logic reset to take effect */
udelay(200);
......@@ -494,21 +509,84 @@ static void fsi_master_gpio_init(struct fsi_master_gpio *master)
clock_zeros(master, FSI_INIT_CLOCKS);
}
static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
{
gpiod_direction_output(master->gpio_mux, 0);
gpiod_direction_output(master->gpio_trans, 0);
gpiod_direction_output(master->gpio_enable, 1);
gpiod_direction_input(master->gpio_clk);
gpiod_direction_input(master->gpio_data);
}
static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
unsigned long flags;
int rc = -EBUSY;
if (link != 0)
return -ENODEV;
gpiod_set_value(master->gpio_enable, 1);
return 0;
spin_lock_irqsave(&master->cmd_lock, flags);
if (!master->external_mode) {
gpiod_set_value(master->gpio_enable, 1);
rc = 0;
}
spin_unlock_irqrestore(&master->cmd_lock, flags);
return rc;
}
static ssize_t external_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fsi_master_gpio *master = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE - 1, "%u\n",
master->external_mode ? 1 : 0);
}
static ssize_t external_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fsi_master_gpio *master = dev_get_drvdata(dev);
unsigned long flags, val;
bool external_mode;
int err;
err = kstrtoul(buf, 0, &val);
if (err)
return err;
external_mode = !!val;
spin_lock_irqsave(&master->cmd_lock, flags);
if (external_mode == master->external_mode) {
spin_unlock_irqrestore(&master->cmd_lock, flags);
return count;
}
master->external_mode = external_mode;
if (master->external_mode)
fsi_master_gpio_init_external(master);
else
fsi_master_gpio_init(master);
spin_unlock_irqrestore(&master->cmd_lock, flags);
fsi_master_rescan(&master->master);
return count;
}
static DEVICE_ATTR(external_mode, 0664,
external_mode_show, external_mode_store);
static int fsi_master_gpio_probe(struct platform_device *pdev)
{
struct fsi_master_gpio *master;
struct gpio_desc *gpio;
int rc;
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master)
......@@ -516,6 +594,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
master->dev = &pdev->dev;
master->master.dev.parent = master->dev;
master->master.dev.of_node = of_node_get(dev_of_node(master->dev));
gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
if (IS_ERR(gpio)) {
......@@ -565,6 +644,10 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
fsi_master_gpio_init(master);
rc = device_create_file(&pdev->dev, &dev_attr_external_mode);
if (rc)
return rc;
return fsi_master_register(&master->master);
}
......@@ -583,6 +666,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev)
devm_gpiod_put(&pdev->dev, master->gpio_mux);
fsi_master_unregister(&master->master);
of_node_put(master->master.dev.of_node);
return 0;
}
......
......@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/fsi.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include "fsi-master.h"
......@@ -253,7 +254,7 @@ static int hub_master_probe(struct device *dev)
reg = be32_to_cpu(__reg);
links = (reg >> 8) & 0xff;
dev_info(dev, "hub version %08x (%d links)\n", reg, links);
dev_dbg(dev, "hub version %08x (%d links)\n", reg, links);
rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
......@@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev)
hub->master.dev.parent = dev;
hub->master.dev.release = hub_master_release;
hub->master.dev.of_node = of_node_get(dev_of_node(dev));
hub->master.n_links = links;
hub->master.read = hub_master_read;
......@@ -286,10 +288,19 @@ static int hub_master_probe(struct device *dev)
hub_master_init(hub);
rc = fsi_master_register(&hub->master);
if (!rc)
return 0;
if (rc)
goto err_release;
/* At this point, fsi_master_register performs the device_initialize(),
* and holds the sole reference on master.dev. This means the device
* will be freed (via ->release) during any subsequent call to
* fsi_master_unregister. We add our own reference to it here, so we
* can perform cleanup (in _remove()) without it being freed before
* we're ready.
*/
get_device(&hub->master.dev);
return 0;
kfree(hub);
err_release:
fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
......@@ -302,6 +313,14 @@ static int hub_master_remove(struct device *dev)
fsi_master_unregister(&hub->master);
fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
of_node_put(hub->master.dev.of_node);
/*
* master.dev will likely be ->release()ed after this, which free()s
* the hub
*/
put_device(&hub->master.dev);
return 0;
}
......
......@@ -37,7 +37,24 @@ struct fsi_master {
#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
/**
* fsi_master registration & lifetime: the fsi_master_register() and
* fsi_master_unregister() functions will take ownership of the master, and
* ->dev in particular. The registration path performs a get_device(), which
* takes the first reference on the device. Similarly, the unregistration path
* performs a put_device(), which may well drop the last reference.
*
* This means that master implementations *may* need to hold their own
* reference (via get_device()) on master->dev. In particular, if the device's
* ->release callback frees the fsi_master, then fsi_master_unregister will
* invoke this free if no other reference is held.
*
* The same applies for the error path of fsi_master_register; if the call
* fails, dev->release will have been invoked.
*/
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);
extern int fsi_master_rescan(struct fsi_master *master);
#endif /* DRIVERS_FSI_MASTER_H */
......@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
......@@ -25,6 +26,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
......@@ -81,6 +83,10 @@ struct sii8620 {
struct edid *edid;
unsigned int gen2_write_burst:1;
enum sii8620_mt_state mt_state;
struct extcon_dev *extcon;
struct notifier_block extcon_nb;
struct work_struct extcon_wq;
int cable_state;
struct list_head mt_queue;
struct {
int r_size;
......@@ -2170,6 +2176,77 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
ctx->rc_dev = rc_dev;
}
static void sii8620_cable_out(struct sii8620 *ctx)
{
disable_irq(to_i2c_client(ctx->dev)->irq);
sii8620_hw_off(ctx);
}
static void sii8620_extcon_work(struct work_struct *work)
{
struct sii8620 *ctx =
container_of(work, struct sii8620, extcon_wq);
int state = extcon_get_state(ctx->extcon, EXTCON_DISP_MHL);
if (state == ctx->cable_state)
return;
ctx->cable_state = state;
if (state > 0)
sii8620_cable_in(ctx);
else
sii8620_cable_out(ctx);
}
static int sii8620_extcon_notifier(struct notifier_block *self,
unsigned long event, void *ptr)
{
struct sii8620 *ctx =
container_of(self, struct sii8620, extcon_nb);
schedule_work(&ctx->extcon_wq);
return NOTIFY_DONE;
}
static int sii8620_extcon_init(struct sii8620 *ctx)
{
struct extcon_dev *edev;
struct device_node *musb, *muic;
int ret;
/* get micro-USB connector node */
musb = of_graph_get_remote_node(ctx->dev->of_node, 1, -1);
/* next get micro-USB Interface Controller node */
muic = of_get_next_parent(musb);
if (!muic) {
dev_info(ctx->dev, "no extcon found, switching to 'always on' mode\n");
return 0;
}
edev = extcon_find_edev_by_node(muic);
of_node_put(muic);
if (IS_ERR(edev)) {
if (PTR_ERR(edev) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(ctx->dev, "Invalid or missing extcon\n");
return PTR_ERR(edev);
}
ctx->extcon = edev;
ctx->extcon_nb.notifier_call = sii8620_extcon_notifier;
INIT_WORK(&ctx->extcon_wq, sii8620_extcon_work);
ret = extcon_register_notifier(edev, EXTCON_DISP_MHL, &ctx->extcon_nb);
if (ret) {
dev_err(ctx->dev, "failed to register notifier for MHL\n");
return ret;
}
return 0;
}
static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
{
return container_of(bridge, struct sii8620, bridge);
......@@ -2302,13 +2379,20 @@ static int sii8620_probe(struct i2c_client *client,
if (ret)
return ret;
ret = sii8620_extcon_init(ctx);
if (ret < 0) {
dev_err(ctx->dev, "failed to initialize EXTCON\n");
return ret;
}
i2c_set_clientdata(client, ctx);
ctx->bridge.funcs = &sii8620_bridge_funcs;
ctx->bridge.of_node = dev->of_node;
drm_bridge_add(&ctx->bridge);
sii8620_cable_in(ctx);
if (!ctx->extcon)
sii8620_cable_in(ctx);
return 0;
}
......@@ -2317,8 +2401,15 @@ static int sii8620_remove(struct i2c_client *client)
{
struct sii8620 *ctx = i2c_get_clientdata(client);
disable_irq(to_i2c_client(ctx->dev)->irq);
sii8620_hw_off(ctx);
if (ctx->extcon) {
extcon_unregister_notifier(ctx->extcon, EXTCON_DISP_MHL,
&ctx->extcon_nb);
flush_work(&ctx->extcon_wq);
if (ctx->cable_state > 0)
sii8620_cable_out(ctx);
} else {
sii8620_cable_out(ctx);
}
drm_bridge_remove(&ctx->bridge);
return 0;
......
# SPDX-License-Identifier: GPL-2.0
menu "Microsoft Hyper-V guest support"
config HYPERV
......
......@@ -4,6 +4,7 @@ obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
CFLAGS_hv_trace.o = -I$(src)
CFLAGS_hv_balloon.o = -I$(src)
hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
......
......@@ -71,7 +71,7 @@ static const struct vmbus_device vmbus_devs[] = {
/* PCIE */
{ .dev_type = HV_PCIE,
HV_PCIE_GUID,
.perf_device = true,
.perf_device = false,
},
/* Synthetic Frame Buffer */
......@@ -596,10 +596,8 @@ static int next_numa_node_id;
/*
* Starting with Win8, we can statically distribute the incoming
* channel interrupt load by binding a channel to VCPU.
* We do this in a hierarchical fashion:
* First distribute the primary channels across available NUMA nodes
* and then distribute the subchannels amongst the CPUs in the NUMA
* node assigned to the primary channel.
* We distribute the interrupt loads to one or more NUMA nodes based on
* the channel's affinity_policy.
*
* For pre-win8 hosts or non-performance critical channels we assign the
* first CPU in the first NUMA node.
......
......@@ -27,7 +27,7 @@
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/random.h>
#include <linux/clockchips.h>
#include <asm/hyperv.h>
#include <asm/mshyperv.h>
......@@ -38,6 +38,17 @@ struct hv_context hv_context = {
.synic_initialized = false,
};
/*
* If false, we're using the old mechanism for stimer0 interrupts
* where it sends a VMbus message when it expires. The old
* mechanism is used when running on older versions of Hyper-V
* that don't support Direct Mode. While Hyper-V provides
* four stimer's per CPU, Linux uses only stimer0.
*/
static bool direct_mode_enabled;
static int stimer0_irq;
static int stimer0_vector;
#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
#define HV_MIN_DELTA_TICKS 1
......@@ -53,6 +64,8 @@ int hv_init(void)
if (!hv_context.cpu_context)
return -ENOMEM;
direct_mode_enabled = ms_hyperv.misc_features &
HV_X64_STIMER_DIRECT_MODE_AVAILABLE;
return 0;
}
......@@ -91,6 +104,21 @@ int hv_post_message(union hv_connection_id connection_id,
return status & 0xFFFF;
}
/*
* ISR for when stimer0 is operating in Direct Mode. Direct Mode
* does not use VMbus or any VMbus messages, so process here and not
* in the VMbus driver code.
*/
static void hv_stimer0_isr(void)
{
struct hv_per_cpu_context *hv_cpu;
hv_cpu = this_cpu_ptr(hv_context.cpu_context);
hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt);
add_interrupt_randomness(stimer0_vector, 0);
}
static int hv_ce_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
......@@ -108,6 +136,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
{
hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
if (direct_mode_enabled)
hv_disable_stimer0_percpu_irq(stimer0_irq);
return 0;
}
......@@ -116,11 +146,26 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
{
union hv_timer_config timer_cfg;
timer_cfg.as_uint64 = 0;
timer_cfg.enable = 1;
timer_cfg.auto_enable = 1;
timer_cfg.sintx = VMBUS_MESSAGE_SINT;
if (direct_mode_enabled) {
/*
* When it expires, the timer will directly interrupt
* on the specified hardware vector/IRQ.
*/
timer_cfg.direct_mode = 1;
timer_cfg.apic_vector = stimer0_vector;
hv_enable_stimer0_percpu_irq(stimer0_irq);
} else {
/*
* When it expires, the timer will generate a VMbus message,
* to be handled by the normal VMbus interrupt handler.
*/
timer_cfg.direct_mode = 0;
timer_cfg.sintx = VMBUS_MESSAGE_SINT;
}
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
return 0;
}
......@@ -147,7 +192,7 @@ int hv_synic_alloc(void)
int cpu;
hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids,
GFP_ATOMIC);
GFP_KERNEL);
if (hv_context.hv_numa_map == NULL) {
pr_err("Unable to allocate NUMA map\n");
goto err;
......@@ -191,6 +236,11 @@ int hv_synic_alloc(void)
INIT_LIST_HEAD(&hv_cpu->chan_list);
}
if (direct_mode_enabled &&
hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector,
hv_stimer0_isr))
goto err;
return 0;
err:
return -ENOMEM;
......@@ -217,7 +267,7 @@ void hv_synic_free(void)
}
/*
* hv_synic_init - Initialize the Synthethic Interrupt Controller.
* hv_synic_init - Initialize the Synthetic Interrupt Controller.
*
* If it is already initialized by another entity (ie x2v shim), we need to
* retrieve the initialized message and event pages. Otherwise, we create and
......@@ -252,7 +302,6 @@ int hv_synic_init(unsigned int cpu)
hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
shared_sint.as_uint64 = 0;
shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
shared_sint.masked = false;
if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED)
......@@ -292,6 +341,9 @@ void hv_synic_clockevents_cleanup(void)
if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
return;
if (direct_mode_enabled)
hv_remove_stimer0_irq(stimer0_irq);
for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
......
......@@ -34,6 +34,9 @@
#include <linux/hyperv.h>
#define CREATE_TRACE_POINTS
#include "hv_trace_balloon.h"
/*
* We begin with definitions supporting the Dynamic Memory protocol
* with the host.
......@@ -576,11 +579,65 @@ static struct hv_dynmem_device dm_device;
static void post_status(struct hv_dynmem_device *dm);
#ifdef CONFIG_MEMORY_HOTPLUG
static inline bool has_pfn_is_backed(struct hv_hotadd_state *has,
unsigned long pfn)
{
struct hv_hotadd_gap *gap;
/* The page is not backed. */
if ((pfn < has->covered_start_pfn) || (pfn >= has->covered_end_pfn))
return false;
/* Check for gaps. */
list_for_each_entry(gap, &has->gap_list, list) {
if ((pfn >= gap->start_pfn) && (pfn < gap->end_pfn))
return false;
}
return true;
}
static unsigned long hv_page_offline_check(unsigned long start_pfn,
unsigned long nr_pages)
{
unsigned long pfn = start_pfn, count = 0;
struct hv_hotadd_state *has;
bool found;
while (pfn < start_pfn + nr_pages) {
/*
* Search for HAS which covers the pfn and when we find one
* count how many consequitive PFNs are covered.
*/
found = false;
list_for_each_entry(has, &dm_device.ha_region_list, list) {
while ((pfn >= has->start_pfn) &&
(pfn < has->end_pfn) &&
(pfn < start_pfn + nr_pages)) {
found = true;
if (has_pfn_is_backed(has, pfn))
count++;
pfn++;
}
}
/*
* This PFN is not in any HAS (e.g. we're offlining a region
* which was present at boot), no need to account for it. Go
* to the next one.
*/
if (!found)
pfn++;
}
return count;
}
static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
void *v)
{
struct memory_notify *mem = (struct memory_notify *)v;
unsigned long flags;
unsigned long flags, pfn_count;
switch (val) {
case MEM_ONLINE:
......@@ -593,7 +650,19 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
case MEM_OFFLINE:
spin_lock_irqsave(&dm_device.ha_lock, flags);
dm_device.num_pages_onlined -= mem->nr_pages;
pfn_count = hv_page_offline_check(mem->start_pfn,
mem->nr_pages);
if (pfn_count <= dm_device.num_pages_onlined) {
dm_device.num_pages_onlined -= pfn_count;
} else {
/*
* We're offlining more pages than we managed to online.
* This is unexpected. In any case don't let
* num_pages_onlined wrap around zero.
*/
WARN_ON_ONCE(1);
dm_device.num_pages_onlined = 0;
}
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break;
case MEM_GOING_ONLINE:
......@@ -612,30 +681,9 @@ static struct notifier_block hv_memory_nb = {
/* Check if the particular page is backed and can be onlined and online it. */
static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
{
unsigned long cur_start_pgp;
unsigned long cur_end_pgp;
struct hv_hotadd_gap *gap;
cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
/* The page is not backed. */
if (((unsigned long)pg < cur_start_pgp) ||
((unsigned long)pg >= cur_end_pgp))
if (!has_pfn_is_backed(has, page_to_pfn(pg)))
return;
/* Check for gaps. */
list_for_each_entry(gap, &has->gap_list, list) {
cur_start_pgp = (unsigned long)
pfn_to_page(gap->start_pfn);
cur_end_pgp = (unsigned long)
pfn_to_page(gap->end_pfn);
if (((unsigned long)pg >= cur_start_pgp) &&
((unsigned long)pg < cur_end_pgp)) {
return;
}
}
/* This frame is currently backed; online the page. */
__online_page_set_limits(pg);
__online_page_increment_counters(pg);
......@@ -691,7 +739,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
(HA_CHUNK << PAGE_SHIFT));
if (ret) {
pr_warn("hot_add memory failed error is %d\n", ret);
pr_err("hot_add memory failed error is %d\n", ret);
if (ret == -EEXIST) {
/*
* This error indicates that the error
......@@ -726,19 +774,13 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
static void hv_online_page(struct page *pg)
{
struct hv_hotadd_state *has;
unsigned long cur_start_pgp;
unsigned long cur_end_pgp;
unsigned long flags;
unsigned long pfn = page_to_pfn(pg);
spin_lock_irqsave(&dm_device.ha_lock, flags);
list_for_each_entry(has, &dm_device.ha_region_list, list) {
cur_start_pgp = (unsigned long)
pfn_to_page(has->start_pfn);
cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
/* The page belongs to a different HAS. */
if (((unsigned long)pg < cur_start_pgp) ||
((unsigned long)pg >= cur_end_pgp))
if ((pfn < has->start_pfn) || (pfn >= has->end_pfn))
continue;
hv_page_online_one(has, pg);
......@@ -1014,7 +1056,7 @@ static void hot_add_req(struct work_struct *dummy)
resp.result = 0;
if (!do_hot_add || (resp.page_count == 0))
pr_info("Memory hot add failed\n");
pr_err("Memory hot add failed\n");
dm->state = DM_INITIALIZED;
resp.hdr.trans_id = atomic_inc_return(&trans_id);
......@@ -1041,7 +1083,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
break;
default:
pr_info("Received Unknown type: %d\n", info_hdr->type);
pr_warn("Received Unknown type: %d\n", info_hdr->type);
}
}
......@@ -1120,6 +1162,9 @@ static void post_status(struct hv_dynmem_device *dm)
dm->num_pages_added - dm->num_pages_onlined : 0) +
compute_balloon_floor();
trace_balloon_status(status.num_avail, status.num_committed,
vm_memory_committed(), dm->num_pages_ballooned,
dm->num_pages_added, dm->num_pages_onlined);
/*
* If our transaction ID is no longer current, just don't
* send the status. This can happen if we were interrupted
......@@ -1290,7 +1335,7 @@ static void balloon_up(struct work_struct *dummy)
/*
* Free up the memory we allocatted.
*/
pr_info("Balloon response failed\n");
pr_err("Balloon response failed\n");
for (i = 0; i < bl_resp->range_count; i++)
free_balloon_pages(&dm_device,
......@@ -1421,7 +1466,7 @@ static void cap_resp(struct hv_dynmem_device *dm,
struct dm_capabilities_resp_msg *cap_resp)
{
if (!cap_resp->is_accepted) {
pr_info("Capabilities not accepted by host\n");
pr_err("Capabilities not accepted by host\n");
dm->state = DM_INIT_ERROR;
}
complete(&dm->host_event);
......@@ -1508,7 +1553,7 @@ static void balloon_onchannelcallback(void *context)
break;
default:
pr_err("Unhandled message: type: %d\n", dm_hdr->type);
pr_warn("Unhandled message: type: %d\n", dm_hdr->type);
}
}
......
// SPDX-License-Identifier: GPL-2.0
#include "hyperv_vmbus.h"
#define CREATE_TRACE_POINTS
......
// SPDX-License-Identifier: GPL-2.0
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hyperv
......
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hyperv
#if !defined(_HV_TRACE_BALLOON_H) || defined(TRACE_HEADER_MULTI_READ)
#define _HV_TRACE_BALLOON_H
#include <linux/tracepoint.h>
TRACE_EVENT(balloon_status,
TP_PROTO(u64 available, u64 committed,
unsigned long vm_memory_committed,
unsigned long pages_ballooned,
unsigned long pages_added,
unsigned long pages_onlined),
TP_ARGS(available, committed, vm_memory_committed,
pages_ballooned, pages_added, pages_onlined),
TP_STRUCT__entry(
__field(u64, available)
__field(u64, committed)
__field(unsigned long, vm_memory_committed)
__field(unsigned long, pages_ballooned)
__field(unsigned long, pages_added)
__field(unsigned long, pages_onlined)
),
TP_fast_assign(
__entry->available = available;
__entry->committed = committed;
__entry->vm_memory_committed = vm_memory_committed;
__entry->pages_ballooned = pages_ballooned;
__entry->pages_added = pages_added;
__entry->pages_onlined = pages_onlined;
),
TP_printk("available %lld, committed %lld; vm_memory_committed %ld;"
" pages_ballooned %ld, pages_added %ld, pages_onlined %ld",
__entry->available, __entry->committed,
__entry->vm_memory_committed, __entry->pages_ballooned,
__entry->pages_added, __entry->pages_onlined
)
);
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE hv_trace_balloon
#endif /* _HV_TRACE_BALLOON_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
......@@ -57,7 +57,9 @@ union hv_timer_config {
u64 periodic:1;
u64 lazy:1;
u64 auto_enable:1;
u64 reserved_z0:12;
u64 apic_vector:8;
u64 direct_mode:1;
u64 reserved_z0:3;
u64 sintx:4;
u64 reserved_z1:44;
};
......
menu "HW tracing support"
source "drivers/hwtracing/stm/Kconfig"
source "drivers/hwtracing/intel_th/Kconfig"
endmenu
......@@ -315,7 +315,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata)
}
pc = debug_adjust_pc(drvdata);
dev_emerg(dev, " EDPCSR: [<%p>] %pS\n", (void *)pc, (void *)pc);
dev_emerg(dev, " EDPCSR: [<%px>] %pS\n", (void *)pc, (void *)pc);
if (drvdata->edcidsr_present)
dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr);
......
......@@ -1780,7 +1780,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
*/
for (j = 0; j < 8; j++) {
if (maskbyte & 1)
config->ctxid_pid[i] &= ~(0xFF << (j * 8));
config->ctxid_pid[i] &= ~(0xFFUL << (j * 8));
maskbyte >>= 1;
}
/* Select the next ctxid comparator mask value */
......@@ -1963,7 +1963,7 @@ static ssize_t vmid_masks_store(struct device *dev,
*/
for (j = 0; j < 8; j++) {
if (maskbyte & 1)
config->vmid_val[i] &= ~(0xFF << (j * 8));
config->vmid_val[i] &= ~(0xFFUL << (j * 8));
maskbyte >>= 1;
}
/* Select the next vmid comparator mask value */
......
......@@ -25,6 +25,18 @@ config INTEL_TH_PCI
Say Y here to enable PCI Intel TH support.
config INTEL_TH_ACPI
tristate "Intel(R) Trace Hub ACPI controller"
depends on ACPI
help
Intel(R) Trace Hub may exist as an ACPI device. This option enables
support glue layer for ACPI-based Intel TH. This typically implies
'host debugger' mode, that is, the trace configuration and capture
is handled by an external debug host and corresponding controls will
not be available on the target.
Say Y here to enable ACPI Intel TH support.
config INTEL_TH_GTH
tristate "Intel(R) Trace Hub Global Trace Hub"
help
......
......@@ -6,6 +6,9 @@ intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o
obj-$(CONFIG_INTEL_TH_PCI) += intel_th_pci.o
intel_th_pci-y := pci.o
obj-$(CONFIG_INTEL_TH_ACPI) += intel_th_acpi.o
intel_th_acpi-y := acpi.o
obj-$(CONFIG_INTEL_TH_GTH) += intel_th_gth.o
intel_th_gth-y := gth.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub ACPI driver
*
* Copyright (C) 2017 Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include "intel_th.h"
#define DRIVER_NAME "intel_th_acpi"
static const struct intel_th_drvdata intel_th_acpi_pch = {
.host_mode_only = 1,
};
static const struct intel_th_drvdata intel_th_acpi_uncore = {
.host_mode_only = 1,
};
static const struct acpi_device_id intel_th_acpi_ids[] = {
{ "INTC1000", (kernel_ulong_t)&intel_th_acpi_uncore },
{ "INTC1001", (kernel_ulong_t)&intel_th_acpi_pch },
{ "", 0 },
};
MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids);
static int intel_th_acpi_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
const struct acpi_device_id *id;
struct intel_th *th;
id = acpi_match_device(intel_th_acpi_ids, &pdev->dev);
if (!id)
return -ENODEV;
th = intel_th_alloc(&pdev->dev, (void *)id->driver_data,
pdev->resource, pdev->num_resources, -1);
if (IS_ERR(th))
return PTR_ERR(th);
adev->driver_data = th;
return 0;
}
static int intel_th_acpi_remove(struct platform_device *pdev)
{
struct intel_th *th = platform_get_drvdata(pdev);
intel_th_free(th);
return 0;
}
static struct platform_driver intel_th_acpi_driver = {
.probe = intel_th_acpi_probe,
.remove = intel_th_acpi_remove,
.driver = {
.name = DRIVER_NAME,
.acpi_match_table = intel_th_acpi_ids,
},
};
module_platform_driver(intel_th_acpi_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel(R) Trace Hub ACPI controller driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub driver core
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......@@ -638,7 +630,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
thdev->output.port = -1;
thdev->output.scratchpad = subdev->scrpd;
} else if (subdev->type == INTEL_TH_SWITCH) {
thdev->host_mode = host_mode;
thdev->host_mode =
INTEL_TH_CAP(th, host_mode_only) ? true : host_mode;
th->hub = thdev;
}
......@@ -737,7 +730,8 @@ static int intel_th_populate(struct intel_th *th)
struct intel_th_device *thdev;
/* only allow SOURCE and SWITCH devices in host mode */
if (host_mode && subdev->type == INTEL_TH_OUTPUT)
if ((INTEL_TH_CAP(th, host_mode_only) || host_mode) &&
subdev->type == INTEL_TH_OUTPUT)
continue;
/*
......@@ -813,7 +807,14 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
struct resource *devres, unsigned int ndevres, int irq)
{
struct intel_th *th;
int err;
int err, r;
if (irq == -1)
for (r = 0; r < ndevres; r++)
if (devres[r].flags & IORESOURCE_IRQ) {
irq = devres[r].start;
break;
}
th = kzalloc(sizeof(*th), GFP_KERNEL);
if (!th)
......@@ -935,9 +936,13 @@ EXPORT_SYMBOL_GPL(intel_th_trace_disable);
int intel_th_set_output(struct intel_th_device *thdev,
unsigned int master)
{
struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
struct intel_th_device *hub = to_intel_th_hub(thdev);
struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
/* In host mode, this is up to the external debugger, do nothing. */
if (hub->host_mode)
return 0;
if (!hubdrv->set_output)
return -ENOTSUPP;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub driver debugging
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/types.h>
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub driver debugging
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_DEBUG_H__
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Global Trace Hub
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub Global Trace Hub (GTH) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_GTH_H__
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_H__
......@@ -50,9 +42,11 @@ struct intel_th_output {
/**
* struct intel_th_drvdata - describes hardware capabilities and quirks
* @tscu_enable: device needs SW to enable time stamping unit
* @host_mode_only: device can only operate in 'host debugger' mode
*/
struct intel_th_drvdata {
unsigned int tscu_enable : 1;
unsigned int tscu_enable : 1,
host_mode_only : 1;
};
#define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0)
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Memory Storage Unit
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Memory Storage Unit (MSU) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_MSU_H__
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub pci driver
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub PTI output driver
*
* Copyright (C) 2014-2016 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub PTI output data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_STH_H__
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Software Trace Hub support
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub Software Trace Hub (STH) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_STH_H__
......
// SPDX-License-Identifier: GPL-2.0
/*
* Simple kernel console driver for STM devices
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM console will send kernel messages over STM devices to a trace host.
*/
......
// SPDX-License-Identifier: GPL-2.0
/*
* System Trace Module (STM) infrastructure
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
......
// SPDX-License-Identifier: GPL-2.0
/*
* A dummy STM device for stm/stm_source class testing.
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
......@@ -20,6 +12,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stm.h>
#include <uapi/linux/stm.h>
static ssize_t notrace
dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
......@@ -52,6 +45,18 @@ static unsigned int fail_mode;
module_param(fail_mode, int, 0600);
static unsigned int master_min;
module_param(master_min, int, 0400);
static unsigned int master_max = STP_MASTER_MAX;
module_param(master_max, int, 0400);
static unsigned int nr_channels = STP_CHANNEL_MAX;
module_param(nr_channels, int, 0400);
static int dummy_stm_link(struct stm_data *data, unsigned int master,
unsigned int channel)
{
......@@ -68,14 +73,19 @@ static int dummy_stm_init(void)
if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX)
return -EINVAL;
if (master_min > master_max ||
master_max > STP_MASTER_MAX ||
nr_channels > STP_CHANNEL_MAX)
return -EINVAL;
for (i = 0; i < nr_dummies; i++) {
dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i);
if (!dummy_stm[i].name)
goto fail_unregister;
dummy_stm[i].sw_start = 0x0000;
dummy_stm[i].sw_end = 0xffff;
dummy_stm[i].sw_nchannels = 0xffff;
dummy_stm[i].sw_start = master_min;
dummy_stm[i].sw_end = master_max;
dummy_stm[i].sw_nchannels = nr_channels;
dummy_stm[i].packet = dummy_stm_packet;
dummy_stm[i].link = dummy_stm_link;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Simple heartbeat STM source driver
* Copyright (c) 2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* Heartbeat STM source will send repetitive messages over STM devices to a
* trace host.
*/
......
// SPDX-License-Identifier: GPL-2.0
/*
* System Trace Module (STM) master/channel allocation policy management
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* A master/channel allocation policy allows mapping string identifiers to
* master and channel ranges, where allocation can be done.
*/
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* System Trace Module (STM) infrastructure
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
......
......@@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev)
static const struct pci_device_id mcb_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
{ PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
......
......@@ -75,7 +75,6 @@ config ATMEL_TCB_CLKSRC
config ATMEL_TCB_CLKSRC_BLOCK
int
depends on ATMEL_TCB_CLKSRC
prompt "TC Block" if CPU_AT32AP700X
default 0
range 0 1
help
......
......@@ -12,7 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_LKDTM) += lkdtm/
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
......@@ -57,21 +57,3 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-$(CONFIG_MISC_RTSX) += cardreader/
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o
KCOV_INSTRUMENT_lkdtm_rodata.o := n
OBJCOPYFLAGS :=
OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
--set-section-flags .text=alloc,readonly \
--rename-section .text=.rodata
targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o
$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE
$(call if_changed,objcopy)
......@@ -7,6 +7,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
......@@ -20,12 +21,17 @@
#define DEVICE_NAME "aspeed-lpc-ctrl"
#define HICR5 0x0
#define HICR5_ENL2H BIT(8)
#define HICR5_ENFWH BIT(10)
#define HICR7 0x8
#define HICR8 0xc
struct aspeed_lpc_ctrl {
struct miscdevice miscdev;
struct regmap *regmap;
struct clk *clk;
phys_addr_t mem_base;
resource_size_t mem_size;
u32 pnor_size;
......@@ -153,8 +159,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
if (rc)
return rc;
return regmap_write(lpc_ctrl->regmap, HICR8,
(~(map.size - 1)) | ((map.size >> 16) - 1));
rc = regmap_write(lpc_ctrl->regmap, HICR8,
(~(map.size - 1)) | ((map.size >> 16) - 1));
if (rc)
return rc;
/*
* Enable LPC FHW cycles. This is required for the host to
* access the regions specified.
*/
return regmap_update_bits(lpc_ctrl->regmap, HICR5,
HICR5_ENFWH | HICR5_ENL2H,
HICR5_ENFWH | HICR5_ENL2H);
}
return -EINVAL;
......@@ -221,16 +237,33 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
return -ENODEV;
}
lpc_ctrl->clk = devm_clk_get(dev, NULL);
if (IS_ERR(lpc_ctrl->clk)) {
dev_err(dev, "couldn't get clock\n");
return PTR_ERR(lpc_ctrl->clk);
}
rc = clk_prepare_enable(lpc_ctrl->clk);
if (rc) {
dev_err(dev, "couldn't enable clock\n");
return rc;
}
lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
lpc_ctrl->miscdev.name = DEVICE_NAME;
lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
lpc_ctrl->miscdev.parent = dev;
rc = misc_register(&lpc_ctrl->miscdev);
if (rc)
if (rc) {
dev_err(dev, "Unable to register device\n");
else
dev_info(dev, "Loaded at %pr\n", &resm);
goto err;
}
dev_info(dev, "Loaded at %pr\n", &resm);
return 0;
err:
clk_disable_unprepare(lpc_ctrl->clk);
return rc;
}
......@@ -239,6 +272,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
misc_deregister(&lpc_ctrl->miscdev);
clk_disable_unprepare(lpc_ctrl->clk);
return 0;
}
......
......@@ -388,17 +388,17 @@ static void rts5260_disable_ocp(struct rtsx_pcr *pcr)
OC_POWER_DOWN);
}
int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
static int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
{
return rtsx_pci_read_register(pcr, REG_OCPSTAT, val);
}
int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
static int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
{
return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val);
}
void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
static void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
{
u8 mask = 0;
u8 val = 0;
......@@ -418,7 +418,7 @@ void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0);
}
void rts5260_process_ocp(struct rtsx_pcr *pcr)
static void rts5260_process_ocp(struct rtsx_pcr *pcr)
{
if (!pcr->option.ocp_en)
return;
......@@ -449,7 +449,7 @@ void rts5260_process_ocp(struct rtsx_pcr *pcr)
}
}
int rts5260_init_hw(struct rtsx_pcr *pcr)
static int rts5260_init_hw(struct rtsx_pcr *pcr)
{
int err;
......@@ -620,7 +620,7 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr)
return 0;
}
void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
static void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
{
struct rtsx_cr_option *option = &pcr->option;
u8 val = 0;
......
// SPDX-License-Identifier: GPL-2.0+
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
......@@ -63,8 +60,6 @@ struct at24_client {
};
struct at24_data {
struct at24_platform_data chip;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
......@@ -75,7 +70,10 @@ struct at24_data {
unsigned int num_addresses;
unsigned int offset_adj;
struct nvmem_config nvmem_config;
u32 byte_len;
u16 page_size;
u8 flags;
struct nvmem_device *nvmem;
struct gpio_desc *wp_gpio;
......@@ -239,8 +237,6 @@ static const struct acpi_device_id at24_acpi_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
......@@ -255,7 +251,7 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
{
unsigned int i;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
if (at24->flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
......@@ -266,6 +262,11 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
return &at24->client[i];
}
static struct device *at24_base_client_dev(struct at24_data *at24)
{
return &at24->client[0].client->dev;
}
static size_t at24_adjust_read_count(struct at24_data *at24,
unsigned int offset, size_t count)
{
......@@ -277,8 +278,8 @@ static size_t at24_adjust_read_count(struct at24_data *at24,
* the next slave address: truncate the count to the slave boundary,
* so that the read never straddles slaves.
*/
if (at24->chip.flags & AT24_FLAG_NO_RDROL) {
bits = (at24->chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
if (at24->flags & AT24_FLAG_NO_RDROL) {
bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8;
remainder = BIT(bits) - offset;
if (count > remainder)
count = remainder;
......@@ -337,7 +338,7 @@ static size_t at24_adjust_write_count(struct at24_data *at24,
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
next_page = roundup(offset + 1, at24->page_size);
if (offset + count > next_page)
count = next_page - offset;
......@@ -371,15 +372,18 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf,
static int at24_read(void *priv, unsigned int off, void *val, size_t count)
{
struct at24_data *at24 = priv;
struct device *dev = &at24->client[0].client->dev;
struct at24_data *at24;
struct device *dev;
char *buf = val;
int ret;
at24 = priv;
dev = at24_base_client_dev(at24);
if (unlikely(!count))
return count;
if (off + count > at24->chip.byte_len)
if (off + count > at24->byte_len)
return -EINVAL;
ret = pm_runtime_get_sync(dev);
......@@ -395,17 +399,15 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
mutex_lock(&at24->lock);
while (count) {
int status;
status = at24_regmap_read(at24, buf, off, count);
if (status < 0) {
ret = at24_regmap_read(at24, buf, off, count);
if (ret < 0) {
mutex_unlock(&at24->lock);
pm_runtime_put(dev);
return status;
return ret;
}
buf += status;
off += status;
count -= status;
buf += ret;
off += ret;
count -= ret;
}
mutex_unlock(&at24->lock);
......@@ -417,15 +419,18 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
static int at24_write(void *priv, unsigned int off, void *val, size_t count)
{
struct at24_data *at24 = priv;
struct device *dev = &at24->client[0].client->dev;
struct at24_data *at24;
struct device *dev;
char *buf = val;
int ret;
at24 = priv;
dev = at24_base_client_dev(at24);
if (unlikely(!count))
return -EINVAL;
if (off + count > at24->chip.byte_len)
if (off + count > at24->byte_len)
return -EINVAL;
ret = pm_runtime_get_sync(dev);
......@@ -442,18 +447,16 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
gpiod_set_value_cansleep(at24->wp_gpio, 0);
while (count) {
int status;
status = at24_regmap_write(at24, buf, off, count);
if (status < 0) {
ret = at24_regmap_write(at24, buf, off, count);
if (ret < 0) {
gpiod_set_value_cansleep(at24->wp_gpio, 1);
mutex_unlock(&at24->lock);
pm_runtime_put(dev);
return status;
return ret;
}
buf += status;
off += status;
count -= status;
buf += ret;
off += ret;
count -= ret;
}
gpiod_set_value_cansleep(at24->wp_gpio, 1);
......@@ -464,7 +467,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
return 0;
}
static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
static void at24_properties_to_pdata(struct device *dev,
struct at24_platform_data *chip)
{
int err;
u32 val;
......@@ -491,6 +495,43 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
}
}
static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
{
struct device_node *of_node = dev->of_node;
const struct at24_chip_data *cdata;
const struct i2c_device_id *id;
struct at24_platform_data *pd;
pd = dev_get_platdata(dev);
if (pd) {
memcpy(pdata, pd, sizeof(*pdata));
return 0;
}
id = i2c_match_id(at24_ids, to_i2c_client(dev));
/*
* The I2C core allows OF nodes compatibles to match against the
* I2C device ID table as a fallback, so check not only if an OF
* node is present but also if it matches an OF device ID entry.
*/
if (of_node && of_match_device(at24_of_match, dev))
cdata = of_device_get_match_data(dev);
else if (id)
cdata = (void *)&id->driver_data;
else
cdata = acpi_device_get_match_data(dev);
if (!cdata)
return -ENODEV;
pdata->byte_len = cdata->byte_len;
pdata->flags = cdata->flags;
at24_properties_to_pdata(dev, pdata);
return 0;
}
static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
{
if (flags & AT24_FLAG_MAC) {
......@@ -514,102 +555,83 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
}
}
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
static int at24_probe(struct i2c_client *client)
{
struct at24_platform_data chip = { 0 };
const struct at24_chip_data *cd = NULL;
bool writable;
struct at24_data *at24;
int err;
unsigned int i, num_addresses;
struct regmap_config regmap_config = { };
struct nvmem_config nvmem_config = { };
struct at24_platform_data pdata = { };
struct device *dev = &client->dev;
bool i2c_fn_i2c, i2c_fn_block;
unsigned int i, num_addresses;
struct at24_data *at24;
struct regmap *regmap;
size_t at24_size;
bool writable;
u8 test_byte;
int err;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
/*
* The I2C core allows OF nodes compatibles to match against the
* I2C device ID table as a fallback, so check not only if an OF
* node is present but also if it matches an OF device ID entry.
*/
if (client->dev.of_node &&
of_match_device(at24_of_match, &client->dev)) {
cd = of_device_get_match_data(&client->dev);
} else if (id) {
cd = (void *)id->driver_data;
} else {
const struct acpi_device_id *aid;
aid = acpi_match_device(at24_acpi_ids, &client->dev);
if (aid)
cd = (void *)aid->driver_data;
}
if (!cd)
return -ENODEV;
i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
i2c_fn_block = i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
chip.byte_len = cd->byte_len;
chip.flags = cd->flags;
at24_get_pdata(&client->dev, &chip);
}
err = at24_get_pdata(dev, &pdata);
if (err)
return err;
if (!i2c_fn_i2c && !i2c_fn_block)
pdata.page_size = 1;
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
if (!pdata.page_size) {
dev_err(dev, "page_size must not be 0!\n");
return -EINVAL;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
chip.page_size = 1;
if (!is_power_of_2(pdata.page_size))
dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
if (chip.flags & AT24_FLAG_TAKE8ADDR)
if (pdata.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len,
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
num_addresses = DIV_ROUND_UP(pdata.byte_len,
(pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) {
dev_err(dev,
"invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
return -EINVAL;
}
regmap_config.val_bits = 8;
regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
regmap_config.disable_locking = true;
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
num_addresses * sizeof(struct at24_client), GFP_KERNEL);
regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client);
at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
at24->chip = chip;
at24->byte_len = pdata.byte_len;
at24->page_size = pdata.page_size;
at24->flags = pdata.flags;
at24->num_addresses = num_addresses;
at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len);
at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len);
at24->client[0].client = client;
at24->client[0].regmap = regmap;
at24->wp_gpio = devm_gpiod_get_optional(&client->dev,
"wp", GPIOD_OUT_HIGH);
at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH);
if (IS_ERR(at24->wp_gpio))
return PTR_ERR(at24->wp_gpio);
at24->client[0].client = client;
at24->client[0].regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(at24->client[0].regmap))
return PTR_ERR(at24->client[0].regmap);
if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) {
dev_err(&client->dev,
"invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
return -EINVAL;
}
writable = !(chip.flags & AT24_FLAG_READONLY);
writable = !(pdata.flags & AT24_FLAG_READONLY);
if (writable) {
at24->write_max = min_t(unsigned int,
chip.page_size, at24_io_limit);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
at24->write_max > I2C_SMBUS_BLOCK_MAX)
pdata.page_size, at24_io_limit);
if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
at24->write_max = I2C_SMBUS_BLOCK_MAX;
}
......@@ -618,8 +640,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->client[i].client = i2c_new_dummy(client->adapter,
client->addr + i);
if (!at24->client[i].client) {
dev_err(&client->dev, "address 0x%02x unavailable\n",
client->addr + i);
dev_err(dev, "address 0x%02x unavailable\n",
client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
......@@ -635,48 +657,47 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_set_clientdata(client, at24);
/* enable runtime pm */
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
* Perform a one-byte test read to verify that the
* chip is functional.
*/
err = at24_read(at24, 0, &test_byte, 1);
pm_runtime_idle(&client->dev);
pm_runtime_idle(dev);
if (err) {
err = -ENODEV;
goto err_clients;
}
at24->nvmem_config.name = dev_name(&client->dev);
at24->nvmem_config.dev = &client->dev;
at24->nvmem_config.read_only = !writable;
at24->nvmem_config.root_only = true;
at24->nvmem_config.owner = THIS_MODULE;
at24->nvmem_config.compat = true;
at24->nvmem_config.base_dev = &client->dev;
at24->nvmem_config.reg_read = at24_read;
at24->nvmem_config.reg_write = at24_write;
at24->nvmem_config.priv = at24;
at24->nvmem_config.stride = 1;
at24->nvmem_config.word_size = 1;
at24->nvmem_config.size = chip.byte_len;
at24->nvmem = nvmem_register(&at24->nvmem_config);
nvmem_config.name = dev_name(dev);
nvmem_config.dev = dev;
nvmem_config.read_only = !writable;
nvmem_config.root_only = true;
nvmem_config.owner = THIS_MODULE;
nvmem_config.compat = true;
nvmem_config.base_dev = dev;
nvmem_config.reg_read = at24_read;
nvmem_config.reg_write = at24_write;
nvmem_config.priv = at24;
nvmem_config.stride = 1;
nvmem_config.word_size = 1;
nvmem_config.size = pdata.byte_len;
at24->nvmem = nvmem_register(&nvmem_config);
if (IS_ERR(at24->nvmem)) {
err = PTR_ERR(at24->nvmem);
goto err_clients;
}
dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
chip.byte_len, client->name,
writable ? "writable" : "read-only", at24->write_max);
dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
pdata.byte_len, client->name,
writable ? "writable" : "read-only", at24->write_max);
/* export data to kernel code */
if (chip.setup)
chip.setup(at24->nvmem, chip.context);
if (pdata.setup)
pdata.setup(at24->nvmem, pdata.context);
return 0;
......@@ -685,7 +706,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (at24->client[i].client)
i2c_unregister_device(at24->client[i].client);
pm_runtime_disable(&client->dev);
pm_runtime_disable(dev);
return err;
}
......@@ -708,15 +729,13 @@ static int at24_remove(struct i2c_client *client)
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.of_match_table = at24_of_match,
.acpi_match_table = ACPI_PTR(at24_acpi_ids),
},
.probe = at24_probe,
.probe_new = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
......
......@@ -102,7 +102,7 @@ static int at25_ee_read(void *priv, unsigned int offset,
}
spi_message_init(&m);
memset(t, 0, sizeof t);
memset(t, 0, sizeof(t));
t[0].tx_buf = command;
t[0].len = at25->addrlen + 1;
......
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LKDTM) += lkdtm.o
lkdtm-$(CONFIG_LKDTM) += core.o
lkdtm-$(CONFIG_LKDTM) += bugs.o
lkdtm-$(CONFIG_LKDTM) += heap.o
lkdtm-$(CONFIG_LKDTM) += perms.o
lkdtm-$(CONFIG_LKDTM) += refcount.o
lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += usercopy.o
KCOV_INSTRUMENT_rodata.o := n
OBJCOPYFLAGS :=
OBJCOPYFLAGS_rodata_objcopy.o := \
--set-section-flags .text=alloc,readonly \
--rename-section .text=.rodata
targets += rodata.o rodata_objcopy.o
$(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE
$(call if_changed,objcopy)
// SPDX-License-Identifier: GPL-2.0
/*
* This is for all the tests related to refcount bugs (e.g. overflow,
* underflow, reaching zero untested, etc).
......
......@@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
goto out;
}
while (cl->tx_cb_queued >= bus->tx_queue_limit) {
mutex_unlock(&bus->device_lock);
rets = wait_event_interruptible(cl->tx_wait,
cl->writing_state == MEI_WRITE_COMPLETE ||
(!mei_cl_is_connected(cl)));
mutex_lock(&bus->device_lock);
if (rets) {
if (signal_pending(current))
rets = -EINTR;
goto out;
}
if (!mei_cl_is_connected(cl)) {
rets = -ENODEV;
goto out;
}
}
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
if (!cb) {
rets = -ENOMEM;
......@@ -449,6 +466,29 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev)
}
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
* mei_cl_bus_module_get - acquire module of the underlying
* hw driver.
*
* @cldev: mei client device
*
* Return: true on success; false if the module was removed.
*/
static bool mei_cl_bus_module_get(struct mei_cl_device *cldev)
{
return try_module_get(cldev->bus->dev->driver->owner);
}
/**
* mei_cl_bus_module_put - release the underlying hw module.
*
* @cldev: mei client device
*/
static void mei_cl_bus_module_put(struct mei_cl_device *cldev)
{
module_put(cldev->bus->dev->driver->owner);
}
/**
* mei_cldev_enable - enable me client device
* create connection with me client
......@@ -487,9 +527,17 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
goto out;
}
if (!mei_cl_bus_module_get(cldev)) {
dev_err(&cldev->dev, "get hw module failed");
ret = -ENODEV;
goto out;
}
ret = mei_cl_connect(cl, cldev->me_cl, NULL);
if (ret < 0)
if (ret < 0) {
dev_err(&cldev->dev, "cannot connect\n");
mei_cl_bus_module_put(cldev);
}
out:
mutex_unlock(&bus->device_lock);
......@@ -553,6 +601,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
dev_err(bus->dev, "Could not disconnect from the ME client\n");
out:
mei_cl_bus_module_put(cldev);
/* Flush queues and remove any pending read */
mei_cl_flush_queues(cl, NULL);
mei_cl_unlink(cl);
......@@ -562,37 +612,6 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
}
EXPORT_SYMBOL_GPL(mei_cldev_disable);
/**
* mei_cl_bus_module_get - acquire module of the underlying
* hw module.
*
* @cl: host client
*
* Return: true on success; false if the module was removed.
*/
bool mei_cl_bus_module_get(struct mei_cl *cl)
{
struct mei_cl_device *cldev = cl->cldev;
if (!cldev)
return true;
return try_module_get(cldev->bus->dev->driver->owner);
}
/**
* mei_cl_bus_module_put - release the underlying hw module.
*
* @cl: host client
*/
void mei_cl_bus_module_put(struct mei_cl *cl)
{
struct mei_cl_device *cldev = cl->cldev;
if (cldev)
module_put(cldev->bus->dev->driver->owner);
}
/**
* mei_cl_device_find - find matching entry in the driver id table
*
......
......@@ -349,6 +349,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
kfree(cb);
}
/**
* mei_tx_cb_queue - queue tx callback
*
* Locking: called under "dev->device_lock" lock
*
* @cb: mei callback struct
* @head: an instance of list to queue on
*/
static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
struct list_head *head)
{
list_add_tail(&cb->list, head);
cb->cl->tx_cb_queued++;
}
/**
* mei_tx_cb_dequeue - dequeue tx callback
*
* Locking: called under "dev->device_lock" lock
*
* @cb: mei callback struct to dequeue and free
*/
static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
{
if (!WARN_ON(cb->cl->tx_cb_queued == 0))
cb->cl->tx_cb_queued--;
mei_io_cb_free(cb);
}
/**
* mei_io_cb_init - allocate and initialize io callback
*
......@@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
}
/**
* __mei_io_list_flush_cl - removes and frees cbs belonging to cl.
* mei_io_list_flush_cl - removes cbs belonging to the cl.
*
* @head: an instance of our list structure
* @cl: host client, can be NULL for flushing the whole list
* @free: whether to free the cbs
* @cl: host client
*/
static void __mei_io_list_flush_cl(struct list_head *head,
const struct mei_cl *cl, bool free)
static void mei_io_list_flush_cl(struct list_head *head,
const struct mei_cl *cl)
{
struct mei_cl_cb *cb, *next;
/* enable removing everything if no cl is specified */
list_for_each_entry_safe(cb, next, head, list) {
if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
if (mei_cl_cmp_id(cl, cb->cl))
list_del_init(&cb->list);
if (free)
mei_io_cb_free(cb);
}
}
}
/**
* mei_io_list_flush_cl - removes list entry belonging to cl.
* mei_io_tx_list_free_cl - removes cb belonging to the cl and free them
*
* @head: An instance of our list structure
* @cl: host client
*/
static inline void mei_io_list_flush_cl(struct list_head *head,
const struct mei_cl *cl)
static void mei_io_tx_list_free_cl(struct list_head *head,
const struct mei_cl *cl)
{
__mei_io_list_flush_cl(head, cl, false);
}
struct mei_cl_cb *cb, *next;
/**
* mei_io_list_free_cl - removes cb belonging to cl and free them
*
* @head: An instance of our list structure
* @cl: host client
*/
static inline void mei_io_list_free_cl(struct list_head *head,
const struct mei_cl *cl)
{
__mei_io_list_flush_cl(head, cl, true);
list_for_each_entry_safe(cb, next, head, list) {
if (mei_cl_cmp_id(cl, cb->cl))
mei_tx_cb_dequeue(cb);
}
}
/**
......@@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
dev = cl->dev;
cl_dbg(dev, cl, "remove list entry belonging to cl\n");
mei_io_list_free_cl(&cl->dev->write_list, cl);
mei_io_list_free_cl(&cl->dev->write_waiting_list, cl);
mei_io_tx_list_free_cl(&cl->dev->write_list, cl);
mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
mei_io_list_free_fp(&cl->rd_pending, fp);
......@@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
return;
cl->state = MEI_FILE_DISCONNECTED;
mei_io_list_free_cl(&dev->write_list, cl);
mei_io_list_free_cl(&dev->write_waiting_list, cl);
mei_io_tx_list_free_cl(&dev->write_list, cl);
mei_io_tx_list_free_cl(&dev->write_waiting_list, cl);
mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
mei_cl_wake_all(cl);
......@@ -765,8 +783,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
cl->tx_flow_ctrl_creds = 0;
cl->timer_count = 0;
mei_cl_bus_module_put(cl);
if (!cl->me_cl)
return;
......@@ -1076,9 +1092,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
dev = cl->dev;
if (!mei_cl_bus_module_get(cl))
return -ENODEV;
rets = mei_cl_set_connecting(cl, me_cl);
if (rets)
goto nortpm;
......@@ -1698,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
out:
if (mei_hdr.msg_complete)
list_add_tail(&cb->list, &dev->write_waiting_list);
mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
else
list_add_tail(&cb->list, &dev->write_list);
mei_tx_cb_enqueue(cb, &dev->write_list);
cb = NULL;
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
......@@ -1746,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
switch (cb->fop_type) {
case MEI_FOP_WRITE:
mei_io_cb_free(cb);
mei_tx_cb_dequeue(cb);
cl->writing_state = MEI_WRITE_COMPLETE;
if (waitqueue_active(&cl->tx_wait)) {
wake_up_interruptible(&cl->tx_wait);
......
......@@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
int pos = 0;
int ret;
#define HDR " |me|host|state|rd|wr|\n"
#define HDR " |me|host|state|rd|wr|wrq\n"
if (!dev)
return -ENODEV;
......@@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
list_for_each_entry(cl, &dev->file_list, link) {
pos += scnprintf(buf + pos, bufsz - pos,
"%3d|%2d|%4d|%5d|%2d|%2d|\n",
"%3d|%2d|%4d|%5d|%2d|%2d|%3u\n",
i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
!list_empty(&cl->rd_completed), cl->writing_state);
!list_empty(&cl->rd_completed), cl->writing_state,
cl->tx_cb_queued);
i++;
}
out:
......
......@@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev,
INIT_LIST_HEAD(&dev->write_waiting_list);
INIT_LIST_HEAD(&dev->ctrl_wr_list);
INIT_LIST_HEAD(&dev->ctrl_rd_list);
dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT;
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->reset_work, mei_reset_work);
......
......@@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
goto out;
}
while (cl->tx_cb_queued >= dev->tx_queue_limit) {
if (file->f_flags & O_NONBLOCK) {
rets = -EAGAIN;
goto out;
}
mutex_unlock(&dev->device_lock);
rets = wait_event_interruptible(cl->tx_wait,
cl->writing_state == MEI_WRITE_COMPLETE ||
(!mei_cl_is_connected(cl)));
mutex_lock(&dev->device_lock);
if (rets) {
if (signal_pending(current))
rets = -EINTR;
goto out;
}
if (!mei_cl_is_connected(cl)) {
rets = -ENODEV;
goto out;
}
}
*offset = 0;
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
if (!cb) {
......@@ -507,7 +528,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
break;
default:
dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd);
rets = -ENOIOCTLCMD;
}
......@@ -580,6 +600,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait)
mei_cl_read_start(cl, mei_cl_mtu(cl), file);
}
if (req_events & (POLLOUT | POLLWRNORM)) {
poll_wait(file, &cl->tx_wait, wait);
if (cl->tx_cb_queued < dev->tx_queue_limit)
mask |= POLLOUT | POLLWRNORM;
}
out:
mutex_unlock(&dev->device_lock);
return mask;
......@@ -749,10 +775,48 @@ static ssize_t hbm_ver_drv_show(struct device *device,
}
static DEVICE_ATTR_RO(hbm_ver_drv);
static ssize_t tx_queue_limit_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct mei_device *dev = dev_get_drvdata(device);
u8 size = 0;
mutex_lock(&dev->device_lock);
size = dev->tx_queue_limit;
mutex_unlock(&dev->device_lock);
return snprintf(buf, PAGE_SIZE, "%u\n", size);
}
static ssize_t tx_queue_limit_store(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct mei_device *dev = dev_get_drvdata(device);
u8 limit;
unsigned int inp;
int err;
err = kstrtouint(buf, 10, &inp);
if (err)
return err;
if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN)
return -EINVAL;
limit = inp;
mutex_lock(&dev->device_lock);
dev->tx_queue_limit = limit;
mutex_unlock(&dev->device_lock);
return count;
}
static DEVICE_ATTR_RW(tx_queue_limit);
static struct attribute *mei_attrs[] = {
&dev_attr_fw_status.attr,
&dev_attr_hbm_ver.attr,
&dev_attr_hbm_ver_drv.attr,
&dev_attr_tx_queue_limit.attr,
NULL
};
ATTRIBUTE_GROUPS(mei);
......
......@@ -210,6 +210,7 @@ struct mei_cl_cb {
* @timer_count: watchdog timer for operation completion
* @notify_en: notification - enabled/disabled
* @notify_ev: pending notification event
* @tx_cb_queued: number of tx callbacks in queue
* @writing_state: state of the tx
* @rd_pending: pending read credits
* @rd_completed: completed read
......@@ -234,6 +235,7 @@ struct mei_cl {
u8 timer_count;
u8 notify_en;
u8 notify_ev;
u8 tx_cb_queued;
enum mei_file_transaction_states writing_state;
struct list_head rd_pending;
struct list_head rd_completed;
......@@ -241,6 +243,10 @@ struct mei_cl {
struct mei_cl_device *cldev;
};
#define MEI_TX_QUEUE_LIMIT_DEFAULT 50
#define MEI_TX_QUEUE_LIMIT_MAX 255
#define MEI_TX_QUEUE_LIMIT_MIN 30
/**
* struct mei_hw_ops - hw specific ops
*
......@@ -315,8 +321,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
bool mei_cl_bus_rx_event(struct mei_cl *cl);
bool mei_cl_bus_notify_event(struct mei_cl *cl);
void mei_cl_bus_remove_devices(struct mei_device *bus);
bool mei_cl_bus_module_get(struct mei_cl *cl);
void mei_cl_bus_module_put(struct mei_cl *cl);
int mei_cl_bus_init(void);
void mei_cl_bus_exit(void);
......@@ -361,6 +365,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @write_waiting_list : write completion list
* @ctrl_wr_list : pending control write list
* @ctrl_rd_list : pending control read list
* @tx_queue_limit: tx queues per client linit
*
* @file_list : list of opened handles
* @open_handle_count: number of opened handles
......@@ -425,6 +430,7 @@ struct mei_device {
struct list_head write_waiting_list;
struct list_head ctrl_wr_list;
struct list_head ctrl_rd_list;
u8 tx_queue_limit;
struct list_head file_list;
long open_handle_count;
......
......@@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(vop_unregister_driver);
static void vop_release_dev(struct device *d)
{
put_device(d);
struct vop_device *dev = dev_to_vop(d);
kfree(dev);
}
struct vop_device *
......@@ -174,7 +176,7 @@ vop_register_device(struct device *pdev, int id,
goto free_vdev;
return vdev;
free_vdev:
kfree(vdev);
put_device(&vdev->dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(vop_register_device);
......
......@@ -519,7 +519,7 @@ static struct ocxl_fn *init_function(struct pci_dev *dev)
rc = device_register(&fn->dev);
if (rc) {
deconfigure_function(fn);
device_unregister(&fn->dev);
put_device(&fn->dev);
return ERR_PTR(rc);
}
return fn;
......
......@@ -167,10 +167,10 @@ config MESON_MX_EFUSE
config NVMEM_SNVS_LPGPR
tristate "Support for Low Power General Purpose Register"
depends on SOC_IMX6 || COMPILE_TEST
depends on SOC_IMX6 || SOC_IMX7D || COMPILE_TEST
help
This is a driver for Low Power General Purpose Register (LPGPR) available on
i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
i.MX6 and i.MX7 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
This driver can also be built as a module. If so, the module
will be called nvmem-snvs-lpgpr.
......
......@@ -262,8 +262,7 @@ static int bcm_otpc_probe(struct platform_device *pdev)
else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2"))
priv->map = &otp_map_v2;
else {
dev_err(&pdev->dev,
"%s otpc config map not defined\n", __func__);
dev_err(dev, "%s otpc config map not defined\n", __func__);
return -EINVAL;
}
......@@ -302,27 +301,17 @@ static int bcm_otpc_probe(struct platform_device *pdev)
priv->config = &bcm_otpc_nvmem_config;
nvmem = nvmem_register(&bcm_otpc_nvmem_config);
nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config);
if (IS_ERR(nvmem)) {
dev_err(dev, "error registering nvmem config\n");
return PTR_ERR(nvmem);
}
platform_set_drvdata(pdev, nvmem);
return 0;
}
static int bcm_otpc_remove(struct platform_device *pdev)
{
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
return nvmem_unregister(nvmem);
}
static struct platform_driver bcm_otpc_driver = {
.probe = bcm_otpc_probe,
.remove = bcm_otpc_remove,
.driver = {
.name = "brcm-otpc",
.of_match_table = bcm_otpc_dt_ids,
......
......@@ -473,9 +473,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
nvmem->dev.of_node = config->dev->of_node;
dev_set_name(&nvmem->dev, "%s%d",
config->name ? : "nvmem",
config->name ? config->id : nvmem->id);
if (config->id == -1 && config->name) {
dev_set_name(&nvmem->dev, "%s", config->name);
} else {
dev_set_name(&nvmem->dev, "%s%d",
config->name ? : "nvmem",
config->name ? config->id : nvmem->id);
}
nvmem->read_only = device_property_present(config->dev, "read-only") |
config->read_only;
......@@ -544,6 +549,65 @@ int nvmem_unregister(struct nvmem_device *nvmem)
}
EXPORT_SYMBOL_GPL(nvmem_unregister);
static void devm_nvmem_release(struct device *dev, void *res)
{
WARN_ON(nvmem_unregister(*(struct nvmem_device **)res));
}
/**
* devm_nvmem_register() - Register a managed nvmem device for given
* nvmem_config.
* Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
*
* @config: nvmem device configuration with which nvmem device is created.
*
* Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
* on success.
*/
struct nvmem_device *devm_nvmem_register(struct device *dev,
const struct nvmem_config *config)
{
struct nvmem_device **ptr, *nvmem;
ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
nvmem = nvmem_register(config);
if (!IS_ERR(nvmem)) {
*ptr = nvmem;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return nvmem;
}
EXPORT_SYMBOL_GPL(devm_nvmem_register);
static int devm_nvmem_match(struct device *dev, void *res, void *data)
{
struct nvmem_device **r = res;
return *r == data;
}
/**
* devm_nvmem_unregister() - Unregister previously registered managed nvmem
* device.
*
* @nvmem: Pointer to previously registered nvmem device.
*
* Return: Will be an negative on error or a zero on success.
*/
int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
{
return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
}
EXPORT_SYMBOL(devm_nvmem_unregister);
static struct nvmem_device *__nvmem_device_get(struct device_node *np,
struct nvmem_cell **cellp,
const char *cell_id)
......
......@@ -125,7 +125,7 @@ static int imx_iim_probe(struct platform_device *pdev)
drvdata = of_id->data;
iim->clk = devm_clk_get(&pdev->dev, NULL);
iim->clk = devm_clk_get(dev, NULL);
if (IS_ERR(iim->clk))
return PTR_ERR(iim->clk);
......@@ -138,25 +138,13 @@ static int imx_iim_probe(struct platform_device *pdev)
cfg.size = drvdata->nregs;
cfg.priv = iim;
nvmem = nvmem_register(&cfg);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);
nvmem = devm_nvmem_register(dev, &cfg);
platform_set_drvdata(pdev, nvmem);
return 0;
}
static int imx_iim_remove(struct platform_device *pdev)
{
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
return nvmem_unregister(nvmem);
return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver imx_iim_driver = {
.probe = imx_iim_probe,
.remove = imx_iim_remove,
.driver = {
.name = "imx-iim",
.of_match_table = imx_iim_dt_ids,
......
......@@ -439,7 +439,6 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
static int imx_ocotp_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct resource *res;
struct ocotp_priv *priv;
......@@ -460,32 +459,19 @@ static int imx_ocotp_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
of_id = of_match_device(imx_ocotp_dt_ids, dev);
priv->params = of_device_get_match_data(&pdev->dev);
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
priv->config = &imx_ocotp_nvmem_config;
nvmem = nvmem_register(&imx_ocotp_nvmem_config);
nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);
platform_set_drvdata(pdev, nvmem);
return 0;
}
static int imx_ocotp_remove(struct platform_device *pdev)
{
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
return nvmem_unregister(nvmem);
return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver imx_ocotp_driver = {
.probe = imx_ocotp_probe,
.remove = imx_ocotp_remove,
.driver = {
.name = "imx_ocotp",
.of_match_table = imx_ocotp_dt_ids,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册