1
0
Fork 0

char/misc drivers for 4.4-rc1

Here is the big char/misc driver update for 4.4-rc1.  Lots of different
 driver and subsystem updates, hwtracing being the largest with the
 addition of some new platforms that are now supported.  Full details in
 the shortlog.
 
 All of these have been in linux-next for a long time with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlY6d/oACgkQMUfUDdst+yl93ACcCf91y+ufwU3cmcnq5LpwHPfx
 VbkAn08Cn6Wu6IcihoEpR4hqGgIOtjqW
 =1a3d
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.4-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 char/misc driver update for 4.4-rc1.  Lots of
  different driver and subsystem updates, hwtracing being the largest
  with the addition of some new platforms that are now supported.  Full
  details in the shortlog.

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

* tag 'char-misc-4.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (181 commits)
  fpga: socfpga: Fix check of return value of devm_request_irq
  lkdtm: fix ACCESS_USERSPACE test
  mcb: Destroy IDA on module unload
  mcb: Do not return zero on error path in mcb_pci_probe()
  mei: bus: set the device name before running fixup
  mei: bus: use correct lock ordering
  mei: Fix debugfs filename in error output
  char: ipmi: ipmi_ssif: Replace timeval with timespec64
  fpga: zynq-fpga: Fix issue with drvdata being overwritten.
  fpga manager: remove unnecessary null pointer checks
  fpga manager: ensure lifetime with of_fpga_mgr_get
  fpga: zynq-fpga: Change fw format to handle bin instead of bit.
  fpga: zynq-fpga: Fix unbalanced clock handling
  misc: sram: partition base address belongs to __iomem space
  coresight: etm3x: adding documentation for sysFS's cpu interface
  vme: 8-bit status/id takes 256 values, not 255
  fpga manager: Adding FPGA Manager support for Xilinx Zynq 7000
  ARM: zynq: dt: Updated devicetree for Zynq 7000 platform.
  ARM: dt: fpga: Added binding docs for Xilinx Zynq FPGA manager.
  ver_linux: proc/modules, limit text processing to 'sed'
  ...
hifive-unleashed-5.1
Linus Torvalds 2015-11-04 22:15:15 -08:00
commit 8e483ed134
204 changed files with 21680 additions and 2628 deletions

View File

@ -0,0 +1,48 @@
What: /config/stp-policy
Date: June 2015
KernelVersion: 4.3
Description:
This group contains policies mandating Master/Channel allocation
for software sources wishing to send trace data over an STM
device.
What: /config/stp-policy/<device>.<policy>
Date: June 2015
KernelVersion: 4.3
Description:
This group is the root of a policy; its name is a concatenation
of an stm device name to which this policy applies and an
arbitrary string. If <device> part doesn't match an existing
stm device, mkdir will fail with ENODEV; if that device already
has a policy assigned to it, mkdir will fail with EBUSY.
What: /config/stp-policy/<device>.<policy>/device
Date: June 2015
KernelVersion: 4.3
Description:
STM device to which this policy applies, read only. Same as the
<device> component of its parent directory.
What: /config/stp-policy/<device>.<policy>/<node>
Date: June 2015
KernelVersion: 4.3
Description:
Policy node is a string identifier that software clients will
use to request a master/channel to be allocated and assigned to
them.
What: /config/stp-policy/<device>.<policy>/<node>/masters
Date: June 2015
KernelVersion: 4.3
Description:
Range of masters from which to allocate for users of this node.
Write two numbers: the first master and the last master number.
What: /config/stp-policy/<device>.<policy>/<node>/channels
Date: June 2015
KernelVersion: 4.3
Description:
Range of channels from which to allocate for users of this node.
Write two numbers: the first channel and the last channel
number.

View File

@ -8,13 +8,6 @@ Description: (RW) Enable/disable tracing on this specific trace entiry.
of coresight components linking the source to the sink is
configured and managed automatically by the coresight framework.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/status
Date: November 2014
KernelVersion: 3.19
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (R) List various control and status registers. The specific
layout and content is driver specific.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/addr_idx
Date: November 2014
KernelVersion: 3.19
@ -251,3 +244,79 @@ Date: November 2014
KernelVersion: 3.19
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Define the event that controls the trigger.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/cpu
Date: October 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Holds the cpu number this tracer is affined to.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmccr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Configuration Code register
(0x004). The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmccer
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Configuration Code Extension
register (0x1e8). The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmscr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM System Configuration
register (0x014). The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmidr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM ID register (0x1e4). The
value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmcr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Main Control register (0x000).
The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmtraceidr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Trace ID register (0x200).
The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmteevr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Trace Enable Event register
(0x020). The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmtsscr
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Trace Start/Stop Conrol
register (0x018). The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmtecr1
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Enable Conrol #1
register (0x024). The value is read directly from the HW.
What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/mgmt/etmtecr2
Date: September 2015
KernelVersion: 4.4
Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RO) Print the content of the ETM Enable Conrol #2
register (0x01c). The value is read directly from the HW.

View File

@ -0,0 +1,49 @@
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/masters/*
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Configure output ports for STP masters. Writing -1
disables a master; any
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/outputs/[0-7]_port
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RO) Output port type:
0: not present,
1: MSU (Memory Storage Unit)
2: CTP (Common Trace Port)
4: PTI (MIPI PTI).
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/outputs/[0-7]_drop
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Data retention policy setting: keep (0) or drop (1)
incoming data while output port is in reset.
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/outputs/[0-7]_null
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) STP NULL packet generation: enabled (1) or disabled (0).
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/outputs/[0-7]_flush
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Force flush data from byte packing buffer for the output
port.
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/outputs/[0-7]_reset
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RO) Output port is in reset (1).
What: /sys/bus/intel_th/devices/<intel_th_id>-gth/outputs/[0-7]_smcfreq
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) STP sync packet frequency for the port. Specifies the
number of clocks between mainenance packets.

View File

@ -0,0 +1,33 @@
What: /sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/wrap
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Configure MSC buffer wrapping. 1 == wrapping enabled.
What: /sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/mode
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Configure MSC operating mode:
- "single", for contiguous buffer mode (high-order alloc);
- "multi", for multiblock mode;
- "ExI", for DCI handler mode;
- "debug", for debug mode.
If operating mode changes, existing buffer is deallocated,
provided there are no active users and tracing is not enabled,
otherwise the write will fail.
What: /sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/nr_pages
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Configure MSC buffer size for "single" or "multi" modes.
In single mode, this is a single number of pages, has to be
power of 2. In multiblock mode, this is a comma-separated list
of numbers of pages for each window to be allocated. Number of
windows is not limited.
Writing to this file deallocates existing buffer (provided
there are no active users and tracing is not enabled) and then
allocates a new one.

View File

@ -0,0 +1,24 @@
What: /sys/bus/intel_th/devices/<intel_th_id>-pti/mode
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Configure PTI output width. Currently supported values
are 4, 8, 12, 16.
What: /sys/bus/intel_th/devices/<intel_th_id>-pti/freerunning_clock
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) 0: PTI trace clock acts as a strobe which only toggles
when there is trace data to send. 1: PTI trace clock is a
free-running clock.
What: /sys/bus/intel_th/devices/<intel_th_id>-pti/clock_divider
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Configure PTI port clock divider:
- 0: Intel TH clock rate,
- 1: 1/2 Intel TH clock rate,
- 2: 1/4 Intel TH clock rate,
- 3: 1/8 Intel TH clock rate.

View File

@ -0,0 +1,13 @@
What: /sys/bus/intel_th/devices/<intel_th_id>-<device><id>/active
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RW) Writes of 1 or 0 enable or disable trace output to this
output device. Reads return current status.
What: /sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/port
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description: (RO) Port number, corresponding to this output device on the
switch (GTH).

View File

@ -19,3 +19,10 @@ KernelVersion: 4.2
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client device uuid
Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
What: /sys/bus/mei/devices/.../version
Date: Aug 2015
KernelVersion: 4.3
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client protocol version
Format: %d

View File

@ -0,0 +1,37 @@
What: /sys/class/fpga_manager/<fpga>/name
Date: August 2015
KernelVersion: 4.3
Contact: Alan Tull <atull@opensource.altera.com>
Description: Name of low level fpga manager driver.
What: /sys/class/fpga_manager/<fpga>/state
Date: August 2015
KernelVersion: 4.3
Contact: Alan Tull <atull@opensource.altera.com>
Description: Read fpga manager state as a string.
The intent is to provide enough detail that if something goes
wrong during FPGA programming (something that the driver can't
fix) then userspace can know, i.e. if the firmware request
fails, that could be due to not being able to find the firmware
file.
This is a superset of FPGA states and fpga manager driver
states. The fpga manager driver is walking through these steps
to get the FPGA into a known operating state. It's a sequence,
though some steps may get skipped. Valid FPGA states will vary
by manufacturer; this is a superset.
* unknown = can't determine state
* power off = FPGA power is off
* power up = FPGA reports power is up
* reset = FPGA held in reset state
* firmware request = firmware class request in progress
* firmware request error = firmware request failed
* write init = preparing FPGA for programming
* write init error = Error while preparing FPGA for
programming
* write = FPGA ready to receive image data
* write error = Error while programming
* write complete = Doing post programming steps
* write complete error = Error while doing post programming
* operating = FPGA is programmed and operating

View File

@ -41,18 +41,15 @@ Description:
When read, this entry provides the current state of an Intel
MIC device in the context of the card OS. Possible values that
will be read are:
"offline" - The MIC device is ready to boot the card OS. On
"ready" - The MIC device is ready to boot the card OS. On
reading this entry after an OSPM resume, a "boot" has to be
written to this entry if the card was previously shutdown
during OSPM suspend.
"online" - The MIC device has initiated booting a card OS.
"booting" - The MIC device has initiated booting a card OS.
"online" - The MIC device has completed boot and is online
"shutting_down" - The card OS is shutting down.
"resetting" - A reset has been initiated for the MIC device
"reset_failed" - The MIC device has failed to reset.
"suspending" - The MIC device is currently being prepared for
suspend. On reading this entry, a "suspend" has to be written
to the state sysfs entry to ensure the card is shutdown during
OSPM suspend.
"suspended" - The MIC device has been suspended.
When written, this sysfs entry triggers different state change
operations depending upon the current state of the card OS.
@ -62,8 +59,6 @@ Description:
sysfs entries.
"reset" - Initiates device reset.
"shutdown" - Initiates card OS shutdown.
"suspend" - Initiates card OS shutdown and also marks the card
as suspended.
What: /sys/class/mic/mic(x)/shutdown_status
Date: October 2013
@ -126,7 +121,7 @@ Description:
the card. This sysfs entry can be written with the following
valid strings:
a) linux - Boot a Linux image.
b) elf - Boot an elf image for flash updates.
b) flash - Boot an image for flash updates.
What: /sys/class/mic/mic(x)/log_buf_addr
Date: October 2013
@ -155,3 +150,17 @@ Description:
daemon to set the log buffer length address. The correct log
buffer length address to be written can be found in the
System.map file of the card OS.
What: /sys/class/mic/mic(x)/heartbeat_enable
Date: March 2015
KernelVersion: 3.20
Contact: Ashutosh Dixit <ashutosh.dixit@intel.com>
Description:
The MIC drivers detect and inform user space about card crashes
via a heartbeat mechanism (see the description of
shutdown_status above). User space can turn off this
notification by setting heartbeat_enable to 0 and enable it by
setting this entry to 1. If this notification is disabled it is
the responsibility of user space to detect card crashes via
alternative means such as a network ping. This setting is
enabled by default.

View File

@ -0,0 +1,14 @@
What: /sys/class/stm/<stm>/masters
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description:
Shows first and last available to software master numbers on
this STM device.
What: /sys/class/stm/<stm>/channels
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description:
Shows the number of channels per master on this STM device.

View File

@ -0,0 +1,11 @@
What: /sys/class/stm_source/<stm_source>/stm_source_link
Date: June 2015
KernelVersion: 4.3
Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Description:
stm_source device linkage to stm device, where its tracing data
is directed. Reads return an existing connection or "<none>" if
this stm_source is not connected to any stm device yet.
Write an existing (registered) stm device's name here to
connect that device. If a device is already connected to this
stm_source, it will first be disconnected.

View File

@ -0,0 +1,19 @@
Xilinx Zynq FPGA Manager
Required properties:
- compatible: should contain "xlnx,zynq-devcfg-1.0"
- reg: base address and size for memory mapped io
- interrupts: interrupt for the FPGA manager device
- clocks: phandle for clocks required operation
- clock-names: name for the clock, should be "ref_clk"
- syscon: phandle for access to SLCR registers
Example:
devcfg: devcfg@f8007000 {
compatible = "xlnx,zynq-devcfg-1.0";
reg = <0xf8007000 0x100>;
interrupts = <0 8 4>;
clocks = <&clkc 12>;
clock-names = "ref_clk";
syscon = <&slcr>;
};

View File

@ -33,6 +33,12 @@ Optional properties in the area nodes:
- compatible : standard definition, should contain a vendor specific string
in the form <vendor>,[<device>-]<usage>
- pool : indicates that the particular reserved SRAM area is addressable
and in use by another device or devices
- export : indicates that the reserved SRAM area may be accessed outside
of the kernel, e.g. by bootloader or userspace
- label : the name for the reserved partition, if omitted, the label
is taken from the node name excluding the unit address.
Example:
@ -48,4 +54,14 @@ sram: sram@5c000000 {
compatible = "socvendor,smp-sram";
reg = <0x100 0x50>;
};
device-sram@1000 {
reg = <0x1000 0x1000>;
pool;
};
exported@20000 {
reg = <0x20000 0x20000>;
export;
};
};

View File

@ -0,0 +1,20 @@
Freescale i.MX6 On-Chip OTP Controller (OCOTP) device tree bindings
This binding represents the on-chip eFuse OTP controller found on
i.MX6Q/D, i.MX6DL/S, i.MX6SL, and i.MX6SX SoCs.
Required properties:
- compatible: should be one of
"fsl,imx6q-ocotp" (i.MX6Q/D/DL/S),
"fsl,imx6sl-ocotp" (i.MX6SL), or
"fsl,imx6sx-ocotp" (i.MX6SX), followed by "syscon".
- reg: Should contain the register base and length.
- clocks: Should contain a phandle pointing to the gated peripheral clock.
Example:
ocotp: ocotp@021bc000 {
compatible = "fsl,imx6q-ocotp", "syscon";
reg = <0x021bc000 0x4000>;
clocks = <&clks IMX6QDL_CLK_IIM>;
};

View File

@ -0,0 +1,25 @@
On-Chip OTP Memory for Freescale i.MX23/i.MX28
Required properties :
- compatible :
- "fsl,imx23-ocotp" for i.MX23
- "fsl,imx28-ocotp" for i.MX28
- #address-cells : Should be 1
- #size-cells : Should be 1
- reg : Address and length of OTP controller registers
- clocks : Should contain a reference to the hbus clock
= Data cells =
Are child nodes of mxs-ocotp, bindings of which as described in
bindings/nvmem/nvmem.txt
Example for i.MX28:
ocotp: ocotp@8002c000 {
compatible = "fsl,imx28-ocotp", "fsl,ocotp";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x8002c000 0x2000>;
clocks = <&clks 25>;
status = "okay";
};

View File

@ -0,0 +1,38 @@
= Rockchip eFuse device tree bindings =
Required properties:
- compatible: Should be "rockchip,rockchip-efuse"
- reg: Should contain the registers location and exact eFuse size
- clocks: Should be the clock id of eFuse
- clock-names: Should be "pclk_efuse"
= Data cells =
Are child nodes of eFuse, bindings of which as described in
bindings/nvmem/nvmem.txt
Example:
efuse: efuse@ffb40000 {
compatible = "rockchip,rockchip-efuse";
reg = <0xffb40000 0x20>;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&cru PCLK_EFUSE256>;
clock-names = "pclk_efuse";
/* Data cells */
cpu_leakage: cpu_leakage {
reg = <0x17 0x1>;
};
};
= Data consumers =
Are device nodes which consume nvmem data cells.
Example:
cpu_leakage {
...
nvmem-cells = <&cpu_leakage>;
nvmem-cell-names = "cpu_leakage";
};

View File

@ -0,0 +1,19 @@
On-Chip OTP Memory for Freescale Vybrid
Required Properties:
compatible:
- "fsl,vf610-ocotp" for VF5xx/VF6xx
#address-cells : Should be 1
#size-cells : Should be 1
reg : Address and length of OTP controller and fuse map registers
clocks : ipg clock we associate with the OCOTP peripheral
Example for Vybrid VF5xx/VF6xx:
ocotp: ocotp@400a5000 {
compatible = "fsl,vf610-ocotp";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x400a5000 0xCF0>;
clocks = <&clks VF610_CLK_OCOTP>;
};

View File

@ -1,11 +1,15 @@
* OMAP HDQ One wire bus master controller
Required properties:
- compatible : should be "ti,omap3-1w"
- compatible : should be "ti,omap3-1w" or "ti,am4372-hdq"
- reg : Address and length of the register set for the device
- interrupts : interrupt line.
- ti,hwmods : "hdq1w"
Optional properties:
- ti,mode: should be "hdq": HDQ mode "1w": one-wire mode.
If not specified HDQ mode is implied.
Example:
- From omap3.dtsi
@ -14,4 +18,5 @@ Example:
reg = <0x480b2000 0x1000>;
interrupts = <58>;
ti,hwmods = "hdq1w";
ti,mode = "hdq";
};

View File

@ -0,0 +1,171 @@
FPGA Manager Core
Alan Tull 2015
Overview
========
The FPGA manager core exports a set of functions for programming an FPGA with
an image. The API is manufacturer agnostic. All manufacturer specifics are
hidden away in a low level driver which registers a set of ops with the core.
The FPGA image data itself is very manufacturer specific, but for our purposes
it's just binary data. The FPGA manager core won't parse it.
API Functions:
==============
To program the FPGA from a file or from a buffer:
-------------------------------------------------
int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags,
const char *buf, size_t count);
Load the FPGA from an image which exists as a buffer in memory.
int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
const char *image_name);
Load the FPGA from an image which exists as a file. The image file must be on
the firmware search path (see the firmware class documentation).
For both these functions, flags == 0 for normal full reconfiguration or
FPGA_MGR_PARTIAL_RECONFIG for partial reconfiguration. If successful, the FPGA
ends up in operating mode. Return 0 on success or a negative error code.
To get/put a reference to a FPGA manager:
-----------------------------------------
struct fpga_manager *of_fpga_mgr_get(struct device_node *node);
void fpga_mgr_put(struct fpga_manager *mgr);
Given a DT node, get an exclusive reference to a FPGA manager or release
the reference.
To register or unregister the low level FPGA-specific driver:
-------------------------------------------------------------
int fpga_mgr_register(struct device *dev, const char *name,
const struct fpga_manager_ops *mops,
void *priv);
void fpga_mgr_unregister(struct device *dev);
Use of these two functions is described below in "How To Support a new FPGA
device."
How to write an image buffer to a supported FPGA
================================================
/* Include to get the API */
#include <linux/fpga/fpga-mgr.h>
/* device node that specifies the FPGA manager to use */
struct device_node *mgr_node = ...
/* FPGA image is in this buffer. count is size of the buffer. */
char *buf = ...
int count = ...
/* flags indicates whether to do full or partial reconfiguration */
int flags = 0;
int ret;
/* Get exclusive control of FPGA manager */
struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node);
/* Load the buffer to the FPGA */
ret = fpga_mgr_buf_load(mgr, flags, buf, count);
/* Release the FPGA manager */
fpga_mgr_put(mgr);
How to write an image file to a supported FPGA
==============================================
/* Include to get the API */
#include <linux/fpga/fpga-mgr.h>
/* device node that specifies the FPGA manager to use */
struct device_node *mgr_node = ...
/* FPGA image is in this file which is in the firmware search path */
const char *path = "fpga-image-9.rbf"
/* flags indicates whether to do full or partial reconfiguration */
int flags = 0;
int ret;
/* Get exclusive control of FPGA manager */
struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node);
/* Get the firmware image (path) and load it to the FPGA */
ret = fpga_mgr_firmware_load(mgr, flags, path);
/* Release the FPGA manager */
fpga_mgr_put(mgr);
How to support a new FPGA device
================================
To add another FPGA manager, write a driver that implements a set of ops. The
probe function calls fpga_mgr_register(), such as:
static const struct fpga_manager_ops socfpga_fpga_ops = {
.write_init = socfpga_fpga_ops_configure_init,
.write = socfpga_fpga_ops_configure_write,
.write_complete = socfpga_fpga_ops_configure_complete,
.state = socfpga_fpga_ops_state,
};
static int socfpga_fpga_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct socfpga_fpga_priv *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* ... do ioremaps, get interrupts, etc. and save
them in priv... */
return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
&socfpga_fpga_ops, priv);
}
static int socfpga_fpga_remove(struct platform_device *pdev)
{
fpga_mgr_unregister(&pdev->dev);
return 0;
}
The ops will implement whatever device specific register writes are needed to
do the programming sequence for this particular FPGA. These ops return 0 for
success or negative error codes otherwise.
The programming sequence is:
1. .write_init
2. .write (may be called once or multiple times)
3. .write_complete
The .write_init function will prepare the FPGA to receive the image data.
The .write function writes a buffer to the FPGA. The buffer may be contain the
whole FPGA image or may be a smaller chunk of an FPGA image. In the latter
case, this function is called multiple times for successive chunks.
The .write_complete function is called after all the image has been written
to put the FPGA into operating mode.
The ops include a .state function which will read the hardware FPGA manager and
return a code of type enum fpga_mgr_states. It doesn't result in a change in
hardware state.

View File

@ -81,6 +81,9 @@ Code Seq#(hex) Include File Comments
0x22 all scsi/sg.h
'#' 00-3F IEEE 1394 Subsystem Block for the entire subsystem
'$' 00-0F linux/perf_counter.h, linux/perf_event.h
'%' 00-0F include/uapi/linux/stm.h
System Trace Module subsystem
<mailto:alexander.shishkin@linux.intel.com>
'&' 00-07 drivers/firewire/nosy-user.h
'1' 00-1F <linux/timepps.h> PPS kit from Ulrich Windl
<ftp://ftp.de.kernel.org/pub/linux/daemons/ntp/PPS/>

View File

@ -28,6 +28,10 @@ The Symmetric Communication Interface (SCIF (pronounced as skiff)) is a
low level communications API across PCIe currently implemented for MIC.
More details are available at scif_overview.txt.
The Coprocessor State Management (COSM) driver on the host allows for
boot, shutdown and reset of Intel MIC devices. It communicates with a COSM
"client" driver on the MIC cards over SCIF to perform these functions.
Here is a block diagram of the various components described above. The
virtio backends are situated on the host rather than the card given better
single threaded performance for the host compared to MIC, the ability of
@ -51,19 +55,20 @@ the fact that the virtio block storage backend can only be on the host.
| | | Virtio over PCIe IOCTLs |
| | +--------------------------+
+-----------+ | | | +-----------+
| MIC DMA | | +----------+ | +-----------+ | | MIC DMA |
| Driver | | | SCIF | | | SCIF | | | Driver |
+-----------+ | +----------+ | +-----------+ | +-----------+
| | | | | | |
+---------------+ | +-----+-----+ | +-----+-----+ | +---------------+
|MIC virtual Bus| | |SCIF HW Bus| | |SCIF HW BUS| | |MIC virtual Bus|
+---------------+ | +-----------+ | +-----+-----+ | +---------------+
| | | | | | |
| +--------------+ | | | +---------------+ |
| |Intel MIC | | | | |Intel MIC | |
+---|Card Driver +----+ | | |Host Driver | |
+--------------+ | +----+---------------+-----+
| | |
| MIC DMA | | +------+ | +------+ +------+ | | MIC DMA |
| Driver | | | SCIF | | | SCIF | | COSM | | | Driver |
+-----------+ | +------+ | +------+ +--+---+ | +-----------+
| | | | | | | |
+---------------+ | +------+ | +--+---+ +--+---+ | +----------------+
|MIC virtual Bus| | |SCIF | | |SCIF | | COSM | | |MIC virtual Bus |
+---------------+ | |HW Bus| | |HW Bus| | Bus | | +----------------+
| | +------+ | +--+---+ +------+ | |
| | | | | | | |
| +-----------+---+ | | | +---------------+ |
| |Intel MIC | | | | |Intel MIC | |
+---|Card Driver | | | | |Host Driver | |
+------------+--------+ | +----+---------------+-----+
| | |
+-------------------------------------------------------------+
| |
| PCIe Bus |

View File

@ -119,10 +119,10 @@ stop()
# Wait for the cards to go offline
for f in $sysfs/*
do
while [ "`cat $f/state`" != "offline" ]
while [ "`cat $f/state`" != "ready" ]
do
sleep 1
echo -e "Waiting for "`basename $f`" to go offline"
echo -e "Waiting for "`basename $f`" to become ready"
done
done

View File

@ -43,7 +43,7 @@
#include <linux/mic_common.h>
#include <tools/endian.h>
static void init_mic(struct mic_info *mic);
static void *init_mic(void *arg);
static FILE *logfp;
static struct mic_info mic_list;
@ -116,19 +116,18 @@ static struct {
.num = htole16(MIC_VRING_ENTRIES),
},
#if GSO_ENABLED
.host_features = htole32(
.host_features = htole32(
1 << VIRTIO_NET_F_CSUM |
1 << VIRTIO_NET_F_GSO |
1 << VIRTIO_NET_F_GUEST_TSO4 |
1 << VIRTIO_NET_F_GUEST_TSO6 |
1 << VIRTIO_NET_F_GUEST_ECN |
1 << VIRTIO_NET_F_GUEST_UFO),
1 << VIRTIO_NET_F_GUEST_ECN),
#else
.host_features = 0,
#endif
};
static const char *mic_config_dir = "/etc/sysconfig/mic";
static const char *mic_config_dir = "/etc/mpss";
static const char *virtblk_backend = "VIRTBLK_BACKEND";
static struct {
struct mic_device_desc dd;
@ -192,7 +191,7 @@ tap_configure(struct mic_info *mic, char *dev)
return ret;
}
snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id);
snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id + 1);
pid = fork();
if (pid == 0) {
@ -255,8 +254,7 @@ static int tun_alloc(struct mic_info *mic, char *dev)
return err;
}
#if GSO_ENABLED
offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
TUN_F_TSO_ECN | TUN_F_UFO;
offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN;
err = ioctl(fd, TUNSETOFFLOAD, offload);
if (err < 0) {
@ -332,7 +330,6 @@ static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type)
return d;
}
mpsslog("%s %s %d not found\n", mic->name, __func__, type);
assert(0);
return NULL;
}
@ -415,6 +412,13 @@ mic_virtio_copy(struct mic_info *mic, int fd,
return ret;
}
static inline unsigned _vring_size(unsigned int num, unsigned long align)
{
return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num)
+ align - 1) & ~(align - 1))
+ sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num;
}
/*
* This initialization routine requires at least one
* vring i.e. vr0. vr1 is optional.
@ -426,8 +430,9 @@ init_vr(struct mic_info *mic, int fd, int type,
int vr_size;
char *va;
vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES,
MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info));
vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES,
MIC_VIRTIO_RING_ALIGN) +
sizeof(struct _mic_vring_info));
va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq,
PROT_READ, MAP_SHARED, fd, 0);
if (MAP_FAILED == va) {
@ -439,25 +444,25 @@ init_vr(struct mic_info *mic, int fd, int type,
set_dp(mic, type, va);
vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END];
vr0->info = vr0->va +
vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN);
_vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN);
vring_init(&vr0->vr,
MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN);
mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ",
__func__, mic->name, vr0->va, vr0->info, vr_size,
vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
_vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
mpsslog("magic 0x%x expected 0x%x\n",
le32toh(vr0->info->magic), MIC_MAGIC + type);
assert(le32toh(vr0->info->magic) == MIC_MAGIC + type);
if (vr1) {
vr1->va = (struct mic_vring *)
&va[MIC_DEVICE_PAGE_END + vr_size];
vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES,
vr1->info = vr1->va + _vring_size(MIC_VRING_ENTRIES,
MIC_VIRTIO_RING_ALIGN);
vring_init(&vr1->vr,
MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN);
mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ",
__func__, mic->name, vr1->va, vr1->info, vr_size,
vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
_vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
mpsslog("magic 0x%x expected 0x%x\n",
le32toh(vr1->info->magic), MIC_MAGIC + type + 1);
assert(le32toh(vr1->info->magic) == MIC_MAGIC + type + 1);
@ -466,16 +471,21 @@ done:
return va;
}
static void
static int
wait_for_card_driver(struct mic_info *mic, int fd, int type)
{
struct pollfd pollfd;
int err;
struct mic_device_desc *desc = get_device_desc(mic, type);
__u8 prev_status;
if (!desc)
return -ENODEV;
prev_status = desc->status;
pollfd.fd = fd;
mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n",
mic->name, __func__, type, desc->status);
while (1) {
pollfd.events = POLLIN;
pollfd.revents = 0;
@ -487,8 +497,13 @@ wait_for_card_driver(struct mic_info *mic, int fd, int type)
}
if (pollfd.revents) {
mpsslog("%s %s Waiting... desc-> type %d status 0x%x\n",
mic->name, __func__, type, desc->status);
if (desc->status != prev_status) {
mpsslog("%s %s Waiting... desc-> type %d "
"status 0x%x\n",
mic->name, __func__, type,
desc->status);
prev_status = desc->status;
}
if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
mpsslog("%s %s poll.revents %d\n",
mic->name, __func__, pollfd.revents);
@ -499,6 +514,7 @@ wait_for_card_driver(struct mic_info *mic, int fd, int type)
}
}
}
return 0;
}
/* Spin till we have some descriptors */
@ -575,9 +591,16 @@ virtio_net(void *arg)
__func__, strerror(errno));
continue;
}
if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK))
wait_for_card_driver(mic, mic->mic_net.virtio_net_fd,
VIRTIO_ID_NET);
if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
err = wait_for_card_driver(mic,
mic->mic_net.virtio_net_fd,
VIRTIO_ID_NET);
if (err) {
mpsslog("%s %s %d Exiting...\n",
mic->name, __func__, __LINE__);
break;
}
}
/*
* Check if there is data to be read from TUN and write to
* virtio net fd if there is.
@ -786,10 +809,16 @@ virtio_console(void *arg)
strerror(errno));
continue;
}
if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK))
wait_for_card_driver(mic,
mic->mic_console.virtio_console_fd,
VIRTIO_ID_CONSOLE);
if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
err = wait_for_card_driver(mic,
mic->mic_console.virtio_console_fd,
VIRTIO_ID_CONSOLE);
if (err) {
mpsslog("%s %s %d Exiting...\n",
mic->name, __func__, __LINE__);
break;
}
}
if (console_poll[MONITOR_FD].revents & POLLIN) {
copy.iov = iov0;
@ -1048,8 +1077,9 @@ stop_virtblk(struct mic_info *mic)
{
int vr_size, ret;
vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES,
MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info));
vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES,
MIC_VIRTIO_RING_ALIGN) +
sizeof(struct _mic_vring_info));
ret = munmap(mic->mic_virtblk.block_dp,
MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq);
if (ret < 0)
@ -1131,6 +1161,10 @@ write_status(int fd, __u8 *status)
return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
}
#ifndef VIRTIO_BLK_T_GET_ID
#define VIRTIO_BLK_T_GET_ID 8
#endif
static void *
virtio_block(void *arg)
{
@ -1297,12 +1331,7 @@ reset(struct mic_info *mic)
mpsslog("%s: %s %d state %s\n",
mic->name, __func__, __LINE__, state);
/*
* If the shutdown was initiated by OSPM, the state stays
* in "suspended" which is also a valid condition for reset.
*/
if ((!strcmp(state, "offline")) ||
(!strcmp(state, "suspended"))) {
if (!strcmp(state, "ready")) {
free(state);
break;
}
@ -1331,34 +1360,50 @@ get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status)
assert(0);
};
static int get_mic_state(struct mic_info *mic, char *state)
static int get_mic_state(struct mic_info *mic)
{
if (!strcmp(state, "offline"))
return MIC_OFFLINE;
if (!strcmp(state, "online"))
return MIC_ONLINE;
if (!strcmp(state, "shutting_down"))
return MIC_SHUTTING_DOWN;
if (!strcmp(state, "reset_failed"))
return MIC_RESET_FAILED;
if (!strcmp(state, "suspending"))
return MIC_SUSPENDING;
if (!strcmp(state, "suspended"))
return MIC_SUSPENDED;
mpsslog("%s: BUG invalid state %s\n", mic->name, state);
/* Invalid state */
assert(0);
char *state = NULL;
enum mic_states mic_state;
while (!state) {
state = readsysfs(mic->name, "state");
sleep(1);
}
mpsslog("%s: %s %d state %s\n",
mic->name, __func__, __LINE__, state);
if (!strcmp(state, "ready")) {
mic_state = MIC_READY;
} else if (!strcmp(state, "booting")) {
mic_state = MIC_BOOTING;
} else if (!strcmp(state, "online")) {
mic_state = MIC_ONLINE;
} else if (!strcmp(state, "shutting_down")) {
mic_state = MIC_SHUTTING_DOWN;
} else if (!strcmp(state, "reset_failed")) {
mic_state = MIC_RESET_FAILED;
} else if (!strcmp(state, "resetting")) {
mic_state = MIC_RESETTING;
} else {
mpsslog("%s: BUG invalid state %s\n", mic->name, state);
assert(0);
}
free(state);
return mic_state;
};
static void mic_handle_shutdown(struct mic_info *mic)
{
#define SHUTDOWN_TIMEOUT 60
int i = SHUTDOWN_TIMEOUT, ret, stat = 0;
int i = SHUTDOWN_TIMEOUT;
char *shutdown_status;
while (i) {
shutdown_status = readsysfs(mic->name, "shutdown_status");
if (!shutdown_status)
if (!shutdown_status) {
sleep(1);
continue;
}
mpsslog("%s: %s %d shutdown_status %s\n",
mic->name, __func__, __LINE__, shutdown_status);
switch (get_mic_shutdown_status(mic, shutdown_status)) {
@ -1377,94 +1422,110 @@ static void mic_handle_shutdown(struct mic_info *mic)
i--;
}
reset:
ret = kill(mic->pid, SIGTERM);
mpsslog("%s: %s %d kill pid %d ret %d\n",
mic->name, __func__, __LINE__,
mic->pid, ret);
if (!ret) {
ret = waitpid(mic->pid, &stat,
WIFSIGNALED(stat));
mpsslog("%s: %s %d waitpid ret %d pid %d\n",
mic->name, __func__, __LINE__,
ret, mic->pid);
if (!i)
mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n",
mic->name, __func__, __LINE__, shutdown_status);
reset(mic);
}
static int open_state_fd(struct mic_info *mic)
{
char pathname[PATH_MAX];
int fd;
snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
MICSYSFSDIR, mic->name, "state");
fd = open(pathname, O_RDONLY);
if (fd < 0)
mpsslog("%s: opening file %s failed %s\n",
mic->name, pathname, strerror(errno));
return fd;
}
static int block_till_state_change(int fd, struct mic_info *mic)
{
struct pollfd ufds[1];
char value[PAGE_SIZE];
int ret;
ufds[0].fd = fd;
ufds[0].events = POLLERR | POLLPRI;
ret = poll(ufds, 1, -1);
if (ret < 0) {
mpsslog("%s: %s %d poll failed %s\n",
mic->name, __func__, __LINE__, strerror(errno));
return ret;
}
if (ret == mic->pid)
reset(mic);
ret = lseek(fd, 0, SEEK_SET);
if (ret < 0) {
mpsslog("%s: %s %d Failed to seek to 0: %s\n",
mic->name, __func__, __LINE__, strerror(errno));
return ret;
}
ret = read(fd, value, sizeof(value));
if (ret < 0) {
mpsslog("%s: %s %d Failed to read sysfs entry: %s\n",
mic->name, __func__, __LINE__, strerror(errno));
return ret;
}
return 0;
}
static void *
mic_config(void *arg)
{
struct mic_info *mic = (struct mic_info *)arg;
char *state = NULL;
char pathname[PATH_MAX];
int fd, ret;
struct pollfd ufds[1];
char value[4096];
int fd, ret, stat = 0;
snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
MICSYSFSDIR, mic->name, "state");
fd = open(pathname, O_RDONLY);
fd = open_state_fd(mic);
if (fd < 0) {
mpsslog("%s: opening file %s failed %s\n",
mic->name, pathname, strerror(errno));
goto error;
mpsslog("%s: %s %d open state fd failed %s\n",
mic->name, __func__, __LINE__, strerror(errno));
goto exit;
}
do {
ret = lseek(fd, 0, SEEK_SET);
ret = block_till_state_change(fd, mic);
if (ret < 0) {
mpsslog("%s: Failed to seek to file start '%s': %s\n",
mic->name, pathname, strerror(errno));
goto close_error1;
mpsslog("%s: %s %d block_till_state_change error %s\n",
mic->name, __func__, __LINE__, strerror(errno));
goto close_exit;
}
ret = read(fd, value, sizeof(value));
if (ret < 0) {
mpsslog("%s: Failed to read sysfs entry '%s': %s\n",
mic->name, pathname, strerror(errno));
goto close_error1;
}
retry:
state = readsysfs(mic->name, "state");
if (!state)
goto retry;
mpsslog("%s: %s %d state %s\n",
mic->name, __func__, __LINE__, state);
switch (get_mic_state(mic, state)) {
switch (get_mic_state(mic)) {
case MIC_SHUTTING_DOWN:
mic_handle_shutdown(mic);
goto close_error;
case MIC_SUSPENDING:
mic->boot_on_resume = 1;
setsysfs(mic->name, "state", "suspend");
mic_handle_shutdown(mic);
goto close_error;
case MIC_OFFLINE:
break;
case MIC_READY:
case MIC_RESET_FAILED:
ret = kill(mic->pid, SIGTERM);
mpsslog("%s: %s %d kill pid %d ret %d\n",
mic->name, __func__, __LINE__,
mic->pid, ret);
if (!ret) {
ret = waitpid(mic->pid, &stat,
WIFSIGNALED(stat));
mpsslog("%s: %s %d waitpid ret %d pid %d\n",
mic->name, __func__, __LINE__,
ret, mic->pid);
}
if (mic->boot_on_resume) {
setsysfs(mic->name, "state", "boot");
mic->boot_on_resume = 0;
}
break;
goto close_exit;
default:
break;
}
free(state);
ufds[0].fd = fd;
ufds[0].events = POLLERR | POLLPRI;
ret = poll(ufds, 1, -1);
if (ret < 0) {
mpsslog("%s: poll failed %s\n",
mic->name, strerror(errno));
goto close_error1;
}
} while (1);
close_error:
free(state);
close_error1:
close_exit:
close(fd);
error:
exit:
init_mic(mic);
pthread_exit(NULL);
}
@ -1477,15 +1538,15 @@ set_cmdline(struct mic_info *mic)
len = snprintf(buffer, PATH_MAX,
"clocksource=tsc highres=off nohz=off ");
len += snprintf(buffer + len, PATH_MAX - len,
len += snprintf(buffer + len, PATH_MAX,
"cpufreq_on;corec6_off;pc3_off;pc6_off ");
len += snprintf(buffer + len, PATH_MAX - len,
len += snprintf(buffer + len, PATH_MAX,
"ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0",
mic->id);
mic->id + 1);
setsysfs(mic->name, "cmdline", buffer);
mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer);
snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id);
snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id + 1);
mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer);
}
@ -1541,8 +1602,6 @@ set_log_buf_info(struct mic_info *mic)
close(fd);
}
static void init_mic(struct mic_info *mic);
static void
change_virtblk_backend(int x, siginfo_t *siginfo, void *p)
{
@ -1553,8 +1612,16 @@ change_virtblk_backend(int x, siginfo_t *siginfo, void *p)
}
static void
init_mic(struct mic_info *mic)
set_mic_boot_params(struct mic_info *mic)
{
set_log_buf_info(mic);
set_cmdline(mic);
}
static void *
init_mic(void *arg)
{
struct mic_info *mic = (struct mic_info *)arg;
struct sigaction ignore = {
.sa_flags = 0,
.sa_handler = SIG_IGN
@ -1564,7 +1631,7 @@ init_mic(struct mic_info *mic)
.sa_sigaction = change_virtblk_backend,
};
char buffer[PATH_MAX];
int err;
int err, fd;
/*
* Currently, one virtio block device is supported for each MIC card
@ -1577,12 +1644,38 @@ init_mic(struct mic_info *mic)
* the MIC daemon.
*/
sigaction(SIGUSR1, &ignore, NULL);
retry:
fd = open_state_fd(mic);
if (fd < 0) {
mpsslog("%s: %s %d open state fd failed %s\n",
mic->name, __func__, __LINE__, strerror(errno));
sleep(2);
goto retry;
}
if (mic->restart) {
snprintf(buffer, PATH_MAX, "boot");
setsysfs(mic->name, "state", buffer);
mpsslog("%s restarting mic %d\n",
mic->name, mic->restart);
mic->restart = 0;
}
while (1) {
while (block_till_state_change(fd, mic)) {
mpsslog("%s: %s %d block_till_state_change error %s\n",
mic->name, __func__, __LINE__, strerror(errno));
sleep(2);
continue;
}
if (get_mic_state(mic) == MIC_BOOTING)
break;
}
mic->pid = fork();
switch (mic->pid) {
case 0:
set_log_buf_info(mic);
set_cmdline(mic);
add_virtio_device(mic, &virtcons_dev_page.dd);
add_virtio_device(mic, &virtnet_dev_page.dd);
err = pthread_create(&mic->mic_console.console_thread, NULL,
@ -1612,24 +1705,29 @@ init_mic(struct mic_info *mic)
mic->name, mic->id, errno);
break;
default:
if (mic->restart) {
snprintf(buffer, PATH_MAX, "boot");
setsysfs(mic->name, "state", buffer);
mpsslog("%s restarting mic %d\n",
mic->name, mic->restart);
mic->restart = 0;
}
pthread_create(&mic->config_thread, NULL, mic_config, mic);
err = pthread_create(&mic->config_thread, NULL,
mic_config, mic);
if (err)
mpsslog("%s mic_config pthread_create failed %s\n",
mic->name, strerror(err));
}
return NULL;
}
static void
start_daemon(void)
{
struct mic_info *mic;
int err;
for (mic = mic_list.next; mic != NULL; mic = mic->next)
init_mic(mic);
for (mic = mic_list.next; mic; mic = mic->next) {
set_mic_boot_params(mic);
err = pthread_create(&mic->init_thread, NULL, init_mic, mic);
if (err)
mpsslog("%s init_mic pthread_create failed %s\n",
mic->name, strerror(err));
}
while (1)
sleep(60);

View File

@ -86,6 +86,7 @@ struct mic_info {
int id;
char *name;
pthread_t config_thread;
pthread_t init_thread;
pid_t pid;
struct mic_console_info mic_console;
struct mic_net_info mic_net;

View File

@ -0,0 +1,99 @@
Intel(R) Trace Hub (TH)
=======================
Overview
--------
Intel(R) Trace Hub (TH) is a set of hardware blocks that produce,
switch and output trace data from multiple hardware and software
sources over several types of trace output ports encoded in System
Trace Protocol (MIPI STPv2) and is intended to perform full system
debugging. For more information on the hardware, see Intel(R) Trace
Hub developer's manual [1].
It consists of trace sources, trace destinations (outputs) and a
switch (Global Trace Hub, GTH). These devices are placed on a bus of
their own ("intel_th"), where they can be discovered and configured
via sysfs attributes.
Currently, the following Intel TH subdevices (blocks) are supported:
- Software Trace Hub (STH), trace source, which is a System Trace
Module (STM) device,
- Memory Storage Unit (MSU), trace output, which allows storing
trace hub output in system memory,
- Parallel Trace Interface output (PTI), trace output to an external
debug host via a PTI port,
- Global Trace Hub (GTH), which is a switch and a central component
of Intel(R) Trace Hub architecture.
Common attributes for output devices are described in
Documentation/ABI/testing/sysfs-bus-intel_th-output-devices, the most
notable of them is "active", which enables or disables trace output
into that particular output device.
GTH allows directing different STP masters into different output ports
via its "masters" attribute group. More detailed GTH interface
description is at Documentation/ABI/testing/sysfs-bus-intel_th-devices-gth.
STH registers an stm class device, through which it provides interface
to userspace and kernelspace software trace sources. See
Documentation/tracing/stm.txt for more information on that.
MSU can be configured to collect trace data into a system memory
buffer, which can later on be read from its device nodes via read() or
mmap() interface.
On the whole, Intel(R) Trace Hub does not require any special
userspace software to function; everything can be configured, started
and collected via sysfs attributes, and device nodes.
[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
Bus and Subdevices
------------------
For each Intel TH device in the system a bus of its own is
created and assigned an id number that reflects the order in which TH
devices were emumerated. All TH subdevices (devices on intel_th bus)
begin with this id: 0-gth, 0-msc0, 0-msc1, 0-pti, 0-sth, which is
followed by device's name and an optional index.
Output devices also get a device node in /dev/intel_thN, where N is
the Intel TH device id. For example, MSU's memory buffers, when
allocated, are accessible via /dev/intel_th0/msc{0,1}.
Quick example
-------------
# figure out which GTH port is the first memory controller:
$ cat /sys/bus/intel_th/devices/0-msc0/port
0
# looks like it's port 0, configure master 33 to send data to port 0:
$ echo 0 > /sys/bus/intel_th/devices/0-gth/masters/33
# allocate a 2-windowed multiblock buffer on the first memory
# controller, each with 64 pages:
$ echo multi > /sys/bus/intel_th/devices/0-msc0/mode
$ echo 64,64 > /sys/bus/intel_th/devices/0-msc0/nr_pages
# enable wrapping for this controller, too:
$ echo 1 > /sys/bus/intel_th/devices/0-msc0/wrap
# and enable tracing into this port:
$ echo 1 > /sys/bus/intel_th/devices/0-msc0/active
# .. send data to master 33, see stm.txt for more details ..
# .. wait for traces to pile up ..
# .. and stop the trace:
$ echo 0 > /sys/bus/intel_th/devices/0-msc0/active
# and now you can collect the trace from the device node:
$ cat /dev/intel_th0/msc0 > my_stp_trace

View File

@ -0,0 +1,80 @@
System Trace Module
===================
System Trace Module (STM) is a device described in MIPI STP specs as
STP trace stream generator. STP (System Trace Protocol) is a trace
protocol multiplexing data from multiple trace sources, each one of
which is assigned a unique pair of master and channel. While some of
these masters and channels are statically allocated to certain
hardware trace sources, others are available to software. Software
trace sources are usually free to pick for themselves any
master/channel combination from this pool.
On the receiving end of this STP stream (the decoder side), trace
sources can only be identified by master/channel combination, so in
order for the decoder to be able to make sense of the trace that
involves multiple trace sources, it needs to be able to map those
master/channel pairs to the trace sources that it understands.
For instance, it is helpful to know that syslog messages come on
master 7 channel 15, while arbitrary user applications can use masters
48 to 63 and channels 0 to 127.
To solve this mapping problem, stm class provides a policy management
mechanism via configfs, that allows defining rules that map string
identifiers to ranges of masters and channels. If these rules (policy)
are consistent with what decoder expects, it will be able to properly
process the trace data.
This policy is a tree structure containing rules (policy_node) that
have a name (string identifier) and a range of masters and channels
associated with it, located in "stp-policy" subsystem directory in
configfs. The topmost directory's name (the policy) is formatted as
the STM device name to which this policy applies and and arbitrary
string identifier separated by a stop. From the examle above, a rule
may look like this:
$ ls /config/stp-policy/dummy_stm.my-policy/user
channels masters
$ cat /config/stp-policy/dummy_stm.my-policy/user/masters
48 63
$ cat /config/stp-policy/dummy_stm.my-policy/user/channels
0 127
which means that the master allocation pool for this rule consists of
masters 48 through 63 and channel allocation pool has channels 0
through 127 in it. Now, any producer (trace source) identifying itself
with "user" identification string will be allocated a master and
channel from within these ranges.
These rules can be nested, for example, one can define a rule "dummy"
under "user" directory from the example above and this new rule will
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 data into its file descriptor. In order to identify themselves
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
automatically allocated a master/channel pair upon first write to this
file descriptor according to the "default" rule of the policy, if such
exists.
Some STM devices may allow direct mapping of the channel mmio regions
to userspace for zero-copy writing. One mappable page (in terms of
mmu) will usually contain multiple channels' mmios, so the user will
need to allocate that many channels to themselves (via the
aforementioned ioctl() call) to be able to do this. That is, if your
stm device's channel mmio region is 64 bytes and hardware page size is
4096 bytes, after a successful STP_POLICY_ID_SET ioctl() call with
width==64, you should be able to mmap() one page on this file
descriptor and obtain direct access to an mmio region for 64 channels.
For kernel-based trace sources, there is "stm_source" device
class. Devices of this class can be connected and disconnected to/from
stm devices at runtime via a sysfs attribute.
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
[2].
[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
[2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html

View File

@ -44,3 +44,9 @@ e.g:
insmod omap_hdq.ko W1_ID=2
inamod w1_bq27000.ko F_ID=2
The driver also supports 1-wire mode. In this mode, there is no need to
pass slave ID as parameter. The driver will auto-detect slaves connected
to the bus using SEARCH_ROM procedure. 1-wire mode can be selected by
setting "ti,mode" property to "1w" in DT (see
Documentation/devicetree/bindings/w1/omap-hdq.txt for more details).
By default driver is in HDQ mode.

View File

@ -4355,6 +4355,13 @@ F: include/linux/fmc*.h
F: include/linux/ipmi-fru.h
K: fmc_d.*register
FPGA MANAGER FRAMEWORK
M: Alan Tull <atull@opensource.altera.com>
S: Maintained
F: drivers/fpga/
F: include/linux/fpga/fpga-mgr.h
W: http://www.rocketboards.org
FPU EMULATOR
M: Bill Metzenthen <billm@melbpc.org.au>
W: http://floatingpoint.sourceforge.net/emulator/index.html
@ -5551,6 +5558,12 @@ F: Documentation/networking/README.ipw2100
F: Documentation/networking/README.ipw2200
F: drivers/net/wireless/ipw2x00/
INTEL(R) TRACE HUB
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
S: Supported
F: Documentation/trace/intel_th.txt
F: drivers/hwtracing/intel_th/
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
M: Richard L Maliszewski <richard.l.maliszewski@intel.com>
M: Gang Wei <gang.wei@intel.com>
@ -5598,6 +5611,22 @@ F: include/linux/mei_cl_bus.h
F: drivers/misc/mei/*
F: Documentation/misc-devices/mei/*
INTEL MIC DRIVERS (mic)
M: Sudeep Dutt <sudeep.dutt@intel.com>
M: Ashutosh Dixit <ashutosh.dixit@intel.com>
S: Supported
W: https://github.com/sudeepdutt/mic
W: http://software.intel.com/en-us/mic-developer
F: include/linux/mic_bus.h
F: include/linux/scif.h
F: include/uapi/linux/mic_common.h
F: include/uapi/linux/mic_ioctl.h
F include/uapi/linux/scif_ioctl.h
F: drivers/misc/mic/
F: drivers/dma/mic_x100_dma.c
F: drivers/dma/mic_x100_dma.h
F Documentation/mic/
INTEL PMC IPC DRIVER
M: Zha Qipeng<qipeng.zha@intel.com>
L: platform-driver-x86@vger.kernel.org
@ -9214,6 +9243,14 @@ S: Maintained
F: include/linux/mmc/dw_mmc.h
F: drivers/mmc/host/dw_mmc*
SYSTEM TRACE MODULE CLASS
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
S: Maintained
F: Documentation/trace/stm.txt
F: drivers/hwtracing/stm/
F: include/linux/stm.h
F: include/uapi/linux/stm.h
THUNDERBOLT DRIVER
M: Andreas Noever <andreas.noever@gmail.com>
S: Maintained

View File

@ -294,6 +294,11 @@
devcfg: devcfg@f8007000 {
compatible = "xlnx,zynq-devcfg-1.0";
reg = <0xf8007000 0x100>;
interrupt-parent = <&intc>;
interrupts = <0 8 4>;
clocks = <&clkc 12>;
clock-names = "ref_clk";
syscon = <&slcr>;
};
global_timer: timer@f8f00200 {

View File

@ -192,4 +192,10 @@ source "drivers/nvdimm/Kconfig"
source "drivers/nvmem/Kconfig"
source "drivers/hwtracing/stm/Kconfig"
source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/fpga/Kconfig"
endmenu

View File

@ -167,5 +167,8 @@ obj-$(CONFIG_PERF_EVENTS) += perf/
obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
obj-y += hwtracing/intel_th/
obj-$(CONFIG_STM) += hwtracing/stm/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/

View File

@ -30,7 +30,6 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/proc_fs.h>
@ -395,14 +394,8 @@ efi_rtc_init(void)
}
return 0;
}
device_initcall(efi_rtc_init);
static void __exit
efi_rtc_exit(void)
{
/* not yet used */
}
module_init(efi_rtc_init);
module_exit(efi_rtc_exit);
/*
MODULE_LICENSE("GPL");
*/

View File

@ -12,7 +12,6 @@
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
@ -1043,24 +1042,16 @@ static int hpet_acpi_add(struct acpi_device *device)
return hpet_alloc(&data);
}
static int hpet_acpi_remove(struct acpi_device *device)
{
/* XXX need to unregister clocksource, dealloc mem, etc */
return -EINVAL;
}
static const struct acpi_device_id hpet_device_ids[] = {
{"PNP0103", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, hpet_device_ids);
static struct acpi_driver hpet_acpi_driver = {
.name = "hpet",
.ids = hpet_device_ids,
.ops = {
.add = hpet_acpi_add,
.remove = hpet_acpi_remove,
},
};
@ -1086,19 +1077,9 @@ static int __init hpet_init(void)
return 0;
}
device_initcall(hpet_init);
static void __exit hpet_exit(void)
{
acpi_bus_unregister_driver(&hpet_acpi_driver);
if (sysctl_header)
unregister_sysctl_table(sysctl_header);
misc_deregister(&hpet_misc);
return;
}
module_init(hpet_init);
module_exit(hpet_exit);
/*
MODULE_AUTHOR("Bob Picco <Robert.Picco@hp.com>");
MODULE_LICENSE("GPL");
*/

View File

@ -52,6 +52,7 @@
#include <linux/kthread.h>
#include <linux/acpi.h>
#include <linux/ctype.h>
#include <linux/time64.h>
#define PFX "ipmi_ssif: "
#define DEVICE_NAME "ipmi_ssif"
@ -1041,12 +1042,12 @@ static void sender(void *send_info,
start_next_msg(ssif_info, flags);
if (ssif_info->ssif_debug & SSIF_DEBUG_TIMING) {
struct timeval t;
struct timespec64 t;
do_gettimeofday(&t);
pr_info("**Enqueue %02x %02x: %ld.%6.6ld\n",
ktime_get_real_ts64(&t);
pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n",
msg->data[0], msg->data[1],
(long) t.tv_sec, (long) t.tv_usec);
(long long) t.tv_sec, (long) t.tv_nsec / NSEC_PER_USEC);
}
}

View File

@ -19,7 +19,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <asm/sn/io.h>
@ -461,5 +461,4 @@ scdrv_init(void)
}
return 0;
}
module_init(scdrv_init);
device_initcall(scdrv_init);

View File

@ -193,8 +193,16 @@ static void mic_dma_prog_intr(struct mic_dma_chan *ch)
static int mic_dma_do_dma(struct mic_dma_chan *ch, int flags, dma_addr_t src,
dma_addr_t dst, size_t len)
{
if (-ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len))
if (len && -ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len)) {
return -ENOMEM;
} else {
/* 3 is the maximum number of status descriptors */
int ret = mic_dma_avail_desc_ring_space(ch, 3);
if (ret < 0)
return ret;
}
/* Above mic_dma_prog_memcpy_desc() makes sure we have enough space */
if (flags & DMA_PREP_FENCE) {
mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0,
@ -270,6 +278,33 @@ allocate_tx(struct mic_dma_chan *ch)
return tx;
}
/* Program a status descriptor with dst as address and value to be written */
static struct dma_async_tx_descriptor *
mic_dma_prep_status_lock(struct dma_chan *ch, dma_addr_t dst, u64 src_val,
unsigned long flags)
{
struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch);
int result;
spin_lock(&mic_ch->prep_lock);
result = mic_dma_avail_desc_ring_space(mic_ch, 4);
if (result < 0)
goto error;
mic_dma_prep_status_desc(&mic_ch->desc_ring[mic_ch->head], src_val, dst,
false);
mic_dma_hw_ring_inc_head(mic_ch);
result = mic_dma_do_dma(mic_ch, flags, 0, 0, 0);
if (result < 0)
goto error;
return allocate_tx(mic_ch);
error:
dev_err(mic_dma_ch_to_device(mic_ch),
"Error enqueueing dma status descriptor, error=%d\n", result);
spin_unlock(&mic_ch->prep_lock);
return NULL;
}
/*
* Prepare a memcpy descriptor to be added to the ring.
* Note that the temporary descriptor adds an extra overhead of copying the
@ -587,6 +622,8 @@ static int mic_dma_register_dma_device(struct mic_dma_device *mic_dma_dev,
mic_dma_free_chan_resources;
mic_dma_dev->dma_dev.device_tx_status = mic_dma_tx_status;
mic_dma_dev->dma_dev.device_prep_dma_memcpy = mic_dma_prep_memcpy_lock;
mic_dma_dev->dma_dev.device_prep_dma_imm_data =
mic_dma_prep_status_lock;
mic_dma_dev->dma_dev.device_prep_dma_interrupt =
mic_dma_prep_interrupt_lock;
mic_dma_dev->dma_dev.device_issue_pending = mic_dma_issue_pending;

View File

@ -1,7 +1,7 @@
/*
* extcon-arizona.c - Extcon driver Wolfson Arizona devices
*
* Copyright (C) 2012 Wolfson Microelectronics plc
* Copyright (C) 2012-2014 Wolfson Microelectronics plc
*
* 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
@ -43,11 +43,18 @@
#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb
#define ARIZONA_TST_CAP_DEFAULT 0x3
#define ARIZONA_TST_CAP_CLAMP 0x1
#define ARIZONA_HPDET_MAX 10000
#define HPDET_DEBOUNCE 500
#define DEFAULT_MICD_TIMEOUT 2000
#define QUICK_HEADPHONE_MAX_OHM 3
#define MICROPHONE_MIN_OHM 1257
#define MICROPHONE_MAX_OHM 30000
#define MICD_DBTIME_TWO_READINGS 2
#define MICD_DBTIME_FOUR_READINGS 4
@ -117,19 +124,22 @@ static const struct arizona_micd_range micd_default_ranges[] = {
{ .max = 430, .key = BTN_5 },
};
/* The number of levels in arizona_micd_levels valid for button thresholds */
#define ARIZONA_NUM_MICD_BUTTON_LEVELS 64
static const int arizona_micd_levels[] = {
3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
1257,
1257, 30000,
};
static const unsigned int arizona_cable[] = {
EXTCON_MECHANICAL,
EXTCON_MICROPHONE,
EXTCON_HEADPHONE,
EXTCON_LINE_OUT,
EXTCON_JACK_MICROPHONE,
EXTCON_JACK_HEADPHONE,
EXTCON_JACK_LINE_OUT,
EXTCON_NONE,
};
@ -140,17 +150,33 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
{
struct arizona *arizona = info->arizona;
unsigned int mask = 0, val = 0;
unsigned int cap_sel = 0;
int ret;
switch (arizona->type) {
case WM8998:
case WM1814:
mask = 0;
break;
case WM5110:
case WM8280:
mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
ARIZONA_HP1L_SHRTI;
if (clamp)
if (clamp) {
val = ARIZONA_HP1L_SHRTO;
else
cap_sel = ARIZONA_TST_CAP_CLAMP;
} else {
val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
cap_sel = ARIZONA_TST_CAP_DEFAULT;
}
ret = regmap_update_bits(arizona->regmap,
ARIZONA_HP_TEST_CTRL_1,
ARIZONA_HP1_TST_CAP_SEL_MASK,
cap_sel);
if (ret != 0)
dev_warn(arizona->dev,
"Failed to set TST_CAP_SEL: %d\n", ret);
break;
default:
mask = ARIZONA_RMV_SHRT_HP1L;
@ -175,17 +201,19 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
ret);
}
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
mask, val);
if (ret != 0)
dev_warn(arizona->dev, "Failed to do clamp: %d\n",
if (mask) {
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
mask, val);
if (ret != 0)
dev_warn(arizona->dev, "Failed to do clamp: %d\n",
ret);
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
mask, val);
if (ret != 0)
dev_warn(arizona->dev, "Failed to do clamp: %d\n",
ret);
ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
mask, val);
if (ret != 0)
dev_warn(arizona->dev, "Failed to do clamp: %d\n",
ret);
}
/* Restore the desired state while not doing the clamp */
if (!clamp) {
@ -270,6 +298,7 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
struct arizona *arizona = info->arizona;
bool change;
int ret;
unsigned int mode;
/* Microphone detection can't use idle mode */
pm_runtime_get(info->dev);
@ -295,9 +324,14 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
regmap_write(arizona->regmap, 0x80, 0x0);
}
if (info->detecting && arizona->pdata.micd_software_compare)
mode = ARIZONA_ACCDET_MODE_ADC;
else
mode = ARIZONA_ACCDET_MODE_MIC;
regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
ARIZONA_ACCDET_MODE_MASK, mode);
arizona_extcon_pulse_micbias(info);
@ -443,9 +477,6 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
arizona_hpdet_b_ranges[range].factor_a);
break;
default:
dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
info->hpdet_ip_version);
case 2:
if (!(val & ARIZONA_HP_DONE_B)) {
dev_err(arizona->dev, "HPDET did not complete: %x\n",
@ -482,6 +513,12 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
arizona_hpdet_c_ranges[range].min);
val = arizona_hpdet_c_ranges[range].min;
}
break;
default:
dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
info->hpdet_ip_version);
return -EINVAL;
}
dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
@ -563,7 +600,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
int id_gpio = arizona->pdata.hpdet_id_gpio;
unsigned int report = EXTCON_HEADPHONE;
unsigned int report = EXTCON_JACK_HEADPHONE;
int ret, reading;
bool mic = false;
@ -608,9 +645,9 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
/* Report high impedence cables as line outputs */
if (reading >= 5000)
report = EXTCON_LINE_OUT;
report = EXTCON_JACK_LINE_OUT;
else
report = EXTCON_HEADPHONE;
report = EXTCON_JACK_HEADPHONE;
ret = extcon_set_cable_state_(info->edev, report, true);
if (ret != 0)
@ -695,7 +732,7 @@ err:
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_set_cable_state_(info->edev, EXTCON_HEADPHONE, true);
ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
@ -752,7 +789,7 @@ err:
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_set_cable_state_(info->edev, EXTCON_HEADPHONE, true);
ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
@ -804,6 +841,37 @@ static void arizona_micd_detect(struct work_struct *work)
return;
}
if (info->detecting && arizona->pdata.micd_software_compare) {
/* Must disable MICD before we read the ADCVAL */
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA, 0);
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to read MICDET_ADCVAL: %d\n",
ret);
mutex_unlock(&info->lock);
return;
}
dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val);
val &= ARIZONA_MICDET_ADCVAL_MASK;
if (val < ARRAY_SIZE(arizona_micd_levels))
val = arizona_micd_levels[val];
else
val = INT_MAX;
if (val <= QUICK_HEADPHONE_MAX_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0;
else if (val <= MICROPHONE_MIN_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1;
else if (val <= MICROPHONE_MAX_OHM)
val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8;
else
val = ARIZONA_MICD_LVL_8;
}
for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
if (ret != 0) {
@ -847,7 +915,7 @@ static void arizona_micd_detect(struct work_struct *work)
arizona_identify_headphone(info);
ret = extcon_set_cable_state_(info->edev,
EXTCON_MICROPHONE, true);
EXTCON_JACK_MICROPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Headset report failed: %d\n",
ret);
@ -932,10 +1000,17 @@ static void arizona_micd_detect(struct work_struct *work)
}
handled:
if (info->detecting)
if (info->detecting) {
if (arizona->pdata.micd_software_compare)
regmap_update_bits(arizona->regmap,
ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA,
ARIZONA_MICD_ENA);
queue_delayed_work(system_power_efficient_wq,
&info->micd_timeout_work,
msecs_to_jiffies(info->micd_timeout));
}
pm_runtime_mark_last_busy(info->dev);
mutex_unlock(&info->lock);
@ -991,12 +1066,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
mutex_lock(&info->lock);
if (arizona->pdata.jd_gpio5) {
if (info->micd_clamp) {
mask = ARIZONA_MICD_CLAMP_STS;
if (arizona->pdata.jd_invert)
present = ARIZONA_MICD_CLAMP_STS;
else
present = 0;
present = 0;
} else {
mask = ARIZONA_JD1_STS;
if (arizona->pdata.jd_invert)
@ -1055,9 +1127,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
msecs_to_jiffies(HPDET_DEBOUNCE));
}
regmap_update_bits(arizona->regmap,
ARIZONA_JACK_DETECT_DEBOUNCE,
ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, 0);
if (info->micd_clamp || !arizona->pdata.jd_invert)
regmap_update_bits(arizona->regmap,
ARIZONA_JACK_DETECT_DEBOUNCE,
ARIZONA_MICD_CLAMP_DB |
ARIZONA_JD1_DB, 0);
} else {
dev_dbg(arizona->dev, "Detected jack removal\n");
@ -1224,6 +1298,11 @@ static int arizona_extcon_probe(struct platform_device *pdev)
break;
}
break;
case WM8998:
case WM1814:
info->micd_clamp = true;
info->hpdet_ip_version = 2;
break;
default:
break;
}
@ -1259,6 +1338,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
}
if (arizona->pdata.gpsw > 0)
regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1,
ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw);
if (arizona->pdata.micd_pol_gpio > 0) {
if (info->micd_modes[0].gpio)
mode = GPIOF_OUT_INIT_HIGH;
@ -1335,7 +1418,8 @@ static int arizona_extcon_probe(struct platform_device *pdev)
break;
}
BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40);
BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) <
ARIZONA_NUM_MICD_BUTTON_LEVELS);
if (arizona->pdata.num_micd_ranges) {
info->micd_ranges = pdata->micd_ranges;
@ -1368,11 +1452,11 @@ static int arizona_extcon_probe(struct platform_device *pdev)
/* Set up all the buttons the user specified */
for (i = 0; i < info->num_micd_ranges; i++) {
for (j = 0; j < ARRAY_SIZE(arizona_micd_levels); j++)
for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++)
if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
break;
if (j == ARRAY_SIZE(arizona_micd_levels)) {
if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) {
dev_err(arizona->dev, "Unsupported MICD level %d\n",
info->micd_ranges[i].max);
ret = -EINVAL;
@ -1436,7 +1520,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
pm_runtime_idle(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
if (arizona->pdata.jd_gpio5) {
if (info->micd_clamp) {
jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
} else {
@ -1541,7 +1625,7 @@ static int arizona_extcon_remove(struct platform_device *pdev)
ARIZONA_MICD_CLAMP_CONTROL,
ARIZONA_MICD_CLAMP_MODE_MASK, 0);
if (arizona->pdata.jd_gpio5) {
if (info->micd_clamp) {
jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
} else {

View File

@ -102,9 +102,9 @@ enum axp288_extcon_irq {
};
static const unsigned int axp288_extcon_cables[] = {
EXTCON_SLOW_CHARGER,
EXTCON_CHARGE_DOWNSTREAM,
EXTCON_FAST_CHARGER,
EXTCON_CHG_USB_SDP,
EXTCON_CHG_USB_CDP,
EXTCON_CHG_USB_DCP,
EXTCON_NONE,
};
@ -192,18 +192,18 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
dev_dbg(info->dev, "sdp cable is connecetd\n");
notify_otg = true;
notify_charger = true;
cable = EXTCON_SLOW_CHARGER;
cable = EXTCON_CHG_USB_SDP;
break;
case DET_STAT_CDP:
dev_dbg(info->dev, "cdp cable is connecetd\n");
notify_otg = true;
notify_charger = true;
cable = EXTCON_CHARGE_DOWNSTREAM;
cable = EXTCON_CHG_USB_CDP;
break;
case DET_STAT_DCP:
dev_dbg(info->dev, "dcp cable is connecetd\n");
notify_charger = true;
cable = EXTCON_FAST_CHARGER;
cable = EXTCON_CHG_USB_DCP;
break;
default:
dev_warn(info->dev,
@ -309,7 +309,7 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
/* Get otg transceiver phy */
info->otg = usb_get_phy(USB_PHY_TYPE_USB2);
info->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(info->otg)) {
dev_err(&pdev->dev, "failed to get otg transceiver\n");
return PTR_ERR(info->otg);
@ -318,11 +318,11 @@ static int axp288_extcon_probe(struct platform_device *pdev)
/* Set up gpio control for USB Mux */
if (info->pdata->gpio_mux_cntl) {
gpio = desc_to_gpio(info->pdata->gpio_mux_cntl);
ret = gpio_request(gpio, "USB_MUX");
ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX");
if (ret < 0) {
dev_err(&pdev->dev,
"failed to request the gpio=%d\n", gpio);
goto gpio_req_failed;
return ret;
}
gpiod_direction_output(info->pdata->gpio_mux_cntl,
EXTCON_GPIO_MUX_SEL_PMIC);
@ -335,7 +335,7 @@ static int axp288_extcon_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"failed to get virtual interrupt=%d\n", pirq);
ret = info->irq[i];
goto gpio_req_failed;
return ret;
}
ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
@ -345,7 +345,7 @@ static int axp288_extcon_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
info->irq[i]);
goto gpio_req_failed;
return ret;
}
}
@ -353,23 +353,10 @@ static int axp288_extcon_probe(struct platform_device *pdev)
axp288_extcon_enable_irq(info);
return 0;
gpio_req_failed:
usb_put_phy(info->otg);
return ret;
}
static int axp288_extcon_remove(struct platform_device *pdev)
{
struct axp288_extcon_info *info = platform_get_drvdata(pdev);
usb_put_phy(info->otg);
return 0;
}
static struct platform_driver axp288_extcon_driver = {
.probe = axp288_extcon_probe,
.remove = axp288_extcon_remove,
.driver = {
.name = "axp288_extcon",
},

View File

@ -1,7 +1,5 @@
/*
* drivers/extcon/extcon_gpio.c
*
* Single-state GPIO extcon driver based on extcon class
* extcon_gpio.c - Single-state GPIO extcon driver based on extcon class
*
* Copyright (C) 2008 Google, Inc.
* Author: Mike Lockwood <lockwood@android.com>
@ -17,12 +15,12 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
*/
#include <linux/extcon.h>
#include <linux/extcon/extcon-gpio.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@ -33,14 +31,12 @@
struct gpio_extcon_data {
struct extcon_dev *edev;
unsigned gpio;
bool gpio_active_low;
const char *state_on;
const char *state_off;
int irq;
struct delayed_work work;
unsigned long debounce_jiffies;
bool check_on_resume;
struct gpio_desc *id_gpiod;
struct gpio_extcon_pdata *pdata;
};
static void gpio_extcon_work(struct work_struct *work)
@ -50,93 +46,107 @@ static void gpio_extcon_work(struct work_struct *work)
container_of(to_delayed_work(work), struct gpio_extcon_data,
work);
state = gpio_get_value(data->gpio);
if (data->gpio_active_low)
state = gpiod_get_value_cansleep(data->id_gpiod);
if (data->pdata->gpio_active_low)
state = !state;
extcon_set_state(data->edev, state);
}
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
struct gpio_extcon_data *extcon_data = dev_id;
struct gpio_extcon_data *data = dev_id;
queue_delayed_work(system_power_efficient_wq, &extcon_data->work,
extcon_data->debounce_jiffies);
queue_delayed_work(system_power_efficient_wq, &data->work,
data->debounce_jiffies);
return IRQ_HANDLED;
}
static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data)
{
struct gpio_extcon_pdata *pdata = data->pdata;
int ret;
ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN,
dev_name(dev));
if (ret < 0)
return ret;
data->id_gpiod = gpio_to_desc(pdata->gpio);
if (!data->id_gpiod)
return -EINVAL;
if (pdata->debounce) {
ret = gpiod_set_debounce(data->id_gpiod,
pdata->debounce * 1000);
if (ret < 0)
data->debounce_jiffies =
msecs_to_jiffies(pdata->debounce);
}
data->irq = gpiod_to_irq(data->id_gpiod);
if (data->irq < 0)
return data->irq;
return 0;
}
static int gpio_extcon_probe(struct platform_device *pdev)
{
struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct gpio_extcon_data *extcon_data;
struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev);
struct gpio_extcon_data *data;
int ret;
if (!pdata)
return -EBUSY;
if (!pdata->irq_flags) {
dev_err(&pdev->dev, "IRQ flag is not specified.\n");
if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
return -EINVAL;
}
extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
GFP_KERNEL);
if (!extcon_data)
if (!data)
return -ENOMEM;
data->pdata = pdata;
extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
if (IS_ERR(extcon_data->edev)) {
/* Initialize the gpio */
ret = gpio_extcon_init(&pdev->dev, data);
if (ret < 0)
return ret;
/* Allocate the memory of extcon devie and register extcon device */
data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
if (IS_ERR(data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
extcon_data->gpio = pdata->gpio;
extcon_data->gpio_active_low = pdata->gpio_active_low;
extcon_data->state_on = pdata->state_on;
extcon_data->state_off = pdata->state_off;
extcon_data->check_on_resume = pdata->check_on_resume;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name);
ret = devm_extcon_dev_register(&pdev->dev, data->edev);
if (ret < 0)
return ret;
if (pdata->debounce) {
ret = gpio_set_debounce(extcon_data->gpio,
pdata->debounce * 1000);
if (ret < 0)
extcon_data->debounce_jiffies =
msecs_to_jiffies(pdata->debounce);
}
INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
/*
* Request the interrput of gpio to detect whether external connector
* is attached or detached.
*/
ret = devm_request_any_context_irq(&pdev->dev, data->irq,
gpio_irq_handler, pdata->irq_flags,
pdev->name, data);
if (ret < 0)
return ret;
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
if (extcon_data->irq < 0)
return extcon_data->irq;
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
pdata->irq_flags, pdev->name,
extcon_data);
if (ret < 0)
return ret;
platform_set_drvdata(pdev, extcon_data);
platform_set_drvdata(pdev, data);
/* Perform initial detection */
gpio_extcon_work(&extcon_data->work.work);
gpio_extcon_work(&data->work.work);
return 0;
}
static int gpio_extcon_remove(struct platform_device *pdev)
{
struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
struct gpio_extcon_data *data = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&extcon_data->work);
free_irq(extcon_data->irq, extcon_data);
cancel_delayed_work_sync(&data->work);
return 0;
}
@ -144,12 +154,12 @@ static int gpio_extcon_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int gpio_extcon_resume(struct device *dev)
{
struct gpio_extcon_data *extcon_data;
struct gpio_extcon_data *data;
extcon_data = dev_get_drvdata(dev);
if (extcon_data->check_on_resume)
data = dev_get_drvdata(dev);
if (data->pdata->check_on_resume)
queue_delayed_work(system_power_efficient_wq,
&extcon_data->work, extcon_data->debounce_jiffies);
&data->work, data->debounce_jiffies);
return 0;
}

View File

@ -150,10 +150,10 @@ enum max14577_muic_acc_type {
static const unsigned int max14577_extcon_cable[] = {
EXTCON_USB,
EXTCON_TA,
EXTCON_FAST_CHARGER,
EXTCON_SLOW_CHARGER,
EXTCON_CHARGE_DOWNSTREAM,
EXTCON_CHG_USB_DCP,
EXTCON_CHG_USB_FAST,
EXTCON_CHG_USB_SLOW,
EXTCON_CHG_USB_CDP,
EXTCON_JIG,
EXTCON_NONE,
};
@ -456,18 +456,19 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info)
extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
break;
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
case MAX14577_CHARGER_TYPE_NONE:

View File

@ -204,11 +204,11 @@ enum max77693_muic_acc_type {
static const unsigned int max77693_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_TA,
EXTCON_FAST_CHARGER,
EXTCON_SLOW_CHARGER,
EXTCON_CHARGE_DOWNSTREAM,
EXTCON_MHL,
EXTCON_CHG_USB_DCP,
EXTCON_CHG_USB_FAST,
EXTCON_CHG_USB_SLOW,
EXTCON_CHG_USB_CDP,
EXTCON_DISP_MHL,
EXTCON_JIG,
EXTCON_DOCK,
EXTCON_NONE,
@ -505,7 +505,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
return ret;
extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
goto out;
case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE: /* Dock-Desk */
dock_id = EXTCON_DOCK;
@ -605,7 +605,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
case MAX77693_MUIC_GND_MHL:
case MAX77693_MUIC_GND_MHL_VB:
/* MHL or MHL with USB/TA cable */
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
break;
default:
dev_err(info->dev, "failed to detect %s cable of gnd type\n",
@ -801,10 +801,11 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
* - Support charging through micro-usb port without
* data connection
*/
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
attached);
if (!cable_attached)
extcon_set_cable_state_(info->edev, EXTCON_MHL,
cable_attached);
extcon_set_cable_state_(info->edev,
EXTCON_DISP_MHL, cable_attached);
break;
}
@ -862,7 +863,7 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
extcon_set_cable_state_(info->edev, EXTCON_DOCK,
attached);
extcon_set_cable_state_(info->edev, EXTCON_MHL,
extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL,
attached);
break;
}
@ -901,20 +902,21 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
break;
case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
/* Only TA cable */
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
}
break;
case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX77693_CHARGER_TYPE_APPLE_500MA:
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX77693_CHARGER_TYPE_APPLE_1A_2A:
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
case MAX77693_CHARGER_TYPE_DEAD_BATTERY:

View File

@ -122,11 +122,11 @@ enum max77843_muic_charger_type {
static const unsigned int max77843_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_TA,
EXTCON_CHARGE_DOWNSTREAM,
EXTCON_FAST_CHARGER,
EXTCON_SLOW_CHARGER,
EXTCON_MHL,
EXTCON_CHG_USB_DCP,
EXTCON_CHG_USB_CDP,
EXTCON_CHG_USB_FAST,
EXTCON_CHG_USB_SLOW,
EXTCON_DISP_MHL,
EXTCON_JIG,
EXTCON_NONE,
};
@ -355,7 +355,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
break;
default:
dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
@ -494,7 +494,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX77843_MUIC_CHG_DEDICATED:
@ -504,7 +504,8 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
case MAX77843_MUIC_CHG_SPECIAL_500MA:
ret = max77843_muic_set_path(info,
@ -513,7 +514,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX77843_MUIC_CHG_SPECIAL_1A:
@ -523,7 +524,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
case MAX77843_MUIC_CHG_GND:
@ -532,9 +533,11 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
/* Charger cable on MHL accessory is attach or detach */
if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
extcon_set_cable_state_(info->edev, EXTCON_TA, true);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
true);
else if (gnd_type == MAX77843_MUIC_GND_MHL)
extcon_set_cable_state_(info->edev, EXTCON_TA, false);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
false);
break;
case MAX77843_MUIC_CHG_NONE:
break;

View File

@ -148,11 +148,11 @@ struct max8997_muic_info {
static const unsigned int max8997_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_TA,
EXTCON_FAST_CHARGER,
EXTCON_SLOW_CHARGER,
EXTCON_CHARGE_DOWNSTREAM,
EXTCON_MHL,
EXTCON_CHG_USB_DCP,
EXTCON_CHG_USB_FAST,
EXTCON_CHG_USB_SLOW,
EXTCON_CHG_USB_CDP,
EXTCON_DISP_MHL,
EXTCON_DOCK,
EXTCON_JIG,
EXTCON_NONE,
@ -403,7 +403,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)
return ret;
break;
case MAX8997_MUIC_ADC_MHL:
extcon_set_cable_state_(info->edev, EXTCON_MHL, attached);
extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
break;
case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:
case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON:
@ -486,18 +486,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)
}
break;
case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
extcon_set_cable_state_(info->edev, EXTCON_TA, attached);
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
case MAX8997_CHARGER_TYPE_500MA:
extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX8997_CHARGER_TYPE_1A:
extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER,
extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
default:

View File

@ -93,7 +93,7 @@ static struct reg_data rt8973a_reg_data[] = {
static const unsigned int rt8973a_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_TA,
EXTCON_CHG_USB_DCP,
EXTCON_JIG,
EXTCON_NONE,
};
@ -333,7 +333,7 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info,
con_sw = DM_DP_SWITCH_USB;
break;
case RT8973A_MUIC_ADC_TA:
id = EXTCON_TA;
id = EXTCON_CHG_USB_DCP;
con_sw = DM_DP_SWITCH_OPEN;
break;
case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:
@ -594,7 +594,7 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c,
for (i = 0; i < info->num_muic_irqs; i++) {
struct muic_irq *muic_irq = &info->muic_irqs[i];
unsigned int virq = 0;
int virq = 0;
virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
if (virq <= 0)
@ -658,6 +658,7 @@ static const struct of_device_id rt8973a_dt_match[] = {
{ .compatible = "richtek,rt8973a-muic" },
{ },
};
MODULE_DEVICE_TABLE(of, rt8973a_dt_match);
#ifdef CONFIG_PM_SLEEP
static int rt8973a_muic_suspend(struct device *dev)

View File

@ -95,7 +95,7 @@ static struct reg_data sm5502_reg_data[] = {
static const unsigned int sm5502_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_TA,
EXTCON_CHG_USB_DCP,
EXTCON_NONE,
};
@ -389,7 +389,7 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB;
break;
case SM5502_MUIC_ADC_OPEN_TA:
id = EXTCON_TA;
id = EXTCON_CHG_USB_DCP;
con_sw = DM_DP_SWITCH_OPEN;
vbus_sw = VBUSIN_SWITCH_VBUSOUT;
break;
@ -586,7 +586,7 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
for (i = 0; i < info->num_muic_irqs; i++) {
struct muic_irq *muic_irq = &info->muic_irqs[i];
unsigned int virq = 0;
int virq = 0;
virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
if (virq <= 0)
@ -650,6 +650,7 @@ static const struct of_device_id sm5502_dt_match[] = {
{ .compatible = "siliconmitus,sm5502-muic" },
{ },
};
MODULE_DEVICE_TABLE(of, sm5502_dt_match);
#ifdef CONFIG_PM_SLEEP
static int sm5502_muic_suspend(struct device *dev)

View File

@ -39,37 +39,40 @@
#define CABLE_NAME_MAX 30
static const char *extcon_name[] = {
[EXTCON_NONE] = "NONE",
[EXTCON_NONE] = "NONE",
/* USB external connector */
[EXTCON_USB] = "USB",
[EXTCON_USB_HOST] = "USB-HOST",
[EXTCON_USB] = "USB",
[EXTCON_USB_HOST] = "USB-HOST",
/* Charger external connector */
[EXTCON_TA] = "TA",
[EXTCON_FAST_CHARGER] = "FAST-CHARGER",
[EXTCON_SLOW_CHARGER] = "SLOW-CHARGER",
[EXTCON_CHARGE_DOWNSTREAM] = "CHARGE-DOWNSTREAM",
/* Charging external connector */
[EXTCON_CHG_USB_SDP] = "SDP",
[EXTCON_CHG_USB_DCP] = "DCP",
[EXTCON_CHG_USB_CDP] = "CDP",
[EXTCON_CHG_USB_ACA] = "ACA",
[EXTCON_CHG_USB_FAST] = "FAST-CHARGER",
[EXTCON_CHG_USB_SLOW] = "SLOW-CHARGER",
/* Audio/Video external connector */
[EXTCON_LINE_IN] = "LINE-IN",
[EXTCON_LINE_OUT] = "LINE-OUT",
[EXTCON_MICROPHONE] = "MICROPHONE",
[EXTCON_HEADPHONE] = "HEADPHONE",
/* Jack external connector */
[EXTCON_JACK_MICROPHONE] = "MICROPHONE",
[EXTCON_JACK_HEADPHONE] = "HEADPHONE",
[EXTCON_JACK_LINE_IN] = "LINE-IN",
[EXTCON_JACK_LINE_OUT] = "LINE-OUT",
[EXTCON_JACK_VIDEO_IN] = "VIDEO-IN",
[EXTCON_JACK_VIDEO_OUT] = "VIDEO-OUT",
[EXTCON_JACK_SPDIF_IN] = "SPDIF-IN",
[EXTCON_JACK_SPDIF_OUT] = "SPDIF-OUT",
[EXTCON_HDMI] = "HDMI",
[EXTCON_MHL] = "MHL",
[EXTCON_DVI] = "DVI",
[EXTCON_VGA] = "VGA",
[EXTCON_SPDIF_IN] = "SPDIF-IN",
[EXTCON_SPDIF_OUT] = "SPDIF-OUT",
[EXTCON_VIDEO_IN] = "VIDEO-IN",
[EXTCON_VIDEO_OUT] = "VIDEO-OUT",
/* Display external connector */
[EXTCON_DISP_HDMI] = "HDMI",
[EXTCON_DISP_MHL] = "MHL",
[EXTCON_DISP_DVI] = "DVI",
[EXTCON_DISP_VGA] = "VGA",
/* Etc external connector */
[EXTCON_DOCK] = "DOCK",
[EXTCON_JIG] = "JIG",
[EXTCON_MECHANICAL] = "MECHANICAL",
/* Miscellaneous external connector */
[EXTCON_DOCK] = "DOCK",
[EXTCON_JIG] = "JIG",
[EXTCON_MECHANICAL] = "MECHANICAL",
NULL,
};

View File

@ -0,0 +1,29 @@
#
# FPGA framework configuration
#
menu "FPGA Configuration Support"
config FPGA
tristate "FPGA Configuration Framework"
help
Say Y here if you want support for configuring FPGAs from the
kernel. The FPGA framework adds a FPGA manager class and FPGA
manager drivers.
if FPGA
config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA
help
FPGA manager driver support for Altera SOCFPGA.
config FPGA_MGR_ZYNQ_FPGA
tristate "Xilinx Zynq FPGA"
help
FPGA manager driver support for Xilinx Zynq FPGAs.
endif # FPGA
endmenu

View File

@ -0,0 +1,10 @@
#
# Makefile for the fpga framework and fpga manager drivers.
#
# Core FPGA Manager Framework
obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o

View File

@ -0,0 +1,380 @@
/*
* FPGA Manager Core
*
* Copyright (C) 2013-2015 Altera Corporation
*
* With code from the mailing list:
* Copyright (C) 2013 Xilinx, Inc.
*
* 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/firmware.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/slab.h>
static DEFINE_IDA(fpga_mgr_ida);
static struct class *fpga_mgr_class;
/**
* fpga_mgr_buf_load - load fpga from image in buffer
* @mgr: fpga manager
* @flags: flags setting fpga confuration modes
* @buf: buffer contain fpga image
* @count: byte count of buf
*
* Step the low level fpga manager through the device-specific steps of getting
* an FPGA ready to be configured, writing the image to it, then doing whatever
* post-configuration steps necessary. This code assumes the caller got the
* mgr pointer from of_fpga_mgr_get() and checked that it is not an error code.
*
* Return: 0 on success, negative error code otherwise.
*/
int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
size_t count)
{
struct device *dev = &mgr->dev;
int ret;
/*
* Call the low level driver's write_init function. This will do the
* device-specific things to get the FPGA into the state where it is
* ready to receive an FPGA image.
*/
mgr->state = FPGA_MGR_STATE_WRITE_INIT;
ret = mgr->mops->write_init(mgr, flags, buf, count);
if (ret) {
dev_err(dev, "Error preparing FPGA for writing\n");
mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
return ret;
}
/*
* Write the FPGA image to the FPGA.
*/
mgr->state = FPGA_MGR_STATE_WRITE;
ret = mgr->mops->write(mgr, buf, count);
if (ret) {
dev_err(dev, "Error while writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_ERR;
return ret;
}
/*
* After all the FPGA image has been written, do the device specific
* steps to finish and set the FPGA into operating mode.
*/
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
ret = mgr->mops->write_complete(mgr, flags);
if (ret) {
dev_err(dev, "Error after writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
return ret;
}
mgr->state = FPGA_MGR_STATE_OPERATING;
return 0;
}
EXPORT_SYMBOL_GPL(fpga_mgr_buf_load);
/**
* fpga_mgr_firmware_load - request firmware and load to fpga
* @mgr: fpga manager
* @flags: flags setting fpga confuration modes
* @image_name: name of image file on the firmware search path
*
* Request an FPGA image using the firmware class, then write out to the FPGA.
* Update the state before each step to provide info on what step failed if
* there is a failure. This code assumes the caller got the mgr pointer
* from of_fpga_mgr_get() and checked that it is not an error code.
*
* Return: 0 on success, negative error code otherwise.
*/
int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
const char *image_name)
{
struct device *dev = &mgr->dev;
const struct firmware *fw;
int ret;
dev_info(dev, "writing %s to %s\n", image_name, mgr->name);
mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ;
ret = request_firmware(&fw, image_name, dev);
if (ret) {
mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ_ERR;
dev_err(dev, "Error requesting firmware %s\n", image_name);
return ret;
}
ret = fpga_mgr_buf_load(mgr, flags, fw->data, fw->size);
if (ret)
return ret;
release_firmware(fw);
return 0;
}
EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load);
static const char * const state_str[] = {
[FPGA_MGR_STATE_UNKNOWN] = "unknown",
[FPGA_MGR_STATE_POWER_OFF] = "power off",
[FPGA_MGR_STATE_POWER_UP] = "power up",
[FPGA_MGR_STATE_RESET] = "reset",
/* requesting FPGA image from firmware */
[FPGA_MGR_STATE_FIRMWARE_REQ] = "firmware request",
[FPGA_MGR_STATE_FIRMWARE_REQ_ERR] = "firmware request error",
/* Preparing FPGA to receive image */
[FPGA_MGR_STATE_WRITE_INIT] = "write init",
[FPGA_MGR_STATE_WRITE_INIT_ERR] = "write init error",
/* Writing image to FPGA */
[FPGA_MGR_STATE_WRITE] = "write",
[FPGA_MGR_STATE_WRITE_ERR] = "write error",
/* Finishing configuration after image has been written */
[FPGA_MGR_STATE_WRITE_COMPLETE] = "write complete",
[FPGA_MGR_STATE_WRITE_COMPLETE_ERR] = "write complete error",
/* FPGA reports to be in normal operating mode */
[FPGA_MGR_STATE_OPERATING] = "operating",
};
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fpga_manager *mgr = to_fpga_manager(dev);
return sprintf(buf, "%s\n", mgr->name);
}
static ssize_t state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fpga_manager *mgr = to_fpga_manager(dev);
return sprintf(buf, "%s\n", state_str[mgr->state]);
}
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(state);
static struct attribute *fpga_mgr_attrs[] = {
&dev_attr_name.attr,
&dev_attr_state.attr,
NULL,
};
ATTRIBUTE_GROUPS(fpga_mgr);
static int fpga_mgr_of_node_match(struct device *dev, const void *data)
{
return dev->of_node == data;
}
/**
* of_fpga_mgr_get - get an exclusive reference to a fpga mgr
* @node: device node
*
* Given a device node, get an exclusive reference to a fpga mgr.
*
* Return: fpga manager struct or IS_ERR() condition containing error code.
*/
struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
struct fpga_manager *mgr;
struct device *dev;
int ret = -ENODEV;
dev = class_find_device(fpga_mgr_class, NULL, node,
fpga_mgr_of_node_match);
if (!dev)
return ERR_PTR(-ENODEV);
mgr = to_fpga_manager(dev);
if (!mgr)
goto err_dev;
/* Get exclusive use of fpga manager */
if (!mutex_trylock(&mgr->ref_mutex)) {
ret = -EBUSY;
goto err_dev;
}
if (!try_module_get(dev->parent->driver->owner))
goto err_ll_mod;
return mgr;
err_ll_mod:
mutex_unlock(&mgr->ref_mutex);
err_dev:
put_device(dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
/**
* fpga_mgr_put - release a reference to a fpga manager
* @mgr: fpga manager structure
*/
void fpga_mgr_put(struct fpga_manager *mgr)
{
module_put(mgr->dev.parent->driver->owner);
mutex_unlock(&mgr->ref_mutex);
put_device(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_put);
/**
* fpga_mgr_register - register a low level fpga manager driver
* @dev: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
*
* Return: 0 on success, negative error code otherwise.
*/
int fpga_mgr_register(struct device *dev, const char *name,
const struct fpga_manager_ops *mops,
void *priv)
{
struct fpga_manager *mgr;
const char *dt_label;
int id, ret;
if (!mops || !mops->write_init || !mops->write ||
!mops->write_complete || !mops->state) {
dev_err(dev, "Attempt to register without fpga_manager_ops\n");
return -EINVAL;
}
if (!name || !strlen(name)) {
dev_err(dev, "Attempt to register with no name!\n");
return -EINVAL;
}
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
goto error_kfree;
}
mutex_init(&mgr->ref_mutex);
mgr->name = name;
mgr->mops = mops;
mgr->priv = priv;
/*
* Initialize framework state by requesting low level driver read state
* from device. FPGA may be in reset mode or may have been programmed
* by bootloader or EEPROM.
*/
mgr->state = mgr->mops->state(mgr);
device_initialize(&mgr->dev);
mgr->dev.class = fpga_mgr_class;
mgr->dev.parent = dev;
mgr->dev.of_node = dev->of_node;
mgr->dev.id = id;
dev_set_drvdata(dev, mgr);
dt_label = of_get_property(mgr->dev.of_node, "label", NULL);
if (dt_label)
ret = dev_set_name(&mgr->dev, "%s", dt_label);
else
ret = dev_set_name(&mgr->dev, "fpga%d", id);
ret = device_add(&mgr->dev);
if (ret)
goto error_device;
dev_info(&mgr->dev, "%s registered\n", mgr->name);
return 0;
error_device:
ida_simple_remove(&fpga_mgr_ida, id);
error_kfree:
kfree(mgr);
return ret;
}
EXPORT_SYMBOL_GPL(fpga_mgr_register);
/**
* fpga_mgr_unregister - unregister a low level fpga manager driver
* @dev: fpga manager device from pdev
*/
void fpga_mgr_unregister(struct device *dev)
{
struct fpga_manager *mgr = dev_get_drvdata(dev);
dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
/*
* If the low level driver provides a method for putting fpga into
* a desired state upon unregister, do it.
*/
if (mgr->mops->fpga_remove)
mgr->mops->fpga_remove(mgr);
device_unregister(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
static void fpga_mgr_dev_release(struct device *dev)
{
struct fpga_manager *mgr = to_fpga_manager(dev);
ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
kfree(mgr);
}
static int __init fpga_mgr_class_init(void)
{
pr_info("FPGA manager framework\n");
fpga_mgr_class = class_create(THIS_MODULE, "fpga_manager");
if (IS_ERR(fpga_mgr_class))
return PTR_ERR(fpga_mgr_class);
fpga_mgr_class->dev_groups = fpga_mgr_groups;
fpga_mgr_class->dev_release = fpga_mgr_dev_release;
return 0;
}
static void __exit fpga_mgr_class_exit(void)
{
class_destroy(fpga_mgr_class);
ida_destroy(&fpga_mgr_ida);
}
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_DESCRIPTION("FPGA manager framework");
MODULE_LICENSE("GPL v2");
subsys_initcall(fpga_mgr_class_init);
module_exit(fpga_mgr_class_exit);

View File

@ -0,0 +1,616 @@
/*
* FPGA Manager Driver for Altera SOCFPGA
*
* Copyright (C) 2013-2015 Altera Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pm.h>
/* Register offsets */
#define SOCFPGA_FPGMGR_STAT_OFST 0x0
#define SOCFPGA_FPGMGR_CTL_OFST 0x4
#define SOCFPGA_FPGMGR_DCLKCNT_OFST 0x8
#define SOCFPGA_FPGMGR_DCLKSTAT_OFST 0xc
#define SOCFPGA_FPGMGR_GPIO_INTEN_OFST 0x830
#define SOCFPGA_FPGMGR_GPIO_INTMSK_OFST 0x834
#define SOCFPGA_FPGMGR_GPIO_INTTYPE_LEVEL_OFST 0x838
#define SOCFPGA_FPGMGR_GPIO_INT_POL_OFST 0x83c
#define SOCFPGA_FPGMGR_GPIO_INTSTAT_OFST 0x840
#define SOCFPGA_FPGMGR_GPIO_RAW_INTSTAT_OFST 0x844
#define SOCFPGA_FPGMGR_GPIO_PORTA_EOI_OFST 0x84c
#define SOCFPGA_FPGMGR_GPIO_EXT_PORTA_OFST 0x850
/* Register bit defines */
/* SOCFPGA_FPGMGR_STAT register mode field values */
#define SOCFPGA_FPGMGR_STAT_POWER_UP 0x0 /*ramping*/
#define SOCFPGA_FPGMGR_STAT_RESET 0x1
#define SOCFPGA_FPGMGR_STAT_CFG 0x2
#define SOCFPGA_FPGMGR_STAT_INIT 0x3
#define SOCFPGA_FPGMGR_STAT_USER_MODE 0x4
#define SOCFPGA_FPGMGR_STAT_UNKNOWN 0x5
#define SOCFPGA_FPGMGR_STAT_STATE_MASK 0x7
/* This is a flag value that doesn't really happen in this register field */
#define SOCFPGA_FPGMGR_STAT_POWER_OFF 0x0
#define MSEL_PP16_FAST_NOAES_NODC 0x0
#define MSEL_PP16_FAST_AES_NODC 0x1
#define MSEL_PP16_FAST_AESOPT_DC 0x2
#define MSEL_PP16_SLOW_NOAES_NODC 0x4
#define MSEL_PP16_SLOW_AES_NODC 0x5
#define MSEL_PP16_SLOW_AESOPT_DC 0x6
#define MSEL_PP32_FAST_NOAES_NODC 0x8
#define MSEL_PP32_FAST_AES_NODC 0x9
#define MSEL_PP32_FAST_AESOPT_DC 0xa
#define MSEL_PP32_SLOW_NOAES_NODC 0xc
#define MSEL_PP32_SLOW_AES_NODC 0xd
#define MSEL_PP32_SLOW_AESOPT_DC 0xe
#define SOCFPGA_FPGMGR_STAT_MSEL_MASK 0x000000f8
#define SOCFPGA_FPGMGR_STAT_MSEL_SHIFT 3
/* SOCFPGA_FPGMGR_CTL register */
#define SOCFPGA_FPGMGR_CTL_EN 0x00000001
#define SOCFPGA_FPGMGR_CTL_NCE 0x00000002
#define SOCFPGA_FPGMGR_CTL_NCFGPULL 0x00000004
#define CDRATIO_X1 0x00000000
#define CDRATIO_X2 0x00000040
#define CDRATIO_X4 0x00000080
#define CDRATIO_X8 0x000000c0
#define SOCFPGA_FPGMGR_CTL_CDRATIO_MASK 0x000000c0
#define SOCFPGA_FPGMGR_CTL_AXICFGEN 0x00000100
#define CFGWDTH_16 0x00000000
#define CFGWDTH_32 0x00000200
#define SOCFPGA_FPGMGR_CTL_CFGWDTH_MASK 0x00000200
/* SOCFPGA_FPGMGR_DCLKSTAT register */
#define SOCFPGA_FPGMGR_DCLKSTAT_DCNTDONE_E_DONE 0x1
/* SOCFPGA_FPGMGR_GPIO_* registers share the same bit positions */
#define SOCFPGA_FPGMGR_MON_NSTATUS 0x0001
#define SOCFPGA_FPGMGR_MON_CONF_DONE 0x0002
#define SOCFPGA_FPGMGR_MON_INIT_DONE 0x0004
#define SOCFPGA_FPGMGR_MON_CRC_ERROR 0x0008
#define SOCFPGA_FPGMGR_MON_CVP_CONF_DONE 0x0010
#define SOCFPGA_FPGMGR_MON_PR_READY 0x0020
#define SOCFPGA_FPGMGR_MON_PR_ERROR 0x0040
#define SOCFPGA_FPGMGR_MON_PR_DONE 0x0080
#define SOCFPGA_FPGMGR_MON_NCONFIG_PIN 0x0100
#define SOCFPGA_FPGMGR_MON_NSTATUS_PIN 0x0200
#define SOCFPGA_FPGMGR_MON_CONF_DONE_PIN 0x0400
#define SOCFPGA_FPGMGR_MON_FPGA_POWER_ON 0x0800
#define SOCFPGA_FPGMGR_MON_STATUS_MASK 0x0fff
#define SOCFPGA_FPGMGR_NUM_SUPPLIES 3
#define SOCFPGA_RESUME_TIMEOUT 3
/* In power-up order. Reverse for power-down. */
static const char *supply_names[SOCFPGA_FPGMGR_NUM_SUPPLIES] __maybe_unused = {
"FPGA-1.5V",
"FPGA-1.1V",
"FPGA-2.5V",
};
struct socfpga_fpga_priv {
void __iomem *fpga_base_addr;
void __iomem *fpga_data_addr;
struct completion status_complete;
int irq;
};
struct cfgmgr_mode {
/* Values to set in the CTRL register */
u32 ctrl;
/* flag that this table entry is a valid mode */
bool valid;
};
/* For SOCFPGA_FPGMGR_STAT_MSEL field */
static struct cfgmgr_mode cfgmgr_modes[] = {
[MSEL_PP16_FAST_NOAES_NODC] = { CFGWDTH_16 | CDRATIO_X1, 1 },
[MSEL_PP16_FAST_AES_NODC] = { CFGWDTH_16 | CDRATIO_X2, 1 },
[MSEL_PP16_FAST_AESOPT_DC] = { CFGWDTH_16 | CDRATIO_X4, 1 },
[MSEL_PP16_SLOW_NOAES_NODC] = { CFGWDTH_16 | CDRATIO_X1, 1 },
[MSEL_PP16_SLOW_AES_NODC] = { CFGWDTH_16 | CDRATIO_X2, 1 },
[MSEL_PP16_SLOW_AESOPT_DC] = { CFGWDTH_16 | CDRATIO_X4, 1 },
[MSEL_PP32_FAST_NOAES_NODC] = { CFGWDTH_32 | CDRATIO_X1, 1 },
[MSEL_PP32_FAST_AES_NODC] = { CFGWDTH_32 | CDRATIO_X4, 1 },
[MSEL_PP32_FAST_AESOPT_DC] = { CFGWDTH_32 | CDRATIO_X8, 1 },
[MSEL_PP32_SLOW_NOAES_NODC] = { CFGWDTH_32 | CDRATIO_X1, 1 },
[MSEL_PP32_SLOW_AES_NODC] = { CFGWDTH_32 | CDRATIO_X4, 1 },
[MSEL_PP32_SLOW_AESOPT_DC] = { CFGWDTH_32 | CDRATIO_X8, 1 },
};
static u32 socfpga_fpga_readl(struct socfpga_fpga_priv *priv, u32 reg_offset)
{
return readl(priv->fpga_base_addr + reg_offset);
}
static void socfpga_fpga_writel(struct socfpga_fpga_priv *priv, u32 reg_offset,
u32 value)
{
writel(value, priv->fpga_base_addr + reg_offset);
}
static u32 socfpga_fpga_raw_readl(struct socfpga_fpga_priv *priv,
u32 reg_offset)
{
return __raw_readl(priv->fpga_base_addr + reg_offset);
}
static void socfpga_fpga_raw_writel(struct socfpga_fpga_priv *priv,
u32 reg_offset, u32 value)
{
__raw_writel(value, priv->fpga_base_addr + reg_offset);
}
static void socfpga_fpga_data_writel(struct socfpga_fpga_priv *priv, u32 value)
{
writel(value, priv->fpga_data_addr);
}
static inline void socfpga_fpga_set_bitsl(struct socfpga_fpga_priv *priv,
u32 offset, u32 bits)
{
u32 val;
val = socfpga_fpga_readl(priv, offset);
val |= bits;
socfpga_fpga_writel(priv, offset, val);
}
static inline void socfpga_fpga_clr_bitsl(struct socfpga_fpga_priv *priv,
u32 offset, u32 bits)
{
u32 val;
val = socfpga_fpga_readl(priv, offset);
val &= ~bits;
socfpga_fpga_writel(priv, offset, val);
}
static u32 socfpga_fpga_mon_status_get(struct socfpga_fpga_priv *priv)
{
return socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_GPIO_EXT_PORTA_OFST) &
SOCFPGA_FPGMGR_MON_STATUS_MASK;
}
static u32 socfpga_fpga_state_get(struct socfpga_fpga_priv *priv)
{
u32 status = socfpga_fpga_mon_status_get(priv);
if ((status & SOCFPGA_FPGMGR_MON_FPGA_POWER_ON) == 0)
return SOCFPGA_FPGMGR_STAT_POWER_OFF;
return socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_STAT_OFST) &
SOCFPGA_FPGMGR_STAT_STATE_MASK;
}
static void socfpga_fpga_clear_done_status(struct socfpga_fpga_priv *priv)
{
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_DCLKSTAT_OFST,
SOCFPGA_FPGMGR_DCLKSTAT_DCNTDONE_E_DONE);
}
/*
* Set the DCLKCNT, wait for DCLKSTAT to report the count completed, and clear
* the complete status.
*/
static int socfpga_fpga_dclk_set_and_wait_clear(struct socfpga_fpga_priv *priv,
u32 count)
{
int timeout = 2;
u32 done;
/* Clear any existing DONE status. */
if (socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_DCLKSTAT_OFST))
socfpga_fpga_clear_done_status(priv);
/* Issue the DCLK count. */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_DCLKCNT_OFST, count);
/* Poll DCLKSTAT to see if it completed in the timeout period. */
do {
done = socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_DCLKSTAT_OFST);
if (done == SOCFPGA_FPGMGR_DCLKSTAT_DCNTDONE_E_DONE) {
socfpga_fpga_clear_done_status(priv);
return 0;
}
udelay(1);
} while (timeout--);
return -ETIMEDOUT;
}
static int socfpga_fpga_wait_for_state(struct socfpga_fpga_priv *priv,
u32 state)
{
int timeout = 2;
/*
* HW doesn't support an interrupt for changes in state, so poll to see
* if it matches the requested state within the timeout period.
*/
do {
if ((socfpga_fpga_state_get(priv) & state) != 0)
return 0;
msleep(20);
} while (timeout--);
return -ETIMEDOUT;
}
static void socfpga_fpga_enable_irqs(struct socfpga_fpga_priv *priv, u32 irqs)
{
/* set irqs to level sensitive */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_INTTYPE_LEVEL_OFST, 0);
/* set interrupt polarity */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_INT_POL_OFST, irqs);
/* clear irqs */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_PORTA_EOI_OFST, irqs);
/* unmask interrupts */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_INTMSK_OFST, 0);
/* enable interrupts */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_INTEN_OFST, irqs);
}
static void socfpga_fpga_disable_irqs(struct socfpga_fpga_priv *priv)
{
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_INTEN_OFST, 0);
}
static irqreturn_t socfpga_fpga_isr(int irq, void *dev_id)
{
struct socfpga_fpga_priv *priv = dev_id;
u32 irqs, st;
bool conf_done, nstatus;
/* clear irqs */
irqs = socfpga_fpga_raw_readl(priv, SOCFPGA_FPGMGR_GPIO_INTSTAT_OFST);
socfpga_fpga_raw_writel(priv, SOCFPGA_FPGMGR_GPIO_PORTA_EOI_OFST, irqs);
st = socfpga_fpga_raw_readl(priv, SOCFPGA_FPGMGR_GPIO_EXT_PORTA_OFST);
conf_done = (st & SOCFPGA_FPGMGR_MON_CONF_DONE) != 0;
nstatus = (st & SOCFPGA_FPGMGR_MON_NSTATUS) != 0;
/* success */
if (conf_done && nstatus) {
/* disable irqs */
socfpga_fpga_raw_writel(priv,
SOCFPGA_FPGMGR_GPIO_INTEN_OFST, 0);
complete(&priv->status_complete);
}
return IRQ_HANDLED;
}
static int socfpga_fpga_wait_for_config_done(struct socfpga_fpga_priv *priv)
{
int timeout, ret = 0;
socfpga_fpga_disable_irqs(priv);
init_completion(&priv->status_complete);
socfpga_fpga_enable_irqs(priv, SOCFPGA_FPGMGR_MON_CONF_DONE);
timeout = wait_for_completion_interruptible_timeout(
&priv->status_complete,
msecs_to_jiffies(10));
if (timeout == 0)
ret = -ETIMEDOUT;
socfpga_fpga_disable_irqs(priv);
return ret;
}
static int socfpga_fpga_cfg_mode_get(struct socfpga_fpga_priv *priv)
{
u32 msel;
msel = socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_STAT_OFST);
msel &= SOCFPGA_FPGMGR_STAT_MSEL_MASK;
msel >>= SOCFPGA_FPGMGR_STAT_MSEL_SHIFT;
/* Check that this MSEL setting is supported */
if ((msel >= ARRAY_SIZE(cfgmgr_modes)) || !cfgmgr_modes[msel].valid)
return -EINVAL;
return msel;
}
static int socfpga_fpga_cfg_mode_set(struct socfpga_fpga_priv *priv)
{
u32 ctrl_reg;
int mode;
/* get value from MSEL pins */
mode = socfpga_fpga_cfg_mode_get(priv);
if (mode < 0)
return mode;
/* Adjust CTRL for the CDRATIO */
ctrl_reg = socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_CTL_OFST);
ctrl_reg &= ~SOCFPGA_FPGMGR_CTL_CDRATIO_MASK;
ctrl_reg &= ~SOCFPGA_FPGMGR_CTL_CFGWDTH_MASK;
ctrl_reg |= cfgmgr_modes[mode].ctrl;
/* Set NCE to 0. */
ctrl_reg &= ~SOCFPGA_FPGMGR_CTL_NCE;
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_CTL_OFST, ctrl_reg);
return 0;
}
static int socfpga_fpga_reset(struct fpga_manager *mgr)
{
struct socfpga_fpga_priv *priv = mgr->priv;
u32 ctrl_reg, status;
int ret;
/*
* Step 1:
* - Set CTRL.CFGWDTH, CTRL.CDRATIO to match cfg mode
* - Set CTRL.NCE to 0
*/
ret = socfpga_fpga_cfg_mode_set(priv);
if (ret)
return ret;
/* Step 2: Set CTRL.EN to 1 */
socfpga_fpga_set_bitsl(priv, SOCFPGA_FPGMGR_CTL_OFST,
SOCFPGA_FPGMGR_CTL_EN);
/* Step 3: Set CTRL.NCONFIGPULL to 1 to put FPGA in reset */
ctrl_reg = socfpga_fpga_readl(priv, SOCFPGA_FPGMGR_CTL_OFST);
ctrl_reg |= SOCFPGA_FPGMGR_CTL_NCFGPULL;
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_CTL_OFST, ctrl_reg);
/* Step 4: Wait for STATUS.MODE to report FPGA is in reset phase */
status = socfpga_fpga_wait_for_state(priv, SOCFPGA_FPGMGR_STAT_RESET);
/* Step 5: Set CONTROL.NCONFIGPULL to 0 to release FPGA from reset */
ctrl_reg &= ~SOCFPGA_FPGMGR_CTL_NCFGPULL;
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_CTL_OFST, ctrl_reg);
/* Timeout waiting for reset */
if (status)
return -ETIMEDOUT;
return 0;
}
/*
* Prepare the FPGA to receive the configuration data.
*/
static int socfpga_fpga_ops_configure_init(struct fpga_manager *mgr, u32 flags,
const char *buf, size_t count)
{
struct socfpga_fpga_priv *priv = mgr->priv;
int ret;
if (flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
return -EINVAL;
}
/* Steps 1 - 5: Reset the FPGA */
ret = socfpga_fpga_reset(mgr);
if (ret)
return ret;
/* Step 6: Wait for FPGA to enter configuration phase */
if (socfpga_fpga_wait_for_state(priv, SOCFPGA_FPGMGR_STAT_CFG))
return -ETIMEDOUT;
/* Step 7: Clear nSTATUS interrupt */
socfpga_fpga_writel(priv, SOCFPGA_FPGMGR_GPIO_PORTA_EOI_OFST,
SOCFPGA_FPGMGR_MON_NSTATUS);
/* Step 8: Set CTRL.AXICFGEN to 1 to enable transfer of config data */
socfpga_fpga_set_bitsl(priv, SOCFPGA_FPGMGR_CTL_OFST,
SOCFPGA_FPGMGR_CTL_AXICFGEN);
return 0;
}
/*
* Step 9: write data to the FPGA data register
*/
static int socfpga_fpga_ops_configure_write(struct fpga_manager *mgr,
const char *buf, size_t count)
{
struct socfpga_fpga_priv *priv = mgr->priv;
u32 *buffer_32 = (u32 *)buf;
size_t i = 0;
if (count <= 0)
return -EINVAL;
/* Write out the complete 32-bit chunks. */
while (count >= sizeof(u32)) {
socfpga_fpga_data_writel(priv, buffer_32[i++]);
count -= sizeof(u32);
}
/* Write out remaining non 32-bit chunks. */
switch (count) {
case 3:
socfpga_fpga_data_writel(priv, buffer_32[i++] & 0x00ffffff);
break;
case 2:
socfpga_fpga_data_writel(priv, buffer_32[i++] & 0x0000ffff);
break;
case 1:
socfpga_fpga_data_writel(priv, buffer_32[i++] & 0x000000ff);
break;
case 0:
break;
default:
/* This will never happen. */
return -EFAULT;
}
return 0;
}
static int socfpga_fpga_ops_configure_complete(struct fpga_manager *mgr,
u32 flags)
{
struct socfpga_fpga_priv *priv = mgr->priv;
u32 status;
/*
* Step 10:
* - Observe CONF_DONE and nSTATUS (active low)
* - if CONF_DONE = 1 and nSTATUS = 1, configuration was successful
* - if CONF_DONE = 0 and nSTATUS = 0, configuration failed
*/
status = socfpga_fpga_wait_for_config_done(priv);
if (status)
return status;
/* Step 11: Clear CTRL.AXICFGEN to disable transfer of config data */
socfpga_fpga_clr_bitsl(priv, SOCFPGA_FPGMGR_CTL_OFST,
SOCFPGA_FPGMGR_CTL_AXICFGEN);
/*
* Step 12:
* - Write 4 to DCLKCNT
* - Wait for STATUS.DCNTDONE = 1
* - Clear W1C bit in STATUS.DCNTDONE
*/
if (socfpga_fpga_dclk_set_and_wait_clear(priv, 4))
return -ETIMEDOUT;
/* Step 13: Wait for STATUS.MODE to report USER MODE */
if (socfpga_fpga_wait_for_state(priv, SOCFPGA_FPGMGR_STAT_USER_MODE))
return -ETIMEDOUT;
/* Step 14: Set CTRL.EN to 0 */
socfpga_fpga_clr_bitsl(priv, SOCFPGA_FPGMGR_CTL_OFST,
SOCFPGA_FPGMGR_CTL_EN);
return 0;
}
/* Translate state register values to FPGA framework state */
static const enum fpga_mgr_states socfpga_state_to_framework_state[] = {
[SOCFPGA_FPGMGR_STAT_POWER_OFF] = FPGA_MGR_STATE_POWER_OFF,
[SOCFPGA_FPGMGR_STAT_RESET] = FPGA_MGR_STATE_RESET,
[SOCFPGA_FPGMGR_STAT_CFG] = FPGA_MGR_STATE_WRITE_INIT,
[SOCFPGA_FPGMGR_STAT_INIT] = FPGA_MGR_STATE_WRITE_INIT,
[SOCFPGA_FPGMGR_STAT_USER_MODE] = FPGA_MGR_STATE_OPERATING,
[SOCFPGA_FPGMGR_STAT_UNKNOWN] = FPGA_MGR_STATE_UNKNOWN,
};
static enum fpga_mgr_states socfpga_fpga_ops_state(struct fpga_manager *mgr)
{
struct socfpga_fpga_priv *priv = mgr->priv;
enum fpga_mgr_states ret;
u32 state;
state = socfpga_fpga_state_get(priv);
if (state < ARRAY_SIZE(socfpga_state_to_framework_state))
ret = socfpga_state_to_framework_state[state];
else
ret = FPGA_MGR_STATE_UNKNOWN;
return ret;
}
static const struct fpga_manager_ops socfpga_fpga_ops = {
.state = socfpga_fpga_ops_state,
.write_init = socfpga_fpga_ops_configure_init,
.write = socfpga_fpga_ops_configure_write,
.write_complete = socfpga_fpga_ops_configure_complete,
};
static int socfpga_fpga_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct socfpga_fpga_priv *priv;
struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->fpga_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->fpga_base_addr))
return PTR_ERR(priv->fpga_base_addr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->fpga_data_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->fpga_data_addr))
return PTR_ERR(priv->fpga_data_addr);
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0)
return priv->irq;
ret = devm_request_irq(dev, priv->irq, socfpga_fpga_isr, 0,
dev_name(dev), priv);
if (ret)
return ret;
return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
&socfpga_fpga_ops, priv);
}
static int socfpga_fpga_remove(struct platform_device *pdev)
{
fpga_mgr_unregister(&pdev->dev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id socfpga_fpga_of_match[] = {
{ .compatible = "altr,socfpga-fpga-mgr", },
{},
};
MODULE_DEVICE_TABLE(of, socfpga_fpga_of_match);
#endif
static struct platform_driver socfpga_fpga_driver = {
.probe = socfpga_fpga_probe,
.remove = socfpga_fpga_remove,
.driver = {
.name = "socfpga_fpga_manager",
.of_match_table = of_match_ptr(socfpga_fpga_of_match),
},
};
module_platform_driver(socfpga_fpga_driver);
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_DESCRIPTION("Altera SOCFPGA FPGA Manager");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,514 @@
/*
* Copyright (c) 2011-2015 Xilinx Inc.
* Copyright (c) 2015, National Instruments Corp.
*
* FPGA Manager Driver for Xilinx Zynq, heavily based on xdevcfg driver
* in their vendor tree.
*
* 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; version 2 of the License.
*
* 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/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/string.h>
/* Offsets into SLCR regmap */
/* FPGA Software Reset Control */
#define SLCR_FPGA_RST_CTRL_OFFSET 0x240
/* Level Shifters Enable */
#define SLCR_LVL_SHFTR_EN_OFFSET 0x900
/* Constant Definitions */
/* Control Register */
#define CTRL_OFFSET 0x00
/* Lock Register */
#define LOCK_OFFSET 0x04
/* Interrupt Status Register */
#define INT_STS_OFFSET 0x0c
/* Interrupt Mask Register */
#define INT_MASK_OFFSET 0x10
/* Status Register */
#define STATUS_OFFSET 0x14
/* DMA Source Address Register */
#define DMA_SRC_ADDR_OFFSET 0x18
/* DMA Destination Address Reg */
#define DMA_DST_ADDR_OFFSET 0x1c
/* DMA Source Transfer Length */
#define DMA_SRC_LEN_OFFSET 0x20
/* DMA Destination Transfer */
#define DMA_DEST_LEN_OFFSET 0x24
/* Unlock Register */
#define UNLOCK_OFFSET 0x34
/* Misc. Control Register */
#define MCTRL_OFFSET 0x80
/* Control Register Bit definitions */
/* Signal to reset FPGA */
#define CTRL_PCFG_PROG_B_MASK BIT(30)
/* Enable PCAP for PR */
#define CTRL_PCAP_PR_MASK BIT(27)
/* Enable PCAP */
#define CTRL_PCAP_MODE_MASK BIT(26)
/* Miscellaneous Control Register bit definitions */
/* Internal PCAP loopback */
#define MCTRL_PCAP_LPBK_MASK BIT(4)
/* Status register bit definitions */
/* FPGA init status */
#define STATUS_DMA_Q_F BIT(31)
#define STATUS_PCFG_INIT_MASK BIT(4)
/* Interrupt Status/Mask Register Bit definitions */
/* DMA command done */
#define IXR_DMA_DONE_MASK BIT(13)
/* DMA and PCAP cmd done */
#define IXR_D_P_DONE_MASK BIT(12)
/* FPGA programmed */
#define IXR_PCFG_DONE_MASK BIT(2)
#define IXR_ERROR_FLAGS_MASK 0x00F0F860
#define IXR_ALL_MASK 0xF8F7F87F
/* Miscellaneous constant values */
/* Invalid DMA addr */
#define DMA_INVALID_ADDRESS GENMASK(31, 0)
/* Used to unlock the dev */
#define UNLOCK_MASK 0x757bdf0d
/* Timeout for DMA to complete */
#define DMA_DONE_TIMEOUT msecs_to_jiffies(1000)
/* Timeout for polling reset bits */
#define INIT_POLL_TIMEOUT 2500000
/* Delay for polling reset bits */
#define INIT_POLL_DELAY 20
/* Masks for controlling stuff in SLCR */
/* Disable all Level shifters */
#define LVL_SHFTR_DISABLE_ALL_MASK 0x0
/* Enable Level shifters from PS to PL */
#define LVL_SHFTR_ENABLE_PS_TO_PL 0xa
/* Enable Level shifters from PL to PS */
#define LVL_SHFTR_ENABLE_PL_TO_PS 0xf
/* Enable global resets */
#define FPGA_RST_ALL_MASK 0xf
/* Disable global resets */
#define FPGA_RST_NONE_MASK 0x0
struct zynq_fpga_priv {
struct device *dev;
int irq;
struct clk *clk;
void __iomem *io_base;
struct regmap *slcr;
struct completion dma_done;
};
static inline void zynq_fpga_write(struct zynq_fpga_priv *priv, u32 offset,
u32 val)
{
writel(val, priv->io_base + offset);
}
static inline u32 zynq_fpga_read(const struct zynq_fpga_priv *priv,
u32 offset)
{
return readl(priv->io_base + offset);
}
#define zynq_fpga_poll_timeout(priv, addr, val, cond, sleep_us, timeout_us) \
readl_poll_timeout(priv->io_base + addr, val, cond, sleep_us, \
timeout_us)
static void zynq_fpga_mask_irqs(struct zynq_fpga_priv *priv)
{
u32 intr_mask;
intr_mask = zynq_fpga_read(priv, INT_MASK_OFFSET);
zynq_fpga_write(priv, INT_MASK_OFFSET,
intr_mask | IXR_DMA_DONE_MASK | IXR_ERROR_FLAGS_MASK);
}
static void zynq_fpga_unmask_irqs(struct zynq_fpga_priv *priv)
{
u32 intr_mask;
intr_mask = zynq_fpga_read(priv, INT_MASK_OFFSET);
zynq_fpga_write(priv, INT_MASK_OFFSET,
intr_mask
& ~(IXR_D_P_DONE_MASK | IXR_ERROR_FLAGS_MASK));
}
static irqreturn_t zynq_fpga_isr(int irq, void *data)
{
struct zynq_fpga_priv *priv = data;
/* disable DMA and error IRQs */
zynq_fpga_mask_irqs(priv);
complete(&priv->dma_done);
return IRQ_HANDLED;
}
static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
const char *buf, size_t count)
{
struct zynq_fpga_priv *priv;
u32 ctrl, status;
int err;
priv = mgr->priv;
err = clk_enable(priv->clk);
if (err)
return err;
/* don't globally reset PL if we're doing partial reconfig */
if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) {
/* assert AXI interface resets */
regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET,
FPGA_RST_ALL_MASK);
/* disable all level shifters */
regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET,
LVL_SHFTR_DISABLE_ALL_MASK);
/* enable level shifters from PS to PL */
regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET,
LVL_SHFTR_ENABLE_PS_TO_PL);
/* create a rising edge on PCFG_INIT. PCFG_INIT follows
* PCFG_PROG_B, so we need to poll it after setting PCFG_PROG_B
* to make sure the rising edge actually happens.
* Note: PCFG_PROG_B is low active, sequence as described in
* UG585 v1.10 page 211
*/
ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
ctrl |= CTRL_PCFG_PROG_B_MASK;
zynq_fpga_write(priv, CTRL_OFFSET, ctrl);
err = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status,
status & STATUS_PCFG_INIT_MASK,
INIT_POLL_DELAY,
INIT_POLL_TIMEOUT);
if (err) {
dev_err(priv->dev, "Timeout waiting for PCFG_INIT");
goto out_err;
}
ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
ctrl &= ~CTRL_PCFG_PROG_B_MASK;
zynq_fpga_write(priv, CTRL_OFFSET, ctrl);
err = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status,
!(status & STATUS_PCFG_INIT_MASK),
INIT_POLL_DELAY,
INIT_POLL_TIMEOUT);
if (err) {
dev_err(priv->dev, "Timeout waiting for !PCFG_INIT");
goto out_err;
}
ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
ctrl |= CTRL_PCFG_PROG_B_MASK;
zynq_fpga_write(priv, CTRL_OFFSET, ctrl);
err = zynq_fpga_poll_timeout(priv, STATUS_OFFSET, status,
status & STATUS_PCFG_INIT_MASK,
INIT_POLL_DELAY,
INIT_POLL_TIMEOUT);
if (err) {
dev_err(priv->dev, "Timeout waiting for PCFG_INIT");
goto out_err;
}
}
/* set configuration register with following options:
* - enable PCAP interface
* - set throughput for maximum speed
* - set CPU in user mode
*/
ctrl = zynq_fpga_read(priv, CTRL_OFFSET);
zynq_fpga_write(priv, CTRL_OFFSET,
(CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK | ctrl));
/* check that we have room in the command queue */
status = zynq_fpga_read(priv, STATUS_OFFSET);
if (status & STATUS_DMA_Q_F) {
dev_err(priv->dev, "DMA command queue full");
err = -EBUSY;
goto out_err;
}
/* ensure internal PCAP loopback is disabled */
ctrl = zynq_fpga_read(priv, MCTRL_OFFSET);
zynq_fpga_write(priv, MCTRL_OFFSET, (~MCTRL_PCAP_LPBK_MASK & ctrl));
clk_disable(priv->clk);
return 0;
out_err:
clk_disable(priv->clk);
return err;
}
static int zynq_fpga_ops_write(struct fpga_manager *mgr,
const char *buf, size_t count)
{
struct zynq_fpga_priv *priv;
int err;
char *kbuf;
size_t in_count;
dma_addr_t dma_addr;
u32 transfer_length;
u32 intr_status;
in_count = count;
priv = mgr->priv;
kbuf = dma_alloc_coherent(priv->dev, count, &dma_addr, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
memcpy(kbuf, buf, count);
/* enable clock */
err = clk_enable(priv->clk);
if (err)
goto out_free;
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
reinit_completion(&priv->dma_done);
/* enable DMA and error IRQs */
zynq_fpga_unmask_irqs(priv);
/* the +1 in the src addr is used to hold off on DMA_DONE IRQ
* until both AXI and PCAP are done ...
*/
zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, (u32)(dma_addr) + 1);
zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, (u32)DMA_INVALID_ADDRESS);
/* convert #bytes to #words */
transfer_length = (count + 3) / 4;
zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, transfer_length);
zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, 0);
wait_for_completion(&priv->dma_done);
intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
zynq_fpga_write(priv, INT_STS_OFFSET, intr_status);
if (!((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) {
dev_err(priv->dev, "Error configuring FPGA");
err = -EFAULT;
}
clk_disable(priv->clk);
out_free:
dma_free_coherent(priv->dev, in_count, kbuf, dma_addr);
return err;
}
static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
{
struct zynq_fpga_priv *priv = mgr->priv;
int err;
u32 intr_status;
err = clk_enable(priv->clk);
if (err)
return err;
err = zynq_fpga_poll_timeout(priv, INT_STS_OFFSET, intr_status,
intr_status & IXR_PCFG_DONE_MASK,
INIT_POLL_DELAY,
INIT_POLL_TIMEOUT);
clk_disable(priv->clk);
if (err)
return err;
/* for the partial reconfig case we didn't touch the level shifters */
if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) {
/* enable level shifters from PL to PS */
regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET,
LVL_SHFTR_ENABLE_PL_TO_PS);
/* deassert AXI interface resets */
regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET,
FPGA_RST_NONE_MASK);
}
return 0;
}
static enum fpga_mgr_states zynq_fpga_ops_state(struct fpga_manager *mgr)
{
int err;
u32 intr_status;
struct zynq_fpga_priv *priv;
priv = mgr->priv;
err = clk_enable(priv->clk);
if (err)
return FPGA_MGR_STATE_UNKNOWN;
intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
clk_disable(priv->clk);
if (intr_status & IXR_PCFG_DONE_MASK)
return FPGA_MGR_STATE_OPERATING;
return FPGA_MGR_STATE_UNKNOWN;
}
static const struct fpga_manager_ops zynq_fpga_ops = {
.state = zynq_fpga_ops_state,
.write_init = zynq_fpga_ops_write_init,
.write = zynq_fpga_ops_write,
.write_complete = zynq_fpga_ops_write_complete,
};
static int zynq_fpga_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct zynq_fpga_priv *priv;
struct resource *res;
int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
priv->slcr = syscon_regmap_lookup_by_phandle(dev->of_node,
"syscon");
if (IS_ERR(priv->slcr)) {
dev_err(dev, "unable to get zynq-slcr regmap");
return PTR_ERR(priv->slcr);
}
init_completion(&priv->dma_done);
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) {
dev_err(dev, "No IRQ available");
return priv->irq;
}
err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0,
dev_name(dev), priv);
if (err) {
dev_err(dev, "unable to request IRQ");
return err;
}
priv->clk = devm_clk_get(dev, "ref_clk");
if (IS_ERR(priv->clk)) {
dev_err(dev, "input clock not found");
return PTR_ERR(priv->clk);
}
err = clk_prepare_enable(priv->clk);
if (err) {
dev_err(dev, "unable to enable clock");
return err;
}
/* unlock the device */
zynq_fpga_write(priv, UNLOCK_OFFSET, UNLOCK_MASK);
clk_disable(priv->clk);
err = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
&zynq_fpga_ops, priv);
if (err) {
dev_err(dev, "unable to register FPGA manager");
clk_unprepare(priv->clk);
return err;
}
return 0;
}
static int zynq_fpga_remove(struct platform_device *pdev)
{
struct zynq_fpga_priv *priv;
struct fpga_manager *mgr;
mgr = platform_get_drvdata(pdev);
priv = mgr->priv;
fpga_mgr_unregister(&pdev->dev);
clk_unprepare(priv->clk);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id zynq_fpga_of_match[] = {
{ .compatible = "xlnx,zynq-devcfg-1.0", },
{},
};
MODULE_DEVICE_TABLE(of, zynq_fpga_of_match);
#endif
static struct platform_driver zynq_fpga_driver = {
.probe = zynq_fpga_probe,
.remove = zynq_fpga_remove,
.driver = {
.name = "zynq_fpga_manager",
.of_match_table = of_match_ptr(zynq_fpga_of_match),
},
};
module_platform_driver(zynq_fpga_driver);
MODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>");
MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>");
MODULE_DESCRIPTION("Xilinx Zynq FPGA Manager");
MODULE_LICENSE("GPL v2");

View File

@ -191,7 +191,8 @@ static void etm_set_prog(struct etm_drvdata *drvdata)
isb();
if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 1)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", ETMSR);
"%s: timeout observed when probing at offset %#x\n",
__func__, ETMSR);
}
}
@ -209,7 +210,8 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
isb();
if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 0)) {
dev_err(drvdata->dev,
"timeout observed when probing at offset %#x\n", ETMSR);
"%s: timeout observed when probing at offset %#x\n",
__func__, ETMSR);
}
}
@ -313,14 +315,6 @@ static void etm_enable_hw(void *info)
dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
}
static int etm_trace_id_simple(struct etm_drvdata *drvdata)
{
if (!drvdata->enable)
return drvdata->traceid;
return (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK);
}
static int etm_trace_id(struct coresight_device *csdev)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -1506,44 +1500,17 @@ static ssize_t timestamp_event_store(struct device *dev,
}
static DEVICE_ATTR_RW(timestamp_event);
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t cpu_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
unsigned long flags;
int val;
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
pm_runtime_get_sync(drvdata->dev);
spin_lock_irqsave(&drvdata->spinlock, flags);
val = drvdata->cpu;
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
CS_UNLOCK(drvdata->base);
ret = sprintf(buf,
"ETMCCR: 0x%08x\n"
"ETMCCER: 0x%08x\n"
"ETMSCR: 0x%08x\n"
"ETMIDR: 0x%08x\n"
"ETMCR: 0x%08x\n"
"ETMTRACEIDR: 0x%08x\n"
"Enable event: 0x%08x\n"
"Enable start/stop: 0x%08x\n"
"Enable control: CR1 0x%08x CR2 0x%08x\n"
"CPU affinity: %d\n",
drvdata->etmccr, drvdata->etmccer,
etm_readl(drvdata, ETMSCR), etm_readl(drvdata, ETMIDR),
etm_readl(drvdata, ETMCR), etm_trace_id_simple(drvdata),
etm_readl(drvdata, ETMTEEVR),
etm_readl(drvdata, ETMTSSCR),
etm_readl(drvdata, ETMTECR1),
etm_readl(drvdata, ETMTECR2),
drvdata->cpu);
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
pm_runtime_put(drvdata->dev);
return ret;
}
static DEVICE_ATTR_RO(status);
static DEVICE_ATTR_RO(cpu);
static ssize_t traceid_show(struct device *dev,
struct device_attribute *attr, char *buf)
@ -1619,11 +1586,61 @@ static struct attribute *coresight_etm_attrs[] = {
&dev_attr_ctxid_mask.attr,
&dev_attr_sync_freq.attr,
&dev_attr_timestamp_event.attr,
&dev_attr_status.attr,
&dev_attr_traceid.attr,
&dev_attr_cpu.attr,
NULL,
};
#define coresight_simple_func(name, offset) \
static ssize_t name##_show(struct device *_dev, \
struct device_attribute *attr, char *buf) \
{ \
struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \
return scnprintf(buf, PAGE_SIZE, "0x%x\n", \
readl_relaxed(drvdata->base + offset)); \
} \
DEVICE_ATTR_RO(name)
coresight_simple_func(etmccr, ETMCCR);
coresight_simple_func(etmccer, ETMCCER);
coresight_simple_func(etmscr, ETMSCR);
coresight_simple_func(etmidr, ETMIDR);
coresight_simple_func(etmcr, ETMCR);
coresight_simple_func(etmtraceidr, ETMTRACEIDR);
coresight_simple_func(etmteevr, ETMTEEVR);
coresight_simple_func(etmtssvr, ETMTSSCR);
coresight_simple_func(etmtecr1, ETMTECR1);
coresight_simple_func(etmtecr2, ETMTECR2);
static struct attribute *coresight_etm_mgmt_attrs[] = {
&dev_attr_etmccr.attr,
&dev_attr_etmccer.attr,
&dev_attr_etmscr.attr,
&dev_attr_etmidr.attr,
&dev_attr_etmcr.attr,
&dev_attr_etmtraceidr.attr,
&dev_attr_etmteevr.attr,
&dev_attr_etmtssvr.attr,
&dev_attr_etmtecr1.attr,
&dev_attr_etmtecr2.attr,
NULL,
};
static const struct attribute_group coresight_etm_group = {
.attrs = coresight_etm_attrs,
};
static const struct attribute_group coresight_etm_mgmt_group = {
.attrs = coresight_etm_mgmt_attrs,
.name = "mgmt",
};
static const struct attribute_group *coresight_etm_groups[] = {
&coresight_etm_group,
&coresight_etm_mgmt_group,
NULL,
};
ATTRIBUTE_GROUPS(coresight_etm);
static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
void *hcpu)

View File

@ -136,7 +136,9 @@ static void etm4_enable_hw(void *info)
writel_relaxed(drvdata->cntr_val[i],
drvdata->base + TRCCNTVRn(i));
}
for (i = 0; i < drvdata->nr_resource; i++)
/* Resource selector pair 0 is always implemented and reserved */
for (i = 2; i < drvdata->nr_resource * 2; i++)
writel_relaxed(drvdata->res_ctrl[i],
drvdata->base + TRCRSCTLRn(i));
@ -489,8 +491,9 @@ static ssize_t reset_store(struct device *dev,
drvdata->cntr_val[i] = 0x0;
}
drvdata->res_idx = 0x0;
for (i = 0; i < drvdata->nr_resource; i++)
/* Resource selector pair 0 is always implemented and reserved */
drvdata->res_idx = 0x2;
for (i = 2; i < drvdata->nr_resource * 2; i++)
drvdata->res_ctrl[i] = 0x0;
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
@ -1732,7 +1735,7 @@ static ssize_t res_idx_store(struct device *dev,
if (kstrtoul(buf, 16, &val))
return -EINVAL;
/* Resource selector pair 0 is always implemented and reserved */
if ((val == 0) || (val >= drvdata->nr_resource))
if (val < 2 || val >= drvdata->nr_resource * 2)
return -EINVAL;
/*
@ -2416,8 +2419,13 @@ static void etm4_init_arch_data(void *info)
drvdata->nr_addr_cmp = BMVAL(etmidr4, 0, 3);
/* NUMPC, bits[15:12] number of PE comparator inputs for tracing */
drvdata->nr_pe_cmp = BMVAL(etmidr4, 12, 15);
/* NUMRSPAIR, bits[19:16] the number of resource pairs for tracing */
drvdata->nr_resource = BMVAL(etmidr4, 16, 19);
/*
* NUMRSPAIR, bits[19:16]
* The number of resource pairs conveyed by the HW starts at 0, i.e a
* value of 0x0 indicate 1 resource pair, 0x1 indicate two and so on.
* As such add 1 to the value of NUMRSPAIR for a better representation.
*/
drvdata->nr_resource = BMVAL(etmidr4, 16, 19) + 1;
/*
* NUMSSCC, bits[23:20] the number of single-shot
* comparator control for tracing
@ -2504,6 +2512,8 @@ static void etm4_init_default_data(struct etmv4_drvdata *drvdata)
drvdata->cntr_val[i] = 0x0;
}
/* Resource selector pair 0 is always implemented and reserved */
drvdata->res_idx = 0x2;
for (i = 2; i < drvdata->nr_resource * 2; i++)
drvdata->res_ctrl[i] = 0x0;

View File

@ -240,6 +240,11 @@ static int coresight_enable_path(struct list_head *path)
int ret = 0;
struct coresight_device *cd;
/*
* At this point we have a full @path, from source to sink. The
* sink is the first entry and the source the last one. Go through
* all the components and enable them one by one.
*/
list_for_each_entry(cd, path, path_link) {
if (cd == list_first_entry(path, struct coresight_device,
path_link)) {

View File

@ -0,0 +1,72 @@
config INTEL_TH
tristate "Intel(R) Trace Hub controller"
help
Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that
produce, switch and output trace data from multiple hardware and
software sources over several types of trace output ports encoded
in System Trace Protocol (MIPI STPv2) and is intended to perform
full system debugging.
This option enables intel_th bus and common code used by TH
subdevices to interact with each other and hardware and for
platform glue layers to drive Intel TH devices.
Say Y here to enable Intel(R) Trace Hub controller support.
if INTEL_TH
config INTEL_TH_PCI
tristate "Intel(R) Trace Hub PCI controller"
depends on PCI
help
Intel(R) Trace Hub may exist as a PCI device. This option enables
support glue layer for PCI-based Intel TH.
Say Y here to enable PCI Intel TH support.
config INTEL_TH_GTH
tristate "Intel(R) Trace Hub Global Trace Hub"
help
Global Trace Hub (GTH) is the central component of the
Intel TH infrastructure and acts as a switch for source
and output devices. This driver is required for other
Intel TH subdevices to initialize.
Say Y here to enable GTH subdevice of Intel(R) Trace Hub.
config INTEL_TH_STH
tristate "Intel(R) Trace Hub Software Trace Hub support"
depends on STM
help
Software Trace Hub (STH) enables trace data from software
trace sources to be sent out via Intel(R) Trace Hub. It
uses stm class device to interface with its sources.
Say Y here to enable STH subdevice of Intel(R) Trace Hub.
config INTEL_TH_MSU
tristate "Intel(R) Trace Hub Memory Storage Unit"
help
Memory Storage Unit (MSU) trace output device enables
storing STP traces to system memory. It supports single
and multiblock modes of operation and provides read()
and mmap() access to the collected data.
Say Y here to enable MSU output device for Intel TH.
config INTEL_TH_PTI
tristate "Intel(R) Trace Hub PTI output"
help
Parallel Trace Interface unit (PTI) is a trace output device
of Intel TH architecture that facilitates STP trace output via
a PTI port.
Say Y to enable PTI output of Intel TH data.
config INTEL_TH_DEBUG
bool "Intel(R) Trace Hub debugging"
depends on DEBUG_FS
help
Say Y here to enable debugging.
endif

View File

@ -0,0 +1,18 @@
obj-$(CONFIG_INTEL_TH) += intel_th.o
intel_th-y := core.o
intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o
obj-$(CONFIG_INTEL_TH_PCI) += intel_th_pci.o
intel_th_pci-y := pci.o
obj-$(CONFIG_INTEL_TH_GTH) += intel_th_gth.o
intel_th_gth-y := gth.o
obj-$(CONFIG_INTEL_TH_STH) += intel_th_sth.o
intel_th_sth-y := sth.o
obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu.o
intel_th_msu-y := msu.o
obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o
intel_th_pti-y := pti.o

View File

@ -0,0 +1,692 @@
/*
* Intel(R) Trace Hub driver core
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/kdev_t.h>
#include <linux/debugfs.h>
#include <linux/idr.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include "intel_th.h"
#include "debug.h"
static DEFINE_IDA(intel_th_ida);
static int intel_th_match(struct device *dev, struct device_driver *driver)
{
struct intel_th_driver *thdrv = to_intel_th_driver(driver);
struct intel_th_device *thdev = to_intel_th_device(dev);
if (thdev->type == INTEL_TH_SWITCH &&
(!thdrv->enable || !thdrv->disable))
return 0;
return !strcmp(thdev->name, driver->name);
}
static int intel_th_child_remove(struct device *dev, void *data)
{
device_release_driver(dev);
return 0;
}
static int intel_th_probe(struct device *dev)
{
struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver);
struct intel_th_device *thdev = to_intel_th_device(dev);
struct intel_th_driver *hubdrv;
struct intel_th_device *hub = NULL;
int ret;
if (thdev->type == INTEL_TH_SWITCH)
hub = thdev;
else if (dev->parent)
hub = to_intel_th_device(dev->parent);
if (!hub || !hub->dev.driver)
return -EPROBE_DEFER;
hubdrv = to_intel_th_driver(hub->dev.driver);
ret = thdrv->probe(to_intel_th_device(dev));
if (ret)
return ret;
if (thdev->type == INTEL_TH_OUTPUT &&
!intel_th_output_assigned(thdev))
ret = hubdrv->assign(hub, thdev);
return ret;
}
static int intel_th_remove(struct device *dev)
{
struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver);
struct intel_th_device *thdev = to_intel_th_device(dev);
struct intel_th_device *hub = to_intel_th_device(dev->parent);
int err;
if (thdev->type == INTEL_TH_SWITCH) {
err = device_for_each_child(dev, thdev, intel_th_child_remove);
if (err)
return err;
}
thdrv->remove(thdev);
if (intel_th_output_assigned(thdev)) {
struct intel_th_driver *hubdrv =
to_intel_th_driver(dev->parent->driver);
if (hub->dev.driver)
hubdrv->unassign(hub, thdev);
}
return 0;
}
static struct bus_type intel_th_bus = {
.name = "intel_th",
.dev_attrs = NULL,
.match = intel_th_match,
.probe = intel_th_probe,
.remove = intel_th_remove,
};
static void intel_th_device_free(struct intel_th_device *thdev);
static void intel_th_device_release(struct device *dev)
{
intel_th_device_free(to_intel_th_device(dev));
}
static struct device_type intel_th_source_device_type = {
.name = "intel_th_source_device",
.release = intel_th_device_release,
};
static char *intel_th_output_devnode(struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
{
struct intel_th_device *thdev = to_intel_th_device(dev);
char *node;
if (thdev->id >= 0)
node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", 0, thdev->name,
thdev->id);
else
node = kasprintf(GFP_KERNEL, "intel_th%d/%s", 0, thdev->name);
return node;
}
static ssize_t port_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct intel_th_device *thdev = to_intel_th_device(dev);
if (thdev->output.port >= 0)
return scnprintf(buf, PAGE_SIZE, "%u\n", thdev->output.port);
return scnprintf(buf, PAGE_SIZE, "unassigned\n");
}
static DEVICE_ATTR_RO(port);
static int intel_th_output_activate(struct intel_th_device *thdev)
{
struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
if (thdrv->activate)
return thdrv->activate(thdev);
intel_th_trace_enable(thdev);
return 0;
}
static void intel_th_output_deactivate(struct intel_th_device *thdev)
{
struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
if (thdrv->deactivate)
thdrv->deactivate(thdev);
else
intel_th_trace_disable(thdev);
}
static ssize_t active_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct intel_th_device *thdev = to_intel_th_device(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", thdev->output.active);
}
static ssize_t active_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct intel_th_device *thdev = to_intel_th_device(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (!!val != thdev->output.active) {
if (val)
ret = intel_th_output_activate(thdev);
else
intel_th_output_deactivate(thdev);
}
return ret ? ret : size;
}
static DEVICE_ATTR_RW(active);
static struct attribute *intel_th_output_attrs[] = {
&dev_attr_port.attr,
&dev_attr_active.attr,
NULL,
};
ATTRIBUTE_GROUPS(intel_th_output);
static struct device_type intel_th_output_device_type = {
.name = "intel_th_output_device",
.groups = intel_th_output_groups,
.release = intel_th_device_release,
.devnode = intel_th_output_devnode,
};
static struct device_type intel_th_switch_device_type = {
.name = "intel_th_switch_device",
.release = intel_th_device_release,
};
static struct device_type *intel_th_device_type[] = {
[INTEL_TH_SOURCE] = &intel_th_source_device_type,
[INTEL_TH_OUTPUT] = &intel_th_output_device_type,
[INTEL_TH_SWITCH] = &intel_th_switch_device_type,
};
int intel_th_driver_register(struct intel_th_driver *thdrv)
{
if (!thdrv->probe || !thdrv->remove)
return -EINVAL;
thdrv->driver.bus = &intel_th_bus;
return driver_register(&thdrv->driver);
}
EXPORT_SYMBOL_GPL(intel_th_driver_register);
void intel_th_driver_unregister(struct intel_th_driver *thdrv)
{
driver_unregister(&thdrv->driver);
}
EXPORT_SYMBOL_GPL(intel_th_driver_unregister);
static struct intel_th_device *
intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name,
int id)
{
struct device *parent;
struct intel_th_device *thdev;
if (type == INTEL_TH_SWITCH)
parent = th->dev;
else
parent = &th->hub->dev;
thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL);
if (!thdev)
return NULL;
thdev->id = id;
thdev->type = type;
strcpy(thdev->name, name);
device_initialize(&thdev->dev);
thdev->dev.bus = &intel_th_bus;
thdev->dev.type = intel_th_device_type[type];
thdev->dev.parent = parent;
thdev->dev.dma_mask = parent->dma_mask;
thdev->dev.dma_parms = parent->dma_parms;
dma_set_coherent_mask(&thdev->dev, parent->coherent_dma_mask);
if (id >= 0)
dev_set_name(&thdev->dev, "%d-%s%d", th->id, name, id);
else
dev_set_name(&thdev->dev, "%d-%s", th->id, name);
return thdev;
}
static int intel_th_device_add_resources(struct intel_th_device *thdev,
struct resource *res, int nres)
{
struct resource *r;
r = kmemdup(res, sizeof(*res) * nres, GFP_KERNEL);
if (!r)
return -ENOMEM;
thdev->resource = r;
thdev->num_resources = nres;
return 0;
}
static void intel_th_device_remove(struct intel_th_device *thdev)
{
device_del(&thdev->dev);
put_device(&thdev->dev);
}
static void intel_th_device_free(struct intel_th_device *thdev)
{
kfree(thdev->resource);
kfree(thdev);
}
/*
* Intel(R) Trace Hub subdevices
*/
static struct intel_th_subdevice {
const char *name;
struct resource res[3];
unsigned nres;
unsigned type;
unsigned otype;
int id;
} intel_th_subdevices[TH_SUBDEVICE_MAX] = {
{
.nres = 1,
.res = {
{
.start = REG_GTH_OFFSET,
.end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
},
.name = "gth",
.type = INTEL_TH_SWITCH,
.id = -1,
},
{
.nres = 2,
.res = {
{
.start = REG_MSU_OFFSET,
.end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
{
.start = BUF_MSU_OFFSET,
.end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
},
.name = "msc",
.id = 0,
.type = INTEL_TH_OUTPUT,
.otype = GTH_MSU,
},
{
.nres = 2,
.res = {
{
.start = REG_MSU_OFFSET,
.end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
{
.start = BUF_MSU_OFFSET,
.end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
},
.name = "msc",
.id = 1,
.type = INTEL_TH_OUTPUT,
.otype = GTH_MSU,
},
{
.nres = 2,
.res = {
{
.start = REG_STH_OFFSET,
.end = REG_STH_OFFSET + REG_STH_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
{
.start = TH_MMIO_SW,
.end = 0,
.flags = IORESOURCE_MEM,
},
},
.id = -1,
.name = "sth",
.type = INTEL_TH_SOURCE,
},
{
.nres = 1,
.res = {
{
.start = REG_PTI_OFFSET,
.end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
},
.id = -1,
.name = "pti",
.type = INTEL_TH_OUTPUT,
.otype = GTH_PTI,
},
{
.nres = 1,
.res = {
{
.start = REG_DCIH_OFFSET,
.end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1,
.flags = IORESOURCE_MEM,
},
},
.id = -1,
.name = "dcih",
.type = INTEL_TH_OUTPUT,
},
};
static int intel_th_populate(struct intel_th *th, struct resource *devres,
unsigned int ndevres, int irq)
{
struct resource res[3];
unsigned int req = 0;
int i, err;
/* create devices for each intel_th_subdevice */
for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) {
struct intel_th_subdevice *subdev = &intel_th_subdevices[i];
struct intel_th_device *thdev;
int r;
thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
subdev->id);
if (!thdev) {
err = -ENOMEM;
goto kill_subdevs;
}
memcpy(res, subdev->res,
sizeof(struct resource) * subdev->nres);
for (r = 0; r < subdev->nres; r++) {
int bar = TH_MMIO_CONFIG;
/*
* Take .end == 0 to mean 'take the whole bar',
* .start then tells us which bar it is. Default to
* TH_MMIO_CONFIG.
*/
if (!res[r].end && res[r].flags == IORESOURCE_MEM) {
bar = res[r].start;
res[r].start = 0;
res[r].end = resource_size(&devres[bar]) - 1;
}
if (res[r].flags & IORESOURCE_MEM) {
res[r].start += devres[bar].start;
res[r].end += devres[bar].start;
dev_dbg(th->dev, "%s:%d @ %pR\n",
subdev->name, r, &res[r]);
} else if (res[r].flags & IORESOURCE_IRQ) {
res[r].start = irq;
}
}
err = intel_th_device_add_resources(thdev, res, subdev->nres);
if (err) {
put_device(&thdev->dev);
goto kill_subdevs;
}
if (subdev->type == INTEL_TH_OUTPUT) {
thdev->dev.devt = MKDEV(th->major, i);
thdev->output.type = subdev->otype;
thdev->output.port = -1;
}
err = device_add(&thdev->dev);
if (err) {
put_device(&thdev->dev);
goto kill_subdevs;
}
/* need switch driver to be loaded to enumerate the rest */
if (subdev->type == INTEL_TH_SWITCH && !req) {
th->hub = thdev;
err = request_module("intel_th_%s", subdev->name);
if (!err)
req++;
}
th->thdev[i] = thdev;
}
return 0;
kill_subdevs:
for (i-- ; i >= 0; i--)
intel_th_device_remove(th->thdev[i]);
return err;
}
static int match_devt(struct device *dev, void *data)
{
dev_t devt = (dev_t)(unsigned long)data;
return dev->devt == devt;
}
static int intel_th_output_open(struct inode *inode, struct file *file)
{
const struct file_operations *fops;
struct intel_th_driver *thdrv;
struct device *dev;
int err;
dev = bus_find_device(&intel_th_bus, NULL,
(void *)(unsigned long)inode->i_rdev,
match_devt);
if (!dev || !dev->driver)
return -ENODEV;
thdrv = to_intel_th_driver(dev->driver);
fops = fops_get(thdrv->fops);
if (!fops)
return -ENODEV;
replace_fops(file, fops);
file->private_data = to_intel_th_device(dev);
if (file->f_op->open) {
err = file->f_op->open(inode, file);
return err;
}
return 0;
}
static const struct file_operations intel_th_output_fops = {
.open = intel_th_output_open,
.llseek = noop_llseek,
};
/**
* intel_th_alloc() - allocate a new Intel TH device and its subdevices
* @dev: parent device
* @devres: parent's resources
* @ndevres: number of resources
* @irq: irq number
*/
struct intel_th *
intel_th_alloc(struct device *dev, struct resource *devres,
unsigned int ndevres, int irq)
{
struct intel_th *th;
int err;
th = kzalloc(sizeof(*th), GFP_KERNEL);
if (!th)
return ERR_PTR(-ENOMEM);
th->id = ida_simple_get(&intel_th_ida, 0, 0, GFP_KERNEL);
if (th->id < 0) {
err = th->id;
goto err_alloc;
}
th->major = __register_chrdev(0, 0, TH_POSSIBLE_OUTPUTS,
"intel_th/output", &intel_th_output_fops);
if (th->major < 0) {
err = th->major;
goto err_ida;
}
th->dev = dev;
err = intel_th_populate(th, devres, ndevres, irq);
if (err)
goto err_chrdev;
return th;
err_chrdev:
__unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
"intel_th/output");
err_ida:
ida_simple_remove(&intel_th_ida, th->id);
err_alloc:
kfree(th);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(intel_th_alloc);
void intel_th_free(struct intel_th *th)
{
int i;
for (i = 0; i < TH_SUBDEVICE_MAX; i++)
if (th->thdev[i] != th->hub)
intel_th_device_remove(th->thdev[i]);
intel_th_device_remove(th->hub);
__unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
"intel_th/output");
ida_simple_remove(&intel_th_ida, th->id);
kfree(th);
}
EXPORT_SYMBOL_GPL(intel_th_free);
/**
* intel_th_trace_enable() - enable tracing for an output device
* @thdev: output device that requests tracing be enabled
*/
int intel_th_trace_enable(struct intel_th_device *thdev)
{
struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH))
return -EINVAL;
if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
return -EINVAL;
hubdrv->enable(hub, &thdev->output);
return 0;
}
EXPORT_SYMBOL_GPL(intel_th_trace_enable);
/**
* intel_th_trace_disable() - disable tracing for an output device
* @thdev: output device that requests tracing be disabled
*/
int intel_th_trace_disable(struct intel_th_device *thdev)
{
struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH);
if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
return -EINVAL;
hubdrv->disable(hub, &thdev->output);
return 0;
}
EXPORT_SYMBOL_GPL(intel_th_trace_disable);
int intel_th_set_output(struct intel_th_device *thdev,
unsigned int master)
{
struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
if (!hubdrv->set_output)
return -ENOTSUPP;
return hubdrv->set_output(hub, master);
}
EXPORT_SYMBOL_GPL(intel_th_set_output);
static int __init intel_th_init(void)
{
intel_th_debug_init();
return bus_register(&intel_th_bus);
}
subsys_initcall(intel_th_init);
static void __exit intel_th_exit(void)
{
intel_th_debug_done();
bus_unregister(&intel_th_bus);
}
module_exit(intel_th_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");

View File

@ -0,0 +1,36 @@
/*
* Intel(R) Trace Hub driver debugging
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/types.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include "intel_th.h"
#include "debug.h"
struct dentry *intel_th_dbg;
void intel_th_debug_init(void)
{
intel_th_dbg = debugfs_create_dir("intel_th", NULL);
if (IS_ERR(intel_th_dbg))
intel_th_dbg = NULL;
}
void intel_th_debug_done(void)
{
debugfs_remove(intel_th_dbg);
intel_th_dbg = NULL;
}

View File

@ -0,0 +1,34 @@
/*
* Intel(R) Trace Hub driver debugging
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_DEBUG_H__
#define __INTEL_TH_DEBUG_H__
#ifdef CONFIG_INTEL_TH_DEBUG
extern struct dentry *intel_th_dbg;
void intel_th_debug_init(void);
void intel_th_debug_done(void);
#else
static inline void intel_th_debug_init(void)
{
}
static inline void intel_th_debug_done(void)
{
}
#endif
#endif /* __INTEL_TH_DEBUG_H__ */

View File

@ -0,0 +1,706 @@
/*
* Intel(R) Trace Hub Global Trace Hub
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
#include "intel_th.h"
#include "gth.h"
struct gth_device;
/**
* struct gth_output - GTH view on an output port
* @gth: backlink to the GTH device
* @output: link to output device's output descriptor
* @index: output port number
* @port_type: one of GTH_* port type values
* @master: bitmap of masters configured for this output
*/
struct gth_output {
struct gth_device *gth;
struct intel_th_output *output;
unsigned int index;
unsigned int port_type;
DECLARE_BITMAP(master, TH_CONFIGURABLE_MASTERS + 1);
};
/**
* struct gth_device - GTH device
* @dev: driver core's device
* @base: register window base address
* @output_group: attributes describing output ports
* @master_group: attributes describing master assignments
* @output: output ports
* @master: master/output port assignments
* @gth_lock: serializes accesses to GTH bits
*/
struct gth_device {
struct device *dev;
void __iomem *base;
struct attribute_group output_group;
struct attribute_group master_group;
struct gth_output output[TH_POSSIBLE_OUTPUTS];
signed char master[TH_CONFIGURABLE_MASTERS + 1];
spinlock_t gth_lock;
};
static void gth_output_set(struct gth_device *gth, int port,
unsigned int config)
{
unsigned long reg = port & 4 ? REG_GTH_GTHOPT1 : REG_GTH_GTHOPT0;
u32 val;
int shift = (port & 3) * 8;
val = ioread32(gth->base + reg);
val &= ~(0xff << shift);
val |= config << shift;
iowrite32(val, gth->base + reg);
}
static unsigned int gth_output_get(struct gth_device *gth, int port)
{
unsigned long reg = port & 4 ? REG_GTH_GTHOPT1 : REG_GTH_GTHOPT0;
u32 val;
int shift = (port & 3) * 8;
val = ioread32(gth->base + reg);
val &= 0xff << shift;
val >>= shift;
return val;
}
static void gth_smcfreq_set(struct gth_device *gth, int port,
unsigned int freq)
{
unsigned long reg = REG_GTH_SMCR0 + ((port / 2) * 4);
int shift = (port & 1) * 16;
u32 val;
val = ioread32(gth->base + reg);
val &= ~(0xffff << shift);
val |= freq << shift;
iowrite32(val, gth->base + reg);
}
static unsigned int gth_smcfreq_get(struct gth_device *gth, int port)
{
unsigned long reg = REG_GTH_SMCR0 + ((port / 2) * 4);
int shift = (port & 1) * 16;
u32 val;
val = ioread32(gth->base + reg);
val &= 0xffff << shift;
val >>= shift;
return val;
}
/*
* "masters" attribute group
*/
struct master_attribute {
struct device_attribute attr;
struct gth_device *gth;
unsigned int master;
};
static void
gth_master_set(struct gth_device *gth, unsigned int master, int port)
{
unsigned int reg = REG_GTH_SWDEST0 + ((master >> 1) & ~3u);
unsigned int shift = (master & 0x7) * 4;
u32 val;
if (master >= 256) {
reg = REG_GTH_GSWTDEST;
shift = 0;
}
val = ioread32(gth->base + reg);
val &= ~(0xf << shift);
if (port >= 0)
val |= (0x8 | port) << shift;
iowrite32(val, gth->base + reg);
}
/*static int gth_master_get(struct gth_device *gth, unsigned int master)
{
unsigned int reg = REG_GTH_SWDEST0 + ((master >> 1) & ~3u);
unsigned int shift = (master & 0x7) * 4;
u32 val;
if (master >= 256) {
reg = REG_GTH_GSWTDEST;
shift = 0;
}
val = ioread32(gth->base + reg);
val &= (0xf << shift);
val >>= shift;
return val ? val & 0x7 : -1;
}*/
static ssize_t master_attr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct master_attribute *ma =
container_of(attr, struct master_attribute, attr);
struct gth_device *gth = ma->gth;
size_t count;
int port;
spin_lock(&gth->gth_lock);
port = gth->master[ma->master];
spin_unlock(&gth->gth_lock);
if (port >= 0)
count = snprintf(buf, PAGE_SIZE, "%x\n", port);
else
count = snprintf(buf, PAGE_SIZE, "disabled\n");
return count;
}
static ssize_t master_attr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct master_attribute *ma =
container_of(attr, struct master_attribute, attr);
struct gth_device *gth = ma->gth;
int old_port, port;
if (kstrtoint(buf, 10, &port) < 0)
return -EINVAL;
if (port >= TH_POSSIBLE_OUTPUTS || port < -1)
return -EINVAL;
spin_lock(&gth->gth_lock);
/* disconnect from the previous output port, if any */
old_port = gth->master[ma->master];
if (old_port >= 0) {
gth->master[ma->master] = -1;
clear_bit(ma->master, gth->output[old_port].master);
if (gth->output[old_port].output->active)
gth_master_set(gth, ma->master, -1);
}
/* connect to the new output port, if any */
if (port >= 0) {
/* check if there's a driver for this port */
if (!gth->output[port].output) {
count = -ENODEV;
goto unlock;
}
set_bit(ma->master, gth->output[port].master);
/* if the port is active, program this setting */
if (gth->output[port].output->active)
gth_master_set(gth, ma->master, port);
}
gth->master[ma->master] = port;
unlock:
spin_unlock(&gth->gth_lock);
return count;
}
struct output_attribute {
struct device_attribute attr;
struct gth_device *gth;
unsigned int port;
unsigned int parm;
};
#define OUTPUT_PARM(_name, _mask, _r, _w, _what) \
[TH_OUTPUT_PARM(_name)] = { .name = __stringify(_name), \
.get = gth_ ## _what ## _get, \
.set = gth_ ## _what ## _set, \
.mask = (_mask), \
.readable = (_r), \
.writable = (_w) }
static const struct output_parm {
const char *name;
unsigned int (*get)(struct gth_device *gth, int port);
void (*set)(struct gth_device *gth, int port,
unsigned int val);
unsigned int mask;
unsigned int readable : 1,
writable : 1;
} output_parms[] = {
OUTPUT_PARM(port, 0x7, 1, 0, output),
OUTPUT_PARM(null, BIT(3), 1, 1, output),
OUTPUT_PARM(drop, BIT(4), 1, 1, output),
OUTPUT_PARM(reset, BIT(5), 1, 0, output),
OUTPUT_PARM(flush, BIT(7), 0, 1, output),
OUTPUT_PARM(smcfreq, 0xffff, 1, 1, smcfreq),
};
static void
gth_output_parm_set(struct gth_device *gth, int port, unsigned int parm,
unsigned int val)
{
unsigned int config = output_parms[parm].get(gth, port);
unsigned int mask = output_parms[parm].mask;
unsigned int shift = __ffs(mask);
config &= ~mask;
config |= (val << shift) & mask;
output_parms[parm].set(gth, port, config);
}
static unsigned int
gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm)
{
unsigned int config = output_parms[parm].get(gth, port);
unsigned int mask = output_parms[parm].mask;
unsigned int shift = __ffs(mask);
config &= mask;
config >>= shift;
return config;
}
/*
* Reset outputs and sources
*/
static int intel_th_gth_reset(struct gth_device *gth)
{
u32 scratchpad;
int port, i;
scratchpad = ioread32(gth->base + REG_GTH_SCRPD0);
if (scratchpad & SCRPD_DEBUGGER_IN_USE)
return -EBUSY;
/* output ports */
for (port = 0; port < 8; port++) {
if (gth_output_parm_get(gth, port, TH_OUTPUT_PARM(port)) ==
GTH_NONE)
continue;
gth_output_set(gth, port, 0);
gth_smcfreq_set(gth, port, 16);
}
/* disable overrides */
iowrite32(0, gth->base + REG_GTH_DESTOVR);
/* masters swdest_0~31 and gswdest */
for (i = 0; i < 33; i++)
iowrite32(0, gth->base + REG_GTH_SWDEST0 + i * 4);
/* sources */
iowrite32(0, gth->base + REG_GTH_SCR);
iowrite32(0xfc, gth->base + REG_GTH_SCR2);
return 0;
}
/*
* "outputs" attribute group
*/
static ssize_t output_attr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct output_attribute *oa =
container_of(attr, struct output_attribute, attr);
struct gth_device *gth = oa->gth;
size_t count;
spin_lock(&gth->gth_lock);
count = snprintf(buf, PAGE_SIZE, "%x\n",
gth_output_parm_get(gth, oa->port, oa->parm));
spin_unlock(&gth->gth_lock);
return count;
}
static ssize_t output_attr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct output_attribute *oa =
container_of(attr, struct output_attribute, attr);
struct gth_device *gth = oa->gth;
unsigned int config;
if (kstrtouint(buf, 16, &config) < 0)
return -EINVAL;
spin_lock(&gth->gth_lock);
gth_output_parm_set(gth, oa->port, oa->parm, config);
spin_unlock(&gth->gth_lock);
return count;
}
static int intel_th_master_attributes(struct gth_device *gth)
{
struct master_attribute *master_attrs;
struct attribute **attrs;
int i, nattrs = TH_CONFIGURABLE_MASTERS + 2;
attrs = devm_kcalloc(gth->dev, nattrs, sizeof(void *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
master_attrs = devm_kcalloc(gth->dev, nattrs,
sizeof(struct master_attribute),
GFP_KERNEL);
if (!master_attrs)
return -ENOMEM;
for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) {
char *name;
name = devm_kasprintf(gth->dev, GFP_KERNEL, "%d%s", i,
i == TH_CONFIGURABLE_MASTERS ? "+" : "");
if (!name)
return -ENOMEM;
master_attrs[i].attr.attr.name = name;
master_attrs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
master_attrs[i].attr.show = master_attr_show;
master_attrs[i].attr.store = master_attr_store;
sysfs_attr_init(&master_attrs[i].attr.attr);
attrs[i] = &master_attrs[i].attr.attr;
master_attrs[i].gth = gth;
master_attrs[i].master = i;
}
gth->master_group.name = "masters";
gth->master_group.attrs = attrs;
return sysfs_create_group(&gth->dev->kobj, &gth->master_group);
}
static int intel_th_output_attributes(struct gth_device *gth)
{
struct output_attribute *out_attrs;
struct attribute **attrs;
int i, j, nouts = TH_POSSIBLE_OUTPUTS;
int nparms = ARRAY_SIZE(output_parms);
int nattrs = nouts * nparms + 1;
attrs = devm_kcalloc(gth->dev, nattrs, sizeof(void *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
out_attrs = devm_kcalloc(gth->dev, nattrs,
sizeof(struct output_attribute),
GFP_KERNEL);
if (!out_attrs)
return -ENOMEM;
for (i = 0; i < nouts; i++) {
for (j = 0; j < nparms; j++) {
unsigned int idx = i * nparms + j;
char *name;
name = devm_kasprintf(gth->dev, GFP_KERNEL, "%d_%s", i,
output_parms[j].name);
if (!name)
return -ENOMEM;
out_attrs[idx].attr.attr.name = name;
if (output_parms[j].readable) {
out_attrs[idx].attr.attr.mode |= S_IRUGO;
out_attrs[idx].attr.show = output_attr_show;
}
if (output_parms[j].writable) {
out_attrs[idx].attr.attr.mode |= S_IWUSR;
out_attrs[idx].attr.store = output_attr_store;
}
sysfs_attr_init(&out_attrs[idx].attr.attr);
attrs[idx] = &out_attrs[idx].attr.attr;
out_attrs[idx].gth = gth;
out_attrs[idx].port = i;
out_attrs[idx].parm = j;
}
}
gth->output_group.name = "outputs";
gth->output_group.attrs = attrs;
return sysfs_create_group(&gth->dev->kobj, &gth->output_group);
}
/**
* intel_th_gth_disable() - enable tracing to an output device
* @thdev: GTH device
* @output: output device's descriptor
*
* This will deconfigure all masters set to output to this device,
* disable tracing using force storeEn off signal and wait for the
* "pipeline empty" bit for corresponding output port.
*/
static void intel_th_gth_disable(struct intel_th_device *thdev,
struct intel_th_output *output)
{
struct gth_device *gth = dev_get_drvdata(&thdev->dev);
unsigned long count;
int master;
u32 reg;
spin_lock(&gth->gth_lock);
output->active = false;
for_each_set_bit(master, gth->output[output->port].master,
TH_CONFIGURABLE_MASTERS) {
gth_master_set(gth, master, -1);
}
spin_unlock(&gth->gth_lock);
iowrite32(0, gth->base + REG_GTH_SCR);
iowrite32(0xfd, gth->base + REG_GTH_SCR2);
/* wait on pipeline empty for the given port */
for (reg = 0, count = GTH_PLE_WAITLOOP_DEPTH;
count && !(reg & BIT(output->port)); count--) {
reg = ioread32(gth->base + REG_GTH_STAT);
cpu_relax();
}
/* clear force capture done for next captures */
iowrite32(0xfc, gth->base + REG_GTH_SCR2);
if (!count)
dev_dbg(&thdev->dev, "timeout waiting for GTH[%d] PLE\n",
output->port);
}
/**
* intel_th_gth_enable() - enable tracing to an output device
* @thdev: GTH device
* @output: output device's descriptor
*
* This will configure all masters set to output to this device and
* enable tracing using force storeEn signal.
*/
static void intel_th_gth_enable(struct intel_th_device *thdev,
struct intel_th_output *output)
{
struct gth_device *gth = dev_get_drvdata(&thdev->dev);
u32 scr = 0xfc0000;
int master;
spin_lock(&gth->gth_lock);
for_each_set_bit(master, gth->output[output->port].master,
TH_CONFIGURABLE_MASTERS + 1) {
gth_master_set(gth, master, output->port);
}
if (output->multiblock)
scr |= 0xff;
output->active = true;
spin_unlock(&gth->gth_lock);
iowrite32(scr, gth->base + REG_GTH_SCR);
iowrite32(0, gth->base + REG_GTH_SCR2);
}
/**
* intel_th_gth_assign() - assign output device to a GTH output port
* @thdev: GTH device
* @othdev: output device
*
* This will match a given output device parameters against present
* output ports on the GTH and fill out relevant bits in output device's
* descriptor.
*
* Return: 0 on success, -errno on error.
*/
static int intel_th_gth_assign(struct intel_th_device *thdev,
struct intel_th_device *othdev)
{
struct gth_device *gth = dev_get_drvdata(&thdev->dev);
int i, id;
if (othdev->type != INTEL_TH_OUTPUT)
return -EINVAL;
for (i = 0, id = 0; i < TH_POSSIBLE_OUTPUTS; i++) {
if (gth->output[i].port_type != othdev->output.type)
continue;
if (othdev->id == -1 || othdev->id == id)
goto found;
id++;
}
return -ENOENT;
found:
spin_lock(&gth->gth_lock);
othdev->output.port = i;
othdev->output.active = false;
gth->output[i].output = &othdev->output;
spin_unlock(&gth->gth_lock);
return 0;
}
/**
* intel_th_gth_unassign() - deassociate an output device from its output port
* @thdev: GTH device
* @othdev: output device
*/
static void intel_th_gth_unassign(struct intel_th_device *thdev,
struct intel_th_device *othdev)
{
struct gth_device *gth = dev_get_drvdata(&thdev->dev);
int port = othdev->output.port;
spin_lock(&gth->gth_lock);
othdev->output.port = -1;
othdev->output.active = false;
gth->output[port].output = NULL;
spin_unlock(&gth->gth_lock);
}
static int
intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master)
{
struct gth_device *gth = dev_get_drvdata(&thdev->dev);
int port = 0; /* FIXME: make default output configurable */
/*
* everything above TH_CONFIGURABLE_MASTERS is controlled by the
* same register
*/
if (master > TH_CONFIGURABLE_MASTERS)
master = TH_CONFIGURABLE_MASTERS;
spin_lock(&gth->gth_lock);
if (gth->master[master] == -1) {
set_bit(master, gth->output[port].master);
gth->master[master] = port;
}
spin_unlock(&gth->gth_lock);
return 0;
}
static int intel_th_gth_probe(struct intel_th_device *thdev)
{
struct device *dev = &thdev->dev;
struct gth_device *gth;
struct resource *res;
void __iomem *base;
int i, ret;
res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
base = devm_ioremap(dev, res->start, resource_size(res));
if (!base)
return -ENOMEM;
gth = devm_kzalloc(dev, sizeof(*gth), GFP_KERNEL);
if (!gth)
return -ENOMEM;
gth->dev = dev;
gth->base = base;
spin_lock_init(&gth->gth_lock);
ret = intel_th_gth_reset(gth);
if (ret)
return ret;
for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++)
gth->master[i] = -1;
for (i = 0; i < TH_POSSIBLE_OUTPUTS; i++) {
gth->output[i].gth = gth;
gth->output[i].index = i;
gth->output[i].port_type =
gth_output_parm_get(gth, i, TH_OUTPUT_PARM(port));
}
if (intel_th_output_attributes(gth) ||
intel_th_master_attributes(gth)) {
pr_warn("Can't initialize sysfs attributes\n");
if (gth->output_group.attrs)
sysfs_remove_group(&gth->dev->kobj, &gth->output_group);
return -ENOMEM;
}
dev_set_drvdata(dev, gth);
return 0;
}
static void intel_th_gth_remove(struct intel_th_device *thdev)
{
struct gth_device *gth = dev_get_drvdata(&thdev->dev);
sysfs_remove_group(&gth->dev->kobj, &gth->output_group);
sysfs_remove_group(&gth->dev->kobj, &gth->master_group);
}
static struct intel_th_driver intel_th_gth_driver = {
.probe = intel_th_gth_probe,
.remove = intel_th_gth_remove,
.assign = intel_th_gth_assign,
.unassign = intel_th_gth_unassign,
.set_output = intel_th_gth_set_output,
.enable = intel_th_gth_enable,
.disable = intel_th_gth_disable,
.driver = {
.name = "gth",
.owner = THIS_MODULE,
},
};
module_driver(intel_th_gth_driver,
intel_th_driver_register,
intel_th_driver_unregister);
MODULE_ALIAS("intel_th_switch");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel(R) Trace Hub Global Trace Hub driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");

View File

@ -0,0 +1,66 @@
/*
* Intel(R) Trace Hub Global Trace Hub (GTH) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_GTH_H__
#define __INTEL_TH_GTH_H__
/* Map output port parameter bits to symbolic names */
#define TH_OUTPUT_PARM(name) \
TH_OUTPUT_ ## name
enum intel_th_output_parm {
/* output port type */
TH_OUTPUT_PARM(port),
/* generate NULL packet */
TH_OUTPUT_PARM(null),
/* packet drop */
TH_OUTPUT_PARM(drop),
/* port in reset state */
TH_OUTPUT_PARM(reset),
/* flush out data */
TH_OUTPUT_PARM(flush),
/* mainenance packet frequency */
TH_OUTPUT_PARM(smcfreq),
};
/*
* Register offsets
*/
enum {
REG_GTH_GTHOPT0 = 0x00, /* Output ports 0..3 config */
REG_GTH_GTHOPT1 = 0x04, /* Output ports 4..7 config */
REG_GTH_SWDEST0 = 0x08, /* Switching destination masters 0..7 */
REG_GTH_GSWTDEST = 0x88, /* Global sw trace destination */
REG_GTH_SMCR0 = 0x9c, /* STP mainenance for ports 0/1 */
REG_GTH_SMCR1 = 0xa0, /* STP mainenance for ports 2/3 */
REG_GTH_SMCR2 = 0xa4, /* STP mainenance for ports 4/5 */
REG_GTH_SMCR3 = 0xa8, /* STP mainenance for ports 6/7 */
REG_GTH_SCR = 0xc8, /* Source control (storeEn override) */
REG_GTH_STAT = 0xd4, /* GTH status */
REG_GTH_SCR2 = 0xd8, /* Source control (force storeEn off) */
REG_GTH_DESTOVR = 0xdc, /* Destination override */
REG_GTH_SCRPD0 = 0xe0, /* ScratchPad[0] */
REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */
REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */
REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */
};
/* Externall debugger is using Intel TH */
#define SCRPD_DEBUGGER_IN_USE BIT(24)
/* waiting for Pipeline Empty bit(s) to assert for GTH */
#define GTH_PLE_WAITLOOP_DEPTH 10000
#endif /* __INTEL_TH_GTH_H__ */

View File

@ -0,0 +1,244 @@
/*
* Intel(R) Trace Hub data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_H__
#define __INTEL_TH_H__
/* intel_th_device device types */
enum {
/* Devices that generate trace data */
INTEL_TH_SOURCE = 0,
/* Output ports (MSC, PTI) */
INTEL_TH_OUTPUT,
/* Switch, the Global Trace Hub (GTH) */
INTEL_TH_SWITCH,
};
/**
* struct intel_th_output - descriptor INTEL_TH_OUTPUT type devices
* @port: output port number, assigned by the switch
* @type: GTH_{MSU,CTP,PTI}
* @multiblock: true for multiblock output configuration
* @active: true when this output is enabled
*
* Output port descriptor, used by switch driver to tell which output
* port this output device corresponds to. Filled in at output device's
* probe time by switch::assign(). Passed from output device driver to
* switch related code to enable/disable its port.
*/
struct intel_th_output {
int port;
unsigned int type;
bool multiblock;
bool active;
};
/**
* struct intel_th_device - device on the intel_th bus
* @dev: device
* @resource: array of resources available to this device
* @num_resources: number of resources in @resource array
* @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH}
* @id: device instance or -1
* @output: output descriptor for INTEL_TH_OUTPUT devices
* @name: device name to match the driver
*/
struct intel_th_device {
struct device dev;
struct resource *resource;
unsigned int num_resources;
unsigned int type;
int id;
/* INTEL_TH_OUTPUT specific */
struct intel_th_output output;
char name[];
};
#define to_intel_th_device(_d) \
container_of((_d), struct intel_th_device, dev)
/**
* intel_th_device_get_resource() - obtain @num'th resource of type @type
* @thdev: the device to search the resource for
* @type: resource type
* @num: number of the resource
*/
static inline struct resource *
intel_th_device_get_resource(struct intel_th_device *thdev, unsigned int type,
unsigned int num)
{
int i;
for (i = 0; i < thdev->num_resources; i++)
if (resource_type(&thdev->resource[i]) == type && !num--)
return &thdev->resource[i];
return NULL;
}
/**
* intel_th_output_assigned() - if an output device is assigned to a switch port
* @thdev: the output device
*
* Return: true if the device is INTEL_TH_OUTPUT *and* is assigned a port
*/
static inline bool
intel_th_output_assigned(struct intel_th_device *thdev)
{
return thdev->type == INTEL_TH_OUTPUT &&
thdev->output.port >= 0;
}
/**
* struct intel_th_driver - driver for an intel_th_device device
* @driver: generic driver
* @probe: probe method
* @remove: remove method
* @assign: match a given output type device against available outputs
* @unassign: deassociate an output type device from an output port
* @enable: enable tracing for a given output device
* @disable: disable tracing for a given output device
* @fops: file operations for device nodes
*
* Callbacks @probe and @remove are required for all device types.
* Switch device driver needs to fill in @assign, @enable and @disable
* callbacks.
*/
struct intel_th_driver {
struct device_driver driver;
int (*probe)(struct intel_th_device *thdev);
void (*remove)(struct intel_th_device *thdev);
/* switch (GTH) ops */
int (*assign)(struct intel_th_device *thdev,
struct intel_th_device *othdev);
void (*unassign)(struct intel_th_device *thdev,
struct intel_th_device *othdev);
void (*enable)(struct intel_th_device *thdev,
struct intel_th_output *output);
void (*disable)(struct intel_th_device *thdev,
struct intel_th_output *output);
/* output ops */
void (*irq)(struct intel_th_device *thdev);
int (*activate)(struct intel_th_device *thdev);
void (*deactivate)(struct intel_th_device *thdev);
/* file_operations for those who want a device node */
const struct file_operations *fops;
/* source ops */
int (*set_output)(struct intel_th_device *thdev,
unsigned int master);
};
#define to_intel_th_driver(_d) \
container_of((_d), struct intel_th_driver, driver)
static inline struct intel_th_device *
to_intel_th_hub(struct intel_th_device *thdev)
{
struct device *parent = thdev->dev.parent;
if (!parent)
return NULL;
return to_intel_th_device(parent);
}
struct intel_th *
intel_th_alloc(struct device *dev, struct resource *devres,
unsigned int ndevres, int irq);
void intel_th_free(struct intel_th *th);
int intel_th_driver_register(struct intel_th_driver *thdrv);
void intel_th_driver_unregister(struct intel_th_driver *thdrv);
int intel_th_trace_enable(struct intel_th_device *thdev);
int intel_th_trace_disable(struct intel_th_device *thdev);
int intel_th_set_output(struct intel_th_device *thdev,
unsigned int master);
enum {
TH_MMIO_CONFIG = 0,
TH_MMIO_SW = 2,
TH_MMIO_END,
};
#define TH_SUBDEVICE_MAX 6
#define TH_POSSIBLE_OUTPUTS 8
#define TH_CONFIGURABLE_MASTERS 256
#define TH_MSC_MAX 2
/**
* struct intel_th - Intel TH controller
* @dev: driver core's device
* @thdev: subdevices
* @hub: "switch" subdevice (GTH)
* @id: this Intel TH controller's device ID in the system
* @major: device node major for output devices
*/
struct intel_th {
struct device *dev;
struct intel_th_device *thdev[TH_SUBDEVICE_MAX];
struct intel_th_device *hub;
int id;
int major;
#ifdef CONFIG_INTEL_TH_DEBUG
struct dentry *dbg;
#endif
};
/*
* Register windows
*/
enum {
/* Global Trace Hub (GTH) */
REG_GTH_OFFSET = 0x0000,
REG_GTH_LENGTH = 0x2000,
/* Software Trace Hub (STH) [0x4000..0x4fff] */
REG_STH_OFFSET = 0x4000,
REG_STH_LENGTH = 0x2000,
/* Memory Storage Unit (MSU) [0xa0000..0xa1fff] */
REG_MSU_OFFSET = 0xa0000,
REG_MSU_LENGTH = 0x02000,
/* Internal MSU trace buffer [0x80000..0x9ffff] */
BUF_MSU_OFFSET = 0x80000,
BUF_MSU_LENGTH = 0x20000,
/* PTI output == same window as GTH */
REG_PTI_OFFSET = REG_GTH_OFFSET,
REG_PTI_LENGTH = REG_GTH_LENGTH,
/* DCI Handler (DCIH) == some window as MSU */
REG_DCIH_OFFSET = REG_MSU_OFFSET,
REG_DCIH_LENGTH = REG_MSU_LENGTH,
};
/*
* GTH, output ports configuration
*/
enum {
GTH_NONE = 0,
GTH_MSU, /* memory/usb */
GTH_CTP, /* Common Trace Port */
GTH_PTI = 4, /* MIPI-PTI */
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
/*
* Intel(R) Trace Hub Memory Storage Unit (MSU) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_MSU_H__
#define __INTEL_TH_MSU_H__
enum {
REG_MSU_MSUPARAMS = 0x0000,
REG_MSU_MSUSTS = 0x0008,
REG_MSU_MSC0CTL = 0x0100, /* MSC0 control */
REG_MSU_MSC0STS = 0x0104, /* MSC0 status */
REG_MSU_MSC0BAR = 0x0108, /* MSC0 output base address */
REG_MSU_MSC0SIZE = 0x010c, /* MSC0 output size */
REG_MSU_MSC0MWP = 0x0110, /* MSC0 write pointer */
REG_MSU_MSC0NWSA = 0x011c, /* MSC0 next window start address */
REG_MSU_MSC1CTL = 0x0200, /* MSC1 control */
REG_MSU_MSC1STS = 0x0204, /* MSC1 status */
REG_MSU_MSC1BAR = 0x0208, /* MSC1 output base address */
REG_MSU_MSC1SIZE = 0x020c, /* MSC1 output size */
REG_MSU_MSC1MWP = 0x0210, /* MSC1 write pointer */
REG_MSU_MSC1NWSA = 0x021c, /* MSC1 next window start address */
};
/* MSUSTS bits */
#define MSUSTS_MSU_INT BIT(0)
/* MSCnCTL bits */
#define MSC_EN BIT(0)
#define MSC_WRAPEN BIT(1)
#define MSC_RD_HDR_OVRD BIT(2)
#define MSC_MODE (BIT(4) | BIT(5))
#define MSC_LEN (BIT(8) | BIT(9) | BIT(10))
/* MSC operating modes (MSC_MODE) */
enum {
MSC_MODE_SINGLE = 0,
MSC_MODE_MULTI,
MSC_MODE_EXI,
MSC_MODE_DEBUG,
};
/* MSCnSTS bits */
#define MSCSTS_WRAPSTAT BIT(1) /* Wrap occurred */
#define MSCSTS_PLE BIT(2) /* Pipeline Empty */
/*
* Multiblock/multiwindow block descriptor
*/
struct msc_block_desc {
u32 sw_tag;
u32 block_sz;
u32 next_blk;
u32 next_win;
u32 res0[4];
u32 hw_tag;
u32 valid_dw;
u32 ts_low;
u32 ts_high;
u32 res1[4];
} __packed;
#define MSC_BDESC sizeof(struct msc_block_desc)
#define DATA_IN_PAGE (PAGE_SIZE - MSC_BDESC)
/* MSC multiblock sw tag bits */
#define MSC_SW_TAG_LASTBLK BIT(0)
#define MSC_SW_TAG_LASTWIN BIT(1)
/* MSC multiblock hw tag bits */
#define MSC_HW_TAG_TRIGGER BIT(0)
#define MSC_HW_TAG_BLOCKWRAP BIT(1)
#define MSC_HW_TAG_WINWRAP BIT(2)
#define MSC_HW_TAG_ENDBIT BIT(3)
static inline unsigned long msc_data_sz(struct msc_block_desc *bdesc)
{
if (!bdesc->valid_dw)
return 0;
return bdesc->valid_dw * 4 - MSC_BDESC;
}
static inline bool msc_block_wrapped(struct msc_block_desc *bdesc)
{
if (bdesc->hw_tag & MSC_HW_TAG_BLOCKWRAP)
return true;
return false;
}
static inline bool msc_block_last_written(struct msc_block_desc *bdesc)
{
if ((bdesc->hw_tag & MSC_HW_TAG_ENDBIT) ||
(msc_data_sz(bdesc) != DATA_IN_PAGE))
return true;
return false;
}
/* waiting for Pipeline Empty bit(s) to assert for MSC */
#define MSC_PLE_WAITLOOP_DEPTH 10000
#endif /* __INTEL_TH_MSU_H__ */

View File

@ -0,0 +1,86 @@
/*
* Intel(R) Trace Hub pci driver
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/pci.h>
#include "intel_th.h"
#define DRIVER_NAME "intel_th_pci"
#define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW))
static int intel_th_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct intel_th *th;
int err;
err = pcim_enable_device(pdev);
if (err)
return err;
err = pcim_iomap_regions_request_all(pdev, BAR_MASK, DRIVER_NAME);
if (err)
return err;
th = intel_th_alloc(&pdev->dev, pdev->resource,
DEVICE_COUNT_RESOURCE, pdev->irq);
if (IS_ERR(th))
return PTR_ERR(th);
pci_set_drvdata(pdev, th);
return 0;
}
static void intel_th_pci_remove(struct pci_dev *pdev)
{
struct intel_th *th = pci_get_drvdata(pdev);
intel_th_free(th);
}
static const struct pci_device_id intel_th_pci_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26),
.driver_data = (kernel_ulong_t)0,
},
{
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa126),
.driver_data = (kernel_ulong_t)0,
},
{ 0 },
};
MODULE_DEVICE_TABLE(pci, intel_th_pci_id_table);
static struct pci_driver intel_th_pci_driver = {
.name = DRIVER_NAME,
.id_table = intel_th_pci_id_table,
.probe = intel_th_pci_probe,
.remove = intel_th_pci_remove,
};
module_pci_driver(intel_th_pci_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel(R) Trace Hub PCI controller driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");

View File

@ -0,0 +1,252 @@
/*
* Intel(R) Trace Hub PTI output driver
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sizes.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include "intel_th.h"
#include "pti.h"
struct pti_device {
void __iomem *base;
struct intel_th_device *thdev;
unsigned int mode;
unsigned int freeclk;
unsigned int clkdiv;
unsigned int patgen;
};
/* map PTI widths to MODE settings of PTI_CTL register */
static const unsigned int pti_mode[] = {
0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
};
static int pti_width_mode(unsigned int width)
{
int i;
for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
if (pti_mode[i] == width)
return i;
return -EINVAL;
}
static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pti_device *pti = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
}
static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct pti_device *pti = dev_get_drvdata(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
ret = pti_width_mode(val);
if (ret < 0)
return ret;
pti->mode = ret;
return size;
}
static DEVICE_ATTR_RW(mode);
static ssize_t
freerunning_clock_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pti_device *pti = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
}
static ssize_t
freerunning_clock_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct pti_device *pti = dev_get_drvdata(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
pti->freeclk = !!val;
return size;
}
static DEVICE_ATTR_RW(freerunning_clock);
static ssize_t
clock_divider_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pti_device *pti = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
}
static ssize_t
clock_divider_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct pti_device *pti = dev_get_drvdata(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (!is_power_of_2(val) || val > 8 || !val)
return -EINVAL;
pti->clkdiv = val;
return size;
}
static DEVICE_ATTR_RW(clock_divider);
static struct attribute *pti_output_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_freerunning_clock.attr,
&dev_attr_clock_divider.attr,
NULL,
};
static struct attribute_group pti_output_group = {
.attrs = pti_output_attrs,
};
static int intel_th_pti_activate(struct intel_th_device *thdev)
{
struct pti_device *pti = dev_get_drvdata(&thdev->dev);
u32 ctl = PTI_EN;
if (pti->patgen)
ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
if (pti->freeclk)
ctl |= PTI_FCEN;
ctl |= pti->mode << __ffs(PTI_MODE);
ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
iowrite32(ctl, pti->base + REG_PTI_CTL);
intel_th_trace_enable(thdev);
return 0;
}
static void intel_th_pti_deactivate(struct intel_th_device *thdev)
{
struct pti_device *pti = dev_get_drvdata(&thdev->dev);
intel_th_trace_disable(thdev);
iowrite32(0, pti->base + REG_PTI_CTL);
}
static void read_hw_config(struct pti_device *pti)
{
u32 ctl = ioread32(pti->base + REG_PTI_CTL);
pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE);
pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
pti->freeclk = !!(ctl & PTI_FCEN);
if (!pti_mode[pti->mode])
pti->mode = pti_width_mode(4);
if (!pti->clkdiv)
pti->clkdiv = 1;
}
static int intel_th_pti_probe(struct intel_th_device *thdev)
{
struct device *dev = &thdev->dev;
struct resource *res;
struct pti_device *pti;
void __iomem *base;
int ret;
res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
base = devm_ioremap(dev, res->start, resource_size(res));
if (!base)
return -ENOMEM;
pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
if (!pti)
return -ENOMEM;
pti->thdev = thdev;
pti->base = base;
read_hw_config(pti);
ret = sysfs_create_group(&dev->kobj, &pti_output_group);
if (ret)
return ret;
dev_set_drvdata(dev, pti);
return 0;
}
static void intel_th_pti_remove(struct intel_th_device *thdev)
{
}
static struct intel_th_driver intel_th_pti_driver = {
.probe = intel_th_pti_probe,
.remove = intel_th_pti_remove,
.activate = intel_th_pti_activate,
.deactivate = intel_th_pti_deactivate,
.driver = {
.name = "pti",
.owner = THIS_MODULE,
},
};
module_driver(intel_th_pti_driver,
intel_th_driver_register,
intel_th_driver_unregister);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");

View File

@ -0,0 +1,29 @@
/*
* Intel(R) Trace Hub PTI output data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_STH_H__
#define __INTEL_TH_STH_H__
enum {
REG_PTI_CTL = 0x1c00,
};
#define PTI_EN BIT(0)
#define PTI_FCEN BIT(1)
#define PTI_MODE 0xf0
#define PTI_CLKDIV 0x000f0000
#define PTI_PATGENMODE 0x00f00000
#endif /* __INTEL_TH_STH_H__ */

View File

@ -0,0 +1,259 @@
/*
* Intel(R) Trace Hub Software Trace Hub support
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/stm.h>
#include "intel_th.h"
#include "sth.h"
struct sth_device {
void __iomem *base;
void __iomem *channels;
phys_addr_t channels_phys;
struct device *dev;
struct stm_data stm;
unsigned int sw_nmasters;
};
static struct intel_th_channel __iomem *
sth_channel(struct sth_device *sth, unsigned int master, unsigned int channel)
{
struct intel_th_channel __iomem *sw_map = sth->channels;
return &sw_map[(master - sth->stm.sw_start) * sth->stm.sw_nchannels +
channel];
}
static void sth_iowrite(void __iomem *dest, const unsigned char *payload,
unsigned int size)
{
switch (size) {
#ifdef CONFIG_64BIT
case 8:
writeq_relaxed(*(u64 *)payload, dest);
break;
#endif
case 4:
writel_relaxed(*(u32 *)payload, dest);
break;
case 2:
writew_relaxed(*(u16 *)payload, dest);
break;
case 1:
writeb_relaxed(*(u8 *)payload, dest);
break;
default:
break;
}
}
static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int packet,
unsigned int flags, unsigned int size,
const unsigned char *payload)
{
struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
struct intel_th_channel __iomem *out =
sth_channel(sth, master, channel);
u64 __iomem *outp = &out->Dn;
unsigned long reg = REG_STH_TRIG;
#ifndef CONFIG_64BIT
if (size > 4)
size = 4;
#endif
size = rounddown_pow_of_two(size);
switch (packet) {
/* Global packets (GERR, XSYNC, TRIG) are sent with register writes */
case STP_PACKET_GERR:
reg += 4;
case STP_PACKET_XSYNC:
reg += 8;
case STP_PACKET_TRIG:
if (flags & STP_PACKET_TIMESTAMPED)
reg += 4;
iowrite8(*payload, sth->base + reg);
break;
case STP_PACKET_MERR:
sth_iowrite(&out->MERR, payload, size);
break;
case STP_PACKET_FLAG:
if (flags & STP_PACKET_TIMESTAMPED)
outp = (u64 __iomem *)&out->FLAG_TS;
else
outp = (u64 __iomem *)&out->FLAG;
size = 1;
sth_iowrite(outp, payload, size);
break;
case STP_PACKET_USER:
if (flags & STP_PACKET_TIMESTAMPED)
outp = &out->USER_TS;
else
outp = &out->USER;
sth_iowrite(outp, payload, size);
break;
case STP_PACKET_DATA:
outp = &out->Dn;
if (flags & STP_PACKET_TIMESTAMPED)
outp += 2;
if (flags & STP_PACKET_MARKED)
outp++;
sth_iowrite(outp, payload, size);
break;
}
return size;
}
static phys_addr_t
sth_stm_mmio_addr(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int nr_chans)
{
struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
phys_addr_t addr;
master -= sth->stm.sw_start;
addr = sth->channels_phys + (master * sth->stm.sw_nchannels + channel) *
sizeof(struct intel_th_channel);
if (offset_in_page(addr) ||
offset_in_page(nr_chans * sizeof(struct intel_th_channel)))
return 0;
return addr;
}
static int sth_stm_link(struct stm_data *stm_data, unsigned int master,
unsigned int channel)
{
struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
intel_th_set_output(to_intel_th_device(sth->dev), master);
return 0;
}
static int intel_th_sw_init(struct sth_device *sth)
{
u32 reg;
reg = ioread32(sth->base + REG_STH_STHCAP1);
sth->stm.sw_nchannels = reg & 0xff;
reg = ioread32(sth->base + REG_STH_STHCAP0);
sth->stm.sw_start = reg & 0xffff;
sth->stm.sw_end = reg >> 16;
sth->sw_nmasters = sth->stm.sw_end - sth->stm.sw_start;
dev_dbg(sth->dev, "sw_start: %x sw_end: %x masters: %x nchannels: %x\n",
sth->stm.sw_start, sth->stm.sw_end, sth->sw_nmasters,
sth->stm.sw_nchannels);
return 0;
}
static int intel_th_sth_probe(struct intel_th_device *thdev)
{
struct device *dev = &thdev->dev;
struct sth_device *sth;
struct resource *res;
void __iomem *base, *channels;
int err;
res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
base = devm_ioremap(dev, res->start, resource_size(res));
if (!base)
return -ENOMEM;
res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 1);
if (!res)
return -ENODEV;
channels = devm_ioremap(dev, res->start, resource_size(res));
if (!channels)
return -ENOMEM;
sth = devm_kzalloc(dev, sizeof(*sth), GFP_KERNEL);
if (!sth)
return -ENOMEM;
sth->dev = dev;
sth->base = base;
sth->channels = channels;
sth->channels_phys = res->start;
sth->stm.name = dev_name(dev);
sth->stm.packet = sth_stm_packet;
sth->stm.mmio_addr = sth_stm_mmio_addr;
sth->stm.sw_mmiosz = sizeof(struct intel_th_channel);
sth->stm.link = sth_stm_link;
err = intel_th_sw_init(sth);
if (err)
return err;
err = stm_register_device(dev, &sth->stm, THIS_MODULE);
if (err) {
dev_err(dev, "stm_register_device failed\n");
return err;
}
dev_set_drvdata(dev, sth);
return 0;
}
static void intel_th_sth_remove(struct intel_th_device *thdev)
{
struct sth_device *sth = dev_get_drvdata(&thdev->dev);
stm_unregister_device(&sth->stm);
}
static struct intel_th_driver intel_th_sth_driver = {
.probe = intel_th_sth_probe,
.remove = intel_th_sth_remove,
.driver = {
.name = "sth",
.owner = THIS_MODULE,
},
};
module_driver(intel_th_sth_driver,
intel_th_driver_register,
intel_th_driver_unregister);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel(R) Trace Hub Software Trace Hub driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");

View File

@ -0,0 +1,42 @@
/*
* Intel(R) Trace Hub Software Trace Hub (STH) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __INTEL_TH_STH_H__
#define __INTEL_TH_STH_H__
enum {
REG_STH_STHCAP0 = 0x0000, /* capabilities pt1 */
REG_STH_STHCAP1 = 0x0004, /* capabilities pt2 */
REG_STH_TRIG = 0x0008, /* TRIG packet payload */
REG_STH_TRIG_TS = 0x000c, /* TRIG_TS packet payload */
REG_STH_XSYNC = 0x0010, /* XSYNC packet payload */
REG_STH_XSYNC_TS = 0x0014, /* XSYNC_TS packet payload */
REG_STH_GERR = 0x0018, /* GERR packet payload */
};
struct intel_th_channel {
u64 Dn;
u64 DnM;
u64 DnTS;
u64 DnMTS;
u64 USER;
u64 USER_TS;
u32 FLAG;
u32 FLAG_TS;
u32 MERR;
u32 __unused;
} __packed;
#endif /* __INTEL_TH_STH_H__ */

View File

@ -0,0 +1,26 @@
config STM
tristate "System Trace Module devices"
select CONFIGFS_FS
help
A System Trace Module (STM) is a device exporting data in System
Trace Protocol (STP) format as defined by MIPI STP standards.
Examples of such devices are Intel(R) Trace Hub and Coresight STM.
Say Y here to enable System Trace Module device support.
config STM_DUMMY
tristate "Dummy STM driver"
help
This is a simple dummy device that pretends to be an stm device
and discards your data. Use for stm class testing.
If you don't know what this is, say N.
config STM_SOURCE_CONSOLE
tristate "Kernel console over STM devices"
help
This is a kernel space trace source that sends kernel log
messages to trace hosts over STM devices.
If you want to send kernel console messages over STM devices,
say Y.

View File

@ -0,0 +1,9 @@
obj-$(CONFIG_STM) += stm_core.o
stm_core-y := core.o policy.o
obj-$(CONFIG_STM_DUMMY) += dummy_stm.o
obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o
stm_console-y := console.o

View File

@ -0,0 +1,80 @@
/*
* Simple kernel console driver for STM devices
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM console will send kernel messages over STM devices to a trace host.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/slab.h>
#include <linux/stm.h>
static int stm_console_link(struct stm_source_data *data);
static void stm_console_unlink(struct stm_source_data *data);
static struct stm_console {
struct stm_source_data data;
struct console console;
} stm_console = {
.data = {
.name = "console",
.nr_chans = 1,
.link = stm_console_link,
.unlink = stm_console_unlink,
},
};
static void
stm_console_write(struct console *con, const char *buf, unsigned len)
{
struct stm_console *sc = container_of(con, struct stm_console, console);
stm_source_write(&sc->data, 0, buf, len);
}
static int stm_console_link(struct stm_source_data *data)
{
struct stm_console *sc = container_of(data, struct stm_console, data);
strcpy(sc->console.name, "stm_console");
sc->console.write = stm_console_write;
sc->console.flags = CON_ENABLED | CON_PRINTBUFFER;
register_console(&sc->console);
return 0;
}
static void stm_console_unlink(struct stm_source_data *data)
{
struct stm_console *sc = container_of(data, struct stm_console, data);
unregister_console(&sc->console);
}
static int stm_console_init(void)
{
return stm_source_register_device(NULL, &stm_console.data);
}
static void stm_console_exit(void)
{
stm_source_unregister_device(&stm_console.data);
}
module_init(stm_console_init);
module_exit(stm_console_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("stm_console driver");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
/*
* A dummy STM device for stm/stm_source class testing.
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stm.h>
static ssize_t
dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int packet, unsigned int flags,
unsigned int size, const unsigned char *payload)
{
#ifdef DEBUG
u64 pl = 0;
if (payload)
pl = *(u64 *)payload;
if (size < 8)
pl &= (1ull << (size * 8)) - 1;
trace_printk("[%u:%u] [pkt: %x/%x] (%llx)\n", master, channel,
packet, size, pl);
#endif
return size;
}
static struct stm_data dummy_stm = {
.name = "dummy_stm",
.sw_start = 0x0000,
.sw_end = 0xffff,
.sw_nchannels = 0xffff,
.packet = dummy_stm_packet,
};
static int dummy_stm_init(void)
{
return stm_register_device(NULL, &dummy_stm, THIS_MODULE);
}
static void dummy_stm_exit(void)
{
stm_unregister_device(&dummy_stm);
}
module_init(dummy_stm_init);
module_exit(dummy_stm_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("dummy_stm device");
MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");

View File

@ -0,0 +1,529 @@
/*
* System Trace Module (STM) master/channel allocation policy management
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* A master/channel allocation policy allows mapping string identifiers to
* master and channel ranges, where allocation can be done.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/configfs.h>
#include <linux/slab.h>
#include <linux/stm.h>
#include "stm.h"
/*
* STP Master/Channel allocation policy configfs layout.
*/
struct stp_policy {
struct config_group group;
struct stm_device *stm;
};
struct stp_policy_node {
struct config_group group;
struct stp_policy *policy;
unsigned int first_master;
unsigned int last_master;
unsigned int first_channel;
unsigned int last_channel;
};
static struct configfs_subsystem stp_policy_subsys;
void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
unsigned int *mstart, unsigned int *mend,
unsigned int *cstart, unsigned int *cend)
{
*mstart = policy_node->first_master;
*mend = policy_node->last_master;
*cstart = policy_node->first_channel;
*cend = policy_node->last_channel;
}
static inline char *stp_policy_node_name(struct stp_policy_node *policy_node)
{
return policy_node->group.cg_item.ci_name ? : "<none>";
}
static inline struct stp_policy *to_stp_policy(struct config_item *item)
{
return item ?
container_of(to_config_group(item), struct stp_policy, group) :
NULL;
}
static inline struct stp_policy_node *
to_stp_policy_node(struct config_item *item)
{
return item ?
container_of(to_config_group(item), struct stp_policy_node,
group) :
NULL;
}
static ssize_t stp_policy_node_masters_show(struct stp_policy_node *policy_node,
char *page)
{
ssize_t count;
count = sprintf(page, "%u %u\n", policy_node->first_master,
policy_node->last_master);
return count;
}
static ssize_t
stp_policy_node_masters_store(struct stp_policy_node *policy_node,
const char *page, size_t count)
{
unsigned int first, last;
struct stm_device *stm;
char *p = (char *)page;
ssize_t ret = -ENODEV;
if (sscanf(p, "%u %u", &first, &last) != 2)
return -EINVAL;
mutex_lock(&stp_policy_subsys.su_mutex);
stm = policy_node->policy->stm;
if (!stm)
goto unlock;
/* must be within [sw_start..sw_end], which is an inclusive range */
if (first > INT_MAX || last > INT_MAX || first > last ||
first < stm->data->sw_start ||
last > stm->data->sw_end) {
ret = -ERANGE;
goto unlock;
}
ret = count;
policy_node->first_master = first;
policy_node->last_master = last;
unlock:
mutex_unlock(&stp_policy_subsys.su_mutex);
return ret;
}
static ssize_t
stp_policy_node_channels_show(struct stp_policy_node *policy_node, char *page)
{
ssize_t count;
count = sprintf(page, "%u %u\n", policy_node->first_channel,
policy_node->last_channel);
return count;
}
static ssize_t
stp_policy_node_channels_store(struct stp_policy_node *policy_node,
const char *page, size_t count)
{
unsigned int first, last;
struct stm_device *stm;
char *p = (char *)page;
ssize_t ret = -ENODEV;
if (sscanf(p, "%u %u", &first, &last) != 2)
return -EINVAL;
mutex_lock(&stp_policy_subsys.su_mutex);
stm = policy_node->policy->stm;
if (!stm)
goto unlock;
if (first > INT_MAX || last > INT_MAX || first > last ||
last >= stm->data->sw_nchannels) {
ret = -ERANGE;
goto unlock;
}
ret = count;
policy_node->first_channel = first;
policy_node->last_channel = last;
unlock:
mutex_unlock(&stp_policy_subsys.su_mutex);
return ret;
}
static void stp_policy_node_release(struct config_item *item)
{
kfree(to_stp_policy_node(item));
}
struct stp_policy_node_attribute {
struct configfs_attribute attr;
ssize_t (*show)(struct stp_policy_node *, char *);
ssize_t (*store)(struct stp_policy_node *, const char *, size_t);
};
static ssize_t stp_policy_node_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *page)
{
struct stp_policy_node *policy_node = to_stp_policy_node(item);
struct stp_policy_node_attribute *pn_attr =
container_of(attr, struct stp_policy_node_attribute, attr);
ssize_t count = 0;
if (pn_attr->show)
count = pn_attr->show(policy_node, page);
return count;
}
static ssize_t stp_policy_node_attr_store(struct config_item *item,
struct configfs_attribute *attr,
const char *page, size_t len)
{
struct stp_policy_node *policy_node = to_stp_policy_node(item);
struct stp_policy_node_attribute *pn_attr =
container_of(attr, struct stp_policy_node_attribute, attr);
ssize_t count = -EINVAL;
if (pn_attr->store)
count = pn_attr->store(policy_node, page, len);
return count;
}
static struct configfs_item_operations stp_policy_node_item_ops = {
.release = stp_policy_node_release,
.show_attribute = stp_policy_node_attr_show,
.store_attribute = stp_policy_node_attr_store,
};
static struct stp_policy_node_attribute stp_policy_node_attr_range = {
.attr = {
.ca_owner = THIS_MODULE,
.ca_name = "masters",
.ca_mode = S_IRUGO | S_IWUSR,
},
.show = stp_policy_node_masters_show,
.store = stp_policy_node_masters_store,
};
static struct stp_policy_node_attribute stp_policy_node_attr_channels = {
.attr = {
.ca_owner = THIS_MODULE,
.ca_name = "channels",
.ca_mode = S_IRUGO | S_IWUSR,
},
.show = stp_policy_node_channels_show,
.store = stp_policy_node_channels_store,
};
static struct configfs_attribute *stp_policy_node_attrs[] = {
&stp_policy_node_attr_range.attr,
&stp_policy_node_attr_channels.attr,
NULL,
};
static struct config_item_type stp_policy_type;
static struct config_item_type stp_policy_node_type;
static struct config_group *
stp_policy_node_make(struct config_group *group, const char *name)
{
struct stp_policy_node *policy_node, *parent_node;
struct stp_policy *policy;
if (group->cg_item.ci_type == &stp_policy_type) {
policy = container_of(group, struct stp_policy, group);
} else {
parent_node = container_of(group, struct stp_policy_node,
group);
policy = parent_node->policy;
}
if (!policy->stm)
return ERR_PTR(-ENODEV);
policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL);
if (!policy_node)
return ERR_PTR(-ENOMEM);
config_group_init_type_name(&policy_node->group, name,
&stp_policy_node_type);
policy_node->policy = policy;
/* default values for the attributes */
policy_node->first_master = policy->stm->data->sw_start;
policy_node->last_master = policy->stm->data->sw_end;
policy_node->first_channel = 0;
policy_node->last_channel = policy->stm->data->sw_nchannels - 1;
return &policy_node->group;
}
static void
stp_policy_node_drop(struct config_group *group, struct config_item *item)
{
config_item_put(item);
}
static struct configfs_group_operations stp_policy_node_group_ops = {
.make_group = stp_policy_node_make,
.drop_item = stp_policy_node_drop,
};
static struct config_item_type stp_policy_node_type = {
.ct_item_ops = &stp_policy_node_item_ops,
.ct_group_ops = &stp_policy_node_group_ops,
.ct_attrs = stp_policy_node_attrs,
.ct_owner = THIS_MODULE,
};
/*
* Root group: policies.
*/
static struct configfs_attribute stp_policy_attr_device = {
.ca_owner = THIS_MODULE,
.ca_name = "device",
.ca_mode = S_IRUGO,
};
static struct configfs_attribute *stp_policy_attrs[] = {
&stp_policy_attr_device,
NULL,
};
static ssize_t stp_policy_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *page)
{
struct stp_policy *policy = to_stp_policy(item);
ssize_t count;
count = sprintf(page, "%s\n",
(policy && policy->stm) ?
policy->stm->data->name :
"<none>");
return count;
}
void stp_policy_unbind(struct stp_policy *policy)
{
struct stm_device *stm = policy->stm;
if (WARN_ON_ONCE(!policy->stm))
return;
mutex_lock(&stm->policy_mutex);
stm->policy = NULL;
mutex_unlock(&stm->policy_mutex);
policy->stm = NULL;
stm_put_device(stm);
}
static void stp_policy_release(struct config_item *item)
{
struct stp_policy *policy = to_stp_policy(item);
stp_policy_unbind(policy);
kfree(policy);
}
static struct configfs_item_operations stp_policy_item_ops = {
.release = stp_policy_release,
.show_attribute = stp_policy_attr_show,
};
static struct configfs_group_operations stp_policy_group_ops = {
.make_group = stp_policy_node_make,
};
static struct config_item_type stp_policy_type = {
.ct_item_ops = &stp_policy_item_ops,
.ct_group_ops = &stp_policy_group_ops,
.ct_attrs = stp_policy_attrs,
.ct_owner = THIS_MODULE,
};
static struct config_group *
stp_policies_make(struct config_group *group, const char *name)
{
struct config_group *ret;
struct stm_device *stm;
char *devname, *p;
devname = kasprintf(GFP_KERNEL, "%s", name);
if (!devname)
return ERR_PTR(-ENOMEM);
/*
* node must look like <device_name>.<policy_name>, where
* <device_name> is the name of an existing stm device and
* <policy_name> is an arbitrary string
*/
p = strchr(devname, '.');
if (!p) {
kfree(devname);
return ERR_PTR(-EINVAL);
}
*p++ = '\0';
stm = stm_find_device(devname);
kfree(devname);
if (!stm)
return ERR_PTR(-ENODEV);
mutex_lock(&stm->policy_mutex);
if (stm->policy) {
ret = ERR_PTR(-EBUSY);
goto unlock_policy;
}
stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL);
if (!stm->policy) {
ret = ERR_PTR(-ENOMEM);
goto unlock_policy;
}
config_group_init_type_name(&stm->policy->group, name,
&stp_policy_type);
stm->policy->stm = stm;
ret = &stm->policy->group;
unlock_policy:
mutex_unlock(&stm->policy_mutex);
if (IS_ERR(ret))
stm_put_device(stm);
return ret;
}
static struct configfs_group_operations stp_policies_group_ops = {
.make_group = stp_policies_make,
};
static struct config_item_type stp_policies_type = {
.ct_group_ops = &stp_policies_group_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem stp_policy_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "stp-policy",
.ci_type = &stp_policies_type,
},
},
};
/*
* Lock the policy mutex from the outside
*/
static struct stp_policy_node *
__stp_policy_node_lookup(struct stp_policy *policy, char *s)
{
struct stp_policy_node *policy_node, *ret;
struct list_head *head = &policy->group.cg_children;
struct config_item *item;
char *start, *end = s;
if (list_empty(head))
return NULL;
/* return the first entry if everything else fails */
item = list_entry(head->next, struct config_item, ci_entry);
ret = to_stp_policy_node(item);
next:
for (;;) {
start = strsep(&end, "/");
if (!start)
break;
if (!*start)
continue;
list_for_each_entry(item, head, ci_entry) {
policy_node = to_stp_policy_node(item);
if (!strcmp(start,
policy_node->group.cg_item.ci_name)) {
ret = policy_node;
if (!end)
goto out;
head = &policy_node->group.cg_children;
goto next;
}
}
break;
}
out:
return ret;
}
struct stp_policy_node *
stp_policy_node_lookup(struct stm_device *stm, char *s)
{
struct stp_policy_node *policy_node = NULL;
mutex_lock(&stp_policy_subsys.su_mutex);
mutex_lock(&stm->policy_mutex);
if (stm->policy)
policy_node = __stp_policy_node_lookup(stm->policy, s);
mutex_unlock(&stm->policy_mutex);
if (policy_node)
config_item_get(&policy_node->group.cg_item);
mutex_unlock(&stp_policy_subsys.su_mutex);
return policy_node;
}
void stp_policy_node_put(struct stp_policy_node *policy_node)
{
config_item_put(&policy_node->group.cg_item);
}
int __init stp_configfs_init(void)
{
int err;
config_group_init(&stp_policy_subsys.su_group);
mutex_init(&stp_policy_subsys.su_mutex);
err = configfs_register_subsystem(&stp_policy_subsys);
return err;
}
void __exit stp_configfs_exit(void)
{
configfs_unregister_subsystem(&stp_policy_subsys);
}

View File

@ -0,0 +1,87 @@
/*
* System Trace Module (STM) infrastructure
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
#ifndef _STM_STM_H_
#define _STM_STM_H_
struct stp_policy;
struct stp_policy_node;
struct stp_policy_node *
stp_policy_node_lookup(struct stm_device *stm, char *s);
void stp_policy_node_put(struct stp_policy_node *policy_node);
void stp_policy_unbind(struct stp_policy *policy);
void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
unsigned int *mstart, unsigned int *mend,
unsigned int *cstart, unsigned int *cend);
int stp_configfs_init(void);
void stp_configfs_exit(void);
struct stp_master {
unsigned int nr_free;
unsigned long chan_map[0];
};
struct stm_device {
struct device dev;
struct module *owner;
struct stp_policy *policy;
struct mutex policy_mutex;
int major;
unsigned int sw_nmasters;
struct stm_data *data;
spinlock_t link_lock;
struct list_head link_list;
/* master allocation */
spinlock_t mc_lock;
struct stp_master *masters[0];
};
#define to_stm_device(_d) \
container_of((_d), struct stm_device, dev)
struct stm_output {
unsigned int master;
unsigned int channel;
unsigned int nr_chans;
};
struct stm_file {
struct stm_device *stm;
struct stp_policy_node *policy_node;
struct stm_output output;
};
struct stm_device *stm_find_device(const char *name);
void stm_put_device(struct stm_device *stm);
struct stm_source_device {
struct device dev;
struct stm_source_data *data;
spinlock_t link_lock;
struct stm_device __rcu *link;
struct list_head link_entry;
/* one output per stm_source device */
struct stp_policy_node *policy_node;
struct stm_output output;
};
#define to_stm_source_device(_d) \
container_of((_d), struct stm_source_device, dev)
#endif /* _STM_STM_H_ */

View File

@ -409,6 +409,7 @@ static int mcb_init(void)
static void mcb_exit(void)
{
ida_destroy(&mcb_ida);
bus_unregister(&mcb_bus_type);
}

View File

@ -51,6 +51,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
priv->mapbase = pci_resource_start(pdev, 0);
if (!priv->mapbase) {
dev_err(&pdev->dev, "No PCI resource\n");
ret = -ENODEV;
goto out_disable;
}

View File

@ -61,6 +61,7 @@ static const struct of_device_id ccf_matches[] = {
},
{}
};
MODULE_DEVICE_TABLE(of, ccf_matches);
struct ccf_err_regs {
u32 errdet; /* 0x00 Error Detect Register */

View File

@ -324,6 +324,7 @@ static const struct of_device_id aemif_of_match[] = {
{ .compatible = "ti,da850-aemif", },
{},
};
MODULE_DEVICE_TABLE(of, aemif_of_match);
static int aemif_probe(struct platform_device *pdev)
{

View File

@ -1481,6 +1481,7 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */
{ 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */
{ 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */
{ 0x00000C18, 0x0000 }, /* R3096 - GP Switch 1 */
{ 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */
{ 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */
{ 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */
@ -1811,6 +1812,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_MIC_DETECT_1:
case ARIZONA_MIC_DETECT_2:
case ARIZONA_MIC_DETECT_3:
case ARIZONA_MIC_DETECT_4:
case ARIZONA_MIC_DETECT_LEVEL_1:
case ARIZONA_MIC_DETECT_LEVEL_2:
case ARIZONA_MIC_DETECT_LEVEL_3:
@ -1910,6 +1912,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_HP1_SHORT_CIRCUIT_CTRL:
case ARIZONA_HP2_SHORT_CIRCUIT_CTRL:
case ARIZONA_HP3_SHORT_CIRCUIT_CTRL:
case ARIZONA_HP_TEST_CTRL_1:
case ARIZONA_AIF1_BCLK_CTRL:
case ARIZONA_AIF1_TX_PIN_CTRL:
case ARIZONA_AIF1_RX_PIN_CTRL:
@ -2527,6 +2530,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_GPIO5_CTRL:
case ARIZONA_IRQ_CTRL_1:
case ARIZONA_GPIO_DEBOUNCE_CONFIG:
case ARIZONA_GP_SWITCH_1:
case ARIZONA_MISC_PAD_CTRL_1:
case ARIZONA_MISC_PAD_CTRL_2:
case ARIZONA_MISC_PAD_CTRL_3:
@ -2847,12 +2851,14 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS:
case ARIZONA_MIC_DETECT_3:
case ARIZONA_MIC_DETECT_4:
case ARIZONA_HP_CTRL_1L:
case ARIZONA_HP_CTRL_1R:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_INPUT_ENABLES_STATUS:
case ARIZONA_OUTPUT_STATUS_1:
case ARIZONA_RAW_OUTPUT_STATUS_1:
case ARIZONA_HP_TEST_CTRL_1:
case ARIZONA_SLIMBUS_RX_PORT_STATUS:
case ARIZONA_SLIMBUS_TX_PORT_STATUS:
case ARIZONA_INTERRUPT_STATUS_1:

View File

@ -414,7 +414,7 @@ config TI_DAC7512
config VMWARE_BALLOON
tristate "VMware Balloon Driver"
depends on X86 && HYPERVISOR_GUEST
depends on VMWARE_VMCI && X86 && HYPERVISOR_GUEST
help
This is VMware physical memory management driver which acts
like a "balloon" that can be inflated to reclaim physical pages

View File

@ -117,4 +117,3 @@ module_i2c_driver(ad_dpot_i2c_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("digital potentiometer I2C bus driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:ad_dpot");

View File

@ -514,7 +514,7 @@ int __genwqe_execute_ddcb(struct genwqe_dev *cd,
/**
* __genwqe_execute_raw_ddcb() - Execute DDCB request without addr translation
*
* This version will not do address translation or any modifcation of
* This version will not do address translation or any modification of
* the DDCB data. It is used e.g. for the MoveFlash DDCB which is
* entirely prepared by the driver itself. That means the appropriate
* DMA addresses are already in the DDCB and do not need any

View File

@ -203,7 +203,7 @@ struct genwqe_ddcb_cmd *ddcb_requ_alloc(void)
{
struct ddcb_requ *req;
req = kzalloc(sizeof(*req), GFP_ATOMIC);
req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return NULL;

View File

@ -449,7 +449,7 @@ static int genwqe_mmap(struct file *filp, struct vm_area_struct *vma)
if (get_order(vsize) > MAX_ORDER)
return -ENOMEM;
dma_map = kzalloc(sizeof(struct dma_mapping), GFP_ATOMIC);
dma_map = kzalloc(sizeof(struct dma_mapping), GFP_KERNEL);
if (dma_map == NULL)
return -ENOMEM;
@ -785,7 +785,7 @@ static int genwqe_pin_mem(struct genwqe_file *cfile, struct genwqe_mem *m)
map_addr = (m->addr & PAGE_MASK);
map_size = round_up(m->size + (m->addr & ~PAGE_MASK), PAGE_SIZE);
dma_map = kzalloc(sizeof(struct dma_mapping), GFP_ATOMIC);
dma_map = kzalloc(sizeof(struct dma_mapping), GFP_KERNEL);
if (dma_map == NULL)
return -ENOMEM;

View File

@ -220,7 +220,8 @@ void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
if (get_order(size) > MAX_ORDER)
return NULL;
return pci_alloc_consistent(cd->pci_dev, size, dma_handle);
return dma_alloc_coherent(&cd->pci_dev->dev, size, dma_handle,
GFP_KERNEL);
}
void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,
@ -229,7 +230,7 @@ void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,
if (vaddr == NULL)
return;
pci_free_consistent(cd->pci_dev, size, vaddr, dma_handle);
dma_free_coherent(&cd->pci_dev->dev, size, vaddr, dma_handle);
}
static void genwqe_unmap_pages(struct genwqe_dev *cd, dma_addr_t *dma_list,

View File

@ -2,7 +2,7 @@
* Driver for the HP iLO management processor.
*
* Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
* David Altobelli <david.altobelli@hp.com>
* David Altobelli <david.altobelli@hpe.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
@ -902,11 +902,11 @@ static void __exit ilo_exit(void)
MODULE_VERSION("1.4.1");
MODULE_ALIAS(ILO_NAME);
MODULE_DESCRIPTION(ILO_NAME);
MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>");
MODULE_AUTHOR("David Altobelli <david.altobelli@hpe.com>");
MODULE_LICENSE("GPL v2");
module_param(max_ccb, uint, 0444);
MODULE_PARM_DESC(max_ccb, "Maximum number of HP iLO channels to attach (16)");
MODULE_PARM_DESC(max_ccb, "Maximum number of HP iLO channels to attach (8-24)(default=16)");
module_init(ilo_init);
module_exit(ilo_exit);

View File

@ -1112,6 +1112,7 @@ static int __init init_kgdbts(void)
return configure_kgdbts();
}
device_initcall(init_kgdbts);
static int kgdbts_get_char(void)
{
@ -1180,10 +1181,9 @@ static struct kgdb_io kgdbts_io_ops = {
.post_exception = kgdbts_post_exp_handler,
};
module_init(init_kgdbts);
/*
* not really modular, but the easiest way to keep compat with existing
* bootargs behaviour is to continue using module_param here.
*/
module_param_call(kgdbts, param_set_kgdbts_var, param_get_string, &kps, 0644);
MODULE_PARM_DESC(kgdbts, "<A|V1|V2>[F#|S#][N#]");
MODULE_DESCRIPTION("KGDB Test Suite");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wind River Systems, Inc.");

View File

@ -472,7 +472,7 @@ static void lkdtm_do_action(enum ctype which)
break;
}
case CT_ACCESS_USERSPACE: {
unsigned long user_addr, tmp;
unsigned long user_addr, tmp = 0;
unsigned long *ptr;
user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
@ -483,6 +483,12 @@ static void lkdtm_do_action(enum ctype which)
return;
}
if (copy_to_user((void __user *)user_addr, &tmp, sizeof(tmp))) {
pr_warn("copy_to_user failed\n");
vm_munmap(user_addr, PAGE_SIZE);
return;
}
ptr = (unsigned long *)user_addr;
pr_info("attempting bad read at %p\n", ptr);

View File

@ -458,7 +458,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
return;
}
if (dev->iamthif_canceled != 1) {
if (!dev->iamthif_canceled) {
dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
dev->iamthif_stall_timer = 0;
list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);

View File

@ -285,11 +285,11 @@ static struct mei_fixup {
};
/**
* mei_cl_dev_fixup - run fixup handlers
* mei_cldev_fixup - run fixup handlers
*
* @cldev: me client device
*/
void mei_cl_dev_fixup(struct mei_cl_device *cldev)
void mei_cl_bus_dev_fixup(struct mei_cl_device *cldev)
{
struct mei_fixup *f;
const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);

View File

@ -91,7 +91,7 @@ out:
* __mei_cl_recv - internal client receive (read)
*
* @cl: host client
* @buf: buffer to send
* @buf: buffer to receive
* @length: buffer length
*
* Return: read size in bytes of < 0 on error
@ -165,7 +165,7 @@ out:
}
/**
* mei_cl_send - me device send (write)
* mei_cldev_send - me device send (write)
*
* @cldev: me client device
* @buf: buffer to send
@ -173,7 +173,7 @@ out:
*
* Return: written size in bytes or < 0 on error
*/
ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
{
struct mei_cl *cl = cldev->cl;
@ -182,18 +182,18 @@ ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
return __mei_cl_send(cl, buf, length, 1);
}
EXPORT_SYMBOL_GPL(mei_cl_send);
EXPORT_SYMBOL_GPL(mei_cldev_send);
/**
* mei_cl_recv - client receive (read)
* mei_cldev_recv - client receive (read)
*
* @cldev: me client device
* @buf: buffer to send
* @buf: buffer to receive
* @length: buffer length
*
* Return: read size in bytes of < 0 on error
*/
ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
{
struct mei_cl *cl = cldev->cl;
@ -202,15 +202,15 @@ ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
return __mei_cl_recv(cl, buf, length);
}
EXPORT_SYMBOL_GPL(mei_cl_recv);
EXPORT_SYMBOL_GPL(mei_cldev_recv);
/**
* mei_bus_event_work - dispatch rx event for a bus device
* mei_cl_bus_event_work - dispatch rx event for a bus device
* and schedule new work
*
* @work: work
*/
static void mei_bus_event_work(struct work_struct *work)
static void mei_cl_bus_event_work(struct work_struct *work)
{
struct mei_cl_device *cldev;
@ -272,7 +272,7 @@ void mei_cl_bus_rx_event(struct mei_cl *cl)
}
/**
* mei_cl_register_event_cb - register event callback
* mei_cldev_register_event_cb - register event callback
*
* @cldev: me client devices
* @event_cb: callback function
@ -283,9 +283,9 @@ void mei_cl_bus_rx_event(struct mei_cl *cl)
* -EALREADY if an callback is already registered
* <0 on other errors
*/
int mei_cl_register_event_cb(struct mei_cl_device *cldev,
unsigned long events_mask,
mei_cl_event_cb_t event_cb, void *context)
int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
unsigned long events_mask,
mei_cldev_event_cb_t event_cb, void *context)
{
int ret;
@ -296,7 +296,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev,
cldev->events_mask = events_mask;
cldev->event_cb = event_cb;
cldev->event_context = context;
INIT_WORK(&cldev->event_work, mei_bus_event_work);
INIT_WORK(&cldev->event_work, mei_cl_bus_event_work);
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
ret = mei_cl_read_start(cldev->cl, 0, NULL);
@ -314,42 +314,81 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev,
return 0;
}
EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb);
/**
* mei_cl_get_drvdata - driver data getter
* mei_cldev_get_drvdata - driver data getter
*
* @cldev: mei client device
*
* Return: driver private data
*/
void *mei_cl_get_drvdata(const struct mei_cl_device *cldev)
void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev)
{
return dev_get_drvdata(&cldev->dev);
}
EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata);
/**
* mei_cl_set_drvdata - driver data setter
* mei_cldev_set_drvdata - driver data setter
*
* @cldev: mei client device
* @data: data to store
*/
void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data)
void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data)
{
dev_set_drvdata(&cldev->dev, data);
}
EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata);
/**
* mei_cl_enable_device - enable me client device
* mei_cldev_uuid - return uuid of the underlying me client
*
* @cldev: mei client device
*
* Return: me client uuid
*/
const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev)
{
return mei_me_cl_uuid(cldev->me_cl);
}
EXPORT_SYMBOL_GPL(mei_cldev_uuid);
/**
* mei_cldev_ver - return protocol version of the underlying me client
*
* @cldev: mei client device
*
* Return: me client protocol version
*/
u8 mei_cldev_ver(const struct mei_cl_device *cldev)
{
return mei_me_cl_ver(cldev->me_cl);
}
EXPORT_SYMBOL_GPL(mei_cldev_ver);
/**
* mei_cldev_enabled - check whether the device is enabled
*
* @cldev: mei client device
*
* Return: true if me client is initialized and connected
*/
bool mei_cldev_enabled(struct mei_cl_device *cldev)
{
return cldev->cl && mei_cl_is_connected(cldev->cl);
}
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
* mei_cldev_enable_device - enable me client device
* create connection with me client
*
* @cldev: me client device
*
* Return: 0 on success and < 0 on error
*/
int mei_cl_enable_device(struct mei_cl_device *cldev)
int mei_cldev_enable(struct mei_cl_device *cldev)
{
struct mei_device *bus = cldev->bus;
struct mei_cl *cl;
@ -389,17 +428,17 @@ out:
return ret;
}
EXPORT_SYMBOL_GPL(mei_cl_enable_device);
EXPORT_SYMBOL_GPL(mei_cldev_enable);
/**
* mei_cl_disable_device - disable me client device
* mei_cldev_disable - disable me client device
* disconnect form the me client
*
* @cldev: me client device
*
* Return: 0 on success and < 0 on error
*/
int mei_cl_disable_device(struct mei_cl_device *cldev)
int mei_cldev_disable(struct mei_cl_device *cldev)
{
struct mei_device *bus;
struct mei_cl *cl;
@ -437,7 +476,7 @@ out:
mutex_unlock(&bus->device_lock);
return err;
}
EXPORT_SYMBOL_GPL(mei_cl_disable_device);
EXPORT_SYMBOL_GPL(mei_cldev_disable);
/**
* mei_cl_device_find - find matching entry in the driver id table
@ -453,17 +492,26 @@ struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev,
{
const struct mei_cl_device_id *id;
const uuid_le *uuid;
u8 version;
bool match;
uuid = mei_me_cl_uuid(cldev->me_cl);
version = mei_me_cl_ver(cldev->me_cl);
id = cldrv->id_table;
while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) {
if (!uuid_le_cmp(*uuid, id->uuid)) {
match = true;
if (!cldev->name[0])
return id;
if (cldev->name[0])
if (strncmp(cldev->name, id->name,
sizeof(id->name)))
match = false;
if (!strncmp(cldev->name, id->name, sizeof(id->name)))
if (id->version != MEI_CL_VERSION_ANY)
if (id->version != version)
match = false;
if (match)
return id;
}
@ -590,6 +638,19 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *a,
}
static DEVICE_ATTR_RO(uuid);
static ssize_t version_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
u8 version = mei_me_cl_ver(cldev->me_cl);
size_t len;
len = snprintf(buf, PAGE_SIZE, "%02X", version);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
}
static DEVICE_ATTR_RO(version);
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
char *buf)
{
@ -597,20 +658,19 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
size_t len;
len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":",
cldev->name, MEI_CL_UUID_ARGS(uuid->b));
len = snprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
}
static DEVICE_ATTR_RO(modalias);
static struct attribute *mei_cl_dev_attrs[] = {
static struct attribute *mei_cldev_attrs[] = {
&dev_attr_name.attr,
&dev_attr_uuid.attr,
&dev_attr_version.attr,
&dev_attr_modalias.attr,
NULL,
};
ATTRIBUTE_GROUPS(mei_cl_dev);
ATTRIBUTE_GROUPS(mei_cldev);
/**
* mei_cl_device_uevent - me client bus uevent handler
@ -624,6 +684,10 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
u8 version = mei_me_cl_ver(cldev->me_cl);
if (add_uevent_var(env, "MEI_CL_VERSION=%d", version))
return -ENOMEM;
if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid))
return -ENOMEM;
@ -631,8 +695,8 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env)
if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name))
return -ENOMEM;
if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":",
cldev->name, MEI_CL_UUID_ARGS(uuid->b)))
if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:",
cldev->name, uuid, version))
return -ENOMEM;
return 0;
@ -640,7 +704,7 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env)
static struct bus_type mei_cl_bus_type = {
.name = "mei",
.dev_groups = mei_cl_dev_groups,
.dev_groups = mei_cldev_groups,
.match = mei_cl_device_match,
.probe = mei_cl_device_probe,
.remove = mei_cl_device_remove,
@ -661,7 +725,7 @@ static void mei_dev_bus_put(struct mei_device *bus)
put_device(bus->dev);
}
static void mei_cl_dev_release(struct device *dev)
static void mei_cl_bus_dev_release(struct device *dev)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
@ -674,19 +738,32 @@ static void mei_cl_dev_release(struct device *dev)
}
static struct device_type mei_cl_device_type = {
.release = mei_cl_dev_release,
.release = mei_cl_bus_dev_release,
};
/**
* mei_cl_dev_alloc - initialize and allocate mei client device
* mei_cl_bus_set_name - set device name for me client device
*
* @cldev: me client device
*/
static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev)
{
dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X",
cldev->name,
mei_me_cl_uuid(cldev->me_cl),
mei_me_cl_ver(cldev->me_cl));
}
/**
* mei_cl_bus_dev_alloc - initialize and allocate mei client device
*
* @bus: mei device
* @me_cl: me client
*
* Return: allocated device structur or NULL on allocation failure
*/
static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus,
struct mei_me_client *me_cl)
static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus,
struct mei_me_client *me_cl)
{
struct mei_cl_device *cldev;
@ -700,6 +777,7 @@ static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus,
cldev->dev.type = &mei_cl_device_type;
cldev->bus = mei_dev_bus_get(bus);
cldev->me_cl = mei_me_cl_get(me_cl);
mei_cl_bus_set_name(cldev);
cldev->is_added = 0;
INIT_LIST_HEAD(&cldev->bus_list);
@ -715,15 +793,15 @@ static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus,
*
* Return: true if the device is eligible for enumeration
*/
static bool mei_cl_dev_setup(struct mei_device *bus,
struct mei_cl_device *cldev)
static bool mei_cl_bus_dev_setup(struct mei_device *bus,
struct mei_cl_device *cldev)
{
cldev->do_match = 1;
mei_cl_dev_fixup(cldev);
mei_cl_bus_dev_fixup(cldev);
/* the device name can change during fix up */
if (cldev->do_match)
dev_set_name(&cldev->dev, "mei:%s:%pUl",
cldev->name, mei_me_cl_uuid(cldev->me_cl));
mei_cl_bus_set_name(cldev);
return cldev->do_match == 1;
}
@ -739,7 +817,9 @@ static int mei_cl_bus_dev_add(struct mei_cl_device *cldev)
{
int ret;
dev_dbg(cldev->bus->dev, "adding %pUL\n", mei_me_cl_uuid(cldev->me_cl));
dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n",
mei_me_cl_uuid(cldev->me_cl),
mei_me_cl_ver(cldev->me_cl));
ret = device_add(&cldev->dev);
if (!ret)
cldev->is_added = 1;
@ -762,17 +842,20 @@ static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev)
* mei_cl_bus_dev_destroy - destroy me client devices object
*
* @cldev: me client device
*
* Locking: called under "dev->cl_bus_lock" lock
*/
static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev)
{
WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock));
if (!cldev->is_added)
return;
device_del(&cldev->dev);
mutex_lock(&cldev->bus->cl_bus_lock);
list_del_init(&cldev->bus_list);
mutex_unlock(&cldev->bus->cl_bus_lock);
cldev->is_added = 0;
put_device(&cldev->dev);
@ -798,35 +881,40 @@ void mei_cl_bus_remove_devices(struct mei_device *bus)
{
struct mei_cl_device *cldev, *next;
mutex_lock(&bus->cl_bus_lock);
list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list)
mei_cl_bus_remove_device(cldev);
mutex_unlock(&bus->cl_bus_lock);
}
/**
* mei_cl_dev_init - allocate and initializes an mei client devices
* mei_cl_bus_dev_init - allocate and initializes an mei client devices
* based on me client
*
* @bus: mei device
* @me_cl: me client
*
* Locking: called under "dev->cl_bus_lock" lock
*/
static void mei_cl_dev_init(struct mei_device *bus, struct mei_me_client *me_cl)
static void mei_cl_bus_dev_init(struct mei_device *bus,
struct mei_me_client *me_cl)
{
struct mei_cl_device *cldev;
WARN_ON(!mutex_is_locked(&bus->cl_bus_lock));
dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl));
if (me_cl->bus_added)
return;
cldev = mei_cl_dev_alloc(bus, me_cl);
cldev = mei_cl_bus_dev_alloc(bus, me_cl);
if (!cldev)
return;
mutex_lock(&cldev->bus->cl_bus_lock);
me_cl->bus_added = true;
list_add_tail(&cldev->bus_list, &bus->device_list);
mutex_unlock(&cldev->bus->cl_bus_lock);
}
@ -841,12 +929,13 @@ void mei_cl_bus_rescan(struct mei_device *bus)
struct mei_cl_device *cldev, *n;
struct mei_me_client *me_cl;
mutex_lock(&bus->cl_bus_lock);
down_read(&bus->me_clients_rwsem);
list_for_each_entry(me_cl, &bus->me_clients, list)
mei_cl_dev_init(bus, me_cl);
mei_cl_bus_dev_init(bus, me_cl);
up_read(&bus->me_clients_rwsem);
mutex_lock(&bus->cl_bus_lock);
list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) {
if (!mei_me_cl_is_active(cldev->me_cl)) {
@ -857,7 +946,7 @@ void mei_cl_bus_rescan(struct mei_device *bus)
if (cldev->is_added)
continue;
if (mei_cl_dev_setup(bus, cldev))
if (mei_cl_bus_dev_setup(bus, cldev))
mei_cl_bus_dev_add(cldev);
else {
list_del_init(&cldev->bus_list);
@ -869,7 +958,8 @@ void mei_cl_bus_rescan(struct mei_device *bus)
dev_dbg(bus->dev, "rescan end");
}
int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner)
int __mei_cldev_driver_register(struct mei_cl_driver *cldrv,
struct module *owner)
{
int err;
@ -885,15 +975,15 @@ int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner)
return 0;
}
EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
EXPORT_SYMBOL_GPL(__mei_cldev_driver_register);
void mei_cl_driver_unregister(struct mei_cl_driver *cldrv)
void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv)
{
driver_unregister(&cldrv->driver);
pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name);
}
EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister);
int __init mei_cl_bus_init(void)

View File

@ -68,6 +68,18 @@ static inline const uuid_le *mei_me_cl_uuid(const struct mei_me_client *me_cl)
return &me_cl->props.protocol_name;
}
/**
* mei_me_cl_ver - return me client protocol version
*
* @me_cl: me client
*
* Return: me client protocol version
*/
static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
{
return me_cl->props.protocol_version;
}
/*
* MEI IO Functions
*/

View File

@ -215,7 +215,7 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name)
f = debugfs_create_file("active", S_IRUSR, dir,
dev, &mei_dbgfs_fops_active);
if (!f) {
dev_err(dev->dev, "meclients: registration failed\n");
dev_err(dev->dev, "active: registration failed\n");
goto err;
}
f = debugfs_create_file("devstate", S_IRUSR, dir,

View File

@ -281,7 +281,7 @@ int mei_hbm_start_req(struct mei_device *dev)
return 0;
}
/*
/**
* mei_hbm_enum_clients_req - sends enumeration client request message.
*
* @dev: the device structure
@ -314,7 +314,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
return 0;
}
/*
/**
* mei_hbm_me_cl_add - add new me client to the list
*
* @dev: the device structure
@ -569,7 +569,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
return 0;
}
/*
/**
* mei_hbm_pg - sends pg command
*
* @dev: the device structure

View File

@ -150,7 +150,7 @@ static inline u32 mei_me_d0i3c_read(const struct mei_device *dev)
u32 reg;
reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C);
trace_mei_reg_read(dev->dev, "H_D0I3C", H_CSR, reg);
trace_mei_reg_read(dev->dev, "H_D0I3C", H_D0I3C, reg);
return reg;
}
@ -163,7 +163,7 @@ static inline u32 mei_me_d0i3c_read(const struct mei_device *dev)
*/
static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg)
{
trace_mei_reg_write(dev->dev, "H_D0I3C", H_CSR, reg);
trace_mei_reg_write(dev->dev, "H_D0I3C", H_D0I3C, reg);
mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg);
}

View File

@ -329,10 +329,10 @@ void mei_stop(struct mei_device *dev)
{
dev_dbg(dev->dev, "stopping the device.\n");
mei_cancel_work(dev);
mei_cl_bus_remove_devices(dev);
mei_cancel_work(dev);
mutex_lock(&dev->device_lock);
mei_wd_stop(dev);

View File

@ -21,6 +21,7 @@
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h>
@ -147,6 +148,9 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
cb->read_time = jiffies;
cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx);
list_move_tail(&cb->list, &complete_list->list);
} else {
pm_runtime_mark_last_busy(dev->dev);
pm_request_autosuspend(dev->dev);
}
out:

View File

@ -275,32 +275,33 @@ struct mei_cl {
struct mei_cl_device *cldev;
};
/** struct mei_hw_ops
/**
* struct mei_hw_ops - hw specific ops
*
* @host_is_ready : query for host readiness
*
* @hw_is_ready : query if hw is ready
* @hw_reset : reset hw
* @hw_start : start hw after reset
* @hw_config : configure hw
*
* @fw_status : get fw status registers
* @pg_state : power gating state of the device
* @pg_in_transition : is device now in pg transition
* @pg_is_enabled : is power gating enabled
*
* @intr_clear : clear pending interrupts
* @intr_enable : enable interrupts
* @intr_disable : disable interrupts
*
* @hbuf_free_slots : query for write buffer empty slots
* @hbuf_is_ready : query if write buffer is empty
* @hbuf_max_len : query for write buffer max len
*
* @write : write a message to FW
*
* @rdbuf_full_slots : query how many slots are filled
*
* @read_hdr : get first 4 bytes (header)
* @read : read a buffer from the FW
*/
@ -340,7 +341,7 @@ struct mei_hw_ops {
/* MEI bus API*/
void mei_cl_bus_rescan(struct mei_device *bus);
void mei_cl_dev_fixup(struct mei_cl_device *dev);
void mei_cl_bus_dev_fixup(struct mei_cl_device *dev);
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
bool blocking);
ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);

Some files were not shown because too many files have changed in this diff Show More