Char/Misc driver patches for 4.18-rc1
Here is the "big" char and misc driver patches for 4.18-rc1. It's not a lot of stuff here, but there are some highlights: - coreboot driver updates - soundwire driver updates - android binder updates - fpga big sync, mostly documentation - lots of minor driver updates All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWxbXfQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymwCACdFbUy2kWwrpZWSfSBpawfrs75lLMAmwVOe+62 9aDsDWzDVUEFxF20qiE6 =CMJ3 -----END PGP SIGNATURE----- Merge tag 'char-misc-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver updates from Greg KH: "Here is the "big" char and misc driver patches for 4.18-rc1. It's not a lot of stuff here, but there are some highlights: - coreboot driver updates - soundwire driver updates - android binder updates - fpga big sync, mostly documentation - lots of minor driver updates All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (81 commits) vmw_balloon: fixing double free when batching mode is off MAINTAINERS: Add driver-api/fpga path fpga: clarify that unregister functions also free documentation: fpga: move fpga-region.txt to driver-api documentation: fpga: add bridge document to driver-api documentation: fpga: move fpga-mgr.txt to driver-api Documentation: fpga: move fpga overview to driver-api fpga: region: kernel-doc fixes fpga: bridge: kernel-doc fixes fpga: mgr: kernel-doc fixes fpga: use SPDX fpga: region: change api, add fpga_region_create/free fpga: bridge: change api, don't use drvdata fpga: manager: change api, don't use drvdata fpga: region: don't use drvdata in common fpga code Drivers: hv: vmbus: Removed an unnecessary cast from void * ver_linux: Drop redundant calls to system() to test if file is readable ver_linux: Move stderr redirection from function parameter to function body misc: IBM Virtual Management Channel Driver (VMC) rpmsg: Correct support for MODULE_DEVICE_TABLE() ...
This commit is contained in:
commit
abf7dba7c4
@ -1,25 +1,25 @@
|
|||||||
What: /sys/bus/vmbus/devices/vmbus_*/id
|
What: /sys/bus/vmbus/devices/<UUID>/id
|
||||||
Date: Jul 2009
|
Date: Jul 2009
|
||||||
KernelVersion: 2.6.31
|
KernelVersion: 2.6.31
|
||||||
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
||||||
Description: The VMBus child_relid of the device's primary channel
|
Description: The VMBus child_relid of the device's primary channel
|
||||||
Users: tools/hv/lsvmbus
|
Users: tools/hv/lsvmbus
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/class_id
|
What: /sys/bus/vmbus/devices/<UUID>/class_id
|
||||||
Date: Jul 2009
|
Date: Jul 2009
|
||||||
KernelVersion: 2.6.31
|
KernelVersion: 2.6.31
|
||||||
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
||||||
Description: The VMBus interface type GUID of the device
|
Description: The VMBus interface type GUID of the device
|
||||||
Users: tools/hv/lsvmbus
|
Users: tools/hv/lsvmbus
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/device_id
|
What: /sys/bus/vmbus/devices/<UUID>/device_id
|
||||||
Date: Jul 2009
|
Date: Jul 2009
|
||||||
KernelVersion: 2.6.31
|
KernelVersion: 2.6.31
|
||||||
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
||||||
Description: The VMBus interface instance GUID of the device
|
Description: The VMBus interface instance GUID of the device
|
||||||
Users: tools/hv/lsvmbus
|
Users: tools/hv/lsvmbus
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channel_vp_mapping
|
What: /sys/bus/vmbus/devices/<UUID>/channel_vp_mapping
|
||||||
Date: Jul 2015
|
Date: Jul 2015
|
||||||
KernelVersion: 4.2.0
|
KernelVersion: 4.2.0
|
||||||
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
||||||
@ -28,112 +28,112 @@ Description: The mapping of which primary/sub channels are bound to which
|
|||||||
Format: <channel's child_relid:the bound cpu's number>
|
Format: <channel's child_relid:the bound cpu's number>
|
||||||
Users: tools/hv/lsvmbus
|
Users: tools/hv/lsvmbus
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/device
|
What: /sys/bus/vmbus/devices/<UUID>/device
|
||||||
Date: Dec. 2015
|
Date: Dec. 2015
|
||||||
KernelVersion: 4.5
|
KernelVersion: 4.5
|
||||||
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
||||||
Description: The 16 bit device ID of the device
|
Description: The 16 bit device ID of the device
|
||||||
Users: tools/hv/lsvmbus and user level RDMA libraries
|
Users: tools/hv/lsvmbus and user level RDMA libraries
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/vendor
|
What: /sys/bus/vmbus/devices/<UUID>/vendor
|
||||||
Date: Dec. 2015
|
Date: Dec. 2015
|
||||||
KernelVersion: 4.5
|
KernelVersion: 4.5
|
||||||
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
Contact: K. Y. Srinivasan <kys@microsoft.com>
|
||||||
Description: The 16 bit vendor ID of the device
|
Description: The 16 bit vendor ID of the device
|
||||||
Users: tools/hv/lsvmbus and user level RDMA libraries
|
Users: tools/hv/lsvmbus and user level RDMA libraries
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Directory for per-channel information
|
Description: Directory for per-channel information
|
||||||
NN is the VMBUS relid associtated with the channel.
|
NN is the VMBUS relid associtated with the channel.
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/cpu
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/cpu
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: VCPU (sub)channel is affinitized to
|
Description: VCPU (sub)channel is affinitized to
|
||||||
Users: tools/hv/lsvmbus and other debugging tools
|
Users: tools/hv/lsvmbus and other debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/cpu
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/cpu
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: VCPU (sub)channel is affinitized to
|
Description: VCPU (sub)channel is affinitized to
|
||||||
Users: tools/hv/lsvmbus and other debugging tools
|
Users: tools/hv/lsvmbus and other debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/in_mask
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/in_mask
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Host to guest channel interrupt mask
|
Description: Host to guest channel interrupt mask
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/latency
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/latency
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Channel signaling latency
|
Description: Channel signaling latency
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/out_mask
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/out_mask
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Guest to host channel interrupt mask
|
Description: Guest to host channel interrupt mask
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/pending
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/pending
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Channel interrupt pending state
|
Description: Channel interrupt pending state
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/read_avail
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/read_avail
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Bytes available to read
|
Description: Bytes available to read
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/write_avail
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/write_avail
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Bytes available to write
|
Description: Bytes available to write
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/events
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/events
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Number of times we have signaled the host
|
Description: Number of times we have signaled the host
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/interrupts
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/interrupts
|
||||||
Date: September. 2017
|
Date: September. 2017
|
||||||
KernelVersion: 4.14
|
KernelVersion: 4.14
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Number of times we have taken an interrupt (incoming)
|
Description: Number of times we have taken an interrupt (incoming)
|
||||||
Users: Debugging tools
|
Users: Debugging tools
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/subchannel_id
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/subchannel_id
|
||||||
Date: January. 2018
|
Date: January. 2018
|
||||||
KernelVersion: 4.16
|
KernelVersion: 4.16
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Subchannel ID associated with VMBUS channel
|
Description: Subchannel ID associated with VMBUS channel
|
||||||
Users: Debugging tools and userspace drivers
|
Users: Debugging tools and userspace drivers
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/monitor_id
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/monitor_id
|
||||||
Date: January. 2018
|
Date: January. 2018
|
||||||
KernelVersion: 4.16
|
KernelVersion: 4.16
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
Description: Monitor bit associated with channel
|
Description: Monitor bit associated with channel
|
||||||
Users: Debugging tools and userspace drivers
|
Users: Debugging tools and userspace drivers
|
||||||
|
|
||||||
What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/ring
|
What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/ring
|
||||||
Date: January. 2018
|
Date: January. 2018
|
||||||
KernelVersion: 4.16
|
KernelVersion: 4.16
|
||||||
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
Contact: Stephen Hemminger <sthemmin@microsoft.com>
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
Lattice MachXO2 Slave SPI FPGA Manager
|
||||||
|
|
||||||
|
Lattice MachXO2 FPGAs support a method of loading the bitstream over
|
||||||
|
'slave SPI' interface.
|
||||||
|
|
||||||
|
See 'MachXO2ProgrammingandConfigurationUsageGuide.pdf' on www.latticesemi.com
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should contain "lattice,machxo2-slave-spi"
|
||||||
|
- reg: spi chip select of the FPGA
|
||||||
|
|
||||||
|
Example for full FPGA configuration:
|
||||||
|
|
||||||
|
fpga-region0 {
|
||||||
|
compatible = "fpga-region";
|
||||||
|
fpga-mgr = <&fpga_mgr_spi>;
|
||||||
|
#address-cells = <0x1>;
|
||||||
|
#size-cells = <0x1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi1: spi@2000 {
|
||||||
|
...
|
||||||
|
|
||||||
|
fpga_mgr_spi: fpga-mgr@0 {
|
||||||
|
compatible = "lattice,machxo2-slave-spi";
|
||||||
|
spi-max-frequency = <8000000>;
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,40 @@
|
|||||||
|
Zodiac Inflight Innovations RAVE EEPROM Bindings
|
||||||
|
|
||||||
|
RAVE SP EEPROM device is a "MFD cell" device exposing physical EEPROM
|
||||||
|
attached to RAVE Supervisory Processor. It is expected that its Device
|
||||||
|
Tree node is specified as a child of the node corresponding to the
|
||||||
|
parent RAVE SP device (as documented in
|
||||||
|
Documentation/devicetree/bindings/mfd/zii,rave-sp.txt)
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible: Should be "zii,rave-sp-eeprom"
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- zii,eeprom-name: Unique EEPROM identifier describing its function in the
|
||||||
|
system. Will be used as created NVMEM deivce's name.
|
||||||
|
|
||||||
|
Data cells:
|
||||||
|
|
||||||
|
Data cells are child nodes of eerpom node, bindings for which are
|
||||||
|
documented in Documentation/bindings/nvmem/nvmem.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
rave-sp {
|
||||||
|
compatible = "zii,rave-sp-rdu1";
|
||||||
|
current-speed = <38400>;
|
||||||
|
|
||||||
|
eeprom@a4 {
|
||||||
|
compatible = "zii,rave-sp-eeprom";
|
||||||
|
reg = <0xa4 0x4000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
zii,eeprom-name = "main-eeprom";
|
||||||
|
|
||||||
|
wdt_timeout: wdt-timeout@81 {
|
||||||
|
reg = <0x81 2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
49
Documentation/driver-api/fpga/fpga-bridge.rst
Normal file
49
Documentation/driver-api/fpga/fpga-bridge.rst
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
FPGA Bridge
|
||||||
|
===========
|
||||||
|
|
||||||
|
API to implement a new FPGA bridge
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-bridge.h
|
||||||
|
:functions: fpga_bridge
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-bridge.h
|
||||||
|
:functions: fpga_bridge_ops
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_create
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_free
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_register
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_unregister
|
||||||
|
|
||||||
|
API to control an FPGA bridge
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You probably won't need these directly. FPGA regions should handle this.
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: of_fpga_bridge_get
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_get
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_put
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_get_to_list
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: of_fpga_bridge_get_to_list
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_enable
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||||
|
:functions: fpga_bridge_disable
|
220
Documentation/driver-api/fpga/fpga-mgr.rst
Normal file
220
Documentation/driver-api/fpga/fpga-mgr.rst
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
FPGA Manager
|
||||||
|
============
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
The FPGA manager core exports a set of functions for programming an FPGA with
|
||||||
|
an image. The API is manufacturer agnostic. All manufacturer specifics are
|
||||||
|
hidden away in a low level driver which registers a set of ops with the core.
|
||||||
|
The FPGA image data itself is very manufacturer specific, but for our purposes
|
||||||
|
it's just binary data. The FPGA manager core won't parse it.
|
||||||
|
|
||||||
|
The FPGA image to be programmed can be in a scatter gather list, a single
|
||||||
|
contiguous buffer, or a firmware file. Because allocating contiguous kernel
|
||||||
|
memory for the buffer should be avoided, users are encouraged to use a scatter
|
||||||
|
gather list instead if possible.
|
||||||
|
|
||||||
|
The particulars for programming the image are presented in a structure (struct
|
||||||
|
fpga_image_info). This struct contains parameters such as pointers to the
|
||||||
|
FPGA image as well as image-specific particulars such as whether the image was
|
||||||
|
built for full or partial reconfiguration.
|
||||||
|
|
||||||
|
How to support a new FPGA device
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
To add another FPGA manager, write a driver that implements a set of ops. The
|
||||||
|
probe function calls fpga_mgr_register(), such as::
|
||||||
|
|
||||||
|
static const struct fpga_manager_ops socfpga_fpga_ops = {
|
||||||
|
.write_init = socfpga_fpga_ops_configure_init,
|
||||||
|
.write = socfpga_fpga_ops_configure_write,
|
||||||
|
.write_complete = socfpga_fpga_ops_configure_complete,
|
||||||
|
.state = socfpga_fpga_ops_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int socfpga_fpga_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct socfpga_fpga_priv *priv;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* do ioremaps, get interrupts, etc. and save
|
||||||
|
* them in priv
|
||||||
|
*/
|
||||||
|
|
||||||
|
mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
|
||||||
|
&socfpga_fpga_ops, priv);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int socfpga_fpga_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct fpga_manager *mgr = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
The ops will implement whatever device specific register writes are needed to
|
||||||
|
do the programming sequence for this particular FPGA. These ops return 0 for
|
||||||
|
success or negative error codes otherwise.
|
||||||
|
|
||||||
|
The programming sequence is::
|
||||||
|
1. .write_init
|
||||||
|
2. .write or .write_sg (may be called once or multiple times)
|
||||||
|
3. .write_complete
|
||||||
|
|
||||||
|
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
|
||||||
|
whole FPGA image or may be a smaller chunk of an FPGA image. In the latter
|
||||||
|
case, this function is called multiple times for successive chunks. This interface
|
||||||
|
is suitable for drivers which use PIO.
|
||||||
|
|
||||||
|
The .write_sg version behaves the same as .write except the input is a sg_table
|
||||||
|
scatter list. This interface is suitable for drivers which use DMA.
|
||||||
|
|
||||||
|
The .write_complete function is called after all the image has been written
|
||||||
|
to put the FPGA into operating mode.
|
||||||
|
|
||||||
|
The ops include a .state function which will read the hardware FPGA manager and
|
||||||
|
return a code of type enum fpga_mgr_states. It doesn't result in a change in
|
||||||
|
hardware state.
|
||||||
|
|
||||||
|
How to write an image buffer to a supported FPGA
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Some sample code::
|
||||||
|
|
||||||
|
#include <linux/fpga/fpga-mgr.h>
|
||||||
|
|
||||||
|
struct fpga_manager *mgr;
|
||||||
|
struct fpga_image_info *info;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a reference to FPGA manager. The manager is not locked, so you can
|
||||||
|
* hold onto this reference without it preventing programming.
|
||||||
|
*
|
||||||
|
* This example uses the device node of the manager. Alternatively, use
|
||||||
|
* fpga_mgr_get(dev) instead if you have the device.
|
||||||
|
*/
|
||||||
|
mgr = of_fpga_mgr_get(mgr_node);
|
||||||
|
|
||||||
|
/* struct with information about the FPGA image to program. */
|
||||||
|
info = fpga_image_info_alloc(dev);
|
||||||
|
|
||||||
|
/* flags indicates whether to do full or partial reconfiguration */
|
||||||
|
info->flags = FPGA_MGR_PARTIAL_RECONFIG;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, indicate where the image is. This is pseudo-code; you're
|
||||||
|
* going to use one of these three.
|
||||||
|
*/
|
||||||
|
if (image is in a scatter gather table) {
|
||||||
|
|
||||||
|
info->sgt = [your scatter gather table]
|
||||||
|
|
||||||
|
} else if (image is in a buffer) {
|
||||||
|
|
||||||
|
info->buf = [your image buffer]
|
||||||
|
info->count = [image buffer size]
|
||||||
|
|
||||||
|
} else if (image is in a firmware file) {
|
||||||
|
|
||||||
|
info->firmware_name = devm_kstrdup(dev, firmware_name, GFP_KERNEL);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get exclusive control of FPGA manager */
|
||||||
|
ret = fpga_mgr_lock(mgr);
|
||||||
|
|
||||||
|
/* Load the buffer to the FPGA */
|
||||||
|
ret = fpga_mgr_buf_load(mgr, &info, buf, count);
|
||||||
|
|
||||||
|
/* Release the FPGA manager */
|
||||||
|
fpga_mgr_unlock(mgr);
|
||||||
|
fpga_mgr_put(mgr);
|
||||||
|
|
||||||
|
/* Deallocate the image info if you're done with it */
|
||||||
|
fpga_image_info_free(info);
|
||||||
|
|
||||||
|
API for implementing a new FPGA Manager driver
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
|
||||||
|
:functions: fpga_manager
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
|
||||||
|
:functions: fpga_manager_ops
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_create
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_free
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_register
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_unregister
|
||||||
|
|
||||||
|
API for programming a FPGA
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
|
||||||
|
:functions: fpga_image_info
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
|
||||||
|
:functions: fpga_mgr_states
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_image_info_alloc
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_image_info_free
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: of_fpga_mgr_get
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_get
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_put
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_lock
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_unlock
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
|
||||||
|
:functions: fpga_mgr_states
|
||||||
|
|
||||||
|
Note - use :c:func:`fpga_region_program_fpga()` instead of :c:func:`fpga_mgr_load()`
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||||
|
:functions: fpga_mgr_load
|
102
Documentation/driver-api/fpga/fpga-region.rst
Normal file
102
Documentation/driver-api/fpga/fpga-region.rst
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
FPGA Region
|
||||||
|
===========
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
This document is meant to be an brief overview of the FPGA region API usage. A
|
||||||
|
more conceptual look at regions can be found in the Device Tree binding
|
||||||
|
document [#f1]_.
|
||||||
|
|
||||||
|
For the purposes of this API document, let's just say that a region associates
|
||||||
|
an FPGA Manager and a bridge (or bridges) with a reprogrammable region of an
|
||||||
|
FPGA or the whole FPGA. The API provides a way to register a region and to
|
||||||
|
program a region.
|
||||||
|
|
||||||
|
Currently the only layer above fpga-region.c in the kernel is the Device Tree
|
||||||
|
support (of-fpga-region.c) described in [#f1]_. The DT support layer uses regions
|
||||||
|
to program the FPGA and then DT to handle enumeration. The common region code
|
||||||
|
is intended to be used by other schemes that have other ways of accomplishing
|
||||||
|
enumeration after programming.
|
||||||
|
|
||||||
|
An fpga-region can be set up to know the following things:
|
||||||
|
|
||||||
|
* which FPGA manager to use to do the programming
|
||||||
|
|
||||||
|
* which bridges to disable before programming and enable afterwards.
|
||||||
|
|
||||||
|
Additional info needed to program the FPGA image is passed in the struct
|
||||||
|
fpga_image_info including:
|
||||||
|
|
||||||
|
* pointers to the image as either a scatter-gather buffer, a contiguous
|
||||||
|
buffer, or the name of firmware file
|
||||||
|
|
||||||
|
* flags indicating specifics such as whether the image if for partial
|
||||||
|
reconfiguration.
|
||||||
|
|
||||||
|
How to program a FPGA using a region
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
First, allocate the info struct::
|
||||||
|
|
||||||
|
info = fpga_image_info_alloc(dev);
|
||||||
|
if (!info)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
Set flags as needed, i.e.::
|
||||||
|
|
||||||
|
info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
|
||||||
|
|
||||||
|
Point to your FPGA image, such as::
|
||||||
|
|
||||||
|
info->sgt = &sgt;
|
||||||
|
|
||||||
|
Add info to region and do the programming::
|
||||||
|
|
||||||
|
region->info = info;
|
||||||
|
ret = fpga_region_program_fpga(region);
|
||||||
|
|
||||||
|
:c:func:`fpga_region_program_fpga()` operates on info passed in the
|
||||||
|
fpga_image_info (region->info). This function will attempt to:
|
||||||
|
|
||||||
|
* lock the region's mutex
|
||||||
|
* lock the region's FPGA manager
|
||||||
|
* build a list of FPGA bridges if a method has been specified to do so
|
||||||
|
* disable the bridges
|
||||||
|
* program the FPGA
|
||||||
|
* re-enable the bridges
|
||||||
|
* release the locks
|
||||||
|
|
||||||
|
Then you will want to enumerate whatever hardware has appeared in the FPGA.
|
||||||
|
|
||||||
|
How to add a new FPGA region
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
An example of usage can be seen in the probe function of [#f2]_.
|
||||||
|
|
||||||
|
.. [#f1] ../devicetree/bindings/fpga/fpga-region.txt
|
||||||
|
.. [#f2] ../../drivers/fpga/of-fpga-region.c
|
||||||
|
|
||||||
|
API to program a FGPA
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||||
|
:functions: fpga_region_program_fpga
|
||||||
|
|
||||||
|
API to add a new FPGA region
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/fpga/fpga-region.h
|
||||||
|
:functions: fpga_region
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||||
|
:functions: fpga_region_create
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||||
|
:functions: fpga_region_free
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||||
|
:functions: fpga_region_register
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||||
|
:functions: fpga_region_unregister
|
13
Documentation/driver-api/fpga/index.rst
Normal file
13
Documentation/driver-api/fpga/index.rst
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
==============
|
||||||
|
FPGA Subsystem
|
||||||
|
==============
|
||||||
|
|
||||||
|
:Author: Alan Tull
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
intro
|
||||||
|
fpga-mgr
|
||||||
|
fpga-bridge
|
||||||
|
fpga-region
|
54
Documentation/driver-api/fpga/intro.rst
Normal file
54
Documentation/driver-api/fpga/intro.rst
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
The FPGA subsystem supports reprogramming FPGAs dynamically under
|
||||||
|
Linux. Some of the core intentions of the FPGA subsystems are:
|
||||||
|
|
||||||
|
* The FPGA subsystem is vendor agnostic.
|
||||||
|
|
||||||
|
* The FPGA subsystem separates upper layers (userspace interfaces and
|
||||||
|
enumeration) from lower layers that know how to program a specific
|
||||||
|
FPGA.
|
||||||
|
|
||||||
|
* Code should not be shared between upper and lower layers. This
|
||||||
|
should go without saying. If that seems necessary, there's probably
|
||||||
|
framework functionality that that can be added that will benefit
|
||||||
|
other users. Write the linux-fpga mailing list and maintainers and
|
||||||
|
seek out a solution that expands the framework for broad reuse.
|
||||||
|
|
||||||
|
* Generally, when adding code, think of the future. Plan for re-use.
|
||||||
|
|
||||||
|
The framework in the kernel is divided into:
|
||||||
|
|
||||||
|
FPGA Manager
|
||||||
|
------------
|
||||||
|
|
||||||
|
If you are adding a new FPGA or a new method of programming a FPGA,
|
||||||
|
this is the subsystem for you. Low level FPGA manager drivers contain
|
||||||
|
the knowledge of how to program a specific device. This subsystem
|
||||||
|
includes the framework in fpga-mgr.c and the low level drivers that
|
||||||
|
are registered with it.
|
||||||
|
|
||||||
|
FPGA Bridge
|
||||||
|
-----------
|
||||||
|
|
||||||
|
FPGA Bridges prevent spurious signals from going out of a FPGA or a
|
||||||
|
region of a FPGA during programming. They are disabled before
|
||||||
|
programming begins and re-enabled afterwards. An FPGA bridge may be
|
||||||
|
actual hard hardware that gates a bus to a cpu or a soft ("freeze")
|
||||||
|
bridge in FPGA fabric that surrounds a partial reconfiguration region
|
||||||
|
of an FPGA. This subsystem includes fpga-bridge.c and the low level
|
||||||
|
drivers that are registered with it.
|
||||||
|
|
||||||
|
FPGA Region
|
||||||
|
-----------
|
||||||
|
|
||||||
|
If you are adding a new interface to the FPGA framework, add it on top
|
||||||
|
of a FPGA region to allow the most reuse of your interface.
|
||||||
|
|
||||||
|
The FPGA Region framework (fpga-region.c) associates managers and
|
||||||
|
bridges as reconfigurable regions. A region may refer to the whole
|
||||||
|
FPGA in full reconfiguration or to a partial reconfiguration region.
|
||||||
|
|
||||||
|
The Device Tree FPGA Region support (of-fpga-region.c) handles
|
||||||
|
reprogramming FPGAs when device tree overlays are applied.
|
@ -51,6 +51,7 @@ available subsections can be seen below.
|
|||||||
dmaengine/index
|
dmaengine/index
|
||||||
slimbus
|
slimbus
|
||||||
soundwire/index
|
soundwire/index
|
||||||
|
fpga/index
|
||||||
|
|
||||||
.. only:: subproject and html
|
.. only:: subproject and html
|
||||||
|
|
||||||
|
65
Documentation/driver-api/soundwire/error_handling.rst
Normal file
65
Documentation/driver-api/soundwire/error_handling.rst
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
========================
|
||||||
|
SoundWire Error Handling
|
||||||
|
========================
|
||||||
|
|
||||||
|
The SoundWire PHY was designed with care and errors on the bus are going to
|
||||||
|
be very unlikely, and if they happen it should be limited to single bit
|
||||||
|
errors. Examples of this design can be found in the synchronization
|
||||||
|
mechanism (sync loss after two errors) and short CRCs used for the Bulk
|
||||||
|
Register Access.
|
||||||
|
|
||||||
|
The errors can be detected with multiple mechanisms:
|
||||||
|
|
||||||
|
1. Bus clash or parity errors: This mechanism relies on low-level detectors
|
||||||
|
that are independent of the payload and usages, and they cover both control
|
||||||
|
and audio data. The current implementation only logs such errors.
|
||||||
|
Improvements could be invalidating an entire programming sequence and
|
||||||
|
restarting from a known position. In the case of such errors outside of a
|
||||||
|
control/command sequence, there is no concealment or recovery for audio
|
||||||
|
data enabled by the SoundWire protocol, the location of the error will also
|
||||||
|
impact its audibility (most-significant bits will be more impacted in PCM),
|
||||||
|
and after a number of such errors are detected the bus might be reset. Note
|
||||||
|
that bus clashes due to programming errors (two streams using the same bit
|
||||||
|
slots) or electrical issues during the transmit/receive transition cannot
|
||||||
|
be distinguished, although a recurring bus clash when audio is enabled is a
|
||||||
|
indication of a bus allocation issue. The interrupt mechanism can also help
|
||||||
|
identify Slaves which detected a Bus Clash or a Parity Error, but they may
|
||||||
|
not be responsible for the errors so resetting them individually is not a
|
||||||
|
viable recovery strategy.
|
||||||
|
|
||||||
|
2. Command status: Each command is associated with a status, which only
|
||||||
|
covers transmission of the data between devices. The ACK status indicates
|
||||||
|
that the command was received and will be executed by the end of the
|
||||||
|
current frame. A NAK indicates that the command was in error and will not
|
||||||
|
be applied. In case of a bad programming (command sent to non-existent
|
||||||
|
Slave or to a non-implemented register) or electrical issue, no response
|
||||||
|
signals the command was ignored. Some Master implementations allow for a
|
||||||
|
command to be retransmitted several times. If the retransmission fails,
|
||||||
|
backtracking and restarting the entire programming sequence might be a
|
||||||
|
solution. Alternatively some implementations might directly issue a bus
|
||||||
|
reset and re-enumerate all devices.
|
||||||
|
|
||||||
|
3. Timeouts: In a number of cases such as ChannelPrepare or
|
||||||
|
ClockStopPrepare, the bus driver is supposed to poll a register field until
|
||||||
|
it transitions to a NotFinished value of zero. The MIPI SoundWire spec 1.1
|
||||||
|
does not define timeouts but the MIPI SoundWire DisCo document adds
|
||||||
|
recommendation on timeouts. If such configurations do not complete, the
|
||||||
|
driver will return a -ETIMEOUT. Such timeouts are symptoms of a faulty
|
||||||
|
Slave device and are likely impossible to recover from.
|
||||||
|
|
||||||
|
Errors during global reconfiguration sequences are extremely difficult to
|
||||||
|
handle:
|
||||||
|
|
||||||
|
1. BankSwitch: An error during the last command issuing a BankSwitch is
|
||||||
|
difficult to backtrack from. Retransmitting the Bank Switch command may be
|
||||||
|
possible in a single segment setup, but this can lead to synchronization
|
||||||
|
problems when enabling multiple bus segments (a command with side effects
|
||||||
|
such as frame reconfiguration would be handled at different times). A global
|
||||||
|
hard-reset might be the best solution.
|
||||||
|
|
||||||
|
Note that SoundWire does not provide a mechanism to detect illegal values
|
||||||
|
written in valid registers. In a number of cases the standard even mentions
|
||||||
|
that the Slave might behave in implementation-defined ways. The bus
|
||||||
|
implementation does not provide a recovery mechanism for such errors, Slave
|
||||||
|
or Master driver implementers are responsible for writing valid values in
|
||||||
|
valid registers and implement additional range checking if needed.
|
@ -6,6 +6,9 @@ SoundWire Documentation
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
summary
|
summary
|
||||||
|
stream
|
||||||
|
error_handling
|
||||||
|
locking
|
||||||
|
|
||||||
.. only:: subproject
|
.. only:: subproject
|
||||||
|
|
||||||
|
106
Documentation/driver-api/soundwire/locking.rst
Normal file
106
Documentation/driver-api/soundwire/locking.rst
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
=================
|
||||||
|
SoundWire Locking
|
||||||
|
=================
|
||||||
|
|
||||||
|
This document explains locking mechanism of the SoundWire Bus. Bus uses
|
||||||
|
following locks in order to avoid race conditions in Bus operations on
|
||||||
|
shared resources.
|
||||||
|
|
||||||
|
- Bus lock
|
||||||
|
|
||||||
|
- Message lock
|
||||||
|
|
||||||
|
Bus lock
|
||||||
|
========
|
||||||
|
|
||||||
|
SoundWire Bus lock is a mutex and is part of Bus data structure
|
||||||
|
(sdw_bus) which is used for every Bus instance. This lock is used to
|
||||||
|
serialize each of the following operations(s) within SoundWire Bus instance.
|
||||||
|
|
||||||
|
- Addition and removal of Slave(s), changing Slave status.
|
||||||
|
|
||||||
|
- Prepare, Enable, Disable and De-prepare stream operations.
|
||||||
|
|
||||||
|
- Access of Stream data structure.
|
||||||
|
|
||||||
|
Message lock
|
||||||
|
============
|
||||||
|
|
||||||
|
SoundWire message transfer lock. This mutex is part of
|
||||||
|
Bus data structure (sdw_bus). This lock is used to serialize the message
|
||||||
|
transfers (read/write) within a SoundWire Bus instance.
|
||||||
|
|
||||||
|
Below examples show how locks are acquired.
|
||||||
|
|
||||||
|
Example 1
|
||||||
|
---------
|
||||||
|
|
||||||
|
Message transfer.
|
||||||
|
|
||||||
|
1. For every message transfer
|
||||||
|
|
||||||
|
a. Acquire Message lock.
|
||||||
|
|
||||||
|
b. Transfer message (Read/Write) to Slave1 or broadcast message on
|
||||||
|
Bus in case of bank switch.
|
||||||
|
|
||||||
|
c. Release Message lock ::
|
||||||
|
|
||||||
|
+----------+ +---------+
|
||||||
|
| | | |
|
||||||
|
| Bus | | Master |
|
||||||
|
| | | Driver |
|
||||||
|
| | | |
|
||||||
|
+----+-----+ +----+----+
|
||||||
|
| |
|
||||||
|
| bus->ops->xfer_msg() |
|
||||||
|
<-------------------------------+ a. Acquire Message lock
|
||||||
|
| | b. Transfer message
|
||||||
|
| |
|
||||||
|
+-------------------------------> c. Release Message lock
|
||||||
|
| return success/error | d. Return success/error
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
|
||||||
|
Example 2
|
||||||
|
---------
|
||||||
|
|
||||||
|
Prepare operation.
|
||||||
|
|
||||||
|
1. Acquire lock for Bus instance associated with Master 1.
|
||||||
|
|
||||||
|
2. For every message transfer in Prepare operation
|
||||||
|
|
||||||
|
a. Acquire Message lock.
|
||||||
|
|
||||||
|
b. Transfer message (Read/Write) to Slave1 or broadcast message on
|
||||||
|
Bus in case of bank switch.
|
||||||
|
|
||||||
|
c. Release Message lock.
|
||||||
|
|
||||||
|
3. Release lock for Bus instance associated with Master 1 ::
|
||||||
|
|
||||||
|
+----------+ +---------+
|
||||||
|
| | | |
|
||||||
|
| Bus | | Master |
|
||||||
|
| | | Driver |
|
||||||
|
| | | |
|
||||||
|
+----+-----+ +----+----+
|
||||||
|
| |
|
||||||
|
| sdw_prepare_stream() |
|
||||||
|
<-------------------------------+ 1. Acquire bus lock
|
||||||
|
| | 2. Perform stream prepare
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| bus->ops->xfer_msg() |
|
||||||
|
<-------------------------------+ a. Acquire Message lock
|
||||||
|
| | b. Transfer message
|
||||||
|
| |
|
||||||
|
+-------------------------------> c. Release Message lock
|
||||||
|
| return success/error | d. Return success/error
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| return success/error | 3. Release bus lock
|
||||||
|
+-------------------------------> 4. Return success/error
|
||||||
|
| |
|
||||||
|
+ +
|
372
Documentation/driver-api/soundwire/stream.rst
Normal file
372
Documentation/driver-api/soundwire/stream.rst
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
=========================
|
||||||
|
Audio Stream in SoundWire
|
||||||
|
=========================
|
||||||
|
|
||||||
|
An audio stream is a logical or virtual connection created between
|
||||||
|
|
||||||
|
(1) System memory buffer(s) and Codec(s)
|
||||||
|
|
||||||
|
(2) DSP memory buffer(s) and Codec(s)
|
||||||
|
|
||||||
|
(3) FIFO(s) and Codec(s)
|
||||||
|
|
||||||
|
(4) Codec(s) and Codec(s)
|
||||||
|
|
||||||
|
which is typically driven by a DMA(s) channel through the data link. An
|
||||||
|
audio stream contains one or more channels of data. All channels within
|
||||||
|
stream must have same sample rate and same sample size.
|
||||||
|
|
||||||
|
Assume a stream with two channels (Left & Right) is opened using SoundWire
|
||||||
|
interface. Below are some ways a stream can be represented in SoundWire.
|
||||||
|
|
||||||
|
Stream Sample in memory (System memory, DSP memory or FIFOs) ::
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
| L | R | L | R | L | R |
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Example 1: Stereo Stream with L and R channels is rendered from Master to
|
||||||
|
Slave. Both Master and Slave is using single port. ::
|
||||||
|
|
||||||
|
+---------------+ Clock Signal +---------------+
|
||||||
|
| Master +----------------------------------+ Slave |
|
||||||
|
| Interface | | Interface |
|
||||||
|
| | | 1 |
|
||||||
|
| | Data Signal | |
|
||||||
|
| L + R +----------------------------------+ L + R |
|
||||||
|
| (Data) | Data Direction | (Data) |
|
||||||
|
+---------------+ +-----------------------> +---------------+
|
||||||
|
|
||||||
|
|
||||||
|
Example 2: Stereo Stream with L and R channels is captured from Slave to
|
||||||
|
Master. Both Master and Slave is using single port. ::
|
||||||
|
|
||||||
|
|
||||||
|
+---------------+ Clock Signal +---------------+
|
||||||
|
| Master +----------------------------------+ Slave |
|
||||||
|
| Interface | | Interface |
|
||||||
|
| | | 1 |
|
||||||
|
| | Data Signal | |
|
||||||
|
| L + R +----------------------------------+ L + R |
|
||||||
|
| (Data) | Data Direction | (Data) |
|
||||||
|
+---------------+ <-----------------------+ +---------------+
|
||||||
|
|
||||||
|
|
||||||
|
Example 3: Stereo Stream with L and R channels is rendered by Master. Each
|
||||||
|
of the L and R channel is received by two different Slaves. Master and both
|
||||||
|
Slaves are using single port. ::
|
||||||
|
|
||||||
|
+---------------+ Clock Signal +---------------+
|
||||||
|
| Master +---------+------------------------+ Slave |
|
||||||
|
| Interface | | | Interface |
|
||||||
|
| | | | 1 |
|
||||||
|
| | | Data Signal | |
|
||||||
|
| L + R +---+------------------------------+ L |
|
||||||
|
| (Data) | | | Data Direction | (Data) |
|
||||||
|
+---------------+ | | +-------------> +---------------+
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| | +---------------+
|
||||||
|
| +----------------------> | Slave |
|
||||||
|
| | Interface |
|
||||||
|
| | 2 |
|
||||||
|
| | |
|
||||||
|
+----------------------------> | R |
|
||||||
|
| (Data) |
|
||||||
|
+---------------+
|
||||||
|
|
||||||
|
|
||||||
|
Example 4: Stereo Stream with L and R channel is rendered by two different
|
||||||
|
Ports of the Master and is received by only single Port of the Slave
|
||||||
|
interface. ::
|
||||||
|
|
||||||
|
+--------------------+
|
||||||
|
| |
|
||||||
|
| +--------------+ +----------------+
|
||||||
|
| | || | |
|
||||||
|
| | Data Port || L Channel | |
|
||||||
|
| | 1 |------------+ | |
|
||||||
|
| | L Channel || | +-----+----+ |
|
||||||
|
| | (Data) || | L + R Channel || Data | |
|
||||||
|
| Master +----------+ | +---+---------> || Port | |
|
||||||
|
| Interface | | || 1 | |
|
||||||
|
| +--------------+ | || | |
|
||||||
|
| | || | +----------+ |
|
||||||
|
| | Data Port |------------+ | |
|
||||||
|
| | 2 || R Channel | Slave |
|
||||||
|
| | R Channel || | Interface |
|
||||||
|
| | (Data) || | 1 |
|
||||||
|
| +--------------+ Clock Signal | L + R |
|
||||||
|
| +---------------------------> | (Data) |
|
||||||
|
+--------------------+ | |
|
||||||
|
+----------------+
|
||||||
|
|
||||||
|
SoundWire Stream Management flow
|
||||||
|
================================
|
||||||
|
|
||||||
|
Stream definitions
|
||||||
|
------------------
|
||||||
|
|
||||||
|
(1) Current stream: This is classified as the stream on which operation has
|
||||||
|
to be performed like prepare, enable, disable, de-prepare etc.
|
||||||
|
|
||||||
|
(2) Active stream: This is classified as the stream which is already active
|
||||||
|
on Bus other than current stream. There can be multiple active streams
|
||||||
|
on the Bus.
|
||||||
|
|
||||||
|
SoundWire Bus manages stream operations for each stream getting
|
||||||
|
rendered/captured on the SoundWire Bus. This section explains Bus operations
|
||||||
|
done for each of the stream allocated/released on Bus. Following are the
|
||||||
|
stream states maintained by the Bus for each of the audio stream.
|
||||||
|
|
||||||
|
|
||||||
|
SoundWire stream states
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Below shows the SoundWire stream states and state transition diagram. ::
|
||||||
|
|
||||||
|
+-----------+ +------------+ +----------+ +----------+
|
||||||
|
| ALLOCATED +---->| CONFIGURED +---->| PREPARED +---->| ENABLED |
|
||||||
|
| STATE | | STATE | | STATE | | STATE |
|
||||||
|
+-----------+ +------------+ +----------+ +----+-----+
|
||||||
|
^
|
||||||
|
|
|
||||||
|
|
|
||||||
|
v
|
||||||
|
+----------+ +------------+ +----+-----+
|
||||||
|
| RELEASED |<----------+ DEPREPARED |<-------+ DISABLED |
|
||||||
|
| STATE | | STATE | | STATE |
|
||||||
|
+----------+ +------------+ +----------+
|
||||||
|
|
||||||
|
NOTE: State transition between prepare and deprepare is supported in Spec
|
||||||
|
but not in the software (subsystem)
|
||||||
|
|
||||||
|
NOTE2: Stream state transition checks need to be handled by caller
|
||||||
|
framework, for example ALSA/ASoC. No checks for stream transition exist in
|
||||||
|
SoundWire subsystem.
|
||||||
|
|
||||||
|
Stream State Operations
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Below section explains the operations done by the Bus on Master(s) and
|
||||||
|
Slave(s) as part of stream state transitions.
|
||||||
|
|
||||||
|
SDW_STREAM_ALLOCATED
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Allocation state for stream. This is the entry state
|
||||||
|
of the stream. Operations performed before entering in this state:
|
||||||
|
|
||||||
|
(1) A stream runtime is allocated for the stream. This stream
|
||||||
|
runtime is used as a reference for all the operations performed
|
||||||
|
on the stream.
|
||||||
|
|
||||||
|
(2) The resources required for holding stream runtime information are
|
||||||
|
allocated and initialized. This holds all stream related information
|
||||||
|
such as stream type (PCM/PDM) and parameters, Master and Slave
|
||||||
|
interface associated with the stream, stream state etc.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_ALLOCATED``.
|
||||||
|
|
||||||
|
Bus implements below API for allocate a stream which needs to be called once
|
||||||
|
per stream. From ASoC DPCM framework, this stream state maybe linked to
|
||||||
|
.startup() operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_alloc_stream(char * stream_name);
|
||||||
|
|
||||||
|
|
||||||
|
SDW_STREAM_CONFIGURED
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Configuration state of stream. Operations performed before entering in
|
||||||
|
this state:
|
||||||
|
|
||||||
|
(1) The resources allocated for stream information in SDW_STREAM_ALLOCATED
|
||||||
|
state are updated here. This includes stream parameters, Master(s)
|
||||||
|
and Slave(s) runtime information associated with current stream.
|
||||||
|
|
||||||
|
(2) All the Master(s) and Slave(s) associated with current stream provide
|
||||||
|
the port information to Bus which includes port numbers allocated by
|
||||||
|
Master(s) and Slave(s) for current stream and their channel mask.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_CONFIGURED``.
|
||||||
|
|
||||||
|
Bus implements below APIs for CONFIG state which needs to be called by
|
||||||
|
the respective Master(s) and Slave(s) associated with stream. These APIs can
|
||||||
|
only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM
|
||||||
|
framework, this stream state is linked to .hw_params() operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_stream_add_master(struct sdw_bus * bus,
|
||||||
|
struct sdw_stream_config * stream_config,
|
||||||
|
struct sdw_ports_config * ports_config,
|
||||||
|
struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
int sdw_stream_add_slave(struct sdw_slave * slave,
|
||||||
|
struct sdw_stream_config * stream_config,
|
||||||
|
struct sdw_ports_config * ports_config,
|
||||||
|
struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
|
||||||
|
SDW_STREAM_PREPARED
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Prepare state of stream. Operations performed before entering in this state:
|
||||||
|
|
||||||
|
(1) Bus parameters such as bandwidth, frame shape, clock frequency,
|
||||||
|
are computed based on current stream as well as already active
|
||||||
|
stream(s) on Bus. Re-computation is required to accommodate current
|
||||||
|
stream on the Bus.
|
||||||
|
|
||||||
|
(2) Transport and port parameters of all Master(s) and Slave(s) port(s) are
|
||||||
|
computed for the current as well as already active stream based on frame
|
||||||
|
shape and clock frequency computed in step 1.
|
||||||
|
|
||||||
|
(3) Computed Bus and transport parameters are programmed in Master(s) and
|
||||||
|
Slave(s) registers. The banked registers programming is done on the
|
||||||
|
alternate bank (bank currently unused). Port(s) are enabled for the
|
||||||
|
already active stream(s) on the alternate bank (bank currently unused).
|
||||||
|
This is done in order to not disrupt already active stream(s).
|
||||||
|
|
||||||
|
(4) Once all the values are programmed, Bus initiates switch to alternate
|
||||||
|
bank where all new values programmed gets into effect.
|
||||||
|
|
||||||
|
(5) Ports of Master(s) and Slave(s) for current stream are prepared by
|
||||||
|
programming PrepareCtrl register.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_PREPARED``.
|
||||||
|
|
||||||
|
Bus implements below API for PREPARE state which needs to be called once per
|
||||||
|
stream. From ASoC DPCM framework, this stream state is linked to
|
||||||
|
.prepare() operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_prepare_stream(struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
|
||||||
|
SDW_STREAM_ENABLED
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Enable state of stream. The data port(s) are enabled upon entering this state.
|
||||||
|
Operations performed before entering in this state:
|
||||||
|
|
||||||
|
(1) All the values computed in SDW_STREAM_PREPARED state are programmed
|
||||||
|
in alternate bank (bank currently unused). It includes programming of
|
||||||
|
already active stream(s) as well.
|
||||||
|
|
||||||
|
(2) All the Master(s) and Slave(s) port(s) for the current stream are
|
||||||
|
enabled on alternate bank (bank currently unused) by programming
|
||||||
|
ChannelEn register.
|
||||||
|
|
||||||
|
(3) Once all the values are programmed, Bus initiates switch to alternate
|
||||||
|
bank where all new values programmed gets into effect and port(s)
|
||||||
|
associated with current stream are enabled.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_ENABLED``.
|
||||||
|
|
||||||
|
Bus implements below API for ENABLE state which needs to be called once per
|
||||||
|
stream. From ASoC DPCM framework, this stream state is linked to
|
||||||
|
.trigger() start operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_enable_stream(struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
SDW_STREAM_DISABLED
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Disable state of stream. The data port(s) are disabled upon exiting this state.
|
||||||
|
Operations performed before entering in this state:
|
||||||
|
|
||||||
|
(1) All the Master(s) and Slave(s) port(s) for the current stream are
|
||||||
|
disabled on alternate bank (bank currently unused) by programming
|
||||||
|
ChannelEn register.
|
||||||
|
|
||||||
|
(2) All the current configuration of Bus and active stream(s) are programmed
|
||||||
|
into alternate bank (bank currently unused).
|
||||||
|
|
||||||
|
(3) Once all the values are programmed, Bus initiates switch to alternate
|
||||||
|
bank where all new values programmed gets into effect and port(s) associated
|
||||||
|
with current stream are disabled.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_DISABLED``.
|
||||||
|
|
||||||
|
Bus implements below API for DISABLED state which needs to be called once
|
||||||
|
per stream. From ASoC DPCM framework, this stream state is linked to
|
||||||
|
.trigger() stop operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_disable_stream(struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
|
||||||
|
SDW_STREAM_DEPREPARED
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
De-prepare state of stream. Operations performed before entering in this
|
||||||
|
state:
|
||||||
|
|
||||||
|
(1) All the port(s) of Master(s) and Slave(s) for current stream are
|
||||||
|
de-prepared by programming PrepareCtrl register.
|
||||||
|
|
||||||
|
(2) The payload bandwidth of current stream is reduced from the total
|
||||||
|
bandwidth requirement of bus and new parameters calculated and
|
||||||
|
applied by performing bank switch etc.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_DEPREPARED``.
|
||||||
|
|
||||||
|
Bus implements below API for DEPREPARED state which needs to be called once
|
||||||
|
per stream. From ASoC DPCM framework, this stream state is linked to
|
||||||
|
.trigger() stop operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_deprepare_stream(struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
|
||||||
|
SDW_STREAM_RELEASED
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Release state of stream. Operations performed before entering in this state:
|
||||||
|
|
||||||
|
(1) Release port resources for all Master(s) and Slave(s) port(s)
|
||||||
|
associated with current stream.
|
||||||
|
|
||||||
|
(2) Release Master(s) and Slave(s) runtime resources associated with
|
||||||
|
current stream.
|
||||||
|
|
||||||
|
(3) Release stream runtime resources associated with current stream.
|
||||||
|
|
||||||
|
After all above operations are successful, stream state is set to
|
||||||
|
``SDW_STREAM_RELEASED``.
|
||||||
|
|
||||||
|
Bus implements below APIs for RELEASE state which needs to be called by
|
||||||
|
all the Master(s) and Slave(s) associated with stream. From ASoC DPCM
|
||||||
|
framework, this stream state is linked to .hw_free() operation.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
int sdw_stream_remove_master(struct sdw_bus * bus,
|
||||||
|
struct sdw_stream_runtime * stream);
|
||||||
|
int sdw_stream_remove_slave(struct sdw_slave * slave,
|
||||||
|
struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
|
||||||
|
The .shutdown() ASoC DPCM operation calls below Bus API to release
|
||||||
|
stream assigned as part of ALLOCATED state.
|
||||||
|
|
||||||
|
In .shutdown() the data structure maintaining stream state are freed up.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
void sdw_release_stream(struct sdw_stream_runtime * stream);
|
||||||
|
|
||||||
|
Not Supported
|
||||||
|
=============
|
||||||
|
|
||||||
|
1. A single port with multiple channels supported cannot be used between two
|
||||||
|
streams or across stream. For example a port with 4 channels cannot be used
|
||||||
|
to handle 2 independent stereo streams even though it's possible in theory
|
||||||
|
in SoundWire.
|
@ -1,199 +0,0 @@
|
|||||||
FPGA Manager Core
|
|
||||||
|
|
||||||
Alan Tull 2015
|
|
||||||
|
|
||||||
Overview
|
|
||||||
========
|
|
||||||
|
|
||||||
The FPGA manager core exports a set of functions for programming an FPGA with
|
|
||||||
an image. The API is manufacturer agnostic. All manufacturer specifics are
|
|
||||||
hidden away in a low level driver which registers a set of ops with the core.
|
|
||||||
The FPGA image data itself is very manufacturer specific, but for our purposes
|
|
||||||
it's just binary data. The FPGA manager core won't parse it.
|
|
||||||
|
|
||||||
The FPGA image to be programmed can be in a scatter gather list, a single
|
|
||||||
contiguous buffer, or a firmware file. Because allocating contiguous kernel
|
|
||||||
memory for the buffer should be avoided, users are encouraged to use a scatter
|
|
||||||
gather list instead if possible.
|
|
||||||
|
|
||||||
The particulars for programming the image are presented in a structure (struct
|
|
||||||
fpga_image_info). This struct contains parameters such as pointers to the
|
|
||||||
FPGA image as well as image-specific particulars such as whether the image was
|
|
||||||
built for full or partial reconfiguration.
|
|
||||||
|
|
||||||
API Functions:
|
|
||||||
==============
|
|
||||||
|
|
||||||
To program the FPGA:
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
int fpga_mgr_load(struct fpga_manager *mgr,
|
|
||||||
struct fpga_image_info *info);
|
|
||||||
|
|
||||||
Load the FPGA from an image which is indicated in the info. If successful,
|
|
||||||
the FPGA ends up in operating mode. Return 0 on success or a negative error
|
|
||||||
code.
|
|
||||||
|
|
||||||
To allocate or free a struct fpga_image_info:
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
struct fpga_image_info *fpga_image_info_alloc(struct device *dev);
|
|
||||||
|
|
||||||
void fpga_image_info_free(struct fpga_image_info *info);
|
|
||||||
|
|
||||||
To get/put a reference to a FPGA manager:
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
struct fpga_manager *of_fpga_mgr_get(struct device_node *node);
|
|
||||||
struct fpga_manager *fpga_mgr_get(struct device *dev);
|
|
||||||
void fpga_mgr_put(struct fpga_manager *mgr);
|
|
||||||
|
|
||||||
Given a DT node or device, get a reference to a FPGA manager. This pointer
|
|
||||||
can be saved until you are ready to program the FPGA. fpga_mgr_put releases
|
|
||||||
the reference.
|
|
||||||
|
|
||||||
|
|
||||||
To get exclusive control of a FPGA manager:
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
int fpga_mgr_lock(struct fpga_manager *mgr);
|
|
||||||
void fpga_mgr_unlock(struct fpga_manager *mgr);
|
|
||||||
|
|
||||||
The user should call fpga_mgr_lock and verify that it returns 0 before
|
|
||||||
attempting to program the FPGA. Likewise, the user should call
|
|
||||||
fpga_mgr_unlock when done programming the FPGA.
|
|
||||||
|
|
||||||
|
|
||||||
To register or unregister the low level FPGA-specific driver:
|
|
||||||
-------------------------------------------------------------
|
|
||||||
|
|
||||||
int fpga_mgr_register(struct device *dev, const char *name,
|
|
||||||
const struct fpga_manager_ops *mops,
|
|
||||||
void *priv);
|
|
||||||
|
|
||||||
void fpga_mgr_unregister(struct device *dev);
|
|
||||||
|
|
||||||
Use of these two functions is described below in "How To Support a new FPGA
|
|
||||||
device."
|
|
||||||
|
|
||||||
|
|
||||||
How to write an image buffer to a supported FPGA
|
|
||||||
================================================
|
|
||||||
#include <linux/fpga/fpga-mgr.h>
|
|
||||||
|
|
||||||
struct fpga_manager *mgr;
|
|
||||||
struct fpga_image_info *info;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get a reference to FPGA manager. The manager is not locked, so you can
|
|
||||||
* hold onto this reference without it preventing programming.
|
|
||||||
*
|
|
||||||
* This example uses the device node of the manager. Alternatively, use
|
|
||||||
* fpga_mgr_get(dev) instead if you have the device.
|
|
||||||
*/
|
|
||||||
mgr = of_fpga_mgr_get(mgr_node);
|
|
||||||
|
|
||||||
/* struct with information about the FPGA image to program. */
|
|
||||||
info = fpga_image_info_alloc(dev);
|
|
||||||
|
|
||||||
/* flags indicates whether to do full or partial reconfiguration */
|
|
||||||
info->flags = FPGA_MGR_PARTIAL_RECONFIG;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* At this point, indicate where the image is. This is pseudo-code; you're
|
|
||||||
* going to use one of these three.
|
|
||||||
*/
|
|
||||||
if (image is in a scatter gather table) {
|
|
||||||
|
|
||||||
info->sgt = [your scatter gather table]
|
|
||||||
|
|
||||||
} else if (image is in a buffer) {
|
|
||||||
|
|
||||||
info->buf = [your image buffer]
|
|
||||||
info->count = [image buffer size]
|
|
||||||
|
|
||||||
} else if (image is in a firmware file) {
|
|
||||||
|
|
||||||
info->firmware_name = devm_kstrdup(dev, firmware_name, GFP_KERNEL);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get exclusive control of FPGA manager */
|
|
||||||
ret = fpga_mgr_lock(mgr);
|
|
||||||
|
|
||||||
/* Load the buffer to the FPGA */
|
|
||||||
ret = fpga_mgr_buf_load(mgr, &info, buf, count);
|
|
||||||
|
|
||||||
/* Release the FPGA manager */
|
|
||||||
fpga_mgr_unlock(mgr);
|
|
||||||
fpga_mgr_put(mgr);
|
|
||||||
|
|
||||||
/* Deallocate the image info if you're done with it */
|
|
||||||
fpga_image_info_free(info);
|
|
||||||
|
|
||||||
How to support a new FPGA device
|
|
||||||
================================
|
|
||||||
To add another FPGA manager, write a driver that implements a set of ops. The
|
|
||||||
probe function calls fpga_mgr_register(), such as:
|
|
||||||
|
|
||||||
static const struct fpga_manager_ops socfpga_fpga_ops = {
|
|
||||||
.write_init = socfpga_fpga_ops_configure_init,
|
|
||||||
.write = socfpga_fpga_ops_configure_write,
|
|
||||||
.write_complete = socfpga_fpga_ops_configure_complete,
|
|
||||||
.state = socfpga_fpga_ops_state,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int socfpga_fpga_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct socfpga_fpga_priv *priv;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
||||||
if (!priv)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* ... do ioremaps, get interrupts, etc. and save
|
|
||||||
them in priv... */
|
|
||||||
|
|
||||||
return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
|
|
||||||
&socfpga_fpga_ops, priv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socfpga_fpga_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
The ops will implement whatever device specific register writes are needed to
|
|
||||||
do the programming sequence for this particular FPGA. These ops return 0 for
|
|
||||||
success or negative error codes otherwise.
|
|
||||||
|
|
||||||
The programming sequence is:
|
|
||||||
1. .write_init
|
|
||||||
2. .write or .write_sg (may be called once or multiple times)
|
|
||||||
3. .write_complete
|
|
||||||
|
|
||||||
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
|
|
||||||
whole FPGA image or may be a smaller chunk of an FPGA image. In the latter
|
|
||||||
case, this function is called multiple times for successive chunks. This interface
|
|
||||||
is suitable for drivers which use PIO.
|
|
||||||
|
|
||||||
The .write_sg version behaves the same as .write except the input is a sg_table
|
|
||||||
scatter list. This interface is suitable for drivers which use DMA.
|
|
||||||
|
|
||||||
The .write_complete function is called after all the image has been written
|
|
||||||
to put the FPGA into operating mode.
|
|
||||||
|
|
||||||
The ops include a .state function which will read the hardware FPGA manager and
|
|
||||||
return a code of type enum fpga_mgr_states. It doesn't result in a change in
|
|
||||||
hardware state.
|
|
@ -1,95 +0,0 @@
|
|||||||
FPGA Regions
|
|
||||||
|
|
||||||
Alan Tull 2017
|
|
||||||
|
|
||||||
CONTENTS
|
|
||||||
- Introduction
|
|
||||||
- The FPGA region API
|
|
||||||
- Usage example
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
============
|
|
||||||
|
|
||||||
This document is meant to be an brief overview of the FPGA region API usage. A
|
|
||||||
more conceptual look at regions can be found in [1].
|
|
||||||
|
|
||||||
For the purposes of this API document, let's just say that a region associates
|
|
||||||
an FPGA Manager and a bridge (or bridges) with a reprogrammable region of an
|
|
||||||
FPGA or the whole FPGA. The API provides a way to register a region and to
|
|
||||||
program a region.
|
|
||||||
|
|
||||||
Currently the only layer above fpga-region.c in the kernel is the Device Tree
|
|
||||||
support (of-fpga-region.c) described in [1]. The DT support layer uses regions
|
|
||||||
to program the FPGA and then DT to handle enumeration. The common region code
|
|
||||||
is intended to be used by other schemes that have other ways of accomplishing
|
|
||||||
enumeration after programming.
|
|
||||||
|
|
||||||
An fpga-region can be set up to know the following things:
|
|
||||||
* which FPGA manager to use to do the programming
|
|
||||||
* which bridges to disable before programming and enable afterwards.
|
|
||||||
|
|
||||||
Additional info needed to program the FPGA image is passed in the struct
|
|
||||||
fpga_image_info [2] including:
|
|
||||||
* pointers to the image as either a scatter-gather buffer, a contiguous
|
|
||||||
buffer, or the name of firmware file
|
|
||||||
* flags indicating specifics such as whether the image if for partial
|
|
||||||
reconfiguration.
|
|
||||||
|
|
||||||
===================
|
|
||||||
The FPGA region API
|
|
||||||
===================
|
|
||||||
|
|
||||||
To register or unregister a region:
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
int fpga_region_register(struct device *dev,
|
|
||||||
struct fpga_region *region);
|
|
||||||
int fpga_region_unregister(struct fpga_region *region);
|
|
||||||
|
|
||||||
An example of usage can be seen in the probe function of [3]
|
|
||||||
|
|
||||||
To program an FPGA:
|
|
||||||
-------------------
|
|
||||||
int fpga_region_program_fpga(struct fpga_region *region);
|
|
||||||
|
|
||||||
This function operates on info passed in the fpga_image_info
|
|
||||||
(region->info).
|
|
||||||
|
|
||||||
This function will attempt to:
|
|
||||||
* lock the region's mutex
|
|
||||||
* lock the region's FPGA manager
|
|
||||||
* build a list of FPGA bridges if a method has been specified to do so
|
|
||||||
* disable the bridges
|
|
||||||
* program the FPGA
|
|
||||||
* re-enable the bridges
|
|
||||||
* release the locks
|
|
||||||
|
|
||||||
=============
|
|
||||||
Usage example
|
|
||||||
=============
|
|
||||||
|
|
||||||
First, allocate the info struct:
|
|
||||||
|
|
||||||
info = fpga_image_info_alloc(dev);
|
|
||||||
if (!info)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
Set flags as needed, i.e.
|
|
||||||
|
|
||||||
info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
|
|
||||||
|
|
||||||
Point to your FPGA image, such as:
|
|
||||||
|
|
||||||
info->sgt = &sgt;
|
|
||||||
|
|
||||||
Add info to region and do the programming:
|
|
||||||
|
|
||||||
region->info = info;
|
|
||||||
ret = fpga_region_program_fpga(region);
|
|
||||||
|
|
||||||
Then enumerate whatever hardware has appeared in the FPGA.
|
|
||||||
|
|
||||||
--
|
|
||||||
[1] ../devicetree/bindings/fpga/fpga-region.txt
|
|
||||||
[2] ./fpga-mgr.txt
|
|
||||||
[3] ../../drivers/fpga/of-fpga-region.c
|
|
@ -1,23 +0,0 @@
|
|||||||
Linux kernel FPGA support
|
|
||||||
|
|
||||||
Alan Tull 2017
|
|
||||||
|
|
||||||
The main point of this project has been to separate the out the upper layers
|
|
||||||
that know when to reprogram a FPGA from the lower layers that know how to
|
|
||||||
reprogram a specific FPGA device. The intention is to make this manufacturer
|
|
||||||
agnostic, understanding that of course the FPGA images are very device specific
|
|
||||||
themselves.
|
|
||||||
|
|
||||||
The framework in the kernel includes:
|
|
||||||
* low level FPGA manager drivers that know how to program a specific device
|
|
||||||
* the fpga-mgr framework they are registered with
|
|
||||||
* low level FPGA bridge drivers for hard/soft bridges which are intended to
|
|
||||||
be disable during FPGA programming
|
|
||||||
* the fpga-bridge framework they are registered with
|
|
||||||
* the fpga-region framework which associates and controls managers and bridges
|
|
||||||
as reconfigurable regions
|
|
||||||
* the of-fpga-region support for reprogramming FPGAs when device tree overlays
|
|
||||||
are applied.
|
|
||||||
|
|
||||||
I would encourage you the user to add code that creates FPGA regions rather
|
|
||||||
that trying to control managers and bridges separately.
|
|
@ -328,6 +328,7 @@ Code Seq#(hex) Include File Comments
|
|||||||
0xCA 80-BF uapi/scsi/cxlflash_ioctl.h
|
0xCA 80-BF uapi/scsi/cxlflash_ioctl.h
|
||||||
0xCB 00-1F CBM serial IEC bus in development:
|
0xCB 00-1F CBM serial IEC bus in development:
|
||||||
<mailto:michael.klein@puffin.lb.shuttle.de>
|
<mailto:michael.klein@puffin.lb.shuttle.de>
|
||||||
|
0xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver
|
||||||
0xCD 01 linux/reiserfs_fs.h
|
0xCD 01 linux/reiserfs_fs.h
|
||||||
0xCF 02 fs/cifs/ioctl.c
|
0xCF 02 fs/cifs/ioctl.c
|
||||||
0xDB 00-0F drivers/char/mwave/mwavepub.h
|
0xDB 00-0F drivers/char/mwave/mwavepub.h
|
||||||
|
226
Documentation/misc-devices/ibmvmc.rst
Normal file
226
Documentation/misc-devices/ibmvmc.rst
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
|
======================================================
|
||||||
|
IBM Virtual Management Channel Kernel Driver (IBMVMC)
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
:Authors:
|
||||||
|
Dave Engebretsen <engebret@us.ibm.com>,
|
||||||
|
Adam Reznechek <adreznec@linux.vnet.ibm.com>,
|
||||||
|
Steven Royer <seroyer@linux.vnet.ibm.com>,
|
||||||
|
Bryant G. Ly <bryantly@linux.vnet.ibm.com>,
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
Note: Knowledge of virtualization technology is required to understand
|
||||||
|
this document.
|
||||||
|
|
||||||
|
A good reference document would be:
|
||||||
|
|
||||||
|
https://openpowerfoundation.org/wp-content/uploads/2016/05/LoPAPR_DRAFT_v11_24March2016_cmt1.pdf
|
||||||
|
|
||||||
|
The Virtual Management Channel (VMC) is a logical device which provides an
|
||||||
|
interface between the hypervisor and a management partition. This interface
|
||||||
|
is like a message passing interface. This management partition is intended
|
||||||
|
to provide an alternative to systems that use a Hardware Management
|
||||||
|
Console (HMC) - based system management.
|
||||||
|
|
||||||
|
The primary hardware management solution that is developed by IBM relies
|
||||||
|
on an appliance server named the Hardware Management Console (HMC),
|
||||||
|
packaged as an external tower or rack-mounted personal computer. In a
|
||||||
|
Power Systems environment, a single HMC can manage multiple POWER
|
||||||
|
processor-based systems.
|
||||||
|
|
||||||
|
Management Application
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
In the management partition, a management application exists which enables
|
||||||
|
a system administrator to configure the system’s partitioning
|
||||||
|
characteristics via a command line interface (CLI) or Representational
|
||||||
|
State Transfer Application (REST API's).
|
||||||
|
|
||||||
|
The management application runs on a Linux logical partition on a
|
||||||
|
POWER8 or newer processor-based server that is virtualized by PowerVM.
|
||||||
|
System configuration, maintenance, and control functions which
|
||||||
|
traditionally require an HMC can be implemented in the management
|
||||||
|
application using a combination of HMC to hypervisor interfaces and
|
||||||
|
existing operating system methods. This tool provides a subset of the
|
||||||
|
functions implemented by the HMC and enables basic partition configuration.
|
||||||
|
The set of HMC to hypervisor messages supported by the management
|
||||||
|
application component are passed to the hypervisor over a VMC interface,
|
||||||
|
which is defined below.
|
||||||
|
|
||||||
|
The VMC enables the management partition to provide basic partitioning
|
||||||
|
functions:
|
||||||
|
|
||||||
|
- Logical Partitioning Configuration
|
||||||
|
- Start, and stop actions for individual partitions
|
||||||
|
- Display of partition status
|
||||||
|
- Management of virtual Ethernet
|
||||||
|
- Management of virtual Storage
|
||||||
|
- Basic system management
|
||||||
|
|
||||||
|
Virtual Management Channel (VMC)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
A logical device, called the Virtual Management Channel (VMC), is defined
|
||||||
|
for communicating between the management application and the hypervisor. It
|
||||||
|
basically creates the pipes that enable virtualization management
|
||||||
|
software. This device is presented to a designated management partition as
|
||||||
|
a virtual device.
|
||||||
|
|
||||||
|
This communication device uses Command/Response Queue (CRQ) and the
|
||||||
|
Remote Direct Memory Access (RDMA) interfaces. A three-way handshake is
|
||||||
|
defined that must take place to establish that both the hypervisor and
|
||||||
|
management partition sides of the channel are running prior to
|
||||||
|
sending/receiving any of the protocol messages.
|
||||||
|
|
||||||
|
This driver also utilizes Transport Event CRQs. CRQ messages are sent
|
||||||
|
when the hypervisor detects one of the peer partitions has abnormally
|
||||||
|
terminated, or one side has called H_FREE_CRQ to close their CRQ.
|
||||||
|
Two new classes of CRQ messages are introduced for the VMC device. VMC
|
||||||
|
Administrative messages are used for each partition using the VMC to
|
||||||
|
communicate capabilities to their partner. HMC Interface messages are used
|
||||||
|
for the actual flow of HMC messages between the management partition and
|
||||||
|
the hypervisor. As most HMC messages far exceed the size of a CRQ buffer,
|
||||||
|
a virtual DMA (RMDA) of the HMC message data is done prior to each HMC
|
||||||
|
Interface CRQ message. Only the management partition drives RDMA
|
||||||
|
operations; hypervisors never directly cause the movement of message data.
|
||||||
|
|
||||||
|
|
||||||
|
Terminology
|
||||||
|
-----------
|
||||||
|
RDMA
|
||||||
|
Remote Direct Memory Access is DMA transfer from the server to its
|
||||||
|
client or from the server to its partner partition. DMA refers
|
||||||
|
to both physical I/O to and from memory operations and to memory
|
||||||
|
to memory move operations.
|
||||||
|
CRQ
|
||||||
|
Command/Response Queue a facility which is used to communicate
|
||||||
|
between partner partitions. Transport events which are signaled
|
||||||
|
from the hypervisor to partition are also reported in this queue.
|
||||||
|
|
||||||
|
Example Management Partition VMC Driver Interface
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
This section provides an example for the management application
|
||||||
|
implementation where a device driver is used to interface to the VMC
|
||||||
|
device. This driver consists of a new device, for example /dev/ibmvmc,
|
||||||
|
which provides interfaces to open, close, read, write, and perform
|
||||||
|
ioctl’s against the VMC device.
|
||||||
|
|
||||||
|
VMC Interface Initialization
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The device driver is responsible for initializing the VMC when the driver
|
||||||
|
is loaded. It first creates and initializes the CRQ. Next, an exchange of
|
||||||
|
VMC capabilities is performed to indicate the code version and number of
|
||||||
|
resources available in both the management partition and the hypervisor.
|
||||||
|
Finally, the hypervisor requests that the management partition create an
|
||||||
|
initial pool of VMC buffers, one buffer for each possible HMC connection,
|
||||||
|
which will be used for management application session initialization.
|
||||||
|
Prior to completion of this initialization sequence, the device returns
|
||||||
|
EBUSY to open() calls. EIO is returned for all open() failures.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Management Partition Hypervisor
|
||||||
|
CRQ INIT
|
||||||
|
---------------------------------------->
|
||||||
|
CRQ INIT COMPLETE
|
||||||
|
<----------------------------------------
|
||||||
|
CAPABILITIES
|
||||||
|
---------------------------------------->
|
||||||
|
CAPABILITIES RESPONSE
|
||||||
|
<----------------------------------------
|
||||||
|
ADD BUFFER (HMC IDX=0,1,..) _
|
||||||
|
<---------------------------------------- |
|
||||||
|
ADD BUFFER RESPONSE | - Perform # HMCs Iterations
|
||||||
|
----------------------------------------> -
|
||||||
|
|
||||||
|
VMC Interface Open
|
||||||
|
------------------
|
||||||
|
|
||||||
|
After the basic VMC channel has been initialized, an HMC session level
|
||||||
|
connection can be established. The application layer performs an open() to
|
||||||
|
the VMC device and executes an ioctl() against it, indicating the HMC ID
|
||||||
|
(32 bytes of data) for this session. If the VMC device is in an invalid
|
||||||
|
state, EIO will be returned for the ioctl(). The device driver creates a
|
||||||
|
new HMC session value (ranging from 1 to 255) and HMC index value (starting
|
||||||
|
at index 0 and ranging to 254) for this HMC ID. The driver then does an
|
||||||
|
RDMA of the HMC ID to the hypervisor, and then sends an Interface Open
|
||||||
|
message to the hypervisor to establish the session over the VMC. After the
|
||||||
|
hypervisor receives this information, it sends Add Buffer messages to the
|
||||||
|
management partition to seed an initial pool of buffers for the new HMC
|
||||||
|
connection. Finally, the hypervisor sends an Interface Open Response
|
||||||
|
message, to indicate that it is ready for normal runtime messaging. The
|
||||||
|
following illustrates this VMC flow:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Management Partition Hypervisor
|
||||||
|
RDMA HMC ID
|
||||||
|
---------------------------------------->
|
||||||
|
Interface Open
|
||||||
|
---------------------------------------->
|
||||||
|
Add Buffer _
|
||||||
|
<---------------------------------------- |
|
||||||
|
Add Buffer Response | - Perform N Iterations
|
||||||
|
----------------------------------------> -
|
||||||
|
Interface Open Response
|
||||||
|
<----------------------------------------
|
||||||
|
|
||||||
|
VMC Interface Runtime
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
During normal runtime, the management application and the hypervisor
|
||||||
|
exchange HMC messages via the Signal VMC message and RDMA operations. When
|
||||||
|
sending data to the hypervisor, the management application performs a
|
||||||
|
write() to the VMC device, and the driver RDMA’s the data to the hypervisor
|
||||||
|
and then sends a Signal Message. If a write() is attempted before VMC
|
||||||
|
device buffers have been made available by the hypervisor, or no buffers
|
||||||
|
are currently available, EBUSY is returned in response to the write(). A
|
||||||
|
write() will return EIO for all other errors, such as an invalid device
|
||||||
|
state. When the hypervisor sends a message to the management, the data is
|
||||||
|
put into a VMC buffer and an Signal Message is sent to the VMC driver in
|
||||||
|
the management partition. The driver RDMA’s the buffer into the partition
|
||||||
|
and passes the data up to the appropriate management application via a
|
||||||
|
read() to the VMC device. The read() request blocks if there is no buffer
|
||||||
|
available to read. The management application may use select() to wait for
|
||||||
|
the VMC device to become ready with data to read.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Management Partition Hypervisor
|
||||||
|
MSG RDMA
|
||||||
|
---------------------------------------->
|
||||||
|
SIGNAL MSG
|
||||||
|
---------------------------------------->
|
||||||
|
SIGNAL MSG
|
||||||
|
<----------------------------------------
|
||||||
|
MSG RDMA
|
||||||
|
<----------------------------------------
|
||||||
|
|
||||||
|
VMC Interface Close
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
HMC session level connections are closed by the management partition when
|
||||||
|
the application layer performs a close() against the device. This action
|
||||||
|
results in an Interface Close message flowing to the hypervisor, which
|
||||||
|
causes the session to be terminated. The device driver must free any
|
||||||
|
storage allocated for buffers for this HMC connection.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
Management Partition Hypervisor
|
||||||
|
INTERFACE CLOSE
|
||||||
|
---------------------------------------->
|
||||||
|
INTERFACE CLOSE RESPONSE
|
||||||
|
<----------------------------------------
|
||||||
|
|
||||||
|
Additional Information
|
||||||
|
======================
|
||||||
|
|
||||||
|
For more information on the documentation for CRQ Messages, VMC Messages,
|
||||||
|
HMC interface Buffers, and signal messages please refer to the Linux on
|
||||||
|
Power Architecture Platform Reference. Section F.
|
@ -177,11 +177,11 @@ Here is an example of the debugging output format:
|
|||||||
ARM external debug module:
|
ARM external debug module:
|
||||||
coresight-cpu-debug 850000.debug: CPU[0]:
|
coresight-cpu-debug 850000.debug: CPU[0]:
|
||||||
coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
|
coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
|
||||||
coresight-cpu-debug 850000.debug: EDPCSR: [<ffff00000808e9bc>] handle_IPI+0x174/0x1d8
|
coresight-cpu-debug 850000.debug: EDPCSR: handle_IPI+0x174/0x1d8
|
||||||
coresight-cpu-debug 850000.debug: EDCIDSR: 00000000
|
coresight-cpu-debug 850000.debug: EDCIDSR: 00000000
|
||||||
coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
|
coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
|
||||||
coresight-cpu-debug 852000.debug: CPU[1]:
|
coresight-cpu-debug 852000.debug: CPU[1]:
|
||||||
coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
|
coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
|
||||||
coresight-cpu-debug 852000.debug: EDPCSR: [<ffff0000087fab34>] debug_notifier_call+0x23c/0x358
|
coresight-cpu-debug 852000.debug: EDPCSR: debug_notifier_call+0x23c/0x358
|
||||||
coresight-cpu-debug 852000.debug: EDCIDSR: 00000000
|
coresight-cpu-debug 852000.debug: EDCIDSR: 00000000
|
||||||
coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
|
coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
|
||||||
|
@ -5586,6 +5586,7 @@ S: Maintained
|
|||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git
|
||||||
Q: http://patchwork.kernel.org/project/linux-fpga/list/
|
Q: http://patchwork.kernel.org/project/linux-fpga/list/
|
||||||
F: Documentation/fpga/
|
F: Documentation/fpga/
|
||||||
|
F: Documentation/driver-api/fpga/
|
||||||
F: Documentation/devicetree/bindings/fpga/
|
F: Documentation/devicetree/bindings/fpga/
|
||||||
F: drivers/fpga/
|
F: drivers/fpga/
|
||||||
F: include/linux/fpga/
|
F: include/linux/fpga/
|
||||||
@ -6773,6 +6774,12 @@ L: linux-scsi@vger.kernel.org
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/scsi/ibmvscsi/ibmvfc*
|
F: drivers/scsi/ibmvscsi/ibmvfc*
|
||||||
|
|
||||||
|
IBM Power Virtual Management Channel Driver
|
||||||
|
M: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
|
||||||
|
M: Steven Royer <seroyer@linux.vnet.ibm.com>
|
||||||
|
S: Supported
|
||||||
|
F: drivers/misc/ibmvmc.*
|
||||||
|
|
||||||
IBM Power Virtual SCSI Device Drivers
|
IBM Power Virtual SCSI Device Drivers
|
||||||
M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
|
M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
|
||||||
L: linux-scsi@vger.kernel.org
|
L: linux-scsi@vger.kernel.org
|
||||||
@ -13136,7 +13143,7 @@ F: include/uapi/sound/
|
|||||||
F: sound/
|
F: sound/
|
||||||
|
|
||||||
SOUND - COMPRESSED AUDIO
|
SOUND - COMPRESSED AUDIO
|
||||||
M: Vinod Koul <vinod.koul@intel.com>
|
M: Vinod Koul <vkoul@kernel.org>
|
||||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||||
S: Supported
|
S: Supported
|
||||||
|
@ -279,6 +279,7 @@
|
|||||||
#define H_GET_MPP_X 0x314
|
#define H_GET_MPP_X 0x314
|
||||||
#define H_SET_MODE 0x31C
|
#define H_SET_MODE 0x31C
|
||||||
#define H_CLEAR_HPT 0x358
|
#define H_CLEAR_HPT 0x358
|
||||||
|
#define H_REQUEST_VMC 0x360
|
||||||
#define H_RESIZE_HPT_PREPARE 0x36C
|
#define H_RESIZE_HPT_PREPARE 0x36C
|
||||||
#define H_RESIZE_HPT_COMMIT 0x370
|
#define H_RESIZE_HPT_COMMIT 0x370
|
||||||
#define H_REGISTER_PROC_TBL 0x37C
|
#define H_REGISTER_PROC_TBL 0x37C
|
||||||
|
@ -102,8 +102,8 @@ static ssize_t driver_override_store(struct device *_dev,
|
|||||||
if (strlen(driver_override)) {
|
if (strlen(driver_override)) {
|
||||||
dev->driver_override = driver_override;
|
dev->driver_override = driver_override;
|
||||||
} else {
|
} else {
|
||||||
kfree(driver_override);
|
kfree(driver_override);
|
||||||
dev->driver_override = NULL;
|
dev->driver_override = NULL;
|
||||||
}
|
}
|
||||||
device_unlock(_dev);
|
device_unlock(_dev);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ if ANDROID
|
|||||||
|
|
||||||
config ANDROID_BINDER_IPC
|
config ANDROID_BINDER_IPC
|
||||||
bool "Android Binder IPC Driver"
|
bool "Android Binder IPC Driver"
|
||||||
depends on MMU
|
depends on MMU && !M68K
|
||||||
default n
|
default n
|
||||||
---help---
|
---help---
|
||||||
Binder is used in Android for both communication between processes,
|
Binder is used in Android for both communication between processes,
|
||||||
@ -32,19 +32,6 @@ config ANDROID_BINDER_DEVICES
|
|||||||
created. Each binder device has its own context manager, and is
|
created. Each binder device has its own context manager, and is
|
||||||
therefore logically separated from the other devices.
|
therefore logically separated from the other devices.
|
||||||
|
|
||||||
config ANDROID_BINDER_IPC_32BIT
|
|
||||||
bool "Use old (Android 4.4 and earlier) 32-bit binder API"
|
|
||||||
depends on !64BIT && ANDROID_BINDER_IPC
|
|
||||||
default y
|
|
||||||
---help---
|
|
||||||
The Binder API has been changed to support both 32 and 64bit
|
|
||||||
applications in a mixed environment.
|
|
||||||
|
|
||||||
Enable this to support an old 32-bit Android user-space (v4.4 and
|
|
||||||
earlier).
|
|
||||||
|
|
||||||
Note that enabling this will break newer Android user-space.
|
|
||||||
|
|
||||||
config ANDROID_BINDER_IPC_SELFTEST
|
config ANDROID_BINDER_IPC_SELFTEST
|
||||||
bool "Android Binder IPC Driver Selftest"
|
bool "Android Binder IPC Driver Selftest"
|
||||||
depends on ANDROID_BINDER_IPC
|
depends on ANDROID_BINDER_IPC
|
||||||
|
@ -72,10 +72,6 @@
|
|||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
|
|
||||||
#define BINDER_IPC_32BIT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <uapi/linux/android/binder.h>
|
#include <uapi/linux/android/binder.h>
|
||||||
#include "binder_alloc.h"
|
#include "binder_alloc.h"
|
||||||
#include "binder_trace.h"
|
#include "binder_trace.h"
|
||||||
@ -2058,8 +2054,8 @@ static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
|
|||||||
struct binder_object_header *hdr;
|
struct binder_object_header *hdr;
|
||||||
size_t object_size = 0;
|
size_t object_size = 0;
|
||||||
|
|
||||||
if (offset > buffer->data_size - sizeof(*hdr) ||
|
if (buffer->data_size < sizeof(*hdr) ||
|
||||||
buffer->data_size < sizeof(*hdr) ||
|
offset > buffer->data_size - sizeof(*hdr) ||
|
||||||
!IS_ALIGNED(offset, sizeof(u32)))
|
!IS_ALIGNED(offset, sizeof(u32)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -3925,10 +3921,11 @@ retry:
|
|||||||
binder_inner_proc_unlock(proc);
|
binder_inner_proc_unlock(proc);
|
||||||
if (put_user(e->cmd, (uint32_t __user *)ptr))
|
if (put_user(e->cmd, (uint32_t __user *)ptr))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
cmd = e->cmd;
|
||||||
e->cmd = BR_OK;
|
e->cmd = BR_OK;
|
||||||
ptr += sizeof(uint32_t);
|
ptr += sizeof(uint32_t);
|
||||||
|
|
||||||
binder_stat_br(proc, thread, e->cmd);
|
binder_stat_br(proc, thread, cmd);
|
||||||
} break;
|
} break;
|
||||||
case BINDER_WORK_TRANSACTION_COMPLETE: {
|
case BINDER_WORK_TRANSACTION_COMPLETE: {
|
||||||
binder_inner_proc_unlock(proc);
|
binder_inner_proc_unlock(proc);
|
||||||
@ -4696,7 +4693,7 @@ static void binder_vma_close(struct vm_area_struct *vma)
|
|||||||
binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
|
binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int binder_vm_fault(struct vm_fault *vmf)
|
static vm_fault_t binder_vm_fault(struct vm_fault *vmf)
|
||||||
{
|
{
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
}
|
}
|
||||||
@ -4730,7 +4727,9 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
|
|||||||
failure_string = "bad vm_flags";
|
failure_string = "bad vm_flags";
|
||||||
goto err_bad_arg;
|
goto err_bad_arg;
|
||||||
}
|
}
|
||||||
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
|
vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
|
||||||
|
vma->vm_flags &= ~VM_MAYWRITE;
|
||||||
|
|
||||||
vma->vm_ops = &binder_vm_ops;
|
vma->vm_ops = &binder_vm_ops;
|
||||||
vma->vm_private_data = proc;
|
vma->vm_private_data = proc;
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
|
|||||||
mm = alloc->vma_vm_mm;
|
mm = alloc->vma_vm_mm;
|
||||||
|
|
||||||
if (mm) {
|
if (mm) {
|
||||||
down_write(&mm->mmap_sem);
|
down_read(&mm->mmap_sem);
|
||||||
vma = alloc->vma;
|
vma = alloc->vma;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
|
|||||||
/* vm_insert_page does not seem to increment the refcount */
|
/* vm_insert_page does not seem to increment the refcount */
|
||||||
}
|
}
|
||||||
if (mm) {
|
if (mm) {
|
||||||
up_write(&mm->mmap_sem);
|
up_read(&mm->mmap_sem);
|
||||||
mmput(mm);
|
mmput(mm);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -321,7 +321,7 @@ err_page_ptr_cleared:
|
|||||||
}
|
}
|
||||||
err_no_vma:
|
err_no_vma:
|
||||||
if (mm) {
|
if (mm) {
|
||||||
up_write(&mm->mmap_sem);
|
up_read(&mm->mmap_sem);
|
||||||
mmput(mm);
|
mmput(mm);
|
||||||
}
|
}
|
||||||
return vma ? -ENOMEM : -ESRCH;
|
return vma ? -ENOMEM : -ESRCH;
|
||||||
|
@ -191,7 +191,7 @@ mspec_close(struct vm_area_struct *vma)
|
|||||||
*
|
*
|
||||||
* Creates a mspec page and maps it to user space.
|
* Creates a mspec page and maps it to user space.
|
||||||
*/
|
*/
|
||||||
static int
|
static vm_fault_t
|
||||||
mspec_fault(struct vm_fault *vmf)
|
mspec_fault(struct vm_fault *vmf)
|
||||||
{
|
{
|
||||||
unsigned long paddr, maddr;
|
unsigned long paddr, maddr;
|
||||||
@ -223,14 +223,7 @@ mspec_fault(struct vm_fault *vmf)
|
|||||||
|
|
||||||
pfn = paddr >> PAGE_SHIFT;
|
pfn = paddr >> PAGE_SHIFT;
|
||||||
|
|
||||||
/*
|
return vmf_insert_pfn(vmf->vma, vmf->address, pfn);
|
||||||
* vm_insert_pfn can fail with -EBUSY, but in that case it will
|
|
||||||
* be because another thread has installed the pte first, so it
|
|
||||||
* is no problem.
|
|
||||||
*/
|
|
||||||
vm_insert_pfn(vmf->vma, vmf->address, pfn);
|
|
||||||
|
|
||||||
return VM_FAULT_NOPAGE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct vm_operations_struct mspec_vm_ops = {
|
static const struct vm_operations_struct mspec_vm_ops = {
|
||||||
|
@ -55,6 +55,14 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY
|
|||||||
the EBDA on Google servers. If found, this log is exported to
|
the EBDA on Google servers. If found, this log is exported to
|
||||||
userland in the file /sys/firmware/log.
|
userland in the file /sys/firmware/log.
|
||||||
|
|
||||||
|
config GOOGLE_FRAMEBUFFER_COREBOOT
|
||||||
|
tristate "Coreboot Framebuffer"
|
||||||
|
depends on FB_SIMPLE
|
||||||
|
depends on GOOGLE_COREBOOT_TABLE
|
||||||
|
help
|
||||||
|
This option enables the kernel to search for a framebuffer in
|
||||||
|
the coreboot table. If found, it is registered with simplefb.
|
||||||
|
|
||||||
config GOOGLE_MEMCONSOLE_COREBOOT
|
config GOOGLE_MEMCONSOLE_COREBOOT
|
||||||
tristate "Firmware Memory Console"
|
tristate "Firmware Memory Console"
|
||||||
depends on GOOGLE_COREBOOT_TABLE
|
depends on GOOGLE_COREBOOT_TABLE
|
||||||
|
@ -4,6 +4,7 @@ obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
|
|||||||
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
|
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
|
||||||
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
|
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
|
||||||
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o
|
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o
|
||||||
|
obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o
|
||||||
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
|
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
|
||||||
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
|
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
|
||||||
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
|
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
|
||||||
|
@ -53,7 +53,7 @@ static int coreboot_table_acpi_probe(struct platform_device *pdev)
|
|||||||
if (!ptr)
|
if (!ptr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
return coreboot_table_init(ptr);
|
return coreboot_table_init(&pdev->dev, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coreboot_table_acpi_remove(struct platform_device *pdev)
|
static int coreboot_table_acpi_remove(struct platform_device *pdev)
|
||||||
|
@ -34,7 +34,7 @@ static int coreboot_table_of_probe(struct platform_device *pdev)
|
|||||||
if (!ptr)
|
if (!ptr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
return coreboot_table_init(ptr);
|
return coreboot_table_init(&pdev->dev, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coreboot_table_of_remove(struct platform_device *pdev)
|
static int coreboot_table_of_remove(struct platform_device *pdev)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* Module providing coreboot table access.
|
* Module providing coreboot table access.
|
||||||
*
|
*
|
||||||
* Copyright 2017 Google Inc.
|
* Copyright 2017 Google Inc.
|
||||||
|
* Copyright 2017 Samuel Holland <samuel@sholland.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License v2.0 as published by
|
* it under the terms of the GNU General Public License v2.0 as published by
|
||||||
@ -15,37 +16,96 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include "coreboot_table.h"
|
#include "coreboot_table.h"
|
||||||
|
|
||||||
struct coreboot_table_entry {
|
#define CB_DEV(d) container_of(d, struct coreboot_device, dev)
|
||||||
u32 tag;
|
#define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
|
||||||
u32 size;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct coreboot_table_header __iomem *ptr_header;
|
static struct coreboot_table_header __iomem *ptr_header;
|
||||||
|
|
||||||
/*
|
static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
|
||||||
* This function parses the coreboot table for an entry that contains the base
|
|
||||||
* address of the given entry tag. The coreboot table consists of a header
|
|
||||||
* directly followed by a number of small, variable-sized entries, which each
|
|
||||||
* contain an identifying tag and their length as the first two fields.
|
|
||||||
*/
|
|
||||||
int coreboot_table_find(int tag, void *data, size_t data_size)
|
|
||||||
{
|
{
|
||||||
struct coreboot_table_header header;
|
struct coreboot_device *device = CB_DEV(dev);
|
||||||
struct coreboot_table_entry entry;
|
struct coreboot_driver *driver = CB_DRV(drv);
|
||||||
|
|
||||||
|
return device->entry.tag == driver->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coreboot_bus_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret = -ENODEV;
|
||||||
|
struct coreboot_device *device = CB_DEV(dev);
|
||||||
|
struct coreboot_driver *driver = CB_DRV(dev->driver);
|
||||||
|
|
||||||
|
if (driver->probe)
|
||||||
|
ret = driver->probe(device);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coreboot_bus_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct coreboot_device *device = CB_DEV(dev);
|
||||||
|
struct coreboot_driver *driver = CB_DRV(dev->driver);
|
||||||
|
|
||||||
|
if (driver->remove)
|
||||||
|
ret = driver->remove(device);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bus_type coreboot_bus_type = {
|
||||||
|
.name = "coreboot",
|
||||||
|
.match = coreboot_bus_match,
|
||||||
|
.probe = coreboot_bus_probe,
|
||||||
|
.remove = coreboot_bus_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init coreboot_bus_init(void)
|
||||||
|
{
|
||||||
|
return bus_register(&coreboot_bus_type);
|
||||||
|
}
|
||||||
|
module_init(coreboot_bus_init);
|
||||||
|
|
||||||
|
static void coreboot_device_release(struct device *dev)
|
||||||
|
{
|
||||||
|
struct coreboot_device *device = CB_DEV(dev);
|
||||||
|
|
||||||
|
kfree(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
int coreboot_driver_register(struct coreboot_driver *driver)
|
||||||
|
{
|
||||||
|
driver->drv.bus = &coreboot_bus_type;
|
||||||
|
|
||||||
|
return driver_register(&driver->drv);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(coreboot_driver_register);
|
||||||
|
|
||||||
|
void coreboot_driver_unregister(struct coreboot_driver *driver)
|
||||||
|
{
|
||||||
|
driver_unregister(&driver->drv);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(coreboot_driver_unregister);
|
||||||
|
|
||||||
|
int coreboot_table_init(struct device *dev, void __iomem *ptr)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
void *ptr_entry;
|
void *ptr_entry;
|
||||||
int i;
|
struct coreboot_device *device;
|
||||||
|
struct coreboot_table_entry entry;
|
||||||
if (!ptr_header)
|
struct coreboot_table_header header;
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
|
ptr_header = ptr;
|
||||||
memcpy_fromio(&header, ptr_header, sizeof(header));
|
memcpy_fromio(&header, ptr_header, sizeof(header));
|
||||||
|
|
||||||
if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
|
if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
|
||||||
@ -54,37 +114,41 @@ int coreboot_table_find(int tag, void *data, size_t data_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ptr_entry = (void *)ptr_header + header.header_bytes;
|
ptr_entry = (void *)ptr_header + header.header_bytes;
|
||||||
|
|
||||||
for (i = 0; i < header.table_entries; i++) {
|
for (i = 0; i < header.table_entries; i++) {
|
||||||
memcpy_fromio(&entry, ptr_entry, sizeof(entry));
|
memcpy_fromio(&entry, ptr_entry, sizeof(entry));
|
||||||
if (entry.tag == tag) {
|
|
||||||
if (data_size < entry.size)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
memcpy_fromio(data, ptr_entry, entry.size);
|
device = kzalloc(sizeof(struct device) + entry.size, GFP_KERNEL);
|
||||||
|
if (!device) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
dev_set_name(&device->dev, "coreboot%d", i);
|
||||||
|
device->dev.parent = dev;
|
||||||
|
device->dev.bus = &coreboot_bus_type;
|
||||||
|
device->dev.release = coreboot_device_release;
|
||||||
|
memcpy_fromio(&device->entry, ptr_entry, entry.size);
|
||||||
|
|
||||||
|
ret = device_register(&device->dev);
|
||||||
|
if (ret) {
|
||||||
|
put_device(&device->dev);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr_entry += entry.size;
|
ptr_entry += entry.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENOENT;
|
return ret;
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(coreboot_table_find);
|
|
||||||
|
|
||||||
int coreboot_table_init(void __iomem *ptr)
|
|
||||||
{
|
|
||||||
ptr_header = ptr;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(coreboot_table_init);
|
EXPORT_SYMBOL(coreboot_table_init);
|
||||||
|
|
||||||
int coreboot_table_exit(void)
|
int coreboot_table_exit(void)
|
||||||
{
|
{
|
||||||
if (ptr_header)
|
if (ptr_header) {
|
||||||
|
bus_unregister(&coreboot_bus_type);
|
||||||
iounmap(ptr_header);
|
iounmap(ptr_header);
|
||||||
|
ptr_header = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
*
|
*
|
||||||
* Internal header for coreboot table access.
|
* Internal header for coreboot table access.
|
||||||
*
|
*
|
||||||
|
* Copyright 2014 Gerd Hoffmann <kraxel@redhat.com>
|
||||||
* Copyright 2017 Google Inc.
|
* Copyright 2017 Google Inc.
|
||||||
|
* Copyright 2017 Samuel Holland <samuel@sholland.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License v2.0 as published by
|
* it under the terms of the GNU General Public License v2.0 as published by
|
||||||
@ -20,14 +22,6 @@
|
|||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
/* List of coreboot entry structures that is used */
|
|
||||||
struct lb_cbmem_ref {
|
|
||||||
uint32_t tag;
|
|
||||||
uint32_t size;
|
|
||||||
|
|
||||||
uint64_t cbmem_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Coreboot table header structure */
|
/* Coreboot table header structure */
|
||||||
struct coreboot_table_header {
|
struct coreboot_table_header {
|
||||||
char signature[4];
|
char signature[4];
|
||||||
@ -38,11 +32,67 @@ struct coreboot_table_header {
|
|||||||
u32 table_entries;
|
u32 table_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Retrieve coreboot table entry with tag *tag* and copy it to data */
|
/* List of coreboot entry structures that is used */
|
||||||
int coreboot_table_find(int tag, void *data, size_t data_size);
|
/* Generic */
|
||||||
|
struct coreboot_table_entry {
|
||||||
|
u32 tag;
|
||||||
|
u32 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Points to a CBMEM entry */
|
||||||
|
struct lb_cbmem_ref {
|
||||||
|
u32 tag;
|
||||||
|
u32 size;
|
||||||
|
|
||||||
|
u64 cbmem_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Describes framebuffer setup by coreboot */
|
||||||
|
struct lb_framebuffer {
|
||||||
|
u32 tag;
|
||||||
|
u32 size;
|
||||||
|
|
||||||
|
u64 physical_address;
|
||||||
|
u32 x_resolution;
|
||||||
|
u32 y_resolution;
|
||||||
|
u32 bytes_per_line;
|
||||||
|
u8 bits_per_pixel;
|
||||||
|
u8 red_mask_pos;
|
||||||
|
u8 red_mask_size;
|
||||||
|
u8 green_mask_pos;
|
||||||
|
u8 green_mask_size;
|
||||||
|
u8 blue_mask_pos;
|
||||||
|
u8 blue_mask_size;
|
||||||
|
u8 reserved_mask_pos;
|
||||||
|
u8 reserved_mask_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A device, additionally with information from coreboot. */
|
||||||
|
struct coreboot_device {
|
||||||
|
struct device dev;
|
||||||
|
union {
|
||||||
|
struct coreboot_table_entry entry;
|
||||||
|
struct lb_cbmem_ref cbmem_ref;
|
||||||
|
struct lb_framebuffer framebuffer;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A driver for handling devices described in coreboot tables. */
|
||||||
|
struct coreboot_driver {
|
||||||
|
int (*probe)(struct coreboot_device *);
|
||||||
|
int (*remove)(struct coreboot_device *);
|
||||||
|
struct device_driver drv;
|
||||||
|
u32 tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Register a driver that uses the data from a coreboot table. */
|
||||||
|
int coreboot_driver_register(struct coreboot_driver *driver);
|
||||||
|
|
||||||
|
/* Unregister a driver that uses the data from a coreboot table. */
|
||||||
|
void coreboot_driver_unregister(struct coreboot_driver *driver);
|
||||||
|
|
||||||
/* Initialize coreboot table module given a pointer to iomem */
|
/* Initialize coreboot table module given a pointer to iomem */
|
||||||
int coreboot_table_init(void __iomem *ptr);
|
int coreboot_table_init(struct device *dev, void __iomem *ptr);
|
||||||
|
|
||||||
/* Cleanup coreboot table module */
|
/* Cleanup coreboot table module */
|
||||||
int coreboot_table_exit(void);
|
int coreboot_table_exit(void);
|
||||||
|
115
drivers/firmware/google/framebuffer-coreboot.c
Normal file
115
drivers/firmware/google/framebuffer-coreboot.c
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* framebuffer-coreboot.c
|
||||||
|
*
|
||||||
|
* Memory based framebuffer accessed through coreboot table.
|
||||||
|
*
|
||||||
|
* Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
* Copyright 2017 Samuel Holland <samuel@sholland.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License v2.0 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_data/simplefb.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#include "coreboot_table.h"
|
||||||
|
|
||||||
|
#define CB_TAG_FRAMEBUFFER 0x12
|
||||||
|
|
||||||
|
static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
|
||||||
|
|
||||||
|
static int framebuffer_probe(struct coreboot_device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u32 length;
|
||||||
|
struct lb_framebuffer *fb = &dev->framebuffer;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct resource res;
|
||||||
|
struct simplefb_platform_data pdata = {
|
||||||
|
.width = fb->x_resolution,
|
||||||
|
.height = fb->y_resolution,
|
||||||
|
.stride = fb->bytes_per_line,
|
||||||
|
.format = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
|
||||||
|
if (fb->bits_per_pixel == formats[i].bits_per_pixel &&
|
||||||
|
fb->red_mask_pos == formats[i].red.offset &&
|
||||||
|
fb->red_mask_size == formats[i].red.length &&
|
||||||
|
fb->green_mask_pos == formats[i].green.offset &&
|
||||||
|
fb->green_mask_size == formats[i].green.length &&
|
||||||
|
fb->blue_mask_pos == formats[i].blue.offset &&
|
||||||
|
fb->blue_mask_size == formats[i].blue.length &&
|
||||||
|
fb->reserved_mask_pos == formats[i].transp.offset &&
|
||||||
|
fb->reserved_mask_size == formats[i].transp.length)
|
||||||
|
pdata.format = formats[i].name;
|
||||||
|
}
|
||||||
|
if (!pdata.format)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
memset(&res, 0, sizeof(res));
|
||||||
|
res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
||||||
|
res.name = "Coreboot Framebuffer";
|
||||||
|
res.start = fb->physical_address;
|
||||||
|
length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line);
|
||||||
|
res.end = res.start + length - 1;
|
||||||
|
if (res.end <= res.start)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pdev = platform_device_register_resndata(&dev->dev,
|
||||||
|
"simple-framebuffer", 0,
|
||||||
|
&res, 1, &pdata,
|
||||||
|
sizeof(pdata));
|
||||||
|
if (IS_ERR(pdev))
|
||||||
|
pr_warn("coreboot: could not register framebuffer\n");
|
||||||
|
else
|
||||||
|
dev_set_drvdata(&dev->dev, pdev);
|
||||||
|
|
||||||
|
return PTR_ERR_OR_ZERO(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int framebuffer_remove(struct coreboot_device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = dev_get_drvdata(&dev->dev);
|
||||||
|
|
||||||
|
platform_device_unregister(pdev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct coreboot_driver framebuffer_driver = {
|
||||||
|
.probe = framebuffer_probe,
|
||||||
|
.remove = framebuffer_remove,
|
||||||
|
.drv = {
|
||||||
|
.name = "framebuffer",
|
||||||
|
},
|
||||||
|
.tag = CB_TAG_FRAMEBUFFER,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init coreboot_framebuffer_init(void)
|
||||||
|
{
|
||||||
|
return coreboot_driver_register(&framebuffer_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coreboot_framebuffer_exit(void)
|
||||||
|
{
|
||||||
|
coreboot_driver_unregister(&framebuffer_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(coreboot_framebuffer_init);
|
||||||
|
module_exit(coreboot_framebuffer_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -15,9 +15,9 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
|
||||||
|
|
||||||
#include "memconsole.h"
|
#include "memconsole.h"
|
||||||
#include "coreboot_table.h"
|
#include "coreboot_table.h"
|
||||||
@ -73,18 +73,19 @@ static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count)
|
|||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int memconsole_coreboot_init(phys_addr_t physaddr)
|
static int memconsole_probe(struct coreboot_device *dev)
|
||||||
{
|
{
|
||||||
struct cbmem_cons __iomem *tmp_cbmc;
|
struct cbmem_cons __iomem *tmp_cbmc;
|
||||||
|
|
||||||
tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB);
|
tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr,
|
||||||
|
sizeof(*tmp_cbmc), MEMREMAP_WB);
|
||||||
|
|
||||||
if (!tmp_cbmc)
|
if (!tmp_cbmc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Read size only once to prevent overrun attack through /dev/mem. */
|
/* Read size only once to prevent overrun attack through /dev/mem. */
|
||||||
cbmem_console_size = tmp_cbmc->size_dont_access_after_boot;
|
cbmem_console_size = tmp_cbmc->size_dont_access_after_boot;
|
||||||
cbmem_console = memremap(physaddr,
|
cbmem_console = memremap(dev->cbmem_ref.cbmem_addr,
|
||||||
cbmem_console_size + sizeof(*cbmem_console),
|
cbmem_console_size + sizeof(*cbmem_console),
|
||||||
MEMREMAP_WB);
|
MEMREMAP_WB);
|
||||||
memunmap(tmp_cbmc);
|
memunmap(tmp_cbmc);
|
||||||
@ -93,26 +94,11 @@ static int memconsole_coreboot_init(phys_addr_t physaddr)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
memconsole_setup(memconsole_coreboot_read);
|
memconsole_setup(memconsole_coreboot_read);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int memconsole_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct lb_cbmem_ref entry;
|
|
||||||
|
|
||||||
ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry));
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = memconsole_coreboot_init(entry.cbmem_addr);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return memconsole_sysfs_init();
|
return memconsole_sysfs_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int memconsole_remove(struct platform_device *pdev)
|
static int memconsole_remove(struct coreboot_device *dev)
|
||||||
{
|
{
|
||||||
memconsole_exit();
|
memconsole_exit();
|
||||||
|
|
||||||
@ -122,28 +108,27 @@ static int memconsole_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver memconsole_driver = {
|
static struct coreboot_driver memconsole_driver = {
|
||||||
.probe = memconsole_probe,
|
.probe = memconsole_probe,
|
||||||
.remove = memconsole_remove,
|
.remove = memconsole_remove,
|
||||||
.driver = {
|
.drv = {
|
||||||
.name = "memconsole",
|
.name = "memconsole",
|
||||||
},
|
},
|
||||||
|
.tag = CB_TAG_CBMEM_CONSOLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init platform_memconsole_init(void)
|
static void coreboot_memconsole_exit(void)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev;
|
coreboot_driver_unregister(&memconsole_driver);
|
||||||
|
|
||||||
pdev = platform_device_register_simple("memconsole", -1, NULL, 0);
|
|
||||||
if (IS_ERR(pdev))
|
|
||||||
return PTR_ERR(pdev);
|
|
||||||
|
|
||||||
platform_driver_register(&memconsole_driver);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(platform_memconsole_init);
|
static int __init coreboot_memconsole_init(void)
|
||||||
|
{
|
||||||
|
return coreboot_driver_register(&memconsole_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_exit(coreboot_memconsole_exit);
|
||||||
|
module_init(coreboot_memconsole_init);
|
||||||
|
|
||||||
MODULE_AUTHOR("Google, Inc.");
|
MODULE_AUTHOR("Google, Inc.");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -286,20 +286,15 @@ static int vpd_sections_init(phys_addr_t physaddr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vpd_probe(struct platform_device *pdev)
|
static int vpd_probe(struct coreboot_device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct lb_cbmem_ref entry;
|
|
||||||
|
|
||||||
ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry));
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
|
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
|
||||||
if (!vpd_kobj)
|
if (!vpd_kobj)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = vpd_sections_init(entry.cbmem_addr);
|
ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kobject_put(vpd_kobj);
|
kobject_put(vpd_kobj);
|
||||||
return ret;
|
return ret;
|
||||||
@ -308,7 +303,7 @@ static int vpd_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vpd_remove(struct platform_device *pdev)
|
static int vpd_remove(struct coreboot_device *dev)
|
||||||
{
|
{
|
||||||
vpd_section_destroy(&ro_vpd);
|
vpd_section_destroy(&ro_vpd);
|
||||||
vpd_section_destroy(&rw_vpd);
|
vpd_section_destroy(&rw_vpd);
|
||||||
@ -318,41 +313,27 @@ static int vpd_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver vpd_driver = {
|
static struct coreboot_driver vpd_driver = {
|
||||||
.probe = vpd_probe,
|
.probe = vpd_probe,
|
||||||
.remove = vpd_remove,
|
.remove = vpd_remove,
|
||||||
.driver = {
|
.drv = {
|
||||||
.name = "vpd",
|
.name = "vpd",
|
||||||
},
|
},
|
||||||
|
.tag = CB_TAG_VPD,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *vpd_pdev;
|
static int __init coreboot_vpd_init(void)
|
||||||
|
|
||||||
static int __init vpd_platform_init(void)
|
|
||||||
{
|
{
|
||||||
int ret;
|
return coreboot_driver_register(&vpd_driver);
|
||||||
|
|
||||||
ret = platform_driver_register(&vpd_driver);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
vpd_pdev = platform_device_register_simple("vpd", -1, NULL, 0);
|
|
||||||
if (IS_ERR(vpd_pdev)) {
|
|
||||||
platform_driver_unregister(&vpd_driver);
|
|
||||||
return PTR_ERR(vpd_pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit vpd_platform_exit(void)
|
static void __exit coreboot_vpd_exit(void)
|
||||||
{
|
{
|
||||||
platform_device_unregister(vpd_pdev);
|
coreboot_driver_unregister(&vpd_driver);
|
||||||
platform_driver_unregister(&vpd_driver);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(vpd_platform_init);
|
module_init(coreboot_vpd_init);
|
||||||
module_exit(vpd_platform_exit);
|
module_exit(coreboot_vpd_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Google, Inc.");
|
MODULE_AUTHOR("Google, Inc.");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -53,7 +53,6 @@ config FPGA_MGR_ALTERA_CVP
|
|||||||
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
|
||||||
depends on HAS_DMA
|
|
||||||
help
|
help
|
||||||
FPGA manager driver support for Xilinx Zynq FPGAs.
|
FPGA manager driver support for Xilinx Zynq FPGAs.
|
||||||
|
|
||||||
@ -70,6 +69,13 @@ config FPGA_MGR_ICE40_SPI
|
|||||||
help
|
help
|
||||||
FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
|
FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
|
||||||
|
|
||||||
|
config FPGA_MGR_MACHXO2_SPI
|
||||||
|
tristate "Lattice MachXO2 SPI"
|
||||||
|
depends on SPI
|
||||||
|
help
|
||||||
|
FPGA manager driver support for Lattice MachXO2 configuration
|
||||||
|
over slave SPI interface.
|
||||||
|
|
||||||
config FPGA_MGR_TS73XX
|
config FPGA_MGR_TS73XX
|
||||||
tristate "Technologic Systems TS-73xx SBC FPGA Manager"
|
tristate "Technologic Systems TS-73xx SBC FPGA Manager"
|
||||||
depends on ARCH_EP93XX && MACH_TS72XX
|
depends on ARCH_EP93XX && MACH_TS72XX
|
||||||
|
@ -10,6 +10,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o
|
|||||||
obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o
|
obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o
|
||||||
obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o
|
obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o
|
||||||
obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
|
obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
|
||||||
|
obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o
|
||||||
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
|
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
|
||||||
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
|
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
|
||||||
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
|
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
|
||||||
|
@ -401,6 +401,7 @@ static int altera_cvp_probe(struct pci_dev *pdev,
|
|||||||
const struct pci_device_id *dev_id)
|
const struct pci_device_id *dev_id)
|
||||||
{
|
{
|
||||||
struct altera_cvp_conf *conf;
|
struct altera_cvp_conf *conf;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
u16 cmd, val;
|
u16 cmd, val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -452,16 +453,24 @@ static int altera_cvp_probe(struct pci_dev *pdev,
|
|||||||
snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
|
snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
|
||||||
ALTERA_CVP_MGR_NAME, pci_name(pdev));
|
ALTERA_CVP_MGR_NAME, pci_name(pdev));
|
||||||
|
|
||||||
ret = fpga_mgr_register(&pdev->dev, conf->mgr_name,
|
mgr = fpga_mgr_create(&pdev->dev, conf->mgr_name,
|
||||||
&altera_cvp_ops, conf);
|
&altera_cvp_ops, conf);
|
||||||
if (ret)
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pci_set_drvdata(pdev, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret) {
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
goto err_unmap;
|
goto err_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
ret = driver_create_file(&altera_cvp_driver.driver,
|
ret = driver_create_file(&altera_cvp_driver.driver,
|
||||||
&driver_attr_chkcfg);
|
&driver_attr_chkcfg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n");
|
dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n");
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
fpga_mgr_unregister(mgr);
|
||||||
goto err_unmap;
|
goto err_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,7 +492,7 @@ static void altera_cvp_remove(struct pci_dev *pdev)
|
|||||||
u16 cmd;
|
u16 cmd;
|
||||||
|
|
||||||
driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg);
|
driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg);
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
fpga_mgr_unregister(mgr);
|
||||||
pci_iounmap(pdev, conf->map);
|
pci_iounmap(pdev, conf->map);
|
||||||
pci_release_region(pdev, CVP_BAR);
|
pci_release_region(pdev, CVP_BAR);
|
||||||
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices
|
* FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
|
* 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/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -106,6 +95,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct alt_fpga2sdram_data *priv;
|
struct alt_fpga2sdram_data *priv;
|
||||||
|
struct fpga_bridge *br;
|
||||||
u32 enable;
|
u32 enable;
|
||||||
struct regmap *sysmgr;
|
struct regmap *sysmgr;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -131,10 +121,18 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
|
|||||||
/* Get f2s bridge configuration saved in handoff register */
|
/* Get f2s bridge configuration saved in handoff register */
|
||||||
regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
|
regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
|
||||||
|
|
||||||
ret = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
|
br = fpga_bridge_create(dev, F2S_BRIDGE_NAME,
|
||||||
&altera_fpga2sdram_br_ops, priv);
|
&altera_fpga2sdram_br_ops, priv);
|
||||||
if (ret)
|
if (!br)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, br);
|
||||||
|
|
||||||
|
ret = fpga_bridge_register(br);
|
||||||
|
if (ret) {
|
||||||
|
fpga_bridge_free(br);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
|
dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
|
||||||
|
|
||||||
@ -146,7 +144,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
|
|||||||
(enable ? "enabling" : "disabling"));
|
(enable ? "enabling" : "disabling"));
|
||||||
ret = _alt_fpga2sdram_enable_set(priv, enable);
|
ret = _alt_fpga2sdram_enable_set(priv, enable);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
fpga_bridge_unregister(&pdev->dev);
|
fpga_bridge_unregister(br);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +155,9 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
static int alt_fpga_bridge_remove(struct platform_device *pdev)
|
static int alt_fpga_bridge_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
fpga_bridge_unregister(&pdev->dev);
|
struct fpga_bridge *br = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
fpga_bridge_unregister(br);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Freeze Bridge Controller
|
* FPGA Freeze Bridge Controller
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016 Altera Corporation. All rights reserved.
|
* 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/delay.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
@ -221,8 +210,10 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
|
|||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
void __iomem *base_addr;
|
void __iomem *base_addr;
|
||||||
struct altera_freeze_br_data *priv;
|
struct altera_freeze_br_data *priv;
|
||||||
|
struct fpga_bridge *br;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
u32 status, revision;
|
u32 status, revision;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!np)
|
if (!np)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -254,13 +245,27 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
priv->base_addr = base_addr;
|
priv->base_addr = base_addr;
|
||||||
|
|
||||||
return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
|
br = fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
|
||||||
&altera_freeze_br_br_ops, priv);
|
&altera_freeze_br_br_ops, priv);
|
||||||
|
if (!br)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, br);
|
||||||
|
|
||||||
|
ret = fpga_bridge_register(br);
|
||||||
|
if (ret) {
|
||||||
|
fpga_bridge_free(br);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int altera_freeze_br_remove(struct platform_device *pdev)
|
static int altera_freeze_br_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
fpga_bridge_unregister(&pdev->dev);
|
struct fpga_bridge *br = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
fpga_bridge_unregister(br);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
|
* FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
|
||||||
*
|
*
|
||||||
@ -6,18 +7,6 @@
|
|||||||
* Includes this patch from the mailing list:
|
* Includes this patch from the mailing list:
|
||||||
* fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
|
* fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
|
||||||
* Signed-off-by: Anatolij Gustschin <agust@denx.de>
|
* 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/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -139,6 +128,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct altera_hps2fpga_data *priv;
|
struct altera_hps2fpga_data *priv;
|
||||||
const struct of_device_id *of_id;
|
const struct of_device_id *of_id;
|
||||||
|
struct fpga_bridge *br;
|
||||||
u32 enable;
|
u32 enable;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -190,11 +180,24 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops,
|
br = fpga_bridge_create(dev, priv->name, &altera_hps2fpga_br_ops, priv);
|
||||||
priv);
|
if (!br) {
|
||||||
err:
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, br);
|
||||||
|
|
||||||
|
ret = fpga_bridge_register(br);
|
||||||
if (ret)
|
if (ret)
|
||||||
clk_disable_unprepare(priv->clk);
|
goto err_free;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
fpga_bridge_free(br);
|
||||||
|
err:
|
||||||
|
clk_disable_unprepare(priv->clk);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -204,7 +207,7 @@ static int alt_fpga_bridge_remove(struct platform_device *pdev)
|
|||||||
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
|
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
|
||||||
struct altera_hps2fpga_data *priv = bridge->priv;
|
struct altera_hps2fpga_data *priv = bridge->priv;
|
||||||
|
|
||||||
fpga_bridge_unregister(&pdev->dev);
|
fpga_bridge_unregister(bridge);
|
||||||
|
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Driver for Altera Partial Reconfiguration IP Core
|
* Driver for Altera Partial Reconfiguration IP Core
|
||||||
*
|
*
|
||||||
@ -5,18 +6,6 @@
|
|||||||
*
|
*
|
||||||
* Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
|
* Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
|
||||||
* by Alan Tull <atull@opensource.altera.com>
|
* by Alan Tull <atull@opensource.altera.com>
|
||||||
*
|
|
||||||
* 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/altera-pr-ip-core.h>
|
#include <linux/fpga/altera-pr-ip-core.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Driver for Altera Partial Reconfiguration IP Core
|
* Driver for Altera Partial Reconfiguration IP Core
|
||||||
*
|
*
|
||||||
@ -5,18 +6,6 @@
|
|||||||
*
|
*
|
||||||
* Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
|
* Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
|
||||||
* by Alan Tull <atull@opensource.altera.com>
|
* by Alan Tull <atull@opensource.altera.com>
|
||||||
*
|
|
||||||
* 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/delay.h>
|
||||||
#include <linux/fpga/altera-pr-ip-core.h>
|
#include <linux/fpga/altera-pr-ip-core.h>
|
||||||
@ -187,6 +176,8 @@ static const struct fpga_manager_ops alt_pr_ops = {
|
|||||||
int alt_pr_register(struct device *dev, void __iomem *reg_base)
|
int alt_pr_register(struct device *dev, void __iomem *reg_base)
|
||||||
{
|
{
|
||||||
struct alt_pr_priv *priv;
|
struct alt_pr_priv *priv;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
|
int ret;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
@ -201,15 +192,27 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
|
|||||||
(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
|
(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
|
||||||
(int)(val & ALT_PR_CSR_PR_START));
|
(int)(val & ALT_PR_CSR_PR_START));
|
||||||
|
|
||||||
return fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
|
mgr = fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev_set_drvdata(dev, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(alt_pr_register);
|
EXPORT_SYMBOL_GPL(alt_pr_register);
|
||||||
|
|
||||||
int alt_pr_unregister(struct device *dev)
|
int alt_pr_unregister(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct fpga_manager *mgr = dev_get_drvdata(dev);
|
||||||
|
|
||||||
dev_dbg(dev, "%s\n", __func__);
|
dev_dbg(dev, "%s\n", __func__);
|
||||||
|
|
||||||
fpga_mgr_unregister(dev);
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -238,6 +238,8 @@ static int altera_ps_probe(struct spi_device *spi)
|
|||||||
{
|
{
|
||||||
struct altera_ps_conf *conf;
|
struct altera_ps_conf *conf;
|
||||||
const struct of_device_id *of_id;
|
const struct of_device_id *of_id;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
|
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
|
||||||
if (!conf)
|
if (!conf)
|
||||||
@ -273,13 +275,25 @@ static int altera_ps_probe(struct spi_device *spi)
|
|||||||
snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
|
snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
|
||||||
dev_driver_string(&spi->dev), dev_name(&spi->dev));
|
dev_driver_string(&spi->dev), dev_name(&spi->dev));
|
||||||
|
|
||||||
return fpga_mgr_register(&spi->dev, conf->mgr_name,
|
mgr = fpga_mgr_create(&spi->dev, conf->mgr_name,
|
||||||
&altera_ps_ops, conf);
|
&altera_ps_ops, conf);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int altera_ps_remove(struct spi_device *spi)
|
static int altera_ps_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
fpga_mgr_unregister(&spi->dev);
|
struct fpga_manager *mgr = spi_get_drvdata(spi);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Bridge Framework Driver
|
* FPGA Bridge Framework Driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
|
* Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
|
||||||
* Copyright (C) 2017 Intel Corporation
|
* Copyright (C) 2017 Intel Corporation
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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-bridge.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
@ -132,6 +121,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
|
|||||||
/**
|
/**
|
||||||
* fpga_bridge_get - get an exclusive reference to a fpga bridge
|
* fpga_bridge_get - get an exclusive reference to a fpga bridge
|
||||||
* @dev: parent device that fpga bridge was registered with
|
* @dev: parent device that fpga bridge was registered with
|
||||||
|
* @info: fpga manager info
|
||||||
*
|
*
|
||||||
* Given a device, get an exclusive reference to a fpga bridge.
|
* Given a device, get an exclusive reference to a fpga bridge.
|
||||||
*
|
*
|
||||||
@ -328,28 +318,29 @@ static struct attribute *fpga_bridge_attrs[] = {
|
|||||||
ATTRIBUTE_GROUPS(fpga_bridge);
|
ATTRIBUTE_GROUPS(fpga_bridge);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_bridge_register - register a fpga bridge driver
|
* fpga_bridge_create - create and initialize a struct fpga_bridge
|
||||||
* @dev: FPGA bridge device from pdev
|
* @dev: FPGA bridge device from pdev
|
||||||
* @name: FPGA bridge name
|
* @name: FPGA bridge name
|
||||||
* @br_ops: pointer to structure of fpga bridge ops
|
* @br_ops: pointer to structure of fpga bridge ops
|
||||||
* @priv: FPGA bridge private data
|
* @priv: FPGA bridge private data
|
||||||
*
|
*
|
||||||
* Return: 0 for success, error code otherwise.
|
* Return: struct fpga_bridge or NULL
|
||||||
*/
|
*/
|
||||||
int fpga_bridge_register(struct device *dev, const char *name,
|
struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
|
||||||
const struct fpga_bridge_ops *br_ops, void *priv)
|
const struct fpga_bridge_ops *br_ops,
|
||||||
|
void *priv)
|
||||||
{
|
{
|
||||||
struct fpga_bridge *bridge;
|
struct fpga_bridge *bridge;
|
||||||
int id, ret = 0;
|
int id, ret = 0;
|
||||||
|
|
||||||
if (!name || !strlen(name)) {
|
if (!name || !strlen(name)) {
|
||||||
dev_err(dev, "Attempt to register with no name!\n");
|
dev_err(dev, "Attempt to register with no name!\n");
|
||||||
return -EINVAL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
|
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
|
||||||
if (!bridge)
|
if (!bridge)
|
||||||
return -ENOMEM;
|
return NULL;
|
||||||
|
|
||||||
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
|
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
@ -370,40 +361,62 @@ int fpga_bridge_register(struct device *dev, const char *name,
|
|||||||
bridge->dev.parent = dev;
|
bridge->dev.parent = dev;
|
||||||
bridge->dev.of_node = dev->of_node;
|
bridge->dev.of_node = dev->of_node;
|
||||||
bridge->dev.id = id;
|
bridge->dev.id = id;
|
||||||
dev_set_drvdata(dev, bridge);
|
|
||||||
|
|
||||||
ret = dev_set_name(&bridge->dev, "br%d", id);
|
ret = dev_set_name(&bridge->dev, "br%d", id);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_device;
|
goto error_device;
|
||||||
|
|
||||||
ret = device_add(&bridge->dev);
|
return bridge;
|
||||||
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:
|
error_device:
|
||||||
ida_simple_remove(&fpga_bridge_ida, id);
|
ida_simple_remove(&fpga_bridge_ida, id);
|
||||||
error_kfree:
|
error_kfree:
|
||||||
kfree(bridge);
|
kfree(bridge);
|
||||||
|
|
||||||
return ret;
|
return NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fpga_bridge_create);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_bridge_free - free a fpga bridge and its id
|
||||||
|
* @bridge: FPGA bridge struct created by fpga_bridge_create
|
||||||
|
*/
|
||||||
|
void fpga_bridge_free(struct fpga_bridge *bridge)
|
||||||
|
{
|
||||||
|
ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
|
||||||
|
kfree(bridge);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fpga_bridge_free);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_bridge_register - register a fpga bridge
|
||||||
|
* @bridge: FPGA bridge struct created by fpga_bridge_create
|
||||||
|
*
|
||||||
|
* Return: 0 for success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int fpga_bridge_register(struct fpga_bridge *bridge)
|
||||||
|
{
|
||||||
|
struct device *dev = &bridge->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = device_add(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||||
|
|
||||||
|
dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fpga_bridge_register);
|
EXPORT_SYMBOL_GPL(fpga_bridge_register);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_bridge_unregister - unregister a fpga bridge driver
|
* fpga_bridge_unregister - unregister and free a fpga bridge
|
||||||
* @dev: FPGA bridge device from pdev
|
* @bridge: FPGA bridge struct created by fpga_bridge_create
|
||||||
*/
|
*/
|
||||||
void fpga_bridge_unregister(struct device *dev)
|
void fpga_bridge_unregister(struct fpga_bridge *bridge)
|
||||||
{
|
{
|
||||||
struct fpga_bridge *bridge = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the low level driver provides a method for putting bridge into
|
* If the low level driver provides a method for putting bridge into
|
||||||
* a desired state upon unregister, do it.
|
* a desired state upon unregister, do it.
|
||||||
@ -419,8 +432,7 @@ static void fpga_bridge_dev_release(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct fpga_bridge *bridge = to_fpga_bridge(dev);
|
struct fpga_bridge *bridge = to_fpga_bridge(dev);
|
||||||
|
|
||||||
ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
|
fpga_bridge_free(bridge);
|
||||||
kfree(bridge);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init fpga_bridge_dev_init(void)
|
static int __init fpga_bridge_dev_init(void)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Manager Core
|
* FPGA Manager Core
|
||||||
*
|
*
|
||||||
@ -6,18 +7,6 @@
|
|||||||
*
|
*
|
||||||
* With code from the mailing list:
|
* With code from the mailing list:
|
||||||
* Copyright (C) 2013 Xilinx, Inc.
|
* Copyright (C) 2013 Xilinx, Inc.
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
#include <linux/fpga/fpga-mgr.h>
|
#include <linux/fpga/fpga-mgr.h>
|
||||||
@ -32,6 +21,12 @@
|
|||||||
static DEFINE_IDA(fpga_mgr_ida);
|
static DEFINE_IDA(fpga_mgr_ida);
|
||||||
static struct class *fpga_mgr_class;
|
static struct class *fpga_mgr_class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_image_info_alloc - Allocate a FPGA image info struct
|
||||||
|
* @dev: owning device
|
||||||
|
*
|
||||||
|
* Return: struct fpga_image_info or NULL
|
||||||
|
*/
|
||||||
struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
|
struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
|
||||||
{
|
{
|
||||||
struct fpga_image_info *info;
|
struct fpga_image_info *info;
|
||||||
@ -50,6 +45,10 @@ struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fpga_image_info_alloc);
|
EXPORT_SYMBOL_GPL(fpga_image_info_alloc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_image_info_free - Free a FPGA image info struct
|
||||||
|
* @info: FPGA image info struct to free
|
||||||
|
*/
|
||||||
void fpga_image_info_free(struct fpga_image_info *info)
|
void fpga_image_info_free(struct fpga_image_info *info)
|
||||||
{
|
{
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
@ -234,7 +233,7 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,
|
|||||||
/**
|
/**
|
||||||
* 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 info
|
||||||
* @buf: buffer contain fpga image
|
* @buf: buffer contain fpga image
|
||||||
* @count: byte count of buf
|
* @count: byte count of buf
|
||||||
*
|
*
|
||||||
@ -343,6 +342,16 @@ static int fpga_mgr_firmware_load(struct fpga_manager *mgr,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_mgr_load - load FPGA from scatter/gather table, buffer, or firmware
|
||||||
|
* @mgr: fpga manager
|
||||||
|
* @info: fpga image information.
|
||||||
|
*
|
||||||
|
* Load the FPGA from an image which is indicated in @info. If successful, the
|
||||||
|
* FPGA ends up in operating mode.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code otherwise.
|
||||||
|
*/
|
||||||
int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
|
int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
|
||||||
{
|
{
|
||||||
if (info->sgt)
|
if (info->sgt)
|
||||||
@ -429,11 +438,9 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_mgr_get - get a reference to a fpga mgr
|
* fpga_mgr_get - Given a device, get a reference to a fpga mgr.
|
||||||
* @dev: parent device that fpga mgr was registered with
|
* @dev: parent device that fpga mgr was registered with
|
||||||
*
|
*
|
||||||
* Given a device, get a reference to a fpga mgr.
|
|
||||||
*
|
|
||||||
* Return: fpga manager struct or IS_ERR() condition containing error code.
|
* Return: fpga manager struct or IS_ERR() condition containing error code.
|
||||||
*/
|
*/
|
||||||
struct fpga_manager *fpga_mgr_get(struct device *dev)
|
struct fpga_manager *fpga_mgr_get(struct device *dev)
|
||||||
@ -453,10 +460,9 @@ static int fpga_mgr_of_node_match(struct device *dev, const void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_fpga_mgr_get - get a reference to a fpga mgr
|
* of_fpga_mgr_get - Given a device node, get a reference to a fpga mgr.
|
||||||
* @node: device node
|
|
||||||
*
|
*
|
||||||
* Given a device node, get a reference to a fpga mgr.
|
* @node: device node
|
||||||
*
|
*
|
||||||
* Return: fpga manager struct or IS_ERR() condition containing error code.
|
* Return: fpga manager struct or IS_ERR() condition containing error code.
|
||||||
*/
|
*/
|
||||||
@ -489,7 +495,10 @@ EXPORT_SYMBOL_GPL(fpga_mgr_put);
|
|||||||
* @mgr: fpga manager
|
* @mgr: fpga manager
|
||||||
*
|
*
|
||||||
* Given a pointer to FPGA Manager (from fpga_mgr_get() or
|
* Given a pointer to FPGA Manager (from fpga_mgr_get() or
|
||||||
* of_fpga_mgr_put()) attempt to get the mutex.
|
* of_fpga_mgr_put()) attempt to get the mutex. The user should call
|
||||||
|
* fpga_mgr_lock() and verify that it returns 0 before attempting to
|
||||||
|
* program the FPGA. Likewise, the user should call fpga_mgr_unlock
|
||||||
|
* when done programming the FPGA.
|
||||||
*
|
*
|
||||||
* Return: 0 for success or -EBUSY
|
* Return: 0 for success or -EBUSY
|
||||||
*/
|
*/
|
||||||
@ -505,7 +514,7 @@ int fpga_mgr_lock(struct fpga_manager *mgr)
|
|||||||
EXPORT_SYMBOL_GPL(fpga_mgr_lock);
|
EXPORT_SYMBOL_GPL(fpga_mgr_lock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_mgr_unlock - Unlock FPGA manager
|
* fpga_mgr_unlock - Unlock FPGA manager after done programming
|
||||||
* @mgr: fpga manager
|
* @mgr: fpga manager
|
||||||
*/
|
*/
|
||||||
void fpga_mgr_unlock(struct fpga_manager *mgr)
|
void fpga_mgr_unlock(struct fpga_manager *mgr)
|
||||||
@ -515,17 +524,17 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
|
|||||||
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
|
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_mgr_register - register a low level fpga manager driver
|
* fpga_mgr_create - create and initialize a FPGA manager struct
|
||||||
* @dev: fpga manager device from pdev
|
* @dev: fpga manager device from pdev
|
||||||
* @name: fpga manager name
|
* @name: fpga manager name
|
||||||
* @mops: pointer to structure of fpga manager ops
|
* @mops: pointer to structure of fpga manager ops
|
||||||
* @priv: fpga manager private data
|
* @priv: fpga manager private data
|
||||||
*
|
*
|
||||||
* Return: 0 on success, negative error code otherwise.
|
* Return: pointer to struct fpga_manager or NULL
|
||||||
*/
|
*/
|
||||||
int fpga_mgr_register(struct device *dev, const char *name,
|
struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
|
||||||
const struct fpga_manager_ops *mops,
|
const struct fpga_manager_ops *mops,
|
||||||
void *priv)
|
void *priv)
|
||||||
{
|
{
|
||||||
struct fpga_manager *mgr;
|
struct fpga_manager *mgr;
|
||||||
int id, ret;
|
int id, ret;
|
||||||
@ -534,17 +543,17 @@ int fpga_mgr_register(struct device *dev, const char *name,
|
|||||||
!mops->write_init || (!mops->write && !mops->write_sg) ||
|
!mops->write_init || (!mops->write && !mops->write_sg) ||
|
||||||
(mops->write && mops->write_sg)) {
|
(mops->write && mops->write_sg)) {
|
||||||
dev_err(dev, "Attempt to register without fpga_manager_ops\n");
|
dev_err(dev, "Attempt to register without fpga_manager_ops\n");
|
||||||
return -EINVAL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name || !strlen(name)) {
|
if (!name || !strlen(name)) {
|
||||||
dev_err(dev, "Attempt to register with no name!\n");
|
dev_err(dev, "Attempt to register with no name!\n");
|
||||||
return -EINVAL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
||||||
if (!mgr)
|
if (!mgr)
|
||||||
return -ENOMEM;
|
return NULL;
|
||||||
|
|
||||||
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
|
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
@ -558,25 +567,56 @@ int fpga_mgr_register(struct device *dev, const char *name,
|
|||||||
mgr->mops = mops;
|
mgr->mops = mops;
|
||||||
mgr->priv = priv;
|
mgr->priv = priv;
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize framework state by requesting low level driver read state
|
|
||||||
* from device. FPGA may be in reset mode or may have been programmed
|
|
||||||
* by bootloader or EEPROM.
|
|
||||||
*/
|
|
||||||
mgr->state = mgr->mops->state(mgr);
|
|
||||||
|
|
||||||
device_initialize(&mgr->dev);
|
device_initialize(&mgr->dev);
|
||||||
mgr->dev.class = fpga_mgr_class;
|
mgr->dev.class = fpga_mgr_class;
|
||||||
mgr->dev.groups = mops->groups;
|
mgr->dev.groups = mops->groups;
|
||||||
mgr->dev.parent = dev;
|
mgr->dev.parent = dev;
|
||||||
mgr->dev.of_node = dev->of_node;
|
mgr->dev.of_node = dev->of_node;
|
||||||
mgr->dev.id = id;
|
mgr->dev.id = id;
|
||||||
dev_set_drvdata(dev, mgr);
|
|
||||||
|
|
||||||
ret = dev_set_name(&mgr->dev, "fpga%d", id);
|
ret = dev_set_name(&mgr->dev, "fpga%d", id);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_device;
|
goto error_device;
|
||||||
|
|
||||||
|
return mgr;
|
||||||
|
|
||||||
|
error_device:
|
||||||
|
ida_simple_remove(&fpga_mgr_ida, id);
|
||||||
|
error_kfree:
|
||||||
|
kfree(mgr);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fpga_mgr_create);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_mgr_free - deallocate a FPGA manager
|
||||||
|
* @mgr: fpga manager struct created by fpga_mgr_create
|
||||||
|
*/
|
||||||
|
void fpga_mgr_free(struct fpga_manager *mgr)
|
||||||
|
{
|
||||||
|
ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
|
||||||
|
kfree(mgr);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fpga_mgr_free);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_mgr_register - register a FPGA manager
|
||||||
|
* @mgr: fpga manager struct created by fpga_mgr_create
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int fpga_mgr_register(struct fpga_manager *mgr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize framework state by requesting low level driver read state
|
||||||
|
* from device. FPGA may be in reset mode or may have been programmed
|
||||||
|
* by bootloader or EEPROM.
|
||||||
|
*/
|
||||||
|
mgr->state = mgr->mops->state(mgr);
|
||||||
|
|
||||||
ret = device_add(&mgr->dev);
|
ret = device_add(&mgr->dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_device;
|
goto error_device;
|
||||||
@ -586,22 +626,18 @@ int fpga_mgr_register(struct device *dev, const char *name,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_device:
|
error_device:
|
||||||
ida_simple_remove(&fpga_mgr_ida, id);
|
ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
|
||||||
error_kfree:
|
|
||||||
kfree(mgr);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fpga_mgr_register);
|
EXPORT_SYMBOL_GPL(fpga_mgr_register);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_mgr_unregister - unregister a low level fpga manager driver
|
* fpga_mgr_unregister - unregister and free a FPGA manager
|
||||||
* @dev: fpga manager device from pdev
|
* @mgr: fpga manager struct
|
||||||
*/
|
*/
|
||||||
void fpga_mgr_unregister(struct device *dev)
|
void fpga_mgr_unregister(struct fpga_manager *mgr)
|
||||||
{
|
{
|
||||||
struct fpga_manager *mgr = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
|
dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -619,8 +655,7 @@ static void fpga_mgr_dev_release(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct fpga_manager *mgr = to_fpga_manager(dev);
|
struct fpga_manager *mgr = to_fpga_manager(dev);
|
||||||
|
|
||||||
ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
|
fpga_mgr_free(mgr);
|
||||||
kfree(mgr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init fpga_mgr_class_init(void)
|
static int __init fpga_mgr_class_init(void)
|
||||||
|
@ -1,22 +1,10 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Region - Device Tree support for FPGA programming under Linux
|
* FPGA Region - Device Tree support for FPGA programming under Linux
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013-2016 Altera Corporation
|
* Copyright (C) 2013-2016 Altera Corporation
|
||||||
* Copyright (C) 2017 Intel Corporation
|
* Copyright (C) 2017 Intel Corporation
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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-bridge.h>
|
||||||
#include <linux/fpga/fpga-mgr.h>
|
#include <linux/fpga/fpga-mgr.h>
|
||||||
#include <linux/fpga/fpga-region.h>
|
#include <linux/fpga/fpga-region.h>
|
||||||
@ -93,8 +81,16 @@ static void fpga_region_put(struct fpga_region *region)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* fpga_region_program_fpga - program FPGA
|
* fpga_region_program_fpga - program FPGA
|
||||||
|
*
|
||||||
* @region: FPGA region
|
* @region: FPGA region
|
||||||
|
*
|
||||||
* Program an FPGA using fpga image info (region->info).
|
* Program an FPGA using fpga image info (region->info).
|
||||||
|
* If the region has a get_bridges function, the exclusive reference for the
|
||||||
|
* bridges will be held if programming succeeds. This is intended to prevent
|
||||||
|
* reprogramming the region until the caller considers it safe to do so.
|
||||||
|
* The caller will need to call fpga_bridges_put() before attempting to
|
||||||
|
* reprogram the region.
|
||||||
|
*
|
||||||
* Return 0 for success or negative error code.
|
* Return 0 for success or negative error code.
|
||||||
*/
|
*/
|
||||||
int fpga_region_program_fpga(struct fpga_region *region)
|
int fpga_region_program_fpga(struct fpga_region *region)
|
||||||
@ -162,45 +158,86 @@ err_put_region:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
|
EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
|
||||||
|
|
||||||
int fpga_region_register(struct device *dev, struct fpga_region *region)
|
/**
|
||||||
|
* fpga_region_create - alloc and init a struct fpga_region
|
||||||
|
* @dev: device parent
|
||||||
|
* @mgr: manager that programs this region
|
||||||
|
* @get_bridges: optional function to get bridges to a list
|
||||||
|
*
|
||||||
|
* Return: struct fpga_region or NULL
|
||||||
|
*/
|
||||||
|
struct fpga_region
|
||||||
|
*fpga_region_create(struct device *dev,
|
||||||
|
struct fpga_manager *mgr,
|
||||||
|
int (*get_bridges)(struct fpga_region *))
|
||||||
{
|
{
|
||||||
|
struct fpga_region *region;
|
||||||
int id, ret = 0;
|
int id, ret = 0;
|
||||||
|
|
||||||
|
region = kzalloc(sizeof(*region), GFP_KERNEL);
|
||||||
|
if (!region)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
|
id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
return id;
|
goto err_free;
|
||||||
|
|
||||||
|
region->mgr = mgr;
|
||||||
|
region->get_bridges = get_bridges;
|
||||||
mutex_init(®ion->mutex);
|
mutex_init(®ion->mutex);
|
||||||
INIT_LIST_HEAD(®ion->bridge_list);
|
INIT_LIST_HEAD(®ion->bridge_list);
|
||||||
|
|
||||||
device_initialize(®ion->dev);
|
device_initialize(®ion->dev);
|
||||||
region->dev.groups = region->groups;
|
|
||||||
region->dev.class = fpga_region_class;
|
region->dev.class = fpga_region_class;
|
||||||
region->dev.parent = dev;
|
region->dev.parent = dev;
|
||||||
region->dev.of_node = dev->of_node;
|
region->dev.of_node = dev->of_node;
|
||||||
region->dev.id = id;
|
region->dev.id = id;
|
||||||
dev_set_drvdata(dev, region);
|
|
||||||
|
|
||||||
ret = dev_set_name(®ion->dev, "region%d", id);
|
ret = dev_set_name(®ion->dev, "region%d", id);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_remove;
|
goto err_remove;
|
||||||
|
|
||||||
ret = device_add(®ion->dev);
|
return region;
|
||||||
if (ret)
|
|
||||||
goto err_remove;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_remove:
|
err_remove:
|
||||||
ida_simple_remove(&fpga_region_ida, id);
|
ida_simple_remove(&fpga_region_ida, id);
|
||||||
return ret;
|
err_free:
|
||||||
|
kfree(region);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fpga_region_create);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_region_free - free a struct fpga_region
|
||||||
|
* @region: FPGA region created by fpga_region_create
|
||||||
|
*/
|
||||||
|
void fpga_region_free(struct fpga_region *region)
|
||||||
|
{
|
||||||
|
ida_simple_remove(&fpga_region_ida, region->dev.id);
|
||||||
|
kfree(region);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(fpga_region_free);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fpga_region_register - register a FPGA region
|
||||||
|
* @region: FPGA region created by fpga_region_create
|
||||||
|
* Return: 0 or -errno
|
||||||
|
*/
|
||||||
|
int fpga_region_register(struct fpga_region *region)
|
||||||
|
{
|
||||||
|
return device_add(®ion->dev);
|
||||||
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fpga_region_register);
|
EXPORT_SYMBOL_GPL(fpga_region_register);
|
||||||
|
|
||||||
int fpga_region_unregister(struct fpga_region *region)
|
/**
|
||||||
|
* fpga_region_unregister - unregister and free a FPGA region
|
||||||
|
* @region: FPGA region
|
||||||
|
*/
|
||||||
|
void fpga_region_unregister(struct fpga_region *region)
|
||||||
{
|
{
|
||||||
device_unregister(®ion->dev);
|
device_unregister(®ion->dev);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fpga_region_unregister);
|
EXPORT_SYMBOL_GPL(fpga_region_unregister);
|
||||||
|
|
||||||
@ -208,7 +245,7 @@ static void fpga_region_dev_release(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct fpga_region *region = to_fpga_region(dev);
|
struct fpga_region *region = to_fpga_region(dev);
|
||||||
|
|
||||||
ida_simple_remove(&fpga_region_ida, region->dev.id);
|
fpga_region_free(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,6 +133,7 @@ static int ice40_fpga_probe(struct spi_device *spi)
|
|||||||
{
|
{
|
||||||
struct device *dev = &spi->dev;
|
struct device *dev = &spi->dev;
|
||||||
struct ice40_fpga_priv *priv;
|
struct ice40_fpga_priv *priv;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
@ -174,14 +175,26 @@ static int ice40_fpga_probe(struct spi_device *spi)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register with the FPGA manager */
|
mgr = fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
|
||||||
return fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
|
&ice40_fpga_ops, priv);
|
||||||
&ice40_fpga_ops, priv);
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ice40_fpga_remove(struct spi_device *spi)
|
static int ice40_fpga_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
fpga_mgr_unregister(&spi->dev);
|
struct fpga_manager *mgr = spi_get_drvdata(spi);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
415
drivers/fpga/machxo2-spi.c
Normal file
415
drivers/fpga/machxo2-spi.c
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Lattice MachXO2 Slave SPI Driver
|
||||||
|
*
|
||||||
|
* Manage Lattice FPGA firmware that is loaded over SPI using
|
||||||
|
* the slave serial configuration interface.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Paolo Pisati <p.pisati@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/fpga/fpga-mgr.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
/* MachXO2 Programming Guide - sysCONFIG Programming Commands */
|
||||||
|
#define IDCODE_PUB {0xe0, 0x00, 0x00, 0x00}
|
||||||
|
#define ISC_ENABLE {0xc6, 0x08, 0x00, 0x00}
|
||||||
|
#define ISC_ERASE {0x0e, 0x04, 0x00, 0x00}
|
||||||
|
#define ISC_PROGRAMDONE {0x5e, 0x00, 0x00, 0x00}
|
||||||
|
#define LSC_INITADDRESS {0x46, 0x00, 0x00, 0x00}
|
||||||
|
#define LSC_PROGINCRNV {0x70, 0x00, 0x00, 0x01}
|
||||||
|
#define LSC_READ_STATUS {0x3c, 0x00, 0x00, 0x00}
|
||||||
|
#define LSC_REFRESH {0x79, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Max CCLK in Slave SPI mode according to 'MachXO2 Family Data
|
||||||
|
* Sheet' sysCONFIG Port Timing Specifications (3-36)
|
||||||
|
*/
|
||||||
|
#define MACHXO2_MAX_SPEED 66000000
|
||||||
|
|
||||||
|
#define MACHXO2_LOW_DELAY_USEC 5
|
||||||
|
#define MACHXO2_HIGH_DELAY_USEC 200
|
||||||
|
#define MACHXO2_REFRESH_USEC 4800
|
||||||
|
#define MACHXO2_MAX_BUSY_LOOP 128
|
||||||
|
#define MACHXO2_MAX_REFRESH_LOOP 16
|
||||||
|
|
||||||
|
#define MACHXO2_PAGE_SIZE 16
|
||||||
|
#define MACHXO2_BUF_SIZE (MACHXO2_PAGE_SIZE + 4)
|
||||||
|
|
||||||
|
/* Status register bits, errors and error mask */
|
||||||
|
#define BUSY 12
|
||||||
|
#define DONE 8
|
||||||
|
#define DVER 27
|
||||||
|
#define ENAB 9
|
||||||
|
#define ERRBITS 23
|
||||||
|
#define ERRMASK 7
|
||||||
|
#define FAIL 13
|
||||||
|
|
||||||
|
#define ENOERR 0 /* no error */
|
||||||
|
#define EID 1
|
||||||
|
#define ECMD 2
|
||||||
|
#define ECRC 3
|
||||||
|
#define EPREAM 4 /* preamble error */
|
||||||
|
#define EABRT 5 /* abort error */
|
||||||
|
#define EOVERFL 6 /* overflow error */
|
||||||
|
#define ESDMEOF 7 /* SDM EOF */
|
||||||
|
|
||||||
|
static inline u8 get_err(unsigned long *status)
|
||||||
|
{
|
||||||
|
return (*status >> ERRBITS) & ERRMASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_status(struct spi_device *spi, unsigned long *status)
|
||||||
|
{
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer rx, tx;
|
||||||
|
static const u8 cmd[] = LSC_READ_STATUS;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&rx, 0, sizeof(rx));
|
||||||
|
memset(&tx, 0, sizeof(tx));
|
||||||
|
tx.tx_buf = cmd;
|
||||||
|
tx.len = sizeof(cmd);
|
||||||
|
rx.rx_buf = status;
|
||||||
|
rx.len = 4;
|
||||||
|
spi_message_init(&msg);
|
||||||
|
spi_message_add_tail(&tx, &msg);
|
||||||
|
spi_message_add_tail(&rx, &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*status = be32_to_cpu(*status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static const char *get_err_string(u8 err)
|
||||||
|
{
|
||||||
|
switch (err) {
|
||||||
|
case ENOERR: return "No Error";
|
||||||
|
case EID: return "ID ERR";
|
||||||
|
case ECMD: return "CMD ERR";
|
||||||
|
case ECRC: return "CRC ERR";
|
||||||
|
case EPREAM: return "Preamble ERR";
|
||||||
|
case EABRT: return "Abort ERR";
|
||||||
|
case EOVERFL: return "Overflow ERR";
|
||||||
|
case ESDMEOF: return "SDM EOF";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Default switch case";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void dump_status_reg(unsigned long *status)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
pr_debug("machxo2 status: 0x%08lX - done=%d, cfgena=%d, busy=%d, fail=%d, devver=%d, err=%s\n",
|
||||||
|
*status, test_bit(DONE, status), test_bit(ENAB, status),
|
||||||
|
test_bit(BUSY, status), test_bit(FAIL, status),
|
||||||
|
test_bit(DVER, status), get_err_string(get_err(status)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wait_until_not_busy(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
unsigned long status;
|
||||||
|
int ret, loop = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = get_status(spi, &status);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (++loop >= MACHXO2_MAX_BUSY_LOOP)
|
||||||
|
return -EBUSY;
|
||||||
|
} while (test_bit(BUSY, &status));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int machxo2_cleanup(struct fpga_manager *mgr)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = mgr->priv;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer tx[2];
|
||||||
|
static const u8 erase[] = ISC_ERASE;
|
||||||
|
static const u8 refresh[] = LSC_REFRESH;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(tx, 0, sizeof(tx));
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx[0].tx_buf = &erase;
|
||||||
|
tx[0].len = sizeof(erase);
|
||||||
|
spi_message_add_tail(&tx[0], &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
ret = wait_until_not_busy(spi);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx[1].tx_buf = &refresh;
|
||||||
|
tx[1].len = sizeof(refresh);
|
||||||
|
tx[1].delay_usecs = MACHXO2_REFRESH_USEC;
|
||||||
|
spi_message_add_tail(&tx[1], &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
dev_err(&mgr->dev, "Cleanup failed\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum fpga_mgr_states machxo2_spi_state(struct fpga_manager *mgr)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = mgr->priv;
|
||||||
|
unsigned long status;
|
||||||
|
|
||||||
|
get_status(spi, &status);
|
||||||
|
if (!test_bit(BUSY, &status) && test_bit(DONE, &status) &&
|
||||||
|
get_err(&status) == ENOERR)
|
||||||
|
return FPGA_MGR_STATE_OPERATING;
|
||||||
|
|
||||||
|
return FPGA_MGR_STATE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int machxo2_write_init(struct fpga_manager *mgr,
|
||||||
|
struct fpga_image_info *info,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = mgr->priv;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer tx[3];
|
||||||
|
static const u8 enable[] = ISC_ENABLE;
|
||||||
|
static const u8 erase[] = ISC_ERASE;
|
||||||
|
static const u8 initaddr[] = LSC_INITADDRESS;
|
||||||
|
unsigned long status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
|
||||||
|
dev_err(&mgr->dev,
|
||||||
|
"Partial reconfiguration is not supported\n");
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
memset(tx, 0, sizeof(tx));
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx[0].tx_buf = &enable;
|
||||||
|
tx[0].len = sizeof(enable);
|
||||||
|
tx[0].delay_usecs = MACHXO2_LOW_DELAY_USEC;
|
||||||
|
spi_message_add_tail(&tx[0], &msg);
|
||||||
|
|
||||||
|
tx[1].tx_buf = &erase;
|
||||||
|
tx[1].len = sizeof(erase);
|
||||||
|
spi_message_add_tail(&tx[1], &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
ret = wait_until_not_busy(spi);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
get_status(spi, &status);
|
||||||
|
if (test_bit(FAIL, &status))
|
||||||
|
goto fail;
|
||||||
|
dump_status_reg(&status);
|
||||||
|
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx[2].tx_buf = &initaddr;
|
||||||
|
tx[2].len = sizeof(initaddr);
|
||||||
|
spi_message_add_tail(&tx[2], &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
dev_err(&mgr->dev, "Error during FPGA init.\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int machxo2_write(struct fpga_manager *mgr, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = mgr->priv;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer tx;
|
||||||
|
static const u8 progincr[] = LSC_PROGINCRNV;
|
||||||
|
u8 payload[MACHXO2_BUF_SIZE];
|
||||||
|
unsigned long status;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
if (count % MACHXO2_PAGE_SIZE != 0) {
|
||||||
|
dev_err(&mgr->dev, "Malformed payload.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
memcpy(payload, &progincr, sizeof(progincr));
|
||||||
|
for (i = 0; i < count; i += MACHXO2_PAGE_SIZE) {
|
||||||
|
memcpy(&payload[sizeof(progincr)], &buf[i], MACHXO2_PAGE_SIZE);
|
||||||
|
memset(&tx, 0, sizeof(tx));
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx.tx_buf = payload;
|
||||||
|
tx.len = MACHXO2_BUF_SIZE;
|
||||||
|
tx.delay_usecs = MACHXO2_HIGH_DELAY_USEC;
|
||||||
|
spi_message_add_tail(&tx, &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&mgr->dev, "Error loading the bitstream.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int machxo2_write_complete(struct fpga_manager *mgr,
|
||||||
|
struct fpga_image_info *info)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = mgr->priv;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer tx[2];
|
||||||
|
static const u8 progdone[] = ISC_PROGRAMDONE;
|
||||||
|
static const u8 refresh[] = LSC_REFRESH;
|
||||||
|
unsigned long status;
|
||||||
|
int ret, refreshloop = 0;
|
||||||
|
|
||||||
|
memset(tx, 0, sizeof(tx));
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx[0].tx_buf = &progdone;
|
||||||
|
tx[0].len = sizeof(progdone);
|
||||||
|
spi_message_add_tail(&tx[0], &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
ret = wait_until_not_busy(spi);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
if (!test_bit(DONE, &status)) {
|
||||||
|
machxo2_cleanup(mgr);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
spi_message_init(&msg);
|
||||||
|
tx[1].tx_buf = &refresh;
|
||||||
|
tx[1].len = sizeof(refresh);
|
||||||
|
tx[1].delay_usecs = MACHXO2_REFRESH_USEC;
|
||||||
|
spi_message_add_tail(&tx[1], &msg);
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* check refresh status */
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
if (!test_bit(BUSY, &status) && test_bit(DONE, &status) &&
|
||||||
|
get_err(&status) == ENOERR)
|
||||||
|
break;
|
||||||
|
if (++refreshloop == MACHXO2_MAX_REFRESH_LOOP) {
|
||||||
|
machxo2_cleanup(mgr);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
get_status(spi, &status);
|
||||||
|
dump_status_reg(&status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
dev_err(&mgr->dev, "Refresh failed.\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fpga_manager_ops machxo2_ops = {
|
||||||
|
.state = machxo2_spi_state,
|
||||||
|
.write_init = machxo2_write_init,
|
||||||
|
.write = machxo2_write,
|
||||||
|
.write_complete = machxo2_write_complete,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int machxo2_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct device *dev = &spi->dev;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (spi->max_speed_hz > MACHXO2_MAX_SPEED) {
|
||||||
|
dev_err(dev, "Speed is too high\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr = fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
|
||||||
|
&machxo2_ops, spi);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int machxo2_spi_remove(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct fpga_manager *mgr = spi_get_drvdata(spi);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id of_match[] = {
|
||||||
|
{ .compatible = "lattice,machxo2-slave-spi", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, of_match);
|
||||||
|
|
||||||
|
static const struct spi_device_id lattice_ids[] = {
|
||||||
|
{ "machxo2-slave-spi", 0 },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, lattice_ids);
|
||||||
|
|
||||||
|
static struct spi_driver machxo2_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "machxo2-slave-spi",
|
||||||
|
.of_match_table = of_match_ptr(of_match),
|
||||||
|
},
|
||||||
|
.probe = machxo2_spi_probe,
|
||||||
|
.remove = machxo2_spi_remove,
|
||||||
|
.id_table = lattice_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_spi_driver(machxo2_spi_driver)
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Paolo Pisati <p.pisati@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Load Lattice FPGA firmware over SPI");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@ -1,22 +1,10 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Region - Device Tree support for FPGA programming under Linux
|
* FPGA Region - Device Tree support for FPGA programming under Linux
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013-2016 Altera Corporation
|
* Copyright (C) 2013-2016 Altera Corporation
|
||||||
* Copyright (C) 2017 Intel Corporation
|
* Copyright (C) 2017 Intel Corporation
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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-bridge.h>
|
||||||
#include <linux/fpga/fpga-mgr.h>
|
#include <linux/fpga/fpga-mgr.h>
|
||||||
#include <linux/fpga/fpga-region.h>
|
#include <linux/fpga/fpga-region.h>
|
||||||
@ -422,27 +410,25 @@ static int of_fpga_region_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(mgr))
|
if (IS_ERR(mgr))
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
|
region = fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
|
||||||
if (!region) {
|
if (!region) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto eprobe_mgr_put;
|
goto eprobe_mgr_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
region->mgr = mgr;
|
ret = fpga_region_register(region);
|
||||||
|
|
||||||
/* Specify how to get bridges for this type of region. */
|
|
||||||
region->get_bridges = of_fpga_region_get_bridges;
|
|
||||||
|
|
||||||
ret = fpga_region_register(dev, region);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto eprobe_mgr_put;
|
goto eprobe_free;
|
||||||
|
|
||||||
of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev);
|
of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev);
|
||||||
|
dev_set_drvdata(dev, region);
|
||||||
|
|
||||||
dev_info(dev, "FPGA Region probed\n");
|
dev_info(dev, "FPGA Region probed\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
eprobe_free:
|
||||||
|
fpga_region_free(region);
|
||||||
eprobe_mgr_put:
|
eprobe_mgr_put:
|
||||||
fpga_mgr_put(mgr);
|
fpga_mgr_put(mgr);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1,21 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Manager Driver for Altera Arria10 SoCFPGA
|
* FPGA Manager Driver for Altera Arria10 SoCFPGA
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015-2016 Altera Corporation
|
* 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/clk.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@ -482,6 +470,7 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct a10_fpga_priv *priv;
|
struct a10_fpga_priv *priv;
|
||||||
void __iomem *reg_base;
|
void __iomem *reg_base;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -519,9 +508,16 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
|
mgr = fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
|
||||||
&socfpga_a10_fpga_mgr_ops, priv);
|
&socfpga_a10_fpga_mgr_ops, priv);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -534,7 +530,7 @@ static int socfpga_a10_fpga_remove(struct platform_device *pdev)
|
|||||||
struct fpga_manager *mgr = platform_get_drvdata(pdev);
|
struct fpga_manager *mgr = platform_get_drvdata(pdev);
|
||||||
struct a10_fpga_priv *priv = mgr->priv;
|
struct a10_fpga_priv *priv = mgr->priv;
|
||||||
|
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
fpga_mgr_unregister(mgr);
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* FPGA Manager Driver for Altera SOCFPGA
|
* FPGA Manager Driver for Altera SOCFPGA
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013-2015 Altera Corporation
|
* Copyright (C) 2013-2015 Altera Corporation
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@ -555,6 +544,7 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct socfpga_fpga_priv *priv;
|
struct socfpga_fpga_priv *priv;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -581,13 +571,25 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
|
mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
|
||||||
&socfpga_fpga_ops, priv);
|
&socfpga_fpga_ops, priv);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int socfpga_fpga_remove(struct platform_device *pdev)
|
static int socfpga_fpga_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
struct fpga_manager *mgr = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,9 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device *kdev = &pdev->dev;
|
struct device *kdev = &pdev->dev;
|
||||||
struct ts73xx_fpga_priv *priv;
|
struct ts73xx_fpga_priv *priv;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
int ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
@ -131,13 +133,25 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(priv->io_base);
|
return PTR_ERR(priv->io_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
|
mgr = fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
|
||||||
&ts73xx_fpga_ops, priv);
|
&ts73xx_fpga_ops, priv);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ts73xx_fpga_remove(struct platform_device *pdev)
|
static int ts73xx_fpga_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
struct fpga_manager *mgr = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match);
|
|||||||
static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
|
static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct xlnx_pr_decoupler_data *priv;
|
struct xlnx_pr_decoupler_data *priv;
|
||||||
|
struct fpga_bridge *br;
|
||||||
int err;
|
int err;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
@ -120,16 +121,27 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
clk_disable(priv->clk);
|
clk_disable(priv->clk);
|
||||||
|
|
||||||
err = fpga_bridge_register(&pdev->dev, "Xilinx PR Decoupler",
|
br = fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler",
|
||||||
&xlnx_pr_decoupler_br_ops, priv);
|
&xlnx_pr_decoupler_br_ops, priv);
|
||||||
|
if (!br) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, br);
|
||||||
|
|
||||||
|
err = fpga_bridge_register(br);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler");
|
dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler");
|
||||||
clk_unprepare(priv->clk);
|
goto err_clk;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_clk:
|
||||||
|
clk_unprepare(priv->clk);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
|
static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
|
||||||
@ -137,7 +149,7 @@ static int xlnx_pr_decoupler_remove(struct platform_device *pdev)
|
|||||||
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
|
struct fpga_bridge *bridge = platform_get_drvdata(pdev);
|
||||||
struct xlnx_pr_decoupler_data *p = bridge->priv;
|
struct xlnx_pr_decoupler_data *p = bridge->priv;
|
||||||
|
|
||||||
fpga_bridge_unregister(&pdev->dev);
|
fpga_bridge_unregister(bridge);
|
||||||
|
|
||||||
clk_unprepare(p->clk);
|
clk_unprepare(p->clk);
|
||||||
|
|
||||||
|
@ -143,6 +143,8 @@ static const struct fpga_manager_ops xilinx_spi_ops = {
|
|||||||
static int xilinx_spi_probe(struct spi_device *spi)
|
static int xilinx_spi_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct xilinx_spi_conf *conf;
|
struct xilinx_spi_conf *conf;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
|
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
|
||||||
if (!conf)
|
if (!conf)
|
||||||
@ -165,13 +167,25 @@ static int xilinx_spi_probe(struct spi_device *spi)
|
|||||||
return PTR_ERR(conf->done);
|
return PTR_ERR(conf->done);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fpga_mgr_register(&spi->dev, "Xilinx Slave Serial FPGA Manager",
|
mgr = fpga_mgr_create(&spi->dev, "Xilinx Slave Serial FPGA Manager",
|
||||||
&xilinx_spi_ops, conf);
|
&xilinx_spi_ops, conf);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, mgr);
|
||||||
|
|
||||||
|
ret = fpga_mgr_register(mgr);
|
||||||
|
if (ret)
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xilinx_spi_remove(struct spi_device *spi)
|
static int xilinx_spi_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
fpga_mgr_unregister(&spi->dev);
|
struct fpga_manager *mgr = spi_get_drvdata(spi);
|
||||||
|
|
||||||
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -558,6 +558,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct zynq_fpga_priv *priv;
|
struct zynq_fpga_priv *priv;
|
||||||
|
struct fpga_manager *mgr;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -613,10 +614,17 @@ static int zynq_fpga_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
clk_disable(priv->clk);
|
clk_disable(priv->clk);
|
||||||
|
|
||||||
err = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
|
mgr = fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
|
||||||
&zynq_fpga_ops, priv);
|
&zynq_fpga_ops, priv);
|
||||||
|
if (!mgr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mgr);
|
||||||
|
|
||||||
|
err = fpga_mgr_register(mgr);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(dev, "unable to register FPGA manager\n");
|
dev_err(dev, "unable to register FPGA manager\n");
|
||||||
|
fpga_mgr_free(mgr);
|
||||||
clk_unprepare(priv->clk);
|
clk_unprepare(priv->clk);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -632,7 +640,7 @@ static int zynq_fpga_remove(struct platform_device *pdev)
|
|||||||
mgr = platform_get_drvdata(pdev);
|
mgr = platform_get_drvdata(pdev);
|
||||||
priv = mgr->priv;
|
priv = mgr->priv;
|
||||||
|
|
||||||
fpga_mgr_unregister(&pdev->dev);
|
fpga_mgr_unregister(mgr);
|
||||||
|
|
||||||
clk_unprepare(priv->clk);
|
clk_unprepare(priv->clk);
|
||||||
|
|
||||||
|
@ -63,6 +63,9 @@ static __u32 vmbus_get_next_version(__u32 current_version)
|
|||||||
case (VERSION_WIN10):
|
case (VERSION_WIN10):
|
||||||
return VERSION_WIN8_1;
|
return VERSION_WIN8_1;
|
||||||
|
|
||||||
|
case (VERSION_WIN10_V5):
|
||||||
|
return VERSION_WIN10;
|
||||||
|
|
||||||
case (VERSION_WS2008):
|
case (VERSION_WS2008):
|
||||||
default:
|
default:
|
||||||
return VERSION_INVAL;
|
return VERSION_INVAL;
|
||||||
@ -80,9 +83,29 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
|
|||||||
|
|
||||||
msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
|
msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(*msg));
|
||||||
msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
|
msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
|
||||||
msg->vmbus_version_requested = version;
|
msg->vmbus_version_requested = version;
|
||||||
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
|
|
||||||
|
/*
|
||||||
|
* VMBus protocol 5.0 (VERSION_WIN10_V5) requires that we must use
|
||||||
|
* VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
|
||||||
|
* and for subsequent messages, we must use the Message Connection ID
|
||||||
|
* field in the host-returned Version Response Message. And, with
|
||||||
|
* VERSION_WIN10_V5, we don't use msg->interrupt_page, but we tell
|
||||||
|
* the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for
|
||||||
|
* compatibility.
|
||||||
|
*
|
||||||
|
* On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
|
||||||
|
*/
|
||||||
|
if (version >= VERSION_WIN10_V5) {
|
||||||
|
msg->msg_sint = VMBUS_MESSAGE_SINT;
|
||||||
|
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
|
||||||
|
} else {
|
||||||
|
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
|
||||||
|
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
|
||||||
|
}
|
||||||
|
|
||||||
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
|
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
|
||||||
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
|
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
|
||||||
/*
|
/*
|
||||||
@ -137,6 +160,10 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
|
|||||||
/* Check if successful */
|
/* Check if successful */
|
||||||
if (msginfo->response.version_response.version_supported) {
|
if (msginfo->response.version_response.version_supported) {
|
||||||
vmbus_connection.conn_state = CONNECTED;
|
vmbus_connection.conn_state = CONNECTED;
|
||||||
|
|
||||||
|
if (version >= VERSION_WIN10_V5)
|
||||||
|
vmbus_connection.msg_conn_id =
|
||||||
|
msginfo->response.version_response.msg_conn_id;
|
||||||
} else {
|
} else {
|
||||||
return -ECONNREFUSED;
|
return -ECONNREFUSED;
|
||||||
}
|
}
|
||||||
@ -354,13 +381,14 @@ void vmbus_on_event(unsigned long data)
|
|||||||
*/
|
*/
|
||||||
int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
|
int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
|
||||||
{
|
{
|
||||||
|
struct vmbus_channel_message_header *hdr;
|
||||||
union hv_connection_id conn_id;
|
union hv_connection_id conn_id;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int retries = 0;
|
int retries = 0;
|
||||||
u32 usec = 1;
|
u32 usec = 1;
|
||||||
|
|
||||||
conn_id.asu32 = 0;
|
conn_id.asu32 = 0;
|
||||||
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
|
conn_id.u.id = vmbus_connection.msg_conn_id;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hv_post_message() can have transient failures because of
|
* hv_post_message() can have transient failures because of
|
||||||
@ -372,6 +400,18 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
|
|||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case HV_STATUS_INVALID_CONNECTION_ID:
|
case HV_STATUS_INVALID_CONNECTION_ID:
|
||||||
|
/*
|
||||||
|
* See vmbus_negotiate_version(): VMBus protocol 5.0
|
||||||
|
* requires that we must use
|
||||||
|
* VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
|
||||||
|
* Contact message, but on old hosts that only
|
||||||
|
* support VMBus protocol 4.0 or lower, here we get
|
||||||
|
* HV_STATUS_INVALID_CONNECTION_ID and we should
|
||||||
|
* return an error immediately without retrying.
|
||||||
|
*/
|
||||||
|
hdr = buffer;
|
||||||
|
if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
|
||||||
|
return -EINVAL;
|
||||||
/*
|
/*
|
||||||
* We could get this if we send messages too
|
* We could get this if we send messages too
|
||||||
* frequently.
|
* frequently.
|
||||||
|
@ -187,6 +187,7 @@ struct hv_input_post_message {
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
VMBUS_MESSAGE_CONNECTION_ID = 1,
|
VMBUS_MESSAGE_CONNECTION_ID = 1,
|
||||||
|
VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
|
||||||
VMBUS_MESSAGE_PORT_ID = 1,
|
VMBUS_MESSAGE_PORT_ID = 1,
|
||||||
VMBUS_EVENT_CONNECTION_ID = 2,
|
VMBUS_EVENT_CONNECTION_ID = 2,
|
||||||
VMBUS_EVENT_PORT_ID = 2,
|
VMBUS_EVENT_PORT_ID = 2,
|
||||||
@ -302,6 +303,8 @@ struct vmbus_connection {
|
|||||||
*/
|
*/
|
||||||
int connect_cpu;
|
int connect_cpu;
|
||||||
|
|
||||||
|
u32 msg_conn_id;
|
||||||
|
|
||||||
atomic_t offer_in_progress;
|
atomic_t offer_in_progress;
|
||||||
|
|
||||||
enum vmbus_connect_state conn_state;
|
enum vmbus_connect_state conn_state;
|
||||||
|
@ -1,20 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Linaro Limited. All rights reserved.
|
* Copyright (c) 2017 Linaro Limited. All rights reserved.
|
||||||
*
|
*
|
||||||
* Author: Leo Yan <leo.yan@linaro.org>
|
* Author: Leo Yan <leo.yan@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/amba/bus.h>
|
#include <linux/amba/bus.h>
|
||||||
#include <linux/coresight.h>
|
#include <linux/coresight.h>
|
||||||
@ -315,7 +303,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pc = debug_adjust_pc(drvdata);
|
pc = debug_adjust_pc(drvdata);
|
||||||
dev_emerg(dev, " EDPCSR: [<%px>] %pS\n", (void *)pc, (void *)pc);
|
dev_emerg(dev, " EDPCSR: %pS\n", (void *)pc);
|
||||||
|
|
||||||
if (drvdata->edcidsr_present)
|
if (drvdata->edcidsr_present)
|
||||||
dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr);
|
dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr);
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/amba/bus.h>
|
#include <linux/amba/bus.h>
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight Embedded Trace Buffer driver
|
* Description: CoreSight Embedded Trace Buffer driver
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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 <asm/local.h>
|
#include <asm/local.h>
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/coresight.h>
|
#include <linux/coresight.h>
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORESIGHT_ETM_PERF_H
|
#ifndef _CORESIGHT_ETM_PERF_H
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORESIGHT_CORESIGHT_ETM_H
|
#ifndef _CORESIGHT_CORESIGHT_ETM_H
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight Program Flow Trace driver
|
* Description: CoreSight Program Flow Trace driver
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORESIGHT_CORESIGHT_ETM_H
|
#ifndef _CORESIGHT_CORESIGHT_ETM_H
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight Funnel driver
|
* Description: CoreSight Funnel driver
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORESIGHT_PRIV_H
|
#ifndef _CORESIGHT_PRIV_H
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight Replicator driver
|
* Description: CoreSight Replicator driver
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight System Trace Macrocell driver
|
* Description: CoreSight System Trace Macrocell driver
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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.
|
|
||||||
*
|
|
||||||
* Initial implementation by Pratik Patel
|
* Initial implementation by Pratik Patel
|
||||||
* (C) 2014-2015 Pratik Patel <pratikp@codeaurora.org>
|
* (C) 2014-2015 Pratik Patel <pratikp@codeaurora.org>
|
||||||
*
|
*
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2016 Linaro Limited. All rights reserved.
|
* Copyright(C) 2016 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/circ_buf.h>
|
#include <linux/circ_buf.h>
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2016 Linaro Limited. All rights reserved.
|
* Copyright(C) 2016 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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/coresight.h>
|
#include <linux/coresight.h>
|
||||||
@ -124,10 +113,9 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
|||||||
bool used = false;
|
bool used = false;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
void __iomem *vaddr = NULL;
|
void __iomem *vaddr = NULL;
|
||||||
dma_addr_t paddr;
|
dma_addr_t paddr = 0;
|
||||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we don't have a buffer release the lock and allocate memory.
|
* If we don't have a buffer release the lock and allocate memory.
|
||||||
* Otherwise keep the lock and move along.
|
* Otherwise keep the lock and move along.
|
||||||
@ -164,11 +152,11 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If drvdata::buf == NULL, use the memory allocated above.
|
* If drvdata::vaddr == NULL, use the memory allocated above.
|
||||||
* Otherwise a buffer still exists from a previous session, so
|
* Otherwise a buffer still exists from a previous session, so
|
||||||
* simply use that.
|
* simply use that.
|
||||||
*/
|
*/
|
||||||
if (drvdata->buf == NULL) {
|
if (drvdata->vaddr == NULL) {
|
||||||
used = true;
|
used = true;
|
||||||
drvdata->vaddr = vaddr;
|
drvdata->vaddr = vaddr;
|
||||||
drvdata->paddr = paddr;
|
drvdata->paddr = paddr;
|
||||||
|
@ -1,15 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight Trace Memory Controller driver
|
* Description: CoreSight Trace Memory Controller driver
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
/*
|
/*
|
||||||
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _CORESIGHT_TMC_H
|
#ifndef _CORESIGHT_TMC_H
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* Description: CoreSight Trace Port Interface Unit driver
|
* Description: CoreSight Trace Port Interface Unit driver
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/kernel.h>
|
||||||
@ -1026,8 +1019,10 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
|||||||
dev_set_name(&csdev->dev, "%s", desc->pdata->name);
|
dev_set_name(&csdev->dev, "%s", desc->pdata->name);
|
||||||
|
|
||||||
ret = device_register(&csdev->dev);
|
ret = device_register(&csdev->dev);
|
||||||
if (ret)
|
if (ret) {
|
||||||
goto err_device_register;
|
put_device(&csdev->dev);
|
||||||
|
goto err_kzalloc_csdev;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&coresight_mutex);
|
mutex_lock(&coresight_mutex);
|
||||||
|
|
||||||
@ -1038,8 +1033,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
|||||||
|
|
||||||
return csdev;
|
return csdev;
|
||||||
|
|
||||||
err_device_register:
|
|
||||||
kfree(conns);
|
|
||||||
err_kzalloc_conns:
|
err_kzalloc_conns:
|
||||||
kfree(refcnts);
|
kfree(refcnts);
|
||||||
err_kzalloc_refcnts:
|
err_kzalloc_refcnts:
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
|
||||||
* only 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/types.h>
|
#include <linux/types.h>
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Simple kernel driver to link kernel Ftrace and an STM device
|
* Simple kernel driver to link kernel Ftrace and an STM device
|
||||||
* Copyright (c) 2016, Linaro Ltd.
|
* Copyright (c) 2016, Linaro Ltd.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* STM Ftrace will be registered as a trace_export.
|
* STM Ftrace will be registered as a trace_export.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -113,6 +113,20 @@ config IBM_ASM
|
|||||||
for information on the specific driver level and support statement
|
for information on the specific driver level and support statement
|
||||||
for your IBM server.
|
for your IBM server.
|
||||||
|
|
||||||
|
config IBMVMC
|
||||||
|
tristate "IBM Virtual Management Channel support"
|
||||||
|
depends on PPC_PSERIES
|
||||||
|
help
|
||||||
|
This is the IBM POWER Virtual Management Channel
|
||||||
|
|
||||||
|
This driver is to be used for the POWER Virtual
|
||||||
|
Management Channel virtual adapter on the PowerVM
|
||||||
|
platform. It provides both request/response and
|
||||||
|
async message support through the /dev/ibmvmc node.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ibmvmc.
|
||||||
|
|
||||||
config PHANTOM
|
config PHANTOM
|
||||||
tristate "Sensable PHANToM (PCI)"
|
tristate "Sensable PHANToM (PCI)"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_IBM_ASM) += ibmasm/
|
obj-$(CONFIG_IBM_ASM) += ibmasm/
|
||||||
|
obj-$(CONFIG_IBMVMC) += ibmvmc.o
|
||||||
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
||||||
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
|
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
|
||||||
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
|
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
|
||||||
|
@ -128,11 +128,12 @@ void cxl_context_set_mapping(struct cxl_context *ctx,
|
|||||||
mutex_unlock(&ctx->mapping_lock);
|
mutex_unlock(&ctx->mapping_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cxl_mmap_fault(struct vm_fault *vmf)
|
static vm_fault_t cxl_mmap_fault(struct vm_fault *vmf)
|
||||||
{
|
{
|
||||||
struct vm_area_struct *vma = vmf->vma;
|
struct vm_area_struct *vma = vmf->vma;
|
||||||
struct cxl_context *ctx = vma->vm_file->private_data;
|
struct cxl_context *ctx = vma->vm_file->private_data;
|
||||||
u64 area, offset;
|
u64 area, offset;
|
||||||
|
vm_fault_t ret;
|
||||||
|
|
||||||
offset = vmf->pgoff << PAGE_SHIFT;
|
offset = vmf->pgoff << PAGE_SHIFT;
|
||||||
|
|
||||||
@ -169,11 +170,11 @@ static int cxl_mmap_fault(struct vm_fault *vmf)
|
|||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT);
|
ret = vmf_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT);
|
||||||
|
|
||||||
mutex_unlock(&ctx->status_mutex);
|
mutex_unlock(&ctx->status_mutex);
|
||||||
|
|
||||||
return VM_FAULT_NOPAGE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct vm_operations_struct cxl_mmap_vmops = {
|
static const struct vm_operations_struct cxl_mmap_vmops = {
|
||||||
|
2418
drivers/misc/ibmvmc.c
Normal file
2418
drivers/misc/ibmvmc.c
Normal file
File diff suppressed because it is too large
Load Diff
209
drivers/misc/ibmvmc.h
Normal file
209
drivers/misc/ibmvmc.h
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*
|
||||||
|
* linux/drivers/misc/ibmvmc.h
|
||||||
|
*
|
||||||
|
* IBM Power Systems Virtual Management Channel Support.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004, 2018 IBM Corp.
|
||||||
|
* Dave Engebretsen engebret@us.ibm.com
|
||||||
|
* Steven Royer seroyer@linux.vnet.ibm.com
|
||||||
|
* Adam Reznechek adreznec@linux.vnet.ibm.com
|
||||||
|
* Bryant G. Ly <bryantly@linux.vnet.ibm.com>
|
||||||
|
*/
|
||||||
|
#ifndef IBMVMC_H
|
||||||
|
#define IBMVMC_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/cdev.h>
|
||||||
|
|
||||||
|
#include <asm/vio.h>
|
||||||
|
|
||||||
|
#define IBMVMC_PROTOCOL_VERSION 0x0101
|
||||||
|
|
||||||
|
#define MIN_BUF_POOL_SIZE 16
|
||||||
|
#define MIN_HMCS 1
|
||||||
|
#define MIN_MTU 4096
|
||||||
|
#define MAX_BUF_POOL_SIZE 64
|
||||||
|
#define MAX_HMCS 2
|
||||||
|
#define MAX_MTU (4 * 4096)
|
||||||
|
#define DEFAULT_BUF_POOL_SIZE 32
|
||||||
|
#define DEFAULT_HMCS 1
|
||||||
|
#define DEFAULT_MTU 4096
|
||||||
|
#define HMC_ID_LEN 32
|
||||||
|
|
||||||
|
#define VMC_INVALID_BUFFER_ID 0xFFFF
|
||||||
|
|
||||||
|
/* ioctl numbers */
|
||||||
|
#define VMC_BASE 0xCC
|
||||||
|
#define VMC_IOCTL_SETHMCID _IOW(VMC_BASE, 0x00, unsigned char *)
|
||||||
|
#define VMC_IOCTL_QUERY _IOR(VMC_BASE, 0x01, struct ibmvmc_query_struct)
|
||||||
|
#define VMC_IOCTL_REQUESTVMC _IOR(VMC_BASE, 0x02, u32)
|
||||||
|
|
||||||
|
#define VMC_MSG_CAP 0x01
|
||||||
|
#define VMC_MSG_CAP_RESP 0x81
|
||||||
|
#define VMC_MSG_OPEN 0x02
|
||||||
|
#define VMC_MSG_OPEN_RESP 0x82
|
||||||
|
#define VMC_MSG_CLOSE 0x03
|
||||||
|
#define VMC_MSG_CLOSE_RESP 0x83
|
||||||
|
#define VMC_MSG_ADD_BUF 0x04
|
||||||
|
#define VMC_MSG_ADD_BUF_RESP 0x84
|
||||||
|
#define VMC_MSG_REM_BUF 0x05
|
||||||
|
#define VMC_MSG_REM_BUF_RESP 0x85
|
||||||
|
#define VMC_MSG_SIGNAL 0x06
|
||||||
|
|
||||||
|
#define VMC_MSG_SUCCESS 0
|
||||||
|
#define VMC_MSG_INVALID_HMC_INDEX 1
|
||||||
|
#define VMC_MSG_INVALID_BUFFER_ID 2
|
||||||
|
#define VMC_MSG_CLOSED_HMC 3
|
||||||
|
#define VMC_MSG_INTERFACE_FAILURE 4
|
||||||
|
#define VMC_MSG_NO_BUFFER 5
|
||||||
|
|
||||||
|
#define VMC_BUF_OWNER_ALPHA 0
|
||||||
|
#define VMC_BUF_OWNER_HV 1
|
||||||
|
|
||||||
|
enum ibmvmc_states {
|
||||||
|
ibmvmc_state_sched_reset = -1,
|
||||||
|
ibmvmc_state_initial = 0,
|
||||||
|
ibmvmc_state_crqinit = 1,
|
||||||
|
ibmvmc_state_capabilities = 2,
|
||||||
|
ibmvmc_state_ready = 3,
|
||||||
|
ibmvmc_state_failed = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ibmhmc_states {
|
||||||
|
/* HMC connection not established */
|
||||||
|
ibmhmc_state_free = 0,
|
||||||
|
|
||||||
|
/* HMC connection established (open called) */
|
||||||
|
ibmhmc_state_initial = 1,
|
||||||
|
|
||||||
|
/* open msg sent to HV, due to ioctl(1) call */
|
||||||
|
ibmhmc_state_opening = 2,
|
||||||
|
|
||||||
|
/* HMC connection ready, open resp msg from HV */
|
||||||
|
ibmhmc_state_ready = 3,
|
||||||
|
|
||||||
|
/* HMC connection failure */
|
||||||
|
ibmhmc_state_failed = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibmvmc_buffer {
|
||||||
|
u8 valid; /* 1 when DMA storage allocated to buffer */
|
||||||
|
u8 free; /* 1 when buffer available for the Alpha Partition */
|
||||||
|
u8 owner;
|
||||||
|
u16 id;
|
||||||
|
u32 size;
|
||||||
|
u32 msg_len;
|
||||||
|
dma_addr_t dma_addr_local;
|
||||||
|
dma_addr_t dma_addr_remote;
|
||||||
|
void *real_addr_local;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibmvmc_admin_crq_msg {
|
||||||
|
u8 valid; /* RPA Defined */
|
||||||
|
u8 type; /* ibmvmc msg type */
|
||||||
|
u8 status; /* Response msg status. Zero is success and on failure,
|
||||||
|
* either 1 - General Failure, or 2 - Invalid Version is
|
||||||
|
* returned.
|
||||||
|
*/
|
||||||
|
u8 rsvd[2];
|
||||||
|
u8 max_hmc; /* Max # of independent HMC connections supported */
|
||||||
|
__be16 pool_size; /* Maximum number of buffers supported per HMC
|
||||||
|
* connection
|
||||||
|
*/
|
||||||
|
__be32 max_mtu; /* Maximum message size supported (bytes) */
|
||||||
|
__be16 crq_size; /* # of entries available in the CRQ for the
|
||||||
|
* source partition. The target partition must
|
||||||
|
* limit the number of outstanding messages to
|
||||||
|
* one half or less.
|
||||||
|
*/
|
||||||
|
__be16 version; /* Indicates the code level of the management partition
|
||||||
|
* or the hypervisor with the high-order byte
|
||||||
|
* indicating a major version and the low-order byte
|
||||||
|
* indicating a minor version.
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibmvmc_crq_msg {
|
||||||
|
u8 valid; /* RPA Defined */
|
||||||
|
u8 type; /* ibmvmc msg type */
|
||||||
|
u8 status; /* Response msg status */
|
||||||
|
union {
|
||||||
|
u8 rsvd; /* Reserved */
|
||||||
|
u8 owner;
|
||||||
|
} var1;
|
||||||
|
u8 hmc_session; /* Session Identifier for the current VMC connection */
|
||||||
|
u8 hmc_index; /* A unique HMC Idx would be used if multiple management
|
||||||
|
* applications running concurrently were desired
|
||||||
|
*/
|
||||||
|
union {
|
||||||
|
__be16 rsvd;
|
||||||
|
__be16 buffer_id;
|
||||||
|
} var2;
|
||||||
|
__be32 rsvd;
|
||||||
|
union {
|
||||||
|
__be32 rsvd;
|
||||||
|
__be32 lioba;
|
||||||
|
__be32 msg_len;
|
||||||
|
} var3;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* an RPA command/response transport queue */
|
||||||
|
struct crq_queue {
|
||||||
|
struct ibmvmc_crq_msg *msgs;
|
||||||
|
int size, cur;
|
||||||
|
dma_addr_t msg_token;
|
||||||
|
spinlock_t lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* VMC server adapter settings */
|
||||||
|
struct crq_server_adapter {
|
||||||
|
struct device *dev;
|
||||||
|
struct crq_queue queue;
|
||||||
|
u32 liobn;
|
||||||
|
u32 riobn;
|
||||||
|
struct tasklet_struct work_task;
|
||||||
|
wait_queue_head_t reset_wait_queue;
|
||||||
|
struct task_struct *reset_task;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Driver wide settings */
|
||||||
|
struct ibmvmc_struct {
|
||||||
|
u32 state;
|
||||||
|
u32 max_mtu;
|
||||||
|
u32 max_buffer_pool_size;
|
||||||
|
u32 max_hmc_index;
|
||||||
|
struct crq_server_adapter *adapter;
|
||||||
|
struct cdev cdev;
|
||||||
|
u32 vmc_drc_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibmvmc_file_session;
|
||||||
|
|
||||||
|
/* Connection specific settings */
|
||||||
|
struct ibmvmc_hmc {
|
||||||
|
u8 session;
|
||||||
|
u8 index;
|
||||||
|
u32 state;
|
||||||
|
struct crq_server_adapter *adapter;
|
||||||
|
spinlock_t lock;
|
||||||
|
unsigned char hmc_id[HMC_ID_LEN];
|
||||||
|
struct ibmvmc_buffer buffer[MAX_BUF_POOL_SIZE];
|
||||||
|
unsigned short queue_outbound_msgs[MAX_BUF_POOL_SIZE];
|
||||||
|
int queue_head, queue_tail;
|
||||||
|
struct ibmvmc_file_session *file_session;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibmvmc_file_session {
|
||||||
|
struct file *file;
|
||||||
|
struct ibmvmc_hmc *hmc;
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ibmvmc_query_struct {
|
||||||
|
int have_vmc;
|
||||||
|
int state;
|
||||||
|
int vmc_drc_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __IBMVMC_H */
|
@ -926,7 +926,7 @@ again:
|
|||||||
*
|
*
|
||||||
* Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries.
|
* Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries.
|
||||||
*/
|
*/
|
||||||
int gru_fault(struct vm_fault *vmf)
|
vm_fault_t gru_fault(struct vm_fault *vmf)
|
||||||
{
|
{
|
||||||
struct vm_area_struct *vma = vmf->vma;
|
struct vm_area_struct *vma = vmf->vma;
|
||||||
struct gru_thread_state *gts;
|
struct gru_thread_state *gts;
|
||||||
|
@ -147,6 +147,7 @@
|
|||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/mmu_notifier.h>
|
#include <linux/mmu_notifier.h>
|
||||||
|
#include <linux/mm_types.h>
|
||||||
#include "gru.h"
|
#include "gru.h"
|
||||||
#include "grulib.h"
|
#include "grulib.h"
|
||||||
#include "gruhandles.h"
|
#include "gruhandles.h"
|
||||||
@ -665,7 +666,7 @@ extern unsigned long gru_reserve_cb_resources(struct gru_state *gru,
|
|||||||
int cbr_au_count, char *cbmap);
|
int cbr_au_count, char *cbmap);
|
||||||
extern unsigned long gru_reserve_ds_resources(struct gru_state *gru,
|
extern unsigned long gru_reserve_ds_resources(struct gru_state *gru,
|
||||||
int dsr_au_count, char *dsmap);
|
int dsr_au_count, char *dsmap);
|
||||||
extern int gru_fault(struct vm_fault *vmf);
|
extern vm_fault_t gru_fault(struct vm_fault *vmf);
|
||||||
extern struct gru_mm_struct *gru_register_mmu_notifier(void);
|
extern struct gru_mm_struct *gru_register_mmu_notifier(void);
|
||||||
extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms);
|
extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms);
|
||||||
|
|
||||||
|
@ -407,7 +407,7 @@ xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg,
|
|||||||
* destination partid. If the destination partid octets are 0xffff,
|
* destination partid. If the destination partid octets are 0xffff,
|
||||||
* this packet is to be broadcast to all connected partitions.
|
* this packet is to be broadcast to all connected partitions.
|
||||||
*/
|
*/
|
||||||
static int
|
static netdev_tx_t
|
||||||
xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct xpnet_pending_msg *queued_msg;
|
struct xpnet_pending_msg *queued_msg;
|
||||||
|
@ -735,7 +735,7 @@ static int kim_probe(struct platform_device *pdev)
|
|||||||
st_kim_devices[0] = pdev;
|
st_kim_devices[0] = pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_ATOMIC);
|
kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_KERNEL);
|
||||||
if (!kim_gdata) {
|
if (!kim_gdata) {
|
||||||
pr_err("no mem to allocate");
|
pr_err("no mem to allocate");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -239,9 +239,13 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
|
|||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
unsigned int good_sockets = 0, bad_sockets = 0;
|
unsigned int good_sockets = 0, bad_sockets = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned char new_ids[fm->num_sockets];
|
/* Maximum number of entries is 4 */
|
||||||
|
unsigned char new_ids[4];
|
||||||
DECLARE_COMPLETION_ONSTACK(finish_resume);
|
DECLARE_COMPLETION_ONSTACK(finish_resume);
|
||||||
|
|
||||||
|
if (WARN_ON(fm->num_sockets > ARRAY_SIZE(new_ids)))
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
pci_set_power_state(dev, PCI_D0);
|
pci_set_power_state(dev, PCI_D0);
|
||||||
pci_restore_state(dev);
|
pci_restore_state(dev);
|
||||||
rc = pci_enable_device(dev);
|
rc = pci_enable_device(dev);
|
||||||
|
@ -576,15 +576,9 @@ static void vmballoon_pop(struct vmballoon *b)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b->batch_page) {
|
/* Clearing the batch_page unconditionally has no adverse effect */
|
||||||
vunmap(b->batch_page);
|
free_page((unsigned long)b->batch_page);
|
||||||
b->batch_page = NULL;
|
b->batch_page = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
if (b->page) {
|
|
||||||
__free_page(b->page);
|
|
||||||
b->page = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -991,16 +985,13 @@ static const struct vmballoon_ops vmballoon_batched_ops = {
|
|||||||
|
|
||||||
static bool vmballoon_init_batching(struct vmballoon *b)
|
static bool vmballoon_init_batching(struct vmballoon *b)
|
||||||
{
|
{
|
||||||
b->page = alloc_page(VMW_PAGE_ALLOC_NOSLEEP);
|
struct page *page;
|
||||||
if (!b->page)
|
|
||||||
|
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||||
|
if (!page)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
b->batch_page = vmap(&b->page, 1, VM_MAP, PAGE_KERNEL);
|
b->batch_page = page_address(page);
|
||||||
if (!b->batch_page) {
|
|
||||||
__free_page(b->page);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +58,7 @@ static const struct mux_control_ops adg792a_ops = {
|
|||||||
.set = adg792a_set,
|
.set = adg792a_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int adg792a_probe(struct i2c_client *i2c,
|
static int adg792a_probe(struct i2c_client *i2c)
|
||||||
const struct i2c_device_id *id)
|
|
||||||
{
|
{
|
||||||
struct device *dev = &i2c->dev;
|
struct device *dev = &i2c->dev;
|
||||||
struct mux_chip *mux_chip;
|
struct mux_chip *mux_chip;
|
||||||
@ -144,7 +143,7 @@ static struct i2c_driver adg792a_driver = {
|
|||||||
.name = "adg792a",
|
.name = "adg792a",
|
||||||
.of_match_table = of_match_ptr(adg792a_of_match),
|
.of_match_table = of_match_ptr(adg792a_of_match),
|
||||||
},
|
},
|
||||||
.probe = adg792a_probe,
|
.probe_new = adg792a_probe,
|
||||||
.id_table = adg792a_id,
|
.id_table = adg792a_id,
|
||||||
};
|
};
|
||||||
module_i2c_driver(adg792a_driver);
|
module_i2c_driver(adg792a_driver);
|
||||||
|
@ -63,20 +63,15 @@ static struct device nubus_parent = {
|
|||||||
.init_name = "nubus",
|
.init_name = "nubus",
|
||||||
};
|
};
|
||||||
|
|
||||||
int __init nubus_bus_register(void)
|
static int __init nubus_bus_register(void)
|
||||||
{
|
{
|
||||||
int err;
|
return bus_register(&nubus_bus_type);
|
||||||
|
}
|
||||||
|
postcore_initcall(nubus_bus_register);
|
||||||
|
|
||||||
err = device_register(&nubus_parent);
|
int __init nubus_parent_device_register(void)
|
||||||
if (err)
|
{
|
||||||
return err;
|
return device_register(&nubus_parent);
|
||||||
|
|
||||||
err = bus_register(&nubus_bus_type);
|
|
||||||
if (!err)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
device_unregister(&nubus_parent);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nubus_device_release(struct device *dev)
|
static void nubus_device_release(struct device *dev)
|
||||||
|
@ -875,7 +875,7 @@ static int __init nubus_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
nubus_proc_init();
|
nubus_proc_init();
|
||||||
err = nubus_bus_register();
|
err = nubus_parent_device_register();
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
nubus_scan_bus();
|
nubus_scan_bus();
|
||||||
|
@ -175,4 +175,10 @@ config NVMEM_SNVS_LPGPR
|
|||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called nvmem-snvs-lpgpr.
|
will be called nvmem-snvs-lpgpr.
|
||||||
|
|
||||||
|
config RAVE_SP_EEPROM
|
||||||
|
tristate "Rave SP EEPROM Support"
|
||||||
|
depends on RAVE_SP_CORE
|
||||||
|
help
|
||||||
|
Say y here to enable Rave SP EEPROM support.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
@ -37,3 +37,6 @@ obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o
|
|||||||
nvmem_meson_mx_efuse-y := meson-mx-efuse.o
|
nvmem_meson_mx_efuse-y := meson-mx-efuse.o
|
||||||
obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
|
obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
|
||||||
nvmem_snvs_lpgpr-y := snvs_lpgpr.o
|
nvmem_snvs_lpgpr-y := snvs_lpgpr.o
|
||||||
|
obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
|
||||||
|
nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
|
||||||
|
|
||||||
|
@ -353,18 +353,27 @@ static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvmem_add_cells(struct nvmem_device *nvmem,
|
/**
|
||||||
const struct nvmem_config *cfg)
|
* nvmem_add_cells() - Add cell information to an nvmem device
|
||||||
|
*
|
||||||
|
* @nvmem: nvmem device to add cells to.
|
||||||
|
* @info: nvmem cell info to add to the device
|
||||||
|
* @ncells: number of cells in info
|
||||||
|
*
|
||||||
|
* Return: 0 or negative error code on failure.
|
||||||
|
*/
|
||||||
|
int nvmem_add_cells(struct nvmem_device *nvmem,
|
||||||
|
const struct nvmem_cell_info *info,
|
||||||
|
int ncells)
|
||||||
{
|
{
|
||||||
struct nvmem_cell **cells;
|
struct nvmem_cell **cells;
|
||||||
const struct nvmem_cell_info *info = cfg->cells;
|
|
||||||
int i, rval;
|
int i, rval;
|
||||||
|
|
||||||
cells = kcalloc(cfg->ncells, sizeof(*cells), GFP_KERNEL);
|
cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
|
||||||
if (!cells)
|
if (!cells)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < cfg->ncells; i++) {
|
for (i = 0; i < ncells; i++) {
|
||||||
cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
|
cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
|
||||||
if (!cells[i]) {
|
if (!cells[i]) {
|
||||||
rval = -ENOMEM;
|
rval = -ENOMEM;
|
||||||
@ -380,7 +389,7 @@ static int nvmem_add_cells(struct nvmem_device *nvmem,
|
|||||||
nvmem_cell_add(cells[i]);
|
nvmem_cell_add(cells[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
nvmem->ncells = cfg->ncells;
|
nvmem->ncells = ncells;
|
||||||
/* remove tmp array */
|
/* remove tmp array */
|
||||||
kfree(cells);
|
kfree(cells);
|
||||||
|
|
||||||
@ -393,6 +402,7 @@ err:
|
|||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nvmem_add_cells);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* nvmem_setup_compat() - Create an additional binary entry in
|
* nvmem_setup_compat() - Create an additional binary entry in
|
||||||
@ -509,7 +519,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config->cells)
|
if (config->cells)
|
||||||
nvmem_add_cells(nvmem, config);
|
nvmem_add_cells(nvmem, config->cells, config->ncells);
|
||||||
|
|
||||||
return nvmem;
|
return nvmem;
|
||||||
|
|
||||||
@ -559,6 +569,7 @@ static void devm_nvmem_release(struct device *dev, void *res)
|
|||||||
* nvmem_config.
|
* nvmem_config.
|
||||||
* Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
|
* Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
|
||||||
*
|
*
|
||||||
|
* @dev: Device that uses the nvmem device.
|
||||||
* @config: nvmem device configuration with which nvmem device is created.
|
* @config: nvmem device configuration with which nvmem device is created.
|
||||||
*
|
*
|
||||||
* Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
|
* Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
|
||||||
@ -597,6 +608,7 @@ static int devm_nvmem_match(struct device *dev, void *res, void *data)
|
|||||||
* devm_nvmem_unregister() - Unregister previously registered managed nvmem
|
* devm_nvmem_unregister() - Unregister previously registered managed nvmem
|
||||||
* device.
|
* device.
|
||||||
*
|
*
|
||||||
|
* @dev: Device that uses the nvmem device.
|
||||||
* @nvmem: Pointer to previously registered nvmem device.
|
* @nvmem: Pointer to previously registered nvmem device.
|
||||||
*
|
*
|
||||||
* Return: Will be an negative on error or a zero on success.
|
* Return: Will be an negative on error or a zero on success.
|
||||||
@ -1107,6 +1119,8 @@ static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
|
|||||||
|
|
||||||
/* setup the first byte with lsb bits from nvmem */
|
/* setup the first byte with lsb bits from nvmem */
|
||||||
rc = nvmem_reg_read(nvmem, cell->offset, &v, 1);
|
rc = nvmem_reg_read(nvmem, cell->offset, &v, 1);
|
||||||
|
if (rc)
|
||||||
|
goto err;
|
||||||
*b++ |= GENMASK(bit_offset - 1, 0) & v;
|
*b++ |= GENMASK(bit_offset - 1, 0) & v;
|
||||||
|
|
||||||
/* setup rest of the byte if any */
|
/* setup rest of the byte if any */
|
||||||
@ -1125,11 +1139,16 @@ static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
|
|||||||
/* setup the last byte with msb bits from nvmem */
|
/* setup the last byte with msb bits from nvmem */
|
||||||
rc = nvmem_reg_read(nvmem,
|
rc = nvmem_reg_read(nvmem,
|
||||||
cell->offset + cell->bytes - 1, &v, 1);
|
cell->offset + cell->bytes - 1, &v, 1);
|
||||||
|
if (rc)
|
||||||
|
goto err;
|
||||||
*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
|
*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
|
err:
|
||||||
|
kfree(buf);
|
||||||
|
return ERR_PTR(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,23 +24,16 @@
|
|||||||
static int meson_efuse_read(void *context, unsigned int offset,
|
static int meson_efuse_read(void *context, unsigned int offset,
|
||||||
void *val, size_t bytes)
|
void *val, size_t bytes)
|
||||||
{
|
{
|
||||||
u8 *buf = val;
|
return meson_sm_call_read((u8 *)val, bytes, SM_EFUSE_READ, offset,
|
||||||
int ret;
|
bytes, 0, 0, 0);
|
||||||
|
|
||||||
ret = meson_sm_call_read(buf, bytes, SM_EFUSE_READ, offset,
|
|
||||||
bytes, 0, 0, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nvmem_config econfig = {
|
static int meson_efuse_write(void *context, unsigned int offset,
|
||||||
.name = "meson-efuse",
|
void *val, size_t bytes)
|
||||||
.stride = 1,
|
{
|
||||||
.word_size = 1,
|
return meson_sm_call_write((u8 *)val, bytes, SM_EFUSE_WRITE, offset,
|
||||||
.read_only = true,
|
bytes, 0, 0, 0);
|
||||||
};
|
}
|
||||||
|
|
||||||
static const struct of_device_id meson_efuse_match[] = {
|
static const struct of_device_id meson_efuse_match[] = {
|
||||||
{ .compatible = "amlogic,meson-gxbb-efuse", },
|
{ .compatible = "amlogic,meson-gxbb-efuse", },
|
||||||
@ -50,17 +43,27 @@ MODULE_DEVICE_TABLE(of, meson_efuse_match);
|
|||||||
|
|
||||||
static int meson_efuse_probe(struct platform_device *pdev)
|
static int meson_efuse_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
struct nvmem_device *nvmem;
|
struct nvmem_device *nvmem;
|
||||||
|
struct nvmem_config *econfig;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
|
|
||||||
if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0)
|
if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
econfig.dev = &pdev->dev;
|
econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
|
||||||
econfig.reg_read = meson_efuse_read;
|
if (!econfig)
|
||||||
econfig.size = size;
|
return -ENOMEM;
|
||||||
|
|
||||||
nvmem = devm_nvmem_register(&pdev->dev, &econfig);
|
econfig->dev = dev;
|
||||||
|
econfig->name = dev_name(dev);
|
||||||
|
econfig->stride = 1;
|
||||||
|
econfig->word_size = 1;
|
||||||
|
econfig->reg_read = meson_efuse_read;
|
||||||
|
econfig->reg_write = meson_efuse_write;
|
||||||
|
econfig->size = size;
|
||||||
|
|
||||||
|
nvmem = devm_nvmem_register(&pdev->dev, econfig);
|
||||||
|
|
||||||
return PTR_ERR_OR_ZERO(nvmem);
|
return PTR_ERR_OR_ZERO(nvmem);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user