Commit 18d0eae3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-4.20-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/misc patches for 4.20-rc1.

  Loads of things here, we have new code in all of these driver
  subsystems:
   - fpga
   - stm
   - extcon
   - nvmem
   - eeprom
   - hyper-v
   - gsmi
   - coresight
   - thunderbolt
   - vmw_balloon
   - goldfish
   - soundwire
  along with lots of fixes and minor changes to other small drivers.

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

* tag 'char-misc-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (245 commits)
  Documentation/security-bugs: Clarify treatment of embargoed information
  lib: Fix ia64 bootloader linkage
  MAINTAINERS: Clarify UIO vs UIOVEC maintainer
  docs/uio: fix a grammar nitpick
  docs: fpga: document programming fpgas using regions
  fpga: add devm_fpga_region_create
  fpga: bridge: add devm_fpga_bridge_create
  fpga: mgr: add devm_fpga_mgr_create
  hv_balloon: Replace spin_is_locked() with lockdep
  sgi-xp: Replace spin_is_locked() with lockdep
  eeprom: New ee1004 driver for DDR4 memory
  eeprom: at25: remove unneeded 'at25_remove'
  w1: IAD Register is yet readable trough iad sys file. Fix snprintf (%u for unsigned, count for max size).
  misc: mic: scif: remove set but not used variables 'src_dma_addr, dst_dma_addr'
  misc: mic: fix a DMA pool free failure
  platform: goldfish: pipe: Add a blank line to separate varibles and code
  platform: goldfish: pipe: Remove redundant casting
  platform: goldfish: pipe: Call misc_deregister if init fails
  platform: goldfish: pipe: Move the file-scope goldfish_pipe_dev variable into the driver state
  platform: goldfish: pipe: Move the file-scope goldfish_pipe_miscdev variable into the driver state
  ...
parents 26873aca 14fdc2c5
What: /config/stp-policy/<device>:p_sys-t.<policy>/<node>/uuid
Date: June 2018
KernelVersion: 4.19
Description:
UUID source identifier string, RW.
Default value is randomly generated at the mkdir <node> time.
Data coming from trace sources that use this <node> will be
tagged with this UUID in the MIPI SyS-T packet stream, to
allow the decoder to discern between different sources
within the same master/channel range, and identify the
higher level decoders that may be needed for each source.
What: /config/stp-policy/<device>:p_sys-t.<policy>/<node>/do_len
Date: June 2018
KernelVersion: 4.19
Description:
Include payload length in the MIPI SyS-T header, boolean.
If enabled, the SyS-T protocol encoder will include payload
length in each packet's metadata. This is normally redundant
if the underlying transport protocol supports marking message
boundaries (which STP does), so this is off by default.
What: /config/stp-policy/<device>:p_sys-t.<policy>/<node>/ts_interval
Date: June 2018
KernelVersion: 4.19
Description:
Time interval in milliseconds. Include a timestamp in the
MIPI SyS-T packet metadata, if this many milliseconds have
passed since the previous packet from this source. Zero is
the default and stands for "never send the timestamp".
What: /config/stp-policy/<device>:p_sys-t.<policy>/<node>/clocksync_interval
Date: June 2018
KernelVersion: 4.19
Description:
Time interval in milliseconds. Send a CLOCKSYNC packet if
this many milliseconds have passed since the previous
CLOCKSYNC packet from this source. Zero is the default and
stands for "never send the CLOCKSYNC". It makes sense to
use this option with sources that generate constant and/or
periodic data, like stm_heartbeat.
What: /sys/bus/vmbus/devices/.../driver_override
Date: August 2019
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description:
This file allows the driver for a device to be specified which
will override standard static and dynamic ID matching. When
specified, only a driver with a name matching the value written
to driver_override will have an opportunity to bind to the
device. The override is specified by writing a string to the
driver_override file (echo uio_hv_generic > driver_override) and
may be cleared with an empty string (echo > driver_override).
This returns the device to standard matching rules binding.
Writing to driver_override does not automatically unbind the
device from its current driver or make any attempt to
automatically load the specified driver. If no driver with a
matching name is currently loaded in the kernel, the device
will not bind to any driver. This also allows devices to
opt-out of driver binding using a driver_override name such as
"none". Only a single driver may be specified in the override,
there is no support for parsing delimiters.
...@@ -26,23 +26,34 @@ information is helpful. Any exploit code is very helpful and will not ...@@ -26,23 +26,34 @@ information is helpful. Any exploit code is very helpful and will not
be released without consent from the reporter unless it has already been be released without consent from the reporter unless it has already been
made public. made public.
Disclosure Disclosure and embargoed information
---------- ------------------------------------
The goal of the Linux kernel security team is to work with the bug The security list is not a disclosure channel. For that, see Coordination
submitter to understand and fix the bug. We prefer to publish the fix as below.
soon as possible, but try to avoid public discussion of the bug itself
and leave that to others. Once a robust fix has been developed, our preference is to release the
fix in a timely fashion, treating it no differently than any of the other
Publishing the fix may be delayed when the bug or the fix is not yet thousands of changes and fixes the Linux kernel project releases every
fully understood, the solution is not well-tested or for vendor month.
coordination. However, we expect these delays to be short, measurable in
days, not weeks or months. A release date is negotiated by the security However, at the request of the reporter, we will postpone releasing the
team working with the bug submitter as well as vendors. However, the fix for up to 5 business days after the date of the report or after the
kernel security team holds the final say when setting a timeframe. The embargo has lifted; whichever comes first. The only exception to that
timeframe varies from immediate (esp. if it's already publicly known bug) rule is if the bug is publicly known, in which case the preference is to
to a few weeks. As a basic default policy, we expect report date to release the fix as soon as it's available.
release date to be on the order of 7 days.
Whilst embargoed information may be shared with trusted individuals in
order to develop a fix, such information will not be published alongside
the fix or on any other disclosure channel without the permission of the
reporter. This includes but is not limited to the original bug report
and followup discussions (if any), exploits, CVE information or the
identity of the reporter.
In other words our only interest is in getting bugs fixed. All other
information submitted to the security list and any followup discussions
of the report are treated confidentially even after the embargo has been
lifted, in perpetuity.
Coordination Coordination
------------ ------------
...@@ -68,7 +79,7 @@ may delay the bug handling. If a reporter wishes to have a CVE identifier ...@@ -68,7 +79,7 @@ may delay the bug handling. If a reporter wishes to have a CVE identifier
assigned ahead of public disclosure, they will need to contact the private assigned ahead of public disclosure, they will need to contact the private
linux-distros list, described above. When such a CVE identifier is known linux-distros list, described above. When such a CVE identifier is known
before a patch is provided, it is desirable to mention it in the commit before a patch is provided, it is desirable to mention it in the commit
message, though. message if the reporter agrees.
Non-disclosure agreements Non-disclosure agreements
------------------------- -------------------------
......
...@@ -54,9 +54,7 @@ its hardware characteristcs. ...@@ -54,9 +54,7 @@ its hardware characteristcs.
clocks the core of that coresight component. The latter clock clocks the core of that coresight component. The latter clock
is optional. is optional.
* port or ports: The representation of the component's port * port or ports: see "Graph bindings for Coresight" below.
layout using the generic DT graph presentation found in
"bindings/graph.txt".
* Additional required properties for System Trace Macrocells (STM): * Additional required properties for System Trace Macrocells (STM):
* reg: along with the physical base address and length of the register * reg: along with the physical base address and length of the register
...@@ -73,7 +71,7 @@ its hardware characteristcs. ...@@ -73,7 +71,7 @@ its hardware characteristcs.
AMBA markee): AMBA markee):
- "arm,coresight-replicator" - "arm,coresight-replicator"
* port or ports: same as above. * port or ports: see "Graph bindings for Coresight" below.
* Optional properties for ETM/PTMs: * Optional properties for ETM/PTMs:
...@@ -96,6 +94,20 @@ its hardware characteristcs. ...@@ -96,6 +94,20 @@ its hardware characteristcs.
* interrupts : Exactly one SPI may be listed for reporting the address * interrupts : Exactly one SPI may be listed for reporting the address
error error
Graph bindings for Coresight
-------------------------------
Coresight components are interconnected to create a data path for the flow of
trace data generated from the "sources" to their collection points "sink".
Each coresight component must describe the "input" and "output" connections.
The connections must be described via generic DT graph bindings as described
by the "bindings/graph.txt", where each "port" along with an "endpoint"
component represents a hardware port and the connection.
* All output ports must be listed inside a child node named "out-ports"
* All input ports must be listed inside a child node named "in-ports".
* Port address must match the hardware port number.
Example: Example:
1. Sinks 1. Sinks
...@@ -105,10 +117,11 @@ Example: ...@@ -105,10 +117,11 @@ Example:
clocks = <&oscclk6a>; clocks = <&oscclk6a>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
port { in-ports {
etb_in_port: endpoint@0 { port {
slave-mode; etb_in_port: endpoint@0 {
remote-endpoint = <&replicator_out_port0>; remote-endpoint = <&replicator_out_port0>;
};
}; };
}; };
}; };
...@@ -119,10 +132,11 @@ Example: ...@@ -119,10 +132,11 @@ Example:
clocks = <&oscclk6a>; clocks = <&oscclk6a>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
port { in-ports {
tpiu_in_port: endpoint@0 { port {
slave-mode; tpiu_in_port: endpoint@0 {
remote-endpoint = <&replicator_out_port1>; remote-endpoint = <&replicator_out_port1>;
};
}; };
}; };
}; };
...@@ -133,22 +147,16 @@ Example: ...@@ -133,22 +147,16 @@ Example:
clocks = <&oscclk6a>; clocks = <&oscclk6a>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
ports { in-ports {
#address-cells = <1>; port {
#size-cells = <0>;
/* input port */
port@0 {
reg = <0>;
etr_in_port: endpoint { etr_in_port: endpoint {
slave-mode;
remote-endpoint = <&replicator2_out_port0>; remote-endpoint = <&replicator2_out_port0>;
}; };
}; };
};
/* CATU link represented by output port */ out-ports {
port@1 { port {
reg = <1>;
etr_out_port: endpoint { etr_out_port: endpoint {
remote-endpoint = <&catu_in_port>; remote-endpoint = <&catu_in_port>;
}; };
...@@ -163,7 +171,7 @@ Example: ...@@ -163,7 +171,7 @@ Example:
*/ */
compatible = "arm,coresight-replicator"; compatible = "arm,coresight-replicator";
ports { out-ports {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
...@@ -181,12 +189,11 @@ Example: ...@@ -181,12 +189,11 @@ Example:
remote-endpoint = <&tpiu_in_port>; remote-endpoint = <&tpiu_in_port>;
}; };
}; };
};
/* replicator input port */ in-ports {
port@2 { port {
reg = <0>;
replicator_in_port0: endpoint { replicator_in_port0: endpoint {
slave-mode;
remote-endpoint = <&funnel_out_port0>; remote-endpoint = <&funnel_out_port0>;
}; };
}; };
...@@ -199,40 +206,36 @@ Example: ...@@ -199,40 +206,36 @@ Example:
clocks = <&oscclk6a>; clocks = <&oscclk6a>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
ports { out-ports {
#address-cells = <1>; port {
#size-cells = <0>;
/* funnel output port */
port@0 {
reg = <0>;
funnel_out_port0: endpoint { funnel_out_port0: endpoint {
remote-endpoint = remote-endpoint =
<&replicator_in_port0>; <&replicator_in_port0>;
}; };
}; };
};
/* funnel input ports */ in-ports {
port@1 { #address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>; reg = <0>;
funnel_in_port0: endpoint { funnel_in_port0: endpoint {
slave-mode;
remote-endpoint = <&ptm0_out_port>; remote-endpoint = <&ptm0_out_port>;
}; };
}; };
port@2 { port@1 {
reg = <1>; reg = <1>;
funnel_in_port1: endpoint { funnel_in_port1: endpoint {
slave-mode;
remote-endpoint = <&ptm1_out_port>; remote-endpoint = <&ptm1_out_port>;
}; };
}; };
port@3 { port@2 {
reg = <2>; reg = <2>;
funnel_in_port2: endpoint { funnel_in_port2: endpoint {
slave-mode;
remote-endpoint = <&etm0_out_port>; remote-endpoint = <&etm0_out_port>;
}; };
}; };
...@@ -248,9 +251,11 @@ Example: ...@@ -248,9 +251,11 @@ Example:
cpu = <&cpu0>; cpu = <&cpu0>;
clocks = <&oscclk6a>; clocks = <&oscclk6a>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
port { out-ports {
ptm0_out_port: endpoint { port {
remote-endpoint = <&funnel_in_port0>; ptm0_out_port: endpoint {
remote-endpoint = <&funnel_in_port0>;
};
}; };
}; };
}; };
...@@ -262,9 +267,11 @@ Example: ...@@ -262,9 +267,11 @@ Example:
cpu = <&cpu1>; cpu = <&cpu1>;
clocks = <&oscclk6a>; clocks = <&oscclk6a>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
port { out-ports {
ptm1_out_port: endpoint { port {
remote-endpoint = <&funnel_in_port1>; ptm1_out_port: endpoint {
remote-endpoint = <&funnel_in_port1>;
};
}; };
}; };
}; };
...@@ -278,9 +285,11 @@ Example: ...@@ -278,9 +285,11 @@ Example:
clocks = <&soc_smc50mhz>; clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk"; clock-names = "apb_pclk";
port { out-ports {
stm_out_port: endpoint { port {
remote-endpoint = <&main_funnel_in_port2>; stm_out_port: endpoint {
remote-endpoint = <&main_funnel_in_port2>;
};
}; };
}; };
}; };
...@@ -295,10 +304,11 @@ Example: ...@@ -295,10 +304,11 @@ Example:
clock-names = "apb_pclk"; clock-names = "apb_pclk";
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
port { in-ports {
catu_in_port: endpoint { port {
slave-mode; catu_in_port: endpoint {
remote-endpoint = <&etr_out_port>; remote-endpoint = <&etr_out_port>;
};
}; };
}; };
}; };
......
...@@ -4,6 +4,12 @@ FPGA Bridge ...@@ -4,6 +4,12 @@ FPGA Bridge
API to implement a new FPGA bridge API to implement a new FPGA bridge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* struct :c:type:`fpga_bridge` — The FPGA Bridge structure
* struct :c:type:`fpga_bridge_ops` — Low level Bridge driver ops
* :c:func:`devm_fpga_bridge_create()` — Allocate and init a bridge struct
* :c:func:`fpga_bridge_register()` — Register a bridge
* :c:func:`fpga_bridge_unregister()` — Unregister a bridge
.. kernel-doc:: include/linux/fpga/fpga-bridge.h .. kernel-doc:: include/linux/fpga/fpga-bridge.h
:functions: fpga_bridge :functions: fpga_bridge
...@@ -11,39 +17,10 @@ API to implement a new FPGA bridge ...@@ -11,39 +17,10 @@ API to implement a new FPGA bridge
:functions: fpga_bridge_ops :functions: fpga_bridge_ops
.. kernel-doc:: drivers/fpga/fpga-bridge.c .. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_create :functions: devm_fpga_bridge_create
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_free
.. kernel-doc:: drivers/fpga/fpga-bridge.c .. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_register :functions: fpga_bridge_register
.. kernel-doc:: drivers/fpga/fpga-bridge.c .. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_unregister :functions: fpga_bridge_unregister
API to control an FPGA bridge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You probably won't need these directly. FPGA regions should handle this.
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: of_fpga_bridge_get
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_get
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_put
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_get_to_list
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: of_fpga_bridge_get_to_list
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_enable
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_disable
...@@ -49,18 +49,14 @@ probe function calls fpga_mgr_register(), such as:: ...@@ -49,18 +49,14 @@ probe function calls fpga_mgr_register(), such as::
* them in priv * them in priv
*/ */
mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
&socfpga_fpga_ops, priv); &socfpga_fpga_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, mgr); platform_set_drvdata(pdev, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int socfpga_fpga_remove(struct platform_device *pdev) static int socfpga_fpga_remove(struct platform_device *pdev)
...@@ -102,67 +98,19 @@ The ops include a .state function which will determine the state the FPGA is in ...@@ -102,67 +98,19 @@ The ops include a .state function which will determine the state the FPGA is in
and return a code of type enum fpga_mgr_states. It doesn't result in a change and return a code of type enum fpga_mgr_states. It doesn't result in a change
in state. in state.
How to write an image buffer to a supported FPGA
------------------------------------------------
Some sample code::
#include <linux/fpga/fpga-mgr.h>
struct fpga_manager *mgr;
struct fpga_image_info *info;
int ret;
/*
* Get a reference to FPGA manager. The manager is not locked, so you can
* hold onto this reference without it preventing programming.
*
* This example uses the device node of the manager. Alternatively, use
* fpga_mgr_get(dev) instead if you have the device.
*/
mgr = of_fpga_mgr_get(mgr_node);
/* struct with information about the FPGA image to program. */
info = fpga_image_info_alloc(dev);
/* flags indicates whether to do full or partial reconfiguration */
info->flags = FPGA_MGR_PARTIAL_RECONFIG;
/*
* At this point, indicate where the image is. This is pseudo-code; you're
* going to use one of these three.
*/
if (image is in a scatter gather table) {
info->sgt = [your scatter gather table]
} else if (image is in a buffer) {
info->buf = [your image buffer]
info->count = [image buffer size]
} else if (image is in a firmware file) {
info->firmware_name = devm_kstrdup(dev, firmware_name, GFP_KERNEL);
}
/* Get exclusive control of FPGA manager */
ret = fpga_mgr_lock(mgr);
/* Load the buffer to the FPGA */
ret = fpga_mgr_buf_load(mgr, &info, buf, count);
/* Release the FPGA manager */
fpga_mgr_unlock(mgr);
fpga_mgr_put(mgr);
/* Deallocate the image info if you're done with it */
fpga_image_info_free(info);
API for implementing a new FPGA Manager driver API for implementing a new FPGA Manager driver
---------------------------------------------- ----------------------------------------------
* ``fpga_mgr_states`` — Values for :c:member:`fpga_manager->state`.
* struct :c:type:`fpga_manager` — the FPGA manager struct
* struct :c:type:`fpga_manager_ops` — Low level FPGA manager driver ops
* :c:func:`devm_fpga_mgr_create` — Allocate and init a manager struct
* :c:func:`fpga_mgr_register` — Register an FPGA manager
* :c:func:`fpga_mgr_unregister` — Unregister an FPGA manager
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_mgr_states
.. kernel-doc:: include/linux/fpga/fpga-mgr.h .. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_manager :functions: fpga_manager
...@@ -170,56 +118,10 @@ API for implementing a new FPGA Manager driver ...@@ -170,56 +118,10 @@ API for implementing a new FPGA Manager driver
:functions: fpga_manager_ops :functions: fpga_manager_ops
.. kernel-doc:: drivers/fpga/fpga-mgr.c .. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_create :functions: devm_fpga_mgr_create
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_free
.. kernel-doc:: drivers/fpga/fpga-mgr.c .. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_register :functions: fpga_mgr_register
.. kernel-doc:: drivers/fpga/fpga-mgr.c .. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_unregister :functions: fpga_mgr_unregister
API for programming an FPGA
---------------------------
FPGA Manager flags
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:doc: FPGA Manager flags
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_image_info
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_mgr_states
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_image_info_alloc
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_image_info_free
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: of_fpga_mgr_get
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_get
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_put
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_lock
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_unlock
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_mgr_states
Note - use :c:func:`fpga_region_program_fpga()` instead of :c:func:`fpga_mgr_load()`
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_load
In-kernel API for FPGA Programming
==================================
Overview
--------
The in-kernel API for FPGA programming is a combination of APIs from
FPGA manager, bridge, and regions. The actual function used to
trigger FPGA programming is :c:func:`fpga_region_program_fpga()`.
:c:func:`fpga_region_program_fpga()` uses functionality supplied by
the FPGA manager and bridges. It will:
* lock the region's mutex
* lock the mutex of the region's FPGA manager
* build a list of FPGA bridges if a method has been specified to do so
* disable the bridges
* program the FPGA using info passed in :c:member:`fpga_region->info`.
* re-enable the bridges
* release the locks
The struct fpga_image_info specifies what FPGA image to program. It is
allocated/freed by :c:func:`fpga_image_info_alloc()` and freed with
:c:func:`fpga_image_info_free()`
How to program an FPGA using a region
-------------------------------------
When the FPGA region driver probed, it was given a pointer to an FPGA manager
driver so it knows which manager to use. The region also either has a list of
bridges to control during programming or it has a pointer to a function that
will generate that list. Here's some sample code of what to do next::
#include <linux/fpga/fpga-mgr.h>
#include <linux/fpga/fpga-region.h>
struct fpga_image_info *info;
int ret;
/*
* First, alloc the struct with information about the FPGA image to
* program.
*/
info = fpga_image_info_alloc(dev);
if (!info)
return -ENOMEM;
/* Set flags as needed, such as: */
info->flags = FPGA_MGR_PARTIAL_RECONFIG;
/*
* Indicate where the FPGA image is. This is pseudo-code; you're
* going to use one of these three.
*/
if (image is in a scatter gather table) {
info->sgt = [your scatter gather table]
} else if (image is in a buffer) {
info->buf = [your image buffer]
info->count = [image buffer size]
} else if (image is in a firmware file) {
info->firmware_name = devm_kstrdup(dev, firmware_name,
GFP_KERNEL);
}
/* Add info to region and do the programming */
region->info = info;
ret = fpga_region_program_fpga(region);
/* Deallocate the image info if you're done with it */
region->info = NULL;
fpga_image_info_free(info);
if (ret)
return ret;
/* Now enumerate whatever hardware has appeared in the FPGA. */
API for programming an FPGA
---------------------------
* :c:func:`fpga_region_program_fpga` — Program an FPGA
* :c:type:`fpga_image_info` — Specifies what FPGA image to program
* :c:func:`fpga_image_info_alloc()` — Allocate an FPGA image info struct
* :c:func:`fpga_image_info_free()` — Free an FPGA image info struct
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_program_fpga
FPGA Manager flags
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:doc: FPGA Manager flags
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_image_info
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_image_info_alloc
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_image_info_free
...@@ -34,41 +34,6 @@ fpga_image_info including: ...@@ -34,41 +34,6 @@ fpga_image_info including:
* flags indicating specifics such as whether the image is for partial * flags indicating specifics such as whether the image is for partial
reconfiguration. reconfiguration.
How to program an FPGA using a region
-------------------------------------
First, allocate the info struct::
info = fpga_image_info_alloc(dev);
if (!info)
return -ENOMEM;
Set flags as needed, i.e.::
info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
Point to your FPGA image, such as::
info->sgt = &sgt;
Add info to region and do the programming::
region->info = info;
ret = fpga_region_program_fpga(region);
:c:func:`fpga_region_program_fpga()` operates on info passed in the
fpga_image_info (region->info). This function will attempt to:
* lock the region's mutex
* lock the region's FPGA manager
* build a list of FPGA bridges if a method has been specified to do so
* disable the bridges
* program the FPGA
* re-enable the bridges
* release the locks
Then you will want to enumerate whatever hardware has appeared in the FPGA.
How to add a new FPGA region How to add a new FPGA region
---------------------------- ----------------------------
...@@ -77,26 +42,62 @@ An example of usage can be seen in the probe function of [#f2]_. ...@@ -77,26 +42,62 @@ An example of usage can be seen in the probe function of [#f2]_.
.. [#f1] ../devicetree/bindings/fpga/fpga-region.txt .. [#f1] ../devicetree/bindings/fpga/fpga-region.txt
.. [#f2] ../../drivers/fpga/of-fpga-region.c .. [#f2] ../../drivers/fpga/of-fpga-region.c
API to program an FPGA
----------------------
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_program_fpga
API to add a new FPGA region API to add a new FPGA region
---------------------------- ----------------------------
* struct :c:type:`fpga_region` — The FPGA region struct
* :c:func:`devm_fpga_region_create` — Allocate and init a region struct
* :c:func:`fpga_region_register` — Register an FPGA region
* :c:func:`fpga_region_unregister` — Unregister an FPGA region
The FPGA region's probe function will need to get a reference to the FPGA
Manager it will be using to do the programming. This usually would happen
during the region's probe function.
* :c:func:`fpga_mgr_get` — Get a reference to an FPGA manager, raise ref count
* :c:func:`of_fpga_mgr_get` — Get a reference to an FPGA manager, raise ref count,
given a device node.
* :c:func:`fpga_mgr_put` — Put an FPGA manager
The FPGA region will need to specify which bridges to control while programming
the FPGA. The region driver can build a list of bridges during probe time
(:c:member:`fpga_region->bridge_list`) or it can have a function that creates
the list of bridges to program just before programming
(:c:member:`fpga_region->get_bridges`). The FPGA bridge framework supplies the
following APIs to handle building or tearing down that list.
* :c:func:`fpga_bridge_get_to_list` — Get a ref of an FPGA bridge, add it to a
list
* :c:func:`of_fpga_bridge_get_to_list` — Get a ref of an FPGA bridge, add it to a
list, given a device node
* :c:func:`fpga_bridges_put` — Given a list of bridges, put them
.. kernel-doc:: include/linux/fpga/fpga-region.h .. kernel-doc:: include/linux/fpga/fpga-region.h
:functions: fpga_region :functions: fpga_region
.. kernel-doc:: drivers/fpga/fpga-region.c .. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_create :functions: devm_fpga_region_create
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_free
.. kernel-doc:: drivers/fpga/fpga-region.c .. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_register :functions: fpga_region_register
.. kernel-doc:: drivers/fpga/fpga-region.c .. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_unregister :functions: fpga_region_unregister
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_get
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: of_fpga_mgr_get
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_put
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_get_to_list
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: of_fpga_bridge_get_to_list
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridges_put
...@@ -11,3 +11,5 @@ FPGA Subsystem ...@@ -11,3 +11,5 @@ FPGA Subsystem
fpga-mgr fpga-mgr
fpga-bridge fpga-bridge
fpga-region fpga-region
fpga-programming
...@@ -44,7 +44,7 @@ FPGA Region ...@@ -44,7 +44,7 @@ FPGA Region
----------- -----------
If you are adding a new interface to the FPGA framework, add it on top If you are adding a new interface to the FPGA framework, add it on top
of an FPGA region to allow the most reuse of your interface. of an FPGA region.
The FPGA Region framework (fpga-region.c) associates managers and The FPGA Region framework (fpga-region.c) associates managers and
bridges as reconfigurable regions. A region may refer to the whole bridges as reconfigurable regions. A region may refer to the whole
......
...@@ -101,6 +101,34 @@ interface. :: ...@@ -101,6 +101,34 @@ interface. ::
+--------------------+ | | +--------------------+ | |
+----------------+ +----------------+
Example 5: Stereo Stream with L and R channel is rendered by 2 Masters, each
rendering one channel, and is received by two different Slaves, each
receiving one channel. Both Masters and both Slaves are using single port. ::
+---------------+ Clock Signal +---------------+
| Master +----------------------------------+ Slave |
| Interface | | Interface |
| 1 | | 1 |
| | Data Signal | |
| L +----------------------------------+ L |
| (Data) | Data Direction | (Data) |
+---------------+ +-----------------------> +---------------+
+---------------+ Clock Signal +---------------+
| Master +----------------------------------+ Slave |
| Interface | | Interface |
| 2 | | 2 |
| | Data Signal | |
| R +----------------------------------+ R |
| (Data) | Data Direction | (Data) |
+---------------+ +-----------------------> +---------------+
Note: In multi-link cases like above, to lock, one would acquire a global
lock and then go on locking bus instances. But, in this case the caller
framework(ASoC DPCM) guarantees that stream operations on a card are
always serialized. So, there is no race condition and hence no need for
global lock.
SoundWire Stream Management flow SoundWire Stream Management flow
================================ ================================
...@@ -174,6 +202,7 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to ...@@ -174,6 +202,7 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to
.startup() operation. .startup() operation.
.. code-block:: c .. code-block:: c
int sdw_alloc_stream(char * stream_name); int sdw_alloc_stream(char * stream_name);
...@@ -200,6 +229,7 @@ only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM ...@@ -200,6 +229,7 @@ only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM
framework, this stream state is linked to .hw_params() operation. framework, this stream state is linked to .hw_params() operation.
.. code-block:: c .. code-block:: c
int sdw_stream_add_master(struct sdw_bus * bus, int sdw_stream_add_master(struct sdw_bus * bus,
struct sdw_stream_config * stream_config, struct sdw_stream_config * stream_config,
struct sdw_ports_config * ports_config, struct sdw_ports_config * ports_config,
...@@ -245,6 +275,7 @@ stream. From ASoC DPCM framework, this stream state is linked to ...@@ -245,6 +275,7 @@ stream. From ASoC DPCM framework, this stream state is linked to
.prepare() operation. .prepare() operation.
.. code-block:: c .. code-block:: c
int sdw_prepare_stream(struct sdw_stream_runtime * stream); int sdw_prepare_stream(struct sdw_stream_runtime * stream);
...@@ -274,6 +305,7 @@ stream. From ASoC DPCM framework, this stream state is linked to ...@@ -274,6 +305,7 @@ stream. From ASoC DPCM framework, this stream state is linked to
.trigger() start operation. .trigger() start operation.
.. code-block:: c .. code-block:: c
int sdw_enable_stream(struct sdw_stream_runtime * stream); int sdw_enable_stream(struct sdw_stream_runtime * stream);
SDW_STREAM_DISABLED SDW_STREAM_DISABLED
...@@ -301,6 +333,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to ...@@ -301,6 +333,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to
.trigger() stop operation. .trigger() stop operation.
.. code-block:: c .. code-block:: c
int sdw_disable_stream(struct sdw_stream_runtime * stream); int sdw_disable_stream(struct sdw_stream_runtime * stream);
...@@ -325,6 +358,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to ...@@ -325,6 +358,7 @@ per stream. From ASoC DPCM framework, this stream state is linked to
.trigger() stop operation. .trigger() stop operation.
.. code-block:: c .. code-block:: c
int sdw_deprepare_stream(struct sdw_stream_runtime * stream); int sdw_deprepare_stream(struct sdw_stream_runtime * stream);
...@@ -349,6 +383,7 @@ all the Master(s) and Slave(s) associated with stream. From ASoC DPCM ...@@ -349,6 +383,7 @@ all the Master(s) and Slave(s) associated with stream. From ASoC DPCM
framework, this stream state is linked to .hw_free() operation. framework, this stream state is linked to .hw_free() operation.
.. code-block:: c .. code-block:: c
int sdw_stream_remove_master(struct sdw_bus * bus, int sdw_stream_remove_master(struct sdw_bus * bus,
struct sdw_stream_runtime * stream); struct sdw_stream_runtime * stream);
int sdw_stream_remove_slave(struct sdw_slave * slave, int sdw_stream_remove_slave(struct sdw_slave * slave,
...@@ -361,6 +396,7 @@ stream assigned as part of ALLOCATED state. ...@@ -361,6 +396,7 @@ stream assigned as part of ALLOCATED state.
In .shutdown() the data structure maintaining stream state are freed up. In .shutdown() the data structure maintaining stream state are freed up.
.. code-block:: c .. code-block:: c
void sdw_release_stream(struct sdw_stream_runtime * stream); void sdw_release_stream(struct sdw_stream_runtime * stream);
Not Supported Not Supported
......
...@@ -463,8 +463,8 @@ Getting information about your UIO device ...@@ -463,8 +463,8 @@ Getting information about your UIO device
Information about all UIO devices is available in sysfs. The first thing Information about all UIO devices is available in sysfs. The first thing
you should do in your driver is check ``name`` and ``version`` to make you should do in your driver is check ``name`` and ``version`` to make
sure your talking to the right device and that its kernel driver has the sure you're talking to the right device and that its kernel driver has
version you expect. the version you expect.
You should also make sure that the memory mapping you need exists and You should also make sure that the memory mapping you need exists and
has the size you expect. has the size you expect.
......
...@@ -58,6 +58,37 @@ static int qfprom_probe(struct platform_device *pdev) ...@@ -58,6 +58,37 @@ static int qfprom_probe(struct platform_device *pdev)
It is mandatory that the NVMEM provider has a regmap associated with its It is mandatory that the NVMEM provider has a regmap associated with its
struct device. Failure to do would return error code from nvmem_register(). struct device. Failure to do would return error code from nvmem_register().
Users of board files can define and register nvmem cells using the
nvmem_cell_table struct:
static struct nvmem_cell_info foo_nvmem_cells[] = {
{
.name = "macaddr",
.offset = 0x7f00,
.bytes = ETH_ALEN,
}
};
static struct nvmem_cell_table foo_nvmem_cell_table = {
.nvmem_name = "i2c-eeprom",
.cells = foo_nvmem_cells,
.ncells = ARRAY_SIZE(foo_nvmem_cells),
};
nvmem_add_cell_table(&foo_nvmem_cell_table);
Additionally it is possible to create nvmem cell lookup entries and register
them with the nvmem framework from machine code as shown in the example below:
static struct nvmem_cell_lookup foo_nvmem_lookup = {
.nvmem_name = "i2c-eeprom",
.cell_name = "macaddr",
.dev_id = "foo_mac.0",
.con_id = "mac-address",
};
nvmem_add_cell_lookups(&foo_nvmem_lookup, 1);
NVMEM Consumers NVMEM Consumers
+++++++++++++++ +++++++++++++++
......
.. SPDX-License-Identifier: GPL-2.0
=================== ===================
System Trace Module System Trace Module
=================== ===================
...@@ -53,12 +55,30 @@ under "user" directory from the example above and this new rule will ...@@ -53,12 +55,30 @@ under "user" directory from the example above and this new rule will
be used for trace sources with the id string of "user/dummy". be used for trace sources with the id string of "user/dummy".
Trace sources have to open the stm class device's node and write their Trace sources have to open the stm class device's node and write their
trace data into its file descriptor. In order to identify themselves trace data into its file descriptor.
to the policy, they need to do a STP_POLICY_ID_SET ioctl on this file
descriptor providing their id string. Otherwise, they will be In order to find an appropriate policy node for a given trace source,
automatically allocated a master/channel pair upon first write to this several mechanisms can be used. First, a trace source can explicitly
file descriptor according to the "default" rule of the policy, if such identify itself by calling an STP_POLICY_ID_SET ioctl on the character
exists. device's file descriptor, providing their id string, before they write
any data there. Secondly, if they chose not to perform the explicit
identification (because you may not want to patch existing software
to do this), they can just start writing the data, at which point the
stm core will try to find a policy node with the name matching the
task's name (e.g., "syslogd") and if one exists, it will be used.
Thirdly, if the task name can't be found among the policy nodes, the
catch-all entry "default" will be used, if it exists. This entry also
needs to be created and configured by the system administrator or
whatever tools are taking care of the policy configuration. Finally,
if all the above steps failed, the write() to an stm file descriptor
will return a error (EINVAL).
Previously, if no policy nodes were found for a trace source, the stm
class would silently fall back to allocating the first available
contiguous range of master/channels from the beginning of the device's
master/channel range. The new requirement for a policy node to exist
will help programmers and sysadmins identify gaps in configuration
and have better control over the un-identified sources.
Some STM devices may allow direct mapping of the channel mmio regions Some STM devices may allow direct mapping of the channel mmio regions
to userspace for zero-copy writing. One mappable page (in terms of to userspace for zero-copy writing. One mappable page (in terms of
...@@ -92,9 +112,9 @@ allocated for the device according to the policy configuration. If ...@@ -92,9 +112,9 @@ allocated for the device according to the policy configuration. If
there's a node in the root of the policy directory that matches the there's a node in the root of the policy directory that matches the
stm_source device's name (for example, "console"), this node will be stm_source device's name (for example, "console"), this node will be
used to allocate master and channel numbers. If there's no such policy used to allocate master and channel numbers. If there's no such policy
node, the stm core will pick the first contiguous chunk of channels node, the stm core will use the catch-all entry "default", if one
within the first available master. Note that the node must exist exists. If neither policy nodes exist, the write() to stm_source_link
before the stm_source device is connected to its stm device. will return an error.
stm_console stm_console
=========== ===========
......
.. SPDX-License-Identifier: GPL-2.0
===================
MIPI SyS-T over STP
===================
The MIPI SyS-T protocol driver can be used with STM class devices to
generate standardized trace stream. Aside from being a standard, it
provides better trace source identification and timestamp correlation.
In order to use the MIPI SyS-T protocol driver with your STM device,
first, you'll need CONFIG_STM_PROTO_SYS_T.
Now, you can select which protocol driver you want to use when you create
a policy for your STM device, by specifying it in the policy name:
# mkdir /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/
In other words, the policy name format is extended like this:
<device_name>:<protocol_name>.<policy_name>
With Intel TH, therefore it can look like "0-sth:p_sys-t.my-policy".
If the protocol name is omitted, the STM class will chose whichever
protocol driver was loaded first.
You can also double check that everything is working as expected by
# cat /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/protocol
p_sys-t
Now, with the MIPI SyS-T protocol driver, each policy node in the
configfs gets a few additional attributes, which determine per-source
parameters specific to the protocol:
# mkdir /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/default
# ls /config/stp-policy/dummy_stm.0:p_sys-t.my-policy/default
channels
clocksync_interval
do_len
masters
ts_interval
uuid
The most important one here is the "uuid", which determines the UUID
that will be used to tag all data coming from this source. It is
automatically generated when a new node is created, but it is likely
that you would want to change it.
do_len switches on/off the additional "payload length" field in the
MIPI SyS-T message header. It is off by default as the STP already
marks message boundaries.
ts_interval and clocksync_interval determine how much time in milliseconds
can pass before we need to include a protocol (not transport, aka STP)
timestamp in a message header or send a CLOCKSYNC packet, respectively.
See Documentation/ABI/testing/configfs-stp-policy-p_sys-t for more
details.
* [1] https://www.mipi.org/specifications/sys-t
...@@ -932,6 +932,7 @@ M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ...@@ -932,6 +932,7 @@ M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
M: Arve Hjønnevåg <arve@android.com> M: Arve Hjønnevåg <arve@android.com>
M: Todd Kjos <tkjos@android.com> M: Todd Kjos <tkjos@android.com>
M: Martijn Coenen <maco@android.com> M: Martijn Coenen <maco@android.com>
M: Joel Fernandes <joel@joelfernandes.org>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
L: devel@driverdev.osuosl.org L: devel@driverdev.osuosl.org
S: Supported S: Supported
...@@ -13757,7 +13758,7 @@ F: sound/soc/ ...@@ -13757,7 +13758,7 @@ F: sound/soc/
F: include/sound/soc* F: include/sound/soc*
SOUNDWIRE SUBSYSTEM SOUNDWIRE SUBSYSTEM
M: Vinod Koul <vinod.koul@intel.com> M: Vinod Koul <vkoul@kernel.org>
M: Sanyog Kale <sanyog.r.kale@intel.com> M: Sanyog Kale <sanyog.r.kale@intel.com>
R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> R: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
...@@ -15523,13 +15524,19 @@ F: arch/x86/um/ ...@@ -15523,13 +15524,19 @@ F: arch/x86/um/
F: fs/hostfs/ F: fs/hostfs/
F: fs/hppfs/ F: fs/hppfs/
USERSPACE COPYIN/COPYOUT (UIOVEC)
M: Alexander Viro <viro@zeniv.linux.org.uk>
S: Maintained
F: lib/iov_iter.c
F: include/linux/uio.h
USERSPACE I/O (UIO) USERSPACE I/O (UIO)
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
F: Documentation/driver-api/uio-howto.rst F: Documentation/driver-api/uio-howto.rst
F: drivers/uio/ F: drivers/uio/
F: include/linux/uio*.h F: include/linux/uio_driver.h
UTIL-LINUX PACKAGE UTIL-LINUX PACKAGE
M: Karel Zak <kzak@redhat.com> M: Karel Zak <kzak@redhat.com>
......
...@@ -10,7 +10,7 @@ if ANDROID ...@@ -10,7 +10,7 @@ if ANDROID
config ANDROID_BINDER_IPC config ANDROID_BINDER_IPC
bool "Android Binder IPC Driver" bool "Android Binder IPC Driver"
depends on MMU depends on MMU && !CPU_CACHE_VIVT
default n default n
---help--- ---help---
Binder is used in Android for both communication between processes, Binder is used in Android for both communication between processes,
......
This diff is collapsed.
...@@ -223,22 +223,40 @@ TRACE_EVENT(binder_transaction_ref_to_ref, ...@@ -223,22 +223,40 @@ TRACE_EVENT(binder_transaction_ref_to_ref,
__entry->dest_ref_debug_id, __entry->dest_ref_desc) __entry->dest_ref_debug_id, __entry->dest_ref_desc)
); );
TRACE_EVENT(binder_transaction_fd, TRACE_EVENT(binder_transaction_fd_send,
TP_PROTO(struct binder_transaction *t, int src_fd, int dest_fd), TP_PROTO(struct binder_transaction *t, int fd, size_t offset),
TP_ARGS(t, src_fd, dest_fd), TP_ARGS(t, fd, offset),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, debug_id) __field(int, debug_id)
__field(int, src_fd) __field(int, fd)
__field(int, dest_fd) __field(size_t, offset)
),
TP_fast_assign(
__entry->debug_id = t->debug_id;
__entry->fd = fd;
__entry->offset = offset;
),
TP_printk("transaction=%d src_fd=%d offset=%zu",
__entry->debug_id, __entry->fd, __entry->offset)
);
TRACE_EVENT(binder_transaction_fd_recv,
TP_PROTO(struct binder_transaction *t, int fd, size_t offset),
TP_ARGS(t, fd, offset),
TP_STRUCT__entry(
__field(int, debug_id)
__field(int, fd)
__field(size_t, offset)
), ),
TP_fast_assign( TP_fast_assign(
__entry->debug_id = t->debug_id; __entry->debug_id = t->debug_id;
__entry->src_fd = src_fd; __entry->fd = fd;
__entry->dest_fd = dest_fd; __entry->offset = offset;
), ),
TP_printk("transaction=%d src_fd=%d ==> dest_fd=%d", TP_printk("transaction=%d dest_fd=%d offset=%zu",
__entry->debug_id, __entry->src_fd, __entry->dest_fd) __entry->debug_id, __entry->fd, __entry->offset)
); );
DECLARE_EVENT_CLASS(binder_buffer_class, DECLARE_EVENT_CLASS(binder_buffer_class,
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC * Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
* *
* Based on various non upstream patches to support the CHT Whiskey Cove PMIC: * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
* Copyright (C) 2013-2015 Intel Corporation. All rights reserved. * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/ */
#include <linux/extcon-provider.h> #include <linux/extcon-provider.h>
...@@ -32,10 +24,10 @@ ...@@ -32,10 +24,10 @@
#define CHT_WC_CHGRCTRL0_EMRGCHREN BIT(1) #define CHT_WC_CHGRCTRL0_EMRGCHREN BIT(1)
#define CHT_WC_CHGRCTRL0_EXTCHRDIS BIT(2) #define CHT_WC_CHGRCTRL0_EXTCHRDIS BIT(2)
#define CHT_WC_CHGRCTRL0_SWCONTROL BIT(3) #define CHT_WC_CHGRCTRL0_SWCONTROL BIT(3)
#define CHT_WC_CHGRCTRL0_TTLCK_MASK BIT(4) #define CHT_WC_CHGRCTRL0_TTLCK BIT(4)
#define CHT_WC_CHGRCTRL0_CCSM_OFF_MASK BIT(5) #define CHT_WC_CHGRCTRL0_CCSM_OFF BIT(5)
#define CHT_WC_CHGRCTRL0_DBPOFF_MASK BIT(6) #define CHT_WC_CHGRCTRL0_DBPOFF BIT(6)
#define CHT_WC_CHGRCTRL0_WDT_NOKICK BIT(7) #define CHT_WC_CHGRCTRL0_CHR_WDT_NOKICK BIT(7)
#define CHT_WC_CHGRCTRL1 0x5e17 #define CHT_WC_CHGRCTRL1 0x5e17
...@@ -52,7 +44,7 @@ ...@@ -52,7 +44,7 @@
#define CHT_WC_USBSRC_TYPE_ACA 4 #define CHT_WC_USBSRC_TYPE_ACA 4
#define CHT_WC_USBSRC_TYPE_SE1 5 #define CHT_WC_USBSRC_TYPE_SE1 5
#define CHT_WC_USBSRC_TYPE_MHL 6 #define CHT_WC_USBSRC_TYPE_MHL 6
#define CHT_WC_USBSRC_TYPE_FLOAT_DP_DN 7 #define CHT_WC_USBSRC_TYPE_FLOATING 7
#define CHT_WC_USBSRC_TYPE_OTHER 8 #define CHT_WC_USBSRC_TYPE_OTHER 8
#define CHT_WC_USBSRC_TYPE_DCP_EXTPHY 9 #define CHT_WC_USBSRC_TYPE_DCP_EXTPHY 9
...@@ -61,9 +53,12 @@ ...@@ -61,9 +53,12 @@
#define CHT_WC_PWRSRC_STS 0x6e1e #define CHT_WC_PWRSRC_STS 0x6e1e
#define CHT_WC_PWRSRC_VBUS BIT(0) #define CHT_WC_PWRSRC_VBUS BIT(0)
#define CHT_WC_PWRSRC_DC BIT(1) #define CHT_WC_PWRSRC_DC BIT(1)
#define CHT_WC_PWRSRC_BAT BIT(2) #define CHT_WC_PWRSRC_BATT BIT(2)
#define CHT_WC_PWRSRC_ID_GND BIT(3) #define CHT_WC_PWRSRC_USBID_MASK GENMASK(4, 3)
#define CHT_WC_PWRSRC_ID_FLOAT BIT(4) #define CHT_WC_PWRSRC_USBID_SHIFT 3
#define CHT_WC_PWRSRC_RID_ACA 0
#define CHT_WC_PWRSRC_RID_GND 1
#define CHT_WC_PWRSRC_RID_FLOAT 2
#define CHT_WC_VBUS_GPIO_CTLO 0x6e2d #define CHT_WC_VBUS_GPIO_CTLO 0x6e2d
#define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0) #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0)
...@@ -104,16 +99,20 @@ struct cht_wc_extcon_data { ...@@ -104,16 +99,20 @@ struct cht_wc_extcon_data {
static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts) static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
{ {
if (pwrsrc_sts & CHT_WC_PWRSRC_ID_GND) switch ((pwrsrc_sts & CHT_WC_PWRSRC_USBID_MASK) >> CHT_WC_PWRSRC_USBID_SHIFT) {
case CHT_WC_PWRSRC_RID_GND:
return USB_ID_GND; return USB_ID_GND;
if (pwrsrc_sts & CHT_WC_PWRSRC_ID_FLOAT) case CHT_WC_PWRSRC_RID_FLOAT:
return USB_ID_FLOAT; return USB_ID_FLOAT;
case CHT_WC_PWRSRC_RID_ACA:
/* default:
* Once we have iio support for the gpadc we should read the USBID /*
* gpadc channel here and determine ACA role based on that. * Once we have IIO support for the GPADC we should read
*/ * the USBID GPADC channel here and determine ACA role
return USB_ID_FLOAT; * based on that.
*/
return USB_ID_FLOAT;
}
} }
static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext, static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
...@@ -156,9 +155,9 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext, ...@@ -156,9 +155,9 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
dev_warn(ext->dev, dev_warn(ext->dev,
"Unhandled charger type %d, defaulting to SDP\n", "Unhandled charger type %d, defaulting to SDP\n",
ret); ret);
/* Fall through, treat as SDP */ return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_SDP: case CHT_WC_USBSRC_TYPE_SDP:
case CHT_WC_USBSRC_TYPE_FLOAT_DP_DN: case CHT_WC_USBSRC_TYPE_FLOATING:
case CHT_WC_USBSRC_TYPE_OTHER: case CHT_WC_USBSRC_TYPE_OTHER:
return EXTCON_CHG_USB_SDP; return EXTCON_CHG_USB_SDP;
case CHT_WC_USBSRC_TYPE_CDP: case CHT_WC_USBSRC_TYPE_CDP:
...@@ -279,7 +278,7 @@ static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable) ...@@ -279,7 +278,7 @@ static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable)
{ {
int ret, mask, val; int ret, mask, val;
mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF_MASK; mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF;
val = enable ? mask : 0; val = enable ? mask : 0;
ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val); ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val);
if (ret) if (ret)
...@@ -292,6 +291,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) ...@@ -292,6 +291,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
{ {
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
struct cht_wc_extcon_data *ext; struct cht_wc_extcon_data *ext;
unsigned long mask = ~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_USBID_MASK);
int irq, ret; int irq, ret;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
...@@ -352,9 +352,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) ...@@ -352,9 +352,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
} }
/* Unmask irqs */ /* Unmask irqs */
ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, mask);
(int)~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_ID_GND |
CHT_WC_PWRSRC_ID_FLOAT));
if (ret) { if (ret) {
dev_err(ext->dev, "Error writing irq-mask: %d\n", ret); dev_err(ext->dev, "Error writing irq-mask: %d\n", ret);
goto disable_sw_control; goto disable_sw_control;
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Intel INT3496 ACPI device extcon driver * Intel INT3496 ACPI device extcon driver
* *
...@@ -7,15 +8,6 @@ ...@@ -7,15 +8,6 @@
* *
* Copyright (c) 2014, Intel Corporation. * Copyright (c) 2014, Intel Corporation.
* Author: David Cohen <david.a.cohen@linux.intel.com> * Author: David Cohen <david.a.cohen@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
...@@ -192,4 +184,4 @@ module_platform_driver(int3496_driver); ...@@ -192,4 +184,4 @@ module_platform_driver(int3496_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver"); MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
/* // SPDX-License-Identifier: GPL-2.0+
* extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC //
* // extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC
* Copyright (C) 2013,2014 Samsung Electronics //
* Chanwoo Choi <cw00.choi@samsung.com> // Copyright (C) 2013,2014 Samsung Electronics
* Krzysztof Kozlowski <krzk@kernel.org> // Chanwoo Choi <cw00.choi@samsung.com>
* // Krzysztof Kozlowski <krzk@kernel.org>
* 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.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
......
/* // SPDX-License-Identifier: GPL-2.0+
* extcon-max77693.c - MAX77693 extcon driver to support MAX77693 MUIC //
* // extcon-max77693.c - MAX77693 extcon driver to support MAX77693 MUIC
* Copyright (C) 2012 Samsung Electrnoics //
* Chanwoo Choi <cw00.choi@samsung.com> // Copyright (C) 2012 Samsung Electrnoics
* // Chanwoo Choi <cw00.choi@samsung.com>
* 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.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
......
/* // SPDX-License-Identifier: GPL-2.0+
* extcon-max77843.c - Maxim MAX77843 extcon driver to support //
* MUIC(Micro USB Interface Controller) // extcon-max77843.c - Maxim MAX77843 extcon driver to support
* // MUIC(Micro USB Interface Controller)
* Copyright (C) 2015 Samsung Electronics //
* Author: Jaewon Kim <jaewon02.kim@samsung.com> // Copyright (C) 2015 Samsung Electronics
* // Author: Jaewon Kim <jaewon02.kim@samsung.com>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/extcon-provider.h> #include <linux/extcon-provider.h>
#include <linux/i2c.h> #include <linux/i2c.h>
......
/* // SPDX-License-Identifier: GPL-2.0+
* extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC //
* // extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC
* Copyright (C) 2012 Samsung Electronics //
* Donggeun Kim <dg77.kim@samsung.com> // Copyright (C) 2012 Samsung Electronics
* // Donggeun Kim <dg77.kim@samsung.com>
* 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.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
......
...@@ -628,7 +628,7 @@ int extcon_get_property(struct extcon_dev *edev, unsigned int id, ...@@ -628,7 +628,7 @@ int extcon_get_property(struct extcon_dev *edev, unsigned int id,
unsigned long flags; unsigned long flags;
int index, ret = 0; int index, ret = 0;
*prop_val = (union extcon_property_value)(0); *prop_val = (union extcon_property_value){0};
if (!edev) if (!edev)
return -EINVAL; return -EINVAL;
...@@ -1123,7 +1123,6 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1123,7 +1123,6 @@ int extcon_dev_register(struct extcon_dev *edev)
(unsigned long)atomic_inc_return(&edev_no)); (unsigned long)atomic_inc_return(&edev_no));
if (edev->max_supported) { if (edev->max_supported) {
char buf[10];
char *str; char *str;
struct extcon_cable *cable; struct extcon_cable *cable;
...@@ -1137,9 +1136,7 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1137,9 +1136,7 @@ int extcon_dev_register(struct extcon_dev *edev)
for (index = 0; index < edev->max_supported; index++) { for (index = 0; index < edev->max_supported; index++) {
cable = &edev->cables[index]; cable = &edev->cables[index];
snprintf(buf, 10, "cable.%d", index); str = kasprintf(GFP_KERNEL, "cable.%d", index);
str = kzalloc(strlen(buf) + 1,
GFP_KERNEL);
if (!str) { if (!str) {
for (index--; index >= 0; index--) { for (index--; index >= 0; index--) {
cable = &edev->cables[index]; cable = &edev->cables[index];
...@@ -1149,7 +1146,6 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1149,7 +1146,6 @@ int extcon_dev_register(struct extcon_dev *edev)
goto err_alloc_cables; goto err_alloc_cables;
} }
strcpy(str, buf);
cable->edev = edev; cable->edev = edev;
cable->cable_index = index; cable->cable_index = index;
...@@ -1172,7 +1168,6 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1172,7 +1168,6 @@ int extcon_dev_register(struct extcon_dev *edev)
} }
if (edev->max_supported && edev->mutually_exclusive) { if (edev->max_supported && edev->mutually_exclusive) {
char buf[80];
char *name; char *name;
/* Count the size of mutually_exclusive array */ /* Count the size of mutually_exclusive array */
...@@ -1197,9 +1192,8 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1197,9 +1192,8 @@ int extcon_dev_register(struct extcon_dev *edev)
} }
for (index = 0; edev->mutually_exclusive[index]; index++) { for (index = 0; edev->mutually_exclusive[index]; index++) {
sprintf(buf, "0x%x", edev->mutually_exclusive[index]); name = kasprintf(GFP_KERNEL, "0x%x",
name = kzalloc(strlen(buf) + 1, edev->mutually_exclusive[index]);
GFP_KERNEL);
if (!name) { if (!name) {
for (index--; index >= 0; index--) { for (index--; index >= 0; index--) {
kfree(edev->d_attrs_muex[index].attr. kfree(edev->d_attrs_muex[index].attr.
...@@ -1210,7 +1204,6 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1210,7 +1204,6 @@ int extcon_dev_register(struct extcon_dev *edev)
ret = -ENOMEM; ret = -ENOMEM;
goto err_muex; goto err_muex;
} }
strcpy(name, buf);
sysfs_attr_init(&edev->d_attrs_muex[index].attr); sysfs_attr_init(&edev->d_attrs_muex[index].attr);
edev->d_attrs_muex[index].attr.name = name; edev->d_attrs_muex[index].attr.name = name;
edev->d_attrs_muex[index].attr.mode = 0000; edev->d_attrs_muex[index].attr.mode = 0000;
......
...@@ -10,37 +10,31 @@ if GOOGLE_FIRMWARE ...@@ -10,37 +10,31 @@ if GOOGLE_FIRMWARE
config GOOGLE_SMI config GOOGLE_SMI
tristate "SMI interface for Google platforms" tristate "SMI interface for Google platforms"
depends on X86 && ACPI && DMI && EFI depends on X86 && ACPI && DMI
select EFI_VARS
help help
Say Y here if you want to enable SMI callbacks for Google Say Y here if you want to enable SMI callbacks for Google
platforms. This provides an interface for writing to and platforms. This provides an interface for writing to and
clearing the EFI event log and reading and writing NVRAM clearing the event log. If EFI_VARS is also enabled this
driver provides an interface for reading and writing NVRAM
variables. variables.
config GOOGLE_COREBOOT_TABLE config GOOGLE_COREBOOT_TABLE
tristate tristate "Coreboot Table Access"
depends on GOOGLE_COREBOOT_TABLE_ACPI || GOOGLE_COREBOOT_TABLE_OF depends on ACPI || OF
config GOOGLE_COREBOOT_TABLE_ACPI
tristate "Coreboot Table Access - ACPI"
depends on ACPI
select GOOGLE_COREBOOT_TABLE
help help
This option enables the coreboot_table module, which provides other This option enables the coreboot_table module, which provides other
firmware modules to access to the coreboot table. The coreboot table firmware modules access to the coreboot table. The coreboot table
pointer is accessed through the ACPI "GOOGCB00" object. pointer is accessed through the ACPI "GOOGCB00" object or the
device tree node /firmware/coreboot.
If unsure say N. If unsure say N.
config GOOGLE_COREBOOT_TABLE_ACPI
tristate
select GOOGLE_COREBOOT_TABLE
config GOOGLE_COREBOOT_TABLE_OF config GOOGLE_COREBOOT_TABLE_OF
tristate "Coreboot Table Access - Device Tree" tristate
depends on OF
select GOOGLE_COREBOOT_TABLE select GOOGLE_COREBOOT_TABLE
help
This option enable the coreboot_table module, which provide other
firmware modules to access coreboot table. The coreboot table pointer
is accessed through the device tree node /firmware/coreboot.
If unsure say N.
config GOOGLE_MEMCONSOLE config GOOGLE_MEMCONSOLE
tristate tristate
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
obj-$(CONFIG_GOOGLE_SMI) += gsmi.o obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o
obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
......
/*
* coreboot_table-acpi.c
*
* Using ACPI to locate Coreboot table and provide coreboot table access.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "coreboot_table.h"
static int coreboot_table_acpi_probe(struct platform_device *pdev)
{
phys_addr_t phyaddr;
resource_size_t len;
struct coreboot_table_header __iomem *header = NULL;
struct resource *res;
void __iomem *ptr = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
len = resource_size(res);
if (!res->start || !len)
return -EINVAL;
phyaddr = res->start;
header = ioremap_cache(phyaddr, sizeof(*header));
if (header == NULL)
return -ENOMEM;
ptr = ioremap_cache(phyaddr,
header->header_bytes + header->table_bytes);
iounmap(header);
if (!ptr)
return -ENOMEM;
return coreboot_table_init(&pdev->dev, ptr);
}
static int coreboot_table_acpi_remove(struct platform_device *pdev)
{
return coreboot_table_exit();
}
static const struct acpi_device_id cros_coreboot_acpi_match[] = {
{ "GOOGCB00", 0 },
{ "BOOT0000", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
static struct platform_driver coreboot_table_acpi_driver = {
.probe = coreboot_table_acpi_probe,
.remove = coreboot_table_acpi_remove,
.driver = {
.name = "coreboot_table_acpi",
.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
},
};
static int __init coreboot_table_acpi_init(void)
{
return platform_driver_register(&coreboot_table_acpi_driver);
}
module_init(coreboot_table_acpi_init);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");
/*
* coreboot_table-of.c
*
* Coreboot table access through open firmware.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "coreboot_table.h"
static int coreboot_table_of_probe(struct platform_device *pdev)
{
struct device_node *fw_dn = pdev->dev.of_node;
void __iomem *ptr;
ptr = of_iomap(fw_dn, 0);
of_node_put(fw_dn);
if (!ptr)
return -ENOMEM;
return coreboot_table_init(&pdev->dev, ptr);
}
static int coreboot_table_of_remove(struct platform_device *pdev)
{
return coreboot_table_exit();
}
static const struct of_device_id coreboot_of_match[] = {
{ .compatible = "coreboot" },
{},
};
static struct platform_driver coreboot_table_of_driver = {
.probe = coreboot_table_of_probe,
.remove = coreboot_table_of_remove,
.driver = {
.name = "coreboot_table_of",
.of_match_table = coreboot_of_match,
},
};
static int __init platform_coreboot_table_of_init(void)
{
struct platform_device *pdev;
struct device_node *of_node;
/* Limit device creation to the presence of /firmware/coreboot node */
of_node = of_find_node_by_path("/firmware/coreboot");
if (!of_node)
return -ENODEV;
if (!of_match_node(coreboot_of_match, of_node))
return -ENODEV;
pdev = of_platform_device_create(of_node, "coreboot_table_of", NULL);
if (!pdev)
return -ENODEV;
return platform_driver_register(&coreboot_table_of_driver);
}
module_init(platform_coreboot_table_of_init);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");
...@@ -16,12 +16,15 @@ ...@@ -16,12 +16,15 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/acpi.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "coreboot_table.h" #include "coreboot_table.h"
...@@ -29,8 +32,6 @@ ...@@ -29,8 +32,6 @@
#define CB_DEV(d) container_of(d, struct coreboot_device, dev) #define CB_DEV(d) container_of(d, struct coreboot_device, dev)
#define CB_DRV(d) container_of(d, struct coreboot_driver, drv) #define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
static struct coreboot_table_header __iomem *ptr_header;
static int coreboot_bus_match(struct device *dev, struct device_driver *drv) static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
{ {
struct coreboot_device *device = CB_DEV(dev); struct coreboot_device *device = CB_DEV(dev);
...@@ -70,12 +71,6 @@ static struct bus_type coreboot_bus_type = { ...@@ -70,12 +71,6 @@ static struct bus_type coreboot_bus_type = {
.remove = coreboot_bus_remove, .remove = coreboot_bus_remove,
}; };
static int __init coreboot_bus_init(void)
{
return bus_register(&coreboot_bus_type);
}
module_init(coreboot_bus_init);
static void coreboot_device_release(struct device *dev) static void coreboot_device_release(struct device *dev)
{ {
struct coreboot_device *device = CB_DEV(dev); struct coreboot_device *device = CB_DEV(dev);
...@@ -97,62 +92,117 @@ void coreboot_driver_unregister(struct coreboot_driver *driver) ...@@ -97,62 +92,117 @@ void coreboot_driver_unregister(struct coreboot_driver *driver)
} }
EXPORT_SYMBOL(coreboot_driver_unregister); EXPORT_SYMBOL(coreboot_driver_unregister);
int coreboot_table_init(struct device *dev, void __iomem *ptr) static int coreboot_table_populate(struct device *dev, void *ptr)
{ {
int i, ret; int i, ret;
void *ptr_entry; void *ptr_entry;
struct coreboot_device *device; struct coreboot_device *device;
struct coreboot_table_entry entry; struct coreboot_table_entry *entry;
struct coreboot_table_header header; struct coreboot_table_header *header = ptr;
ptr_header = ptr;
memcpy_fromio(&header, ptr_header, sizeof(header));
if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
pr_warn("coreboot_table: coreboot table missing or corrupt!\n");
return -ENODEV;
}
ptr_entry = (void *)ptr_header + header.header_bytes; ptr_entry = ptr + header->header_bytes;
for (i = 0; i < header.table_entries; i++) { for (i = 0; i < header->table_entries; i++) {
memcpy_fromio(&entry, ptr_entry, sizeof(entry)); entry = ptr_entry;
device = kzalloc(sizeof(struct device) + entry.size, GFP_KERNEL); device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
if (!device) { if (!device)
ret = -ENOMEM; return -ENOMEM;
break;
}
dev_set_name(&device->dev, "coreboot%d", i); dev_set_name(&device->dev, "coreboot%d", i);
device->dev.parent = dev; device->dev.parent = dev;
device->dev.bus = &coreboot_bus_type; device->dev.bus = &coreboot_bus_type;
device->dev.release = coreboot_device_release; device->dev.release = coreboot_device_release;
memcpy_fromio(&device->entry, ptr_entry, entry.size); memcpy(&device->entry, ptr_entry, entry->size);
ret = device_register(&device->dev); ret = device_register(&device->dev);
if (ret) { if (ret) {
put_device(&device->dev); put_device(&device->dev);
break; return ret;
} }
ptr_entry += entry.size; ptr_entry += entry->size;
} }
return ret; return 0;
} }
EXPORT_SYMBOL(coreboot_table_init);
int coreboot_table_exit(void) static int coreboot_table_probe(struct platform_device *pdev)
{ {
if (ptr_header) { resource_size_t len;
bus_unregister(&coreboot_bus_type); struct coreboot_table_header *header;
iounmap(ptr_header); struct resource *res;
ptr_header = NULL; struct device *dev = &pdev->dev;
void *ptr;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
len = resource_size(res);
if (!res->start || !len)
return -EINVAL;
/* Check just the header first to make sure things are sane */
header = memremap(res->start, sizeof(*header), MEMREMAP_WB);
if (!header)
return -ENOMEM;
len = header->header_bytes + header->table_bytes;
ret = strncmp(header->signature, "LBIO", sizeof(header->signature));
memunmap(header);
if (ret) {
dev_warn(dev, "coreboot table missing or corrupt!\n");
return -ENODEV;
} }
ptr = memremap(res->start, len, MEMREMAP_WB);
if (!ptr)
return -ENOMEM;
ret = bus_register(&coreboot_bus_type);
if (!ret) {
ret = coreboot_table_populate(dev, ptr);
if (ret)
bus_unregister(&coreboot_bus_type);
}
memunmap(ptr);
return ret;
}
static int coreboot_table_remove(struct platform_device *pdev)
{
bus_unregister(&coreboot_bus_type);
return 0; return 0;
} }
EXPORT_SYMBOL(coreboot_table_exit);
#ifdef CONFIG_ACPI
static const struct acpi_device_id cros_coreboot_acpi_match[] = {
{ "GOOGCB00", 0 },
{ "BOOT0000", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
#endif
#ifdef CONFIG_OF
static const struct of_device_id coreboot_of_match[] = {
{ .compatible = "coreboot" },
{}
};
MODULE_DEVICE_TABLE(of, coreboot_of_match);
#endif
static struct platform_driver coreboot_table_driver = {
.probe = coreboot_table_probe,
.remove = coreboot_table_remove,
.driver = {
.name = "coreboot_table",
.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
.of_match_table = of_match_ptr(coreboot_of_match),
},
};
module_platform_driver(coreboot_table_driver);
MODULE_AUTHOR("Google, Inc."); MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -91,10 +91,4 @@ int coreboot_driver_register(struct coreboot_driver *driver); ...@@ -91,10 +91,4 @@ int coreboot_driver_register(struct coreboot_driver *driver);
/* Unregister a driver that uses the data from a coreboot table. */ /* Unregister a driver that uses the data from a coreboot table. */
void coreboot_driver_unregister(struct coreboot_driver *driver); void coreboot_driver_unregister(struct coreboot_driver *driver);
/* Initialize coreboot table module given a pointer to iomem */
int coreboot_table_init(struct device *dev, void __iomem *ptr);
/* Cleanup coreboot table module */
int coreboot_table_exit(void);
#endif /* __COREBOOT_TABLE_H */ #endif /* __COREBOOT_TABLE_H */
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/ucs2_string.h> #include <linux/ucs2_string.h>
#include <linux/suspend.h>
#define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */ #define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */
/* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */ /* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */
...@@ -70,6 +71,8 @@ ...@@ -70,6 +71,8 @@
#define GSMI_CMD_SET_NVRAM_VAR 0x03 #define GSMI_CMD_SET_NVRAM_VAR 0x03
#define GSMI_CMD_SET_EVENT_LOG 0x08 #define GSMI_CMD_SET_EVENT_LOG 0x08
#define GSMI_CMD_CLEAR_EVENT_LOG 0x09 #define GSMI_CMD_CLEAR_EVENT_LOG 0x09
#define GSMI_CMD_LOG_S0IX_SUSPEND 0x0a
#define GSMI_CMD_LOG_S0IX_RESUME 0x0b
#define GSMI_CMD_CLEAR_CONFIG 0x20 #define GSMI_CMD_CLEAR_CONFIG 0x20
#define GSMI_CMD_HANDSHAKE_TYPE 0xC1 #define GSMI_CMD_HANDSHAKE_TYPE 0xC1
...@@ -84,7 +87,7 @@ struct gsmi_buf { ...@@ -84,7 +87,7 @@ struct gsmi_buf {
u32 address; /* physical address of buffer */ u32 address; /* physical address of buffer */
}; };
struct gsmi_device { static struct gsmi_device {
struct platform_device *pdev; /* platform device */ struct platform_device *pdev; /* platform device */
struct gsmi_buf *name_buf; /* variable name buffer */ struct gsmi_buf *name_buf; /* variable name buffer */
struct gsmi_buf *data_buf; /* generic data buffer */ struct gsmi_buf *data_buf; /* generic data buffer */
...@@ -122,7 +125,6 @@ struct gsmi_log_entry_type_1 { ...@@ -122,7 +125,6 @@ struct gsmi_log_entry_type_1 {
u32 instance; u32 instance;
} __packed; } __packed;
/* /*
* Some platforms don't have explicit SMI handshake * Some platforms don't have explicit SMI handshake
* and need to wait for SMI to complete. * and need to wait for SMI to complete.
...@@ -133,6 +135,15 @@ module_param(spincount, uint, 0600); ...@@ -133,6 +135,15 @@ module_param(spincount, uint, 0600);
MODULE_PARM_DESC(spincount, MODULE_PARM_DESC(spincount,
"The number of loop iterations to use when using the spin handshake."); "The number of loop iterations to use when using the spin handshake.");
/*
* Platforms might not support S0ix logging in their GSMI handlers. In order to
* avoid any side-effects of generating an SMI for S0ix logging, use the S0ix
* related GSMI commands only for those platforms that explicitly enable this
* option.
*/
static bool s0ix_logging_enable;
module_param(s0ix_logging_enable, bool, 0600);
static struct gsmi_buf *gsmi_buf_alloc(void) static struct gsmi_buf *gsmi_buf_alloc(void)
{ {
struct gsmi_buf *smibuf; struct gsmi_buf *smibuf;
...@@ -289,6 +300,10 @@ static int gsmi_exec(u8 func, u8 sub) ...@@ -289,6 +300,10 @@ static int gsmi_exec(u8 func, u8 sub)
return rc; return rc;
} }
#ifdef CONFIG_EFI_VARS
static struct efivars efivars;
static efi_status_t gsmi_get_variable(efi_char16_t *name, static efi_status_t gsmi_get_variable(efi_char16_t *name,
efi_guid_t *vendor, u32 *attr, efi_guid_t *vendor, u32 *attr,
unsigned long *data_size, unsigned long *data_size,
...@@ -466,6 +481,8 @@ static const struct efivar_operations efivar_ops = { ...@@ -466,6 +481,8 @@ static const struct efivar_operations efivar_ops = {
.get_next_variable = gsmi_get_next_variable, .get_next_variable = gsmi_get_next_variable,
}; };
#endif /* CONFIG_EFI_VARS */
static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, static ssize_t eventlog_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count) char *buf, loff_t pos, size_t count)
...@@ -480,11 +497,10 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, ...@@ -480,11 +497,10 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj,
if (count < sizeof(u32)) if (count < sizeof(u32))
return -EINVAL; return -EINVAL;
param.type = *(u32 *)buf; param.type = *(u32 *)buf;
count -= sizeof(u32);
buf += sizeof(u32); buf += sizeof(u32);
/* The remaining buffer is the data payload */ /* The remaining buffer is the data payload */
if (count > gsmi_dev.data_buf->length) if ((count - sizeof(u32)) > gsmi_dev.data_buf->length)
return -EINVAL; return -EINVAL;
param.data_len = count - sizeof(u32); param.data_len = count - sizeof(u32);
...@@ -504,7 +520,7 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, ...@@ -504,7 +520,7 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj,
spin_unlock_irqrestore(&gsmi_dev.lock, flags); spin_unlock_irqrestore(&gsmi_dev.lock, flags);
return rc; return (rc == 0) ? count : rc;
} }
...@@ -716,6 +732,12 @@ static const struct dmi_system_id gsmi_dmi_table[] __initconst = { ...@@ -716,6 +732,12 @@ static const struct dmi_system_id gsmi_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
}, },
}, },
{
.ident = "Coreboot Firmware",
.matches = {
DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
},
},
{} {}
}; };
MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table); MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table);
...@@ -762,7 +784,6 @@ static __init int gsmi_system_valid(void) ...@@ -762,7 +784,6 @@ static __init int gsmi_system_valid(void)
} }
static struct kobject *gsmi_kobj; static struct kobject *gsmi_kobj;
static struct efivars efivars;
static const struct platform_device_info gsmi_dev_info = { static const struct platform_device_info gsmi_dev_info = {
.name = "gsmi", .name = "gsmi",
...@@ -771,6 +792,78 @@ static const struct platform_device_info gsmi_dev_info = { ...@@ -771,6 +792,78 @@ static const struct platform_device_info gsmi_dev_info = {
.dma_mask = DMA_BIT_MASK(32), .dma_mask = DMA_BIT_MASK(32),
}; };
#ifdef CONFIG_PM
static void gsmi_log_s0ix_info(u8 cmd)
{
unsigned long flags;
/*
* If platform has not enabled S0ix logging, then no action is
* necessary.
*/
if (!s0ix_logging_enable)
return;
spin_lock_irqsave(&gsmi_dev.lock, flags);
memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
gsmi_exec(GSMI_CALLBACK, cmd);
spin_unlock_irqrestore(&gsmi_dev.lock, flags);
}
static int gsmi_log_s0ix_suspend(struct device *dev)
{
/*
* If system is not suspending via firmware using the standard ACPI Sx
* types, then make a GSMI call to log the suspend info.
*/
if (!pm_suspend_via_firmware())
gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_SUSPEND);
/*
* Always return success, since we do not want suspend
* to fail just because of logging failure.
*/
return 0;
}
static int gsmi_log_s0ix_resume(struct device *dev)
{
/*
* If system did not resume via firmware, then make a GSMI call to log
* the resume info and wake source.
*/
if (!pm_resume_via_firmware())
gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_RESUME);
/*
* Always return success, since we do not want resume
* to fail just because of logging failure.
*/
return 0;
}
static const struct dev_pm_ops gsmi_pm_ops = {
.suspend_noirq = gsmi_log_s0ix_suspend,
.resume_noirq = gsmi_log_s0ix_resume,
};
static int gsmi_platform_driver_probe(struct platform_device *dev)
{
return 0;
}
static struct platform_driver gsmi_driver_info = {
.driver = {
.name = "gsmi",
.pm = &gsmi_pm_ops,
},
.probe = gsmi_platform_driver_probe,
};
#endif
static __init int gsmi_init(void) static __init int gsmi_init(void)
{ {
unsigned long flags; unsigned long flags;
...@@ -782,6 +875,14 @@ static __init int gsmi_init(void) ...@@ -782,6 +875,14 @@ static __init int gsmi_init(void)
gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command; gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command;
#ifdef CONFIG_PM
ret = platform_driver_register(&gsmi_driver_info);
if (unlikely(ret)) {
printk(KERN_ERR "gsmi: unable to register platform driver\n");
return ret;
}
#endif
/* register device */ /* register device */
gsmi_dev.pdev = platform_device_register_full(&gsmi_dev_info); gsmi_dev.pdev = platform_device_register_full(&gsmi_dev_info);
if (IS_ERR(gsmi_dev.pdev)) { if (IS_ERR(gsmi_dev.pdev)) {
...@@ -886,11 +987,14 @@ static __init int gsmi_init(void) ...@@ -886,11 +987,14 @@ static __init int gsmi_init(void)
goto out_remove_bin_file; goto out_remove_bin_file;
} }
#ifdef CONFIG_EFI_VARS
ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj);
if (ret) { if (ret) {
printk(KERN_INFO "gsmi: Failed to register efivars\n"); printk(KERN_INFO "gsmi: Failed to register efivars\n");
goto out_remove_sysfs_files; sysfs_remove_files(gsmi_kobj, gsmi_attrs);
goto out_remove_bin_file;
} }
#endif
register_reboot_notifier(&gsmi_reboot_notifier); register_reboot_notifier(&gsmi_reboot_notifier);
register_die_notifier(&gsmi_die_notifier); register_die_notifier(&gsmi_die_notifier);
...@@ -901,8 +1005,6 @@ static __init int gsmi_init(void) ...@@ -901,8 +1005,6 @@ static __init int gsmi_init(void)
return 0; return 0;
out_remove_sysfs_files:
sysfs_remove_files(gsmi_kobj, gsmi_attrs);
out_remove_bin_file: out_remove_bin_file:
sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr);
out_err: out_err:
...@@ -922,7 +1024,9 @@ static void __exit gsmi_exit(void) ...@@ -922,7 +1024,9 @@ static void __exit gsmi_exit(void)
unregister_die_notifier(&gsmi_die_notifier); unregister_die_notifier(&gsmi_die_notifier);
atomic_notifier_chain_unregister(&panic_notifier_list, atomic_notifier_chain_unregister(&panic_notifier_list,
&gsmi_panic_notifier); &gsmi_panic_notifier);
#ifdef CONFIG_EFI_VARS
efivars_unregister(&efivars); efivars_unregister(&efivars);
#endif
sysfs_remove_files(gsmi_kobj, gsmi_attrs); sysfs_remove_files(gsmi_kobj, gsmi_attrs);
sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr);
......
...@@ -198,7 +198,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, ...@@ -198,7 +198,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec,
sec->name = name; sec->name = name;
/* We want to export the raw partion with name ${name}_raw */ /* We want to export the raw partition with name ${name}_raw */
sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name); sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
if (!sec->raw_name) { if (!sec->raw_name) {
err = -ENOMEM; err = -ENOMEM;
......
...@@ -453,8 +453,8 @@ static int altera_cvp_probe(struct pci_dev *pdev, ...@@ -453,8 +453,8 @@ static int altera_cvp_probe(struct pci_dev *pdev,
snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
ALTERA_CVP_MGR_NAME, pci_name(pdev)); ALTERA_CVP_MGR_NAME, pci_name(pdev));
mgr = fpga_mgr_create(&pdev->dev, conf->mgr_name, mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
&altera_cvp_ops, conf); &altera_cvp_ops, conf);
if (!mgr) { if (!mgr) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_unmap; goto err_unmap;
...@@ -463,10 +463,8 @@ static int altera_cvp_probe(struct pci_dev *pdev, ...@@ -463,10 +463,8 @@ static int altera_cvp_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mgr); pci_set_drvdata(pdev, mgr);
ret = fpga_mgr_register(mgr); ret = fpga_mgr_register(mgr);
if (ret) { if (ret)
fpga_mgr_free(mgr);
goto err_unmap; goto err_unmap;
}
ret = driver_create_file(&altera_cvp_driver.driver, ret = driver_create_file(&altera_cvp_driver.driver,
&driver_attr_chkcfg); &driver_attr_chkcfg);
......
...@@ -121,18 +121,16 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) ...@@ -121,18 +121,16 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
/* Get f2s bridge configuration saved in handoff register */ /* Get f2s bridge configuration saved in handoff register */
regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask); regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
br = fpga_bridge_create(dev, F2S_BRIDGE_NAME, br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME,
&altera_fpga2sdram_br_ops, priv); &altera_fpga2sdram_br_ops, priv);
if (!br) if (!br)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, br); platform_set_drvdata(pdev, br);
ret = fpga_bridge_register(br); ret = fpga_bridge_register(br);
if (ret) { if (ret)
fpga_bridge_free(br);
return ret; return ret;
}
dev_info(dev, "driver initialized with handoff %08x\n", priv->mask); dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
......
...@@ -213,7 +213,6 @@ static int altera_freeze_br_probe(struct platform_device *pdev) ...@@ -213,7 +213,6 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
struct fpga_bridge *br; struct fpga_bridge *br;
struct resource *res; struct resource *res;
u32 status, revision; u32 status, revision;
int ret;
if (!np) if (!np)
return -ENODEV; return -ENODEV;
...@@ -245,20 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev) ...@@ -245,20 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
priv->base_addr = base_addr; priv->base_addr = base_addr;
br = fpga_bridge_create(dev, FREEZE_BRIDGE_NAME, br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
&altera_freeze_br_br_ops, priv); &altera_freeze_br_br_ops, priv);
if (!br) if (!br)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, br); platform_set_drvdata(pdev, br);
ret = fpga_bridge_register(br); return fpga_bridge_register(br);
if (ret) {
fpga_bridge_free(br);
return ret;
}
return 0;
} }
static int altera_freeze_br_remove(struct platform_device *pdev) static int altera_freeze_br_remove(struct platform_device *pdev)
......
...@@ -180,7 +180,8 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) ...@@ -180,7 +180,8 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
} }
} }
br = fpga_bridge_create(dev, priv->name, &altera_hps2fpga_br_ops, priv); br = devm_fpga_bridge_create(dev, priv->name,
&altera_hps2fpga_br_ops, priv);
if (!br) { if (!br) {
ret = -ENOMEM; ret = -ENOMEM;
goto err; goto err;
...@@ -190,12 +191,10 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) ...@@ -190,12 +191,10 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
ret = fpga_bridge_register(br); ret = fpga_bridge_register(br);
if (ret) if (ret)
goto err_free; goto err;
return 0; return 0;
err_free:
fpga_bridge_free(br);
err: err:
clk_disable_unprepare(priv->clk); clk_disable_unprepare(priv->clk);
......
...@@ -177,7 +177,6 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) ...@@ -177,7 +177,6 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
{ {
struct alt_pr_priv *priv; struct alt_pr_priv *priv;
struct fpga_manager *mgr; struct fpga_manager *mgr;
int ret;
u32 val; u32 val;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
...@@ -192,17 +191,13 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) ...@@ -192,17 +191,13 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT, (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
(int)(val & ALT_PR_CSR_PR_START)); (int)(val & ALT_PR_CSR_PR_START));
mgr = fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv); mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(dev, mgr); dev_set_drvdata(dev, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
EXPORT_SYMBOL_GPL(alt_pr_register); EXPORT_SYMBOL_GPL(alt_pr_register);
......
...@@ -239,7 +239,6 @@ static int altera_ps_probe(struct spi_device *spi) ...@@ -239,7 +239,6 @@ static int altera_ps_probe(struct spi_device *spi)
struct altera_ps_conf *conf; struct altera_ps_conf *conf;
const struct of_device_id *of_id; const struct of_device_id *of_id;
struct fpga_manager *mgr; struct fpga_manager *mgr;
int ret;
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
if (!conf) if (!conf)
...@@ -275,18 +274,14 @@ static int altera_ps_probe(struct spi_device *spi) ...@@ -275,18 +274,14 @@ static int altera_ps_probe(struct spi_device *spi)
snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
dev_driver_string(&spi->dev), dev_name(&spi->dev)); dev_driver_string(&spi->dev), dev_name(&spi->dev));
mgr = fpga_mgr_create(&spi->dev, conf->mgr_name, mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
&altera_ps_ops, conf); &altera_ps_ops, conf);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, mgr); spi_set_drvdata(spi, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int altera_ps_remove(struct spi_device *spi) static int altera_ps_remove(struct spi_device *spi)
......
...@@ -70,7 +70,7 @@ static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr) ...@@ -70,7 +70,7 @@ static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr)
dev_dbg(dev, "[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n", current->pid, dev_dbg(dev, "[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n", current->pid,
incr ? '+' : '-', npages << PAGE_SHIFT, incr ? '+' : '-', npages << PAGE_SHIFT,
current->mm->locked_vm << PAGE_SHIFT, rlimit(RLIMIT_MEMLOCK), current->mm->locked_vm << PAGE_SHIFT, rlimit(RLIMIT_MEMLOCK),
ret ? "- execeeded" : ""); ret ? "- exceeded" : "");
up_write(&current->mm->mmap_sem); up_write(&current->mm->mmap_sem);
......
...@@ -61,7 +61,6 @@ static int fme_br_probe(struct platform_device *pdev) ...@@ -61,7 +61,6 @@ static int fme_br_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct fme_br_priv *priv; struct fme_br_priv *priv;
struct fpga_bridge *br; struct fpga_bridge *br;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -69,18 +68,14 @@ static int fme_br_probe(struct platform_device *pdev) ...@@ -69,18 +68,14 @@ static int fme_br_probe(struct platform_device *pdev)
priv->pdata = dev_get_platdata(dev); priv->pdata = dev_get_platdata(dev);
br = fpga_bridge_create(dev, "DFL FPGA FME Bridge", br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge",
&fme_bridge_ops, priv); &fme_bridge_ops, priv);
if (!br) if (!br)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, br); platform_set_drvdata(pdev, br);
ret = fpga_bridge_register(br); return fpga_bridge_register(br);
if (ret)
fpga_bridge_free(br);
return ret;
} }
static int fme_br_remove(struct platform_device *pdev) static int fme_br_remove(struct platform_device *pdev)
......
...@@ -201,7 +201,7 @@ static int fme_mgr_write(struct fpga_manager *mgr, ...@@ -201,7 +201,7 @@ static int fme_mgr_write(struct fpga_manager *mgr,
} }
if (count < 4) { if (count < 4) {
dev_err(dev, "Invaild PR bitstream size\n"); dev_err(dev, "Invalid PR bitstream size\n");
return -EINVAL; return -EINVAL;
} }
...@@ -287,7 +287,6 @@ static int fme_mgr_probe(struct platform_device *pdev) ...@@ -287,7 +287,6 @@ static int fme_mgr_probe(struct platform_device *pdev)
struct fme_mgr_priv *priv; struct fme_mgr_priv *priv;
struct fpga_manager *mgr; struct fpga_manager *mgr;
struct resource *res; struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -309,19 +308,15 @@ static int fme_mgr_probe(struct platform_device *pdev) ...@@ -309,19 +308,15 @@ static int fme_mgr_probe(struct platform_device *pdev)
fme_mgr_get_compat_id(priv->ioaddr, compat_id); fme_mgr_get_compat_id(priv->ioaddr, compat_id);
mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager", mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
&fme_mgr_ops, priv); &fme_mgr_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
mgr->compat_id = compat_id; mgr->compat_id = compat_id;
platform_set_drvdata(pdev, mgr); platform_set_drvdata(pdev, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int fme_mgr_remove(struct platform_device *pdev) static int fme_mgr_remove(struct platform_device *pdev)
......
...@@ -39,7 +39,7 @@ static int fme_region_probe(struct platform_device *pdev) ...@@ -39,7 +39,7 @@ static int fme_region_probe(struct platform_device *pdev)
if (IS_ERR(mgr)) if (IS_ERR(mgr))
return -EPROBE_DEFER; return -EPROBE_DEFER;
region = fpga_region_create(dev, mgr, fme_region_get_bridges); region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
if (!region) { if (!region) {
ret = -ENOMEM; ret = -ENOMEM;
goto eprobe_mgr_put; goto eprobe_mgr_put;
...@@ -51,14 +51,12 @@ static int fme_region_probe(struct platform_device *pdev) ...@@ -51,14 +51,12 @@ static int fme_region_probe(struct platform_device *pdev)
ret = fpga_region_register(region); ret = fpga_region_register(region);
if (ret) if (ret)
goto region_free; goto eprobe_mgr_put;
dev_dbg(dev, "DFL FME FPGA Region probed\n"); dev_dbg(dev, "DFL FME FPGA Region probed\n");
return 0; return 0;
region_free:
fpga_region_free(region);
eprobe_mgr_put: eprobe_mgr_put:
fpga_mgr_put(mgr); fpga_mgr_put(mgr);
return ret; return ret;
......
...@@ -899,7 +899,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) ...@@ -899,7 +899,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
if (!cdev) if (!cdev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
cdev->region = fpga_region_create(info->dev, NULL, NULL); cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
if (!cdev->region) { if (!cdev->region) {
ret = -ENOMEM; ret = -ENOMEM;
goto free_cdev_exit; goto free_cdev_exit;
...@@ -911,7 +911,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) ...@@ -911,7 +911,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
ret = fpga_region_register(cdev->region); ret = fpga_region_register(cdev->region);
if (ret) if (ret)
goto free_region_exit; goto free_cdev_exit;
/* create and init build info for enumeration */ /* create and init build info for enumeration */
binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL); binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
...@@ -942,8 +942,6 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) ...@@ -942,8 +942,6 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
unregister_region_exit: unregister_region_exit:
fpga_region_unregister(cdev->region); fpga_region_unregister(cdev->region);
free_region_exit:
fpga_region_free(cdev->region);
free_cdev_exit: free_cdev_exit:
devm_kfree(info->dev, cdev); devm_kfree(info->dev, cdev);
return ERR_PTR(ret); return ERR_PTR(ret);
......
...@@ -324,6 +324,9 @@ ATTRIBUTE_GROUPS(fpga_bridge); ...@@ -324,6 +324,9 @@ ATTRIBUTE_GROUPS(fpga_bridge);
* @br_ops: pointer to structure of fpga bridge ops * @br_ops: pointer to structure of fpga bridge ops
* @priv: FPGA bridge private data * @priv: FPGA bridge private data
* *
* The caller of this function is responsible for freeing the bridge with
* fpga_bridge_free(). Using devm_fpga_bridge_create() instead is recommended.
*
* Return: struct fpga_bridge or NULL * Return: struct fpga_bridge or NULL
*/ */
struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
...@@ -378,8 +381,8 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, ...@@ -378,8 +381,8 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
EXPORT_SYMBOL_GPL(fpga_bridge_create); EXPORT_SYMBOL_GPL(fpga_bridge_create);
/** /**
* fpga_bridge_free - free a fpga bridge and its id * fpga_bridge_free - free a fpga bridge created by fpga_bridge_create()
* @bridge: FPGA bridge struct created by fpga_bridge_create * @bridge: FPGA bridge struct
*/ */
void fpga_bridge_free(struct fpga_bridge *bridge) void fpga_bridge_free(struct fpga_bridge *bridge)
{ {
...@@ -388,9 +391,56 @@ void fpga_bridge_free(struct fpga_bridge *bridge) ...@@ -388,9 +391,56 @@ void fpga_bridge_free(struct fpga_bridge *bridge)
} }
EXPORT_SYMBOL_GPL(fpga_bridge_free); EXPORT_SYMBOL_GPL(fpga_bridge_free);
static void devm_fpga_bridge_release(struct device *dev, void *res)
{
struct fpga_bridge *bridge = *(struct fpga_bridge **)res;
fpga_bridge_free(bridge);
}
/** /**
* fpga_bridge_register - register a fpga bridge * devm_fpga_bridge_create - create and init a managed struct fpga_bridge
* @bridge: FPGA bridge struct created by fpga_bridge_create * @dev: FPGA bridge device from pdev
* @name: FPGA bridge name
* @br_ops: pointer to structure of fpga bridge ops
* @priv: FPGA bridge private data
*
* This function is intended for use in a FPGA bridge driver's probe function.
* After the bridge driver creates the struct with devm_fpga_bridge_create(), it
* should register the bridge with fpga_bridge_register(). The bridge driver's
* remove function should call fpga_bridge_unregister(). The bridge struct
* allocated with this function will be freed automatically on driver detach.
* This includes the case of a probe function returning error before calling
* fpga_bridge_register(), the struct will still get cleaned up.
*
* Return: struct fpga_bridge or NULL
*/
struct fpga_bridge
*devm_fpga_bridge_create(struct device *dev, const char *name,
const struct fpga_bridge_ops *br_ops, void *priv)
{
struct fpga_bridge **ptr, *bridge;
ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
bridge = fpga_bridge_create(dev, name, br_ops, priv);
if (!bridge) {
devres_free(ptr);
} else {
*ptr = bridge;
devres_add(dev, ptr);
}
return bridge;
}
EXPORT_SYMBOL_GPL(devm_fpga_bridge_create);
/**
* fpga_bridge_register - register a FPGA bridge
*
* @bridge: FPGA bridge struct
* *
* Return: 0 for success, error code otherwise. * Return: 0 for success, error code otherwise.
*/ */
...@@ -412,8 +462,11 @@ int fpga_bridge_register(struct fpga_bridge *bridge) ...@@ -412,8 +462,11 @@ int fpga_bridge_register(struct fpga_bridge *bridge)
EXPORT_SYMBOL_GPL(fpga_bridge_register); EXPORT_SYMBOL_GPL(fpga_bridge_register);
/** /**
* fpga_bridge_unregister - unregister and free a fpga bridge * fpga_bridge_unregister - unregister a FPGA bridge
* @bridge: FPGA bridge struct created by fpga_bridge_create *
* @bridge: FPGA bridge struct
*
* This function is intended for use in a FPGA bridge driver's remove function.
*/ */
void fpga_bridge_unregister(struct fpga_bridge *bridge) void fpga_bridge_unregister(struct fpga_bridge *bridge)
{ {
...@@ -430,9 +483,6 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister); ...@@ -430,9 +483,6 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
static void fpga_bridge_dev_release(struct device *dev) static void fpga_bridge_dev_release(struct device *dev)
{ {
struct fpga_bridge *bridge = to_fpga_bridge(dev);
fpga_bridge_free(bridge);
} }
static int __init fpga_bridge_dev_init(void) static int __init fpga_bridge_dev_init(void)
......
...@@ -558,6 +558,9 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock); ...@@ -558,6 +558,9 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
* @mops: pointer to structure of fpga manager ops * @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data * @priv: fpga manager private data
* *
* The caller of this function is responsible for freeing the struct with
* fpga_mgr_free(). Using devm_fpga_mgr_create() instead is recommended.
*
* Return: pointer to struct fpga_manager or NULL * Return: pointer to struct fpga_manager or NULL
*/ */
struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
...@@ -618,8 +621,8 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, ...@@ -618,8 +621,8 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
EXPORT_SYMBOL_GPL(fpga_mgr_create); EXPORT_SYMBOL_GPL(fpga_mgr_create);
/** /**
* fpga_mgr_free - deallocate a FPGA manager * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
* @mgr: fpga manager struct created by fpga_mgr_create * @mgr: fpga manager struct
*/ */
void fpga_mgr_free(struct fpga_manager *mgr) void fpga_mgr_free(struct fpga_manager *mgr)
{ {
...@@ -628,9 +631,55 @@ void fpga_mgr_free(struct fpga_manager *mgr) ...@@ -628,9 +631,55 @@ void fpga_mgr_free(struct fpga_manager *mgr)
} }
EXPORT_SYMBOL_GPL(fpga_mgr_free); EXPORT_SYMBOL_GPL(fpga_mgr_free);
static void devm_fpga_mgr_release(struct device *dev, void *res)
{
struct fpga_manager *mgr = *(struct fpga_manager **)res;
fpga_mgr_free(mgr);
}
/**
* devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
* @dev: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
*
* This function is intended for use in a FPGA manager driver's probe function.
* After the manager driver creates the manager struct with
* devm_fpga_mgr_create(), it should register it with fpga_mgr_register(). The
* manager driver's remove function should call fpga_mgr_unregister(). The
* manager struct allocated with this function will be freed automatically on
* driver detach. This includes the case of a probe function returning error
* before calling fpga_mgr_register(), the struct will still get cleaned up.
*
* Return: pointer to struct fpga_manager or NULL
*/
struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
const struct fpga_manager_ops *mops,
void *priv)
{
struct fpga_manager **ptr, *mgr;
ptr = devres_alloc(devm_fpga_mgr_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
mgr = fpga_mgr_create(dev, name, mops, priv);
if (!mgr) {
devres_free(ptr);
} else {
*ptr = mgr;
devres_add(dev, ptr);
}
return mgr;
}
EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
/** /**
* fpga_mgr_register - register a FPGA manager * fpga_mgr_register - register a FPGA manager
* @mgr: fpga manager struct created by fpga_mgr_create * @mgr: fpga manager struct
* *
* Return: 0 on success, negative error code otherwise. * Return: 0 on success, negative error code otherwise.
*/ */
...@@ -661,8 +710,10 @@ int fpga_mgr_register(struct fpga_manager *mgr) ...@@ -661,8 +710,10 @@ int fpga_mgr_register(struct fpga_manager *mgr)
EXPORT_SYMBOL_GPL(fpga_mgr_register); EXPORT_SYMBOL_GPL(fpga_mgr_register);
/** /**
* fpga_mgr_unregister - unregister and free a FPGA manager * fpga_mgr_unregister - unregister a FPGA manager
* @mgr: fpga manager struct * @mgr: fpga manager struct
*
* This function is intended for use in a FPGA manager driver's remove function.
*/ */
void fpga_mgr_unregister(struct fpga_manager *mgr) void fpga_mgr_unregister(struct fpga_manager *mgr)
{ {
...@@ -681,9 +732,6 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unregister); ...@@ -681,9 +732,6 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
static void fpga_mgr_dev_release(struct device *dev) static void fpga_mgr_dev_release(struct device *dev)
{ {
struct fpga_manager *mgr = to_fpga_manager(dev);
fpga_mgr_free(mgr);
} }
static int __init fpga_mgr_class_init(void) static int __init fpga_mgr_class_init(void)
......
...@@ -185,6 +185,10 @@ ATTRIBUTE_GROUPS(fpga_region); ...@@ -185,6 +185,10 @@ ATTRIBUTE_GROUPS(fpga_region);
* @mgr: manager that programs this region * @mgr: manager that programs this region
* @get_bridges: optional function to get bridges to a list * @get_bridges: optional function to get bridges to a list
* *
* The caller of this function is responsible for freeing the resulting region
* struct with fpga_region_free(). Using devm_fpga_region_create() instead is
* recommended.
*
* Return: struct fpga_region or NULL * Return: struct fpga_region or NULL
*/ */
struct fpga_region struct fpga_region
...@@ -230,8 +234,8 @@ struct fpga_region ...@@ -230,8 +234,8 @@ struct fpga_region
EXPORT_SYMBOL_GPL(fpga_region_create); EXPORT_SYMBOL_GPL(fpga_region_create);
/** /**
* fpga_region_free - free a struct fpga_region * fpga_region_free - free a FPGA region created by fpga_region_create()
* @region: FPGA region created by fpga_region_create * @region: FPGA region
*/ */
void fpga_region_free(struct fpga_region *region) void fpga_region_free(struct fpga_region *region)
{ {
...@@ -240,21 +244,69 @@ void fpga_region_free(struct fpga_region *region) ...@@ -240,21 +244,69 @@ void fpga_region_free(struct fpga_region *region)
} }
EXPORT_SYMBOL_GPL(fpga_region_free); EXPORT_SYMBOL_GPL(fpga_region_free);
static void devm_fpga_region_release(struct device *dev, void *res)
{
struct fpga_region *region = *(struct fpga_region **)res;
fpga_region_free(region);
}
/**
* devm_fpga_region_create - create and initialize a managed FPGA region struct
* @dev: device parent
* @mgr: manager that programs this region
* @get_bridges: optional function to get bridges to a list
*
* This function is intended for use in a FPGA region driver's probe function.
* After the region driver creates the region struct with
* devm_fpga_region_create(), it should register it with fpga_region_register().
* The region driver's remove function should call fpga_region_unregister().
* The region struct allocated with this function will be freed automatically on
* driver detach. This includes the case of a probe function returning error
* before calling fpga_region_register(), the struct will still get cleaned up.
*
* Return: struct fpga_region or NULL
*/
struct fpga_region
*devm_fpga_region_create(struct device *dev,
struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *))
{
struct fpga_region **ptr, *region;
ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
region = fpga_region_create(dev, mgr, get_bridges);
if (!region) {
devres_free(ptr);
} else {
*ptr = region;
devres_add(dev, ptr);
}
return region;
}
EXPORT_SYMBOL_GPL(devm_fpga_region_create);
/** /**
* fpga_region_register - register a FPGA region * fpga_region_register - register a FPGA region
* @region: FPGA region created by fpga_region_create * @region: FPGA region
*
* Return: 0 or -errno * Return: 0 or -errno
*/ */
int fpga_region_register(struct fpga_region *region) int fpga_region_register(struct fpga_region *region)
{ {
return device_add(&region->dev); return device_add(&region->dev);
} }
EXPORT_SYMBOL_GPL(fpga_region_register); EXPORT_SYMBOL_GPL(fpga_region_register);
/** /**
* fpga_region_unregister - unregister and free a FPGA region * fpga_region_unregister - unregister a FPGA region
* @region: FPGA region * @region: FPGA region
*
* This function is intended for use in a FPGA region driver's remove function.
*/ */
void fpga_region_unregister(struct fpga_region *region) void fpga_region_unregister(struct fpga_region *region)
{ {
...@@ -264,9 +316,6 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister); ...@@ -264,9 +316,6 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
static void fpga_region_dev_release(struct device *dev) static void fpga_region_dev_release(struct device *dev)
{ {
struct fpga_region *region = to_fpga_region(dev);
fpga_region_free(region);
} }
/** /**
......
...@@ -175,18 +175,14 @@ static int ice40_fpga_probe(struct spi_device *spi) ...@@ -175,18 +175,14 @@ static int ice40_fpga_probe(struct spi_device *spi)
return ret; return ret;
} }
mgr = fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager", mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
&ice40_fpga_ops, priv); &ice40_fpga_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, mgr); spi_set_drvdata(spi, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int ice40_fpga_remove(struct spi_device *spi) static int ice40_fpga_remove(struct spi_device *spi)
......
...@@ -356,25 +356,20 @@ static int machxo2_spi_probe(struct spi_device *spi) ...@@ -356,25 +356,20 @@ static int machxo2_spi_probe(struct spi_device *spi)
{ {
struct device *dev = &spi->dev; struct device *dev = &spi->dev;
struct fpga_manager *mgr; struct fpga_manager *mgr;
int ret;
if (spi->max_speed_hz > MACHXO2_MAX_SPEED) { if (spi->max_speed_hz > MACHXO2_MAX_SPEED) {
dev_err(dev, "Speed is too high\n"); dev_err(dev, "Speed is too high\n");
return -EINVAL; return -EINVAL;
} }
mgr = fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager", mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
&machxo2_ops, spi); &machxo2_ops, spi);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, mgr); spi_set_drvdata(spi, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int machxo2_spi_remove(struct spi_device *spi) static int machxo2_spi_remove(struct spi_device *spi)
......
...@@ -410,7 +410,7 @@ static int of_fpga_region_probe(struct platform_device *pdev) ...@@ -410,7 +410,7 @@ static int of_fpga_region_probe(struct platform_device *pdev)
if (IS_ERR(mgr)) if (IS_ERR(mgr))
return -EPROBE_DEFER; return -EPROBE_DEFER;
region = fpga_region_create(dev, mgr, of_fpga_region_get_bridges); region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
if (!region) { if (!region) {
ret = -ENOMEM; ret = -ENOMEM;
goto eprobe_mgr_put; goto eprobe_mgr_put;
...@@ -418,7 +418,7 @@ static int of_fpga_region_probe(struct platform_device *pdev) ...@@ -418,7 +418,7 @@ static int of_fpga_region_probe(struct platform_device *pdev)
ret = fpga_region_register(region); ret = fpga_region_register(region);
if (ret) if (ret)
goto eprobe_free; goto eprobe_mgr_put;
of_platform_populate(np, fpga_region_of_match, NULL, &region->dev); of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
dev_set_drvdata(dev, region); dev_set_drvdata(dev, region);
...@@ -427,8 +427,6 @@ static int of_fpga_region_probe(struct platform_device *pdev) ...@@ -427,8 +427,6 @@ static int of_fpga_region_probe(struct platform_device *pdev)
return 0; return 0;
eprobe_free:
fpga_region_free(region);
eprobe_mgr_put: eprobe_mgr_put:
fpga_mgr_put(mgr); fpga_mgr_put(mgr);
return ret; return ret;
......
...@@ -508,8 +508,8 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) ...@@ -508,8 +508,8 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
return -EBUSY; return -EBUSY;
} }
mgr = fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager", mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
&socfpga_a10_fpga_mgr_ops, priv); &socfpga_a10_fpga_mgr_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
...@@ -517,7 +517,6 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) ...@@ -517,7 +517,6 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
ret = fpga_mgr_register(mgr); ret = fpga_mgr_register(mgr);
if (ret) { if (ret) {
fpga_mgr_free(mgr);
clk_disable_unprepare(priv->clk); clk_disable_unprepare(priv->clk);
return ret; return ret;
} }
......
...@@ -571,18 +571,14 @@ static int socfpga_fpga_probe(struct platform_device *pdev) ...@@ -571,18 +571,14 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
&socfpga_fpga_ops, priv); &socfpga_fpga_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, mgr); platform_set_drvdata(pdev, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int socfpga_fpga_remove(struct platform_device *pdev) static int socfpga_fpga_remove(struct platform_device *pdev)
......
...@@ -118,7 +118,6 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) ...@@ -118,7 +118,6 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
struct ts73xx_fpga_priv *priv; struct ts73xx_fpga_priv *priv;
struct fpga_manager *mgr; struct fpga_manager *mgr;
struct resource *res; struct resource *res;
int ret;
priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -133,18 +132,14 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) ...@@ -133,18 +132,14 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
return PTR_ERR(priv->io_base); return PTR_ERR(priv->io_base);
} }
mgr = fpga_mgr_create(kdev, "TS-73xx FPGA Manager", mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
&ts73xx_fpga_ops, priv); &ts73xx_fpga_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, mgr); platform_set_drvdata(pdev, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int ts73xx_fpga_remove(struct platform_device *pdev) static int ts73xx_fpga_remove(struct platform_device *pdev)
......
...@@ -121,8 +121,8 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) ...@@ -121,8 +121,8 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
clk_disable(priv->clk); clk_disable(priv->clk);
br = fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler", br = devm_fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler",
&xlnx_pr_decoupler_br_ops, priv); &xlnx_pr_decoupler_br_ops, priv);
if (!br) { if (!br) {
err = -ENOMEM; err = -ENOMEM;
goto err_clk; goto err_clk;
......
...@@ -144,7 +144,6 @@ static int xilinx_spi_probe(struct spi_device *spi) ...@@ -144,7 +144,6 @@ static int xilinx_spi_probe(struct spi_device *spi)
{ {
struct xilinx_spi_conf *conf; struct xilinx_spi_conf *conf;
struct fpga_manager *mgr; struct fpga_manager *mgr;
int ret;
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
if (!conf) if (!conf)
...@@ -167,18 +166,15 @@ static int xilinx_spi_probe(struct spi_device *spi) ...@@ -167,18 +166,15 @@ static int xilinx_spi_probe(struct spi_device *spi)
return PTR_ERR(conf->done); return PTR_ERR(conf->done);
} }
mgr = fpga_mgr_create(&spi->dev, "Xilinx Slave Serial FPGA Manager", mgr = devm_fpga_mgr_create(&spi->dev,
&xilinx_spi_ops, conf); "Xilinx Slave Serial FPGA Manager",
&xilinx_spi_ops, conf);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, mgr); spi_set_drvdata(spi, mgr);
ret = fpga_mgr_register(mgr); return fpga_mgr_register(mgr);
if (ret)
fpga_mgr_free(mgr);
return ret;
} }
static int xilinx_spi_remove(struct spi_device *spi) static int xilinx_spi_remove(struct spi_device *spi)
......
...@@ -614,8 +614,8 @@ static int zynq_fpga_probe(struct platform_device *pdev) ...@@ -614,8 +614,8 @@ static int zynq_fpga_probe(struct platform_device *pdev)
clk_disable(priv->clk); clk_disable(priv->clk);
mgr = fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager", mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
&zynq_fpga_ops, priv); &zynq_fpga_ops, priv);
if (!mgr) if (!mgr)
return -ENOMEM; return -ENOMEM;
...@@ -624,7 +624,6 @@ static int zynq_fpga_probe(struct platform_device *pdev) ...@@ -624,7 +624,6 @@ static int zynq_fpga_probe(struct platform_device *pdev)
err = fpga_mgr_register(mgr); err = fpga_mgr_register(mgr);
if (err) { if (err) {
dev_err(dev, "unable to register FPGA manager\n"); dev_err(dev, "unable to register FPGA manager\n");
fpga_mgr_free(mgr);
clk_unprepare(priv->clk); clk_unprepare(priv->clk);
return err; return err;
} }
......
This diff is collapsed.
...@@ -198,24 +198,19 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) ...@@ -198,24 +198,19 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel)
} }
/** /**
* vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message * vmbus_prep_negotiate_resp() - Create default response for Negotiate message
* @icmsghdrp: Pointer to msg header structure * @icmsghdrp: Pointer to msg header structure
* @icmsg_negotiate: Pointer to negotiate message structure
* @buf: Raw buffer channel data * @buf: Raw buffer channel data
* @fw_version: The framework versions we can support.
* @fw_vercnt: The size of @fw_version.
* @srv_version: The service versions we can support.
* @srv_vercnt: The size of @srv_version.
* @nego_fw_version: The selected framework version.
* @nego_srv_version: The selected service version.
* *
* @icmsghdrp is of type &struct icmsg_hdr. * Note: Versions are given in decreasing order.
* Set up and fill in default negotiate response message.
*
* The fw_version and fw_vercnt specifies the framework version that
* we can support.
*
* The srv_version and srv_vercnt specifies the service
* versions we can support.
*
* Versions are given in decreasing order.
*
* nego_fw_version and nego_srv_version store the selected protocol versions.
* *
* Set up and fill in default negotiate response message.
* Mainly used by Hyper-V drivers. * Mainly used by Hyper-V drivers.
*/ */
bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
...@@ -385,21 +380,14 @@ static void vmbus_release_relid(u32 relid) ...@@ -385,21 +380,14 @@ static void vmbus_release_relid(u32 relid)
trace_vmbus_release_relid(&msg, ret); trace_vmbus_release_relid(&msg, ret);
} }
void hv_process_channel_removal(u32 relid) void hv_process_channel_removal(struct vmbus_channel *channel)
{ {
struct vmbus_channel *primary_channel;
unsigned long flags; unsigned long flags;
struct vmbus_channel *primary_channel, *channel;
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
/*
* Make sure channel is valid as we may have raced.
*/
channel = relid2channel(relid);
if (!channel)
return;
BUG_ON(!channel->rescind); BUG_ON(!channel->rescind);
if (channel->target_cpu != get_cpu()) { if (channel->target_cpu != get_cpu()) {
put_cpu(); put_cpu();
smp_call_function_single(channel->target_cpu, smp_call_function_single(channel->target_cpu,
...@@ -429,7 +417,7 @@ void hv_process_channel_removal(u32 relid) ...@@ -429,7 +417,7 @@ void hv_process_channel_removal(u32 relid)
cpumask_clear_cpu(channel->target_cpu, cpumask_clear_cpu(channel->target_cpu,
&primary_channel->alloced_cpus_in_node); &primary_channel->alloced_cpus_in_node);
vmbus_release_relid(relid); vmbus_release_relid(channel->offermsg.child_relid);
free_channel(channel); free_channel(channel);
} }
...@@ -606,16 +594,18 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -606,16 +594,18 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
bool perf_chn = vmbus_devs[dev_type].perf_device; bool perf_chn = vmbus_devs[dev_type].perf_device;
struct vmbus_channel *primary = channel->primary_channel; struct vmbus_channel *primary = channel->primary_channel;
int next_node; int next_node;
struct cpumask available_mask; cpumask_var_t available_mask;
struct cpumask *alloced_mask; struct cpumask *alloced_mask;
if ((vmbus_proto_version == VERSION_WS2008) || if ((vmbus_proto_version == VERSION_WS2008) ||
(vmbus_proto_version == VERSION_WIN7) || (!perf_chn)) { (vmbus_proto_version == VERSION_WIN7) || (!perf_chn) ||
!alloc_cpumask_var(&available_mask, GFP_KERNEL)) {
/* /*
* Prior to win8, all channel interrupts are * Prior to win8, all channel interrupts are
* delivered on cpu 0. * delivered on cpu 0.
* Also if the channel is not a performance critical * Also if the channel is not a performance critical
* channel, bind it to cpu 0. * channel, bind it to cpu 0.
* In case alloc_cpumask_var() fails, bind it to cpu 0.
*/ */
channel->numa_node = 0; channel->numa_node = 0;
channel->target_cpu = 0; channel->target_cpu = 0;
...@@ -653,7 +643,7 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -653,7 +643,7 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
cpumask_clear(alloced_mask); cpumask_clear(alloced_mask);
} }
cpumask_xor(&available_mask, alloced_mask, cpumask_xor(available_mask, alloced_mask,
cpumask_of_node(primary->numa_node)); cpumask_of_node(primary->numa_node));
cur_cpu = -1; cur_cpu = -1;
...@@ -671,10 +661,10 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -671,10 +661,10 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
} }
while (true) { while (true) {
cur_cpu = cpumask_next(cur_cpu, &available_mask); cur_cpu = cpumask_next(cur_cpu, available_mask);
if (cur_cpu >= nr_cpu_ids) { if (cur_cpu >= nr_cpu_ids) {
cur_cpu = -1; cur_cpu = -1;
cpumask_copy(&available_mask, cpumask_copy(available_mask,
cpumask_of_node(primary->numa_node)); cpumask_of_node(primary->numa_node));
continue; continue;
} }
...@@ -704,6 +694,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -704,6 +694,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
channel->target_cpu = cur_cpu; channel->target_cpu = cur_cpu;
channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu); channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu);
free_cpumask_var(available_mask);
} }
static void vmbus_wait_for_unload(void) static void vmbus_wait_for_unload(void)
...@@ -943,7 +935,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) ...@@ -943,7 +935,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
* The channel is currently not open; * The channel is currently not open;
* it is safe for us to cleanup the channel. * it is safe for us to cleanup the channel.
*/ */
hv_process_channel_removal(rescind->child_relid); hv_process_channel_removal(channel);
} else { } else {
complete(&channel->rescind_event); complete(&channel->rescind_event);
} }
......
...@@ -189,6 +189,17 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu) ...@@ -189,6 +189,17 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
int hv_synic_alloc(void) int hv_synic_alloc(void)
{ {
int cpu; int cpu;
struct hv_per_cpu_context *hv_cpu;
/*
* First, zero all per-cpu memory areas so hv_synic_free() can
* detect what memory has been allocated and cleanup properly
* after any failures.
*/
for_each_present_cpu(cpu) {
hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);
memset(hv_cpu, 0, sizeof(*hv_cpu));
}
hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask), hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask),
GFP_KERNEL); GFP_KERNEL);
...@@ -198,10 +209,8 @@ int hv_synic_alloc(void) ...@@ -198,10 +209,8 @@ int hv_synic_alloc(void)
} }
for_each_present_cpu(cpu) { for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);
= per_cpu_ptr(hv_context.cpu_context, cpu);
memset(hv_cpu, 0, sizeof(*hv_cpu));
tasklet_init(&hv_cpu->msg_dpc, tasklet_init(&hv_cpu->msg_dpc,
vmbus_on_msg_dpc, (unsigned long) hv_cpu); vmbus_on_msg_dpc, (unsigned long) hv_cpu);
......
...@@ -689,7 +689,7 @@ static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) ...@@ -689,7 +689,7 @@ static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
__online_page_increment_counters(pg); __online_page_increment_counters(pg);
__online_page_free(pg); __online_page_free(pg);
WARN_ON_ONCE(!spin_is_locked(&dm_device.ha_lock)); lockdep_assert_held(&dm_device.ha_lock);
dm_device.num_pages_onlined++; dm_device.num_pages_onlined++;
} }
......
...@@ -353,7 +353,6 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op) ...@@ -353,7 +353,6 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled; out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled;
default:
utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id, utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id,
MAX_ADAPTER_ID_SIZE, MAX_ADAPTER_ID_SIZE,
UTF16_LITTLE_ENDIAN, UTF16_LITTLE_ENDIAN,
...@@ -406,7 +405,7 @@ kvp_send_key(struct work_struct *dummy) ...@@ -406,7 +405,7 @@ kvp_send_key(struct work_struct *dummy)
process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO);
break; break;
case KVP_OP_GET_IP_INFO: case KVP_OP_GET_IP_INFO:
process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); /* We only need to pass on message->kvp_hdr.operation. */
break; break;
case KVP_OP_SET: case KVP_OP_SET:
switch (in_msg->body.kvp_set.data.value_type) { switch (in_msg->body.kvp_set.data.value_type) {
...@@ -421,7 +420,7 @@ kvp_send_key(struct work_struct *dummy) ...@@ -421,7 +420,7 @@ kvp_send_key(struct work_struct *dummy)
UTF16_LITTLE_ENDIAN, UTF16_LITTLE_ENDIAN,
message->body.kvp_set.data.value, message->body.kvp_set.data.value,
HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1;
break; break;
case REG_U32: case REG_U32:
/* /*
...@@ -446,6 +445,9 @@ kvp_send_key(struct work_struct *dummy) ...@@ -446,6 +445,9 @@ kvp_send_key(struct work_struct *dummy)
break; break;
} }
break;
case KVP_OP_GET: case KVP_OP_GET:
message->body.kvp_set.data.key_size = message->body.kvp_set.data.key_size =
utf16s_to_utf8s( utf16s_to_utf8s(
...@@ -454,7 +456,7 @@ kvp_send_key(struct work_struct *dummy) ...@@ -454,7 +456,7 @@ kvp_send_key(struct work_struct *dummy)
UTF16_LITTLE_ENDIAN, UTF16_LITTLE_ENDIAN,
message->body.kvp_set.data.key, message->body.kvp_set.data.key,
HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
break; break;
case KVP_OP_DELETE: case KVP_OP_DELETE:
message->body.kvp_delete.key_size = message->body.kvp_delete.key_size =
...@@ -464,12 +466,12 @@ kvp_send_key(struct work_struct *dummy) ...@@ -464,12 +466,12 @@ kvp_send_key(struct work_struct *dummy)
UTF16_LITTLE_ENDIAN, UTF16_LITTLE_ENDIAN,
message->body.kvp_delete.key, message->body.kvp_delete.key,
HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
break; break;
case KVP_OP_ENUMERATE: case KVP_OP_ENUMERATE:
message->body.kvp_enum_data.index = message->body.kvp_enum_data.index =
in_msg->body.kvp_enum_data.index; in_msg->body.kvp_enum_data.index;
break; break;
} }
kvp_transaction.state = HVUTIL_USERSPACE_REQ; kvp_transaction.state = HVUTIL_USERSPACE_REQ;
......
...@@ -241,6 +241,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, ...@@ -241,6 +241,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
{ {
vunmap(ring_info->ring_buffer); vunmap(ring_info->ring_buffer);
ring_info->ring_buffer = NULL;
} }
/* Write to the ring buffer. */ /* Write to the ring buffer. */
......
...@@ -498,6 +498,54 @@ static ssize_t device_show(struct device *dev, ...@@ -498,6 +498,54 @@ static ssize_t device_show(struct device *dev,
} }
static DEVICE_ATTR_RO(device); static DEVICE_ATTR_RO(device);
static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hv_device *hv_dev = device_to_hv_device(dev);
char *driver_override, *old, *cp;
/* We need to keep extra room for a newline */
if (count >= (PAGE_SIZE - 1))
return -EINVAL;
driver_override = kstrndup(buf, count, GFP_KERNEL);
if (!driver_override)
return -ENOMEM;
cp = strchr(driver_override, '\n');
if (cp)
*cp = '\0';
device_lock(dev);
old = hv_dev->driver_override;
if (strlen(driver_override)) {
hv_dev->driver_override = driver_override;
} else {
kfree(driver_override);
hv_dev->driver_override = NULL;
}
device_unlock(dev);
kfree(old);
return count;
}
static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hv_device *hv_dev = device_to_hv_device(dev);
ssize_t len;
device_lock(dev);
len = snprintf(buf, PAGE_SIZE, "%s\n", hv_dev->driver_override);
device_unlock(dev);
return len;
}
static DEVICE_ATTR_RW(driver_override);
/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
static struct attribute *vmbus_dev_attrs[] = { static struct attribute *vmbus_dev_attrs[] = {
&dev_attr_id.attr, &dev_attr_id.attr,
...@@ -528,6 +576,7 @@ static struct attribute *vmbus_dev_attrs[] = { ...@@ -528,6 +576,7 @@ static struct attribute *vmbus_dev_attrs[] = {
&dev_attr_channel_vp_mapping.attr, &dev_attr_channel_vp_mapping.attr,
&dev_attr_vendor.attr, &dev_attr_vendor.attr,
&dev_attr_device.attr, &dev_attr_device.attr,
&dev_attr_driver_override.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(vmbus_dev); ATTRIBUTE_GROUPS(vmbus_dev);
...@@ -563,17 +612,26 @@ static inline bool is_null_guid(const uuid_le *guid) ...@@ -563,17 +612,26 @@ static inline bool is_null_guid(const uuid_le *guid)
return true; return true;
} }
/* static const struct hv_vmbus_device_id *
* Return a matching hv_vmbus_device_id pointer. hv_vmbus_dev_match(const struct hv_vmbus_device_id *id, const uuid_le *guid)
* If there is no match, return NULL.
*/ {
static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, if (id == NULL)
const uuid_le *guid) return NULL; /* empty device table */
for (; !is_null_guid(&id->guid); id++)
if (!uuid_le_cmp(id->guid, *guid))
return id;
return NULL;
}
static const struct hv_vmbus_device_id *
hv_vmbus_dynid_match(struct hv_driver *drv, const uuid_le *guid)
{ {
const struct hv_vmbus_device_id *id = NULL; const struct hv_vmbus_device_id *id = NULL;
struct vmbus_dynid *dynid; struct vmbus_dynid *dynid;
/* Look at the dynamic ids first, before the static ones */
spin_lock(&drv->dynids.lock); spin_lock(&drv->dynids.lock);
list_for_each_entry(dynid, &drv->dynids.list, node) { list_for_each_entry(dynid, &drv->dynids.list, node) {
if (!uuid_le_cmp(dynid->id.guid, *guid)) { if (!uuid_le_cmp(dynid->id.guid, *guid)) {
...@@ -583,18 +641,37 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, ...@@ -583,18 +641,37 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv,
} }
spin_unlock(&drv->dynids.lock); spin_unlock(&drv->dynids.lock);
if (id) return id;
return id; }
id = drv->id_table; static const struct hv_vmbus_device_id vmbus_device_null = {
if (id == NULL) .guid = NULL_UUID_LE,
return NULL; /* empty device table */ };
for (; !is_null_guid(&id->guid); id++) /*
if (!uuid_le_cmp(id->guid, *guid)) * Return a matching hv_vmbus_device_id pointer.
return id; * If there is no match, return NULL.
*/
static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv,
struct hv_device *dev)
{
const uuid_le *guid = &dev->dev_type;
const struct hv_vmbus_device_id *id;
return NULL; /* When driver_override is set, only bind to the matching driver */
if (dev->driver_override && strcmp(dev->driver_override, drv->name))
return NULL;
/* Look at the dynamic ids first, before the static ones */
id = hv_vmbus_dynid_match(drv, guid);
if (!id)
id = hv_vmbus_dev_match(drv->id_table, guid);
/* driver_override will always match, send a dummy id */
if (!id && dev->driver_override)
id = &vmbus_device_null;
return id;
} }
/* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */ /* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */
...@@ -643,7 +720,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, ...@@ -643,7 +720,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
if (retval) if (retval)
return retval; return retval;
if (hv_vmbus_get_id(drv, &guid)) if (hv_vmbus_dynid_match(drv, &guid))
return -EEXIST; return -EEXIST;
retval = vmbus_add_dynid(drv, &guid); retval = vmbus_add_dynid(drv, &guid);
...@@ -708,7 +785,7 @@ static int vmbus_match(struct device *device, struct device_driver *driver) ...@@ -708,7 +785,7 @@ static int vmbus_match(struct device *device, struct device_driver *driver)
if (is_hvsock_channel(hv_dev->channel)) if (is_hvsock_channel(hv_dev->channel))
return drv->hvsock; return drv->hvsock;
if (hv_vmbus_get_id(drv, &hv_dev->dev_type)) if (hv_vmbus_get_id(drv, hv_dev))
return 1; return 1;
return 0; return 0;
...@@ -725,7 +802,7 @@ static int vmbus_probe(struct device *child_device) ...@@ -725,7 +802,7 @@ static int vmbus_probe(struct device *child_device)
struct hv_device *dev = device_to_hv_device(child_device); struct hv_device *dev = device_to_hv_device(child_device);
const struct hv_vmbus_device_id *dev_id; const struct hv_vmbus_device_id *dev_id;
dev_id = hv_vmbus_get_id(drv, &dev->dev_type); dev_id = hv_vmbus_get_id(drv, dev);
if (drv->probe) { if (drv->probe) {
ret = drv->probe(dev, dev_id); ret = drv->probe(dev, dev_id);
if (ret != 0) if (ret != 0)
...@@ -787,10 +864,9 @@ static void vmbus_device_release(struct device *device) ...@@ -787,10 +864,9 @@ static void vmbus_device_release(struct device *device)
struct vmbus_channel *channel = hv_dev->channel; struct vmbus_channel *channel = hv_dev->channel;
mutex_lock(&vmbus_connection.channel_mutex); mutex_lock(&vmbus_connection.channel_mutex);
hv_process_channel_removal(channel->offermsg.child_relid); hv_process_channel_removal(channel);
mutex_unlock(&vmbus_connection.channel_mutex); mutex_unlock(&vmbus_connection.channel_mutex);
kfree(hv_dev); kfree(hv_dev);
} }
/* The one and only one */ /* The one and only one */
......
...@@ -406,6 +406,7 @@ static inline int catu_wait_for_ready(struct catu_drvdata *drvdata) ...@@ -406,6 +406,7 @@ static inline int catu_wait_for_ready(struct catu_drvdata *drvdata)
static int catu_enable_hw(struct catu_drvdata *drvdata, void *data) static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
{ {
int rc;
u32 control, mode; u32 control, mode;
struct etr_buf *etr_buf = data; struct etr_buf *etr_buf = data;
...@@ -418,6 +419,10 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data) ...@@ -418,6 +419,10 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
return -EBUSY; return -EBUSY;
} }
rc = coresight_claim_device_unlocked(drvdata->base);
if (rc)
return rc;
control |= BIT(CATU_CONTROL_ENABLE); control |= BIT(CATU_CONTROL_ENABLE);
if (etr_buf && etr_buf->mode == ETR_MODE_CATU) { if (etr_buf && etr_buf->mode == ETR_MODE_CATU) {
...@@ -459,6 +464,7 @@ static int catu_disable_hw(struct catu_drvdata *drvdata) ...@@ -459,6 +464,7 @@ static int catu_disable_hw(struct catu_drvdata *drvdata)
int rc = 0; int rc = 0;
catu_write_control(drvdata, 0); catu_write_control(drvdata, 0);
coresight_disclaim_device_unlocked(drvdata->base);
if (catu_wait_for_ready(drvdata)) { if (catu_wait_for_ready(drvdata)) {
dev_info(drvdata->dev, "Timeout while waiting for READY\n"); dev_info(drvdata->dev, "Timeout while waiting for READY\n");
rc = -EAGAIN; rc = -EAGAIN;
......
...@@ -34,48 +34,87 @@ struct replicator_state { ...@@ -34,48 +34,87 @@ struct replicator_state {
struct coresight_device *csdev; struct coresight_device *csdev;
}; };
/*
* replicator_reset : Reset the replicator configuration to sane values.
*/
static void replicator_reset(struct replicator_state *drvdata)
{
CS_UNLOCK(drvdata->base);
if (!coresight_claim_device_unlocked(drvdata->base)) {
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
coresight_disclaim_device_unlocked(drvdata->base);
}
CS_LOCK(drvdata->base);
}
static int replicator_enable(struct coresight_device *csdev, int inport, static int replicator_enable(struct coresight_device *csdev, int inport,
int outport) int outport)
{ {
int rc = 0;
u32 reg;
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
switch (outport) {
case 0:
reg = REPLICATOR_IDFILTER0;
break;
case 1:
reg = REPLICATOR_IDFILTER1;
break;
default:
WARN_ON(1);
return -EINVAL;
}
CS_UNLOCK(drvdata->base); CS_UNLOCK(drvdata->base);
/* if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) &&
* Ensure that the other port is disabled (readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff))
* 0x00 - passing through the replicator unimpeded rc = coresight_claim_device_unlocked(drvdata->base);
* 0xff - disable (or impede) the flow of ATB data
*/ /* Ensure that the outport is enabled. */
if (outport == 0) { if (!rc) {
writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER0); writel_relaxed(0x00, drvdata->base + reg);
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); dev_dbg(drvdata->dev, "REPLICATOR enabled\n");
} else {
writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER1);
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
} }
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
dev_info(drvdata->dev, "REPLICATOR enabled\n"); return rc;
return 0;
} }
static void replicator_disable(struct coresight_device *csdev, int inport, static void replicator_disable(struct coresight_device *csdev, int inport,
int outport) int outport)
{ {
u32 reg;
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
switch (outport) {
case 0:
reg = REPLICATOR_IDFILTER0;
break;
case 1:
reg = REPLICATOR_IDFILTER1;
break;
default:
WARN_ON(1);
return;
}
CS_UNLOCK(drvdata->base); CS_UNLOCK(drvdata->base);
/* disable the flow of ATB data through port */ /* disable the flow of ATB data through port */
if (outport == 0) writel_relaxed(0xff, drvdata->base + reg);
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
else
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) &&
(readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff))
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
dev_info(drvdata->dev, "REPLICATOR disabled\n"); dev_dbg(drvdata->dev, "REPLICATOR disabled\n");
} }
static const struct coresight_ops_link replicator_link_ops = { static const struct coresight_ops_link replicator_link_ops = {
...@@ -156,7 +195,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -156,7 +195,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
desc.groups = replicator_groups; desc.groups = replicator_groups;
drvdata->csdev = coresight_register(&desc); drvdata->csdev = coresight_register(&desc);
return PTR_ERR_OR_ZERO(drvdata->csdev); if (!IS_ERR(drvdata->csdev)) {
replicator_reset(drvdata);
return 0;
}
return PTR_ERR(drvdata->csdev);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* Description: CoreSight Embedded Trace Buffer driver * Description: CoreSight Embedded Trace Buffer driver
*/ */
#include <asm/local.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -28,6 +27,7 @@ ...@@ -28,6 +27,7 @@
#include "coresight-priv.h" #include "coresight-priv.h"
#include "coresight-etm-perf.h"
#define ETB_RAM_DEPTH_REG 0x004 #define ETB_RAM_DEPTH_REG 0x004
#define ETB_STATUS_REG 0x00c #define ETB_STATUS_REG 0x00c
...@@ -71,8 +71,8 @@ ...@@ -71,8 +71,8 @@
* @miscdev: specifics to handle "/dev/xyz.etb" entry. * @miscdev: specifics to handle "/dev/xyz.etb" entry.
* @spinlock: only one at a time pls. * @spinlock: only one at a time pls.
* @reading: synchronise user space access to etb buffer. * @reading: synchronise user space access to etb buffer.
* @mode: this ETB is being used.
* @buf: area of memory where ETB buffer content gets sent. * @buf: area of memory where ETB buffer content gets sent.
* @mode: this ETB is being used.
* @buffer_depth: size of @buf. * @buffer_depth: size of @buf.
* @trigger_cntr: amount of words to store after a trigger. * @trigger_cntr: amount of words to store after a trigger.
*/ */
...@@ -84,12 +84,15 @@ struct etb_drvdata { ...@@ -84,12 +84,15 @@ struct etb_drvdata {
struct miscdevice miscdev; struct miscdevice miscdev;
spinlock_t spinlock; spinlock_t spinlock;
local_t reading; local_t reading;
local_t mode;
u8 *buf; u8 *buf;
u32 mode;
u32 buffer_depth; u32 buffer_depth;
u32 trigger_cntr; u32 trigger_cntr;
}; };
static int etb_set_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle);
static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata)
{ {
u32 depth = 0; u32 depth = 0;
...@@ -103,7 +106,7 @@ static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) ...@@ -103,7 +106,7 @@ static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata)
return depth; return depth;
} }
static void etb_enable_hw(struct etb_drvdata *drvdata) static void __etb_enable_hw(struct etb_drvdata *drvdata)
{ {
int i; int i;
u32 depth; u32 depth;
...@@ -131,32 +134,92 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) ...@@ -131,32 +134,92 @@ static void etb_enable_hw(struct etb_drvdata *drvdata)
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
} }
static int etb_enable(struct coresight_device *csdev, u32 mode) static int etb_enable_hw(struct etb_drvdata *drvdata)
{
__etb_enable_hw(drvdata);
return 0;
}
static int etb_enable_sysfs(struct coresight_device *csdev)
{ {
u32 val; int ret = 0;
unsigned long flags; unsigned long flags;
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
val = local_cmpxchg(&drvdata->mode, spin_lock_irqsave(&drvdata->spinlock, flags);
CS_MODE_DISABLED, mode);
/* /* Don't messup with perf sessions. */
* When accessing from Perf, a HW buffer can be handled if (drvdata->mode == CS_MODE_PERF) {
* by a single trace entity. In sysFS mode many tracers ret = -EBUSY;
* can be logging to the same HW buffer. goto out;
*/ }
if (val == CS_MODE_PERF)
return -EBUSY;
/* Nothing to do, the tracer is already enabled. */ /* Nothing to do, the tracer is already enabled. */
if (val == CS_MODE_SYSFS) if (drvdata->mode == CS_MODE_SYSFS)
goto out; goto out;
spin_lock_irqsave(&drvdata->spinlock, flags); ret = etb_enable_hw(drvdata);
etb_enable_hw(drvdata); if (!ret)
drvdata->mode = CS_MODE_SYSFS;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
static int etb_enable_perf(struct coresight_device *csdev, void *data)
{
int ret = 0;
unsigned long flags;
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock_irqsave(&drvdata->spinlock, flags);
/* No need to continue if the component is already in use. */
if (drvdata->mode != CS_MODE_DISABLED) {
ret = -EBUSY;
goto out;
}
/*
* We don't have an internal state to clean up if we fail to setup
* the perf buffer. So we can perform the step before we turn the
* ETB on and leave without cleaning up.
*/
ret = etb_set_buffer(csdev, (struct perf_output_handle *)data);
if (ret)
goto out;
ret = etb_enable_hw(drvdata);
if (!ret)
drvdata->mode = CS_MODE_PERF;
out: out:
dev_info(drvdata->dev, "ETB enabled\n"); spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
static int etb_enable(struct coresight_device *csdev, u32 mode, void *data)
{
int ret;
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
switch (mode) {
case CS_MODE_SYSFS:
ret = etb_enable_sysfs(csdev);
break;
case CS_MODE_PERF:
ret = etb_enable_perf(csdev, data);
break;
default:
ret = -EINVAL;
break;
}
if (ret)
return ret;
dev_dbg(drvdata->dev, "ETB enabled\n");
return 0; return 0;
} }
...@@ -256,13 +319,16 @@ static void etb_disable(struct coresight_device *csdev) ...@@ -256,13 +319,16 @@ static void etb_disable(struct coresight_device *csdev)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags); spin_lock_irqsave(&drvdata->spinlock, flags);
etb_disable_hw(drvdata);
etb_dump_hw(drvdata);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
local_set(&drvdata->mode, CS_MODE_DISABLED); /* 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);
dev_info(drvdata->dev, "ETB disabled\n"); dev_dbg(drvdata->dev, "ETB disabled\n");
} }
static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu,
...@@ -294,12 +360,14 @@ static void etb_free_buffer(void *config) ...@@ -294,12 +360,14 @@ static void etb_free_buffer(void *config)
} }
static int etb_set_buffer(struct coresight_device *csdev, static int etb_set_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle, struct perf_output_handle *handle)
void *sink_config)
{ {
int ret = 0; int ret = 0;
unsigned long head; unsigned long head;
struct cs_buffers *buf = sink_config; struct cs_buffers *buf = etm_perf_sink_config(handle);
if (!buf)
return -EINVAL;
/* wrap head around to the amount of space we have */ /* wrap head around to the amount of space we have */
head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
...@@ -315,37 +383,7 @@ static int etb_set_buffer(struct coresight_device *csdev, ...@@ -315,37 +383,7 @@ static int etb_set_buffer(struct coresight_device *csdev,
return ret; return ret;
} }
static unsigned long etb_reset_buffer(struct coresight_device *csdev, static unsigned long etb_update_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle,
void *sink_config)
{
unsigned long size = 0;
struct cs_buffers *buf = sink_config;
if (buf) {
/*
* In snapshot mode ->data_size holds the new address of the
* ring buffer's head. The size itself is the whole address
* range since we want the latest information.
*/
if (buf->snapshot)
handle->head = local_xchg(&buf->data_size,
buf->nr_pages << PAGE_SHIFT);
/*
* Tell the tracer PMU how much we got in this run and if
* something went wrong along the way. Nobody else can use
* this cs_buffers instance until we are done. As such
* resetting parameters here and squaring off with the ring
* buffer API in the tracer PMU is fine.
*/
size = local_xchg(&buf->data_size, 0);
}
return size;
}
static void etb_update_buffer(struct coresight_device *csdev,
struct perf_output_handle *handle, struct perf_output_handle *handle,
void *sink_config) void *sink_config)
{ {
...@@ -354,13 +392,13 @@ static void etb_update_buffer(struct coresight_device *csdev, ...@@ -354,13 +392,13 @@ static void etb_update_buffer(struct coresight_device *csdev,
u8 *buf_ptr; u8 *buf_ptr;
const u32 *barrier; const u32 *barrier;
u32 read_ptr, write_ptr, capacity; u32 read_ptr, write_ptr, capacity;
u32 status, read_data, to_read; u32 status, read_data;
unsigned long offset; unsigned long offset, to_read;
struct cs_buffers *buf = sink_config; struct cs_buffers *buf = sink_config;
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (!buf) if (!buf)
return; return 0;
capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
...@@ -465,18 +503,17 @@ static void etb_update_buffer(struct coresight_device *csdev, ...@@ -465,18 +503,17 @@ static void etb_update_buffer(struct coresight_device *csdev,
writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
/* /*
* In snapshot mode all we have to do is communicate to * In snapshot mode we have to update the handle->head to point
* perf_aux_output_end() the address of the current head. In full * to the new location.
* trace mode the same function expects a size to move rb->aux_head
* forward.
*/ */
if (buf->snapshot) if (buf->snapshot) {
local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); handle->head = (cur * PAGE_SIZE) + offset;
else to_read = buf->nr_pages << PAGE_SHIFT;
local_add(to_read, &buf->data_size); }
etb_enable_hw(drvdata); etb_enable_hw(drvdata);
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
return to_read;
} }
static const struct coresight_ops_sink etb_sink_ops = { static const struct coresight_ops_sink etb_sink_ops = {
...@@ -484,8 +521,6 @@ static const struct coresight_ops_sink etb_sink_ops = { ...@@ -484,8 +521,6 @@ static const struct coresight_ops_sink etb_sink_ops = {
.disable = etb_disable, .disable = etb_disable,
.alloc_buffer = etb_alloc_buffer, .alloc_buffer = etb_alloc_buffer,
.free_buffer = etb_free_buffer, .free_buffer = etb_free_buffer,
.set_buffer = etb_set_buffer,
.reset_buffer = etb_reset_buffer,
.update_buffer = etb_update_buffer, .update_buffer = etb_update_buffer,
}; };
...@@ -498,14 +533,14 @@ static void etb_dump(struct etb_drvdata *drvdata) ...@@ -498,14 +533,14 @@ static void etb_dump(struct etb_drvdata *drvdata)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags); spin_lock_irqsave(&drvdata->spinlock, flags);
if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { if (drvdata->mode == CS_MODE_SYSFS) {
etb_disable_hw(drvdata); etb_disable_hw(drvdata);
etb_dump_hw(drvdata); etb_dump_hw(drvdata);
etb_enable_hw(drvdata); etb_enable_hw(drvdata);
} }
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "ETB dumped\n"); dev_dbg(drvdata->dev, "ETB dumped\n");
} }
static int etb_open(struct inode *inode, struct file *file) static int etb_open(struct inode *inode, struct file *file)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#ifndef _CORESIGHT_ETM_PERF_H #ifndef _CORESIGHT_ETM_PERF_H
#define _CORESIGHT_ETM_PERF_H #define _CORESIGHT_ETM_PERF_H
#include <linux/percpu-defs.h>
#include "coresight-priv.h" #include "coresight-priv.h"
struct coresight_device; struct coresight_device;
...@@ -42,14 +43,39 @@ struct etm_filters { ...@@ -42,14 +43,39 @@ struct etm_filters {
bool ssstatus; bool ssstatus;
}; };
/**
* struct etm_event_data - Coresight specifics associated to an event
* @work: Handle to free allocated memory outside IRQ context.
* @mask: Hold the CPU(s) this event was set for.
* @snk_config: The sink configuration.
* @path: An array of path, each slot for one CPU.
*/
struct etm_event_data {
struct work_struct work;
cpumask_t mask;
void *snk_config;
struct list_head * __percpu *path;
};
#ifdef CONFIG_CORESIGHT #ifdef CONFIG_CORESIGHT
int etm_perf_symlink(struct coresight_device *csdev, bool link); int etm_perf_symlink(struct coresight_device *csdev, bool link);
static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
{
struct etm_event_data *data = perf_get_aux(handle);
if (data)
return data->snk_config;
return NULL;
}
#else #else
static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) static inline int etm_perf_symlink(struct coresight_device *csdev, bool link)
{ return -EINVAL; } { return -EINVAL; }
static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
{
return NULL;
}
#endif /* CONFIG_CORESIGHT */ #endif /* CONFIG_CORESIGHT */
#endif #endif
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define FUNNEL_HOLDTIME_MASK 0xf00 #define FUNNEL_HOLDTIME_MASK 0xf00
#define FUNNEL_HOLDTIME_SHFT 0x8 #define FUNNEL_HOLDTIME_SHFT 0x8
#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
#define FUNNEL_ENSx_MASK 0xff
/** /**
* struct funnel_drvdata - specifics associated to a funnel component * struct funnel_drvdata - specifics associated to a funnel component
...@@ -42,31 +43,42 @@ struct funnel_drvdata { ...@@ -42,31 +43,42 @@ struct funnel_drvdata {
unsigned long priority; unsigned long priority;
}; };
static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port) static int funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
{ {
u32 functl; u32 functl;
int rc = 0;
CS_UNLOCK(drvdata->base); CS_UNLOCK(drvdata->base);
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
/* Claim the device only when we enable the first slave */
if (!(functl & FUNNEL_ENSx_MASK)) {
rc = coresight_claim_device_unlocked(drvdata->base);
if (rc)
goto done;
}
functl &= ~FUNNEL_HOLDTIME_MASK; functl &= ~FUNNEL_HOLDTIME_MASK;
functl |= FUNNEL_HOLDTIME; functl |= FUNNEL_HOLDTIME;
functl |= (1 << port); functl |= (1 << port);
writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
done:
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
return rc;
} }
static int funnel_enable(struct coresight_device *csdev, int inport, static int funnel_enable(struct coresight_device *csdev, int inport,
int outport) int outport)
{ {
int rc;
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
funnel_enable_hw(drvdata, inport); rc = funnel_enable_hw(drvdata, inport);
dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); if (!rc)
return 0; dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
return rc;
} }
static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
...@@ -79,6 +91,10 @@ static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) ...@@ -79,6 +91,10 @@ static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
functl &= ~(1 << inport); functl &= ~(1 << inport);
writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
/* Disclaim the device if none of the slaves are now active */
if (!(functl & FUNNEL_ENSx_MASK))
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base); CS_LOCK(drvdata->base);
} }
...@@ -89,7 +105,7 @@ static void funnel_disable(struct coresight_device *csdev, int inport, ...@@ -89,7 +105,7 @@ static void funnel_disable(struct coresight_device *csdev, int inport,
funnel_disable_hw(drvdata, inport); funnel_disable_hw(drvdata, inport);
dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); dev_dbg(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
} }
static const struct coresight_ops_link funnel_link_ops = { static const struct coresight_ops_link funnel_link_ops = {
......
...@@ -25,6 +25,13 @@ ...@@ -25,6 +25,13 @@
#define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVID 0xfc8
#define CORESIGHT_DEVTYPE 0xfcc #define CORESIGHT_DEVTYPE 0xfcc
/*
* Coresight device CLAIM protocol.
* See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
*/
#define CORESIGHT_CLAIM_SELF_HOSTED BIT(1)
#define TIMEOUT_US 100 #define TIMEOUT_US 100
#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb)
...@@ -137,7 +144,7 @@ static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, ...@@ -137,7 +144,7 @@ static inline void coresight_write_reg_pair(void __iomem *addr, u64 val,
} }
void coresight_disable_path(struct list_head *path); void coresight_disable_path(struct list_head *path);
int coresight_enable_path(struct list_head *path, u32 mode); int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data);
struct coresight_device *coresight_get_sink(struct list_head *path); struct coresight_device *coresight_get_sink(struct list_head *path);
struct coresight_device *coresight_get_enabled_sink(bool reset); struct coresight_device *coresight_get_enabled_sink(bool reset);
struct list_head *coresight_build_path(struct coresight_device *csdev, struct list_head *coresight_build_path(struct coresight_device *csdev,
......
...@@ -35,7 +35,7 @@ static int replicator_enable(struct coresight_device *csdev, int inport, ...@@ -35,7 +35,7 @@ static int replicator_enable(struct coresight_device *csdev, int inport,
{ {
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
dev_info(drvdata->dev, "REPLICATOR enabled\n"); dev_dbg(drvdata->dev, "REPLICATOR enabled\n");
return 0; return 0;
} }
...@@ -44,7 +44,7 @@ static void replicator_disable(struct coresight_device *csdev, int inport, ...@@ -44,7 +44,7 @@ static void replicator_disable(struct coresight_device *csdev, int inport,
{ {
struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
dev_info(drvdata->dev, "REPLICATOR disabled\n"); dev_dbg(drvdata->dev, "REPLICATOR disabled\n");
} }
static const struct coresight_ops_link replicator_link_ops = { static const struct coresight_ops_link replicator_link_ops = {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment