Char/Misc driver patches for 4.21-rc1
Here is the big set of char and misc driver patches for 4.21-rc1. Lots of different types of driver things in here, as this tree seems to be the "collection of various driver subsystems not big enough to have their own git tree" lately. Anyway, some highlights of the changes in here: - binderfs: is it a rule that all driver subsystems will eventually grow to have their own filesystem? Binder now has one to handle the use of it in containerized systems. This was discussed at the Plumbers conference a few months ago and knocked into mergable shape very fast by Christian Brauner. Who also has signed up to be another binder maintainer, showing a distinct lack of good judgement :) - binder updates and fixes - mei driver updates - fpga driver updates and additions - thunderbolt driver updates - soundwire driver updates - extcon driver updates - nvmem driver updates - hyper-v driver updates - coresight driver updates - pvpanic driver additions and reworking for more device support - lp driver updates. Yes really, it's _finally_ moved to the proper parallal port driver model, something I never thought I would see happen. Good stuff. - other tiny driver updates and fixes. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXCZCUA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymF9QCgx/Z8Fj1qzGVGrIE4flXOi7pxOrgAoMqJEWtU ywwL8M9suKDz7cZT9fWQ =xxr6 -----END PGP SIGNATURE----- Merge tag 'char-misc-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver updates from Greg KH: "Here is the big set of char and misc driver patches for 4.21-rc1. Lots of different types of driver things in here, as this tree seems to be the "collection of various driver subsystems not big enough to have their own git tree" lately. Anyway, some highlights of the changes in here: - binderfs: is it a rule that all driver subsystems will eventually grow to have their own filesystem? Binder now has one to handle the use of it in containerized systems. This was discussed at the Plumbers conference a few months ago and knocked into mergable shape very fast by Christian Brauner. Who also has signed up to be another binder maintainer, showing a distinct lack of good judgement :) - binder updates and fixes - mei driver updates - fpga driver updates and additions - thunderbolt driver updates - soundwire driver updates - extcon driver updates - nvmem driver updates - hyper-v driver updates - coresight driver updates - pvpanic driver additions and reworking for more device support - lp driver updates. Yes really, it's _finally_ moved to the proper parallal port driver model, something I never thought I would see happen. Good stuff. - other tiny driver updates and fixes. All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (116 commits) MAINTAINERS: add another Android binder maintainer intel_th: msu: Fix an off-by-one in attribute store stm class: Add a reference to the SyS-T document stm class: Fix a module refcount leak in policy creation error path char: lp: use new parport device model char: lp: properly count the lp devices char: lp: use first unused lp number while registering char: lp: detach the device when parallel port is removed char: lp: introduce list to save port number bus: qcom: remove duplicated include from qcom-ebi2.c VMCI: Use memdup_user() rather than duplicating its implementation char/rtc: Use of_node_name_eq for node name comparisons misc: mic: fix a DMA pool free failure ptp: fix an IS_ERR() vs NULL check genwqe: Fix size check binder: implement binderfs binder: fix use-after-free due to ksys_close() during fdget() bus: fsl-mc: remove duplicated include files bus: fsl-mc: explicitly define the fsl_mc_command endianness misc: ti-st: make array read_ver_cmd static, shrinks object size ...
This commit is contained in:
commit
457fa3469a
|
@ -21,6 +21,15 @@ Description: Holds a comma separated list of device unique_ids that
|
|||
If a device is authorized automatically during boot its
|
||||
boot attribute is set to 1.
|
||||
|
||||
What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
|
||||
Date: Mar 2019
|
||||
KernelVersion: 4.21
|
||||
Contact: thunderbolt-software@lists.01.org
|
||||
Description: This attribute tells whether the system uses IOMMU
|
||||
for DMA protection. Value of 1 means IOMMU is used 0 means
|
||||
it is not (DMA protection is solely based on Thunderbolt
|
||||
security levels).
|
||||
|
||||
What: /sys/bus/thunderbolt/devices/.../domainX/security
|
||||
Date: Sep 2017
|
||||
KernelVersion: 4.13
|
||||
|
|
|
@ -133,6 +133,26 @@ If the user still wants to connect the device they can either approve
|
|||
the device without a key or write a new key and write 1 to the
|
||||
``authorized`` file to get the new key stored on the device NVM.
|
||||
|
||||
DMA protection utilizing IOMMU
|
||||
------------------------------
|
||||
Recent systems from 2018 and forward with Thunderbolt ports may natively
|
||||
support IOMMU. This means that Thunderbolt security is handled by an IOMMU
|
||||
so connected devices cannot access memory regions outside of what is
|
||||
allocated for them by drivers. When Linux is running on such system it
|
||||
automatically enables IOMMU if not enabled by the user already. These
|
||||
systems can be identified by reading ``1`` from
|
||||
``/sys/bus/thunderbolt/devices/domainX/iommu_dma_protection`` attribute.
|
||||
|
||||
The driver does not do anything special in this case but because DMA
|
||||
protection is handled by the IOMMU, security levels (if set) are
|
||||
redundant. For this reason some systems ship with security level set to
|
||||
``none``. Other systems have security level set to ``user`` in order to
|
||||
support downgrade to older OS, so users who want to automatically
|
||||
authorize devices when IOMMU DMA protection is enabled can use the
|
||||
following ``udev`` rule::
|
||||
|
||||
ACTION=="add", SUBSYSTEM=="thunderbolt", ATTRS{iommu_dma_protection}=="1", ATTR{authorized}=="0", ATTR{authorized}="1"
|
||||
|
||||
Upgrading NVM on Thunderbolt device or host
|
||||
-------------------------------------------
|
||||
Since most of the functionality is handled in firmware running on a
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
Intel Service Layer Driver for Stratix10 SoC
|
||||
============================================
|
||||
Intel Stratix10 SoC is composed of a 64 bit quad-core ARM Cortex A53 hard
|
||||
processor system (HPS) and Secure Device Manager (SDM). When the FPGA is
|
||||
configured from HPS, there needs to be a way for HPS to notify SDM the
|
||||
location and size of the configuration data. Then SDM will get the
|
||||
configuration data from that location and perform the FPGA configuration.
|
||||
|
||||
To meet the whole system security needs and support virtual machine requesting
|
||||
communication with SDM, only the secure world of software (EL3, Exception
|
||||
Layer 3) can interface with SDM. All software entities running on other
|
||||
exception layers must channel through the EL3 software whenever it needs
|
||||
service from SDM.
|
||||
|
||||
Intel Stratix10 service layer driver, running at privileged exception level
|
||||
(EL1, Exception Layer 1), interfaces with the service providers and provides
|
||||
the services for FPGA configuration, QSPI, Crypto and warm reset. Service layer
|
||||
driver also manages secure monitor call (SMC) to communicate with secure monitor
|
||||
code running in EL3.
|
||||
|
||||
Required properties:
|
||||
-------------------
|
||||
The svc node has the following mandatory properties, must be located under
|
||||
the firmware node.
|
||||
|
||||
- compatible: "intel,stratix10-svc"
|
||||
- method: smc or hvc
|
||||
smc - Secure Monitor Call
|
||||
hvc - Hypervisor Call
|
||||
- memory-region:
|
||||
phandle to the reserved memory node. See
|
||||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
|
||||
for details
|
||||
|
||||
Example:
|
||||
-------
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
service_reserved: svcbuffer@0 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x0 0x0 0x0 0x1000000>;
|
||||
alignment = <0x1000>;
|
||||
no-map;
|
||||
};
|
||||
};
|
||||
|
||||
firmware {
|
||||
svc {
|
||||
compatible = "intel,stratix10-svc";
|
||||
method = "smc";
|
||||
memory-region = <&service_reserved>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
Intel Stratix10 SoC FPGA Manager
|
||||
|
||||
Required properties:
|
||||
The fpga_mgr node has the following mandatory property, must be located under
|
||||
firmware/svc node.
|
||||
|
||||
- compatible : should contain "intel,stratix10-soc-fpga-mgr"
|
||||
|
||||
Example:
|
||||
|
||||
firmware {
|
||||
svc {
|
||||
fpga_mgr: fpga-mgr {
|
||||
compatible = "intel,stratix10-soc-fpga-mgr";
|
||||
};
|
||||
};
|
||||
};
|
29
Documentation/devicetree/bindings/misc/pvpanic-mmio.txt
Normal file
29
Documentation/devicetree/bindings/misc/pvpanic-mmio.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
* QEMU PVPANIC MMIO Configuration bindings
|
||||
|
||||
QEMU's emulation / virtualization targets provide the following PVPANIC
|
||||
MMIO Configuration interface on the "virt" machine.
|
||||
type:
|
||||
|
||||
- a read-write, 16-bit wide data register.
|
||||
|
||||
QEMU exposes the data register to guests as memory mapped registers.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "qemu,pvpanic-mmio".
|
||||
- reg: the MMIO region used by the device.
|
||||
* Bytes 0x0 Write panic event to the reg when guest OS panics.
|
||||
* Bytes 0x1 Reserved.
|
||||
|
||||
Example:
|
||||
|
||||
/ {
|
||||
#size-cells = <0x2>;
|
||||
#address-cells = <0x2>;
|
||||
|
||||
pvpanic-mmio@9060000 {
|
||||
compatible = "qemu,pvpanic-mmio";
|
||||
reg = <0x0 0x9060000 0x0 0x2>;
|
||||
};
|
||||
};
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: should be "amlogic,meson-gxbb-efuse"
|
||||
- clocks: phandle to the efuse peripheral clock provided by the
|
||||
clock controller.
|
||||
|
||||
= Data cells =
|
||||
Are child nodes of eFuse, bindings of which as described in
|
||||
|
@ -11,6 +13,7 @@ Example:
|
|||
|
||||
efuse: efuse {
|
||||
compatible = "amlogic,meson-gxbb-efuse";
|
||||
clocks = <&clkc CLKID_EFUSE>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
|
|
|
@ -13,3 +13,33 @@ EDD Interfaces
|
|||
.. kernel-doc:: drivers/firmware/edd.c
|
||||
:internal:
|
||||
|
||||
Intel Stratix10 SoC Service Layer
|
||||
---------------------------------
|
||||
Some features of the Intel Stratix10 SoC require a level of privilege
|
||||
higher than the kernel is granted. Such secure features include
|
||||
FPGA programming. In terms of the ARMv8 architecture, the kernel runs
|
||||
at Exception Level 1 (EL1), access to the features requires
|
||||
Exception Level 3 (EL3).
|
||||
|
||||
The Intel Stratix10 SoC service layer provides an in kernel API for
|
||||
drivers to request access to the secure features. The requests are queued
|
||||
and processed one by one. ARM’s SMCCC is used to pass the execution
|
||||
of the requests on to a secure monitor (EL3).
|
||||
|
||||
.. kernel-doc:: include/linux/firmware/intel/stratix10-svc-client.h
|
||||
:functions: stratix10_svc_command_code
|
||||
|
||||
.. kernel-doc:: include/linux/firmware/intel/stratix10-svc-client.h
|
||||
:functions: stratix10_svc_client_msg
|
||||
|
||||
.. kernel-doc:: include/linux/firmware/intel/stratix10-svc-client.h
|
||||
:functions: stratix10_svc_command_reconfig_payload
|
||||
|
||||
.. kernel-doc:: include/linux/firmware/intel/stratix10-svc-client.h
|
||||
:functions: stratix10_svc_cb_data
|
||||
|
||||
.. kernel-doc:: include/linux/firmware/intel/stratix10-svc-client.h
|
||||
:functions: stratix10_svc_client
|
||||
|
||||
.. kernel-doc:: drivers/firmware/stratix10-svc.c
|
||||
:export:
|
||||
|
|
|
@ -22,3 +22,4 @@ Linux Tracing Technologies
|
|||
hwlat_detector
|
||||
intel_th
|
||||
stm
|
||||
sys-t
|
||||
|
|
|
@ -958,6 +958,7 @@ M: Arve Hjønnevåg <arve@android.com>
|
|||
M: Todd Kjos <tkjos@android.com>
|
||||
M: Martijn Coenen <maco@android.com>
|
||||
M: Joel Fernandes <joel@joelfernandes.org>
|
||||
M: Christian Brauner <christian@brauner.io>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
|
||||
L: devel@driverdev.osuosl.org
|
||||
S: Supported
|
||||
|
@ -1442,6 +1443,7 @@ F: arch/arm/mach-ep93xx/micro9.c
|
|||
|
||||
ARM/CORESIGHT FRAMEWORK AND DRIVERS
|
||||
M: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
R: Suzuki K Poulose <suzuki.poulose@arm.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/hwtracing/coresight/*
|
||||
|
@ -16242,7 +16244,7 @@ F: drivers/vme/
|
|||
F: include/linux/vme*
|
||||
|
||||
VMWARE BALLOON DRIVER
|
||||
M: Xavier Deguillard <xdeguillard@vmware.com>
|
||||
M: Julien Freche <jfreche@vmware.com>
|
||||
M: Nadav Amit <namit@vmware.com>
|
||||
M: "VMware, Inc." <pv-drivers@vmware.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
|
|
@ -24,6 +24,19 @@
|
|||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
service_reserved: svcbuffer@0 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x0 0x0 0x0 0x1000000>;
|
||||
alignment = <0x1000>;
|
||||
no-map;
|
||||
};
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -93,6 +106,14 @@
|
|||
interrupt-parent = <&intc>;
|
||||
ranges = <0 0 0 0xffffffff>;
|
||||
|
||||
base_fpga_region {
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x1>;
|
||||
|
||||
compatible = "fpga-region";
|
||||
fpga-mgr = <&fpga_mgr>;
|
||||
};
|
||||
|
||||
clkmgr: clock-controller@ffd10000 {
|
||||
compatible = "intel,stratix10-clkmgr";
|
||||
reg = <0xffd10000 0x1000>;
|
||||
|
@ -537,5 +558,17 @@
|
|||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
firmware {
|
||||
svc {
|
||||
compatible = "intel,stratix10-svc";
|
||||
method = "smc";
|
||||
memory-region = <&service_reserved>;
|
||||
|
||||
fpga_mgr: fpga-mgr {
|
||||
compatible = "intel,stratix10-soc-fpga-mgr";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -24,6 +24,14 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data,
|
|||
acpi_object_type type,
|
||||
const union acpi_object **obj);
|
||||
|
||||
/*
|
||||
* The GUIDs here are made equivalent to each other in order to avoid extra
|
||||
* complexity in the properties handling code, with the caveat that the
|
||||
* kernel will accept certain combinations of GUID and properties that are
|
||||
* not defined without a warning. For instance if any of the properties
|
||||
* from different GUID appear in a property list of another, it will be
|
||||
* accepted by the kernel. Firmware validation tools should catch these.
|
||||
*/
|
||||
static const guid_t prp_guids[] = {
|
||||
/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
|
||||
GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
|
||||
|
@ -31,6 +39,9 @@ static const guid_t prp_guids[] = {
|
|||
/* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
|
||||
GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
|
||||
0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
|
||||
/* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */
|
||||
GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
|
||||
0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
|
||||
};
|
||||
|
||||
static const guid_t ads_guid =
|
||||
|
|
|
@ -20,6 +20,18 @@ config ANDROID_BINDER_IPC
|
|||
Android process, using Binder to identify, invoke and pass arguments
|
||||
between said processes.
|
||||
|
||||
config ANDROID_BINDERFS
|
||||
bool "Android Binderfs filesystem"
|
||||
depends on ANDROID_BINDER_IPC
|
||||
default n
|
||||
---help---
|
||||
Binderfs is a pseudo-filesystem for the Android Binder IPC driver
|
||||
which can be mounted per-ipc namespace allowing to run multiple
|
||||
instances of Android.
|
||||
Each binderfs mount initially only contains a binder-control device.
|
||||
It can be used to dynamically allocate new binder IPC devices via
|
||||
ioctls.
|
||||
|
||||
config ANDROID_BINDER_DEVICES
|
||||
string "Android Binder devices"
|
||||
depends on ANDROID_BINDER_IPC
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
ccflags-y += -I$(src) # needed for trace events
|
||||
|
||||
obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o
|
||||
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o
|
||||
obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o
|
||||
|
|
|
@ -72,12 +72,14 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/task_work.h>
|
||||
|
||||
#include <uapi/linux/android/binder.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "binder_alloc.h"
|
||||
#include "binder_internal.h"
|
||||
#include "binder_trace.h"
|
||||
|
||||
static HLIST_HEAD(binder_deferred_list);
|
||||
|
@ -94,22 +96,8 @@ static struct dentry *binder_debugfs_dir_entry_root;
|
|||
static struct dentry *binder_debugfs_dir_entry_proc;
|
||||
static atomic_t binder_last_id;
|
||||
|
||||
#define BINDER_DEBUG_ENTRY(name) \
|
||||
static int binder_##name##_open(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
return single_open(file, binder_##name##_show, inode->i_private); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations binder_##name##_fops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = binder_##name##_open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
static int binder_proc_show(struct seq_file *m, void *unused);
|
||||
BINDER_DEBUG_ENTRY(proc);
|
||||
static int proc_show(struct seq_file *m, void *unused);
|
||||
DEFINE_SHOW_ATTRIBUTE(proc);
|
||||
|
||||
/* This is only defined in include/asm-arm/sizes.h */
|
||||
#ifndef SZ_1K
|
||||
|
@ -262,20 +250,6 @@ static struct binder_transaction_log_entry *binder_transaction_log_add(
|
|||
return e;
|
||||
}
|
||||
|
||||
struct binder_context {
|
||||
struct binder_node *binder_context_mgr_node;
|
||||
struct mutex context_mgr_node_lock;
|
||||
|
||||
kuid_t binder_context_mgr_uid;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct binder_device {
|
||||
struct hlist_node hlist;
|
||||
struct miscdevice miscdev;
|
||||
struct binder_context context;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_work - work enqueued on a worklist
|
||||
* @entry: node enqueued on list
|
||||
|
@ -660,6 +634,7 @@ struct binder_transaction {
|
|||
#define binder_proc_lock(proc) _binder_proc_lock(proc, __LINE__)
|
||||
static void
|
||||
_binder_proc_lock(struct binder_proc *proc, int line)
|
||||
__acquires(&proc->outer_lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
|
@ -675,6 +650,7 @@ _binder_proc_lock(struct binder_proc *proc, int line)
|
|||
#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__)
|
||||
static void
|
||||
_binder_proc_unlock(struct binder_proc *proc, int line)
|
||||
__releases(&proc->outer_lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
|
@ -690,6 +666,7 @@ _binder_proc_unlock(struct binder_proc *proc, int line)
|
|||
#define binder_inner_proc_lock(proc) _binder_inner_proc_lock(proc, __LINE__)
|
||||
static void
|
||||
_binder_inner_proc_lock(struct binder_proc *proc, int line)
|
||||
__acquires(&proc->inner_lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
|
@ -705,6 +682,7 @@ _binder_inner_proc_lock(struct binder_proc *proc, int line)
|
|||
#define binder_inner_proc_unlock(proc) _binder_inner_proc_unlock(proc, __LINE__)
|
||||
static void
|
||||
_binder_inner_proc_unlock(struct binder_proc *proc, int line)
|
||||
__releases(&proc->inner_lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
|
@ -720,6 +698,7 @@ _binder_inner_proc_unlock(struct binder_proc *proc, int line)
|
|||
#define binder_node_lock(node) _binder_node_lock(node, __LINE__)
|
||||
static void
|
||||
_binder_node_lock(struct binder_node *node, int line)
|
||||
__acquires(&node->lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
|
@ -735,6 +714,7 @@ _binder_node_lock(struct binder_node *node, int line)
|
|||
#define binder_node_unlock(node) _binder_node_unlock(node, __LINE__)
|
||||
static void
|
||||
_binder_node_unlock(struct binder_node *node, int line)
|
||||
__releases(&node->lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
|
@ -751,12 +731,16 @@ _binder_node_unlock(struct binder_node *node, int line)
|
|||
#define binder_node_inner_lock(node) _binder_node_inner_lock(node, __LINE__)
|
||||
static void
|
||||
_binder_node_inner_lock(struct binder_node *node, int line)
|
||||
__acquires(&node->lock) __acquires(&node->proc->inner_lock)
|
||||
{
|
||||
binder_debug(BINDER_DEBUG_SPINLOCKS,
|
||||
"%s: line=%d\n", __func__, line);
|
||||
spin_lock(&node->lock);
|
||||
if (node->proc)
|
||||
binder_inner_proc_lock(node->proc);
|
||||
else
|
||||
/* annotation for sparse */
|
||||
__acquire(&node->proc->inner_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -768,6 +752,7 @@ _binder_node_inner_lock(struct binder_node *node, int line)
|
|||
#define binder_node_inner_unlock(node) _binder_node_inner_unlock(node, __LINE__)
|
||||
static void
|
||||
_binder_node_inner_unlock(struct binder_node *node, int line)
|
||||
__releases(&node->lock) __releases(&node->proc->inner_lock)
|
||||
{
|
||||
struct binder_proc *proc = node->proc;
|
||||
|
||||
|
@ -775,6 +760,9 @@ _binder_node_inner_unlock(struct binder_node *node, int line)
|
|||
"%s: line=%d\n", __func__, line);
|
||||
if (proc)
|
||||
binder_inner_proc_unlock(proc);
|
||||
else
|
||||
/* annotation for sparse */
|
||||
__release(&node->proc->inner_lock);
|
||||
spin_unlock(&node->lock);
|
||||
}
|
||||
|
||||
|
@ -1384,10 +1372,14 @@ static void binder_dec_node_tmpref(struct binder_node *node)
|
|||
binder_node_inner_lock(node);
|
||||
if (!node->proc)
|
||||
spin_lock(&binder_dead_nodes_lock);
|
||||
else
|
||||
__acquire(&binder_dead_nodes_lock);
|
||||
node->tmp_refs--;
|
||||
BUG_ON(node->tmp_refs < 0);
|
||||
if (!node->proc)
|
||||
spin_unlock(&binder_dead_nodes_lock);
|
||||
else
|
||||
__release(&binder_dead_nodes_lock);
|
||||
/*
|
||||
* Call binder_dec_node() to check if all refcounts are 0
|
||||
* and cleanup is needed. Calling with strong=0 and internal=1
|
||||
|
@ -1890,18 +1882,22 @@ static struct binder_thread *binder_get_txn_from(
|
|||
*/
|
||||
static struct binder_thread *binder_get_txn_from_and_acq_inner(
|
||||
struct binder_transaction *t)
|
||||
__acquires(&t->from->proc->inner_lock)
|
||||
{
|
||||
struct binder_thread *from;
|
||||
|
||||
from = binder_get_txn_from(t);
|
||||
if (!from)
|
||||
if (!from) {
|
||||
__acquire(&from->proc->inner_lock);
|
||||
return NULL;
|
||||
}
|
||||
binder_inner_proc_lock(from->proc);
|
||||
if (t->from) {
|
||||
BUG_ON(from != t->from);
|
||||
return from;
|
||||
}
|
||||
binder_inner_proc_unlock(from->proc);
|
||||
__acquire(&from->proc->inner_lock);
|
||||
binder_thread_dec_tmpref(from);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1973,6 +1969,8 @@ static void binder_send_failed_reply(struct binder_transaction *t,
|
|||
binder_thread_dec_tmpref(target_thread);
|
||||
binder_free_transaction(t);
|
||||
return;
|
||||
} else {
|
||||
__release(&target_thread->proc->inner_lock);
|
||||
}
|
||||
next = t->from_parent;
|
||||
|
||||
|
@ -2160,6 +2158,64 @@ static bool binder_validate_fixup(struct binder_buffer *b,
|
|||
return (fixup_offset >= last_min_offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct binder_task_work_cb - for deferred close
|
||||
*
|
||||
* @twork: callback_head for task work
|
||||
* @fd: fd to close
|
||||
*
|
||||
* Structure to pass task work to be handled after
|
||||
* returning from binder_ioctl() via task_work_add().
|
||||
*/
|
||||
struct binder_task_work_cb {
|
||||
struct callback_head twork;
|
||||
struct file *file;
|
||||
};
|
||||
|
||||
/**
|
||||
* binder_do_fd_close() - close list of file descriptors
|
||||
* @twork: callback head for task work
|
||||
*
|
||||
* It is not safe to call ksys_close() during the binder_ioctl()
|
||||
* function if there is a chance that binder's own file descriptor
|
||||
* might be closed. This is to meet the requirements for using
|
||||
* fdget() (see comments for __fget_light()). Therefore use
|
||||
* task_work_add() to schedule the close operation once we have
|
||||
* returned from binder_ioctl(). This function is a callback
|
||||
* for that mechanism and does the actual ksys_close() on the
|
||||
* given file descriptor.
|
||||
*/
|
||||
static void binder_do_fd_close(struct callback_head *twork)
|
||||
{
|
||||
struct binder_task_work_cb *twcb = container_of(twork,
|
||||
struct binder_task_work_cb, twork);
|
||||
|
||||
fput(twcb->file);
|
||||
kfree(twcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_deferred_fd_close() - schedule a close for the given file-descriptor
|
||||
* @fd: file-descriptor to close
|
||||
*
|
||||
* See comments in binder_do_fd_close(). This function is used to schedule
|
||||
* a file-descriptor to be closed after returning from binder_ioctl().
|
||||
*/
|
||||
static void binder_deferred_fd_close(int fd)
|
||||
{
|
||||
struct binder_task_work_cb *twcb;
|
||||
|
||||
twcb = kzalloc(sizeof(*twcb), GFP_KERNEL);
|
||||
if (!twcb)
|
||||
return;
|
||||
init_task_work(&twcb->twork, binder_do_fd_close);
|
||||
__close_fd_get_file(fd, &twcb->file);
|
||||
if (twcb->file)
|
||||
task_work_add(current, &twcb->twork, true);
|
||||
else
|
||||
kfree(twcb);
|
||||
}
|
||||
|
||||
static void binder_transaction_buffer_release(struct binder_proc *proc,
|
||||
struct binder_buffer *buffer,
|
||||
binder_size_t *failed_at)
|
||||
|
@ -2299,7 +2355,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
}
|
||||
fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset);
|
||||
for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
|
||||
ksys_close(fd_array[fd_index]);
|
||||
binder_deferred_fd_close(fd_array[fd_index]);
|
||||
} break;
|
||||
default:
|
||||
pr_err("transaction release %d bad object type %x\n",
|
||||
|
@ -2394,11 +2450,15 @@ static int binder_translate_handle(struct flat_binder_object *fp,
|
|||
fp->cookie = node->cookie;
|
||||
if (node->proc)
|
||||
binder_inner_proc_lock(node->proc);
|
||||
else
|
||||
__acquire(&node->proc->inner_lock);
|
||||
binder_inc_node_nilocked(node,
|
||||
fp->hdr.type == BINDER_TYPE_BINDER,
|
||||
0, NULL);
|
||||
if (node->proc)
|
||||
binder_inner_proc_unlock(node->proc);
|
||||
else
|
||||
__release(&node->proc->inner_lock);
|
||||
trace_binder_transaction_ref_to_node(t, node, &src_rdata);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" ref %d desc %d -> node %d u%016llx\n",
|
||||
|
@ -2762,6 +2822,8 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
binder_set_nice(in_reply_to->saved_priority);
|
||||
target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
|
||||
if (target_thread == NULL) {
|
||||
/* annotation for sparse */
|
||||
__release(&target_thread->proc->inner_lock);
|
||||
return_error = BR_DEAD_REPLY;
|
||||
return_error_line = __LINE__;
|
||||
goto err_dead_binder;
|
||||
|
@ -3912,7 +3974,7 @@ static int binder_apply_fd_fixups(struct binder_transaction *t)
|
|||
} else if (ret) {
|
||||
u32 *fdp = (u32 *)(t->buffer->data + fixup->offset);
|
||||
|
||||
ksys_close(*fdp);
|
||||
binder_deferred_fd_close(*fdp);
|
||||
}
|
||||
list_del(&fixup->fixup_entry);
|
||||
kfree(fixup);
|
||||
|
@ -4164,6 +4226,11 @@ retry:
|
|||
if (cmd == BR_DEAD_BINDER)
|
||||
goto done; /* DEAD_BINDER notifications can cause transactions */
|
||||
} break;
|
||||
default:
|
||||
binder_inner_proc_unlock(proc);
|
||||
pr_err("%d:%d: bad work type %d\n",
|
||||
proc->pid, thread->pid, w->type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!t)
|
||||
|
@ -4467,6 +4534,8 @@ static int binder_thread_release(struct binder_proc *proc,
|
|||
spin_lock(&t->lock);
|
||||
if (t->to_thread == thread)
|
||||
send_reply = t;
|
||||
} else {
|
||||
__acquire(&t->lock);
|
||||
}
|
||||
thread->is_dead = true;
|
||||
|
||||
|
@ -4495,7 +4564,11 @@ static int binder_thread_release(struct binder_proc *proc,
|
|||
spin_unlock(&last_t->lock);
|
||||
if (t)
|
||||
spin_lock(&t->lock);
|
||||
else
|
||||
__acquire(&t->lock);
|
||||
}
|
||||
/* annotation for sparse, lock not acquired in last iteration above */
|
||||
__release(&t->lock);
|
||||
|
||||
/*
|
||||
* If this thread used poll, make sure we remove the waitqueue
|
||||
|
@ -4938,8 +5011,12 @@ static int binder_open(struct inode *nodp, struct file *filp)
|
|||
proc->tsk = current->group_leader;
|
||||
INIT_LIST_HEAD(&proc->todo);
|
||||
proc->default_priority = task_nice(current);
|
||||
binder_dev = container_of(filp->private_data, struct binder_device,
|
||||
miscdev);
|
||||
/* binderfs stashes devices in i_private */
|
||||
if (is_binderfs_device(nodp))
|
||||
binder_dev = nodp->i_private;
|
||||
else
|
||||
binder_dev = container_of(filp->private_data,
|
||||
struct binder_device, miscdev);
|
||||
proc->context = &binder_dev->context;
|
||||
binder_alloc_init(&proc->alloc);
|
||||
|
||||
|
@ -4967,7 +5044,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
|
|||
proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
|
||||
binder_debugfs_dir_entry_proc,
|
||||
(void *)(unsigned long)proc->pid,
|
||||
&binder_proc_fops);
|
||||
&proc_fops);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -5391,6 +5468,9 @@ static void print_binder_proc(struct seq_file *m,
|
|||
for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
|
||||
struct binder_node *node = rb_entry(n, struct binder_node,
|
||||
rb_node);
|
||||
if (!print_all && !node->has_async_transaction)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* take a temporary reference on the node so it
|
||||
* survives and isn't removed from the tree
|
||||
|
@ -5595,7 +5675,7 @@ static void print_binder_proc_stats(struct seq_file *m,
|
|||
}
|
||||
|
||||
|
||||
static int binder_state_show(struct seq_file *m, void *unused)
|
||||
static int state_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct binder_proc *proc;
|
||||
struct binder_node *node;
|
||||
|
@ -5634,7 +5714,7 @@ static int binder_state_show(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int binder_stats_show(struct seq_file *m, void *unused)
|
||||
static int stats_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct binder_proc *proc;
|
||||
|
||||
|
@ -5650,7 +5730,7 @@ static int binder_stats_show(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int binder_transactions_show(struct seq_file *m, void *unused)
|
||||
static int transactions_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct binder_proc *proc;
|
||||
|
||||
|
@ -5663,7 +5743,7 @@ static int binder_transactions_show(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int binder_proc_show(struct seq_file *m, void *unused)
|
||||
static int proc_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct binder_proc *itr;
|
||||
int pid = (unsigned long)m->private;
|
||||
|
@ -5706,7 +5786,7 @@ static void print_binder_transaction_log_entry(struct seq_file *m,
|
|||
"\n" : " (incomplete)\n");
|
||||
}
|
||||
|
||||
static int binder_transaction_log_show(struct seq_file *m, void *unused)
|
||||
static int transaction_log_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct binder_transaction_log *log = m->private;
|
||||
unsigned int log_cur = atomic_read(&log->cur);
|
||||
|
@ -5727,7 +5807,7 @@ static int binder_transaction_log_show(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations binder_fops = {
|
||||
const struct file_operations binder_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.poll = binder_poll,
|
||||
.unlocked_ioctl = binder_ioctl,
|
||||
|
@ -5738,10 +5818,10 @@ static const struct file_operations binder_fops = {
|
|||
.release = binder_release,
|
||||
};
|
||||
|
||||
BINDER_DEBUG_ENTRY(state);
|
||||
BINDER_DEBUG_ENTRY(stats);
|
||||
BINDER_DEBUG_ENTRY(transactions);
|
||||
BINDER_DEBUG_ENTRY(transaction_log);
|
||||
DEFINE_SHOW_ATTRIBUTE(state);
|
||||
DEFINE_SHOW_ATTRIBUTE(stats);
|
||||
DEFINE_SHOW_ATTRIBUTE(transactions);
|
||||
DEFINE_SHOW_ATTRIBUTE(transaction_log);
|
||||
|
||||
static int __init init_binder_device(const char *name)
|
||||
{
|
||||
|
@ -5795,27 +5875,27 @@ static int __init binder_init(void)
|
|||
0444,
|
||||
binder_debugfs_dir_entry_root,
|
||||
NULL,
|
||||
&binder_state_fops);
|
||||
&state_fops);
|
||||
debugfs_create_file("stats",
|
||||
0444,
|
||||
binder_debugfs_dir_entry_root,
|
||||
NULL,
|
||||
&binder_stats_fops);
|
||||
&stats_fops);
|
||||
debugfs_create_file("transactions",
|
||||
0444,
|
||||
binder_debugfs_dir_entry_root,
|
||||
NULL,
|
||||
&binder_transactions_fops);
|
||||
&transactions_fops);
|
||||
debugfs_create_file("transaction_log",
|
||||
0444,
|
||||
binder_debugfs_dir_entry_root,
|
||||
&binder_transaction_log,
|
||||
&binder_transaction_log_fops);
|
||||
&transaction_log_fops);
|
||||
debugfs_create_file("failed_transaction_log",
|
||||
0444,
|
||||
binder_debugfs_dir_entry_root,
|
||||
&binder_transaction_log_failed,
|
||||
&binder_transaction_log_fops);
|
||||
&transaction_log_fops);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -939,6 +939,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
|
|||
struct list_lru_one *lru,
|
||||
spinlock_t *lock,
|
||||
void *cb_arg)
|
||||
__must_hold(lock)
|
||||
{
|
||||
struct mm_struct *mm = NULL;
|
||||
struct binder_lru_page *page = container_of(item,
|
||||
|
|
|
@ -30,16 +30,16 @@ struct binder_transaction;
|
|||
* struct binder_buffer - buffer used for binder transactions
|
||||
* @entry: entry alloc->buffers
|
||||
* @rb_node: node for allocated_buffers/free_buffers rb trees
|
||||
* @free: true if buffer is free
|
||||
* @allow_user_free: describe the second member of struct blah,
|
||||
* @async_transaction: describe the second member of struct blah,
|
||||
* @debug_id: describe the second member of struct blah,
|
||||
* @transaction: describe the second member of struct blah,
|
||||
* @target_node: describe the second member of struct blah,
|
||||
* @data_size: describe the second member of struct blah,
|
||||
* @offsets_size: describe the second member of struct blah,
|
||||
* @extra_buffers_size: describe the second member of struct blah,
|
||||
* @data:i describe the second member of struct blah,
|
||||
* @free: %true if buffer is free
|
||||
* @allow_user_free: %true if user is allowed to free buffer
|
||||
* @async_transaction: %true if buffer is in use for an async txn
|
||||
* @debug_id: unique ID for debugging
|
||||
* @transaction: pointer to associated struct binder_transaction
|
||||
* @target_node: struct binder_node associated with this buffer
|
||||
* @data_size: size of @transaction data
|
||||
* @offsets_size: size of array of offsets
|
||||
* @extra_buffers_size: size of space for other objects (like sg lists)
|
||||
* @data: pointer to base of buffer space
|
||||
*
|
||||
* Bookkeeping structure for binder transaction buffers
|
||||
*/
|
||||
|
|
49
drivers/android/binder_internal.h
Normal file
49
drivers/android/binder_internal.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _LINUX_BINDER_INTERNAL_H
|
||||
#define _LINUX_BINDER_INTERNAL_H
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uidgid.h>
|
||||
|
||||
struct binder_context {
|
||||
struct binder_node *binder_context_mgr_node;
|
||||
struct mutex context_mgr_node_lock;
|
||||
kuid_t binder_context_mgr_uid;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct binder_device - information about a binder device node
|
||||
* @hlist: list of binder devices (only used for devices requested via
|
||||
* CONFIG_ANDROID_BINDER_DEVICES)
|
||||
* @miscdev: information about a binder character device node
|
||||
* @context: binder context information
|
||||
* @binderfs_inode: This is the inode of the root dentry of the super block
|
||||
* belonging to a binderfs mount.
|
||||
*/
|
||||
struct binder_device {
|
||||
struct hlist_node hlist;
|
||||
struct miscdevice miscdev;
|
||||
struct binder_context context;
|
||||
struct inode *binderfs_inode;
|
||||
};
|
||||
|
||||
extern const struct file_operations binder_fops;
|
||||
|
||||
#ifdef CONFIG_ANDROID_BINDERFS
|
||||
extern bool is_binderfs_device(const struct inode *inode);
|
||||
#else
|
||||
static inline bool is_binderfs_device(const struct inode *inode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_BINDER_INTERNAL_H */
|
544
drivers/android/binderfs.c
Normal file
544
drivers/android/binderfs.c
Normal file
|
@ -0,0 +1,544 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#include <linux/compiler_types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ipc_namespace.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/radix-tree.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/xarray.h>
|
||||
#include <uapi/asm-generic/errno-base.h>
|
||||
#include <uapi/linux/android/binder.h>
|
||||
#include <uapi/linux/android/binder_ctl.h>
|
||||
|
||||
#include "binder_internal.h"
|
||||
|
||||
#define FIRST_INODE 1
|
||||
#define SECOND_INODE 2
|
||||
#define INODE_OFFSET 3
|
||||
#define INTSTRLEN 21
|
||||
#define BINDERFS_MAX_MINOR (1U << MINORBITS)
|
||||
|
||||
static struct vfsmount *binderfs_mnt;
|
||||
|
||||
static dev_t binderfs_dev;
|
||||
static DEFINE_MUTEX(binderfs_minors_mutex);
|
||||
static DEFINE_IDA(binderfs_minors);
|
||||
|
||||
/**
|
||||
* binderfs_info - information about a binderfs mount
|
||||
* @ipc_ns: The ipc namespace the binderfs mount belongs to.
|
||||
* @control_dentry: This records the dentry of this binderfs mount
|
||||
* binder-control device.
|
||||
* @root_uid: uid that needs to be used when a new binder device is
|
||||
* created.
|
||||
* @root_gid: gid that needs to be used when a new binder device is
|
||||
* created.
|
||||
*/
|
||||
struct binderfs_info {
|
||||
struct ipc_namespace *ipc_ns;
|
||||
struct dentry *control_dentry;
|
||||
kuid_t root_uid;
|
||||
kgid_t root_gid;
|
||||
|
||||
};
|
||||
|
||||
static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
|
||||
{
|
||||
return inode->i_sb->s_fs_info;
|
||||
}
|
||||
|
||||
bool is_binderfs_device(const struct inode *inode)
|
||||
{
|
||||
if (inode->i_sb->s_magic == BINDERFS_SUPER_MAGIC)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* binderfs_binder_device_create - allocate inode from super block of a
|
||||
* binderfs mount
|
||||
* @ref_inode: inode from wich the super block will be taken
|
||||
* @userp: buffer to copy information about new device for userspace to
|
||||
* @req: struct binderfs_device as copied from userspace
|
||||
*
|
||||
* This function allocated a new binder_device and reserves a new minor
|
||||
* number for it.
|
||||
* Minor numbers are limited and tracked globally in binderfs_minors. The
|
||||
* function will stash a struct binder_device for the specific binder
|
||||
* device in i_private of the inode.
|
||||
* It will go on to allocate a new inode from the super block of the
|
||||
* filesystem mount, stash a struct binder_device in its i_private field
|
||||
* and attach a dentry to that inode.
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
*/
|
||||
static int binderfs_binder_device_create(struct inode *ref_inode,
|
||||
struct binderfs_device __user *userp,
|
||||
struct binderfs_device *req)
|
||||
{
|
||||
int minor, ret;
|
||||
struct dentry *dentry, *dup, *root;
|
||||
struct binder_device *device;
|
||||
size_t name_len = BINDERFS_MAX_NAME + 1;
|
||||
char *name = NULL;
|
||||
struct inode *inode = NULL;
|
||||
struct super_block *sb = ref_inode->i_sb;
|
||||
struct binderfs_info *info = sb->s_fs_info;
|
||||
|
||||
/* Reserve new minor number for the new device. */
|
||||
mutex_lock(&binderfs_minors_mutex);
|
||||
minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL);
|
||||
mutex_unlock(&binderfs_minors_mutex);
|
||||
if (minor < 0)
|
||||
return minor;
|
||||
|
||||
ret = -ENOMEM;
|
||||
device = kzalloc(sizeof(*device), GFP_KERNEL);
|
||||
if (!device)
|
||||
goto err;
|
||||
|
||||
inode = new_inode(sb);
|
||||
if (!inode)
|
||||
goto err;
|
||||
|
||||
inode->i_ino = minor + INODE_OFFSET;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
init_special_inode(inode, S_IFCHR | 0600,
|
||||
MKDEV(MAJOR(binderfs_dev), minor));
|
||||
inode->i_fop = &binder_fops;
|
||||
inode->i_uid = info->root_uid;
|
||||
inode->i_gid = info->root_gid;
|
||||
|
||||
name = kmalloc(name_len, GFP_KERNEL);
|
||||
if (!name)
|
||||
goto err;
|
||||
|
||||
strscpy(name, req->name, name_len);
|
||||
|
||||
device->binderfs_inode = inode;
|
||||
device->context.binder_context_mgr_uid = INVALID_UID;
|
||||
device->context.name = name;
|
||||
device->miscdev.name = name;
|
||||
device->miscdev.minor = minor;
|
||||
mutex_init(&device->context.context_mgr_node_lock);
|
||||
|
||||
req->major = MAJOR(binderfs_dev);
|
||||
req->minor = minor;
|
||||
|
||||
ret = copy_to_user(userp, req, sizeof(*req));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
root = sb->s_root;
|
||||
inode_lock(d_inode(root));
|
||||
dentry = d_alloc_name(root, name);
|
||||
if (!dentry) {
|
||||
inode_unlock(d_inode(root));
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Verify that the name userspace gave us is not already in use. */
|
||||
dup = d_lookup(root, &dentry->d_name);
|
||||
if (dup) {
|
||||
if (d_really_is_positive(dup)) {
|
||||
dput(dup);
|
||||
dput(dentry);
|
||||
inode_unlock(d_inode(root));
|
||||
ret = -EEXIST;
|
||||
goto err;
|
||||
}
|
||||
dput(dup);
|
||||
}
|
||||
|
||||
inode->i_private = device;
|
||||
d_add(dentry, inode);
|
||||
fsnotify_create(root->d_inode, dentry);
|
||||
inode_unlock(d_inode(root));
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(name);
|
||||
kfree(device);
|
||||
mutex_lock(&binderfs_minors_mutex);
|
||||
ida_free(&binderfs_minors, minor);
|
||||
mutex_unlock(&binderfs_minors_mutex);
|
||||
iput(inode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* binderfs_ctl_ioctl - handle binder device node allocation requests
|
||||
*
|
||||
* The request handler for the binder-control device. All requests operate on
|
||||
* the binderfs mount the binder-control device resides in:
|
||||
* - BINDER_CTL_ADD
|
||||
* Allocate a new binder device.
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
*/
|
||||
static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct binderfs_device __user *device = (struct binderfs_device __user *)arg;
|
||||
struct binderfs_device device_req;
|
||||
|
||||
switch (cmd) {
|
||||
case BINDER_CTL_ADD:
|
||||
ret = copy_from_user(&device_req, device, sizeof(device_req));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = binderfs_binder_device_create(inode, device, &device_req);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void binderfs_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct binder_device *device = inode->i_private;
|
||||
|
||||
clear_inode(inode);
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
mutex_lock(&binderfs_minors_mutex);
|
||||
ida_free(&binderfs_minors, device->miscdev.minor);
|
||||
mutex_unlock(&binderfs_minors_mutex);
|
||||
|
||||
kfree(device->context.name);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static const struct super_operations binderfs_super_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.evict_inode = binderfs_evict_inode,
|
||||
};
|
||||
|
||||
static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct inode *inode = d_inode(old_dentry);
|
||||
|
||||
/* binderfs doesn't support directories. */
|
||||
if (d_is_dir(old_dentry))
|
||||
return -EPERM;
|
||||
|
||||
if (flags & ~RENAME_NOREPLACE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!simple_empty(new_dentry))
|
||||
return -ENOTEMPTY;
|
||||
|
||||
if (d_really_is_positive(new_dentry))
|
||||
simple_unlink(new_dir, new_dentry);
|
||||
|
||||
old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime =
|
||||
new_dir->i_mtime = inode->i_ctime = current_time(old_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int binderfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
/*
|
||||
* The control dentry is only ever touched during mount so checking it
|
||||
* here should not require us to take lock.
|
||||
*/
|
||||
if (BINDERFS_I(dir)->control_dentry == dentry)
|
||||
return -EPERM;
|
||||
|
||||
return simple_unlink(dir, dentry);
|
||||
}
|
||||
|
||||
static const struct file_operations binder_ctl_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = nonseekable_open,
|
||||
.unlocked_ioctl = binder_ctl_ioctl,
|
||||
.compat_ioctl = binder_ctl_ioctl,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* binderfs_binder_ctl_create - create a new binder-control device
|
||||
* @sb: super block of the binderfs mount
|
||||
*
|
||||
* This function creates a new binder-control device node in the binderfs mount
|
||||
* referred to by @sb.
|
||||
*
|
||||
* Return: 0 on success, negative errno on failure
|
||||
*/
|
||||
static int binderfs_binder_ctl_create(struct super_block *sb)
|
||||
{
|
||||
int minor, ret;
|
||||
struct dentry *dentry;
|
||||
struct binder_device *device;
|
||||
struct inode *inode = NULL;
|
||||
struct dentry *root = sb->s_root;
|
||||
struct binderfs_info *info = sb->s_fs_info;
|
||||
|
||||
device = kzalloc(sizeof(*device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
inode_lock(d_inode(root));
|
||||
|
||||
/* If we have already created a binder-control node, return. */
|
||||
if (info->control_dentry) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
inode = new_inode(sb);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
/* Reserve a new minor number for the new device. */
|
||||
mutex_lock(&binderfs_minors_mutex);
|
||||
minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL);
|
||||
mutex_unlock(&binderfs_minors_mutex);
|
||||
if (minor < 0) {
|
||||
ret = minor;
|
||||
goto out;
|
||||
}
|
||||
|
||||
inode->i_ino = SECOND_INODE;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
init_special_inode(inode, S_IFCHR | 0600,
|
||||
MKDEV(MAJOR(binderfs_dev), minor));
|
||||
inode->i_fop = &binder_ctl_fops;
|
||||
inode->i_uid = info->root_uid;
|
||||
inode->i_gid = info->root_gid;
|
||||
|
||||
device->binderfs_inode = inode;
|
||||
device->miscdev.minor = minor;
|
||||
|
||||
dentry = d_alloc_name(root, "binder-control");
|
||||
if (!dentry)
|
||||
goto out;
|
||||
|
||||
inode->i_private = device;
|
||||
info->control_dentry = dentry;
|
||||
d_add(dentry, inode);
|
||||
inode_unlock(d_inode(root));
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
inode_unlock(d_inode(root));
|
||||
kfree(device);
|
||||
iput(inode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct inode_operations binderfs_dir_inode_operations = {
|
||||
.lookup = simple_lookup,
|
||||
.rename = binderfs_rename,
|
||||
.unlink = binderfs_unlink,
|
||||
};
|
||||
|
||||
static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct binderfs_info *info;
|
||||
int ret = -ENOMEM;
|
||||
struct inode *inode = NULL;
|
||||
struct ipc_namespace *ipc_ns = sb->s_fs_info;
|
||||
|
||||
get_ipc_ns(ipc_ns);
|
||||
|
||||
sb->s_blocksize = PAGE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_SHIFT;
|
||||
|
||||
/*
|
||||
* The binderfs filesystem can be mounted by userns root in a
|
||||
* non-initial userns. By default such mounts have the SB_I_NODEV flag
|
||||
* set in s_iflags to prevent security issues where userns root can
|
||||
* just create random device nodes via mknod() since it owns the
|
||||
* filesystem mount. But binderfs does not allow to create any files
|
||||
* including devices nodes. The only way to create binder devices nodes
|
||||
* is through the binder-control device which userns root is explicitly
|
||||
* allowed to do. So removing the SB_I_NODEV flag from s_iflags is both
|
||||
* necessary and safe.
|
||||
*/
|
||||
sb->s_iflags &= ~SB_I_NODEV;
|
||||
sb->s_iflags |= SB_I_NOEXEC;
|
||||
sb->s_magic = BINDERFS_SUPER_MAGIC;
|
||||
sb->s_op = &binderfs_super_ops;
|
||||
sb->s_time_gran = 1;
|
||||
|
||||
info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto err_without_dentry;
|
||||
|
||||
info->ipc_ns = ipc_ns;
|
||||
info->root_gid = make_kgid(sb->s_user_ns, 0);
|
||||
if (!gid_valid(info->root_gid))
|
||||
info->root_gid = GLOBAL_ROOT_GID;
|
||||
info->root_uid = make_kuid(sb->s_user_ns, 0);
|
||||
if (!uid_valid(info->root_uid))
|
||||
info->root_uid = GLOBAL_ROOT_UID;
|
||||
|
||||
sb->s_fs_info = info;
|
||||
|
||||
inode = new_inode(sb);
|
||||
if (!inode)
|
||||
goto err_without_dentry;
|
||||
|
||||
inode->i_ino = FIRST_INODE;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_mode = S_IFDIR | 0755;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
inode->i_op = &binderfs_dir_inode_operations;
|
||||
set_nlink(inode, 2);
|
||||
|
||||
sb->s_root = d_make_root(inode);
|
||||
if (!sb->s_root)
|
||||
goto err_without_dentry;
|
||||
|
||||
ret = binderfs_binder_ctl_create(sb);
|
||||
if (ret)
|
||||
goto err_with_dentry;
|
||||
|
||||
return 0;
|
||||
|
||||
err_with_dentry:
|
||||
dput(sb->s_root);
|
||||
sb->s_root = NULL;
|
||||
|
||||
err_without_dentry:
|
||||
put_ipc_ns(ipc_ns);
|
||||
iput(inode);
|
||||
kfree(info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int binderfs_test_super(struct super_block *sb, void *data)
|
||||
{
|
||||
struct binderfs_info *info = sb->s_fs_info;
|
||||
|
||||
if (info)
|
||||
return info->ipc_ns == data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int binderfs_set_super(struct super_block *sb, void *data)
|
||||
{
|
||||
sb->s_fs_info = data;
|
||||
return set_anon_super(sb, NULL);
|
||||
}
|
||||
|
||||
static struct dentry *binderfs_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data)
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
|
||||
|
||||
if (!ns_capable(ipc_ns->user_ns, CAP_SYS_ADMIN))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
sb = sget_userns(fs_type, binderfs_test_super, binderfs_set_super,
|
||||
flags, ipc_ns->user_ns, ipc_ns);
|
||||
if (IS_ERR(sb))
|
||||
return ERR_CAST(sb);
|
||||
|
||||
if (!sb->s_root) {
|
||||
int ret = binderfs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
|
||||
if (ret) {
|
||||
deactivate_locked_super(sb);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
sb->s_flags |= SB_ACTIVE;
|
||||
}
|
||||
|
||||
return dget(sb->s_root);
|
||||
}
|
||||
|
||||
static void binderfs_kill_super(struct super_block *sb)
|
||||
{
|
||||
struct binderfs_info *info = sb->s_fs_info;
|
||||
|
||||
if (info && info->ipc_ns)
|
||||
put_ipc_ns(info->ipc_ns);
|
||||
|
||||
kfree(info);
|
||||
kill_litter_super(sb);
|
||||
}
|
||||
|
||||
static struct file_system_type binder_fs_type = {
|
||||
.name = "binder",
|
||||
.mount = binderfs_mount,
|
||||
.kill_sb = binderfs_kill_super,
|
||||
.fs_flags = FS_USERNS_MOUNT,
|
||||
};
|
||||
|
||||
static int __init init_binderfs(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Allocate new major number for binderfs. */
|
||||
ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR,
|
||||
"binder");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = register_filesystem(&binder_fs_type);
|
||||
if (ret) {
|
||||
unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
binderfs_mnt = kern_mount(&binder_fs_type);
|
||||
if (IS_ERR(binderfs_mnt)) {
|
||||
ret = PTR_ERR(binderfs_mnt);
|
||||
binderfs_mnt = NULL;
|
||||
unregister_filesystem(&binder_fs_type);
|
||||
unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_initcall(init_binderfs);
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
|
||||
#include "fsl-mc-private.h"
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
#include <linux/fsl/mc.h>
|
||||
|
||||
#include "fsl-mc-private.h"
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bitops.h>
|
||||
|
|
|
@ -141,6 +141,7 @@
|
|||
|
||||
static DEFINE_MUTEX(lp_mutex);
|
||||
static struct lp_struct lp_table[LP_NO];
|
||||
static int port_num[LP_NO];
|
||||
|
||||
static unsigned int lp_count = 0;
|
||||
static struct class *lp_class;
|
||||
|
@ -184,7 +185,7 @@ static int lp_preempt(void *handle)
|
|||
{
|
||||
struct lp_struct *this_lp = (struct lp_struct *)handle;
|
||||
set_bit(LP_PREEMPT_REQUEST, &this_lp->bits);
|
||||
return (1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -199,7 +200,7 @@ static int lp_negotiate(struct parport * port, int mode)
|
|||
parport_negotiate(port, mode);
|
||||
}
|
||||
|
||||
return (mode);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int lp_reset(int minor)
|
||||
|
@ -223,12 +224,15 @@ static void lp_error (int minor)
|
|||
return;
|
||||
|
||||
polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
|
||||
if (polling) lp_release_parport (&lp_table[minor]);
|
||||
if (polling)
|
||||
lp_release_parport(&lp_table[minor]);
|
||||
prepare_to_wait(&lp_table[minor].waitq, &wait, TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(LP_TIMEOUT_POLLED);
|
||||
finish_wait(&lp_table[minor].waitq, &wait);
|
||||
if (polling) lp_claim_parport_or_block (&lp_table[minor]);
|
||||
else parport_yield_blocking (lp_table[minor].dev);
|
||||
if (polling)
|
||||
lp_claim_parport_or_block(&lp_table[minor]);
|
||||
else
|
||||
parport_yield_blocking(lp_table[minor].dev);
|
||||
}
|
||||
|
||||
static int lp_check_status(int minor)
|
||||
|
@ -276,7 +280,7 @@ static int lp_wait_ready(int minor, int nonblock)
|
|||
|
||||
/* If we're not in compatibility mode, we're ready now! */
|
||||
if (lp_table[minor].current_mode != IEEE1284_MODE_COMPAT) {
|
||||
return (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
|
@ -908,9 +912,13 @@ static int __init lp_setup (char *str)
|
|||
|
||||
static int lp_register(int nr, struct parport *port)
|
||||
{
|
||||
lp_table[nr].dev = parport_register_device(port, "lp",
|
||||
lp_preempt, NULL, NULL, 0,
|
||||
(void *) &lp_table[nr]);
|
||||
struct pardev_cb ppdev_cb;
|
||||
|
||||
memset(&ppdev_cb, 0, sizeof(ppdev_cb));
|
||||
ppdev_cb.preempt = lp_preempt;
|
||||
ppdev_cb.private = &lp_table[nr];
|
||||
lp_table[nr].dev = parport_register_dev_model(port, "lp",
|
||||
&ppdev_cb, nr);
|
||||
if (lp_table[nr].dev == NULL)
|
||||
return 1;
|
||||
lp_table[nr].flags |= LP_EXIST;
|
||||
|
@ -935,6 +943,7 @@ static int lp_register(int nr, struct parport *port)
|
|||
CONSOLE_LP, port->name);
|
||||
}
|
||||
#endif
|
||||
port_num[nr] = port->number;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -953,7 +962,11 @@ static void lp_attach (struct parport *port)
|
|||
printk(KERN_INFO "lp: ignoring parallel port (max. %d)\n",LP_NO);
|
||||
return;
|
||||
}
|
||||
if (!lp_register(lp_count, port))
|
||||
for (i = 0; i < LP_NO; i++)
|
||||
if (port_num[i] == -1)
|
||||
break;
|
||||
|
||||
if (!lp_register(i, port))
|
||||
lp_count++;
|
||||
break;
|
||||
|
||||
|
@ -971,6 +984,8 @@ static void lp_attach (struct parport *port)
|
|||
|
||||
static void lp_detach(struct parport *port)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* Write this some day. */
|
||||
#ifdef CONFIG_LP_CONSOLE
|
||||
if (console_registered == port) {
|
||||
|
@ -978,12 +993,22 @@ static void lp_detach (struct parport *port)
|
|||
console_registered = NULL;
|
||||
}
|
||||
#endif /* CONFIG_LP_CONSOLE */
|
||||
|
||||
for (n = 0; n < LP_NO; n++) {
|
||||
if (port_num[n] == port->number) {
|
||||
port_num[n] = -1;
|
||||
lp_count--;
|
||||
device_destroy(lp_class, MKDEV(LP_MAJOR, n));
|
||||
parport_unregister_device(lp_table[n].dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct parport_driver lp_driver = {
|
||||
.name = "lp",
|
||||
.attach = lp_attach,
|
||||
.match_port = lp_attach,
|
||||
.detach = lp_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
|
||||
static int __init lp_init(void)
|
||||
|
@ -1010,6 +1035,7 @@ static int __init lp_init (void)
|
|||
init_waitqueue_head(&lp_table[i].dataq);
|
||||
mutex_init(&lp_table[i].port_mutex);
|
||||
lp_table[i].timeout = 10 * HZ;
|
||||
port_num[i] = -1;
|
||||
}
|
||||
|
||||
if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
|
||||
|
@ -1076,8 +1102,6 @@ static int __init lp_init_module (void)
|
|||
|
||||
static void lp_cleanup_module(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
parport_unregister_driver(&lp_driver);
|
||||
|
||||
#ifdef CONFIG_LP_CONSOLE
|
||||
|
@ -1085,12 +1109,6 @@ static void lp_cleanup_module (void)
|
|||
#endif
|
||||
|
||||
unregister_chrdev(LP_MAJOR, "lp");
|
||||
for (offset = 0; offset < LP_NO; offset++) {
|
||||
if (lp_table[offset].dev == NULL)
|
||||
continue;
|
||||
parport_unregister_device(lp_table[offset].dev);
|
||||
device_destroy(lp_class, MKDEV(LP_MAJOR, offset));
|
||||
}
|
||||
class_destroy(lp_class);
|
||||
}
|
||||
|
||||
|
|
|
@ -866,8 +866,8 @@ static int __init rtc_init(void)
|
|||
#ifdef CONFIG_SPARC32
|
||||
for_each_node_by_name(ebus_dp, "ebus") {
|
||||
struct device_node *dp;
|
||||
for (dp = ebus_dp; dp; dp = dp->sibling) {
|
||||
if (!strcmp(dp->name, "rtc")) {
|
||||
for_each_child_of_node(ebus_dp, dp) {
|
||||
if (of_node_name_eq(dp, "rtc")) {
|
||||
op = of_find_device_by_node(dp);
|
||||
if (op) {
|
||||
rtc_port = op->resource[0].start;
|
||||
|
|
|
@ -525,9 +525,9 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d,
|
|||
SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
SET_PORT_BITS(TLCLK_REG3, 0xc7, val << 3);
|
||||
|
||||
}
|
||||
spin_unlock_irqrestore(&event_lock, flags);
|
||||
|
||||
return strnlen(buf, count);
|
||||
|
@ -567,8 +567,9 @@ static ssize_t store_select_amcb1_transmit_clock(struct device *d,
|
|||
SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
SET_PORT_BITS(TLCLK_REG3, 0xf8, val);
|
||||
}
|
||||
spin_unlock_irqrestore(&event_lock, flags);
|
||||
|
||||
return strnlen(buf, count);
|
||||
|
|
|
@ -1309,7 +1309,7 @@ static const struct attribute_group port_attribute_group = {
|
|||
.attrs = port_sysfs_entries,
|
||||
};
|
||||
|
||||
static int debugfs_show(struct seq_file *s, void *data)
|
||||
static int port_debugfs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct port *port = s->private;
|
||||
|
||||
|
@ -1327,18 +1327,7 @@ static int debugfs_show(struct seq_file *s, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, debugfs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations port_debugfs_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(port_debugfs);
|
||||
|
||||
static void set_console_size(struct port *port, u16 rows, u16 cols)
|
||||
{
|
||||
|
@ -1490,7 +1479,7 @@ static int add_port(struct ports_device *portdev, u32 id)
|
|||
port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
|
||||
pdrvdata.debugfs_dir,
|
||||
port,
|
||||
&port_debugfs_ops);
|
||||
&port_debugfs_fops);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -657,6 +657,8 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
|||
struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max14577_muic_info *info;
|
||||
int delay_jiffies;
|
||||
int cable_type;
|
||||
bool attached;
|
||||
int ret;
|
||||
int i;
|
||||
u8 id;
|
||||
|
@ -725,7 +727,16 @@ static int max14577_muic_probe(struct platform_device *pdev)
|
|||
info->path_uart = CTRL1_SW_UART;
|
||||
delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT);
|
||||
|
||||
/* Set initial path for UART */
|
||||
/* Set initial path for UART when JIG is connected to get serial logs */
|
||||
ret = max14577_bulk_read(info->max14577->regmap,
|
||||
MAX14577_MUIC_REG_STATUS1, info->status, 2);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "Cannot read STATUS registers\n");
|
||||
return ret;
|
||||
}
|
||||
cable_type = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_ADC,
|
||||
&attached);
|
||||
if (attached && cable_type == MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF)
|
||||
max14577_muic_set_path(info, info->path_uart, true);
|
||||
|
||||
/* Check revision number of MUIC device*/
|
||||
|
|
|
@ -1072,6 +1072,8 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
|||
struct max77693_reg_data *init_data;
|
||||
int num_init_data;
|
||||
int delay_jiffies;
|
||||
int cable_type;
|
||||
bool attached;
|
||||
int ret;
|
||||
int i;
|
||||
unsigned int id;
|
||||
|
@ -1212,7 +1214,17 @@ static int max77693_muic_probe(struct platform_device *pdev)
|
|||
delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT);
|
||||
}
|
||||
|
||||
/* Set initial path for UART */
|
||||
/* Set initial path for UART when JIG is connected to get serial logs */
|
||||
ret = regmap_bulk_read(info->max77693->regmap_muic,
|
||||
MAX77693_MUIC_REG_STATUS1, info->status, 2);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to read MUIC register\n");
|
||||
return ret;
|
||||
}
|
||||
cable_type = max77693_muic_get_cable_type(info,
|
||||
MAX77693_CABLE_GROUP_ADC, &attached);
|
||||
if (attached && (cable_type == MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON ||
|
||||
cable_type == MAX77693_MUIC_ADC_FACTORY_MODE_UART_OFF))
|
||||
max77693_muic_set_path(info, info->path_uart, true);
|
||||
|
||||
/* Check revision number of MUIC device*/
|
||||
|
|
|
@ -812,6 +812,8 @@ static int max77843_muic_probe(struct platform_device *pdev)
|
|||
struct max77693_dev *max77843 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77843_muic_info *info;
|
||||
unsigned int id;
|
||||
int cable_type;
|
||||
bool attached;
|
||||
int i, ret;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -856,9 +858,19 @@ static int max77843_muic_probe(struct platform_device *pdev)
|
|||
/* Set ADC debounce time */
|
||||
max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS);
|
||||
|
||||
/* Set initial path for UART */
|
||||
max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_UART, true,
|
||||
false);
|
||||
/* Set initial path for UART when JIG is connected to get serial logs */
|
||||
ret = regmap_bulk_read(max77843->regmap_muic,
|
||||
MAX77843_MUIC_REG_STATUS1, info->status,
|
||||
MAX77843_MUIC_STATUS_NUM);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "Cannot read STATUS registers\n");
|
||||
goto err_muic_irq;
|
||||
}
|
||||
cable_type = max77843_muic_get_cable_type(info, MAX77843_CABLE_GROUP_ADC,
|
||||
&attached);
|
||||
if (attached && cable_type == MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF)
|
||||
max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_UART,
|
||||
true, false);
|
||||
|
||||
/* Check revision number of MUIC device */
|
||||
ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id);
|
||||
|
|
|
@ -311,13 +311,11 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
if (usb_type == MAX8997_USB_HOST) {
|
||||
ret = max8997_muic_set_path(info, info->path_usb, attached);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "failed to update muic register\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
switch (usb_type) {
|
||||
case MAX8997_USB_HOST:
|
||||
|
@ -632,6 +630,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
|
|||
struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev);
|
||||
struct max8997_muic_info *info;
|
||||
int delay_jiffies;
|
||||
int cable_type;
|
||||
bool attached;
|
||||
int ret, i;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info),
|
||||
|
@ -724,7 +724,16 @@ static int max8997_muic_probe(struct platform_device *pdev)
|
|||
delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT);
|
||||
}
|
||||
|
||||
/* Set initial path for UART */
|
||||
/* Set initial path for UART when JIG is connected to get serial logs */
|
||||
ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
|
||||
2, info->status);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to read MUIC register\n");
|
||||
return ret;
|
||||
}
|
||||
cable_type = max8997_muic_get_cable_type(info,
|
||||
MAX8997_CABLE_GROUP_ADC, &attached);
|
||||
if (attached && cable_type == MAX8997_MUIC_ADC_FACTORY_MODE_UART_OFF)
|
||||
max8997_muic_set_path(info, info->path_uart, true);
|
||||
|
||||
/* Set ADC debounce time */
|
||||
|
|
|
@ -216,6 +216,18 @@ config FW_CFG_SYSFS_CMDLINE
|
|||
WARNING: Using incorrect parameters (base address in particular)
|
||||
may crash your system.
|
||||
|
||||
config INTEL_STRATIX10_SERVICE
|
||||
tristate "Intel Stratix10 Service Layer"
|
||||
depends on HAVE_ARM_SMCCC
|
||||
default n
|
||||
help
|
||||
Intel Stratix10 service layer runs at privileged exception level,
|
||||
interfaces with the service providers (FPGA manager is one of them)
|
||||
and manages secure monitor call to communicate with secure monitor
|
||||
software at secure monitor exception level.
|
||||
|
||||
Say Y here if you want Stratix10 service layer support.
|
||||
|
||||
config QCOM_SCM
|
||||
bool
|
||||
depends on ARM || ARM64
|
||||
|
|
|
@ -12,6 +12,7 @@ obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
|
|||
obj-$(CONFIG_EDD) += edd.o
|
||||
obj-$(CONFIG_EFI_PCDP) += pcdp.o
|
||||
obj-$(CONFIG_DMIID) += dmi-id.o
|
||||
obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o
|
||||
obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
|
||||
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
|
||||
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
|
||||
|
|
1041
drivers/firmware/stratix10-svc.c
Normal file
1041
drivers/firmware/stratix10-svc.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -56,6 +56,12 @@ config FPGA_MGR_ZYNQ_FPGA
|
|||
help
|
||||
FPGA manager driver support for Xilinx Zynq FPGAs.
|
||||
|
||||
config FPGA_MGR_STRATIX10_SOC
|
||||
tristate "Intel Stratix10 SoC FPGA Manager"
|
||||
depends on (ARCH_STRATIX10 && INTEL_STRATIX10_SERVICE)
|
||||
help
|
||||
FPGA manager driver support for the Intel Stratix10 SoC.
|
||||
|
||||
config FPGA_MGR_XILINX_SPI
|
||||
tristate "Xilinx Configuration over Slave Serial (SPI)"
|
||||
depends on SPI
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
|
|||
obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o
|
||||
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
|
||||
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
|
||||
obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) += stratix10-soc.o
|
||||
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
|
||||
obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
|
||||
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
|
||||
|
|
|
@ -403,6 +403,7 @@ static int altera_cvp_probe(struct pci_dev *pdev,
|
|||
struct altera_cvp_conf *conf;
|
||||
struct fpga_manager *mgr;
|
||||
u16 cmd, val;
|
||||
u32 regval;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
@ -416,6 +417,14 @@ static int altera_cvp_probe(struct pci_dev *pdev,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
pci_read_config_dword(pdev, VSE_CVP_STATUS, ®val);
|
||||
if (!(regval & VSE_CVP_STATUS_CVP_EN)) {
|
||||
dev_err(&pdev->dev,
|
||||
"CVP is disabled for this device: CVP_STATUS Reg 0x%x\n",
|
||||
regval);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL);
|
||||
if (!conf)
|
||||
return -ENOMEM;
|
||||
|
@ -466,17 +475,10 @@ static int altera_cvp_probe(struct pci_dev *pdev,
|
|||
if (ret)
|
||||
goto err_unmap;
|
||||
|
||||
ret = driver_create_file(&altera_cvp_driver.driver,
|
||||
&driver_attr_chkcfg);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n");
|
||||
fpga_mgr_unregister(mgr);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unmap:
|
||||
if (conf->map)
|
||||
pci_iounmap(pdev, conf->map);
|
||||
pci_release_region(pdev, CVP_BAR);
|
||||
err_disable:
|
||||
|
@ -491,8 +493,8 @@ static void altera_cvp_remove(struct pci_dev *pdev)
|
|||
struct altera_cvp_conf *conf = mgr->priv;
|
||||
u16 cmd;
|
||||
|
||||
driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg);
|
||||
fpga_mgr_unregister(mgr);
|
||||
if (conf->map)
|
||||
pci_iounmap(pdev, conf->map);
|
||||
pci_release_region(pdev, CVP_BAR);
|
||||
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
||||
|
@ -500,7 +502,30 @@ static void altera_cvp_remove(struct pci_dev *pdev)
|
|||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
}
|
||||
|
||||
module_pci_driver(altera_cvp_driver);
|
||||
static int __init altera_cvp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_register_driver(&altera_cvp_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = driver_create_file(&altera_cvp_driver.driver,
|
||||
&driver_attr_chkcfg);
|
||||
if (ret)
|
||||
pr_warn("Can't create sysfs chkcfg file\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit altera_cvp_exit(void)
|
||||
{
|
||||
driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg);
|
||||
pci_unregister_driver(&altera_cvp_driver);
|
||||
}
|
||||
|
||||
module_init(altera_cvp_init);
|
||||
module_exit(altera_cvp_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
|
||||
|
|
|
@ -75,6 +75,12 @@ static struct altera_ps_data a10_data = {
|
|||
.t_st2ck_us = 10, /* min(t_ST2CK) */
|
||||
};
|
||||
|
||||
/* Array index is enum altera_ps_devtype */
|
||||
static const struct altera_ps_data *altera_ps_data_map[] = {
|
||||
&c5_data,
|
||||
&a10_data,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_ef_match[] = {
|
||||
{ .compatible = "altr,fpga-passive-serial", .data = &c5_data },
|
||||
{ .compatible = "altr,fpga-arria10-passive-serial", .data = &a10_data },
|
||||
|
@ -234,6 +240,22 @@ static const struct fpga_manager_ops altera_ps_ops = {
|
|||
.write_complete = altera_ps_write_complete,
|
||||
};
|
||||
|
||||
static const struct altera_ps_data *id_to_data(const struct spi_device_id *id)
|
||||
{
|
||||
kernel_ulong_t devtype = id->driver_data;
|
||||
const struct altera_ps_data *data;
|
||||
|
||||
/* someone added a altera_ps_devtype without adding to the map array */
|
||||
if (devtype >= ARRAY_SIZE(altera_ps_data_map))
|
||||
return NULL;
|
||||
|
||||
data = altera_ps_data_map[devtype];
|
||||
if (!data || data->devtype != devtype)
|
||||
return NULL;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int altera_ps_probe(struct spi_device *spi)
|
||||
{
|
||||
struct altera_ps_conf *conf;
|
||||
|
@ -244,11 +266,17 @@ static int altera_ps_probe(struct spi_device *spi)
|
|||
if (!conf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
of_id = of_match_device(of_ef_match, &spi->dev);
|
||||
if (!of_id)
|
||||
return -ENODEV;
|
||||
|
||||
conf->data = of_id->data;
|
||||
} else {
|
||||
conf->data = id_to_data(spi_get_device_id(spi));
|
||||
if (!conf->data)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
conf->spi = spi;
|
||||
conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(conf->config)) {
|
||||
|
@ -294,7 +322,9 @@ static int altera_ps_remove(struct spi_device *spi)
|
|||
}
|
||||
|
||||
static const struct spi_device_id altera_ps_spi_ids[] = {
|
||||
{"cyclone-ps-spi", 0},
|
||||
{ "cyclone-ps-spi", CYCLONE5 },
|
||||
{ "fpga-passive-serial", CYCLONE5 },
|
||||
{ "fpga-arria10-passive-serial", ARRIA10 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids);
|
||||
|
|
|
@ -444,10 +444,8 @@ static void pr_mgmt_uinit(struct platform_device *pdev,
|
|||
struct dfl_feature *feature)
|
||||
{
|
||||
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct dfl_fme *priv;
|
||||
|
||||
mutex_lock(&pdata->lock);
|
||||
priv = dfl_fpga_pdata_get_private(pdata);
|
||||
|
||||
dfl_fme_destroy_regions(pdata);
|
||||
dfl_fme_destroy_bridges(pdata);
|
||||
|
|
|
@ -64,7 +64,7 @@ eprobe_mgr_put:
|
|||
|
||||
static int fme_region_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fpga_region *region = dev_get_drvdata(&pdev->dev);
|
||||
struct fpga_region *region = platform_get_drvdata(pdev);
|
||||
struct fpga_manager *mgr = region->mgr;
|
||||
|
||||
fpga_region_unregister(region);
|
||||
|
|
|
@ -421,7 +421,7 @@ static int of_fpga_region_probe(struct platform_device *pdev)
|
|||
goto eprobe_mgr_put;
|
||||
|
||||
of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev);
|
||||
dev_set_drvdata(dev, region);
|
||||
platform_set_drvdata(pdev, region);
|
||||
|
||||
dev_info(dev, "FPGA Region probed\n");
|
||||
|
||||
|
|
535
drivers/fpga/stratix10-soc.c
Normal file
535
drivers/fpga/stratix10-soc.c
Normal file
|
@ -0,0 +1,535 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* FPGA Manager Driver for Intel Stratix10 SoC
|
||||
*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*/
|
||||
#include <linux/completion.h>
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/firmware/intel/stratix10-svc-client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
/*
|
||||
* FPGA programming requires a higher level of privilege (EL3), per the SoC
|
||||
* design.
|
||||
*/
|
||||
#define NUM_SVC_BUFS 4
|
||||
#define SVC_BUF_SIZE SZ_512K
|
||||
|
||||
/* Indicates buffer is in use if set */
|
||||
#define SVC_BUF_LOCK 0
|
||||
|
||||
#define S10_BUFFER_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_BUFFER_TIMEOUT_MS))
|
||||
#define S10_RECONFIG_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_REQUEST_TIMEOUT_MS))
|
||||
|
||||
/*
|
||||
* struct s10_svc_buf
|
||||
* buf: virtual address of buf provided by service layer
|
||||
* lock: locked if buffer is in use
|
||||
*/
|
||||
struct s10_svc_buf {
|
||||
char *buf;
|
||||
unsigned long lock;
|
||||
};
|
||||
|
||||
struct s10_priv {
|
||||
struct stratix10_svc_chan *chan;
|
||||
struct stratix10_svc_client client;
|
||||
struct completion status_return_completion;
|
||||
struct s10_svc_buf svc_bufs[NUM_SVC_BUFS];
|
||||
unsigned long status;
|
||||
};
|
||||
|
||||
static int s10_svc_send_msg(struct s10_priv *priv,
|
||||
enum stratix10_svc_command_code command,
|
||||
void *payload, u32 payload_length)
|
||||
{
|
||||
struct stratix10_svc_chan *chan = priv->chan;
|
||||
struct device *dev = priv->client.dev;
|
||||
struct stratix10_svc_client_msg msg;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s cmd=%d payload=%p length=%d\n",
|
||||
__func__, command, payload, payload_length);
|
||||
|
||||
msg.command = command;
|
||||
msg.payload = payload;
|
||||
msg.payload_length = payload_length;
|
||||
|
||||
ret = stratix10_svc_send(chan, &msg);
|
||||
dev_dbg(dev, "stratix10_svc_send returned status %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free buffers allocated from the service layer's pool that are not in use.
|
||||
* Return true when all buffers are freed.
|
||||
*/
|
||||
static bool s10_free_buffers(struct fpga_manager *mgr)
|
||||
{
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
uint num_free = 0;
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < NUM_SVC_BUFS; i++) {
|
||||
if (!priv->svc_bufs[i].buf) {
|
||||
num_free++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!test_and_set_bit_lock(SVC_BUF_LOCK,
|
||||
&priv->svc_bufs[i].lock)) {
|
||||
stratix10_svc_free_memory(priv->chan,
|
||||
priv->svc_bufs[i].buf);
|
||||
priv->svc_bufs[i].buf = NULL;
|
||||
num_free++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_free == NUM_SVC_BUFS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns count of how many buffers are not in use.
|
||||
*/
|
||||
static uint s10_free_buffer_count(struct fpga_manager *mgr)
|
||||
{
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
uint num_free = 0;
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < NUM_SVC_BUFS; i++)
|
||||
if (!priv->svc_bufs[i].buf)
|
||||
num_free++;
|
||||
|
||||
return num_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* s10_unlock_bufs
|
||||
* Given the returned buffer address, match that address to our buffer struct
|
||||
* and unlock that buffer. This marks it as available to be refilled and sent
|
||||
* (or freed).
|
||||
* priv: private data
|
||||
* kaddr: kernel address of buffer that was returned from service layer
|
||||
*/
|
||||
static void s10_unlock_bufs(struct s10_priv *priv, void *kaddr)
|
||||
{
|
||||
uint i;
|
||||
|
||||
if (!kaddr)
|
||||
return;
|
||||
|
||||
for (i = 0; i < NUM_SVC_BUFS; i++)
|
||||
if (priv->svc_bufs[i].buf == kaddr) {
|
||||
clear_bit_unlock(SVC_BUF_LOCK,
|
||||
&priv->svc_bufs[i].lock);
|
||||
return;
|
||||
}
|
||||
|
||||
WARN(1, "Unknown buffer returned from service layer %p\n", kaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* s10_receive_callback - callback for service layer to use to provide client
|
||||
* (this driver) messages received through the mailbox.
|
||||
* client: service layer client struct
|
||||
* data: message from service layer
|
||||
*/
|
||||
static void s10_receive_callback(struct stratix10_svc_client *client,
|
||||
struct stratix10_svc_cb_data *data)
|
||||
{
|
||||
struct s10_priv *priv = client->priv;
|
||||
u32 status;
|
||||
int i;
|
||||
|
||||
WARN_ONCE(!data, "%s: stratix10_svc_rc_data = NULL", __func__);
|
||||
|
||||
status = data->status;
|
||||
|
||||
/*
|
||||
* Here we set status bits as we receive them. Elsewhere, we always use
|
||||
* test_and_clear_bit() to check status in priv->status
|
||||
*/
|
||||
for (i = 0; i <= SVC_STATUS_RECONFIG_ERROR; i++)
|
||||
if (status & (1 << i))
|
||||
set_bit(i, &priv->status);
|
||||
|
||||
if (status & BIT(SVC_STATUS_RECONFIG_BUFFER_DONE)) {
|
||||
s10_unlock_bufs(priv, data->kaddr1);
|
||||
s10_unlock_bufs(priv, data->kaddr2);
|
||||
s10_unlock_bufs(priv, data->kaddr3);
|
||||
}
|
||||
|
||||
complete(&priv->status_return_completion);
|
||||
}
|
||||
|
||||
/*
|
||||
* s10_ops_write_init - prepare for FPGA reconfiguration by requesting
|
||||
* partial reconfig and allocating buffers from the service layer.
|
||||
*/
|
||||
static int s10_ops_write_init(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
struct device *dev = priv->client.dev;
|
||||
struct stratix10_svc_command_config_type ctype;
|
||||
char *kbuf;
|
||||
uint i;
|
||||
int ret;
|
||||
|
||||
ctype.flags = 0;
|
||||
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
|
||||
dev_dbg(dev, "Requesting partial reconfiguration.\n");
|
||||
ctype.flags |= BIT(COMMAND_RECONFIG_FLAG_PARTIAL);
|
||||
} else {
|
||||
dev_dbg(dev, "Requesting full reconfiguration.\n");
|
||||
}
|
||||
|
||||
reinit_completion(&priv->status_return_completion);
|
||||
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
|
||||
&ctype, sizeof(ctype));
|
||||
if (ret < 0)
|
||||
goto init_done;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(
|
||||
&priv->status_return_completion, S10_RECONFIG_TIMEOUT);
|
||||
if (!ret) {
|
||||
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto init_done;
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error (%d) waiting for RECONFIG_REQUEST\n", ret);
|
||||
goto init_done;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (!test_and_clear_bit(SVC_STATUS_RECONFIG_REQUEST_OK,
|
||||
&priv->status)) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto init_done;
|
||||
}
|
||||
|
||||
/* Allocate buffers from the service layer's pool. */
|
||||
for (i = 0; i < NUM_SVC_BUFS; i++) {
|
||||
kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
|
||||
if (!kbuf) {
|
||||
s10_free_buffers(mgr);
|
||||
ret = -ENOMEM;
|
||||
goto init_done;
|
||||
}
|
||||
|
||||
priv->svc_bufs[i].buf = kbuf;
|
||||
priv->svc_bufs[i].lock = 0;
|
||||
}
|
||||
|
||||
init_done:
|
||||
stratix10_svc_done(priv->chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* s10_send_buf - send a buffer to the service layer queue
|
||||
* mgr: fpga manager struct
|
||||
* buf: fpga image buffer
|
||||
* count: size of buf in bytes
|
||||
* Returns # of bytes transferred or -ENOBUFS if the all the buffers are in use
|
||||
* or if the service queue is full. Never returns 0.
|
||||
*/
|
||||
static int s10_send_buf(struct fpga_manager *mgr, const char *buf, size_t count)
|
||||
{
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
struct device *dev = priv->client.dev;
|
||||
void *svc_buf;
|
||||
size_t xfer_sz;
|
||||
int ret;
|
||||
uint i;
|
||||
|
||||
/* get/lock a buffer that that's not being used */
|
||||
for (i = 0; i < NUM_SVC_BUFS; i++)
|
||||
if (!test_and_set_bit_lock(SVC_BUF_LOCK,
|
||||
&priv->svc_bufs[i].lock))
|
||||
break;
|
||||
|
||||
if (i == NUM_SVC_BUFS)
|
||||
return -ENOBUFS;
|
||||
|
||||
xfer_sz = count < SVC_BUF_SIZE ? count : SVC_BUF_SIZE;
|
||||
|
||||
svc_buf = priv->svc_bufs[i].buf;
|
||||
memcpy(svc_buf, buf, xfer_sz);
|
||||
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG_DATA_SUBMIT,
|
||||
svc_buf, xfer_sz);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error while sending data to service layer (%d)", ret);
|
||||
clear_bit_unlock(SVC_BUF_LOCK, &priv->svc_bufs[i].lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return xfer_sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a FPGA image to privileged layers to write to the FPGA. When done
|
||||
* sending, free all service layer buffers we allocated in write_init.
|
||||
*/
|
||||
static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
struct device *dev = priv->client.dev;
|
||||
long wait_status;
|
||||
int sent = 0;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Loop waiting for buffers to be returned. When a buffer is returned,
|
||||
* reuse it to send more data or free if if all data has been sent.
|
||||
*/
|
||||
while (count > 0 || s10_free_buffer_count(mgr) != NUM_SVC_BUFS) {
|
||||
reinit_completion(&priv->status_return_completion);
|
||||
|
||||
if (count > 0) {
|
||||
sent = s10_send_buf(mgr, buf, count);
|
||||
if (sent < 0)
|
||||
continue;
|
||||
|
||||
count -= sent;
|
||||
buf += sent;
|
||||
} else {
|
||||
if (s10_free_buffers(mgr))
|
||||
return 0;
|
||||
|
||||
ret = s10_svc_send_msg(
|
||||
priv, COMMAND_RECONFIG_DATA_CLAIM,
|
||||
NULL, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If callback hasn't already happened, wait for buffers to be
|
||||
* returned from service layer
|
||||
*/
|
||||
wait_status = 1; /* not timed out */
|
||||
if (!priv->status)
|
||||
wait_status = wait_for_completion_interruptible_timeout(
|
||||
&priv->status_return_completion,
|
||||
S10_BUFFER_TIMEOUT);
|
||||
|
||||
if (test_and_clear_bit(SVC_STATUS_RECONFIG_BUFFER_DONE,
|
||||
&priv->status) ||
|
||||
test_and_clear_bit(SVC_STATUS_RECONFIG_BUFFER_SUBMITTED,
|
||||
&priv->status)) {
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(SVC_STATUS_RECONFIG_ERROR,
|
||||
&priv->status)) {
|
||||
dev_err(dev, "ERROR - giving up - SVC_STATUS_RECONFIG_ERROR\n");
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!wait_status) {
|
||||
dev_err(dev, "timeout waiting for svc layer buffers\n");
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
if (wait_status < 0) {
|
||||
ret = wait_status;
|
||||
dev_err(dev,
|
||||
"error (%d) waiting for svc layer buffers\n",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s10_free_buffers(mgr))
|
||||
dev_err(dev, "%s not all buffers were freed\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s10_ops_write_complete(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
struct device *dev = priv->client.dev;
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
timeout = usecs_to_jiffies(info->config_complete_timeout_us);
|
||||
|
||||
do {
|
||||
reinit_completion(&priv->status_return_completion);
|
||||
|
||||
ret = s10_svc_send_msg(priv, COMMAND_RECONFIG_STATUS, NULL, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(
|
||||
&priv->status_return_completion, timeout);
|
||||
if (!ret) {
|
||||
dev_err(dev,
|
||||
"timeout waiting for RECONFIG_COMPLETED\n");
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"error (%d) waiting for RECONFIG_COMPLETED\n",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
/* Not error or timeout, so ret is # of jiffies until timeout */
|
||||
timeout = ret;
|
||||
ret = 0;
|
||||
|
||||
if (test_and_clear_bit(SVC_STATUS_RECONFIG_COMPLETED,
|
||||
&priv->status))
|
||||
break;
|
||||
|
||||
if (test_and_clear_bit(SVC_STATUS_RECONFIG_ERROR,
|
||||
&priv->status)) {
|
||||
dev_err(dev, "ERROR - giving up - SVC_STATUS_RECONFIG_ERROR\n");
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
stratix10_svc_done(priv->chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum fpga_mgr_states s10_ops_state(struct fpga_manager *mgr)
|
||||
{
|
||||
return FPGA_MGR_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
static const struct fpga_manager_ops s10_ops = {
|
||||
.state = s10_ops_state,
|
||||
.write_init = s10_ops_write_init,
|
||||
.write = s10_ops_write,
|
||||
.write_complete = s10_ops_write_complete,
|
||||
};
|
||||
|
||||
static int s10_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct s10_priv *priv;
|
||||
struct fpga_manager *mgr;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->client.dev = dev;
|
||||
priv->client.receive_cb = s10_receive_callback;
|
||||
priv->client.priv = priv;
|
||||
|
||||
priv->chan = stratix10_svc_request_channel_byname(&priv->client,
|
||||
SVC_CLIENT_FPGA);
|
||||
if (IS_ERR(priv->chan)) {
|
||||
dev_err(dev, "couldn't get service channel (%s)\n",
|
||||
SVC_CLIENT_FPGA);
|
||||
return PTR_ERR(priv->chan);
|
||||
}
|
||||
|
||||
init_completion(&priv->status_return_completion);
|
||||
|
||||
mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
|
||||
&s10_ops, priv);
|
||||
if (!mgr) {
|
||||
dev_err(dev, "unable to create FPGA manager\n");
|
||||
ret = -ENOMEM;
|
||||
goto probe_err;
|
||||
}
|
||||
|
||||
ret = fpga_mgr_register(mgr);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to register FPGA manager\n");
|
||||
fpga_mgr_free(mgr);
|
||||
goto probe_err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, mgr);
|
||||
return ret;
|
||||
|
||||
probe_err:
|
||||
stratix10_svc_free_channel(priv->chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s10_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fpga_manager *mgr = platform_get_drvdata(pdev);
|
||||
struct s10_priv *priv = mgr->priv;
|
||||
|
||||
fpga_mgr_unregister(mgr);
|
||||
stratix10_svc_free_channel(priv->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id s10_of_match[] = {
|
||||
{ .compatible = "intel,stratix10-soc-fpga-mgr", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, s10_of_match);
|
||||
|
||||
static struct platform_driver s10_driver = {
|
||||
.probe = s10_probe,
|
||||
.remove = s10_remove,
|
||||
.driver = {
|
||||
.name = "Stratix10 SoC FPGA manager",
|
||||
.of_match_table = of_match_ptr(s10_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init s10_init(void)
|
||||
{
|
||||
struct device_node *fw_np;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
fw_np = of_find_node_by_name(NULL, "svc");
|
||||
if (!fw_np)
|
||||
return -ENODEV;
|
||||
|
||||
np = of_find_matching_node(fw_np, s10_of_match);
|
||||
if (!np) {
|
||||
of_node_put(fw_np);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
ret = of_platform_populate(fw_np, s10_of_match, NULL, NULL);
|
||||
of_node_put(fw_np);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return platform_driver_register(&s10_driver);
|
||||
}
|
||||
|
||||
static void __exit s10_exit(void)
|
||||
{
|
||||
return platform_driver_unregister(&s10_driver);
|
||||
}
|
||||
|
||||
module_init(s10_init);
|
||||
module_exit(s10_exit);
|
||||
|
||||
MODULE_AUTHOR("Alan Tull <atull@kernel.org>");
|
||||
MODULE_DESCRIPTION("Intel Stratix 10 SOC FPGA Manager");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -501,6 +501,10 @@ static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/* Release 'PR' control back to the ICAP */
|
||||
zynq_fpga_write(priv, CTRL_OFFSET,
|
||||
zynq_fpga_read(priv, CTRL_OFFSET) & ~CTRL_PCAP_PR_MASK);
|
||||
|
||||
err = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, intr_status,
|
||||
intr_status & IXR_PCFG_DONE_MASK,
|
||||
INIT_POLL_DELAY,
|
||||
|
|
|
@ -711,7 +711,6 @@ int vmbus_disconnect_ring(struct vmbus_channel *channel)
|
|||
/* Snapshot the list of subchannels */
|
||||
spin_lock_irqsave(&channel->lock, flags);
|
||||
list_splice_init(&channel->sc_list, &list);
|
||||
channel->num_sc = 0;
|
||||
spin_unlock_irqrestore(&channel->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(cur_channel, tmp, &list, sc_list) {
|
||||
|
|
|
@ -405,7 +405,6 @@ void hv_process_channel_removal(struct vmbus_channel *channel)
|
|||
primary_channel = channel->primary_channel;
|
||||
spin_lock_irqsave(&primary_channel->lock, flags);
|
||||
list_del(&channel->sc_list);
|
||||
primary_channel->num_sc--;
|
||||
spin_unlock_irqrestore(&primary_channel->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -1302,49 +1301,6 @@ cleanup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the (sub) channel on which to send an outgoing request.
|
||||
* When a primary channel has multiple sub-channels, we try to
|
||||
* distribute the load equally amongst all available channels.
|
||||
*/
|
||||
struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
|
||||
{
|
||||
struct list_head *cur, *tmp;
|
||||
int cur_cpu;
|
||||
struct vmbus_channel *cur_channel;
|
||||
struct vmbus_channel *outgoing_channel = primary;
|
||||
int next_channel;
|
||||
int i = 1;
|
||||
|
||||
if (list_empty(&primary->sc_list))
|
||||
return outgoing_channel;
|
||||
|
||||
next_channel = primary->next_oc++;
|
||||
|
||||
if (next_channel > (primary->num_sc)) {
|
||||
primary->next_oc = 0;
|
||||
return outgoing_channel;
|
||||
}
|
||||
|
||||
cur_cpu = hv_cpu_number_to_vp_number(smp_processor_id());
|
||||
list_for_each_safe(cur, tmp, &primary->sc_list) {
|
||||
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
||||
if (cur_channel->state != CHANNEL_OPENED_STATE)
|
||||
continue;
|
||||
|
||||
if (cur_channel->target_vp == cur_cpu)
|
||||
return cur_channel;
|
||||
|
||||
if (i == next_channel)
|
||||
return cur_channel;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return outgoing_channel;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vmbus_get_outgoing_channel);
|
||||
|
||||
static void invoke_sc_cb(struct vmbus_channel *primary_channel)
|
||||
{
|
||||
struct list_head *cur, *tmp;
|
||||
|
|
|
@ -33,9 +33,7 @@
|
|||
#include "hyperv_vmbus.h"
|
||||
|
||||
/* The one and only */
|
||||
struct hv_context hv_context = {
|
||||
.synic_initialized = false,
|
||||
};
|
||||
struct hv_context hv_context;
|
||||
|
||||
/*
|
||||
* If false, we're using the old mechanism for stimer0 interrupts
|
||||
|
@ -326,8 +324,6 @@ int hv_synic_init(unsigned int cpu)
|
|||
|
||||
hv_set_synic_state(sctrl.as_uint64);
|
||||
|
||||
hv_context.synic_initialized = true;
|
||||
|
||||
/*
|
||||
* Register the per-cpu clockevent source.
|
||||
*/
|
||||
|
@ -373,7 +369,8 @@ int hv_synic_cleanup(unsigned int cpu)
|
|||
bool channel_found = false;
|
||||
unsigned long flags;
|
||||
|
||||
if (!hv_context.synic_initialized)
|
||||
hv_get_synic_state(sctrl.as_uint64);
|
||||
if (sctrl.enable != 1)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
|
@ -435,7 +432,6 @@ int hv_synic_cleanup(unsigned int cpu)
|
|||
hv_set_siefp(siefp.as_uint64);
|
||||
|
||||
/* Disable the global synic bit */
|
||||
hv_get_synic_state(sctrl.as_uint64);
|
||||
sctrl.enable = 0;
|
||||
hv_set_synic_state(sctrl.as_uint64);
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ kvp_send_key(struct work_struct *dummy)
|
|||
val32 = in_msg->body.kvp_set.data.value_u32;
|
||||
message->body.kvp_set.data.value_size =
|
||||
sprintf(message->body.kvp_set.data.value,
|
||||
"%d", val32) + 1;
|
||||
"%u", val32) + 1;
|
||||
break;
|
||||
|
||||
case REG_U64:
|
||||
|
|
|
@ -483,7 +483,7 @@ MODULE_DEVICE_TABLE(vmbus, id_table);
|
|||
|
||||
/* The one and only one */
|
||||
static struct hv_driver util_drv = {
|
||||
.name = "hv_util",
|
||||
.name = "hv_utils",
|
||||
.id_table = id_table,
|
||||
.probe = util_probe,
|
||||
.remove = util_remove,
|
||||
|
|
|
@ -162,8 +162,6 @@ struct hv_context {
|
|||
|
||||
void *tsc_page;
|
||||
|
||||
bool synic_initialized;
|
||||
|
||||
struct hv_per_cpu_context __percpu *cpu_context;
|
||||
|
||||
/*
|
||||
|
|
|
@ -136,6 +136,11 @@ static void __etb_enable_hw(struct etb_drvdata *drvdata)
|
|||
|
||||
static int etb_enable_hw(struct etb_drvdata *drvdata)
|
||||
{
|
||||
int rc = coresight_claim_device(drvdata->base);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
__etb_enable_hw(drvdata);
|
||||
return 0;
|
||||
}
|
||||
|
@ -223,7 +228,7 @@ static int etb_enable(struct coresight_device *csdev, u32 mode, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void etb_disable_hw(struct etb_drvdata *drvdata)
|
||||
static void __etb_disable_hw(struct etb_drvdata *drvdata)
|
||||
{
|
||||
u32 ffcr;
|
||||
|
||||
|
@ -313,6 +318,13 @@ static void etb_dump_hw(struct etb_drvdata *drvdata)
|
|||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
static void etb_disable_hw(struct etb_drvdata *drvdata)
|
||||
{
|
||||
__etb_disable_hw(drvdata);
|
||||
etb_dump_hw(drvdata);
|
||||
coresight_disclaim_device(drvdata->base);
|
||||
}
|
||||
|
||||
static void etb_disable(struct coresight_device *csdev)
|
||||
{
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
@ -323,7 +335,6 @@ static void etb_disable(struct coresight_device *csdev)
|
|||
/* Disable the ETB only if it needs to */
|
||||
if (drvdata->mode != CS_MODE_DISABLED) {
|
||||
etb_disable_hw(drvdata);
|
||||
etb_dump_hw(drvdata);
|
||||
drvdata->mode = CS_MODE_DISABLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
@ -402,7 +413,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
|
|||
|
||||
capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
|
||||
|
||||
etb_disable_hw(drvdata);
|
||||
__etb_disable_hw(drvdata);
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/* unit is in words, not bytes */
|
||||
|
@ -510,7 +521,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
|
|||
handle->head = (cur * PAGE_SIZE) + offset;
|
||||
to_read = buf->nr_pages << PAGE_SHIFT;
|
||||
}
|
||||
etb_enable_hw(drvdata);
|
||||
__etb_enable_hw(drvdata);
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
return to_read;
|
||||
|
@ -534,9 +545,9 @@ static void etb_dump(struct etb_drvdata *drvdata)
|
|||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->mode == CS_MODE_SYSFS) {
|
||||
etb_disable_hw(drvdata);
|
||||
__etb_disable_hw(drvdata);
|
||||
etb_dump_hw(drvdata);
|
||||
etb_enable_hw(drvdata);
|
||||
__etb_enable_hw(drvdata);
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
|
|
|
@ -363,15 +363,16 @@ static int etm_enable_hw(struct etm_drvdata *drvdata)
|
|||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
rc = coresight_claim_device_unlocked(drvdata->base);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
/* Turn engine on */
|
||||
etm_clr_pwrdwn(drvdata);
|
||||
/* Apply power to trace registers */
|
||||
etm_set_pwrup(drvdata);
|
||||
/* Make sure all registers are accessible */
|
||||
etm_os_unlock(drvdata);
|
||||
rc = coresight_claim_device_unlocked(drvdata->base);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
etm_set_prog(drvdata);
|
||||
|
||||
|
@ -422,8 +423,6 @@ static int etm_enable_hw(struct etm_drvdata *drvdata)
|
|||
etm_clr_prog(drvdata);
|
||||
|
||||
done:
|
||||
if (rc)
|
||||
etm_set_pwrdwn(drvdata);
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
dev_dbg(drvdata->dev, "cpu: %d enable smp call done: %d\n",
|
||||
|
@ -577,9 +576,9 @@ static void etm_disable_hw(void *info)
|
|||
for (i = 0; i < drvdata->nr_cntr; i++)
|
||||
config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
|
||||
|
||||
etm_set_pwrdwn(drvdata);
|
||||
coresight_disclaim_device_unlocked(drvdata->base);
|
||||
|
||||
etm_set_pwrdwn(drvdata);
|
||||
CS_LOCK(drvdata->base);
|
||||
|
||||
dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
|
||||
|
@ -602,6 +601,7 @@ static void etm_disable_perf(struct coresight_device *csdev)
|
|||
* power down the tracer.
|
||||
*/
|
||||
etm_set_pwrdwn(drvdata);
|
||||
coresight_disclaim_device_unlocked(drvdata->base);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
|
|
@ -856,7 +856,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
|
||||
if (stm_register_device(dev, &drvdata->stm, THIS_MODULE)) {
|
||||
dev_info(dev,
|
||||
"stm_register_device failed, probing deffered\n");
|
||||
"stm_register_device failed, probing deferred\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,8 +86,8 @@ static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
|
|||
|
||||
static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
coresight_disclaim_device(drvdata);
|
||||
__tmc_etb_disable_hw(drvdata);
|
||||
coresight_disclaim_device(drvdata->base);
|
||||
}
|
||||
|
||||
static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
|
||||
|
|
|
@ -1423,7 +1423,8 @@ nr_pages_store(struct device *dev, struct device_attribute *attr,
|
|||
if (!end)
|
||||
break;
|
||||
|
||||
len -= end - p;
|
||||
/* consume the number and the following comma, hence +1 */
|
||||
len -= end - p + 1;
|
||||
p = end + 1;
|
||||
} while (len);
|
||||
|
||||
|
|
|
@ -440,10 +440,8 @@ stp_policy_make(struct config_group *group, const char *name)
|
|||
|
||||
stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL);
|
||||
if (!stm->policy) {
|
||||
mutex_unlock(&stm->policy_mutex);
|
||||
stm_put_protocol(pdrv);
|
||||
stm_put_device(stm);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto unlock_policy;
|
||||
}
|
||||
|
||||
config_group_init_type_name(&stm->policy->group, name,
|
||||
|
@ -458,7 +456,11 @@ unlock_policy:
|
|||
mutex_unlock(&stm->policy_mutex);
|
||||
|
||||
if (IS_ERR(ret)) {
|
||||
stm_put_protocol(stm->pdrv);
|
||||
/*
|
||||
* pdrv and stm->pdrv at this point can be quite different,
|
||||
* and only one of them needs to be 'put'
|
||||
*/
|
||||
stm_put_protocol(pdrv);
|
||||
stm_put_device(stm);
|
||||
}
|
||||
|
||||
|
|
|
@ -2042,3 +2042,28 @@ int dmar_device_remove(acpi_handle handle)
|
|||
{
|
||||
return dmar_device_hotplug(handle, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* dmar_platform_optin - Is %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in DMAR table
|
||||
*
|
||||
* Returns true if the platform has %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in
|
||||
* the ACPI DMAR table. This means that the platform boot firmware has made
|
||||
* sure no device can issue DMA outside of RMRR regions.
|
||||
*/
|
||||
bool dmar_platform_optin(void)
|
||||
{
|
||||
struct acpi_table_dmar *dmar;
|
||||
acpi_status status;
|
||||
bool ret;
|
||||
|
||||
status = acpi_get_table(ACPI_SIG_DMAR, 0,
|
||||
(struct acpi_table_header **)&dmar);
|
||||
if (ACPI_FAILURE(status))
|
||||
return false;
|
||||
|
||||
ret = !!(dmar->flags & DMAR_PLATFORM_OPT_IN);
|
||||
acpi_put_table((struct acpi_table_header *)dmar);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dmar_platform_optin);
|
||||
|
|
|
@ -184,6 +184,7 @@ static int rwbf_quirk;
|
|||
*/
|
||||
static int force_on = 0;
|
||||
int intel_iommu_tboot_noforce;
|
||||
static int no_platform_optin;
|
||||
|
||||
#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
|
||||
|
||||
|
@ -503,6 +504,7 @@ static int __init intel_iommu_setup(char *str)
|
|||
pr_info("IOMMU enabled\n");
|
||||
} else if (!strncmp(str, "off", 3)) {
|
||||
dmar_disabled = 1;
|
||||
no_platform_optin = 1;
|
||||
pr_info("IOMMU disabled\n");
|
||||
} else if (!strncmp(str, "igfx_off", 8)) {
|
||||
dmar_map_gfx = 0;
|
||||
|
@ -1471,7 +1473,8 @@ static void iommu_enable_dev_iotlb(struct device_domain_info *info)
|
|||
if (info->pri_supported && !pci_reset_pri(pdev) && !pci_enable_pri(pdev, 32))
|
||||
info->pri_enabled = 1;
|
||||
#endif
|
||||
if (info->ats_supported && !pci_enable_ats(pdev, VTD_PAGE_SHIFT)) {
|
||||
if (!pdev->untrusted && info->ats_supported &&
|
||||
!pci_enable_ats(pdev, VTD_PAGE_SHIFT)) {
|
||||
info->ats_enabled = 1;
|
||||
domain_update_iotlb(info->domain);
|
||||
info->ats_qdep = pci_ats_queue_depth(pdev);
|
||||
|
@ -2895,6 +2898,13 @@ static int iommu_should_identity_map(struct device *dev, int startup)
|
|||
if (device_is_rmrr_locked(dev))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Prevent any device marked as untrusted from getting
|
||||
* placed into the statically identity mapping domain.
|
||||
*/
|
||||
if (pdev->untrusted)
|
||||
return 0;
|
||||
|
||||
if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
|
||||
return 1;
|
||||
|
||||
|
@ -4722,14 +4732,54 @@ const struct attribute_group *intel_iommu_groups[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static int __init platform_optin_force_iommu(void)
|
||||
{
|
||||
struct pci_dev *pdev = NULL;
|
||||
bool has_untrusted_dev = false;
|
||||
|
||||
if (!dmar_platform_optin() || no_platform_optin)
|
||||
return 0;
|
||||
|
||||
for_each_pci_dev(pdev) {
|
||||
if (pdev->untrusted) {
|
||||
has_untrusted_dev = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_untrusted_dev)
|
||||
return 0;
|
||||
|
||||
if (no_iommu || dmar_disabled)
|
||||
pr_info("Intel-IOMMU force enabled due to platform opt in\n");
|
||||
|
||||
/*
|
||||
* If Intel-IOMMU is disabled by default, we will apply identity
|
||||
* map for all devices except those marked as being untrusted.
|
||||
*/
|
||||
if (dmar_disabled)
|
||||
iommu_identity_mapping |= IDENTMAP_ALL;
|
||||
|
||||
dmar_disabled = 0;
|
||||
#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB)
|
||||
swiotlb = 0;
|
||||
#endif
|
||||
no_iommu = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __init intel_iommu_init(void)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
struct dmar_drhd_unit *drhd;
|
||||
struct intel_iommu *iommu;
|
||||
|
||||
/* VT-d is required for a TXT/tboot launch, so enforce that */
|
||||
force_on = tboot_force_iommu();
|
||||
/*
|
||||
* Intel IOMMU is required for a TXT/tboot launch or platform
|
||||
* opt in, so enforce that.
|
||||
*/
|
||||
force_on = tboot_force_iommu() || platform_optin_force_iommu();
|
||||
|
||||
if (iommu_init_mempool()) {
|
||||
if (force_on)
|
||||
|
|
|
@ -513,6 +513,14 @@ config MISC_RTSX
|
|||
tristate
|
||||
default MISC_RTSX_PCI || MISC_RTSX_USB
|
||||
|
||||
config PVPANIC
|
||||
tristate "pvpanic device support"
|
||||
depends on HAS_IOMEM && (ACPI || OF)
|
||||
help
|
||||
This driver provides support for the pvpanic device. pvpanic is
|
||||
a paravirtualized device provided by QEMU; it lets a virtual machine
|
||||
(guest) communicate panic events to the host.
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
|
|
|
@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
|
|||
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
|
||||
obj-$(CONFIG_OCXL) += ocxl/
|
||||
obj-y += cardreader/
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
|
|
|
@ -2176,8 +2176,7 @@ static int altera_get_note(u8 *p, s32 program_size,
|
|||
key_ptr = &p[note_strings +
|
||||
get_unaligned_be32(
|
||||
&p[note_table + (8 * i)])];
|
||||
if ((strncasecmp(key, key_ptr, strlen(key_ptr)) == 0) &&
|
||||
(key != NULL)) {
|
||||
if (key && !strncasecmp(key, key_ptr, strlen(key_ptr))) {
|
||||
status = 0;
|
||||
|
||||
value_ptr = &p[note_strings +
|
||||
|
|
|
@ -33,19 +33,6 @@
|
|||
#include "card_base.h"
|
||||
#include "card_ddcb.h"
|
||||
|
||||
#define GENWQE_DEBUGFS_RO(_name, _showfn) \
|
||||
static int genwqe_debugfs_##_name##_open(struct inode *inode, \
|
||||
struct file *file) \
|
||||
{ \
|
||||
return single_open(file, _showfn, inode->i_private); \
|
||||
} \
|
||||
static const struct file_operations genwqe_##_name##_fops = { \
|
||||
.open = genwqe_debugfs_##_name##_open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
static void dbg_uidn_show(struct seq_file *s, struct genwqe_reg *regs,
|
||||
int entries)
|
||||
{
|
||||
|
@ -87,26 +74,26 @@ static int curr_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int genwqe_curr_dbg_uid0_show(struct seq_file *s, void *unused)
|
||||
static int curr_dbg_uid0_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return curr_dbg_uidn_show(s, unused, 0);
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(curr_dbg_uid0, genwqe_curr_dbg_uid0_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid0);
|
||||
|
||||
static int genwqe_curr_dbg_uid1_show(struct seq_file *s, void *unused)
|
||||
static int curr_dbg_uid1_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return curr_dbg_uidn_show(s, unused, 1);
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(curr_dbg_uid1, genwqe_curr_dbg_uid1_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid1);
|
||||
|
||||
static int genwqe_curr_dbg_uid2_show(struct seq_file *s, void *unused)
|
||||
static int curr_dbg_uid2_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return curr_dbg_uidn_show(s, unused, 2);
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(curr_dbg_uid2, genwqe_curr_dbg_uid2_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(curr_dbg_uid2);
|
||||
|
||||
static int prev_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
|
||||
{
|
||||
|
@ -116,28 +103,28 @@ static int prev_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int genwqe_prev_dbg_uid0_show(struct seq_file *s, void *unused)
|
||||
static int prev_dbg_uid0_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return prev_dbg_uidn_show(s, unused, 0);
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(prev_dbg_uid0, genwqe_prev_dbg_uid0_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid0);
|
||||
|
||||
static int genwqe_prev_dbg_uid1_show(struct seq_file *s, void *unused)
|
||||
static int prev_dbg_uid1_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return prev_dbg_uidn_show(s, unused, 1);
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(prev_dbg_uid1, genwqe_prev_dbg_uid1_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid1);
|
||||
|
||||
static int genwqe_prev_dbg_uid2_show(struct seq_file *s, void *unused)
|
||||
static int prev_dbg_uid2_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return prev_dbg_uidn_show(s, unused, 2);
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(prev_dbg_uid2, genwqe_prev_dbg_uid2_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(prev_dbg_uid2);
|
||||
|
||||
static int genwqe_curr_regs_show(struct seq_file *s, void *unused)
|
||||
static int curr_regs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct genwqe_dev *cd = s->private;
|
||||
unsigned int i;
|
||||
|
@ -164,9 +151,9 @@ static int genwqe_curr_regs_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(curr_regs, genwqe_curr_regs_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(curr_regs);
|
||||
|
||||
static int genwqe_prev_regs_show(struct seq_file *s, void *unused)
|
||||
static int prev_regs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct genwqe_dev *cd = s->private;
|
||||
unsigned int i;
|
||||
|
@ -188,9 +175,9 @@ static int genwqe_prev_regs_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(prev_regs, genwqe_prev_regs_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(prev_regs);
|
||||
|
||||
static int genwqe_jtimer_show(struct seq_file *s, void *unused)
|
||||
static int jtimer_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct genwqe_dev *cd = s->private;
|
||||
unsigned int vf_num;
|
||||
|
@ -209,9 +196,9 @@ static int genwqe_jtimer_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(jtimer, genwqe_jtimer_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(jtimer);
|
||||
|
||||
static int genwqe_queue_working_time_show(struct seq_file *s, void *unused)
|
||||
static int queue_working_time_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct genwqe_dev *cd = s->private;
|
||||
unsigned int vf_num;
|
||||
|
@ -227,9 +214,9 @@ static int genwqe_queue_working_time_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(queue_working_time, genwqe_queue_working_time_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(queue_working_time);
|
||||
|
||||
static int genwqe_ddcb_info_show(struct seq_file *s, void *unused)
|
||||
static int ddcb_info_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct genwqe_dev *cd = s->private;
|
||||
unsigned int i;
|
||||
|
@ -300,9 +287,9 @@ static int genwqe_ddcb_info_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(ddcb_info, genwqe_ddcb_info_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(ddcb_info);
|
||||
|
||||
static int genwqe_info_show(struct seq_file *s, void *unused)
|
||||
static int info_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct genwqe_dev *cd = s->private;
|
||||
u64 app_id, slu_id, bitstream = -1;
|
||||
|
@ -335,7 +322,7 @@ static int genwqe_info_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
GENWQE_DEBUGFS_RO(info, genwqe_info_show);
|
||||
DEFINE_SHOW_ATTRIBUTE(info);
|
||||
|
||||
int genwqe_init_debugfs(struct genwqe_dev *cd)
|
||||
{
|
||||
|
@ -356,14 +343,14 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
|||
|
||||
/* non privileged interfaces are done here */
|
||||
file = debugfs_create_file("ddcb_info", S_IRUGO, root, cd,
|
||||
&genwqe_ddcb_info_fops);
|
||||
&ddcb_info_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("info", S_IRUGO, root, cd,
|
||||
&genwqe_info_fops);
|
||||
&info_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
|
@ -396,56 +383,56 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
|||
}
|
||||
|
||||
file = debugfs_create_file("curr_regs", S_IRUGO, root, cd,
|
||||
&genwqe_curr_regs_fops);
|
||||
&curr_regs_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("curr_dbg_uid0", S_IRUGO, root, cd,
|
||||
&genwqe_curr_dbg_uid0_fops);
|
||||
&curr_dbg_uid0_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("curr_dbg_uid1", S_IRUGO, root, cd,
|
||||
&genwqe_curr_dbg_uid1_fops);
|
||||
&curr_dbg_uid1_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("curr_dbg_uid2", S_IRUGO, root, cd,
|
||||
&genwqe_curr_dbg_uid2_fops);
|
||||
&curr_dbg_uid2_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("prev_regs", S_IRUGO, root, cd,
|
||||
&genwqe_prev_regs_fops);
|
||||
&prev_regs_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("prev_dbg_uid0", S_IRUGO, root, cd,
|
||||
&genwqe_prev_dbg_uid0_fops);
|
||||
&prev_dbg_uid0_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("prev_dbg_uid1", S_IRUGO, root, cd,
|
||||
&genwqe_prev_dbg_uid1_fops);
|
||||
&prev_dbg_uid1_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("prev_dbg_uid2", S_IRUGO, root, cd,
|
||||
&genwqe_prev_dbg_uid2_fops);
|
||||
&prev_dbg_uid2_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
|
@ -463,14 +450,14 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
|
|||
}
|
||||
|
||||
file = debugfs_create_file("jobtimer", S_IRUGO, root, cd,
|
||||
&genwqe_jtimer_fops);
|
||||
&jtimer_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("queue_working_time", S_IRUGO, root, cd,
|
||||
&genwqe_queue_working_time_fops);
|
||||
&queue_working_time_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
|
|
|
@ -215,7 +215,7 @@ u32 genwqe_crc32(u8 *buff, size_t len, u32 init)
|
|||
void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
|
||||
dma_addr_t *dma_handle)
|
||||
{
|
||||
if (get_order(size) > MAX_ORDER)
|
||||
if (get_order(size) >= MAX_ORDER)
|
||||
return NULL;
|
||||
|
||||
return dma_zalloc_coherent(&cd->pci_dev->dev, size, dma_handle,
|
||||
|
|
|
@ -9,6 +9,7 @@ mei-objs += hbm.o
|
|||
mei-objs += interrupt.o
|
||||
mei-objs += client.o
|
||||
mei-objs += main.o
|
||||
mei-objs += dma-ring.o
|
||||
mei-objs += bus.o
|
||||
mei-objs += bus-fixup.o
|
||||
mei-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
|
|
@ -317,23 +317,6 @@ void mei_me_cl_rm_all(struct mei_device *dev)
|
|||
up_write(&dev->me_clients_rwsem);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_cmp_id - tells if the clients are the same
|
||||
*
|
||||
* @cl1: host client 1
|
||||
* @cl2: host client 2
|
||||
*
|
||||
* Return: true - if the clients has same host and me ids
|
||||
* false - otherwise
|
||||
*/
|
||||
static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
|
||||
const struct mei_cl *cl2)
|
||||
{
|
||||
return cl1 && cl2 &&
|
||||
(cl1->host_client_id == cl2->host_client_id) &&
|
||||
(mei_cl_me_id(cl1) == mei_cl_me_id(cl2));
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_io_cb_free - free mei_cb_private related memory
|
||||
*
|
||||
|
@ -418,7 +401,7 @@ static void mei_io_list_flush_cl(struct list_head *head,
|
|||
struct mei_cl_cb *cb, *next;
|
||||
|
||||
list_for_each_entry_safe(cb, next, head, list) {
|
||||
if (mei_cl_cmp_id(cl, cb->cl))
|
||||
if (cl == cb->cl)
|
||||
list_del_init(&cb->list);
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +418,7 @@ static void mei_io_tx_list_free_cl(struct list_head *head,
|
|||
struct mei_cl_cb *cb, *next;
|
||||
|
||||
list_for_each_entry_safe(cb, next, head, list) {
|
||||
if (mei_cl_cmp_id(cl, cb->cl))
|
||||
if (cl == cb->cl)
|
||||
mei_tx_cb_dequeue(cb);
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +461,7 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
|
|||
if (length == 0)
|
||||
return cb;
|
||||
|
||||
cb->buf.data = kmalloc(length, GFP_KERNEL);
|
||||
cb->buf.data = kmalloc(roundup(length, MEI_SLOT_SIZE), GFP_KERNEL);
|
||||
if (!cb->buf.data) {
|
||||
mei_io_cb_free(cb);
|
||||
return NULL;
|
||||
|
@ -1374,7 +1357,9 @@ int mei_cl_notify_request(struct mei_cl *cl,
|
|||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
wait_event_timeout(cl->wait,
|
||||
cl->notify_en == request || !mei_cl_is_connected(cl),
|
||||
cl->notify_en == request ||
|
||||
cl->status ||
|
||||
!mei_cl_is_connected(cl),
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
|
@ -1573,10 +1558,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
struct mei_msg_hdr mei_hdr;
|
||||
size_t hdr_len = sizeof(mei_hdr);
|
||||
size_t len;
|
||||
size_t hbuf_len;
|
||||
size_t hbuf_len, dr_len;
|
||||
int hbuf_slots;
|
||||
u32 dr_slots;
|
||||
u32 dma_len;
|
||||
int rets;
|
||||
bool first_chunk;
|
||||
const void *data;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
@ -1597,6 +1585,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
}
|
||||
|
||||
len = buf->size - cb->buf_idx;
|
||||
data = buf->data + cb->buf_idx;
|
||||
hbuf_slots = mei_hbuf_empty_slots(dev);
|
||||
if (hbuf_slots < 0) {
|
||||
rets = -EOVERFLOW;
|
||||
|
@ -1604,6 +1593,8 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
}
|
||||
|
||||
hbuf_len = mei_slots2data(hbuf_slots);
|
||||
dr_slots = mei_dma_ring_empty_slots(dev);
|
||||
dr_len = mei_slots2data(dr_slots);
|
||||
|
||||
mei_msg_hdr_init(&mei_hdr, cb);
|
||||
|
||||
|
@ -1614,23 +1605,33 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
if (len + hdr_len <= hbuf_len) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
|
||||
mei_hdr.dma_ring = 1;
|
||||
if (len > dr_len)
|
||||
len = dr_len;
|
||||
else
|
||||
mei_hdr.msg_complete = 1;
|
||||
|
||||
mei_hdr.length = sizeof(dma_len);
|
||||
dma_len = len;
|
||||
data = &dma_len;
|
||||
} else if ((u32)hbuf_slots == mei_hbuf_depth(dev)) {
|
||||
mei_hdr.length = hbuf_len - hdr_len;
|
||||
len = hbuf_len - hdr_len;
|
||||
mei_hdr.length = len;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
|
||||
cb->buf.size, cb->buf_idx);
|
||||
if (mei_hdr.dma_ring)
|
||||
mei_dma_ring_write(dev, buf->data + cb->buf_idx, len);
|
||||
|
||||
rets = mei_write_message(dev, &mei_hdr, hdr_len,
|
||||
buf->data + cb->buf_idx, mei_hdr.length);
|
||||
rets = mei_write_message(dev, &mei_hdr, hdr_len, data, mei_hdr.length);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
cl->status = 0;
|
||||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx += mei_hdr.length;
|
||||
cb->buf_idx += len;
|
||||
|
||||
if (first_chunk) {
|
||||
if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
|
||||
|
@ -1665,11 +1666,13 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
|
|||
struct mei_msg_data *buf;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
size_t hdr_len = sizeof(mei_hdr);
|
||||
size_t len;
|
||||
size_t hbuf_len;
|
||||
size_t len, hbuf_len, dr_len;
|
||||
int hbuf_slots;
|
||||
u32 dr_slots;
|
||||
u32 dma_len;
|
||||
ssize_t rets;
|
||||
bool blocking;
|
||||
const void *data;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
@ -1681,10 +1684,12 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
|
|||
|
||||
buf = &cb->buf;
|
||||
len = buf->size;
|
||||
blocking = cb->blocking;
|
||||
|
||||
cl_dbg(dev, cl, "len=%zd\n", len);
|
||||
|
||||
blocking = cb->blocking;
|
||||
data = buf->data;
|
||||
|
||||
rets = pm_runtime_get(dev->dev);
|
||||
if (rets < 0 && rets != -EINPROGRESS) {
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
|
@ -1721,16 +1726,32 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
|
|||
}
|
||||
|
||||
hbuf_len = mei_slots2data(hbuf_slots);
|
||||
dr_slots = mei_dma_ring_empty_slots(dev);
|
||||
dr_len = mei_slots2data(dr_slots);
|
||||
|
||||
if (len + hdr_len <= hbuf_len) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
|
||||
mei_hdr.dma_ring = 1;
|
||||
if (len > dr_len)
|
||||
len = dr_len;
|
||||
else
|
||||
mei_hdr.msg_complete = 1;
|
||||
|
||||
mei_hdr.length = sizeof(dma_len);
|
||||
dma_len = len;
|
||||
data = &dma_len;
|
||||
} else {
|
||||
mei_hdr.length = hbuf_len - hdr_len;
|
||||
len = hbuf_len - hdr_len;
|
||||
mei_hdr.length = len;
|
||||
}
|
||||
|
||||
if (mei_hdr.dma_ring)
|
||||
mei_dma_ring_write(dev, buf->data, len);
|
||||
|
||||
rets = mei_write_message(dev, &mei_hdr, hdr_len,
|
||||
buf->data, mei_hdr.length);
|
||||
data, mei_hdr.length);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
|
@ -1739,7 +1760,9 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
|
|||
goto err;
|
||||
|
||||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx = mei_hdr.length;
|
||||
cb->buf_idx = len;
|
||||
/* restore return value */
|
||||
len = buf->size;
|
||||
|
||||
out:
|
||||
if (mei_hdr.msg_complete)
|
||||
|
|
269
drivers/misc/mei/dma-ring.c
Normal file
269
drivers/misc/mei/dma-ring.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright(c) 2016 - 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
|
||||
/**
|
||||
* mei_dmam_dscr_alloc() - allocate a managed coherent buffer
|
||||
* for the dma descriptor
|
||||
* @dev: mei_device
|
||||
* @dscr: dma descriptor
|
||||
*
|
||||
* Return:
|
||||
* * 0 - on success or zero allocation request
|
||||
* * -EINVAL - if size is not power of 2
|
||||
* * -ENOMEM - of allocation has failed
|
||||
*/
|
||||
static int mei_dmam_dscr_alloc(struct mei_device *dev,
|
||||
struct mei_dma_dscr *dscr)
|
||||
{
|
||||
if (!dscr->size)
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(!is_power_of_2(dscr->size)))
|
||||
return -EINVAL;
|
||||
|
||||
if (dscr->vaddr)
|
||||
return 0;
|
||||
|
||||
dscr->vaddr = dmam_alloc_coherent(dev->dev, dscr->size, &dscr->daddr,
|
||||
GFP_KERNEL);
|
||||
if (!dscr->vaddr)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dmam_dscr_free() - free a managed coherent buffer
|
||||
* from the dma descriptor
|
||||
* @dev: mei_device
|
||||
* @dscr: dma descriptor
|
||||
*/
|
||||
static void mei_dmam_dscr_free(struct mei_device *dev,
|
||||
struct mei_dma_dscr *dscr)
|
||||
{
|
||||
if (!dscr->vaddr)
|
||||
return;
|
||||
|
||||
dmam_free_coherent(dev->dev, dscr->size, dscr->vaddr, dscr->daddr);
|
||||
dscr->vaddr = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dmam_ring_free() - free dma ring buffers
|
||||
* @dev: mei device
|
||||
*/
|
||||
void mei_dmam_ring_free(struct mei_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DMA_DSCR_NUM; i++)
|
||||
mei_dmam_dscr_free(dev, &dev->dr_dscr[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dmam_ring_alloc() - allocate dma ring buffers
|
||||
* @dev: mei device
|
||||
*
|
||||
* Return: -ENOMEM on allocation failure 0 otherwise
|
||||
*/
|
||||
int mei_dmam_ring_alloc(struct mei_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DMA_DSCR_NUM; i++)
|
||||
if (mei_dmam_dscr_alloc(dev, &dev->dr_dscr[i]))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mei_dmam_ring_free(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_ring_is_allocated() - check if dma ring is allocated
|
||||
* @dev: mei device
|
||||
*
|
||||
* Return: true if dma ring is allocated
|
||||
*/
|
||||
bool mei_dma_ring_is_allocated(struct mei_device *dev)
|
||||
{
|
||||
return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct hbm_dma_ring_ctrl *mei_dma_ring_ctrl(struct mei_device *dev)
|
||||
{
|
||||
return (struct hbm_dma_ring_ctrl *)dev->dr_dscr[DMA_DSCR_CTRL].vaddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_ring_reset() - reset the dma control block
|
||||
* @dev: mei device
|
||||
*/
|
||||
void mei_dma_ring_reset(struct mei_device *dev)
|
||||
{
|
||||
struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
|
||||
|
||||
if (!ctrl)
|
||||
return;
|
||||
|
||||
memset(ctrl, 0, sizeof(*ctrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_copy_from() - copy from dma ring into buffer
|
||||
* @dev: mei device
|
||||
* @buf: data buffer
|
||||
* @offset: offset in slots.
|
||||
* @n: number of slots to copy.
|
||||
*/
|
||||
static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
|
||||
u32 offset, u32 n)
|
||||
{
|
||||
unsigned char *dbuf = dev->dr_dscr[DMA_DSCR_DEVICE].vaddr;
|
||||
|
||||
size_t b_offset = offset << 2;
|
||||
size_t b_n = n << 2;
|
||||
|
||||
memcpy(buf, dbuf + b_offset, b_n);
|
||||
|
||||
return b_n;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_copy_to() - copy to a buffer to the dma ring
|
||||
* @dev: mei device
|
||||
* @buf: data buffer
|
||||
* @offset: offset in slots.
|
||||
* @n: number of slots to copy.
|
||||
*/
|
||||
static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
|
||||
u32 offset, u32 n)
|
||||
{
|
||||
unsigned char *hbuf = dev->dr_dscr[DMA_DSCR_HOST].vaddr;
|
||||
|
||||
size_t b_offset = offset << 2;
|
||||
size_t b_n = n << 2;
|
||||
|
||||
memcpy(hbuf + b_offset, buf, b_n);
|
||||
|
||||
return b_n;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_ring_read() - read data from the ring
|
||||
* @dev: mei device
|
||||
* @buf: buffer to read into: may be NULL in case of droping the data.
|
||||
* @len: length to read.
|
||||
*/
|
||||
void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
|
||||
{
|
||||
struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
|
||||
u32 dbuf_depth;
|
||||
u32 rd_idx, rem, slots;
|
||||
|
||||
if (WARN_ON(!ctrl))
|
||||
return;
|
||||
|
||||
dev_dbg(dev->dev, "reading from dma %u bytes\n", len);
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
dbuf_depth = dev->dr_dscr[DMA_DSCR_DEVICE].size >> 2;
|
||||
rd_idx = READ_ONCE(ctrl->dbuf_rd_idx) & (dbuf_depth - 1);
|
||||
slots = mei_data2slots(len);
|
||||
|
||||
/* if buf is NULL we drop the packet by advancing the pointer.*/
|
||||
if (!buf)
|
||||
goto out;
|
||||
|
||||
if (rd_idx + slots > dbuf_depth) {
|
||||
buf += mei_dma_copy_from(dev, buf, rd_idx, dbuf_depth - rd_idx);
|
||||
rem = slots - (dbuf_depth - rd_idx);
|
||||
rd_idx = 0;
|
||||
} else {
|
||||
rem = slots;
|
||||
}
|
||||
|
||||
mei_dma_copy_from(dev, buf, rd_idx, rem);
|
||||
out:
|
||||
WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots);
|
||||
}
|
||||
|
||||
static inline u32 mei_dma_ring_hbuf_depth(struct mei_device *dev)
|
||||
{
|
||||
return dev->dr_dscr[DMA_DSCR_HOST].size >> 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_ring_empty_slots() - calaculate number of empty slots in dma ring
|
||||
* @dev: mei_device
|
||||
*
|
||||
* Return: number of empty slots
|
||||
*/
|
||||
u32 mei_dma_ring_empty_slots(struct mei_device *dev)
|
||||
{
|
||||
struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
|
||||
u32 wr_idx, rd_idx, hbuf_depth, empty;
|
||||
|
||||
if (!mei_dma_ring_is_allocated(dev))
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(!ctrl))
|
||||
return 0;
|
||||
|
||||
/* easier to work in slots */
|
||||
hbuf_depth = mei_dma_ring_hbuf_depth(dev);
|
||||
rd_idx = READ_ONCE(ctrl->hbuf_rd_idx);
|
||||
wr_idx = READ_ONCE(ctrl->hbuf_wr_idx);
|
||||
|
||||
if (rd_idx > wr_idx)
|
||||
empty = rd_idx - wr_idx;
|
||||
else
|
||||
empty = hbuf_depth - (wr_idx - rd_idx);
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_dma_ring_write - write data to dma ring host buffer
|
||||
*
|
||||
* @dev: mei_device
|
||||
* @buf: data will be written
|
||||
* @len: data length
|
||||
*/
|
||||
void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len)
|
||||
{
|
||||
struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
|
||||
u32 hbuf_depth;
|
||||
u32 wr_idx, rem, slots;
|
||||
|
||||
if (WARN_ON(!ctrl))
|
||||
return;
|
||||
|
||||
dev_dbg(dev->dev, "writing to dma %u bytes\n", len);
|
||||
hbuf_depth = mei_dma_ring_hbuf_depth(dev);
|
||||
wr_idx = READ_ONCE(ctrl->hbuf_wr_idx) & (hbuf_depth - 1);
|
||||
slots = mei_data2slots(len);
|
||||
|
||||
if (wr_idx + slots > hbuf_depth) {
|
||||
buf += mei_dma_copy_to(dev, buf, wr_idx, hbuf_depth - wr_idx);
|
||||
rem = slots - (hbuf_depth - wr_idx);
|
||||
wr_idx = 0;
|
||||
} else {
|
||||
rem = slots;
|
||||
}
|
||||
|
||||
mei_dma_copy_to(dev, buf, wr_idx, rem);
|
||||
|
||||
WRITE_ONCE(ctrl->hbuf_wr_idx, ctrl->hbuf_wr_idx + slots);
|
||||
}
|
|
@ -65,6 +65,7 @@ const char *mei_hbm_state_str(enum mei_hbm_state state)
|
|||
MEI_HBM_STATE(IDLE);
|
||||
MEI_HBM_STATE(STARTING);
|
||||
MEI_HBM_STATE(STARTED);
|
||||
MEI_HBM_STATE(DR_SETUP);
|
||||
MEI_HBM_STATE(ENUM_CLIENTS);
|
||||
MEI_HBM_STATE(CLIENT_PROPERTIES);
|
||||
MEI_HBM_STATE(STOPPED);
|
||||
|
@ -295,6 +296,48 @@ int mei_hbm_start_req(struct mei_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_dma_setup_req() - setup DMA request
|
||||
* @dev: the device structure
|
||||
*
|
||||
* Return: 0 on success and < 0 on failure
|
||||
*/
|
||||
static int mei_hbm_dma_setup_req(struct mei_device *dev)
|
||||
{
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
struct hbm_dma_setup_request req;
|
||||
const size_t len = sizeof(struct hbm_dma_setup_request);
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
mei_hbm_hdr(&mei_hdr, len);
|
||||
|
||||
memset(&req, 0, len);
|
||||
req.hbm_cmd = MEI_HBM_DMA_SETUP_REQ_CMD;
|
||||
for (i = 0; i < DMA_DSCR_NUM; i++) {
|
||||
phys_addr_t paddr;
|
||||
|
||||
paddr = dev->dr_dscr[i].daddr;
|
||||
req.dma_dscr[i].addr_hi = upper_32_bits(paddr);
|
||||
req.dma_dscr[i].addr_lo = lower_32_bits(paddr);
|
||||
req.dma_dscr[i].size = dev->dr_dscr[i].size;
|
||||
}
|
||||
|
||||
mei_dma_ring_reset(dev);
|
||||
|
||||
ret = mei_hbm_write_message(dev, &mei_hdr, &req);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "dma setup request write failed: ret = %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev->hbm_state = MEI_HBM_DR_SETUP;
|
||||
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
|
||||
mei_schedule_stall_timer(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_enum_clients_req - sends enumeration client request message.
|
||||
*
|
||||
|
@ -1044,6 +1087,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
struct hbm_host_version_response *version_res;
|
||||
struct hbm_props_response *props_res;
|
||||
struct hbm_host_enum_response *enum_res;
|
||||
struct hbm_dma_setup_response *dma_setup_res;
|
||||
struct hbm_add_client_request *add_cl_req;
|
||||
int ret;
|
||||
|
||||
|
@ -1108,13 +1152,51 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (mei_hbm_enum_clients_req(dev)) {
|
||||
dev_err(dev->dev, "hbm: start: failed to send enumeration request\n");
|
||||
if (dev->hbm_f_dr_supported) {
|
||||
if (mei_dmam_ring_alloc(dev))
|
||||
dev_info(dev->dev, "running w/o dma ring\n");
|
||||
if (mei_dma_ring_is_allocated(dev)) {
|
||||
if (mei_hbm_dma_setup_req(dev))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
wake_up(&dev->wait_hbm_start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev->hbm_f_dr_supported = 0;
|
||||
mei_dmam_ring_free(dev);
|
||||
|
||||
if (mei_hbm_enum_clients_req(dev))
|
||||
return -EIO;
|
||||
|
||||
wake_up(&dev->wait_hbm_start);
|
||||
break;
|
||||
|
||||
case MEI_HBM_DMA_SETUP_RES_CMD:
|
||||
dev_dbg(dev->dev, "hbm: dma setup response: message received.\n");
|
||||
|
||||
dev->init_clients_timer = 0;
|
||||
|
||||
if (dev->hbm_state != MEI_HBM_DR_SETUP) {
|
||||
dev_err(dev->dev, "hbm: dma setup response: state mismatch, [%d, %d]\n",
|
||||
dev->dev_state, dev->hbm_state);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
dma_setup_res = (struct hbm_dma_setup_response *)mei_msg;
|
||||
|
||||
if (dma_setup_res->status) {
|
||||
dev_info(dev->dev, "hbm: dma setup response: failure = %d %s\n",
|
||||
dma_setup_res->status,
|
||||
mei_hbm_status_str(dma_setup_res->status));
|
||||
dev->hbm_f_dr_supported = 0;
|
||||
mei_dmam_ring_free(dev);
|
||||
}
|
||||
|
||||
if (mei_hbm_enum_clients_req(dev))
|
||||
return -EIO;
|
||||
break;
|
||||
|
||||
case CLIENT_CONNECT_RES_CMD:
|
||||
dev_dbg(dev->dev, "hbm: client connect response: message received.\n");
|
||||
|
@ -1271,8 +1353,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
WARN(1, "hbm: wrong command %d\n", mei_msg->hbm_cmd);
|
||||
return -EPROTO;
|
||||
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -26,6 +26,7 @@ struct mei_cl;
|
|||
*
|
||||
* @MEI_HBM_IDLE : protocol not started
|
||||
* @MEI_HBM_STARTING : start request message was sent
|
||||
* @MEI_HBM_DR_SETUP : dma ring setup request message was sent
|
||||
* @MEI_HBM_ENUM_CLIENTS : enumeration request was sent
|
||||
* @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties
|
||||
* @MEI_HBM_STARTED : enumeration was completed
|
||||
|
@ -34,6 +35,7 @@ struct mei_cl;
|
|||
enum mei_hbm_state {
|
||||
MEI_HBM_IDLE = 0,
|
||||
MEI_HBM_STARTING,
|
||||
MEI_HBM_DR_SETUP,
|
||||
MEI_HBM_ENUM_CLIENTS,
|
||||
MEI_HBM_CLIENT_PROPERTIES,
|
||||
MEI_HBM_STARTED,
|
||||
|
|
|
@ -1471,15 +1471,21 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
|
|||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_hw *hw;
|
||||
int i;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) +
|
||||
sizeof(struct mei_me_hw), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
hw = to_me_hw(dev);
|
||||
|
||||
for (i = 0; i < DMA_DSCR_NUM; i++)
|
||||
dev->dr_dscr[i].size = cfg->dma_size[i];
|
||||
|
||||
mei_device_init(dev, &pdev->dev, &mei_me_hw_ops);
|
||||
hw->cfg = cfg;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
/*
|
||||
* MEI Version
|
||||
*/
|
||||
#define HBM_MINOR_VERSION 0
|
||||
#define HBM_MINOR_VERSION 1
|
||||
#define HBM_MAJOR_VERSION 2
|
||||
|
||||
/*
|
||||
|
@ -206,6 +206,7 @@ enum mei_cl_disconnect_status {
|
|||
* @dma_ring: message is on dma ring
|
||||
* @internal: message is internal
|
||||
* @msg_complete: last packet of the message
|
||||
* @extension: extension of the header
|
||||
*/
|
||||
struct mei_msg_hdr {
|
||||
u32 me_addr:8;
|
||||
|
@ -215,8 +216,11 @@ struct mei_msg_hdr {
|
|||
u32 dma_ring:1;
|
||||
u32 internal:1;
|
||||
u32 msg_complete:1;
|
||||
u32 extension[0];
|
||||
} __packed;
|
||||
|
||||
#define MEI_MSG_HDR_MAX 2
|
||||
|
||||
struct mei_bus_message {
|
||||
u8 hbm_cmd;
|
||||
u8 data[0];
|
||||
|
@ -512,4 +516,27 @@ struct hbm_dma_setup_response {
|
|||
u8 reserved[2];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct mei_dma_ring_ctrl - dma ring control block
|
||||
*
|
||||
* @hbuf_wr_idx: host circular buffer write index in slots
|
||||
* @reserved1: reserved for alignment
|
||||
* @hbuf_rd_idx: host circular buffer read index in slots
|
||||
* @reserved2: reserved for alignment
|
||||
* @dbuf_wr_idx: device circular buffer write index in slots
|
||||
* @reserved3: reserved for alignment
|
||||
* @dbuf_rd_idx: device circular buffer read index in slots
|
||||
* @reserved4: reserved for alignment
|
||||
*/
|
||||
struct hbm_dma_ring_ctrl {
|
||||
u32 hbuf_wr_idx;
|
||||
u32 reserved1;
|
||||
u32 hbuf_rd_idx;
|
||||
u32 reserved2;
|
||||
u32 dbuf_wr_idx;
|
||||
u32 reserved3;
|
||||
u32 dbuf_rd_idx;
|
||||
u32 reserved4;
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -151,7 +151,7 @@ int mei_reset(struct mei_device *dev)
|
|||
|
||||
mei_hbm_reset(dev);
|
||||
|
||||
dev->rd_msg_hdr = 0;
|
||||
memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "hw_reset failed ret = %d\n", ret);
|
||||
|
|
|
@ -75,6 +75,8 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
|
|||
*/
|
||||
static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
||||
{
|
||||
if (hdr->dma_ring)
|
||||
mei_dma_ring_read(dev, NULL, hdr->extension[0]);
|
||||
/*
|
||||
* no need to check for size as it is guarantied
|
||||
* that length fits into rd_msg_buf
|
||||
|
@ -100,6 +102,7 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
|
|||
struct mei_device *dev = cl->dev;
|
||||
struct mei_cl_cb *cb;
|
||||
size_t buf_sz;
|
||||
u32 length;
|
||||
|
||||
cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
|
||||
if (!cb) {
|
||||
|
@ -119,25 +122,31 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl,
|
|||
goto discard;
|
||||
}
|
||||
|
||||
buf_sz = mei_hdr->length + cb->buf_idx;
|
||||
length = mei_hdr->dma_ring ? mei_hdr->extension[0] : mei_hdr->length;
|
||||
|
||||
buf_sz = length + cb->buf_idx;
|
||||
/* catch for integer overflow */
|
||||
if (buf_sz < cb->buf_idx) {
|
||||
cl_err(dev, cl, "message is too big len %d idx %zu\n",
|
||||
mei_hdr->length, cb->buf_idx);
|
||||
length, cb->buf_idx);
|
||||
cb->status = -EMSGSIZE;
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if (cb->buf.size < buf_sz) {
|
||||
cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
|
||||
cb->buf.size, mei_hdr->length, cb->buf_idx);
|
||||
cb->buf.size, length, cb->buf_idx);
|
||||
cb->status = -EMSGSIZE;
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if (mei_hdr->dma_ring)
|
||||
mei_dma_ring_read(dev, cb->buf.data + cb->buf_idx, length);
|
||||
|
||||
/* for DMA read 0 length to generate an interrupt to the device */
|
||||
mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length);
|
||||
|
||||
cb->buf_idx += mei_hdr->length;
|
||||
cb->buf_idx += length;
|
||||
|
||||
if (mei_hdr->msg_complete) {
|
||||
cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx);
|
||||
|
@ -247,6 +256,9 @@ static inline int hdr_is_valid(u32 msg_hdr)
|
|||
if (!msg_hdr || mei_hdr->reserved)
|
||||
return -EBADMSG;
|
||||
|
||||
if (mei_hdr->dma_ring && mei_hdr->length != MEI_SLOT_SIZE)
|
||||
return -EBADMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -267,20 +279,20 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
struct mei_cl *cl;
|
||||
int ret;
|
||||
|
||||
if (!dev->rd_msg_hdr) {
|
||||
dev->rd_msg_hdr = mei_read_hdr(dev);
|
||||
if (!dev->rd_msg_hdr[0]) {
|
||||
dev->rd_msg_hdr[0] = mei_read_hdr(dev);
|
||||
(*slots)--;
|
||||
dev_dbg(dev->dev, "slots =%08x.\n", *slots);
|
||||
|
||||
ret = hdr_is_valid(dev->rd_msg_hdr);
|
||||
ret = hdr_is_valid(dev->rd_msg_hdr[0]);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "corrupted message header 0x%08X\n",
|
||||
dev->rd_msg_hdr);
|
||||
dev->rd_msg_hdr[0]);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
mei_hdr = (struct mei_msg_hdr *)&dev->rd_msg_hdr;
|
||||
mei_hdr = (struct mei_msg_hdr *)dev->rd_msg_hdr;
|
||||
dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
|
||||
|
||||
if (mei_slots2data(*slots) < mei_hdr->length) {
|
||||
|
@ -291,6 +303,12 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (mei_hdr->dma_ring) {
|
||||
dev->rd_msg_hdr[1] = mei_read_hdr(dev);
|
||||
(*slots)--;
|
||||
mei_hdr->length = 0;
|
||||
}
|
||||
|
||||
/* HBM message */
|
||||
if (hdr_is_hbm(mei_hdr)) {
|
||||
ret = mei_hbm_dispatch(dev, mei_hdr);
|
||||
|
@ -324,7 +342,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
goto reset_slots;
|
||||
}
|
||||
dev_err(dev->dev, "no destination client found 0x%08X\n",
|
||||
dev->rd_msg_hdr);
|
||||
dev->rd_msg_hdr[0]);
|
||||
ret = -EBADMSG;
|
||||
goto end;
|
||||
}
|
||||
|
@ -334,9 +352,8 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
|
||||
reset_slots:
|
||||
/* reset the number of slots and header */
|
||||
memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
|
||||
*slots = mei_count_full_read_slots(dev);
|
||||
dev->rd_msg_hdr = 0;
|
||||
|
||||
if (*slots == -EOVERFLOW) {
|
||||
/* overflow - reset */
|
||||
dev_err(dev->dev, "resetting due to slots overflow.\n");
|
||||
|
|
|
@ -122,6 +122,19 @@ struct mei_msg_data {
|
|||
unsigned char *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mei_dma_dscr - dma address descriptor
|
||||
*
|
||||
* @vaddr: dma buffer virtual address
|
||||
* @daddr: dma buffer physical address
|
||||
* @size : dma buffer size
|
||||
*/
|
||||
struct mei_dma_dscr {
|
||||
void *vaddr;
|
||||
dma_addr_t daddr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* Maximum number of processed FW status registers */
|
||||
#define MEI_FW_STATUS_MAX 6
|
||||
/* Minimal buffer for FW status string (8 bytes in dw + space or '\0') */
|
||||
|
@ -409,6 +422,7 @@ struct mei_fw_version {
|
|||
* @rd_msg_hdr : read message header storage
|
||||
*
|
||||
* @hbuf_is_ready : query if the host host/write buffer is ready
|
||||
* @dr_dscr: DMA ring descriptors: TX, RX, and CTRL
|
||||
*
|
||||
* @version : HBM protocol version in use
|
||||
* @hbm_f_pg_supported : hbm feature pgi protocol
|
||||
|
@ -483,11 +497,13 @@ struct mei_device {
|
|||
#endif /* CONFIG_PM */
|
||||
|
||||
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE];
|
||||
u32 rd_msg_hdr;
|
||||
u32 rd_msg_hdr[MEI_MSG_HDR_MAX];
|
||||
|
||||
/* write buffer */
|
||||
bool hbuf_is_ready;
|
||||
|
||||
struct mei_dma_dscr dr_dscr[DMA_DSCR_NUM];
|
||||
|
||||
struct hbm_version version;
|
||||
unsigned int hbm_f_pg_supported:1;
|
||||
unsigned int hbm_f_dc_supported:1;
|
||||
|
@ -578,6 +594,14 @@ int mei_restart(struct mei_device *dev);
|
|||
void mei_stop(struct mei_device *dev);
|
||||
void mei_cancel_work(struct mei_device *dev);
|
||||
|
||||
int mei_dmam_ring_alloc(struct mei_device *dev);
|
||||
void mei_dmam_ring_free(struct mei_device *dev);
|
||||
bool mei_dma_ring_is_allocated(struct mei_device *dev);
|
||||
void mei_dma_ring_reset(struct mei_device *dev);
|
||||
void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len);
|
||||
void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len);
|
||||
u32 mei_dma_ring_empty_slots(struct mei_device *dev);
|
||||
|
||||
/*
|
||||
* MEI interrupt functions prototype
|
||||
*/
|
||||
|
|
|
@ -98,9 +98,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
|
|||
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)},
|
||||
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH8_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_4, MEI_ME_PCH8_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH8_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_4, MEI_ME_PCH8_CFG)},
|
||||
|
||||
/* required last entry */
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
static struct dentry *mic_dbg;
|
||||
|
||||
/**
|
||||
* mic_intr_test - Send interrupts to host.
|
||||
* mic_intr_show - Send interrupts to host.
|
||||
*/
|
||||
static int mic_intr_test(struct seq_file *s, void *unused)
|
||||
static int mic_intr_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct mic_driver *mdrv = s->private;
|
||||
struct mic_device *mdev = &mdrv->mdev;
|
||||
|
@ -56,23 +56,7 @@ static int mic_intr_test(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mic_intr_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mic_intr_test, inode->i_private);
|
||||
}
|
||||
|
||||
static int mic_intr_test_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations intr_test_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = mic_intr_test_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = mic_intr_test_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(mic_intr);
|
||||
|
||||
/**
|
||||
* mic_create_card_debug_dir - Initialize MIC debugfs entries.
|
||||
|
@ -91,7 +75,7 @@ void __init mic_create_card_debug_dir(struct mic_driver *mdrv)
|
|||
}
|
||||
|
||||
d = debugfs_create_file("intr_test", 0444, mdrv->dbg_dir,
|
||||
mdrv, &intr_test_ops);
|
||||
mdrv, &mic_intr_fops);
|
||||
|
||||
if (!d) {
|
||||
dev_err(mdrv->dev,
|
||||
|
|
|
@ -28,12 +28,12 @@
|
|||
static struct dentry *cosm_dbg;
|
||||
|
||||
/**
|
||||
* cosm_log_buf_show - Display MIC kernel log buffer
|
||||
* log_buf_show - Display MIC kernel log buffer
|
||||
*
|
||||
* log_buf addr/len is read from System.map by user space
|
||||
* and populated in sysfs entries.
|
||||
*/
|
||||
static int cosm_log_buf_show(struct seq_file *s, void *unused)
|
||||
static int log_buf_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
void __iomem *log_buf_va;
|
||||
int __iomem *log_buf_len_va;
|
||||
|
@ -78,26 +78,15 @@ done:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cosm_log_buf_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, cosm_log_buf_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations log_buf_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = cosm_log_buf_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(log_buf);
|
||||
|
||||
/**
|
||||
* cosm_force_reset_show - Force MIC reset
|
||||
* force_reset_show - Force MIC reset
|
||||
*
|
||||
* Invokes the force_reset COSM bus op instead of the standard reset
|
||||
* op in case a force reset of the MIC device is required
|
||||
*/
|
||||
static int cosm_force_reset_show(struct seq_file *s, void *pos)
|
||||
static int force_reset_show(struct seq_file *s, void *pos)
|
||||
{
|
||||
struct cosm_device *cdev = s->private;
|
||||
|
||||
|
@ -105,18 +94,7 @@ static int cosm_force_reset_show(struct seq_file *s, void *pos)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cosm_force_reset_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, cosm_force_reset_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations force_reset_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = cosm_force_reset_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(force_reset);
|
||||
|
||||
void cosm_create_debug_dir(struct cosm_device *cdev)
|
||||
{
|
||||
|
@ -130,9 +108,10 @@ void cosm_create_debug_dir(struct cosm_device *cdev)
|
|||
if (!cdev->dbg_dir)
|
||||
return;
|
||||
|
||||
debugfs_create_file("log_buf", 0444, cdev->dbg_dir, cdev, &log_buf_ops);
|
||||
debugfs_create_file("log_buf", 0444, cdev->dbg_dir, cdev,
|
||||
&log_buf_fops);
|
||||
debugfs_create_file("force_reset", 0444, cdev->dbg_dir, cdev,
|
||||
&force_reset_ops);
|
||||
&force_reset_fops);
|
||||
}
|
||||
|
||||
void cosm_delete_debug_dir(struct cosm_device *cdev)
|
||||
|
|
|
@ -54,23 +54,7 @@ static int mic_smpt_show(struct seq_file *s, void *pos)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mic_smpt_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mic_smpt_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int mic_smpt_debug_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations smpt_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = mic_smpt_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = mic_smpt_debug_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(mic_smpt);
|
||||
|
||||
static int mic_post_code_show(struct seq_file *s, void *pos)
|
||||
{
|
||||
|
@ -81,23 +65,7 @@ static int mic_post_code_show(struct seq_file *s, void *pos)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mic_post_code_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mic_post_code_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int mic_post_code_debug_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations post_code_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = mic_post_code_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = mic_post_code_debug_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(mic_post_code);
|
||||
|
||||
static int mic_msi_irq_info_show(struct seq_file *s, void *pos)
|
||||
{
|
||||
|
@ -143,24 +111,7 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mic_msi_irq_info_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int
|
||||
mic_msi_irq_info_debug_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations msi_irq_info_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = mic_msi_irq_info_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = mic_msi_irq_info_debug_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(mic_msi_irq_info);
|
||||
|
||||
/**
|
||||
* mic_create_debug_dir - Initialize MIC debugfs entries.
|
||||
|
@ -177,13 +128,14 @@ void mic_create_debug_dir(struct mic_device *mdev)
|
|||
if (!mdev->dbg_dir)
|
||||
return;
|
||||
|
||||
debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops);
|
||||
debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev,
|
||||
&mic_smpt_fops);
|
||||
|
||||
debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev,
|
||||
&post_code_ops);
|
||||
&mic_post_code_fops);
|
||||
|
||||
debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev,
|
||||
&msi_irq_info_ops);
|
||||
&mic_msi_irq_info_fops);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
/* Debugfs parent dir */
|
||||
static struct dentry *scif_dbg;
|
||||
|
||||
static int scif_dev_test(struct seq_file *s, void *unused)
|
||||
static int scif_dev_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int node;
|
||||
|
||||
|
@ -44,23 +44,7 @@ static int scif_dev_test(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int scif_dev_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, scif_dev_test, inode->i_private);
|
||||
}
|
||||
|
||||
static int scif_dev_test_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations scif_dev_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = scif_dev_test_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = scif_dev_test_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(scif_dev);
|
||||
|
||||
static void scif_display_window(struct scif_window *window, struct seq_file *s)
|
||||
{
|
||||
|
@ -104,7 +88,7 @@ static void scif_display_all_windows(struct list_head *head, struct seq_file *s)
|
|||
}
|
||||
}
|
||||
|
||||
static int scif_rma_test(struct seq_file *s, void *unused)
|
||||
static int scif_rma_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct scif_endpt *ep;
|
||||
struct list_head *pos;
|
||||
|
@ -123,23 +107,7 @@ static int scif_rma_test(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int scif_rma_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, scif_rma_test, inode->i_private);
|
||||
}
|
||||
|
||||
static int scif_rma_test_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations scif_rma_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = scif_rma_test_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = scif_rma_test_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(scif_rma);
|
||||
|
||||
void __init scif_init_debugfs(void)
|
||||
{
|
||||
|
@ -150,8 +118,8 @@ void __init scif_init_debugfs(void)
|
|||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("scif_dev", 0444, scif_dbg, NULL, &scif_dev_ops);
|
||||
debugfs_create_file("scif_rma", 0444, scif_dbg, NULL, &scif_rma_ops);
|
||||
debugfs_create_file("scif_dev", 0444, scif_dbg, NULL, &scif_dev_fops);
|
||||
debugfs_create_file("scif_rma", 0444, scif_dbg, NULL, &scif_rma_fops);
|
||||
debugfs_create_u8("en_msg_log", 0666, scif_dbg, &scif_info.en_msg_log);
|
||||
debugfs_create_u8("p2p_enable", 0666, scif_dbg, &scif_info.p2p_enable);
|
||||
}
|
||||
|
|
|
@ -195,10 +195,11 @@ static inline void *scif_get_local_va(off_t off, struct scif_window *window)
|
|||
|
||||
static void scif_prog_signal_cb(void *arg)
|
||||
{
|
||||
struct scif_status *status = arg;
|
||||
struct scif_cb_arg *cb_arg = arg;
|
||||
|
||||
dma_pool_free(status->ep->remote_dev->signal_pool, status,
|
||||
status->src_dma_addr);
|
||||
dma_pool_free(cb_arg->ep->remote_dev->signal_pool, cb_arg->status,
|
||||
cb_arg->src_dma_addr);
|
||||
kfree(cb_arg);
|
||||
}
|
||||
|
||||
static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val)
|
||||
|
@ -209,6 +210,7 @@ static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val)
|
|||
bool x100 = !is_dma_copy_aligned(chan->device, 1, 1, 1);
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct scif_status *status = NULL;
|
||||
struct scif_cb_arg *cb_arg = NULL;
|
||||
dma_addr_t src;
|
||||
dma_cookie_t cookie;
|
||||
int err;
|
||||
|
@ -257,8 +259,16 @@ static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val)
|
|||
goto dma_fail;
|
||||
}
|
||||
if (!x100) {
|
||||
cb_arg = kmalloc(sizeof(*cb_arg), GFP_KERNEL);
|
||||
if (!cb_arg) {
|
||||
err = -ENOMEM;
|
||||
goto dma_fail;
|
||||
}
|
||||
cb_arg->src_dma_addr = src;
|
||||
cb_arg->status = status;
|
||||
cb_arg->ep = ep;
|
||||
tx->callback = scif_prog_signal_cb;
|
||||
tx->callback_param = status;
|
||||
tx->callback_param = cb_arg;
|
||||
}
|
||||
cookie = tx->tx_submit(tx);
|
||||
if (dma_submit_error(cookie)) {
|
||||
|
@ -270,9 +280,11 @@ static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val)
|
|||
dma_async_issue_pending(chan);
|
||||
return 0;
|
||||
dma_fail:
|
||||
if (!x100)
|
||||
if (!x100) {
|
||||
dma_pool_free(ep->remote_dev->signal_pool, status,
|
||||
src - offsetof(struct scif_status, val));
|
||||
kfree(cb_arg);
|
||||
}
|
||||
alloc_fail:
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -205,6 +205,19 @@ struct scif_status {
|
|||
struct scif_endpt *ep;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct scif_cb_arg - Stores the argument of the callback func
|
||||
*
|
||||
* @src_dma_addr: Source buffer DMA address
|
||||
* @status: DMA status
|
||||
* @ep: SCIF endpoint
|
||||
*/
|
||||
struct scif_cb_arg {
|
||||
dma_addr_t src_dma_addr;
|
||||
struct scif_status *status;
|
||||
struct scif_endpt *ep;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct scif_window - Registration Window for Self and Remote
|
||||
*
|
||||
|
|
|
@ -101,23 +101,7 @@ static int vop_dp_show(struct seq_file *s, void *pos)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vop_dp_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, vop_dp_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int vop_dp_debug_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations dp_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vop_dp_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = vop_dp_debug_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(vop_dp);
|
||||
|
||||
static int vop_vdev_info_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
|
@ -194,23 +178,7 @@ static int vop_vdev_info_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vop_vdev_info_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, vop_vdev_info_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int vop_vdev_info_debug_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_release(inode, file);
|
||||
}
|
||||
|
||||
static const struct file_operations vdev_info_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vop_vdev_info_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = vop_vdev_info_debug_release
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(vop_vdev_info);
|
||||
|
||||
void vop_init_debugfs(struct vop_info *vi)
|
||||
{
|
||||
|
@ -222,8 +190,8 @@ void vop_init_debugfs(struct vop_info *vi)
|
|||
pr_err("can't create debugfs dir vop\n");
|
||||
return;
|
||||
}
|
||||
debugfs_create_file("dp", 0444, vi->dbg, vi, &dp_ops);
|
||||
debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vdev_info_ops);
|
||||
debugfs_create_file("dp", 0444, vi->dbg, vi, &vop_dp_fops);
|
||||
debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vop_vdev_info_fops);
|
||||
}
|
||||
|
||||
void vop_exit_debugfs(struct vop_info *vi)
|
||||
|
|
192
drivers/misc/pvpanic.c
Normal file
192
drivers/misc/pvpanic.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Pvpanic Device Support
|
||||
*
|
||||
* Copyright (C) 2013 Fujitsu.
|
||||
* Copyright (C) 2018 ZTE.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
static void __iomem *base;
|
||||
|
||||
#define PVPANIC_PANICKED (1 << 0)
|
||||
|
||||
MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
|
||||
MODULE_DESCRIPTION("pvpanic device driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static void
|
||||
pvpanic_send_event(unsigned int event)
|
||||
{
|
||||
iowrite8(event, base);
|
||||
}
|
||||
|
||||
static int
|
||||
pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
|
||||
void *unused)
|
||||
{
|
||||
pvpanic_send_event(PVPANIC_PANICKED);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block pvpanic_panic_nb = {
|
||||
.notifier_call = pvpanic_panic_notify,
|
||||
.priority = 1, /* let this called before broken drm_fb_helper */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static int pvpanic_add(struct acpi_device *device);
|
||||
static int pvpanic_remove(struct acpi_device *device);
|
||||
|
||||
static const struct acpi_device_id pvpanic_device_ids[] = {
|
||||
{ "QEMU0001", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
|
||||
|
||||
static struct acpi_driver pvpanic_driver = {
|
||||
.name = "pvpanic",
|
||||
.class = "QEMU",
|
||||
.ids = pvpanic_device_ids,
|
||||
.ops = {
|
||||
.add = pvpanic_add,
|
||||
.remove = pvpanic_remove,
|
||||
},
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static acpi_status
|
||||
pvpanic_walk_resources(struct acpi_resource *res, void *context)
|
||||
{
|
||||
struct resource r;
|
||||
|
||||
if (acpi_dev_resource_io(res, &r)) {
|
||||
base = ioport_map(r.start, resource_size(&r));
|
||||
return AE_OK;
|
||||
} else if (acpi_dev_resource_memory(res, &r)) {
|
||||
base = ioremap(r.start, resource_size(&r));
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
return AE_ERROR;
|
||||
}
|
||||
|
||||
static int pvpanic_add(struct acpi_device *device)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acpi_bus_get_status(device);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!device->status.enabled || !device->status.functional)
|
||||
return -ENODEV;
|
||||
|
||||
acpi_walk_resources(device->handle, METHOD_NAME__CRS,
|
||||
pvpanic_walk_resources, NULL);
|
||||
|
||||
if (!base)
|
||||
return -ENODEV;
|
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&pvpanic_panic_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvpanic_remove(struct acpi_device *device)
|
||||
{
|
||||
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&pvpanic_panic_nb);
|
||||
iounmap(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvpanic_register_acpi_driver(void)
|
||||
{
|
||||
return acpi_bus_register_driver(&pvpanic_driver);
|
||||
}
|
||||
|
||||
static void pvpanic_unregister_acpi_driver(void)
|
||||
{
|
||||
acpi_bus_unregister_driver(&pvpanic_driver);
|
||||
}
|
||||
#else
|
||||
static int pvpanic_register_acpi_driver(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void pvpanic_unregister_acpi_driver(void) {}
|
||||
#endif
|
||||
|
||||
static int pvpanic_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *mem;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem)
|
||||
return -EINVAL;
|
||||
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&pvpanic_panic_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvpanic_mmio_remove(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&pvpanic_panic_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pvpanic_mmio_match[] = {
|
||||
{ .compatible = "qemu,pvpanic-mmio", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver pvpanic_mmio_driver = {
|
||||
.driver = {
|
||||
.name = "pvpanic-mmio",
|
||||
.of_match_table = pvpanic_mmio_match,
|
||||
},
|
||||
.probe = pvpanic_mmio_probe,
|
||||
.remove = pvpanic_mmio_remove,
|
||||
};
|
||||
|
||||
static int __init pvpanic_mmio_init(void)
|
||||
{
|
||||
if (acpi_disabled)
|
||||
return platform_driver_register(&pvpanic_mmio_driver);
|
||||
else
|
||||
return pvpanic_register_acpi_driver();
|
||||
}
|
||||
|
||||
static void __exit pvpanic_mmio_exit(void)
|
||||
{
|
||||
if (acpi_disabled)
|
||||
platform_driver_unregister(&pvpanic_mmio_driver);
|
||||
else
|
||||
pvpanic_unregister_acpi_driver();
|
||||
}
|
||||
|
||||
module_init(pvpanic_mmio_init);
|
||||
module_exit(pvpanic_mmio_exit);
|
|
@ -211,7 +211,7 @@ static void kim_int_recv(struct kim_data_s *kim_gdata,
|
|||
static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
|
||||
{
|
||||
unsigned short version = 0, chip = 0, min_ver = 0, maj_ver = 0;
|
||||
const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 };
|
||||
static const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 };
|
||||
long timeout;
|
||||
|
||||
pr_debug("%s", __func__);
|
||||
|
@ -564,7 +564,7 @@ long st_kim_stop(void *kim_data)
|
|||
/* functions called from subsystems */
|
||||
/* called when debugfs entry is read from */
|
||||
|
||||
static int show_version(struct seq_file *s, void *unused)
|
||||
static int version_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct kim_data_s *kim_gdata = (struct kim_data_s *)s->private;
|
||||
seq_printf(s, "%04X %d.%d.%d\n", kim_gdata->version.full,
|
||||
|
@ -573,7 +573,7 @@ static int show_version(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int show_list(struct seq_file *s, void *unused)
|
||||
static int list_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct kim_data_s *kim_gdata = (struct kim_data_s *)s->private;
|
||||
kim_st_list_protocols(kim_gdata->core_data, s);
|
||||
|
@ -688,30 +688,8 @@ err:
|
|||
*core_data = NULL;
|
||||
}
|
||||
|
||||
static int kim_version_open(struct inode *i, struct file *f)
|
||||
{
|
||||
return single_open(f, show_version, i->i_private);
|
||||
}
|
||||
|
||||
static int kim_list_open(struct inode *i, struct file *f)
|
||||
{
|
||||
return single_open(f, show_list, i->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations version_debugfs_fops = {
|
||||
/* version info */
|
||||
.open = kim_version_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
static const struct file_operations list_debugfs_fops = {
|
||||
/* protocols info */
|
||||
.open = kim_list_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(version);
|
||||
DEFINE_SHOW_ATTRIBUTE(list);
|
||||
|
||||
/**********************************************************************/
|
||||
/* functions called from platform device driver subsystem
|
||||
|
@ -789,9 +767,9 @@ static int kim_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
debugfs_create_file("version", S_IRUGO, kim_debugfs_dir,
|
||||
kim_gdata, &version_debugfs_fops);
|
||||
kim_gdata, &version_fops);
|
||||
debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir,
|
||||
kim_gdata, &list_debugfs_fops);
|
||||
kim_gdata, &list_fops);
|
||||
return 0;
|
||||
|
||||
err_sysfs_group:
|
||||
|
|
|
@ -61,7 +61,7 @@ static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
|
|||
int tries;
|
||||
long timeout;
|
||||
|
||||
if (WARN_ON(index > func->num_templates))
|
||||
if (WARN_ON(index >= func->num_templates))
|
||||
return -EINVAL;
|
||||
|
||||
command = readl(syscfg->base + SYS_CFGCTRL);
|
||||
|
|
|
@ -1470,18 +1470,7 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vmballoon_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, vmballoon_debug_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations vmballoon_debug_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = vmballoon_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(vmballoon_debug);
|
||||
|
||||
static int __init vmballoon_debugfs_init(struct vmballoon *b)
|
||||
{
|
||||
|
|
|
@ -750,19 +750,10 @@ static int vmci_host_do_ctx_set_cpt_state(struct vmci_host_dev *vmci_host_dev,
|
|||
if (copy_from_user(&set_info, uptr, sizeof(set_info)))
|
||||
return -EFAULT;
|
||||
|
||||
cpt_buf = kmalloc(set_info.buf_size, GFP_KERNEL);
|
||||
if (!cpt_buf) {
|
||||
vmci_ioctl_err(
|
||||
"cannot allocate memory to set cpt state (type=%d)\n",
|
||||
set_info.cpt_type);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(cpt_buf, (void __user *)(uintptr_t)set_info.cpt_buf,
|
||||
set_info.buf_size)) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
cpt_buf = memdup_user((void __user *)(uintptr_t)set_info.cpt_buf,
|
||||
set_info.buf_size);
|
||||
if (IS_ERR(cpt_buf))
|
||||
return PTR_ERR(cpt_buf);
|
||||
|
||||
cid = vmci_ctx_get_id(vmci_host_dev->context);
|
||||
set_info.result = vmci_ctx_set_chkpt_state(cid, set_info.cpt_type,
|
||||
|
@ -770,7 +761,6 @@ static int vmci_host_do_ctx_set_cpt_state(struct vmci_host_dev *vmci_host_dev,
|
|||
|
||||
retval = copy_to_user(uptr, &set_info, sizeof(set_info)) ? -EFAULT : 0;
|
||||
|
||||
out:
|
||||
kfree(cpt_buf);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
menuconfig MTD
|
||||
tristate "Memory Technology Device (MTD) support"
|
||||
imply NVMEM
|
||||
help
|
||||
Memory Technology Devices are flash, RAM and similar chips, often
|
||||
used for solid state file systems on embedded devices. This option
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/reboot.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
@ -488,6 +489,50 @@ int mtd_pairing_groups(struct mtd_info *mtd)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_pairing_groups);
|
||||
|
||||
static int mtd_nvmem_reg_read(void *priv, unsigned int offset,
|
||||
void *val, size_t bytes)
|
||||
{
|
||||
struct mtd_info *mtd = priv;
|
||||
size_t retlen;
|
||||
int err;
|
||||
|
||||
err = mtd_read(mtd, offset, bytes, &retlen, val);
|
||||
if (err && err != -EUCLEAN)
|
||||
return err;
|
||||
|
||||
return retlen == bytes ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int mtd_nvmem_add(struct mtd_info *mtd)
|
||||
{
|
||||
struct nvmem_config config = {};
|
||||
|
||||
config.dev = &mtd->dev;
|
||||
config.name = mtd->name;
|
||||
config.owner = THIS_MODULE;
|
||||
config.reg_read = mtd_nvmem_reg_read;
|
||||
config.size = mtd->size;
|
||||
config.word_size = 1;
|
||||
config.stride = 1;
|
||||
config.read_only = true;
|
||||
config.root_only = true;
|
||||
config.no_of_node = true;
|
||||
config.priv = mtd;
|
||||
|
||||
mtd->nvmem = nvmem_register(&config);
|
||||
if (IS_ERR(mtd->nvmem)) {
|
||||
/* Just ignore if there is no NVMEM support in the kernel */
|
||||
if (PTR_ERR(mtd->nvmem) == -ENOSYS) {
|
||||
mtd->nvmem = NULL;
|
||||
} else {
|
||||
dev_err(&mtd->dev, "Failed to register NVMEM device\n");
|
||||
return PTR_ERR(mtd->nvmem);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dentry *dfs_dir_mtd;
|
||||
|
||||
/**
|
||||
|
@ -570,6 +615,11 @@ int add_mtd_device(struct mtd_info *mtd)
|
|||
if (error)
|
||||
goto fail_added;
|
||||
|
||||
/* Add the nvmem provider */
|
||||
error = mtd_nvmem_add(mtd);
|
||||
if (error)
|
||||
goto fail_nvmem_add;
|
||||
|
||||
if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
|
||||
mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
|
||||
if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
|
||||
|
@ -595,6 +645,8 @@ int add_mtd_device(struct mtd_info *mtd)
|
|||
__module_get(THIS_MODULE);
|
||||
return 0;
|
||||
|
||||
fail_nvmem_add:
|
||||
device_unregister(&mtd->dev);
|
||||
fail_added:
|
||||
of_node_put(mtd_get_of_node(mtd));
|
||||
idr_remove(&mtd_idr, i);
|
||||
|
@ -637,6 +689,10 @@ int del_mtd_device(struct mtd_info *mtd)
|
|||
mtd->index, mtd->name, mtd->usecount);
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
/* Try to remove the NVMEM provider */
|
||||
if (mtd->nvmem)
|
||||
nvmem_unregister(mtd->nvmem);
|
||||
|
||||
device_unregister(&mtd->dev);
|
||||
|
||||
idr_remove(&mtd_idr, mtd->index);
|
||||
|
|
|
@ -28,6 +28,7 @@ struct nvmem_device {
|
|||
size_t size;
|
||||
bool read_only;
|
||||
int flags;
|
||||
enum nvmem_type type;
|
||||
struct bin_attribute eeprom;
|
||||
struct device *base_dev;
|
||||
struct list_head cells;
|
||||
|
@ -60,6 +61,13 @@ static LIST_HEAD(nvmem_lookup_list);
|
|||
|
||||
static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
|
||||
|
||||
static const char * const nvmem_type_str[] = {
|
||||
[NVMEM_TYPE_UNKNOWN] = "Unknown",
|
||||
[NVMEM_TYPE_EEPROM] = "EEPROM",
|
||||
[NVMEM_TYPE_OTP] = "OTP",
|
||||
[NVMEM_TYPE_BATTERY_BACKED] = "Battery backed",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key eeprom_lock_key;
|
||||
#endif
|
||||
|
@ -83,6 +91,21 @@ static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nvmem_device *nvmem = to_nvmem_device(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(type);
|
||||
|
||||
static struct attribute *nvmem_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
|
@ -168,6 +191,7 @@ static struct bin_attribute *nvmem_bin_rw_attributes[] = {
|
|||
|
||||
static const struct attribute_group nvmem_bin_rw_group = {
|
||||
.bin_attrs = nvmem_bin_rw_attributes,
|
||||
.attrs = nvmem_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *nvmem_rw_dev_groups[] = {
|
||||
|
@ -191,6 +215,7 @@ static struct bin_attribute *nvmem_bin_ro_attributes[] = {
|
|||
|
||||
static const struct attribute_group nvmem_bin_ro_group = {
|
||||
.bin_attrs = nvmem_bin_ro_attributes,
|
||||
.attrs = nvmem_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *nvmem_ro_dev_groups[] = {
|
||||
|
@ -215,6 +240,7 @@ static struct bin_attribute *nvmem_bin_rw_root_attributes[] = {
|
|||
|
||||
static const struct attribute_group nvmem_bin_rw_root_group = {
|
||||
.bin_attrs = nvmem_bin_rw_root_attributes,
|
||||
.attrs = nvmem_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *nvmem_rw_root_dev_groups[] = {
|
||||
|
@ -238,6 +264,7 @@ static struct bin_attribute *nvmem_bin_ro_root_attributes[] = {
|
|||
|
||||
static const struct attribute_group nvmem_bin_ro_root_group = {
|
||||
.bin_attrs = nvmem_bin_ro_root_attributes,
|
||||
.attrs = nvmem_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *nvmem_ro_root_dev_groups[] = {
|
||||
|
@ -605,8 +632,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
|
|||
nvmem->dev.bus = &nvmem_bus_type;
|
||||
nvmem->dev.parent = config->dev;
|
||||
nvmem->priv = config->priv;
|
||||
nvmem->type = config->type;
|
||||
nvmem->reg_read = config->reg_read;
|
||||
nvmem->reg_write = config->reg_write;
|
||||
if (!config->no_of_node)
|
||||
nvmem->dev.of_node = config->dev->of_node;
|
||||
|
||||
if (config->id == -1 && config->name) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -46,10 +47,36 @@ static int meson_efuse_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct nvmem_device *nvmem;
|
||||
struct nvmem_config *econfig;
|
||||
struct clk *clk;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0)
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get efuse gate");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable gate");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev,
|
||||
(void(*)(void *))clk_disable_unprepare,
|
||||
clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add disable callback");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) {
|
||||
dev_err(dev, "failed to get max user");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
|
||||
if (!econfig)
|
||||
|
|
|
@ -1667,7 +1667,7 @@ static int parport_ECP_supported(struct parport *pb)
|
|||
default:
|
||||
printk(KERN_WARNING "0x%lx: Unknown implementation ID\n",
|
||||
pb->base);
|
||||
/* Assume 1 */
|
||||
/* Fall through - Assume 1 */
|
||||
case 1:
|
||||
pword = 1;
|
||||
}
|
||||
|
|
|
@ -789,6 +789,24 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
|
|||
ACPI_FREE(obj);
|
||||
}
|
||||
|
||||
static void pci_acpi_set_untrusted(struct pci_dev *dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
|
||||
return;
|
||||
if (device_property_read_u8(&dev->dev, "ExternalFacingPort", &val))
|
||||
return;
|
||||
|
||||
/*
|
||||
* These root ports expose PCIe (including DMA) outside of the
|
||||
* system so make sure we treat them and everything behind as
|
||||
* untrusted.
|
||||
*/
|
||||
if (val)
|
||||
dev->untrusted = 1;
|
||||
}
|
||||
|
||||
static void pci_acpi_setup(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
@ -798,6 +816,7 @@ static void pci_acpi_setup(struct device *dev)
|
|||
return;
|
||||
|
||||
pci_acpi_optimize_delay(pci_dev, adev->handle);
|
||||
pci_acpi_set_untrusted(pci_dev);
|
||||
|
||||
pci_acpi_add_pm_notifier(adev, pci_dev);
|
||||
if (!adev->wakeup.flags.valid)
|
||||
|
|
|
@ -1378,6 +1378,19 @@ static void set_pcie_thunderbolt(struct pci_dev *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static void set_pcie_untrusted(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *parent;
|
||||
|
||||
/*
|
||||
* If the upstream bridge is untrusted we treat this device
|
||||
* untrusted as well.
|
||||
*/
|
||||
parent = pci_upstream_bridge(dev);
|
||||
if (parent && parent->untrusted)
|
||||
dev->untrusted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_ext_cfg_is_aliased - Is ext config space just an alias of std config?
|
||||
* @dev: PCI device
|
||||
|
@ -1638,6 +1651,8 @@ int pci_setup_device(struct pci_dev *dev)
|
|||
/* Need to have dev->cfg_size ready */
|
||||
set_pcie_thunderbolt(dev);
|
||||
|
||||
set_pcie_untrusted(dev);
|
||||
|
||||
/* "Unknown power state" */
|
||||
dev->current_state = PCI_UNKNOWN;
|
||||
|
||||
|
|
|
@ -1172,14 +1172,6 @@ config INTEL_SMARTCONNECT
|
|||
This driver checks to determine whether the device has Intel Smart
|
||||
Connect enabled, and if so disables it.
|
||||
|
||||
config PVPANIC
|
||||
tristate "pvpanic device support"
|
||||
depends on ACPI
|
||||
---help---
|
||||
This driver provides support for the pvpanic device. pvpanic is
|
||||
a paravirtualized device provided by QEMU; it lets a virtual machine
|
||||
(guest) communicate panic events to the host.
|
||||
|
||||
config INTEL_PMC_IPC
|
||||
tristate "Intel PMC IPC Driver"
|
||||
depends on ACPI
|
||||
|
|
|
@ -79,7 +79,6 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
|
|||
obj-$(CONFIG_INTEL_RST) += intel-rst.o
|
||||
obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
|
||||
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
|
||||
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* pvpanic.c - pvpanic Device Support
|
||||
*
|
||||
* Copyright (C) 2013 Fujitsu.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
|
||||
MODULE_DESCRIPTION("pvpanic device driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int pvpanic_add(struct acpi_device *device);
|
||||
static int pvpanic_remove(struct acpi_device *device);
|
||||
|
||||
static const struct acpi_device_id pvpanic_device_ids[] = {
|
||||
{ "QEMU0001", 0 },
|
||||
{ "", 0 },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
|
||||
|
||||
#define PVPANIC_PANICKED (1 << 0)
|
||||
|
||||
static u16 port;
|
||||
|
||||
static struct acpi_driver pvpanic_driver = {
|
||||
.name = "pvpanic",
|
||||
.class = "QEMU",
|
||||
.ids = pvpanic_device_ids,
|
||||
.ops = {
|
||||
.add = pvpanic_add,
|
||||
.remove = pvpanic_remove,
|
||||
},
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void
|
||||
pvpanic_send_event(unsigned int event)
|
||||
{
|
||||
outb(event, port);
|
||||
}
|
||||
|
||||
static int
|
||||
pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
|
||||
void *unused)
|
||||
{
|
||||
pvpanic_send_event(PVPANIC_PANICKED);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block pvpanic_panic_nb = {
|
||||
.notifier_call = pvpanic_panic_notify,
|
||||
.priority = 1, /* let this called before broken drm_fb_helper */
|
||||
};
|
||||
|
||||
|
||||
static acpi_status
|
||||
pvpanic_walk_resources(struct acpi_resource *res, void *context)
|
||||
{
|
||||
switch (res->type) {
|
||||
case ACPI_RESOURCE_TYPE_END_TAG:
|
||||
return AE_OK;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_IO:
|
||||
port = res->data.io.minimum;
|
||||
return AE_OK;
|
||||
|
||||
default:
|
||||
return AE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int pvpanic_add(struct acpi_device *device)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acpi_bus_get_status(device);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!device->status.enabled || !device->status.functional)
|
||||
return -ENODEV;
|
||||
|
||||
acpi_walk_resources(device->handle, METHOD_NAME__CRS,
|
||||
pvpanic_walk_resources, NULL);
|
||||
|
||||
if (!port)
|
||||
return -ENODEV;
|
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&pvpanic_panic_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pvpanic_remove(struct acpi_device *device)
|
||||
{
|
||||
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&pvpanic_panic_nb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_acpi_driver(pvpanic_driver);
|
|
@ -158,10 +158,10 @@ static int pps_gpio_probe(struct platform_device *pdev)
|
|||
if (data->capture_clear)
|
||||
pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
|
||||
data->pps = pps_register_source(&data->info, pps_default_params);
|
||||
if (data->pps == NULL) {
|
||||
if (IS_ERR(data->pps)) {
|
||||
dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n",
|
||||
data->irq);
|
||||
return -EINVAL;
|
||||
return PTR_ERR(data->pps);
|
||||
}
|
||||
|
||||
/* register IRQ interrupt handler */
|
||||
|
|
|
@ -80,9 +80,9 @@ static int __init pps_ktimer_init(void)
|
|||
{
|
||||
pps = pps_register_source(&pps_ktimer_info,
|
||||
PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
|
||||
if (pps == NULL) {
|
||||
if (IS_ERR(pps)) {
|
||||
pr_err("cannot register PPS source\n");
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(pps);
|
||||
}
|
||||
|
||||
timer_setup(&ktimer, pps_ktimer_event, 0);
|
||||
|
|
|
@ -72,9 +72,9 @@ static int pps_tty_open(struct tty_struct *tty)
|
|||
|
||||
pps = pps_register_source(&info, PPS_CAPTUREBOTH | \
|
||||
PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
|
||||
if (pps == NULL) {
|
||||
if (IS_ERR(pps)) {
|
||||
pr_err("cannot register PPS source \"%s\"\n", info.path);
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(pps);
|
||||
}
|
||||
pps->lookup_cookie = tty;
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ static void parport_attach(struct parport *port)
|
|||
|
||||
device->pps = pps_register_source(&info,
|
||||
PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
|
||||
if (device->pps == NULL) {
|
||||
if (IS_ERR(device->pps)) {
|
||||
pr_err("couldn't register PPS source\n");
|
||||
goto err_release_dev;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,8 @@ static void pps_echo_client_default(struct pps_device *pps, int event,
|
|||
* source is described by info's fields and it will have, as default PPS
|
||||
* parameters, the ones specified into default_params.
|
||||
*
|
||||
* The function returns, in case of success, the PPS device. Otherwise NULL.
|
||||
* The function returns, in case of success, the PPS device. Otherwise
|
||||
* ERR_PTR(errno).
|
||||
*/
|
||||
|
||||
struct pps_device *pps_register_source(struct pps_source_info *info,
|
||||
|
@ -135,7 +136,7 @@ kfree_pps:
|
|||
pps_register_source_exit:
|
||||
pr_err("%s: unable to register source\n", info->name);
|
||||
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(pps_register_source);
|
||||
|
||||
|
|
|
@ -265,8 +265,8 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
|
|||
pps.mode = PTP_PPS_MODE;
|
||||
pps.owner = info->owner;
|
||||
ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
|
||||
if (!ptp->pps_source) {
|
||||
err = -EINVAL;
|
||||
if (IS_ERR(ptp->pps_source)) {
|
||||
err = PTR_ERR(ptp->pps_source);
|
||||
pr_err("failed to register pps source\n");
|
||||
goto no_pps;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ config SLIM_QCOM_CTRL
|
|||
|
||||
config SLIM_QCOM_NGD_CTRL
|
||||
tristate "Qualcomm SLIMbus Satellite Non-Generic Device Component"
|
||||
depends on QCOM_QMI_HELPERS
|
||||
depends on HAS_IOMEM && DMA_ENGINE
|
||||
depends on HAS_IOMEM && DMA_ENGINE && NET
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select QCOM_QMI_HELPERS
|
||||
help
|
||||
Select driver if Qualcomm's SLIMbus Satellite Non-Generic Device
|
||||
Component is programmed using Linux kernel.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue