提交 b78b499a 编写于 作者: L Linus Torvalds

Merge tag 'char-misc-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here's the big char/misc driver patches for 4.10-rc1. Lots of tiny
  changes over lots of "minor" driver subsystems, the largest being some
  new FPGA drivers. Other than that, a few other new drivers, but no new
  driver subsystems added for this kernel cycle, a nice change.

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

* tag 'char-misc-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (107 commits)
  uio-hv-generic: store physical addresses instead of virtual
  Tools: hv: kvp: configurable external scripts path
  uio-hv-generic: new userspace i/o driver for VMBus
  vmbus: add support for dynamic device id's
  hv: change clockevents unbind tactics
  hv: acquire vmbus_connection.channel_mutex in vmbus_free_channels()
  hyperv: Fix spelling of HV_UNKOWN
  mei: bus: enable non-blocking RX
  mei: fix the back to back interrupt handling
  mei: synchronize irq before initiating a reset.
  VME: Remove shutdown entry from vme_driver
  auxdisplay: ht16k33: select framebuffer helper modules
  MAINTAINERS: add git url for fpga
  fpga: Clarify how write_init works streaming modes
  fpga zynq: Fix incorrect ISR state on bootup
  fpga zynq: Remove priv->dev
  fpga zynq: Add missing \n to messages
  fpga: Add COMPILE_TEST to all drivers
  uio: pruss: add clk_disable()
  char/pcmcia: add some error checking in scr24x_read()
  ...
What: /sys/class/fpga_bridge/<bridge>/name
Date: January 2016
KernelVersion: 4.5
Contact: Alan Tull <atull@opensource.altera.com>
Description: Name of low level FPGA bridge driver.
What: /sys/class/fpga_bridge/<bridge>/state
Date: January 2016
KernelVersion: 4.5
Contact: Alan Tull <atull@opensource.altera.com>
Description: Show bridge state as "enabled" or "disabled"
...@@ -29,3 +29,19 @@ Description: Display fw status registers content ...@@ -29,3 +29,19 @@ Description: Display fw status registers content
Also number of registers varies between 1 and 6 Also number of registers varies between 1 and 6
depending on generation. depending on generation.
What: /sys/class/mei/meiN/hbm_ver
Date: Aug 2016
KernelVersion: 4.9
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Display the negotiated HBM protocol version.
The HBM protocol version negotiated
between the driver and the device.
What: /sys/class/mei/meiN/hbm_ver_drv
Date: Aug 2016
KernelVersion: 4.9
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Display the driver HBM protocol version.
The HBM protocol version supported by the driver.
Holtek ht16k33 RAM mapping 16*8 LED controller driver with keyscan
-------------------------------------------------------------------------------
Required properties:
- compatible: "holtek,ht16k33"
- reg: I2C slave address of the chip.
- interrupt-parent: A phandle pointing to the interrupt controller
serving the interrupt for this chip.
- interrupts: Interrupt specification for the key pressed interrupt.
- refresh-rate-hz: Display update interval in HZ.
- debounce-delay-ms: Debouncing interval time in milliseconds.
- linux,keymap: The keymap for keys as described in the binding
document (devicetree/bindings/input/matrix-keymap.txt).
Optional properties:
- linux,no-autorepeat: Disable keyrepeat.
- default-brightness-level: Initial brightness level [0-15] (default: 15).
Example:
&i2c1 {
ht16k33: ht16k33@70 {
compatible = "holtek,ht16k33";
reg = <0x70>;
refresh-rate-hz = <20>;
debounce-delay-ms = <50>;
interrupt-parent = <&gpio4>;
interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>;
linux,keymap = <
MATRIX_KEY(2, 0, KEY_F6)
MATRIX_KEY(3, 0, KEY_F8)
MATRIX_KEY(4, 0, KEY_F10)
MATRIX_KEY(5, 0, KEY_F4)
MATRIX_KEY(6, 0, KEY_F2)
MATRIX_KEY(2, 1, KEY_F5)
MATRIX_KEY(3, 1, KEY_F7)
MATRIX_KEY(4, 1, KEY_F9)
MATRIX_KEY(5, 1, KEY_F3)
MATRIX_KEY(6, 1, KEY_F1)
>;
};
};
FPGA Region Device Tree Binding
Alan Tull 2016
CONTENTS
- Introduction
- Terminology
- Sequence
- FPGA Region
- Supported Use Models
- Device Tree Examples
- Constraints
Introduction
============
FPGA Regions represent FPGA's and partial reconfiguration regions of FPGA's in
the Device Tree. FPGA Regions provide a way to program FPGAs under device tree
control.
This device tree binding document hits some of the high points of FPGA usage and
attempts to include terminology used by both major FPGA manufacturers. This
document isn't a replacement for any manufacturers specifications for FPGA
usage.
Terminology
===========
Full Reconfiguration
* The entire FPGA is programmed.
Partial Reconfiguration (PR)
* A section of an FPGA is reprogrammed while the rest of the FPGA is not
affected.
* Not all FPGA's support PR.
Partial Reconfiguration Region (PRR)
* Also called a "reconfigurable partition"
* A PRR is a specific section of a FPGA reserved for reconfiguration.
* A base (or static) FPGA image may create a set of PRR's that later may
be independently reprogrammed many times.
* The size and specific location of each PRR is fixed.
* The connections at the edge of each PRR are fixed. The image that is loaded
into a PRR must fit and must use a subset of the region's connections.
* The busses within the FPGA are split such that each region gets its own
branch that may be gated independently.
Persona
* Also called a "partial bit stream"
* An FPGA image that is designed to be loaded into a PRR. There may be
any number of personas designed to fit into a PRR, but only one at at time
may be loaded.
* A persona may create more regions.
FPGA Bridge
* FPGA Bridges gate bus signals between a host and FPGA.
* FPGA Bridges should be disabled while the FPGA is being programmed to
prevent spurious signals on the cpu bus and to the soft logic.
* FPGA bridges may be actual hardware or soft logic on an FPGA.
* During Full Reconfiguration, hardware bridges between the host and FPGA
will be disabled.
* During Partial Reconfiguration of a specific region, that region's bridge
will be used to gate the busses. Traffic to other regions is not affected.
* In some implementations, the FPGA Manager transparantly handles gating the
buses, eliminating the need to show the hardware FPGA bridges in the
device tree.
* An FPGA image may create a set of reprogrammable regions, each having its
own bridge and its own split of the busses in the FPGA.
FPGA Manager
* An FPGA Manager is a hardware block that programs an FPGA under the control
of a host processor.
Base Image
* Also called the "static image"
* An FPGA image that is designed to do full reconfiguration of the FPGA.
* A base image may set up a set of partial reconfiguration regions that may
later be reprogrammed.
---------------- ----------------------------------
| Host CPU | | FPGA |
| | | |
| ----| | ----------- -------- |
| | H | | |==>| Bridge0 |<==>| PRR0 | |
| | W | | | ----------- -------- |
| | | | | |
| | B |<=====>|<==| ----------- -------- |
| | R | | |==>| Bridge1 |<==>| PRR1 | |
| | I | | | ----------- -------- |
| | D | | | |
| | G | | | ----------- -------- |
| | E | | |==>| Bridge2 |<==>| PRR2 | |
| ----| | ----------- -------- |
| | | |
---------------- ----------------------------------
Figure 1: An FPGA set up with a base image that created three regions. Each
region (PRR0-2) gets its own split of the busses that is independently gated by
a soft logic bridge (Bridge0-2) in the FPGA. The contents of each PRR can be
reprogrammed independently while the rest of the system continues to function.
Sequence
========
When a DT overlay that targets a FPGA Region is applied, the FPGA Region will
do the following:
1. Disable appropriate FPGA bridges.
2. Program the FPGA using the FPGA manager.
3. Enable the FPGA bridges.
4. The Device Tree overlay is accepted into the live tree.
5. Child devices are populated.
When the overlay is removed, the child nodes will be removed and the FPGA Region
will disable the bridges.
FPGA Region
===========
FPGA Regions represent FPGA's and FPGA PR regions in the device tree. An FPGA
Region brings together the elements needed to program on a running system and
add the child devices:
* FPGA Manager
* FPGA Bridges
* image-specific information needed to to the programming.
* child nodes
The intended use is that a Device Tree overlay (DTO) can be used to reprogram an
FPGA while an operating system is running.
An FPGA Region that exists in the live Device Tree reflects the current state.
If the live tree shows a "firmware-name" property or child nodes under a FPGA
Region, the FPGA already has been programmed. A DTO that targets a FPGA Region
and adds the "firmware-name" property is taken as a request to reprogram the
FPGA. After reprogramming is successful, the overlay is accepted into the live
tree.
The base FPGA Region in the device tree represents the FPGA and supports full
reconfiguration. It must include a phandle to an FPGA Manager. The base
FPGA region will be the child of one of the hardware bridges (the bridge that
allows register access) between the cpu and the FPGA. If there are more than
one bridge to control during FPGA programming, the region will also contain a
list of phandles to the additional hardware FPGA Bridges.
For partial reconfiguration (PR), each PR region will have an FPGA Region.
These FPGA regions are children of FPGA bridges which are then children of the
base FPGA region. The "Full Reconfiguration to add PRR's" example below shows
this.
If an FPGA Region does not specify a FPGA Manager, it will inherit the FPGA
Manager specified by its ancestor FPGA Region. This supports both the case
where the same FPGA Manager is used for all of a FPGA as well the case where
a different FPGA Manager is used for each region.
FPGA Regions do not inherit their ancestor FPGA regions' bridges. This prevents
shutting down bridges that are upstream from the other active regions while one
region is getting reconfigured (see Figure 1 above). During PR, the FPGA's
hardware bridges remain enabled. The PR regions' bridges will be FPGA bridges
within the static image of the FPGA.
Required properties:
- compatible : should contain "fpga-region"
- fpga-mgr : should contain a phandle to an FPGA Manager. Child FPGA Regions
inherit this property from their ancestor regions. A fpga-mgr property
in a region will override any inherited FPGA manager.
- #address-cells, #size-cells, ranges : must be present to handle address space
mapping for child nodes.
Optional properties:
- firmware-name : should contain the name of an FPGA image file located on the
firmware search path. If this property shows up in a live device tree
it indicates that the FPGA has already been programmed with this image.
If this property is in an overlay targeting a FPGA region, it is a
request to program the FPGA with that image.
- fpga-bridges : should contain a list of phandles to FPGA Bridges that must be
controlled during FPGA programming along with the parent FPGA bridge.
This property is optional if the FPGA Manager handles the bridges.
If the fpga-region is the child of a fpga-bridge, the list should not
contain the parent bridge.
- partial-fpga-config : boolean, set if partial reconfiguration is to be done,
otherwise full reconfiguration is done.
- external-fpga-config : boolean, set if the FPGA has already been configured
prior to OS boot up.
- region-unfreeze-timeout-us : The maximum time in microseconds to wait for
bridges to successfully become enabled after the region has been
programmed.
- region-freeze-timeout-us : The maximum time in microseconds to wait for
bridges to successfully become disabled before the region has been
programmed.
- child nodes : devices in the FPGA after programming.
In the example below, when an overlay is applied targeting fpga-region0,
fpga_mgr is used to program the FPGA. Two bridges are controlled during
programming: the parent fpga_bridge0 and fpga_bridge1. Because the region is
the child of fpga_bridge0, only fpga_bridge1 needs to be specified in the
fpga-bridges property. During programming, these bridges are disabled, the
firmware specified in the overlay is loaded to the FPGA using the FPGA manager
specified in the region. If FPGA programming succeeds, the bridges are
reenabled and the overlay makes it into the live device tree. The child devices
are then populated. If FPGA programming fails, the bridges are left disabled
and the overlay is rejected. The overlay's ranges property maps the lwhps
bridge's region (0xff200000) and the hps bridge's region (0xc0000000) for use by
the two child devices.
Example:
Base tree contains:
fpga_mgr: fpga-mgr@ff706000 {
compatible = "altr,socfpga-fpga-mgr";
reg = <0xff706000 0x1000
0xffb90000 0x20>;
interrupts = <0 175 4>;
};
fpga_bridge0: fpga-bridge@ff400000 {
compatible = "altr,socfpga-lwhps2fpga-bridge";
reg = <0xff400000 0x100000>;
resets = <&rst LWHPS2FPGA_RESET>;
clocks = <&l4_main_clk>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
fpga_region0: fpga-region0 {
compatible = "fpga-region";
fpga-mgr = <&fpga_mgr>;
};
};
fpga_bridge1: fpga-bridge@ff500000 {
compatible = "altr,socfpga-hps2fpga-bridge";
reg = <0xff500000 0x10000>;
resets = <&rst HPS2FPGA_RESET>;
clocks = <&l4_main_clk>;
};
Overlay contains:
/dts-v1/ /plugin/;
/ {
fragment@0 {
target = <&fpga_region0>;
#address-cells = <1>;
#size-cells = <1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <1>;
firmware-name = "soc_system.rbf";
fpga-bridges = <&fpga_bridge1>;
ranges = <0x20000 0xff200000 0x100000>,
<0x0 0xc0000000 0x20000000>;
gpio@10040 {
compatible = "altr,pio-1.0";
reg = <0x10040 0x20>;
altr,gpio-bank-width = <4>;
#gpio-cells = <2>;
clocks = <2>;
gpio-controller;
};
onchip-memory {
device_type = "memory";
compatible = "altr,onchipmem-15.1";
reg = <0x0 0x10000>;
};
};
};
};
Supported Use Models
====================
In all cases the live DT must have the FPGA Manager, FPGA Bridges (if any), and
a FPGA Region. The target of the Device Tree Overlay is the FPGA Region. Some
uses are specific to a FPGA device.
* No FPGA Bridges
In this case, the FPGA Manager which programs the FPGA also handles the
bridges behind the scenes. No FPGA Bridge devices are needed for full
reconfiguration.
* Full reconfiguration with hardware bridges
In this case, there are hardware bridges between the processor and FPGA that
need to be controlled during full reconfiguration. Before the overlay is
applied, the live DT must include the FPGA Manager, FPGA Bridges, and a
FPGA Region. The FPGA Region is the child of the bridge that allows
register access to the FPGA. Additional bridges may be listed in a
fpga-bridges property in the FPGA region or in the device tree overlay.
* Partial reconfiguration with bridges in the FPGA
In this case, the FPGA will have one or more PRR's that may be programmed
separately while the rest of the FPGA can remain active. To manage this,
bridges need to exist in the FPGA that can gate the buses going to each FPGA
region while the buses are enabled for other sections. Before any partial
reconfiguration can be done, a base FPGA image must be loaded which includes
PRR's with FPGA bridges. The device tree should have a FPGA region for each
PRR.
Device Tree Examples
====================
The intention of this section is to give some simple examples, focusing on
the placement of the elements detailed above, especially:
* FPGA Manager
* FPGA Bridges
* FPGA Region
* ranges
* target-path or target
For the purposes of this section, I'm dividing the Device Tree into two parts,
each with its own requirements. The two parts are:
* The live DT prior to the overlay being added
* The DT overlay
The live Device Tree must contain an FPGA Region, an FPGA Manager, and any FPGA
Bridges. The FPGA Region's "fpga-mgr" property specifies the manager by phandle
to handle programming the FPGA. If the FPGA Region is the child of another FPGA
Region, the parent's FPGA Manager is used. If FPGA Bridges need to be involved,
they are specified in the FPGA Region by the "fpga-bridges" property. During
FPGA programming, the FPGA Region will disable the bridges that are in its
"fpga-bridges" list and will re-enable them after FPGA programming has
succeeded.
The Device Tree Overlay will contain:
* "target-path" or "target"
The insertion point where the the contents of the overlay will go into the
live tree. target-path is a full path, while target is a phandle.
* "ranges"
The address space mapping from processor to FPGA bus(ses).
* "firmware-name"
Specifies the name of the FPGA image file on the firmware search
path. The search path is described in the firmware class documentation.
* "partial-fpga-config"
This binding is a boolean and should be present if partial reconfiguration
is to be done.
* child nodes corresponding to hardware that will be loaded in this region of
the FPGA.
Device Tree Example: Full Reconfiguration without Bridges
=========================================================
Live Device Tree contains:
fpga_mgr0: fpga-mgr@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>;
};
fpga_region0: fpga-region0 {
compatible = "fpga-region";
fpga-mgr = <&fpga_mgr0>;
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
};
DT Overlay contains:
/dts-v1/ /plugin/;
/ {
fragment@0 {
target = <&fpga_region0>;
#address-cells = <1>;
#size-cells = <1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <1>;
firmware-name = "zynq-gpio.bin";
gpio1: gpio@40000000 {
compatible = "xlnx,xps-gpio-1.00.a";
reg = <0x40000000 0x10000>;
gpio-controller;
#gpio-cells = <0x2>;
xlnx,gpio-width= <0x6>;
};
};
};
Device Tree Example: Full Reconfiguration to add PRR's
======================================================
The base FPGA Region is specified similar to the first example above.
This example programs the FPGA to have two regions that can later be partially
configured. Each region has its own bridge in the FPGA fabric.
DT Overlay contains:
/dts-v1/ /plugin/;
/ {
fragment@0 {
target = <&fpga_region0>;
#address-cells = <1>;
#size-cells = <1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <1>;
firmware-name = "base.rbf";
fpga-bridge@4400 {
compatible = "altr,freeze-bridge";
reg = <0x4400 0x10>;
fpga_region1: fpga-region1 {
compatible = "fpga-region";
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
};
};
fpga-bridge@4420 {
compatible = "altr,freeze-bridge";
reg = <0x4420 0x10>;
fpga_region2: fpga-region2 {
compatible = "fpga-region";
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
};
};
};
};
};
Device Tree Example: Partial Reconfiguration
============================================
This example reprograms one of the PRR's set up in the previous example.
The sequence that occurs when this overlay is similar to the above, the only
differences are that the FPGA is partially reconfigured due to the
"partial-fpga-config" boolean and the only bridge that is controlled during
programming is the FPGA based bridge of fpga_region1.
/dts-v1/ /plugin/;
/ {
fragment@0 {
target = <&fpga_region1>;
#address-cells = <1>;
#size-cells = <1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <1>;
firmware-name = "soc_image2.rbf";
partial-fpga-config;
gpio@10040 {
compatible = "altr,pio-1.0";
reg = <0x10040 0x20>;
clocks = <0x2>;
altr,gpio-bank-width = <0x4>;
resetvalue = <0x0>;
#gpio-cells = <0x2>;
gpio-controller;
};
};
};
};
Constraints
===========
It is beyond the scope of this document to fully describe all the FPGA design
constraints required to make partial reconfiguration work[1] [2] [3], but a few
deserve quick mention.
A persona must have boundary connections that line up with those of the partion
or region it is designed to go into.
During programming, transactions through those connections must be stopped and
the connections must be held at a fixed logic level. This can be achieved by
FPGA Bridges that exist on the FPGA fabric prior to the partial reconfiguration.
--
[1] www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/ug/ug_partrecon.pdf
[2] tspace.library.utoronto.ca/bitstream/1807/67932/1/Byma_Stuart_A_201411_MAS_thesis.pdf
[3] http://www.xilinx.com/support/documentation/sw_manuals/xilinx14_1/ug702.pdf
Broadcom OTP memory controller
Required Properties:
- compatible: "brcm,ocotp" for the first generation Broadcom OTPC which is used
in Cygnus and supports 32 bit read/write. Use "brcm,ocotp-v2" for the second
generation Broadcom OTPC which is used in SoC's such as Stingray and supports
64-bit read/write.
- reg: Base address of the OTP controller.
- brcm,ocotp-size: Amount of memory available, in 32 bit words
Example:
otp: otp@0301c800 {
compatible = "brcm,ocotp";
reg = <0x0301c800 0x2c>;
brcm,ocotp-size = <2048>;
};
* NXP LPC18xx OTP memory
Internal OTP (One Time Programmable) memory for NXP LPC18xx/43xx devices.
Required properties:
- compatible: Should be "nxp,lpc1850-otp"
- reg: Must contain an entry with the physical base address and length
for each entry in reg-names.
- address-cells: must be set to 1.
- size-cells: must be set to 1.
See nvmem.txt for more information.
Example:
otp: otp@40045000 {
compatible = "nxp,lpc1850-otp";
reg = <0x40045000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
};
...@@ -127,6 +127,7 @@ hitex Hitex Development Tools ...@@ -127,6 +127,7 @@ hitex Hitex Development Tools
holt Holt Integrated Circuits, Inc. holt Holt Integrated Circuits, Inc.
honeywell Honeywell honeywell Honeywell
hp Hewlett Packard hp Hewlett Packard
holtek Holtek Semiconductor, Inc.
i2se I2SE GmbH i2se I2SE GmbH
ibm International Business Machines (IBM) ibm International Business Machines (IBM)
idt Integrated Device Technologies, Inc. idt Integrated Device Technologies, Inc.
......
...@@ -18,31 +18,37 @@ API Functions: ...@@ -18,31 +18,37 @@ API Functions:
To program the FPGA from a file or from a buffer: To program the FPGA from a file or from a buffer:
------------------------------------------------- -------------------------------------------------
int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, int fpga_mgr_buf_load(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count); const char *buf, size_t count);
Load the FPGA from an image which exists as a buffer in memory. Load the FPGA from an image which exists as a buffer in memory.
int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags, int fpga_mgr_firmware_load(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *image_name); const char *image_name);
Load the FPGA from an image which exists as a file. The image file must be on 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). the firmware search path (see the firmware class documentation). If successful,
the FPGA ends up in operating mode. Return 0 on success or a negative error
For both these functions, flags == 0 for normal full reconfiguration or code.
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.
A FPGA design contained in a FPGA image file will likely have particulars that
affect how the image is programmed to the FPGA. These are contained in struct
fpga_image_info. Currently the only such particular is a single flag bit
indicating whether the image is for full or partial reconfiguration.
To get/put a reference to a FPGA manager: To get/put a reference to a FPGA manager:
----------------------------------------- -----------------------------------------
struct fpga_manager *of_fpga_mgr_get(struct device_node *node); struct fpga_manager *of_fpga_mgr_get(struct device_node *node);
struct fpga_manager *fpga_mgr_get(struct device *dev);
Given a DT node or device, get an exclusive reference to a FPGA manager.
void fpga_mgr_put(struct fpga_manager *mgr); void fpga_mgr_put(struct fpga_manager *mgr);
Given a DT node, get an exclusive reference to a FPGA manager or release Release the reference.
the reference.
To register or unregister the low level FPGA-specific driver: To register or unregister the low level FPGA-specific driver:
...@@ -70,8 +76,11 @@ struct device_node *mgr_node = ... ...@@ -70,8 +76,11 @@ struct device_node *mgr_node = ...
char *buf = ... char *buf = ...
int count = ... int count = ...
/* struct with information about the FPGA image to program. */
struct fpga_image_info info;
/* flags indicates whether to do full or partial reconfiguration */ /* flags indicates whether to do full or partial reconfiguration */
int flags = 0; info.flags = 0;
int ret; int ret;
...@@ -79,7 +88,7 @@ int ret; ...@@ -79,7 +88,7 @@ int ret;
struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node); struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node);
/* Load the buffer to the FPGA */ /* Load the buffer to the FPGA */
ret = fpga_mgr_buf_load(mgr, flags, buf, count); ret = fpga_mgr_buf_load(mgr, &info, buf, count);
/* Release the FPGA manager */ /* Release the FPGA manager */
fpga_mgr_put(mgr); fpga_mgr_put(mgr);
...@@ -96,8 +105,11 @@ struct device_node *mgr_node = ... ...@@ -96,8 +105,11 @@ struct device_node *mgr_node = ...
/* FPGA image is in this file which is in the firmware search path */ /* FPGA image is in this file which is in the firmware search path */
const char *path = "fpga-image-9.rbf" const char *path = "fpga-image-9.rbf"
/* struct with information about the FPGA image to program. */
struct fpga_image_info info;
/* flags indicates whether to do full or partial reconfiguration */ /* flags indicates whether to do full or partial reconfiguration */
int flags = 0; info.flags = 0;
int ret; int ret;
...@@ -105,7 +117,7 @@ int ret; ...@@ -105,7 +117,7 @@ int ret;
struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node); struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node);
/* Get the firmware image (path) and load it to the FPGA */ /* Get the firmware image (path) and load it to the FPGA */
ret = fpga_mgr_firmware_load(mgr, flags, path); ret = fpga_mgr_firmware_load(mgr, &info, path);
/* Release the FPGA manager */ /* Release the FPGA manager */
fpga_mgr_put(mgr); fpga_mgr_put(mgr);
...@@ -157,7 +169,10 @@ The programming sequence is: ...@@ -157,7 +169,10 @@ The programming sequence is:
2. .write (may be called once or multiple times) 2. .write (may be called once or multiple times)
3. .write_complete 3. .write_complete
The .write_init function will prepare the FPGA to receive the image data. The .write_init function will prepare the FPGA to receive the image data. The
buffer passed into .write_init will be atmost .initial_header_size bytes long,
if the whole bitstream is not immediately available then the core code will
buffer up at least this much before starting.
The .write function writes a buffer to the FPGA. The buffer may be contain the 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 whole FPGA image or may be a smaller chunk of an FPGA image. In the latter
......
...@@ -97,3 +97,25 @@ $ echo 0 > /sys/bus/intel_th/devices/0-msc0/active ...@@ -97,3 +97,25 @@ $ echo 0 > /sys/bus/intel_th/devices/0-msc0/active
# and now you can collect the trace from the device node: # and now you can collect the trace from the device node:
$ cat /dev/intel_th0/msc0 > my_stp_trace $ cat /dev/intel_th0/msc0 > my_stp_trace
Host Debugger Mode
==================
It is possible to configure the Trace Hub and control its trace
capture from a remote debug host, which should be connected via one of
the hardware debugging interfaces, which will then be used to both
control Intel Trace Hub and transfer its trace data to the debug host.
The driver needs to be told that such an arrangement is taking place
so that it does not touch any capture/port configuration and avoids
conflicting with the debug host's configuration accesses. The only
activity that the driver will perform in this mode is collecting
software traces to the Software Trace Hub (an stm class device). The
user is still responsible for setting up adequate master/channel
mappings that the decoder on the receiving end would recognize.
In order to enable the host mode, set the 'host_mode' parameter of the
'intel_th' kernel module to 'y'. None of the virtual output devices
will show up on the intel_th bus. Also, trace configuration and
capture controlling attribute groups of the 'gth' device will not be
exposed. The 'sth' device will operate as usual.
...@@ -69,12 +69,43 @@ stm device's channel mmio region is 64 bytes and hardware page size is ...@@ -69,12 +69,43 @@ stm device's channel mmio region is 64 bytes and hardware page size is
width==64, you should be able to mmap() one page on this file 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. descriptor and obtain direct access to an mmio region for 64 channels.
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
[2].
stm_source
==========
For kernel-based trace sources, there is "stm_source" device For kernel-based trace sources, there is "stm_source" device
class. Devices of this class can be connected and disconnected to/from class. Devices of this class can be connected and disconnected to/from
stm devices at runtime via a sysfs attribute. stm devices at runtime via a sysfs attribute called "stm_source_link"
by writing the name of the desired stm device there, for example:
Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM $ echo dummy_stm.0 > /sys/class/stm_source/console/stm_source_link
[2].
For examples on how to use stm_source interface in the kernel, refer
to stm_console or stm_heartbeat drivers.
Each stm_source device will need to assume a master and a range of
channels, depending on how many channels it requires. These are
allocated for the device according to the policy configuration. If
there's a node in the root of the policy directory that matches the
stm_source device's name (for example, "console"), this node will be
used to allocate master and channel numbers. If there's no such policy
node, the stm core will pick the first contiguous chunk of channels
within the first available master. Note that the node must exist
before the stm_source device is connected to its stm device.
stm_console
===========
One implementation of this interface also used in the example above is
the "stm_console" driver, which basically provides a one-way console
for kernel messages over an stm device.
To configure the master/channel pair that will be assigned to this
console in the STP stream, create a "console" policy entry (see the
beginning of this text on how to do that). When initialized, it will
consume one channel.
[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf [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 [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html
...@@ -3067,6 +3067,12 @@ F: drivers/usb/host/whci/ ...@@ -3067,6 +3067,12 @@ F: drivers/usb/host/whci/
F: drivers/usb/wusbcore/ F: drivers/usb/wusbcore/
F: include/linux/usb/wusb* F: include/linux/usb/wusb*
HT16K33 LED CONTROLLER DRIVER
M: Robin van der Gracht <robin@protonic.nl>
S: Maintained
F: drivers/auxdisplay/ht16k33.c
F: Documentation/devicetree/bindings/display/ht16k33.txt
CFAG12864B LCD DRIVER CFAG12864B LCD DRIVER
M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com> M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
W: http://miguelojeda.es/auxdisplay.htm W: http://miguelojeda.es/auxdisplay.htm
...@@ -5043,7 +5049,9 @@ K: fmc_d.*register ...@@ -5043,7 +5049,9 @@ K: fmc_d.*register
FPGA MANAGER FRAMEWORK FPGA MANAGER FRAMEWORK
M: Alan Tull <atull@opensource.altera.com> M: Alan Tull <atull@opensource.altera.com>
R: Moritz Fischer <moritz.fischer@ettus.com> R: Moritz Fischer <moritz.fischer@ettus.com>
L: linux-fpga@vger.kernel.org
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git
F: drivers/fpga/ F: drivers/fpga/
F: include/linux/fpga/fpga-mgr.h F: include/linux/fpga/fpga-mgr.h
W: http://www.rocketboards.org W: http://www.rocketboards.org
...@@ -5940,6 +5948,7 @@ F: drivers/input/serio/hyperv-keyboard.c ...@@ -5940,6 +5948,7 @@ F: drivers/input/serio/hyperv-keyboard.c
F: drivers/pci/host/pci-hyperv.c F: drivers/pci/host/pci-hyperv.c
F: drivers/net/hyperv/ F: drivers/net/hyperv/
F: drivers/scsi/storvsc_drv.c F: drivers/scsi/storvsc_drv.c
F: drivers/uio/uio_hv_generic.c
F: drivers/video/fbdev/hyperv_fb.c F: drivers/video/fbdev/hyperv_fb.c
F: include/linux/hyperv.h F: include/linux/hyperv.h
F: tools/hv/ F: tools/hv/
...@@ -9231,7 +9240,7 @@ F: drivers/misc/panel.c ...@@ -9231,7 +9240,7 @@ F: drivers/misc/panel.c
PARALLEL PORT SUBSYSTEM PARALLEL PORT SUBSYSTEM
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com> M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
M: Sudip Mukherjee <sudip@vectorindia.org> M: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
L: linux-parport@lists.infradead.org (subscribers-only) L: linux-parport@lists.infradead.org (subscribers-only)
S: Maintained S: Maintained
F: drivers/parport/ F: drivers/parport/
...@@ -10841,6 +10850,11 @@ W: http://www.sunplus.com ...@@ -10841,6 +10850,11 @@ W: http://www.sunplus.com
S: Supported S: Supported
F: arch/score/ F: arch/score/
SCR24X CHIP CARD INTERFACE DRIVER
M: Lubomir Rintel <lkundrak@v3.sk>
S: Supported
F: drivers/char/pcmcia/scr24x_cs.c
SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers
M: Sudeep Holla <sudeep.holla@arm.com> M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-arm-kernel@lists.infradead.org L: linux-arm-kernel@lists.infradead.org
...@@ -11244,7 +11258,7 @@ F: include/media/i2c/ov2659.h ...@@ -11244,7 +11258,7 @@ F: include/media/i2c/ov2659.h
SILICON MOTION SM712 FRAME BUFFER DRIVER SILICON MOTION SM712 FRAME BUFFER DRIVER
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com> M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
M: Teddy Wang <teddy.wang@siliconmotion.com> M: Teddy Wang <teddy.wang@siliconmotion.com>
M: Sudip Mukherjee <sudip@vectorindia.org> M: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
L: linux-fbdev@vger.kernel.org L: linux-fbdev@vger.kernel.org
S: Maintained S: Maintained
F: drivers/video/fbdev/sm712* F: drivers/video/fbdev/sm712*
...@@ -11672,7 +11686,7 @@ F: drivers/staging/rtl8712/ ...@@ -11672,7 +11686,7 @@ F: drivers/staging/rtl8712/
STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com> M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
M: Teddy Wang <teddy.wang@siliconmotion.com> M: Teddy Wang <teddy.wang@siliconmotion.com>
M: Sudip Mukherjee <sudip@vectorindia.org> M: Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>
L: linux-fbdev@vger.kernel.org L: linux-fbdev@vger.kernel.org
S: Maintained S: Maintained
F: drivers/staging/sm750fb/ F: drivers/staging/sm750fb/
......
/* Load firmware into Core B on a BF561 /* Load firmware into Core B on a BF561
*
* Author: Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
* *
* Copyright 2004-2009 Analog Devices Inc. * Copyright 2004-2009 Analog Devices Inc.
* Licensed under the GPL-2 or later. * Licensed under the GPL-2 or later.
...@@ -14,9 +16,9 @@ ...@@ -14,9 +16,9 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/module.h>
#define CMD_COREB_START _IO('b', 0) #define CMD_COREB_START _IO('b', 0)
#define CMD_COREB_STOP _IO('b', 1) #define CMD_COREB_STOP _IO('b', 1)
...@@ -59,8 +61,4 @@ static struct miscdevice coreb_dev = { ...@@ -59,8 +61,4 @@ static struct miscdevice coreb_dev = {
.name = "coreb", .name = "coreb",
.fops = &coreb_fops, .fops = &coreb_fops,
}; };
module_misc_device(coreb_dev); builtin_misc_device(coreb_dev);
MODULE_AUTHOR("Bas Vermeulen <bvermeul@blackstar.xs4all.nl>");
MODULE_DESCRIPTION("BF561 Core B Support");
MODULE_LICENSE("GPL");
...@@ -128,4 +128,17 @@ config IMG_ASCII_LCD ...@@ -128,4 +128,17 @@ config IMG_ASCII_LCD
development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3 development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3
from Imagination Technologies. from Imagination Technologies.
config HT16K33
tristate "Holtek Ht16K33 LED controller with keyscan"
depends on FB && OF && I2C && INPUT
select FB_SYS_FOPS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select INPUT_MATRIXKMAP
select FB_BACKLIGHT
help
Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
LED controller driver with keyscan.
endif # AUXDISPLAY endif # AUXDISPLAY
...@@ -5,3 +5,4 @@ ...@@ -5,3 +5,4 @@
obj-$(CONFIG_KS0108) += ks0108.o obj-$(CONFIG_KS0108) += ks0108.o
obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
obj-$(CONFIG_HT16K33) += ht16k33.o
/*
* HT16K33 driver
*
* Author: Robin van der Gracht <robin@protonic.nl>
*
* Copyright: (C) 2016 Protonic Holland.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/fb.h>
#include <linux/slab.h>
#include <linux/backlight.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
/* Registers */
#define REG_SYSTEM_SETUP 0x20
#define REG_SYSTEM_SETUP_OSC_ON BIT(0)
#define REG_DISPLAY_SETUP 0x80
#define REG_DISPLAY_SETUP_ON BIT(0)
#define REG_ROWINT_SET 0xA0
#define REG_ROWINT_SET_INT_EN BIT(0)
#define REG_ROWINT_SET_INT_ACT_HIGH BIT(1)
#define REG_BRIGHTNESS 0xE0
/* Defines */
#define DRIVER_NAME "ht16k33"
#define MIN_BRIGHTNESS 0x1
#define MAX_BRIGHTNESS 0x10
#define HT16K33_MATRIX_LED_MAX_COLS 8
#define HT16K33_MATRIX_LED_MAX_ROWS 16
#define HT16K33_MATRIX_KEYPAD_MAX_COLS 3
#define HT16K33_MATRIX_KEYPAD_MAX_ROWS 12
#define BYTES_PER_ROW (HT16K33_MATRIX_LED_MAX_ROWS / 8)
#define HT16K33_FB_SIZE (HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)
struct ht16k33_keypad {
struct input_dev *dev;
spinlock_t lock;
struct delayed_work work;
uint32_t cols;
uint32_t rows;
uint32_t row_shift;
uint32_t debounce_ms;
uint16_t last_key_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
};
struct ht16k33_fbdev {
struct fb_info *info;
uint32_t refresh_rate;
uint8_t *buffer;
uint8_t *cache;
struct delayed_work work;
};
struct ht16k33_priv {
struct i2c_client *client;
struct ht16k33_keypad keypad;
struct ht16k33_fbdev fbdev;
struct workqueue_struct *workqueue;
};
static struct fb_fix_screeninfo ht16k33_fb_fix = {
.id = DRIVER_NAME,
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_MONO10,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.line_length = HT16K33_MATRIX_LED_MAX_ROWS,
.accel = FB_ACCEL_NONE,
};
static struct fb_var_screeninfo ht16k33_fb_var = {
.xres = HT16K33_MATRIX_LED_MAX_ROWS,
.yres = HT16K33_MATRIX_LED_MAX_COLS,
.xres_virtual = HT16K33_MATRIX_LED_MAX_ROWS,
.yres_virtual = HT16K33_MATRIX_LED_MAX_COLS,
.bits_per_pixel = 1,
.red = { 0, 1, 0 },
.green = { 0, 1, 0 },
.blue = { 0, 1, 0 },
.left_margin = 0,
.right_margin = 0,
.upper_margin = 0,
.lower_margin = 0,
.vmode = FB_VMODE_NONINTERLACED,
};
static int ht16k33_display_on(struct ht16k33_priv *priv)
{
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON;
return i2c_smbus_write_byte(priv->client, data);
}
static int ht16k33_display_off(struct ht16k33_priv *priv)
{
return i2c_smbus_write_byte(priv->client, REG_DISPLAY_SETUP);
}
static void ht16k33_fb_queue(struct ht16k33_priv *priv)
{
struct ht16k33_fbdev *fbdev = &priv->fbdev;
queue_delayed_work(priv->workqueue, &fbdev->work,
msecs_to_jiffies(HZ / fbdev->refresh_rate));
}
static void ht16k33_keypad_queue(struct ht16k33_priv *priv)
{
struct ht16k33_keypad *keypad = &priv->keypad;
queue_delayed_work(priv->workqueue, &keypad->work,
msecs_to_jiffies(keypad->debounce_ms));
}
/*
* This gets the fb data from cache and copies it to ht16k33 display RAM
*/
static void ht16k33_fb_update(struct work_struct *work)
{
struct ht16k33_fbdev *fbdev =
container_of(work, struct ht16k33_fbdev, work.work);
struct ht16k33_priv *priv =
container_of(fbdev, struct ht16k33_priv, fbdev);
uint8_t *p1, *p2;
int len, pos = 0, first = -1;
p1 = fbdev->cache;
p2 = fbdev->buffer;
/* Search for the first byte with changes */
while (pos < HT16K33_FB_SIZE && first < 0) {
if (*(p1++) - *(p2++))
first = pos;
pos++;
}
/* No changes found */
if (first < 0)
goto requeue;
len = HT16K33_FB_SIZE - first;
p1 = fbdev->cache + HT16K33_FB_SIZE - 1;
p2 = fbdev->buffer + HT16K33_FB_SIZE - 1;
/* Determine i2c transfer length */
while (len > 1) {
if (*(p1--) - *(p2--))
break;
len--;
}
p1 = fbdev->cache + first;
p2 = fbdev->buffer + first;
if (!i2c_smbus_write_i2c_block_data(priv->client, first, len, p2))
memcpy(p1, p2, len);
requeue:
ht16k33_fb_queue(priv);
}
static int ht16k33_keypad_start(struct input_dev *dev)
{
struct ht16k33_priv *priv = input_get_drvdata(dev);
struct ht16k33_keypad *keypad = &priv->keypad;
/*
* Schedule an immediate key scan to capture current key state;
* columns will be activated and IRQs be enabled after the scan.
*/
queue_delayed_work(priv->workqueue, &keypad->work, 0);
return 0;
}
static void ht16k33_keypad_stop(struct input_dev *dev)
{
struct ht16k33_priv *priv = input_get_drvdata(dev);
struct ht16k33_keypad *keypad = &priv->keypad;
cancel_delayed_work(&keypad->work);
/*
* ht16k33_keypad_scan() will leave IRQs enabled;
* we should disable them now.
*/
disable_irq_nosync(priv->client->irq);
}
static int ht16k33_initialize(struct ht16k33_priv *priv)
{
uint8_t byte;
int err;
uint8_t data[HT16K33_MATRIX_LED_MAX_COLS * 2];
/* Clear RAM (8 * 16 bits) */
memset(data, 0, sizeof(data));
err = i2c_smbus_write_block_data(priv->client, 0, sizeof(data), data);
if (err)
return err;
/* Turn on internal oscillator */
byte = REG_SYSTEM_SETUP_OSC_ON | REG_SYSTEM_SETUP;
err = i2c_smbus_write_byte(priv->client, byte);
if (err)
return err;
/* Configure INT pin */
byte = REG_ROWINT_SET | REG_ROWINT_SET_INT_ACT_HIGH;
if (priv->client->irq > 0)
byte |= REG_ROWINT_SET_INT_EN;
return i2c_smbus_write_byte(priv->client, byte);
}
/*
* This gets the keys from keypad and reports it to input subsystem
*/
static void ht16k33_keypad_scan(struct work_struct *work)
{
struct ht16k33_keypad *keypad =
container_of(work, struct ht16k33_keypad, work.work);
struct ht16k33_priv *priv =
container_of(keypad, struct ht16k33_priv, keypad);
const unsigned short *keycodes = keypad->dev->keycode;
uint16_t bits_changed, new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
uint8_t data[HT16K33_MATRIX_KEYPAD_MAX_COLS * 2];
int row, col, code;
bool reschedule = false;
if (i2c_smbus_read_i2c_block_data(priv->client, 0x40, 6, data) != 6) {
dev_err(&priv->client->dev, "Failed to read key data\n");
goto end;
}
for (col = 0; col < keypad->cols; col++) {
new_state[col] = (data[col * 2 + 1] << 8) | data[col * 2];
if (new_state[col])
reschedule = true;
bits_changed = keypad->last_key_state[col] ^ new_state[col];
while (bits_changed) {
row = ffs(bits_changed) - 1;
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(keypad->dev, EV_MSC, MSC_SCAN, code);
input_report_key(keypad->dev, keycodes[code],
new_state[col] & BIT(row));
bits_changed &= ~BIT(row);
}
}
input_sync(keypad->dev);
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
end:
if (reschedule)
ht16k33_keypad_queue(priv);
else
enable_irq(priv->client->irq);
}
static irqreturn_t ht16k33_irq_thread(int irq, void *dev)
{
struct ht16k33_priv *priv = dev;
disable_irq_nosync(priv->client->irq);
ht16k33_keypad_queue(priv);
return IRQ_HANDLED;
}
static int ht16k33_bl_update_status(struct backlight_device *bl)
{
int brightness = bl->props.brightness;
struct ht16k33_priv *priv = bl_get_data(bl);
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
bl->props.state & BL_CORE_FBBLANK || brightness == 0) {
return ht16k33_display_off(priv);
}
ht16k33_display_on(priv);
return i2c_smbus_write_byte(priv->client,
REG_BRIGHTNESS | (brightness - 1));
}
static int ht16k33_bl_check_fb(struct backlight_device *bl, struct fb_info *fi)
{
struct ht16k33_priv *priv = bl_get_data(bl);
return (fi == NULL) || (fi->par == priv);
}
static const struct backlight_ops ht16k33_bl_ops = {
.update_status = ht16k33_bl_update_status,
.check_fb = ht16k33_bl_check_fb,
};
static int ht16k33_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
struct ht16k33_priv *priv = info->par;
return vm_insert_page(vma, vma->vm_start,
virt_to_page(priv->fbdev.buffer));
}
static struct fb_ops ht16k33_fb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
.fb_mmap = ht16k33_mmap,
};
static int ht16k33_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
uint32_t rows, cols, dft_brightness;
struct backlight_device *bl;
struct backlight_properties bl_props;
struct ht16k33_priv *priv;
struct ht16k33_keypad *keypad;
struct ht16k33_fbdev *fbdev;
struct device_node *node = client->dev.of_node;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "i2c_check_functionality error\n");
return -EIO;
}
if (client->irq <= 0) {
dev_err(&client->dev, "No IRQ specified\n");
return -EINVAL;
}
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->client = client;
i2c_set_clientdata(client, priv);
fbdev = &priv->fbdev;
keypad = &priv->keypad;
priv->workqueue = create_singlethread_workqueue(DRIVER_NAME "-wq");
if (priv->workqueue == NULL)
return -ENOMEM;
err = ht16k33_initialize(priv);
if (err)
goto err_destroy_wq;
/* Framebuffer (2 bytes per column) */
BUILD_BUG_ON(PAGE_SIZE < HT16K33_FB_SIZE);
fbdev->buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
if (!fbdev->buffer) {
err = -ENOMEM;
goto err_free_fbdev;
}
fbdev->cache = devm_kmalloc(&client->dev, HT16K33_FB_SIZE, GFP_KERNEL);
if (!fbdev->cache) {
err = -ENOMEM;
goto err_fbdev_buffer;
}
fbdev->info = framebuffer_alloc(0, &client->dev);
if (!fbdev->info) {
err = -ENOMEM;
goto err_fbdev_buffer;
}
err = of_property_read_u32(node, "refresh-rate-hz",
&fbdev->refresh_rate);
if (err) {
dev_err(&client->dev, "refresh rate not specified\n");
goto err_fbdev_info;
}
fb_bl_default_curve(fbdev->info, 0, MIN_BRIGHTNESS, MAX_BRIGHTNESS);
INIT_DELAYED_WORK(&fbdev->work, ht16k33_fb_update);
fbdev->info->fbops = &ht16k33_fb_ops;
fbdev->info->screen_base = (char __iomem *) fbdev->buffer;
fbdev->info->screen_size = HT16K33_FB_SIZE;
fbdev->info->fix = ht16k33_fb_fix;
fbdev->info->var = ht16k33_fb_var;
fbdev->info->pseudo_palette = NULL;
fbdev->info->flags = FBINFO_FLAG_DEFAULT;
fbdev->info->par = priv;
err = register_framebuffer(fbdev->info);
if (err)
goto err_fbdev_info;
/* Keypad */
keypad->dev = devm_input_allocate_device(&client->dev);
if (!keypad->dev) {
err = -ENOMEM;
goto err_fbdev_unregister;
}
keypad->dev->name = DRIVER_NAME"-keypad";
keypad->dev->id.bustype = BUS_I2C;
keypad->dev->open = ht16k33_keypad_start;
keypad->dev->close = ht16k33_keypad_stop;
if (!of_get_property(node, "linux,no-autorepeat", NULL))
__set_bit(EV_REP, keypad->dev->evbit);
err = of_property_read_u32(node, "debounce-delay-ms",
&keypad->debounce_ms);
if (err) {
dev_err(&client->dev, "key debounce delay not specified\n");
goto err_fbdev_unregister;
}
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
ht16k33_irq_thread,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
DRIVER_NAME, priv);
if (err) {
dev_err(&client->dev, "irq request failed %d, error %d\n",
client->irq, err);
goto err_fbdev_unregister;
}
disable_irq_nosync(client->irq);
rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS;
cols = HT16K33_MATRIX_KEYPAD_MAX_COLS;
err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
if (err)
goto err_fbdev_unregister;
err = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL,
keypad->dev);
if (err) {
dev_err(&client->dev, "failed to build keymap\n");
goto err_fbdev_unregister;
}
input_set_drvdata(keypad->dev, priv);
keypad->rows = rows;
keypad->cols = cols;
keypad->row_shift = get_count_order(cols);
INIT_DELAYED_WORK(&keypad->work, ht16k33_keypad_scan);
err = input_register_device(keypad->dev);
if (err)
goto err_fbdev_unregister;
/* Backlight */
memset(&bl_props, 0, sizeof(struct backlight_properties));
bl_props.type = BACKLIGHT_RAW;
bl_props.max_brightness = MAX_BRIGHTNESS;
bl = devm_backlight_device_register(&client->dev, DRIVER_NAME"-bl",
&client->dev, priv,
&ht16k33_bl_ops, &bl_props);
if (IS_ERR(bl)) {
dev_err(&client->dev, "failed to register backlight\n");
err = PTR_ERR(bl);
goto err_keypad_unregister;
}
err = of_property_read_u32(node, "default-brightness-level",
&dft_brightness);
if (err) {
dft_brightness = MAX_BRIGHTNESS;
} else if (dft_brightness > MAX_BRIGHTNESS) {
dev_warn(&client->dev,
"invalid default brightness level: %u, using %u\n",
dft_brightness, MAX_BRIGHTNESS);
dft_brightness = MAX_BRIGHTNESS;
}
bl->props.brightness = dft_brightness;
ht16k33_bl_update_status(bl);
ht16k33_fb_queue(priv);
return 0;
err_keypad_unregister:
input_unregister_device(keypad->dev);
err_fbdev_unregister:
unregister_framebuffer(fbdev->info);
err_fbdev_info:
framebuffer_release(fbdev->info);
err_fbdev_buffer:
free_page((unsigned long) fbdev->buffer);
err_free_fbdev:
kfree(fbdev);
err_destroy_wq:
destroy_workqueue(priv->workqueue);
return err;
}
static int ht16k33_remove(struct i2c_client *client)
{
struct ht16k33_priv *priv = i2c_get_clientdata(client);
struct ht16k33_keypad *keypad = &priv->keypad;
struct ht16k33_fbdev *fbdev = &priv->fbdev;
ht16k33_keypad_stop(keypad->dev);
cancel_delayed_work(&fbdev->work);
unregister_framebuffer(fbdev->info);
framebuffer_release(fbdev->info);
free_page((unsigned long) fbdev->buffer);
destroy_workqueue(priv->workqueue);
return 0;
}
static const struct i2c_device_id ht16k33_i2c_match[] = {
{ "ht16k33", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ht16k33_i2c_match);
static const struct of_device_id ht16k33_of_match[] = {
{ .compatible = "holtek,ht16k33", },
{ }
};
MODULE_DEVICE_TABLE(of, ht16k33_of_match);
static struct i2c_driver ht16k33_driver = {
.probe = ht16k33_probe,
.remove = ht16k33_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(ht16k33_of_match),
},
.id_table = ht16k33_i2c_match,
};
module_i2c_driver(ht16k33_driver);
MODULE_DESCRIPTION("Holtek HT16K33 driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robin van der Gracht <robin@protonic.nl>");
...@@ -17,7 +17,6 @@ config DEVMEM ...@@ -17,7 +17,6 @@ config DEVMEM
config DEVKMEM config DEVKMEM
bool "/dev/kmem virtual device support" bool "/dev/kmem virtual device support"
default y
help help
Say Y here if you want to support the /dev/kmem device. The Say Y here if you want to support the /dev/kmem device. The
/dev/kmem device is rarely used, but can be used for certain /dev/kmem device is rarely used, but can be used for certain
...@@ -579,7 +578,7 @@ config DEVPORT ...@@ -579,7 +578,7 @@ config DEVPORT
source "drivers/s390/char/Kconfig" source "drivers/s390/char/Kconfig"
config TILE_SROM config TILE_SROM
bool "Character-device access via hypervisor to the Tilera SPI ROM" tristate "Character-device access via hypervisor to the Tilera SPI ROM"
depends on TILE depends on TILE
default y default y
---help--- ---help---
......
...@@ -43,6 +43,17 @@ config CARDMAN_4040 ...@@ -43,6 +43,17 @@ config CARDMAN_4040
(http://www.omnikey.com/), or a current development version of OpenCT (http://www.omnikey.com/), or a current development version of OpenCT
(http://www.opensc-project.org/opensc). (http://www.opensc-project.org/opensc).
config SCR24X
tristate "SCR24x Chip Card Interface support"
depends on PCMCIA
help
Enable support for the SCR24x PCMCIA Chip Card Interface.
To compile this driver as a module, choose M here.
The module will be called scr24x_cs..
If unsure say N.
config IPWIRELESS config IPWIRELESS
tristate "IPWireless 3G UMTS PCMCIA card support" tristate "IPWireless 3G UMTS PCMCIA card support"
depends on PCMCIA && NETDEVICES && TTY depends on PCMCIA && NETDEVICES && TTY
......
...@@ -7,3 +7,4 @@ ...@@ -7,3 +7,4 @@
obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o
obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
obj-$(CONFIG_SCR24X) += scr24x_cs.o
/*
* SCR24x PCMCIA Smart Card Reader Driver
*
* Copyright (C) 2005-2006 TL Sudheendran
* Copyright (C) 2016 Lubomir Rintel
*
* Derived from "scr24x_v4.2.6_Release.tar.gz" driver by TL Sudheendran.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#define CCID_HEADER_SIZE 10
#define CCID_LENGTH_OFFSET 1
#define CCID_MAX_LEN 271
#define SCR24X_DATA(n) (1 + n)
#define SCR24X_CMD_STATUS 7
#define CMD_START 0x40
#define CMD_WRITE_BYTE 0x41
#define CMD_READ_BYTE 0x42
#define STATUS_BUSY 0x80
struct scr24x_dev {
struct device *dev;
struct cdev c_dev;
unsigned char buf[CCID_MAX_LEN];
int devno;
struct mutex lock;
struct kref refcnt;
u8 __iomem *regs;
};
#define SCR24X_DEVS 8
static DECLARE_BITMAP(scr24x_minors, SCR24X_DEVS);
static struct class *scr24x_class;
static dev_t scr24x_devt;
static void scr24x_delete(struct kref *kref)
{
struct scr24x_dev *dev = container_of(kref, struct scr24x_dev,
refcnt);
kfree(dev);
}
static int scr24x_wait_ready(struct scr24x_dev *dev)
{
u_char status;
int timeout = 100;
do {
status = ioread8(dev->regs + SCR24X_CMD_STATUS);
if (!(status & STATUS_BUSY))
return 0;
msleep(20);
} while (--timeout);
return -EIO;
}
static int scr24x_open(struct inode *inode, struct file *filp)
{
struct scr24x_dev *dev = container_of(inode->i_cdev,
struct scr24x_dev, c_dev);
kref_get(&dev->refcnt);
filp->private_data = dev;
return nonseekable_open(inode, filp);
}
static int scr24x_release(struct inode *inode, struct file *filp)
{
struct scr24x_dev *dev = filp->private_data;
/* We must not take the dev->lock here as scr24x_delete()
* might be called to remove the dev structure altogether.
* We don't need the lock anyway, since after the reference
* acquired in probe() is released in remove() the chrdev
* is already unregistered and noone can possibly acquire
* a reference via open() anymore. */
kref_put(&dev->refcnt, scr24x_delete);
return 0;
}
static int read_chunk(struct scr24x_dev *dev, size_t offset, size_t limit)
{
size_t i, y;
int ret;
for (i = offset; i < limit; i += 5) {
iowrite8(CMD_READ_BYTE, dev->regs + SCR24X_CMD_STATUS);
ret = scr24x_wait_ready(dev);
if (ret < 0)
return ret;
for (y = 0; y < 5 && i + y < limit; y++)
dev->buf[i + y] = ioread8(dev->regs + SCR24X_DATA(y));
}
return 0;
}
static ssize_t scr24x_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
struct scr24x_dev *dev = filp->private_data;
int ret;
int len;
if (count < CCID_HEADER_SIZE)
return -EINVAL;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
if (!dev->dev) {
ret = -ENODEV;
goto out;
}
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
len = CCID_HEADER_SIZE;
ret = read_chunk(dev, 0, len);
if (ret < 0)
goto out;
len += le32_to_cpu(*(__le32 *)(&dev->buf[CCID_LENGTH_OFFSET]));
if (len > sizeof(dev->buf)) {
ret = -EIO;
goto out;
}
ret = read_chunk(dev, CCID_HEADER_SIZE, len);
if (ret < 0)
goto out;
if (len < count)
count = len;
if (copy_to_user(buf, dev->buf, count)) {
ret = -EFAULT;
goto out;
}
ret = count;
out:
mutex_unlock(&dev->lock);
return ret;
}
static ssize_t scr24x_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct scr24x_dev *dev = filp->private_data;
size_t i, y;
int ret;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
if (!dev->dev) {
ret = -ENODEV;
goto out;
}
if (count > sizeof(dev->buf)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(dev->buf, buf, count)) {
ret = -EFAULT;
goto out;
}
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
iowrite8(CMD_START, dev->regs + SCR24X_CMD_STATUS);
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
for (i = 0; i < count; i += 5) {
for (y = 0; y < 5 && i + y < count; y++)
iowrite8(dev->buf[i + y], dev->regs + SCR24X_DATA(y));
iowrite8(CMD_WRITE_BYTE, dev->regs + SCR24X_CMD_STATUS);
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
}
ret = count;
out:
mutex_unlock(&dev->lock);
return ret;
}
static const struct file_operations scr24x_fops = {
.owner = THIS_MODULE,
.read = scr24x_read,
.write = scr24x_write,
.open = scr24x_open,
.release = scr24x_release,
.llseek = no_llseek,
};
static int scr24x_config_check(struct pcmcia_device *link, void *priv_data)
{
if (resource_size(link->resource[PCMCIA_IOPORT_0]) != 0x11)
return -ENODEV;
return pcmcia_request_io(link);
}
static int scr24x_probe(struct pcmcia_device *link)
{
struct scr24x_dev *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->devno = find_first_zero_bit(scr24x_minors, SCR24X_DEVS);
if (dev->devno >= SCR24X_DEVS) {
ret = -EBUSY;
goto err;
}
mutex_init(&dev->lock);
kref_init(&dev->refcnt);
link->priv = dev;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
ret = pcmcia_loop_config(link, scr24x_config_check, NULL);
if (ret < 0)
goto err;
dev->dev = &link->dev;
dev->regs = devm_ioport_map(&link->dev,
link->resource[PCMCIA_IOPORT_0]->start,
resource_size(link->resource[PCMCIA_IOPORT_0]));
if (!dev->regs) {
ret = -EIO;
goto err;
}
cdev_init(&dev->c_dev, &scr24x_fops);
dev->c_dev.owner = THIS_MODULE;
dev->c_dev.ops = &scr24x_fops;
ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1);
if (ret < 0)
goto err;
ret = pcmcia_enable_device(link);
if (ret < 0) {
pcmcia_disable_device(link);
goto err;
}
device_create(scr24x_class, NULL, MKDEV(MAJOR(scr24x_devt), dev->devno),
NULL, "scr24x%d", dev->devno);
dev_info(&link->dev, "SCR24x Chip Card Interface\n");
return 0;
err:
if (dev->devno < SCR24X_DEVS)
clear_bit(dev->devno, scr24x_minors);
kfree (dev);
return ret;
}
static void scr24x_remove(struct pcmcia_device *link)
{
struct scr24x_dev *dev = (struct scr24x_dev *)link->priv;
device_destroy(scr24x_class, MKDEV(MAJOR(scr24x_devt), dev->devno));
mutex_lock(&dev->lock);
pcmcia_disable_device(link);
cdev_del(&dev->c_dev);
clear_bit(dev->devno, scr24x_minors);
dev->dev = NULL;
mutex_unlock(&dev->lock);
kref_put(&dev->refcnt, scr24x_delete);
}
static const struct pcmcia_device_id scr24x_ids[] = {
PCMCIA_DEVICE_PROD_ID12("HP", "PC Card Smart Card Reader",
0x53cb94f9, 0xbfdf89a5),
PCMCIA_DEVICE_PROD_ID1("SCR241 PCMCIA", 0x6271efa3),
PCMCIA_DEVICE_PROD_ID1("SCR243 PCMCIA", 0x2054e8de),
PCMCIA_DEVICE_PROD_ID1("SCR24x PCMCIA", 0x54a33665),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, scr24x_ids);
static struct pcmcia_driver scr24x_driver = {
.owner = THIS_MODULE,
.name = "scr24x_cs",
.probe = scr24x_probe,
.remove = scr24x_remove,
.id_table = scr24x_ids,
};
static int __init scr24x_init(void)
{
int ret;
scr24x_class = class_create(THIS_MODULE, "scr24x");
if (IS_ERR(scr24x_class))
return PTR_ERR(scr24x_class);
ret = alloc_chrdev_region(&scr24x_devt, 0, SCR24X_DEVS, "scr24x");
if (ret < 0) {
class_destroy(scr24x_class);
return ret;
}
ret = pcmcia_register_driver(&scr24x_driver);
if (ret < 0) {
unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
class_destroy(scr24x_class);
}
return ret;
}
static void __exit scr24x_exit(void)
{
pcmcia_unregister_driver(&scr24x_driver);
unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
class_destroy(scr24x_class);
}
module_init(scr24x_init);
module_exit(scr24x_exit);
MODULE_AUTHOR("Lubomir Rintel");
MODULE_DESCRIPTION("SCR24x PCMCIA Smart Card Reader Driver");
MODULE_LICENSE("GPL");
...@@ -86,6 +86,9 @@ struct pp_struct { ...@@ -86,6 +86,9 @@ struct pp_struct {
long default_inactivity; long default_inactivity;
}; };
/* should we use PARDEVICE_MAX here? */
static struct device *devices[PARPORT_MAX];
/* pp_struct.flags bitfields */ /* pp_struct.flags bitfields */
#define PP_CLAIMED (1<<0) #define PP_CLAIMED (1<<0)
#define PP_EXCL (1<<1) #define PP_EXCL (1<<1)
...@@ -294,7 +297,7 @@ static int register_device(int minor, struct pp_struct *pp) ...@@ -294,7 +297,7 @@ static int register_device(int minor, struct pp_struct *pp)
port = parport_find_number(minor); port = parport_find_number(minor);
if (!port) { if (!port) {
printk(KERN_WARNING "%s: no associated port!\n", name); pr_warn("%s: no associated port!\n", name);
kfree(name); kfree(name);
return -ENXIO; return -ENXIO;
} }
...@@ -305,10 +308,10 @@ static int register_device(int minor, struct pp_struct *pp) ...@@ -305,10 +308,10 @@ static int register_device(int minor, struct pp_struct *pp)
ppdev_cb.private = pp; ppdev_cb.private = pp;
pdev = parport_register_dev_model(port, name, &ppdev_cb, minor); pdev = parport_register_dev_model(port, name, &ppdev_cb, minor);
parport_put_port(port); parport_put_port(port);
kfree(name);
if (!pdev) { if (!pdev) {
printk(KERN_WARNING "%s: failed to register device!\n", name); pr_warn("%s: failed to register device!\n", name);
kfree(name);
return -ENXIO; return -ENXIO;
} }
...@@ -789,13 +792,29 @@ static const struct file_operations pp_fops = { ...@@ -789,13 +792,29 @@ static const struct file_operations pp_fops = {
static void pp_attach(struct parport *port) static void pp_attach(struct parport *port)
{ {
device_create(ppdev_class, port->dev, MKDEV(PP_MAJOR, port->number), struct device *ret;
NULL, "parport%d", port->number);
if (devices[port->number])
return;
ret = device_create(ppdev_class, port->dev,
MKDEV(PP_MAJOR, port->number), NULL,
"parport%d", port->number);
if (IS_ERR(ret)) {
pr_err("Failed to create device parport%d\n",
port->number);
return;
}
devices[port->number] = ret;
} }
static void pp_detach(struct parport *port) static void pp_detach(struct parport *port)
{ {
if (!devices[port->number])
return;
device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number)); device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
devices[port->number] = NULL;
} }
static int pp_probe(struct pardevice *par_dev) static int pp_probe(struct pardevice *par_dev)
...@@ -822,8 +841,7 @@ static int __init ppdev_init(void) ...@@ -822,8 +841,7 @@ static int __init ppdev_init(void)
int err = 0; int err = 0;
if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) { if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) {
printk(KERN_WARNING CHRDEV ": unable to get major %d\n", pr_warn(CHRDEV ": unable to get major %d\n", PP_MAJOR);
PP_MAJOR);
return -EIO; return -EIO;
} }
ppdev_class = class_create(THIS_MODULE, CHRDEV); ppdev_class = class_create(THIS_MODULE, CHRDEV);
...@@ -833,11 +851,11 @@ static int __init ppdev_init(void) ...@@ -833,11 +851,11 @@ static int __init ppdev_init(void)
} }
err = parport_register_driver(&pp_driver); err = parport_register_driver(&pp_driver);
if (err < 0) { if (err < 0) {
printk(KERN_WARNING CHRDEV ": unable to register with parport\n"); pr_warn(CHRDEV ": unable to register with parport\n");
goto out_class; goto out_class;
} }
printk(KERN_INFO PP_VERSION "\n"); pr_info(PP_VERSION "\n");
goto out; goto out;
out_class: out_class:
......
...@@ -285,7 +285,7 @@ scdrv_write(struct file *file, const char __user *buf, ...@@ -285,7 +285,7 @@ scdrv_write(struct file *file, const char __user *buf,
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
spin_unlock(&sd->sd_wlock); spin_unlock_irqrestore(&sd->sd_wlock, flags);
up(&sd->sd_wbs); up(&sd->sd_wbs);
return -EAGAIN; return -EAGAIN;
} }
......
...@@ -312,7 +312,8 @@ ATTRIBUTE_GROUPS(srom_dev); ...@@ -312,7 +312,8 @@ ATTRIBUTE_GROUPS(srom_dev);
static char *srom_devnode(struct device *dev, umode_t *mode) static char *srom_devnode(struct device *dev, umode_t *mode)
{ {
*mode = S_IRUGO | S_IWUSR; if (mode)
*mode = 0644;
return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev)); return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
} }
......
...@@ -13,12 +13,26 @@ config FPGA ...@@ -13,12 +13,26 @@ config FPGA
if FPGA if FPGA
config FPGA_REGION
tristate "FPGA Region"
depends on OF && FPGA_BRIDGE
help
FPGA Regions allow loading FPGA images under control of
the Device Tree.
config FPGA_MGR_SOCFPGA config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager" tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA depends on ARCH_SOCFPGA || COMPILE_TEST
help help
FPGA manager driver support for Altera SOCFPGA. FPGA manager driver support for Altera SOCFPGA.
config FPGA_MGR_SOCFPGA_A10
tristate "Altera SoCFPGA Arria10"
depends on ARCH_SOCFPGA || COMPILE_TEST
select REGMAP_MMIO
help
FPGA manager driver support for Altera Arria10 SoCFPGA.
config FPGA_MGR_ZYNQ_FPGA config FPGA_MGR_ZYNQ_FPGA
tristate "Xilinx Zynq FPGA" tristate "Xilinx Zynq FPGA"
depends on ARCH_ZYNQ || COMPILE_TEST depends on ARCH_ZYNQ || COMPILE_TEST
...@@ -26,6 +40,29 @@ config FPGA_MGR_ZYNQ_FPGA ...@@ -26,6 +40,29 @@ config FPGA_MGR_ZYNQ_FPGA
help help
FPGA manager driver support for Xilinx Zynq FPGAs. FPGA manager driver support for Xilinx Zynq FPGAs.
config FPGA_BRIDGE
tristate "FPGA Bridge Framework"
depends on OF
help
Say Y here if you want to support bridges connected between host
processors and FPGAs or between FPGAs.
config SOCFPGA_FPGA_BRIDGE
tristate "Altera SoCFPGA FPGA Bridges"
depends on ARCH_SOCFPGA && FPGA_BRIDGE
help
Say Y to enable drivers for FPGA bridges for Altera SOCFPGA
devices.
config ALTERA_FREEZE_BRIDGE
tristate "Altera FPGA Freeze Bridge"
depends on ARCH_SOCFPGA && FPGA_BRIDGE
help
Say Y to enable drivers for Altera FPGA Freeze bridges. A
freeze bridge is a bridge that exists in the FPGA fabric to
isolate one region of the FPGA from the busses while that
region is being reprogrammed.
endif # FPGA endif # FPGA
endmenu endmenu
...@@ -7,4 +7,13 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o ...@@ -7,4 +7,13 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers # FPGA Manager Drivers
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
# FPGA Bridge Drivers
obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o
obj-$(CONFIG_ALTERA_FREEZE_BRIDGE) += altera-freeze-bridge.o
# High Level Interfaces
obj-$(CONFIG_FPGA_REGION) += fpga-region.o
/*
* FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices
*
* Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This driver manages a bridge between an FPGA and the SDRAM used by the ARM
* host processor system (HPS).
*
* The bridge contains 4 read ports, 4 write ports, and 6 command ports.
* Reconfiguring these ports requires that no SDRAM transactions occur during
* reconfiguration. The code reconfiguring the ports cannot run out of SDRAM
* nor can the FPGA access the SDRAM during reconfiguration. This driver does
* not support reconfiguring the ports. The ports are configured by code
* running out of on chip ram before Linux is started and the configuration
* is passed in a handoff register in the system manager.
*
* This driver supports enabling and disabling of the configured ports, which
* allows for safe reprogramming of the FPGA, assuming that the new FPGA image
* uses the same port configuration. Bridges must be disabled before
* reprogramming the FPGA and re-enabled after the FPGA has been programmed.
*/
#include <linux/fpga/fpga-bridge.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#define ALT_SDR_CTL_FPGAPORTRST_OFST 0x80
#define ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK 0x00003fff
#define ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT 0
#define ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT 4
#define ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT 8
/*
* From the Cyclone V HPS Memory Map document:
* These registers are used to store handoff information between the
* preloader and the OS. These 8 registers can be used to store any
* information. The contents of these registers have no impact on
* the state of the HPS hardware.
*/
#define SYSMGR_ISWGRP_HANDOFF3 (0x8C)
#define F2S_BRIDGE_NAME "fpga2sdram"
struct alt_fpga2sdram_data {
struct device *dev;
struct regmap *sdrctl;
int mask;
};
static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge)
{
struct alt_fpga2sdram_data *priv = bridge->priv;
int value;
regmap_read(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, &value);
return (value & priv->mask) == priv->mask;
}
static inline int _alt_fpga2sdram_enable_set(struct alt_fpga2sdram_data *priv,
bool enable)
{
return regmap_update_bits(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST,
priv->mask, enable ? priv->mask : 0);
}
static int alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable)
{
return _alt_fpga2sdram_enable_set(bridge->priv, enable);
}
struct prop_map {
char *prop_name;
u32 *prop_value;
u32 prop_max;
};
static const struct fpga_bridge_ops altera_fpga2sdram_br_ops = {
.enable_set = alt_fpga2sdram_enable_set,
.enable_show = alt_fpga2sdram_enable_show,
};
static const struct of_device_id altera_fpga_of_match[] = {
{ .compatible = "altr,socfpga-fpga2sdram-bridge" },
{},
};
static int alt_fpga_bridge_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct alt_fpga2sdram_data *priv;
u32 enable;
struct regmap *sysmgr;
int ret = 0;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->sdrctl = syscon_regmap_lookup_by_compatible("altr,sdr-ctl");
if (IS_ERR(priv->sdrctl)) {
dev_err(dev, "regmap for altr,sdr-ctl lookup failed.\n");
return PTR_ERR(priv->sdrctl);
}
sysmgr = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
if (IS_ERR(sysmgr)) {
dev_err(dev, "regmap for altr,sys-mgr lookup failed.\n");
return PTR_ERR(sysmgr);
}
/* Get f2s bridge configuration saved in handoff register */
regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
ret = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
&altera_fpga2sdram_br_ops, priv);
if (ret)
return ret;
dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
if (enable > 1) {
dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
} else {
dev_info(dev, "%s bridge\n",
(enable ? "enabling" : "disabling"));
ret = _alt_fpga2sdram_enable_set(priv, enable);
if (ret) {
fpga_bridge_unregister(&pdev->dev);
return ret;
}
}
}
return ret;
}
static int alt_fpga_bridge_remove(struct platform_device *pdev)
{
fpga_bridge_unregister(&pdev->dev);
return 0;
}
MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
static struct platform_driver altera_fpga_driver = {
.probe = alt_fpga_bridge_probe,
.remove = alt_fpga_bridge_remove,
.driver = {
.name = "altera_fpga2sdram_bridge",
.of_match_table = of_match_ptr(altera_fpga_of_match),
},
};
module_platform_driver(altera_fpga_driver);
MODULE_DESCRIPTION("Altera SoCFPGA FPGA to SDRAM Bridge");
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_LICENSE("GPL v2");
/*
* FPGA Freeze Bridge Controller
*
* Copyright (C) 2016 Altera Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/fpga/fpga-bridge.h>
#define FREEZE_CSR_STATUS_OFFSET 0
#define FREEZE_CSR_CTRL_OFFSET 4
#define FREEZE_CSR_ILLEGAL_REQ_OFFSET 8
#define FREEZE_CSR_REG_VERSION 12
#define FREEZE_CSR_SUPPORTED_VERSION 2
#define FREEZE_CSR_STATUS_FREEZE_REQ_DONE BIT(0)
#define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE BIT(1)
#define FREEZE_CSR_CTRL_FREEZE_REQ BIT(0)
#define FREEZE_CSR_CTRL_RESET_REQ BIT(1)
#define FREEZE_CSR_CTRL_UNFREEZE_REQ BIT(2)
#define FREEZE_BRIDGE_NAME "freeze"
struct altera_freeze_br_data {
struct device *dev;
void __iomem *base_addr;
bool enable;
};
/*
* Poll status until status bit is set or we have a timeout.
*/
static int altera_freeze_br_req_ack(struct altera_freeze_br_data *priv,
u32 timeout, u32 req_ack)
{
struct device *dev = priv->dev;
void __iomem *csr_illegal_req_addr = priv->base_addr +
FREEZE_CSR_ILLEGAL_REQ_OFFSET;
u32 status, illegal, ctrl;
int ret = -ETIMEDOUT;
do {
illegal = readl(csr_illegal_req_addr);
if (illegal) {
dev_err(dev, "illegal request detected 0x%x", illegal);
writel(1, csr_illegal_req_addr);
illegal = readl(csr_illegal_req_addr);
if (illegal)
dev_err(dev, "illegal request not cleared 0x%x",
illegal);
ret = -EINVAL;
break;
}
status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
dev_dbg(dev, "%s %x %x\n", __func__, status, req_ack);
status &= req_ack;
if (status) {
ctrl = readl(priv->base_addr + FREEZE_CSR_CTRL_OFFSET);
dev_dbg(dev, "%s request %x acknowledged %x %x\n",
__func__, req_ack, status, ctrl);
ret = 0;
break;
}
udelay(1);
} while (timeout--);
if (ret == -ETIMEDOUT)
dev_err(dev, "%s timeout waiting for 0x%x\n",
__func__, req_ack);
return ret;
}
static int altera_freeze_br_do_freeze(struct altera_freeze_br_data *priv,
u32 timeout)
{
struct device *dev = priv->dev;
void __iomem *csr_ctrl_addr = priv->base_addr +
FREEZE_CSR_CTRL_OFFSET;
u32 status;
int ret;
status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
if (status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE) {
dev_dbg(dev, "%s bridge already disabled %d\n",
__func__, status);
return 0;
} else if (!(status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)) {
dev_err(dev, "%s bridge not enabled %d\n", __func__, status);
return -EINVAL;
}
writel(FREEZE_CSR_CTRL_FREEZE_REQ, csr_ctrl_addr);
ret = altera_freeze_br_req_ack(priv, timeout,
FREEZE_CSR_STATUS_FREEZE_REQ_DONE);
if (ret)
writel(0, csr_ctrl_addr);
else
writel(FREEZE_CSR_CTRL_RESET_REQ, csr_ctrl_addr);
return ret;
}
static int altera_freeze_br_do_unfreeze(struct altera_freeze_br_data *priv,
u32 timeout)
{
struct device *dev = priv->dev;
void __iomem *csr_ctrl_addr = priv->base_addr +
FREEZE_CSR_CTRL_OFFSET;
u32 status;
int ret;
writel(0, csr_ctrl_addr);
status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE) {
dev_dbg(dev, "%s bridge already enabled %d\n",
__func__, status);
return 0;
} else if (!(status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE)) {
dev_err(dev, "%s bridge not frozen %d\n", __func__, status);
return -EINVAL;
}
writel(FREEZE_CSR_CTRL_UNFREEZE_REQ, csr_ctrl_addr);
ret = altera_freeze_br_req_ack(priv, timeout,
FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE);
status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
writel(0, csr_ctrl_addr);
return ret;
}
/*
* enable = 1 : allow traffic through the bridge
* enable = 0 : disable traffic through the bridge
*/
static int altera_freeze_br_enable_set(struct fpga_bridge *bridge,
bool enable)
{
struct altera_freeze_br_data *priv = bridge->priv;
struct fpga_image_info *info = bridge->info;
u32 timeout = 0;
int ret;
if (enable) {
if (info)
timeout = info->enable_timeout_us;
ret = altera_freeze_br_do_unfreeze(bridge->priv, timeout);
} else {
if (info)
timeout = info->disable_timeout_us;
ret = altera_freeze_br_do_freeze(bridge->priv, timeout);
}
if (!ret)
priv->enable = enable;
return ret;
}
static int altera_freeze_br_enable_show(struct fpga_bridge *bridge)
{
struct altera_freeze_br_data *priv = bridge->priv;
return priv->enable;
}
static struct fpga_bridge_ops altera_freeze_br_br_ops = {
.enable_set = altera_freeze_br_enable_set,
.enable_show = altera_freeze_br_enable_show,
};
static const struct of_device_id altera_freeze_br_of_match[] = {
{ .compatible = "altr,freeze-bridge-controller", },
{},
};
MODULE_DEVICE_TABLE(of, altera_freeze_br_of_match);
static int altera_freeze_br_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct altera_freeze_br_data *priv;
struct resource *res;
u32 status, revision;
if (!np)
return -ENODEV;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base_addr))
return PTR_ERR(priv->base_addr);
status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
priv->enable = 1;
revision = readl(priv->base_addr + FREEZE_CSR_REG_VERSION);
if (revision != FREEZE_CSR_SUPPORTED_VERSION)
dev_warn(dev,
"%s Freeze Controller unexpected revision %d != %d\n",
__func__, revision, FREEZE_CSR_SUPPORTED_VERSION);
return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
&altera_freeze_br_br_ops, priv);
}
static int altera_freeze_br_remove(struct platform_device *pdev)
{
fpga_bridge_unregister(&pdev->dev);
return 0;
}
static struct platform_driver altera_freeze_br_driver = {
.probe = altera_freeze_br_probe,
.remove = altera_freeze_br_remove,
.driver = {
.name = "altera_freeze_br",
.of_match_table = of_match_ptr(altera_freeze_br_of_match),
},
};
module_platform_driver(altera_freeze_br_driver);
MODULE_DESCRIPTION("Altera Freeze Bridge");
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_LICENSE("GPL v2");
/*
* FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
*
* Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
*
* Includes this patch from the mailing list:
* fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
* Signed-off-by: Anatolij Gustschin <agust@denx.de>
*
* 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/>.
*/
/*
* This driver manages bridges on a Altera SOCFPGA between the ARM host
* processor system (HPS) and the embedded FPGA.
*
* This driver supports enabling and disabling of the configured ports, which
* allows for safe reprogramming of the FPGA, assuming that the new FPGA image
* uses the same port configuration. Bridges must be disabled before
* reprogramming the FPGA and re-enabled after the FPGA has been programmed.
*/
#include <linux/clk.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
#define ALT_L3_REMAP_OFST 0x0
#define ALT_L3_REMAP_MPUZERO_MSK 0x00000001
#define ALT_L3_REMAP_H2F_MSK 0x00000008
#define ALT_L3_REMAP_LWH2F_MSK 0x00000010
#define HPS2FPGA_BRIDGE_NAME "hps2fpga"
#define LWHPS2FPGA_BRIDGE_NAME "lwhps2fpga"
#define FPGA2HPS_BRIDGE_NAME "fpga2hps"
struct altera_hps2fpga_data {
const char *name;
struct reset_control *bridge_reset;
struct regmap *l3reg;
unsigned int remap_mask;
struct clk *clk;
};
static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge)
{
struct altera_hps2fpga_data *priv = bridge->priv;
return reset_control_status(priv->bridge_reset);
}
/* The L3 REMAP register is write only, so keep a cached value. */
static unsigned int l3_remap_shadow;
static spinlock_t l3_remap_lock;
static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv,
bool enable)
{
unsigned long flags;
int ret;
/* bring bridge out of reset */
if (enable)
ret = reset_control_deassert(priv->bridge_reset);
else
ret = reset_control_assert(priv->bridge_reset);
if (ret)
return ret;
/* Allow bridge to be visible to L3 masters or not */
if (priv->remap_mask) {
spin_lock_irqsave(&l3_remap_lock, flags);
l3_remap_shadow |= ALT_L3_REMAP_MPUZERO_MSK;
if (enable)
l3_remap_shadow |= priv->remap_mask;
else
l3_remap_shadow &= ~priv->remap_mask;
ret = regmap_write(priv->l3reg, ALT_L3_REMAP_OFST,
l3_remap_shadow);
spin_unlock_irqrestore(&l3_remap_lock, flags);
}
return ret;
}
static int alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable)
{
return _alt_hps2fpga_enable_set(bridge->priv, enable);
}
static const struct fpga_bridge_ops altera_hps2fpga_br_ops = {
.enable_set = alt_hps2fpga_enable_set,
.enable_show = alt_hps2fpga_enable_show,
};
static struct altera_hps2fpga_data hps2fpga_data = {
.name = HPS2FPGA_BRIDGE_NAME,
.remap_mask = ALT_L3_REMAP_H2F_MSK,
};
static struct altera_hps2fpga_data lwhps2fpga_data = {
.name = LWHPS2FPGA_BRIDGE_NAME,
.remap_mask = ALT_L3_REMAP_LWH2F_MSK,
};
static struct altera_hps2fpga_data fpga2hps_data = {
.name = FPGA2HPS_BRIDGE_NAME,
};
static const struct of_device_id altera_fpga_of_match[] = {
{ .compatible = "altr,socfpga-hps2fpga-bridge",
.data = &hps2fpga_data },
{ .compatible = "altr,socfpga-lwhps2fpga-bridge",
.data = &lwhps2fpga_data },
{ .compatible = "altr,socfpga-fpga2hps-bridge",
.data = &fpga2hps_data },
{},
};
static int alt_fpga_bridge_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct altera_hps2fpga_data *priv;
const struct of_device_id *of_id;
u32 enable;
int ret;
of_id = of_match_device(altera_fpga_of_match, dev);
priv = (struct altera_hps2fpga_data *)of_id->data;
priv->bridge_reset = of_reset_control_get_by_index(dev->of_node, 0);
if (IS_ERR(priv->bridge_reset)) {
dev_err(dev, "Could not get %s reset control\n", priv->name);
return PTR_ERR(priv->bridge_reset);
}
if (priv->remap_mask) {
priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs");
if (IS_ERR(priv->l3reg)) {
dev_err(dev, "regmap for altr,l3regs lookup failed\n");
return PTR_ERR(priv->l3reg);
}
}
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "no clock specified\n");
return PTR_ERR(priv->clk);
}
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "could not enable clock\n");
return -EBUSY;
}
spin_lock_init(&l3_remap_lock);
if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
if (enable > 1) {
dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
} else {
dev_info(dev, "%s bridge\n",
(enable ? "enabling" : "disabling"));
ret = _alt_hps2fpga_enable_set(priv, enable);
if (ret) {
fpga_bridge_unregister(&pdev->dev);
return ret;
}
}
}
return fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops,
priv);
}
static int alt_fpga_bridge_remove(struct platform_device *pdev)
{
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
struct altera_hps2fpga_data *priv = bridge->priv;
fpga_bridge_unregister(&pdev->dev);
clk_disable_unprepare(priv->clk);
return 0;
}
MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
static struct platform_driver alt_fpga_bridge_driver = {
.probe = alt_fpga_bridge_probe,
.remove = alt_fpga_bridge_remove,
.driver = {
.name = "altera_hps2fpga_bridge",
.of_match_table = of_match_ptr(altera_fpga_of_match),
},
};
module_platform_driver(alt_fpga_bridge_driver);
MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge");
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_LICENSE("GPL v2");
/*
* FPGA Bridge Framework Driver
*
* Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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/fpga/fpga-bridge.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
static DEFINE_IDA(fpga_bridge_ida);
static struct class *fpga_bridge_class;
/* Lock for adding/removing bridges to linked lists*/
spinlock_t bridge_list_lock;
static int fpga_bridge_of_node_match(struct device *dev, const void *data)
{
return dev->of_node == data;
}
/**
* fpga_bridge_enable - Enable transactions on the bridge
*
* @bridge: FPGA bridge
*
* Return: 0 for success, error code otherwise.
*/
int fpga_bridge_enable(struct fpga_bridge *bridge)
{
dev_dbg(&bridge->dev, "enable\n");
if (bridge->br_ops && bridge->br_ops->enable_set)
return bridge->br_ops->enable_set(bridge, 1);
return 0;
}
EXPORT_SYMBOL_GPL(fpga_bridge_enable);
/**
* fpga_bridge_disable - Disable transactions on the bridge
*
* @bridge: FPGA bridge
*
* Return: 0 for success, error code otherwise.
*/
int fpga_bridge_disable(struct fpga_bridge *bridge)
{
dev_dbg(&bridge->dev, "disable\n");
if (bridge->br_ops && bridge->br_ops->enable_set)
return bridge->br_ops->enable_set(bridge, 0);
return 0;
}
EXPORT_SYMBOL_GPL(fpga_bridge_disable);
/**
* of_fpga_bridge_get - get an exclusive reference to a fpga bridge
*
* @np: node pointer of a FPGA bridge
* @info: fpga image specific information
*
* Return fpga_bridge struct if successful.
* Return -EBUSY if someone already has a reference to the bridge.
* Return -ENODEV if @np is not a FPGA Bridge.
*/
struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
struct fpga_image_info *info)
{
struct device *dev;
struct fpga_bridge *bridge;
int ret = -ENODEV;
dev = class_find_device(fpga_bridge_class, NULL, np,
fpga_bridge_of_node_match);
if (!dev)
goto err_dev;
bridge = to_fpga_bridge(dev);
if (!bridge)
goto err_dev;
bridge->info = info;
if (!mutex_trylock(&bridge->mutex)) {
ret = -EBUSY;
goto err_dev;
}
if (!try_module_get(dev->parent->driver->owner))
goto err_ll_mod;
dev_dbg(&bridge->dev, "get\n");
return bridge;
err_ll_mod:
mutex_unlock(&bridge->mutex);
err_dev:
put_device(dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
/**
* fpga_bridge_put - release a reference to a bridge
*
* @bridge: FPGA bridge
*/
void fpga_bridge_put(struct fpga_bridge *bridge)
{
dev_dbg(&bridge->dev, "put\n");
bridge->info = NULL;
module_put(bridge->dev.parent->driver->owner);
mutex_unlock(&bridge->mutex);
put_device(&bridge->dev);
}
EXPORT_SYMBOL_GPL(fpga_bridge_put);
/**
* fpga_bridges_enable - enable bridges in a list
* @bridge_list: list of FPGA bridges
*
* Enable each bridge in the list. If list is empty, do nothing.
*
* Return 0 for success or empty bridge list; return error code otherwise.
*/
int fpga_bridges_enable(struct list_head *bridge_list)
{
struct fpga_bridge *bridge;
struct list_head *node;
int ret;
list_for_each(node, bridge_list) {
bridge = list_entry(node, struct fpga_bridge, node);
ret = fpga_bridge_enable(bridge);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(fpga_bridges_enable);
/**
* fpga_bridges_disable - disable bridges in a list
*
* @bridge_list: list of FPGA bridges
*
* Disable each bridge in the list. If list is empty, do nothing.
*
* Return 0 for success or empty bridge list; return error code otherwise.
*/
int fpga_bridges_disable(struct list_head *bridge_list)
{
struct fpga_bridge *bridge;
struct list_head *node;
int ret;
list_for_each(node, bridge_list) {
bridge = list_entry(node, struct fpga_bridge, node);
ret = fpga_bridge_disable(bridge);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(fpga_bridges_disable);
/**
* fpga_bridges_put - put bridges
*
* @bridge_list: list of FPGA bridges
*
* For each bridge in the list, put the bridge and remove it from the list.
* If list is empty, do nothing.
*/
void fpga_bridges_put(struct list_head *bridge_list)
{
struct fpga_bridge *bridge;
struct list_head *node, *next;
unsigned long flags;
list_for_each_safe(node, next, bridge_list) {
bridge = list_entry(node, struct fpga_bridge, node);
fpga_bridge_put(bridge);
spin_lock_irqsave(&bridge_list_lock, flags);
list_del(&bridge->node);
spin_unlock_irqrestore(&bridge_list_lock, flags);
}
}
EXPORT_SYMBOL_GPL(fpga_bridges_put);
/**
* fpga_bridges_get_to_list - get a bridge, add it to a list
*
* @np: node pointer of a FPGA bridge
* @info: fpga image specific information
* @bridge_list: list of FPGA bridges
*
* Get an exclusive reference to the bridge and and it to the list.
*
* Return 0 for success, error code from of_fpga_bridge_get() othewise.
*/
int fpga_bridge_get_to_list(struct device_node *np,
struct fpga_image_info *info,
struct list_head *bridge_list)
{
struct fpga_bridge *bridge;
unsigned long flags;
bridge = of_fpga_bridge_get(np, info);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
spin_lock_irqsave(&bridge_list_lock, flags);
list_add(&bridge->node, bridge_list);
spin_unlock_irqrestore(&bridge_list_lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(fpga_bridge_get_to_list);
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fpga_bridge *bridge = to_fpga_bridge(dev);
return sprintf(buf, "%s\n", bridge->name);
}
static ssize_t state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fpga_bridge *bridge = to_fpga_bridge(dev);
int enable = 1;
if (bridge->br_ops && bridge->br_ops->enable_show)
enable = bridge->br_ops->enable_show(bridge);
return sprintf(buf, "%s\n", enable ? "enabled" : "disabled");
}
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(state);
static struct attribute *fpga_bridge_attrs[] = {
&dev_attr_name.attr,
&dev_attr_state.attr,
NULL,
};
ATTRIBUTE_GROUPS(fpga_bridge);
/**
* fpga_bridge_register - register a fpga bridge driver
* @dev: FPGA bridge device from pdev
* @name: FPGA bridge name
* @br_ops: pointer to structure of fpga bridge ops
* @priv: FPGA bridge private data
*
* Return: 0 for success, error code otherwise.
*/
int fpga_bridge_register(struct device *dev, const char *name,
const struct fpga_bridge_ops *br_ops, void *priv)
{
struct fpga_bridge *bridge;
int id, ret = 0;
if (!name || !strlen(name)) {
dev_err(dev, "Attempt to register with no name!\n");
return -EINVAL;
}
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
if (!bridge)
return -ENOMEM;
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
goto error_kfree;
}
mutex_init(&bridge->mutex);
INIT_LIST_HEAD(&bridge->node);
bridge->name = name;
bridge->br_ops = br_ops;
bridge->priv = priv;
device_initialize(&bridge->dev);
bridge->dev.class = fpga_bridge_class;
bridge->dev.parent = dev;
bridge->dev.of_node = dev->of_node;
bridge->dev.id = id;
dev_set_drvdata(dev, bridge);
ret = dev_set_name(&bridge->dev, "br%d", id);
if (ret)
goto error_device;
ret = device_add(&bridge->dev);
if (ret)
goto error_device;
of_platform_populate(dev->of_node, NULL, NULL, dev);
dev_info(bridge->dev.parent, "fpga bridge [%s] registered\n",
bridge->name);
return 0;
error_device:
ida_simple_remove(&fpga_bridge_ida, id);
error_kfree:
kfree(bridge);
return ret;
}
EXPORT_SYMBOL_GPL(fpga_bridge_register);
/**
* fpga_bridge_unregister - unregister a fpga bridge driver
* @dev: FPGA bridge device from pdev
*/
void fpga_bridge_unregister(struct device *dev)
{
struct fpga_bridge *bridge = dev_get_drvdata(dev);
/*
* If the low level driver provides a method for putting bridge into
* a desired state upon unregister, do it.
*/
if (bridge->br_ops && bridge->br_ops->fpga_bridge_remove)
bridge->br_ops->fpga_bridge_remove(bridge);
device_unregister(&bridge->dev);
}
EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
static void fpga_bridge_dev_release(struct device *dev)
{
struct fpga_bridge *bridge = to_fpga_bridge(dev);
ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
kfree(bridge);
}
static int __init fpga_bridge_dev_init(void)
{
spin_lock_init(&bridge_list_lock);
fpga_bridge_class = class_create(THIS_MODULE, "fpga_bridge");
if (IS_ERR(fpga_bridge_class))
return PTR_ERR(fpga_bridge_class);
fpga_bridge_class->dev_groups = fpga_bridge_groups;
fpga_bridge_class->dev_release = fpga_bridge_dev_release;
return 0;
}
static void __exit fpga_bridge_dev_exit(void)
{
class_destroy(fpga_bridge_class);
ida_destroy(&fpga_bridge_ida);
}
MODULE_DESCRIPTION("FPGA Bridge Driver");
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_LICENSE("GPL v2");
subsys_initcall(fpga_bridge_dev_init);
module_exit(fpga_bridge_dev_exit);
...@@ -32,19 +32,20 @@ static struct class *fpga_mgr_class; ...@@ -32,19 +32,20 @@ static struct class *fpga_mgr_class;
/** /**
* fpga_mgr_buf_load - load fpga from image in buffer * fpga_mgr_buf_load - load fpga from image in buffer
* @mgr: fpga manager * @mgr: fpga manager
* @flags: flags setting fpga confuration modes * @info: fpga image specific information
* @buf: buffer contain fpga image * @buf: buffer contain fpga image
* @count: byte count of buf * @count: byte count of buf
* *
* Step the low level fpga manager through the device-specific steps of getting * 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 * 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 * 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. * mgr pointer from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is
* not an error code.
* *
* Return: 0 on success, negative error code otherwise. * Return: 0 on success, negative error code otherwise.
*/ */
int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf, int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
size_t count) const char *buf, size_t count)
{ {
struct device *dev = &mgr->dev; struct device *dev = &mgr->dev;
int ret; int ret;
...@@ -52,10 +53,12 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf, ...@@ -52,10 +53,12 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
/* /*
* Call the low level driver's write_init function. This will do the * 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 * device-specific things to get the FPGA into the state where it is
* ready to receive an FPGA image. * ready to receive an FPGA image. The low level driver only gets to
* see the first initial_header_size bytes in the buffer.
*/ */
mgr->state = FPGA_MGR_STATE_WRITE_INIT; mgr->state = FPGA_MGR_STATE_WRITE_INIT;
ret = mgr->mops->write_init(mgr, flags, buf, count); ret = mgr->mops->write_init(mgr, info, buf,
min(mgr->mops->initial_header_size, count));
if (ret) { if (ret) {
dev_err(dev, "Error preparing FPGA for writing\n"); dev_err(dev, "Error preparing FPGA for writing\n");
mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
...@@ -78,7 +81,7 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf, ...@@ -78,7 +81,7 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
* steps to finish and set the FPGA into operating mode. * steps to finish and set the FPGA into operating mode.
*/ */
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
ret = mgr->mops->write_complete(mgr, flags); ret = mgr->mops->write_complete(mgr, info);
if (ret) { if (ret) {
dev_err(dev, "Error after writing image data to FPGA\n"); dev_err(dev, "Error after writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
...@@ -93,17 +96,19 @@ EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); ...@@ -93,17 +96,19 @@ EXPORT_SYMBOL_GPL(fpga_mgr_buf_load);
/** /**
* fpga_mgr_firmware_load - request firmware and load to fpga * fpga_mgr_firmware_load - request firmware and load to fpga
* @mgr: fpga manager * @mgr: fpga manager
* @flags: flags setting fpga confuration modes * @info: fpga image specific information
* @image_name: name of image file on the firmware search path * @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. * 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 * 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 * 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. * from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is not an error
* code.
* *
* Return: 0 on success, negative error code otherwise. * Return: 0 on success, negative error code otherwise.
*/ */
int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags, int fpga_mgr_firmware_load(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *image_name) const char *image_name)
{ {
struct device *dev = &mgr->dev; struct device *dev = &mgr->dev;
...@@ -121,7 +126,7 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags, ...@@ -121,7 +126,7 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
return ret; return ret;
} }
ret = fpga_mgr_buf_load(mgr, flags, fw->data, fw->size); ret = fpga_mgr_buf_load(mgr, info, fw->data, fw->size);
release_firmware(fw); release_firmware(fw);
...@@ -181,30 +186,11 @@ static struct attribute *fpga_mgr_attrs[] = { ...@@ -181,30 +186,11 @@ static struct attribute *fpga_mgr_attrs[] = {
}; };
ATTRIBUTE_GROUPS(fpga_mgr); ATTRIBUTE_GROUPS(fpga_mgr);
static int fpga_mgr_of_node_match(struct device *dev, const void *data) struct fpga_manager *__fpga_mgr_get(struct device *dev)
{
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 fpga_manager *mgr;
struct device *dev;
int ret = -ENODEV; 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); mgr = to_fpga_manager(dev);
if (!mgr) if (!mgr)
goto err_dev; goto err_dev;
...@@ -226,6 +212,55 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node) ...@@ -226,6 +212,55 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
put_device(dev); put_device(dev);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
static int fpga_mgr_dev_match(struct device *dev, const void *data)
{
return dev->parent == data;
}
/**
* fpga_mgr_get - get an exclusive reference to a fpga mgr
* @dev: parent device that fpga mgr was registered with
*
* Given a device, get an exclusive reference to a fpga mgr.
*
* Return: fpga manager struct or IS_ERR() condition containing error code.
*/
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
struct device *mgr_dev = class_find_device(fpga_mgr_class, NULL, dev,
fpga_mgr_dev_match);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
return __fpga_mgr_get(mgr_dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_get);
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 device *dev;
dev = class_find_device(fpga_mgr_class, NULL, node,
fpga_mgr_of_node_match);
if (!dev)
return ERR_PTR(-ENODEV);
return __fpga_mgr_get(dev);
}
EXPORT_SYMBOL_GPL(of_fpga_mgr_get); EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
/** /**
......
/*
* FPGA Region - Device Tree support for FPGA programming under Linux
*
* Copyright (C) 2013-2016 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/fpga/fpga-bridge.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
/**
* struct fpga_region - FPGA Region structure
* @dev: FPGA Region device
* @mutex: enforces exclusive reference to region
* @bridge_list: list of FPGA bridges specified in region
* @info: fpga image specific information
*/
struct fpga_region {
struct device dev;
struct mutex mutex; /* for exclusive reference to region */
struct list_head bridge_list;
struct fpga_image_info *info;
};
#define to_fpga_region(d) container_of(d, struct fpga_region, dev)
static DEFINE_IDA(fpga_region_ida);
static struct class *fpga_region_class;
static const struct of_device_id fpga_region_of_match[] = {
{ .compatible = "fpga-region", },
{},
};
MODULE_DEVICE_TABLE(of, fpga_region_of_match);
static int fpga_region_of_node_match(struct device *dev, const void *data)
{
return dev->of_node == data;
}
/**
* fpga_region_find - find FPGA region
* @np: device node of FPGA Region
* Caller will need to put_device(&region->dev) when done.
* Returns FPGA Region struct or NULL
*/
static struct fpga_region *fpga_region_find(struct device_node *np)
{
struct device *dev;
dev = class_find_device(fpga_region_class, NULL, np,
fpga_region_of_node_match);
if (!dev)
return NULL;
return to_fpga_region(dev);
}
/**
* fpga_region_get - get an exclusive reference to a fpga region
* @region: FPGA Region struct
*
* Caller should call fpga_region_put() when done with region.
*
* Return fpga_region struct if successful.
* Return -EBUSY if someone already has a reference to the region.
* Return -ENODEV if @np is not a FPGA Region.
*/
static struct fpga_region *fpga_region_get(struct fpga_region *region)
{
struct device *dev = &region->dev;
if (!mutex_trylock(&region->mutex)) {
dev_dbg(dev, "%s: FPGA Region already in use\n", __func__);
return ERR_PTR(-EBUSY);
}
get_device(dev);
of_node_get(dev->of_node);
if (!try_module_get(dev->parent->driver->owner)) {
of_node_put(dev->of_node);
put_device(dev);
mutex_unlock(&region->mutex);
return ERR_PTR(-ENODEV);
}
dev_dbg(&region->dev, "get\n");
return region;
}
/**
* fpga_region_put - release a reference to a region
*
* @region: FPGA region
*/
static void fpga_region_put(struct fpga_region *region)
{
struct device *dev = &region->dev;
dev_dbg(&region->dev, "put\n");
module_put(dev->parent->driver->owner);
of_node_put(dev->of_node);
put_device(dev);
mutex_unlock(&region->mutex);
}
/**
* fpga_region_get_manager - get exclusive reference for FPGA manager
* @region: FPGA region
*
* Get FPGA Manager from "fpga-mgr" property or from ancestor region.
*
* Caller should call fpga_mgr_put() when done with manager.
*
* Return: fpga manager struct or IS_ERR() condition containing error code.
*/
static struct fpga_manager *fpga_region_get_manager(struct fpga_region *region)
{
struct device *dev = &region->dev;
struct device_node *np = dev->of_node;
struct device_node *mgr_node;
struct fpga_manager *mgr;
of_node_get(np);
while (np) {
if (of_device_is_compatible(np, "fpga-region")) {
mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
if (mgr_node) {
mgr = of_fpga_mgr_get(mgr_node);
of_node_put(np);
return mgr;
}
}
np = of_get_next_parent(np);
}
of_node_put(np);
return ERR_PTR(-EINVAL);
}
/**
* fpga_region_get_bridges - create a list of bridges
* @region: FPGA region
* @overlay: device node of the overlay
*
* Create a list of bridges including the parent bridge and the bridges
* specified by "fpga-bridges" property. Note that the
* fpga_bridges_enable/disable/put functions are all fine with an empty list
* if that happens.
*
* Caller should call fpga_bridges_put(&region->bridge_list) when
* done with the bridges.
*
* Return 0 for success (even if there are no bridges specified)
* or -EBUSY if any of the bridges are in use.
*/
static int fpga_region_get_bridges(struct fpga_region *region,
struct device_node *overlay)
{
struct device *dev = &region->dev;
struct device_node *region_np = dev->of_node;
struct device_node *br, *np, *parent_br = NULL;
int i, ret;
/* If parent is a bridge, add to list */
ret = fpga_bridge_get_to_list(region_np->parent, region->info,
&region->bridge_list);
if (ret == -EBUSY)
return ret;
if (!ret)
parent_br = region_np->parent;
/* If overlay has a list of bridges, use it. */
if (of_parse_phandle(overlay, "fpga-bridges", 0))
np = overlay;
else
np = region_np;
for (i = 0; ; i++) {
br = of_parse_phandle(np, "fpga-bridges", i);
if (!br)
break;
/* If parent bridge is in list, skip it. */
if (br == parent_br)
continue;
/* If node is a bridge, get it and add to list */
ret = fpga_bridge_get_to_list(br, region->info,
&region->bridge_list);
/* If any of the bridges are in use, give up */
if (ret == -EBUSY) {
fpga_bridges_put(&region->bridge_list);
return -EBUSY;
}
}
return 0;
}
/**
* fpga_region_program_fpga - program FPGA
* @region: FPGA region
* @firmware_name: name of FPGA image firmware file
* @overlay: device node of the overlay
* Program an FPGA using information in the device tree.
* Function assumes that there is a firmware-name property.
* Return 0 for success or negative error code.
*/
static int fpga_region_program_fpga(struct fpga_region *region,
const char *firmware_name,
struct device_node *overlay)
{
struct fpga_manager *mgr;
int ret;
region = fpga_region_get(region);
if (IS_ERR(region)) {
pr_err("failed to get fpga region\n");
return PTR_ERR(region);
}
mgr = fpga_region_get_manager(region);
if (IS_ERR(mgr)) {
pr_err("failed to get fpga region manager\n");
return PTR_ERR(mgr);
}
ret = fpga_region_get_bridges(region, overlay);
if (ret) {
pr_err("failed to get fpga region bridges\n");
goto err_put_mgr;
}
ret = fpga_bridges_disable(&region->bridge_list);
if (ret) {
pr_err("failed to disable region bridges\n");
goto err_put_br;
}
ret = fpga_mgr_firmware_load(mgr, region->info, firmware_name);
if (ret) {
pr_err("failed to load fpga image\n");
goto err_put_br;
}
ret = fpga_bridges_enable(&region->bridge_list);
if (ret) {
pr_err("failed to enable region bridges\n");
goto err_put_br;
}
fpga_mgr_put(mgr);
fpga_region_put(region);
return 0;
err_put_br:
fpga_bridges_put(&region->bridge_list);
err_put_mgr:
fpga_mgr_put(mgr);
fpga_region_put(region);
return ret;
}
/**
* child_regions_with_firmware
* @overlay: device node of the overlay
*
* If the overlay adds child FPGA regions, they are not allowed to have
* firmware-name property.
*
* Return 0 for OK or -EINVAL if child FPGA region adds firmware-name.
*/
static int child_regions_with_firmware(struct device_node *overlay)
{
struct device_node *child_region;
const char *child_firmware_name;
int ret = 0;
of_node_get(overlay);
child_region = of_find_matching_node(overlay, fpga_region_of_match);
while (child_region) {
if (!of_property_read_string(child_region, "firmware-name",
&child_firmware_name)) {
ret = -EINVAL;
break;
}
child_region = of_find_matching_node(child_region,
fpga_region_of_match);
}
of_node_put(child_region);
if (ret)
pr_err("firmware-name not allowed in child FPGA region: %s",
child_region->full_name);
return ret;
}
/**
* fpga_region_notify_pre_apply - pre-apply overlay notification
*
* @region: FPGA region that the overlay was applied to
* @nd: overlay notification data
*
* Called after when an overlay targeted to a FPGA Region is about to be
* applied. Function will check the properties that will be added to the FPGA
* region. If the checks pass, it will program the FPGA.
*
* The checks are:
* The overlay must add either firmware-name or external-fpga-config property
* to the FPGA Region.
*
* firmware-name : program the FPGA
* external-fpga-config : FPGA is already programmed
*
* The overlay can add other FPGA regions, but child FPGA regions cannot have a
* firmware-name property since those regions don't exist yet.
*
* If the overlay that breaks the rules, notifier returns an error and the
* overlay is rejected before it goes into the main tree.
*
* Returns 0 for success or negative error code for failure.
*/
static int fpga_region_notify_pre_apply(struct fpga_region *region,
struct of_overlay_notify_data *nd)
{
const char *firmware_name = NULL;
struct fpga_image_info *info;
int ret;
info = devm_kzalloc(&region->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
region->info = info;
/* Reject overlay if child FPGA Regions have firmware-name property */
ret = child_regions_with_firmware(nd->overlay);
if (ret)
return ret;
/* Read FPGA region properties from the overlay */
if (of_property_read_bool(nd->overlay, "partial-fpga-config"))
info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
if (of_property_read_bool(nd->overlay, "external-fpga-config"))
info->flags |= FPGA_MGR_EXTERNAL_CONFIG;
of_property_read_string(nd->overlay, "firmware-name", &firmware_name);
of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us",
&info->enable_timeout_us);
of_property_read_u32(nd->overlay, "region-freeze-timeout-us",
&info->disable_timeout_us);
/* If FPGA was externally programmed, don't specify firmware */
if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) {
pr_err("error: specified firmware and external-fpga-config");
return -EINVAL;
}
/* FPGA is already configured externally. We're done. */
if (info->flags & FPGA_MGR_EXTERNAL_CONFIG)
return 0;
/* If we got this far, we should be programming the FPGA */
if (!firmware_name) {
pr_err("should specify firmware-name or external-fpga-config\n");
return -EINVAL;
}
return fpga_region_program_fpga(region, firmware_name, nd->overlay);
}
/**
* fpga_region_notify_post_remove - post-remove overlay notification
*
* @region: FPGA region that was targeted by the overlay that was removed
* @nd: overlay notification data
*
* Called after an overlay has been removed if the overlay's target was a
* FPGA region.
*/
static void fpga_region_notify_post_remove(struct fpga_region *region,
struct of_overlay_notify_data *nd)
{
fpga_bridges_disable(&region->bridge_list);
fpga_bridges_put(&region->bridge_list);
devm_kfree(&region->dev, region->info);
region->info = NULL;
}
/**
* of_fpga_region_notify - reconfig notifier for dynamic DT changes
* @nb: notifier block
* @action: notifier action
* @arg: reconfig data
*
* This notifier handles programming a FPGA when a "firmware-name" property is
* added to a fpga-region.
*
* Returns NOTIFY_OK or error if FPGA programming fails.
*/
static int of_fpga_region_notify(struct notifier_block *nb,
unsigned long action, void *arg)
{
struct of_overlay_notify_data *nd = arg;
struct fpga_region *region;
int ret;
switch (action) {
case OF_OVERLAY_PRE_APPLY:
pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__);
break;
case OF_OVERLAY_POST_APPLY:
pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__);
return NOTIFY_OK; /* not for us */
case OF_OVERLAY_PRE_REMOVE:
pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__);
return NOTIFY_OK; /* not for us */
case OF_OVERLAY_POST_REMOVE:
pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__);
break;
default: /* should not happen */
return NOTIFY_OK;
}
region = fpga_region_find(nd->target);
if (!region)
return NOTIFY_OK;
ret = 0;
switch (action) {
case OF_OVERLAY_PRE_APPLY:
ret = fpga_region_notify_pre_apply(region, nd);
break;
case OF_OVERLAY_POST_REMOVE:
fpga_region_notify_post_remove(region, nd);
break;
}
put_device(&region->dev);
if (ret)
return notifier_from_errno(ret);
return NOTIFY_OK;
}
static struct notifier_block fpga_region_of_nb = {
.notifier_call = of_fpga_region_notify,
};
static int fpga_region_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct fpga_region *region;
int id, ret = 0;
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
goto err_kfree;
}
mutex_init(&region->mutex);
INIT_LIST_HEAD(&region->bridge_list);
device_initialize(&region->dev);
region->dev.class = fpga_region_class;
region->dev.parent = dev;
region->dev.of_node = np;
region->dev.id = id;
dev_set_drvdata(dev, region);
ret = dev_set_name(&region->dev, "region%d", id);
if (ret)
goto err_remove;
ret = device_add(&region->dev);
if (ret)
goto err_remove;
of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
dev_info(dev, "FPGA Region probed\n");
return 0;
err_remove:
ida_simple_remove(&fpga_region_ida, id);
err_kfree:
kfree(region);
return ret;
}
static int fpga_region_remove(struct platform_device *pdev)
{
struct fpga_region *region = platform_get_drvdata(pdev);
device_unregister(&region->dev);
return 0;
}
static struct platform_driver fpga_region_driver = {
.probe = fpga_region_probe,
.remove = fpga_region_remove,
.driver = {
.name = "fpga-region",
.of_match_table = of_match_ptr(fpga_region_of_match),
},
};
static void fpga_region_dev_release(struct device *dev)
{
struct fpga_region *region = to_fpga_region(dev);
ida_simple_remove(&fpga_region_ida, region->dev.id);
kfree(region);
}
/**
* fpga_region_init - init function for fpga_region class
* Creates the fpga_region class and registers a reconfig notifier.
*/
static int __init fpga_region_init(void)
{
int ret;
fpga_region_class = class_create(THIS_MODULE, "fpga_region");
if (IS_ERR(fpga_region_class))
return PTR_ERR(fpga_region_class);
fpga_region_class->dev_release = fpga_region_dev_release;
ret = of_overlay_notifier_register(&fpga_region_of_nb);
if (ret)
goto err_class;
ret = platform_driver_register(&fpga_region_driver);
if (ret)
goto err_plat;
return 0;
err_plat:
of_overlay_notifier_unregister(&fpga_region_of_nb);
err_class:
class_destroy(fpga_region_class);
ida_destroy(&fpga_region_ida);
return ret;
}
static void __exit fpga_region_exit(void)
{
platform_driver_unregister(&fpga_region_driver);
of_overlay_notifier_unregister(&fpga_region_of_nb);
class_destroy(fpga_region_class);
ida_destroy(&fpga_region_ida);
}
subsys_initcall(fpga_region_init);
module_exit(fpga_region_exit);
MODULE_DESCRIPTION("FPGA Region");
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_LICENSE("GPL v2");
/*
* FPGA Manager Driver for Altera Arria10 SoCFPGA
*
* Copyright (C) 2015-2016 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/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#define A10_FPGAMGR_DCLKCNT_OFST 0x08
#define A10_FPGAMGR_DCLKSTAT_OFST 0x0c
#define A10_FPGAMGR_IMGCFG_CTL_00_OFST 0x70
#define A10_FPGAMGR_IMGCFG_CTL_01_OFST 0x74
#define A10_FPGAMGR_IMGCFG_CTL_02_OFST 0x78
#define A10_FPGAMGR_IMGCFG_STAT_OFST 0x80
#define A10_FPGAMGR_DCLKSTAT_DCLKDONE BIT(0)
#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NCONFIG BIT(0)
#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NSTATUS BIT(1)
#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_CONDONE BIT(2)
#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NCONFIG BIT(8)
#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_NSTATUS_OE BIT(16)
#define A10_FPGAMGR_IMGCFG_CTL_00_S2F_CONDONE_OE BIT(24)
#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG BIT(0)
#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST BIT(16)
#define A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE BIT(24)
#define A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL BIT(0)
#define A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_MASK (BIT(16) | BIT(17))
#define A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_SHIFT 16
#define A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH BIT(24)
#define A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH_SHIFT 24
#define A10_FPGAMGR_IMGCFG_STAT_F2S_CRC_ERROR BIT(0)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_EARLY_USERMODE BIT(1)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE BIT(2)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN BIT(4)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_CONDONE_PIN BIT(6)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY BIT(9)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_DONE BIT(10)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR BIT(11)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_NCONFIG_PIN BIT(12)
#define A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_MASK (BIT(16) | BIT(17) | BIT(18))
#define A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_SHIFT 16
/* FPGA CD Ratio Value */
#define CDRATIO_x1 0x0
#define CDRATIO_x2 0x1
#define CDRATIO_x4 0x2
#define CDRATIO_x8 0x3
/* Configuration width 16/32 bit */
#define CFGWDTH_32 1
#define CFGWDTH_16 0
/*
* struct a10_fpga_priv - private data for fpga manager
* @regmap: regmap for register access
* @fpga_data_addr: iomap for single address data register to FPGA
* @clk: clock
*/
struct a10_fpga_priv {
struct regmap *regmap;
void __iomem *fpga_data_addr;
struct clk *clk;
};
static bool socfpga_a10_fpga_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case A10_FPGAMGR_DCLKCNT_OFST:
case A10_FPGAMGR_DCLKSTAT_OFST:
case A10_FPGAMGR_IMGCFG_CTL_00_OFST:
case A10_FPGAMGR_IMGCFG_CTL_01_OFST:
case A10_FPGAMGR_IMGCFG_CTL_02_OFST:
return true;
}
return false;
}
static bool socfpga_a10_fpga_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case A10_FPGAMGR_DCLKCNT_OFST:
case A10_FPGAMGR_DCLKSTAT_OFST:
case A10_FPGAMGR_IMGCFG_CTL_00_OFST:
case A10_FPGAMGR_IMGCFG_CTL_01_OFST:
case A10_FPGAMGR_IMGCFG_CTL_02_OFST:
case A10_FPGAMGR_IMGCFG_STAT_OFST:
return true;
}
return false;
}
static const struct regmap_config socfpga_a10_fpga_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.writeable_reg = socfpga_a10_fpga_writeable_reg,
.readable_reg = socfpga_a10_fpga_readable_reg,
.max_register = A10_FPGAMGR_IMGCFG_STAT_OFST,
.cache_type = REGCACHE_NONE,
};
/*
* from the register map description of cdratio in imgcfg_ctrl_02:
* Normal Configuration : 32bit Passive Parallel
* Partial Reconfiguration : 16bit Passive Parallel
*/
static void socfpga_a10_fpga_set_cfg_width(struct a10_fpga_priv *priv,
int width)
{
width <<= A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH_SHIFT;
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
A10_FPGAMGR_IMGCFG_CTL_02_CFGWIDTH, width);
}
static void socfpga_a10_fpga_generate_dclks(struct a10_fpga_priv *priv,
u32 count)
{
u32 val;
/* Clear any existing DONE status. */
regmap_write(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST,
A10_FPGAMGR_DCLKSTAT_DCLKDONE);
/* Issue the DCLK regmap. */
regmap_write(priv->regmap, A10_FPGAMGR_DCLKCNT_OFST, count);
/* wait till the dclkcnt done */
regmap_read_poll_timeout(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST, val,
val, 1, 100);
/* Clear DONE status. */
regmap_write(priv->regmap, A10_FPGAMGR_DCLKSTAT_OFST,
A10_FPGAMGR_DCLKSTAT_DCLKDONE);
}
#define RBF_ENCRYPTION_MODE_OFFSET 69
#define RBF_DECOMPRESS_OFFSET 229
static int socfpga_a10_fpga_encrypted(u32 *buf32, size_t buf32_size)
{
if (buf32_size < RBF_ENCRYPTION_MODE_OFFSET + 1)
return -EINVAL;
/* Is the bitstream encrypted? */
return ((buf32[RBF_ENCRYPTION_MODE_OFFSET] >> 2) & 3) != 0;
}
static int socfpga_a10_fpga_compressed(u32 *buf32, size_t buf32_size)
{
if (buf32_size < RBF_DECOMPRESS_OFFSET + 1)
return -EINVAL;
/* Is the bitstream compressed? */
return !((buf32[RBF_DECOMPRESS_OFFSET] >> 1) & 1);
}
static unsigned int socfpga_a10_fpga_get_cd_ratio(unsigned int cfg_width,
bool encrypt, bool compress)
{
unsigned int cd_ratio;
/*
* cd ratio is dependent on cfg width and whether the bitstream
* is encrypted and/or compressed.
*
* | width | encr. | compr. | cd ratio |
* | 16 | 0 | 0 | 1 |
* | 16 | 0 | 1 | 4 |
* | 16 | 1 | 0 | 2 |
* | 16 | 1 | 1 | 4 |
* | 32 | 0 | 0 | 1 |
* | 32 | 0 | 1 | 8 |
* | 32 | 1 | 0 | 4 |
* | 32 | 1 | 1 | 8 |
*/
if (!compress && !encrypt)
return CDRATIO_x1;
if (compress)
cd_ratio = CDRATIO_x4;
else
cd_ratio = CDRATIO_x2;
/* If 32 bit, double the cd ratio by incrementing the field */
if (cfg_width == CFGWDTH_32)
cd_ratio += 1;
return cd_ratio;
}
static int socfpga_a10_fpga_set_cdratio(struct fpga_manager *mgr,
unsigned int cfg_width,
const char *buf, size_t count)
{
struct a10_fpga_priv *priv = mgr->priv;
unsigned int cd_ratio;
int encrypt, compress;
encrypt = socfpga_a10_fpga_encrypted((u32 *)buf, count / 4);
if (encrypt < 0)
return -EINVAL;
compress = socfpga_a10_fpga_compressed((u32 *)buf, count / 4);
if (compress < 0)
return -EINVAL;
cd_ratio = socfpga_a10_fpga_get_cd_ratio(cfg_width, encrypt, compress);
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_MASK,
cd_ratio << A10_FPGAMGR_IMGCFG_CTL_02_CDRATIO_SHIFT);
return 0;
}
static u32 socfpga_a10_fpga_read_stat(struct a10_fpga_priv *priv)
{
u32 val;
regmap_read(priv->regmap, A10_FPGAMGR_IMGCFG_STAT_OFST, &val);
return val;
}
static int socfpga_a10_fpga_wait_for_pr_ready(struct a10_fpga_priv *priv)
{
u32 reg, i;
for (i = 0; i < 10 ; i++) {
reg = socfpga_a10_fpga_read_stat(priv);
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR)
return -EINVAL;
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY)
return 0;
}
return -ETIMEDOUT;
}
static int socfpga_a10_fpga_wait_for_pr_done(struct a10_fpga_priv *priv)
{
u32 reg, i;
for (i = 0; i < 10 ; i++) {
reg = socfpga_a10_fpga_read_stat(priv);
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_ERROR)
return -EINVAL;
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_DONE)
return 0;
}
return -ETIMEDOUT;
}
/* Start the FPGA programming by initialize the FPGA Manager */
static int socfpga_a10_fpga_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
struct a10_fpga_priv *priv = mgr->priv;
unsigned int cfg_width;
u32 msel, stat, mask;
int ret;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG)
cfg_width = CFGWDTH_16;
else
return -EINVAL;
/* Check for passive parallel (msel == 000 or 001) */
msel = socfpga_a10_fpga_read_stat(priv);
msel &= A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_MASK;
msel >>= A10_FPGAMGR_IMGCFG_STAT_F2S_MSEL_SHIFT;
if ((msel != 0) && (msel != 1)) {
dev_dbg(&mgr->dev, "Fail: invalid msel=%d\n", msel);
return -EINVAL;
}
/* Make sure no external devices are interfering */
stat = socfpga_a10_fpga_read_stat(priv);
mask = A10_FPGAMGR_IMGCFG_STAT_F2S_NCONFIG_PIN |
A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN;
if ((stat & mask) != mask)
return -EINVAL;
/* Set cfg width */
socfpga_a10_fpga_set_cfg_width(priv, cfg_width);
/* Determine cd ratio from bitstream header and set cd ratio */
ret = socfpga_a10_fpga_set_cdratio(mgr, cfg_width, buf, count);
if (ret)
return ret;
/*
* Clear s2f_nce to enable chip select. Leave pr_request
* unasserted and override disabled.
*/
regmap_write(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG);
/* Set cfg_ctrl to enable s2f dclk and data */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL,
A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL);
/*
* Disable overrides not needed for pr.
* s2f_config==1 leaves reset deasseted.
*/
regmap_write(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_00_OFST,
A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NCONFIG |
A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_NSTATUS |
A10_FPGAMGR_IMGCFG_CTL_00_S2F_NENABLE_CONDONE |
A10_FPGAMGR_IMGCFG_CTL_00_S2F_NCONFIG);
/* Enable override for data, dclk, nce, and pr_request to CSS */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG, 0);
/* Send some clocks to clear out any errors */
socfpga_a10_fpga_generate_dclks(priv, 256);
/* Assert pr_request */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST);
/* Provide 2048 DCLKs before starting the config data streaming. */
socfpga_a10_fpga_generate_dclks(priv, 0x7ff);
/* Wait for pr_ready */
return socfpga_a10_fpga_wait_for_pr_ready(priv);
}
/*
* write data to the FPGA data register
*/
static int socfpga_a10_fpga_write(struct fpga_manager *mgr, const char *buf,
size_t count)
{
struct a10_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)) {
writel(buffer_32[i++], priv->fpga_data_addr);
count -= sizeof(u32);
}
/* Write out remaining non 32-bit chunks */
switch (count) {
case 3:
writel(buffer_32[i++] & 0x00ffffff, priv->fpga_data_addr);
break;
case 2:
writel(buffer_32[i++] & 0x0000ffff, priv->fpga_data_addr);
break;
case 1:
writel(buffer_32[i++] & 0x000000ff, priv->fpga_data_addr);
break;
case 0:
break;
default:
/* This will never happen */
return -EFAULT;
}
return 0;
}
static int socfpga_a10_fpga_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
struct a10_fpga_priv *priv = mgr->priv;
u32 reg;
int ret;
/* Wait for pr_done */
ret = socfpga_a10_fpga_wait_for_pr_done(priv);
/* Clear pr_request */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_PR_REQUEST, 0);
/* Send some clocks to clear out any errors */
socfpga_a10_fpga_generate_dclks(priv, 256);
/* Disable s2f dclk and data */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_02_OFST,
A10_FPGAMGR_IMGCFG_CTL_02_EN_CFG_CTRL, 0);
/* Deassert chip select */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_NCE);
/* Disable data, dclk, nce, and pr_request override to CSS */
regmap_update_bits(priv->regmap, A10_FPGAMGR_IMGCFG_CTL_01_OFST,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG,
A10_FPGAMGR_IMGCFG_CTL_01_S2F_NENABLE_CONFIG);
/* Return any errors regarding pr_done or pr_error */
if (ret)
return ret;
/* Final check */
reg = socfpga_a10_fpga_read_stat(priv);
if (((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE) == 0) ||
((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_CONDONE_PIN) == 0) ||
((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN) == 0)) {
dev_dbg(&mgr->dev,
"Timeout in final check. Status=%08xf\n", reg);
return -ETIMEDOUT;
}
return 0;
}
static enum fpga_mgr_states socfpga_a10_fpga_state(struct fpga_manager *mgr)
{
struct a10_fpga_priv *priv = mgr->priv;
u32 reg = socfpga_a10_fpga_read_stat(priv);
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_USERMODE)
return FPGA_MGR_STATE_OPERATING;
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_PR_READY)
return FPGA_MGR_STATE_WRITE;
if (reg & A10_FPGAMGR_IMGCFG_STAT_F2S_CRC_ERROR)
return FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
if ((reg & A10_FPGAMGR_IMGCFG_STAT_F2S_NSTATUS_PIN) == 0)
return FPGA_MGR_STATE_RESET;
return FPGA_MGR_STATE_UNKNOWN;
}
static const struct fpga_manager_ops socfpga_a10_fpga_mgr_ops = {
.initial_header_size = (RBF_DECOMPRESS_OFFSET + 1) * 4,
.state = socfpga_a10_fpga_state,
.write_init = socfpga_a10_fpga_write_init,
.write = socfpga_a10_fpga_write,
.write_complete = socfpga_a10_fpga_write_complete,
};
static int socfpga_a10_fpga_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct a10_fpga_priv *priv;
void __iomem *reg_base;
struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* First mmio base is for register access */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
/* Second mmio base is for writing FPGA image data */
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);
/* regmap for register access */
priv->regmap = devm_regmap_init_mmio(dev, reg_base,
&socfpga_a10_fpga_regmap_config);
if (IS_ERR(priv->regmap))
return -ENODEV;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "no clock specified\n");
return PTR_ERR(priv->clk);
}
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "could not enable clock\n");
return -EBUSY;
}
return fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
&socfpga_a10_fpga_mgr_ops, priv);
}
static int socfpga_a10_fpga_remove(struct platform_device *pdev)
{
struct fpga_manager *mgr = platform_get_drvdata(pdev);
struct a10_fpga_priv *priv = mgr->priv;
fpga_mgr_unregister(&pdev->dev);
clk_disable_unprepare(priv->clk);
return 0;
}
static const struct of_device_id socfpga_a10_fpga_of_match[] = {
{ .compatible = "altr,socfpga-a10-fpga-mgr", },
{},
};
MODULE_DEVICE_TABLE(of, socfpga_a10_fpga_of_match);
static struct platform_driver socfpga_a10_fpga_driver = {
.probe = socfpga_a10_fpga_probe,
.remove = socfpga_a10_fpga_remove,
.driver = {
.name = "socfpga_a10_fpga_manager",
.of_match_table = socfpga_a10_fpga_of_match,
},
};
module_platform_driver(socfpga_a10_fpga_driver);
MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
MODULE_DESCRIPTION("SoCFPGA Arria10 FPGA Manager");
MODULE_LICENSE("GPL v2");
...@@ -407,13 +407,14 @@ static int socfpga_fpga_reset(struct fpga_manager *mgr) ...@@ -407,13 +407,14 @@ static int socfpga_fpga_reset(struct fpga_manager *mgr)
/* /*
* Prepare the FPGA to receive the configuration data. * Prepare the FPGA to receive the configuration data.
*/ */
static int socfpga_fpga_ops_configure_init(struct fpga_manager *mgr, u32 flags, static int socfpga_fpga_ops_configure_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct socfpga_fpga_priv *priv = mgr->priv; struct socfpga_fpga_priv *priv = mgr->priv;
int ret; int ret;
if (flags & FPGA_MGR_PARTIAL_RECONFIG) { if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
return -EINVAL; return -EINVAL;
} }
...@@ -478,7 +479,7 @@ static int socfpga_fpga_ops_configure_write(struct fpga_manager *mgr, ...@@ -478,7 +479,7 @@ static int socfpga_fpga_ops_configure_write(struct fpga_manager *mgr,
} }
static int socfpga_fpga_ops_configure_complete(struct fpga_manager *mgr, static int socfpga_fpga_ops_configure_complete(struct fpga_manager *mgr,
u32 flags) struct fpga_image_info *info)
{ {
struct socfpga_fpga_priv *priv = mgr->priv; struct socfpga_fpga_priv *priv = mgr->priv;
u32 status; u32 status;
......
...@@ -118,7 +118,6 @@ ...@@ -118,7 +118,6 @@
#define FPGA_RST_NONE_MASK 0x0 #define FPGA_RST_NONE_MASK 0x0
struct zynq_fpga_priv { struct zynq_fpga_priv {
struct device *dev;
int irq; int irq;
struct clk *clk; struct clk *clk;
...@@ -175,7 +174,8 @@ static irqreturn_t zynq_fpga_isr(int irq, void *data) ...@@ -175,7 +174,8 @@ static irqreturn_t zynq_fpga_isr(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags, static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct zynq_fpga_priv *priv; struct zynq_fpga_priv *priv;
...@@ -189,7 +189,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags, ...@@ -189,7 +189,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
return err; return err;
/* don't globally reset PL if we're doing partial reconfig */ /* don't globally reset PL if we're doing partial reconfig */
if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) { if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
/* assert AXI interface resets */ /* assert AXI interface resets */
regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET, regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET,
FPGA_RST_ALL_MASK); FPGA_RST_ALL_MASK);
...@@ -217,7 +217,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags, ...@@ -217,7 +217,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
INIT_POLL_DELAY, INIT_POLL_DELAY,
INIT_POLL_TIMEOUT); INIT_POLL_TIMEOUT);
if (err) { if (err) {
dev_err(priv->dev, "Timeout waiting for PCFG_INIT"); dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n");
goto out_err; goto out_err;
} }
...@@ -231,7 +231,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags, ...@@ -231,7 +231,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
INIT_POLL_DELAY, INIT_POLL_DELAY,
INIT_POLL_TIMEOUT); INIT_POLL_TIMEOUT);
if (err) { if (err) {
dev_err(priv->dev, "Timeout waiting for !PCFG_INIT"); dev_err(&mgr->dev, "Timeout waiting for !PCFG_INIT\n");
goto out_err; goto out_err;
} }
...@@ -245,7 +245,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags, ...@@ -245,7 +245,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
INIT_POLL_DELAY, INIT_POLL_DELAY,
INIT_POLL_TIMEOUT); INIT_POLL_TIMEOUT);
if (err) { if (err) {
dev_err(priv->dev, "Timeout waiting for PCFG_INIT"); dev_err(&mgr->dev, "Timeout waiting for PCFG_INIT\n");
goto out_err; goto out_err;
} }
} }
...@@ -262,7 +262,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags, ...@@ -262,7 +262,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
/* check that we have room in the command queue */ /* check that we have room in the command queue */
status = zynq_fpga_read(priv, STATUS_OFFSET); status = zynq_fpga_read(priv, STATUS_OFFSET);
if (status & STATUS_DMA_Q_F) { if (status & STATUS_DMA_Q_F) {
dev_err(priv->dev, "DMA command queue full"); dev_err(&mgr->dev, "DMA command queue full\n");
err = -EBUSY; err = -EBUSY;
goto out_err; goto out_err;
} }
...@@ -295,7 +295,8 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, ...@@ -295,7 +295,8 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
in_count = count; in_count = count;
priv = mgr->priv; priv = mgr->priv;
kbuf = dma_alloc_coherent(priv->dev, count, &dma_addr, GFP_KERNEL); kbuf =
dma_alloc_coherent(mgr->dev.parent, count, &dma_addr, GFP_KERNEL);
if (!kbuf) if (!kbuf)
return -ENOMEM; return -ENOMEM;
...@@ -331,19 +332,19 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, ...@@ -331,19 +332,19 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
zynq_fpga_write(priv, INT_STS_OFFSET, intr_status); zynq_fpga_write(priv, INT_STS_OFFSET, intr_status);
if (!((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) { if (!((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) {
dev_err(priv->dev, "Error configuring FPGA"); dev_err(&mgr->dev, "Error configuring FPGA\n");
err = -EFAULT; err = -EFAULT;
} }
clk_disable(priv->clk); clk_disable(priv->clk);
out_free: out_free:
dma_free_coherent(priv->dev, in_count, kbuf, dma_addr); dma_free_coherent(mgr->dev.parent, count, kbuf, dma_addr);
return err; return err;
} }
static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags) static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{ {
struct zynq_fpga_priv *priv = mgr->priv; struct zynq_fpga_priv *priv = mgr->priv;
int err; int err;
...@@ -364,7 +365,7 @@ static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags) ...@@ -364,7 +365,7 @@ static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
return err; return err;
/* for the partial reconfig case we didn't touch the level shifters */ /* for the partial reconfig case we didn't touch the level shifters */
if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) { if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
/* enable level shifters from PL to PS */ /* enable level shifters from PL to PS */
regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET, regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET,
LVL_SHFTR_ENABLE_PL_TO_PS); LVL_SHFTR_ENABLE_PL_TO_PS);
...@@ -416,8 +417,6 @@ static int zynq_fpga_probe(struct platform_device *pdev) ...@@ -416,8 +417,6 @@ static int zynq_fpga_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(dev, res); priv->io_base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->io_base)) if (IS_ERR(priv->io_base))
...@@ -426,7 +425,7 @@ static int zynq_fpga_probe(struct platform_device *pdev) ...@@ -426,7 +425,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
priv->slcr = syscon_regmap_lookup_by_phandle(dev->of_node, priv->slcr = syscon_regmap_lookup_by_phandle(dev->of_node,
"syscon"); "syscon");
if (IS_ERR(priv->slcr)) { if (IS_ERR(priv->slcr)) {
dev_err(dev, "unable to get zynq-slcr regmap"); dev_err(dev, "unable to get zynq-slcr regmap\n");
return PTR_ERR(priv->slcr); return PTR_ERR(priv->slcr);
} }
...@@ -434,38 +433,41 @@ static int zynq_fpga_probe(struct platform_device *pdev) ...@@ -434,38 +433,41 @@ static int zynq_fpga_probe(struct platform_device *pdev)
priv->irq = platform_get_irq(pdev, 0); priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) { if (priv->irq < 0) {
dev_err(dev, "No IRQ available"); dev_err(dev, "No IRQ available\n");
return priv->irq; 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"); priv->clk = devm_clk_get(dev, "ref_clk");
if (IS_ERR(priv->clk)) { if (IS_ERR(priv->clk)) {
dev_err(dev, "input clock not found"); dev_err(dev, "input clock not found\n");
return PTR_ERR(priv->clk); return PTR_ERR(priv->clk);
} }
err = clk_prepare_enable(priv->clk); err = clk_prepare_enable(priv->clk);
if (err) { if (err) {
dev_err(dev, "unable to enable clock"); dev_err(dev, "unable to enable clock\n");
return err; return err;
} }
/* unlock the device */ /* unlock the device */
zynq_fpga_write(priv, UNLOCK_OFFSET, UNLOCK_MASK); zynq_fpga_write(priv, UNLOCK_OFFSET, UNLOCK_MASK);
zynq_fpga_write(priv, INT_MASK_OFFSET, 0xFFFFFFFF);
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev),
priv);
if (err) {
dev_err(dev, "unable to request IRQ\n");
clk_disable_unprepare(priv->clk);
return err;
}
clk_disable(priv->clk); clk_disable(priv->clk);
err = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager", err = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
&zynq_fpga_ops, priv); &zynq_fpga_ops, priv);
if (err) { if (err) {
dev_err(dev, "unable to register FPGA manager"); dev_err(dev, "unable to register FPGA manager\n");
clk_unprepare(priv->clk); clk_unprepare(priv->clk);
return err; return err;
} }
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* vmbus_setevent- Trigger an event notification on the specified * vmbus_setevent- Trigger an event notification on the specified
* channel. * channel.
*/ */
static void vmbus_setevent(struct vmbus_channel *channel) void vmbus_setevent(struct vmbus_channel *channel)
{ {
struct hv_monitor_page *monitorpage; struct hv_monitor_page *monitorpage;
...@@ -65,6 +65,7 @@ static void vmbus_setevent(struct vmbus_channel *channel) ...@@ -65,6 +65,7 @@ static void vmbus_setevent(struct vmbus_channel *channel)
vmbus_set_event(channel); vmbus_set_event(channel);
} }
} }
EXPORT_SYMBOL_GPL(vmbus_setevent);
/* /*
* vmbus_open - Open the specified channel. * vmbus_open - Open the specified channel.
...@@ -635,8 +636,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, ...@@ -635,8 +636,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64));
struct kvec bufferlist[3]; struct kvec bufferlist[3];
u64 aligned_data = 0; u64 aligned_data = 0;
int ret;
bool signal = false;
bool lock = channel->acquire_ring_lock; bool lock = channel->acquire_ring_lock;
int num_vecs = ((bufferlen != 0) ? 3 : 1); int num_vecs = ((bufferlen != 0) ? 3 : 1);
...@@ -656,33 +655,9 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, ...@@ -656,33 +655,9 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs, return hv_ringbuffer_write(channel, bufferlist, num_vecs,
&signal, lock, channel->signal_policy); lock, kick_q);
/*
* Signalling the host is conditional on many factors:
* 1. The ring state changed from being empty to non-empty.
* This is tracked by the variable "signal".
* 2. The variable kick_q tracks if more data will be placed
* on the ring. We will not signal if more data is
* to be placed.
*
* Based on the channel signal state, we will decide
* which signaling policy will be applied.
*
* If we cannot write to the ring-buffer; signal the host
* even if we may not have written anything. This is a rare
* enough condition that it should not matter.
* NOTE: in this case, the hvsock channel is an exception, because
* it looks the host side's hvsock implementation has a throttling
* mechanism which can hurt the performance otherwise.
*/
if (((ret == 0) && kick_q && signal) ||
(ret && !is_hvsock_channel(channel)))
vmbus_setevent(channel);
return ret;
} }
EXPORT_SYMBOL(vmbus_sendpacket_ctl); EXPORT_SYMBOL(vmbus_sendpacket_ctl);
...@@ -723,7 +698,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, ...@@ -723,7 +698,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
u32 flags, u32 flags,
bool kick_q) bool kick_q)
{ {
int ret;
int i; int i;
struct vmbus_channel_packet_page_buffer desc; struct vmbus_channel_packet_page_buffer desc;
u32 descsize; u32 descsize;
...@@ -731,7 +705,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, ...@@ -731,7 +705,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
u32 packetlen_aligned; u32 packetlen_aligned;
struct kvec bufferlist[3]; struct kvec bufferlist[3];
u64 aligned_data = 0; u64 aligned_data = 0;
bool signal = false;
bool lock = channel->acquire_ring_lock; bool lock = channel->acquire_ring_lock;
if (pagecount > MAX_PAGE_BUFFER_COUNT) if (pagecount > MAX_PAGE_BUFFER_COUNT)
...@@ -769,29 +742,8 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel, ...@@ -769,29 +742,8 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, return hv_ringbuffer_write(channel, bufferlist, 3,
&signal, lock, channel->signal_policy); lock, kick_q);
/*
* Signalling the host is conditional on many factors:
* 1. The ring state changed from being empty to non-empty.
* This is tracked by the variable "signal".
* 2. The variable kick_q tracks if more data will be placed
* on the ring. We will not signal if more data is
* to be placed.
*
* Based on the channel signal state, we will decide
* which signaling policy will be applied.
*
* If we cannot write to the ring-buffer; signal the host
* even if we may not have written anything. This is a rare
* enough condition that it should not matter.
*/
if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel);
return ret;
} }
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl); EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl);
...@@ -822,12 +774,10 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, ...@@ -822,12 +774,10 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
u32 desc_size, u32 desc_size,
void *buffer, u32 bufferlen, u64 requestid) void *buffer, u32 bufferlen, u64 requestid)
{ {
int ret;
u32 packetlen; u32 packetlen;
u32 packetlen_aligned; u32 packetlen_aligned;
struct kvec bufferlist[3]; struct kvec bufferlist[3];
u64 aligned_data = 0; u64 aligned_data = 0;
bool signal = false;
bool lock = channel->acquire_ring_lock; bool lock = channel->acquire_ring_lock;
packetlen = desc_size + bufferlen; packetlen = desc_size + bufferlen;
...@@ -848,13 +798,8 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, ...@@ -848,13 +798,8 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, return hv_ringbuffer_write(channel, bufferlist, 3,
&signal, lock, channel->signal_policy); lock, true);
if (ret == 0 && signal)
vmbus_setevent(channel);
return ret;
} }
EXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc); EXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc);
...@@ -866,14 +811,12 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, ...@@ -866,14 +811,12 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
struct hv_multipage_buffer *multi_pagebuffer, struct hv_multipage_buffer *multi_pagebuffer,
void *buffer, u32 bufferlen, u64 requestid) void *buffer, u32 bufferlen, u64 requestid)
{ {
int ret;
struct vmbus_channel_packet_multipage_buffer desc; struct vmbus_channel_packet_multipage_buffer desc;
u32 descsize; u32 descsize;
u32 packetlen; u32 packetlen;
u32 packetlen_aligned; u32 packetlen_aligned;
struct kvec bufferlist[3]; struct kvec bufferlist[3];
u64 aligned_data = 0; u64 aligned_data = 0;
bool signal = false;
bool lock = channel->acquire_ring_lock; bool lock = channel->acquire_ring_lock;
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
multi_pagebuffer->len); multi_pagebuffer->len);
...@@ -913,13 +856,8 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, ...@@ -913,13 +856,8 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen); bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, return hv_ringbuffer_write(channel, bufferlist, 3,
&signal, lock, channel->signal_policy); lock, true);
if (ret == 0 && signal)
vmbus_setevent(channel);
return ret;
} }
EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer);
...@@ -941,16 +879,9 @@ __vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, ...@@ -941,16 +879,9 @@ __vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u32 *buffer_actual_len, u64 *requestid, u32 bufferlen, u32 *buffer_actual_len, u64 *requestid,
bool raw) bool raw)
{ {
int ret; return hv_ringbuffer_read(channel, buffer, bufferlen,
bool signal = false; buffer_actual_len, requestid, raw);
ret = hv_ringbuffer_read(&channel->inbound, buffer, bufferlen,
buffer_actual_len, requestid, &signal, raw);
if (signal)
vmbus_setevent(channel);
return ret;
} }
int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
......
...@@ -134,7 +134,7 @@ static const struct vmbus_device vmbus_devs[] = { ...@@ -134,7 +134,7 @@ static const struct vmbus_device vmbus_devs[] = {
}, },
/* Unknown GUID */ /* Unknown GUID */
{ .dev_type = HV_UNKOWN, { .dev_type = HV_UNKNOWN,
.perf_device = false, .perf_device = false,
}, },
}; };
...@@ -163,9 +163,9 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) ...@@ -163,9 +163,9 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel)
u16 i; u16 i;
if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid)) if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid))
return HV_UNKOWN; return HV_UNKNOWN;
for (i = HV_IDE; i < HV_UNKOWN; i++) { for (i = HV_IDE; i < HV_UNKNOWN; i++) {
if (!uuid_le_cmp(*guid, vmbus_devs[i].guid)) if (!uuid_le_cmp(*guid, vmbus_devs[i].guid))
return i; return i;
} }
...@@ -389,6 +389,7 @@ void vmbus_free_channels(void) ...@@ -389,6 +389,7 @@ void vmbus_free_channels(void)
{ {
struct vmbus_channel *channel, *tmp; struct vmbus_channel *channel, *tmp;
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list, list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
listentry) { listentry) {
/* hv_process_channel_removal() needs this */ /* hv_process_channel_removal() needs this */
...@@ -396,6 +397,7 @@ void vmbus_free_channels(void) ...@@ -396,6 +397,7 @@ void vmbus_free_channels(void)
vmbus_device_unregister(channel->device_obj); vmbus_device_unregister(channel->device_obj);
} }
mutex_unlock(&vmbus_connection.channel_mutex);
} }
/* /*
...@@ -447,8 +449,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) ...@@ -447,8 +449,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
} }
dev_type = hv_get_dev_type(newchannel); dev_type = hv_get_dev_type(newchannel);
if (dev_type == HV_NIC)
set_channel_signal_state(newchannel, HV_SIGNAL_POLICY_EXPLICIT);
init_vp_index(newchannel, dev_type); init_vp_index(newchannel, dev_type);
......
...@@ -39,6 +39,7 @@ struct vmbus_connection vmbus_connection = { ...@@ -39,6 +39,7 @@ struct vmbus_connection vmbus_connection = {
.conn_state = DISCONNECTED, .conn_state = DISCONNECTED,
.next_gpadl_handle = ATOMIC_INIT(0xE1E10), .next_gpadl_handle = ATOMIC_INIT(0xE1E10),
}; };
EXPORT_SYMBOL_GPL(vmbus_connection);
/* /*
* Negotiated protocol version with the host. * Negotiated protocol version with the host.
......
...@@ -575,7 +575,7 @@ void hv_synic_clockevents_cleanup(void) ...@@ -575,7 +575,7 @@ void hv_synic_clockevents_cleanup(void)
if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
return; return;
for_each_online_cpu(cpu) for_each_present_cpu(cpu)
clockevents_unbind_device(hv_context.clk_evt[cpu], cpu); clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
} }
...@@ -594,8 +594,10 @@ void hv_synic_cleanup(void *arg) ...@@ -594,8 +594,10 @@ void hv_synic_cleanup(void *arg)
return; return;
/* Turn off clockevent device */ /* Turn off clockevent device */
if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) {
clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
hv_ce_shutdown(hv_context.clk_evt[cpu]); hv_ce_shutdown(hv_context.clk_evt[cpu]);
}
rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
......
...@@ -564,6 +564,11 @@ struct hv_dynmem_device { ...@@ -564,6 +564,11 @@ struct hv_dynmem_device {
* next version to try. * next version to try.
*/ */
__u32 next_version; __u32 next_version;
/*
* The negotiated version agreed by host.
*/
__u32 version;
}; };
static struct hv_dynmem_device dm_device; static struct hv_dynmem_device dm_device;
...@@ -645,6 +650,7 @@ static void hv_bring_pgs_online(struct hv_hotadd_state *has, ...@@ -645,6 +650,7 @@ static void hv_bring_pgs_online(struct hv_hotadd_state *has,
{ {
int i; int i;
pr_debug("Online %lu pages starting at pfn 0x%lx\n", size, start_pfn);
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
hv_page_online_one(has, pfn_to_page(start_pfn + i)); hv_page_online_one(has, pfn_to_page(start_pfn + i));
} }
...@@ -685,7 +691,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, ...@@ -685,7 +691,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
(HA_CHUNK << PAGE_SHIFT)); (HA_CHUNK << PAGE_SHIFT));
if (ret) { if (ret) {
pr_info("hot_add memory failed error is %d\n", ret); pr_warn("hot_add memory failed error is %d\n", ret);
if (ret == -EEXIST) { if (ret == -EEXIST) {
/* /*
* This error indicates that the error * This error indicates that the error
...@@ -814,6 +820,9 @@ static unsigned long handle_pg_range(unsigned long pg_start, ...@@ -814,6 +820,9 @@ static unsigned long handle_pg_range(unsigned long pg_start,
unsigned long old_covered_state; unsigned long old_covered_state;
unsigned long res = 0, flags; unsigned long res = 0, flags;
pr_debug("Hot adding %lu pages starting at pfn 0x%lx.\n", pg_count,
pg_start);
spin_lock_irqsave(&dm_device.ha_lock, flags); spin_lock_irqsave(&dm_device.ha_lock, flags);
list_for_each_entry(has, &dm_device.ha_region_list, list) { list_for_each_entry(has, &dm_device.ha_region_list, list) {
/* /*
...@@ -1025,8 +1034,13 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) ...@@ -1025,8 +1034,13 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
switch (info_hdr->type) { switch (info_hdr->type) {
case INFO_TYPE_MAX_PAGE_CNT: case INFO_TYPE_MAX_PAGE_CNT:
pr_info("Received INFO_TYPE_MAX_PAGE_CNT\n"); if (info_hdr->data_size == sizeof(__u64)) {
pr_info("Data Size is %d\n", info_hdr->data_size); __u64 *max_page_count = (__u64 *)&info_hdr[1];
pr_info("INFO_TYPE_MAX_PAGE_CNT = %llu\n",
*max_page_count);
}
break; break;
default: default:
pr_info("Received Unknown type: %d\n", info_hdr->type); pr_info("Received Unknown type: %d\n", info_hdr->type);
...@@ -1196,8 +1210,6 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm, ...@@ -1196,8 +1210,6 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm,
return num_pages; return num_pages;
} }
static void balloon_up(struct work_struct *dummy) static void balloon_up(struct work_struct *dummy)
{ {
unsigned int num_pages = dm_device.balloon_wrk.num_pages; unsigned int num_pages = dm_device.balloon_wrk.num_pages;
...@@ -1224,6 +1236,10 @@ static void balloon_up(struct work_struct *dummy) ...@@ -1224,6 +1236,10 @@ static void balloon_up(struct work_struct *dummy)
/* Refuse to balloon below the floor, keep the 2M granularity. */ /* Refuse to balloon below the floor, keep the 2M granularity. */
if (avail_pages < num_pages || avail_pages - num_pages < floor) { if (avail_pages < num_pages || avail_pages - num_pages < floor) {
pr_warn("Balloon request will be partially fulfilled. %s\n",
avail_pages < num_pages ? "Not enough memory." :
"Balloon floor reached.");
num_pages = avail_pages > floor ? (avail_pages - floor) : 0; num_pages = avail_pages > floor ? (avail_pages - floor) : 0;
num_pages -= num_pages % PAGES_IN_2M; num_pages -= num_pages % PAGES_IN_2M;
} }
...@@ -1245,6 +1261,9 @@ static void balloon_up(struct work_struct *dummy) ...@@ -1245,6 +1261,9 @@ static void balloon_up(struct work_struct *dummy)
} }
if (num_ballooned == 0 || num_ballooned == num_pages) { if (num_ballooned == 0 || num_ballooned == num_pages) {
pr_debug("Ballooned %u out of %u requested pages.\n",
num_pages, dm_device.balloon_wrk.num_pages);
bl_resp->more_pages = 0; bl_resp->more_pages = 0;
done = true; done = true;
dm_device.state = DM_INITIALIZED; dm_device.state = DM_INITIALIZED;
...@@ -1292,12 +1311,16 @@ static void balloon_down(struct hv_dynmem_device *dm, ...@@ -1292,12 +1311,16 @@ static void balloon_down(struct hv_dynmem_device *dm,
int range_count = req->range_count; int range_count = req->range_count;
struct dm_unballoon_response resp; struct dm_unballoon_response resp;
int i; int i;
unsigned int prev_pages_ballooned = dm->num_pages_ballooned;
for (i = 0; i < range_count; i++) { for (i = 0; i < range_count; i++) {
free_balloon_pages(dm, &range_array[i]); free_balloon_pages(dm, &range_array[i]);
complete(&dm_device.config_event); complete(&dm_device.config_event);
} }
pr_debug("Freed %u ballooned pages.\n",
prev_pages_ballooned - dm->num_pages_ballooned);
if (req->more_pages == 1) if (req->more_pages == 1)
return; return;
...@@ -1365,6 +1388,7 @@ static void version_resp(struct hv_dynmem_device *dm, ...@@ -1365,6 +1388,7 @@ static void version_resp(struct hv_dynmem_device *dm,
version_req.hdr.size = sizeof(struct dm_version_request); version_req.hdr.size = sizeof(struct dm_version_request);
version_req.hdr.trans_id = atomic_inc_return(&trans_id); version_req.hdr.trans_id = atomic_inc_return(&trans_id);
version_req.version.version = dm->next_version; version_req.version.version = dm->next_version;
dm->version = version_req.version.version;
/* /*
* Set the next version to try in case current version fails. * Set the next version to try in case current version fails.
...@@ -1501,7 +1525,11 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1501,7 +1525,11 @@ static int balloon_probe(struct hv_device *dev,
struct dm_version_request version_req; struct dm_version_request version_req;
struct dm_capabilities cap_msg; struct dm_capabilities cap_msg;
#ifdef CONFIG_MEMORY_HOTPLUG
do_hot_add = hot_add; do_hot_add = hot_add;
#else
do_hot_add = false;
#endif
/* /*
* First allocate a send buffer. * First allocate a send buffer.
...@@ -1553,6 +1581,7 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1553,6 +1581,7 @@ static int balloon_probe(struct hv_device *dev,
version_req.hdr.trans_id = atomic_inc_return(&trans_id); version_req.hdr.trans_id = atomic_inc_return(&trans_id);
version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN10; version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN10;
version_req.is_last_attempt = 0; version_req.is_last_attempt = 0;
dm_device.version = version_req.version.version;
ret = vmbus_sendpacket(dev->channel, &version_req, ret = vmbus_sendpacket(dev->channel, &version_req,
sizeof(struct dm_version_request), sizeof(struct dm_version_request),
...@@ -1575,6 +1604,11 @@ static int balloon_probe(struct hv_device *dev, ...@@ -1575,6 +1604,11 @@ static int balloon_probe(struct hv_device *dev,
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto probe_error2; goto probe_error2;
} }
pr_info("Using Dynamic Memory protocol version %u.%u\n",
DYNMEM_MAJOR_VERSION(dm_device.version),
DYNMEM_MINOR_VERSION(dm_device.version));
/* /*
* Now submit our capabilities to the host. * Now submit our capabilities to the host.
*/ */
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -111,7 +111,9 @@ static inline void CS_UNLOCK(void __iomem *addr) ...@@ -111,7 +111,9 @@ static inline void CS_UNLOCK(void __iomem *addr)
void coresight_disable_path(struct list_head *path); void coresight_disable_path(struct list_head *path);
int coresight_enable_path(struct list_head *path, u32 mode); int coresight_enable_path(struct list_head *path, u32 mode);
struct coresight_device *coresight_get_sink(struct list_head *path); struct coresight_device *coresight_get_sink(struct list_head *path);
struct list_head *coresight_build_path(struct coresight_device *csdev); struct coresight_device *coresight_get_enabled_sink(bool reset);
struct list_head *coresight_build_path(struct coresight_device *csdev,
struct coresight_device *sink);
void coresight_release_path(struct list_head *path); void coresight_release_path(struct list_head *path);
#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
......
...@@ -117,7 +117,7 @@ struct tmc_drvdata { ...@@ -117,7 +117,7 @@ struct tmc_drvdata {
void __iomem *vaddr; void __iomem *vaddr;
u32 size; u32 size;
u32 len; u32 len;
local_t mode; u32 mode;
enum tmc_config_type config_type; enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth; enum tmc_mem_intf_width memwidth;
u32 trigger_cntr; u32 trigger_cntr;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册