Char/Misc patches for 4.17-rc1
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 handfull of even smaller driver subsystem and individual driver updates All of these have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWsShSQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ykNqwCfUbfvopswb1PesHCLABDBsFQChgoAniDa6pS9 kI8TN5MdLN85UU27Mkb6 =BzFR -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
Коммит
06dd3dfeea
|
@ -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
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
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.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
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
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,151 @@
|
|||
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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM hyperv
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#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;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// 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>");
|
|
@ -1,16 +1,8 @@
|
|||
// 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;
|
||||
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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>
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
/* 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__
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
/* 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__
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
/* 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)
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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__
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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__
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
/* 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__
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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.
|
||||
*/
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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;
|
||||
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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.
|
||||
*/
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// 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.
|
||||
*/
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
/* 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;
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
// 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;
|
||||
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);
|
||||
|
||||
aid = acpi_match_device(at24_acpi_ids, &client->dev);
|
||||
if (aid)
|
||||
cd = (void *)aid->driver_data;
|
||||
}
|
||||
if (!cd)
|
||||
return -ENODEV;
|
||||
err = at24_get_pdata(dev, &pdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
chip.byte_len = cd->byte_len;
|
||||
chip.flags = cd->flags;
|
||||
at24_get_pdata(&client->dev, &chip);
|
||||
}
|
||||
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);
|
||||
|
||||
regmap_config.val_bits = 8;
|
||||
regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
|
||||
|
||||
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
|
||||
num_addresses * sizeof(struct at24_client), GFP_KERNEL);
|
||||
if (!at24)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&at24->lock);
|
||||
at24->chip = chip;
|
||||
at24->num_addresses = num_addresses;
|
||||
at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len);
|
||||
|
||||
at24->wp_gpio = devm_gpiod_get_optional(&client->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, ®map_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,
|
||||
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;
|
||||
}
|
||||
|
||||
writable = !(chip.flags & AT24_FLAG_READONLY);
|
||||
regmap_config.val_bits = 8;
|
||||
regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
|
||||
regmap_config.disable_locking = true;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, ®map_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->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(pdata.flags, pdata.byte_len);
|
||||
at24->client[0].client = client;
|
||||
at24->client[0].regmap = regmap;
|
||||
|
||||
at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(at24->wp_gpio))
|
||||
return PTR_ERR(at24->wp_gpio);
|
||||
|
||||
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 @@ err_clients:
|
|||
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;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# 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)
|
|
@ -1,3 +1,4 @@
|
|||
// 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 @@ out:
|
|||
}
|
||||
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,
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче