提交 8e483ed1 编写于 作者: L Linus Torvalds

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'
  ...
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.
......@@ -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.
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.
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.
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.
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).
......@@ -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
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
......@@ -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.
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.
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.
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>;
};
......@@ -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;
};
};
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>;
};
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";
};
= 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";
};
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>;
};
* 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";
};
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.
......@@ -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/>
......
......@@ -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 |
......
......@@ -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
......
......@@ -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 @@ init_vr(struct mic_info *mic, int fd, int type,
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 (ret == mic->pid)
reset(mic);
if (!i)
mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n",
mic->name, __func__, __LINE__, shutdown_status);
reset(mic);
}
static void *
mic_config(void *arg)
static int open_state_fd(struct mic_info *mic)
{
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;
snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
MICSYSFSDIR, mic->name, "state");
fd = open(pathname, O_RDONLY);
if (fd < 0) {
if (fd < 0)
mpsslog("%s: opening file %s failed %s\n",
mic->name, pathname, strerror(errno));
goto error;
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;
}
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;
int fd, ret, stat = 0;
fd = open_state_fd(mic);
if (fd < 0) {
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);
......
......@@ -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;
......
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
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
......@@ -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.
......@@ -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
......
......@@ -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 {
......
......@@ -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
......@@ -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/
......@@ -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");
*/
......@@ -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");
*/
......@@ -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);
}
}
......
......@@ -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);
......@@ -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;
......
/*
* 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 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
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 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
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 {
......
......@@ -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",
},
......
/*
* 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)) {
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);
/* Initialize the gpio */
ret = gpio_extcon_init(&pdev->dev, data);
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);
/* 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;
}
ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
ret = devm_extcon_dev_register(&pdev->dev, data->edev);
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;
INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
pdata->irq_flags, pdev->name,
extcon_data);
/*
* 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;
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;
}
......
......@@ -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:
......
......@@ -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:
......
......@@ -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;
......
......@@ -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:
......
......@@ -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)
......
......@@ -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)
......
......@@ -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",
/* Charger external connector */
[EXTCON_TA] = "TA",
[EXTCON_FAST_CHARGER] = "FAST-CHARGER",
[EXTCON_SLOW_CHARGER] = "SLOW-CHARGER",
[EXTCON_CHARGE_DOWNSTREAM] = "CHARGE-DOWNSTREAM",
/* Audio/Video external connector */
[EXTCON_LINE_IN] = "LINE-IN",
[EXTCON_LINE_OUT] = "LINE-OUT",
[EXTCON_MICROPHONE] = "MICROPHONE",
[EXTCON_HEADPHONE] = "HEADPHONE",
[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",
/* Etc external connector */
[EXTCON_DOCK] = "DOCK",
[EXTCON_JIG] = "JIG",
[EXTCON_MECHANICAL] = "MECHANICAL",
[EXTCON_USB] = "USB",
[EXTCON_USB_HOST] = "USB-HOST",
/* 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",
/* 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",
/* Display external connector */
[EXTCON_DISP_HDMI] = "HDMI",
[EXTCON_DISP_MHL] = "MHL",
[EXTCON_DISP_DVI] = "DVI",
[EXTCON_DISP_VGA] = "VGA",
/* Miscellaneous external connector */
[EXTCON_DOCK] = "DOCK",
[EXTCON_JIG] = "JIG",
[EXTCON_MECHANICAL] = "MECHANICAL",
NULL,
};
......
#
# 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
#
# 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
/*
* 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);
/*
* 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");
/*
* 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");
......@@ -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);
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);
val = drvdata->cpu;
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
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)
......
......@@ -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;
......
......@@ -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)) {
......
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
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
此差异已折叠。
/*
* 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;
}
/*
* 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__ */
此差异已折叠。
/*
* 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__ */
此差异已折叠。
此差异已折叠。
/*
* 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__ */
此差异已折叠。
此差异已折叠。
/*
* 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__ */
此差异已折叠。
此差异已折叠。
此差异已折叠。
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
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -409,6 +409,7 @@ static int mcb_init(void)
static void mcb_exit(void)
{
ida_destroy(&mcb_ida);
bus_unregister(&mcb_bus_type);
}
......
......@@ -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;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册